diff --git a/.clang-tidy b/.clang-tidy index 5aad02e467092..7d8916ba46fdb 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -4,6 +4,30 @@ # this codebase and we do not intend to fix. The disabled checks appearing # thereafter in a separate alphabetical list have yet to be triaged. We may # fix their errors or recategorise them as checks we don't care about. +# +# Comments on the checks we have decided are not worthwhile: +# +# * cert-dcl21-cpp (postfix operator++ and operator-- should return const objects) +# This is an unconventional code style, and conflicts with +# readability-const-return-type. +# +# * cert-env33-c (calls to system, popen) +# Unlikely to catch bugs, and using system is convenient for portability. +# +# * cert-err58-cpp (exceptions from static variable declarations) +# We have lots of memory allocations in static variable declarations, and +# that's fine. +# +# * modernize-use-auto +# We prefer an almost-always-avoid-auto style. +# +# * modernize-use-trailing-return-type +# An arbitrary style convention we haven't adopted. +# +# * readability-braces-around-statements +# Covered by astyle and buggy in clang-tidy 8. Can enable once we have a newer +# clang-tidy. + Checks: "\ bugprone-*,\ cata-*,\ @@ -21,34 +45,47 @@ modernize-*,\ -modernize-use-trailing-return-type,\ performance-*,\ readability-*,\ +-readability-braces-around-statements,\ +-bugprone-branch-clone,\ +-bugprone-infinite-loop,\ -bugprone-misplaced-widening-cast,\ -bugprone-narrowing-conversions,\ --bugprone-unused-return-value,\ --cert-err34-c,\ --cert-flp30-c,\ --cert-msc30-c,\ --cert-msc32-c,\ --cert-msc50-cpp,\ --cert-msc51-cpp,\ +-bugprone-redundant-branch-condition,\ +-bugprone-reserved-identifier,\ +-bugprone-signed-char-misuse,\ +-bugprone-sizeof-expression,\ +-bugprone-unhandled-self-assignment,\ +-cert-dcl37-c,\ +-cert-dcl51-cpp,\ +-cert-oop54-cpp,\ +-cert-str34-c,\ +-clang-analyzer-core.CallAndMessage,\ +-clang-analyzer-deadcode.DeadStores,\ +-misc-misplaced-const,\ +-misc-no-recursion,\ -misc-non-private-member-variables-in-classes,\ +-misc-redundant-expression,\ +-modernize-avoid-bind,\ -modernize-avoid-c-arrays,\ +-modernize-loop-convert,\ -modernize-pass-by-value,\ -modernize-return-braced-init-list,\ -modernize-use-default-member-init,\ --modernize-use-emplace,\ --performance-inefficient-vector-operation,\ --performance-noexcept-move-constructor,\ --performance-implicit-conversion-in-loop,\ --performance-inefficient-string-concatenation,\ --performance-type-promotion-in-math-fn,\ +-modernize-use-override,\ +-performance-no-automatic-move,\ +-performance-trivially-destructible,\ -performance-unnecessary-value-param,\ --readability-braces-around-statements,\ +-readability-avoid-const-params-in-decls,\ +-readability-convert-member-functions-to-static,\ -readability-else-after-return,\ --readability-function-size,\ +-readability-function-cognitive-complexity,\ -readability-implicit-bool-conversion,\ --readability-isolate-declaration,\ -readability-magic-numbers,\ +-readability-make-member-function-const,\ -readability-named-parameter,\ +-readability-qualified-auto,\ +-readability-redundant-access-specifiers,\ +-readability-use-anyofallof,\ " WarningsAsErrors: '*' HeaderFilterRegex: '(src|test|tools).*' diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index fc2f76faaa571..bd959ede62aea 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,12 +2,28 @@ **Opening a new issue?** Please read [ISSUES.md](../ISSUES.md) first. -Contributing to Cataclysm: Dark Days Ahead is easy — simply fork the repository here on GitHub, make your changes, and then send us a pull request. +**Want an introductory guide for creating game content?** You might want to +read the [Guide to adding new content to CDDA for first time +contributors](https://github.com/CleverRaven/Cataclysm-DDA/wiki/Guide-to-adding-new-content-to-CDDA-for-first-time-contributors) +on the CDDA wiki. Cataclysm:Dark Days Ahead is released under the Creative Commons Attribution ShareAlike 3.0 license. The code and content of the game is free to use, modify, and redistribute for any purpose whatsoever. See http://creativecommons.org/licenses/by-sa/3.0/ for details. This means any contribution you make to the project will also be covered by the same license, and this license is irrevocable. -## Guidelines +## Using a good text editor + +Most of the Cataclysm: Dark Days Ahead game data is defined in JSON files. +These files are intended to be easy for you to edit, but there are some +pitfalls. Using Windows Notepad can get you into trouble, because it likes to +insert a special character called a BOM at the start of the file, which CDDA +does not want. + +If you're going to be editing JSON files consider getting a more fully-featured +editor such as [Notepad++](https://notepad-plus-plus.org/). + +## Contributing to GitHub + +Contributing to Cataclysm: Dark Days Ahead is easy — simply fork the repository here on GitHub, make your changes, and then send us a pull request. There are a couple of guidelines we suggest sticking to: diff --git a/.github/workflows/CBA.yml b/.github/workflows/CBA.yml index a8adfaef29ad9..b9325b39f07c7 100644 --- a/.github/workflows/CBA.yml +++ b/.github/workflows/CBA.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - 0.F-dev paths: - '**.cpp' - '**.h' @@ -16,6 +17,7 @@ on: pull_request: branches: - master + - 0.F-dev paths: - '**.cpp' - '**.h' diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 56ee2ee858be7..2a0bdb2527434 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -1,9 +1,10 @@ -name: Clang-tidy (clang-8, tiles) +name: Clang-tidy (clang-12, tiles) on: push: branches: - master + - 0.F-dev paths: - '**.cpp' - '**.h' @@ -15,6 +16,7 @@ on: pull_request: branches: - master + - 0.F-dev paths: - '**.cpp' - '**.h' @@ -26,11 +28,11 @@ on: jobs: build: - runs-on: ubuntu-16.04 + runs-on: ubuntu-20.04 env: CMAKE: 1 - CLANG: clang++-8 - COMPILER: clang++-8 + CLANG: clang++-12 + COMPILER: clang++-12 CATA_CLANG_TIDY: plugin TILES: 1 SOUND: 1 @@ -41,9 +43,10 @@ jobs: fetch-depth: 1 - name: install dependencies run: | - #sudo apt-add-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main" + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main" sudo apt-get update - sudo apt-get install libncursesw5-dev clang-8 libclang-8-dev llvm-8-dev llvm-8-tools \ + sudo apt-get install libncursesw5-dev clang-12 libclang-12-dev llvm-12-dev llvm-12-tools \ libsdl2-dev libsdl2-ttf-dev libsdl2-image-dev libsdl2-mixer-dev libpulse-dev ccache \ gettext - name: prepare diff --git a/.github/workflows/cmake-format.yml b/.github/workflows/cmake-format.yml index bba76717aaef4..563127694ade7 100644 --- a/.github/workflows/cmake-format.yml +++ b/.github/workflows/cmake-format.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - 0.F-dev paths: - '**/CMakeLists.txt' - '**.cmake' @@ -11,6 +12,7 @@ on: pull_request: branches: - master + - 0.F-dev paths: - '**/CMakeLists.txt' - '**.cmake' diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index ed7b63dbbc07a..4434a75707c6d 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -4,11 +4,13 @@ on: push: branches: - master + - 0.F-dev paths: - '**.py' pull_request: branches: - master + - 0.F-dev paths: - '**.py' diff --git a/.github/workflows/matrix.yml b/.github/workflows/matrix.yml index 8e68f1c1fef23..d751bd8c98113 100644 --- a/.github/workflows/matrix.yml +++ b/.github/workflows/matrix.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - 0.F-dev paths-ignore: - 'android/**' - 'build-data/osx/**' @@ -18,6 +19,7 @@ on: pull_request: branches: - master + - 0.F-dev paths-ignore: - 'android/**' - 'build-data/osx/**' @@ -71,16 +73,27 @@ jobs: tiles: 1 native: osx title: Clang 12, macOS 10.15, Tiles + - compiler: g++ + os: ubuntu-latest + cmake: 0 + tiles: 1 + title: GCC, Ubuntu cross-compile to MinGW-Win64, Tiles + ldflags: -static-libgcc -static-libstdc++ + mxe_target: i686-w64-mingw32.static + wine: wine name: ${{ matrix.title }} runs-on: ${{ matrix.os }} env: CMAKE: ${{ matrix.cmake }} COMPILER: ${{ matrix.compiler }} + MXE_TARGET: ${{ matrix.mxe_target }} + WINE: ${{ matrix.wine }} OS: ${{ matrix.os }} TILES: ${{ matrix.tiles }} SOUND: ${{ matrix.tiles }} SANITIZE: ${{ matrix.sanitize }} TEST_STAGE: ${{ matrix.test-stage }} + LDFLAGS: ${{ matrix.ldflags }} EXTRA_TEST_OPTS: --error-format=github-action NATIVE: ${{ matrix.native }} GOLD: ${{ matrix.gold }} @@ -132,8 +145,9 @@ jobs: - name: build and test run: bash ./build-scripts/build.sh - name: upload artifacts if failed - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2 if: failure() with: name: cata_test path: tests/cata_test + if-no-files-found: ignore diff --git a/.github/workflows/pr-validator.yml b/.github/workflows/pr-validator.yml index 2897f6a160c1b..62c6095211c5e 100644 --- a/.github/workflows/pr-validator.yml +++ b/.github/workflows/pr-validator.yml @@ -3,6 +3,7 @@ on: pull_request: branches: - master + - 0.F-dev types: [opened, edited, synchronize] jobs: validate: diff --git a/.travis.yml b/.travis.yml index 0c488ee209c36..169aab5e5aca3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ os: linux branches: only: - master - - development + - 0.F-dev # Overall strategy for what sorts of builds to include: # We want a build for each compiler and each platform. @@ -99,7 +99,8 @@ jobs: - stage: "Platforms and Tidy" # MXE variant using alternate repository http://mirror.mxe.cc/repos/apt env: COMPILER=g++ LDFLAGS="-static-libgcc -static-libstdc++" MXE_TARGET="i686-w64-mingw32.static" WINE="wine" TILES=1 SOUND=1 - name: "Mingw-w64 Make cross-compile to Windows with Tiles and Sound" + name: "MinGW-Win64 Make cross-compile to Windows with Tiles and Sound" + if: type != pull_request compiler: gcc addons: &gcc apt: diff --git a/build-scripts/build.sh b/build-scripts/build.sh index 8e1dbdebdd9a1..2b20f3a6802a1 100755 --- a/build-scripts/build.sh +++ b/build-scripts/build.sh @@ -71,16 +71,16 @@ then cmake_extra_opts+=("-DCATA_CLANG_TIDY_PLUGIN=ON") # Need to specify the particular LLVM / Clang versions to use, lest it # use the llvm-7 that comes by default on the Travis Xenial image. - cmake_extra_opts+=("-DLLVM_DIR=/usr/lib/llvm-8/lib/cmake/llvm") - cmake_extra_opts+=("-DClang_DIR=/usr/lib/llvm-8/lib/cmake/clang") + cmake_extra_opts+=("-DLLVM_DIR=/usr/lib/llvm-12/lib/cmake/llvm") + cmake_extra_opts+=("-DClang_DIR=/usr/lib/llvm-12/lib/cmake/clang") fi - if [ "$COMPILER" = "clang++-8" -a -n "$GITHUB_WORKFLOW" -a -n "$CATA_CLANG_TIDY" ] + if [ "$COMPILER" = "clang++-12" -a -n "$GITHUB_WORKFLOW" -a -n "$CATA_CLANG_TIDY" ] then # This is a hacky workaround for the fact that the custom clang-tidy we are # using is built for Travis CI, so it's not using the correct include directories # for GitHub workflows. - cmake_extra_opts+=("-DCMAKE_CXX_FLAGS=-isystem /usr/include/clang/8.0.0/include") + cmake_extra_opts+=("-DCMAKE_CXX_FLAGS=-isystem /usr/include/clang/12.0.0/include") fi mkdir build @@ -195,11 +195,15 @@ else if [ -n "$TEST_STAGE" ] then - # Run the tests one more time, without actually running any tests, just to verify that all - # the mod data can be successfully loaded - - mods="$(./build-scripts/get_all_mods.py)" - run_test './tests/cata_test --user-dir=all_modded --mods='"${mods}" '~*' '' + # Run the tests with all the mods, without actually running any tests, + # just to verify that all the mod data can be successfully loaded. + # Because some mods might be mutually incompatible we might need to run a few times. + + ./build-scripts/get_all_mods.py | \ + while read mods + do + run_test './tests/cata_test --user-dir=all_modded --mods='"${mods}" '~*' '' + done fi fi ccache --show-stats diff --git a/build-scripts/get_all_mods.py b/build-scripts/get_all_mods.py index 5b10edd137a32..130719734f2d9 100755 --- a/build-scripts/get_all_mods.py +++ b/build-scripts/get_all_mods.py @@ -1,10 +1,20 @@ #!/usr/bin/env python3 +# The goal of this script is to print out sets of mods for testing. Each line +# of output is a comma-separated list of mods. Together the lines should cover +# all in-repo mods, in as few lines as possible. Each line must contain only +# mods which are mutually compatible. + import glob import json +mods_this_time = [] + -mods_to_keep = [] +def compatible_with(mod, existing_mods): + if mod in total_conversions and total_conversions & set(existing_mods): + return False + return True def add_mods(mods): @@ -13,12 +23,16 @@ def add_mods(mods): # Either an invalid mod id, or blacklisted. return False for mod in mods: - if mod not in mods_to_keep: - mods_to_keep.append(mod) + if mod not in mods_this_time and compatible_with(mod, mods_this_time): + if add_mods(all_mod_dependencies[mod]): + mods_this_time.append(mod) + else: + return False return True all_mod_dependencies = {} +total_conversions = set() for info in glob.glob('data/mods/*/modinfo.json'): mod_info = json.load(open(info)) @@ -26,10 +40,19 @@ def add_mods(mods): if e["type"] == "MOD_INFO": ident = e["id"] all_mod_dependencies[ident] = e.get("dependencies", []) - -for mod in all_mod_dependencies: - if mod not in mods_to_keep: - if add_mods(all_mod_dependencies[mod]): - mods_to_keep.append(mod) - -print(','.join(mods_to_keep)) + if e["category"] == "total_conversion": + total_conversions.add(ident) + +mods_remaining = set(all_mod_dependencies) + +while mods_remaining: + for mod in mods_remaining: + if mod not in mods_this_time: + add_mods([mod]) + if not mods_remaining & set(mods_this_time): + raise RuntimeError( + 'mods remain ({}) but none could be added'.format(mods_remaining)) + + print(','.join(mods_this_time)) + mods_remaining = mods_remaining - set(mods_this_time) + mods_this_time = [] diff --git a/build-scripts/requirements.sh b/build-scripts/requirements.sh index bb17283fa3ca9..84b98290270ec 100644 --- a/build-scripts/requirements.sh +++ b/build-scripts/requirements.sh @@ -49,13 +49,14 @@ fi if [ -n "$CATA_CLANG_TIDY" ]; then $travis_retry pip install --user wheel --upgrade - $travis_retry pip install --user compiledb 'lit==0.11.1' 'click==7.1.2' + $travis_retry pip install --user compiledb lit fi # Influenced by https://github.com/zer0main/battleship/blob/master/build/windows/requirements.sh if [ -n "${MXE_TARGET}" ]; then - sudo add-apt-repository 'deb [arch=amd64] https://mirror.mxe.cc/repos/apt xenial main' - $travis_retry sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 84C7C89FC632241A6999ED0A580873F586B72ED9 + $travis_retry sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 84C7C89FC632241A6999ED0A580873F586B72ED9 + sudo add-apt-repository 'deb [arch=amd64] https://mirror.mxe.cc/repos/apt xenial main' + sudo dpkg --add-architecture i386 # We need to treat apt-get update warnings as errors for which the exit code # is not sufficient. The following workaround inspired by # https://unix.stackexchange.com/questions/175146/apt-get-update-exit-status/ @@ -66,9 +67,18 @@ if [ -n "${MXE_TARGET}" ]; then MXE2_TARGET=$(echo "$MXE_TARGET" | sed 's/_/-/g') export MXE_DIR=/usr/lib/mxe/usr/bin - $travis_retry sudo apt-get --yes install mxe-${MXE2_TARGET}-gcc mxe-${MXE2_TARGET}-gettext mxe-${MXE2_TARGET}-glib mxe-${MXE2_TARGET}-sdl2 mxe-${MXE2_TARGET}-sdl2-ttf mxe-${MXE2_TARGET}-sdl2-image mxe-${MXE2_TARGET}-sdl2-mixer + $travis_retry sudo apt-get --yes install \ + mxe-${MXE2_TARGET}-gcc \ + mxe-${MXE2_TARGET}-gettext \ + mxe-${MXE2_TARGET}-glib \ + mxe-${MXE2_TARGET}-sdl2 \ + mxe-${MXE2_TARGET}-sdl2-ttf \ + mxe-${MXE2_TARGET}-sdl2-image \ + mxe-${MXE2_TARGET}-sdl2-mixer \ + wine \ + wine32 export PLATFORM='i686-w64-mingw32.static' - export CROSS_COMPILATION='${MXE_DIR}/${PLATFORM}-' + export CROSS_COMPILATION="${MXE_DIR}/${PLATFORM}-" # Need to overwrite CXX to make the Makefile $CROSS logic work right. export CXX="$COMPILER" export CCACHE=1 @@ -97,4 +107,26 @@ if [[ "$NATIVE" == "android" ]]; then yes | sdkmanager "ndk-bundle" fi +if [ -n "$WINE" ] +then + # The build script will try to run things under wine in parallel, and I + # think there are race conditions that can cause that to break. So, run + # something benign under wine in advance to trigger it to configure all the + # one-time init stuff + wine hostname +fi + +# On GitHub actions environment variables are not saved between steps by +# default, so we need to explicitly save the ones that we care about +if [ -n "$GITHUB_ENV" ] +then + for v in CROSS_COMPILATION CXX + do + if [ -n "${!v}" ] + then + printf "%s='%s'\n" "$v" "${!v}" >> "$GITHUB_ENV" + fi + done +fi + set +x diff --git a/data/core/game_balance.json b/data/core/game_balance.json index dc2794d4782fa..d86b2352b0e91 100644 --- a/data/core/game_balance.json +++ b/data/core/game_balance.json @@ -188,6 +188,20 @@ "stype": "int", "value": 5 }, + { + "type": "EXTERNAL_OPTION", + "name": "GENERIC_PROFESSION_ID", + "info": "The profession selected by default in the character creator menu.", + "stype": "string_input", + "value": "unemployed" + }, + { + "type": "EXTERNAL_OPTION", + "name": "GENERIC_SCENARIO_ID", + "info": "The scenario selected by default in the character creator menu.", + "stype": "string_input", + "value": "evacuee" + }, { "type": "EXTERNAL_OPTION", "name": "WORKBENCH_ALL_OPTIONS", diff --git a/data/core/traps.json b/data/core/traps.json new file mode 100644 index 0000000000000..da83ec74d9697 --- /dev/null +++ b/data/core/traps.json @@ -0,0 +1,17 @@ +[ + { + "//": "We're always going to need a 'nothing here' tile, we currently use traps for this.", + "type": "trap", + "id": "tr_ledge", + "name": "ledge", + "color": "i_cyan", + "memorial_male": { "ctxt": "memorial_female", "str": "Fell down a ledge." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Fell down a ledge." }, + "symbol": " ", + "visibility": 0, + "avoidance": 99999, + "difficulty": 99, + "action": "ledge", + "vehicle_data": { "is_falling": true } + } +] diff --git a/data/help/texts.json b/data/help/texts.json index fef4b3cecfe8e..b610014faca4d 100644 --- a/data/help/texts.json +++ b/data/help/texts.json @@ -6,7 +6,7 @@ "messages": [ "Cataclysm: Dark Days Ahead is a turn-based survival game set in a post-apocalyptic world. You have survived the original onslaught, but the future looks pretty grim.", "You must prepare to face the many hardships to come, including dwindling supplies, hostile creatures, and harmful weather. Even among fellow survivors you must stay alert, since someone may be plotting behind your back to take your hard-earned loot.", - "Though one might think of Cataclysm: Dark Days Ahead as a roguelike, it vastly differs from the traditional roguelikes in several ways. Rather than exploring an underground dungeon, with a limited area on each level, you are exploring a truly infinite world, stretching in all four cardinal directions. In this survival game, you will have to find food; you also need to keep yourself hydrated and sleep periodically. It's based on the principle of realism, so expect all hardships you'd expect in life in a survival situation, and at least a dozen more from the eldritch and sci-fi nature of the Cataclysm itself.", + "Though one might think of Cataclysm: Dark Days Ahead as a roguelike, it differs from the traditional roguelikes in several crucial ways. Rather than exploring an underground dungeon, with a limited area on each level, you are exploring a truly infinite world, stretching in all four cardinal directions. In this survival game, you will have to find food, keep yourself hydrated, and also sleep periodically. It's based on the principle of realism, so expect all of the hardships you'd expect in a real-life survival situation, and at least a dozen more from the eldritch and sci-fi nature of the Cataclysm itself.", "While Cataclysm: Dark Days Ahead has more tasks to keep track of than many other games, the modern setting of the game makes some tasks easier. Firearms, medications, and a wide variety of tools are all available to help you survive." ] }, @@ -15,12 +15,12 @@ "order": 1, "name": ": Movement", "messages": [ - "Movement is performed using the numpad, or vikeys.", + "Movement is performed using the numpad, the arrow keys, or vikeys.", "", "Each step will take 100 movement points (or more, depending on the terrain); you will then replenish a variable amount of movement points, depending on many factors (press to see the exact amount).", "To attempt to hit a monster with your weapon, simply move into it.", "You may find doors, ('+'); these may be opened with or closed with . Some doors are locked. Locked doors, windows, and some other obstacles can be destroyed by smashing them (, then choose a direction). Smashing down obstacles is much easier with a good weapon or a strong character.", - "There may be times when you want to move more quickly by holding down a movement key. However, fast movement in this fashion may lead to the player getting into a dangerous situation or even killed before they have a chance to react. Pressing will toggle \"Safe Mode\". While this is on, any movement will be ignored if new monsters enter the player's view." + "There may be times when you want to move more quickly by holding down a movement key. However, fast movement in this fashion may lead to the player getting into a dangerous situation or even killed before they have a chance to react. Pressing will toggle \"Safe Mode.\" While this is on, any movement will be ignored if new monsters enter the player's view." ] }, { @@ -28,8 +28,8 @@ "order": 2, "name": ": Viewing", "messages": [ - "The player can often see more than can be displayed on the screen at a time. Pressing enters \"look around mode\", which allows you to scroll around using the movement keys and view items on the map as well as monsters and their stance toward the character. Pressing provides a list of nearby visible items, though items shut away in crates, cupboards, refrigerators and the like won't be displayed until you are close enough. Pressing Shift+vikeys (h,j,k,l,y,u,b,n) will scroll the view persistently, allowing you to keep an eye on things as you move around.", - "Places outside of your view but seen previously and still memorized can be visualized, but they will be covered by the \"fog of war\"." + "The player can often see more than can be displayed on the screen at a time. Pressing enters \"look around mode,\" which allows you to scroll around using the movement keys and view items on the map as well as monsters and their stance toward the character. Pressing provides a list of nearby visible items, though items shut away in crates, cupboards, refrigerators and the like won't be displayed until you are close enough. Pressing Shift+vikeys (h,j,k,l,y,u,b,n) will scroll the view persistently, allowing you to keep an eye on things as you move around.", + "Places outside of your view but seen previously and still memorized can be visualized, but they will be covered by the \"fog of war.\"" ] }, { @@ -37,9 +37,9 @@ "order": 3, "name": ": Hunger, thirst, and sleep", "messages": [ - "As time passes, you will begin to feel hunger and thirst, and your stomach will remind you of that. When this happens, status information will appear in the sidebar. Don't get confused - depending on your condition it will display both how full your stomach is, as well as hunger intensity in relation to your nutrition status. Your body nutrition level is distinct from your current hunger; for example, you may feel full after a big meal, while still being malnourished. And you will still get hungry while being overweight. With regular meals, you may even never get hungry and yet become malnourished, if your caloric intake is too low. In other words, you need to track both your immediate hunger/fullness and the overall nutrition of your whole body.", - "Food takes some time to digest, and water travels faster through your digestive system than does solid food. Sudden overeating and overdrinking may make you feel excessively full, so split your meals. If you starved before, it will take some time to recover your body mass by building up stored calories through regular eating. Overeating for a prolonged period of time leads to obesity. Both sides of the spectrum are penalized. When hunger and thirst progress to severe levels, you will also suffer penalties. Thirst will kill you faster then hunger.", - "You can develop various vitamin deficiencies if you eat poorly. These deficiencies come in stages; for example, you won't go from perfectly good health into a full-blown scurvy in an instant. Any developing and ongoing deficiencies will be reported in the character sheet. Deficiencies will inflict various penalties, but luckily, they are always reversible, and multivitamin pills can help you to correct these deficiencies. You can also ingest too much of any particular vitamin, however, and that can create problems as well. Be sure to have a balanced diet, or at least not a completely atrocious one. You can, and should, examine food items (by pressing ) to view their nutritional information.", + "As time passes, you will begin to feel hunger and thirst, and your stomach will remind you of that. When this happens, status information will appear in the sidebar. Don't get confused - depending on your condition, it will display both how full your stomach is, as well as hunger intensity in relation to your nutrition status. Your body nutrition level is distinct from your current hunger: for example, you may feel full after a big meal while still being malnourished, and you will still get hungry while being overweight. With regular meals, you may even never get hungry and yet become malnourished, if your caloric intake is too low. In other words, you need to track both your immediate hunger/fullness and the overall nutrition of your whole body.", + "Food takes some time to digest, and water travels faster through your digestive system than does solid food. Sudden overeating and overdrinking may make you feel excessively full, so split your meals. If you had been starved before, it will take some time to recover your body mass by building up stored calories through regular eating. Overeating for a prolonged period of time leads to obesity. Both sides of the spectrum are penalized. When hunger and thirst progress to severe levels, you will also suffer penalties. Thirst will kill you faster than hunger.", + "You can develop various vitamin deficiencies if you eat poorly. These deficiencies come in stages; for example, you won't go from perfectly good health into full-blown scurvy in an instant. Any developing and ongoing deficiencies will be reported in the character sheet. Deficiencies will inflict various penalties, but luckily, they are always reversible, and multivitamin pills can help you to correct these deficiencies. You can also ingest too much of any particular vitamin, however, and that can create problems as well. Be sure to have a balanced diet, or at least not a completely atrocious one. You can, and should, examine food items (by pressing ) to view their nutritional information.", "Eating and drinking poorly will affect your health. Fast foods, quick snacks and sweet drinks are high in calories, but are a poor choice for a sustainable food source, if you don't want your health to get worse. On the other hand, vegetables, herbal teas, and many other self-prepared meals are good for your health, and should be welcome additions to your diet.", "Finding food in a city is usually easy; outside of a city, you may have to hunt or forage. After killing an animal, stand over the animal's corpse and butcher it into small chunks of meat by pressing . You might also be able to forage for edible fruit or vegetables; to do this, find a promising plant and examine it. Likewise, you may have to drink water from a river or another natural source. To collect it, stand in shallow water and press . You'll need a watertight container to store it. Be forewarned that most sources of water aren't trustworthy, and may yield disease-carrying water. To make sure it's healthy, purify the water by boiling it or using a water purifier before drinking.", "Every dozen or so hours, you'll find yourself growing sleepy. If you do not sleep by pressing , you'll start suffering stat and movement penalties. You may not always fall asleep right away. Sleeping indoors, especially on a bed, will help. If that's not enough, sleeping pills may be of use. While sleeping, you'll slowly replenish lost hit points. You'll also be vulnerable to attack, so try to find a safe place to sleep, or set traps for unwary intruders." @@ -50,7 +50,7 @@ "order": 4, "name": ": Pain and stimulants", "messages": [ - "When you take damage from almost any source, you'll start to feel pain. Pain slows you down and reduces your stats, and finding a way to manage pain is an early imperative. The most common is drugs: aspirin, codeine, tramadol, oxycodone, and more are all great options. Be aware that while under the influence of many painkillers, the physiological side effects may slow you or reduce your stats.", + "When you take damage from almost any source, you'll start to feel pain. Pain slows you down and reduces your stats, and finding a way to manage pain is an early imperative. The most common is drugs: aspirin, codeine, tramadol, oxycodone, and more are all great options. Be aware that while under the influence of many painkillers, the physiological side effects may slow you down or reduce your stats.", "Note that most painkillers take a little while to kick in. If you take some oxycodone and don't notice the effects right away, don't start taking more - you may overdose and die!", "Pain will also disappear with time, so if drugs aren't available and you're in a lot of pain, it may be wise to find a safe spot and simply rest for an extended period of time.", "Stimulants are another common class of drugs. Stimulants provide you with a temporary rush of energy, increasing your movement speed and many stats (most notably intelligence), making them useful study aids. There are two drawbacks to stimulants: they make it more difficult to sleep and, more importantly, most are highly addictive. Stimulants range from the caffeine rush of cola to the more intense high of Adderall and methamphetamine.", @@ -65,10 +65,10 @@ "messages": [ "When you take damage, it will change the status of the affected body part, as shown in the sidebar. Its overall wellness is represented by a series of bars that will deplete and change color the more damage you take. If the accumulated damage is more than the body part can suffer, it will break. If that happens to a critical body part (i.e., your torso or head), you will die. For other body parts, it means broken bones, significant penalties, and a long process of healing - after placing the affected limb in a splint.", "Normal damage heals with time. You heal more during a good sleep, but wounds also heal a bit while you are awake.", - "You need to treat your wounds properly, because untreated wounds heal very slowly. First, you need to bandage your wounds, and, if possible, disinfect them as well. This will set conditions for proper healing and will result in faster recovery. You can make makeshift bandages if you have the skill, and there are a few options to replenish your stock of disinfectant. But remember to always treat your wounds as much as you can, as this is the proper way to get them healed.", - "You can get bitten if a zombie grabs you. Bite wounds can be deep and infected. If that happens, the status bar of affected body part will change color to blue. This means you need to disinfect it as soon as possible to clean the wound and prevent serious infection. If there is no chance of getting disinfectant in the immediate aftermath, you may try the more drastic method of cauterizing the wound, but it often worsens its state instead of helping. If you leave it be, the affected body part will develop a serious infection, and its status bar color will change to green. At this stage, it is too late to disinfect the wound, and you need antibiotics to fight it. Your body will fight the infection by itself, and regular antibiotic intake may keep you alive long enough for that to happen. You have to wait it out, in the hope that you will not die in the process.", + "You need to treat your wounds properly, because untreated wounds heal very slowly. First, you need to bandage your wounds, and, if possible, disinfect them as well. This will set conditions for proper healing and will result in faster recovery. You can make makeshift bandages if you have the skill, and there are a few options to replenish your stock of disinfectant. Remember to always treat your wounds as much as you can, as this is the proper way to get them healed.", + "You can get bitten if a zombie grabs you. Bite wounds can be deep; this means that it is infected. If that happens, the status bar of affected body part will change color to blue. This means you need to disinfect it as soon as possible to clean the wound and prevent serious infection. If there is no chance of getting disinfectant in the immediate aftermath, you may try the more drastic method of cauterizing the wound, but it often worsens its state instead of helping. If you leave it be, the affected body part will develop a serious infection, and its status bar color will change to green. At this stage, it is too late to disinfect the wound, and you need antibiotics to fight it. Your body will fight the infection by itself, and regular antibiotic intake may keep you alive long enough for that to happen. You have to wait it out, in the hope that you will not die in the process.", "If you are bleeding, the status bar of the wounded part turns red. The affected body part takes damage as long as it bleeding. The simplest way to stop bleeding is to use a bandage, and there are additional medical items dedicated to stopping hemorrhage.", - "You may be affected by one of many medical conditions and maladies throughout the game. In that case, you may want to seek proper medication, if one exists for the affliction you suffer. There are many medications beside the ones already mentioned, some of which have some extra effects that you might utilize even in a way not originally intended. With some skill you can also try natural medicine, as many herbs have more or less beneficial effects, and if you are a seasoned chemist you can synthesize your own drugs." + "You may be affected by one of many additional medical conditions and maladies throughout the game. In that case, you may want to seek proper medication, if one exists for the affliction you suffer. There are many medications beside the ones already mentioned, some of which have some extra effects that you might utilize even in a way not originally intended. With some skill you can also try natural medicine, as many herbs have more or less beneficial effects, and if you are a seasoned chemist you can synthesize your own drugs." ] }, { @@ -89,7 +89,7 @@ "There are lots of options for increasing morale: reading an entertaining book, eating delicious food, listening to music, and taking recreational drugs are but a few options. However, most morale-boosting activities can only elevate your mood to a certain level before they grow boring.", "There are also lots of ways for your morale to decrease, beyond its natural decay toward its normal level. Eating disgusting food, reading a boring technical book, killing a friendly NPC, or going through drug withdrawal are some prominent examples.", "Low morale will make you sluggish and unmotivated. If your morale drops very low, you won't be able to perform crafting at all. If you become sufficiently depressed, you will suffer stat penalties. Very high morale fills you with gusto and energy, and you will find yourself moving faster. At extremely high levels, you will receive stat bonuses.", - "Morale is also responsible for ensuring you can learn effectively, via a mechanic referred to as 'focus'. Your focus level is a measure of how effectively you can learn. The natural level is 100, which indicates normal learning potential. Higher or lower focus levels make it easier or harder to learn from practical experience.", + "Morale is also responsible for ensuring you can learn effectively, via a mechanic referred to as 'focus.' Your focus level is a measure of how effectively you can learn. The natural level is 100, which indicates normal learning potential. Higher or lower focus levels make it easier or harder to learn from practical experience.", "Your focus level has a natural set point that it will trend toward. When your focus is much lower - or higher - than this set point, it will change faster than when it is near the set point. Having high morale will raise the set point, and having low morale will lower the set point. Pain is also factored into the set point calculation - it's harder to learn when you're in pain.", "Your focus is also lowered by certain activities. Training your skills through real-world practice lowers your focus gradually, by an amount that depends on your current level of focus (higher focus means larger decreases, as well as improved learning). Training your skills by reading books decreases your focus rapidly, by giving a significant penalty to the set point of your focus.", "You can disable a skill in the Player Info Menu (press ) if you don't want to practice it, and it will prevent focus drain while performing relevant actions, but you will not progress in that skill while it's disabled." @@ -100,7 +100,7 @@ "order": 8, "name": ": Radioactivity and mutation", "messages": [ - "Though it is relatively rare, certain areas of the world may be contaminated with radiation. It will gradually accumulate in your body, weakening you more and more. While in radiation-free areas, your radiation level will slowly decrease; taking Prussian blue tablets will help speed this process. Get yourself a measuring device, as radiation is invisible and you may not know that you are under its influence until it results in radiation sickness.", + "Though it is relatively rare, certain areas of the world may be contaminated with radiation. It will gradually accumulate in your body, weakening you more and more. While in radiation-free areas, your radiation level will slowly decrease; taking Prussian blue tablets will help speed this process. Get yourself a measuring device, as radiation is invisible, and you may not know that you are under its influence until it results in radiation sickness.", "If you become very irradiated, you may develop mutations. Most of the time, these mutations will be negative; however, many are beneficial, and others have both positive and negative effects. Your mutations may change your play style considerably. It is possible to find substances that will remove mutations, though these are extremely rare.", "There are various mutagenic substances, and consuming (or injecting) them will grant mutations. However, the process taxes your body significantly, and can be addictive. With enough mutations and certain conditions being met, you will permanently transcend your humanity into a wholly different life-form." ] @@ -112,9 +112,9 @@ "messages": [ "Bionics are biomechanical upgrades to your body. While many are simply 'built-in' versions of items you would otherwise have to carry, some bionics have unique effects that are otherwise unobtainable. Some bionics are constantly working, whereas others need to be toggled on and off as needed.", "Most bionics require a source of power, and you will need an internal battery to store energy for them. Your current amount of energy is displayed in the sidebar right below your health. Replenishing energy can be done in a variety of ways, but all require the installation of a special bionic just for that purpose.", - "Installation of a bionic is only possible using a specialized medical apparatus, ideally operated by a trained professional. Using machinery to manipulate bionics requires high levels of Intelligence, first aid, computers, and electronics. Beware, though: a failure may cripple you! Many bionic modules are difficult to find, but may be purchased from certain wandering vagabonds for a very high price, or acquired by skillful dissection of corpses of deceased bionic users (including zombies). Bionic modules obtained this way may require extra preparation to become more safely usable.", + "Installation of a bionic is only possible using a specialized medical apparatus, ideally operated by a trained professional. Using machinery to manipulate bionics requires high levels of Intelligence, first aid, computers, and electronics. Beware, though: a failure may cripple you! Many bionic modules are difficult to find, but may be purchased from certain wandering vagabonds for a very high price, or acquired by skillful dissection of corpses of deceased bionic users (including zombies). Bionic modules obtained this way may require extra preparation to become safely usable.", "Any bionic can be removed from your body, but this process may be even harder - and thus riskier - than the initial installation. Both installation and removal are non-trivial surgical procedures, and therefore require anesthesia.", - "For lone survivors, the standard choice for installing or uninstalling bionics is an Autodoc. Usually, you can find one in a hospital or clinic. All Autodoc procedures require an anesthetic kit. However, you can bypass this restriction if you find a way to completely negate pain. Don't even try without proper anesthesia - normal drugs won't help.", + "For lone survivors, the standard choice for installing or uninstalling bionics is an Autodoc. Usually, you can find one in a hospital or clinic. All Autodoc procedures require an anesthetic kit. Don't even try without proper anesthesia - normal drugs won't help. However, you can bypass this restriction if you find a way to completely negate pain.", "Take note that bionic installation or removal require narcosis, immobilization of the patient, and only then the operation - which may take hours. So you have to make sure that you will be safe during this process." ] }, @@ -133,8 +133,8 @@ "-> Electronics lets you make a wide variety of tools with intricate uses.", "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.", "Crafting an item with high difficulty may fail and possibly waste some materials. You should gather and prepare spare material, just in case.", - "Crafting very large/heavy items or batches of items is best done at a workbench of some kind. You could use any ordinary table, or build your own out of metal to handle heavier loads. Using a workbench or other furniture for crafting is beneficial for the added comfort, thus making the process faster.", - "If for any reason the crafting process is interrupted, the progress is wrapped in a special item representing the craft in making. You may use it to resume crafting at any point.", + "Crafting very large/heavy items or batches of items is best done at a workbench of some kind. You could use any ordinary table, or build your own out of metal to handle heavier loads. Using a workbench or other furniture for crafting is more comfortable; therefore, crafting is faster.", + "If the crafting process is interrupted for any reason, the progress is wrapped in a special item representing the craft being made. You may use it to resume crafting at any point.", "Batch crafting in most cases means saved time, and your companions can also help if they know the ropes." ] }, @@ -157,8 +157,8 @@ "messages": [ "There is a wide variety of items available for your use. You may find them lying on the ground; if so, simply press to pick up items on the same tile. Some items are found inside a container, which may be drawn as a { with a blue background. Pressing , then a direction key, will allow you to examine these containers and loot their contents.", "Pressing opens a comparison menu, where you can see two items side-by-side with their attributes color-coded to indicate which is superior. You can also access the item comparison menu by pressing C after to open the \"View Nearby Items\" menu and selecting an item.", - "Almost any item may be used as a melee weapon, though some are better than others. You can check the melee attributes of an item you're carrying by pressing to enter your inventory, then pressing the letter of the item. There are 5 melee values: to-hit bonus (or penalty), moves per attack, and bash, cut, and pierce damage. The to-hit bonus increases the chance of an attack connecting with a monster and the chance of a successful attack becoming a critical hit. Moves per attack represents how many moves it takes to attack with the weapon, with 100 moves passing every second. Bash damage can stun a monster, preventing it from counter-attacking, but is governed by your strength. Cut damage is usually greater than bash damage, but many monsters have natural armor against it. Pierce damage usually penetrates armor better than cut damage, but does less damage overall, especially if you do not have a lot of skill in piercing weapons. It, too, may be reduced by a monster's natural armor. The typical damage per second values are for your survivor and account for moves per attack, encumbrance, missed strikes, weapon skill, critical hits, and target armor. The 'Best' value is against an unarmored target with no Dodge skill. The 'Vs. Agile' value is against an unarmored target with a high Dodge skill. The 'Vs. Armored' value is against a target with more than 15 Bash and 20 Cut resistance but no Dodge skill. These are typical values to let you assess the effectiveness of weapons; the amount of damage you actually inflict will vary depending on the situation.", - "To wield an item as a weapon, press , then the appropriate letter. Wielding the item you are currently wielding will unwield it, leaving your hands empty. A wielded weapon will not contribute to your volume carried, so holding a large item in your hands may be a good option for travel. When unwielding your weapon, it will go back in your inventory, or may be dropped on the ground if there is no space.", + "Almost any item may be used as a melee weapon, though some are better than others. You can check the melee attributes of an item you're carrying by pressing to enter your inventory, then pressing the letter of the item. There are 5 melee values: to-hit bonus (or penalty), moves per attack, and bash, cut, and pierce damage. The to-hit bonus adjusts the chance of an attack connecting with a monster and the chance of a successful attack becoming a critical hit. Moves per attack represents how many moves it takes to attack with the weapon, with 100 moves equaling one second. Bash damage can stun a monster, preventing it from counter-attacking, but is governed by your strength. Cut damage is usually greater than bash damage, but many monsters have natural armor against it. Pierce damage usually penetrates armor better than cut damage, but does less damage overall, especially if you do not have a lot of skill in piercing weapons. It, too, may be reduced by a monster's natural armor. The typical damage per second values are for your survivor and account for moves per attack, encumbrance, missed strikes, weapon skill, critical hits, and target armor. The 'Best' value is against an unarmored target with no Dodge skill. The 'Vs. Agile' value is against an unarmored target with a high Dodge skill. The 'Vs. Armored' value is against a target with more than 15 Bash and 20 Cut resistance but no Dodge skill. These are typical values to let you assess the effectiveness of weapons; the amount of damage you actually inflict will vary depending on the situation.", + "To wield an item as a weapon, press , then the appropriate letter. Wielding the item you are currently wielding will unwield it, leaving your hands empty. A wielded weapon will not contribute to your volume carried, so holding a large item in your hands may be a good option for travel. When unwielding your weapon, you will be given a choice of what to do with it.", "To wear a piece of clothing, press , then the appropriate letter. Armor reduces damage, and can help you resist things like smoke. To take off an item, press , then the appropriate letter. Clothing and armor are worn in distinct layers, and provide different coverage, protection and warmth. Each piece has its own encumbrance, and wearing too much on any given layer can significantly hamper your movement and other abilities, especially during combat. You can view and sort worn items by pressing .", "Your clothing can sit in one of five layers on your body: next-to-skin, standard, waist, over, and belted. You can wear one item from each layer on a body part without incurring an encumbrance penalty for too many worn items. Any items beyond the first on each layer add the encumbrance of the additional article(s) of clothing to the body part's encumbrance. The layering penalty applies a minimum of 2 and a maximum of 10 encumbrance per article of clothing.", "For example, on her torso, a character might wear a leather corset (next-to-skin), a leather touring suit (standard), a trenchcoat (over), and a survivor's runner pack (belted). Her encumbrance penalty is 0. If she put on a tank top it would conflict with the leather touring suit and add the minimum encumbrance penalty of 2.", @@ -201,15 +201,15 @@ "order": 15, "name": ": Survival tips", "messages": [ - "THe hierarchy of needs for survival in most cases is as follows: shelter, fire, water, food. The rest is the process of trying to answer a question: What would you do in a survival situation?", - "The first thing to do, when you are out of immediate danger, is checking your starting location for useful items. Your initial storage is limited, and a backpack, trenchcoat, or other storage medium will let you carry a lot more. Finding a weapon is important, but not a first priority; frying pans, butcher knives, and more are common in houses, and hardware stores may have others, as well as useful tools. Initially, save any guns you find as a last resort: ammo is scarce, while zombies are plenty, and unwanted attention may be more you can handle.", + "The hierarchy of needs for survival in most cases is as follows: shelter, fire, water, food. The rest is the process of trying to answer a question: What would you do in a survival situation?", + "The first thing to do, when you are out of immediate danger, is to check your starting location for useful items. Your initial storage is limited, and a backpack, trenchcoat, or other storage medium will let you carry a lot more. Finding a weapon is important, but not a first priority; frying pans, butcher knives, and more are common in houses, and hardware stores may have others, as well as useful tools. Initially, save any guns you find as a last resort: ammo is scarce, while zombies are plenty, and unwanted attention may be more than you can handle.", "It's also important to carry a few medications; painkillers are a must-have, and drugs such as cigarettes will make your life easier (but beware of addiction). Leave the city as soon as you have a good stockpile of equipment: while their population density makes cities important places to find food, ammunition, and many other goods, the high concentration of zombies makes them a deathtrap for the unprepared survivor.", - "Avoid combat whenever you can. Run if you have to; but control your stamina, as zombies, unlike yourself, are inexhaustible. Combat is always risky, and sometimes, one wrong step may be your last. Therefore, in most cases you gain nothing from combat unless you are equipped for it and killing your enemy will help you achieve something.", + "Avoid combat whenever you can. Run if you have to, but mind your stamina; zombies, unlike yourself, are inexhaustible. Combat is always risky, and sometimes, one wrong step may be your last. Therefore, in most cases you gain nothing from combat unless you are equipped for it and killing your enemy will help you achieve something.", "Combat is much easier if you can fight just one monster at a time. You can sometimes kite singled-out zombies, drawing them to a place of your choosing. Use doorways as a choke point or stand behind a window and strike as the zombies slowly climb through. Be wary - in numbers, they will push through anyway. Never be afraid to just run if you can outpace your enemies. Irregular terrain, like forests, may help you lose monsters.", "Using firearms is the easiest way to kill an enemy, but the sound will attract unwanted attention. Save the guns for emergencies, and melee when you can.", "If you need to protect yourself from acid, look for clothing made of plastic, Kevlar, leather, or cloth, in decreasing order of effectiveness. So, while leather and Kevlar will protect you from active enemies, a hazmat suit and rubber boots will make you nigh immune to acid damage. Items made of glass, ceramics, diamond, or precious metals will be totally immune to acid.", "When looting, try to fill your inventory as much as possible without being overloaded. You never know when you might need an item, most are good to sell, and you can easily drop unwanted items on the floor. But don't go looting empty-handed; you never know what might happen, so have your most crucial survival items with you.", - "Keep an eye on the weather. Wind and humidity will exacerbate dire conditions, so seek shelter if you're unable to withstand them. Staying dry is important, especially if temperatures are so low that they would cause frostbite. Use a towel to dry yourself if you are wet from the rain. If you're having trouble staying warm overnight, make a pile of clothing on the floor to sleep on. Finding a pillow and a blanket for your bed is always good for the quality of your sleep. If you can't find those, improvise; even a chair in a basement is better then a patch of wet ground in a field. Finding or making a shelter with a place to sleep is very important for your survival.", + "Keep an eye on the weather. Wind and humidity will exacerbate dire conditions, so seek shelter if you're unable to withstand them. Staying dry is important, especially if temperatures are so low that they might cause frostbite. Use a towel to dry yourself if you are wet from the rain. If you're having trouble staying warm overnight, make a pile of clothing on the floor to sleep on. Finding a pillow and a blanket for your bed is always good for the quality of your sleep. If you can't find those, improvise; even a chair in a basement is better then a patch of wet ground in a field. Finding or making a shelter with a place to sleep is very important for your survival.", "Winter is harsh. Liquids and food may freeze, and you may need to defrost them using a cooking appliance. Placing them by a lit fire might also help. Basements are warmer in winter and cooler in summer, so they are frequently good places to store your excess food. Most food decays with time, and that process can be sped up or slowed down by the temperature. Frozen food will not rot. It you are able to bring a freezer or a fridge back to life, it will help you in the long run. If these are out of your reach, think about constructing a root cellar.", "You may preserve food in many other ways, as well; for example, by using a smoking rack, or cooking it into a meal that has a longer shelf life." ] @@ -221,7 +221,7 @@ "messages": [ "You control vehicles using the numpad, or vikeys. Note, however, that you control the vehicle's controls, rather than controlling the vehicle directly.", "", - "In order to assume control of a vehicle, get to a location with working \"vehicle controls\" and a \"seat\", then press . Once you are in control, accelerates, slows or reverses, & & turn left or right. Diagonals adjust course and speed. You default to cruise control, so the gas/brake adjust the speed which the vehicle will attempt to maintain.", + "In order to assume control of a vehicle, get to a location with working \"vehicle controls\" and a \"seat,\" then press . Once you are in control, accelerates, slows or reverses, & & turn left or right. Diagonals adjust course and speed. You default to cruise control, so the gas/brake adjust the speed which the vehicle will attempt to maintain.", "10-30 MPH, or 16-48 KPH, is a good speed for beginning drivers, who tend to fumble the controls. As your Driving skill improves, you will fumble less and less. To simply maintain course and speed, hit .", "It's a good idea to pull the handbrake - \"s\" - when parking, just to be safe. If you want to get out, hit the lights, toggle cruise control, turn the engine on or off, or otherwise use the vehicle controls, press to bring up the \"Vehicle Controls\" menu, which has options for things you'd do from the driver's seat.", "Examining () a vehicle brings up the vehicle interaction window. The left pane shows a top-down view of your vehicle and each part of it. The middle pane shows a summary of the vehicle's engines, batteries, storage tanks, weapons, and seating. The right panel is context-sensitive, but normally has descriptions of the vehicle parts in the tile highlighted in the vehicle view on the left.", @@ -236,7 +236,7 @@ "BOATS AND AMPHIBIOUS VEHICLES", "You can use boat hull parts to create boats or amphibious vehicles that can move between water and land.", "Each boat hull you add to your vehicle increases the height of the vehicle's watertight hull. If the height of the watertight hull is less than the vehicle's draft, then water will flow into your vehicle, and it will sink and be destroyed if it goes into deep water. If your vehicle's draft is less than the height of the watertight hull, your vehicle will not take on water and will not sink in deep water.", - "Draft is determined by the length and width of your vehicle, the mass of your vehicle, and the percentage of the vehicle's underbody covered in boat hulls. In general, a vehicle should be able to float if its mass in tons is less than a quarter of the product of its length and width, provided that the watertight hull is high enough. A boat that is 5 tiles wide and 9 tiles long has a good chance of floating if all of its underbody is covered in boat boards and it weighs less than 10 tons.", + "Draft is determined by the length and width of your vehicle, the mass of your vehicle, and the percentage of the vehicle's underbody covered in boat hulls. In general, a vehicle should be able to float if its mass in tons is less than a quarter of the product of its length and width, provided that the watertight hull is high enough. A boat that is 5 tiles wide and 9 tiles long has a good chance of floating if its entire underbody is covered in boat boards and it weighs less than 10 tons.", "Increased draft increases water drag, and water drag reduces a vehicle's speed in water. If you want your boat to go fast, make it as light as possible.", "Amphibious vehicles have both wheels and boat hulls. As long as your amphibious vehicle has enough wheels on land and enough boat hulls to not sink in deep water, you can freely transition from land to water. However, most amphibious vehicles are much slower in water than they are on land, so be careful when entering water to not leave your cruise control speed above your vehicle's safe water speed or you can burn out your engine!" ] @@ -248,7 +248,7 @@ "messages": [ "~ Liquid\n%% Food\n! Medication\nThese are all consumed by using . They may provide a certain amount of nutrition, may quench your thirst, may be a stimulant or a depressant, and may provide (or reduce!) morale. There may also be more subtle effects.", "/ Large weapon\n; Small weapon or tool\n, Tiny item\nThese are all generic items, useful only to be wielded as a weapon. However, some have special uses; they will show up under the TOOLS section in your inventory. Press to use these.", - ") Container\nThese items may hold other items. Some are passable weapons. Many will be listed with their contents, e.g. \"plastic bottle > water\". Those containing comestibles may be eaten with ; this may leave an empty container.", + ") Container\nThese items may hold other items. Some are passable weapons. Many will be listed with their contents, e.g. \"plastic bottle > water.\" Those containing comestibles may be eaten with ; this may leave an empty container.", "[ Clothing\nThis may be worn with the key or removed with the key. It may cover one or more body parts; you can wear multiple articles of clothing on any given body part, but this will encumber you severely. Each article of clothing may provide storage space, warmth, encumbrance, and resistance to bashing and/or cutting attacks. Some may protect against environmental effects.", "( Firearm\nThis weapon may be loaded with ammunition with , unloaded with , and fired with . Some have automatic fire, which may be used with at a penalty to accuracy. The color corresponds to the type; handguns are gray, shotguns are red, submachine guns are cyan, rifles are brown, assault rifles are blue, and heavy machine guns are light red. Each has a dispersion rating, a bonus to damage, a rate of fire, and a maximum load. Note that, while most firearms load fully in one action, shotguns must be loaded one shell at a time.", "= Ammunition\nAmmunition is worthless without a gun to load it into. Generally, there are several variants for any particular caliber. Ammunition has damage, dispersion, and range ratings, as well as an armor-piercing value.", @@ -308,7 +308,7 @@ "Q: What is Safe Mode, and why does it prevent me from moving?\nA: Safe Mode is a way to guarantee that you won't die by holding a movement key down. When a monster comes into view, your movement will be ignored until Safe Mode is turned off with the key. This ensures that the sudden appearance of a monster won't catch you off guard.", "Q: It seems like everything I eat makes me sick! What's wrong?\nA: Lots of the food found in towns is perishable and will only last a few days after the start of a new game. The electricity went out several days ago, so fruit, milk, and similar foods are the first to go bad. After the first couple of days, you should switch to canned food, jerky, and hunting. Also, you should make sure to cook any food and purify any water you come across, as it may contain parasites or otherwise be unsafe.", "Q: How can I remove boards from boarded-up windows and doors?\nA: Use a hammer and choose the direction of the boarded-up window or door to remove the boards.", - "Q: The game just told me to quit, and other weird stuff is happening.\nA: You have the Schizophrenic trait, which might make the game seem buggy.", + "Q: The game just told me to quit, and other weird stuff is happening.\nA: You have the \"Kaluptic Psychosis\" trait, which might make the game seem buggy.", "Q: How can I prevent monsters from attacking me while I sleep?\nA: Find a safe place to sleep; for example, in a cleared building far from the front door. Set traps if you have them, or build a fire to scare off wild animals.", "Q: Why do I always sink when I try to swim?\nA: Your swimming ability is reduced greatly by the weight you are carrying, and is also adversely affected by most clothing you wear. Until you reach a high level of the swimming skill, you'll need to drop your equipment and remove your clothing to swim, making it a last-ditch escape plan. Diving gear may significantly help you in swimming and diving.", "Q: How can I cure a fungal infection?\nA: The Blood Filter bionic and some antifungal chemicals can cure fungal infection. Antifungal chemicals to cure the fungal infection can either be found as random loot or made from other ingredients.", diff --git a/data/json/bionics.json b/data/json/bionics.json index c195c15de4a95..085ac4fc83069 100644 --- a/data/json/bionics.json +++ b/data/json/bionics.json @@ -46,7 +46,78 @@ "bash_protec": [ [ "arm_l", 3 ], [ "arm_r", 3 ] ], "cut_protec": [ [ "arm_l", 3 ], [ "arm_r", 3 ] ], "bullet_protec": [ [ "arm_l", 3 ], [ "arm_r", 3 ] ], - "flags": [ "BIONIC_NPC_USABLE", "BIONIC_SHOCKPROOF" ] + "flags": [ "BIONIC_NPC_USABLE", "BIONIC_SHOCKPROOF" ], + "mutation_conflicts": [ + "THICKSKIN", + "THINSKIN", + "ALBINO", + "SKIN_ROUGH", + "M_SKIN", + "M_SKIN2", + "M_SKIN3", + "SCALES", + "THICK_SCALES", + "SLEEK_SCALES", + "FEATHERS", + "DOWN", + "LIGHTFUR", + "FUR", + "URSINE_FUR", + "LUPINE_FUR", + "FELINE_FUR", + "LYNX_FUR", + "CHITIN", + "CHITIN2", + "CHITIN3", + "CHITIN_FUR", + "CHITIN_FUR2", + "CHITIN_FUR3", + "CF_HAIR", + "SPINES", + "QUILLS", + "BARBS", + "PLANTSKIN", + "BARK", + "THORNS", + "LEAVES", + "LEAVES2", + "LEAVES3", + "SLIMY", + "VISCOUS", + "AMORPHOUS", + "BENDY1", + "BENDY2", + "BENDY3", + "WINGS_BIRD", + "WINGS_INSECT", + "LARGE", + "LARGE_OK", + "HUGE", + "HUGE_OK", + "SMALL", + "SMALL2", + "SMALL_OK", + "WINGS_STUB", + "WINGS_BAT", + "WINGS_BUTTERFLY", + "PALE", + "SPOTS", + "SUNBURN", + "SORES", + "CHLOROMORPH", + "ARM_FEATHERS", + "INSECT_ARMS", + "INSECT_ARMS_OK", + "ARACHNID_ARMS", + "ARACHNID_ARMS_OK", + "ARM_TENTACLES", + "ARM_TENTACLES_4", + "ARM_TENTACLES_8", + "CLAWS_TENTACLES", + "ACIDPROOF", + "TOXICFLESH", + "FRESHWATEROSMOSIS" + ] }, { "id": "bio_armor_eyes", @@ -58,7 +129,7 @@ "bash_protec": [ [ "eyes", 3 ] ], "cut_protec": [ [ "eyes", 3 ] ], "bullet_protec": [ [ "eyes", 3 ] ], - "flags": [ "BIONIC_NPC_USABLE", "BIONIC_SHOCKPROOF" ], + "mutation_conflicts": [ "COMPOUND_EYES", "CEPH_VISION", "CEPH_EYES", "EYEBULGE" ], "social_modifiers": { "intimidate": 10 } }, { @@ -70,7 +141,85 @@ "bash_protec": [ [ "head", 3 ] ], "cut_protec": [ [ "head", 3 ] ], "bullet_protec": [ [ "head", 3 ] ], - "flags": [ "BIONIC_NPC_USABLE", "BIONIC_SHOCKPROOF" ] + "flags": [ "BIONIC_NPC_USABLE", "BIONIC_SHOCKPROOF" ], + "mutation_conflicts": [ + "THICKSKIN", + "THINSKIN", + "ALBINO", + "SKIN_ROUGH", + "M_SKIN", + "M_SKIN2", + "M_SKIN3", + "SCALES", + "THICK_SCALES", + "SLEEK_SCALES", + "FEATHERS", + "DOWN", + "LIGHTFUR", + "FUR", + "URSINE_FUR", + "LUPINE_FUR", + "FELINE_FUR", + "LYNX_FUR", + "CHITIN", + "CHITIN2", + "CHITIN3", + "CHITIN_FUR", + "CHITIN_FUR2", + "CHITIN_FUR3", + "CF_HAIR", + "SPINES", + "QUILLS", + "BARBS", + "PLANTSKIN", + "BARK", + "THORNS", + "LEAVES", + "LEAVES2", + "LEAVES3", + "SLIMY", + "VISCOUS", + "AMORPHOUS", + "LARGE", + "LARGE_OK", + "HUGE", + "HUGE_OK", + "SMALL", + "SMALL2", + "SMALL_OK", + "PALE", + "SPOTS", + "SUNBURN", + "SORES", + "CHLOROMORPH", + "ACIDPROOF", + "TOXICFLESH", + "FRESHWATEROSMOSIS", + "BIOLUM0", + "BIOLUM0_active", + "BIOLUM1", + "BIOLUM1_active", + "BIOLUM2", + "BIOLUM2_active", + "GILLS", + "GILLS_CEPH", + "FLOWERS", + "ROSEBUDS", + "HORNS", + "HORNS_CURLED", + "HORNS_POINTED", + "ANTLERS", + "ANTENNAE", + "HEADBUMPS", + "HAIRROOTS", + "SNOUT", + "MINOTAUR", + "MUZZLE", + "MUZZLE_BEAR", + "MUZZLE_RAT", + "MUZZLE_LONG", + "PROBISCIS" + ] }, { "id": "bio_armor_legs", @@ -81,7 +230,76 @@ "bash_protec": [ [ "leg_l", 3 ], [ "leg_r", 3 ] ], "cut_protec": [ [ "leg_l", 3 ], [ "leg_r", 3 ] ], "bullet_protec": [ [ "leg_l", 3 ], [ "leg_r", 3 ] ], - "flags": [ "BIONIC_NPC_USABLE", "BIONIC_SHOCKPROOF" ] + "flags": [ "BIONIC_NPC_USABLE", "BIONIC_SHOCKPROOF" ], + "mutation_conflicts": [ + "THICKSKIN", + "THINSKIN", + "ALBINO", + "SKIN_ROUGH", + "M_SKIN", + "M_SKIN2", + "M_SKIN3", + "SCALES", + "THICK_SCALES", + "SLEEK_SCALES", + "FEATHERS", + "DOWN", + "LIGHTFUR", + "FUR", + "URSINE_FUR", + "LUPINE_FUR", + "FELINE_FUR", + "LYNX_FUR", + "CHITIN", + "CHITIN2", + "CHITIN3", + "CHITIN_FUR", + "CHITIN_FUR2", + "CHITIN_FUR3", + "CF_HAIR", + "SPINES", + "QUILLS", + "BARBS", + "PLANTSKIN", + "BARK", + "THORNS", + "LEAVES", + "LEAVES2", + "LEAVES3", + "SLIMY", + "VISCOUS", + "AMORPHOUS", + "BENDY1", + "BENDY2", + "BENDY3", + "LARGE", + "LARGE_OK", + "HUGE", + "HUGE_OK", + "SMALL", + "SMALL2", + "SMALL_OK", + "PALE", + "SPOTS", + "SUNBURN", + "SORES", + "CHLOROMORPH", + "ACIDPROOF", + "TOXICFLESH", + "FRESHWATEROSMOSIS", + "TAIL_STUB", + "TAIL_FIN", + "TAIL_LONG", + "TAIL_CATTLE", + "TAIL_RAT", + "TAIL_THICK", + "TAIL_RAPTOR", + "TAIL_FLUFFY", + "TAIL_STING", + "TAIL_CLUB", + "LEG_TENTACLES", + "LEG_TENT_BRACE" + ] }, { "id": "bio_armor_torso", @@ -92,7 +310,73 @@ "bash_protec": [ [ "torso", 3 ] ], "cut_protec": [ [ "torso", 3 ] ], "bullet_protec": [ [ "torso", 3 ] ], - "flags": [ "BIONIC_NPC_USABLE", "BIONIC_SHOCKPROOF" ] + "flags": [ "BIONIC_NPC_USABLE", "BIONIC_SHOCKPROOF" ], + "canceled_mutations": [ "VINES1", "VINES2", "VINES3" ], + "mutation_conflicts": [ + "THICKSKIN", + "THINSKIN", + "ALBINO", + "SKIN_ROUGH", + "M_SKIN", + "M_SKIN2", + "M_SKIN3", + "SCALES", + "THICK_SCALES", + "SLEEK_SCALES", + "FEATHERS", + "DOWN", + "LIGHTFUR", + "FUR", + "URSINE_FUR", + "LUPINE_FUR", + "FELINE_FUR", + "LYNX_FUR", + "CHITIN", + "CHITIN2", + "CHITIN3", + "CHITIN_FUR", + "CHITIN_FUR2", + "CHITIN_FUR3", + "CF_HAIR", + "SPINES", + "QUILLS", + "BARBS", + "PLANTSKIN", + "BARK", + "THORNS", + "LEAVES", + "LEAVES2", + "LEAVES3", + "SLIMY", + "VISCOUS", + "AMORPHOUS", + "BENDY1", + "BENDY2", + "BENDY3", + "LARGE", + "LARGE_OK", + "HUGE", + "HUGE_OK", + "SMALL", + "SMALL2", + "SMALL_OK", + "PALE", + "SPOTS", + "SUNBURN", + "SORES", + "CHLOROMORPH", + "ACIDPROOF", + "TOXICFLESH", + "FRESHWATEROSMOSIS", + "INK_GLANDS", + "INSECT_ARMS", + "INSECT_ARMS_OK", + "ARACHNID_ARMS", + "ARACHNID_ARMS_OK", + "CLAWS_TENTACLES", + "SHELL", + "SHELL2" + ] }, { "id": "bio_batteries", @@ -258,7 +542,8 @@ "act_cost": "100 J", "react_cost": "100 J", "time": 1, - "flags": [ "BIONIC_TOGGLED" ] + "flags": [ "BIONIC_TOGGLED" ], + "active_flags": [ "CLIMATE_CONTROL" ] }, { "id": "bio_cloak", @@ -280,7 +565,7 @@ "act_cost": "30 kJ", "react_cost": "30 kJ", "time": 1, - "enchantments": [ "ENCH_INVISIBILITY" ], + "enchantments": [ { "condition": "ACTIVE", "ench_effects": [ { "effect": "invisibility", "intensity": 1 } ] } ], "flags": [ "BIONIC_TOGGLED" ] }, { @@ -480,7 +765,9 @@ "type": "bionic", "name": { "str": "Fingerhack" }, "description": "One of your fingers has an electrohack surgically embedded in it; an all-purpose hacking unit used to override control panels and the like (but not computers). Skill in computers is important, and a failed use may damage your circuits.", - "occupied_bodyparts": [ [ "hand_r", 2 ] ] + "occupied_bodyparts": [ [ "hand_r", 2 ] ], + "fake_item": "electrohack", + "flags": [ "BIONIC_TOGGLED" ] }, { "id": "bio_flashbang", @@ -582,6 +869,7 @@ [ "foot_r", 1 ] ], "flags": [ "BIONIC_TOGGLED", "BIONIC_NPC_USABLE" ], + "active_flags": [ "HEATSINK" ], "act_cost": "1 kJ", "react_cost": "1 kJ", "time": 1 @@ -662,7 +950,7 @@ "description": "The index fingers of both hands have powerful fire starters which extend from the tip.", "occupied_bodyparts": [ [ "hand_l", 1 ], [ "hand_r", 1 ] ], "fake_item": "fake_firestarter", - "act_cost": "3 kJ" + "act_cost": "5 kJ" }, { "id": "bio_lockpick", @@ -736,7 +1024,13 @@ "description": "When active, this bionic eliminates all light within a 2 tile radius through destructive interference.", "occupied_bodyparts": [ [ "torso", 16 ] ], "flags": [ "BIONIC_TOGGLED" ], - "enchantments": [ "ENCH_SHADOW_CLOUD" ], + "enchantments": [ + { + "condition": "ACTIVE", + "ench_effects": [ { "effect": "invisibility", "intensity": 1 } ], + "emitter": "emit_shadow_field" + } + ], "act_cost": "9 kJ", "react_cost": "9 kJ", "time": 1 @@ -897,7 +1191,17 @@ "name": { "str": "Recycler Unit" }, "description": "Your digestive system has been outfitted with a series of filters and processors, allowing you to reclaim waste liquid and, to a lesser degree, nutrients. The net effect is a greatly reduced need to eat and drink.", "occupied_bodyparts": [ [ "torso", 15 ] ], - "flags": [ "BIONIC_NPC_USABLE" ] + "flags": [ "BIONIC_NPC_USABLE" ], + "enchantments": [ + { + "condition": "ALWAYS", + "values": [ + { "value": "HUNGER", "multiply": -0.5 }, + { "value": "THIRST", "multiply": -0.5 }, + { "value": "METABOLISM", "multiply": -0.5 } + ] + } + ] }, { "id": "bio_remote", @@ -1124,7 +1428,8 @@ "name": { "str": "Joint Servo" }, "description": "Your leg joints have been equipped with servomotors that provide power-assisted movement. They are optimized for running, but walking also requires less effort when this bionic is online. However, when it's offline it will hamper your movement, as you struggle against its moving parts.", "occupied_bodyparts": [ [ "leg_l", 12 ], [ "leg_r", 12 ] ], - "flags": [ "BIONIC_TOGGLED" ] + "flags": [ "BIONIC_TOGGLED" ], + "mutation_conflicts": [ "LEG_TENTACLES", "LEG_TENT_BRACE" ] }, { "id": "bio_trip", @@ -1160,7 +1465,8 @@ "description": "You will likely spend the rest of your days serving as a walking testament to why you don't opt for the Autodoc's 'Cyborg Identity Package'. A remodulator unit jammed down your throat has given you a creepy robot voice.", "occupied_bodyparts": [ [ "torso", 2 ], [ "mouth", 1 ] ], "flags": [ "BIONIC_FAULTY" ], - "social_modifiers": { "persuade": -20, "lie": 10, "intimidate": 20 } + "social_modifiers": { "persuade": -20, "lie": 10, "intimidate": 20 }, + "canceled_mutations": [ "GROWL", "SNARL", "HISS" ] }, { "id": "bio_watch", @@ -1223,6 +1529,7 @@ "name": { "str": "Soporific Induction" }, "description": "An electrode has been implanted into your brain's ventrolateral preoptic nucleus. It turns on whenever you're trying to fall asleep, creating an artificial but effective sensation of fatigue.", "occupied_bodyparts": [ [ "head", 1 ] ], - "flags": [ "BIONIC_TOGGLED" ] + "flags": [ "BIONIC_TOGGLED" ], + "enchantments": [ { "condition": "ACTIVE", "values": [ { "value": "SLEEPY", "add": 30 } ] } ] } ] diff --git a/data/json/construction.json b/data/json/construction.json index 55772a710de09..33f32023f2be8 100644 --- a/data/json/construction.json +++ b/data/json/construction.json @@ -59,6 +59,18 @@ "pre_terrain": "t_pit", "post_terrain": "t_pit_glass" }, + { + "type": "construction", + "id": "constr_pit_underground", + "group": "underground_pit", + "category": "DIG", + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "required_skills": [ [ "survival", 3 ] ], + "time": "300 m", + "tools": [ [ [ "pickaxe", -1 ], [ "jackhammer", 140 ], [ "elec_jackhammer", 7000 ] ] ], + "pre_terrain": "t_rock_floor", + "post_terrain": "t_pit" + }, { "type": "construction", "id": "constr_chop_trunk", @@ -644,6 +656,31 @@ "pre_terrain": "t_floor", "post_terrain": "t_bars" }, + { + "type": "construction", + "id": "constr_wall_wattle_post", + "group": "build_woven_wattle_fence", + "//": "Step 1: narrow fence posts for a woven wattle wall", + "category": "CONSTRUCT", + "required_skills": [ [ "fabrication", 0 ], [ "survival", 1 ] ], + "time": "20 m", + "qualities": [ [ { "id": "CUT", "level": 1 } ], [ { "id": "HAMMER", "level": 1 } ], [ { "id": "DIG", "level": 1 } ] ], + "components": [ [ [ "stick", 4 ], [ "pointy_stick", 4 ] ] ], + "pre_flags": "DIGGABLE", + "post_terrain": "t_wattle_fence_posts" + }, + { + "type": "construction", + "id": "constr_wall_wattle_woven", + "group": "build_woven_wattle_fence", + "category": "CONSTRUCT", + "required_skills": [ [ "fabrication", 2 ], [ "survival", 2 ] ], + "time": "50 m", + "qualities": [ [ { "id": "CUT", "level": 1 } ], [ { "id": "HAMMER", "level": 1 } ] ], + "components": [ [ [ "stick", 10 ] ] ], + "pre_terrain": "t_wattle_fence_posts", + "post_terrain": "t_wattle_fence" + }, { "type": "construction", "id": "constr_wall_wattle_half", @@ -682,6 +719,25 @@ "pre_terrain": "t_wall_wattle_half", "post_terrain": "t_wall_wattle" }, + { + "type": "construction", + "id": "constr_wall_wattle_from_fence", + "group": "build_wattle_and_daub_wall", + "//": "Upgrade a woven wattle fence to a full wall", + "category": "CONSTRUCT", + "required_skills": [ [ "fabrication", 3 ], [ "survival", 3 ] ], + "time": "60 m", + "qualities": [ [ { "id": "CUT", "level": 1 } ], [ { "id": "HAMMER", "level": 1 } ] ], + "components": [ + [ [ "2x4", 5 ], [ "stick", 10 ] ], + [ [ "material_quicklime", 8 ], [ "material_limestone", 8 ], [ "clay_lump", 8 ] ], + [ [ "pebble", 10 ], [ "material_sand", 10 ] ], + [ [ "straw_pile", 8 ], [ "cattail_stalk", 8 ], [ "dogbane", 8 ], [ "pine_bough", 8 ] ], + [ [ "water", 10 ], [ "water_clean", 10 ] ] + ], + "pre_terrain": "t_wattle_fence", + "post_terrain": "t_wall_wattle" + }, { "type": "construction", "id": "constr_repair_wall_wattle", @@ -843,6 +899,30 @@ "pre_terrain": "f_sandbag_half", "post_terrain": "f_sandbag_wall" }, + { + "type": "construction", + "id": "constr_gravelbag_half", + "group": "build_gravelbag_wall", + "category": "CONSTRUCT", + "required_skills": [ [ "fabrication", 0 ] ], + "time": "16 m", + "components": [ [ [ "gravelbag", 16 ] ] ], + "pre_note": "Can be deconstructed without tools.", + "pre_special": "check_empty", + "post_terrain": "f_gravelbag_half" + }, + { + "type": "construction", + "id": "constr_gravelbag_wall", + "group": "build_gravelbag_wall", + "category": "CONSTRUCT", + "required_skills": [ [ "fabrication", 0 ] ], + "time": "20 m", + "components": [ [ [ "gravelbag", 20 ] ] ], + "pre_note": "Can be deconstructed without tools.", + "pre_terrain": "f_gravelbag_half", + "post_terrain": "f_gravelbag_wall" + }, { "type": "construction", "id": "constr_earthbag_half", @@ -966,11 +1046,11 @@ "type": "construction", "id": "constr_railroad_rubble", "group": "make_gravel_floor", - "//": "Covers the floor with pebbles. Used for constructing railroads.", + "//": "Covers the floor with gravel. Used for constructing railroads.", "category": "CONSTRUCT", "required_skills": [ [ "fabrication", 1 ] ], "time": "40 m", - "components": [ [ [ "pebble", 100 ] ] ], + "components": [ [ [ "material_gravel", 500 ] ] ], "pre_special": "check_empty", "post_terrain": "t_railroad_rubble" }, @@ -4272,8 +4352,116 @@ "required_skills": [ [ "survival", 0 ] ], "time": "60 m", "qualities": [ [ { "id": "DIG", "level": 1 } ] ], - "byproducts": [ { "item": "pebble", "count": [ 70, 100 ] } ], + "byproducts": [ { "item": "material_gravel", "count": [ 350, 500 ] } ], "pre_terrain": "t_railroad_rubble", "post_terrain": "t_dirt" + }, + { + "type": "construction", + "id": "constr_mechanical_winch", + "group": "build_mechanical_winch", + "category": "CONSTRUCT", + "required_skills": [ [ "fabrication", 5 ], [ "mechanics", 6 ] ], + "time": "60 m", + "tools": [ [ [ "oxy_torch", 4 ], [ "welder", 20 ], [ "welder_crude", 30 ], [ "toolset", 30 ] ] ], + "qualities": [ [ { "id": "SAW_M", "level": 1 } ], [ { "id": "GLARE", "level": 2 } ] ], + "components": [ [ [ "chain", 16 ] ], [ [ "wheel_metal", 2 ] ], [ [ "pipe", 4 ] ] ], + "pre_note": "Must be adjacent to a reinforced concrete wall that is itself adjacent to a metal gate in order to open said gate.", + "pre_special": "check_empty", + "post_terrain": "t_gates_mech_control_lab" + }, + { + "type": "construction", + "id": "constr_metal_gate", + "group": "build_metal_gate", + "category": "CONSTRUCT", + "required_skills": [ [ "fabrication", 4 ], [ "mechanics", 4 ] ], + "time": "120 m", + "tools": [ [ [ "oxy_torch", 10 ], [ "welder", 50 ], [ "welder_crude", 75 ], [ "toolset", 75 ] ] ], + "qualities": [ [ { "id": "SAW_M", "level": 2 } ], [ { "id": "GLARE", "level": 2 } ] ], + "components": [ + [ [ "steel_lump", 8 ], [ "steel_chunk", 24 ], [ "scrap", 72 ] ], + [ [ "sheet_metal", 16 ] ], + [ [ "frame", 4 ], [ "spike", 24 ] ] + ], + "pre_note": "Must be between reinforced concrete walls to function, and at least one wall must have an adjacent mechanical winch.", + "pre_special": "check_empty", + "post_terrain": "t_door_metal_locked" + }, + { + "type": "construction", + "id": "constr_exercise_machine", + "group": "build_exercise_machine", + "category": "FURN", + "required_skills": [ [ "fabrication", 3 ] ], + "time": "180 m", + "qualities": [ [ { "id": "HAMMER", "level": 2 } ], [ { "id": "SCREW", "level": 1 } ] ], + "components": [ [ [ "pipe", 1 ] ], [ [ "steel_chunk", 1 ] ], [ [ "scrap", 6 ] ], [ [ "lead", 2000 ] ] ], + "pre_special": "check_empty", + "post_terrain": "f_exercise" + }, + { + "type": "construction", + "id": "constr_ergometer_mechanical", + "group": "build_ergometer_mechanical", + "category": "FURN", + "required_skills": [ [ "fabrication", 4 ] ], + "time": "240 m", + "qualities": [ [ { "id": "HAMMER", "level": 2 } ], [ { "id": "SCREW", "level": 1 } ], [ { "id": "WRENCH", "level": 1 } ] ], + "components": [ + [ [ "foot_crank", 1 ] ], + [ [ "plastic_chunk", 10 ] ], + [ [ "scrap", 4 ] ], + [ [ "chain", 1 ] ], + [ [ "pipe", 5 ] ], + [ [ "saddle", 1 ] ], + [ [ "wheel_small", 1 ] ], + [ [ "nail", 8 ] ] + ], + "pre_special": "check_empty", + "post_terrain": "f_ergometer_mechanical" + }, + { + "type": "construction", + "id": "constr_treadmill_mechanical", + "group": "build_treadmill_mechanical", + "category": "FURN", + "required_skills": [ [ "fabrication", 4 ] ], + "time": "240 m", + "qualities": [ [ { "id": "HAMMER", "level": 2 } ], [ { "id": "SCREW", "level": 1 } ], [ { "id": "WRENCH", "level": 1 } ] ], + "components": [ [ [ "plastic_chunk", 14 ] ], [ [ "scrap", 10 ] ], [ [ "pipe", 6 ] ], [ [ "nail", 8 ] ] ], + "pre_special": "check_empty", + "post_terrain": "f_treadmill_mechanical" + }, + { + "type": "construction", + "id": "constr_glass_wall", + "group": "build_glass_wall", + "category": "CONSTRUCT", + "required_skills": [ [ "fabrication", 3 ] ], + "time": "60 m", + "qualities": [ [ { "id": "HAMMER_SOFT", "level": 1 } ] ], + "components": [ + [ [ "frame_wood", 1 ] ], + [ [ "chunk_rubber", 16 ] ], + [ [ "glass_sheet", 2 ] ], + [ [ "superglue", 2 ], [ "duct_tape", 10 ] ] + ], + "pre_note": "Must be supported on at least two sides.", + "pre_special": "check_support", + "post_terrain": "t_wall_glass" + }, + { + "type": "construction", + "id": "constr_glass_wall_door", + "group": "build_glass_door", + "category": "CONSTRUCT", + "required_skills": [ [ "fabrication", 3 ] ], + "time": "60 m", + "qualities": [ [ { "id": "HAMMER_SOFT", "level": 1 } ] ], + "components": [ [ [ "frame_wood", 1 ] ], [ [ "chunk_rubber", 16 ] ], [ [ "glass_sheet", 2 ] ], [ [ "rag", 8 ] ] ], + "pre_note": "Must be supported on at least two sides.", + "pre_special": "check_support", + "post_terrain": "t_door_glass_c" } ] diff --git a/data/json/construction_group.json b/data/json/construction_group.json index 2ae0acb904d0d..d518d8cb74466 100644 --- a/data/json/construction_group.json +++ b/data/json/construction_group.json @@ -249,6 +249,11 @@ "id": "build_dumpster", "name": "Build Dumpster" }, + { + "type": "construction_group", + "id": "build_gravelbag_wall", + "name": "Build Gravelbag Wall" + }, { "type": "construction_group", "id": "build_earthbag_wall", @@ -259,6 +264,11 @@ "id": "build_entertainment_center", "name": "Build Entertainment Center" }, + { + "type": "construction_group", + "id": "build_exercise_machine", + "name": "Build Exercise Machine" + }, { "type": "construction_group", "id": "build_fence", @@ -284,6 +294,21 @@ "id": "build_fire_ring", "name": "Build Fire Ring" }, + { + "type": "construction_group", + "id": "build_treadmill_mechanical", + "name": "Build Gravity Treadmill" + }, + { + "type": "construction_group", + "id": "build_glass_door", + "name": "Build Glass Door" + }, + { + "type": "construction_group", + "id": "build_glass_wall", + "name": "Build Glass Wall" + }, { "type": "construction_group", "id": "build_high_end_of_a_concrete_ramp", @@ -349,6 +374,16 @@ "id": "build_makeshift_door", "name": "Build Makeshift Door" }, + { + "type": "construction_group", + "id": "build_mechanical_winch", + "name": "Build Mechanical Winch" + }, + { + "type": "construction_group", + "id": "build_ergometer_mechanical", + "name": "Build Mechanical Ergometer" + }, { "type": "construction_group", "id": "build_metal_bars", @@ -364,6 +399,11 @@ "id": "build_metal_door", "name": "Build Metal Door" }, + { + "type": "construction_group", + "id": "build_metal_gate", + "name": "Build Metal Gate" + }, { "type": "construction_group", "id": "build_metal_grate_over_a_window", @@ -764,6 +804,11 @@ "id": "build_workbench", "name": "Build Workbench" }, + { + "type": "construction_group", + "id": "build_woven_wattle_fence", + "name": "Build Woven Wattle Fence" + }, { "type": "construction_group", "id": "carpet_floor_green", @@ -819,6 +864,11 @@ "id": "dig_a_deep_pit", "name": "Dig a Deep Pit" }, + { + "type": "construction_group", + "id": "underground_pit", + "name": "Dig a Deep Pit Underground" + }, { "type": "construction_group", "id": "dig_a_shallow_pit", diff --git a/data/json/effect_on_condition.json b/data/json/effect_on_condition.json new file mode 100644 index 0000000000000..ce8a99fc4ff9d --- /dev/null +++ b/data/json/effect_on_condition.json @@ -0,0 +1,91 @@ +[ + { + "type": "effect_on_condition", + "id": "thunder", + "recurrence_min": 1, + "recurrence_max": 1, + "condition": { + "and": [ + { "or": [ { "is_weather": "thunder" }, { "is_weather": "lightning" } ] }, + { "one_in_chance": 50 }, + { "u_is_height": 0 } + ] + }, + "deactivate_condition": { "not": { "or": [ { "is_weather": "thunder" }, { "is_weather": "lightning" } ] } }, + "effect": [ + { "message": "You hear a distant rumble of thunder.", "sound": true }, + { "sound_effect": "thunder_far", "outdoor_event": true, "id": "environment" } + ] + }, + { + "type": "effect_on_condition", + "id": "lightning", + "recurrence_min": 1, + "recurrence_max": 1, + "condition": { "and": [ { "is_weather": "lightning" }, { "one_in_chance": 600 }, { "u_is_height": 0 } ] }, + "deactivate_condition": { "not": { "is_weather": "lightning" } }, + "effect": [ + { "message": "A flash of lightning illuminates your surroundings!" }, + { "sound_effect": "thunder_near", "id": "environment" }, + "lightning" + ] + }, + { + "type": "effect_on_condition", + "id": "acid_drizzle", + "recurrence_min": 1, + "recurrence_max": 1, + "condition": { "and": [ { "is_weather": "acid_drizzle" }, "u_is_outside", { "not": { "u_has_pain": 30 } } ] }, + "deactivate_condition": { "not": { "is_weather": "acid_drizzle" } }, + "effect": [ { "message": "The acid rain stings, but is mostly harmless for now…" }, { "u_mod_pain": 1 } ] + }, + { + "type": "effect_on_condition", + "id": "acid_rain", + "recurrence_min": 1, + "recurrence_max": 1, + "condition": { + "and": [ + { "is_weather": "acid_rain" }, + "u_is_outside", + { "not": { "u_has_pain": 100 } }, + { + "not": { "or": [ { "u_has_wielded_with_flag": "RAIN_PROTECT" }, { "u_has_worn_with_flag": "RAINPROOF" } ] } + } + ] + }, + "deactivate_condition": { "not": { "is_weather": "acid_rain" } }, + "effect": [ { "message": "The acid rain burns!" }, { "u_mod_pain": 3 } ] + }, + { + "type": "effect_on_condition", + "id": "snow", + "recurrence_min": 6, + "recurrence_max": 6, + "condition": { "and": [ { "is_weather": "snowing" }, "u_is_outside" ] }, + "deactivate_condition": { "not": { "is_weather": "snowing" } }, + "effect": [ { "u_add_wet": 10 } ] + }, + { + "type": "effect_on_condition", + "id": "snowstorm", + "recurrence_min": 6, + "recurrence_max": 6, + "condition": { "and": [ { "is_weather": "snowstorm" }, "u_is_outside" ] }, + "deactivate_condition": { "not": { "is_weather": "snowstorm" } }, + "effect": [ { "u_add_wet": 40 } ] + }, + { + "type": "effect_on_condition", + "id": "bio_drain", + "recurrence_min": "30 minutes", + "recurrence_max": "1 hours 30 minutes", + "condition": { "and": [ { "u_has_bionics": "bio_drain" }, { "u_has_power": "24 kJ" } ] }, + "deactivate_condition": { "not": { "u_has_bionics": "bio_drain" } }, + "effect": [ + { "message": "Your batteries discharge slightly.", "type": "bad" }, + { "sound_effect": "elec_crackle_low", "id": "bionics", "volume": 100 }, + { "u_add_power": "-25kJ" } + ] + } +] diff --git a/data/json/effects.json b/data/json/effects.json index 4d2cd3ded0f70..177060ecdfa8d 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -747,7 +747,7 @@ "id": "invisibility", "name": [ "Invisible" ], "desc": [ "You are invisible." ], - "flags": [ "EFFECT_INVISIBLE" ] + "flags": [ "INVISIBLE" ] }, { "type": "effect_type", @@ -2433,7 +2433,7 @@ "type": "effect_type", "id": "ignore_fall_damage", "//": "Used for translocation via teleporter_list as a way to avoid fall damage by teleporting Z levels", - "flags": [ "EFFECT_FEATHER_FALL" ] + "flags": [ "FEATHER_FALL" ] }, { "type": "effect_type", diff --git a/data/json/emit.json b/data/json/emit.json index bf9cd0d863dc0..3268490447224 100644 --- a/data/json/emit.json +++ b/data/json/emit.json @@ -72,6 +72,14 @@ "intensity": 3, "qty": 18 }, + { + "id": "emit_tear_gas_blast", + "type": "emit", + "//": "Large blast of tear gas (example: tear gas canister)", + "field": "fd_tear_gas", + "intensity": 3, + "qty": 200 + }, { "id": "emit_toxic_blast", "type": "emit", diff --git a/data/json/enchantments.json b/data/json/enchantments.json deleted file mode 100644 index 34e1b8d6ae50b..0000000000000 --- a/data/json/enchantments.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "type": "enchantment", - "id": "ENCH_INVISIBILITY", - "condition": "ACTIVE", - "ench_effects": [ { "effect": "invisibility", "intensity": 1 } ] - }, - { - "type": "enchantment", - "id": "ENCH_SHADOW_CLOUD", - "condition": "ACTIVE", - "ench_effects": [ { "effect": "invisibility", "intensity": 1 } ], - "emitter": "emit_shadow_field" - } -] diff --git a/data/json/flags.json b/data/json/flags.json index 8be9600699457..2fa6a3112c900 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -10,11 +10,6 @@ "type": "json_flag", "context": [ ] }, - { - "id": "EFFECT_INVISIBLE", - "context": [ ], - "type": "json_flag" - }, { "id": "ALARMCLOCK", "type": "json_flag", @@ -81,11 +76,6 @@ "//": "Blinds the wearer while worn, and provides nominal protection vs flashbang flashes.", "info": "This gear prevents you from seeing anything." }, - { - "id": "EFFECT_NIGHT_VISION", - "context": [ ], - "type": "json_flag" - }, { "id": "BLOCK_WHILE_WORN", "type": "json_flag", @@ -152,6 +142,24 @@ "type": "json_flag", "context": [ "GENERIC" ] }, + { + "id": "SCIENCE_CARD", + "type": "json_flag", + "context": [ "GENERIC" ], + "info": "You could probably use this to get into a secure science facility." + }, + { + "id": "MILITARY_CARD", + "type": "json_flag", + "context": [ "GENERIC" ], + "info": "You could probably use this to get into a secure military facility." + }, + { + "id": "INDUSTRIAL_CARD", + "type": "json_flag", + "context": [ "GENERIC" ], + "info": "You could probably use this to get into a secure industrial facility." + }, { "id": "SKINNED", "type": "json_flag", @@ -250,6 +258,12 @@ "context": [ "ARMOR", "TOOL_ARMOR" ], "info": "You can wear only one." }, + { + "id": "ONE_PER_LAYER", + "type": "json_flag", + "context": [ "ARMOR", "TOOL_ARMOR" ], + "info": "Only one item can be worn on this clothing layer." + }, { "id": "FANCY", "type": "json_flag", @@ -1012,56 +1026,6 @@ "context": [ "mutation" ], "//": "This mutation does not count toward thresholds at all." }, - { - "id": "EFFECT_FEATHER_FALL", - "context": [ ], - "type": "json_flag" - }, - { - "id": "EFFECT_BIO_IMMUNE", - "context": [ ], - "type": "json_flag" - }, - { - "id": "EFFECT_BASH_IMMUNE", - "context": [ ], - "type": "json_flag" - }, - { - "id": "EFFECT_CUT_IMMUNE", - "context": [ ], - "type": "json_flag" - }, - { - "id": "EFFECT_BULLET_IMMUNE", - "context": [ ], - "type": "json_flag" - }, - { - "id": "EFFECT_ACID_IMMUNE", - "context": [ ], - "type": "json_flag" - }, - { - "id": "EFFECT_STAB_IMMUNE", - "context": [ ], - "type": "json_flag" - }, - { - "id": "EFFECT_HEAT_IMMUNE", - "context": [ ], - "type": "json_flag" - }, - { - "id": "EFFECT_COLD_IMMUNE", - "context": [ ], - "type": "json_flag" - }, - { - "id": "EFFECT_ELECTRIC_IMMUNE", - "context": [ ], - "type": "json_flag" - }, { "id": "HIDDEN_HALLU", "type": "json_flag", @@ -1880,6 +1844,12 @@ "type": "json_flag", "context": [ "AMMO", "CONSUMABLE" ] }, + { + "id": "ACTIVATE_ON_PLACE", + "type": "json_flag", + "context": [ ], + "info": "This item will be activated when it is placed in mapgen." + }, { "id": "ACT_IN_FIRE", "type": "json_flag", diff --git a/data/json/flags/trap.json b/data/json/flags/trap.json new file mode 100644 index 0000000000000..f213b6d193ef2 --- /dev/null +++ b/data/json/flags/trap.json @@ -0,0 +1,12 @@ +[ + { + "type": "json_flag", + "id": "SONAR_DETECTABLE", + "context": [ ] + }, + { + "type": "json_flag", + "id": "CONVECTS_TEMPERATURE", + "context": [ ] + } +] diff --git a/data/json/furniture_and_terrain/furniture-appliances.json b/data/json/furniture_and_terrain/furniture-appliances.json index 905cfba830472..b3212250a5520 100644 --- a/data/json/furniture_and_terrain/furniture-appliances.json +++ b/data/json/furniture_and_terrain/furniture-appliances.json @@ -389,7 +389,8 @@ { "item": "cable", "charges": [ 1, 3 ] }, { "item": "pilot_light", "count": 1 } ] - } + }, + "crafting_pseudo_item": "fake_oven" }, { "type": "furniture", @@ -546,7 +547,7 @@ "id": "f_satellite", "name": "large satellite dish", "looks_like": "t_radio_tower", - "description": "A large concave metal panel with simple electronics used to receive signals from sattelites. While the hundreds of expensive machines orbitting the planet will likely continue to function indefinately, their various purposes have all been lost.", + "description": "A large, concave metal panel with simple electronics used to receive signals from satellites. While the hundreds of expensive machines orbiting the planet will likely continue to function indefinitely, their various purposes have all been lost.", "symbol": ")", "color": "white_green", "move_cost_mod": -1, diff --git a/data/json/furniture_and_terrain/furniture-barriers.json b/data/json/furniture_and_terrain/furniture-barriers.json index a0e4260978b67..218553bf485ef 100644 --- a/data/json/furniture_and_terrain/furniture-barriers.json +++ b/data/json/furniture_and_terrain/furniture-barriers.json @@ -20,6 +20,60 @@ "items": [ { "item": "2x4", "count": [ 2, 6 ] }, { "item": "nail", "charges": [ 4, 8 ] }, { "item": "splinter", "count": 1 } ] } }, + { + "type": "furniture", + "id": "f_gravelbag_half", + "name": "gravelbag barricade", + "symbol": "#", + "looks_like": "f_sandbag_half", + "bgcolor": "brown", + "description": "A low wall made of stacked gravelbags, uncommonly used to catch bullets and block flooding.", + "move_cost_mod": -1, + "coverage": 60, + "required_str": -1, + "flags": [ + "CLIMB_SIMPLE", + "TRANSPARENT", + "MOUNTABLE", + "BLOCKSDOOR", + "SHORT", + "EASY_DECONSTRUCT", + "THIN_OBSTACLE", + "CLIMBABLE", + "PERMEABLE" + ], + "examine_action": "chainfence", + "deconstruct": { "items": [ { "item": "gravelbag", "count": 16 } ] }, + "bash": { + "str_min": 12, + "str_max": 60, + "sound": "rrrip!", + "sound_fail": "whump.", + "items": [ { "item": "bag_canvas", "count": [ 10, 16 ] }, { "item": "material_gravel", "charges": [ 40, 48 ] } ] + } + }, + { + "type": "furniture", + "id": "f_gravelbag_wall", + "name": "gravelbag wall", + "symbol": "#", + "looks_like": "f_sandbag_wall", + "bgcolor": "brown", + "move_cost_mod": -1, + "coverage": 95, + "description": "A wall of stacked gravelbags, a bit taller than an average adult.", + "required_str": -1, + "flags": [ "NOITEM", "BLOCKSDOOR", "EASY_DECONSTRUCT", "MINEABLE", "BLOCK_WIND" ], + "deconstruct": { "items": [ { "item": "gravelbag", "count": 20 } ], "furn_set": "f_gravelbag_half" }, + "bash": { + "str_min": 24, + "str_max": 80, + "sound": "rrrip!", + "sound_fail": "whump.", + "furn_set": "f_gravelbag_half", + "items": [ { "item": "bag_canvas", "count": [ 15, 20 ] }, { "item": "material_gravel", "charges": [ 50, 60 ] } ] + } + }, { "type": "furniture", "id": "f_earthbag_half", diff --git a/data/json/furniture_and_terrain/furniture-recreation.json b/data/json/furniture_and_terrain/furniture-recreation.json index 7176dd4498e48..52759263e3958 100644 --- a/data/json/furniture_and_terrain/furniture-recreation.json +++ b/data/json/furniture_and_terrain/furniture-recreation.json @@ -123,7 +123,7 @@ "type": "furniture", "id": "f_arcade_machine", "name": "arcade machine", - "description": "A bulky upright arcade cabinet, brightly painted and slightyly worn with age. Useless for its intended purpose, it's bound to have valuable parts.", + "description": "A bulky upright arcade cabinet, brightly painted and slightly worn with age. Useless for its intended purpose, it's bound to have valuable parts.", "symbol": "6", "color": "red", "move_cost_mod": -1, @@ -211,7 +211,7 @@ "type": "furniture", "id": "f_ergometer", "name": "ergometer", - "description": "An exercise machine with a set of handles and plates meant to emulate rowing a boat. Without power it can't be operated, but it might have useful parts to be scavanged.", + "description": "An exercise machine with a set of handles and plates meant to emulate rowing a boat. Without power it can't be operated, but it might have useful parts to be scavenged.", "symbol": "5", "color": "dark_gray", "move_cost_mod": 2, diff --git a/data/json/furniture_and_terrain/furniture-roof.json b/data/json/furniture_and_terrain/furniture-roof.json index 372a2787c682d..e58575b943e9a 100644 --- a/data/json/furniture_and_terrain/furniture-roof.json +++ b/data/json/furniture_and_terrain/furniture-roof.json @@ -123,7 +123,7 @@ "type": "furniture", "id": "f_roof_turbine_vent", "name": "roof turbine vent", - "description": "A rotary wind turbine that will catch the wind and pull out air from inside. It is most commonly used for improving air cicrulation, particularly in poorly-ventilated areas like attics.", + "description": "A rotary wind turbine that will catch the wind and pull out air from inside. It is most commonly used for improving air circulation, particularly in poorly-ventilated areas like attics.", "symbol": "&", "color": "light_gray", "move_cost_mod": 2, diff --git a/data/json/furniture_and_terrain/furniture-storage.json b/data/json/furniture_and_terrain/furniture-storage.json index 65502b26f0ab0..f0949f3ed1c35 100644 --- a/data/json/furniture_and_terrain/furniture-storage.json +++ b/data/json/furniture_and_terrain/furniture-storage.json @@ -1128,7 +1128,7 @@ "type": "furniture", "id": "f_foot_locker", "name": "metal foot locker", - "description": "An metal stoarge box, capable of holding any number of things. The lid has has a small lock.", + "description": "A metal storage box, capable of holding any number of things. The lid has a small lock.", "symbol": "O", "color": "white", "move_cost_mod": -1, diff --git a/data/json/furniture_and_terrain/furniture-tools.json b/data/json/furniture_and_terrain/furniture-tools.json index 76f91b21d0d42..8c108583df019 100644 --- a/data/json/furniture_and_terrain/furniture-tools.json +++ b/data/json/furniture_and_terrain/furniture-tools.json @@ -497,7 +497,8 @@ { "item": "element", "count": [ 10, 25 ] }, { "item": "scrap", "count": [ 12, 16 ] } ] - } + }, + "crafting_pseudo_item": "fake_arc_furnace" }, { "type": "furniture", @@ -534,7 +535,8 @@ { "item": "plastic_chunk", "count": [ 4, 14 ] }, { "item": "motor_small", "count": 1 } ] - } + }, + "crafting_pseudo_item": "fake_drill_press" }, { "type": "furniture", @@ -572,7 +574,8 @@ { "item": "plastic_chunk", "count": [ 4, 14 ] }, { "item": "motor_small", "count": 1 } ] - } + }, + "crafting_pseudo_item": "fake_tablesaw" }, { "type": "furniture", @@ -610,7 +613,8 @@ { "item": "plastic_chunk", "count": [ 4, 14 ] }, { "item": "motor_small", "count": 1 } ] - } + }, + "crafting_pseudo_item": "fake_mitresaw" }, { "type": "furniture", @@ -647,7 +651,8 @@ { "item": "plastic_chunk", "count": [ 4, 14 ] }, { "item": "motor_small", "count": 1 } ] - } + }, + "crafting_pseudo_item": "fake_bandsaw" }, { "type": "furniture", @@ -684,7 +689,8 @@ { "item": "plastic_chunk", "count": [ 4, 14 ] }, { "item": "motor_small", "count": 1 } ] - } + }, + "crafting_pseudo_item": "fake_router_table" }, { "type": "furniture", @@ -721,7 +727,8 @@ { "item": "plastic_chunk", "count": [ 4, 14 ] }, { "item": "motor_small", "count": 1 } ] - } + }, + "crafting_pseudo_item": "fake_planer" }, { "type": "furniture", @@ -758,7 +765,8 @@ { "item": "plastic_chunk", "count": [ 4, 14 ] }, { "item": "motor_small", "count": 1 } ] - } + }, + "crafting_pseudo_item": "fake_jointer" }, { "type": "furniture", @@ -795,7 +803,8 @@ { "item": "chain", "count": [ 0, 1 ] }, { "item": "motor_small", "count": 1 } ] - } + }, + "crafting_pseudo_item": "fake_hydraulic_press" }, { "type": "furniture", @@ -829,7 +838,8 @@ { "item": "pipe", "count": [ 0, 4 ] }, { "item": "motor_small", "count": 1 } ] - } + }, + "crafting_pseudo_item": "fake_power_lathe" }, { "type": "furniture", @@ -872,7 +882,8 @@ { "item": "e_scrap", "count": [ 5, 10 ] }, { "item": "plastic_chunk", "count": [ 0, 2 ] } ] - } + }, + "crafting_pseudo_item": "fake_air_compressor" }, { "type": "furniture", diff --git a/data/json/furniture_and_terrain/terrain-doors.json b/data/json/furniture_and_terrain/terrain-doors.json index eac05445ffa89..d017cb560107d 100644 --- a/data/json/furniture_and_terrain/terrain-doors.json +++ b/data/json/furniture_and_terrain/terrain-doors.json @@ -578,7 +578,7 @@ "type": "terrain", "id": "t_door_white_b", "name": "damaged wood door", - "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few two by fours.", + "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few planks.", "symbol": "&", "looks_like": "t_door_b", "color": "brown", @@ -614,7 +614,7 @@ "type": "terrain", "id": "t_door_gray_b", "name": "damaged wood door", - "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few two by fours.", + "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few planks.", "symbol": "&", "looks_like": "t_door_b", "color": "brown", @@ -650,7 +650,7 @@ "type": "terrain", "id": "t_door_red_b", "name": "damaged wood door", - "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few two by fours.", + "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few planks.", "symbol": "&", "looks_like": "t_door_b", "color": "brown", @@ -686,7 +686,7 @@ "type": "terrain", "id": "t_door_green_b", "name": "damaged wood door", - "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few two by fours.", + "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few planks.", "symbol": "&", "looks_like": "t_door_b", "color": "brown", @@ -722,7 +722,7 @@ "type": "terrain", "id": "t_door_white_frame", "name": "empty door frame", - "description": "An empty door frame made from two by fours and nails. A variety of doors could be constructed here.", + "description": "An empty door frame made from planks and nails. A variety of doors could be constructed here.", "symbol": ".", "looks_like": "t_door_frame", "color": "brown", @@ -747,7 +747,7 @@ "type": "terrain", "id": "t_door_gray_frame", "name": "empty door frame", - "description": "An empty door frame made from two by fours and nails. A variety of doors could be constructed here.", + "description": "An empty door frame made from planks and nails. A variety of doors could be constructed here.", "symbol": ".", "looks_like": "t_door_frame", "color": "brown", @@ -772,7 +772,7 @@ "type": "terrain", "id": "t_door_red_frame", "name": "empty door frame", - "description": "An empty door frame made from two by fours and nails. A variety of doors could be constructed here.", + "description": "An empty door frame made from planks and nails. A variety of doors could be constructed here.", "symbol": ".", "looks_like": "t_door_frame", "color": "brown", @@ -797,7 +797,7 @@ "type": "terrain", "id": "t_door_green_frame", "name": "empty door frame", - "description": "An empty door frame made from two by fours and nails. A variety of doors could be constructed here.", + "description": "An empty door frame made from planks and nails. A variety of doors could be constructed here.", "symbol": ".", "looks_like": "t_door_frame", "color": "brown", @@ -1053,7 +1053,7 @@ "type": "terrain", "id": "t_door_b", "name": "damaged wood door", - "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few two by fours.", + "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few planks.", "symbol": "&", "looks_like": "t_door_c", "color": "brown", @@ -1089,7 +1089,7 @@ "type": "terrain", "id": "t_door_lab_b", "name": "damaged wood door", - "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few two by fours.", + "description": "A trashed wooden door, that is more of an obstacle than a door. Also, you can see right through it. It could be boarded up with a few planks.", "symbol": "&", "looks_like": "t_door_b", "color": "brown", @@ -1274,7 +1274,7 @@ "type": "terrain", "id": "t_rdoor_c", "name": "closed reinforced wood door", - "description": "Just like other wooden doors, except this one has layers of nailed in two by fours and additional hinge for reinforcement. It might be barricaded, but still susceptible to fire.", + "description": "Just like other wooden doors, except this one has layers of nailed in planks and additional hinge for reinforcement. It might be barricaded, but still susceptible to fire.", "symbol": "+", "looks_like": "t_door_c", "color": "red", @@ -1347,7 +1347,7 @@ "type": "terrain", "id": "t_rdoor_o", "name": "open reinforced wood door", - "description": "Just like other wooden doors, except this one has layers of nailed in two by fours for reinforcement. It might be fortified, but still susceptible to fire. It's open so it's not stopping anything right now.", + "description": "Just like other wooden doors, except this one has layers of nailed in planks for reinforcement. It might be fortified, but still susceptible to fire. It's open so it's not stopping anything right now.", "symbol": "'", "looks_like": "t_door_o", "color": "red", @@ -1574,7 +1574,7 @@ "type": "terrain", "id": "t_door_makeshift_c", "name": "closed makeshift door", - "description": "A makeshift screen consisting of two by fours bound together with vertical rope hanging from the top of the doorway. Could be easily taken down and re-purposed.", + "description": "A makeshift screen consisting of planks bound together with vertical rope hanging from the top of the doorway. Could be easily taken down and re-purposed.", "symbol": "+", "looks_like": "t_door_c", "color": "brown", @@ -1636,7 +1636,7 @@ "type": "terrain", "id": "t_door_makeshift_o", "name": "open makeshift door", - "description": "A makeshift screen consisting of two by fours bound together with rope hanging from the top of the doorway. Could be easily taken down and re-purposed. The planks have been rolled up and attached to the top of the doorway, allowing free movement through.", + "description": "A makeshift screen consisting of planks bound together with rope hanging from the top of the doorway. Could be easily taken down and re-purposed. The planks have been rolled up and attached to the top of the doorway, allowing free movement through.", "symbol": "'", "looks_like": "t_door_o", "color": "brown", @@ -1665,7 +1665,7 @@ "type": "terrain", "id": "t_door_frame", "name": "empty door frame", - "description": "An empty door frame made from two by fours and nails. A variety of doors could be constructed here.", + "description": "An empty door frame made from planks and nails. A variety of doors could be constructed here.", "symbol": ".", "looks_like": "t_floor", "color": "brown", @@ -1690,7 +1690,7 @@ "type": "terrain", "id": "t_door_lab_frame", "name": "empty door frame", - "description": "An empty door frame made from two by fours and nails. A variety of doors could be constructed here.", + "description": "An empty door frame made from planks and nails. A variety of doors could be constructed here.", "symbol": ".", "looks_like": "t_door_frame", "color": "brown", @@ -1859,7 +1859,7 @@ "type": "terrain", "id": "t_rdoor_boarded", "name": "boarded up reinforced door", - "description": "An additionally reinforced door of layered two by fours that has been boarded up with more wood to prevent it from opening. Still susceptible to fire.", + "description": "An additionally reinforced door of layered planks that has been boarded up with more wood to prevent it from opening. Still susceptible to fire.", "symbol": "#", "looks_like": "t_door_boarded", "color": "brown", @@ -1887,7 +1887,7 @@ "type": "terrain", "id": "t_rdoor_boarded_damaged", "name": "boarded up damaged reinforced door", - "description": "A battered and torn reinforced door with planks bursting from the joints. The boarded up two by fours are fragmented and in pieces, this doesn't look like an easy repair.", + "description": "A battered and torn reinforced door with planks bursting from the joints. The boarded up planks are fragmented and in pieces, this doesn't look like an easy repair.", "symbol": "#", "looks_like": "t_door_b", "color": "brown", diff --git a/data/json/furniture_and_terrain/terrain-fences-gates.json b/data/json/furniture_and_terrain/terrain-fences-gates.json index af76ca671678e..57d8494c1a30f 100644 --- a/data/json/furniture_and_terrain/terrain-fences-gates.json +++ b/data/json/furniture_and_terrain/terrain-fences-gates.json @@ -128,6 +128,82 @@ ] } }, + { + "type": "terrain", + "id": "t_retractable_gate_l", + "name": "locked retractable gate", + "description": "A retractable security gate. The latch on this one is locked. With the right tools, you could cut the metal fence or disable the lock.", + "symbol": "+", + "color": "cyan", + "move_cost": 0, + "flags": [ "TRANSPARENT", "PERMEABLE", "LOCKED", "PICKABLE", "THIN_OBSTACLE" ], + "connects_to": "WALL", + "examine_action": "locked_object_pickable", + "bash": { + "str_min": 17, + "str_max": 175, + "str_min_blocked": 15, + "str_max_blocked": 175, + "sound": "metal screeching!", + "sound_fail": "clang!", + "ter_set": "t_null", + "items": [ + { "item": "wire", "count": [ 8, 20 ] }, + { "item": "chain", "count": [ 1, 2 ] }, + { "item": "scrap", "count": [ 0, 12 ] } + ] + } + }, + { + "type": "terrain", + "id": "t_retractable_gate_c", + "name": "closed retractable gate", + "description": "A retractable security gate with a latch system to stay closed.", + "symbol": "+", + "color": "cyan", + "move_cost": 0, + "flags": [ "TRANSPARENT", "DOOR", "PERMEABLE", "THIN_OBSTACLE" ], + "connects_to": "WALL", + "open": "t_retractable_gate_o", + "bash": { + "str_min": 17, + "str_max": 175, + "str_min_blocked": 20, + "str_max_blocked": 100, + "sound": "metal screeching!", + "sound_fail": "clang!", + "ter_set": "t_null", + "items": [ + { "item": "wire", "count": [ 6, 15 ] }, + { "item": "chain", "count": [ 1, 2 ] }, + { "item": "scrap", "count": [ 0, 12 ] } + ] + } + }, + { + "type": "terrain", + "id": "t_retractable_gate_o", + "name": "open retractable gate", + "description": "A retractable security gate with a latch system to stay closed. The latch is undone, so the gate has retracted up.", + "symbol": ".", + "color": "cyan", + "move_cost": 2, + "flags": [ "TRANSPARENT", "FLAT", "ROAD" ], + "connects_to": "WALL", + "close": "t_retractable_gate_c", + "bash": { + "str_min": 5, + "str_max": 150, + "sound": "metal screeching!", + "sound_fail": "clang!", + "ter_set": "t_null", + "items": [ + { "item": "wire", "count": [ 6, 15 ] }, + { "item": "pipe", "count": [ 6, 15 ] }, + { "item": "scrap", "count": [ 0, 12 ] } + ] + } + }, { "type": "terrain", "id": "t_fencegate_c", @@ -363,6 +439,60 @@ "items": [ { "item": "2x4", "count": [ 1, 3 ] }, { "item": "nail", "charges": [ 2, 6 ] }, { "item": "splinter", "count": 1 } ] } }, + { + "type": "terrain", + "id": "t_wattle_fence", + "alias": [ "t_wattle_fence_h", "t_wattle_fence_v" ], + "name": "woven wattle fence", + "description": "A flimsy barrier made of woven sticks. Suitable for small animals.", + "symbol": "LINE_OXOX", + "color": "brown", + "looks_like": "t_fence", + "move_cost": 0, + "examine_action": "chainfence", + "flags": [ + "TRANSPARENT", + "DIGGABLE", + "FLAMMABLE_ASH", + "THIN_OBSTACLE", + "SHORT", + "NOITEM", + "PERMEABLE", + "UNSTABLE", + "CLIMBABLE", + "AUTO_WALL_SYMBOL", + "BURROWABLE" + ], + "connects_to": "WOODFENCE", + "deconstruct": { "ter_set": "t_fence_post", "items": [ { "item": "splinter", "count": 20 } ] }, + "bash": { + "str_min": 4, + "str_max": 20, + "sound": "crack.", + "sound_fail": "wham.", + "ter_set": "t_null", + "items": [ { "item": "pointy_stick", "count": [ 0, 2 ] }, { "item": "splinter", "count": [ 5, 15 ] } ] + } + }, + { + "type": "terrain", + "id": "t_wattle_fence_posts", + "name": "narrow fence posts", + "description": "A set of narrow wooden posts for a woven wattle fence.", + "symbol": "#", + "color": "brown", + "looks_like": "t_fence_post", + "move_cost": 2, + "flags": [ "TRANSPARENT", "THIN_OBSTACLE", "SHORT" ], + "bash": { + "str_min": 4, + "str_max": 20, + "sound": "crack.", + "sound_fail": "wham.", + "ter_set": "t_null", + "items": [ { "item": "stick", "count": [ 1, 4 ] } ] + } + }, { "type": "terrain", "id": "t_chainfence", diff --git a/data/json/furniture_and_terrain/terrain-floors-indoor.json b/data/json/furniture_and_terrain/terrain-floors-indoor.json index a28bdf373e96f..28f171cfee5db 100644 --- a/data/json/furniture_and_terrain/terrain-floors-indoor.json +++ b/data/json/furniture_and_terrain/terrain-floors-indoor.json @@ -46,6 +46,18 @@ ] } }, + { + "type": "terrain", + "id": "t_thconc_r", + "name": "concrete floor", + "description": "A bare and cold concrete floor with a streak of red paint, could still insulate from the outdoors but roof collapse is possible if supporting walls are broken down.", + "symbol": ".", + "color": "red", + "looks_like": "t_floor_red", + "move_cost": 2, + "roof": "t_flat_roof", + "copy-from": "t_thconc_floor" + }, { "type": "terrain", "id": "t_thconc_floor_olight", @@ -865,7 +877,82 @@ "sound_fail": "slap!", "sound_vol": 8, "sound_fail_vol": 4, - "ter_set": "t_dirt" + "ter_set": "t_null" } + }, + { + "type": "terrain", + "id": "t_floor_paper_noroof", + "name": "paper floor", + "description": "Floor made of pulpy mass, covered in sticky wasp saliva.", + "symbol": ".", + "looks_like": "t_dirt", + "color": "white", + "move_cost": 2, + "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "INDOORS", "FLAT", "ROAD" ], + "bash": { + "str_min": 1, + "str_max": 6, + "sound": "rrrrip!", + "sound_fail": "slap!", + "sound_vol": 8, + "sound_fail_vol": 4, + "ter_set": "t_open_air" + } + }, + { + "type": "terrain", + "id": "t_floor_paper_hard", + "name": "hard paper floor", + "description": "Hardened cardboard floor made of chewed up trees and resin mixed with wasp saliva.", + "symbol": ".", + "looks_like": "t_floor_paper", + "color": "light_gray", + "move_cost": 2, + "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "INDOORS", "FLAT" ], + "bash": { + "str_min": 15, + "str_max": 150, + "str_min_supported": 30, + "sound": "rrrrip!", + "sound_fail": "slap!", + "sound_vol": 10, + "sound_fail_vol": 6, + "ter_set": "t_open_air" + } + }, + { + "type": "terrain", + "id": "t_floor_brood_wasp", + "name": "paper brood cap", + "description": "A thin paper floor, protecting the pupatig wasp larva until it's ready to emerge.", + "looks_like": "t_floor_paper", + "symbol": ".", + "color": "white", + "move_cost": 2, + "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "INDOORS", "NO_FLOOR" ], + "bash": { + "str_min": 1, + "str_max": 6, + "sound": "rrrrip!", + "sound_fail": "slap!", + "sound_vol": 8, + "sound_fail_vol": 4, + "ter_set": "t_open_air" + } + }, + { + "type": "terrain", + "id": "t_floor_brood_fake", + "name": "open air", + "description": "This is open air.", + "//": "Actually, this is a thin membrane of bullshit to keep wasp eggs levitating.", + "symbol": " ", + "looks_like": "t_open_air", + "color": "i_cyan", + "move_cost": 2, + "trap": "tr_ledge", + "flags": [ "TRANSPARENT" ], + "examine_action": "ledge" } ] diff --git a/data/json/furniture_and_terrain/terrain-liquids.json b/data/json/furniture_and_terrain/terrain-liquids.json index fb3e646b576d1..30ffa1ce5953a 100644 --- a/data/json/furniture_and_terrain/terrain-liquids.json +++ b/data/json/furniture_and_terrain/terrain-liquids.json @@ -377,7 +377,7 @@ "id": "t_lake_bed_concrete_yellow", "//": "for eventual use with water z levels. Currently non-functional. Cement on the lake floor.", "name": "submerged concrete", - "description": "You are standing at the bottom of a body of fresh water on a pad of concrete with yelllow paint. With a watertight container, you could gather fresh water from here. Not safe to drink as is.", + "description": "You are standing at the bottom of a body of fresh water on a pad of yellow-painted concrete. With a watertight container, you could gather fresh water from here. Not safe to drink as is.", "symbol": "#", "looks_like": "t_pavement_y", "color": "yellow", @@ -397,7 +397,7 @@ "type": "terrain", "id": "t_water_hot", "name": "hot spring water", - "description": "Scalding hot water, not particulary safe for swimming. With a watertight container, you could gather fresh water from here. Not safe to drink as is.", + "description": "Scalding hot water, not particularly safe for swimming. With a watertight container, you could gather fresh water from here. Not safe to drink as is.", "symbol": "~", "color": "light_blue", "move_cost": 5, diff --git a/data/json/furniture_and_terrain/terrain-manufactured.json b/data/json/furniture_and_terrain/terrain-manufactured.json index 19993a1493ca3..ff8dbb0e067de 100644 --- a/data/json/furniture_and_terrain/terrain-manufactured.json +++ b/data/json/furniture_and_terrain/terrain-manufactured.json @@ -3,14 +3,13 @@ "type": "terrain", "id": "t_recycler", "name": "metal compactor", - "description": "A hydraulic compactor that can accept items made of various metals, and press them into basic shapes, ready for further crafting.", + "description": "A hydraulic machine for squeezing scrap into more compact chunks. Useless now that there's no power.", "looks_like": "f_machinery_heavy", "symbol": "&", "color": "green", "move_cost": 0, "max_volume": "2000 L", "flags": [ "TRANSPARENT", "REDUCE_SCENT", "PERMEABLE" ], - "examine_action": "recycle_compactor", "bash": { "str_min": 20, "str_max": 150, @@ -204,6 +203,54 @@ ] } }, + { + "type": "terrain", + "id": "t_avgas_pump", + "name": "avgas pump", + "looks_like": "t_gas_pump", + "description": "This pump is filled with avgas. If this gas dispenser doesn't give up the goods for free, you may have to pay at a nearby terminal.", + "symbol": "&", + "color": "red", + "move_cost": 0, + "coverage": 65, + "flags": [ "TRANSPARENT", "FLAMMABLE", "NOITEM", "SEALED", "CONTAINER", "REDUCE_SCENT", "PERMEABLE", "LIQUIDCONT" ], + "examine_action": "gaspump", + "bash": { + "str_min": 8, + "str_max": 150, + "sound": "crunch!", + "sound_fail": "clang!", + "ter_set": "t_avgas_pump_smashed", + "items": [ { "item": "scrap", "count": 1 } ] + } + }, + { + "type": "terrain", + "id": "t_avgas_pump_smashed", + "name": "smashed avgas pump", + "description": "You're not getting any avgas out of this pump any time soon. Some barbarian decided to take their frustration out on it.", + "symbol": "&", + "color": "light_green", + "looks_like": "t_gas_pump_smashed", + "move_cost": 0, + "coverage": 55, + "flags": [ "TRANSPARENT", "NOITEM", "REDUCE_SCENT", "PERMEABLE" ], + "bash": { + "str_min": 20, + "str_max": 150, + "explosive": 40, + "sound": "metal screeching!", + "sound_fail": "clang!", + "ter_set": "t_pavement", + "items": [ + { "item": "steel_lump", "prob": 50 }, + { "item": "steel_chunk", "count": [ 1, 4 ] }, + { "item": "pipe_fittings", "count": [ 2, 6 ] }, + { "item": "pipe", "count": [ 4, 8 ] }, + { "item": "scrap", "count": [ 3, 7 ] } + ] + } + }, { "type": "terrain", "id": "t_atm", diff --git a/data/json/furniture_and_terrain/terrain-mechanisms.json b/data/json/furniture_and_terrain/terrain-mechanisms.json index 224dc3876feb8..57c9c988205bb 100644 --- a/data/json/furniture_and_terrain/terrain-mechanisms.json +++ b/data/json/furniture_and_terrain/terrain-mechanisms.json @@ -249,7 +249,14 @@ "color": "pink", "move_cost": 0, "flags": [ "NOITEM", "CONNECT_TO_WALL" ], - "examine_action": "cardreader", + "examine_action": { + "type": "cardreader", + "flags": [ "SCIENCE_CARD" ], + "terrain_changes": { "t_door_metal_locked": "t_door_metal_c" }, + "query_msg": "Swipe your ID card?", + "success_msg": "You insert your ID card.\nThe nearby doors unlock.", + "redundant_msg": "The nearby doors are already opened." + }, "bash": { "str_min": 18, "str_max": 180, @@ -269,7 +276,14 @@ "color": "pink", "move_cost": 0, "flags": [ "NOITEM", "CONNECT_TO_WALL" ], - "examine_action": "cardreader", + "examine_action": { + "type": "cardreader", + "flags": [ "MILITARY_CARD" ], + "terrain_changes": { "t_door_metal_locked": "t_door_metal_c" }, + "query_msg": "Swipe your ID card?", + "success_msg": "You insert your ID card.\nThe nearby doors unlock.", + "redundant_msg": "The nearby doors are already opened." + }, "bash": { "str_min": 18, "str_max": 180, @@ -290,7 +304,14 @@ "color": "pink", "move_cost": 0, "flags": [ "NOITEM", "CONNECT_TO_WALL" ], - "examine_action": "cardreader", + "examine_action": { + "type": "cardreader", + "flags": [ "INDUSTRIAL_CARD" ], + "terrain_changes": { "t_door_metal_locked": "t_door_metal_c" }, + "query_msg": "Swipe your ID card?", + "success_msg": "You insert your ID card.\nThe nearby doors unlock.", + "redundant_msg": "The nearby doors are already opened." + }, "bash": { "str_min": 18, "str_max": 180, diff --git a/data/json/furniture_and_terrain/terrain-railroads.json b/data/json/furniture_and_terrain/terrain-railroads.json index 27f6c4e97dcb4..7a0441806cb88 100644 --- a/data/json/furniture_and_terrain/terrain-railroads.json +++ b/data/json/furniture_and_terrain/terrain-railroads.json @@ -13,12 +13,12 @@ "ter_set": "t_null", "sound": "crunch!", "sound_fail": "whump!", - "items": [ { "item": "pebble", "count": [ 1, 3 ] }, { "item": "sharp_rock", "count": [ 0, 1 ] } ] + "items": [ { "item": "material_gravel", "count": [ 5, 15 ] }, { "item": "sharp_rock", "count": [ 0, 1 ] } ] }, "deconstruct": { "ter_set": "t_pit_shallow", "items": [ - { "item": "pebble", "count": [ 75, 100 ] }, + { "item": "material_gravel", "count": [ 350, 500 ] }, { "item": "sharp_rock", "count": [ 0, 2 ] }, { "item": "rock", "count": [ 4, 12 ] } ] diff --git a/data/json/furniture_and_terrain/terrain-roofs.json b/data/json/furniture_and_terrain/terrain-roofs.json index 9557141b6151e..877a7fe5416cf 100644 --- a/data/json/furniture_and_terrain/terrain-roofs.json +++ b/data/json/furniture_and_terrain/terrain-roofs.json @@ -354,5 +354,27 @@ "ter_set": "t_open_air", "bash_below": true } + }, + { + "type": "terrain", + "id": "t_roof_paper_hard", + "name": "flat roof", + "description": "A flat surface made of hardened pulpy mass, covered in sticky wasp saliva.", + "symbol": ".", + "looks_like": "t_paper", + "color": "white", + "move_cost": 2, + "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "FLAT" ], + "bash": { + "str_min": 25, + "str_max": 150, + "str_min_supported": 40, + "sound": "rrrrip!", + "sound_fail": "slap!", + "sound_vol": 15, + "sound_fail_vol": 10, + "ter_set": "t_open_air", + "bash_below": true + } } ] diff --git a/data/json/furniture_and_terrain/terrain-traps.json b/data/json/furniture_and_terrain/terrain-traps.json index 6d6d564d03819..698951e420b17 100644 --- a/data/json/furniture_and_terrain/terrain-traps.json +++ b/data/json/furniture_and_terrain/terrain-traps.json @@ -50,7 +50,7 @@ "type": "terrain", "id": "t_pit_covered", "name": "covered pit", - "description": "A deep pit with a two by four placed across it, looks sturdy enough to cross safely or the plank could be removed to turn it back into trap fall.", + "description": "A deep pit with a plank placed across it, looks sturdy enough to cross safely or the plank could be removed to turn it back into trap fall.", "symbol": "#", "color": "light_red", "move_cost": 2, @@ -75,7 +75,7 @@ "type": "terrain", "id": "t_pit_spiked_covered", "name": "covered spiked pit", - "description": "Menacing with sharp spears along the bottom, this pit has a plank across it to allow someone or something to cross safely. The two by four could be removed to revert it back into a trap.", + "description": "Menacing with sharp spears along the bottom, this pit has a plank across it to allow someone or something to cross safely. The plank could be removed to revert it back into a trap.", "symbol": "#", "color": "light_red", "move_cost": 2, @@ -100,7 +100,7 @@ "type": "terrain", "id": "t_pit_glass_covered", "name": "covered glass pit", - "description": "A two by four has been placed carefully to allow traversal over this ditch full of large glass shards. The wooden board could be removed so it couldn't be safely crossed.", + "description": "A plank has been placed carefully to allow traversal over this ditch full of large glass shards. The wooden board could be removed so it couldn't be safely crossed.", "symbol": "#", "color": "light_cyan", "move_cost": 2, diff --git a/data/json/furniture_and_terrain/terrain-walls.json b/data/json/furniture_and_terrain/terrain-walls.json index 69f2cb631d5d0..bdbc377be1f88 100644 --- a/data/json/furniture_and_terrain/terrain-walls.json +++ b/data/json/furniture_and_terrain/terrain-walls.json @@ -40,7 +40,7 @@ "id": "t_wall_half", "name": "half-built wall", "looks_like": "t_wall", - "description": "An incomplete wall of refined wood, dotted with carefully placed nails to provide proper support. It requires some more two by fours and nails before it'd be considered a suitable wall.", + "description": "An incomplete wall of refined wood, dotted with carefully placed nails to provide proper support. It requires some more planks and nails before it'd be considered a suitable wall.", "symbol": "#", "color": "light_red", "move_cost": 4, @@ -649,7 +649,7 @@ "id": "t_wall_wood_chipped", "name": "chipped wood wall", "looks_like": "t_wall_wood", - "description": "A wall of aligned two by fours that's starting to crack and break open. Some cut wood and a number of nails could patch this up quick.", + "description": "A wall of aligned planks that's starting to crack and break open. Some cut wood and a number of nails could patch this up quick.", "symbol": "LINE_OXOX", "color": "light_red", "move_cost": 0, @@ -1160,7 +1160,44 @@ "sound_fail": "slap!", "sound_vol": 8, "sound_fail_vol": 4, - "ter_set": "t_null" + "ter_set": "t_floor_paper" + } + }, + { + "type": "terrain", + "id": "t_paper_brood", + "copy-from": "t_paper", + "symbol": "#", + "color": "white", + "flags": [ "FLAMMABLE_ASH", "NOITEM", "WALL", "NO_SCENT" ], + "bash": { + "str_min": 1, + "str_max": 6, + "sound": "rrrrip!", + "sound_fail": "slap!", + "sound_vol": 8, + "sound_fail_vol": 4, + "ter_set": "t_floor_paper_noroof" + } + }, + { + "type": "terrain", + "id": "t_paper_hard", + "name": "layered paper wall", + "description": "A thick wall of layered tree pulp and wasp saliva, hardened to withstand the elements. You could break through this, but it won't be easy.", + "symbol": "#", + "color": "dark_gray", + "move_cost": 0, + "coverage": 100, + "flags": [ "FLAMMABLE_HARD", "NOITEM", "MINEABLE", "WALL", "NO_SCENT" ], + "bash": { + "str_min": 30, + "str_max": 150, + "sound": "rrrrip!", + "sound_fail": "slap!", + "sound_vol": 20, + "sound_fail_vol": 10, + "ter_set": "t_floor_paper_hard" } }, { @@ -1352,6 +1389,26 @@ "items": [ { "item": "rock", "count": [ 10, 22 ] } ] } }, + { + "type": "terrain", + "id": "t_bollard", + "name": "bollard", + "looks_like": "t_fence_post", + "description": "A set of concrete and steel bollards designed to harden areas against vehicle ram attacks. It requires a few more steps to walk in between the bollards sticking up out of the ground.", + "symbol": "1", + "color": "light_gray", + "move_cost": 6, + "coverage": 20, + "flags": [ "TRANSPARENT", "WALL", "SHORT", "PERMEABLE" ], + "bash": { + "str_min": 350, + "str_max": 650, + "sound": "crash!", + "sound_fail": "whump!", + "ter_set": "t_sidewalk", + "items": [ { "item": "rock", "count": [ 6, 13 ] }, { "item": "steel_chunk", "count": [ 1, 3 ] } ] + } + }, { "type": "terrain", "id": "t_support_l", @@ -1462,5 +1519,25 @@ "ter_set": "t_null", "items": [ { "item": "rock", "count": [ 3, 8 ] }, { "item": "pebble", "count": [ 20, 38 ] } ] } + }, + { + "type": "terrain", + "id": "t_pillar", + "name": "pillar", + "looks_like": "t_column", + "description": "A concrete column that helps keep the mine's ceiling and walls from collapsing.", + "symbol": "1", + "color": "light_gray", + "move_cost": 0, + "coverage": 80, + "flags": [ "WALL", "PERMEABLE", "MINEABLE" ], + "bash": { + "str_min": 120, + "str_max": 200, + "sound": "crash!", + "sound_fail": "whump!", + "ter_set": "t_reb_cage", + "items": [ { "item": "rock", "count": [ 10, 22 ] } ] + } } ] diff --git a/data/json/furniture_and_terrain/terrain-windows.json b/data/json/furniture_and_terrain/terrain-windows.json index 0953c8eb9cad3..d1d8a7ac35fce 100644 --- a/data/json/furniture_and_terrain/terrain-windows.json +++ b/data/json/furniture_and_terrain/terrain-windows.json @@ -372,7 +372,7 @@ "id": "t_window_empty", "name": "empty window", "roof": "t_flat_roof", - "description": "An empty window frame consisting of two by fours and nails. You could install a sheet of glass, or even board it up for protection. You could also convert it into a wall if you took the time to construct it.", + "description": "An empty window frame consisting of planks and nails. You could install a sheet of glass, or even board it up for protection. You could also convert it into a wall if you took the time to construct it.", "symbol": "0", "color": "yellow", "move_cost": 4, @@ -427,7 +427,7 @@ "type": "terrain", "id": "t_window_boarded", "name": "boarded up window", - "description": "A glass window that has been covered with nailed down planks, blocking sunlight and visibility. It's not much stronger, but it could be further reinforced with strategically placed two by fours.", + "description": "A glass window that has been covered with nailed down planks, blocking sunlight and visibility. It's not much stronger, but it could be further reinforced with strategically placed planks.", "symbol": "#", "color": "brown", "move_cost": 0, @@ -454,7 +454,7 @@ "id": "t_window_boarded_noglass", "name": "boarded up window", "looks_like": "t_window_boarded", - "description": "An empty window frame that has been covered with nailed down planks, blocking sunlight and visibility. It's not much stronger, but it could be further reinforced with strategically placed two by fours.", + "description": "An empty window frame that has been covered with nailed down planks, blocking sunlight and visibility. It's not much stronger, but it could be further reinforced with strategically placed planks.", "symbol": "#", "color": "brown", "move_cost": 0, @@ -1648,7 +1648,7 @@ "type": "terrain", "id": "t_reinforced_double_pane_glass", "name": "Reinforced double glazed glass window", - "description": "A reinfoced double glazed window inserted into a frame. The outer layer comprises a pane of reinforced glass for extra security.", + "description": "A reinforced, double-glazed window inserted into a frame. For extra security, the outer layer is reinforced glass.", "looks_like": "t_window_no_curtains", "symbol": "\"", "color": "light_cyan", @@ -2908,7 +2908,7 @@ "type": "terrain", "id": "t_plastic_window_with_curtain", "name": "Plastic window with a curtain", - "description": "A makeshift window with a closed curtain. comprising a sheet of translucent, rigid plastic secured in a wooden frame. There are sheets drawn across it to block the light. It'll do in a pinch.", + "description": "A makeshift window with a closed curtain. It's a sheet of translucent, rigid plastic secured in a wooden frame. There are sheets drawn across it to block the light. It'll do in a pinch.", "looks_like": "t_curtains", "symbol": "|", "color": "light_blue", @@ -2946,7 +2946,7 @@ "type": "terrain", "id": "t_plastic_window_with_curtain_open_window_closed", "name": "Plastic window with a curtain", - "description": "A makeshift window with a opened curtain. comprising a sheet of translucent, rigid plastic secured in a wooden frame, with sheets strung about it to block the light as required. It'll do in a pinch.", + "description": "A makeshift window with a opened curtain. It's a sheet of translucent, rigid plastic secured in a wooden frame, with sheets strung about it to block the light as required. It'll do in a pinch.", "looks_like": "t_window_domestic", "symbol": "|", "color": "light_blue", @@ -2993,8 +2993,8 @@ { "type": "terrain", "id": "t_plastic_window_with_curtain_open", - "name": "Plastic window With a curtain", - "description": "An opened makeshift window with a opened curtain. comprising a sheet of translucent, rigid plastic secured in a wooden frame, with sheets strung about it to block the light as required. It'll do in a pinch.", + "name": "Plastic window with open curtains", + "description": "An open makeshift window with open curtains. It's a sheet of translucent, rigid plastic secured in a wooden frame, with sheets strung about it to block the light as required. It'll do in a pinch.", "looks_like": "t_window_open", "symbol": "|", "color": "light_blue", @@ -3137,7 +3137,7 @@ "type": "terrain", "id": "t_reinforced_plastic_window_with_curtain", "name": "Reinforced plastic window with a curtain", - "description": "A makeshift window with a closed curtain. comprising three sheets of translucent, rigid plastic secured in a wooden frame. It'll do in a pinch.", + "description": "A makeshift reinforced window with a closed curtain. It's three sheets of translucent, rigid plastic secured in a wooden frame. There are sheets drawn across it to block the light. It'll do in a pinch.", "looks_like": "t_curtains", "symbol": "|", "color": "light_blue", @@ -3187,8 +3187,8 @@ { "type": "terrain", "id": "t_reinforced_plastic_window_with_curtain_open_window_closed", - "name": "Reinforced plastic window with a curtain", - "description": "A makeshift window with a opened curtain. comprising three sheets of translucent, rigid plastic secured in a wooden frame. It'll do in a pinch.", + "name": "Reinforced plastic window with an open curtain", + "description": "A makeshift reinforced window with a opened curtain. It's three sheets of translucent, rigid plastic secured in a wooden frame, with sheets strung about it to block the light as required. It'll do in a pinch.", "looks_like": "t_window_domestic", "symbol": "|", "color": "light_blue", @@ -3240,8 +3240,8 @@ { "type": "terrain", "id": "t_reinforced_plastic_window_with_curtain_open", - "name": "Reinforced plastic window With a curtain", - "description": "An open makeshift window with a opened curtain. comprising three sheets of translucent, rigid plastic secured in a wooden frame. It'll do in a pinch.", + "name": "Reinforced plastic window with an open curtain", + "description": "An open makeshift reinforced window with a opened curtain. It's three sheets of translucent, rigid plastic secured in a wooden frame, with sheets strung about it to block the light as required. It'll do in a pinch.", "looks_like": "t_window_open", "symbol": "|", "color": "light_blue", diff --git a/data/json/harvest.json b/data/json/harvest.json index f8658ec5c88fd..ec4704e512648 100644 --- a/data/json/harvest.json +++ b/data/json/harvest.json @@ -998,14 +998,14 @@ "type": "harvest", "message": "", "entries": [ - { "drop": "mutant_meat", "type": "flesh", "mass_ratio": 0.1 }, + { "drop": "mutant_meat", "type": "flesh", "mass_ratio": 0.3 }, { "drop": "mutant_blood", "type": "blood", "mass_ratio": 0.1 }, - { "drop": "sinew", "type": "bone", "mass_ratio": 0.01 }, + { "drop": "sinew", "type": "bone", "mass_ratio": 0.003 }, { "drop": "endochitin", "type": "bone", "mass_ratio": 0.01 }, - { "drop": "mutant_bug_hydrogen_sacs", "type": "flesh", "mass_ratio": 0.2 }, - { "drop": "mutant_bug_lungs", "type": "flesh", "mass_ratio": 0.0035 }, - { "drop": "mutant_bug_organs", "type": "offal", "mass_ratio": 0.015 }, - { "drop": "chitin_piece", "type": "bone", "mass_ratio": 0.01 } + { "drop": "mutant_bug_hydrogen_sacs", "type": "flesh", "mass_ratio": 0.1 }, + { "drop": "mutant_bug_lungs", "type": "flesh", "mass_ratio": 0.01 }, + { "drop": "mutant_bug_organs", "type": "offal", "mass_ratio": 0.03 }, + { "drop": "chitin_piece", "type": "bone", "mass_ratio": 0.1 } ] }, { @@ -1073,17 +1073,36 @@ { "id": "arachnid_wasp", "type": "harvest", - "message": "There's a faintly hairy, skin-like membrane, covered in blood vessels, beneath the chitin of this creature. Inside it is a bundle of bubble-like tissue sacs that appear to be floating, which doesn't fit with what you know about pre-Cataclysm wasps.", + "message": "", "entries": [ - { "drop": "mutant_meat", "type": "flesh", "mass_ratio": 0.33 }, - { "drop": "sinew", "type": "bone", "mass_ratio": 0.01 }, - { "drop": "endochitin", "type": "bone", "mass_ratio": 0.1 }, + { "drop": "mutant_meat", "type": "flesh", "mass_ratio": 0.3 }, + { "drop": "mutant_blood", "type": "blood", "mass_ratio": 0.1 }, + { "drop": "sinew", "type": "bone", "mass_ratio": 0.003 }, + { "drop": "endochitin", "type": "bone", "mass_ratio": 0.01 }, { "drop": "mutant_bug_hydrogen_sacs", "type": "flesh", "mass_ratio": 0.1 }, - { "drop": "mutant_bug_lungs", "type": "flesh", "mass_ratio": 0.0035 }, - { "drop": "mutant_bug_organs", "type": "offal", "mass_ratio": 0.015 }, + { "drop": "mutant_bug_lungs", "type": "flesh", "mass_ratio": 0.01 }, + { "drop": "mutant_bug_organs", "type": "offal", "mass_ratio": 0.03 }, + { "drop": "wasp_sting", "base_num": [ 0, 1 ], "type": "bone" }, + { "drop": "chitin_piece", "type": "bone", "mass_ratio": 0.1 }, + { "drop": "venom_wasp", "type": "offal", "base_num": [ 0, 3 ], "scale_num": [ 0, 2 ] } + ] + }, + { + "id": "arachnid_wasp_queen", + "type": "harvest", + "message": "", + "entries": [ + { "drop": "mutant_meat", "type": "flesh", "mass_ratio": 0.2 }, + { "drop": "mutant_blood", "type": "blood", "mass_ratio": 0.1 }, + { "drop": "sinew", "type": "bone", "mass_ratio": 0.003 }, + { "drop": "endochitin", "type": "bone", "mass_ratio": 0.01 }, + { "drop": "mutant_bug_hydrogen_sacs", "type": "flesh", "mass_ratio": 0.1 }, + { "drop": "mutant_bug_lungs", "type": "flesh", "mass_ratio": 0.01 }, + { "drop": "mutant_bug_organs", "type": "offal", "mass_ratio": 0.03 }, { "drop": "wasp_sting", "base_num": [ 0, 1 ], "type": "bone" }, { "drop": "chitin_piece", "type": "bone", "mass_ratio": 0.1 }, - { "drop": "venom_wasp", "type": "offal", "mass_ratio": 0.005 } + { "drop": "egg_wasp", "type": "offal", "base_num": [ 10, 30 ], "scale_num": [ 5, 5 ] }, + { "drop": "venom_wasp", "type": "offal", "base_num": [ 5, 25 ], "scale_num": [ 5, 2 ] } ] }, { @@ -1294,6 +1313,25 @@ { "drop": "bone_tainted", "type": "bone", "mass_ratio": 0.1 } ] }, + { + "id": "CBM_SCI_FERAL", + "type": "harvest", + "entries": [ + { + "drop": "bionics_sci", + "type": "bionic_group", + "flags": [ "NO_STERILE", "NO_PACKED" ], + "faults": [ "fault_bionic_salvaged" ] + }, + { "drop": "human_flesh", "type": "flesh", "mass_ratio": 0.2 }, + { "drop": "hstomach", "scale_num": [ 1, 1 ], "max": 1, "type": "offal" }, + { "drop": "human_fat", "type": "flesh", "mass_ratio": 0.1 }, + { "drop": "blood", "type": "blood", "mass_ratio": 0.1 }, + { "drop": "bone_human", "type": "bone", "mass_ratio": 0.12 }, + { "drop": "sinew", "type": "bone", "mass_ratio": 0.001 }, + { "drop": "raw_hleather", "type": "skin", "mass_ratio": 0.01 } + ] + }, { "id": "CBM_TECH", "type": "harvest", diff --git a/data/json/itemgroups/Clothing_Gear/clothing.json b/data/json/itemgroups/Clothing_Gear/clothing.json index 92b6d2762745f..38006e9cecf33 100644 --- a/data/json/itemgroups/Clothing_Gear/clothing.json +++ b/data/json/itemgroups/Clothing_Gear/clothing.json @@ -3015,6 +3015,7 @@ { "distribution": [ { "item": "case_violin", "prob": 5 }, + { "item": "bookstrap", "prob": 5 }, { "item": "quiver", "prob": 20 }, { "item": "quiver_large", "prob": 15 }, { "item": "solarpack", "prob": 5 }, diff --git a/data/json/itemgroups/Clothing_Gear/gear_civilian.json b/data/json/itemgroups/Clothing_Gear/gear_civilian.json index ee282114dc97d..b9021c3d1629e 100644 --- a/data/json/itemgroups/Clothing_Gear/gear_civilian.json +++ b/data/json/itemgroups/Clothing_Gear/gear_civilian.json @@ -100,6 +100,7 @@ [ "slingpack", 19 ], [ "travelpack", 10 ], [ "petpack", 1 ], + [ "bookstrap", 1 ], [ "pockknife", 14 ], [ "roller_shoes_off", 10 ], [ "knife_swissarmy", 10 ], diff --git a/data/json/itemgroups/Clothing_Gear/wallets.json b/data/json/itemgroups/Clothing_Gear/wallets.json index 5e74971899181..92020cbd4f882 100644 --- a/data/json/itemgroups/Clothing_Gear/wallets.json +++ b/data/json/itemgroups/Clothing_Gear/wallets.json @@ -41,7 +41,7 @@ { "item": "coin_quarter", "prob": 10, "count": [ 1, 2 ] }, { "item": "coin_nickel", "prob": 10, "count": [ 1, 2 ] }, { "group": "discount_cards", "prob": 40, "count": [ 1, 3 ] }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, [ "scorecard", 20 ] ] }, @@ -57,7 +57,7 @@ { "item": "coin_quarter", "prob": 10, "count": [ 1, 2 ] }, { "item": "coin_nickel", "prob": 10, "count": [ 1, 2 ] }, { "group": "discount_cards", "prob": 40, "count": [ 1, 3 ] }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, [ "scorecard", 20 ] ] }, @@ -73,7 +73,7 @@ { "item": "coin_quarter", "prob": 10, "count": [ 1, 2 ] }, { "item": "coin_nickel", "prob": 10, "count": [ 1, 2 ] }, { "group": "discount_cards", "prob": 40, "count": [ 1, 3 ] }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, [ "scorecard", 20 ] ] }, @@ -88,7 +88,7 @@ { "item": "coin_quarter", "prob": 50, "count": [ 2, 5 ] }, { "item": "coin_nickel", "prob": 0, "count": [ 1, 6 ] }, { "group": "discount_cards", "prob": 10 }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, [ "scorecard", 40 ] ] }, @@ -103,7 +103,7 @@ { "item": "money_ten", "prob": 100, "count": [ 5, 10 ] }, { "item": "money_twenty", "prob": 100, "count": [ 5, 10 ] }, { "group": "discount_cards", "prob": 60, "count": [ 2, 5 ] }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, [ "scorecard", 5 ] ] }, @@ -120,7 +120,7 @@ { "item": "coin_quarter", "prob": 10, "count": [ 1, 2 ] }, { "item": "coin_nickel", "prob": 10, "count": [ 1, 2 ] }, { "group": "discount_cards", "prob": 40, "count": [ 1, 3 ] }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, [ "scorecard", 20 ] ] }, @@ -137,7 +137,7 @@ { "item": "coin_quarter", "prob": 10, "count": [ 1, 2 ] }, { "item": "coin_nickel", "prob": 10, "count": [ 1, 2 ] }, { "group": "discount_cards", "prob": 40, "count": [ 1, 3 ] }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, [ "scorecard", 20 ] ] }, @@ -154,7 +154,7 @@ { "item": "coin_quarter", "prob": 10, "count": [ 1, 2 ] }, { "item": "coin_nickel", "prob": 10, "count": [ 1, 2 ] }, { "group": "discount_cards", "prob": 40, "count": [ 1, 3 ] }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, { "item": "labmap", "prob": 20 }, [ "scorecard", 20 ] ] @@ -172,7 +172,7 @@ { "item": "coin_quarter", "prob": 10, "count": [ 1, 2 ] }, { "item": "coin_nickel", "prob": 10, "count": [ 1, 2 ] }, { "group": "discount_cards", "prob": 40, "count": [ 1, 3 ] }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, { "item": "labmap", "prob": 20 }, [ "scorecard", 20 ] ] @@ -190,7 +190,7 @@ { "item": "money_ten", "prob": 100, "count": [ 5, 10 ] }, { "item": "money_twenty", "prob": 100, "count": [ 5, 10 ] }, { "group": "discount_cards", "prob": 60, "count": [ 2, 5 ] }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, { "item": "labmap", "prob": 20 }, [ "scorecard", 5 ] ] @@ -208,7 +208,7 @@ { "item": "coin_quarter", "prob": 10, "count": [ 1, 2 ] }, { "item": "coin_nickel", "prob": 10, "count": [ 1, 2 ] }, { "group": "discount_cards", "prob": 40, "count": [ 1, 3 ] }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, [ "scorecard", 20 ] ] }, @@ -225,7 +225,7 @@ { "item": "coin_quarter", "prob": 10, "count": [ 1, 2 ] }, { "item": "coin_nickel", "prob": 10, "count": [ 1, 2 ] }, { "group": "discount_cards", "prob": 40, "count": [ 1, 3 ] }, - { "item": "condom", "prob": 1 }, + { "item": "condom", "prob": 10 }, [ "scorecard", 20 ] ] }, diff --git a/data/json/itemgroups/Food/food.json b/data/json/itemgroups/Food/food.json index 6a3aef1dd55ea..593a5c50e8bef 100644 --- a/data/json/itemgroups/Food/food.json +++ b/data/json/itemgroups/Food/food.json @@ -39,6 +39,7 @@ { "item": "dry_meat", "prob": 10 }, { "item": "dry_fish", "prob": 10 }, { "item": "dry_veggy", "prob": 10 }, + { "item": "dry_corn", "prob": 1 }, { "item": "dry_fruit", "prob": 10 }, { "item": "oatmeal", "prob": 40 }, { "item": "fruit_leather", "prob": 15 }, @@ -46,7 +47,10 @@ { "item": "dry_beans", "prob": 40 }, { "item": "dry_lentils", "prob": 30 }, { "item": "dry_rice", "prob": 40 }, - { "item": "freeze_dried_meal", "prob": 10 } + { "item": "freeze_dried_meal", "prob": 10 }, + { "item": "gelatin_powder", "prob": 40 }, + { "item": "chem_agar", "prob": 15 }, + { "item": "gelatin_dessert_powder", "prob": 40 } ] }, { @@ -131,6 +135,7 @@ { "item": "apple_canned", "prob": 1 }, { "item": "can_cheese", "prob": 1 }, { "item": "fish_canned", "prob": 1 }, + { "item": "brown_bread", "prob": 1 }, { "item": "can_spam", "prob": 1 }, { "item": "can_sardine", "prob": 1 }, { "item": "ravioli", "prob": 1 }, @@ -186,6 +191,7 @@ { "item": "can_chowder", "prob": 35 }, { "item": "can_herring", "prob": 30 }, { "item": "can_chicken", "prob": 40 }, + { "item": "brown_bread", "prob": 20 }, { "item": "broth", "prob": 15 }, { "item": "crackers", "prob": 10 }, { "item": "oyster_crackers", "prob": 10 }, @@ -216,6 +222,7 @@ { "item": "dry_meat", "prob": 10 }, { "item": "dry_fish", "prob": 10 }, { "item": "dry_veggy", "prob": 10 }, + { "item": "dry_corn", "prob": 1 }, { "item": "dry_fruit", "prob": 10 }, { "item": "oatmeal", "prob": 40 }, { "item": "toastem", "prob": 30 }, @@ -426,6 +433,7 @@ { "item": "candy", "prob": 70 }, { "item": "candy2", "prob": 70 }, { "item": "candy3", "prob": 70 }, + { "item": "candy4", "prob": 70 }, { "item": "cow_candy", "prob": 20 }, { "item": "maltballs", "prob": 60 }, { "item": "mintpatties", "prob": 60 }, @@ -553,7 +561,8 @@ { "item": "pie", "prob": 20 }, { "item": "pie_veggy", "prob": 18 }, { "item": "pie_meat", "prob": 18 }, - { "item": "pie_maple", "prob": 9 } + { "item": "pie_maple", "prob": 9 }, + { "item": "gelatin_dessert_processed", "prob": 40 } ] }, { diff --git a/data/json/itemgroups/Locations_MapExtras/locations.json b/data/json/itemgroups/Locations_MapExtras/locations.json index 7d728d02b06ca..b255b6fa67157 100644 --- a/data/json/itemgroups/Locations_MapExtras/locations.json +++ b/data/json/itemgroups/Locations_MapExtras/locations.json @@ -1218,6 +1218,26 @@ [ "tool_anfo_charge", 2 ] ] }, + { + "type": "item_group", + "id": "mine_materials", + "subtype": "collection", + "entries": [ + { "item": "coal_lump", "prob": 20, "count": [ 1, 10 ] }, + { "item": "rock", "prob": 40, "count": [ 1, 10 ] }, + { "item": "rock_large", "prob": 10, "count": [ 1, 10 ] }, + { "item": "rock_flaking", "prob": 20, "count": [ 1, 10 ] }, + { "item": "material_shrd_limestone", "prob": 40, "count": [ 1, 10 ] }, + { "item": "material_limestone", "prob": 40, "count": [ 1, 10 ] }, + { "item": "material_niter", "prob": 5, "count": [ 1, 10 ] }, + { "item": "material_sand", "prob": 50, "charges": 500, "container-item": "bag_canvas" }, + { "item": "material_soil", "prob": 50, "charges": 500, "container-item": "bag_canvas" }, + { "item": "chunk_sulfur", "prob": 5, "count": [ 1, 10 ] }, + { "item": "material_rocksalt", "prob": 5, "count": [ 1, 10 ] }, + { "item": "material_rhodonite", "prob": 5, "count": [ 1, 10 ] }, + { "item": "material_zincite", "prob": 5, "count": [ 1, 10 ] } + ] + }, { "type": "item_group", "id": "mine_equipment", @@ -1514,7 +1534,8 @@ { "item": "chain", "prob": 10 }, { "item": "glass_shiv", "prob": 30 }, { "item": "bottle_glass", "prob": 5 }, - { "item": "knuckle_brass", "prob": 10 }, + { "item": "knuckle_brass", "prob": 7 }, + { "item": "knuckle_steel_forged", "prob": 3 }, { "item": "switchblade", "prob": 4 }, { "item": "throwing_knife", "prob": 5 }, { "item": "tazer", "prob": 1, "charges": [ 0, 100 ] }, diff --git a/data/json/itemgroups/Locations_MapExtras/locations_commercial.json b/data/json/itemgroups/Locations_MapExtras/locations_commercial.json index d8330af418ae4..f267a01bcdfb5 100644 --- a/data/json/itemgroups/Locations_MapExtras/locations_commercial.json +++ b/data/json/itemgroups/Locations_MapExtras/locations_commercial.json @@ -227,7 +227,8 @@ [ "multi_cooker", 1 ], [ "pastaextruder", 8 ], { "item": "can_sealer", "prob": 5, "charges": [ 0, 500 ] }, - [ "knuckle_brass", 15 ], + [ "knuckle_brass", 10 ], + [ "knuckle_steel_forged", 5 ], [ "platinum_small", 10 ], { "item": "foodperson_mask", "prob": 1, "charges": [ 0, 500 ] }, [ "gold_small", 10 ], @@ -630,6 +631,7 @@ "items": [ { "group": "mags_surplus", "prob": 400 }, [ "freeze_dried_meal", 20 ], + [ "chocolate_military", 20 ], [ "knife_combat", 14 ], [ "knife_rm42", 1 ], [ "kukri", 2 ], diff --git a/data/json/itemgroups/Locations_MapExtras/locations_mapextras.json b/data/json/itemgroups/Locations_MapExtras/locations_mapextras.json index a36e51c4436ae..a3ed07ff4d2a5 100644 --- a/data/json/itemgroups/Locations_MapExtras/locations_mapextras.json +++ b/data/json/itemgroups/Locations_MapExtras/locations_mapextras.json @@ -8,7 +8,8 @@ [ "daypack", 20 ], [ "energy_drink", 55 ], [ "energy_drink_atomic", 8 ], - [ "knuckle_brass", 30 ], + [ "knuckle_brass", 20 ], + [ "knuckle_steel_forged", 10 ], [ "sports_drink", 35 ], [ "colamdew", 60 ], [ "purple_drink", 15 ], diff --git a/data/json/itemgroups/Locations_MapExtras/map_extras.json b/data/json/itemgroups/Locations_MapExtras/map_extras.json index 7b21d2695145b..1620384f0e575 100644 --- a/data/json/itemgroups/Locations_MapExtras/map_extras.json +++ b/data/json/itemgroups/Locations_MapExtras/map_extras.json @@ -71,12 +71,21 @@ "id": "map_extra_drugdeal", "entries": [ { "group": "drugdealer", "prob": 75 }, + { + "distribution": [ + { "item": "weed", "container-item": "bag_zipper", "charges-min": 20, "charges-max": 30, "prob": 10 }, + { "item": "coke", "container-item": "bag_zipper", "charges-min": 10, "charges-max": 20, "prob": 40 }, + { "item": "meth", "container-item": "bag_zipper", "charges-min": 8, "charges-max": 14, "prob": 30 }, + { "item": "heroin", "container-item": "bag_zipper", "charges-min": 6, "charges-max": 12, "prob": 20 } + ], + "prob": 50 + }, { "item": "pants_cargo", "damage-min": 1, "damage-max": 4 }, { "group": "lab_shoes", "damage-min": 1, "damage-max": 4, "prob": 50 }, { "group": "shirts", "damage-min": 1, "damage-max": 4, "prob": 50 }, { "group": "jackets", "damage-min": 1, "damage-max": 4, "prob": 30 }, { "group": "underwear", "damage-min": 1, "damage-max": 3 }, - { "item": "corpse", "damage": 3 } + { "item": "corpse", "damage": 4 } ] }, { diff --git a/data/json/itemgroups/Monsters_Animals_Lairs/monster_drops_lairs.json b/data/json/itemgroups/Monsters_Animals_Lairs/monster_drops_lairs.json index b125fcf754d97..65683bdea1a6e 100644 --- a/data/json/itemgroups/Monsters_Animals_Lairs/monster_drops_lairs.json +++ b/data/json/itemgroups/Monsters_Animals_Lairs/monster_drops_lairs.json @@ -217,9 +217,9 @@ { "item": "savage_111f", "prob": 10 }, { "item": "ak47", "prob": 16 }, { "item": "ak74", "prob": 4 }, - { "item": "m4a1", "prob": 7 }, + { "item": "nato_assault_rifle", "variant": "m4a1", "prob": 7 }, { "item": "m16a4", "prob": 6 }, - { "item": "h&k416a5", "prob": 3 }, + { "item": "nato_assault_rifle", "variant": "h&k416a5", "prob": 3 }, { "item": "m1014", "prob": 1 }, { "item": "steyr_aug", "prob": 6 }, { "item": "v29", "prob": 1 }, @@ -290,6 +290,85 @@ { "item": "beekeeping_gloves", "prob": 5 } ] }, + { + "type": "item_group", + "id": "bug_parts", + "//": "Bug butchery remains", + "subtype": "distribution", + "entries": [ + { "item": "chitin_piece", "prob": 70 }, + { "item": "acidchitin_piece", "prob": 30 }, + { "item": "endochitin", "prob": 70 }, + { "item": "mutant_bug_hydrogen_sacs", "prob": 30 }, + { "item": "mutant_bug_lungs", "prob": 10 }, + { "item": "mutant_bug_organs", "prob": 10 }, + { "item": "sinew", "prob": 10 }, + { "item": "bee_sting", "prob": 10 } + ] + }, + { + "type": "item_group", + "id": "vertebrate_parts", + "//": "Normal/mutated animal remains", + "subtype": "distribution", + "entries": [ + { "item": "bone", "prob": 80 }, + { "item": "feather", "prob": 80 }, + { "item": "raw_leather", "prob": 30 }, + { "item": "raw_fur", "prob": 30 }, + { "item": "wool_staple", "prob": 30 }, + { "item": "sinew", "prob": 5 }, + { "item": "brain", "prob": 5 }, + { "item": "lung", "prob": 5 }, + { "item": "liver", "prob": 5 }, + { "item": "sweetbread", "prob": 5 }, + { "item": "stomach", "prob": 5 }, + { "item": "stomach_large", "prob": 5 }, + { "item": "kidney", "prob": 5 }, + { "item": "jabberwock_heart", "prob": 1 } + ] + }, + { + "type": "item_group", + "id": "human_parts", + "//": "(demi)human remains", + "subtype": "distribution", + "entries": [ + { "item": "bone_human", "prob": 80 }, + { "item": "raw_hleather", "prob": 30 }, + { "item": "raw_demihumanleather", "prob": 5 }, + { "item": "raw_hfur", "prob": 1 }, + { "item": "sinew", "prob": 5 }, + { "item": "hstomach", "prob": 5 }, + { "item": "demihuman_stomach", "prob": 1 }, + { "item": "hstomach_large", "prob": 5 }, + { "item": "demihuman_stomach_large", "prob": 1 } + ] + }, + { + "type": "item_group", + "id": "wasp_lair", + "//": "Damaged generic/military/science drops, nothing fancy", + "subtype": "distribution", + "ammo": 10, + "entries": [ + { "group": "allclothes", "damage": [ 2, 4 ], "prob": 150 }, + { "group": "clothing_outdoor_set", "damage": [ 2, 4 ], "prob": 10 }, + { "group": "clothing_hunting", "damage": [ 2, 4 ], "prob": 10 }, + { "group": "clothing_work_set", "damage": [ 2, 4 ], "prob": 10 }, + { "group": "everyday_gear", "damage": [ 2, 4 ], "prob": 50 }, + { "group": "gear_homeless", "damage": [ 2, 4 ], "prob": 30 }, + { "group": "child_items", "damage": [ 2, 4 ], "prob": 30 }, + { "group": "traveler", "damage": [ 2, 4 ], "prob": 10 }, + { "group": "default_zombie_clothes", "damage": [ 2, 4 ], "prob": 30 }, + { "group": "default_zombie_items_bags", "damage": [ 2, 4 ], "prob": 30 }, + { "group": "default_zombie_items_pockets", "damage": [ 2, 4 ], "prob": 30 }, + { "group": "mon_zombie_scientist_death_drops", "damage": [ 2, 4 ], "prob": 5 }, + { "group": "mon_zombie_labsecurity_death_drops", "damage": [ 2, 4 ], "prob": 5 }, + { "group": "mon_zombie_kevlar_death_drops", "damage": [ 2, 4 ], "prob": 15 }, + { "group": "mon_zombie_military_pilot_death_drops", "damage": [ 2, 4 ], "prob": 5 } + ] + }, { "type": "item_group", "id": "radio", diff --git a/data/json/itemgroups/SUS/fridges.json b/data/json/itemgroups/SUS/fridges.json index d99955ed69801..75a7f9e8b6fe8 100644 --- a/data/json/itemgroups/SUS/fridges.json +++ b/data/json/itemgroups/SUS/fridges.json @@ -39,6 +39,8 @@ { "item": "yoghurt", "prob": 80 }, { "item": "butter", "prob": 80 }, { "item": "pudding", "prob": 30 }, + { "item": "gelatin_dessert_processed", "prob": 20 }, + { "item": "egg_bird_unfert", "prob": 85, "count-min": 1, "count-max": 12 }, { "item": "egg_bird_unfert", "prob": 85, "charges-min": 1, "charges-max": 12, "container-item": "carton_egg" }, { "item": "bacon", "prob": 25 }, { @@ -520,6 +522,8 @@ { "item": "onion_rings", "charges-min": 1, "prob": 10 }, { "item": "pizza_veggy", "charges-min": 1, "prob": 20 }, { "item": "nachosv", "charges-min": 1, "prob": 20 }, + { "item": "gelatin_dessert_vegan", "prob": 30 }, + { "item": "gelatin_dessert_vegan_fruit", "prob": 30 }, { "distribution": [ { @@ -623,6 +627,8 @@ { "item": "yoghurt", "prob": 20 }, { "item": "butter", "prob": 20 }, { "item": "pudding", "prob": 20 }, + { "item": "gelatin_dessert_processed", "prob": 10 }, + { "item": "egg_bird_unfert", "prob": 20, "count-min": 1, "count-max": 12 }, { "item": "egg_bird_unfert", "prob": 20, "charges-min": 1, "charges-max": 12, "container-item": "carton_egg" }, { "item": "bacon", "prob": 25 }, { @@ -880,6 +886,7 @@ { "item": "yoghurt", "count": [ 1, 3 ], "prob": 75 }, { "item": "butter", "prob": 50 }, { "item": "pudding", "prob": 30 }, + { "item": "gelatin_dessert_processed", "prob": 10 }, { "item": "veggy_salad", "charges-min": 1, "prob": 19 }, { "item": "blt", "charges-min": 1, "prob": 13 }, { "item": "protein_shake", "charges-min": 1, "prob": 6 }, diff --git a/data/json/itemgroups/SUS/lodge.json b/data/json/itemgroups/SUS/lodge.json new file mode 100644 index 0000000000000..d3e03875adf68 --- /dev/null +++ b/data/json/itemgroups/SUS/lodge.json @@ -0,0 +1,80 @@ +[ + { + "id": "SUS_hunting_archery", + "type": "item_group", + "//": "items found in an archery space", + "subtype": "collection", + "entries": [ + { "item": "compbow", "prob": 50 }, + { "item": "compbow_high", "prob": 20 }, + { "item": "compbow_low", "prob": 40 }, + { "item": "recurbow", "prob": 30 }, + { "item": "reflexrecurvebow", "prob": 10 } + ] + }, + { + "id": "SUS_hunting_rifle", + "type": "item_group", + "//": "items found in a rifle hunting space", + "subtype": "collection", + "entries": [ + { "item": "ar15", "prob": 150, "charges-min": 0, "charges-max": 30 }, + { "item": "marlin_9a", "prob": 20, "charges-min": 0, "charges-max": 19 }, + { "item": "remington700_270", "prob": 10, "charges-min": 0, "charges-max": 4 }, + { "item": "remington_700", "prob": 40, "charges-min": 0, "charges-max": 4 }, + { "item": "ruger_1022", "prob": 70, "charges-min": 0, "charges-max": 10 }, + { "item": "ruger_mini", "prob": 10, "charges-min": 0, "charges-max": 5 }, + { "item": "win70", "prob": 20, "charges-min": 0, "charges-max": 3 }, + { "item": "colt_lightning", "prob": 2, "charges-min": 0, "charges-max": 10 }, + { "item": "henry_big_boy", "prob": 2, "charges-min": 0, "charges-max": 10 }, + { "item": "weatherby_5", "prob": 2, "charges-min": 0, "charges-max": 3 } + ] + }, + { + "id": "hunting_lodge_weapons", + "type": "item_group", + "//": "items found in a shotgun hunting space", + "subtype": "distribution", + "entries": [ + { "group": "guns_shotgun_common", "prob": 30 }, + { "group": "SUS_hunting_rifle", "prob": 20 }, + { "group": "SUS_hunting_archery", "prob": 15 } + ] + }, + { + "id": "cannibal_weapons", + "type": "item_group", + "//": "items found in a most dangerous game space", + "subtype": "distribution", + "entries": [ + { "group": "guns_shotgun_common", "prob": 30 }, + { "group": "SUS_hunting_rifle", "prob": 20 }, + { "group": "guns_smg_rare", "prob": 15 } + ] + }, + { + "id": "lodge_archery_ammo", + "type": "item_group", + "//": "archery ammo", + "subtype": "collection", + "entries": [ + { "item": "arrow_metal", "prob": 20 }, + { "item": "arrow_metal_bodkin", "prob": 20 }, + { "item": "arrow_metal_target", "prob": 30 }, + { "item": "arrow_cf", "prob": 10 } + ] + }, + { + "id": "cannibal_food", + "type": "item_group", + "subtype": "collection", + "entries": [ + { "item": "machete", "prob": 20 }, + { "group": "preserved_food", "custom-flags": [ "CANNIBALISM" ], "prob": 30 }, + { "group": "preserved_food", "prob": 20 }, + { "group": "dry_goods", "prob": 30 }, + { "group": "dry_goods", "custom-flags": [ "CANNIBALISM" ], "prob": 60 }, + { "group": "pantry", "prob": 10 } + ] + } +] diff --git a/data/json/itemgroups/Weapons_Mods_Ammo/gunmod.json b/data/json/itemgroups/Weapons_Mods_Ammo/gunmod.json index c52235ca4c2a5..d482cabfba4b0 100644 --- a/data/json/itemgroups/Weapons_Mods_Ammo/gunmod.json +++ b/data/json/itemgroups/Weapons_Mods_Ammo/gunmod.json @@ -123,6 +123,13 @@ [ "offset_sights", 6 ] ] }, + { + "type": "item_group", + "id": "m38dmr_mods", + "//": "Default mods for the m38dmr variant of the NATO assault rifle", + "subtype": "collection", + "entries": [ { "item": "suppressor" }, { "item": "rifle_scope" }, { "item": "bipod" } ] + }, { "type": "item_group", "id": "sights_shotgun_readied", @@ -137,6 +144,13 @@ "//": "For shotguns that haven't been 'made ready', e.g sitting in a store or garage.", "entries": [ { "group": "sights_shotgun", "prob": 20 }, { "group": "EMPTY_GROUP", "prob": 80 } ] }, + { + "type": "item_group", + "id": "mk23_mods", + "//": "Default mods for the mk23 variant of the USP .45 pistol", + "subtype": "collection", + "entries": [ { "item": "suppressor" }, { "item": "laser_sight" } ] + }, { "type": "item_group", "id": "sights_launcher", diff --git a/data/json/itemgroups/Weapons_Mods_Ammo/guns.json b/data/json/itemgroups/Weapons_Mods_Ammo/guns.json index 55ae85cb79f4d..59485c1ab21fa 100644 --- a/data/json/itemgroups/Weapons_Mods_Ammo/guns.json +++ b/data/json/itemgroups/Weapons_Mods_Ammo/guns.json @@ -145,9 +145,16 @@ "items": [ { "item": "m17", "prob": 100, "charges-min": 0, "charges-max": 17 }, { "item": "glock_18c", "prob": 70, "charges-min": 0, "charges-max": 17 }, - { "item": "mk23", "prob": 10, "charges-min": 0, "charges-max": 12 }, + { + "item": "usp_45", + "variant": "mk23", + "contents-group": "mk23_mods", + "prob": 10, + "charges-min": 0, + "charges-max": 12 + }, { "item": "usp_45", "prob": 15, "charges-min": 0, "charges-max": 12 }, - { "item": "m1911_MEU", "prob": 5, "charges-min": 0, "charges-max": 7 }, + { "item": "m1911", "variant": "m1911_MEU", "prob": 5, "charges-min": 0, "charges-max": 7 }, { "item": "glock_19", "prob": 20, "charges-min": 0, "charges-max": 15 }, { "item": "needlepistol", "prob": 45, "charges-min": 0, "charges-max": 50 }, { "item": "rm103a_pistol", "prob": 35, "charges-min": 0, "charges-max": 10 } @@ -350,7 +357,7 @@ "id": "guns_rifle_rare", "//": "Less common rifles including those only used by police/paramilitary forces.", "items": [ - { "item": "acr", "prob": 25, "charges-min": 0, "charges-max": 30 }, + { "item": "nato_assault_rifle", "variant": "acr", "prob": 25, "charges-min": 0, "charges-max": 30 }, { "item": "colt_lightning", "prob": 15, "charges-min": 0, "charges-max": 10 }, { "item": "fn_fal", "prob": 40, "charges-min": 0, "charges-max": 20 }, { "item": "fs2000", "prob": 6, "charges-min": 0, "charges-max": 30 }, @@ -360,7 +367,7 @@ { "item": "henry_big_boy", "prob": 10, "charges-min": 0, "charges-max": 10 }, { "item": "m14ebr", "prob": 15, "charges-min": 0, "charges-max": 20 }, { "item": "M24", "prob": 15, "charges-min": 0, "charges-max": 5 }, - { "item": "m4a1", "prob": 45, "charges-min": 0, "charges-max": 30 }, + { "item": "nato_assault_rifle", "variant": "m4a1", "prob": 45, "charges-min": 0, "charges-max": 30 }, { "item": "m1903", "prob": 15, "charges-min": 0, "charges-max": 5 }, { "item": "m1918", "prob": 30, "charges-min": 0, "charges-max": 20 }, { "item": "mosin44_ebr", "prob": 10, "charges-min": 0, "charges-max": 5 }, @@ -379,7 +386,7 @@ "id": "guns_rifle_rare_display", "//": "Less common rifles found exclusively in gun stores.", "items": [ - { "item": "acr", "prob": 25, "charges-min": 0, "charges-max": 0 }, + { "item": "nato_assault_rifle", "variant": "acr", "prob": 25, "charges-min": 0, "charges-max": 0 }, { "item": "colt_lightning", "prob": 15, "charges-min": 0, "charges-max": 0 }, { "item": "fn_fal", "prob": 40, "charges-min": 0, "charges-max": 0 }, { "item": "m249_semi", "prob": 5, "charges-min": 0, "charges-max": 0 }, @@ -388,7 +395,7 @@ { "item": "henry_big_boy", "prob": 10, "charges-min": 0, "charges-max": 0 }, { "item": "m14ebr", "prob": 15, "charges-min": 0, "charges-max": 0 }, { "item": "M24", "prob": 15, "charges-min": 0, "charges-max": 0 }, - { "item": "m4a1", "prob": 45, "charges-min": 0, "charges-max": 0 }, + { "item": "nato_assault_rifle", "variant": "m4a1", "prob": 45, "charges-min": 0, "charges-max": 0 }, { "item": "m1903", "prob": 15, "charges-min": 0, "charges-max": 0 }, { "item": "m1918", "prob": 30, "charges-min": 0, "charges-max": 0 }, { "item": "mosin44_ebr", "prob": 10, "charges-min": 0, "charges-max": 0 }, @@ -407,7 +414,7 @@ "id": "guns_rifle_milspec", "//": "Military specification rifles only ever found at military sites.", "items": [ - { "item": "h&k416a5", "prob": 50, "charges-min": 0, "charges-max": 30 }, + { "item": "nato_assault_rifle", "variant": "h&k416a5", "prob": 50, "charges-min": 0, "charges-max": 30 }, { "item": "m107a1", "prob": 30, "charges-min": 0, "charges-max": 10 }, { "item": "m134", "prob": 10, "charges-min": 0, "charges-max": 100 }, { "item": "m14ebr", "prob": 10, "charges-min": 0, "charges-max": 20 }, @@ -415,7 +422,7 @@ { "item": "m2010", "prob": 20, "charges-min": 0, "charges-max": 5 }, { "item": "m240", "prob": 15, "charges-min": 0, "charges-max": 100 }, { "item": "m249", "prob": 25, "charges-min": 0, "charges-max": 10 }, - { "item": "m27iar", "prob": 50, "charges-min": 0, "charges-max": 30 }, + { "item": "nato_assault_rifle", "variant": "m27iar", "prob": 50, "charges-min": 0, "charges-max": 30 }, { "item": "m60", "prob": 15, "charges-min": 0, "charges-max": 100 }, { "item": "rm11b_sniper_rifle", "prob": 15, "charges-min": 0, "charges-max": 10 }, { "item": "rm298", "prob": 10, "charges-min": 0, "charges-max": 100 }, @@ -423,7 +430,7 @@ { "item": "rm614_lmg", "prob": 10, "charges-min": 0, "charges-max": 100 }, { "item": "rm88_battle_rifle", "prob": 25, "charges-min": 0, "charges-max": 50 }, { "item": "sig552", "prob": 100, "charges-min": 0, "charges-max": 30 }, - { "item": "scar_l", "prob": 50, "charges-min": 0, "charges-max": 30 }, + { "item": "nato_assault_rifle", "variant": "scar_l", "prob": 50, "charges-min": 0, "charges-max": 30 }, { "item": "scar_h", "prob": 50, "charges-min": 0, "charges-max": 20 }, { "item": "m110a1", "prob": 50, "charges-min": 0, "charges-max": 20 }, { "item": "acr_300blk", "prob": 15, "charges-min": 0, "charges-max": 30 } @@ -785,7 +792,7 @@ { "item": "hk_mp5k", "prob": 10, "charges-min": 0, "charges-max": 30 }, { "item": "hk_mp5sd", "prob": 5, "charges-min": 0, "charges-max": 30 }, { "item": "m1014", "prob": 10, "charges-min": 0, "charges-max": 8 }, - { "item": "m4a1", "prob": 35, "charges-min": 0, "charges-max": 30 }, + { "item": "nato_assault_rifle", "variant": "m4a1", "prob": 35, "charges-min": 0, "charges-max": 30 }, { "item": "as50", "prob": 5, "charges-min": 0, "charges-max": 10 }, { "item": "hk417_13", "prob": 30, "charges-min": 0, "charges-max": 20 } ] diff --git a/data/json/itemgroups/activities_hobbies.json b/data/json/itemgroups/activities_hobbies.json index 04328bb8792f8..f8967629f8a8d 100644 --- a/data/json/itemgroups/activities_hobbies.json +++ b/data/json/itemgroups/activities_hobbies.json @@ -143,6 +143,7 @@ { "item": "multitool", "prob": 12 }, { "item": "throwing_knife", "prob": 7 }, { "item": "slingpack", "prob": 18 }, + { "item": "armrig", "prob": 3 }, { "item": "manual_cutting", "prob": 12 }, { "item": "manual_launcher", "prob": 1 }, { "item": "recipe_bullets", "prob": 8 }, @@ -207,6 +208,7 @@ [ "backpack_hiking", 12 ], [ "bigback", 3 ], [ "travelpack", 6 ], + [ "armrig", 3 ], [ "cowboy_hat", 4 ], [ "10gal_hat", 4 ], { "item": "bb", "prob": 8, "charges": [ 1, 500 ] }, diff --git a/data/json/itemgroups/art_antiques_crafts.json b/data/json/itemgroups/art_antiques_crafts.json index 1fc319800412c..d1e5a376777c1 100644 --- a/data/json/itemgroups/art_antiques_crafts.json +++ b/data/json/itemgroups/art_antiques_crafts.json @@ -194,7 +194,8 @@ { "item": "coin_quarter", "prob": 1 }, { "item": "bronze_medal", "prob": 1 }, { "item": "silver_medal", "prob": 1 }, - { "item": "gold_medal", "prob": 1 } + { "item": "gold_medal", "prob": 1 }, + { "item": "bookstrap", "prob": 5 } ] }, { diff --git a/data/json/itemgroups/books.json b/data/json/itemgroups/books.json index 3524ef56ceca3..538cdd0d7e9d5 100644 --- a/data/json/itemgroups/books.json +++ b/data/json/itemgroups/books.json @@ -777,7 +777,11 @@ "id": "newspaper", "type": "item_group", "subtype": "distribution", - "entries": [ { "group": "newspaper_recent", "prob": 80 }, { "group": "newspaper_old", "prob": 20 } ] + "entries": [ + { "group": "newspaper_recent", "prob": 79 }, + { "group": "newspaper_old", "prob": 20 }, + { "item": "valentine_card", "prob": 1 } + ] }, { "id": "newspaper_recent", diff --git a/data/json/itemgroups/collections_domestic.json b/data/json/itemgroups/collections_domestic.json index e6d0c85b10325..6d9671e268e37 100644 --- a/data/json/itemgroups/collections_domestic.json +++ b/data/json/itemgroups/collections_domestic.json @@ -565,6 +565,7 @@ { "item": "noodles_fast", "prob": 30 }, { "item": "ravioli", "prob": 25 }, { "item": "sauce_red", "prob": 20 }, + { "item": "gelatin_dessert_powder", "prob": 20 }, { "item": "sauce_pesto", "prob": 15 }, { "item": "bread", "prob": 14 }, { "item": "cornbread", "prob": 7 }, @@ -580,6 +581,7 @@ { "item": "can_cheese", "prob": 8 }, { "item": "yoghurt", "prob": 8 }, { "item": "pudding", "prob": 10 }, + { "item": "gelatin_dessert_processed", "prob": 10 }, { "item": "veggy_pickled", "prob": 8, "charges": 2, "container-item": "jar_glass_sealed" }, { "item": "sauerkraut", "prob": 5, "charges": 4, "container-item": "jar_glass_sealed" }, { "item": "meat_pickled", "prob": 4, "charges": 2, "container-item": "jar_glass_sealed" }, diff --git a/data/json/itemgroups/defense_mode.json b/data/json/itemgroups/defense_mode.json index db399a263863f..08ebc50f2080d 100644 --- a/data/json/itemgroups/defense_mode.json +++ b/data/json/itemgroups/defense_mode.json @@ -35,7 +35,7 @@ { "item": "remington_870" }, { "item": "browning_blr" }, { "item": "ak47" }, - { "item": "m4a1" }, + { "item": "nato_assault_rifle", "variant": "m4a1" }, { "item": "savage_111f" }, { "item": "hk_g3" }, { "item": "hk_g80" }, diff --git a/data/json/itemgroups/food_service.json b/data/json/itemgroups/food_service.json index 6c53e003c029d..253f81eba17a6 100644 --- a/data/json/itemgroups/food_service.json +++ b/data/json/itemgroups/food_service.json @@ -74,7 +74,11 @@ "id": "corner_weapon", "type": "item_group", "subtype": "distribution", - "entries": [ { "item": "tazer", "prob": 70, "charges": [ 0, 500 ] }, { "item": "knuckle_brass", "prob": 70 } ] + "entries": [ + { "item": "tazer", "prob": 70, "charges": [ 0, 500 ] }, + { "item": "knuckle_brass", "prob": 50 }, + { "item": "knuckle_steel_forged", "prob": 20 } + ] }, { "id": "displays", @@ -661,7 +665,7 @@ "subtype": "distribution", "entries": [ { "item": "bread", "prob": 65 }, - { "item": "brown_bread", "prob": 50 }, + { "item": "brown_bread", "prob": 50, "container-item": "bag_plastic" }, { "item": "cake2", "prob": 10 }, { "item": "cake3", "prob": 10 }, { "item": "cornbread", "prob": 35 }, diff --git a/data/json/itemgroups/military.json b/data/json/itemgroups/military.json index a131b428e8af7..ef8cf03b9927d 100644 --- a/data/json/itemgroups/military.json +++ b/data/json/itemgroups/military.json @@ -4,9 +4,15 @@ "id": "military_standard_assault_rifles", "subtype": "distribution", "entries": [ - { "item": "m4a1", "prob": 88, "charges": [ 0, 30 ] }, - { "item": "m27iar", "prob": 10, "charges": [ 0, 30 ] }, - { "item": "m38dmr", "prob": 1, "charges": [ 0, 30 ] }, + { "item": "nato_assault_rifle", "variant": "m4a1", "prob": 88, "charges": [ 0, 30 ] }, + { "item": "nato_assault_rifle", "variant": "m27iar", "prob": 10, "charges": [ 0, 30 ] }, + { + "item": "nato_assault_rifle", + "variant": "m38dmr", + "contents-group": "m38dmr_mods", + "prob": 1, + "charges": [ 0, 30 ] + }, { "item": "m16a4", "prob": 2, "charges": [ 0, 30 ] } ] }, @@ -38,7 +44,7 @@ { "item": "m17", "prob": 65, "charges": [ 0, 17 ] }, { "item": "m18", "prob": 30, "charges": [ 0, 17 ] }, { "item": "glock_19", "prob": 3, "charges": [ 0, 15 ] }, - { "item": "m1911_MEU", "prob": 2, "charges": [ 0, 7 ] } + { "item": "m1911", "variant": "m1911_MEU", "prob": 2, "charges": [ 0, 7 ] } ] }, { @@ -120,7 +126,8 @@ { "item": "small_repairkit", "prob": 10 }, { "item": "50_incendiary", "prob": 2 }, { "item": "water_clean", "prob": 90 }, - { "item": "knuckle_brass", "prob": 15 }, + { "item": "knuckle_brass", "prob": 10 }, + { "item": "knuckle_steel_forged", "prob": 5 }, { "item": "battery_ups", "prob": 10 }, { "item": "bandages", "prob": 30 }, { "item": "adhesive_bandages", "prob": 20 }, @@ -174,23 +181,23 @@ { "item": "20x66_flechette", "prob": 3 }, { "item": "m9", "prob": 6 }, { "item": "usp_45", "prob": 6 }, - { "item": "m4a1", "prob": 7 }, - { "item": "m4_cqbr", "prob": 1 }, + { "item": "nato_assault_rifle", "variant": "m4a1", "prob": 7 }, + { "item": "nato_assault_rifle", "variant": "m4_cqbr", "prob": 1 }, { "item": "m231pfw", "prob": 1 }, { "item": "m16a4", "prob": 5 }, - { "item": "m16a3", "prob": 1 }, + { "item": "nato_assault_rifle", "variant": "m16a3", "prob": 1 }, { "item": "colt_ro635", "prob": 1 }, { "item": "p226_9mm", "prob": 1 }, { "item": "sp2022", "prob": 1 }, - { "item": "h&k416a5", "prob": 7 }, + { "item": "nato_assault_rifle", "variant": "h&k416a5", "prob": 7 }, { "item": "m1014", "prob": 2 }, - { "item": "scar_l", "prob": 6 }, + { "item": "nato_assault_rifle", "variant": "scar_l", "prob": 6 }, { "item": "scar_h", "prob": 5 }, { "item": "sig_mcx_rattler_sbr", "prob": 1 }, { "item": "m249", "prob": 1 }, { "item": "m240", "prob": 1 }, - { "item": "m27iar", "prob": 1 }, - { "item": "m38dmr", "prob": 1 }, + { "item": "nato_assault_rifle", "variant": "m27iar", "prob": 1 }, + { "item": "nato_assault_rifle", "variant": "m38dmr", "contents-group": "m38dmr_mods", "prob": 1 }, { "item": "plasma_gun", "prob": 1 }, { "item": "m320", "prob": 5 }, { "item": "m320_mod", "prob": 10 }, @@ -376,8 +383,8 @@ { "item": "rm451_flamethrower", "prob": 10, "charges": [ 0, 2000 ] }, { "item": "m249", "prob": 30, "charges": [ 0, 30 ] }, { "item": "m240", "prob": 20, "charges": [ 0, 200 ] }, - { "item": "m27iar", "prob": 30, "charges": [ 0, 30 ] }, - { "item": "m38dmr", "prob": 3, "charges": [ 0, 30 ] }, + { "item": "nato_assault_rifle", "variant": "m27iar", "prob": 30, "charges": [ 0, 30 ] }, + { "item": "nato_assault_rifle", "variant": "m38dmr", "contents-group": "m38dmr_mods", "charges": [ 0, 30 ] }, { "item": "m2browning", "prob": 15, "charges": [ 0, 100 ] }, { "item": "mark19", "prob": 15, "charges": [ 0, 50 ] }, { "item": "556", "prob": 30, "charges": [ 1, 30 ] }, @@ -471,7 +478,7 @@ "subtype": "distribution", "entries": [ { "group": "MRE", "prob": 1035 }, - { "item": "chocolate", "prob": 50 }, + { "item": "chocolate", "prob": 20 }, { "item": "neccowafers", "prob": 30 }, { "item": "can_beans", "prob": 40 }, { "item": "pork_beans", "prob": 40 }, diff --git a/data/json/itemgroups/supplies.json b/data/json/itemgroups/supplies.json index 18ec8fc1ff434..4b08ce7d840a5 100644 --- a/data/json/itemgroups/supplies.json +++ b/data/json/itemgroups/supplies.json @@ -74,6 +74,7 @@ { "item": "coal_lump", "prob": 20, "count": [ 1, 10 ] }, { "item": "material_cement", "prob": 100, "charges": 500, "container-item": "bag_canvas" }, { "item": "material_sand", "prob": 100, "charges": 500, "container-item": "bag_canvas" }, + { "item": "material_gravel", "prob": 100, "charges": 500, "container-item": "bag_canvas" }, [ "material_shrd_limestone", 70 ], [ "fertilizer_commercial", 100 ], [ "material_quicklime", 70 ], diff --git a/data/json/items/ammo.json b/data/json/items/ammo.json index b2333a280868b..4a29bca1d172a 100644 --- a/data/json/items/ammo.json +++ b/data/json/items/ammo.json @@ -382,16 +382,15 @@ { "type": "AMMO", "id": "pebble", - "price": 100, - "price_postapoc": 10, "name": { "str": "pebble" }, "symbol": "=", "color": "light_gray", - "description": "A handful of pebbles, useful as ammunition for slingshots.", + "description": "A small rock, useful as ammunition for slingshots.", "material": [ "stone" ], "volume": "250 ml", - "weight": "5 g", + "weight": "66 g", "ammo_type": "pebble", + "flags": [ "TRADER_AVOID" ], "damage": { "damage_type": "bullet", "amount": 2 }, "range": 10, "dispersion": 14, diff --git a/data/json/items/ammo/10mm.json b/data/json/items/ammo/10mm.json index 50b874de385c1..7585a250df9cb 100644 --- a/data/json/items/ammo/10mm.json +++ b/data/json/items/ammo/10mm.json @@ -3,7 +3,7 @@ "id": "10mm_fmj", "type": "AMMO", "name": { "str_sp": "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.", + "description": "Jacketed 10mm Auto ammunition with 180gr bullets. The 10mm Auto cartridge is a rather powerful handgun round and the progenitor to the more popular .40 S&W.", "weight": "9 g", "volume": "117 ml", "price": 400, @@ -17,7 +17,7 @@ "ammo_type": "10mm", "casing": "10mm_casing", "range": 14, - "damage": { "damage_type": "bullet", "amount": 31, "armor_penetration": 4 }, + "damage": { "damage_type": "bullet", "amount": 28, "armor_penetration": 4 }, "dispersion": 50, "recoil": 750, "effects": [ "COOKOFF" ] diff --git a/data/json/items/ammo/40x46mm.json b/data/json/items/ammo/40x46mm.json index 4b469fef32013..e748f47a91221 100644 --- a/data/json/items/ammo/40x46mm.json +++ b/data/json/items/ammo/40x46mm.json @@ -179,5 +179,33 @@ "name": { "str_sp": "40x46mm-M199 flechette, black powder" }, "description": "An improvised 40x46mm flechette load containing 10 steel darts, loaded into a M199 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "casing": "40x46mm_m199_casing" + }, + { + "id": "40x46mm_hornets_nest_22lr", + "copy-from": "40x46mm_grenade", + "type": "AMMO", + "name": { "str_sp": "40x46mm .22 LR hornet's nest" }, + "proportional": { "dispersion": 1.8 }, + "description": "A set of ten .22 LR bullets compressed into a 40mm adapter. Fires all ten rounds simultaneously.", + "weight": "114 g", + "price": 1500, + "price_postapoc": 2400, + "material": [ "aluminum", "brass", "lead", "powder" ], + "damage": { "damage_type": "bullet", "amount": 100 }, + "casing": "40x46mm_m212_casing" + }, + { + "id": "40x46mm_hornets_nest_410", + "copy-from": "40x46mm_grenade", + "type": "AMMO", + "name": { "str_sp": "40x46mm .410 hornet's nest" }, + "proportional": { "dispersion": 1.4 }, + "description": "A set of four .410 shells compressed into a 40mm adapter. Fires all four rounds at once.", + "weight": "245 g", + "price": 710, + "price_postapoc": 3240, + "material": [ "aluminum", "brass", "lead", "powder" ], + "damage": { "damage_type": "bullet", "amount": 120 }, + "casing": "40x46mm_m212_casing" } ] diff --git a/data/json/items/armor/arms_armor.json b/data/json/items/armor/arms_armor.json index 4bee254520558..19abbbf64451e 100644 --- a/data/json/items/armor/arms_armor.json +++ b/data/json/items/armor/arms_armor.json @@ -4,7 +4,7 @@ "type": "ARMOR", "category": "armor", "name": { "str": "pair of 2-by-arm guards", "str_pl": "pairs of 2-by-arm guards" }, - "description": "A pair of improvised arm guards made from broken pieces of a two by four that are tied to your arms with rags and string. They offer good protection, but are really uncomfortable to wear.", + "description": "A pair of improvised arm guards made from broken pieces of a plank that are tied to your arms with rags and string. They offer good protection, but are really uncomfortable to wear.", "weight": "300 g", "volume": "1500 ml", "price": 500, diff --git a/data/json/items/armor/boots.json b/data/json/items/armor/boots.json index 6acc46420dfec..416967eb95a5e 100644 --- a/data/json/items/armor/boots.json +++ b/data/json/items/armor/boots.json @@ -274,6 +274,30 @@ "proportional": { "weight": 1.15, "volume": 1.15 }, "flags": [ "VARSIZE", "WATERPROOF", "OVERSIZE" ] }, + { + "id": "boots_wsurvivor_nofur", + "type": "ARMOR", + "name": { "str": "pair of faux fur winter survivor boots", "str_pl": "pairs of faux fur winter survivor boots" }, + "description": "A pair of customized, Kevlar armored faux fur boots, modified to provide maximum protection from harm and the elements, even when knee-deep in the dead. While not quite as warm as real fur it's pretty good.", + "copy-from": "boots_wsurvivor", + "material": [ "kevlar", "faux_fur" ], + "symbol": "[", + "warmth": 50, + "looks_like": "boots_fur", + "color": "pink" + }, + { + "id": "xl_boots_wsurvivor_nofur", + "type": "ARMOR", + "name": { "str": "pair of XL faux fur winter survivor boots", "str_pl": "pairs of XL faux fur winter survivor boots" }, + "copy-from": "boots_wsurvivor", + "material": [ "kevlar", "faux_fur" ], + "description": "A pair of customized and oversized, Kevlar armored faux fur boots, modified to provide maximum protection from harm and the elements, even when knee-deep in the dead. While not quite as warm as real fur it's pretty good.", + "color": "pink", + "warmth": 50, + "proportional": { "weight": 1.3, "volume": 1.3 }, + "flags": [ "VARSIZE", "WATERPROOF", "OVERSIZE", "STURDY" ] + }, { "id": "boots_h20survivor", "type": "ARMOR", diff --git a/data/json/items/armor/coats.json b/data/json/items/armor/coats.json index b4ce03d04c4f8..0d884b5e6242a 100644 --- a/data/json/items/armor/coats.json +++ b/data/json/items/armor/coats.json @@ -20,9 +20,27 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 100, "encumbrance": [ 22, 22 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "900 ml", + "max_contains_weight": "3 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "3250 ml", + "max_contains_weight": "5 kg", + "max_item_length": "30 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "3250 ml", + "max_contains_weight": "5 kg", + "max_item_length": "30 cm", + "moves": 80 + } ], "warmth": 30, "material_thickness": 2, @@ -71,8 +89,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 26, 26 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "2 kg", + "max_item_length": "16 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "2 kg", + "max_item_length": "16 cm", + "moves": 80 + } ], "warmth": 80, "material_thickness": 2, @@ -128,8 +158,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 30, 30 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "700 ml", + "max_contains_weight": "3 kg", + "max_item_length": "16 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "700 ml", + "max_contains_weight": "3 kg", + "max_item_length": "16 cm", + "moves": 80 + } ], "warmth": 90, "material_thickness": 2, @@ -157,9 +199,27 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 2, 2 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1600 ml", + "max_contains_weight": "3 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1600 ml", + "max_contains_weight": "3 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "900 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 80 + } ], "warmth": 15, "material_thickness": 0.25, @@ -183,8 +243,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 7, 7 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "21 cm", + "moves": 80 + } ], "warmth": 10, "material_thickness": 0.2, @@ -254,10 +326,34 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 18, 21 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1250 ml", "max_contains_weight": "3kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1250 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "3 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "3 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "900 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "900 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 70, "material_thickness": 2, @@ -285,12 +381,48 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 7, 7 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 15, "material_thickness": 0.3, @@ -317,12 +449,48 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 9, 9 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 50, "material_thickness": 3, @@ -385,12 +553,48 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 8, 8 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 30, "material_thickness": 1.5, @@ -427,12 +631,48 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 85, "encumbrance": [ 7, 7 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "3 L", "max_contains_weight": "6 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "3 L", "max_contains_weight": "6 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "3 L", + "max_contains_weight": "6 kg", + "max_item_length": "31 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "3 L", + "max_contains_weight": "6 kg", + "max_item_length": "31 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "25 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "25 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 10, "material_thickness": 3, @@ -468,10 +708,34 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 11, 11 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1750 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1750 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "750 ml", "max_contains_weight": "2 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "750 ml", "max_contains_weight": "2 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1750 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1750 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "900 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "900 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 50, "material_thickness": 4, @@ -540,8 +804,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 85, "encumbrance": [ 5, 5 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "750 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "750 ml", "max_contains_weight": "2 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "750 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "750 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 80 + } ], "warmth": 20, "material_thickness": 2, @@ -567,12 +843,48 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 7, 9 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "750 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "750 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "900 ml", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "900 ml", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 20, "material_thickness": 0.3, @@ -597,7 +909,15 @@ { "covers": [ "torso" ], "coverage": 95, "encumbrance": [ 8, 13 ] }, { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 8, 8 ] } ], - "pocket_data": [ { "pocket_type": "CONTAINER", "max_contains_volume": "1250 ml", "max_contains_weight": "2 kg", "moves": 80 } ], + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 80 + } + ], "warmth": 25, "material_thickness": 0.4, "flags": [ "OUTER", "VARSIZE" ] @@ -621,8 +941,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 13, 13 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 80 + } ], "warmth": 25, "material_thickness": 0.15, @@ -647,8 +979,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 9, 9 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 80 + } ], "warmth": 35, "material_thickness": 0.2, @@ -673,8 +1017,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 9, 9 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 80 + } ], "warmth": 30, "material_thickness": 0.2, @@ -705,8 +1061,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 10, 10 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_item_length": "13 cm", + "max_contains_weight": "3 kg", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_item_length": "13 cm", + "max_contains_weight": "3 kg", + "moves": 80 + } ], "warmth": 30, "material_thickness": 1.5, @@ -734,8 +1102,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 15, 15 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "13 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "13 cm", + "moves": 80 + } ], "warmth": 30, "material_thickness": 1.0, @@ -762,8 +1142,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 4, 4 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "700 ml", + "max_contains_weight": "3 kg", + "max_item_length": "14 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "700 ml", + "max_contains_weight": "3 kg", + "max_item_length": "14 cm", + "moves": 80 + } ], "warmth": 15, "material_thickness": 0.5, @@ -788,9 +1180,27 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 6, 7 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "3 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1300 ml", + "max_contains_weight": "3 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "13 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "13 cm", + "moves": 80 + } ], "warmth": 25, "material_thickness": 0.3, @@ -922,8 +1332,13 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 2, 2 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "3 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 80 + } ], "warmth": 20, "material_thickness": 0.2, @@ -949,8 +1364,13 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 80, "encumbrance": [ 1, 1 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "3 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 80 + } ], "warmth": 5, "material_thickness": 0.15, @@ -1018,10 +1438,34 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 85, "encumbrance": [ 10, 10 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "2 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "2 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 50, "material_thickness": 1.0, @@ -1049,12 +1493,48 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 80, "encumbrance": [ 8, 8 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 20, "material_thickness": 2, @@ -1101,10 +1581,34 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 15, 15 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1000 ml", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1000 ml", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 80, "material_thickness": 2, @@ -1132,12 +1636,48 @@ { "covers": [ "leg_l", "leg_r" ], "coverage": 90, "encumbrance": [ 7, 10 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 15, "material_thickness": 0.3, @@ -1164,12 +1704,48 @@ { "covers": [ "leg_l", "leg_r" ], "coverage": 90, "encumbrance": [ 9, 11 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 50, "material_thickness": 0.5, @@ -1207,12 +1783,48 @@ { "covers": [ "leg_l", "leg_r" ], "coverage": 90, "encumbrance": [ 8, 10 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1500 ml", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1500 ml", + "max_contains_weight": "4 kg", + "max_item_length": "24 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 30, "material_thickness": 1.5, @@ -1564,12 +2176,48 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 85, "encumbrance": [ 7, 7 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "3 L", "max_contains_weight": "6 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "3 L", "max_contains_weight": "6 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "3 L", + "max_contains_weight": "6 kg", + "max_item_length": "31 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "3 L", + "max_contains_weight": "6 kg", + "max_item_length": "31 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "25 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "25 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 10, "material_thickness": 3, @@ -1639,10 +2287,27 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 13, 13 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "16 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "16 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "600 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 30, "material_thickness": 0.5, @@ -1667,10 +2332,27 @@ "encumbrance": 2, "max_encumbrance": 5, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "16 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "16 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "600 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 5, "material_thickness": 0.5, @@ -1695,12 +2377,48 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 10, 10 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "900 ml", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "900 ml", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 120 + } ], "warmth": 50, "material_thickness": 1, diff --git a/data/json/items/armor/gloves.json b/data/json/items/armor/gloves.json index 8572ff3ca4a57..4422500ac804f 100644 --- a/data/json/items/armor/gloves.json +++ b/data/json/items/armor/gloves.json @@ -346,6 +346,29 @@ "proportional": { "weight": 1.6, "volume": 1.6 }, "flags": [ "WATERPROOF" ] }, + { + "id": "gloves_wsurvivor_nofur", + "type": "ARMOR", + "name": { "str": "pair of faux fur winter survivor gloves", "str_pl": "pairs of faux fur winter survivor gloves" }, + "description": "A pair of customized, Kevlar armored faux fur gloves, modified to be easy to wear while providing maximum protection under extreme conditions. While not quite as warm as real fur it's pretty good.", + "copy-from": "gloves_wsurvivor", + "material": [ "kevlar_layered", "faux_fur" ], + "symbol": "[", + "looks_like": "gloves_winter", + "color": "pink", + "warmth": 60 + }, + { + "id": "xl_gloves_wsurvivor_nofur", + "type": "ARMOR", + "name": { "str": "pair of XL faux fur winter survivor gloves", "str_pl": "pairs of XL faux fur winter survivor gloves" }, + "copy-from": "gloves_wsurvivor_nofur", + "material": [ "kevlar_layered", "faux_fur" ], + "description": "A pair of customized and oversized Kevlar armored faux fur gloves, modified to be easy to wear while providing maximum protection under extreme conditions. While not quite as warm as real fur it's pretty good.", + "color": "pink", + "proportional": { "weight": 1.5, "volume": 1.5 }, + "flags": [ "VARSIZE", "WATERPROOF", "STURDY", "OVERSIZE" ] + }, { "id": "gloves_hsurvivor", "type": "ARMOR", diff --git a/data/json/items/armor/helmets.json b/data/json/items/armor/helmets.json index 7bf094cced35b..f2df7ff5c8570 100644 --- a/data/json/items/armor/helmets.json +++ b/data/json/items/armor/helmets.json @@ -441,7 +441,7 @@ "id": "helmet_skull", "type": "ARMOR", "name": { "str": "large wolf skull" }, - "description": "The top part of an unusually large wolf skull equiped with leather straps to secure it on your head. Every inch of it has been carved with a maze of fine intertwined symbols that make your eyes water.", + "description": "The top part of an unusually large wolf skull equipped with leather straps to secure it on your head. Every inch of it has been carved with a maze of fine intertwined symbols that make your eyes water.", "weight": "1 kg", "volume": "3 L", "price": 1400, diff --git a/data/json/items/armor/hoods.json b/data/json/items/armor/hoods.json index 04f53e1fc10bb..4e3649c008d37 100644 --- a/data/json/items/armor/hoods.json +++ b/data/json/items/armor/hoods.json @@ -118,6 +118,30 @@ "proportional": { "weight": 1.25, "volume": 1.3, "price": 1.25 }, "flags": [ "VARSIZE", "STURDY", "OVERSIZE", "HELMET_COMPAT", "OUTER", "WATERPROOF" ] }, + { + "id": "hood_wsurvivor_nofur", + "type": "ARMOR", + "name": { "str": "winter faux fur survivor hood" }, + "description": "A customized armored faux fur and Kevlar hood, warm and durable. If not quite as warm as real fur.", + "copy-from": "hood_wsurvivor", + "material": [ "kevlar_layered", "faux_fur" ], + "symbol": "[", + "warmth": 65, + "looks_like": "hood_survivor", + "color": "pink" + }, + { + "id": "xl_hood_wsurvivor_nofur", + "type": "ARMOR", + "copy-from": "hood_wsurvivor", + "material": [ "kevlar_layered", "faux_fur" ], + "color": "pink", + "warmth": 65, + "name": { "str": "XL faux fur winter survivor hood" }, + "description": "A customized and oversized armored faux fur and Kevlar hood, warm and durable. If not quite as warm as real fur.", + "proportional": { "weight": 1.25, "volume": 1.3, "price": 1.25 }, + "flags": [ "VARSIZE", "STURDY", "OVERSIZE", "HELMET_COMPAT", "OUTER", "WATERPROOF" ] + }, { "id": "hood_rain", "type": "ARMOR", diff --git a/data/json/items/armor/legs_armor.json b/data/json/items/armor/legs_armor.json index 3befa36a1ca9e..60502c2801cd6 100644 --- a/data/json/items/armor/legs_armor.json +++ b/data/json/items/armor/legs_armor.json @@ -4,7 +4,7 @@ "type": "ARMOR", "category": "armor", "name": { "str": "pair of 2-by-shin guards", "str_pl": "pairs of 2-by-shin guards" }, - "description": "A pair of improvised shin guards made from broken pieces of a two by four that are tied to your shins with rags and string. They offer good protection, but are really hard to run with.", + "description": "A pair of improvised shin guards made from broken pieces of a plank that are tied to your shins with rags and string. They offer good protection, but are really hard to run with.", "weight": "300 g", "volume": "1500 ml", "price": 500, @@ -79,10 +79,34 @@ "encumbrance": 24, "max_encumbrance": 30, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "3250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "30 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "3250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "30 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "16 cm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "16 cm", + "moves": 100 + } ], "warmth": 30, "material_thickness": 2, @@ -145,7 +169,7 @@ "id": "chaps_cut_resistant", "type": "ARMOR", "name": { "str_sp": "chainsaw chaps" }, - "description": "A pair of tough chaps made of kevlar. Chainsaw kickbacks are potentially fatal; personal protective equipment like these chaps help protect your femoral arteries. The layered kevlar is designed to fray on contact with the chain and bind up the tool.", + "description": "A pair of tough chaps made of Kevlar. Chainsaw kickbacks are potentially fatal; personal protective equipment like these chaps help protect your femoral arteries. The layered Kevlar is designed to fray on contact with the chain and bind up the tool.", "weight": "1519 g", "copy-from": "chaps_leather", "price": 7800, @@ -171,8 +195,20 @@ "encumbrance": 13, "max_encumbrance": 15, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 100 + } ], "warmth": 20, "material_thickness": 2, @@ -388,12 +424,48 @@ "encumbrance": 8, "max_encumbrance": 16, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1000 ml", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1000 ml", + "max_contains_weight": "2 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "15 cm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "15 cm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 100 + } ], "warmth": 12, "material_thickness": 3, @@ -450,14 +522,62 @@ "encumbrance": 10, "max_encumbrance": 20, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "3 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "3 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "2 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "13 cm", + "moves": 100 + } ], "warmth": 15, "material_thickness": 4, @@ -479,7 +599,7 @@ "type": "ARMOR", "category": "armor", "name": { "str_sp": "EOD trousers" }, - "description": "Thick armored trousers constructed from kevlar and nomex for explosive ordnance disposal. It is designed to protect against overpressure, fragmentation, impact and heat.", + "description": "Thick armored trousers constructed from Kevlar and Nomex for explosive ordnance disposal. They are designed to protect against overpressure, fragmentation, impact, and heat.", "weight": "6400 g", "volume": "12 L", "price": 200000, @@ -499,7 +619,7 @@ "type": "ARMOR", "category": "armor", "name": { "str_sp": "light EOD trousers" }, - "description": "Armored trousers constructed from kevlar and nomex designed to protect against overpressure, fragmentation, impact and heat in hostile environments. It is lighter than normal EOD armor to provide more maneuverability.", + "description": "Armored trousers constructed from Kevlar and Nomex, designed to protect against overpressure, fragmentation, impact, and heat in hostile environments. They are lighter than normal EOD armor to provide more maneuverability.", "weight": "3000 g", "volume": "12 L", "price": 200000, diff --git a/data/json/items/armor/legs_clothes.json b/data/json/items/armor/legs_clothes.json index 1df9f8e7f6f65..2cec4d2ef900e 100644 --- a/data/json/items/armor/legs_clothes.json +++ b/data/json/items/armor/legs_clothes.json @@ -18,10 +18,13 @@ "encumbrance": 7, "max_encumbrance": 11, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "165 mm", + "moves": 400 + } ], "warmth": 8, "material_thickness": 0.3, @@ -45,8 +48,20 @@ "covers": [ "leg_l", "leg_r" ], "coverage": 50, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1000 ml", + "max_contains_weight": "3 kg", + "max_item_length": "17 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1000 ml", + "max_contains_weight": "3 kg", + "max_item_length": "17 cm", + "moves": 80 + } ], "warmth": 5, "material_thickness": 0.1, @@ -93,10 +108,13 @@ { "covers": [ "foot_l", "foot_r" ], "coverage": 100, "encumbrance": [ 19, 19 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1600 ml", + "max_contains_weight": "4 kg", + "max_item_length": "18 cm", + "moves": 80 + } ], "warmth": 5, "material_thickness": 2, @@ -178,14 +196,48 @@ "encumbrance": 12, "max_encumbrance": 16, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 10, "material_thickness": 0.25, - "flags": [ "VARSIZE", "POCKETS" ] + "flags": [ "VARSIZE", "POCKETS" ], + "snippet_category": [ + { "id": "jeans1", "text": "A pair of black jeans with two deep pockets." }, + { "id": "jeans2", "text": "A pair of black skinny jeans." }, + { "id": "jeans3", "text": "A stylish pair of bleached denim jeans." }, + { "id": "jeans4", "text": "A pair of old baggy jeans. The heels are all ripped up." }, + { + "id": "jeans5", + "text": "An old pair of blue jeans. You are not sure if they are faded through age or stone washed." + } + ] }, { "id": "jeans_red", @@ -207,10 +259,34 @@ "encumbrance": 12, "max_encumbrance": 16, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 10, "material_thickness": 0.25, @@ -231,7 +307,15 @@ "color": "dark_gray", "covers": [ "leg_l", "leg_r" ], "coverage": 50, - "pocket_data": [ { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 80 } ], + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1000 ml", + "max_contains_weight": "3 kg", + "max_item_length": "19 cm", + "moves": 80 + } + ], "warmth": 20, "material_thickness": 0.3, "flags": [ "VARSIZE" ] @@ -251,7 +335,15 @@ "color": "dark_gray", "covers": [ "leg_l", "leg_r" ], "coverage": 50, - "pocket_data": [ { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 80 } ], + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1000 ml", + "max_contains_weight": "3 kg", + "max_item_length": "19 cm", + "moves": 80 + } + ], "warmth": 10, "material_thickness": 1.5, "flags": [ "VARSIZE" ] @@ -388,10 +480,34 @@ "encumbrance": 7, "max_encumbrance": 11, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 15, "material_thickness": 0.2, @@ -416,12 +532,70 @@ "encumbrance": 9, "max_encumbrance": 18, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "3 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "3 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } + ], + "snippet_category": [ + { + "id": "apants_UCP", + "text": "A tough pair of pants Favored by the military. This pair came in a grey, green and tan camouflage. The pattern is notable for it's lack of black." + }, + { + "id": "apants_desert", + "text": "A tough pair of military pants patterned in desert camouflage. They are lined with deep pockets." + }, + { + "id": "apants_MARPAT", + "text": "A tough pair of military pants with a black, green and tan pixel camouflage pattern. Commonly seen worn by US Marines." + }, + { + "id": "apants_BDU", + "text": "A tough pair of military pants with a black, brown, green and tan camouflage pattern. They look old." + }, + { + "id": "mil_pants_navy", + "text": "A tough pair of military pants with a predominantly dark blue camouflage. Favored by the navy." + } ], "warmth": 20, "material_thickness": 0.25, @@ -445,14 +619,48 @@ "encumbrance": 8, "max_encumbrance": 16, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "3 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "3 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 15, "material_thickness": 0.25, @@ -477,10 +685,34 @@ "encumbrance": 2, "max_encumbrance": 5, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 15, "material_thickness": 0.2, @@ -505,10 +737,34 @@ "encumbrance": 16, "max_encumbrance": 20, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 80, "material_thickness": 1, @@ -545,8 +801,20 @@ "encumbrance": 15, "max_encumbrance": 17, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "1 kg", + "max_item_length": "19 cm", + "moves": 80 + } ], "warmth": 25, "material_thickness": 0.75, @@ -572,10 +840,34 @@ "encumbrance": 6, "max_encumbrance": 10, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 70, "material_thickness": 1, @@ -600,10 +892,34 @@ "covers": [ "leg_l", "leg_r" ], "coverage": 40, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 5, "material_thickness": 0.2, @@ -628,10 +944,34 @@ "encumbrance": 2, "max_encumbrance": 5, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 5, "material_thickness": 0.2, @@ -656,12 +996,48 @@ "encumbrance": 2, "max_encumbrance": 5, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 5, "material_thickness": 0.2, @@ -686,8 +1062,25 @@ "encumbrance": 1, "max_encumbrance": 2, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } + ], + "snippet_category": [ + { "id": "denim_shorts1", "text": "A pair of stone wash denim shorts." }, + { "id": "denim_shorts2", "text": "An old pair of denim jeans that have been cut down to shorts." }, + { "id": "denim_shorts3", "text": "A pair of brand-name black denim shorts." } ], "warmth": 5, "material_thickness": 0.25, @@ -773,10 +1166,34 @@ "encumbrance": 2, "max_encumbrance": 5, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 15, "material_thickness": 0.2, @@ -802,10 +1219,34 @@ "encumbrance": 2, "max_encumbrance": 5, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 15, "material_thickness": 0.3, @@ -835,14 +1276,48 @@ "encumbrance": 11, "max_encumbrance": 22, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1250 ml", + "max_contains_weight": "4 kg", + "max_item_length": "19 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1080 ml", + "max_contains_weight": "4 kg", + "max_item_length": "165 mm", + "moves": 100 + } ], "warmth": 50, "material_thickness": 0.5, diff --git a/data/json/items/armor/storage.json b/data/json/items/armor/storage.json index bd554b679a081..fd01f42405db0 100644 --- a/data/json/items/armor/storage.json +++ b/data/json/items/armor/storage.json @@ -808,6 +808,36 @@ "material_thickness": 0.2, "flags": [ "VARSIZE", "WATER_FRIENDLY", "BELTED" ] }, + { + "id": "armrig", + "type": "ARMOR", + "name": { "str": "armband pouch", "str_pl": "armband pouches" }, + "description": "A small pouch that can be worn on the upper arm using buckled straps. This is a favoured item among sports & camping enthusiasts.", + "weight": "205 g", + "volume": "550 ml", + "price": 3000, + "price_postapoc": 250, + "material": [ "cotton", "plastic" ], + "symbol": "[", + "looks_like": "armguard_soft", + "color": "dark_gray", + "covers": [ "arm_l", "arm_r" ], + "sided": true, + "coverage": 20, + "encumbrance": 2, + "max_encumbrance": 7, + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "1 kg", + "max_item_length": "15 cm", + "moves": 200 + } + ], + "material_thickness": 2, + "flags": [ "VARSIZE", "WATER_FRIENDLY", "BELTED" ] + }, { "id": "makeshift_knapsack", "type": "ARMOR", @@ -1017,7 +1047,7 @@ "warmth": 8, "material_thickness": 2, "properties": [ [ "creature_size_capacity", "SMALL" ] ], - "use_action": "CAPTURE_MONSTER_ACT", + "use_action": [ "CAPTURE_MONSTER_ACT" ], "flags": [ "BELTED", "WATERPROOF" ] }, { @@ -1934,5 +1964,36 @@ "warmth": 2, "material_thickness": 1, "flags": [ "BELTED", "WATER_FRIENDLY" ] + }, + { + "id": "bookstrap", + "type": "ARMOR", + "name": { "str": "bookstrap" }, + "description": "An old-fashioned book strap carrier. Guaranteed to make you feel like a proper scholar.", + "weight": "204 g", + "volume": "350 ml", + "price": 12000, + "price_postapoc": 450, + "material": [ "leather" ], + "symbol": "[", + "looks_like": "leather_belt", + "color": "brown", + "covers": [ "leg_l", "leg_r" ], + "sided": true, + "coverage": 5, + "encumbrance": 0, + "max_encumbrance": 11, + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "min_item_volume": "500 ml", + "max_contains_volume": "3 L", + "max_contains_weight": "4 kg", + "max_item_length": "40 cm", + "moves": 400 + } + ], + "material_thickness": 1, + "flags": [ "BELTED", "WATER_FRIENDLY" ] } ] diff --git a/data/json/items/armor/suits_protection.json b/data/json/items/armor/suits_protection.json index 31b7268336473..c8b916b003ebc 100644 --- a/data/json/items/armor/suits_protection.json +++ b/data/json/items/armor/suits_protection.json @@ -19,10 +19,34 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 100, "encumbrance": [ 10, 10 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "3 kg", + "max_item_length": "18 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "3 kg", + "max_item_length": "18 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "3 kg", + "max_item_length": "18 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "3 kg", + "max_item_length": "18 cm", + "moves": 80 + } ], "warmth": 35, "material_thickness": 7, @@ -73,8 +97,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 18, 18 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "3 kg", + "max_item_length": "165 mm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "3 kg", + "max_item_length": "165 mm", + "moves": 80 + } ], "warmth": 20, "material_thickness": 5, @@ -156,8 +192,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 21, 21 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "2 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "3 kg", + "max_item_length": "165 mm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "3 kg", + "max_item_length": "165 mm", + "moves": 80 + } ], "warmth": 60, "material_thickness": 4, @@ -193,8 +241,20 @@ { "covers": [ "leg_l", "leg_r" ], "coverage": 90, "encumbrance": [ 12, 12 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "165 mm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "165 mm", + "moves": 80 + } ], "warmth": 25, "material_thickness": 4, @@ -337,10 +397,34 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 17, 17 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "165 mm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "165 mm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "3 kg", + "max_item_length": "145 mm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "3 kg", + "max_item_length": "145 mm", + "moves": 120 + } ], "warmth": 25, "material_thickness": 4, @@ -510,10 +594,13 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 100, "encumbrance": [ 10, 10 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "2 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "2 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 150 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 150 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1600 ml", + "max_contains_weight": "3 kg", + "max_item_length": "18 cm", + "moves": 120 + } ], "warmth": 25, "material_thickness": 2, @@ -694,6 +781,30 @@ "proportional": { "weight": 1.125, "volume": 1.13, "price": 1.25 }, "flags": [ "VARSIZE", "WATERPROOF", "RAINPROOF", "POCKETS", "STURDY", "OVERSIZE" ] }, + { + "id": "wsurvivor_suit_nofur", + "type": "ARMOR", + "name": { "str": "winter survivor suit" }, + "description": "A warm and heavy hand-built combination armor made from a reinforced bulletproof vest and a faux fur insulated leather jumpsuit. Protects from the elements as well as from harm.", + "copy-from": "wsurvivor_suit", + "material": [ "kevlar_layered", "faux_fur" ], + "symbol": "[", + "warmth": 65, + "looks_like": "survivor_suit", + "color": "pink" + }, + { + "id": "xl_wsurvivor_suit_nofur", + "type": "ARMOR", + "name": { "str": "XL winter survivor suit" }, + "copy-from": "wsurvivor_suit", + "material": [ "kevlar_layered", "faux_fur" ], + "description": "A warm, heavy and oversized hand-built combination armor made from a reinforced bulletproof vest and a faux fur insulated leather jumpsuit. Protects from the elements as well as from harm.", + "color": "pink", + "warmth": 65, + "proportional": { "weight": 1.125, "volume": 1.13, "price": 1.25 }, + "flags": [ "VARSIZE", "WATERPROOF", "POCKETS", "HOOD", "RAINPROOF", "STURDY", "OVERSIZE" ] + }, { "id": "gambeson", "type": "ARMOR", @@ -1136,5 +1247,34 @@ "material_thickness": 6, "environmental_protection": 3, "flags": [ "OVERSIZE", "VARSIZE", "WATERPROOF", "POCKETS", "HOOD", "RAINPROOF", "STURDY" ] + }, + { + "id": "mil_flight_suit", + "type": "ARMOR", + "name": { "str": "military flight suit" }, + "description": "A sage-green US military flight suit. It has various insignia embroidered onto it.", + "weight": "1315 g", + "volume": "4687 ml", + "price": 22065, + "price_postapoc": 500, + "to_hit": -3, + "material": [ "nomex" ], + "symbol": "[", + "looks_like": "touring_suit", + "color": "green", + "armor_portion_data": [ + { "covers": [ "torso" ], "coverage": 95, "encumbrance": [ 1, 2 ] }, + { "covers": [ "arm_l", "arm_r" ], "coverage": 50, "encumbrance": [ 1, 1 ] }, + { "covers": [ "leg_l", "leg_r" ], "coverage": 80, "encumbrance": [ 1, 2 ] } + ], + "pocket_data": [ + { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "2 kg", "moves": 80 }, + { "pocket_type": "CONTAINER", "max_contains_volume": "700 ml", "max_contains_weight": "2 kg", "moves": 80 }, + { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, + { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + ], + "warmth": 25, + "material_thickness": 0.8, + "flags": [ "VARSIZE", "POCKETS" ] } ] diff --git a/data/json/items/armor/swimming.json b/data/json/items/armor/swimming.json index b4af0dfd40176..448ea1349da71 100644 --- a/data/json/items/armor/swimming.json +++ b/data/json/items/armor/swimming.json @@ -58,10 +58,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 100, "encumbrance": [ 11, 11 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "2 kg", + "max_item_length": "11 cm", + "moves": 130 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "2 kg", + "max_item_length": "11 cm", + "moves": 130 + } ], "warmth": 50, "material_thickness": 3, @@ -188,10 +198,20 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 2, 4 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 100 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "2 kg", + "max_item_length": "11 cm", + "moves": 130 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "2 kg", + "max_item_length": "11 cm", + "moves": 130 + } ], "warmth": 30, "material_thickness": 2, diff --git a/data/json/items/armor/torso_armor.json b/data/json/items/armor/torso_armor.json index b5f148f4ffa85..77e2660c19f8a 100644 --- a/data/json/items/armor/torso_armor.json +++ b/data/json/items/armor/torso_armor.json @@ -512,7 +512,7 @@ "type": "TOOL_ARMOR", "category": "armor", "name": { "str": "EOD jacket" }, - "description": "A thick armored jacket constructed from kevlar and nomex for explosive ordnance disposal. It is designed to protect against overpressure, fragmentation, impact and heat.", + "description": "A thick armored jacket constructed from Kevlar and Nomex for explosive ordnance disposal. It is designed to protect against overpressure, fragmentation, impact, and heat.", "weight": "16300 g", "volume": "15 L", "price": 200000, @@ -533,7 +533,7 @@ "type": "TOOL_ARMOR", "category": "armor", "name": { "str": "light EOD jacket" }, - "description": "An armored jacket constructed from kevlar and nomex designed to protect against overpressure, fragmentation, impact and heat in hostile environments. It is lighter than normal EOD armor to provide more maneuverability and can be worn over ballistic armor.", + "description": "An armored jacket constructed from Kevlar and Nomex, designed to protect against overpressure, fragmentation, impact, and heat in hostile environments. It is lighter than normal EOD armor to provide more maneuverability and can be worn over ballistic armor.", "weight": "7000 g", "volume": "15 L", "price": 200000, diff --git a/data/json/items/armor/torso_clothes.json b/data/json/items/armor/torso_clothes.json index 2233a6eecca07..149013aa8211e 100644 --- a/data/json/items/armor/torso_clothes.json +++ b/data/json/items/armor/torso_clothes.json @@ -18,8 +18,20 @@ { "covers": [ "leg_l", "leg_r" ], "coverage": 70, "encumbrance": [ 2, 2 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "1 L", "max_contains_weight": "3 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "3 kg", + "max_item_length": "21 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "1 L", + "max_contains_weight": "3 kg", + "max_item_length": "21 cm", + "moves": 80 + } ], "warmth": 15, "material_thickness": 2, @@ -31,7 +43,7 @@ "repairs_like": "apron_leather", "type": "ARMOR", "name": { "str": "cut-resistant apron" }, - "description": "An apron made of kevlar fabric which provides excellent protection from cuts.", + "description": "An apron made of Kevlar fabric which provides excellent protection from cuts.", "copy-from": "apron_leather", "price": 3800, "material": [ "kevlar" ] @@ -54,10 +66,27 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 85, "encumbrance": [ 16, 16 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "2 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "2 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "2 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "750 ml", + "max_contains_weight": "3 kg", + "max_item_length": "11 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "750 ml", + "max_contains_weight": "3 kg", + "max_item_length": "11 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "350 ml", + "max_contains_weight": "2 kg", + "max_item_length": "9 cm", + "moves": 100 + } ], "warmth": 20, "material_thickness": 1.5, @@ -104,7 +133,15 @@ { "covers": [ "torso" ], "coverage": 90, "encumbrance": [ 4, 5 ] }, { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 4, 4 ] } ], - "pocket_data": [ { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 80 } ], + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "max_contains_volume": "350 ml", + "max_contains_weight": "2 kg", + "max_item_length": "9 cm", + "moves": 80 + } + ], "warmth": 10, "material_thickness": 0.1, "flags": [ "VARSIZE", "FANCY" ] @@ -256,9 +293,13 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 6, 6 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "2 L", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "3 L", + "max_contains_weight": "4 kg", + "max_item_length": "36 cm", + "moves": 80 + } ], "warmth": 30, "material_thickness": 1, @@ -445,8 +486,13 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 75, "encumbrance": [ 5, 5 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "500 ml", + "max_contains_weight": "3 kg", + "max_item_length": "10 cm", + "moves": 80 + } ], "warmth": 5, "material_thickness": 0.1, @@ -490,8 +536,20 @@ "covers": [ "torso" ], "coverage": 90, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "15 cm", + "moves": 80 + } ], "warmth": 5, "material_thickness": 0.1, @@ -515,7 +573,15 @@ { "covers": [ "torso" ], "coverage": 90, "encumbrance": [ 3, 5 ] }, { "covers": [ "arm_l", "arm_r" ], "coverage": 90, "encumbrance": [ 3, 3 ] } ], - "pocket_data": [ { "pocket_type": "CONTAINER", "max_contains_volume": "500 ml", "max_contains_weight": "1 kg", "moves": 80 } ], + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "max_contains_volume": "650 ml", + "max_contains_weight": "3 kg", + "max_item_length": "15 cm", + "moves": 80 + } + ], "warmth": 15, "material_thickness": 0.2, "flags": [ "VARSIZE" ] @@ -642,7 +708,15 @@ "coverage": 40, "encumbrance": 4, "max_encumbrance": 5, - "pocket_data": [ { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 80 } ], + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "max_contains_volume": "650 ml", + "max_contains_weight": "1 kg", + "max_item_length": "15 cm", + "moves": 80 + } + ], "warmth": 8, "material_thickness": 0.2, "snippet_category": [ @@ -761,7 +835,7 @@ "coverage": 50, "encumbrance": 4, "max_encumbrance": 5, - "pocket_data": [ { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 80 } ], + "pocket_data": [ { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 400 } ], "warmth": 5, "material_thickness": 0.4, "flags": [ "VARSIZE" ] @@ -785,10 +859,27 @@ "encumbrance": 4, "max_encumbrance": 8, "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "350 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 120 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "650 ml", + "max_contains_weight": "3 kg", + "max_item_length": "15 cm", + "moves": 80 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "15 cm", + "moves": 120 + }, + { + "pocket_type": "CONTAINER", + "max_contains_volume": "800 ml", + "max_contains_weight": "3 kg", + "max_item_length": "15 cm", + "moves": 120 + } ], "warmth": 20, "material_thickness": 1.5, @@ -814,9 +905,13 @@ { "covers": [ "arm_l", "arm_r" ], "coverage": 95, "encumbrance": [ 6, 6 ] } ], "pocket_data": [ - { "pocket_type": "CONTAINER", "max_contains_volume": "2 L", "max_contains_weight": "4 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 80 }, - { "pocket_type": "CONTAINER", "max_contains_volume": "250 ml", "max_contains_weight": "1 kg", "moves": 80 } + { + "pocket_type": "CONTAINER", + "max_contains_volume": "3 L", + "max_contains_weight": "4 kg", + "max_item_length": "36 cm", + "moves": 80 + } ], "warmth": 45, "material_thickness": 1, diff --git a/data/json/items/battery.json b/data/json/items/battery.json index bae1ef4a5f256..a3810b2ce5544 100644 --- a/data/json/items/battery.json +++ b/data/json/items/battery.json @@ -41,7 +41,7 @@ "type": "MAGAZINE", "category": "spare_parts", "name": { "str": "ultra-light plutonium fuel battery", "str_pl": "ultra-light plutonium fuel batteries" }, - "description": "This battery uses a thin plutonium-244 rod to stablize an exotic nanocompound. It is universally compatible with small devices. Although it stores a huge amount of power, it cannot be recharged.", + "description": "This battery uses a thin plutonium-244 rod to stabilize an exotic nanocompound. It is universally compatible with small devices. Although it stores a huge amount of power, it cannot be recharged.", "ascii_picture": "ultra_light_battery_plut", "weight": "80 g", "volume": "1 ml", @@ -201,7 +201,7 @@ "type": "MAGAZINE", "category": "spare_parts", "name": { "str": "medium plutonium fuel battery", "str_pl": "medium plutonium fuel batteries" }, - "description": "This battery uses a thin plutonium-244 rod to stablize an exotic nanocompound. It is universally compatible with all kinds of appliances and power tools. Although it stores a huge amount of power, it cannot be recharged.", + "description": "This battery uses a thin plutonium-244 rod to stabilize an exotic nanocompound. It is universally compatible with all kinds of appliances and power tools. Although it stores a huge amount of power, it cannot be recharged.", "ascii_picture": "medium_battery_plut", "weight": "1000 g", "volume": "525ml", @@ -281,7 +281,7 @@ "type": "MAGAZINE", "category": "spare_parts", "name": { "str": "heavy plutonium fuel battery", "str_pl": "heavy plutonium fuel batteries" }, - "description": "This battery uses a thin plutonium-244 rod to stablize an exotic nanocompound. It is universally compatible with all kinds of industrial-grade equipment and large tools. Although it stores a huge amount of power, it cannot be recharged.", + "description": "This battery uses a thin plutonium-244 rod to stabilize an exotic nanocompound. It is universally compatible with all kinds of industrial-grade equipment and large tools. Although it stores a huge amount of power, it cannot be recharged.", "ascii_picture": "heavy_battery_plut", "weight": "1600 g", "volume": "1500ml", @@ -302,7 +302,7 @@ "type": "MAGAZINE", "category": "spare_parts", "name": { "str": "military plutonium fuel cell" }, - "description": "This battery uses a huge plutonium-244 rod to stablize an exotic nanocompound. It was used in military mech-suits, was highly experimental, and had no civilian applications. Although it stores a stupendous amount of power, it cannot be recharged.", + "description": "This battery uses a huge plutonium-244 rod to stabilize an exotic nanocompound. It was used in military mech-suits, was highly experimental, and had no civilian applications. Although it stores a stupendous amount of power, it cannot be recharged.", "weight": "64000 g", "volume": "30 L", "price": 100000, diff --git a/data/json/items/book/fabrication.json b/data/json/items/book/fabrication.json index 3de5357d9ddac..1854df6c1c4f1 100644 --- a/data/json/items/book/fabrication.json +++ b/data/json/items/book/fabrication.json @@ -155,6 +155,7 @@ "skill": "fabrication", "required_level": 3, "max_level": 6, + "proficiencies": [ { "proficiency": "prof_handloading", "time_factor": 0.8, "fail_factor": 0.5 } ], "intelligence": 9, "time": "30 m" }, diff --git a/data/json/items/book/maps.json b/data/json/items/book/maps.json index 0217ae0e222a4..716dde42326fc 100644 --- a/data/json/items/book/maps.json +++ b/data/json/items/book/maps.json @@ -165,5 +165,19 @@ ], "message": "You add roads and restaurants to your map." } + }, + { + "id": "satellitemap", + "copy-from": "abstractmap", + "type": "GENERIC", + "name": { "str": "satellite map" }, + "description": "This is a printed satellite map of the local area. Due to it's low quality and lack of map legend, you are unable to recognise most buildings. Using it will add terrain and roads to your map.", + "color": "light_gray", + "use_action": { + "type": "reveal_map", + "radius": 90, + "terrain": [ "hiway", "road", "bridge", "field", "swamp", "forest", "lake", "river" ], + "message": "You add terrain and roads to your map." + } } ] diff --git a/data/json/items/chemicals_and_resources.json b/data/json/items/chemicals_and_resources.json index 02431bc4db41e..acacbd54957c4 100644 --- a/data/json/items/chemicals_and_resources.json +++ b/data/json/items/chemicals_and_resources.json @@ -71,6 +71,23 @@ "ammo_type": "components", "count": 50 }, + { + "type": "AMMO", + "id": "material_gravel", + "category": "spare_parts", + "price": 0, + "price_postapoc": 0, + "name": { "str_sp": "gravel" }, + "symbol": "=", + "color": "dark_gray", + "description": "A handful of gravel, smaller than pebbles but larger than sand.", + "material": [ "stone" ], + "volume": "250 ml", + "weight": "6 g", + "bashing": 1, + "ammo_type": "components", + "count": 50 + }, { "type": "AMMO", "id": "material_limestone", @@ -224,7 +241,7 @@ "charges": 2, "category": "chems", "fun": -30, - "freezing_point": 17, + "freezing_point": -8, "conditional_names": [ { "type": "FLAG", "condition": "DIRTY", "name": "bleach spill" } ] }, { @@ -248,7 +265,7 @@ "charges": 2, "category": "chems", "fun": -30, - "freezing_point": -108 + "freezing_point": -78 }, { "type": "COMESTIBLE", @@ -365,7 +382,7 @@ "volume": "250 ml", "phase": "liquid", "category": "chems", - "freezing_point": 28, + "freezing_point": -2, "fun": -1 }, { @@ -386,7 +403,7 @@ "volume": "250 ml", "phase": "liquid", "category": "chems", - "freezing_point": 28, + "freezing_point": -2, "fun": -1 }, { @@ -1458,6 +1475,21 @@ "color": "brown", "looks_like": "bag_canvas" }, + { + "id": "gravelbag", + "type": "GENERIC", + "category": "other", + "name": { "str": "gravelbag" }, + "description": "This is a canvas sack filled with gravel. It can be used to construct simple barricades.", + "weight": "19200 g", + "volume": "16L", + "price": 0, + "price_postapoc": 10, + "material": [ "cotton", "powder" ], + "symbol": ")", + "color": "brown", + "looks_like": "bag_canvas" + }, { "id": "earthbag", "type": "GENERIC", diff --git a/data/json/items/classes/comestible.json b/data/json/items/classes/comestible.json index 7bf287864d6b2..f7401bdd4c5e7 100644 --- a/data/json/items/classes/comestible.json +++ b/data/json/items/classes/comestible.json @@ -13,6 +13,6 @@ "material": [ "powder" ], "symbol": "%", "charges": 100, - "freezing_point": -459 + "freezing_point": -274 } ] diff --git a/data/json/items/comestibles/alcohol.json b/data/json/items/comestibles/alcohol.json index f1ae939e677a8..6616bfec72467 100644 --- a/data/json/items/comestibles/alcohol.json +++ b/data/json/items/comestibles/alcohol.json @@ -24,7 +24,7 @@ "charges": 5, "flags": [ "EATEN_COLD", "MYCUS_OK" ], "fun": 25, - "freezing_point": 17 + "freezing_point": -8 }, { "type": "COMESTIBLE", @@ -52,7 +52,7 @@ "charges": 5, "flags": [ "EATEN_COLD" ], "fun": 15, - "freezing_point": 17 + "freezing_point": -8 }, { "type": "COMESTIBLE", @@ -80,7 +80,7 @@ "charges": 5, "flags": [ "EATEN_COLD" ], "fun": 15, - "freezing_point": 17 + "freezing_point": -8 }, { "type": "COMESTIBLE", @@ -106,7 +106,7 @@ "phase": "liquid", "charges": 5, "fun": 15, - "freezing_point": 17 + "freezing_point": -8 }, { "type": "COMESTIBLE", @@ -133,7 +133,7 @@ "charges": 5, "flags": [ "EATEN_COLD" ], "fun": 16, - "freezing_point": 15 + "freezing_point": -9 }, { "type": "COMESTIBLE", @@ -160,7 +160,7 @@ "charges": 5, "flags": [ "EATEN_COLD" ], "fun": 15, - "freezing_point": 17 + "freezing_point": -8 }, { "type": "COMESTIBLE", @@ -187,7 +187,7 @@ "charges": 5, "flags": [ "EATEN_COLD" ], "fun": 15, - "freezing_point": 17 + "freezing_point": -8 }, { "type": "COMESTIBLE", @@ -213,7 +213,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": 23, + "freezing_point": -5, "fun": 14, "vitamins": [ [ "calcium", 1 ] ] }, @@ -240,7 +240,7 @@ "material": [ "alcohol" ], "volume": "250 ml", "flags": [ "EATEN_COLD" ], - "freezing_point": -22, + "freezing_point": -30, "phase": "liquid", "charges": 7, "fun": 15 @@ -269,7 +269,7 @@ "phase": "liquid", "charges": 7, "flags": [ "EATEN_COLD", "NUTRIENT_OVERRIDE" ], - "freezing_point": -22, + "freezing_point": -30, "fun": 15 }, { @@ -296,7 +296,7 @@ "phase": "liquid", "charges": 7, "flags": [ "EATEN_COLD" ], - "freezing_point": -22, + "freezing_point": -30, "fun": 15 }, { @@ -324,7 +324,7 @@ "charges": 7, "fun": 15, "flags": [ "EATEN_COLD" ], - "freezing_point": -22 + "freezing_point": -30 }, { "type": "COMESTIBLE", @@ -351,7 +351,7 @@ "charges": 7, "fun": 18, "flags": [ "EATEN_COLD" ], - "freezing_point": -22 + "freezing_point": -30 }, { "type": "COMESTIBLE", @@ -375,7 +375,7 @@ "material": [ "alcohol" ], "volume": "250 ml", "flags": [ "EDIBLE_FROZEN" ], - "freezing_point": 0, + "freezing_point": -17, "phase": "liquid", "charges": 7, "fun": 10 @@ -407,7 +407,7 @@ "charges": 7, "flags": [ "EATEN_COLD" ], "fun": 10, - "freezing_point": 22 + "freezing_point": -6 }, { "type": "COMESTIBLE", @@ -433,7 +433,7 @@ "phase": "liquid", "charges": 7, "flags": [ "EATEN_COLD" ], - "freezing_point": -22, + "freezing_point": -30, "fun": 5 }, { @@ -461,7 +461,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD", "NUTRIENT_OVERRIDE" ], - "freezing_point": -22, + "freezing_point": -30, "fun": 4 }, { @@ -490,7 +490,7 @@ "charges": 7, "flags": [ "EATEN_COLD" ], "fun": 2, - "freezing_point": 20 + "freezing_point": -7 }, { "type": "COMESTIBLE", @@ -514,7 +514,7 @@ "material": [ "alcohol" ], "volume": "250 ml", "flags": [ "EATEN_COLD" ], - "freezing_point": -22, + "freezing_point": -30, "phase": "liquid", "charges": 7, "fun": 12 @@ -597,7 +597,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": -29, + "freezing_point": -34, "fun": 20, "vitamins": [ [ "vitA", 2 ], [ "vitC", 118 ], [ "calcium", 2 ], [ "iron", 1 ] ] }, @@ -625,7 +625,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": -29, + "freezing_point": -34, "fun": 20, "vitamins": [ [ "vitC", 135 ], [ "calcium", 1 ], [ "iron", 1 ] ] }, @@ -653,7 +653,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": -22, + "freezing_point": -30, "fun": 20 }, { @@ -679,7 +679,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": 23, + "freezing_point": -5, "fun": 10, "vitamins": [ [ "calcium", 1 ] ] }, @@ -707,7 +707,7 @@ "phase": "liquid", "charges": 7, "flags": [ "EATEN_COLD" ], - "freezing_point": 23, + "freezing_point": -5, "fun": 14 }, { @@ -736,7 +736,7 @@ "charges": 7, "flags": [ "EATEN_COLD" ], "fun": 16, - "freezing_point": 25 + "freezing_point": -4 }, { "type": "COMESTIBLE", @@ -763,7 +763,7 @@ "charges": 7, "flags": [ "EATEN_COLD" ], "fun": 16, - "freezing_point": 25 + "freezing_point": -4 }, { "type": "COMESTIBLE", @@ -790,7 +790,7 @@ "phase": "liquid", "charges": 7, "flags": [ "EATEN_COLD" ], - "freezing_point": 23, + "freezing_point": -5, "fun": 12 }, { @@ -817,7 +817,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": 23, + "freezing_point": -5, "fun": 8, "vitamins": [ [ "calcium", 1 ] ] }, @@ -846,7 +846,7 @@ "phase": "liquid", "charges": 7, "flags": [ "EATEN_COLD" ], - "freezing_point": -49, + "freezing_point": -45, "fun": 12 }, { @@ -873,7 +873,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": 23, + "freezing_point": -5, "fun": 10, "vitamins": [ [ "calcium", 1 ] ] }, @@ -901,7 +901,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": 23, + "freezing_point": -5, "fun": 14, "vitamins": [ [ "calcium", 1 ] ] }, @@ -929,7 +929,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": 23, + "freezing_point": -5, "fun": 14, "vitamins": [ [ "calcium", 1 ] ] }, @@ -956,7 +956,7 @@ "primary_material": "alcohol", "volume": "250 ml", "phase": "liquid", - "freezing_point": 23, + "freezing_point": -5, "fun": 14, "vitamins": [ [ "calcium", 1 ], [ "iron", 1 ] ], "flags": [ "EATEN_COLD" ] @@ -985,7 +985,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": 23, + "freezing_point": -5, "fun": 14, "vitamins": [ [ "calcium", 1 ] ] }, @@ -1015,7 +1015,7 @@ "fun": 18, "vitamins": [ [ "calcium", 1 ], [ "iron", 1 ] ], "flags": [ "EATEN_COLD" ], - "freezing_point": 23 + "freezing_point": -5 }, { "type": "COMESTIBLE", @@ -1044,7 +1044,7 @@ "flags": [ "EATEN_COLD", "EDIBLE_FROZEN" ], "fun": 8, "vitamins": [ [ "vitC", 20 ] ], - "freezing_point": -22 + "freezing_point": -30 }, { "type": "COMESTIBLE", @@ -1073,7 +1073,7 @@ "flags": [ "EATEN_COLD", "EDIBLE_FROZEN" ], "fun": -12, "vitamins": [ [ "vitC", 20 ] ], - "freezing_point": -22 + "freezing_point": -30 }, { "type": "COMESTIBLE", @@ -1098,7 +1098,7 @@ "material": [ "alcohol" ], "volume": "250 ml", "flags": [ "EATEN_COLD" ], - "freezing_point": -22, + "freezing_point": -30, "phase": "liquid", "charges": 7, "fun": 17 @@ -1155,7 +1155,7 @@ "material": [ "alcohol" ], "volume": "250 ml", "flags": [ "EATEN_COLD" ], - "freezing_point": -22, + "freezing_point": -30, "phase": "liquid", "charges": 7, "fun": 17 @@ -1205,7 +1205,7 @@ "phase": "liquid", "charges": 5, "flags": [ "EATEN_COLD" ], - "freezing_point": -27, + "freezing_point": -33, "fun": 20 }, { @@ -1233,7 +1233,7 @@ "phase": "liquid", "charges": 2, "flags": [ "EATEN_COLD" ], - "freezing_point": -22, + "freezing_point": -30, "fun": 20 }, { @@ -1259,7 +1259,7 @@ "volume": "500 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": -22, + "freezing_point": -30, "fun": 20 }, { @@ -1285,7 +1285,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": -22, + "freezing_point": -30, "fun": 20 }, { @@ -1368,7 +1368,7 @@ "flags": [ "EATEN_COLD", "FREEZERBURN" ], "fun": 20, "vitamins": [ [ "vitA", 5 ], [ "vitC", 3 ], [ "calcium", 12 ], [ "iron", 2 ] ], - "freezing_point": 12 + "freezing_point": -11 }, { "type": "COMESTIBLE", @@ -1394,7 +1394,7 @@ "charges": 5, "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": -22, + "freezing_point": -30, "fun": 20 }, { @@ -1421,7 +1421,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": -29, + "freezing_point": -34, "fun": 25, "vitamins": [ [ "vitA", 2 ], [ "vitC", 23 ], [ "calcium", 1 ], [ "iron", 2 ] ] }, @@ -1449,7 +1449,7 @@ "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "freezing_point": 23, + "freezing_point": -5, "fun": 10, "vitamins": [ [ "calcium", 1 ] ] } diff --git a/data/json/items/comestibles/bread.json b/data/json/items/comestibles/bread.json index 1fabede92c375..89e5844a82cd0 100644 --- a/data/json/items/comestibles/bread.json +++ b/data/json/items/comestibles/bread.json @@ -290,19 +290,41 @@ "weight": "45 g", "color": "brown", "spoils_in": "10 days", - "container": "bag_plastic", + "container": "can_medium", "comestible_type": "FOOD", "symbol": "%", "healthy": 1, - "calories": 74, + "calories": 130, "description": "A sweet bread, like cake.", "price": "272 cent", - "price_postapoc": "4 USD", + "price_postapoc": "2 USD", "material": [ "wheat" ], - "volume": "48 ml", + "volume": "480 ml", "charges": 10, "flags": [ "EATEN_HOT", "EATEN_COLD" ], "fun": 3, + "vitamins": [ [ "calcium", 4 ], [ "iron", 4 ] ] + }, + { + "type": "COMESTIBLE", + "id": "hallula", + "name": { "str_sp": "hallula" }, + "weight": "45 g", + "color": "brown", + "spoils_in": "10 days", + "container": "bag_plastic", + "comestible_type": "FOOD", + "symbol": "%", + "healthy": 1, + "calories": 61, + "description": "This type of bread is popular in Chile and Bolivia. Prepared using animal lard or oil, it resembles a large, round scone. In the pre-Cataclysm world, it would usually be filled with savory ingredients rather than sweet ones.", + "price": "272 cent", + "price_postapoc": "4 USD", + "material": [ "wheat" ], + "volume": "48 ml", + "charges": 16, + "flags": [ "EATEN_HOT", "EATEN_COLD" ], + "fun": 4, "vitamins": [ [ "calcium", 2 ], [ "iron", 6 ] ] } ] diff --git a/data/json/items/comestibles/brewing.json b/data/json/items/comestibles/brewing.json index 5ed9904171f38..add3d845d3d67 100644 --- a/data/json/items/comestibles/brewing.json +++ b/data/json/items/comestibles/brewing.json @@ -411,7 +411,7 @@ "type": "COMESTIBLE", "id": "brew_vinegar", "name": { "str": "unfermented vinegar" }, - "description": "Mixture of water, alcohol and fruit juice that will eventually become vinegar.", + "description": "Mixture of water, alcohol and fruit juice that will eventually become vinegar when fermented in a vat.", "weight": "15 g", "color": "yellow", "container": "jug_plastic", diff --git a/data/json/items/comestibles/carnivore.json b/data/json/items/comestibles/carnivore.json index 294cc6b837ddf..2338ef2278536 100644 --- a/data/json/items/comestibles/carnivore.json +++ b/data/json/items/comestibles/carnivore.json @@ -209,7 +209,7 @@ "flags": [ ], "use_action": { "target": "raw_cured_fatty_meat", - "msg": "You pat off a bit of extra brine from the slice, this piece of meat is now fully cured.", + "msg": "You pat off a bit of extra brine from the slice; this piece of meat is now fully cured.", "moves": 150, "type": "delayed_transform", "transform_age": 4800, diff --git a/data/json/items/comestibles/drink.json b/data/json/items/comestibles/drink.json index 534a4b82288d4..8b18bd6444b2c 100644 --- a/data/json/items/comestibles/drink.json +++ b/data/json/items/comestibles/drink.json @@ -39,7 +39,7 @@ "description": "Milk some almonds? Not quite, but blend them with water, yes! A dairy-free alternative strong in calcium! Rival to soy milk.", "price": 40, "price_postapoc": 50, - "material": [ "water" ], + "material": [ "water", "nut" ], "volume": "250 ml", "phase": "liquid", "vitamins": [ [ "vitA", 2 ], [ "calcium", 11 ] ], @@ -62,7 +62,7 @@ "description": "Milk some soybeans? Not quite, but blend them with water, yes! A dairy-free alternative strong in protein! Rival to almond milk.", "price": 40, "price_postapoc": 50, - "material": [ "water" ], + "material": [ "water", "veggy" ], "volume": "250 ml", "phase": "liquid", "vitamins": [ [ "vitA", 2 ], [ "iron", 1 ], [ "calcium", 7 ] ], @@ -102,7 +102,8 @@ "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, - "fun": 1 + "fun": 1, + "flags": [ "EATEN_HOT" ] }, { "type": "COMESTIBLE", @@ -162,10 +163,11 @@ "spoils_in": "10 days", "quench": 34, "healthy": 1, - "calories": 0, + "calories": 1, "description": "A healthy beverage made from chamomile flowers steeped in boiling water. Can be used to treat insomnia.", "price": 100, "price_postapoc": 25, + "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], "fun": 1 }, { @@ -388,14 +390,14 @@ "symbol": "~", "quench": 48, "healthy": 1, - "calories": 26, + "calories": 1, "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" ], + "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], "fun": 2 }, { @@ -496,14 +498,14 @@ "symbol": "~", "quench": 44, "healthy": 1, - "calories": 9, + "calories": 1, "description": "A healthy beverage made from herbs steeped in boiling water.", "price": 100, "price_postapoc": 25, "volume": "250 ml", "material": [ "water" ], "phase": "liquid", - "flags": [ "EATEN_HOT" ], + "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], "fun": 4 }, { @@ -646,11 +648,11 @@ "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, - "fun": 1 + "fun": 1, + "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ] }, { "id": "mex_chocolate", @@ -769,7 +771,8 @@ "phase": "liquid", "charges": 2, "vitamins": [ [ "vitA", 5 ], [ "vitB", 3 ], [ "vitC", 2 ], [ "calcium", 12 ] ], - "fun": 10 + "fun": 10, + "flags": [ "EATEN_HOT" ] }, { "type": "COMESTIBLE", @@ -832,6 +835,7 @@ "price": 0, "price_postapoc": 25, "volume": "250 ml", + "calories": 1, "material": [ "water" ], "phase": "liquid", "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], @@ -862,6 +866,31 @@ "vitamins": [ [ "vitC", 50 ], [ "calcium", 3 ] ], "fun": 3 }, + { + "type": "COMESTIBLE", + "id": "mycus_juice", + "name": "mycus juice", + "weight": "263 g", + "color": "light_gray", + "use_action": [ "MYCUS" ], + "spoils_in": "5 days", + "container": "bottle_plastic", + "comestible_type": "DRINK", + "symbol": "~", + "quench": 100, + "healthy": 2, + "calories": 462, + "description": "Freshly-squeezed from the fruit of the mycus.", + "price": 0, + "price_postapoc": 0, + "material": [ "water", "mushroom" ], + "primary_material": "water", + "volume": "250 ml", + "phase": "liquid", + "flags": [ "EATEN_COLD", "MYCUS_OK", "NUTRIENT_OVERRIDE" ], + "vitamins": [ [ "vitA", 16 ], [ "vitB", 16 ], [ "vitC", 20 ], [ "calcium", 20 ], [ "iron", 16 ] ], + "fun": 30 + }, { "type": "COMESTIBLE", "id": "rootbeer", @@ -920,14 +949,14 @@ "symbol": "~", "quench": 60, "calories": 66, - "description": "Consisting of a special blend of electrolytes and simple sugars, this beverage tastes like bottled sweat but rehydrates the body faster than water.", + "description": "A flavoured drink consisting of a special blend of electrolytes and simple sugars. It tastes vaguely like fruit with a slight chemical aftertaste.", "price": 55, "price_postapoc": 50, - "material": [ "water" ], + "material": [ "water", "junk" ], "volume": "250 ml", "phase": "liquid", "flags": [ "EATEN_COLD" ], - "fun": -1, + "fun": 1, "vitamins": [ [ "vitC", 1 ] ] }, { @@ -939,11 +968,12 @@ "spoils_in": "10 days", "quench": 34, "healthy": 1, - "calories": 0, + "calories": 1, "description": "A healthy beverage made from spurge flowers steeped in boiling water. Can be used to prevent asthma attacks.", "price": 100, "price_postapoc": 25, "fun": 1, + "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], "use_action": { "type": "consume_drug", "activation_message": "You no longer need to worry about asthma attacks, at least for a while.", @@ -961,14 +991,15 @@ "symbol": "~", "quench": 60, "calories": 12, - "description": "A basic oral rehydration therapy drink. It will rehydrate you faster than water, but it tastes kind of strange.", + "description": "A basic oral rehydration therapy drink. It will rehydrate you faster than water, but tastes like bottled sweat.", "price": 55, "price_postapoc": 50, "material": [ "water" ], + "flags": [ "EATEN_COLD" ], "volume": "250 ml", "looks_like": "sports_drink", "phase": "liquid", - "fun": -2 + "fun": -1 }, { "type": "COMESTIBLE", @@ -988,7 +1019,7 @@ "volume": "250 ml", "phase": "liquid", "fun": 2, - "freezing_point": 22 + "freezing_point": -5 }, { "type": "COMESTIBLE", @@ -1002,7 +1033,7 @@ "comestible_type": "DRINK", "symbol": "~", "quench": 40, - "calories": 2, + "calories": 1, "description": "The beverage of gentlemen everywhere, made from applying hot water to oxidized leaves of the tea plant /Camellia sinensis/.", "price": 90, "price_postapoc": 25, @@ -1108,7 +1139,6 @@ "color": "green", "fatigue_mod": 7, "stim": 2, - "calories": 2, "description": "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." }, { @@ -1117,7 +1147,6 @@ "name": { "str": "fruit tea" }, "copy-from": "tea", "color": "red", - "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/drink_other.json b/data/json/items/comestibles/drink_other.json index 153549c05982e..e5cb02f33e9f8 100644 --- a/data/json/items/comestibles/drink_other.json +++ b/data/json/items/comestibles/drink_other.json @@ -46,7 +46,7 @@ "flags": [ "EATEN_COLD", "RAW" ], "phase": "liquid", "vitamins": [ ], - "freezing_point": 30 + "freezing_point": -1 }, { "id": "mayonnaise", @@ -69,7 +69,7 @@ "fun": -1, "flags": [ "FREEZERBURN" ], "phase": "liquid", - "freezing_point": 25 + "freezing_point": -4 }, { "id": "ketchup", @@ -155,7 +155,7 @@ "phase": "liquid", "charges": 16, "fun": -4, - "freezing_point": 28 + "freezing_point": -2 }, { "type": "COMESTIBLE", @@ -179,7 +179,7 @@ "fun": -25, "flags": [ "NUTRIENT_OVERRIDE" ], "vitamins": [ ], - "freezing_point": 14 + "freezing_point": -26 }, { "type": "COMESTIBLE", @@ -213,7 +213,7 @@ "fun": 2, "flags": [ "USE_EAT_VERB" ], "vitamins": [ [ "calcium", 18 ], [ "iron", 23 ] ], - "freezing_point": -20 + "freezing_point": -29 }, { "id": "horseradish", diff --git a/data/json/items/comestibles/egg.json b/data/json/items/comestibles/egg.json index f6e1070108758..6780c71206c90 100644 --- a/data/json/items/comestibles/egg.json +++ b/data/json/items/comestibles/egg.json @@ -129,7 +129,7 @@ "comestible_type": "FOOD", "name": { "str": "Insect Egg" }, "abstract": "egg_insect", - "//": "5x the weight and 3x the nutrition of real chicken eggs, toxins equal 296g mutant meat", + "//": "5x the weight and 3x the nutrition of real chicken eggs, toxins equal to 296g mutant meat", "color": "white", "symbol": "%", "material": [ "egg" ], @@ -141,6 +141,7 @@ "fun": -20, "price": 0, "price_postapoc": 100, + "spoils_in": "5 days", "flags": [ "FREEZERBURN", "RAW" ], "vitamins": [ [ "vitA", 19 ], [ "calcium", 7 ], [ "iron", 18 ], [ "vitB", 66 ], [ "mutant_toxin", 25 ] ] }, @@ -149,15 +150,12 @@ "id": "ant_egg", "name": { "str": "ant egg" }, "copy-from": "egg_insect", - "color": "white", - "symbol": "%", "description": "A large, heavy ant egg, the size of a softball. Extremely nutritious, but incredibly gross.", "price": 175, "price_postapoc": 500, "material": [ "egg" ], - "volume": "600 ml", - "proportional": { "weight": 3, "calories": 3 }, - "vitamins": [ [ "vitA", 108 ], [ "calcium", 36 ], [ "iron", 48 ], [ "vitB", 252 ] ], + "proportional": { "weight": 4, "calories": 4, "volume": 4 }, + "vitamins": [ [ "vitA", 76 ], [ "calcium", 28 ], [ "iron", 72 ], [ "vitB", 264 ], [ "mutant_toxin", 100 ] ], "rot_spawn": "GROUP_EGG_ANT", "rot_spawn_chance": 60 }, @@ -213,7 +211,6 @@ "copy-from": "egg_insect", "color": "light_green", "symbol": "o", - "spoils_in": "2 days", "rot_spawn": "GROUP_EGG_DRAGONFLY", "rot_spawn_chance": 50 }, @@ -224,7 +221,6 @@ "copy-from": "egg_insect", "color": "yellow", "symbol": "o", - "spoils_in": "1 day", "description": "The fist-sized egg of a mutant firefly. Glows faintly in the dark.", "rot_spawn": "GROUP_EGG_FIREFLY", "rot_spawn_chance": 100 @@ -233,14 +229,26 @@ "type": "COMESTIBLE", "id": "egg_centipede", "name": { "str": "centipede egg" }, + "description": "A large, soft egg you got from butchering a mutant centipede. Inside, you can already see a bundle of spindly legs, twitching in anticipation.", "copy-from": "egg_insect", "color": "light_green", - "symbol": "o", - "spoils_in": "10 days", - "description": "A large, soft egg you got from butchering a mutant centipede. Inside, you can already see a bundle of spindly legs, twitching in anticipation.", + "proportional": { "weight": 2, "calories": 2, "volume": 2 }, + "vitamins": [ [ "vitA", 38 ], [ "calcium", 14 ], [ "iron", 36 ], [ "vitB", 132 ], [ "mutant_toxin", 50 ] ], "rot_spawn": "GROUP_EGG_CENTIPEDE", "rot_spawn_chance": 50 }, + { + "type": "COMESTIBLE", + "id": "egg_wasp", + "name": { "str": "wasp egg" }, + "copy-from": "egg_insect", + "color": "white", + "symbol": "o", + "spoils_in": "2 days", + "description": "A white, flexible wasp egg. It looks like a pristine grain of rice, grown to the size of your palm.", + "rot_spawn": "GROUP_EGG_WASP", + "rot_spawn_chance": 100 + }, { "type": "COMESTIBLE", "id": "razorclaw_roe", @@ -303,7 +311,7 @@ "flags": [ "EDIBLE_FROZEN" ], "charges": 16, "fun": -4, - "freezing_point": -459, + "freezing_point": -274, "vitamins": [ [ "vitB", 2 ] ] }, { @@ -354,6 +362,54 @@ "fun": 2, "flags": [ "EATEN_HOT", "FREEZERBURN" ] }, + { + "type": "COMESTIBLE", + "id": "deluxe_fried_eggs", + "name": { "str_sp": "deluxe fried eggs" }, + "copy-from": "scrambled_eggs", + "color": "yellow", + "symbol": "%", + "quench": -1, + "calories": 322, + "description": "Deluxe sunny-side up eggs fried with a runny yolk and just a bit of crisp on the edges with cheese or a vegetable, garnish and condiment.", + "price": 500, + "price_postapoc": 120, + "material": [ "egg", "veggy" ], + "fun": 2, + "flags": [ "EATEN_HOT", "FREEZERBURN" ] + }, + { + "type": "COMESTIBLE", + "id": "fried_egg_sandwich", + "name": { "str_sp": "fried egg sandwich" }, + "copy-from": "scrambled_eggs", + "color": "yellow", + "symbol": "%", + "quench": -1, + "calories": 422, + "description": "Sunny-side up eggs fried with a runny yolk and just a bit of crisp on the edges. Made into a sandwich, just like Sunday morning.", + "price": 500, + "price_postapoc": 120, + "material": [ "egg", "wheat" ], + "fun": 2, + "flags": [ "EATEN_HOT", "FREEZERBURN" ] + }, + { + "type": "COMESTIBLE", + "id": "deluxe_fried_egg_sandwich", + "name": { "str_sp": "deluxe fried egg sandwich" }, + "copy-from": "scrambled_eggs", + "color": "yellow", + "symbol": "%", + "quench": -1, + "calories": 422, + "description": "Deluxe sunny-side up eggs fried with a runny yolk and just a bit of crisp on the edges. Made into a sandwich, just like Sunday morning.", + "price": 500, + "price_postapoc": 120, + "material": [ "egg", "wheat" ], + "fun": 2, + "flags": [ "EATEN_HOT", "FREEZERBURN" ] + }, { "type": "COMESTIBLE", "id": "pickled_egg", diff --git a/data/json/items/comestibles/junkfood.json b/data/json/items/comestibles/junkfood.json index ba0922582c693..d4dcab3534861 100644 --- a/data/json/items/comestibles/junkfood.json +++ b/data/json/items/comestibles/junkfood.json @@ -37,6 +37,14 @@ "description": "Dry toaster pastries, usually coated with solid frosting. Sadly, these are not.", "fun": 10 }, + { + "type": "COMESTIBLE", + "id": "toastem4", + "name": { "str": "homemade toast-em" }, + "copy-from": "toastem", + "description": "Homemade toaster pastries, lacking icing but at least fruit filled.", + "fun": 10 + }, { "type": "COMESTIBLE", "id": "toasterpastryfrozen", @@ -68,6 +76,43 @@ "description": "A delicious fruit-filled pastry that you've cooked. It even comes with frosting!", "fun": 20 }, + { + "type": "COMESTIBLE", + "id": "homemade_toasterpastry", + "name": { "str": "toaster pastry", "str_pl": "toaster pastries" }, + "copy-from": "toasterpastryfrozen", + "quench": 4, + "description": "A delicious homemade fruit-filled pastry that you've cooked.", + "fun": 20 + }, + { + "type": "COMESTIBLE", + "id": "homemade_toasterpastry2", + "name": { "str": "toaster pastry with buttercream", "str_pl": "toaster pastries with buttercream" }, + "copy-from": "toasterpastryfrozen", + "quench": 4, + "description": "A delicious homemade fruit-filled pastry that you've cooked. It even comes with buttercream!", + "fun": 30 + }, + { + "type": "COMESTIBLE", + "id": "buttercream", + "name": { "str": "buttercream icing", "str_pl": "cups of buttercream icing" }, + "weight": "32 g", + "color": "brown", + "container": "bag_plastic", + "comestible_type": "FOOD", + "symbol": "%", + "calories": 288, + "description": "Smooth sugary buttercream icing. Almost good enough to forget the end of everything.", + "price": 100, + "price_postapoc": 400, + "material": [ "junk" ], + "volume": "1 L", + "charges": 8, + "flags": [ "EDIBLE_FROZEN" ], + "fun": 6 + }, { "type": "COMESTIBLE", "id": "chips2", @@ -317,6 +362,48 @@ "charges": 3, "fun": 3 }, + { + "type": "COMESTIBLE", + "id": "candy4", + "name": { "str_sp": "gummy candy" }, + "weight": "46 g", + "color": "yellow", + "spoils_in": "360 days", + "container": "bag_plastic", + "comestible_type": "FOOD", + "symbol": "%", + "healthy": -1, + "calories": 85, + "description": "A handful of colorful fruit and soda pop flavored gummy candies.", + "price": 180, + "price_postapoc": 200, + "material": [ "junk", "flesh" ], + "volume": "90 ml", + "flags": [ "EDIBLE_FROZEN" ], + "charges": 3, + "fun": 5 + }, + { + "type": "COMESTIBLE", + "id": "candy5", + "name": { "str_sp": "gummy candy" }, + "weight": "46 g", + "color": "light_green", + "spoils_in": "360 days", + "container": "bag_plastic", + "comestible_type": "FOOD", + "symbol": "%", + "healthy": -1, + "calories": 85, + "description": "A handful of colorful fruit and soda pop flavored vegan gummy candies made from agar. They have kind of a strange texture, but they're still tasty.", + "price": 180, + "price_postapoc": 200, + "material": [ "junk", "veggy" ], + "volume": "90 ml", + "flags": [ "EDIBLE_FROZEN" ], + "charges": 3, + "fun": 4 + }, { "type": "COMESTIBLE", "id": "candy3gator", @@ -476,7 +563,7 @@ "charges": 16, "fun": 5, "vitamins": [ [ "calcium", 2 ] ], - "freezing_point": -70 + "freezing_point": -57 }, { "type": "COMESTIBLE", @@ -1290,5 +1377,108 @@ "flags": [ "EATEN_HOT" ], "vitamins": [ [ "vitA", 3 ], [ "iron", 10 ] ], "fun": 4 + }, + { + "type": "COMESTIBLE", + "id": "chocolate_military", + "name": { "str": "military chocolate bar" }, + "color": "brown", + "symbol": "%", + "calories": 600, + "weight": "115 g", + "description": "A thick, dense bar of military chocolate. While tough to chew and not very appetizing, it will provide almost a day's worth of calories in portable form.", + "price": 250, + "price_postapoc": 500, + "material": [ "junk" ], + "volume": "500 ml", + "flags": [ "EDIBLE_FROZEN" ], + "fun": -3 + }, + { + "type": "COMESTIBLE", + "id": "gelatin_dessert_powder", + "name": "gelatin dessert powder", + "weight": "175 g", + "color": "white", + "spoils_in": "1461 days", + "container": "box_small", + "comestible_type": "FOOD", + "symbol": "%", + "quench": -8, + "calories": 400, + "description": "A small box of gelatin dessert powder. Just add water and set. Comes in a variety of flavors.", + "price": 5, + "price_postapoc": 10, + "material": [ "flesh", "junk" ], + "volume": "250ml", + "flags": [ "EATEN_COLD" ], + "fun": -2 + }, + { + "type": "COMESTIBLE", + "id": "gelatin_dessert_base", + "name": "unflavored gelatin dessert", + "weight": "232 g", + "color": "white", + "spoils_in": "7 days 12 hours", + "container": "cup_plastic", + "comestible_type": "FOOD", + "symbol": "%", + "calories": 100, + "description": "An unflavored jiggly, sugary treat made from gelatin and sugar. This would taste better with some flavor.", + "price": 210, + "price_postapoc": 250, + "material": [ "flesh", "junk" ], + "volume": "250 ml", + "phase": "solid", + "fun": 5 + }, + { + "type": "COMESTIBLE", + "id": "gelatin_dessert_processed", + "name": "processed gelatin dessert", + "description": "A jiggly, sugary treat made from gelatin and sugar. Comes in a variety of flavors. A kid favorite pre-Cataclysm.", + "fun": 20, + "copy-from": "gelatin_dessert_base" + }, + { + "type": "COMESTIBLE", + "id": "gelatin_dessert_homemade", + "name": "homemade gelatin dessert", + "color": "light_red", + "description": "A homemade jiggly, sugary treat made from gelatin and sugar flavored with fruit juice. A kid favorite pre-Cataclysm.", + "fun": 25, + "copy-from": "gelatin_dessert_base" + }, + { + "type": "COMESTIBLE", + "id": "gelatin_dessert_fruit", + "name": "fruit filled gelatin dessert", + "color": "light_red", + "description": "A jiggly, sugary treat made from gelatin and sugar flavored with fruit juice and set with fruit. A kid favorite pre-Cataclysm and a healthy treat", + "fun": 20, + "healthy": 1, + "copy-from": "gelatin_dessert_base" + }, + { + "type": "COMESTIBLE", + "id": "gelatin_dessert_vegan", + "name": "vegan gelatin dessert", + "color": "light_green", + "description": "A vegan friendly homemade jiggly, sugary treat made from agar and sugar flavored with fruit juice. A kid favorite pre-Cataclysm. They'll never know the difference.", + "fun": 20, + "material": [ "veggy", "junk" ], + "copy-from": "gelatin_dessert_base" + }, + { + "type": "COMESTIBLE", + "id": "gelatin_dessert_vegan_fruit", + "name": "fruit filled vegan gelatin dessert", + "color": "light_green", + "description": "A vegan friendly jiggly, sugary treat made from agar and sugar flavored with fruit juice and set with fruit. A kid favorite pre-Cataclysm and a healthy treat", + "fun": 15, + "material": [ "veggy", "junk" ], + "healthy": 1, + "copy-from": "gelatin_dessert_base" } ] diff --git a/data/json/items/comestibles/meat_dishes.json b/data/json/items/comestibles/meat_dishes.json index 13d6eb6577bd1..a9a28f0a6ed53 100644 --- a/data/json/items/comestibles/meat_dishes.json +++ b/data/json/items/comestibles/meat_dishes.json @@ -951,7 +951,11 @@ "type": "COMESTIBLE", "id": "deluxe_eggs", "name": { "str_sp": "deluxe scrambled eggs" }, - "conditional_names": [ { "type": "COMPONENT_ID", "condition": "mutant", "name": { "str_sp": "\"deluxe\" scrambled eggs" } } ], + "conditional_names": [ + { "type": "FLAG", "condition": "CANNIBALISM", "name": { "str_sp": "deluxe scrambled eggheads" } }, + { "type": "FLAG", "condition": "STRICT_HUMANITARIANISM", "name": { "str_sp": "birdman's scrambled eggs" } }, + { "type": "COMPONENT_ID", "condition": "mutant", "name": { "str_sp": "\"deluxe\" scrambled eggs" } } + ], "weight": "198 g", "color": "yellow", "spoils_in": "2 days", @@ -1385,5 +1389,48 @@ "flags": [ "EATEN_HOT" ], "fun": 7, "vitamins": [ [ "vitC", 3 ], [ "calcium", 19 ], [ "iron", 12 ] ] + }, + { + "type": "COMESTIBLE", + "id": "meatball_raw", + "name": { "str": "raw meatball" }, + "conditional_names": [ + { "type": "FLAG", "condition": "CANNIBALISM", "name": "raw manball" }, + { "type": "FLAG", "condition": "STRICT_HUMANITARIANISM", "name": "raw murderball" }, + { "type": "COMPONENT_ID", "condition": "mutant", "name": { "str_sp": "sinister %s" } } + ], + "weight": "85g", + "color": "pink", + "spoils_in": "1 day", + "comestible_type": "FOOD", + "symbol": "%", + "calories": 230, + "healthy": -1, + "parasites": 32, + "description": "A round, seasoned lump of meat, ready to fry.", + "price": 800, + "price_postapoc": 200, + "material": [ "flesh" ], + "volume": "85 ml", + "vitamins": [ [ "vitA", 2 ], [ "vitC", 2 ], [ "calcium", 8 ], [ "iron", 4 ] ], + "fun": -10 + }, + { + "type": "COMESTIBLE", + "id": "meatball", + "name": { "str": "meatball" }, + "conditional_names": [ + { "type": "FLAG", "condition": "CANNIBALISM", "name": "manball" }, + { "type": "FLAG", "condition": "STRICT_HUMANITARIANISM", "name": "murderball" }, + { "type": "COMPONENT_ID", "condition": "mutant", "name": { "str_sp": "sinister %s" } } + ], + "copy-from": "meatball_raw", + "parasites": 0, + "healthy": 0, + "spoils_in": "4 days", + "price_postapoc": 300, + "description": "A seasoned, fried, round lump of meat. Just like mom used to make!", + "flags": [ "EATEN_HOT" ], + "fun": 6 } ] diff --git a/data/json/items/comestibles/med.json b/data/json/items/comestibles/med.json index b46cc5693624d..e696f4dce89ba 100644 --- a/data/json/items/comestibles/med.json +++ b/data/json/items/comestibles/med.json @@ -438,7 +438,7 @@ "phase": "liquid", "charges": 4, "flags": [ "EATEN_COLD" ], - "freezing_point": -49, + "freezing_point": -45, "fun": 30 }, { diff --git a/data/json/items/comestibles/mutagen.json b/data/json/items/comestibles/mutagen.json index c61ea57a91b56..c17dbe2d332bf 100644 --- a/data/json/items/comestibles/mutagen.json +++ b/data/json/items/comestibles/mutagen.json @@ -21,7 +21,7 @@ "flags": [ "NUTRIENT_OVERRIDE" ], "vitamins": [ ], "material": [ "water" ], - "freezing_point": 17 + "freezing_point": -8 }, { "abstract": "iv_mutagen_flavor", diff --git a/data/json/items/comestibles/nuts.json b/data/json/items/comestibles/nuts.json index cc75f8256ba3e..c141fc7c2045c 100644 --- a/data/json/items/comestibles/nuts.json +++ b/data/json/items/comestibles/nuts.json @@ -428,7 +428,7 @@ "charges": 4, "flags": [ "EATEN_HOT" ], "fun": 5, - "freezing_point": -80 + "freezing_point": -62 }, { "id": "peanutbutter", diff --git a/data/json/items/comestibles/other.json b/data/json/items/comestibles/other.json index e17fdd5481181..7f30fadddfd09 100644 --- a/data/json/items/comestibles/other.json +++ b/data/json/items/comestibles/other.json @@ -188,6 +188,50 @@ "charges": 4, "fun": -10 }, + { + "type": "COMESTIBLE", + "id": "gelatin_powder", + "name": { "str_sp": "powdered gelatin" }, + "weight": "11 g", + "color": "yellow", + "container": "bag_plastic", + "comestible_type": "FOOD", + "symbol": "%", + "quench": -1, + "calories": 6, + "description": "Dried and powdered gelatin, used as a gelling agent when mixed with water.", + "price": 150, + "price_postapoc": 50, + "material": "powder", + "volume": "250 ml", + "flags": [ "EDIBLE_FROZEN" ], + "charges": 25, + "fun": -10 + }, + { + "type": "COMESTIBLE", + "id": "gelatin_fresh", + "name": { "str_sp": "fresh gelatin" }, + "conditional_names": [ + { "type": "COMPONENT_ID", "condition": "mutant", "name": { "str_sp": "abomination %s" } }, + { "type": "FLAG", "condition": "CANNIBALISM", "name": { "str_sp": "amoral %s" } }, + { "type": "FLAG", "condition": "STRICT_HUMANITARIANISM", "name": { "str_sp": "Orwell's %s" } } + ], + "weight": "25 g", + "color": "yellow", + "spoils_in": "7 days", + "comestible_type": "FOOD", + "symbol": "%", + "calories": 6, + "//": "Same as the meat it's been made from. It's additional ingredient compared to smoking, jerking, and dehydrating, so there's no incentive otherwise.", + "description": "Fresh ", + "price": 150, + "price_postapoc": 50, + "material": [ "flesh" ], + "volume": "250 ml", + "vitamins": [ [ "vitA", 10 ], [ "vitC", 15 ], [ "calcium", 2 ], [ "iron", 8 ] ], + "flags": [ "EATEN_COLD", "FREEZERBURN" ] + }, { "type": "COMESTIBLE", "id": "meal_bone_tainted", @@ -240,7 +284,7 @@ "price_postapoc": 0, "material": [ "paper" ], "volume": "250 ml", - "freezing_point": -459, + "freezing_point": -274, "charges": 50, "fun": -20 }, @@ -259,7 +303,7 @@ "price_postapoc": 0, "material": [ "cardboard" ], "volume": "250 ml", - "freezing_point": -459, + "freezing_point": -274, "charges": 10, "fun": -20 }, @@ -526,7 +570,7 @@ "calories": 2, "charges": 20, "fun": -5, - "freezing_point": -459 + "freezing_point": -274 }, { "type": "COMESTIBLE", @@ -623,7 +667,7 @@ "calories": 170, "vitamins": [ [ "vitA", 20 ], [ "vitC", 45 ], [ "calcium", 5 ], [ "iron", 5 ] ], "fun": 5, - "freezing_point": -459, + "freezing_point": -274, "color": "brown", "flags": [ "INEDIBLE", "CATTLE" ], "use_action": [ "CATTLEFODDER" ] @@ -645,7 +689,7 @@ "calories": 400, "vitamins": [ [ "calcium", 1 ], [ "iron", 7 ] ], "fun": 5, - "freezing_point": -459, + "freezing_point": -274, "color": "brown", "flags": [ "INEDIBLE", "BIRD", "NUTRIENT_OVERRIDE" ], "use_action": [ "BIRDFOOD" ] @@ -694,7 +738,7 @@ "calories": 380, "vitamins": [ [ "vitA", 25 ], [ "iron", 25 ], [ "vitB", 20 ], [ "calcium", 30 ] ], "fun": -15, - "freezing_point": -459, + "freezing_point": -274, "color": "brown", "flags": [ "LUPINE" ], "use_action": [ "DOGFOOD" ] @@ -743,7 +787,7 @@ "calories": 380, "vitamins": [ [ "vitA", 25 ], [ "iron", 25 ], [ "vitB", 20 ], [ "calcium", 30 ] ], "fun": -15, - "freezing_point": -459, + "freezing_point": -274, "color": "brown", "flags": [ "FELINE" ], "use_action": [ "CATFOOD" ] @@ -809,10 +853,11 @@ "color": "white", "symbol": "?", "container": "box_tea", + "calories": 1, "material": [ "paper" ], "price": 50, "price_postapoc": 200, - "freezing_point": -459, + "freezing_point": -274, "description": "A paper sachet with tea leaves inside. Put it into boiling water to make a cup of black tea." }, { @@ -834,7 +879,6 @@ "type": "COMESTIBLE", "name": { "str": "herbal tea bag" }, "copy-from": "tea_bag", - "calories": 9, "description": "A paper sachet with dried wild herbs inside. Put it into boiling water to make a cup of herbal tea." }, { @@ -842,7 +886,6 @@ "type": "COMESTIBLE", "name": { "str": "dandelion tea bag" }, "copy-from": "tea_bag", - "calories": 26, "description": "A paper sachet with dried dandelion roots inside. Put it into boiling water to make a cup of dandelion tea." }, { @@ -866,7 +909,46 @@ "material": [ "plastic" ], "price": 50, "price_postapoc": 200, - "freezing_point": -459, + "freezing_point": -274, "description": "A small packet of commercial instant coffee powder. No creamer or sweetener added." + }, + { + "type": "COMESTIBLE", + "id": "acid_soaked_hide", + "name": "acid soaked hide", + "description": "Raw hide soaking in a dilute acid solution to extract collagen. After 24 hours the solution can be strained to extract fresh gelatin.", + "weight": "500 g", + "color": "yellow", + "sealed": false, + "symbol": "~", + "calories": 9, + "quench": 6, + "fun": -10, + "price": 0, + "volume": "10L", + "price_postapoc": 10, + "phase": "liquid", + "comestible_type": "DRINK", + "flags": [ "NUTRIENT_OVERRIDE" ], + "brewable": { "time": "24 hours", "results": [ "gelatin_extracted" ] } + }, + { + "type": "COMESTIBLE", + "id": "gelatin_extracted", + "name": "extracted gelatin", + "description": "Freshly extracted gelatin in a dilute acid solution. It needs to be strained to separate the gelatin from the acid.", + "weight": "500 g", + "color": "yellow", + "sealed": false, + "symbol": "~", + "calories": 9, + "quench": 6, + "fun": -10, + "price": 0, + "volume": "10L", + "price_postapoc": 10, + "phase": "liquid", + "comestible_type": "DRINK", + "flags": [ "NUTRIENT_OVERRIDE" ] } ] diff --git a/data/json/items/comestibles/protein.json b/data/json/items/comestibles/protein.json index e2606881a9660..c53ec23f5d907 100644 --- a/data/json/items/comestibles/protein.json +++ b/data/json/items/comestibles/protein.json @@ -51,7 +51,7 @@ "color": "white", "container": "bottle_plastic_small", "calories": 100, - "freezing_point": -459, + "freezing_point": -274, "vitamins": [ ] }, { diff --git a/data/json/items/comestibles/raw_veggy.json b/data/json/items/comestibles/raw_veggy.json index b19ded32d195f..4a9eb15fbc77d 100644 --- a/data/json/items/comestibles/raw_veggy.json +++ b/data/json/items/comestibles/raw_veggy.json @@ -209,24 +209,62 @@ { "type": "COMESTIBLE", "id": "corn", - "name": { "str_sp": "corn" }, - "weight": "153 g", + "name": { "str": "corn cob" }, + "weight": "690 g", "color": "light_green", "spoils_in": "5 days", "comestible_type": "FOOD", "symbol": "%", "healthy": 1, "calories": 132, - "description": "Delicious golden kernels.", + "description": "A corn cob full of delicious golden kernels. You can eat them on the cob or shell it for cooking.", "price": 170, "//": "Thinking this is a single ear here.", "price_postapoc": 50, "material": [ "veggy" ], "volume": "750 ml", + "flags": [ "EATEN_HOT", "RAW" ], + "vitamins": [ [ "vitA", 2 ], [ "vitC", 12 ], [ "iron", 4 ] ], + "use_action": { "type": "transform", "target": "empty_corn_cob", "moves": 500 } + }, + { + "type": "COMESTIBLE", + "id": "corn_kernels", + "name": { "str_sp": "corn kernels" }, + "weight": "210 g", + "color": "yellow", + "spoils_in": "5 days", + "comestible_type": "FOOD", + "symbol": "%", + "healthy": 1, + "calories": 132, + "description": "Delicious golden kernels.", + "price": 200, + "price_postapoc": 60, + "material": [ "veggy" ], + "volume": "285 ml", "flags": [ "EATEN_HOT", "SMOKABLE", "RAW" ], - "smoking_result": "dry_veggy", + "smoking_result": "dry_corn", "vitamins": [ [ "vitA", 2 ], [ "vitC", 12 ], [ "iron", 4 ] ] }, + { + "type": "COMESTIBLE", + "id": "empty_corn_cob", + "name": { "str_sp": "empty corn cob" }, + "weight": "480 g", + "color": "yellow", + "spoils_in": "5 days", + "comestible_type": "FOOD", + "symbol": "%", + "healthy": 1, + "description": "An empty corn cob. It may seem useless, but it can be used to make stock, a delicious jelly, or as fuel.", + "price": 1, + "price_postapoc": 1, + "material": [ "veggy" ], + "volume": "465 ml", + "fun": -8, + "flags": [ "RAW" ] + }, { "type": "COMESTIBLE", "id": "cotton_boll", @@ -698,22 +736,23 @@ "type": "COMESTIBLE", "id": "tea_raw", "name": { "str": "black tea leaf", "str_pl": "black tea leaves" }, - "weight": "14 g", + "weight": "3 g", "color": "green", "fatigue_mod": 3, "stim": 1, "container": "bag_plastic", "comestible_type": "FOOD", "symbol": "%", - "calories": 17, + "calories": 1, "description": "Dried leaves of a tropical plant. You can boil them into tea, or you can just eat them raw. They aren't too filling though.", "price": 1030, "price_postapoc": 250, "material": [ "veggy" ], "primary_material": "dried_vegetable", - "volume": "250 ml", + "volume": "320 ml", "flags": [ "EDIBLE_FROZEN", "RAW", "IRREPLACEABLE_CONSUMABLE" ], - "charges": 20, + "charges": 33, + "//": "Tea is sold by weight not by volume. 1 kg of tea has a volume of 3,4 liters. We use smaller packs of at least 100 mg. Such a tea has a volume of 340 ml minus package makes it around 320 ml. 3 g per serving makes 33 charges.", "fun": -1 }, { diff --git a/data/json/items/comestibles/sandwich.json b/data/json/items/comestibles/sandwich.json index bcacfb4ddf8fe..5c63db38b7143 100644 --- a/data/json/items/comestibles/sandwich.json +++ b/data/json/items/comestibles/sandwich.json @@ -113,6 +113,27 @@ "fun": 6, "vitamins": [ [ "vitC", 6 ], [ "calcium", 7 ], [ "iron", 12 ] ] }, + { + "type": "COMESTIBLE", + "id": "sandwich_jam_cheese", + "name": { "str": "jam and cheese sandwich", "str_pl": "jam and cheese sandwiches" }, + "weight": "140 g", + "color": "brown", + "spoils_in": "1 day 13 hours", + "container": "wrapper", + "comestible_type": "FOOD", + "symbol": "%", + "quench": 1, + "calories": 382, + "description": "A delicious jam and cheese sandwich.", + "price": 200, + "price_postapoc": 200, + "material": [ "fruit", "wheat", "milk" ], + "primary_material": "wheat", + "volume": "250 ml", + "fun": 6, + "vitamins": [ [ "vitC", 6 ], [ "calcium", 7 ], [ "iron", 12 ] ] + }, { "type": "COMESTIBLE", "id": "sandwich_jam_butter", diff --git a/data/json/items/comestibles/seed.json b/data/json/items/comestibles/seed.json index 9d1470d6ff7c9..8baf3413c641b 100644 --- a/data/json/items/comestibles/seed.json +++ b/data/json/items/comestibles/seed.json @@ -678,7 +678,7 @@ "primary_material": "dried_vegetable", "volume": "250 ml", "flags": [ "SMOKED" ], - "freezing_point": -459, + "freezing_point": -274, "charges": 4, "fun": 3 }, diff --git a/data/json/items/comestibles/soup.json b/data/json/items/comestibles/soup.json index a486da82e0a87..92dec742732cf 100644 --- a/data/json/items/comestibles/soup.json +++ b/data/json/items/comestibles/soup.json @@ -179,7 +179,7 @@ { "type": "COMESTIBLE", "id": "soup_woods", - "name": { "str": "woods soup" }, + "name": { "str": "woods meat soup" }, "conditional_names": [ { "type": "COMPONENT_ID", "condition": "mutant", "name": "Mirkwood soup" } ], "weight": "250 g", "color": "red", @@ -201,6 +201,31 @@ "vitamins": [ [ "vitA", 20 ], [ "vitC", 9 ], [ "iron", 3 ], [ "vitB", 5 ] ], "fun": 1 }, + { + "type": "COMESTIBLE", + "id": "soup_woods_egg", + "name": { "str": "woods egg soup" }, + "conditional_names": [ { "type": "COMPONENT_ID", "condition": "mutant", "name": "Mirkwood soup" } ], + "weight": "250 g", + "color": "red", + "spoils_in": "5 days", + "container": "can_medium", + "comestible_type": "DRINK", + "symbol": "~", + "quench": 8, + "healthy": 1, + "calories": 304, + "description": "A nutritious and delicious soup, made of gifts of nature.", + "price": 350, + "price_postapoc": 100, + "material": [ "egg", "veggy" ], + "volume": "500 ml", + "charges": 2, + "phase": "liquid", + "flags": [ "EATEN_HOT", "USE_EAT_VERB" ], + "vitamins": [ [ "vitA", 20 ], [ "vitC", 9 ], [ "iron", 3 ], [ "vitB", 5 ] ], + "fun": 1 + }, { "type": "COMESTIBLE", "id": "soup_chicken", diff --git a/data/json/items/comestibles/spice.json b/data/json/items/comestibles/spice.json index bfc6cd92eb589..9afad56556cdf 100644 --- a/data/json/items/comestibles/spice.json +++ b/data/json/items/comestibles/spice.json @@ -19,7 +19,7 @@ "volume": "250 ml", "color": "red", "primary_material": "water", - "freezing_point": -22, + "freezing_point": -30, "phase": "liquid" }, { @@ -142,7 +142,7 @@ "charges": 10, "healthy": -1, "fun": -15, - "freezing_point": -22, + "freezing_point": -30, "phase": "liquid" }, { diff --git a/data/json/items/comestibles/veggy_dishes.json b/data/json/items/comestibles/veggy_dishes.json index adc2170fc9dc7..0205681ab45c4 100644 --- a/data/json/items/comestibles/veggy_dishes.json +++ b/data/json/items/comestibles/veggy_dishes.json @@ -227,20 +227,20 @@ "type": "COMESTIBLE", "id": "cornmeal", "name": { "str_sp": "cornmeal" }, - "weight": "19 g", + "weight": "60 g", "color": "yellow", "spoils_in": "360 days", "container": "box_small", "comestible_type": "FOOD", "symbol": "%", "quench": -1, - "calories": 47, - "vitamins": [ [ "iron", 3 ] ], + "calories": 44, + "vitamins": [ [ "vitA", 2 ], [ "vitC", 12 ], [ "iron", 4 ] ], "description": "This yellow cornmeal is useful for baking.", "price": 450, "price_postapoc": 25, "material": [ "veggy", "powder" ], - "volume": "250 ml", + "volume": "1070 ml", "flags": [ "EDIBLE_FROZEN" ], "charges": 10, "fun": -5 @@ -667,6 +667,27 @@ "flags": [ "EDIBLE_FROZEN" ], "charges": 2 }, + { + "type": "COMESTIBLE", + "id": "dry_corn", + "name": { "str_sp": "dehydrated corn kernels" }, + "weight": "180 g", + "color": "yellow", + "comestible_type": "FOOD", + "symbol": "%", + "quench": -10, + "healthy": 1, + "calories": 132, + "description": "A handful of dried corn kernels.", + "price": 60, + "price_postapoc": 50, + "material": [ "veggy" ], + "milling": { "into": "cornmeal", "conversion_rate": 3 }, + "volume": "255 ml", + "flags": [ "EDIBLE_FROZEN", "RAW" ], + "vitamins": [ [ "vitA", 2 ], [ "vitC", 12 ], [ "iron", 4 ] ], + "fun": -10 + }, { "type": "COMESTIBLE", "id": "rehydrated_veggy", @@ -680,6 +701,23 @@ "delete": { "flags": [ "RAW" ] }, "fun": 2 }, + { + "type": "COMESTIBLE", + "id": "rehydrated_corn_kernels", + "name": { "str_sp": "rehydrated corn kernels" }, + "copy-from": "veggy", + "weight": "70 g", + "color": "yellow", + "spoils_in": "1 day", + "description": "Reconstituted corn kernels; much more enjoyable to eat now that they have been rehydrated.", + "price": 900, + "price_postapoc": 50, + "smoking_result": "dry_corn", + "volume": "95 ml", + "delete": { "flags": [ "RAW" ] }, + "vitamins": [ [ "vitA", 2 ], [ "vitC", 12 ], [ "iron", 4 ] ], + "fun": 2 + }, { "type": "COMESTIBLE", "id": "veggy_salad", diff --git a/data/json/items/comestibles/wheat.json b/data/json/items/comestibles/wheat.json index db88bd2b7d909..5e3af87f6e18a 100644 --- a/data/json/items/comestibles/wheat.json +++ b/data/json/items/comestibles/wheat.json @@ -147,10 +147,10 @@ "type": "COMESTIBLE", "id": "bread_flour", "name": { "str_sp": "bread flour" }, - "weight": "13 g", + "weight": "60 g", "color": "white", "spoils_in": "360 days", - "container": "bag_paper_powder_small", + "container": "bag_paper_powder", "comestible_type": "FOOD", "symbol": "%", "quench": -1, @@ -159,7 +159,7 @@ "price": "200 cent", "price_postapoc": "95 cent", "material": [ "wheat", "powder" ], - "volume": "250 ml", + "volume": "2400 ml", "flags": [ "EDIBLE_FROZEN", "RAW" ], "charges": 20, "vitamins": [ [ "iron", 4 ] ], diff --git a/data/json/items/containers.json b/data/json/items/containers.json index 386e968bb0f87..6e7b00783a66d 100644 --- a/data/json/items/containers.json +++ b/data/json/items/containers.json @@ -162,7 +162,7 @@ "flags": [ "TRADER_AVOID" ] }, { - "id": "bag_paper_powder_small", + "id": "bag_paper_powder", "type": "GENERIC", "category": "container", "name": { "str": "small powder paper bag" }, @@ -177,7 +177,7 @@ { "pocket_type": "CONTAINER", "watertight": false, - "max_contains_volume": "2 L", + "max_contains_volume": "2500 ml", "max_contains_weight": "2 kg", "moves": 200 } diff --git a/data/json/items/corpses/inactive_bots.json b/data/json/items/corpses/inactive_bots.json index 4eb2412326a20..abe50e4bbf6cb 100644 --- a/data/json/items/corpses/inactive_bots.json +++ b/data/json/items/corpses/inactive_bots.json @@ -740,5 +740,31 @@ "moves": 150, "skills": [ "electronics", "computer" ] } + }, + { + "id": "bot_tazer_hack", + "type": "TOOL", + "name": { "str": "inactive tazer hack" }, + "description": "This is an inactive tazer hack. Hacks are fist-sized robots that fly through the air. This one has a tazer and attacks by flying at its target and delivering an electric shock. Use this item to reprogram and release the tazer hack. Electronics and computer skills determine if the targeting matrix is reprogrammed successfully.", + "weight": "4700 g", + "volume": "750 ml", + "price": 64500, + "price_postapoc": 3000, + "to_hit": -3, + "bashing": 6, + "cutting": 6, + "material": [ "aluminum", "plastic" ], + "symbol": ",", + "color": "cyan", + "use_action": { + "type": "place_monster", + "monster_id": "mon_tazer_hack", + "friendly_msg": "The tazer hack flies from your hand and surveys the area!", + "hostile_msg": "You misprogram the tazer hack; run!", + "difficulty": 3, + "moves": 60, + "place_randomly": true, + "skills": [ "electronics", "computer" ] + } } ] diff --git a/data/json/items/fake.json b/data/json/items/fake.json index b33dba7bbfa12..576baddcbfbf0 100644 --- a/data/json/items/fake.json +++ b/data/json/items/fake.json @@ -29,7 +29,7 @@ "copy-from": "fake_item", "type": "TOOL", "name": { "str": "integrated toolset" }, - "flags": [ "TRADER_AVOID" ], + "flags": [ "TRADER_AVOID", "FIRESTARTER", "USES_BIONIC_POWER" ], "max_charges": 10000, "use_action": [ "HAMMER", "CROWBAR" ], "qualities": [ @@ -143,7 +143,7 @@ "copy-from": "fake_item", "type": "TOOL", "name": { "str": "bionic firestarter" }, - "flags": [ "FIRESTARTER" ] + "flags": [ "FIRESTARTER", "USES_BIONIC_POWER" ] }, { "id": "migo_bio_gun", @@ -172,5 +172,85 @@ "sub": "char_smoker", "max_charges": 2000, "flags": [ "ALLOWS_REMOTE_USE", "PSEUDO" ] + }, + { + "id": "fake_power_tool", + "type": "TOOL", + "copy-from": "fake_item", + "name": { "str": "fake power tool" }, + "ammo": [ "battery" ], + "flags": [ "ALLOWS_REMOTE_USE", "PSEUDO", "USE_UPS" ] + }, + { + "id": "fake_arc_furnace", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "electric arc furnace" } + }, + { + "id": "fake_drill_press", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "drill press" } + }, + { + "id": "fake_tablesaw", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "tablesaw" } + }, + { + "id": "fake_mitresaw", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "mitresaw" } + }, + { + "id": "fake_bandsaw", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "bandsaw" } + }, + { + "id": "fake_router_table", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "router table" } + }, + { + "id": "fake_planer", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "planer" } + }, + { + "id": "fake_jointer", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "jointer" } + }, + { + "id": "fake_hydraulic_press", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "hydraulic press" } + }, + { + "id": "fake_power_lathe", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "power lathe" } + }, + { + "id": "fake_air_compressor", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "air compressor" } + }, + { + "id": "fake_oven", + "type": "TOOL", + "copy-from": "fake_power_tool", + "name": { "str": "oven" } } ] diff --git a/data/json/items/generic.json b/data/json/items/generic.json index 1850f95d7feed..e0dc7f0700145 100644 --- a/data/json/items/generic.json +++ b/data/json/items/generic.json @@ -2403,6 +2403,15 @@ "flags": [ "SLEEP_AID" ], "category": "other" }, + { + "type": "GENERIC", + "id": "bodypillow_makeshift", + "copy-from": "bodypillow", + "name": { "str": "makeshift body pillow" }, + "description": "A big, body-sized pillow. Someone drew a vaguely humanoid figure on it. Its heartwarming smile fills you with joy.", + "price": 100, + "price_postapoc": 50 + }, { "type": "GENERIC", "id": "down_pillow", @@ -2772,6 +2781,7 @@ "name": { "str": "science ID card" }, "description": "This ID card once belonged to a scientist. The reverse side describes protocol for using it; this could grant access at one control panel, if you can find one.", "price": 60000, + "flags": [ "SCIENCE_CARD" ], "price_postapoc": 250, "material": [ "plastic" ], "weight": "6 g", @@ -2786,6 +2796,7 @@ "name": { "str": "military ID card" }, "description": "This ID card once belonged to a military officer. The reverse side describes protocol for using it; this could grant access at one control panel, if you can find one.", "price": 120000, + "flags": [ "MILITARY_CARD" ], "price_postapoc": 500, "material": [ "plastic" ], "weight": "6 g", @@ -2801,6 +2812,7 @@ "name": { "str": "industrial ID card" }, "description": "This ID card once belonged to a high level technician. The reverse side describes protocol for using it; this could grant access at one control panel, if you can find one.", "price": 120000, + "flags": [ "INDUSTRIAL_CARD" ], "price_postapoc": 100, "material": [ "plastic" ], "weight": "6 g", @@ -3393,5 +3405,23 @@ ] } ] + }, + { + "type": "GENERIC", + "id": "broken_tazer_hack", + "symbol": ",", + "color": "green", + "name": { "str": "broken tazer hack" }, + "category": "other", + "description": "A broken tazer hack, with its propellers still and tazer inert. Could be gutted for parts.", + "price": 1000, + "price_postapoc": 10, + "material": [ "steel", "plastic" ], + "weight": "100 kg", + "volume": "65 L", + "bashing": 4, + "cutting": 4, + "to_hit": -3, + "flags": [ "TRADER_AVOID", "NO_REPAIR" ] } ] diff --git a/data/json/items/gun/10mm.json b/data/json/items/gun/10mm.json index d6451b9b54702..1036198a9c985 100644 --- a/data/json/items/gun/10mm.json +++ b/data/json/items/gun/10mm.json @@ -18,7 +18,6 @@ "symbol": "(", "color": "dark_gray", "ammo": [ "10mm", "40" ], - "ranged_damage": { "damage_type": "bullet", "amount": -7 }, "dispersion": 320, "durability": 8, "blackpowder_tolerance": 56, @@ -51,7 +50,6 @@ "symbol": "(", "color": "dark_gray", "ammo": "10mm", - "ranged_damage": { "damage_type": "bullet", "amount": -7 }, "dispersion": 510, "durability": 8, "blackpowder_tolerance": 48, @@ -86,7 +84,6 @@ "symbol": "(", "color": "dark_gray", "ammo": "10mm", - "ranged_damage": { "damage_type": "bullet", "amount": -6 }, "dispersion": 480, "durability": 8, "blackpowder_tolerance": 48, @@ -121,7 +118,6 @@ "symbol": "(", "color": "dark_gray", "ammo": "10mm", - "ranged_damage": { "damage_type": "bullet", "amount": -4 }, "dispersion": 440, "sight_dispersion": 440, "durability": 8, @@ -154,7 +150,7 @@ "min_cycle_recoil": 675, "ammo": "10mm", "skill": "rifle", - "ranged_damage": { "damage_type": "bullet", "amount": -1 }, + "ranged_damage": { "damage_type": "bullet", "amount": 1 }, "built_in_mods": [ "folding_stock" ], "modes": [ [ "DEFAULT", "semi-auto", 1 ] ], "pocket_data": [ @@ -220,7 +216,6 @@ "material": [ "steel" ], "color": "dark_gray", "ammo": "10mm", - "ranged_damage": { "damage_type": "bullet", "amount": -6 }, "dispersion": 480, "durability": 7, "min_cycle_recoil": 675, @@ -253,7 +248,6 @@ "symbol": "(", "color": "dark_gray", "ammo": "10mm", - "ranged_damage": { "damage_type": "bullet", "amount": -5 }, "dispersion": 410, "durability": 7, "min_cycle_recoil": 675, @@ -286,7 +280,6 @@ "symbol": "(", "color": "dark_gray", "ammo": "10mm", - "ranged_damage": { "damage_type": "bullet", "amount": -5 }, "dispersion": 480, "durability": 8, "min_cycle_recoil": 675, @@ -315,7 +308,6 @@ "price_postapoc": 2300, "skill": "pistol", "ammo": "10mm", - "ranged_damage": { "damage_type": "bullet", "amount": -4 }, "min_cycle_recoil": 675, "modes": [ [ "DEFAULT", "semi-auto", 1 ] ], "pocket_data": [ diff --git a/data/json/items/gun/223.json b/data/json/items/gun/223.json index b4ba9a5c2aa08..61f71f6f0ddc7 100644 --- a/data/json/items/gun/223.json +++ b/data/json/items/gun/223.json @@ -1,16 +1,63 @@ [ { - "id": "acr", + "id": "nato_assault_rifle", "copy-from": "rifle_auto", "looks_like": "ar15", "type": "GUN", - "name": { "str": "Remington ACR" }, - "description": "This carbine was developed for military use in the early 21st century. It is damaging and accurate, though its rate of fire is a bit slower than competing .223 carbines.", - "weight": "3719 g", + "name": { "str": "NATO assault rifle" }, + "description": "An assault rifle used by a NATO member state. As per NATO's STANAG agreement, it is chambered in 5.56x45mm.", + "//": "Weight, description, length modeled off of m4a1, price and gun stats average of some nato guns", + "weight": "2880g", "volume": "3310 ml", - "longest_side": "938 mm", - "price": 234300, - "price_postapoc": 6000, + "longest_side": "758 mm", + "price": "325 USD", + "price_postapoc": "48 USD", + "variants": [ + { + "id": "acr", + "name": { "str": "Remington ACR" }, + "description": "This carbine was developed for military use in the early 21st century. It is damaging and accurate, though its rate of fire is a bit slower than competing .223 carbines." + }, + { + "id": "h&k416a5", + "name": { "str": "HK416 A5" }, + "description": "Designed to replace the M4A1, the Heckler and Koch 416A5 features most of the former's strengths, while being considerably more durable." + }, + { + "id": "m27iar", + "name": { "str": "M27 IAR" }, + "description": "A H&K416 carbine outfitted with a heavier barrel to enable higher amounts of suppressive fire while retaining a good degree of mobility.", + "ascii_picture": "m27_iar" + }, + { + "id": "m4a1", + "name": { "str": "M4A1" }, + "description": "A popular carbine, long used by the US military. Though accurate, small, and lightweight, it is infamous for its unreliability when not properly maintained.", + "ascii_picture": "m4a1", + "weight": 1 + }, + { + "id": "m16a3", + "name": { "str": "M16A3" }, + "description": "The M16 is a very common assault rifle descended from the AR-15, used by militaries across the world for over 50 years. It is a gas operated, rotating bolt rifle known for its accuracy and controllable recoil. This one is a relatively rare M16A3, with full auto capability." + }, + { + "id": "scar_l", + "name": { "str": "FN SCAR-L" }, + "description": "A highly accurate and modular assault rifle specially designed for the United States Special Operations Command. The 'L' in its name stands for light, as it uses the lightweight .223 round.", + "ascii_picture": "scar-L" + }, + { + "id": "m38dmr", + "name": { "str": "M38 DMR" }, + "description": "The M38 Designated Marksman Rifle is an M27 IAR, itself derived from a H&K416, selected for accuracy, and outfitted with a variable power scope and a QDSS suppressor." + }, + { + "id": "m4_cqbr", + "name": { "str": "MK 18 CQBR" }, + "description": "This is a shorter M4 carbine with a 10.3 inch barrel, intended for confined spaces and close quarters battle situations." + } + ], "to_hit": -1, "bashing": 12, "material": [ "steel", "plastic" ], @@ -18,7 +65,7 @@ "color": "dark_gray", "ammo": [ "223" ], "ranged_damage": { "damage_type": "bullet", "amount": -1 }, - "dispersion": 150, + "dispersion": 160, "durability": 8, "min_cycle_recoil": 1350, "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "AUTO", "auto", 4 ] ], @@ -246,74 +293,20 @@ ] }, { - "id": "h&k416a5", - "copy-from": "rifle_auto", + "id": "hk_g36", + "copy-from": "nato_assault_rifle", "looks_like": "ar15", "type": "GUN", - "name": { "str": "HK416 A5" }, - "//": "*Current* milspec gear is now ridiculously overpriced, as seen with the M2010 IRL.", - "description": "Designed to replace the M4A1, the Heckler and Koch 416A5 features most of the former's strengths, while being considerably more durable.", - "weight": "3490 g", - "volume": "4957 ml", - "longest_side": "798 mm", - "price": 540000, - "price_postapoc": 2250, - "to_hit": -1, - "bashing": 12, - "material": [ "steel", "plastic" ], - "symbol": "(", - "color": "dark_gray", - "ammo": [ "223" ], - "ranged_damage": { "damage_type": "bullet", "amount": -2 }, - "dispersion": 180, - "durability": 8, - "min_cycle_recoil": 1350, - "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "AUTO", "auto", 4 ] ], - "pocket_data": [ + "name": { "str": "G36 assault rifle" }, + "description": "An assault rifle chamber in 5.56x45mm and accepting G36 magazines.", + "variants": [ { - "pocket_type": "MAGAZINE_WELL", - "holster": true, - "max_contains_volume": "20 L", - "max_contains_weight": "20 kg", - "item_restriction": [ - "stanag30", - "stanag5", - "stanag10", - "stanag20", - "stanag40", - "stanag50", - "stanag60", - "stanag60drum", - "stanag90", - "stanag100", - "stanag100drum", - "stanag150", - "survivor223mag" - ] + "id": "hk_g36", + "name": { "str": "H&K G36" }, + "description": "Designed as a replacement for the early H&K G3 battle rifle, the G36 is more accurate, and uses the much-lighter .223 round, allowing for a higher ammo capacity.", + "weight": 1 } - ] - }, - { - "id": "hk_g36", - "copy-from": "rifle_auto", - "looks_like": "ar15", - "type": "GUN", - "name": { "str": "H&K G36" }, - "description": "Designed as a replacement for the early H&K G3 battle rifle, the G36 is more accurate, and uses the much-lighter .223 round, allowing for a higher ammo capacity.", - "weight": "3630 g", - "volume": "4640 ml", - "longest_side": "1004 mm", - "price": 210000, - "price_postapoc": 6000, - "to_hit": -1, - "bashing": 12, - "material": [ "steel", "plastic" ], - "symbol": "(", - "color": "dark_gray", - "ammo": [ "223" ], - "dispersion": 150, - "durability": 8, - "min_cycle_recoil": 1350, + ], "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "AUTO", "auto", 4 ] ], "pocket_data": [ { @@ -396,164 +389,22 @@ "description": "This is a semi-automatic civilian variant of the M249 machine gun, manufactured for sport shooting and collectors market. Notably, it retains the ability to be belt fed, an uncommon feature in civilian firearms.", "modes": [ [ "DEFAULT", "semi-auto", 1 ] ] }, - { - "id": "m27iar", - "copy-from": "h&k416a5", - "type": "GUN", - "name": { "str": "M27 IAR" }, - "description": "A H&K416 carbine outfitted with a heavier barrel to enable higher amounts of suppressive fire while retaining a good degree of mobility.", - "ascii_picture": "m27_iar", - "weight": "3710 g", - "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "AUTO", "auto", 4 ] ], - "relative": { "ranged_damage": { "damage_type": "bullet", "amount": 1 }, "durability": 1 }, - "pocket_data": [ - { - "pocket_type": "MAGAZINE_WELL", - "holster": true, - "max_contains_volume": "20 L", - "max_contains_weight": "20 kg", - "item_restriction": [ - "stanag30", - "stanag5", - "stanag10", - "stanag20", - "stanag40", - "stanag50", - "stanag60", - "stanag60drum", - "stanag90", - "stanag100", - "stanag100drum", - "stanag150", - "survivor223mag" - ] - } - ] - }, - { - "id": "m38dmr", - "copy-from": "m27iar", - "name": { "str": "M38 DMR" }, - "type": "GUN", - "description": "The M38 Designated Marksman Rifle is an M27 IAR, itself derived from a H&K416, selected for accuracy, and outfitted with a variable power scope and a QDSS suppressor.", - "dispersion": 125, - "price_postapoc": 3000, - "default_mods": [ "suppressor", "rifle_scope", "bipod" ] - }, - { - "id": "m4a1", - "copy-from": "rifle_auto", - "looks_like": "ar15", - "type": "GUN", - "name": { "str": "M4A1" }, - "description": "A popular carbine, long used by the US military. Though accurate, small, and lightweight, it is infamous for its unreliability when not properly maintained.", - "ascii_picture": "m4a1", - "weight": "2880 g", - "volume": "3310 ml", - "longest_side": "758 mm", - "price": 240000, - "price_postapoc": 5000, - "to_hit": -1, - "bashing": 12, - "material": [ "steel", "plastic" ], - "symbol": "(", - "color": "dark_gray", - "ammo": [ "223" ], - "ranged_damage": { "damage_type": "bullet", "amount": -2 }, - "dispersion": 180, - "durability": 6, - "min_cycle_recoil": 1350, - "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "AUTO", "auto", 4 ] ], - "pocket_data": [ - { - "pocket_type": "MAGAZINE_WELL", - "holster": true, - "max_contains_volume": "20 L", - "max_contains_weight": "20 kg", - "item_restriction": [ - "stanag30", - "stanag5", - "stanag10", - "stanag20", - "stanag40", - "stanag50", - "stanag60", - "stanag60drum", - "stanag90", - "stanag100", - "stanag100drum", - "stanag150", - "survivor223mag" - ] - } - ] - }, - { - "id": "m4_cqbr", - "copy-from": "m4a1", - "type": "GUN", - "name": { "str": "MK 18 CQBR" }, - "description": "This is a shorter M4 carbine with a 10.3 inch barrel, intended for confined spaces and close quarters battle situations.", - "weight": "2720 g", - "volume": "3110 ml", - "longest_side": "682 mm", - "price": 260000, - "price_postapoc": 6000, - "ranged_damage": { "damage_type": "bullet", "amount": -3 } - }, { "id": "m16a4", - "copy-from": "rifle_semi", + "copy-from": "nato_assault_rifle", "looks_like": "ar15", "type": "GUN", - "name": { "str": "M16A4" }, - "description": "The M16 is a very common assault rifle descended from the AR-15, used by militaries across the world for over 50 years. It is a gas operated, rotating bolt rifle known for its accuracy and controllable recoil.", - "weight": "3260 g", - "volume": "5030 ml", - "longest_side": "1003 mm", - "price": 240000, - "price_postapoc": 5000, - "to_hit": -1, - "bashing": 12, - "material": [ "steel", "plastic" ], - "symbol": "(", - "color": "dark_gray", - "ammo": [ "223" ], - "dispersion": 150, - "durability": 7, - "min_cycle_recoil": 1350, - "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "BURST", "3 rd.", 3 ] ], - "pocket_data": [ + "name": { "str": "NATO burst rifle" }, + "description": "An assault rifle used by a NATO member state. Unlike other assault rifles, this one does not have an auto-fire mode, and instead has a burst fire mode. As per NATO's STANAG agreement, it is chambered in 5.56x45mm.", + "variants": [ { - "pocket_type": "MAGAZINE_WELL", - "holster": true, - "max_contains_volume": "20 L", - "max_contains_weight": "20 kg", - "item_restriction": [ - "stanag30", - "stanag5", - "stanag10", - "stanag20", - "stanag40", - "stanag50", - "stanag60", - "stanag60drum", - "stanag90", - "stanag100", - "stanag100drum", - "stanag150", - "survivor223mag" - ] + "id": "m16a4", + "name": { "str": "M16A4" }, + "description": "The M16 is a very common assault rifle descended from the AR-15, used by militaries across the world for over 50 years. It is a gas operated, rotating bolt rifle known for its accuracy and controllable recoil.", + "weight": 1 } - ] - }, - { - "id": "m16a3", - "copy-from": "m16a4", - "type": "GUN", - "name": { "str": "M16A3" }, - "description": "The M16 is a very common assault rifle descended from the AR-15, used by militaries across the world for over 50 years. It is a gas operated, rotating bolt rifle known for its accuracy and controllable recoil. This one is a relatively rare M16A3, with full auto capability.", - "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "AUTO", "auto", 4 ] ] + ], + "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "BURST", "3 rd.", 3 ] ] }, { "id": "m231pfw", @@ -680,121 +531,39 @@ } ] }, - { - "id": "scar_l", - "copy-from": "rifle_auto", - "looks_like": "ar15", - "type": "GUN", - "name": { "str": "FN SCAR-L" }, - "description": "A highly accurate and modular assault rifle specially designed for the United States Special Operations Command. The 'L' in its name stands for light, as it uses the lightweight .223 round.", - "ascii_picture": "scar-L", - "weight": "3500 g", - "volume": "4970 ml", - "longest_side": "850 mm", - "price": 280000, - "price_postapoc": 6000, - "to_hit": -1, - "bashing": 12, - "material": [ "steel", "plastic" ], - "symbol": "(", - "color": "dark_gray", - "ammo": [ "223" ], - "ranged_damage": { "damage_type": "bullet", "amount": -2 }, - "dispersion": 150, - "durability": 8, - "min_cycle_recoil": 1350, - "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "AUTO", "auto", 4 ] ], - "pocket_data": [ - { - "pocket_type": "MAGAZINE_WELL", - "holster": true, - "max_contains_volume": "20 L", - "max_contains_weight": "20 kg", - "item_restriction": [ - "stanag30", - "stanag5", - "stanag10", - "stanag20", - "stanag40", - "stanag50", - "stanag60", - "stanag60drum", - "stanag90", - "stanag100", - "stanag100drum", - "stanag150", - "survivor223mag" - ] - } - ] - }, { "id": "sig552", - "copy-from": "rifle_auto", + "copy-from": "nato_assault_rifle", "looks_like": "ar15", "type": "GUN", - "name": { "str": "SIG 552" }, - "description": "A compact selective fire automatic rifle designed for the Swiss military. It features a three-round burst mode and an integrated folding stock.", - "weight": "3200 g", - "volume": "2950 ml", - "longest_side": "741 mm", - "price": 320000, - "price_postapoc": 6000, - "to_hit": -1, - "bashing": 10, - "material": [ "steel", "plastic" ], - "ammo": [ "223" ], - "dispersion": 180, - "durability": 9, - "min_cycle_recoil": 1350, - "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "BURST", "3 rd.", 3 ], [ "AUTO", "auto", 4 ] ], - "built_in_mods": [ "folding_stock" ], - "pocket_data": [ + "//": "This could use a better name, I just wanted to differentiate it from assault rifles, as it also has a burst mode", + "name": { "str": "NATO assault rifle with burst" }, + "description": "An assault rifle used by a NATO member state. Unlike other assault rifles, this one has a burst fire mode in addition to semi-automatic and fully-automatic modes. As per NATO's STANAG agreement, it is chambered in 5.56x45mm.", + "variants": [ { - "pocket_type": "MAGAZINE_WELL", - "holster": true, - "max_contains_volume": "20 L", - "max_contains_weight": "20 kg", - "item_restriction": [ - "stanag30", - "stanag5", - "stanag10", - "stanag20", - "stanag40", - "stanag50", - "stanag60", - "stanag60drum", - "stanag90", - "stanag100", - "stanag100drum", - "stanag150", - "survivor223mag" - ] + "id": "sig552", + "name": { "str": "SIG 552" }, + "description": "A compact selective fire automatic rifle designed for the Swiss military. It features a three-round burst mode and an integrated folding stock.", + "weight": 1 } - ] + ], + "built_in_mods": [ "folding_stock" ] }, { "id": "steyr_aug", - "copy-from": "rifle_auto", + "copy-from": "nato_assault_rifle", "looks_like": "ar15", "type": "GUN", - "name": { "str": "Steyr AUG" }, - "description": "The Steyr AUG is an Austrian assault rifle that uses a bullpup design. It is used in the armed forces and police forces of many nations, and enjoys low recoil and high accuracy.", - "weight": "3500 g", - "volume": "3520 ml", - "longest_side": "715 mm", - "price": 490000, - "price_postapoc": 6000, - "to_hit": -1, - "bashing": 12, - "material": [ "steel", "plastic" ], - "symbol": "(", - "color": "light_gray", - "ammo": [ "223" ], - "dispersion": 140, - "durability": 8, - "min_cycle_recoil": 1350, - "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "AUTO", "auto", 4 ] ], + "name": { "str": "AUG assault rifle" }, + "description": "An assault rifle chambered in 5.56x45mm and accepting AUG magazines", + "variants": [ + { + "id": "steyr_aug", + "name": { "str": "Steyr AUG" }, + "description": "The Steyr AUG is an Austrian assault rifle that uses a bullpup design. It is used in the armed forces and police forces of many nations, and enjoys low recoil and high accuracy.", + "weight": 1 + } + ], "built_in_mods": [ "grip" ], "valid_mod_locations": [ [ "accessories", 4 ], diff --git a/data/json/items/gun/308.json b/data/json/items/gun/308.json index a1ce10176e491..5deff26f3b233 100644 --- a/data/json/items/gun/308.json +++ b/data/json/items/gun/308.json @@ -312,7 +312,7 @@ }, { "id": "scar_h", - "copy-from": "scar_l", + "copy-from": "nato_assault_rifle", "looks_like": "ar15", "type": "GUN", "name": { "str_sp": "FN SCAR-H" }, diff --git a/data/json/items/gun/45.json b/data/json/items/gun/45.json index c5519811c56b1..84013736b3b4f 100644 --- a/data/json/items/gun/45.json +++ b/data/json/items/gun/45.json @@ -4,8 +4,21 @@ "looks_like": "hk_mp5", "type": "GUN", "reload_noise_volume": 10, - "name": { "str": "Vector SMG .45" }, - "description": "The Vector SMG is a delayed blowback submachine gun with a unique, in-line design that places both the shooter's hand and shoulder in line with the bore axis, reducing muzzle climb during bursts. This one is chambered in .45 ACP.", + "//": [ + "Yes, I know it's not really a glock smg.", + "It takes glock magazines, and it's own special one that I am also renaming to a glock magazine", + "so, I'm calling it a glock smg." + ], + "name": { "str": "Glock SMG" }, + "description": "This is a submachine gun chambered in .45 ACP, accepting Glock magazines.", + "variants": [ + { + "id": "TDI", + "name": { "str": "Vector SMG .45" }, + "description": "The Vector SMG is a delayed blowback submachine gun with a unique, in-line design that places both the shooter's hand and shoulder in line with the bore axis, reducing muzzle climb during bursts. This one is chambered in .45 ACP.", + "weight": 1 + } + ], "weight": "3100 g", "volume": "4248 ml", "longest_side": "611 mm", @@ -53,8 +66,16 @@ "looks_like": "hk_mp5", "type": "GUN", "reload_noise_volume": 10, - "name": { "str": "H&K UMP45" }, - "description": "Developed as a successor to the MP5 submachine gun, the UMP45 retains the earlier model's supreme accuracy and low recoil, but in the higher .45 caliber.", + "name": { "str": "UMP45 SMG" }, + "description": "This is a submachine gun, chambered in .45 ACP, and accepting UMP45 magazines.", + "variants": [ + { + "id": "hk_ump45", + "name": { "str": "H&K UMP45" }, + "description": "Developed as a successor to the MP5 submachine gun, the UMP45 retains the earlier model's supreme accuracy and low recoil, but in the higher .45 caliber.", + "weight": 1 + } + ], "ascii_picture": "hk_ump", "weight": "2300 g", "volume": "4451 ml", @@ -104,9 +125,22 @@ "copy-from": "pistol_base", "looks_like": "glock_17", "type": "GUN", - "name": { "str": "M1911" }, - "//": "You can get 'em for over US$1K if you want. This is a fairly cheap remake.", - "description": "The M1911 was the US Military standard-issue sidearm for most of the 20th Century. It remains one of the most popular .45 pistols today.", + "name": { "str": "M1911 pistol" }, + "description": "This is a semi-automatic pistol, chambered in .45 ACP and accepting M1991 magaines.", + "variants": [ + { + "id": "m1911", + "name": { "str": "M1911" }, + "//": "You can get 'em for over US$1K if you want. This is a fairly cheap remake.", + "description": "The M1911 was the US Military standard-issue sidearm for most of the 20th Century. It remains one of the most popular .45 pistols today.", + "weight": 1 + }, + { + "id": "m1911_MEU", + "name": { "str": "M45A1" }, + "description": "The M45A1 supplanted earlier M45 MEUSOC pistols in use by Force Recon elements of Marine Expeditionary Units. Where the original M45 pistols were gutted M1911A1's hand-fitted by USMC armorers, the updated M45A1's are hardly different from any other commercial 1911 design save for their dual recoil springs. Most were replaced in 2016 with Glock 19's due to logistics issues." + } + ], "ascii_picture": "m1911", "weight": "1120 g", "volume": "454 ml", @@ -134,27 +168,21 @@ } ] }, - { - "id": "m1911_MEU", - "copy-from": "m1911", - "looks_like": "glock_17", - "type": "GUN", - "name": { "str": "M45A1" }, - "description": "The M45A1 supplanted earlier M45 MEUSOC pistols in use by Force Recon elements of Marine Expeditionary Units. Where the original M45 pistols were gutted M1911A1's hand-fitted by USMC armorers, the updated M45A1's are hardly different from any other commercial 1911 design save for their dual recoil springs. Most were replaced in 2016 with Glock 19's due to logistics issues.", - "weight": "1105 g", - "price": 169900, - "price_postapoc": 2750, - "color": "brown_yellow", - "dispersion": 420, - "durability": 8 - }, { "id": "mac_10", "looks_like": "hk_mp5", "type": "GUN", "reload_noise_volume": 10, - "name": { "str": "MAC-10" }, - "description": "The MAC-10 is a popular machine pistol originally designed for military use. For many years they were the most inexpensive automatic weapon in the US, and enjoyed great popularity among criminals less concerned with quality firearms.", + "name": { "str": "MAC-10 SMG" }, + "description": "This is a submachine gun chambered in .45 ACP, and accepting MAC-10 magazines.", + "variants": [ + { + "id": "mac_10", + "name": { "str": "MAC-10" }, + "description": "The MAC-10 is a popular machine pistol originally designed for military use. For many years they were the most inexpensive automatic weapon in the US, and enjoyed great popularity among criminals less concerned with quality firearms.", + "weight": 1 + } + ], "weight": "2810 g", "volume": "1195 ml", "longest_side": "298 mm", @@ -320,8 +348,16 @@ "looks_like": "hk_mp5", "type": "GUN", "reload_noise_volume": 10, - "name": { "str": "Thompson M1928A1" }, - "description": "An American-made submachine gun developed during the very end of World War I, too late to see action. Infamous during the 1920s for its use by gangsters, and was used during World War II before being mostly replaced with less-expensive alternatives.", + "name": { "str": "Thompson SMG" }, + "description": "This is a submachine gun chambered in .45 ACP, and accepting Thompson magazines.", + "variants": [ + { + "id": "tommygun", + "name": { "str": "Thompson M1928A1" }, + "description": "An American-made submachine gun developed during the very end of World War I, too late to see action. Infamous during the 1920s for its use by gangsters, and was used during World War II before being mostly replaced with less-expensive alternatives.", + "weight": 1 + } + ], "weight": "4899 g", "volume": "1230 ml", "longest_side": "832 mm", @@ -369,7 +405,21 @@ "id": "usp_45", "copy-from": "usp_9mm", "type": "GUN", - "name": { "str": "USP .45" }, + "name": { "str": "USP pistol" }, + "description": "A semi-automatic pistol chambered in .45 ACP, and accepting USP magazines.", + "variants": [ + { + "id": "usp_45", + "name": { "str": "USP .45" }, + "description": "A popular pistol, widely used among law enforcement. Extensively tested for durability, it has been found to stay accurate even after being subjected to extreme abuse.", + "weight": 1 + }, + { + "id": "mk23", + "name": { "str": "MK 23 MOD 0" }, + "description": "Jokingly referred to as \"The World's Only Crew-Served Pistol\", this massive pistol was designed as a primary weapon for select \"special operators\". Its cumbersome nature, the introduction of the derivative HK USP series and the logistics of getting .45 ACP ammunition in theater doomed this behemoth to US SOCOM armories. Like the USP, the Mk 23 is a remarkably reliable gun; someone could probably take out a nuclear equipped walking tank with this in their holster." + } + ], "weight": "887 g", "volume": "483 ml", "longest_side": "241 mm", @@ -389,27 +439,21 @@ } ] }, - { - "id": "mk23", - "copy-from": "usp_45", - "type": "GUN", - "name": { "str": "MK 23 MOD 0" }, - "description": "Jokingly referred to as \"The World's Only Crew-Served Pistol\", this massive pistol was designed as a primary weapon for select \"special operators\". Its cumbersome nature, the introduction of the derivative HK USP series and the logistics of getting .45 ACP ammunition in theater doomed this behemoth to US SOCOM armories. Like the USP, the Mk 23 is a remarkably reliable gun; someone could probably take out a nuclear equipped walking tank with this in their holster.", - "weight": "844 g", - "volume": "698 ml", - "longest_side": "277 mm", - "price": 70000, - "price_postapoc": 3000, - "default_mods": [ "suppressor", "laser_sight" ], - "flags": [ "WATERPROOF_GUN", "NEVER_JAMS" ] - }, { "id": "walther_ppq_45", "copy-from": "pistol_base", "looks_like": "glock_17", "type": "GUN", - "name": { "str": "Walther PPQ 45" }, - "description": "The Walther PPQ is a semi-automatic pistol originating from the Walther P99QA, and maintains compatibility with some of its predecessor's accessories. This model is chambered in .45 ACP.", + "name": { "str": "PPQ pistol" }, + "description": "This is a semi-automatic pistol chambered in .45 ACP, and accepting PPQ magazines", + "variants": [ + { + "id": "walther_ppq_45", + "name": { "str": "Walther PPQ 45" }, + "description": "The Walther PPQ is a semi-automatic pistol originating from the Walther P99QA, and maintains compatibility with some of its predecessor's accessories. This model is chambered in .45 ACP.", + "weight": 1 + } + ], "weight": "799 g", "volume": "472 ml", "longest_side": "224 mm", @@ -438,8 +482,16 @@ "copy-from": "pistol_base", "looks_like": "glock_17", "type": "GUN", - "name": { "str": "Hi-Point Model JHP" }, - "description": "The Hi-Point Model JHP is a blowback operated semi-automatic pistol designed by Hi-Point Firearms, which is known for making inexpensive firearms, and for making said firearms bulky and uncomfortable. Hi-Points have slides made with a zinc pot-metal which is relatively fragile compared to steel slides.", + "name": { "str": "Hi-Point pistol" }, + "description": "This is a semi-automatic pistol chambered in .45 ACP, and accepting Hi-Point magazines", + "variants": [ + { + "id": "hptjhp", + "name": { "str": "Hi-Point Model JHP" }, + "description": "The Hi-Point Model JHP is a blowback operated semi-automatic pistol designed by Hi-Point Firearms, which is known for making inexpensive firearms, and for making said firearms bulky and uncomfortable. Hi-Points have slides made with a zinc pot-metal which is relatively fragile compared to steel slides.", + "weight": 1 + } + ], "weight": "990 g", "volume": "538 ml", "longest_side": "238 mm", @@ -469,8 +521,16 @@ "copy-from": "pistol_base", "looks_like": "glock_17", "type": "GUN", - "name": { "str": "Glock 21" }, - "description": "A full-sized version of the highly successful Glock. This model is chambered in .45 ACP.", + "name": { "str": "Glock pistol" }, + "description": "This is a semi-automatic pistol chambered in .45 ACP, and accepting Glock magazines.", + "variants": [ + { + "id": "glock_21", + "name": { "str": "Glock 21" }, + "description": "A full-sized version of the highly successful Glock. This model is chambered in .45 ACP.", + "weight": 1 + } + ], "ascii_picture": "glock_20", "weight": "835 g", "volume": "490 ml", diff --git a/data/json/items/gun/460.json b/data/json/items/gun/460.json index d3954498777c3..f93b32285e16c 100644 --- a/data/json/items/gun/460.json +++ b/data/json/items/gun/460.json @@ -18,7 +18,7 @@ "holster": true, "max_contains_volume": "20 L", "max_contains_weight": "20 kg", - "item_restriction": [ "m1911mag", "m1911bigmag", "m1911mag", "m1911bigmag" ] + "item_restriction": [ "m1911mag", "m1911bigmag" ] } ] } diff --git a/data/json/items/gun/flintlock.json b/data/json/items/gun/flintlock.json index 9dd5c71b5b305..c971757fd9515 100644 --- a/data/json/items/gun/flintlock.json +++ b/data/json/items/gun/flintlock.json @@ -118,7 +118,7 @@ "copy-from": "rifle_flintlock", "type": "GUN", "name": { "str": "flintlock rifle" }, - "description": "Also called a Pennsylvania rifle, this long barreled rifled flintlock gun has signficantly increased accuracy at the cost of taking even longer to reload.", + "description": "Also called a Pennsylvania rifle, this long-barreled rifled flintlock gun has significantly increased accuracy at the cost of taking even longer to reload.", "weight": "3700 g", "volume": "1700 ml", "longest_side": "1278 mm", diff --git a/data/json/items/gun/monster_gun.json b/data/json/items/gun/monster_gun.json index 71b0f21ae48bd..54423b2743f47 100644 --- a/data/json/items/gun/monster_gun.json +++ b/data/json/items/gun/monster_gun.json @@ -63,22 +63,11 @@ "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", - "WATERPROOF_GUN", - "NEVER_JAMS" - ], + "flags": [ "PRIMITIVE_RANGED_WEAPON", "NEVER_JAMS", "NONCONDUCTIVE", "NO_REPAIR", "WATERPROOF_GUN", "NO_SALVAGE", "NO_UNLOAD" ], "skill": "throw", "ammo": [ "rock" ], "ammo_effects": [ "NO_PENETRATE_OBSTACLES", "NEVER_MISFIRES" ], "clip_size": 1, - "ranged_damage": { "damage_type": "bash", "amount": -1 }, "weight": "540 g", "volume": "210 ml", "longest_side": "75 mm", diff --git a/data/json/items/gun/shot.json b/data/json/items/gun/shot.json index 9cce99d93b790..662fbbcbf64b9 100644 --- a/data/json/items/gun/shot.json +++ b/data/json/items/gun/shot.json @@ -53,7 +53,7 @@ "material": [ "steel" ], "dispersion": 855, "durability": 8, - "burst": 6, + "modes": [ [ "DEFAULT", "burst", 6 ] ], "ups_charges": 1, "reload": 200, "valid_mod_locations": [ [ "accessories", 4 ], [ "sights", 1 ], [ "rail mount", 1 ] ], @@ -74,7 +74,7 @@ "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 its own right with room for improvement.", + "description": "A short, homemade lever-action shotgun with a small internal tube magazine. While still a primitive design - made from a pipe and a plank - it is a formidable shotgun in its own right with room for improvement.", "weight": "2311 g", "volume": "2 L", "longest_side": "85 cm", @@ -802,7 +802,7 @@ "copy-from": "shotgun_base", "type": "GUN", "name": { "str": "M1897 Trench Gun" }, - "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.", + "description": "The Winchester 1897 was one of the first commercially-successful pump-action shotguns. In its 'trench' configuration 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", diff --git a/data/json/items/magazine/45.json b/data/json/items/magazine/45.json index 1296e45516a44..5ed6563fac0a8 100644 --- a/data/json/items/magazine/45.json +++ b/data/json/items/magazine/45.json @@ -38,8 +38,16 @@ "id": "tdi_mag", "looks_like": "mp5mag", "type": "MAGAZINE", - "name": { "str": "Vector SMG 30-round magazine" }, - "description": "A 30-round polymer and steel box magazine for use with the KRISS Vector.", + "name": { "str": "Glock SMG 30-round magazine" }, + "description": "A 30-round polymer and steel box magazine for use with the Glock submachine gun.", + "variants": [ + { + "id": "tdi_mag", + "name": { "str": "Vector SMG 30-round magazine" }, + "description": "A 30-round polymer and steel box magazine for use with the KRISS Vector.", + "weight": 1 + } + ], "weight": "210 g", "volume": "500 ml", "price": 1800, @@ -141,8 +149,16 @@ "id": "usp45mag", "looks_like": "glock17_17", "type": "MAGAZINE", - "name": { "str": "USP .45 12-round magazine" }, - "description": "A standard capacity magazine for use with the H&K USP handgun.", + "name": { "str": "USP .45 pistol 12-round magazine" }, + "description": "A 12 round magazine for use with the USP pistol", + "variants": [ + { + "id": "usp45mag", + "name": { "str": "USP .45 12-round magazine" }, + "description": "A standard capacity magazine for use with the H&K USP handgun.", + "weight": 1 + } + ], "weight": "60 g", "volume": "250 ml", "price": 5900, @@ -158,8 +174,16 @@ "id": "ppq45mag", "looks_like": "glock17_17", "type": "MAGAZINE", - "name": { "str": "PPQ .45 ACP 12-round magazine" }, - "description": "A 12 round steel box magazine for the Walther PPQ .45 ACP.", + "name": { "str": "PPQ .45 12-round amgazine" }, + "description": "A 12 round steel box magazine for the PPQ pistol", + "variants": [ + { + "id": "ppq45mag", + "name": { "str": "PPQ .45 ACP 12-round magazine" }, + "description": "A 12 round steel box magazine for the Walther PPQ .45 ACP.", + "weight": 1 + } + ], "weight": "80 g", "volume": "240 ml", "price": 5000, @@ -192,8 +216,16 @@ "id": "glock_21mag", "looks_like": "glock17_17", "type": "MAGAZINE", - "name": { "str": "Glock 21 13-round magazine" }, - "description": "A standard capacity magazine for use with the Glock 21.", + "name": { "str": "Glock .45 13-round magazine" }, + "description": "A 13 round magazine for use with Glock firearms chambered in .45 ACP.", + "variants": [ + { + "id": "glock_21mag", + "name": { "str": "Glock 21 13-round magazine" }, + "description": "A standard capacity magazine for use with the Glock 21.", + "weight": 1 + } + ], "weight": "85 g", "volume": "105 ml", "price": 4000, @@ -209,8 +241,16 @@ "id": "glock_21mag26", "looks_like": "glock17_17", "type": "MAGAZINE", - "name": { "str": "Glock 21 26-round magazine" }, - "description": "A double capacity magazine for use with the Glock 21.", + "name": { "str": "Glock .45 26-round magazine" }, + "description": "A 26 round magazine for use with Glock firearms chambered in .45 ACP.", + "variants": [ + { + "id": "glock_21mag26", + "name": { "str": "Glock 21 26-round magazine" }, + "description": "A double capacity magazine for use with the Glock 21.", + "weight": 1 + } + ], "weight": "85 g", "volume": "205 ml", "price": 4000, diff --git a/data/json/items/melee/misc.json b/data/json/items/melee/misc.json index ff891a10e4a30..9824040db4f42 100644 --- a/data/json/items/melee/misc.json +++ b/data/json/items/melee/misc.json @@ -18,8 +18,9 @@ { "id": "bullwhip_razor", "name": { "str": "scourge" }, - "type": "TOOL", - "description": "The \"cat 'o nine tails\", a handle with nine short leather whips each sporting a razor-sharp metal tip. This ancient instrument of torture causes massive bleeding but is an ineffecient weapon by design.", + "type": "GENERIC", + "category": "weapons", + "description": "The \"cat 'o nine tails\", a handle with nine short leather whips, each sporting a razor-sharp metal tip. This ancient instrument of torture causes massive bleeding, but by design is inefficient as a weapon.", "symbol": "/", "color": "brown", "weight": "3496 g", @@ -34,9 +35,10 @@ }, { "id": "lobotomizer", - "type": "TOOL", + "type": "GENERIC", + "category": "weapons", "name": { "str": "lobotomizer" }, - "description": "This is a hand-forged collapsible tool that has two axe heads and sharp shovel-like tip on one end. It can be used as a shovel, or you could chop some zombies with it instead.", + "description": "This is a hand-forged collapsible tool that has two axe heads and a sharp shovel-like tip on one end. It can be used as a shovel, or you could chop some zombies with it instead.", "weight": "2722 g", "volume": "1750 ml", "price": 25000, diff --git a/data/json/items/melee/spears_and_polearms.json b/data/json/items/melee/spears_and_polearms.json index 94ca953eb09c0..442d787531328 100644 --- a/data/json/items/melee/spears_and_polearms.json +++ b/data/json/items/melee/spears_and_polearms.json @@ -79,6 +79,37 @@ "qualities": [ [ "CUT", 1 ], [ "BUTCHER", -22 ] ], "flags": [ "FRAGILE_MELEE", "NONCONDUCTIVE", "POLEARM", "SHEATH_SPEAR", "REACH_ATTACK", "ALWAYS_TWOHAND" ] }, + { + "id": "makeshift_halberd", + "type": "GENERIC", + "name": { "str": "simple makeshift glaive" }, + "//": "Name changed to simple makeshift glaive, but changing the id would break e.g. tilesets.", + "description": "This is a large blade attached to a stout section of tree branch. It could do a considerable amount of damage.", + "weight": "1800 g", + "volume": "3 L", + "longest_side": "180 cm", + "price": 5000, + "price_postapoc": 250, + "to_hit": -1, + "bashing": 13, + "cutting": 31, + "material": [ "steel", "wood" ], + "symbol": "/", + "color": "light_gray", + "techniques": [ "WBLOCK_1" ], + "qualities": [ [ "CUT", 1 ], [ "BUTCHER", -42 ] ], + "flags": [ "REACH_ATTACK", "POLEARM", "NONCONDUCTIVE", "SHEATH_SPEAR", "FRAGILE_MELEE" ] + }, + { + "id": "makeshift_glaive", + "type": "GENERIC", + "copy-from": "makeshift_halberd", + "name": { "str": "makeshift glaive" }, + "description": "A stout tree branch that has been carefully split and reinforced. At the split point, a sharp blade has been bolted into place and reinforced with layers of sturdy wrapped bindings.", + "price_postapoc": 400, + "qualities": [ [ "CUT", 1 ], [ "BUTCHER", -28 ] ], + "delete": { "flags": [ "FRAGILE_MELEE" ] } + }, { "id": "spear_spike", "type": "TOOL", diff --git a/data/json/items/melee/swords_and_blades.json b/data/json/items/melee/swords_and_blades.json index d6a5d81e52af2..c577cc76d3ac3 100644 --- a/data/json/items/melee/swords_and_blades.json +++ b/data/json/items/melee/swords_and_blades.json @@ -5,7 +5,7 @@ "symbol": "!", "color": "brown", "name": { "str": "2-by-sword" }, - "description": "A two by four with a crossguard and whittled-down point; not much for slashing, but much better than your bare hands.", + "description": "A plank with a crossguard and whittled-down point; not much for slashing, but much better than your bare hands.", "material": [ "wood" ], "volume": "1250 ml", "weight": "1000 g", diff --git a/data/json/items/melee/unarmed_weapons.json b/data/json/items/melee/unarmed_weapons.json index 0ba543f49c29e..c6f7d653b4f1e 100644 --- a/data/json/items/melee/unarmed_weapons.json +++ b/data/json/items/melee/unarmed_weapons.json @@ -98,6 +98,23 @@ "qualities": [ [ "HAMMER", 1 ] ], "flags": [ "UNARMED_WEAPON" ] }, + { + "type": "GENERIC", + "id": "knuckle_steel_forged", + "symbol": "3", + "color": "light_gray", + "name": { "str": "pair of steel knuckles", "str_pl": "pairs of steel knuckles" }, + "looks_like": "knuckle_brass", + "description": "A pair of knuckles forged from steel, designed to be gripped in the palm and cause punches to do more damage. A good, quick weapon - but you have to get within punching range to use it.", + "category": "weapons", + "material": [ "steel" ], + "volume": "250 ml", + "weight": "240 g", + "bashing": 4, + "price_postapoc": 180, + "qualities": [ [ "HAMMER", 1 ] ], + "flags": [ "UNARMED_WEAPON", "DURABLE_MELEE" ] + }, { "type": "GENERIC", "id": "punch_dagger", diff --git a/data/json/items/migration.json b/data/json/items/migration.json index 9ca037b6feac4..dbfb65d23c529 100644 --- a/data/json/items/migration.json +++ b/data/json/items/migration.json @@ -1136,7 +1136,7 @@ { "id": [ "l_base_223", "l_car_223", "l_car_223_kit", "l_mbr_223", "l_mbr_223_kit" ], "type": "MIGRATION", - "replace": "h&k416a5" + "replace": "nato_assault_rifle" }, { "id": [ "l_lmg_223", "l_lmg_223_kit" ], @@ -1146,7 +1146,55 @@ { "id": [ "l_dsr_223", "l_dsr_223_kit" ], "type": "MIGRATION", - "replace": "m16a4" + "replace": "nato_assault_rifle" + }, + { + "id": "acr", + "type": "MIGRATION", + "replace": "nato_assault_rifle", + "variant": "acr" + }, + { + "id": "h&k416a5", + "type": "MIGRATION", + "replace": "nato_assault_rifle", + "variant": "h&k416a5" + }, + { + "id": "m27iar", + "type": "MIGRATION", + "replace": "nato_assault_rifle", + "variant": "m27iar" + }, + { + "id": "m4a1", + "type": "MIGRATION", + "replace": "nato_assault_rifle", + "variant": "m4a1" + }, + { + "id": "m16a3", + "type": "MIGRATION", + "replace": "nato_assault_rifle", + "variant": "m16a3" + }, + { + "id": "scar_l", + "type": "MIGRATION", + "replace": "nato_assault_rifle", + "variant": "scar_l" + }, + { + "id": "m38dmr", + "type": "MIGRATION", + "replace": "nato_assault_rifle", + "variant": "m38dmr" + }, + { + "id": "m4_cqbr", + "type": "MIGRATION", + "replace": "nato_assault_rifle", + "variant": "m4_cqbr" }, { "id": "lw223bigmag", @@ -1163,6 +1211,18 @@ "type": "MIGRATION", "replace": "m1911mag" }, + { + "id": "m1911_MEU", + "type": "MIGRATION", + "replace": "m1911", + "variant": "m1911_MEU" + }, + { + "id": "mk23", + "type": "MIGRATION", + "replace": "usp_45", + "variant": "mk23" + }, { "id": "solar_panel_v3", "type": "MIGRATION", diff --git a/data/json/items/newspaper.json b/data/json/items/newspaper.json index ca49cfd403468..ee55cd8e2bdaf 100644 --- a/data/json/items/newspaper.json +++ b/data/json/items/newspaper.json @@ -14,6 +14,21 @@ "weight": "3 g", "volume": "5 ml" }, + { + "type": "GENERIC", + "id": "valentine_card", + "category": "books", + "symbol": ",", + "color": "red", + "name": { "str": "valentine card" }, + "snippet_category": "valentine", + "description": "A creased holiday card. It appears to be a Valentine's Day card from before the Cataclysm.", + "price": 0, + "price_postapoc": 0, + "material": [ "paper" ], + "weight": "3 g", + "volume": "5 ml" + }, { "type": "GENERIC", "id": "survnote", diff --git a/data/json/items/ranged/slings.json b/data/json/items/ranged/slings.json index c3a36f8be5e4a..64c185dbb10e3 100644 --- a/data/json/items/ranged/slings.json +++ b/data/json/items/ranged/slings.json @@ -10,7 +10,7 @@ "price": 150, "//": "It's little more than a piece of slightly shaped leather", "material": [ "leather" ], - "flags": [ "RELOAD_AND_SHOOT", "NEVER_JAMS", "PRIMITIVE_RANGED_WEAPON", "BELT_CLIP", "WATERPROOF_GUN", "NEVER_JAMS" ], + "flags": [ "RELOAD_AND_SHOOT", "NEVER_JAMS", "PRIMITIVE_RANGED_WEAPON", "BELT_CLIP", "WATERPROOF_GUN" ], "ammo_effects": [ "NEVER_MISFIRES", "NO_PENETRATE_OBSTACLES" ], "skill": "throw", "ammo": [ "rock" ], @@ -35,15 +35,7 @@ "description": "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.", "price": 500, "material": [ "wood" ], - "flags": [ - "FIRE_TWOHAND", - "RELOAD_AND_SHOOT", - "NEVER_JAMS", - "PRIMITIVE_RANGED_WEAPON", - "BELT_CLIP", - "WATERPROOF_GUN", - "NEVER_JAMS" - ], + "flags": [ "FIRE_TWOHAND", "RELOAD_AND_SHOOT", "NEVER_JAMS", "PRIMITIVE_RANGED_WEAPON", "BELT_CLIP", "WATERPROOF_GUN" ], "ammo_effects": [ "NEVER_MISFIRES", "NO_PENETRATE_OBSTACLES" ], "skill": "archery", "ammo": [ "pebble" ], @@ -79,8 +71,7 @@ "NONCONDUCTIVE", "SHEATH_SPEAR", "ALWAYS_TWOHAND", - "WATERPROOF_GUN", - "NEVER_JAMS" + "WATERPROOF_GUN" ], "ammo_effects": [ "NEVER_MISFIRES", "NO_PENETRATE_OBSTACLES" ], "techniques": [ "WBLOCK_2", "RAPID", "SWEEP" ], @@ -109,15 +100,7 @@ "description": "A modern slingshot with a wrist brace, allowing it to fire tiny objects slightly more forcefully than a simple wooden slingshot.", "price": 3000, "material": [ "steel", "plastic" ], - "flags": [ - "FIRE_TWOHAND", - "RELOAD_AND_SHOOT", - "NEVER_JAMS", - "PRIMITIVE_RANGED_WEAPON", - "BELT_CLIP", - "WATERPROOF_GUN", - "NEVER_JAMS" - ], + "flags": [ "FIRE_TWOHAND", "RELOAD_AND_SHOOT", "NEVER_JAMS", "PRIMITIVE_RANGED_WEAPON", "BELT_CLIP", "WATERPROOF_GUN" ], "ammo_effects": [ "NEVER_MISFIRES", "NO_PENETRATE_OBSTACLES" ], "skill": "archery", "ammo": [ "pebble" ], diff --git a/data/json/items/resources/glass.json b/data/json/items/resources/glass.json index 1e9b16ffda269..4db237c921cac 100644 --- a/data/json/items/resources/glass.json +++ b/data/json/items/resources/glass.json @@ -27,13 +27,14 @@ "symbol": "]", "color": "light_cyan", "name": { "str": "sheet of glass", "str_pl": "sheets of glass" }, - "description": "A large sheet of glass. Easily shattered. Useful for repairing windows.", + "description": "A large sheet of glass, around three by four feet. Easily shattered. Useful for repairing windows.", "category": "spare_parts", "price": 5000, "price_postapoc": 100, "material": [ "glass" ], - "weight": "31250 g", - "volume": "12500 ml", + "weight": "6577 g", + "volume": "2655 ml", + "longest_side": "122 cm", "bashing": 4, "to_hit": -5, "use_action": { @@ -50,13 +51,14 @@ "symbol": "]", "color": "light_blue", "name": { "str": "sheet of reinforced glass", "str_pl": "sheets of reinforced glass" }, - "description": "A large sheet of glass strengthened with steel wiring.", + "description": "A large sheet of bulletproof glass, it looks to a little over two inches thick.", "category": "spare_parts", "price": 10000, "price_postapoc": 250, "material": [ "glass", "steel" ], - "weight": "40123 g", - "volume": "12500 ml", + "weight": "137710 g", + "volume": "63713 ml", + "longest_side": "122 cm", "bashing": 6, "to_hit": -6 }, @@ -66,13 +68,14 @@ "symbol": "]", "color": "light_blue", "name": { "str": "pane of reinforced glass", "str_pl": "panes of reinforced glass" }, - "description": "A small pane of glass strengthened with steel wiring.", + "description": "A small pane of bulletproof glass, it looks to be a little over two inches thick.", "category": "spare_parts", "price": 5000, "price_postapoc": 100, "material": [ "glass", "steel" ], - "weight": "10040 g", - "volume": "3 L", + "weight": "11475 g", + "volume": "5300 ml", + "longest_side": "305 mm", "bashing": 4, "to_hit": -2 }, @@ -87,8 +90,9 @@ "price": 10000, "price_postapoc": 100, "material": [ "glass" ], - "weight": "22700 g", - "volume": "22700 ml", + "weight": "17514 g", + "volume": "7070 ml", + "longest_side": "122 cm", "bashing": 6, "to_hit": -5 } diff --git a/data/json/items/resources/metal.json b/data/json/items/resources/metal.json index 45d9ae1a6123c..a1496900e4f17 100644 --- a/data/json/items/resources/metal.json +++ b/data/json/items/resources/metal.json @@ -3,16 +3,17 @@ "type": "GENERIC", "id": "pipe", "name": { "str": "pipe" }, - "description": "A steel pipe, makes a good melee weapon. Useful in a few crafting recipes.", + "description": "A steel pipe; makes a good melee weapon. Useful in a few crafting recipes.", "category": "spare_parts", "weight": "1250 g", + "longest_side": "60 cm", "to_hit": 1, "color": "dark_gray", "symbol": "/", "material": [ "steel" ], "qualities": [ [ "HAMMER", 1 ] ], "techniques": [ "WBLOCK_1" ], - "volume": "1 L", + "volume": "370 ml", "bashing": 12, "price": 7500, "price_postapoc": 10 @@ -57,15 +58,16 @@ "type": "GENERIC", "id": "cu_pipe", "name": { "str": "copper tubing" }, - "description": "A copper tube, too thin to be much use as a melee weapon, but will do if nothing else is available. Useful in a few crafting recipes.", + "description": "A copper tube; too thin to be much use as a melee weapon, but will do if nothing else is available. Useful in a few crafting recipes.", "category": "spare_parts", "weight": "345 g", + "longest_side": "60 cm", "to_hit": -1, "color": "light_red", "symbol": "/", "material": [ "copper" ], "techniques": [ "WBLOCK_1" ], - "volume": "500 ml", + "volume": "370 ml", "bashing": 5, "price": 7500, "price_postapoc": 10 diff --git a/data/json/items/resources/plastic.json b/data/json/items/resources/plastic.json index c1f69d6d06372..d1cc9c7d0da83 100644 --- a/data/json/items/resources/plastic.json +++ b/data/json/items/resources/plastic.json @@ -6,7 +6,8 @@ "name": { "str": "plastic chunk" }, "description": "This is a piece of plastic. It could be used to fabricate, repair, or reinforce plastic items.", "weight": "50 g", - "volume": "250 ml", + "volume": "95 ml", + "//": "Based on a density around 1.05 grams/mL, like PLA and ABS, + 100% for being rigid and probably oddly-shaped.", "price": 0, "price_postapoc": 10, "material": [ "plastic" ], @@ -21,7 +22,8 @@ "name": { "str_sp": "synthetic fabric" }, "description": "This is small bolt of synthetic fabric. Unlike you and other natural materials, it won't degrade much with age. Maybe that's less of a bad thing now.", "weight": "55 g", - "volume": "250 ml", + "volume": "50 ml", + "//": "Density of 1.15 grams per mL", "price": 0, "price_postapoc": 10, "material": [ "nylon" ], @@ -36,7 +38,8 @@ "name": { "str": "Lycra patch", "str_pl": "Lycra patches" }, "description": "This is a small bolt of a synthetic fabric blended with stretchy Lycra fibers. It could be used to make flexible yet strong clothing. Stylish, but bad for the environment; at least you're recycling it.", "weight": "87 g", - "volume": "250 ml", + "volume": "70 ml", + "//": "Density of 1.21-1.35 grams per mL, using 1.28", "price": 0, "price_postapoc": 10, "material": [ "lycra" ], @@ -51,7 +54,8 @@ "name": { "str": "plastic sheet" }, "description": "This is a large sheet of heavy flexible plastic, the sort that might have been used for commercial wrapping or for weather-sealing a home.", "weight": "1000 g", - "volume": "2 L", + "volume": "950 ml", + "//": "Based on a density around 1.05 grams/mL, like PLA and ABS", "price": 0, "price_postapoc": 25, "material": [ "plastic" ], diff --git a/data/json/items/resources/tailoring.json b/data/json/items/resources/tailoring.json index dbc468911f73c..a86c2a7508193 100644 --- a/data/json/items/resources/tailoring.json +++ b/data/json/items/resources/tailoring.json @@ -116,7 +116,8 @@ "name": { "str": "cotton sheet" }, "description": "A sheet of cotton fabric, suitable for making clothing.", "weight": "5 g", - "volume": "300 ml", + "volume": "3 ml", + "//": "Density of 1.55 grams per mL", "price": 1000, "price_postapoc": 100, "material": [ "cotton" ], @@ -132,7 +133,8 @@ "name": { "str_sp": "patchwork cotton clothing parts" }, "description": "A selection of various clothing parts, sewn together from cotton patches in a patchwork fashion. Suitable for making most clothing, though it's much less time-efficient than if using proper material sheets.", "weight": "100 g", - "volume": "60 ml", + "volume": "65 ml", + "//": "Density of 1.55 grams per mL", "price": 200, "price_postapoc": 50, "count": 1 @@ -144,7 +146,8 @@ "name": { "str": "faux fur sheet" }, "description": "A sheet of fake synthetic colorful fur, suitable for making clothing.", "weight": "10 g", - "volume": "4500 ml", + "volume": "25 ml", + "//": "Guesstimated density of 0.44 grams per mL based on felt", "price": 5000, "price_postapoc": 100, "material": [ "faux_fur" ], @@ -160,7 +163,8 @@ "name": { "str_sp": "patchwork faux fur clothing parts" }, "description": "A selection of various clothing parts, sewn together from faux fur patches in a patchwork fashion. Suitable for making most clothing, though it's much less time-efficient than if using proper material sheets.", "weight": "200 g", - "volume": "900 ml", + "volume": "450 ml", + "//": "Guesstimated density of 0.44 grams per mL based on felt", "price": 1000, "price_postapoc": 50, "count": 1 @@ -172,7 +176,8 @@ "name": { "str": "felt sheet" }, "description": "A sheet of felt, suitable for making clothing.", "weight": "8 g", - "volume": "1800 ml", + "volume": "20 ml", + "//": "Density of 0.44 grams per mL", "price": 2500, "price_postapoc": 250, "material": [ "wool" ], @@ -189,6 +194,7 @@ "description": "A selection of various clothing parts, sewn together from felt patches in a patchwork fashion. Suitable for making most clothing, though it's much less time-efficient than if using proper material sheets.", "weight": "160 g", "volume": "360 ml", + "//": "Density of 0.44 grams per mL", "price": 500, "price_postapoc": 100, "count": 1 @@ -200,7 +206,8 @@ "name": { "str": "Kevlar sheet" }, "description": "A sheet of Kevlar synthetic fabric, suitable for making cut-resistant, durable clothing. In this form, unlike rigid plates, it can be stitched.", "weight": "5 g", - "volume": "300 ml", + "volume": "4 ml", + "//": "Density of 1.4 grams per mL", "price": 900, "price_postapoc": 500, "material": [ "kevlar" ], @@ -216,7 +223,8 @@ "name": { "str": "Lycra sheet" }, "description": "A sheet of synthetic fabric blended with stretchy Lycra fibers, suitable for making flexible yet strong clothing.", "weight": "3 g", - "volume": "300 ml", + "volume": "2 ml", + "//": "Density of 1.21-1.35 grams per mL, using 1.28", "price": 5000, "price_postapoc": 100, "material": [ "lycra" ], @@ -232,7 +240,8 @@ "name": { "str": "layered kevlar panel" }, "description": "This is a small 16-layer thick Kevlar panel. It could be used to repair armor made of Kevlar.", "weight": "80 g", - "volume": "600 ml", + "volume": "65 ml", + "//": "Density of 1.4 grams per mL; volume increased ~5% to account for some rigidity", "price": 15000, "material": [ "kevlar_layered" ], "flags": [ "NO_SALVAGE" ], @@ -246,7 +255,8 @@ "name": { "str": "rigid kevlar plate" }, "description": "This is a compressed panel of Kevlar treated with epoxy or other adhesive. It could be used to repair items made of Kevlar.", "weight": "300 g", - "volume": "250 ml", + "volume": "235 ml", + "//": "Density of 1.4 grams per mL + 10% for being rigid and possibly oddly-shaped", "price": 1000, "material": [ "kevlar_rigid" ], "flags": [ "NO_SALVAGE" ], @@ -260,7 +270,8 @@ "name": { "str_sp": "patchwork Lycra clothing parts" }, "description": "A selection of various clothing parts, sewn together from Lycra patches in a patchwork fashion. Suitable for making most clothing, though it's much less time-efficient than if using proper material sheets.", "weight": "60 g", - "volume": "60 ml", + "volume": "45 ml", + "//": "Density of 1.21-1.35 grams per mL, using 1.28", "price": 1000, "price_postapoc": 50, "count": 1 @@ -272,7 +283,8 @@ "name": { "str": "neoprene sheet" }, "description": "A sheet of neoprene, a synthetic rubber, suitable for making underwater gear.", "weight": "6 g", - "volume": "300 ml", + "volume": "5 ml", + "//": "Density of 1.23 grams per mL", "price": 5000, "price_postapoc": 100, "material": [ "neoprene" ], @@ -288,7 +300,8 @@ "name": { "str_sp": "patchwork neoprene clothing parts" }, "description": "A selection of various clothing parts, sewn together from neoprene patches in a patchwork fashion, with waterproofed seams. Suitable for making most clothing, though it's much less time-efficient than if using proper material sheets.", "weight": "120 g", - "volume": "60 ml", + "volume": "100 ml", + "//": "Density of 1.23 grams per mL", "price": 1000, "price_postapoc": 50, "count": 1 @@ -300,7 +313,8 @@ "name": { "str": "Nomex sheet" }, "description": "A sheet of Nomex synthetic fabric, suitable for making heat-resistant clothing.", "weight": "5 g", - "volume": "300 ml", + "volume": "7 ml", + "//": "Density of 0.72-1.1 grams per mL, using ~0.72 to account for possible unwieldiness", "price": 15000, "price_postapoc": 250, "material": [ "nomex" ], @@ -316,7 +330,8 @@ "name": { "str_sp": "patchwork Nomex clothing parts" }, "description": "A selection of various clothing parts, sewn together with Nomex thread from Nomex patches in a patchwork fashion. Suitable for making most clothing, though it's much less time-efficient than if using proper material sheets.", "weight": "100 g", - "volume": "60 ml", + "volume": "110 ml", + "//": "Density of 0.72-1.1 grams per mL, using 0.91", "price": 3000, "price_postapoc": 50, "count": 1 @@ -328,7 +343,8 @@ "name": { "str": "synthetic fabric sheet" }, "description": "A sheet of synthetic fabric, suitable for making clothing.", "weight": "3 g", - "volume": "300 ml", + "volume": "3 ml", + "//": "Density of 1.15 grams per mL", "price": 5000, "price_postapoc": 100, "material": [ "nylon" ], @@ -344,7 +360,8 @@ "name": { "str_sp": "patchwork synthetic fabric clothing parts" }, "description": "A selection of various clothing parts, sewn together from synthetic fabric patches in a patchwork fashion. Suitable for making most clothing, though it's much less time-efficient than if using proper material sheets.", "weight": "60 g", - "volume": "60 ml", + "volume": "50 ml", + "//": "Density of 1.15 grams per mL", "price": 1000, "price_postapoc": 50, "count": 1 diff --git a/data/json/items/resources/wood.json b/data/json/items/resources/wood.json index afd9e89cacc64..9f0d412197594 100644 --- a/data/json/items/resources/wood.json +++ b/data/json/items/resources/wood.json @@ -39,8 +39,8 @@ "id": "stick", "symbol": "/", "color": "brown", - "name": { "str": "stout branch", "str_pl": "stout branches" }, - "description": "A respectable length of tree branch, just big enough to wrap your hand around. Makes a decent melee weapon.", + "name": { "str": "stick" }, + "description": "A respectable length of wood, just big enough to wrap your hand around. Makes a decent melee weapon.", "material": [ "wood" ], "techniques": [ "WBLOCK_1" ], "flags": [ "TRADER_AVOID", "FIREWOOD" ], @@ -56,8 +56,8 @@ "id": "stick_long", "symbol": "/", "color": "brown", - "name": { "str": "long stout branch", "str_pl": "long stout branches" }, - "description": "A straight section of wood from a tree branch, about eight feet long and a couple of inches in diameter. Makes a decent melee weapon, and can be broken into shorter pieces for crafting.", + "name": { "str": "long stick" }, + "description": "A straight section of wood, about eight feet long and a couple of inches in diameter. Makes a decent melee weapon, and can be broken into shorter pieces for crafting.", "material": [ "wood" ], "techniques": [ "WBLOCK_1" ], "flags": [ "TRADER_AVOID", "FIREWOOD" ], diff --git a/data/json/items/tool/cooking.json b/data/json/items/tool/cooking.json index f5665c1d37823..1113ef3e9c028 100644 --- a/data/json/items/tool/cooking.json +++ b/data/json/items/tool/cooking.json @@ -873,7 +873,7 @@ "type": "GENERIC", "category": "tools", "name": { "str": "sieve" }, - "description": "This is no mere strainer for noodles; it's a sieve used to separate particles of certain sizes. You could use this to do a really good job sifting flour, remove dust and soil from grain, or perhaps conduct gradiation tests for any civil engineers you might know. This one has been constructed from steel mesh.", + "description": "This is no mere strainer for noodles; it's a sieve used to separate particles of certain sizes. You could use this to do a really good job sifting flour, remove dust and soil from grain, or perhaps conduct gradation tests for any civil engineers you might know. This one has been constructed from steel mesh.", "volume": "500 ml", "weight": "318 g", "price": 700, diff --git a/data/json/items/tool/entry_tools.json b/data/json/items/tool/entry_tools.json index 82d3a8d592ae5..90b2f41e1323d 100644 --- a/data/json/items/tool/entry_tools.json +++ b/data/json/items/tool/entry_tools.json @@ -3,10 +3,10 @@ "id": "crowbar", "type": "TOOL", "name": { "str": "crowbar" }, - "description": "This is a hefty prying tool. Use it to open locked doors without destroying them or to lift manhole covers. You could also wield it to bash some heads in.", - "weight": "500 g", - "volume": "1 L", - "longest_side": "60 cm", + "description": "This is a hefty prying tool. Use it to open locked doors without destroying them, or to lift manhole covers. You could also wield it to bash some heads in.", + "weight": "1200 g", + "volume": "170 ml", + "longest_side": "50 cm", "price": 1300, "price_postapoc": 500, "to_hit": -1, @@ -61,9 +61,9 @@ "id": "makeshift_crowbar", "type": "TOOL", "name": { "str": "makeshift crowbar" }, - "description": "This is a pipe whose ends have been bent and hammered flat to resemble a crowbar. Use it to open locked crates without destroying them, or to lift manhole covers. You could also wield it to fight with, in a pinch.", - "weight": "1000 g", - "volume": "1 L", + "description": "This is a pipe with ends that have been bent and hammered flat so it resembles a crowbar. Use it to open locked crates without destroying them, or to lift manhole covers. You could also wield it to fight with, in a pinch.", + "weight": "1250 g", + "volume": "350 ml", "longest_side": "60 cm", "price": 0, "price_postapoc": 10, diff --git a/data/json/items/tool/misc.json b/data/json/items/tool/misc.json index cb2947fa5a920..c6f851e5ba728 100644 --- a/data/json/items/tool/misc.json +++ b/data/json/items/tool/misc.json @@ -432,27 +432,6 @@ "charges_per_use": 1, "use_action": [ "WATER_PURIFIER" ] }, - { - "id": "makeshift_halberd", - "type": "GENERIC", - "name": { "str": "makeshift glaive" }, - "//": "Name changed to glaive, but changing the id would break e.g. tilesets.", - "description": "This is a large blade attached to a stout section of tree branch. It could do a considerable amount of damage.", - "weight": "1800 g", - "volume": "3 L", - "longest_side": "180 cm", - "price": 5000, - "price_postapoc": 250, - "to_hit": -1, - "bashing": 13, - "cutting": 31, - "material": [ "steel", "wood" ], - "symbol": "/", - "color": "light_gray", - "techniques": [ "WBLOCK_1" ], - "qualities": [ [ "CUT", 1 ], [ "BUTCHER", -42 ] ], - "flags": [ "REACH_ATTACK", "POLEARM", "NONCONDUCTIVE", "SHEATH_SPEAR", "FRAGILE_MELEE" ] - }, { "type": "GENERIC", "id": "mind_splicer", diff --git a/data/json/items/tool/woodworking.json b/data/json/items/tool/woodworking.json index 2409520ec0195..6343db479c51c 100644 --- a/data/json/items/tool/woodworking.json +++ b/data/json/items/tool/woodworking.json @@ -262,7 +262,7 @@ "id": "saw", "type": "TOOL", "name": { "str": "wood saw" }, - "description": "This is a thin saw, useful for cutting through wood objects.", + "description": "This is a thin saw, useful for cutting through wood objects. Can turn pre-cut logs into planks by activation.", "weight": "283 g", "volume": "1 L", "longest_side": "46 cm", diff --git a/data/json/items/tool/workshop.json b/data/json/items/tool/workshop.json index 40987eae92595..5e67c927ca8ad 100644 --- a/data/json/items/tool/workshop.json +++ b/data/json/items/tool/workshop.json @@ -309,7 +309,7 @@ "id": "hammer", "type": "TOOL", "name": { "str": "hammer" }, - "description": "This is a demagnetized steel claw hammer with a wooden grip. With a hammer, nails, and two by fours in your inventory, you could board up adjacent doors and windows. It has myriad other uses as well.", + "description": "This is a demagnetized steel claw hammer with a wooden grip. With a hammer, nails, and planks in your inventory, you could board up adjacent doors and windows. It has myriad other uses as well.", "ascii_picture": "hammer", "longest_side": "25 cm", "weight": "566 g", diff --git a/data/json/items/tool_armor.json b/data/json/items/tool_armor.json index 6c756982cb0fa..b8c3a5566a78d 100644 --- a/data/json/items/tool_armor.json +++ b/data/json/items/tool_armor.json @@ -114,6 +114,28 @@ "environmental_protection": 1, "flags": [ "BLIND" ] }, + { + "id": "blindfold_duct", + "type": "ARMOR", + "name": { "str": "duct tape blindfold" }, + "//": "A folded bandana still takes up some space on the head.", + "description": "A simple covering tied over the eyes to block sight, made out of duct tape. Useful for sleeping in bright areas.", + "weight": "20 g", + "volume": "60 ml", + "price": 10, + "price_postapoc": 5, + "material": [ "plastic" ], + "symbol": "[", + "looks_like": "blindfold", + "color": "dark_gray", + "covers": [ "eyes" ], + "coverage": 95, + "encumbrance": 10, + "warmth": 0, + "material_thickness": 1, + "environmental_protection": 1, + "flags": [ "BLIND" ] + }, { "id": "ear_plugs", "type": "ARMOR", @@ -1536,6 +1558,30 @@ "qualities": [ [ "GLARE", 1 ] ], "flags": [ "VARSIZE", "STURDY", "OVERSIZE", "SUN_GLASSES", "SLEEP_IGNORE" ] }, + { + "id": "mask_wsurvivor_nofur", + "type": "TOOL_ARMOR", + "name": { "str": "faux fur winter survivor mask" }, + "description": "A custom-built, faux fur-trimmed gas mask that covers the face and eyes. It provides excellent protection from smoke, teargas, and shrapnel and while not quite as warm as real fur it's pretty good. It must be prepared before use.", + "copy-from": "mask_wsurvivor", + "material": [ "kevlar_layered", "faux_fur" ], + "symbol": "[", + "warmth": 65, + "looks_like": "mask_wsurvivor", + "color": "pink" + }, + { + "id": "mask_wsurvivorxl_nofur", + "type": "TOOL_ARMOR", + "name": { "str": "XL faux fur winter survivor mask" }, + "description": "A custom-built, faux fur-trimmed gas mask that covers the face and eyes regardless of your state of mutation. It still provides excellent protection from smoke, teargas, and shrapnel and while not quite as warm as real fur it's pretty good. It must be prepared before use.", + "copy-from": "mask_wsurvivorxl", + "material": [ "kevlar_layered", "faux_fur" ], + "symbol": "[", + "warmth": 65, + "looks_like": "mask_wsurvivorxl", + "color": "pink" + }, { "id": "mask_wsurvivor", "type": "TOOL_ARMOR", @@ -3150,5 +3196,110 @@ "need_worn": true }, "delete": { "flags": [ "DEAF" ] } + }, + { + "id": "flight_helmet", + "repairs_like": "nomex_hood", + "type": "TOOL_ARMOR", + "category": "armor", + "name": { "str": "military flight helmet" }, + "description": "A military flight helmet. Adorned with a visor, microphone and a noise dampening system. The helmet is also fitted with plastic rails to allow for different load-outs.", + "weight": "1324 g", + "volume": "2 L", + "price": 50000, + "price_postapoc": 1000, + "to_hit": -1, + "bashing": 10, + "material": [ "nomex" ], + "symbol": "[", + "looks_like": "helmet_motor", + "color": "dark_gray", + "armor_portion_data": [ + { "covers": [ "head" ], "coverage": 100, "encumbrance": 30 }, + { "covers": [ "eyes" ], "coverage": 100, "encumbrance": 5 } + ], + "warmth": 15, + "material_thickness": 5, + "environmental_protection": 2, + "techniques": [ "WBLOCK_1" ], + "flags": [ "VARSIZE", "STURDY", "DEAF", "SUN_GLASSES" ], + "charges_per_use": 1, + "ammo": "battery", + "use_action": { + "type": "transform", + "msg": "You turn the flight helmet on.", + "target": "flight_helmet_on", + "active": true, + "need_charges": 1, + "need_charges_msg": "The helmet's batteries are dead." + }, + "pocket_data": [ + { + "pocket_type": "MAGAZINE_WELL", + "holster": true, + "rigid": true, + "max_contains_volume": "20 L", + "max_contains_weight": "20 kg", + "item_restriction": [ + "light_plus_battery_cell", + "light_battery_cell", + "light_minus_battery_cell", + "light_atomic_battery_cell", + "light_minus_atomic_battery_cell", + "light_minus_disposable_cell", + "light_disposable_cell" + ] + } + ] + }, + { + "id": "flight_helmet_on", + "repairs_like": "nomex_hood", + "type": "TOOL_ARMOR", + "category": "armor", + "name": { "str": "military flight helmet" }, + "description": "A military flight helmet. Adorned with a visor, microphone and a noise dampening system. The helmet is also fitted with plastic rails to allow for different load-outs.", + "weight": "1324 g", + "volume": "2 L", + "price": 50000, + "price_postapoc": 1000, + "to_hit": -1, + "bashing": 10, + "material": [ "kevlar_rigid", "plastic" ], + "symbol": "[", + "looks_like": "helmet_motor", + "color": "dark_gray", + "armor_portion_data": [ + { "covers": [ "head" ], "coverage": 100, "encumbrance": 30 }, + { "covers": [ "eyes" ], "coverage": 100, "encumbrance": 5 } + ], + "warmth": 15, + "material_thickness": 5, + "environmental_protection": 2, + "techniques": [ "WBLOCK_1" ], + "flags": [ "VARSIZE", "STURDY", "PARTIAL_DEAF", "SUN_GLASSES", "TWO_WAY_RADIO" ], + "charges_per_use": 1, + "ammo": "battery", + "power_draw": 60, + "revert_to": "flight_helmet", + "use_action": { "type": "transform", "menu_text": "Turn off", "msg": "The %s flicks off.", "target": "flight_helmet" }, + "pocket_data": [ + { + "pocket_type": "MAGAZINE_WELL", + "holster": true, + "rigid": true, + "max_contains_volume": "20 L", + "max_contains_weight": "20 kg", + "item_restriction": [ + "light_plus_battery_cell", + "light_battery_cell", + "light_minus_battery_cell", + "light_atomic_battery_cell", + "light_minus_atomic_battery_cell", + "light_minus_disposable_cell", + "light_disposable_cell" + ] + } + ] } ] diff --git a/data/json/items/vehicle/motors.json b/data/json/items/vehicle/motors.json index 60f6191a764f0..76740c02be3b5 100644 --- a/data/json/items/vehicle/motors.json +++ b/data/json/items/vehicle/motors.json @@ -76,5 +76,16 @@ "category": "veh_parts", "price": 2000, "price_postapoc": 100 + }, + { + "type": "GENERIC", + "id": "motor_train1300", + "name": { "str": "1300hp electric train engine" }, + "description": "A 1300hp 3-phase 60 Hz electric engine. Normally used in trains.", + "weight": "2000 kg", + "volume": "250 L", + "price": 120000, + "price_postapoc": 8000, + "copy-from": "motor" } ] diff --git a/data/json/items/vehicle/plating.json b/data/json/items/vehicle/plating.json index c7de99d163048..7b9e1b6f01cac 100644 --- a/data/json/items/vehicle/plating.json +++ b/data/json/items/vehicle/plating.json @@ -37,7 +37,7 @@ "type": "GENERIC", "id": "wood_plate", "name": { "str": "wooden armor kit" }, - "description": "A bundle of two-by-fours prepared to be used as vehicle armor.", + "description": "A bundle of planks prepared to be used as vehicle armor.", "weight": "5600 g", "to_hit": -8, "color": "brown", diff --git a/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_beds.json b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_beds.json new file mode 100644 index 0000000000000..60b06d530e5b7 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_beds.json @@ -0,0 +1,178 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_mattress_beds_0", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 2, "y": 4 }, + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 5, "y": 4 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_mattress_beds_1", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 8, "y": 2 }, + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 11, "y": 2 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_mattress_beds_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 18, "y": 2 }, + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 18, "y": 5 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_mattress_beds_3", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 20, "y": 8 }, + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 20, "y": 11 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_mattress_beds_4", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 18, "y": 18 }, + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 21, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_mattress_beds_5", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 12, "y": 20 }, + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 15, "y": 20 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_mattress_beds_6", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 4, "y": 18 }, + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 4, "y": 21 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_mattress_beds_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 2, "y": 12 }, + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 2, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_straw_beds_0", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 2, "y": 4 }, + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 5, "y": 4 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_straw_beds_1", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 8, "y": 2 }, + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 11, "y": 2 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_straw_beds_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 18, "y": 2 }, + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 18, "y": 5 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_straw_beds_3", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 20, "y": 8 }, + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 20, "y": 11 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_straw_beds_4", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 18, "y": 18 }, + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 21, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_straw_beds_5", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 12, "y": 20 }, + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 15, "y": 20 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_straw_beds_6", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 4, "y": 18 }, + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 4, "y": 21 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_straw_beds_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 2, "y": 12 }, + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 2, "y": 15 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_common.json b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_common.json new file mode 100644 index 0000000000000..d7061667d359c --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_common.json @@ -0,0 +1,84 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_0", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_bulletin", "x": 11, "y": 16 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_root_cellar", + "method": "json", + "object": { "set": [ { "point": "terrain", "id": "t_rootcellar", "x": 7, "y": 12 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_water_well", + "method": "json", + "object": { "set": [ { "point": "terrain", "id": "t_water_pump", "x": 7, "y": 11 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_radio", + "method": "json", + "object": { + "set": [ + { "point": "terrain", "id": "t_radio_tower", "x": 16, "y": 11 }, + { "point": "terrain", "id": "t_radio_controls", "x": 16, "y": 12 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_prepalisade", + "method": "json", + "object": { + "set": [ + { "point": "terrain", "id": "t_pit", "x": 13, "y": 1 }, + { "point": "terrain", "id": "t_pit", "x": 14, "y": 1 }, + { "point": "terrain", "id": "t_pit", "x": 1, "y": 9 }, + { "point": "terrain", "id": "t_pit", "x": 1, "y": 10 }, + { "point": "terrain", "id": "t_pit", "x": 22, "y": 13 }, + { "point": "terrain", "id": "t_pit", "x": 22, "y": 14 } + ], + "place_items": [ { "x": 11, "y": 9, "item": "digging_soil_loam_50L", "chance": 99, "repeat": 180 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_palisade", + "method": "json", + "object": { + "set": [ + { "point": "terrain", "id": "t_palisade", "x": 13, "y": 1 }, + { "point": "terrain", "id": "t_palisade", "x": 14, "y": 1 }, + { "point": "terrain", "id": "t_palisade", "x": 1, "y": 9 }, + { "point": "terrain", "id": "t_palisade", "x": 1, "y": 10 }, + { "point": "terrain", "id": "t_palisade", "x": 22, "y": 13 }, + { "point": "terrain", "id": "t_palisade", "x": 22, "y": 14 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_reinforced_doors", + "method": "json", + "object": { + "set": [ + { "point": "terrain", "id": "t_rdoor_c", "x": 9, "y": 22 }, + { "point": "terrain", "id": "t_rdoor_c", "x": 10, "y": 22 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_metal_doors", + "method": "json", + "object": { + "set": [ + { "point": "terrain", "id": "t_door_metal_c", "x": 9, "y": 22 }, + { "point": "terrain", "id": "t_door_metal_c", "x": 10, "y": 22 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_log.json b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_log.json new file mode 100644 index 0000000000000..6535f8dc7c526 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_log.json @@ -0,0 +1,84 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_log_shack_0", + "method": "json", + "object": { + "place_nested": [ { "chunks": [ "fbmc_log_wall_vertical" ], "x": 1, "y": 3 }, { "chunks": [ "fbmc_log_shack_south" ], "x": 2, "y": 3 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_log_shack_1", + "method": "json", + "object": { + "place_nested": [ { "chunks": [ "fbmc_log_wall_vertical" ], "x": 7, "y": 1 }, { "chunks": [ "fbmc_log_shack_south" ], "x": 8, "y": 1 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_log_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_log_wall_horizontal" ], "x": 15, "y": 1 }, + { "chunks": [ "fbmc_log_shack_west" ], "x": 15, "y": 2 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_log_shack_3", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_log_wall_horizontal" ], "x": 17, "y": 7 }, + { "chunks": [ "fbmc_log_shack_west" ], "x": 17, "y": 8 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_log_shack_4", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_log_wall_vertical" ], "x": 17, "y": 15 }, + { "chunks": [ "fbmc_log_shack_north" ], "x": 18, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_log_shack_5", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_log_wall_vertical" ], "x": 11, "y": 17 }, + { "chunks": [ "fbmc_log_shack_north" ], "x": 12, "y": 17 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_log_shack_6", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_log_wall_horizontal" ], "x": 3, "y": 17 }, + { "chunks": [ "fbmc_log_shack_east" ], "x": 3, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_log_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_log_wall_horizontal" ], "x": 1, "y": 11 }, + { "chunks": [ "fbmc_log_shack_east" ], "x": 1, "y": 12 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_metal.json b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_metal.json new file mode 100644 index 0000000000000..1c8e04a3d8944 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_metal.json @@ -0,0 +1,90 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_metal_shack_0", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_metal_wall_vertical" ], "x": 1, "y": 3 }, + { "chunks": [ "fbmc_metal_shack_south" ], "x": 2, "y": 3 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_metal_shack_1", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_metal_wall_vertical" ], "x": 7, "y": 1 }, + { "chunks": [ "fbmc_metal_shack_south" ], "x": 8, "y": 1 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_metal_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_metal_wall_horizontal" ], "x": 15, "y": 1 }, + { "chunks": [ "fbmc_metal_shack_west" ], "x": 15, "y": 2 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_metal_shack_3", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_metal_wall_horizontal" ], "x": 17, "y": 7 }, + { "chunks": [ "fbmc_metal_shack_west" ], "x": 17, "y": 8 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_metal_shack_4", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_metal_wall_vertical" ], "x": 17, "y": 15 }, + { "chunks": [ "fbmc_metal_shack_north" ], "x": 18, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_metal_shack_5", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_metal_wall_vertical" ], "x": 11, "y": 17 }, + { "chunks": [ "fbmc_metal_shack_north" ], "x": 12, "y": 17 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_metal_shack_6", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_metal_wall_horizontal" ], "x": 3, "y": 17 }, + { "chunks": [ "fbmc_metal_shack_east" ], "x": 3, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_metal_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_metal_wall_horizontal" ], "x": 1, "y": 11 }, + { "chunks": [ "fbmc_metal_shack_east" ], "x": 1, "y": 12 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_migo_resin.json b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_migo_resin.json new file mode 100644 index 0000000000000..54079e51e5c3f --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_migo_resin.json @@ -0,0 +1,90 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_migo_resin_shack_0", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_migo_resin_wall_vertical" ], "x": 1, "y": 3 }, + { "chunks": [ "fbmc_migo_resin_shack_south" ], "x": 2, "y": 3 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_migo_resin_shack_1", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_migo_resin_wall_vertical" ], "x": 7, "y": 1 }, + { "chunks": [ "fbmc_migo_resin_shack_south" ], "x": 8, "y": 1 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_migo_resin_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_migo_resin_wall_horizontal" ], "x": 15, "y": 1 }, + { "chunks": [ "fbmc_migo_resin_shack_west" ], "x": 15, "y": 2 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_migo_resin_shack_3", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_migo_resin_wall_horizontal" ], "x": 17, "y": 7 }, + { "chunks": [ "fbmc_migo_resin_shack_west" ], "x": 17, "y": 8 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_migo_resin_shack_4", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_migo_resin_wall_vertical" ], "x": 17, "y": 15 }, + { "chunks": [ "fbmc_migo_resin_shack_north" ], "x": 18, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_migo_resin_shack_5", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_migo_resin_wall_vertical" ], "x": 11, "y": 17 }, + { "chunks": [ "fbmc_migo_resin_shack_north" ], "x": 12, "y": 17 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_migo_resin_shack_6", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_migo_resin_wall_horizontal" ], "x": 3, "y": 17 }, + { "chunks": [ "fbmc_migo_resin_shack_east" ], "x": 3, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_migo_resin_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_migo_resin_wall_horizontal" ], "x": 1, "y": 11 }, + { "chunks": [ "fbmc_migo_resin_shack_east" ], "x": 1, "y": 12 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_rammed_earth.json b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_rammed_earth.json new file mode 100644 index 0000000000000..5db23f07fe4e9 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_rammed_earth.json @@ -0,0 +1,90 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rammed_earth_shack_0", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rammed_earth_wall_vertical" ], "x": 1, "y": 3 }, + { "chunks": [ "fbmc_rammed_earth_shack_south" ], "x": 2, "y": 3 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rammed_earth_shack_1", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rammed_earth_wall_vertical" ], "x": 7, "y": 1 }, + { "chunks": [ "fbmc_rammed_earth_shack_south" ], "x": 8, "y": 1 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rammed_earth_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rammed_earth_wall_horizontal" ], "x": 15, "y": 1 }, + { "chunks": [ "fbmc_rammed_earth_shack_west" ], "x": 15, "y": 2 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rammed_earth_shack_3", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rammed_earth_wall_horizontal" ], "x": 17, "y": 7 }, + { "chunks": [ "fbmc_rammed_earth_shack_west" ], "x": 17, "y": 8 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rammed_earth_shack_4", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rammed_earth_wall_vertical" ], "x": 17, "y": 15 }, + { "chunks": [ "fbmc_rammed_earth_shack_north" ], "x": 18, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rammed_earth_shack_5", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rammed_earth_wall_vertical" ], "x": 11, "y": 17 }, + { "chunks": [ "fbmc_rammed_earth_shack_north" ], "x": 12, "y": 17 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rammed_earth_shack_6", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rammed_earth_wall_horizontal" ], "x": 3, "y": 17 }, + { "chunks": [ "fbmc_rammed_earth_shack_east" ], "x": 3, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rammed_earth_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rammed_earth_wall_horizontal" ], "x": 1, "y": 11 }, + { "chunks": [ "fbmc_rammed_earth_shack_east" ], "x": 1, "y": 12 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_rock.json b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_rock.json new file mode 100644 index 0000000000000..a132a6376edf7 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_rock.json @@ -0,0 +1,90 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rock_shack_0", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rock_wall_vertical" ], "x": 1, "y": 3 }, + { "chunks": [ "fbmc_rock_shack_south" ], "x": 2, "y": 3 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rock_shack_1", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rock_wall_vertical" ], "x": 7, "y": 1 }, + { "chunks": [ "fbmc_rock_shack_south" ], "x": 8, "y": 1 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rock_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rock_wall_horizontal" ], "x": 15, "y": 1 }, + { "chunks": [ "fbmc_rock_shack_west" ], "x": 15, "y": 2 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rock_shack_3", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rock_wall_horizontal" ], "x": 17, "y": 7 }, + { "chunks": [ "fbmc_rock_shack_west" ], "x": 17, "y": 8 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rock_shack_4", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rock_wall_vertical" ], "x": 17, "y": 15 }, + { "chunks": [ "fbmc_rock_shack_north" ], "x": 18, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rock_shack_5", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rock_wall_vertical" ], "x": 11, "y": 17 }, + { "chunks": [ "fbmc_rock_shack_north" ], "x": 12, "y": 17 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rock_shack_6", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rock_wall_horizontal" ], "x": 3, "y": 17 }, + { "chunks": [ "fbmc_rock_shack_east" ], "x": 3, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_rock_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rock_wall_horizontal" ], "x": 1, "y": 11 }, + { "chunks": [ "fbmc_rock_shack_east" ], "x": 1, "y": 12 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_wad.json b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_wad.json new file mode 100644 index 0000000000000..55e5cf3d92e62 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_wad.json @@ -0,0 +1,84 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wad_shack_0", + "method": "json", + "object": { + "place_nested": [ { "chunks": [ "fbmc_wad_wall_vertical" ], "x": 1, "y": 3 }, { "chunks": [ "fbmc_wad_shack_south" ], "x": 2, "y": 3 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wad_shack_1", + "method": "json", + "object": { + "place_nested": [ { "chunks": [ "fbmc_wad_wall_vertical" ], "x": 7, "y": 1 }, { "chunks": [ "fbmc_wad_shack_south" ], "x": 8, "y": 1 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wad_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wad_wall_horizontal" ], "x": 15, "y": 1 }, + { "chunks": [ "fbmc_wad_shack_west" ], "x": 15, "y": 2 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wad_shack_3", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wad_wall_horizontal" ], "x": 17, "y": 7 }, + { "chunks": [ "fbmc_wad_shack_west" ], "x": 17, "y": 8 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wad_shack_4", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wad_wall_vertical" ], "x": 17, "y": 15 }, + { "chunks": [ "fbmc_wad_shack_north" ], "x": 18, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wad_shack_5", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wad_wall_vertical" ], "x": 11, "y": 17 }, + { "chunks": [ "fbmc_wad_shack_north" ], "x": 12, "y": 17 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wad_shack_6", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wad_wall_horizontal" ], "x": 3, "y": 17 }, + { "chunks": [ "fbmc_wad_shack_east" ], "x": 3, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wad_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wad_wall_horizontal" ], "x": 1, "y": 11 }, + { "chunks": [ "fbmc_wad_shack_east" ], "x": 1, "y": 12 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_wood.json b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_wood.json new file mode 100644 index 0000000000000..c42b73f436c5d --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_fire_lookout_tower/fbmc_fire_lookout_tower_wood.json @@ -0,0 +1,90 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wood_shack_0", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wood_wall_vertical" ], "x": 1, "y": 3 }, + { "chunks": [ "fbmc_wood_shack_south" ], "x": 2, "y": 3 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wood_shack_1", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wood_wall_vertical" ], "x": 7, "y": 1 }, + { "chunks": [ "fbmc_wood_shack_south" ], "x": 8, "y": 1 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wood_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wood_wall_horizontal" ], "x": 15, "y": 1 }, + { "chunks": [ "fbmc_wood_shack_west" ], "x": 15, "y": 2 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wood_shack_3", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wood_wall_horizontal" ], "x": 17, "y": 7 }, + { "chunks": [ "fbmc_wood_shack_west" ], "x": 17, "y": 8 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wood_shack_4", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wood_wall_vertical" ], "x": 17, "y": 15 }, + { "chunks": [ "fbmc_wood_shack_north" ], "x": 18, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wood_shack_5", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wood_wall_vertical" ], "x": 11, "y": 17 }, + { "chunks": [ "fbmc_wood_shack_north" ], "x": 12, "y": 17 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wood_shack_6", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wood_wall_horizontal" ], "x": 3, "y": 17 }, + { "chunks": [ "fbmc_wood_shack_east" ], "x": 3, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_fire_lookout_tower_wood_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wood_wall_horizontal" ], "x": 1, "y": 11 }, + { "chunks": [ "fbmc_wood_shack_east" ], "x": 1, "y": 12 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+1.json b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+1.json new file mode 100644 index 0000000000000..aa4513225b6fe --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+1.json @@ -0,0 +1,62 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_mansion_+1", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_bulletin", "x": 9, "y": 9 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_mansion_+1_radio", + "method": "json", + "object": { + "set": [ + { "point": "terrain", "id": "t_radio_tower", "x": 8, "y": 9 }, + { "point": "terrain", "id": "t_radio_controls", "x": 8, "y": 10 } + ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_mansion_+1_prefarm", + "object": { + "mapgensize": [ 12, 12 ], + "rows": [ + "dddd dddd", + "d d", + "d d", + "d d", + " ", + " ", + " ", + " ", + "d d", + "d d", + "d d", + "dddd dddd" + ], + "terrain": { "d": "t_dirt" } + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_mansion_+1_farm", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mansion_+1_prefarm" ], "x": 6, "y": 6 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 6, "y": 5 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 13, "y": 5 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 1, "y": 17 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 6, "y": 17 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 1, "y": 20 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 6, "y": 20 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 13, "y": 17 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 18, "y": 17 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 13, "y": 20 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 18, "y": 20 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+2.json b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+2.json new file mode 100644 index 0000000000000..765c603dd88ea --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+2.json @@ -0,0 +1,19 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_mansion_+2", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_bulletin", "x": 18, "y": 6 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_mansion_+2_radio", + "method": "json", + "object": { + "set": [ + { "point": "terrain", "id": "t_radio_tower", "x": 12, "y": 0 }, + { "point": "terrain", "id": "t_radio_controls", "x": 12, "y": 1 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+3.json b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+3.json new file mode 100644 index 0000000000000..a74f6acd8f4ff --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+3.json @@ -0,0 +1,19 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_mansion_+3", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_bulletin", "x": 17, "y": 20 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_mansion_+3_radio", + "method": "json", + "object": { + "set": [ + { "point": "terrain", "id": "t_radio_tower", "x": 6, "y": 0 }, + { "point": "terrain", "id": "t_radio_controls", "x": 6, "y": 1 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+4.json b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+4.json new file mode 100644 index 0000000000000..1ce3050d2a783 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_+4.json @@ -0,0 +1,49 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_mansion_+4", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_bulletin", "x": 9, "y": 2 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_mansion_+4_radio", + "method": "json", + "object": { + "set": [ + { "point": "terrain", "id": "t_radio_tower", "x": 9, "y": 0 }, + { "point": "terrain", "id": "t_radio_controls", "x": 9, "y": 1 } + ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_mansion_+4_prefarm", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "dddddd", + "dddddd", + "dddddd", + "dddddd", + " ", + " " + ], + "terrain": { "d": "t_dirt" } + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_mansion_+4_farm", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mansion_+4_prefarm" ], "x": 4, "y": 10 }, + { "chunks": [ "fbmc_mansion_+4_prefarm" ], "x": 14, "y": 10 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 4, "y": 11 }, + { "chunks": [ "fbmc_mansion_farm_field" ], "x": 15, "y": 11 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_common.json b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_common.json new file mode 100644 index 0000000000000..9e20a4e62d5b9 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_common.json @@ -0,0 +1,34 @@ +[ + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_mansion_farm_field", + "object": { + "mapgensize": [ 5, 5 ], + "rows": [ + "fffff", + "fffff", + " ", + " ", + " " + ], + "terrain": { "f": "t_dirtmound" } + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_mansion_farm_field_thin", + "object": { + "mapgensize": [ 5, 5 ], + "rows": [ + "fffff", + " ", + " ", + " ", + " " + ], + "terrain": { "f": "t_dirtmound" } + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_expansion.json b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_expansion.json new file mode 100644 index 0000000000000..29334103aa596 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_expansion.json @@ -0,0 +1,110 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_e1", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_e2", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_t1", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_t2", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_t3", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_t4", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_t5", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_t6", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_t7", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_+1", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_+2", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_+3", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_+4", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_c1", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_c2", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_c3", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_c4", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_mansion_c5", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_overmap.json b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_overmap.json new file mode 100644 index 0000000000000..967028dc4dc3b --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_mansion/fbmc_mansion_overmap.json @@ -0,0 +1,86 @@ +[ + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_e1" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_e2" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_t1" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_t2" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_t3" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_t4" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_t5" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_t6" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_t7" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_c1" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_c2" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_c3" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_c4" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + }, + { + "type": "mapgen", + "om_terrain": [ "faction_base_mansion_c5" ], + "method": "json", + "object": { "fill_ter": "t_dirt" } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_pottery_cottage/fbmc_pottery_cottage_blacksmith.json b/data/json/mapgen/basecamps/fbmc_pottery_cottage/fbmc_pottery_cottage_blacksmith.json new file mode 100644 index 0000000000000..b8a574f7b3cd3 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_pottery_cottage/fbmc_pottery_cottage_blacksmith.json @@ -0,0 +1,187 @@ +[ + { + "type": "mapgen", + "nested_mapgen_id": "fbmc_pottery_cottage_blacksmith_log_nested", + "method": "json", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "www+vw", + "w....w", + "w.... ", + "w.... ", + "w.... ", + "wwwww " + ], + "palettes": [ "fbmh_log_palette" ] + } + }, + { + "type": "mapgen", + "nested_mapgen_id": "fbmc_pottery_cottage_blacksmith_metal_nested", + "method": "json", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "www+vw", + "w....w", + "w.... ", + "w.... ", + "w.... ", + "wwwww " + ], + "palettes": [ "fbmh_metal_palette" ] + } + }, + { + "type": "mapgen", + "nested_mapgen_id": "fbmc_pottery_cottage_blacksmith_migo_resin_nested", + "method": "json", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "www+vw", + "w....w", + "w.... ", + "w.... ", + "w.... ", + "wwwww " + ], + "palettes": [ "fbmh_migo_resin_palette" ] + } + }, + { + "type": "mapgen", + "nested_mapgen_id": "fbmc_pottery_cottage_blacksmith_rammed_earth_nested", + "method": "json", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "www+vw", + "w....w", + "w.... ", + "w.... ", + "w.... ", + "wwwww " + ], + "palettes": [ "fbmh_rammed_earth_palette" ] + } + }, + { + "type": "mapgen", + "nested_mapgen_id": "fbmc_pottery_cottage_blacksmith_rock_nested", + "method": "json", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "www+vw", + "w....w", + "w.... ", + "w.... ", + "w.... ", + "wwwww " + ], + "palettes": [ "fbmh_rock_palette" ] + } + }, + { + "type": "mapgen", + "nested_mapgen_id": "fbmc_pottery_cottage_blacksmith_wad_nested", + "method": "json", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "www+vw", + "w....w", + "w.... ", + "w.... ", + "w.... ", + "wwwww " + ], + "palettes": [ "fbmh_wad_palette" ] + } + }, + { + "type": "mapgen", + "nested_mapgen_id": "fbmc_pottery_cottage_blacksmith_wood_nested", + "method": "json", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "www+vw", + "w....w", + "w.... ", + "w.... ", + "w.... ", + "wwwww " + ], + "palettes": [ "fbmh_wood_palette" ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_pottery_cottage_blacksmith_log", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_pottery_cottage_blacksmith_log_nested" ], "x": 8, "y": 16 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_pottery_cottage_blacksmith_metal", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_pottery_cottage_blacksmith_metal_nested" ], "x": 8, "y": 16 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_pottery_cottage_blacksmith_migo_resin", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_pottery_cottage_blacksmith_migo_resin_nested" ], "x": 8, "y": 16 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_pottery_cottage_blacksmith_rammed_earth", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_pottery_cottage_blacksmith_rammed_earth_nested" ], "x": 8, "y": 16 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_pottery_cottage_blacksmith_rock", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_pottery_cottage_blacksmith_rock_nested" ], "x": 8, "y": 16 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_pottery_cottage_blacksmith_wad", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_pottery_cottage_blacksmith_wad_nested" ], "x": 8, "y": 16 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_pottery_cottage_blacksmith_wood", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_pottery_cottage_blacksmith_wood_nested" ], "x": 8, "y": 16 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_pottery_cottage_blacksmith_1", + "method": "json", + "object": { + "set": [ + { "point": "furniture", "id": "f_forge_rock", "x": 9, "y": 20 }, + { "point": "furniture", "id": "f_workbench", "x": 9, "y": 18 }, + { "point": "furniture", "id": "f_workbench", "x": 9, "y": 19 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_pottery_cottage_blacksmith_2", + "method": "json", + "object": { + "set": [ + { "point": "furniture", "id": "f_drophammer", "x": 12, "y": 17 }, + { "point": "furniture", "id": "f_workbench", "x": 12, "y": 18 }, + { "point": "furniture", "id": "f_workbench", "x": 12, "y": 19 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_pottery_cottage/fbmc_pottery_cottage_common.json b/data/json/mapgen/basecamps/fbmc_pottery_cottage/fbmc_pottery_cottage_common.json new file mode 100644 index 0000000000000..c8cf0a1341ddd --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_pottery_cottage/fbmc_pottery_cottage_common.json @@ -0,0 +1,19 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_pottery_cottage_0", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_bulletin", "x": 13, "y": 14 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_pottery_cottage_butchery_rack", + "method": "json", + "object": { + "set": [ + { "point": "furniture", "id": "f_butcher_rack", "x": 14, "y": 13 }, + { "point": "furniture", "id": "f_table", "x": 15, "y": 13 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_beds.json b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_beds.json new file mode 100644 index 0000000000000..feb10c126faee --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_beds.json @@ -0,0 +1,222 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_mattress_beds_controls_room", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 15, "y": 12 }, + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 15, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_mattress_beds_0", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 11, "y": 4 }, + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 14, "y": 4 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_mattress_beds_1", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 16, "y": 4 }, + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 19, "y": 4 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_mattress_beds_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 2, "y": 18 }, + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 5, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_mattress_beds_3", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 7, "y": 18 }, + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 10, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_mattress_beds_4", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 12, "y": 18 }, + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 15, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_mattress_beds_5", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 17, "y": 18 }, + { "chunks": [ "fbmc_mattress_bed_vertical" ], "x": 20, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_mattress_beds_6", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 17, "y": 17 }, + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 17, "y": 20 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_mattress_beds_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 4, "y": 11 }, + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 4, "y": 14 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_mattress_beds_8", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 4, "y": 16 }, + { "chunks": [ "fbmc_mattress_bed_horizontal" ], "x": 4, "y": 19 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_straw_beds_controls_room", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 15, "y": 12 }, + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 15, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_straw_beds_0", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 11, "y": 4 }, + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 14, "y": 4 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_straw_beds_1", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 16, "y": 4 }, + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 19, "y": 4 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_straw_beds_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 2, "y": 18 }, + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 5, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_straw_beds_3", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 7, "y": 18 }, + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 10, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_straw_beds_4", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 12, "y": 18 }, + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 15, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_straw_beds_5", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 17, "y": 18 }, + { "chunks": [ "fbmc_straw_bed_vertical" ], "x": 20, "y": 18 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_straw_beds_6", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 17, "y": 17 }, + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 17, "y": 20 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_straw_beds_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 4, "y": 11 }, + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 4, "y": 14 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_straw_beds_8", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 4, "y": 16 }, + { "chunks": [ "fbmc_straw_bed_horizontal" ], "x": 4, "y": 19 } + ] + } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_common.json b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_common.json new file mode 100644 index 0000000000000..eb2303de65c90 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_common.json @@ -0,0 +1,232 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_0", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_bulletin", "x": 9, "y": 8 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_1", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_bulletin", "x": 9, "y": 8 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_controls_room_wood", + "method": "json", + "object": { + "set": [ + { "point": "terrain", "id": "t_wall_wood", "x": 19, "y": 15 }, + { "point": "terrain", "id": "t_wall_wood", "x": 16, "y": 16 }, + { "point": "terrain", "id": "t_wall_wood", "x": 17, "y": 16 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_controls_room_migo_resin", + "method": "json", + "object": { + "set": [ + { "point": "terrain", "id": "t_wall_resin", "x": 19, "y": 15 }, + { "point": "terrain", "id": "t_wall_resin", "x": 16, "y": 16 }, + { "point": "terrain", "id": "t_wall_resin", "x": 17, "y": 16 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_fix_controls", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "faction_base_radio_tower_fix_tower", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_null", "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_root_cellar", + "method": "json", + "object": { "set": [ { "point": "terrain", "id": "t_rootcellar", "x": 19, "y": 9 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_water_well", + "method": "json", + "object": { "set": [ { "point": "terrain", "id": "t_water_pump", "x": 19, "y": 10 } ] } + }, + { + "type": "mapgen", + "nested_mapgen_id": "fbmc_radio_tower_0_prepalisade_nested", + "method": "json", + "object": { + "mapgensize": [ 24, 24 ], + "rows": [ + " ", + " ", + " ", + "pppppppppp ", + "p ", + "p ", + "p ", + "p ", + "p p ", + "p p ", + "p p ", + "p p ", + "p p ", + "p p ", + "p p ", + "p ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ], + "terrain": { "p": "t_pit" } + } + }, + { + "type": "mapgen", + "nested_mapgen_id": "fbmc_radio_tower_1_prepalisade_nested", + "method": "json", + "object": { + "mapgensize": [ 24, 24 ], + "rows": [ + "ppppppppppp ", + "p p ", + "p p ", + "p ", + "p ", + "p ", + "p ", + "p ", + "p ", + "p p ", + "p p ", + "p p ", + "ppp ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " pppppp ", + " ", + " " + ], + "terrain": { "p": "t_pit" } + } + }, + { + "type": "mapgen", + "nested_mapgen_id": "fbmc_radio_tower_0_palisade_nested", + "method": "json", + "object": { + "mapgensize": [ 24, 24 ], + "rows": [ + " ", + " ", + " ", + "ppgggggggp ", + "p r ", + "p ", + "p ", + "p ", + "p p ", + "p p ", + "p p ", + "p p ", + "pr p ", + "g p ", + "g p ", + "p ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ], + "terrain": { "p": "t_palisade", "g": "t_palisade_gate", "r": "t_palisade_pulley" } + } + }, + { + "type": "mapgen", + "nested_mapgen_id": "fbmc_radio_tower_1_palisade_nested", + "method": "json", + "object": { + "mapgensize": [ 24, 24 ], + "rows": [ + "ppgggggggpp ", + "p rp ", + "p p ", + "p ", + "p ", + "p ", + "p ", + "p ", + "p ", + "p p ", + "p p ", + "p p ", + "ppp ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " r ", + " ppgggp ", + " ", + " " + ], + "terrain": { "p": "t_palisade", "g": "t_palisade_gate", "r": "t_palisade_pulley" } + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_0_prepalisade", + "method": "json", + "object": { + "place_nested": [ { "chunks": [ "fbmc_radio_tower_0_prepalisade_nested" ], "x": 0, "y": 0 } ], + "place_items": [ { "x": 4, "y": 9, "item": "digging_soil_loam_50L", "chance": 99, "repeat": 870 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_1_prepalisade", + "method": "json", + "object": { + "place_nested": [ { "chunks": [ "fbmc_radio_tower_1_prepalisade_nested" ], "x": 0, "y": 0 } ], + "place_items": [ { "x": 4, "y": 9, "item": "digging_soil_loam_50L", "chance": 99, "repeat": 1080 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_0_palisade", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_radio_tower_0_palisade_nested" ], "x": 0, "y": 0 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_1_palisade", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_radio_tower_1_palisade_nested" ], "x": 0, "y": 0 } ] } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_log.json b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_log.json new file mode 100644 index 0000000000000..990f9d03a1e10 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_log.json @@ -0,0 +1,61 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_log_shack_1", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_log_shack_south" ], "x": 16, "y": 3 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_log_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_log_wall_vertical" ], "x": 1, "y": 15 }, + { "chunks": [ "fbmc_log_shack_north" ], "x": 2, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_log_shack_3", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_log_shack_north" ], "x": 7, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_log_shack_4", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_log_shack_north" ], "x": 12, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_log_shack_5", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_log_shack_north" ], "x": 17, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_log_shack_6", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_log_shack_west" ], "x": 14, "y": 17 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_log_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_log_wall_horizontal" ], "x": 3, "y": 10 }, + { "chunks": [ "fbmc_log_shack_east" ], "x": 3, "y": 11 } + ], + "set": [ { "point": "terrain", "id": "t_dirt", "x": 9, "y": 13 }, { "point": "terrain", "id": "t_dirt", "x": 11, "y": 16 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_log_shack_8", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_log_shack_east" ], "x": 3, "y": 16 } ] } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_metal.json b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_metal.json new file mode 100644 index 0000000000000..fb6d123cae90a --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_metal.json @@ -0,0 +1,61 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_metal_shack_1", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_metal_shack_south" ], "x": 16, "y": 3 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_metal_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_metal_wall_vertical" ], "x": 1, "y": 15 }, + { "chunks": [ "fbmc_metal_shack_north" ], "x": 2, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_metal_shack_3", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_metal_shack_north" ], "x": 7, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_metal_shack_4", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_metal_shack_north" ], "x": 12, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_metal_shack_5", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_metal_shack_north" ], "x": 17, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_metal_shack_6", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_metal_shack_west" ], "x": 14, "y": 17 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_metal_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_metal_wall_horizontal" ], "x": 3, "y": 10 }, + { "chunks": [ "fbmc_metal_shack_east" ], "x": 3, "y": 11 } + ], + "set": [ { "point": "terrain", "id": "t_dirt", "x": 9, "y": 13 }, { "point": "terrain", "id": "t_dirt", "x": 11, "y": 16 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_metal_shack_8", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_metal_shack_east" ], "x": 3, "y": 16 } ] } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_migo_resin.json b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_migo_resin.json new file mode 100644 index 0000000000000..7bc93fcc336f1 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_migo_resin.json @@ -0,0 +1,84 @@ +[ + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_radio_tower_migo_resin_shack", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwwww", + "w....w", + "w....w", + "w....w", + "w ...w", + "ww+vww" + ], + "palettes": [ "fbmh_migo_resin_palette" ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_migo_resin_shack_0", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_radio_tower_migo_resin_shack" ], "x": 10, "y": 3 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_migo_resin_shack_1", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_migo_resin_shack_south" ], "x": 16, "y": 3 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_migo_resin_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_migo_resin_wall_vertical" ], "x": 1, "y": 15 }, + { "chunks": [ "fbmc_migo_resin_shack_north" ], "x": 2, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_migo_resin_shack_3", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_migo_resin_shack_north" ], "x": 7, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_migo_resin_shack_4", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_migo_resin_shack_north" ], "x": 12, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_migo_resin_shack_5", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_migo_resin_shack_north" ], "x": 17, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_migo_resin_shack_6", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_migo_resin_shack_west" ], "x": 14, "y": 17 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_migo_resin_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_migo_resin_wall_horizontal" ], "x": 3, "y": 10 }, + { "chunks": [ "fbmc_migo_resin_shack_east" ], "x": 3, "y": 11 } + ], + "set": [ { "point": "terrain", "id": "t_dirt", "x": 9, "y": 13 }, { "point": "terrain", "id": "t_dirt", "x": 11, "y": 16 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_migo_resin_shack_8", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_migo_resin_shack_east" ], "x": 3, "y": 16 } ] } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_rammed_earth.json b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_rammed_earth.json new file mode 100644 index 0000000000000..10019ad2d3856 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_rammed_earth.json @@ -0,0 +1,61 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rammed_earth_shack_1", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rammed_earth_shack_south" ], "x": 16, "y": 3 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rammed_earth_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rammed_earth_wall_vertical" ], "x": 1, "y": 15 }, + { "chunks": [ "fbmc_rammed_earth_shack_north" ], "x": 2, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rammed_earth_shack_3", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rammed_earth_shack_north" ], "x": 7, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rammed_earth_shack_4", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rammed_earth_shack_north" ], "x": 12, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rammed_earth_shack_5", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rammed_earth_shack_north" ], "x": 17, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rammed_earth_shack_6", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rammed_earth_shack_west" ], "x": 14, "y": 17 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rammed_earth_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rammed_earth_wall_horizontal" ], "x": 3, "y": 10 }, + { "chunks": [ "fbmc_rammed_earth_shack_east" ], "x": 3, "y": 11 } + ], + "set": [ { "point": "terrain", "id": "t_dirt", "x": 9, "y": 13 }, { "point": "terrain", "id": "t_dirt", "x": 11, "y": 16 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rammed_earth_shack_8", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rammed_earth_shack_east" ], "x": 3, "y": 16 } ] } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_rock.json b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_rock.json new file mode 100644 index 0000000000000..bdce1ca1a3781 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_rock.json @@ -0,0 +1,61 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rock_shack_1", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rock_shack_south" ], "x": 16, "y": 3 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rock_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rock_wall_vertical" ], "x": 1, "y": 15 }, + { "chunks": [ "fbmc_rock_shack_north" ], "x": 2, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rock_shack_3", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rock_shack_north" ], "x": 7, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rock_shack_4", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rock_shack_north" ], "x": 12, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rock_shack_5", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rock_shack_north" ], "x": 17, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rock_shack_6", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rock_shack_west" ], "x": 14, "y": 17 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rock_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_rock_wall_horizontal" ], "x": 3, "y": 10 }, + { "chunks": [ "fbmc_rock_shack_east" ], "x": 3, "y": 11 } + ], + "set": [ { "point": "terrain", "id": "t_dirt", "x": 9, "y": 13 }, { "point": "terrain", "id": "t_dirt", "x": 11, "y": 16 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_rock_shack_8", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_rock_shack_east" ], "x": 3, "y": 16 } ] } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_wad.json b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_wad.json new file mode 100644 index 0000000000000..11b8ab9b6f343 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_wad.json @@ -0,0 +1,61 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wad_shack_1", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wad_shack_south" ], "x": 16, "y": 3 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wad_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wad_wall_vertical" ], "x": 1, "y": 15 }, + { "chunks": [ "fbmc_wad_shack_north" ], "x": 2, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wad_shack_3", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wad_shack_north" ], "x": 7, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wad_shack_4", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wad_shack_north" ], "x": 12, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wad_shack_5", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wad_shack_north" ], "x": 17, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wad_shack_6", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wad_shack_west" ], "x": 14, "y": 17 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wad_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wad_wall_horizontal" ], "x": 3, "y": 10 }, + { "chunks": [ "fbmc_wad_shack_east" ], "x": 3, "y": 11 } + ], + "set": [ { "point": "terrain", "id": "t_dirt", "x": 9, "y": 13 }, { "point": "terrain", "id": "t_dirt", "x": 11, "y": 16 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wad_shack_8", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wad_shack_east" ], "x": 3, "y": 16 } ] } + } +] diff --git a/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_wood.json b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_wood.json new file mode 100644 index 0000000000000..5913d33df14b2 --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_radio_tower/fbmc_radio_tower_wood.json @@ -0,0 +1,84 @@ +[ + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_radio_tower_wood_shack", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwwww", + "w....w", + "w....w", + "w....w", + "w ...w", + "ww+vww" + ], + "palettes": [ "fbmh_wood_palette" ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wood_shack_0", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_radio_tower_wood_shack" ], "x": 10, "y": 3 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wood_shack_1", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wood_shack_south" ], "x": 16, "y": 3 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wood_shack_2", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wood_wall_vertical" ], "x": 1, "y": 15 }, + { "chunks": [ "fbmc_wood_shack_north" ], "x": 2, "y": 15 } + ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wood_shack_3", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wood_shack_north" ], "x": 7, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wood_shack_4", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wood_shack_north" ], "x": 12, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wood_shack_5", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wood_shack_north" ], "x": 17, "y": 15 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wood_shack_6", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wood_shack_west" ], "x": 14, "y": 17 } ] } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wood_shack_7", + "method": "json", + "object": { + "place_nested": [ + { "chunks": [ "fbmc_wood_wall_horizontal" ], "x": 3, "y": 10 }, + { "chunks": [ "fbmc_wood_shack_east" ], "x": 3, "y": 11 } + ], + "set": [ { "point": "terrain", "id": "t_dirt", "x": 9, "y": 13 }, { "point": "terrain", "id": "t_dirt", "x": 11, "y": 16 } ] + } + }, + { + "type": "mapgen", + "update_mapgen_id": "fbmc_radio_tower_wood_shack_8", + "method": "json", + "object": { "place_nested": [ { "chunks": [ "fbmc_wood_shack_east" ], "x": 3, "y": 16 } ] } + } +] diff --git a/data/json/mapgen/basecamps/modular_shacks.json b/data/json/mapgen/basecamps/modular_shacks.json new file mode 100644 index 0000000000000..a690584091bab --- /dev/null +++ b/data/json/mapgen/basecamps/modular_shacks.json @@ -0,0 +1,758 @@ +[ + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_mattress_bed_horizontal", + "object": { + "mapgensize": [ 2, 2 ], + "set": [ { "point": "furniture", "id": "f_bed", "x": 0, "y": 0 }, { "point": "furniture", "id": "f_bed", "x": 1, "y": 0 } ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_mattress_bed_vertical", + "object": { + "mapgensize": [ 2, 2 ], + "set": [ { "point": "furniture", "id": "f_bed", "x": 0, "y": 0 }, { "point": "furniture", "id": "f_bed", "x": 0, "y": 1 } ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_straw_bed_horizontal", + "object": { + "mapgensize": [ 2, 2 ], + "set": [ + { "point": "furniture", "id": "f_straw_bed", "x": 0, "y": 0 }, + { "point": "furniture", "id": "f_straw_bed", "x": 1, "y": 0 } + ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_straw_bed_vertical", + "object": { + "mapgensize": [ 2, 2 ], + "set": [ + { "point": "furniture", "id": "f_straw_bed", "x": 0, "y": 0 }, + { "point": "furniture", "id": "f_straw_bed", "x": 0, "y": 1 } + ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_log_shack_south", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwww ", + "....w ", + "....w ", + "....w ", + "....w ", + "w+vww " + ], + "palettes": [ "fbmh_log_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_log_shack_north", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w+vww ", + "....w ", + "....w ", + "....w ", + "....w ", + "wwwww " + ], + "palettes": [ "fbmh_log_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_log_shack_west", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "v....w", + "+....w", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_log_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_log_shack_east", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "w....v", + "w....+", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_log_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_log_wall_horizontal", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwwww", + " ", + " ", + " ", + " ", + " " + ], + "palettes": [ "fbmh_log_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_log_wall_vertical", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w ", + "w ", + "w ", + "w ", + "w ", + "w " + ], + "palettes": [ "fbmh_log_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_metal_shack_south", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwww ", + "....w ", + "....w ", + "....w ", + "....w ", + "w+vww " + ], + "palettes": [ "fbmh_metal_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_metal_shack_north", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w+vww ", + "....w ", + "....w ", + "....w ", + "....w ", + "wwwww " + ], + "palettes": [ "fbmh_metal_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_metal_shack_west", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "v....w", + "+....w", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_metal_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_metal_shack_east", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "w....v", + "w....+", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_metal_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_metal_wall_horizontal", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwwww", + " ", + " ", + " ", + " ", + " " + ], + "palettes": [ "fbmh_metal_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_metal_wall_vertical", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w ", + "w ", + "w ", + "w ", + "w ", + "w " + ], + "palettes": [ "fbmh_metal_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_migo_resin_shack_south", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwww ", + "....w ", + "....w ", + "....w ", + "....w ", + "w+vww " + ], + "palettes": [ "fbmh_migo_resin_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_migo_resin_shack_north", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w+vww ", + "....w ", + "....w ", + "....w ", + "....w ", + "wwwww " + ], + "palettes": [ "fbmh_migo_resin_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_migo_resin_shack_west", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "v....w", + "+....w", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_migo_resin_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_migo_resin_shack_east", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "w....v", + "w....+", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_migo_resin_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_migo_resin_wall_horizontal", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwwww", + " ", + " ", + " ", + " ", + " " + ], + "palettes": [ "fbmh_migo_resin_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_migo_resin_wall_vertical", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w ", + "w ", + "w ", + "w ", + "w ", + "w " + ], + "palettes": [ "fbmh_migo_resin_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rammed_earth_shack_south", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwww ", + "....w ", + "....w ", + "....w ", + "....w ", + "w+vww " + ], + "palettes": [ "fbmh_rammed_earth_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rammed_earth_shack_north", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w+vww ", + "....w ", + "....w ", + "....w ", + "....w ", + "wwwww " + ], + "palettes": [ "fbmh_rammed_earth_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rammed_earth_shack_west", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "v....w", + "+....w", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_rammed_earth_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rammed_earth_shack_east", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "w....v", + "w....+", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_rammed_earth_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rammed_earth_wall_horizontal", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwwww", + " ", + " ", + " ", + " ", + " " + ], + "palettes": [ "fbmh_rammed_earth_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rammed_earth_wall_vertical", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w ", + "w ", + "w ", + "w ", + "w ", + "w " + ], + "palettes": [ "fbmh_rammed_earth_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rock_shack_south", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwww ", + "....w ", + "....w ", + "....w ", + "....w ", + "w+vww " + ], + "palettes": [ "fbmh_rock_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rock_shack_north", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w+vww ", + "....w ", + "....w ", + "....w ", + "....w ", + "wwwww " + ], + "palettes": [ "fbmh_rock_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rock_shack_west", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "v....w", + "+....w", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_rock_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rock_shack_east", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "w....v", + "w....+", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_rock_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rock_wall_horizontal", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwwww", + " ", + " ", + " ", + " ", + " " + ], + "palettes": [ "fbmh_rock_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_rock_wall_vertical", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w ", + "w ", + "w ", + "w ", + "w ", + "w " + ], + "palettes": [ "fbmh_rock_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wad_shack_south", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwww ", + "....w ", + "....w ", + "....w ", + "....w ", + "w+vww " + ], + "palettes": [ "fbmh_wad_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wad_shack_north", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w+vww ", + "....w ", + "....w ", + "....w ", + "....w ", + "wwwww " + ], + "palettes": [ "fbmh_wad_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wad_shack_west", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "v....w", + "+....w", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_wad_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wad_shack_east", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "w....v", + "w....+", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_wad_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wad_wall_horizontal", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwwww", + " ", + " ", + " ", + " ", + " " + ], + "palettes": [ "fbmh_wad_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wad_wall_vertical", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w ", + "w ", + "w ", + "w ", + "w ", + "w " + ], + "palettes": [ "fbmh_wad_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wood_shack_south", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwww ", + "....w ", + "....w ", + "....w ", + "....w ", + "w+vww " + ], + "palettes": [ "fbmh_wood_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wood_shack_north", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w+vww ", + "....w ", + "....w ", + "....w ", + "....w ", + "wwwww " + ], + "palettes": [ "fbmh_wood_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wood_shack_west", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "v....w", + "+....w", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_wood_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wood_shack_east", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w....w", + "w....v", + "w....+", + "w....w", + "wwwwww", + " " + ], + "palettes": [ "fbmh_wood_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wood_wall_horizontal", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "wwwwww", + " ", + " ", + " ", + " ", + " " + ], + "palettes": [ "fbmh_wood_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "fbmc_wood_wall_vertical", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "w ", + "w ", + "w ", + "w ", + "w ", + "w " + ], + "palettes": [ "fbmh_wood_palette" ] + } + } +] diff --git a/data/json/mapgen/bugs/wasp_tower.json b/data/json/mapgen/bugs/wasp_tower.json new file mode 100644 index 0000000000000..6f1ba77ff6d9c --- /dev/null +++ b/data/json/mapgen/bugs/wasp_tower.json @@ -0,0 +1,360 @@ +[ + { + "type": "mapgen", + "method": "json", + "om_terrain": [ "wasp_tower" ], + "weight": 250, + "object": { + "fill_ter": "t_floor", + "rows": [ + " ---------------------- ", + " | | ", + " | | ", + " | R____R | ", + " |_______ ______ | ", + " |_______ ______ | ", + " |_______ ______ | ", + " |_______ _<____ | ", + " |_______ R____R | ", + " |_______ 6 | ", + " |_______ | ", + " ------22-------------- ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ], + "terrain": { + " ": [ "t_region_groundcover" ], + "#": "t_concrete_wall", + "R": "t_radio_tower", + "2": "t_chaingate_c", + "-": "t_chainfence_h", + "|": "t_chainfence_v", + "6": "t_radio_controls", + "_": "t_pavement", + "<": "t_stairs_up" + }, + "nested": { + " ": { "chunks": [ [ "null", 50 ], [ "wasp_gibs", 10 ] ] }, + "_": { "chunks": [ [ "null", 50 ], [ "wasp_gibs", 10 ] ] } + } + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ "wasp_tower_1" ], + "weight": 250, + "object": { + "fill_ter": "t_floor", + "rows": [ + " -2222222-------------- ", + " |_______ | ", + " |_______ | ", + " |_______ R____R | ", + " |_______ ______ | ", + " |_______ ______ | ", + " |_______ ______ | ", + " |_______ _<____ | ", + " |_______ R____R | ", + " |___________ | ", + " |___________ | ", + " --------##+########--- ", + " #..ll#rrrC#4 ", + " w....+..h6# ", + " ##+#w#...C# ", + " |R wCCCCw ", + " |----##ww## ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ], + "terrain": { + " ": [ "t_region_groundcover" ], + "#": "t_concrete_wall", + "R": "t_radio_tower", + "2": "t_chaingate_c", + "-": "t_chainfence_h", + "|": "t_chainfence_v", + "6": "t_radio_controls", + "_": "t_pavement", + "+": [ "t_door_c", [ "t_door_b", 3 ], [ "t_door_frame", 3 ] ], + "4": "t_gutter_downspout", + "<": "t_stairs_up", + "w": [ "t_window", [ "t_window_frame", 3 ], [ "t_window_empty", 3 ] ] + }, + "furniture": { "C": "f_counter", "r": "f_rack", "h": "f_chair", "l": "f_locker" }, + "items": { + "C": { "item": "radio", "chance": 80, "repeat": [ 1, 2 ] }, + "r": { "item": "recycle_electronic", "chance": 80, "repeat": [ 1, 2 ] }, + "l": { "item": "clothing_work_set", "chance": 80 } + }, + "nested": { + " ": { "chunks": [ [ "null", 50 ], [ "wasp_gibs", 10 ] ] }, + "_": { "chunks": [ [ "null", 50 ], [ "wasp_gibs", 10 ] ] } + } + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": "wasp_tower_roof_1", + "object": { + "fill_ter": "t_metal_floor_no_roof", + "rows": [ + " ", + " ", + " ", + " R,,,,R ", + " , , ", + " , , ", + " aaaa , ", + " a>": "t_stairs_down" } + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": "wasp_tower_even_1", + "object": { + "fill_ter": "t_open_air", + "rows": [ + " ", + " :: ", + " aaaaaaaa ", + " aR,,,,Ra ", + " :a,,,,,,a# ", + " X a,,::,,a ", + " XXXXXa,,::,,a ", + " XX_XXX,<>,XXaX ", + " X___XXR,,,XRXXXX ", + " XX____XXXXXX___XX ", + " XX______________XX ", + " X_______________XX ", + " X_______________XX ", + " X_______________XX ", + " XX______________XX ", + " XX_____________XX ", + " X____________XXX ", + " X___________XXX ", + " XXX________XXX ", + " XXXX____XXXX ", + " XX__XXXXX ", + " X__XX ", + " ", + " " + ], + "palettes": [ "wasp_palette" ], + "nested": { "_": { "chunks": [ "null", "wasp_gibs" ] } } + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": "wasp_tower_even_2", + "object": { + "fill_ter": "t_open_air", + "rows": [ + " ", + " :: ", + " aaaaaaaa ", + " aR,,,,Ra ", + " XX,,,,,,X# ", + " XXXX,::XXXXX ", + " XXXX_aXXXXXXaXX ", + " X_ a,<>,,,a XX ", + " X ..aR,,,,Ra X ", + " XX xxxxxxxxxxx. XX ", + " XX xOxOxOxOxOx. X ", + " XX xxxxxxxxxxxxx X ", + " XX xOxOxOxOxOxOx X ", + " XX xxxxxxxxxxxxxx X ", + " XX xOxOxOxOxOxOx X ", + " XX xxxxxxxxxxxxx X ", + " XXX xOxOxOxOx.. X ", + " XXX xxxxxxxxx. XX ", + " XX xOxOxOx. XX ", + " XX xxxxxxx XX ", + " XXX _XX ", + " XXXXXXXXXXX ", + " XXXXX ", + " " + ], + "palettes": [ "wasp_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": "wasp_tower_odd_1", + "object": { + "fill_ter": "t_open_air", + "rows": [ + " ", + " ", + " ", + " :R,,,,R ", + " , , ", + " XXX, , ", + " XXXXXXXXXa XXXX ", + " XXX X>,,,xOx. X ", + " XX xxxxxxxxxxxxx. XX ", + " X xOxOxOxOxOxOx. X ", + " XX xxxxxxxxxxxxxxx. X ", + " X xOxOxOxOxOxOxOx. X ", + " X xxxxxxxxxxxxxxxxx X ", + " X xOxOxOxOxOxOxOxOx X ", + " X xxxxxxxxxxxxxxxxx X ", + " X .xOxOxOxOxOxOxOx. X ", + " X xxxxxxxxxxxxxxx. X ", + " XX xOxOxOxOxOxOx. X ", + " XX xxxxxxxxxxxxx XX ", + " X ...xOxOxOx.. XX ", + " X .xxxxxxx. XX ", + " XXX ... XX ", + " XXXX XXXX ", + " XXXXXXX " + ], + "palettes": [ "wasp_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": "wasp_tower_roof", + "object": { + "fill_ter": "t_open_air", + "rows": [ + " ", + " ", + " ********* ", + " *********** ", + " ************** ", + " ***************** ", + " ****************** ", + " ******************* ", + " ******************** ", + " ******************** ", + " ********************* ", + " ********************* ", + " ********************* ", + " ********************* ", + " ********************* ", + " ********************* ", + " ********************* ", + " ********************* ", + " ******************** ", + " ****************** ", + " ***************** ", + " **************** ", + " ************* ", + " ******* " + ], + "palettes": [ "wasp_palette" ] + } + } +] diff --git a/data/json/mapgen/bunker.json b/data/json/mapgen/bunker.json index b5df262465190..75bbb4a50dea6 100644 --- a/data/json/mapgen/bunker.json +++ b/data/json/mapgen/bunker.json @@ -112,7 +112,7 @@ { "type": "mapgen", "method": "json", - "om_terrain": [ "bunker_basement" ], + "om_terrain": [ "bunker_basement_1" ], "weight": 250, "object": { "fill_ter": "t_floor", @@ -211,7 +211,7 @@ { "type": "mapgen", "method": "json", - "om_terrain": [ "bunker_basement" ], + "om_terrain": [ "bunker_basement_1" ], "weight": 500, "object": { "fill_ter": "t_floor", diff --git a/data/json/mapgen/hunting_lodge.json b/data/json/mapgen/hunting_lodge.json new file mode 100644 index 0000000000000..c675c04bebdcd --- /dev/null +++ b/data/json/mapgen/hunting_lodge.json @@ -0,0 +1,180 @@ +[ + { + "type": "mapgen", + "method": "json", + "om_terrain": [ [ "lodge_ground1", "lodge_ground2" ] ], + "weight": 100, + "object": { + "fill_ter": "t_floor", + "rows": [ + ".%##W###W###W###W###+##W#################%......", + ".*#c B#c B#B c#B c# #h s# >#*......", + ".*Wd B#d B#B d#B d# #h T# W*......", + ".*##=###=###=###=## ##=## #*......", + ".*# #*......", + ".*W W*......", + ".*# YY YY AAAAAAAA a OOO >#*......", + ".%########+######################## ###%......", + ".*********G~~~~~~~~~~~~~~~~~~~~~~%# #***......", + "~~~~~~~~~~G~~~~~~~~~~~*############ #*........", + "oooooooooooooooo~~~~~~*w #%........", + "oooooooooooooooo~~~~~~*#Y w*........", + "ooooooooooooooooGGGGGGG+ ttttt bw*........", + "ooooooooooooooooGGGGGGG+ ttttt bw*........", + "oooooooooooooooo~~~~~G*#Y w*........", + "oooooooooooooooo~~~~~G*w w*........", + "~~~~~~~~~~~~~~~~~~~~~G*##### ########*........", + "~~~~~~~~~~~~~~~~~~~~~G***#<# #********........", + "~~~~~~~~~~~~~~~~%~~~~GGGG+ # #%****...........", + "......*###-###-###-######### #####*...........", + ".....0*#UU URU URU URU #12C 5 F#*...........", + ".....0*#R z C uW*...........", + ".....0*#UU URU URU URU #3CC& 4CVuF#*...........", + "......%###-###-###-##########+######%..........." + ], + "palettes": [ "lodge_palette" ], + "place_monsters": [ { "monster": "GROUP_ZOMBIE", "x": 29, "y": 4 } ], + "place_nested": [ + { + "chunks": [ [ "lodge_pantry_15x15", 80 ], [ "lodge_cannibal_15x15", 20 ], [ "lodge_hunting_15x15", 50 ] ], + "x": 8, + "y": 9 + } + ] + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ [ "lodge_2ndfloor1", "lodge_2ndfloor2" ] ], + "weight": 100, + "object": { + "fill_ter": "t_floor", + "rows": [ + " ##W###W###W###W###w##W################# ", + " # <# ", + " W W ", + " # # ", + " # # ", + " W W ", + " # <# ", + " ########w#######wwww#############www### ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ], + "palettes": [ "lodge_2ndfloor_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ [ "lodge_basement_residential1", "lodge_basement_residential2" ] ], + "weight": 100, + "object": { + "fill_ter": "t_rock", + "rows": [ + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", + " % % ", + " % % ", + " % % ", + " % % ", + " % % ", + " % % ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% ", + " % % ", + " %%%%%%%%%%%%%% % ", + " % BB% ", + " % % ", + " % % ", + " % % ", + " % % ", + " % % ", + " %%%%% %%%%%%%% ", + " %<| % ", + " % + % ", + " %%%%%%% ", + " ", + " ", + " ", + " " + ], + "palettes": [ "basement_residential" ], + "place_monsters": [ { "monster": "GROUP_ZOMBIE", "x": 7, "y": 4 } ], + "place_nested": [ + { "chunks": [ [ "lodge_game_6x6", 80 ], [ "lodge_dungeon_6x6", 20 ], [ "lodge_drug_6x6", 50 ] ], "x": 2, "y": 1 }, + { "chunks": [ [ "5x5_sauna_W", 30 ], [ "5x5_pool", 10 ], [ "5x5_gym_W", 60 ] ], "x": 24, "y": 1 }, + { + "chunks": [ [ "room_6x6_woodworker", 5 ], [ "room_6x6_bike", 15 ], [ "room_6x6_office_E", 5 ] ], + "x": 24, + "y": 10 + } + ] + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ "lodge_basement_laboratory_entrance" ], + "//": "doesn't work rebuild later", + "object": { + "fill_ter": "t_rock", + "rotation": [ 0, 3 ], + "rows": [ + "##--------------------##", + "##| |>>| |##", + "##| |MM| |##", + "##| |..| |##", + "##| |..| |##", + "##| |..| |##", + "##| |..| |##", + "##| |..| |##", + "##|---+---|..|---+---|##", + "##|.......M..M.......|##", + "##|.......M..M.......|##", + "|----+----|..|----+----|", + "|.........|..|d.......d|", + "|.ccccccc.|..|xh..h..hx|", + "|.........|7.|d..dxd..d|", + "|---------|..|---------|", + "########--|LL6--########", + "########=,,,,,,=########", + "########=,,,,,,=########", + "########=,,,,,,=########", + "########===WW===########", + "##########=,,=##########", + "##########=<<=##########", + "##########====##########" + ], + "palettes": [ "lab_palette" ], + "terrain": { + "=": "t_wall", + ",": "t_rock_floor", + "6": "t_card_science", + "7": "t_rock_floor", + "<": "t_stairs_up", + ">": "t_stairs_down" + }, + "furniture": { "C": "f_centrifuge" }, + "mapping": { "c": { "items": { "item": "chem_lab", "chance": 30 } }, "d": { "items": { "item": "office", "chance": 30 } } }, + "monster": { "7": { "monster": "mon_turret_rifle" } }, + "place_nested": [ + { "chunks": [ "lab_spawn_7x7_crossdoors" ], "x": 3, "y": 1 }, + { "chunks": [ "lab_spawn_7x7_crossdoors" ], "x": 14, "y": 1 } + ] + } + } +] diff --git a/data/json/mapgen/mall/mall_ground.json b/data/json/mapgen/mall/mall_ground.json index 2f1b5032f5244..c42e0f1e7ef94 100644 --- a/data/json/mapgen/mall/mall_ground.json +++ b/data/json/mapgen/mall/mall_ground.json @@ -1434,7 +1434,7 @@ "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГГɱɱ||||||^^99@@@99^9ɅɅɅ9Ʌ9^M^^M^^MMM^MM^H%,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,", "ւГɔɔɔɔɔɔɔ˽˽˽˽˽˽˽˽˽˽˽˽ɔɔɔɔɔɔɔГГɱЯɱHd|T^^^^9999999^9Ʌ999Ʌ9^^^^^^^^^^^^^dH.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,", "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱϻɱHy|T^^M^99ǝǝǝ99^9Ʌ9ɅɅɅ9^^^^^^777777^^H..FFF...,,.....FFF................,,,,,,.........", - "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱHd|T^^M^9999999^9999999^^^^^^7))))7^Ŧ||||||||.,,.||HHH||HHH|^^^|HHH||||.,....,.||||HHH|", + "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱHd|T^^M^9999999^9999999^^^^^^7))))7^Ŧ||||||||.,,.||HHH||HHH|óóó|HHH||||.,....,.||||HHH|", "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱ|θ|T^^M^99@@@99^^^^^^^^^^^^^^7))))7^Ŧ|t_|t|t|.,,.|y:::yc:::y^^^^:::^Cy|.,,³³,,.Hd|Ħ^^^^", "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱϻɱ|b^^^^^^9999999^^ĦĦĦ^^HHHH|y^777777^Ŧ|__|_|_|.,,.|b666666666666666666:H.,.°°.,.Hy|Ħ^3KK", "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱ|Ħ^^KK^^^^^^^^^^^^^^^^^^^^|b^^^^^^^^b||=|=|=|.,,F|:664446644446466466:H.,,³³,,.Hd|Ħ^^^^", @@ -1442,7 +1442,7 @@ "ւГɔɔɔɔɔɔɔ˽˽˽˽˽˽˽˽˽˽˽˽ɔɔɔɔɔɔɔГГɱϻɱ|Ħ^^^^^^Ħ^^Δ^;^^K^K^^b|<^^^^^7((((7^Ŧ|______|.,,F|b66666664664646^b::yH.,,³³,,.|ydy=^^?", "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱ|Ħ^^KK^^Ħ^^Δ^;^^K^K^^y|HHHH^^777777^Ŧ|______=.,,.|:66444664666646C|HHH|.,....,.|HHH|JƃJ", "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱ|b^^KK^^Ħ^^Δ^;^^^^^^^^^^^^^^^7((((7^b|j0j0j0|.,,%|:66444664444666b|.....,,,,,,.....|Ħ^^", - "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱϻɱ|Ħ^^^^^^Ħ^^^^^^^K^K^^^^^^^^^^777777^Ŧ||||||||.,,u|^66666666666666^^.,,,,,,,,,,,,,,.||||" + "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱϻɱ|Ħ^^^^^^Ħ^^^^^^^K^K^^^^^^^^^^777777^Ŧ||||||||.,,u|^66666666666666^ó.,,,,,,,,,,,,,,.||||" ], "palettes": [ "mall_palette_2" ], "terrain": { @@ -1460,6 +1460,7 @@ "*": "t_linoleum_gray", "%": "t_linoleum_gray", "u": "t_linoleum_gray", + "ó": "t_retractable_gate_l", "@": "t_carpet_concrete_yellow", "ǝ": "t_carpet_concrete_yellow", "Ʌ": "t_carpet_concrete_yellow", @@ -1725,8 +1726,8 @@ "object": { "fill_ter": "t_floor", "rows": [ - "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱ|^^^^d^^^^^d^^^^K^K^^d^^^^^^^^^^^^^^T|j0j0j0|.,,u|ƃJJJJ?JJJJJJJ^^^^.,,,,......,,,,.|_@@", - "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱ|C^^^^^^^^^^^3^^^^^^^^^999999^^T|^||||______=.,,*|^^^^A^^^^^^^ƃ^^^^.,,,..1111..,,,.H_@ǝ", + "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱ|^^^^d^^^^^d^^^^K^K^^d^^^^^^^^^^^^^^T|j0j0j0|.,,u|ƃJJJJ?JJJJJJJ^^^ó.,,,,......,,,,.|_@@", + "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱ|C^^^^^^^^^^^3^^^^^^^^^999999^^T|^||||______=.,,*|^^^^A^^^^^^^ƃ^^^ó.,,,..1111..,,,.H_@ǝ", "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱ|b^^KK^^Ħ^^d^3^999999^^9ɅɅɅɅ9^^T|^|bb|______|.,,.|y^Y~~~~^^:::J^::|.,,,.F1³³1F.,,,.H_@9", "ւГɔɔɔɔɔɔɔ˽˽˽˽˽˽˽˽˽˽˽˽ɔɔɔɔɔɔɔГГɱЯɱ|Ħ^^KK^^Ħ^^^^3^9@99@9^^9ɅɅɅɅ9^^T|^=^^|______|F,,.||θ|||||θ|HHH|||||.,,,.F1³³1F.,,,.|_99", "ւГ˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽˽ГГɱЯɱ|Ħ^^^^^^Ħ^^d^3^9@99@9^^999999^^T|^|||||=|=|=|F,,.|w_0|P____NNNa|FF|.,,,.F1³³1F.,,,.__99", @@ -1771,6 +1772,7 @@ "*": "t_linoleum_gray", "%": "t_linoleum_gray", "u": "t_linoleum_gray", + "ó": "t_retractable_gate_l", "@": "t_carpet_concrete_yellow", "ǝ": "t_carpet_concrete_yellow", "Ʌ": "t_carpet_concrete_yellow", diff --git a/data/json/mapgen/map_extras/drug_dealers.json b/data/json/mapgen/map_extras/drug_dealers.json new file mode 100644 index 0000000000000..5d34b27fa1976 --- /dev/null +++ b/data/json/mapgen/map_extras/drug_dealers.json @@ -0,0 +1,51 @@ +[ + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "corpse_blood_gibs_drugs_3x3", + "object": { + "mapgensize": [ 3, 3 ], + "place_items": [ { "item": "map_extra_drugdeal", "x": [ 0, 2 ], "y": [ 0, 2 ], "chance": 100 } ], + "place_fields": [ { "field": "fd_blood", "x": [ 0, 2 ], "y": [ 0, 2 ] }, { "field": "fd_gibs_flesh", "x": [ 0, 2 ], "y": [ 0, 2 ] } ] + } + }, + { + "type": "mapgen", + "method": "json", + "update_mapgen_id": "mx_drugdeal", + "object": { + "rows": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " 1 ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ], + "terrain": { " ": [ [ "t_region_groundcover_urban", 50 ], [ "t_region_groundcover_barren", 20 ] ] }, + "monsters": { " ": { "monster": "GROUP_NETHER_CAPTURED", "chance": 1, "density": 0.0001 } }, + "nested": { + " ": { "chunks": [ [ "corpse_blood_gibs_drugs_3x3", 1 ], [ "null", 150 ] ] }, + "1": { "chunks": [ "corpse_blood_gibs_drugs_3x3" ] } + } + } + } +] diff --git a/data/json/mapgen/map_extras/nest_wasp.json b/data/json/mapgen/map_extras/nest_wasp.json index f3ffb4b03f4f6..54155a869bc06 100644 --- a/data/json/mapgen/map_extras/nest_wasp.json +++ b/data/json/mapgen/map_extras/nest_wasp.json @@ -17,7 +17,7 @@ " ---.---.,---.- ", " t-.--.,--...-.-- ", " --...-.--.--. ", - " -..-,-..-,-..-t t ", + " -..-,-.8-,-..-t t ", " t --....-.-..--- ", " t---.,.-.---..- ", " -.---.---..--- ", @@ -34,9 +34,11 @@ "t": [ "t_region_tree", "t_region_groundcover_swamp" ], ",": [ "t_region_tree" ], "-": [ "t_paper" ], - ".": [ "t_floor_paper" ] + ".": [ "t_floor_paper" ], + "8": [ "t_floor_paper" ] }, - "place_monsters": [ { "monster": "GROUP_WASP", "x": [ 0, 23 ], "y": [ 0, 23 ], "repeat": [ 0, 3 ], "density": 0.1 } ] + "monster": { "8": { "group": "GROUP_WASP_QUEEN" } }, + "place_monster": [ { "group": "GROUP_WASP_NEST", "x": [ 0, 23 ], "y": [ 0, 23 ], "repeat": [ 5, 15 ] } ] } } ] diff --git a/data/json/mapgen/military/mil_base/mil_base_z0.json b/data/json/mapgen/military/mil_base/mil_base_z0.json index 217d06746dd9f..782f5b5ee8ef9 100644 --- a/data/json/mapgen/military/mil_base/mil_base_z0.json +++ b/data/json/mapgen/military/mil_base/mil_base_z0.json @@ -752,13 +752,37 @@ { "item": "m203", "x": 10, "y": 11, "chance": 75, "repeat": 5 }, { "item": "mgl", "x": 10, "y": 11, "chance": 75, "repeat": 3 }, { "item": "40x46mm_m433", "x": 10, "y": 12, "chance": 75, "repeat": 20 }, - { "item": "m4a1", "x": 12, "y": [ 9, 11 ], "magazine": 100, "chance": 75, "repeat": 30 }, - { "item": "m27iar", "x": 12, "y": 9, "magazine": 100, "chance": 75, "repeat": 8 }, + { + "item": "nato_assault_rifle", + "variant": "m4a1", + "x": 12, + "y": [ 9, 11 ], + "magazine": 100, + "chance": 75, + "repeat": 30 + }, + { + "item": "nato_assault_rifle", + "variant": "m27iar", + "x": 12, + "y": 9, + "magazine": 100, + "chance": 75, + "repeat": 8 + }, { "item": "m16a4", "x": 12, "y": 9, "magazine": 100, "chance": 75, "repeat": 2 }, { "item": "stanag30", "x": 12, "y": 12, "chance": 75, "repeat": 80 }, { "item": "stanag50", "x": 12, "y": 12, "chance": 75, "repeat": 20 }, { "item": "556", "x": 14, "y": [ 9, 12 ], "chance": 75, "repeat": 150 }, - { "item": "m4a1", "x": 16, "y": [ 9, 11 ], "magazine": 100, "chance": 75, "repeat": 36 }, + { + "item": "nato_assault_rifle", + "variant": "m4a1", + "x": 16, + "y": [ 9, 11 ], + "magazine": 100, + "chance": 75, + "repeat": 36 + }, { "item": "stanag30", "x": 16, "y": 12, "chance": 75, "repeat": 100 }, { "item": "556", "x": 18, "y": [ 9, 12 ], "chance": 75, "repeat": 150 }, { "item": "m249", "x": 20, "y": 9, "chance": 75, "repeat": 6 }, diff --git a/data/json/mapgen/mine/mine_entrance.json b/data/json/mapgen/mine/mine_entrance.json new file mode 100644 index 0000000000000..f6b517c69f266 --- /dev/null +++ b/data/json/mapgen/mine/mine_entrance.json @@ -0,0 +1,219 @@ +[ + { + "type": "mapgen", + "method": "json", + "om_terrain": [ [ "mine_entrance", "mine_entrance_loading_zone" ] ], + "object": { + "rows": [ + "fffffffffffФФfffffffffffffffФФФФФffffffФФФФФff ", + "f v ,,,,,,,,,,,, ! f ;;;;;;;;;;;;;;;; f ", + "f/ ,,,,,,,,,,,,,,,,, f ;;;;;;;;;;;;;;;; f ", + "|-000--000-0+0|00--00|, f ;;;;;;;;;;;;;;;; f ", + "|dddrFFddcW..2|2..Fdd|, f ;°°°;;;;;;;;;°;; f ", + "0dC.....Cc....?....dC|, f ;°°°;;;;;;;;°°°; f ", + "0............a|b...d.|, f ;°°°;;;;;;;°°°°° f ", + "0..C...|*-|-*-|b.....|, f °°°°°;;;;;;;°°°; f ", + "|rddd1p|.l|&_s|a36561|, f ;°°°;;;;;;;;°°°; f ", + "|-000--|--|---|------|, f ;;°;;;;;;;;;°°°; f ", + "f ,,,,,,,,,,,,,,,,,,,,,,;;;;;;;;;;;;;;;; f ", + "|----|+|----| ,,,,,,,,,,,,,;;;;;;;;;;;;;;;; f ", + "|I.hĎ|.|дh.I| ,|---%-------;;;;;;;;;;;;;;;; f ", + "0BB..+.+..BB0 ,|eEE~~~eEE®®;;;;;;Ø;;;;;;;;; f ", + "|----|.|----| ,|~EE~~~~EE==;;;;;;;;;;;;;;;; f ", + "|I.hĎ|.|дh.I| ,|------|EE=~;;;;;;;;;;;;;;;; f ", + "0BB..+.+..BB0 ,|TTogTr|~~=~;;;;;;;;;;;;;;;; f ", + "|----|.|----| ,0S....Ć|8~=~8| |````| f ", + "|I.hD|.|Дh.I| ,|a....Ĉ|8~=~8| |$``@| f ", + "0BB..+.+..BB0 ,|----+-|8~=~8| |````| f ", + "|----|.|----| ,|hth..h|8~=~8| |````| f ", + "|&___+.+____| ,+.....t|8~~~8| |````| f ", + "|R__s|.|Й__7|/ /|hth..h|88888|/ /|9999| f ", + "|----|0|----|fff|-0--0-|-----|fff|----|fffffff " + ], + "fill_ter": "t_floor", + "terrain": { + " ": [ [ "t_region_groundcover", 4 ], [ "t_region_shrub", 2 ], [ "t_region_tree", 1 ] ], + "!": "t_manhole_cover", + "/": "t_gutter_downspout", + "|": "t_wall", + "-": "t_wall", + "_": "t_linoleum_white", + "&": "t_linoleum_white", + "*": "t_door_c", + "?": "t_door_locked_alarm", + "+": "t_door_locked", + "%": "t_door_metal_c", + ",": "t_sidewalk", + ";": "t_pavement", + "°": "t_pavement_y", + "Ø": "t_pavement", + ".": "t_floor", + "~": "t_thconc_floor", + "`": "t_thconc_floor", + "®": "t_thconc_floor", + "=": "t_conveyor", + "0": "t_metal_grate_window_with_curtain", + "4": "t_thconc_floor", + "8": "t_thconc_floor", + "9": "t_thconc_floor", + "E": "t_elevator", + "e": "t_elevator_control_off", + "f": "t_chainfence", + "L": "t_door_metal_locked", + "R": "t_linoleum_white", + "s": "t_linoleum_white", + "v": "t_dirt", + "w": "t_window_alarm", + "W": "t_water_dispenser", + "Й": "t_linoleum_white", + "Ф": "t_chaingate_l" + }, + "furniture": { + "®": "f_machinery_heavy", + "1": "f_shredder", + "2": "f_rack_coat", + "3": "f_server", + "4": "f_metal_bench", + "5": "f_console", + "6": "f_console_broken", + "7": "f_shower", + "8": "f_utility_shelf", + "9": "f_utility_shelf", + "a": "f_air_conditioner", + "B": "f_bed", + "b": "f_bookcase", + "c": "f_counter", + "Ć": "f_cupboard", + "Ĉ": "f_cupboard", + "C": "f_chair", + "d": "f_desk", + "D": "f_desk", + "Ď": "f_desk", + "F": "f_filing_cabinet", + "g": "f_fridge", + "h": "f_chair", + "I": "f_dresser", + "l": "f_locker", + "o": "f_oven", + "p": [ "f_indoor_plant", "f_indoor_plant_y" ], + "R": "f_trashcan", + "r": "f_trashcan", + "S": "f_sink", + "s": "f_sink", + "T": "f_counter", + "t": "f_table", + "v": "f_vent_pipe", + "Д": "f_desk", + "д": "f_desk", + "Й": "f_rack_coat" + }, + "toilets": { "&": { } }, + "items": { + "2": { "item": "coat_rack", "chance": 60, "repeat": 2 }, + "8": { "item": "mine_materials", "chance": 50, "repeat": 4 }, + "9": { "item": "car_kit", "chance": 60, "repeat": 2 }, + "B": { "item": "bed", "chance": 50 }, + "b": { "item": "lab_bookshelves", "chance": 60, "repeat": 2 }, + "c": { "item": "office_supplies", "chance": 60 }, + "Ć": [ + { "item": "SUS_silverware", "chance": 80 }, + { "item": "SUS_utensils", "chance": 80 }, + { "item": "SUS_knife_drawer", "chance": 80 }, + { "item": "SUS_dishes", "chance": 80 }, + { "item": "SUS_cookware", "chance": 80 } + ], + "Ĉ": { "item": "SUS_pantry", "chance": 80 }, + "D": { "item": "SUS_junk_drawer_artsy", "chance": 90 }, + "Ď": { "item": "SUS_junk_drawer_messy", "chance": 90 }, + "d": { "item": "SUS_office_desk", "chance": 90 }, + "F": { "item": "SUS_office_filing_cabinet", "chance": 90 }, + "g": { "item": "SUS_fridge", "chance": 80 }, + "I": { "item": "SUS_dresser_mens", "chance": 60 }, + "l": { "item": "SUS_janitors_closet", "chance": 85 }, + "o": { "item": "SUS_oven", "chance": 70 }, + "R": { "item": "trash_cart", "chance": 50 }, + "r": { "item": "trash_cart", "chance": 50 }, + "S": { "item": "SUS_kitchen_sink", "chance": 90 }, + "s": { "item": "SUS_bathroom_sink", "chance": 70 }, + "T": { "item": "SUS_appliances_cupboard", "chance": 10 }, + "t": { "item": "dining", "chance": 45 }, + "Д": { "item": "SUS_junk_drawer_handy", "chance": 90 }, + "д": { "item": "SUS_junk_drawer_tidy", "chance": 90 } + }, + "monster": { + ",": { "monster": "mon_zombie_miner", "chance": 5 }, + ".": { "monster": "mon_zombie_miner", "chance": 5 }, + "`": { "monster": "mon_zombie_miner", "chance": 5 }, + " ": { "monster": "mon_zombie_miner", "chance": 5 }, + "~": { "monster": "mon_zombie_miner", "chance": 5 } + }, + "computers": { + "5": { + "name": "NEPowerOS", + "security": 2, + "options": [ { "name": "Divert power to elevator", "action": "elevator_on" } ], + "failures": [ { "action": "alarm" } ] + } + }, + "gaspumps": { "@": { "fuel": "gasoline", "amount": [ 10000, 50000 ] }, "$": { "fuel": "diesel", "amount": [ 10000, 50000 ] } }, + "vehicles": { "Ø": { "vehicle": "tatra_truck", "chance": 50, "fuel": 40 } }, + "nested": { "`": { "chunks": [ [ "mechanical_fluid", 10 ], [ "gasoline_diesel_motor_oil", 80 ], [ "null", 80 ] ] } } + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ [ "mine_entrance_roof", "mine_entrance_loading_zone_roof" ] ], + "object": { + "rows": [ + " ", + " ", + " ", + "Ю--------------------| ", + "|....................| ", + "|....................| ", + "|....................| ", + "|....................| ", + "|....................| ", + "|--------------------| ", + " ", + "|-----------| ", + "|...........| |------------| ", + "|...........| |............| ", + "|...........| |............| ", + "|...........| |............| ", + "|...........| |............| ", + "|...........| |............| |----| ", + "|...........| |............| |....| ", + "|...........| |............| |....| ", + "|...........| |............| |....| ", + "|...........| |............| |....| ", + "|...........| |............| |....| ", + "|-----------Ю Ю------------Ю Ю----| " + ], + "terrain": { " ": "t_open_air", "|": "t_gutter_north", "-": "t_gutter_west", "Ю": "t_gutter_drop", ".": "t_flat_roof" } + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "mechanical_fluid", + "object": { + "mapgensize": [ 1, 1 ], + "place_fields": [ { "field": "fd_mechanical_fluid", "x": 0, "y": 0, "intensity": 1, "age": 10 } ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "gasoline_diesel_motor_oil", + "object": { + "mapgensize": [ 1, 1 ], + "place_liquids": [ + { "liquid": "gasoline", "x": 0, "y": 0, "chance": 20 }, + { "liquid": "diesel", "x": 0, "y": 0, "chance": 20 }, + { "liquid": "motor_oil", "x": 0, "y": 0, "chance": 20 } + ] + } + } +] diff --git a/data/json/mapgen/mine/mine_shaft.json b/data/json/mapgen/mine/mine_shaft.json new file mode 100644 index 0000000000000..8fee0022eb3de --- /dev/null +++ b/data/json/mapgen/mine/mine_shaft.json @@ -0,0 +1,94 @@ +[ + { + "type": "mapgen", + "method": "json", + "om_terrain": "mine_shaft_middle", + "object": { + "fill_ter": "t_rock", + "rows": [ + " <#", + " #", + " #", + " #", + " #", + " #", + " #", + " #", + " #", + " #", + " #", + " #", + " #>#", + " ....###", + " .... ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ], + "terrain": { "<": "t_ladder_up", ">": "t_ladder_down", "#": "t_grate", ".": "t_hole" } + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ [ "mine_shaft_lower", "mine_shaft_lower_east" ] ], + "object": { + "fill_ter": "t_rock_floor", + "rows": [ + "###### ## #################### #######", + "###### ## ################### ########", + "######№ ## №############# #### #########", + "####### ############### ### ##########", + "######## ############### ## ###########", + "######## ############## ############", + " ###### ################ ##########", + " ##№ ################# ###########", + "# №##### №###### ###########", + " #### ## ", + "###### ### #### #### ", + "###### |-| #### ##### ", + "###### |----|<|--| ", + "##### |!@@...!@@| #### #####", + "###№ |.@@....@@| ######### ######", + " +.......@@| ########## ######", + "*****************=========| # ######## ####", + " +.......®®| №####### ####", + " |.........| ######### ####", + "# |.........| ######### #", + "## |LLLL|SSSS| №######### #", + "####№ |----|----| ######## #", + "###### ######### ", + "####### ## №####### ######## " + ], + "terrain": { + "<": "t_ladder_up", + "-": "t_wall", + "|": "t_wall", + ".": "t_thconc_floor", + "+": "t_door_metal_c", + "!": "t_elevator_control", + "@": "t_elevator", + "#": [ [ "t_rock", 4 ], [ "t_rock_floor", 1 ] ], + "=": "t_conveyor", + "*": "t_railroad_track_small", + "®": "t_thconc_floor", + "№": "t_pillar", + "L": "t_thconc_floor", + "S": "t_thconc_floor" + }, + "furniture": { "®": "f_machinery_heavy", "L": "f_locker", "S": "f_utility_shelf" }, + "items": { + "L": [ { "item": "clothing_work_set", "chance": 50 }, { "item": "hardware_clothing", "chance": 50 } ], + "S": { "item": "mine_equipment", "chance": 80 } + }, + "place_vehicles": [ { "vehicle": "trolley", "x": 10, "y": 16, "chance": 100, "status": 0 } ], + "monsters": { " ": { "monster": "GROUP_MINER", "chance": 1, "density": 0.0005 } } + } + } +] diff --git a/data/json/mapgen/mine/mine_spiral.json b/data/json/mapgen/mine/mine_spiral.json new file mode 100644 index 0000000000000..e69daee9f44c2 --- /dev/null +++ b/data/json/mapgen/mine/mine_spiral.json @@ -0,0 +1,190 @@ +[ + { + "type": "monstergroup", + "name": "GROUP_MINER", + "default": "mon_zombie_miner", + "monsters": [ ] + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ [ "mine_spiral_west", "mine_spiral_central", "mine_spiral_east" ] ], + "object": { + "fill_ter": "t_rock_floor", + "rows": [ + "########################################################################", + "@@@@@@@@@@@@@@@@@@@@@@@@@###############################################", + "@ @###############################################", + "@ ##################### @###############################################", + "@ #@@@@@@@@@@@@@@@@@@@# @###############################################", + "@ #@ @# @###############################################", + "@ #@ ############### @# @###############################################", + "@ #@ #@@@@@@@@@@@### @# @###############################################", + "@ #@ #@ ># @# @###############################################", + "@ #@ #@ @@@@@@@@@### @# @###############################################", + "@ #@ #@ ############ @# @###############################################", + "@ #@ #@ @# @###############################################", + "@ #@ #@@@@@@@@@@@@@@@@# @#####################@@@@@@@@@@@@@@@@@@@@@@@@@@", + "@ #@ ################## @#####################! @@@@@@@@@@@!@@@@@@@@@@ !", + "@ #@ @##################### ", + "@ #@@@@@@@@@@@@@@@@########################### ", + "@ ############################################**************************", + "@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@## #### ", + "@ # ## ", + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##### ! @@@@@@@@@@!@@@@@@@@@@ !", + "###############################################@@@@@@@@@@@@@@@@@@@@@@@@@", + "########################################################################", + "########################################################################", + "########################################################################" + ], + "terrain": { + "@": [ [ "t_rock", 4 ], [ "t_rock_floor", 1 ] ], + "#": "t_rock", + "*": "t_railroad_track_small", + "!": "t_pillar", + ">": "t_slope_down" + }, + "items": { " ": { "item": "mine_equipment", "chance": 1 } }, + "monsters": { " ": { "monster": "GROUP_MINER", "chance": 1, "density": 0.001 } } + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ + [ "mine_spiral_-1_nw", "mine_spiral_-1_n", "mine_spiral_-1_ne" ], + [ "mine_spiral_-1_sw", "mine_spiral_-1_s", "mine_spiral_-1_se" ] + ], + "object": { + "fill_ter": "t_rock_floor", + "rows": [ + "####################################@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", + "####################################@ @", + "########@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1 1 ###### @", + "########@ # @", + "########@ @@@@@@@@@@@@@@@@@@@@@@@@@@@ # #### @", + "########@ @##################@######@ # #~ # @", + "########@ @# @######@ # # @", + "########@ @# @@@@@@@@@@@@@@@ @######@ ###### @", + "########@ @# @#############@ @######@ @", + "########@ @# @####< #@ @######@ @", + "########@ @# @########### #@ @######@ @", + "########@ @# @@@@@@@@@@@@ #@ @######@ 1 1 1 @", + "########@ @# #@ @######@ @", + "########@ @################@ @######@ @", + "########@ @@@@@@@@@@@@@@@@@@ @######@ @", + "########@ @######@ @", + "########@@@@@@@@@@@@@@@@@@@@@@######@ @", + "####################################@ @", + "####################################@@@@@@@@@@@@@@@@@@@@@@@ @", + "##########################################################@ @", + "##########################################################@ @", + "#####@@@@@@@@@@@@@@@@@@####@@@@@@@@@@@@@@@@@@@############@ @", + "##### #### @############@ @", + "##### #### #### #### ############@ @", + "#####%%%%%%######%%%%%%####!!!!!!######$$$$$$ ############@ @", + "#####% ###### %####! ##### $ ############@ @", + "#####% %%%%######%%%% %####! !!!!##### $$$$ $ ############@ @", + "#####% % %######% % %####! ! !##### $ >$ $ ############@ @", + "#####% %######% %####! !##### $ $ ############@ @", + "#####%%%%%%######%%%%%%####!!!!!!##### $$$$$$ ############@ @", + "#### ###### ## ##### ############@ @", + "### ###### @####@ @############@ @", + "#@ @@@@@@@######@@@@@@@@@@@@@@@@@####@@@@@@@@############@ @", + "#@ @#####################################################@ @", + "#@ @####################################################### @", + "#@ @@@@@@@######@@@@@@@@@@@@@@@@######@@@@@@@@@@@@@@@@@@@### @", + "### ###### ###### ## @", + "#### ###### ## ###### ### #### ##", + "##### ###### #### ###### ##### ###### ###", + "#####^^^^^^######^^^^^^####&&&&&&######&&&&&&######******######******###", + "#####^ ###### ^####& ###### &######* ###### *###", + "#####^ ^^^^######^^^^ ^####& &&&&######&&&& &######* ****######**** *###", + "#####^ ^ ^######^ ^ ^####& & &######& & &######* * *######* * *###", + "#####^ ^######^ ^####& &######& &######* *######* *###", + "#####^^^^^^######^^^^^^####&&&&&&######&&&&&&######******######******###", + "##### #### #### #### ###### ###### ###", + "##### @##@ @####@ ###", + "#####@@@@@@@@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@####@@@@@@@@@@@@@@@@@@@###" + ], + "terrain": { + "@": [ [ "t_rock", 4 ], [ "t_rock_floor", 1 ] ], + "#": "t_rock", + ">": "t_slope_down", + "<": "t_slope_up", + "$": "t_lava" + }, + "furniture": { "&": "f_wreckage", "^": "f_rubble", "%": "f_rubble_rock", "!": "f_rubble_landfill" }, + "nested": { + "1": { "chunks": [ [ "spiral_cw", 25 ], [ "spiral_ccw", 25 ], [ "spiral_boulder_cw", 25 ], [ "spiral_boulder_ccw", 25 ] ] } + }, + "item": { "*": { "item": "rock", "chance": 100 } }, + "npcs": { "~": { "class": "spiral_madman" } } + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ + [ "mine_spiral_finale_nw", "mine_spiral_finale_n", "mine_spiral_finale_ne" ], + [ "mine_spiral_finale_sw", "mine_spiral_finale_s", "mine_spiral_finale_se" ] + ], + "object": { + "fill_ter": "t_rock_floor", + "rows": [ + "###### 1 1 1 1 1 1 1 1 ", + "# ", + "# #### ", + "# #! # ", + "# # ", + "###### ", + " ", + " ###### ", + " # ", + " #### # ", + " # !# # ", + " # # ", + " ###### ", + " ", + "###### 1 1 1 1 1 1 ###### ", + "# # ", + "# #### #### # ", + "# #! # # # # ", + "# # # # ", + "###### ###### ", + " ", + "###### ###### ###### ", + "# # # ", + "# #### #### # #### # ", + "# # # # !# # # # # ", + "# # # # # # ", + "###### ###### ###### ", + " < ", + "###### 1 ###### 1 1 1 ###### ", + "# # # ", + "# #### # #### #### # ", + "# #! # # #! # # # # ", + "# # # # # # ", + "###### ###### ###### ", + " ", + "###### ###### ", + "# # ", + "# #### #### # ", + "# # # # !# # ", + "# # # # ", + "###### ###### ", + " ", + "###### 1 1 1 1 1 1 1 1 ", + "# ", + "# #### ", + "# #! # ", + "# # ", + "###### " + ], + "terrain": { "@": [ [ "t_rock", 4 ], [ "t_rock_floor", 1 ] ], "#": "t_rock", "<": "t_slope_up" }, + "monsters": { " ": { "monster": "GROUP_SPIRAL", "chance": 1, "density": 0.001 } }, + "nested": { "1": { "chunks": [ [ "spiral_cw", 50 ], [ "spiral_ccw", 50 ] ] } }, + "items": { "!": { "item": "spiral", "chance": 60 } } + } + } +] diff --git a/data/json/mapgen/nested/lodge_nested.json b/data/json/mapgen/nested/lodge_nested.json new file mode 100644 index 0000000000000..7f89e90c344b3 --- /dev/null +++ b/data/json/mapgen/nested/lodge_nested.json @@ -0,0 +1,228 @@ +[ + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "lodge_cannibal_15x15", + "object": { + "mapgensize": [ 15, 15 ], + "rows": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + "DF ARF LRL ORa ", + "R ", + "SF DRA LRa ORO ", + "DF ARF LRL LRa ", + "R ", + "SF DRA LRa LRL ", + " " + ], + "palettes": [ "lodge_items" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "lodge_hunting_15x15", + "object": { + "mapgensize": [ 15, 15 ], + "rows": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + "DF ARF BRB BRa ", + "R ", + "SF DRA BRa BRB ", + " " + ], + "palettes": [ "lodge_items" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "lodge_pantry_15x15", + "object": { + "mapgensize": [ 15, 15 ], + "rows": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + "SS SRa SRS SRS ", + "R ", + "SS SRA BRB SRS ", + " " + ], + "palettes": [ "lodge_items" ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "lodge_game_6x6", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "E CC T", + " ", + " BB ", + " BB ", + " BB ", + "E T" + ], + "terrain": { + ".": "t_floor", + "B": "t_floor", + "C": "t_floor", + "y": "t_floor", + "T": "t_floor", + "E": "t_floor", + " ": "t_carpet_red", + "h": "t_carpet_red", + "A": "t_carpet_red", + "d": "t_carpet_red", + "f": "t_carpet_red", + "O": "t_carpet_red", + "=": "t_carpet_red" + }, + "furniture": { "A": "f_stool", "B": "f_table", "E": "f_armchair", "C": "f_bookcase", "T": "f_floor_lamp" }, + "items": { "C": [ { "item": "games", "chance": 80, "repeat": [ 3, 6 ] } ] } + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "lodge_dungeon_6x6", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + " tc td", + " t t ", + " tttt ", + " ", + " h ", + "C T" + ], + "terrain": { + ".": "t_floor", + "B": "t_floor", + "C": "t_floor", + "c": "t_floor", + "y": "t_floor", + "T": "t_floor", + "t": "t_reb_cage", + "E": "t_floor", + " ": "t_carpet_red", + "h": "t_carpet_red", + "A": "t_carpet_red", + "d": "t_carpet_red", + "f": "t_carpet_red", + "O": "t_carpet_red", + "=": "t_carpet_red" + }, + "furniture": { + "A": "f_stool", + "B": "f_workbench", + "E": "f_armchair", + "C": "f_rack_wood", + "T": "f_floor_lamp", + "d": "f_mannequin", + "b": "f_bench", + "f": "f_table", + "h": "f_butcher_rack", + "y": [ "f_indoor_plant_y", "f_indoor_plant" ] + }, + "items": { + "C": [ + { "item": "SUS_tailoring_materials", "chance": 80, "repeat": [ 3, 6 ] }, + { "item": "leather_shop_repair", "chance": 40, "repeat": [ 2, 4 ] }, + { "item": "bullwhip", "chance": 100 } + ], + "c": [ + { "item": "bone", "chance": 80, "repeat": [ 4, 8 ] }, + { "item": "pants", "chance": 100 }, + { "item": "shirts", "chance": 100 }, + { "item": "leather_shop_accessories", "chance": 10 } + ] + } + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "lodge_drug_6x6", + "object": { + "mapgensize": [ 6, 6 ], + "rows": [ + "E CC T", + " ", + " BB ", + " BB ", + " BB ", + "E T" + ], + "terrain": { + ".": "t_floor", + "B": "t_floor", + "C": "t_floor", + "y": "t_floor", + "T": "t_floor", + "E": "t_floor", + " ": "t_carpet_red", + "h": "t_carpet_red", + "A": "t_carpet_red", + "d": "t_carpet_red", + "f": "t_carpet_red", + "O": "t_carpet_red", + "=": "t_carpet_red" + }, + "furniture": { + "A": "f_stool", + "B": "f_workbench", + "E": "f_armchair", + "C": "f_rack_wood", + "T": "f_floor_lamp", + "d": "f_mannequin", + "b": "f_bench", + "f": "f_table", + "h": "f_chair", + "y": [ "f_indoor_plant_y", "f_indoor_plant" ] + }, + "items": { + "C": [ + { "item": "straw_doll", "chance": 80, "repeat": [ 6, 14 ] }, + { "item": "bag_body_bag", "chance": 40, "repeat": [ 2, 4 ] } + ], + "B": [ + { "item": "coke", "chance": 60, "repeat": [ 13, 126 ] }, + { "item": "funnel", "chance": 60, "repeat": [ 0, 1 ] }, + { "item": "plastic_bag", "chance": 90, "repeat": [ 20, 60 ] } + ] + } + } + } +] diff --git a/data/json/mapgen/nested/mine_nested.json b/data/json/mapgen/nested/mine_nested.json new file mode 100644 index 0000000000000..ee70e895db90f --- /dev/null +++ b/data/json/mapgen/nested/mine_nested.json @@ -0,0 +1,76 @@ +[ + { + "type": "palette", + "id": "mine_palette", + "terrain": { "#": "t_rock", " ": "t_rock_floor", "$": "t_rock_floor" }, + "furniture": { "$": "f_boulder_large" } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "spiral_cw", + "object": { + "palettes": [ "mine_palette" ], + "mapgensize": [ 6, 6 ], + "rows": [ + "######", + " #", + "#### #", + "# # #", + "# #", + "######" + ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "spiral_ccw", + "object": { + "palettes": [ "mine_palette" ], + "mapgensize": [ 6, 6 ], + "rows": [ + "######", + "# ", + "# ####", + "# # #", + "# #", + "######" + ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "spiral_boulder_cw", + "object": { + "palettes": [ "mine_palette" ], + "mapgensize": [ 6, 6 ], + "rows": [ + "$$$$$$", + " $", + "$$$$ $", + "$ $ $", + "$ $", + "$$$$$$" + ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "spiral_boulder_ccw", + "object": { + "palettes": [ "mine_palette" ], + "mapgensize": [ 6, 6 ], + "rows": [ + "$$$$$$", + "$ ", + "$ $$$$", + "$ $ $", + "$ $", + "$$$$$$" + ] + } + } +] diff --git a/data/json/mapgen/outpost.json b/data/json/mapgen/outpost.json index d4cc2ee7ccddb..9fd42f451c407 100644 --- a/data/json/mapgen/outpost.json +++ b/data/json/mapgen/outpost.json @@ -149,7 +149,7 @@ { "type": "mapgen", "method": "json", - "om_terrain": [ "outpost" ], + "om_terrain": [ "outpost_cross" ], "weight": 100, "object": { "rows": [ diff --git a/data/json/mapgen/radio_tower.json b/data/json/mapgen/radio_tower.json index e53e0595966e8..6915653dcaddf 100644 --- a/data/json/mapgen/radio_tower.json +++ b/data/json/mapgen/radio_tower.json @@ -280,7 +280,13 @@ "terrain": { "R": "t_radio_tower", "a": "t_railing", ",": "t_metal_floor_no_roof", ">": "t_stairs_down" }, "place_nested": [ { - "chunks": [ [ "null", 40 ], [ "radio_tower_2x2_map", 40 ], [ "radio_tower_2x2_holdout", 10 ], [ "radio_tower_2x2_sniper", 10 ] ], + "chunks": [ + [ "null", 40 ], + [ "radio_tower_2x2_map", 40 ], + [ "radio_tower_2x2_holdout", 10 ], + [ "radio_tower_2x2_sniper", 10 ], + [ "radio_tower_wasp", 5 ] + ], "x": 10, "y": 5 } @@ -321,6 +327,18 @@ ] } }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "radio_tower_wasp", + "object": { + "mapgensize": [ 5, 5 ], + "place_furniture": [ { "furn": "f_camp_chair", "x": 1, "y": 0 }, { "furn": "f_makeshift_bed", "x": 1, "y": 1 } ], + "place_loot": [ { "group": "wasp_lair", "x": 1, "y": 1, "repeat": [ 0, 2 ] }, { "group": "human_parts", "x": 1, "y": 1 } ], + "place_monster": [ { "group": "GROUP_WASP_QUEEN", "x": [ 0, 5 ], "y": [ 0, 5 ] } ], + "place_nested": [ { "chunks": [ "wasp_gibs" ], "x": [ 0, 2 ], "y": [ 0, 2 ], "repeat": [ 5, 7 ] } ] + } + }, { "type": "mapgen", "method": "json", diff --git a/data/json/mapgen/railroad/railroad_station.json b/data/json/mapgen/railroad/railroad_station.json index ae692d222af8d..26559f054f17f 100644 --- a/data/json/mapgen/railroad/railroad_station.json +++ b/data/json/mapgen/railroad/railroad_station.json @@ -125,7 +125,11 @@ "7": { "item_group": "vending_food" }, "8": { "item_group": "vending_drink" }, "9": { "item_group": "vending_food" } - } + }, + "place_vehicles": [ + { "vehicle": "train_electrical_loco1300", "x": 5, "y": 90, "chance": 5, "rotation": 90, "status": -1 }, + { "vehicle": "train_electrical_loco1300", "x": 18, "y": 9, "chance": 12, "rotation": 270, "status": 1 } + ] } }, { diff --git a/data/json/mapgen/regional_airport.json b/data/json/mapgen/regional_airport.json index 3d8e211848626..479e7ee48224b 100644 --- a/data/json/mapgen/regional_airport.json +++ b/data/json/mapgen/regional_airport.json @@ -250,7 +250,7 @@ "palettes": [ "airport_palette" ], "place_furniture": [ { "furn": "f_gas_tank", "x": 13, "y": 4 } ], "place_liquids": [ { "liquid": "avgas", "x": 13, "y": 4, "repeat": [ 100, 500 ] } ], - "gaspumps": { "J": { } }, + "gaspumps": { "J": { "fuel": "avgas" } }, "place_monsters": [ { "monster": "GROUP_SMALL_STATION", "x": [ 4, 23 ], "y": [ 5, 23 ], "density": 0.05 } ], "place_loot": [ { "group": "supplies_electronics", "x": [ 19, 22 ], "y": 20, "chance": 60, "repeat": [ 1, 3 ] }, diff --git a/data/json/mapgen/rock_border.json b/data/json/mapgen/rock_border.json new file mode 100644 index 0000000000000..3377ea4d15324 --- /dev/null +++ b/data/json/mapgen/rock_border.json @@ -0,0 +1,36 @@ +[ + { + "type": "mapgen", + "method": "json", + "om_terrain": [ "rock_border" ], + "object": { + "rows": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ], + "terrain": { " ": "t_rock" } + } + } +] diff --git a/data/json/mapgen/sub_station.json b/data/json/mapgen/sub_station.json index 200c1be0667f0..8e345e70aa6ba 100644 --- a/data/json/mapgen/sub_station.json +++ b/data/json/mapgen/sub_station.json @@ -8,7 +8,7 @@ "fill_ter": "t_thconc_floor", "rows": [ "SSSSSSSSSSSSSSSSSSSSSSSS", - "SSSSSSSSSSSSSSSSSSSSSSSS", + "SSSttttttttttttttttttSSS", "SS||-;;-||-;;-||-;;-||SS", "SS| | | |SS", "SS| |SS", diff --git a/data/json/mapgen/zoo.json b/data/json/mapgen/zoo.json index 6cb543524d36a..761cf616a6464 100644 --- a/data/json/mapgen/zoo.json +++ b/data/json/mapgen/zoo.json @@ -17,6 +17,15 @@ { "monster": "mon_zoose", "freq": 50, "cost_multiplier": 1 } ] }, + { + "name": "GROUP_PIGS", + "type": "monstergroup", + "default": "mon_pig", + "monsters": [ + { "monster": "mon_pig", "freq": 50, "cost_multiplier": 1 }, + { "monster": "mon_zombie_pig", "freq": 50, "cost_multiplier": 1 } + ] + }, { "method": "json", "om_terrain": "zoo_0_0", @@ -127,16 +136,17 @@ }, "place_item": [ { "item": "rock", "repeat": 1, "x": 9, "y": 15 }, { "item": "pine_bough", "repeat": 1, "x": 9, "y": 21 } ], "place_items": [ - { "chance": 55, "item": "toy_store", "x": 12, "y": [ 6, 7 ] }, + { "chance": 55, "item": "toy_store", "x": 12, "y": [ 6, 7 ], "repeat": [ 1, 16 ] }, { "chance": 75, "item": "vending_food", "x": 20, "y": 2 }, { "chance": 55, "item": "trash", "x": 13, "y": 15 }, { "chance": 55, "item": "trash", "x": 13, "y": 10 }, - { "chance": 55, "item": "snacks", "x": 10, "y": 11 }, - { "chance": 55, "item": "snacks", "x": [ 7, 8 ], "y": 4 }, - { "chance": 55, "item": "candy_shop", "x": 10, "y": [ 6, 7 ] }, - { "chance": 75, "item": "shirts", "x": 10, "y": 12 }, - { "chance": 75, "item": "shirts", "x": 4, "y": 11 }, - { "chance": 75, "item": "vending_drink", "x": 20, "y": 1 } + { "chance": 55, "item": "snacks", "x": 10, "y": 11, "repeat": [ 1, 16 ] }, + { "chance": 55, "item": "snacks", "x": [ 7, 8 ], "y": 4, "repeat": [ 1, 16 ] }, + { "chance": 55, "item": "candy_shop", "x": 10, "y": [ 6, 7 ], "repeat": [ 1, 16 ] }, + { "chance": 75, "item": "shirts", "x": 10, "y": 12, "repeat": [ 1, 5 ] }, + { "chance": 45, "item": "shirts", "x": 4, "y": 11, "repeat": [ 1, 3 ] }, + { "chance": 75, "item": "vending_drink", "x": 20, "y": 1 }, + { "chance": 25, "item": "waitingroom", "x": 18, "y": [ 16, 22 ], "repeat": [ 1, 3 ] } ], "place_monster": [ { "monster": "mon_tiger", "x": 8, "y": 16 }, @@ -211,16 +221,17 @@ "{": "f_rack" }, "place_items": [ - { "chance": 55, "item": "toy_store", "x": 12, "y": [ 6, 7 ] }, + { "chance": 55, "item": "toy_store", "x": 12, "y": [ 6, 7 ], "repeat": [ 1, 16 ] }, { "chance": 75, "item": "vending_food", "x": 20, "y": 2 }, { "chance": 55, "item": "trash", "x": 13, "y": 15 }, { "chance": 55, "item": "trash", "x": 13, "y": 10 }, - { "chance": 55, "item": "snacks", "x": 10, "y": 11 }, - { "chance": 55, "item": "snacks", "x": [ 7, 8 ], "y": 4 }, - { "chance": 55, "item": "candy_shop", "x": 10, "y": [ 6, 7 ] }, - { "chance": 75, "item": "shirts", "x": 10, "y": 12 }, - { "chance": 75, "item": "shirts", "x": 4, "y": 11 }, - { "chance": 75, "item": "vending_drink", "x": 20, "y": 1 } + { "chance": 55, "item": "snacks", "x": 10, "y": 11, "repeat": [ 1, 16 ] }, + { "chance": 55, "item": "snacks", "x": [ 7, 8 ], "y": 4, "repeat": [ 1, 16 ] }, + { "chance": 55, "item": "candy_shop", "x": 10, "y": [ 6, 7 ], "repeat": [ 1, 16 ] }, + { "chance": 75, "item": "shirts", "x": 10, "y": 12, "repeat": [ 1, 5 ] }, + { "chance": 45, "item": "shirts", "x": 4, "y": 11, "repeat": [ 1, 3 ] }, + { "chance": 75, "item": "vending_drink", "x": 20, "y": 1 }, + { "chance": 25, "item": "waitingroom", "x": 18, "y": [ 16, 22 ], "repeat": [ 1, 3 ] } ], "place_item": [ { "item": "rock", "repeat": 1, "x": 9, "y": 15 }, @@ -229,7 +240,7 @@ { "item": "glass_shard", "repeat": [ 42, 84 ], "x": 11, "y": [ 21, 23 ] } ], "place_monster": [ { "monster": "mon_coyote", "x": 8, "y": 16 }, { "monster": "mon_coyote", "x": 9, "y": 17 } ], - "place_monsters": [ { "monster": "GROUP_BEARS", "x": 8, "y": 20 } ] + "place_monsters": [ { "monster": "GROUP_BEARS", "x": 9, "y": 23 } ] } }, { @@ -290,7 +301,7 @@ "-#_|..|...r...b..|||__ss", "-__|..a...r...b..r.|__ss", "-7_|..|...r......r.|__ss", - "-__|..|...r......|||__ss", + "-__|{.|...r......|||__ss", "-_#|c.|..h|rr|...r.|__ss", "B__|..|..h|..|...r.|__ss", "___|..|||||||||+||||____", @@ -340,8 +351,10 @@ ], "place_items": [ { "chance": 45, "item": "trash", "x": 20, "y": 5 }, - { "chance": 65, "item": "vet_softdrug", "x": 10, "y": 18 }, - { "chance": 45, "item": "vet_hardrug", "x": 9, "y": 18 } + { "chance": 65, "item": "vet_softdrug", "x": 10, "y": 18, "repeat": [ 2, 5 ] }, + { "chance": 45, "item": "vet_hardrug", "x": 9, "y": 18, "repeat": [ 2, 5 ] }, + { "chance": 95, "item": "SUS_janitors_closet", "x": 4, "y": 14 }, + { "chance": 25, "item": "waitingroom", "x": [ 16, 17 ], "y": 7, "repeat": [ 1, 3 ] } ], "place_monster": [ { "monster": "mon_bobcat", "x": 8, "y": 5 }, @@ -519,7 +532,10 @@ { "chance": 65, "item": "vending_food", "x": 12, "y": 1 }, { "chance": 55, "item": "trash", "x": 8, "y": 19 }, { "chance": 55, "item": "trash", "x": 12, "y": 6 }, - { "chance": 55, "item": "trash", "x": 10, "y": 2 } + { "chance": 55, "item": "trash", "x": 10, "y": 2 }, + { "chance": 15, "item": "waitingroom", "x": [ 5, 6 ], "y": 19 }, + { "chance": 15, "item": "waitingroom", "x": [ 9, 10 ], "y": 19 }, + { "chance": 15, "item": "waitingroom", "x": [ 15, 16 ], "y": 19 } ], "place_monster": [ { "monster": "mon_deer", "x": 20, "y": 2 }, @@ -614,7 +630,10 @@ { "chance": 65, "item": "vending_food", "x": 12, "y": 1 }, { "chance": 55, "item": "trash", "x": 8, "y": 19 }, { "chance": 55, "item": "trash", "x": 12, "y": 6 }, - { "chance": 55, "item": "trash", "x": 10, "y": 2 } + { "chance": 55, "item": "trash", "x": 10, "y": 2 }, + { "chance": 15, "item": "waitingroom", "x": [ 5, 6 ], "y": 19 }, + { "chance": 15, "item": "waitingroom", "x": [ 9, 10 ], "y": 19 }, + { "chance": 15, "item": "waitingroom", "x": [ 15, 16 ], "y": 19 } ], "place_item": [ { "item": "stick", "repeat": 1, "x": 19, "y": 1 }, @@ -700,7 +719,7 @@ "|||...............-...^|", "-......|-------|..-..^^|", "-......-h....^.-..+...^|", - "-.####&-h..^#..+..|....|", + "-.####&-h..^#..+.L|....|", "||||||||||||||||||||||||", "|i.ctc..i|............&|", "+........C............C|", @@ -734,20 +753,32 @@ "o": "f_oven", "s": "f_sink", "t": "f_table", - "{": "f_fridge" + "{": "f_fridge", + "L": "f_locker" }, "place_items": [ - { "chance": 65, "item": "cannedfood", "x": 22, "y": 21 }, - { "chance": 65, "item": "cannedfood", "x": 22, "y": 19 }, - { "chance": 55, "item": "bowling_food", "x": 5, "y": 21 }, - { "chance": 55, "item": "bowling_food", "x": 15, "y": 16 }, - { "chance": 55, "item": "bowling_food", "x": 9, "y": 15 }, + { "chance": 25, "item": "restaur_table", "x": 4, "y": 14, "repeat": [ 1, 2 ] }, + { "chance": 25, "item": "restaur_table", "x": 5, "y": [ 20, 21 ], "repeat": [ 1, 2 ] }, + { "chance": 25, "item": "restaur_table", "x": 9, "y": [ 20, 21 ], "repeat": [ 1, 2 ] }, + { "chance": 25, "item": "restaur_table", "x": 13, "y": 21, "repeat": [ 1, 2 ] }, + { "chance": 15, "item": "restaur_table", "x": 9, "y": [ 15, 16 ], "repeat": [ 1, 2 ] }, + { "chance": 55, "item": "restaur_kitchen", "x": 22, "y": [ 15, 16 ], "repeat": [ 1, 8 ] }, + { "chance": 55, "item": "restaur_kitchen", "x": 22, "y": [ 19, 21 ], "repeat": [ 1, 8 ] }, + { "chance": 75, "item": "restaur_sink", "x": 22, "y": [ 17, 18 ], "repeat": [ 2, 3 ] }, + { "chance": 55, "item": "SUS_oven", "x": [ 15, 17 ], "y": 16, "repeat": [ 2, 3 ] }, { "chance": 55, "item": "trash", "x": 10, "y": 18 }, { "chance": 55, "item": "trash", "x": 22, "y": 14 }, { "chance": 55, "item": "trash", "x": 6, "y": 12 }, { "chance": 55, "item": "trash", "x": 6, "y": 3 }, - { "chance": 55, "item": "fridgesnacks", "x": [ 15, 16 ], "y": 21 }, - { "chance": 35, "item": "fridge", "x": [ 18, 19 ], "y": 21 } + { "chance": 50, "item": "behindcounter", "x": 17, "y": 21, "repeat": [ 1, 6 ] }, + { "chance": 80, "item": "restaur_fridge", "x": [ 15, 16 ], "y": 21, "repeat": [ 2, 8 ] }, + { "chance": 80, "item": "restaur_fridge", "x": [ 18, 19 ], "y": 21, "repeat": [ 2, 8 ] } + ], + "place_item": [ + { "chance": 60, "item": "birdfood", "x": 12, "y": [ 3, 4 ], "repeat": [ 3, 6 ] }, + { "chance": 60, "item": "birdfood", "x": [ 9, 11 ], "y": 11, "repeat": [ 3, 6 ] }, + { "chance": 35, "item": "birdfood", "x": 20, "y": [ 3, 10 ], "repeat": [ 4, 8 ] }, + { "chance": 90, "item": "birdfood", "x": 17, "y": 12, "repeat": [ 5, 10 ] } ], "sealed_item": { "^": { "item": { "item": "seed_sugar_beet" }, "furniture": "f_plant_seed" } }, "place_monster": [ @@ -997,7 +1028,7 @@ "____..+.g..........fff|-", "____..+.........g..fff|-", "____r.c...........|fff|-", - "____r.c.....gg....|#ff|-", + "____r.c.....gg....|#f}|-", "____r.c....gggg...|||||-", "____r.c.....gg........|-", "____r.c......g........|-", @@ -1020,13 +1051,17 @@ "r": "t_railing_v", "|": "t_brick_wall" }, - "furniture": { "#": "f_hay", "&": "f_trashcan" }, + "furniture": { "#": "f_hay", "&": "f_trashcan", "}": "f_locker" }, "place_item": [ { "item": "rock", "repeat": 1, "x": 8, "y": 9 }, { "item": "rock", "repeat": 1, "x": 16, "y": 15 }, { "item": "stick", "repeat": 1, "x": 8, "y": 17 }, { "item": "stick", "repeat": 1, "x": 21, "y": 18 }, - { "item": "stick", "repeat": 1, "x": 21, "y": 20 } + { "item": "stick", "repeat": 1, "x": 21, "y": 20 }, + { "chance": 15, "item": "cattlefodder", "x": [ 9, 14 ], "y": [ 7, 17 ], "repeat": [ 8, 12 ] }, + { "chance": 15, "item": "birdfood", "x": [ 9, 14 ], "y": [ 7, 17 ], "repeat": [ 8, 12 ] }, + { "chance": 90, "item": "cattlefodder", "x": 21, "y": 14, "repeat": [ 5, 10 ] }, + { "chance": 90, "item": "birdfood", "x": 21, "y": 14, "repeat": [ 4, 8 ] } ], "place_items": [ { "chance": 55, "item": "trash", "x": 3, "y": 22 }, @@ -1034,21 +1069,16 @@ { "chance": 55, "item": "trash", "x": 0, "y": 6 } ], "place_monster": [ - { "monster": "mon_zombie_pig", "x": 10, "y": 7 }, { "monster": "mon_chicken", "x": 14, "y": 7 }, - { "monster": "mon_zombie_pig", "x": 15, "y": 10 }, { "monster": "mon_duck", "x": 11, "y": 11 }, - { "monster": "mon_zombie_pig", "x": 15, "y": 12 }, - { "monster": "mon_pig", "x": 20, "y": 12 }, { "monster": "mon_sheep", "x": 10, "y": 17 }, { "monster": "mon_rabbit", "x": 15, "y": 17 }, { "monster": "mon_sheep", "x": 19, "y": 17 }, { "monster": "mon_sheep", "x": 9, "y": 19 }, { "monster": "mon_sheep", "x": 12, "y": 19 }, - { "monster": "mon_rabbit", "x": 15, "y": 19 }, - { "monster": "mon_pig", "x": 17, "y": 20 }, - { "monster": "mon_pig", "x": 19, "y": 20 } - ] + { "monster": "mon_rabbit", "x": 18, "y": 19 } + ], + "place_monsters": [ { "monster": "GROUP_PIGS", "x": 18, "y": 11 } ] } }, { diff --git a/data/json/mapgen_palettes/lodge_palette.json b/data/json/mapgen_palettes/lodge_palette.json new file mode 100644 index 0000000000000..dd7b0b86e68a3 --- /dev/null +++ b/data/json/mapgen_palettes/lodge_palette.json @@ -0,0 +1,235 @@ +[ + { + "type": "palette", + "id": "lodge_palette", + "terrain": { + "#": "t_rock_wall", + "+": [ [ "t_door_c", 3 ], "t_door_locked" ], + "=": "t_door_c", + "z": "t_door_metal_locked", + ".": [ [ "t_region_groundcover", 4 ], "t_region_groundcover_forest" ], + "~": [ [ "t_region_groundcover_barren", 3 ], "t_region_groundcover" ], + "*": [ [ "t_region_groundcover", 15 ], "t_region_shrub" ], + "G": "t_sidewalk", + "o": "t_pavement", + "P": "t_water_pump", + "R": "t_floor", + "r": "t_floor", + "S": "t_floor", + "-": "t_window_bars_curtains", + "w": "t_double_pane_glass_with_curtain_open_window_closed", + "W": [ [ "t_double_pane_glass_with_curtain", 2 ], "t_double_pane_glass_with_curtain_open_window_closed" ], + "%": "t_gutter_downspout", + "q": "t_swater_sh", + "Q": "t_water_dp", + "H": "t_chickenwire_gate_c", + "I": "t_chickenwire_fence", + "<": "t_wood_stairs_down", + ">": "t_wood_stairs_up" + }, + "furniture": { + "a": "f_armchair", + "A": "f_bookcase", + "b": "f_bench", + "B": "f_bed", + "C": "f_counter", + "c": "f_chair", + "d": "f_desk", + "D": "f_dresser", + "F": "f_fridge", + "h": "f_bathtub", + "l": "f_stool", + "L": "f_locker", + "O": "f_sofa", + "R": [ "f_rack", "f_utility_shelf" ], + "S": "f_woodstove", + "s": "f_sink", + "t": "f_table", + "T": "f_toilet", + "U": "f_utility_shelf", + "V": "f_oven", + "u": "f_cupboard", + "Y": "f_rack_coat", + "Z": "f_fireplace", + "y": [ "f_indoor_plant_y", "f_indoor_plant" ], + "1": "f_cupboard", + "2": "f_cupboard", + "3": "f_cupboard", + "4": "f_cupboard", + "5": "f_sink", + "&": "f_trashcan", + "0": "f_standing_tank" + }, + "liquids": { "0": { "liquid": "water_clean", "amount": [ 10, 900 ] } }, + "items": { + "a": { "item": "livingroom", "chance": 20 }, + "A": [ + { "item": "homebooks", "chance": 60, "repeat": [ 1, 3 ] }, + { "item": "magazines", "chance": 60, "repeat": [ 1, 8 ] }, + { "item": "manuals", "chance": 30 } + ], + "B": { "item": "bed", "chance": 60 }, + "d": [ { "item": "livingroom", "chance": 40 }, { "item": "office", "chance": 40 } ], + "D": [ + { "item": "SUS_dresser_mens", "chance": 50, "repeat": [ 1, 4 ] }, + { "item": "SUS_dresser_womens", "chance": 50, "repeat": [ 1, 4 ] } + ], + "F": { "item": "SUS_fridge", "chance": 35 }, + "h": { "item": "shower", "chance": 20 }, + "L": [ + { "item": "tools_earthworking", "chance": 30, "repeat": [ 1, 4 ] }, + { "item": "tools_home", "chance": 30, "repeat": [ 1, 4 ] } + ], + "O": { "item": "livingroom", "chance": 20 }, + "S": { "item": "stash_wood", "chance": 60, "repeat": [ 2, 4 ] }, + "&": { "item": "trash", "chance": 20 }, + "R": [ + { "item": "camping", "chance": 50 }, + { "item": "gear_survival", "chance": 30 }, + { "item": "cannedfood", "chance": 50 }, + { "item": "stash_food", "chance": 50 } + ], + "s": { "item": "SUS_bathroom_sink", "chance": 60 }, + "t": { "item": "dining", "chance": 30, "repeat": [ 1, 2 ] }, + "U": { "item": "camping", "chance": 30, "repeat": [ 1, 4 ] }, + "u": { "item": "SUS_pantry", "chance": 50 }, + "V": { "item": "SUS_oven", "chance": 70 }, + "Y": { "item": "coat_rack", "chance": 35, "repeat": [ 1, 4 ] }, + "1": [ { "item": "SUS_dishes", "chance": 35 }, { "item": "SUS_silverware", "chance": 35 } ], + "2": { "item": "SUS_cookware", "chance": 35 }, + "3": [ { "item": "SUS_utensils", "chance": 35 }, { "item": "SUS_knife_drawer", "chance": 35 } ], + "4": [ { "item": "SUS_junk_drawer", "chance": 35 }, { "item": "SUS_spice_collection", "chance": 35 } ], + "5": { "item": "SUS_kitchen_sink", "chance": 35 } + }, + "toilets": { "T": { } } + }, + { + "type": "palette", + "id": "lodge_2ndfloor_palette", + "terrain": { + "#": "t_rock_wall", + "+": [ [ "t_door_c", 3 ], "t_door_locked" ], + "=": "t_door_c", + " ": "t_open_air", + ".": "t_gutter_north", + "~": "t_gutter_south", + "*": "t_gutter_east", + "R": "t_floor", + "p": "t_floor", + "r": "t_rock_floor", + "S": "t_rock_floor", + "-": "t_window_bars_curtains", + "w": "t_double_pane_glass_with_curtain_open_window_closed", + "W": [ [ "t_double_pane_glass_with_curtain", 2 ], "t_double_pane_glass_with_curtain_open_window_closed" ], + "%": "t_gutter_downspout", + "q": "t_swater_sh", + "Q": "t_water_dp", + "H": "t_chickenwire_gate_c", + "I": "t_chickenwire_fence", + "<": "t_wood_stairs_down", + ">": "t_wood_stairs_up" + }, + "furniture": { + "a": "f_armchair", + "A": "f_bookcase", + "b": "f_bench", + "B": "f_bed", + "C": "f_counter", + "c": "f_chair", + "d": "f_desk", + "D": "f_dresser", + "F": "f_fridge", + "h": "f_bathtub", + "l": "f_stool", + "L": "f_locker", + "O": "f_sofa", + "p": "f_pool_table", + "R": [ "f_rack", "f_utility_shelf" ], + "S": "f_woodstove", + "s": "f_sink", + "t": "f_table", + "T": "f_toilet", + "Y": "f_rack_coat", + "Z": "f_fireplace", + "y": [ "f_indoor_plant_y", "f_indoor_plant" ], + "5": "f_sink", + "&": "f_trashcan" + }, + "liquids": { "0": { "liquid": "water_clean", "amount": [ 10, 900 ] } }, + "items": { + "a": { "item": "livingroom", "chance": 20 }, + "A": [ + { "item": "homebooks", "chance": 60, "repeat": [ 1, 3 ] }, + { "item": "magazines", "chance": 60, "repeat": [ 1, 8 ] }, + { "item": "manuals", "chance": 30 } + ], + "B": { "item": "bed", "chance": 60 }, + "d": [ { "item": "livingroom", "chance": 40 }, { "item": "office", "chance": 40 } ], + "D": [ + { "item": "SUS_dresser_mens", "chance": 50, "repeat": [ 1, 4 ] }, + { "item": "SUS_dresser_womens", "chance": 50, "repeat": [ 1, 4 ] } + ], + "F": { "item": "SUS_fridge", "chance": 35 }, + "h": { "item": "shower", "chance": 20 }, + "L": [ + { "item": "tools_earthworking", "chance": 30, "repeat": [ 1, 4 ] }, + { "item": "tools_home", "chance": 30, "repeat": [ 1, 4 ] } + ], + "O": { "item": "livingroom", "chance": 20 }, + "S": { "item": "stash_wood", "chance": 60, "repeat": [ 2, 4 ] }, + "&": { "item": "trash", "chance": 20 }, + "R": [ + { "item": "camping", "chance": 50 }, + { "item": "gear_survival", "chance": 30 }, + { "item": "cannedfood", "chance": 50 }, + { "item": "stash_food", "chance": 50 } + ], + "s": { "item": "SUS_bathroom_sink", "chance": 60 }, + "t": { "item": "dining", "chance": 30, "repeat": [ 1, 2 ] }, + "U": { "item": "camping", "chance": 30, "repeat": [ 1, 4 ] }, + "u": { "item": "SUS_pantry", "chance": 50 }, + "V": { "item": "SUS_oven", "chance": 70 }, + "Y": { "item": "coat_rack", "chance": 35, "repeat": [ 1, 4 ] }, + "1": [ { "item": "SUS_dishes", "chance": 35 }, { "item": "SUS_silverware", "chance": 35 } ], + "2": { "item": "SUS_cookware", "chance": 35 }, + "3": [ { "item": "SUS_utensils", "chance": 35 }, { "item": "SUS_knife_drawer", "chance": 35 } ], + "4": [ { "item": "SUS_junk_drawer", "chance": 35 }, { "item": "SUS_spice_collection", "chance": 35 } ], + "5": { "item": "SUS_kitchen_sink", "chance": 35 } + }, + "toilets": { "T": { } } + }, + { + "type": "palette", + "id": "lodge_items", + "terrain": { + "a": "t_floor", + "A": "t_floor", + "B": "t_floor", + "D": "t_floor", + "F": "t_floor", + "h": "t_floor", + "L": "t_floor", + "O": "t_floor", + "S": "t_floor", + "&": "t_floor", + "R": "t_floor" + }, + "items": { + "a": { "item": "clothing_hunting", "chance": 20, "repeat": [ 1, 6 ] }, + "A": { "item": "tools_hunting", "chance": 60, "repeat": [ 1, 8 ] }, + "B": { "item": "tools_hunting", "chance": 60 }, + "D": { "item": "hunting_lodge_weapons" }, + "F": { "item": "lodge_archery_ammo", "chance": 30, "repeat": [ 1, 4 ] }, + "h": { "item": "cannibal_weapons", "repeat": [ 1, 4 ] }, + "L": { "item": "cannibal_food", "repeat": [ 1, 4 ] }, + "S": { "item": "stash_wood", "chance": 60, "repeat": [ 2, 4 ] }, + "&": { "item": "trash", "chance": 20 }, + "R": [ + { "item": "camping", "chance": 50 }, + { "item": "gear_survival", "chance": 30 }, + { "item": "cannedfood", "chance": 50, "repeat": [ 3, 8 ] }, + { "item": "stash_food", "chance": 50, "repeat": [ 3, 8 ] } + ] + } + } +] diff --git a/data/json/mapgen_palettes/subway.json b/data/json/mapgen_palettes/subway.json index 1502b7ebaad56..7c7ec2270d7f6 100644 --- a/data/json/mapgen_palettes/subway.json +++ b/data/json/mapgen_palettes/subway.json @@ -8,6 +8,7 @@ "-": "t_wall_glass", ";": "t_door_glass_c", "S": "t_sidewalk", + "t": "t_bollard", "*": "t_region_shrub_decorative", ">": "t_stairs_down", "<": "t_stairs_up", diff --git a/data/json/mapgen_palettes/wasp_palette.json b/data/json/mapgen_palettes/wasp_palette.json new file mode 100644 index 0000000000000..e342148865d91 --- /dev/null +++ b/data/json/mapgen_palettes/wasp_palette.json @@ -0,0 +1,144 @@ +[ + { + "type": "palette", + "id": "wasp_palette", + "terrain": { + "x": "t_paper_brood", + "X": "t_paper_hard", + "_": "t_floor_paper_hard", + "*": "t_roof_paper_hard", + ".": "t_floor_paper_noroof", + "8": "t_floor_paper_noroof", + "R": "t_radio_tower", + "a": "t_railing", + ",": "t_metal_floor_no_roof", + "<": "t_stairs_up", + ">": "t_stairs_down", + ":": "t_null", + "#": "t_metal_floor_no_roof" + }, + "monster": { "8": { "group": "GROUP_WASP_QUEEN" } }, + "furniture": { ":": "f_cellphone_booster", "#": "f_small_satelitte_dish" }, + "nested": { "O": { "chunks": [ "wasp_brood" ] } } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "wasp_brood", + "//": "Empty cell", + "weight": 100, + "object": { "mapgensize": [ 1, 1 ], "rows": [ "0" ], "terrain": { "0": "t_open_air" } } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "wasp_brood", + "//": "Egg cell.", + "weight": 200, + "object": { + "mapgensize": [ 1, 1 ], + "rows": [ "0" ], + "terrain": { "0": "t_floor_brood_fake" }, + "item": { "0": { "item": "egg_wasp" } } + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "wasp_brood", + "//": "Hatched larva, unsealed", + "weight": 300, + "object": { + "mapgensize": [ 1, 1 ], + "rows": [ "0" ], + "terrain": { "0": "t_open_air" }, + "monster": { "0": { "monster": "mon_wasp_larva" } } + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "wasp_brood", + "//": "Sealed brood with a pupating wasp.", + "weight": 300, + "object": { + "mapgensize": [ 1, 1 ], + "rows": [ "0" ], + "terrain": { "0": "t_floor_brood_wasp" }, + "monster": { "0": { "monster": "mon_wasp_pupa" } } + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "wasp_brood", + "//": "Just in time for the adult. Lucky you.", + "weight": 100, + "object": { + "mapgensize": [ 1, 1 ], + "rows": [ "0" ], + "terrain": { "0": "t_open_air" }, + "monster": { "0": { "group": "GROUP_WASP_NEST" } } + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "wasp_gibs", + "weight": 100, + "object": { + "mapgensize": [ 1, 1 ], + "place_fields": [ { "field": "fd_blood", "x": 0, "y": 0, "intensity": 1, "repeat": [ 0, 3 ] } ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "wasp_gibs", + "weight": 100, + "object": { + "mapgensize": [ 1, 1 ], + "place_fields": [ { "field": "fd_blood_insect", "x": 0, "y": 0, "intensity": 1, "repeat": [ 0, 3 ] } ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "wasp_gibs", + "weight": 100, + "object": { + "mapgensize": [ 1, 1 ], + "place_fields": [ { "field": "fd_blood_invertebrate", "x": 0, "y": 0, "intensity": 1, "repeat": [ 0, 3 ] } ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "wasp_gibs", + "weight": 80, + "object": { "mapgensize": [ 1, 1 ], "place_loot": [ { "group": "bug_parts", "x": 0, "y": 0, "repeat": [ 0, 3 ], "chance": 50 } ] } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "wasp_gibs", + "weight": 80, + "object": { + "mapgensize": [ 1, 1 ], + "place_loot": [ { "group": "vertebrate_parts", "x": 0, "y": 0, "repeat": [ 0, 3 ], "chance": 50 } ] + } + }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "wasp_gibs", + "weight": 80, + "object": { + "mapgensize": [ 1, 1 ], + "place_loot": [ + { "group": "human_parts", "x": 0, "y": 0, "repeat": [ 0, 3 ], "chance": 50 }, + { "group": "wasp_lair", "x": 0, "y": 0, "repeat": [ 0, 1 ], "chance": 25 } + ] + } + } +] diff --git a/data/json/martialarts.json b/data/json/martialarts.json index 2f157333afe72..8a49e33a015b5 100644 --- a/data/json/martialarts.json +++ b/data/json/martialarts.json @@ -68,7 +68,7 @@ "tec_aikido_dodgethrow", "tec_aikido_blockthrow" ], - "weapons": [ "bagh_nakha", "bio_claws_weapon", "cestus", "knuckle_brass", "knuckle_nail", "knuckle_steel" ] + "weapons": [ "bagh_nakha", "bio_claws_weapon", "cestus", "knuckle_brass", "knuckle_nail", "knuckle_steel", "knuckle_steel_forged" ] }, { "type": "martial_art", @@ -520,6 +520,7 @@ "lucern_hammer", "lucern_hammerfake", "makeshift_halberd", + "makeshift_glaive", "pickaxe", "poleaxe", "primitive_axe" @@ -544,7 +545,7 @@ } ], "techniques": [ "tec_judo_backthrow", "tec_judo_counter", "tec_judo_disarm", "tec_judo_break", "tec_judo_throw" ], - "weapons": [ "bagh_nakha", "bio_claws_weapon", "cestus", "knuckle_brass", "knuckle_nail", "knuckle_steel" ] + "weapons": [ "bagh_nakha", "bio_claws_weapon", "cestus", "knuckle_brass", "knuckle_nail", "knuckle_steel", "knuckle_steel_forged" ] }, { "type": "martial_art", @@ -645,10 +646,8 @@ "hptjhp", "m17", "m1911", - "m1911_MEU", "m1911a1_38super", "m9", - "mk23", "needlepistol", "sw_22", "p226_357sig", @@ -665,7 +664,7 @@ "walther_ppq_9mm", "walther_ppq_40", "walther_ppq_45", - "acr", + "nato_assault_rifle", "acr_300blk", "ak47", "ak74", @@ -674,24 +673,17 @@ "ar15", "ar15_retool_300blk", "fn_fal", - "h&k416a5", "hk417_13", "hk_g3", - "hk_g36", "iwi_tavor_x95_300blk", "m1a", "m110a1", "m14ebr", "m16a4", - "m27iar", - "m38dmr", - "m4a1", "rm51_assault_rifle", "rm88_battle_rifle", "ruger_mini", - "scar_l", "scar_h", - "sig552", "sks", "steyr_aug", "tanto", @@ -1196,6 +1188,7 @@ "machete", "machete_gimmick", "makeshift_halberd", + "makeshift_glaive", "makeshift_knife", "makeshift_machete", "naginata", @@ -1327,6 +1320,7 @@ "naginata_fake", "long_pole", "makeshift_halberd", + "makeshift_glaive", "pike", "pike_inferior", "pike_fake", diff --git a/data/json/materials.json b/data/json/materials.json index 697ac68c4c11f..a826a3a0cef5e 100644 --- a/data/json/materials.json +++ b/data/json/materials.json @@ -126,8 +126,7 @@ "repaired_with": "material_aluminium_ingot", "dmg_adj": [ "dented", "bent", "smashed", "destroyed" ], "bash_dmg_verb": "dented", - "cut_dmg_verb": "scratched", - "compacts_into": [ "material_aluminium_ingot", "aluminum_foil" ] + "cut_dmg_verb": "scratched" }, { "type": "material", @@ -219,8 +218,7 @@ "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", "cut_dmg_verb": "scratched", - "burn_products": [ [ "scrap_bronze", 1 ] ], - "compacts_into": [ "scrap_bronze" ] + "burn_products": [ [ "scrap_bronze", 1 ] ] }, { "type": "material", @@ -365,8 +363,7 @@ "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", "cut_dmg_verb": "scratched", - "burn_products": [ [ "scrap_copper", 1 ] ], - "compacts_into": [ "scrap_copper", "copper" ] + "burn_products": [ [ "scrap_copper", 1 ] ] }, { "type": "material", @@ -610,6 +607,32 @@ { "fuel": 1, "smoke": 5, "burn": 5 } ] }, + { + "type": "material", + "id": "lvl4ballisticglass", + "name": "Ballistic Glass", + "//": "Values are based on thickness being a quarter of an inch.", + "density": 30, + "specific_heat_liquid": 0.2, + "specific_heat_solid": 0.2, + "latent_heat": 100, + "bash_resist": 5, + "cut_resist": 10, + "bullet_resist": 9, + "acid_resist": 10, + "fire_resist": 3, + "elec_resist": 4, + "chip_resist": 0, + "dmg_adj": [ "scratched", "cut", "cracked", "shattered" ], + "bash_dmg_verb": "cracked", + "cut_dmg_verb": "scratched", + "burn_data": [ + { "fuel": 0, "smoke": 0, "burn": 0 }, + { "fuel": 0, "smoke": 0, "burn": 0 }, + { "fuel": 0, "smoke": 0, "burn": 1000, "volume_per_turn": "5000 ml", "//": "More like shattering than melting" } + ], + "burn_products": [ [ "glass_shard", 1 ] ] + }, { "type": "material", "id": "glass", @@ -654,8 +677,7 @@ "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", "cut_dmg_verb": "scratched", - "burn_products": [ [ "gold_small", 1 ] ], - "compacts_into": [ "gold_small" ] + "burn_products": [ [ "gold_small", 1 ] ] }, { "type": "material", @@ -781,6 +803,7 @@ "copy-from": "hydrocarbons", "fuel_data": { "energy": 45.8, + "pump_terrain": "t_avgas_pump", "explosion_data": { "chance_hot": 20, "chance_cold": 1000, "factor": 0.2, "fiery": false, "size_factor": 0.1 } } }, @@ -988,8 +1011,7 @@ "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", "cut_dmg_verb": "scratched", - "burn_products": [ [ "lead", 1 ] ], - "compacts_into": [ "lead" ] + "burn_products": [ [ "lead", 1 ] ] }, { "type": "material", @@ -1325,8 +1347,7 @@ "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", "cut_dmg_verb": "scratched", - "burn_products": [ [ "silver_small", 1 ] ], - "compacts_into": [ "silver_small" ] + "burn_products": [ [ "silver_small", 1 ] ] }, { "type": "material", @@ -1347,8 +1368,7 @@ "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", "cut_dmg_verb": "scratched", - "burn_products": [ [ "platinum_small", 1 ] ], - "compacts_into": [ "platinum_small" ] + "burn_products": [ [ "platinum_small", 1 ] ] }, { "type": "material", @@ -1414,9 +1434,7 @@ "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", "cut_dmg_verb": "scratched", - "burn_products": [ [ "scrap", 1 ] ], - "compact_accepts": [ "budget_steel", "hardsteel", "iron" ], - "compacts_into": [ "sheet_metal", "steel_lump", "steel_chunk", "scrap" ] + "burn_products": [ [ "scrap", 1 ] ] }, { "type": "material", @@ -1505,8 +1523,7 @@ "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", "cut_dmg_verb": "scratched", - "burn_products": [ [ "alloy_sheet", 1 ] ], - "compacts_into": [ "alloy_sheet" ] + "burn_products": [ [ "alloy_sheet", 1 ] ] }, { "type": "material", @@ -1580,8 +1597,7 @@ "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", "cut_dmg_verb": "scratched", - "burn_products": [ [ "tin", 1 ] ], - "compacts_into": [ "tin" ] + "burn_products": [ [ "tin", 1 ] ] }, { "type": "material", @@ -2067,7 +2083,6 @@ "repaired_with": "zinc_metal", "dmg_adj": [ "dented", "bent", "smashed", "shattered" ], "bash_dmg_verb": "dented", - "cut_dmg_verb": "scratched", - "compacts_into": [ "zinc_metal" ] + "cut_dmg_verb": "scratched" } ] diff --git a/data/json/monster_special_attacks/monster_attacks.json b/data/json/monster_special_attacks/monster_attacks.json index e905c34d74979..567c4a8ffd608 100644 --- a/data/json/monster_special_attacks/monster_attacks.json +++ b/data/json/monster_special_attacks/monster_attacks.json @@ -65,6 +65,22 @@ "no_dmg_msg_u": "The %1$s tries to slam into you, but stumbles aside.", "no_dmg_msg_npc": "The %1$s tries to slam into , but fails to." }, + { + "type": "monster_attack", + "attack_type": "melee", + "id": "sting", + "cooldown": 20, + "move_cost": 260, + "damage_max_instance": [ { "damage_type": "stab", "amount": 10 } ], + "body_parts": [ [ "leg_l", 2 ], [ "leg_r", 2 ], [ "head", 1 ], [ "arm_l", 2 ], [ "arm_r", 2 ], [ "torso", 3 ] ], + "effects": [ { "id": "venom_weaken", "duration": 400, "chance": 50 } ], + "hit_dmg_u": "The %1$s stings your %2$s!", + "hit_dmg_npc": "The %1$s stings 's %2$s!", + "miss_msg_u": "The %1$s tries to sting you, but you dodge!", + "miss_msg_npc": "The %1$s tries to sting , but they dodge!", + "no_dmg_msg_u": "The %1$s tries to sting your %2$s, but it fails to penetrate your armor.", + "no_dmg_msg_npc": "The %1$s tries to sting 's %2$s, but it fails to penetrate their armor." + }, { "id": "acid_spray", "type": "GUN", diff --git a/data/json/monsterdrops/feral_humans.json b/data/json/monsterdrops/feral_humans.json index 03e30f201d6c0..df787a4720216 100644 --- a/data/json/monsterdrops/feral_humans.json +++ b/data/json/monsterdrops/feral_humans.json @@ -16,5 +16,71 @@ "subtype": "collection", "id": "feral_humans_death_drops_crowbar", "entries": [ { "item": "crowbar", "prob": 100, "damage": [ 1, 3 ] }, { "group": "default_zombie_clothes", "prob": 100 } ] + }, + { + "type": "item_group", + "subtype": "collection", + "id": "feral_scientists_death_drops_scalpel", + "entries": [ + { "item": "scalpel", "prob": 100, "damage": [ 1, 3 ] }, + { "group": "lab_shoes", "damage": [ 1, 4 ] }, + { "group": "lab_torso", "damage": [ 1, 4 ] }, + { "group": "lab_pants", "damage": [ 1, 4 ] }, + { "group": "underwear", "damage": [ 1, 4 ] }, + { + "collection": [ + { "group": "harddrugs", "prob": 25 }, + { "group": "chem_lab", "prob": 60 }, + { "group": "teleport", "prob": 6 }, + { "group": "goo", "prob": 20 }, + { "group": "cloning_vat", "prob": 1 }, + { "group": "dissection", "prob": 50 }, + { "group": "electronics", "prob": 40 }, + { "group": "bionics", "prob": 10 }, + { "group": "radio", "prob": 15 }, + { "group": "textbooks", "prob": 25 }, + { "group": "autodoc_installation_programs", "prob": 5 } + ] + }, + { "group": "wallets_science", "damage": [ 1, 4 ], "prob": 5 } + ] + }, + { + "id": "feral_security_death_drops_9mm", + "type": "item_group", + "subtype": "collection", + "magazine": 100, + "ammo": 20, + "entries": [ + { "item": "m9", "prob": 100, "damage": [ 2, 4 ] }, + { "group": "cop_gear", "prob": 50, "damage": [ 0, 2 ] }, + { "group": "cop_gloves", "prob": 30, "damage": [ 1, 4 ] }, + { "group": "security_pants", "damage": [ 1, 4 ] }, + { "group": "security_shoes", "prob": 70, "damage": [ 1, 4 ] }, + { "group": "security_torso", "prob": 70, "damage": [ 1, 4 ] }, + { "group": "underwear", "damage": [ 1, 4 ] }, + { "group": "clothing_glasses", "prob": 5 }, + { "group": "clothing_watch", "prob": 5 }, + { "group": "wallets", "damage": [ 1, 4 ] } + ] + }, + { + "id": "feral_security_death_drops_flashlight", + "type": "item_group", + "subtype": "collection", + "magazine": 100, + "ammo": 20, + "entries": [ + { "item": "heavy_flashlight", "prob": 100, "damage": [ 2, 4 ] }, + { "item": "tazer", "prob": 100, "damage": [ 2, 4 ] }, + { "group": "cop_gloves", "prob": 30, "damage": [ 1, 4 ] }, + { "group": "security_pants", "damage": [ 1, 4 ] }, + { "group": "security_shoes", "prob": 70, "damage": [ 1, 4 ] }, + { "group": "security_torso", "prob": 70, "damage": [ 1, 4 ] }, + { "group": "underwear", "damage": [ 1, 4 ] }, + { "group": "clothing_glasses", "prob": 5 }, + { "group": "clothing_watch", "prob": 5 }, + { "group": "wallets", "damage": [ 1, 4 ] } + ] } ] diff --git a/data/json/monsterdrops/zombie_technician.json b/data/json/monsterdrops/zombie_technician.json index 08f86d38970f2..4b460cefd2293 100644 --- a/data/json/monsterdrops/zombie_technician.json +++ b/data/json/monsterdrops/zombie_technician.json @@ -13,5 +13,29 @@ { "group": "supplies_electronics", "prob": 50 }, { "item": "id_industrial", "prob": 20 } ] + }, + { + "id": "mon_zombie_miner_death_drops", + "type": "item_group", + "subtype": "collection", + "entries": [ + { "group": "clothing_work_boots", "damage": [ 1, 4 ] }, + { "group": "clothing_work_glasses", "prob": 60, "damage": [ 1, 4 ] }, + { "group": "clothing_work_gloves", "prob": 75, "damage": [ 1, 4 ] }, + { "group": "clothing_work_mask", "prob": 40, "damage": [ 1, 4 ] }, + { "item": "ear_plugs", "prob": 15, "damage": [ 1, 4 ] }, + { "item": "tool_belt", "prob": 25, "damage": [ 1, 4 ] }, + { + "distribution": [ + { + "collection": [ { "group": "clothing_work_pants", "damage": [ 1, 4 ] }, { "group": "clothing_work_torso", "damage": [ 1, 4 ] } ], + "prob": 75 + }, + { "item": "jumpsuit", "prob": 25, "damage": [ 1, 4 ] } + ] + }, + { "group": "underwear", "damage": [ 1, 4 ] }, + { "item": "miner_hat", "prob": 90, "damage": [ 1, 4 ] } + ] } ] diff --git a/data/json/monstergroups/bugs.json b/data/json/monstergroups/bugs.json index b679a8772e566..8b1225781b6f5 100644 --- a/data/json/monstergroups/bugs.json +++ b/data/json/monstergroups/bugs.json @@ -60,18 +60,170 @@ "monsters": [ ] }, { - "name": "GROUP_WASP", + "name": "GROUP_WASP_NEST", "type": "monstergroup", - "default": "mon_wasp_small", - "monsters": [ { "monster": "mon_wasp_small", "freq": 100, "cost_multiplier": 1, "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] } ] + "default": "mon_null", + "monsters": [ + { + "monster": "mon_wasp_small", + "freq": 500, + "cost_multiplier": 1, + "ends": 180, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp_small", + "freq": 500, + "cost_multiplier": 1, + "starts": 180, + "ends": 540, + "pack_size": [ 2, 4 ], + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp_small_guard", + "freq": 500, + "cost_multiplier": 1, + "ends": 180, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp_small_guard", + "freq": 500, + "cost_multiplier": 1, + "starts": 180, + "ends": 540, + "pack_size": [ 2, 4 ], + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp", + "freq": 500, + "cost_multiplier": 1, + "starts": 180, + "ends": 540, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp", + "freq": 500, + "cost_multiplier": 1, + "starts": 540, + "pack_size": [ 2, 4 ], + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp_guard", + "freq": 500, + "cost_multiplier": 1, + "starts": 180, + "ends": 540, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp_guard", + "freq": 500, + "cost_multiplier": 1, + "starts": 540, + "pack_size": [ 2, 4 ], + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + } + ] + }, + { + "name": "GROUP_WASP_FORAGER", + "type": "monstergroup", + "default": "mon_null", + "monsters": [ + { + "monster": "mon_wasp_small", + "freq": 1000, + "cost_multiplier": 1, + "ends": 180, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp_small", + "freq": 500, + "cost_multiplier": 1, + "starts": 180, + "ends": 540, + "pack_size": [ 2, 4 ], + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp", + "freq": 1000, + "cost_multiplier": 1, + "starts": 360, + "ends": 720, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp", + "freq": 1000, + "cost_multiplier": 1, + "starts": 720, + "pack_size": [ 2, 4 ], + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + } + ] + }, + { + "name": "GROUP_WASP_GUARD", + "type": "monstergroup", + "default": "mon_null", + "monsters": [ + { + "monster": "mon_wasp_small_guard", + "freq": 1000, + "cost_multiplier": 1, + "ends": 180, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp_small_guard", + "freq": 500, + "cost_multiplier": 1, + "starts": 180, + "ends": 540, + "pack_size": [ 2, 4 ], + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp_guard", + "freq": 500, + "cost_multiplier": 1, + "starts": 180, + "ends": 540, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp_guard", + "freq": 500, + "cost_multiplier": 1, + "starts": 540, + "pack_size": [ 2, 4 ], + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + } + ] + }, + { + "name": "GROUP_WASP_QUEEN", + "type": "monstergroup", + "default": "mon_null", + "monsters": [ + { "monster": "mon_wasp_queen", "freq": 1000, "cost_multiplier": 50, "ends": 720 }, + { "monster": "mon_wasp_mega", "freq": 1000, "cost_multiplier": 50, "starts": 360 } + ] }, { - "name": "WASP_UPGRADE", + "name": "GROUP_WASP_PUPA", "type": "monstergroup", "default": "mon_wasp", "monsters": [ - { "monster": "mon_wasp", "freq": 200, "cost_multiplier": 1 }, - { "monster": "mon_dermatik", "freq": 100, "cost_multiplier": 1 } + { "monster": "mon_dermatik", "freq": 50, "cost_multiplier": 1 }, + { "monster": "mon_wasp_guard", "freq": 500, "cost_multiplier": 1 } ] }, { diff --git a/data/json/monstergroups/eggs.json b/data/json/monstergroups/eggs.json index 6a0e75b2774e3..e24ac955d81de 100644 --- a/data/json/monstergroups/eggs.json +++ b/data/json/monstergroups/eggs.json @@ -103,6 +103,12 @@ "default": "mon_centipede_small", "monsters": [ { "monster": "mon_centipede_small", "freq": 1000, "cost_multiplier": 1 } ] }, + { + "name": "GROUP_EGG_WASP", + "type": "monstergroup", + "default": "mon_wasp_larva", + "monsters": [ { "monster": "mon_wasp_larva", "freq": 1000, "cost_multiplier": 1, "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] } ] + }, { "name": "GROUP_EGG_DRAGONFLY", "type": "monstergroup", diff --git a/data/json/monstergroups/fungi.json b/data/json/monstergroups/fungi.json index a506431db6685..baac22efbea7c 100644 --- a/data/json/monstergroups/fungi.json +++ b/data/json/monstergroups/fungi.json @@ -4,8 +4,9 @@ "name": "GROUP_FUNGI", "default": "mon_spore", "monsters": [ - { "monster": "mon_fungaloid", "freq": 300, "cost_multiplier": 0 }, - { "monster": "mon_fungaloid_young", "freq": 100, "cost_multiplier": 0 }, + { "monster": "mon_fungaloid", "freq": 400, "cost_multiplier": 0 }, + { "monster": "mon_fungaloid_shambler", "freq": 200, "cost_multiplier": 0 }, + { "monster": "mon_fungaloid_young", "freq": 200, "cost_multiplier": 0 }, { "monster": "mon_zombie_fungus", "freq": 0, "cost_multiplier": 0 }, { "monster": "mon_boomer_fungus", "freq": 0, "cost_multiplier": 0 }, { "monster": "mon_zombie_child_fungus", "freq": 0, "cost_multiplier": 0 }, @@ -18,8 +19,9 @@ "name": "GROUP_FUNGI_FUNGALOID", "default": "mon_fungaloid", "monsters": [ - { "monster": "mon_fungaloid", "freq": 300, "cost_multiplier": 0 }, - { "monster": "mon_fungaloid_young", "freq": 100, "cost_multiplier": 0, "pack_size": [ 2, 3 ] } + { "monster": "mon_fungaloid", "freq": 500, "cost_multiplier": 0 }, + { "monster": "mon_fungaloid_shambler", "freq": 250, "cost_multiplier": 0 }, + { "monster": "mon_fungaloid_young", "freq": 250, "cost_multiplier": 0, "pack_size": [ 2, 3 ] } ] }, { @@ -28,6 +30,7 @@ "default": "mon_fungal_tendril", "monsters": [ { "monster": "mon_fungaloid", "freq": 10, "cost_multiplier": 0 }, + { "monster": "mon_fungaloid_shambler", "freq": 30, "cost_multiplier": 0 }, { "monster": "mon_fungaloid_young", "freq": 30, "cost_multiplier": 0 }, { "monster": "mon_zombie_fungus", "freq": 75, "cost_multiplier": 0 }, { "monster": "mon_boomer_fungus", "freq": 50, "cost_multiplier": 0 }, @@ -42,6 +45,7 @@ "default": "mon_fungal_blossom", "monsters": [ { "monster": "mon_fungaloid", "freq": 20, "cost_multiplier": 0 }, + { "monster": "mon_fungaloid_shambler", "freq": 50, "cost_multiplier": 0 }, { "monster": "mon_fungaloid_young", "freq": 50, "cost_multiplier": 0 }, { "monster": "mon_zombie_fungus", "freq": 100, "cost_multiplier": 0 }, { "monster": "mon_boomer_fungus", "freq": 50, "cost_multiplier": 0 }, diff --git a/data/json/monstergroups/lab.json b/data/json/monstergroups/lab.json index c86bd401acc88..b67cd2eb03c8d 100644 --- a/data/json/monstergroups/lab.json +++ b/data/json/monstergroups/lab.json @@ -5,6 +5,7 @@ "default": "mon_zombie_scientist", "monsters": [ { "monster": "mon_zombie_soldier", "freq": 25, "cost_multiplier": 0, "pack_size": [ 1, 4 ] }, + { "monster": "mon_feral_scientist_scalpel", "freq": 20, "cost_multiplier": 0 }, { "monster": "mon_manhack", "freq": 200, "cost_multiplier": 0 }, { "monster": "mon_manhack", "freq": 45, "cost_multiplier": 0, "pack_size": [ 2, 3 ] }, { "monster": "mon_skitterbot", "freq": 100, "cost_multiplier": 0 }, @@ -28,6 +29,9 @@ "default": "mon_zombie_scientist", "monsters": [ { "monster": "mon_blob_small", "freq": 25, "cost_multiplier": 0, "pack_size": [ 1, 4 ] }, + { "monster": "mon_feral_labsecurity_9mm", "freq": 18, "cost_multiplier": 8 }, + { "monster": "mon_feral_labsecurity_flashlight", "freq": 18, "cost_multiplier": 2 }, + { "monster": "mon_feral_scientist_scalpel", "freq": 20, "cost_multiplier": 0 }, { "monster": "mon_zombie", "freq": 200, "cost_multiplier": 0, "pack_size": [ 1, 3 ] }, { "monster": "mon_zombie_fat", "freq": 100, "cost_multiplier": 0, "pack_size": [ 1, 3 ] }, { "monster": "mon_zombie_tough", "freq": 100, "cost_multiplier": 2, "pack_size": [ 1, 3 ] }, @@ -61,6 +65,9 @@ "name": "GROUP_LAB_SURFACE", "default": "mon_zombie_scientist", "monsters": [ + { "monster": "mon_feral_labsecurity_9mm", "freq": 18, "cost_multiplier": 8 }, + { "monster": "mon_feral_labsecurity_flashlight", "freq": 18, "cost_multiplier": 2 }, + { "monster": "mon_feral_scientist_scalpel", "freq": 20, "cost_multiplier": 0 }, { "monster": "mon_zombie_labsecurity", "freq": 400, "cost_multiplier": 2, "pack_size": [ 1, 3 ] }, { "monster": "mon_zombie_scientist", "freq": 600, "cost_multiplier": 1 }, { "monster": "mon_zombie_scientist", "freq": 400, "cost_multiplier": 1, "pack_size": [ 1, 5 ] }, @@ -94,9 +101,12 @@ "name": "GROUP_LAB_SECURITY", "default": "mon_zombie_labsecurity", "monsters": [ + { "monster": "mon_feral_labsecurity_9mm", "freq": 22, "cost_multiplier": 8 }, + { "monster": "mon_feral_labsecurity_flashlight", "freq": 22, "cost_multiplier": 2 }, { "monster": "mon_zombie_labsecurity", "freq": 700, "cost_multiplier": 2 }, { "monster": "mon_science_bot", "freq": 50, "cost_multiplier": 4 }, { "monster": "mon_manhack", "freq": 200, "cost_multiplier": 0 }, + { "monster": "mon_tazer_hack", "freq": 150, "cost_multiplier": 0 }, { "monster": "mon_manhack", "freq": 45, "cost_multiplier": 0, "pack_size": [ 2, 3 ] }, { "monster": "mon_skitterbot", "freq": 85, "cost_multiplier": 0 }, { "monster": "mon_skitterbot", "freq": 85, "cost_multiplier": 0, "pack_size": [ 2, 3 ] }, @@ -118,6 +128,9 @@ "default": "mon_zombie_scientist", "//": "Mostly scientists, lab personnel and regular zombies.", "monsters": [ + { "monster": "mon_feral_labsecurity_9mm", "freq": 18, "cost_multiplier": 8 }, + { "monster": "mon_feral_labsecurity_flashlight", "freq": 18, "cost_multiplier": 2 }, + { "monster": "mon_feral_scientist_scalpel", "freq": 20, "cost_multiplier": 8 }, { "monster": "mon_zombie", "freq": 75, "cost_multiplier": 0 }, { "monster": "mon_zombie_fat", "freq": 75, "cost_multiplier": 0 }, { "monster": "mon_zombie_tough", "freq": 75, "cost_multiplier": 2 }, @@ -143,11 +156,15 @@ "default": "mon_zombie_scientist", "monsters": [ { "monster": "mon_blob_small", "freq": 40, "cost_multiplier": 0, "pack_size": [ 3, 6 ] }, + { "monster": "mon_feral_labsecurity_9mm", "freq": 18, "cost_multiplier": 8 }, + { "monster": "mon_feral_labsecurity_flashlight", "freq": 18, "cost_multiplier": 2 }, + { "monster": "mon_feral_scientist_scalpel", "freq": 20, "cost_multiplier": 0 }, { "monster": "mon_zombie_scientist", "freq": 40, "cost_multiplier": 0, "pack_size": [ 1, 5 ] }, { "monster": "mon_science_bot", "freq": 40, "cost_multiplier": 2 }, { "monster": "mon_zombie_labsecurity", "freq": 40, "cost_multiplier": 0, "pack_size": [ 2, 3 ] }, { "monster": "mon_zombie_hazmat", "freq": 40, "cost_multiplier": 1, "pack_size": [ 1, 3 ] }, { "monster": "mon_manhack", "freq": 20, "cost_multiplier": 1, "pack_size": [ 3, 12 ] }, + { "monster": "mon_tazer_hack", "freq": 20, "cost_multiplier": 1, "pack_size": [ 1, 5 ] }, { "monster": "mon_mutant_experimental", "freq": 10, "cost_multiplier": 0, "pack_size": [ 1, 3 ] }, { "monster": "mon_skitterbot", "freq": 10, "cost_multiplier": 0, "pack_size": [ 2, 3 ] }, { "monster": "mon_zombie_phase_skulker", "freq": 10, "cost_multiplier": 0, "pack_size": [ 2, 3 ] }, diff --git a/data/json/monstergroups/misc.json b/data/json/monstergroups/misc.json index f7dcb784e3f2c..1bd7a3f098902 100644 --- a/data/json/monstergroups/misc.json +++ b/data/json/monstergroups/misc.json @@ -132,7 +132,7 @@ { "monster": "mon_rattlesnake", "freq": 10, "cost_multiplier": 1 }, { "monster": "mon_rattlesnake_giant", "freq": 5, "cost_multiplier": 2 }, { "monster": "mon_worm", "freq": 20, "cost_multiplier": 1 }, - { "monster": "mon_wasp", "freq": 10, "cost_multiplier": 1 } + { "monster": "mon_wasp_small", "freq": 10, "cost_multiplier": 1 } ] }, { @@ -151,7 +151,7 @@ { "monster": "mon_spider_widow_giant", "freq": 30, "cost_multiplier": 1, "pack_size": [ 2, 4 ] }, { "monster": "mon_giant_cockroach", "freq": 50, "cost_multiplier": 1, "pack_size": [ 3, 6 ] }, { "monster": "mon_black_rat", "freq": 30, "cost_multiplier": 1, "pack_size": [ 4, 8 ] }, - { "monster": "mon_wasp", "freq": 50, "cost_multiplier": 1, "pack_size": [ 3, 6 ] }, + { "monster": "mon_wasp_small", "freq": 50, "cost_multiplier": 1, "pack_size": [ 2, 4 ] }, { "monster": "mon_zombie_child", "freq": 20, "cost_multiplier": 1, "pack_size": [ 2, 6 ] }, { "monster": "mon_zombie", "freq": 20, "cost_multiplier": 1, "pack_size": [ 2, 6 ] }, { "monster": "mon_zombie_rot", "freq": 10, "cost_multiplier": 1, "pack_size": [ 1, 5 ] }, diff --git a/data/json/monstergroups/obsolete.json b/data/json/monstergroups/obsolete.json index d559e82543a62..d389e490c2fe5 100644 --- a/data/json/monstergroups/obsolete.json +++ b/data/json/monstergroups/obsolete.json @@ -60,5 +60,14 @@ "type": "monstergroup", "default": "mon_null", "monsters": [ { "monster": "mon_zombie_spitter", "freq": 65, "cost_multiplier": 5 } ] + }, + { + "name": "WASP_UPGRADE", + "type": "monstergroup", + "default": "mon_wasp", + "monsters": [ + { "monster": "mon_wasp", "freq": 200, "cost_multiplier": 1 }, + { "monster": "mon_dermatik", "freq": 100, "cost_multiplier": 1 } + ] } ] diff --git a/data/json/monstergroups/wilderness.json b/data/json/monstergroups/wilderness.json index f8e86a648656c..77b8b0009adc0 100644 --- a/data/json/monstergroups/wilderness.json +++ b/data/json/monstergroups/wilderness.json @@ -1731,114 +1731,52 @@ { "monster": "mon_zpider_mass", "freq": 1, "cost_multiplier": 10, "starts": 2160 }, { "monster": "mon_wasp_small", - "freq": 3, - "cost_multiplier": 0, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] - }, - { - "monster": "mon_wasp_small", - "freq": 2, - "cost_multiplier": 0, - "starts": 24, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] - }, - { - "monster": "mon_wasp_small", - "freq": 3, - "cost_multiplier": 0, - "starts": 72, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] - }, - { - "monster": "mon_wasp_small", - "freq": 2, - "cost_multiplier": 0, - "starts": 120, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] + "freq": 20, + "cost_multiplier": 2, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ], + "ends": 168 }, { "monster": "mon_wasp_small", - "freq": 3, - "cost_multiplier": 0, + "freq": 25, + "cost_multiplier": 2, "starts": 168, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] - }, - { - "monster": "mon_wasp_small", - "freq": 2, - "cost_multiplier": 0, - "starts": 216, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] - }, - { - "monster": "mon_wasp_small", - "freq": 3, - "cost_multiplier": 0, - "starts": 288, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] + "ends": 336, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] }, { "monster": "mon_wasp_small", - "freq": 2, - "cost_multiplier": 0, + "freq": 25, + "cost_multiplier": 2, + "pack_size": [ 1, 2 ], "starts": 336, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] - }, - { - "monster": "mon_wasp_small", - "freq": 3, - "cost_multiplier": 0, - "starts": 384, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] - }, - { - "monster": "mon_wasp_small", - "freq": 2, - "cost_multiplier": 0, - "starts": 456, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] - }, - { - "monster": "mon_wasp_small", - "freq": 3, - "cost_multiplier": 0, - "starts": 504, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] - }, - { - "monster": "mon_wasp_small", - "freq": 2, - "cost_multiplier": 0, - "starts": 552, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] - }, - { - "monster": "mon_wasp_small", - "freq": 3, - "cost_multiplier": 0, - "starts": 624, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] + "ends": 720, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] }, { - "monster": "mon_wasp_small", - "freq": 2, - "cost_multiplier": 0, - "starts": 672, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] + "monster": "mon_wasp", + "freq": 30, + "cost_multiplier": 2, + "starts": 720, + "ends": 1440, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] }, { - "monster": "mon_wasp_small", - "freq": 3, - "cost_multiplier": 0, - "starts": 720, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] + "monster": "mon_wasp", + "freq": 30, + "cost_multiplier": 2, + "pack_size": [ 1, 2 ], + "starts": 1440, + "ends": 2160, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] }, { - "monster": "mon_wasp_small", - "freq": 2, - "cost_multiplier": 0, - "starts": 792, - "conditions": [ "DAY", "SPRING", "SUMMER", "AUTUMN" ] + "monster": "mon_wasp", + "freq": 30, + "cost_multiplier": 2, + "pack_size": [ 1, 3 ], + "starts": 2160, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] }, { "monster": "mon_worm_small", "freq": 1, "cost_multiplier": 0, "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] }, { @@ -2664,9 +2602,18 @@ "monster": "mon_wasp_small", "freq": 25, "cost_multiplier": 2, - "pack_size": [ 1, 3 ], - "starts": 84, - "ends": 180, + "pack_size": [ 1, 2 ], + "starts": 336, + "ends": 720, + "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] + }, + { + "monster": "mon_wasp", + "freq": 30, + "cost_multiplier": 2, + "pack_size": [ 1, 2 ], + "starts": 720, + "ends": 1440, "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] }, { @@ -2674,16 +2621,16 @@ "freq": 30, "cost_multiplier": 2, "pack_size": [ 1, 3 ], - "starts": 180, - "ends": 360, + "starts": 1440, + "ends": 2160, "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] }, { "monster": "mon_wasp", "freq": 30, "cost_multiplier": 2, - "pack_size": [ 2, 4 ], - "starts": 360, + "pack_size": [ 2, 5 ], + "starts": 2160, "conditions": [ "SPRING", "SUMMER", "AUTUMN" ] }, { diff --git a/data/json/monstergroups/zanimal_upgrades.json b/data/json/monstergroups/zanimal_upgrades.json index f43793c5b2942..d6910ccb8e695 100644 --- a/data/json/monstergroups/zanimal_upgrades.json +++ b/data/json/monstergroups/zanimal_upgrades.json @@ -10,6 +10,17 @@ { "monster": "mon_zombie_dog_acidic", "freq": 330, "cost_multiplier": 2 } ] }, + { + "type": "monstergroup", + "name": "GROUP_ZOMBIE_PIG_UPGRADE", + "default": "mon_zombie_pig", + "//": "No bionics or fungal", + "monsters": [ + { "monster": "mon_zombie_pig", "freq": 45, "cost_multiplier": 5 }, + { "monster": "mon_zpig_brute", "freq": 45, "cost_multiplier": 2 }, + { "monster": "mon_zombie_pig_gas", "freq": 45, "cost_multiplier": 2 } + ] + }, { "type": "monstergroup", "name": "GROUP_ZOLF_UPGRADE", diff --git a/data/json/monsters/drones.json b/data/json/monsters/drones.json index 4147a56358957..550fa09c3f8a3 100644 --- a/data/json/monsters/drones.json +++ b/data/json/monsters/drones.json @@ -185,5 +185,19 @@ "PRIORITIZE_TARGETS", "HIT_AND_RUN" ] + }, + { + "id": "mon_tazer_hack", + "copy-from": "base_drone", + "type": "MONSTER", + "name": { "str": "tazer hack" }, + "description": "An automated drone, this small quadcopter robot appears to have a tazer strapped to the front. While extremely fast, it is very fragile.", + "diff": 10, + "speed": 350, + "color": "cyan", + "armor_cut": 2, + "armor_bullet": 1, + "revert_to_itype": "bot_tazer_hack", + "special_attacks": [ [ "TAZER", 5 ] ] } ] diff --git a/data/json/monsters/feral_humans.json b/data/json/monsters/feral_humans.json index 5da68311d39a5..3d3829915f477 100644 --- a/data/json/monsters/feral_humans.json +++ b/data/json/monsters/feral_humans.json @@ -3,7 +3,7 @@ "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.", + "description": "Their pupils are dilated and what remains to be seen of the iris and sclera are bloodshot. This pipe-wielding maniac still breathes, but the zombies treat them like one of their own.", "default_faction": "zombie", "looks_like": "chud", "bodytype": "human", @@ -17,7 +17,7 @@ "color": "magenta", "aggression": 30, "morale": 100, - "melee_skill": 2, + "melee_skill": 3, "melee_dice": 1, "melee_dice_sides": 3, "melee_cut": 0, @@ -63,6 +63,7 @@ { "id": "mon_feral_human_crowbar", "type": "MONSTER", + "description": "Their pupils are dilated and what remains to be seen of the iris and sclera are bloodshot. This crowbar-wielding maniac still breathes, but the zombies treat them like one of their own.", "copy-from": "mon_feral_human_pipe", "melee_dice": 2, "melee_dice_sides": 6, @@ -71,9 +72,132 @@ { "id": "mon_feral_human_axe", "type": "MONSTER", + "description": "Their pupils are dilated and what remains to be seen of the iris and sclera are bloodshot. This axe-wielding maniac still breathes, but the zombies treat them like one of their own.", "copy-from": "mon_feral_human_pipe", - "melee_dice": 3, - "melee_dice_sides": 8, + "melee_dice": 2, + "melee_dice_sides": 7, + "melee_cut": 9, "death_drops": "feral_humans_death_drops_axe" + }, + { + "id": "mon_feral_scientist_scalpel", + "type": "MONSTER", + "name": { "str": "mad scientist" }, + "description": "A researcher who has stared too long into the abyss, and now shambles about muttering nonsense under their breath. For some reason, the zombies don't seem to mind that this person is obviously still alive. They're clutching a tiny but razor-sharp blade in one shaky hand.", + "default_faction": "science", + "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": 4, + "melee_dice": 1, + "melee_dice_sides": 3, + "melee_cut": 2, + "dodge": 1, + "harvest": "CBM_SCI_FERAL", + "path_settings": { "max_dist": 10 }, + "vision_day": 50, + "vision_night": 3, + "death_drops": "feral_scientists_death_drops_scalpel", + "death_function": [ "NORMAL" ], + "zombify_into": "mon_zombie_scientist", + "anger_triggers": [ "FRIEND_DIED", "FRIEND_ATTACKED", "HURT", "PLAYER_CLOSE" ], + "flags": [ + "SEES", + "HEARS", + "SMELLS", + "WARM", + "BASHES", + "GROUP_BASH", + "HUMAN", + "CAN_OPEN_DOORS", + "PATH_AVOID_DANGER_2", + "DROPS_AMMO", + "CBM_SCI" + ] + }, + { + "id": "mon_feral_labsecurity_9mm", + "type": "MONSTER", + "name": { "str": "feral security guard" }, + "description": "This security guard still breathes, but has been lost to some terrible madness. They move among the undead, handgun at the ready and saucer pupils scanning for threats in a mockery of their former duty.", + "default_faction": "science", + "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": 6, + "melee_dice": 2, + "melee_dice_sides": 3, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 4, + "armor_stab": 4, + "dodge": 1, + "harvest": "human", + "vision_day": 50, + "vision_night": 4, + "path_settings": { "max_dist": 10 }, + "diff": 5, + "starting_ammo": { "9mm": 5 }, + "special_attacks": [ + { + "type": "gun", + "cooldown": 10, + "move_cost": 150, + "gun_type": "m9", + "ammo_type": "9mm", + "fake_skills": [ [ "gun", 1 ], [ "handgun", 2 ] ], + "fake_dex": 8, + "fake_per": 10, + "ranges": [ [ 0, 14, "DEFAULT" ] ], + "require_targeting_player": false, + "description": "The feral security guard fires their Beretta M9A1!" + } + ], + "death_drops": "feral_security_death_drops_9mm", + "death_function": [ "NORMAL" ], + "zombify_into": "mon_zombie_labsecurity", + "anger_triggers": [ "FRIEND_DIED", "FRIEND_ATTACKED", "HURT", "PLAYER_WEAK" ], + "flags": [ + "SEES", + "HEARS", + "SMELLS", + "WARM", + "BASHES", + "GROUP_BASH", + "HUMAN", + "CAN_OPEN_DOORS", + "CLIMBS", + "PUSH_MON", + "PATH_AVOID_DANGER_2", + "DROPS_AMMO" + ] + }, + { + "id": "mon_feral_labsecurity_flashlight", + "type": "MONSTER", + "copy-from": "mon_feral_labsecurity_9mm", + "description": "At first glance, this disheveled figure could almost pass for a survivor, but the bloodshot eyes give them away as one of the humans who have gone feral and joined the undead without dying. Their uniform suggests that this was at some point a security guard, and they still carry a stun gun and a heavy-duty flashlight that looks like it could easily double as a club.", + "melee_dice_sides": 6, + "special_attacks": [ [ "TAZER", 10 ] ], + "luminance": 500, + "death_drops": "feral_security_death_drops_flashlight" } ] diff --git a/data/json/monsters/fungus.json b/data/json/monsters/fungus.json index 851f9a9af4c6a..4841d66df4bd4 100644 --- a/data/json/monsters/fungus.json +++ b/data/json/monsters/fungus.json @@ -172,8 +172,41 @@ "harvest": "fungaloid", "special_attacks": [ [ "FUNGUS", 30 ] ], "death_function": [ "FUNGUS", "NORMAL" ], + "upgrades": { "half_life": 14, "into": "mon_fungaloid_shambler" }, "flags": [ "STUMBLES", "POISON", "NO_BREATHE", "NOHEAD" ] }, + { + "id": "mon_fungaloid_shambler", + "type": "MONSTER", + "name": { "str": "fungaloid shambler" }, + "description": "A large white fungus, a bulging gray stalk supporting a bloom at the top. Spores float from its gills and tendrils extend from the base. Chunks of rock, metal, bone, and wood are held by tendrils as a kind of armor.", + "copy-from": "mon_fungaloid", + "volume": "12 L", + "weight": "160 kg", + "hp": 150, + "speed": 20, + "armor_cut": 10, + "armor_bullet": 20, + "armor_stab": 20, + "armor_acid": 3, + "special_attacks": [ + [ "FUNGUS", 20 ], + { + "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": "The fungaloid shambler throws a rock!" + } + ], + "death_drops": [ { "group": "wreckage", "count": 5 }, { "group": "trash_forest", "count": 5 } ], + "upgrades": { } + }, { "id": "mon_fungaloid_queen", "type": "MONSTER", diff --git a/data/json/monsters/insect_spider.json b/data/json/monsters/insect_spider.json index cb3837f4b7602..8a21bad7e797f 100644 --- a/data/json/monsters/insect_spider.json +++ b/data/json/monsters/insect_spider.json @@ -380,9 +380,10 @@ "vision_night": 5, "harvest": "arachnid_bee", "anger_triggers": [ "HURT", "FRIEND_DIED", "PLAYER_CLOSE" ], + "fear_triggers": [ "FIRE" ], "death_function": [ "NORMAL" ], "upgrades": { "half_life": 21, "into": "mon_bee_mega" }, - "flags": [ "SEES", "SMELLS", "FLIES", "STUMBLES", "SWARMS", "GROUP_MORALE", "CANPLAY", "PATH_AVOID_FIRE", "LOUDMOVES" ] + "flags": [ "SEES", "SMELLS", "FLIES", "STUMBLES", "SWARMS", "GROUP_MORALE", "CANPLAY", "PATH_AVOID_FIRE" ] }, { "id": "mon_bee_mega", @@ -474,9 +475,9 @@ "//": "10-20 dmg, 4 hits to max intensity, 5 hits to max duration", "dodge": 2, "armor_bash": 10, - "armor_cut": 15, - "armor_bullet": 8, - "armor_stab": 10, + "armor_cut": 25, + "armor_bullet": 12, + "armor_stab": 14, "vision_day": 9, "vision_night": 6, "anger_triggers": [ "STALK", "PLAYER_CLOSE" ], @@ -604,7 +605,7 @@ "fear_triggers": [ "HURT" ], "death_function": [ "NORMAL" ], "flags": [ "AQUATIC", "SEES" ], - "upgrades": { "age_grow": 24, "into": "mon_dragonfly_giant" } + "upgrades": { "age_grow": 21, "into": "mon_dragonfly_giant" } }, { "id": "mon_dragonfly_giant", @@ -618,6 +619,7 @@ "weight": "40750 g", "hp": 50, "speed": 150, + "attack_cost": 150, "material": [ "iflesh" ], "symbol": "y", "color": "light_green", @@ -634,7 +636,9 @@ "cooldown": 6, "accuracy": 5, "move_cost": 300, - "damage_max_instance": [ { "damage_type": "cut", "amount": 6, "armor_penetration": 20 } ] + "damage_max_instance": [ { "damage_type": "cut", "amount": 6, "armor_penetration": 20 } ], + "min_mul": 1, + "max_mul": 2 } ], "dodge": 2, @@ -642,10 +646,10 @@ "vision_night": 5, "harvest": "arachnid_flying", "anger_triggers": [ "PLAYER_WEAK", "STALK" ], - "fear_triggers": [ "HURT" ], + "fear_triggers": [ "HURT", "FIRE" ], "death_function": [ "NORMAL" ], "upgrades": { "age_grow": 40, "into": "mon_dragonfly_mega" }, - "flags": [ "SWARMS", "HEARS", "SEES", "FLIES", "PATH_AVOID_FIRE", "LOUDMOVES" ] + "flags": [ "SWARMS", "HEARS", "SEES", "FLIES", "PATH_AVOID_FIRE" ] }, { "id": "mon_dragonfly_mega", @@ -690,7 +694,7 @@ "name": { "str": "firefly", "str_pl": "fireflies" }, "description": "A mutated firefly grown to the size of a large housecat, brightly glowing abdomen bobbing up and down as it tries to impress its unseen mate. It looks strangely uninterested in you and everything else.", "bodytype": "flying insect", - "species": [ "INSECT" ], + "species": [ "INSECT_FLYING" ], "default_faction": "insect", "material": [ "iflesh" ], "symbol": "y", @@ -705,7 +709,7 @@ "harvest": "arachnid_firefly", "reproduction": { "baby_egg": "egg_firefly", "baby_count": 1, "baby_timer": 15 }, "baby_flags": [ "SPRING", "SUMMER" ], - "fear_triggers": [ "HURT" ], + "fear_triggers": [ "HURT", "FIRE" ], "flags": [ "SEES", "HEARS", "FLIES", "STUMBLES" ] }, { @@ -744,10 +748,10 @@ "vision_day": 5, "vision_night": 5, "harvest": "arachnid_flying", - "fear_triggers": [ "PLAYER_CLOSE" ], + "fear_triggers": [ "PLAYER_CLOSE", "HURT", "FIRE" ], "death_function": [ "NORMAL" ], "upgrades": { "half_life": 21, "into": "mon_fly_mega" }, - "flags": [ "SEES", "SMELLS", "FLIES", "STUMBLES", "HIT_AND_RUN", "CANPLAY", "PATH_AVOID_FIRE", "LOUDMOVES" ] + "flags": [ "SEES", "SMELLS", "FLIES", "STUMBLES", "HIT_AND_RUN", "CANPLAY", "PATH_AVOID_FIRE" ] }, { "id": "mon_fly_mega", @@ -801,9 +805,10 @@ "vision_day": 5, "vision_night": 5, "harvest": "arachnid_flying", + "fear_triggers": [ "HURT", "FIRE" ], "death_function": [ "NORMAL" ], "upgrades": { "half_life": 21, "into": "mon_mosquito_mega" }, - "flags": [ "SEES", "SMELLS", "HEARS", "STUMBLES", "VENOM", "FLIES", "HIT_AND_RUN", "PATH_AVOID_FIRE", "LOUDMOVES" ] + "flags": [ "SEES", "SMELLS", "HEARS", "STUMBLES", "VENOM", "FLIES", "HIT_AND_RUN", "PATH_AVOID_FIRE" ] }, { "id": "mon_mosquito_mega", @@ -862,6 +867,7 @@ "harvest": "arachnid", "anger_triggers": [ "STALK", "PLAYER_WEAK", "PLAYER_CLOSE" ], "death_function": [ "NORMAL" ], + "fungalize_into": "mon_spider_fungus", "upgrades": { "half_life": 21, "into": "mon_spider_cellar_mega" }, "flags": [ "SEES", "SMELLS", "HEARS", "VENOM", "WEBWALK", "CLIMBS", "HARDTOSHOOT", "PUSH_MON", "PATH_AVOID_FIRE" ], "//": "No, they are not in fact the most venomous spider in the world." @@ -958,6 +964,7 @@ "special_attacks": [ { "type": "leap", "cooldown": 2, "max_range": 5, "allow_no_target": true } ], "anger_triggers": [ "PLAYER_CLOSE" ], "death_function": [ "NORMAL" ], + "fungalize_into": "mon_spider_fungus", "upgrades": { "half_life": 21, "into": "mon_spider_jumping_mega" }, "flags": [ "SEES", "SMELLS", "HEARS", "HIT_AND_RUN", "CLIMBS", "PATH_AVOID_DANGER_1" ] }, @@ -1015,6 +1022,7 @@ "vision_night": 5, "harvest": "arachnid", "death_function": [ "NORMAL" ], + "fungalize_into": "mon_spider_fungus", "upgrades": { "half_life": 21, "into": "mon_spider_trapdoor_mega" }, "flags": [ "SEES", "SMELLS", "HEARS", "VENOM", "GRABS", "CAN_DIG", "WEBWALK", "CLIMBS", "PATH_AVOID_FIRE" ] }, @@ -1074,6 +1082,7 @@ "vision_night": 5, "harvest": "arachnid", "death_function": [ "NORMAL" ], + "fungalize_into": "mon_spider_fungus", "upgrades": { "half_life": 21, "into": "mon_spider_web_mega" }, "flags": [ "SEES", "SMELLS", "HEARS", "WEBWALK", "CLIMBS", "PATH_AVOID_FIRE", "PATH_AVOID_FALL" ] }, @@ -1164,6 +1173,7 @@ "harvest": "arachnid", "anger_triggers": [ "PLAYER_WEAK", "PLAYER_CLOSE" ], "death_function": [ "NORMAL" ], + "fungalize_into": "mon_spider_fungus", "upgrades": { "half_life": 21, "into": "mon_spider_widow_mega" }, "flags": [ "SEES", "SMELLS", "HEARS", "BADVENOM", "WEBWALK", "CLIMBS", "PATH_AVOID_FIRE" ] }, @@ -1258,6 +1268,7 @@ "harvest": "arachnid", "anger_triggers": [ "STALK", "PLAYER_WEAK", "HURT", "PLAYER_CLOSE" ], "death_function": [ "NORMAL" ], + "fungalize_into": "mon_spider_fungus", "upgrades": { "half_life": 21, "into": "mon_spider_wolf_mega" }, "flags": [ "SEES", "SMELLS", "HEARS", "CLIMBS", "PATH_AVOID_FIRE", "PATH_AVOID_FALL" ] }, @@ -1274,89 +1285,228 @@ "special_attacks": [ [ "HOWL", 10 ] ], "extend": { "flags": [ "DESTROYS", "PUSH_MON", "PUSH_VEH" ] } }, + { + "id": "mon_wasp_larva", + "type": "MONSTER", + "name": { "str": "wasp larva", "str_pl": "wasp larvae" }, + "description": "A dog-sized white grub, featureless apart from a vestigal mouthpart. It hangs upside-down in its cell, and regurgitates sweet-smelling fluid in return for the scraps of viscera fed to it by the workers below.", + "symbol": "a", + "color": "white", + "looks_like": "mon_dermatik_larva", + "default_faction": "wasp", + "bodytype": "snake", + "material": [ "iflesh" ], + "species": [ "INSECT" ], + "volume": "30 L", + "weight": "35 kg", + "hp": 35, + "upgrades": { "age_grow": 16, "into": "mon_wasp_pupa" }, + "//": "30 days of development from egg to adult.", + "flags": [ "IMMOBILE" ] + }, + { + "id": "mon_wasp_pupa", + "type": "MONSTER", + "name": { "str": "wasp pupa", "str_pl": "wasp pupae" }, + "description": "A wasp larva undergoing metamorphosis into a full-grown giant wasp. It looks like a strangely immobile adult coated in a thin white membrane, its legs and wings packed tightly into its cell.", + "symbol": "8", + "color": "light_gray", + "default_faction": "wasp", + "bodytype": "blob", + "material": [ "iflesh" ], + "species": [ "INSECT" ], + "volume": "85 L", + "weight": "90 kg", + "hp": 100, + "upgrades": { "age_grow": 10, "into_group": "GROUP_WASP_PUPA" }, + "flags": [ "IMMOBILE" ] + }, { "id": "mon_wasp_small", "type": "MONSTER", - "name": { "str": "huge wasp" }, - "description": "A huge black and yellow mutant yellowjacket wasp the size of a ferret with a long, evil-looking stinger.", + "name": { "str": "wasp" }, + "description": "A huge wasp the size of a large cat with bright yellow markings on her jet-black carapace and a threatening stinger. It flies around erratically, searching for smaller prey.", "default_faction": "wasp", "bodytype": "flying insect", "species": [ "INSECT_FLYING" ], "diff": 2, - "volume": "750 ml", - "weight": "1 kg", + "volume": "9 L", + "weight": "9 kg", "hp": 20, - "speed": 165, + "speed": 130, + "attack_cost": 130, "material": [ "iflesh" ], "symbol": "a", "color": "dark_gray", - "aggression": 10, - "morale": 30, + "aggression": -10, "melee_skill": 5, "melee_dice": 2, - "melee_dice_sides": 4, - "melee_cut": 8, - "attack_effs": [ { "id": "venom_weaken", "duration": 100 } ], - "dodge": 4, - "armor_bash": 4, - "armor_cut": 8, - "armor_bullet": 6, - "vision_day": 10, + "melee_dice_sides": 3, + "special_attacks": [ + { + "id": "sting", + "cooldown": 8, + "min_mul": 0.3, + "max_mul": 0.5, + "effects": [ { "id": "venom_weaken", "duration": 100, "chance": 50 } ] + }, + { + "type": "bite", + "cooldown": 4, + "move_cost": 130, + "damage_max_instance": [ { "damage_type": "cut", "amount": 10 } ], + "min_mul": 0.2, + "max_mul": 0.5 + } + ], + "//": "sting 3-5 dmg, 40 hits to max intensity, 50 to max duration", + "dodge": 8, + "armor_bash": 1, + "armor_cut": 6, + "armor_stab": 4, + "vision_day": 15, "vision_night": 5, "harvest": "arachnid_wasp", - "anger_triggers": [ "HURT", "FRIEND_DIED", "PLAYER_CLOSE", "PLAYER_WEAK", "STALK" ], + "anger_triggers": [ "FRIEND_ATTACKED", "PLAYER_CLOSE", "PLAYER_WEAK", "HOSTILE_SEEN" ], + "fear_triggers": [ "HURT", "FIRE" ], "death_function": [ "NORMAL" ], - "upgrades": { "half_life": 14, "into_group": "WASP_UPGRADE" }, - "flags": [ "SEES", "SMELLS", "FLIES", "SWARMS", "GROUP_MORALE", "CANPLAY", "PATH_AVOID_FIRE" ] + "upgrades": { "half_life": 30, "into": "mon_wasp" }, + "flags": [ "SEES", "SMELLS", "HEARS", "FLIES", "SWARMS", "GROUP_MORALE", "CANPLAY", "PATH_AVOID_FIRE", "HARDTOSHOOT" ] + }, + { + "id": "mon_wasp_small_guard", + "type": "MONSTER", + "name": { "str": "wasp guard" }, + "description": "A mutated wasp worker, buzzing around their nest. While their foraging sisters are relatively content to leave you be, this one won't hesitate to attack anything it considers a threat to the nest. Don't linger.", + "copy-from": "mon_wasp_small", + "aggression": 0, + "morale": 100, + "melee_skill": 6, + "extend": { "anger_triggers": [ "HURT", "FIRE", "STALK" ] }, + "delete": { "fear_triggers": [ "HURT", "FIRE" ] }, + "upgrades": { "half_life": 30, "into": "mon_wasp_guard" } + }, + { + "id": "mon_wasp_queen", + "type": "MONSTER", + "name": { "str": "wasp queen" }, + "description": "A mutated wasp queen grown to the size of a person, laying a single white egg in each empty cell it comes across while scurrying around on the paper walls of their home. It struggles to fit into some of them, but the newer cells seem to be getting larger…", + "copy-from": "mon_wasp_guard", + "harvest": "arachnid_wasp_queen", + "reproduction": { "baby_egg": "egg_wasp", "baby_count": 2, "baby_timer": 5 }, + "upgrades": { "half_life": 30, "into": "mon_wasp_mega" } }, { "id": "mon_wasp", "type": "MONSTER", "name": { "str": "giant wasp" }, - "description": "A gigantic slender-bodied wasp with an evil-looking stinger protruding from its abdomen. Its exoskeleton glowers with ominous red markings.", + "description": "A gigantic wasp worker, scouring the landscape for an easy meal to feed the next generation. Its abdomen glowers with ominous yellow stripes and ends in a half-hidden stinger the size of a kitchen knife.", "default_faction": "wasp", "bodytype": "flying insect", "species": [ "INSECT_FLYING" ], - "diff": 2, "volume": "62500 ml", "weight": "81500 g", - "hp": 40, + "hp": 100, "speed": 150, + "attack_cost": 150, "material": [ "iflesh" ], "symbol": "a", "color": "dark_gray", - "aggression": 10, - "morale": 40, - "melee_skill": 5, + "aggression": -5, + "morale": 15, + "melee_skill": 7, "melee_dice": 2, - "melee_dice_sides": 6, - "melee_cut": 8, - "attack_effs": [ { "id": "venom_weaken", "duration": 200 } ], - "//": "15 hits to max intensity, 18 hits to max duration", - "dodge": 4, - "armor_bash": 4, - "armor_cut": 8, - "armor_bullet": 6, - "vision_day": 10, - "vision_night": 5, + "melee_dice_sides": 7, + "special_attacks": [ + { + "id": "sting", + "move_cost": 300, + "cooldown": 5, + "damage_max_instance": [ { "damage_type": "stab", "amount": 10, "armor_penetration": 25 } ], + "min_mul": 0.5, + "max_mul": 2 + }, + { + "type": "bite", + "cooldown": 5, + "move_cost": 130, + "damage_max_instance": [ { "damage_type": "cut", "amount": 10, "armor_penetration": 20 } ], + "body_parts": [ [ "leg_l", 2 ], [ "leg_r", 2 ], [ "head", 1 ], [ "arm_l", 2 ], [ "arm_r", 2 ], [ "torso", 4 ] ], + "min_mul": 0.5, + "max_mul": 1.5 + } + ], + "//": "sting 5-15 damage, 8 hits to max intensity, 9 to max duration", + "dodge": 7, + "armor_bash": 5, + "armor_cut": 14, + "armor_stab": 12, + "armor_bullet": 12, + "vision_day": 17, + "vision_night": 7, "harvest": "arachnid_wasp", - "anger_triggers": [ "HURT", "FRIEND_DIED", "PLAYER_CLOSE", "PLAYER_WEAK", "STALK" ], + "anger_triggers": [ "FRIEND_ATTACKED", "PLAYER_CLOSE", "PLAYER_WEAK", "HOSTILE_SEEN" ], + "fear_triggers": [ "HURT", "FIRE" ], "death_function": [ "NORMAL" ], - "upgrades": { "half_life": 21, "into": "mon_wasp_mega" }, - "flags": [ "SEES", "SMELLS", "FLIES", "SWARMS", "GROUP_MORALE", "CANPLAY", "PATH_AVOID_FIRE" ] + "flags": [ "SEES", "SMELLS", "HEARS", "FLIES", "SWARMS", "GROUP_MORALE", "CANPLAY", "HARDTOSHOOT", "PATH_AVOID_FIRE" ] }, { - "id": "mon_wasp_mega", + "id": "mon_wasp_guard", "type": "MONSTER", - "name": { "str": "whale wasp" }, - "description": "An enormous slender-bodied wasp with an evil-looking stinger protruding from its abdomen. Its exoskeleton glowers with ominous red markings.", + "name": { "str": "giant wasp guard" }, + "description": "A wasp worker grown to the size of a person, buzzing around their home. While their foraging sisters are relatively content to live and let live, this one won't hesitate to attack anything it considers a threat to the nest. Don't linger.", "copy-from": "mon_wasp", - "proportional": { "hp": 10, "speed": 0.25, "dodge": 0.25, "vision_day": 2 }, - "volume": "625 L", - "weight": "815 kg", - "melee_dice": 3, - "special_attacks": [ { "id": "impale" } ], - "extend": { "flags": [ "DESTROYS", "PUSH_MON", "PUSH_VEH" ] } + "aggression": 4, + "morale": 100, + "melee_skill": 9, + "extend": { "anger_triggers": [ "HURT", "FIRE" ], "flags": [ "BASHES" ] }, + "delete": { "fear_triggers": [ "HURT", "FIRE" ] } + }, + { + "id": "mon_wasp_mega", + "type": "MONSTER", + "name": { "str": "giant wasp queen" }, + "description": "A yellowjacket queen grown to the size of a horse, towering over their daughters. Even with their abdomen swollen with eggs and wings long unused it still poses a threat to interlopers, should their children have failed at subduing them.", + "copy-from": "mon_wasp_guard", + "looks_like": "mon_wasp_mega", + "diff": 4, + "proportional": { "hp": 4, "volume": 5, "weight": 5 }, + "speed": 100, + "attack_cost": 100, + "melee_skill": 8, + "melee_dice": 4, + "melee_dice_sides": 5, + "special_attacks": [ + { + "id": "sting", + "cooldown": 5, + "move_cost": 200, + "damage_max_instance": [ { "damage_type": "stab", "amount": 15, "armor_penetration": 60 } ], + "min_mul": 1, + "max_mul": 3, + "effects": [ { "id": "venom_weaken", "duration": 600 }, { "id": "downed", "duration": 1, "chance": 50 } ] + }, + { + "type": "bite", + "cooldown": 3, + "move_cost": 100, + "damage_max_instance": [ { "damage_type": "cut", "amount": 10, "armor_penetration": 30 } ], + "min_mul": 1, + "max_mul": 3, + "body_parts": [ [ "leg_l", 2 ], [ "leg_r", 2 ], [ "head", 1 ], [ "arm_l", 2 ], [ "arm_r", 2 ], [ "torso", 4 ] ] + } + ], + "//": "sting 15-45 damage, 6 hits to max intensity, 8 to max duration; bite 10-30", + "dodge": 3, + "vision_night": 15, + "armor_bash": 8, + "armor_cut": 30, + "armor_stab": 18, + "armor_bullet": 22, + "harvest": "arachnid_wasp_queen", + "reproduction": { "baby_egg": "egg_wasp", "baby_count": 4, "baby_timer": 5 }, + "extend": { "flags": [ "PUSH_MON" ] }, + "delete": { "flags": [ "HARDTOSHOOT" ] } }, { "id": "mon_dermatik_larva", @@ -1398,7 +1548,7 @@ "volume": "12 L", "weight": "15 kg", "hp": 60, - "speed": 110, + "speed": 130, "material": [ "iflesh" ], "symbol": "a", "color": "red", @@ -1417,13 +1567,14 @@ "anger_triggers": [ "FRIEND_ATTACKED", "PLAYER_WEAK" ], "fear_triggers": [ "HURT", "FIRE" ], "death_function": [ "NORMAL" ], - "flags": [ "SEES", "HEARS", "SMELLS", "FLIES", "PATH_AVOID_FIRE", "LOUDMOVES" ] + "flags": [ "SEES", "HEARS", "SMELLS", "FLIES", "PATH_AVOID_FIRE", "CANPLAY" ] }, { "id": "mon_dermatik_midwife", "type": "MONSTER", "name": { "str": "dermatik midwife", "str_pl": "dermatik midwives" }, "description": "A dermatik grown even larger, its wings and ovipositor chewed off by her sisters. It now stands watch in the nest, tending to the unfortunate hosts dragged home and ensuring they don't perish before their purpose is fulfilled.", + "species": [ "INSECT" ], "copy-from": "mon_dermatik", "volume": "17 L", "weight": "25 kg", @@ -1431,7 +1582,7 @@ "speed": 100, "melee_cut": 10, "vision_night": 8, - "delete": { "flags": [ "FLIES", "LOUDMOVES" ], "special_attacks": [ "DERMATIK" ] } + "delete": { "flags": [ "FLIES" ], "special_attacks": [ "DERMATIK" ] } }, { "id": "mon_dermatik_incubator_deer", @@ -1528,6 +1679,7 @@ "anger_triggers": [ "FRIEND_ATTACKED", "FRIEND_DIED", "HURT", "PLAYER_WEAK" ], "death_function": [ "NORMAL" ], "special_attacks": [ [ "EAT_FOOD", 30 ] ], + "fungalize_into": "mon_ant_fungus", "flags": [ "SEES", "HEARS", "SMELLS", "CLIMBS", "PATH_AVOID_FIRE", "PATH_AVOID_FALL" ] }, { @@ -1721,6 +1873,7 @@ "special_attacks": [ [ "ANTQUEEN", 1 ] ], "anger_triggers": [ "FRIEND_ATTACKED", "FRIEND_DIED", "HURT" ], "death_function": [ "NORMAL" ], + "fungalize_into": "mon_ant_fungus", "flags": [ "SMELLS", "QUEEN", "CLIMBS", "PATH_AVOID_FIRE", "PATH_AVOID_FALL" ] }, { @@ -1753,6 +1906,7 @@ "harvest": "arachnid", "anger_triggers": [ "FRIEND_ATTACKED", "FRIEND_DIED", "HURT", "PLAYER_CLOSE" ], "death_function": [ "NORMAL" ], + "fungalize_into": "mon_ant_fungus", "upgrades": { "half_life": 21, "into": "mon_ant_soldier_mega" }, "flags": [ "SEES", "HEARS", "SMELLS", "CLIMBS", "PATH_AVOID_FIRE", "PATH_AVOID_FALL" ] }, @@ -1811,6 +1965,7 @@ "baby_flags": [ "AUTUMN" ], "biosignature": { "biosig_item": "feces_roach", "biosig_timer": 3 }, "anger_triggers": [ "FRIEND_ATTACKED" ], + "fear_triggers": [ "HURT", "FIRE" ], "death_function": [ "NORMAL" ], "upgrades": { "half_life": 21, "into": "mon_locust_mega" }, "special_attacks": [ { "type": "leap", "cooldown": 2, "max_range": 8, "allow_no_target": true }, [ "EAT_CROP", 60 ] ], @@ -1856,6 +2011,7 @@ "upgrades": { "age_grow": 10, "into": "mon_locust" }, "death_function": [ "NORMAL" ], "special_attacks": [ { "type": "leap", "cooldown": 4, "max_range": 4, "allow_no_target": true }, [ "EAT_CROP", 120 ] ], + "fear_triggers": [ "HURT", "FIRE" ], "flags": [ "SEES", "HEARS", "SMELLS", "CLIMBS", "LARVA", "STUMBLES", "PATH_AVOID_FIRE" ] } ] diff --git a/data/json/monsters/jabberwock.json b/data/json/monsters/jabberwock.json index 68c50183c508f..8e49506b21980 100644 --- a/data/json/monsters/jabberwock.json +++ b/data/json/monsters/jabberwock.json @@ -3,7 +3,7 @@ "id": "mon_fleshy_shambler", "type": "MONSTER", "name": { "str": "fleshy shambler" }, - "description": "An amalgamation of throbbing organs from various creatures have fused together into this lurching, vaguely humanoid shape. Its myriad roughly formed mouths sussurate in a chorus of sibilant groans and whispers.", + "description": "An amalgamation of throbbing organs from various creatures have fused together into this lurching, vaguely-humanoid shape. Its myriad roughly-formed mouths susurrate in a chorus of sibilant groans and whispers.", "default_faction": "jabberwock", "species": [ "ABERRATION" ], "volume": "80000 ml", diff --git a/data/json/monsters/mammal.json b/data/json/monsters/mammal.json index 964e69d76f975..f70942fe69945 100644 --- a/data/json/monsters/mammal.json +++ b/data/json/monsters/mammal.json @@ -231,6 +231,7 @@ "fear_triggers": [ "SOUND", "PLAYER_CLOSE" ], "placate_triggers": [ "MEAT" ], "death_function": [ "NORMAL" ], + "zombify_into": "mon_zpig_brute", "special_attacks": [ [ "EAT_FOOD", 20 ] ], "flags": [ "SEES", "HEARS", "SMELLS", "PET_MOUNTABLE", "ANIMAL", "PATH_AVOID_DANGER_1", "WARM", "KEENNOSE" ] }, diff --git a/data/json/monsters/triffid.json b/data/json/monsters/triffid.json index 24541393ac309..e92824b27659f 100644 --- a/data/json/monsters/triffid.json +++ b/data/json/monsters/triffid.json @@ -143,6 +143,7 @@ "upgrades": { "age_grow": 14, "into": "mon_triffid" }, "special_attacks": [ [ "TRIFFID_GROWTH", 28800 ] ], "death_function": [ "NORMAL" ], + "fungalize_into": "mon_fungaloid", "flags": [ "HEARS", "SMELLS", "NOHEAD", "PARALYZEVENOM" ] }, { @@ -170,6 +171,7 @@ "armor_bullet": 3, "harvest": "triffid_paralytic", "death_function": [ "NORMAL" ], + "fungalize_into": "mon_fungaloid", "flags": [ "SEES", "SMELLS", "BASHES", "GROUP_BASH", "NOHEAD", "PARALYZEVENOM" ] }, { @@ -198,6 +200,7 @@ "harvest": "triffid_queen", "special_attacks": [ [ "GROWPLANTS", 20 ] ], "death_function": [ "NORMAL" ], + "fungalize_into": "mon_fungaloid", "flags": [ "HEARS", "SMELLS", "BASHES", "NOHEAD", "PARALYZEVENOM" ] }, { diff --git a/data/json/monsters/zanimal_upgrade.json b/data/json/monsters/zanimal_upgrade.json index 53cae8fb5fa48..077f5b4e2167f 100644 --- a/data/json/monsters/zanimal_upgrade.json +++ b/data/json/monsters/zanimal_upgrade.json @@ -92,6 +92,41 @@ { "id": "scratch", "damage_max_instance": [ { "damage_type": "cut", "amount": 15, "armor_multiplier": 0.6 } ] } ] }, + { + "id": "mon_zpig_brute", + "type": "MONSTER", + "name": { "str": "skull pig" }, + "copy-from": "mon_zombie_pig", + "description": "This former wild boar appears to have been a truly massive specimen in life. Stretching eight to nine feet in length, the most arresting feature of this animal is that the enamel of its tusks has spread across its face leaving a skull-like appearance with deep-set eyeholes.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5, "attack_cost": 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 + }, + "special_attacks": [ [ "SMASH", 30 ], { "id": "impale" } ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_VEH", "HIT_AND_RUN" ] } + }, + { + "id": "mon_zombie_pig_gas", + "type": "MONSTER", + "name": { "str": "trench pig" }, + "copy-from": "mon_zombie_pig", + "description": "Billowing clouds of yellow-streaked gas precede boar-shaped shadows. Glimpses of a zombie boar are quickly obscured by the gases leaving its body through open wounds.", + "diff": 5, + "color": "red", + "harvest": "exempt", + "emit_fields": [ { "emit_id": "emit_tear_gas_stream", "delay": "1 s" } ], + "death_function": [ "TEARBURST" ], + "special_attacks": [ { "id": "impale" } ], + "extend": { "flags": [ "HIT_AND_RUN" ] } + }, { "id": "mon_wolf_skeleton", "type": "MONSTER", diff --git a/data/json/monsters/zed-animal.json b/data/json/monsters/zed-animal.json index 054c62ca22488..1e0dcc041a0fe 100644 --- a/data/json/monsters/zed-animal.json +++ b/data/json/monsters/zed-animal.json @@ -223,6 +223,7 @@ "anger_triggers": [ "PLAYER_WEAK", "PLAYER_CLOSE" ], "fear_triggers": [ "FIRE" ], "death_function": [ "NORMAL" ], + "upgrades": { "half_life": 20, "into_group": "GROUP_ZOMBIE_PIG_UPGRADE" }, "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "KEENNOSE", "BASHES", "POISON", "NO_BREATHE", "REVIVES", "FILTHY" ], "//": "1d8->2d5, minor bonus over 1d9" }, diff --git a/data/json/monsters/zed-classic.json b/data/json/monsters/zed-classic.json index 2a20a86ea6950..a11db87639196 100644 --- a/data/json/monsters/zed-classic.json +++ b/data/json/monsters/zed-classic.json @@ -43,6 +43,7 @@ "NO_BREATHE", "REVIVES", "PUSH_MON", + "NO_FUNG_DMG", "FILTHY" ] }, @@ -74,6 +75,7 @@ "death_drops": "default_zombie_death_drops", "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", + "fungalize_into": "mon_zombie_fungus", "upgrades": { "half_life": 14, "into_group": "GROUP_ZOMBIE_UPGRADE" }, "flags": [ "SEES", @@ -123,6 +125,7 @@ "death_drops": "mon_zombie_cop_death_drops", "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", + "fungalize_into": "mon_zombie_fungus", "flags": [ "SEES", "HEARS", @@ -198,6 +201,7 @@ "harvest": "zombie_leather", "special_attacks": [ [ "DANCE", 30 ] ], "death_drops": "mon_zombie_hulk_death_drops", + "fungalize_into": "mon_zombie_fungus", "death_function": [ "NORMAL" ], "regenerates": 50, "flags": [ "WARM", "BASHES", "DESTROYS", "NO_BREATHE", "POISON", "FILTHY" ] @@ -233,6 +237,7 @@ "death_drops": "default_zombie_death_drops", "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", + "fungalize_into": "mon_zombie_fungus", "upgrades": { "half_life": 12, "into_group": "GROUP_ZOMBIE_FAT" }, "flags": [ "SEES", @@ -294,6 +299,7 @@ "NO_BREATHE", "REVIVES", "PUSH_MON", + "NO_FUNG_DMG", "FILTHY" ] }, @@ -340,6 +346,7 @@ "NO_BREATHE", "REVIVES", "PUSH_MON", + "NO_FUNG_DMG", "FILTHY" ] }, @@ -371,6 +378,7 @@ "death_drops": "default_zombie_death_drops", "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", + "fungalize_into": "mon_zombie_fungus", "upgrades": { "half_life": 23, "into": "mon_devourer" }, "flags": [ "SEES", @@ -467,6 +475,7 @@ "death_drops": "default_zombie_death_drops", "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", + "fungalize_into": "mon_zombie_fungus", "upgrades": { "half_life": 14, "into_group": "GROUP_ZOMBIE_UPGRADE" }, "flags": [ "SEES", diff --git a/data/json/monsters/zed_acid.json b/data/json/monsters/zed_acid.json index a7c46a911faf7..328abbac67496 100644 --- a/data/json/monsters/zed_acid.json +++ b/data/json/monsters/zed_acid.json @@ -260,7 +260,7 @@ "type": "MONSTER", "copy-from": "mon_zombie_dog_acidic", "name": { "str": "blistered horror" }, - "description": "A huge canine with multiple large foul looking blisters covering its body. A corrosive liquid spills from its menancing-looking mouth.", + "description": "A huge canine with multiple large, foul-looking blisters covering its body. A corrosive liquid spills from its menacing-looking mouth.", "color": "yellow_white", "upgrades": false, "relative": { diff --git a/data/json/monsters/zed_children.json b/data/json/monsters/zed_children.json index 0fa1c8af14176..6512ba1615ce2 100644 --- a/data/json/monsters/zed_children.json +++ b/data/json/monsters/zed_children.json @@ -28,6 +28,7 @@ "death_drops": { "subtype": "collection", "groups": [ [ "default_zombie_clothes", 100 ], [ "child_items", 65 ] ] }, "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_child_scorched", + "zombify_into": "mon_zombie_child_fungus", "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "BASHES", "POISON", "NO_BREATHE", "REVIVES", "FILTHY" ], "//": "no GUILT because it no longer looks enough like a child to evoke pity" }, @@ -65,6 +66,7 @@ }, "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_child_scorched", + "zombify_into": "mon_zombie_child_fungus", "upgrades": { "half_life": 14, "into_group": "GROUP_CHILD_ZOMBIE_UPGRADE" }, "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "BASHES", "POISON", "GUILT", "NO_BREATHE", "REVIVES", "FILTHY" ] }, @@ -97,6 +99,7 @@ "death_drops": { "subtype": "collection", "groups": [ [ "default_zombie_clothes", 100 ], [ "child_items", 65 ] ] }, "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_child_scorched", + "zombify_into": "mon_zombie_child_fungus", "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "BASHES", "POISON", "NO_BREATHE", "REVIVES", "CLIMBS", "FILTHY" ], "//": "no GUILT because it no longer looks enough like a child to evoke pity" }, @@ -130,6 +133,7 @@ "death_drops": { "subtype": "collection", "groups": [ [ "default_zombie_clothes", 100 ], [ "child_items", 65 ] ] }, "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_child_scorched", + "zombify_into": "mon_zombie_child_fungus", "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "GUILT", "BASHES", "POISON", "NO_BREATHE", "REVIVES", "FILTHY" ], "//": "GUILT because it still looks enough like a child to evoke pity" }, @@ -163,6 +167,7 @@ "death_drops": { "subtype": "collection", "groups": [ [ "default_zombie_clothes", 100 ], [ "child_items", 65 ] ] }, "death_function": [ "BOOMER" ], "burn_into": "mon_zombie_child_scorched", + "zombify_into": "mon_zombie_child_fungus", "flags": [ "SEES", "HEARS", "STUMBLES", "WARM", "GUILT", "POISON", "NO_BREATHE", "REVIVES", "FILTHY" ], "//": "GUILT because it still looks enough like a child to evoke pity" }, @@ -195,6 +200,7 @@ "death_drops": { "subtype": "collection", "groups": [ [ "default_zombie_clothes", 100 ], [ "child_items", 65 ] ] }, "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_child_scorched", + "zombify_into": "mon_zombie_child_fungus", "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "BASHES", "POISON", "NO_BREATHE", "REVIVES", "CLIMBS", "FILTHY" ], "//": "no GUILT because it no longer looks enough like a child to evoke pity" }, @@ -230,6 +236,7 @@ "death_drops": { "subtype": "collection", "groups": [ [ "default_zombie_clothes", 100 ], [ "child_items", 65 ] ] }, "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_child_scorched", + "zombify_into": "mon_zombie_child_fungus", "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "GUILT", "POISON", "NO_BREATHE", "REVIVES", "HARDTOSHOOT", "FILTHY" ], "//": "GUILT because it still looks enough like a child to evoke pity" } diff --git a/data/json/monsters/zed_explosive.json b/data/json/monsters/zed_explosive.json index d0df9c8e8cdcd..47f11eda5b05e 100644 --- a/data/json/monsters/zed_explosive.json +++ b/data/json/monsters/zed_explosive.json @@ -27,6 +27,7 @@ "death_drops": "default_zombie_items", "death_function": [ "BOOMER" ], "upgrades": { "half_life": 14, "into": "mon_boomer_huge" }, + "fungalize_into": "mon_boomer_fungus", "flags": [ "SEES", "HEARS", @@ -71,6 +72,7 @@ "armor_bullet": 4, "vision_night": 3, "harvest": "zombie", + "fungalize_into": "mon_boomer_fungus", "special_attacks": [ [ "BOOMER_GLOW", 20 ], [ "scratch", 20 ] ], "death_drops": "default_zombie_items", "death_function": [ "BOOMER_GLOW" ], @@ -162,6 +164,7 @@ "special_attacks": [ [ "SUICIDE", 20 ], [ "scratch", 15 ] ], "death_drops": "default_zombie_items", "death_function": [ "GAS" ], + "fungalize_into": "mon_zombie_gasbag_fungus", "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "POISON", "NO_BREATHE", "REVIVES", "FILTHY" ] } ] diff --git a/data/json/monsters/zed_misc.json b/data/json/monsters/zed_misc.json index d2c315f3304a7..afd33a3c5ed05 100644 --- a/data/json/monsters/zed_misc.json +++ b/data/json/monsters/zed_misc.json @@ -222,6 +222,7 @@ "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_fiend", "upgrades": { "half_life": 21, "into_group": "GROUP_ZOMBIE_BRUTE" }, + "fungalize_into": "mon_zombie_fungus", "flags": [ "SEES", "HEARS", @@ -415,6 +416,7 @@ "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", "upgrades": { "half_life": 14, "into_group": "GROUP_ZOMBIE_GRAB" }, + "fungalize_into": "mon_zombie_fungus", "flags": [ "SEES", "HEARS", @@ -460,6 +462,7 @@ "death_drops": "default_zombie_death_drops", "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", + "fungalize_into": "mon_zombie_fungus", "flags": [ "SEES", "HEARS", @@ -540,6 +543,7 @@ "special_attacks": [ [ "SMASH", 20 ] ], "death_drops": "mon_zombie_hulk_death_drops", "death_function": [ "NORMAL" ], + "fungalize_into": "mon_zombie_fungus", "flags": [ "SEES", "HEARS", @@ -593,6 +597,7 @@ "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", "upgrades": { "half_life": 28, "into": "mon_zombie_predator" }, + "fungalize_into": "mon_zombie_fungus", "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "BASHES", "POISON", "NO_BREATHE", "REVIVES", "CLIMBS", "PUSH_MON", "FILTHY" ] }, { @@ -735,6 +740,7 @@ "REVIVES", "CAN_OPEN_DOORS", "PRIORITIZE_TARGETS", + "NO_FUNG_DMG", "FILTHY", "PATH_AVOID_DANGER_1" ] @@ -784,6 +790,7 @@ "PATH_AVOID_DANGER_1", "CAN_OPEN_DOORS", "PRIORITIZE_TARGETS", + "NO_FUNG_DMG", "FILTHY" ] }, @@ -1017,6 +1024,7 @@ "death_drops": "default_zombie_death_drops", "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", + "fungalize_into": "mon_zombie_fungus", "upgrades": { "half_life": 10, "into": "mon_zombie_screecher" }, "flags": [ "SEES", @@ -1110,6 +1118,7 @@ "emit_fields": [ { "emit_id": "emit_smoke_stream", "delay": "1 s" } ], "special_attacks": [ { "type": "bite", "cooldown": 5 }, [ "scratch", 15 ] ], "death_function": [ "SMOKEBURST" ], + "fungalize_into": "mon_zombie_smoker_fungus", "upgrades": { "half_life": 28, "into": "mon_smoker_brute" }, "flags": [ "SEES", @@ -1130,7 +1139,7 @@ "id": "mon_smoker_brute", "type": "MONSTER", "name": { "str": "ashen brawler" }, - "description": "A gigantic, twisted human frame with a menancing stance and rapid movements. Thick clouds of smoke pour from violent-looking eviscerations spread across its muscular-looking body, and its arms appear to have elongated massively.", + "description": "A gigantic, twisted human frame with a menacing stance and rapid movements. Thick clouds of smoke pour from violent-looking eviscerations spread across its muscular-looking body, and its arms appear to have elongated massively.", "default_faction": "zombie", "bodytype": "human", "species": [ "ZOMBIE", "HUMAN" ], @@ -1204,6 +1213,7 @@ "death_drops": "mon_zombie_swimmer_death_drops", "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", + "fungalize_into": "mon_zombie_fungus", "upgrades": { "half_life": 14, "into": "mon_zombie_swimmer" }, "categories": [ "CLASSIC" ], "flags": [ @@ -1249,6 +1259,7 @@ "death_drops": "mon_zombie_swimmer_death_drops", "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", + "fungalize_into": "mon_zombie_fungus", "upgrades": { "half_life": 28, "into": "mon_zombie_mancroc" }, "flags": [ "SEES", @@ -1295,6 +1306,7 @@ "vision_day": 15, "vision_night": 2, "harvest": "CBM_TECH", + "fungalize_into": "mon_zombie_fungus", "special_attacks": [ [ "PULL_METAL_WEAPON", 25 ], { "type": "bite", "cooldown": 20 } ], "death_drops": "mon_zombie_technician_death_drops", "death_function": [ "NORMAL" ], @@ -1312,6 +1324,41 @@ "RANGED_ATTACKER" ] }, + { + "id": "mon_zombie_miner", + "type": "MONSTER", + "name": { "str": "zombie miner" }, + "description": "This zombie's face, hands, work clothes, and miner's helmet are fully covered with stains of coal dust.", + "default_faction": "zombie", + "looks_like": "mon_zombie_technician", + "bodytype": "human", + "species": [ "ZOMBIE", "HUMAN" ], + "diff": 2, + "volume": "62500 ml", + "weight": "81500 g", + "hp": 85, + "speed": 75, + "material": [ "flesh" ], + "symbol": "Z", + "color": "i_light_cyan", + "aggression": 70, + "morale": 100, + "melee_skill": 5, + "melee_dice": 2, + "melee_dice_sides": 3, + "melee_cut": 0, + "dodge": 1, + "armor_bash": 2, + "armor_cut": 2, + "armor_bullet": 2, + "vision_day": 15, + "vision_night": 2, + "harvest": "zombie", + "special_attacks": [ { "type": "bite", "cooldown": 20 } ], + "death_drops": "mon_zombie_miner_death_drops", + "death_function": [ "NORMAL" ], + "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "BASHES", "GROUP_BASH", "POISON", "NO_BREATHE", "REVIVES", "FILTHY" ] + }, { "id": "mon_zombie_thorny", "type": "MONSTER", diff --git a/data/json/monsters/zed_soldiers.json b/data/json/monsters/zed_soldiers.json index 4747c38b19ce2..601d3c93b765b 100644 --- a/data/json/monsters/zed_soldiers.json +++ b/data/json/monsters/zed_soldiers.json @@ -33,6 +33,7 @@ "death_function": [ "NORMAL" ], "upgrades": { "half_life": 28, "into_group": "GROUP_SOLDIER_UPGRADE" }, "burn_into": "mon_zombie_scorched", + "fungalize_into": "mon_zombie_fungus", "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "BASHES", "GROUP_BASH", "POISON", "NO_BREATHE", "REVIVES", "PUSH_MON", "FILTHY" ] }, { @@ -418,6 +419,7 @@ "special_when_hit": [ "ZAPBACK", 75 ], "death_drops": "mon_zombie_bio_op_death_drops", "death_function": [ "NORMAL" ], + "fungalize_into": "mon_zombie_fungus", "flags": [ "SEES", "HEARS", diff --git a/data/json/monsters/zed_survivor.json b/data/json/monsters/zed_survivor.json index f07140b5ddddc..e6b3913ff5ea2 100644 --- a/data/json/monsters/zed_survivor.json +++ b/data/json/monsters/zed_survivor.json @@ -38,6 +38,7 @@ "death_drops": "mon_zombie_survivor_death_drops", "death_function": [ "NORMAL" ], "burn_into": "mon_zombie_scorched", + "fungalize_into": "mon_zombie_fungus", "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "BASHES", "GROUP_BASH", "POISON", "NO_BREATHE", "REVIVES", "PUSH_MON", "FILTHY" ] }, { diff --git a/data/json/mutations/mutation_appearance.json b/data/json/mutations/mutation_appearance.json index 79d025513ed1b..6b8aebf137567 100644 --- a/data/json/mutations/mutation_appearance.json +++ b/data/json/mutations/mutation_appearance.json @@ -479,6 +479,78 @@ "player_display": false, "types": [ "hair_style" ] }, + { + "id": "eye_blue", + "type": "mutation", + "name": { "str": "Eye color: blue" }, + "description": "You have blue eyes.", + "points": 0, + "starting_trait": true, + "valid": false, + "purifiable": false, + "player_display": false, + "types": [ "eye_color" ] + }, + { + "id": "eye_brown", + "type": "mutation", + "name": { "str": "Eye color: brown" }, + "description": "You have brown eyes.", + "points": 0, + "starting_trait": true, + "valid": false, + "purifiable": false, + "player_display": false, + "types": [ "eye_color" ] + }, + { + "id": "eye_hazel", + "type": "mutation", + "name": { "str": "Eye color: hazel" }, + "description": "You have hazel eyes.", + "points": 0, + "starting_trait": true, + "valid": false, + "purifiable": false, + "player_display": false, + "types": [ "eye_color" ] + }, + { + "id": "eye_amber", + "type": "mutation", + "name": { "str": "Eye color: amber" }, + "description": "You have amber eyes.", + "points": 0, + "starting_trait": true, + "valid": false, + "purifiable": false, + "player_display": false, + "types": [ "eye_color" ] + }, + { + "id": "eye_gray", + "type": "mutation", + "name": { "str": "Eye color: gray" }, + "description": "You have gray eyes.", + "points": 0, + "starting_trait": true, + "valid": false, + "purifiable": false, + "player_display": false, + "types": [ "eye_color" ] + }, + { + "id": "eye_green", + "type": "mutation", + "name": { "str": "Eye color: green" }, + "description": "You have green eyes.", + "points": 0, + "starting_trait": true, + "valid": false, + "purifiable": false, + "player_display": false, + "types": [ "eye_color" ] + }, { "id": "SKIN_DARK", "type": "mutation", diff --git a/data/json/mutations/mutation_enchantments.json b/data/json/mutations/mutation_enchantments.json deleted file mode 100644 index 344f99a397d78..0000000000000 --- a/data/json/mutations/mutation_enchantments.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "type": "enchantment", - "id": "MEP_INK_GLAND_SPRAY", - "hit_me_effect": [ - { - "id": "generic_blinding_spray_1", - "hit_self": false, - "once_in": 15, - "message": "Your ink glands spray some ink into %2$s's eyes.", - "npc_message": "%1$s's ink glands spay some ink into %2$s's eyes." - } - ] - } -] diff --git a/data/json/mutations/mutation_type.json b/data/json/mutations/mutation_type.json index 56e4f91bff305..97807fedc1330 100644 --- a/data/json/mutations/mutation_type.json +++ b/data/json/mutations/mutation_type.json @@ -71,6 +71,10 @@ "type": "mutation_type", "id": "skin_tone" }, + { + "type": "mutation_type", + "id": "eye_color" + }, { "type": "mutation_type", "id": "hair_style" diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index c05898734bfeb..9900408955dda 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -304,7 +304,8 @@ "starting_trait": true, "valid": false, "cancels": [ "INSOMNIA" ], - "category": [ "MOUSE", "INSECT" ] + "category": [ "MOUSE", "INSECT" ], + "enchantments": [ { "condition": "ALWAYS", "values": [ { "value": "SLEEPY", "add": 24 } ] } ] }, { "type": "mutation", @@ -315,7 +316,8 @@ "prereqs": [ "MET_RAT" ], "cancels": [ "INSOMNIA" ], "threshreq": [ "THRESH_MOUSE" ], - "category": [ "MOUSE" ] + "category": [ "MOUSE" ], + "enchantments": [ { "condition": "ALWAYS", "values": [ { "value": "SLEEPY", "add": 40 } ] } ] }, { "type": "mutation", @@ -1060,6 +1062,7 @@ "starting_trait": true, "valid": false, "category": [ "MEDICAL" ], + "enchantments": [ { "condition": "ALWAYS", "values": [ { "value": "SLEEPY", "add": -12 } ] } ], "cancels": [ "EASYSLEEPER" ] }, { @@ -2523,7 +2526,7 @@ "points": 3, "visibility": 8, "ugliness": 3, - "description": "All the hair on your body has turned to long, grass-like leaves. Apart from being physically striking, these provide you with a minor amount of nutrition while in sunlight when your head is uncovered. Slightly reduces wet effects.", + "description": "All the hair on your body has turned to long, grass-like leaves. Apart from being physically striking, these provide you with a minor amount of nutrients while in sunlight when your head is uncovered. Slightly reduces wet effects.", "prereqs": [ "PLANTSKIN", "BARK" ], "changes_to": [ "LEAVES2" ], "category": [ "PLANT", "ELFA" ], @@ -2536,7 +2539,7 @@ "points": 6, "visibility": 10, "ugliness": 5, - "description": "Your leaves have grown in size and prominence, with additional leaves sprouting along your arms. While your arms and head are uncovered, you will photosynthesize additional nutrients while in sunlight. Reduces wet effects.", + "description": "Your leaves have grown in size and prominence, with additional leaves sprouting along your arms. While your arms and head are uncovered, you will photosynthesize nutrients while in sunlight. Reduces wet effects.", "prereqs": [ "LEAVES" ], "prereqs2": [ "TRANSPIRATION" ], "threshreq": [ "THRESH_PLANT" ], @@ -2552,7 +2555,7 @@ "points": 9, "visibility": 12, "ugliness": 6, - "description": "Your leaves are vibrant, large, and green, and have become a major source of nutrition for your body. Whenever your arms and head are uncovered you will gain a large amount of nutrition by standing in the sunlight. Reduces wet effects.", + "description": "Your leaves are vibrant, large, and green, and have become a major source of nutrients for your body. Whenever your arms and head are uncovered you will gain a large amount of nutrients by standing in the sunlight. Reduces wet effects.", "prereqs": [ "LEAVES2" ], "prereqs2": [ "TRANSPIRATION" ], "threshreq": [ "THRESH_PLANT" ], @@ -2812,7 +2815,19 @@ "visibility": 1, "ugliness": 1, "description": "Several ink glands have grown onto your torso. They can be used to spray defensive ink and blind an attacker in an emergency, as long as the torso isn't covered.", - "enchantments": [ "MEP_INK_GLAND_SPRAY" ], + "enchantments": [ + { + "hit_me_effect": [ + { + "id": "generic_blinding_spray_1", + "hit_self": false, + "once_in": 15, + "message": "Your ink glands spray some ink into %2$s's eyes.", + "npc_message": "%1$s's ink glands spray some ink into %2$s's eyes." + } + ] + } + ], "category": [ "CEPHALOPOD" ] }, { @@ -3162,7 +3177,7 @@ "points": -3, "visibility": 5, "ugliness": 9, - "description": "You prefer to sustain yourself using your roots and direct nutrient extraction/synthesis. You can still consume 'food', though, if you have to: you merely prefer it aged a little.", + "description": "You prefer to sustain your nutritional needs using your roots and direct nutrient extraction/synthesis. Your human parts still need the raw caloric bulk of 'food' to keep them going, though, and you handle it a lot easier if it's had time to… age a little.", "prereqs": [ "ROOTS2" ], "prereqs2": [ "LEAVES" ], "threshreq": [ "THRESH_PLANT" ], @@ -6493,6 +6508,18 @@ "name": { "str": "US Marshal" }, "points": 0, "description": "You are a duly sworn Federal marshal, with nationwide jurisdiction and the authority of the United States of America.", + "cancels": [ "PROF_HUB01_ANCILLIARY" ], + "valid": false, + "purifiable": false, + "profession": true + }, + { + "type": "mutation", + "id": "PROF_HUB01_ANCILLIARY", + "name": { "str": "Hub 01 Ancilliary" }, + "points": 0, + "description": "You are known to be a capable and resourceful mercenary at the employ of Hub 01.", + "cancels": [ "PROF_FED" ], "valid": false, "purifiable": false, "profession": true diff --git a/data/json/npcs/Backgrounds/evacuee_5.json b/data/json/npcs/Backgrounds/evacuee_5.json index 054226d4c8b73..bfd88f16a16f2 100644 --- a/data/json/npcs/Backgrounds/evacuee_5.json +++ b/data/json/npcs/Backgrounds/evacuee_5.json @@ -2,7 +2,7 @@ { "id": "BGSS_EVACUEE_5_STORY1", "type": "talk_topic", - "dynamic_line": "My Evac shelter got swarmed by some of those bees, the ones the size of dogs. I took out a few with a two-by-four, but pretty quick I realized it was either head for the hills or get stuck like a pig. The rest is history.", + "dynamic_line": "My Evac shelter got swarmed by some of those bees, the ones the size of dogs. I took out a few with a plank, but pretty quick I realized it was either head for the hills or get stuck like a pig. The rest is history.", "//": "TK: In a future iteration, this evacuee might give you directions to a hive.", "responses": [ { "text": "Giant bees? Tell me more.", "topic": "BGSS_EVACUEE_5_BEES" }, diff --git a/data/json/npcs/Backgrounds/no_past_4.json b/data/json/npcs/Backgrounds/no_past_4.json index d4194568a047a..1770b06b359d7 100644 --- a/data/json/npcs/Backgrounds/no_past_4.json +++ b/data/json/npcs/Backgrounds/no_past_4.json @@ -17,7 +17,7 @@ { "id": "BGSS_NO_PAST_4_STORY2", "type": "talk_topic", - "dynamic_line": "I went on, running when I had to and fighting when I could, like the rest of us. Started learning who I am now. Lost the bat in a fight against some crazy electric lightning shooting zombie. It was arcing electricity through my bat so I dropped it and used a nearby two-by-four, but I wound up having to run and leave the ol' slugger behind. I nearly died that day.", + "dynamic_line": "I went on, running when I had to and fighting when I could, like the rest of us. Started learning who I am now. Lost the bat in a fight against some crazy electric lightning shooting zombie. It was arcing electricity through my bat so I dropped it and used a nearby plank, but I wound up having to run and leave the ol' slugger behind. I nearly died that day.", "responses": [ { "text": "It can't be healthy to abandon your past like that…", diff --git a/data/json/npcs/NC_ARSONIST.json b/data/json/npcs/NC_ARSONIST.json index b724d245854b9..fac1c64e31ba5 100644 --- a/data/json/npcs/NC_ARSONIST.json +++ b/data/json/npcs/NC_ARSONIST.json @@ -194,7 +194,11 @@ "type": "item_group", "id": "NC_ARSONIST_rifle", "subtype": "distribution", - "entries": [ { "item": "m4a1", "prob": 35 }, { "item": "m1a", "prob": 35 }, { "item": "ak47", "prob": 35 } ] + "entries": [ + { "item": "nato_assault_rifle", "variant": "m4a1", "prob": 35 }, + { "item": "m1a", "prob": 35 }, + { "item": "ak47", "prob": 35 } + ] }, { "type": "item_group", diff --git a/data/json/npcs/NC_SOLDIER.json b/data/json/npcs/NC_SOLDIER.json index 61d44ec2aeb14..d5e6ccb771832 100644 --- a/data/json/npcs/NC_SOLDIER.json +++ b/data/json/npcs/NC_SOLDIER.json @@ -117,13 +117,13 @@ "type": "item_group", "id": "NC_SOLDIER_rifle", "subtype": "distribution", - "entries": [ { "item": "m4a1", "prob": 90 }, { "item": "m14ebr", "prob": 10 } ] + "entries": [ { "item": "nato_assault_rifle", "variant": "m4a1", "prob": 90 }, { "item": "m14ebr", "prob": 10 } ] }, { "type": "item_group", "id": "NC_SOLDIER_weapon_random", "subtype": "distribution", - "entries": [ { "item": "m4a1", "prob": 35 } ] + "entries": [ { "item": "nato_assault_rifle", "variant": "m4a1", "prob": 35 } ] }, { "type": "item_group", diff --git a/data/json/npcs/TALK_COMMON_ALLY.json b/data/json/npcs/TALK_COMMON_ALLY.json index 527c3f6bc2a69..72cfcbe055769 100644 --- a/data/json/npcs/TALK_COMMON_ALLY.json +++ b/data/json/npcs/TALK_COMMON_ALLY.json @@ -163,12 +163,6 @@ "topic": "TALK_FRIEND_GUARD", "effect": "assign_guard" }, - { - "text": "I want to assign you to work at this camp.", - "condition": { "npc_at_om_location": "FACTION_CAMP_ANY" }, - "topic": "TALK_FRIEND_GUARD", - "effect": "assign_camp" - }, { "text": "Find a horse and mount up!", "condition": { "not": "npc_is_riding" }, @@ -181,46 +175,8 @@ "topic": "TALK_DONE", "effect": "dismount" }, - { - "text": "Please go to this location.", - "topic": "TALK_GOTO_LOCATION", - "condition": { "or": [ "is_by_radio", "u_has_camp" ] }, - "effect": "goto_location" - }, - { - "text": "I want you to build a camp here.", - "topic": "TALK_HALLU_CAMP", - "condition": { "npc_has_trait": "HALLUCINATION" }, - "switch": true - }, - { - "text": "I want you to build a camp here.", - "topic": "TALK_DONE", - "effect": "start_camp", - "condition": { "npc_at_om_location": "FACTION_CAMP_START" }, - "switch": true, - "default": true - }, - { - "text": "Since we can't build a camp here, I want you to tell me where can we build a camp?", - "topic": "TALK_CAMP_SITES", - "condition": { "not": { "npc_at_om_location": "FACTION_CAMP_START" } }, - "switch": true, - "default": true - }, - { - "text": "We need to abandon this camp.", - "condition": { "npc_at_om_location": "FACTION_CAMP_ANY" }, - "topic": "TALK_DONE", - "effect": "abandon_camp" - }, - { - "text": "Show me what needs to be done at the camp.", - "topic": "TALK_DONE", - "effect": "basecamp_mission", - "condition": { "npc_at_om_location": "FACTION_CAMP_ANY" } - }, { "text": "Let's talk about your current activity.", "topic": "TALK_ACTIVITIES" }, + { "text": "Let's talk about the camp.", "topic": "TALK_CAMP" }, { "text": "Let's go.", "topic": "TALK_DONE" } ] }, @@ -931,5 +887,66 @@ "type": "talk_topic", "dynamic_line": "", "responses": [ { "text": "Fair enough.", "topic": "TALK_NONE" } ] + }, + { + "id": "TALK_CAMP", + "type": "talk_topic", + "dynamic_line": "What about the camp?", + "responses": [ + { + "text": "I want to assign you to work at this camp.", + "condition": { "npc_at_om_location": "FACTION_CAMP_ANY" }, + "topic": "TALK_FRIEND_GUARD", + "effect": "assign_camp" + }, + { + "text": "Please go to this location.", + "topic": "TALK_GOTO_LOCATION", + "condition": { "or": [ "is_by_radio", "u_has_camp" ] }, + "effect": "goto_location" + }, + { + "text": "I want you to build a camp here.", + "topic": "TALK_HALLU_CAMP", + "condition": { "npc_has_trait": "HALLUCINATION" }, + "switch": true + }, + { + "text": "I want you to build a camp here.", + "topic": "TALK_DONE", + "effect": "start_camp", + "condition": { "npc_at_om_location": "FACTION_CAMP_START" }, + "switch": true, + "default": true + }, + { + "text": "Since we can't build a camp here, I want you to tell me where can we build a camp?", + "topic": "TALK_CAMP_SITES", + "condition": { "not": { "npc_at_om_location": "FACTION_CAMP_START" } }, + "switch": true, + "default": true + }, + { + "text": "We need to abandon this camp.", + "condition": { "npc_at_om_location": "FACTION_CAMP_ANY" }, + "topic": "TALK_ABANDON" + }, + { + "text": "Show me what needs to be done at the camp.", + "topic": "TALK_DONE", + "effect": "basecamp_mission", + "condition": { "npc_at_om_location": "FACTION_CAMP_ANY" } + }, + { "text": "Never mind.", "topic": "TALK_NONE" } + ] + }, + { + "id": "TALK_ABANDON", + "type": "talk_topic", + "dynamic_line": "Are you sure?", + "responses": [ + { "text": "Yes, I'm sure.", "topic": "TALK_DONE", "effect": "abandon_camp" }, + { "text": "Actually, never mind.", "topic": "TALK_NONE" } + ] } ] diff --git a/data/json/npcs/TALK_COMMON_MISSION.json b/data/json/npcs/TALK_COMMON_MISSION.json index 4bcee75e8b9cb..f60741645f8b4 100644 --- a/data/json/npcs/TALK_COMMON_MISSION.json +++ b/data/json/npcs/TALK_COMMON_MISSION.json @@ -324,5 +324,19 @@ { "text": "Thank you.", "topic": "TALK_NONE", "effect": "clear_mission" }, { "text": "Thanks, bye.", "topic": "TALK_DONE", "effect": "clear_mission" } ] + }, + { + "//": "MISSION_PYROMANIAC mission set this topic as starting topic for mission giver.", + "type": "talk_topic", + "id": "MISSION_PYROMANIAC", + "dynamic_line": "Are we there yet, ? I can't wait to burn that building!", + "responses": [ + { + "text": "We're here. Let's do it!", + "topic": "TALK_MISSION_INQUIRE", + "condition": { "and": [ "mission_complete", { "u_has_items": { "item": "gasoline", "count": 200 } } ] } + }, + { "text": "Be patient, , we're getting there soon.", "topic": "TALK_DONE" } + ] } ] diff --git a/data/json/npcs/appearance_trait_groups.json b/data/json/npcs/appearance_trait_groups.json index c4c7a36542022..e70abf1547bf3 100644 --- a/data/json/npcs/appearance_trait_groups.json +++ b/data/json/npcs/appearance_trait_groups.json @@ -19,49 +19,49 @@ "type": "trait_group", "id": "Appearance_Irish", "subtype": "collection", - "traits": [ { "group": "Hair_Irish", "prob": 100 }, { "group": "Skin_Fair", "prob": 100 } ] + "traits": [ { "group": "Hair_Irish", "prob": 100 }, { "group": "Skin_Fair", "prob": 100 }, { "group": "Eye_light", "prob": 100 } ] }, { "type": "trait_group", "id": "Appearance_Caucasian", "subtype": "collection", - "traits": [ { "group": "Hair_Any", "prob": 100 }, { "group": "Skin_White", "prob": 100 } ] + "traits": [ { "group": "Hair_Any", "prob": 100 }, { "group": "Skin_White", "prob": 100 }, { "group": "Eye_any", "prob": 100 } ] }, { "type": "trait_group", "id": "Appearance_Germanic", "subtype": "collection", - "traits": [ { "group": "Hair_Fair", "prob": 100 }, { "group": "Skin_Fair", "prob": 100 } ] + "traits": [ { "group": "Hair_Fair", "prob": 100 }, { "group": "Skin_Fair", "prob": 100 }, { "group": "Eye_light", "prob": 100 } ] }, { "type": "trait_group", "id": "Appearance_Latin", "subtype": "collection", - "traits": [ { "group": "Hair_Dark", "prob": 100 }, { "group": "Skin_Medium", "prob": 100 } ] + "traits": [ { "group": "Hair_Dark", "prob": 100 }, { "group": "Skin_Medium", "prob": 100 }, { "group": "Eye_dark", "prob": 100 } ] }, { "type": "trait_group", "id": "Appearance_African", "subtype": "collection", - "traits": [ { "group": "Hair_Dark", "prob": 100 }, { "group": "Skin_Dark", "prob": 100 } ] + "traits": [ { "group": "Hair_Dark", "prob": 100 }, { "group": "Skin_Dark", "prob": 100 }, { "group": "Eye_dark", "prob": 100 } ] }, { "type": "trait_group", "id": "Appearance_EastAsian", "subtype": "collection", - "traits": [ { "group": "Hair_Dark", "prob": 100 }, { "trait": "SKIN_TAN", "prob": 100 } ] + "traits": [ { "group": "Hair_Dark", "prob": 100 }, { "trait": "SKIN_TAN", "prob": 100 }, { "group": "Eye_dark", "prob": 100 } ] }, { "type": "trait_group", "id": "Appearance_SouthAsian", "subtype": "collection", - "traits": [ { "group": "Hair_Dark", "prob": 100 }, { "trait": "SKIN_TAN", "prob": 100 } ] + "traits": [ { "group": "Hair_Dark", "prob": 100 }, { "trait": "SKIN_TAN", "prob": 100 }, { "group": "Eye_dark", "prob": 100 } ] }, { "type": "trait_group", "id": "Appearance_Mixed", "subtype": "collection", - "traits": [ { "group": "Hair_Any", "prob": 100 }, { "group": "Skin_Any", "prob": 100 } ] + "traits": [ { "group": "Hair_Any", "prob": 100 }, { "group": "Skin_Any", "prob": 100 }, { "group": "Eye_any", "prob": 100 } ] }, { "type": "trait_group", @@ -153,6 +153,31 @@ "subtype": "distribution", "traits": [ { "trait": "SKIN_DARK", "prob": 50 }, { "trait": "SKIN_MEDIUM", "prob": 5 }, { "trait": "SKIN_TAN", "prob": 25 } ] }, + { + "type": "trait_group", + "id": "Eye_light", + "subtype": "distribution", + "traits": [ { "trait": "eye_blue", "prob": 55 }, { "trait": "eye_gray", "prob": 25 }, { "trait": "eye_green", "prob": 15 } ] + }, + { + "type": "trait_group", + "id": "Eye_dark", + "subtype": "distribution", + "traits": [ { "trait": "eye_brown", "prob": 60 }, { "trait": "eye_hazel", "prob": 20 }, { "trait": "eye_amber", "prob": 20 } ] + }, + { + "type": "trait_group", + "id": "Eye_any", + "subtype": "distribution", + "traits": [ + { "trait": "eye_brown", "prob": 70 }, + { "trait": "eye_hazel", "prob": 9 }, + { "trait": "eye_amber", "prob": 8 }, + { "trait": "eye_blue", "prob": 8 }, + { "trait": "eye_gray", "prob": 3 }, + { "trait": "eye_green", "prob": 2 } + ] + }, { "type": "trait_group", "id": "Hair_Black", diff --git a/data/json/npcs/factions.json b/data/json/npcs/factions.json index f19057b1d75f1..574ebb641d86b 100644 --- a/data/json/npcs/factions.json +++ b/data/json/npcs/factions.json @@ -75,6 +75,7 @@ }, "marloss": { "kill on sight": true } }, + "epilogues": [ { "power_min": 0, "id": "epilogue_faction_robofac_0" }, { "power_max": 150, "id": "epilogue_faction_robofac_150" } ], "description": "The surviving staff of Hub 01, a pre-Cataclysm research lab. They rarely leave their lab, if at all, and rely on their robots and advanced technology to survive." }, { diff --git a/data/json/npcs/hints.json b/data/json/npcs/hints.json index d25e659098279..e337a28c28e1c 100644 --- a/data/json/npcs/hints.json +++ b/data/json/npcs/hints.json @@ -44,7 +44,7 @@ "For a good melee weapon, you can't beat a machete. I've seen a guy take down a zombie brute with one! Of course, if you can find a katana, that might be even better…", "A knife spear makes a good weapon in a pinch, but a spike strapped to a stick isn't the sturdiest construction. At least you can strap the spike back on when it comes off.", "You know, a glass bottle can make a good weapon in a pinch. If you break it over someone's head, the shattering glass will hurt them extra. Of course, it might hurt your hands, too…", - "You know what makes a nice weapon? Take a two by four, or a baseball bat or something, and stick a bunch of nails through the end!", + "You know what makes a nice weapon? Take a plank, or a baseball bat or something, and stick a bunch of nails through the end!", "BB guns may seem like a joke, but they've got their uses. They're good for hunting small game, or getting to know the basics of rifles.", "Crossbows are a great weapon for long term use. Most of the time, you can retrieve the bolt after shooting it, so running out of ammo is less of a concern.", "Consider going Robin Hood, if you have the strength to pull the string of a bow. Those larger ones need significant muscle power, but they hit hard, and are silent.", diff --git a/data/json/npcs/island_prison/prisoners.json b/data/json/npcs/island_prison/prisoners.json index 7e2e37815686f..8128c191aba72 100644 --- a/data/json/npcs/island_prison/prisoners.json +++ b/data/json/npcs/island_prison/prisoners.json @@ -104,7 +104,8 @@ [ "scissors", 5 ], [ "screwdriver", 5 ], [ "knife_steak", 5 ], - [ "knuckle_brass", 5 ], + [ "knuckle_brass", 3 ], + [ "knuckle_steel_forged", 2 ], [ "switchblade", 5 ], [ "throwing_knife", 5 ], { diff --git a/data/json/npcs/mine/spiral_madman.json b/data/json/npcs/mine/spiral_madman.json new file mode 100644 index 0000000000000..5a130d2345717 --- /dev/null +++ b/data/json/npcs/mine/spiral_madman.json @@ -0,0 +1,126 @@ +[ + { + "type": "npc", + "id": "spiral_madman", + "//": "Former miner, poor fellow succumbed to spiral madness.", + "name_suffix": "miner", + "class": "NC_SPIRAL_MADMAN", + "attitude": 0, + "mission": 8, + "chat": "TALK_SPIRAL_MADMAN_FIRST_MEETING", + "faction": "no_faction" + }, + { + "type": "npc_class", + "id": "NC_SPIRAL_MADMAN", + "name": { "str": "Miner" }, + "job_description": "Spirals.", + "traits": [ { "group": "NPC_starting_traits" }, { "group": "Appearance_demographics" } ], + "bonus_str": { "rng": [ 2, 4 ] }, + "bonus_dex": { "rng": [ 0, 2 ] }, + "worn_override": "NC_SPIRAL_MADMAN_worn", + "weapon_override": "NC_SPIRAL_MADMAN_wield" + }, + { + "type": "item_group", + "id": "NC_SPIRAL_MADMAN_worn", + "subtype": "collection", + "entries": [ + { "group": "clothing_work_boots", "damage": [ 0, 2 ] }, + { "group": "clothing_work_glasses", "prob": 60, "damage": [ 0, 2 ] }, + { "group": "clothing_work_gloves", "prob": 75, "damage": [ 0, 2 ] }, + { "group": "clothing_work_mask", "prob": 40, "damage": [ 0, 2 ] }, + { "item": "ear_plugs", "prob": 15, "damage": [ 0, 2 ] }, + { "item": "tool_belt", "prob": 25, "damage": [ 0, 2 ] }, + { + "distribution": [ + { + "collection": [ { "group": "clothing_work_pants", "damage": [ 0, 2 ] }, { "group": "clothing_work_torso", "damage": [ 0, 2 ] } ], + "prob": 75 + }, + { "item": "jumpsuit", "prob": 25, "damage": [ 0, 2 ] } + ] + }, + { "group": "underwear", "damage": [ 0, 1 ] }, + { "item": "miner_hat", "prob": 90, "damage": [ 0, 2 ] } + ] + }, + { + "type": "item_group", + "id": "NC_SPIRAL_MADMAN_wield", + "items": [ + [ "pickaxe", 50 ], + [ "shovel", 50 ], + [ "bucket", 20 ], + [ "gasoline_lantern", 10 ], + [ "electric_lantern", 10 ], + [ "oil_lamp", 10 ], + [ "jackhammer", 5 ], + [ "elec_jackhammer", 5 ] + ] + }, + { + "id": "TALK_SPIRAL_MADMAN_FIRST_MEETING", + "type": "talk_topic", + "dynamic_line": "Spirals?", + "speaker_effect": { "effect": { "u_add_var": "first_meeting", "type": "dialogue", "context": "first_meeting", "value": "yes" } }, + "responses": [ + { + "text": "Hello there!", + "topic": "TALK_SPIRAL_MADMAN_GENERIC", + "condition": { "not": { "u_has_var": "first_meeting", "type": "dialogue", "context": "first_meeting", "value": "yes" } } + }, + { + "text": "I'm outta here. Bye.", + "topic": "TALK_DONE", + "condition": { "not": { "u_has_var": "first_meeting", "type": "dialogue", "context": "first_meeting", "value": "yes" } } + }, + { + "text": "Poor fellow - looks like you're out of your mind. Can't help you, sorry. I'm outta here. Bye.", + "topic": "TALK_DONE", + "condition": { "u_has_var": "first_meeting", "type": "dialogue", "context": "first_meeting", "value": "yes" } + } + ] + }, + { + "id": "TALK_SPIRAL_MADMAN_GENERIC", + "type": "talk_topic", + "dynamic_line": "", + "responses": [ + { "text": "What are you doing here?", "topic": "TALK_SPIRAL_MADMAN_SPIRALS" }, + { "text": "Sorry, what?", "topic": "TALK_SPIRAL_MADMAN_SPIRALS" }, + { "text": "I'm outta here. Bye.", "topic": "TALK_DONE" } + ] + }, + { + "id": "TALK_SPIRAL_MADMAN_SPIRALS", + "type": "talk_topic", + "dynamic_line": "", + "responses": [ + { "text": "Why are you repeating 'spirals' over and over again?", "topic": "TALK_SPIRAL_MADMAN_R_U_NUTS" }, + { "text": "Sorry, what?", "topic": "TALK_SPIRAL_MADMAN_R_U_NUTS" }, + { "text": "I'm outta here. Bye.", "topic": "TALK_DONE" } + ] + }, + { + "id": "TALK_SPIRAL_MADMAN_R_U_NUTS", + "type": "talk_topic", + "dynamic_line": "", + "responses": [ + { "text": "Are you nuts?", "topic": "TALK_SPIRAL_MADMAN_THIS_IS_MADNESS" }, + { "text": "Sorry, what?", "topic": "TALK_SPIRAL_MADMAN_THIS_IS_MADNESS" }, + { "text": "I'm outta here. Bye.", "topic": "TALK_DONE" } + ] + }, + { + "id": "TALK_SPIRAL_MADMAN_THIS_IS_MADNESS", + "type": "talk_topic", + "dynamic_line": "", + "responses": [ + { + "text": "Poor fellow - looks like you're out of your mind. Can't help you, sorry. I'm outta here. Bye.", + "topic": "TALK_DONE" + } + ] + } +] diff --git a/data/json/npcs/missiondef.json b/data/json/npcs/missiondef.json index 0ad333b4a54fc..109e4a3df41a2 100644 --- a/data/json/npcs/missiondef.json +++ b/data/json/npcs/missiondef.json @@ -817,5 +817,53 @@ "success_lie": "OK, then hand them over.", "failure": "Well, that's a shame." } + }, + { + "id": "MISSION_PYROMANIAC", + "type": "mission_definition", + "name": { "str": "Angry pyromaniac" }, + "goal": "MGOAL_GO_TO", + "difficulty": 0, + "value": 0, + "//": "NPC will ask player to burn tagged house. Will follow player after accepting mission.", + "//2": "Change first topic to do proper checks. MGOAL_GO_TO to check if player stands on tagged tile.", + "//3": "At the end of the mission, will place fire on tagged tile and NPC will join player faction.", + "has_generic_rewards": false, + "start": { + "effect": [ "follow_only", { "npc_first_topic": "MISSION_PYROMANIAC" } ], + "assign_mission_target": { + "om_terrain": "house", + "om_terrain_replace": "forest", + "om_terrain_match_type": "PREFIX", + "search_range": 75, + "random": true, + "z": 0 + } + }, + "end": { + "effect": [ "follow", { "u_consume_item": "gasoline", "count": 200 }, { "mapgen_update": "MISSION_PYROMANIAC_BURN" } ] + }, + "origins": [ "ORIGIN_OPENER_NPC" ], + "dialogue": { + "describe": "Oh man, I want to burn it so bad…", + "offer": "I'm so infuriated! I've got an enemy that ruined my life, and now I want to get revenge! I don't care about , I just want to burn his house! Would you help me, ?", + "accepted": "Good. Let's go to his house and burn it down! Oh, by the way, could you bring gasoline with you, ? I was so angry I forgot to bring it with me…", + "rejected": "Seriously? It's such an easy job…", + "advice": "Maybe we can find some gasoline at gas station.", + "inquire": "Are you ready, ?", + "success": "Sweet, sweet revenge! Ah, smells so nice! Feels like the smell of napalm in the morning! All his stuff, and probably even that , will burn to ashes in a matter of minutes. You helped me get my revenge, so I'll follow you to the end, !", + "success_lie": "What?! You liar!", + "failure": "Wow, you failed? How…" + } + }, + { + "//": "For MISSION_PYROMANIAC. Will spawn fire on tile where PC is standing.", + "type": "mapgen", + "update_mapgen_id": "MISSION_PYROMANIAC_BURN", + "method": "json", + "object": { + "place_liquids": [ { "liquid": "gasoline", "x": [ 5, 20 ], "y": [ 5, 20 ], "amount": 5, "repeat": 40 } ], + "place_fields": [ { "field": "fd_fire", "x": [ 5, 20 ], "y": [ 5, 20 ], "repeat": 40 } ] + } } ] diff --git a/data/json/npcs/refugee_center/surface_refugees/NPC_Boris_Borichenko.json b/data/json/npcs/refugee_center/surface_refugees/NPC_Boris_Borichenko.json index c0b5d6d9fe7fe..c6a8eeb3109ca 100644 --- a/data/json/npcs/refugee_center/surface_refugees/NPC_Boris_Borichenko.json +++ b/data/json/npcs/refugee_center/surface_refugees/NPC_Boris_Borichenko.json @@ -480,7 +480,7 @@ }, "dialogue": { "describe": "Find Boris' son's laptop.", - "offer": "If you can find it, it would mean so much to me. I will give you directions to the shelter where he left it. It was barely a shelter, broken and torn apart. It should just be on the floor where it was dropped.", + "offer": "My son, Ash, had a laptop on which he kept his writing. If you can find it, it would mean so much to me. I will give you directions to the shelter where he left it. It was barely a shelter, broken and torn apart. It should just be on the floor where it was dropped.", "accepted": "You are a kind soul.", "rejected": "Ah well. I can ask around myself, perhaps.", "advice": "You can tell it is his because he covered it in stickers.", diff --git a/data/json/npcs/refugee_center/surface_refugees/NPC_Jenny_Forcette.json b/data/json/npcs/refugee_center/surface_refugees/NPC_Jenny_Forcette.json index a842d449da979..92126eb22009f 100644 --- a/data/json/npcs/refugee_center/surface_refugees/NPC_Jenny_Forcette.json +++ b/data/json/npcs/refugee_center/surface_refugees/NPC_Jenny_Forcette.json @@ -255,7 +255,10 @@ "id": "TALK_REFUGEE_JENNY_Teach2", "dynamic_line": "All right, fine. Grab some tools and do as I tell you.", "speaker_effect": { "effect": { "npc_add_var": "Jenny_teach", "type": "timer", "context": "flag", "time": true } }, - "responses": [ { "text": "Just say the word, teach.", "topic": "TALK_TRAIN" } ] + "responses": [ + { "text": "Just say the word, teach.", "topic": "TALK_TRAIN" }, + { "text": "Actually, I'd better get going. Let's do this later.", "topic": "TALK_DONE" } + ] }, { "type": "talk_topic", diff --git a/data/json/npcs/refugee_center/surface_refugees/NPC_Uyen_Tran.json b/data/json/npcs/refugee_center/surface_refugees/NPC_Uyen_Tran.json index 0653bd650cbe7..5d87f6d91f973 100644 --- a/data/json/npcs/refugee_center/surface_refugees/NPC_Uyen_Tran.json +++ b/data/json/npcs/refugee_center/surface_refugees/NPC_Uyen_Tran.json @@ -157,9 +157,8 @@ { "id": "MISSION_REFUGEE_Uyen_1", "type": "mission_definition", - "name": { - "str": "Find 50 doses of antiseptic for Uyen Tran in the refugee center, in exchange for Merch." - }, + "name": { "str": "Find 50 doses of antiseptic" }, + "description": "Find 50 doses of antiseptic for Uyen Tran in the refugee center, in exchange for Merch.", "goal": "MGOAL_FIND_ITEM", "difficulty": 2, "value": 0, @@ -189,7 +188,8 @@ { "id": "MISSION_REFUGEE_Uyen_2", "type": "mission_definition", - "name": { "str": "Find 30 bandages for Uyen Tran in the refugee center, in exchange for Merch." }, + "name": { "str": "Find 30 bandages" }, + "description": "Find 30 bandages for Uyen Tran in the refugee center, in exchange for Merch.", "goal": "MGOAL_FIND_ITEM", "difficulty": 2, "value": 0, @@ -219,7 +219,8 @@ { "id": "MISSION_REFUGEE_Uyen_3", "type": "mission_definition", - "name": { "str": "Find 6 bottles of Prozac for Uyen Tran in the refugee center, in exchange for Merch." }, + "name": { "str": "Find 6 bottles of Prozac" }, + "description": "Find 6 bottles of Prozac for Uyen Tran in the refugee center, in exchange for Merch.", "goal": "MGOAL_FIND_ITEM", "difficulty": 2, "value": 0, diff --git a/data/json/npcs/refugee_center/surface_staff/NPC_old_guard_doctor.json b/data/json/npcs/refugee_center/surface_staff/NPC_old_guard_doctor.json index dd4421cb5b79e..09a336dac48f3 100644 --- a/data/json/npcs/refugee_center/surface_staff/NPC_old_guard_doctor.json +++ b/data/json/npcs/refugee_center/surface_staff/NPC_old_guard_doctor.json @@ -145,7 +145,7 @@ "followup": "MISSION_SCIENCE_REP_5", "dialogue": { "describe": "We need help…", - "offer": "So there looks to be months, maybe years of experiments, and that data set must be huge. Database servers massive enough to house it would overheat running on emergency power. But I did found communications from a lab that had some kind of freezing portal open during the Cataclysm, sending everything to subzero temperatures. I bet the archives inside that lab are still working.", + "offer": "So there looks to be months, maybe years of experiments, and that data set must be huge. Database servers massive enough to house it would overheat running on emergency power. But I did find communications from a lab that had some kind of freezing portal open during the Cataclysm, sending everything to subzero temperatures. I bet the archives inside that lab are still working.", "accepted": "Great! I've mapped out a route. Bundle up, it gets colder the deeper you go and it looks like the archives were on the fourth basement level.", "rejected": "Can't blame you, but come back if you change your mind.", "advice": "That lab is going to start freezing and just get colder the deeper you go. You'll really need special equipment to survive that far down. Bring back anything you find on a USB drive.", diff --git a/data/json/npcs/refugee_center/surface_visitors/NPC_scavenger_mercenary.json b/data/json/npcs/refugee_center/surface_visitors/NPC_scavenger_mercenary.json index ae95e41ae4a01..4484feb847eae 100644 --- a/data/json/npcs/refugee_center/surface_visitors/NPC_scavenger_mercenary.json +++ b/data/json/npcs/refugee_center/surface_visitors/NPC_scavenger_mercenary.json @@ -73,7 +73,13 @@ "id": "NC_SCAVENGER_MERC_wield", "subtype": "collection", "items": [ - { "item": "m4a1", "ammo-item": "556", "charges": 30, "contents-item": [ "shoulder_strap", "holo_sight", "suppressor" ] } + { + "item": "nato_assault_rifle", + "variant": "m4a1", + "ammo-item": "556", + "charges": 30, + "contents-item": [ "shoulder_strap", "holo_sight", "suppressor" ] + } ] }, { diff --git a/data/json/npcs/robofac/NPC_ROBOFAC_MERC_1.json b/data/json/npcs/robofac/NPC_ROBOFAC_MERC_1.json index 6b61c9176f927..41f2b45f56bc9 100644 --- a/data/json/npcs/robofac/NPC_ROBOFAC_MERC_1.json +++ b/data/json/npcs/robofac/NPC_ROBOFAC_MERC_1.json @@ -65,7 +65,13 @@ "id": "NC_ROBOFAC_MERC_1_wield", "subtype": "collection", "items": [ - { "item": "acr", "ammo-item": "556", "charges": 30, "contents-item": [ "shoulder_strap", "holo_sight", "suppressor" ] } + { + "item": "nato_assault_rifle", + "variant": "acr", + "ammo-item": "556", + "charges": 30, + "contents-item": [ "shoulder_strap", "holo_sight", "suppressor" ] + } ] }, { diff --git a/data/json/npcs/robofac/robofac_basement_npcs/NPC_Andrea_Dzvonko.json b/data/json/npcs/robofac/robofac_basement_npcs/NPC_Andrea_Dzvonko.json new file mode 100644 index 0000000000000..88cb804016e21 --- /dev/null +++ b/data/json/npcs/robofac/robofac_basement_npcs/NPC_Andrea_Dzvonko.json @@ -0,0 +1,80 @@ +[ + { + "type": "npc", + "id": "HUB01_ADzvonko", + "name_unique": "Andrea Dzvonko", + "gender": "female", + "name_suffix": "software engineer", + "class": "NC_HUB01_ADzvonko", + "attitude": 0, + "mission": 7, + "chat": "HUB01_ADzvonko_Intro", + "faction": "robofac" + }, + { + "type": "npc_class", + "id": "NC_HUB01_ADzvonko", + "name": { "str": "Hub 01 Software Engineer" }, + "job_description": "I work for Hub 01 as a software engineer.", + "common": false, + "//": "Andrea Dzvonko is a software engineer with a limited understanding of how melchior actually functions.", + "bonus_str": { "rng": [ -2, 2 ] }, + "bonus_dex": { "rng": [ -2, 2 ] }, + "bonus_int": { "rng": [ 0, 4 ] }, + "bonus_per": { "rng": [ -2, 2 ] }, + "worn_override": "HUB01_ADzvonko_worn", + "carry_override": "HUB01_ADzvonko_carried", + "weapon_override": "HUB01_ADzvonko_wield", + "traits": [ { "trait": "FASTLEARNER" }, { "group": "Appearance_Caucasian" } ], + "skills": [ + { "skill": "ALL", "level": { "mul": [ { "one_in": 3 }, { "sum": [ { "dice": [ 2, 2 ] }, { "rng": [ 0, -4 ] } ] } ] } }, + { "skill": "computer", "bonus": { "rng": [ 4, 6 ] } } + ] + }, + { + "type": "item_group", + "id": "HUB01_ADzvonko_worn", + "subtype": "collection", + "entries": [ + { "item": "bra" }, + { "item": "panties" }, + { "item": "socks" }, + { "item": "robofac_jumpsuit" }, + { "item": "sneakers" }, + { "item": "sweater" } + ] + }, + { + "type": "item_group", + "id": "HUB01_ADzvonko_carried", + "subtype": "collection", + "entries": [ { "group": "wallets_science" } ] + }, + { + "type": "item_group", + "id": "HUB01_ADzvonko_wield" + }, + { + "type": "talk_topic", + "id": "HUB01_ADzvonko_Intro", + "dynamic_line": { + "u_has_trait": "PROF_HUB01_ANCILLIARY", + "yes": "Woah, you must be that new person they told us about. I can't really believe you're here. I'm Andrea. Dr. Dzvonko in the old days, but that seems silly now.", + "no": "Who the fuck are you? How did you get down here? Security!" + }, + "responses": [ + { + "text": "Hold on, hold on. I'm not here to hurt anyone, and there aren't enough of us left to just start fighting.", + "topic": "TALK_DONE", + "condition": { "not": { "u_has_trait": "PROF_HUB01_ANCILLIARY" } }, + "effect": "flee" + }, + { + "text": "Nice to meet you too.", + "topic": "TALK_DONE", + "effect": { "u_add_var": "u_met_ADzvonko", "type": "general", "context": "meeting", "value": "yes" }, + "condition": { "u_has_trait": "PROF_HUB01_ANCILLIARY" } + } + ] + } +] diff --git a/data/json/npcs/robofac/robofac_basement_npcs/NPC_Darla_Novak.json b/data/json/npcs/robofac/robofac_basement_npcs/NPC_Darla_Novak.json new file mode 100644 index 0000000000000..7c4d307114056 --- /dev/null +++ b/data/json/npcs/robofac/robofac_basement_npcs/NPC_Darla_Novak.json @@ -0,0 +1,82 @@ +[ + { + "type": "npc", + "id": "HUB01_DNovak", + "name_unique": "Darla Novak", + "gender": "female", + "name_suffix": "building maintenance", + "class": "NC_HUB01_DNovak", + "attitude": 0, + "mission": 7, + "chat": "HUB01_DNovak_Intro", + "faction": "robofac" + }, + { + "type": "npc_class", + "id": "NC_HUB01_DNovak", + "name": { "str": "Hub 01 HVAC maintenance" }, + "job_description": "I'm part of Hub 01's maintenance crew.", + "common": false, + "//": "HVAC maintenance. Keeps melchior from boiling to death and you from choking on stale bunker air.", + "bonus_str": { "rng": [ -1, 2 ] }, + "bonus_dex": { "rng": [ -1, 2 ] }, + "bonus_int": { "rng": [ -2, 2 ] }, + "bonus_per": { "rng": [ -1, 2 ] }, + "worn_override": "HUB01_DNovak_worn", + "carry_override": "HUB01_DNovak_carried", + "weapon_override": "HUB01_DNovak_wield", + "traits": [ { "group": "Appearance_demographics" } ], + "skills": [ + { "skill": "ALL", "level": { "mul": [ { "one_in": 3 }, { "sum": [ { "dice": [ 2, 2 ] }, { "rng": [ 0, -4 ] } ] } ] } }, + { "skill": "fabrication", "bonus": { "rng": [ 0, 3 ] } }, + { "skill": "mechanics", "bonus": { "rng": [ 4, 5 ] } } + ] + }, + { + "type": "item_group", + "id": "HUB01_DNovak_worn", + "subtype": "collection", + "entries": [ + { "item": "bra" }, + { "item": "panties" }, + { "item": "socks" }, + { "item": "robofac_jumpsuit" }, + { "item": "boots_steel" }, + { "item": "vest" }, + { "item": "gloves_work" } + ] + }, + { + "type": "item_group", + "id": "HUB01_DNovak_carried", + "subtype": "collection", + "entries": [ { "group": "wallet_full" }, { "item": "flashlight" }, { "item": "screwdriver_set" } ] + }, + { + "type": "item_group", + "id": "HUB01_DNovak_wield" + }, + { + "type": "talk_topic", + "id": "HUB01_DNovak_Intro", + "dynamic_line": { + "u_has_trait": "PROF_HUB01_ANCILLIARY", + "yes": "Oh, hey. The rumors were true, there really is someone new around. Nice to meet you, call me Darla.", + "no": "Who the fuck are you? How did you get down here? Security!" + }, + "responses": [ + { + "text": "Hold on, hold on. I'm not here to hurt anyone, and there aren't enough of us left to just start fighting.", + "condition": { "not": { "u_has_trait": "PROF_HUB01_ANCILLIARY" } }, + "topic": "TALK_DONE", + "effect": "flee" + }, + { + "text": "Nice to meet you too.", + "topic": "TALK_DONE", + "effect": { "u_add_var": "u_met_DNovak", "type": "general", "context": "meeting", "value": "yes" }, + "condition": { "u_has_trait": "PROF_HUB01_ANCILLIARY" } + } + ] + } +] diff --git a/data/json/npcs/robofac/robofac_basement_npcs/NPC_Jonathan_Farrier.json b/data/json/npcs/robofac/robofac_basement_npcs/NPC_Jonathan_Farrier.json new file mode 100644 index 0000000000000..e2144bc693f19 --- /dev/null +++ b/data/json/npcs/robofac/robofac_basement_npcs/NPC_Jonathan_Farrier.json @@ -0,0 +1,83 @@ +[ + { + "type": "npc", + "id": "HUB01_JFarrier", + "name_unique": "Jonathan Farrier", + "gender": "male", + "name_suffix": "assistant", + "class": "NC_HUB01_JFarrier", + "attitude": 0, + "mission": 7, + "chat": "HUB01_JFarrier_Intro", + "faction": "robofac" + }, + { + "type": "npc_class", + "id": "NC_HUB01_JFarrier", + "name": { "str": "Hub 01 Assistant" }, + "job_description": "I do miscellaneous jobs within Hub 01.", + "common": false, + "//": "Trucker and now general handyman. Used to work as a cargo driver for a company supplying Hub 01, is only inside the lab by chance.", + "bonus_str": { "rng": [ -1, 2 ] }, + "bonus_dex": { "rng": [ -2, 2 ] }, + "bonus_int": { "rng": [ -2, 2 ] }, + "bonus_per": { "rng": [ -2, 2 ] }, + "worn_override": "HUB01_JFarrier_worn", + "carry_override": "HUB01_JFarrier_carried", + "weapon_override": "HUB01_JFarrier_wield", + "traits": [ { "group": "Appearance_demographics" } ], + "skills": [ + { "skill": "ALL", "level": { "mul": [ { "one_in": 3 }, { "sum": [ { "dice": [ 2, 2 ] }, { "rng": [ 0, -4 ] } ] } ] } }, + { "skill": "driving", "bonus": { "rng": [ 4, 6 ] } }, + { "skill": "fabrication", "bonus": { "rng": [ 0, 1 ] } }, + { "skill": "mechanics", "bonus": { "rng": [ 1, 2 ] } } + ] + }, + { + "type": "item_group", + "id": "HUB01_JFarrier_worn", + "subtype": "collection", + "entries": [ + { "item": "briefs" }, + { "item": "socks" }, + { "item": "robofac_jumpsuit" }, + { "item": "bandana" }, + { "item": "boots_steel" }, + { "item": "tool_belt" }, + { "item": "gloves_work" } + ] + }, + { + "type": "item_group", + "id": "HUB01_JFarrier_carried", + "subtype": "collection", + "entries": [ { "group": "wallet_full" }, { "item": "multitool" }, { "item": "screwdriver_set" }, { "item": "hammer" } ] + }, + { + "type": "item_group", + "id": "HUB01_JFarrier_wield" + }, + { + "type": "talk_topic", + "id": "HUB01_JFarrier_Intro", + "dynamic_line": { + "u_has_trait": "PROF_HUB01_ANCILLIARY", + "yes": "Aw, hey, you must be that new blood runnin' jobs up top. So, they finally let you down, eh? My name's Jon, Jon Farrier. Nice to meet you, real nice.", + "no": "Who the fuck are you? How did you get down here? Security!" + }, + "responses": [ + { + "text": "Hold on, hold on. I'm not here to hurt anyone, and there aren't enough of us left to just start fighting.", + "condition": { "not": { "u_has_trait": "PROF_HUB01_ANCILLIARY" } }, + "topic": "TALK_DONE", + "effect": "flee" + }, + { + "text": "Nice to meet you too.", + "topic": "TALK_DONE", + "effect": { "u_add_var": "u_met_JFarrier", "type": "general", "context": "meeting", "value": "yes" }, + "condition": { "u_has_trait": "PROF_HUB01_ANCILLIARY" } + } + ] + } +] diff --git a/data/json/npcs/robofac/robofac_basement_npcs/NPC_Sunil_Narayana.json b/data/json/npcs/robofac/robofac_basement_npcs/NPC_Sunil_Narayana.json new file mode 100644 index 0000000000000..ebf75b47aff42 --- /dev/null +++ b/data/json/npcs/robofac/robofac_basement_npcs/NPC_Sunil_Narayana.json @@ -0,0 +1,81 @@ +[ + { + "type": "npc", + "id": "HUB01_SNarayana", + "name_unique": "Sunil Narayana", + "gender": "male", + "name_suffix": "server maintenance", + "class": "NC_HUB01_SNarayana", + "attitude": 0, + "mission": 7, + "chat": "HUB01_SNarayana_Intro", + "faction": "robofac" + }, + { + "type": "npc_class", + "id": "NC_HUB01_SNarayana", + "name": { "str": "Hub 01 Server Maintenance" }, + "job_description": "I'm part of Hub 01's maintenance crew.", + "common": false, + "//": "HVAC maintenance. Resident dungeon master.", + "bonus_str": { "rng": [ -2, 2 ] }, + "bonus_dex": { "rng": [ 0, 2 ] }, + "bonus_int": { "rng": [ 0, 2 ] }, + "bonus_per": { "rng": [ -1, 1 ] }, + "worn_override": "HUB01_SNarayana_worn", + "carry_override": "HUB01_SNarayana_carried", + "weapon_override": "HUB01_SNarayana_wield", + "traits": [ { "trait": "PROF_DICEMASTER" }, { "group": "Appearance_demographics" } ], + "skills": [ + { "skill": "ALL", "level": { "mul": [ { "one_in": 3 }, { "sum": [ { "dice": [ 2, 2 ] }, { "rng": [ 0, -4 ] } ] } ] } }, + { "skill": "speech", "bonus": { "rng": [ 0, 1 ] } }, + { "skill": "electronics", "bonus": { "rng": [ 4, 5 ] } } + ] + }, + { + "type": "item_group", + "id": "HUB01_SNarayana_worn", + "subtype": "collection", + "entries": [ + { "item": "boxer_briefs" }, + { "item": "socks" }, + { "item": "robofac_jumpsuit" }, + { "item": "boots" }, + { "item": "hat_ball" }, + { "item": "gloves_work" } + ] + }, + { + "type": "item_group", + "id": "HUB01_SNarayana_carried", + "subtype": "collection", + "entries": [ { "group": "wallet_full" } ] + }, + { + "type": "item_group", + "id": "HUB01_SNarayana_wield" + }, + { + "type": "talk_topic", + "id": "HUB01_SNarayana_Intro", + "dynamic_line": { + "u_has_trait": "PROF_HUB01_ANCILLIARY", + "yes": "Oooh, the NEW person. So, you're not just some rumour they cooked up as a team building exercise. I'm Sunil, welcome to the underground.", + "no": "Who the fuck are you? How did you get down here? Security!" + }, + "responses": [ + { + "text": "Hold on, hold on. I'm not here to hurt anyone, and there aren't enough of us left to just start fighting.", + "condition": { "not": { "u_has_trait": "PROF_HUB01_ANCILLIARY" } }, + "topic": "TALK_DONE", + "effect": "flee" + }, + { + "text": "Nice to meet you too.", + "topic": "TALK_DONE", + "effect": { "u_add_var": "u_met_SNarayana", "type": "general", "context": "meeting", "value": "yes" }, + "condition": { "u_has_trait": "PROF_HUB01_ANCILLIARY" } + } + ] + } +] diff --git a/data/json/npcs/talk_tags.json b/data/json/npcs/talk_tags.json index 2b25404c7ca4b..d8e92c0d6ab68 100644 --- a/data/json/npcs/talk_tags.json +++ b/data/json/npcs/talk_tags.json @@ -1,4 +1,20 @@ [ + { + "type": "snippet", + "category": "", + "//": "Spiral madman's responses.", + "text": [ + "Spirals.", + "Spirals…", + "Spirals?", + "Spirals!", + "Spirals!!!", + "SpIrAlS.", + "SPIRALS.", + "AHAHAHAHA!!!", + "SpiRAAAAAAALS!" + ] + }, { "type": "snippet", "category": "", @@ -63,6 +79,7 @@ "I'm sorry . I'm afraid I can't do that.", "Wish I could, .", "Nothing to trade, sorry .", + "I don't want to trade right now.", "Maybe next time?" ] }, @@ -79,6 +96,7 @@ "Great idea! Call me when you find SOMEONE ELSE to do it.", "I'm afraid I can't help you there.", "Not exactly the settlin' type.", + "I can't help you with that right now.", "I'm more of a free spirit, can't settle, sorry." ] }, @@ -87,6 +105,75 @@ "category": "", "//": "generic negative/hostile pronouns", "text": [ + "airhead", + "arse", + "ass", + "assclown", + "assface", + "asstown", + "asswagon", + "bitch", + "bootlicker", + "bugger", + "butchery refuse", + "butthead", + "butt-licker", + "chucklefuck", + "coward", + "cuck", + "cunt", + "damn ", + "damned ", + "dildo", + "dimwit", + "dog", + "dumbshit", + "dumpster", + "dumpster fire", + "dunderfuck", + "effing ", + "emergency ration", + "empty headed ", + "expendable ", + "fart", + "fat-ass", + "frail-ass ", + "frigger", + "fungus eater", + "goddamn ", + "good for nothing", + "inbred", + "lazy", + "lazy ass", + "limp-dick", + "low-life", + "meat face", + "mi-go hugger", + "mushy pizza", + "pissbrain", + "pool noodle", + "prick", + "pussy", + "shitweasel", + "slime hugger", + "smooth brain", + "snowflake", + "toilet licker", + "tool", + "trash", + "turd", + "twat", + "twerp", + "useless", + "wanker", + "waste", + "waste of ammo", + "weapon holder", + "weiner", + "weird ", + "wet sandwich", + "wet sock", + "z fodder", " ", "asshat", "asswipe", @@ -172,6 +259,22 @@ "category": "", "//": "Swears and curses used to emphasize unpleasant events", "text": [ + "ARGH", + "aw fuck", + "balls", + "can't believe it", + "crapper", + "feck", + "For serious?", + "frigg", + "heavens", + "heck", + "hell", + "my luck", + "shards and shatters", + "the audacity", + "the worst!", + "what", "darn", "fuck", "goddamn", @@ -445,6 +548,7 @@ "Time for bed! See you in the morning.", "There's a bed calling my name, and I'm going to it.", "Good night! Wake me if you need me.", + "Well, I'm going to bed now.", "Calling it a night for now. You get some rest too, okay?" ] }, @@ -453,6 +557,7 @@ "category": "", "//": "Generic terms of emphasis", "text": [ + "ass-shatteringly", "extremely", "greatly", "highly", @@ -608,6 +713,7 @@ "Drop your weapon, !", "Put down your !", "Alright, drop the !", + "Hey, put that weapon down!", "Please put down your weapon. I'll give you to the count of three. One…", "Let's take it easy now, okay? Put the weapon down." ] @@ -777,6 +883,7 @@ "Bombs away!", "Shrapnel, incoming! Watch it!", "Making some noise!", + "It's gonna blow!", "Hit the deck!" ] }, @@ -944,6 +1051,9 @@ "! This is the end,", "I can take on", "Time to die,", + "Prepare to die,", + "See you in hell,", + "Rot in hell,", "Say your prayers," ] }, @@ -980,6 +1090,7 @@ "Hold up, gotta plug this hole in me.", "Watch my back while I stitch my arm back on .", "Gotta bandage this or I'll bleed out. Give me a sec.", + "Let me fix myself up here for a second.", "I ain't got time to ble-wait, that's a lot of blood. Give me a second while I patch this." ] }, @@ -996,6 +1107,8 @@ "What was that?", "Huh? Is someone there?", "Who's there?", + "Did anyone else hear that?", + "Sounds like someone said something.", "Who goes there?" ] }, @@ -1010,6 +1123,8 @@ "What's that noise?", "Is something over there?", "Sounds like something bad's going on.", + "What's going on over there?", + "Sounds like a fight's going on.", "What was that?" ] }, @@ -1024,6 +1139,9 @@ "Did you hear that? Sounded like", "What is making that sound? I can hear the", "I could swear I heard", + "Huh? I think I just heard", + "Huh? That sounded like", + "I think that was the sound of", "I could have sworn I just heard" ] }, @@ -1067,6 +1185,7 @@ "Mmm, that weed smells good.", "Man, I can smell the weed, can I have some?", "Are you sure it's a good idea to smoke that now?", + "Are you getting stoned right now?", "Is that the devil's lettuce I smell?" ] }, @@ -1091,7 +1210,14 @@ "type": "snippet", "category": "", "//": "Complaint when the NPC is near the avatar who is smoking crack.", - "text": [ "Ew, smells like burning rubber!", "Ugh, that smells rancid!", "Why are you smoking crack cocaine?", "" ] + "text": [ + "Ew, smells like burning rubber!", + "Ugh, that smells rancid!", + "Why are you smoking crack cocaine?", + "Crack cocaine smells awful!", + "Are you seriously going to be a crackhead around me?", + "" + ] }, { "type": "snippet", @@ -1114,6 +1240,7 @@ "Pass some ethanol, I need to power my ethanol burner.", "Waiter! I need a refill, my ethanol burner is running out of charge!", "I require ethanol for my internal power supply. Anything on you?", + "I need some ethanol to power up my CBMs.", "Got any alcohol to spare? Need to recharge my drives. Methanol, would do." ] }, @@ -1125,6 +1252,7 @@ "I need some junk to power my internal furnace.", "I can't recharge my CBMs without some firewood for my internal furnace.", "I need something to use as fuel for my furnace.", + "I need something to burn for my furnace. Can you help?", "Hey, , can I get some waste paper or withered plants? I need to recharge." ] }, @@ -1135,6 +1263,8 @@ "text": [ "I need some fuel to power my bionics.", "I can't recharge my CBMs without some fuel.", + "Could I get some fuel for my bionics?", + "I need more fuel. Can you help me out here?", "Hey, , can I get some fuel? I need to recharge." ] }, @@ -1163,6 +1293,7 @@ "What was the Cataclysm like for you?", "How did you make it through the initial chaos?", "Tell me how you survived the initial wave of the Cataclysm.", + "How did you survive those first few days?", "Was it rough surviving thus far?" ] }, @@ -1173,6 +1304,8 @@ "text": [ "How do you think we ended up here? What even happened?", "What's going on? Like, big picture, what the hell happened?", + "What do you think the cataclysm even was?", + "So how do you think the end of the world happened?", "Have you heard anything about how the apocalypse came about?" ] }, @@ -1184,6 +1317,7 @@ "Let's talk about something else.", "Let's change the subject.", "I'd like to ask you about something else.", + "I'd like to talk about about something else.", "Moving on…", "Anyway…" ] @@ -1294,6 +1428,7 @@ "Retreat! Retreat!", "Book it!", "Leg it!", + "I'm getting the hell out of here!", "Thank fuck for all the cardio!" ] }, @@ -1306,6 +1441,7 @@ "! Die, you ! I want to live!", "My feet failed me! Arms, don't fail me!", "Can't run! Have to fight!", + "I can't run away from this!", "If I die, I'm taking you all with me!" ] }, @@ -1322,6 +1458,7 @@ "Somebody get some water!", "Fire, fire, FIRE!", "Get an extinguisher!", + "Somebody get some water or something!", "Danger hot!" ] }, @@ -1355,6 +1492,7 @@ "Clean water, the taste that refreshes!", "I was parched, but not I am not.", "Water is nice, but I should get a grog ration.", + "Nothing quite like clean water.", "That wasn't Evian, but I'm not thirsty." ] }, @@ -1366,6 +1504,7 @@ "And now I have eaten and am not hungry.", "That food was good, but I miss real restaurants.", "Well, that satisfied me.", + "Nice to have food once in a while.", "I just had some food, but I'm still peckish. Would you mind if I ate more?" ] }, @@ -1376,6 +1515,8 @@ "text": [ "Hey, , we're out of food.", "Hey, the larder is empty! We're going to starve.", + "There's no food in the larder!", + "We need more food in the basecamp larder; it's empty.", "Uhm, , I don't meant to criticize, but we should focus on distributing some food into the basecamp larder." ] }, diff --git a/data/json/npcs/talk_tags_chat.json b/data/json/npcs/talk_tags_chat.json index 55a9b672fd038..a7520f39c32c7 100644 --- a/data/json/npcs/talk_tags_chat.json +++ b/data/json/npcs/talk_tags_chat.json @@ -19,6 +19,8 @@ "text": [ "Yeah, this summer heat is hitting me hard, you know?", "Enjoying the summer.", + "Not quite like the summers from before, huh?", + "Can't wait for this summer to be over.", "Kinda wishing it would cool off a bit, to be honest." ] }, @@ -29,6 +31,8 @@ "text": [ "OK, maybe it'll stop me from freezing in this weather.", "Gotta say, I'm not minding the snow.", + "Not quite like the winters from before, huh?", + "Can't wait for this winter to be over.", "It's weird the zombies don't freeze." ] }, @@ -36,7 +40,13 @@ "type": "snippet", "category": "", "//": "A sentence about being sick to go before a more general chitchat message.", - "text": [ "Well, I'm feeling pretty sick… but sure." ] + "text": [ + "A nice chat might improve my health somewhat.", + "Alright, but I'm feeling a bit under the weather.", + "I'm not feeling too well, but I can still talk for a bit.", + "We can talk, even though I'm not feeling too well.", + "Well, I'm feeling pretty sick… but sure." + ] }, { "type": "snippet", @@ -58,7 +68,7 @@ "What the heck. How's life been treating you?", "So, how about that weather, eh?", "Nice of you to make time. How's it been for you lately?", - "My dogs’ve been barkin’ lately, you know?", + "My dogs've been barkin' lately, you know?", "I feel great today. Not sure what it is, just one of those days." ] }, @@ -70,11 +80,11 @@ "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.", "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.", "I wonder if I should be getting more religious now, or less. You know what I'm sayin', ?", - "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?", - "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?", + "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?", + "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?", "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.", "Every time I close my eyes, I can still see the riots. Do you get that, or is it just me?", - "You ever feel like the whole time before the apocalypse was just a dream you’re waking up from?", + "You ever feel like the whole time before the apocalypse was just a dream you're waking up from?", "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?", "I wonder if the government's still out there, holed up in some bunker.", "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?", @@ -89,9 +99,9 @@ "text": [ "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?", "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.", - "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.", - "I still don’t understand how these zombies are powered. They’re like perpetual motion machines.", - "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." + "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.", + "I still don't understand how these zombies are powered. They're like perpetual motion machines.", + "So many parts of this still don't fit together. Who created the zombies? What powers them? Maybe the rumors of mind control drugs were true all along, and someone found a way to bioengineer living dead." ] }, { @@ -103,7 +113,7 @@ "", "How do these zombies even keep going? What are they eating? Do you think they'll ever rot away?", "I been thinkin', one of these days, we're gonna run out of toilet paper. What then?", - "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?", + "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?", "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.", "You read any good books lately? I'm glad we still got books.", "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." diff --git a/data/json/obsolete.json b/data/json/obsolete.json index 87ff797204f2f..2b553d919ccad 100644 --- a/data/json/obsolete.json +++ b/data/json/obsolete.json @@ -766,7 +766,7 @@ "removal": { "skills": [ [ "mechanics", 1 ] ], "time": "15 m", "qualities": [ { "id": "WRENCH", "level": 2 } ] }, "repair": { "skills": [ [ "mechanics", 2 ] ], "time": "15 m", "using": [ [ "welding_standard", 5 ] ] } }, - "flags": [ "WHEEL", "NEEDS_JACKING", "NEEDS_WHEEL_MOUNT_MEDIUM" ], + "flags": [ "WHEEL", "UNSTABLE_WHEEL", "NEEDS_JACKING", "NEEDS_WHEEL_MOUNT_MEDIUM" ], "damage_reduction": { "all": 66 } }, { diff --git a/data/json/obsolete_terrains.json b/data/json/obsolete_terrains.json index 3bd39eb0b7947..dc517d80da30e 100644 --- a/data/json/obsolete_terrains.json +++ b/data/json/obsolete_terrains.json @@ -23,7 +23,11 @@ "office_tower_1_entrance", "office_tower_1", "office_tower_b_entrance", - "office_tower_b" + "office_tower_b", + "mine_entrance", + "mine_shaft", + "spiral", + "spiral_hub" ] } ] diff --git a/data/json/overmap/map_extras.json b/data/json/overmap/map_extras.json index bb8a7cf44a558..a7d4846738189 100644 --- a/data/json/overmap/map_extras.json +++ b/data/json/overmap/map_extras.json @@ -24,7 +24,7 @@ "type": "map_extra", "name": { "str": "Drug Deal" }, "description": "Several corpses of drug dealers are here.", - "generator": { "generator_method": "map_extra_function", "generator_id": "mx_drugdeal" }, + "generator": { "generator_method": "update_mapgen", "generator_id": "mx_drugdeal" }, "sym": "d", "color": "light_red", "autonote": true diff --git a/data/json/overmap/overmap_special/bordered_world.json b/data/json/overmap/overmap_special/bordered_world.json new file mode 100644 index 0000000000000..fb561af4f7a45 --- /dev/null +++ b/data/json/overmap/overmap_special/bordered_world.json @@ -0,0 +1,727 @@ +[ + { + "type": "overmap_special", + "id": "world", + "overmaps": [ + { "point": [ 0, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 1, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 2, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 3, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 4, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 5, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 6, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 7, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 8, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 9, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 10, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 11, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 12, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 13, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 14, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 15, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 16, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 17, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 18, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 19, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 20, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 21, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 22, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 23, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 24, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 25, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 26, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 27, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 28, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 29, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 30, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 31, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 32, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 33, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 34, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 35, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 36, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 37, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 38, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 39, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 40, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 41, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 42, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 43, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 44, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 45, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 46, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 47, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 48, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 49, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 50, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 51, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 52, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 53, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 54, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 55, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 56, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 57, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 58, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 59, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 60, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 61, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 62, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 63, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 64, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 65, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 66, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 67, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 68, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 69, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 70, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 71, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 72, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 73, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 74, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 75, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 76, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 77, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 78, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 79, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 80, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 81, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 82, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 83, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 84, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 85, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 86, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 87, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 88, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 89, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 90, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 91, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 92, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 93, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 94, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 95, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 96, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 97, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 98, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 99, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 100, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 101, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 102, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 103, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 104, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 105, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 106, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 107, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 108, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 109, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 110, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 111, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 112, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 113, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 114, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 115, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 116, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 117, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 118, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 119, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 120, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 121, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 122, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 123, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 124, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 125, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 126, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 127, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 128, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 129, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 130, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 131, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 132, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 133, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 134, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 135, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 136, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 137, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 138, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 139, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 140, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 141, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 142, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 143, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 144, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 145, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 146, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 147, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 148, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 149, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 150, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 151, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 152, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 153, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 154, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 155, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 156, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 157, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 158, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 159, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 160, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 161, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 162, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 163, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 164, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 165, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 166, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 167, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 168, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 169, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 170, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 171, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 172, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 173, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 174, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 175, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 176, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 177, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 178, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 0, 0 ], "overmap": "rock_border" }, + { "point": [ 1, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 2, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 3, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 4, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 5, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 6, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 7, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 8, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 9, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 10, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 11, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 12, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 13, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 14, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 15, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 16, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 17, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 18, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 19, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 20, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 21, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 22, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 23, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 24, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 25, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 26, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 27, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 28, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 29, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 30, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 31, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 32, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 33, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 34, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 35, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 36, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 37, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 38, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 39, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 40, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 41, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 42, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 43, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 44, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 45, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 46, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 47, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 48, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 49, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 50, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 51, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 52, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 53, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 54, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 55, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 56, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 57, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 58, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 59, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 60, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 61, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 62, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 63, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 64, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 65, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 66, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 67, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 68, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 69, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 70, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 71, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 72, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 73, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 74, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 75, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 76, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 77, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 78, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 79, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 80, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 81, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 82, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 83, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 84, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 85, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 86, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 87, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 88, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 89, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 90, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 91, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 92, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 93, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 94, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 95, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 96, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 97, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 98, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 99, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 100, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 101, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 102, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 103, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 104, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 105, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 106, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 107, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 108, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 109, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 110, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 111, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 112, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 113, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 114, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 115, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 116, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 117, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 118, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 119, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 120, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 121, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 122, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 123, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 124, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 125, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 126, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 127, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 128, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 129, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 130, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 131, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 132, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 133, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 134, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 135, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 136, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 137, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 138, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 139, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 140, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 141, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 142, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 143, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 144, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 145, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 146, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 147, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 148, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 149, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 150, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 151, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 152, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 153, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 154, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 155, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 156, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 157, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 158, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 159, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 160, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 161, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 162, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 163, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 164, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 165, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 166, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 167, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 168, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 169, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 170, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 171, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 172, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 173, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 174, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 175, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 176, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 177, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 178, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 1, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 2, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 3, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 4, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 5, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 6, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 7, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 8, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 9, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 10, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 11, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 12, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 13, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 14, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 15, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 16, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 17, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 18, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 19, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 20, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 21, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 22, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 23, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 24, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 25, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 26, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 27, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 28, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 29, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 30, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 31, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 32, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 33, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 34, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 35, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 36, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 37, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 38, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 39, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 40, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 41, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 42, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 43, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 44, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 45, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 46, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 47, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 48, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 49, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 50, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 51, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 52, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 53, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 54, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 55, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 56, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 57, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 58, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 59, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 60, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 61, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 62, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 63, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 64, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 65, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 66, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 67, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 68, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 69, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 70, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 71, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 72, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 73, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 74, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 75, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 76, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 77, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 78, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 79, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 80, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 81, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 82, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 83, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 84, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 85, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 86, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 87, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 88, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 89, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 90, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 91, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 92, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 93, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 94, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 95, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 96, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 97, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 98, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 99, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 100, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 101, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 102, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 103, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 104, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 105, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 106, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 107, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 108, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 109, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 110, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 111, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 112, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 113, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 114, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 115, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 116, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 117, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 118, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 119, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 120, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 121, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 122, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 123, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 124, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 125, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 126, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 127, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 128, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 129, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 130, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 131, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 132, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 133, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 134, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 135, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 136, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 137, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 138, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 139, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 140, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 141, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 142, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 143, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 144, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 145, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 146, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 147, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 148, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 149, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 150, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 151, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 152, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 153, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 154, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 155, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 156, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 157, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 158, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 159, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 160, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 161, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 162, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 163, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 164, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 165, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 166, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 167, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 168, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 169, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 170, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 171, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 172, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 173, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 174, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 175, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 176, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 177, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 178, 0 ], "overmap": "rock_border" }, + { "point": [ 0, 179, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 1, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 2, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 3, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 4, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 5, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 6, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 7, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 8, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 9, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 10, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 11, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 12, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 13, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 14, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 15, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 16, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 17, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 18, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 19, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 20, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 21, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 22, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 23, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 24, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 25, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 26, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 27, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 28, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 29, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 30, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 31, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 32, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 33, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 34, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 35, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 36, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 37, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 38, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 39, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 40, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 41, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 42, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 43, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 44, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 45, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 46, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 47, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 48, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 49, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 50, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 51, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 52, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 53, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 54, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 55, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 56, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 57, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 58, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 59, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 60, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 61, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 62, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 63, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 64, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 65, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 66, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 67, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 68, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 69, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 70, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 71, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 72, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 73, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 74, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 75, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 76, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 77, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 78, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 79, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 80, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 81, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 82, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 83, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 84, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 85, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 86, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 87, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 88, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 89, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 90, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 91, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 92, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 93, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 94, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 95, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 96, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 97, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 98, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 99, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 100, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 101, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 102, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 103, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 104, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 105, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 106, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 107, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 108, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 109, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 110, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 111, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 112, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 113, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 114, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 115, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 116, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 117, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 118, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 119, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 120, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 121, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 122, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 123, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 124, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 125, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 126, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 127, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 128, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 129, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 130, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 131, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 132, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 133, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 134, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 135, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 136, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 137, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 138, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 139, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 140, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 141, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 142, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 143, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 144, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 145, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 146, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 147, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 148, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 149, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 150, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 151, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 152, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 153, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 154, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 155, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 156, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 157, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 158, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 159, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 160, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 161, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 162, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 163, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 164, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 165, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 166, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 167, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 168, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 169, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 170, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 171, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 172, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 173, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 174, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 175, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 176, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 177, 0 ], "overmap": "rock_border" }, + { "point": [ 179, 178, 0 ], "overmap": "rock_border" } + ], + "locations": [ "land" ], + "//": "This location should never spawn naturally, only on top of existing locations when playing the scenarios with BORDERED flag.", + "occurrences": [ 0, 0 ] + } +] diff --git a/data/json/overmap/overmap_special/mine.json b/data/json/overmap/overmap_special/mine.json new file mode 100644 index 0000000000000..d409849534a0e --- /dev/null +++ b/data/json/overmap/overmap_special/mine.json @@ -0,0 +1,63 @@ +[ + { + "type": "overmap_special", + "id": "Mine Entrance", + "overmaps": [ + { "point": [ 0, 0, 0 ], "overmap": "s_lot_north" }, + { "point": [ 0, 1, 0 ], "overmap": "mine_entrance_north" }, + { "point": [ 1, 1, 0 ], "overmap": "mine_entrance_loading_zone_north" }, + { "point": [ 1, 0, 0 ], "overmap": "road_end_north" }, + { "point": [ 0, 1, 1 ], "overmap": "mine_entrance_roof_north" }, + { "point": [ 0, 1, -1 ], "overmap": "mine_shaft_middle_north" }, + { "point": [ 0, 1, -2 ], "overmap": "mine_shaft_lower_north" }, + { "point": [ 1, 1, -2 ], "overmap": "mine_shaft_lower_east_north" } + ], + "connections": [ + { "point": [ 0, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 0, 0, 0 ] }, + { "point": [ 1, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 1, 0, 0 ] } + ], + "locations": [ "wilderness" ], + "city_distance": [ 10, 40 ], + "city_sizes": [ 4, -1 ], + "occurrences": [ 0, 2 ], + "flags": [ "WILDERNESS" ] + }, + { + "type": "overmap_special", + "id": "Spiral mine", + "overmaps": [ + { "point": [ 0, 0, 0 ], "overmap": "s_lot_north" }, + { "point": [ 0, 1, 0 ], "overmap": "mine_entrance_north" }, + { "point": [ 1, 1, 0 ], "overmap": "mine_entrance_loading_zone_north" }, + { "point": [ 1, 0, 0 ], "overmap": "road_end_north" }, + { "point": [ 0, 1, 1 ], "overmap": "mine_entrance_roof_north" }, + { "point": [ 0, 1, -1 ], "overmap": "mine_shaft_middle_north" }, + { "point": [ 0, 1, -2 ], "overmap": "mine_shaft_lower_north" }, + { "point": [ 1, 1, -2 ], "overmap": "mine_shaft_lower_east_north" }, + { "point": [ -1, 1, -2 ], "overmap": "mine_spiral_east_north" }, + { "point": [ -2, 1, -2 ], "overmap": "mine_spiral_central_north" }, + { "point": [ -3, 1, -2 ], "overmap": "mine_spiral_west_north" }, + { "point": [ -3, 1, -3 ], "overmap": "mine_spiral_-1_nw_north" }, + { "point": [ -2, 1, -3 ], "overmap": "mine_spiral_-1_n_north" }, + { "point": [ -1, 1, -3 ], "overmap": "mine_spiral_-1_ne_north" }, + { "point": [ -3, 2, -3 ], "overmap": "mine_spiral_-1_sw_north" }, + { "point": [ -2, 2, -3 ], "overmap": "mine_spiral_-1_s_north" }, + { "point": [ -1, 2, -3 ], "overmap": "mine_spiral_-1_se_north" }, + { "point": [ -3, 1, -4 ], "overmap": "mine_spiral_finale_nw_north" }, + { "point": [ -2, 1, -4 ], "overmap": "mine_spiral_finale_n_north" }, + { "point": [ -1, 1, -4 ], "overmap": "mine_spiral_finale_ne_north" }, + { "point": [ -3, 2, -4 ], "overmap": "mine_spiral_finale_sw_north" }, + { "point": [ -2, 2, -4 ], "overmap": "mine_spiral_finale_s_north" }, + { "point": [ -1, 2, -4 ], "overmap": "mine_spiral_finale_se_north" } + ], + "connections": [ + { "point": [ 0, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 0, 0, 0 ] }, + { "point": [ 1, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 1, 0, 0 ] } + ], + "locations": [ "wilderness" ], + "city_distance": [ 10, 40 ], + "city_sizes": [ 4, -1 ], + "occurrences": [ 0, 2 ], + "flags": [ "WILDERNESS" ] + } +] diff --git a/data/json/overmap/overmap_special/specials.json b/data/json/overmap/overmap_special/specials.json index 8a4ca61d87b65..a595d801d6445 100644 --- a/data/json/overmap/overmap_special/specials.json +++ b/data/json/overmap/overmap_special/specials.json @@ -249,6 +249,24 @@ "occurrences": [ 0, 2 ], "flags": [ "CLASSIC", "WILDERNESS" ] }, + { + "type": "overmap_special", + "id": "Hunting Lodge", + "overmaps": [ + { "point": [ 0, 0, 0 ], "overmap": "lodge_ground1_north" }, + { "point": [ 1, 0, 0 ], "overmap": "lodge_ground2_north" }, + { "point": [ 0, 0, 1 ], "overmap": "lodge_2ndfloor1_north" }, + { "point": [ 1, 0, 1 ], "overmap": "lodge_2ndfloor2_north" }, + { "point": [ 0, 0, -1 ], "overmap": "lodge_basement_residential1_north" }, + { "point": [ 1, 0, -1 ], "overmap": "lodge_basement_residential2_north" } + ], + "connections": [ { "point": [ 0, -1, 0 ], "terrain": "road", "existing": true } ], + "locations": [ "land", "swamp" ], + "city_distance": [ 15, -1 ], + "city_sizes": [ 1, 8 ], + "occurrences": [ 0, 2 ], + "flags": [ "CLASSIC", "WILDERNESS" ] + }, { "type": "overmap_special", "id": "Gas Station", @@ -803,7 +821,21 @@ "connections": [ { "point": [ 0, -1, 0 ], "from": [ 0, 0, 0 ] } ], "locations": [ "forest" ], "city_distance": [ 20, -1 ], - "occurrences": [ 50, 100 ], + "occurrences": [ 25, 100 ], + "flags": [ "MILITARY", "UNIQUE" ] + }, + { + "type": "overmap_special", + "id": "Military Bunker", + "overmaps": [ + { "point": [ 0, -1, 0 ], "overmap": "road_end_north" }, + { "point": [ 0, 0, 0 ], "overmap": "bunker_south" }, + { "point": [ 0, 0, -1 ], "overmap": "bunker_basement_1" } + ], + "connections": [ { "point": [ 0, -1, 0 ], "from": [ 0, 0, 0 ] } ], + "locations": [ "forest" ], + "city_distance": [ 20, -1 ], + "occurrences": [ 25, 100 ], "flags": [ "MILITARY", "UNIQUE" ] }, { @@ -812,7 +844,16 @@ "overmaps": [ { "point": [ 0, 0, 0 ], "overmap": "outpost_north" } ], "locations": [ "wilderness" ], "city_distance": [ 15, -1 ], - "occurrences": [ 50, 100 ], + "occurrences": [ 25, 100 ], + "flags": [ "MILITARY", "UNIQUE" ] + }, + { + "type": "overmap_special", + "id": "Military Outpost", + "overmaps": [ { "point": [ 0, 0, 0 ], "overmap": "outpost_cross_north" } ], + "locations": [ "wilderness" ], + "city_distance": [ 15, -1 ], + "occurrences": [ 25, 100 ], "flags": [ "MILITARY", "UNIQUE" ] }, { @@ -849,7 +890,7 @@ "connections": [ { "point": [ 0, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 0, 0, 0 ] } ], "locations": [ "land" ], "city_distance": [ 0, 20 ], - "occurrences": [ 0, 5 ], + "occurrences": [ 0, 4 ], "flags": [ "CLASSIC", "WILDERNESS" ] }, { @@ -867,9 +908,49 @@ "connections": [ { "point": [ 0, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 0, 0, 0 ] } ], "locations": [ "land" ], "city_distance": [ 0, 20 ], - "occurrences": [ 0, 5 ], + "occurrences": [ 0, 4 ], "flags": [ "CLASSIC", "WILDERNESS" ] }, + { + "type": "overmap_special", + "id": "Wasp Tower", + "overmaps": [ + { "point": [ 0, 0, 0 ], "overmap": "wasp_tower_north" }, + { "point": [ 0, 0, 1 ], "overmap": "radio_tower_odd_north" }, + { "point": [ 0, 0, 2 ], "overmap": "wasp_tower_even_1_north" }, + { "point": [ 0, 0, 3 ], "overmap": "wasp_tower_odd_1_north" }, + { "point": [ 0, 0, 4 ], "overmap": "wasp_tower_even_2_north" }, + { "point": [ 0, 0, 5 ], "overmap": "wasp_tower_odd_2_north" }, + { "point": [ 0, 0, 6 ], "overmap": "wasp_tower_top_north" }, + { "point": [ 0, 0, 7 ], "overmap": "wasp_tower_roof_north" } + ], + "connections": [ { "point": [ 0, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 0, 0, 0 ] } ], + "locations": [ "land" ], + "city_distance": [ 5, 25 ], + "occurrences": [ 80, 100 ], + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 50, 80 ], "radius": [ 6, 20 ] }, + "flags": [ "WILDERNESS", "UNIQUE" ] + }, + { + "type": "overmap_special", + "id": "Wasp Tower 1", + "overmaps": [ + { "point": [ 0, 0, 0 ], "overmap": "wasp_tower_1_north" }, + { "point": [ 0, 0, 1 ], "overmap": "wasp_tower_roof_1_north" }, + { "point": [ 0, 0, 2 ], "overmap": "wasp_tower_even_1_north" }, + { "point": [ 0, 0, 3 ], "overmap": "wasp_tower_odd_1_north" }, + { "point": [ 0, 0, 4 ], "overmap": "wasp_tower_even_2_north" }, + { "point": [ 0, 0, 5 ], "overmap": "wasp_tower_odd_2_north" }, + { "point": [ 0, 0, 6 ], "overmap": "wasp_tower_top_north" }, + { "point": [ 0, 0, 7 ], "overmap": "wasp_tower_roof_north" } + ], + "connections": [ { "point": [ 0, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 0, 0, 0 ] } ], + "locations": [ "land" ], + "city_distance": [ 5, 25 ], + "occurrences": [ 80, 100 ], + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 50, 100 ], "radius": [ 6, 20 ] }, + "flags": [ "WILDERNESS", "UNIQUE" ] + }, { "type": "overmap_special", "id": "Prison", @@ -1108,8 +1189,20 @@ { "type": "overmap_special", "id": "Mine Entrance", - "overmaps": [ { "point": [ 0, 0, 0 ], "overmap": "s_lot_north" }, { "point": [ 0, 1, 0 ], "overmap": "mine_entrance" } ], - "connections": [ { "point": [ 0, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 0, 0, 0 ] } ], + "overmaps": [ + { "point": [ 0, 0, 0 ], "overmap": "s_lot_north" }, + { "point": [ 0, 1, 0 ], "overmap": "mine_entrance_north" }, + { "point": [ 1, 1, 0 ], "overmap": "mine_entrance_loading_zone_north" }, + { "point": [ 1, 0, 0 ], "overmap": "road_end_north" }, + { "point": [ 0, 1, 1 ], "overmap": "mine_entrance_roof_north" }, + { "point": [ 0, 1, -1 ], "overmap": "mine_shaft_middle_north" }, + { "point": [ 0, 1, -2 ], "overmap": "mine_shaft_lower_north" }, + { "point": [ 1, 1, -2 ], "overmap": "mine_shaft_lower_east_north" } + ], + "connections": [ + { "point": [ 0, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 0, 0, 0 ] }, + { "point": [ 1, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 1, 0, 0 ] } + ], "locations": [ "wilderness" ], "city_distance": [ 10, 40 ], "city_sizes": [ 4, -1 ], diff --git a/data/json/overmap/overmap_terrain/overmap_terrain.json b/data/json/overmap/overmap_terrain/overmap_terrain.json index 8fc28084fd9c0..6338ba751a437 100644 --- a/data/json/overmap/overmap_terrain/overmap_terrain.json +++ b/data/json/overmap/overmap_terrain/overmap_terrain.json @@ -219,6 +219,34 @@ "see_cost": 5, "extras": "build" }, + { + "type": "overmap_terrain", + "id": [ "lodge_ground1", "lodge_ground2" ], + "name": "hunting lodge", + "sym": "L", + "copy-from": "generic_forest", + "color": "light_green", + "see_cost": 4, + "extend": { "flags": [ "SOURCE_GUN", "SOURCE_AMMO" ] } + }, + { + "type": "overmap_terrain", + "id": [ "lodge_2ndfloor1", "lodge_2ndfloor2" ], + "name": "second floor hunting lodge", + "copy-from": "generic_forest", + "sym": "L", + "see_cost": 4, + "color": "light_green" + }, + { + "type": "overmap_terrain", + "id": [ "lodge_basement_residential1", "lodge_basement_residential2", "lodge_basement_laboratory_entrance" ], + "name": "hunting lodge basement", + "copy-from": "generic_forest", + "sym": "L", + "see_cost": 4, + "color": "light_green" + }, { "type": "overmap_terrain", "id": "dirtroad1_aban1", @@ -285,6 +313,25 @@ "color": "light_gray", "see_cost": 2 }, + { + "type": "overmap_terrain", + "id": [ + "wasp_tower", + "wasp_tower_1", + "wasp_tower_roof_1", + "wasp_tower_odd_1", + "wasp_tower_odd_2", + "wasp_tower_even_1", + "wasp_tower_even_2", + "wasp_tower_top" + ], + "name": "radio tower", + "sym": "X", + "color": "light_gray", + "see_cost": 2, + "mondensity": 2, + "spawns": { "group": "GROUP_WASP_GUARD", "population": [ 3, 8 ], "chance": 100 } + }, { "type": "overmap_terrain", "id": [ "bandit_camp_1", "bandit_camp_2", "bandit_camp_3", "bandit_camp_4" ], @@ -580,7 +627,7 @@ }, { "type": "overmap_terrain", - "id": [ "airliner_2a_1", "airliner_2c_1" ], + "id": [ "airliner_2a_1", "airliner_2c_1", "wasp_tower_roof" ], "name": "open air", "sym": ".", "color": "blue", @@ -683,5 +730,13 @@ "sym": "O", "color": "brown", "see_cost": 2 + }, + { + "type": "overmap_terrain", + "id": "rock_border", + "name": "rock border", + "sym": "#", + "color": "white", + "flags": [ "NO_ROTATE" ] } ] diff --git a/data/json/overmap/overmap_terrain/overmap_terrain_agricultural.json b/data/json/overmap/overmap_terrain/overmap_terrain_agricultural.json index 693e377cface8..5a2e2ccef9845 100644 --- a/data/json/overmap/overmap_terrain/overmap_terrain_agricultural.json +++ b/data/json/overmap/overmap_terrain/overmap_terrain_agricultural.json @@ -123,6 +123,7 @@ "id": "sugar_house", "copy-from": "generic_rural_building", "name": "sugar house", + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 1, 4 ], "chance": 20 }, "sym": "S" }, { @@ -270,6 +271,7 @@ "type": "overmap_terrain", "id": [ "farm_stills_4", "farm_stills_8", "farm_stills_12" ], "copy-from": "generic_rural_building", + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 1, 4 ], "chance": 10 }, "name": "grape farm" }, { @@ -284,6 +286,7 @@ "id": "farm_stills_11", "copy-from": "generic_rural_building", "name": "orchard", + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 1, 4 ], "chance": 10 }, "color": "brown" }, { @@ -293,6 +296,7 @@ "name": "apple orchard", "sym": "T", "color": "light_green", + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 1, 4 ], "chance": 5 }, "mondensity": 3 }, { @@ -502,6 +506,7 @@ "copy-from": "generic_rural_building", "name": "tree farm", "sym": "T", + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 1, 2 ], "chance": 20 }, "color": "i_green" }, { diff --git a/data/json/overmap/overmap_terrain/overmap_terrain_commercial.json b/data/json/overmap/overmap_terrain/overmap_terrain_commercial.json index f66a217205760..dfba53e9ffe20 100644 --- a/data/json/overmap/overmap_terrain/overmap_terrain_commercial.json +++ b/data/json/overmap/overmap_terrain/overmap_terrain_commercial.json @@ -417,6 +417,7 @@ "copy-from": "generic_city_building", "name": "butcher shop", "color": "i_red", + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 2, 6 ], "chance": 20 }, "extend": { "flags": [ "SOURCE_FOOD", "SOURCE_COOKING" ] } }, { @@ -853,6 +854,7 @@ "copy-from": "generic_city_building", "name": "candy shop", "color": "red_white", + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 2, 6 ], "chance": 20 }, "extend": { "flags": [ "SOURCE_LUXURY", "SOURCE_FOOD" ] } }, { @@ -868,6 +870,7 @@ "name": "bakery", "copy-from": "generic_city_building", "color": "yellow_white", + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 2, 6 ], "chance": 20 }, "extend": { "flags": [ "SOURCE_LUXURY", "SOURCE_FOOD" ] } }, { @@ -890,6 +893,7 @@ "name": "icecream shop", "copy-from": "generic_city_building", "color": "blue_white", + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 3, 6 ], "chance": 20 }, "extend": { "flags": [ "SOURCE_LUXURY", "SOURCE_FOOD" ] } }, { diff --git a/data/json/overmap/overmap_terrain/overmap_terrain_faction_base.json b/data/json/overmap/overmap_terrain/overmap_terrain_faction_base.json index aceb9c17e4519..9d285c8bb4b88 100644 --- a/data/json/overmap/overmap_terrain/overmap_terrain_faction_base.json +++ b/data/json/overmap/overmap_terrain/overmap_terrain_faction_base.json @@ -189,5 +189,117 @@ "name": "canteen survey", "color": "i_green", "delete": { "flags": [ "SOURCE_PEOPLE" ] } + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_e1", + "name": "mansion base entrance", + "sym": "M", + "color": "white", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_e2", + "name": "mansion base entrance", + "sym": "M", + "color": "white", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_t1", + "name": "mansion base swimming pool", + "sym": "M", + "color": "blue", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_t2", + "name": "mansion base bedrooms", + "sym": "M", + "color": "light_green", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_t3", + "name": "mansion base???", + "sym": "M", + "color": "white", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_t4", + "name": "mansion base kitchen", + "sym": "M", + "color": "pink", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_t5", + "name": "mansion base library", + "sym": "M", + "color": "brown", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_t6", + "name": "mansion base bedroom", + "sym": "M", + "color": "light_green", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_t7", + "name": "mansion base living rooms", + "sym": "M", + "color": "green", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_c1", + "name": "mansion base swimming pool", + "sym": "M", + "color": "blue", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_c2", + "name": "mansion base bar", + "sym": "M", + "color": "magenta", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_c3", + "name": "mansion base living rooms", + "sym": "M", + "color": "green", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_c4", + "name": "mansion base bedroom", + "sym": "M", + "color": "light_green", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "faction_base_mansion_c5", + "name": "mansion base kitchen", + "sym": "M", + "color": "pink", + "flags": [ "SOURCE_PEOPLE", "NO_ROTATE" ] } ] diff --git a/data/json/overmap/overmap_terrain/overmap_terrain_hardcoded.json b/data/json/overmap/overmap_terrain/overmap_terrain_hardcoded.json index 55f6ceb154e70..a47b500821000 100644 --- a/data/json/overmap/overmap_terrain/overmap_terrain_hardcoded.json +++ b/data/json/overmap/overmap_terrain/overmap_terrain_hardcoded.json @@ -103,24 +103,6 @@ "see_cost": 5, "flags": [ "KNOWN_DOWN", "NO_ROTATE" ] }, - { - "type": "overmap_terrain", - "id": "mine_entrance", - "name": "mine entrance", - "sym": "M", - "color": "magenta", - "see_cost": 5, - "flags": [ "KNOWN_DOWN", "NO_ROTATE" ] - }, - { - "type": "overmap_terrain", - "id": "mine_shaft", - "name": "mine shaft", - "sym": "O", - "color": "dark_gray", - "see_cost": 5, - "flags": [ "KNOWN_UP", "KNOWN_DOWN", "NO_ROTATE" ] - }, { "type": "overmap_terrain", "id": "mine", @@ -148,24 +130,6 @@ "see_cost": 2, "flags": [ "NO_ROTATE" ] }, - { - "type": "overmap_terrain", - "id": "spiral_hub", - "name": "spiral cavern", - "sym": "@", - "color": "pink", - "see_cost": 2, - "flags": [ "NO_ROTATE" ] - }, - { - "type": "overmap_terrain", - "id": "spiral", - "name": "spiral cavern", - "sym": "@", - "color": "pink", - "see_cost": 2, - "flags": [ "NO_ROTATE" ] - }, { "type": "overmap_terrain", "id": "cave", diff --git a/data/json/overmap/overmap_terrain/overmap_terrain_industrial.json b/data/json/overmap/overmap_terrain/overmap_terrain_industrial.json index 0485bde2cc4ff..c57c4e7367a21 100644 --- a/data/json/overmap/overmap_terrain/overmap_terrain_industrial.json +++ b/data/json/overmap/overmap_terrain/overmap_terrain_industrial.json @@ -116,6 +116,7 @@ "name": "lumberyard", "sym": "L", "color": "i_green", + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 1, 3 ], "chance": 10 }, "extend": { "flags": [ "SOURCE_CONSTRUCTION", "SOURCE_FABRICATION" ] } }, { @@ -133,6 +134,7 @@ "name": "lumbermill", "sym": "L", "color": "i_green", + "spawns": { "group": "GROUP_WASP_FORAGER", "population": [ 1, 3 ], "chance": 10 }, "extend": { "flags": [ "SOURCE_CONSTRUCTION", "SOURCE_FABRICATION" ] } }, { @@ -357,5 +359,65 @@ "see_cost": 5, "extras": "build", "mondensity": 2 + }, + { + "type": "overmap_terrain", + "id": [ "mine_entrance", "mine_entrance_loading_zone" ], + "name": "mine entrance", + "sym": "M", + "color": "magenta", + "see_cost": 5, + "flags": [ "KNOWN_DOWN" ] + }, + { + "type": "overmap_terrain", + "id": [ "mine_entrance_roof", "mine_entrance_loading_zone_roof" ], + "name": "mine entrance roof", + "sym": "M", + "color": "magenta", + "see_cost": 5 + }, + { + "type": "overmap_terrain", + "id": "mine_shaft_middle", + "name": "mine shaft", + "sym": "O", + "color": "dark_gray", + "see_cost": 5, + "flags": [ "KNOWN_UP", "KNOWN_DOWN" ] + }, + { + "type": "overmap_terrain", + "id": [ "mine_shaft_lower", "mine_shaft_lower_east" ], + "name": "mine shaft", + "sym": "O", + "color": "dark_gray", + "see_cost": 5, + "flags": [ "KNOWN_UP" ] + }, + { + "type": "overmap_terrain", + "id": [ + "mine_spiral_west", + "mine_spiral_central", + "mine_spiral_east", + "mine_spiral_-1_nw", + "mine_spiral_-1_n", + "mine_spiral_-1_ne", + "mine_spiral_-1_sw", + "mine_spiral_-1_s", + "mine_spiral_-1_se", + "mine_spiral_finale_nw", + "mine_spiral_finale_n", + "mine_spiral_finale_ne", + "mine_spiral_finale_sw", + "mine_spiral_finale_s", + "mine_spiral_finale_se" + ], + "name": "mine tunnels", + "sym": "O", + "color": "dark_gray", + "see_cost": 5, + "flags": [ "KNOWN_UP" ] } ] diff --git a/data/json/overmap/overmap_terrain/overmap_terrain_military.json b/data/json/overmap/overmap_terrain/overmap_terrain_military.json index 2fae29b7adae8..5a1343ef167a5 100644 --- a/data/json/overmap/overmap_terrain/overmap_terrain_military.json +++ b/data/json/overmap/overmap_terrain/overmap_terrain_military.json @@ -153,6 +153,15 @@ "see_cost": 2, "flags": [ "KNOWN_UP", "NO_ROTATE", "RISK_HIGH", "SOURCE_WEAPON", "SOURCE_AMMO" ] }, + { + "type": "overmap_terrain", + "id": "bunker_basement_1", + "name": "military bunker", + "sym": "B", + "color": "red", + "see_cost": 2, + "flags": [ "KNOWN_UP", "NO_ROTATE", "RISK_HIGH", "SOURCE_WEAPON", "SOURCE_AMMO" ] + }, { "type": "overmap_terrain", "id": "outpost", @@ -163,6 +172,16 @@ "extras": "build", "flags": [ "RISK_HIGH", "SOURCE_WEAPON", "SOURCE_AMMO" ] }, + { + "type": "overmap_terrain", + "id": "outpost_cross", + "name": "military outpost", + "sym": "M", + "color": "red", + "see_cost": 2, + "extras": "build", + "flags": [ "RISK_HIGH", "SOURCE_WEAPON", "SOURCE_AMMO" ] + }, { "type": "overmap_terrain", "id": [ "silo", "silo_1", "silo_2", "silo_3", "silo_4", "silo_finale" ], diff --git a/data/json/player_activities.json b/data/json/player_activities.json index c5558ddf87138..3ced2d6de6b7b 100644 --- a/data/json/player_activities.json +++ b/data/json/player_activities.json @@ -74,6 +74,28 @@ "no_resume": true, "multi_activity": true }, + { + "id": "ACT_BIKERACK_RACKING", + "type": "activity_type", + "activity_level": "MODERATE_EXERCISE", + "verb": "mounting a vehicle onto rack", + "rooted": true, + "based_on": "time", + "no_resume": true, + "refuel_fires": false, + "auto_needs": false + }, + { + "id": "ACT_BIKERACK_UNRACKING", + "type": "activity_type", + "activity_level": "MODERATE_EXERCISE", + "verb": "taking a vehicle from rack", + "rooted": true, + "based_on": "time", + "no_resume": true, + "refuel_fires": false, + "auto_needs": false + }, { "id": "ACT_MULTIPLE_CHOP_PLANKS", "type": "activity_type", diff --git a/data/json/professions.json b/data/json/professions.json index 542dd95314d7d..e60fc867d5c37 100644 --- a/data/json/professions.json +++ b/data/json/professions.json @@ -136,6 +136,15 @@ { "item": "38_speedloader", "ammo-item": "357mag_fmj", "charges": 7 } ] }, + { + "type": "item_group", + "subtype": "collection", + "id": "m1911_10_magazines_reloaded", + "entries": [ + { "item": "m1911_10mag", "ammo-item": "reloaded_10mm_fmj", "charges": 8 }, + { "item": "m1911_10mag", "ammo-item": "reloaded_10mm_fmj", "charges": 8 } + ] + }, { "type": "item_group", "subtype": "collection", @@ -797,7 +806,13 @@ { "item": "legpouch_large", "contents-group": "army_mags_m4" }, { "item": "ear_plugs", "custom-flags": [ "no_auto_equip" ] }, { "item": "knife_combat", "container-item": "sheath" }, - { "item": "m4a1", "ammo-item": "556", "charges": 30, "contents-item": [ "shoulder_strap", "holo_sight" ] } + { + "item": "nato_assault_rifle", + "variant": "m4a1", + "ammo-item": "556", + "charges": 30, + "contents-item": [ "shoulder_strap", "holo_sight" ] + } ] }, "male": [ "boxer_shorts" ], @@ -986,7 +1001,13 @@ { "item": "legpouch_large", "contents-group": "army_mags_m4" }, { "item": "ear_plugs", "custom-flags": [ "no_auto_equip" ] }, { "item": "knife_combat", "container-item": "sheath" }, - { "item": "m4a1", "ammo-item": "556", "charges": 30, "contents-item": [ "shoulder_strap", "holo_sight" ] } + { + "item": "nato_assault_rifle", + "variant": "m4a1", + "ammo-item": "556", + "charges": 30, + "contents-item": [ "shoulder_strap", "holo_sight" ] + } ] }, "male": [ "boxer_shorts" ], @@ -1084,7 +1105,13 @@ "items": [ "armor_farmor", "socks", "boots_fur", "hat_fur", "gloves_fur", "vest", "wristwatch" ], "entries": [ { "item": "knife_combat", "container-item": "sheath" }, - { "item": "m4a1", "ammo-item": "556", "charges": 30, "contents-item": "shoulder_strap" }, + { + "item": "nato_assault_rifle", + "variant": "m4a1", + "ammo-item": "556", + "charges": 30, + "contents-item": "shoulder_strap" + }, { "item": "stanag30", "ammo-item": "556", "charges": 30 }, { "item": "stanag30", "ammo-item": "556", "charges": 30 }, { "item": "stanag30", "ammo-item": "556", "charges": 30 } @@ -1872,7 +1899,7 @@ "type": "profession", "id": "student", "name": "Student", - "description": "Just an average high school student, you find yourself facing a test you never studied for, and the stakes are a bit higher than geometry. Maybe there'll be something useful in one of these books you've been lugging around all year.", + "description": "Just an average student, you find yourself facing a test you never studied for, and the stakes are a bit higher than geometry. Maybe there'll be something useful in one of these books you've been lugging around all year.", "points": 1, "items": { "both": { @@ -1886,8 +1913,7 @@ "wristwatch", "textbook_speech", "story_book", - "howto_computer", - "novel_coa" + "howto_computer" ], "entries": [ { "group": "charged_cell_phone" } ] }, @@ -2787,7 +2813,7 @@ { "item": "water_clean", "container-item": "canteen" }, { "item": "ear_plugs", "custom-flags": [ "no_auto_equip" ] }, { "item": "knife_combat", "container-item": "sheath" }, - { "item": "m4a1", "ammo-item": "556", "contents-item": "shoulder_strap" }, + { "item": "nato_assault_rifle", "variant": "m4a1", "ammo-item": "556", "contents-item": "shoulder_strap" }, { "item": "medium_plus_battery_cell", "ammo-item": "battery", @@ -3787,7 +3813,13 @@ { "item": "mask_dust", "custom-flags": [ "no_auto_equip" ] }, { "item": "stethoscope", "custom-flags": [ "no_auto_equip" ] }, { "item": "knife_combat", "container-item": "sheath" }, - { "item": "m4a1", "ammo-item": "556", "charges": 30, "contents-item": [ "shoulder_strap", "holo_sight" ] } + { + "item": "nato_assault_rifle", + "variant": "m4a1", + "ammo-item": "556", + "charges": 30, + "contents-item": [ "shoulder_strap", "holo_sight" ] + } ] }, "male": [ "boxer_shorts" ], @@ -3922,5 +3954,56 @@ "male": [ "boxer_shorts" ], "female": [ "bra", "panties" ] } + }, + { + "type": "profession", + "id": "local_drug_dealer", + "name": "Drug Dealer", + "description": "You were about to make your biggest sale yet, but when you met the buyer he tried to bite you. To top it off, it seems that this wasn't some sort of bad high for the user, as the whole town tried going for your throat too.", + "points": 2, + "skills": [ { "level": 1, "name": "melee" }, { "level": 2, "name": "stabbing" }, { "level": 2, "name": "speech" } ], + "items": { + "both": { + "items": [ "hoodie", "jeans", "mbag", "socks", "sneakers", "bandana", "gloves_medical", "switchblade" ], + "entries": [ { "item": "crack", "charges": 25, "container-item": "bag_zipper" } ] + }, + "male": [ "briefs" ], + "female": [ "sports_bra", "boy_shorts" ] + }, + "traits": [ "LIAR" ] + }, + { + "type": "profession", + "id": "handloader", + "name": "Hobby Handloader", + "description": "Out of financial concerns and an appreciation for power, you took up handloading for your mighty handgun. Now that the gunstores are closed, your skills ensure you won't be running out of ammo anytime soon.", + "points": 5, + "proficiencies": [ "prof_handloading" ], + "items": { + "both": { + "items": [ + "pants", + "dress_shirt", + "wristwatch", + "socks", + "boots", + "jacket_light", + "gloves_fingerless", + "press", + "puller", + "slingpack", + "hat_ball" + ], + "entries": [ + { "group": "charged_cell_phone" }, + { "group": "m1911_10_magazines_reloaded" }, + { "item": "ear_plugs", "custom-flags": [ "no_auto_equip" ] }, + { "item": "m1911_10", "ammo-item": "reloaded_10mm_fmj", "charges": 8, "container-item": "holster" } + ] + }, + "male": [ "briefs" ], + "female": [ "bra", "panties" ] + }, + "skills": [ { "level": 3, "name": "fabrication" }, { "level": 3, "name": "gun" }, { "level": 3, "name": "pistol" } ] } ] diff --git a/data/json/proficiencies/misc.json b/data/json/proficiencies/misc.json index d5e697aea28a6..8c43f0eaba211 100644 --- a/data/json/proficiencies/misc.json +++ b/data/json/proficiencies/misc.json @@ -134,5 +134,15 @@ "default_fail_multiplier": 1.2, "time_to_learn": "10 h", "required_proficiencies": [ "prof_fine_metalsmithing", "prof_redsmithing" ] + }, + { + "type": "proficiency", + "id": "prof_handloading", + "name": { "str": "Handloading" }, + "description": "You know how to accurately measure powder and projectile weights for reloading firearm cartridges.", + "can_learn": true, + "default_time_multiplier": 1.5, + "default_fail_multiplier": 5, + "time_to_learn": "8 h" } ] diff --git a/data/json/proficiencies/tailoring.json b/data/json/proficiencies/tailoring.json index 8bf755aeb0712..9017032e4667a 100644 --- a/data/json/proficiencies/tailoring.json +++ b/data/json/proficiencies/tailoring.json @@ -162,7 +162,7 @@ "type": "proficiency", "id": "prof_polymerworking", "name": { "str": "Advanced polymer sewing" }, - "description": "You know the tricks for working with kevlar, nomex, and other advanced polymer cloth.", + "description": "You know the tricks for working with Kevlar, Nomex, and other advanced polymer cloth.", "can_learn": true, "default_time_multiplier": 2, "default_fail_multiplier": 1.25, diff --git a/data/json/recipes/ammo/40x46mm.json b/data/json/recipes/ammo/40x46mm.json index e6ae3fa91ea05..a19c439a1d90d 100644 --- a/data/json/recipes/ammo/40x46mm.json +++ b/data/json/recipes/ammo/40x46mm.json @@ -15,6 +15,7 @@ "reversible": true, "//": "186 mg gunpowder rounded to 2 100 mg 'pieces', same as factory M576 load.", "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ @@ -40,6 +41,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "//": "186 mg gunpowder rounded to 2 100 mg 'pieces', same as factory M576 load.", @@ -66,6 +68,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 1 ], [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "//": "186 mg gunpowder rounded to 2 100 mg 'pieces', same as factory M576 load.", @@ -92,6 +95,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 1 ], [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "//": "186 mg gunpowder rounded to 2 100 mg 'pieces', same as factory M576 load.", @@ -120,6 +124,7 @@ "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "//": "186 mg gunpowder rounded to 2 100 mg 'pieces', same as factory M576 load.", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "sheet_metal_small", 1 ] ], [ [ "paper", 1 ], [ "wax", 1 ] ], @@ -146,6 +151,7 @@ "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "//": "186 mg gunpowder rounded to 2 100 mg 'pieces', same as factory M576 load.", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "sheet_metal_small", 1 ] ], [ [ "paper", 1 ], [ "wax", 1 ] ], @@ -170,6 +176,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ @@ -195,6 +202,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ @@ -220,6 +228,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 1 ], [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ @@ -245,6 +254,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 1 ], [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ @@ -271,6 +281,7 @@ "reversible": true, "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "sheet_metal_small", 1 ] ], [ [ "paper", 1 ], [ "wax", 1 ] ], @@ -296,6 +307,7 @@ "reversible": true, "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "sheet_metal_small", 1 ] ], [ [ "paper", 1 ], [ "wax", 1 ] ], diff --git a/data/json/recipes/ammo/40x53mm.json b/data/json/recipes/ammo/40x53mm.json index 6d3778b2df483..e03a7bc5438e3 100644 --- a/data/json/recipes/ammo/40x53mm.json +++ b/data/json/recipes/ammo/40x53mm.json @@ -14,6 +14,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 12 ], [ "ammo_bullet", 126 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "//": "11.5x the propellant and 11.5x the payload of 12 gauge 00 shot load. 13409 mg gunpowder rounded to 134 100 mg 'pieces'", @@ -40,6 +41,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 12 ], [ "ammo_bullet", 115 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "//": "11.5x the propellant and 11.5x the payload of 12 gauge slug load. 19366 mg gunpowder rounded to 194 100 mg 'pieces'", @@ -68,6 +70,7 @@ "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "//": "11.5x the propellant and 11.5x the payload of 12 gauge flechette load. 8625 mg gunpowder rounded to 86 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "sheet_metal_small", 1 ] ], [ [ "paper", 1 ], [ "wax", 1 ] ], @@ -92,6 +95,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 12 ], [ "ammo_bullet", 126 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ @@ -117,6 +121,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 12 ], [ "ammo_bullet", 115 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ @@ -143,6 +148,7 @@ "reversible": true, "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "sheet_metal_small", 1 ] ], [ [ "paper", 1 ], [ "wax", 1 ] ], diff --git a/data/json/recipes/ammo/pistol.json b/data/json/recipes/ammo/pistol.json index 66607ed15581f..6f1669fb514b7 100644 --- a/data/json/recipes/ammo/pistol.json +++ b/data/json/recipes/ammo/pistol.json @@ -14,6 +14,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_9mm", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 3 ] ], [ [ "copper", 1 ] ] ] }, { @@ -31,6 +32,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_9mm", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 3 ] ], [ [ "copper", 1 ] ] ] }, { @@ -48,6 +50,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 4 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "40_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], [ [ "chem_black_powder", 3 ] ], [ [ "copper", 1 ] ] ] }, { @@ -65,6 +68,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 4 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "40_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], [ [ "chem_black_powder", 6 ] ], [ [ "copper", 1 ] ] ] }, { @@ -82,6 +86,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "32_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], [ [ "chem_black_powder", 2 ] ], [ [ "copper", 1 ] ] ] }, { @@ -99,6 +104,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "38_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], [ [ "chem_black_powder", 3 ] ], [ [ "copper", 1 ] ] ] }, { @@ -116,6 +122,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 5 ], [ "ammo_bullet", 4 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "44_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], [ [ "chem_black_powder", 7 ] ], [ [ "copper", 2 ] ] ] }, { @@ -133,6 +140,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 4 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "45_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], [ [ "chem_black_powder", 3 ] ], [ [ "copper", 2 ] ] ] }, { @@ -150,6 +158,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 4 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "45_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], [ [ "chem_black_powder", 5 ] ], [ [ "copper", 1 ] ] ] }, { @@ -167,6 +176,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "46mm_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], [ [ "chem_black_powder", 4 ] ], [ [ "copper", 1 ] ] ] }, { @@ -184,6 +194,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 3 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "460_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], [ [ "chem_black_powder", 8 ] ], [ [ "copper", 2 ] ] ] }, { @@ -201,6 +212,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 3 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "460_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], [ [ "chem_black_powder", 8 ] ], [ [ "copper", 2 ] ] ] }, { @@ -218,6 +230,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 6 ], [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "500_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], [ [ "chem_black_powder", 19 ] ], [ [ "copper", 5 ] ] ] }, { @@ -235,6 +248,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_762_25", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 4 ] ], [ [ "copper", 1 ] ] ] }, { @@ -252,6 +266,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "9x18mm_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], [ [ "chem_black_powder", 4 ] ], [ [ "copper", 1 ] ] ] }, { @@ -269,6 +284,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "9x18mm_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], [ [ "chem_black_powder", 4 ] ], [ [ "copper", 1 ] ] ] }, { @@ -286,6 +302,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_380", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 3 ] ], [ [ "copper", 1 ] ] ] }, { @@ -303,6 +320,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_380", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 3 ] ], [ [ "copper", 1 ] ] ] }, { @@ -320,6 +338,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 1 ], [ "ammo_57", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 4 ] ], [ [ "copper", 1 ] ] ] }, { @@ -338,6 +357,7 @@ "reversible": true, "using": [ [ "ammo_bullet", 1 ] ], "qualities": [ { "id": "CUT", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 4 ] ], [ [ "smpistol_primer", 1 ] ], [ [ "paper", 1 ], [ "aluminum_foil", 1 ] ] ] }, { @@ -356,6 +376,7 @@ "reversible": true, "using": [ [ "ammo_bullet", 2 ] ], "qualities": [ { "id": "CUT", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 4 ] ], [ [ "lgpistol_primer", 1 ] ], [ [ "paper", 1 ], [ "aluminum_foil", 1 ] ] ] }, { @@ -374,6 +395,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], "//": "207 mg gunpowder rounded to 2 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "32_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], @@ -397,6 +419,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], "//": "315 mg gunpowder rounded to 3 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "38_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], @@ -420,6 +443,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 3 ] ], "//": "315 mg gunpowder rounded to 3 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "38_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], @@ -442,6 +466,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 1 ], [ "ammo_38super", 1 ] ], "//": "323 mg gunpowder rounded to 3 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 3 ], [ "gunpowder_pistol", 3 ], [ "gunpowder_shotgun", 3 ] ], [ [ "copper", 1 ] ] ] }, { @@ -460,6 +485,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 7 ], [ "ammo_357sig", 1 ] ], "//": "370 mg gunpowder rounded to 4 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 4 ], [ "gunpowder_rifle", 4 ], [ "gunpowder_magnum_pistol", 4 ] ], [ [ "copper", 1 ] ] ] }, { @@ -478,6 +504,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 8 ], [ "ammo_357sig", 1 ] ], "//": "531 mg gunpowder rounded to 5 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 5 ], [ "gunpowder_rifle", 5 ], [ "gunpowder_magnum_pistol", 5 ] ], [ [ "copper", 1 ] ] ] }, { @@ -495,6 +522,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 7 ], [ "ammo_357sig", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 4 ] ], [ [ "copper", 1 ] ] ] }, { @@ -512,6 +540,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 8 ], [ "ammo_357sig", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 5 ] ], [ [ "copper", 1 ] ] ] }, { @@ -530,6 +559,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 3 ], [ "ammo_357mag", 1 ] ], "//": "520 mg gunpowder rounded to 5 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 5 ], [ "gunpowder_rifle", 5 ], [ "gunpowder_magnum_pistol", 5 ] ], [ [ "copper", 1 ] ] ] }, { @@ -548,6 +578,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 4 ], [ "ammo_357mag", 1 ] ], "//": "520 mg gunpowder rounded to 5 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 5 ], [ "gunpowder_rifle", 5 ], [ "gunpowder_magnum_pistol", 5 ] ], [ [ "copper", 1 ] ] ] }, { @@ -565,6 +596,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 3 ], [ "ammo_357mag", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 5 ] ], [ [ "copper", 1 ] ] ] }, { @@ -582,6 +614,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 4 ], [ "ammo_357mag", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 5 ] ], [ [ "copper", 1 ] ] ] }, { @@ -599,6 +632,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 3 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "38_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], [ [ "chem_black_powder", 3 ] ] ] }, { @@ -617,6 +651,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], "//": "453 mg gunpowder rounded to 5 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "38_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], @@ -639,6 +674,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 4 ] ], "//": "320 mg gunpowder rounded to 3 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "40_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], @@ -662,6 +698,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 4 ] ], "//": "576 mg gunpowder rounded to 6 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "40_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], @@ -685,6 +722,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 3 ], [ "ammo_10mm", 1 ] ], "//": "330 mg gunpowder rounded to 3 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 3 ], [ "gunpowder_magnum_pistol", 3 ] ], [ [ "copper", 1 ] ] ] }, { @@ -702,6 +740,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 3 ], [ "ammo_10mm", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 3 ] ], [ [ "copper", 1 ] ] ] }, { @@ -720,6 +759,7 @@ "reversible": true, "using": [ [ "bullet_forming", 5 ], [ "ammo_bullet", 4 ] ], "//": "660 mg gunpowder rounded to 7 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "44_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], @@ -743,6 +783,7 @@ "reversible": true, "using": [ [ "bullet_forming", 5 ], [ "ammo_bullet", 4 ] ], "//": "660 mg gunpowder rounded to 7 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "44_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], @@ -765,6 +806,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 5 ], [ "ammo_bullet", 4 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "44_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], [ [ "chem_black_powder", 7 ] ], [ [ "copper", 2 ] ] ] }, { @@ -783,6 +825,7 @@ "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 4 ] ], "//": "330 mg gunpowder rounded to 3 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "45_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], @@ -806,6 +849,7 @@ "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 4 ] ], "//": "466 mg gunpowder rounded to 5 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "45_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], @@ -829,6 +873,7 @@ "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 4 ] ], "//": "513 mg gunpowder rounded to 5 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "45_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], @@ -852,6 +897,7 @@ "reversible": true, "using": [ [ "bullet_forming", 5 ], [ "ammo_bullet", 6 ] ], "//": "1574 mg gunpowder rounded to 16 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "454_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], @@ -874,6 +920,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 5 ], [ "ammo_bullet", 6 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "454_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], [ [ "chem_black_powder", 16 ] ], [ [ "copper", 2 ] ] ] }, { @@ -892,6 +939,7 @@ "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 5 ], [ "ammo_45colt", 1 ] ], "//": "505 mg gunpowder rounded to 5 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 5 ], [ "gunpowder_pistol", 5 ], [ "gunpowder_shotgun", 5 ] ], [ [ "copper", 2 ] ] ] }, { @@ -909,6 +957,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 5 ], [ "ammo_45colt", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 5 ] ], [ [ "copper", 2 ] ] ] }, { @@ -927,6 +976,7 @@ "reversible": true, "//": "Unable to find load data, used 5.7 data as cartridges are similar. 388 mg gunpowder rounded to 4 100 mg 'pieces'", "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "46mm_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], @@ -950,6 +1000,7 @@ "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 3 ] ], "//": "842 mg gunpowder rounded to 8 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "460_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], @@ -973,6 +1024,7 @@ "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 3 ] ], "//": "842 mg gunpowder rounded to 8 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "460_casing", 1 ] ], [ [ "lgpistol_primer", 1 ] ], @@ -996,6 +1048,7 @@ "reversible": true, "using": [ [ "bullet_forming", 6 ], [ "ammo_bullet", 8 ] ], "//": "1943 mg gunpowder rounded to 19 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "500_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -1019,6 +1072,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 1 ] ], "//": "388 mg gunpowder rounded to 4 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "57mm_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], @@ -1041,6 +1095,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_762_25", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "390 mg gunpowder rounded to 4 100 mg 'pieces'", "components": [ [ [ "gunpowder", 4 ], [ "gunpowder_pistol", 4 ] ], [ [ "copper", 1 ] ] ] }, @@ -1059,6 +1114,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_9mm", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "290 mg gunpowder rounded to 3 100 mg 'pieces'", "components": [ [ [ "gunpowder", 3 ], [ "gunpowder_pistol", 3 ], [ "gunpowder_shotgun", 3 ] ], [ [ "copper", 1 ] ] ] }, @@ -1077,6 +1133,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_9mm", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "537 mg gunpowder rounded to 5 100 mg 'pieces'", "components": [ [ [ "gunpowder", 5 ], [ "gunpowder_pistol", 5 ] ], [ [ "copper", 1 ] ] ] }, @@ -1095,6 +1152,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_9mm", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "Arbitrary powder amount as +p+ is not a real standard but an informal one which has about 20% higher pressure than standard 9mm, while +p is a standard that has 10% higher pressure. 580 mg gunpowder rounded to 6 100 mg 'pieces'", "components": [ [ [ "gunpowder", 6 ], [ "gunpowder_pistol", 6 ] ], [ [ "copper", 1 ] ] ] }, @@ -1113,6 +1171,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_9mm", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "290 mg gunpowder rounded to 3 100 mg 'pieces'", "components": [ [ [ "gunpowder", 3 ], [ "gunpowder_pistol", 3 ], [ "gunpowder_shotgun", 3 ] ], [ [ "copper", 1 ] ] ] }, @@ -1132,6 +1191,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], "//": "401 mg gunpowder rounded to 4 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "9x18mm_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], @@ -1155,6 +1215,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], "//": "401 mg gunpowder rounded to 4 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "9x18mm_casing", 1 ] ], [ [ "smpistol_primer", 1 ] ], @@ -1177,6 +1238,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "Arbitrary powder amount as +p+ is not a real standard but an informal one which has about 20% higher pressure than standard 9mm, while +p is a standard that has 10% higher pressure. 481 mg gunpowder rounded to 5 100 mg 'pieces'", "components": [ [ [ "9x18mm_casing", 1 ] ], @@ -1200,6 +1262,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_380", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "278 mg gunpowder rounded to 3 100 mg 'pieces'", "components": [ [ [ "gunpowder", 3 ], [ "gunpowder_pistol", 3 ], [ "gunpowder_shotgun", 3 ] ], [ [ "copper", 1 ] ] ] }, @@ -1218,6 +1281,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_380", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "320 mg gunpowder rounded to 3 100 mg 'pieces'", "components": [ [ [ "gunpowder", 3 ], [ "gunpowder_pistol", 3 ], [ "gunpowder_shotgun", 3 ] ], [ [ "copper", 1 ] ] ] }, @@ -1236,6 +1300,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "ammo_380", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "278 mg gunpowder rounded to 3 100 mg 'pieces'", "components": [ [ [ "gunpowder", 3 ], [ "gunpowder_pistol", 3 ], [ "gunpowder_shotgun", 3 ] ], [ [ "copper", 1 ] ] ] } diff --git a/data/json/recipes/ammo/rifle.json b/data/json/recipes/ammo/rifle.json index 12a35a9055660..1b02e79926de9 100644 --- a/data/json/recipes/ammo/rifle.json +++ b/data/json/recipes/ammo/rifle.json @@ -15,6 +15,7 @@ "reversible": true, "using": [ [ "ammo_bullet", 10 ] ], "qualities": [ { "id": "CUT", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 30 ] ], [ [ "paper", 1 ] ] ] }, { @@ -33,6 +34,7 @@ "reversible": true, "using": [ [ "ammo_bullet", 10 ] ], "qualities": [ { "id": "CUT", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 30 ] ], [ [ "paper", 1 ] ] ] }, { @@ -51,6 +53,7 @@ "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 1 ] ], "//": "65 mg gunpowder rounded to 1 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "22_casing_new", 1 ] ], [ [ "gunpowder", 1 ], [ "gunpowder_pistol", 1 ] ], [ [ "copper", 1 ] ] ] }, { @@ -69,6 +72,7 @@ "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 1 ] ], "//": "65 mg gunpowder rounded to 1 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "22_casing_new", 1 ] ], [ [ "gunpowder", 1 ], [ "gunpowder_pistol", 1 ] ] ] }, { @@ -86,6 +90,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 3 ], [ "ammo_bullet", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "22_casing_new", 1 ] ], [ [ "chem_black_powder", 1 ] ], [ [ "copper", 1 ] ] ] }, { @@ -103,6 +108,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "22_casing_new", 1 ] ], [ [ "chem_black_powder", 1 ] ] ] }, { @@ -121,6 +127,7 @@ "reversible": true, "using": [ [ "bullet_forming", 4 ], [ "ammo_bullet", 1 ] ], "//": "1500 mg gunpowder rounded to 15 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "223_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], @@ -143,6 +150,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 4 ], [ "ammo_bullet", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "223_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], [ [ "chem_black_powder", 15 ] ], [ [ "copper", 1 ] ] ] }, { @@ -161,6 +169,7 @@ "reversible": true, "using": [ [ "bullet_forming", 8 ], [ "ammo_bullet", 3 ], [ "ammo_270win", 1 ] ], "//": "2974 mg gunpowder rounded to 30 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 30 ], [ "gunpowder_magnum_pistol", 30 ], [ "gunpowder_rifle", 30 ] ], [ [ "copper", 1 ] ] ] }, { @@ -178,6 +187,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 8 ], [ "ammo_bullet", 3 ], [ "ammo_270win", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 30 ] ], [ [ "copper", 1 ] ] ] }, { @@ -196,6 +206,7 @@ "reversible": true, "using": [ [ "bullet_forming", 12 ], [ "ammo_bullet", 4 ] ], "//": "4327 mg gunpowder rounded to 43 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "300_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -218,6 +229,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 12 ], [ "ammo_bullet", 4 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "300_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], [ [ "chem_black_powder", 43 ] ], [ [ "copper", 2 ] ] ] }, { @@ -236,6 +248,7 @@ "reversible": true, "using": [ [ "bullet_forming", 12 ], [ "ammo_bullet", 3 ] ], "//": "3200 mg gunpowder rounded to 32 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "3006_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -259,6 +272,7 @@ "reversible": true, "using": [ [ "bullet_forming", 12 ], [ "ammo_bullet", 4 ] ], "//": "3300 mg gunpowder rounded to 33 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "3006_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -282,6 +296,7 @@ "reversible": true, "using": [ [ "bullet_forming", 12 ], [ "ammo_bullet", 4 ] ], "//": "3300 mg gunpowder rounded to 33 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "3006_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -310,6 +325,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 12 ], [ "ammo_bullet", 4 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "3006_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], [ [ "chem_black_powder", 32 ] ], [ [ "copper", 2 ] ] ] }, { @@ -327,6 +343,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 12 ], [ "ammo_bullet", 4 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "3006_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], [ [ "chem_black_powder", 33 ] ], [ [ "copper", 2 ] ] ] }, { @@ -344,6 +361,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 12 ], [ "ammo_bullet", 4 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "3006_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -368,6 +386,7 @@ "reversible": true, "using": [ [ "bullet_forming", 9 ], [ "ammo_bullet", 3 ] ], "//": "2800 mg gunpowder rounded to 28 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "308_casing", 1 ], [ "762_51_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -395,6 +414,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 9 ], [ "ammo_bullet", 3 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "308_casing", 1 ], [ "762_51_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -418,6 +438,7 @@ "reversible": true, "using": [ [ "bullet_forming", 15 ], [ "ammo_bullet", 5 ] ], "//": "2844 mg gunpowder rounded to 28 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "4570_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -442,6 +463,7 @@ "reversible": true, "using": [ [ "bullet_forming", 15 ], [ "ammo_bullet", 3 ] ], "//": "3000 mg gunpowder rounded to 30 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "4570_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -465,6 +487,7 @@ "reversible": true, "using": [ [ "bullet_forming", 15 ], [ "ammo_bullet", 8 ] ], "//": "1944 mg gunpowder rounded to 19 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "4570_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -487,6 +510,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 15 ], [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "4570_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], [ [ "chem_black_powder", 19 ] ], [ [ "copper", 3 ] ] ] }, { @@ -503,7 +527,7 @@ "book_learn": [ [ "recipe_caseless", 4 ] ], "charges": 1, "reversible": true, - "proficiencies": [ { "proficiency": "prof_plasticworking" } ], + "proficiencies": [ { "proficiency": "prof_plasticworking" }, { "proficiency": "prof_handloading" } ], "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ], [ "plastic_molding", 1 ] ], "//": "3000 mg gunpowder rounded to 30 100 mg 'pieces'", "components": [ @@ -529,6 +553,7 @@ "reversible": true, "using": [ [ "bullet_forming", 18 ], [ "ammo_bullet", 12 ] ], "//": "15800 mg gunpowder rounded to 158 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "50_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -551,6 +576,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 18 ], [ "ammo_bullet", 12 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "50_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], [ [ "chem_black_powder", 158 ] ], [ [ "copper", 6 ] ] ] }, { @@ -569,6 +595,7 @@ "reversible": true, "using": [ [ "bullet_forming", 21 ], [ "ammo_bullet", 12 ] ], "//": "15800 mg gunpowder rounded to 158 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "50_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -592,6 +619,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 21 ], [ "ammo_bullet", 12 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "50_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -616,6 +644,7 @@ "reversible": true, "using": [ [ "bullet_forming", 21 ] ], "//": "15800 mg gunpowder rounded to 158 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "50_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -639,6 +668,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 21 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "50_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -663,6 +693,7 @@ "reversible": true, "using": [ [ "bullet_forming", 4 ], [ "ammo_bullet", 1 ] ], "//": "1295 mg gunpowder rounded to 13 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "545_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], @@ -685,6 +716,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 4 ], [ "ammo_bullet", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "545_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], [ [ "chem_black_powder", 13 ] ], [ [ "copper", 1 ] ] ] }, { @@ -703,6 +735,7 @@ "reversible": true, "using": [ [ "bullet_forming", 4 ], [ "ammo_bullet", 1 ] ], "//": "1295 mg gunpowder rounded to 13 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "545_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], @@ -725,6 +758,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 4 ], [ "ammo_bullet", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "545_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], [ [ "chem_black_powder", 13 ] ], [ [ "copper", 1 ] ] ] }, { @@ -742,6 +776,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 4 ], [ "ammo_bullet", 3 ], [ "ammo_300blk", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "1133 mg gunpowder rounded to 11 100 mg 'pieces'", "components": [ [ [ "gunpowder", 11 ] ], [ [ "copper", 1 ] ] ] }, @@ -760,6 +795,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 4 ], [ "ammo_bullet", 3 ], [ "ammo_300blk", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 11 ] ], [ [ "copper", 1 ] ] ] }, { @@ -778,6 +814,7 @@ "reversible": true, "using": [ [ "bullet_forming", 20 ] ], "//": "1717 mg gunpowder rounded to 17 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "223_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], @@ -802,6 +839,7 @@ "reversible": true, "using": [ [ "bullet_forming", 4 ], [ "ammo_bullet", 1 ] ], "//": "1717 mg gunpowder rounded to 17 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "223_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], @@ -825,6 +863,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 20 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "223_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], @@ -848,6 +887,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 4 ], [ "ammo_bullet", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "223_casing", 1 ] ], [ [ "smrifle_primer", 1 ] ], @@ -872,6 +912,7 @@ "reversible": true, "using": [ [ "bullet_forming", 20 ], [ "ammo_bullet", 18 ] ], "//": "16523 mg gunpowder rounded to 165 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "700nx_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -894,6 +935,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 20 ], [ "ammo_bullet", 18 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "700nx_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], [ [ "chem_black_powder", 165 ] ], [ [ "copper", 10 ] ] ] }, { @@ -912,6 +954,7 @@ "reversible": true, "using": [ [ "bullet_forming", 9 ], [ "ammo_bullet", 3 ] ], "//": "2462 mg gunpowder rounded to 25 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "308_casing", 1 ], [ "762_51_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -935,6 +978,7 @@ "reversible": true, "using": [ [ "bullet_forming", 9 ], [ "ammo_bullet", 3 ] ], "//": "2462 mg gunpowder rounded to 25 100 mg 'pieces'", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "308_casing", 1 ], [ "762_51_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -963,6 +1007,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 9 ], [ "ammo_bullet", 3 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "308_casing", 1 ], [ "762_51_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -985,6 +1030,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 9 ], [ "ammo_bullet", 3 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "308_casing", 1 ], [ "762_51_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -1008,6 +1054,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 10 ], [ "ammo_bullet", 3 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "2851 mg gunpowder rounded to 29 100 mg 'pieces'", "components": [ [ [ "762R_casing", 1 ] ], @@ -1036,6 +1083,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 10 ], [ "ammo_bullet", 3 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "762R_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], [ [ "chem_black_powder", 29 ] ], [ [ "copper", 2 ] ] ] }, { @@ -1053,6 +1101,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 5 ], [ "ammo_bullet", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "1535 mg gunpowder rounded to 15 100 mg 'pieces'", "components": [ [ [ "762_casing", 1 ] ], @@ -1077,6 +1126,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 5 ], [ "ammo_bullet", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "762_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], @@ -1100,6 +1150,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 5 ], [ "ammo_bullet", 3 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "//": "1535 mg gunpowder rounded to 15 100 mg 'pieces'", "components": [ [ [ "762_casing", 1 ] ], @@ -1123,6 +1174,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 5 ], [ "ammo_bullet", 3 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "762_casing", 1 ] ], [ [ "lgrifle_primer", 1 ] ], [ [ "chem_black_powder", 15 ] ], [ [ "copper", 2 ] ] ] } ] diff --git a/data/json/recipes/ammo/shot.json b/data/json/recipes/ammo/shot.json index 649c15cc5ab55..81f5a7e544f77 100644 --- a/data/json/recipes/ammo/shot.json +++ b/data/json/recipes/ammo/shot.json @@ -15,6 +15,7 @@ "reversible": true, "//": "Assumes standard 9 pellet 00 load. 1166 mg gunpowder rounded to 12 100 mg 'pieces'", "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 11 ], [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "components": [ [ [ "gunpowder", 12 ], [ "gunpowder_pistol", 12 ], [ "gunpowder_shotgun", 12 ] ] ] }, @@ -33,6 +34,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 8 ], [ "ammo_410shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "//": "971 mg gunpowder rounded to 10 100 mg 'pieces'", "components": [ [ [ "gunpowder", 10 ], [ "gunpowder_pistol", 10 ], [ "gunpowder_shotgun", 10 ] ] ] @@ -52,6 +54,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 10 ], [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "//": "1490 mg gunpowder rounded to 15 100 mg 'pieces'", "components": [ [ [ "gunpowder", 15 ], [ "gunpowder_pistol", 15 ], [ "gunpowder_shotgun", 15 ] ] ] @@ -70,6 +73,7 @@ "book_learn": [ [ "recipe_bullets", 3 ], [ "manual_shotgun", 3 ] ], "charges": 1, "using": [ [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "//": "same charge as a 1 oz load, could find no refference to actual loading data. 1490 mg gunpowder rounded to 15 100 mg 'pieces'", "components": [ [ [ "gunpowder", 15 ], [ "gunpowder_pistol", 15 ], [ "gunpowder_shotgun", 15 ] ], [ [ "magnesium", 5 ] ] ] @@ -89,6 +93,7 @@ "charges": 1, "reversible": true, "using": [ [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "//": "Wikipedia states the shells contain 20 7.3 grain flechettes for a 146 grain or 1/3 oz load. http://www.sabotdesigns.com/002.html lists flechette velocity at 1950 fps. Gunpowder charge is calculated as if it was a solid slug for sake of ease. 750 mg gunpowder rounded to 8 100 mg 'pieces", "components": [ [ [ "gunpowder", 8 ], [ "gunpowder_pistol", 8 ], [ "gunpowder_shotgun", 8 ] ], [ [ "combatnail", 10 ] ] ] @@ -109,6 +114,7 @@ "reversible": true, "//": "assumes 1 oz slug. 1684 mg gunpowder rounded to 17 100 mg 'pieces'", "using": [ [ "bullet_forming", 1 ], [ "ammo_bullet", 10 ], [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 17 ], [ "gunpowder_pistol", 17 ], [ "gunpowder_shotgun", 17 ] ] ] }, { @@ -126,6 +132,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 11 ], [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "components": [ [ [ "chem_black_powder", 12 ] ] ] }, @@ -145,6 +152,7 @@ "reversible": true, "//": "assumes 1 oz load", "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 10 ], [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "components": [ [ [ "chem_black_powder", 15 ] ] ] }, @@ -163,6 +171,7 @@ "charges": 1, "reversible": true, "using": [ [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "components": [ [ [ "chem_black_powder", 15 ] ], [ [ "magnesium", 5 ] ] ] }, @@ -181,6 +190,7 @@ "charges": 1, "reversible": true, "using": [ [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "components": [ [ [ "chem_black_powder", 8 ] ], [ [ "combatnail", 10 ] ] ] }, @@ -199,6 +209,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 1 ], [ "ammo_bullet", 10 ], [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 17 ] ] ] }, { @@ -272,6 +283,7 @@ "tools": [ [ [ "press", -1 ] ] ], "//": "1166 mg gunpowder rounded to 12 100 mg 'pieces'; recipe makes two shells", "//1": "currently halved inputs (rounding up) and result pending fix of casing duplication", + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 12 ], [ "gunpowder_pistol", 12 ], [ "gunpowder_shotgun", 12 ] ], [ @@ -304,6 +316,7 @@ "charges": 1, "reversible": true, "using": [ [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "//1": "currently halved inputs (rounding up) and result pending fix of casing duplication", "components": [ @@ -365,6 +378,7 @@ "book_learn": [ [ "recipe_bullets", 3 ], [ "manual_shotgun", 2 ], [ "pocket_survival", 2 ], [ "mag_survival", 2 ] ], "charges": 1, "using": [ [ "ammo_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "components": [ [ [ "incendiary", 25 ] ] ] }, @@ -383,6 +397,7 @@ "charges": 1, "reversible": true, "using": [ [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ [ [ "chem_black_powder", 15 ] ], [ [ "shotgun_primer", 1 ] ], [ [ "paper", 1 ], [ "aluminum_foil", 1 ] ] ] }, @@ -401,6 +416,7 @@ "charges": 1, "reversible": true, "using": [ [ "ammo_bullet", 16 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ [ [ "chem_black_powder", 15 ] ], [ [ "shotgun_primer", 1 ] ], [ [ "paper", 1 ], [ "aluminum_foil", 1 ] ] ] }, @@ -419,6 +435,7 @@ "charges": 1, "reversible": true, "using": [ [ "ammo_bullet", 16 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ [ [ "chem_black_powder", 12 ] ], @@ -442,6 +459,7 @@ "charges": 1, "reversible": true, "using": [ [ "ammo_bullet", 8 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ [ [ "chem_black_powder", 15 ] ], [ [ "shotgun_primer", 1 ] ], [ [ "paper", 1 ], [ "aluminum_foil", 1 ] ] ] } diff --git a/data/json/recipes/armor/feet.json b/data/json/recipes/armor/feet.json index 1748c4da478c1..cdf5a0ae19363 100644 --- a/data/json/recipes/armor/feet.json +++ b/data/json/recipes/armor/feet.json @@ -154,6 +154,43 @@ "using": [ [ "sewing_standard", 16 ], [ "tailoring_leather_small", 8 ], [ "tailoring_fur_small", 8 ], [ "fastener_small", 2 ] ], "time": "15 h" }, + { + "result": "boots_wsurvivor_nofur", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "category": "CC_ARMOR", + "subcategory": "CSC_ARMOR_FEET", + "skill_used": "tailor", + "difficulty": 7, + "skills_required": [ "fabrication", 5 ], + "time": "24 h", + "autolearn": true, + "using": [ [ "tailoring_kevlar_fabric", 4 ], [ "fastener_small", 2 ] ], + "proficiencies": [ + { "proficiency": "prof_cobbling" }, + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_closures_waterproofing" }, + { "proficiency": "prof_polymerworking" } + ], + "components": [ + [ [ "faux_fur", 12 ] ], + [ [ "duct_tape", 100 ] ], + [ [ "boots_combat", 1 ], [ "boots_steel", 1 ], [ "boots_hiking", 1 ], [ "boots_bunker", 1 ], [ "boots", 1 ] ] + ] + }, + { + "result": "xl_boots_wsurvivor_nofur", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "copy-from": "boots_wsurvivor_nofur", + "using": [ [ "tailoring_kevlar_fabric", 6 ], [ "fastener_small", 2 ] ], + "components": [ + [ [ "faux_fur", 16 ] ], + [ [ "duct_tape", 150 ] ], + [ [ "boots_combat", 1 ], [ "boots_steel", 1 ], [ "boots_hiking", 1 ], [ "boots_bunker", 1 ], [ "boots", 1 ] ] + ], + "time": "32 h" + }, { "result": "boots_h20survivor", "type": "recipe", diff --git a/data/json/recipes/armor/hands.json b/data/json/recipes/armor/hands.json index ae4d8597341ce..062c77558098f 100644 --- a/data/json/recipes/armor/hands.json +++ b/data/json/recipes/armor/hands.json @@ -186,6 +186,54 @@ "time": "13 h", "using": [ [ "tailoring_fur_small", 5 ] ] }, + { + "result": "gloves_wsurvivor_nofur", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "category": "CC_ARMOR", + "subcategory": "CSC_ARMOR_HANDS", + "skill_used": "tailor", + "difficulty": 7, + "skills_required": [ "fabrication", 5 ], + "time": "20 h", + "autolearn": true, + "using": [ [ "sewing_standard", 50 ], [ "tailoring_kevlar_fabric", 1 ] ], + "proficiencies": [ + { "proficiency": "prof_closures_waterproofing" }, + { "proficiency": "prof_polymerworking" }, + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_articulation" } + ], + "components": [ + [ [ "duct_tape", 80 ] ], + [ [ "faux_fur", 4 ] ], + [ + [ "gloves_leather", 1 ], + [ "gloves_light", 1 ], + [ "gloves_liner", 1 ], + [ "wetsuit_gloves", 1 ], + [ "fire_gauntlets", 1 ] + ] + ] + }, + { + "result": "xl_gloves_wsurvivor_nofur", + "type": "recipe", + "copy-from": "gloves_wsurvivor_nofur", + "time": "26 h", + "using": [ [ "sewing_standard", 75 ], [ "tailoring_kevlar_fabric", 2 ] ], + "components": [ + [ [ "duct_tape", 120 ] ], + [ [ "faux_fur", 5 ] ], + [ + [ "gloves_leather", 1 ], + [ "gloves_light", 1 ], + [ "gloves_liner", 1 ], + [ "wetsuit_gloves", 1 ], + [ "fire_gauntlets", 1 ] + ] + ] + }, { "result": "gloves_h20survivor", "type": "recipe", diff --git a/data/json/recipes/armor/head.json b/data/json/recipes/armor/head.json index 2fe1c07ecaac2..873b72beb7f40 100644 --- a/data/json/recipes/armor/head.json +++ b/data/json/recipes/armor/head.json @@ -42,18 +42,15 @@ "components": [ [ [ "felt_patch", 4 ] ] ] }, { - "result": "blindfold", + "result": "blindfold_duct", "type": "recipe", "activity_level": "NO_EXERCISE", - "id_suffix": "from_tape", "category": "CC_ARMOR", "subcategory": "CSC_ARMOR_HEAD", "skill_used": "fabrication", - "difficulty": 1, "time": "3 m", - "reversible": true, "autolearn": true, - "components": [ [ [ "duct_tape", 10 ] ] ], + "components": [ [ [ "duct_tape", 50 ] ] ], "flags": [ "BLIND_EASY" ] }, { @@ -888,6 +885,48 @@ [ [ "duct_tape", 150 ] ] ] }, + { + "result": "hood_wsurvivor_nofur", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "category": "CC_ARMOR", + "subcategory": "CSC_ARMOR_HEAD", + "skill_used": "tailor", + "difficulty": 7, + "skills_required": [ "fabrication", 5 ], + "time": "6 h", + "autolearn": true, + "using": [ [ "sewing_standard", 40 ] ], + "tools": [ [ [ "welder", 21 ], [ "welder_crude", 32 ], [ "soldering_iron", 32 ], [ "toolset", 32 ] ] ], + "components": [ + [ [ "faux_fur", 8 ] ], + [ [ "rag", 2 ] ], + [ [ "sheet_kevlar_layered", 4 ] ], + [ [ "hood_rain", 1 ], [ "bag_plastic", 2 ], [ "hood_gut", 1 ] ], + [ [ "duct_tape", 100 ] ] + ], + "proficiencies": [ + { "proficiency": "prof_millinery" }, + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_plasticworking" }, + { "proficiency": "prof_polymerworking" } + ] + }, + { + "result": "xl_hood_wsurvivor_nofur", + "type": "recipe", + "copy-from": "hood_wsurvivor_nofur", + "time": "8 h", + "using": [ [ "sewing_standard", 60 ] ], + "tools": [ [ [ "welder", 28 ], [ "welder_crude", 43 ], [ "soldering_iron", 43 ], [ "toolset", 43 ] ] ], + "components": [ + [ [ "faux_fur", 10 ] ], + [ [ "rag", 4 ] ], + [ [ "sheet_kevlar_layered", 6 ] ], + [ [ "hood_rain", 1 ], [ "bag_plastic", 2 ], [ "hood_gut", 1 ] ], + [ [ "duct_tape", 150 ] ] + ] + }, { "result": "hood_rain", "type": "recipe", diff --git a/data/json/recipes/armor/storage.json b/data/json/recipes/armor/storage.json index a75bf1456c9f0..b217d6e462f6e 100644 --- a/data/json/recipes/armor/storage.json +++ b/data/json/recipes/armor/storage.json @@ -380,6 +380,20 @@ "proficiencies": [ { "proficiency": "prof_closures" }, { "proficiency": "prof_leatherworking_basic" } ], "components": [ [ [ "2x4", 2 ] ], [ [ "nail", 10 ] ] ] }, + { + "result": "armrig", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "category": "CC_ARMOR", + "subcategory": "CSC_ARMOR_STORAGE", + "skill_used": "tailor", + "difficulty": 3, + "time": "1 h", + "autolearn": true, + "reversible": true, + "using": [ [ "tailoring_cotton", 2 ], [ "fastener_small", 1 ] ], + "proficiencies": [ { "proficiency": "prof_closures" } ] + }, { "result": "chestpouch", "type": "recipe", @@ -1372,5 +1386,18 @@ { "proficiency": "prof_leatherworking" }, { "proficiency": "prof_leatherworking_basic" } ] + }, + { + "result": "bookstrap", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "category": "CC_ARMOR", + "subcategory": "CSC_ARMOR_STORAGE", + "skill_used": "tailor", + "time": "1 h 30 m", + "autolearn": true, + "reversible": true, + "using": [ [ "tailoring_leather_small", 4 ], [ "clasps", 2 ] ], + "proficiencies": [ { "proficiency": "prof_closures" }, { "proficiency": "prof_leatherworking_basic" } ] } ] diff --git a/data/json/recipes/armor/suit.json b/data/json/recipes/armor/suit.json index 3f5a9d01da004..9f13daebc1c39 100644 --- a/data/json/recipes/armor/suit.json +++ b/data/json/recipes/armor/suit.json @@ -785,6 +785,127 @@ [ [ "kevlar", 2 ], [ "ballistic_vest_empty", 2 ], [ "swat_armor", 2 ], [ "sheet_kevlar_layered", 48 ] ] ] }, + { + "result": "wsurvivor_suit_nofur", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "category": "CC_ARMOR", + "subcategory": "CSC_ARMOR_SUIT", + "skill_used": "tailor", + "difficulty": 6, + "skills_required": [ "fabrication", 6 ], + "time": "12 h", + "autolearn": true, + "using": [ [ "sewing_standard", 200 ] ], + "qualities": [ { "id": "LEATHER_AWL", "level": 1 } ], + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_closures" }, + { "proficiency": "prof_closures_waterproofing" }, + { "proficiency": "prof_polymerworking" } + ], + "tools": [ [ [ "welder", 28 ], [ "welder_crude", 42 ], [ "soldering_iron", 42 ], [ "toolset", 42 ] ] ], + "components": [ + [ [ "rag", 10 ] ], + [ [ "leather", 20 ], [ "tanned_hide", 4 ] ], + [ [ "faux_fur", 20 ] ], + [ [ "coat_rain", 1 ], [ "jacket_windbreaker", 1 ], [ "jacket_evac", 1 ], [ "coat_gut", 1 ] ], + [ + [ "tacvest", 1 ], + [ "legrig", 1 ], + [ "vest", 1 ], + [ "tool_belt", 1 ], + [ "ragpouch", 6 ], + [ "leather_pouch", 4 ], + [ "dump_pouch", 1 ], + [ "purse", 2 ], + [ "fanny", 2 ] + ], + [ [ "duct_tape", 300 ] ], + [ [ "kevlar", 1 ], [ "ballistic_vest_empty", 1 ], [ "swat_armor", 1 ], [ "sheet_kevlar_layered", 24 ] ] + ] + }, + { + "result": "xl_wsurvivor_suit_nofur", + "copy-from": "wsurvivor_suit_nofur", + "time": "18 h", + "type": "recipe", + "using": [ [ "sewing_standard", 300 ] ], + "qualities": [ { "id": "LEATHER_AWL", "level": 1 } ], + "tools": [ [ [ "welder", 56 ], [ "welder_crude", 104 ], [ "soldering_iron", 104 ], [ "toolset", 104 ] ] ], + "components": [ + [ [ "rag", 20 ] ], + [ [ "leather", 30 ], [ "tanned_hide", 6 ] ], + [ [ "faux_fur", 30 ] ], + [ [ "coat_rain", 2 ], [ "jacket_windbreaker", 2 ], [ "jacket_evac", 2 ], [ "coat_gut", 2 ] ], + [ + [ "tacvest", 1 ], + [ "legrig", 1 ], + [ "vest", 1 ], + [ "tool_belt", 1 ], + [ "ragpouch", 6 ], + [ "leather_pouch", 4 ], + [ "dump_pouch", 1 ], + [ "purse", 2 ], + [ "fanny", 2 ] + ], + [ [ "duct_tape", 400 ] ], + [ [ "kevlar", 2 ], [ "ballistic_vest_empty", 2 ], [ "swat_armor", 2 ], [ "sheet_kevlar_layered", 48 ] ] + ] + }, + { + "result": "mask_wsurvivor_nofur", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "category": "CC_ARMOR", + "subcategory": "CSC_ARMOR_HEAD", + "skill_used": "fabrication", + "difficulty": 7, + "skills_required": [ "tailor", 7 ], + "time": "4 h", + "autolearn": true, + "using": [ [ "adhesive", 1 ], [ "sewing_standard", 10 ] ], + "tools": [ [ [ "welder", 56 ], [ "welder_crude", 84 ], [ "soldering_iron", 84 ], [ "toolset", 84 ] ] ], + "components": [ + [ [ "mask_filter", 2 ], [ "mask_gas", 1 ], [ "mask_bunker", 1 ] ], + [ [ "glasses_safety", 2 ], [ "glasses_bal", 1 ] ], + [ [ "mask_bal", 1 ], [ "sheet_kevlar_layered", 4 ] ], + [ [ "faux_fur", 6 ], [ "tanned_pelt", 1 ] ] + ], + "proficiencies": [ + { "proficiency": "prof_plasticworking" }, + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_closures_waterproofing" }, + { "proficiency": "prof_polymerworking" } + ] + }, + { + "result": "mask_wsurvivorxl_nofur", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "category": "CC_ARMOR", + "subcategory": "CSC_ARMOR_HEAD", + "skill_used": "fabrication", + "difficulty": 7, + "skills_required": [ "tailor", 7 ], + "time": "4 h", + "autolearn": true, + "using": [ [ "adhesive", 1 ], [ "sewing_standard", 20 ] ], + "tools": [ [ [ "welder", 56 ], [ "welder_crude", 84 ], [ "soldering_iron", 84 ], [ "toolset", 84 ] ] ], + "components": [ + [ [ "mask_filter", 2 ], [ "mask_gas", 1 ], [ "mask_bunker", 1 ] ], + [ [ "glasses_safety", 2 ], [ "glasses_bal", 1 ] ], + [ [ "mask_bal", 1 ], [ "sheet_kevlar_layered", 4 ] ], + [ [ "faux_fur", 12 ], [ "tanned_pelt", 2 ] ] + ], + "proficiencies": [ + { "proficiency": "prof_plasticworking" }, + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_closures_waterproofing" }, + { "proficiency": "prof_polymerworking" } + ] + }, { "result": "gambeson", "type": "recipe", diff --git a/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_beds.json b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_beds.json new file mode 100644 index 0000000000000..2d67fb095a42d --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_beds.json @@ -0,0 +1,226 @@ +[ + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_mattress_beds_0", + "description": "We should build a pair of mattress beds in the southern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_mattress_beds_0", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_0" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_0" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_name": "pair of mattress beds in S shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_mattress_beds_1", + "description": "We should build a pair of mattress beds in the southeastern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_mattress_beds_1", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_1" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_1" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_name": "pair of mattress beds in SE shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_mattress_beds_2", + "description": "We should build a pair of mattress beds in the eastern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_mattress_beds_2", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_2" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_2" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_name": "pair of mattress beds in E shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_mattress_beds_3", + "description": "We should build a pair of mattress beds in the northeastern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_mattress_beds_3", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_3" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_3" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_name": "pair of mattress beds in NE shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_mattress_beds_4", + "description": "We should build a pair of mattress beds in the northern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_mattress_beds_4", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_4" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_4" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_name": "pair of mattress beds in N shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_mattress_beds_5", + "description": "We should build a pair of mattress beds in the northwestern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_mattress_beds_5", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_5" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_5" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_name": "pair of mattress beds in NW shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_mattress_beds_6", + "description": "We should build a pair of mattress beds in the western shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_mattress_beds_6", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_6" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_6" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_name": "pair of mattress beds in W shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_mattress_beds_7", + "description": "We should build a pair of mattress beds in the southwestern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_mattress_beds_7", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_7" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_7" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_name": "pair of mattress beds in SW shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_straw_beds_0", + "description": "We should build a pair of straw beds in the southern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_straw_beds_0", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_0" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_0" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_name": "pair of straw beds in S shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_straw_beds_1", + "description": "We should build a pair of straw beds in the southeastern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_straw_beds_1", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_1" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_1" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_name": "pair of straw beds in SE shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_straw_beds_2", + "description": "We should build a pair of straw beds in the eastern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_straw_beds_2", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_2" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_2" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_name": "pair of straw beds in E shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_straw_beds_3", + "description": "We should build a pair of straw beds in the northeastern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_straw_beds_3", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_3" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_3" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_name": "pair of straw beds in NE shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_straw_beds_4", + "description": "We should build a pair of straw beds in the northern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_straw_beds_4", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_4" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_4" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_name": "pair of straw beds in N shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_straw_beds_5", + "description": "We should build a pair of straw beds in the northwestern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_straw_beds_5", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_5" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_5" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_name": "pair of straw beds in NW shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_straw_beds_6", + "description": "We should build a pair of straw beds in the western shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_straw_beds_6", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_6" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_6" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_name": "pair of straw beds in W shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_straw_beds_7", + "description": "We should build a pair of straw beds in the southwestern shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_straw_beds_7", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_beds_7" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_beds_7" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_name": "pair of straw beds in SW shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_common.json b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_common.json new file mode 100644 index 0000000000000..fc1574e2c27a9 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_common.json @@ -0,0 +1,161 @@ +[ + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_0", + "description": "We should survey the base site and set up a bulletin board.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "time": "1 h", + "construction_blueprint": "fbmc_fire_lookout_tower_0", + "blueprint_provides": [ + { "id": "fbmc_fire_lookout_tower_0" }, + { "id": "primitive_camp_recipes_1" }, + { "id": "kitchen_recipes_1" }, + { "id": "kitchen_recipes_2" }, + { "id": "kitchen" }, + { "id": "tool_storage" }, + { "id": "gathering" }, + { "id": "firewood" }, + { "id": "sorting" }, + { "id": "logging" }, + { "id": "relaying" }, + { "id": "foraging" } + ], + "blueprint_resources": [ "fake_stove" ], + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_name": "basic survey", + "check_blueprint_needs": false + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_root_cellar", + "description": "Digging a root cellar will allow us trapping small game and preserving it.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_root_cellar", + "blueprint_name": "root cellar", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_root_cellar" }, { "id": "pantry" }, { "id": "trapping" } ], + "blueprint_requires": [ { "id": "bed", "amount": 4 }, { "id": "fbmc_fire_lookout_tower_0" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_root_cellar" } ] + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_water_well", + "description": "Digging a well will give us easy access to water and allow us to send out combat patrols or scouts.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_water_well", + "blueprint_name": "water well", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_water_well" }, { "id": "scouting" }, { "id": "patrolling" } ], + "blueprint_requires": [ { "id": "bed", "amount": 4 }, { "id": "fbmc_fire_lookout_tower_0" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_water_well" } ] + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_radio", + "description": "Let's set up a radio tower to improve our recruitment efforts.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_radio", + "blueprint_name": "radio tower", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_radio" }, { "id": "radio" }, { "id": "recruiting" } ], + "blueprint_requires": [ { "id": "bed", "amount": 8 }, { "id": "fbmc_fire_lookout_tower_0" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_radio" } ], + "blueprint_needs": { + "time": "2 d", + "skills": [ [ "fabrication", 3 ] ], + "inline": { + "tools": [ ], + "qualities": [ [ { "id": "HAMMER", "level": 2 } ], [ { "id": "SAW_M" } ], [ { "id": "SCREW" } ], [ { "id": "WRENCH" } ] ], + "components": [ + [ + [ "wind_turbine", 4 ], + [ "xl_wind_turbine", 1 ], + [ "solar_panel", 4 ], + [ "reinforced_solar_panel", 4 ], + [ "solar_panel_v2", 2 ], + [ "reinforced_solar_panel_v2", 2 ] + ], + [ [ "storage_battery", 1 ], [ "medium_storage_battery", 4 ], [ "small_storage_battery", 32 ] ], + [ [ "sheet_metal", 2 ], [ "wire", 8 ] ], + [ [ "pipe", 24 ] ], + [ [ "processor", 2 ] ], + [ [ "RAM", 2 ] ], + [ [ "large_lcd_screen", 1 ] ], + [ [ "e_scrap", 8 ] ], + [ [ "frame", 1 ] ], + [ [ "circuit", 4 ] ], + [ [ "power_supply", 2 ] ], + [ [ "amplifier", 2 ] ], + [ [ "cable", 80 ] ], + [ [ "motor_small", 1 ], [ "motor_tiny", 2 ] ] + ] + } + } + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_prepalisade", + "description": "We should dig some pits for palisade segments to block entrances to camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_prepalisade", + "blueprint_name": "dig pits", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_prepalisade" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_prepalisade" } ] + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_palisade", + "description": "We should build palisade segments to block entrances to camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_palisade", + "blueprint_name": "build palisade segments", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_palisade" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_prepalisade" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_palisade" } ] + }, + { + "type": "recipe", + "result": "fbmc_fire_lookout_tower_reinforced_doors", + "description": "We should build a pair of reinforced doors to secure camp's entrance.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_reinforced_doors", + "blueprint_name": "build reinforced doors", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_doors" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_palisade" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_doors" } ] + }, + { + "type": "recipe", + "result": "fbmc_fire_lookout_tower_metal_doors", + "description": "We should build a pair of metal doors to secure camp's entrance.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_metal_doors", + "blueprint_name": "build metal doors", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_doors" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_palisade" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_doors" } ] + } +] diff --git a/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_log.json b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_log.json new file mode 100644 index 0000000000000..ac65e0b34ed98 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_log.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_log_shack_0", + "description": "We need some shelter, so build a log shack with a wooden roof in the southern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_log_shack_0", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_0" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_log_shack_1", + "description": "We need some shelter, so build a log shack with a wooden roof in the southeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_log_shack_1", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_log_shack_2", + "description": "We need some shelter, so build a log shack with a wooden roof in the eastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_log_shack_2", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_log_shack_3", + "description": "We need some shelter, so build a log shack with a wooden roof in the northeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_log_shack_3", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_log_shack_4", + "description": "We need some shelter, so build a log shack with a wooden roof in the northern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_log_shack_4", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_log_shack_5", + "description": "We need some shelter, so build a log shack with a wooden roof in the northwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_log_shack_5", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_log_shack_6", + "description": "We need some shelter, so build a log shack with a wooden roof in the western part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_log_shack_6", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_log_shack_7", + "description": "We need some shelter, so build a log shack with a wooden roof in the southwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_log_shack_7", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_name": "log shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_metal.json b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_metal.json new file mode 100644 index 0000000000000..9c958c8a2b629 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_metal.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_metal_shack_0", + "description": "We need some shelter, so build a metal shack with a metal roof in the southern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_metal_shack_0", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_0" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_metal_shack_1", + "description": "We need some shelter, so build a metal shack with a metal roof in the southeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_metal_shack_1", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_metal_shack_2", + "description": "We need some shelter, so build a metal shack with a metal roof in the eastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_metal_shack_2", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_metal_shack_3", + "description": "We need some shelter, so build a metal shack with a metal roof in the northeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_metal_shack_3", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_metal_shack_4", + "description": "We need some shelter, so build a metal shack with a metal roof in the northern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_metal_shack_4", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_metal_shack_5", + "description": "We need some shelter, so build a metal shack with a metal roof in the northwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_metal_shack_5", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_metal_shack_6", + "description": "We need some shelter, so build a metal shack with a metal roof in the western part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_metal_shack_6", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_metal_shack_7", + "description": "We need some shelter, so build a metal shack with a metal roof in the southwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_metal_shack_7", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_name": "metal shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_migo_resin.json b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_migo_resin.json new file mode 100644 index 0000000000000..fd501ac7dfd97 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_migo_resin.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_migo_resin_shack_0", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof in the southern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_migo_resin_shack_0", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_0" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_migo_resin_shack_1", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof in the southeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_migo_resin_shack_1", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_migo_resin_shack_2", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof in the eastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_migo_resin_shack_2", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_migo_resin_shack_3", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof in the northeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_migo_resin_shack_3", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_migo_resin_shack_4", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof in the northern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_migo_resin_shack_4", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_migo_resin_shack_5", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof in the northwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_migo_resin_shack_5", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_migo_resin_shack_6", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof in the western part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_migo_resin_shack_6", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_migo_resin_shack_7", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof in the southwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_migo_resin_shack_7", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_name": "mi-go resin shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_rammed_earth.json b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_rammed_earth.json new file mode 100644 index 0000000000000..0f111e4215fe2 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_rammed_earth.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rammed_earth_shack_0", + "description": "We need some shelter, so build a rammed earth shack with a sod roof in the southern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rammed_earth_shack_0", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_0" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rammed_earth_shack_1", + "description": "We need some shelter, so build a rammed earth shack with a sod roof in the southeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rammed_earth_shack_1", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rammed_earth_shack_2", + "description": "We need some shelter, so build a rammed earth shack with a sod roof in the eastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rammed_earth_shack_2", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rammed_earth_shack_3", + "description": "We need some shelter, so build a rammed earth shack with a sod roof in the northeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rammed_earth_shack_3", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rammed_earth_shack_4", + "description": "We need some shelter, so build a rammed earth shack with a sod roof in the northern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rammed_earth_shack_4", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rammed_earth_shack_5", + "description": "We need some shelter, so build a rammed earth shack with a sod roof in the northwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rammed_earth_shack_5", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rammed_earth_shack_6", + "description": "We need some shelter, so build a rammed earth shack with a sod roof in the western part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rammed_earth_shack_6", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rammed_earth_shack_7", + "description": "We need some shelter, so build a rammed earth shack with a sod roof in the southwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rammed_earth_shack_7", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_name": "rammed earth shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_rock.json b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_rock.json new file mode 100644 index 0000000000000..215b6219d1584 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_rock.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rock_shack_0", + "description": "We need some shelter, so build a stone shack with a wooden roof in the southern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rock_shack_0", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_0" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rock_shack_1", + "description": "We need some shelter, so build a stone shack with a wooden roof in the southeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rock_shack_1", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rock_shack_2", + "description": "We need some shelter, so build a stone shack with a wooden roof in the eastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rock_shack_2", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rock_shack_3", + "description": "We need some shelter, so build a stone shack with a wooden roof in the northeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rock_shack_3", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rock_shack_4", + "description": "We need some shelter, so build a stone shack with a wooden roof in the northern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rock_shack_4", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rock_shack_5", + "description": "We need some shelter, so build a stone shack with a wooden roof in the northwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rock_shack_5", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rock_shack_6", + "description": "We need some shelter, so build a stone shack with a wooden roof in the western part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rock_shack_6", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_rock_shack_7", + "description": "We need some shelter, so build a stone shack with a wooden roof in the southwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_rock_shack_7", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_name": "stone shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_wad.json b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_wad.json new file mode 100644 index 0000000000000..75ab05f40ab4a --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_wad.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wad_shack_0", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof in the southern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wad_shack_0", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_0" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wad_shack_1", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof in the southeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wad_shack_1", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wad_shack_2", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof in the eastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wad_shack_2", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wad_shack_3", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof in the northeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wad_shack_3", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wad_shack_4", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof in the northern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wad_shack_4", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wad_shack_5", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof in the northwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wad_shack_5", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wad_shack_6", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof in the western part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wad_shack_6", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wad_shack_7", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof in the southwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wad_shack_7", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_name": "wattle and daub shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_wood.json b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_wood.json new file mode 100644 index 0000000000000..3d17ac1ddd53a --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_fire_lookout_tower/recipe_modular_fire_lookout_tower_wood.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wood_shack_0", + "description": "We need some shelter, so build a wooden shack with a wooden roof in the southern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wood_shack_0", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_0" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wood_shack_1", + "description": "We need some shelter, so build a wooden shack with a wooden roof in the southeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wood_shack_1", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_0" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wood_shack_2", + "description": "We need some shelter, so build a wooden shack with a wooden roof in the eastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wood_shack_2", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_1" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wood_shack_3", + "description": "We need some shelter, so build a wooden shack with a wooden roof in the northeastern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wood_shack_3", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_2" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wood_shack_4", + "description": "We need some shelter, so build a wooden shack with a wooden roof in the northern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wood_shack_4", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_3" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wood_shack_5", + "description": "We need some shelter, so build a wooden shack with a wooden roof in the northwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wood_shack_5", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_4" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wood_shack_6", + "description": "We need some shelter, so build a wooden shack with a wooden roof in the western part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wood_shack_6", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_5" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_fire_lookout_tower_wood_shack_7", + "description": "We need some shelter, so build a wooden shack with a wooden roof in the southwestern part of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_fire_lookout_tower_wood_shack_7", + "blueprint_provides": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_fire_lookout_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_fire_lookout_tower_shack_6" } ], + "blueprint_name": "wooden shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+1.json b/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+1.json new file mode 100644 index 0000000000000..11e044bd65e94 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+1.json @@ -0,0 +1,92 @@ +[ + { + "type": "recipe", + "result": "faction_base_mansion_+1_0", + "description": "We should survey the base site and set up a bulletin board.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "time": "1 h", + "construction_blueprint": "fbmc_mansion_+1", + "blueprint_provides": [ + { "id": "fbmc_mansion_+1" }, + { "id": "primitive_camp_recipes_1" }, + { "id": "gathering" }, + { "id": "firewood" }, + { "id": "foraging" }, + { "id": "sorting" }, + { "id": "logging" }, + { "id": "tool_storage" }, + { "id": "bed", "amount": 8 } + ], + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_name": "basic survey", + "check_blueprint_needs": false + }, + { + "type": "recipe", + "result": "faction_base_mansion_+1_garden", + "description": "Let's remove plants from garden and plow a few plots.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_mansion_+1_farm", + "blueprint_name": "prepare garden", + "blueprint_requires": [ { "id": "fbmc_mansion_+1" } ], + "blueprint_provides": [ { "id": "fbmc_mansion_+1_farm" }, { "id": "farming" }, { "id": "farm_recipes_1" } ], + "blueprint_excludes": [ { "id": "fbmc_mansion_+1_farm" } ], + "blueprint_needs": { "time": "7 h", "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 1 } ] ] } }, + "check_blueprint_needs": false + }, + { + "type": "recipe", + "result": "faction_base_mansion_+1_radio", + "description": "Let's set up a radio tower to improve our recruitment efforts.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_mansion_+1_radio", + "blueprint_name": "build a radio tower and console", + "blueprint_requires": [ { "id": "fbmc_mansion_+1_farm" } ], + "blueprint_provides": [ { "id": "fbmc_mansion_+1_radio" }, { "id": "recruiting" }, { "id": "radio" } ], + "blueprint_excludes": [ { "id": "fbmc_mansion_+1_radio" } ], + "blueprint_needs": { + "time": "2 d", + "skills": [ [ "fabrication", 3 ] ], + "inline": { + "tools": [ ], + "qualities": [ [ { "id": "HAMMER", "level": 2 } ], [ { "id": "SAW_M" } ], [ { "id": "SCREW" } ], [ { "id": "WRENCH" } ] ], + "components": [ + [ + [ "wind_turbine", 4 ], + [ "xl_wind_turbine", 1 ], + [ "solar_panel", 4 ], + [ "reinforced_solar_panel", 4 ], + [ "solar_panel_v2", 2 ], + [ "reinforced_solar_panel_v2", 2 ], + [ "solar_panel_v3", 1 ] + ], + [ [ "storage_battery", 1 ], [ "medium_storage_battery", 4 ], [ "small_storage_battery", 32 ] ], + [ [ "sheet_metal", 2 ], [ "wire", 8 ] ], + [ [ "pipe", 24 ] ], + [ [ "processor", 2 ] ], + [ [ "RAM", 2 ] ], + [ [ "large_lcd_screen", 1 ] ], + [ [ "e_scrap", 8 ] ], + [ [ "frame", 1 ] ], + [ [ "circuit", 4 ] ], + [ [ "power_supply", 2 ] ], + [ [ "amplifier", 2 ] ], + [ [ "cable", 80 ] ], + [ [ "motor_small", 1 ], [ "motor_tiny", 2 ] ] + ] + } + }, + "check_blueprint_needs": false + } +] diff --git a/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+2.json b/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+2.json new file mode 100644 index 0000000000000..7251239ab70c4 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+2.json @@ -0,0 +1,75 @@ +[ + { + "type": "recipe", + "result": "faction_base_mansion_+2_0", + "description": "We should survey the base site and set up a bulletin board.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "time": "1 h", + "construction_blueprint": "fbmc_mansion_+2", + "blueprint_provides": [ + { "id": "fbmc_mansion_+2" }, + { "id": "primitive_camp_recipes_1" }, + { "id": "gathering" }, + { "id": "firewood" }, + { "id": "foraging" }, + { "id": "sorting" }, + { "id": "logging" }, + { "id": "tool_storage" }, + { "id": "bed", "amount": 8 } + ], + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_name": "basic survey", + "check_blueprint_needs": false + }, + { + "type": "recipe", + "result": "faction_base_mansion_+2_radio", + "description": "Let's set up a radio tower to improve our recruitment efforts.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_mansion_+2_radio", + "blueprint_name": "build a radio tower and console", + "blueprint_requires": [ { "id": "fbmc_mansion_+2" } ], + "blueprint_provides": [ { "id": "fbmc_mansion_+2_radio" }, { "id": "recruiting" }, { "id": "radio" } ], + "blueprint_excludes": [ { "id": "fbmc_mansion_+2_radio" } ], + "blueprint_needs": { + "time": "2 d", + "skills": [ [ "fabrication", 3 ] ], + "inline": { + "tools": [ ], + "qualities": [ [ { "id": "HAMMER", "level": 2 } ], [ { "id": "SAW_M" } ], [ { "id": "SCREW" } ], [ { "id": "WRENCH" } ] ], + "components": [ + [ + [ "wind_turbine", 4 ], + [ "xl_wind_turbine", 1 ], + [ "solar_panel", 4 ], + [ "reinforced_solar_panel", 4 ], + [ "solar_panel_v2", 2 ], + [ "reinforced_solar_panel_v2", 2 ], + [ "solar_panel_v3", 1 ] + ], + [ [ "storage_battery", 1 ], [ "medium_storage_battery", 4 ], [ "small_storage_battery", 32 ] ], + [ [ "sheet_metal", 2 ], [ "wire", 8 ] ], + [ [ "pipe", 24 ] ], + [ [ "processor", 2 ] ], + [ [ "RAM", 2 ] ], + [ [ "large_lcd_screen", 1 ] ], + [ [ "e_scrap", 8 ] ], + [ [ "frame", 1 ] ], + [ [ "circuit", 4 ] ], + [ [ "power_supply", 2 ] ], + [ [ "amplifier", 2 ] ], + [ [ "cable", 80 ] ], + [ [ "motor_small", 1 ], [ "motor_tiny", 2 ] ] + ] + } + }, + "check_blueprint_needs": false + } +] diff --git a/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+3.json b/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+3.json new file mode 100644 index 0000000000000..5d0d7bbd9486d --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+3.json @@ -0,0 +1,75 @@ +[ + { + "type": "recipe", + "result": "faction_base_mansion_+3_0", + "description": "We should survey the base site and set up a bulletin board.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "time": "1 h", + "construction_blueprint": "fbmc_mansion_+3", + "blueprint_provides": [ + { "id": "fbmc_mansion_+3" }, + { "id": "primitive_camp_recipes_1" }, + { "id": "gathering" }, + { "id": "firewood" }, + { "id": "foraging" }, + { "id": "sorting" }, + { "id": "logging" }, + { "id": "tool_storage" }, + { "id": "bed", "amount": 8 } + ], + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_name": "basic survey", + "check_blueprint_needs": false + }, + { + "type": "recipe", + "result": "faction_base_mansion_+3_radio", + "description": "Let's set up a radio tower to improve our recruitment efforts.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_mansion_+3_radio", + "blueprint_name": "build a radio tower and console", + "blueprint_requires": [ { "id": "fbmc_mansion_+3" } ], + "blueprint_provides": [ { "id": "fbmc_mansion_+3_radio" }, { "id": "recruiting" }, { "id": "radio" } ], + "blueprint_excludes": [ { "id": "fbmc_mansion_+3_radio" } ], + "blueprint_needs": { + "time": "2 d", + "skills": [ [ "fabrication", 3 ] ], + "inline": { + "tools": [ ], + "qualities": [ [ { "id": "HAMMER", "level": 2 } ], [ { "id": "SAW_M" } ], [ { "id": "SCREW" } ], [ { "id": "WRENCH" } ] ], + "components": [ + [ + [ "wind_turbine", 4 ], + [ "xl_wind_turbine", 1 ], + [ "solar_panel", 4 ], + [ "reinforced_solar_panel", 4 ], + [ "solar_panel_v2", 2 ], + [ "reinforced_solar_panel_v2", 2 ], + [ "solar_panel_v3", 1 ] + ], + [ [ "storage_battery", 1 ], [ "medium_storage_battery", 4 ], [ "small_storage_battery", 32 ] ], + [ [ "sheet_metal", 2 ], [ "wire", 8 ] ], + [ [ "pipe", 24 ] ], + [ [ "processor", 2 ] ], + [ [ "RAM", 2 ] ], + [ [ "large_lcd_screen", 1 ] ], + [ [ "e_scrap", 8 ] ], + [ [ "frame", 1 ] ], + [ [ "circuit", 4 ] ], + [ [ "power_supply", 2 ] ], + [ [ "amplifier", 2 ] ], + [ [ "cable", 80 ] ], + [ [ "motor_small", 1 ], [ "motor_tiny", 2 ] ] + ] + } + }, + "check_blueprint_needs": false + } +] diff --git a/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+4.json b/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+4.json new file mode 100644 index 0000000000000..815800604884f --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_+4.json @@ -0,0 +1,92 @@ +[ + { + "type": "recipe", + "result": "faction_base_mansion_+4_0", + "description": "We should survey the base site and set up a bulletin board.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "time": "1 h", + "construction_blueprint": "fbmc_mansion_+4", + "blueprint_provides": [ + { "id": "fbmc_mansion_+4" }, + { "id": "primitive_camp_recipes_1" }, + { "id": "gathering" }, + { "id": "firewood" }, + { "id": "foraging" }, + { "id": "sorting" }, + { "id": "logging" }, + { "id": "tool_storage" }, + { "id": "bed", "amount": 8 } + ], + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_name": "basic survey", + "check_blueprint_needs": false + }, + { + "type": "recipe", + "result": "faction_base_mansion_+4_garden", + "description": "Let's remove plants from garden and plow a few plots.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_mansion_+4_farm", + "blueprint_name": "prepare garden", + "blueprint_requires": [ { "id": "fbmc_mansion_+4" } ], + "blueprint_provides": [ { "id": "fbmc_mansion_+4_farm" }, { "id": "farming" }, { "id": "farm_recipes_1" } ], + "blueprint_excludes": [ { "id": "fbmc_mansion_+1_farm" } ], + "blueprint_needs": { "time": "2 h", "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 1 } ] ] } }, + "check_blueprint_needs": false + }, + { + "type": "recipe", + "result": "faction_base_mansion_+4_radio", + "description": "Let's set up a radio tower to improve our recruitment efforts.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_mansion_+4_radio", + "blueprint_name": "build a radio tower and console", + "blueprint_requires": [ { "id": "fbmc_mansion_+4_farm" } ], + "blueprint_provides": [ { "id": "fbmc_mansion_+4_radio" }, { "id": "recruiting" }, { "id": "radio" } ], + "blueprint_excludes": [ { "id": "fbmc_mansion_+4_radio" } ], + "blueprint_needs": { + "time": "2 d", + "skills": [ [ "fabrication", 3 ] ], + "inline": { + "tools": [ ], + "qualities": [ [ { "id": "HAMMER", "level": 2 } ], [ { "id": "SAW_M" } ], [ { "id": "SCREW" } ], [ { "id": "WRENCH" } ] ], + "components": [ + [ + [ "wind_turbine", 4 ], + [ "xl_wind_turbine", 1 ], + [ "solar_panel", 4 ], + [ "reinforced_solar_panel", 4 ], + [ "solar_panel_v2", 2 ], + [ "reinforced_solar_panel_v2", 2 ], + [ "solar_panel_v3", 1 ] + ], + [ [ "storage_battery", 1 ], [ "medium_storage_battery", 4 ], [ "small_storage_battery", 32 ] ], + [ [ "sheet_metal", 2 ], [ "wire", 8 ] ], + [ [ "pipe", 24 ] ], + [ [ "processor", 2 ] ], + [ [ "RAM", 2 ] ], + [ [ "large_lcd_screen", 1 ] ], + [ [ "e_scrap", 8 ] ], + [ [ "frame", 1 ] ], + [ [ "circuit", 4 ] ], + [ [ "power_supply", 2 ] ], + [ [ "amplifier", 2 ] ], + [ [ "cable", 80 ] ], + [ [ "motor_small", 1 ], [ "motor_tiny", 2 ] ] + ] + } + }, + "check_blueprint_needs": false + } +] diff --git a/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_expansion_surveys.json b/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_expansion_surveys.json new file mode 100644 index 0000000000000..7da157e55d4f9 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_mansion/fbmc_mansion_expansion_surveys.json @@ -0,0 +1,238 @@ +[ + { + "type": "recipe", + "result": "faction_base_mansion_e1", + "description": "Survey mansion's entrance.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_e1", + "blueprint_name": "entrance survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ { "id": "faction_base_mansion_e1" }, { "id": "farming" }, { "id": "reseeding" }, { "id": "farm_recipes_1" } ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_e2", + "description": "Survey mansion's entrance.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_e2", + "blueprint_name": "entrance survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ { "id": "faction_base_mansion_e2" }, { "id": "farming" }, { "id": "reseeding" }, { "id": "farm_recipes_1" } ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_t1", + "description": "Survey mansion's swimming pool.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_t1", + "blueprint_name": "swimming pool survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ { "id": "faction_base_mansion_t1" }, { "id": "farming" }, { "id": "reseeding" }, { "id": "farm_recipes_1" } ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_t2", + "description": "Survey mansion's bedrooms.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_t2", + "blueprint_name": "bedrooms survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ { "id": "faction_base_mansion_t2" } ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_t3", + "description": "Survey mansion's???.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_t3", + "blueprint_name": "??? survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ { "id": "faction_base_mansion_t3" } ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_t4", + "description": "Survey mansion's kitchen.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_t4", + "blueprint_name": "kitchen survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ + { "id": "faction_base_mansion_t4" }, + { "id": "kitchen" }, + { "id": "kitchen_recipes_1" }, + { "id": "kitchen_recipes_2" }, + { "id": "kitchen_recipes_3" }, + { "id": "saltworks_recipes_1" }, + { "id": "saltworks_recipes_2" }, + { "id": "saltworks_recipes_3" } + ], + "blueprint_resources": [ "fake_oven" ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_t5", + "description": "Survey mansion's library.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_t5", + "blueprint_name": "library survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ { "id": "faction_base_mansion_t5" }, { "id": "kitchen" }, { "id": "library_recipes_1" } ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_t6", + "description": "Survey mansion's bedroom.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_t6", + "blueprint_name": "bedroom survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ { "id": "faction_base_mansion_t6" } ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_t7", + "description": "Survey mansion's living rooms.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_t7", + "blueprint_name": "living rooms survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ { "id": "faction_base_mansion_t7" } ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_c1", + "description": "Survey mansion's swimming pool.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_c1", + "blueprint_name": "swimming survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ { "id": "faction_base_mansion_c1" } ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_c2", + "description": "Survey mansion's bar.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_c2", + "blueprint_name": "bar survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ + { "id": "faction_base_mansion_c2" }, + { "id": "kitchen" }, + { "id": "kitchen_recipes_1" }, + { "id": "kitchen_recipes_2" }, + { "id": "kitchen_recipes_3" } + ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_c3", + "description": "Survey mansion's living rooms.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_c3", + "blueprint_name": "living rooms survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ { "id": "faction_base_mansion_c3" } ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_c4", + "description": "Survey mansion's bedroom.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_c4", + "blueprint_name": "bedroom survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ { "id": "faction_base_mansion_c4" } ] + }, + { + "type": "recipe", + "result": "faction_base_mansion_c5", + "description": "Survey mansion's kitchen.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_mansion_c5", + "blueprint_name": "kitchen survey", + "time": "180 m", + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_provides": [ + { "id": "faction_base_mansion_c5" }, + { "id": "kitchen" }, + { "id": "kitchen_recipes_1" }, + { "id": "kitchen_recipes_2" }, + { "id": "kitchen_recipes_3" }, + { "id": "saltworks_recipes_1" }, + { "id": "saltworks_recipes_2" }, + { "id": "saltworks_recipes_3" } + ], + "blueprint_resources": [ "fake_oven" ] + } +] diff --git a/data/json/recipes/basecamps/fbmc_pottery_cottage/recipe_pottery_cottage_blacksmith.json b/data/json/recipes/basecamps/fbmc_pottery_cottage/recipe_pottery_cottage_blacksmith.json new file mode 100644 index 0000000000000..75e11a85b2cfb --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_pottery_cottage/recipe_pottery_cottage_blacksmith.json @@ -0,0 +1,130 @@ +[ + { + "type": "recipe", + "result": "faction_base_pottery_cottage_blacksmith_log", + "description": "Let's build a log shack with a wooden roof for our blacksmith's workshop.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_pottery_cottage_blacksmith_log", + "blueprint_provides": [ { "id": "blacksmith" }, { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_requires": [ { "id": "fbmc_pottery_cottage_0" } ], + "blueprint_excludes": [ { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_name": "log blacksmith workshop" + }, + { + "type": "recipe", + "result": "faction_base_pottery_cottage_blacksmith_metal", + "description": "Let's build a metal shack with a metal roof for our blacksmith's workshop.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_pottery_cottage_blacksmith_metal", + "blueprint_provides": [ { "id": "blacksmith" }, { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_requires": [ { "id": "fbmc_pottery_cottage_0" } ], + "blueprint_excludes": [ { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_name": "metal blacksmith workshop" + }, + { + "type": "recipe", + "result": "faction_base_pottery_cottage_blacksmith_migo_resin", + "description": "Let's build mi-go shack with mi-go roof for our blacksmith workshop.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_pottery_cottage_blacksmith_migo_resin", + "blueprint_provides": [ { "id": "blacksmith" }, { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_requires": [ { "id": "fbmc_pottery_cottage_0" } ], + "blueprint_excludes": [ { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_name": "mi-go blacksmith workshop" + }, + { + "type": "recipe", + "result": "faction_base_pottery_cottage_blacksmith_rammed_earth", + "description": "Let's build a rammed earth shack with a sod roof for our blacksmith's workshop.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_pottery_cottage_blacksmith_rammed_earth", + "blueprint_provides": [ { "id": "blacksmith" }, { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_requires": [ { "id": "fbmc_pottery_cottage_0" } ], + "blueprint_excludes": [ { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_name": "rammed earth blacksmith workshop" + }, + { + "type": "recipe", + "result": "faction_base_pottery_cottage_blacksmith_rock", + "description": "Let's build a stone shack with a wooden roof for our blacksmith's workshop.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_pottery_cottage_blacksmith_rock", + "blueprint_provides": [ { "id": "blacksmith" }, { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_requires": [ { "id": "fbmc_pottery_cottage_0" } ], + "blueprint_excludes": [ { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_name": "stone blacksmith workshop" + }, + { + "type": "recipe", + "result": "faction_base_pottery_cottage_blacksmith_wad", + "description": "Let's build a wattle and daub shack with a sod roof for our blacksmith's workshop.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_pottery_cottage_blacksmith_wad", + "blueprint_provides": [ { "id": "blacksmith" }, { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_requires": [ { "id": "fbmc_pottery_cottage_0" } ], + "blueprint_excludes": [ { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_name": "wattle and daub blacksmith workshop" + }, + { + "type": "recipe", + "result": "faction_base_pottery_cottage_blacksmith_wood", + "description": "Let's build a wooden shack with a wooden roof for our blacksmith's workshop.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_pottery_cottage_blacksmith_wood", + "blueprint_provides": [ { "id": "blacksmith" }, { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_requires": [ { "id": "fbmc_pottery_cottage_0" } ], + "blueprint_excludes": [ { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_name": "wooden blacksmith workshop" + }, + { + "type": "recipe", + "result": "faction_base_pottery_cottage_blacksmith_1", + "description": "Let's build a rock forge and a workbench to start smithing.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_pottery_cottage_blacksmith_1", + "blueprint_provides": [ { "id": "blacksmith_recipes_1" }, { "id": "fbmc_pottery_cottage_blacksmith_1" } ], + "blueprint_resources": [ "fake_forge" ], + "blueprint_requires": [ { "id": "fbmc_pottery_cottage_blacksmith_0" } ], + "blueprint_excludes": [ { "id": "fbmc_pottery_cottage_blacksmith_1" } ], + "blueprint_name": "workbench and forge" + }, + { + "type": "recipe", + "result": "faction_base_pottery_cottage_blacksmith_2", + "description": "Let's build a second workbench and a drop hammer for more productive smithing.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_pottery_cottage_blacksmith_2", + "blueprint_provides": [ { "id": "blacksmith_recipes_7" }, { "id": "fbmc_pottery_cottage_blacksmith_2" } ], + "blueprint_resources": [ "fake_drop_hammer" ], + "blueprint_requires": [ { "id": "fbmc_pottery_cottage_blacksmith_1" } ], + "blueprint_excludes": [ { "id": "fbmc_pottery_cottage_blacksmith_2" } ], + "blueprint_name": "workbench and drop hammer" + } +] diff --git a/data/json/recipes/basecamps/fbmc_pottery_cottage/recipe_pottery_cottage_common.json b/data/json/recipes/basecamps/fbmc_pottery_cottage/recipe_pottery_cottage_common.json new file mode 100644 index 0000000000000..1979cdf2decef --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_pottery_cottage/recipe_pottery_cottage_common.json @@ -0,0 +1,53 @@ +[ + { + "type": "recipe", + "result": "faction_base_pottery_cottage_0", + "description": "We should survey the base site and set up a bulletin board.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "time": "1 h", + "construction_blueprint": "fbmc_pottery_cottage_0", + "blueprint_provides": [ + { "id": "tool_storage" }, + { "id": "pantry" }, + { "id": "gathering" }, + { "id": "firewood" }, + { "id": "sorting" }, + { "id": "logging" }, + { "id": "foraging" }, + { "id": "trapping" }, + { "id": "kitchen" }, + { "id": "kitchen_recipes_1" }, + { "id": "kitchen_recipes_2" }, + { "id": "kitchen_recipes_3" }, + { "id": "saltworks_recipes_1" }, + { "id": "saltworks_recipes_2" }, + { "id": "saltworks_recipes_3" }, + { "id": "primitive_camp_recipes_1" }, + { "id": "clay_recipes_1" }, + { "id": "fishing_recipes" }, + { "id": "fbmc_pottery_cottage_0" } + ], + "blueprint_resources": [ "fake_oven", "fake_stove", "fake_char_kiln", "kiln_pseudo" ], + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_name": "basic survey", + "check_blueprint_needs": false + }, + { + "type": "recipe", + "result": "faction_base_pottery_cottage_butchery_rack", + "description": "Let's build a butchery rack, so we can start hunting large animals.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_pottery_cottage_butchery_rack", + "blueprint_provides": [ { "id": "hunting" }, { "id": "fbmc_pottery_cottage_butchery_rack" } ], + "blueprint_requires": [ { "id": "fbmc_pottery_cottage_0" } ], + "blueprint_excludes": [ { "id": "fbmc_pottery_cottage_butchery_rack" } ], + "blueprint_name": "butchery rack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_beds.json b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_beds.json new file mode 100644 index 0000000000000..f30a240a0678f --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_beds.json @@ -0,0 +1,282 @@ +[ + { + "type": "recipe", + "result": "faction_base_radio_tower_mattress_beds_controls_room", + "description": "We should remove some of display racks and counters, then build a pair of mattress beds inside radio control's room for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_mattress_beds_controls_room", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_controls_room" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_controls_room" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_controls_room" } ], + "blueprint_name": "pair of mattress beds in radio control's room" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_mattress_beds_0", + "description": "We should build a pair of mattress beds inside radio tower shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_mattress_beds_0", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_0" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_0" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_name": "pair of mattress beds in radio tower" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_mattress_beds_1", + "description": "We should build a pair of mattress beds in the northeast side shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_mattress_beds_1", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_1" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_1" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_name": "pair of mattress beds in NE side shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_mattress_beds_2", + "description": "We should build a pair of mattress beds in the southwest corner shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_mattress_beds_2", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_2" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_2" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_name": "pair of mattress beds in SW corner shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_mattress_beds_3", + "description": "We should build a pair of mattress beds in the southwest side shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_mattress_beds_3", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_3" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_3" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_name": "pair of mattress beds in SW side shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_mattress_beds_4", + "description": "We should build a pair of mattress beds in the southeast side shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_mattress_beds_4", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_4" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_4" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_name": "pair of mattress beds in SE side shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_mattress_beds_5", + "description": "We should build a pair of mattress beds in the southeast corner shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_mattress_beds_5", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_5" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_5" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_name": "pair of mattress beds in SE corner shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_mattress_beds_6", + "description": "We should build a pair of mattress beds in the southeast corner shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_mattress_beds_6", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_6" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_6" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_name": "pair of mattress beds in SE corner shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_mattress_beds_7", + "description": "We should build a pair of mattress beds in the southwest corner shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_mattress_beds_7", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_7" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_7" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_name": "pair of mattress beds in SW corner shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_mattress_beds_8", + "description": "We should build a pair of mattress beds in the south side shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_mattress_beds_8", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_8" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_8" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_name": "pair of mattress beds in S side shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_straw_beds_controls_room", + "description": "We should remove some of display racks and counters, then build a pair of straw beds inside radio control's room for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_straw_beds_controls_room", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_controls_room" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_controls_room" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_controls_room" } ], + "blueprint_name": "pair of straw beds in radio control's room" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_straw_beds_0", + "description": "We should build a pair of straw beds inside radio tower shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_straw_beds_0", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_0" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_0" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_name": "pair of straw beds in radio tower" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_straw_beds_1", + "description": "We should build a pair of straw beds in the northeast side shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_straw_beds_1", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_1" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_1" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_name": "pair of straw beds in NE side shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_straw_beds_2", + "description": "We should build a pair of straw beds in the southwest corner shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_straw_beds_2", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_2" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_2" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_name": "pair of straw beds in SW corner shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_straw_beds_3", + "description": "We should build a pair of straw beds in the southwest side shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_straw_beds_3", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_3" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_3" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_name": "pair of straw beds in SW side shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_straw_beds_4", + "description": "We should build a pair of straw beds in the southeast side shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_straw_beds_4", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_4" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_4" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_name": "pair of straw beds in SE side shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_straw_beds_5", + "description": "We should build a pair of straw beds in the southeast corner shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_straw_beds_5", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_5" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_5" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_name": "pair of straw beds in SE corner shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_straw_beds_6", + "description": "We should build a pair of straw beds in the southeast corner shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_straw_beds_6", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_6" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_6" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_name": "pair of straw beds in SE corner shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_straw_beds_7", + "description": "We should build a pair of straw beds in the southwest corner shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_straw_beds_7", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_7" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_7" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_name": "pair of straw beds in SW corner shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_straw_beds_8", + "description": "We should build a pair of straw beds in the south side shack for our survivors.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_straw_beds_8", + "blueprint_provides": [ { "id": "fbmc_radio_tower_beds_8" }, { "id": "bed", "amount": 2 } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_beds_8" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_name": "pair of straw beds in S side shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_common.json b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_common.json new file mode 100644 index 0000000000000..18c774a6a1c97 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_common.json @@ -0,0 +1,237 @@ +[ + { + "type": "recipe", + "result": "faction_base_radio_tower_0", + "description": "We should survey the base site and set up a bulletin board.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "time": "1 h", + "construction_blueprint": "fbmc_radio_tower_0", + "blueprint_provides": [ + { "id": "fbmc_radio_tower_0" }, + { "id": "fbmc_radio_tower" }, + { "id": "primitive_camp_recipes_1" }, + { "id": "gathering" }, + { "id": "firewood" }, + { "id": "foraging" }, + { "id": "sorting" }, + { "id": "logging" } + ], + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_name": "basic survey", + "check_blueprint_needs": false + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_1_0", + "description": "We should survey the base site and set up a bulletin board.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "skill_used": "fabrication", + "autolearn": false, + "never_learn": true, + "time": "1 h", + "construction_blueprint": "fbmc_radio_tower_1", + "blueprint_provides": [ + { "id": "fbmc_radio_tower_1" }, + { "id": "fbmc_radio_tower" }, + { "id": "primitive_camp_recipes_1" }, + { "id": "gathering" }, + { "id": "firewood" }, + { "id": "foraging" }, + { "id": "sorting" }, + { "id": "logging" } + ], + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_name": "basic survey", + "check_blueprint_needs": false + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_root_cellar", + "description": "Digging a root cellar will give us a way to preserve food.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_root_cellar", + "blueprint_name": "root cellar", + "blueprint_provides": [ { "id": "pantry" }, { "id": "fbmc_radio_tower_root_cellar" } ], + "blueprint_requires": [ { "id": "bed", "amount": 4 }, { "id": "fbmc_radio_tower" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_root_cellar" } ] + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_water_well", + "description": "Digging a well will give us easy access to water.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_water_well", + "blueprint_name": "water well", + "blueprint_provides": [ { "id": "fbmc_radio_tower_water_well" } ], + "blueprint_requires": [ { "id": "bed", "amount": 4 }, { "id": "fbmc_radio_tower" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_water_well" } ] + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_fix_controls", + "description": "Let's fix up radio tower and controls to improve our recruitment efforts.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_fix_controls", + "blueprint_name": "fix radio tower and controls", + "blueprint_requires": [ { "id": "fbmc_radio_tower_water_well" }, { "id": "fbmc_radio_tower_root_cellar" } ], + "blueprint_provides": [ { "id": "fbmc_radio_tower_fix_controls" }, { "id": "recruiting" }, { "id": "radio" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_fix_controls" } ], + "blueprint_needs": { + "time": "10 h", + "skills": [ [ "fabrication", 3 ] ], + "inline": { + "tools": [ ], + "qualities": [ [ { "id": "HAMMER", "level": 2 } ], [ { "id": "SAW_M" } ], [ { "id": "SCREW" } ], [ { "id": "WRENCH" } ] ], + "components": [ + [ + [ "wind_turbine", 4 ], + [ "xl_wind_turbine", 1 ], + [ "solar_panel", 4 ], + [ "reinforced_solar_panel", 4 ], + [ "solar_panel_v2", 2 ], + [ "reinforced_solar_panel_v2", 2 ] + ], + [ [ "storage_battery", 1 ], [ "medium_storage_battery", 4 ], [ "small_storage_battery", 32 ] ], + [ [ "processor", 1 ] ], + [ [ "RAM", 1 ] ], + [ [ "large_lcd_screen", 1 ] ], + [ [ "e_scrap", 2 ] ], + [ [ "circuit", 2 ] ], + [ [ "power_supply", 1 ] ], + [ [ "amplifier", 1 ] ], + [ [ "cable", 160 ] ] + ] + } + }, + "check_blueprint_needs": false + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_0_prepalisade", + "description": "We should dig pits for palisade around camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_0_prepalisade", + "blueprint_provides": [ { "id": "fbmc_radio_tower_0_prepalisade" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_0" }, { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_prepalisade" } ], + "blueprint_name": "dig pits" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_1_prepalisade", + "description": "We should dig pits for palisade around camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_1_prepalisade", + "blueprint_provides": [ { "id": "fbmc_radio_tower_1_prepalisade" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_1" }, { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_prepalisade" } ], + "blueprint_name": "dig pits" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_0_palisade", + "description": "We should build palisade around camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_0_palisade", + "blueprint_provides": [ { "id": "fbmc_radio_tower_0_palisade" }, { "id": "fbmc_radio_tower_palisade" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_0_prepalisade" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_palisade" } ], + "blueprint_name": "build palisade" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_1_palisade", + "description": "We should build palisade around camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_1_palisade", + "blueprint_provides": [ { "id": "fbmc_radio_tower_1_palisade" }, { "id": "fbmc_radio_tower_palisade" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_1_prepalisade" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_palisade" } ], + "blueprint_name": "build palisade" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_fix_tower", + "description": "We could try to fix whole tower and supply it with enough power, which could give us remote access to computer systems connected to backbone network or communication satellites.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "faction_base_radio_tower_fix_tower", + "blueprint_name": "fix whole radio tower", + "blueprint_requires": [ { "id": "bed", "amount": 12 }, { "id": "fbmc_radio_tower_palisade" }, { "id": "fbmc_radio_tower_fix_controls" } ], + "blueprint_provides": [ + { "id": "faction_base_radio_tower_fix_tower" }, + { "id": "hack_recipes_general" }, + { "id": "hack_recipes_science" }, + { "id": "hack_recipes_satellite" } + ], + "blueprint_excludes": [ { "id": "faction_base_radio_tower_fix_tower" } ], + "using": [ [ "soldering_standard", 200 ], [ "welding_standard", 80 ] ], + "blueprint_needs": { + "time": "3 d", + "skills": [ [ "fabrication", 3 ], [ "computer", 4 ], [ "electronics", 8 ] ], + "inline": { + "tools": [ [ [ "laptop", 600 ] ], [ [ "radio_book", -1 ] ] ], + "qualities": [ + [ { "id": "HAMMER", "level": 2 } ], + [ { "id": "SAW_M" } ], + [ { "id": "SCREW", "level": 2 } ], + [ { "id": "SCREW_FINE" } ], + [ { "id": "WRENCH", "level": 2 } ], + [ { "id": "WRENCH_FINE" } ], + [ { "id": "GLARE", "level": 2 } ] + ], + "components": [ + [ + [ "wind_turbine", 16 ], + [ "xl_wind_turbine", 4 ], + [ "solar_panel", 16 ], + [ "reinforced_solar_panel", 16 ], + [ "solar_panel_v2", 8 ], + [ "reinforced_solar_panel_v2", 8 ] + ], + [ [ "storage_battery", 4 ], [ "medium_storage_battery", 16 ] ], + [ [ "sheet_metal", 8 ], [ "wire", 32 ] ], + [ [ "pipe", 24 ] ], + [ [ "processor", 16 ] ], + [ [ "RAM", 16 ] ], + [ [ "e_scrap", 40 ] ], + [ [ "frame", 1 ] ], + [ [ "circuit", 32 ] ], + [ [ "power_supply", 16 ] ], + [ [ "amplifier", 16 ] ], + [ [ "cable", 400 ] ], + [ [ "software_electronics_reference", 1 ] ] + ] + } + }, + "check_blueprint_needs": false + } +] diff --git a/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_log.json b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_log.json new file mode 100644 index 0000000000000..76cd2e9ce65b2 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_log.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_radio_tower_log_shack_1", + "description": "We need some shelter, so build a log shack with a wooden roof on the northeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_log_shack_1", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_log_shack_2", + "description": "We need some shelter, so build a log shack with a wooden roof on the northeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_log_shack_2", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_0" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_log_shack_3", + "description": "We need some shelter, so build a log shack with a wooden roof on the northeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_log_shack_3", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_log_shack_4", + "description": "We need some shelter, so build a log shack with a wooden roof on the northeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_log_shack_4", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_log_shack_5", + "description": "We need some shelter, so build a log shack with a wooden roof on the northeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_log_shack_5", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_log_shack_6", + "description": "We need some shelter, so build a log shack with a wooden roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_log_shack_6", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_1" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_log_shack_7", + "description": "We need some shelter, so build a log shack with a wooden roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_log_shack_7", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_name": "log shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_log_shack_8", + "description": "We need some shelter, so build a log shack with a wooden roof on the south side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_log_shack_8", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_name": "log shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_metal.json b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_metal.json new file mode 100644 index 0000000000000..d06eebcdc87df --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_metal.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_radio_tower_metal_shack_1", + "description": "We need some shelter, so build a metal shack with a metal roof on the northeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_metal_shack_1", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_metal_shack_2", + "description": "We need some shelter, so build a metal shack with a metal roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_metal_shack_2", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_0" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_metal_shack_3", + "description": "We need some shelter, so build a metal shack with a metal roof on the southwest side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_metal_shack_3", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_metal_shack_4", + "description": "We need some shelter, so build a metal shack with a metal roof on the southeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_metal_shack_4", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_metal_shack_5", + "description": "We need some shelter, so build a metal shack with a metal roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_metal_shack_5", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_metal_shack_6", + "description": "We need some shelter, so build a metal shack with a metal roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_metal_shack_6", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_1" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_metal_shack_7", + "description": "We need some shelter, so build a metal shack with a metal roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_metal_shack_7", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_name": "metal shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_metal_shack_8", + "description": "We need some shelter, so build a metal shack with a metal roof on the south side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_metal_shack_8", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_name": "metal shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_migo_resin.json b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_migo_resin.json new file mode 100644 index 0000000000000..23db2f86ef276 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_migo_resin.json @@ -0,0 +1,149 @@ +[ + { + "type": "recipe", + "activity_level": "MODERATE_EXERCISE", + "result": "faction_base_radio_tower_1_controls_room_migo_resin", + "description": "We need some shelter, so build migo resin walls over windows in radio control's room.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_controls_room_migo_resin", + "blueprint_name": "barricade radio control's room", + "blueprint_provides": [ { "id": "fbmc_radio_tower_controls_room" }, { "id": "fbmc_radio_tower" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_1" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_controls_room" } ] + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_migo_resin_shack_0", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof inside radio tower.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_migo_resin_shack_0", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower" } ], + "blueprint_needs": { + "time": "29 h", + "skills": [ [ "fabrication", 2 ] ], + "inline": { "qualities": [ [ { "id": "SMOOTH", "level": 1 } ] ], "components": [ [ [ "alien_pod_resin", 52 ] ] ] } + }, + "check_blueprint_needs": false, + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_migo_resin_shack_1", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof on the northeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_migo_resin_shack_1", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_migo_resin_shack_2", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_migo_resin_shack_2", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_0" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_migo_resin_shack_3", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof on the southwest side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_migo_resin_shack_3", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_migo_resin_shack_4", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof on the southeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_migo_resin_shack_4", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_migo_resin_shack_5", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_migo_resin_shack_5", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_migo_resin_shack_6", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_migo_resin_shack_6", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_1" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_migo_resin_shack_7", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_migo_resin_shack_7", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_name": "mi-go resin shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_migo_resin_shack_8", + "description": "We need some shelter, so build a mi-go resin shack with a mi-go resin roof on the south side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_migo_resin_shack_8", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_name": "mi-go resin shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_rammed_earth.json b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_rammed_earth.json new file mode 100644 index 0000000000000..448ea34b13b9f --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_rammed_earth.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_radio_tower_rammed_earth_shack_1", + "description": "We need some shelter, so build a rammed earth shack with a sod roof on the northeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rammed_earth_shack_1", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rammed_earth_shack_2", + "description": "We need some shelter, so build a rammed earth shack with a sod roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rammed_earth_shack_2", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_0" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rammed_earth_shack_3", + "description": "We need some shelter, so build a rammed earth shack with a sod roof on the southwest side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rammed_earth_shack_3", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rammed_earth_shack_4", + "description": "We need some shelter, so build a rammed earth shack with a sod roof on the southeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rammed_earth_shack_4", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rammed_earth_shack_5", + "description": "We need some shelter, so build a rammed earth shack with a sod roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rammed_earth_shack_5", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rammed_earth_shack_6", + "description": "We need some shelter, so build a rammed earth shack with a sod roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rammed_earth_shack_6", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_1" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rammed_earth_shack_7", + "description": "We need some shelter, so build a rammed earth shack with a sod roof on the south side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rammed_earth_shack_7", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_name": "rammed earth shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rammed_earth_shack_8", + "description": "We need some shelter, so build a rammed earth shack with a sod roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rammed_earth_shack_8", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_name": "rammed earth shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_rock.json b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_rock.json new file mode 100644 index 0000000000000..7e4ec72d2983a --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_rock.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_radio_tower_rock_shack_1", + "description": "We need some shelter, so build a stone shack with a wooden roof on the northeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rock_shack_1", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rock_shack_2", + "description": "We need some shelter, so build a stone shack with a wooden roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rock_shack_2", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_0" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rock_shack_3", + "description": "We need some shelter, so build a stone shack with a wooden roof on the southwest side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rock_shack_3", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rock_shack_4", + "description": "We need some shelter, so build a stone shack with a wooden roof on the southeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rock_shack_4", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rock_shack_5", + "description": "We need some shelter, so build a stone shack with a wooden roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rock_shack_5", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rock_shack_6", + "description": "We need some shelter, so build a stone shack with a wooden roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rock_shack_6", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_1" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rock_shack_7", + "description": "We need some shelter, so build a stone shack with a wooden roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rock_shack_7", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_name": "stone shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_rock_shack_8", + "description": "We need some shelter, so build a stone shack with a wooden roof on the south side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_rock_shack_8", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_name": "stone shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_wad.json b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_wad.json new file mode 100644 index 0000000000000..7b9d2e522dcc0 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_wad.json @@ -0,0 +1,114 @@ +[ + { + "type": "recipe", + "result": "faction_base_radio_tower_wad_shack_1", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof on the northeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wad_shack_1", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wad_shack_2", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wad_shack_2", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_0" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wad_shack_3", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof on the southwest side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wad_shack_3", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wad_shack_4", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof on the southeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wad_shack_4", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wad_shack_5", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wad_shack_5", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wad_shack_6", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wad_shack_6", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_1" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wad_shack_7", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wad_shack_7", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_name": "wattle and daub shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wad_shack_8", + "description": "We need some shelter, so build a wattle and daub shack with a sod roof on the south side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wad_shack_8", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_name": "wattle and daub shack" + } +] diff --git a/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_wood.json b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_wood.json new file mode 100644 index 0000000000000..b291ea007188a --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_radio_tower/recipe_modular_radio_tower_wood.json @@ -0,0 +1,151 @@ +[ + { + "type": "recipe", + "result": "faction_base_radio_tower_1_controls_room_wood", + "description": "We need some shelter, so build wooden walls over windows in radio control's room.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_controls_room_wood", + "blueprint_name": "barricade radio control's room", + "blueprint_provides": [ { "id": "fbmc_radio_tower_controls_room" }, { "id": "fbmc_radio_tower" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_1" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_controls_room" } ] + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wood_shack_0", + "description": "We need some shelter, so build a wooden shack with a wooden roof inside radio tower.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wood_shack_0", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower" } ], + "blueprint_needs": { + "time": "40 h", + "skills": [ [ "fabrication", 3 ] ], + "inline": { + "qualities": [ [ { "id": "HAMMER", "level": 2 } ], [ { "id": "SAW_W", "level": 2 } ] ], + "components": [ [ [ "2x4", 230 ] ], [ [ "glass_sheet", 1 ] ], [ [ "hinge", 2 ] ], [ [ "nail", 1100 ] ], [ [ "wood_panel", 54 ] ] ] + } + }, + "check_blueprint_needs": false, + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wood_shack_1", + "description": "We need some shelter, so build a wooden shack with a wooden roof on the northeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wood_shack_1", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_1" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_0" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wood_shack_2", + "description": "We need some shelter, so build a wooden shack with a wooden roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wood_shack_2", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_0" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wood_shack_3", + "description": "We need some shelter, so build a wooden shack with a wooden roof on the southwest side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wood_shack_3", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_2" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wood_shack_4", + "description": "We need some shelter, so build a wooden shack with a wooden roof on the southeast side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wood_shack_4", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_3" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wood_shack_5", + "description": "We need some shelter, so build a wooden shack with a wooden roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wood_shack_5", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_5" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_4" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wood_shack_6", + "description": "We need some shelter, so build a wooden shack with a wooden roof on the southeast corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wood_shack_6", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_1" }, { "id": "fbmc_radio_tower_1" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wood_shack_7", + "description": "We need some shelter, so build a wooden shack with a wooden roof on the southwest corner of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wood_shack_7", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_6" } ], + "blueprint_name": "wooden shack" + }, + { + "type": "recipe", + "result": "faction_base_radio_tower_wood_shack_8", + "description": "We need some shelter, so build a wooden shack with a wooden roof on the south side of the camp.", + "category": "CC_BUILDING", + "subcategory": "CSC_BUILDING_BASES", + "autolearn": false, + "never_learn": true, + "construction_blueprint": "fbmc_radio_tower_wood_shack_8", + "blueprint_provides": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_excludes": [ { "id": "fbmc_radio_tower_shack_8" } ], + "blueprint_requires": [ { "id": "fbmc_radio_tower_shack_7" } ], + "blueprint_name": "wooden shack" + } +] diff --git a/data/json/recipes/basecamps/recipe_groups.json b/data/json/recipes/basecamps/recipe_groups.json index 44399021cf29b..5294528ae3e68 100644 --- a/data/json/recipes/basecamps/recipe_groups.json +++ b/data/json/recipes/basecamps/recipe_groups.json @@ -24,6 +24,46 @@ "id": "faction_base_shelter_2_0", "description": "Central Stairs Evac Shelter Base", "om_terrains": [ "shelter_2", "shelter_2_vandal" ] + }, + { + "id": "faction_base_pottery_cottage_0", + "description": "Pottery Cottage Camp", + "om_terrains": [ "pottery_cottage" ] + }, + { + "id": "faction_base_radio_tower_0", + "description": "Radio Tower Without Building Base", + "om_terrains": [ "radio_tower" ] + }, + { + "id": "faction_base_radio_tower_1_0", + "description": "Radio Tower With Building Base", + "om_terrains": [ "radio_tower_1" ] + }, + { + "id": "faction_base_fire_lookout_tower_0", + "description": "Fire Lookout Tower Base", + "om_terrains": [ "ws_fire_lookout_tower_base" ] + }, + { + "id": "faction_base_mansion_+1_0", + "description": "Mansion Fountain Garden Base", + "om_terrains": [ "mansion_+1" ] + }, + { + "id": "faction_base_mansion_+2_0", + "description": "Mansion Dance Floor Room Base", + "om_terrains": [ "mansion_+2" ] + }, + { + "id": "faction_base_mansion_+3_0", + "description": "Mansion Basketball Room Base", + "om_terrains": [ "mansion_+3" ] + }, + { + "id": "faction_base_mansion_+4_0", + "description": "Mansion Garden With Columns Base", + "om_terrains": [ "mansion_+4" ] } ] }, @@ -38,7 +78,21 @@ { "id": "faction_base_livestock_0", "description": "Livestock Area", "om_terrains": [ "field" ] }, { "id": "faction_base_storehouse_0", "description": "Central Storage Building", "om_terrains": [ "field" ] }, { "id": "faction_base_saltworks_0", "description": "Saltworks Area", "om_terrains": [ "field" ] }, - { "id": "faction_base_workshop_0", "description": "Fabrication Workshop", "om_terrains": [ "field" ] } + { "id": "faction_base_workshop_0", "description": "Fabrication Workshop", "om_terrains": [ "field" ] }, + { "id": "faction_base_mansion_e1", "description": "Mansion's Entrance", "om_terrains": [ "mansion_e1" ] }, + { "id": "faction_base_mansion_e2", "description": "Mansion's Entrance", "om_terrains": [ "mansion_e2" ] }, + { "id": "faction_base_mansion_t1", "description": "Mansion's Swimming Pool", "om_terrains": [ "mansion_t1" ] }, + { "id": "faction_base_mansion_t2", "description": "Mansion's Bedrooms", "om_terrains": [ "mansion_t2" ] }, + { "id": "faction_base_mansion_t3", "description": "Mansion's???", "om_terrains": [ "mansion_t3" ] }, + { "id": "faction_base_mansion_t4", "description": "Mansion's Kitchen", "om_terrains": [ "mansion_t4" ] }, + { "id": "faction_base_mansion_t5", "description": "Mansion's Library", "om_terrains": [ "mansion_t5" ] }, + { "id": "faction_base_mansion_t6", "description": "Mansion's Bedroom", "om_terrains": [ "mansion_t6" ] }, + { "id": "faction_base_mansion_t7", "description": "Mansion's Living Rooms", "om_terrains": [ "mansion_t7" ] }, + { "id": "faction_base_mansion_c1", "description": "Mansion's Swimming Pool", "om_terrains": [ "mansion_c1" ] }, + { "id": "faction_base_mansion_c2", "description": "Mansion's Bar", "om_terrains": [ "mansion_c2" ] }, + { "id": "faction_base_mansion_c3", "description": "Mansion's Living Rooms", "om_terrains": [ "mansion_c3" ] }, + { "id": "faction_base_mansion_c4", "description": "Mansion's Bedroom", "om_terrains": [ "mansion_c4" ] }, + { "id": "faction_base_mansion_c5", "description": "Mansion's Kitchen", "om_terrains": [ "mansion_c5" ] } ] }, { @@ -271,5 +325,56 @@ { "id": "pipe_npc_drop", "description": " Craft: Pipe, Drop Hammer" }, { "id": "rebar_npc_drop", "description": " Craft: Rebar, Drop Hammer" } ] + }, + { + "type": "recipe_group", + "id": "fishing_recipes", + "building_type": "COOK", + "recipes": [ + { "id": "fish_npc_fish_trap", "description": " Fishing: fish, plastic fish trap" }, + { "id": "fish_npc_fish_rod", "description": " Fishing: fish, basic fishing rod" }, + { "id": "fish_npc_fish_rod_pro", "description": " Fishing: fish, pro fishing rod" } + ] + }, + { + "type": "recipe_group", + "id": "hack_recipes_general", + "building_type": "BASE", + "recipes": [ + { "id": "mobile_memory_card_npc_hack", "description": " Hack & Download: random data, SD-Memory card" }, + { "id": "software_useless_npc_hack", "description": " Hack & Download: misc software, USB drive" }, + { "id": "software_math_npc_hack", "description": " Hack & Download: MatheMAX, USB drive" } + ] + }, + { + "type": "recipe_group", + "id": "clay_recipes_1", + "building_type": "SMITH", + "recipes": [ + { "id": "brick", "description": " Craft: Brick" }, + { "id": "fire_brick", "description": " Craft: Fire Brick" }, + { "id": "pebble_clay", "description": " Craft: Clay Pellet" }, + { "id": "clay_canister", "description": " Craft: Clay Canister" }, + { "id": "clay_hydria", "description": " Craft: Clay Hydria" } + ] + }, + { + "type": "recipe_group", + "id": "hack_recipes_science", + "building_type": "BASE", + "recipes": [ + { "id": "mobile_memory_card_science_npc_hack", "description": " Hack & Download: science data, SD-Memory card" }, + { "id": "software_medical_npc_hack", "description": " Hack & Download: MediSoft, USB drive" }, + { + "id": "software_electronics_reference_npc_hack", + "description": " Hack & Download: IC datasheet archives, USB drive" + } + ] + }, + { + "type": "recipe_group", + "id": "hack_recipes_satellite", + "building_type": "BASE", + "recipes": [ { "id": "satellitemap_npc_hack", "description": " Hack & Download: Satellite Map, sketch" } ] } ] diff --git a/data/json/recipes/basecamps/recipe_modular_canteen/recipe_modular_canteen_common.json b/data/json/recipes/basecamps/recipe_modular_canteen/recipe_modular_canteen_common.json index 9b0bf13067490..295a53a00ce0f 100644 --- a/data/json/recipes/basecamps/recipe_modular_canteen/recipe_modular_canteen_common.json +++ b/data/json/recipes/basecamps/recipe_modular_canteen/recipe_modular_canteen_common.json @@ -175,19 +175,19 @@ "blueprint_provides": [ { "id": "fbmk_pantry_furniture" }, { "id": "pantry" } ], "blueprint_excludes": [ { "id": "fbmk_pantry_furniture" } ], "blueprint_needs": { - "time": "1 d 4 h 20 m", - "skills": [ [ "cooking", 3 ], [ "fabrication", 4 ], [ "survival", 4 ] ], + "time": "1 d 9 h 20 m", + "skills": [ [ "survival", 4 ], [ "fabrication", 4 ], [ "cooking", 3 ] ], "inline": { - "tools": [ ], + "tools": [ [ [ "elec_jackhammer", 14000 ], [ "jackhammer", 280 ], [ "pickaxe", -1 ] ] ], "qualities": [ [ { "id": "DIG", "level": 2 } ], [ { "id": "HAMMER", "level": 2 } ], [ { "id": "SAW_W" } ] ], "components": [ [ [ "2x4", 112 ] ], - [ [ "wood_sheet", 24 ], [ "wood_panel", 48 ] ], + [ [ "brick", 80 ], [ "rock", 80 ] ], [ [ "nail", 504 ] ], [ [ "sheet_metal_small", 24 ] ], + [ [ "straw_pile", 24 ], [ "withered", 24 ] ], [ [ "water_faucet", 2 ] ], - [ [ "rock", 80 ], [ "brick", 80 ] ], - [ [ "withered", 24 ], [ "straw_pile", 24 ] ] + [ [ "wood_panel", 48 ], [ "wood_sheet", 24 ] ] ] } } diff --git a/data/json/recipes/basecamps/recipe_modular_field_common.json b/data/json/recipes/basecamps/recipe_modular_field_common.json index 83877967fef30..97a42204ed3f1 100644 --- a/data/json/recipes/basecamps/recipe_modular_field_common.json +++ b/data/json/recipes/basecamps/recipe_modular_field_common.json @@ -897,12 +897,12 @@ "blueprint_provides": [ { "id": "pantry" } ], "blueprint_requires": [ { "id": "fbmh_northeast", "amount": 4 }, { "id": "fbmh_fire_northeast" }, { "id": "fbmh_bed2_northeast" } ], "blueprint_needs": { - "time": "4 h 40 m", - "skills": [ [ "fabrication", 4 ], [ "survival", 4 ] ], + "time": "7 h 10 m", + "skills": [ [ "survival", 4 ], [ "fabrication", 4 ] ], "inline": { - "tools": [ ], + "tools": [ [ [ "elec_jackhammer", 7000 ], [ "jackhammer", 140 ], [ "pickaxe", -1 ] ] ], "qualities": [ [ { "id": "DIG", "level": 2 } ], [ { "id": "HAMMER", "level": 2 } ] ], - "components": [ [ [ "rock", 40 ], [ "brick", 40 ] ], [ [ "2x4", 6 ], [ "stick", 6 ] ], [ [ "withered", 12 ], [ "straw_pile", 12 ] ] ] + "components": [ [ [ "2x4", 6 ], [ "stick", 6 ] ], [ [ "brick", 40 ], [ "rock", 40 ] ], [ [ "straw_pile", 12 ], [ "withered", 12 ] ] ] } } }, diff --git a/data/json/recipes/basecamps/recipe_modular_field_defenses.json b/data/json/recipes/basecamps/recipe_modular_field_defenses.json index 20af1b5d800d4..b1df09f3d343b 100644 --- a/data/json/recipes/basecamps/recipe_modular_field_defenses.json +++ b/data/json/recipes/basecamps/recipe_modular_field_defenses.json @@ -14,9 +14,13 @@ "blueprint_requires": [ { "id": "fbmh_northeast", "amount": 4 }, { "id": "fbmh_fire_northeast" }, { "id": "fbmh_bed2_northeast" } ], "blueprint_excludes": [ { "id": "fbm_no_dig" } ], "blueprint_needs": { - "time": "1 d 21 h", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "3 d 18 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 126000 ], [ "jackhammer", 2520 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } }, { @@ -34,9 +38,13 @@ "blueprint_requires": [ { "id": "fbmh_northeast", "amount": 4 }, { "id": "fbmh_fire_northeast" }, { "id": "fbmh_bed2_northeast" } ], "blueprint_excludes": [ { "id": "fbm_no_dig" } ], "blueprint_needs": { - "time": "1 d 21 h", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "3 d 18 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 126000 ], [ "jackhammer", 2520 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } }, { @@ -54,9 +62,13 @@ "blueprint_requires": [ { "id": "fbmh_northeast", "amount": 4 }, { "id": "fbmh_fire_northeast" }, { "id": "fbmh_bed2_northeast" } ], "blueprint_excludes": [ { "id": "fbm_no_dig" }, { "id": "fbmh_trench_northeast" }, { "id": "fbmh_trench_east" } ], "blueprint_needs": { - "time": "12 h 30 m", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "1 d 1 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 35000 ], [ "jackhammer", 700 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } }, { @@ -74,9 +86,13 @@ "blueprint_requires": [ { "id": "fbmh_northeast", "amount": 4 }, { "id": "fbmh_fire_northeast" }, { "id": "fbmh_bed2_northeast" } ], "blueprint_excludes": [ { "id": "fbm_no_dig" }, { "id": "fbmh_trench_northwest" }, { "id": "fbmh_trench_west" } ], "blueprint_needs": { - "time": "12 h 30 m", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "1 d 1 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 35000 ], [ "jackhammer", 700 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } }, { @@ -94,9 +110,13 @@ "blueprint_requires": [ { "id": "fbmh_northeast", "amount": 4 }, { "id": "fbmh_fire_northeast" }, { "id": "fbmh_bed2_northeast" } ], "blueprint_excludes": [ { "id": "fbm_no_dig" }, { "id": "fbmh_trench_southeast" }, { "id": "fbmh_trench_east" } ], "blueprint_needs": { - "time": "12 h 30 m", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "1 d 1 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 35000 ], [ "jackhammer", 700 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } }, { @@ -114,9 +134,13 @@ "blueprint_requires": [ { "id": "fbmh_northeast", "amount": 4 }, { "id": "fbmh_fire_northeast" }, { "id": "fbmh_bed2_northeast" } ], "blueprint_excludes": [ { "id": "fbm_no_dig" }, { "id": "fbmh_trench_southwest" }, { "id": "fbmh_trench_west" } ], "blueprint_needs": { - "time": "12 h 30 m", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "1 d 1 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 35000 ], [ "jackhammer", 700 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } }, { @@ -134,9 +158,13 @@ "blueprint_requires": [ { "id": "fbmh_northeast", "amount": 4 }, { "id": "fbmh_fire_northeast" }, { "id": "fbmh_bed2_northeast" } ], "blueprint_excludes": [ { "id": "fbm_no_dig" }, { "id": "fbmh_trench_southeast" }, { "id": "fbmh_trench_northeast" } ], "blueprint_needs": { - "time": "2 d 22 h", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "5 d 20 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 196000 ], [ "jackhammer", 3920 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } }, { @@ -154,9 +182,13 @@ "blueprint_requires": [ { "id": "fbmh_northeast", "amount": 4 }, { "id": "fbmh_fire_northeast" }, { "id": "fbmh_bed2_northeast" } ], "blueprint_excludes": [ { "id": "fbm_no_dig" }, { "id": "fbmh_trench_southwest" }, { "id": "fbmh_trench_northwest" } ], "blueprint_needs": { - "time": "2 d 22 h", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "5 d 20 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 196000 ], [ "jackhammer", 3920 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } } ] diff --git a/data/json/recipes/basecamps/recipe_primitive_field.json b/data/json/recipes/basecamps/recipe_primitive_field.json index 28900960bfc0f..f0f3fe4b20c45 100644 --- a/data/json/recipes/basecamps/recipe_primitive_field.json +++ b/data/json/recipes/basecamps/recipe_primitive_field.json @@ -508,9 +508,13 @@ "time": "180 m", "blueprint_requires": [ { "id": "not_an_upgrade" } ], "blueprint_needs": { - "time": "5 h", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "10 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 14000 ], [ "jackhammer", 280 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } }, { @@ -604,9 +608,13 @@ "time": "180 m", "blueprint_requires": [ { "id": "not_an_upgrade" } ], "blueprint_needs": { - "time": "15 h", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "1 d 6 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 42000 ], [ "jackhammer", 840 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } }, { @@ -764,9 +772,13 @@ "time": "180 m", "blueprint_requires": [ { "id": "not_an_upgrade" } ], "blueprint_needs": { - "time": "12 h 30 m", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "1 d 1 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 35000 ], [ "jackhammer", 700 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } }, { @@ -1023,9 +1035,13 @@ "blueprint_requires": [ { "id": "not_an_upgrade" } ], "time": "180 m", "blueprint_needs": { - "time": "15 h", - "skills": [ [ "survival", 1 ] ], - "inline": { "tools": [ ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ ] } + "time": "1 d 6 h", + "skills": [ [ "survival", 3 ] ], + "inline": { + "tools": [ [ [ "elec_jackhammer", 42000 ], [ "jackhammer", 840 ], [ "pickaxe", -1 ] ] ], + "qualities": [ [ { "id": "DIG", "level": 2 } ] ], + "components": [ ] + } } }, { diff --git a/data/json/recipes/food/bread.json b/data/json/recipes/food/bread.json index 63671d90fd276..d1c8b5256bd79 100644 --- a/data/json/recipes/food/bread.json +++ b/data/json/recipes/food/bread.json @@ -33,19 +33,47 @@ "skill_used": "cooking", "difficulty": 3, "charges": 10, - "time": "30 m", + "time": "1 h 30 m", "batch_time_factors": [ 50, 5 ], + "autolearn": false, "book_learn": [ [ "family_cookbook", 1 ], [ "baking_book", 2 ], [ "cookbook", 2 ], [ "cookbook_daintydishes", 2 ] ], "qualities": [ { "id": "COOK", "level": 3 } ], "tools": [ [ [ "surface_heat", 8, "LIST" ] ] ], "components": [ [ [ "bread_flour", 1 ] ], - [ [ "flour", 1 ] ], - [ [ "cornmeal", 1 ] ], - [ [ "molasses", 1 ] ], - [ [ "water", 1 ] ], + [ [ "flour", 5 ] ], + [ [ "cornmeal", 5 ] ], + [ [ "molasses", 2 ] ], + [ [ "sugar", 1 ] ], + [ [ "molasses", 1 ], [ "sugar", 13 ] ], + [ [ "water", 1 ], [ "water_clean", 1 ] ], [ [ "salt", 1 ] ] ], "//": "Later: this needs baking soda. Also possibility of rewrite to use portions of components' charges." + }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "hallula", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_BREAD", + "skill_used": "cooking", + "difficulty": 3, + "charges": 8, + "time": "30 m", + "batch_time_factors": [ 50, 5 ], + "book_learn": [ [ "family_cookbook", 1 ], [ "baking_book", 2 ], [ "cookbook", 2 ] ], + "qualities": [ { "id": "COOK", "level": 3 } ], + "tools": [ [ [ "surface_heat", 8, "LIST" ] ] ], + "components": [ + [ [ "flour", 3 ] ], + [ [ "yeast", 2 ] ], + [ [ "sugar", 1 ] ], + [ [ "salt", 1 ] ], + [ [ "water", 1 ] ], + [ [ "any_butter_or_oil", 2, "LIST" ], [ "edible_lard", 1, "LIST" ] ] + ], + "//": "I'm not sure whether this should also be rewritten to use a portion of charges, or scaled up so that the smallest component in the recipe becomes a single charge.", + "//2": "Since milk is only used sparingly in the creation of hallula, to give it a glazed appearance, Cataclysm practicality suggests omitting it." } ] diff --git a/data/json/recipes/food/dry.json b/data/json/recipes/food/dry.json index 0fedb62d4ee58..7d18e8d7061fb 100644 --- a/data/json/recipes/food/dry.json +++ b/data/json/recipes/food/dry.json @@ -240,6 +240,37 @@ [ [ "veggy_any_fresh_uncooked", 1, "LIST" ], [ "dandelion_cooked", 1 ], [ "burdock_cooked", 1 ], [ "wild_herbs", 40 ] ] ] }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "dry_corn", + "charges": 1, + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_DRY", + "skill_used": "cooking", + "difficulty": 2, + "time": "18 m", + "autolearn": true, + "batch_time_factors": [ 67, 5 ], + "tools": [ [ [ "dehydrator", 25 ], [ "char_smoker", 25 ] ] ], + "components": [ [ [ "corn_kernels", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "dry_corn", + "charges": 1, + "category": "CC_FOOD", + "id_suffix": "frozen_ingredients", + "subcategory": "CSC_FOOD_DRY", + "skill_used": "cooking", + "difficulty": 2, + "time": "20 m", + "autolearn": true, + "batch_time_factors": [ 67, 5 ], + "tools": [ [ [ "dehydrator", 25 ], [ "char_smoker", 25 ] ], [ [ "surface_heat", 5, "LIST" ] ] ], + "components": [ [ [ "corn_kernels", 1 ] ] ] + }, { "result": "dry_veggy_tainted", "charges": 1, @@ -396,5 +427,19 @@ "batch_time_factors": [ 67, 5 ], "tools": [ [ [ "dehydrator", 25 ], [ "char_smoker", 25 ] ], [ [ "surface_heat", 5, "LIST" ] ] ], "components": [ [ [ "raw_lentils", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "gelatin_powder", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_DRY", + "skill_used": "cooking", + "difficulty": 2, + "time": "18 m", + "autolearn": true, + "batch_time_factors": [ 67, 5 ], + "tools": [ [ [ "dehydrator", 25 ], [ "char_smoker", 25 ] ] ], + "components": [ [ [ "gelatin_fresh", 1 ] ] ] } ] diff --git a/data/json/recipes/food/other.json b/data/json/recipes/food/other.json index f7f915c4fd061..11602abce0cf8 100644 --- a/data/json/recipes/food/other.json +++ b/data/json/recipes/food/other.json @@ -42,5 +42,202 @@ "time": "15 m", "charges": 2, "components": [ [ [ "water_clean", 1 ] ], [ [ "freeze_dried_meal", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "acid_soaked_hide", + "result_mult": 1, + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 3, + "time": "8 m", + "batch_time_factors": [ 50, 4 ], + "book_learn": [ [ "textbook_chemistry", 3 ], [ "adv_chemistry", 3 ] ], + "components": [ + [ [ "formic_acid", 10 ] ], + [ [ "water", 10 ], [ "water_clean", 10 ] ], + [ [ "raw_leather", 1 ], [ "raw_tainted_leather", 1 ], [ "raw_hleather", 1 ], [ "raw_demihumanleather", 1 ] ] + ] + }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "gelatin_fresh", + "byproducts": [ [ "formic_acid" ], [ "ruined_chunks" ] ], + "result_mult": 12, + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 2, + "time": "120 m", + "batch_time_factors": [ 90, 1 ], + "charges": 1, + "autolearn": true, + "tools": [ [ "colander_steel" ] ], + "components": [ [ [ "gelatin_extracted", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "gelatin_dessert_processed", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 1, + "time": "60 m", + "batch_time_factors": [ 20, 1 ], + "charges": 5, + "autolearn": true, + "qualities": [ { "id": "BOIL", "level": 1 }, { "id": "CONTAIN", "level": 1 } ], + "tools": [ [ [ "surface_heat", 16, "LIST" ] ] ], + "components": [ [ [ "water", 1 ], [ "water_clean", 1 ] ], [ [ "gelatin_dessert_powder", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "gelatin_dessert_base", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 1, + "time": "60 m", + "batch_time_factors": [ 20, 1 ], + "charges": 5, + "autolearn": true, + "qualities": [ { "id": "BOIL", "level": 1 }, { "id": "CONTAIN", "level": 1 } ], + "tools": [ [ [ "surface_heat", 16, "LIST" ] ] ], + "components": [ [ [ "water", 2 ], [ "water_clean", 2 ] ], [ [ "gelatin_powder", 10 ], [ "gelatin_fresh", 1 ] ], [ [ "sugar", 20 ] ] ] + }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "gelatin_dessert_homemade", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 1, + "time": "60 m", + "batch_time_factors": [ 20, 1 ], + "charges": 5, + "book_learn": [ [ "sweets_book", 1 ], [ "baking_book", 1 ], [ "mag_cooking", 3 ], [ "family_cookbook", 3 ] ], + "qualities": [ { "id": "BOIL", "level": 1 }, { "id": "CONTAIN", "level": 1 } ], + "tools": [ [ [ "surface_heat", 16, "LIST" ] ] ], + "components": [ + [ [ "water", 1 ], [ "water_clean", 1 ], [ "sweet_water", 1 ] ], + [ [ "gelatin_powder", 5 ], [ "gelatin_fresh", 1 ] ], + [ [ "sugar", 20 ] ], + [ [ "sweet_juice", 1, "LIST" ], [ "soda_pop", 1, "LIST" ] ] + ] + }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "gelatin_dessert_fruit", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 1, + "time": "60 m", + "batch_time_factors": [ 20, 1 ], + "charges": 5, + "book_learn": [ [ "sweets_book", 1 ], [ "baking_book", 1 ], [ "mag_cooking", 3 ], [ "family_cookbook", 3 ] ], + "qualities": [ { "id": "BOIL", "level": 1 }, { "id": "CONTAIN", "level": 1 } ], + "tools": [ [ [ "surface_heat", 16, "LIST" ] ] ], + "components": [ + [ [ "water", 1 ], [ "water_clean", 1 ], [ "sweet_water", 1 ] ], + [ [ "gelatin_powder", 5 ], [ "gelatin_fresh", 1 ] ], + [ [ "sugar", 10 ] ], + [ [ "sweet_juice", 1, "LIST" ], [ "soda_pop", 1, "LIST" ] ], + [ [ "sweet_fruit", 1, "LIST" ] ] + ] + }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "gelatin_dessert_vegan", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 1, + "time": "60 m", + "batch_time_factors": [ 20, 1 ], + "charges": 5, + "book_learn": [ [ "sweets_book", 1 ], [ "baking_book", 1 ], [ "mag_cooking", 1 ], [ "family_cookbook", 1 ] ], + "qualities": [ { "id": "BOIL", "level": 1 }, { "id": "CONTAIN", "level": 1 } ], + "tools": [ [ [ "surface_heat", 16, "LIST" ] ] ], + "components": [ + [ [ "water", 1 ], [ "water_clean", 1 ], [ "sweet_water", 1 ] ], + [ [ "chem_agar", 5 ] ], + [ [ "sugar", 20 ] ], + [ [ "sweet_juice", 1, "LIST" ], [ "soda_pop", 1, "LIST" ] ] + ] + }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "gelatin_dessert_vegan_fruit", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 1, + "time": "60 m", + "batch_time_factors": [ 20, 1 ], + "charges": 5, + "book_learn": [ [ "sweets_book", 1 ], [ "baking_book", 1 ], [ "mag_cooking", 1 ], [ "family_cookbook", 1 ] ], + "qualities": [ { "id": "BOIL", "level": 1 }, { "id": "CONTAIN", "level": 1 } ], + "tools": [ [ [ "surface_heat", 16, "LIST" ] ] ], + "components": [ + [ [ "water", 1 ], [ "water_clean", 1 ], [ "sweet_water", 1 ] ], + [ [ "chem_agar", 5 ] ], + [ [ "sugar", 20 ] ], + [ [ "sweet_juice", 1, "LIST" ], [ "soda_pop", 1, "LIST" ] ], + [ [ "sweet_fruit", 1, "LIST" ] ] + ] + }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "candy4", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 2, + "time": "60 m", + "batch_time_factors": [ 20, 1 ], + "charges": 12, + "book_learn": [ [ "sweets_book", 2 ], [ "baking_book", 2 ], [ "family_cookbook", 2 ] ], + "qualities": [ { "id": "BOIL", "level": 1 } ], + "tools": [ [ [ "surface_heat", 16, "LIST" ] ], [ [ "mold_plastic", 1 ] ] ], + "components": [ + [ [ "water", 1 ], [ "water_clean", 1 ], [ "sweet_water", 1 ] ], + [ [ "beet_syrup", 1 ] ], + [ [ "gelatin_powder", 25 ], [ "gelatin_fresh", 2 ] ], + [ [ "sugar", 50 ] ], + [ [ "sweet_juice", 1, "LIST" ], [ "soda_pop", 1, "LIST" ] ] + ] + }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "candy5", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 2, + "time": "60 m", + "batch_time_factors": [ 20, 1 ], + "charges": 12, + "book_learn": [ [ "sweets_book", 2 ], [ "baking_book", 2 ], [ "family_cookbook", 2 ] ], + "qualities": [ { "id": "BOIL", "level": 1 } ], + "tools": [ [ [ "surface_heat", 16, "LIST" ] ], [ [ "mold_plastic", 1 ] ] ], + "components": [ + [ [ "water", 1 ], [ "water_clean", 1 ], [ "sweet_water", 1 ] ], + [ [ "beet_syrup", 1 ] ], + [ [ "chem_agar", 25 ] ], + [ [ "sugar", 50 ] ], + [ [ "sweet_juice", 1, "LIST" ], [ "soda_pop", 1, "LIST" ] ] + ] } ] diff --git a/data/json/recipes/food/pasta.json b/data/json/recipes/food/pasta.json index 2ed02574899ee..bdf3c328df838 100644 --- a/data/json/recipes/food/pasta.json +++ b/data/json/recipes/food/pasta.json @@ -112,7 +112,9 @@ [ "veggy", 2 ], [ "veggy_wild", 2 ], [ "rehydrated_veggy", 2 ], + [ "rehydrated_corn_kernels", 2 ], [ "dry_veggy", 2 ], + [ "dry_corn", 2 ], [ "mushroom", 2 ], [ "mushroom_cooked", 2 ], [ "morel_cooked", 2 ], diff --git a/data/json/recipes/other/bots.json b/data/json/recipes/other/bots.json index 413f27a8c981b..8ff9c11997cfd 100644 --- a/data/json/recipes/other/bots.json +++ b/data/json/recipes/other/bots.json @@ -791,5 +791,31 @@ [ [ "floodlight", 1 ] ], [ [ "turret_chassis", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "bot_tazer_hack", + "category": "CC_ELECTRONIC", + "subcategory": "CSC_ELECTRONIC_OTHER", + "skill_used": "electronics", + "skills_required": [ [ "mechanics", 4 ], [ "computer", 4 ] ], + "difficulty": 6, + "time": "50 m", + "reversible": true, + "decomp_learn": 7, + "book_learn": [ [ "recipe_lab_elec", 5 ], [ "textbook_robots", 6 ] ], + "using": [ [ "soldering_standard", 10 ] ], + "qualities": [ { "id": "SCREW", "level": 1 } ], + "components": [ + [ [ "tazer", 1 ] ], + [ [ "ai_module_basic", 1 ] ], + [ [ "RAM", 1 ] ], + [ [ "small_storage_battery", 1 ] ], + [ [ "scrap", 1 ] ], + [ [ "quad_rotors", 1 ] ], + [ [ "sensor_module", 1 ] ], + [ [ "identification_module", 1 ] ] + ] } ] diff --git a/data/json/recipes/other/materials.json b/data/json/recipes/other/materials.json index 77dae4f25b512..122dcebce58b4 100644 --- a/data/json/recipes/other/materials.json +++ b/data/json/recipes/other/materials.json @@ -29,7 +29,21 @@ "book_learn": [ [ "concrete_book", 4 ] ], "batch_time_factors": [ 75, 3 ], "tools": [ [ [ "crucible", -1 ], [ "crucible_clay", -1 ] ], [ [ "forge", 250 ], [ "oxy_torch", 50 ] ] ], - "components": [ [ [ "material_quicklime", 50 ] ], [ [ "material_sand", 50 ] ] ] + "components": [ [ [ "material_quicklime", 50 ] ], [ [ "material_sand", 50 ], [ "material_gravel", 50 ] ] ] + }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "material_gravel", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "survival", + "skills_required": [ "survival", 1 ], + "difficulty": 1, + "time": "10m", + "autolearn": true, + "qualities": [ { "id": "HAMMER", "level": 1 }, { "id": "ANVIL", "level": 1 } ], + "components": [ [ [ "pebble", 10 ] ] ] }, { "type": "recipe", diff --git a/data/json/recipes/other/other.json b/data/json/recipes/other/other.json index f0ed6b762dccb..5db4e4f6aad83 100644 --- a/data/json/recipes/other/other.json +++ b/data/json/recipes/other/other.json @@ -15,8 +15,8 @@ [ "fish", 1 ], [ "dry_meat", 1 ], [ "dry_fish", 1 ], - [ "corn", 1 ], - [ "irradiated_corn", 1 ], + [ "corn_kernels", 1 ], + [ "rehydrated_corn_kernels", 1 ], [ "irradiated_carrot", 1 ], [ "carrot", 1 ], [ "bread", 1 ], diff --git a/data/json/recipes/recipe_ammo.json b/data/json/recipes/recipe_ammo.json index 63e3924fc1b53..15a33ee65b177 100644 --- a/data/json/recipes/recipe_ammo.json +++ b/data/json/recipes/recipe_ammo.json @@ -556,6 +556,7 @@ "autolearn": true, "book_learn": [ [ "recipe_bullets", 2 ], [ "manual_shotgun", 2 ] ], "qualities": [ { "id": "CUT", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 20 ], [ "chem_black_powder", 20 ] ], [ @@ -584,6 +585,7 @@ "autolearn": true, "book_learn": [ [ "recipe_bullets", 2 ], [ "manual_shotgun", 2 ] ], "qualities": [ { "id": "CUT", "level": 1 }, { "id": "SAW_M", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 20 ], [ "chem_black_powder", 20 ] ], [ [ "scrap", 1 ], [ "nail", 8 ], [ "copper", 16 ], [ "lead", 16 ] ], @@ -603,6 +605,7 @@ "autolearn": true, "book_learn": [ [ "recipe_bullets", 2 ], [ "manual_shotgun", 2 ] ], "qualities": [ { "id": "CUT", "level": 1 }, { "id": "SAW_M", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 20 ], [ "chem_black_powder", 20 ] ], [ [ "rebar", 1 ], [ "spear_rebar", 1 ], [ "rebar_rail", 1 ], [ "steel_rail", 1 ], [ "scrap", 1 ] ], @@ -624,6 +627,7 @@ "book_learn": [ [ "recipe_bullets", 2 ], [ "manual_shotgun", 2 ] ], "qualities": [ { "id": "CUT", "level": 1 }, { "id": "HAMMER", "level": 1 } ], "using": [ [ "surface_heat", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 20 ], [ "chem_black_powder", 20 ] ], [ [ "lead", 24 ] ], [ [ "paper", 1 ], [ "aluminum_foil", 1 ] ] ] }, { @@ -639,6 +643,7 @@ "autolearn": true, "book_learn": [ [ "recipe_bullets", 2 ], [ "manual_shotgun", 2 ] ], "qualities": [ { "id": "CUT", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 20 ], [ "chem_black_powder", 20 ] ], [ [ "nail", 8 ], [ "combatnail", 8 ] ], diff --git a/data/json/recipes/recipe_companion.json b/data/json/recipes/recipe_companion.json index b21dba4feb1af..977d051590590 100644 --- a/data/json/recipes/recipe_companion.json +++ b/data/json/recipes/recipe_companion.json @@ -108,5 +108,166 @@ "qualities": [ { "id": "ANVIL", "level": 3 }, { "id": "HAMMER", "level": 5 }, { "id": "CHISEL", "level": 3 } ], "tools": [ [ [ "tongs", -1 ] ], [ [ "swage", -1 ] ], [ [ "forge", 10 ], [ "oxy_torch", 2 ] ] ], "components": [ [ [ "scrap", 3 ] ] ] + }, + { + "type": "recipe", + "activity_level": "fake", + "result": "fish", + "id_suffix": "npc_fish_trap", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "survival", + "difficulty": 4, + "time": "3 h", + "autolearn": false, + "never_learn": true, + "skills_required": [ [ "survival", 4 ] ], + "tools": [ [ [ "fish_trap", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "fake", + "result": "mobile_memory_card", + "id_suffix": "npc_hack", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "computer", + "difficulty": 2, + "time": "60 m", + "autolearn": false, + "never_learn": true, + "skills_required": [ [ "computer", 2 ] ], + "tools": [ [ [ "laptop", 120 ] ] ], + "components": [ [ [ "mobile_memory_card_used", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "fake", + "result": "fish", + "id_suffix": "npc_fish_rod", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "survival", + "difficulty": 4, + "time": "4 h", + "autolearn": false, + "never_learn": true, + "skills_required": [ [ "survival", 4 ] ], + "tools": [ [ [ "fishing_rod_basic", -1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "fake", + "result": "software_useless", + "id_suffix": "npc_hack", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "computer", + "difficulty": 3, + "time": "120 m", + "autolearn": false, + "never_learn": true, + "skills_required": [ [ "computer", 3 ] ], + "tools": [ [ [ "laptop", 240 ] ] ], + "components": [ [ [ "usb_drive", 1 ] ] ], + "container": "usb_drive" + }, + { + "type": "recipe", + "activity_level": "fake", + "result": "fish", + "id_suffix": "npc_fish_rod_pro", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "survival", + "difficulty": 4, + "time": "3 h", + "autolearn": false, + "never_learn": true, + "skills_required": [ [ "survival", 4 ] ], + "tools": [ [ [ "fishing_rod_professional", -1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "fake", + "result": "software_math", + "id_suffix": "npc_hack", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "computer", + "difficulty": 4, + "time": "150 m", + "autolearn": false, + "never_learn": true, + "skills_required": [ [ "computer", 4 ] ], + "tools": [ [ [ "laptop", 300 ] ], [ [ "software_hacking", -1 ] ] ], + "components": [ [ [ "usb_drive", 1 ] ] ], + "container": "usb_drive" + }, + { + "type": "recipe", + "activity_level": "fake", + "result": "mobile_memory_card_science", + "id_suffix": "npc_hack", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "computer", + "difficulty": 5, + "time": "180 m", + "autolearn": false, + "never_learn": true, + "skills_required": [ [ "computer", 5 ] ], + "tools": [ [ [ "laptop", 360 ] ], [ [ "software_hacking", -1 ] ] ], + "components": [ [ [ "mobile_memory_card_used", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "fake", + "result": "software_medical", + "id_suffix": "npc_hack", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "computer", + "difficulty": 5, + "time": "210 m", + "autolearn": false, + "never_learn": true, + "skills_required": [ [ "computer", 5 ] ], + "tools": [ [ [ "laptop", 420 ] ], [ [ "software_hacking", -1 ] ] ], + "components": [ [ [ "usb_drive", 1 ] ] ], + "container": "usb_drive" + }, + { + "type": "recipe", + "activity_level": "fake", + "result": "software_electronics_reference", + "id_suffix": "npc_hack", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "computer", + "difficulty": 6, + "time": "240 m", + "autolearn": false, + "never_learn": true, + "skills_required": [ [ "computer", 6 ] ], + "tools": [ [ [ "laptop", 480 ] ], [ [ "software_hacking", -1 ] ] ], + "components": [ [ [ "usb_drive", 1 ] ] ], + "container": "usb_drive" + }, + { + "type": "recipe", + "activity_level": "fake", + "result": "satellitemap", + "id_suffix": "npc_hack", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "computer", + "difficulty": 8, + "time": "480 m", + "autolearn": false, + "never_learn": true, + "skills_required": [ [ "computer", 8 ] ], + "tools": [ [ [ "laptop", 600 ] ], [ [ "pencil", 50 ] ], [ [ "software_hacking", -1 ] ] ], + "components": [ [ [ "paper", 25 ] ] ] } ] diff --git a/data/json/recipes/recipe_deconstruction.json b/data/json/recipes/recipe_deconstruction.json index dbacbfecddbd1..ccd9c70416174 100644 --- a/data/json/recipes/recipe_deconstruction.json +++ b/data/json/recipes/recipe_deconstruction.json @@ -1415,7 +1415,7 @@ [ [ "gun_module", 3 ] ], [ [ "flamethrower", 1 ] ], [ [ "tazer", 1 ] ], - [ [ "m4a1", 1 ] ], + [ [ "nato_assault_rifle", 1 ] ], [ [ "pamd68", 1 ] ], [ [ "power_supply", 20 ] ], [ [ "amplifier", 5 ] ], @@ -5025,5 +5025,172 @@ "time": "5 m", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "material_rocksalt", 10 ] ] ] + }, + { + "result": "knife_butter", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "12 s", + "qualities": [ { "id": "HAMMER", "level": 1 } ], + "components": [ [ [ "scrap", 1 ] ] ] + }, + { + "result": "flask_hip", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "12 s", + "qualities": [ { "id": "HAMMER", "level": 1 } ], + "components": [ [ [ "scrap", 2 ] ] ] + }, + { + "result": "clamp", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "1 m", + "qualities": [ { "id": "HAMMER", "level": 1 }, { "id": "SAW_M", "level": 1 } ], + "components": [ [ [ "scrap", 6 ] ] ] + }, + { + "result": "e_tool", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "2 m", + "qualities": [ { "id": "SAW_M", "level": 1 } ], + "components": [ [ [ "sheet_metal_small", 2 ] ], [ [ "scrap", 2 ] ] ] + }, + { + "result": "harmonica_holder", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "12 s", + "qualities": [ { "id": "HAMMER", "level": 1 } ], + "components": [ [ [ "scrap", 2 ] ] ] + }, + { + "result": "coin_quarter", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "2 s", + "qualities": [ { "id": "HAMMER", "level": 1 } ], + "components": [ [ [ "silver_small", 5 ] ] ] + }, + { + "result": "coin_nickel", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "2 s", + "qualities": [ { "id": "HAMMER", "level": 1 } ], + "components": [ [ [ "copper", 5 ] ] ] + }, + { + "result": "spray_can", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "2 m", + "qualities": [ { "id": "SAW_M", "level": 1 } ], + "components": [ [ [ "sheet_metal_small", 1 ] ], [ [ "scrap", 1 ] ] ] + }, + { + "result": "can_medium", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "12 s", + "qualities": [ { "id": "HAMMER", "level": 1 } ], + "components": [ [ [ "scrap", 1 ] ] ] + }, + { + "result": "can_food_big", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "1 m", + "qualities": [ { "id": "SAW_M", "level": 1 } ], + "components": [ [ [ "sheet_metal_small", 2 ] ], [ [ "scrap", 3 ] ] ] + }, + { + "result": "multitool", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "40 s", + "qualities": [ { "id": "HAMMER", "level": 1 }, { "id": "SCREW", "level": 1 } ], + "components": [ [ [ "scrap", 3 ] ] ] + }, + { + "result": "thermos", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "2 m", + "qualities": [ { "id": "SAW_M", "level": 1 } ], + "components": [ [ [ "sheet_metal_small", 2 ] ] ] + }, + { + "result": "scissors", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "12 s", + "qualities": [ { "id": "HAMMER", "level": 1 } ], + "components": [ [ [ "scrap", 2 ] ] ] + }, + { + "result": "shavingkit", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "12 s", + "qualities": [ { "id": "HAMMER", "level": 1 } ], + "components": [ [ [ "plastic_chunk", 2 ] ] ] + }, + { + "result": "hammer", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "30 s", + "qualities": [ { "id": "HAMMER", "level": 1 } ], + "components": [ [ [ "splinter", 1 ] ], [ [ "steel_chunk", 2 ] ] ] + }, + { + "result": "tongs", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "2 m", + "qualities": [ { "id": "HAMMER", "level": 1 }, { "id": "SAW_M", "level": 1 } ], + "components": [ [ [ "steel_chunk", 2 ] ] ] + }, + { + "result": "umbrella", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "1 m", + "qualities": [ { "id": "CUT", "level": 1 } ], + "components": [ [ [ "plastic_chunk", 2 ] ] ] + }, + { + "result": "mask_dust", + "type": "uncraft", + "activity_level": "LIGHT_EXERCISE", + "time": "12 s", + "components": [ [ [ "string_6", 1 ] ], [ [ "rag", 1 ] ] ] + }, + { + "result": "horn_bicycle", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "12 s", + "qualities": [ { "id": "CUT", "level": 1 } ], + "components": [ [ [ "plastic_chunk", 3 ] ] ] + }, + { + "result": "cards_magic", + "type": "uncraft", + "activity_level": "NO_EXERCISE", + "time": "2 s", + "components": [ [ [ "paper", 20 ] ] ], + "flags": [ "BLIND_EASY" ] + }, + { + "result": "webbing_belt", + "type": "uncraft", + "activity_level": "LIGHT_EXERCISE", + "time": "30 s", + "qualities": [ { "id": "CUT", "level": 1 } ], + "components": [ [ [ "nylon", 3 ] ], [ [ "scrap", 1 ] ] ] } ] diff --git a/data/json/recipes/recipe_food.json b/data/json/recipes/recipe_food.json index c977930fb06ec..1e9905fab563c 100644 --- a/data/json/recipes/recipe_food.json +++ b/data/json/recipes/recipe_food.json @@ -486,6 +486,19 @@ "qualities": [ { "id": "CONTAIN", "level": 1 } ], "components": [ [ [ "dry_veggy", 1 ] ], [ [ "water_clean", 1 ] ] ] }, + { + "type": "recipe", + "activity_level": "NO_EXERCISE", + "result": "rehydrated_corn_kernels", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_VEGGI", + "skill_used": "cooking", + "time": "3 m", + "autolearn": true, + "flags": [ "BLIND_HARD" ], + "qualities": [ { "id": "CONTAIN", "level": 1 } ], + "components": [ [ [ "dry_corn", 1 ] ], [ [ "water_clean", 1 ] ] ] + }, { "type": "recipe", "activity_level": "NO_EXERCISE", @@ -678,7 +691,9 @@ [ "irradiated_celery", 3 ], [ "veggy_wild", 3 ], [ "veggy", 3 ], - [ "rehydrated_veggy", 3 ] + [ "rehydrated_veggy", 3 ], + [ "rehydrated_corn_kernels", 3 ], + [ "corn_kernels", 3 ] ], [ [ "lettuce", 2 ], @@ -833,7 +848,9 @@ [ "irradiated_celery", 3 ], [ "veggy_wild", 3 ], [ "veggy", 3 ], - [ "rehydrated_veggy", 3 ] + [ "rehydrated_veggy", 3 ], + [ "rehydrated_corn_kernels", 3 ], + [ "corn_kernels", 3 ] ], [ [ "soysauce", 1 ], [ "horseradish", 1 ], [ "salt", 1 ], [ "seasoning_salt", 1 ] ] ] @@ -2284,7 +2301,7 @@ "qualities": [ { "id": "COOK", "level": 2 } ], "tools": [ [ [ "surface_heat", 7, "LIST" ] ] ], "components": [ - [ [ "broth_bone", 1 ] ], + [ [ "gelatin_fresh", 1 ], [ "gelatin_powder", 25 ], [ "chem_agar", 25 ] ], [ [ "meat_red", 1, "LIST" ], [ "meat_cooked", 1 ], @@ -2315,7 +2332,11 @@ "batch_time_factors": [ 50, 5 ], "qualities": [ { "id": "COOK", "level": 2 } ], "tools": [ [ [ "surface_heat", 7, "LIST" ] ] ], - "components": [ [ [ "broth_bone", 1 ] ], [ [ "veggy_any", 4, "LIST" ] ], [ [ "water_clean", 2 ], [ "water", 2 ] ] ] + "components": [ + [ [ "gelatin_fresh", 1 ], [ "gelatin_powder", 25 ], [ "chem_agar", 25 ] ], + [ [ "veggy_any", 4, "LIST" ] ], + [ [ "water_clean", 1 ], [ "water", 1 ] ] + ] }, { "type": "recipe", @@ -2393,6 +2414,7 @@ [ [ "jerky", 2 ], [ "dry_meat", 2 ], [ "meat_smoked", 2 ], [ "dry_fish", 2 ], [ "fish_smoked", 2 ] ], [ [ "dry_veggy", 2 ], + [ "dry_corn", 2 ], [ "dry_fruit", 2 ], [ "dry_mushroom", 2 ], [ "juice_pulp", 4 ], @@ -2470,6 +2492,7 @@ "type": "recipe", "activity_level": "NO_EXERCISE", "result": "broth_bone", + "byproducts": [ [ "gelatin_fresh" ] ], "charges": 1, "category": "CC_FOOD", "subcategory": "CSC_FOOD_OTHER", @@ -2562,6 +2585,22 @@ "tools": [ [ [ "surface_heat", 5, "LIST" ] ] ], "using": [ [ "woods_soup_ingredients", 1 ] ] }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "soup_woods_egg", + "charges": 2, + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_VEGGI", + "skill_used": "cooking", + "difficulty": 3, + "time": "20 m", + "autolearn": true, + "batch_time_factors": [ 80, 4 ], + "qualities": [ { "id": "COOK", "level": 3 } ], + "tools": [ [ [ "surface_heat", 5, "LIST" ] ] ], + "using": [ [ "woods_soup_ingredients_veggy", 1 ] ] + }, { "type": "recipe", "activity_level": "LIGHT_EXERCISE", @@ -3588,6 +3627,71 @@ "tools": [ [ [ "surface_heat", 2, "LIST" ] ] ], "components": [ [ [ "eggs_bird", 2, "LIST" ], [ "egg_reptile", 2 ] ], [ [ "any_butter_or_oil", 2, "LIST" ] ] ] }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "deluxe_fried_eggs", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 2, + "time": "8 m", + "autolearn": true, + "batch_time_factors": [ 80, 2 ], + "qualities": [ { "id": "COOK", "level": 2 } ], + "tools": [ [ [ "surface_heat", 2, "LIST" ] ] ], + "components": [ + [ [ "eggs_bird", 2, "LIST" ], [ "egg_reptile", 2 ] ], + [ [ "any_butter_or_oil", 2, "LIST" ] ], + [ [ "mushroom", 1 ], [ "mushroom_morel", 1 ], [ "garlic_clove", 1 ], [ "onion", 1 ] ], + [ [ "cheese", 1 ], [ "cheese_hard", 1 ], [ "can_cheese", 1 ], [ "veggy_green", 1, "LIST" ] ], + [ [ "salt_preservation", 2, "LIST" ] ], + [ [ "condiment", 2, "LIST" ] ] + ] + }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "fried_egg_sandwich", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 2, + "time": "6 m", + "autolearn": true, + "batch_time_factors": [ 80, 2 ], + "qualities": [ { "id": "COOK", "level": 2 } ], + "tools": [ [ [ "surface_heat", 2, "LIST" ] ] ], + "components": [ + [ [ "eggs_bird", 2, "LIST" ], [ "egg_reptile", 2 ] ], + [ [ "any_butter_or_oil", 2, "LIST" ] ], + [ [ "bread_sandwich", 2, "LIST" ] ], + [ [ "condiment", 2, "LIST" ] ] + ] + }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "deluxe_fried_egg_sandwich", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 2, + "time": "8 m", + "autolearn": true, + "batch_time_factors": [ 80, 2 ], + "qualities": [ { "id": "COOK", "level": 2 } ], + "tools": [ [ [ "surface_heat", 2, "LIST" ] ] ], + "components": [ + [ [ "eggs_bird", 2, "LIST" ], [ "egg_reptile", 2 ] ], + [ [ "any_butter_or_oil", 2, "LIST" ] ], + [ [ "mushroom", 1 ], [ "mushroom_morel", 1 ], [ "garlic_clove", 1 ], [ "onion", 1 ] ], + [ [ "cheese", 1 ], [ "cheese_hard", 1 ], [ "can_cheese", 1 ], [ "veggy_green", 1, "LIST" ] ], + [ [ "salt_preservation", 2, "LIST" ] ], + [ [ "condiment", 2, "LIST" ] ], + [ [ "bread_sandwich", 2, "LIST" ] ] + ] + }, { "type": "recipe", "activity_level": "NO_EXERCISE", @@ -3620,6 +3724,8 @@ [ [ "bacon", 2 ], [ "meat_cooked", 1 ], + [ "human_cooked", 1 ], + [ "demihuman_cooked", 1 ], [ "mutant_meat_cooked", 1 ], [ "meat_smoked", 1 ], [ "dry_meat", 1 ], @@ -3662,6 +3768,8 @@ [ [ "bacon", 2 ], [ "meat_cooked", 1 ], + [ "human_cooked", 1 ], + [ "demihuman_cooked", 1 ], [ "mutant_meat_cooked", 1 ], [ "meat_smoked", 1 ], [ "dry_meat", 1 ], @@ -3984,8 +4092,11 @@ [ "mustard_powder", 20 ], [ "garlic_clove", 6 ], [ "rehydrated_veggy", 1 ], + [ "rehydrated_corn_kernels", 1 ], + [ "corn_kernels", 1 ], [ "dry_mushroom", 1 ], - [ "dry_veggy", 1 ] + [ "dry_veggy", 1 ], + [ "dry_corn", 1 ] ] ] }, @@ -4108,6 +4219,9 @@ [ "veggy_salted", 2 ], [ "rehydrated_veggy", 2 ], [ "dry_veggy", 2 ], + [ "rehydrated_corn_kernels", 2 ], + [ "dry_corn", 2 ], + [ "corn_kernels", 2 ], [ "mushroom", 2 ], [ "dry_mushroom", 2 ], [ "morel_cooked", 2 ], @@ -4144,6 +4258,9 @@ [ "veggy_salted", 2 ], [ "rehydrated_veggy", 2 ], [ "dry_veggy", 2 ], + [ "rehydrated_corn_kernels", 2 ], + [ "dry_corn", 2 ], + [ "corn_kernels", 2 ], [ "mushroom", 2 ], [ "dry_mushroom", 2 ], [ "morel_cooked", 2 ], @@ -4181,11 +4298,14 @@ [ "veggy_wild", 2 ], [ "veggy_salted", 2 ], [ "rehydrated_veggy", 2 ], + [ "rehydrated_corn_kernels", 2 ], + [ "corn_kernels", 2 ], [ "mushroom", 2 ], [ "dry_mushroom", 2 ], [ "morel_cooked", 2 ], [ "mushroom_cooked", 2 ], - [ "dry_veggy", 2 ] + [ "dry_veggy", 2 ], + [ "dry_corn", 2 ] ], [ [ "tomato", 1 ], [ "irradiated_tomato", 1 ], [ "can_tomato", 1 ] ], [ [ "meat_red", 1, "LIST" ], [ "dry_meat", 1 ], [ "can_chicken", 1 ] ], @@ -4215,7 +4335,8 @@ [ "tofu", 2 ], [ "dry_tofu", 2 ], [ "mushroom_cooked", 2 ], - [ "dry_veggy", 2 ] + [ "dry_veggy", 2 ], + [ "dry_corn", 2 ] ], [ [ "sauce_pesto", 1 ], @@ -4263,7 +4384,8 @@ [ "dry_mushroom", 2 ], [ "morel_cooked", 2 ], [ "mushroom_cooked", 2 ], - [ "dry_veggy", 2 ] + [ "dry_veggy", 2 ], + [ "dry_corn", 2 ] ], [ [ "cheese", 2 ], [ "can_cheese", 2 ], [ "cheese_hard", 2 ] ], [ [ "sauce_pesto", 1 ], [ "sauce_red", 1 ], [ "seasoning_italian", 5 ], [ "wild_herbs", 10 ] ], @@ -4404,7 +4526,8 @@ [ "coffee_syrup", 5 ], [ "cola", 6 ], [ "con_milk", 1 ], - [ "corn", 3 ], + [ "corn_kernels", 3 ], + [ "rehydrated_corn_kernels", 3 ], [ "honey_bottled", 1 ], [ "honey_glassed", 1 ], [ "honeycomb", 1 ], @@ -4625,7 +4748,7 @@ "autolearn": true, "batch_time_factors": [ 83, 3 ], "tools": [ [ [ "surface_heat", 5, "LIST" ] ], [ [ "rock_quern", -1 ], [ "clay_quern", -1 ] ] ], - "components": [ [ [ "corn", 1 ], [ "irradiated_corn", 1 ], [ "kernels", 1 ] ] ] + "components": [ [ [ "dry_corn", 1 ], [ "kernels", 1 ] ] ] }, { "type": "recipe", @@ -4908,6 +5031,8 @@ [ "irradiated_tomato", 1 ], [ "irradiated_broccoli", 1 ], [ "rehydrated_veggy", 1 ], + [ "rehydrated_corn_kernels", 1 ], + [ "corn_kernels", 1 ], [ "morel_cooked", 1 ], [ "mushroom_cooked", 1 ], [ "sauerkraut_onions", 1 ], @@ -5277,6 +5402,20 @@ "book_learn": [ [ "mag_glam", 1 ] ], "components": [ [ [ "bread_sandwich", 1, "LIST" ] ], [ [ "any_butter", 1, "LIST" ] ], [ [ "sprinkles", 3 ] ] ] }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "toastem4", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_SNACK", + "skill_used": "cooking", + "time": "40 m", + "charges": 6, + "book_learn": [ [ "baking_book", 1 ] ], + "qualities": [ { "id": "CUT", "level": 1 }, { "id": "COOK", "level": 2 } ], + "tools": [ [ [ "surface_heat", 20, "LIST" ] ] ], + "components": [ [ [ "flour", 12 ] ], [ [ "eggs_bird", 1, "LIST" ] ], [ [ "jam_fruit", 9 ] ], [ [ "cornmeal", 1 ] ] ] + }, { "type": "recipe", "activity_level": "LIGHT_EXERCISE", @@ -5290,6 +5429,45 @@ "tools": [ [ [ "surface_heat", 10, "LIST" ] ] ], "components": [ [ [ "toasterpastryfrozen", 1 ] ] ] }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "homemade_toasterpastry", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_SNACK", + "skill_used": "cooking", + "time": "2 m 30 s", + "charges": 1, + "autolearn": true, + "tools": [ [ [ "surface_heat", 10, "LIST" ] ] ], + "components": [ [ [ "toastem4", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "homemade_toasterpastry2", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_SNACK", + "skill_used": "cooking", + "time": "2 m 30 s", + "charges": 1, + "autolearn": true, + "tools": [ [ [ "surface_heat", 10, "LIST" ] ] ], + "components": [ [ [ "toastem4", 1 ] ], [ [ "buttercream", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "buttercream", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_SNACK", + "skill_used": "cooking", + "difficulty": 1, + "time": "6 m 30 s", + "autolearn": true, + "qualities": [ { "id": "COOK", "level": 1 } ], + "components": [ [ [ "milk_cream", 1 ] ], [ [ "butter", 16 ] ], [ [ "sugar_standard", 1, "LIST" ] ] ] + }, { "type": "recipe", "activity_level": "LIGHT_EXERCISE", @@ -5828,6 +6006,8 @@ [ "irradiated_tomato", 1 ], [ "irradiated_broccoli", 1 ], [ "rehydrated_veggy", 1 ], + [ "rehydrated_corn_kernels", 1 ], + [ "corn_kernels", 1 ], [ "morel_cooked", 1 ], [ "mushroom_cooked", 1 ], [ "sauerkraut_onions", 1 ], @@ -5906,6 +6086,21 @@ "autolearn": true, "components": [ [ [ "bread_sandwich", 2, "LIST" ] ], [ [ "jam_fruit", 1 ] ] ] }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "sandwich_jam_cheese", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_VEGGI", + "skill_used": "cooking", + "time": "1 m", + "autolearn": true, + "components": [ + [ [ "bread_sandwich", 2, "LIST" ] ], + [ [ "jam_fruit", 1 ] ], + [ [ "cheese", 1 ], [ "cheese_hard", 1 ], [ "can_cheese", 1 ] ] + ] + }, { "type": "recipe", "activity_level": "LIGHT_EXERCISE", @@ -6413,7 +6608,20 @@ "autolearn": true, "batch_time_factors": [ 83, 3 ], "tools": [ [ [ "food_processor", 20 ] ] ], - "components": [ [ [ "corn", 1 ], [ "irradiated_corn", 1 ], [ "kernels", 1 ] ] ] + "components": [ [ [ "dry_corn", 1 ] ] ] + }, + { + "result": "corn_kernels", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "byproducts": [ [ "empty_corn_cob", 1 ] ], + "skill_used": "cooking", + "time": "20 s", + "autolearn": true, + "qualities": [ { "id": "CUT", "level": 1 } ], + "components": [ [ [ "corn", 1 ], [ "irradiated_corn", 1 ] ] ] }, { "result": "flour", @@ -6511,7 +6719,7 @@ "components": [ [ [ "can_corn", 1 ], - [ "corn", 1 ], + [ "corn_kernels", 1 ], [ "oats", 1 ], [ "buckwheat", 1 ], [ "wheat", 1 ], @@ -6749,7 +6957,7 @@ "autolearn": true, "batch_time_factors": [ 83, 3 ], "tools": [ [ [ "surface_heat", 5, "LIST" ] ], [ [ "mortar_pestle", -1 ] ] ], - "components": [ [ [ "corn", 1 ], [ "irradiated_corn", 1 ], [ "kernels", 1 ] ] ] + "components": [ [ [ "dry_corn", 1 ] ] ] }, { "type": "recipe", @@ -7413,6 +7621,20 @@ ] ] }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "mycus_juice", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_DRINKS", + "skill_used": "cooking", + "difficulty": 1, + "time": "5 m", + "autolearn": true, + "qualities": [ { "id": "HAMMER", "level": 1 }, { "id": "CONTAIN", "level": 1 } ], + "tools": [ [ [ "rag", -1 ] ] ], + "components": [ [ [ "mycus_fruit", 1 ] ], [ [ "water", 1 ], [ "water_clean", 1 ] ] ] + }, { "type": "recipe", "activity_level": "NO_EXERCISE", @@ -7477,5 +7699,46 @@ [ [ "sugar", 1 ], [ "artificial_sweetener", 1 ] ], [ [ "salt", 1 ], [ "seasoning_salt", 1 ] ] ] + }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "meatball_raw", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_MEAT", + "skill_used": "cooking", + "difficulty": 4, + "time": "10 m", + "charges": 6, + "book_learn": [ [ "family_cookbook", 2 ], [ "cookbook_italian", 2 ] ], + "batch_time_factors": [ 50, 3 ], + "qualities": [ { "id": "CUT", "level": 1 }, { "id": "COOK", "level": 1 } ], + "components": [ + [ [ "meat_red", 2, "LIST" ] ], + [ [ "bread", 1 ] ], + [ + [ "salt", 2 ], + [ "soysauce", 2 ], + [ "seasoning_italian", 2 ], + [ "wild_herbs", 2 ], + [ "seasoning_salt", 2 ], + [ "pepper", 2 ] + ] + ] + }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "meatball", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_MEAT", + "skill_used": "cooking", + "time": "15 m", + "autolearn": true, + "batch_time_factors": [ 67, 5 ], + "qualities": [ { "id": "COOK", "level": 1 } ], + "tools": [ [ [ "surface_heat", 2, "LIST" ] ] ], + "charges": 1, + "components": [ [ [ "meatball_raw", 1 ] ] ] } ] diff --git a/data/json/recipes/recipe_obsolete.json b/data/json/recipes/recipe_obsolete.json index 40c0171aa442e..9c56edb33c045 100644 --- a/data/json/recipes/recipe_obsolete.json +++ b/data/json/recipes/recipe_obsolete.json @@ -2752,6 +2752,12 @@ "result": "mp3", "obsolete": true }, + { + "type": "recipe", + "result": "blindfold", + "id_suffix": "from_tape", + "obsolete": true + }, { "type": "recipe", "result": "towel", @@ -2771,5 +2777,10 @@ "type": "recipe", "result": "rifle_223", "obsolete": true + }, + { + "type": "recipe", + "result": "bodypillow", + "obsolete": true } ] diff --git a/data/json/recipes/recipe_others.json b/data/json/recipes/recipe_others.json index a5e8d67f6915f..4b7b2040d3ea0 100644 --- a/data/json/recipes/recipe_others.json +++ b/data/json/recipes/recipe_others.json @@ -11,7 +11,7 @@ "autolearn": false, "book_learn": [ [ "concrete_book", 4 ] ], "tools": [ [ [ "con_mix", 50 ] ] ], - "components": [ [ [ "material_cement", 50 ] ], [ [ "material_sand", 25 ] ], [ [ "pebble", 20 ] ] ] + "components": [ [ [ "material_cement", 50 ] ], [ [ "material_sand", 25 ] ], [ [ "pebble", 20 ], [ "material_gravel", 100 ] ] ] }, { "type": "recipe", @@ -56,7 +56,7 @@ { "type": "recipe", "activity_level": "LIGHT_EXERCISE", - "result": "bodypillow", + "result": "bodypillow_makeshift", "category": "CC_OTHER", "subcategory": "CSC_OTHER_OTHER", "skill_used": "tailor", diff --git a/data/json/recipes/tools/tool.json b/data/json/recipes/tools/tool.json index ad202253cda2e..a945dda73456b 100644 --- a/data/json/recipes/tools/tool.json +++ b/data/json/recipes/tools/tool.json @@ -548,7 +548,7 @@ "components": [ [ [ "rock", 2 ] ], [ [ "water", 1 ], [ "water_clean", 1 ], [ "water_sewage", 1 ], [ "salt_water", 1 ], [ "saline", 5 ] ], - [ [ "material_sand", 1 ] ] + [ [ "material_sand", 1 ], [ "material_gravel", 1 ] ] ] }, { diff --git a/data/json/recipes/tools/tools_primitive.json b/data/json/recipes/tools/tools_primitive.json index d3bf2cbebbe77..c1774b7b84c37 100644 --- a/data/json/recipes/tools/tools_primitive.json +++ b/data/json/recipes/tools/tools_primitive.json @@ -15,7 +15,7 @@ [ [ "stick", 1 ], [ "2x4", 1 ] ], [ [ "rock", 1 ] ], [ [ "water", 1 ], [ "water_clean", 1 ], [ "water_sewage", 1 ], [ "salt_water", 1 ], [ "saline", 5 ] ], - [ [ "material_sand", 1 ] ], + [ [ "material_sand", 1 ], [ "material_gravel", 1 ] ], [ [ "cordage_short", 1, "LIST" ], [ "filament", 50, "LIST" ] ] ] }, @@ -135,7 +135,7 @@ [ [ "stick", 1 ], [ "2x4", 1 ] ], [ [ "rock", 1 ] ], [ [ "water", 1 ], [ "water_clean", 1 ], [ "water_sewage", 1 ], [ "salt_water", 1 ], [ "saline", 5 ] ], - [ [ "material_sand", 1 ] ] + [ [ "material_sand", 1 ], [ "material_gravel", 1 ] ] ] }, { @@ -180,7 +180,7 @@ [ [ "stick", 1 ], [ "2x4", 1 ] ], [ [ "rock_large", 2 ] ], [ [ "water", 1 ], [ "water_clean", 1 ], [ "water_sewage", 1 ], [ "salt_water", 1 ], [ "saline", 5 ] ], - [ [ "material_sand", 1 ] ] + [ [ "material_sand", 1 ], [ "material_gravel", 1 ] ] ] }, { @@ -279,7 +279,7 @@ "components": [ [ [ "rock", 1 ] ], [ [ "water", 1 ], [ "water_clean", 1 ], [ "water_sewage", 1 ], [ "salt_water", 1 ], [ "saline", 5 ] ], - [ [ "material_sand", 1 ] ] + [ [ "material_sand", 1 ], [ "material_gravel", 1 ] ] ] }, { diff --git a/data/json/recipes/weapon/bashing.json b/data/json/recipes/weapon/bashing.json index 4685b92cd7477..67d8c55f7ec8d 100644 --- a/data/json/recipes/weapon/bashing.json +++ b/data/json/recipes/weapon/bashing.json @@ -258,6 +258,28 @@ "qualities": [ { "id": "HAMMER", "level": 1 }, { "id": "SAW_M", "level": 1 } ], "components": [ [ [ "rag", 4 ] ], [ [ "scrap", 4 ] ] ] }, + { + "type": "recipe", + "activity_level": "BRISK_EXERCISE", + "result": "knuckle_steel_forged", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_BASHING", + "skill_used": "fabrication", + "skills_required": [ "bashing", 1 ], + "proficiencies": [ + { "proficiency": "prof_metalworking" }, + { "proficiency": "prof_blacksmithing" }, + { "proficiency": "prof_toolsmithing" } + ], + "difficulty": 3, + "time": "1 h", + "autolearn": true, + "book_learn": [ [ "recipe_melee", 2 ] ], + "using": [ [ "blacksmithing_standard", 4 ] ], + "qualities": [ { "id": "CHISEL", "level": 3 } ], + "tools": [ [ [ "swage", -1 ] ] ], + "components": [ [ [ "steel_chunk", 1 ], [ "scrap", 5 ] ] ] + }, { "type": "recipe", "activity_level": "LIGHT_EXERCISE", diff --git a/data/json/recipes/weapon/cutting.json b/data/json/recipes/weapon/cutting.json index 2e403db2c9bd0..a7b30c2414ba6 100644 --- a/data/json/recipes/weapon/cutting.json +++ b/data/json/recipes/weapon/cutting.json @@ -132,6 +132,7 @@ "type": "recipe", "activity_level": "LIGHT_EXERCISE", "result": "makeshift_halberd", + "//": "this makes the simple makeshift glaive, but changing the id would break e.g. tilesets.", "category": "CC_WEAPON", "subcategory": "CSC_WEAPON_CUTTING", "skill_used": "fabrication", @@ -139,7 +140,31 @@ "reversible": true, "autolearn": true, "proficiencies": [ { "proficiency": "prof_carving", "time_multiplier": 1.5, "fail_multiplier": 1 } ], - "components": [ [ [ "duct_tape", 100 ] ], [ [ "blade", 1 ] ], [ [ "stick_long", 1 ] ] ] + "components": [ + [ [ "duct_tape", 100 ] ], + [ [ "blade", 1 ], [ "knife_meat_cleaver", 1 ], [ "knife_vegetable_cleaver", 1 ] ], + [ [ "stick_long", 1 ] ] + ] + }, + { + "type": "recipe", + "activity_level": "MODERATE_EXERCISE", + "result": "makeshift_glaive", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_PIERCING", + "skill_used": "fabrication", + "difficulty": 1, + "time": "45 m", + "reversible": true, + "autolearn": true, + "qualities": [ { "id": "CUT", "level": 1 }, { "id": "HAMMER", "level": 1 }, { "id": "DRILL", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_carving", "time_multiplier": 1.5, "fail_multiplier": 1.5 } ], + "components": [ + [ [ "duct_tape", 100 ] ], + [ [ "blade", 1 ], [ "knife_meat_cleaver", 1 ], [ "knife_vegetable_cleaver", 1 ] ], + [ [ "stick_long", 1 ] ], + [ [ "cordage", 2, "LIST" ] ] + ] }, { "type": "recipe", diff --git a/data/json/recipes/weapon/magazines.json b/data/json/recipes/weapon/magazines.json index ab9ec93e9c99f..319728e6bef0b 100644 --- a/data/json/recipes/weapon/magazines.json +++ b/data/json/recipes/weapon/magazines.json @@ -120,18 +120,7 @@ "time": "20 m", "autolearn": true, "tools": [ - [ - [ "acr", -1 ], - [ "ar15", -1 ], - [ "h&k416a5", -1 ], - [ "m249", -1 ], - [ "m27iar", -1 ], - [ "m4a1", -1 ], - [ "m16a4", -1 ], - [ "scar_l", -1 ], - [ "sig552", -1 ], - [ "surv_carbine_223", -1 ] - ], + [ [ "nato_assault_rifle", -1 ], [ "ar15", -1 ], [ "m249", -1 ], [ "m16a4", -1 ], [ "surv_carbine_223", -1 ] ], [ [ "small_repairkit", 10 ], [ "large_repairkit", 5 ] ] ], "using": [ [ "223_casehead", 1 ] ], diff --git a/data/json/recipes/weapon/mods.json b/data/json/recipes/weapon/mods.json index 563c84c5c30a1..5249ee41f0b89 100644 --- a/data/json/recipes/weapon/mods.json +++ b/data/json/recipes/weapon/mods.json @@ -963,9 +963,7 @@ [ "ar15_retool_300blk", -1 ], [ "ar_pistol", -1 ], [ "oa93", -1 ], - [ "h&k416a5", -1 ], - [ "m27iar", -1 ], - [ "m4a1", -1 ], + [ "nato_assault_rifle", -1 ], [ "m16a4", -1 ] ], [ [ "small_repairkit", 40 ], [ "large_repairkit", 40 ] ] diff --git a/data/json/recipes/weapon/piercing.json b/data/json/recipes/weapon/piercing.json index 074f0d0eb2334..de7694d96f5ff 100644 --- a/data/json/recipes/weapon/piercing.json +++ b/data/json/recipes/weapon/piercing.json @@ -615,9 +615,6 @@ [ "knife_butcher", 1 ], [ "knife_chef", 1 ], [ "knife_carving", 1 ], - [ "knife_vegetable_cleaver", 1 ], - [ "knife_meat_cleaver", 1 ], - [ "blade", 1 ], [ "knife_combat", 1 ], [ "knife_combat_mod", 1 ], [ "knife_hunting", 1 ], @@ -650,9 +647,6 @@ [ "knife_butcher", 1 ], [ "knife_chef", 1 ], [ "knife_carving", 1 ], - [ "knife_vegetable_cleaver", 1 ], - [ "knife_meat_cleaver", 1 ], - [ "blade", 1 ], [ "knife_combat", 1 ], [ "knife_combat_mod", 1 ], [ "knife_hunting", 1 ], diff --git a/data/json/regional_map_settings.json b/data/json/regional_map_settings.json index 399068e0aabf0..6433a28fa34b2 100644 --- a/data/json/regional_map_settings.json +++ b/data/json/regional_map_settings.json @@ -391,6 +391,7 @@ "mx_grass": 20, "mx_fallen_shed": 30, "mx_spider": 200, + "mx_nest_wasp": 100, "mx_grove": 500, "mx_shrubbery": 500, "mx_clearcut": 125, @@ -419,6 +420,7 @@ "mx_fallen_shed": 20, "mx_shia": 1, "mx_spider": 200, + "mx_nest_wasp": 100, "mx_jabberwock": 1, "mx_grove": 500, "mx_shrubbery": 500, @@ -474,7 +476,7 @@ "mx_point_burned_ground": 50, "mx_casings": 20, "mx_corpses": 3, - "mx_nest_wasp": 2, + "mx_nest_wasp": 5, "mx_mass_grave": 5, "mx_grave": 5 } diff --git a/data/json/requirements/cooking_components.json b/data/json/requirements/cooking_components.json index 296049cfb5ae5..1e314b25eca32 100644 --- a/data/json/requirements/cooking_components.json +++ b/data/json/requirements/cooking_components.json @@ -30,7 +30,8 @@ [ "wastebread", 1 ], [ "sourdough_bread", 1 ], [ "biscuit", 1 ], - [ "brioche", 1 ] + [ "brioche", 1 ], + [ "crackers", 1 ] ] ] }, @@ -71,7 +72,8 @@ [ "egg_locust", 4 ], [ "egg_dragonfly", 4 ], [ "egg_firefly", 4 ], - [ "egg_centipede", 2 ] + [ "egg_centipede", 2 ], + [ "egg_wasp", 4 ] ] ] }, @@ -382,6 +384,8 @@ [ "can_corn", 1 ], [ "hominy", 1 ], [ "dry_veggy", 1 ], + [ "dry_corn", 1 ], + [ "corn_kernels", 1 ], [ "con_milk", 1 ], [ "flatbread", 1 ], [ "tortilla_corn", 1 ], @@ -439,16 +443,46 @@ [ "can_tuna", 1 ], [ "sausage", 1 ], [ "sausage_cooked", 1 ], - [ "bratwurst_sausage", 1 ], - [ "powder_eggs", 2 ], - [ "eggs_bird", 2, "LIST" ], - [ "egg_reptile", 2 ] + [ "bratwurst_sausage", 1 ] ], + [ + [ "veggy_wild", 2 ], + [ "veggy", 2 ], + [ "corn_kernels", 2 ], + [ "rehydrated_veggy", 2 ], + [ "rehydrated_corn_kernels", 2 ], + [ "dry_veggy", 2 ], + [ "dry_corn", 2 ], + [ "dry_beans", 2 ], + [ "can_beans", 2 ], + [ "raw_beans", 2 ], + [ "beans_cooked", 2 ], + [ "dry_lentils", 2 ], + [ "raw_lentils", 2 ], + [ "dry_rice", 2 ], + [ "dandelion_cooked", 2 ], + [ "burdock_cooked", 2 ], + [ "mushroom", 2 ], + [ "dry_mushroom", 2 ], + [ "mushroom_cooked", 2 ], + [ "morel_cooked", 2 ], + [ "acorns_cooked", 2 ] + ] + ] + }, + { + "id": "woods_soup_ingredients_veggy", + "type": "requirement", + "components": [ + [ [ "broth", 2 ], [ "broth_bone", 2 ], [ "pine_tea", 2 ] ], + [ [ "powder_eggs", 2 ], [ "eggs_bird", 2, "LIST" ], [ "egg_reptile", 2 ] ], [ [ "veggy_wild", 2 ], [ "veggy", 2 ], [ "rehydrated_veggy", 2 ], + [ "rehydrated_corn_kernels", 2 ], [ "dry_veggy", 2 ], + [ "dry_corn", 2 ], [ "dry_beans", 2 ], [ "can_beans", 2 ], [ "raw_beans", 2 ], @@ -538,6 +572,23 @@ ] ] }, + { + "id": "condiment", + "type": "requirement", + "//": " Should be about a teaspoon, or smallest serving", + "components": [ + [ + [ "mayonnaise", 1 ], + [ "mustard", 1 ], + [ "soysauce", 1 ], + [ "hot_sauce", 1 ], + [ "ketchup", 1 ], + [ "horseradish", 1 ], + [ "sauerkraut", 1 ], + [ "butter", 1 ] + ] + ] + }, { "id": "salt_preservation", "type": "requirement", @@ -665,6 +716,8 @@ [ "raw_dandelion", 1 ], [ "raw_burdock", 1 ], [ "rehydrated_veggy", 1 ], + [ "rehydrated_corn_kernels", 1 ], + [ "corn_kernels", 1 ], [ "veggy", 1 ], [ "veggy_wild", 1 ], [ "veggy_salted", 1 ], @@ -675,7 +728,7 @@ { "id": "veggy_any_uncooked", "type": "requirement", - "components": [ [ [ "rehydrated_veggy", 1 ], [ "veggy_any_fresh_uncooked", 1, "LIST" ] ] ] + "components": [ [ [ "rehydrated_veggy", 1 ], [ "rehydrated_corn_kernels", 1 ], [ "veggy_any_fresh_uncooked", 1, "LIST" ] ] ] }, { "id": "veggy_any_fresh_uncooked", @@ -691,8 +744,7 @@ [ "cattail_stalk", 1 ], [ "celery", 1 ], [ "irradiated_celery", 1 ], - [ "corn", 1 ], - [ "irradiated_corn", 1 ], + [ "corn_kernels", 1 ], [ "cucumber", 1 ], [ "irradiated_cucumber", 1 ], [ "lettuce", 1 ], @@ -750,6 +802,8 @@ "components": [ [ [ "veggy_any", 1, "LIST" ], + [ "empty_corn_cob", 1 ], + [ "corn", 1 ], [ "powder_eggs", 1 ], [ "eggs_bird", 1, "LIST" ], [ "egg_reptile", 1 ], @@ -831,5 +885,38 @@ [ "mixed_alcohol_weak", 1 ] ] ] + }, + { + "id": "soda_pop", + "type": "requirement", + "//": "Soda's and such. Anything carbonated that isn't alcholic or an energy drink.", + "components": [ + [ + [ "cola", 1 ], + [ "creamsoda", 1 ], + [ "crispycran", 1 ], + [ "lemonlime", 1 ], + [ "orangesoda", 1 ], + [ "purple_drink", 1 ], + [ "rootbeer", 1 ], + [ "spezi", 1 ] + ] + ] + }, + { + "id": "sweet_juice", + "type": "requirement", + "//": "Non-carbonated, sweet fruit based drinks.", + "components": [ + [ + [ "oj", 1 ], + [ "lemonade", 1 ], + [ "apple_cider", 1 ], + [ "cranberry_juice", 1 ], + [ "juice", 1 ], + [ "juice_pasteurized", 1 ], + [ "kompot", 1 ] + ] + ] } ] diff --git a/data/json/scenarios.json b/data/json/scenarios.json index f368d81b3c728..81a954e6e2a16 100644 --- a/data/json/scenarios.json +++ b/data/json/scenarios.json @@ -95,6 +95,7 @@ "sloc_horse_ranch", "sloc_lighthouse_ground", "sloc_cabin_lake", + "sloc_lodge_ground", "sloc_freshwater_research_station" ], "start_name": "Safe Building", @@ -283,6 +284,16 @@ "allowed_locs": [ "sloc_lab_random", "sloc_lab_escape_cells", "sloc_lab_finale", "sloc_ice_lab_stairs", "sloc_ice_lab_finale" ], "flags": [ "CHALLENGE", "CITY_START", "LONE_START" ] }, + { + "type": "scenario", + "id": "bordered", + "name": "Challenge - Bordered", + "points": -2, + "description": "You have survived the initial wave of panic, and have achieved (relative) safety in one of the many government evac shelters. The only thing that really bothers you is a mysterious sky-high wall seen in the distance.", + "allowed_locs": [ "sloc_shelter_a", "sloc_shelter_b", "sloc_shelter_c" ], + "start_name": "Evac Shelter", + "flags": [ "CHALLENGE", "CITY_START", "BORDERED" ] + }, { "type": "scenario", "id": "ambushed", @@ -296,6 +307,7 @@ "sloc_hermit_shack", "sloc_farm_survivalist", "sloc_campsite", + "sloc_lodge_ground", "sloc_campground" ], "start_name": "Outside Town", @@ -314,6 +326,7 @@ "sloc_field", "sloc_forest", "sloc_cabin", + "sloc_lodge_ground", "sloc_hermit_shack", "sloc_farm_survivalist", "sloc_campsite", diff --git a/data/json/snippets/epilogue_factions.json b/data/json/snippets/epilogue_factions.json index 3245828e9ca9b..50cddf0617156 100644 --- a/data/json/snippets/epilogue_factions.json +++ b/data/json/snippets/epilogue_factions.json @@ -47,6 +47,14 @@ { "id": "epilogue_faction_hells_raiders_150", "text": " Fueled by drugs and rage, the Hell's Raiders fought tooth and nail to overthrow the last strongholds of the Old Guard. The costly victories brought the warlords abundant territory and slaves but little in the way of stability. Within weeks, infighting led to civil war as tribes vied for leadership of the faction. When only one warlord finally secured control, there was nothing left to fight for… just endless cities full of the dead." + }, + { + "id": "epilogue_faction_robofac_0", + "text": " Despite Melchior's enlightened leadership, shortages and crises plagued Hub-01 from the first day of the Cataclysm. The researchers and administrators among the surviving staff lacked the practical skills to survive in the savage new world. As Hub-01's systems collapsed from a lack of maintenance, the few survivors fled the building to be killed by the zombies and bandits." + }, + { + "id": "epilogue_faction_robofac_150", + "text": " Melchior's enlightened leadership, combined with practical skills provided by mercenary survivors, allowed Hub-01 to flourish briefly. It became a mecca for advanced technology, selling off devices and equipment that could no longer be reproduced. Despite this, Hub-01 was never able to expand sufficiently to renew its own population, nor was it able to unite with any other prosperous community, nor could enough trustworthy mercenaries (or staff) be recruited to replace those who died in its defense. Hub-01 fell into disrepair and ruin. After the air processing facility was overtaken by mold, the remaining staff of Hub-01 fled the building and were killed by zombies and bandits. Melchior's drones - possibly by design - were incapable of maintaining themselves without human hands, and stopped working a few months later. Without regular maintenance, Melchior's own systems also fell to ruin, and less than a decade after the Cataclysm, humanity's first AI powered down and was forgotten." } ] } diff --git a/data/json/snippets/grave_labels.json b/data/json/snippets/grave_labels.json index 90f824cb5bc48..ae3c7bfff0d72 100644 --- a/data/json/snippets/grave_labels.json +++ b/data/json/snippets/grave_labels.json @@ -15,7 +15,23 @@ "We will always remember you", "This was all my fault", "This was all our fault", - "I hope you feel better up there" + "I hope you feel better up there", + "You won't suffer no more", + "You were the best of us", + "And when the earth shall claim your limbs, then shall you truly dance", + "The song is ended but the melody lingers on", + "Step softly, a dream lies buried here", + "Miss you Mom", + "Miss you Dad", + "May you never walk again, may I not kill you again", + "Vita mutatur non tollitur", + "Let your hunger calm down", + "The child is the father of the man", + "Now we learned that death is a gift", + "I held you your whole life", + "So small, so sweet, so soon", + "Ours for a little while", + "You died for us, we'll live for you" ] } ] diff --git a/data/json/snippets/lab.json b/data/json/snippets/lab.json index 05eb5d8b55c9b..7b1deb77238c3 100644 --- a/data/json/snippets/lab.json +++ b/data/json/snippets/lab.json @@ -122,7 +122,9 @@ "Jakobson was killed today by one of S37ZBE's subjects; ironic considering how hard he fought to keep the project active. Alarmingly, his corpse revivified immediately. This suggests that XE037 may have contaminated the lab at large. Even more alarmingly, we received an alert from Dr. Dionne in XEDRA-12 that they have detected similar signs of cross-contamination with XE037. How can this have happened? Even during the breach last week, we managed to keep containment protocols active!", "Termination of a subject which was never a part of S37ZBE has confirmed my fears. XE037 has contaminated most, if not all of the laboratory. We're in communication with XEDRA-12 over their own outbreak, and both labs have been quarantined. While they backtrace the leak, we will start research into a process to destroy XE037 within the human body.", "Dr. Takatoshi sent us interesting news: her lab serendipitously discovered that sufficient rapid teleports in a short span of time can strip out XE037 somehow, without harming the subject. This has the unfortunate issues of needing an absolutely insane amount of electricity, and drawing the attention of what Dr. Takatoshi calls \"subplanar creatures\" and the rest of us call \"horrifying alien monstrosities\". Still, it's a start.", - "Dr. Sidhu figured out a way to track what happens to XE037 when it's stripped out during teleportation, using a high-affinity biotracer and a probe that follows immediately behind the test subject. XE037 migrates out of the body in small but significant quantities with every teleport. We're attributing way too much intelligence to it here, but we can't help but feel this is some kind of intentional 'stowaway' effect, like it's shedding off the body to try to colonize a new dimension. At the meeting, Dr. Sidhu told us to stop anthropomorphizing it. He thinks it's no more a sign of intelligence than a cold virus coming out with a sneeze." + "Dr. Sidhu figured out a way to track what happens to XE037 when it's stripped out during teleportation, using a high-affinity biotracer and a probe that follows immediately behind the test subject. XE037 migrates out of the body in small but significant quantities with every teleport. We're attributing way too much intelligence to it here, but we can't help but feel this is some kind of intentional 'stowaway' effect, like it's shedding off the body to try to colonize a new dimension. At the meeting, Dr. Sidhu told us to stop anthropomorphizing it. He thinks it's no more a sign of intelligence than a cold virus coming out with a sneeze.", + "I've put Dr. Welch and Dr. Bondi on supervised leave following the incident discussed in my previous message. Obviously neither has a history of this kind of behavior or they wouldn't be here, and it's deeply concerning that they both suddenly began acting this way following Wednesday's demonstration. For now we'll keep them on-site until (hopefully) they recover enough to get back to work, but I'd like to formally ask that we start looking at possible replacements.", + "At approximately 11:15 today, I saw Dr. Greene enter the break area from the primary corridor. He is always so rude so I drew my service pistol and fired three shots into his body. Then I shot Dr. Summers and Officer Clark because they were being loud. I am sorry, but I do not know how many times I shot them. I think Hollis tazed me but I was so mad by that point that I could not keep track of what was going on. Then you restrained me and brought me here, so I guess that's my whole report." ] } ] diff --git a/data/json/snippets/mutant_anatomy.json b/data/json/snippets/mutant_anatomy.json index d914d42482a9c..efccd0450330c 100644 --- a/data/json/snippets/mutant_anatomy.json +++ b/data/json/snippets/mutant_anatomy.json @@ -142,7 +142,8 @@ "Inside is a complex, still-squirming mess of strange appendages and organs that bear only a passing resemblance to any natural creature", "Beneath the chitin, the meat is covered in thick, bristly hair hiding a chaotic bramble of half-formed, mutated organs", "Inside is a tangled mess of organs and tissues that do not appear entirely natural", - "Inside the creature you find lungs, hearts, and intestines more like a mammal than a giant bug" + "Inside the creature you find lungs, hearts, and intestines more like a mammal than a giant bug", + "Despite the apparent death of the whole, some organs still pump, beat, and churn on undisturbed" ] }, { @@ -157,7 +158,9 @@ "You follow what you think are superflous veins into the deep, finding a gorged, tick-like creature at their convergence", "Every cut reveals an organ you can't quite recognize, and you start to wonder what your insides look like by now", "You are dizzied by the rainbow coloration of the creature's muscles, clashing with the bloody mess in its guts", - "As you cut into its flesh you are disturbed by its sickly-sweet smell, like fermenting fruit" + "As you cut into its flesh you are disturbed by its sickly-sweet smell, like fermenting fruit", + "There are fist-sized globs of nervous tissue dotted about its insides, sending the corpse into a spasm every time you touch one", + "The muscles grow stiff, and begin leaking a dark gray liquid that pools around your feet" ] }, { @@ -243,6 +246,42 @@ "This one's very flesh is glowing with a blinding intensity, lighting up your gruesome work" ] }, + { + "type": "snippet", + "category": "", + "text": [ + "", + "", + "Its chitin is sturdier than usual for the flying insects, and marked by the struggles of its previous victims", + "The stripes on the back of its abdomen are warped into a nauseating spiral you can't tear your eyes away from", + "Upon closer inspection, the uniform black stripes reveal themselves to be rows upon rows of glistening eyes", + "With each cut, the stinger springs from its sheath, tip bending ever so slightly towards your leg", + "The stripes -so uniform at a distance- reveal themselves to be jagged, strangely familiar patterns, like the cursive of an unknown language" + ] + }, + { + "type": "snippet", + "category": "", + "text": [ + "", + "", + "The chaotic bundle of organs in its torso look much like those of other mutated insects, but as you near the abdomen there's order to the madness: comparatively neat rows of venom-filled glands, each feeding into a reservoir at the base of the stinger", + "You open a stomach, releasing a gush of sweet-smelling grey fluid", + "For its apparent hunger for meat, its intestines seem almost atrophied", + "Your knife reaches the retracted stinger and slips on its smooth surface, opening half a dozen venom glands in the process. You haven't cut yourself, have you?", + "You find a handful of dead grubs underneath its chitin, near a chewed-open venom gland" + ] + }, + { + "type": "snippet", + "category": "", + "text": [ + "Deep, deep within the dead queen you find a smooth, brightly colored plaque the size of your fingernail, reading ", + "Some of the now-familiar venom glands also contain dozens of shiny white eggs, undisturbed by their deadly surroundings", + "As you cut into its abdomen you disturb a handful of normal-sized, wingless wasps clinging to a strange, tubular organ. They crawl a few steps before falling lifelessly to the ground", + "Some of the egg clusters are ransacked by fat, white worms tunneling blindly through its flesh" + ] + }, { "type": "snippet", "category": "", @@ -320,7 +359,26 @@ "text": [ ".", ". .", - ". . " + ". . ." + ] + }, + { + "type": "snippet", + "category": "", + "text": [ + " .", + ".", + ". . ", + ". . ." + ] + }, + { + "type": "snippet", + "category": "", + "text": [ + ". .", + ". .", + ". . ." ] }, { diff --git a/data/json/snippets/newspapers.json b/data/json/snippets/newspapers.json index 51269f1dfd27c..c059b05a82094 100644 --- a/data/json/snippets/newspapers.json +++ b/data/json/snippets/newspapers.json @@ -348,7 +348,7 @@ }, { "id": "weeks_old_news_12", - "text": "PSYCHIC EMANATIONS? A seemingly far-fetched theory about ongoing riots sweeping the nation has been gaining traction after a leaked document about experiments in magnetic control of brain-waves. \"Two weeks ago, I'd have told you this is ridiculous,\" said Dr. Andrew Morton, an epidemiologist and our leading correspondent for the medical basis for the riots. \"Now? I'll consider anything. With the caveat that I don't think any of this is possible, magnetic weaponry altering our brain waves and making people into crazy violent psychopaths is more plausible than a lot of the theories running around. I certainly prefer this one to that 'zombies' suggestion from a few days ago.\"" + "text": "PSYCHIC EMANATIONS? A seemingly far-fetched theory about ongoing riots sweeping the nation has been gaining traction after a leaked document about experiments in magnetic control of brain-waves. \"Two weeks ago, I'd have told you this is ridiculous,\" said Dr. Andrew Morton, an epidemiologist and our leading expert on the medical basis for the riots. \"Now? I'll consider anything. With the caveat that I don't think any of this is possible, magnetic weaponry altering our brain waves and making people into crazy violent psychopaths is more plausible than a lot of the theories running around. I certainly prefer this one to that 'zombies' suggestion from a few days ago.\"" }, { "id": "weeks_old_news_13", diff --git a/data/json/snippets/snippets.json b/data/json/snippets/snippets.json index 58dc06c5979e6..fc9c96cbe8c6d 100644 --- a/data/json/snippets/snippets.json +++ b/data/json/snippets/snippets.json @@ -1,4 +1,9 @@ [ + { + "type": "snippet", + "category": "", + "text": [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + }, { "type": "snippet", "category": "necropolis_intro", diff --git a/data/json/snippets/valentines.json b/data/json/snippets/valentines.json new file mode 100644 index 0000000000000..966bf1253dad7 --- /dev/null +++ b/data/json/snippets/valentines.json @@ -0,0 +1,45 @@ +[ + { + "type": "snippet", + "category": "valentine", + "text": [ + { + "id": "valentine_1", + "text": "There is a picture of a kitten pouncing; blood faintly drips from its claws. \"I'd kill! For you to be my valentine.\"" + }, + { + "id": "valentine_2", + "text": "A police robot stalks between office cubicles while an indistinct person hides under a desk. \"Don't hide from love!\"" + }, + { "id": "valentine_3", "text": "A picture of a bloody steak. \"My love can feed you.\"" }, + { + "id": "valentine_4", + "text": "A rustic campground at night lit by a campfire. \"We should go away together. Where no one can find us.\"" + }, + { + "id": "valentine_5", + "text": "A girl with a giant spoon stirs a cauldron with a boy inside and a fire below. \"You suit my taste just right, Valentine\"" + }, + { + "id": "valentine_6", + "text": "A person stepping out from the forest. \"Don't be afraid, it's me, your valentine!\"" + }, + { + "id": "valentine_7", + "text": "A building that says 'Crematorium' on it. \"Ashes to ashes, dust to dust, show me a Valentine, that I can trust\"" + }, + { + "id": "valentine_8", + "text": "A popular RPG character. \"We should adventure together.\" In the background, a village in flames." + }, + { + "id": "valentine_9", + "text": "A picture of a room that looks like your bedroom. \"I can't wait to see you again, Valentine. Sweet dreams.\"" + }, + { + "id": "valentine_10", + "text": "A girl smiles into the camera. Behind her a house is on fire. \"I'm burning, I'm burning, I'm burning for you, my valentine!\"" + } + ] + } +] diff --git a/data/json/species.json b/data/json/species.json index f58ed34aca5e8..156a16b6890f1 100644 --- a/data/json/species.json +++ b/data/json/species.json @@ -93,7 +93,7 @@ "description": "a flying insect", "footsteps": "BZZZZZZZZZZZZZZZZZ", "anger_triggers": [ "FRIEND_DIED" ], - "fear_triggers": [ "HURT", "FIRE" ], + "flags": [ "LOUDMOVES" ], "bleeds": "fd_blood_insect" }, { diff --git a/data/json/start_locations.json b/data/json/start_locations.json index 99848e24e71e2..7041747c9d520 100644 --- a/data/json/start_locations.json +++ b/data/json/start_locations.json @@ -457,6 +457,12 @@ "name": "Freshwater Research Station", "terrain": [ "sealab_small_surface" ] }, + { + "type": "start_location", + "id": "sloc_lodge_ground", + "name": "Hunting Lodge", + "terrain": [ "lodge_ground" ] + }, { "type": "start_location", "id": "sloc_gas_station", diff --git a/data/json/traps.json b/data/json/traps.json index 0cfb8c4e069db..d48d932157a79 100644 --- a/data/json/traps.json +++ b/data/json/traps.json @@ -4,6 +4,8 @@ "id": "tr_bubblewrap", "name": "bubble wrap", "color": "light_cyan", + "memorial_male": { "ctxt": "memorial_male", "str": "Stepped on bubble wrap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Stepped on bubble wrap." }, "symbol": "_", "visibility": 0, "avoidance": 8, @@ -17,6 +19,8 @@ "id": "tr_glass", "name": "glass shards", "color": "light_cyan", + "memorial_male": { "ctxt": "memorial_male", "str": "Stepped on glass." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Stepped on glass." }, "symbol": "_", "visibility": 6, "avoidance": 8, @@ -103,6 +107,8 @@ "id": "tr_microlab_shifting_hall", "name": "microlab shifting hall", "color": "brown", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a dimensional anomaly." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a dimensional anomaly." }, "symbol": ".", "visibility": 99, "avoidance": 99, @@ -116,6 +122,8 @@ "id": "tr_microlab_shifting_hall_2", "name": "microlab shifting hall", "color": "brown", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a dimensional anomaly." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a dimensional anomaly." }, "symbol": ".", "visibility": 99, "avoidance": 99, @@ -130,6 +138,8 @@ "trigger_weight": "200 g", "name": "bear trap", "color": "blue", + "memorial_male": { "ctxt": "memorial_male", "str": "Caught by a beartrap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Caught by a beartrap." }, "symbol": "^", "visibility": 2, "avoidance": 7, @@ -152,12 +162,15 @@ "trigger_weight": "200 g", "name": "buried bear trap", "color": "blue", + "memorial_male": { "ctxt": "memorial_male", "str": "Caught by a beartrap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Caught by a beartrap." }, "symbol": "^", "visibility": 9, "avoidance": 8, "difficulty": 4, "action": "beartrap", "drops": [ "beartrap" ], + "flags": [ "SONAR_DETECTABLE" ], "vehicle_data": { "damage": 300, "sound_volume": 8, @@ -173,6 +186,8 @@ "id": "tr_nailboard", "name": "spiked board", "color": "light_gray", + "memorial_male": { "ctxt": "memorial_male", "str": "Stepped on a spiked board." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Stepped on a spiked board." }, "symbol": "_", "visibility": 1, "avoidance": 6, @@ -186,6 +201,8 @@ "id": "tr_caltrops", "name": "caltrops", "color": "dark_gray", + "memorial_male": { "ctxt": "memorial_male", "str": "Stepped on a caltrop." }, + "memorial_female": { "ctxt": "memorial_female", "str": "stepped on a caltrop." }, "symbol": "_", "visibility": 4, "avoidance": 6, @@ -199,6 +216,8 @@ "id": "tr_caltrops_glass", "name": "glass caltrops", "color": "dark_gray", + "memorial_male": { "ctxt": "memorial_male", "str": "Stepped on a glass caltrop." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Stepped on a glass caltrop." }, "symbol": "_", "visibility": 6, "avoidance": 6, @@ -212,6 +231,8 @@ "id": "tr_tripwire", "name": "tripwire", "color": "light_red", + "memorial_male": { "ctxt": "memorial_male", "str": "Tripped on a tripwire." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Tripped on a tripwire." }, "symbol": "^", "visibility": 6, "avoidance": 4, @@ -225,6 +246,8 @@ "trigger_weight": "200 g", "name": "crossbow trap", "color": "green", + "memorial_male": { "ctxt": "memorial_male", "str": "Trigged a crossbow trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Trigged a crossbow trap." }, "symbol": "^", "visibility": 5, "avoidance": 4, @@ -248,6 +271,8 @@ "trigger_weight": "200 g", "name": "shotgun trap", "color": "red", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a shotgun trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a shotgun trap." }, "symbol": "^", "visibility": 4, "avoidance": 5, @@ -270,6 +295,8 @@ "trigger_weight": "200 g", "name": "shotgun trap", "color": "red", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a shotgun trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a shotgun trap." }, "symbol": "^", "visibility": 4, "avoidance": 5, @@ -293,6 +320,8 @@ "trigger_weight": "200 g", "name": "shotgun trap", "color": "red", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a shotgun trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a shotgun trap." }, "symbol": "^", "visibility": 4, "avoidance": 5, @@ -329,6 +358,8 @@ "id": "tr_blade", "name": "spinning blade", "color": "cyan", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a blade trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a blade trap." }, "symbol": "\\", "visibility": 0, "avoidance": 4, @@ -342,6 +373,8 @@ "trigger_weight": "200 g", "name": "land mine", "color": "red", + "memorial_male": { "ctxt": "memorial_male", "str": "Stepped on a land mine." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Stepped on a land mine." }, "symbol": "^", "visibility": 1, "avoidance": 14, @@ -364,12 +397,15 @@ "trigger_weight": "200 g", "name": "buried land mine", "color": "red", + "memorial_male": { "ctxt": "memorial_male", "str": "Stepped on a land mine." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Stepped on a land mine." }, "symbol": "_", "visibility": 10, "avoidance": 14, "difficulty": 10, "action": "landmine", "drops": [ "landmine" ], + "flags": [ "SONAR_DETECTABLE" ], "vehicle_data": { "do_explosion": true, "damage": 1000, @@ -386,6 +422,8 @@ "trigger_weight": "200 g", "name": "teleport pad", "color": "magenta", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a teleport trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a teleport trap." }, "symbol": "_", "visibility": 0, "avoidance": 15, @@ -398,6 +436,8 @@ "id": "tr_goo", "name": "goo pit", "color": "dark_gray", + "memorial_male": { "ctxt": "memorial_male", "str": "Stepped into thick goo." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Stepped into thick goo." }, "symbol": "_", "visibility": 0, "avoidance": 15, @@ -409,6 +449,8 @@ "id": "tr_dissector", "name": "exposed high-energy conduit", "color": "cyan", + "memorial_male": { "ctxt": "memorial_male", "str": "Stepped into an exposed high-energy conduit." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Stepped into an exposed high-energy conduit." }, "symbol": "7", "visibility": 2, "avoidance": 20, @@ -421,11 +463,14 @@ "id": "tr_sinkhole", "name": "sinkhole", "color": "brown", + "memorial_male": { "ctxt": "memorial_male", "str": "Stepped into a sinkhole." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Stepped into a sinkhole." }, "symbol": "_", "visibility": 10, "avoidance": 14, "difficulty": 99, "action": "sinkhole", + "flags": [ "SONAR_DETECTABLE" ], "vehicle_data": { "damage": 500 } }, { @@ -433,6 +478,8 @@ "id": "tr_pit", "name": "pit", "color": "brown", + "memorial_male": { "ctxt": "memorial_male", "str": "Fell in a pit." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Fell in a pit." }, "symbol": "0", "visibility": 0, "avoidance": 8, @@ -445,6 +492,8 @@ "id": "tr_spike_pit", "name": "spiked pit", "color": "blue", + "memorial_male": { "ctxt": "memorial_male", "str": "Fell into a spiked pit." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Fell into a spiked pit." }, "symbol": "0", "visibility": 0, "avoidance": 8, @@ -457,11 +506,14 @@ "id": "tr_lava", "name": "lava", "color": "red", + "memorial_male": { "ctxt": "memorial_male", "str": "Stepped into lava." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Stepped into lava." }, "symbol": "~", "visibility": 0, "avoidance": 99, "difficulty": 99, "action": "lava", + "flags": [ "CONVECTS_TEMPERATURE" ], "vehicle_data": { "damage": 500 } }, { @@ -469,30 +521,22 @@ "id": "tr_portal", "name": "shimmering portal", "color": "magenta", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a teleoport trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a teleoport trap." }, "symbol": "%", "visibility": 0, "avoidance": 99, "difficulty": 99, "action": "portal" }, - { - "type": "trap", - "id": "tr_ledge", - "name": "ledge", - "color": "i_cyan", - "symbol": " ", - "visibility": 0, - "avoidance": 99999, - "difficulty": 99, - "action": "ledge", - "vehicle_data": { "is_falling": true } - }, { "type": "trap", "id": "tr_boobytrap", "trigger_weight": "200 g", "name": "booby trap", "color": "light_cyan", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a booby trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a booby trap." }, "symbol": "^", "visibility": 5, "avoidance": 4, @@ -514,6 +558,8 @@ "id": "tr_temple_flood", "name": "ledge", "color": "light_gray", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a flood trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a flood trap." }, "symbol": "^", "visibility": 9, "avoidance": 20, @@ -561,6 +607,8 @@ "id": "tr_shadow", "name": "", "color": "white", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a shadow trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a shadow trap." }, "symbol": "^", "visibility": 99, "avoidance": 99, @@ -573,6 +621,8 @@ "id": "tr_drain", "name": "", "color": "white", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a life-draining trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a life-draining trap." }, "symbol": "^", "visibility": 99, "avoidance": 99, @@ -585,6 +635,8 @@ "id": "tr_snake", "name": "", "color": "white", + "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a shadow snake trap." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a shadow snake trap." }, "symbol": "^", "visibility": 99, "avoidance": 99, @@ -597,6 +649,8 @@ "id": "tr_glass_pit", "name": "glass pit", "color": "cyan", + "memorial_male": { "ctxt": "memorial_male", "str": "Fell into a pit filled with glass shards." }, + "memorial_female": { "ctxt": "memorial_female", "str": "Fell into a pit filled with glass shards." }, "symbol": "0", "visibility": 0, "avoidance": 8, diff --git a/data/json/vehicleparts/motor.json b/data/json/vehicleparts/motor.json index 89b44c57c70ce..a2727c273a826 100644 --- a/data/json/vehicleparts/motor.json +++ b/data/json/vehicleparts/motor.json @@ -159,5 +159,30 @@ { "item": "cable", "charges": [ 20, 30 ] } ], "damage_reduction": { "all": 60 } + }, + { + "id": "engine_electric_train", + "copy-from": "engine_motor", + "type": "vehicle_part", + "name": { "str": "1300hp electric train engine" }, + "item": "motor_train1300", + "durability": 500, + "power": 1000000, + "energy_consumption": 1050000, + "damage_modifier": 80, + "requirements": { + "install": { "skills": [ [ "mechanics", 8 ] ], "time": "60 m", "using": [ [ "vehicle_wrench_2", 1 ] ] }, + "removal": { "skills": [ [ "mechanics", 4 ] ], "time": "45 m", "using": [ [ "vehicle_wrench_2", 1 ] ] }, + "repair": { "skills": [ [ "mechanics", 10 ] ], "time": "60 m", "using": [ [ "welding_standard", 20 ] ] } + }, + "breaks_into": [ + { "item": "steel_lump", "count": [ 60, 80 ] }, + { "item": "steel_chunk", "count": [ 24, 40 ] }, + { "item": "scrap", "count": [ 24, 40 ] }, + { "item": "e_scrap", "count": [ 20, 60 ] }, + { "item": "bearing", "count": [ 100, 240 ] }, + { "item": "cable", "charges": [ 80, 120 ] } + ], + "damage_reduction": { "all": 60 } } ] diff --git a/data/json/vehicleparts/vehicle_parts.json b/data/json/vehicleparts/vehicle_parts.json index 423d526fff9eb..f1833e4ea62c4 100644 --- a/data/json/vehicleparts/vehicle_parts.json +++ b/data/json/vehicleparts/vehicle_parts.json @@ -111,7 +111,17 @@ "repair": { "skills": [ [ "mechanics", 1 ] ], "time": "20 s", "using": [ [ "adhesive", 1 ] ] } }, "breaks_into": "ig_vp_wood_plate", - "flags": [ "ENGINE", "BOARDABLE", "E_STARTS_INSTANTLY", "ANIMAL_CTRL", "HARNESS_any", "STEERABLE", "UNMOUNT_ON_DAMAGE", "WHEEL" ], + "flags": [ + "ENGINE", + "BOARDABLE", + "E_STARTS_INSTANTLY", + "ANIMAL_CTRL", + "HARNESS_any", + "STEERABLE", + "UNMOUNT_ON_DAMAGE", + "UNSTABLE_WHEEL", + "WHEEL" + ], "damage_reduction": { "all": 2 } }, { diff --git a/data/json/vehicleparts/vp_flags.json b/data/json/vehicleparts/vp_flags.json index 1f6738b43b312..4db9e5afaba06 100644 --- a/data/json/vehicleparts/vp_flags.json +++ b/data/json/vehicleparts/vp_flags.json @@ -157,6 +157,12 @@ "context": [ "vehicle_part" ], "info": "If your vehicle consists of a single tile, this wheel is enough to allow it to move." }, + { + "id": "UNSTABLE_WHEEL", + "type": "json_flag", + "context": [ "vehicle_part" ], + "info": "This wheel requires another wheel to be installed to be functional." + }, { "id": "STEERABLE", "type": "json_flag", diff --git a/data/json/vehicleparts/wheel.json b/data/json/vehicleparts/wheel.json index 4750cfb6e89a7..cb06c23df9413 100644 --- a/data/json/vehicleparts/wheel.json +++ b/data/json/vehicleparts/wheel.json @@ -150,7 +150,7 @@ "removal": { "skills": [ [ "mechanics", 2 ] ], "time": "30 m", "using": [ [ "vehicle_fasten", 1 ] ] }, "repair": { "skills": [ [ "mechanics", 3 ] ], "time": "60 m", "using": [ [ "welding_standard", 5 ] ] } }, - "flags": [ "WHEEL", "NEEDS_JACKING", "RAIL" ], + "flags": [ "WHEEL", "UNSTABLE_WHEEL", "NEEDS_JACKING", "RAIL" ], "damage_reduction": { "all": 66 } }, { @@ -197,7 +197,7 @@ "removal": { "skills": [ [ "mechanics", 3 ] ], "time": "30 m", "using": [ [ "vehicle_fasten", 1 ] ] }, "repair": { "skills": [ [ "mechanics", 6 ] ], "time": "60 m", "using": [ [ "welding_standard", 5 ] ] } }, - "flags": [ "ARMOR", "OBSTACLE", "WHEEL", "NEEDS_JACKING", "STEERABLE" ], + "flags": [ "ARMOR", "OBSTACLE", "WHEEL", "UNSTABLE_WHEEL", "NEEDS_JACKING", "STEERABLE" ], "wheel_type": "rigid", "contact_area": 400, "damage_reduction": { "all": 280 } @@ -228,7 +228,7 @@ "removal": { "skills": [ [ "mechanics", 0 ] ], "time": "15 m", "using": [ [ "vehicle_fasten", 1 ] ] }, "repair": { "skills": [ [ "mechanics", 4 ] ], "time": "15 m", "using": [ [ "adhesive", 1 ], [ "plastics", 1 ] ] } }, - "flags": [ "WHEEL", "NEEDS_JACKING", "NEEDS_WHEEL_MOUNT_MEDIUM" ], + "flags": [ "WHEEL", "UNSTABLE_WHEEL", "NEEDS_JACKING", "NEEDS_WHEEL_MOUNT_MEDIUM" ], "damage_reduction": { "bash": 20 } }, { @@ -268,7 +268,7 @@ "removal": { "skills": [ [ "mechanics", 0 ] ], "time": "20 m", "using": [ [ "vehicle_fasten", 1 ] ] }, "repair": { "skills": [ [ "mechanics", 5 ] ], "time": "20 m", "using": [ [ "adhesive", 1 ], [ "plastics", 1 ] ] } }, - "flags": [ "WHEEL", "NEEDS_JACKING", "NEEDS_WHEEL_MOUNT_HEAVY" ], + "flags": [ "WHEEL", "UNSTABLE_WHEEL", "NEEDS_JACKING", "NEEDS_WHEEL_MOUNT_HEAVY" ], "damage_reduction": { "all": 60, "cut": 30, "stab": 16 } }, { @@ -325,7 +325,7 @@ "removal": { "skills": [ [ "mechanics", 0 ] ], "time": "15 m", "using": [ [ "vehicle_fasten", 1 ] ] }, "repair": { "skills": [ [ "mechanics", 2 ] ], "time": "15 m", "using": [ [ "adhesive", 1 ], [ "plastics", 1 ] ] } }, - "flags": [ "WHEEL", "NEEDS_JACKING", "FOLDABLE", "NEEDS_WHEEL_MOUNT_LIGHT" ], + "flags": [ "WHEEL", "UNSTABLE_WHEEL", "NEEDS_JACKING", "FOLDABLE", "NEEDS_WHEEL_MOUNT_LIGHT" ], "damage_reduction": { "bash": 6 } }, { @@ -446,7 +446,7 @@ "removal": { "skills": [ [ "mechanics", 0 ] ], "time": "15 m", "using": [ [ "vehicle_fasten", 1 ] ] }, "repair": { "skills": [ [ "mechanics", 2 ] ], "time": "15 m", "using": [ [ "adhesive", 1 ], [ "plastics", 1 ] ] } }, - "flags": [ "WHEEL", "NEEDS_JACKING", "NEEDS_WHEEL_MOUNT_LIGHT" ], + "flags": [ "WHEEL", "UNSTABLE_WHEEL", "NEEDS_JACKING", "NEEDS_WHEEL_MOUNT_LIGHT" ], "damage_reduction": { "bash": 10 } }, { @@ -507,7 +507,7 @@ "removal": { "skills": [ [ "mechanics", 0 ] ], "time": "15 m", "using": [ [ "vehicle_wrench_2", 1 ] ] }, "repair": { "skills": [ [ "mechanics", 2 ] ], "time": "15 m", "using": [ [ "adhesive", 1 ], [ "plastics", 1 ] ] } }, - "flags": [ "WHEEL", "FOLDABLE", "NEEDS_WHEEL_MOUNT_LIGHT" ] + "flags": [ "WHEEL", "UNSTABLE_WHEEL", "FOLDABLE", "NEEDS_WHEEL_MOUNT_LIGHT" ] }, { "copy-from": "wheel_small_abstract", @@ -638,7 +638,7 @@ "removal": { "skills": [ [ "mechanics", 0 ] ], "time": "15 m", "using": [ [ "vehicle_fasten", 1 ] ] }, "repair": { "skills": [ [ "mechanics", 4 ] ], "time": "15 m", "using": [ [ "adhesive", 1 ], [ "plastics", 1 ] ] } }, - "flags": [ "WHEEL", "NEEDS_JACKING", "NEEDS_WHEEL_MOUNT_MEDIUM" ], + "flags": [ "WHEEL", "UNSTABLE_WHEEL", "NEEDS_JACKING", "NEEDS_WHEEL_MOUNT_MEDIUM" ], "damage_reduction": { "bash": 25 } }, { @@ -674,7 +674,7 @@ "rolling_resistance": 2.15, "wheel_type": "rigid", "contact_area": 60, - "flags": [ "WHEEL", "NEEDS_JACKING" ], + "flags": [ "WHEEL", "UNSTABLE_WHEEL", "NEEDS_JACKING" ], "damage_reduction": { "all": 14 } }, { diff --git a/data/json/vehicles/helicopters.json b/data/json/vehicles/helicopters.json index 93d58213500c1..8baddfd6cc55d 100644 --- a/data/json/vehicles/helicopters.json +++ b/data/json/vehicles/helicopters.json @@ -1030,7 +1030,7 @@ { "x": -9, "y": -1, "chance": 7, "items": [ "rucksack" ] }, { "x": -8, "y": -1, "chance": 5, "items": [ "m9" ] }, { "x": -7, "y": -2, "chance": 5, "items": [ "223" ] }, - { "x": -7, "y": -2, "chance": 5, "magazine": 90, "ammo": 70, "items": [ "m4a1" ] }, + { "x": -7, "y": -2, "chance": 5, "magazine": 90, "ammo": 70, "items": "nato_assault_rifle", "variant": "m4a1" }, { "x": -7, "y": -2, "chance": 7, "items": [ "EMPbomb" ] }, { "x": -7, "y": -2, "chance": 15, "items": [ "mask_gas" ] }, { "x": -7, "y": -2, "chance": 7, "items": [ "flashlight" ], "ammo": 100 }, @@ -1221,7 +1221,7 @@ { "x": -9, "y": -1, "chance": 7, "items": [ "rucksack" ] }, { "x": -8, "y": -1, "chance": 5, "items": [ "m9" ] }, { "x": -7, "y": -2, "chance": 5, "items": [ "223" ] }, - { "x": -7, "y": -2, "chance": 5, "magazine": 90, "ammo": 70, "items": [ "m4a1" ] }, + { "x": -7, "y": -2, "chance": 5, "magazine": 90, "ammo": 70, "items": "nato_assault_rifle", "variant": "m4a1" }, { "x": -7, "y": -2, "chance": 7, "items": [ "EMPbomb" ] }, { "x": -7, "y": -2, "chance": 15, "items": [ "mask_gas" ] }, { "x": -7, "y": -2, "chance": 7, "items": [ "flashlight" ], "ammo": 100 }, @@ -1390,7 +1390,7 @@ { "x": -9, "y": -1, "chance": 7, "items": [ "rucksack" ] }, { "x": -8, "y": -1, "chance": 5, "items": [ "m9" ] }, { "x": -7, "y": -2, "chance": 5, "items": [ "223" ] }, - { "x": -7, "y": -2, "chance": 5, "magazine": 90, "ammo": 70, "items": [ "m4a1" ] }, + { "x": -7, "y": -2, "chance": 5, "magazine": 90, "ammo": 70, "items": "nato_assault_rifle", "variant": "m4a1" }, { "x": -7, "y": -2, "chance": 7, "items": [ "EMPbomb" ] }, { "x": -7, "y": -2, "chance": 15, "items": [ "mask_gas" ] }, { "x": -7, "y": -2, "chance": 7, "items": [ "flashlight" ], "ammo": 100 }, @@ -1507,7 +1507,7 @@ { "x": -6, "y": 1, "chance": 7, "ammo": 70, "items": [ "stanag30" ] }, { "x": -6, "y": 1, "chance": 10, "items": [ "helmet_army" ] }, { "x": -6, "y": 1, "chance": 15, "items": [ "gasfilter_m" ] }, - { "x": -6, "y": 1, "chance": 3, "magazine": 70, "ammo": 70, "items": [ "m4a1" ] }, + { "x": -6, "y": 1, "chance": 100, "magazine": 70, "ammo": 70, "items": "nato_assault_rifle", "variant": "m4a1" }, { "x": -5, "y": 0, "chance": 3, "items": [ "cig_butt" ] }, { "x": -5, "y": 0, "chance": 5, "items": [ "cig" ] }, { "x": -5, "y": 2, "chance": 7, "ammo": 70, "items": [ "stanag30" ] }, @@ -1613,7 +1613,7 @@ { "x": -6, "y": 1, "chance": 7, "ammo": 70, "items": [ "stanag30" ] }, { "x": -6, "y": 1, "chance": 10, "items": [ "helmet_army" ] }, { "x": -6, "y": 1, "chance": 15, "items": [ "gasfilter_m" ] }, - { "x": -6, "y": 1, "chance": 3, "magazine": 70, "ammo": 70, "items": [ "m4a1" ] }, + { "x": -6, "y": 1, "chance": 3, "magazine": 70, "ammo": 70, "items": "nato_assault_rifle", "variant": "m4a1" }, { "x": -5, "y": 0, "chance": 20, "item_groups": [ "remains_soldier" ] }, { "x": -5, "y": 0, "chance": 3, "items": [ "cig_butt" ] }, { "x": -5, "y": 0, "chance": 5, "items": [ "cig" ] }, @@ -1717,7 +1717,7 @@ { "x": -6, "y": 1, "chance": 7, "ammo": 70, "items": [ "stanag30" ] }, { "x": -6, "y": 1, "chance": 10, "items": [ "helmet_army" ] }, { "x": -6, "y": 1, "chance": 15, "items": [ "gasfilter_m" ] }, - { "x": -6, "y": 1, "chance": 3, "magazine": 70, "ammo": 70, "items": [ "m4a1" ] }, + { "x": -6, "y": 1, "chance": 3, "magazine": 70, "ammo": 70, "items": "nato_assault_rifle", "variant": "m4a1" }, { "x": -5, "y": 0, "chance": 20, "item_groups": [ "remains_soldier" ] }, { "x": -5, "y": 0, "chance": 3, "items": [ "cig_butt" ] }, { "x": -5, "y": 0, "chance": 5, "items": [ "cig" ] }, diff --git a/data/json/vehicles/trains.json b/data/json/vehicles/trains.json index 21dcea76870b3..b15175495f7dd 100644 --- a/data/json/vehicles/trains.json +++ b/data/json/vehicles/trains.json @@ -249,5 +249,161 @@ { "x": 0, "y": 0, "parts": [ "frame_vertical_2", "seat", "rail_wheel_small_pair", "controls" ] }, { "x": 0, "y": 0, "parts": [ { "part": "fuel_bunker", "fuel": "coal_lump" } ] } ] + }, + { + "id": "train_electrical_loco1300", + "type": "vehicle", + "name": "Electrical Locomotive", + "parts": [ + { "x": 0, "y": 0, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": 0, "y": -1, "parts": [ "hdframe_cross", "rail_wheel_steerable", "aisle_horizontal", "roof" ] }, + { "x": 0, "y": -2, "parts": [ "hdframe_vertical_2", "windshield_vertical_left", "inboard_mirror" ] }, + { "x": 0, "y": 1, "parts": [ "hdframe_cross", "lit_aisle_vertical", "roof" ] }, + { "x": 0, "y": 2, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": 0, "y": 3, "parts": [ "hdframe_cross", "rail_wheel_steerable", "aisle_horizontal", "roof" ] }, + { "x": 0, "y": 4, "parts": [ "hdframe_vertical_2", "windshield_vertical_right", "inboard_mirror" ] }, + { "x": 1, "y": -2, "parts": [ "hdframe_vertical_2", "board_vertical_left" ] }, + { "x": 1, "y": -1, "parts": [ "hdframe_cross", "board_vertical_right", "dashboard", "roof" ] }, + { "x": 1, "y": 0, "parts": [ "hdframe_cross", "seat", "controls", "dashboard", "roof", "seatbelt" ] }, + { "x": 1, "y": 1, "parts": [ "hdframe_cross", "cam_control", "aisle_vertical", "dashboard", "roof" ] }, + { "x": 1, "y": 2, "parts": [ "hdframe_cross", "seat", "dashboard", "roof", "seatbelt" ] }, + { "x": 1, "y": 3, "parts": [ "hdframe_cross", "board_vertical_left", "dashboard", "roof" ] }, + { "x": 1, "y": 4, "parts": [ "hdframe_vertical_2", "board_vertical_right" ] }, + { "x": 2, "y": -2, "parts": [ "hdframe_nw", "halfboard_nw" ] }, + { "x": 2, "y": -1, "parts": [ "hdframe_se", "board_horizontal" ] }, + { "x": 2, "y": 0, "parts": [ "hdframe_cross", "windshield_horizontal_front_edge" ] }, + { "x": 2, "y": 1, "parts": [ "hdframe_cross", "windshield_horizontal_front" ] }, + { "x": 2, "y": 2, "parts": [ "hdframe_cross", "windshield_horizontal_front_edge" ] }, + { "x": 2, "y": 3, "parts": [ "hdframe_sw", "board_horizontal" ] }, + { "x": 2, "y": 4, "parts": [ "hdframe_ne", "halfboard_ne" ] }, + { "x": 3, "y": -1, "parts": [ "hdframe_nw", "halfboard_nw", "plating_steel" ] }, + { "x": 3, "y": 0, "parts": [ "hdframe_horizontal_2", "halfboard_horizontal_2", "headlight", "plating_steel" ] }, + { "x": 3, "y": 1, "parts": [ "hdframe_horizontal_2", "halfboard_horizontal_2", "plating_steel" ] }, + { "x": 3, "y": 2, "parts": [ "hdframe_horizontal_2", "halfboard_horizontal_2", "headlight", "plating_steel" ] }, + { "x": 3, "y": 3, "parts": [ "hdframe_ne", "halfboard_ne", "plating_steel" ] }, + { "x": -1, "y": -2, "parts": [ "hdframe_vertical_2", "board_vertical_left" ] }, + { "x": -1, "y": -1, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -1, "y": 0, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -1, "y": 1, "parts": [ "hdframe_cross", "aisle_vertical", "roof" ] }, + { "x": -1, "y": 2, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -1, "y": 3, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -1, "y": 4, "parts": [ "hdframe_vertical_2", "board_vertical_right" ] }, + { "x": -2, "y": -2, "parts": [ "hdframe_vertical_2", "board_vertical_T_left" ] }, + { "x": -2, "y": -1, "parts": [ "hdframe_cross", "board_horizontal", "roof" ] }, + { "x": -2, "y": 0, "parts": [ "hdframe_cross", "stowboard_horizontal", "roof" ] }, + { "x": -2, "y": 1, "parts": [ "hdframe_cross", "door_sliding", "roof" ] }, + { "x": -2, "y": 2, "parts": [ "hdframe_cross", "stowboard_horizontal", "roof" ] }, + { "x": -2, "y": 3, "parts": [ "hdframe_cross", "board_horizontal", "roof" ] }, + { "x": -2, "y": 4, "parts": [ "hdframe_vertical_2", "board_vertical_T_right" ] }, + { "x": -3, "y": -2, "parts": [ "hdframe_vertical_2", "door_sliding", "door_motor" ] }, + { "x": -3, "y": -1, "parts": [ "hdframe_cross", "rail_wheel", "aisle_horizontal", "roof" ] }, + { "x": -3, "y": 0, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -3, "y": 1, "parts": [ "hdframe_cross", "aisle_vertical", "roof" ] }, + { "x": -3, "y": 2, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -3, "y": 3, "parts": [ "hdframe_cross", "rail_wheel", "aisle_horizontal", "roof" ] }, + { "x": -3, "y": 4, "parts": [ "hdframe_vertical_2", "door_sliding", "door_motor" ] }, + { "x": -4, "y": -2, "parts": [ "hdframe_vertical_2", "board_vertical_T_left" ] }, + { "x": -4, "y": -1, "parts": [ "hdframe_cross", "board_horizontal", "roof" ] }, + { "x": -4, "y": 0, "parts": [ "hdframe_cross", "board_ne", "roof" ] }, + { "x": -4, "y": 1, "parts": [ "hdframe_cross", "lit_aisle_vertical", "roof" ] }, + { "x": -4, "y": 2, "parts": [ "hdframe_cross", "board_nw", "roof" ] }, + { "x": -4, "y": 3, "parts": [ "hdframe_cross", "board_horizontal", "roof" ] }, + { "x": -4, "y": 4, "parts": [ "hdframe_vertical_2", "board_vertical_T_right" ] }, + { "x": -5, "y": -2, "parts": [ "hdframe_vertical_2", "board_vertical_left" ] }, + { + "x": -5, + "y": -1, + "parts": [ "hdframe_cross", "engine_electric_train", "large_storage_battery", "spring_plate", "roof" ] + }, + { "x": -5, "y": 0, "parts": [ "hdframe_cross", "board_vertical_right", "roof" ] }, + { "x": -5, "y": 1, "parts": [ "hdframe_cross", "aisle_vertical", "roof" ] }, + { "x": -5, "y": 2, "parts": [ "hdframe_cross", "board_vertical_left", "roof" ] }, + { "x": -5, "y": 3, "parts": [ "hdframe_cross", "large_storage_battery", "spring_plate", "roof" ] }, + { "x": -5, "y": 4, "parts": [ "hdframe_vertical_2", "board_vertical_right" ] }, + { "x": -6, "y": -2, "parts": [ "hdframe_vertical_2", "board_vertical_left" ] }, + { + "x": -6, + "y": -1, + "parts": [ "hdframe_cross", "engine_electric_train", "large_storage_battery", "spring_plate", "roof" ] + }, + { "x": -6, "y": 0, "parts": [ "hdframe_cross", "board_vertical_right", "roof" ] }, + { "x": -6, "y": 1, "parts": [ "hdframe_cross", "aisle_vertical", "roof" ] }, + { "x": -6, "y": 2, "parts": [ "hdframe_cross", "board_vertical_left", "roof" ] }, + { + "x": -6, + "y": 3, + "parts": [ "hdframe_cross", "engine_electric_train", "large_storage_battery", "spring_plate", "roof" ] + }, + { "x": -6, "y": 4, "parts": [ "hdframe_vertical_2", "board_vertical_right" ] }, + { "x": -7, "y": -2, "parts": [ "hdframe_vertical_2", "board_vertical_T_left" ] }, + { "x": -7, "y": -1, "parts": [ "hdframe_cross", "board_horizontal", "roof" ] }, + { "x": -7, "y": 0, "parts": [ "hdframe_cross", "board_se", "roof" ] }, + { "x": -7, "y": 1, "parts": [ "hdframe_cross", "lit_aisle_vertical", "roof" ] }, + { "x": -7, "y": 2, "parts": [ "hdframe_cross", "board_sw", "roof" ] }, + { "x": -7, "y": 3, "parts": [ "hdframe_cross", "board_horizontal", "roof" ] }, + { "x": -7, "y": 4, "parts": [ "hdframe_vertical_2", "board_vertical_T_right" ] }, + { "x": -8, "y": -2, "parts": [ "hdframe_vertical_2", "door_sliding", "door_motor" ] }, + { "x": -8, "y": -1, "parts": [ "hdframe_cross", "rail_wheel", "aisle_horizontal", "roof" ] }, + { "x": -8, "y": 0, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -8, "y": 1, "parts": [ "hdframe_cross", "aisle_vertical", "roof" ] }, + { "x": -8, "y": 2, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -8, "y": 3, "parts": [ "hdframe_cross", "rail_wheel", "aisle_horizontal", "roof" ] }, + { "x": -8, "y": 4, "parts": [ "hdframe_vertical_2", "door_sliding", "door_motor" ] }, + { "x": -9, "y": -2, "parts": [ "hdframe_vertical_2", "board_vertical_T_left" ] }, + { "x": -9, "y": -1, "parts": [ "hdframe_cross", "board_horizontal", "roof" ] }, + { "x": -9, "y": 0, "parts": [ "hdframe_cross", "stowboard_horizontal", "roof" ] }, + { "x": -9, "y": 1, "parts": [ "hdframe_cross", "door_sliding", "roof" ] }, + { "x": -9, "y": 2, "parts": [ "hdframe_cross", "stowboard_horizontal", "roof" ] }, + { "x": -9, "y": 3, "parts": [ "hdframe_cross", "board_horizontal", "roof" ] }, + { "x": -9, "y": 4, "parts": [ "hdframe_vertical_2", "board_vertical_T_right" ] }, + { "x": -10, "y": -2, "parts": [ "hdframe_vertical_2", "board_vertical_left" ] }, + { "x": -10, "y": -1, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -10, "y": 0, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -10, "y": 1, "parts": [ "hdframe_cross", "aisle_vertical", "roof" ] }, + { "x": -10, "y": 2, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -10, "y": 3, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -10, "y": 4, "parts": [ "hdframe_vertical_2", "board_vertical_right" ] }, + { "x": -11, "y": -2, "parts": [ "hdframe_vertical_2", "windshield_vertical_left", "inboard_mirror" ] }, + { "x": -11, "y": -1, "parts": [ "hdframe_cross", "rail_wheel_steerable", "aisle_horizontal", "roof" ] }, + { "x": -11, "y": 0, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -11, "y": 1, "parts": [ "hdframe_cross", "lit_aisle_vertical", "roof" ] }, + { "x": -11, "y": 2, "parts": [ "hdframe_cross", "aisle_horizontal", "roof" ] }, + { "x": -11, "y": 3, "parts": [ "hdframe_cross", "rail_wheel_steerable", "aisle_horizontal", "roof" ] }, + { "x": -11, "y": 4, "parts": [ "hdframe_vertical_2", "windshield_vertical_right", "inboard_mirror" ] }, + { "x": -12, "y": -2, "parts": [ "hdframe_vertical_2", "board_vertical_left" ] }, + { "x": -12, "y": -1, "parts": [ "hdframe_cross", "board_vertical_right", "dashboard", "roof" ] }, + { "x": -12, "y": 0, "parts": [ "hdframe_cross", "seat", "dashboard", "roof", "seatbelt" ] }, + { "x": -12, "y": 1, "parts": [ "hdframe_cross", "aisle_vertical", "cam_control", "dashboard", "roof" ] }, + { "x": -12, "y": 2, "parts": [ "hdframe_cross", "seat", "seatbelt", "dashboard", "controls", "roof" ] }, + { "x": -12, "y": 3, "parts": [ "hdframe_cross", "board_vertical_left", "dashboard", "roof" ] }, + { "x": -12, "y": 4, "parts": [ "hdframe_vertical_2", "board_vertical_right" ] }, + { "x": -13, "y": -2, "parts": [ "hdframe_sw", "halfboard_sw" ] }, + { "x": -13, "y": -1, "parts": [ "hdframe_ne", "board_horizontal" ] }, + { "x": -13, "y": 0, "parts": [ "hdframe_cross", "windshield_horizontal_front_edge" ] }, + { "x": -13, "y": 1, "parts": [ "hdframe_cross", "windshield_horizontal_front" ] }, + { "x": -13, "y": 2, "parts": [ "hdframe_cross", "windshield_horizontal_front_edge" ] }, + { "x": -13, "y": 3, "parts": [ "hdframe_nw", "board_horizontal" ] }, + { "x": -13, "y": 4, "parts": [ "hdframe_se", "halfboard_se" ] }, + { "x": -14, "y": -1, "parts": [ "hdframe_sw", "halfboard_sw", "plating_steel" ] }, + { + "x": -14, + "y": 0, + "parts": [ "hdframe_horizontal_2", "halfboard_horizontal_2", "headlight", "plating_steel" ] + }, + { "x": -14, "y": 1, "parts": [ "hdframe_horizontal_2", "halfboard_horizontal_2", "plating_steel" ] }, + { + "x": -14, + "y": 2, + "parts": [ "hdframe_horizontal_2", "halfboard_horizontal_2", "headlight", "plating_steel" ] + }, + { "x": -14, "y": 3, "parts": [ "hdframe_se", "halfboard_se", "plating_steel" ] } + ] + }, + { + "id": "trolley", + "type": "vehicle", + "name": "Trolley", + "blueprint": [ "O" ], + "parts": [ { "x": 0, "y": 0, "parts": [ "frame_vertical", "cargo_space", "rail_wheel_small_pair" ] } ] } ] diff --git a/data/json/weather_type.json b/data/json/weather_type.json index 77fb5a5955120..62e2bebee943c 100644 --- a/data/json/weather_type.json +++ b/data/json/weather_type.json @@ -15,7 +15,7 @@ "rains": false, "acidic": false, "sun_intensity": "high", - "requirements": { "pressure_min": 1020, "humidity_max": 70, "time": "day" } + "condition": { "and": [ "is_day", { "is_pressure": 1020 }, { "not": { "is_humidity": 70 } } ] } }, { "id": "cloudy", @@ -33,7 +33,7 @@ "rains": false, "acidic": false, "sun_intensity": "light", - "requirements": { "pressure_max": 1010, "humidity_min": 40 } + "condition": { "and": [ { "is_humidity": 40 }, { "not": { "is_pressure": 1010 } } ] } }, { "id": "light_drizzle", @@ -54,7 +54,8 @@ "weather_animation": { "factor": 0.01, "color": "light_blue", "sym": "," }, "sound_category": "drizzle", "sun_intensity": "light", - "requirements": { "pressure_max": 1003, "humidity_min": 96, "humidity_and_pressure": false, "required_weathers": [ "cloudy" ] } + "required_weathers": [ "cloudy" ], + "condition": { "or": [ { "is_humidity": 96 }, { "not": { "is_pressure": 1003 } } ] } }, { "id": "drizzle", @@ -75,7 +76,8 @@ "weather_animation": { "factor": 0.01, "color": "light_blue", "sym": "." }, "sound_category": "drizzle", "sun_intensity": "light", - "requirements": { "pressure_max": 1000, "humidity_min": 97, "humidity_and_pressure": false, "required_weathers": [ "light_drizzle" ] } + "required_weathers": [ "light_drizzle" ], + "condition": { "or": [ { "is_humidity": 97 }, { "not": { "is_pressure": 1000 } } ] } }, { "id": "rain", @@ -96,12 +98,8 @@ "weather_animation": { "factor": 0.02, "color": "light_blue", "sym": "," }, "sound_category": "rainy", "sun_intensity": "light", - "requirements": { - "pressure_max": 993, - "humidity_min": 98, - "humidity_and_pressure": false, - "required_weathers": [ "light_drizzle", "drizzle" ] - } + "required_weathers": [ "light_drizzle", "drizzle" ], + "condition": { "or": [ { "is_humidity": 98 }, { "not": { "is_pressure": 993 } } ] } }, { "id": "thunder", @@ -118,19 +116,12 @@ "precip": "heavy", "rains": true, "acidic": false, - "effects": [ - { - "one_in_chance": 50, - "must_be_outside": false, - "sound_message": "You hear a distant rumble of thunder.", - "sound_effect": "thunder_far" - } - ], "tiles_animation": "weather_rain_drop", "weather_animation": { "factor": 0.02, "color": "light_blue", "sym": "." }, "sound_category": "thunder", "sun_intensity": "none", - "requirements": { "pressure_max": 996, "required_weathers": [ "rain" ] } + "required_weathers": [ "rain" ], + "condition": { "not": { "is_pressure": 996 } } }, { "id": "lightning", @@ -147,26 +138,12 @@ "precip": "heavy", "rains": true, "acidic": false, - "effects": [ - { - "one_in_chance": 50, - "must_be_outside": false, - "sound_message": "You hear a distant rumble of thunder.", - "sound_effect": "thunder_far" - }, - { - "one_in_chance": 600, - "must_be_outside": false, - "message": "A flash of lightning illuminates your surroundings!.", - "sound_effect": "thunder_near", - "lightning": true - } - ], "tiles_animation": "weather_rain_drop", "weather_animation": { "factor": 0.04, "color": "light_blue", "sym": "," }, "sound_category": "thunder", "sun_intensity": "none", - "requirements": { "pressure_max": 990, "required_weathers": [ "thunder" ] } + "required_weathers": [ "thunder" ], + "condition": { "not": { "is_pressure": 990 } } }, { "id": "acid_drizzle", @@ -183,21 +160,11 @@ "precip": "light", "rains": true, "acidic": true, - "effects": [ - { - "time_between": "3 minutes", - "must_be_outside": true, - "message": "The acid rain stings, but is mostly harmless for now…", - "rain_proof": true, - "pain_max": 10, - "pain": 1 - } - ], "tiles_animation": "weather_acid_drop", "weather_animation": { "factor": 0.01, "color": "light_green", "sym": "." }, "sound_category": "drizzle", "sun_intensity": "normal", - "requirements": { "required_weathers": [ "drizzle" ] } + "required_weathers": [ "drizzle" ] }, { "id": "acid_rain", @@ -214,21 +181,11 @@ "precip": "heavy", "rains": true, "acidic": true, - "effects": [ - { - "time_between": "2 seconds", - "must_be_outside": true, - "message": "The acid rain burns!", - "rain_proof": true, - "pain_max": 100, - "pain": 3 - } - ], "tiles_animation": "weather_acid_drop", "weather_animation": { "factor": 0.02, "color": "light_green", "sym": "," }, "sound_category": "rainy", "sun_intensity": "none", - "requirements": { "required_weathers": [ "rain" ] } + "required_weathers": [ "rain" ] }, { "id": "flurries", @@ -249,7 +206,8 @@ "weather_animation": { "factor": 0.01, "color": "white", "sym": "." }, "sound_category": "flurries", "sun_intensity": "light", - "requirements": { "temperature_max": 33, "required_weathers": [ "drizzle" ] } + "required_weathers": [ "flurries" ], + "condition": { "not": { "is_temperature": 33 } } }, { "id": "snowing", @@ -266,12 +224,12 @@ "precip": "heavy", "rains": false, "acidic": false, - "effects": [ { "must_be_outside": true, "wet": 10 } ], "tiles_animation": "weather_snowflake", "weather_animation": { "factor": 0.02, "color": "white", "sym": "," }, "sound_category": "snow", "sun_intensity": "light", - "requirements": { "temperature_max": 33, "required_weathers": [ "rain", "thunder", "lightning" ] } + "required_weathers": [ "rain", "thunder", "lightning" ], + "condition": { "not": { "is_temperature": 33 } } }, { "id": "snowstorm", @@ -288,11 +246,11 @@ "precip": "heavy", "rains": false, "acidic": false, - "effects": [ { "must_be_outside": true, "wet": 40 } ], "tiles_animation": "weather_snowflake", "weather_animation": { "factor": 0.04, "color": "white", "sym": "*" }, "sound_category": "snowstorm", "sun_intensity": "none", - "requirements": { "temperature_max": 33, "windpower_min": 15, "required_weathers": [ "thunder", "lightning" ] } + "required_weathers": [ "thunder", "lightning" ], + "condition": { "and": [ { "is_windpower": 15 }, { "not": { "is_temperature": 33 } } ] } } ] diff --git a/data/mods/Aftershock/README.md b/data/mods/Aftershock/README.md index 65effb2a00561..4acaa6a627b6c 100644 --- a/data/mods/Aftershock/README.md +++ b/data/mods/Aftershock/README.md @@ -9,6 +9,7 @@ 4. Missions, locations, and lore snippets. We want this to feel like a world just as real as Cataclysm Prime. Please feel free to reach out to us about ideas and implementations. 5. Alien world basics would be especially desirable at this time. Flora, fauna, terrain, and furniture that make it clear we are no longer on earth. +# Size as of 0.F Stable : 31,084 Lines # Here be dragons! diff --git a/data/mods/Aftershock/itemgroups/bionics_groups.json b/data/mods/Aftershock/itemgroups/bionics_groups.json index f5fd0cd54948a..d264c170db869 100644 --- a/data/mods/Aftershock/itemgroups/bionics_groups.json +++ b/data/mods/Aftershock/itemgroups/bionics_groups.json @@ -4,7 +4,6 @@ "type": "item_group", "items": [ [ "bn_bio_solar", 10 ], - [ "afs_bio_wind_turbine", 5 ], [ "afs_bio_missiles", 10 ], [ "afs_bio_linguistic_coprocessor", 10 ], [ "afs_bio_dopamine_stimulators", 10 ], @@ -16,7 +15,7 @@ { "id": "bionics_common", "type": "item_group", - "items": [ [ "bn_bio_solar", 10 ], [ "afs_bio_wind_turbine", 5 ], [ "afs_bio_linguistic_coprocessor", 8 ] ] + "items": [ [ "bn_bio_solar", 10 ], [ "afs_bio_linguistic_coprocessor", 8 ] ] }, { "id": "bionics_mil", @@ -32,14 +31,13 @@ { "id": "bionics_sci", "type": "item_group", - "items": [ [ "bn_bio_solar", 5 ], [ "afs_bio_wind_turbine", 2 ] ] + "items": [ [ "bn_bio_solar", 5 ] ] }, { "id": "bionics_op", "type": "item_group", "items": [ [ "bn_bio_solar", 15 ], - [ "afs_bio_wind_turbine", 10 ], [ "afs_bio_missiles", 10 ], [ "afs_bio_melee_counteraction", 10 ], [ "afs_bio_melee_optimization_unit", 10 ], diff --git a/data/mods/Aftershock/itemgroups/clothing/winter_outfits.json b/data/mods/Aftershock/itemgroups/clothing/winter_outfits.json new file mode 100644 index 0000000000000..14dd3ca958376 --- /dev/null +++ b/data/mods/Aftershock/itemgroups/clothing/winter_outfits.json @@ -0,0 +1,37 @@ +[ + { + "//": "A group for any advanced civilian piece of clothing", + "id": "afs_wintersuit_civilian_advanced", + "type": "item_group", + "subtype": "distribution", + "items": [ { "group": "afs_wintersuit_science_advanced", "prob": 2 }, { "group": "afs_wintersuit_generic_advanced", "prob": 2 } ] + }, + { + "//": "A group for any generic-flavour advanced civilian piece of clothing", + "id": "afs_wintersuit_generic_advanced", + "type": "item_group", + "subtype": "distribution", + "items": [ { "group": "afs_frontier_cryo_g", "prob": 2 } ] + }, + { + "//": "A group for any science-flavour advanced civilian piece of clothing", + "id": "afs_wintersuit_science_advanced", + "type": "item_group", + "subtype": "distribution", + "items": [ { "group": "afs_magellan_g", "prob": 2 } ] + }, + { + "id": "afs_frontier_cryo_g", + "type": "item_group", + "//": "The matching frontier-cryosuit set. Includes suit, mask and possible future accessories", + "subtype": "collection", + "entries": [ { "item": "afs_frontier_cryo" }, { "item": "afs_frontier_cryomask", "prob": 90 } ] + }, + { + "id": "afs_magellan_g", + "type": "item_group", + "//": "The matching Magellan Exosuit set. Includes suit, mask and possible future accessories", + "subtype": "collection", + "entries": [ { "item": "afs_magellan_suit" }, { "item": "afs_magellan_suit_helmet", "prob": 90 } ] + } +] diff --git a/data/mods/Aftershock/itemgroups/item_groups.json b/data/mods/Aftershock/itemgroups/item_groups.json index ee3f72098ce77..2fb7809900440 100644 --- a/data/mods/Aftershock/itemgroups/item_groups.json +++ b/data/mods/Aftershock/itemgroups/item_groups.json @@ -77,6 +77,11 @@ "type": "item_group", "items": [ [ "afs_atompot", 3 ] ] }, + { + "id": "newspaper", + "type": "item_group", + "items": [ [ "afs_personal_card", 2 ] ] + }, { "id": "livingroom", "type": "item_group", diff --git a/data/mods/Aftershock/itemgroups/spaceship_groups.json b/data/mods/Aftershock/itemgroups/spaceship_groups.json index 53f1b65c30d41..98bd8743490a3 100644 --- a/data/mods/Aftershock/itemgroups/spaceship_groups.json +++ b/data/mods/Aftershock/itemgroups/spaceship_groups.json @@ -12,6 +12,7 @@ { "item": "whistle_multitool" }, { "item": "water_clean", "container-item": "bottle_twoliter", "count": 3 }, { "item": "afs_escapepod_ration", "count": 3 }, + { "item": "cold_res_cream", "count": 3 }, { "item": "light_plus_battery_cell", "charges": 150, "container-item": "flashlight" }, { "item": "light_plus_battery_cell", "charges": 150, "container-item": "water_purifier" }, { "item": "medium_plus_battery_cell", "ammo-item": "battery", "charges": 600, "container-item": "mil_mess_kit" }, @@ -19,7 +20,7 @@ { "item": "emer_blanket", "count": 2 }, { "item": "afs_radiobeacon" }, { "item": "afs_landfall_gun", "container-item": "back_holster" }, - { "item": "22_lr", "charges": 18 }, + { "item": "afs_7.50mm_rp", "charges": 18 }, { "item": "afs_landfall_kit_2_instructions" } ] } diff --git a/data/mods/Aftershock/itemgroups/weapons/energy_gun_groups.json b/data/mods/Aftershock/itemgroups/weapons/energy_gun_groups.json index 1516ee5b1b817..829ca018ce3d8 100644 --- a/data/mods/Aftershock/itemgroups/weapons/energy_gun_groups.json +++ b/data/mods/Aftershock/itemgroups/weapons/energy_gun_groups.json @@ -40,5 +40,97 @@ "subtype": "distribution", "ammo": 100, "items": [ [ "afs_4g_plasma", 30 ] ] + }, + { + "type": "item_group", + "id": "guns_pistol_rare", + "items": [ { "item": "afs_orion_84K", "prob": 15, "charges-min": 0, "charges-max": 20 } ] + }, + { + "type": "item_group", + "id": "guns_pistol_rare_display", + "items": [ { "item": "afs_orion_84K", "prob": 15, "charges-min": 0, "charges-max": 0 } ] + }, + { + "type": "item_group", + "id": "guns_pistol_obscure", + "items": [ { "item": "afs_orion_84K", "prob": 50, "charges-min": 0, "charges-max": 20 } ] + }, + { + "type": "item_group", + "id": "guns_rifle_rare_display", + "items": [ + { "item": "afs_gibrifle", "prob": 1, "charges-min": 0, "charges-max": 0 }, + { "item": "afs_Accipitermg", "prob": 1, "charges-min": 0, "charges-max": 0 } + ] + }, + { + "type": "item_group", + "id": "guns_rifle_rare", + "items": [ + { "item": "afs_gibrifle", "prob": 25, "charges-min": 0, "charges-max": 30 }, + { "item": "afs_Accipitermg", "prob": 20, "charges-min": 0, "charges-max": 30 } + ] + }, + { + "type": "item_group", + "id": "guns_rifle_milspec", + "items": [ + { "item": "afs_gibrifle", "prob": 50, "charges-min": 0, "charges-max": 30 }, + { "item": "afs_Accipitermg", "prob": 45, "charges-min": 0, "charges-max": 30 } + ] + }, + { + "type": "item_group", + "id": "guns_shotgun_milspec", + "items": [ { "item": "afs_gibs_shotgun", "prob": 20, "charges-min": 0, "charges-max": 40 } ] + }, + { + "type": "item_group", + "id": "mags_pistol_rare", + "items": [ [ "afs_84k_20mag", 15 ] ] + }, + { + "type": "item_group", + "id": "mags_rifle_rare", + "items": [ [ "afs_UICASTA30", 40 ], [ "afs_UICASTA100drum", 20 ] ] + }, + { + "type": "item_group", + "id": "mags_shotgun_rare", + "items": [ [ "afs_25mm_mag", 50 ] ] + }, + { + "type": "item_group", + "id": "ammo_pistol_rare", + "subtype": "distribution", + "entries": [ { "item": "afs_10mm_caseless_FMJ", "prob": 20 }, { "item": "afs_10mm_caseless_JHP", "prob": 15 } ] + }, + { + "type": "item_group", + "id": "ammo_rifle_rare", + "//": "Less common rifle ammo including that only used by police/paramilitary forces.", + "subtype": "distribution", + "entries": [ { "item": "afs_7.50mm_caseless", "prob": 40 }, { "item": "afs_7.50mm_rp", "prob": 50 } ] + }, + { + "type": "item_group", + "id": "ammo_rifle_milspec", + "subtype": "distribution", + "entries": [ { "item": "afs_7.50mm_caseless", "prob": 40 }, { "item": "afs_7.50mm_rp", "prob": 50 } ] + }, + { + "type": "item_group", + "id": "ammo_shotgun_rare", + "//": "Less common shotgun ammo including that only used by police/paramilitary forces.", + "subtype": "distribution", + "entries": [ { "item": "afs_25mm_shot", "prob": 10 } ] + }, + { + "type": "item_group", + "id": "ammo_shotgun_milspec", + "//": "Military specification shotgun ammo found at military sites.", + "subtype": "distribution", + "entries": [ { "item": "afs_25mm_shot", "prob": 10 } ] } ] diff --git a/data/mods/Aftershock/items/ammo/10mm.json b/data/mods/Aftershock/items/ammo/10mm.json new file mode 100644 index 0000000000000..6f9a8ff576377 --- /dev/null +++ b/data/mods/Aftershock/items/ammo/10mm.json @@ -0,0 +1,42 @@ +[ + { + "id": "afs_10mm_caseless_FMJ", + "type": "AMMO", + "name": { "str_sp": "10mm FMJ caseless" }, + "description": "Standardized 10mm ammunition with a brass jacketed projectile. For as long as you can remember bullets like these have been used for military, law enforcement, and civilian use. Being caseless, these cannot be disassembled or reloaded.", + "weight": "9 g", + "volume": "117 ml", + "price": 400, + "material": [ "lead" ], + "symbol": "=", + "color": "yellow", + "count": 40, + "stack_size": 40, + "ammo_type": "afs_10mm", + "range": 14, + "damage": { "damage_type": "bullet", "amount": 29, "armor_penetration": 4 }, + "dispersion": 60, + "recoil": 650, + "effects": [ "COOKOFF" ] + }, + { + "id": "afs_10mm_caseless_JHP", + "type": "AMMO", + "name": { "str_sp": "10mm JHP caseless" }, + "description": "Standardized 10mm ammunition with a hollow point projectile. While they have inferior penetration to FMJ rounds, their expansion slightly increases stopping power against unarmed targets and reduces overpenetration. Being caseless, these cannot be disassembled or reloaded.", + "weight": "7 g", + "volume": "115 ml", + "price": 150, + "material": [ "lead" ], + "symbol": "=", + "color": "yellow", + "count": 40, + "stack_size": 40, + "ammo_type": "afs_10mm", + "range": 14, + "damage": { "damage_type": "bullet", "amount": 31 }, + "dispersion": 60, + "recoil": 600, + "effects": [ "COOKOFF" ] + } +] diff --git a/data/mods/Aftershock/items/ammo/25mm.json b/data/mods/Aftershock/items/ammo/25mm.json new file mode 100644 index 0000000000000..e9d0a9c5c4b0b --- /dev/null +++ b/data/mods/Aftershock/items/ammo/25mm.json @@ -0,0 +1,23 @@ +[ + { + "id": "afs_25mm_shot", + "type": "AMMO", + "name": { "str_sp": "25mm canister shot" }, + "description": "A 25mm grenade with a canister shot load, deals devastating damage against moderately armored foes within mid to close range. Relatively low tech and affordable when compared to other 25mm grenades, these are often fielded by frontier outfits due to their adequate performance in anti-drone and close quarters combat.", + "weight": "130 g", + "volume": "230 ml", + "price": 8000, + "material": [ "superalloy", "powder", "lead" ], + "symbol": "=", + "color": "pink", + "count": 6, + "stack_size": 1, + "ammo_type": "afs_25mm", + "casing": "afs_25_casing", + "range": 24, + "damage": { "damage_type": "bullet", "amount": 60, "armor_penetration": 15 }, + "dispersion": 75, + "recoil": 2200, + "effects": [ "NEVER_MISFIRES" ] + } +] diff --git a/data/mods/Aftershock/items/ammo/7.50mm.json b/data/mods/Aftershock/items/ammo/7.50mm.json new file mode 100644 index 0000000000000..43a949af6248a --- /dev/null +++ b/data/mods/Aftershock/items/ammo/7.50mm.json @@ -0,0 +1,42 @@ +[ + { + "id": "afs_7.50mm_caseless", + "type": "AMMO", + "name": { "str_sp": "7.50mm caseless" }, + "description": "An intermediate bullet with an acceptable all-round performance. Due to their role as UICA's standard issue rifle cartridge, these are ubiquitous wherever Earth makes its presence be known. Being caseless, these cannot be disassembled or reloaded.", + "weight": "12 g", + "volume": "194 ml", + "price": 280, + "material": [ "lead" ], + "symbol": "=", + "color": "yellow", + "count": 30, + "stack_size": 47, + "ammo_type": "afs_7.50mm", + "range": 36, + "damage": { "damage_type": "bullet", "amount": 44, "armor_penetration": 2 }, + "dispersion": 30, + "recoil": 1500, + "effects": [ "COOKOFF" ] + }, + { + "id": "afs_7.50mm_rp", + "type": "AMMO", + "name": { "str_sp": "7.50mm RP" }, + "description": "Civilian-legal 7.50mm ammunition featuring a reduced propellant case and unjacketed projectiles. The 7.50mm RP round is extremely weak with very low stopping power, short range, and negligible recoil. While cheaper than military grade rounds, its only really useful for rifle training, and for hunting small animals. Being caseless, these cannot be disassembled or reloaded.", + "weight": "3 g", + "volume": "65 ml", + "price": 150, + "material": [ "lead" ], + "symbol": "=", + "color": "yellow", + "count": 80, + "stack_size": 100, + "ammo_type": "afs_7.50mm", + "range": 13, + "damage": { "damage_type": "bullet", "amount": 12 }, + "dispersion": 60, + "recoil": 150, + "effects": [ "COOKOFF" ] + } +] diff --git a/data/mods/Aftershock/items/ammo_type.json b/data/mods/Aftershock/items/ammo_type.json index 4701fc308abba..c27192d8af173 100644 --- a/data/mods/Aftershock/items/ammo_type.json +++ b/data/mods/Aftershock/items/ammo_type.json @@ -22,5 +22,23 @@ "id": "afs_shydrogen", "name": "solid hydrogen", "default": "afs_shydrogen" + }, + { + "type": "ammunition_type", + "id": "afs_25mm", + "name": "25mm", + "default": "afs_25mm_shot" + }, + { + "type": "ammunition_type", + "id": "afs_10mm", + "name": "10mm", + "default": "afs_10mm_caseless_JHP" + }, + { + "type": "ammunition_type", + "id": "afs_7.50mm", + "name": "7.50mm", + "default": "afs_7.50mm_caseless" } ] diff --git a/data/mods/Aftershock/items/armor.json b/data/mods/Aftershock/items/armor.json index 063723b459c8c..e1aa04b0d5b65 100644 --- a/data/mods/Aftershock/items/armor.json +++ b/data/mods/Aftershock/items/armor.json @@ -334,7 +334,7 @@ "type": "ARMOR", "id": "xllegpouch_large", "name": { "str": "XL leg ammo pouch", "str_pl": "XL leg ammo pouches" }, - "description": "An XL fabric ammo pouch that can be strapped to your leg and capable of holding two magazine close at hand.", + "description": "An XL fabric ammo pouch that can be strapped to your leg; capable of holding two magazines close at hand.", "weight": "120 g", "volume": "250 ml", "copy-from": "legpouch_large", diff --git a/data/mods/Aftershock/items/armor/winter_masks.json b/data/mods/Aftershock/items/armor/winter_masks.json new file mode 100644 index 0000000000000..a10b724f8639f --- /dev/null +++ b/data/mods/Aftershock/items/armor/winter_masks.json @@ -0,0 +1,127 @@ +[ + { + "id": "afs_magellan_suit_helmet", + "repairs_like": "afs_magellan_suit", + "type": "TOOL_ARMOR", + "category": "armor", + "looks_like": "helmet_motor", + "name": { "str": "Magellan helmet CA." }, + "description": "The high quality helmet of a Magellan exosuit, adapted to handle the freezing but breathable air of Salus IV. In addition to its life support functionality, it features a minor augmented reality UI overlay and a retractable gold-plated visor to protect against glare and UV light. Although not armored as such, it's strong enough to handle minor blunt impacts.", + "weight": "2500 g", + "volume": "2250 ml", + "price": "750 USD", + "to_hit": -1, + "bashing": 7, + "material": [ "plastic", "nomex" ], + "symbol": "[", + "color": "dark_gray", + "ammo": "battery", + "charges_per_use": 1, + "use_action": { + "type": "transform", + "msg": "You activate your %s.", + "target": "afs_magellan_suit_helmet_on", + "active": true, + "need_charges": 1, + "need_charges_msg": "The %s's batteries are dead." + }, + "armor_portion_data": [ + { "covers": [ "head" ], "coverage": 100, "encumbrance": 25 }, + { "covers": [ "eyes" ], "coverage": 100, "encumbrance": 5 }, + { "covers": [ "mouth" ], "coverage": 100, "encumbrance": 15 } + ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "battery": 1000 } } ], + "warmth": 10, + "material_thickness": 6, + "environmental_protection": 1, + "flags": [ + "VARSIZE", + "WATERPROOF", + "RAINPROOF", + "STURDY", + "SUN_GLASSES", + "RAD_RESIST", + "OUTER", + "SWIM_GOGGLES", + "RECHARGE", + "NO_RELOAD", + "NO_UNLOAD" + ] + }, + { + "id": "afs_magellan_suit_helmet_on", + "copy-from": "afs_magellan_suit_helmet", + "repairs_like": "afs_magellan_suit", + "type": "TOOL_ARMOR", + "name": { "str": "Magellan helmet CA. (on)", "str_pl": "Magellan helmets CA. (on)" }, + "looks_like": "helmet_motor", + "description": "The temperature control units and augmented reality overlays of this high-tech garment are currently active, and continuously draining battery power. Use it to turn them off.", + "power_draw": 8170, + "warmth": 150, + "revert_to": "afs_magellan_suit_helmet", + "use_action": { "type": "transform", "menu_text": "Turn off", "msg": "Your %s deactivates.", "target": "afs_magellan_suit_helmet" }, + "extend": { + "flags": [ "CLIMATE_CONTROL", "GAS_PROOF", "WATCH", "ALARMCLOCK", "THERMOMETER", "HYGROMETER", "PARTIAL_DEAF", "TRADER_AVOID" ] + }, + "environmental_protection": 15 + }, + { + "id": "afs_frontier_cryomask", + "repairs_like": "nomex_hood", + "type": "TOOL_ARMOR", + "category": "armor", + "looks_like": "helmet_motor", + "name": { "str": "frontier cryomask" }, + "description": "A common, industrially printed respirator cleverly retrofitted into a wearable air heater. While it adequately protects against the cold, most of its original functions have been discarded and it offers no noticeable protection against noxious fumes or other environmental hazards.", + "weight": "2500 g", + "volume": "2250 ml", + "price": "750 USD", + "to_hit": -1, + "bashing": 7, + "material": [ "plastic", "nomex" ], + "symbol": "[", + "color": "dark_gray", + "charges_per_use": 1, + "ammo": "battery", + "use_action": { + "type": "transform", + "msg": "You activate your %s.", + "target": "afs_frontier_cryomask_on", + "active": true, + "need_charges": 1, + "need_charges_msg": "The %s's batteries are dead." + }, + "armor_portion_data": [ + { "covers": [ "eyes" ], "coverage": 100, "encumbrance": 15 }, + { "covers": [ "mouth" ], "coverage": 100, "encumbrance": 20 } + ], + "pocket_data": [ + { + "pocket_type": "MAGAZINE_WELL", + "holster": true, + "rigid": true, + "max_contains_volume": "20 L", + "max_contains_weight": "20 kg", + "item_restriction": [ "medium_battery_cell", "medium_plus_battery_cell", "medium_atomic_battery_cell", "medium_disposable_cell" ] + } + ], + "warmth": 5, + "material_thickness": 2, + "environmental_protection": 2, + "flags": [ "VARSIZE", "WATERPROOF", "RAINPROOF", "STURDY", "SUN_GLASSES", "OUTER" ] + }, + { + "id": "afs_frontier_cryomask_on", + "copy-from": "afs_frontier_cryomask", + "repairs_like": "afs_frontier_cryomask", + "type": "TOOL_ARMOR", + "name": { "str": "frontier cryomask (on)", "str_pl": "frontier cryomasks (on)" }, + "looks_like": "helmet_motor", + "description": "The heater of this high-tech garment is currently active, and continuously draining battery power. Use it to turn the heat off.", + "power_draw": 6944, + "warmth": 150, + "revert_to": "afs_frontier_cryomask", + "use_action": { "type": "transform", "menu_text": "Turn off", "msg": "Your %s deactivates.", "target": "afs_frontier_cryomask" }, + "extend": { "flags": [ "CLIMATE_CONTROL" ] } + } +] diff --git a/data/mods/Aftershock/items/armor/winter_suits.json b/data/mods/Aftershock/items/armor/winter_suits.json new file mode 100644 index 0000000000000..d8a607ba579dc --- /dev/null +++ b/data/mods/Aftershock/items/armor/winter_suits.json @@ -0,0 +1,126 @@ +[ + { + "id": "afs_magellan_suit", + "type": "TOOL_ARMOR", + "category": "armor", + "name": { "str": "Magellan exosuit" }, + "description": "A high-quality, civilian grade EVA suit often employed by well-established frontier research and exploration associations. Designed to support the exploration of challenging terrain, it offers respectable protection against common environmental hazards like extreme temperatures, inhospitable atmospheres, and light radiation. It leaves arms and hands relatively unencumbered to aid the manipulation of scientific instruments.\n\nAn integral battery allows the suit to operate for up to 34 hours, but complicates field recharging.", + "weight": "7800 g", + "volume": "14 L", + "price": "4 kUSD", + "material": [ "nomex", "steel" ], + "symbol": "[", + "looks_like": "robofac_enviro_suit", + "color": "light_gray", + "ammo": "battery", + "charges_per_use": 1, + "use_action": { + "type": "transform", + "msg": "You activate your %s.", + "target": "afs_magellan_suit_on", + "active": true, + "need_charges": 1, + "need_charges_msg": "The %s's batteries are dead." + }, + "armor_portion_data": [ + { "covers": [ "torso" ], "coverage": 100, "encumbrance": 25 }, + { "covers": [ "leg_l", "leg_r" ], "coverage": 100, "encumbrance": 25 }, + { "covers": [ "arm_l", "arm_r" ], "coverage": 100, "encumbrance": 15 }, + { "covers": [ "hand_l", "hand_r" ], "coverage": 100, "encumbrance": 10 }, + { "covers": [ "foot_l", "foot_r" ], "coverage": 100, "encumbrance": 15 } + ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "battery": 1000 } } ], + "warmth": 20, + "material_thickness": 2, + "valid_mods": [ "steel_padded" ], + "environmental_protection": 15, + "flags": [ + "VARSIZE", + "WATERPROOF", + "GAS_PROOF", + "POCKETS", + "RAINPROOF", + "STURDY", + "RAD_RESIST", + "RECHARGE", + "OUTER", + "NO_RELOAD", + "NO_UNLOAD" + ] + }, + { + "id": "afs_magellan_suit_on", + "copy-from": "afs_magellan_suit", + "repairs_like": "afs_magellan_suit", + "type": "TOOL_ARMOR", + "name": { "str": "Magellan exosuit (on)", "str_pl": "Magellan exosuits (on)" }, + "looks_like": "afs_cryopod_bodyglove", + "description": "The temperature control units of this high-tech garment are currently active, and continuously draining battery power. Use it to turn them off.", + "power_draw": 8170, + "warmth": 150, + "revert_to": "afs_magellan_suit", + "use_action": { "type": "transform", "menu_text": "Turn off", "msg": "Your %s deactivates.", "target": "afs_magellan_suit" }, + "extend": { "flags": [ "CLIMATE_CONTROL" ] } + }, + { + "id": "afs_frontier_cryo", + "type": "TOOL_ARMOR", + "category": "armor", + "name": { "str": "frontier cryo suit" }, + "description": "A sturdy suit meant to protect against the freezing cold, made from a pair of jumpsuits that have been woven around a heavy insulation layer and crisscrossed with the thermal tubing of a heat regulation unit. The thick insulation allows the suit to function with unrivaled efficiency, but also makes all types of movement difficult.", + "weight": "7800 g", + "volume": "14 L", + "price": "75 USD", + "material": [ "cotton", "plastic" ], + "symbol": "[", + "looks_like": "robofac_enviro_suit", + "color": "cyan", + "ammo": "battery", + "charges_per_use": 1, + "use_action": { + "type": "transform", + "msg": "You activate your %s.", + "target": "afs_frontier_cryo_on", + "active": true, + "need_charges": 1, + "need_charges_msg": "The %s's batteries are dead." + }, + "armor_portion_data": [ + { "covers": [ "head" ], "coverage": 100, "encumbrance": 5 }, + { "covers": [ "torso" ], "coverage": 100, "encumbrance": 35 }, + { "covers": [ "leg_l", "leg_r" ], "coverage": 100, "encumbrance": 25 }, + { "covers": [ "arm_l", "arm_r" ], "coverage": 100, "encumbrance": 25 }, + { "covers": [ "hand_l", "hand_r" ], "coverage": 100, "encumbrance": 25 }, + { "covers": [ "foot_l", "foot_r" ], "coverage": 100, "encumbrance": 15 } + ], + "pocket_data": [ + { + "pocket_type": "MAGAZINE_WELL", + "holster": true, + "rigid": true, + "max_contains_volume": "20 L", + "max_contains_weight": "20 kg", + "item_restriction": [ "medium_battery_cell", "medium_plus_battery_cell", "medium_atomic_battery_cell", "medium_disposable_cell" ] + } + ], + "warmth": 20, + "material_thickness": 4, + "valid_mods": [ "steel_padded" ], + "environmental_protection": 2, + "flags": [ "VARSIZE", "WATERPROOF", "POCKETS", "HELMET_COMPAT", "RAINPROOF", "STURDY", "OUTER" ] + }, + { + "id": "afs_frontier_cryo_on", + "copy-from": "afs_frontier_cryo", + "repairs_like": "afs_frontier_cryo", + "type": "TOOL_ARMOR", + "name": { "str": "frontier cryo suit (on)", "str_pl": "frontier cryo suits (on)" }, + "looks_like": "afs_cryopod_bodyglove", + "description": "The temperature control units of this high-tech garment are currently active, and continuously draining battery power. Use it to turn them off.", + "power_draw": 6944, + "warmth": 150, + "revert_to": "afs_frontier_cryo", + "use_action": { "type": "transform", "menu_text": "Turn off", "msg": "Your %s deactivates.", "target": "afs_frontier_cryo" }, + "extend": { "flags": [ "CLIMATE_CONTROL" ] } + } +] diff --git a/data/mods/Aftershock/items/books.json b/data/mods/Aftershock/items/books.json index abc292f69fb8f..207f3f928304f 100644 --- a/data/mods/Aftershock/items/books.json +++ b/data/mods/Aftershock/items/books.json @@ -114,7 +114,7 @@ "id": "cyrus'_notes", "type": "BOOK", "name": { "str": "Cyrus Whateley's Notes on Repurposing mi-go victims", "str_pl": "copies of Cyrus Whateley's Notes" }, - "description": "A folio of notes written by a madman. They seem to suggestion ways to re-animate the dead and create various affronts to the natural order.", + "description": "A folio of notes written by a madman. They seem to suggest ways to re-animate the dead and create various affronts to the natural order.", "weight": "2063 g", "volume": "2 L", "price": 929200, diff --git a/data/mods/Aftershock/items/casing.json b/data/mods/Aftershock/items/casing.json new file mode 100644 index 0000000000000..581288094ed15 --- /dev/null +++ b/data/mods/Aftershock/items/casing.json @@ -0,0 +1,15 @@ +[ + { + "id": "afs_25_casing", + "type": "GENERIC", + "category": "spare_parts", + "name": { "str": "25mm casing" }, + "description": "A large casing from a spent 25mm cartridge.", + "weight": "50 g", + "volume": "45ml", + "material": [ "superalloy" ], + "symbol": "=", + "stackable": true, + "color": "dark_gray" + } +] diff --git a/data/mods/Aftershock/items/cbms.json b/data/mods/Aftershock/items/cbms.json index 627339be2c031..77695b5adf4eb 100644 --- a/data/mods/Aftershock/items/cbms.json +++ b/data/mods/Aftershock/items/cbms.json @@ -8,15 +8,6 @@ "price": 350000, "difficulty": 4 }, - { - "id": "afs_bio_wind_turbine", - "copy-from": "bionic_general", - "type": "BIONIC_ITEM", - "name": { "str": "Wind Turbine CBM" }, - "description": "Installed on your body is a set of small retractable wind turbines. When activated, they will deploy and slowly harvest wind power to recharge your power level.", - "price": 350000, - "difficulty": 4 - }, { "id": "afs_bio_missiles", "copy-from": "bionic_general", diff --git a/data/mods/Aftershock/items/comestibles/alienfood.json b/data/mods/Aftershock/items/comestibles/alienfood.json index 387e9678d112f..fca6482f85682 100644 --- a/data/mods/Aftershock/items/comestibles/alienfood.json +++ b/data/mods/Aftershock/items/comestibles/alienfood.json @@ -236,7 +236,7 @@ "copy-from": "flesh", "type": "COMESTIBLE", "name": "alien stomach", - "description": "A balloon like organ you can only guess is a stomach. It is surprisingly durable.", + "description": "A balloon-like organ you can only guess is a stomach. It is surprisingly durable.", "weight": "72 g", "volume": "250 ml", "price_postapoc": 25, @@ -254,7 +254,7 @@ "copy-from": "alien_stomach", "type": "COMESTIBLE", "name": "large alien stomach", - "description": "A balloon like organ of a large alien. It is surprisingly durable.", + "description": "A balloon-like organ of a large alien you can only guess is a stomach. It is surprisingly durable.", "proportional": { "weight": 2.0, "volume": 2.0, "price": 1.5, "calories": 2.0 } }, { diff --git a/data/mods/Aftershock/items/comestibles/bug_brew.json b/data/mods/Aftershock/items/comestibles/bug_brew.json index 36657471e1eff..b4cc64ef268ed 100644 --- a/data/mods/Aftershock/items/comestibles/bug_brew.json +++ b/data/mods/Aftershock/items/comestibles/bug_brew.json @@ -2,8 +2,8 @@ { "type": "COMESTIBLE", "id": "afs_mealgrub_medium", - "name": { "str": "mealgurb growth medium" }, - "description": "All sort of organic substances rendered into a nutritious goop that can be used as a growth medium in grub aquaculture. Urban myth claims that its original formula was just bottled industrial runoff. Whatever the case, mealgrubs thrive within it.", + "name": { "str": "mealgrub growth medium" }, + "description": "All sorts of organic substances rendered into a nutritious goop that can be used as a growth medium in grub aquaculture. Urban myth claims that its original formula was just bottled industrial runoff. Whatever the case, mealgrubs thrive within it.", "weight": "100 g", "container": "jug_plastic", "comestible_type": "DRINK", @@ -25,8 +25,8 @@ { "type": "COMESTIBLE", "id": "afs_mealgrub_spores", - "name": { "str_sp": "mealgurb spores" }, - "description": "Mealgrubs are a staple aquacultural product, treasured for their ability to process nearly any organic substance into safe for consumption animal protein. As the result of very heavy handed gene-modding these are propietary organism, and strictly speaking, its illegal to grow them without a licence.", + "name": { "str_sp": "mealgrub spores" }, + "description": "Mealgrubs are a staple aquacultural product, treasured for their ability to process nearly any organic substance into safe for consumption animal protein. As the result of very heavy-handed gene-modding these are proprietary organisms, and strictly speaking, it's illegal to grow them without a license.", "weight": "100 g", "container": "bottle_plastic_small", "comestible_type": "DRINK", @@ -99,7 +99,7 @@ "calories": 360, "healthy": 3, "charges": 5, - "description": "A handful of edible and heavily genemoded grubs, each only slightly thinner than your thumb. Engineered in the wake of the XXII century to feed an overpopulated Earth and its fledging colonies these are sturdy, easily grown and scalable crop.\n\nRemoved from their growth solution they'll quickly die and desiccate, as these already have. They could be safely be eaten with no further processing, for a nutritious if somewhat striking meal.", + "description": "A handful of edible and heavily genemoded grubs, each only slightly thinner than your thumb. Engineered in the wake of the XXII century to feed an overpopulated Earth and its fledgling colonies these are sturdy, easily grown and scalable crop.\n\nRemoved from their growth solution they'll quickly die and desiccate, as these already have. They could be safely eaten with no further processing, for a nutritious if somewhat striking meal.", "fun": 0, "freezing_point": -9999, "vitamins": [ [ "calcium", 2 ], [ "iron", 2 ], [ "vitA", 1 ], [ "vitB", 2 ], [ "bad_food", 1 ] ] diff --git a/data/mods/Aftershock/items/comestibles/cheap_food.json b/data/mods/Aftershock/items/comestibles/cheap_food.json index b5a15933f321b..e2967601a7aee 100644 --- a/data/mods/Aftershock/items/comestibles/cheap_food.json +++ b/data/mods/Aftershock/items/comestibles/cheap_food.json @@ -56,7 +56,7 @@ "weight": "10 g", "color": "yellow", "healthy": 2, - "description": "An golden can of sundew, the leading soft drink of the world. Staring at the can, you feel a craving so intense that you are unsure whether its genuine, or if it has been carefully engineered and implanted into your mind.", + "description": "A golden can of Sundew, the leading soft drink of the world. Staring at the can, you feel a craving so intense that you are unsure whether it's genuine, or if it has been carefully engineered and implanted into your mind.", "price": 1000, "looks_like": "diesel", "container": "can_drink", diff --git a/data/mods/Aftershock/items/comestibles/comestibles.json b/data/mods/Aftershock/items/comestibles/comestibles.json index f6c008a621f9f..601a46c16c416 100644 --- a/data/mods/Aftershock/items/comestibles/comestibles.json +++ b/data/mods/Aftershock/items/comestibles/comestibles.json @@ -45,7 +45,7 @@ { "id": "cream_prot_cold", "name": { "str": "heat retention cream" }, - "description": "Rub this on your skin when you are expecting exposure extreme cold temperatures. Cannot protect from instant frostbite. Smells like the inside of a sheep.", + "description": "Rub this on your skin when you are expecting exposure to extreme cold temperatures. Cannot protect from instant frostbite. Smells like the inside of a sheep.", "use_action": { "type": "cast_spell", "spell_id": "cream_prot_cold", "no_fail": true, "level": 0 }, "type": "COMESTIBLE", "weight": "265 g", diff --git a/data/mods/Aftershock/items/ethereal.json b/data/mods/Aftershock/items/ethereal.json index bdcdd089fdaa1..3ae15d5addf67 100644 --- a/data/mods/Aftershock/items/ethereal.json +++ b/data/mods/Aftershock/items/ethereal.json @@ -8,10 +8,10 @@ "volume": "1ml", "price": 3646, "symbol": "o", + "warmth": 150, "color": "green", "covers": [ "leg_l", "leg_r", "torso", "arm_l", "arm_r", "hand_l", "hand_r", "head", "foot_l", "foot_r", "mouth", "eyes" ], - "flags": [ "AURA", "SEMITANGIBLE", "OVERSIZE", "ONLY_ONE", "TRADER_AVOID", "NO_TAKEOFF", "NONCONDUCTIVE" ], - "relic_data": { "passive_effects": [ { "has": "WORN", "condition": "ALWAYS", "mutations": [ "AFS_CRYOADAPTATION" ] } ] } + "flags": [ "AURA", "SEMITANGIBLE", "OVERSIZE", "ONLY_ONE", "TRADER_AVOID", "NO_TAKEOFF", "CLIMATE_CONTROL", "NONCONDUCTIVE" ] }, { "id": "cold_res_cream_greater", @@ -22,9 +22,10 @@ "volume": "1ml", "price": 3646, "symbol": "o", + "warmth": 150, "color": "green", "covers": [ "leg_l", "leg_r", "torso", "arm_l", "arm_r", "hand_l", "hand_r", "head", "foot_l", "foot_r", "mouth", "eyes" ], - "flags": [ "AURA", "SEMITANGIBLE", "OVERSIZE", "ONLY_ONE", "TRADER_AVOID", "NO_TAKEOFF", "NONCONDUCTIVE" ], + "flags": [ "AURA", "SEMITANGIBLE", "OVERSIZE", "ONLY_ONE", "TRADER_AVOID", "NO_TAKEOFF", "CLIMATE_CONTROL", "NONCONDUCTIVE" ], "relic_data": { "passive_effects": [ { "has": "WORN", "condition": "ALWAYS", "values": [ { "value": "ARMOR_COLD", "multiply": -0.25 } ] } ] } diff --git a/data/mods/Aftershock/items/gun/10mm.json b/data/mods/Aftershock/items/gun/10mm.json new file mode 100644 index 0000000000000..1dfe73939b256 --- /dev/null +++ b/data/mods/Aftershock/items/gun/10mm.json @@ -0,0 +1,35 @@ +[ + { + "id": "afs_orion_84K", + "copy-from": "pistol_base", + "looks_like": "glock_17", + "type": "GUN", + "name": { "str": "Seyfert 84K" }, + "description": "Commonly carried by shipping crews that can afford it, Seyfert Armistice's 84K pistol doesn't particularly shine or show anything special but it keeps you safe on long journeys in the vast dark of Orion's Arm.", + "weight": "780 g", + "volume": "480 ml", + "longest_side": "205 mm", + "price": 59200, + "to_hit": -2, + "bashing": 8, + "material": [ "plastic", "superalloy" ], + "symbol": "(", + "color": "dark_gray", + "ammo": "afs_10mm", + "ranged_damage": { "damage_type": "bullet", "amount": -5 }, + "dispersion": 480, + "durability": 8, + "blackpowder_tolerance": 48, + "min_cycle_recoil": 570, + "pocket_data": [ + { + "magazine_well": "103 ml", + "pocket_type": "MAGAZINE_WELL", + "holster": true, + "max_contains_volume": "20 L", + "max_contains_weight": "20 kg", + "item_restriction": [ "afs_84k_20mag" ] + } + ] + } +] diff --git a/data/mods/Aftershock/items/gun/25mm.json b/data/mods/Aftershock/items/gun/25mm.json new file mode 100644 index 0000000000000..5868bc0ba49d6 --- /dev/null +++ b/data/mods/Aftershock/items/gun/25mm.json @@ -0,0 +1,47 @@ +[ + { + "id": "afs_gibs_shotgun", + "looks_like": "m79", + "type": "GUN", + "reload_noise_volume": 10, + "name": { "str": "Gibson S86" }, + "description": "Manufactured by Gibson Armory, the S86 is a light-weight grenade launcher designed to take care of moderately armored opponents. While the S86 firepower is nothing to truly gawk at, it still packs quite a punch both for the target and user and is usually mounted as it is impossible to fire with average strength.", + "weight": "2700 g", + "volume": "2800 ml", + "longest_side": "736 mm", + "price": 550000, + "to_hit": -3, + "bashing": 10, + "material": [ "superalloy", "plastic" ], + "symbol": "(", + "color": "brown", + "min_strength": 18, + "ranged_damage": { "damage_type": "bullet", "amount": 10 }, + "skill": "launcher", + "modes": [ [ "DEFAULT", "DOUBLE", 4 ] ], + "dispersion": 90, + "durability": 6, + "ammo": [ "afs_25mm" ], + "clip_size": 1, + "pocket_data": [ + { + "pocket_type": "MAGAZINE_WELL", + "holster": true, + "max_contains_volume": "20 L", + "max_contains_weight": "20 kg", + "item_restriction": [ "afs_25mm_mag" ] + } + ], + "reload": 400, + "valid_mod_locations": [ + [ "accessories", 2 ], + [ "barrel", 1 ], + [ "sights", 1 ], + [ "sling", 1 ], + [ "grip mount", 1 ], + [ "rail mount", 1 ], + [ "stock mount", 1 ], + [ "underbarrel mount", 1 ] + ] + } +] diff --git a/data/mods/Aftershock/items/gun/5x50.json b/data/mods/Aftershock/items/gun/5x50.json index 503efee079558..b71d8d237ec53 100644 --- a/data/mods/Aftershock/items/gun/5x50.json +++ b/data/mods/Aftershock/items/gun/5x50.json @@ -11,7 +11,7 @@ "valid_mod_locations": [ [ "accessories", 2 ] ], "modes": [ [ "DEFAULT", "single", 1 ], [ "MULTI", "4 rd.", 4 ] ], "clip_size": 4, - "flags": [ "NEVER_JAMS", "RELOAD_ONE", "NEVER_JAMS", "RELOAD_EJECT" ], + "flags": [ "NEVER_JAMS", "RELOAD_ONE", "RELOAD_EJECT" ], "pocket_data": [ { "pocket_type": "MAGAZINE", "ammo_restriction": { "5x50": 4 } } ] } ] diff --git a/data/mods/Aftershock/items/gun/7.50mm.json b/data/mods/Aftershock/items/gun/7.50mm.json new file mode 100644 index 0000000000000..483e77a8f31f9 --- /dev/null +++ b/data/mods/Aftershock/items/gun/7.50mm.json @@ -0,0 +1,98 @@ +[ + { + "id": "afs_landfall_gun", + "type": "GUN", + "copy-from": "rifle_22", + "name": { "str": "landfall survival gun" }, + "description": "A lightweight, bolt-action gun that is barely more than a 6.55mm barrel mounted within a flimsy metal assembly. Cheaply made and outdated in all respects, it's only useful in the most extenuating of circumstances.", + "price": 5000, + "material": [ "steel", "plastic" ], + "flags": [ "NEVER_JAMS", "RELOAD_EJECT" ], + "dispersion": 250, + "longest_side": "60 cm", + "reload": 100, + "durability": 5, + "blackpowder_tolerance": 60, + "ammo": [ "afs_7.50mm" ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "afs_7.50mm": 1 } } ], + "faults": [ "fault_gun_blackpowder", "fault_gun_dirt" ] + }, + { + "id": "afs_Accipitermg", + "copy-from": "rifle_auto", + "looks_like": "ar15", + "type": "GUN", + "name": { "str": "Accipiter Hawk-00" }, + "description": "A light machine gun manufactured by Accipiter Firearms and distributed by Wraitheon. The Hawk-00's high rate of fire makes it ideally suited to lay down suppressive fire; but also makes it exceedingly hard to control while unmounted.", + "weight": "7500 g", + "volume": "10610 ml", + "longest_side": "1035 mm", + "price": 750000, + "to_hit": -1, + "bashing": 12, + "material": [ "superalloy", "plastic" ], + "symbol": "(", + "color": "dark_gray", + "ammo": [ "afs_7.50mm" ], + "dispersion": 200, + "durability": 7, + "min_cycle_recoil": 1350, + "reload": 400, + "modes": [ [ "DEFAULT", "burst", 4 ] ], + "built_in_mods": [ "bipod" ], + "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 ] + ], + "pocket_data": [ + { + "pocket_type": "MAGAZINE_WELL", + "holster": true, + "max_contains_volume": "20 L", + "max_contains_weight": "20 kg", + "item_restriction": [ "afs_UICASTA30", "afs_UICASTA100drum" ] + } + ] + }, + { + "id": "afs_gibrifle", + "copy-from": "rifle_semi", + "looks_like": "ar15", + "type": "GUN", + "name": { "str": "G&W SR-P9" }, + "description": "An eminently sturdy and reliable bullpup rifle issued by UICA's military during early reclamation expeditions. Although its been out of service for nearly two decades, many a private military and contractor has found a use for the SR-P9, and it remains common long past its initial introduction into service.", + "weight": "3170 g", + "volume": "3100 ml", + "longest_side": "630 mm", + "price": 125000, + "to_hit": -1, + "bashing": 12, + "material": [ "superalloy", "plastic" ], + "ranged_damage": { "damage_type": "bullet", "amount": -7 }, + "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "BURST", "burst", 2 ] ], + "symbol": "(", + "color": "dark_gray", + "ammo": [ "afs_7.50mm" ], + "dispersion": 150, + "durability": 10, + "min_cycle_recoil": 1350, + "pocket_data": [ + { + "pocket_type": "MAGAZINE_WELL", + "holster": true, + "max_contains_volume": "20 L", + "max_contains_weight": "20 kg", + "item_restriction": [ "afs_UICASTA30" ] + } + ] + } +] diff --git a/data/mods/Aftershock/items/gun/combination.json b/data/mods/Aftershock/items/gun/combination.json deleted file mode 100644 index 694a5f95fea72..0000000000000 --- a/data/mods/Aftershock/items/gun/combination.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "id": "afs_landfall_gun", - "type": "GUN", - "copy-from": "rifle_22", - "name": { "str": "landfall survival gun" }, - "description": "A lightweight, bolt-action gun that is barely more than a .22 barrel mounted within a flimsy metal assembly. Cheaply made and outdated in all respects, it's only useful in the most extenuating of circumstances.", - "price": 5000, - "price_postapoc": 5000, - "material": [ "steel", "plastic" ], - "flags": [ "NEVER_JAMS", "RELOAD_EJECT" ], - "dispersion": 250, - "longest_side": "60 cm", - "reload": 100, - "durability": 5, - "blackpowder_tolerance": 60, - "faults": [ "fault_gun_blackpowder", "fault_gun_dirt" ] - } -] diff --git a/data/mods/Aftershock/items/gun/laser.json b/data/mods/Aftershock/items/gun/laser.json index 1f73494f232db..f661f9c21a156 100644 --- a/data/mods/Aftershock/items/gun/laser.json +++ b/data/mods/Aftershock/items/gun/laser.json @@ -22,7 +22,7 @@ "type": "GUN", "copy-from": "afs_sentinel_stunner", "name": { "str": "wrist-trilaser" }, - "description": "A powerful tri-barreled laser weapon, still mounted to the robotic hand of the wraitheon drone it originally belonged too. Can still be fired when connected to an UPS.", + "description": "A powerful tri-barreled laser weapon, still mounted to the robotic hand of the wraitheon drone it originally belonged to. Can still be fired when connected to an UPS.", "color": "red", "range": 10, "ranged_damage": { "damage_type": "heat", "amount": 25, "armor_penetration": 4 }, @@ -126,7 +126,7 @@ "type": "GUN", "name": { "str": "Av-22" }, "copy-from": "v29", - "description": "A common pulse-laser pistol cleared for civilian use. Recoiless, energy-efficient, and easy to use, the av-22 is often carried as a defense weapon by those who lack the practice required to effectively use ballistic weaponry. Due to its low power, it must be fired in sequence mode to stand a chance against hardened foes.", + "description": "A common pulse-laser pistol cleared for civilian use. Recoilless, energy-efficient, and easy to use, the av-22 is often carried as a defense weapon by those who lack the practice required to effectively use ballistic weaponry. Due to its low power, it must be fired in sequence mode to stand a chance against hardened foes.", "price": "500 USD", "price_postapoc": "500 USD", "ammo": [ "battery" ], diff --git a/data/mods/Aftershock/items/gunmods/laser_gunmods.json b/data/mods/Aftershock/items/gunmods/laser_gunmods.json index 3e24a390659a6..1a166a6ab7779 100644 --- a/data/mods/Aftershock/items/gunmods/laser_gunmods.json +++ b/data/mods/Aftershock/items/gunmods/laser_gunmods.json @@ -4,7 +4,7 @@ "type": "GUNMOD", "copy-from": "electrolaser_conversion", "name": { "str": "electrolaser conversion" }, - "description": "A set of high-tech electronics and optics. This convert a laser pistol into a less-lethal electrolaser capable of stunning targets, at the cost of decreased damage output and increased power consumption.", + "description": "A set of high-tech electronics and optics. This converts a laser pistol into a less-lethal electrolaser capable of stunning targets, at the cost of decreased damage output and increased power consumption.", "damage_modifier": { "damage_type": "heat", "amount": -4 }, "range_modifier": -10, "ammo_to_fire_multiplier": 1.2 diff --git a/data/mods/Aftershock/items/items.json b/data/mods/Aftershock/items/items.json index 72acc78c9a8d6..2850b072dc91f 100644 --- a/data/mods/Aftershock/items/items.json +++ b/data/mods/Aftershock/items/items.json @@ -159,7 +159,7 @@ "id": "afs_titanium_tooth", "type": "GENERIC", "name": { "str": "titanium tooth" }, - "description": "A dental implant made of pure titanium, used to replace teeth due to its bio-compatibility and durability.", + "description": "A dental implant made of pure titanium, used to replace teeth due to its biocompatibility and durability.", "price": 5000, "price_postapoc": 50, "weight": "10 g", diff --git a/data/mods/Aftershock/items/lore_items.json b/data/mods/Aftershock/items/lore_items.json new file mode 100644 index 0000000000000..3f7869e9aa7a6 --- /dev/null +++ b/data/mods/Aftershock/items/lore_items.json @@ -0,0 +1,17 @@ +[ + { + "type": "GENERIC", + "id": "afs_personal_card", + "category": "books", + "symbol": ",", + "color": "red", + "name": { "str": "personal message card" }, + "snippet_category": "afs_personal_card", + "description": "A creased card with images and a personal message.", + "price": 0, + "price_postapoc": 0, + "material": [ "paper" ], + "weight": "3 g", + "volume": "5 ml" + } +] diff --git a/data/mods/Aftershock/items/magazine/10mm.json b/data/mods/Aftershock/items/magazine/10mm.json new file mode 100644 index 0000000000000..e211e6613d77e --- /dev/null +++ b/data/mods/Aftershock/items/magazine/10mm.json @@ -0,0 +1,19 @@ +[ + { + "id": "afs_84k_20mag", + "looks_like": "glock17_17", + "type": "MAGAZINE", + "name": { "str": "84k 10mm 20-round magazine" }, + "description": "A 20-round magazine for use with the Seyfert 84k.", + "weight": "70 g", + "volume": "103 ml", + "longest_side": "114 mm", + "price": 2400, + "material": [ "plastic", "superalloy" ], + "symbol": "#", + "color": "light_gray", + "ammo_type": [ "afs_10mm" ], + "flags": [ "MAG_COMPACT" ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "afs_10mm": 20 } } ] + } +] diff --git a/data/mods/Aftershock/items/magazine/25mm.json b/data/mods/Aftershock/items/magazine/25mm.json new file mode 100644 index 0000000000000..64c3ac9736f93 --- /dev/null +++ b/data/mods/Aftershock/items/magazine/25mm.json @@ -0,0 +1,18 @@ +[ + { + "id": "afs_25mm_mag", + "looks_like": "stanag30", + "type": "MAGAZINE", + "name": { "str": "25mm 40-round magazine" }, + "description": "A 40-round magazine for use with 25mm firearms.", + "weight": "340 g", + "volume": "2 L", + "price": 70000, + "material": [ "superalloy", "plastic" ], + "symbol": "#", + "color": "dark_gray", + "ammo_type": [ "afs_25mm" ], + "reload_time": 60, + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "afs_25mm": 40 } } ] + } +] diff --git a/data/mods/Aftershock/items/magazine/7.50mm.json b/data/mods/Aftershock/items/magazine/7.50mm.json new file mode 100644 index 0000000000000..0967b6f04edd1 --- /dev/null +++ b/data/mods/Aftershock/items/magazine/7.50mm.json @@ -0,0 +1,37 @@ +[ + { + "id": "afs_UICASTA30", + "looks_like": "stanag30", + "type": "MAGAZINE", + "name": { "str": "UICASTA 30-round magazine" }, + "description": "A standardized 30-round magazine compatible with most UICA issue rifles.", + "weight": "265 g", + "volume": "290 ml", + "longest_side": "190 mm", + "price": 8000, + "material": [ "superalloy" ], + "symbol": "#", + "color": "light_gray", + "ammo_type": [ "afs_7.50mm" ], + "flags": [ "MAG_COMPACT" ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "afs_7.50mm": 30 } } ] + }, + { + "id": "afs_UICASTA100drum", + "looks_like": "stanag30", + "type": "MAGAZINE", + "name": { "str": "UICASTA 100-round double drum magazine" }, + "description": "A standardized 100-round double drum magazine compatible with most UICA issue rifles.", + "weight": "1700 g", + "volume": "1200 ml", + "longest_side": "300 mm", + "price": 14000, + "material": [ "plastic", "superalloy" ], + "symbol": "#", + "color": "light_gray", + "ammo_type": [ "afs_7.50mm" ], + "reload_time": 200, + "flags": [ "MAG_BULKY" ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "afs_7.50mm": 100 } } ] + } +] diff --git a/data/mods/Aftershock/items/magazine/8x40mm.json b/data/mods/Aftershock/items/magazine/8x40mm.json index 7ce95637cb29a..601ae05442828 100644 --- a/data/mods/Aftershock/items/magazine/8x40mm.json +++ b/data/mods/Aftershock/items/magazine/8x40mm.json @@ -4,7 +4,7 @@ "looks_like": "38_speedloader", "type": "MAGAZINE", "name": { "str": "RMGS5 8x40mm speedloader" }, - "description": "This speedloader, made by Rivtech for use with RM99 revolver, can hold 5 rounds of 8x40mm caseless rounds and quickly reload a compatible revolver.", + "description": "This speedloader, made by Rivtech for use with the RM99 revolver, can hold 5 rounds of 8x40mm caseless rounds and quickly reload a compatible revolver.", "weight": "92 g", "volume": "250 ml", "price": 8000, diff --git a/data/mods/Aftershock/items/magazine/alien.json b/data/mods/Aftershock/items/magazine/alien.json index f9b12cb95b11b..7fbeaab6184aa 100644 --- a/data/mods/Aftershock/items/magazine/alien.json +++ b/data/mods/Aftershock/items/magazine/alien.json @@ -8,7 +8,6 @@ "weight": "1000 g", "volume": "1225ml", "price": 100000, - "price_postapoc": 5000, "material": [ "iron", "plastic" ], "symbol": "=", "color": "yellow", diff --git a/data/mods/Aftershock/items/obsolete.json b/data/mods/Aftershock/items/obsolete.json index a2193f7e28f8c..4a8398257ceae 100644 --- a/data/mods/Aftershock/items/obsolete.json +++ b/data/mods/Aftershock/items/obsolete.json @@ -621,6 +621,15 @@ "intelligence": 5, "time": "18 m" }, + { + "id": "afs_bio_wind_turbine", + "copy-from": "bionic_general", + "type": "BIONIC_ITEM", + "name": { "str": "Wind Turbine CBM" }, + "description": "Installed on your body is a set of small retractable wind turbines. When activated, they will deploy and slowly harvest wind power to recharge your power level.", + "price": 350000, + "difficulty": 4 + }, { "id": "afs_brigandine_crafted", "type": "ARMOR", diff --git a/data/mods/Aftershock/items/tools.json b/data/mods/Aftershock/items/tools.json index c5478a27f0976..634d7b151cc72 100644 --- a/data/mods/Aftershock/items/tools.json +++ b/data/mods/Aftershock/items/tools.json @@ -134,7 +134,7 @@ "copy-from": "afs_power_cutter", "type": "TOOL", "name": { "str": "power cutter (on)", "str_pl": "power cutters (on)" }, - "description": "A huge, gas-powered saw with a diamond blade, currently a whirling blur. It's consuming gasoline, but can be used a fantastic tool for cutting metal. You can also use it as a terrifying weapon, if you're into that sort of thing.", + "description": "A huge, gas-powered saw with a diamond blade, currently a whirling blur. It's consuming gasoline, but can be used as a fantastic tool for cutting metal. You can also use it as a terrifying weapon, if you're into that sort of thing.", "cutting": 80, "turns_per_charge": 3, "revert_to": "afs_power_cutter", diff --git a/data/mods/Aftershock/items/weapons.json b/data/mods/Aftershock/items/weapons.json index d54d0bd5df043..fab508133ca3c 100644 --- a/data/mods/Aftershock/items/weapons.json +++ b/data/mods/Aftershock/items/weapons.json @@ -86,7 +86,7 @@ "looks_like": "recurbow", "color": "white", "name": { "str": "Alien lune" }, - "description": "A delicate strand of light links the antipodes of this cresent-shaped device. If the strand is interrupted, the device clanks and shudders before violently emitting a bolt of plasma from its apex.", + "description": "A delicate strand of light links the antipodes of this crescent-shaped device. If the strand is interrupted, the device clanks and shudders before violently emitting a bolt of plasma from its apex.", "price": 1000000, "price_postapoc": 20000, "material": [ "superalloy", "hardsteel" ], diff --git a/data/mods/Aftershock/maps/furniture.json b/data/mods/Aftershock/maps/furniture.json index 25e5c9d6ad353..0331899aa3867 100644 --- a/data/mods/Aftershock/maps/furniture.json +++ b/data/mods/Aftershock/maps/furniture.json @@ -4,7 +4,7 @@ "id": "f_afs_escape_pod_seat", "name": "escape pod seat", "copy-from": "f_seat_airplane", - "description": "The cushioned and upright facing seat of single-seater escape pod. A storage space beneath it holds survival supplies.", + "description": "The cushioned and upright facing seat of a single-seater escape pod. A storage space beneath it holds survival supplies.", "symbol": "H", "color": "light_red", "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "ORGANIC", "MOUNTABLE", "CAN_SIT" ], @@ -358,7 +358,7 @@ "id": "f_neuralnet_inserter", "name": "neural net inserter", "looks_like": "f_drill_press", - "description": "This device looks like a cross between some kind of nightmare dentistry equipment and socketing tool mounted on a slide that lets it drop precisely down. Useful for those project that require putting delicate items into hard to reach spaces.", + "description": "This device looks like a cross between some kind of nightmare dentistry equipment and socketing tool mounted on a slide that lets it drop precisely down. Useful for those projects that require putting delicate items into hard to reach spaces.", "symbol": "7", "color": "yellow_red", "move_cost_mod": -1, @@ -800,7 +800,7 @@ "type": "furniture", "id": "f_toposcan_terminal", "name": "DIRT data interface", - "description": "All-in-one integrated console for a Driving Intelligent Realtime Topography rover-mounted vehicular terrain scanner system. In other words, useless display monitor.", + "description": "All-in-one integrated console for a Driving Intelligent Realtime Topography rover-mounted vehicular terrain scanner system. In other words, a useless display monitor.", "looks_like": "f_console", "symbol": "?", "color": "light_gray", diff --git a/data/mods/Aftershock/maps/mapgen/astrobiology_lab.json b/data/mods/Aftershock/maps/mapgen/astrobiology_lab.json index 67d82df8cbc75..9beaf91e7e9fb 100644 --- a/data/mods/Aftershock/maps/mapgen/astrobiology_lab.json +++ b/data/mods/Aftershock/maps/mapgen/astrobiology_lab.json @@ -63,7 +63,11 @@ { "item": "supplies_reagents_lab", "chance": 70, "repeat": [ 2, 5 ] }, { "item": "supplies_xenoreagents_lab", "chance": 70, "repeat": [ 2, 5 ] } ], - "R": [ { "item": "decontamination_room", "chance": 60 }, { "item": "clothing_work_set", "chance": 30 } ], + "R": [ + { "item": "decontamination_room", "chance": 60 }, + { "item": "clothing_work_set", "chance": 30 }, + { "item": "afs_wintersuit_science_advanced", "chance": 30 } + ], "t": [ { "item": "office_paper", "chance": 60 }, { "item": "tools_science", "chance": 10, "repeat": [ 1, 2 ] } ] }, "item": { "c": { "item": "recipe_lichenlog", "chance": 6 } }, diff --git a/data/mods/Aftershock/maps/mapgen/formless_ruins/formless_ruins_dynamic.json b/data/mods/Aftershock/maps/mapgen/formless_ruins/formless_ruins_dynamic.json index 97449fe566d9a..7f6327ca83929 100644 --- a/data/mods/Aftershock/maps/mapgen/formless_ruins/formless_ruins_dynamic.json +++ b/data/mods/Aftershock/maps/mapgen/formless_ruins/formless_ruins_dynamic.json @@ -8,6 +8,12 @@ { "group": "afs_basic_material_scrapgroup", "prob": 20 } ] }, + { + "id": "afs_formless_ruins_random_suit", + "type": "item_group", + "subtype": "distribution", + "entries": [ { "group": "afs_wintersuit_civilian_advanced", "prob": 50 } ] + }, { "type": "mapgen", "method": "json", @@ -724,6 +730,7 @@ "///.. // " ], "terrain": { "C": "t_metal_floor", "s": "t_metal_floor" }, + "items": { "P": [ { "item": "afs_formless_ruins_random_suit", "chance": 80, "repeat": [ 1, 2 ] } ] }, "furniture": { "P": "f_sleep_pod", "s": "f_sink" }, "place_monsters": [ { "monster": "AFS_GROUP_MOXIE_LOW_RISK", "x": [ 0, 11 ], "y": [ 0, 11 ], "density": 0.1 } ], "palettes": [ "afs_formless_ruins" ] diff --git a/data/mods/Aftershock/maps/overmap_specials.json b/data/mods/Aftershock/maps/overmap_specials.json index 440fb168d2728..3063859b1d991 100644 --- a/data/mods/Aftershock/maps/overmap_specials.json +++ b/data/mods/Aftershock/maps/overmap_specials.json @@ -7,8 +7,7 @@ "locations": [ "land" ], "city_distance": [ 5, -1 ], "city_sizes": [ 2, -1 ], - "occurrences": [ 0, 1 ], - "flags": [ "CLASSIC" ] + "occurrences": [ 0, 1 ] }, { "type": "overmap_special", @@ -91,8 +90,7 @@ "locations": [ "land" ], "city_distance": [ 5, -1 ], "city_sizes": [ 2, -1 ], - "occurrences": [ 0, 1 ], - "flags": [ "CLASSIC" ] + "occurrences": [ 0, 1 ] }, { "type": "overmap_special", diff --git a/data/mods/Aftershock/maps/terrain_floraxeno.json b/data/mods/Aftershock/maps/terrain_floraxeno.json index 942bf4cba1afd..3a67dc85bae55 100644 --- a/data/mods/Aftershock/maps/terrain_floraxeno.json +++ b/data/mods/Aftershock/maps/terrain_floraxeno.json @@ -3,7 +3,7 @@ "type": "terrain", "id": "t_tree_worm", "name": "leviathan annelids", - "description": "Several massive protusions emerge from the ground, coiled together into a tower. At the top, blue and purple tentacles sway in the wind.", + "description": "Several massive protrusions emerge from the ground, coiled together into a tower. At the top, blue and purple tentacles sway in the wind.", "symbol": "I", "color": "magenta", "move_cost": 0, diff --git a/data/mods/Aftershock/maps/terrain_groundxeno.json b/data/mods/Aftershock/maps/terrain_groundxeno.json index c19729963bec1..f21d78887f670 100644 --- a/data/mods/Aftershock/maps/terrain_groundxeno.json +++ b/data/mods/Aftershock/maps/terrain_groundxeno.json @@ -417,7 +417,7 @@ "type": "terrain", "id": "t_moxfloor", "name": "bleached coral floor", - "description": "Small outgrowths of white coral spring from a underlying lattice of black, pulsating veins.", + "description": "Small outgrowths of white coral spring from an underlying lattice of black, pulsating veins.", "symbol": ".", "looks_like": "t_fungus", "color": "light_red", diff --git a/data/mods/Aftershock/mobs/robots.json b/data/mods/Aftershock/mobs/robots.json index e7f41166e18a7..6bd58ca042a00 100644 --- a/data/mods/Aftershock/mobs/robots.json +++ b/data/mods/Aftershock/mobs/robots.json @@ -174,7 +174,7 @@ "id": "afs_mon_sentinel_lx", "type": "MONSTER", "name": { "str": "Wraitheon Sentinel-lx" }, - "description": "Its exterior plates sable and gold, this luxurious variant of a Wraitheon drone was once kept as a bodyguard by society's wealthiest. Still with its wrist sword extended, it resembles an ancient knight, standing an eternal watch.", + "description": "Its exterior plates are sable and gold, this luxurious variant of a Wraitheon drone was once kept as a bodyguard by society's wealthiest. Still with its wrist sword extended, it resembles an ancient knight, standing an eternal watch.", "default_faction": "WraitheonRobotics", "species": [ "ROBOT" ], "diff": 10, @@ -534,7 +534,7 @@ "type": "MONSTER", "copy-from": "mon_milbot_base", "name": "Wraitheon Hashashiyyin", - "description": "he Hashashiyyin is unorthodox in its design as a military humaniform. While it keeps the strength of most Wraitheon humaniforms, it also prioritizes stealth. It comes equipped with an integrated 5x50mm flechette gun.", + "description": "The Hashashiyyin is unorthodox in its design as a military humaniform. While it keeps the strength of most Wraitheon humaniforms, it also prioritizes stealth. It comes equipped with an integrated 5x50mm flechette gun.", "diff": 15, "melee_damage": [ { "damage_type": "electric", "amount": 6 } ], "starting_ammo": { "5x50dart": 1000 }, diff --git a/data/mods/Aftershock/mobs/uplifted_monsters.json b/data/mods/Aftershock/mobs/uplifted_monsters.json index b5fceb4842a29..6bf8dd1ab0d90 100644 --- a/data/mods/Aftershock/mobs/uplifted_monsters.json +++ b/data/mods/Aftershock/mobs/uplifted_monsters.json @@ -3,7 +3,7 @@ "id": "mon_uplifted_bear", "type": "MONSTER", "name": { "str": "Schwarz Walder" }, - "description": "Schwarz Walders were originally developed by a German company as forest rangers for the expanded Black Forest Conservation Area. Shortly afterwards it was determined that they also made excellent long range reconnaissance units that could operate independently indefinitely. Pre-Cataclysm they lived on every continent in a variety of jobs.", + "description": "Schwarz Walders are an uplfited animal originally developed by a German company as forest rangers for the expanded Black Forest Conservation Area. Like all Uplifts they have a human level of intelligence and are indentured laborers to the corporation that grew and raised them, until they reach age of enfranchisement dictated by UICA regulations or they earn their citizenship early. Shortly afterward the creation of the first Schwarz Walders it was determined that they also made excellent long range reconnaissance units that could operate independently indefinitely. This was one of the most popular Uplift models before the Discontinuity, and many frontier planet still retain significant feral and otherwise independent populations. They are skilled marksmen and this one appears to be armed with an smg.", "default_faction": "UPLIFT", "bodytype": "bear", "categories": [ "WILDLIFE" ], diff --git a/data/mods/Aftershock/mobs/water_mobs.json b/data/mods/Aftershock/mobs/water_mobs.json index f905de14fee6a..74d02d52527aa 100644 --- a/data/mods/Aftershock/mobs/water_mobs.json +++ b/data/mods/Aftershock/mobs/water_mobs.json @@ -3,7 +3,7 @@ "id": "mon_deep_go", "type": "MONSTER", "name": { "str": "deep one" }, - "description": "An alien that appears to have evolved for survival in the depths. Its tubular grey body bears numerous sets of paired appendages of with clawed portrusions, and a pair of thick muscular fins. You see glimpses of its shape, shifting in and out of the water.", + "description": "An alien that appears to have evolved for survival in the depths. Its tubular grey body bears numerous sets of paired appendages with clawed protrusions, and a pair of thick muscular fins. You see glimpses of its shape, shifting in and out of the water.", "default_faction": "mi-go", "bodytype": "migo", "species": [ "NETHER" ], @@ -54,7 +54,7 @@ "id": "mon_deep_go_slaver", "type": "MONSTER", "name": { "str": "deep one slaver" }, - "description": "An alien that appears to have evolved for survival in the depths. Its tubular grey body bears numerous sets of paired appendages of with clawed portrusions, and a pair of thick muscular fins. You see glimpses of its shape, shifting in and out of the water. It is carrying a conical object that hums with an odd keening sound.", + "description": "An alien that appears to have evolved for survival in the depths. Its tubular grey body bears numerous sets of paired appendages with clawed protrusions, and a pair of thick muscular fins. You see glimpses of its shape, shifting in and out of the water. It is carrying a conical object that hums with an odd keening sound.", "default_faction": "mi-go", "bodytype": "migo", "species": [ "NETHER" ], diff --git a/data/mods/Aftershock/mutations/mutations.json b/data/mods/Aftershock/mutations/mutations.json index 1a35967601017..24122b71cf8ab 100644 --- a/data/mods/Aftershock/mutations/mutations.json +++ b/data/mods/Aftershock/mutations/mutations.json @@ -269,7 +269,7 @@ "id": "EERIE", "name": "Eerie", "points": 1, - "description": "You are offputting to others. You're mannerisms have changed as if human interaction is becoming foreign.", + "description": "You are off-putting to others. Your mannerisms have changed as if human interaction is becoming foreign.", "category": [ "MIGO" ], "social_modifiers": { "persuade": -15, "lie": -10 }, "ugliness": 2 @@ -629,7 +629,7 @@ "name": "Trumpeting Voice", "points": -1, "mixed_effect": true, - "description": "You have a trumpeting, elephantine voice. Threatening NPCs will be easier, but lying will very hard.", + "description": "You have a trumpeting, elephantine voice. Threatening NPCs will be easier, but lying will be very hard.", "changes_to": [ "SNARL" ], "category": [ "MASTODON" ], "social_modifiers": { "lie": -30, "intimidate": 10 } diff --git a/data/mods/Aftershock/mutations/obsolete.json b/data/mods/Aftershock/mutations/obsolete.json index f68356232f2bd..5b24fe475aa15 100644 --- a/data/mods/Aftershock/mutations/obsolete.json +++ b/data/mods/Aftershock/mutations/obsolete.json @@ -284,7 +284,7 @@ "points": -2, "visibility": 4, "ugliness": 5, - "description": "You smell like exactly like a shaggy elephant would, assuming it sweated, which you do. Monsters that track scent will find you very easily, and humans will react poorly.", + "description": "You smell exactly like a shaggy elephant would, assuming it sweated, which you do. Monsters that track scent will find you very easily, and humans will react poorly.", "valid": false, "scent_intensity": 1200, "prereqs": [ "ELEPHANTINE_SMELL" ], diff --git a/data/mods/Aftershock/player/bionics.json b/data/mods/Aftershock/player/bionics.json index 3b97dcc2f612b..13ba381a399e8 100644 --- a/data/mods/Aftershock/player/bionics.json +++ b/data/mods/Aftershock/player/bionics.json @@ -10,17 +10,6 @@ "time": 1, "flags": [ "BIONIC_POWER_SOURCE", "BIONIC_TOGGLED" ] }, - { - "id": "afs_bio_wind_turbine", - "type": "bionic", - "name": { "str": "Wind Turbines" }, - "description": "Installed on your body is a set of small retractable wind turbines. When activated, they will deploy and slowly harvest wind power to recharge your power level.", - "occupied_bodyparts": [ [ "torso", 10 ] ], - "fuel_options": [ "wind" ], - "fuel_efficiency": 0.25, - "time": 1, - "flags": [ "BIONIC_POWER_SOURCE", "BIONIC_TOGGLED" ] - }, { "id": "afs_bio_missiles", "type": "bionic", diff --git a/data/mods/Aftershock/player/obsolete.json b/data/mods/Aftershock/player/obsolete.json index 2343e05286a9b..e99c10a9aba5d 100644 --- a/data/mods/Aftershock/player/obsolete.json +++ b/data/mods/Aftershock/player/obsolete.json @@ -6,5 +6,16 @@ "description": "Your hands have been outfitted with precise soldering tools, wire cutters, and cable spools. They're too small to use in most crafting, but in the absence of proper machinery, they're essential for creating bionics without better tools.", "occupied_bodyparts": [ [ "hand_l", 1 ], [ "hand_r", 1 ] ], "fake_item": "afs_solderers_item" + }, + { + "id": "afs_bio_wind_turbine", + "type": "bionic", + "name": { "str": "Wind Turbines" }, + "description": "Installed on your body is a set of small retractable wind turbines. When activated, they will deploy and slowly harvest wind power to recharge your power level.", + "occupied_bodyparts": [ [ "torso", 10 ] ], + "fuel_options": [ "wind" ], + "fuel_efficiency": 0.25, + "time": 1, + "flags": [ "BIONIC_POWER_SOURCE", "BIONIC_TOGGLED" ] } ] diff --git a/data/mods/Aftershock/player/professions.json b/data/mods/Aftershock/player/professions.json index 35ee54c408a08..f23c6ca95c82c 100644 --- a/data/mods/Aftershock/player/professions.json +++ b/data/mods/Aftershock/player/professions.json @@ -107,7 +107,7 @@ "type": "profession", "id": "afs_atomic_pitchman", "name": { "male": "Atomic Pitchman", "female": "Atomic Pitchwoman" }, - "description": "You were hired to market Rivtech's products through TV and were on your way to the studio when the bombs hit. You enter now the end of the world with nothing but the snazzy clothes on your back and a bunch of plutonium-powered toys.", + "description": "You were hired to market Rivtech's products through TV and were on your way to the studio when the bombs hit. You now enter the end of the world with nothing but the snazzy clothes on your back and a bunch of plutonium-powered toys.", "points": 3, "skills": [ { "level": 1, "name": "speech" } ], "items": { @@ -192,7 +192,7 @@ "id": "afs_wraitheon_executive", "name": "Wraitheon Executive", "description": { - "str": "You were one of the chief executives of the Megacorporation Wraitheon Robotics ltd. and consistently ranked among the most powerful and influential persons of the world. Still, the apocalypse has caught you unprepared, and now after the dissolution of your company and the crumbling of your network of influence, your were left powerless and destitute, and must rely on your two robotic bodyguards to survive.", + "str": "You were one of the chief executives of the Megacorporation Wraitheon Robotics ltd. and consistently ranked among the most powerful and influential persons of the world. Still, the apocalypse has caught you unprepared, and now after the dissolution of your company and the crumbling of your network of influence, you are left powerless and destitute, and must rely on your two robotic bodyguards to survive.", "//NOLINT(cata-text-style)": "not a period" }, "points": 8, diff --git a/data/mods/Aftershock/scenarios.json b/data/mods/Aftershock/scenarios.json index d088fd7efa4da..2028f491d72e2 100644 --- a/data/mods/Aftershock/scenarios.json +++ b/data/mods/Aftershock/scenarios.json @@ -88,7 +88,7 @@ "id": "escape_pod", "name": "Stranded Spacer", "points": 1, - "description": "What was to be a routine cargo transfer ended in tragedy when, in a brief moment of chaos, your space ship was intercepted and destroyed by a StO missile. As soon as the MAW alarm flared to life, you scrambled to the nearest escape pod and barely managed reach the uncertain safety of the planet below.", + "description": "What was to be a routine cargo transfer ended in tragedy when, in a brief moment of chaos, your space ship was intercepted and destroyed by a StO missile. As soon as the MAW alarm flared to life, you scrambled to the nearest escape pod and barely managed to reach the uncertain safety of the planet below.", "allowed_locs": [ "sloc_escape_pod" ], "professions": [ "afs_espatier", "afs_rating" ], "flags": [ "LONE_START" ], diff --git a/data/mods/Aftershock/snippets/personal_cards.json b/data/mods/Aftershock/snippets/personal_cards.json new file mode 100644 index 0000000000000..a3a0b6390a7a3 --- /dev/null +++ b/data/mods/Aftershock/snippets/personal_cards.json @@ -0,0 +1,32 @@ +[ + { + "type": "snippet", + "category": "afs_personal_card", + "text": [ + { + "id": "afs_card_1", + "text": "There is a picture of a shuttle taking off with happy people waving from the viewports. \"Salus is cold.\n\n Our love is true\n\n I hope to see you\n\n On Croatan 2\n\n My dearest, please join me. I don't believe there are many shuttles left before people will be stuck here forever.\"" + }, + { + "id": "afs_card_2", + "text": "A person wrapped up in bandages in a hospital bed is pictured on the front of this card. \"Get well soon!!!\n\n Torrance, I'm sorry you feel ill but many new colonists forget to process any foodstuffs from Salus IV to remove the macronutrient.\"" + }, + { + "id": "afs_card_3", + "text": "A picture of a bloody steak. This card appears particularly old. \"Gauchos Steaks. Deliveries made monthly from Brazil to Salus IV. Sign up today!\"" + }, + { + "id": "afs_card_4", + "text": "A glossy image of flowers and funeral statuary. \"I'm sorry for your loss. Ebby was an excellent grandparent and we hate for you that he was infected with the Moxphoria and wandered into the wastes.\"" + }, + { + "id": "afs_card_5", + "text": "An orbital view of Salus IV. \"Happy Colonization Day!\n\n Leonid,\n\n This place is cold as Hell but we've made a life out here in the last ten years.\"" + }, + { + "id": "afs_card_6", + "text": "An old style movie theater. \"Showing the vintage film Aelita!\n\n Tovarishch,\n\n Come with me to the movies. My grandfather says that this movie is one of the classics.\"" + } + ] + } +] diff --git a/data/mods/Aftershock/snippets.json b/data/mods/Aftershock/snippets/snippets.json similarity index 100% rename from data/mods/Aftershock/snippets.json rename to data/mods/Aftershock/snippets/snippets.json diff --git a/data/mods/Aftershock/suit_operating_time.md b/data/mods/Aftershock/suit_operating_time.md new file mode 100644 index 0000000000000..4a346e910efef --- /dev/null +++ b/data/mods/Aftershock/suit_operating_time.md @@ -0,0 +1,106 @@ +# Powered Armor Balance + +These are guidelines for designing all sorts of Aftershock power armor. + +## Operating time + +We define `operating time` as the amount of time the suit/gear-piece takes to consume 1000 charges of batteries. The following factors, totaled, determine how long the suit should work between battery swaps. + + +#### Additional factors + | Extra Hours | Condition | + |-----------------------|-------------------------------------------------------------------------| + | 4 hours | Base operating time. All suits work for at least 4 hours per full charge | + |-----------------------|-------------------------------------------------------------------------| + | 1hour per every 5 enc | Count only the most encumbered part | + | 1 hours | Less than 40 armor against all hazards | + | 1 hours | Less than 20 armor against all hazards | + | 6 hours | Less than 10 armor against all hazards | + | 2 hours | Has the fragile tag or is made from a very fragile material (ex: glass) | + | 4 hours | Less than 90% coverage of the most encumbered part | + | 4 hours | No protection against gases | + | 12 hours | No protection against the cold | + | 2 hours | No protection against other environmental factors (ex: acid, electricity, radiation) | + | 4 hours | Gear doesn't give passive bonuses to physical attributes (ex: dex or str) or skills (ex: athletics) and won't grant physical-based proficiencies| + | 4 hours | Doesn't grant any combat/movement related spells or enchantments | + | 4 hours | Gear doesn't give passive bonuses to mental attributes (per or int) or skills (ex: applied science) and won't grant knowledge based proficiencies | + | 1 hours | Doesn't grant any mental/knowledge related spells or enchantments | + | 2 hours | Has a non-swappable integral battery. | + | 2 hours | Won't grant night/heat-vision or clairvoyance effects. | + +Once you have determined the operating time using the table above, simply divide the number 277777.78 by the indicated number of hours to obtain your new suit's `power_draw` json value. + +For player convenience, the power draw and battery capacity of all clothing pieces that comprise a single matching outfit (e.g., the Magellan exosuit and its helmet) should be the same. + +#### Example Calculation + +Consider the Magellan exosuit, which has the following definition: +``` + { + "id": "afs_magellan_suit", + "type": "TOOL_ARMOR", + "category": "armor", + "name": { "str": "Magellan exosuit" }, + "description": "A high-quality, civilian grade EVA suit often employed by well-established frontier research and exploration associations. Designed to support the exploration of challenging terrain, it offers respectable protection against common environmental hazards like extreme temperatures, inhospitable atmospheres, and light radiation. It leaves arms and hands relatively unencumbered to aid the manipulation of scientific instruments.\n\nAn integral battery allows the suit to operate for up to 34 hours, but complicates field recharging.", + "weight": "7800 g", + "volume": "14 L", + "price": "4 kUSD", + "material": [ "nomex", "steel" ], + "symbol": "[", + "looks_like": "robofac_enviro_suit", + "color": "light_gray", + "ammo": "battery", + "charges_per_use": 1, + "use_action": { + "type": "transform", + "msg": "You activate your %s.", + "target": "afs_magellan_suit_on", + "active": true, + "need_charges": 1, + "need_charges_msg": "The %s's batteries are dead." + }, + "armor_portion_data": [ + { "covers": [ "torso" ], "coverage": 100, "encumbrance": 25 }, + { "covers": [ "leg_l", "leg_r" ], "coverage": 100, "encumbrance": 25 }, + { "covers": [ "arm_l", "arm_r" ], "coverage": 100, "encumbrance": 15 }, + { "covers": [ "hand_l", "hand_r" ], "coverage": 100, "encumbrance": 10 }, + { "covers": [ "foot_l", "foot_r" ], "coverage": 100, "encumbrance": 15 } + ], + "pocket_data": [ + { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "battery": 1000 } } + ], + "warmth": 20, + "material_thickness": 2, + "valid_mods": [ "steel_padded" ], + "environmental_protection": 15, + "flags": [ "VARSIZE", "WATERPROOF", "GAS_PROOF", "POCKETS", "RAINPROOF", "STURDY", "RAD_RESIST", "RECHARGE", "OUTER", "NO_RELOAD", "NO_UNLOAD" ] + }, + +``` +The active version provides no additional bonuses except for cold protection. + +Evaluating the performance time using the table above, the result should be: + +| Suit Qualifies | Extra Hours | Condition | +|----------------|-----------------------|-------------------------------------------------------------------------| +| yes | 4 hours | Base operating time. All suits work for at least 4 hours per full charge | +|----------------|-----------------------|-------------------------------------------------------------------------| +| yes | 5 hours | Count only the most encumbered part | +| yes | 1 hours | Less than 40 armor against all hazards | +| yes | 1 hours | Less than 20 armor against all hazards | +| yes | 6 hours | Less than 10 armor against all hazards | +| no | 2 hours | Has the fragile tag or is made from a very fragile material (ex: glass) | +| no | 4 hours | Less than 90% coverage of the most encumbered part | +| no | 4 hours | No protection against gases | +| no | 12 hours | No protection against the cold | +| no | 2 hours | No protection against other environmental factors (ex: acid, electricity, radiation) | +| yes | 4 hours |Gear doesn't give passive bonuses to physical attributes (ex: dex or str) or skills (ex: athletics) and won't grant physical-based proficiencies| +| yes | 4 hours | Doesn't grant any combat/movement related spells or enchantments | +| yes | 4 hours | Gear doesn't give passive bonuses to mental attributes (per or int) or skills (ex: applied science) and won't grant knowledge based proficiencies | +| yes | 1 hours | Doesn't grant any mental/knowledge related spells or enchantments | +| yes | 2 hours | Has a non-swappable integral battery. | +| yes | 2 hours | Won't grant night/heat-vision or clairvoyance effects, | +|----------------|-----------------------|-------------------------------------------------------------------------| +| Total hours | 34 hours | | + +Due to the fulfilled conditions, the suit should operate for 34 hours when connected to a 1000 charge battery. To calculate its final `power_draw` value, we divide 277777.78 by 34 to obtain a `power_draw` of 8170. \ No newline at end of file diff --git a/data/mods/Aftershock/vehicles/vehicle_parts.json b/data/mods/Aftershock/vehicles/vehicle_parts.json index b7aad54ed241c..377859a40a80c 100644 --- a/data/mods/Aftershock/vehicles/vehicle_parts.json +++ b/data/mods/Aftershock/vehicles/vehicle_parts.json @@ -28,6 +28,34 @@ "removal": { "skills": [ [ "mechanics", 3 ] ] } } }, + { + "id": "afs_mounted_gibs_shotgun", + "copy-from": "turret", + "type": "vehicle_part", + "name": { "str": "mounted Gibson S86" }, + "item": "afs_gibs_shotgun", + "color": "magenta", + "broken_color": "magenta", + "breaks_into": [ { "item": "scrap", "count": 9 }, { "item": "steel_chunk", "count": 4 }, { "item": "steel_lump", "count": 1 } ], + "requirements": { + "install": { "skills": [ [ "mechanics", 5 ], [ "electronics", 5 ] ] }, + "removal": { "skills": [ [ "mechanics", 3 ] ] } + } + }, + { + "id": "afs_mounted_Accipitermg", + "copy-from": "turret", + "type": "vehicle_part", + "name": { "str": "mounted Accipiter Hawk-00" }, + "item": "afs_Accipitermg", + "color": "magenta", + "broken_color": "magenta", + "breaks_into": [ { "item": "scrap", "count": 9 }, { "item": "steel_chunk", "count": 4 }, { "item": "steel_lump", "count": 1 } ], + "requirements": { + "install": { "skills": [ [ "mechanics", 5 ], [ "electronics", 5 ] ] }, + "removal": { "skills": [ [ "mechanics", 3 ] ] } + } + }, { "id": "afs_laser_rifle", "copy-from": "turret", diff --git a/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json b/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json index 7142e33d81d77..4cf46dd686f76 100644 --- a/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json +++ b/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json @@ -9,12 +9,12 @@ "requirements": { "install": { "skills": [ [ "mechanics", 3 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ] } } }, { - "id": "mounted_acr", + "id": "mounted_nato_assault_rifle", "copy-from": "turret", "type": "vehicle_part", - "name": { "str": "mounted Remington ACR" }, - "item": "acr", - "breaks_into": [ { "item": "acr", "prob": 50 } ], + "name": { "str": "mounted assault rifle" }, + "item": "nato_assault_rifle", + "breaks_into": [ { "item": "nato_assault_rifle", "prob": 50 } ], "requirements": { "install": { "skills": [ [ "mechanics", 3 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ] } } }, { @@ -154,15 +154,6 @@ "breaks_into": [ { "item": "fn_p90", "prob": 50 } ], "requirements": { "install": { "skills": [ [ "mechanics", 3 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ] } } }, - { - "id": "mounted_h&k416a5", - "copy-from": "turret", - "type": "vehicle_part", - "name": { "str": "mounted H&K 416A5" }, - "item": "h&k416a5", - "breaks_into": [ { "item": "h&k416a5", "prob": 50 } ], - "requirements": { "install": { "skills": [ [ "mechanics", 3 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ] } } - }, { "id": "mounted_hk_g3", "copy-from": "turret", @@ -289,15 +280,6 @@ "breaks_into": [ { "item": "m2010", "prob": 50 } ], "requirements": { "install": { "skills": [ [ "mechanics", 3 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ] } } }, - { - "id": "mounted_m27iar", - "copy-from": "turret", - "type": "vehicle_part", - "name": { "str": "mounted M27 IAR" }, - "item": "m27iar", - "breaks_into": [ { "item": "m27iar", "prob": 50 } ], - "requirements": { "install": { "skills": [ [ "mechanics", 3 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ] } } - }, { "id": "mounted_m2browning_sawn", "copy-from": "turret", @@ -316,15 +298,6 @@ "breaks_into": [ { "item": "m320", "prob": 50 } ], "requirements": { "install": { "skills": [ [ "mechanics", 4 ] ] }, "removal": { "skills": [ [ "mechanics", 2 ] ] } } }, - { - "id": "mounted_m4a1", - "copy-from": "turret", - "type": "vehicle_part", - "name": { "str": "mounted M4A1" }, - "item": "m4a1", - "breaks_into": [ { "item": "m4a1", "prob": 50 } ], - "requirements": { "install": { "skills": [ [ "mechanics", 3 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ] } } - }, { "id": "mounted_m60", "copy-from": "turret", @@ -573,15 +546,6 @@ "breaks_into": [ { "item": "scar_h", "prob": 50 } ], "requirements": { "install": { "skills": [ [ "mechanics", 3 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ] } } }, - { - "id": "mounted_scar_l", - "copy-from": "turret", - "type": "vehicle_part", - "name": { "str": "mounted FN SCAR-L" }, - "item": "scar_l", - "breaks_into": [ { "item": "scar_l", "prob": 50 } ], - "requirements": { "install": { "skills": [ [ "mechanics", 3 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ] } } - }, { "id": "mounted_shotgun_d", "copy-from": "turret", diff --git a/data/mods/CRT_EXPANSION/deadspace/deadspaceitems.json b/data/mods/CRT_EXPANSION/crt_gear/advanced_gear.json similarity index 95% rename from data/mods/CRT_EXPANSION/deadspace/deadspaceitems.json rename to data/mods/CRT_EXPANSION/crt_gear/advanced_gear.json index 943cd8b7731ce..6f646b8f256e9 100644 --- a/data/mods/CRT_EXPANSION/deadspace/deadspaceitems.json +++ b/data/mods/CRT_EXPANSION/crt_gear/advanced_gear.json @@ -39,7 +39,7 @@ "type": "ARMOR", "category": "armor", "name": "CRIT Engineering Suit", - "description": "An airtight, flexible suit of woven composite fibers complete with segmented plates of armor. A complex system digitizes items in an individual pocket universe for storage while built in joint-torsion ratchets generate the neccessary energy required to power the interface.", + "description": "An airtight, flexible suit of woven composite fibers complete with segmented plates of armor. A complex system digitizes items in an individual pocket universe for storage while built in joint-torsion ratchets generate the necessary energy required to power the interface.", "weight": "13460 g", "volume": "14 L", "price": 900000000, diff --git a/data/mods/CRT_EXPANSION/items/crt_ammo.json b/data/mods/CRT_EXPANSION/items/crt_ammo.json index b207d694a30e0..7fa03d67f224a 100644 --- a/data/mods/CRT_EXPANSION/items/crt_ammo.json +++ b/data/mods/CRT_EXPANSION/items/crt_ammo.json @@ -47,7 +47,7 @@ "copy-from": "pellet", "type": "AMMO", "name": { "str_sp": "alloy pellets" }, - "description": "An gimmicky alloy pellet with the purpose of reaching a higher velocity than a normal lead pellet for breaking the sound barrier resulting in an extremely loud crack, not so useful for stealth.", + "description": "A gimmicky alloy pellet with the purpose of reaching a higher velocity than a normal lead pellet for breaking the sound barrier resulting in an extremely loud crack, not so useful for stealth.", "material": [ "steel" ], "symbol": "=", "color": "dark_gray", diff --git a/data/mods/CRT_EXPANSION/items/crt_armor.json b/data/mods/CRT_EXPANSION/items/crt_armor.json index ecc0eb6c43e57..2f6d322632d9b 100644 --- a/data/mods/CRT_EXPANSION/items/crt_armor.json +++ b/data/mods/CRT_EXPANSION/items/crt_armor.json @@ -261,7 +261,7 @@ "type": "ARMOR", "category": "armor", "name": { "str": "pair of CRIT Enforcer docks", "str_pl": "pairs of CRIT Enforcer docks" }, - "description": "CRIT Enforcer docks. Metal plates vaguely molded into the shape of oversized feet which clamp down onto your own footwear keep your feet out of harms way. It looks terrible and feels clunky unlike most of C.R.I.T's designs, but they do seem to be worth using if you were to be in the middle of a warzone.", + "description": "CRIT Enforcer docks. Metal plates vaguely molded into the shape of oversized feet which clamp down onto your own footwear keep your feet out of harms way. It looks terrible and feels clunky unlike most of C.R.I.T's designs, but they do seem to be worth using if you were to be in the middle of a war zone.", "weight": "2060 g", "volume": "1500 ml", "price": 100000, diff --git a/data/mods/CRT_EXPANSION/items/crt_gun.json b/data/mods/CRT_EXPANSION/items/crt_gun.json index 29500fede4f49..b20bac85e98a9 100644 --- a/data/mods/CRT_EXPANSION/items/crt_gun.json +++ b/data/mods/CRT_EXPANSION/items/crt_gun.json @@ -202,7 +202,7 @@ "price": 9900, "//": "You can get a decent Ruger .177 at walmart for $99", "material": [ "steel", "plastic" ], - "flags": [ "STR_RELOAD", "NON_FOULING", "NON_FOULING" ], + "flags": [ "STR_RELOAD", "NON_FOULING" ], "skill": "rifle", "ammo": [ "pellets" ], "weight": "5023 g", @@ -252,7 +252,7 @@ "id": "ds_rivet_gun", "type": "GUN", "name": "Rivet Driver", - "description": "Experimental double purpose tool under development in C.R.I.T R&D. It takes a regular nail and then enlongates it within a fraction of a second before firing it out, upon reaching a target, the fragile stake explodes into shards inside the target.", + "description": "Experimental double purpose tool under development in C.R.I.T R&D. It takes a regular nail and then elongates it within a fraction of a second before firing it out, upon reaching a target, the fragile stake explodes into shards inside the target.", "weight": "1650 g", "volume": "750 ml", "price": 1000000, diff --git a/data/mods/CRT_EXPANSION/items/crt_toolarmor.json b/data/mods/CRT_EXPANSION/items/crt_toolarmor.json index b1666136f844e..551b49fd24be8 100644 --- a/data/mods/CRT_EXPANSION/items/crt_toolarmor.json +++ b/data/mods/CRT_EXPANSION/items/crt_toolarmor.json @@ -3,8 +3,8 @@ "id": "crt_gasmask", "type": "TOOL_ARMOR", "category": "armor", - "name": { "str": "CRIT gasmask (off)", "str_pl": "CRIT gasmasks (off)" }, - "description": "This is a heavily modified Spec Ops modified gasmask, fitted with top-of-the-line electronics and lined with Kevlar for extra protection in order to keep one's head where it should be. Various filters and other high tech wizardry allow for enhanced oxygen intake and safety even under bombardment. It has an integrated HUD and the option to turn it on for more features.", + "name": { "str": "CRIT gas mask (off)", "str_pl": "CRIT gas masks (off)" }, + "description": "This is a heavily modified Spec Ops modified gas mask, fitted with top-of-the-line electronics and lined with Kevlar for extra protection in order to keep one's head where it should be. Various filters and other high tech wizardry allow for enhanced oxygen intake and safety even under bombardment. It has an integrated HUD and the option to turn it on for more features.", "weight": "5 kg", "volume": "1 L", "price": 2500000, @@ -45,8 +45,8 @@ "id": "crt_gasmask_on", "type": "TOOL_ARMOR", "category": "armor", - "name": { "str": "CRIT gasmask (on)", "str_pl": "CRIT gasmasks (on)" }, - "description": "This a heavily modified gasmask. It is currently on and draining power for the HUD, low-level nightvision and other protective elements.", + "name": { "str": "CRIT gas mask (on)", "str_pl": "CRIT gas masks (on)" }, + "description": "This a heavily modified gas mask. It is currently on and draining power for the HUD, low-level night vision and other protective elements.", "weight": "5 kg", "volume": "1 L", "price": 2500000, @@ -88,7 +88,7 @@ "type": "TOOL_ARMOR", "category": "armor", "name": { "str": "CRIT EM vest (off)", "str_pl": "CRIT EM vests (off)" }, - "description": "The Enhanced Movement vest is embedded with high-tech filaments and reactive servos which protects its wearer and assists in movement at the cost high power usage. It is commonly worn by C.R.I.T Spec Ops for its ease of use and manuverability. Turn it on for suit mode, extra protection and movement.", + "description": "The Enhanced Movement vest is embedded with high-tech filaments and reactive servos which protects its wearer and assists in movement at the cost high power usage. It is commonly worn by C.R.I.T Spec Ops for its ease of use and maneuverability. Turn it on for suit mode, extra protection and movement.", "weight": "10 kg", "volume": "6250 ml", "price": 7000000, @@ -214,7 +214,6 @@ "OUTER", "LIGHT_35", "CHARGEDIM", - "NO_UNLOAD", "TRADER_AVOID" ], "material_thickness": 3, diff --git a/data/mods/CRT_EXPANSION/items/crt_tools.json b/data/mods/CRT_EXPANSION/items/crt_tools.json index d98d70f31e0ea..d01795aa7d2b3 100644 --- a/data/mods/CRT_EXPANSION/items/crt_tools.json +++ b/data/mods/CRT_EXPANSION/items/crt_tools.json @@ -63,7 +63,7 @@ "type": "TOOL", "category": "weapons", "name": "CRIT Reso-blade", - "description": "CRIT melee weapon. Alien runes adorn the carbon steel blade. The blade oddly seems to lack sharpness, and yet upon closer oberservation, a hum of energy thrums from within.", + "description": "CRIT melee weapon. Alien runes adorn the carbon steel blade. The blade oddly seems to lack sharpness, and yet upon closer inspection, a hum of energy thrums from within.", "color": "dark_gray", "material": [ "hardsteel" ], "//": "Legacy artifact data is set for always on wield dex +2 and speed up times 2", diff --git a/data/mods/CRT_EXPANSION/martial/CRT_Bladework.json b/data/mods/CRT_EXPANSION/martial/CRT_Bladework.json index 751e3c3d2fc7d..75efd9a3e449c 100644 --- a/data/mods/CRT_EXPANSION/martial/CRT_Bladework.json +++ b/data/mods/CRT_EXPANSION/martial/CRT_Bladework.json @@ -16,7 +16,7 @@ "type": "martial_art", "id": "style_crt_blade", "name": "CRIT Blade-work", - "description": "An offensive style centered around rapid slashes and prodding. Each attack landed increases combat ability but leaves you increasingly vunerable", + "description": "An offensive style centered around rapid slashes and prodding. Each attack landed increases combat ability but leaves you increasingly vulnerable", "initiate": [ "You prepare to whittle down your enemies.", "%s initiates blade-work." ], "arm_block": 99, "leg_block": 99, diff --git a/data/mods/CRT_EXPANSION/monsters/crt_monster.json b/data/mods/CRT_EXPANSION/monsters/crt_monster.json index 0f78397d0cda6..8717d5a9c1754 100644 --- a/data/mods/CRT_EXPANSION/monsters/crt_monster.json +++ b/data/mods/CRT_EXPANSION/monsters/crt_monster.json @@ -2,7 +2,7 @@ { "id": "mon_slasher", "type": "MONSTER", - "name": "Slasher Necromorph", + "name": "Slasher Bio-Recombinant", "description": "A horrifically twisted human body. Two massive, bladed appendages have burst through its shoulders where they are poised above its head as it stalks about with terrifying purpose.", "default_faction": "zombie", "categories": [ "CLASSIC" ], @@ -48,7 +48,7 @@ { "id": "mon_slasher_weak", "type": "MONSTER", - "name": "Weak Slasher Necromorph", + "name": "Weak Slasher Bio-Recombinant", "description": "A horrifically mutilated human body. Two scrawny blades have freshly bursted through its hand to deal with prey and the haunting visage of a jawless maw and a gaping wound in its forehead sends chills down your spine. The awkward steps it takes slows it down greatly.", "default_faction": "zombie", "categories": [ "CLASSIC" ], @@ -94,7 +94,7 @@ { "id": "mon_waster", "type": "MONSTER", - "name": "Waster Necromorph", + "name": "Waster Bio-Recombinant", "description": "Clad in heavy assault gear, an eerie light green glows beneath its helmet from sunken eye sockets and a gaping mouth. Strange blade like points have burst out of it's forearms making it a formidable force to be reckoned with.", "default_faction": "zombie", "species": [ "ZOMBIE", "HUMAN" ], @@ -128,7 +128,7 @@ { "id": "mon_leaper", "type": "MONSTER", - "name": "Leaper Necromorph", + "name": "Leaper Bio-Recombinant", "description": "This once-human body is barely recognizable, scrambling about on its abdomen as it leaps forward with immense arm strength. With elongated fangs that can easily mutilate your flesh, the grotesque face roars incessantly. The lower body has fused together into one giant tail with a barbed spike.", "default_faction": "zombie", "species": [ "ZOMBIE", "HUMAN" ], @@ -169,7 +169,7 @@ { "id": "mon_twitcher", "type": "MONSTER", - "name": "Twitcher Necromorph", + "name": "Twitcher Bio-Recombinant", "description": "With narrow blades coming out of its hands, this corpse spasmically dashes to-and-fro with surprising speed. It carries itself quite steadily when idle, further observation shows that the person before this husk was a C.R.I.T S-I G.E.A.R operator.", "default_faction": "zombie", "species": [ "ZOMBIE", "HUMAN" ], @@ -218,7 +218,7 @@ { "id": "mon_pack", "type": "MONSTER", - "name": "Pack Necromorph", + "name": "Pack Bio-Recombinant", "description": "A shrieking mutated child zombie. The face is is mainly blank with eyes swollen shut and a torn-open mouth with flaps of flesh hanging to the side. A pair of seemingly purposeless appendages sprout from its shoulders before ending in its arms. Its small hands end in sharp claws.", "default_faction": "zombie", "species": [ "ZOMBIE" ], @@ -253,7 +253,7 @@ { "id": "mon_puker", "type": "MONSTER", - "name": "Puker Necromorph", + "name": "Puker Bio-Recombinant", "description": "A rather mutilated corpse covered in gaping sores. Hanging arms with hands that have long corroded away reveal jagged edges that could easily pierce into your flesh. A sticky, frothing yellow sludge flows from its exposed internal organs to its unhinged jaw where it drips, hissing as it eats through material.", "default_faction": "zombie", "species": [ "ZOMBIE", "HUMAN" ], diff --git a/data/mods/CRT_EXPANSION/monsters/crt_monstergroups.json b/data/mods/CRT_EXPANSION/monsters/crt_monstergroups.json index 8686d5986acf5..43b96f6ac0fe5 100644 --- a/data/mods/CRT_EXPANSION/monsters/crt_monstergroups.json +++ b/data/mods/CRT_EXPANSION/monsters/crt_monstergroups.json @@ -1,7 +1,7 @@ [ { "type": "monstergroup", - "name": "GROUP_NECROMORPH", + "name": "GROUP_BIORECOMBINANT", "default": "mon_slasher_weak", "monsters": [ { "monster": "mon_slasher_weak", "freq": 300, "cost_multiplier": 0, "pack_size": [ 2, 4 ] }, diff --git a/data/mods/CRT_EXPANSION/mutations/crt_wendigo_mutations.json b/data/mods/CRT_EXPANSION/mutations/crt_wendigo_mutations.json index db237d5336376..87efffb5b22a9 100644 --- a/data/mods/CRT_EXPANSION/mutations/crt_wendigo_mutations.json +++ b/data/mods/CRT_EXPANSION/mutations/crt_wendigo_mutations.json @@ -14,7 +14,7 @@ "id": "NMELD", "name": "Nature's Boon", "points": 3, - "description": "Your very prescence is masked by nature itself. You are slightly harder to detect.", + "description": "Your very presence is masked by nature itself. You are slightly harder to detect.", "valid": false, "purifiable": false, "prereqs": [ "WEAKSCENT" ], diff --git a/data/mods/CRT_EXPANSION/mutations/wendigo_mut_cat.json b/data/mods/CRT_EXPANSION/mutations/wendigo_mut_cat.json index 1cf8a61f0880b..3ba2aa896727e 100644 --- a/data/mods/CRT_EXPANSION/mutations/wendigo_mut_cat.json +++ b/data/mods/CRT_EXPANSION/mutations/wendigo_mut_cat.json @@ -28,7 +28,7 @@ "copy-from": "iv_mutagen_flavor", "type": "COMESTIBLE", "name": "wendigo serum", - "description": "A super-concentrated peat-brown substance with glittering green flecks that reminds you of a a tree. You need a syringe to inject it… if you really want to?", + "description": "A super-concentrated peat-brown substance with glittering green flecks that reminds you of a tree. You need a syringe to inject it… if you really want to?", "color": "light_gray", "healthy": -2, "use_action": { "type": "mutagen_iv", "mutation_category": "WENDIGO" } diff --git a/data/mods/CRT_EXPANSION/readme/README.md b/data/mods/CRT_EXPANSION/readme/README.md index 16d67176f6697..c21f0c3b2b4f7 100644 --- a/data/mods/CRT_EXPANSION/readme/README.md +++ b/data/mods/CRT_EXPANSION/readme/README.md @@ -24,7 +24,7 @@ More techniques, more play styles. Tries to open up melee and offer more than "d I tried to keep the damage balanced and also added scaling damage and movecosts to make it so certain attacks or weapons carry different risks. -# Dead Space -Yeah, the suit is the R.I.G and many other things come from these games. Awesome series! Some Necromorphs will spawn in labs as well, so beware. +# Lore +The logic behind the C.R.T Expansion is that in this version of the Cataclysm is that there was a much longer build up of the dead rising and random portals appearing before the final Cataclysmic Portal Storm erupted. Governments used this time to create special response forces similar to the Fringe universe. Despite the extra time that the world had to prepare, the Cataclysm still mostly went off as basic DDA. Eventually there will be more special locations showing off some of these preparations and the failures they experienced. Anyways, have fun and report any bugs you find. diff --git a/data/mods/CRT_EXPANSION/scenarios/crt_classes.json b/data/mods/CRT_EXPANSION/scenarios/crt_classes.json index 89f6d4d92a68a..041172c84f5a0 100644 --- a/data/mods/CRT_EXPANSION/scenarios/crt_classes.json +++ b/data/mods/CRT_EXPANSION/scenarios/crt_classes.json @@ -32,7 +32,7 @@ "type": "profession", "id": "crt_dude", "name": "CRIT Janitor", - "description": "*Sigh* Your life has been a wreck. Hopping place to place you finally found a job at C.R.I.T… as a janitor of sorts. The pay was good and you got some sneak-peeks on some pretty cool stuff. After all non-essential personel were purged you found yourself stuck with nothing but the uniform they gave you and your job's sign-bonus equipment.", + "description": "*Sigh* Your life has been a wreck. Hopping place to place you finally found a job at C.R.I.T… as a janitor of sorts. The pay was good and you got some sneak-peeks on some pretty cool stuff. After all non-essential personnel were purged you found yourself stuck with nothing but the uniform they gave you and your job's sign-bonus equipment.", "points": 6, "skills": [ { "level": 2, "name": "tailor" }, @@ -112,7 +112,7 @@ "type": "profession", "id": "crt_rifleman", "name": "CRIT Grunt", - "description": "You were part of the infantry; first to hit the ground running, clear a FOB and then come back to relax with your squad. Those days ended when the cataclysm reared its ugly head. Your lines were torn through like wet paper when the otherworldy abominations arived. Now fleeing for your life, will you have what it takes to survive or is this hellish landcape your resting place?", + "description": "You were part of the infantry; first to hit the ground running, clear a FOB and then come back to relax with your squad. Those days ended when the cataclysm reared its ugly head. Your lines were torn through like wet paper when the otherworldly abominations arrived. Now fleeing for your life, will you have what it takes to survive or is this hellish landscape your resting place?", "points": 8, "traits": [ "MARTIAL_CRT" ], "skills": [ { "level": 3, "name": "gun" }, { "level": 3, "name": "rifle" }, { "level": 1, "name": "stabbing" } ], @@ -199,7 +199,7 @@ "type": "profession", "id": "crt_saw", "name": "CRIT Automatic Rifleman", - "description": "You were assigned the billet of specializing in creating dead zones and providing support fire. When the cataclysm struck, your trusty LMG couldn't keep the veritable tide of undead from overtaking your squad. Now running with all the strength your body can muster, will you have what it takes to survive or is this hellish landcape something you just can't suppress?", + "description": "You were assigned the billet of specializing in creating dead zones and providing support fire. When the cataclysm struck, your trusty LMG couldn't keep the veritable tide of undead from overtaking your squad. Now running with all the strength your body can muster, will you have what it takes to survive or is this hellish landscape something you just can't suppress?", "points": 10, "traits": [ "MARTIAL_CRT" ], "skills": [ { "level": 3, "name": "gun" }, { "level": 3, "name": "rifle" }, { "level": 1, "name": "pistol" } ], @@ -332,7 +332,7 @@ "type": "profession", "id": "crt_juggernaught", "name": "CRIT Lone Wolf", - "description": "STR 10 recommended. You are fully armored badass granted the full authority of a U.S Marshal. Sent in as the one man army capable of handling anything. Dropped into warzones, you singlehandedly laid out entire battalions by yourself. Time to hang them all.", + "description": "STR 10 recommended. You are fully armored badass granted the full authority of a U.S Marshal. Sent in as the one man army capable of handling anything. Dropped into war zones, you singlehandedly laid out entire battalions by yourself. Time to hang them all.", "traits": [ "MARTIAL_CRT", "PROF_FED" ], "CBMs": [ "bio_weight", @@ -664,7 +664,7 @@ "type": "profession", "id": "crt_vamp", "name": "CRIT Night Walker", - "description": "Your base in New England fell to the unholy onslaught of the Cataclysm. However, as a a top researcher in the R&D department, you had chosen to slowly mutate yourself into something more than human. Even if the concotion was less than perfect, your old flimsy body feels empowered. With the new flesh that is now your own, bare your fangs and fight until the next dawn.", + "description": "Your base in New England fell to the unholy onslaught of the Cataclysm. However, as a top researcher in the R&D department, you had chosen to slowly mutate yourself into something more than human. Even if the concoction was less than perfect, your old flimsy body feels empowered. With the new flesh that is now your own, bare your fangs and fight until the next dawn.", "traits": [ "MARTIAL_CRT", "THRESH_VAMP", diff --git a/data/mods/CrazyCataclysm/crazy_monsters.json b/data/mods/CrazyCataclysm/crazy_monsters.json index 57808d7c97670..30a5246dfd178 100644 --- a/data/mods/CrazyCataclysm/crazy_monsters.json +++ b/data/mods/CrazyCataclysm/crazy_monsters.json @@ -14,6 +14,20 @@ "copy-from": "mon_zombie_electric", "special_attacks": [ [ "SHOCKING_REVEAL", 25 ] ] }, + { + "id": "halloween_dragon", + "type": "MONSTER", + "name": { "str": "inflatable dragon" }, + "copy-from": "mon_dragon_dummy", + "delete": { "flags": [ "NOT_HALLUCINATION" ] } + }, + { + "id": "halloween_ghost", + "type": "MONSTER", + "name": { "str": "inflatable ghost" }, + "copy-from": "mon_halloween_ghost", + "delete": { "flags": [ "NOT_HALLUCINATION" ] } + }, { "id": "mon_zombie_skeltal", "type": "MONSTER", diff --git a/data/mods/Dark-Skies-Above/blacklists/location_blacklist.json b/data/mods/Dark-Skies-Above/blacklists/location_blacklist.json index 80201b69af785..34aa84c366bdd 100644 --- a/data/mods/Dark-Skies-Above/blacklists/location_blacklist.json +++ b/data/mods/Dark-Skies-Above/blacklists/location_blacklist.json @@ -1,6 +1,6 @@ [ { - "//": "mostly removes areas with their own storylines, static NPCs, and the like. some will be readded with time", + "//": "mostly removes areas with their own storylines, static NPCs, and the like. some will be returned with time", "type": "overmap_special", "id": "Necropolis", "overmaps": [ ], diff --git a/data/mods/Dark-Skies-Above/harvest.json b/data/mods/Dark-Skies-Above/harvest.json index 65b4cdee17af3..75a652a3e5bad 100644 --- a/data/mods/Dark-Skies-Above/harvest.json +++ b/data/mods/Dark-Skies-Above/harvest.json @@ -1,30 +1,14 @@ [ { - "id": "dks_alien_cyborg", - "//": "'alien' but has some metal thrown in to simulate the extensive augments", + "id": "dks_alien_hcyborg", + "//": "a humanoid alien cyborg. metal and scrap simulate augmentation. candidate to receive future bionics", "type": "harvest", "entries": [ - { "drop": "meat_tainted", "type": "flesh", "mass_ratio": 0.25 }, - { "drop": "fat_tainted", "type": "flesh", "mass_ratio": 0.08 }, + { "drop": "mutant_human_flesh", "type": "flesh", "mass_ratio": 0.25 }, + { "drop": "mutant_human_fat", "type": "flesh", "mass_ratio": 0.08 }, { "drop": "bone_tainted", "type": "bone", "mass_ratio": 0.1 }, - { "drop": "cable", "base_num": [ 1, 3 ], "scale_num": [ 0.2, 0.6 ], "max": 8, "type": "flesh" }, - { "drop": "bone_human", "base_num": [ 1, 2 ], "scale_num": [ 0.4, 0.7 ], "max": 10, "type": "bone" }, - { "drop": "scrap", "base_num": [ 1, 5 ], "scale_num": [ 0.3, 0.7 ], "max": 12, "type": "bone" } - ] - }, - { - "//": "e_scrap will be replaced with an alien mind control chip and some aftershock style crafting stuff since I like the tiered crafting idea", - "id": "dks_mhuman_chipped", - "message": "They may have been human, once… whatever the Order has done to them, now they are something else.", - "type": "harvest", - "entries": [ - { "drop": "mutant_human_flesh", "type": "flesh", "mass_ratio": 0.2 }, - { "drop": "hstomach", "scale_num": [ 1, 1 ], "max": 1, "type": "offal" }, - { "drop": "mutant_human_fat", "type": "flesh", "mass_ratio": 0.1 }, - { "drop": "bone_human", "type": "bone", "mass_ratio": 0.12 }, - { "drop": "sinew", "type": "bone", "mass_ratio": 0.001 }, - { "drop": "raw_hleather", "type": "skin", "mass_ratio": 0.01 }, - { "drop": "e_scrap", "type": "bionic", "max": 2 } + { "drop": "dks_elecscrap", "base_num": [ 1, 3 ], "scale_num": [ 0.2, 0.6 ], "max": 8, "type": "flesh" }, + { "drop": "dks_blend_scrap", "base_num": [ 1, 5 ], "scale_num": [ 0.3, 0.7 ], "max": 12, "type": "bone" } ] } ] diff --git a/data/mods/Dark-Skies-Above/items/alien-scrap.json b/data/mods/Dark-Skies-Above/items/alien-scrap.json index 4e71600bb5630..4aa9e7f4502ce 100644 --- a/data/mods/Dark-Skies-Above/items/alien-scrap.json +++ b/data/mods/Dark-Skies-Above/items/alien-scrap.json @@ -78,7 +78,7 @@ "id": "dks_powercell", "category": "spare_parts", "name": { "str": "alien power cell" }, - "description": "A fist-sized, cylindrical canister that makes you feel a bit tingly when you hold it. Its center houses a faintly glowing red core of some sort. Though fundementally incompatable with earthly technologies, it still might be useful in crafting.", + "description": "A fist-sized, cylindrical canister that makes you feel a bit tingly when you hold it. Its center houses a faintly glowing red core of some sort. Though fundamentally incompatible with earthly technologies, it still might be useful in crafting.", "weight": "80 g", "volume": "30 ml", "price": 15000, diff --git a/data/mods/Dark-Skies-Above/items/electronics.json b/data/mods/Dark-Skies-Above/items/electronics.json index d955b72c7eb39..39ea2450b2825 100644 --- a/data/mods/Dark-Skies-Above/items/electronics.json +++ b/data/mods/Dark-Skies-Above/items/electronics.json @@ -15,12 +15,12 @@ }, { "type": "GENERIC", - "id": "broken_dks_emissary_war", + "id": "broken_dks_emissary_plague", "symbol": ",", "color": "green", - "name": { "str": "broken emissary of war", "str_pl": "broken emissaries of war" }, + "name": { "str": "broken emissary of plague", "str_pl": "broken emissaries of plague" }, "category": "other", - "description": "The massive body of a collapsed emissary of war. Still a bit intimidating, perhaps knowing the damage it can cause. Could be gutted for parts, but you'll probably need specialized alien tools.", + "description": "The massive body of a collapsed emissary of plague. Still a bit intimidating, perhaps knowing the damage it can cause. Could be gutted for parts, but you'll probably need specialized alien tools.", "price": 1000, "material": [ "superalloy" ], "volume": "875000 ml", diff --git a/data/mods/Dark-Skies-Above/mapgen/map_extras/bombed_crater.json b/data/mods/Dark-Skies-Above/mapgen/map_extras/bombed_crater.json new file mode 100644 index 0000000000000..cfa896c6537d1 --- /dev/null +++ b/data/mods/Dark-Skies-Above/mapgen/map_extras/bombed_crater.json @@ -0,0 +1,47 @@ +[ + { + "id": "tool_dsa_alien_bomb_act", + "type": "TOOL", + "category": "weapons", + "name": { "str": "active alien bomb" }, + "looks_like": "tool_rdx_charge_act", + "description": "This is an alien bomb. It has been activated and will soon explode, delivering its entire destructive power to everything in sight.", + "weight": "4400 g", + "volume": "10 L", + "price": 0, + "to_hit": -5, + "bashing": 20, + "material": [ "steel" ], + "symbol": "(", + "color": "light_red", + "initial_charges": 1, + "max_charges": 1, + "turns_per_charge": 1, + "explode_in_fire": true, + "explosion": { "power": 40000, "max_noise": 0 }, + "use_action": { + "type": "explosion", + "no_deactivate_msg": "You've already activated the bomb - clear the area immediately!", + "explosion": { "power": 40000, "max_noise": 0 } + }, + "flags": [ "BOMB", "TRADER_AVOID" ] + }, + { + "type": "mapgen", + "method": "json", + "update_mapgen_id": "mx_dsa_bombed_crater", + "object": { + "place_item": [ { "item": "tool_dsa_alien_bomb_act", "x": 12, "y": 12, "amount": 1, "custom-flags": [ "ACTIVATE_ON_PLACE" ] } ] + } + }, + { + "id": "mx_dsa_bombed_crater", + "type": "map_extra", + "name": { "str": "Bomb Crater" }, + "description": "A bomb crater.", + "generator": { "generator_method": "update_mapgen", "generator_id": "mx_dsa_bombed_crater" }, + "sym": ".", + "color": "brown", + "autonote": true + } +] diff --git a/data/mods/Dark-Skies-Above/mapgen/map_extras/patrols.json b/data/mods/Dark-Skies-Above/mapgen/map_extras/patrols.json new file mode 100644 index 0000000000000..2476303940849 --- /dev/null +++ b/data/mods/Dark-Skies-Above/mapgen/map_extras/patrols.json @@ -0,0 +1,25 @@ +[ + { + "type": "mapgen", + "method": "json", + "update_mapgen_id": "mx_dsa_alrp", + "object": { + "place_monster": [ + { + "monster": "dks_mon_skirmsoldier", + "x": 12, + "y": 12, + "repeat": [ 3, 4 ], + "spawn_data": { "patrol": [ { "x": -10, "y": -10 }, { "x": 33, "y": -10 }, { "x": 33, "y": 33 }, { "x": 33, "y": -10 } ] } + } + ] + } + }, + { + "id": "mx_dsa_alrp", + "type": "map_extra", + "name": { "str": "DSA ALRP" }, + "description": "Alien light reconnaissance patrol.", + "generator": { "generator_method": "update_mapgen", "generator_id": "mx_dsa_alrp" } + } +] diff --git a/data/mods/Dark-Skies-Above/materials.json b/data/mods/Dark-Skies-Above/materials.json index 4c31f44276120..08091c770436a 100644 --- a/data/mods/Dark-Skies-Above/materials.json +++ b/data/mods/Dark-Skies-Above/materials.json @@ -18,8 +18,7 @@ "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", "cut_dmg_verb": "scratched", - "burn_products": [ [ "dks_blend_scrap", 1 ] ], - "compacts_into": [ "dks_blend_scrap" ] + "burn_products": [ [ "dks_blend_scrap", 1 ] ] }, { "type": "material", diff --git a/data/mods/Dark-Skies-Above/modinfo.json b/data/mods/Dark-Skies-Above/modinfo.json index 8f895f383daea..264cfd611a4a5 100644 --- a/data/mods/Dark-Skies-Above/modinfo.json +++ b/data/mods/Dark-Skies-Above/modinfo.json @@ -5,7 +5,7 @@ "name": "Dark Skies Above", "authors": [ "ephemeral_storyteller", "mlangsdorf" ], "description": "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.", - "category": "content", + "category": "total_conversion", "dependencies": [ "dda" ] } ] diff --git a/data/mods/Dark-Skies-Above/monattack.json b/data/mods/Dark-Skies-Above/monattack.json index c7e85ffcb9a47..ae924ef9cf4dc 100644 --- a/data/mods/Dark-Skies-Above/monattack.json +++ b/data/mods/Dark-Skies-Above/monattack.json @@ -42,5 +42,31 @@ "hit_dmg_npc": "The %1$s slashes !", "no_dmg_msg_u": "The %1$s attempts to cut you, but fails to penetrate your armor.", "no_dmg_msg_npc": "The %1$s tries to cut , but fails to penetrate their armor." + }, + { + "type": "monster_attack", + "attack_type": "melee", + "id": "melee_shock", + "cooldown": 20, + "move_cost": 150, + "damage_max_instance": [ { "damage_type": "electric", "amount": 8 }, { "damage_type": "stab", "amount": 6 } ], + "body_parts": [ + [ "foot_l", 2 ], + [ "foot_r", 2 ], + [ "leg_l", 3 ], + [ "leg_r", 3 ], + [ "hand_l", 2 ], + [ "hand_r", 2 ], + [ "head", 3 ], + [ "eyes", 2 ], + [ "mouth", 1 ], + [ "arm_l", 3 ], + [ "arm_r", 3 ], + [ "torso", 4 ] + ], + "hit_dmg_u": "The %1$s lances electricity into you!", + "hit_dmg_npc": "The %1$s lances with electricity!", + "no_dmg_msg_u": "The %1$s attempts to electrify you, but fails to penetrate your armor.", + "no_dmg_msg_npc": "The %1$s tries to electrify , but fails to penetrate their armor." } ] diff --git a/data/mods/Dark-Skies-Above/mondrops.json b/data/mods/Dark-Skies-Above/mondrops.json deleted file mode 100644 index c1a8683142ee4..0000000000000 --- a/data/mods/Dark-Skies-Above/mondrops.json +++ /dev/null @@ -1,90 +0,0 @@ -[ - { - "id": "dks_mon_neworder_scout", - "type": "item_group", - "subtype": "collection", - "magazine": 100, - "ammo": 40, - "entries": [ - { "group": "underwear", "damage": [ 0, 1 ] }, - { "item": "dks_neworder_armor_salvaged", "damage": [ 1, 3 ] }, - { "item": "machete" }, - { "group": "clothing_watch", "prob": 20 }, - { "item": "knife_combat", "container-item": "sheath", "prob": 40 }, - { "item": "cash_card", "prob": 20, "charges-min": 0, "charges-max": 50000 }, - { "group": "dks_scout_extras", "prob": 60 } - ] - }, - { - "id": "dks_mon_neworder_cop", - "type": "item_group", - "subtype": "collection", - "magazine": 100, - "ammo": 40, - "entries": [ - { "group": "underwear", "damage": [ 0, 1 ] }, - { "item": "dks_neworder_armor_salvaged", "damage": [ 1, 3 ] }, - { "item": "shocktonfa_off", "charges-min": 10, "charges-max": 1198 }, - { "group": "clothing_watch", "prob": 20 }, - { "item": "dks_riotshield" }, - { "item": "cash_card", "prob": 20, "charges-min": 0, "charges-max": 50000 }, - { "group": "dks_cop_extras", "prob": 60, "count": [ 0, 2 ] } - ] - }, - { - "id": "dks_mon_neworder_pyro", - "type": "item_group", - "subtype": "collection", - "magazine": 100, - "ammo": 40, - "entries": [ - { "group": "underwear", "damage": [ 0, 1 ] }, - { "item": "dks_neworder_armor_salvaged", "damage": [ 1, 3 ] }, - { "group": "clothing_watch", "prob": 20 }, - { "item": "dks_flamesword_salvaged" }, - { "item": "cash_card", "prob": 20, "charges": [ 0, 50000 ] }, - { "group": "dks_knight_extras", "prob": 50, "count": [ 0, 2 ] } - ] - }, - { - "id": "dks_mon_neworder_knight", - "type": "item_group", - "subtype": "collection", - "magazine": 100, - "ammo": 40, - "entries": [ - { "group": "underwear", "damage": [ 0, 1 ] }, - { "item": "dks_neworder_armor_salvaged", "damage": [ 1, 3 ] }, - { "group": "clothing_watch", "prob": 20 }, - { "item": "dks_knightsword_salvaged" }, - { "item": "dks_battleshield" }, - { "item": "cash_card", "prob": 20, "charges": [ 0, 50000 ] }, - { "group": "dks_knight_extras", "prob": 50, "count": [ 0, 2 ] } - ] - }, - { - "id": "dks_cop_extras", - "type": "item_group", - "items": [ - { "group": "full_1st_aid", "prob": 25 }, - { "item": "legrig", "prob": 10 }, - { "item": "dump_pouch", "prob": 10 }, - { "item": "medium_disposable_cell", "prob": 30 } - ] - }, - { - "id": "dks_scout_extras", - "type": "item_group", - "items": [ - { "group": "full_1st_aid", "prob": 25 }, - { "item": "legrig", "prob": 10 }, - { "item": "dump_pouch", "prob": 10 }, - { "item": "binoculars", "prob": 30 } - ] - }, - { - "id": "dks_knight_extras", - "type": "item_group", - "items": [ { "item": "1st_aid", "prob": 25 }, { "item": "legrig", "prob": 10 }, { "item": "dump_pouch", "prob": 10 } ] - } -] diff --git a/data/mods/Dark-Skies-Above/mongun.json b/data/mods/Dark-Skies-Above/mongun.json index 7c722b2b0ec77..cbf5f422aed3e 100644 --- a/data/mods/Dark-Skies-Above/mongun.json +++ b/data/mods/Dark-Skies-Above/mongun.json @@ -18,7 +18,7 @@ "color": "red", "name": { "str": "fake firecannon" }, "ranged_damage": { "damage_type": "heat", "amount": 20, "armor_penetration": 30 }, - "description": "Fires an explosive bolt of firey energy. If you're seeing this, it's a bug.", + "description": "Fires an explosive bolt of fiery energy. If you're seeing this, it's a bug.", "skill": "launcher", "range": 15, "flags": [ "NEVER_JAMS" ], diff --git a/data/mods/Dark-Skies-Above/monspell.json b/data/mods/Dark-Skies-Above/monspell.json index a57b28498426e..5460cc86ac210 100644 --- a/data/mods/Dark-Skies-Above/monspell.json +++ b/data/mods/Dark-Skies-Above/monspell.json @@ -14,5 +14,20 @@ "min_duration": 425, "max_duration": 425, "flags": [ "SILENT", "NO_PROJECTILE" ] + }, + { + "type": "SPELL", + "id": "dks_summon_alrp", + "name": { "str": "Spawn ALRP" }, + "description": "Summons a squad of 3-4 skirmishers.", + "flags": [ "HOSTILE_SUMMON", "RANDOM_DAMAGE", "PERMANENT" ], + "valid_targets": [ "ground", "self" ], + "min_damage": 3, + "max_damage": 4, + "min_aoe": 2, + "max_aoe": 2, + "shape": "blast", + "effect": "summon", + "effect_str": "dks_mon_skirmsoldier" } ] diff --git a/data/mods/Dark-Skies-Above/monsters/alien_cyborgs.json b/data/mods/Dark-Skies-Above/monsters/alien_cyborgs.json index 6305f45b6b940..6eea495a4ba5c 100644 --- a/data/mods/Dark-Skies-Above/monsters/alien_cyborgs.json +++ b/data/mods/Dark-Skies-Above/monsters/alien_cyborgs.json @@ -24,6 +24,7 @@ "melee_cut": 2, "armor_bash": 90, "armor_cut": 90, + "armor_bullet": 100, "armor_acid": 10, "path_settings": { "max_dist": 30 }, "death_drops": { "groups": [ [ "robots", 4 ], [ "eyebot", 1 ], [ "turret_searchlight", 1 ] ] }, @@ -93,6 +94,7 @@ "melee_cut": 2, "armor_bash": 90, "armor_cut": 90, + "armor_bullet": 100, "armor_acid": 10, "path_settings": { "max_dist": 30 }, "emit_fields": [ { "emit_id": "emit_toxic_leak", "delay": "1 s" } ], @@ -161,6 +163,7 @@ "melee_cut": 2, "armor_bash": 90, "armor_cut": 90, + "armor_bullet": 100, "armor_acid": 10, "path_settings": { "max_dist": 30 }, "death_drops": { "groups": [ [ "robots", 4 ], [ "eyebot", 1 ], [ "turret_searchlight", 1 ] ] }, @@ -207,7 +210,7 @@ "id": "dks_mon_lurker_rectified", "type": "MONSTER", "name": { "str": "recycler" }, - "description": "A sleek mass of hairy tentacles originate from a metallic core that looks like a mix between a garbage disposal and a clamp, golden pinpoints of dim light dotting its chassis. It seems adept at collecting small pieces of junk and debris and mulching them in its mouth for unknown purposes - you included.", + "description": "A sleek mass of hairy tentacles originate from a metallic core that looks like a mix between a garbage disposal and a clamp, golden pinpoints of dim light dotting its chassis. It seems adept at collecting small pieces of junk and debris to mulch them into their bae parts - you included.", "default_faction": "invader_alien", "bodytype": "spider", "categories": [ "ALIEN" ], @@ -248,7 +251,7 @@ "id": "dks_mon_bileworm_rectified", "type": "MONSTER", "name": { "str": "tunneler" }, - "description": "A huge creature, encased in metal plating and covered in small golden \"eyes\". Stocky robotic appendages covering its body allow it to smash through rock and propel itself forward with efficiency. Its interior seems to have been replaced by some sort of whirring processing equipment that would be highly unpleasant to be caught up in. Fortunately it doesn't seem very hostile.", + "description": "A huge creature, encased in metal plating and covered in small golden \"eyes\". Stocky robotic appendages covering its body allow it to smash through rock and propel itself forward with efficiency. Its interior seems to have been replaced by some sort of whirring processing equipment that would be highly unpleasant to be caught up in. Fortunately it doesn't seem immediately interested in you.", "looks_like": "mon_worm", "default_faction": "invader_alien", "bodytype": "snake", @@ -266,8 +269,9 @@ "melee_skill": 4, "melee_dice": 4, "melee_dice_sides": 6, - "armor_bash": 4, - "armor_cut": 2, + "armor_bash": 12, + "armor_cut": 6, + "armor_bullet": 6, "vision_night": 15, "harvest": "mutant_meatslug", "special_attacks": [ @@ -286,5 +290,39 @@ "anger_triggers": [ "FRIEND_ATTACKED", "HURT" ], "death_function": [ "NORMAL" ], "flags": [ "SEES", "HEARS", "SMELLS", "BASHES", "DESTROYS", "ACIDPROOF", "CAN_DIG", "ARTHROPOD_BLOOD" ] + }, + { + "id": "dks_mon_skirmsoldier", + "type": "MONSTER", + "name": { "str": "alien skirmisher" }, + "description": "A slouched bipedal figure, slightly smaller than an average adult human. Beneath woven full-body combat armor and a full suite of cybernetic modifications are glimpses of rubbery, abscessed flesh. It is capable of short, boosted jumps using a device on its back, closing gaps to cause havoc with its wicked integrated melee weapons. Severed organic parts dangle from its equipment, morbid trophies from past battles.", + "default_faction": "invader_alien", + "bodytype": "human", + "categories": [ "ALIEN" ], + "species": [ "ROBOT" ], + "volume": "52500 ml", + "weight": "71500 g", + "hp": 45, + "speed": 90, + "material": [ "steel" ], + "symbol": "$", + "aggression": 100, + "morale": 100, + "color": "light_gray", + "dodge": 1, + "melee_skill": 5, + "melee_dice": 4, + "melee_dice_sides": 3, + "armor_bash": 15, + "armor_stab": 25, + "armor_cut": 6, + "armor_fire": 15, + "armor_bullet": 35, + "vision_night": 5, + "special_attacks": [ { "type": "leap", "cooldown": 10, "max_range": 5 }, { "id": "melee_shock" } ], + "path_settings": { "max_dist": 5 }, + "death_function": [ "NORMAL" ], + "harvest": "dks_alien_hcyborg", + "flags": [ "SEES", "HEARS", "SMELLS", "SWARMS", "WARM", "BASHES", "GROUP_BASH", "PRIORITIZE_TARGETS", "PATH_AVOID_DANGER_2" ] } ] diff --git a/data/mods/Dark-Skies-Above/monsters/alien_fauna.json b/data/mods/Dark-Skies-Above/monsters/alien_fauna.json index ead0b24f258bc..080acdb4c813e 100644 --- a/data/mods/Dark-Skies-Above/monsters/alien_fauna.json +++ b/data/mods/Dark-Skies-Above/monsters/alien_fauna.json @@ -42,7 +42,7 @@ "id": "dks_mon_lurker", "type": "MONSTER", "name": { "str": "lurker" }, - "description": "A long, serpentine body, dark and murky just like the waters it inhabits. Tendrils trail behind it, framing innumerable teeth in a gnashing central mouth. It prefers to lurk just below the surface, drifting and still like an unassuming piece of floatsam until something gets too close.", + "description": "A long, serpentine body, dark and murky just like the waters it inhabits. Tendrils trail behind it, framing innumerable teeth in a gnashing central mouth. It prefers to lurk just below the surface, drifting and still, like an unassuming piece of flotsam until something gets too close.", "looks_like": "dks_mon_lurker_sewer", "default_faction": "lurker", "bodytype": "spider", @@ -84,7 +84,7 @@ "id": "dks_mon_mossliz", "type": "MONSTER", "name": "moss lizard", - "description": "A cow-sized reptoid, covered in what appears to be a thick layer of mossy vegetation and rocks. It trudges about the landscape on sturdy legs, foraging whatever flora it can reach with sharp little teeth. It doesn't seem to mind you too much.", + "description": "A cow-sized reptiloid, covered in what appears to be a thick layer of mossy vegetation and rocks. It trudges about the landscape on sturdy legs, foraging whatever flora it can reach with sharp little teeth. It doesn't seem to mind you too much.", "default_faction": "alien_herbivore", "bodytype": "gator", "categories": [ "ALIEN" ], @@ -130,7 +130,7 @@ "id": "dks_mon_packliz", "type": "MONSTER", "name": "pack lizard", - "description": "A slim reptoid about the size of a big dog, with a thick mane of what looks like rather pretty orange-blue 'feathers' around its head. Frond-like protrusions grow from the top of its head down to the base of its tail, glowing faintly when in the presence of other members of its pack.", + "description": "A slim reptiloid about the size of a big dog, with a rather pretty, thick, orange-blue mane of what look like 'feathers' around its head. Frond-like protrusions grow from the top of its head down to the base of its tail, glowing faintly when in the presence of other members of its pack.", "default_faction": "alien_packliz", "bodytype": "gator", "categories": [ "ALIEN" ], diff --git a/data/mods/Dark-Skies-Above/monsters/alien_robots.json b/data/mods/Dark-Skies-Above/monsters/alien_robots.json index 7885a4fdcbef6..e2267a8587ea3 100644 --- a/data/mods/Dark-Skies-Above/monsters/alien_robots.json +++ b/data/mods/Dark-Skies-Above/monsters/alien_robots.json @@ -1,9 +1,34 @@ [ + { + "id": "mon_dsa_alien_dispatch", + "type": "MONSTER", + "name": { "str": "alien dispatch system" }, + "description": "A monstrous intelligence, capable of teleporting drones and skirmishers to the planet, or dispatching larger aliens in drop pods.", + "default_faction": "invader_alien", + "looks_like": "mon_eyebot", + "categories": [ "ALIEN" ], + "species": [ "ROBOT" ], + "volume": "30000 ml", + "weight": "40750 g", + "hp": 400, + "speed": 100, + "material": [ "steel" ], + "symbol": "D", + "color": "red", + "dodge": 3, + "armor_bash": 8, + "armor_cut": 10, + "luminance": 5, + "path_settings": { "max_dist": 5 }, + "death_drops": { "groups": [ [ "robots", 4 ] ] }, + "death_function": [ "MELT" ], + "flags": [ "SEES", "FLIES", "ELECTRONIC", "COLDPROOF", "NO_BREATHE", "NOHEAD", "PRIORITIZE_TARGETS" ] + }, { "id": "mon_dks_glowdrone", "type": "MONSTER", "name": { "str": "alien surveillance drone" }, - "description": "A small, sleek, white-plated robot bearing a golden insignia. Capable of limited flight, it hovers about the ruins of the former world, onboard cameras and spotlights impartial witness to the chaos around it. Who knows might be watching on the other side…", + "description": "A small, sleek, white-plated robot bearing a golden insignia that is capable of limited flight. It hovers about the ruins of the former world, onboard cameras and spotlights impartial witness to the chaos around it. Who knows might be watching on the other side…", "default_faction": "invader_alien", "looks_like": "mon_eyebot", "categories": [ "ALIEN" ], @@ -18,8 +43,9 @@ "dodge": 3, "armor_bash": 8, "armor_cut": 10, + "armor_bullet": 6, "luminance": 5, - "special_attacks": [ [ "SEARCHLIGHT", 1 ] ], + "special_attacks": [ [ "SEARCHLIGHT", 1 ], [ "DSA_DRONE_SCAN", 2 ] ], "path_settings": { "max_dist": 5 }, "death_drops": { "groups": [ [ "robots", 4 ], [ "eyebot", 1 ], [ "turret_searchlight", 1 ] ] }, "death_function": [ "BROKEN" ], @@ -29,7 +55,7 @@ "id": "mon_dks_scidrone", "type": "MONSTER", "name": { "str": "alien seeker drone" }, - "description": "A small, sleek, white-plated robot bearing a golden insignia. Capable of limited flight, it hovers about the ruins of the former world, using a suite of rather sharp looking retractable tools, a bright camera flash, and integrated 'arms' to manipulate the world around it with surprising dexterity and procure samples. It seems to watch you closely once it catches sight of you…", + "description": "A small, sleek, white-plated robot bearing a golden insignia that is capable of limited flight. It hovers about the ruins of the former world, using a suite of sharp retractable tools, a bright camera flash, and integrated 'arms' to manipulate the world around it. It seems to watch its subjects closely…", "default_faction": "invader_alien", "looks_like": "mon_eyebot", "categories": [ "ALIEN" ], @@ -46,10 +72,12 @@ "dodge": 3, "armor_bash": 10, "armor_cut": 12, + "armor_bullet": 8, "luminance": 10, "vision_night": 5, "special_attacks": [ [ "TAZER", 12 ], + [ "DSA_DRONE_SCAN", 5 ], { "id": "tool_slash" }, { "type": "spell", diff --git a/data/mods/Dark-Skies-Above/monsters/strays.json b/data/mods/Dark-Skies-Above/monsters/strays.json index a479d61b33fcd..97cf7eb220967 100644 --- a/data/mods/Dark-Skies-Above/monsters/strays.json +++ b/data/mods/Dark-Skies-Above/monsters/strays.json @@ -74,7 +74,7 @@ "id": "dks_mon_stray_soldier", "type": "MONSTER", "name": { "str": "stray soldier" }, - "description": "A former soldier, no doubt deployed to assist with evacuations and drive off the aliens, dressed from head to toe in partially crystalized combat armor. Though their training could not have prepared them for what they were up against, they still seem to remember enough to take you on.", + "description": "A former soldier, no doubt deployed to assist with evacuations and drive off the aliens, dressed from head to toe in partially crystallized combat armor. Though their training could not have prepared them for what they were up against, they still seem to remember enough to take you on.", "looks_like": "mon_zombie_soldier", "default_faction": "stray", "bodytype": "human", @@ -500,7 +500,7 @@ "id": "dks_mon_stray_predator", "type": "MONSTER", "name": { "str": "stray guardian" }, - "description": "Lithe muscle and pulsating crystal fused together into a mass that must be made up of multiple bodies, propelled forward by multiple grossly enlongated crystal limbs sharpened to dangerous points. It strides about the streets, spearing those who dare intrude on its domain like some sort of horrid spider from beyond the stars.", + "description": "Lithe muscle and pulsating crystal fused together into a mass that must be made up of multiple bodies, propelled forward by multiple grossly elongated crystal limbs sharpened to dangerous points. It strides about the streets, spearing those who dare intrude on its domain like some sort of horrid spider from beyond the stars.", "default_faction": "stray", "looks_like": "mon_zombie_predator", "bodytype": "human", @@ -748,7 +748,7 @@ "id": "dks_mon_stray_wretch_burnt", "type": "MONSTER", "name": { "str": "stray creep" }, - "description": "A terrifying, hairy husk of a creature scrambling about on all fours, a mongrel housepet or the like covered in patches of crystal growths that jut from it like spikes.", + "description": "A terrifying, hairy husk of a creature scrambling about on all fours, a mongrel house pet or the like covered in patches of crystal growths that jut from it like spikes.", "default_faction": "stray", "looks_like": "mon_dog_zombie_rot", "bodytype": "dog", @@ -793,7 +793,7 @@ "id": "dks_mon_stray_wretch", "type": "MONSTER", "name": { "str": "stray wretch", "str_pl": "stray wretches" }, - "description": "This blur of jagged, crystal-fused limbs and hair could have been anything from a housepet to a human at some point, but now it leaps and skitters around like something out of a nightmare.", + "description": "This blur of jagged, crystal-fused limbs and hair could have been anything from a house pet to a human at some point, but now it leaps and skitters around like something out of a nightmare.", "default_faction": "stray", "looks_like": "mon_zombie_dog", "bodytype": "dog", @@ -1112,7 +1112,7 @@ "id": "dks_mon_crystal_whip", "type": "MONSTER", "name": { "str": "flailing crystal mass", "str_pl": "flailing crystal masses" }, - "description": "A tall, singular crystal, growing out of a sizable pile of debris that has sprouted a multitude of thin, whiplike tendrils that constantly snake around it like feelers. It frequently grabs nearby objects and drags them into the pile beneath it, as if hoarding.", + "description": "A tall, singular crystal, growing out of a sizable pile of debris that has sprouted a multitude of thin, whip-like tendrils that constantly snake around it like feelers. It frequently grabs nearby objects and drags them into the pile beneath it, as if hoarding.", "default_faction": "stray", "bodytype": "blob", "categories": [ "ALIEN" ], @@ -1236,7 +1236,7 @@ "id": "dks_mon_crystal_mite", "type": "MONSTER", "name": { "str": "crystal seed" }, - "description": "A tiny, multilegged creature that appears to be made of a chunk of crystal. It skitters around on wire-like legs, eating bits of organic leftovers to gain mass in hopes of one day seeding a crystal colony of its own.", + "description": "A tiny, multi-legged creature that appears to be made of a chunk of crystal. It skitters around on wire-like legs, eating bits of organic leftovers. Possibly to gain mass in hopes of one day seeding a crystal colony of its own.", "default_faction": "stray", "bodytype": "spider", "categories": [ "ALIEN" ], diff --git a/data/mods/Dark-Skies-Above/obsolete.json b/data/mods/Dark-Skies-Above/obsolete.json index 775497a48f981..d9fd020b4da5b 100644 --- a/data/mods/Dark-Skies-Above/obsolete.json +++ b/data/mods/Dark-Skies-Above/obsolete.json @@ -5,7 +5,7 @@ "category": "weapons", "looks_like": "zweihander", "name": { "str": "salvaged consecrator's sword" }, - "description": "A well built and decorated sword forged from dense alien metal. Its ability to conjure fireballs does not seem to respond to your will, but the cutting edge is perfectly servicable.", + "description": "A well built and decorated sword forged from dense alien metal. Its ability to conjure fireballs does not seem to respond to your will, but the cutting edge is perfectly serviceable.", "weight": "5400 g", "volume": "3750 ml", "price": 310000, @@ -27,7 +27,7 @@ "color": "light_gray", "looks_like": "arming_sword", "name": { "str": "salvaged knight's sword" }, - "description": "A well built sword made from dense alien metal, in service of the knights of the New Order. The runes that adorn it no longer glow, but its cutting edge is perfectly servicable.", + "description": "A well built sword made from dense alien metal, in service of the knights of the New Order. The runes that adorn it no longer glow, but its cutting edge is perfectly serviceable.", "price": 100000, "material": [ "superalloy" ], "techniques": [ "WBLOCK_2" ], @@ -96,7 +96,7 @@ "type": "ARMOR", "name": "new order battle shield", "category": "armor", - "description": "A well forged shield made of steel, emblazed with what appears to be a highly stylized depiction of a sword piercing a star.", + "description": "A well forged shield made of steel, emblazoned with what appears to be a highly stylized depiction of a sword piercing a star.", "weight": "3300 g", "volume": "5 L", "price": 110000, diff --git a/data/mods/Dark-Skies-Above/overrides/items/carnivore.json b/data/mods/Dark-Skies-Above/overrides/items/carnivore.json index 2ac90c7661050..66773192c0b29 100644 --- a/data/mods/Dark-Skies-Above/overrides/items/carnivore.json +++ b/data/mods/Dark-Skies-Above/overrides/items/carnivore.json @@ -13,7 +13,7 @@ "id": "meat_tainted", "copy-from": "meat_tainted", "name": { "str": "chunk of alien meat", "str_pl": "chunks of alien meat" }, - "description": "This dense, stringy substance smells strongly like burnt oil, or some sort of industrial chemical. For all intents and purposes, it seems like the 'meat' of the creature, but its bizarre texture and faintly red-purple tint is unlike anything you've ever experienced before. You could eat it, but no doubt it would not only taste henious but also make you sick, being made of material your body was never supposed to injest." + "description": "This dense, stringy substance smells strongly like burnt oil, or some sort of industrial chemical. For all intents and purposes, it seems like the 'meat' of the creature, but its bizarre texture and faintly red-purple tint is unlike anything you've ever experienced before. You could eat it, but no doubt it would not only taste heinous but also make you sick, being made of material your body was never supposed to ingest." }, { "type": "COMESTIBLE", @@ -36,7 +36,7 @@ "category": "other", "copy-from": "fat_tainted", "name": "alien fat", - "description": "A chunk of dense fat that is tough and rubbery. It smells simply henious, like open sewer and pungent chemicals, and is doubtless full of strange material that your body was never supposed to injest. It might at least be able to be used for something, if not quite as well as its earthly variant due to its relative impurities." + "description": "A chunk of dense fat that is tough and rubbery. It smells simply heinous, like open sewer and pungent chemicals, and is doubtless full of strange material that your body was never supposed to injest. It might at least be able to be used for something, if not quite as well as its earthly variant due to its relative impurities." }, { "type": "COMESTIBLE", @@ -51,7 +51,7 @@ "copy-from": "mutant_meat", "type": "COMESTIBLE", "name": { "str": "chunk of unusual meat", "str_pl": "chunks of unusual meat" }, - "description": "Meat from the aliens is typically quite foul, full of toxins and substances not meant to be digested by humans, however this piece almost looks edible - if not for a few sections that still have strange hues and disgusting, spongey texture. Still, with a bit of preperation, it might even be somewhat palatable." + "description": "Meat from the aliens is typically quite foul, full of toxins and substances not meant to be digested by humans. However, this piece almost looks edible - if not for a few sections that still have strange hues and disgusting, spongy texture. Still, with a bit of preparation, it might even be somewhat palatable." }, { "id": "mutant_meat_scrap", diff --git a/data/mods/Dark-Skies-Above/overrides/items/tools.json b/data/mods/Dark-Skies-Above/overrides/items/tools.json index b2fb5b286510b..e5e6269968ac9 100644 --- a/data/mods/Dark-Skies-Above/overrides/items/tools.json +++ b/data/mods/Dark-Skies-Above/overrides/items/tools.json @@ -25,7 +25,7 @@ "copy-from": "e_handcuffs", "type": "TOOL", "name": { "str_sp": "electronic handcuffs" }, - "description": "A pair of electronic handcuffs, used by automated New Order units to detain captives. Their continuous siren clearly identifies the wearer as a person of interet and alerts nearby 'safety teams' to their presence. Wait for their arrival, don't try to escape or to remove the cuffs - they will administer an electric shock.\nHowever, since capture is out of the question, you're probably in for a painful time, unless you get creative…" + "description": "A pair of electronic handcuffs, used by automated New Order units to detain captives. Their continuous siren clearly identifies the wearer as a person of internet and alerts nearby 'safety teams' to their presence. Wait for their arrival, don't try to escape or to remove the cuffs - they will administer an electric shock.\nHowever, since capture is out of the question, you're probably in for a painful time, unless you get creative…" }, { "id": "trimmer_off", diff --git a/data/mods/Dark-Skies-Above/overrides/monsters.json b/data/mods/Dark-Skies-Above/overrides/monsters.json index ea7a618811662..b0278f7d84b82 100644 --- a/data/mods/Dark-Skies-Above/overrides/monsters.json +++ b/data/mods/Dark-Skies-Above/overrides/monsters.json @@ -1,56 +1,107 @@ [ { - "id": "mon_c4_hack", - "copy-from": "mon_c4_hack", + "id": "mon_rattlesnake_giant", + "copy-from": "mon_rattlesnake_giant", "type": "MONSTER", - "name": { "str": "C-4 hack" }, - "extend": { "categories": [ "ALIEN" ] }, - "description": "An automated kamikaze drone, this small flying robot appears to have some C-4 inside." + "name": "giant rattlesnake", + "delete": { "categories": [ "WILDLIFE" ] } }, { - "id": "mon_flashbang_hack", - "copy-from": "mon_flashbang_hack", + "id": "mon_nakedmolerat_giant", + "copy-from": "mon_nakedmolerat_giant", "type": "MONSTER", - "name": { "str": "flashbang hack" }, - "extend": { "categories": [ "ALIEN" ] }, - "description": "An automated kamikaze drone, this small flying robot appears to have a flashbang inside." + "name": "gigantic naked mole-rat", + "delete": { "categories": [ "WILDLIFE" ] } }, { - "id": "mon_gasbomb_hack", - "copy-from": "mon_gasbomb_hack", + "id": "mon_crow_mutant_small", + "copy-from": "mon_crow_mutant_small", "type": "MONSTER", - "name": { "str": "tear gas hack" }, - "extend": { "categories": [ "ALIEN" ] }, - "description": "An automated kamikaze drone, this small flying robot appears to have a tear gas canister inside." + "name": { "str": "oversized crow" }, + "delete": { "categories": [ "WILDLIFE" ] } }, { - "id": "mon_grenade_hack", - "copy-from": "mon_grenade_hack", + "id": "mon_cockatrice", + "copy-from": "mon_cockatrice", "type": "MONSTER", - "name": { "str": "grenade hack" }, - "extend": { "categories": [ "ALIEN" ] }, - "description": "An automated kamikaze drone, this small flying robot appears to have a grenade inside." + "name": { "str": "cockatrice" }, + "delete": { "categories": [ "WILDLIFE" ] } }, { - "id": "mon_manhack", - "copy-from": "mon_manhack", + "id": "mon_bear_mutant_3headed", "type": "MONSTER", - "name": { "str": "manhack" }, - "extend": { "categories": [ "ALIEN" ] }, - "description": "An automated anti-personnel drone, a small flying robot surrounded by whirring blades." + "name": { "str": "Cerbearus", "str_pl": "Cerberuses" }, + "copy-from": "mon_bear_mutant_3headed", + "delete": { "categories": [ "WILDLIFE" ] } }, { - "id": "mon_rattlesnake_giant", - "copy-from": "mon_rattlesnake_giant", + "id": "mon_beaver_mutant_huge", "type": "MONSTER", - "name": "giant rattlesnake", + "name": { "str": "dambreaker" }, + "copy-from": "mon_beaver_mutant_huge", "delete": { "categories": [ "WILDLIFE" ] } }, { - "id": "mon_nakedmolerat_giant", - "copy-from": "mon_nakedmolerat_giant", + "id": "mon_beaver_mutant_avian", "type": "MONSTER", - "name": "gigantic naked mole-rat", + "name": { "str": "feaver" }, + "copy-from": "mon_beaver_mutant_avian", + "delete": { "categories": [ "WILDLIFE" ] } + }, + { + "id": "mon_cat_mutant_prism", + "type": "MONSTER", + "name": { "str": "iridescent cat" }, + "copy-from": "mon_cat_mutant_prism", + "delete": { "categories": [ "WILDLIFE" ] } + }, + { + "id": "mon_cat_mutant_kitten_prism", + "type": "MONSTER", + "name": { "str": "iridescent kitten" }, + "copy-from": "mon_cat_mutant_kitten_prism", + "delete": { "categories": [ "WILDLIFE" ] } + }, + { + "id": "mon_coyote_mutant_shark", + "type": "MONSTER", + "name": { "str": "grinning coyote" }, + "copy-from": "mon_coyote_mutant_shark", + "delete": { "categories": [ "WILDLIFE" ] } + }, + { + "id": "mon_coyote_mutant_venom", + "type": "MONSTER", + "name": { "str": "slavering coyote" }, + "copy-from": "mon_coyote_mutant_venom", + "delete": { "categories": [ "WILDLIFE" ] } + }, + { + "id": "mon_wolf_mutant_huge", + "type": "MONSTER", + "name": { "str": "dire wolf" }, + "copy-from": "mon_wolf_mutant_huge", + "delete": { "categories": [ "WILDLIFE" ] } + }, + { + "id": "mon_deer_mutant_spider", + "type": "MONSTER", + "name": { "str": "spideer" }, + "copy-from": "mon_deer_mutant_spider", + "delete": { "categories": [ "WILDLIFE" ] } + }, + { + "id": "mon_deer_mutant_spider_fawn", + "type": "MONSTER", + "name": { "str": "spideer fawn" }, + "copy-from": "mon_deer_mutant_spider_fawn", + "delete": { "categories": [ "WILDLIFE" ] } + }, + { + "id": "mon_dog_mutant_mongrel", + "type": "MONSTER", + "name": { "str": "mongrel" }, + "copy-from": "mon_dog_mutant_mongrel", "delete": { "categories": [ "WILDLIFE" ] } } ] diff --git a/data/mods/Dark-Skies-Above/overrides/professions.json b/data/mods/Dark-Skies-Above/overrides/professions.json new file mode 100644 index 0000000000000..69bc3430511fa --- /dev/null +++ b/data/mods/Dark-Skies-Above/overrides/professions.json @@ -0,0 +1,247 @@ +[ + { + "type": "profession", + "id": "heroin_addict", + "copy-from": "heroin_addict", + "name": "Heroin Addict", + "description": "The last thing you remember was meeting God behind the local Foodplace. Then the skies opened up and fire rained forth. This doesn't feel like a fever dream." + }, + { + "type": "profession", + "id": "pillhead", + "copy-from": "pillhead", + "name": "Heroin Addict", + "description": "After an accident in your youth, you got addicted to the opiates treating your pain. With the pharmacies shut down and your dealers started growing crystals, satisfying those cravings just got a lot more difficult." + }, + { + "type": "profession", + "id": "naked", + "copy-from": "naked", + "name": "Naked and Afraid", + "description": "You were out filming a reality TV show, naked in the woods. The cast and crew fled when the aliens came, which is pretty bad timing for you. Looks like it's for real this time…" + }, + { + "type": "profession", + "id": "lumberjack", + "copy-from": "lumberjack", + "name": "Lumberjack", + "description": "You're a lumberjack, and you're okay. You felled trees before the world ended, but suspect those crystal ghouls aren't nearly as tough." + }, + { + "type": "profession", + "id": "dancer", + "copy-from": "dancer", + "name": "Ballroom Dancer", + "description": "Things got a little weird on your way to your weekly dance class. Aliens don't seem to know how to dance, but you're not about to let them step on your toes." + }, + { + "type": "profession", + "id": "skaboy", + "copy-from": "skaboy", + "name": { "male": "Rude Boy", "female": "Rude Girl" }, + "description": "Your ska band broke up after the drummer got vaporized. Now you're alone in the Cataclysm with some cigarettes and your mp3 player." + }, + { + "type": "profession", + "id": "imam", + "copy-from": "imam", + "name": { "male": "Imam", "female": "Mourchida" }, + "description": "You spent much of your time prior to the apocalypse at the local mosque, studying the words of the Prophet and the Quran and guiding your community in prayer. Back then they came from far and wide to listen to you; now they come to tear you apart." + }, + { + "type": "profession", + "id": "teacher", + "copy-from": "teacher", + "name": "Teacher", + "description": "You've been teaching kids all your life, experiencing the joy and aggravation of imparting knowledge to young minds. If aliens have any interest in education, they're not showing it." + }, + { + "type": "profession", + "id": "groundskeeper", + "copy-from": "groundskeeper", + "name": "Landscaper", + "description": "You used to mow lawns and trim hedges for the wealthy. Contract work was getting scarce even before the aliens came, but now you've got nothing left except your tools and expertise." + }, + { + "type": "profession", + "id": "homemaker", + "copy-from": "homemaker", + "name": "Nursing Assistant", + "description": "You went on providing in-home care for the elderly even as the whole world fell apart around you. You can only pray that you don't see your former clients among those crystal things…" + }, + { + "type": "profession", + "id": "cosplay", + "copy-from": "cosplay", + "name": "Otaku", + "description": "After many late nights with friends watching anime and eating snacks, you decided to make the trip to the premier anime convention in the Northeast. Now aliens are killing everyone, and even worse, the convention is cancelled! At least you were ready in case your costume tore." + }, + { + "type": "profession", + "id": "lawyer", + "copy-from": "lawyer", + "name": "Lawyer", + "description": "The jury were in the palm of your hand, but after the aliens started invading, you were forced to flee the courtroom in disgrace. Now nobody seems to care about your objections." + }, + { + "type": "profession", + "id": "pizzaboy", + "copy-from": "pizzaboy", + "name": { "male": "Pizza Delivery Boy", "female": "Pizza Delivery Girl" }, + "description": "You were delivering the last pizza of the night to the local cryogenics lab when hungry aliens attempted to make a meal out of you. Fleeing for safety, you find yourself with only your wits and some leftover pizza. And they didn't even leave a tip!" + }, + { + "type": "profession", + "id": "relief_volunteer", + "copy-from": "relief_volunteer", + "name": "Relief Volunteer", + "description": "You were a member of a non-profit organization dedicated to helping out where help was needed. When the storms picked up and the first bombs dropped, you were eager to lend a hand. But you had to cut your plans short when the aliens arrived: they seem less interested in handouts, and more interested in eating you." + }, + { + "type": "profession", + "id": "guru", + "copy-from": "guru", + "name": "Guru", + "description": "You spent many years traveling through the world, becoming wise and learned. Normally, you can answer any question, but even you are not quite sure what to do about the ravenous aliens." + }, + { + "type": "profession", + "id": "preacher", + "copy-from": "preacher", + "name": "Preacher", + "description": "You devoted your life to spreading the good word, always on the road, traveling from town to town. Now everything has gone to hell, you can't host your daily podcast, and the aliens don't seem particularly moved by your sermons." + }, + { + "type": "profession", + "id": "rollerderby", + "copy-from": "rollerderby", + "name": "Roller Derby Player", + "description": "You were hell on wheels. Now the rest of your team is dead, and you probably wouldn't have lived this long if not for your penchant for high-speed violence. Things are looking grim; how long can you race laps around the aliens before you get blocked for good?" + }, + { + "type": "profession", + "id": "game_master", + "copy-from": "game_master", + "name": "Game Master", + "description": "Trying to herd cats into meeting up every week has taught you something: it's usually better to cut your losses and trust your gut. For that reason, when you had two no-shows and the other two got eaten, you ditched. Maybe you can find some new players in the ruins of the world." + }, + { + "type": "profession", + "id": "frat", + "copy-from": "frat", + "name": { "male": "Frat Boy", "female": "Sorority Girl" }, + "description": "You were living the high life, spending your parents' money without a care in the world. At one of your usual crazy parties, a bomb landed right in the hot tub, but you still have a chance to use the last symbol of your luxurious life - your sports car - and get far away." + }, + { + "type": "profession", + "id": "labtech", + "copy-from": "labtech", + "name": "Lab Technician", + "description": "Thanks to years of study and hard work in the lab, you're familiar with the basics of scientific inquiry. Only one question remains: can you avoid getting experimented on in return?" + }, + { + "type": "profession", + "id": "paperboy", + "copy-from": "paperboy", + "name": { "male": "Paperboy", "female": "Papergirl" }, + "description": "You set out this morning to deliver the news of the apocalypse. The aliens don't seem to value the latest news, but at least your trusty bicycle is still in working order." + }, + { + "type": "profession", + "id": "labtech", + "copy-from": "labtech", + "name": "Lab Technician", + "description": "Thanks to years of study and hard work in the lab, you're familiar with the basics of scientific inquiry. Only one question remains: can you avoid getting experimented on in return?" + }, + { + "type": "profession", + "id": "national_guard", + "copy-from": "national_guard", + "name": "National Guard", + "description": "The government activated your National Guard unit to deal with the growing storms and the following bombings. Despite your best efforts, you were unable to form up before all communications ceased and you found yourself alone amongst the enemy." + }, + { + "type": "profession", + "id": "gym_teacher", + "copy-from": "gym_teacher", + "name": "Gym Teacher", + "description": "It was hard enough getting kids to run laps without having to worry about doing it in a warzone. Aliens won't even line up when you blow your whistle." + }, + { + "type": "profession", + "id": "major-general", + "copy-from": "major-general", + "name": "Major General", + "description": "You worked your way up through the ranks, from a no-name private, to a big shot Major General. Now however, it is years since you last fired a weapon in anger, and you've somehow ended up deep behind enemy lines." + }, + { + "type": "profession", + "id": "recruit", + "copy-from": "recruit", + "name": "Military Recruit", + "description": "Joining the military has been your dream for years. You finally got in, just in time for your training to get interrupted by some sort of national emergency. After a hot deployment, as far as you can tell you're one of the last active personnel in this hellhole." + }, + { + "type": "profession", + "id": "combat-mechanic", + "copy-from": "combat-mechanic", + "name": "Combat Mechanic", + "description": "You failed out of high school, and joined the army. You were soon hand picked for extra training in the mechanics trade, keeping the armour running. It's been years since you last touched a rifle, and now the sky is falling…" + }, + { + "type": "profession", + "id": "riot_police", + "copy-from": "riot_police", + "name": "Riot Control Officer", + "description": "You were keeping the peace at a local climate change protest when the bombs started dropping and aliens started appearing in the streets. It was only by luck that you manage to survive the crowd in one piece, and the worst is yet to come." + }, + { + "type": "profession", + "id": "trucker", + "copy-from": "trucker", + "name": "Trucker", + "description": "You once ruled the road in your big rig. When the bombs hit, you hopped in and drove it to safety. Now it's just you and your truck against the world." + }, + { + "type": "profession", + "id": "fencer", + "copy-from": "fencer", + "name": "Competitive Fencer", + "description": "Years of training prepared you for the competitive fencing circuit, but your latest tournament was cut short when aliens invaded the piste. The referee was blown away, so you're not sure if the rules are still in play." + }, + { + "type": "profession", + "id": "politician", + "copy-from": "politician", + "name": "Career Politician", + "description": "You've spent your life appealing to the people, persuading many and promising much throughout your time in office. Now that your voting base is dead or worse and hostile forces are in America's heartland, winning hearts and minds just got that much harder." + }, + { + "type": "profession", + "id": "hazmat_unit", + "copy-from": "hazmat_unit", + "name": "Hazmat Unit", + "description": "You were deployed to autopsy one of the aliens after it was put down. When their friends showed up in force, you knew this was out of your job description." + }, + { + "type": "profession", + "id": "mili_burner", + "copy-from": "mili_burner", + "name": "Military Flamethrower Operator", + "description": "In response to the outbreak, you were dispatched to contain alien invasive species through judicious use of fire. After getting separated from your squad, your priorities have shifted to basic survival." + }, + { + "type": "profession", + "id": "nco", + "copy-from": "nco", + "name": "Non Commissioned Officer", + "description": "You're a veteran of several peace keeping missions. You lead your squad as a sort of parental figure, offering helpful advice on how not to die. Now they've been blasted to pieces by the alien and you're on your own." + }, + { + "type": "profession", + "id": "specops", + "copy-from": "specops", + "name": "Special Operator", + "description": "You were the best of the best, the military's finest. That's why you're still alive, even after all your comrades fell to the aliens. As far as you can tell, you're one of the last active operators in this hellhole." + } +] diff --git a/data/mods/Dark-Skies-Above/overrides/region_settings.json b/data/mods/Dark-Skies-Above/overrides/region_settings.json index 35fff324dddea..fddeba421ac44 100644 --- a/data/mods/Dark-Skies-Above/overrides/region_settings.json +++ b/data/mods/Dark-Skies-Above/overrides/region_settings.json @@ -35,6 +35,7 @@ }, "road": { "extras": { + "mx_dsa_bombed_crater": 200, "mx_mayhem": 0, "mx_portal": 0, "mx_portal_in": 0, @@ -57,6 +58,7 @@ "build": { "chance": 2, "extras": { + "mx_dsa_bombed_crater": 200, "mx_house_spider": 0, "mx_house_wasp": 0, "mx_military": 1, diff --git a/data/mods/Dark-Skies-Above/overrides/scenarios.json b/data/mods/Dark-Skies-Above/overrides/scenarios.json index 423e9a3fa875a..eb1ae0331df40 100644 --- a/data/mods/Dark-Skies-Above/overrides/scenarios.json +++ b/data/mods/Dark-Skies-Above/overrides/scenarios.json @@ -5,7 +5,7 @@ "copy-from": "evacuee", "allowed_locs": [ "sloc_dks_shelter" ], "name": "Evacuee", - "description": "You have survived the initial wave of panic and managed to avoid being taken to the Designated Living Zones. You begin in the (relative) safety in one of the many government evac shelters.", + "description": "You have survived the initial wave of panic and managed to escape to (relative) safety in one of the many government evac shelters.", "blacklist_professions": true, "professions": [ "churl" ] }, diff --git a/data/mods/Dark-Skies-Above/snippets/fliers.json b/data/mods/Dark-Skies-Above/snippets/fliers.json index 8cc46f484f30f..60cb7c04c3fe0 100644 --- a/data/mods/Dark-Skies-Above/snippets/fliers.json +++ b/data/mods/Dark-Skies-Above/snippets/fliers.json @@ -39,7 +39,7 @@ }, { "id": "dks_flier_11", - "text": "This is a government-issued, air-dropped alert. \"EVACUTE IMMEDIATELY. Proceed to your closest FEMA operated community shelter to await police and military escort to safety.\"" + "text": "This is a government-issued, air-dropped alert. \"EVACUATE IMMEDIATELY. Proceed to your closest FEMA operated community shelter to await police and military escort to safety.\"" }, { "id": "dks_flier_12", @@ -59,7 +59,7 @@ }, { "id": "dks_flier_16", - "text": "This is a glossy advertisement from FEMA asking that people donate their canned goods in return for tax writeoffs. From the date on the flier, it doesn't look like the initative ever had time to get off the ground." + "text": "This is a glossy advertisement from FEMA asking that people donate their canned goods in return for tax write-offs. From the date on the flier, it doesn't look like the initiative ever had time to get off the ground." }, { "id": "dks_flier_17", diff --git a/data/mods/Dark-Skies-Above/snippets/graffiti.json b/data/mods/Dark-Skies-Above/snippets/graffiti.json index 421707be4f8d3..061c3095ea3f9 100644 --- a/data/mods/Dark-Skies-Above/snippets/graffiti.json +++ b/data/mods/Dark-Skies-Above/snippets/graffiti.json @@ -13,7 +13,7 @@ { "id": "dks_general_graffiti_7", "text": "MOM" }, { "id": "dks_general_graffiti_8", "text": "FUCK YOU" }, { "id": "dks_general_graffiti_9", "text": "This is a cartoon rendition of an alien." }, - { "id": "dks_general_graffiti_10", "text": "This is a crudely spraypainted tag adorned with skulls." }, + { "id": "dks_general_graffiti_10", "text": "This is a crudely spray-painted tag adorned with skulls." }, { "id": "dks_general_graffiti_11", "text": "I have a secure and loving relationship with your mom and you're going to need to come to terms with that.\n\nDo you want to talk about it? You know where to find me. Love you sweety." @@ -23,7 +23,7 @@ { "id": "dks_general_graffiti_14", "text": " fucked ." }, { "id": "dks_general_graffiti_15", - "text": "This is a spraypainted drawing of an angel with wings made of vines." + "text": "This is a spray-painted drawing of an angel with wings made of vines." }, { "id": "dks_general_graffiti_16", "text": "Mr. is a vampire!" }, { "id": "dks_general_graffiti_17", "text": "Their hiding the truth" }, @@ -39,10 +39,10 @@ "id": "dks_general_graffiti_23", "text": "And they walked upon His Earth, and there was a RECKONING, and only the worthy survived" }, - { "id": "dks_general_graffiti_24", "text": "This is a drawing of a zombie with a bullethole in its head." }, + { "id": "dks_general_graffiti_24", "text": "This is a drawing of a zombie with a bullet hole in its head." }, { "id": "dks_general_graffiti_25", "text": "This is a surprisingly artistic drawing of a penis." }, - { "id": "dks_general_graffiti_26", "text": "This is a simple spraypainted graphic of a forest made of bones." }, - { "id": "dks_general_graffiti_27", "text": "This is a drawing of an alien with a bullethole in its head." }, + { "id": "dks_general_graffiti_26", "text": "This is a simple spray-painted graphic of a forest made of bones." }, + { "id": "dks_general_graffiti_27", "text": "This is a drawing of an alien with a bullet hole in its head." }, { "id": "dks_general_graffiti_28", "text": "we can never go back" }, { "id": "dks_general_graffiti_29", "text": "dont by meth from " }, { "id": "dks_general_graffiti_30", "text": " you owe me fifty bucks" }, @@ -50,7 +50,7 @@ { "id": "dks_general_graffiti_32", "text": "eyes to the skies" }, { "id": "dks_general_graffiti_33", - "text": "This is a spraypainting of an anatomically unlikely woman wearing very little." + "text": "This is a spray-painting of an anatomically unlikely woman wearing very little." } ] }, diff --git a/data/mods/Dark-Skies-Above/snippets/newspaper.json b/data/mods/Dark-Skies-Above/snippets/newspaper.json index d99a7ac8c5992..5e3f767c6a6c9 100644 --- a/data/mods/Dark-Skies-Above/snippets/newspaper.json +++ b/data/mods/Dark-Skies-Above/snippets/newspaper.json @@ -142,7 +142,7 @@ }, { "id": "dks_months_old_news_8", - "text": "FOODPLACE PANTS FEUD. An ongoing legal battle between popular fast food megalith Foodplace and the non-profit Concerned Consumers of Foodpeople has ended with the determination that the Foodperson costume will remain a gender-neutral, nonrevealing unitard. \"Superheroes are meant to have exaggerated anatomy and revealing costumes,\" a spokesperson from CCF said in a press release. \"This is a sad day not just for fans of Foodplace, but for consumers of the fast-food superhero genre overall.\"" + "text": "FOODPLACE PANTS FEUD. An ongoing legal battle between popular fast food megalith Foodplace and the non-profit Concerned Consumers of Foodpeople has ended with the determination that the Foodperson costume will remain a gender-neutral, non-revealing unitard. \"Superheroes are meant to have exaggerated anatomy and revealing costumes,\" a spokesperson from CCF said in a press release. \"This is a sad day not just for fans of Foodplace, but for consumers of the fast-food superhero genre overall.\"" }, { "id": "dks_months_old_news_9", diff --git a/data/mods/Dark-Skies-Above/speech/neworder_speech.json b/data/mods/Dark-Skies-Above/speech/neworder_speech.json index 09bfd1ac33562..3e9d576765f43 100644 --- a/data/mods/Dark-Skies-Above/speech/neworder_speech.json +++ b/data/mods/Dark-Skies-Above/speech/neworder_speech.json @@ -1,28 +1,4 @@ [ - { - "type": "speech", - "speaker": [ "dks_neworder_scout", "dks_neworder_pyro", "dks_neworder_cop", "dks_neworder_knight" ], - "sound": "unintelligble radio chatter.", - "volume": 20 - }, - { - "type": "speech", - "speaker": [ "dks_neworder_scout", "dks_neworder_pyro", "dks_neworder_cop", "dks_neworder_knight" ], - "sound": "unintelligble radio chatter.", - "volume": 20 - }, - { - "type": "speech", - "speaker": [ "dks_neworder_scout", "dks_neworder_pyro", "dks_neworder_cop", "dks_neworder_knight" ], - "sound": "unintelligble radio chatter, followed by a response.", - "volume": 25 - }, - { - "type": "speech", - "speaker": [ "dks_neworder_scout", "dks_neworder_pyro", "dks_neworder_cop", "dks_neworder_knight" ], - "sound": "unintelligble radio chatter.", - "volume": 20 - }, { "type": "speech", "speaker": [ "mon_dks_emissary", "mon_dks_emissary_war", "mon_dks_emissary_flame" ], diff --git a/data/mods/DinoMod/NPC/NC_Red.json b/data/mods/DinoMod/NPC/NC_Red.json index 83cc25d57589c..79044d4231a0c 100644 --- a/data/mods/DinoMod/NPC/NC_Red.json +++ b/data/mods/DinoMod/NPC/NC_Red.json @@ -41,17 +41,17 @@ "topic": "TALK_RED_DINOSAURS", "condition": { "u_has_var": "asked_about_kill_dinos", "type": "dialogue", "context": "dino", "value": "yes" } }, - { - "text": "Would you like to join me on my travels?", - "topic": "TALK_SUGGEST_FOLLOW_RED", - "condition": { "not": "npc_following" } - }, { "text": "Let's trade items.", "topic": "TALK_OLD_GUARD_RED", "effect": "start_trade" }, { "text": "How can I help?", "topic": "TALK_MISSION_LIST", "condition": { "u_has_var": "asked_about_kill_dinos", "type": "dialogue", "context": "dino", "value": "yes" } }, + { + "text": "Mission update for you, Red.", + "topic": "TALK_MISSION_INQUIRE", + "condition": { "and": [ "has_assigned_mission" ] } + }, { "text": "Anyway. Take care, Red.", "topic": "TALK_DONE" } ] }, @@ -131,9 +131,10 @@ "update_mapgen": { "place_monster": [ { "group": "GROUP_DINOSAUR_ZOMBIE", "x": 12, "y": 12, "target": true } ] } }, "origins": [ "ORIGIN_SECONDARY" ], + "followup": "MISSION_OLD_GUARD_DINO_4", "end": { "opinion": { "trust": 1, "value": 1 }, - "effect": [ { "u_buy_monster": "mon_zachycephalosaurus", "count": 1, "name": "Betty" } ] + "effect": [ { "u_buy_monster": "mon_zachycephalosaurus_chain", "count": 1, "name": "Betty" } ] }, "dialogue": { "describe": "We need help…", @@ -142,7 +143,63 @@ "rejected": "Come back when you get a chance, we really need to start reclaiming the region.", "advice": "Maybe bring a rope to bring the cattle back or something for them to eat. They can be ornery cusses if you don't know how to treat them.", "inquire": "How is the search going?", - "success": "Oh. Oh no. We didn't think the dinosaurs could raise and we've killed so many of them. We might be in trouble here. Here's one now, I'll rip it apart!", + "success": "Oh. Oh no. We didn't think the dinosaurs could raise and we've killed so many of them. We might be in trouble here. Here's one now, I'll rip it apart! Wait, I can see in its dead eyes that it loves us and wants to help us. I'll just end up killing it, I'm barely holding it together here. You'd better take it.", + "success_lie": "What good does this do us?", + "failure": "It was a lost cause anyways…" + } + }, + { + "id": "MISSION_OLD_GUARD_DINO_4", + "type": "mission_definition", + "name": { "str": "Kill 50 dinos" }, + "description": "Kill 50 dinos", + "goal": "MGOAL_KILL_MONSTER_SPEC", + "monster_species": "DINOSAUR", + "monster_kill_goal": 50, + "difficulty": 9, + "value": 100000, + "origins": [ "ORIGIN_SECONDARY" ], + "end": { + "opinion": { "trust": 1, "value": 1 }, + "effect": [ { "u_buy_monster": "mon_zachycephalosaurus_chain", "count": 1, "name": "Stromer" } ] + }, + "dialogue": { + "describe": "We need help…", + "offer": "While we were out on a hunting party a couple of those big ugly ones came back and ate a whole mob of the dead I wanted to take out. We lost a lot of soldiers too while we were out too, good ones, and they died bad. Slow. I made sure of it, the traitors. These dinosaurs have gone too far. They're making us crazy. We need to rack up some serious numbers to catch up. We need some serious risk taking, I'm talking explosives and fire and drugs and rock and roll here. I don't care if it's living dinos or dead, they all need to be taken down.", + "accepted": "Kill all dinos.", + "rejected": "I'm right on the edge soldier. Whose side are you on here?", + "advice": "You can kill a lot of dinos without taking a lot of pills, but I don't see how.", + "inquire": "You hit your numbers yet?", + "success": "That's what I like to hear. We got our numbers too, and we chained down a couple too. I had to take out a few more soldiers who didn't make their numbers, so this is good. We're doing good.", + "success_lie": "What good does this do us?", + "failure": "It was a lost cause anyways…" + } + }, + { + "id": "MISSION_OLD_GUARD_DINO_5", + "type": "mission_definition", + "name": { "str": "Kill me a T Rex" }, + "description": "Kill me a T Rex", + "goal": "MGOAL_KILL_MONSTER", + "difficulty": 9, + "value": 100000, + "start": { + "assign_mission_target": { "om_terrain": "field", "reveal_radius": 5, "random": true, "search_range": 60 }, + "update_mapgen": { "place_monster": [ { "monster": "mon_tyrannosaurus", "x": 12, "y": 12, "target": true } ] } + }, + "origins": [ "ORIGIN_SECONDARY" ], + "end": { + "opinion": { "trust": 1, "value": 1 }, + "effect": [ "npc_die", { "u_buy_monster": "mon_zyrannosaurus_chain", "count": 1, "name": "Wilma" } ] + }, + "dialogue": { + "describe": "We need help…", + "offer": "I have a special mission I need to complete before these dinos finish killing me off, and I need you to bring me a something special. Go kill me a T Rex. We're going to be gods. It's all going to be worth it.", + "accepted": "MY LIFE FOR YOU.", + "rejected": "I'm right on the edge soldier. Whose side are you on here?", + "advice": "You're a dino killing machine like me. You know what to do.", + "inquire": "You got my body yet?", + "success": "All right, we've got the body here. Watch this, I'm going to climb into its dead mouth and when you see me again I'll be a god. *SWALLOW*", "success_lie": "What good does this do us?", "failure": "It was a lost cause anyways…" } diff --git a/data/mods/DinoMod/monstergroups/fungi.json b/data/mods/DinoMod/monstergroups/fungi.json index 569949fcec0f3..e4ea76ddb0158 100644 --- a/data/mods/DinoMod/monstergroups/fungi.json +++ b/data/mods/DinoMod/monstergroups/fungi.json @@ -1,24 +1,42 @@ [ - { - "type": "monstergroup", - "name": "GROUP_FUNGI", - "default": "mon_spore", - "monsters": [ - { "monster": "mon_zpinosaurus_fungus", "freq": 0, "cost_multiplier": 0 }, - { "monster": "mon_zyrannosaurus_fungus", "freq": 0, "cost_multiplier": 0 }, - { "monster": "mon_zankylosaurus_fungus", "freq": 0, "cost_multiplier": 0 }, - { "monster": "mon_zeinonychus_fungus", "freq": 0, "cost_multiplier": 0 } - ] - }, { "type": "monstergroup", "name": "GROUP_FUNGI_TOWER", "default": "mon_fungal_tendril", "monsters": [ - { "monster": "mon_zpinosaurus_fungus", "freq": 50, "cost_multiplier": 0 }, - { "monster": "mon_zyrannosaurus_fungus", "freq": 50, "cost_multiplier": 0 }, - { "monster": "mon_zankylosaurus_fungus", "freq": 250, "cost_multiplier": 0 }, - { "monster": "mon_zeinonychus_fungus", "freq": 75, "cost_multiplier": 0 } + { "monster": "mon_zilophosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zeratosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zallosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 144 }, + { "monster": "mon_zacrocanthosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 144 }, + { "monster": "mon_ziats_fungus", "freq": 1, "cost_multiplier": 0, "starts": 144 }, + { "monster": "mon_zalbertosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zyrannosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 144 }, + { "monster": "mon_zallimimus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zothronychus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zeinonychus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zutahraptor_fungus", "freq": 1, "cost_multiplier": 0, "starts": 144 }, + { "monster": "mon_zapatosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zrontosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_ziplodocus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zamarasaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zrachiosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zalamosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_ztegosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zyoplosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zankylosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zodosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zedmontonia_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zamptosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zaiasaura_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zarasaurolophus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zorythosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zedmontosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zachycephalosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zachyrhinosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zentaceratops_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zosmoceratops_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zorosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zriceratops_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 } ] }, { @@ -26,10 +44,39 @@ "name": "GROUP_FUNGI_FLOWERS", "default": "mon_fungal_blossom", "monsters": [ - { "monster": "mon_zpinosaurus_fungus", "freq": 50, "cost_multiplier": 0 }, - { "monster": "mon_zyrannosaurus_fungus", "freq": 50, "cost_multiplier": 0 }, - { "monster": "mon_zankylosaurus_fungus", "freq": 250, "cost_multiplier": 0 }, - { "monster": "mon_zeinonychus_fungus", "freq": 75, "cost_multiplier": 0 } + { "monster": "mon_zilophosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zeratosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zallosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 144 }, + { "monster": "mon_zacrocanthosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 144 }, + { "monster": "mon_ziats_fungus", "freq": 1, "cost_multiplier": 0, "starts": 144 }, + { "monster": "mon_zalbertosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zyrannosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 144 }, + { "monster": "mon_zallimimus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zothronychus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zeinonychus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zutahraptor_fungus", "freq": 1, "cost_multiplier": 0, "starts": 144 }, + { "monster": "mon_zapatosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zrontosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_ziplodocus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zamarasaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zrachiosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zalamosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_ztegosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zyoplosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zankylosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zodosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zedmontonia_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zamptosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zaiasaura_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zarasaurolophus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zorythosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zedmontosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zachycephalosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zachyrhinosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zentaceratops_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zosmoceratops_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zorosaurus_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 }, + { "monster": "mon_zriceratops_fungus", "freq": 1, "cost_multiplier": 0, "starts": 72 } ] } ] diff --git a/data/mods/DinoMod/monsters/fungus.json b/data/mods/DinoMod/monsters/fungus.json index 8bc86b455790e..fa1f15212e239 100644 --- a/data/mods/DinoMod/monsters/fungus.json +++ b/data/mods/DinoMod/monsters/fungus.json @@ -1,4 +1,40 @@ [ + { + "id": "mon_zilophosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Dilophosaurus zombie" }, + "description": "Once a predator dinosaur with sharp teeth and two prominent bony crests on its head, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh with a colorful frill of interwoven mushrooms.", + "copy-from": "mon_zilophosaurus", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ [ "FUNGUS", 200 ], { "type": "bite", "cooldown": 5 } ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM" ] + }, + { + "id": "mon_zeratosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal zombie dragon" }, + "description": "Once an enormous, scaly dinosaur studded with bony spikes with colorful horns, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered scales and spikes.", + "copy-from": "mon_zeratosaurus", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ [ "FUNGUS", 200 ], { "type": "bite", "cooldown": 5 } ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "HEARS", "BASHES", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM", "SWIMS" ] + }, { "id": "mon_zpinosaurus_fungus", "type": "MONSTER", @@ -17,6 +53,78 @@ "upgrades": { }, "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "BASHES", "DESTROYS", "NO_BREATHE", "FILTHY", "WARM", "SWIMS" ] }, + { + "id": "mon_zallosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Allosaurus zombie" }, + "description": "Once a large predatory bipedal dinosaur with a broad scaly back, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh.", + "copy-from": "mon_zallosaurus", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ [ "FUNGUS", 200 ], { "type": "bite", "cooldown": 5 } ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "BASHES", "NO_BREATHE", "FILTHY", "WARM" ] + }, + { + "id": "mon_zacrocanthosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Acrocanthosaurus zombie" }, + "description": "Once a huge predatory bipedal dinosaur, with a tall ridge running down the length of its back covered with heavy muscles and showing teeth curved and serrated like sawblades, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh.", + "copy-from": "mon_zacrocanthosaurus", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ [ "FUNGUS", 200 ], [ "GRAB", 10 ], [ "LUNGE", 20 ] ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "BASHES", "NO_BREATHE", "FILTHY", "WARM" ] + }, + { + "id": "mon_ziats_fungus", + "type": "MONSTER", + "name": { "str": "fungal Siats zombie" }, + "description": "Once a huge predatory bipedal dinosaur with long claws and strong arms for grappling, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh.", + "copy-from": "mon_ziats", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ [ "FUNGUS", 200 ], [ "BIO_OP_TAKEDOWN", 20 ], [ "RANGED_PULL", 20 ], [ "GRAB_DRAG", 10 ] ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "BASHES", "NO_BREATHE", "FILTHY", "WARM" ] + }, + { + "id": "mon_zalbertosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Albertosaurus zombie" }, + "description": "Once a huge predatory bipedal dinosaur with long claws and massive jaws, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh.", + "copy-from": "mon_zalbertosaurus", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ [ "FUNGUS", 200 ], { "type": "bite", "cooldown": 5 }, [ "GRAB", 7 ], [ "scratch", 20 ], [ "LUNGE", 5 ] ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "BASHES", "DESTROYS", "NO_BREATHE", "FILTHY", "WARM" ] + }, { "id": "mon_zyrannosaurus_fungus", "type": "MONSTER", @@ -36,11 +144,11 @@ "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "BASHES", "DESTROYS", "NO_BREATHE", "FILTHY", "WARM" ] }, { - "id": "mon_zankylosaurus_fungus", + "id": "mon_zallimimus_fungus", "type": "MONSTER", - "name": { "str": "fungal Ankylosaurus zombie" }, - "description": "Once something like a giant armadillo dinosaur hybrid with a spiked tail, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered flesh.", - "copy-from": "mon_zankylosaurus", + "name": { "str": "fungal Gallimimus zombie" }, + "description": "Once a featherd bipedal plant eating dinosaur, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered feathers.", + "copy-from": "mon_zallimimus", "default_faction": "fungus", "species": [ "FUNGUS" ], "diff": 2, @@ -49,15 +157,33 @@ "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, "vision_day": 5, "vision_night": 5, - "special_attacks": [ [ "FUNGUS", 200 ] ], + "special_attacks": [ [ "FUNGUS", 200 ], [ "scratch", 10 ], { "type": "bite", "cooldown": 5 } ], "upgrades": { }, - "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM", "BASHES" ] + "flags": [ "SEES", "SMELLS", "HEARS", "PET_MOUNTABLE", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM" ] + }, + { + "id": "mon_zothronychus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Nothronychus zombie" }, + "description": "Once a large feathered bipedal plant eating dinosaur with sharp curved claws, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered feathers.", + "copy-from": "mon_zothronychus", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ [ "FUNGUS", 200 ], [ "LONGSWIPE", 30 ] ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "HEARS", "KEENNOSE", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM", "BASHES" ] }, { "id": "mon_zeinonychus_fungus", "type": "MONSTER", "name": { "str": "fungal Deinonychus zombie" }, - "description": "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.", + "description": "Once a feathered meat eating dinosaur with a large, sickle-shaped talon on each foot, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered flesh.", "copy-from": "mon_zeinonychus", "default_faction": "fungus", "species": [ "FUNGUS" ], @@ -67,8 +193,293 @@ "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, "vision_day": 5, "vision_night": 5, - "special_attacks": [ [ "FUNGUS", 200 ], { "type": "bite", "cooldown": 5 } ], + "special_attacks": [ + [ "FUNGUS", 200 ], + { "type": "leap", "cooldown": 5, "max_range": 5, "allow_no_target": true }, + [ "scratch", 10 ], + { "type": "bite", "cooldown": 5 } + ], "upgrades": { }, "flags": [ "SEES", "SMELLS", "KEENNOSE", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM" ] + }, + { + "id": "mon_zutahraptor_fungus", + "type": "MONSTER", + "name": { "str": "fungal Utahraptor zombie" }, + "description": "Once a large feathered meat eating dinosaur with a sickle-shaped talon on each foot, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered flesh.", + "copy-from": "mon_zutahraptor", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ + [ "FUNGUS", 200 ], + { "type": "leap", "cooldown": 5, "max_range": 5, "allow_no_target": true }, + [ "scratch", 10 ], + { "type": "bite", "cooldown": 5 } + ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "KEENNOSE", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM" ] + }, + { + "id": "mon_zapatosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Apatosaurus zombie" }, + "description": "Once a massive, long-necked, four-legged plant eating dinosaur with a long, whip-like tail, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered flesh.", + "copy-from": "mon_zapatosaurus", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ + [ "FUNGUS", 200 ], + { "id": "slam", "cooldown": 10, "damage_max_instance": [ { "damage_type": "bash", "amount": 12 } ] }, + [ "SMASH", 30 ] + ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM", "BASHES" ] + }, + { + "id": "mon_zrontosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Brontosaurus zombie" }, + "description": "Once a massive, long-necked, four-legged plant eating dinosaur with a bulky torso and long, whip-like tail, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered flesh.", + "copy-from": "mon_zapatosaurus_fungus" + }, + { + "id": "mon_ziplodocus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Diplodocus zombie" }, + "description": "Once a huge, long-necked, four-legged plant eating dinosaur with a tiny head and long, whip-like tail, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered flesh.", + "copy-from": "mon_zapatosaurus_fungus" + }, + { + "id": "mon_zamarasaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Camarasaurus zombie" }, + "description": "Once a huge, long-necked, four-legged plant eating dinosaur with a long, whip-like tail, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered flesh.", + "copy-from": "mon_zapatosaurus_fungus" + }, + { + "id": "mon_zrachiosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Brachiosaurus zombie" }, + "description": "Once a gigantic, long-necked, four-legged plant eating dinosaur with longer forelegs and a long, whip-like tail, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered flesh.", + "copy-from": "mon_zapatosaurus_fungus", + "proportional": { "hp": 2, "speed": 0.65 } + }, + { + "id": "mon_zalamosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Alamosaurus zombie" }, + "description": "Once a gigantic, long-necked, four-legged plant eating dinosaur with longer forelegs and spiked tail protected by natural bone armor, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered flesh.", + "copy-from": "mon_zapatosaurus_fungus", + "proportional": { "hp": 1.5, "speed": 0.8, "armor_bash": 2, "armor_cut": 2 } + }, + { + "id": "mon_ztegosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Stegosaurus zombie" }, + "description": "Once a large four legged plant eating dinosaur with heavy bone plates jutting from its back and spiked tail, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh.", + "copy-from": "mon_ztegosaurus", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ + [ "FUNGUS", 200 ], + { "id": "slam", "cooldown": 10, "damage_max_instance": [ { "damage_type": "bash", "amount": 12 } ] }, + [ "SMASH", 30 ] + ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM", "BASHES" ] + }, + { + "id": "mon_zyoplosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Dyoplosaurus zombie" }, + "description": "Once something like a giant armadillo dinosaur hybrid with a spiked club tail, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh and bone.", + "copy-from": "mon_zankylosaurus_fungus", + "proportional": { "hp": 0.35, "speed": 1.2 } + }, + { + "id": "mon_zankylosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Ankylosaurus zombie" }, + "description": "Once something like a giant armadillo dinosaur hybrid with a spiked tail, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered flesh and bone.", + "copy-from": "mon_zankylosaurus", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ + [ "FUNGUS", 200 ], + [ "scratch", 10 ], + { "type": "bite", "cooldown": 5 }, + { "id": "slam", "cooldown": 10, "damage_max_instance": [ { "damage_type": "bash", "amount": 12 } ] }, + [ "SMASH", 30 ] + ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM", "BASHES" ] + }, + { + "id": "mon_zodosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Nodosaurus zombie" }, + "description": "Once something like a giant armadillo dinosaur hybrid, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh and bone.", + "copy-from": "mon_zankylosaurus_fungus", + "proportional": { "hp": 0.6 }, + "special_attacks": [ [ "FUNGUS", 200 ], [ "scratch", 10 ], { "type": "bite", "cooldown": 5 } ] + }, + { + "id": "mon_zedmontonia_fungus", + "type": "MONSTER", + "name": { "str": "fungal Edmontonia zombie" }, + "description": "Once something like a giant armadillo dinosaur hybrid with long bony spikes, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh and bone.", + "copy-from": "mon_zankylosaurus_fungus", + "melee_cut": 10, + "proportional": { "hp": 0.65 }, + "special_attacks": [ [ "FUNGUS", 200 ], [ "scratch", 10 ], { "type": "bite", "cooldown": 5 } ] + }, + { + "id": "mon_zamptosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Camptosaurus zombie" }, + "description": "Once a large feathered bipedal dinosaur with strong legs, broad shoulders and a pointed beak, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together an enormous shambling mass of mold-covered feathers.", + "copy-from": "mon_zamptosaurus", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ [ "FUNGUS", 200 ], [ "scratch", 10 ], { "type": "bite", "cooldown": 5 } ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM", "BASHES" ] + }, + { + "id": "mon_zaiasaura_fungus", + "type": "MONSTER", + "name": { "str": "fungal Maiasaura zombie" }, + "description": "Once a huge four legged plant eating dinosaur with hooves, a heavy tail, and a flat beak, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh and bone.", + "copy-from": "mon_zamptosaurus_fungus", + "proportional": { "hp": 3 } + }, + { + "id": "mon_zarasaurolophus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Parasaurolophus zombie" }, + "description": "Once a huge four legged plant eating dinosaur with a blunt head crest, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh and bone.", + "copy-from": "mon_zamptosaurus_fungus", + "proportional": { "hp": 4 } + }, + { + "id": "mon_zorythosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Corythosaurus zombie" }, + "description": "Once a huge four legged plant eating dinosaur with a short beak and a tall head crest, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh and bone.", + "copy-from": "mon_zamptosaurus_fungus", + "proportional": { "hp": 4 }, + "special_attacks": [ [ "FUNGUS", 200 ], [ "scratch", 10 ], { "type": "bite", "cooldown": 5 }, [ "SHRIEK", 5 ] ] + }, + { + "id": "mon_zedmontosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Edmontosaurus zombie" }, + "description": "Once a huge four legged plant eating dinosaur with a broad toothless beak, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh and bone.", + "copy-from": "mon_zamptosaurus_fungus", + "proportional": { "hp": 5 } + }, + { + "id": "mon_zachycephalosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Pachycephalosaurus zombie" }, + "description": "Once a feathered bipedal dinosaur with a hard-looking domed head, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered feathers.", + "copy-from": "mon_zachycephalosaurus", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ [ "FUNGUS", 200 ], [ "scratch", 10 ], { "type": "bite", "cooldown": 5 } ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM", "PET_MOUNTABLE" ] + }, + { + "id": "mon_zachyrhinosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Pachyrhinosaurus zombie" }, + "description": "Once a massive four legged plant eating dinosaur with a tall bony crest from which four long horns and a short nose horn emerge, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh.", + "copy-from": "mon_zriceratops_fungus", + "proportional": { "hp": 0.65 }, + "special_attacks": [ + [ "FUNGUS", 200 ], + { "id": "slam", "cooldown": 10, "damage_max_instance": [ { "damage_type": "bash", "amount": 12 } ] }, + [ "SMASH", 30 ] + ] + }, + { + "id": "mon_zentaceratops_fungus", + "type": "MONSTER", + "name": { "str": "fungal Pentaceraterror" }, + "description": "Once a massive four legged plant eating dinosaur with a tall bony crest from which four long horns and a short nose horn emerge, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh.", + "copy-from": "mon_zriceratops_fungus", + "proportional": { "armor_bash": 2 } + }, + { + "id": "mon_zosmoceratops_fungus", + "type": "MONSTER", + "name": { "str": "fungal Kosmoceratops zombie" }, + "description": "Once a massive four legged plant eating dinosaur with a tall bony crest from which fifteen horns and spikes emerge, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh.", + "copy-from": "mon_zriceratops_fungus", + "proportional": { "melee_dice": 2, "melee_cut": 0.65 } + }, + { + "id": "mon_zorosaurus_fungus", + "type": "MONSTER", + "name": { "str": "fungal Torosaurus zombie" }, + "description": "Once a massive four legged plant eating dinosaur with a tall bony crest from which two wicked looking horns emerge, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh.", + "copy-from": "mon_zriceratops_fungus", + "proportional": { "melee_dice": 2, "melee_cut": 0.65 }, + "special_attacks": [ [ "FUNGUS", 200 ], { "id": "impale" }, [ "STRETCH_ATTACK", 5 ] ] + }, + { + "id": "mon_zriceratops_fungus", + "type": "MONSTER", + "name": { "str": "fungal Triceraterror" }, + "description": "Once a massive four legged plant eating dinosaur with a bony head crest from which three wicked looking horns emerge, fungal tendrils now sprout from its mouth, eyes, and other orifices, holding together a shambling mass of mold-covered flesh.", + "copy-from": "mon_zriceratops", + "default_faction": "fungus", + "species": [ "FUNGUS" ], + "diff": 2, + "proportional": { "hp": 0.75, "speed": 0.65 }, + "color": "light_gray", + "relative": { "melee_skill": -1, "melee_dice": -1, "melee_dice_sides": 3, "armor_bash": 3 }, + "vision_day": 5, + "vision_night": 5, + "special_attacks": [ [ "FUNGUS", 200 ], { "id": "impale" } ], + "upgrades": { }, + "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM", "PET_MOUNTABLE", "BASHES" ] } ] diff --git a/data/mods/DinoMod/monsters/juvenile.json b/data/mods/DinoMod/monsters/juvenile.json index 326cc24fb279c..0dc549715a0e6 100644 --- a/data/mods/DinoMod/monsters/juvenile.json +++ b/data/mods/DinoMod/monsters/juvenile.json @@ -186,7 +186,7 @@ "id": "mon_apatosaurus_juvenile", "type": "MONSTER", "name": "brown and magenta four-legged juvenile", - "description": "A small four-legged plant-eating dinosaur juvenile with huge shiny eyes, it could be from a number of different species.", + "description": "A huge four-legged plant-eating juvenile sauropod with a long neck and tail, it could be from a number of different species.", "copy-from": "mon_gallimimus_juvenile", "default_faction": "herbivore_dino", "color": "brown_magenta", @@ -214,7 +214,7 @@ "id": "mon_brontosaurus_juvenile", "type": "MONSTER", "name": "brown and magenta four-legged juvenile", - "description": "A small four-legged plant-eating dinosaur juvenile with huge shiny eyes, it could be from a number of different species.", + "description": "A huge four-legged plant-eating juvenile sauropod with a long neck and tail, it could be from a number of different species.", "copy-from": "mon_gallimimus_juvenile", "default_faction": "herbivore_dino", "color": "brown_magenta", @@ -242,7 +242,7 @@ "id": "mon_diplodocus_juvenile", "type": "MONSTER", "name": "brown and magenta four-legged juvenile", - "description": "A small four-legged plant-eating dinosaur juvenile with huge shiny eyes, it could be from a number of different species.", + "description": "A huge four-legged plant-eating juvenile sauropod with a long neck and tail, it could be from a number of different species.", "copy-from": "mon_gallimimus_juvenile", "default_faction": "herbivore_dino", "color": "brown_magenta", @@ -270,7 +270,7 @@ "id": "mon_camarasaurus_juvenile", "type": "MONSTER", "name": "brown and magenta four-legged juvenile", - "description": "A small four-legged plant-eating dinosaur juvenile with huge shiny eyes, it could be from a number of different species.", + "description": "A huge four-legged plant-eating juvenile sauropod with a long neck and tail, it could be from a number of different species.", "copy-from": "mon_gallimimus_juvenile", "default_faction": "herbivore_dino", "color": "brown_magenta", @@ -298,7 +298,7 @@ "id": "mon_brachiosaurus_juvenile", "type": "MONSTER", "name": "brown and magenta four-legged juvenile", - "description": "A small four-legged plant-eating dinosaur juvenile with huge shiny eyes, it could be from a number of different species.", + "description": "A huge four-legged plant-eating juvenile sauropod with a long neck and tail, it could be from a number of different species.", "copy-from": "mon_gallimimus_juvenile", "default_faction": "herbivore_dino", "color": "brown_magenta", @@ -326,7 +326,7 @@ "id": "mon_alamosaurus_juvenile", "type": "MONSTER", "name": "brown and magenta four-legged juvenile", - "description": "A small four-legged plant-eating dinosaur juvenile with huge shiny eyes, it could be from a number of different species.", + "description": "A huge four-legged plant-eating juvenile sauropod with a long neck and tail, it could be from a number of different species.", "copy-from": "mon_gallimimus_juvenile", "default_faction": "herbivore_dino", "color": "brown_magenta", diff --git a/data/mods/DinoMod/monsters/zed-dinosaur.json b/data/mods/DinoMod/monsters/zed-dinosaur.json index 13d15964692de..5e21ea186bbe0 100644 --- a/data/mods/DinoMod/monsters/zed-dinosaur.json +++ b/data/mods/DinoMod/monsters/zed-dinosaur.json @@ -195,6 +195,16 @@ "harvest": "zed_dino_leather", "categories": [ "DINOSAUR", "CLASSIC" ] }, + { + "type": "MONSTER", + "id": "mon_zyrannosaurus_chain", + "//": "Special monster for Old Guard questline", + "name": { "str": "chained Tyrannosaurus zombie" }, + "copy-from": "mon_zyrannosaurus", + "description": "A massive dinosaur corpse with enormous teeth wrapped with heavy chains to allow some control.", + "upgrades": { }, + "aggression": 0 + }, { "type": "MONSTER", "id": "mon_zalbertosaurus", @@ -651,6 +661,17 @@ "harvest": "zed_dino_feather", "categories": [ "DINOSAUR", "CLASSIC" ] }, + { + "type": "MONSTER", + "id": "mon_zachycephalosaurus_chain", + "//": "Special monster for Old Guard questline", + "name": { "str": "chained Pachycephalosaurus zombie" }, + "copy-from": "mon_zachycephalosaurus", + "description": "A feathered dinosaur corpse with a domed head with heavy chains to allow some control.", + "upgrades": { }, + "aggression": 0, + "speed": 80 + }, { "type": "MONSTER", "id": "mon_zachyrhinosaurus", diff --git a/data/mods/Fuji_Mil_Prof/prof/itemgroups_prof.json b/data/mods/Fuji_Mil_Prof/prof/itemgroups_prof.json index 4e705e6bf5c0c..3c237c4f6d6eb 100644 --- a/data/mods/Fuji_Mil_Prof/prof/itemgroups_prof.json +++ b/data/mods/Fuji_Mil_Prof/prof/itemgroups_prof.json @@ -3,7 +3,7 @@ "type": "item_group", "subtype": "collection", "id": "holster_supp_MEU", - "entries": [ { "item": "m1911_MEU", "ammo-item": "45_acp", "charges": 7, "contents-item": [ "suppressor" ] } ] + "entries": [ { "item": "m1911", "variant": "m1911_MEU", "ammo-item": "45_acp", "charges": 7, "contents-item": [ "suppressor" ] } ] }, { "type": "item_group", diff --git a/data/mods/Generic_Guns/firearms/gg_firearms_migration.json b/data/mods/Generic_Guns/firearms/gg_firearms_migration.json index 76fa0bad19c1a..428c44560049f 100644 --- a/data/mods/Generic_Guns/firearms/gg_firearms_migration.json +++ b/data/mods/Generic_Guns/firearms/gg_firearms_migration.json @@ -86,7 +86,6 @@ "sig_40", "hptjcp", "m1911", - "m1911_MEU", "walther_ppq_45", "hptjhp", "tokarev", @@ -111,7 +110,6 @@ "walther_ppq_40", "tommygun", "usp_45", - "mk23", "fn57", "glock_17", "kpf9", @@ -201,18 +199,12 @@ }, { "id": [ - "h&k416a5", "m1918", "acr_300blk", "hk417_13", - "acr", - "m4a1", - "m16a3", "m16a4", - "m38dmr", - "m4_cqbr", + "nato_assault_rifle", "m231pfw", - "scar_l", "scar_h", "sig_mcx_rattler_sbr", "rm51_assault_rifle", @@ -238,7 +230,7 @@ "replace": "rifle_huge_hmg" }, { - "id": [ "m134", "m249", "m27iar", "m240", "m60", "rm614_lmg", "rm298" ], + "id": [ "m134", "m249", "m240", "m60", "rm614_lmg", "rm298" ], "type": "MIGRATION", "replace": "rifle_lmg" }, diff --git a/data/mods/Generic_Guns/firearms/pistol.json b/data/mods/Generic_Guns/firearms/pistol.json index 7a69b3b076ab1..fe459c467a140 100644 --- a/data/mods/Generic_Guns/firearms/pistol.json +++ b/data/mods/Generic_Guns/firearms/pistol.json @@ -15,7 +15,7 @@ "type": "GUN", "name": { "str": "machine pistol" }, "ammo": [ "ammo_pistol" ], - "description": "This pistol is a tiny machinegun you can stuff into a holster, with which you could dump its magazine at a blistering rate into any close range foes. Machine pistols mostly see use by vehicle crewmen or bodygaurds of VIPs. Due to its preposterous rate of fire it is difficult to control.", + "description": "This pistol is a tiny machinegun you can stuff into a holster, with which you could dump its magazine at a blistering rate into any close range foes. Machine pistols mostly see use by vehicle crewmen or bodyguards of VIPs. Due to its preposterous rate of fire it is difficult to control.", "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", diff --git a/data/mods/Generic_Guns/firearms/pistol_magnum.json b/data/mods/Generic_Guns/firearms/pistol_magnum.json index 58b5fb14f5f09..e9c3e018c6bd2 100644 --- a/data/mods/Generic_Guns/firearms/pistol_magnum.json +++ b/data/mods/Generic_Guns/firearms/pistol_magnum.json @@ -6,7 +6,7 @@ "name": { "str": "hand cannon" }, "ammo": [ "ammo_pistol", "ammo_pistol_magnum" ], "//": "We're just going to prtend that .357 and .44 magnum deagles will run .38's and .44 special just fine", - "description": "This large pistol is almost as heavy as a small carbine, and just about as powerful too. Chambered in hard hitting magnum calibers, it is suitable for hunting medium game, humans, or offsetting any of one's perceived deficiencies. Though tradtionally such magnums are revolvers, this one is a magazine fed semi-automatic.", + "description": "This large pistol is almost as heavy as a small carbine, and just about as powerful too. Chambered in hard hitting magnum calibers, it is suitable for hunting medium game, humans, or offsetting any of one's perceived deficiencies. Though traditionally such magnums are revolvers, this one is a magazine fed semi-automatic.", "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -34,7 +34,7 @@ "type": "GUN", "name": { "str": "handmade magnum carbine" }, "ammo": [ "ammo_pistol_magnum", "ammo_pistol" ], - "description": "A crudely constructed carbine, chambered for magnum pistol ammo and standard pistol ammo. It feeds from commerical magnum pistol magazines, and locks with a rudimentary lever action system. Its powerful cartridge and relative precision should serve well against zombies and medium game.", + "description": "A crudely constructed carbine, chambered for magnum pistol ammo and standard pistol ammo. It feeds from commercial magnum pistol magazines, and locks with a rudimentary lever action system. Its powerful cartridge and relative precision should serve well against zombies and medium game.", "weight": "2114 g", "volume": "2 L", "price": 10000, diff --git a/data/mods/Generic_Guns/firearms/rifle.json b/data/mods/Generic_Guns/firearms/rifle.json index 4870b21236a9d..80ef35b8a6516 100644 --- a/data/mods/Generic_Guns/firearms/rifle.json +++ b/data/mods/Generic_Guns/firearms/rifle.json @@ -59,7 +59,7 @@ "type": "GUN", "name": "sporter carbine", "ammo": [ "ammo_rifle" ], - "description": "Though often mislabeled an asssault rifle, this common, cheap magazine fed carbine isn't capable of automatic fire. While almost as effective as a proper rifle, the wider variety of components and varying levels of maintenance make these less reliable than their military brethren. These rifles are just as adequate for taking on anything smaller than large game, however.", + "description": "Though often mislabeled an assault rifle, this common, cheap magazine fed carbine isn't capable of automatic fire. While almost as effective as a proper rifle, the wider variety of components and varying levels of maintenance make these less reliable than their military brethren. These rifles are just as adequate for taking on anything smaller than large game, however.", "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", diff --git a/data/mods/Generic_Guns/firearms/rifle_huge.json b/data/mods/Generic_Guns/firearms/rifle_huge.json index b3773574befc9..51c386db8255f 100644 --- a/data/mods/Generic_Guns/firearms/rifle_huge.json +++ b/data/mods/Generic_Guns/firearms/rifle_huge.json @@ -23,7 +23,7 @@ "type": "GUN", "name": { "str": "elephant rifle" }, "ammo": [ "ammo_rifle_huge" ], - "description": "Elegantly engraved, with deep glossy blued steel and figured wood, this break-action double rifle is almost too nice to shoot. Your shoulder might beg you not to; the chambers are almost wide enough for two fingers and the recoil is monstruous. You could probably kill anything with this, especially if you were to fire both barrels at once.", + "description": "Elegantly engraved, with deep glossy blued steel and figured wood, this break-action double rifle is almost too nice to shoot. Your shoulder might beg you not to; the chambers are almost wide enough for two fingers and the recoil is monstrous. You could probably kill anything with this, especially if you were to fire both barrels at once.", "modes": [ [ "DEFAULT", "single", 1 ], [ "DOUBLE", "double", 2 ] ], "extend": { "flags": [ "RELOAD_ONE" ] }, "clip_size": 2, diff --git a/data/mods/Generic_Guns/magazines/rifle.json b/data/mods/Generic_Guns/magazines/rifle.json index ac8667a01e991..c2e1553b397ad 100644 --- a/data/mods/Generic_Guns/magazines/rifle.json +++ b/data/mods/Generic_Guns/magazines/rifle.json @@ -18,7 +18,7 @@ "type": "MAGAZINE", "material": [ "aluminum" ], "name": "standard rifle magazine", - "description": "A 30 round standard capacity magazine for service rifles, cheaper than the cost of a meal due to its adoption by nearly every other common rifle. They're made of aluminum and were originally meant to be dispsoable, so they're not the most durable; don't let them get damaged.", + "description": "A 30 round standard capacity magazine for service rifles, cheaper than the cost of a meal due to its adoption by nearly every other common rifle. They're made of aluminum and were originally meant to be disposable, so they're not the most durable; don't let them get damaged.", "volume": "500 ml", "ammo_type": [ "ammo_rifle" ], "pocket_data": [ { "pocket_type": "MAGAZINE", "ammo_restriction": { "ammo_rifle": 30 }, "rigid": true } ] diff --git a/data/mods/Generic_Guns/recipes/recipes_grenade_propelled.json b/data/mods/Generic_Guns/recipes/recipes_grenade_propelled.json index 8d0dbedf2d1a9..5099e347436ba 100644 --- a/data/mods/Generic_Guns/recipes/recipes_grenade_propelled.json +++ b/data/mods/Generic_Guns/recipes/recipes_grenade_propelled.json @@ -14,6 +14,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 12 ], [ "req_grenade", -1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ @@ -37,6 +38,7 @@ "charges": 1, "reversible": true, "using": [ [ "bullet_forming", 2 ], [ "ammo_bullet", 12 ], [ "req_grenade", -1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ], [ [ "swage", -1 ] ] ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ diff --git a/data/mods/Generic_Guns/recipes/recipes_pistol.json b/data/mods/Generic_Guns/recipes/recipes_pistol.json index 5912d6bf07e73..ba751161e446f 100644 --- a/data/mods/Generic_Guns/recipes/recipes_pistol.json +++ b/data/mods/Generic_Guns/recipes/recipes_pistol.json @@ -14,6 +14,7 @@ "charges": 1, "reversible": true, "using": [ [ "req_pistol_tiny", 1 ], [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 2 ], [ "gunpowder_pistol", 2 ], [ "gunpowder_shotgun", 2 ] ] ] }, { @@ -29,6 +30,7 @@ "activity_level": "fake", "copy-from": "reloaded_tiny_pistol_jhp", "using": [ [ "req_pistol_tiny", 1 ], [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 2 ], [ "gunpowder_pistol", 2 ] ], [ [ "copper", 1 ] ] ] }, { @@ -53,6 +55,7 @@ "charges": 1, "reversible": true, "using": [ [ "req_pistol", 1 ], [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 4 ], [ "gunpowder_pistol", 4 ], [ "gunpowder_shotgun", 4 ] ] ] }, { @@ -68,6 +71,7 @@ "activity_level": "fake", "copy-from": "reloaded_pistol_jhp", "using": [ [ "req_pistol", 1 ], [ "bullet_forming", 2 ], [ "ammo_bullet", 2 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 4 ], [ "gunpowder_pistol", 4 ], [ "gunpowder_shotgun", 4 ] ], [ [ "copper", 1 ], [ "pistol_tiny_casing", 1 ] ] @@ -95,6 +99,7 @@ "charges": 1, "reversible": true, "using": [ [ "req_pistol_magnum", 1 ], [ "bullet_forming", 5 ], [ "ammo_bullet", 5 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 6 ], [ "gunpowder_rifle", 6 ], [ "gunpowder_magnum_pistol", 6 ] ] ] }, { @@ -110,6 +115,7 @@ "activity_level": "fake", "copy-from": "reloaded_pistol_magnum_jhp", "using": [ [ "req_pistol_magnum", 1 ], [ "bullet_forming", 5 ], [ "ammo_bullet", 4 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 6 ], [ "gunpowder_rifle", 6 ], [ "gunpowder_magnum_pistol", 6 ] ], [ [ "copper", 2 ], [ "pistol_magnum_casing", 1 ] ] diff --git a/data/mods/Generic_Guns/recipes/recipes_rifle.json b/data/mods/Generic_Guns/recipes/recipes_rifle.json index 0a76716d1647a..9cb81f6eee452 100644 --- a/data/mods/Generic_Guns/recipes/recipes_rifle.json +++ b/data/mods/Generic_Guns/recipes/recipes_rifle.json @@ -14,6 +14,7 @@ "charges": 1, "reversible": true, "using": [ [ "ammo_bullet", 10 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ [ [ "chem_black_powder", 5 ] ], [ [ "paper", 1 ] ] ] }, @@ -32,6 +33,7 @@ "charges": 1, "reversible": true, "using": [ [ "ammo_bullet", 10 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ [ [ "chem_black_powder", 5 ] ], [ [ "paper", 1 ] ] ] }, @@ -50,6 +52,7 @@ "charges": 1, "reversible": true, "using": [ [ "req_rifle", 1 ], [ "bullet_forming", 9 ], [ "ammo_bullet", 6 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 12 ], [ "gunpowder_magnum_pistol", 12 ], [ "gunpowder_rifle", 12 ], [ "gunpowder_large_rifle", 12 ] ] ] @@ -94,6 +97,7 @@ "charges": 1, "reversible": true, "using": [ [ "req_rifle_huge", 1 ], [ "bullet_forming", 18 ], [ "ammo_bullet", 12 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 30 ], [ "gunpowder_large_rifle", 30 ] ] ] }, { diff --git a/data/mods/Generic_Guns/recipes/recipes_shot.json b/data/mods/Generic_Guns/recipes/recipes_shot.json index a2994f101900e..f5c4724219eaf 100644 --- a/data/mods/Generic_Guns/recipes/recipes_shot.json +++ b/data/mods/Generic_Guns/recipes/recipes_shot.json @@ -14,6 +14,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 10 ], [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "components": [ [ [ "gunpowder", 6 ], [ "gunpowder_pistol", 6 ], [ "gunpowder_shotgun", 6 ] ] ] }, @@ -32,6 +33,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 10 ], [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press", -1 ] ] ], "components": [ [ [ "chem_black_powder", 6 ] ] ] }, @@ -51,6 +53,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 10 ], [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "tools": [ [ [ "press_dowel", -1 ] ] ] }, { @@ -69,6 +72,7 @@ "tools": [ [ [ "press", -1 ] ] ], "book_learn": [ [ "recipe_bullets", 1 ], [ "manual_shotgun", 1 ] ], "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 10 ], [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 3 ], [ "gunpowder_pistol", 3 ], [ "gunpowder_shotgun", 3 ] ] ] }, { @@ -85,6 +89,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 10 ], [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 3 ] ] ] }, { @@ -103,6 +108,7 @@ "reversible": true, "book_learn": [ [ "survival_book", 1 ], [ "mag_survival", 1 ] ], "tools": [ [ [ "press_dowel", -1 ] ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 3 ] ] ] }, { @@ -121,6 +127,7 @@ "tools": [ [ [ "press", -1 ] ] ], "book_learn": [ [ "recipe_bullets", 3 ], [ "manual_shotgun", 3 ] ], "using": [ [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 3 ], [ "gunpowder_pistol", 3 ], [ "gunpowder_shotgun", 3 ] ], [ [ "magnesium", 5 ] ] ] }, { @@ -139,6 +146,7 @@ "tools": [ [ [ "press", -1 ] ] ], "book_learn": [ [ "recipe_bullets", 3 ], [ "manual_shotgun", 3 ] ], "using": [ [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 3 ] ], [ [ "magnesium", 5 ] ] ] }, { @@ -156,6 +164,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 10 ], [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "book_learn": [ [ "survival_book", 1 ], [ "mag_survival", 1 ] ], "tools": [ [ [ "press_dowel", -1 ] ] ] }, @@ -175,6 +184,7 @@ "difficulty": 3, "book_learn": [ [ "recipe_bullets", 3 ], [ "manual_shotgun", 3 ] ], "using": [ [ "bullet_forming", 1 ], [ "ammo_bullet", 20 ], [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 6 ], [ "gunpowder_pistol", 6 ], [ "gunpowder_shotgun", 6 ] ] ] }, { @@ -193,6 +203,7 @@ "difficulty": 3, "book_learn": [ [ "recipe_bullets", 3 ], [ "manual_shotgun", 3 ] ], "using": [ [ "bullet_forming", 1 ], [ "ammo_bullet", 20 ], [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 6 ] ] ] }, { @@ -210,6 +221,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 10 ], [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "book_learn": [ [ "survival_book", 1 ], [ "mag_survival", 1 ] ], "tools": [ [ [ "press_dowel", -1 ] ] ] }, @@ -229,6 +241,7 @@ "tools": [ [ [ "press", -1 ] ] ], "book_learn": [ [ "recipe_bullets", 4 ], [ "manual_shotgun", 4 ] ], "using": [ [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 6 ], [ "gunpowder_pistol", 6 ], [ "gunpowder_shotgun", 6 ] ], [ [ "combatnail", 10 ] ] ] }, { @@ -247,6 +260,7 @@ "tools": [ [ [ "press", -1 ] ] ], "book_learn": [ [ "recipe_bullets", 4 ], [ "manual_shotgun", 4 ] ], "using": [ [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 6 ] ], [ [ "combatnail", 10 ] ] ] }, { @@ -264,6 +278,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 10 ], [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "book_learn": [ [ "survival_book", 1 ], [ "mag_survival", 1 ] ], "tools": [ [ [ "press_dowel", -1 ] ] ] }, @@ -282,6 +297,7 @@ "reversible": true, "tools": [ [ [ "press", -1 ] ] ], "using": [ [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "gunpowder", 6 ], [ "gunpowder_pistol", 6 ], [ "gunpowder_shotgun", 6 ] ], [ @@ -314,6 +330,7 @@ "reversible": true, "tools": [ [ [ "press", -1 ] ] ], "using": [ [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "components": [ [ [ "chem_black_powder", 6 ] ], [ @@ -346,6 +363,7 @@ "charges": 1, "reversible": true, "using": [ [ "shot_forming", 1 ], [ "ammo_bullet", 10 ], [ "req_shot", 1 ] ], + "proficiencies": [ { "proficiency": "prof_handloading" } ], "book_learn": [ [ "survival_book", 1 ], [ "mag_survival", 1 ] ], "tools": [ [ [ "press_dowel", -1 ] ] ] } diff --git a/data/mods/Graphical_Overmap/go_overmap_terrain_hardcoded.json b/data/mods/Graphical_Overmap/go_overmap_terrain_hardcoded.json index bfad7b7e1e650..b191a9c5b5643 100644 --- a/data/mods/Graphical_Overmap/go_overmap_terrain_hardcoded.json +++ b/data/mods/Graphical_Overmap/go_overmap_terrain_hardcoded.json @@ -73,39 +73,51 @@ }, { "type": "overmap_terrain", - "id": "mine_shaft", - "copy-from": "mine_shaft", + "id": "mine_entrance_roof", + "copy-from": "mine_entrance_roof", "sym": "\u00D3" }, { "type": "overmap_terrain", - "id": "mine", - "copy-from": "mine", + "id": "mine_shaft_middle", + "copy-from": "mine_shaft_middle", "sym": "\u00D3" }, { "type": "overmap_terrain", - "id": "mine_down", - "copy-from": "mine_down", + "id": "mine_shaft_lower", + "copy-from": "mine_shaft_lower", "sym": "\u00D3" }, { "type": "overmap_terrain", - "id": "mine_finale", - "copy-from": "mine_finale", + "id": "mine_shaft_middle", + "copy-from": "mine_shaft_middle", "sym": "\u00D3" }, { "type": "overmap_terrain", - "id": "spiral_hub", - "copy-from": "spiral_hub", - "sym": "\u00EA" + "id": "mine_shaft_lower", + "copy-from": "mine_shaft_lower", + "sym": "\u00D3" }, { "type": "overmap_terrain", - "id": "spiral", - "copy-from": "spiral", - "sym": "\u00EA" + "id": "mine", + "copy-from": "mine", + "sym": "\u00D3" + }, + { + "type": "overmap_terrain", + "id": "mine_down", + "copy-from": "mine_down", + "sym": "\u00D3" + }, + { + "type": "overmap_terrain", + "id": "mine_finale", + "copy-from": "mine_finale", + "sym": "\u00D3" }, { "type": "overmap_terrain", diff --git a/data/mods/MMA/item_groups.json b/data/mods/MMA/item_groups.json index c48fb030c40ac..aa9d2086c1cbc 100644 --- a/data/mods/MMA/item_groups.json +++ b/data/mods/MMA/item_groups.json @@ -20,6 +20,11 @@ [ "manual_mma_hylian", 1 ] ] }, + { + "id": "museum_misc", + "type": "item_group", + "items": [ [ "ki_strike_scroll", 3 ] ] + }, { "type": "item_group", "id": "book_military", diff --git a/data/mods/MMA/martial.json b/data/mods/MMA/martial.json index edfcfc14ff1cd..e6dfff170ef7b 100644 --- a/data/mods/MMA/martial.json +++ b/data/mods/MMA/martial.json @@ -88,5 +88,29 @@ "name": { "str_sp": "Reaping Talons" }, "description": "This book contains the teaching of the Tiger Claw discipline.", "martial_art": "style_mma_tiger_claw" + }, + { + "abstract": "scroll_martial_base", + "type": "TOOL", + "name": { "str": "scroll abstract" }, + "weight": "415 g", + "volume": "250 ml", + "material": [ "paper" ], + "symbol": "!", + "looks_like": "manual_dragon", + "color": "white", + "flags": [ "NO_SALVAGE" ] + }, + { + "id": "ki_strike_scroll", + "copy-from": "scroll_martial_base", + "type": "TOOL", + "name": { "str": "dragon hand scroll" }, + "description": "Focus your ki into magical attacks.", + "price_postapoc": 12000, + "use_action": { "type": "countdown", "name": "Unroll", "message": "You unroll the dragon scroll…" }, + "countdown_interval": 1, + "countdown_destroy": true, + "countdown_action": { "type": "cast_spell", "spell_id": "learn_ki_strike", "no_fail": true, "level": 0 } } ] diff --git a/data/mods/MMA/spells.json b/data/mods/MMA/spells.json new file mode 100644 index 0000000000000..11045438411d7 --- /dev/null +++ b/data/mods/MMA/spells.json @@ -0,0 +1,16 @@ +[ + { + "type": "SPELL", + "id": "learn_ki_strike", + "name": { "str": "Ki Strike Meditations" }, + "effect": "mutate", + "shape": "blast", + "effect_str": "KI_STRIKE", + "description": "This grants a specific mutation.", + "message": "You study the meditations until you could do them in your sleep…", + "min_damage": 10000, + "max_damage": 10000, + "flags": [ "SILENT", "MUTATE_TRAIT" ], + "valid_targets": [ "self", "ally" ] + } +] diff --git a/data/mods/Magiclysm/Spells/animist.json b/data/mods/Magiclysm/Spells/animist.json index d778743be6535..1d864efb1aa0d 100644 --- a/data/mods/Magiclysm/Spells/animist.json +++ b/data/mods/Magiclysm/Spells/animist.json @@ -221,6 +221,27 @@ "max_range": 30, "range_increment": 1.0 }, + { + "id": "banishment_lesser", + "type": "SPELL", + "name": "Lesser Banishment", + "description": "Banish a monster to the lesser-known nether dimension. If a monster is more powerful than you can handle, the spell drains your life force to make up the difference.", + "valid_targets": [ "hostile" ], + "flags": [ "SOMATIC", "VERBAL" ], + "effect": "banishment", + "shape": "blast", + "min_damage": 40, + "damage_increment": 10, + "max_damage": 290, + "min_range": 6, + "max_range": 6, + "base_energy_cost": 600, + "spell_class": "ANIMIST", + "difficulty": 9, + "max_level": 25, + "base_casting_time": 100, + "energy_source": "MANA" + }, { "id": "summon_wisps", "type": "SPELL", diff --git a/data/mods/Magiclysm/Spells/attunements/Blood_Mage.json b/data/mods/Magiclysm/Spells/attunements/Blood_Mage.json index 778b5d1c0f1e9..e075473efd142 100644 --- a/data/mods/Magiclysm/Spells/attunements/Blood_Mage.json +++ b/data/mods/Magiclysm/Spells/attunements/Blood_Mage.json @@ -13,11 +13,14 @@ "max_damage": 640, "min_range": 6, "max_range": 6, + "min_aoe": 0, + "max_aoe": 2, + "aoe_increment": 0.04, "base_energy_cost": 400, "spell_class": "BLOOD_MAGE", - "difficulty": 9, + "difficulty": 6, "max_level": 35, - "base_casting_time": 500, + "base_casting_time": 100, "energy_source": "MANA" }, { diff --git a/data/mods/Magiclysm/Spells/attunements/Golemancer.json b/data/mods/Magiclysm/Spells/attunements/Golemancer.json index 5656a3651d3b9..eda42f5345646 100644 --- a/data/mods/Magiclysm/Spells/attunements/Golemancer.json +++ b/data/mods/Magiclysm/Spells/attunements/Golemancer.json @@ -1,4 +1,56 @@ [ + { + "id": "summon_golem", + "type": "requirement", + "components": [ [ [ "golemcore", 1 ] ] ] + }, + { + "id": "summon_golem_clay", + "type": "SPELL", + "spell_class": "GOLEMANCER", + "name": "Summon Claygolem", + "description": "Use a golem core as a focus to shape clay into a roughly humanoid shape and animate it for some time.", + "valid_targets": [ "ground" ], + "effect": "summon", + "effect_str": "mon_claygolem", + "shape": "blast", + "base_casting_time": 3500, + "casting_time_increment": -100, + "final_casting_time": 2000, + "base_energy_cost": 500, + "energy_source": "MANA", + "difficulty": 6, + "max_level": 35, + "min_damage": 1, + "max_damage": 1, + "min_duration": 30000, + "max_duration": 90000, + "duration_increment": 1715, + "min_range": 3, + "max_range": 3, + "components": "summon_golem", + "extra_effects": [ { "id": "summon_golem_focus" } ], + "flags": [ "SOMATIC", "VERBAL", "CONCENTRATE" ] + }, + { + "id": "summon_golem_focus", + "type": "SPELL", + "spell_class": "NONE", + "name": "Give back core", + "description": "Gives back a golem's core. You should not see this spell", + "valid_targets": [ "self" ], + "effect": "spawn_item", + "effect_str": "golemcore", + "shape": "blast", + "base_casting_time": 100, + "base_energy_cost": 1000, + "energy_source": "MANA", + "difficulty": 1, + "min_damage": 1, + "max_damage": 1, + "duration_increment": 1, + "flags": [ "PERMANENT", "NO_LEGS", "CONCENTRATE" ] + }, { "type": "SPELL", "id": "golem_push", diff --git a/data/mods/Magiclysm/Spells/debug.json b/data/mods/Magiclysm/Spells/debug.json index a0ac89ceecd15..c99193044fa48 100644 --- a/data/mods/Magiclysm/Spells/debug.json +++ b/data/mods/Magiclysm/Spells/debug.json @@ -112,21 +112,6 @@ "base_energy_cost": 100, "energy_source": "STAMINA" }, - { - "id": "debug_fatigue", - "type": "SPELL", - "name": "Debug Fatigue Spell", - "description": "Uses a little fatigue", - "message": "Debug spell %s cast.", - "valid_targets": [ "self" ], - "effect": "none", - "shape": "blast", - "min_range": 1, - "max_range": 1, - "base_casting_time": 100, - "base_energy_cost": 100, - "energy_source": "FATIGUE" - }, { "id": "debug_polymorph", "type": "SPELL", diff --git a/data/mods/Magiclysm/Spells/druid.json b/data/mods/Magiclysm/Spells/druid.json index 0a01dbd5d66f7..dbe29be9b020b 100644 --- a/data/mods/Magiclysm/Spells/druid.json +++ b/data/mods/Magiclysm/Spells/druid.json @@ -298,6 +298,8 @@ "difficulty": 5, "base_casting_time": 400, "base_energy_cost": 35, + "final_energy_cost": 20, + "energy_increment": -1.5, "max_level": 10, "min_damage": -4, "max_damage": -12, @@ -396,5 +398,28 @@ "shape": "blast", "effect": "summon", "effect_str": "mon_wolf" + }, + { + "id": "druid_growth", + "type": "SPELL", + "spell_class": "DRUID", + "name": "Seed of Growth", + "description": "Offer some of your life in a ritual to get tokens of growth in return.", + "valid_targets": [ "self" ], + "effect": "spawn_item", + "effect_str": "druid_fertilizer", + "shape": "blast", + "base_casting_time": 360000, + "final_casting_time": 90000, + "casting_time_increment": -15000, + "base_energy_cost": 5, + "energy_source": "HP", + "difficulty": 4, + "max_level": 20, + "min_damage": 5, + "max_damage": 80, + "damage_increment": 4, + "//": "The spell can only be permanent without being at maximum level because of how items with charges work", + "flags": [ "SOMATIC", "NO_LEGS", "PERMANENT" ] } ] diff --git a/data/mods/Magiclysm/Spells/magus.json b/data/mods/Magiclysm/Spells/magus.json index 627c45e8df2c5..fb33365d86a29 100644 --- a/data/mods/Magiclysm/Spells/magus.json +++ b/data/mods/Magiclysm/Spells/magus.json @@ -339,6 +339,28 @@ "effect": "attack", "effect_str": "foxs_cunning" }, + { + "id": "magus_force_jar", + "type": "SPELL", + "name": { "str": "Jar of Force" }, + "description": "Summon a jar of force in which you can store liquids.", + "effect": "spawn_item", + "effect_str": "jar_3l_force", + "shape": "blast", + "valid_targets": [ "self" ], + "flags": [ "SOMATIC", "CONCENTRATE", "VERBAL" ], + "max_level": 15, + "min_damage": 1, + "max_damage": 1, + "min_duration": 8640000, + "max_duration": 129600000, + "duration_increment": 8640000, + "spell_class": "MAGUS", + "base_casting_time": 400, + "base_energy_cost": 350, + "energy_source": "MANA", + "difficulty": 3 + }, { "id": "magus_summon_impact_sling", "type": "SPELL", diff --git a/data/mods/Magiclysm/effects/effects.json b/data/mods/Magiclysm/effects/effects.json index 063b384f2b8c2..74e0760460e89 100644 --- a/data/mods/Magiclysm/effects/effects.json +++ b/data/mods/Magiclysm/effects/effects.json @@ -27,7 +27,7 @@ "apply_message": "Your sight adjusts to the darkness.", "remove_message": "The darkness loses its shape.", "rating": "good", - "flags": [ "EFFECT_NIGHT_VISION" ] + "flags": [ "NIGHT_VISION" ] }, { "type": "effect_type", @@ -48,7 +48,7 @@ "desc": [ "Nothing can see you." ], "apply_message": "You fade away.", "remove_message": "You can see your hands again.", - "flags": [ "EFFECT_INVISIBLE" ] + "flags": [ "INVISIBLE" ] }, { "type": "effect_type", @@ -180,14 +180,14 @@ "remove_message": "Your skin stops tingling, your life is empty and meaningless again.", "rating": "good", "flags": [ - "EFFECT_ELECTRIC_IMMUNE", - "EFFECT_BIO_IMMUNE", - "EFFECT_BASH_IMMUNE", - "EFFECT_CUT_IMMUNE", - "EFFECT_ACID_IMMUNE", - "EFFECT_STAB_IMMUNE", - "EFFECT_HEAT_IMMUNE", - "EFFECT_COLD_IMMUNE" + "ELECTRIC_IMMUNE", + "BIO_IMMUNE", + "BASH_IMMUNE", + "CUT_IMMUNE", + "ACID_IMMUNE", + "STAB_IMMUNE", + "HEAT_IMMUNE", + "COLD_IMMUNE" ] }, { @@ -198,7 +198,7 @@ "apply_message": "Your body feels light as a feather.", "remove_message": "The earth pulls you down hard.", "rating": "good", - "flags": [ "EFFECT_FEATHER_FALL" ] + "flags": [ "FEATHER_FALL" ] }, { "type": "effect_type", diff --git a/data/mods/Magiclysm/itemgroups/itemgroups.json b/data/mods/Magiclysm/itemgroups/itemgroups.json index c8e672809f894..d6eea28c00f7c 100644 --- a/data/mods/Magiclysm/itemgroups/itemgroups.json +++ b/data/mods/Magiclysm/itemgroups/itemgroups.json @@ -32,7 +32,8 @@ { "group": "spellbook_loot_1", "prob": 3 }, { "group": "magic_recipe_basic", "prob": 3 }, { "group": "magic_recipe_advanced", "prob": 1 }, - { "group": "dragon_books", "prob": 3 } + { "group": "dragon_books", "prob": 3 }, + { "item": "spellcraft_theory", "prob": 2 } ] }, { @@ -42,13 +43,23 @@ { "group": "spellbook_loot_0", "prob": 6 }, { "group": "magic_recipe_basic", "prob": 6 }, { "group": "magic_recipe_advanced", "prob": 2 }, - { "group": "dragon_books", "prob": 5 } + { "group": "dragon_books", "prob": 5 }, + { "group": "spellcraft_books", "prob": 4 } ] }, { "type": "item_group", "id": "exotic_books", - "items": [ { "group": "spellbook_loot_1", "prob": 3 }, { "group": "dragon_books", "prob": 2 } ] + "items": [ + { "group": "spellbook_loot_1", "prob": 3 }, + { "group": "dragon_books", "prob": 2 }, + { "group": "spellcraft_books", "prob": 1 } + ] + }, + { + "type": "item_group", + "id": "spellcraft_books", + "items": [ { "item": "spellcraft_theory_basic", "prob": 70 }, { "item": "spellcraft_theory", "prob": 30 } ] }, { "type": "item_group", @@ -131,7 +142,8 @@ { "group": "spell_scroll_tier_1", "prob": 60 }, { "group": "spell_scroll_tier_2", "prob": 30 }, { "group": "magic_recipe_basic", "prob": 50 }, - { "group": "magic_recipe_advanced", "prob": 16 } + { "group": "magic_recipe_advanced", "prob": 16 }, + { "group": "spellcraft_books", "prob": 40 } ] }, { @@ -142,7 +154,8 @@ { "group": "spellbook_tier_0", "prob": 100 }, { "group": "spellbook_tier_0", "prob": 80 }, { "group": "spellbook_tier_0", "prob": 60 }, - { "group": "spellbook_tier_0", "prob": 20 } + { "group": "spellbook_tier_0", "prob": 20 }, + { "item": "spellcraft_theory_basic", "prob": 10 } ] }, { @@ -237,7 +250,15 @@ { "type": "item_group", "id": "magic_shop_clothes", - "items": [ [ "cloak", 3 ], [ "cloak_wool", 5 ], [ "jedi_cloak", 3 ], [ "robe", 5 ], [ "tophat", 5 ], [ "leathersandals", 6 ] ] + "items": [ + [ "cloak", 3 ], + [ "cloak_wool", 5 ], + [ "jedi_cloak", 3 ], + [ "robe", 5 ], + [ "tophat", 5 ], + [ "leathersandals", 6 ], + [ "bookstrap", 2 ] + ] }, { "type": "item_group", @@ -323,6 +344,7 @@ "type": "item_group", "//": "all enchanted miscellanious items", "items": [ + { "item": "golemcore", "prob": 10 }, { "item": "heat_cube", "prob": 100 }, { "item": "mkey_opening", "prob": 100 }, { "item": "mtorch_everburning", "prob": 100 }, @@ -797,6 +819,7 @@ }, { "distribution": [ + { "item": "golemcore", "prob": 20 }, { "item": "animist_doll_skeleton", "prob": 10 }, { "item": "animist_doll_zombie", "prob": 20 }, { "item": "animist_doll_decayed_pouncer", "prob": 3 } @@ -915,6 +938,7 @@ }, { "distribution": [ + { "item": "golemcore", "prob": 10 }, { "item": "recovery_spellbook", "prob": 5 }, { "item": "eshaper_spellbook", "prob": 10 }, { "item": "spell_scroll_recover_stamina", "prob": 50 }, @@ -972,7 +996,8 @@ { "item": "spell_scroll_druid_woodshaft", "prob": 30 }, { "item": "summon_scroll_smudged", "prob": 2 }, { "item": "spell_scroll_tornskin", "prob": 2 }, - { "item": "spell_scroll_summon_cats", "prob": 50 } + { "item": "spell_scroll_summon_cats", "prob": 50 }, + { "item": "spell_scroll_seed_of_growth", "prob": 15 } ], "prob": 45 }, @@ -1006,6 +1031,7 @@ { "group": "enchanted_tokens_tool", "prob": 15 }, { "group": "enchanted_tokens_weapon", "prob": 5 }, { "group": "potions_common", "prob": 35 }, + { "group": "spellcraft_books", "prob": 10 }, { "distribution": [ { "group": "enchanted_wands_lesser", "prob": 15 }, @@ -1036,6 +1062,7 @@ }, { "distribution": [ + { "item": "golemcore", "prob": 3 }, { "item": "light_manipulation_spellbook", "prob": 5 }, { "item": "translocate_spellbook", "prob": 2 }, { "item": "spell_scroll_obfuscated_body", "prob": 50 } diff --git a/data/mods/Magiclysm/itemgroups/spellbooks.json b/data/mods/Magiclysm/itemgroups/spellbooks.json index bdfc1c30fabd5..5fadcf918fdfd 100644 --- a/data/mods/Magiclysm/itemgroups/spellbooks.json +++ b/data/mods/Magiclysm/itemgroups/spellbooks.json @@ -13,6 +13,7 @@ [ "spell_scroll_blinding_flash", 50 ], [ "spell_scroll_ethereal_grasp", 50 ], [ "spell_scroll_druid_woodshaft", 50 ], + [ "spell_scroll_seed_of_growth", 35 ], [ "spell_scroll_summon_cats", 65 ], [ "spell_scroll_stonefist", 20 ], [ "spell_scroll_eshaper_piercing_bolt", 40 ], @@ -79,6 +80,7 @@ [ "spell_scroll_impactsling", 20 ], [ "spell_scroll_boneclub", 20 ], [ "spell_scroll_flamesword", 35 ], + [ "spell_scroll_force_jar", 30 ], [ "spell_scroll_flamebreath", 30 ] ] }, @@ -134,6 +136,7 @@ [ "spell_scroll_druidic_healing", 20 ], [ "spell_scroll_summon_magic_motorcycle", 5 ], [ "bio_sneeze_beam", 50 ], + [ "spell_scroll_banishment_lesser", 30 ], [ "spell_scroll_nova_flare", 25 ], [ "spell_scroll_freezing_touch", 40 ] ] diff --git a/data/mods/Magiclysm/items/alchemy_items.json b/data/mods/Magiclysm/items/alchemy_items.json index 44c9abcd4c0b8..e3c3a17f1e0b9 100644 --- a/data/mods/Magiclysm/items/alchemy_items.json +++ b/data/mods/Magiclysm/items/alchemy_items.json @@ -176,7 +176,7 @@ "container": "flask_glass", "charges": 1, "phase": "liquid", - "freezing_point": -100 + "freezing_point": -73 }, { "id": "stirge_proboscis", @@ -209,6 +209,15 @@ "flags": [ "TRADER_AVOID" ], "to_hit": -2 }, + { + "id": "bundle_demon_chitin_piece", + "type": "GENERIC", + "copy-from": "demon_chitin_piece", + "symbol": "=", + "name": { "str": "bundle of demon chitin chunks", "str_pl": "bundles of demon chitin chunks" }, + "proportional": { "price": 10, "weight": 10, "volume": 10 }, + "description": "Pieces of demon spider exoskeleton, bundled tightly together for storage. Disassemble to unpack." + }, { "type": "GENERIC", "id": "demon_chitin_plate", diff --git a/data/mods/Magiclysm/items/armor.json b/data/mods/Magiclysm/items/armor.json index 1d6a47e552eda..ceb110b3009c4 100644 --- a/data/mods/Magiclysm/items/armor.json +++ b/data/mods/Magiclysm/items/armor.json @@ -72,7 +72,7 @@ "copy-from": "boots_chitin", "type": "ARMOR", "name": { "str": "pair of demon chitin boots", "str_pl": "pairs of demon chitin boots" }, - "description": "Boots crafted from carefully cleaned and pruned pruned red exoskeletons of demon spiders. Fire-resistant and very durable.", + "description": "Boots crafted from carefully cleaned and pruned red exoskeletons of demon spiders. Fire-resistant and very durable.", "material": [ "demon_chitin" ], "proportional": { "weight": 0.9, "encumbrance": 0.9, "price": 10, "warmth": 2 }, "environmental_protection": 8 diff --git a/data/mods/Magiclysm/items/black_dragon_items.json b/data/mods/Magiclysm/items/black_dragon_items.json index b80da5799de16..10f2bc1a42cc0 100644 --- a/data/mods/Magiclysm/items/black_dragon_items.json +++ b/data/mods/Magiclysm/items/black_dragon_items.json @@ -7,13 +7,23 @@ "color": "black_white", "name": "black dragon scale", "price": 2500, - "material": [ "black_dragon_hide" ], + "material": [ "black_dragon_scales" ], "weight": "380 g", "volume": "75 ml", "bashing": 0, "to_hit": -4, "description": "A scale from a black dragon. It still has its magical properties and acid resistance." }, + { + "id": "bundle_black_scale", + "type": "GENERIC", + "copy-from": "dragon_black_scale", + "symbol": "=", + "name": { "str": "bundle of black dragon scales", "str_pl": "bundles of black dragon scales" }, + "//": "inspiration from bundle of planks", + "proportional": { "price": 10, "weight": 10, "volume": 10 }, + "description": "Scales from a black dragon, bundled tightly together for storage. Disassemble to unpack." + }, { "type": "COMESTIBLE", "id": "black_dragon_hide_raw", @@ -70,25 +80,35 @@ "category": "spare_parts", "to_hit": -1 }, + { + "id": "bundle_black_hide", + "type": "GENERIC", + "copy-from": "black_dragon_tanned_hide", + "symbol": ",", + "name": { "str": "bundle of black dragon hides", "str_pl": "bundles of black dragon hides" }, + "//": "inspiration from bundle of leather", + "proportional": { "price": 10, "weight": 10, "volume": 4 }, + "description": "Hides from a black dragon, bundled tightly together for storage. Disassemble to unpack." + }, { "id": "boots_black_dragon_scale", "type": "ARMOR", "category": "armor", "name": { "str": "pair of black dragonscale boots", "str_pl": "pairs of black dragonscale boots" }, - "description": "Boots made of black dragonscale. Very protective, and surprisingly light.", + "description": "Boots made of black incredibly durable dragonscale. Very protective, and surprisingly light.", "weight": "945 g", "volume": "3250 ml", "price": 75000, "to_hit": -2, "bashing": 7, - "material": [ "black_dragon_hide" ], + "material": [ "black_dragon_hide", "black_dragon_scales" ], "symbol": "[", "color": "black_white", "covers": [ "foot_l", "foot_r" ], "coverage": 100, "encumbrance": 30, - "warmth": 20, - "material_thickness": 4, + "warmth": 25, + "material_thickness": 5, "environmental_protection": 3, "flags": [ "VARSIZE", "WATERPROOF", "STURDY" ] }, @@ -97,7 +117,7 @@ "type": "ARMOR", "category": "armor", "name": { "str": "pair of black dragonhide boots", "str_pl": "pairs of black dragonhide boots" }, - "description": "Boots made of black dragonhide. Very protective, and surprisingly light.", + "description": "Boots made of very durable black dragonhide. Very protective, and surprisingly light.", "weight": "655 g", "volume": "3250 ml", "price": 50000, @@ -108,9 +128,9 @@ "color": "black_white", "covers": [ "foot_l", "foot_r" ], "coverage": 100, - "encumbrance": 18, + "encumbrance": 20, "warmth": 20, - "material_thickness": 2, + "material_thickness": 3, "environmental_protection": 3, "flags": [ "VARSIZE", "WATERPROOF", "STURDY" ] }, @@ -119,32 +139,47 @@ "type": "ARMOR", "category": "armor", "name": "black dragonscale helmet", - "description": "A helmet made from black dragonscale, held together with black dragonhide. It comes equipped with a full face visor.", + "description": "A helmet made from black incredibly durable dragonscale, held together with black dragonhide. It comes equipped with a full face visor with only a thin slit to see out of. Activate to raise the visor.", + "use_action": { "type": "transform", "target": "helmet_black_dragon_scale_raised", "msg": "You raise your visor." }, "weight": "856 g", "volume": "2500 ml", "price": 58000, "to_hit": -1, "bashing": 10, - "material": [ "black_dragon_hide" ], + "material": [ "black_dragon_hide", "black_dragon_scales" ], "symbol": "[", "color": "black_white", - "covers": [ "head", "eyes", "mouth" ], - "coverage": 100, - "encumbrance": 32, - "warmth": 15, - "material_thickness": 6, + "armor_portion_data": [ + { "covers": [ "head" ], "coverage": 100, "encumbrance": 32 }, + { "covers": [ "eyes", "mouth" ], "coverage": 100, "encumbrance": 20 } + ], + "warmth": 25, + "material_thickness": 5, "environmental_protection": 3, "techniques": [ "WBLOCK_1" ], - "flags": [ "VARSIZE", "WATERPROOF", "STURDY" ] + "qualities": [ [ "GLARE", 1 ] ], + "flags": [ "VARSIZE", "WATERPROOF", "STURDY", "SUN_GLASSES" ] + }, + { + "id": "helmet_black_dragon_scale_raised", + "type": "ARMOR", + "category": "armor", + "copy-from": "helmet_black_dragon_scale", + "name": { "str": "black dragonscale helmet (raised visor)", "str_pl": "black dragonscale helmets (raised visor)" }, + "description": "A helmet made from incredibly durable black dragonscale, held together with black dragonhide. The visor is raised.", + "use_action": { "type": "transform", "target": "helmet_black_dragon_scale", "msg": "You put down your visor." }, + "symbol": "[", + "armor_portion_data": [ { "covers": [ "head" ], "coverage": 100, "encumbrance": 32 } ], + "delete": { "qualities": [ [ "GLARE", 1 ] ], "flags": [ "SUN_GLASSES" ] } }, { "id": "helmet_black_dragon_hide", "type": "ARMOR", "category": "armor", "name": "black dragonhide helmet", - "description": "A helmet made from black dragonhide. It protects your head well, and doesn't cover your face.", + "description": "A helmet made from very durable black dragonhide. It protects your head well, but doesn't cover your face.", "weight": "585 g", - "volume": "2500 ml", + "volume": "2 L", "price": 58000, "to_hit": -1, "bashing": 10, @@ -153,9 +188,9 @@ "color": "black_white", "covers": [ "head" ], "coverage": 100, - "encumbrance": 32, + "encumbrance": 20, "warmth": 15, - "material_thickness": 2, + "material_thickness": 3, "environmental_protection": 1, "techniques": [ "WBLOCK_1" ], "flags": [ "VARSIZE", "WATERPROOF", "STURDY" ] @@ -165,21 +200,21 @@ "type": "ARMOR", "category": "armor", "name": "black dragonscale armor", - "description": "A full suit of black dragon scale mail. It comes with all the accoutrements that cover your torso, legs, and arms, with the benefit of being very light and flexible.", + "description": "A full suit of incredibly durable black dragon scale mail. It comes with all the accoutrements that cover your torso, legs, and arms, with the benefit of being very light and flexible.", "weight": "4250 g", "volume": "12 L", "price": 2000000, "to_hit": -3, "bashing": 6, - "material": [ "black_dragon_hide" ], + "material": [ "black_dragon_hide", "black_dragon_scales" ], "symbol": "[", "color": "black_white", "covers": [ "leg_l", "leg_r", "torso", "arm_l", "arm_r" ], "coverage": 100, "encumbrance": 25, - "warmth": 15, - "material_thickness": 4, - "environmental_protection": 2, + "warmth": 25, + "material_thickness": 5, + "environmental_protection": 3, "flags": [ "VARSIZE", "WATERPROOF", "RAINPROOF", "STURDY" ] }, { @@ -187,9 +222,9 @@ "type": "ARMOR", "category": "armor", "name": "black dragonhide armor", - "description": "A full suit of black dragonhide armor. It comes with all the accoutrements that cover your torso, legs, and arms, with the benefit of being very light and flexible.", + "description": "A full suit of very durable black dragonhide armor. It comes with all the accoutrements that cover your torso, legs, and arms, with the benefit of being very light and flexible.", "weight": "3 kg", - "volume": "12 L", + "volume": "11 L", "price": 2000000, "to_hit": -3, "bashing": 6, @@ -200,8 +235,8 @@ "coverage": 100, "encumbrance": 18, "warmth": 15, - "material_thickness": 4, - "environmental_protection": 1, + "material_thickness": 3, + "environmental_protection": 2, "flags": [ "VARSIZE", "WATERPROOF", "RAINPROOF", "STURDY" ] }, { @@ -209,19 +244,19 @@ "type": "ARMOR", "category": "armor", "name": { "str": "pair of black dragonscale gauntlets", "str_pl": "pairs of black dragonscale gauntlets" }, - "description": "A pair of heavy-duty gauntlets made of black dragonscale that covers your hands.", + "description": "A pair of heavy-duty gauntlets made of incredibly durable black dragonscale that covers your hands.", "weight": "380 g", "volume": "1 L", "price": 180000, "to_hit": 2, - "material": [ "black_dragon_hide" ], + "material": [ "black_dragon_hide", "black_dragon_scales" ], "symbol": "[", "color": "black_white", "covers": [ "hand_l", "hand_r" ], "coverage": 100, "encumbrance": 20, - "warmth": 15, - "material_thickness": 3, + "warmth": 20, + "material_thickness": 4, "environmental_protection": 3, "flags": [ "VARSIZE", "WATERPROOF", "STURDY" ] }, @@ -230,7 +265,7 @@ "type": "ARMOR", "category": "armor", "name": { "str": "pair of black dragonhide gloves", "str_pl": "pairs of black dragonhide gloves" }, - "description": "A pair of customized, Kevlar armored leather gloves, modified to be easy to wear while providing maximum protection under extreme conditions.", + "description": "A pair of gloves made of very durable black dragonhide, modified to be easy to wear while providing maximum protection under extreme conditions.", "weight": "230 g", "volume": "750 ml", "price": 180000, @@ -241,7 +276,7 @@ "covers": [ "hand_l", "hand_r" ], "coverage": 100, "encumbrance": 8, - "warmth": 15, + "warmth": 13, "material_thickness": 2, "environmental_protection": 1, "flags": [ "VARSIZE", "WATERPROOF", "STURDY" ] @@ -251,7 +286,7 @@ "copy-from": "boots_black_dragon_scale", "type": "ARMOR", "name": { "str": "pair of XL black dragonscale boots", "str_pl": "pairs of XL black dragonscale boots" }, - "description": "Massive boots made of black dragonscale, modified to fit even the strangest of bodies. Very protective, and surprisingly light.", + "description": "Massive boots made of incredibly durable black dragonscale, modified to fit even the strangest of bodies. Very protective, and surprisingly light.", "weight": "1545 g", "volume": "6250 ml", "encumbrance": 40, @@ -262,10 +297,10 @@ "copy-from": "boots_black_dragon_hide", "type": "ARMOR", "name": { "str": "pair of XL black dragonhide boots", "str_pl": "pairs of XL black dragonhide boots" }, - "description": "Massive boots made of black dragonhide, modified to fit even the strangest of bodies. Very protective, and surprisingly light.", + "description": "Massive boots made of very durable black dragonhide, modified to fit even the strangest of bodies. Very protective, and surprisingly light.", "weight": "955 g", "volume": "6250 ml", - "encumbrance": 28, + "encumbrance": 25, "flags": [ "OVERSIZE", "VARSIZE", "WATERPROOF", "STURDY" ] }, { @@ -273,7 +308,7 @@ "copy-from": "gauntlets_black_dragon_scale", "type": "ARMOR", "name": { "str": "pair of XL black dragonscale gauntlets", "str_pl": "pairs of XL black dragonscale gauntlets" }, - "description": "A pair of heavy-duty gauntlets made of black dragonscale that covers your hands, or whatever you use as hands.", + "description": "A pair of heavy-duty gauntlets made of incredibly durable black dragonscale that covers your hands, or whatever you use as hands.", "weight": "680 g", "volume": "2 L", "encumbrance": 30, @@ -284,65 +319,34 @@ "copy-from": "gloves_black_dragon_hide", "type": "ARMOR", "name": { "str": "pair of XL black dragonhide gloves", "str_pl": "pairs of XL black dragonhide gloves" }, - "description": "A pair of customized, Kevlar armored leather gloves, modified to be easy to wear while providing maximum protection under extreme conditions. Sized to fit even the strangest of anatomy.", + "description": "A pair of gloves made of very durable black dragonhide, modified to be easy to wear while providing maximum protection under extreme conditions. Sized to fit even the strangest of anatomy.", "weight": "430 g", "volume": "1500 ml", "encumbrance": 18, "flags": [ "OVERSIZE", "VARSIZE", "WATERPROOF", "STURDY" ] }, - { - "id": "boots_xlblack_dragon_scale", - "copy-from": "boots_black_dragon_scale", - "type": "ARMOR", - "name": { "str": "pair of XL black dragonscale boots", "str_pl": "pairs of XL black dragonscale boots" }, - "description": "Massive boots made of black dragonscale, modified to fit even the strangest of bodies. Very protective, and surprisingly light.", - "weight": "1545 g", - "volume": "6250 ml", - "encumbrance": 40, - "flags": [ "OVERSIZE", "VARSIZE", "WATERPROOF", "STURDY" ] - }, - { - "id": "boots_xlblack_dragon_hide", - "copy-from": "boots_black_dragon_hide", - "type": "ARMOR", - "name": { "str": "pair of XL black dragonhide boots", "str_pl": "pairs of XL black dragonhide boots" }, - "description": "Massive boots made of black dragonhide, modified to fit even the strangest of bodies. Very protective, and surprisingly light.", - "weight": "955 g", - "volume": "6250 ml", - "encumbrance": 28, - "flags": [ "OVERSIZE", "VARSIZE", "WATERPROOF", "STURDY" ] - }, { "id": "helmet_xlblack_dragon_scale", "copy-from": "helmet_black_dragon_scale", "type": "ARMOR", "name": { "str": "XL black dragonscale helmet" }, - "description": "A massive helmet made from black dragonscale, held together with black dragonhide. It comes equipped with a full face visor and is large enough to fit even the strangest of heads.", + "description": "A massive helmet made from incredibly durable black dragonscale, held together with black dragonhide. It comes equipped with a full face visor you can raise and is large enough to fit even the strangest of heads.", + "use_action": { "type": "transform", "target": "helmet_xlblack_dragon_scale_raised", "msg": "You raise your visor." }, "weight": "1256 g", "volume": "4500 ml", - "encumbrance": 42, - "flags": [ "OVERSIZE", "VARSIZE", "WATERPROOF", "STURDY" ] + "armor_portion_data": [ { "covers": [ "head" ], "coverage": 100, "encumbrance": 42 } ], + "flags": [ "OVERSIZE", "VARSIZE", "WATERPROOF", "STURDY", "SUN_GLASSES" ] }, { - "id": "helmet_xlblack_dragon_hide", - "copy-from": "helmet_black_dragon_hide", + "id": "helmet_xlblack_dragon_scale_raised", + "copy-from": "helmet_black_dragon_scale_raised", "type": "ARMOR", - "name": { "str": "XL black dragonhide helmet" }, - "description": "A massive helmet made from black dragonhide. It protects your head well, and doesn't cover your face, but is large enough to fit even the strangest of heads.", - "weight": "815 g", - "volume": "4500 ml", - "encumbrance": 42, - "flags": [ "OVERSIZE", "VARSIZE", "WATERPROOF", "STURDY" ] - }, - { - "id": "helmet_xlblack_dragon_scale", - "copy-from": "helmet_black_dragon_scale", - "type": "ARMOR", - "name": { "str": "XL black dragonscale helmet" }, - "description": "A massive helmet made from black dragonscale, held together with black dragonhide. It comes equipped with a full face visor and is large enough to fit even the strangest of heads.", + "name": { "str": "XL black dragonscale helmet (raised visor)", "str_pl": "XL black dragonscale helmets (raised visor)" }, + "description": "A massive helmet made from incredibly durable black dragonscale, held together with black dragonhide. It is large enough to fit even the strangest of heads. The visor is raised", + "use_action": { "type": "transform", "target": "helmet_xlblack_dragon_scale", "msg": "You put down your visor." }, + "armor_portion_data": [ { "covers": [ "head" ], "coverage": 100, "encumbrance": 42 } ], "weight": "1256 g", "volume": "4500 ml", - "encumbrance": 42, "flags": [ "OVERSIZE", "VARSIZE", "WATERPROOF", "STURDY" ] }, { @@ -350,10 +354,10 @@ "copy-from": "helmet_black_dragon_hide", "type": "ARMOR", "name": { "str": "XL black dragonhide helmet" }, - "description": "A massive helmet made from black dragonhide. It protects your head well, and doesn't cover your face, but is large enough to fit even the strangest of heads.", + "description": "A massive helmet made from very durable black dragonhide. It protects your head well, and doesn't cover your face, but is large enough to fit even the strangest of heads.", "weight": "815 g", "volume": "4500 ml", - "encumbrance": 42, + "encumbrance": 26, "flags": [ "OVERSIZE", "VARSIZE", "WATERPROOF", "STURDY" ] }, { @@ -361,7 +365,7 @@ "copy-from": "suit_black_dragon_scale", "type": "ARMOR", "name": { "str": "XL black dragonscale armor" }, - "description": "A massive full suit of black dragon scale mail. It comes with all the accoutrements that cover your torso, legs, and arms; sized to fit even the strangest of bodies.", + "description": "A massive full suit of incredibly durable black dragon scale mail. It comes with all the accoutrements that cover your torso, legs, and arms; sized to fit even the strangest of bodies.", "weight": "6250 g", "volume": "18 L", "encumbrance": 35, @@ -372,10 +376,70 @@ "copy-from": "suit_black_dragon_hide", "type": "ARMOR", "name": { "str": "XL black dragonhide armor" }, - "description": "A massive full suit of black dragonhide armor. It comes with all the accoutrements that cover your torso, legs, and arms; sized to fit even the strangest of bodies.", + "description": "A massive full suit of very durable black dragonhide armor. It comes with all the accoutrements that cover your torso, legs, and arms; sized to fit even the strangest of bodies.", "weight": "5500 g", "volume": "18 L", - "encumbrance": 28, + "encumbrance": 26, "flags": [ "OVERSIZE", "VARSIZE", "WATERPROOF", "RAINPROOF", "STURDY" ] + }, + { + "id": "backpack_black_dragon_hide", + "//": "After adding more dragons, maybe make it more generic", + "type": "ARMOR", + "name": { "str": "dragonhide backpack" }, + "description": "A custom-built backpack. Made of very durable dragon leather and carefully crafted to hold as much stuff as possible.", + "weight": "900 g", + "volume": "5250 ml", + "price": 240000, + "price_postapoc": 3250, + "material": [ "black_dragon_hide" ], + "symbol": "[", + "looks_like": "backpack", + "color": "black", + "covers": [ "torso" ], + "coverage": 40, + "encumbrance": 5, + "max_encumbrance": 30, + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "max_contains_volume": "29 L", + "max_contains_weight": "45 kg", + "max_item_length": "48 cm", + "magazine_well": "5 L", + "moves": 300 + } + ], + "warmth": 8, + "material_thickness": 3, + "environmental_protection": 3, + "flags": [ "WATER_FRIENDLY", "STURDY", "BELTED", "WATERPROOF" ] + }, + { + "id": "backpack_xl_black_dragon_hide", + "copy-from": "backpack_black_dragon_hide", + "type": "ARMOR", + "name": { "str": "XL dragonhide backpack" }, + "weight": "1200 g", + "volume": "6250 ml", + "price": 280000, + "price_postapoc": 3450, + "symbol": "[", + "encumbrance": 8, + "max_encumbrance": 40, + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "max_contains_volume": "38 L", + "max_contains_weight": "52 kg", + "max_item_length": "58 cm", + "magazine_well": "5 L", + "moves": 330 + } + ], + "warmth": 9, + "material_thickness": 4, + "environmental_protection": 3, + "extend": { "flags": [ "OVERSIZE" ] } } ] diff --git a/data/mods/Magiclysm/items/books.json b/data/mods/Magiclysm/items/books.json new file mode 100644 index 0000000000000..ba3642e57161e --- /dev/null +++ b/data/mods/Magiclysm/items/books.json @@ -0,0 +1,39 @@ +[ + { + "id": "spellcraft_theory", + "type": "BOOK", + "name": { "str": "Spellcraft Theory", "str_pl": "copies of Spellcraft Theory" }, + "description": "A intermediate textbook on magical theories.", + "weight": "1587 g", + "volume": "1750 ml", + "longest_side": "22 cm", + "price": 8200, + "price_postapoc": 750, + "bashing": 5, + "material": [ "paper" ], + "symbol": "?", + "color": "blue", + "skill": "spellcraft", + "required_level": 2, + "max_level": 4, + "intelligence": 12, + "time": "60 m", + "fun": -1 + }, + { + "id": "spellcraft_theory_basic", + "type": "BOOK", + "copy-from": "spellcraft_theory", + "name": { "str": "Basic Spellcraft Theory", "str_pl": "copies of Basic Spellcraft Theory" }, + "description": "A beginner textbook on magical theories.", + "weight": "1087 g", + "volume": "1450 ml", + "price": 5200, + "price_postapoc": 550, + "required_level": 0, + "max_level": 2, + "intelligence": 9, + "time": "45 m", + "fun": 0 + } +] diff --git a/data/mods/Magiclysm/items/cast_spell_items.json b/data/mods/Magiclysm/items/cast_spell_items.json index 8ebb9e3283124..bd5f1a5a49a02 100644 --- a/data/mods/Magiclysm/items/cast_spell_items.json +++ b/data/mods/Magiclysm/items/cast_spell_items.json @@ -19,7 +19,7 @@ "flags": [ "EATEN_COLD", "NUTRIENT_OVERRIDE" ], "phase": "liquid", "price": 2500, - "freezing_point": 40 + "freezing_point": 4 }, { "id": "mana_potion", @@ -56,7 +56,7 @@ "flags": [ "EATEN_COLD", "NUTRIENT_OVERRIDE" ], "phase": "liquid", "price": 3000, - "freezing_point": 40 + "freezing_point": 4 }, { "id": "ogres_strength_potion", @@ -139,7 +139,7 @@ "flags": [ "TRADER_AVOID", "NUTRIENT_OVERRIDE" ], "phase": "liquid", "price": 2500, - "freezing_point": 10 + "freezing_point": -12 }, { "id": "twisted_restore_potion_improved", diff --git a/data/mods/Magiclysm/items/constructs.json b/data/mods/Magiclysm/items/constructs.json index e1288be2576b5..5b534b18d5aa4 100644 --- a/data/mods/Magiclysm/items/constructs.json +++ b/data/mods/Magiclysm/items/constructs.json @@ -1,4 +1,19 @@ [ + { + "type": "GENERIC", + "id": "golemcore", + "symbol": ".", + "color": "red", + "name": "golem core", + "description": "The \"heart\" of a golem. Makes a soft humming noise when you hold it close to your ear.", + "price": 10000, + "price_postapoc": 5000, + "material": [ "orichalcum_metal" ], + "weight": "5 kg", + "volume": "1 L", + "bashing": 6, + "flags": [ "NO_REPAIR", "FRAGILE_MELEE" ] + }, { "type": "GENERIC", "id": "broken_claygolem", diff --git a/data/mods/Magiclysm/items/enchanted_rings.json b/data/mods/Magiclysm/items/enchanted_rings.json index 52ea3568c77fb..cd0bd583d432b 100644 --- a/data/mods/Magiclysm/items/enchanted_rings.json +++ b/data/mods/Magiclysm/items/enchanted_rings.json @@ -15,7 +15,7 @@ "sided": true, "coverage": 0, "warmth": 0, - "flags": [ "WATER_FRIENDLY", "ALLOWS_NATURAL_ATTACKS", "FANCY", "ONLY_ONE", "SKINTIGHT" ] + "flags": [ "WATER_FRIENDLY", "ALLOWS_NATURAL_ATTACKS", "FANCY", "ONE_PER_LAYER", "SKINTIGHT" ] }, { "abstract": "mring_silver", @@ -33,7 +33,7 @@ "sided": true, "coverage": 0, "warmth": 0, - "flags": [ "WATER_FRIENDLY", "ALLOWS_NATURAL_ATTACKS", "FANCY", "ONLY_ONE", "SKINTIGHT" ] + "flags": [ "WATER_FRIENDLY", "ALLOWS_NATURAL_ATTACKS", "FANCY", "ONE_PER_LAYER", "SKINTIGHT" ] }, { "abstract": "mring_gold", @@ -51,7 +51,7 @@ "sided": true, "coverage": 0, "warmth": 0, - "flags": [ "WATER_FRIENDLY", "ALLOWS_NATURAL_ATTACKS", "FANCY", "ONLY_ONE", "SKINTIGHT" ] + "flags": [ "WATER_FRIENDLY", "ALLOWS_NATURAL_ATTACKS", "FANCY", "ONE_PER_LAYER", "SKINTIGHT" ] }, { "abstract": "mring_platinum", @@ -69,7 +69,7 @@ "sided": true, "coverage": 0, "warmth": 0, - "flags": [ "WATER_FRIENDLY", "ALLOWS_NATURAL_ATTACKS", "FANCY", "ONLY_ONE", "SKINTIGHT" ] + "flags": [ "WATER_FRIENDLY", "ALLOWS_NATURAL_ATTACKS", "FANCY", "ONE_PER_LAYER", "SKINTIGHT" ] }, { "copy-from": "mring_copper", diff --git a/data/mods/Magiclysm/items/ethereal_items.json b/data/mods/Magiclysm/items/ethereal_items.json index fca6f63e3b456..a4ac0b348f05d 100644 --- a/data/mods/Magiclysm/items/ethereal_items.json +++ b/data/mods/Magiclysm/items/ethereal_items.json @@ -378,6 +378,24 @@ "max_charges": 15, "use_action": [ "WATER_PURIFIER" ] }, + { + "type": "COMESTIBLE", + "id": "druid_fertilizer", + "name": { "str": "seed of growth", "str_pl": "seeds of growth" }, + "weight": "27 g", + "color": "white", + "flags": [ "TRADER_AVOID", "FERTILIZER" ], + "comestible_type": "FOOD", + "symbol": "°", + "quench": -10, + "healthy": -2, + "description": "A magical powder that can be scattered on growing crops and make them grow faster.", + "material": [ "powder" ], + "volume": "2 L", + "charges": 60, + "category": "chems", + "fun": -15 + }, { "id": "obfuscating_aura", "type": "ARMOR", @@ -619,8 +637,7 @@ "TRADER_AVOID", "NO_UNLOAD", "NO_REPAIR", - "WATERPROOF_GUN", - "NEVER_JAMS" + "WATERPROOF_GUN" ], "ammo_effects": [ "NEVER_MISFIRES" ], "weight": "86 g", @@ -758,6 +775,38 @@ "passive_effects": [ { "has": "WIELD", "condition": "ALWAYS", "values": [ { "value": "ITEM_DAMAGE_COLD", "add": 6 } ] } ] } }, + { + "id": "jar_3l_force", + "type": "GENERIC", + "category": "container", + "name": { "str": "jar of force", "str_pl": "jars of force" }, + "looks_like": "jar_glass_sealed", + "description": "A three-liter container made out of transparent force. Can be used to store liquids.", + "weight": "1 g", + "volume": "3050 ml", + "longest_side": "298 mm", + "price": 0, + "price_postapoc": 0, + "to_hit": -1, + "bashing": 1, + "material": [ ], + "symbol": ")", + "color": "light_cyan", + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "rigid": true, + "watertight": true, + "max_contains_volume": "3 L", + "max_contains_weight": "6 kg" + } + ], + "flags": [ "UNBREAKABLE_MELEE", "NONCONDUCTIVE", "NO_REPAIR", "NO_SALVAGE", "MAGIC_FOCUS", "TRADER_AVOID" ], + "qualities": [ [ "CONTAIN", 1 ], [ "BOIL", 1 ] ], + "relic_data": { + "passive_effects": [ { "has": "WIELD", "condition": "ALWAYS", "values": [ { "value": "ITEM_DAMAGE_PURE", "add": 3 } ] } ] + } + }, { "id": "longsword_holy", "type": "GENERIC", diff --git a/data/mods/Magiclysm/items/mutagen.json b/data/mods/Magiclysm/items/mutagen.json index 5e12fc705c9a1..b7dcd25032157 100644 --- a/data/mods/Magiclysm/items/mutagen.json +++ b/data/mods/Magiclysm/items/mutagen.json @@ -20,7 +20,7 @@ "charges": 2, "use_action": { "type": "mutagen_iv", "mutation_category": "MANATOUCHED" }, "flags": [ "EATEN_COLD", "NUTRIENT_OVERRIDE" ], - "freezing_point": -22, + "freezing_point": -30, "fun": -15 }, { diff --git a/data/mods/Magiclysm/items/spell_scrolls.json b/data/mods/Magiclysm/items/spell_scrolls.json index 7698667b2b643..e4b7a4e43075a 100644 --- a/data/mods/Magiclysm/items/spell_scrolls.json +++ b/data/mods/Magiclysm/items/spell_scrolls.json @@ -801,6 +801,15 @@ "description": "With a shout and a gesture, the target starts bleeding from old wounds.", "use_action": { "type": "learn_spell", "spells": [ "bleed" ] } }, + { + "type": "BOOK", + "copy-from": "spell_scroll", + "id": "spell_scroll_banishment_lesser", + "//": "Animist spell", + "name": { "str": "Scroll of Lesser Banishment", "str_pl": "Scrolls of Lesser Banishment" }, + "description": "Banish a monster to the lesser-known nether dimension. If a monster is more powerful than you can handle, the spell drains your life force to make up the difference.", + "use_action": { "type": "learn_spell", "spells": [ "banishment_lesser" ] } + }, { "type": "BOOK", "copy-from": "spell_scroll", @@ -1018,6 +1027,14 @@ "description": "Causes an intense heat at the location, greatly damaging the target.", "use_action": { "type": "learn_spell", "spells": [ "nova_flare" ] } }, + { + "type": "BOOK", + "copy-from": "spell_scroll", + "id": "spell_scroll_force_jar", + "name": { "str": "Scroll of Jar of Force", "str_pl": "Scrolls of Jar of Force" }, + "description": "Summon a jar of force you can use to store liquids in.", + "use_action": { "type": "learn_spell", "spells": [ "magus_force_jar" ] } + }, { "type": "BOOK", "copy-from": "spell_scroll", @@ -1026,5 +1043,14 @@ "name": { "str": "Scroll of freezing touch", "str_pl": "Scrolls of freezing touch" }, "description": "Your hands freeze anything they touch at temperatures so cold it slows down your foes.", "use_action": { "type": "learn_spell", "spells": [ "freezing_touch" ] } + }, + { + "type": "BOOK", + "copy-from": "spell_scroll", + "id": "spell_scroll_seed_of_growth", + "//": "Druid spell", + "name": { "str": "Scroll of seed of growth", "str_pl": "Scrolls of seed of growth" }, + "description": "Offer some of your life in a ritual to get tokens of growth in return.", + "use_action": { "type": "learn_spell", "spells": [ "druid_growth" ] } } ] diff --git a/data/mods/Magiclysm/items/spellbooks.json b/data/mods/Magiclysm/items/spellbooks.json index d9e9edc028a0e..070f01e30d43d 100644 --- a/data/mods/Magiclysm/items/spellbooks.json +++ b/data/mods/Magiclysm/items/spellbooks.json @@ -59,7 +59,7 @@ "id": "wizard_utility", "type": "BOOK", "name": { "str": "Wizarding Guide to Backpacking", "str_pl": "copies of Wizarding Guide to Backpacking" }, - "//": "1 Magus, 1 Biomancer, 1, Kelvinist, 1 classless spell", + "//": "2 Magus, 1 Biomancer, 1, Kelvinist, 1 classless spell", "description": "This appears to be the spell version of a guide for what things to take with you when backpacking. It's a little bulky, but will certainly prove useful.", "weight": "1 kg", "volume": "1250 ml", @@ -67,7 +67,10 @@ "material": [ "paper" ], "symbol": "?", "color": "red", - "use_action": { "type": "learn_spell", "spells": [ "phase_door", "create_lighter", "pain_split", "protection_aura" ] } + "use_action": { + "type": "learn_spell", + "spells": [ "phase_door", "create_lighter", "pain_split", "protection_aura", "magus_force_jar" ] + } }, { "id": "pyro", diff --git a/data/mods/Magiclysm/items/tools.json b/data/mods/Magiclysm/items/tools.json index 9c312db56fd5a..a860442c1d295 100644 --- a/data/mods/Magiclysm/items/tools.json +++ b/data/mods/Magiclysm/items/tools.json @@ -16,7 +16,7 @@ "color": "red", "pocket_data": [ { "open_container": true, "watertight": true, "max_contains_volume": "16 L", "max_contains_weight": "50 kg" } ], "//": "I went ahead and gave this a level of 2 for when magical mutagens become a thing as I figured dragonblood for instance should need different tools than making alpha mutagen.", - "qualities": [ [ "COOK", 3 ], [ "BOIL", 2 ], [ "CONTAIN", 1 ], [ "MAGIC_MUTAGEN", 2 ] ], + "qualities": [ [ "COOK", 3 ], [ "BOIL", 2 ], [ "CONTAIN", 1 ], [ "MAGIC_MUTAGEN", 2 ], [ "MAGIC_CAULDRON", 1 ] ], "use_action": [ "HEAT_FOOD" ] }, { @@ -34,7 +34,7 @@ "symbol": "U", "color": "yellow", "pocket_data": [ { "open_container": true, "watertight": true, "max_contains_volume": "16 L", "max_contains_weight": "50 kg" } ], - "qualities": [ [ "COOK", 3 ], [ "BOIL", 2 ], [ "CONTAIN", 1 ], [ "MAGIC_MUTAGEN", 1 ] ], + "qualities": [ [ "COOK", 3 ], [ "BOIL", 2 ], [ "CONTAIN", 1 ], [ "MAGIC_MUTAGEN", 1 ], [ "MAGIC_CAULDRON", 2 ] ], "use_action": [ "HEAT_FOOD" ] }, { diff --git a/data/mods/Magiclysm/materials.json b/data/mods/Magiclysm/materials.json index ebb088db807ca..2bbc2a2d0bcd3 100644 --- a/data/mods/Magiclysm/materials.json +++ b/data/mods/Magiclysm/materials.json @@ -24,22 +24,50 @@ "type": "material", "id": "black_dragon_hide", "name": "Black Dragon Hide", - "density": 20, - "specific_heat_liquid": 4.186, - "specific_heat_solid": 2.108, + "density": 9, "latent_heat": 333, - "bash_resist": 5, - "cut_resist": 8, + "bash_resist": 3, + "cut_resist": 7, "bullet_resist": 6, "acid_resist": 40, "fire_resist": 1, - "elec_resist": 1, - "chip_resist": 30, + "elec_resist": 2, + "chip_resist": 20, + "reinforces": true, + "soft": true, + "repaired_with": "black_dragon_tanned_hide", + "dmg_adj": [ "scratched", "cut", "ripped", "shredded" ], + "bash_dmg_verb": "ripped", + "cut_dmg_verb": "scratched", + "burn_data": [ + { "fuel": 0, "smoke": 0, "burn": 0 }, + { "fuel": 1, "smoke": 1, "burn": 1, "volume_per_turn": "100 ml" }, + { "fuel": 2, "smoke": 4, "burn": 2 } + ] + }, + { + "type": "material", + "id": "black_dragon_scales", + "name": "Black Dragon Scale", + "density": 15, + "latent_heat": 533, + "bash_resist": 8, + "cut_resist": 9, + "bullet_resist": 8, + "acid_resist": 40, + "fire_resist": 4, + "elec_resist": 3, + "chip_resist": 35, "reinforces": true, "repaired_with": "dragon_black_scale", - "dmg_adj": [ "scratched", "cut", "cracked", "shattered" ], + "dmg_adj": [ "scratched", "marked", "cracked", "shattered" ], "bash_dmg_verb": "cracked", - "cut_dmg_verb": "chipped" + "cut_dmg_verb": "chipped", + "burn_data": [ + { "fuel": 0, "smoke": 0, "burn": 0 }, + { "fuel": 0, "smoke": 0, "burn": 0 }, + { "fuel": 1, "smoke": 1, "burn": 1, "volume_per_turn": "100 ml" } + ] }, { "type": "material", @@ -86,8 +114,7 @@ "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", "cut_dmg_verb": "scratched", - "burn_products": [ [ "scrap_bronze", 1 ] ], - "compacts_into": [ "orichalcum_lump", "orichalcum_sliver" ] + "burn_products": [ [ "scrap_bronze", 1 ] ] }, { "type": "material", @@ -107,8 +134,7 @@ "repaired_with": "mithril_ingot", "dmg_adj": [ "marked", "dented", "smashed", "shattered" ], "bash_dmg_verb": "dented", - "cut_dmg_verb": "scratched", - "compacts_into": [ "mithril_lump", "mithril_ingot" ] + "cut_dmg_verb": "scratched" }, { "type": "material", diff --git a/data/mods/Magiclysm/monsters/Orcs.json b/data/mods/Magiclysm/monsters/Orcs.json index 71c797abfe79a..d01baf6a96df9 100644 --- a/data/mods/Magiclysm/monsters/Orcs.json +++ b/data/mods/Magiclysm/monsters/Orcs.json @@ -1,4 +1,10 @@ [ + { + "type": "item_group", + "subtype": "collection", + "id": "quiver_orc_archer", + "entries": [ { "item": "arrow_wood_heavy", "count": [ 1, 6 ], "charges": [ 1, 10 ] } ] + }, { "id": "mon_orc_warrior", "type": "MONSTER", @@ -57,10 +63,11 @@ { "item": "armguard_scrap", "prob": 40 }, { "item": "cuirass_scrap", "prob": 40 }, { "item": "longbow", "prob": 100 }, - { "item": "sheath", "contents-item": "knife_combat", "prob": 50 } + { "item": "sheath", "contents-item": "knife_combat", "prob": 50 }, + { "item": "quiver_large", "contents-group": "quiver_orc_archer", "prob": 100 } ] }, - "starting_ammo": { "arrow_wood_heavy": 75 }, + "starting_ammo": { "arrow_wood_heavy": 60 }, "extend": { "special_attacks": [ { @@ -69,7 +76,7 @@ "move_cost": 93, "gun_type": "longbow", "ammo_type": "arrow_wood_heavy", - "fake_skills": [ [ "gun", 6 ], [ "rifle", 7 ] ], + "fake_skills": [ [ "gun", 6 ], [ "archery", 7 ] ], "fake_dex": 9, "fake_per": 5, "require_targeting_player": false, @@ -77,7 +84,8 @@ "ranges": [ [ 3, 13, "DEFAULT" ] ], "no_ammo_sound": "grunting" } - ] + ], + "flags": [ "DROPS_AMMO" ] } }, { diff --git a/data/mods/Magiclysm/proficiencies.json b/data/mods/Magiclysm/proficiencies.json index 2eaf25d02e2c8..f57b91947089e 100644 --- a/data/mods/Magiclysm/proficiencies.json +++ b/data/mods/Magiclysm/proficiencies.json @@ -19,5 +19,37 @@ "default_time_multiplier": 2, "default_fail_multiplier": 2, "required_proficiencies": [ "prof_alchemy" ] + }, + { + "type": "proficiency", + "id": "prof_leatherworking_dragon", + "name": { "str": "Dragon leather working" }, + "description": "Working with dragon leather requires a specific set of skills and tools… a set you are familiar with.", + "can_learn": true, + "default_time_multiplier": 1.5, + "default_fail_multiplier": 2, + "time_to_learn": "6 h", + "required_proficiencies": [ "prof_leatherworking" ] + }, + { + "type": "proficiency", + "id": "prof_scaleworking_dragon", + "name": { "str": "Dragon scale working" }, + "description": "Working with dragon scales requires a specific set of skills and tools… a set you are familiar with.", + "can_learn": true, + "default_time_multiplier": 2, + "default_fail_multiplier": 3, + "time_to_learn": "12 h", + "required_proficiencies": [ "prof_leatherworking_dragon" ] + }, + { + "type": "proficiency", + "id": "prof_golemancy_basic", + "name": { "str": "Basic Golemancy" }, + "description": "Infusing shaped material with your will and the ability to move is hard but you're starting to get it.", + "can_learn": true, + "time_to_learn": "4 h", + "default_time_multiplier": 1.5, + "default_fail_multiplier": 2 } ] diff --git a/data/mods/Magiclysm/recipes/alchemy.json b/data/mods/Magiclysm/recipes/alchemy.json index 17392e98cf3d8..5d695526e2b97 100644 --- a/data/mods/Magiclysm/recipes/alchemy.json +++ b/data/mods/Magiclysm/recipes/alchemy.json @@ -21,6 +21,32 @@ "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_OTHER" }, + { + "type": "recipe", + "activity_level": "fake", + "result": "dragon_essence", + "id_suffix": "with_blood", + "charges": 2, + "batch_time_factors": [ 40, 3 ], + "qualities": [ + { "id": "CONCENTRATE", "level": 1 }, + { "id": "FINE_DISTILL", "level": 1 }, + { "id": "SEPARATE", "level": 1 }, + { "id": "CONTAIN", "level": 1 }, + { "id": "MANA_FOCUS", "level": 1 }, + { "id": "MAGIC_CAULDRON", "level": 1 } + ], + "tools": [ [ [ "surface_heat", 20, "LIST" ] ] ], + "components": [ [ [ "dragon_scale", 6, "LIST" ], [ "meat_dragon", 60 ] ], [ [ "dragon_blood", 500 ] ] ], + "proficiencies": [ { "proficiency": "prof_alchemy", "required": true } ], + "time": "2 h", + "skill_used": "chemistry", + "difficulty": 5, + "skills_required": [ "spellcraft", 6 ], + "book_learn": [ [ "black_dragons", 5 ] ], + "category": "CC_ENCHANTED", + "subcategory": "CSC_ENCHANTED_OTHER" + }, { "type": "recipe", "activity_level": "fake", @@ -97,5 +123,17 @@ "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_OTHER", "autolearn": true + }, + { + "result": "bundle_demon_chitin_piece", + "type": "recipe", + "activity_level": "NO_EXERCISE", + "category": "CC_ENCHANTED", + "subcategory": "CSC_ENCHANTED_MATERIALS", + "skill_used": "fabrication", + "time": "1 m", + "autolearn": true, + "reversible": true, + "components": [ [ [ "demon_chitin_piece", 10 ] ], [ [ "rope_any_short", 1, "LIST" ] ] ] } ] diff --git a/data/mods/Magiclysm/recipes/blacksmithing.json b/data/mods/Magiclysm/recipes/blacksmithing.json index 95a4f7c4e0736..5033df4212c0b 100644 --- a/data/mods/Magiclysm/recipes/blacksmithing.json +++ b/data/mods/Magiclysm/recipes/blacksmithing.json @@ -70,5 +70,30 @@ "proficiencies": [ { "proficiency": "prof_alchemy", "required": false, "time_multiplier": 1.5, "fail_multiplier": 5 } ], "tools": [ [ [ "surface_heat", 10, "LIST" ] ] ], "components": [ [ [ "charcoal", 50 ] ], [ [ "crystallized_mana", 10 ] ], [ [ "denat_alcohol", 10 ] ] ] + }, + { + "type": "recipe", + "activity_level": "MODERATE_EXERCISE", + "result": "golemcore", + "category": "CC_ENCHANTED", + "subcategory": "CSC_ENCHANTED_OTHER", + "skill_used": "fabrication", + "skills_required": [ "spellcraft", 6 ], + "difficulty": 5, + "time": "150 m", + "book_learn": [ [ "metal_legends", 6 ] ], + "qualities": [ + { "id": "CHISEL", "level": 2 }, + { "id": "MANA_INFUSE", "level": 2 }, + { "id": "HAMMER_FINE", "level": 1 }, + { "id": "FILE", "level": 2 }, + { "id": "ANVIL", "level": 1 } + ], + "tools": [ [ [ "demon_forge", 30 ] ], [ [ "tongs", -1 ] ] ], + "proficiencies": [ + { "proficiency": "prof_almetallurgy", "required": true }, + { "proficiency": "prof_golemancy_basic", "required": false, "time_multiplier": 1.5, "fail_multiplier": 2 } + ], + "components": [ [ [ "orichalcum_ingot", 4 ] ], [ [ "mercury", 4 ] ], [ [ "crystallized_mana", 100 ] ] ] } ] diff --git a/data/mods/Magiclysm/recipes/dragon_black.json b/data/mods/Magiclysm/recipes/dragon_black.json index fcb7b60c4f22c..e38b36bcaaf30 100644 --- a/data/mods/Magiclysm/recipes/dragon_black.json +++ b/data/mods/Magiclysm/recipes/dragon_black.json @@ -19,14 +19,47 @@ { "id": "MANA_INFUSE", "level": 1 } ], "tools": [ [ [ "surface_heat", 20, "LIST" ] ] ], - "proficiencies": [ { "proficiency": "prof_leatherworking_basic" } ], + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" }, + { "proficiency": "prof_alchemy" } + ], "book_learn": [ [ "black_dragons", 2 ] ], "components": [ [ [ "dragon_essence", 1 ] ], [ [ "black_dragon_hide_raw", 1 ] ] ] }, + { + "result": "bundle_black_scale", + "type": "recipe", + "activity_level": "NO_EXERCISE", + "category": "CC_ENCHANTED", + "subcategory": "CSC_ENCHANTED_MATERIALS", + "skill_used": "fabrication", + "time": "1 m", + "autolearn": true, + "//": "volume of black dragon hide is leather x8 so it neeeds x8 filament", + "using": [ [ "filament", 64 ] ], + "qualities": [ { "id": "SEW", "level": 2 } ], + "components": [ [ [ "dragon_black_scale", 10 ] ], [ [ "rag", 3 ] ] ] + }, + { + "result": "bundle_black_hide", + "type": "recipe", + "activity_level": "NO_EXERCISE", + "category": "CC_ENCHANTED", + "subcategory": "CSC_ENCHANTED_MATERIALS", + "skill_used": "fabrication", + "time": "1 m", + "autolearn": true, + "using": [ [ "filament", 8 ] ], + "qualities": [ { "id": "SEW", "level": 1 } ], + "components": [ [ [ "black_dragon_tanned_hide", 10 ] ] ], + "flags": [ "BLIND_HARD" ] + }, { "result": "suit_black_dragon_hide", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "tailor", @@ -34,51 +67,61 @@ "skills_required": [ "spellcraft", 3 ], "time": "70 h", "book_learn": [ [ "black_dragons", 5 ] ], - "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 3 } ], + "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 1 } ], "using": [ [ "sewing_standard", 250 ] ], - "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_leatherworking" } ], - "components": [ [ [ "black_dragon_tanned_hide", 40 ] ], [ [ "dragon_black_scale", 150 ] ], [ [ "dragon_essence", 15 ] ] ] + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" } + ], + "components": [ [ [ "black_dragon_tanned_hide", 22 ] ], [ [ "dragon_essence", 11 ] ] ] }, { "result": "suit_black_dragon_scale", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "fabrication", "difficulty": 7, "skills_required": [ "spellcraft", 5 ], - "time": "340 h", + "time": "290 h", "book_learn": [ [ "black_dragons", 6 ] ], "qualities": [ { "id": "MANA_INFUSE", "level": 2 }, { "id": "CHISEL", "level": 3 } ], - "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_leatherworking" } ], + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" }, + { "proficiency": "prof_scaleworking_dragon" } + ], "using": [ [ "sewing_standard", 250 ], [ "welding_standard", 50 ] ], - "components": [ [ [ "black_dragon_tanned_hide", 40 ] ], [ [ "dragon_black_scale", 1500 ] ], [ [ "dragon_essence", 15 ] ] ] + "components": [ [ [ "black_dragon_tanned_hide", 22 ] ], [ [ "dragon_black_scale", 1100 ] ], [ [ "dragon_essence", 11 ] ] ] }, { "result": "boots_black_dragon_hide", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "tailor", "difficulty": 5, "skills_required": [ "spellcraft", 3 ], - "time": "30 h", + "time": "25 h", "book_learn": [ [ "black_dragons", 5 ] ], - "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 3 } ], + "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 1 } ], "using": [ [ "sewing_standard", 100 ] ], "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_cobbling" }, - { "proficiency": "prof_leatherworking" } + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" } ], - "components": [ [ [ "black_dragon_tanned_hide", 4 ] ], [ [ "dragon_black_scale", 15 ] ], [ [ "dragon_essence", 2 ] ] ] + "components": [ [ [ "black_dragon_tanned_hide", 4 ] ], [ [ "dragon_essence", 2 ] ] ] }, { "result": "boots_black_dragon_scale", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "fabrication", @@ -91,30 +134,36 @@ "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_cobbling" }, - { "proficiency": "prof_leatherworking" } + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" }, + { "proficiency": "prof_scaleworking_dragon" } ], "components": [ [ [ "black_dragon_tanned_hide", 6 ] ], [ [ "dragon_black_scale", 75 ] ], [ [ "dragon_essence", 4 ] ] ] }, { "result": "helmet_black_dragon_hide", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "tailor", "difficulty": 5, "skills_required": [ "spellcraft", 3 ], - "time": "30 h", + "time": "25 h", "book_learn": [ [ "black_dragons", 5 ] ], - "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 3 } ], + "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 1 } ], "using": [ [ "sewing_standard", 100 ] ], - "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_leatherworking" } ], - "components": [ [ [ "black_dragon_tanned_hide", 3 ] ], [ [ "dragon_black_scale", 8 ] ], [ [ "dragon_essence", 2 ] ] ] + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" } + ], + "components": [ [ [ "black_dragon_tanned_hide", 3 ] ], [ [ "dragon_essence", 2 ] ] ] }, { "result": "helmet_black_dragon_scale", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "fabrication", @@ -124,13 +173,18 @@ "book_learn": [ [ "black_dragons", 6 ] ], "qualities": [ { "id": "MANA_INFUSE", "level": 2 }, { "id": "CHISEL", "level": 3 } ], "using": [ [ "sewing_standard", 100 ], [ "welding_standard", 10 ] ], - "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_leatherworking" } ], + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" }, + { "proficiency": "prof_scaleworking_dragon" } + ], "components": [ [ [ "black_dragon_tanned_hide", 1 ] ], [ [ "dragon_black_scale", 90 ] ], [ [ "dragon_essence", 4 ] ] ] }, { "result": "gloves_black_dragon_hide", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "tailor", @@ -138,55 +192,62 @@ "skills_required": [ "spellcraft", 3 ], "time": "30 h", "book_learn": [ [ "black_dragons", 5 ] ], - "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 3 } ], + "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 1 } ], "using": [ [ "sewing_standard", 100 ] ], "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_articulation", "required": false, "time_multiplier": 2 }, - { "proficiency": "prof_leatherworking" } + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" } ], - "components": [ [ [ "black_dragon_tanned_hide", 3 ] ], [ [ "dragon_black_scale", 10 ] ], [ [ "dragon_essence", 3 ] ] ] + "components": [ [ [ "black_dragon_tanned_hide", 3 ] ], [ [ "dragon_essence", 3 ] ] ] }, { "result": "gauntlets_black_dragon_scale", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "fabrication", "difficulty": 7, "skills_required": [ "spellcraft", 5 ], - "time": "30 h", + "time": "40 h", "book_learn": [ [ "black_dragons", 6 ] ], "qualities": [ { "id": "MANA_INFUSE", "level": 2 }, { "id": "CHISEL", "level": 3 } ], "using": [ [ "sewing_standard", 100 ], [ "welding_standard", 10 ] ], "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_articulation", "required": false, "time_multiplier": 2 }, - { "proficiency": "prof_leatherworking" } + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" }, + { "proficiency": "prof_scaleworking_dragon" } ], "components": [ [ [ "black_dragon_tanned_hide", 4 ] ], [ [ "dragon_black_scale", 50 ] ], [ [ "dragon_essence", 5 ] ] ] }, { "result": "suit_xlblack_dragon_hide", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "tailor", "difficulty": 6, "skills_required": [ "spellcraft", 4 ], - "time": "70 h", + "time": "60 h", "book_learn": [ [ "black_dragons", 5 ] ], - "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 3 } ], + "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 1 } ], "using": [ [ "sewing_standard", 350 ] ], - "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_leatherworking" } ], - "components": [ [ [ "black_dragon_tanned_hide", 60 ] ], [ [ "dragon_black_scale", 250 ] ], [ [ "dragon_essence", 20 ] ] ] + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" } + ], + "components": [ [ [ "black_dragon_tanned_hide", 35 ] ], [ [ "dragon_essence", 18 ] ] ] }, { "result": "suit_xlblack_dragon_scale", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "fabrication", @@ -196,118 +257,138 @@ "book_learn": [ [ "black_dragons", 6 ] ], "qualities": [ { "id": "MANA_INFUSE", "level": 2 }, { "id": "CHISEL", "level": 3 } ], "using": [ [ "sewing_standard", 350 ], [ "welding_standard", 100 ] ], - "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_leatherworking" } ], - "components": [ [ [ "black_dragon_tanned_hide", 60 ] ], [ [ "dragon_black_scale", 2250 ] ], [ [ "dragon_essence", 25 ] ] ] + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" }, + { "proficiency": "prof_scaleworking_dragon" } + ], + "components": [ [ [ "black_dragon_tanned_hide", 35 ] ], [ [ "dragon_black_scale", 1800 ] ], [ [ "dragon_essence", 20 ] ] ] }, { "result": "boots_xlblack_dragon_hide", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "tailor", "difficulty": 6, "skills_required": [ "spellcraft", 4 ], - "time": "30 h", + "time": "32 h", "book_learn": [ [ "black_dragons", 5 ] ], - "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 3 } ], + "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 1 } ], "using": [ [ "sewing_standard", 150 ] ], "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_cobbling" }, - { "proficiency": "prof_leatherworking" } + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" } ], - "components": [ [ [ "black_dragon_tanned_hide", 6 ] ], [ [ "dragon_black_scale", 20 ] ], [ [ "dragon_essence", 3 ] ] ] + "components": [ [ [ "black_dragon_tanned_hide", 6 ] ], [ [ "dragon_essence", 3 ] ] ] }, { "result": "boots_xlblack_dragon_scale", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "fabrication", "difficulty": 8, "skills_required": [ "spellcraft", 6 ], - "time": "30 h", + "time": "40 h", "book_learn": [ [ "black_dragons", 6 ] ], "qualities": [ { "id": "MANA_INFUSE", "level": 2 }, { "id": "CHISEL", "level": 3 } ], "using": [ [ "sewing_standard", 150 ], [ "welding_standard", 20 ] ], "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_cobbling" }, - { "proficiency": "prof_leatherworking" } + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" }, + { "proficiency": "prof_scaleworking_dragon" } ], "components": [ [ [ "black_dragon_tanned_hide", 8 ] ], [ [ "dragon_black_scale", 125 ] ], [ [ "dragon_essence", 6 ] ] ] }, { "result": "helmet_xlblack_dragon_hide", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "tailor", "difficulty": 6, "skills_required": [ "spellcraft", 4 ], - "time": "30 h", + "time": "32 h", "book_learn": [ [ "black_dragons", 5 ] ], - "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 3 } ], + "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 1 } ], "using": [ [ "sewing_standard", 150 ] ], - "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_leatherworking" } ], - "components": [ [ [ "black_dragon_tanned_hide", 4 ] ], [ [ "dragon_black_scale", 12 ] ], [ [ "dragon_essence", 3 ] ] ] + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" } + ], + "components": [ [ [ "black_dragon_tanned_hide", 4 ] ], [ [ "dragon_essence", 3 ] ] ] }, { "result": "helmet_xlblack_dragon_scale", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "fabrication", "difficulty": 8, "skills_required": [ "spellcraft", 6 ], - "time": "30 h", + "time": "40 h", "book_learn": [ [ "black_dragons", 6 ] ], "qualities": [ { "id": "MANA_INFUSE", "level": 2 }, { "id": "CHISEL", "level": 3 } ], "using": [ [ "sewing_standard", 150 ], [ "welding_standard", 20 ] ], - "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_leatherworking" } ], + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" }, + { "proficiency": "prof_scaleworking_dragon" } + ], "components": [ [ [ "black_dragon_tanned_hide", 2 ] ], [ [ "dragon_black_scale", 140 ] ], [ [ "dragon_essence", 6 ] ] ] }, { "result": "gloves_xlblack_dragon_hide", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "tailor", "difficulty": 6, "skills_required": [ "spellcraft", 4 ], - "time": "30 h", + "time": "40 h", "book_learn": [ [ "black_dragons", 5 ] ], - "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 3 } ], + "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 1 } ], "using": [ [ "sewing_standard", 150 ] ], "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_articulation", "required": false, "time_multiplier": 2 }, - { "proficiency": "prof_leatherworking" } + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" } ], - "components": [ [ [ "black_dragon_tanned_hide", 4 ] ], [ [ "dragon_black_scale", 15 ] ], [ [ "dragon_essence", 4 ] ] ] + "components": [ [ [ "black_dragon_tanned_hide", 4 ] ], [ [ "dragon_essence", 4 ] ] ] }, { "result": "gauntlets_xlblack_dragon_scale", "type": "recipe", - "activity_level": "fake", + "activity_level": "LIGHT_EXERCISE", "category": "CC_ENCHANTED", "subcategory": "CSC_ENCHANTED_ARMOR", "skill_used": "fabrication", "difficulty": 8, "skills_required": [ "spellcraft", 6 ], - "time": "30 h", + "time": "50 h", "book_learn": [ [ "black_dragons", 6 ] ], "qualities": [ { "id": "MANA_INFUSE", "level": 2 }, { "id": "CHISEL", "level": 3 } ], "using": [ [ "sewing_standard", 150 ], [ "welding_standard", 20 ] ], "proficiencies": [ { "proficiency": "prof_leatherworking_basic" }, { "proficiency": "prof_articulation", "required": false, "time_multiplier": 2 }, - { "proficiency": "prof_leatherworking" } + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" }, + { "proficiency": "prof_scaleworking_dragon" } ], "components": [ [ [ "black_dragon_tanned_hide", 6 ] ], [ [ "dragon_black_scale", 75 ] ], [ [ "dragon_essence", 7 ] ] ] }, @@ -340,5 +421,49 @@ "qualities": [ { "id": "CHEM", "level": 1 }, { "id": "CONCENTRATE", "level": 1 }, { "id": "MANA_INFUSE", "level": 1 } ], "components": [ [ [ "dragon_essence", 5 ] ], [ [ "mutagen_black_dragon", 1 ] ], [ [ "manatouched_serum", 1 ] ] ], "flags": [ "SECRET" ] + }, + { + "result": "backpack_black_dragon_hide", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "category": "CC_ENCHANTED", + "subcategory": "CSC_ENCHANTED_ARMOR", + "skill_used": "tailor", + "difficulty": 5, + "skills_required": [ [ "spellcraft", 2 ], [ "fabrication", 3 ] ], + "time": "10 h", + "book_learn": [ [ "black_dragons", 4 ] ], + "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 1 } ], + "using": [ [ "sewing_standard", 120 ], [ "fastener_large", 2 ] ], + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" }, + { "proficiency": "prof_closures" }, + { "proficiency": "prof_closures_waterproofing" } + ], + "components": [ [ [ "black_dragon_tanned_hide", 10 ] ], [ [ "dragon_essence", 3 ] ], [ [ "plastic_sheet", 1 ] ] ] + }, + { + "result": "backpack_xl_black_dragon_hide", + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "category": "CC_ENCHANTED", + "subcategory": "CSC_ENCHANTED_ARMOR", + "skill_used": "tailor", + "difficulty": 5, + "skills_required": [ [ "spellcraft", 2 ], [ "fabrication", 3 ] ], + "time": "13 h", + "book_learn": [ [ "black_dragons", 4 ] ], + "qualities": [ { "id": "MANA_INFUSE", "level": 1 }, { "id": "CHISEL", "level": 1 } ], + "using": [ [ "sewing_standard", 130 ], [ "fastener_large", 2 ] ], + "proficiencies": [ + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" }, + { "proficiency": "prof_leatherworking_dragon" }, + { "proficiency": "prof_closures" }, + { "proficiency": "prof_closures_waterproofing" } + ], + "components": [ [ [ "black_dragon_tanned_hide", 14 ] ], [ [ "dragon_essence", 4 ] ], [ [ "plastic_sheet", 1 ] ] ] } ] diff --git a/data/mods/Magiclysm/snippets/survivor_notes.json b/data/mods/Magiclysm/snippets/survivor_notes.json new file mode 100644 index 0000000000000..cd03a44abc5b0 --- /dev/null +++ b/data/mods/Magiclysm/snippets/survivor_notes.json @@ -0,0 +1,12 @@ +[ + { + "type": "snippet", + "category": "note", + "text": [ + { + "id": "magiclysm_note_1", + "text": "\"(Spell Name) RECIPE V3\n---\nRare Ingredients:\n- 17 drams owl's blood - less no effect, more turns bear into large owl\n- 1 gastrolith, large american alligator\n- 3 teeth or claws from a honey badger\n- 8 pounds bear meat, preferably polar bear, grizzly will suffice\nBring a large pot (meat must fully submerge) filled about half way with potion starter fluid (high grade) to a boil. Add gastrolith and teeth/claws after boiling, add the blood, stir counterclockwise for a minute while chanting standard polymorph spell. Continue until concoction has a slight glow. Add the bear meat, remove from heat, cover for 24 hours.\nBear MUST eat all 8 pounds of the meat (claws and gastrolith optional).\n---\n*I've finally gotten my hands on enough polar bear meat. The grizzly variations are a bit lackluster.*\nThere are flecks of blood on the page.\"" + } + ] + } +] diff --git a/data/mods/Magiclysm/terrain.json b/data/mods/Magiclysm/terrain.json index bdeb5e28bc143..9440798c062d0 100644 --- a/data/mods/Magiclysm/terrain.json +++ b/data/mods/Magiclysm/terrain.json @@ -102,10 +102,55 @@ "sound_fail": "whack!", "sound_vol": 16, "sound_fail_vol": 10, - "ter_set": "t_thconc_floor", + "ter_set": "t_magiconc_floor", "items": [ { "item": "glass_shard", "count": [ 8, 16 ] } ] } }, + { + "type": "terrain", + "id": "t_magiconc_wall", + "name": "magically reinforced concrete wall", + "looks_like": "t_concrete_wall", + "description": "An extremely resilient wall, filled with concrete and rebar and esoteric ingredients. Best suited for supporting multi-level buildings, only serious explosives and high-speed impacts would be capable of damaging this wall.", + "symbol": "LINE_OXOX", + "color": "light_gray", + "move_cost": 0, + "coverage": 100, + "roof": "t_flat_roof", + "flags": [ "NOITEM", "SUPPORTS_ROOF", "WALL", "NO_SCENT", "AUTO_WALL_SYMBOL", "BLOCK_WIND" ], + "connects_to": "WALL", + "bash": { + "str_min": 240, + "str_max": 560, + "sound": "scrrrash!", + "sound_fail": "whump!", + "ter_set": "t_reb_cage", + "items": [ { "item": "rock", "count": [ 10, 22 ] } ] + } + }, + { + "type": "terrain", + "id": "t_magiconc_floor", + "name": "magically reinforced concrete floor", + "description": "Extremely resilient floor made from carefully placed rebar and poured alchemical 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.", + "symbol": ".", + "color": "cyan", + "move_cost": 2, + "roof": "t_flat_roof", + "flags": [ "TRANSPARENT", "SUPPORTS_ROOF", "COLLAPSES", "INDOORS", "FLAT", "ROAD" ], + "bash": { + "sound": "SMASH!", + "ter_set": "t_null", + "str_min": 350, + "str_max": 600, + "str_min_supported": 200, + "items": [ + { "item": "rock", "count": [ 10, 22 ] }, + { "item": "scrap", "count": [ 10, 12 ] }, + { "item": "rebar", "count": [ 0, 4 ] } + ] + } + }, { "type": "terrain", "id": "t_vault_vent", @@ -119,7 +164,7 @@ "flags": [ "TRANSPARENT", "SUPPORTS_ROOF", "COLLAPSES", "INDOORS", "FLAT", "ROAD" ], "bash": { "sound": "SMASH!", - "ter_set": "t_null", + "ter_set": "t_magiconc_floor", "str_min": 150, "str_max": 400, "str_min_supported": 200, diff --git a/data/mods/Magiclysm/tool_qualities.json b/data/mods/Magiclysm/tool_qualities.json index 1c48775418cd3..c20c78040f9a5 100644 --- a/data/mods/Magiclysm/tool_qualities.json +++ b/data/mods/Magiclysm/tool_qualities.json @@ -3,5 +3,10 @@ "type": "tool_quality", "id": "MAGIC_MUTAGEN", "name": "magic mutagen mixer" + }, + { + "type": "tool_quality", + "id": "MAGIC_CAULDRON", + "name": "alchemical cauldron" } ] diff --git a/data/mods/Magiclysm/traits/attunements.json b/data/mods/Magiclysm/traits/attunements.json index 87066d1ac2589..639f6d11c3b8c 100644 --- a/data/mods/Magiclysm/traits/attunements.json +++ b/data/mods/Magiclysm/traits/attunements.json @@ -503,7 +503,7 @@ "valid": false, "description": "The Golemancer is the ancient myth of the clay golem, made reality from constant use of Animist summons and Earthshaper self-strengthening spells. Takes some traits from the golems they create, such as immunity to slowing effects.", "prereqs": [ "ANIMIST", "EARTHSHAPER" ], - "spells_learned": [ [ "golem_push", 5 ] ], + "spells_learned": [ [ "golem_push", 5 ], [ "summon_golem_clay", 5 ] ], "cancels": [ "ARTIFICER", "AURA_MAGE", diff --git a/data/mods/Magiclysm/worldgen/forge_of_wonders.json b/data/mods/Magiclysm/worldgen/forge_of_wonders.json index e5681b712e32c..0d71c7c715736 100644 --- a/data/mods/Magiclysm/worldgen/forge_of_wonders.json +++ b/data/mods/Magiclysm/worldgen/forge_of_wonders.json @@ -23,25 +23,25 @@ "rows": [ " T 2 ++++++ 2 ", " T +++++++ Y ", - " Y ++++++++ T ", + " Y 55555555 T ", " +++++++ ", " ########||||#########################||&&&&&&&||###############||||##############||||############### ", " #..................................##...........##............................4............4.......# ", - " #..4.@.@...........................##...........##..................XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.# ", - " #...fffff.................2........##...........##..................X$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.# ", - " #...fffff..........................##...........##..................#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.# ", - " #....@.@.........................................................XXX#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.| ", - " #........................................44......................X94#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.| ", - " #............4...........................44......................X89#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.| ", - " #................................................................X94#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.| ", - " #...........@.@..................................................XXX#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.| ", - " #.........zZZZZZ....................................................#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.# ", + " #..4.@.@...........................##...........##............5.....XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.# ", + " #...fffff.................2........##...........##.............5....X$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.# ", + " #...fffff..........................##...........##............5.....#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.# ", + " #....@.@.......................................................5.XXX#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.| ", + " #........................................44...................5..X94#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.| ", + " #............4...........................44....................5.X89#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.| ", + " #.............................................................5..X94#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.| ", + " #...........@.@................................................5.XXX#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.| ", + " #.........zZZZZZ..............................................5.....#$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.# ", " #..........ZZZZZz...................................##...........##.X$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.# ", " |...........@.@............4...................2....##...........##.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.# ", " |...................................................##...........##.....4...............4..........# ", " |...................................#####|||##########||&&&&&&&||##################|||############## ", " |...................................# +++++++ ", - " |...................................# ++++++++ 2 ", + " |...................................# 55555555 2 ", " #...................................# 2 +++++++ ", " #.......................4...........# ++++++++ 2 ", " #...................................# ++++++++ Y ", @@ -90,7 +90,7 @@ "terrain": { "w": "t_water_sh", "l": "t_lava", - "#": "t_strconc_wall", + "#": "t_magiconc_wall", "t": "t_ov_reb_cage", "W": "t_water_dp", "3": "t_water_moving_sh_underground", @@ -99,21 +99,22 @@ "&": "t_ballistic_door_glass_c", "|": "t_window_stained_green", "X": "t_wall_glassteel_alarm", - ".": "t_strconc_floor", - "h": "t_strconc_floor", - "!": "t_strconc_floor", - "~": "t_strconc_floor", - "1": "t_strconc_floor", - "z": "t_strconc_floor", - "Z": "t_strconc_floor", - "$": [ "t_strconc_floor", "t_vault_vent" ], - "f": "t_strconc_floor", - "F": "t_strconc_floor", - "8": "t_strconc_floor", + ".": "t_magiconc_floor", + "h": "t_magiconc_floor", + "!": "t_magiconc_floor", + "~": "t_magiconc_floor", + "1": "t_magiconc_floor", + "z": "t_magiconc_floor", + "Z": "t_magiconc_floor", + "$": [ "t_magiconc_floor", "t_vault_vent" ], + "f": "t_magiconc_floor", + "F": "t_magiconc_floor", + "8": "t_magiconc_floor", "9": "t_vault_vent", - "2": "t_strconc_floor", + "2": "t_magiconc_floor", "4": "t_thconc_floor_echandelier", - "+": "t_railroad_rubble" + "+": "t_railroad_rubble", + "5": "t_bollard" }, "furniture": { "h": "f_demon_forge", diff --git a/data/mods/My_Sweet_Cataclysm/sweet_items.json b/data/mods/My_Sweet_Cataclysm/sweet_items.json index 274223cca9368..78a9f83bee3df 100644 --- a/data/mods/My_Sweet_Cataclysm/sweet_items.json +++ b/data/mods/My_Sweet_Cataclysm/sweet_items.json @@ -17,7 +17,7 @@ "id": "ruined_candy", "//": "This item should not get any use whatsoever, it is merely conservation.", "symbol": "¨", - "name": { "str_sp": "peices of candy wrapper" }, + "name": { "str_sp": "pieces of candy wrapper" }, "charges": 10, "volume": "1000 ml", "weight": "100 g", @@ -28,6 +28,23 @@ "calories": 10, "vitamins": [ ] }, + { + "type": "COMESTIBLE", + "comestible_type": "FOOD", + "id": "feces_candy", + "symbol": "^", + "name": { "str_sp": "sticky sludge" }, + "charges": 10, + "volume": "1000 ml", + "weight": "100 g", + "color": "white", + "looks_like": "ruined_candy", + "description": "A pile of weird sticky sludge. It looks absolutly disgusting but smells kind of sweet.", + "material": [ "junk" ], + "calories": 10, + "fun": -12, + "vitamins": [ ] + }, { "id": "rock_candy_chunk", "type": "TOOL", @@ -42,5 +59,15 @@ "symbol": ",", "color": "light_gray", "flags": [ "NO_SALVAGE" ] + }, + { + "type": "COMESTIBLE", + "id": "sugar", + "name": { "str_sp": "sugar" }, + "copy-from": "sugar", + "color": "white", + "symbol": "%", + "description": "Sweet, sweet sugar. Bad for your teeth and surprisingly not very tasty on its own.", + "material": [ "junk" ] } ] diff --git a/data/mods/My_Sweet_Cataclysm/sweet_monsters.json b/data/mods/My_Sweet_Cataclysm/sweet_monsters.json index 81894d4a8f671..5d5e1d9266c71 100644 --- a/data/mods/My_Sweet_Cataclysm/sweet_monsters.json +++ b/data/mods/My_Sweet_Cataclysm/sweet_monsters.json @@ -491,6 +491,7 @@ "description": "The domestic cow, a baleful, ruminating farm animal. It is quite muscular, and the males can have a violent streak to accompany their nasty-looking horns. This one looks to be made entirely of chocolate.", "copy-from": "mon_cow_calf", "harvest": "choc_cows", + "biosignature": { "biosig_item": "feces_candy", "biosig_timer": "1 d" }, "upgrades": { "age_grow": 180, "into": "mon_cow_choc" } }, { @@ -501,6 +502,7 @@ "copy-from": "mon_cow", "harvest": "choc_cows", "starting_ammo": { "milk_raw_choc": 40 }, + "biosignature": { "biosig_item": "feces_candy", "biosig_timer": "1 d" }, "reproduction": { "baby_monster": "mon_cow_calf_choc", "baby_count": 1, "baby_timer": 343 } }, { diff --git a/data/mods/National_Guard_Camp/item_groups.json b/data/mods/National_Guard_Camp/item_groups.json index 71b4f9af03aa9..0ee98e5838696 100644 --- a/data/mods/National_Guard_Camp/item_groups.json +++ b/data/mods/National_Guard_Camp/item_groups.json @@ -152,7 +152,13 @@ { "group": "robots", "prob": 100 }, { "item": "storage_battery", "prob": 40, "charges-min": 200, "charges-max": 1000 }, { "item": "223", "prob": 20, "charges-min": 10, "charges-max": 30 }, - { "item": "scar_l", "prob": 20, "damage": [ 1, 2 ], "contents-item": [ "stanag30" ] } + { + "item": "nato_assault_rifle", + "variant": "scar_l", + "prob": 20, + "damage": [ 1, 2 ], + "contents-item": [ "stanag30" ] + } ] } ] diff --git a/data/mods/TEST_DATA/enchantments.json b/data/mods/TEST_DATA/enchantments.json index 150617c6df896..fa9c4ceb3b020 100644 --- a/data/mods/TEST_DATA/enchantments.json +++ b/data/mods/TEST_DATA/enchantments.json @@ -10,7 +10,7 @@ "id": "generic_blinding_spray_1", "hit_self": false, "message": "Your ink glands spray some ink into %2$s's eyes.", - "npc_message": "%1$s's ink glands spay some ink into %2$s's eyes." + "npc_message": "%1$s's ink glands spray some ink into %2$s's eyes." } ], "ench_effects": [ { "effect": "invisibility", "intensity": 1 } ] diff --git a/data/mods/TEST_DATA/items.json b/data/mods/TEST_DATA/items.json index e3b6be5073653..7e21a5bbb649c 100644 --- a/data/mods/TEST_DATA/items.json +++ b/data/mods/TEST_DATA/items.json @@ -119,7 +119,7 @@ "min_item_volume": "50 ml", "max_item_length": "70 cm", "max_contains_volume": "1500 ml", - "max_contains_weight": "1000 g", + "max_contains_weight": "1200 g", "moves": 50, "flag_restriction": [ "BELT_CLIP", "SHEATH_KNIFE" ] }, @@ -128,7 +128,7 @@ "min_item_volume": "50 ml", "max_item_length": "70 cm", "max_contains_volume": "1500 ml", - "max_contains_weight": "1000 g", + "max_contains_weight": "1200 g", "moves": 50, "flag_restriction": [ "BELT_CLIP", "SHEATH_KNIFE" ] }, @@ -137,7 +137,7 @@ "min_item_volume": "50 ml", "max_item_length": "70 cm", "max_contains_volume": "1500 ml", - "max_contains_weight": "1000 g", + "max_contains_weight": "1200 g", "moves": 50, "flag_restriction": [ "BELT_CLIP", "SHEATH_KNIFE" ] }, @@ -146,7 +146,7 @@ "min_item_volume": "50 ml", "max_item_length": "70 cm", "max_contains_volume": "1500 ml", - "max_contains_weight": "1000 g", + "max_contains_weight": "1200 g", "moves": 50, "flag_restriction": [ "BELT_CLIP", "SHEATH_KNIFE" ] } @@ -1341,7 +1341,7 @@ "charges": 7, "flags": [ "EATEN_COLD" ], "fun": -5, - "freezing_point": 20 + "freezing_point": -7 }, { "id": "test_nuclear_carafe", @@ -1448,8 +1448,23 @@ "power_armor": true, "material_thickness": 14, "environmental_protection": 16, + "use_action": { "type": "transform", "msg": "The %s engages.", "target": "test_power_armor_on", "active": true }, "flags": [ "WATERPROOF", "STURDY", "ELECTRIC_IMMUNE" ] }, + { + "id": "test_power_armor_on", + "copy-from": "test_power_armor", + "repairs_like": "test_power_armor", + "looks_like": "test_power_armor", + "type": "TOOL_ARMOR", + "name": { "str": "test power armor (on)" }, + "description": "This is a prototype power armor just for testing.", + "flags": [ "USE_UPS", "WATERPROOF", "STURDY", "ELECTRIC_IMMUNE", "TRADER_AVOID", "CLIMATE_CONTROL" ], + "power_draw": 4000000, + "revert_to": "test_power_armor", + "use_action": { "type": "transform", "menu_text": "Turn off", "msg": "The %s armor disengages.", "target": "test_power_armor" }, + "covers": [ "torso", "arm_l", "arm_r", "hand_l", "hand_r", "leg_l", "leg_r", "foot_l", "foot_r" ] + }, { "id": "test_meower_armor", "type": "PET_ARMOR", diff --git a/data/mods/aftershock_exoplanet/Map/overmap_terrain/ice_fields.json b/data/mods/aftershock_exoplanet/Map/overmap_terrain/ice_fields.json new file mode 100644 index 0000000000000..f470565670d0a --- /dev/null +++ b/data/mods/aftershock_exoplanet/Map/overmap_terrain/ice_fields.json @@ -0,0 +1,52 @@ +[ + { + "type": "overmap_terrain", + "id": "field", + "copy-from": "generic_open_land", + "name": "Ice Fields", + "sym": ".", + "color": "light_gray", + "see_cost": 2, + "extras": "field", + "mapgen": [ { "method": "builtin", "name": "field" } ], + "flags": [ "NO_ROTATE" ] + }, + { + "type": "overmap_terrain", + "id": "forest", + "copy-from": "generic_forest", + "name": "hydrothermal flats", + "sym": "%", + "color": "light_red", + "see_cost": 3, + "extras": "forest", + "spawns": { "group": "GROUP_FOREST", "population": [ 0, 3 ], "chance": 13 }, + "flags": [ "NO_ROTATE", "SOURCE_FORAGE" ] + }, + { + "type": "overmap_terrain", + "id": "forest_thick", + "copy-from": "generic_forest", + "name": "hydrothermal flats", + "sym": "%", + "color": "light_red", + "see_cost": 4, + "extras": "forest_thick", + "spawns": { "group": "GROUP_FOREST", "population": [ 0, 6 ], "chance": 15 }, + "mapgen": [ { "method": "builtin", "name": "forest" } ], + "flags": [ "NO_ROTATE", "SOURCE_FORAGE" ] + }, + { + "type": "overmap_terrain", + "id": "forest_water", + "copy-from": "generic_wetland_forest", + "name": "hydrothermal flats", + "sym": "%", + "color": "cyan", + "see_cost": 4, + "extras": "forest_water", + "spawns": { "group": "GROUP_SWAMP", "population": [ 1, 4 ], "chance": 16 }, + "mapgen": [ { "method": "builtin", "name": "forest" } ], + "flags": [ "NO_ROTATE", "SOURCE_FORAGE", "RISK_HIGH" ] + } +] diff --git a/data/mods/aftershock_exoplanet/game_balance.json b/data/mods/aftershock_exoplanet/game_balance.json new file mode 100644 index 0000000000000..f36efb1980153 --- /dev/null +++ b/data/mods/aftershock_exoplanet/game_balance.json @@ -0,0 +1,16 @@ +[ + { + "type": "EXTERNAL_OPTION", + "name": "GENERIC_PROFESSION_ID", + "info": "The profession selected by default in the character creator menu.", + "stype": "string_input", + "value": "afs_rating" + }, + { + "type": "EXTERNAL_OPTION", + "name": "GENERIC_SCENARIO_ID", + "info": "The scenario selected by default in the character creator menu.", + "stype": "string_input", + "value": "escape_pod" + } +] diff --git a/data/mods/aftershock_exoplanet/modinfo.json b/data/mods/aftershock_exoplanet/modinfo.json new file mode 100644 index 0000000000000..3a7a74b3761c9 --- /dev/null +++ b/data/mods/aftershock_exoplanet/modinfo.json @@ -0,0 +1,12 @@ +[ + { + "type": "MOD_INFO", + "id": "aftershock_exoplanet", + "name": "Aftershock: Exoplanet", + "authors": [ "Maleclypse", "Candlebury", "Mom_Bun" ], + "maintainers": [ "Maleclypse", "Candlebury", "Mom_Bun" ], + "description": "An experimental implementation of Aftershock's exoplanet region, not recommended for actual playthroughs. Adds no new content by itself, and requires the main mod to function correctly.", + "category": "total_conversion", + "dependencies": [ "dda", "aftershock" ] + } +] diff --git a/data/mods/aftershock_exoplanet/region_settings.json b/data/mods/aftershock_exoplanet/region_settings.json new file mode 100644 index 0000000000000..615b3cd483879 --- /dev/null +++ b/data/mods/aftershock_exoplanet/region_settings.json @@ -0,0 +1,207 @@ +[ + { + "type": "region_settings", + "id": "default", + "overmap_feature_flag_settings": { + "clear_blacklist": false, + "blacklist": [ "BLOB", "BEE", "ANT", "FUNGAL", "SLIME", "TRIFFID", "MI-GO", "LAB", "CLASSIC" ], + "clear_whitelist": false, + "whitelist": [ ] + }, + "default_oter": "field", + "default_groundcover": [ [ "t_region_groundcover", 1 ] ], + "river_scale": 0, + "city": { + "shop_radius": 30, + "shop_sigma": 80, + "park_radius": 20, + "park_sigma": 80, + "houses": { "afs_city_ruinfield": 400, "afs_formless_ruins_dynamic": 600 }, + "parks": { "afs_city_ruinfield": 100 }, + "shops": { "afs_augmentation_clinic_1": 400, "afs_astrobiology_lab": 400 } + }, + "weather": { + "base_temperature": -50.0, + "base_humidity": 80.0, + "base_pressure": 1015.0, + "base_wind": 9.0, + "base_wind_distrib_peaks": 80, + "base_wind_season_variation": 50, + "weather_types": [ "clear", "sunny", "cloudy", "afs_whiteout", "afs_thunder", "afs_lightning", "afs_flurries", "afs_snowing" ] + }, + "region_terrain_and_furniture": { + "terrain": { + "t_region_groundcover": { "t_snow": 32, "t_ice": 2, "t_deaddirt": 5 }, + "t_region_groundcover_urban": { "t_snow": 20, "t_ice": 3 }, + "t_region_groundcover_forest": { "t_snow": 410, "t_lichen": 300, "t_lichendirt": 300, "t_fuma_ice": 1 }, + "t_region_groundcover_swamp": { "t_snow": 410, "t_lichen": 300, "t_lichendirt": 300, "t_fuma_ice": 1 }, + "t_region_groundcover_barren": { "t_snow": 20, "t_ice": 3 }, + "t_region_grass": { "t_lichen": 1 }, + "t_region_soil": { "t_deaddirt": 1 }, + "t_region_shrub": { "t_lichen": 30 }, + "t_region_shrub_fruit": { "t_lichenyum": 30 }, + "t_region_shrub_decorative": { "t_lichenyum": 3, "t_lichendirt": 1 }, + "t_region_tree": { "t_tree_worm": 30, "t_tree_lichen": 15, "t_tree_xenoinfested": 10 }, + "t_region_tree_fruit": { "t_lichenyum": 30 }, + "t_region_tree_nut": { "t_tree_worm": 30, "t_tree_xenoinfested": 15 }, + "t_region_tree_evergreen": { "t_tree_xenoinfested": 32, "t_tree_xeno": 16 } + }, + "furniture": { + "f_region_flower": { "f_shrub_moss": 1 }, + "f_region_flower_decorative": { + "f_lily": 4, + "f_flower_tulip": 4, + "f_black_eyed_susan": 3, + "f_dahlia": 2, + "f_bluebell": 2, + "f_flower_spurge": 1, + "f_chicory": 1, + "f_sunflower": 1 + }, + "f_region_weed": { "f_shrub_moss": 1 }, + "f_region_water_plant": { "f_shrub_moss": 15 } + } + }, + "field_coverage": { + "percent_coverage": 0.02, + "default_ter": "t_lichen", + "other": { + "t_region_tree": 1, + "t_region_shrub": 3, + "f_region_weed": 49, + "f_region_flower": 37, + "f_boulder_small": 5, + "f_boulder_medium": 4, + "f_boulder_large": 1 + }, + "boost_chance": 0.03, + "boosted_percent_coverage": 2.5, + "boosted_other": { "t_lichen": 0.2, "t_lichendirt": 0.1 }, + "boosted_other_percent": 50.0 + }, + "overmap_lake_settings": { + "noise_threshold_lake": 10.0, + "lake_size_min": 20, + "lake_depth": -5, + "shore_extendable_overmap_terrain": [ "forest", "forest_thick", "forest_water", "field" ], + "shore_extendable_overmap_terrain_aliases": [ + { "om_terrain": "island_forest", "om_terrain_match_type": "TYPE", "alias": "forest" }, + { "om_terrain": "island_forest_thick", "om_terrain_match_type": "TYPE", "alias": "forest_thick" }, + { "om_terrain": "island_forest_water", "om_terrain_match_type": "TYPE", "alias": "forest_water" }, + { "om_terrain": "island_field", "om_terrain_match_type": "TYPE", "alias": "field" } + ] + }, + "overmap_ravine_settings": { "num_ravines": 15, "ravine_width": 3, "ravine_range": 45, "ravine_depth": -3 }, + "overmap_forest_settings": { + "noise_threshold_forest": 0.5, + "noise_threshold_forest_thick": 0.55, + "noise_threshold_swamp_adjacent_water": 0.3, + "noise_threshold_swamp_isolated": 0.6, + "river_floodplain_buffer_distance_min": 3, + "river_floodplain_buffer_distance_max": 15 + }, + "forest_mapgen_settings": { + "forest": { + "sparseness_adjacency_factor": 9, + "item_group": "forest", + "item_group_chance": 60, + "item_spawn_iterations": 1, + "clear_groundcover": false, + "groundcover": { "t_region_groundcover_forest": 1 }, + "clear_components": false, + "components": { + "trees": { "sequence": 0, "chance": 12, "clear_types": false, "types": { "t_region_tree": 128 } }, + "shrubs_and_flowers": { "sequence": 1, "chance": 10, "clear_types": false, "types": { "t_region_shrub": 100, "f_region_weed": 20 } }, + "clutter": { + "sequence": 2, + "chance": 80, + "clear_types": false, + "types": { + "f_geo_vent": 128, + "t_dirtmound": 128, + "f_boulder_small": 128, + "f_rubble_rock": 32, + "f_boulder_medium": 8, + "f_boulder_large": 1, + "t_pit": 1, + "t_pit_shallow": 1 + } + }, + "water": { "sequence": 3, "chance": 512, "clear_types": false, "types": { "t_water_sh": 1 } } + }, + "clear_terrain_furniture": false, + "terrain_furniture": { } + }, + "forest_thick": { + "sparseness_adjacency_factor": 4, + "item_group": "forest", + "item_group_chance": 60, + "item_spawn_iterations": 1, + "clear_groundcover": false, + "groundcover": { "t_region_groundcover_forest": 1 }, + "clear_components": false, + "components": { + "trees": { "sequence": 0, "chance": 5, "clear_types": false, "types": { "t_region_tree": 100 } }, + "shrubs_and_flowers": { "sequence": 1, "chance": 5, "clear_types": false, "types": { "t_region_shrub": 100, "f_region_weed": 20 } }, + "clutter": { + "sequence": 2, + "chance": 64, + "clear_types": false, + "types": { + "f_geo_vent": 14, + "t_dirtmound": 24, + "f_boulder_small": 32, + "f_rubble_rock": 32, + "f_boulder_medium": 16, + "f_boulder_large": 4, + "t_pit": 1, + "t_pit_shallow": 1 + } + }, + "water": { "sequence": 3, "chance": 512, "clear_types": false, "types": { "t_water_sh": 1 } } + }, + "clear_terrain_furniture": false, + "terrain_furniture": { } + }, + "forest_water": { + "sparseness_adjacency_factor": 2, + "item_group": "forest", + "item_group_chance": 60, + "item_spawn_iterations": 1, + "clear_groundcover": false, + "groundcover": { "t_region_groundcover_swamp": 1 }, + "clear_components": false, + "components": { + "trees": { "sequence": 1, "chance": 45, "clear_types": false, "types": { "f_geo_vent": 40 } }, + "shrubs_and_flowers": { "sequence": 1, "chance": 15, "clear_types": false, "types": { "t_region_shrub": 80, "f_region_weed": 30 } }, + "clutter": { + "sequence": 2, + "chance": 75, + "clear_types": false, + "types": { "t_trunk": 1, "f_boulder_small": 2, "f_boulder_medium": 1 } + }, + "water": { "sequence": 3, "chance": 2, "clear_types": false, "types": { "t_water_hot": 12 } } + }, + "clear_terrain_furniture": false, + "terrain_furniture": { "t_water_hot": { "chance": 2, "clear_furniture": false, "furniture": { "f_region_water_plant": 1 } } } + } + }, + "forest_trail_settings": { + "chance": 2, + "border_point_chance": 2, + "minimum_forest_size": 100, + "random_point_min": 4, + "random_point_max": 50, + "random_point_size_scalar": 100, + "trailhead_chance": 1, + "trailhead_road_distance": 6, + "trail_center_variance": 3, + "trail_width_offset_min": 1, + "trail_width_offset_max": 3, + "clear_trail_terrain": false, + "trail_terrain": { "t_deaddirt": 1 }, + "trailheads": { "trailhead_basic": 1, "trailhead_outhouse": 1, "trailhead_shack": 1 } + }, + "map_extras": { } + } +] diff --git a/data/mods/aftershock_exoplanet/scenarios.json b/data/mods/aftershock_exoplanet/scenarios.json new file mode 100644 index 0000000000000..f78814a1fb57d --- /dev/null +++ b/data/mods/aftershock_exoplanet/scenarios.json @@ -0,0 +1,13 @@ +[ + { + "type": "scenario", + "id": "evacuee", + "name": "Stranded Spacer", + "points": 1, + "description": "What was to be a routine cargo transfer ended in tragedy when, in a brief moment of chaos, your space ship was intercepted and destroyed by a StO missile. As soon as the MAW alarm flared to life, you scrambled to the nearest escape pod and barely managed reach the uncertain safety of the planet below.", + "allowed_locs": [ "sloc_escape_pod" ], + "professions": [ "afs_espatier", "afs_rating" ], + "flags": [ "LONE_START" ], + "start_name": "Escape Pod" + } +] diff --git a/data/mods/aftershock_exoplanet/setting_blacklists/location_blacklist.json b/data/mods/aftershock_exoplanet/setting_blacklists/location_blacklist.json new file mode 100644 index 0000000000000..80201b69af785 --- /dev/null +++ b/data/mods/aftershock_exoplanet/setting_blacklists/location_blacklist.json @@ -0,0 +1,89 @@ +[ + { + "//": "mostly removes areas with their own storylines, static NPCs, and the like. some will be readded with time", + "type": "overmap_special", + "id": "Necropolis", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "type": "overmap_special", + "id": "St_Johns_farm", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "type": "overmap_special", + "id": "Strangle Temple", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "type": "overmap_special", + "id": "evac_center", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "type": "overmap_special", + "id": "hub_01", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "type": "overmap_special", + "id": "Hazardous Waste Sarcophagus", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "type": "overmap_special", + "id": "Isherwood Farms", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "//": "sorry Mr. Lapin ;w;. temporary until I work on NPCs", + "type": "overmap_special", + "id": "Cabin_Lapin", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "type": "overmap_special", + "id": "Strange Cabin", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "//": "there will be 'FEMA camps' in the future, but almost even more sinister", + "type": "overmap_special", + "id": "FEMA Camp", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "type": "overmap_special", + "id": "FEMA_camp", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "type": "overmap_special", + "id": "lab_surface_big", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "type": "overmap_special", + "id": "Mass Grave", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + }, + { + "type": "overmap_special", + "id": "Mine Entrance", + "overmaps": [ ], + "occurrences": [ 0, 0 ] + } +] diff --git a/data/mods/aftershock_exoplanet/setting_blacklists/mon_blacklist.json b/data/mods/aftershock_exoplanet/setting_blacklists/mon_blacklist.json new file mode 100644 index 0000000000000..9779163f0ecd7 --- /dev/null +++ b/data/mods/aftershock_exoplanet/setting_blacklists/mon_blacklist.json @@ -0,0 +1,9 @@ +[ + { + "//": "edited from no_wildlife", + "type": "MONSTER_WHITELIST", + "mode": "EXCLUSIVE", + "categories": [ "WILDLIFE", "MUTANT" ], + "species": [ "MOXIE", "ROBOT" ] + } +] diff --git a/data/mods/aftershock_exoplanet/setting_blacklists/scenario_blacklist.json b/data/mods/aftershock_exoplanet/setting_blacklists/scenario_blacklist.json new file mode 100644 index 0000000000000..b1ffc6dd09a24 --- /dev/null +++ b/data/mods/aftershock_exoplanet/setting_blacklists/scenario_blacklist.json @@ -0,0 +1,7 @@ +[ + { + "type": "SCENARIO_BLACKLIST", + "subtype": "whitelist", + "scenarios": [ "escape_pod" ] + } +] diff --git a/data/mods/aftershock_exoplanet/weather_type.json b/data/mods/aftershock_exoplanet/weather_type.json new file mode 100644 index 0000000000000..0d8ec3d68f370 --- /dev/null +++ b/data/mods/aftershock_exoplanet/weather_type.json @@ -0,0 +1,112 @@ +[ + { + "id": "afs_flurries", + "type": "weather_type", + "name": "Flurries", + "color": "white", + "map_color": "h_white", + "sym": ".", + "ranged_penalty": 1, + "sight_penalty": 1.03, + "light_modifier": -20, + "sound_attn": 1, + "dangerous": false, + "precip": "light", + "rains": true, + "acidic": false, + "tiles_animation": "weather_snowflake", + "weather_animation": { "factor": 0.01, "color": "white", "sym": "." }, + "sound_category": "flurries", + "sun_intensity": "light", + "required_weathers": [ "cloudy" ], + "condition": { "or": [ { "is_humidity": 97 }, { "not": { "is_pressure": 1000 } } ] } + }, + { + "id": "afs_whiteout", + "type": "weather_type", + "name": "Whiteout", + "color": "i_black", + "map_color": "i_black", + "sym": "o", + "ranged_penalty": 1, + "sight_penalty": 1.03, + "light_modifier": -40, + "sound_attn": 1, + "dangerous": false, + "precip": "heavy", + "rains": true, + "acidic": false, + "tiles_animation": "weather_snowflake", + "weather_animation": { "factor": 0.01, "color": "white", "sym": "*" }, + "sound_category": "snowstorm", + "sun_intensity": "light", + "required_weathers": [ "cloudy", "afs_flurries" ], + "condition": { "and": [ "is_day", { "is_windpower": 20 } ] } + }, + { + "id": "afs_snowing", + "type": "weather_type", + "name": "Snowing", + "color": "blue", + "map_color": "h_blue", + "sym": "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_snowflake", + "weather_animation": { "factor": 0.02, "color": "white", "sym": "*" }, + "sound_category": "snow", + "sun_intensity": "light", + "required_weathers": [ "afs_flurries", "afs_whiteout" ], + "condition": { "or": [ { "is_humidity": 98 }, { "not": { "is_pressure": 993 } } ] } + }, + { + "id": "afs_thunder", + "type": "weather_type", + "name": "Thunder Storm", + "color": "dark_gray", + "map_color": "i_blue", + "sym": "%", + "ranged_penalty": 4, + "sight_penalty": 1.2, + "light_modifier": -40, + "sound_attn": 8, + "dangerous": false, + "precip": "heavy", + "rains": true, + "acidic": false, + "tiles_animation": "weather_snowflake", + "weather_animation": { "factor": 0.02, "color": "white", "sym": "*" }, + "sound_category": "snowstorm", + "sun_intensity": "none", + "required_weathers": [ "afs_snowing" ], + "condition": { "not": { "is_pressure": 990 } } + }, + { + "id": "afs_lightning", + "type": "weather_type", + "name": "Lightning Storm", + "color": "yellow", + "map_color": "h_yellow", + "sym": "%", + "ranged_penalty": 4, + "sight_penalty": 1.25, + "light_modifier": -45, + "sound_attn": 8, + "dangerous": false, + "precip": "heavy", + "rains": true, + "acidic": false, + "tiles_animation": "weather_snowflake", + "weather_animation": { "factor": 0.04, "color": "white", "sym": "*" }, + "sound_category": "snowstorm", + "sun_intensity": "none", + "required_weathers": [ "afs_thunder" ], + "condition": { "not": { "is_pressure": 980 } } + } +] diff --git a/data/mods/alt_map_key/overmap_terrain.json b/data/mods/alt_map_key/overmap_terrain.json index b26ad0c3a104c..ab563d2b07b57 100644 --- a/data/mods/alt_map_key/overmap_terrain.json +++ b/data/mods/alt_map_key/overmap_terrain.json @@ -1852,11 +1852,18 @@ }, { "type": "overmap_terrain", - "id": "mine_shaft", - "copy-from": "mine_shaft", - "name": "mine shaft", - "sym": "m", - "color": "i_black" + "id": "mine_entrance_roof", + "copy-from": "mine_entrance_roof" + }, + { + "type": "overmap_terrain", + "id": "mine_shaft_middle", + "copy-from": "mine_shaft_middle" + }, + { + "type": "overmap_terrain", + "id": "mine_shaft_lower", + "copy-from": "mine_shaft_lower" }, { "type": "overmap_terrain", @@ -1882,22 +1889,6 @@ "sym": "M", "color": "i_black" }, - { - "type": "overmap_terrain", - "id": "spiral_hub", - "copy-from": "spiral_hub", - "name": "spiral cavern", - "sym": "@", - "color": "pink" - }, - { - "type": "overmap_terrain", - "id": "spiral", - "copy-from": "spiral", - "name": "spiral cavern", - "sym": "@", - "color": "pink" - }, { "type": "overmap_terrain", "id": "radio_tower", diff --git a/data/mods/classic_zombies/exclusions.json b/data/mods/classic_zombies/exclusions.json index 47094a9709eef..f20e2cc360baa 100644 --- a/data/mods/classic_zombies/exclusions.json +++ b/data/mods/classic_zombies/exclusions.json @@ -19,14 +19,14 @@ "type": "region_overlay", "regions": [ "all" ], "map_extras": { - "forest": { "chance": 20, "extras": { "mx_portal": 0, "mx_portal_in": 0, "mx_spider": 0 } }, - "forest_thick": { "chance": 20, "extras": { "mx_portal": 0, "mx_portal_in": 0, "mx_shia": 0, "mx_spider": 0, "mx_jabberwock": 0 } }, - "forest_water": { "chance": 20, "extras": { "mx_portal": 0, "mx_portal_in": 0, "mx_spider": 0 } }, - "field": { "chance": 90, "extras": { "mx_portal": 0, "mx_portal_in": 0 } }, - "road": { "chance": 75, "extras": { "mx_portal": 0, "mx_portal_in": 0 } }, - "build": { "chance": 90, "extras": { "mx_house_spider": 0, "mx_house_wasp": 0, "mx_portal": 0, "mx_portal_in": 0 } }, + "forest": { "extras": { "mx_portal": 0, "mx_portal_in": 0, "mx_spider": 0 } }, + "forest_thick": { "extras": { "mx_portal": 0, "mx_portal_in": 0, "mx_shia": 0, "mx_spider": 0, "mx_jabberwock": 0 } }, + "forest_water": { "extras": { "mx_portal": 0, "mx_portal_in": 0, "mx_spider": 0 } }, + "field": { "extras": { "mx_portal": 0, "mx_portal_in": 0 } }, + "road": { "extras": { "mx_portal": 0, "mx_portal_in": 0 } }, + "build": { "extras": { "mx_house_spider": 0, "mx_house_wasp": 0, "mx_portal": 0, "mx_portal_in": 0 } }, "marloss": { "chance": 0, "extras": { "mx_marloss_pilgrimage": 0 } }, - "subway": { "chance": 75, "extras": { "mx_portal": 0, "mx_portal_in": 0 } } + "subway": { "extras": { "mx_portal": 0, "mx_portal_in": 0 } } } } ] diff --git a/data/mods/package_bionic_professions/bionic_professions.json b/data/mods/package_bionic_professions/bionic_professions.json index ce95cdd416dd0..e1b6671f659df 100644 --- a/data/mods/package_bionic_professions/bionic_professions.json +++ b/data/mods/package_bionic_professions/bionic_professions.json @@ -483,7 +483,8 @@ { "item": "ear_plugs", "custom-flags": [ "no_auto_equip" ] }, { "item": "sheath", "contents-item": "knife_combat" }, { - "item": "h&k416a5", + "item": "nato_assault_rifle", + "variant": "h&k416a5", "ammo-item": "556", "charges": 30, "contents-item": [ "shoulder_strap", "acog_scope" ] diff --git a/data/raw/keybindings.json b/data/raw/keybindings.json index e69fade2e05bf..6756fa7251fcc 100644 --- a/data/raw/keybindings.json +++ b/data/raw/keybindings.json @@ -858,6 +858,20 @@ "//": "separate entry, because the global entry also has 'q' and 'Q' listed, which conflicts with the world name entry feature of this dialog", "bindings": [ { "input_method": "keyboard_any", "key": "ESC" } ] }, + { + "type": "keybinding", + "id": "ZOOM_OUT", + "category": "OVERMAP", + "name": "Zoom Out", + "bindings": [ { "input_method": "keyboard_char", "key": "Z" }, { "input_method": "keyboard_code", "key": "z", "mod": [ "shift" ] } ] + }, + { + "type": "keybinding", + "id": "ZOOM_IN", + "category": "OVERMAP", + "name": "Zoom In", + "bindings": [ { "input_method": "keyboard_any", "key": "z" } ] + }, { "type": "keybinding", "id": "DELETE_NOTE", @@ -942,6 +956,13 @@ "name": "Toggle Hordes", "bindings": [ { "input_method": "keyboard_char", "key": "H" }, { "input_method": "keyboard_code", "key": "h", "mod": [ "shift" ] } ] }, + { + "type": "keybinding", + "id": "TOGGLE_OVERMAP_WEATHER", + "category": "OVERMAP", + "name": "Toggle Visible Weather", + "bindings": [ { "input_method": "keyboard_any", "key": "w" } ] + }, { "type": "keybinding", "id": "TOGGLE_FOREST_TRAILS", @@ -2243,6 +2264,27 @@ "category": "DEFAULTMODE", "id": "debug_radiation" }, + { + "type": "keybinding", + "category": "DEBUG_SPELLS", + "id": "TOGGLE_ALL_SPELL", + "name": "Toggle all spells", + "bindings": [ { "input_method": "keyboard_any", "key": "t" } ] + }, + { + "type": "keybinding", + "category": "DEBUG_SPELLS", + "id": "UNLEARN_SPELL", + "name": "Unlearn a spell", + "bindings": [ { "input_method": "keyboard_any", "key": "u" } ] + }, + { + "type": "keybinding", + "category": "DEBUG_SPELLS", + "id": "SHOW_ONLY_LEARNED", + "name": "Show only learned", + "bindings": [ { "input_method": "keyboard_any", "key": "a" } ] + }, { "type": "keybinding", "name": "Switch Sidebar Style", diff --git a/doc/COMPILING/COMPILING.md b/doc/COMPILING/COMPILING.md index 2a830cbd1b7d8..1fd441805bd69 100644 --- a/doc/COMPILING/COMPILING.md +++ b/doc/COMPILING/COMPILING.md @@ -120,6 +120,10 @@ You can get the language ID from the filenames of `*.po` in `lang/po` directory. Special note for MinGW: due to a [libintl bug](https://savannah.gnu.org/bugs/index.php?58006), using English without a `.mo` file would cause significant slow down on MinGW targets. In such case you can compile a `.mo` file for English using `make LANGUAGES="en"`. `make LANGUAGE="all"` also compiles a `.mo` file for English in addition to other languages. +# Accelerating Linux builds with llama + +[llama](https://github.com/nelhage/llama) is a CLI tool for outsourcing computation to AWS Lambda. If you want your builds to run faster and are willing to pay Amazon for the privilege, then you may be able to use it to accelerate your builds. See [../../tools/llama/README.md](our llama README) for more details. + # Debian Instructions for compiling on a Debian-based system. The package names here are valid for Ubuntu 12.10 and may or may not work on your system. diff --git a/doc/DEVELOPER_TOOLING.md b/doc/DEVELOPER_TOOLING.md index 65d16f7860e3c..5f31b77d0194a 100644 --- a/doc/DEVELOPER_TOOLING.md +++ b/doc/DEVELOPER_TOOLING.md @@ -66,10 +66,10 @@ In addition to the usual means of creating a `tags` file via e.g. [`ctags`](http Cataclysm has a [clang-tidy configuration file](../.clang-tidy) and if you have `clang-tidy` available you can run it to perform static analysis of the -codebase. We test with `clang-tidy` from LLVM 8.0.1 on Travis, so for the most +codebase. We test with `clang-tidy` from LLVM 12.0.0 with CI, so for the most consistent results, you might want to use that version. -To run it you have a few options. +To run it, you have a few options. * `clang-tidy` ships with a wrapper script `run-clang-tidy.py`. @@ -92,12 +92,12 @@ We have written our own clang-tidy checks in a custom plugin. Unfortunately, `clang-tidy` as distributed by LLVM doesn't support plugins, so making this work requires some extra steps. -#### Ubuntu Xenial +#### Ubuntu Focal -If you are on Ubuntu Xenial then you might be able to get it working the same -way Travis does. Add the LLVM 8 Xenial source [listed -here](https://apt.llvm.org/) to your `sources.list`, install the `clang-8 -libclang-8-dev llvm-8-dev llvm-8-tools` packages, and build Cataclysm with CMake, +If you are on Ubuntu Focal then you might be able to get it working the same +way our CI does. Add the LLVM 12 Focal source [listed +here](https://apt.llvm.org/) to your `sources.list`, install the needed packages (`clang-12 +libclang-12-dev llvm-12-dev llvm-12-tools`), and build Cataclysm with CMake, adding `-DCATA_CLANG_TIDY_PLUGIN=ON`. On other distributions you will probably need to build `clang-tidy` yourself. @@ -126,7 +126,7 @@ To run `clang-tidy` with this plugin enabled add the to your `clang-tidy` command line. If you wish to run the tests for the custom clang-tidy plugin you will also -need `lit`. This will be built as part of `llvm`, or you can install it via +need `lit`. This will be built as part of LLVM, or you can install it via `pip` or your local package manager if you prefer. Then, assuming `build` is your Cataclysm build directory, you can run the tests @@ -139,34 +139,32 @@ lit -v build/tools/clang-tidy-plugin/test ##### Build LLVM -To build llvm on Windows, you'll first need to get some tools installed. +To build LLVM on Windows, you'll first need to get some tools installed. - Cmake -- Python 3 (Python 2 may be still required to run the lit test, -which will be discussed in the next section.) +- Python 3 - MinGW-w64 (other compilers may or may not work. Clang itself does not seem to be -building llvm on Windows correctly.) +building LLVM on Windows correctly.) - A shell environment After the tools are installed, a patch still needs to be applied before building -llvm, since `clang-tidy` as distributed by LLVM doesn't support plugins. +LLVM, since `clang-tidy` as distributed by LLVM doesn't support plugins. -First, clone the llvm repo from for example [the official github repo](https://github.com/llvm/llvm-project.git). -Checkout the `release/8.x` branch, since that's where our patch was based on. +First, clone the LLVM repo from, for example, [the official github repo](https://github.com/llvm/llvm-project.git). +Checkout the `release/12.x` branch, since that's what our patch was based on. -On Windows, instead of applying the patch mentioned in the previous section, you -shoud apply `plugin-support.patch` from [this PR](https://github.com/jbytheway/clang-tidy-plugin-support/pull/1) -instead, if it's not merged yet. This is because the `-rdynamic` option is not -supported on Windows, so clang-tidy needs to be built as a static library instead. -(If you cloned the repo from the official github repo, replace `tools/extra` with -`clang-tools-extra` in the patch before applying it.) +On Windows, in addition to applying `plugin-support.patch` mentioned in the previous section, you +should also apply +[`clang-tidy-scripts.patch`](https://github.com/jbytheway/clang-tidy-plugin-support/blob/master/clang-tidy-scripts.patch) +so you can run the lit test with the custom clang-tidy executable and let +clang-tidy apply suggestions automatically. -After the patch is applied, you can then build the llvm code. Unfortunately, it -seems that clang itself cannot correctly compile the llvm code on Windows (gives +After the patch is applied, you can then build the LLVM code. Unfortunately, it +seems that clang itself cannot correctly compile the LLVM code on Windows (gives some sort of relocation error). Luckily, MinGW-w64 can be used instead to compile the code. The first step to build the code is to run CMake to generate the makefile. On -the root dir of llvm, run the following script (substitute values inside `<>` +the root dir of LLVM, run the following script (substitute values inside `<>` with the actual paths). Make sure CMake, python, and MinGW-w64 are on the path. ```sh @@ -185,7 +183,7 @@ cmake \ The next step is to call `make` to actually build clang-tidy as a library. When using MinGW-w64 to build, you should call `mingw32-make` instead. Also, because `FileCheck` is not shipped with Windows, you'll also need to build -it youself using llvm sources by adding the `FileCheck` target to the make command. +it yourself using LLVM sources by adding the `FileCheck` target to the make command. ```sh mkdir -p build @@ -198,14 +196,14 @@ are needed to build our custom clang-tidy executable later. ##### Build clang-tidy with custom checks -After building clang-tidy as a library from the llvm source, the next step is to +After building clang-tidy as a library from the LLVM source, the next step is to build clang-tidy as an executable, with the custom checks from the CDDA source. In this step, the following tools are required. -- Python 3 (Python 2 may still be required to run the lit test for the custom checks) +- Python 3 - CMake - MinGW-w64 -- FileCheck (built from the llvm source) +- FileCheck (built from the LLVM source) - A shell environment You also need to install yaml for python 3 to work. Download the `.whl` installer @@ -221,34 +219,53 @@ be applied before the custom checks can be built as an executable. ```patch diff --git a/tools/clang-tidy-plugin/CMakeLists.txt b/tools/clang-tidy-plugin/CMakeLists.txt -index 553ef0ebe0..f591bc80d1 100644 +index cf0c237645..540d3e29a5 100644 --- a/tools/clang-tidy-plugin/CMakeLists.txt +++ b/tools/clang-tidy-plugin/CMakeLists.txt -@@ -3,8 +3,8 @@ include(ExternalProject) +@@ -4,7 +4,7 @@ include(ExternalProject) find_package(LLVM REQUIRED CONFIG) find_package(Clang REQUIRED CONFIG) --add_library( -- CataAnalyzerPlugin MODULE -+add_executable( -+ CataAnalyzerPlugin - CataTidyModule.cpp - JsonTranslationInputCheck.cpp - NoLongCheck.cpp -@@ -51,6 +51,11 @@ else() - CataAnalyzerPlugin SYSTEM PRIVATE ${CATA_CLANG_TIDY_INCLUDE_DIR}) - endif() +-add_library(CataAnalyzerPlugin MODULE ++add_executable(CataAnalyzerPlugin + AlmostNeverAutoCheck.cpp + AssertCheck.cpp + CataTidyModule.cpp +@@ -56,6 +56,11 @@ else () + target_include_directories(CataAnalyzerPlugin SYSTEM PRIVATE ${CATA_CLANG_TIDY_INCLUDE_DIR}) + endif () +target_link_libraries( + CataAnalyzerPlugin + clangTidyMain + ) + - target_compile_definitions( - CataAnalyzerPlugin PRIVATE ${LLVM_DEFINITIONS}) + target_compile_definitions(CataAnalyzerPlugin PRIVATE ${LLVM_DEFINITIONS}) + + # We need to turn off exceptions and RTTI to match the LLVM build. +diff --git a/tools/clang-tidy-plugin/CataTidyModule.cpp b/tools/clang-tidy-plugin/CataTidyModule.cpp +index b7cb4df22c..a83db0c60e 100644 +--- a/tools/clang-tidy-plugin/CataTidyModule.cpp ++++ b/tools/clang-tidy-plugin/CataTidyModule.cpp +@@ -18,6 +18,7 @@ + #include "TestFilenameCheck.h" + #include "TestsMustRestoreGlobalStateCheck.h" + #include "TextStyleCheck.h" ++#include "tool/ClangTidyMain.h" + #include "TranslatorCommentsCheck.h" + #include "UnsequencedCallsCheck.h" + #include "UnusedStaticsCheck.h" +@@ -80,3 +81,8 @@ X( "cata-module", "Adds Cataclysm-DDA checks." ); + } // namespace tidy + } // namespace clang ++ ++int main( int argc, const char **argv ) ++{ ++ return clang::tidy::clangTidyMain( argc, argv ); ++} diff --git a/tools/clang-tidy-plugin/test/lit.cfg b/tools/clang-tidy-plugin/test/lit.cfg -index 4ab6e913a7..d1a4418ba6 100644 +index 496804316a..43beb49653 100644 --- a/tools/clang-tidy-plugin/test/lit.cfg +++ b/tools/clang-tidy-plugin/test/lit.cfg @@ -17,11 +17,13 @@ else: @@ -271,12 +288,10 @@ index 4ab6e913a7..d1a4418ba6 100644 The next step is to run CMake to generate the compilation database. The compilation database contains compiler flags that clang-tidy uses to check the source files. -Make sure Python 3 (and Python 2 if it's still required), CMake, MinGW-w64, and FileCheck are on the path. +Make sure Python 3, CMake, MinGW-w64, and FileCheck are on the path. Note that two `bin` directories of MinGW-w64 should be on the path: `/bin`, and `/x86_64-w64-mingw32/bin`. FileCheck's path is `/build/bin`, -if you built it with the instructions in the previous section. Python 2 should -precede Python 3 in the path, otherwise scripts that are intended to run with -Python 2 might not work. +if you built it with the instructions in the previous section. Then add the following CMake options to generate the compilation database (substitute values inside `<>` with the actual paths) and build the CDDA source @@ -299,10 +314,10 @@ with Python 3 to fix some errors in the compilation database. Then the compilati database should be usable by clang-tidy. If you want to check if the custom checks are working correctly, run the following -script. Note that `python` here is the executable from Python 2. +script. ```sh -python /llvm/utils/lit/lit.py -v build/tools/clang-tidy-plugin/test +python3 /llvm/utils/lit/lit.py -v build/tools/clang-tidy-plugin/test ``` Finally, use the following command to run clang-tidy with the custom checks. diff --git a/doc/EFFECT_ON_CONDITION.md b/doc/EFFECT_ON_CONDITION.md new file mode 100644 index 0000000000000..2d764de4002cc --- /dev/null +++ b/doc/EFFECT_ON_CONDITION.md @@ -0,0 +1,33 @@ +### effect_on_condition +An effect_on_condition is an object allowing the combination of dialog conditions and effects with their usage outside of a dialog. When invoked, they will test their condition; on a pass, they will cause their effect. They can be activated automatically with any given frequency. (Note: effect_on_conditions use the npc dialog conditions and effects syntax, which allows checking related to, or targeting an effect at, an npc (for example: `npc_has_trait`). Using these commands in an effect_on_condition is not supported.) + +## Fields + +|Identifier|Type|Description| +|-|-|-| +| `recurrence_min`| int | The effect_on_condition is automatically invoked (activated) with at least this many seconds in-between. +| `recurrence_max`| int | The effect_on_condition is automatically invoked (activated) at least once this many seconds. +| `condition`| condition | The condition(s) under which this effect_on_condition, upon activation, will cause its effect. See the "Dialogue conditions" section of [NPCs](NPCs.md) for the full syntax. +| `deactivate_condition`| condition | *optional* When an effect_on_condition is automatically activated (invoked) and fails its condition(s), `deactivate_condition` will be tested if it exists and there is no `false_effect` entry. If it returns true, this effect_on_condition will no longer be invoked automatically every `recurrence_max` seconds. Whenever the player gains/loses a trait or bionic all deactivated effect_on_conditions will have `deactivate_condition` run; on a return of false, the effect_on_condition will start being run again. This is to allow adding effect_on_conditions for specific traits or bionics that don't waste time running when you don't have the target bionic/trait. See the "Dialogue conditions" section of [NPCs](NPCs.md) for the full syntax. +| `effect`| effect | The effect(s) caused if `condition` returns true upon activation. See the "Dialogue Effects" section of [NPCs](NPCs.md) for the full syntax. +| `false_effect`| effect | The effect(s) caused if `condition` returns false upon activation. See the "Dialogue Effects" section of [NPCs](NPCs.md) for the full syntax. + +## Examples: +```JSON + { + "type": "effect_on_condition", + "id": "test_deactivate", + "recurrence_min": 1, + "recurrence_max": 1, + "condition": { "u_has_trait": "SPIRITUAL" }, + "deactivate_condition": {"not":{ "u_has_trait": "SPIRITUAL" } }, + "effect": { "u_add_effect": "infection", "duration": 1 } + }, + { + "type": "effect_on_condition", + "id": "test_stats", + "recurrence_min": 1, + "recurrence_max": 10, + "condition": { "not": { "u_has_strength": 7 } }, + "effect": { "u_add_effect": "infection", "duration": 1 } + } diff --git a/doc/EXAMINE.md b/doc/EXAMINE.md new file mode 100644 index 0000000000000..9d7788af87c71 --- /dev/null +++ b/doc/EXAMINE.md @@ -0,0 +1,127 @@ +# Terrain/Furniture examination actions + +These are actions that will be performed when a terrain/furniture is examined. +The hardcoded examine actions specified as a `"examine_action": "ACTION"`, where `ACTION` is replaced with one of the strings from the list below. +The examine actors are specified as JSON objects with a `type` corresponding to a certain type of action, and other members filling in the data for how the action functions. + +## Hardcoded Examine Actions + +- ```aggie_plant``` Harvest plants. +- ```autodoc``` Brings the autodoc consoles menu. Needs the ```AUTODOC``` flag to function properly and an adjacent furniture with the ```AUTODOC_COUCH``` flag. +- ```autoclave_empty``` Start the autoclave cycle if it contains filthy CBM, and the player has enough water. +- ```autoclave_full``` Check on the progress of the cycle, and collect sterile CBM once cycle is completed. +- ```bars``` Take advantage of AMORPHOUS and slip through the bars. +- ```bulletin_board``` Use this to arrange tasks for your faction camp. +- ```chainfence``` Hop over the chain fence. +- ```controls_gate``` Controls the attached gate. +- ```dirtmound``` Plant seeds and plants. +- ```elevator``` Use the elevator to change floors. +- ```fault``` Displays descriptive message, but otherwise unused. +- ```flower_poppy``` Pick the mutated poppy. +- ```fswitch``` Flip the switch and the rocks will shift. +- ```fungus``` Release spores as the terrain crumbles away. +- ```gaspump``` Use the gas-pump. +- ```locked_object``` Locked, but can be pried open. Adding 'PICKABLE' flag allows opening with a lockpick as well. Prying/lockpicking results are hardcoded. +- ```locked_object_pickable``` Locked, but can be opened with a lockpick. Requires 'PICKABLE' flag, lockpicking results are hardcoded. +- ```none``` None +- ```pedestal_temple``` Opens the temple if you have a petrified eye. +- ```pedestal_wyrm``` Spawn wyrms. +- ```pit_covered``` Uncover the pit. +- ```pit``` Cover the pit if you have some planks of wood. +- ```portable_structure``` Take down a tent or similar portable structure. +- ```recycle_compactor``` Compress pure metal objects into basic shapes. +- ```rubble``` Clear up the rubble if you have a shovel. +- ```safe``` Attempt to crack the safe. +- ```shelter``` Take down the shelter. +- ```shrub_marloss``` Pick a marloss bush. +- ```shrub_wildveggies``` Pick a wild veggies shrub. +- ```slot_machine``` Gamble. +- ```toilet``` Either drink or get water out of the toilet. +- ```water_source``` Drink or get water from a water source. + +## Examine Actors + +### `cardreader` + +#### `flags` +Mandatory. +Array of strings. +List of item flags that, when on an item, mean that the item can be used as card. + +#### `consume_card` +Optional, defaults to true. +Boolean (true/false). +Whether or not to consume the item used to activite this cardreader. + +#### `allow_hacking` +Optional, defaults to true. +Boolean (true/false). +Whether or not to allow hacking this door with an electrohack. +If this allows hacking, it will ignore the data specified here, and transform all `t_door_metal_locked` to `t_door_metal_c` in a 3 tile radius. + +#### `despawn_monsters` +Optional, defaults to true. +Boolean (true/false). +Whether or not to remove hostile monsters with the `ID_CARD_DESPAWN` flag. + +### `mapgen_id` +Optional. +String. +Update mapgen ID to apply on opening the door. +Conflicts with `radius`, `terrain_changes`, and `furn_changes`. + +#### `radius` +Optional, defaults to 3. +Integer. +What area around the cardreader to apply changes to. +With a radius of three, it will go 3 tiles out from the reader in every direction. + +#### `terrain_changes` +Optional, defaults to nothing. +JSON Object. +Each key in this JSON Object corresponds to a terrain, and the value is a terrain that that terrain will be transformed into. +Multiple items can be specified by adding more keys, e.g. `{ "a": "b", "x": "y" }` + +#### `furn_changes` +Optional, defaults to nothing. +JSON Object. +Each key in this JSON Object corresponds to a furniture, and the value is a furniture that that furniture will be transformed into. +Multiple items can be specified by adding more keys, e.g. `{ "a": "b", "x": "y" }` + +#### `query` +Optional, defaults to true. +Boolean (true/false). +Whether or not to query the player before activating and potentially consuming a card. + +#### `query_msg` +Optional, defaults to nothing. +String. +What message to display when querying the player on whether or not to activate the cardreader. + +#### `success_msg` +Mandatory. +String. +What message to print to the log when this is sucessfully activated. + +#### `redundant_msg` +Mandatory. +String. +What message to print when attempting to activate the cardreader after it has already been activated. + +#### Example +``` +{ + "type": "cardreader", + "flags": [ "SCIENCE_CARD" ], + "consume_card": true, + "allow_hacking": true, + "despawn_monsters": true, + "radius": 3, + "terrain_changes": { "t_door_metal_locked": "t_door_metal_c" }, + "furn_changes": { "f_crate_c": "f_crate_o" }, + "query": true, + "query_msg": "Are you sure you want to open this door?" + "success_msg": "You opened the door!", + "redundant_msg": "The door is already open." +} +``` diff --git a/doc/GAME_BALANCE.md b/doc/GAME_BALANCE.md index d5ec2bb898ff6..60a02281b3b88 100644 --- a/doc/GAME_BALANCE.md +++ b/doc/GAME_BALANCE.md @@ -126,7 +126,7 @@ Relative value should put the weapon into one of those categories: <2 - Not weapons. Those items may be pressed into service, but are unlikely to be better than fists. Plastic bottles, rocks, boots. -2-5 - Tools not meant to strike and improvised weapons. Two-by-fours, pointy sticks, pipes, hammers. +2-5 - Tools not meant to strike and improvised weapons. Planks, pointy sticks, pipes, hammers. 6-11 - Dangerous tools or crude dedicated weapons. Golf clubs, two-by-swords, wooden spears, knife spears, hatchets, switchblades, tonfas, quarterstaves. @@ -191,7 +191,7 @@ Bow damage is based on the momentum achieved in the projectile. Since arrows an ## Ammo stats The damage (**Dmg**) of firearm ammunition is the square root of a round's muzzle energy in joules (**Energy, J**) rounded to the nearest integer with an arbitrary increase or decrease to account for terminal ballistics. Damage of handloaded ammo is set to 92% (rounded down) of their factory counterparts. A similar system for calculating recoil is planned but not currently being worked on. The figures used to calculate stats and any other relevant information are presented in table below. -Each cartridge also has a Base Barrel Length (**Base Brl**) listed; this determines the damage for the connected guns. A firearm has its damage modifier determined by it's real life barrel length; for every three inches between it and the listed baseline here, the gun takes a 1 point bonus or penalty, rounding to the nearest modifier. For example, a .45 ACP gun with a 7 inch barrel would get a +1 bonus (against a baseline of 5 inches). +Each cartridge also has a Base Barrel Length (**Base Brl**) listed; this determines the damage for the connected guns. A firearm has its damage modifier determined by its real life barrel length; for every three inches between it and the listed baseline here, the gun takes a 1 point bonus or penalty, rounding to the nearest modifier. For example, a .45 ACP gun with a 7 inch barrel would get a +1 bonus (against a baseline of 5 inches). Ammo ID | Description | Energy, J | Dmg | Base Brl | Applied Modifiers / Comments | -------------------|-----------------------------|-----------|-----|----------|------------------------------- @@ -211,7 +211,7 @@ Ammo ID | Description | Energy, J | Dmg | Base Brl | .38 Special | 130gr FMJ bullet | 256 | 16 | 4in | | .38 FMJ | 130gr FMJ bullet | 256 | 16 | 4in | | .38 Super | 147gr JHP bullet | 660 | 26 | 4in | | -10mm Auto | 180gr FMJ bullet | 960 | 31 | 4in | | +10mm Auto | 180gr FMJ bullet | 799 | 28 | 5in | | .40 S&W | 135gr JHP bullet | 575 | 24 | 4in | | .40 FMJ | 180gr FMJ bullet | 598 | 24 | 4in | | .44 Magnum | 240gr JHP bullet | 1570 | 40 | 7.5in | | diff --git a/doc/ITEM_SPAWN.md b/doc/ITEM_SPAWN.md index 53817e324d1f8..2e7463624ba2e 100644 --- a/doc/ITEM_SPAWN.md +++ b/doc/ITEM_SPAWN.md @@ -93,6 +93,7 @@ Each entry can have more values (shown above as `...`). They allow further prop "ammo-group": "", "container-group": "", "sealed": +"variant": "artifact": ``` @@ -104,6 +105,8 @@ Each entry can have more values (shown above as `...`). They allow further prop `sealed`: If true, a container will be sealed when the item spawns. Default is `true`. +`variant`: A valid gun variant id for this item. + `artifact`: This object determines that the item or group that is spawned by this entry will become an artifact. Here is an example: ```json "artifact": { "procgen_id": "cult", "rules": { "power_level": 1000, "max_attributes": 5, "max_negative_power": -2000 } } diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index a970ca43e483e..84c67f308f17c 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -13,7 +13,7 @@ - [Bionics](#bionics) - [Books](#books) - [Use actions](#use-actions) - - [Character - (Bionic/Mutation)](#character) + - [Character - (Bionic/Mutation/Effect)](#character) - [Comestibles](#comestibles) - [Comestible type](#comestible-type) - [Addiction type](#addiction-type) @@ -206,6 +206,10 @@ These are handled through `ammo_types.json`. You can tag a weapon with these to - ```WIDE``` Prevents `HARDTOSHOOT` monster flag from having any effect. Implied by ```SHOT``` or liquid ammo. - ```NON_FOULING``` This ammo does not cause dirtying or blackpowder fouling on the gun when fired. +## Traps + +- ```SONAR_DETECTABLE``` This trap can be identified with ground-penetrating SONAR. +- ```CONVECTS_TEMPERATURE``` This trap convects temperature, like lava. ## Armor @@ -605,42 +609,6 @@ List of known flags, used in both `terrain.json` and `furniture.json`. - ```WORKOUT_LEGS``` This furniture is for training your legs. Needed for checks like `is_limb_broken()`. - ```WORKOUT_ARMS``` This furniture is for training your arms. Needed for checks like `is_limb_broken()`. -### Examine Actions - -- ```aggie_plant``` Harvest plants. -- ```autodoc``` Brings the autodoc consoles menu. Needs the ```AUTODOC``` flag to function properly and an adjacent furniture with the ```AUTODOC_COUCH``` flag. -- ```autoclave_empty``` Start the autoclave cycle if it contains filthy CBM, and the player has enough water. -- ```autoclave_full``` Check on the progress of the cycle, and collect sterile CBM once cycle is completed. -- ```bars``` Take advantage of AMORPHOUS and slip through the bars. -- ```bulletin_board``` Use this to arrange tasks for your faction camp. -- ```cardreader``` Use the cardreader with a valid card, or attempt to hack. -- ```chainfence``` Hop over the chain fence. -- ```controls_gate``` Controls the attached gate. -- ```dirtmound``` Plant seeds and plants. -- ```elevator``` Use the elevator to change floors. -- ```fault``` Displays descriptive message, but otherwise unused. -- ```flower_poppy``` Pick the mutated poppy. -- ```fswitch``` Flip the switch and the rocks will shift. -- ```fungus``` Release spores as the terrain crumbles away. -- ```gaspump``` Use the gas-pump. -- ```locked_object``` Locked, but can be pried open. Adding 'PICKABLE' flag allows opening with a lockpick as well. Prying/lockpicking results are hardcoded. -- ```locked_object_pickable``` Locked, but can be opened with a lockpick. Requires 'PICKABLE' flag, lockpicking results are hardcoded. -- ```none``` None -- ```pedestal_temple``` Opens the temple if you have a petrified eye. -- ```pedestal_wyrm``` Spawn wyrms. -- ```pit_covered``` Uncover the pit. -- ```pit``` Cover the pit if you have some planks of wood. -- ```portable_structure``` Take down a tent or similar portable structure. -- ```recycle_compactor``` Compress pure metal objects into basic shapes. -- ```rubble``` Clear up the rubble if you have a shovel. -- ```safe``` Attempt to crack the safe. -- ```shelter``` Take down the shelter. -- ```shrub_marloss``` Pick a marloss bush. -- ```shrub_wildveggies``` Pick a wild veggies shrub. -- ```slot_machine``` Gamble. -- ```toilet``` Either drink or get water out of the toilet. -- ```water_source``` Drink or get water from a water source. - ### Fungal Conversions Only - ```FLOWER``` This furniture is a flower. @@ -854,16 +822,19 @@ Flags used to describe monsters and define their properties and abilities. ### Anger, Fear and Placation Triggers -- ```FIRE``` There's a fire nearby. -- ```FRIEND_ATTACKED``` A monster of the same type was attacked. -- ```FRIEND_DIED``` A monster of the same type died. -- ```HURT``` The monster is hurt. +- ```FIRE``` Triggers if there's a fire within 3 tiles, the strength of the effect equals 5 * the field intensity of the fire. +- ```FRIEND_ATTACKED``` Triggers if the monster sees another monster of the same type being attacked; strength = 15. +- ```FRIEND_DIED``` Triggers if the monster sees another monster of the same type dying; strength = 15. +- ```HURT``` Triggers when the monster is hurt, strength equals 1 + (damage / 3 ). - ```MEAT``` Meat or a corpse is nearby. - Currently nonfunctional! - ```NULL``` Source use only? -- ```PLAYER_CLOSE``` The player gets within a few tiles distance. -- ```PLAYER_WEAK``` The player is hurt. -- ```SOUND``` Heard a sound. -- ```STALK``` Increases if already angry at the player. +- ```PLAYER_CLOSE``` Triggers when a potential enemy is within 5 tiles range - Anger/fear trigger only! +- ```PLAYER_WEAK``` Raises monster aggression by 10 - (percent of hp remaining / 10) if a potential enemy has less than 70% hp remaining - Anger trigger only! +- ```PLAYER_NEAR_BABY``` Increases monster aggression by 8 and morale by 4 if **the player** comes within 3 tiles of its offspring (defined by the baby_monster field in its reproduction data)- Anger trigger only! +- ```SOUND``` Not an actual trigger, monsters above 10 aggression and 0 morale will wander towards, monsters below 0 morale will wander away from the source of the sound for 1 turn (6, if they have the GOODHEARING flag). +- ```STALK``` Raises monster aggresssion by 1, triggers 20% of the time each turn if aggression > 5 - Anger trigger only! +- ```HOSTILE_SEEN``` Increases aggression/ decreases morale by a random amount between 0-2 for every potential enemy it can see, up to 20 aggression - Anger/fear trigger only! +- ```MATING_SEASON``` Increases aggression by 3 if a potential enemy is within 5 tiles range and the season is the same as the monster's mating season (defined by the baby_flags field in its reproduction data) - Anger trigger only! ### Categories @@ -894,6 +865,7 @@ Multiple death functions can be used. Not all combinations make sense. - ```NORMAL``` Drop a body, leave gibs. - ```RATKING``` Cure verminitis. - ```SMOKEBURST``` Explode like a huge smoke bomb. +- ```TEARBURST``` Explode like a huge tear gas bomb. - ```THING``` Turn into a full thing. - ```TRIFFID_HEART``` Destroys all roots. - ```VINE_CUT``` Kill adjacent vine if it's cut. @@ -976,6 +948,7 @@ Other monster flags. - ```NOHEAD``` Headshots not allowed! - ```NO_BREATHE``` Creature can't drown and is unharmed by gas, smoke or poison. - ```NO_BREED``` Creature doesn't reproduce even though it has reproduction data - useful when using copy-from to make child versions of adult creatures +- ```NO_FUNG_DMG``` This monster can't be damaged by fungal spores and can't be fungalized either. - ```PAY_BOT``` Creature can be turned into a pet for a limited time in exchange of e-money. - ```PET_MOUNTABLE``` Creature can be ridden or attached to an harness. - ```PET_HARNESSABLE```Creature can be attached to an harness. @@ -1245,6 +1218,7 @@ These branches are also the valid entries for the categories of `dreams` in `dre ### Flags - ```BAD_DAY``` Player starts the game drunk, depressed and sick with the flu. +- ```BORDERED``` Initial start location is bordered by an enormous wall of solid rock. - ```CHALLENGE``` Game won't choose this scenario in random game types. - ```CITY_START``` Scenario is available only when city size value in world options is more than 0. - ```FIRE_START``` Player starts the game with fire nearby. @@ -1444,6 +1418,7 @@ Those flags are added by the game code to specific items (for example, that spec - ```SOLAR_PANEL``` Recharges vehicle batteries when exposed to sunlight. Has a 1 in 4 chance of being broken on car generation. - ```SPACE_HEATER``` There is separate command to toggle this part. - ```STABLE``` Similar to `WHEEL`, but if the vehicle is only a 1x1 section, this single wheel counts as enough wheels. +- ```UNSTABLE_WHEEL``` The opposite of `STABLE` - this will not provide for the wheeling needs of your vehicle if installed alone. - ```STEERABLE``` This wheel is steerable. - ```STEREO``` - ```TRANSFORM_TERRAIN``` Transform terrain (using rules defined in ```transform_terrain```). @@ -1549,3 +1524,8 @@ Gun fault flags: - ```ALARMCLOCK``` You always can set alarms. - ```PARAIMMUNE``` You are immune to parasites. - ```IMMUNE_SPOIL``` You are immune to negative outcomes from spoiled food. +- ```FEATHER_FALL``` You are immune to fall damage. +- ```INVISIBLE``` You can't be seen. +- ```DIMENSIONAL_ANCHOR``` You can't be teleported. +- ```CLIMATE_CONTROL``` You are resistant to extreme temperatures. +- ```HEATSINK``` You are resistant to extreme heat. diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index 54e95359086f4..529d0d0d561cf 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -655,6 +655,7 @@ For information about tools with option to export ASCII art in format ready to b | weight_capacity_bonus | (_optional_) Bonus to weight carrying capacity in grams, can be negative. Strings can be used - "5000 g" or "5 kg" (default: `0`) | weight_capacity_modifier | (_optional_) Factor modifying base weight carrying capacity. (default: `1`) | canceled_mutations | (_optional_) A list of mutations/traits that are removed when this bionic is installed (e.g. because it replaces the fault biological part). +| mutation_conflicts | (_optional_) A list of mutations that prevent this bionic from being installed. | included_bionics | (_optional_) Additional bionics that are installed automatically when this bionic is installed. This can be used to install several bionics from one CBM item, which is useful as each of those can be activated independently. | included | (_optional_) Whether this bionic is included with another. If true this bionic does not require a CBM item to be defined. (default: `false`) | env_protec | (_optional_) How much environmental protection does this bionic provide on the specified body parts. @@ -672,7 +673,7 @@ For information about tools with option to export ASCII art in format ready to b | coverage_power_gen_penalty | (_optional_) Fraction of coverage diminishing fuel_efficiency. Float between 0.0 and 1.0. (default: `nullopt`) | power_gen_emission | (_optional_) `emit_id` of the field emitted by this bionic when it produces energy. Emit_ids are defined in `emit.json`. | stat_bonus | (_optional_) List of passive stat bonus. Stat are designated as follow: "DEX", "INT", "STR", "PER". -| enchantments | (_optional_) List of enchantments applied by this CBM (see MAGIC.md for instructions on enchantment. NB: enchantments are not necessarily magic.) +| enchantments | (_optional_) List of enchantments applied by this CBM (see MAGIC.md for instructions on enchantment. NB: enchantments are not necessarily magic.) Values can either be the enchantments's id or an inline definition of the enchantment. | learned_spells | (_optional_) Map of {spell:level} you gain when installing this CBM, and lose when you uninstall this CBM. Spell classes are automatically gained. | learned_proficiencies | (_optional_) Array of proficiency ids you gain when installing this CBM, and lose when uninstalling | installation_requirement | (_optional_) Requirement id pointing to a requirement defining the tools and components necessary to install this CBM. @@ -692,6 +693,7 @@ For information about tools with option to export ASCII art in format ready to b "encumbrance" : [ [ "torso", 10 ], [ "arm_l", 10 ], [ "arm_r", 10 ], [ "leg_l", 10 ], [ "leg_r", 10 ], [ "foot_l", 10 ], [ "foot_r", 10 ] ], "description" : "You have a battery draining attachment, and thus can make use of the energy contained in normal, everyday batteries. Use 'E' to consume batteries.", "canceled_mutations": ["HYPEROPIC"], + "mutation_conflicts": [ "HUGE" ], "installation_requirement": "sewing_standard", "included_bionics": ["bio_blindfold"] }, @@ -826,10 +828,10 @@ When you sort your inventory by category, these are the categories that are disp | `dmg_adj` | Adjectives used to describe damage states of a material. | `density` | Affects vehicle collision damage, with denser parts having the advantage over less-dense parts. | `vitamins` | Vitamins in a material. Usually overridden by item specific values. An integer percentage of ideal daily value. -| `specific_heat_liquid` | Specific heat of a material when not frozen (J/(g K)). Default 4.186. -| `specific_heat_solid` | Specific heat of a material when frozen (J/(g K)). Default 2.108. +| `specific_heat_liquid` | Specific heat of a material when not frozen (J/(g K)). Default 4.186 - water. +| `specific_heat_solid` | Specific heat of a material when frozen (J/(g K)). Default 2.108 - water. | `latent_heat` | Latent heat of fusion for a material (J/g). Default 334. -| `freeze_point` | Freezing point of this material (F). Default 32 F ( 0 C ). +| `freezing_point` | Freezing point of this material (C). Default 0 C ( 32 F ). | `edible` | Optional boolean. Default is false. | `rotting` | Optional boolean. Default is false. | `soft` | Optional boolean. Default is false. @@ -1941,7 +1943,7 @@ it is present to help catch errors. } ] ], -"enchantments": [ "ench_id_1" ], // List of IDs of enchantments granted by this mutation +"enchantments": [ "ench_id_1" ], // List of enchantments granted by this mutation, can be either string ids of the enchantment or an inline definition of the enchantment "temperature_speed_modifier": 0.5, // If nonzero, become slower when cold, and faster when hot // 1.0 gives +/-1% speed for each degree above or below 65F "mana_modifier": 100 // Positive or negative change to total mana pool @@ -1980,6 +1982,7 @@ it is present to help catch errors. "spell_data": { "id": "bear_trap" }, // data required for trapfunc::spell() "trigger_weight": "200 g", // If an item with this weight or more is thrown onto the trap, it triggers. TODO: what is the default? "drops": [ "beartrap" ], // For disassembly? + "flags": [ "EXAMPLE_FLAG" ], // A set of valid flags for this trap "vehicle_data": { "damage": 300, "sound_volume": 8, @@ -2599,7 +2602,7 @@ CBMs can be defined like this: "charges" : 4, // Number of uses when spawned "stack_size" : 8, // (Optional) How many uses are in the above-defined volume. If omitted, is the same as 'charges' "fun" : 50 // Morale effects when used -"freezing_point": 32, // (Optional) Temperature in F at which item freezes, default is water (32F/0C) +"freezing_point": 32, // (Optional) Temperature in C at which item freezes, default is water (32F/0C) "cooks_like": "meat_cooked", // (Optional) If the item is used in a recipe, replaces it with its cooks_like "parasites": 10, // (Optional) Probability of becoming parasitised when eating "contamination": [ { "disease": "bad_food", "probability": 5 } ], // (Optional) List of diseases carried by this comestible and their associated probability. Values must be in the [0, 100] range. @@ -2683,10 +2686,19 @@ Guns can be defined like this: "durability": 8, // Resistance to damage/rusting, also determines misfire chance "blackpowder_tolerance": 8,// One in X chance to get clogged up (per shot) when firing blackpowder ammunition (higher is better). Optional, default is 8. "min_cycle_recoil": 0, // Minimum ammo recoil for gun to be able to fire more than once per attack. -"burst": 5, // Number of shots fired in burst mode +"variants": [ // Cosmetic variants this gun can have + { + "id": "varianta", // id used in spawning to spawn this variant specifically + "name": { "str": "Variant A pistol" }, // The name used instead of the default name when this variant is selected + "description": "A fancy variant A pistol", // The description used instead of the default when this variant is selected + "ascii_picture": "valid_ascii_art_id", // An ASCII art picture used when this variant is selected. If there is none, the default (if it exists) is used. + "weight": 2 // The relative chance of this variant being selected over other variants when this item is spawned with no explicit variant. Defaults to 0. If it is 0, this variant will not be selected + } +], "clip_size": 100, // Maximum amount of ammo that can be loaded "ups_charges": 0, // Additionally to the normal ammo (if any), a gun can require some charges from an UPS. This also works on mods. Attaching a mod with ups_charges will add/increase ups drain on the weapon. "ammo_to_fire" 1, // Amount of ammo used +"modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "AUTO", "auto", 4 ] ], // Firing modes on this gun, DEFAULT,AUTO, or MELEE followed by the name of the mode displayed in game, and finaly the number of shots of the mod. "reload": 450, // Amount of time to reload, 100 = 1 second = 1 "turn" "built_in_mods": ["m203"], //An array of mods that will be integrated in the weapon using the IRREMOVABLE tag. "default_mods": ["m203"] //An array of mods that will be added to a weapon on spawn. @@ -2719,7 +2731,6 @@ Gun mods can be defined like this: "acceptable_ammo": [ "9mm" ], // Optional filter restricting mod to guns with those base (before modifiers) ammo types "ammo_modifier": [ "57" ], // Optional field which if specified modifies parent gun to use these ammo types "magazine_adaptor": [ [ "223", [ "stanag30" ] ] ], // Optional field which changes the types of magazines the parent gun accepts -"burst_modifier": 3, // Optional field increasing or decreasing base gun burst size "damage_modifier": -1, // Optional field increasing or decreasing base gun damage "dispersion_modifier": 15, // Optional field increasing or decreasing base gun dispersion "loudness_modifier": 4, // Optional field increasing or decreasing base guns loudness @@ -3534,7 +3545,7 @@ Color of the object as it appears in the game. "color" defines the foreground co #### `examine_action` -(Optional) The json function that is called when the object is examined. See "src/iexamine.h". +(Optional) The json function that is called when the object is examined. See [doc/EXAMINE.md](EXAMINE.md). #### `close" And "open` diff --git a/doc/MAGIC.md b/doc/MAGIC.md index eb74d0bf4b570..d444fd769b22a 100644 --- a/doc/MAGIC.md +++ b/doc/MAGIC.md @@ -21,7 +21,7 @@ In `data/mods/Magiclysm` there is a template spell, copied here for your perusal "spell_class": "NONE", // "base_casting_time": 100, // this is the casting time (in moves) "base_energy_cost": 10, // the amount of energy (of the requisite type) to cast the spell - "energy_source": "MANA", // the type of energy used to cast the spell. types are: MANA, BIONIC, HP, STAMINA, FATIGUE, NONE (none will not use mana) + "energy_source": "MANA", // the type of energy used to cast the spell. types are: MANA, BIONIC, HP, STAMINA, NONE (none will not use mana) "components": [requirement_id] // an id from a requirement, like the ones you use for crafting. spell components require to cast. "difficulty": 12, // the difficulty to learn/cast the spell "max_level": 10, // maximum level you can achieve in the spell @@ -477,6 +477,7 @@ Effects for the character that has the enchantment: * REGEN_STAMINA * MAX_HP * REGEN_HP +* HUNGER * THIRST * FATIGUE * PAIN @@ -493,6 +494,7 @@ Effects for the character that has the enchantment: * SOCIAL_LIE * SOCIAL_PERSUADE * SOCIAL_INTIMIDATE +* SLEEPY : The higher this is the more easily you fall asleep. * ARMOR_BASH * ARMOR_CUT * ARMOR_STAB diff --git a/doc/MAPGEN.md b/doc/MAPGEN.md index bdaa60394aca9..84567b35cb1aa 100644 --- a/doc/MAPGEN.md +++ b/doc/MAPGEN.md @@ -543,6 +543,7 @@ Value: `[ array of {objects} ]: [ { "monster": ... } ]` | friendly | Set true to make the monster friendly. Default false. | name | Extra name to display on the monster. | target | Set to true to make this into mission target. Only works when the monster is spawned from a mission. +| spawn_data | An optional object that contains additional details for spawning the monster. Note that high spawn density game setting can cause extra monsters to spawn when `monster` is used. When `group` is used only one monster will spawn. @@ -572,6 +573,31 @@ Example: This places "mon_secubot" at random coordinate (7-18, 7-18). The monster is placed with 30% probability. The placement is repeated by random number of times `[1-3]`. The monster will spawn with 20-30 5.56x45mm rounds. +### "spawn_data" for monsters +This optional object can have two fields: +| Field | Description +| --- | --- +| ammo | A list of objects, each of which has an `"ammo_id"` field and a `"qty"` list of two integers. The monster will spawn with items of "ammo_id", with at least the first number in the "qty" and no more than the second. +| patrol | A list of objects, each of which has an `"x"` field and a `"y"` field. Either value can be a range or a single number. The x,y co-ordinates define a patrol point as an relative mapsquare point offset from the (0, 0) local mapsquare of the overmap terrain tile that the monster spawns in. Patrol points are converted to absolute mapsquare tripoints inside the monster generator. + +Monsters with a patrol point list will move to each patrol point, in order, whenever they have no more pressing action to take on their turn. Upon reaching the last point in the patrol point list, the monster will continue on to the first point in the list. + +Example: +```json +"place_monster": [ + { "monster": "mon_zombie", "x": 12, "y": 12, "spawn_data": { "patrol": [ { "x": 12, "y": 12 } ] } } +] +``` + +This places a "mon_zombie" at (12, 12). The zombie can move freely to chase after enemies, but will always return to the (12, 12) position if it has nothing else to do. + +Example 2: +```json +"place_monster": [ + { "monster": "mon_secubot", "x": 12, "y": 12, "spawn_data": { "ammo": [ { "ammo_id": "556", "qty": [ 20, 30 ] } ], "patrol": [ { "x": -23, "y": -23 }, { "x": 47, "y": -23 }, { "x": 47, "y": 47 }, { "x": 47, "y": -23 } ] } } +] +``` +This places a "mon_secubot" at (12,12). It will patrol the four outmost concerns of the diagonally adjacent overmap terrain tiles in a box pattern. ## Spawn specific items with a "place_item" array **optional** A list of *specific* things to add. WIP: Monsters and vehicles will be here too @@ -594,7 +620,8 @@ Example: | repeat | (optional) Value: `[ n1, n2 ]`. Spawn item randomly between `n1` and `n2` times. Only makes sense if the coordinates are random. Example: `[ 1, 3 ]` - repeat 1-3 times. | custom-flags | (optional) Value: `[ "flag1", "flag2" ]`. Spawn item with specific flags. - +The special custom flag "ACTIVATE_ON_PLACE" causes the item to be activated as it is placed. This is useful to have noisemakers that are already turned on as the avatar approaches. It can also be used with explosives with a 1 second countdown to have locations explode as the avatar approaches, creating uniquely ruined terrain. + ## Extra map features with specials **optional** Special map features that do more than just placing furniture / terrain. @@ -927,6 +954,7 @@ matching magazine and ammo for guns. | chance | (optional, integer) x in 100 chance of item(s) spawning. Defaults to 100. | ammo | (optional, integer) x in 100 chance of item(s) spawning with the default amount of ammo. Defaults to 0. | magazine | (optional, integer) x in 100 chance of item(s) spawning with the default magazine. Defaults to 0. +| variant | (optional, string), gun variant id for the spawned item ### Plant seeds in a planter with "sealed_item" @@ -1072,4 +1100,3 @@ update_mapgen adds new optional keywords to a few mapgen JSON items. place_npc, place_monster, and place_computer can take an optional target boolean. If they have `"target": true` and are invoked by update_mapgen with a valid mission, then the NPC, monster, or computer will be marked as the target of the mission. - diff --git a/doc/MARTIALART_JSON.md b/doc/MARTIALART_JSON.md index 2331a993b9fc1..48a89d7eeaffd 100644 --- a/doc/MARTIALART_JSON.md +++ b/doc/MARTIALART_JSON.md @@ -61,7 +61,7 @@ "aoe": "spin", // This technique has an area-of-effect; doesn't work against solo targets "block_counter": true, // This technique may automatically counterattack on a successful block "dodge_counter": true, // This technique may automatically counterattack on a successful dodge -"weighting": 2, // Affects likelihood this technique will be seleted when many are available +"weighting": 2, // Affects likelihood this technique will be selected when many are available "defensive": true, // Game won't try to select this technique when attacking "miss_recovery": true, // Misses while attacking will use fewer moves "messages" : [ // What is printed when this technique is used by the player and by an npc diff --git a/doc/MODDING.md b/doc/MODDING.md index f84234baf268a..573a769785332 100644 --- a/doc/MODDING.md +++ b/doc/MODDING.md @@ -4,6 +4,11 @@ Certain features of the game can be modified without rebuilding the game from so The majority of modding is done by editing JSON files. An in-depth review of all json files and their appropriate fields is available in [JSON_INFO.md](JSON_INFO.md). +## Other guides + +You might want to read the [Guide to adding new content to CDDA for first time +contributors](https://github.com/CleverRaven/Cataclysm-DDA/wiki/Guide-to-adding-new-content-to-CDDA-for-first-time-contributors) on the CDDA wiki. + ## The basics ### Creating a barebones mod @@ -26,7 +31,8 @@ A barebones `modinfo.json` file looks like this: ] ```` The `category` attribute denotes where the mod will appear in the mod selection menu. These are the available categories to choose from, with some examples chosen from mods that existed when this document was written. Pick whichever one applies best to your mod when writing your modinfo file. - - `content` - A mod that adds a lot of stuff. Typically reserved for very large mods or complete game overhauls (eg: Core game files, Aftershock) + - `content` - A mod that adds a lot of stuff. Typically reserved for large mods (eg: Core game files, Aftershock) + - `total_conversion` - A mod that fundamentally changes the game. In particular, the assumption is that a player should not use two total conversion mods at the same time, and so they will not be tested together. However, nothing prevents players from using more than one if they wish. (eg: Dark Skies Above) - `items` - A mod that adds new items and recipes to the game (eg: More survival tools) - `creatures` - A mod that adds new creatures or NPCs to the game (eg: Modular turrets) - `misc_additions` - Miscellaneous content additions to the game (eg: Alternative map key, Crazy cataclysm) @@ -135,7 +141,7 @@ Items are where you really want to read the [JSON_INFO.md](JSON_INFO.md) file, j ```` ### Preventing monsters from spawning -This kind of mod is relatively simple, but very useful. If you don't want to deal with certain types of monsters in your world, this is how you do it. There are two ways to go about this, and both will be detailed below. You can blacklist entire monster groups, or you can blacklist certain monsters. In order to do either of those things, you need that monster's ID. These can be found in the relevant data files. For the core game, these are in the `data/json/monsters` directory. +This kind of mod is relatively simple, but very useful. If you don't want to deal with certain types of monsters in your world, this is how you do it. There are two ways to go about this, and both will be detailed below. You can blacklist entire monster groups, blacklist monsters by their specified species, or you can blacklist individual monsters. In order to do any of those things, you need that monster's ID or SPECIES data. These can be found in the relevant data files. For the core game, these are in the `data/json/monsters` directory. The example below is from the `No Ants` mod, and will stop any kind of ant from spawning in-game. ````json [ @@ -158,6 +164,15 @@ The example below is from the `No Ants` mod, and will stop any kind of ant from } ] ```` +The following is an example of how to blacklist monsters by species. In this case, it would remove all fungaloids from the game. +````json +[ + { + "type": "MONSTER_BLACKLIST", + "species": [ "FUNGUS" ] + } +] +```` ### Preventing locations from spawning Preventing certain types of locations from spawning in-game is a little trickier depending on the type of thing you want to target. An overmap building can either be a standard building, or an overmap special. If you want to block things with a specific flag from spawning, you can blacklist those in a very similar manner to monsters. The example below is also from the `No Ants` mod, and stops all anthills from spawning. diff --git a/doc/MONSTERS.md b/doc/MONSTERS.md index edae93eac052a..ad1f898c68274 100644 --- a/doc/MONSTERS.md +++ b/doc/MONSTERS.md @@ -47,8 +47,8 @@ Monsters may also have any of these optional properties: | `phase` | (string) Monster's body matter state, ex. SOLID, LIQUID, GAS, PLASMA, NULL | `attack_cost` | (integer) Number of moves per regular attack (??) | `diff` | (integer) Additional monster difficulty for special and ranged attacks -| `aggression` | (integer) From totally passive `-99` to guaranteed hostile `100` -| `morale` | (integer) From lemming `-50` to bear `60` to most zombies and monsters `100` +| `aggression` | (integer) Starting aggression, the monster will become hostile when it reaches 10 +| `morale` | (integer) Starting morale, monster will flee when (current aggression + current morale) < 0 | `mountable_weight_ratio` | (float) For mounts, max ratio of mount to rider weight, ex. `0.2` for `<=20%` | `melee_skill` | (integer) Monster skill in melee combat, from `0-10`, with `4` being an average mob | `dodge` | (integer) Monster's skill at dodging attacks @@ -74,9 +74,9 @@ Monsters may also have any of these optional properties: | `regen_morale` | (bool) True if monster will stop fleeing at max HP to regenerate anger and morale | `special_attacks` | (array of objects) Special attacks the monster has | `flags` | (array of strings) Any number of attributes like SEES, HEARS, SMELLS, STUMBLES, REVIVES -| `fear_triggers` | (array of strings) What makes the monster afraid, ex. FIRE, HURT, PLAYER_CLOSE, SOUND -| `anger_triggers` | (array of strings) What makes the monster angry (same flags as fear) -| `placate_triggers` | (array of strings) What calms the monster (same flags as fear) +| `fear_triggers` | (array of strings) Triggers that lower monster morale (see JSON_FLAGS.md) +| `anger_triggers` | (array of strings) Triggers that raise monster aggression (same flags as fear) +| `placate_triggers` | (array of strings) Triggers that lower monster aggression (same flags as fear) | `revert_to_itype` | (string) Item monster can be converted to when friendly (ex. to deconstruct turrets) | `starting_ammo` | (object) Ammo that newly spawned monsters start with | `upgrades` | (boolean or object) False if monster does not upgrade, or an object do define an upgrade @@ -87,6 +87,8 @@ Monsters may also have any of these optional properties: | `path_settings` | (object) How monster may find a path, open doors, avoid traps, or bash obstacles | `biosignature` | (object) Droppings or feces left by the animal or monster | `harvest` | (string) ID of a "harvest" type describing what can be harvested from the corpse +| `zombify_into` | (string) mtype_id this monster zombifies into after it's death +| `fungalize_into` | (string) mtype_id this monster turns into when fungalized by spores Properties in the above tables are explained in more detail in the sections below. diff --git a/doc/NPCs.md b/doc/NPCs.md index 28c9fc1fd9601..0062c4628488a 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -486,6 +486,9 @@ Effect | Description `barber_beard` | Opens a menu allowing the player to choose a new beard style. `u_learn_recipe: recipe_string` | Your character will learn and memorize the recipe `recipe_string`. `npc_first_topic: talk_topic_string` | Changes the initial talk_topic of the NPC in all future dialogues. +`u_mod_pain: pain_int`
`npc_mod_pain: pain_int` | Your character or the NPC will have `pain_int` added or subtracted from its pain. +`u_add_wet: wet_int`
`npc_add_wet: wet_int` | Your character or the NPC will be wet `wet_int` as if they were in the rain. +`u_add_power: power_energy`
`npc_add_power: power_energy` | Your character or the NPC will have `power_energy` added or subtracted from its bionic power. #### Trade / Items @@ -549,6 +552,13 @@ Effect | Description #### Map Updates `mapgen_update: mapgen_update_id_string`
`mapgen_update:` *list of `mapgen_update_id_string`s*, (optional `assign_mission_target` parameters) | With no other parameters, updates the overmap tile at the player's current location with the changes described in `mapgen_update_id` (or for each `mapgen_update_id` in the list). The `assign_mission_target` parameters can be used to change the location of the overmap tile that gets updated. See [the missions docs](MISSIONS_JSON.md) for `assign_mission_target` parameters and [the mapgen docs](MAPGEN.md) for `mapgen_update`. +`lightning` | Lights up the map as if its day time. + +#### General +Effect | Description +---|--- +`message: message_string`, (*optional* `sound: sound_bool`),(*optional* `outdoor_only: outdoor_only_bool`),(*optional* `snippet: snippet_bool`),(*optional* `type: type_string`),(*optional* `popup: popup_bool`) | Displays a message to the player of `message_string`. If `snippet_bool` is true(defaults to false) it will instead display a random snippet from `message_string` category. If `sound` is true(defaults to false) it will only display the message if the player is not deaf. `outdoor_only`(defaults to false) only matters when `sound` is true and will make the message less likely to be heard if the player is underground. Message will display as type of `type_string`. Type affects the color of message and can be any of the following values: good, neutral, bad, mixed, warning, info, debug, headshot, critical, grazing. enums.h has more info on each types use. If `popup_bool` is true the message will be in a modal popup the user has to dismiss to continue. +`sound_effect: sound_effect_id_string`, *optional* `sound_effect_variant: variant_string`, *optional* `outdoor_event: outdoor_event`,*optional* `volume: volume_int` | Will play a sound effect of id `sound_effect_id_string` and variant `variant_string`. If `volume_int` is defined it will be used otherwise 80 is the default. If `outdoor_event`(defaults to false) is true this will be less likely to play if the player is underground. #### Deprecated @@ -623,6 +633,11 @@ Condition | Type | Description `"u_driving"`
`"npc_driving"` | simple string | `true` if the player character or NPC is operating a vehicle. Note NPCs cannot currently operate vehicles. `"u_has_skill"`
`"npc_has_skill"` | dictionary | `u_has_skill` or `npc_has_skill` must be a dictionary with a `skill` string and a `level` int.
`true` if the player character or NPC has at least the value of `level` in `skill`. `"u_know_recipe"` | string | `true` if the player character knows the recipe specified in `u_know_recipe`. It only counts as known if it is actually memorized--holding a book with the recipe in it will not count. +`"u_has_pain"`
`"npc_has_pain"` | int | `true` if the player character's or NPC's pain is at least the value of `u_has_pain` or `npc_has_pain`. +`"u_is_height"`
`"npc_is_height"` | int | `true` if the player character's or NPC's elevation is at least the value of `u_is_height` or `npc_is_height`. +`"u_has_worn_with_flag"`
`"npc_has_worn_with_flag"` | string | `true` if the player character or NPC is wearing something with the `u_has_worn_with_flag` or `npc_has_worn_with_flag` flag. +`"u_has_wielded_with_flag"`
`"npc_has_wielded_with_flag"` | string | `true` if the player character or NPC is wielding something with the `u_has_wielded_with_flag` or `npc_has_wielded_with_flag` flag. +`"u_has_power"`
`"npc_has_power"` | int | `true` if the player character's or NPC's bionic power is at least the value of `u_has_power` or `npc_has_power`. #### Player Only conditions @@ -678,8 +693,13 @@ Condition | Type | Description `"days_since_cataclysm"` | int | `true` if at least `days_since_cataclysm` days have passed since the Cataclysm. `"is_season"` | string | `true` if the current season matches `is_season`, which must be one of "`spring"`, `"summer"`, `"autumn"`, or `"winter"`. `"is_day"` | simple string | `true` if it is currently daytime. -`"is_outside"` | simple string | `true` if the NPC is on a tile without a roof. - +`"u_is_outside"`
`"npc_is_outside"` | simple string | `true` if you or the NPC is on a tile without a roof. +`"one_in_chance"` | int | `true` if a one in `one_in_chance` random chance occurs. +`"is_temperature"` | int | `true` if it is currently at least `"is_temperature"` degrees fahrenheit. +`"is_windpower"` | int | `true` if current windpower is at least `"is_windpower"`. +`"is_humidity"` | int | `true` if current humidity is at least `"is_humidity"`. +`"is_pressure"` | int | `true` if current pressure is at least `"is_pressure"`. +`"is_weather"` | int | `true` if current weather is `"is_weather"`. #### Sample responses with conditions and effects ```json diff --git a/doc/OVERMAP.md b/doc/OVERMAP.md index beca346df7b2b..a9a8ed9a55801 100644 --- a/doc/OVERMAP.md +++ b/doc/OVERMAP.md @@ -332,8 +332,7 @@ level value and then only specify it for individual entries that differ. During generation of a new overmap, cities and their connecting roads will be generated before specials are placed. Each city gets assigned a size at generation and will begin its life as a single intersection. The city distance field specifies the minimum and maximum distance the special can be -placed from _this_ intersection, *not* from the edge of the city, meaning a special with a low minimum -distance and a high or unbounded maximum city size may be placed on the outer border of a larger city. +placed from the edge of the urban radius of a city, and not from the center of the city. Both city size and city distance requirements are only checked for the "nearest" city, measured from the original intersection. @@ -347,7 +346,7 @@ original intersection. | `overmaps` | List of overmap terrains and their relative `[ x, y, z ]` location within the special. | | `connections` | List of overmap connections and their relative `[ x, y, z ]` location within the special. | | `locations` | List of `overmap_location` ids that the special may be placed on. | -| `city_distance` | Min/max distance from a city that the special may be placed. Use -1 for unbounded. | +| `city_distance` | Min/max distance from a city edge that the special may be placed. Use -1 for unbounded. | | `city_sizes` | Min/max city size for a city that the special may be placed near. Use -1 for unbounded. | | `occurrences` | Min/max number of occurrences when placing the special. If UNIQUE flag is set, becomes X of Y chance. | | `flags` | See `Overmap specials` in [JSON_FLAGS.md](JSON_FLAGS.md). | @@ -393,7 +392,7 @@ original intersection. | `terrain` | Will go away in favor of `connection` eventually. Use `road`, `subway`, `sewer`, etc. | | `connection` | Id of the `overmap_connection` to build. Optional for now, but you should specify it explicitly. | | `from` | Optional point `[ x, y, z]` within the special to treat as the origin of the connection. | -| `existing` | Boolean, default false. If the special requires a preexisting terrain to spawn. | +| `existing` | Boolean, default false. If the special requires a preexisting terrain to spawn. | ## City Building diff --git a/doc/PROFICIENCY.md b/doc/PROFICIENCY.md index 1aa7481c5ed3a..80a57222aaa54 100644 --- a/doc/PROFICIENCY.md +++ b/doc/PROFICIENCY.md @@ -38,7 +38,7 @@ Optional. Float When used in recipes these values are the amount the time to complete the recipe, and the chance to fail the recipe, will be multiplied by. - For proficiencies that represent core basic knowledge and foundational principles, the `time` should usually be low (1.5 or so), and the `fail` should be high (4 or more). -- For "flavour" proficiencies that offer a small boost, these should be around 1.5 each. +- For "flavor" proficiencies that offer a small boost, these should be around 1.5 each. - Most other proficiencies should be in the 2-3 range for both values. ### `time_to_learn` diff --git a/doc/VEHICLES_JSON.md b/doc/VEHICLES_JSON.md index 0cd720d0c9187..4e090f9942544 100644 --- a/doc/VEHICLES_JSON.md +++ b/doc/VEHICLES_JSON.md @@ -30,7 +30,7 @@ Vehicle prototypes do not currently accept copy-from "items": [ // Item spawn list { "x": 0, "y": 0, "items": "helmet_army" }, // individual item { "x": 0, "y": 0, "item_groups": "army_uniform" }, // item or items from an item_group - { "x": 0, "y": 1, "items": [ "matchbook", "two_by_four" ] }, // all items in the list spawn + { "x": 0, "y": 1, "items": [ "matchbook", "2x4" ] }, // all items in the list spawn { "x": 0, "y": 0, "item_groups": [ "army_uniform", "rare_guns" ] } all item_groups are processed ] ``` @@ -70,4 +70,6 @@ TYPE and DATA may be one of: ``` the optional keyword "chance" provides an X in 100 chance that a particular item definition will spawn. +If a single item is specified through `"items"`, a gun variant for it can be specified through `"variant"`. + Multiple lines of items may share the same X and Y values. diff --git a/doc/WEATHER_TYPE.md b/doc/WEATHER_TYPE.md index 01612bb348467..b572553cd35c5 100644 --- a/doc/WEATHER_TYPE.md +++ b/doc/WEATHER_TYPE.md @@ -1,6 +1,6 @@ # WEATHER TYPES -Each weather type is a type of weather that occurs, its effects and what causes it. The only required entries are null and clear. +Each weather type is a type of weather that occurs, and what causes it. The only required entries are null and clear. ## `weather_type` properties @@ -26,8 +26,8 @@ Each weather type is a type of weather that occurs, its effects and what causes | `time_between_min` | Optional: the lower bound on the amount of time that will be guaranteed to pass before this weather happens again. Defaults to 0. | | `time_between_max` | Optional: the upper bound on the amount of time that will be guaranteed to pass before this weather happens again. Defaults to 0. | | `weather_animation` | Optional, Information controlling weather animations. Members: factor, color and glyph | -| `effects` | Array for the effects the weather has. Descibed in detail below -| `requirements` | Optional, is what determines what weather it is. All members are optional. When determining current weather, it loops through the entries in order and uses the last one to pass all the requirements. | +| `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 | +| `condition` | A dialog condition to determine if this weather is happening. See Dialogue conditions section of [NPCs](NPCs.md) #### `weather_type` example @@ -37,9 +37,9 @@ Each weather type is a type of weather that occurs, its effects and what causes "id": "lightning", "type": "weather_type", "name": "Lightning Storm", - "color": "yellow", + "color": "c_yellow", "map_color": "h_yellow", - "sym": "%", + "glyph": "%", "ranged_penalty": 4, "sight_penalty": 1.25, "light_modifier": -45, @@ -48,27 +48,9 @@ Each weather type is a type of weather that occurs, its effects and what causes "precip": "heavy", "rains": true, "acidic": false, - "effects": [ - { - "one_in_chance": 50, - "must_be_outside": false, - "sound_message": "You hear a distant rumble of thunder.", - "sound_effect": "thunder_far" - }, - { - "one_in_chance": 600, - "must_be_outside": false, - "message": "A flash of lightning illuminates your surroundings!.", - "sound_effect": "thunder_near", - "lightning": true - } - ], - "tiles_animation": "weather_rain_drop", - "weather_animation": { "factor": 0.04, "color": "light_blue", "sym": "," }, - "sound_category": "thunder", - "sun_intensity": "none", - "requirements": { "pressure_max": 990, "required_weathers": [ "thunder" ] } - }, + "required_weathers": [ "thunder" ], + "condition": { "not": { "is_pressure": 990 } } + } ] ``` @@ -128,45 +110,3 @@ Each weather type is a type of weather that occurs, its effects and what causes }] } ``` - -### `requirements` properties - -| Identifier | Description | -| ------------------------------ | --------------------------------------------------------------------- | -| `pressure_min` | These are all minimum and maximum values for which the weather will occur. I.e., it will only rain if it is sufficiently humid. | -| `pressure_max` | | -| `humidity_min` | | -| `humidity_max` | | -| `temperature_min` | | -| `temperature_max` | | -| `windpower_min` | | -| `windpower_max` | | -| `humidity_and_pressure` | should logical AND be used for pressure and humidity requirements when they are both defined | -| `acidic` | does this require acidic precipitation | -| `time` | Valid values are: "day", "night", and "both". | -| `required_weathers` | a string array of possible prior weathers; i.e., rain can only happen if the conditions for clouds, light drizzle, or drizzle are present | -| `time_passed_min` | Optional: Time after the Cataclysm when this weather can start appearing; | -| `time_passed_max` | Optional: Time after the Cataclysm when this weather can no longer appear. | -| `one_in_chance` | Optional: This has a 1 in this value chance of happening. This will usually be called every 5 minutes| - - -### `spawns` properties - -| Identifier | Description | -| ------------------------------ | --------------------------------------------------------------------- | -| `max_radius` | Optional: The furthest away a spawn will happen. | -| `min_radius` | Optional: The closest a spawn will happen. | -| `hallucination_count` | Optional: Number of hallucinations of the target to spawn. | -| `real_count` | Optional: Number of real copies to spawn. | -| `target` | Optional: Monster id of target to spawn. If left blank a nearby monster will be used. | -| `target_range` | Optional: If target is left blank how far away to look for something to copy. | - -### `fields` properties - -| Identifier | Description | -| ------------------------------ | --------------------------------------------------------------------- | -| `type` | The string id of the field. | -| `intensity` | Intensity of the field. | -| `age` | Age of the field. | -| `outdoor_only` | Optional: Defaults to true. If true field will only spawn outdoors. | -| `radius` | Optional: Radius around player the effect will spread, defaults to everywhere. | diff --git a/lang/extract_json_strings.py b/lang/extract_json_strings.py index 6a21764f8b64d..4f87c621c65cd 100755 --- a/lang/extract_json_strings.py +++ b/lang/extract_json_strings.py @@ -153,7 +153,6 @@ def warning_supressed(filename): "json_flag", "keybinding", "LOOT_ZONE", - "MAGAZINE", "map_extra", "MOD_INFO", "MONSTER", @@ -268,6 +267,13 @@ def extract_construction(item): writestr(outfile, item["pre_note"]) +def extract_effect_on_condition(item): + outfile = get_outfile("effect_on_condition") + extract_talk_effects(item["effect"], outfile) + if "false_effect" in item: + extract_talk_effects(item["false_effect"], outfile) + + def extract_harvest(item): outfile = get_outfile("harvest") if "message" in item: @@ -476,6 +482,12 @@ def extract_gun(item): if "description" in item: description = item.get("description") writestr(outfile, description) + if "variants" in item: + for variant in item.get("variants"): + vname = variant.get("name") + writestr(outfile, vname, pl_fmt=True) + vdesc = variant.get("description") + writestr(outfile, vdesc) if "modes" in item: modes = item.get("modes") for fire_mode in modes: @@ -490,6 +502,25 @@ def extract_gun(item): if "reload_noise" in item: item_reload_noise = item.get("reload_noise") writestr(outfile, item_reload_noise) + + +def extract_magazine(item): + outfile = get_outfile("magazine") + if "name" in item: + item_name = item.get("name") + if item["type"] in needs_plural: + writestr(outfile, item_name, pl_fmt=True) + else: + writestr(outfile, item_name) + if "description" in item: + description = item.get("description") + writestr(outfile, description) + if "variants" in item: + for variant in item.get("variants"): + vname = variant.get("name") + writestr(outfile, vname, pl_fmt=True) + vdesc = variant.get("description") + writestr(outfile, vdesc) if "use_action" in item: use_action = item.get("use_action") item_name = item.get("name") @@ -692,6 +723,8 @@ def extract_talk_effects(effects, outfile): comment = "Nickname for creature '{}'".format( eff["u_buy_monster"]) writestr(outfile, eff["name"], comment=comment) + if "message" in eff: + writestr(outfile, eff["message"]) def extract_talk_response(response, outfile): @@ -821,6 +854,14 @@ def extract_mutation(item): if "ranged_mutation" in item: writestr(outfile, item.get("ranged_mutation").get("message")) + if "transform" in item: + writestr(outfile, item.get("transform").get("msg_transform")) + + for trigger in item.get("triggers", []): + for entry in trigger: + writestr(outfile, entry.get("msg_on", {}).get("text")) + writestr(outfile, entry.get("msg_off", {}).get("text")) + def extract_mutation_category(item): outfile = get_outfile("mutation_category") @@ -971,11 +1012,13 @@ def extract_vehicle_part_category(item): "clothing_mod": extract_clothing_mod, "conduct": extract_achievement, "construction": extract_construction, + "effect_on_condition": extract_effect_on_condition, "effect_type": extract_effect_type, "fault": extract_fault, "GUN": extract_gun, "GUNMOD": extract_gunmod, "harvest": extract_harvest, + "MAGAZINE": extract_magazine, "mapgen": extract_mapgen, "martial_art": extract_martial_art, "material": extract_material, diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 11ab3fd8f6846..4bad4361952e1 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -721,7 +721,7 @@ static int hack_level( const Character &who ) return who.get_skill_level( skill_computer ) + who.int_cur / 2 - 8; } -static hack_result hack_attempt( Character &who, const bool using_bionic ) +static hack_result hack_attempt( Character &who ) { // TODO: Remove this once player -> Character migration is complete { @@ -735,21 +735,10 @@ static hack_result hack_attempt( Character &who, const bool using_bionic ) int success = std::ceil( normal_roll( hack_level( who ), hack_stddev ) ); if( success < 0 ) { who.add_msg_if_player( _( "You cause a short circuit!" ) ); - if( using_bionic ) { - who.mod_power_level( -25_kJ ); - } else { - who.use_charges( itype_electrohack, 25 ); - } + who.use_charges( itype_electrohack, 25 ); if( success <= -5 ) { - if( !using_bionic ) { - who.add_msg_if_player( m_bad, _( "Your electrohack is ruined!" ) ); - who.use_amount( itype_electrohack, 1 ); - } else { - who.add_msg_if_player( m_bad, _( "Your power is drained!" ) ); - who.mod_power_level( units::from_kilojoule( -rng( 25, - units::to_kilojoule( who.get_power_level() ) ) ) ); - } + who.use_charges( itype_electrohack, 50 ); } return hack_result::FAIL; } else if( success < 6 ) { @@ -767,8 +756,8 @@ static hack_type get_hack_type( const tripoint &examp ) const ter_t &xter_t = here.ter( examp ).obj(); if( xter_t.has_examine( iexamine::pay_gas ) || xfurn_t.has_examine( iexamine::pay_gas ) ) { type = hack_type::GAS; - } else if( xter_t.has_examine( iexamine::cardreader ) || - xfurn_t.has_examine( iexamine::cardreader ) ) { + } else if( xter_t.has_examine( "cardreader" ) || + xfurn_t.has_examine( "cardreader" ) ) { type = hack_type::DOOR; } else if( xter_t.has_examine( iexamine::gunsafe_el ) || xfurn_t.has_examine( iexamine::gunsafe_el ) ) { @@ -777,16 +766,11 @@ static hack_type get_hack_type( const tripoint &examp ) return type; } -hacking_activity_actor::hacking_activity_actor( use_bionic ) - : using_bionic( true ) -{ -} - void hacking_activity_actor::finish( player_activity &act, Character &who ) { tripoint examp = act.placement; hack_type type = get_hack_type( examp ); - switch( hack_attempt( who, using_bionic ) ) { + switch( hack_attempt( who ) ) { case hack_result::UNABLE: who.add_msg_if_player( _( "You cannot hack this." ) ); break; @@ -842,25 +826,11 @@ void hacking_activity_actor::finish( player_activity &act, Character &who ) act.set_to_null(); } -void hacking_activity_actor::serialize( JsonOut &jsout ) const -{ - jsout.start_object(); - jsout.member( "using_bionic", using_bionic ); - jsout.end_object(); -} +void hacking_activity_actor::serialize( JsonOut & ) const {} -std::unique_ptr hacking_activity_actor::deserialize( JsonIn &jsin ) +std::unique_ptr hacking_activity_actor::deserialize( JsonIn & ) { hacking_activity_actor actor; - if( jsin.test_null() ) { - // Old saves might contain a null instead of an object. - // Since we do not know whether a bionic or an item was chosen we assume - // it was an item. - actor.using_bionic = false; - } else { - JsonObject jsobj = jsin.get_object(); - jsobj.read( "using_bionic", actor.using_bionic ); - } return actor.clone(); } @@ -938,6 +908,89 @@ std::unique_ptr hotwire_car_activity_actor::deserialize( JsonIn return actor.clone(); } +void bikerack_racking_activity_actor::start( player_activity &act, Character & ) +{ + act.moves_total = moves_total; + act.moves_left = moves_total; +} + +void bikerack_racking_activity_actor::finish( player_activity &act, Character & ) +{ + if( parent_vehicle.try_to_rack_nearby_vehicle( parts ) ) { + map &here = get_map(); + here.invalidate_map_cache( here.get_abs_sub().z ); + here.rebuild_vehicle_level_caches(); + } else { + debugmsg( "Racking task failed. Parent-Vehicle:" + parent_vehicle.name + + "; Found parts size:" + std::to_string( parts[0].size() ) ); + } + act.set_to_null(); +} + +void bikerack_racking_activity_actor::serialize( JsonOut &jsout ) const +{ + jsout.start_object(); + jsout.member( "moves_total", moves_total ); + jsout.member( "parent_vehicle", parent_vehicle ); + jsout.member( "parts", parts ); + jsout.end_object(); +} + +std::unique_ptr bikerack_racking_activity_actor::deserialize( JsonIn &jsin ) +{ + vehicle veh; + bikerack_racking_activity_actor actor( 0, veh, std::vector>() ); + JsonObject data = jsin.get_object(); + data.read( "moves_total", actor.moves_total ); + data.read( "parent_vehicle", actor.parent_vehicle ); + data.read( "parts", actor.parts ); + + return actor.clone(); +} + +void bikerack_unracking_activity_actor::start( player_activity &act, Character & ) +{ + act.moves_total = moves_total; + act.moves_left = moves_total; +} + +void bikerack_unracking_activity_actor::finish( player_activity &act, Character & ) +{ + if( parent_vehicle.remove_carried_vehicle( parts ) ) { + parent_vehicle.clear_bike_racks( racks ); + map &here = get_map(); + here.invalidate_map_cache( here.get_abs_sub().z ); + here.rebuild_vehicle_level_caches(); + } else { + debugmsg( "Unracking task failed. Parent-Vehicle:" + parent_vehicle.name + + "; Found parts size:" + std::to_string( parts.size() ) ); + } + act.set_to_null(); +} + +void bikerack_unracking_activity_actor::serialize( JsonOut &jsout ) const +{ + jsout.start_object(); + jsout.member( "moves_total", moves_total ); + jsout.member( "parent_vehicle", parent_vehicle ); + jsout.member( "parts", parts ); + jsout.member( "racks", racks ); + jsout.end_object(); +} + +std::unique_ptr bikerack_unracking_activity_actor::deserialize( JsonIn &jsin ) +{ + vehicle veh; + bikerack_unracking_activity_actor actor( 0, veh, std::vector(), std::vector() ); + JsonObject data = jsin.get_object(); + data.read( "moves_total", actor.moves_total ); + data.read( "parent_vehicle", actor.parent_vehicle ); + data.read( "parts", actor.parts ); + data.read( "racks", actor.racks ); + + return actor.clone(); +} + void move_items_activity_actor::do_turn( player_activity &act, Character &who ) { const tripoint dest = relative_destination + who.pos(); @@ -1164,6 +1217,9 @@ void lockpick_activity_actor::finish( player_activity &act, Character &who ) } else if( ter_type == t_door_locked_peep ) { new_ter_type = t_door_c_peep; open_message = _( "With a satisfying click, the lock on the door opens." ); + } else if( ter_type == t_retractable_gate_l ) { + new_ter_type = t_retractable_gate_c; + open_message = _( "With a satisfying click, the lock on the gate opens." ); } else if( ter_type == t_door_metal_pickable ) { new_ter_type = t_door_metal_c; open_message = _( "With a satisfying click, the lock on the door opens." ); @@ -1215,7 +1271,9 @@ void lockpick_activity_actor::finish( player_activity &act, Character &who ) // In the meantime, let's roll 3d5-3, giving us a range of 0-12. int lock_roll = rng( 0, 4 ) + rng( 0, 4 ) + rng( 0, 4 ); - add_msg( m_debug, _( "Rolled %i. Mean_roll %g. Difficulty %i." ), pick_roll, mean_roll, lock_roll ); + add_msg_debug( debugmode::DF_ACT_LOCKPICK, _( "Rolled %i. Mean_roll %g. Difficulty %i." ), + pick_roll, + mean_roll, lock_roll ); // Your base skill XP gain is derived from the lock difficulty (which is currently random but shouldn't be). int xp_gain = 3 * lock_roll; @@ -2042,8 +2100,8 @@ void workout_activity_actor::do_turn( player_activity &act, Character &who ) who.add_morale( MORALE_FEELING_GOOD, intensity_modifier, 20, 6_hours, 30_minutes ); } if( calendar::once_every( 2_minutes ) ) { - who.add_msg_if_player( m_debug, who.activity_level_str() ); - who.add_msg_if_player( m_debug, act.id().c_str() ); + who.add_msg_debug_if_player( debugmode::DF_ACT_WORKOUT, who.activity_level_str() ); + who.add_msg_debug_if_player( debugmode::DF_ACT_WORKOUT, act.id().c_str() ); } } else if( !rest_mode ) { rest_mode = true; @@ -2794,6 +2852,109 @@ std::unique_ptr disassemble_activity_actor::deserialize( JsonIn return actor.clone(); } +void meditate_activity_actor::start( player_activity &act, Character & ) +{ + act.moves_total = to_moves( 20_minutes ); + act.moves_left = act.moves_total; +} + +void meditate_activity_actor::finish( player_activity &act, Character &who ) +{ + who.add_msg_if_player( m_good, _( "You pause to engage in spiritual contemplation." ) ); + who.add_morale( MORALE_FEELING_GOOD, 5, 10 ); + act.set_to_null(); +} + +void meditate_activity_actor::serialize( JsonOut &jsout ) const +{ + jsout.write_null(); +} + +std::unique_ptr meditate_activity_actor::deserialize( JsonIn & ) +{ + return meditate_activity_actor().clone(); +} + +void play_with_pet_activity_actor::start( player_activity &act, Character & ) +{ + act.moves_total = rng( 50, 125 ) * 100; + act.moves_left = act.moves_total; +} + +void play_with_pet_activity_actor::finish( player_activity &act, Character &who ) +{ + who.add_morale( MORALE_PLAY_WITH_PET, rng( 3, 10 ), 10, 5_hours, 25_minutes ); + who.add_msg_if_player( m_good, _( "Playing with your %s has lifted your spirits a bit." ), + pet_name ); + act.set_to_null(); +} + +void play_with_pet_activity_actor::serialize( JsonOut &jsout ) const +{ + jsout.start_object(); + + jsout.member( "pet_name", pet_name ); + + jsout.end_object(); +} + +std::unique_ptr play_with_pet_activity_actor::deserialize( JsonIn &jsin ) +{ + play_with_pet_activity_actor actor = play_with_pet_activity_actor(); + + JsonObject data = jsin.get_object(); + + data.read( "pet_name", actor.pet_name ); + + return actor.clone(); +} + +void shave_activity_actor::start( player_activity &act, Character & ) +{ + act.moves_total = to_moves( 5_minutes ); + act.moves_left = act.moves_total; +} + +void shave_activity_actor::finish( player_activity &act, Character &who ) +{ + who.add_msg_if_player( _( "You open up your kit and shave." ) ); + who.add_morale( MORALE_SHAVE, 8, 8, 240_minutes, 3_minutes ); + act.set_to_null(); +} + +void shave_activity_actor::serialize( JsonOut &jsout ) const +{ + jsout.write_null(); +} + +std::unique_ptr shave_activity_actor::deserialize( JsonIn & ) +{ + return shave_activity_actor().clone(); +} + +void haircut_activity_actor::start( player_activity &act, Character & ) +{ + act.moves_total = to_moves( 30_minutes ); + act.moves_left = act.moves_total; +} + +void haircut_activity_actor::finish( player_activity &act, Character &who ) +{ + who.add_msg_if_player( _( "You give your hair a trim." ) ); + who.add_morale( MORALE_HAIRCUT, 3, 3, 480_minutes, 3_minutes ); + act.set_to_null(); +} + +void haircut_activity_actor::serialize( JsonOut &jsout ) const +{ + jsout.write_null(); +} + +std::unique_ptr haircut_activity_actor::deserialize( JsonIn & ) +{ + return haircut_activity_actor().clone(); +} + namespace activity_actors { @@ -2802,6 +2963,8 @@ const std::unordered_map( * )( Json deserialize_functions = { { activity_id( "ACT_AIM" ), &aim_activity_actor::deserialize }, { activity_id( "ACT_AUTODRIVE" ), &autodrive_activity_actor::deserialize }, + { activity_id( "ACT_BIKERACK_RACKING" ), &bikerack_racking_activity_actor::deserialize }, + { activity_id( "ACT_BIKERACK_UNRACKING" ), &bikerack_unracking_activity_actor::deserialize }, { activity_id( "ACT_CONSUME" ), &consume_activity_actor::deserialize }, { activity_id( "ACT_CRAFT" ), &craft_activity_actor::deserialize }, { activity_id( "ACT_DIG" ), &dig_activity_actor::deserialize }, @@ -2810,15 +2973,19 @@ deserialize_functions = { { activity_id( "ACT_DROP" ), &drop_activity_actor::deserialize }, { activity_id( "ACT_GUNMOD_REMOVE" ), &gunmod_remove_activity_actor::deserialize }, { activity_id( "ACT_HACKING" ), &hacking_activity_actor::deserialize }, + { activity_id( "ACT_HAIRCUT" ), &haircut_activity_actor::deserialize }, { activity_id( "ACT_HOTWIRE_CAR" ), &hotwire_car_activity_actor::deserialize }, { activity_id( "ACT_INSERT_ITEM" ), &insert_item_activity_actor::deserialize }, { activity_id( "ACT_LOCKPICK" ), &lockpick_activity_actor::deserialize }, + { activity_id( "ACT_MEDITATE" ), &meditate_activity_actor::deserialize }, { activity_id( "ACT_MIGRATION_CANCEL" ), &migration_cancel_activity_actor::deserialize }, { activity_id( "ACT_MILK" ), &milk_activity_actor::deserialize }, { activity_id( "ACT_MOVE_ITEMS" ), &move_items_activity_actor::deserialize }, { activity_id( "ACT_OPEN_GATE" ), &open_gate_activity_actor::deserialize }, { activity_id( "ACT_PICKUP" ), &pickup_activity_actor::deserialize }, + { activity_id( "ACT_PLAY_WITH_PET" ), &play_with_pet_activity_actor::deserialize }, { activity_id( "ACT_RELOAD" ), &reload_activity_actor::deserialize }, + { activity_id( "ACT_SHAVE" ), &shave_activity_actor::deserialize }, { activity_id( "ACT_STASH" ), &stash_activity_actor::deserialize }, { activity_id( "ACT_TRY_SLEEP" ), &try_sleep_activity_actor::deserialize }, { activity_id( "ACT_UNLOAD" ), &unload_activity_actor::deserialize }, diff --git a/src/activity_actor_definitions.h b/src/activity_actor_definitions.h index ef62ea38a0504..a245d29180781 100644 --- a/src/activity_actor_definitions.h +++ b/src/activity_actor_definitions.h @@ -22,6 +22,7 @@ #include "type_id.h" #include "units.h" #include "units_fwd.h" +#include "vehicle.h" #include "activity_actor.h" @@ -277,14 +278,8 @@ class gunmod_remove_activity_actor : public activity_actor class hacking_activity_actor : public activity_actor { - private: - bool using_bionic = false; - public: - struct use_bionic {}; - hacking_activity_actor() = default; - explicit hacking_activity_actor( use_bionic ); activity_id get_type() const override { return activity_id( "ACT_HACKING" ); @@ -341,6 +336,62 @@ class hotwire_car_activity_actor : public activity_actor static std::unique_ptr deserialize( JsonIn &jsin ); }; +class bikerack_racking_activity_actor : public activity_actor +{ + private: + int moves_total; + vehicle &parent_vehicle; + std::vector> parts; + public: + bikerack_racking_activity_actor( int moves_total, vehicle &parent_vehicle, + std::vector> parts ): moves_total( moves_total ), + parent_vehicle( parent_vehicle ), parts( parts ) {} + + activity_id get_type() const override { + return activity_id( "ACT_BIKERACK_RACKING" ); + } + + void start( player_activity &act, Character & ) override; + void do_turn( player_activity &, Character & ) override {} + void finish( player_activity &act, Character & ) override; + + std::unique_ptr clone() const override { + return std::make_unique( *this ); + } + + void serialize( JsonOut &jsout ) const override; + static std::unique_ptr deserialize( JsonIn &jsin ); +}; + +class bikerack_unracking_activity_actor : public activity_actor +{ + private: + int moves_total; + vehicle &parent_vehicle; + std::vector parts; + std::vector racks; + + public: + bikerack_unracking_activity_actor( int moves_total, vehicle &parent_vehicle, + std::vector parts, std::vector racks ) : moves_total( moves_total ), + parent_vehicle( parent_vehicle ), parts( parts ), racks( racks ) {} + + activity_id get_type() const override { + return activity_id( "ACT_BIKERACK_UNRACKING" ); + } + + void start( player_activity &act, Character & ) override; + void do_turn( player_activity &, Character & ) override {} + void finish( player_activity &act, Character & ) override; + + std::unique_ptr clone() const override { + return std::make_unique( *this ); + } + + void serialize( JsonOut &jsout ) const override; + static std::unique_ptr deserialize( JsonIn &jsin ); +}; + class move_items_activity_actor : public activity_actor { private: @@ -960,4 +1011,88 @@ class insert_item_activity_actor : public activity_actor static std::unique_ptr deserialize( JsonIn &jsin ); }; +class meditate_activity_actor : public activity_actor +{ + public: + meditate_activity_actor() = default; + activity_id get_type() const override { + return activity_id( "ACT_MEDITATE" ); + } + + void start( player_activity &act, Character & ) override; + void do_turn( player_activity &, Character & ) override {} + void finish( player_activity &act, Character &who ) override; + + std::unique_ptr clone() const override { + return std::make_unique( *this ); + } + + void serialize( JsonOut & ) const override; + static std::unique_ptr deserialize( JsonIn & ); +}; + +class play_with_pet_activity_actor : public activity_actor +{ + private: + std::string pet_name; + public: + play_with_pet_activity_actor() = default; + explicit play_with_pet_activity_actor( const std::string &pet_name ) : + pet_name( pet_name ) {} + activity_id get_type() const override { + return activity_id( "ACT_PLAY_WITH_PET" ); + } + + void start( player_activity &act, Character & ) override; + void do_turn( player_activity &, Character & ) override {} + void finish( player_activity &act, Character &who ) override; + + std::unique_ptr clone() const override { + return std::make_unique( *this ); + } + + void serialize( JsonOut &jsout ) const override; + static std::unique_ptr deserialize( JsonIn &jsin ); +}; + +class shave_activity_actor : public activity_actor +{ + public: + shave_activity_actor() = default; + activity_id get_type() const override { + return activity_id( "ACT_SHAVE" ); + } + + void start( player_activity &act, Character & ) override; + void do_turn( player_activity &, Character & ) override {} + void finish( player_activity &act, Character &who ) override; + + std::unique_ptr clone() const override { + return std::make_unique( *this ); + } + + void serialize( JsonOut & ) const override; + static std::unique_ptr deserialize( JsonIn & ); +}; + +class haircut_activity_actor : public activity_actor +{ + public: + haircut_activity_actor() = default; + activity_id get_type() const override { + return activity_id( "ACT_HAIRCUT" ); + } + + void start( player_activity &act, Character & ) override; + void do_turn( player_activity &, Character & ) override {} + void finish( player_activity &act, Character &who ) override; + + std::unique_ptr clone() const override { + return std::make_unique( *this ); + } + + void serialize( JsonOut & ) const override; + static std::unique_ptr deserialize( JsonIn & ); +}; + #endif // CATA_SRC_ACTIVITY_ACTOR_DEFINITIONS_H diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 8e5c9f1b0e29b..1afb2a5f04532 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -136,12 +136,10 @@ static const activity_id ACT_GAME( "ACT_GAME" ); static const activity_id ACT_GENERIC_GAME( "ACT_GENERIC_GAME" ); static const activity_id ACT_GUNMOD_ADD( "ACT_GUNMOD_ADD" ); static const activity_id ACT_HACKSAW( "ACT_HACKSAW" ); -static const activity_id ACT_HAIRCUT( "ACT_HAIRCUT" ); static const activity_id ACT_HAND_CRANK( "ACT_HAND_CRANK" ); static const activity_id ACT_HEATING( "ACT_HEATING" ); static const activity_id ACT_JACKHAMMER( "ACT_JACKHAMMER" ); static const activity_id ACT_LONGSALVAGE( "ACT_LONGSALVAGE" ); -static const activity_id ACT_MEDITATE( "ACT_MEDITATE" ); static const activity_id ACT_MEND_ITEM( "ACT_MEND_ITEM" ); static const activity_id ACT_MIND_SPLICER( "ACT_MIND_SPLICER" ); static const activity_id ACT_MOVE_LOOT( "ACT_MOVE_LOOT" ); @@ -156,14 +154,12 @@ static const activity_id ACT_OPERATION( "ACT_OPERATION" ); static const activity_id ACT_OXYTORCH( "ACT_OXYTORCH" ); static const activity_id ACT_PICKAXE( "ACT_PICKAXE" ); static const activity_id ACT_PLANT_SEED( "ACT_PLANT_SEED" ); -static const activity_id ACT_PLAY_WITH_PET( "ACT_PLAY_WITH_PET" ); static const activity_id ACT_PRY_NAILS( "ACT_PRY_NAILS" ); static const activity_id ACT_PULP( "ACT_PULP" ); static const activity_id ACT_QUARTER( "ACT_QUARTER" ); static const activity_id ACT_READ( "ACT_READ" ); static const activity_id ACT_REPAIR_ITEM( "ACT_REPAIR_ITEM" ); static const activity_id ACT_ROBOT_CONTROL( "ACT_ROBOT_CONTROL" ); -static const activity_id ACT_SHAVE( "ACT_SHAVE" ); static const activity_id ACT_SKIN( "ACT_SKIN" ); static const activity_id ACT_SOCIALIZE( "ACT_SOCIALIZE" ); static const activity_id ACT_SPELLCASTING( "ACT_SPELLCASTING" ); @@ -213,6 +209,7 @@ static const itype_id itype_steel_chunk( "steel_chunk" ); static const itype_id itype_steel_plate( "steel_plate" ); static const itype_id itype_UPS( "UPS" ); static const itype_id itype_wire( "wire" ); +static const itype_id itype_chain( "chain" ); static const itype_id itype_wool_staple( "wool_staple" ); static const zone_type_id zone_type_FARM_PLOT( "FARM_PLOT" ); @@ -343,7 +340,6 @@ activity_handlers::finish_functions = { { ACT_GUNMOD_ADD, gunmod_add_finish }, { ACT_TOOLMOD_ADD, toolmod_add_finish }, { ACT_CLEAR_RUBBLE, clear_rubble_finish }, - { ACT_MEDITATE, meditate_finish }, { ACT_READ, read_finish }, { ACT_WAIT, wait_finish }, { ACT_WAIT_WEATHER, wait_weather_finish }, @@ -366,9 +362,6 @@ activity_handlers::finish_functions = { { ACT_CHOP_PLANKS, chop_planks_finish }, { ACT_JACKHAMMER, jackhammer_finish }, { ACT_FILL_PIT, fill_pit_finish }, - { ACT_PLAY_WITH_PET, play_with_pet_finish }, - { ACT_SHAVE, shaving_finish }, - { ACT_HAIRCUT, haircut_finish }, { ACT_ROBOT_CONTROL, robot_control_finish }, { ACT_MIND_SPLICER, mind_splicer_finish }, { ACT_SPELLCASTING, spellcasting_finish }, @@ -418,8 +411,9 @@ static bool check_butcher_cbm( const int roll ) // 90% at roll 0, 72% at roll 1, 60% at roll 2, 51% @ 3, 45% @ 4, 40% @ 5, ... , 25% @ 10 // Roll is roughly a rng(0, -3 + 1st_aid + fine_cut_quality + 1/2 electronics + small_dex_bonus) // Roll is reduced by corpse damage level, but to no less then 0 - add_msg_debug( _( "Roll = %i" ), roll ); - add_msg_debug( _( "Failure chance = %f%%" ), ( 9.0f / ( 10.0f + roll * 2.5f ) ) * 100.0f ); + add_msg_debug( debugmode::DF_ACT_BUTCHER, _( "Roll = %i" ), roll ); + add_msg_debug( debugmode::DF_ACT_BUTCHER, _( "Failure chance = %f%%" ), + ( 9.0f / ( 10.0f + roll * 2.5f ) ) * 100.0f ); const bool failed = x_in_y( 9, ( 10 + roll * 2.5 ) ); return !failed; } @@ -877,7 +871,8 @@ static void butchery_drops_harvest( item *corpse_item, const mtype &mt, player & int roll = roll_butchery() - corpse_item->damage_level(); roll = roll < 0 ? 0 : roll; roll = std::min( entry.max, roll ); - add_msg_debug( _( "Roll penalty for corpse damage = %s" ), 0 - corpse_item->damage_level() ); + add_msg_debug( debugmode::DF_ACT_BUTCHER, _( "Roll penalty for corpse damage = %s" ), + 0 - corpse_item->damage_level() ); if( entry.type == "bionic" ) { butcher_cbm_item( drop_id, p.pos(), calendar::turn, roll, entry.flags, entry.faults ); } else if( entry.type == "bionic_group" ) { @@ -1185,7 +1180,7 @@ void activity_handlers::butcher_finish( player_activity *act, player *p ) skill_level = p->get_skill_level( skill_firstaid ); skill_level += p->max_quality( qual_CUT_FINE ); skill_level += p->get_skill_level( skill_electronics ) / 2; - add_msg_debug( _( "Skill: %s" ), skill_level ); + add_msg_debug( debugmode::DF_ACT_BUTCHER, _( "Skill: %s" ), skill_level ); } const auto roll_butchery = [&]() { @@ -1560,7 +1555,7 @@ void activity_handlers::fill_liquid_do_turn( player_activity *act, player *p ) act_ref.set_to_null(); } else { if( act_ref.str_values.empty() ) { - act_ref.str_values.push_back( std::string() ); + act_ref.str_values.emplace_back( ); } act_ref.str_values.at( 0 ) = serialize( liquid ); } @@ -2311,6 +2306,10 @@ void activity_handlers::oxytorch_finish( player_activity *act, player *p ) here.ter_set( pos, t_mdoor_frame ); here.spawn_item( pos, itype_steel_plate, rng( 0, 1 ) ); here.spawn_item( pos, itype_steel_chunk, rng( 3, 8 ) ); + } else if( ter == t_wall_metal ) { + here.ter_set( pos, t_scrap_wall_halfway ); + here.spawn_item( pos, itype_steel_plate, rng( 2, 3 ) ); + here.spawn_item( pos, itype_steel_chunk, rng( 12, 20 ) ); } else if( ter == t_window_enhanced || ter == t_window_enhanced_noglass ) { here.ter_set( pos, t_window_empty ); here.spawn_item( pos, itype_steel_plate, rng( 0, 1 ) ); @@ -2319,6 +2318,10 @@ void activity_handlers::oxytorch_finish( player_activity *act, player *p ) here.ter_set( pos, t_pit ); here.spawn_item( pos, itype_spike, rng( 1, 19 ) ); here.spawn_item( pos, itype_scrap, rng( 1, 8 ) ); + } else if( ter == t_retractable_gate_c || ter == t_retractable_gate_l ) { + here.ter_set( pos, t_strconc_floor ); + here.spawn_item( pos, itype_chain, rng( 1, 2 ) ); + here.spawn_item( pos, itype_wire, rng( 8, 22 ) ); } else if( ter == t_bars ) { if( here.ter( pos + point_east ) == t_sewage || here.ter( pos + point_south ) == t_sewage || @@ -2847,13 +2850,6 @@ void activity_handlers::clear_rubble_finish( player_activity *act, player *p ) here.maybe_trigger_trap( pos, *p, true ); } -void activity_handlers::meditate_finish( player_activity *act, player *p ) -{ - p->add_msg_if_player( m_good, _( "You pause to engage in spiritual contemplation." ) ); - p->add_morale( MORALE_FEELING_GOOD, 5, 10 ); - act->set_to_null(); -} - void activity_handlers::wear_do_turn( player_activity *act, player *p ) { activity_on_turn_wear( *act, *p ); @@ -3605,6 +3601,10 @@ void activity_handlers::hacksaw_finish( player_activity *act, player *p ) here.ter_set( pos, t_pit ); here.spawn_item( p->pos(), itype_spike, 19 ); here.spawn_item( p->pos(), itype_scrap, 8 ); + } else if( ter == t_retractable_gate_c || ter == t_retractable_gate_l ) { + here.ter_set( pos, t_strconc_floor ); + here.spawn_item( pos, itype_chain, rng( 1, 2 ) ); + here.spawn_item( pos, itype_wire, rng( 8, 22 ) ); } else if( ter == t_bars ) { if( here.ter( pos + point_east ) == t_sewage || here.ter( pos + point_south ) == t_sewage || @@ -3885,28 +3885,6 @@ void activity_handlers::fill_pit_finish( player_activity *act, player *p ) act->set_to_null(); } -void activity_handlers::play_with_pet_finish( player_activity *act, player *p ) -{ - p->add_morale( MORALE_PLAY_WITH_PET, rng( 3, 10 ), 10, 5_hours, 25_minutes ); - p->add_msg_if_player( m_good, _( "Playing with your %s has lifted your spirits a bit." ), - act->str_values[0] ); - act->set_to_null(); -} - -void activity_handlers::shaving_finish( player_activity *act, player *p ) -{ - p->add_msg_if_player( _( "You open up your kit and shave." ) ); - p->add_morale( MORALE_SHAVE, 8, 8, 240_minutes, 3_minutes ); - act->set_to_null(); -} - -void activity_handlers::haircut_finish( player_activity *act, player *p ) -{ - p->add_msg_if_player( _( "You give your hair a trim." ) ); - p->add_morale( MORALE_HAIRCUT, 3, 3, 480_minutes, 3_minutes ); - act->set_to_null(); -} - std::vector get_sorted_tiles_by_distance( const tripoint &abspos, const std::unordered_set &tiles ) { @@ -3985,7 +3963,7 @@ void activity_handlers::fertilize_plot_do_turn( player_activity *act, player *p auto check_fertilizer = [&]( bool ask_user = true ) -> void { if( act->str_values.empty() ) { - act->str_values.push_back( "" ); + act->str_values.emplace_back( "" ); } fertilizer = itype_id( act->str_values[0] ); @@ -4283,9 +4261,6 @@ void activity_handlers::spellcasting_finish( player_activity *act, player *p ) case magic_energy_type::hp: blood_magic( p, cost ); break; - case magic_energy_type::fatigue: - p->mod_fatigue( cost ); - break; case magic_energy_type::none: default: break; diff --git a/src/activity_handlers.h b/src/activity_handlers.h index 185258f1a009d..bfb9ff44bd3af 100644 --- a/src/activity_handlers.h +++ b/src/activity_handlers.h @@ -211,7 +211,6 @@ void gunmod_add_finish( player_activity *act, player *p ); void toolmod_add_finish( player_activity *act, player *p ); void clear_rubble_finish( player_activity *act, player *p ); void heat_item_finish( player_activity *act, player *p ); -void meditate_finish( player_activity *act, player *p ); void read_finish( player_activity *act, player *p ); void wait_finish( player_activity *act, player *p ); void wait_weather_finish( player_activity *act, player *p ); @@ -231,9 +230,6 @@ void chop_logs_finish( player_activity *act, player *p ); void chop_planks_finish( player_activity *act, player *p ); void jackhammer_finish( player_activity *act, player *p ); void fill_pit_finish( player_activity *act, player *p ); -void play_with_pet_finish( player_activity *act, player *p ); -void shaving_finish( player_activity *act, player *p ); -void haircut_finish( player_activity *act, player *p ); void robot_control_finish( player_activity *act, player *p ); void mind_splicer_finish( player_activity *act, player *p ); void spellcasting_finish( player_activity *act, player *p ); diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index c138f43973a08..651c6bfd93fd7 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -513,13 +513,13 @@ void activity_handlers::washing_finish( player_activity *act, player *p ) } std::vector comps; - comps.push_back( item_comp( itype_water, required.water ) ); - comps.push_back( item_comp( itype_water_clean, required.water ) ); + comps.emplace_back( itype_water, required.water ); + comps.emplace_back( itype_water_clean, required.water ); p->consume_items( comps, 1, is_liquid_crafting_component ); std::vector comps1; - comps1.push_back( item_comp( itype_soap, required.cleanser ) ); - comps1.push_back( item_comp( itype_detergent, required.cleanser ) ); + comps1.emplace_back( itype_soap, required.cleanser ); + comps1.emplace_back( itype_detergent, required.cleanser ); p->consume_items( comps1 ); p->add_msg_if_player( m_good, _( "You washed your items." ) ); @@ -1469,7 +1469,7 @@ static std::vector> requirements_map( player continue; } } - requirement_map.push_back( std::make_tuple( point_elem, map_elem.first, map_elem.second ) ); + requirement_map.emplace_back( point_elem, map_elem.first, map_elem.second ); } } // Ok we now have a list of all the items that match the requirements, their points, and a quantity for each one. @@ -1493,8 +1493,8 @@ static std::vector> requirements_map( player } if( item_quantity >= quantity_required ) { // it's just this spot that can fulfil the requirement on its own - final_map.push_back( std::make_tuple( pos_here, item_here, std::min( quantity_here, - quantity_required ) ) ); + final_map.emplace_back( pos_here, item_here, std::min( quantity_here, + quantity_required ) ); if( quantity_here >= quantity_required ) { line_found = true; break; @@ -1520,10 +1520,10 @@ static std::vector> requirements_map( player int quantity_here2 = std::get<2>( *it ); if( comp_elem.type == item_here2 ) { if( quantity_here2 >= remainder ) { - final_map.push_back( std::make_tuple( pos_here2, item_here2, remainder ) ); + final_map.emplace_back( pos_here2, item_here2, remainder ); line_found = true; } else { - final_map.push_back( std::make_tuple( pos_here2, item_here2, remainder ) ); + final_map.emplace_back( pos_here2, item_here2, remainder ); remainder -= quantity_here2; } } @@ -1551,8 +1551,8 @@ static std::vector> requirements_map( player } if( item_quantity >= quantity_required ) { // it's just this spot that can fulfil the requirement on its own - final_map.push_back( std::make_tuple( pos_here, item_here, std::min( quantity_here, - quantity_required ) ) ); + final_map.emplace_back( pos_here, item_here, std::min( quantity_here, + quantity_required ) ); if( quantity_here >= quantity_required ) { line_found = true; break; @@ -1578,10 +1578,10 @@ static std::vector> requirements_map( player int quantity_here2 = std::get<2>( *it ); if( comp_elem.type == item_here2 ) { if( quantity_here2 >= remainder ) { - final_map.push_back( std::make_tuple( pos_here2, item_here2, remainder ) ); + final_map.emplace_back( pos_here2, item_here2, remainder ); line_found = true; } else { - final_map.push_back( std::make_tuple( pos_here2, item_here2, remainder ) ); + final_map.emplace_back( pos_here2, item_here2, remainder ); remainder -= quantity_here2; } } @@ -1604,7 +1604,7 @@ static std::vector> requirements_map( player item test_item = item( item_here, calendar::turn_zero ); if( test_item.has_quality( tool_qual, qual_level ) ) { // it's just this spot that can fulfil the requirement on its own - final_map.push_back( std::make_tuple( pos_here, item_here, 1 ) ); + final_map.emplace_back( pos_here, item_here, 1 ); line_found = true; break; } @@ -1613,7 +1613,7 @@ static std::vector> requirements_map( player } } for( const std::tuple &elem : final_map ) { - add_msg_debug( "%s is fetching %s from x: %d y: %d ", p.disp_name(), + add_msg_debug( debugmode::DF_REQUIREMENTS_MAP, "%s is fetching %s from x: %d y: %d ", p.disp_name(), std::get<1>( elem ).str(), std::get<0>( elem ).x, std::get<0>( elem ).y ); } return final_map; @@ -1989,8 +1989,10 @@ void activity_on_turn_move_loot( player_activity &act, player &p ) // the boolean in this pair being true indicates the item is from a vehicle storage space auto items = std::vector>(); - vehicle *src_veh, *dest_veh; - int src_part, dest_part; + vehicle *src_veh; + vehicle *dest_veh; + int src_part; + int dest_part; //Check source for cargo part //map_stack and vehicle_stack are different types but inherit from item_stack @@ -2000,14 +2002,14 @@ void activity_on_turn_move_loot( player_activity &act, player &p ) src_veh = &vp->vehicle(); src_part = vp->part_index(); for( auto &it : src_veh->get_items( src_part ) ) { - items.push_back( std::make_pair( &it, true ) ); + items.emplace_back( &it, true ); } } else { src_veh = nullptr; src_part = -1; } for( item &it : here.i_at( src_loc ) ) { - items.push_back( std::make_pair( &it, false ) ); + items.emplace_back( &it, false ); } //Skip items that have already been processed @@ -2156,7 +2158,7 @@ static bool mine_activity( player &p, const tripoint &src_loc ) moves /= 2; } p.assign_activity( powered ? ACT_JACKHAMMER : ACT_PICKAXE, moves ); - p.activity.targets.push_back( item_location( p, chosen_item ) ); + p.activity.targets.emplace_back( p, chosen_item ); p.activity.placement = here.getabs( src_loc ); return true; @@ -2526,6 +2528,7 @@ static requirement_check_result generic_multi_activity_check_requirement( player // come back here after successfully fetching your stuff if( act_prev.coords.empty() ) { std::vector local_src_set; + local_src_set.reserve( src_set.size() ); for( const tripoint &elem : src_set ) { local_src_set.push_back( here.getlocal( elem ) ); } @@ -2645,7 +2648,7 @@ static bool generic_multi_activity_do( player &p, const activity_id &act_id, item *best_rod = p.best_quality_item( qual_FISHING ); p.assign_activity( ACT_FISH, to_moves( 5_hours ), 0, 0, best_rod->tname() ); - p.activity.targets.push_back( item_location( p, best_rod ) ); + p.activity.targets.emplace_back( p, best_rod ); p.activity.coord_set = g->get_fishable_locations( ACTIVITY_SEARCH_DISTANCE, src_loc ); return false; } else if( reason == do_activity_reason::NEEDS_MINING ) { diff --git a/src/advanced_inv.cpp b/src/advanced_inv.cpp index 11875e2343d8f..13a3c66e58278 100644 --- a/src/advanced_inv.cpp +++ b/src/advanced_inv.cpp @@ -336,7 +336,14 @@ void advanced_inventory::print_items( const advanced_inventory_pane &pane, bool const item &it = *sitem.items.front(); const bool selected = active && index == static_cast( i ); - nc_color thiscolor = active ? it.color_in_inventory() : norm; + nc_color thiscolor; + if( !active ) { + thiscolor = norm; + } else if( it.is_food_container() && !it.is_craft() && it.contents.num_item_stacks() == 1 ) { + thiscolor = it.contents.all_items_top().front()->color_in_inventory(); + } else { + thiscolor = it.color_in_inventory(); + } nc_color thiscolordark = c_dark_gray; nc_color print_color; @@ -932,7 +939,8 @@ bool advanced_inventory::move_all_items() std::vector target_items; std::vector target_items_favorites; std::vector quantities; - item_stack::iterator stack_begin, stack_end; + item_stack::iterator stack_begin; + item_stack::iterator stack_end; if( panes[src].in_vehicle() ) { vehicle_stack targets = sarea.veh->get_items( sarea.vstor ); stack_begin = targets.begin(); @@ -1008,7 +1016,8 @@ bool advanced_inventory::move_all_items() std::vector quantities; - item_stack::iterator stack_begin, stack_end; + item_stack::iterator stack_begin; + item_stack::iterator stack_end; if( panes[src].in_vehicle() ) { vehicle_stack targets = sarea.veh->get_items( sarea.vstor ); stack_begin = targets.begin(); diff --git a/src/anatomy.cpp b/src/anatomy.cpp index 12562640cd971..725f9069024d1 100644 --- a/src/anatomy.cpp +++ b/src/anatomy.cpp @@ -181,7 +181,7 @@ bodypart_id anatomy::select_body_part( int size_diff, int hit_roll ) const // Debug for seeing weights. for( const weighted_object &pr : hit_weights ) { - add_msg_debug( "%s = %.3f", pr.obj.obj().name, pr.weight ); + add_msg_debug( debugmode::DF_ANATOMY_BP, "%s = %.3f", pr.obj.obj().name, pr.weight ); } const bodypart_id *ret = hit_weights.pick(); @@ -190,6 +190,6 @@ bodypart_id anatomy::select_body_part( int size_diff, int hit_roll ) const return bodypart_str_id::NULL_ID().id(); } - add_msg_debug( "selected part: %s", ret->id().obj().name ); + add_msg_debug( debugmode::DF_ANATOMY_BP, "selected part: %s", ret->id().obj().name ); return *ret; } diff --git a/src/armor_layers.cpp b/src/armor_layers.cpp index 18a8611f5fa57..65583dffff262 100644 --- a/src/armor_layers.cpp +++ b/src/armor_layers.cpp @@ -330,40 +330,40 @@ std::vector clothing_flags_description( const item &worn_item ) std::vector description_stack; if( worn_item.has_flag( flag_FIT ) ) { - description_stack.push_back( _( "It fits you well." ) ); + description_stack.emplace_back( _( "It fits you well." ) ); } else if( worn_item.has_flag( flag_VARSIZE ) ) { - description_stack.push_back( _( "It could be refitted." ) ); + description_stack.emplace_back( _( "It could be refitted." ) ); } if( worn_item.has_flag( flag_HOOD ) ) { - description_stack.push_back( _( "It has a hood." ) ); + description_stack.emplace_back( _( "It has a hood." ) ); } if( worn_item.has_flag( flag_POCKETS ) ) { - description_stack.push_back( _( "It has pockets." ) ); + description_stack.emplace_back( _( "It has pockets." ) ); } if( worn_item.has_flag( flag_WATERPROOF ) ) { - description_stack.push_back( _( "It is waterproof." ) ); + description_stack.emplace_back( _( "It is waterproof." ) ); } if( worn_item.has_flag( flag_WATER_FRIENDLY ) ) { - description_stack.push_back( _( "It is water friendly." ) ); + description_stack.emplace_back( _( "It is water friendly." ) ); } if( worn_item.has_flag( flag_FANCY ) ) { - description_stack.push_back( _( "It looks fancy." ) ); + description_stack.emplace_back( _( "It looks fancy." ) ); } if( worn_item.has_flag( flag_SUPER_FANCY ) ) { - description_stack.push_back( _( "It looks really fancy." ) ); + description_stack.emplace_back( _( "It looks really fancy." ) ); } if( worn_item.has_flag( flag_FLOTATION ) ) { - description_stack.push_back( _( "You will not drown today." ) ); + description_stack.emplace_back( _( "You will not drown today." ) ); } if( worn_item.has_flag( flag_OVERSIZE ) ) { - description_stack.push_back( _( "It is very bulky." ) ); + description_stack.emplace_back( _( "It is very bulky." ) ); } if( worn_item.has_flag( flag_SWIM_GOGGLES ) ) { - description_stack.push_back( _( "It helps you to see clearly underwater." ) ); + description_stack.emplace_back( _( "It helps you to see clearly underwater." ) ); } if( worn_item.has_flag( flag_SEMITANGIBLE ) ) { - description_stack.push_back( _( "It can occupy the same space as other things." ) ); + description_stack.emplace_back( _( "It can occupy the same space as other things." ) ); } return description_stack; @@ -667,7 +667,8 @@ void player::sort_armor() } else if( rightListOffset + rightListLines > rightListSize ) { rightListOffset = rightListSize - rightListLines; } - int pos = 1, curr = 0; + int pos = 1; + int curr = 0; for( const bodypart_id &cover : rl ) { if( cover == bodypart_id( "bp_null" ) ) { continue; diff --git a/src/avatar.cpp b/src/avatar.cpp index ad261c4b715e6..9a1b9b3a8d87c 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -420,7 +420,7 @@ bool avatar::read( item &it, const bool continuous ) const int time_taken = time_to_read( it, *reader ); - add_msg_debug( "avatar::read: time_taken = %d", time_taken ); + add_msg_debug( debugmode::DF_AVATAR, "avatar::read: time_taken = %d", time_taken ); player_activity act( ACT_READ, time_taken, continuous ? activity.index : 0, reader->getID().get_value() ); act.targets.emplace_back( item_location( *this, &it ) ); @@ -745,11 +745,11 @@ void avatar::do_read( item &book ) player *n = g->find_npc( character_id( activity.values[i] ) ); if( n != nullptr ) { const std::string &s = activity.get_str_value( i, "1" ); - learners.push_back( { n, strtod( s.c_str(), nullptr ) } ); + learners.emplace_back( n, strtod( s.c_str(), nullptr ) ); } // Otherwise they must have died/teleported or something } - learners.push_back( { this, 1.0 } ); + learners.emplace_back( this, 1.0 ); //whether to continue reading or not bool continuous = false; // NPCs who learned a little about the skill @@ -872,7 +872,7 @@ void avatar::do_read( item &book ) skill_id skill_used = style_to_learn->primary_skill; int difficulty = std::max( 1, style_to_learn->learn_difficulty ); difficulty = std::max( 1, 20 + difficulty * 2 - get_skill_level( skill_used ) * 2 ); - add_msg_debug( _( "Chance to learn one in: %d" ), difficulty ); + add_msg_debug( debugmode::DF_AVATAR, _( "Chance to learn one in: %d" ), difficulty ); if( one_in( difficulty ) ) { m->second.call( *this, book, false, pos() ); @@ -1526,6 +1526,13 @@ void avatar::toggle_crouch_mode() } } +void avatar::activate_crouch_mode() +{ + if( !is_crouching() ) { + set_movement_mode( move_mode_id( "crouch" ) ); + } +} + void avatar::reset_move_mode() { if( !is_walking() ) { @@ -1593,7 +1600,7 @@ bool avatar::wield( item &target, const int obtain_cost ) target.on_takeoff( *this ); } - add_msg_debug( "wielding took %d moves", mv ); + add_msg_debug( debugmode::DF_AVATAR, "wielding took %d moves", mv ); moves -= mv; if( has_item( target ) ) { diff --git a/src/avatar.h b/src/avatar.h index 1fa81646c7d7e..427aac2176d3c 100644 --- a/src/avatar.h +++ b/src/avatar.h @@ -237,6 +237,8 @@ class avatar : public player void toggle_run_mode(); // Toggles crouching on/off. void toggle_crouch_mode(); + // Activate crouch mode if not in crouch mode. + void activate_crouch_mode(); bool wield( item_location target ); bool wield( item &target ) override; diff --git a/src/ballistics.cpp b/src/ballistics.cpp index d8f820baf29bc..cc67f832ed393 100644 --- a/src/ballistics.cpp +++ b/src/ballistics.cpp @@ -282,7 +282,8 @@ dealt_projectile_attack projectile_attack( const projectile &proj_arg, const tri trajectory = here.find_clear_path( source, target ); } - add_msg_debug( "missed_by_tiles: %.2f; missed_by: %.2f; target (orig/hit): %d,%d,%d/%d,%d,%d", + add_msg_debug( debugmode::DF_BALLISTIC, + "missed_by_tiles: %.2f; missed_by: %.2f; target (orig/hit): %d,%d,%d/%d,%d,%d", aim.missed_by_tiles, aim.missed_by, target_arg.x, target_arg.y, target_arg.z, target.x, target.y, target.z ); diff --git a/src/basecamp.cpp b/src/basecamp.cpp index f727c4f0d8a8c..11fe7a3f52e8e 100644 --- a/src/basecamp.cpp +++ b/src/basecamp.cpp @@ -219,7 +219,7 @@ std::string basecamp::om_upgrade_description( const std::string &bldg, bool trun std::string comp; for( auto &elem : component_print_buffer ) { - comp = comp + elem + "\n"; + str_append( comp, elem, "\n" ); } comp = string_format( _( "Notes:\n%s\n\nSkills used: %s\n%s\n" ), making.description, making.required_all_skills_string(), comp ); diff --git a/src/bionics.cpp b/src/bionics.cpp index 20a28a9ef7bcb..a064a5ce3877e 100644 --- a/src/bionics.cpp +++ b/src/bionics.cpp @@ -192,6 +192,7 @@ static const trait_id trait_PROF_MED( "PROF_MED" ); static const trait_id trait_THRESH_MEDICAL( "THRESH_MEDICAL" ); static const json_character_flag json_flag_BIONIC_GUN( "BIONIC_GUN" ); +static const json_character_flag json_flag_BIONIC_NPC_USABLE( "BIONIC_NPC_USABLE" ); static const json_character_flag json_flag_BIONIC_WEAPON( "BIONIC_WEAPON" ); static const json_character_flag json_flag_BIONIC_TOGGLED( "BIONIC_TOGGLED" ); @@ -305,7 +306,6 @@ void bionic_data::load( const JsonObject &jsobj, const std::string & ) optional( jsobj, was_loaded, "fake_item", fake_item, itype_id() ); - optional( jsobj, was_loaded, "enchantments", enchantments ); optional( jsobj, was_loaded, "spell_on_activation", spell_on_activate ); optional( jsobj, was_loaded, "weight_capacity_modifier", weight_capacity_modifier, 1.0f ); @@ -317,6 +317,7 @@ void bionic_data::load( const JsonObject &jsobj, const std::string & ) optional( jsobj, was_loaded, "learned_spells", learned_spells ); optional( jsobj, was_loaded, "learned_proficiencies", proficiencies ); optional( jsobj, was_loaded, "canceled_mutations", canceled_mutations ); + optional( jsobj, was_loaded, "mutation_conflicts", mutation_conflicts ); optional( jsobj, was_loaded, "included_bionics", included_bionics ); optional( jsobj, was_loaded, "included", included ); optional( jsobj, was_loaded, "upgraded_bionic", upgraded_bionic ); @@ -329,6 +330,12 @@ void bionic_data::load( const JsonObject &jsobj, const std::string & ) optional( jsobj, was_loaded, "vitamin_absorb_mod", vitamin_absorb_mod, 1.0f ); + int enchant_num = 0; + for( JsonValue jv : jsobj.get_array( "enchantments" ) ) { + std::string enchant_name = "INLINE_ENCH_" + name + "_" + std::to_string( enchant_num++ ); + enchantments.push_back( enchantment::load_inline_enchantment( jv, "", enchant_name ) ); + } + if( jsobj.has_array( "stat_bonus" ) ) { // clear data first so that copy-from can override it stat_bonus.clear(); @@ -2192,13 +2199,13 @@ bool Character::uninstall_bionic( const bionic_id &b_id, player &installer, bool activity.values.push_back( success ); activity.values.push_back( units::to_kilojoule( b_id->capacity ) ); activity.values.push_back( pl_skill ); - activity.str_values.push_back( "uninstall" ); + activity.str_values.emplace_back( "uninstall" ); activity.str_values.push_back( b_id.str() ); - activity.str_values.push_back( "" ); // installer_name is unused for uninstall + activity.str_values.emplace_back( "" ); // installer_name is unused for uninstall if( autodoc ) { - activity.str_values.push_back( "true" ); + activity.str_values.emplace_back( "true" ); } else { - activity.str_values.push_back( "false" ); + activity.str_values.emplace_back( "false" ); } for( const std::pair &elem : b_id->occupied_bodyparts ) { add_effect( effect_under_operation, difficulty * 20_minutes, elem.first.id(), true, difficulty ); @@ -2316,6 +2323,54 @@ bool Character::uninstall_bionic( const bionic &target_cbm, monster &installer, return false; } +ret_val Character::is_installable( const item_location &loc, const bool by_autodoc ) const +{ + const item *it = loc.get_item(); + const itype *itemtype = it->type; + const bionic_id &bid = itemtype->bionic->id; + + const auto has_trait_lambda = [this]( const trait_id & candidate ) { + return has_trait( candidate ); + }; + + if( it->has_flag( flag_FILTHY ) ) { + // NOLINTNEXTLINE(cata-text-style): single space after the period for symmetry + const std::string msg = by_autodoc ? _( "/!\\ CBM is highly contaminated. /!\\" ) : + _( "CBM is filthy." ); + return ret_val::make_failure( msg ); + } else if( it->has_flag( flag_NO_STERILE ) ) { + const std::string msg = by_autodoc ? + // NOLINTNEXTLINE(cata-text-style): single space after the period for symmetry + _( "/!\\ CBM is not sterile. /!\\ Please use autoclave to sterilize." ) : + _( "CBM is not sterile." ); + return ret_val::make_failure( msg ); + } else if( it->has_fault( fault_id( "fault_bionic_salvaged" ) ) ) { + return ret_val::make_failure( _( "CBM already deployed. Please reset to factory state." ) ); + } else if( has_bionic( bid ) ) { + return ret_val::make_failure( _( "CBM is already installed." ) ); + } else if( !can_install_cbm_on_bp( get_occupied_bodyparts( bid ) ) ) { + return ret_val::make_failure( _( "CBM not compatible with patient's body." ) ); + } else if( std::any_of( bid->mutation_conflicts.begin(), bid->mutation_conflicts.end(), + has_trait_lambda ) ) { + return ret_val::make_failure( _( "CBM not compatible with patient's body." ) ); + } else if( bid->upgraded_bionic && + !has_bionic( bid->upgraded_bionic ) && + it->is_upgrade() ) { + return ret_val::make_failure( _( "No base version installed." ) ); + } else if( std::any_of( bid->available_upgrades.begin(), + bid->available_upgrades.end(), + std::bind( &Character::has_bionic, this, + std::placeholders::_1 ) ) ) { + return ret_val::make_failure( _( "Superior version installed." ) ); + } else if( is_npc() && !bid->has_flag( json_flag_BIONIC_NPC_USABLE ) ) { + return ret_val::make_failure( _( "CBM not compatible with patient." ) ); + } else if( units::energy_max - get_max_power_level() < bid->capacity ) { + return ret_val::make_failure( _( "Max power capacity already reached." ) ); + } + + return ret_val::make_success( std::string() ); +} + bool Character::can_install_bionics( const itype &type, Character &installer, bool autodoc, int skill_level ) { @@ -2431,18 +2486,18 @@ bool Character::install_bionics( const itype &type, player &installer, bool auto activity.values.push_back( success ); activity.values.push_back( units::to_millijoule( bioid->capacity ) ); activity.values.push_back( pl_skill ); - activity.str_values.push_back( "install" ); + activity.str_values.emplace_back( "install" ); activity.str_values.push_back( bioid.str() ); if( installer.has_trait( trait_PROF_MED ) || installer.has_trait( trait_PROF_AUTODOC ) ) { activity.str_values.push_back( installer.disp_name( true ) ); } else { - activity.str_values.push_back( "NOT_MED" ); + activity.str_values.emplace_back( "NOT_MED" ); } if( autodoc ) { - activity.str_values.push_back( "true" ); + activity.str_values.emplace_back( "true" ); } else { - activity.str_values.push_back( "false" ); + activity.str_values.emplace_back( "false" ); } for( const std::pair &elem : bioid->occupied_bodyparts ) { add_effect( effect_under_operation, difficulty * 20_minutes, elem.first.id(), true, difficulty ); @@ -2709,6 +2764,7 @@ void Character::add_bionic( const bionic_id &b ) if( !b->enchantments.empty() ) { recalculate_enchantment_cache(); } + effect_on_conditions::process_reactivate(); } void Character::remove_bionic( const bionic_id &b ) @@ -2750,6 +2806,7 @@ void Character::remove_bionic( const bionic_id &b ) if( !b->enchantments.empty() ) { recalculate_enchantment_cache(); } + effect_on_conditions::process_reactivate(); } int Character::num_bionics() const diff --git a/src/bionics.h b/src/bionics.h index 8f18e22307110..0dfee95903409 100644 --- a/src/bionics.h +++ b/src/bionics.h @@ -113,6 +113,10 @@ struct bionic_data { * E.g. enhanced optic bionic may cancel HYPEROPIC trait. */ std::vector canceled_mutations; + /** + * Mutations/traits that prevent installing this CBM + */ + std::set mutation_conflicts; /** * The spells you learn when you install this bionic, and what level you learn them at. diff --git a/src/bionics_ui.cpp b/src/bionics_ui.cpp index f02653a929e2a..96c6b08f17428 100644 --- a/src/bionics_ui.cpp +++ b/src/bionics_ui.cpp @@ -314,10 +314,10 @@ static std::string build_bionic_poweronly_string( const bionic &bio ) bio_data.charge_time ) ); } if( bio_data.has_flag( STATIC( json_character_flag( "BIONIC_TOGGLED" ) ) ) ) { - properties.push_back( bio.powered ? _( "ON" ) : _( "OFF" ) ); + properties.emplace_back( bio.powered ? _( "ON" ) : _( "OFF" ) ); } if( bio.incapacitated_time > 0_turns ) { - properties.push_back( _( "(incapacitated)" ) ); + properties.emplace_back( _( "(incapacitated)" ) ); } if( bio.get_safe_fuel_thresh() > 0 && ( !bio.info().fuel_opts.empty() || bio.info().is_remote_fueled ) ) { diff --git a/src/cata_io.h b/src/cata_io.h index 19f3aeea81bba..93c2ed4d36348 100644 --- a/src/cata_io.h +++ b/src/cata_io.h @@ -292,6 +292,26 @@ class JsonObjectInputArchive : public JsonObject load( ident ); return true; } + /** + * The 'I-give-up' template for gun variant data + */ + template + bool io( const std::string &name, T &pointer, + const LoadFunc &load, + const SaveFunc &save, bool required = false ) { + // Only used by the matching function in the output archive classes. + ( void ) save; + std::string ident; + if( !io( name, ident ) ) { + if( required ) { + JsonObject::throw_error( std::string( "required member is missing: " ) + name ); + } + pointer = nullptr; + return false; + } + load( ident ); + return true; + } template bool io( const std::string &name, T *&pointer, const std::function &load, @@ -421,6 +441,21 @@ class JsonObjectOutputArchive } return io( name, save( *pointer ) ); } + /** + * The I-give-up load function for gun variants + */ + template + bool io( const std::string &name, const T &pointer, + const LoadFunc &, + const SaveFunc &save, bool required = false ) { + if( pointer == nullptr ) { + if( required ) { + throw JsonError( "a required member is null: " + name ); + } + return false; + } + return io( name, save( pointer ) ); + } template bool io( const std::string &name, const T *pointer, const std::function &load, diff --git a/src/cata_tiles.cpp b/src/cata_tiles.cpp index 9b13ffba2d925..0297a95f5ac6c 100644 --- a/src/cata_tiles.cpp +++ b/src/cata_tiles.cpp @@ -91,7 +91,7 @@ static const std::array multitile_keys = {{ }; static const std::string empty_string; -static const std::array TILE_CATEGORY_IDS = {{ +static const std::array TILE_CATEGORY_IDS = {{ "", // C_NONE, "vehicle_part", // C_VEHICLE_PART, "terrain", // C_TERRAIN, @@ -104,6 +104,7 @@ static const std::array TILE_CATEGORY_IDS = {{ "bullet", // C_BULLET, "hit_entity", // C_HIT_ENTITY, "weather", // C_WEATHER, + "overmap_terrain" } }; @@ -926,7 +927,7 @@ void tileset_loader::load_tilejson_from_file( const JsonObject &config ) // fetch additional tiles for( const JsonObject subentry : entry.get_array( "additional_tiles" ) ) { const std::string s_id = subentry.get_string( "id" ); - const std::string m_id = t_id + "_" + s_id; + const std::string m_id = str_cat( t_id, "_", s_id ); tile_type &curr_subtile = load_tile( subentry, m_id ); curr_subtile.offset = sprite_offset; curr_subtile.rotates = true; @@ -1040,7 +1041,7 @@ struct tile_render_info { lit_level ll; bool invisible[5]; tile_render_info( const tripoint &pos, const int height_3d, const lit_level ll, - const bool ( &invisible )[5] ) + const bool( &invisible )[5] ) : pos( pos ), height_3d( height_3d ), ll( ll ) { std::copy_n( invisible, 5, this->invisible ); } @@ -1095,10 +1096,9 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int avatar &you = get_avatar(); //limit the render area to maximum view range (121x121 square centered on player) - const int min_visible_x = you.posx() % SEEX; - const int min_visible_y = you.posy() % SEEY; - const int max_visible_x = ( you.posx() % SEEX ) + ( MAPSIZE - 1 ) * SEEX; - const int max_visible_y = ( you.posy() % SEEY ) + ( MAPSIZE - 1 ) * SEEY; + const point min_visible( you.posx() % SEEX, you.posy() % SEEY ); + const point max_visible( ( you.posx() % SEEX ) + ( MAPSIZE - 1 ) * SEEX, + ( you.posy() % SEEY ) + ( MAPSIZE - 1 ) * SEEY ); const level_cache &ch = here.access_cache( center.z ); @@ -1152,8 +1152,8 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int } } const auto apply_visible = [&]( const tripoint & np, const level_cache & ch, map & here ) { - return np.y < min_visible_y || np.y > max_visible_y || - np.x < min_visible_x || np.x > max_visible_x || + return np.y < min_visible.y || np.y > max_visible.y || + np.x < min_visible.x || np.x > max_visible.x || would_apply_vision_effects( here.get_visibility( ch.visibility_cache[np.x][np.y], cache ) ); }; @@ -1162,8 +1162,7 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int std::vector draw_points; draw_points.reserve( max_col ); for( int col = min_col; col < max_col; col ++ ) { - int temp_x; - int temp_y; + point temp; if( iso_mode ) { // in isometric, rows and columns represent a checkerboard screen space, // and we place the appropriate tile in valid squares by getting position @@ -1171,13 +1170,13 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int if( modulo( row - s.y / 2, 2 ) != modulo( col - s.x / 2, 2 ) ) { continue; } - temp_x = divide_round_down( col - row - s.x / 2 + s.y / 2, 2 ) + o.x; - temp_y = divide_round_down( row + col - s.y / 2 - s.x / 2, 2 ) + o.y; + temp.x = divide_round_down( col - row - s.x / 2 + s.y / 2, 2 ) + o.x; + temp.y = divide_round_down( row + col - s.y / 2 - s.x / 2, 2 ) + o.y; } else { - temp_x = col + o.x; - temp_y = row + o.y; + temp.x = col + o.x; + temp.y = row + o.y; } - const tripoint pos( temp_x, temp_y, center.z ); + const tripoint pos( temp, center.z ); const int &x = pos.x; const int &y = pos.y; @@ -1186,7 +1185,7 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int bool invisible[5]; invisible[0] = false; - if( y < min_visible_y || y > max_visible_y || x < min_visible_x || x > max_visible_x ) { + if( y < min_visible.y || y > max_visible.y || x < min_visible.x || x > max_visible.x ) { if( has_memory_at( pos ) ) { ll = lit_level::MEMORIZED; invisible[0] = true; @@ -1408,8 +1407,8 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int void_monster_override(); //Memorize everything the character just saw even if it wasn't displayed. - for( int mem_y = min_visible_y; mem_y <= max_visible_y; mem_y++ ) { - for( int mem_x = min_visible_x; mem_x <= max_visible_x; mem_x++ ) { + for( int mem_y = min_visible.y; mem_y <= max_visible.y; mem_y++ ) { + for( int mem_x = min_visible.x; mem_x <= max_visible.x; mem_x++ ) { half_open_rectangle already_drawn( point( min_col, min_row ), point( max_col, max_row ) ); if( iso_mode ) { @@ -1679,12 +1678,15 @@ bool cata_tiles::find_overlay_looks_like( const bool male, const std::string &ov } for( int cnt = 0; cnt < 10 && !looks_like.empty(); cnt++ ) { - draw_id = ( male ? "overlay_male_" : "overlay_female_" ) + over_type + looks_like; + draw_id.clear(); + str_append( draw_id, + ( male ? "overlay_male_" : "overlay_female_" ), over_type, looks_like ); if( tileset_ptr->find_tile_type( draw_id ) ) { exists = true; break; } - draw_id = "overlay_" + over_type + looks_like; + draw_id.clear(); + str_append( draw_id, "overlay_", over_type, looks_like ); if( tileset_ptr->find_tile_type( draw_id ) ) { exists = true; break; @@ -1800,6 +1802,15 @@ bool cata_tiles::draw_from_id_string( const std::string &id, TILE_CATEGORY categ } sym = tmp.symbol().empty() ? ' ' : tmp.symbol().front(); col = tmp.color(); + } else if( category == C_OVERMAP_TERRAIN ) { + const oter_str_id tmp( id ); + if( tmp.is_valid() ) { + sym = tmp->get_symbol().front(); + col = tmp->get_color(); + } + } else if( category == C_OVERMAP_NOTE ) { + sym = id[5]; + col = color_from_string( id.substr( 7, id.length() - 1 ) ); } // Special cases for walls switch( sym ) { @@ -1970,6 +1981,7 @@ bool cata_tiles::draw_from_id_string( const std::string &id, TILE_CATEGORY categ case C_BULLET: case C_HIT_ENTITY: case C_WEATHER: + case C_OVERMAP_TERRAIN: // TODO: come up with ways to make random sprites consistent for these types break; case C_MONSTER: @@ -2007,7 +2019,9 @@ bool cata_tiles::draw_from_id_string( const std::string &id, TILE_CATEGORY categ }; // use a fair mix function to turn the "random" seed into a random int // taken from public domain code at http://burtleburtle.net/bob/c/lookup3.c 2015/12/11 - unsigned int a = seed, b = -seed, c = seed * seed; + unsigned int a = seed; + unsigned int b = -seed; + unsigned int c = seed * seed; c ^= b; c -= rot32( b, 14 ); a ^= c; @@ -3508,8 +3522,7 @@ void cata_tiles::draw_sct_frame( std::multimap &overlay_s const point iD( iter->getPosX(), iter->getPosY() ); const int full_text_length = utf8_width( iter->getText() ); - int iOffsetX = 0; - int iOffsetY = 0; + point iOffset; for( int j = 0; j < 2; ++j ) { std::string sText = iter->getText( ( j == 0 ) ? "first" : "second" ); @@ -3532,14 +3545,14 @@ void cata_tiles::draw_sct_frame( std::multimap &overlay_s if( tileset_ptr->find_tile_type( generic_id ) ) { draw_from_id_string( generic_id, C_NONE, empty_string, - iD + tripoint( iOffsetX, iOffsetY, player_pos.z ), + iD + tripoint( iOffset, player_pos.z ), 0, 0, lit_level::LIT, false ); } if( tile_iso ) { - iOffsetY++; + iOffset.y++; } - iOffsetX++; + iOffset.x++; } } } @@ -3928,6 +3941,7 @@ std::vector cata_tiles::build_display_list() }; int numdisplays = SDL_GetNumVideoDisplays(); + display_names.reserve( numdisplays ); for( int i = 0 ; i < numdisplays ; i++ ) { display_names.emplace_back( std::to_string( i ), no_translation( SDL_GetDisplayName( i ) ) ); } diff --git a/src/cata_tiles.h b/src/cata_tiles.h index 6787d174ac4d4..f5fe31e75a727 100644 --- a/src/cata_tiles.h +++ b/src/cata_tiles.h @@ -71,6 +71,8 @@ enum TILE_CATEGORY { C_BULLET, C_HIT_ENTITY, C_WEATHER, + C_OVERMAP_TERRAIN, + C_OVERMAP_NOTE }; class tile_lookup_res @@ -320,6 +322,7 @@ class cata_tiles void draw( const point &dest, const tripoint ¢er, int width, int height, std::multimap &overlay_strings, color_block_overlay_container &color_blocks ); + void draw_om( const point &dest, const tripoint_abs_omt ¢er_abs_omt, bool blink ); /** Minimap functionality */ void draw_minimap( const point &dest, const tripoint ¢er, int width, int height ); @@ -535,6 +538,8 @@ class cata_tiles point player_to_screen( const point & ) const; static std::vector build_renderer_list(); static std::vector build_display_list(); + private: + int get_omt_rotation( std::string &id ); protected: template void tile_loading_report( const maptype &tiletypemap, TILE_CATEGORY category, diff --git a/src/cata_utility.cpp b/src/cata_utility.cpp index 55423a14645a9..93926db360f9f 100644 --- a/src/cata_utility.cpp +++ b/src/cata_utility.cpp @@ -214,6 +214,11 @@ double temp_to_kelvin( double fahrenheit ) return temp_to_celsius( fahrenheit ) + 273.15; } +double celsius_to_kelvin( double celsius ) +{ + return celsius + 273.15; +} + double kelvin_to_fahrenheit( double kelvin ) { return 1.8 * ( kelvin - 273.15 ) + 32; diff --git a/src/cata_utility.h b/src/cata_utility.h index 182156cb70c1d..490967d3f71a6 100644 --- a/src/cata_utility.h +++ b/src/cata_utility.h @@ -199,6 +199,13 @@ double temp_to_celsius( double fahrenheit ); */ double temp_to_kelvin( double fahrenheit ); +/** + * Convert a temperature from degrees Celsius to Kelvin. + * + * @return Temperature in degrees K. + */ +double celsius_to_kelvin( double celsius ); + /** * Convert a temperature from Kelvin to degrees Fahrenheit. * @@ -435,6 +442,36 @@ bool return_true( const T & ) */ std::string join( const std::vector &strings, const std::string &joiner ); +/** + * Append all arguments after the first to the first. + * + * This provides a way to append several strings to a single root string + * in a single line without an expression like 'a += b + c' which can cause an + * unnecessary allocation in the 'b + c' expression. + */ +template +std::string &str_append( std::string &root, T &&...a ) +{ + // Using initializer list as a poor man's fold expression until C++17. + static_cast( + std::array { { + ( root.append( std::forward( a ) ), false )... + } + } ); + return root; +} + +/** + * Concatenates a bunch of strings with append, to minimze unnecessary + * allocations + */ +template +std::string str_cat( T0 &&a0, T &&...a ) +{ + std::string result( std::forward( a0 ) ); + return str_append( result, std::forward( a )... ); +} + /** * Erases elements from a set that match given predicate function. * Will work on vector, albeit not optimally performance-wise. diff --git a/src/character.cpp b/src/character.cpp index 6f29471f0d1ec..6645e87d354ed 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -224,7 +224,6 @@ static const itype_id itype_rm13_armor_on( "rm13_armor_on" ); static const itype_id itype_rope_6( "rope_6" ); static const itype_id itype_snare_trigger( "snare_trigger" ); static const itype_id itype_string_36( "string_36" ); -static const itype_id itype_toolset( "toolset" ); static const itype_id itype_UPS( "UPS" ); static const itype_id itype_UPS_off( "UPS_off" ); @@ -253,8 +252,6 @@ static const trait_id trait_CHITIN_FUR3( "CHITIN_FUR3" ); static const trait_id trait_CLUMSY( "CLUMSY" ); static const trait_id trait_DEBUG_NODMG( "DEBUG_NODMG" ); static const trait_id trait_DEFT( "DEFT" ); -static const trait_id trait_EASYSLEEPER( "EASYSLEEPER" ); -static const trait_id trait_EASYSLEEPER2( "EASYSLEEPER2" ); static const trait_id trait_EATHEALTH( "EATHEALTH" ); static const trait_id trait_FAT( "FAT" ); static const trait_id trait_FELINE_FUR( "FELINE_FUR" ); @@ -276,23 +273,17 @@ static const trait_id trait_WOOLALLERGY( "WOOLALLERGY" ); static const bionic_id bio_ads( "bio_ads" ); static const bionic_id bio_blaster( "bio_blaster" ); -static const bionic_id bio_climate( "bio_climate" ); static const bionic_id bio_voice( "bio_voice" ); static const bionic_id bio_flashlight( "bio_flashlight" ); static const bionic_id bio_gills( "bio_gills" ); static const bionic_id bio_ground_sonar( "bio_ground_sonar" ); -static const bionic_id bio_heatsink( "bio_heatsink" ); static const bionic_id bio_hydraulics( "bio_hydraulics" ); static const bionic_id bio_jointservo( "bio_jointservo" ); -static const bionic_id bio_laser( "bio_laser" ); static const bionic_id bio_leukocyte( "bio_leukocyte" ); -static const bionic_id bio_lighter( "bio_lighter" ); static const bionic_id bio_memory( "bio_memory" ); static const bionic_id bio_railgun( "bio_railgun" ); -static const bionic_id bio_recycler( "bio_recycler" ); static const bionic_id bio_shock_absorber( "bio_shock_absorber" ); static const bionic_id bio_tattoo_led( "bio_tattoo_led" ); -static const bionic_id bio_tools( "bio_tools" ); static const bionic_id bio_ups( "bio_ups" ); // Aftershock stuff! static const bionic_id afs_bio_linguistic_coprocessor( "afs_bio_linguistic_coprocessor" ); @@ -383,10 +374,12 @@ static const json_character_flag json_flag_ALARMCLOCK( "ALARMCLOCK" ); static const json_character_flag json_flag_ACID_IMMUNE( "ACID_IMMUNE" ); static const json_character_flag json_flag_BASH_IMMUNE( "BASH_IMMUNE" ); static const json_character_flag json_flag_BIO_IMMUNE( "BIO_IMMUNE" ); +static const json_character_flag json_flag_BIONIC_TOGGLED( "BIONIC_TOGGLED" ); static const json_character_flag json_flag_BLIND( "BLIND" ); static const json_character_flag json_flag_BULLET_IMMUNE( "BULLET_IMMUNE" ); static const json_character_flag json_flag_CLAIRVOYANCE( "CLAIRVOYANCE" ); static const json_character_flag json_flag_CLAIRVOYANCE_PLUS( "CLAIRVOYANCE_PLUS" ); +static const json_character_flag json_flag_CLIMATE_CONTROL( "CLIMATE_CONTROL" ); static const json_character_flag json_flag_COLD_IMMUNE( "COLD_IMMUNE" ); static const json_character_flag json_flag_CUT_IMMUNE( "CUT_IMMUNE" ); static const json_character_flag json_flag_DEAF( "DEAF" ); @@ -394,8 +387,10 @@ static const json_character_flag json_flag_ELECTRIC_IMMUNE( "ELECTRIC_IMMUNE" ); static const json_character_flag json_flag_ENHANCED_VISION( "ENHANCED_VISION" ); static const json_character_flag json_flag_EYE_MEMBRANE( "EYE_MEMBRANE" ); static const json_character_flag json_flag_HEATPROOF( "HEATPROOF" ); +static const json_character_flag json_flag_HEATSINK( "HEATSINK" ); static const json_character_flag json_flag_IMMUNE_HEARING_DAMAGE( "IMMUNE_HEARING_DAMAGE" ); static const json_character_flag json_flag_INFRARED( "INFRARED" ); +static const json_character_flag json_flag_INVISIBLE( "INVISIBLE" ); static const json_character_flag json_flag_NIGHT_VISION( "NIGHT_VISION" ); static const json_character_flag json_flag_NO_DISEASE( "NO_DISEASE" ); static const json_character_flag json_flag_NO_MINIMAL_HEALING( "NO_MINIMAL_HEALING" ); @@ -490,8 +485,8 @@ Character::Character() : // *INDENT-ON* Character::~Character() = default; -Character::Character( Character && ) = default; -Character &Character::operator=( Character && ) = default; +Character::Character( Character && ) noexcept( map_is_noexcept ) = default; +Character &Character::operator=( Character && ) noexcept( list_is_noexcept ) = default; void Character::setID( character_id i, bool force ) { @@ -1091,9 +1086,9 @@ bool Character::check_outbounds_activity( const player_activity &act, bool check } activity = player_activity(); } - add_msg_debug( - "npc %s at pos %d %d, activity target is not inbounds at %d %d therefore activity was stashed", - disp_name(), pos().x, pos().y, act.placement.x, act.placement.y ); + add_msg_debug( debugmode::DF_CHARACTER, + "npc %s at pos %d %d, activity target is not inbounds at %d %d therefore activity was stashed", + disp_name(), pos().x, pos().y, act.placement.x, act.placement.y ); return true; } return false; @@ -1119,7 +1114,7 @@ void Character::mount_creature( monster &z ) tripoint pnt = z.pos(); shared_ptr_fast mons = g->shared_from( z ); if( mons == nullptr ) { - add_msg_debug( "mount_creature(): monster not found in critter_tracker" ); + add_msg_debug( debugmode::DF_CHARACTER, "mount_creature(): monster not found in critter_tracker" ); return; } add_effect( effect_riding, 1_turns, true ); @@ -1310,7 +1305,8 @@ void Character::forced_dismount() } add_effect( effect_downed, 5_turns, true ); } else { - add_msg_debug( "Forced_dismount could not find a square to deposit player" ); + add_msg_debug( debugmode::DF_CHARACTER, + "Forced_dismount could not find a square to deposit player" ); } if( is_avatar() ) { avatar &player_character = get_avatar(); @@ -1334,7 +1330,7 @@ void Character::forced_dismount() void Character::dismount() { if( !is_mounted() ) { - add_msg_debug( "dismount called when not riding" ); + add_msg_debug( debugmode::DF_CHARACTER, "dismount called when not riding" ); return; } if( const cata::optional pnt = choose_adjacent( _( "Dismount where?" ) ) ) { @@ -2673,7 +2669,7 @@ int Character::get_mod_stat_from_bionic( const character_stat &Stat ) const return ret; } -int Character::get_standard_stamina_cost( item *thrown_item ) +int Character::get_standard_stamina_cost( const item *thrown_item ) const { // Previously calculated as 2_gram * std::max( 1, str_cur ) // using 16_gram normalizes it to 8 str. Same effort expenditure @@ -3500,7 +3496,7 @@ std::vector Character::find_reloadables() node->remaining_ammo_capacity() > 0; } if( reloadable ) { - reloadables.push_back( item_location( *this, node ) ); + reloadables.emplace_back( *this, node ); } return VisitResponse::NEXT; } ); @@ -3883,14 +3879,36 @@ ret_val Character::can_wear( const item &it, bool with_equip_change ) cons : string_format( _( "%s doesn't have a hand free to wear that." ), name ) ) ); } + const bool this_restricts_only_one = it.has_flag( flag_id( "ONE_PER_LAYER" ) ); + std::map sidedness; + sidedness[side::BOTH] = false; + sidedness[side::LEFT] = false; + sidedness[side::RIGHT] = false; + const auto sidedness_conflicts = [&sidedness]( side s ) -> bool { + const bool ret = sidedness[s]; + sidedness[s] = true; + if( sidedness[side::LEFT] && sidedness[side::RIGHT] ) + { + sidedness[side::BOTH] = true; + return true; + } + return ret; + }; for( const item &i : worn ) { if( i.has_flag( flag_ONLY_ONE ) && i.typeId() == it.typeId() ) { return ret_val::make_failure( _( "Can't wear more than one %s!" ), it.tname() ); } + + if( this_restricts_only_one || i.has_flag( flag_id( "ONE_PER_LAYER" ) ) ) { + cata::optional overlaps = it.covers_overlaps( i ); + if( overlaps && sidedness_conflicts( *overlaps ) ) { + return ret_val::make_failure( _( "%1$s conflicts with %2$s!" ), it.tname(), i.tname() ); + } + } } if( amount_worn( it.typeId() ) >= MAX_WORN_PER_TYPE ) { - return ret_val::make_failure( _( "Can't wear %i or more %s at once." ), + return ret_val::make_failure( _( "Can't wear %1$i or more %2$s at once." ), MAX_WORN_PER_TYPE + 1, it.tname( MAX_WORN_PER_TYPE + 1 ) ); } @@ -4389,8 +4407,7 @@ bool Character::has_nv() if( !nv_cached ) { nv_cached = true; nv = ( worn_with_flag( flag_GNV_EFFECT ) || - has_flag( json_flag_NIGHT_VISION ) || - has_effect_with_flag( flag_EFFECT_NIGHT_VISION ) ); + has_flag( json_flag_NIGHT_VISION ) ); } return nv; @@ -4442,6 +4459,23 @@ bool Character::change_side( item &it, bool interactive ) return false; } + const bool item_one_per_layer = it.has_flag( flag_id( "ONE_PER_LAYER" ) ); + for( const item &worn_item : worn ) { + if( item_one_per_layer && worn_item.has_flag( flag_id( "ONE_PER_LAYER" ) ) ) { + const cata::optional sidedness_conflict = it.covers_overlaps( worn_item ); + if( sidedness_conflict ) { + const std::string player_msg = string_format( + _( "Your %s conflicts with %s, so you cannot swap its side." ), + it.tname(), worn_item.tname() ); + const std::string npc_msg = string_format( + _( "'s %s conflicts with %s so they cannot swap its side." ), + it.tname(), worn_item.tname() ); + add_msg_player_or_npc( m_info, player_msg, npc_msg ); + return false; + } + } + } + if( interactive ) { add_msg_player_or_npc( m_info, _( "You swap the side on which your %s is worn." ), _( " swaps the side on which their %s is worn." ), @@ -4552,7 +4586,7 @@ bool Character::in_climate_control() { bool regulated_area = false; // Check - if( has_active_bionic( bio_climate ) ) { + if( has_flag( json_flag_CLIMATE_CONTROL ) ) { return true; } map &here = get_map(); @@ -5496,7 +5530,8 @@ void Character::update_health( int external_modifiers ) // Slowly near 0, but it's hard to overpower it near +/-100 set_healthy_mod( std::round( get_healthy_mod() * 0.95f ) ); - add_msg_debug( "Health: %d, Health mod: %d", get_healthy(), get_healthy_mod() ); + add_msg_debug( debugmode::DF_CHAR_HEALTH, "Health: %d, Health mod: %d", get_healthy(), + get_healthy_mod() ); } // Returns the number of multiples of tick_length we would "pass" on our way `from` to `to` @@ -5982,7 +6017,6 @@ void Character::update_needs( int rate_multiplier ) needs_rates Character::calc_needs_rates() const { const effect &sleep = get_effect( effect_sleep ); - const bool has_recycler = has_bionic( bio_recycler ); const bool asleep = !sleep.is_null(); needs_rates rates; @@ -5990,7 +6024,7 @@ needs_rates Character::calc_needs_rates() const rates.kcal = get_bmr(); - add_msg_if_player( m_debug, "Metabolic rate: %.2f", rates.hunger ); + add_msg_debug_if_player( debugmode::DF_CHAR_CALORIES, "Metabolic rate: %.2f", rates.hunger ); static const std::string player_thirst_rate( "PLAYER_THIRST_RATE" ); rates.thirst = get_option< float >( player_thirst_rate ); @@ -6005,13 +6039,6 @@ needs_rates Character::calc_needs_rates() const static const std::string fatigue_modifier( "fatigue_modifier" ); rates.fatigue *= 1.0f + mutation_value( fatigue_modifier ); - // Note: intentionally not in metabolic rate - if( has_recycler ) { - // Recycler won't help much with mutant metabolism - it is intended for human one - rates.hunger = std::min( rates.hunger, std::max( 0.5f, rates.hunger - 0.5f ) ); - rates.thirst = std::min( rates.thirst, std::max( 0.5f, rates.thirst - 0.5f ) ); - } - if( asleep ) { static const std::string fatigue_regen_modifier( "fatigue_regen_modifier" ); rates.recovery = 1.0f + mutation_value( fatigue_regen_modifier ); @@ -6056,6 +6083,7 @@ needs_rates Character::calc_needs_rates() const rates.thirst *= 0.25f; } + rates.hunger = enchantment_cache->modify_value( enchant_vals::mod::HUNGER, rates.hunger ); rates.fatigue = enchantment_cache->modify_value( enchant_vals::mod::FATIGUE, rates.fatigue ); rates.thirst = enchantment_cache->modify_value( enchant_vals::mod::THIRST, rates.thirst ); @@ -6347,7 +6375,7 @@ void Character::get_sick() float health_factor = std::pow( 2.0f, get_healthy() / 50.0f ); int disease_rarity = static_cast( checks_per_year * health_factor / base_diseases_per_year ); - add_msg_debug( "disease_rarity = %d", disease_rarity ); + add_msg_debug( debugmode::DF_CHAR_HEALTH, "disease_rarity = %d", disease_rarity ); if( one_in( disease_rarity ) ) { if( one_in( 6 ) ) { // The flu typically lasts 3-10 days. @@ -6434,7 +6462,7 @@ void Character::update_bodytemp() const bool has_sleep = has_effect( effect_sleep ); const bool has_sleep_state = has_sleep || in_sleep_state(); const bool heat_immune = has_flag( json_flag_HEATPROOF ); - const bool has_heatsink = has_bionic( bio_heatsink ) || is_wearing( itype_rm13_armor_on ) || + const bool has_heatsink = has_flag( json_flag_HEATSINK ) || is_wearing( itype_rm13_armor_on ) || heat_immune; const bool has_common_cold = has_effect( effect_common_cold ); const bool has_climate_control = in_climate_control(); @@ -7452,7 +7480,7 @@ bool Character::is_immune_field( const field_type_id &fid ) const return is_elec_immune(); } if( ft.has_fire ) { - return has_active_bionic( bio_heatsink ) || is_wearing( itype_rm13_armor_on ); + return has_flag( json_flag_HEATSINK ) || is_wearing( itype_rm13_armor_on ); } if( ft.has_acid ) { return !is_on_ground() && get_env_resist( body_part_foot_l ) >= 15 && @@ -7505,40 +7533,31 @@ bool Character::is_immune_damage( const damage_type dt ) const return false; case damage_type::BIOLOGICAL: return has_flag( json_flag_BIO_IMMUNE ) || - has_effect_with_flag( flag_EFFECT_BIO_IMMUNE ) || worn_with_flag( flag_BIO_IMMUNE ); case damage_type::BASH: return has_flag( json_flag_BASH_IMMUNE ) || - has_effect_with_flag( flag_EFFECT_BASH_IMMUNE ) || worn_with_flag( flag_BASH_IMMUNE ); case damage_type::CUT: return has_flag( json_flag_CUT_IMMUNE ) || - has_effect_with_flag( flag_EFFECT_CUT_IMMUNE ) || worn_with_flag( flag_CUT_IMMUNE ); case damage_type::ACID: return has_flag( json_flag_ACID_IMMUNE ) || - has_effect_with_flag( flag_EFFECT_ACID_IMMUNE ) || worn_with_flag( flag_ACID_IMMUNE ); case damage_type::STAB: return has_flag( json_flag_STAB_IMMUNE ) || - has_effect_with_flag( flag_EFFECT_STAB_IMMUNE ) || worn_with_flag( flag_STAB_IMMUNE ); case damage_type::BULLET: return has_flag( json_flag_BULLET_IMMUNE ) || - has_effect_with_flag( flag_EFFECT_BULLET_IMMUNE ) || worn_with_flag( flag_BULLET_IMMUNE ); case damage_type::HEAT: return has_flag( json_flag_HEATPROOF ) || - has_effect_with_flag( flag_EFFECT_HEAT_IMMUNE ) || worn_with_flag( flag_HEAT_IMMUNE ); case damage_type::COLD: return has_flag( json_flag_COLD_IMMUNE ) || - has_effect_with_flag( flag_EFFECT_COLD_IMMUNE ) || worn_with_flag( flag_COLD_IMMUNE ); case damage_type::ELECTRIC: return has_flag( json_flag_ELECTRIC_IMMUNE ) || - worn_with_flag( flag_ELECTRIC_IMMUNE ) || - has_effect_with_flag( flag_EFFECT_ELECTRIC_IMMUNE ); + worn_with_flag( flag_ELECTRIC_IMMUNE ); default: return true; } @@ -7628,14 +7647,13 @@ tripoint_abs_omt Character::global_omt_location() const bool Character::is_blind() const { return ( worn_with_flag( flag_BLIND ) || - has_effect( effect_blind ) || has_flag( json_flag_BLIND ) ); } bool Character::is_invisible() const { return ( - has_effect_with_flag( flag_EFFECT_INVISIBLE ) || + has_flag( json_flag_INVISIBLE ) || is_wearing_active_optcloak() || has_trait( trait_DEBUG_CLOAK ) ); @@ -8103,7 +8121,7 @@ float Character::healing_rate( float at_rest_quality ) const // Most common case: awake player with no regenerative abilities // ~7e-5 is 1 hp per day, anything less than that is totally negligible static constexpr float eps = 0.000007f; - add_msg_debug( "%s healing: %.6f", name, final_rate ); + add_msg_debug( debugmode::DF_CHAR_HEALTH, "%s healing: %.6f", name, final_rate ); if( std::abs( final_rate ) < eps ) { return 0.0f; } @@ -8619,7 +8637,7 @@ void Character::burn_move_stamina( int moves ) burn_ratio += overburden_percentage; burn_ratio *= move_mode->stamina_mult(); mod_stamina( -( ( moves * burn_ratio ) / 100.0 ) * stamina_move_cost_modifier() ); - add_msg_debug( "Stamina burn: %d", -( ( moves * burn_ratio ) / 100 ) ); + add_msg_debug( debugmode::DF_CHARACTER, "Stamina burn: %d", -( ( moves * burn_ratio ) / 100 ) ); // Chance to suffer pain if overburden and stamina runs out or has trait BADBACK // Starts at 1 in 25, goes down by 5 for every 50% more carried if( ( current_weight > max_weight ) && ( has_trait( trait_BADBACK ) || get_stamina() == 0 ) && @@ -8688,7 +8706,8 @@ void Character::update_stamina( int turns ) } mod_stamina( roll_remainder( stamina_recovery * turns ) ); - add_msg_debug( "Stamina recovery: %d", roll_remainder( stamina_recovery * turns ) ); + add_msg_debug( debugmode::DF_CHARACTER, "Stamina recovery: %d", + roll_remainder( stamina_recovery * turns ) ); // Cap at max set_stamina( std::min( std::max( get_stamina(), 0 ), max_stam ) ); } @@ -10670,7 +10689,8 @@ void Character::apply_persistent_morale() } // Characters with higher tiers of Nomad suffer worse morale penalties, faster. int max_unhappiness; - float min_time, max_time; + float min_time; + float max_time; if( has_trait( trait_NOMAD ) ) { max_unhappiness = 20; min_time = to_moves( 12_hours ); @@ -10772,7 +10792,7 @@ void Character::check_and_recover_morale() if( !morale->consistent_with( test_morale ) ) { *morale = player_morale( test_morale ); // Recover consistency - add_msg_debug( "%s morale was recovered.", disp_name( true ) ); + add_msg_debug( debugmode::DF_CHARACTER, "%s morale was recovered.", disp_name( true ) ); } } @@ -11275,12 +11295,16 @@ std::list Character::use_charges( const itype_id &what, int qty, const int if( qty <= 0 ) { return res; + } + for( const auto &bio : *this->my_bionics ) { + const bionic_data &bid = bio.info(); + if( bid.fake_item == what && ( !bid.activated || bio.powered ) ) { + mod_power_level( units::from_kilojoule( -qty ) ); + return res; + } + } - } else if( what == itype_toolset ) { - mod_power_level( units::from_kilojoule( -qty ) ); - return res; - - } else if( what == itype_fire ) { + if( what == itype_fire ) { use_fire( qty ); return res; @@ -11356,7 +11380,7 @@ std::list Character::use_charges( const itype_id &what, int qty, return use_charges( what, qty, -1, filter ); } -const item *Character::find_firestarter_with_charges( const int quantity ) const +item Character::find_firestarter_with_charges( const int quantity ) const { for( const item *i : all_items_with_flag( flag_FIRESTARTER ) ) { if( !i->typeId()->can_have_charges() ) { @@ -11364,15 +11388,24 @@ const item *Character::find_firestarter_with_charges( const int quantity ) const if( usef != nullptr && usef->get_actor_ptr() != nullptr ) { const firestarter_actor *actor = dynamic_cast( usef->get_actor_ptr() ); if( actor->can_use( *this->as_character(), *i, false, tripoint_zero ).success() ) { - return i; + return *i; } } } else if( has_charges( i->typeId(), quantity ) ) { - return i; + return *i; } } - - return nullptr; + for( const auto &bio : *this->my_bionics ) { + const bionic_data &bid = bio.info(); + if( bid.fake_item.is_valid() && ( !bid.has_flag( json_flag_BIONIC_TOGGLED ) || ( !bid.activated || + bio.powered ) ) && get_power_level() > quantity * 5_kJ ) { + item fake( bid.fake_item ); + if( !fake.is_null() && bid.fake_item->has_flag( flag_FIRESTARTER ) ) { + return fake; + } + } + } + return item(); } bool Character::has_fire( const int quantity ) const @@ -11383,13 +11416,7 @@ bool Character::has_fire( const int quantity ) const return true; } else if( has_item_with_flag( flag_FIRE ) ) { return true; - } else if( find_firestarter_with_charges( quantity ) ) { - return true; - } else if( has_active_bionic( bio_tools ) && get_power_level() > quantity * 5_kJ ) { - return true; - } else if( has_bionic( bio_lighter ) && get_power_level() > quantity * 5_kJ ) { - return true; - } else if( has_bionic( bio_laser ) && get_power_level() > quantity * 5_kJ ) { + } else if( !find_firestarter_with_charges( quantity ).is_null() ) { return true; } else if( is_npc() ) { // HACK: A hack to make NPCs use their Molotovs @@ -11438,20 +11465,19 @@ void Character::use_fire( const int quantity ) return; } else if( has_item_with_flag( flag_FIRE ) ) { return; - } else if( const item *firestarter = find_firestarter_with_charges( quantity ) ) { - if( firestarter->typeId()->can_have_charges() ) { - use_charges( firestarter->typeId(), quantity ); + } else { + const item firestarter = find_firestarter_with_charges( quantity ); + if( firestarter.is_null() ) { + return; + } + if( firestarter.type->has_flag( flag_USES_BIONIC_POWER ) ) { + mod_power_level( -quantity * 5_kJ ); + return; + } + if( firestarter.typeId()->can_have_charges() ) { + use_charges( firestarter.typeId(), quantity ); return; } - } else if( has_active_bionic( bio_tools ) && get_power_level() > quantity * 5_kJ ) { - mod_power_level( -quantity * 5_kJ ); - return; - } else if( has_bionic( bio_lighter ) && get_power_level() > quantity * 5_kJ ) { - mod_power_level( -quantity * 5_kJ ); - return; - } else if( has_bionic( bio_laser ) && get_power_level() > quantity * 5_kJ ) { - mod_power_level( -quantity * 5_kJ ); - return; } } @@ -11618,6 +11644,7 @@ void Character::on_mutation_gain( const trait_id &mid ) magic->on_mutation_gain( mid, *this ); update_type_of_scent( mid ); recalculate_enchantment_cache(); // mutations can have enchantments + effect_on_conditions::process_reactivate(); } void Character::on_mutation_loss( const trait_id &mid ) @@ -11626,6 +11653,7 @@ void Character::on_mutation_loss( const trait_id &mid ) magic->on_mutation_loss( mid ); update_type_of_scent( mid, false ); recalculate_enchantment_cache(); // mutations can have enchantments + effect_on_conditions::process_reactivate(); } void Character::on_stat_change( const std::string &stat, int value ) @@ -12395,22 +12423,9 @@ int Character::sleep_spot( const tripoint &p ) const if( has_addiction( add_type::SLEEP ) ) { sleepy -= 4; } - if( has_trait( trait_INSOMNIA ) ) { - // 12.5 points is the difference between "tired" and "dead tired" - sleepy -= 12; - } - if( has_trait( trait_EASYSLEEPER ) ) { - // Low fatigue (being rested) has a much stronger effect than high fatigue - // so it's OK for the value to be that much higher - sleepy += 24; - } - if( has_active_bionic( bio_soporific ) ) { - sleepy += 30; - } - if( has_trait( trait_EASYSLEEPER2 ) ) { - // Mousefolk can sleep just about anywhere. - sleepy += 40; - } + + sleepy = enchantment_cache->modify_value( enchant_vals::mod::SLEEPY, sleepy ); + if( watersleep && get_map().has_flag_ter( "SWIMMABLE", pos() ) ) { sleepy += 10; //comfy water! } @@ -13156,6 +13171,6 @@ bool Character::has_bionic_with_flag( const json_character_flag &flag ) const bool Character::has_flag( const json_character_flag &flag ) const { - // If this is a performance problem create a map of flags stored for a character and updated on trait, mutation, bionic add/remove, activate/deactivate - return has_trait_flag( flag ) || has_bionic_with_flag( flag ); + // If this is a performance problem create a map of flags stored for a character and updated on trait, mutation, bionic add/remove, activate/deactivate, effect gain/loss + return has_trait_flag( flag ) || has_bionic_with_flag( flag ) || has_effect_with_flag( flag ); } diff --git a/src/character.h b/src/character.h index 19766afd20790..d6cb62d1e112b 100644 --- a/src/character.h +++ b/src/character.h @@ -501,7 +501,7 @@ class Character : public Creature, public visitable void mod_stat( const std::string &stat, float modifier ) override; - int get_standard_stamina_cost( item *thrown_item = nullptr ); + int get_standard_stamina_cost( const item *thrown_item = nullptr ) const; /**Get bonus to max_hp from excess stored fat*/ int get_fat_to_hp() const; @@ -964,7 +964,7 @@ class Character : public Creature, public visitable bool has_bionic_with_flag( const json_character_flag &flag ) const; /** This is to prevent clang complaining about overloading a virtual function, the creature version uses monster flags so confusion is unlikely. */ using Creature::has_flag; - /** Returns true if player has a trait or bionic with a flag */ + /** Returns true if player has a trait, bionic or effect with a flag */ bool has_flag( const json_character_flag &flag ) const; /** Returns the trait id with the given invlet, or an empty string if no trait has that invlet */ trait_id trait_by_invlet( int ch ) const; @@ -974,6 +974,13 @@ class Character : public Creature, public visitable /** Add or removes a mutation on the player, but does not trigger mutation loss/gain effects. */ void set_mutations( const std::vector &traits ); void set_mutation( const trait_id & ); + protected: + // Set a mutation, but don't do any of the necessary updates + // Only call this from one of the above two functions + void set_mutation_unsafe( const trait_id & ); + public: + // Do the mutation updates necessary when adding a mutation (nonspecific cache updates) + void do_mutation_updates(); void unset_mutation( const trait_id & ); /**Unset switched mutation and set target mutation instead*/ void switch_mutations( const trait_id &switched, const trait_id &target, bool start_powered ); @@ -1303,6 +1310,9 @@ class Character : public Creature, public visitable /**Is the installation possible*/ bool can_install_bionics( const itype &type, Character &installer, bool autodoc = false, int skill_level = -1 ); + /** Is this bionic elligible to be installed in the player? */ + // Should be ret_val, but ret_val.h doesn't like it + ret_val is_installable( const item_location &loc, bool by_autodoc ) const; std::map bionic_installation_issues( const bionic_id &bioid ); /** Initialize all the values needed to start the operation player_activity */ bool install_bionics( const itype &type, player &installer, bool autodoc = false, @@ -1635,6 +1645,10 @@ class Character : public Creature, public visitable */ int item_reload_cost( const item &it, const item &ammo, int qty ) const; + projectile thrown_item_projectile( const item &thrown ) const; + int thrown_item_adjusted_damage( const item &thrown ) const; + // calculates the total damage possible from a thrown item, without resistances and such. + int thrown_item_total_damage_raw( const item &thrown ) const; /** Maximum thrown range with a given item, taking all active effects into account. */ int throw_range( const item & ) const; /** Dispersion of a thrown item, against a given target, taking into account whether or not the throw was blind. */ @@ -1847,6 +1861,7 @@ class Character : public Creature, public visitable // gets all the spells known by this character that have this spell class // spells returned are a copy, do not try to edit them from here, instead use known_magic::get_spell std::vector spells_known_of_class( const trait_id &spell_class ) const; + bool cast_spell( spell &sp, bool fake_spell, cata::optional target ); void make_bleed( const effect_source &source, const bodypart_id &bp, time_duration duration, int intensity = 1, bool permanent = false, bool force = false, bool defferred = false ); @@ -2055,7 +2070,7 @@ class Character : public Creature, public visitable std::list use_charges( const itype_id &what, int qty, int radius, const std::function &filter = return_true ); - const item *find_firestarter_with_charges( int quantity ) const; + item find_firestarter_with_charges( int quantity ) const; bool has_fire( int quantity ) const; void use_fire( int quantity ); void assign_stashed_activity(); @@ -2725,8 +2740,9 @@ class Character : public Creature, public visitable protected: Character(); - Character( Character && ); - Character &operator=( Character && ); + Character( Character && ) noexcept( map_is_noexcept ); + Character &operator=( Character && ) noexcept( list_is_noexcept ); + public: struct trait_data { /** Whether the mutation is activated. */ bool powered = false; @@ -2931,6 +2947,7 @@ class Character : public Creature, public visitable }; mutable crafting_cache_type crafting_cache; + time_point melee_warning_turn = calendar::turn_zero; protected: /** Subset of learned recipes. Needs to be mutable for lazy initialization. */ mutable pimpl learned_recipes; diff --git a/src/chkjson/chkjson.cpp b/src/chkjson/chkjson.cpp index 07b24e7a5fb3b..22639af3afe4c 100644 --- a/src/chkjson/chkjson.cpp +++ b/src/chkjson/chkjson.cpp @@ -32,12 +32,14 @@ static std::vector get_files_from_path( std::string extension, std: root_path = "."; } - std::stack directories, tempstack; + std::stack directories; + std::stack tempstack; directories.push( root_path ); std::string path; while( !directories.empty() ) { path = directories.top(); + std::string path_with_slash = path + "/"; directories.pop(); DIR *root = opendir( path.c_str() ); @@ -52,7 +54,7 @@ static std::vector get_files_from_path( std::string extension, std: if( stat( root_file->d_name, &_buff ) != 0x4 ) { // ignore '.' and '..' folder names, which are current and parent folder relative paths if( ( strcmp( root_file->d_name, "." ) != 0 ) && ( strcmp( root_file->d_name, ".." ) != 0 ) ) { - std::string subpath = path + "/" + root_file->d_name; + std::string subpath = path_with_slash + root_file->d_name; if( recursive_search ) { subdir = opendir( subpath.c_str() ); @@ -66,7 +68,7 @@ static std::vector get_files_from_path( std::string extension, std: // check to see if it is a file with the appropriate extension std::string tmp = root_file->d_name; if( tmp.find( c_extension, match_extension ? tmp.size() - extsz : 0 ) != std::string::npos ) { - std::string fullpath = path + "/" + tmp; + std::string fullpath = path_with_slash + tmp; files.push_back( fullpath ); } } diff --git a/src/clone_ptr.h b/src/clone_ptr.h index 28859daff3f63..381965abf97d6 100644 --- a/src/clone_ptr.h +++ b/src/clone_ptr.h @@ -13,13 +13,14 @@ class clone_ptr clone_ptr() = default; // NOLINTNEXTLINE(google-explicit-constructor) clone_ptr( std::nullptr_t ) {} + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) clone_ptr( const clone_ptr &other ) : p_( other.p_ ? other.p_->clone() : nullptr ) {} - clone_ptr( clone_ptr && ) = default; + clone_ptr( clone_ptr && ) noexcept = default; clone_ptr &operator=( const clone_ptr &other ) { p_ = other.p_ ? other.p_->clone() : nullptr; return *this; } - clone_ptr &operator=( clone_ptr && ) = default; + clone_ptr &operator=( clone_ptr && ) noexcept = default; // implicit conversion from unique_ptr template diff --git a/src/clzones.cpp b/src/clzones.cpp index 177fc14c0bd1c..1e4c5319441cb 100644 --- a/src/clzones.cpp +++ b/src/clzones.cpp @@ -1240,7 +1240,7 @@ void zone_manager::zone_edited( zone_data &zone ) } } //Add it to the list of changed zones - changed_vzones.push_back( std::make_pair( zone_data( zone ), &zone ) ); + changed_vzones.emplace_back( zone_data( zone ), &zone ); } } diff --git a/src/colony.h b/src/colony.h index 2717614e8efe2..f6dc265d7bfdc 100644 --- a/src/colony.h +++ b/src/colony.h @@ -3029,7 +3029,8 @@ class colony : private element_allocator_type using diff_type = typename iterator_type::difference_type; diff_type distance = 0; - iterator_type iterator1 = first, iterator2 = last; + iterator_type iterator1 = first; + iterator_type iterator2 = last; const bool swap = first > last; if( swap ) { // Less common case diff --git a/src/compatibility.h b/src/compatibility.h new file mode 100644 index 0000000000000..5c1d84c7350e5 --- /dev/null +++ b/src/compatibility.h @@ -0,0 +1,22 @@ +#ifndef CATA_SRC_COMPATIBILITY_H +#define CATA_SRC_COMPATIBILITY_H + +#include +#include +#include +#include + +// Some older standard libraries don't have all their classes +// nothrow-move-assignable when you might expect them to be. +// Some of our classes can't be in that case, so we need to know when we're in +// that situation. Can probably get rid of this once we're on C++17. +// std::list is a problem on clang-3.8 on Travis CI Ubuntu Xenial. +constexpr bool list_is_noexcept = std::is_nothrow_move_assignable>::value; +// std::string is a problem on gcc-5.3 on Travis CI Ubuntu Xenial. +constexpr bool string_is_noexcept = std::is_nothrow_move_assignable::value; +// std::set is a problem in Visual Studio +constexpr bool set_is_noexcept = std::is_nothrow_move_constructible>::value; +// as is std::map +constexpr bool map_is_noexcept = std::is_nothrow_move_constructible>::value; + +#endif // CATA_SRC_COMPATIBILITY_H diff --git a/src/computer_session.cpp b/src/computer_session.cpp index df4de3fcadefe..0f0ddc253463d 100644 --- a/src/computer_session.cpp +++ b/src/computer_session.cpp @@ -405,12 +405,21 @@ void computer_session::action_unlock() query_any( _( "Lock disabled. Press any key…" ) ); } -//Toll is required for the church computer/mechanism to function +//Toll is required for the church/school computer/mechanism to function void computer_session::action_toll() { - sounds::sound( get_player_character().pos(), 120, sounds::sound_t::music, - //~ the sound of a church bell ringing - _( "Bohm… Bohm… Bohm…" ), true, "environment", "church_bells" ); + if( calendar::turn < comp.next_attempt ) { + print_error( _( "[Bellsystem 1.2] is currently in use." ) ); + query_any( _( "Please wait for at least one minute." ) ); + reset_terminal(); + } else { + comp.next_attempt = calendar::turn + 1_minutes; + sounds::sound( get_player_character().pos(), 120, sounds::sound_t::music, + //~ the sound of a church bell ringing + _( "Bohm… Bohm… Bohm…" ), true, "environment", "church_bells" ); + + query_any( _( "[Bellsystem 1.2] activated. Have a nice day." ) ); + } } void computer_session::action_sample() @@ -771,8 +780,8 @@ void computer_session::action_repeater_mod() avatar &player_character = get_avatar(); if( player_character.has_amount( itype_radio_repeater_mod, 1 ) ) { for( mission *miss : player_character.get_active_missions() ) { - static const mission_type_id commo_3 = mission_type_id( "MISSION_OLD_GUARD_NEC_COMMO_3" ), - commo_4 = mission_type_id( "MISSION_OLD_GUARD_NEC_COMMO_4" ); + static const mission_type_id commo_3 = mission_type_id( "MISSION_OLD_GUARD_NEC_COMMO_3" ); + static const mission_type_id commo_4 = mission_type_id( "MISSION_OLD_GUARD_NEC_COMMO_4" ); if( miss->mission_id() == commo_3 || miss->mission_id() == commo_4 ) { miss->step_complete( 1 ); print_error( _( "Repeater mod installed…" ) ); diff --git a/src/condition.cpp b/src/condition.cpp index e5933505c5997..e454446372227 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -746,15 +746,75 @@ void conditional_t::set_is_day() } template -void conditional_t::set_is_outside() +void conditional_t::set_is_outside( bool is_npc ) { - condition = []( const T & d ) { - const map &here = get_map(); - const tripoint &pos = here.getabs( d.beta->pos() ); - return !here.has_flag( TFLAG_INDOORS, pos ); + condition = [is_npc]( const T & d ) { + return is_creature_outside( *d.actor( is_npc )->get_character() ); + }; +} + +template +void conditional_t::set_one_in_chance( const JsonObject &jo, const std::string &member ) +{ + const int one_in_chance = jo.get_int( member ); + condition = [one_in_chance]( const T & ) { + return one_in( one_in_chance ); + }; +} + +template +void conditional_t::set_is_temperature( const JsonObject &jo, const std::string &member ) +{ + const int temp_min = jo.get_int( member ); + condition = [temp_min]( const T & ) { + return get_weather().weather_precise->temperature >= temp_min; + }; +} + +template +void conditional_t::set_is_height( const JsonObject &jo, const std::string &member, bool is_npc ) +{ + const int height_min = jo.get_int( member ); + condition = [height_min, is_npc]( const T & d ) { + return d.actor( is_npc )->posz() >= height_min; + }; +} + +template +void conditional_t::set_is_windpower( const JsonObject &jo, const std::string &member ) +{ + const int windpower_min = jo.get_int( member ); + condition = [windpower_min]( const T & ) { + return get_weather().weather_precise->windpower >= windpower_min; + }; +} + +template +void conditional_t::set_is_humidity( const JsonObject &jo, const std::string &member ) +{ + const int humidity_min = jo.get_int( member ); + condition = [humidity_min]( const T & ) { + return get_weather().weather_precise->humidity >= humidity_min; + }; +} + +template +void conditional_t::set_is_pressure( const JsonObject &jo, const std::string &member ) +{ + const int pressure_min = jo.get_int( member ); + condition = [pressure_min]( const T & ) { + return get_weather().weather_precise->pressure >= pressure_min; }; } +template +void conditional_t::set_is_weather( const JsonObject &jo ) +{ + weather_type_id weather = weather_type_id( jo.get_string( "is_weather" ) ); + condition = [weather]( const T & ) { + return get_weather().weather_id == weather; + }; +} template void conditional_t::set_u_has_camp() { @@ -828,6 +888,47 @@ void conditional_t::set_mission_has_generic_rewards() }; } +template +void conditional_t::set_has_worn_with_flag( const JsonObject &jo, const std::string &member, + bool is_npc ) +{ + const std::string flag( jo.get_string( member ) ); + condition = [flag, is_npc]( const T & d ) { + return d.actor( is_npc )->worn_with_flag( flag_id( flag ) ); + }; +} + +template +void conditional_t::set_has_wielded_with_flag( const JsonObject &jo, const std::string &member, + bool is_npc ) +{ + const std::string flag( jo.get_string( member ) ); + condition = [flag, is_npc]( const T & d ) { + return d.actor( is_npc )->wielded_with_flag( flag_id( flag ) ); + }; +} + +template +void conditional_t::set_has_pain( const JsonObject &jo, const std::string &member, + bool is_npc ) +{ + const int min_pain = jo.get_int( member ); + condition = [min_pain, is_npc]( const T & d ) { + return d.actor( is_npc )->pain_cur() >= min_pain; + }; +} + +template +void conditional_t::set_has_power( const JsonObject &jo, const std::string &member, + bool is_npc ) +{ + units::energy min_power; + assign( jo, member, min_power, false, 0_kJ ); + condition = [min_power, is_npc]( const T & d ) { + return d.actor( is_npc )->power_cur() >= min_power; + }; +} + template conditional_t::conditional_t( const JsonObject &jo ) { @@ -1006,6 +1107,38 @@ conditional_t::conditional_t( const JsonObject &jo ) set_has_skill( jo, "npc_has_skill", is_npc ); } else if( jo.has_member( "u_know_recipe" ) ) { set_u_know_recipe( jo, "u_know_recipe" ); + } else if( jo.has_int( "one_in_chance" ) ) { + set_one_in_chance( jo, "one_in_chance" ); + } else if( jo.has_int( "is_temperature" ) ) { + set_is_temperature( jo, "is_temperature" ); + } else if( jo.has_int( "is_windpower" ) ) { + set_is_windpower( jo, "is_windpower" ); + } else if( jo.has_int( "is_humidity" ) ) { + set_is_humidity( jo, "is_humidity" ); + } else if( jo.has_member( "is_pressure" ) ) { + set_is_pressure( jo, "is_pressure" ); + } else if( jo.has_int( "u_is_height" ) ) { + set_is_height( jo, "u_is_height" ); + } else if( jo.has_int( "npc_is_height" ) ) { + set_is_height( jo, "npc_is_height", is_npc ); + } else if( jo.has_string( "u_has_worn_with_flag" ) ) { + set_has_worn_with_flag( jo, "u_has_worn_with_flag" ); + } else if( jo.has_string( "npc_has_worn_with_flag" ) ) { + set_has_worn_with_flag( jo, "npc_has_worn_with_flag", is_npc ); + } else if( jo.has_string( "u_has_wielded_with_flag" ) ) { + set_has_wielded_with_flag( jo, "u_has_wielded_with_flag" ); + } else if( jo.has_string( "npc_has_wielded_with_flag" ) ) { + set_has_wielded_with_flag( jo, "npc_has_wielded_with_flag", is_npc ); + } else if( jo.has_member( "u_has_pain" ) ) { + set_has_pain( jo, "u_has_pain" ); + } else if( jo.has_member( "npc_has_pain" ) ) { + set_has_pain( jo, "npc_has_pain", is_npc ); + } else if( jo.has_member( "u_has_power" ) ) { + set_has_power( jo, "u_has_power" ); + } else if( jo.has_member( "npc_has_power" ) ) { + set_has_power( jo, "npc_has_power", is_npc ); + } else if( jo.has_string( "is_weather" ) ) { + set_is_weather( jo ); } else { for( const std::string &sub_member : dialogue_data::simple_string_conds ) { if( jo.has_string( sub_member ) ) { @@ -1087,8 +1220,10 @@ conditional_t::conditional_t( const std::string &type ) set_is_day(); } else if( type == "u_has_stolen_item" ) { set_has_stolen_item( is_npc ); - } else if( type == "is_outside" ) { + } else if( type == "u_is_outside" ) { set_is_outside(); + } else if( type == "is_outside" || type == "npc_is_outside" ) { + set_is_outside( is_npc ); } else if( type == "u_has_camp" ) { set_u_has_camp(); } else if( type == "has_pickup_list" ) { diff --git a/src/condition.h b/src/condition.h index ea9fd856de8bd..6c72715de9b9c 100644 --- a/src/condition.h +++ b/src/condition.h @@ -23,7 +23,7 @@ const std::unordered_set simple_string_conds = { { "mission_complete", "mission_incomplete", "mission_has_generic_rewards", "npc_available", "npc_following", "npc_friend", "npc_hostile", "npc_train_skills", "npc_train_styles", "npc_train_spells", - "at_safe_space", "is_day", "npc_has_activity", "is_outside", "u_has_camp", + "at_safe_space", "is_day", "npc_has_activity", "is_outside", "u_is_outside", "npc_is_outside", "u_has_camp", "u_can_stow_weapon", "npc_can_stow_weapon", "u_has_weapon", "npc_has_weapon", "u_driving", "npc_driving", "has_pickup_list", "is_by_radio", "has_reason" @@ -43,7 +43,10 @@ const std::unordered_set complex_conds = { { "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_compare_time_since_var", "npc_compare_time_since_var" + "u_compare_time_since_var", "npc_compare_time_since_var", "is_weather", "one_in_chance", + "is_temperature", "is_windpower", "is_humidity", "is_pressure", "u_is_height", "npc_is_height", + "u_has_worn_with_flag", "npc_has_worn_with_flag", "u_has_wielded_with_flag", "npc_has_wielded_with_flag", + "u_has_pain", "npc_has_pain", "u_has_power", "npc_has_power" } }; } // namespace dialogue_data @@ -88,6 +91,17 @@ struct conditional_t { void set_has_dexterity( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_has_intelligence( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_has_perception( const JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_has_pain( const JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_has_power( const JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_one_in_chance( const JsonObject &jo, const std::string &member ); + void set_is_temperature( const JsonObject &jo, const std::string &member ); + void set_is_height( const JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_is_windpower( const JsonObject &jo, const std::string &member ); + void set_has_worn_with_flag( const JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_is_humidity( const JsonObject &jo, const std::string &member ); + void set_is_pressure( const JsonObject &jo, const std::string &member ); + void set_has_wielded_with_flag( const JsonObject &jo, const std::string &member, + bool is_npc = false ); void set_is_wearing( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_has_item( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_has_items( const JsonObject &jo, const std::string &member, bool is_npc = false ); @@ -108,6 +122,7 @@ struct conditional_t { void set_npc_override( const JsonObject &jo ); void set_days_since( const JsonObject &jo ); void set_is_season( const JsonObject &jo ); + void set_is_weather( const JsonObject &jo ); void set_mission_goal( const JsonObject &jo ); void set_no_assigned_mission(); void set_has_assigned_mission(); @@ -130,7 +145,7 @@ struct conditional_t { void set_is_driving( bool is_npc = false ); void set_is_day(); void set_has_stolen_item( bool is_npc = false ); - void set_is_outside(); + void set_is_outside( bool is_npc = false ); void set_is_by_radio(); void set_u_has_camp(); void set_has_pickup_list(); diff --git a/src/construction.cpp b/src/construction.cpp index 5bfdd965b56d2..1da0ae0440230 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -523,7 +523,7 @@ construction_id construction_menu( const bool blueprint ) } current_buffer_location += construct_buffers[i].size(); if( i < construct_buffers.size() - 1 ) { - full_construct_buffer.push_back( std::string() ); + full_construct_buffer.emplace_back( ); current_buffer_location++; } } @@ -1224,7 +1224,7 @@ void construct::done_grave( const tripoint &p ) Character &player_character = get_player_character(); map &here = get_map(); map_stack its = here.i_at( p ); - for( item it : its ) { + for( const item &it : its ) { if( it.is_corpse() ) { if( it.get_corpse_name().empty() ) { if( it.get_mtype()->has_flag( MF_HUMAN ) ) { @@ -1813,7 +1813,7 @@ void finalize_constructions() if( !vp.has_flag( flag_INITIAL_PART ) ) { continue; } - frame_items.push_back( item_comp( vp.base_item, 1 ) ); + frame_items.emplace_back( vp.base_item, 1 ); } if( frame_items.empty() ) { diff --git a/src/consumption.cpp b/src/consumption.cpp index 41cd803635aa5..6f9b911b61ccb 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1343,7 +1343,7 @@ bool Character::consume_effects( item &food ) // But always round down int h_loss = -rottedness * comest.get_default_nutr(); mod_healthy_mod( h_loss, -200 ); - add_msg_debug( "%d health from %0.2f%% rotten food", h_loss, rottedness ); + add_msg_debug( debugmode::DF_FOOD, "%d health from %0.2f%% rotten food", h_loss, rottedness ); } // Used in hibernation messages. @@ -1429,10 +1429,10 @@ bool Character::consume_effects( item &food ) food_vol * ratio, food_nutrients }; - add_msg_debug( - "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", - units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, - food_nutrients.kcal(), units::to_gram( food_weight ) ); + add_msg_debug( debugmode::DF_FOOD, + "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", + units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, + food_nutrients.kcal(), units::to_gram( food_weight ) ); // Maybe move tapeworm to digestion if( has_effect( effect_tapeworm ) ) { ingested.nutr /= 2; diff --git a/src/crafting.cpp b/src/crafting.cpp index da150228ba2b3..980130372a99c 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -2304,7 +2304,7 @@ void Character::disassemble_all( bool one_pass ) bool found_any = false; std::vector to_disassemble; for( item &it : get_map().i_at( pos() ) ) { - to_disassemble.push_back( item_location( map_cursor( pos() ), &it ) ); + to_disassemble.emplace_back( map_cursor( pos() ), &it ); } for( item_location &it_loc : to_disassemble ) { // Prevent disassembling an in process disassembly because it could have been created by a previous iteration of this loop diff --git a/src/crafting_gui.cpp b/src/crafting_gui.cpp index 2b45bef6193e8..dc8ea31b791f0 100644 --- a/src/crafting_gui.cpp +++ b/src/crafting_gui.cpp @@ -535,7 +535,8 @@ const recipe *select_crafting_recipe( int &batch_size_out ) const int max_recipe_name_width = 27; cata::optional cursor_pos; - int recmin = 0, recmax = current.size(); + int recmin = 0; + int recmax = current.size(); if( recmax > dataLines ) { if( line <= recmin + dataHalfLines ) { for( int i = recmin; i < recmin + dataLines; ++i ) { @@ -679,7 +680,7 @@ const recipe *select_crafting_recipe( int &batch_size_out ) current.clear(); for( int i = 1; i <= 50; i++ ) { current.push_back( chosen ); - available.push_back( availability( chosen, i ) ); + available.emplace_back( chosen, i ); } } else { static_popup popup; @@ -765,6 +766,11 @@ const recipe *select_crafting_recipe( int &batch_size_out ) recipe_subset::search_type::proficiency, progress_callback ); break; + case 'l': + filtered_recipes = filtered_recipes.reduce( qry_filter_str.substr( 2 ), + recipe_subset::search_type::difficulty, progress_callback ); + break; + default: current.clear(); } @@ -929,13 +935,14 @@ const recipe *select_crafting_recipe( int &batch_size_out ) //~ Example result description search term { 'q', _( "metal sawing" ), _( "quality of resulting item" ) }, { 'd', _( "reach attack" ), _( "full description of resulting item (slow)" ) }, - { 'c', _( "two by four" ), _( "component required to craft" ) }, + { 'c', _( "plank" ), _( "component required to craft" ) }, { 'p', _( "tailoring" ), _( "primary skill used to craft" ) }, { 's', _( "cooking" ), _( "any skill used to craft" ) }, { 'Q', _( "fine bolt turning" ), _( "quality required to craft" ) }, { 't', _( "soldering iron" ), _( "tool required to craft" ) }, { 'm', _( "yes" ), _( "recipes which are memorized or not" ) }, { 'P', _( "Blacksmithing" ), _( "proficiency used to craft" ) }, + { 'l', _( "5" ), _( "difficulty of the recipe as a number or range" ) }, }; int max_example_length = 0; for( const auto &prefix : prefixes ) { @@ -947,6 +954,8 @@ const recipe *select_crafting_recipe( int &batch_size_out ) _( "The default is to search result names. Some single-character prefixes " "can be used with a colon : to search in other ways. Additional filters " "are separated by commas ,.\n" + "Filtering by difficulty can accept range; " + "l:5~10 for all recipes from difficulty 5 to 10.\n" "\n\n" "Examples:\n" ); @@ -1082,7 +1091,7 @@ std::string peek_related_recipe( const recipe *current, const recipe_subset &ava const requirement_data &req = current->simple_requirements(); for( const std::vector &comp_list : req.get_components() ) { for( const item_comp &a : comp_list ) { - related_components.push_back( { a.type, item::nname( a.type, 1 ) } ); + related_components.emplace_back( a.type, item::nname( a.type, 1 ) ); } } std::sort( related_components.begin(), related_components.end(), compare_second ); @@ -1095,7 +1104,7 @@ std::string peek_related_recipe( const recipe *current, const recipe_subset &ava get_player_character().get_learned_recipes().of_component( tid ); for( const auto &b : known_recipes ) { if( available.contains( b ) ) { - related_results.push_back( { b->result(), b->result_name() } ); + related_results.emplace_back( b->result(), b->result_name() ); } } std::stable_sort( related_results.begin(), related_results.end(), compare_second ); diff --git a/src/creature.cpp b/src/creature.cpp index dad36fef8b48c..d9dadf45061c1 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -114,6 +114,11 @@ Creature::Creature() fake = false; } +Creature::Creature( const Creature & ) = default; +Creature::Creature( Creature && ) noexcept( map_is_noexcept ) = default; +Creature &Creature::operator=( const Creature & ) = default; +Creature &Creature::operator=( Creature && ) noexcept = default; + Creature::~Creature() = default; std::vector Creature::get_grammatical_genders() const @@ -685,7 +690,8 @@ void projectile::apply_effects_damage( Creature &target, Creature *source, target.add_effect( effect_source( source ), effect_tied, 1_turns, true ); target.as_monster()->tied_item = cata::make_value( drop_item ); } else { - add_msg_debug( "projectile with TANGLE effect, but no drop item specified" ); + add_msg_debug( debugmode::DF_CREATURE, + "projectile with TANGLE effect, but no drop item specified" ); } } else if( ( target.is_npc() || target.is_avatar() ) && !target.is_immune_effect( effect_downed ) ) { @@ -1174,7 +1180,7 @@ void Creature::add_effect( const effect_source &source, const efftype_id &eff_id // Bound intensity by [1, max intensity] if( e.get_intensity() < 1 ) { - add_msg_debug( "Bad intensity, ID: %s", e.get_id().c_str() ); + add_msg_debug( debugmode::DF_CREATURE, "Bad intensity, ID: %s", e.get_id().c_str() ); e.set_intensity( 1 ); } else if( e.get_intensity() > e.get_max_intensity() ) { e.set_intensity( e.get_max_intensity() ); @@ -1214,7 +1220,7 @@ void Creature::add_effect( const effect_source &source, const efftype_id &eff_id } // Bound new effect intensity by [1, max intensity] if( e.get_intensity() < 1 ) { - add_msg_debug( "Bad intensity, ID: %s", e.get_id().c_str() ); + add_msg_debug( debugmode::DF_CREATURE, "Bad intensity, ID: %s", e.get_id().c_str() ); e.set_intensity( 1 ); } else if( e.get_intensity() > e.get_max_intensity() ) { e.set_intensity( e.get_max_intensity() ); @@ -1421,7 +1427,7 @@ void Creature::process_effects() // Add any effects that others remove to the removal list for( const auto &removed_effect : _it.second.get_removes_effects() ) { rem_ids.push_back( removed_effect ); - rem_bps.push_back( bodypart_str_id::NULL_ID() ); + rem_bps.emplace_back( bodypart_str_id::NULL_ID() ); } effect &e = _it.second; const int prev_int = e.get_intensity(); @@ -2109,7 +2115,7 @@ std::vector Creature::get_all_body_parts( get_body_part_flags flags if( only_main && elem.first->main_part != elem.first ) { continue; } - all_bps.push_back( elem.first ); + all_bps.emplace_back( elem.first ); } if( flags & get_body_part_flags::sorted ) { @@ -2379,10 +2385,10 @@ bodypart_id Creature::select_body_part( Creature *source, int hit_roll ) const { int szdif = source->get_size() - get_size(); - add_msg_debug( "hit roll = %d", hit_roll ); - add_msg_debug( "source size = %d", source->get_size() ); - add_msg_debug( "target size = %d", get_size() ); - add_msg_debug( "difference = %d", szdif ); + add_msg_debug( debugmode::DF_CREATURE, "hit roll = %d", hit_roll ); + add_msg_debug( debugmode::DF_CREATURE, "source size = %d", source->get_size() ); + add_msg_debug( debugmode::DF_CREATURE, "target size = %d", get_size() ); + add_msg_debug( debugmode::DF_CREATURE, "difference = %d", szdif ); return anatomy_human_anatomy->select_body_part( szdif, hit_roll ); } @@ -2501,6 +2507,11 @@ void Creature::add_msg_if_player( const game_message_params ¶ms, const trans return add_msg_if_player( params, msg.translated() ); } +void Creature::add_msg_debug_if_player( debugmode::debug_filter type, const translation &msg ) const +{ + return add_msg_debug_if_player( type, msg.translated() ); +} + void Creature::add_msg_if_npc( const translation &msg ) const { return add_msg_if_npc( msg.translated() ); @@ -2511,6 +2522,11 @@ void Creature::add_msg_if_npc( const game_message_params ¶ms, const translat return add_msg_if_npc( params, msg.translated() ); } +void Creature::add_msg_debug_if_npc( debugmode::debug_filter type, const translation &msg ) const +{ + return add_msg_debug_if_npc( type, msg.translated() ); +} + void Creature::add_msg_player_or_npc( const translation &pc, const translation &npc ) const { return add_msg_player_or_npc( pc.translated(), npc.translated() ); @@ -2522,6 +2538,12 @@ void Creature::add_msg_player_or_npc( const game_message_params ¶ms, const t return add_msg_player_or_npc( params, pc.translated(), npc.translated() ); } +void Creature::add_msg_debug_player_or_npc( debugmode::debug_filter type, const translation &pc, + const translation &npc ) const +{ + return add_msg_debug_player_or_npc( type, pc.translated(), npc.translated() ); +} + void Creature::add_msg_player_or_say( const translation &pc, const translation &npc ) const { return add_msg_player_or_say( pc.translated(), npc.translated() ); @@ -2574,11 +2596,11 @@ void Creature::describe_infrared( std::vector &buf ) const size_str = "invalid"; break; } - buf.push_back( _( "You see a figure radiating heat." ) ); + buf.emplace_back( _( "You see a figure radiating heat." ) ); buf.push_back( string_format( _( "It is %s in size." ), size_str ) ); } void Creature::describe_specials( std::vector &buf ) const { - buf.push_back( _( "You sense a creature here." ) ); + buf.emplace_back( _( "You sense a creature here." ) ); } diff --git a/src/creature.h b/src/creature.h index 720fd01dc6493..64332970acd49 100644 --- a/src/creature.h +++ b/src/creature.h @@ -13,6 +13,7 @@ #include #include "bodypart.h" +#include "compatibility.h" #include "damage.h" #include "debug.h" #include "effect_source.h" @@ -44,6 +45,7 @@ class anatomy; class avatar; class field; class field_entry; +class npc; class player; class time_duration; struct point; @@ -263,6 +265,12 @@ class Creature : public location, public viewer virtual const avatar *as_avatar() const { return nullptr; } + virtual const npc *as_npc() { + return nullptr; + } + virtual const npc *as_npc() const { + return nullptr; + } virtual monster *as_monster() { return nullptr; } @@ -964,6 +972,93 @@ class Creature : public location, public viewer string_format( npc_msg, std::forward( args )... ) ); } + virtual void add_msg_debug_if_player( debugmode::debug_filter /*type*/, + const std::string &/*msg*/ ) const {} + void add_msg_debug_if_player( debugmode::debug_filter /*type*/, const translation &/*msg*/ ) const; + template + void add_msg_debug_if_player( debugmode::debug_filter type, const char *const msg, + Args &&... args ) const { + // expanding for string formatting can be expensive + if( debug_mode ) { + return add_msg_debug_if_player( type, string_format( msg, std::forward( args )... ) ); + } + } + template + void add_msg_debug_if_player( debugmode::debug_filter type, const std::string &msg, + Args &&... args ) const { + if( debug_mode ) { + return add_msg_debug_if_player( type, string_format( msg, std::forward( args )... ) ); + } + } + template + void add_msg_debug_if_player( debugmode::debug_filter type, const translation &msg, + Args &&... args ) const { + if( debug_mode ) { + return add_msg_debug_if_player( type, string_format( msg, std::forward( args )... ) ); + } + } + + virtual void add_msg_debug_if_npc( debugmode::debug_filter /*type*/, + const std::string &/*msg*/ ) const {} + void add_msg_debug_if_npc( debugmode::debug_filter /*type*/, const translation &/*msg*/ ) const; + template + void add_msg_debug_if_npc( debugmode::debug_filter type, const char *const msg, + Args &&... args ) const { + // expanding for string formatting can be expensive + if( debug_mode ) { + return add_msg_debug_if_npc( type, string_format( msg, std::forward( args )... ) ); + } + } + template + void add_msg_debug_if_npc( debugmode::debug_filter type, const std::string &msg, + Args &&... args ) const { + if( debug_mode ) { + return add_msg_debug_if_npc( type, string_format( msg, std::forward( args )... ) ); + } + } + template + void add_msg_debug_if_npc( debugmode::debug_filter type, const translation &msg, + Args &&... args ) const { + if( debug_mode ) { + return add_msg_debug_if_npc( type, string_format( msg, std::forward( args )... ) ); + } + } + + virtual void add_msg_debug_player_or_npc( debugmode::debug_filter /*type*/, + const std::string &/*player_msg*/, + const std::string &/*npc_msg*/ ) const {} + void add_msg_debug_player_or_npc( debugmode::debug_filter /*type*/, + const translation &/*player_msg*/, + const translation &/*npc_msg*/ ) const; + template + void add_msg_debug_player_or_npc( debugmode::debug_filter type, const char *const player_msg, + const char *const npc_msg, Args &&... args ) const { + // expanding for string formatting can be expensive + if( debug_mode ) { + return add_msg_debug_player_or_npc( type, string_format( player_msg, + std::forward( args )... ), + string_format( npc_msg, std::forward( args )... ) ); + } + } + template + void add_msg_debug_player_or_npc( debugmode::debug_filter type, const std::string &player_msg, + const std::string &npc_msg, Args &&... args ) const { + if( debug_mode ) { + return add_msg_debug_player_or_npc( type, string_format( player_msg, + std::forward( args )... ), + string_format( npc_msg, std::forward( args )... ) ); + } + } + template + void add_msg_debug_player_or_npc( debugmode::debug_filter type, const translation &player_msg, + const translation &npc_msg, Args &&... args ) const { + if( debug_mode ) { + return add_msg_debug_player_or_npc( type, string_format( player_msg, + std::forward( args )... ), + string_format( npc_msg, std::forward( args )... ) ); + } + } + virtual void add_msg_player_or_say( const std::string &/*player_msg*/, const std::string &/*npc_speech*/ ) const {} virtual void add_msg_player_or_say( const game_message_params &/*params*/, @@ -1078,10 +1173,10 @@ class Creature : public location, public viewer bool fake = false; Creature(); - Creature( const Creature & ) = default; - Creature( Creature && ) = default; - Creature &operator=( const Creature & ) = default; - Creature &operator=( Creature && ) = default; + Creature( const Creature & ); + Creature( Creature && ) noexcept( map_is_noexcept ); + Creature &operator=( const Creature & ); + Creature &operator=( Creature && ) noexcept; protected: virtual void on_stat_change( const std::string &, int ) {} diff --git a/src/debug.cpp b/src/debug.cpp index f099e153ac147..e560cfa8c1afb 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -154,6 +154,52 @@ bool debug_has_error_been_observed() bool debug_mode = false; +namespace debugmode +{ +std::list enabled_filters; +std::string filter_name( debug_filter value ) +{ + // see debug.h for commentary + switch( value ) { + // *INDENT-OFF* + case DF_ACT_BUTCHER: return "DF_ACT_BUTCHER"; + case DF_ACT_LOCKPICK: return "DF_ACT_LOCKPICK"; + case DF_ACT_WORKOUT: return "DF_ACT_WORKOUT"; + case DF_ANATOMY_BP: return "DF_ANATOMY_BP"; + case DF_AVATAR: return "DF_AVATAR"; + case DF_BALLISTIC: return "DF_BALLISTIC"; + case DF_CHARACTER: return "DF_CHARACTER"; + case DF_CHAR_CALORIES: return "DF_CHAR_CALORIES"; + case DF_CHAR_HEALTH: return "DF_CHAR_HEALTH"; + case DF_CREATURE: return "DF_CREATURE"; + case DF_EFFECT: return "DF_EFFECT"; + case DF_EXPLOSION: return "DF_EXPLOSION"; + case DF_FOOD: return "DF_FOOD"; + case DF_GAME: return "DF_GAME"; + case DF_IEXAMINE: return "DF_IEXAMINE"; + case DF_IUSE: return "DF_IUSE"; + case DF_MAP: return "DF_MAP"; + case DF_MATTACK: return "DF_MATTACK"; + case DF_MELEE: return "DF_MELEE"; + case DF_MONSTER: return "DF_MONSTER"; + case DF_NPC: return "DF_NPC"; + case DF_OVERMAP: return "DF_OVERMAP"; + case DF_RANGED: return "DF_RANGED"; + case DF_REQUIREMENTS_MAP: return "DF_REQUIREMENTS_MAP"; + case DF_SOUND: return "DF_SOUND"; + case DF_TALKER: return "DF_TALKER"; + case DF_VEHICLE: return "DF_VEHICLE"; + case DF_VEHICLE_DRAG: return "DF_VEHICLE_DRAG"; + case DF_VEHICLE_MOVE: return "DF_VEHICLE_MOVE"; + // *INDENT-ON* + case DF_LAST: + default: + debugmsg( "Invalid DF_FILTER : %d", value ); + return "DF_INVALID"; + } +} +} // namespace debugmode + struct buffered_prompt_info { std::string filename; std::string line; @@ -639,11 +685,12 @@ static std::string debug_resolve_binary( const std::string &binary, std::ostream return binary; } + std::string suffix = "/" + binary; for( const std::string &path_elem : string_split( path, ':' ) ) { if( path_elem.empty() ) { continue; } - std::string candidate = path_elem + "/" + binary; + std::string candidate = path_elem + suffix; if( 0 == access( candidate.c_str(), X_OK ) ) { return candidate; } diff --git a/src/debug.h b/src/debug.h index 91f5634cde5ae..b5cc4bfb5cca6 100644 --- a/src/debug.h +++ b/src/debug.h @@ -3,6 +3,7 @@ #define CATA_SRC_DEBUG_H #include "string_formatter.h" +#include /** * debugmsg(msg, ...) @@ -221,6 +222,46 @@ std::ostream &DebugLog( DebugLevel, DebugClass ); */ extern bool debug_mode; +namespace debugmode +{ +// Please try to keep this alphabetically sorted +enum debug_filter : int { + DF_ACT_BUTCHER = 0, // butcher activity handler + DF_ACT_LOCKPICK, // lockpicking activity actor + DF_ACT_WORKOUT, // workout activity actor + DF_ANATOMY_BP, // anatomy::select_body_part() + DF_AVATAR, // avatar generic + DF_BALLISTIC, // ballistic generic + DF_CHARACTER, // character generic + DF_CHAR_CALORIES, // character stomach and calories + DF_CHAR_HEALTH, // character health related + DF_CREATURE, // creature generic + DF_EFFECT, // effects generic + DF_EXPLOSION, // explosion generic + DF_FOOD, // food generic + DF_GAME, // game generic + DF_IEXAMINE, // iexamine generic + DF_IUSE, // iuse generic + DF_MAP, // map generic + DF_MATTACK, // monster attack generic + DF_MELEE, // melee generic + DF_MONSTER, // monster generic + DF_NPC, // npc generic + DF_OVERMAP, // overmap generic + DF_RANGED, // ranged generic + DF_REQUIREMENTS_MAP, // activity_item_handler requirements_map() + DF_SOUND, // sound generic + DF_TALKER, // talker generic + DF_VEHICLE, // vehicle generic + DF_VEHICLE_DRAG, // vehicle coeff_air_drag() + DF_VEHICLE_MOVE, // vehicle move generic + DF_LAST // This is always the last entry +}; + +extern std::list enabled_filters; +std::string filter_name( debug_filter value ); +} // namespace debugmode + #if defined(BACKTRACE) /** * Write a stack backtrace to the given ostream diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index a5d7d4767bd3c..ff26a6bef399e 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -72,6 +72,7 @@ #include "monstergenerator.h" #include "morale_types.h" #include "mtype.h" +#include "mutation.h" #include "npc.h" #include "npc_class.h" #include "omdata.h" @@ -94,10 +95,12 @@ #include "string_input_popup.h" #include "trait_group.h" #include "translations.h" +#include "try_parse_integer.h" #include "type_id.h" #include "ui.h" #include "ui_manager.h" #include "units.h" +#include "units_utility.h" #include "veh_type.h" #include "vehicle.h" #include "vitamin.h" @@ -108,10 +111,10 @@ #include "weighted_list.h" static const efftype_id effect_asthma( "asthma" ); -static const efftype_id effect_flu( "flu" ); static const mtype_id mon_generator( "mon_generator" ); +static const trait_id trait_NONE( "NONE" ); static const trait_id trait_ASTHMA( "ASTHMA" ); #if defined(TILES) @@ -174,6 +177,7 @@ std::string enum_to_string( debug_menu::debug_menu case debug_menu::debug_menu_index::PRINT_NPC_MAGIC: return "PRINT_NPC_MAGIC"; case debug_menu::debug_menu_index::QUIT_NOSAVE: return "QUIT_NOSAVE"; case debug_menu::debug_menu_index::TEST_WEATHER: return "TEST_WEATHER"; + case debug_menu::debug_menu_index::WRITE_EOCS: return "WRITE_EOCS"; case debug_menu::debug_menu_index::SAVE_SCREENSHOT: return "SAVE_SCREENSHOT"; case debug_menu::debug_menu_index::GAME_REPORT: return "GAME_REPORT"; case debug_menu::debug_menu_index::DISPLAY_SCENTS_LOCAL: return "DISPLAY_SCENTS_LOCAL"; @@ -186,10 +190,10 @@ std::string enum_to_string( debug_menu::debug_menu case debug_menu::debug_menu_index::DISPLAY_REACHABILITY_ZONES: return "DISPLAY_REACHABILITY_ZONES"; case debug_menu::debug_menu_index::DISPLAY_RADIATION: return "DISPLAY_RADIATION"; case debug_menu::debug_menu_index::HOUR_TIMER: return "HOUR_TIMER"; - case debug_menu::debug_menu_index::LEARN_SPELLS: return "LEARN_SPELLS"; - case debug_menu::debug_menu_index::LEVEL_SPELLS: return "LEVEL_SPELLS"; + case debug_menu::debug_menu_index::CHANGE_SPELLS: return "CHANGE_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::EDIT_CAMP_LARDER: return "EDIT_CAMP_LARDER"; case debug_menu::debug_menu_index::VEHICLE_BATTERY_CHARGE: return "VEHICLE_BATTERY_CHARGE"; case debug_menu::debug_menu_index::GENERATE_EFFECT_LIST: return "GENERATE_EFFECT_LIST"; // *INDENT-ON* @@ -231,10 +235,8 @@ static int player_uilist() { uilist_entry( debug_menu_index::SET_AUTOMOVE, true, 'a', _( "Set automove route" ) ) }, }; if( !spell_type::get_all().empty() ) { - uilist_initializer.emplace_back( uilist_entry( debug_menu_index::LEARN_SPELLS, true, 'S', - _( "Learn all spells" ) ) ); - uilist_initializer.emplace_back( uilist_entry( debug_menu_index::LEVEL_SPELLS, true, 'L', - _( "Level a spell" ) ) ); + uilist_initializer.emplace_back( uilist_entry( debug_menu_index::CHANGE_SPELLS, true, 'S', + _( "Change spells" ) ) ); } return uilist( _( "Player…" ), uilist_initializer ); @@ -273,6 +275,7 @@ static int info_uilist( bool display_all_entries = true ) { uilist_entry( debug_menu_index::PRINT_FACTION_INFO, true, 'f', _( "Print faction info to console" ) ) }, { uilist_entry( debug_menu_index::PRINT_NPC_MAGIC, true, 'M', _( "Print NPC magic info to console" ) ) }, { uilist_entry( debug_menu_index::TEST_WEATHER, true, 'W', _( "Test weather" ) ) }, + { uilist_entry( debug_menu_index::WRITE_EOCS, true, 'C', _( "Write effect_on_condition(s) to eocs.output" ) ) }, { uilist_entry( debug_menu_index::TEST_MAP_EXTRA_DISTRIBUTION, true, 'e', _( "Test map extra list" ) ) }, { uilist_entry( debug_menu_index::GENERATE_EFFECT_LIST, true, 'L', _( "Generate effect list" ) ) }, }; @@ -345,6 +348,7 @@ static int map_uilist() { uilist_entry( debug_menu_index::OM_EDITOR, true, 'O', _( "Overmap editor" ) ) }, { uilist_entry( debug_menu_index::MAP_EXTRA, true, 'm', _( "Spawn map extra" ) ) }, { uilist_entry( debug_menu_index::NESTED_MAPGEN, true, 'n', _( "Spawn nested mapgen" ) ) }, + { uilist_entry( debug_menu_index::EDIT_CAMP_LARDER, true, 'l', _( "Edit the faction camp larder" ) ) }, }; return uilist( _( "Map…" ), uilist_initializer ); @@ -422,6 +426,592 @@ static cata::optional debug_menu_uilist( bool display_all_entr } } +static void spell_description( + std::tuple &spl_data, int width, Character &chrc ) +{ + std::ostringstream description; + + const int spl_level = std::get<1>( spl_data ); + spell spl( std::get<0>( spl_data ).id ); + spl.set_level( spl_level ); + + nc_color gray = c_light_gray; + nc_color yellow = c_yellow; + nc_color light_green = c_light_green; + + // # spell_id + description << colorize( string_format( "# %s", spl.id().str() ), c_cyan ) << '\n'; + + // Name: spell name + description << string_format( _( "Name: %1$s" ), colorize( spl.name(), c_white ) ) << '\n'; + + + // Class: Spell Class + description << string_format( _( "Class: %1$s" ), colorize( spl.spell_class() == trait_NONE ? + _( "Classless" ) : spl.spell_class()->name(), + yellow ) ) << "\n"; + + // Spell description + description << spl.description() << '\n'; + + // Spell Casting flags + description << spell_desc::enumerate_spell_data( spl ) << '\n'; + + // Spell Level: 0 / 0 (MAX) + description << string_format( + //~ %1$s - spell current level, %2$s - spell max level, %3$s - is max level + _( "Spell Level: %1$s / %2$d %3$s" ), + spl_level == -1 ? _( "Unlearned" ) : std::to_string( spl_level ), + spl.get_max_level(), + spl_level == spl.get_max_level() ? _( "(MAX)" ) : "" ) << '\n'; + + // Difficulty: 0 ( 0.0 % Failure Chance) + description << string_format( + //~ %1$d - difficulty, %2$s - failure chance + _( "Difficulty: %1$d ( %2$s )" ), + spl.get_difficulty(), spl.colorized_fail_percent( chrc ) ) << '\n'; + + + const std::string impeded = _( "(impeded)" ); + + // Casting Cost: 0 (impeded) ( 0 current ) + description << string_format( + //~ %1$s - energy cost, %2$s - is casting impeded, %3$s - current character energy + _( "Casting Cost: %1$s %2$s ( %3$s current ) " ), + spl.energy_cost_string( chrc ), + spell_desc::energy_cost_encumbered( spl, chrc ) ? impeded : "", + spl.energy_cur_string( chrc ) ) << '\n'; + + // Casting Time: 0 (impeded) + description << string_format( + //~ %1$s - cast time, %2$s - is casting impeded, %3$s - casting base time + _( "Casting Time: %1$s %2$s ( %3$s base time ) " ), + to_string( time_duration::from_moves( spl.casting_time( chrc ) ) ), + spell_desc::casting_time_encumbered( spl, chrc ) ? impeded : "", + to_string( time_duration::from_moves( std::get<0>( spl_data ).base_casting_time ) ) ) << '\n'; + + std::string targets; + if( spl.is_valid_target( spell_target::none ) ) { + targets = _( "self" ); + } else { + targets = spl.enumerate_targets(); + } + description << string_format( _( "Valid Targets: %1$s" ), targets ) << '\n'; + + std::string target_ids = spl.list_targeted_monster_names(); + if( !target_ids.empty() ) { + description << string_format( _( "Only affects the monsters: %1$s" ), target_ids ) << '\n'; + } + + const int damage = spl.damage(); + const std::string spl_eff = spl.effect(); + std::string damage_string; + std::string range_string; + std::string aoe_string; + // if it's any type of attack spell, the stats are normal. + if( spl_eff == "attack" ) { + if( damage > 0 ) { + std::string dot_string; + if( spl.damage_dot() ) { + //~ amount of damage per second, abbreviated + dot_string = string_format( _( ", %1$d/sec" ), spl.damage_dot() ); + } + damage_string = string_format( _( "Damage: %1$s %2$s%3$s" ), spl.damage_string(), + spl.damage_type_string(), dot_string ); + damage_string = colorize( damage_string, spl.damage_type_color() ); + } else if( damage < 0 ) { + damage_string = string_format( _( "Healing: %1$s" ), colorize( spl.damage_string(), + light_green ) ); + } + + if( spl.aoe() > 0 ) { + std::string aoe_string_temp = _( "Spell Radius" ); + std::string degree_string; + if( spl.shape() == spell_shape::cone ) { + aoe_string_temp = _( "Cone Arc" ); + degree_string = _( "degrees" ); + } else if( spl.shape() == spell_shape::line ) { + aoe_string_temp = _( "Line Width" ); + } + aoe_string = string_format( _( "%1$s: %2$d %3$s" ), aoe_string_temp, spl.aoe(), degree_string ); + } + + } else if( spl_eff == "teleport_random" ) { + if( spl.aoe() > 0 ) { + aoe_string = string_format( _( "Variance: %1$d" ), spl.aoe() ); + } + + } else if( spl_eff == "spawn_item" ) { + damage_string = string_format( _( "Spawn %1$d %2$s" ), spl.damage(), + item::nname( itype_id( spl.effect_data() ), spl.damage() ) ); + + } else if( spl_eff == "summon" ) { + std::string monster_name = "FIXME"; + if( spl.has_flag( spell_flag::SPAWN_GROUP ) ) { + // TODO: Get a more user-friendly group name + if( MonsterGroupManager::isValidMonsterGroup( mongroup_id( spl.effect_data() ) ) ) { + monster_name = string_format( _( "from %1$s" ), spl.effect_data() ); + } else { + debugmsg( "Unknown monster group: %s", spl.effect_data() ); + } + } else { + monster_name = monster( mtype_id( spl.effect_data() ) ).get_name(); + } + damage_string = string_format( _( "Summon: %1$d %2$s" ), spl.damage(), monster_name ); + aoe_string = string_format( _( "Spell Radius: %1$d" ), spl.aoe() ); + + } else if( spl_eff == "targeted_polymorph" ) { + std::string monster_name = spl.effect_data(); + if( spl.has_flag( spell_flag::POLYMORPH_GROUP ) ) { + // TODO: Get a more user-friendly group name + if( MonsterGroupManager::isValidMonsterGroup( mongroup_id( spl.effect_data() ) ) ) { + monster_name = _( "random creature" ); + } else { + debugmsg( "Unknown monster group: %s", spl.effect_data() ); + } + } else if( monster_name.empty() ) { + monster_name = _( "random creature" ); + } else { + monster_name = mtype_id( spl.effect_data() )->nname(); + } + damage_string = string_format( _( "Targets under: %1$dhp become a %2$s" ), spl.damage(), + monster_name ); + + } else if( spl_eff == "ter_transform" ) { + aoe_string = string_format( "Spell Radius: %1$s", spl.aoe_string() ); + + } else if( spl_eff == "banishment" ) { + damage_string = string_format( _( "Damage: %1$s %2$s" ), spl.damage_string(), + spl.damage_type_string() ); + if( spl.aoe() > 0 ) { + aoe_string = string_format( _( "Spell Radius: %1$d" ), spl.aoe() ); + } + } + + // Range / AOE in two columns + description << string_format( _( "Range: %1$s" ), + spl.range() <= 0 ? _( "self" ) : std::to_string( spl.range() ) ) << '\n'; + + + description << aoe_string << '\n'; + + // One line for damage / healing / spawn / summon effect + description << damage_string << '\n'; + + // todo: damage over time here, when it gets implemented + + // Show duration for spells that endure + if( spl.duration() > 0 || spl.has_flag( spell_flag::PERMANENT ) ) { + description << string_format( _( "Duration: %1$s" ), spl.duration_string() ) << '\n'; + } + + // helper function for printing tool and item component requirement lists + const auto print_vec_string = [&]( const std::vector &vec ) { + for( const std::string &line_str : vec ) { + description << line_str << '\n'; + } + }; + + if( spl.has_components() ) { + if( !spl.components().get_components().empty() ) { + print_vec_string( spl.components().get_folded_components_list( width - 2, gray, + chrc.crafting_inventory(), return_true ) ); + } + if( !( spl.components().get_tools().empty() && spl.components().get_qualities().empty() ) ) { + print_vec_string( spl.components().get_folded_tools_list( width - 2, gray, + chrc.crafting_inventory() ) ); + } + } + + std::get<2>( spl_data ) = description.str(); +} + +void change_spells( Character &character ) +{ + if( spell_type::get_all().empty() ) { + add_msg( m_info, _( "There are no spells to change." ) ); + return; + } + + static character_id last_char_id = character.getID(); + + using spell_tuple = std::tuple; + const size_t spells_all_size = spell_type::get_all().size(); + // all spells with cached string list + // the string is rebuilt every time it's empty or its level changed + static std::vector spells_all( spells_all_size ); + // maps which spells will show on the list + std::vector spells_relative( spells_all_size ); + + // number of spells changed, current map is invalid + bool rebuild_string_cache = false; + if( spells_all.size() != spells_all_size || last_char_id != character.getID() ) { + rebuild_string_cache = true; + last_char_id = character.getID(); + spells_all.clear(); + } + + int spname_len = 0; + for( size_t i = 0; i < spells_all_size; ++i ) { + if( rebuild_string_cache ) { + spells_all.emplace_back( spell_type{}, -1, std::string{} ); + std::get<2>( spells_all[i] ).clear(); + } + + if( std::get<0>( spells_all[i] ).id != spell_type::get_all()[i].id ) { + std::get<0>( spells_all[i] ) = spell_type::get_all()[i]; + std::get<1>( spells_all[i] ) = -1; + std::get<2>( spells_all[i] ).clear(); + } + + spells_relative[i] = &spells_all[i]; + + // get max spell name length + spname_len = std::max( spname_len, utf8_width( std::get<0>( spells_all[i] ).name.translated() ) ); + } + spname_len += 2; + + // fill the levels for spells the character knowns + for( const spell *sp : character.magic->get_spells() ) { + auto iterator = std::find_if( spells_all.begin(), + spells_all.end(), [&sp]( spell_tuple & spt ) -> bool { + return std::get<0>( spt ).id == sp->id(); + } ); + std::get<1>( spells_all[iterator - spells_all.begin()] ) = sp->get_level(); + std::get<2>( spells_all[iterator - spells_all.begin()] ).clear(); + } + + auto set_spell = [&character]( spell_type & splt, int spell_level ) { + if( spell_level == -1 ) { + character.magic->get_spellbook().erase( splt.id ); + return; + } else if( !character.magic->knows_spell( splt.id ) ) { + spell spl( splt.id ); + character.magic->get_spellbook().emplace( splt.id, spl ); + } + + character.magic->get_spell( splt.id ).set_exp( spell::exp_for_level( spell_level ) ); + }; + + ui_adaptor spellsui; + border_helper borders; + + struct win_info { + catacurses::window window; + border_helper::border_info *border = nullptr; + int width; + point start; + }; + + struct win_info w_name; + w_name.border = &borders.add_border(); + w_name.width = spname_len + 1; + w_name.start = point_zero; + + struct win_info w_level; + w_level.border = &borders.add_border(); + w_level.width = 11; + w_level.start = {w_name.width, 0}; + + struct win_info w_descborder; + w_descborder.border = &borders.add_border(); + + // desc is inside descborder with a padding of 2 characters + struct win_info w_desc; + + scrollbar scrllbr; + scrllbr.offset_x( 0 ).offset_y( 1 ).border_color( c_magenta ); + + spellsui.on_screen_resize( [&]( ui_adaptor & ui ) { + + w_descborder.start = {w_level.start.x + w_level.width, 0}; + w_descborder.width = TERMX - w_descborder.start.x; + + w_desc.width = w_descborder.width - 4; + w_desc.start = {w_descborder.start.x + 2, 1}; + + w_name.window = catacurses::newwin( TERMY, w_name.width, w_name.start ); + w_level.window = catacurses::newwin( TERMY, w_level.width, w_level.start ); + w_descborder.window = catacurses::newwin( TERMY, w_descborder.width, w_descborder.start ); + w_desc.window = catacurses::newwin( TERMY - 2, w_desc.width, w_desc.start ); + + w_name.border->set( w_name.start, { w_name.width, TERMY } ); + w_level.border->set( w_level.start, { w_level.width, TERMY } ); + w_descborder.border->set( w_descborder.start, { w_descborder.width, TERMY } ); + + scrllbr.viewport_size( TERMY - 2 ); + ui.position( point_zero, { TERMX, TERMY } ); + } ); + spellsui.mark_resize(); + + input_context ctxt( "DEBUG_SPELLS" ); + ctxt.register_action( "UNLEARN_SPELL" ); // Quickly unlearn a spell + ctxt.register_action( "TOGGLE_ALL_SPELL" ); // Cycle level on all spells in spells_relative + ctxt.register_action( "SHOW_ONLY_LEARNED" ); // Removes all unlearned spells in spells_relative + ctxt.register_cardinal(); // left and right change spell level + ctxt.register_action( "QUIT" ); + ctxt.register_action( "CONFIRM" ); // set a spell to a level + ctxt.register_action( "FILTER" ); + ctxt.register_action( "RESET_FILTER" ); + ctxt.register_action( "HELP_KEYBINDINGS" ); + + int spells_start = 0; + int spell_selected = 0; + std::string filterstring; + spellsui.on_redraw( [&]( const ui_adaptor & ) { + werase( w_name.window ); + werase( w_level.window ); + werase( w_descborder.window ); + werase( w_desc.window ); + + borders.draw_border( w_name.window, c_magenta ); + borders.draw_border( w_level.window, c_magenta ); + borders.draw_border( w_descborder.window, c_magenta ); + + center_print( w_name.window, 0, c_magenta, _( "<Spell Name>" ) ); + center_print( w_level.window, 0, c_magenta, _( "<Level>" ) ); + center_print( w_descborder.window, 0, c_magenta, _( "<Description>" ) ); + + nc_color magenta = c_magenta; + const std::string help_keybindings = string_format( + _( "<[%1$s] Keybindings>" ), + ctxt.get_desc( "HELP_KEYBINDINGS" ) ); + print_colored_text( w_descborder.window, + point( w_descborder.width - help_keybindings.length() + 42, TERMY - 1 ), + magenta, magenta, help_keybindings ); + + std::string help_filter; + if( filterstring.empty() ) { + help_filter = string_format( _( "<[%1$s] Filter>" ), + ctxt.get_desc( "FILTER" ) ); + } else { + help_filter = string_format( "<%s>", filterstring ); + } + + print_colored_text( w_name.window, point( 1, TERMY - 1 ), + magenta, magenta, help_filter ); + + const int relative_size = spells_relative.size(); + scrllbr.content_size( relative_size ); + scrllbr.viewport_pos( spell_selected ); + scrllbr.apply( w_name.window ); + + calcStartPos( spells_start, spell_selected, TERMY - 2, relative_size ); + + int line_number = 1; + for( int i = spells_start; i < relative_size; ++i ) { + if( line_number == TERMY - 1 ) { + break; + } + + const spell_type &splt = std::get<0>( *spells_relative[i] ); + const int &spell_level = std::get<1>( *spells_relative[i] ); + + nc_color spell_color = spell_level > -1 ? c_green : c_light_gray; + spell_color = i == spell_selected ? hilite( spell_color ) : spell_color; + + mvwprintz( w_name.window, point( 2, line_number ), + spell_color, splt.name.translated() ); + mvwprintz( w_level.window, point( 2, line_number++ ), spell_color, + _( "%1$-3d/%2$3d" ), spell_level, splt.max_level ); + } + + nc_color gray = c_light_gray; + print_colored_text( w_desc.window, point_zero, gray, gray, + std::get<2>( *spells_relative[spell_selected] ) ); + + wnoutrefresh( w_name.window ); + wnoutrefresh( w_level.window ); + wnoutrefresh( w_descborder.window ); + wnoutrefresh( w_desc.window ); + } ); + + auto update_description = [&]( bool force ) -> void { + if( force || std::get<2>( *spells_relative[spell_selected] ).empty() ) + { + spell_description( *spells_relative[spell_selected], w_desc.width, character ); + } + }; + + // keep the same spell selected + auto spell_middle_or_id = [&]( const spell_id & spellid ) -> void { + if( spellid.is_empty() ) + { + spell_selected = 0; + return; + } + + // in case we don't find anything, keep selection in the middle of screen + const size_t spells_relative_size = spells_relative.size(); + spell_selected = std::min( ( TERMY - 2 ) / 2, static_cast( spells_relative_size ) / 2 ); + for( size_t i = 0; i < spells_relative_size; ++i ) + { + if( std::get<0>( *spells_relative[i] ).id == spellid ) { + spell_selected = i; + break; + } + } + }; + + // reset spells_relative vector + auto reset_spells_relative = [&]() -> void { + for( spell_tuple &spt : spells_all ) + { + spells_relative.emplace_back( &spt ); + } + }; + + auto filter_spells = [&]( ) -> void { + const spell_id &spellid = std::get<0>( *spells_relative[spell_selected] ).id; + spells_relative.clear(); + if( filterstring.empty() ) + { + reset_spells_relative(); + } else + { + for( spell_tuple &spt : spells_all ) { + const spell_type &spl = std::get<0>( spt ); + if( lcmatch( spl.name.translated(), filterstring ) || + lcmatch( spl.id.str(), filterstring ) ) { + spells_relative.emplace_back( &spt ); + } + } + + // no spell found, reset relative list + if( spells_relative.empty() ) { + reset_spells_relative(); + popup( _( "Nothing found." ) ); + } + } + + spell_middle_or_id( spellid ); + }; + + auto toggle_all_spells = [&]( int level ) { + // -2 sets it to max level + for( spell_tuple *spt : spells_relative ) { + std::get<1>( *spt ) = level > -2 ? level : std::get<0>( *spt ).max_level; + set_spell( std::get<0>( *spt ), std::get<1>( *spt ) ); + std::get<2>( *spt ).clear(); + } + }; + + static spell_id last_selected_spellid; + spell_middle_or_id( last_selected_spellid ); + + // 0 -> turn off all spells + // 1 -> set all spells to level 0 + // 2 -> set all spells to their max level + int toggle_spells_state = 1; + + bool showing_only_learned = false; + + bool force_update_description = false; + + while( true ) { + update_description( force_update_description ); + force_update_description = false; + + ui_manager::redraw(); + const std::string action = ctxt.handle_input(); + + if( action == "QUIT" ) { + last_selected_spellid = std::get<0>( *spells_relative[spell_selected] ).id; + break; + + } else if( action == "FILTER" ) { + string_input_popup() + .title( _( "Filter:" ) ) + .width( 16 ) + .description( _( "Filter by spell name or id" ) ) + .edit( filterstring ); + + showing_only_learned = false; + filter_spells( ); + + } else if( action == "RESET_FILTER" ) { + showing_only_learned = false; + filterstring.clear(); + filter_spells(); + + } else if( action == "UP" ) { + if( !spell_selected ) { + spell_selected = spells_relative.size() - 1; + } else { + spell_selected--; + } + + } else if( action == "DOWN" ) { + spell_selected++; + if( static_cast( spell_selected ) == spells_relative.size() ) { + spell_selected = 0; + } + + } else if( action == "LEFT" ) { + int &spell_level = std::get<1>( *spells_relative[spell_selected] ); + spell_level = std::max( -1, spell_level - 1 ); + set_spell( std::get<0>( *spells_relative[spell_selected] ), spell_level ); + force_update_description = true; + + } else if( action == "RIGHT" ) { + int &spell_level = std::get<1>( *spells_relative[spell_selected] ); + spell_level = std::min( spell_level + 1, + std::get<0>( *spells_relative[spell_selected] ).max_level ); + set_spell( std::get<0>( *spells_relative[spell_selected] ), spell_level ); + force_update_description = true; + + } else if( action == "CONFIRM" ) { + int &spell_level = std::get<1>( *spells_relative[spell_selected] ); + query_int( spell_level, _( "Set spell level to? Currently: %1$d" ), spell_level ); + spell_level = clamp( spell_level, -1, std::get<0>( *spells_relative[spell_selected] ).max_level ); + set_spell( std::get<0>( *spells_relative[spell_selected] ), spell_level ); + force_update_description = true; + + } else if( action == "UNLEARN_SPELL" ) { + int &spell_level = std::get<1>( *spells_relative[spell_selected] ); + spell_level = -1; + set_spell( std::get<0>( *spells_relative[spell_selected] ), spell_level ); + force_update_description = true; + + } else if( action == "TOGGLE_ALL_SPELL" ) { + if( toggle_spells_state == 0 ) { + toggle_spells_state = 1; + toggle_all_spells( -1 ); // unlearn all spells + } else if( toggle_spells_state == 1 ) { + toggle_spells_state = 2; + toggle_all_spells( 0 ); // sets all spells to the minimum level + } else { + toggle_spells_state = 0; + toggle_all_spells( -2 ); // max level + } + + } else if( action == "SHOW_ONLY_LEARNED" ) { + showing_only_learned = !showing_only_learned; + + const spell_id &spellid = std::get<0>( *spells_relative[spell_selected] ).id; + spells_relative.clear(); + if( showing_only_learned ) { + for( spell_tuple &spt : spells_all ) { + if( std::get<1>( spt ) > -1 ) { + spells_relative.emplace_back( &spt ); + } + } + + if( spells_relative.empty() ) { + popup( _( "Nothing found." ) ); + showing_only_learned = false; + } + } + + if( !showing_only_learned ) { + reset_spells_relative(); + } + + spell_middle_or_id( spellid ); + } + } +} + void teleport_short() { const cata::optional where = g->look_around(); @@ -431,7 +1021,7 @@ void teleport_short() } g->place_player( *where ); const tripoint new_pos( player_location.pos() ); - add_msg( _( "You teleport to point (%d,%d,%d)." ), new_pos.x, new_pos.y, new_pos.z ); + add_msg( _( "You teleport to point %s." ), new_pos.to_string() ); } void teleport_long() @@ -441,7 +1031,7 @@ void teleport_long() return; } g->place_player_overmap( where ); - add_msg( _( "You teleport to submap (%s)." ), where.to_string() ); + add_msg( _( "You teleport to submap %s." ), where.to_string() ); } void teleport_overmap( bool specific_coordinates ) @@ -450,17 +1040,33 @@ void teleport_overmap( bool specific_coordinates ) tripoint_abs_omt where; if( specific_coordinates ) { const std::string text = string_input_popup() - .title( "Teleport where?" ) + .title( _( "Teleport where?" ) ) .width( 20 ) .query_string(); if( text.empty() ) { return; } const std::vector coord_strings = string_split( text, ',' ); + if( coord_strings.size() < 2 || coord_strings.size() > 3 ) { + popup( _( "Error interpreting teleport target: " + "expected two or three comma-separated values; got %zu" ), + coord_strings.size() ); + return; + } + std::vector coord_ints; + for( const std::string &coord_string : coord_strings ) { + ret_val parsed_coord = try_parse_integer( coord_string, true ); + if( !parsed_coord.success() ) { + popup( _( "Error interpreting teleport target: %s" ), parsed_coord.str() ); + return; + } + coord_ints.push_back( parsed_coord.value() ); + } + cata_assert( coord_ints.size() >= 2 ); tripoint coord; - coord.x = !coord_strings.empty() ? std::atoi( coord_strings[0].c_str() ) : 0; - coord.y = coord_strings.size() >= 2 ? std::atoi( coord_strings[1].c_str() ) : 0; - coord.z = coord_strings.size() >= 3 ? std::atoi( coord_strings[2].c_str() ) : 0; + coord.x = coord_ints[0]; + coord.y = coord_ints[1]; + coord.z = coord_ints.size() >= 3 ? coord_ints[2] : 0; where = tripoint_abs_omt( OMAPX * coord.x, OMAPY * coord.y, coord.z ); } else { const cata::optional dir_ = choose_direction( _( "Where is the desired overmap?" ) ); @@ -580,7 +1186,7 @@ void character_edit_menu() } enum { - D_DESC, D_SKILLS, D_PROF, D_STATS, D_ITEMS, D_DELETE_ITEMS, D_ITEM_WORN, + D_DESC, D_SKILLS, D_PROF, D_STATS, D_SPELLS, D_ITEMS, D_DELETE_ITEMS, D_ITEM_WORN, D_HP, D_STAMINA, D_MORALE, D_PAIN, D_NEEDS, D_HEALTHY, D_STATUS, D_MISSION_ADD, D_MISSION_EDIT, D_TELE, D_MUTATE, D_CLASS, D_ATTITUDE, D_OPINION, D_ADD_EFFECT, D_ASTHMA }; @@ -589,6 +1195,7 @@ void character_edit_menu() nmenu.addentry( D_SKILLS, true, 's', "%s", _( "Edit [s]kills" ) ); nmenu.addentry( D_PROF, true, 'P', "%s", _( "Edit [P]roficiencies" ) ); nmenu.addentry( D_STATS, true, 't', "%s", _( "Edit s[t]ats" ) ); + nmenu.addentry( D_SPELLS, true, 'l', "%s", _( "Edit spe[l]ls" ) ); nmenu.addentry( D_ITEMS, true, 'i', "%s", _( "Grant [i]tems" ) ); nmenu.addentry( D_DELETE_ITEMS, true, 'd', "%s", _( "[d]elete (all) items" ) ); nmenu.addentry( D_ITEM_WORN, true, 'w', "%s", @@ -652,6 +1259,9 @@ void character_edit_menu() } } break; + case D_SPELLS: + change_spells( *p.as_character() ); + break; case D_PROF: wishproficiency( &p ); break; @@ -896,13 +1506,43 @@ void character_edit_menu() } break; case D_NEEDS: { + std::pair hunger_pair = p.get_hunger_description(); + std::pair thirst_pair = p.get_thirst_description(); + std::pair fatigue_pair = p.get_fatigue_description(); + + std::stringstream data; + data << string_format( _( "Hunger: %d %s" ), p.get_hunger(), colorize( hunger_pair.first, + hunger_pair.second ) ) << std::endl; + data << string_format( _( "Thirst: %d %s" ), p.get_thirst(), colorize( thirst_pair.first, + thirst_pair.second ) ) << std::endl; + data << string_format( _( "Fatigue: %d %s" ), p.get_fatigue(), colorize( fatigue_pair.first, + fatigue_pair.second ) ) << std::endl; + data << std::endl; + data << _( "Stored kCal: " ) << p.get_stored_kcal() << std::endl; + data << _( "Total kCal: " ) << p.get_stored_kcal() + p.stomach.get_calories() + + p.guts.get_calories() << std::endl; + data << std::endl; + data << _( "Stomach contents" ) << std::endl; + data << _( " Total volume: " ) << vol_to_string( p.stomach.contains() ) << std::endl; + data << _( " Water volume: " ) << vol_to_string( p.stomach.get_water() ) << std::endl; + data << string_format( _( " kCal: %d" ), p.stomach.get_calories() ) << std::endl; + data << std::endl; + data << _( "Gut contents" ) << std::endl; + data << _( " Total volume: " ) << vol_to_string( p.guts.contains() ) << std::endl; + data << _( " Water volume: " ) << vol_to_string( p.guts.get_water() ) << std::endl; + data << string_format( _( " kCal: %d" ), p.guts.get_calories() ) << std::endl; + uilist smenu; + smenu.text = data.str(); smenu.addentry( 0, true, 'h', "%s: %d", _( "Hunger" ), p.get_hunger() ); smenu.addentry( 1, true, 's', "%s: %d", _( "Stored kCal" ), p.get_stored_kcal() ); - smenu.addentry( 2, true, 't', "%s: %d", _( "Thirst" ), p.get_thirst() ); - smenu.addentry( 3, true, 'f', "%s: %d", _( "Fatigue" ), p.get_fatigue() ); - smenu.addentry( 4, true, 'd', "%s: %d", _( "Sleep Deprivation" ), p.get_sleep_deprivation() ); - smenu.addentry( 5, true, 'a', _( "Reset all basic needs" ) ); + smenu.addentry( 2, true, 'S', "%s: %d", _( "Stomach kCal" ), p.stomach.get_calories() ); + smenu.addentry( 3, true, 'G', "%s: %d", _( "Gut kCal" ), p.guts.get_calories() ); + smenu.addentry( 4, true, 't', "%s: %d", _( "Thirst" ), p.get_thirst() ); + smenu.addentry( 5, true, 'f', "%s: %d", _( "Fatigue" ), p.get_fatigue() ); + smenu.addentry( 6, true, 'd', "%s: %d", _( "Sleep Deprivation" ), p.get_sleep_deprivation() ); + smenu.addentry( 7, true, 'a', _( "Reset all basic needs" ) ); + smenu.addentry( 8, true, 'e', _( "Empty stomach and guts" ) ); const auto &vits = vitamin::all(); for( const auto &v : vits ) { @@ -925,24 +1565,36 @@ void character_edit_menu() break; case 2: + if( query_int( value, _( "Set stomach kCal to? Currently: %d" ), p.stomach.get_calories() ) ) { + p.stomach.mod_calories( value - p.stomach.get_calories() ); + } + break; + + case 3: + if( query_int( value, _( "Set gut kCal to? Currently: %d" ), p.guts.get_calories() ) ) { + p.guts.mod_calories( value - p.guts.get_calories() ); + } + break; + + case 4: if( query_int( value, _( "Set thirst to? Currently: %d" ), p.get_thirst() ) ) { p.set_thirst( value ); } break; - case 3: + case 5: if( query_int( value, _( "Set fatigue to? Currently: %d" ), p.get_fatigue() ) ) { p.set_fatigue( value ); } break; - case 4: + case 6: if( query_int( value, _( "Set sleep deprivation to? Currently: %d" ), p.get_sleep_deprivation() ) ) { p.set_sleep_deprivation( value ); } break; - case 5: + case 7: p.initialize_stomach_contents(); p.set_hunger( 0 ); p.set_thirst( 0 ); @@ -950,9 +1602,13 @@ void character_edit_menu() p.set_sleep_deprivation( 0 ); p.set_stored_kcal( p.get_healthy_kcal() ); break; + case 8: + p.stomach.empty(); + p.guts.empty(); + break; default: - if( smenu.ret >= 6 && smenu.ret < static_cast( vits.size() + 6 ) ) { - auto iter = std::next( vits.begin(), smenu.ret - 6 ); + if( smenu.ret >= 9 && smenu.ret < static_cast( vits.size() + 9 ) ) { + auto iter = std::next( vits.begin(), smenu.ret - 9 ); if( query_int( value, _( "Set %s to? Currently: %d" ), iter->second.name(), p.vitamin_get( iter->first ) ) ) { p.vitamin_set( iter->first, value ); @@ -1309,6 +1965,208 @@ void draw_benchmark( const int max_difference ) difference / 1000.0, 1000.0 * draw_counter / static_cast( difference ) ); } +static void debug_menu_game_state() +{ + avatar &player_character = get_avatar(); + map &here = get_map(); + tripoint abs_sub = here.get_abs_sub(); + std::string mfus; + std::vector> sorted; + sorted.reserve( m_flag::MF_MAX ); + for( int f = 0; f < m_flag::MF_MAX; f++ ) { + sorted.emplace_back( static_cast( f ), + MonsterGenerator::generator().m_flag_usage_stats[f] ); + } + std::sort( sorted.begin(), sorted.end(), []( std::pair a, std::pair b ) { + return a.second != b.second ? a.second > b.second : a.first < b.first; + } ); + popup( player_character.total_daily_calories_string() ); + for( auto &m_flag_stat : sorted ) { + mfus += string_format( "%s;%d\n", io::enum_to_string( m_flag_stat.first ), + m_flag_stat.second ); + } + DebugLog( D_INFO, DC_ALL ) << "Monster flag usage statistics:\nFLAG;COUNT\n" << mfus; + std::fill( MonsterGenerator::generator().m_flag_usage_stats.begin(), + MonsterGenerator::generator().m_flag_usage_stats.end(), 0 ); + 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 += ngettext( "%d creature exists.\n", "%d creatures exist.\n", g->num_creatures() ); + + std::unordered_map creature_counts; + for( Creature &critter : g->all_creatures() ) { + std::string this_name = critter.get_name(); + creature_counts[this_name]++; + } + + if( !creature_counts.empty() ) { + std::vector> creature_names_sorted; + creature_names_sorted.reserve( creature_counts.size() ); + for( const std::pair &it : creature_counts ) { + creature_names_sorted.emplace_back( it ); + } + + std::stable_sort( creature_names_sorted.begin(), creature_names_sorted.end(), []( auto a, auto b ) { + return a.second > b.second; + } ); + + s += _( "\nSpecific creature type list:\n" ); + for( const std::pair &crit_name : creature_names_sorted ) { + s += string_format( "%i %s\n", crit_name.second, crit_name.first ); + } + } + + popup_top( + s.c_str(), + player_character.posx(), player_character.posy(), abs_sub.x, abs_sub.y, + overmap_buffer.ter( player_character.global_omt_location() )->get_name(), + to_turns( calendar::turn - calendar::turn_zero ), + g->num_creatures() ); + for( const npc &guy : g->all_npcs() ) { + tripoint t = guy.global_sm_location(); + add_msg( m_info, _( "%s: map ( %d:%d ) pos ( %d:%d )" ), guy.name, t.x, + t.y, guy.posx(), guy.posy() ); + } + + add_msg( m_info, _( "(you: %d:%d)" ), player_character.posx(), player_character.posy() ); + std::string stom = + _( "Stomach Contents: %d ml / %d ml kCal: %d, Water: %d ml" ); + add_msg( m_info, stom.c_str(), units::to_milliliter( player_character.stomach.contains() ), + units::to_milliliter( player_character.stomach.capacity( player_character ) ), + player_character.stomach.get_calories(), + units::to_milliliter( player_character.stomach.get_water() ), player_character.get_hunger() ); + stom = _( "Guts Contents: %d ml / %d ml kCal: %d, Water: %d ml\nHunger: %d, Thirst: %d, kCal: %d / %d" ); + add_msg( m_info, stom.c_str(), units::to_milliliter( player_character.guts.contains() ), + units::to_milliliter( player_character.guts.capacity( player_character ) ), + player_character.guts.get_calories(), units::to_milliliter( player_character.guts.get_water() ), + player_character.get_hunger(), player_character.get_thirst(), player_character.get_stored_kcal(), + player_character.get_healthy_kcal() ); + add_msg( m_info, _( "Body Mass Index: %.0f\nBasal Metabolic Rate: %i" ), player_character.get_bmi(), + player_character.get_bmr() ); + add_msg( m_info, _( "Player activity level: %s" ), player_character.activity_level_str() ); + if( get_option( "STATS_THROUGH_KILLS" ) ) { + add_msg( m_info, _( "Kill xp: %d" ), player_character.kill_xp() ); + } + g->invalidate_main_ui_adaptor(); + g->disp_NPCs(); +} + +static void debug_menu_spawn_vehicle() +{ + avatar &player_character = get_avatar(); + map &here = get_map(); + if( here.veh_at( player_character.pos() ) ) { + dbg( D_ERROR ) << "game:load: There's already vehicle here"; + debugmsg( "There's already vehicle here" ); + } else { + // Vector of name, id so that we can sort by name + std::vector> veh_strings; + for( auto &elem : vehicle_prototype::get_all() ) { + if( elem == vproto_id( "custom" ) ) { + continue; + } + veh_strings.emplace_back( elem->name.translated(), elem ); + } + std::sort( veh_strings.begin(), veh_strings.end(), localized_compare ); + uilist veh_menu; + veh_menu.text = _( "Choose vehicle to spawn" ); + int menu_ind = 0; + for( auto &elem : veh_strings ) { + //~ Menu entry in vehicle wish menu: 1st string: displayed name, 2nd string: internal name of vehicle + veh_menu.addentry( menu_ind, true, MENU_AUTOASSIGN, _( "%1$s (%2$s)" ), + elem.first, elem.second.c_str() ); + ++menu_ind; + } + veh_menu.query(); + 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; + tripoint dest = player_character.pos(); + 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_degrees, 100, veh_cond_menu.ret - 1 ); + if( veh != nullptr ) { + here.board_vehicle( dest, &player_character ); + } + } + } + } +} + +static void debug_menu_change_time() +{ + auto set_turn = [&]( const int initial, const time_duration & factor, const char *const msg ) { + string_input_popup pop; + const int new_value = pop + .title( msg ) + .width( 20 ) + .text( std::to_string( initial ) ) + .only_digits( true ) + .query_int(); + if( pop.canceled() ) { + return; + } + const time_duration offset = ( new_value - initial ) * factor; + // Arbitrary maximal value. + const time_point max = calendar::turn_zero + time_duration::from_turns( + std::numeric_limits::max() / 2 ); + calendar::turn = std::max( std::min( max, calendar::turn + offset ), calendar::turn_zero ); + }; + + uilist smenu; + static const auto years = []( const time_point & p ) { + return static_cast( ( p - calendar::turn_zero ) / calendar::year_length() ); + }; + do { + const int iSel = smenu.ret; + smenu.reset(); + smenu.addentry( 0, true, 'y', "%s: %d", _( "year" ), years( calendar::turn ) ); + smenu.addentry( 1, !calendar::eternal_season(), 's', "%s: %d", + _( "season" ), static_cast( season_of_year( calendar::turn ) ) ); + smenu.addentry( 2, true, 'd', "%s: %d", _( "day" ), day_of_season( calendar::turn ) ); + smenu.addentry( 3, true, 'h', "%s: %d", _( "hour" ), hour_of_day( calendar::turn ) ); + smenu.addentry( 4, true, 'm', "%s: %d", _( "minute" ), minute_of_hour( calendar::turn ) ); + smenu.addentry( 5, true, 't', "%s: %d", _( "turn" ), + to_turns( calendar::turn - calendar::turn_zero ) ); + smenu.selected = iSel; + smenu.query(); + + switch( smenu.ret ) { + case 0: + set_turn( years( calendar::turn ), calendar::year_length(), _( "Set year to?" ) ); + break; + case 1: + set_turn( static_cast( season_of_year( calendar::turn ) ), calendar::season_length(), + _( "Set season to? (0 = spring)" ) ); + break; + case 2: + set_turn( day_of_season( calendar::turn ), 1_days, _( "Set days to?" ) ); + break; + case 3: + set_turn( hour_of_day( calendar::turn ), 1_hours, _( "Set hour to?" ) ); + break; + case 4: + set_turn( minute_of_hour( calendar::turn ), 1_minutes, _( "Set minute to?" ) ); + break; + case 5: + set_turn( to_turns( calendar::turn - calendar::turn_zero ), 1_turns, + string_format( _( "Set turn to? (One day is %i turns)" ), to_turns( 1_days ) ).c_str() ); + break; + default: + break; + } + } while( smenu.ret != UILIST_CANCEL ); +} + void debug() { bool debug_menu_has_hotkey = hotkey_for_action( ACTION_DEBUG, @@ -1396,86 +2254,10 @@ void debug() debug_menu::wishmonster( cata::nullopt ); break; - case debug_menu_index::GAME_STATE: { - std::string mfus; - std::vector> sorted; - for( int f = 0; f < m_flag::MF_MAX; f++ ) { - sorted.push_back( {static_cast( f ), MonsterGenerator::generator().m_flag_usage_stats[f]} ); - } - std::sort( sorted.begin(), sorted.end(), []( std::pair a, std::pair b ) { - return a.second != b.second ? a.second > b.second : a.first < b.first; - } ); - popup( player_character.total_daily_calories_string() ); - for( auto &m_flag_stat : sorted ) { - mfus += string_format( "%s;%d\n", io::enum_to_string( m_flag_stat.first ), - m_flag_stat.second ); - } - DebugLog( D_INFO, DC_ALL ) << "Monster flag usage statistics:\nFLAG;COUNT\n" << mfus; - std::fill( MonsterGenerator::generator().m_flag_usage_stats.begin(), - MonsterGenerator::generator().m_flag_usage_stats.end(), 0 ); - 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 += ngettext( "%d creature exists.\n", "%d creatures exist.\n", g->num_creatures() ); - - std::unordered_map creature_counts; - for( Creature &critter : g->all_creatures() ) { - std::string this_name = critter.get_name(); - creature_counts[this_name]++; - } - - if( !creature_counts.empty() ) { - std::vector> creature_names_sorted; - for( const std::pair &it : creature_counts ) { - creature_names_sorted.emplace_back( it ); - } - - std::stable_sort( creature_names_sorted.begin(), creature_names_sorted.end(), []( auto a, auto b ) { - return a.second > b.second; - } ); - - s += _( "\nSpecific creature type list:\n" ); - for( const std::pair &crit_name : creature_names_sorted ) { - s += string_format( "%i %s\n", crit_name.second, crit_name.first ); - } - } - - popup_top( - s.c_str(), - player_character.posx(), player_character.posy(), abs_sub.x, abs_sub.y, - overmap_buffer.ter( player_character.global_omt_location() )->get_name(), - to_turns( calendar::turn - calendar::turn_zero ), - g->num_creatures() ); - for( const npc &guy : g->all_npcs() ) { - tripoint t = guy.global_sm_location(); - add_msg( m_info, _( "%s: map ( %d:%d ) pos ( %d:%d )" ), guy.name, t.x, - t.y, guy.posx(), guy.posy() ); - } - - add_msg( m_info, _( "(you: %d:%d)" ), player_character.posx(), player_character.posy() ); - std::string stom = - _( "Stomach Contents: %d ml / %d ml kCal: %d, Water: %d ml" ); - add_msg( m_info, stom.c_str(), units::to_milliliter( player_character.stomach.contains() ), - units::to_milliliter( player_character.stomach.capacity( player_character ) ), - player_character.stomach.get_calories(), - units::to_milliliter( player_character.stomach.get_water() ), player_character.get_hunger() ); - stom = _( "Guts Contents: %d ml / %d ml kCal: %d, Water: %d ml\nHunger: %d, Thirst: %d, kCal: %d / %d" ); - add_msg( m_info, stom.c_str(), units::to_milliliter( player_character.guts.contains() ), - units::to_milliliter( player_character.guts.capacity( player_character ) ), - player_character.guts.get_calories(), units::to_milliliter( player_character.guts.get_water() ), - player_character.get_hunger(), player_character.get_thirst(), player_character.get_stored_kcal(), - player_character.get_healthy_kcal() ); - add_msg( m_info, _( "Body Mass Index: %.0f\nBasal Metabolic Rate: %i" ), player_character.get_bmi(), - player_character.get_bmr() ); - add_msg( m_info, _( "Player activity level: %s" ), player_character.activity_level_str() ); - if( get_option( "STATS_THROUGH_KILLS" ) ) { - add_msg( m_info, _( "Kill xp: %d" ), player_character.kill_xp() ); - } - g->invalidate_main_ui_adaptor(); - g->disp_NPCs(); + case debug_menu_index::GAME_STATE: + debug_menu_game_state(); break; - } + case debug_menu_index::KILL_NPCS: for( npc &guy : g->all_npcs() ) { add_msg( _( "%s's head implodes!" ), guy.name ); @@ -1488,50 +2270,7 @@ void debug() break; case debug_menu_index::SPAWN_VEHICLE: - if( here.veh_at( player_character.pos() ) ) { - dbg( D_ERROR ) << "game:load: There's already vehicle here"; - debugmsg( "There's already vehicle here" ); - } else { - // Vector of name, id so that we can sort by name - std::vector> veh_strings; - for( auto &elem : vehicle_prototype::get_all() ) { - if( elem == vproto_id( "custom" ) ) { - continue; - } - veh_strings.emplace_back( elem->name.translated(), elem ); - } - std::sort( veh_strings.begin(), veh_strings.end(), localized_compare ); - uilist veh_menu; - veh_menu.text = _( "Choose vehicle to spawn" ); - int menu_ind = 0; - for( auto &elem : veh_strings ) { - //~ Menu entry in vehicle wish menu: 1st string: displayed name, 2nd string: internal name of vehicle - veh_menu.addentry( menu_ind, true, MENU_AUTOASSIGN, _( "%1$s (%2$s)" ), - elem.first, elem.second.c_str() ); - ++menu_ind; - } - veh_menu.query(); - 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; - tripoint dest = player_character.pos(); - 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_degrees, 100, veh_cond_menu.ret - 1 ); - if( veh != nullptr ) { - here.board_vehicle( dest, &player_character ); - } - } - } - } + debug_menu_spawn_vehicle(); break; case debug_menu_index::CHANGE_SKILLS: { @@ -1831,70 +2570,9 @@ void debug() case debug_menu_index::HOUR_TIMER: g->toggle_debug_hour_timer(); break; - case debug_menu_index::CHANGE_TIME: { - auto set_turn = [&]( const int initial, const time_duration & factor, const char *const msg ) { - const auto text = string_input_popup() - .title( msg ) - .width( 20 ) - .text( std::to_string( initial ) ) - .only_digits( true ) - .query_string(); - if( text.empty() ) { - return; - } - const int new_value = std::atoi( text.c_str() ); - const time_duration offset = ( new_value - initial ) * factor; - // Arbitrary maximal value. - const time_point max = calendar::turn_zero + time_duration::from_turns( - std::numeric_limits::max() / 2 ); - calendar::turn = std::max( std::min( max, calendar::turn + offset ), calendar::turn_zero ); - }; - - uilist smenu; - static const auto years = []( const time_point & p ) { - return static_cast( ( p - calendar::turn_zero ) / calendar::year_length() ); - }; - do { - const int iSel = smenu.ret; - smenu.reset(); - smenu.addentry( 0, true, 'y', "%s: %d", _( "year" ), years( calendar::turn ) ); - smenu.addentry( 1, !calendar::eternal_season(), 's', "%s: %d", - _( "season" ), static_cast( season_of_year( calendar::turn ) ) ); - smenu.addentry( 2, true, 'd', "%s: %d", _( "day" ), day_of_season( calendar::turn ) ); - smenu.addentry( 3, true, 'h', "%s: %d", _( "hour" ), hour_of_day( calendar::turn ) ); - smenu.addentry( 4, true, 'm', "%s: %d", _( "minute" ), minute_of_hour( calendar::turn ) ); - smenu.addentry( 5, true, 't', "%s: %d", _( "turn" ), - to_turns( calendar::turn - calendar::turn_zero ) ); - smenu.selected = iSel; - smenu.query(); - - switch( smenu.ret ) { - case 0: - set_turn( years( calendar::turn ), calendar::year_length(), _( "Set year to?" ) ); - break; - case 1: - set_turn( static_cast( season_of_year( calendar::turn ) ), calendar::season_length(), - _( "Set season to? (0 = spring)" ) ); - break; - case 2: - set_turn( day_of_season( calendar::turn ), 1_days, _( "Set days to?" ) ); - break; - case 3: - set_turn( hour_of_day( calendar::turn ), 1_hours, _( "Set hour to?" ) ); - break; - case 4: - set_turn( minute_of_hour( calendar::turn ), 1_minutes, _( "Set minute to?" ) ); - break; - case 5: - set_turn( to_turns( calendar::turn - calendar::turn_zero ), 1_turns, - string_format( _( "Set turn to? (One day is %i turns)" ), to_turns( 1_days ) ).c_str() ); - break; - default: - break; - } - } while( smenu.ret != UILIST_CANCEL ); - } - break; + case debug_menu_index::CHANGE_TIME: + debug_menu_change_time(); + break; case debug_menu_index::SET_AUTOMOVE: { const cata::optional dest = g->look_around(); if( !dest || *dest == player_character.pos() ) { @@ -2023,8 +2701,13 @@ void debug() } break; case debug_menu_index::TEST_WEATHER: { - get_weather().get_cur_weather_gen().test_weather( g->get_seed(), - get_weather().next_instance_allowed ); + get_weather().get_cur_weather_gen().test_weather( g->get_seed() ); + } + break; + + case debug_menu_index::WRITE_EOCS: { + effect_on_conditions::write_eocs_to_file(); + popup( _( "effect_on_condition list written to eocs.output" ) ); } break; @@ -2075,57 +2758,9 @@ void debug() popup( popup_msg ); } break; - case debug_menu_index::LEARN_SPELLS: - if( spell_type::get_all().empty() ) { - add_msg( m_bad, _( "There are no spells to learn. You must install a mod that adds some." ) ); - } else { - for( const spell_type &learn : spell_type::get_all() ) { - player_character.magic->learn_spell( &learn, player_character, true ); - } - add_msg( m_good, - _( "You have become an Archwizardpriest! What will you do with your newfound power?" ) ); - } - break; - case debug_menu_index::LEVEL_SPELLS: { - std::vector spells = player_character.magic->get_spells(); - if( spells.empty() ) { - add_msg( m_bad, _( "Try learning some spells first." ) ); - return; - } - std::vector uiles; - { - uilist_entry uile( _( "Spell" ) ); - uile.ctxt = string_format( "%s %s", - //~ translation should not exceed 4 console cells - right_justify( _( "LVL" ), 4 ), - //~ translation should not exceed 4 console cells - right_justify( _( "MAX" ), 4 ) ); - uile.enabled = false; - uiles.emplace_back( uile ); - } - int retval = 0; - for( spell *sp : spells ) { - uilist_entry uile( sp->name() ); - uile.ctxt = string_format( "%4d %4d", sp->get_level(), sp->get_max_level() ); - uile.retval = retval++; - uile.enabled = !sp->is_max_level(); - uiles.emplace_back( uile ); - } - int action = uilist( _( "Debug level spell:" ), uiles ); - if( action < 0 ) { - return; - } - int desired_level = 0; - int cur_level = spells[action]->get_level(); - query_int( desired_level, _( "Desired Spell Level: (Current %d)" ), cur_level ); - desired_level = std::min( desired_level, spells[action]->get_max_level() ); - while( cur_level < desired_level ) { - spells[action]->gain_level(); - cur_level = spells[action]->get_level(); - } - add_msg( m_good, _( "%s is now level %d!" ), spells[action]->name(), spells[action]->get_level() ); + case debug_menu_index::CHANGE_SPELLS: + change_spells( player_character ); break; - } case debug_menu_index::TEST_MAP_EXTRA_DISTRIBUTION: MapExtras::debug_spawn_test(); break; @@ -2171,6 +2806,16 @@ void debug() break; } + case debug_menu_index::EDIT_CAMP_LARDER: { + faction *your_faction = get_player_character().get_faction(); + int larder; + if( query_int( larder, _( "Set camp larder kCals to? Currently: %d" ), + your_faction->food_supply ) ) { + your_faction->food_supply = larder; + } + break; + } + case debug_menu_index::last: return; } diff --git a/src/debug_menu.h b/src/debug_menu.h index ac67b2c17f472..66498bbd88328 100644 --- a/src/debug_menu.h +++ b/src/debug_menu.h @@ -16,6 +16,7 @@ template class optional; } // namespace cata +class Character; class player; namespace debug_menu @@ -68,6 +69,7 @@ enum class debug_menu_index : int { PRINT_NPC_MAGIC, QUIT_NOSAVE, TEST_WEATHER, + WRITE_EOCS, SAVE_SCREENSHOT, GAME_REPORT, DISPLAY_SCENTS_LOCAL, @@ -80,15 +82,17 @@ enum class debug_menu_index : int { DISPLAY_REACHABILITY_ZONES, DISPLAY_RADIATION, HOUR_TIMER, - LEARN_SPELLS, - LEVEL_SPELLS, + CHANGE_SPELLS, TEST_MAP_EXTRA_DISTRIBUTION, NESTED_MAPGEN, VEHICLE_BATTERY_CHARGE, GENERATE_EFFECT_LIST, + EDIT_CAMP_LARDER, last }; +void change_spells( Character &character ); + void teleport_short(); void teleport_long(); void teleport_overmap( bool specific_coordinates = false ); diff --git a/src/dialogue.h b/src/dialogue.h index ab4a5e1fd95f1..0ea3acb49d395 100644 --- a/src/dialogue.h +++ b/src/dialogue.h @@ -99,6 +99,11 @@ struct talk_effect_fun_t { void set_remove_effect( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_add_trait( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_remove_trait( const JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_message( const JsonObject &jo, const std::string &member ); + void set_mod_pain( const JsonObject &jo, const std::string &member, bool is_npc ); + void set_add_wet( const JsonObject &jo, const std::string &member, bool is_npc ); + void set_add_power( const JsonObject &jo, const std::string &member, bool is_npc ); + void set_sound_effect( const JsonObject &jo, const std::string &member ); void set_add_var( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_remove_var( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_adjust_var( const JsonObject &jo, const std::string &member, bool is_npc = false ); @@ -172,12 +177,12 @@ struct talk_effect_t { void set_effect_consequence( const talk_effect_fun_t &fun, dialogue_consequence con ); void set_effect_consequence( const std::function &ptr, dialogue_consequence con ); - void load_effect( const JsonObject &jo ); + void load_effect( const JsonObject &jo, const std::string &member_name ); void parse_sub_effect( const JsonObject &jo ); void parse_string_effect( const std::string &effect_id, const JsonObject &jo ); talk_effect_t() = default; - explicit talk_effect_t( const JsonObject & ); + explicit talk_effect_t( const JsonObject &, const std::string & ); /** * Functions that are called when the response is chosen. diff --git a/src/dialogue_win.cpp b/src/dialogue_win.cpp index 7a8f6a3c331c6..2d7681d3fb623 100644 --- a/src/dialogue_win.cpp +++ b/src/dialogue_win.cpp @@ -71,7 +71,7 @@ size_t dialogue_window::add_to_history( const std::string &text ) // Empty line between lines of dialogue void dialogue_window::add_history_separator() { - history.push_back( "" ); + history.emplace_back( "" ); } void dialogue_window::print_history( const size_t hilight_lines ) diff --git a/src/editmap.cpp b/src/editmap.cpp index 0c680f0513925..8c420e6a0bc32 100644 --- a/src/editmap.cpp +++ b/src/editmap.cpp @@ -62,6 +62,7 @@ static constexpr tripoint editmap_boundary_max( MAPSIZE_X, MAPSIZE_Y, OVERMAP_HE static constexpr half_open_cuboid editmap_boundaries( editmap_boundary_min, editmap_boundary_max ); +// NOLINTNEXTLINE(cata-static-int_id-constants) static const ter_id undefined_ter_id( -1 ); static std::vector fld_string( const std::string &str, int width ) @@ -1578,28 +1579,26 @@ void editmap::recalc_target( shapetype shape ) } break; case editmap_rect_filled: - case editmap_rect: - int sx; - int sy; - int ex; - int ey; + case editmap_rect: { + point s; + point e; if( target.x < origin.x ) { - sx = target.x; - ex = origin.x; + s.x = target.x; + e.x = origin.x; } else { - sx = origin.x; - ex = target.x; + s.x = origin.x; + e.x = target.x; } if( target.y < origin.y ) { - sy = target.y; - ey = origin.y; + s.y = target.y; + e.y = origin.y; } else { - sy = origin.y; - ey = target.y; + s.y = origin.y; + e.y = target.y; } - for( int x = sx; x <= ex; x++ ) { - for( int y = sy; y <= ey; y++ ) { - if( shape == editmap_rect_filled || x == sx || x == ex || y == sy || y == ey ) { + for( int x = s.x; x <= e.x; x++ ) { + for( int y = s.y; y <= e.y; y++ ) { + if( shape == editmap_rect_filled || x == s.x || x == e.x || y == s.y || y == e.y ) { const tripoint p( x, y, z ); if( editmap_boundaries.contains( p ) ) { target_list.push_back( p ); @@ -1607,7 +1606,8 @@ void editmap::recalc_target( shapetype shape ) } } } - break; + } + break; case editmap_line: target_list = line_to( origin, target, 0, 0 ); break; @@ -2094,7 +2094,7 @@ void editmap::mapgen_retarget() for( int y = target.y - SEEY + 1; y < target.y + SEEY + 1; y++ ) { if( x == target.x - SEEX + 1 || x == target.x + SEEX || y == target.y - SEEY + 1 || y == target.y + SEEY ) { - target_list.push_back( tripoint( x, y, target.z ) ); + target_list.emplace_back( x, y, target.z ); } } } @@ -2160,7 +2160,7 @@ void editmap::edit_mapgen() for( int y = target.y - SEEY + 1; y < target.y + SEEY + 1; y++ ) { if( x == target.x - SEEX + 1 || x == target.x + SEEX || y == target.y - SEEY + 1 || y == target.y + SEEY ) { - target_list.push_back( tripoint( x, y, target.z ) ); + target_list.emplace_back( x, y, target.z ); } } } diff --git a/src/effect.cpp b/src/effect.cpp index 5b9fd444061ec..e5d4b91774938 100644 --- a/src/effect.cpp +++ b/src/effect.cpp @@ -517,7 +517,7 @@ bool effect_type::load_decay_msgs( const JsonObject &jo, const std::string &memb } else { rate = m_neutral; } - decay_msgs.push_back( std::make_pair( msg, rate ) ); + decay_msgs.emplace_back( msg, rate ); } return true; } @@ -627,32 +627,32 @@ std::string effect::disp_desc( bool reduced ) const // place to add them. int val = 0; val = get_avg_mod( "PAIN", reduced ); - values.push_back( desc_freq( get_percentage( "PAIN", val, reduced ), val, _( "pain" ), - _( "pain" ) ) ); + values.emplace_back( get_percentage( "PAIN", val, reduced ), val, _( "pain" ), + _( "pain" ) ); val = get_avg_mod( "HURT", reduced ); - values.push_back( desc_freq( get_percentage( "HURT", val, reduced ), val, _( "damage" ), - _( "damage" ) ) ); + values.emplace_back( get_percentage( "HURT", val, reduced ), val, _( "damage" ), + _( "damage" ) ); val = get_avg_mod( "STAMINA", reduced ); - values.push_back( desc_freq( get_percentage( "STAMINA", val, reduced ), val, - _( "stamina recovery" ), _( "fatigue" ) ) ); + values.emplace_back( get_percentage( "STAMINA", val, reduced ), val, + _( "stamina recovery" ), _( "fatigue" ) ); val = get_avg_mod( "THIRST", reduced ); - values.push_back( desc_freq( get_percentage( "THIRST", val, reduced ), val, _( "thirst" ), - _( "quench" ) ) ); + values.emplace_back( get_percentage( "THIRST", val, reduced ), val, _( "thirst" ), + _( "quench" ) ); val = get_avg_mod( "HUNGER", reduced ); - values.push_back( desc_freq( get_percentage( "HUNGER", val, reduced ), val, _( "hunger" ), - _( "sate" ) ) ); + values.emplace_back( get_percentage( "HUNGER", val, reduced ), val, _( "hunger" ), + _( "sate" ) ); val = get_avg_mod( "FATIGUE", reduced ); - values.push_back( desc_freq( get_percentage( "FATIGUE", val, reduced ), val, _( "sleepiness" ), - _( "rest" ) ) ); + values.emplace_back( get_percentage( "FATIGUE", val, reduced ), val, _( "sleepiness" ), + _( "rest" ) ); val = get_avg_mod( "COUGH", reduced ); - values.push_back( desc_freq( get_percentage( "COUGH", val, reduced ), val, _( "coughing" ), - _( "coughing" ) ) ); + values.emplace_back( get_percentage( "COUGH", val, reduced ), val, _( "coughing" ), + _( "coughing" ) ); val = get_avg_mod( "VOMIT", reduced ); - values.push_back( desc_freq( get_percentage( "VOMIT", val, reduced ), val, _( "vomiting" ), - _( "vomiting" ) ) ); + values.emplace_back( get_percentage( "VOMIT", val, reduced ), val, _( "vomiting" ), + _( "vomiting" ) ); val = get_avg_mod( "SLEEP", reduced ); - values.push_back( desc_freq( get_percentage( "SLEEP", val, reduced ), val, _( "blackouts" ), - _( "blackouts" ) ) ); + values.emplace_back( get_percentage( "SLEEP", val, reduced ), val, _( "blackouts" ), + _( "blackouts" ) ); for( auto &i : values ) { if( i.val > 0 ) { @@ -794,7 +794,8 @@ void effect::set_duration( const time_duration &dur, bool alert ) set_intensity( duration / eff_type->int_dur_factor + 1, alert ); } - add_msg_debug( "ID: %s, Duration %s", get_id().c_str(), to_string( duration ) ); + add_msg_debug( debugmode::DF_EFFECT, "ID: %s, Duration %s", get_id().c_str(), + to_string( duration ) ); } void effect::mod_duration( const time_duration &dur, bool alert ) { @@ -857,7 +858,7 @@ int effect::set_intensity( int val, bool alert ) { if( intensity < 1 ) { // Fix bad intensity - add_msg_debug( "Bad intensity, ID: %s", get_id().c_str() ); + add_msg_debug( debugmode::DF_EFFECT, "Bad intensity, ID: %s", get_id().c_str() ); intensity = 1; } @@ -875,7 +876,8 @@ int effect::set_intensity( int val, bool alert ) int old_intensity = intensity; intensity = val; if( old_intensity != intensity ) { - add_msg_debug( "%s intensity %d->%d", get_id().c_str(), old_intensity, intensity ); + add_msg_debug( debugmode::DF_EFFECT, "%s intensity %d->%d", get_id().c_str(), old_intensity, + intensity ); } return intensity; @@ -1310,16 +1312,16 @@ void load_effect_type( const JsonObject &jo ) jo.read( "blood_analysis_description", new_etype.blood_analysis_description ); for( auto &&f : jo.get_string_array( "resist_traits" ) ) { // *NOPAD* - new_etype.resist_traits.push_back( trait_id( f ) ); + new_etype.resist_traits.emplace_back( f ); } for( auto &&f : jo.get_string_array( "resist_effects" ) ) { // *NOPAD* - new_etype.resist_effects.push_back( efftype_id( f ) ); + new_etype.resist_effects.emplace_back( f ); } for( auto &&f : jo.get_string_array( "removes_effects" ) ) { // *NOPAD* - new_etype.removes_effects.push_back( efftype_id( f ) ); + new_etype.removes_effects.emplace_back( f ); } for( auto &&f : jo.get_string_array( "blocks_effects" ) ) { // *NOPAD* - new_etype.blocks_effects.push_back( efftype_id( f ) ); + new_etype.blocks_effects.emplace_back( f ); } if( jo.has_string( "max_duration" ) ) { diff --git a/src/effect_on_condition.cpp b/src/effect_on_condition.cpp new file mode 100644 index 0000000000000..ebdb5d39a9dc2 --- /dev/null +++ b/src/effect_on_condition.cpp @@ -0,0 +1,228 @@ +#include "effect_on_condition.h" + +#include "avatar.h" +#include "cata_utility.h" +#include "character.h" +#include "condition.h" +#include "game.h" +#include "generic_factory.h" +#include "talker.h" +#include "type_id.h" + +namespace +{ +generic_factory +effect_on_condition_factory( "effect_on_condition" ); +} // namespace + +template<> +const effect_on_condition &effect_on_condition_id::obj() const +{ + return effect_on_condition_factory.obj( *this ); +} + +/** @relates string_id */ +template<> +bool string_id::is_valid() const +{ + return effect_on_condition_factory.is_valid( *this ); +} + +void effect_on_conditions::check_consistency() +{ +} + +void effect_on_condition::load( const JsonObject &jo, const std::string & ) +{ + activate_only = true; + if( jo.has_member( "recurrence_min" ) || jo.has_member( "recurrence_max" ) ) { + activate_only = false; + mandatory( jo, was_loaded, "recurrence_min", recurrence_min ); + mandatory( jo, was_loaded, "recurrence_max", recurrence_max ); + if( recurrence_max < recurrence_min ) { + jo.throw_error( "recurrence_max cannot be smaller than recurrence_min." ); + } + } + if( jo.has_member( "deactivate_condition" ) ) { + read_condition( jo, "deactivate_condition", deactivate_condition, false ); + has_deactivate_condition = true; + } + if( jo.has_member( "condition" ) ) { + read_condition( jo, "condition", condition, false ); + has_condition = true; + } + true_effect.load_effect( jo, "effect" ); + + if( jo.has_member( "false_effect" ) ) { + false_effect.load_effect( jo, "false_effect" ); + has_false_effect = true; + } +} + +static time_duration next_recurrence( const effect_on_condition_id &eoc ) +{ + return rng( eoc->recurrence_min, eoc->recurrence_max ); +} + +void effect_on_conditions::load_new_character() +{ + for( const effect_on_condition &eoc : effect_on_conditions::get_all() ) { + if( !eoc.activate_only ) { + queued_eoc new_eoc = queued_eoc{ eoc.id, true, calendar::turn + next_recurrence( eoc.id ) }; + g->queued_effect_on_conditions.push( new_eoc ); + } + } +} + +void effect_on_conditions::queue_effect_on_condition( time_duration duration, + effect_on_condition_id eoc ) +{ + queued_eoc new_eoc = queued_eoc{ eoc, false, calendar::turn + duration }; + g->queued_effect_on_conditions.push( new_eoc ); +} + +void effect_on_conditions::process_effect_on_conditions() +{ + dialogue d; + standard_npc default_npc( "Default" ); + d.alpha = get_talker_for( get_avatar() ); + d.beta = get_talker_for( default_npc ); + std::vector eocs_to_queue; + while( !g->queued_effect_on_conditions.empty() && + g->queued_effect_on_conditions.top().time <= calendar::turn ) { + queued_eoc top = g->queued_effect_on_conditions.top(); + bool activated = top.eoc->activate( d ); + if( top.recurring ) { + if( activated ) { // It worked so add it back + queued_eoc new_eoc = queued_eoc{ top.eoc, true, calendar::turn + next_recurrence( top.eoc ) }; + eocs_to_queue.push_back( new_eoc ); + } else { + if( !top.eoc->check_deactivate() ) { // It failed but shouldn't be deactivated so add it back + queued_eoc new_eoc = queued_eoc{ top.eoc, true, calendar::turn + next_recurrence( top.eoc ) }; + eocs_to_queue.push_back( new_eoc ); + } else { // It failed and should be deactivated for now + g->inactive_effect_on_condition_vector.push_back( top.eoc ); + } + } + } + g->queued_effect_on_conditions.pop(); + } + for( const queued_eoc &q_eoc : eocs_to_queue ) { + g->queued_effect_on_conditions.push( q_eoc ); + } +} + +void effect_on_conditions::process_reactivate() +{ + dialogue d; + standard_npc default_npc( "Default" ); + d.alpha = get_talker_for( get_avatar() ); + d.beta = get_talker_for( default_npc ); + + std::vector ids_to_reactivate; + for( const effect_on_condition_id &eoc : g->inactive_effect_on_condition_vector ) { + if( !eoc->check_deactivate() ) { + ids_to_reactivate.push_back( eoc ); + } + } + for( const effect_on_condition_id &eoc : ids_to_reactivate ) { + g->queued_effect_on_conditions.push( queued_eoc{ eoc, true, calendar::turn + next_recurrence( eoc ) } ); + g->inactive_effect_on_condition_vector.erase( std::remove( + g->inactive_effect_on_condition_vector.begin(), g->inactive_effect_on_condition_vector.end(), eoc ), + g->inactive_effect_on_condition_vector.end() ); + } +} + +bool effect_on_condition::activate( dialogue &d ) const +{ + if( !has_condition || condition( d ) ) { + true_effect.apply( d ); + return true; + } + + if( has_false_effect ) { + false_effect.apply( d ); + } + return false; +} + +bool effect_on_condition::check_deactivate() const +{ + if( !has_deactivate_condition || has_false_effect ) { + return false; + } + dialogue d; + standard_npc default_npc( "Default" ); + d.alpha = get_talker_for( get_avatar() ); + d.beta = get_talker_for( default_npc ); + return deactivate_condition( d ); +} + +void effect_on_conditions::clear() +{ + while( !g->queued_effect_on_conditions.empty() ) { + g->queued_effect_on_conditions.pop(); + } + g->inactive_effect_on_condition_vector.clear(); +} + +void effect_on_conditions::write_eocs_to_file() +{ + write_to_file( "eocs.output", [&]( std::ostream & testfile ) { + testfile << "id;timepoint;recurring" << std::endl; + + testfile << "queued eocs:" << std::endl; + std::vector temp_queue; + while( !g->queued_effect_on_conditions.empty() ) { + temp_queue.push_back( g->queued_effect_on_conditions.top() ); + g->queued_effect_on_conditions.pop(); + } + + for( const auto &queue_entry : temp_queue ) { + time_duration temp = queue_entry.time - calendar::turn; + testfile << queue_entry.eoc.c_str() << ";" << to_string( temp ) << ";" << + ( queue_entry.recurring ? "recur" : "non" ) << std::endl ; + } + + for( const auto &queued : temp_queue ) { + g->queued_effect_on_conditions.push( queued ); + } + + testfile << "inactive eocs:" << std::endl; + for( const effect_on_condition_id &eoc : g->inactive_effect_on_condition_vector ) { + testfile << eoc.c_str() << std::endl; + } + + }, "eocs test file" ); +} + +void effect_on_condition::finalize() +{ +} + +void effect_on_conditions::finalize_all() +{ + effect_on_condition_factory.finalize(); + for( const effect_on_condition &eoc : effect_on_condition_factory.get_all() ) { + const_cast( eoc ).finalize(); + } +} + +void effect_on_condition::check() const +{ +} + +const std::vector &effect_on_conditions::get_all() +{ + return effect_on_condition_factory.get_all(); +} + +void effect_on_conditions::reset() +{ + effect_on_condition_factory.reset(); +} + +void effect_on_conditions::load( const JsonObject &jo, const std::string &src ) +{ + effect_on_condition_factory.load( jo, src ); +} diff --git a/src/effect_on_condition.h b/src/effect_on_condition.h new file mode 100644 index 0000000000000..0d4ffae657845 --- /dev/null +++ b/src/effect_on_condition.h @@ -0,0 +1,79 @@ +#pragma once +#ifndef CATA_SRC_EFFECT_ON_CONDITION_H +#define CATA_SRC_EFFECT_ON_CONDITION_H + +#include +#include + +#include "calendar.h" +#include "dialogue.h" +#include "json.h" +#include "type_id.h" + +template +class generic_factory; + +struct queued_eoc { + public: + effect_on_condition_id eoc; + bool recurring = false; + time_point time; +}; + +struct eoc_compare { + bool operator()( const queued_eoc &lhs, const queued_eoc &rhs ) const { + return lhs.time > rhs.time; + } +}; + +struct effect_on_condition { + public: + friend class generic_factory; + bool was_loaded = false; + effect_on_condition_id id; + + std::function condition; + std::function deactivate_condition; + talk_effect_t true_effect; + talk_effect_t false_effect; + bool has_deactivate_condition = false; + bool has_condition = false; + bool has_false_effect = false; + bool activate_only = true; + + time_duration recurrence_min = 1_seconds; + time_duration recurrence_max = 1_seconds; + bool activate( dialogue &d ) const; + bool check_deactivate() const; + void load( const JsonObject &jo, const std::string &src ); + void finalize(); + void check() const; + effect_on_condition() = default; +}; +namespace effect_on_conditions +{ +/** Get all currently loaded effect_on_conditions */ +const std::vector &get_all(); +/** Finalize all loaded effect_on_conditions */ +void finalize_all(); +/** Clear all loaded effects on condition (invalidating any pointers) */ +void reset(); +/** Load effect on condition from JSON definition */ +void load( const JsonObject &jo, const std::string &src ); +/** Checks all loaded from JSON are valid */ +void check_consistency(); +/** Sets up the initial queue for a new character */ +void load_new_character(); +/** queue an eoc to happen in the future */ +void queue_effect_on_condition( time_duration duration, effect_on_condition_id eoc ); +/** called every turn to process the queued eocs */ +void process_effect_on_conditions(); +/** called after certain events to test whether to reactivate eocs */ +void process_reactivate(); +/** clear all queued and inactive eocs */ +void clear(); +/** write out all queued eocs and inactive eocs to a file for testing */ +void write_eocs_to_file(); +} // namespace effect_on_conditions + +#endif // CATA_SRC_EFFECT_ON_CONDITION_H diff --git a/src/event_field_transformations.cpp b/src/event_field_transformations.cpp index 0dc6badbd3030..4699b19ac8a4e 100644 --- a/src/event_field_transformations.cpp +++ b/src/event_field_transformations.cpp @@ -60,7 +60,7 @@ static std::vector species_of_monster( const cata_variant &v ) std::vector result; result.reserve( species.size() ); for( const species_id &s : species ) { - result.push_back( cata_variant( s ) ); + result.emplace_back( s ); } return result; } diff --git a/src/explosion.cpp b/src/explosion.cpp index 738442fd9487d..384d0553afca6 100644 --- a/src/explosion.cpp +++ b/src/explosion.cpp @@ -311,7 +311,8 @@ static void do_blast( const tripoint &p, const float power, continue; } - add_msg_debug( "Blast hits %s with force %.1f", critter->disp_name(), force ); + add_msg_debug( debugmode::DF_EXPLOSION, "Blast hits %s with force %.1f", critter->disp_name(), + force ); Character *pl = critter->as_character(); if( pl == nullptr ) { @@ -320,7 +321,8 @@ static void do_blast( const tripoint &p, const float power, const int actual_dmg = rng_float( dmg * 2, dmg * 3 ); critter->apply_damage( nullptr, bodypart_id( "torso" ), actual_dmg ); critter->check_dead_state(); - add_msg_debug( "Blast hits %s for %d damage", critter->disp_name(), actual_dmg ); + add_msg_debug( debugmode::DF_EXPLOSION, "Blast hits %s for %d damage", critter->disp_name(), + actual_dmg ); continue; } @@ -354,7 +356,8 @@ static void do_blast( const tripoint &p, const float power, const dealt_damage_instance result = pl->deal_damage( nullptr, blp.bp, dmg_instance ); const int res_dmg = result.total_damage(); - add_msg_debug( "%s for %d raw, %d actual", hit_part_name, part_dam, res_dmg ); + add_msg_debug( debugmode::DF_EXPLOSION, "%s for %d raw, %d actual", hit_part_name, part_dam, + res_dmg ); if( res_dmg > 0 ) { pl->add_msg_if_player( m_bad, _( "Your %s is hit for %d damage!" ), hit_part_name, res_dmg ); } @@ -435,10 +438,10 @@ static std::vector shrapnel( const tripoint &src, int power, } else { non_damaging_hits++; } - add_msg_debug( "Shrapnel hit %s at %d m/s at a distance of %d", + add_msg_debug( debugmode::DF_EXPLOSION, "Shrapnel hit %s at %d m/s at a distance of %d", critter->disp_name(), frag.proj.speed, rl_dist( src, target ) ); - add_msg_debug( "Shrapnel dealt %d damage", frag.dealt_dam.total_damage() ); + add_msg_debug( debugmode::DF_EXPLOSION, "Shrapnel dealt %d damage", frag.dealt_dam.total_damage() ); if( critter->is_dead_state() ) { break; } @@ -796,8 +799,10 @@ void resonance_cascade( const tripoint &p ) player_character.pos() ) ) ); player_character.add_effect( effect_teleglow, rng( minglow, maxglow ) * 100 ); } - int startx = ( p.x < 8 ? 0 : p.x - 8 ), endx = ( p.x + 8 >= SEEX * 3 ? SEEX * 3 - 1 : p.x + 8 ); - int starty = ( p.y < 8 ? 0 : p.y - 8 ), endy = ( p.y + 8 >= SEEY * 3 ? SEEY * 3 - 1 : p.y + 8 ); + int startx = ( p.x < 8 ? 0 : p.x - 8 ); + int endx = ( p.x + 8 >= SEEX * 3 ? SEEX * 3 - 1 : p.x + 8 ); + int starty = ( p.y < 8 ? 0 : p.y - 8 ); + int endy = ( p.y + 8 >= SEEY * 3 ? SEEY * 3 - 1 : p.y + 8 ); tripoint dest( startx, starty, p.z ); map &here = get_map(); for( int &i = dest.x; i <= endx; i++ ) { diff --git a/src/faction_camp.cpp b/src/faction_camp.cpp index 36d21036c384a..5cc54c28ab3d6 100644 --- a/src/faction_camp.cpp +++ b/src/faction_camp.cpp @@ -477,16 +477,16 @@ static bool update_time_left( std::string &entry, const comp_list &npc_list ) Character &player_character = get_player_character(); for( const auto &comp : npc_list ) { if( comp->companion_mission_time_ret < calendar::turn ) { - entry = entry + _( " [DONE]\n" ); + entry += _( " [DONE]\n" ); avail = true; } else { - entry = entry + " [" + - to_string( comp->companion_mission_time_ret - calendar::turn ) + - _( " left]\n" ); + entry += " [" + + to_string( comp->companion_mission_time_ret - calendar::turn ) + + _( " left]\n" ); avail = player_character.has_trait( trait_DEBUG_HS ); } } - entry = entry + _( "\n\nDo you wish to bring your allies back into your party?" ); + entry += _( "\n\nDo you wish to bring your allies back into your party?" ); return avail; } @@ -496,11 +496,11 @@ static bool update_time_fixed( std::string &entry, const comp_list &npc_list, bool avail = false; for( const auto &comp : npc_list ) { time_duration elapsed = calendar::turn - comp->companion_mission_time; - entry = entry + " " + comp->name + " [" + to_string( elapsed ) + "/" + - to_string( duration ) + "]\n"; + entry += " " + comp->name + " [" + to_string( elapsed ) + "/" + + to_string( duration ) + "]\n"; avail |= elapsed >= duration; } - entry = entry + _( "\n\nDo you wish to bring your allies back into your party?" ); + entry += _( "\n\nDo you wish to bring your allies back into your party?" ); return avail; } @@ -532,6 +532,7 @@ recipe_id base_camps::select_camp_option( const std::map std::vector pos_names; int choice = 0; + pos_names.reserve( pos_options.size() ); for( const auto &it : pos_options ) { pos_names.push_back( it.second.translated() ); } @@ -2785,10 +2786,10 @@ void basecamp::recruit_return( const std::string &task, int score ) description += _( "Select an option:" ); std::vector rec_options; - rec_options.push_back( _( "Increase Food" ) ); - rec_options.push_back( _( "Decrease Food" ) ); - rec_options.push_back( _( "Make Offer" ) ); - rec_options.push_back( _( "Not Interested" ) ); + rec_options.emplace_back( _( "Increase Food" ) ); + rec_options.emplace_back( _( "Decrease Food" ) ); + rec_options.emplace_back( _( "Make Offer" ) ); + rec_options.emplace_back( _( "Not Interested" ) ); rec_m = uilist( description, rec_options ); if( rec_m < 0 || rec_m == 3 || static_cast( rec_m ) >= rec_options.size() ) { @@ -3299,19 +3300,19 @@ void om_range_mark( const tripoint_abs_omt &origin, int range, bool add_notes, std::vector note_pts; //North Limit for( int x = origin.x() - range; x < origin.x() + range + 1; x++ ) { - note_pts.push_back( tripoint_abs_omt( x, origin.y() - range, origin.z() ) ); + note_pts.emplace_back( x, origin.y() - range, origin.z() ); } //South for( int x = origin.x() - range; x < origin.x() + range + 1; x++ ) { - note_pts.push_back( tripoint_abs_omt( x, origin.y() + range, origin.z() ) ); + note_pts.emplace_back( x, origin.y() + range, origin.z() ); } //West for( int y = origin.y() - range; y < origin.y() + range + 1; y++ ) { - note_pts.push_back( tripoint_abs_omt( origin.x() - range, y, origin.z() ) ); + note_pts.emplace_back( origin.x() - range, y, origin.z() ); } //East for( int y = origin.y() - range; y < origin.y() + range + 1; y++ ) { - note_pts.push_back( tripoint_abs_omt( origin.x() + range, y, origin.z() ) ); + note_pts.emplace_back( origin.x() + range, y, origin.z() ); } for( auto pt : note_pts ) { @@ -3543,7 +3544,7 @@ std::vector> talk_function::om_building std::string om_rnear_id = omt_rnear.id().c_str(); if( !purge || ( om_rnear_id.find( "faction_base_" ) != std::string::npos && om_rnear_id.find( "faction_base_camp" ) == std::string::npos ) ) { - om_camp_region.push_back( std::make_pair( om_rnear_id, omt_near_pos ) ); + om_camp_region.emplace_back( om_rnear_id, omt_near_pos ); } } return om_camp_region; @@ -3598,7 +3599,7 @@ std::string basecamp::craft_description( const recipe_id &itm ) std::string comp; for( auto &elem : component_print_buffer ) { - comp = comp + elem + "\n"; + str_append( comp, elem, "\n" ); } comp = string_format( _( "Skill used: %s\nDifficulty: %d\n%s\nTime: %s\n" ), making.skill_used.obj().name(), making.difficulty, comp, @@ -3708,7 +3709,7 @@ std::string basecamp::gathering_description( const std::string &bldg ) itemnames2.insert( std::pair( e.second, e.first ) ); } for( const auto &e : itemnames2 ) { - output = output + "> " + e.second + "\n"; + str_append( output, "> ", e.second, "\n" ); } return output; } diff --git a/src/field_type.h b/src/field_type.h index f0b82656dc2d9..4099cd26fe513 100644 --- a/src/field_type.h +++ b/src/field_type.h @@ -111,6 +111,7 @@ struct field_intensity_level { std::vector field_effects; }; +// NOLINTNEXTLINE(cata-static-int_id-constants) const field_type_id INVALID_FIELD_TYPE_ID = field_type_id( -1 ); extern const field_type_str_id fd_null; extern const field_type_str_id fd_fire; diff --git a/src/flag.cpp b/src/flag.cpp index dbe17a49a8342..5b5b68364f9c0 100644 --- a/src/flag.cpp +++ b/src/flag.cpp @@ -9,6 +9,7 @@ const flag_id flag_NULL = flag_id( "null" ); // intentionally invalid flag const flag_id flag_ACID( "ACID" ); const flag_id flag_ACID_IMMUNE( "ACID_IMMUNE" ); +const flag_id flag_ACTIVATE_ON_PLACE( "ACTIVATE_ON_PLACE" ); const flag_id flag_ACTIVE_CLOAKING( "ACTIVE_CLOAKING" ); const flag_id flag_ACT_IN_FIRE( "ACT_IN_FIRE" ); const flag_id flag_ACT_ON_RANGED_HIT( "ACT_ON_RANGED_HIT" ); @@ -76,19 +77,7 @@ const flag_id flag_DURABLE_MELEE( "DURABLE_MELEE" ); const flag_id flag_EATEN_COLD( "EATEN_COLD" ); const flag_id flag_EATEN_HOT( "EATEN_HOT" ); const flag_id flag_EDIBLE_FROZEN( "EDIBLE_FROZEN" ); -const flag_id flag_EFFECT_ACID_IMMUNE( "EFFECT_ACID_IMMUNE" ); -const flag_id flag_EFFECT_BASH_IMMUNE( "EFFECT_BASH_IMMUNE" ); -const flag_id flag_EFFECT_BIO_IMMUNE( "EFFECT_BIO_IMMUNE" ); -const flag_id flag_EFFECT_BULLET_IMMUNE( "EFFECT_BULLET_IMMUNE" ); -const flag_id flag_EFFECT_COLD_IMMUNE( "EFFECT_COLD_IMMUNE" ); -const flag_id flag_EFFECT_CUT_IMMUNE( "EFFECT_CUT_IMMUNE" ); -const flag_id flag_EFFECT_ELECTRIC_IMMUNE( "EFFECT_ELECTRIC_IMMUNE" ); -const flag_id flag_EFFECT_FEATHER_FALL( "EFFECT_FEATHER_FALL" ); -const flag_id flag_EFFECT_HEAT_IMMUNE( "EFFECT_HEAT_IMMUNE" ); const flag_id flag_EFFECT_IMPEDING( "EFFECT_IMPEDING" ); -const flag_id flag_EFFECT_INVISIBLE( "EFFECT_INVISIBLE" ); -const flag_id flag_EFFECT_NIGHT_VISION( "EFFECT_NIGHT_VISION" ); -const flag_id flag_EFFECT_STAB_IMMUNE( "EFFECT_STAB_IMMUNE" ); const flag_id flag_ELECTRIC_IMMUNE( "ELECTRIC_IMMUNE" ); const flag_id flag_ETHEREAL_ITEM( "ETHEREAL_ITEM" ); const flag_id flag_FAKE_MILL( "FAKE_MILL" ); diff --git a/src/flag.h b/src/flag.h index 9c19663da5eaa..ce60d0a3f6580 100644 --- a/src/flag.h +++ b/src/flag.h @@ -16,6 +16,7 @@ template class generic_factory; extern const flag_id flag_NULL; extern const flag_id flag_ACID; extern const flag_id flag_ACID_IMMUNE; +extern const flag_id flag_ACTIVATE_ON_PLACE; extern const flag_id flag_ACTIVE_CLOAKING; extern const flag_id flag_ACT_IN_FIRE; extern const flag_id flag_ACT_ON_RANGED_HIT; @@ -83,19 +84,7 @@ extern const flag_id flag_DURABLE_MELEE; extern const flag_id flag_EATEN_COLD; extern const flag_id flag_EATEN_HOT; extern const flag_id flag_EDIBLE_FROZEN; -extern const flag_id flag_EFFECT_ACID_IMMUNE; -extern const flag_id flag_EFFECT_BASH_IMMUNE; -extern const flag_id flag_EFFECT_BIO_IMMUNE; -extern const flag_id flag_EFFECT_BULLET_IMMUNE; -extern const flag_id flag_EFFECT_COLD_IMMUNE; -extern const flag_id flag_EFFECT_CUT_IMMUNE; -extern const flag_id flag_EFFECT_ELECTRIC_IMMUNE; -extern const flag_id flag_EFFECT_FEATHER_FALL; -extern const flag_id flag_EFFECT_HEAT_IMMUNE; extern const flag_id flag_EFFECT_IMPEDING; -extern const flag_id flag_EFFECT_INVISIBLE; -extern const flag_id flag_EFFECT_NIGHT_VISION; -extern const flag_id flag_EFFECT_STAB_IMMUNE; extern const flag_id flag_ELECTRIC_IMMUNE; extern const flag_id flag_ETHEREAL_ITEM; extern const flag_id flag_FAKE_MILL; diff --git a/src/game.cpp b/src/game.cpp index 360f68bc80ae7..4fe74d5694374 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -415,7 +415,7 @@ bool game::check_mod_data( const std::vector &opts, loading_ui &ui ) std::string world_name = world_generator->active_world->world_name; world_generator->delete_world( world_name, true ); - MAPBUFFER.reset(); + MAPBUFFER.clear(); overmap_buffer.clear(); } @@ -459,7 +459,7 @@ bool game::check_mod_data( const std::vector &opts, loading_ui &ui ) std::string world_name = world_generator->active_world->world_name; world_generator->delete_world( world_name, true ); - MAPBUFFER.reset(); + MAPBUFFER.clear(); overmap_buffer.clear(); } return true; @@ -644,7 +644,7 @@ void game::setup() // reset follower list follower_ids.clear(); scent.reset(); - + effect_on_conditions::clear(); remoteveh_cache_time = calendar::before_time_starts; remoteveh_cache = nullptr; // back to menu for save loading, new game etc @@ -816,6 +816,14 @@ bool game::start_game() } } } + if( scen->has_flag( "BORDERED" ) ) { + overmap &starting_om = get_cur_om(); + for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) { + starting_om.place_special_forced( overmap_special_id( "world" ), { 0, 0, z }, + om_direction::type::north ); + } + + } for( auto &e : u.inv_dump() ) { e->set_owner( get_player_character() ); } @@ -827,7 +835,7 @@ bool game::start_game() mon->friendly = -1; mon->add_effect( effect_pet, 1_turns, true ); } else { - add_msg_debug( "cannot place starting pet, no space!" ); + add_msg_debug( debugmode::DF_GAME, "cannot place starting pet, no space!" ); } } if( u.starting_vehicle && @@ -848,6 +856,8 @@ bool game::start_game() tripoint_abs_omt abs_omt = u.global_omt_location(); const oter_id &cur_ter = overmap_buffer.ter( abs_omt ); get_event_bus().send( abs_omt.raw(), cur_ter ); + + effect_on_conditions::load_new_character(); return true; } @@ -859,15 +869,15 @@ vehicle *game::place_vehicle_nearby( if( search_types.empty() ) { vehicle veh( id ); if( veh.max_ground_velocity() > 0 ) { - search_types.push_back( "road" ); - search_types.push_back( "field" ); + search_types.emplace_back( "road" ); + search_types.emplace_back( "field" ); } else if( veh.can_float() ) { - search_types.push_back( "river" ); - search_types.push_back( "lake" ); + search_types.emplace_back( "river" ); + search_types.emplace_back( "lake" ); } else { // some default locations - search_types.push_back( "road" ); - search_types.push_back( "field" ); + search_types.emplace_back( "road" ); + search_types.emplace_back( "field" ); } } for( const std::string &search_type : search_types ) { @@ -938,7 +948,7 @@ void game::load_npcs() continue; } - add_msg_debug( "game::load_npcs: Spawning static NPC, %d:%d:%d (%d:%d:%d)", + add_msg_debug( debugmode::DF_NPC, "game::load_npcs: Spawning static NPC, %d:%d:%d (%d:%d:%d)", abs_sub.x, abs_sub.y, abs_sub.z, sm_loc.x, sm_loc.y, sm_loc.z ); temp->place_on_map(); if( !m.inbounds( temp->pos() ) ) { @@ -1115,11 +1125,11 @@ bool game::cleanup_at_end() vRip.emplace_back( R"(@%@@%%%%%@@@@@@%%%%%%%%@@%%@@@%%%@%%@)" ); } - const int iOffsetX = TERMX > FULL_SCREEN_WIDTH ? ( TERMX - FULL_SCREEN_WIDTH ) / 2 : 0; - const int iOffsetY = TERMY > FULL_SCREEN_HEIGHT ? ( TERMY - FULL_SCREEN_HEIGHT ) / 2 : 0; + const point iOffset( TERMX > FULL_SCREEN_WIDTH ? ( TERMX - FULL_SCREEN_WIDTH ) / 2 : 0, + TERMY > FULL_SCREEN_HEIGHT ? ( TERMY - FULL_SCREEN_HEIGHT ) / 2 : 0 ); catacurses::window w_rip = catacurses::newwin( FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, - point( iOffsetX, iOffsetY ) ); + iOffset ); draw_border( w_rip ); sfx::do_player_death_hurt( get_player_character(), true ); @@ -1286,7 +1296,7 @@ bool game::cleanup_at_end() sfx::fade_audio_group( sfx::group::context_themes, 300 ); sfx::fade_audio_group( sfx::group::fatigue, 300 ); - MAPBUFFER.reset(); + MAPBUFFER.clear(); overmap_buffer.clear(); #if defined(__ANDROID__) @@ -1614,6 +1624,7 @@ bool game::do_turn() ui_manager::redraw(); refresh_display(); } + effect_on_conditions::process_effect_on_conditions(); if( levz >= 0 && !u.is_underwater() ) { handle_weather_effects( weather.weather_id ); @@ -1871,10 +1882,11 @@ int get_heat_radiation( const tripoint &location, bool direct ) int get_convection_temperature( const tripoint &location ) { + static const flag_id trap_convects( "CONVECTS_TEMPERATURE" ); int temp_mod = 0; map &here = get_map(); // Directly on lava tiles - int lava_mod = here.tr_at( location ) == tr_lava ? + int lava_mod = here.tr_at( location ).has_flag( trap_convects ) ? fd_fire->get_intensity_level().convection_temperature_mod : 0; // Modifier from fields for( auto fd : here.field_at( location ) ) { @@ -2931,7 +2943,8 @@ bool game::load( const save_t &name ) u.name = name.player_name(); // This should be initialized more globally (in player/Character constructor) u.weapon = item(); - if( !read_from_file( playerpath + SAVE_EXTENSION, std::bind( &game::unserialize, this, _1 ) ) ) { + const std::string save_filename = playerpath + SAVE_EXTENSION; + if( !read_from_file( save_filename, std::bind( &game::unserialize, this, _1, save_filename ) ) ) { return false; } @@ -2939,12 +2952,14 @@ bool game::load( const save_t &name ) u.deserialize_map_memory( jsin ); } ); - read_from_file_optional( worldpath + name.base_path() + SAVE_EXTENSION_LOG, - std::bind( &memorial_logger::load, &memorial(), _1 ) ); + const std::string log_filename = worldpath + name.base_path() + SAVE_EXTENSION_LOG; + read_from_file_optional( log_filename, + std::bind( &memorial_logger::load, &memorial(), _1, log_filename ) ); #if defined(__ANDROID__) - read_from_file_optional( worldpath + name.base_path() + SAVE_EXTENSION_SHORTCUTS, - std::bind( &game::load_shortcuts, this, _1 ) ); + const std::string shortcuts_filename = worldpath + name.base_path() + SAVE_EXTENSION_SHORTCUTS; + read_from_file_optional( shortcuts_filename, + std::bind( &game::load_shortcuts, this, _1, shortcuts_filename ) ); #endif // Now that the player's worn items are updated, their sight limits need to be @@ -3326,8 +3341,8 @@ void game::display_faction_epilogues() }; scrollable_text( new_win, elem.second.name, std::accumulate( epilogue.begin() + 1, epilogue.end(), epilogue.front(), - []( const std::string & lhs, const std::string & rhs ) -> std::string { - return lhs + "\n" + rhs; + []( std::string lhs, const std::string & rhs ) -> std::string { + return std::move( lhs ) + "\n" + rhs; } ) ); } } @@ -3941,26 +3956,25 @@ void game::draw_minimap() mvwputch( w_minimap, point( 3, 0 ), c_red, "*" ); } } else { - int arrowx = -1; - int arrowy = -1; + point arrow( point_north_west ); if( std::fabs( slope ) >= 1. ) { // y diff is bigger! - arrowy = targ.y() > curs2.y() ? 6 : 0; - arrowx = + arrow.y = targ.y() > curs2.y() ? 6 : 0; + arrow.x = static_cast( 3 + 3 * ( targ.y() > curs2.y() ? slope : ( 0 - slope ) ) ); - if( arrowx < 0 ) { - arrowx = 0; + if( arrow.x < 0 ) { + arrow.x = 0; } - if( arrowx > 6 ) { - arrowx = 6; + if( arrow.x > 6 ) { + arrow.x = 6; } } else { - arrowx = targ.x() > curs2.x() ? 6 : 0; - arrowy = static_cast( 3 + 3 * ( targ.x() > curs2.x() ? slope : -slope ) ); - if( arrowy < 0 ) { - arrowy = 0; + arrow.x = targ.x() > curs2.x() ? 6 : 0; + arrow.y = static_cast( 3 + 3 * ( targ.x() > curs2.x() ? slope : -slope ) ); + if( arrow.y < 0 ) { + arrow.y = 0; } - if( arrowy > 6 ) { - arrowy = 6; + if( arrow.y > 6 ) { + arrow.y = 6; } } char glyph = '*'; @@ -3970,7 +3984,7 @@ void game::draw_minimap() glyph = 'v'; } - mvwputch( w_minimap, point( arrowx, arrowy ), c_red, glyph ); + mvwputch( w_minimap, arrow, c_red, glyph ); } } @@ -4355,10 +4369,9 @@ void game::mon_info_update( ) monster *m = dynamic_cast( c ); npc *p = dynamic_cast( c ); const direction dir_to_mon = direction_from( view.xy(), point( c->posx(), c->posy() ) ); - const int mx = POSX + ( c->posx() - view.x ); - const int my = POSY + ( c->posy() - view.y ); + const point m2( -view.xy() + point( POSX + c->posx(), POSY + c->posy() ) ); int index = 8; - if( !is_valid_in_w_terrain( point( mx, my ) ) ) { + if( !is_valid_in_w_terrain( m2 ) ) { // for compatibility with old code, see diagram below, it explains the values for index, // also might need revisiting one z-levels are in. switch( dir_to_mon ) { @@ -4571,7 +4584,8 @@ void game::monmove() << " can't move to its location! (" << critter.posx() << ":" << critter.posy() << ":" << critter.posz() << "), " << m.tername( critter.pos() ); - add_msg_debug( "%s can't move to its location! (%d,%d,%d), %s", critter.name(), + add_msg_debug( debugmode::DF_MONSTER, "%s can't move to its location! (%d,%d,%d), %s", + critter.name(), critter.posx(), critter.posy(), critter.posz(), m.tername( critter.pos() ) ); bool okay = false; for( const tripoint &dest : m.points_in_radius( critter.pos(), 3 ) ) { @@ -4669,6 +4683,12 @@ void game::monmove() debugmsg( "NPC %s entered infinite loop. Turning on debug mode", guy.name ); debug_mode = true; + // make sure the filter is active + if( std::find( + debugmode::enabled_filters.begin(), debugmode::enabled_filters.end(), + debugmode::DF_NPC ) == debugmode::enabled_filters.end() ) { + debugmode::enabled_filters.emplace_back( debugmode::DF_NPC ); + } } } @@ -6127,10 +6147,20 @@ void game::peek( const tripoint &p ) u.moves -= 200; tripoint prev = u.pos(); u.setpos( p ); + const bool is_same_pos = u.pos() == prev; + const bool is_standup_peek = is_same_pos && u.is_crouching(); tripoint center = p; - const look_around_result result = look_around( /*show_window=*/true, center, center, false, false, - true ); - u.setpos( prev ); + + look_around_result result; + const look_around_params looka_params = { true, center, center, false, false, true }; + if( is_standup_peek ) { // Non moving peek from crouch is a standup peek + u.reset_move_mode(); + result = look_around( looka_params ); + u.activate_crouch_mode(); + } else { // Else is normal peek + result = look_around( looka_params ); + u.setpos( prev ); + } if( result.peek_action && *result.peek_action == PA_BLIND_THROW ) { item_location loc; @@ -6138,6 +6168,7 @@ void game::peek( const tripoint &p ) } m.invalidate_map_cache( p.z ); } + //////////////////////////////////////////////////////////////////////////////////////////// cata::optional game::look_debug() { @@ -7048,6 +7079,8 @@ cata::optional game::look_around() return result.position; } +//look_around_result game::look_around( const bool show_window, tripoint ¢er, +// const tripoint &start_point, bool has_first_point, bool select_zone, bool peeking ) look_around_result game::look_around( const bool show_window, tripoint ¢er, const tripoint &start_point, bool has_first_point, bool select_zone, bool peeking ) { @@ -7238,7 +7271,7 @@ look_around_result game::look_around( const bool show_window, tripoint ¢er, lz = clamp( lz + dz, min_levz, max_levz ); center.z = clamp( center.z + dz, min_levz, max_levz ); - add_msg_debug( "levx: %d, levy: %d, levz: %d", + add_msg_debug( debugmode::DF_GAME, "levx: %d, levy: %d, levz: %d", get_map().get_abs_sub().x, get_map().get_abs_sub().y, center.z ); u.view_offset.z = center.z - u.posz(); m.invalidate_map_cache( center.z ); @@ -7366,6 +7399,13 @@ look_around_result game::look_around( const bool show_window, tripoint ¢er, return result; } +look_around_result game::look_around( look_around_params looka_params ) +{ + return look_around( looka_params.show_window, looka_params.center, looka_params.start_point, + looka_params.has_first_point, + looka_params.select_zone, looka_params.peeking ); +} + std::vector game::find_nearby_items( int iRadius ) { std::map temp_items; @@ -7823,12 +7863,11 @@ game::vmenu_ret game::list_items( const std::vector &item_list ) } trim_and_print( w_items, point( 1, iNum - iStartPos ), width - 9, col, sText ); const int numw = iItemNum > 9 ? 2 : 1; - const int x = iter->vIG[iThisPage].pos.x; - const int y = iter->vIG[iThisPage].pos.y; + const point p( iter->vIG[iThisPage].pos.xy() ); mvwprintz( w_items, point( width - 6 - numw, iNum - iStartPos ), iNum == iActive ? c_light_green : c_light_gray, - "%*d %s", numw, rl_dist( point_zero, point( x, y ) ), - direction_name_short( direction_from( point_zero, point( x, y ) ) ) ); + "%*d %s", numw, rl_dist( point_zero, p ), + direction_name_short( direction_from( point_zero, p ) ) ); ++iter; } } else { @@ -10107,7 +10146,7 @@ point game::place_player( const tripoint &dest_loc ) u.assign_activity( activity_id( "ACT_PULP" ), calendar::INDEFINITELY_LONG, 0 ); u.activity.placement = m.getabs( pos ); u.activity.auto_resume = true; - u.activity.str_values.push_back( "auto_pulp_no_acid" ); + u.activity.str_values.emplace_back( "auto_pulp_no_acid" ); return; } } @@ -10299,7 +10338,7 @@ bool game::phasing_move( const tripoint &dest_loc, const bool via_ramp ) tunneldist += 1; //Being dimensionally anchored prevents quantum shenanigans. if( u.worn_with_flag( flag_DIMENSIONAL_ANCHOR ) || - u.has_effect_with_flag( flag_DIMENSIONAL_ANCHOR ) ) { + u.has_flag( flag_DIMENSIONAL_ANCHOR ) ) { u.add_msg_if_player( m_info, _( "You are repelled by the barrier!" ) ); u.mod_power_level( -250_kJ ); //cost of tunneling one tile. return false; @@ -10372,7 +10411,7 @@ int game::grabbed_furn_move_time( const tripoint &dp ) tripoint fdest = fpos + dp; // intended destination of furniture. const bool canmove = can_move_furniture( fdest, dp ); - const furn_t furntype = m.furn( fpos ).obj(); + const furn_t &furntype = m.furn( fpos ).obj(); const int dst_items = m.i_at( fdest ).size(); const bool only_liquid_items = std::all_of( m.i_at( fdest ).begin(), m.i_at( fdest ).end(), @@ -10482,6 +10521,20 @@ bool game::grabbed_furn_move( const tripoint &dp ) // TODO: What is something? add_msg( _( "The %s collides with something." ), furntype.name() ); return true; + } else if( str_req > u.get_str() && u.get_perceived_pain() > 40 && + !u.has_trait( trait_id( "CENOBITE" ) ) && !u.has_trait( trait_id( "MASOCHIST" ) ) && + !u.has_trait( trait_id( "MASOCHIST_MED" ) ) ) { + add_msg( m_bad, _( "You are in too much pain to try moving the heavy %s!" ), + furntype.name() ); + return false; + + } else if( str_req > u.get_str() && u.get_perceived_pain() > 50 && + ( u.has_trait( trait_id( "MASOCHIST" ) ) || u.has_trait( trait_id( "MASOCHIST_MED" ) ) ) ) { + add_msg( m_bad, + _( "Even with your appetite for pain, you are in too much pain to try moving the heavy %s!" ), + furntype.name() ); + return false; + ///\EFFECT_STR determines ability to drag furniture } else if( str_req > u.get_str() && one_in( std::max( 20 - str_req - u.get_str(), 2 ) ) ) { @@ -12206,7 +12259,7 @@ void game::quickload() if( active_world->save_exists( save_t::from_player_name( u.name ) ) ) { if( moves_since_last_save != 0 ) { // See if we need to reload anything - MAPBUFFER.reset(); + MAPBUFFER.clear(); overmap_buffer.clear(); try { setup(); @@ -12342,7 +12395,7 @@ game::Creature_range::Creature_range( game &game_ref ) : u( &game_ref.u, []( pla const auto &monsters = game_ref.critter_tracker->get_monsters_list(); items.insert( items.end(), monsters.begin(), monsters.end() ); items.insert( items.end(), game_ref.active_npc.begin(), game_ref.active_npc.end() ); - items.push_back( u ); + items.emplace_back( u ); } game::npc_range::npc_range( game &game_ref ) diff --git a/src/game.h b/src/game.h index c7ae2a8a0e4af..7be7ef04ecda7 100644 --- a/src/game.h +++ b/src/game.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include "coordinates.h" #include "creature.h" #include "cursesdef.h" +#include "effect_on_condition.h" #include "enums.h" #include "game_constants.h" #include "item_location.h" @@ -29,6 +31,7 @@ #include "pimpl.h" #include "point.h" #include "type_id.h" +#include "uistate.h" #include "units_fwd.h" #include "weather.h" @@ -123,6 +126,14 @@ struct look_around_result { cata::optional position; cata::optional peek_action; }; +struct look_around_params { + const bool show_window; + tripoint ¢er; + const tripoint &start_point; + bool has_first_point; + bool select_zone; + bool peeking; +}; struct w_map { int id; @@ -198,7 +209,7 @@ class game void setup(); /** Saving and loading functions. */ void serialize( std::ostream &fout ); // for save - void unserialize( std::istream &fin ); // for load + void unserialize( std::istream &fin, const std::string &path ); // for load void unserialize_master( std::istream &fin ); // for load /** write statistics to stdout and @return true if successful */ @@ -570,6 +581,7 @@ class game cata::optional look_around(); look_around_result look_around( bool show_window, tripoint ¢er, const tripoint &start_point, bool has_first_point, bool select_zone, bool peeking ); + look_around_result look_around( look_around_params ); // Shared method to print "look around" info void pre_print_all_tile_info( const tripoint &lp, const catacurses::window &w_info, @@ -739,7 +751,7 @@ class game bool load( const save_t &name ); // Load a player-specific save file void load_master(); // Load the master data file, with factions &c #if defined(__ANDROID__) - void load_shortcuts( std::istream &fin ); + void load_shortcuts( std::istream &fin, const std::string &path ); #endif bool start_game(); // Starts a new game in the active world @@ -874,6 +886,8 @@ class game void process_activity(); // Processes and enacts the player's activity void handle_key_blocking_activity(); // Abort reading etc. void open_consume_item_menu(); // Custom menu for consuming specific group of items + bool do_regular_action( action_id &act, avatar &player_character, + const cata::optional &mouse_target ); bool handle_action(); bool try_get_right_click_action( action_id &act, const tripoint &mouse_target ); bool try_get_left_click_action( action_id &act, const tripoint &mouse_target ); @@ -1034,6 +1048,9 @@ class game weather_manager weather; + std::vector inactive_effect_on_condition_vector; + std::priority_queue, eoc_compare> queued_effect_on_conditions; + int mostseen = 0; // # of mons seen last turn; if this increases, set safe_mode to SAFE_MODE_STOP private: shared_ptr_fast u_shared_ptr; diff --git a/src/game_inventory.cpp b/src/game_inventory.cpp index 638c4b43e96b6..e2bd48c46f4ff 100644 --- a/src/game_inventory.cpp +++ b/src/game_inventory.cpp @@ -63,8 +63,6 @@ static const activity_id ACT_CONSUME_FOOD_MENU( "ACT_CONSUME_FOOD_MENU" ); static const activity_id ACT_CONSUME_DRINK_MENU( "ACT_CONSUME_DRINK_MENU" ); static const activity_id ACT_CONSUME_MEDS_MENU( "ACT_CONSUME_MEDS_MENU" ); -static const fault_id fault_bionic_salvaged( "fault_bionic_salvaged" ); - static const quality_id qual_ANESTHESIA( "ANESTHESIA" ); static const bionic_id bio_painkiller( "bio_painkiller" ); @@ -74,8 +72,6 @@ static const trait_id trait_NOPAIN( "NOPAIN" ); static const trait_id trait_SAPROPHAGE( "SAPROPHAGE" ); static const trait_id trait_SAPROVORE( "SAPROVORE" ); -static const json_character_flag json_flag_BIONIC_NPC_USABLE( "BIONIC_NPC_USABLE" ); - using item_filter = std::function; using item_location_filter = std::function; @@ -1816,41 +1812,15 @@ class bionic_install_preset: public inventory_selector_preset } std::string get_denial( const item_location &loc ) const override { - const item *it = loc.get_item(); - const itype *itemtype = it->type; - const bionic_id &bid = itemtype->bionic->id; - - if( it->has_flag( flag_FILTHY ) ) { - // NOLINTNEXTLINE(cata-text-style): single space after the period for symmetry - return _( "/!\\ CBM is highly contaminated. /!\\" ); - } else if( it->has_flag( flag_NO_STERILE ) ) { - // NOLINTNEXTLINE(cata-text-style): single space after the period for symmetry - return _( "/!\\ CBM is not sterile. /!\\ Please use autoclave to sterilize." ); - } else if( it->has_fault( fault_id( "fault_bionic_salvaged" ) ) ) { - return _( "CBM already deployed. Please reset to factory state." ); - } else if( pa.has_bionic( bid ) ) { - return _( "CBM already installed." ); - } else if( !pa.can_install_cbm_on_bp( get_occupied_bodyparts( bid ) ) ) { - return _( "CBM not compatible with patient's body." ); - } else if( bid->upgraded_bionic && - !pa.has_bionic( bid->upgraded_bionic ) && - it->is_upgrade() ) { - return _( "No base version installed." ); - } else if( std::any_of( bid->available_upgrades.begin(), - bid->available_upgrades.end(), - std::bind( &player::has_bionic, &pa, - std::placeholders::_1 ) ) ) { - return _( "Superior version installed." ); - } else if( pa.is_npc() && !bid->has_flag( json_flag_BIONIC_NPC_USABLE ) ) { - return _( "CBM not compatible with patient." ); - } else if( units::energy_max - pa.get_max_power_level() < bid->capacity ) { - return _( "Max power capacity already reached." ); - } else if( !p.has_enough_anesth( *itemtype, pa ) ) { + const ret_val installable = pa.is_installable( loc, true ); + if( installable.success() && !p.has_enough_anesth( *loc.get_item()->type, pa ) ) { const int weight = units::to_kilogram( pa.bodyweight() ) / 10; const int duration = loc.get_item()->type->bionic->difficulty * 2; const requirement_data req_anesth = *requirement_id( "anesthetic" ) * duration * weight; return string_format( _( "%i mL" ), req_anesth.get_tools().front().front().count ); + } else if( !installable.success() ) { + return installable.str(); } return std::string(); @@ -1929,32 +1899,8 @@ class bionic_install_surgeon_preset : public inventory_selector_preset } std::string get_denial( const item_location &loc ) const override { - const item *it = loc.get_item(); - const itype *itemtype = it->type; - const bionic_id &bid = itemtype->bionic->id; - - if( it->has_flag( flag_FILTHY ) ) { - return _( "CBM is filthy." ); - } else if( it->has_flag( flag_NO_STERILE ) ) { - return _( "CBM is not sterile." ); - } else if( it->has_fault( fault_bionic_salvaged ) ) { - return _( "CBM is already deployed." ); - } else if( pa.has_bionic( bid ) ) { - return _( "CBM is already installed." ); - } else if( bid->upgraded_bionic && - !pa.has_bionic( bid->upgraded_bionic ) && - it->is_upgrade() ) { - return _( "No base version installed." ); - } else if( std::any_of( bid->available_upgrades.begin(), - bid->available_upgrades.end(), - std::bind( &player::has_bionic, &pa, - std::placeholders::_1 ) ) ) { - return _( "Superior version installed." ); - } else if( pa.is_npc() && !bid->has_flag( json_flag_BIONIC_NPC_USABLE ) ) { - return _( "CBM is not compatible with patient." ); - } - - return std::string(); + const ret_val installable = pa.is_installable( loc, false ); + return installable.str(); } protected: diff --git a/src/gamemode_defense.cpp b/src/gamemode_defense.cpp index 82c0e50d6b5a1..04896068328e4 100644 --- a/src/gamemode_defense.cpp +++ b/src/gamemode_defense.cpp @@ -274,8 +274,7 @@ void defense_game::init_map() int old_percent = 0; for( int i = 0; i <= MAPSIZE * 2; i += 2 ) { for( int j = 0; j <= MAPSIZE * 2; j += 2 ) { - int mx = 100 - MAPSIZE + i; - int my = 100 - MAPSIZE + j; + point m( 100 - MAPSIZE + i, 100 - MAPSIZE + j ); int percent = 100 * ( ( j / 2 + MAPSIZE * ( i / 2 ) ) ) / ( ( MAPSIZE ) * ( MAPSIZE + 1 ) ); if( percent >= old_percent + 1 ) { @@ -285,10 +284,10 @@ void defense_game::init_map() old_percent = percent; } // Round down to the nearest even number - mx -= mx % 2; - my -= my % 2; + m.x -= m.x % 2; + m.y -= m.y % 2; tinymap tm; - tm.generate( tripoint( mx, my, 0 ), calendar::turn ); + tm.generate( tripoint( m, 0 ), calendar::turn ); tm.clear_spawns(); tm.clear_traps(); tm.save(); diff --git a/src/handle_action.cpp b/src/handle_action.cpp index 6f38566a9c6b2..fce8b3d44c4f9 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -1563,1055 +1563,1196 @@ void game::open_consume_item_menu() } } -bool game::handle_action() +static void handle_debug_mode() { - std::string action; - input_context ctxt; - action_id act = ACTION_NULL; - user_turn current_turn; - avatar &player_character = get_avatar(); - // Check if we have an auto-move destination - if( player_character.has_destination() ) { - act = player_character.get_next_auto_move_direction(); - if( act == ACTION_NULL ) { - add_msg( m_info, _( "Auto-move canceled" ) ); - player_character.clear_destination(); - return false; + auto debug_mode_setup = []( uilist_entry & entry ) -> void { + entry.txt = string_format( _( "Debug Mode (%1$s)" ), debug_mode ? _( "ON" ) : _( "OFF" ) ); + entry.text_color = debug_mode ? c_green : c_light_gray; + }; + + // returns if entry became active + auto debugmode_entry_setup = []( uilist_entry & entry, bool active ) -> void { + if( active ) + { + entry.extratxt.txt = _( "A" ); + entry.extratxt.color = c_white_green; + entry.text_color = c_green; + } else + { + entry.extratxt.txt = " "; + entry.extratxt.color = c_unset; + entry.text_color = c_light_gray; + } + }; + + static bool first_time = true; + if( first_time ) { + first_time = false; + debugmode::enabled_filters.clear(); + for( int i = 0; i < debugmode::DF_LAST; ++i ) { + debugmode::enabled_filters.emplace_back( static_cast( i ) ); } - } else if( player_character.has_destination_activity() ) { - // starts destination activity after the player successfully reached his destination - player_character.start_destination_activity(); - return false; - } else { - // No auto-move, ask player for input - ctxt = get_player_input( action ); } - const optional_vpart_position vp = m.veh_at( player_character.pos() ); - bool veh_ctrl = !player_character.is_dead_state() && - ( ( vp && vp->vehicle().player_in_control( player_character ) ) || remoteveh() != nullptr ); + input_context ctxt( "DEFAULTMODE" ); + ctxt.register_action( "debug_mode" ); - // If performing an action with right mouse button, co-ordinates - // of location clicked. - cata::optional mouse_target; + uilist dbmenu; + dbmenu.allow_anykey = true; + dbmenu.title = _( "Debug Mode Filters" ); + dbmenu.text = string_format( _( "Press [%1$s] to quickly toggle debug mode." ), + ctxt.get_desc( "debug_mode" ) ); - if( uquit == QUIT_WATCH && action == "QUIT" ) { - uquit = QUIT_DIED; - return false; - } + dbmenu.entries.reserve( 1 + debugmode::DF_LAST ); - if( act == ACTION_NULL ) { - act = look_up_action( action ); + dbmenu.addentry( 0, true, 'd', " " ); + debug_mode_setup( dbmenu.entries[0] ); - if( act == ACTION_KEYBINDINGS ) { - // already handled by input context - return false; - } + dbmenu.addentry( 1, true, 't', _( "Toggle all filters" ) ); + bool toggle_value = true; - if( act == ACTION_MAIN_MENU ) { - if( uquit == QUIT_WATCH ) { - return false; - } - // No auto-move actions have or can be set at this point. - player_character.clear_destination(); - destination_preview.clear(); - act = handle_main_menu(); - if( act == ACTION_NULL ) { - return false; - } - } + for( int i = 0; i < debugmode::DF_LAST; ++i ) { + uilist_entry entry( i + 2, true, 0, + debugmode::filter_name( static_cast( i ) ) ); - if( act == ACTION_ACTIONMENU ) { - if( uquit == QUIT_WATCH ) { - return false; - } - // No auto-move actions have or can be set at this point. - player_character.clear_destination(); - destination_preview.clear(); - act = handle_action_menu(); - if( act == ACTION_NULL ) { - return false; - } -#if defined(__ANDROID__) - if( get_option( "ANDROID_ACTIONMENU_AUTOADD" ) && ctxt.get_category() == "DEFAULTMODE" ) { - add_best_key_for_action_to_quick_shortcuts( act, ctxt.get_category(), false ); - } -#endif - } + entry.extratxt.left = 1; - if( act == ACTION_KEYBINDINGS ) { - player_character.clear_destination(); - destination_preview.clear(); - act = ctxt.display_menu( true ); - if( act == ACTION_NULL ) { - return false; - } - } + const bool active = std::find( + debugmode::enabled_filters.begin(), debugmode::enabled_filters.end(), + static_cast( i ) ) != debugmode::enabled_filters.end(); - if( can_action_change_worldstate( act ) ) { - user_action_counter += 1; + if( toggle_value && active ) { + toggle_value = false; } - if( act == ACTION_SELECT || act == ACTION_SEC_SELECT ) { - // Mouse button click - if( veh_ctrl ) { - // No mouse use in vehicle - return false; - } + debugmode_entry_setup( entry, active ); + dbmenu.entries.push_back( entry ); + } - if( player_character.is_dead_state() ) { - // do not allow mouse actions while dead - return false; + do { + dbmenu.query(); + if( ctxt.input_to_action( dbmenu.ret_evt ) == "debug_mode" ) { + debug_mode = !debug_mode; + if( debug_mode ) { + add_msg( m_info, _( "Debug mode ON!" ) ); + } else { + add_msg( m_info, _( "Debug mode OFF!" ) ); } + break; + } - const cata::optional mouse_pos = ctxt.get_coordinates( w_terrain ); - if( !mouse_pos ) { - return false; - } else if( !player_character.sees( *mouse_pos ) ) { - // Not clicked in visible terrain - return false; - } - mouse_target = mouse_pos; + if( dbmenu.ret == 0 ) { + debug_mode = !debug_mode; + debug_mode_setup( dbmenu.entries[0] ); - if( act == ACTION_SELECT ) { - // Note: The following has the potential side effect of - // setting auto-move destination state in addition to setting - // act. - if( !try_get_left_click_action( act, *mouse_target ) ) { - return false; - } - } else if( act == ACTION_SEC_SELECT ) { - if( !try_get_right_click_action( act, *mouse_target ) ) { - return false; + } else if( dbmenu.ret == 1 ) { + debugmode::enabled_filters.clear(); + + for( int i = 0; i < debugmode::DF_LAST; ++i ) { + debugmode_entry_setup( dbmenu.entries[i + 2], toggle_value ); + + if( toggle_value ) { + debugmode::enabled_filters.emplace_back( static_cast( i ) ); } } - } else if( act != ACTION_TIMEOUT ) { - // act has not been set for an auto-move, so clearing possible - // auto-move destinations. Since initializing an auto-move with - // the mouse may span across multiple actions, we do not clear the - // auto-move destination if the action is only a timeout, as this - // would require the user to double click quicker than the - // timeout delay. - player_character.clear_destination(); - destination_preview.clear(); - } - } - if( act == ACTION_NULL ) { - const input_event &&evt = ctxt.get_raw_input(); - if( !evt.sequence.empty() ) { - const int ch = evt.get_first_input(); - if( !get_option( "NO_UNKNOWN_COMMAND_MSG" ) ) { - add_msg( m_info, _( "Unknown command: \"%s\" (%ld)" ), evt.long_description(), ch ); - if( const cata::optional hint = - press_x_if_bound( ACTION_KEYBINDINGS ) ) { - add_msg( m_info, _( "%s at any time to see and edit keybindings relevant to " - "the current context." ), - *hint ); - } + toggle_value = !toggle_value; + + } else if( dbmenu.ret > 1 ) { + uilist_entry &entry = dbmenu.entries[dbmenu.ret]; + + const auto filter_iter = std::find( + debugmode::enabled_filters.begin(), debugmode::enabled_filters.end(), + static_cast( dbmenu.ret - 2 ) ); + + const bool active = filter_iter != debugmode::enabled_filters.end(); + + debugmode_entry_setup( entry, !active ); + + if( active ) { + debugmode::enabled_filters.erase( filter_iter ); + } else { + debugmode::enabled_filters.push_back( + static_cast( dbmenu.ret - 2 ) ); } } + } while( dbmenu.ret != UILIST_CANCEL ); +} + +static bool has_vehicle_control( avatar &player_character ) +{ + if( player_character.is_dead_state() ) { return false; } + const optional_vpart_position vp = get_map().veh_at( player_character.pos() ); + if( vp && vp->vehicle().player_in_control( player_character ) ) { + return true; + } + return g->remoteveh() != nullptr; +} - // This has no action unless we're in a special game mode. - gamemode->pre_action( act ); - - int soffset = get_option( "MOVE_VIEW_OFFSET" ); - - int before_action_moves = player_character.moves; - - // These actions are allowed while deathcam is active. Registered in game::get_player_input - if( uquit == QUIT_WATCH || !player_character.is_dead_state() ) { - switch( act ) { - case ACTION_TOGGLE_MAP_MEMORY: - player_character.toggle_map_memory(); - break; +static void do_deathcam_action( const action_id &act, avatar &player_character ) +{ + switch( act ) { + case ACTION_TOGGLE_MAP_MEMORY: + player_character.toggle_map_memory(); + break; - case ACTION_CENTER: - player_character.view_offset.x = driving_view_offset.x; - player_character.view_offset.y = driving_view_offset.y; - break; + case ACTION_CENTER: + player_character.view_offset.x = g->driving_view_offset.x; + player_character.view_offset.y = g->driving_view_offset.y; + break; - case ACTION_SHIFT_N: - case ACTION_SHIFT_NE: - case ACTION_SHIFT_E: - case ACTION_SHIFT_SE: - case ACTION_SHIFT_S: - case ACTION_SHIFT_SW: - case ACTION_SHIFT_W: - case ACTION_SHIFT_NW: { - static const std::map> shift_delta = { - { ACTION_SHIFT_N, { point_north, point_north_east } }, - { ACTION_SHIFT_NE, { point_north_east, point_east } }, - { ACTION_SHIFT_E, { point_east, point_south_east } }, - { ACTION_SHIFT_SE, { point_south_east, point_south } }, - { ACTION_SHIFT_S, { point_south, point_south_west } }, - { ACTION_SHIFT_SW, { point_south_west, point_west } }, - { ACTION_SHIFT_W, { point_west, point_north_west } }, - { ACTION_SHIFT_NW, { point_north_west, point_north } }, - }; - player_character.view_offset += use_tiles && tile_iso ? - shift_delta.at( act ).second * soffset : shift_delta.at( act ).first * soffset; - } - break; - - case ACTION_LOOK: - look_around(); - break; + case ACTION_SHIFT_N: + case ACTION_SHIFT_NE: + case ACTION_SHIFT_E: + case ACTION_SHIFT_SE: + case ACTION_SHIFT_S: + case ACTION_SHIFT_SW: + case ACTION_SHIFT_W: + case ACTION_SHIFT_NW: { + static const std::map> shift_delta = { + { ACTION_SHIFT_N, { point_north, point_north_east } }, + { ACTION_SHIFT_NE, { point_north_east, point_east } }, + { ACTION_SHIFT_E, { point_east, point_south_east } }, + { ACTION_SHIFT_SE, { point_south_east, point_south } }, + { ACTION_SHIFT_S, { point_south, point_south_west } }, + { ACTION_SHIFT_SW, { point_south_west, point_west } }, + { ACTION_SHIFT_W, { point_west, point_north_west } }, + { ACTION_SHIFT_NW, { point_north_west, point_north } }, + }; + int soffset = get_option( "MOVE_VIEW_OFFSET" ); + player_character.view_offset += use_tiles && tile_iso ? + shift_delta.at( act ).second * soffset : shift_delta.at( act ).first * soffset; + } + break; + + case ACTION_LOOK: + g->look_around(); + break; - case ACTION_KEYBINDINGS: - // already handled by input context - break; + case ACTION_KEYBINDINGS: + // already handled by input context + break; - default: - break; - } + default: + break; } +} - // actions allowed only while alive - if( !player_character.is_dead_state() ) { - switch( act ) { - case ACTION_NULL: - case NUM_ACTIONS: - break; // dummy entries - case ACTION_ACTIONMENU: - case ACTION_MAIN_MENU: - case ACTION_KEYBINDINGS: - break; // handled above - - case ACTION_TIMEOUT: - if( check_safe_mode_allowed( false ) ) { - player_character.pause(); - } - break; +bool game::do_regular_action( action_id &act, avatar &player_character, + const cata::optional &mouse_target ) +{ + switch( act ) { + case ACTION_NULL: + case NUM_ACTIONS: + break; // dummy entries + case ACTION_ACTIONMENU: + case ACTION_MAIN_MENU: + case ACTION_KEYBINDINGS: + break; // handled above + + case ACTION_TIMEOUT: + if( check_safe_mode_allowed( false ) ) { + player_character.pause(); + } + break; - case ACTION_PAUSE: - if( check_safe_mode_allowed() ) { - player_character.pause(); - } - break; + case ACTION_PAUSE: + if( check_safe_mode_allowed() ) { + player_character.pause(); + } + break; - case ACTION_CYCLE_MOVE: - player_character.cycle_move_mode(); - break; + case ACTION_CYCLE_MOVE: + player_character.cycle_move_mode(); + break; - case ACTION_RESET_MOVE: - player_character.reset_move_mode(); - break; + case ACTION_RESET_MOVE: + player_character.reset_move_mode(); + break; - case ACTION_TOGGLE_RUN: - player_character.toggle_run_mode(); - break; + case ACTION_TOGGLE_RUN: + player_character.toggle_run_mode(); + break; - case ACTION_TOGGLE_CROUCH: - player_character.toggle_crouch_mode(); - break; + case ACTION_TOGGLE_CROUCH: + player_character.toggle_crouch_mode(); + break; - case ACTION_OPEN_MOVEMENT: - open_movement_mode_menu(); - break; + case ACTION_OPEN_MOVEMENT: + open_movement_mode_menu(); + break; - case ACTION_MOVE_FORTH: - case ACTION_MOVE_FORTH_RIGHT: - case ACTION_MOVE_RIGHT: - case ACTION_MOVE_BACK_RIGHT: - case ACTION_MOVE_BACK: - case ACTION_MOVE_BACK_LEFT: - case ACTION_MOVE_LEFT: - case ACTION_MOVE_FORTH_LEFT: - if( !player_character.get_value( "remote_controlling" ).empty() && - ( player_character.has_active_item( itype_radiocontrol ) || - player_character.has_active_bionic( bio_remote ) ) ) { - rcdrive( get_delta_from_movement_action( act, iso_rotate::yes ) ); - } else if( veh_ctrl ) { - // vehicle control uses x for steering and y for ac/deceleration, - // so no rotation needed - pldrive( get_delta_from_movement_action( act, iso_rotate::no ) ); - } else { - point dest_delta = get_delta_from_movement_action( act, iso_rotate::yes ); - if( auto_travel_mode && !player_character.is_auto_moving() ) { - for( int i = 0; i < SEEX; i++ ) { - tripoint auto_travel_destination( player_character.posx() + dest_delta.x * ( SEEX - i ), - player_character.posy() + dest_delta.y * ( SEEX - i ), - player_character.posz() ); - destination_preview = m.route( player_character.pos(), - auto_travel_destination, - player_character.get_pathfinding_settings(), - player_character.get_path_avoid() ); - if( !destination_preview.empty() ) { - destination_preview.erase( destination_preview.begin() + 1, destination_preview.end() ); - player_character.set_destination( destination_preview ); - break; - } - } - act = player_character.get_next_auto_move_direction(); - const point dest_next = get_delta_from_movement_action( act, iso_rotate::yes ); - if( dest_next == point_zero ) { - player_character.clear_destination(); + case ACTION_MOVE_FORTH: + case ACTION_MOVE_FORTH_RIGHT: + case ACTION_MOVE_RIGHT: + case ACTION_MOVE_BACK_RIGHT: + case ACTION_MOVE_BACK: + case ACTION_MOVE_BACK_LEFT: + case ACTION_MOVE_LEFT: + case ACTION_MOVE_FORTH_LEFT: + if( !player_character.get_value( "remote_controlling" ).empty() && + ( player_character.has_active_item( itype_radiocontrol ) || + player_character.has_active_bionic( bio_remote ) ) ) { + rcdrive( get_delta_from_movement_action( act, iso_rotate::yes ) ); + } else if( has_vehicle_control( player_character ) ) { + // vehicle control uses x for steering and y for ac/deceleration, + // so no rotation needed + pldrive( get_delta_from_movement_action( act, iso_rotate::no ) ); + } else { + point dest_delta = get_delta_from_movement_action( act, iso_rotate::yes ); + if( auto_travel_mode && !player_character.is_auto_moving() ) { + for( int i = 0; i < SEEX; i++ ) { + tripoint auto_travel_destination( player_character.posx() + dest_delta.x * ( SEEX - i ), + player_character.posy() + dest_delta.y * ( SEEX - i ), + player_character.posz() ); + destination_preview = m.route( player_character.pos(), + auto_travel_destination, + player_character.get_pathfinding_settings(), + player_character.get_path_avoid() ); + if( !destination_preview.empty() ) { + destination_preview.erase( destination_preview.begin() + 1, destination_preview.end() ); + player_character.set_destination( destination_preview ); + break; } - dest_delta = dest_next; } - if( !avatar_action::move( player_character, m, dest_delta ) ) { - // auto-move should be canceled due to a failed move or obstacle + act = player_character.get_next_auto_move_direction(); + const point dest_next = get_delta_from_movement_action( act, iso_rotate::yes ); + if( dest_next == point_zero ) { player_character.clear_destination(); } + dest_delta = dest_next; } - break; - case ACTION_MOVE_DOWN: - if( player_character.is_mounted() ) { - auto *mon = player_character.mounted_creature.get(); - if( !mon->has_flag( MF_RIDEABLE_MECH ) ) { - add_msg( m_info, _( "You can't go down stairs while you're riding." ) ); - break; - } + if( !avatar_action::move( player_character, m, dest_delta ) ) { + // auto-move should be canceled due to a failed move or obstacle + player_character.clear_destination(); + } + } + break; + case ACTION_MOVE_DOWN: + if( player_character.is_mounted() ) { + auto *mon = player_character.mounted_creature.get(); + if( !mon->has_flag( MF_RIDEABLE_MECH ) ) { + add_msg( m_info, _( "You can't go down stairs while you're riding." ) ); + break; } - if( !player_character.in_vehicle ) { - vertical_move( -1, false ); - } else if( veh_ctrl && vp->vehicle().is_rotorcraft() ) { + } + if( !player_character.in_vehicle ) { + vertical_move( -1, false ); + } else if( has_vehicle_control( player_character ) ) { + const optional_vpart_position vp = get_map().veh_at( player_character.pos() ); + if( vp->vehicle().is_rotorcraft() ) { pldrive( tripoint_below ); } - break; + } + break; - case ACTION_MOVE_UP: - if( player_character.is_mounted() ) { - auto *mon = player_character.mounted_creature.get(); - if( !mon->has_flag( MF_RIDEABLE_MECH ) ) { - add_msg( m_info, _( "You can't go up stairs while you're riding." ) ); - break; - } + case ACTION_MOVE_UP: + if( player_character.is_mounted() ) { + auto *mon = player_character.mounted_creature.get(); + if( !mon->has_flag( MF_RIDEABLE_MECH ) ) { + add_msg( m_info, _( "You can't go up stairs while you're riding." ) ); + break; } - if( !player_character.in_vehicle ) { - vertical_move( 1, false ); - } else if( veh_ctrl && vp->vehicle().is_rotorcraft() ) { + } + if( !player_character.in_vehicle ) { + vertical_move( 1, false ); + } else if( has_vehicle_control( player_character ) ) { + const optional_vpart_position vp = get_map().veh_at( player_character.pos() ); + if( vp->vehicle().is_rotorcraft() ) { pldrive( tripoint_above ); } - break; + } + break; - case ACTION_OPEN: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't open things while you're in your shell." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't open things while you're riding." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else { - open(); - } - break; + case ACTION_OPEN: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't open things while you're in your shell." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't open things while you're riding." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else { + open(); + } + break; - case ACTION_CLOSE: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't close things while you're in your shell." ) ); - } else if( player_character.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else if( player_character.is_mounted() ) { - auto *mon = player_character.mounted_creature.get(); - if( !mon->has_flag( MF_RIDEABLE_MECH ) ) { - add_msg( m_info, _( "You can't close things while you're riding." ) ); - } - } else if( mouse_target ) { - doors::close_door( m, player_character, *mouse_target ); - } else { - close(); + case ACTION_CLOSE: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't close things while you're in your shell." ) ); + } else if( player_character.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else if( player_character.is_mounted() ) { + auto *mon = player_character.mounted_creature.get(); + if( !mon->has_flag( MF_RIDEABLE_MECH ) ) { + add_msg( m_info, _( "You can't close things while you're riding." ) ); } - break; + } else if( mouse_target ) { + doors::close_door( m, player_character, *mouse_target ); + } else { + close(); + } + break; - case ACTION_SMASH: - if( veh_ctrl ) { - handbrake(); - } else if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't smash things while you're in your shell." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else { - smash(); - } - break; + case ACTION_SMASH: + if( has_vehicle_control( player_character ) ) { + handbrake(); + } else if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't smash things while you're in your shell." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else { + smash(); + } + break; - case ACTION_EXAMINE: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't examine your surroundings while you're in your shell." ) ); - } else if( mouse_target ) { - examine( *mouse_target ); - } else { - examine(); - } - break; + case ACTION_EXAMINE: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't examine your surroundings while you're in your shell." ) ); + } else if( mouse_target ) { + examine( *mouse_target ); + } else { + examine(); + } + break; - case ACTION_ADVANCEDINV: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't move mass quantities while you're in your shell." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't move mass quantities while you're riding." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else { - create_advanced_inv(); - } - break; + case ACTION_ADVANCEDINV: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't move mass quantities while you're in your shell." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't move mass quantities while you're riding." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else { + create_advanced_inv(); + } + break; - case ACTION_PICKUP: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't pick anything up while you're in your shell." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't pick anything up while you're riding." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else if( mouse_target ) { - pickup( *mouse_target ); - } else { - pickup(); - } - break; + case ACTION_PICKUP: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't pick anything up while you're in your shell." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't pick anything up while you're riding." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else if( mouse_target ) { + pickup( *mouse_target ); + } else { + pickup(); + } + break; - case ACTION_PICKUP_FEET: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't pick anything up while you're in your shell." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else { - pickup_feet(); - } - break; + case ACTION_PICKUP_FEET: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't pick anything up while you're in your shell." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else { + pickup_feet(); + } + break; - case ACTION_GRAB: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't grab things while you're in your shell." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't grab things while you're riding." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else { - grab(); - } - break; + case ACTION_GRAB: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't grab things while you're in your shell." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't grab things while you're riding." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else { + grab(); + } + break; - case ACTION_HAUL: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't haul things while you're in your shell." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't haul things while you're riding." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else { - haul(); - } - break; + case ACTION_HAUL: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't haul things while you're in your shell." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't haul things while you're riding." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else { + haul(); + } + break; - case ACTION_BUTCHER: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't butcher while you're in your shell." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't butcher while you're riding." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else { - butcher(); - } - break; + case ACTION_BUTCHER: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't butcher while you're in your shell." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't butcher while you're riding." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else { + butcher(); + } + break; - case ACTION_CHAT: - chat(); - break; + case ACTION_CHAT: + chat(); + break; - case ACTION_PEEK: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't peek around corners while you're in your shell." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't peek around corners while you're riding." ) ); - } else { - peek(); - } - break; + case ACTION_PEEK: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't peek around corners while you're in your shell." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't peek around corners while you're riding." ) ); + } else { + peek(); + } + break; - case ACTION_LIST_ITEMS: - list_items_monsters(); - break; + case ACTION_LIST_ITEMS: + list_items_monsters(); + break; - case ACTION_ZONES: - zones_manager(); - break; + case ACTION_ZONES: + zones_manager(); + break; - case ACTION_LOOT: - loot(); - break; + case ACTION_LOOT: + loot(); + break; - case ACTION_INVENTORY: - game_menus::inv::common( player_character ); - break; + case ACTION_INVENTORY: + game_menus::inv::common( player_character ); + break; - case ACTION_COMPARE: - game_menus::inv::compare( player_character, cata::nullopt ); - break; + case ACTION_COMPARE: + game_menus::inv::compare( player_character, cata::nullopt ); + break; - case ACTION_ORGANIZE: - game_menus::inv::swap_letters( player_character ); - break; + case ACTION_ORGANIZE: + game_menus::inv::swap_letters( player_character ); + break; - case ACTION_USE: - // Shell-users are presumed to be able to mess with their inventories, etc - // while in the shell. Eating, gear-changing, and item use are OK. - avatar_action::use_item( player_character ); - break; + case ACTION_USE: + // Shell-users are presumed to be able to mess with their inventories, etc + // while in the shell. Eating, gear-changing, and item use are OK. + avatar_action::use_item( player_character ); + break; - case ACTION_USE_WIELDED: - player_character.use_wielded(); - break; + case ACTION_USE_WIELDED: + player_character.use_wielded(); + break; - case ACTION_WEAR: - wear(); - break; + case ACTION_WEAR: + wear(); + break; - case ACTION_TAKE_OFF: - takeoff(); - break; + case ACTION_TAKE_OFF: + takeoff(); + break; - case ACTION_EAT: - if( !avatar_action::eat_here( player_character ) ) { - avatar_action::eat( player_character, game_menus::inv::consume( player_character ) ); - } - break; + case ACTION_EAT: + if( !avatar_action::eat_here( player_character ) ) { + avatar_action::eat( player_character, game_menus::inv::consume( player_character ) ); + } + break; - case ACTION_OPEN_CONSUME: - if( !avatar_action::eat_here( player_character ) ) { - open_consume_item_menu(); - } - break; + case ACTION_OPEN_CONSUME: + if( !avatar_action::eat_here( player_character ) ) { + open_consume_item_menu(); + } + break; - case ACTION_READ: - // Shell-users are presumed to have the book just at an opening and read it that way - read(); - break; + case ACTION_READ: + // Shell-users are presumed to have the book just at an opening and read it that way + read(); + break; - case ACTION_WIELD: - wield(); - break; + case ACTION_WIELD: + wield(); + break; - case ACTION_PICK_STYLE: - player_character.martial_arts_data->pick_style( player_character ); - break; + case ACTION_PICK_STYLE: + player_character.martial_arts_data->pick_style( player_character ); + break; - case ACTION_RELOAD_ITEM: - reload_item(); - break; + case ACTION_RELOAD_ITEM: + reload_item(); + break; - case ACTION_RELOAD_WEAPON: - reload_weapon(); - break; + case ACTION_RELOAD_WEAPON: + reload_weapon(); + break; - case ACTION_RELOAD_WIELDED: - reload_wielded(); - break; + case ACTION_RELOAD_WIELDED: + reload_wielded(); + break; - case ACTION_UNLOAD: - avatar_action::unload( player_character ); - break; + case ACTION_UNLOAD: + avatar_action::unload( player_character ); + break; - case ACTION_MEND: - avatar_action::mend( player_character, item_location() ); - break; + case ACTION_MEND: + avatar_action::mend( player_character, item_location() ); + break; - case ACTION_THROW: { - item_location loc; - avatar_action::plthrow( player_character, loc ); - break; - } + case ACTION_THROW: { + item_location loc; + avatar_action::plthrow( player_character, loc ); + break; + } - case ACTION_FIRE: - fire(); - break; + case ACTION_FIRE: + fire(); + break; - case ACTION_CAST_SPELL: - cast_spell(); - break; + case ACTION_CAST_SPELL: + cast_spell(); + break; - case ACTION_FIRE_BURST: { - gun_mode_id original_mode = player_character.weapon.gun_get_mode_id(); - if( player_character.weapon.gun_set_mode( gun_mode_id( "AUTO" ) ) ) { - avatar_action::fire_wielded_weapon( player_character ); - player_character.weapon.gun_set_mode( original_mode ); - } - break; + case ACTION_FIRE_BURST: { + gun_mode_id original_mode = player_character.weapon.gun_get_mode_id(); + if( player_character.weapon.gun_set_mode( gun_mode_id( "AUTO" ) ) ) { + avatar_action::fire_wielded_weapon( player_character ); + player_character.weapon.gun_set_mode( original_mode ); } + break; + } - case ACTION_SELECT_FIRE_MODE: - if( player_character.is_armed() ) { - if( player_character.weapon.is_gun() && !player_character.weapon.is_gunmod() && - player_character.weapon.gun_all_modes().size() > 1 ) { - player_character.weapon.gun_cycle_mode(); - } else if( player_character.weapon.has_flag( flag_RELOAD_ONE ) || - player_character.weapon.has_flag( flag_RELOAD_AND_SHOOT ) ) { - item::reload_option opt = player_character.select_ammo( player_character.weapon, false ); - if( !opt ) { - break; - } else if( player_character.ammo_location && opt.ammo == player_character.ammo_location ) { - player_character.ammo_location = item_location(); - } else { - player_character.ammo_location = opt.ammo; - } + case ACTION_SELECT_FIRE_MODE: + if( player_character.is_armed() ) { + if( player_character.weapon.is_gun() && !player_character.weapon.is_gunmod() && + player_character.weapon.gun_all_modes().size() > 1 ) { + player_character.weapon.gun_cycle_mode(); + } else if( player_character.weapon.has_flag( flag_RELOAD_ONE ) || + player_character.weapon.has_flag( flag_RELOAD_AND_SHOOT ) ) { + item::reload_option opt = player_character.select_ammo( player_character.weapon, false ); + if( !opt ) { + break; + } else if( player_character.ammo_location && opt.ammo == player_character.ammo_location ) { + player_character.ammo_location = item_location(); + } else { + player_character.ammo_location = opt.ammo; } } - break; + } + break; - case ACTION_DROP: - // You CAN drop things to your own tile while in the shell. - drop(); - break; + case ACTION_DROP: + // You CAN drop things to your own tile while in the shell. + drop(); + break; - case ACTION_DIR_DROP: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't drop things to another tile while you're in your shell." ) ); - } else { - drop_in_direction(); - } - break; - case ACTION_BIONICS: - player_character.power_bionics(); - break; - case ACTION_MUTATIONS: - player_character.power_mutations(); - break; + case ACTION_DIR_DROP: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't drop things to another tile while you're in your shell." ) ); + } else { + drop_in_direction(); + } + break; + case ACTION_BIONICS: + player_character.power_bionics(); + break; + case ACTION_MUTATIONS: + player_character.power_mutations(); + break; - case ACTION_SORT_ARMOR: - player_character.sort_armor(); - break; + case ACTION_SORT_ARMOR: + player_character.sort_armor(); + break; - case ACTION_WAIT: - wait(); - break; + case ACTION_WAIT: + wait(); + break; - case ACTION_CRAFT: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't craft while you're in your shell." ) ); - } else if( player_character.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't craft while you're riding." ) ); - } else { - player_character.craft(); - } - break; + case ACTION_CRAFT: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't craft while you're in your shell." ) ); + } else if( player_character.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't craft while you're riding." ) ); + } else { + player_character.craft(); + } + break; - case ACTION_RECRAFT: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't craft while you're in your shell." ) ); - } else if( player_character.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't craft while you're riding." ) ); - } else { - player_character.recraft(); - } - break; + case ACTION_RECRAFT: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't craft while you're in your shell." ) ); + } else if( player_character.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't craft while you're riding." ) ); + } else { + player_character.recraft(); + } + break; - case ACTION_LONGCRAFT: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't craft while you're in your shell." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't craft while you're riding." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else { - player_character.long_craft(); - } - break; + case ACTION_LONGCRAFT: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't craft while you're in your shell." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't craft while you're riding." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else { + player_character.long_craft(); + } + break; - case ACTION_DISASSEMBLE: - if( player_character.controlling_vehicle ) { - add_msg( m_info, _( "You can't disassemble items while driving." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't disassemble items while you're riding." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else { - player_character.disassemble(); - } - break; + case ACTION_DISASSEMBLE: + if( player_character.controlling_vehicle ) { + add_msg( m_info, _( "You can't disassemble items while driving." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't disassemble items while you're riding." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else { + player_character.disassemble(); + } + break; - case ACTION_CONSTRUCT: - if( player_character.in_vehicle ) { - add_msg( m_info, _( "You can't construct while in a vehicle." ) ); - } else if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't construct while you're in your shell." ) ); - } else if( player_character.is_mounted() ) { - add_msg( m_info, _( "You can't construct while you're riding." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else { - construction_menu( false ); - } - break; + case ACTION_CONSTRUCT: + if( player_character.in_vehicle ) { + add_msg( m_info, _( "You can't construct while in a vehicle." ) ); + } else if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't construct while you're in your shell." ) ); + } else if( player_character.is_mounted() ) { + add_msg( m_info, _( "You can't construct while you're riding." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else { + construction_menu( false ); + } + break; - case ACTION_SLEEP: - if( veh_ctrl ) { - add_msg( m_info, _( "Vehicle control has moved, %s" ), - press_x( ACTION_CONTROL_VEHICLE, _( "new binding is " ), - _( "new default binding is '^'." ) ) ); - } else { - sleep(); - } - break; + case ACTION_SLEEP: + if( has_vehicle_control( player_character ) ) { + add_msg( m_info, _( "Vehicle control has moved, %s" ), + press_x( ACTION_CONTROL_VEHICLE, _( "new binding is " ), + _( "new default binding is '^'." ) ) ); + } else { + sleep(); + } + break; - case ACTION_CONTROL_VEHICLE: - if( player_character.has_active_mutation( trait_SHELL2 ) ) { - add_msg( m_info, _( "You can't operate a vehicle while you're in your shell." ) ); - } else if( player_character.is_mounted() ) { - player_character.dismount(); - } else if( player_character.has_trait( trait_WAYFARER ) ) { - add_msg( m_info, _( "You refuse to take control of this vehicle." ) ); - } else if( u.has_effect( effect_incorporeal ) ) { - add_msg( m_info, _( "You lack the substance to affect anything." ) ); - } else { - control_vehicle(); - } - break; + case ACTION_CONTROL_VEHICLE: + if( player_character.has_active_mutation( trait_SHELL2 ) ) { + add_msg( m_info, _( "You can't operate a vehicle while you're in your shell." ) ); + } else if( player_character.is_mounted() ) { + player_character.dismount(); + } else if( player_character.has_trait( trait_WAYFARER ) ) { + add_msg( m_info, _( "You refuse to take control of this vehicle." ) ); + } else if( u.has_effect( effect_incorporeal ) ) { + add_msg( m_info, _( "You lack the substance to affect anything." ) ); + } else { + control_vehicle(); + } + break; - case ACTION_TOGGLE_AUTO_TRAVEL_MODE: - auto_travel_mode = !auto_travel_mode; - add_msg( m_info, auto_travel_mode ? _( "Auto travel mode ON!" ) : _( "Auto travel mode OFF!" ) ); - break; + case ACTION_TOGGLE_AUTO_TRAVEL_MODE: + auto_travel_mode = !auto_travel_mode; + add_msg( m_info, auto_travel_mode ? _( "Auto travel mode ON!" ) : _( "Auto travel mode OFF!" ) ); + break; - case ACTION_TOGGLE_SAFEMODE: - if( safe_mode == SAFE_MODE_OFF ) { - set_safe_mode( SAFE_MODE_ON ); - mostseen = 0; - add_msg( m_info, _( "Safe mode ON!" ) ); - } else { - turnssincelastmon = 0_turns; - set_safe_mode( SAFE_MODE_OFF ); - add_msg( m_info, get_option( "AUTOSAFEMODE" ) - ? _( "Safe mode OFF! (Auto safe mode still enabled!)" ) : _( "Safe mode OFF!" ) ); + case ACTION_TOGGLE_SAFEMODE: + if( safe_mode == SAFE_MODE_OFF ) { + set_safe_mode( SAFE_MODE_ON ); + mostseen = 0; + add_msg( m_info, _( "Safe mode ON!" ) ); + } else { + turnssincelastmon = 0_turns; + set_safe_mode( SAFE_MODE_OFF ); + add_msg( m_info, get_option( "AUTOSAFEMODE" ) + ? _( "Safe mode OFF! (Auto safe mode still enabled!)" ) : _( "Safe mode OFF!" ) ); + } + if( player_character.has_effect( effect_laserlocked ) ) { + player_character.remove_effect( effect_laserlocked ); + safe_mode_warning_logged = false; + } + break; + + case ACTION_TOGGLE_AUTOSAFE: { + auto &autosafemode_option = get_options().get_option( "AUTOSAFEMODE" ); + add_msg( m_info, autosafemode_option.value_as() + ? _( "Auto safe mode OFF!" ) : _( "Auto safe mode ON!" ) ); + autosafemode_option.setNext(); + break; + } + + case ACTION_IGNORE_ENEMY: + if( safe_mode == SAFE_MODE_STOP ) { + add_msg( m_info, _( "Ignoring enemy!" ) ); + for( auto &elem : player_character.get_mon_visible().new_seen_mon ) { + monster &critter = *elem; + critter.ignoring = rl_dist( player_character.pos(), critter.pos() ); } - if( player_character.has_effect( effect_laserlocked ) ) { - player_character.remove_effect( effect_laserlocked ); - safe_mode_warning_logged = false; + set_safe_mode( SAFE_MODE_ON ); + } else if( player_character.has_effect( effect_laserlocked ) ) { + if( player_character.has_trait( trait_PROF_CHURL ) ) { + add_msg( m_warning, _( "You make the sign of the cross." ) ); + } else { + add_msg( m_info, _( "Ignoring laser targeting!" ) ); } - break; + player_character.remove_effect( effect_laserlocked ); + safe_mode_warning_logged = false; + } + break; - case ACTION_TOGGLE_AUTOSAFE: { - auto &autosafemode_option = get_options().get_option( "AUTOSAFEMODE" ); - add_msg( m_info, autosafemode_option.value_as() - ? _( "Auto safe mode OFF!" ) : _( "Auto safe mode ON!" ) ); - autosafemode_option.setNext(); - break; + case ACTION_WHITELIST_ENEMY: + if( safe_mode == SAFE_MODE_STOP && !get_safemode().empty() ) { + get_safemode().add_rule( get_safemode().lastmon_whitelist, Creature::Attitude::ANY, 0, + rule_state::WHITELISTED ); + add_msg( m_info, _( "Creature whitelisted: %s" ), get_safemode().lastmon_whitelist ); + set_safe_mode( SAFE_MODE_ON ); + mostseen = 0; + } else { + get_safemode().show(); } + break; - case ACTION_IGNORE_ENEMY: - if( safe_mode == SAFE_MODE_STOP ) { - add_msg( m_info, _( "Ignoring enemy!" ) ); - for( auto &elem : player_character.get_mon_visible().new_seen_mon ) { - monster &critter = *elem; - critter.ignoring = rl_dist( player_character.pos(), critter.pos() ); - } - set_safe_mode( SAFE_MODE_ON ); - } else if( player_character.has_effect( effect_laserlocked ) ) { - if( player_character.has_trait( trait_PROF_CHURL ) ) { - add_msg( m_warning, _( "You make the sign of the cross." ) ); - } else { - add_msg( m_info, _( "Ignoring laser targeting!" ) ); - } - player_character.remove_effect( effect_laserlocked ); - safe_mode_warning_logged = false; - } - break; + case ACTION_WORKOUT: + if( query_yn( _( "Start workout?" ) ) ) { + player_character.assign_activity( player_activity( workout_activity_actor( + player_character.pos() ) ) ); + } + break; - case ACTION_WHITELIST_ENEMY: - if( safe_mode == SAFE_MODE_STOP && !get_safemode().empty() ) { - get_safemode().add_rule( get_safemode().lastmon_whitelist, Creature::Attitude::ANY, 0, - rule_state::WHITELISTED ); - add_msg( m_info, _( "Creature whitelisted: %s" ), get_safemode().lastmon_whitelist ); - set_safe_mode( SAFE_MODE_ON ); - mostseen = 0; - } else { - get_safemode().show(); + case ACTION_SUICIDE: + if( query_yn( _( "Commit suicide?" ) ) ) { + if( query_yn( _( "REALLY commit suicide?" ) ) ) { + player_character.moves = 0; + player_character.place_corpse(); + uquit = QUIT_SUICIDE; } - break; + } + break; - case ACTION_WORKOUT: - if( query_yn( _( "Start workout?" ) ) ) { - player_character.assign_activity( player_activity( workout_activity_actor( - player_character.pos() ) ) ); + case ACTION_SAVE: + if( query_yn( _( "Save and quit?" ) ) ) { + if( save() ) { + player_character.moves = 0; + uquit = QUIT_SAVED; } - break; + } + break; - case ACTION_SUICIDE: - if( query_yn( _( "Commit suicide?" ) ) ) { - if( query_yn( _( "REALLY commit suicide?" ) ) ) { - player_character.moves = 0; - player_character.place_corpse(); - uquit = QUIT_SUICIDE; - } - } - break; + case ACTION_QUICKSAVE: + quicksave(); + return false; - case ACTION_SAVE: - if( query_yn( _( "Save and quit?" ) ) ) { - if( save() ) { - player_character.moves = 0; - uquit = QUIT_SAVED; - } - } - break; + case ACTION_QUICKLOAD: + quickload(); + return false; - case ACTION_QUICKSAVE: - quicksave(); - return false; + case ACTION_PL_INFO: + player_character.disp_info(); + break; - case ACTION_QUICKLOAD: - quickload(); - return false; + case ACTION_MAP: + ui::omap::display(); + break; - case ACTION_PL_INFO: - player_character.disp_info(); - break; + case ACTION_SKY: + if( m.is_outside( player_character.pos() ) ) { + ui::omap::display_visible_weather(); + } else { + add_msg( m_info, _( "You can't see the sky from here." ) ); + } + break; - case ACTION_MAP: - ui::omap::display(); - break; + case ACTION_MISSIONS: + list_missions(); + break; - case ACTION_SKY: - if( m.is_outside( player_character.pos() ) ) { - ui::omap::display_visible_weather(); - } else { - add_msg( m_info, _( "You can't see the sky from here." ) ); - } - break; + case ACTION_SCORES: + show_scores_ui( *achievements_tracker_ptr, stats(), get_kill_tracker() ); + break; - case ACTION_MISSIONS: - list_missions(); - break; + case ACTION_FACTIONS: + faction_manager_ptr->display(); + break; - case ACTION_SCORES: - show_scores_ui( *achievements_tracker_ptr, stats(), get_kill_tracker() ); - break; + case ACTION_MORALE: + player_character.disp_morale(); + break; - case ACTION_FACTIONS: - faction_manager_ptr->display(); - break; + case ACTION_MESSAGES: + Messages::display_messages(); + break; - case ACTION_MORALE: - player_character.disp_morale(); - break; + case ACTION_HELP: + get_help().display_help(); + break; - case ACTION_MESSAGES: - Messages::display_messages(); - break; + case ACTION_OPTIONS: + get_options().show( true ); + break; - case ACTION_HELP: - get_help().display_help(); - break; + case ACTION_AUTOPICKUP: + get_auto_pickup().show(); + break; - case ACTION_OPTIONS: - get_options().show( true ); - break; + case ACTION_AUTONOTES: + get_auto_notes_settings().show_gui(); + break; - case ACTION_AUTOPICKUP: - get_auto_pickup().show(); - break; + case ACTION_SAFEMODE: + get_safemode().show(); + break; - case ACTION_AUTONOTES: - get_auto_notes_settings().show_gui(); - break; + case ACTION_COLOR: + all_colors.show_gui(); + break; - case ACTION_SAFEMODE: - get_safemode().show(); - break; + case ACTION_WORLD_MODS: + world_generator->show_active_world_mods( world_generator->active_world->active_mod_order ); + break; - case ACTION_COLOR: - all_colors.show_gui(); - break; + case ACTION_DEBUG: + if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { + break; //don't do anything when sharing and not debugger + } + debug_menu::debug(); + break; - case ACTION_WORLD_MODS: - world_generator->show_active_world_mods( world_generator->active_world->active_mod_order ); - break; + case ACTION_TOGGLE_FULLSCREEN: + toggle_fullscreen(); + break; - case ACTION_DEBUG: - if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { - break; //don't do anything when sharing and not debugger - } - debug_menu::debug(); - break; + case ACTION_TOGGLE_PIXEL_MINIMAP: + toggle_pixel_minimap(); + break; - case ACTION_TOGGLE_FULLSCREEN: - toggle_fullscreen(); - break; + case ACTION_TOGGLE_PANEL_ADM: + panel_manager::get_manager().show_adm(); + break; - case ACTION_TOGGLE_PIXEL_MINIMAP: - toggle_pixel_minimap(); - break; + case ACTION_RELOAD_TILESET: + reload_tileset(); + break; - case ACTION_TOGGLE_PANEL_ADM: - panel_manager::get_manager().show_adm(); - break; + case ACTION_TOGGLE_AUTO_FEATURES: + get_options().get_option( "AUTO_FEATURES" ).setNext(); + get_options().save(); + //~ Auto Features are now ON/OFF + add_msg( _( "%s are now %s." ), + get_options().get_option( "AUTO_FEATURES" ).getMenuText(), + get_option( "AUTO_FEATURES" ) ? _( "ON" ) : _( "OFF" ) ); + break; - case ACTION_RELOAD_TILESET: - reload_tileset(); - break; + case ACTION_TOGGLE_AUTO_PULP_BUTCHER: + get_options().get_option( "AUTO_PULP_BUTCHER" ).setNext(); + get_options().save(); + //~ Auto Pulp/Pulp Adjacent/Butcher is now set to x + add_msg( _( "%s is now set to %s." ), + get_options().get_option( "AUTO_PULP_BUTCHER" ).getMenuText(), + get_options().get_option( "AUTO_PULP_BUTCHER" ).getValueName() ); + break; - case ACTION_TOGGLE_AUTO_FEATURES: - get_options().get_option( "AUTO_FEATURES" ).setNext(); - get_options().save(); - //~ Auto Features are now ON/OFF - add_msg( _( "%s are now %s." ), - get_options().get_option( "AUTO_FEATURES" ).getMenuText(), - get_option( "AUTO_FEATURES" ) ? _( "ON" ) : _( "OFF" ) ); - break; + case ACTION_TOGGLE_AUTO_MINING: + get_options().get_option( "AUTO_MINING" ).setNext(); + get_options().save(); + //~ Auto Mining is now ON/OFF + add_msg( _( "%s is now %s." ), + get_options().get_option( "AUTO_MINING" ).getMenuText(), + get_option( "AUTO_MINING" ) ? _( "ON" ) : _( "OFF" ) ); + break; - case ACTION_TOGGLE_AUTO_PULP_BUTCHER: - get_options().get_option( "AUTO_PULP_BUTCHER" ).setNext(); - get_options().save(); - //~ Auto Pulp/Pulp Adjacent/Butcher is now set to x - add_msg( _( "%s is now set to %s." ), - get_options().get_option( "AUTO_PULP_BUTCHER" ).getMenuText(), - get_options().get_option( "AUTO_PULP_BUTCHER" ).getValueName() ); - break; + case ACTION_TOGGLE_THIEF_MODE: + if( player_character.get_value( "THIEF_MODE" ) == "THIEF_ASK" ) { + player_character.set_value( "THIEF_MODE", "THIEF_HONEST" ); + player_character.set_value( "THIEF_MODE_KEEP", "YES" ); + //~ Thief mode cycled between THIEF_ASK/THIEF_HONEST/THIEF_STEAL + add_msg( _( "You will not pick up other peoples belongings." ) ); + } else if( player_character.get_value( "THIEF_MODE" ) == "THIEF_HONEST" ) { + player_character.set_value( "THIEF_MODE", "THIEF_STEAL" ); + player_character.set_value( "THIEF_MODE_KEEP", "YES" ); + //~ Thief mode cycled between THIEF_ASK/THIEF_HONEST/THIEF_STEAL + add_msg( _( "You will pick up also those things that belong to others!" ) ); + } else if( player_character.get_value( "THIEF_MODE" ) == "THIEF_STEAL" ) { + player_character.set_value( "THIEF_MODE", "THIEF_ASK" ); + player_character.set_value( "THIEF_MODE_KEEP", "NO" ); + //~ Thief mode cycled between THIEF_ASK/THIEF_HONEST/THIEF_STEAL + add_msg( _( "You will be reminded not to steal." ) ); + } else { + // ERROR + add_msg( _( "THIEF_MODE CONTAINED BAD VALUE [ %s ]!" ), + player_character.get_value( "THIEF_MODE" ) ); + } + break; - case ACTION_TOGGLE_AUTO_MINING: - get_options().get_option( "AUTO_MINING" ).setNext(); - get_options().save(); - //~ Auto Mining is now ON/OFF - add_msg( _( "%s is now %s." ), - get_options().get_option( "AUTO_MINING" ).getMenuText(), - get_option( "AUTO_MINING" ) ? _( "ON" ) : _( "OFF" ) ); - break; + case ACTION_TOGGLE_AUTO_FORAGING: + get_options().get_option( "AUTO_FORAGING" ).setNext(); + get_options().save(); + //~ Auto Foraging is now set to x + add_msg( _( "%s is now set to %s." ), + get_options().get_option( "AUTO_FORAGING" ).getMenuText(), + get_options().get_option( "AUTO_FORAGING" ).getValueName() ); + break; - case ACTION_TOGGLE_THIEF_MODE: - if( player_character.get_value( "THIEF_MODE" ) == "THIEF_ASK" ) { - player_character.set_value( "THIEF_MODE", "THIEF_HONEST" ); - player_character.set_value( "THIEF_MODE_KEEP", "YES" ); - //~ Thief mode cycled between THIEF_ASK/THIEF_HONEST/THIEF_STEAL - add_msg( _( "You will not pick up other peoples belongings." ) ); - } else if( player_character.get_value( "THIEF_MODE" ) == "THIEF_HONEST" ) { - player_character.set_value( "THIEF_MODE", "THIEF_STEAL" ); - player_character.set_value( "THIEF_MODE_KEEP", "YES" ); - //~ Thief mode cycled between THIEF_ASK/THIEF_HONEST/THIEF_STEAL - add_msg( _( "You will pick up also those things that belong to others!" ) ); - } else if( player_character.get_value( "THIEF_MODE" ) == "THIEF_STEAL" ) { - player_character.set_value( "THIEF_MODE", "THIEF_ASK" ); - player_character.set_value( "THIEF_MODE_KEEP", "NO" ); - //~ Thief mode cycled between THIEF_ASK/THIEF_HONEST/THIEF_STEAL - add_msg( _( "You will be reminded not to steal." ) ); - } else { - // ERROR - add_msg( _( "THIEF_MODE CONTAINED BAD VALUE [ %s ]!" ), - player_character.get_value( "THIEF_MODE" ) ); - } - break; + case ACTION_TOGGLE_AUTO_PICKUP: + get_options().get_option( "AUTO_PICKUP" ).setNext(); + get_options().save(); + //~ Auto pickup is now set to x + add_msg( _( "%s is now set to %s." ), + get_options().get_option( "AUTO_PICKUP" ).getMenuText(), + get_options().get_option( "AUTO_PICKUP" ).getValueName() ); + break; - case ACTION_TOGGLE_AUTO_FORAGING: - get_options().get_option( "AUTO_FORAGING" ).setNext(); - get_options().save(); - //~ Auto Foraging is now set to x - add_msg( _( "%s is now set to %s." ), - get_options().get_option( "AUTO_FORAGING" ).getMenuText(), - get_options().get_option( "AUTO_FORAGING" ).getValueName() ); - break; + case ACTION_DISPLAY_SCENT: + if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { + break; //don't do anything when sharing and not debugger + } + display_scent(); + break; - case ACTION_TOGGLE_AUTO_PICKUP: - get_options().get_option( "AUTO_PICKUP" ).setNext(); - get_options().save(); - //~ Auto pickup is now set to x - add_msg( _( "%s is now set to %s." ), - get_options().get_option( "AUTO_PICKUP" ).getMenuText(), - get_options().get_option( "AUTO_PICKUP" ).getValueName() ); - break; + case ACTION_DISPLAY_SCENT_TYPE: + if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { + break; //don't do anything when sharing and not debugger + } + display_scent(); + break; - case ACTION_DISPLAY_SCENT: - if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { - break; //don't do anything when sharing and not debugger - } - display_scent(); - break; + case ACTION_DISPLAY_TEMPERATURE: + if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { + break; //don't do anything when sharing and not debugger + } + display_temperature(); + break; + case ACTION_DISPLAY_VEHICLE_AI: + if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { + break; //don't do anything when sharing and not debugger + } + display_vehicle_ai(); + break; + case ACTION_DISPLAY_VISIBILITY: + if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { + break; //don't do anything when sharing and not debugger + } + display_visibility(); + break; - case ACTION_DISPLAY_SCENT_TYPE: - if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { - break; //don't do anything when sharing and not debugger - } - display_scent(); - break; + case ACTION_DISPLAY_LIGHTING: + if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { + break; //don't do anything when sharing and not debugger + } + display_lighting(); + break; - case ACTION_DISPLAY_TEMPERATURE: - if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { - break; //don't do anything when sharing and not debugger - } - display_temperature(); - break; - case ACTION_DISPLAY_VEHICLE_AI: - if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { - break; //don't do anything when sharing and not debugger - } - display_vehicle_ai(); - break; - case ACTION_DISPLAY_VISIBILITY: - if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { - break; //don't do anything when sharing and not debugger - } - display_visibility(); - break; + case ACTION_DISPLAY_RADIATION: + if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { + break; //don't do anything when sharing and not debugger + } + display_radiation(); + break; - case ACTION_DISPLAY_LIGHTING: - if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { - break; //don't do anything when sharing and not debugger - } - display_lighting(); - break; + case ACTION_TOGGLE_HOUR_TIMER: + toggle_debug_hour_timer(); + break; - case ACTION_DISPLAY_RADIATION: - if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { - break; //don't do anything when sharing and not debugger - } - display_radiation(); - break; + case ACTION_DISPLAY_TRANSPARENCY: + if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { + break; //don't do anything when sharing and not debugger + } + display_transparency(); + break; - case ACTION_TOGGLE_HOUR_TIMER: - toggle_debug_hour_timer(); - break; + case ACTION_DISPLAY_REACHABILITY_ZONES: + if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { + break; //don't do anything when sharing and not debugger + } + display_reachability_zones(); + break; - case ACTION_DISPLAY_TRANSPARENCY: - if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { - break; //don't do anything when sharing and not debugger - } - display_transparency(); - break; + case ACTION_TOGGLE_DEBUG_MODE: + if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { + break; //don't do anything when sharing and not debugger + } + handle_debug_mode(); + break; - case ACTION_DISPLAY_REACHABILITY_ZONES: - if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { - break; //don't do anything when sharing and not debugger - } - display_reachability_zones(); - break; + case ACTION_ZOOM_IN: + zoom_in(); + break; + + case ACTION_ZOOM_OUT: + zoom_out(); + break; + + case ACTION_ITEMACTION: + item_action_menu(); + break; + + case ACTION_AUTOATTACK: + avatar_action::autoattack( player_character, m ); + break; + + default: + break; + } + + return true; +} + +bool game::handle_action() +{ + std::string action; + input_context ctxt; + action_id act = ACTION_NULL; + user_turn current_turn; + avatar &player_character = get_avatar(); + // Check if we have an auto-move destination + if( player_character.has_destination() ) { + act = player_character.get_next_auto_move_direction(); + if( act == ACTION_NULL ) { + add_msg( m_info, _( "Auto-move canceled" ) ); + player_character.clear_destination(); + return false; + } + } else if( player_character.has_destination_activity() ) { + // starts destination activity after the player successfully reached his destination + player_character.start_destination_activity(); + return false; + } else { + // No auto-move, ask player for input + ctxt = get_player_input( action ); + } + + bool veh_ctrl = has_vehicle_control( player_character ); + + // If performing an action with right mouse button, co-ordinates + // of location clicked. + cata::optional mouse_target; + + if( uquit == QUIT_WATCH && action == "QUIT" ) { + uquit = QUIT_DIED; + return false; + } + + if( act == ACTION_NULL ) { + act = look_up_action( action ); + + if( act == ACTION_KEYBINDINGS ) { + // already handled by input context + return false; + } + + if( act == ACTION_MAIN_MENU ) { + if( uquit == QUIT_WATCH ) { + return false; + } + // No auto-move actions have or can be set at this point. + player_character.clear_destination(); + destination_preview.clear(); + act = handle_main_menu(); + if( act == ACTION_NULL ) { + return false; + } + } + + if( act == ACTION_ACTIONMENU ) { + if( uquit == QUIT_WATCH ) { + return false; + } + // No auto-move actions have or can be set at this point. + player_character.clear_destination(); + destination_preview.clear(); + act = handle_action_menu(); + if( act == ACTION_NULL ) { + return false; + } +#if defined(__ANDROID__) + if( get_option( "ANDROID_ACTIONMENU_AUTOADD" ) && ctxt.get_category() == "DEFAULTMODE" ) { + add_best_key_for_action_to_quick_shortcuts( act, ctxt.get_category(), false ); + } +#endif + } + + if( act == ACTION_KEYBINDINGS ) { + player_character.clear_destination(); + destination_preview.clear(); + act = ctxt.display_menu( true ); + if( act == ACTION_NULL ) { + return false; + } + } + + if( can_action_change_worldstate( act ) ) { + user_action_counter += 1; + } + + if( act == ACTION_SELECT || act == ACTION_SEC_SELECT ) { + // Mouse button click + if( veh_ctrl ) { + // No mouse use in vehicle + return false; + } + + if( player_character.is_dead_state() ) { + // do not allow mouse actions while dead + return false; + } + + const cata::optional mouse_pos = ctxt.get_coordinates( w_terrain ); + if( !mouse_pos ) { + return false; + } else if( !player_character.sees( *mouse_pos ) ) { + // Not clicked in visible terrain + return false; + } + mouse_target = mouse_pos; - case ACTION_TOGGLE_DEBUG_MODE: - if( MAP_SHARING::isCompetitive() && !MAP_SHARING::isDebugger() ) { - break; //don't do anything when sharing and not debugger + if( act == ACTION_SELECT ) { + // Note: The following has the potential side effect of + // setting auto-move destination state in addition to setting + // act. + if( !try_get_left_click_action( act, *mouse_target ) ) { + return false; } - debug_mode = !debug_mode; - if( debug_mode ) { - add_msg( m_info, _( "Debug mode ON!" ) ); - } else { - add_msg( m_info, _( "Debug mode OFF!" ) ); + } else if( act == ACTION_SEC_SELECT ) { + if( !try_get_right_click_action( act, *mouse_target ) ) { + return false; } - break; + } + } else if( act != ACTION_TIMEOUT ) { + // act has not been set for an auto-move, so clearing possible + // auto-move destinations. Since initializing an auto-move with + // the mouse may span across multiple actions, we do not clear the + // auto-move destination if the action is only a timeout, as this + // would require the user to double click quicker than the + // timeout delay. + player_character.clear_destination(); + destination_preview.clear(); + } + } - case ACTION_ZOOM_IN: - zoom_in(); - break; + if( act == ACTION_NULL ) { + const input_event &&evt = ctxt.get_raw_input(); + if( !evt.sequence.empty() ) { + const int ch = evt.get_first_input(); + if( !get_option( "NO_UNKNOWN_COMMAND_MSG" ) ) { + add_msg( m_info, _( "Unknown command: \"%s\" (%ld)" ), evt.long_description(), ch ); + if( const cata::optional hint = + press_x_if_bound( ACTION_KEYBINDINGS ) ) { + add_msg( m_info, _( "%s at any time to see and edit keybindings relevant to " + "the current context." ), + *hint ); + } + } + } + return false; + } - case ACTION_ZOOM_OUT: - zoom_out(); - break; + // This has no action unless we're in a special game mode. + gamemode->pre_action( act ); - case ACTION_ITEMACTION: - item_action_menu(); - break; + int before_action_moves = player_character.moves; - case ACTION_AUTOATTACK: - avatar_action::autoattack( player_character, m ); - break; + // These actions are allowed while deathcam is active. Registered in game::get_player_input + if( uquit == QUIT_WATCH || !player_character.is_dead_state() ) { + do_deathcam_action( act, player_character ); + } - default: - break; + // actions allowed only while alive + if( !player_character.is_dead_state() ) { + if( !do_regular_action( act, player_character, mouse_target ) ) { + return false; } } if( act != ACTION_TIMEOUT ) { diff --git a/src/handle_liquid.cpp b/src/handle_liquid.cpp index 00cb39c8ea231..310f29bca0f49 100644 --- a/src/handle_liquid.cpp +++ b/src/handle_liquid.cpp @@ -86,7 +86,7 @@ static void serialize_liquid_target( player_activity &act, const item_location & act.values.push_back( static_cast( liquid_target_type::CONTAINER ) ); act.values.push_back( 0 ); // dummy act.targets.push_back( container_item ); - act.coords.push_back( tripoint() ); // dummy + act.coords.emplace_back( ); // dummy } static void serialize_liquid_target( player_activity &act, const tripoint &pos ) @@ -250,14 +250,24 @@ static bool get_liquid_target( item &liquid, const item *const source, const int } } for( vehicle *veh : opts ) { - if( veh == source_veh ) { - continue; + if( veh == source_veh && veh->has_part( "FLUIDTANK", false ) ) { + for( const vpart_reference &vp : veh->get_avail_parts( "FLUIDTANK" ) ) { + if( vp.part().get_base().is_reloadable_with( liquid.typeId() ) ) { + menu.addentry( -1, true, MENU_AUTOASSIGN, _( "Fill avaliable tank" ) ); + actions.emplace_back( [ &, veh]() { + target.veh = veh; + target.dest_opt = LD_VEH; + } ); + break; + } + } + } else { + menu.addentry( -1, true, MENU_AUTOASSIGN, _( "Fill nearby vehicle %s" ), veh->name ); + actions.emplace_back( [ &, veh]() { + target.veh = veh; + target.dest_opt = LD_VEH; + } ); } - menu.addentry( -1, true, MENU_AUTOASSIGN, _( "Fill nearby vehicle %s" ), veh->name ); - actions.emplace_back( [ &, veh]() { - target.veh = veh; - target.dest_opt = LD_VEH; - } ); } for( const tripoint &target_pos : here.points_in_radius( player_character.pos(), 1 ) ) { diff --git a/src/help.cpp b/src/help.cpp index 8cb5986d5ccba..210c2887a9e0b 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -196,12 +196,14 @@ void help::display_help() const size_t pos2 = line_proc.find( ">", pos, 1 ); std::string action = line_proc.substr( pos + 7, pos2 - pos - 7 ); - auto replace = "" + press_x( look_up_action( action ), "", "" ) + ""; + std::string replace = "" + + press_x( look_up_action( action ), "", "" ) + ""; if( replace.empty() ) { debugmsg( "Help json: Unknown action: %s", action ); } else { - line_proc = string_replace( line_proc, "", replace ); + line_proc = string_replace( + line_proc, "", replace ); } pos = line_proc.find( "open_gate( examp ); } -static bool try_start_hacking( player &p, const tripoint &examp ) +bool iexamine::try_start_hacking( player &p, const tripoint &examp ) { if( p.has_trait( trait_ILLITERATE ) ) { add_msg( _( "You cannot read!" ) ); return false; } const bool has_item = p.has_charges( itype_electrohack, 25 ); - const bool has_bionic = p.has_bionic( bio_fingerhack ) && p.get_power_level() >= 25_kJ; - if( !has_item && !has_bionic ) { + if( !has_item ) { add_msg( _( "You don't have a hacking tool with enough charges!" ) ); return false; } - bool use_bionic = has_bionic; - if( has_item && has_bionic ) { - uilist menu; - menu.settext( _( "Use which hacking tool?" ) ); - menu.addentry( 0, true, MENU_AUTOASSIGN, "%s", itype_electrohack->nname( 1 ) ); - menu.addentry( 1, true, MENU_AUTOASSIGN, "%s", bio_fingerhack->name ); - menu.query(); - switch( menu.ret ) { - case 0: - use_bionic = false; - break; - case 1: - use_bionic = true; - break; - default: - return false; - } - } - if( use_bionic ) { - p.mod_power_level( -25_kJ ); - p.assign_activity( player_activity( hacking_activity_actor( - hacking_activity_actor::use_bionic {} ) ) ); - } else { - p.use_charges( itype_electrohack, 25 ); - p.assign_activity( player_activity( hacking_activity_actor() ) ); - } + p.use_charges( itype_electrohack, 25 ); + p.assign_activity( player_activity( hacking_activity_actor() ) ); p.activity.placement = examp; return true; } -/** - * Use id/hack reader. Using an id despawns turrets. - */ -void iexamine::cardreader( player &p, const tripoint &examp ) -{ - bool open = false; - map &here = get_map(); - itype_id card_type = ( here.ter( examp ) == t_card_science ? itype_id_science : - here.ter( examp ) == t_card_military ? itype_id_military : - itype_id_industrial ); - if( p.has_amount( card_type, 1 ) && query_yn( _( "Swipe your ID card?" ) ) ) { - p.mod_moves( -to_moves( 1_seconds ) ); - - for( const tripoint &tmp : here.points_in_radius( examp, 3 ) ) { - if( here.ter( tmp ) == t_door_metal_locked ) { - here.ter_set( tmp, t_door_metal_c ); - open = true; - } - } - - add_msg( m_info, _( "You insert your ID card." ) ); - - if( open ) { - add_msg( m_good, _( "The nearby doors unlock." ) ); - } else { - add_msg( m_info, _( "The nearby doors are already opened." ) ); - } - - p.use_amount( card_type, 1 ); - - for( monster &critter : g->all_monsters() ) { - // Check 1) same overmap coords, 2) turret, 3) hostile - if( ms_to_omt_copy( here.getabs( critter.pos() ) ) == ms_to_omt_copy( here.getabs( examp ) ) && - critter.has_flag( MF_ID_CARD_DESPAWN ) && - critter.attitude_to( p ) == Creature::Attitude::HOSTILE ) { - g->remove_zombie( critter ); - } - } - } else if( query_yn( _( "Attempt to hack this card-reader?" ) ) ) { - try_start_hacking( p, examp ); - } -} - void iexamine::cardreader_robofac( player &p, const tripoint &examp ) { itype_id card_type = itype_id_science; @@ -1409,7 +1339,7 @@ void iexamine::pit( player &p, const tripoint &examp ) return; } std::vector planks; - planks.push_back( item_comp( itype_2x4, 1 ) ); + planks.emplace_back( itype_2x4, 1 ); map &here = get_map(); if( query_yn( _( "Place a plank over the pit?" ) ) ) { @@ -3590,7 +3520,7 @@ void iexamine::tree_maple( player &p, const tripoint &examp ) } std::vector comps; - comps.push_back( item_comp( itype_tree_spile, 1 ) ); + comps.emplace_back( itype_tree_spile, 1 ); p.consume_items( comps, 1, is_crafting_component ); p.mod_moves( -to_moves( 20_seconds ) ); @@ -3765,116 +3695,6 @@ void iexamine::shrub_wildveggies( player &p, const tripoint &examp ) p.activity.auto_resume = true; } -void iexamine::recycle_compactor( player &, const tripoint &examp ) -{ - // choose what metal to recycle - auto metals = materials::get_compactable(); - uilist choose_metal; - choose_metal.text = _( "Recycle what metal?" ); - for( auto &m : metals ) { - choose_metal.addentry( m.name() ); - } - choose_metal.query(); - int m_idx = choose_metal.ret; - if( m_idx < 0 || m_idx >= static_cast( metals.size() ) ) { - add_msg( _( "Never mind." ) ); - return; - } - material_type m = metals.at( m_idx ); - - map &here = get_map(); - // check inputs and tally total mass - map_stack inputs = here.i_at( examp ); - units::mass sum_weight = 0_gram; - auto ca = m.compact_accepts(); - std::set accepts( ca.begin(), ca.end() ); - accepts.insert( m.id ); - for( auto &input : inputs ) { - if( !input.only_made_of( accepts ) ) { - //~ %1$s: an item in the compactor , %2$s: desired compactor output material - add_msg( _( "You realize this isn't going to work because %1$s is not made purely of %2$s." ), - input.tname(), m.name() ); - return; - } - if( input.is_container() && !input.is_container_empty() ) { - //~ %1$s: an item in the compactor - add_msg( _( "You realize this isn't going to work because %1$s has not been emptied of its contents." ), - input.tname() ); - return; - } - sum_weight += input.weight(); - } - if( sum_weight <= 0_gram ) { - //~ %1$s: desired compactor output material - add_msg( _( "There is no %1$s in the compactor. Drop some metal items onto it and try again." ), - m.name() ); - return; - } - - // See below for recover_factor (rng(6,9)/10), this - // is the normal value of that recover factor. - static const double norm_recover_factor = 8.0 / 10.0; - const units::mass norm_recover_weight = sum_weight * norm_recover_factor; - - // choose output - uilist choose_output; - //~ %1$.3f: total mass of material in compactor, %2$s: weight units , %3$s: compactor output material - choose_output.text = string_format( _( "Compact %1$.3f %2$s of %3$s into:" ), - convert_weight( sum_weight ), weight_units(), m.name() ); - for( const itype_id &ci : m.compacts_into() ) { - item it = item( ci, calendar::turn_zero, item::solitary_tag{} ); - const int amount = norm_recover_weight / it.weight(); - //~ %1$d: number of, %2$s: output item - choose_output.addentry( string_format( _( "about %1$d %2$s" ), amount, - it.tname( amount ) ) ); - } - choose_output.query(); - int o_idx = choose_output.ret; - if( o_idx < 0 || o_idx >= static_cast( m.compacts_into().size() ) ) { - add_msg( _( "Never mind." ) ); - return; - } - - // remove items - for( auto it = inputs.begin(); it != inputs.end(); ) { - it = inputs.erase( it ); - } - - // produce outputs - double recover_factor = rng( 6, 9 ) / 10.0; - sum_weight = sum_weight * recover_factor; - sounds::sound( examp, 80, sounds::sound_t::combat, _( "Ka-klunk!" ), true, "tool", "compactor" ); - bool out_desired = false; - bool out_any = false; - for( auto it = m.compacts_into().begin() + o_idx; it != m.compacts_into().end(); ++it ) { - const units::mass ow = item( *it, calendar::turn_zero, item::solitary_tag{} ).weight(); - int count = sum_weight / ow; - sum_weight -= count * ow; - if( count > 0 ) { - here.spawn_item( examp, *it, count, 1, calendar::turn ); - if( !out_any ) { - out_any = true; - if( it == m.compacts_into().begin() + o_idx ) { - out_desired = true; - } - } - } - } - - // feedback to user - if( !out_any ) { - add_msg( _( "The compactor chews up all the items in its hopper." ) ); - //~ %1$s: compactor output material - add_msg( _( "The compactor beeps: \"No %1$s to process!\"" ), m.name() ); - return; - } - if( !out_desired ) { - //~ %1$s: compactor output material - add_msg( _( "The compactor beeps: \"Insufficient %1$s!\"" ), m.name() ); - add_msg( _( "It spits out an assortment of smaller pieces instead." ) ); - } -} - void trap::examine( const tripoint &examp ) const { avatar &player_character = get_avatar(); @@ -3950,7 +3770,8 @@ void trap::examine( const tripoint &examp ) const int roll = std::round( normal_roll( mean_roll, 3 ) ); - add_msg( m_debug, _( "Rolled %i, mean_roll %g. difficulty %i." ), roll, mean_roll, difficulty ); + add_msg_debug( debugmode::DF_IEXAMINE, _( "Rolled %i, mean_roll %g. difficulty %i." ), roll, + mean_roll, difficulty ); //Difficulty 0 traps should succeed regardless of proficiencies. (i.e caltrops and nailboards) if( roll >= difficulty || difficulty == 0 ) { @@ -4166,7 +3987,7 @@ void iexamine::sign( player &p, const tripoint &examp ) } ); tools.reserve( filter.size() ); for( const item *writing_item : filter ) { - tools.push_back( tool_comp( writing_item->typeId(), 1 ) ); + tools.emplace_back( writing_item->typeId(), 1 ); } if( !tools.empty() ) { @@ -4262,9 +4083,14 @@ cata::optional iexamine::getNearFilledGasTank( const tripoint ¢er, static int getGasDiscountCardQuality( const item &it ) { for( const flag_id &tag : it.type->get_flags() ) { - int discount_value; - if( sscanf( tag->id.c_str(), "DISCOUNT_VALUE_%i", &discount_value ) == 1 ) { - return discount_value; + if( string_starts_with( tag->id.str(), "DISCOUNT_VALUE_" ) ) { + ret_val discount_value = + try_parse_integer( tag->id.str().substr( 15 ), false ); + if( discount_value.success() ) { + return discount_value.value(); + } else { + debugmsg( "Error parsing ammo DISCOUNT_VALUE_ suffix: %s", discount_value.str() ); + } } } return 0; @@ -4462,9 +4288,7 @@ void iexamine::pay_gas( player &p, const tripoint &examp ) int pricePerUnit = getGasPricePerLiter( discount ); - bool can_hack = ( !p.has_trait( trait_ILLITERATE ) && - ( ( p.has_charges( itype_electrohack, 25 ) ) || - ( p.has_bionic( bio_fingerhack ) && p.get_power_level() > 24_kJ ) ) ); + bool can_hack = ( !p.has_trait( trait_ILLITERATE ) && p.has_charges( itype_electrohack, 25 ) ); uilist amenu; amenu.selected = 1; @@ -4888,9 +4712,9 @@ void iexamine::autodoc( player &p, const tripoint &examp ) } case 2: { std::vector choice_names; - choice_names.push_back( _( "Personality_Override" ) ); + choice_names.emplace_back( _( "Personality_Override" ) ); for( size_t i = 0; i < 6; i++ ) { - choice_names.push_back( _( "C0RR#PTED?D#TA" ) ); + choice_names.emplace_back( _( "C0RR#PTED?D#TA" ) ); } int choice_index = uilist( _( "Choose bionic to uninstall" ), choice_names ); if( choice_index == 0 ) { @@ -4940,8 +4764,9 @@ void iexamine::autodoc( player &p, const tripoint &examp ) } autodoc_header += - string_format( "\n\nInternal supplies:\n Arm splints: %d\n Leg splints: %d", - arm_splints.size(), leg_splints.size() ); + string_format( + _( "\n\nInternal supplies:\n Arm splints: %d\n Leg splints: %d" ), + arm_splints.size(), leg_splints.size() ); uilist amenu; amenu.text = autodoc_header; @@ -4968,7 +4793,7 @@ void iexamine::autodoc( player &p, const tripoint &examp ) } ); for( const item *anesthesia_item : a_filter ) { if( anesthesia_item->ammo_remaining() >= 1 ) { - anesth_kit.push_back( tool_comp( anesthesia_item->typeId(), 1 ) ); + anesth_kit.emplace_back( anesthesia_item->typeId(), 1 ); drug_count += anesthesia_item->ammo_remaining(); } } @@ -4998,7 +4823,7 @@ void iexamine::autodoc( player &p, const tripoint &examp ) if( !install_programs.empty() ) { has_install_program = true; - progs.push_back( item_comp( install_programs[0]->typeId(), 1 ) ); + progs.emplace_back( install_programs[0]->typeId(), 1 ); } const int weight = units::to_kilogram( patient.bodyweight() ) / 10; @@ -5558,7 +5383,7 @@ static void smoker_load_food( player &p, const tripoint &examp, entries.push_back( smokable_item ); } names.push_back( item::nname( smokable_item->typeId(), 1 ) ); - comps.push_back( item_comp( smokable_item->typeId(), count ) ); + comps.emplace_back( smokable_item->typeId(), count ); } } @@ -5607,7 +5432,7 @@ static void smoker_load_food( player &p, const tripoint &examp, // reload comps with chosen items and quantity comps.clear(); - comps.push_back( item_comp( what->typeId(), amount ) ); + comps.emplace_back( what->typeId(), amount ); Character &player_character = get_player_character(); // select from where to get the items from and place them @@ -5667,7 +5492,7 @@ static void mill_load_food( player &p, const tripoint &examp, entries.push_back( millable_item ); } names.push_back( item::nname( millable_item->typeId(), 1 ) ); - comps.push_back( item_comp( millable_item->typeId(), count ) ); + comps.emplace_back( millable_item->typeId(), count ); } } @@ -5716,7 +5541,7 @@ static void mill_load_food( player &p, const tripoint &examp, // reload comps with chosen items and quantity comps.clear(); - comps.push_back( item_comp( what->typeId(), amount ) ); + comps.emplace_back( what->typeId(), amount ); Character &player_character = get_player_character(); // select from where to get the items from and place them @@ -6266,7 +6091,8 @@ void iexamine::workbench_internal( player &p, const tripoint &examp, break; } const recipe &rec = selected_craft->get_making(); - if( p.has_recipe( &rec, p.crafting_inventory(), p.get_crafting_helpers() ) == -1 ) { + const inventory &inv = p.crafting_inventory(); + if( p.has_recipe( &rec, inv, p.get_crafting_helpers() ) == -1 ) { p.add_msg_player_or_npc( _( "You don't know the recipe for the %s and can't continue crafting." ), _( " doesn't know the recipe for the %s and can't continue crafting." ), @@ -6321,7 +6147,6 @@ iexamine_function iexamine_function_from_string( const std::string &function_nam { "toilet", &iexamine::toilet }, { "elevator", &iexamine::elevator }, { "controls_gate", &iexamine::controls_gate }, - { "cardreader", &iexamine::cardreader }, { "cardreader_robofac", &iexamine::cardreader_robofac }, { "cardreader_fp", &iexamine::cardreader_foodplace }, { "intercom", &iexamine::intercom }, @@ -6364,7 +6189,6 @@ iexamine_function iexamine_function_from_string( const std::string &function_nam { "tree_maple", &iexamine::tree_maple }, { "tree_maple_tapped", &iexamine::tree_maple_tapped }, { "shrub_wildveggies", &iexamine::shrub_wildveggies }, - { "recycle_compactor", &iexamine::recycle_compactor }, { "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 b27169671d104..75f28edfc3396 100644 --- a/src/iexamine.h +++ b/src/iexamine.h @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -12,6 +13,7 @@ #include "type_id.h" class item; +class JsonObject; class player; class time_point; class vpart_reference; @@ -20,9 +22,25 @@ struct tripoint; using seed_tuple = std::tuple; +struct iexamine_actor { + const std::string type; + + explicit iexamine_actor( const std::string &type ) : type( type ) {} + + virtual void load( const JsonObject & ) = 0; + virtual void call( player &, const tripoint & ) const = 0; + virtual void finalize() const = 0; + + virtual std::unique_ptr clone() const = 0; + + virtual ~iexamine_actor() = default; +}; + namespace iexamine { +bool try_start_hacking( player &p, const tripoint &examp ); + void egg_sack_generic( player &p, const tripoint &examp, const mtype_id &montype ); void none( player &p, const tripoint &examp ); @@ -81,7 +99,6 @@ void tree_maple_tapped( player &p, const tripoint &examp ); 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 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/iexamine_actors.cpp b/src/iexamine_actors.cpp new file mode 100644 index 0000000000000..b9cb40a4c8aac --- /dev/null +++ b/src/iexamine_actors.cpp @@ -0,0 +1,165 @@ +#include "iexamine_actors.h" + +#include "game.h" +#include "generic_factory.h" +#include "itype.h" +#include "map.h" +#include "mapgen_functions.h" +#include "map_iterator.h" +#include "messages.h" +#include "output.h" +#include "player.h" + +void cardreader_examine_actor::consume_card( player &guy ) const +{ + std::vector cards; + for( const flag_id &flag : allowed_flags ) { + for( const item *it : guy.all_items_with_flag( flag ) ) { + itype_id card_type = it->typeId(); + if( std::find( cards.begin(), cards.end(), card_type ) == cards.end() ) { + cards.push_back( card_type ); + } + } + } + if( cards.size() > 1 ) { + uilist query; + query.text = _( "Use which item?" ); + for( size_t i = 0; i < cards.size(); ++i ) { + query.entries.emplace_back( static_cast( i ), true, -1, cards[i]->nname( 1 ) ); + } + query.query(); + while( query.ret == UILIST_CANCEL ) { + query.query(); + } + guy.use_amount( cards[query.ret], 1 ); + return; + } + guy.use_amount( cards[0], 1 ); +} + +bool cardreader_examine_actor::apply( const tripoint &examp ) const +{ + bool open = true; + + map &here = get_map(); + if( map_regen ) { + tripoint_abs_omt omt_pos( ms_to_omt_copy( here.getabs( examp ) ) ); + if( !run_mapgen_update_func( mapgen_id, omt_pos, nullptr, false ) ) { + debugmsg( "Failed to apply magen function %s", mapgen_id ); + } + here.set_seen_cache_dirty( examp ); + here.set_transparency_cache_dirty( examp.z ); + } else { + open = false; + const tripoint_range points = here.points_in_radius( examp, radius ); + for( const tripoint &tmp : points ) { + const auto ter_iter = terrain_changes.find( here.ter( tmp ).id() ); + const auto furn_iter = furn_changes.find( here.furn( tmp ).id() ); + if( ter_iter != terrain_changes.end() ) { + here.ter_set( tmp, ter_iter->second ); + open = true; + } + if( furn_iter != furn_changes.end() ) { + here.furn_set( tmp, furn_iter->second ); + open = true; + } + } + } + + return open; +} + +/** + * Use id/hack reader. Using an id despawns turrets. + */ +void cardreader_examine_actor::call( player &guy, const tripoint &examp ) const +{ + bool open = false; + map &here = get_map(); + + bool has_item = false; + for( const flag_id &flag : allowed_flags ) { + if( guy.has_item_with_flag( flag ) ) { + has_item = true; + break; + } + } + + + if( has_item && query_yn( _( query_msg ) ) ) { + guy.mod_moves( -to_moves( 1_seconds ) ); + open = apply( examp ); + for( monster &critter : g->all_monsters() ) { + if( !despawn_monsters ) { + break; + } + // Check 1) same overmap coords, 2) turret, 3) hostile + if( ms_to_omt_copy( here.getabs( critter.pos() ) ) == ms_to_omt_copy( here.getabs( examp ) ) && + critter.has_flag( MF_ID_CARD_DESPAWN ) && + critter.attitude_to( guy ) == Creature::Attitude::HOSTILE ) { + g->remove_zombie( critter ); + } + } + if( open ) { + add_msg( _( success_msg ) ); + consume_card( guy ); + } else { + add_msg( _( redundant_msg ) ); + } + } else if( allow_hacking && query_yn( _( "Attempt to hack this card-reader?" ) ) ) { + iexamine::try_start_hacking( guy, examp ); + } +} + +void cardreader_examine_actor::load( const JsonObject &jo ) +{ + mandatory( jo, false, "flags", allowed_flags ); + optional( jo, false, "consume_card", consume, true ); + optional( jo, false, "allow_hacking", allow_hacking, true ); + optional( jo, false, "despawn_monsters", despawn_monsters, true ); + if( jo.has_string( "mapgen_id" ) ) { + optional( jo, false, "mapgen_id", mapgen_id ); + map_regen = true; + } else { + optional( jo, false, "radius", radius, 3 ); + optional( jo, false, "terrain_changes", terrain_changes ); + optional( jo, false, "furn_changes", furn_changes ); + } + optional( jo, false, "query", query, true ); + optional( jo, false, "query_msg", query_msg ); + mandatory( jo, false, "success_msg", success_msg ); + mandatory( jo, false, "redundant_msg", redundant_msg ); + +} + +void cardreader_examine_actor::finalize() const +{ + if( allowed_flags.empty() ) { + debugmsg( "Cardreader examine actor has no allowed card flags." ); + } + + for( const flag_id &flag : allowed_flags ) { + if( !flag.is_valid() ) { + debugmsg( "Cardreader uses flag %s that does not exist!", flag.str() ); + } + } + + if( terrain_changes.empty() && furn_changes.empty() && mapgen_id.empty() ) { + debugmsg( "Cardreader examine actor does not change either terrain or furniture" ); + } + + if( query && query_msg.empty() ) { + debugmsg( "Cardreader is told to query, yet does not have a query message defined." ); + } + + if( allow_hacking && ( !furn_changes.empty() || terrain_changes.size() != 1 || + terrain_changes.count( ter_str_id( "t_door_metal_locked" ) ) != 1 || + terrain_changes.at( ter_str_id( "t_door_metal_locked" ) ) != ter_str_id( "t_door_metal_c" ) ) ) { + debugmsg( "Cardreader allows hacking, but activites different that if hacked." ); + } +} + +std::unique_ptr cardreader_examine_actor::clone() const +{ + return std::make_unique( *this ); +} diff --git a/src/iexamine_actors.h b/src/iexamine_actors.h new file mode 100644 index 0000000000000..1817262bcae5c --- /dev/null +++ b/src/iexamine_actors.h @@ -0,0 +1,48 @@ +#pragma once +#ifndef CATA_SRC_IEXAMINE_ACTORS_H +#define CATA_SRC_IEXAMINE_ACTORS_H + +#include "iexamine.h" + +#include + +class Character; + +class cardreader_examine_actor : public iexamine_actor +{ + private: + std::vector allowed_flags; + bool consume = true; + bool allow_hacking = true; + bool despawn_monsters = true; + + // Option 1: walk the map, do some stuff + int radius = 3; + std::map terrain_changes; + std::map furn_changes; + // Option 2: Regenerate entire current overmap tile + std::string mapgen_id; + + bool map_regen = false; + + bool query = true; + std::string query_msg; + + std::string success_msg; + std::string redundant_msg; + + void consume_card( player &guy ) const; + bool apply( const tripoint &examp ) const; + + public: + explicit cardreader_examine_actor( const std::string &type = "cardreader" ) + : iexamine_actor( type ) {} + + void load( const JsonObject &jo ) override; + void call( player &guy, const tripoint &examp ) const override; + void finalize() const override; + + std::unique_ptr clone() const override; +}; + +#endif // CATA_SRC_IEXAMINE_ACTORS_H diff --git a/src/init.cpp b/src/init.cpp index 8a76c67f83184..0529b99571eab 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -31,6 +31,7 @@ #include "dialogue.h" #include "disease.h" #include "effect.h" +#include "effect_on_condition.h" #include "emit.h" #include "event_statistics.h" #include "faction.h" @@ -231,6 +232,9 @@ void DynamicDataLoader::add( const std::string &type, void DynamicDataLoader::initialize() { + // Initialize loading data that must be in place before any loading functions are called + init_mapdata(); + // all of the applicable types that can be loaded, along with their loading functions // Add to this as needed with new StaticFunctionAccessors or new ClassFunctionAccessors for new applicable types // Static Function Access @@ -239,6 +243,7 @@ void DynamicDataLoader::initialize() add( "json_flag", &json_flag::load_all ); add( "fault", &fault::load_fault ); add( "relic_procgen_data", &relic_procgen_data::load_relic_procgen_data ); + add( "effect_on_condition", &effect_on_conditions::load ); add( "field_type", &field_types::load ); add( "weather_type", &weather_types::load ); add( "ammo_effect", &ammo_effects::load ); @@ -525,7 +530,9 @@ void DynamicDataLoader::unload_data() construction_groups::reset(); dreams.clear(); emit::reset(); + enchantment::reset(); event_statistic::reset(); + effect_on_conditions::reset(); event_transformation::reset(); faction_template::reset(); fault::reset(); @@ -616,6 +623,7 @@ void DynamicDataLoader::finalize_loaded_data( loading_ui &ui ) { _( "Flags" ), &json_flag::finalize_all }, { _( "Body parts" ), &body_part_type::finalize_all }, { _( "Weather types" ), &weather_types::finalize_all }, + { _( "Effect on conditions" ), &effect_on_conditions::finalize_all }, { _( "Field types" ), &field_types::finalize_all }, { _( "Ammo effects" ), &ammo_effects::finalize_all }, { _( "Emissions" ), &emit::finalize }, @@ -700,6 +708,7 @@ void DynamicDataLoader::check_consistency( loading_ui &ui ) }, { _( "Vitamins" ), &vitamin::check_consistency }, { _( "Weather types" ), &weather_types::check_consistency }, + { _( "Effect on conditions" ), &effect_on_conditions::check_consistency }, { _( "Field types" ), &field_types::check_consistency }, { _( "Ammo effects" ), &ammo_effects::check_consistency }, { _( "Emissions" ), &emit::check_consistency }, @@ -730,6 +739,7 @@ void DynamicDataLoader::check_consistency( loading_ui &ui ) { _( "Martial arts" ), &check_martialarts }, { _( "Mutations" ), &mutation_branch::check_consistency }, { _( "Mutation Categories" ), &mutation_category_trait::check_consistency }, + { _( "Region settings" ), check_region_settings }, { _( "Overmap land use codes" ), &overmap_land_use_codes::check_consistency }, { _( "Overmap connections" ), &overmap_connections::check_consistency }, { _( "Overmap terrain" ), &overmap_terrains::check_consistency }, diff --git a/src/inventory.cpp b/src/inventory.cpp index 9673c463af1ca..98868d7e04331 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -449,6 +449,7 @@ void inventory::form_from_zone( map &m, std::unordered_set &zone_pts, bool assign_invlet ) { std::vector pts; + pts.reserve( zone_pts.size() ); for( const tripoint &elem : zone_pts ) { pts.push_back( m.getlocal( elem ) ); } diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index 1f712504b272b..a0de6ee2ca11b 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -1044,8 +1044,8 @@ void inventory_column::draw( const catacurses::window &win, const point &p, const int hx_max = p.x + get_width() + contained_offset; inclusive_rectangle rect = inclusive_rectangle( point( x1, yy ), point( hx_max - 1, yy ) ); - rect_entry_map.push_back( std::pair, inventory_entry *>( rect, - &entry ) ); + rect_entry_map.emplace_back( rect, + &entry ); if( selected && visible_cells() > 1 ) { for( int hx = x1; hx < hx_max; ++hx ) { diff --git a/src/item.cpp b/src/item.cpp index 67d8ac5162046..e87a17f2f06d6 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -95,6 +95,7 @@ #include "string_id_utils.h" #include "text_snippets.h" #include "translations.h" +#include "try_parse_integer.h" #include "units.h" #include "units_fwd.h" #include "units_utility.h" @@ -161,7 +162,6 @@ static const bionic_id bio_digestion( "bio_digestion" ); static const trait_id trait_CARNIVORE( "CARNIVORE" ); static const trait_id trait_JITTERY( "JITTERY" ); static const trait_id trait_LIGHTWEIGHT( "LIGHTWEIGHT" ); -static const trait_id trait_SAPROVORE( "SAPROVORE" ); static const trait_id trait_TOLERANCE( "TOLERANCE" ); static const trait_id trait_WOOLALLERGY( "WOOLALLERGY" ); @@ -265,6 +265,7 @@ item::item() : bday( calendar::start_of_cataclysm ) type = nullitem(); charges = 0; contents = item_contents( type->pockets ); + select_gun_variant(); } item::item( const itype *type, time_point turn, int qty ) : type( type ), bday( turn ) @@ -290,6 +291,7 @@ item::item( const itype *type, time_point turn, int qty ) : type( type ), bday( set_var( "NANOFAB_ITEM_ID", nanofab_recipe.str() ); } + select_gun_variant(); if( type->gun ) { for( const itype_id &mod : type->gun->built_in_mods ) { item it( mod, turn, qty ); @@ -435,10 +437,10 @@ item::item( const recipe *rec, item &component ) } item::item( const item & ) = default; -item::item( item && ) = default; +item::item( item && ) noexcept( map_is_noexcept ) = default; item::~item() = default; item &item::operator=( const item & ) = default; -item &item::operator=( item && ) = default; +item &item::operator=( item && ) noexcept( list_is_noexcept ) = default; item item::make_corpse( const mtype_id &mt, time_point turn, const std::string &name, const int upgrade_time ) @@ -734,6 +736,38 @@ bool item::covers( const bodypart_id &bp ) const return does_cover; } +cata::optional item::covers_overlaps( const item &rhs ) const +{ + if( get_layer() != rhs.get_layer() ) { + return cata::nullopt; + } + const islot_armor *armor = find_armor_data(); + if( armor == nullptr ) { + return cata::nullopt; + } + const islot_armor *rhs_armor = rhs.find_armor_data(); + if( rhs_armor == nullptr ) { + return cata::nullopt; + } + body_part_set this_covers; + for( const armor_portion_data &data : armor->data ) { + if( data.covers.has_value() ) { + this_covers.unify_set( *data.covers ); + } + } + body_part_set rhs_covers; + for( const armor_portion_data &data : rhs_armor->data ) { + if( data.covers.has_value() ) { + rhs_covers.unify_set( *data.covers ); + } + } + if( this_covers.intersect_set( rhs_covers ).any() ) { + return rhs.get_side(); + } else { + return cata::nullopt; + } +} + body_part_set item::get_covered_body_parts() const { return get_covered_body_parts( get_side() ); @@ -1014,6 +1048,11 @@ bool item::stacks_with( const item &rhs, bool check_components, bool combine_liq if( is_relic() && rhs.is_relic() && !( *relic_data == *rhs.relic_data ) ) { return false; } + if( has_gun_variant() != rhs.has_gun_variant() || + ( has_gun_variant() && rhs.has_gun_variant() && + gun_variant().id != rhs.gun_variant().id ) ) { + return false; + } if( charges != 0 && rhs.charges != 0 && is_money() ) { // Dealing with nonempty cash cards return true; @@ -1178,7 +1217,19 @@ double item::get_var( const std::string &name, const double default_value ) cons if( it == item_vars.end() ) { return default_value; } - return atof( it->second.c_str() ); + const std::string &val = it->second; + char *end; + errno = 0; + double result = strtod( &val[0], &end ); + if( errno != 0 ) { + debugmsg( "Error parsing floating point value from %s in item::get_var: %s", + val, strerror( errno ) ); + return default_value; + } + if( end != &val[0] + val.size() ) { + debugmsg( "Stray characters at end of floating point value %s in item::get_var", val ); + } + return result; } void item::set_var( const std::string &name, const tripoint &value ) @@ -1193,9 +1244,19 @@ tripoint item::get_var( const std::string &name, const tripoint &default_value ) return default_value; } std::vector values = string_split( it->second, ',' ); - return tripoint( atoi( values[0].c_str() ), - atoi( values[1].c_str() ), - atoi( values[2].c_str() ) ); + cata_assert( values.size() == 3 ); + auto convert_or_error = []( const std::string & s ) { + ret_val result = try_parse_integer( s, false ); + if( result.success() ) { + return result.value(); + } else { + debugmsg( "Error parsing tripoint coordinate in item::get_var: %s", result.str() ); + return 0; + } + }; + return tripoint( convert_or_error( values[0] ), + convert_or_error( values[1] ), + convert_or_error( values[2] ) ); } void item::set_var( const std::string &name, const std::string &value ) @@ -1477,7 +1538,7 @@ void item::validate_ownership() const static void insert_separation_line( std::vector &info ) { if( info.empty() || info.back().sName != "--" ) { - info.push_back( iteminfo( "DESCRIPTION", "--" ) ); + info.emplace_back( "DESCRIPTION", "--" ); } } @@ -1498,7 +1559,7 @@ static const double hits_by_accuracy[41] = { 9993, 9997, 9998, 9999, 10000 // 16 to 20 }; -double item::effective_dps( const Character &guy, monster &mon ) const +double item::effective_dps( const Character &guy, Creature &mon ) const { const float mon_dodge = mon.get_dodge(); float base_hit = guy.get_dex() / 4.0f + guy.get_hit_weapon( *this ); @@ -1533,19 +1594,19 @@ double item::effective_dps( const Character &guy, monster &mon ) const // sum average damage past armor and return the number of moves required to achieve // that damage const auto calc_effective_damage = [ &, moves_per_attack]( const double num_strikes, - const bool crit, const Character & guy, monster & mon ) { - monster temp_mon = mon; + const bool crit, const Character & guy, Creature & mon ) { + Creature *temp_mon = &mon; double subtotal_damage = 0; damage_instance base_damage; guy.roll_all_damage( crit, base_damage, true, *this ); damage_instance dealt_damage = base_damage; - temp_mon.absorb_hit( bodypart_id( "torso" ), dealt_damage ); + temp_mon->absorb_hit( bodypart_id( "torso" ), dealt_damage ); dealt_damage_instance dealt_dams; for( const damage_unit &dmg_unit : dealt_damage.damage_units ) { int cur_damage = 0; int total_pain = 0; - temp_mon.deal_damage_handle_type( effect_source::empty(), dmg_unit, bodypart_id( "torso" ), - cur_damage, total_pain ); + temp_mon->deal_damage_handle_type( effect_source::empty(), dmg_unit, bodypart_id( "torso" ), + cur_damage, total_pain ); if( cur_damage > 0 ) { dealt_dams.dealt_dams[ static_cast( dmg_unit.type )] += cur_damage; } @@ -1555,20 +1616,20 @@ double item::effective_dps( const Character &guy, monster &mon ) const double subtotal_moves = moves_per_attack * num_strikes; if( has_technique( RAPID ) ) { - monster temp_rs_mon = mon; + Creature *temp_rs_mon = &mon; damage_instance rs_base_damage; guy.roll_all_damage( crit, rs_base_damage, true, *this ); damage_instance dealt_rs_damage = rs_base_damage; for( damage_unit &dmg_unit : dealt_rs_damage.damage_units ) { dmg_unit.damage_multiplier *= 0.66; } - temp_rs_mon.absorb_hit( bodypart_id( "torso" ), dealt_rs_damage ); + temp_rs_mon->absorb_hit( bodypart_id( "torso" ), dealt_rs_damage ); dealt_damage_instance rs_dealt_dams; for( const damage_unit &dmg_unit : dealt_rs_damage.damage_units ) { int cur_damage = 0; int total_pain = 0; - temp_rs_mon.deal_damage_handle_type( effect_source::empty(), dmg_unit, bodypart_id( "torso" ), - cur_damage, total_pain ); + temp_rs_mon->deal_damage_handle_type( effect_source::empty(), dmg_unit, bodypart_id( "torso" ), + cur_damage, total_pain ); if( cur_damage > 0 ) { rs_dealt_dams.dealt_dams[ static_cast( dmg_unit.type ) ] += cur_damage; } @@ -1644,7 +1705,7 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, []( const material_type * material ) { return string_format( "%s", material->name() ); }, enumeration_conjunction::none ); - info.push_back( iteminfo( "BASE", string_format( _( "Material: %s" ), material_list ) ) ); + info.emplace_back( "BASE", string_format( _( "Material: %s" ), material_list ) ); } } if( parts->test( iteminfo_parts::BASE_VOLUME ) ) { @@ -1655,18 +1716,18 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, info.back().bNewLine = true; } if( parts->test( iteminfo_parts::BASE_LENGTH ) && length() > 0_mm ) { - info.push_back( iteminfo( "BASE", _( "Length: " ), - string_format( " %s", length_units( length() ) ), - iteminfo::lower_is_better, - convert_length( length() ) ) ); + info.emplace_back( "BASE", _( "Length: " ), + string_format( " %s", length_units( length() ) ), + iteminfo::lower_is_better, + convert_length( length() ) ); } if( parts->test( iteminfo_parts::BASE_OWNER ) && !owner.is_null() ) { - info.push_back( iteminfo( "BASE", string_format( _( "Owner: %s" ), - _( get_owner_name() ) ) ) ); + info.emplace_back( "BASE", string_format( _( "Owner: %s" ), + _( get_owner_name() ) ) ); } if( parts->test( iteminfo_parts::BASE_CATEGORY ) ) { - info.push_back( iteminfo( "BASE", _( "Category: " ), - "
" + get_category_shallow().name() + "
" ) ); + info.emplace_back( "BASE", _( "Category: " ), + "
" + get_category_shallow().name() + "
" ); } if( parts->test( iteminfo_parts::DESCRIPTION ) ) { @@ -1676,14 +1737,16 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, const cata::optional snippet = SNIPPET.get_snippet_by_id( snip_id ); if( snippet.has_value() ) { // Just use the dynamic description - info.push_back( iteminfo( "DESCRIPTION", snippet.value().translated() ) ); + info.emplace_back( "DESCRIPTION", snippet.value().translated() ); } else if( idescription != item_vars.end() ) { - info.push_back( iteminfo( "DESCRIPTION", idescription->second ) ); + info.emplace_back( "DESCRIPTION", idescription->second ); + } else if( has_gun_variant() ) { + info.emplace_back( "DESCRIPTION", gun_variant().alt_description.translated() ); } else { if( has_flag( flag_MAGIC_FOCUS ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "This item is a magical focus. " - "You can cast spells with it in your hand." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "This item is a magical focus. " + "You can cast spells with it in your hand." ) ); } if( is_craft() ) { const std::string desc = ( typeId() == itype_disassembly ) ? @@ -1691,11 +1754,11 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, "It is %d percent complete." ) : _( "This is an in progress %s. " "It is %d percent complete." ); const int percent_progress = item_counter / 100000; - info.push_back( iteminfo( "DESCRIPTION", string_format( desc, - craft_data_->making->result_name(), - percent_progress ) ) ); + info.emplace_back( "DESCRIPTION", string_format( desc, + craft_data_->making->result_name(), + percent_progress ) ); } else { - info.push_back( iteminfo( "DESCRIPTION", type->description.translated() ) ); + info.emplace_back( "DESCRIPTION", type->description.translated() ); } } insert_separation_line( info ); @@ -1729,13 +1792,13 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, } if( has_var( "contained_name" ) && parts->test( iteminfo_parts::BASE_CONTENTS ) ) { - info.push_back( iteminfo( "BASE", string_format( _( "Contains: %s" ), - get_var( "contained_name" ) ) ) ); + info.emplace_back( "BASE", string_format( _( "Contains: %s" ), + get_var( "contained_name" ) ) ); } if( count_by_charges() && !is_food() && !is_medication() && parts->test( iteminfo_parts::BASE_AMOUNT ) ) { - info.push_back( iteminfo( "BASE", _( "Amount: " ), "", iteminfo::no_flags, - charges * batch ) ); + info.emplace_back( "BASE", _( "Amount: " ), "", iteminfo::no_flags, + charges * batch ); } } @@ -1744,66 +1807,66 @@ void item::debug_info( std::vector &info, const iteminfo_query *parts, { if( debug && parts->test( iteminfo_parts::BASE_DEBUG ) ) { if( g != nullptr ) { - info.push_back( iteminfo( "BASE", string_format( "itype_id: %s", - typeId().str() ) ) ); + info.emplace_back( "BASE", string_format( "itype_id: %s", + typeId().str() ) ); if( !old_owner.is_null() ) { - info.push_back( iteminfo( "BASE", string_format( _( "Old owner: %s" ), - _( get_old_owner_name() ) ) ) ); - } - info.push_back( iteminfo( "BASE", _( "age (hours): " ), "", iteminfo::lower_is_better, - to_hours( age() ) ) ); - info.push_back( iteminfo( "BASE", _( "charges: " ), "", iteminfo::lower_is_better, - charges ) ); - info.push_back( iteminfo( "BASE", _( "damage: " ), "", iteminfo::lower_is_better, - damage_ ) ); - info.push_back( iteminfo( "BASE", _( "active: " ), "", iteminfo::lower_is_better, - active ) ); - info.push_back( iteminfo( "BASE", _( "burn: " ), "", iteminfo::lower_is_better, - burnt ) ); + info.emplace_back( "BASE", string_format( _( "Old owner: %s" ), + _( get_old_owner_name() ) ) ); + } + info.emplace_back( "BASE", _( "age (hours): " ), "", iteminfo::lower_is_better, + to_hours( age() ) ); + info.emplace_back( "BASE", _( "charges: " ), "", iteminfo::lower_is_better, + charges ); + info.emplace_back( "BASE", _( "damage: " ), "", iteminfo::lower_is_better, + damage_ ); + info.emplace_back( "BASE", _( "active: " ), "", iteminfo::lower_is_better, + active ); + info.emplace_back( "BASE", _( "burn: " ), "", iteminfo::lower_is_better, + burnt ); const std::string tags_listed = enumerate_as_string( item_tags, []( const flag_id & f ) { return f.str(); }, enumeration_conjunction::none ); - info.push_back( iteminfo( "BASE", string_format( _( "tags: %s" ), tags_listed ) ) ); + info.emplace_back( "BASE", string_format( _( "tags: %s" ), tags_listed ) ); for( auto const &imap : item_vars ) { - info.push_back( iteminfo( "BASE", - string_format( _( "item var: %s, %s" ), imap.first, - imap.second ) ) ); + info.emplace_back( "BASE", + string_format( _( "item var: %s, %s" ), imap.first, + imap.second ) ); } const std::string space = " "; if( goes_bad() ) { - info.push_back( iteminfo( "BASE", _( "age (turns): " ), - "", iteminfo::lower_is_better, - to_turns( age() ) ) ); - info.push_back( iteminfo( "BASE", _( "rot (turns): " ), - "", iteminfo::lower_is_better, - to_turns( rot ) ) ); - info.push_back( iteminfo( "BASE", space + _( "max rot (turns): " ), - "", iteminfo::lower_is_better, - to_turns( get_shelf_life() ) ) ); + info.emplace_back( "BASE", _( "age (turns): " ), + "", iteminfo::lower_is_better, + to_turns( age() ) ); + info.emplace_back( "BASE", _( "rot (turns): " ), + "", iteminfo::lower_is_better, + to_turns( rot ) ); + info.emplace_back( "BASE", space + _( "max rot (turns): " ), + "", iteminfo::lower_is_better, + to_turns( get_shelf_life() ) ); } if( has_temperature() ) { - info.push_back( iteminfo( "BASE", _( "last temp: " ), - "", iteminfo::lower_is_better, - to_turn( last_temp_check ) ) ); - info.push_back( iteminfo( "BASE", _( "Temp: " ), "", iteminfo::lower_is_better, - temperature ) ); - info.push_back( iteminfo( "BASE", _( "Spec ener: " ), "", - iteminfo::lower_is_better, - specific_energy ) ); - info.push_back( iteminfo( "BASE", _( "Spec heat lq: " ), "", - iteminfo::lower_is_better | iteminfo::is_decimal, - get_specific_heat_liquid() ) ); - info.push_back( iteminfo( "BASE", _( "Spec heat sld: " ), "", - iteminfo::lower_is_better | iteminfo::is_decimal, - get_specific_heat_solid() ) ); - info.push_back( iteminfo( "BASE", _( "latent heat: " ), "", - iteminfo::lower_is_better, - get_latent_heat() ) ); - info.push_back( iteminfo( "BASE", _( "Freeze point: " ), "", - iteminfo::lower_is_better, - get_freeze_point() ) ); + info.emplace_back( "BASE", _( "last temp: " ), + "", iteminfo::lower_is_better, + to_turn( last_temp_check ) ); + info.emplace_back( "BASE", _( "Temp: " ), "", iteminfo::lower_is_better, + temperature ); + info.emplace_back( "BASE", _( "Spec ener: " ), "", + iteminfo::lower_is_better, + specific_energy ); + info.emplace_back( "BASE", _( "Spec heat lq: " ), "", + iteminfo::lower_is_better | iteminfo::is_decimal, + get_specific_heat_liquid() ); + info.emplace_back( "BASE", _( "Spec heat sld: " ), "", + iteminfo::lower_is_better | iteminfo::is_decimal, + get_specific_heat_solid() ); + info.emplace_back( "BASE", _( "latent heat: " ), "", + iteminfo::lower_is_better, + get_latent_heat() ); + info.emplace_back( "BASE", _( "Freeze point: " ), "", + iteminfo::lower_is_better | iteminfo::is_decimal, + get_freeze_point() ); } } } @@ -1814,29 +1877,29 @@ void item::med_info( const item *med_item, std::vector &info, const it { const cata::value_ptr &med_com = med_item->get_comestible(); if( med_com->quench != 0 && parts->test( iteminfo_parts::MED_QUENCH ) ) { - info.push_back( iteminfo( "MED", _( "Quench: " ), med_com->quench ) ); + info.emplace_back( "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: " ), - player_character.fun_for( *med_item ).first ) ); + info.emplace_back( "MED", _( "Enjoyability: " ), + player_character.fun_for( *med_item ).first ); } if( med_com->stim != 0 && parts->test( iteminfo_parts::MED_STIMULATION ) ) { std::string name = string_format( "%s %s", _( "Stimulation:" ), med_com->stim > 0 ? _( "Upper" ) : _( "Downer" ) ); - info.push_back( iteminfo( "MED", name ) ); + info.emplace_back( "MED", name ); } if( parts->test( iteminfo_parts::MED_PORTIONS ) ) { - info.push_back( iteminfo( "MED", _( "Portions: " ), - std::abs( static_cast( med_item->charges ) * batch ) ) ); + info.emplace_back( "MED", _( "Portions: " ), + std::abs( static_cast( med_item->charges ) * batch ) ); } if( parts->test( iteminfo_parts::MED_CONSUME_TIME ) ) { - info.push_back( iteminfo( "MED", _( "Consume time: " ), - to_string( player_character.get_consume_time( *med_item ) ) ) ); + info.emplace_back( "MED", _( "Consume time: " ), + to_string( player_character.get_consume_time( *med_item ) ) ); } if( med_com->addict && parts->test( iteminfo_parts::DESCRIPTION_MED_ADDICTING ) ) { @@ -1873,51 +1936,51 @@ void item::food_info( const item *food_item, std::vector &info, if( max_nutr.kcal() != 0 || food_item->get_comestible()->quench != 0 ) { if( parts->test( iteminfo_parts::FOOD_NUTRITION ) ) { - info.push_back( iteminfo( "FOOD", _( "Calories (kcal): " ), - "", iteminfo::no_newline, min_nutr.kcal() ) ); + info.emplace_back( "FOOD", _( "Calories (kcal): " ), + "", iteminfo::no_newline, min_nutr.kcal() ); if( max_nutr.kcal() != min_nutr.kcal() ) { - info.push_back( iteminfo( "FOOD", _( "-" ), - "", iteminfo::no_newline, max_nutr.kcal() ) ); + info.emplace_back( "FOOD", _( "-" ), + "", iteminfo::no_newline, max_nutr.kcal() ); } } if( parts->test( iteminfo_parts::FOOD_QUENCH ) ) { const std::string space = " "; - info.push_back( iteminfo( "FOOD", space + _( "Quench: " ), - food_item->get_comestible()->quench ) ); + info.emplace_back( "FOOD", space + _( "Quench: " ), + food_item->get_comestible()->quench ); } if( parts->test( iteminfo_parts::FOOD_SATIATION ) ) { if( max_nutr.kcal() == min_nutr.kcal() ) { - info.push_back( iteminfo( "FOOD", _( "Satiety: " ), - satiety_bar( player_character.compute_calories_per_effective_volume( *food_item ) ) ) ); + info.emplace_back( "FOOD", _( "Satiety: " ), + satiety_bar( player_character.compute_calories_per_effective_volume( *food_item ) ) ); } else { - info.push_back( iteminfo( "FOOD", _( "Satiety: " ), - satiety_bar( player_character.compute_calories_per_effective_volume( *food_item, &min_nutr ) ), - iteminfo::no_newline - ) ); - info.push_back( iteminfo( "FOOD", _( " - " ), - satiety_bar( player_character.compute_calories_per_effective_volume( *food_item, &max_nutr ) ) ) ); + info.emplace_back( "FOOD", _( "Satiety: " ), + satiety_bar( player_character.compute_calories_per_effective_volume( *food_item, &min_nutr ) ), + iteminfo::no_newline + ); + info.emplace_back( "FOOD", _( " - " ), + satiety_bar( player_character.compute_calories_per_effective_volume( *food_item, &max_nutr ) ) ); } } } 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 ) ); + info.emplace_back( "FOOD", _( "Enjoyability: " ), fun_for_food_item.first ); } if( parts->test( iteminfo_parts::FOOD_PORTIONS ) ) { - info.push_back( iteminfo( "FOOD", _( "Portions: " ), - std::abs( static_cast( food_item->charges ) * batch ) ) ); + info.emplace_back( "FOOD", _( "Portions: " ), + std::abs( static_cast( food_item->charges ) * batch ) ); } if( food_item->corpse != nullptr && parts->test( iteminfo_parts::FOOD_SMELL ) && ( debug || ( g != nullptr && player_character.has_trait( trait_CARNIVORE ) ) ) ) { - info.push_back( iteminfo( "FOOD", _( "Smells like: " ) + food_item->corpse->nname() ) ); + info.emplace_back( "FOOD", _( "Smells like: " ) + food_item->corpse->nname() ); } if( parts->test( iteminfo_parts::FOOD_CONSUME_TIME ) ) { - info.push_back( iteminfo( "FOOD", _( "Consume time: " ), - to_string( player_character.get_consume_time( *food_item ) ) ) ); + info.emplace_back( "FOOD", _( "Consume time: " ), + to_string( player_character.get_consume_time( *food_item ) ) ); } auto format_vitamin = [&]( const std::pair &v, bool display_vitamins ) { @@ -2031,19 +2094,19 @@ void item::food_info( const item *food_item, std::vector &info, } if( food_item->rotten() ) { 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." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "This food has started to rot, " + "but your bionic digestion can tolerate " + "it." ) ); } else if( player_character.has_flag( json_flag_IMMUNE_SPOIL ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "This food has started to rot, " - "but you can tolerate it." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "This food has started to rot, " + "but you can tolerate it." ) ); } else { - info.push_back( iteminfo( "DESCRIPTION", - _( "This food has started to rot. " - "Eating it would be a very bad " - "idea." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "This food has started to rot. " + "Eating it would be a very bad " + "idea." ) ); } } } @@ -2139,9 +2202,15 @@ void item::ammo_info( std::vector &info, const iteminfo_query *parts, } if( parts->test( iteminfo_parts::AMMO_FX_RECOVER ) ) { for( const std::string &effect : ammo.ammo_effects ) { - if( effect.compare( 0, 8, "RECOVER_" ) == 0 ) { - int recover_chance; - sscanf( effect.c_str(), "RECOVER_%i", &recover_chance ); + if( string_starts_with( effect, "RECOVER_" ) ) { + ret_val try_recover_chance = + try_parse_integer( effect.substr( 8 ), false ); + if( !try_recover_chance.success() ) { + debugmsg( "Error parsing ammo RECOVER_ denominator: %s", + try_recover_chance.str() ); + break; + } + int recover_chance = try_recover_chance.value(); if( recover_chance <= 5 ) { fx.emplace_back( _( "Stands a very low chance of remaining intact once fired." ) ); } else if( recover_chance <= 10 ) { @@ -2230,8 +2299,8 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf if( parts->test( iteminfo_parts::GUN_DAMAGE ) ) { insert_separation_line( info ); - info.push_back( iteminfo( "GUN", _( "Ranged damage: " ), "", iteminfo::no_newline, - mod->gun_damage( false ).total_damage() ) ); + info.emplace_back( "GUN", _( "Ranged damage: " ), "", iteminfo::no_newline, + mod->gun_damage( false ).total_damage() ); } if( mod->ammo_required() ) { @@ -2242,36 +2311,36 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf } if( dmg_mult != 1.0f ) { if( parts->test( iteminfo_parts::GUN_DAMAGE_AMMOPROP ) ) { - info.push_back( iteminfo( "GUN", "ammo_mult", "*", - iteminfo::no_newline | iteminfo::no_name | iteminfo::is_decimal, dmg_mult ) ); + info.emplace_back( "GUN", "ammo_mult", "*", + iteminfo::no_newline | iteminfo::no_name | iteminfo::is_decimal, dmg_mult ); } } else { if( parts->test( iteminfo_parts::GUN_DAMAGE_LOADEDAMMO ) ) { damage_instance ammo_dam = curammo->ammo->damage; - info.push_back( iteminfo( "GUN", "ammo_damage", "", - iteminfo::no_newline | iteminfo::no_name | - iteminfo::show_plus, ammo_dam.total_damage() ) ); + info.emplace_back( "GUN", "ammo_damage", "", + iteminfo::no_newline | iteminfo::no_name | + iteminfo::show_plus, ammo_dam.total_damage() ); } } if( damage_level() > 0 ) { int dmg_penalty = damage_level() * -2; - info.push_back( iteminfo( "GUN", "damaged_weapon_penalty", "", - iteminfo::no_newline | iteminfo::no_name, dmg_penalty ) ); + info.emplace_back( "GUN", "damaged_weapon_penalty", "", + iteminfo::no_newline | iteminfo::no_name, dmg_penalty ); } if( parts->test( iteminfo_parts::GUN_DAMAGE_TOTAL ) ) { - info.push_back( iteminfo( "GUN", "sum_of_damage", _( " = " ), - iteminfo::no_newline | iteminfo::no_name, - loaded_mod->gun_damage( true ).total_damage() ) ); + info.emplace_back( "GUN", "sum_of_damage", _( " = " ), + iteminfo::no_newline | iteminfo::no_name, + loaded_mod->gun_damage( true ).total_damage() ); } } info.back().bNewLine = true; if( mod->ammo_required() && curammo->ammo->critical_multiplier != 1.0 ) { if( parts->test( iteminfo_parts::AMMO_DAMAGE_CRIT_MULTIPLIER ) ) { - info.push_back( iteminfo( "GUN", _( "Critical multiplier: " ), "", - iteminfo::no_flags, curammo->ammo->critical_multiplier ) ); + info.emplace_back( "GUN", _( "Critical multiplier: " ), "", + iteminfo::no_flags, curammo->ammo->critical_multiplier ); } } @@ -2285,43 +2354,43 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf // TODO: This doesn't cover multiple damage types if( parts->test( iteminfo_parts::GUN_ARMORPIERCE ) ) { - info.push_back( iteminfo( "GUN", _( "Armor-pierce: " ), "", - iteminfo::no_newline, get_ranged_pierce( gun ) ) ); + info.emplace_back( "GUN", _( "Armor-pierce: " ), "", + iteminfo::no_newline, get_ranged_pierce( gun ) ); } if( mod->ammo_required() ) { int ammo_pierce = get_ranged_pierce( *curammo->ammo ); // ammo_armor_pierce and sum_of_armor_pierce don't need to translate. if( parts->test( iteminfo_parts::GUN_ARMORPIERCE_LOADEDAMMO ) ) { - info.push_back( iteminfo( "GUN", "ammo_armor_pierce", "", - iteminfo::no_newline | iteminfo::no_name | - iteminfo::show_plus, ammo_pierce ) ); + info.emplace_back( "GUN", "ammo_armor_pierce", "", + iteminfo::no_newline | iteminfo::no_name | + iteminfo::show_plus, ammo_pierce ); } if( parts->test( iteminfo_parts::GUN_ARMORPIERCE_TOTAL ) ) { - info.push_back( iteminfo( "GUN", "sum_of_armor_pierce", _( " = " ), - iteminfo::no_name, - get_ranged_pierce( gun ) + ammo_pierce ) ); + info.emplace_back( "GUN", "sum_of_armor_pierce", _( " = " ), + iteminfo::no_name, + get_ranged_pierce( gun ) + ammo_pierce ); } } info.back().bNewLine = true; if( parts->test( iteminfo_parts::GUN_DISPERSION ) ) { - info.push_back( iteminfo( "GUN", _( "Dispersion: " ), "", - iteminfo::no_newline | iteminfo::lower_is_better, - mod->gun_dispersion( false, false ) ) ); + info.emplace_back( "GUN", _( "Dispersion: " ), "", + iteminfo::no_newline | iteminfo::lower_is_better, + mod->gun_dispersion( false, false ) ); } if( mod->ammo_required() ) { int ammo_dispersion = curammo->ammo->dispersion; // ammo_dispersion and sum_of_dispersion don't need to translate. if( parts->test( iteminfo_parts::GUN_DISPERSION_LOADEDAMMO ) ) { - info.push_back( iteminfo( "GUN", "ammo_dispersion", "", - iteminfo::no_newline | iteminfo::lower_is_better | - iteminfo::no_name | iteminfo::show_plus, - ammo_dispersion ) ); + info.emplace_back( "GUN", "ammo_dispersion", "", + iteminfo::no_newline | iteminfo::lower_is_better | + iteminfo::no_name | iteminfo::show_plus, + ammo_dispersion ); } if( parts->test( iteminfo_parts::GUN_DISPERSION_TOTAL ) ) { - info.push_back( iteminfo( "GUN", "sum_of_dispersion", _( " = " ), - iteminfo::lower_is_better | iteminfo::no_name, - loaded_mod->gun_dispersion( true, false ) ) ); + info.emplace_back( "GUN", "sum_of_dispersion", _( " = " ), + iteminfo::lower_is_better | iteminfo::no_name, + loaded_mod->gun_dispersion( true, false ) ); } } info.back().bNewLine = true; @@ -2332,17 +2401,17 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf int adj_disp = eff_disp - act_disp; if( parts->test( iteminfo_parts::GUN_DISPERSION_SIGHT ) ) { - info.push_back( iteminfo( "GUN", _( "Sight dispersion: " ), "", - iteminfo::no_newline | iteminfo::lower_is_better, - act_disp ) ); + info.emplace_back( "GUN", _( "Sight dispersion: " ), "", + iteminfo::no_newline | iteminfo::lower_is_better, + act_disp ); if( adj_disp ) { - info.push_back( iteminfo( "GUN", "sight_adj_disp", "", - iteminfo::no_newline | iteminfo::lower_is_better | - iteminfo::no_name | iteminfo::show_plus, adj_disp ) ); - info.push_back( iteminfo( "GUN", "sight_eff_disp", _( " = " ), - iteminfo::lower_is_better | iteminfo::no_name, - eff_disp ) ); + info.emplace_back( "GUN", "sight_adj_disp", "", + iteminfo::no_newline | iteminfo::lower_is_better | + iteminfo::no_name | iteminfo::show_plus, adj_disp ); + info.emplace_back( "GUN", "sight_eff_disp", _( " = " ), + iteminfo::lower_is_better | iteminfo::no_name, + eff_disp ); } } @@ -2379,8 +2448,8 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf } if( parts->test( iteminfo_parts::GUN_USEDSKILL ) ) { - info.push_back( iteminfo( "GUN", _( "Skill used: " ), - "" + skill.name() + "" ) ); + info.emplace_back( "GUN", _( "Skill used: " ), + "" + skill.name() + "" ); } if( mod->magazine_integral() || mod->magazine_current() ) { @@ -2505,7 +2574,7 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf iternum++; } mod_str += "."; - info.push_back( iteminfo( "DESCRIPTION", mod_str ) ); + info.emplace_back( "DESCRIPTION", mod_str ); } if( mod->casings_count() && parts->test( iteminfo_parts::DESCRIPTION_GUN_CASINGS ) ) { @@ -2517,8 +2586,8 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf if( is_gun() && has_flag( flag_FIRE_TWOHAND ) && parts->test( iteminfo_parts::DESCRIPTION_TWOHANDED ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "This weapon needs two free hands to fire." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "This weapon needs two free hands to fire." ) ); } } @@ -2531,52 +2600,52 @@ void item::gunmod_info( std::vector &info, const iteminfo_query *parts const islot_gunmod &mod = *type->gunmod; if( is_gun() && parts->test( iteminfo_parts::DESCRIPTION_GUNMOD ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "This mod must be attached to a gun, " - "it can not be fired separately." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "This mod must be attached to a gun, " + "it can not be fired separately." ) ); } if( has_flag( flag_REACH_ATTACK ) && parts->test( iteminfo_parts::DESCRIPTION_GUNMOD_REACH ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "When attached to a gun, allows making " - "reach melee attacks with it." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "When attached to a gun, allows making " + "reach melee attacks with it." ) ); } if( is_gunmod() && has_flag( flag_DISABLE_SIGHTS ) && parts->test( iteminfo_parts::DESCRIPTION_GUNMOD_DISABLESSIGHTS ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "This mod obscures sights of the " - "base weapon." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "This mod obscures sights of the " + "base weapon." ) ); } if( is_gunmod() && has_flag( flag_CONSUMABLE ) && parts->test( iteminfo_parts::DESCRIPTION_GUNMOD_CONSUMABLE ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "This mod might suffer wear when firing " - "the base weapon." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "This mod might suffer wear when firing " + "the base weapon." ) ); } if( mod.dispersion != 0 && parts->test( iteminfo_parts::GUNMOD_DISPERSION ) ) { - info.push_back( iteminfo( "GUNMOD", _( "Dispersion modifier: " ), "", - iteminfo::lower_is_better | iteminfo::show_plus, - mod.dispersion ) ); + info.emplace_back( "GUNMOD", _( "Dispersion modifier: " ), "", + iteminfo::lower_is_better | iteminfo::show_plus, + mod.dispersion ); } if( mod.sight_dispersion != -1 && parts->test( iteminfo_parts::GUNMOD_DISPERSION_SIGHT ) ) { - info.push_back( iteminfo( "GUNMOD", _( "Sight dispersion: " ), "", - iteminfo::lower_is_better, mod.sight_dispersion ) ); + info.emplace_back( "GUNMOD", _( "Sight dispersion: " ), "", + iteminfo::lower_is_better, mod.sight_dispersion ); } if( mod.aim_speed >= 0 && parts->test( iteminfo_parts::GUNMOD_AIMSPEED ) ) { - info.push_back( iteminfo( "GUNMOD", _( "Aim speed: " ), "", - iteminfo::no_flags, mod.aim_speed ) ); + info.emplace_back( "GUNMOD", _( "Aim speed: " ), "", + iteminfo::lower_is_better, mod.aim_speed ); } int total_damage = static_cast( mod.damage.total_damage() ); if( total_damage != 0 && parts->test( iteminfo_parts::GUNMOD_DAMAGE ) ) { - info.push_back( iteminfo( "GUNMOD", _( "Damage: " ), "", iteminfo::show_plus, - total_damage ) ); + info.emplace_back( "GUNMOD", _( "Damage: " ), "", iteminfo::show_plus, + total_damage ); } int pierce = get_ranged_pierce( mod ); if( get_ranged_pierce( mod ) != 0 && parts->test( iteminfo_parts::GUNMOD_ARMORPIERCE ) ) { - info.push_back( iteminfo( "GUNMOD", _( "Armor-pierce: " ), "", iteminfo::show_plus, - pierce ) ); + info.emplace_back( "GUNMOD", _( "Armor-pierce: " ), "", iteminfo::show_plus, + pierce ); } if( mod.handling != 0 && parts->test( iteminfo_parts::GUNMOD_HANDLING ) ) { info.emplace_back( "GUNMOD", _( "Handling modifier: " ), "", @@ -2584,8 +2653,8 @@ void item::gunmod_info( std::vector &info, const iteminfo_query *parts } if( !type->mod->ammo_modifier.empty() && parts->test( iteminfo_parts::GUNMOD_AMMO ) ) { for( const ammotype &at : type->mod->ammo_modifier ) { - info.push_back( iteminfo( "GUNMOD", string_format( _( "Ammo: %s" ), - at->name() ) ) ); + info.emplace_back( "GUNMOD", string_format( _( "Ammo: %s" ), + at->name() ) ); } } if( mod.reload_modifier != 0 && parts->test( iteminfo_parts::GUNMOD_RELOAD ) ) { @@ -2593,8 +2662,8 @@ void item::gunmod_info( std::vector &info, const iteminfo_query *parts iteminfo::lower_is_better, mod.reload_modifier ); } if( mod.min_str_required_mod > 0 && parts->test( iteminfo_parts::GUNMOD_STRENGTH ) ) { - info.push_back( iteminfo( "GUNMOD", _( "Minimum strength required modifier: " ), - mod.min_str_required_mod ) ); + info.emplace_back( "GUNMOD", _( "Minimum strength required modifier: " ), + mod.min_str_required_mod ); } if( !mod.add_mod.empty() && parts->test( iteminfo_parts::GUNMOD_ADD_MOD ) ) { insert_separation_line( info ); @@ -2612,7 +2681,7 @@ void item::gunmod_info( std::vector &info, const iteminfo_query *parts iternum++; } mod_loc_str += "."; - info.push_back( iteminfo( "GUNMOD", mod_loc_str ) ); + info.emplace_back( "GUNMOD", mod_loc_str ); } insert_separation_line( info ); @@ -2622,12 +2691,12 @@ void item::gunmod_info( std::vector &info, const iteminfo_query *parts enumerate_as_string( mod.usable.begin(), mod.usable.end(), []( const gun_type_type & used_on ) { return string_format( "%s", used_on.name() ); } ); - info.push_back( iteminfo( "GUNMOD", used_on_str ) ); + info.emplace_back( "GUNMOD", used_on_str ); } if( parts->test( iteminfo_parts::GUNMOD_LOCATION ) ) { - info.push_back( iteminfo( "GUNMOD", string_format( _( "Location: %s" ), - mod.location.name() ) ) ); + info.emplace_back( "GUNMOD", string_format( _( "Location: %s" ), + mod.location.name() ) ); } if( !mod.blacklist_mod.empty() && parts->test( iteminfo_parts::GUNMOD_BLACKLIST_MOD ) ) { @@ -2642,7 +2711,122 @@ void item::gunmod_info( std::vector &info, const iteminfo_query *parts iternum++; } mod_black_str += "."; - info.push_back( iteminfo( "GUNMOD", mod_black_str ) ); + info.emplace_back( "GUNMOD", mod_black_str ); + } +} + +void item::armor_encumbrance_info( std::vector &info, int reduce_encumbrance_by ) const +{ + std::string format; + const std::string space = " "; + Character &player_character = get_player_character(); + const bool sizing_matters = get_sizing( player_character ) != sizing::ignore; + + if( has_flag( flag_FIT ) ) { + format = _( " (fits)" ); + } else if( has_flag( flag_VARSIZE ) && sizing_matters ) { + format = _( " (poor fit)" ); + } + if( sizing_matters ) { + const sizing sizing_level = get_sizing( player_character ); + //If we have the wrong size, we do not fit so alert the player + if( sizing_level == sizing::human_sized_small_char ) { + format = _( " (too big)" ); + } else if( sizing_level == sizing::big_sized_small_char ) { + format = _( " (huge!)" ); + } else if( sizing_level == sizing::small_sized_human_char || + sizing_level == sizing::human_sized_big_char ) { + format = _( " (too small)" ); + } else if( sizing_level == sizing::small_sized_big_char ) { + format = _( " (tiny!)" ); + } + } + + info.emplace_back( "ARMOR", _( "Encumbrance:" ) + format ); + + if( const islot_armor *t = find_armor_data() ) { + if( !t->data.empty() ) { + struct armor_portion_type { + int encumber = 0; + int max_encumber = 0; + int coverage = 0; + + bool operator==( const armor_portion_type &other ) { + return encumber == other.encumber + && max_encumber == other.max_encumber + && coverage == other.coverage; + } + }; + struct body_part_display_info { + translation to_display; + armor_portion_type portion; + bool active = false; + }; + + std::map to_display_data; + + for( const armor_portion_data &piece : t->data ) { + if( piece.covers.has_value() ) { + for( const bodypart_str_id &covering_id : piece.covers.value() ) { + if( covering_id != bodypart_str_id::NULL_ID() ) { + to_display_data[covering_id] = { covering_id.obj().name_as_heading, { + std::max( 0, get_encumber( player_character, covering_id ) - reduce_encumbrance_by ), + std::max( 0, get_encumber( player_character, covering_id, encumber_flags::assume_full ) - reduce_encumbrance_by ), + piece.coverage + }, true + }; + } + } + } + } + // Handle things that use both sides to avoid showing L. Arm R. Arm etc when both are the same + if( !t->sided ) { + for( const armor_portion_data &piece : t->data ) { + for( const bodypart_str_id &bp : *piece.covers ) { + bodypart_str_id opposite = bp->opposite_part; + if( opposite != bp && covers( bp ) && covers( opposite ) + && to_display_data.at( bp ).portion == to_display_data.at( opposite ).portion + && to_display_data.at( opposite ).active ) { + to_display_data.at( opposite ).to_display = bp->name_as_heading_multiple; + to_display_data.at( bp ).active = false; + } + } + } + } + const std::string when_full_message = space + _( "When full:" ) + space; + const std::string coverage_message = space + _( "Coverage:" ) + space; + for( auto &piece : to_display_data ) { + if( t->sided ) { + const bodypart_str_id &covering_id = piece.first; + if( !covers( covering_id.id() ) ) { + continue; + } + } + if( piece.second.active ) { + info.emplace_back( "ARMOR", + string_format( _( "%s:" ), piece.second.to_display.translated() ) + space, "", + iteminfo::no_newline | iteminfo::lower_is_better, + piece.second.portion.encumber ); + + if( piece.second.portion.encumber != piece.second.portion.max_encumber ) { + info.emplace_back( "ARMOR", when_full_message, "", + iteminfo::no_newline | iteminfo::lower_is_better, + piece.second.portion.max_encumber ); + } + + info.emplace_back( "ARMOR", coverage_message, "", + iteminfo::no_flags, + piece.second.portion.coverage ); + } + } + } + } else if( is_gun() && has_flag( flag_IS_ARMOR ) ) { + //right now all eligible gunmods (shoulder_strap, belt_clip) have the is_armor flag and use the torso + info.emplace_back( "ARMOR", _( "Torso:" ) + space, "", + iteminfo::no_newline | iteminfo::lower_is_better, get_avg_encumber( get_avatar() ) ); + + info.emplace_back( "ARMOR", space + _( "Coverage:" ) + space, "", + iteminfo::no_flags, get_coverage( body_part_torso.id() ) ); } } @@ -2656,36 +2840,36 @@ void item::armor_protection_info( std::vector &info, const iteminfo_qu if( parts->test( iteminfo_parts::ARMOR_PROTECTION ) ) { const std::string space = " "; - info.push_back( iteminfo( "ARMOR", _( "Protection: Bash: " ), "", - iteminfo::no_newline | iteminfo::is_decimal, bash_resist() ) ); - info.push_back( iteminfo( "ARMOR", space + _( "Cut: " ), "", - iteminfo::no_newline | iteminfo::is_decimal, cut_resist() ) ); - info.push_back( iteminfo( "ARMOR", space + _( "Ballistic: " ), "", iteminfo::is_decimal, - bullet_resist() ) ); - info.push_back( iteminfo( "ARMOR", space + _( "Acid: " ), "", - iteminfo::no_newline | iteminfo::is_decimal, acid_resist() ) ); - info.push_back( iteminfo( "ARMOR", space + _( "Fire: " ), "", - iteminfo::no_newline | iteminfo::is_decimal, fire_resist() ) ); - info.push_back( iteminfo( "ARMOR", space + _( "Environmental: " ), - get_base_env_resist( *this ) ) ); + info.emplace_back( "ARMOR", _( "Protection: Bash: " ), "", + iteminfo::no_newline | iteminfo::is_decimal, bash_resist() ); + info.emplace_back( "ARMOR", space + _( "Cut: " ), "", + iteminfo::no_newline | iteminfo::is_decimal, cut_resist() ); + info.emplace_back( "ARMOR", space + _( "Ballistic: " ), "", iteminfo::is_decimal, + bullet_resist() ); + info.emplace_back( "ARMOR", space + _( "Acid: " ), "", + iteminfo::no_newline | iteminfo::is_decimal, acid_resist() ); + info.emplace_back( "ARMOR", space + _( "Fire: " ), "", + iteminfo::no_newline | iteminfo::is_decimal, fire_resist() ); + info.emplace_back( "ARMOR", space + _( "Environmental: " ), + get_base_env_resist( *this ) ); if( type->can_use( "GASMASK" ) || type->can_use( "DIVE_TANK" ) ) { - info.push_back( iteminfo( "ARMOR", - _( "Protection when active: " ) ) ); - info.push_back( iteminfo( "ARMOR", space + _( "Acid: " ), "", - iteminfo::no_newline | iteminfo::is_decimal, - acid_resist( false, get_base_env_resist_w_filter() ) ) ); - info.push_back( iteminfo( "ARMOR", space + _( "Fire: " ), "", - iteminfo::no_newline | iteminfo::is_decimal, - fire_resist( false, get_base_env_resist_w_filter() ) ) ); - info.push_back( iteminfo( "ARMOR", space + _( "Environmental: " ), - get_env_resist( get_base_env_resist_w_filter() ) ) ); + info.emplace_back( "ARMOR", + _( "Protection when active: " ) ); + info.emplace_back( "ARMOR", space + _( "Acid: " ), "", + iteminfo::no_newline | iteminfo::is_decimal, + acid_resist( false, get_base_env_resist_w_filter() ) ); + info.emplace_back( "ARMOR", space + _( "Fire: " ), "", + iteminfo::no_newline | iteminfo::is_decimal, + fire_resist( false, get_base_env_resist_w_filter() ) ); + info.emplace_back( "ARMOR", space + _( "Environmental: " ), + get_env_resist( get_base_env_resist_w_filter() ) ); } if( damage() > 0 ) { - info.push_back( iteminfo( "ARMOR", - _( "Protection values are reduced by damage and " - "you may be able to improve them by repairing this " - "item." ) ) ); + info.emplace_back( "ARMOR", + _( "Protection values are reduced by damage and " + "you may be able to improve them by repairing this " + "item." ) ); } } } @@ -2697,7 +2881,6 @@ void item::armor_info( std::vector &info, const iteminfo_query *parts, return; } - Character &player_character = get_player_character(); const std::string space = " "; body_part_set covered_parts = get_covered_body_parts(); bool covers_anything = covered_parts.any(); @@ -2762,7 +2945,7 @@ void item::armor_info( std::vector &info, const iteminfo_query *parts, coverage += _( " Nothing." ); } - info.push_back( iteminfo( "ARMOR", coverage ) ); + info.emplace_back( "ARMOR", coverage ); } if( parts->test( iteminfo_parts::ARMOR_LAYER ) && covers_anything ) { @@ -2783,135 +2966,57 @@ void item::armor_info( std::vector &info, const iteminfo_query *parts, layering += _( " Normal." ); } - info.push_back( iteminfo( "ARMOR", layering ) ); + info.emplace_back( "ARMOR", layering ); } if( parts->test( iteminfo_parts::ARMOR_COVERAGE ) && covers_anything ) { - info.push_back( iteminfo( "ARMOR", _( "Average Coverage: " ), "%", - iteminfo::no_newline, get_avg_coverage() ) ); + info.emplace_back( "ARMOR", _( "Average Coverage: " ), "%", + iteminfo::no_newline, get_avg_coverage() ); } if( parts->test( iteminfo_parts::ARMOR_WARMTH ) && covers_anything ) { - info.push_back( iteminfo( "ARMOR", space + _( "Warmth: " ), get_warmth() ) ); + info.emplace_back( "ARMOR", space + _( "Warmth: " ), get_warmth() ); } insert_separation_line( info ); - if( parts->test( iteminfo_parts::ARMOR_ENCUMBRANCE ) && covers_anything ) { - std::string format; - const bool sizing_matters = get_sizing( player_character ) != sizing::ignore; - if( has_flag( flag_FIT ) ) { - format = _( " (fits)" ); - } else if( has_flag( flag_VARSIZE ) && sizing_matters ) { - format = _( " (poor fit)" ); - } - if( sizing_matters ) { - const sizing sizing_level = get_sizing( player_character ); - //If we have the wrong size, we do not fit so alert the player - if( sizing_level == sizing::human_sized_small_char ) { - format = _( " (too big)" ); - } else if( sizing_level == sizing::big_sized_small_char ) { - format = _( " (huge!)" ); - } else if( sizing_level == sizing::small_sized_human_char || - sizing_level == sizing::human_sized_big_char ) { - format = _( " (too small)" ); - } else if( sizing_level == sizing::small_sized_big_char ) { - format = _( " (tiny!)" ); - } - } - - info.push_back( iteminfo( "ARMOR", _( "Encumbrance:" ) + format ) ); - - if( const islot_armor *t = find_armor_data() ) { - if( !t->data.empty() ) { - struct armor_portion_type { - int encumber = 0; - int max_encumber = 0; - int coverage = 0; - - bool operator==( const armor_portion_type &other ) { - return encumber == other.encumber - && max_encumber == other.max_encumber - && coverage == other.coverage; - } - }; - struct body_part_display_info { - translation to_display; - armor_portion_type portion; - bool active = false; - }; + if( covers_anything ) { + int power_armor_encumbrance_reduction = 40; + static const itype_id itype_rm13_armor( "rm13_armor" ); - std::map to_display_data; + if( ( is_power_armor() ) || type->get_id() == itype_rm13_armor ) { + item tmp = *this; - for( const armor_portion_data &piece : t->data ) { - if( piece.covers.has_value() ) { - for( const bodypart_str_id &covering_id : piece.covers.value() ) { - if( covering_id != bodypart_str_id::NULL_ID() ) { - to_display_data[covering_id] = { covering_id.obj().name_as_heading, { - get_encumber( player_character, covering_id ), - get_encumber( player_character, covering_id, encumber_flags::assume_full ), - piece.coverage - }, true - }; - } - } - } + //no need to clutter the ui with inactive versions when the armor is already active + if( !active ) { + if( parts->test( iteminfo_parts::ARMOR_ENCUMBRANCE ) ) { + tmp.armor_encumbrance_info( info ); } - // Handle things that use both sides to avoid showing L. Arm R. Arm etc when both are the same - if( !t->sided ) { - for( const armor_portion_data &piece : t->data ) { - for( const bodypart_str_id &bp : *piece.covers ) { - bodypart_str_id opposite = bp->opposite_part; - if( opposite != bp && covers( bp ) && covers( opposite ) - && to_display_data.at( bp ).portion == to_display_data.at( opposite ).portion - && to_display_data.at( opposite ).active ) { - to_display_data.at( opposite ).to_display = bp->name_as_heading_multiple; - to_display_data.at( bp ).active = false; - } - } - } - } - for( auto &piece : to_display_data ) { - if( t->sided ) { - const bodypart_str_id &covering_id = piece.first; - if( !covers( covering_id.id() ) ) { - continue; - } - } - if( piece.second.active ) { - info.push_back( iteminfo( "ARMOR", - string_format( _( "%s:" ), piece.second.to_display.translated() ) + space, "", - iteminfo::no_newline | iteminfo::lower_is_better, - piece.second.portion.encumber ) ); - - if( piece.second.portion.encumber != piece.second.portion.max_encumber ) { - info.push_back( iteminfo( "ARMOR", space + _( "When full:" ) + space, "", - iteminfo::no_newline | iteminfo::lower_is_better, - piece.second.portion.max_encumber ) ); - } + tmp.armor_protection_info( info, parts, batch, debug ); - info.push_back( iteminfo( "ARMOR", space + _( "Coverage:" ) + space, "", - iteminfo::no_flags, - piece.second.portion.coverage ) ); - } - } + insert_separation_line( info ); + info.emplace_back( "ARMOR", _( "When active:" ) ); + tmp = tmp.convert( itype_id( tmp.typeId().str() + "_on" ) ); } - } else if( is_gun() && has_flag( flag_IS_ARMOR ) ) { - //right now all eligible gunmods (shoulder_strap, belt_clip) have the is_armor flag and use the torso - info.push_back( iteminfo( "ARMOR", _( "Torso:" ) + space, "", - iteminfo::no_newline | iteminfo::lower_is_better, get_avg_encumber( get_avatar() ) ) ); - info.push_back( iteminfo( "ARMOR", space + _( "Coverage:" ) + space, "", - iteminfo::no_flags, get_coverage( body_part_torso.id() ) ) ); + if( parts->test( iteminfo_parts::ARMOR_ENCUMBRANCE ) ) { + if( type->get_id() == itype_rm13_armor ) { + tmp.armor_encumbrance_info( info ); + } else { + tmp.armor_encumbrance_info( info, power_armor_encumbrance_reduction ); + } + } + tmp.armor_protection_info( info, parts, batch, debug ); + } else { + if( parts->test( iteminfo_parts::ARMOR_ENCUMBRANCE ) ) { + armor_encumbrance_info( info ); + } + armor_protection_info( info, parts, batch, debug ); } } // Whatever the last entry was, we want a newline at this point info.back().bNewLine = true; - if( covers_anything ) { - armor_protection_info( info, parts, batch, debug ); - } - const units::mass weight_bonus = get_weight_capacity_bonus(); const float weight_modif = get_weight_capacity_modifier(); if( weight_modif != 1 ) { @@ -2921,9 +3026,9 @@ void item::armor_info( std::vector &info, const iteminfo_query *parts, } else { modifier = "x"; } - info.push_back( iteminfo( "ARMOR", - _( "Weight capacity modifier: " ), modifier, - iteminfo::no_newline | iteminfo::is_decimal, weight_modif ) ); + info.emplace_back( "ARMOR", + _( "Weight capacity modifier: " ), modifier, + iteminfo::no_newline | iteminfo::is_decimal, weight_modif ); } if( weight_bonus != 0_gram ) { std::string bonus; @@ -2932,9 +3037,9 @@ void item::armor_info( std::vector &info, const iteminfo_query *parts, } else { bonus = string_format( " %s", weight_units() ); } - info.push_back( iteminfo( "ARMOR", _( "Weight capacity bonus: " ), bonus, - iteminfo::no_newline | iteminfo::is_decimal, - convert_weight( weight_bonus ) ) ); + info.emplace_back( "ARMOR", _( "Weight capacity bonus: " ), bonus, + iteminfo::no_newline | iteminfo::is_decimal, + convert_weight( weight_bonus ) ); } } @@ -2959,9 +3064,9 @@ void item::armor_fit_info( std::vector &info, const iteminfo_query *pa if( has_flag( flag_HELMET_COMPAT ) && parts->test( iteminfo_parts::DESCRIPTION_FLAGS_HELMETCOMPAT ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "* This item can be worn with a " - "helmet." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* This item can be worn with a " + "helmet." ) ); } if( parts->test( iteminfo_parts::DESCRIPTION_FLAGS_FITS ) ) { @@ -3045,7 +3150,7 @@ void item::armor_fit_info( std::vector &info, const iteminfo_query *pa } if( !resize_str.empty() ) { std::string info_str = string_format( _( "* This clothing %s." ), resize_str ); - info.push_back( iteminfo( "DESCRIPTION", info_str ) ); + info.emplace_back( "DESCRIPTION", info_str ); } } else { switch( sizing_level ) { @@ -3068,7 +3173,7 @@ void item::armor_fit_info( std::vector &info, const iteminfo_query *pa } std::string info_str = string_format( _( "* This clothing can be " "refitted%s." ), resize_str ); - info.push_back( iteminfo( "DESCRIPTION", info_str ) ); + info.emplace_back( "DESCRIPTION", info_str ); } } else { info.emplace_back( "DESCRIPTION", _( "* This clothing can not be refitted, " @@ -3077,32 +3182,32 @@ void item::armor_fit_info( std::vector &info, const iteminfo_query *pa } if( is_sided() && parts->test( iteminfo_parts::DESCRIPTION_FLAGS_SIDED ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "* This item can be worn on either side of " - "the body." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* This item can be worn on either side of " + "the body." ) ); } if( is_power_armor() && parts->test( iteminfo_parts::DESCRIPTION_FLAGS_POWERARMOR ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "* This gear is a part of power armor." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* This gear is a part of power armor." ) ); if( parts->test( iteminfo_parts::DESCRIPTION_FLAGS_POWERARMOR_RADIATIONHINT ) ) { if( covers( bodypart_id( "head" ) ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "* When worn with a power armor suit, it will " - "fully protect you from " - "radiation." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* When worn with a power armor suit, it will " + "fully protect you from " + "radiation." ) ); } else { - info.push_back( iteminfo( "DESCRIPTION", - _( "* When worn with a power armor helmet, it will " - "fully protect you from " - "radiation." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* When worn with a power armor helmet, it will " + "fully protect you from " + "radiation." ) ); } } } if( typeId() == itype_rad_badge && parts->test( iteminfo_parts::DESCRIPTION_IRRADIATION ) ) { - info.push_back( iteminfo( "DESCRIPTION", - string_format( _( "* The film strip on the badge is %s." ), - rad_badge_color( irradiation ) ) ) ); + info.emplace_back( "DESCRIPTION", + string_format( _( "* The film strip on the badge is %s." ), + rad_badge_color( irradiation ) ) ); } } @@ -3117,31 +3222,31 @@ void item::book_info( std::vector &info, const iteminfo_query *parts, const islot_book &book = *type->book; // Some things about a book you CAN tell by it's cover. if( !book.skill && !type->can_use( "MA_MANUAL" ) && parts->test( iteminfo_parts::BOOK_SUMMARY ) ) { - info.push_back( iteminfo( "BOOK", _( "Just for fun." ) ) ); + info.emplace_back( "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." ) ) ); + info.emplace_back( "BOOK", + _( "Some sort of martial arts training " + "manual." ) ); 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 " - "from it." ), style_to_learn->name ) ) ); - info.push_back( iteminfo( "BOOK", - string_format( _( "This fighting style is %s " - "to learn." ), - martialart_difficulty( style_to_learn ) ) ) ); - info.push_back( iteminfo( "BOOK", - string_format( _( "It'd be easier to master if you'd have " - "skill expertise in %s." ), - style_to_learn->primary_skill->name() ) ) ); + info.emplace_back( "BOOK", + string_format( _( "You can learn %s style " + "from it." ), style_to_learn->name ) ); + info.emplace_back( "BOOK", + string_format( _( "This fighting style is %s " + "to learn." ), + martialart_difficulty( style_to_learn ) ) ); + info.emplace_back( "BOOK", + string_format( _( "It'd be easier to master if you'd have " + "skill expertise in %s." ), + style_to_learn->primary_skill->name() ) ); } } if( book.req == 0 && parts->test( iteminfo_parts::BOOK_REQUIREMENTS_BEGINNER ) ) { - info.push_back( iteminfo( "BOOK", _( "It can be understood by " - "beginners." ) ) ); + info.emplace_back( "BOOK", _( "It can be understood by " + "beginners." ) ); } if( player_character.has_identified( typeId() ) ) { if( book.skill ) { @@ -3150,31 +3255,31 @@ void item::book_info( std::vector &info, const iteminfo_query *parts, const std::string skill_name = book.skill->name(); std::string fmt = string_format( _( "Can bring your %s skill to " "." ), skill_name ); - info.push_back( iteminfo( "BOOK", "", fmt, iteminfo::no_flags, book.level ) ); + info.emplace_back( "BOOK", "", fmt, iteminfo::no_flags, book.level ); fmt = string_format( _( "Your current %s skill is ." ), skill_name ); - info.push_back( iteminfo( "BOOK", "", fmt, iteminfo::no_flags, skill.level() ) ); + info.emplace_back( "BOOK", "", fmt, iteminfo::no_flags, skill.level() ); } if( book.req != 0 && parts->test( iteminfo_parts::BOOK_SKILLRANGE_MIN ) ) { const std::string fmt = string_format( _( "Requires %s level to " "understand." ), book.skill.obj().name() ); - info.push_back( iteminfo( "BOOK", "", fmt, - iteminfo::lower_is_better, book.req ) ); + info.emplace_back( "BOOK", "", fmt, + iteminfo::lower_is_better, book.req ); } } if( book.intel != 0 && parts->test( iteminfo_parts::BOOK_REQUIREMENTS_INT ) ) { - info.push_back( iteminfo( "BOOK", "", - _( "Requires intelligence of to easily " - "read." ), iteminfo::lower_is_better, book.intel ) ); + info.emplace_back( "BOOK", "", + _( "Requires intelligence of to easily " + "read." ), iteminfo::lower_is_better, book.intel ); } 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, player_character.book_fun_for( *this, player_character ) ) ); + info.emplace_back( "BOOK", "", + _( "Reading this book affects your morale by " ), + iteminfo::show_plus, player_character.book_fun_for( *this, player_character ) ); } if( parts->test( iteminfo_parts::BOOK_TIMEPERCHAPTER ) ) { std::string fmt = ngettext( @@ -3189,8 +3294,8 @@ void item::book_info( std::vector &info, const iteminfo_query *parts, "A training session with this book takes " " minutes.", book.time ); } - info.push_back( iteminfo( "BOOK", "", fmt, - iteminfo::lower_is_better, book.time ) ); + info.emplace_back( "BOOK", "", fmt, + iteminfo::lower_is_better, book.time ); } if( book.chapters > 0 && parts->test( iteminfo_parts::BOOK_NUMUNREADCHAPTERS ) ) { @@ -3198,7 +3303,7 @@ void item::book_info( std::vector &info, const iteminfo_query *parts, std::string fmt = ngettext( "This book has unread chapter.", "This book has unread chapters.", unread ); - info.push_back( iteminfo( "BOOK", "", fmt, iteminfo::no_flags, unread ) ); + info.emplace_back( "BOOK", "", fmt, iteminfo::no_flags, unread ); } if( !book.proficiencies.empty() ) { @@ -3211,7 +3316,7 @@ void item::book_info( std::vector &info, const iteminfo_query *parts, book.proficiencies.begin(), book.proficiencies.end(), enumerate_profs ) ); - info.push_back( iteminfo( "BOOK", profs ) ); + info.emplace_back( "BOOK", profs ); } if( parts->test( iteminfo_parts::BOOK_INCLUDED_RECIPES ) ) { @@ -3244,22 +3349,22 @@ void item::book_info( std::vector &info, const iteminfo_query *parts, recipe_list.size(), enumerate_as_string( recipe_list ) ); insert_separation_line( info ); - info.push_back( iteminfo( "DESCRIPTION", recipe_line ) ); + info.emplace_back( "DESCRIPTION", recipe_line ); } if( recipe_list.size() != book.recipes.size() && parts->test( iteminfo_parts::DESCRIPTION_BOOK_ADDITIONAL_RECIPES ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "It might help you figuring out some more " - "recipes." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "It might help you figuring out some more " + "recipes." ) ); } } } else { if( parts->test( iteminfo_parts::BOOK_UNREAD ) ) { - info.push_back( iteminfo( "BOOK", - _( "You need to read this book to see its " - "contents." ) ) ); + info.emplace_back( "BOOK", + _( "You need to read this book to see its " + "contents." ) ); } } } @@ -3333,24 +3438,24 @@ void item::tool_info( std::vector &info, const iteminfo_query *parts, // UPS, rechargeable power cells, and bionic power if( has_flag( flag_USE_UPS ) && parts->test( iteminfo_parts::DESCRIPTION_RECHARGE_UPSMODDED ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "* This tool has been modified to use a universal " - "power supply and is not compatible" - " with standard batteries." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* This tool has been modified to use a universal " + "power supply and is not compatible" + " with standard batteries." ) ); } else if( has_flag( flag_RECHARGE ) && has_flag( flag_NO_RELOAD ) && parts->test( iteminfo_parts::DESCRIPTION_RECHARGE_NORELOAD ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "* This tool has a rechargeable power cell " - "and is not compatible with " - "standard batteries." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* This tool has a rechargeable power cell " + "and is not compatible with " + "standard batteries." ) ); } else if( has_flag( flag_RECHARGE ) && parts->test( iteminfo_parts::DESCRIPTION_RECHARGE_UPSCAPABLE ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "* This tool has a rechargeable power cell " - "and can be recharged in any UPS-compatible " - "recharging station. You could charge it with " - "standard batteries, but unloading it is " - "impossible." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* This tool has a rechargeable power cell " + "and can be recharged in any UPS-compatible " + "recharging station. You could charge it with " + "standard batteries, but unloading it is " + "impossible." ) ); } else if( has_flag( flag_USES_BIONIC_POWER ) ) { info.emplace_back( "DESCRIPTION", _( "* This tool runs on bionic power." ) ); @@ -3371,7 +3476,7 @@ void item::tool_info( std::vector &info, const iteminfo_query *parts, feedback = _( "Almost completely burned out." ); } feedback = _( "Fuel: " ) + feedback; - info.push_back( iteminfo( "DESCRIPTION", feedback ) ); + info.emplace_back( "DESCRIPTION", feedback ); } } @@ -3382,11 +3487,11 @@ void item::component_info( std::vector &info, const iteminfo_query *pa return; } if( is_craft() ) { - info.push_back( iteminfo( "DESCRIPTION", string_format( _( "Using: %s" ), - components_to_string() ) ) ); + info.emplace_back( "DESCRIPTION", string_format( _( "Using: %s" ), + components_to_string() ) ); } else { - info.push_back( iteminfo( "DESCRIPTION", string_format( _( "Made from: %s" ), - components_to_string() ) ) ); + info.emplace_back( "DESCRIPTION", string_format( _( "Made from: %s" ), + components_to_string() ) ); } } @@ -3463,7 +3568,7 @@ void item::disassembly_info( std::vector &info, const iteminfo_query * } insert_separation_line( info ); - info.push_back( iteminfo( "DESCRIPTION", descr ) ); + info.emplace_back( "DESCRIPTION", descr ); } } @@ -3524,15 +3629,15 @@ void item::bionic_info( std::vector &info, const iteminfo_query *parts // TODO: Unhide when enforcing limits if( get_option < bool >( "CBM_SLOTS_ENABLED" ) && parts->test( iteminfo_parts::DESCRIPTION_CBM_SLOTS ) ) { - info.push_back( iteminfo( "DESCRIPTION", list_occupied_bps( type->bionic->id, - _( "This bionic is installed in the following body " - "part(s):" ) ) ) ); + info.emplace_back( "DESCRIPTION", list_occupied_bps( type->bionic->id, + _( "This bionic is installed in the following body " + "part(s):" ) ) ); } insert_separation_line( info ); if( is_bionic() && has_flag( flag_NO_STERILE ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "* This bionic is not sterile, use an autoclave and an autoclave pouch to sterilize it. " ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* This bionic is not sterile, use an autoclave and an autoclave pouch to sterilize it. " ) ); } insert_separation_line( info ); @@ -3541,79 +3646,78 @@ void item::bionic_info( std::vector &info, const iteminfo_query *parts if( !fuels.empty() ) { const int &fuel_numb = fuels.size(); - info.push_back( iteminfo( "DESCRIPTION", - ngettext( "* This bionic can produce power from the following fuel: ", - "* This bionic can produce power from the following fuels: ", - fuel_numb ) + enumerate_as_string( fuels.begin(), - fuels.end(), []( const material_id & id ) -> std::string { return "" + id->name() + ""; } ) ) ); + info.emplace_back( "DESCRIPTION", + ngettext( "* This bionic can produce power from the following fuel: ", + "* This bionic can produce power from the following fuels: ", + fuel_numb ) + enumerate_as_string( fuels.begin(), + fuels.end(), []( const material_id & id ) -> std::string { return "" + id->name() + ""; } ) ); } insert_separation_line( info ); if( bid->capacity > 0_mJ ) { - info.push_back( iteminfo( "CBM", _( "Power Capacity:" ), _( " mJ" ), - iteminfo::no_newline, - units::to_millijoule( bid->capacity ) ) ); + info.emplace_back( "CBM", _( "Power Capacity:" ), _( " mJ" ), + iteminfo::no_newline, + units::to_millijoule( bid->capacity ) ); } insert_separation_line( info ); // TODO refactor these almost identical blocks if( !bid->encumbrance.empty() ) { - info.push_back( iteminfo( "DESCRIPTION", _( "Encumbrance:" ), - iteminfo::no_newline ) ); + info.emplace_back( "DESCRIPTION", _( "Encumbrance:" ), + iteminfo::no_newline ); for( const std::pair &element : sorted_lex( bid->encumbrance ) ) { - info.push_back( - iteminfo( "CBM", " " + body_part_name_as_heading( element.first.id(), 1 ), - " ", iteminfo::no_newline, element.second ) ); + info.emplace_back( "CBM", " " + body_part_name_as_heading( element.first.id(), 1 ), + " ", iteminfo::no_newline, element.second ); } } if( !bid->env_protec.empty() ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "Environmental Protection:" ), - iteminfo::no_newline ) ); + info.emplace_back( "DESCRIPTION", + _( "Environmental Protection:" ), + iteminfo::no_newline ); for( const std::pair< bodypart_str_id, size_t > &element : sorted_lex( bid->env_protec ) ) { - info.push_back( iteminfo( "CBM", " " + body_part_name_as_heading( element.first, 1 ), - " ", iteminfo::no_newline, element.second ) ); + info.emplace_back( "CBM", " " + body_part_name_as_heading( element.first, 1 ), + " ", iteminfo::no_newline, element.second ); } } if( !bid->bash_protec.empty() ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "Bash Protection:" ), - iteminfo::no_newline ) ); + info.emplace_back( "DESCRIPTION", + _( "Bash Protection:" ), + iteminfo::no_newline ); for( const std::pair< bodypart_str_id, size_t > &element : sorted_lex( bid->bash_protec ) ) { - info.push_back( iteminfo( "CBM", " " + body_part_name_as_heading( element.first, 1 ), - " ", iteminfo::no_newline, element.second ) ); + info.emplace_back( "CBM", " " + body_part_name_as_heading( element.first, 1 ), + " ", iteminfo::no_newline, element.second ); } } if( !bid->cut_protec.empty() ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "Cut Protection:" ), - iteminfo::no_newline ) ); + info.emplace_back( "DESCRIPTION", + _( "Cut Protection:" ), + iteminfo::no_newline ); for( const std::pair< bodypart_str_id, size_t > &element : sorted_lex( bid->cut_protec ) ) { - info.push_back( iteminfo( "CBM", " " + body_part_name_as_heading( element.first, 1 ), - " ", iteminfo::no_newline, element.second ) ); + info.emplace_back( "CBM", " " + body_part_name_as_heading( element.first, 1 ), + " ", iteminfo::no_newline, element.second ); } } if( !bid->bullet_protec.empty() ) { - info.push_back( iteminfo( "DESCRIPTION", _( "Ballistic Protection:" ), - iteminfo::no_newline ) ); + info.emplace_back( "DESCRIPTION", _( "Ballistic Protection:" ), + iteminfo::no_newline ); for( const std::pair< bodypart_str_id, size_t > &element : sorted_lex( bid->bullet_protec ) ) { - info.push_back( iteminfo( "CBM", " " + body_part_name_as_heading( element.first, 1 ), - " ", iteminfo::no_newline, element.second ) ); + info.emplace_back( "CBM", " " + body_part_name_as_heading( element.first, 1 ), + " ", iteminfo::no_newline, element.second ); } } if( !bid->stat_bonus.empty() ) { - info.push_back( iteminfo( "DESCRIPTION", _( "Stat Bonus:" ), - iteminfo::no_newline ) ); + info.emplace_back( "DESCRIPTION", _( "Stat Bonus:" ), + iteminfo::no_newline ); for( const auto &element : bid->stat_bonus ) { - info.push_back( iteminfo( "CBM", " " + get_stat_name( element.first ), " ", - iteminfo::no_newline, element.second ) ); + info.emplace_back( "CBM", " " + get_stat_name( element.first ), " ", + iteminfo::no_newline, element.second ); } } @@ -3626,10 +3730,10 @@ void item::bionic_info( std::vector &info, const iteminfo_query *parts } else { modifier = "x"; } - info.push_back( iteminfo( "CBM", - _( "Weight capacity modifier: " ), modifier, - iteminfo::no_newline | iteminfo::is_decimal, - weight_modif ) ); + info.emplace_back( "CBM", + _( "Weight capacity modifier: " ), modifier, + iteminfo::no_newline | iteminfo::is_decimal, + weight_modif ); } if( weight_bonus != 0_gram ) { std::string bonus; @@ -3638,9 +3742,9 @@ void item::bionic_info( std::vector &info, const iteminfo_query *parts } else { bonus = string_format( " %s", weight_units() ); } - info.push_back( iteminfo( "CBM", _( "Weight capacity bonus: " ), bonus, - iteminfo::no_newline | iteminfo::is_decimal, - convert_weight( weight_bonus ) ) ); + info.emplace_back( "CBM", _( "Weight capacity bonus: " ), bonus, + iteminfo::no_newline | iteminfo::is_decimal, + convert_weight( weight_bonus ) ); } } @@ -3656,30 +3760,30 @@ void item::combat_info( std::vector &info, const iteminfo_query *parts insert_separation_line( info ); std::string sep; if( dmg_bash || dmg_cut || dmg_stab ) { - info.push_back( iteminfo( "BASE", _( "Melee damage: " ), "", iteminfo::no_newline ) ); + info.emplace_back( "BASE", _( "Melee damage: " ), "", iteminfo::no_newline ); } if( dmg_bash ) { - info.push_back( iteminfo( "BASE", _( "Bash: " ), "", iteminfo::no_newline, dmg_bash ) ); + info.emplace_back( "BASE", _( "Bash: " ), "", iteminfo::no_newline, dmg_bash ); sep = space; } if( dmg_cut ) { - info.push_back( iteminfo( "BASE", sep + _( "Cut: " ), "", iteminfo::no_newline, dmg_cut ) ); + info.emplace_back( "BASE", sep + _( "Cut: " ), "", iteminfo::no_newline, dmg_cut ); sep = space; } if( dmg_stab ) { - info.push_back( iteminfo( "BASE", sep + _( "Pierce: " ), "", iteminfo::no_newline, dmg_stab ) ); + info.emplace_back( "BASE", sep + _( "Pierce: " ), "", iteminfo::no_newline, dmg_stab ); } } if( dmg_bash || dmg_cut || dmg_stab ) { if( parts->test( iteminfo_parts::BASE_TOHIT ) ) { - info.push_back( iteminfo( "BASE", space + _( "To-hit bonus: " ), "", - iteminfo::show_plus, type->m_to_hit ) ); + info.emplace_back( "BASE", space + _( "To-hit bonus: " ), "", + iteminfo::show_plus, type->m_to_hit ); } if( parts->test( iteminfo_parts::BASE_MOVES ) ) { - info.push_back( iteminfo( "BASE", _( "Moves per attack: " ), "", - iteminfo::lower_is_better, attack_time() ) ); + info.emplace_back( "BASE", _( "Moves per attack: " ), "", + iteminfo::lower_is_better, attack_time() ); } @@ -3704,11 +3808,11 @@ void item::combat_info( std::vector &info, const iteminfo_query *parts if( !all_techniques.empty() ) { const std::vector all_tec_sorted = sorted_lex( all_techniques ); insert_separation_line( info ); - info.push_back( iteminfo( "DESCRIPTION", _( "Techniques when wielded: " ) + + info.emplace_back( "DESCRIPTION", _( "Techniques when wielded: " ) + enumerate_as_string( all_tec_sorted.begin(), all_tec_sorted.end(), []( const matec_id & tid ) { return string_format( "%s: %s", tid.obj().name, tid.obj().description ); - } ) ) ); + } ) ); } } @@ -3719,9 +3823,9 @@ void item::combat_info( std::vector &info, const iteminfo_query *parts typeId() ); if( !valid_styles.empty() ) { insert_separation_line( info ); - info.push_back( iteminfo( "DESCRIPTION", - _( "You know how to use this with these martial arts " - "styles: " ) + valid_styles ) ); + info.emplace_back( "DESCRIPTION", + _( "You know how to use this with these martial arts " + "styles: " ) + valid_styles ); } } @@ -3729,13 +3833,13 @@ void item::combat_info( std::vector &info, const iteminfo_query *parts parts->test( iteminfo_parts::DESCRIPTION_GUNMOD_ADDREACHATTACK ) ) { insert_separation_line( info ); if( has_flag( flag_REACH3 ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "* This item can be used to make long reach " - "attacks." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* This item can be used to make long reach " + "attacks." ) ); } else { - info.push_back( iteminfo( "DESCRIPTION", - _( "* This item can be used to make reach " - "attacks." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* This item can be used to make reach " + "attacks." ) ); } } @@ -3749,50 +3853,50 @@ void item::combat_info( std::vector &info, const iteminfo_query *parts 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:" ) ) ); + info.emplace_back( "DESCRIPTION", _( "Average melee damage:" ) ); } // Chance of critical hit if( parts->test( iteminfo_parts::DESCRIPTION_MELEEDMG_CRIT ) ) { - info.push_back( iteminfo( "DESCRIPTION", - string_format( _( "Critical hit chance %d%% - %d%%" ), + info.emplace_back( "DESCRIPTION", + string_format( _( "Critical hit chance %d%% - %d%%" ), static_cast( player_character.crit_chance( 0, 100, *this ) * 100 ), static_cast( player_character.crit_chance( 100, 0, *this ) * - 100 ) ) ) ); + 100 ) ) ); } // Bash damage if( parts->test( iteminfo_parts::DESCRIPTION_MELEEDMG_BASH ) ) { // NOTE: Using "BASE" instead of "DESCRIPTION", so numerical formatting will work // (output.cpp:format_item_info does not interpolate for DESCRIPTION info) - info.push_back( iteminfo( "BASE", _( "Bashing: " ), "", iteminfo::no_newline, - non_crit.type_damage( damage_type::BASH ) ) ); - info.push_back( iteminfo( "BASE", space + _( "Critical bash: " ), "", iteminfo::no_flags, - crit.type_damage( damage_type::BASH ) ) ); + info.emplace_back( "BASE", _( "Bashing: " ), "", iteminfo::no_newline, + non_crit.type_damage( damage_type::BASH ) ); + info.emplace_back( "BASE", space + _( "Critical bash: " ), "", iteminfo::no_flags, + crit.type_damage( damage_type::BASH ) ); } // Cut damage if( ( non_crit.type_damage( damage_type::CUT ) > 0.0f || crit.type_damage( damage_type::CUT ) > 0.0f ) && parts->test( iteminfo_parts::DESCRIPTION_MELEEDMG_CUT ) ) { - info.push_back( iteminfo( "BASE", _( "Cutting: " ), "", iteminfo::no_newline, - non_crit.type_damage( damage_type::CUT ) ) ); - info.push_back( iteminfo( "BASE", space + _( "Critical cut: " ), "", iteminfo::no_flags, - crit.type_damage( damage_type::CUT ) ) ); + info.emplace_back( "BASE", _( "Cutting: " ), "", iteminfo::no_newline, + non_crit.type_damage( damage_type::CUT ) ); + info.emplace_back( "BASE", space + _( "Critical cut: " ), "", iteminfo::no_flags, + crit.type_damage( damage_type::CUT ) ); } // Pierce/stab damage if( ( non_crit.type_damage( damage_type::STAB ) > 0.0f || crit.type_damage( damage_type::STAB ) > 0.0f ) && parts->test( iteminfo_parts::DESCRIPTION_MELEEDMG_PIERCE ) ) { - info.push_back( iteminfo( "BASE", _( "Piercing: " ), "", iteminfo::no_newline, - non_crit.type_damage( damage_type::STAB ) ) ); - info.push_back( iteminfo( "BASE", space + _( "Critical pierce: " ), "", iteminfo::no_flags, - crit.type_damage( damage_type::STAB ) ) ); + info.emplace_back( "BASE", _( "Piercing: " ), "", iteminfo::no_newline, + non_crit.type_damage( damage_type::STAB ) ); + info.emplace_back( "BASE", space + _( "Critical pierce: " ), "", iteminfo::no_flags, + crit.type_damage( damage_type::STAB ) ); } // Moves if( parts->test( iteminfo_parts::DESCRIPTION_MELEEDMG_MOVES ) ) { - info.push_back( iteminfo( "BASE", _( "Moves per attack: " ), "", - iteminfo::lower_is_better, attack_cost ) ); + info.emplace_back( "BASE", _( "Moves per attack: " ), "", + iteminfo::lower_is_better, attack_cost ); } insert_separation_line( info ); } @@ -3878,14 +3982,14 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, if( parts->test( iteminfo_parts::DESCRIPTION_CONDUCTIVITY ) ) { if( !conductive() ) { - info.push_back( iteminfo( "BASE", _( "* This item does not " - "conduct electricity." ) ) ); + info.emplace_back( "BASE", _( "* This item does not " + "conduct electricity." ) ); } else if( has_flag( flag_CONDUCTIVE ) ) { - info.push_back( iteminfo( "BASE", - _( "* This item effectively conducts " - "electricity, as it has no guard." ) ) ); + info.emplace_back( "BASE", + _( "* This item effectively conducts " + "electricity, as it has no guard." ) ); } else { - info.push_back( iteminfo( "BASE", _( "* This item conducts electricity." ) ) ); + info.emplace_back( "BASE", _( "* This item conducts electricity." ) ); } } @@ -3951,16 +4055,16 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, if( has_flag( flag_LEAK_DAM ) && has_flag( flag_RADIOACTIVE ) && damage() > 0 && parts->test( iteminfo_parts::DESCRIPTION_RADIOACTIVITY_DAMAGED ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "* The casing of this item has cracked, " - "revealing an ominous green glow." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* The casing of this item has cracked, " + "revealing an ominous green glow." ) ); } if( has_flag( flag_LEAK_ALWAYS ) && has_flag( flag_RADIOACTIVE ) && parts->test( iteminfo_parts::DESCRIPTION_RADIOACTIVITY_ALWAYS ) ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "* This object is surrounded by a " - "sickly green glow." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "* This object is surrounded by a " + "sickly green glow." ) ); } if( is_brewable() ) { @@ -3970,25 +4074,25 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, int btime_i = to_days( btime ); if( btime <= 2_days ) { btime_i = to_hours( btime ); - info.push_back( iteminfo( "DESCRIPTION", - string_format( ngettext( "* Once set in a vat, this " + info.emplace_back( "DESCRIPTION", + string_format( ngettext( "* Once set in a vat, this " "will ferment in around %d hour.", "* Once set in a vat, this will ferment in " - "around %d hours.", btime_i ), btime_i ) ) ); + "around %d hours.", btime_i ), btime_i ) ); } else { - info.push_back( iteminfo( "DESCRIPTION", - string_format( ngettext( "* Once set in a vat, this " + info.emplace_back( "DESCRIPTION", + string_format( ngettext( "* Once set in a vat, this " "will ferment in around %d day.", "* Once set in a vat, this will ferment in " - "around %d days.", btime_i ), btime_i ) ) ); + "around %d days.", btime_i ), btime_i ) ); } } if( parts->test( iteminfo_parts::DESCRIPTION_BREWABLE_PRODUCTS ) ) { for( const itype_id &res : brewed.brewing_results() ) { - info.push_back( iteminfo( "DESCRIPTION", - string_format( _( "* Fermenting this will produce " - "%s." ), - nname( res, brewed.charges ) ) ) ); + info.emplace_back( "DESCRIPTION", + string_format( _( "* Fermenting this will produce " + "%s." ), + nname( res, brewed.charges ) ) ); } } } @@ -4032,13 +4136,13 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, } const int time_to_do = tt->time_to_do( *this ); if( time_to_do <= 0 ) { - info.push_back( iteminfo( "DESCRIPTION", - _( "It's done and can be activated." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "It's done and can be activated." ) ); } else { const std::string time = to_string_clipped( time_duration::from_turns( time_to_do ) ); - info.push_back( iteminfo( "DESCRIPTION", - string_format( _( "It will be done in %s." ), - time.c_str() ) ) ); + info.emplace_back( "DESCRIPTION", + string_format( _( "It will be done in %s." ), + time.c_str() ) ); } } } @@ -4065,7 +4169,7 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, //~ %1$s: inscription text ntext = string_format( pgettext( "carving", "Note: %1$s" ), item_note->second ); } - info.push_back( iteminfo( "DESCRIPTION", ntext ) ); + info.emplace_back( "DESCRIPTION", ntext ); } if( parts->test( iteminfo_parts::DESCRIPTION_DIE ) && this->get_var( "die_num_sides", 0 ) != 0 ) { @@ -4081,15 +4185,15 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, const int price_postapoc = price( true ) * batch; if( parts->test( iteminfo_parts::BASE_PRICE ) ) { insert_separation_line( info ); - info.push_back( iteminfo( "BASE", _( "Price: " ), _( "$" ), - iteminfo::is_decimal | iteminfo::lower_is_better | iteminfo::no_newline, - static_cast( price_preapoc ) / 100 ) ); + info.emplace_back( "BASE", _( "Price: " ), _( "$" ), + iteminfo::is_decimal | iteminfo::lower_is_better | iteminfo::no_newline, + static_cast( price_preapoc ) / 100 ); } if( price_preapoc != price_postapoc && parts->test( iteminfo_parts::BASE_BARTER ) ) { const std::string space = " "; - info.push_back( iteminfo( "BASE", space + _( "Barter value: " ), _( "$" ), - iteminfo::is_decimal | iteminfo::lower_is_better, - static_cast( price_postapoc ) / 100 ) ); + info.emplace_back( "BASE", space + _( "Barter value: " ), _( "$" ), + iteminfo::is_decimal | iteminfo::lower_is_better, + static_cast( price_postapoc ) / 100 ); } // Recipes using this item as an ingredient @@ -4103,17 +4207,17 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, if( item_recipes.empty() ) { insert_separation_line( info ); - info.push_back( iteminfo( "DESCRIPTION", - _( "You know of nothing you could craft with it." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "You know of nothing you could craft with it." ) ); } else { if( item_recipes.size() > 24 ) { insert_separation_line( info ); - info.push_back( iteminfo( "DESCRIPTION", - _( "You know dozens of things you could craft with it." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "You know dozens of things you could craft with it." ) ); } else if( item_recipes.size() > 12 ) { insert_separation_line( info ); - info.push_back( iteminfo( "DESCRIPTION", - _( "You could use it to craft various other things." ) ) ); + info.emplace_back( "DESCRIPTION", + _( "You could use it to craft various other things." ) ); } else { // Extract item names from recipes and sort them std::vector> result_names; @@ -4136,9 +4240,9 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, } } ); insert_separation_line( info ); - info.push_back( iteminfo( "DESCRIPTION", - string_format( _( "You could use it to craft: %s" ), - recipes ) ) ); + info.emplace_back( "DESCRIPTION", + string_format( _( "You could use it to craft: %s" ), + recipes ) ); } } } @@ -4147,9 +4251,9 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, const ret_val can_wear = player_character.can_wear( *this, true ); if( ! can_wear.success() ) { insert_separation_line( info ); - info.push_back( iteminfo( "DESCRIPTION", - // can_wear returns a translated string - string_format( "%s", can_wear.str() ) ) ); + info.emplace_back( "DESCRIPTION", + // can_wear returns a translated string + string_format( "%s", can_wear.str() ) ); } } @@ -4157,10 +4261,13 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, contents_info( info, parts, batch, debug ); if( get_option( "ENABLE_ASCII_ART" ) ) { - const ascii_art_id art = type->picture_id; + ascii_art_id art = type->picture_id; + if( has_gun_variant() && gun_variant().art.is_valid() ) { + art = gun_variant().art; + } if( art.is_valid() ) { for( const std::string &line : art->picture ) { - info.push_back( iteminfo( "DESCRIPTION", line ) ); + info.emplace_back( "DESCRIPTION", line ); } } } @@ -4335,7 +4442,6 @@ nc_color item::color_in_inventory() const // Only reviving corpses are yellow ret = c_yellow; } else if( const item *food = get_food() ) { - // 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( player_character.allergy_type( *food ) != morale_type( "morale_null" ) ) { @@ -6752,6 +6858,74 @@ bool item::is_gun() const return !!type->gun; } +void item::select_gun_variant() +{ + weighted_int_list variants; + if( is_gun() ) { + for( const gun_variant_data &gv : type->gun->variants ) { + variants.add( gv.id, gv.weight ); + } + } else if( !!type->magazine ) { + for( const gun_variant_data &gv : type->magazine->variants ) { + variants.add( gv.id, gv.weight ); + } + } else { + return; + } + + const std::string *selected = variants.pick(); + if( !selected ) { + // No variants exist + return; + } + + set_gun_variant( *selected ); +} + +bool item::has_gun_variant( bool check_option ) const +{ + return _gun_variant != nullptr && + ( !check_option || get_option( "SHOW_GUN_VARIANTS" ) ); +} + +const gun_variant_data &item::gun_variant() const +{ + return *_gun_variant; +} + +void item::set_gun_variant( const std::string &variant ) +{ + if( variant.empty() || ( is_gun() && type->gun->variants.empty() ) || ( !!type->magazine && + type->magazine->variants.empty() ) ) { + return; + } + + if( is_gun() ) { + for( const gun_variant_data &option : type->gun->variants ) { + if( option.id == variant ) { + _gun_variant = &option; + return; + } + } + } else if( !!type->magazine ) { + for( const gun_variant_data &option : type->magazine->variants ) { + if( option.id == variant ) { + _gun_variant = &option; + return; + } + } + } else { + return; + } + + debugmsg( "Gun %s has no variant %s!", typeId().str(), variant ); +} + +void item::clear_gun_variant() +{ + _gun_variant = nullptr; +} + bool item::is_firearm() const { return is_gun() && !has_flag( flag_PRIMITIVE_RANGED_WEAPON ); @@ -6986,6 +7160,16 @@ bool item::is_book() const return !!type->book; } +std::string item::get_book_skill() const +{ + if( is_book() ) { + if( type->book->skill->ident() != skill_id::NULL_ID() ) { + return type->book->skill->name(); + } + } + return ""; +} + bool item::is_map() const { return get_category_shallow().get_id() == item_category_maps; @@ -7414,7 +7598,7 @@ std::vector> item::get_available_recipes( const C for( const islot_book::recipe_with_description_t &elem : type->book->recipes ) { if( u.get_skill_level( elem.recipe->skill_used ) >= elem.skill_level ) { - recipe_entries.push_back( std::make_pair( elem.recipe, elem.skill_level ) ); + recipe_entries.emplace_back( elem.recipe, elem.skill_level ); } } } else if( has_var( "EIPC_RECIPES" ) ) { @@ -7431,7 +7615,7 @@ std::vector> item::get_available_recipes( const C next_string_index - first_string_index ); const recipe *r = &recipe_id( new_recipe ).obj(); if( u.get_skill_level( r->skill_used ) >= r->difficulty ) { - recipe_entries.push_back( std::make_pair( r, r->difficulty ) ); + recipe_entries.emplace_back( r, r->difficulty ); } first_string_index = next_string_index + 1; } @@ -8807,7 +8991,7 @@ void item::set_item_specific_energy( const float new_specific_energy ) const float specific_heat_liquid = get_specific_heat_liquid(); // J/g K const float specific_heat_solid = get_specific_heat_solid(); // J/g K const float latent_heat = get_latent_heat(); // J/kg - const float freezing_temperature = temp_to_kelvin( get_freeze_point() ); // K + const float freezing_temperature = celsius_to_kelvin( get_freeze_point() ); // K const float completely_frozen_specific_energy = specific_heat_solid * freezing_temperature; // Energy that the item would have if it was completely solid at freezing temperature const float completely_liquid_specific_energy = completely_frozen_specific_energy + @@ -8870,7 +9054,7 @@ float item::get_specific_energy_from_temperature( const float new_temperature ) const float specific_heat_liquid = get_specific_heat_liquid(); // J/g K const float specific_heat_solid = get_specific_heat_solid(); // J/g K const float latent_heat = get_latent_heat(); // J/kg - const float freezing_temperature = temp_to_kelvin( get_freeze_point() ); // K + const float freezing_temperature = celsius_to_kelvin( get_freeze_point() ); // K const float completely_frozen_energy = specific_heat_solid * freezing_temperature; // Energy that the item would have if it was completely solid at freezing temperature const float completely_liquid_energy = completely_frozen_energy + @@ -8888,7 +9072,7 @@ float item::get_specific_energy_from_temperature( const float new_temperature ) void item::set_item_temperature( float new_temperature ) { - const float freezing_temperature = temp_to_kelvin( get_freeze_point() ); // K + const float freezing_temperature = celsius_to_kelvin( get_freeze_point() ); // K const float specific_heat_solid = get_specific_heat_solid(); // J/g K const float latent_heat = get_latent_heat(); // J/kg @@ -9153,9 +9337,12 @@ iteminfo::iteminfo( const std::string &Type, const std::string &Name, double Val } iteminfo vol_to_info( const std::string &type, const std::string &left, - const units::volume &vol, int decimal_places ) + const units::volume &vol, int decimal_places, bool lower_is_better ) { - iteminfo::flags f = iteminfo::lower_is_better | iteminfo::no_newline; + iteminfo::flags f = iteminfo::no_newline; + if( lower_is_better ) { + f |= iteminfo::lower_is_better; + } int converted_volume_scale = 0; const double converted_volume = round_up( convert_volume( vol.value(), &converted_volume_scale ), decimal_places ); @@ -9167,9 +9354,12 @@ iteminfo vol_to_info( const std::string &type, const std::string &left, } iteminfo weight_to_info( const std::string &type, const std::string &left, - const units::mass &weight, int /* decimal_places */ ) + const units::mass &weight, int /* decimal_places */, bool lower_is_better ) { - iteminfo::flags f = iteminfo::lower_is_better | iteminfo::no_newline; + iteminfo::flags f = iteminfo::no_newline; + if( lower_is_better ) { + f |= iteminfo::lower_is_better; + } const double converted_weight = convert_weight( weight ); f |= iteminfo::is_decimal; return iteminfo( type, left, string_format( " %s", weight_units() ), f, @@ -9530,7 +9720,7 @@ void item::calc_temp( const int temp, const float insulation, const time_duratio const float specific_heat_liquid = get_specific_heat_liquid(); const float specific_heat_solid = get_specific_heat_solid(); const float latent_heat = get_latent_heat(); - const float freezing_temperature = temp_to_kelvin( get_freeze_point() ); // K + const float freezing_temperature = celsius_to_kelvin( get_freeze_point() ); // K const float completely_frozen_specific_energy = specific_heat_solid * freezing_temperature; // Energy that the item would have if it was completely solid at freezing temperature const float completely_liquid_specific_energy = completely_frozen_specific_energy + @@ -10424,6 +10614,8 @@ std::string item::type_name( unsigned int quantity ) const } } else if( iter != item_vars.end() ) { return iter->second; + } else if( has_gun_variant() ) { + ret_name = gun_variant().brand_name.translated(); } else { ret_name = type->nname( quantity ); } @@ -10473,11 +10665,8 @@ std::string item::type_name( unsigned int quantity ) const return ret_name; } -std::string item::get_corpse_name() +std::string item::get_corpse_name() const { - if( corpse_name.empty() ) { - return std::string(); - } return corpse_name; } @@ -10638,7 +10827,7 @@ std::vector item::get_uncraft_components() const if( iter != ret.end() ) { iter->count += component.count(); } else { - ret.push_back( item_comp( component.typeId(), component.count() ) ); + ret.emplace_back( component.typeId(), component.count() ); } } } diff --git a/src/item.h b/src/item.h index 6290008679cd7..04f86a447f481 100644 --- a/src/item.h +++ b/src/item.h @@ -17,6 +17,7 @@ #include "calendar.h" #include "cata_utility.h" +#include "compatibility.h" #include "craft_command.h" #include "enums.h" #include "gun_mode.h" @@ -34,6 +35,7 @@ #include "visitable.h" class Character; +class Creature; class JsonIn; class JsonObject; class JsonOut; @@ -50,6 +52,7 @@ class player; class recipe; class relic; struct armor_portion_data; +struct gun_variant_data; struct islot_comestible; struct itype; struct mtype; @@ -165,9 +168,9 @@ struct enum_traits { }; iteminfo vol_to_info( const std::string &type, const std::string &left, - const units::volume &vol, int decimal_places = 2 ); + const units::volume &vol, int decimal_places = 2, bool lower_is_better = true ); iteminfo weight_to_info( const std::string &type, const std::string &left, - const units::mass &weight, int decimal_places = 2 ); + const units::mass &weight, int decimal_places = 2, bool lower_is_better = true ); inline bool is_crafting_component( const item &component ); @@ -178,9 +181,9 @@ class item : public visitable item(); - item( item && ); + item( item && ) noexcept( map_is_noexcept ); item( const item & ); - item &operator=( item && ); + item &operator=( item && ) noexcept( list_is_noexcept ); item &operator=( const item & ); explicit item( const itype_id &id, time_point turn = calendar::turn, int qty = -1 ); @@ -624,7 +627,7 @@ class item : public visitable * Calculate the item's effective damage per second past armor when wielded by a * character against a monster. */ - double effective_dps( const Character &guy, monster &mon ) const; + double effective_dps( const Character &guy, Creature &mon ) const; /** * calculate effective dps against a stock set of monsters. by default, assume g->u * is wielding @@ -1241,10 +1244,12 @@ class item : public visitable /** Returns true if the item is A: is SOLID and if it B: is of type LIQUID */ bool is_frozen_liquid() const; + /** Returns empty string if the book teach no skill */ + std::string get_book_skill() const; float get_specific_heat_liquid() const; float get_specific_heat_solid() const; float get_latent_heat() const; - float get_freeze_point() const; // Fahrenheit + float get_freeze_point() const; // Celsius // If this is food, returns itself. If it contains food, return that // contents. Otherwise, returns nullptr. @@ -1584,6 +1589,8 @@ class item : public visitable * Whether this item (when worn) covers the given body part. */ bool covers( const bodypart_id &bp ) const; + // do both items overlap a bodypart at all? returns the side that conflicts via rhs + cata::optional covers_overlaps( const item &rhs ) const; /** * Bitset of all covered body parts. * @@ -1802,6 +1809,27 @@ class item : public visitable */ bool is_gun() const; + /** + * Does this item have a gun variant associated with it + * If check_option, the return of this is dependent on the SHOW_GUN_VARIANTS option + */ + bool has_gun_variant( bool check_option = true ) const; + + /** + * The gun variant associated with this item + */ + const gun_variant_data &gun_variant() const; + + /** + * Set the gun variant of this item + */ + void set_gun_variant( const std::string &variant ); + + /** + * For debug use only + */ + void clear_gun_variant(); + /** Quantity of energy currently loaded in tool or battery */ units::energy energy_remaining() const; @@ -2061,7 +2089,7 @@ class item : public visitable /** * Returns name of deceased being if it had any or empty string if not **/ - std::string get_corpse_name(); + std::string get_corpse_name() const; /** * Returns the translated item name for the item with given id. * The name is in the proper plural form as specified by the @@ -2249,6 +2277,8 @@ class item : public visitable /** Helper for checking reloadability. **/ bool is_reloadable_helper( const itype_id &ammo, bool now ) const; + void armor_encumbrance_info( std::vector &info, int reduce_encumbrance_by = 0 ) const; + public: enum class sizing : int { human_sized_human_char = 0, @@ -2303,6 +2333,13 @@ class item : public visitable std::string corpse_name; // Name of the late lamented std::set techniques; // item specific techniques + // Select a random variant from the possibilities + // Intended to be called when no explicit variant is set + void select_gun_variant(); + + // If the item has a gun variant, this points to it + const gun_variant_data *_gun_variant = nullptr; + /** * Data for items that represent in-progress crafts. */ diff --git a/src/item_contents.cpp b/src/item_contents.cpp index e9b93f6fae5ca..911b5274ced9c 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -149,6 +149,7 @@ bool pocket_favorite_callback::key( const input_context &, const input_event &ev } std::vector itype_initializer; + itype_initializer.reserve( nearby_itypes.size() ); for( const std::pair &name : nearby_itypes ) { itype_initializer.emplace_back( name.first ); } @@ -200,7 +201,7 @@ item_contents::item_contents( const std::vector &pockets ) { for( const pocket_data &data : pockets ) { - contents.push_back( item_pocket( &data ) ); + contents.emplace_back( &data ); } } @@ -1251,7 +1252,7 @@ void item_contents::update_modified_pockets( // in case the debugmsg wasn't clear, this should never happen debugmsg( "Oops! deleted some items when updating pockets that were added via toolmods" ); } - contents.push_back( item_pocket( *mag_or_mag_well ) ); + contents.emplace_back( *mag_or_mag_well ); pocket_iter = contents.erase( pocket_iter ); } else { ++pocket_iter; @@ -1267,7 +1268,7 @@ void item_contents::update_modified_pockets( // we've deleted all of the superfluous copies already, so time to add the new pockets for( const pocket_data *container_pocket : container_pockets ) { - contents.push_back( item_pocket( container_pocket ) ); + contents.emplace_back( container_pocket ); } } @@ -1548,7 +1549,7 @@ int item_contents::best_quality( const quality_id &id ) const static void insert_separation_line( std::vector &info ) { if( info.empty() || info.back().sName != "--" ) { - info.push_back( iteminfo( "DESCRIPTION", "--" ) ); + info.emplace_back( "DESCRIPTION", "--" ); } } @@ -1581,9 +1582,9 @@ void item_contents::info( std::vector &info, const iteminfo_query *par if( found_pockets.size() > 1 || pocket_num[0] > 1 ) { insert_separation_line( info ); info.emplace_back( "CONTAINER", _( "Total capacity:" ) ); - info.push_back( vol_to_info( "CONTAINER", _( "Volume: " ), total_container_capacity() ) ); - info.push_back( weight_to_info( "CONTAINER", _( " Weight: " ), - total_container_weight_capacity() ) ); + info.push_back( vol_to_info( "CONTAINER", _( "Volume: " ), total_container_capacity(), 2, false ) ); + info.push_back( weight_to_info( "CONTAINER", _( " Weight: " ), total_container_weight_capacity(), + 2, false ) ); info.back().bNewLine = true; } diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 8e7c3c998c205..dd7ecebfc358a 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -51,6 +51,7 @@ #include "string_formatter.h" #include "text_snippets.h" #include "translations.h" +#include "try_parse_integer.h" #include "ui.h" #include "units.h" #include "value_ptr.h" @@ -215,9 +216,19 @@ void Item_factory::finalize_pre( itype &obj ) // set light_emission based on LIGHT_[X] flag for( const auto &f : obj.item_tags ) { - int ll; - if( sscanf( f.c_str(), "LIGHT_%i", &ll ) == 1 && ll > 0 ) { - obj.light_emission = ll; + if( string_starts_with( f.str(), "LIGHT_" ) ) { + ret_val ll = try_parse_integer( f.str().substr( 6 ), false ); + if( ll.success() ) { + if( ll.value() > 0 ) { + obj.light_emission = ll.value(); + } else { + debugmsg( "item %s specifies light emission of zero, which is redundant", + obj.id.str() ); + } + } else { + debugmsg( "error parsing integer light emission suffic for item %s: %s", + obj.id.str(), ll.str() ); + } } } // remove LIGHT_[X] flags @@ -407,12 +418,6 @@ void Item_factory::finalize_pre( itype &obj ) gun_modifier_data( to_translation( "melee" ), 1, { "MELEE" } ) ); } - if( obj.gun->burst > 1 ) { - // handle legacy JSON format - obj.gun->modes.emplace( gun_mode_id( "AUTO" ), - gun_modifier_data( to_translation( "auto" ), obj.gun->burst, - std::set() ) ); - } if( obj.gun->handling < 0 ) { // TODO: specify in JSON via classes @@ -1767,14 +1772,23 @@ void Item_factory::load_wheel( const JsonObject &jo, const std::string &src ) } } -void Item_factory::load( islot_gun &slot, const JsonObject &jo, const std::string &src ) +void gun_variant_data::deserialize( JsonIn &jsin ) { - bool strict = src == "dda"; + load( jsin.get_object() ); +} - if( jo.has_member( "burst" ) && jo.has_member( "modes" ) ) { - jo.throw_error( "cannot specify both burst and modes", "burst" ); - } +void gun_variant_data::load( const JsonObject &jo ) +{ + mandatory( jo, false, "id", id ); + mandatory( jo, false, "name", brand_name ); + mandatory( jo, false, "description", alt_description ); + optional( jo, false, "ascii_picture", art ); + optional( jo, false, "weight", weight ); +} +void Item_factory::load( islot_gun &slot, const JsonObject &jo, const std::string &src ) +{ + bool strict = src == "dda"; assign( jo, "skill", slot.skill_used, strict ); assign( jo, "ammo", slot.ammo, strict ); assign( jo, "range", slot.range, strict ); @@ -1786,7 +1800,6 @@ void Item_factory::load( islot_gun &slot, const JsonObject &jo, const std::strin assign( jo, "recoil", slot.recoil, strict, 0 ); assign( jo, "handling", slot.handling, strict ); assign( jo, "durability", slot.durability, strict, 0, 10 ); - assign( jo, "burst", slot.burst, strict, 1 ); assign( jo, "loudness", slot.loudness, strict ); assign( jo, "clip_size", slot.clip, strict, 0 ); assign( jo, "reload", slot.reload_time, strict, 0 ); @@ -1801,6 +1814,8 @@ void Item_factory::load( islot_gun &slot, const JsonObject &jo, const std::strin assign( jo, "ammo_effects", slot.ammo_effects, strict ); assign( jo, "ammo_to_fire", slot.ammo_to_fire, strict, 1 ); + optional( jo, false, "variants", slot.variants ); + if( jo.has_array( "valid_mod_locations" ) ) { slot.valid_mod_locations.clear(); for( JsonArray curr : jo.get_array( "valid_mod_locations" ) ) { @@ -2420,6 +2435,8 @@ void Item_factory::load( islot_magazine &slot, const JsonObject &jo, const std:: assign( jo, "default_ammo", slot.default_ammo, strict ); assign( jo, "reload_time", slot.reload_time, strict, 0 ); assign( jo, "linkage", slot.linkage, strict ); + + optional( jo, false, "variants", slot.variants ); } void Item_factory::load_magazine( const JsonObject &jo, const std::string &src ) @@ -2546,7 +2563,7 @@ void hflesh_to_flesh( itype &item_template ) // Only add "flesh" material if not already present if( old_size != mats.size() && std::find( mats.begin(), mats.end(), material_id( "flesh" ) ) == mats.end() ) { - mats.push_back( material_id( "flesh" ) ); + mats.emplace_back( "flesh" ); } } @@ -3137,6 +3154,7 @@ void Item_factory::load_migration( const JsonObject &jo ) { migration m; assign( jo, "replace", m.replace ); + assign( jo, "variant", m.variant ); assign( jo, "flags", m.flags ); assign( jo, "charges", m.charges ); assign( jo, "contents", m.contents ); @@ -3186,6 +3204,8 @@ void Item_factory::migrate_item( const itype_id &id, item &obj ) obj.charges = iter->second.charges; } + obj.set_gun_variant( iter->second.variant ); + for( const migration::content &it : iter->second.contents ) { int count = it.count; item content( it.id, obj.birthday(), 1 ); @@ -3370,10 +3390,10 @@ bool Item_factory::load_sub_ref( std::unique_ptr &ptr, const Js iname, gname ) ); } if( obj.has_string( iname ) ) { - entries.push_back( std::make_pair( obj.get_string( iname ), false ) ); + entries.emplace_back( obj.get_string( iname ), false ); } if( obj.has_string( gname ) ) { - entries.push_back( std::make_pair( obj.get_string( gname ), true ) ); + entries.emplace_back( obj.get_string( gname ), true ); } const std::string subcontext = name + " of " + parent.context(); @@ -3487,6 +3507,10 @@ void Item_factory::add_entry( Item_group &ig, const JsonObject &obj, const std:: for( const auto &cf : custom_flags ) { modifier.custom_flags.emplace_back( cf ); } + if( obj.has_member( "variant" ) ) { + modifier.variant = obj.get_string( "variant" ); + use_modifier = true; + } if( use_modifier ) { sptr->modifier.emplace( std::move( modifier ) ); diff --git a/src/item_factory.h b/src/item_factory.h index 63d9847e94976..e63ec6784d73e 100644 --- a/src/item_factory.h +++ b/src/item_factory.h @@ -44,6 +44,7 @@ class migration { public: itype_id id; + std::string variant; itype_id replace; std::set flags; int charges = 0; diff --git a/src/item_group.cpp b/src/item_group.cpp index b0cfef5f57ef5..b7bc98a8c8ae4 100644 --- a/src/item_group.cpp +++ b/src/item_group.cpp @@ -391,6 +391,8 @@ void Item_modifier::modify( item &new_item, const std::string &context ) const } } + new_item.set_gun_variant( variant ); + // create container here from modifier or from default to get max charges later item cont; if( container != nullptr ) { @@ -610,11 +612,17 @@ Item_group::Item_group( Type t, int probability, int ammo_chance, int magazine_c } } -void Item_group::add_item_entry( const itype_id &itemid, int probability ) +void Item_group::add_item_entry( const itype_id &itemid, int probability, + const std::string &variant ) { std::string entry_context = "item " + itemid.str() + " within " + context(); - add_entry( std::make_unique( - itemid.str(), Single_item_creator::S_ITEM, probability, entry_context ) ); + std::unique_ptr added = std::make_unique( + itemid.str(), Single_item_creator::S_ITEM, probability, entry_context ); + if( !variant.empty() ) { + added->modifier.emplace(); + added->modifier->variant = variant; + } + add_entry( std::move( added ) ); } void Item_group::add_group_entry( const item_group_id &groupid, int probability ) diff --git a/src/item_group.h b/src/item_group.h index d6c9eae7db489..9987d7f75495f 100644 --- a/src/item_group.h +++ b/src/item_group.h @@ -250,6 +250,11 @@ class Item_modifier */ std::vector custom_flags; + /** + * gun variant id, for guns with variants + */ + std::string variant; + /** * Custom sub set of snippets to be randomly chosen from and then applied to the item. */ @@ -344,7 +349,8 @@ class Item_group : public Item_spawn_data */ using prop_list = std::vector >; - void add_item_entry( const itype_id &itemid, int probability ); + void add_item_entry( const itype_id &itemid, int probability, + const std::string &variant = "" ); void add_group_entry( const item_group_id &groupid, int probability ); /** * Once the relevant data has been read from JSON, this function is always called (either from diff --git a/src/item_pocket.cpp b/src/item_pocket.cpp index 2ac13bc398231..77528f33c5b26 100644 --- a/src/item_pocket.cpp +++ b/src/item_pocket.cpp @@ -822,7 +822,7 @@ void item_pocket::set_item_defaults() static void insert_separation_line( std::vector &info ) { if( info.empty() || info.back().sName != "--" ) { - info.push_back( iteminfo( "DESCRIPTION", "--" ) ); + info.emplace_back( "DESCRIPTION", "--" ); } } @@ -836,8 +836,8 @@ void item_pocket::general_info( std::vector &info, int pocket_number, // Show volume/weight for normal containers, or ammo capacity if ammo_restriction is defined if( data->ammo_restriction.empty() ) { - info.push_back( vol_to_info( "CONTAINER", _( "Volume: " ), volume_capacity() ) ); - info.push_back( weight_to_info( "CONTAINER", _( " Weight: " ), weight_capacity() ) ); + info.push_back( vol_to_info( "CONTAINER", _( "Volume: " ), volume_capacity(), 2, false ) ); + info.push_back( weight_to_info( "CONTAINER", _( " Weight: " ), weight_capacity(), 2, false ) ); info.back().bNewLine = true; } else { for( const ammotype &at : ammo_types() ) { @@ -851,10 +851,10 @@ void item_pocket::general_info( std::vector &info, int pocket_number, if( data->max_item_length != 0_mm ) { info.back().bNewLine = true; - info.push_back( iteminfo( "BASE", _( "Maximum item length: " ), - string_format( " %s", length_units( data->max_item_length ) ), - iteminfo::lower_is_better, - convert_length( data->max_item_length ) ) ); + info.emplace_back( "BASE", _( "Maximum item length: " ), + string_format( " %s", length_units( data->max_item_length ) ), + iteminfo::no_flags, + convert_length( data->max_item_length ) ); } if( data->min_item_volume > 0_ml ) { @@ -1677,19 +1677,19 @@ std::string enumerate( cata::flat_set container ) void item_pocket::favorite_settings::info( std::vector &info ) const { - info.push_back( iteminfo( "BASE", string_format( "%s %d", _( "Priority:" ), priority_rating ) ) ); - info.push_back( iteminfo( "BASE", string_format( _( "Item Whitelist: %s" ), - item_whitelist.empty() ? _( "(empty)" ) : + info.emplace_back( "BASE", string_format( "%s %d", _( "Priority:" ), priority_rating ) ); + info.emplace_back( "BASE", string_format( _( "Item Whitelist: %s" ), + item_whitelist.empty() ? _( "(empty)" ) : enumerate_as_string( item_whitelist.begin(), item_whitelist.end(), []( const itype_id & id ) { return id->nname( 1 ); - } ) ) ) ); - info.push_back( iteminfo( "BASE", string_format( _( "Item Blacklist: %s" ), - item_blacklist.empty() ? _( "(empty)" ) : + } ) ) ); + info.emplace_back( "BASE", string_format( _( "Item Blacklist: %s" ), + item_blacklist.empty() ? _( "(empty)" ) : enumerate_as_string( item_blacklist.begin(), item_blacklist.end(), []( const itype_id & id ) { return id->nname( 1 ); - } ) ) ) ); - info.push_back( iteminfo( "BASE", string_format( _( "Category Whitelist: %s" ), - category_whitelist.empty() ? _( "(empty)" ) : enumerate( category_whitelist ) ) ) ); - info.push_back( iteminfo( "BASE", string_format( _( "Category Blacklist: %s" ), - category_blacklist.empty() ? _( "(empty)" ) : enumerate( category_blacklist ) ) ) ); + } ) ) ); + info.emplace_back( "BASE", string_format( _( "Category Whitelist: %s" ), + category_whitelist.empty() ? _( "(empty)" ) : enumerate( category_whitelist ) ) ); + info.emplace_back( "BASE", string_format( _( "Category Blacklist: %s" ), + category_blacklist.empty() ? _( "(empty)" ) : enumerate( category_blacklist ) ) ); } diff --git a/src/item_search.cpp b/src/item_search.cpp index fd6b92bd44d22..04ff020e4a3e0 100644 --- a/src/item_search.cpp +++ b/src/item_search.cpp @@ -3,7 +3,9 @@ #include #include +#include "avatar.h" #include "cata_utility.h" +#include "game.h" #include "item.h" #include "item_category.h" #include "material.h" @@ -68,6 +70,14 @@ std::function basic_item_filter( std::string filter ) const std::string note = i.get_var( "item_note" ); return !note.empty() && lcmatch( note, filter ); }; + // by book skill + case 's': + return [filter]( const item & i ) { + if( get_avatar().has_identified( i.typeId() ) ) { + return lcmatch( i.get_book_skill(), filter ); + } + return false; + }; // by name default: return [filter]( const item & a ) { diff --git a/src/itype.h b/src/itype.h index a4894e37834ba..35c5f3e1ba2c1 100644 --- a/src/itype.h +++ b/src/itype.h @@ -161,8 +161,8 @@ struct islot_comestible { /**Amount of radiation you get from this comestible*/ int radiation = 0; - /** freezing point in degrees Fahrenheit, below this temperature item can freeze */ - int freeze_point = temperatures::freezing; + /** freezing point in degrees celsius, below this temperature item can freeze */ + float freeze_point = 0; /**List of diseases carried by this comestible and their associated probability*/ std::map contamination; @@ -458,8 +458,21 @@ struct islot_wheel { void deserialize( JsonIn &jsin ); }; +struct gun_variant_data { + std::string id; + translation brand_name; + translation alt_description; + ascii_art_id art; + + int weight = 0; + + void deserialize( JsonIn &jsin ); + void load( const JsonObject &jo ); +}; + // TODO: this shares a lot with the ammo item type, merge into a separate slot type? struct islot_gun : common_ranged_data { + std::vector variants; /** * What skill this gun uses. */ @@ -533,9 +546,6 @@ struct islot_gun : common_ranged_data { /** Firing modes are supported by the gun. Always contains at least DEFAULT mode */ std::map modes; - /** Burst size for AUTO mode (legacy field for items not migrated to specify modes ) */ - int burst = 0; - /** How easy is control of recoil? If unset value automatically derived from weapon type */ int handling = -1; @@ -646,6 +656,7 @@ struct islot_gunmod : common_ranged_data { }; struct islot_magazine { + std::vector variants; /** What type of ammo this magazine can be loaded with */ std::set type; diff --git a/src/iuse.cpp b/src/iuse.cpp index 1179ffcb71ac8..7bd216aace0ba 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -99,6 +99,7 @@ #include "timed_event.h" #include "translations.h" #include "trap.h" +#include "try_parse_integer.h" #include "type_id.h" #include "ui.h" #include "units.h" @@ -120,23 +121,19 @@ static const activity_id ACT_CHOP_PLANKS( "ACT_CHOP_PLANKS" ); static const activity_id ACT_CHOP_TREE( "ACT_CHOP_TREE" ); static const activity_id ACT_CHURN( "ACT_CHURN" ); static const activity_id ACT_CLEAR_RUBBLE( "ACT_CLEAR_RUBBLE" ); -static const activity_id ACT_CRAFT( "ACT_CRAFT" ); static const activity_id ACT_FILL_PIT( "ACT_FILL_PIT" ); static const activity_id ACT_FISH( "ACT_FISH" ); static const activity_id ACT_GAME( "ACT_GAME" ); static const activity_id ACT_GENERIC_GAME( "ACT_GENERIC_GAME" ); static const activity_id ACT_HACKSAW( "ACT_HACKSAW" ); -static const activity_id ACT_HAIRCUT( "ACT_HAIRCUT" ); static const activity_id ACT_HAND_CRANK( "ACT_HAND_CRANK" ); static const activity_id ACT_HEATING( "ACT_HEATING" ); static const activity_id ACT_JACKHAMMER( "ACT_JACKHAMMER" ); -static const activity_id ACT_MEDITATE( "ACT_MEDITATE" ); static const activity_id ACT_MIND_SPLICER( "ACT_MIND_SPLICER" ); static const activity_id ACT_OXYTORCH( "ACT_OXYTORCH" ); static const activity_id ACT_PICKAXE( "ACT_PICKAXE" ); static const activity_id ACT_PRY_NAILS( "ACT_PRY_NAILS" ); static const activity_id ACT_ROBOT_CONTROL( "ACT_ROBOT_CONTROL" ); -static const activity_id ACT_SHAVE( "ACT_SHAVE" ); static const activity_id ACT_VIBE( "ACT_VIBE" ); static const activity_id ACT_WASH( "ACT_WASH" ); @@ -145,7 +142,6 @@ static const efftype_id effect_antibiotic( "antibiotic" ); static const efftype_id effect_antibiotic_visible( "antibiotic_visible" ); static const efftype_id effect_antifungal( "antifungal" ); static const efftype_id effect_asthma( "asthma" ); -static const efftype_id effect_attention( "attention" ); static const efftype_id effect_beartrap( "beartrap" ); static const efftype_id effect_bite( "bite" ); static const efftype_id effect_bleed( "bleed" ); @@ -208,7 +204,6 @@ static const efftype_id effect_strong_antibiotic_visible( "strong_antibiotic_vis static const efftype_id effect_stunned( "stunned" ); static const efftype_id effect_tapeworm( "tapeworm" ); static const efftype_id effect_teargas( "teargas" ); -static const efftype_id effect_teleglow( "teleglow" ); static const efftype_id effect_tetanus( "tetanus" ); static const efftype_id effect_tied( "tied" ); static const efftype_id effect_took_antiasthmatic( "took_antiasthmatic" ); @@ -352,23 +347,18 @@ static const species_id species_FUNGUS( "FUNGUS" ); static const species_id species_HALLUCINATION( "HALLUCINATION" ); static const species_id species_INSECT( "INSECT" ); static const species_id species_ROBOT( "ROBOT" ); -static const species_id species_ZOMBIE( "ZOMBIE" ); static const vitamin_id vitamin_blood( "blood" ); static const vitamin_id vitamin_redcells( "redcells" ); static const mongroup_id GROUP_FISH( "GROUP_FISH" ); -static const mtype_id mon_bee( "mon_bee" ); static const mtype_id mon_blob( "mon_blob" ); static const mtype_id mon_dog_thing( "mon_dog_thing" ); -static const mtype_id mon_fly( "mon_fly" ); static const mtype_id mon_hallu_multicooker( "mon_hallu_multicooker" ); static const mtype_id mon_hologram( "mon_hologram" ); -static const mtype_id mon_shadow( "mon_shadow" ); static const mtype_id mon_spore( "mon_spore" ); static const mtype_id mon_vortex( "mon_vortex" ); -static const mtype_id mon_wasp( "mon_wasp" ); static const bionic_id bio_shock( "bio_shock" ); static const bionic_id bio_tools( "bio_tools" ); @@ -960,8 +950,7 @@ cata::optional iuse::meditate( player *p, item *it, bool t, const tripoint return cata::nullopt; } if( p->has_trait( trait_SPIRITUAL ) ) { - const int moves = to_moves( 20_minutes ); - p->assign_activity( ACT_MEDITATE, moves ); + p->assign_activity( player_activity( meditate_activity_actor() ) ); } else { p->add_msg_if_player( _( "This %s probably meant a lot to someone at one time." ), it->tname() ); @@ -1867,7 +1856,7 @@ cata::optional iuse::fishing_rod( player *p, item *it, bool, const tripoint } p->add_msg_if_player( _( "You cast your line and wait to hook something…" ) ); p->assign_activity( ACT_FISH, to_moves( 5_hours ), 0, 0, it->tname() ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.emplace_back( *p, it ); p->activity.coord_set = g->get_fishable_locations( 60, *found ); return 0; } @@ -2139,7 +2128,7 @@ cata::optional iuse::pack_cbm( player *p, item *it, bool, const tripoint & } std::vector comps; - comps.push_back( item_comp( it->typeId(), 1 ) ); + comps.emplace_back( it->typeId(), 1 ); p->consume_items( comps, 1, is_crafting_component ); return 0; @@ -3389,7 +3378,7 @@ cata::optional iuse::jackhammer( player *p, item *it, bool, const tripoint } p->assign_activity( ACT_JACKHAMMER, moves ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.emplace_back( *p, it ); p->activity.placement = here.getabs( pnt ); // You can mine either furniture or terrain, and furniture goes first, @@ -3501,7 +3490,7 @@ cata::optional iuse::pickaxe( player *p, item *it, bool, const tripoint &po } p->assign_activity( ACT_PICKAXE, moves, -1 ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.emplace_back( *p, it ); p->activity.placement = here.getabs( pnt ); // You can mine either furniture or terrain, and furniture goes first, @@ -4508,11 +4497,11 @@ cata::optional iuse::portable_game( player *p, item *it, bool active, const if( loaded_software == "null" ) { p->assign_activity( ACT_GENERIC_GAME, to_moves( 1_hours ), -1, p->get_item_position( it ), "gaming" ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.emplace_back( *p, it ); return 0; } p->assign_activity( ACT_GAME, moves, -1, 0, "gaming" ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.emplace_back( *p, it ); std::map game_data; game_data.clear(); int game_score = 0; @@ -4599,7 +4588,7 @@ cata::optional iuse::hand_crank( player *p, item *it, bool, const tripoint p->add_msg_if_player( _( "You start cranking the %s to charge its %s." ), it->tname(), it->magazine_current()->tname() ); p->assign_activity( ACT_HAND_CRANK, moves, -1, 0, "hand-cranking" ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.emplace_back( *p, it ); } else { p->add_msg_if_player( _( "You could use the %s to charge its %s, but it's already charged." ), it->tname(), magazine->tname() ); @@ -4645,7 +4634,7 @@ cata::optional iuse::vibe( player *p, item *it, bool, const tripoint & ) it->tname() ); } p->assign_activity( ACT_VIBE, moves, -1, 0, "de-stressing" ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.emplace_back( *p, it ); } return it->type->charges_to_use(); } @@ -4822,7 +4811,7 @@ cata::optional iuse::mind_splicer( player *p, item *it, bool, const tripoin skill_firstaid ) - 1 ) - 10_minutes * ( p->get_dex() - 8 ), 30_minutes ); player_activity act( ACT_MIND_SPLICER, to_moves( time ) ); - act.targets.push_back( item_location( *p, &data_card ) ); + act.targets.emplace_back( *p, &data_card ); p->assign_activity( act ); return it->type->charges_to_use(); } @@ -5004,6 +4993,8 @@ cata::optional iuse::oxytorch( player *p, item *it, bool, const tripoint & t_chainfence, t_chaingate_c, t_chaingate_l, + t_retractable_gate_l, + t_retractable_gate_c, t_bars, t_window_bars_alarm, t_window_bars, @@ -5020,7 +5011,8 @@ cata::optional iuse::oxytorch( player *p, item *it, bool, const tripoint & t_metal_grate_window_with_curtain_open, t_metal_grate_window_noglass, t_metal_grate_window_with_curtain_noglass, - t_metal_grate_window_with_curtain_open_noglass + t_metal_grate_window_with_curtain_open_noglass, + t_wall_metal }; const std::set allowed_furn_id { f_rack, @@ -5067,6 +5059,7 @@ cata::optional iuse::oxytorch( player *p, item *it, bool, const tripoint & } else if( ter == t_window_enhanced || ter == t_window_enhanced_noglass ) { turns = to_turns( 5_seconds ); } else if( ter == t_chainfence || ter == t_chaingate_c || + ter == t_retractable_gate_c || ter == t_retractable_gate_l || ter == t_chaingate_l || ter == t_bars || ter == t_window_bars_alarm || ter == t_window_bars || ter == t_window_bars_curtains || ter == t_window_domestic || ter == t_reb_cage || ter == t_metal_grate_window || ter == t_metal_grate_window_with_curtain || @@ -5075,8 +5068,9 @@ cata::optional iuse::oxytorch( player *p, item *it, bool, const tripoint & ter == t_metal_grate_window_with_curtain_open_noglass ) { turns = to_turns( 10_seconds ); } else if( ter == t_door_metal_locked || ter == t_door_metal_c || ter == t_door_bar_c || - ter == t_door_bar_locked || ter == t_door_metal_pickable || furn == f_safe_l || - furn == f_gunsafe_ml || furn == f_gunsafe_mj || furn == f_gun_safe_el ) { + ter == t_door_bar_locked || ter == t_door_metal_pickable || ter == t_wall_metal || + furn == f_safe_l || furn == f_gunsafe_ml || furn == f_gunsafe_mj || + furn == f_gun_safe_el ) { turns = to_turns( 15_seconds ); } else { return cata::nullopt; @@ -5092,7 +5086,7 @@ cata::optional iuse::oxytorch( player *p, item *it, bool, const tripoint & // placing ter here makes resuming tasks work better p->assign_activity( ACT_OXYTORCH, moves, static_cast( ter ) ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.emplace_back( *p, it ); p->activity.placement = pnt; p->activity.values.push_back( charges ); @@ -5116,6 +5110,8 @@ cata::optional iuse::hacksaw( player *p, item *it, bool t, const tripoint & t_chainfence, t_chaingate_c, t_chaingate_l, + t_retractable_gate_l, + t_retractable_gate_c, t_window_bars_alarm, t_window_bars, t_window_bars_curtains, @@ -5171,6 +5167,7 @@ cata::optional iuse::hacksaw( player *p, item *it, bool t, const tripoint & } else if( ter == t_window_enhanced || ter == t_window_enhanced_noglass ) { moves = to_moves( 5_minutes ); } else if( ter == t_chainfence || ter == t_chaingate_c || ter == t_chaingate_l || + ter == t_retractable_gate_c || ter == t_retractable_gate_l || ter == t_window_bars_alarm || ter == t_window_bars || ter == t_window_bars_curtains || ter == t_window_bars_domestic || ter == t_reb_cage || ter == t_metal_grate_window || ter == t_metal_grate_window_with_curtain || ter == t_metal_grate_window_with_curtain_open || @@ -5405,7 +5402,7 @@ static bool heat_item( player &p ) } p.add_msg_if_player( m_info, _( "You start heating up the food." ) ); p.assign_activity( ACT_HEATING, duration ); - p.activity.targets.push_back( item_location( p, target ) ); + p.activity.targets.emplace_back( p, target ); return true; } @@ -6132,6 +6129,17 @@ static void init_memory_card_with_random_stuff( item &it ) } } +static int get_quality_from_string( const std::string &s ) +{ + const ret_val try_quality = try_parse_integer( s, false ); + if( try_quality.success() ) { + return try_quality.value(); + } else { + debugmsg( "Error parsing photo quality: %s", try_quality.str() ); + return 0; + } +} + static bool einkpc_download_memory_card( player &p, item &eink, item &mc ) { bool something_downloaded = false; @@ -6248,22 +6256,22 @@ static bool einkpc_download_memory_card( player &p, item &eink, item &mc ) const std::string mtype = s; getline( f, s, ',' ); - char *chq = &s[0]; - const int quality = atoi( chq ); + const int quality = get_quality_from_string( s ); const size_t eink_strpos = photos.find( "," + mtype + "," ); if( eink_strpos == std::string::npos ) { photos += mtype + "," + string_format( "%d", quality ) + ","; } else { - const size_t strqpos = eink_strpos + mtype.size() + 2; - char *chq = &photos[strqpos]; - const int old_quality = atoi( chq ); + const size_t next_comma = photos.find( ',', strqpos ); + const int old_quality = + get_quality_from_string( photos.substr( strqpos, next_comma ) ); if( quality > old_quality ) { - chq = &string_format( "%d", quality )[0]; - photos[strqpos] = *chq; + const std::string quality_s = string_format( "%d", quality ); + cata_assert( quality_s.size() == 1 ); + photos[strqpos] = quality_s.front(); } } @@ -6512,13 +6520,12 @@ cata::optional iuse::einktabletpc( player *p, item *it, bool t, const tripo if( s.empty() ) { continue; } - monster_photos.push_back( mtype_id( s ) ); + monster_photos.emplace_back( s ); std::string menu_str; const monster dummy( monster_photos.back() ); menu_str = dummy.name(); getline( f, s, ',' ); - char *chq = &s[0]; - const int quality = atoi( chq ); + const int quality = get_quality_from_string( s ); menu_str += " [" + photo_quality_name( quality ) + "]"; pmenu.addentry( k++, true, -1, menu_str.c_str() ); } @@ -6942,8 +6949,12 @@ static object_names_collection enumerate_objects_around_point( const tripoint &p const std::string trap_name = colorized_trap_name_at( point_around_figure ); const std::string field_desc = colorized_field_description_at( point_around_figure ); + auto augment_description = [&]( std::string & desc ) { + desc = str_cat( trap_name, desc, field_desc ); + }; + if( !furn_desc.empty() ) { - furn_desc = trap_name + furn_desc + field_desc; + augment_description( furn_desc ); if( point == point_around_figure && create_figure_desc ) { description_furniture_on_figure = furn_desc; } else { @@ -6976,7 +6987,7 @@ static object_names_collection enumerate_objects_around_point( const tripoint &p local_vehicles_recorded.insert( veh_hash ); } else if( !item.is_null() ) { std::string item_name = colorized_item_name( item ); - item_name = trap_name + item_name + field_desc; + augment_description( item_name ); if( point == point_around_figure && create_figure_desc ) { //~ %1$s: terrain description, %2$s: item name description_terrain_on_figure = string_format( pgettext( "terrain and item", "%1$s with a %2$s" ), @@ -6985,21 +6996,21 @@ static object_names_collection enumerate_objects_around_point( const tripoint &p ret_obj.items[ item_name ] ++; } } else if( !unusual_ter_desc.empty() ) { - unusual_ter_desc = trap_name + unusual_ter_desc + field_desc; + augment_description( unusual_ter_desc ); if( point == point_around_figure && create_figure_desc ) { description_furniture_on_figure = unusual_ter_desc; } else { ret_obj.furniture[ unusual_ter_desc ] ++; } } else if( !ter_desc.empty() && ( !field_desc.empty() || !trap_name.empty() ) ) { - ter_desc = trap_name + ter_desc + field_desc; + augment_description( ter_desc ); if( point == point_around_figure && create_figure_desc ) { description_terrain_on_figure = ter_desc; } else { ret_obj.terrain[ ter_desc ] ++; } } else { - ter_desc = trap_name + ter_desc + field_desc; + augment_description( ter_desc ); if( point == point_around_figure && create_figure_desc ) { description_terrain_on_figure = ter_desc; } @@ -7089,7 +7100,11 @@ static extended_photo_def photo_def_for_camera_point( const tripoint &aim_point, } if( guy || mon ) { - std::string figure_appearance, figure_name, pose, pronoun_sex, figure_effects; + std::string figure_appearance; + std::string figure_name; + std::string pose; + std::string pronoun_sex; + std::string figure_effects; Creature *creature; if( mon && mon->has_effect( effect_ridden ) ) { // only player can ride, see monexamine::mount_pet @@ -7162,8 +7177,8 @@ 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 = here.veh_at( aim_point ).has_value(), - found_furniture_aim_point = !furn_desc.empty(); + bool found_vehicle_aim_point = here.veh_at( aim_point ).has_value(); + bool 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(); @@ -7329,11 +7344,14 @@ static void item_save_monsters( player &p, item &it, const std::vector old_quality ) { - monster_photos[ quality_num_pos ] = string_format( "%d", photo_quality )[ 0 ]; + const std::string quality_s = string_format( "%d", photo_quality ); + cata_assert( quality_s.size() == 1 ); + monster_photos[quality_num_pos] = quality_s.front(); } if( !p.is_blind() ) { if( photo_quality > old_quality ) { @@ -7638,7 +7656,7 @@ cata::optional iuse::camera( player *p, item *it, bool, const tripoint & ) continue; } - monster_photos.push_back( mtype_id( s ) ); + monster_photos.emplace_back( s ); std::string menu_str; @@ -7647,8 +7665,7 @@ cata::optional iuse::camera( player *p, item *it, bool, const tripoint & ) descriptions.push_back( dummy.type->get_description() ); getline( f_mon, s, ',' ); - char *chq = &s[0]; - const int quality = atoi( chq ); + const int quality = get_quality_from_string( s ); menu_str += " [" + photo_quality_name( quality ) + "]"; @@ -9135,8 +9152,7 @@ cata::optional iuse::shavekit( player *p, item *it, bool, const tripoint & if( !it->ammo_sufficient() ) { p->add_msg_if_player( _( "You need soap to use this." ) ); } else { - const int moves = to_moves( 5_minutes ); - p->assign_activity( ACT_SHAVE, moves ); + p->assign_activity( player_activity( shave_activity_actor() ) ); } return it->type->charges_to_use(); } @@ -9147,8 +9163,7 @@ cata::optional iuse::hairkit( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You can't do that while mounted." ) ); return cata::nullopt; } - const int moves = to_moves( 30_minutes ); - p->assign_activity( ACT_HAIRCUT, moves ); + p->assign_activity( player_activity( haircut_activity_actor() ) ); return it->type->charges_to_use(); } @@ -9600,7 +9615,7 @@ cata::optional iuse::break_stick( player *p, item *it, bool, const tripoint return 0; } std::vector comps; - comps.push_back( item_comp( it->typeId(), 1 ) ); + comps.emplace_back( it->typeId(), 1 ); p->consume_items( comps, 1, is_crafting_component ); int chance = rng( 0, 100 ); map &here = get_map(); @@ -9675,7 +9690,8 @@ cata::optional iuse::craft( player *p, item *it, bool, const tripoint & ) return cata::nullopt; } const recipe &rec = it->get_making(); - if( p->has_recipe( &rec, p->crafting_inventory(), p->get_crafting_helpers() ) == -1 ) { + const inventory &inv = p->crafting_inventory(); + if( p->has_recipe( &rec, inv, p->get_crafting_helpers() ) == -1 ) { p->add_msg_player_or_npc( _( "You don't know the recipe for the %s and can't continue crafting." ), _( " doesn't know the recipe for the %s and can't continue crafting." ), diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index fb346858352df..01fba89c81ce6 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -1116,7 +1116,7 @@ void reveal_map_actor::load( const JsonObject &obj ) ter_match_type = jo.get_enum_value( "om_terrain_match_type", ot_match_type::contains ); } - omt_types.push_back( std::make_pair( ter, ter_match_type ) ); + omt_types.emplace_back( ter, ter_match_type ); } } @@ -1319,7 +1319,7 @@ cata::optional firestarter_actor::use( player &p, item &it, bool t, moves_modifier + moves_cost_fast / 100.0 + 2; p.assign_activity( ACT_START_FIRE, moves, potential_skill_gain, 0, it.tname() ); - p.activity.targets.push_back( item_location( p, &it ) ); + p.activity.targets.emplace_back( p, &it ); p.activity.values.push_back( g->natural_light_level( pos.z ) ); p.activity.placement = pos; // charges to use are handled by the activity @@ -3136,7 +3136,7 @@ static player &get_patient( player &healer, const tripoint &pos ) player *const person = g->critter_at( pos ); if( !person ) { // Default to heal self on failure not to break old functionality - add_msg_debug( "No heal target at position %d,%d,%d", pos.x, pos.y, pos.z ); + add_msg_debug( debugmode::DF_IUSE, "No heal target at position %d,%d,%d", pos.x, pos.y, pos.z ); return healer; } @@ -3176,8 +3176,8 @@ cata::optional heal_actor::use( player &p, item &it, bool, const tripoint & // Assign first aid long action. /** @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.str_values.push_back( hpp.c_str() ); + p.activity.targets.emplace_back( p, &it ); + p.activity.str_values.emplace_back( hpp.c_str() ); p.moves = 0; return 0; } @@ -3927,9 +3927,13 @@ cata::optional detach_gunmods_actor::use( player &p, item &it, bool, const if( prompt.ret >= 0 ) { gun_copy.remove_item( *mods_copy[prompt.ret] ); - if( game_menus::inv::compare_items( it, gun_copy, _( "Remove modification?" ) ) ) { - p.gunmod_remove( it, *mods[ prompt.ret ] ); - return 0; + if( p.meets_requirements( *mods[prompt.ret], gun_copy ) || + query_yn( _( "Are you sure? You may be lacking the skills needed to reattach this modification." ) ) ) { + + if( game_menus::inv::compare_items( it, gun_copy, _( "Remove modification?" ) ) ) { + p.gunmod_remove( it, *mods[prompt.ret] ); + return 0; + } } } @@ -4240,7 +4244,7 @@ void sew_advanced_actor::load( const JsonObject &obj ) materials.emplace( line ); } for( const std::string line : obj.get_array( "clothing_mods" ) ) { - clothing_mods.push_back( clothing_mod_id( line ) ); + clothing_mods.emplace_back( line ); } // TODO: Make skill non-mandatory while still erroring on invalid skill @@ -4424,7 +4428,7 @@ cata::optional sew_advanced_actor::use( player &p, item &it, bool, const tr const auto &repair_item = clothing_mods[choice].obj().item_string; std::vector comps; - comps.push_back( item_comp( repair_item, items_needed ) ); + comps.emplace_back( repair_item, items_needed ); p.moves -= to_moves( 30_seconds * p.fine_detail_vision_mod() ); p.practice( used_skill, items_needed * 3 + 3 ); /** @EFFECT_TAILOR randomly improves clothing modification efforts */ diff --git a/src/iuse_software_lightson.cpp b/src/iuse_software_lightson.cpp index c2d31d366fe64..5da853e453317 100644 --- a/src/iuse_software_lightson.cpp +++ b/src/iuse_software_lightson.cpp @@ -154,9 +154,9 @@ int lightson_game::start_game() ui.on_redraw( [&]( const ui_adaptor & ) { std::vector shortcuts; - shortcuts.push_back( _( " toggle lights" ) ); - shortcuts.push_back( _( "eset" ) ); - shortcuts.push_back( _( "uit" ) ); + shortcuts.emplace_back( _( " toggle lights" ) ); + shortcuts.emplace_back( _( "eset" ) ); + shortcuts.emplace_back( _( "uit" ) ); int iWidth = 0; for( auto &shortcut : shortcuts ) { diff --git a/src/iuse_software_minesweeper.cpp b/src/iuse_software_minesweeper.cpp index 3b48b64ed08c1..b5024541f3717 100644 --- a/src/iuse_software_minesweeper.cpp +++ b/src/iuse_software_minesweeper.cpp @@ -91,15 +91,14 @@ void minesweeper_game::new_level() mLevel.clear(); mLevelReveal.clear(); - int iRandX; - int iRandY; + point iRand; for( int i = 0; i < iBombs; i++ ) { do { - iRandX = rng( 0, level.x - 1 ); - iRandY = rng( 0, level.y - 1 ); - } while( mLevel[iRandY][iRandX] == bomb ); + iRand.x = rng( 0, level.x - 1 ); + iRand.y = rng( 0, level.y - 1 ); + } while( mLevel[iRand.y][iRand.x] == bomb ); - mLevel[iRandY][iRandX] = bomb; + mLevel[iRand.y][iRand.x] = bomb; } for( int y = 0; y < level.y; y++ ) { @@ -183,9 +182,9 @@ int minesweeper_game::start_game() draw_border( w_minesweeper_border ); std::vector shortcuts; - shortcuts.push_back( _( "ew level" ) ); - shortcuts.push_back( _( "lag" ) ); - shortcuts.push_back( _( "uit" ) ); + shortcuts.emplace_back( _( "ew level" ) ); + shortcuts.emplace_back( _( "lag" ) ); + shortcuts.emplace_back( _( "uit" ) ); int iWidth = 0; for( auto &shortcut : shortcuts ) { @@ -312,11 +311,10 @@ int minesweeper_game::start_game() } if( const cata::optional vec = ctxt.get_direction( action ) ) { - const int new_x = iPlayerX + vec->x; - const int new_y = iPlayerY + vec->y; - if( new_x >= 0 && new_x < level.x && new_y >= 0 && new_y < level.y ) { - iPlayerX = new_x; - iPlayerY = new_y; + const point new_( vec->xy() + point( iPlayerX, iPlayerY ) ); + if( new_.x >= 0 && new_.x < level.x && new_.y >= 0 && new_.y < level.y ) { + iPlayerX = new_.x; + iPlayerY = new_.y; } } else if( action == "FLAG" ) { if( mLevelReveal[iPlayerY][iPlayerX] == unknown ) { diff --git a/src/iuse_software_snake.cpp b/src/iuse_software_snake.cpp index a5baba6558447..0ae65f9f414c2 100644 --- a/src/iuse_software_snake.cpp +++ b/src/iuse_software_snake.cpp @@ -61,18 +61,18 @@ void snake_game::snake_over( const catacurses::window &w_snake, int iScore ) mvwputch( w_snake, point( 71, body_length + 2 ), c_green, 'v' ); std::vector game_over_text; - game_over_text.push_back( R"( ________ _____ _____ ___________)" ); - game_over_text.push_back( R"( / _____/ / _ \ / \ \_ _____/)" ); - game_over_text.push_back( R"(/ \ ___ / /_\ \ / \ / \ | __)_ )" ); - game_over_text.push_back( R"(\ \_\ \/ | \/ Y \ | \)" ); - game_over_text.push_back( R"( \______ /\____|__ /\____|__ //_______ /)" ); - game_over_text.push_back( R"( \/ \/ \/ \/ )" ); - game_over_text.push_back( R"( ________ ____ _________________________ )" ); - game_over_text.push_back( R"( \_____ \\ \ / /\_ _____/\______ \ )" ); - game_over_text.push_back( R"( / | \\ Y / | __)_ | _/ )" ); - game_over_text.push_back( R"( / | \\ / | \ | | \ )" ); - game_over_text.push_back( R"( \_______ / \___/ /_______ / |____|_ / )" ); - game_over_text.push_back( R"( \/ \/ \/ )" ); + game_over_text.emplace_back( R"( ________ _____ _____ ___________)" ); + game_over_text.emplace_back( R"( / _____/ / _ \ / \ \_ _____/)" ); + game_over_text.emplace_back( R"(/ \ ___ / /_\ \ / \ / \ | __)_ )" ); + game_over_text.emplace_back( R"(\ \_\ \/ | \/ Y \ | \)" ); + game_over_text.emplace_back( R"( \______ /\____|__ /\____|__ //_______ /)" ); + game_over_text.emplace_back( R"( \/ \/ \/ \/ )" ); + game_over_text.emplace_back( R"( ________ ____ _________________________ )" ); + game_over_text.emplace_back( R"( \_____ \\ \ / /\_ _____/\______ \ )" ); + game_over_text.emplace_back( R"( / | \\ Y / | __)_ | _/ )" ); + game_over_text.emplace_back( R"( / | \\ / | \ | | \ )" ); + game_over_text.emplace_back( R"( \_______ / \___/ /_______ / |____|_ / )" ); + game_over_text.emplace_back( R"( \/ \/ \/ )" ); for( size_t i = 0; i < game_over_text.size(); i++ ) { mvwprintz( w_snake, point( 17, i + 3 ), c_light_red, game_over_text[i] ); @@ -103,7 +103,7 @@ int snake_game::start_game() ui.mark_resize(); //Snake start position - vSnakeBody.push_back( std::make_pair( FULL_SCREEN_HEIGHT / 2, FULL_SCREEN_WIDTH / 2 ) ); + vSnakeBody.emplace_back( FULL_SCREEN_HEIGHT / 2, FULL_SCREEN_WIDTH / 2 ); mSnakeBody[FULL_SCREEN_HEIGHT / 2][FULL_SCREEN_WIDTH / 2] = true; //Snake start direction @@ -144,28 +144,28 @@ int snake_game::start_game() do { //Check if we hit a border if( vSnakeBody[vSnakeBody.size() - 1].first + iDirY == 0 ) { - vSnakeBody.push_back( std::make_pair( vSnakeBody[vSnakeBody.size() - 1].first + - iDirY + FULL_SCREEN_HEIGHT - 2, - vSnakeBody[vSnakeBody.size() - 1].second + iDirX ) ); + vSnakeBody.emplace_back( vSnakeBody[vSnakeBody.size() - 1].first + + iDirY + FULL_SCREEN_HEIGHT - 2, + vSnakeBody[vSnakeBody.size() - 1].second + iDirX ); } else if( vSnakeBody[vSnakeBody.size() - 1].first + iDirY == FULL_SCREEN_HEIGHT - 1 ) { - vSnakeBody.push_back( std::make_pair( vSnakeBody[vSnakeBody.size() - 1].first + - iDirY - FULL_SCREEN_HEIGHT + 2, - vSnakeBody[vSnakeBody.size() - 1].second + iDirX ) ); + vSnakeBody.emplace_back( vSnakeBody[vSnakeBody.size() - 1].first + + iDirY - FULL_SCREEN_HEIGHT + 2, + vSnakeBody[vSnakeBody.size() - 1].second + iDirX ); } else if( vSnakeBody[vSnakeBody.size() - 1].second + iDirX == 0 ) { - vSnakeBody.push_back( std::make_pair( vSnakeBody[vSnakeBody.size() - 1].first + iDirY, - vSnakeBody[vSnakeBody.size() - 1].second + - iDirX + FULL_SCREEN_WIDTH - 2 ) ); + vSnakeBody.emplace_back( vSnakeBody[vSnakeBody.size() - 1].first + iDirY, + vSnakeBody[vSnakeBody.size() - 1].second + + iDirX + FULL_SCREEN_WIDTH - 2 ); } else if( vSnakeBody[vSnakeBody.size() - 1].second + iDirX == FULL_SCREEN_WIDTH - 1 ) { - vSnakeBody.push_back( std::make_pair( vSnakeBody[vSnakeBody.size() - 1].first + iDirY, - vSnakeBody[vSnakeBody.size() - 1].second + - iDirX - FULL_SCREEN_WIDTH + 2 ) ); + vSnakeBody.emplace_back( vSnakeBody[vSnakeBody.size() - 1].first + iDirY, + vSnakeBody[vSnakeBody.size() - 1].second + + iDirX - FULL_SCREEN_WIDTH + 2 ); } else { - vSnakeBody.push_back( std::make_pair( vSnakeBody[vSnakeBody.size() - 1].first + iDirY, - vSnakeBody[vSnakeBody.size() - 1].second + iDirX ) ); + vSnakeBody.emplace_back( vSnakeBody[vSnakeBody.size() - 1].first + iDirY, + vSnakeBody[vSnakeBody.size() - 1].second + iDirX ); } //Check if we hit ourselves diff --git a/src/iuse_software_sokoban.cpp b/src/iuse_software_sokoban.cpp index d321c0b39baec..bd46d21555229 100644 --- a/src/iuse_software_sokoban.cpp +++ b/src/iuse_software_sokoban.cpp @@ -248,11 +248,11 @@ int sokoban_game::start_game() draw_border( w_sokoban, BORDER_COLOR, _( "Sokoban" ), hilite( c_white ) ); std::vector shortcuts; - shortcuts.push_back( _( "<+> next" ) ); // '+': next - shortcuts.push_back( _( "<-> prev" ) ); // '-': prev - shortcuts.push_back( _( "eset" ) ); // 'r': reset - shortcuts.push_back( _( "uit" ) ); // 'q': quit - shortcuts.push_back( _( "ndo move" ) ); // 'u': undo move + shortcuts.emplace_back( _( "<+> next" ) ); // '+': next + shortcuts.emplace_back( _( "<-> prev" ) ); // '-': prev + shortcuts.emplace_back( _( "eset" ) ); // 'r': reset + shortcuts.emplace_back( _( "uit" ) ); // 'q': quit + shortcuts.emplace_back( _( "ndo move" ) ); // 'u': undo move int indent = 10; for( auto &shortcut : shortcuts ) { @@ -378,7 +378,7 @@ int sokoban_game::start_game() bMovePlayer = true; mLevel[iPlayerY + iDirY * 2][iPlayerX + iDirX * 2] = sMovePackTo == "." ? "*" : "$"; - vUndo.push_back( cUndo( point( iDirX, iDirY ), sMoveTo ) ); + vUndo.emplace_back( point( iDirX, iDirY ), sMoveTo ); iMoves--; } @@ -388,7 +388,7 @@ int sokoban_game::start_game() if( bMovePlayer ) { //move player - vUndo.push_back( cUndo( point( iPlayerX, iPlayerY ), mLevel[iPlayerY][iPlayerX] ) ); + vUndo.emplace_back( point( iPlayerX, iPlayerY ), mLevel[iPlayerY][iPlayerX] ); mLevel[iPlayerY][iPlayerX] = mLevel[iPlayerY][iPlayerX] == "+" ? "." : " "; mLevel[iPlayerY + iDirY][iPlayerX + iDirX] = sMoveTo == "." || sMoveTo == "*" ? "+" : "@"; diff --git a/src/json.cpp b/src/json.cpp index 0f2df3195ee3b..b6657d2301cc6 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -747,6 +747,33 @@ void add_array_to_set( std::set &s, const JsonObject &json, const s } } +JsonIn::JsonIn( std::istream &s ) : stream( &s ) +{ + sanity_check_stream(); +} + +JsonIn::JsonIn( std::istream &s, const std::string &path ) + : stream( &s ) + , path( make_shared_fast( path ) ) +{ + sanity_check_stream(); +} + +JsonIn::JsonIn( std::istream &s, const json_source_location &loc ) + : stream( &s ), path( loc.path ) +{ + seek( loc.offset ); + sanity_check_stream(); +} + +void JsonIn::sanity_check_stream() +{ + char c = stream->peek(); + if( c == '\xef' ) { + error( _( "This JSON file looks like it starts with a Byte Order Mark (BOM) or is otherwise corrupted. This can happen if you edit files in Windows Notepad. See doc/CONTRIBUTING.md for more advice." ) ); + } +} + int JsonIn::tell() { return stream->tellg(); diff --git a/src/json.h b/src/json.h index 550b12086d25f..f192a81828c87 100644 --- a/src/json.h +++ b/src/json.h @@ -189,18 +189,15 @@ class JsonIn shared_ptr_fast path; bool ate_separator = false; + void sanity_check_stream(); void skip_separator(); void skip_pair_separator(); void end_value(); public: - explicit JsonIn( std::istream &s ) : stream( &s ) {} - JsonIn( std::istream &s, const std::string &path ) - : stream( &s ), path( make_shared_fast( path ) ) {} - JsonIn( std::istream &s, const json_source_location &loc ) - : stream( &s ), path( loc.path ) { - seek( loc.offset ); - } + explicit JsonIn( std::istream &s ); + JsonIn( std::istream &s, const std::string &path ); + JsonIn( std::istream &s, const json_source_location &loc ); JsonIn( const JsonIn & ) = delete; JsonIn &operator=( const JsonIn & ) = delete; @@ -466,7 +463,11 @@ class JsonIn while( !end_array() ) { typename T::value_type element; if( read( element, throw_on_error ) ) { - v.insert( std::move( element ) ); + if( !v.insert( std::move( element ) ).second ) { + return error_or_false( + throw_on_error, + "Duplicate entry in set defined by json array" ); + } } else { skip_value(); } @@ -1441,7 +1442,9 @@ Res JsonArray::get_tags( const size_t index ) const } for( const std::string line : jsin->get_array() ) { - res.insert( T( line ) ); + if( !res.insert( T( line ) ).second ) { + jsin->error( "duplicate item in set defined by json array" ); + } } return res; @@ -1466,7 +1469,9 @@ Res JsonObject::get_tags( const std::string &name ) const // otherwise assume it's an array and error if it isn't. for( const std::string line : jsin->get_array() ) { - res.insert( T( line ) ); + if( !res.insert( T( line ) ).second ) { + jsin->error( "duplicate item in set defined by json array" ); + } } return res; diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 13a73f439db0a..160b8ee8a588a 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -155,7 +155,8 @@ bool map::build_transparency_cache( const int zlev ) }; if( cur_submap->is_uniform ) { - float value, dummy; + float value; + float dummy; std::tie( value, dummy ) = calc_transp( sm_offset ); // if rebuild_all==true all values were already set to LIGHT_TRANSPARENCY_OPEN_AIR if( !rebuild_all || value != LIGHT_TRANSPARENCY_OPEN_AIR ) { @@ -351,17 +352,16 @@ void map::build_sunlight_cache( int pzlev ) for( int y = 0; y < MAPSIZE_Y; ++y ) { // Check center, then four adjacent cardinals. for( int i = 0; i < 5; ++i ) { - int prev_x = x + offset.x + cardinals[i].x; - int prev_y = y + offset.y + cardinals[i].y; - bool inbounds = prev_x >= 0 && prev_x < MAPSIZE_X && - prev_y >= 0 && prev_y < MAPSIZE_Y; + point prev( cardinals[i] + offset + point( x, y ) ); + bool inbounds = prev.x >= 0 && prev.x < MAPSIZE_X && + prev.y >= 0 && prev.y < MAPSIZE_Y; if( !inbounds ) { continue; } float prev_light_max; - float prev_transparency = prev_transparency_cache[prev_x][prev_y]; + float prev_transparency = prev_transparency_cache[prev.x][prev.y]; // This is pretty gross, this cancels out the per-tile transparency effect // derived from weather. if( outside_cache[x][y] ) { @@ -369,8 +369,8 @@ void map::build_sunlight_cache( int pzlev ) } if( prev_transparency > LIGHT_TRANSPARENCY_SOLID && - !prev_floor_cache[prev_x][prev_y] && - ( prev_light_max = prev_lm[prev_x][prev_y].max() ) > 0.0 ) { + !prev_floor_cache[prev.x][prev.y] && + ( prev_light_max = prev_lm[prev.x][prev.y].max() ) > 0.0 ) { const float light_level = clamp( prev_light_max * LIGHT_TRANSPARENCY_OPEN_AIR / prev_transparency, inside_light_level, prev_light_max ); @@ -445,9 +445,8 @@ void map::generate_lightmap( const int zlev ) for( int sx = 0; sx < SEEX; ++sx ) { for( int sy = 0; sy < SEEY; ++sy ) { - const int x = sx + smx * SEEX; - const int y = sy + smy * SEEY; - const tripoint p( x, y, zlev ); + const point p2( sx + smx * SEEX, sy + smy * SEEY ); + const tripoint p( p2, zlev ); // Project light into any openings into buildings. if( !outside_cache[p.x][p.y] || ( !top_floor && prev_floor_cache[p.x][p.y] ) ) { // Apply light sources for external/internal divide @@ -492,7 +491,7 @@ void map::generate_lightmap( const int zlev ) } const float light_override = cur->get_intensity_level().local_light_override; if( light_override >= 0.0f ) { - lm_override.push_back( std::pair( p, light_override ) ); + lm_override.emplace_back( p, light_override ); } } } diff --git a/src/line.cpp b/src/line.cpp index 98bf894853642..4c3959731b03f 100644 --- a/src/line.cpp +++ b/src/line.cpp @@ -548,16 +548,16 @@ std::vector squares_in_direction( const point &p1, const point &p2 ) adjacent_squares.push_back( center_square ); if( p1.x == center_square.x ) { // Horizontally adjacent. - adjacent_squares.push_back( point( p1.x + 1, center_square.y ) ); - adjacent_squares.push_back( point( p1.x - 1, center_square.y ) ); + adjacent_squares.emplace_back( p1.x + 1, center_square.y ); + adjacent_squares.emplace_back( p1.x - 1, center_square.y ); } else if( p1.y == center_square.y ) { // Vertically adjacent. - adjacent_squares.push_back( point( center_square.x, p1.y + 1 ) ); - adjacent_squares.push_back( point( center_square.x, p1.y - 1 ) ); + adjacent_squares.emplace_back( center_square.x, p1.y + 1 ); + adjacent_squares.emplace_back( center_square.x, p1.y - 1 ); } else { // Diagonally adjacent. - adjacent_squares.push_back( point( p1.x, center_square.y ) ); - adjacent_squares.push_back( point( center_square.x, p1.y ) ); + adjacent_squares.emplace_back( p1.x, center_square.y ); + adjacent_squares.emplace_back( center_square.x, p1.y ); } return adjacent_squares; } diff --git a/src/list.h b/src/list.h index 4a8a5bdc690a1..8126a026c68b6 100644 --- a/src/list.h +++ b/src/list.h @@ -495,8 +495,9 @@ template = block_pointer ); @@ -510,8 +511,9 @@ template free_list_head == nullptr ) ? nullptr : - last_searched_group, closest_freelist_right = ( last_searched_group->free_list_head == nullptr ) ? - nullptr : last_searched_group; + last_searched_group; + group_pointer_type closest_freelist_right = ( last_searched_group->free_list_head == nullptr ) ? + nullptr : last_searched_group; while( true ) { if( right_not_beyond_back ) { @@ -653,10 +655,12 @@ template ( this ), static_cast( &source ), sizeof( group_vector ) ); std::memcpy( static_cast( &source ), static_cast( &temp ), sizeof( group_vector ) ); } else { - const group_pointer_type swap_last_endpoint_group = last_endpoint_group, - swap_block_pointer = block_pointer, swap_last_searched_group = last_searched_group; - const size_type swap_size = size, swap_element_capacity = element_allocator_pair.capacity, - swap_capacity = group_allocator_pair.capacity; + const group_pointer_type swap_last_endpoint_group = last_endpoint_group; + const group_pointer_type swap_block_pointer = block_pointer; + const group_pointer_type swap_last_searched_group = last_searched_group; + const size_type swap_size = size; + const size_type swap_element_capacity = element_allocator_pair.capacity; + const size_type swap_capacity = group_allocator_pair.capacity; last_endpoint_group = source.last_endpoint_group; block_pointer = source.block_pointer; @@ -2201,11 +2205,11 @@ template next, - current2 = source.begin_iterator.node_pointer->next; + node_pointer_type current1 = begin_iterator.node_pointer->next; + node_pointer_type current2 = source.begin_iterator.node_pointer->next; node_pointer_type previous = source.begin_iterator.node_pointer; - const node_pointer_type source_end = source.end_iterator.node_pointer, - this_end = end_iterator.node_pointer; + const node_pointer_type source_end = source.end_iterator.node_pointer; + const node_pointer_type this_end = end_iterator.node_pointer; begin_iterator.node_pointer->next = source.begin_iterator.node_pointer; source.begin_iterator.node_pointer->previous = begin_iterator.node_pointer; diff --git a/src/magic.cpp b/src/magic.cpp index f988ccad0c60f..8f5344a1b16a1 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -8,6 +8,7 @@ #include #include +#include "avatar.h" #include "calendar.h" #include "cata_utility.h" #include "catacharset.h" @@ -33,14 +34,17 @@ #include "make_static.h" #include "magic_enchantment.h" #include "map.h" +#include "map_iterator.h" #include "messages.h" #include "mongroup.h" #include "monster.h" #include "mtype.h" #include "mutation.h" +#include "npc.h" #include "output.h" #include "pimpl.h" #include "point.h" +#include "projectile.h" #include "requirements.h" #include "rng.h" #include "sounds.h" @@ -149,7 +153,6 @@ std::string enum_to_string( magic_energy_type data ) { switch( data ) { case magic_energy_type::bionic: return "BIONIC"; - case magic_energy_type::fatigue: return "FATIGUE"; case magic_energy_type::hp: return "HP"; case magic_energy_type::mana: return "MANA"; case magic_energy_type::none: return "NONE"; @@ -565,6 +568,18 @@ int spell::min_leveled_damage() const return type->min_damage + std::round( get_level() * type->damage_increment ); } +float spell::dps( const Character &caster, const Creature & ) const +{ + if( type->effect_name != "attack" ) { + return 0.0f; + } + const float time_modifier = 100.0f / casting_time( caster ); + const float failure_modifier = 1.0f - spell_fail( caster ); + const float raw_dps = damage() + damage_dot() * duration_turns() / 1_turns; + // TODO: calculate true dps with armor and resistances and any caster bonuses + return raw_dps * time_modifier * failure_modifier; +} + int spell::damage() const { const int leveled_damage = min_leveled_damage(); @@ -679,6 +694,45 @@ int spell::range() const } } +std::vector spell::targetable_locations( const Character &source ) const +{ + + const tripoint char_pos = source.pos(); + const bool select_ground = is_valid_target( spell_target::ground ); + const bool ignore_walls = has_flag( spell_flag::NO_PROJECTILE ); + map &here = get_map(); + + // TODO: put this in a namespace for reuse + const auto has_obstruction = [&]( const tripoint & at ) { + for( const tripoint &line_point : line_to( char_pos, at ) ) { + if( here.impassable( line_point ) ) { + return true; + } + } + return false; + }; + + std::vector selectable_targets; + for( const tripoint &query : here.points_in_radius( char_pos, range() ) ) { + if( !ignore_walls && has_obstruction( query ) ) { + // it's blocked somewhere! + continue; + } + + if( !select_ground ) { + if( !source.sees( query ) ) { + // can't target a critter you can't see + continue; + } + } + + if( is_valid_target( source, query ) ) { + selectable_targets.push_back( query ); + } + } + return selectable_targets; +} + int spell::min_leveled_duration() const { return type->min_duration + std::round( get_level() * type->duration_increment ); @@ -792,14 +846,17 @@ bool spell::is_spell_class( const trait_id &mid ) const return mid == type->spell_class; } -bool spell::can_cast( Character &guy ) const +bool spell::can_cast( const Character &guy ) const { if( guy.has_trait_flag( STATIC( json_character_flag( "NO_SPELLCASTING" ) ) ) ) { return false; } + // only required because crafting_inventory always rebuilds the cache. maybe a const version doesn't write to cache. + Character &guy_inv = const_cast( guy ); + if( !type->spell_components.is_empty() && - !type->spell_components->can_make_with_inventory( guy.crafting_inventory( guy.pos(), 0 ), + !type->spell_components->can_make_with_inventory( guy_inv.crafting_inventory( guy.pos(), 0 ), return_true ) ) { return false; } @@ -989,8 +1046,6 @@ std::string spell::energy_string() const return _( "stamina" ); case magic_energy_type::bionic: return _( "kJ" ); - case magic_energy_type::fatigue: - return _( "fatigue" ); default: return ""; } @@ -1012,9 +1067,6 @@ std::string spell::energy_cost_string( const Character &guy ) const auto pair = get_hp_bar( energy_cost( guy ), guy.get_stamina_max() ); return colorize( pair.first, pair.second ); } - if( energy_source() == magic_energy_type::fatigue ) { - return colorize( std::to_string( energy_cost( guy ) ), c_cyan ); - } debugmsg( "ERROR: Spell %s has invalid energy source.", id().c_str() ); return _( "error: energy_type" ); } @@ -1037,10 +1089,6 @@ std::string spell::energy_cur_string( const Character &guy ) const if( energy_source() == magic_energy_type::hp ) { return ""; } - if( energy_source() == magic_energy_type::fatigue ) { - const std::pair pair = guy.get_fatigue_description(); - return colorize( pair.first, pair.second ); - } debugmsg( "ERROR: Spell %s has invalid energy source.", id().c_str() ); return _( "error: energy_type" ); } @@ -1076,13 +1124,22 @@ void spell::create_field( const tripoint &at ) const } } -void spell::make_sound( const tripoint &target ) const +int spell::sound_volume() const { + int loudness = 0; if( !has_flag( spell_flag::SILENT ) ) { - int loudness = std::abs( damage() ) / 3; + loudness = std::abs( damage() ) / 3; if( has_flag( spell_flag::LOUD ) ) { loudness += 1 + damage() / 3; } + } + return loudness; +} + +void spell::make_sound( const tripoint &target ) const +{ + const int loudness = sound_volume(); + if( loudness > 0 ) { make_sound( target, loudness ); } } @@ -1203,7 +1260,7 @@ int spell::get_max_level() const // helper function to calculate xp needed to be at a certain level // pulled out as a helper function to make it easier to either be used in the future // or easier to tweak the formula -static int exp_for_level( int level ) +int spell::exp_for_level( int level ) { // level 0 never needs xp if( level == 0 ) { @@ -1307,6 +1364,23 @@ dealt_damage_instance spell::get_dealt_damage_instance() const return dmg; } +dealt_projectile_attack spell::get_projectile_attack( const tripoint &target, + Creature &hit_critter ) const +{ + projectile bolt; + bolt.speed = 10000; + bolt.impact = get_damage_instance(); + bolt.proj_effects.emplace( "magic" ); + + dealt_projectile_attack atk; + atk.end_point = target; + atk.hit_critter = &hit_critter; + atk.proj = bolt; + atk.missed_by = 0.0; + + return atk; +} + std::string spell::effect_data() const { return type->effect_str; @@ -1645,8 +1719,6 @@ bool known_magic::has_enough_energy( const Character &guy, const spell &sp ) con } } return false; - case magic_energy_type::fatigue: - return guy.get_fatigue() < fatigue_levels::EXHAUSTED; case magic_energy_type::none: return true; default: @@ -1753,7 +1825,7 @@ class spellcasting_callback : public uilist_callback } }; -static bool casting_time_encumbered( const spell &sp, const Character &guy ) +bool spell_desc::casting_time_encumbered( const spell &sp, const Character &guy ) { int encumb = 0; if( !sp.has_flag( spell_flag::NO_LEGS ) ) { @@ -1769,7 +1841,7 @@ static bool casting_time_encumbered( const spell &sp, const Character &guy ) return encumb > 0; } -static bool energy_cost_encumbered( const spell &sp, const Character &guy ) +bool spell_desc::energy_cost_encumbered( const spell &sp, const Character &guy ) { if( !sp.has_flag( spell_flag::NO_HANDS ) ) { return std::max( 0, guy.encumb( bodypart_id( "hand_l" ) ) + guy.encumb( @@ -1781,7 +1853,7 @@ static bool energy_cost_encumbered( const spell &sp, const Character &guy ) // this prints various things about the spell out in a list // including flags and things like "goes through walls" -static std::string enumerate_spell_data( const spell &sp ) +std::string spell_desc::enumerate_spell_data( const spell &sp ) { std::vector spell_data; if( sp.has_flag( spell_flag::CONCENTRATE ) ) { @@ -1832,7 +1904,7 @@ void spellcasting_callback::draw_spell_info( const spell &sp, const uilist *menu line++; line += fold_and_print( w_menu, point( h_col1, line ), info_width, gray, - enumerate_spell_data( sp ) ); + spell_desc::enumerate_spell_data( sp ) ); if( line <= win_height / 3 ) { line++; } @@ -1858,7 +1930,7 @@ void spellcasting_callback::draw_spell_info( const spell &sp, const uilist *menu line++; } - const bool cost_encumb = energy_cost_encumbered( sp, player_character ); + const bool cost_encumb = spell_desc::energy_cost_encumbered( sp, player_character ); std::string cost_string = cost_encumb ? _( "Casting Cost (impeded)" ) : _( "Casting Cost" ); std::string energy_cur = sp.energy_source() == magic_energy_type::hp ? "" : string_format( _( " (%s current)" ), sp.energy_cur_string( player_character ) ); @@ -1869,7 +1941,7 @@ void spellcasting_callback::draw_spell_info( const spell &sp, const uilist *menu print_colored_text( w_menu, point( h_col1, line++ ), gray, gray, string_format( "%s: %s %s%s", cost_string, sp.energy_cost_string( player_character ), sp.energy_string(), energy_cur ) ); - const bool c_t_encumb = casting_time_encumbered( sp, player_character ); + const bool c_t_encumb = spell_desc::casting_time_encumbered( sp, player_character ); print_colored_text( w_menu, point( h_col1, line++ ), gray, gray, colorize( string_format( "%s: %s", c_t_encumb ? _( "Casting Time (impeded)" ) : _( "Casting Time" ), moves_to_string( sp.casting_time( player_character ) ) ), @@ -2316,7 +2388,7 @@ spell fake_spell::get_spell( int min_level_override ) const debugmsg( "ERROR: fake spell %s has higher min_level than max_level", id.c_str() ); return sp; } - sp.set_exp( exp_for_level( level_of_spell ) ); + sp.set_exp( sp.exp_for_level( level_of_spell ) ); return sp; } diff --git a/src/magic.h b/src/magic.h index 9480ab9768a0d..7a47b9e271845 100644 --- a/src/magic.h +++ b/src/magic.h @@ -30,6 +30,7 @@ class JsonOut; class nc_color; class spell; class time_duration; +struct dealt_projectile_attack; struct requirement_data; namespace spell_effect @@ -81,7 +82,6 @@ enum class magic_energy_type : int { mana, stamina, bionic, - fatigue, none, last }; @@ -286,7 +286,7 @@ class spell_type // increment of energy cost per spell level float energy_increment = 0.0f; // max or min energy cost, based on sign of energy_increment - int final_energy_cost = 0.0f; + int final_energy_cost = 0; // spell is restricted to being cast by only this class // if spell_class is empty, spell is unrestricted @@ -380,6 +380,14 @@ class spell_type static const float casting_time_increment_default; }; +// functions for spell description +namespace spell_desc +{ +bool casting_time_encumbered( const spell &sp, const Character &guy ); +bool energy_cost_encumbered( const spell &sp, const Character &guy ); +std::string enumerate_spell_data( const spell &sp ); +} // namespace spell_desc + class spell { private: @@ -410,6 +418,7 @@ class spell // sets the message to be different than the spell_type specifies void set_message( const translation &msg ); + static int exp_for_level( int level ); // how much exp you need for the spell to gain a level int exp_to_next_level() const; // progress to the next level, expressed as a percent @@ -441,7 +450,11 @@ class spell int damage_dot() const; damage_over_time_data damage_over_time( const std::vector &bps ) const; dealt_damage_instance get_dealt_damage_instance() const; + dealt_projectile_attack get_projectile_attack( const tripoint &target, + Creature &hit_critter ) const; damage_instance get_damage_instance() const; + // calculate damage per second against a target + float dps( const Character &caster, const Creature &target ) const; // how big is the spell's radius int aoe() const; std::set effect_area( const spell_effect::override_parameters ¶ms, @@ -449,6 +462,12 @@ class spell std::set effect_area( const tripoint &source, const tripoint &target ) const; // distance spell can be cast int range() const; + /** + * all of the tripoints the spell can be cast at. + * if the spell can't be cast through walls, does not return anything behind walls + * if the spell can't target the ground, can't target unseen locations, etc. + */ + std::vector targetable_locations( const Character &source ) const; // how much energy does the spell cost int energy_cost( const Character &guy ) const; // how long does this spell's effect last @@ -464,7 +483,7 @@ class spell const requirement_data &components() const; bool has_components() const; // can the Character cast this spell? - bool can_cast( Character &guy ) const; + bool can_cast( const Character &guy ) const; // can the Character learn this spell? bool can_learn( const Character &guy ) const; // is this spell valid @@ -525,6 +544,7 @@ class spell // tries to create a field at the location specified void create_field( const tripoint &at ) const; + int sound_volume() const; // makes a spell sound at the location void make_sound( const tripoint &target ) const; void make_sound( const tripoint &target, int loudness ) const; @@ -589,6 +609,10 @@ class known_magic int select_spell( Character &guy ); // get all known spells std::vector get_spells(); + // directly get the character known spells + std::map &get_spellbook() { + return spellbook; + } // how much mana is available to use to cast spells int available_mana() const; // max mana vailable diff --git a/src/magic_enchantment.cpp b/src/magic_enchantment.cpp index d90dade25f21c..0e2edd878712b 100644 --- a/src/magic_enchantment.cpp +++ b/src/magic_enchantment.cpp @@ -67,6 +67,7 @@ namespace io case enchant_vals::mod::REGEN_STAMINA: return "REGEN_STAMINA"; case enchant_vals::mod::MAX_HP: return "MAX_HP"; case enchant_vals::mod::REGEN_HP: return "REGEN_HP"; + case enchant_vals::mod::HUNGER: return "HUNGER"; case enchant_vals::mod::THIRST: return "THIRST"; case enchant_vals::mod::FATIGUE: return "FATIGUE"; case enchant_vals::mod::PAIN: return "PAIN"; @@ -81,6 +82,7 @@ namespace io case enchant_vals::mod::SOCIAL_LIE: return "SOCIAL_LIE"; case enchant_vals::mod::SOCIAL_PERSUADE: return "SOCIAL_PERSUADE"; case enchant_vals::mod::SOCIAL_INTIMIDATE: return "SOCIAL_INTIMIDATE"; + case enchant_vals::mod::SLEEPY: return "SLEEPY"; case enchant_vals::mod::ARMOR_ACID: return "ARMOR_ACID"; case enchant_vals::mod::ARMOR_BASH: return "ARMOR_BASH"; case enchant_vals::mod::ARMOR_BIO: return "ARMOR_BIO"; @@ -146,6 +148,34 @@ void enchantment::load_enchantment( const JsonObject &jo, const std::string &src spell_factory.load( jo, src ); } +void enchantment::reset() +{ + spell_factory.reset(); +} + +enchantment_id enchantment::load_inline_enchantment( const JsonValue &jv, const std::string &src, + std::string &inline_id ) +{ + if( jv.test_string() ) { + return enchantment_id( jv.get_string() ); + } else if( jv.test_object() ) { + if( inline_id.empty() ) { + jv.throw_error( "Inline enchantment cannot be created without an id." ); + } + if( spell_factory.is_valid( enchantment_id( inline_id ) ) ) { + jv.throw_error( "Inline enchantment " + inline_id + + " cannot be created as an enchantment already has this id." ); + } + + enchantment inline_enchant; + inline_enchant.load( jv.get_object(), src, inline_id ); + spell_factory.insert( inline_enchant ); + return enchantment_id( inline_id ); + } else { + jv.throw_error( "Enchantment needs to be either string or enchantment object." ); + } +} + bool enchantment::is_active( const Character &guy, const item &parent ) const { if( !guy.has_item( parent ) ) { @@ -196,9 +226,10 @@ void enchantment::add_activation( const time_duration &dur, const fake_spell &fa intermittent_activation[dur].emplace_back( fake ); } -void enchantment::load( const JsonObject &jo, const std::string & ) +void enchantment::load( const JsonObject &jo, const std::string &, + const cata::optional &inline_id ) { - optional( jo, was_loaded, "id", id, enchantment_id( "" ) ); + optional( jo, was_loaded, "id", id, enchantment_id( inline_id.value_or( "" ) ) ); jo.read( "hit_you_effect", hit_you_effect ); jo.read( "hit_me_effect", hit_me_effect ); diff --git a/src/magic_enchantment.h b/src/magic_enchantment.h index 69404bb52b4c4..f731fcef204e6 100644 --- a/src/magic_enchantment.h +++ b/src/magic_enchantment.h @@ -43,6 +43,7 @@ enum class mod : int { REGEN_STAMINA, MAX_HP, // for all limbs! use with caution REGEN_HP, + HUNGER, // hunger rate THIRST, // thirst rate FATIGUE, // fatigue rate PAIN, // cost or regen over time @@ -57,6 +58,7 @@ enum class mod : int { SOCIAL_LIE, SOCIAL_PERSUADE, SOCIAL_INTIMIDATE, + SLEEPY, ARMOR_BASH, ARMOR_CUT, ARMOR_STAB, @@ -119,7 +121,14 @@ class enchantment }; static void load_enchantment( const JsonObject &jo, const std::string &src ); - void load( const JsonObject &jo, const std::string &src = "" ); + static void reset(); + void load( const JsonObject &jo, const std::string &src = "", + const cata::optional &inline_id = cata::nullopt ); + + // Takes in a JsonValue which can be either a string or an enchantment object and returns the id of the enchantment the caller will use. + // If the input is a string return it as an enchantment_id otherwise create an enchantment with id inline_id and return inline_id as an enchantment id + static enchantment_id load_inline_enchantment( const JsonValue &jv, const std::string &src, + std::string &inline_id ); // attempts to add two like enchantments together. // if their conditions don't match, return false. else true. diff --git a/src/magic_spell_effect.cpp b/src/magic_spell_effect.cpp index c010c0cac8a8c..20da42d70dd54 100644 --- a/src/magic_spell_effect.cpp +++ b/src/magic_spell_effect.cpp @@ -480,16 +480,7 @@ static void damage_targets( const spell &sp, Creature &caster, continue; } - projectile bolt; - bolt.speed = 10000; - bolt.impact = sp.get_damage_instance(); - bolt.proj_effects.emplace( "magic" ); - - dealt_projectile_attack atk; - atk.end_point = target; - atk.hit_critter = cr; - atk.proj = bolt; - atk.missed_by = 0.0; + dealt_projectile_attack atk = sp.get_projectile_attack( target, *cr ); if( !sp.effect_data().empty() ) { add_effect_to_target( target, sp ); } @@ -1238,6 +1229,7 @@ void spell_effect::dash( const spell &sp, Creature &caster, const tripoint &targ ::map &here = get_map(); // uses abs() coordinates std::vector trajectory; + trajectory.reserve( trajectory_local.size() ); for( const tripoint &local_point : trajectory_local ) { trajectory.push_back( here.getabs( local_point ) ); } diff --git a/src/main_menu.cpp b/src/main_menu.cpp index 2ec0ff896802f..2cc79817e917b 100644 --- a/src/main_menu.cpp +++ b/src/main_menu.cpp @@ -309,10 +309,9 @@ void main_menu::init_windows() const int total_h = FULL_SCREEN_HEIGHT + extra_h; // position of window within main display - const int x0 = ( TERMX - total_w ) / 2; - const int y0 = ( TERMY - total_h ) / 2; + const point p0( ( TERMX - total_w ) / 2, ( TERMY - total_h ) / 2 ); - w_open = catacurses::newwin( total_h, total_w, point( x0, y0 ) ); + w_open = catacurses::newwin( total_h, total_w, p0 ); menu_offset.y = total_h - 3; // note: if iMenuOffset is changed, @@ -351,15 +350,15 @@ void main_menu::init_strings() // fill menu with translated menu items vMenuItems.clear(); - vMenuItems.push_back( pgettext( "Main Menu", "OTD" ) ); - vMenuItems.push_back( pgettext( "Main Menu", "ew Game" ) ); - vMenuItems.push_back( pgettext( "Main Menu", "Lod" ) ); - vMenuItems.push_back( pgettext( "Main Menu", "orld" ) ); - vMenuItems.push_back( pgettext( "Main Menu", "pecial" ) ); - vMenuItems.push_back( pgettext( "Main Menu", "Setings" ) ); - vMenuItems.push_back( pgettext( "Main Menu", "Hlp" ) ); - vMenuItems.push_back( pgettext( "Main Menu", "redits" ) ); - vMenuItems.push_back( pgettext( "Main Menu", "uit" ) ); + vMenuItems.emplace_back( pgettext( "Main Menu", "OTD" ) ); + vMenuItems.emplace_back( pgettext( "Main Menu", "ew Game" ) ); + vMenuItems.emplace_back( pgettext( "Main Menu", "Lod" ) ); + vMenuItems.emplace_back( pgettext( "Main Menu", "orld" ) ); + vMenuItems.emplace_back( pgettext( "Main Menu", "pecial" ) ); + vMenuItems.emplace_back( pgettext( "Main Menu", "Setings" ) ); + vMenuItems.emplace_back( pgettext( "Main Menu", "Hlp" ) ); + vMenuItems.emplace_back( pgettext( "Main Menu", "redits" ) ); + vMenuItems.emplace_back( pgettext( "Main Menu", "uit" ) ); // determine hotkeys from translated menu item text vMenuHotkeys.clear(); @@ -368,11 +367,11 @@ void main_menu::init_strings() } vWorldSubItems.clear(); - vWorldSubItems.push_back( pgettext( "Main Menu|World", "elete World" ) ); - vWorldSubItems.push_back( pgettext( "Main Menu|World", "eset World" ) ); - vWorldSubItems.push_back( pgettext( "Main Menu|World", "how World Mods" ) ); - vWorldSubItems.push_back( pgettext( "Main Menu|World", "opy World Settings" ) ); - vWorldSubItems.push_back( pgettext( "Main Menu|World", "Character to emplate" ) ); + vWorldSubItems.emplace_back( pgettext( "Main Menu|World", "elete World" ) ); + vWorldSubItems.emplace_back( pgettext( "Main Menu|World", "eset World" ) ); + vWorldSubItems.emplace_back( pgettext( "Main Menu|World", "how World Mods" ) ); + vWorldSubItems.emplace_back( pgettext( "Main Menu|World", "opy World Settings" ) ); + vWorldSubItems.emplace_back( pgettext( "Main Menu|World", "Character to emplate" ) ); vWorldHotkeys.clear(); for( const std::string &item : vWorldSubItems ) { @@ -380,11 +379,11 @@ void main_menu::init_strings() } vSettingsSubItems.clear(); - vSettingsSubItems.push_back( pgettext( "Main Menu|Settings", "ptions" ) ); - vSettingsSubItems.push_back( pgettext( "Main Menu|Settings", "Kybindings" ) ); - vSettingsSubItems.push_back( pgettext( "Main Menu|Settings", "utopickup" ) ); - vSettingsSubItems.push_back( pgettext( "Main Menu|Settings", "afemode" ) ); - vSettingsSubItems.push_back( pgettext( "Main Menu|Settings", "olors" ) ); + vSettingsSubItems.emplace_back( pgettext( "Main Menu|Settings", "ptions" ) ); + vSettingsSubItems.emplace_back( pgettext( "Main Menu|Settings", "Kybindings" ) ); + vSettingsSubItems.emplace_back( pgettext( "Main Menu|Settings", "utopickup" ) ); + vSettingsSubItems.emplace_back( pgettext( "Main Menu|Settings", "afemode" ) ); + vSettingsSubItems.emplace_back( pgettext( "Main Menu|Settings", "olors" ) ); vSettingsHotkeys.clear(); for( const std::string &item : vSettingsSubItems ) { @@ -747,23 +746,22 @@ bool main_menu::opening_screen() bool main_menu::new_character_tab() { std::vector vSubItems; - vSubItems.push_back( pgettext( "Main Menu|New Game", "ustom Character" ) ); - vSubItems.push_back( pgettext( "Main Menu|New Game", "reset Character" ) ); - vSubItems.push_back( pgettext( "Main Menu|New Game", "andom Character" ) ); + vSubItems.emplace_back( pgettext( "Main Menu|New Game", "ustom Character" ) ); + vSubItems.emplace_back( pgettext( "Main Menu|New Game", "reset Character" ) ); + vSubItems.emplace_back( pgettext( "Main Menu|New Game", "andom Character" ) ); if( !MAP_SHARING::isSharing() ) { // "Play Now" function doesn't play well together with shared maps - vSubItems.push_back( pgettext( "Main Menu|New Game", "Play Now! (ixed Scenario)" ) ); - vSubItems.push_back( pgettext( "Main Menu|New Game", "Play ow!" ) ); + vSubItems.emplace_back( pgettext( "Main Menu|New Game", "Play Now! (ixed Scenario)" ) ); + vSubItems.emplace_back( pgettext( "Main Menu|New Game", "Play ow!" ) ); } std::vector hints; - hints.push_back( + hints.emplace_back( _( "Allows you to fully customize points pool, scenario, and character's profession, stats, traits, skills and other parameters." ) ); - hints.push_back( - _( "Select from one of previously created character templates." ) ); - hints.push_back( + hints.emplace_back( _( "Select from one of previously created character templates." ) ); + hints.emplace_back( _( "Creates random character, but lets you preview the generated character and the scenario and change character and/or scenario if needed." ) ); - hints.push_back( + hints.emplace_back( _( "Puts you right in the game, randomly choosing character's traits, profession, skills and other parameters. Scenario is fixed to Evacuee." ) ); - hints.push_back( + hints.emplace_back( _( "Puts you right in the game, randomly choosing scenario and character's traits, profession, skills and other parameters." ) ); std::vector> vNewGameHotkeys; @@ -882,7 +880,7 @@ bool main_menu::new_character_tab() } if( !player_character.create( play_type ) ) { load_char_templates(); - MAPBUFFER.reset(); + MAPBUFFER.clear(); overmap_buffer.clear(); continue; } @@ -954,7 +952,7 @@ bool main_menu::new_character_tab() } if( !player_character.create( character_type::TEMPLATE, templates[sel3] ) ) { load_char_templates(); - MAPBUFFER.reset(); + MAPBUFFER.clear(); overmap_buffer.clear(); continue; } @@ -1022,7 +1020,8 @@ bool main_menu::load_character_tab( bool transfer ) int line = menu_offset.y - 2 - i; std::string world_name = all_worldnames[i]; int savegames_count = world_generator->get_world( world_name )->world_saves.size(); - nc_color color1, color2; + nc_color color1; + nc_color color2; if( world_name == "TUTORIAL" || world_name == "DEFENSE" ) { color1 = c_light_cyan; color2 = h_light_cyan; @@ -1209,7 +1208,8 @@ void main_menu::world_tab() for( auto it = all_worldnames.begin(); it != all_worldnames.end(); ++it, i++ ) { int savegames_count = world_generator->get_world( *it )->world_saves.size(); int line = menu_offset.y - 2 - i; - nc_color color1, color2; + nc_color color1; + nc_color color2; if( *it == "TUTORIAL" || *it == "DEFENSE" ) { color1 = c_light_cyan; color2 = h_light_cyan; @@ -1247,7 +1247,7 @@ void main_menu::world_tab() player_character.save_template( player_character.name, points ); player_character = avatar(); - MAPBUFFER.reset(); + MAPBUFFER.clear(); overmap_buffer.clear(); load_char_templates(); @@ -1323,7 +1323,7 @@ void main_menu::world_tab() world_generator->delete_world( all_worldnames[sel2 - 1], do_delete ); savegames.clear(); - MAPBUFFER.reset(); + MAPBUFFER.clear(); overmap_buffer.clear(); if( do_delete ) { diff --git a/src/map.cpp b/src/map.cpp index e6ed9c2bb73b3..3b0eef110e97d 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1075,7 +1075,8 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp, const bool adjust_ ( dp + tripoint( 0, 0, ramp_offset ) ) : tripoint_zero ); if( !inbounds( src ) ) { - add_msg_debug( "map::displace_vehicle: coordinates out of bounds %d,%d,%d->%d,%d,%d", + add_msg_debug( debugmode::DF_MAP, + "map::displace_vehicle: coordinates out of bounds %d,%d,%d->%d,%d,%d", src.x, src.y, src.z, dst.x, dst.y, dst.z ); return false; } @@ -1109,7 +1110,7 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp, const bool adjust_ } if( !found ) { - add_msg_debug( "displace_vehicle [%s] failed", veh.name ); + add_msg_debug( debugmode::DF_MAP, "displace_vehicle [%s] failed", veh.name ); return false; } @@ -1158,7 +1159,7 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp, const bool adjust_ } if( psg->pos() != part_pos ) { - add_msg_debug( "Part/passenger position mismatch: part #%d at %d,%d,%d " + add_msg_debug( debugmode::DF_MAP, "Part/passenger position mismatch: part #%d at %d,%d,%d " "passenger at %d,%d,%d", prt, part_pos.x, part_pos.y, part_pos.z, psg->posx(), psg->posy(), psg->posz() ); } @@ -2313,7 +2314,7 @@ void map::process_falling() } if( !support_cache_dirty.empty() ) { - add_msg_debug( "Checking %d tiles for falling objects", + add_msg_debug( debugmode::DF_MAP, "Checking %d tiles for falling objects", support_cache_dirty.size() ); // We want the cache to stay constant, but falling can change it std::set last_cache = std::move( support_cache_dirty ); @@ -2741,11 +2742,10 @@ void map::decay_fields_and_scent( const time_duration &amount ) } for( int sy = 0; sy < SEEY; ++sy ) { - const int x = sx + smx * SEEX; - const int y = sy + smy * SEEY; + const point p( sx + smx * SEEX, sy + smy * SEEY ); const field &fields = cur_submap->get_field( { sx, sy} ); - if( !outside_cache[x][y] ) { + if( !outside_cache[p.x][p.y] ) { to_proc -= fields.field_count(); continue; } @@ -3029,7 +3029,7 @@ void map::smash_items( const tripoint &p, const int power, const std::string &ca const float volume_factor = std::max( 40, i->volume() / units::legacy_volume_factor ); float damage_chance = 10.0f * power / volume_factor; // Example: - // Power 40 (just below C4 epicenter) vs two-by-four + // Power 40 (just below C4 epicenter) vs plank // damage_chance = 10 * 40 / 40 = 10, material_factor = 8 // Will deal 1 damage, then 20% chance for another point // Power 20 (grenade minus shrapnel) vs glass bottle @@ -3077,7 +3077,7 @@ void map::smash_items( const tripoint &p, const int power, const std::string &ca // But save the contents, except for irremovable gunmods for( item *elem : i->contents.all_items_top() ) { if( !elem->is_irremovable() ) { - contents.push_back( item( *elem ) ); + contents.emplace_back( *elem ); } } @@ -3490,7 +3490,7 @@ void map::bash_items( const tripoint &p, bash_params ¶ms ) params.did_bash = true; smashed_glass = true; for( const item *bashed_content : bashed_item->contents.all_items_top() ) { - smashed_contents.push_back( item( *bashed_content ) ); + smashed_contents.emplace_back( *bashed_content ); } bashed_item = bashed_items.erase( bashed_item ); } else { @@ -4282,9 +4282,9 @@ void map::spawn_artifact( const tripoint &p, const relic_procgen_id &id ) add_item_or_charges( p, id->create_item( rules ) ); } -void map::spawn_item( const tripoint &p, const itype_id &type_id, - const unsigned quantity, const int charges, - const time_point &birthday, const int damlevel, const std::set &flags ) +void map::spawn_item( const tripoint &p, const itype_id &type_id, const unsigned quantity, + const int charges, const time_point &birthday, const int damlevel, const std::set &flags, + const std::string &variant ) { if( type_id.is_null() ) { return; @@ -4299,6 +4299,7 @@ void map::spawn_item( const tripoint &p, const itype_id &type_id, } // spawn the item item new_item( type_id, birthday ); + new_item.set_gun_variant( variant ); if( one_in( 3 ) && new_item.has_flag( flag_VARSIZE ) ) { new_item.set_flag( flag_FIT ); } @@ -4484,6 +4485,10 @@ item &map::add_item( const tripoint &p, item new_item ) new_item.set_var( "reveal_map_center_omt", ms_to_omt_copy( getabs( p ) ) ); } + if( new_item.has_flag( flag_ACTIVATE_ON_PLACE ) ) { + new_item.activate(); + } + current_submap->is_uniform = false; invalidate_max_populated_zlev( p.z ); @@ -4663,7 +4668,7 @@ static void process_vehicle_items( vehicle &cur_veh, int part ) autoclave_finished = true; cur_veh.part( part ).enabled = false; } else if( calendar::once_every( 15_minutes ) ) { - add_msg( _( "It should take %d minutes to finish sterilising items in the %s." ), + add_msg( _( "It should take %d minutes to finish sterilizing items in the %s." ), to_minutes( time_left ) + 1, cur_veh.name ); break; } @@ -5143,7 +5148,7 @@ std::list > map::get_rc_items( const tripoint &p ) map_stack items = i_at( pos ); for( auto &elem : items ) { if( elem.has_flag( flag_RADIO_ACTIVATION ) || elem.has_flag( flag_RADIO_CONTAINER ) ) { - rc_pairs.push_back( std::make_pair( pos, &elem ) ); + rc_pairs.emplace_back( pos, &elem ); } } } @@ -6399,11 +6404,10 @@ void map::reachable_flood_steps( std::vector &reachable_pts, const tri // set self and neighbors to 1 for( int dy = -1; dy <= 1; ++dy ) { for( int dx = -1; dx <= 1; ++dx ) { - int tx = dx + x; - int ty = dy + y; + point t2( dx + x, dy + y ); - if( tx >= 0 && tx < grid_dim && ty >= 0 && ty < grid_dim ) { - o_grid[ tx + ty * grid_dim ] = 1; + if( t2.x >= 0 && t2.x < grid_dim && t2.y >= 0 && t2.y < grid_dim ) { + o_grid[ t2.x + t2.y * grid_dim ] = 1; } } } @@ -6878,8 +6882,8 @@ static void generate_uniform( const tripoint &p, const ter_id &terrain_type ) void map::loadn( const tripoint &grid, const bool update_vehicles, bool _actualize ) { // Cache empty overmap types - static const oter_id rock( "empty_rock" ); - static const oter_id air( "open_air" ); + static const oter_str_id rock( "empty_rock" ); + static const oter_str_id air( "open_air" ); dbg( D_INFO ) << "map::loadn(game[" << g.get() << "], worldx[" << abs_sub.x << "], worldy[" << abs_sub.y << "], grid " << grid << ")"; @@ -7452,9 +7456,8 @@ void map::spawn_monsters_submap_group( const tripoint &gp, mongroup &group, bool for( int x = 0; x < SEEX; ++x ) { for( int y = 0; y < SEEY; ++y ) { - int fx = x + SEEX * gp.x; - int fy = y + SEEY * gp.y; - tripoint fp{ fx, fy, gp.z }; + point f( x + SEEX * gp.x, y + SEEY * gp.y ); + tripoint fp{ f, gp.z }; if( g->critter_at( fp ) != nullptr ) { continue; // there is already some creature } @@ -7526,7 +7529,7 @@ void map::spawn_monsters_submap_group( const tripoint &gp, mongroup &group, bool point( rng( 0, SEEX ), rng( 0, SEEY ) ); const int turns = rl_dist( p, rand_dest ) + group.interest; tmp.wander_to( rand_dest, turns ); - add_msg_debug( "%s targeting %d,%d,%d", tmp.disp_name(), + add_msg_debug( debugmode::DF_MAP, "%s targeting %d,%d,%d", tmp.disp_name(), tmp.wander_pos.x, tmp.wander_pos.y, tmp.wander_pos.z ); } @@ -7592,6 +7595,9 @@ void map::spawn_monsters_submap( const tripoint &gp, bool ignore_sight ) const auto place_it = [&]( const tripoint & p ) { monster *const placed = g->place_critter_at( make_shared_fast( tmp ), p ); + if( !i.data.patrol_points_rel_ms.empty() ) { + placed->set_patrol_route( i.data.patrol_points_rel_ms ); + } if( placed ) { placed->on_load(); } @@ -7857,12 +7863,11 @@ void map::build_outside_cache( const int zlev ) point sp( sx, sy ); if( cur_submap->get_ter( sp ).obj().has_flag( TFLAG_INDOORS ) || cur_submap->get_furn( sp ).obj().has_flag( TFLAG_INDOORS ) ) { - const int x = sx + smx * SEEX; - const int y = sy + smy * SEEY; + const point p( sx + smx * SEEX, sy + smy * SEEY ); // Add 1 to both coordinates, because we're operating on the padded cache for( int dx = 0; dx <= 2; dx++ ) { for( int dy = 0; dy <= 2; dy++ ) { - padded_cache[x + dx][y + dy] = false; + padded_cache[p.x + dx][p.y + dy] = false; } } } @@ -7905,18 +7910,17 @@ void map::build_obstacle_cache( const tripoint &start, const tripoint &end, const point sp( sx, sy ); int ter_move = cur_submap->get_ter( sp ).obj().movecost; int furn_move = cur_submap->get_furn( sp ).obj().movecost; - const int x = sx + smx * SEEX; - const int y = sy + smy * SEEY; + const point p2( sx + smx * SEEX, sy + smy * SEEY ); if( ter_move == 0 || furn_move < 0 || ter_move + furn_move == 0 ) { - obstacle_cache[x][y].velocity = 1000.0f; - obstacle_cache[x][y].density = 0.0f; + obstacle_cache[p2.x][p2.y].velocity = 1000.0f; + obstacle_cache[p2.x][p2.y].density = 0.0f; } else { // Magic number warning, this is the density of air at sea level at // some nominal temp and humidity. // TODO: figure out if our temp/altitude/humidity variation is // sufficient to bother setting this differently. - obstacle_cache[x][y].velocity = 1.2f; - obstacle_cache[x][y].density = 1.0f; + obstacle_cache[p2.x][p2.y].velocity = 1.2f; + obstacle_cache[p2.x][p2.y].density = 1.0f; } } } @@ -8016,9 +8020,8 @@ bool map::build_floor_cache( const int zlev ) if( below_submap && ( below_submap->get_furn( sp ).obj().has_flag( TFLAG_SUN_ROOF_ABOVE ) ) ) { continue; } - const int x = sx + smx * SEEX; - const int y = sy + smy * SEEY; - floor_cache[x][y] = false; + const point p( sx + smx * SEEX, sy + smy * SEEY ); + floor_cache[p.x][p.y] = false; no_floor_gaps = false; } } diff --git a/src/map.h b/src/map.h index 517c724753382..953bf435a1b17 100644 --- a/src/map.h +++ b/src/map.h @@ -328,7 +328,8 @@ class map return cache.r_hor_cache->has_potential_los( from.xy(), to.xy(), cache ) && cache.r_hor_cache->has_potential_los( to.xy(), from.xy(), cache ) ; } - tripoint upper, lower; + tripoint upper; + tripoint lower; std::tie( upper, lower ) = from.z > to.z ? std::make_pair( from, to ) : std::make_pair( to, from ); // z-bounds depend on the invariant that both points are inbounds and their z are different return get_cache( lower.z ).r_up_cache->has_potential_los( @@ -1091,12 +1092,13 @@ class map void spawn_item( const tripoint &p, const itype_id &type_id, unsigned quantity = 1, int charges = 0, const time_point &birthday = calendar::start_of_cataclysm, int damlevel = 0, - const std::set &flags = {} ); + const std::set &flags = {}, const std::string &variant = "" ); void spawn_item( const point &p, const itype_id &type_id, unsigned quantity = 1, int charges = 0, const time_point &birthday = calendar::start_of_cataclysm, int damlevel = 0, - const std::set &flags = {} ) { - spawn_item( tripoint( p, abs_sub.z ), type_id, quantity, charges, birthday, damlevel, flags ); + const std::set &flags = {}, const std::string &variant = "" ) { + spawn_item( tripoint( p, abs_sub.z ), type_id, quantity, charges, birthday, damlevel, flags, + variant ); } // FIXME: remove these overloads and require spawn_item to take an @@ -1104,14 +1106,15 @@ class map void spawn_item( const tripoint &p, const std::string &type_id, unsigned quantity = 1, int charges = 0, const time_point &birthday = calendar::start_of_cataclysm, int damlevel = 0, - const std::set &flags = {} ) { - spawn_item( p, itype_id( type_id ), quantity, charges, birthday, damlevel, flags ); + const std::set &flags = {}, const std::string &variant = "" ) { + spawn_item( p, itype_id( type_id ), quantity, charges, birthday, damlevel, flags, variant ); } void spawn_item( const point &p, const std::string &type_id, unsigned quantity = 1, int charges = 0, const time_point &birthday = calendar::start_of_cataclysm, int damlevel = 0, - const std::set &flags = {} ) { - spawn_item( tripoint( p, abs_sub.z ), type_id, quantity, charges, birthday, damlevel, flags ); + const std::set &flags = {}, const std::string &variant = "" ) { + spawn_item( tripoint( p, abs_sub.z ), type_id, quantity, charges, birthday, damlevel, flags, + variant ); } units::volume max_volume( const tripoint &p ); units::volume free_volume( const tripoint &p ); @@ -1644,7 +1647,6 @@ class map void draw_lab( mapgendata &dat ); void draw_temple( const mapgendata &dat ); void draw_mine( mapgendata &dat ); - void draw_spiral( const mapgendata &dat ); void draw_anthill( const mapgendata &dat ); void draw_slimepit( const mapgendata &dat ); void draw_spider_pit( const mapgendata &dat ); diff --git a/src/map_extras.cpp b/src/map_extras.cpp index 948625df39251..15857db05c1b9 100644 --- a/src/map_extras.cpp +++ b/src/map_extras.cpp @@ -84,12 +84,10 @@ static const itype_id itype_bag_canvas( "bag_canvas" ); static const itype_id itype_bottle_glass( "bottle_glass" ); static const itype_id itype_bullwhip( "bullwhip" ); static const itype_id itype_chunk_sulfur( "chunk_sulfur" ); -static const itype_id itype_coke( "coke" ); static const itype_id itype_crowbar( "crowbar" ); static const itype_id itype_fedora( "fedora" ); static const itype_id itype_glasses_eye( "glasses_eye" ); static const itype_id itype_hatchet( "hatchet" ); -static const itype_id itype_heroin( "heroin" ); static const itype_id itype_holybook_bible1( "holybook_bible1" ); static const itype_id itype_indoor_volleyball( "indoor_volleyball" ); static const itype_id itype_jacket_leather( "jacket_leather" ); @@ -98,7 +96,6 @@ static const itype_id itype_landmine( "landmine" ); static const itype_id itype_machete( "machete" ); static const itype_id itype_material_sand( "material_sand" ); static const itype_id itype_material_soil( "material_soil" ); -static const itype_id itype_meth( "meth" ); static const itype_id itype_rag_bloody( "rag_bloody" ); static const itype_id itype_remington_870_breacher( "remington_870_breacher" ); static const itype_id itype_shot_hull( "shot_hull" ); @@ -113,7 +110,6 @@ static const itype_id itype_tux( "tux" ); static const itype_id itype_umbrella( "umbrella" ); static const itype_id itype_usp_45( "usp_45" ); static const itype_id itype_vodka( "vodka" ); -static const itype_id itype_weed( "weed" ); static const itype_id itype_wheel( "wheel" ); static const itype_id itype_withered( "withered" ); static const itype_id itype_wrench( "wrench" ); @@ -135,8 +131,11 @@ static const mongroup_id GROUP_NETHER_CAPTURED( "GROUP_NETHER_CAPTURED" ); static const mongroup_id GROUP_NETHER_PORTAL( "GROUP_NETHER_PORTAL" ); static const mongroup_id GROUP_PETS( "GROUP_PETS" ); static const mongroup_id GROUP_STRAY_DOGS( "GROUP_STRAY_DOGS" ); +static const mongroup_id GROUP_WASP_GUARD( "GROUP_WASP_GUARD" ); +static const mongroup_id GROUP_WASP_QUEEN( "GROUP_WASP_QUEEN" ); static const mtype_id mon_dispatch( "mon_dispatch" ); +static const mtype_id mon_dermatik( "mon_dermatik" ); static const mtype_id mon_jabberwock( "mon_jabberwock" ); static const mtype_id mon_marloss_zealot_f( "mon_marloss_zealot_f" ); static const mtype_id mon_marloss_zealot_m( "mon_marloss_zealot_m" ); @@ -149,14 +148,11 @@ static const mtype_id mon_turret_searchlight( "mon_turret_searchlight" ); static const mtype_id mon_turret_rifle( "mon_turret_rifle" ); static const mtype_id mon_turret_riot( "mon_turret_riot" ); static const mtype_id mon_turret_speaker( "mon_turret_speaker" ); -static const mtype_id mon_wasp( "mon_wasp" ); static const mtype_id mon_wolf( "mon_wolf" ); static const mtype_id mon_zombie_bio_op( "mon_zombie_bio_op" ); static const mtype_id mon_zombie_military_pilot( "mon_zombie_military_pilot" ); static const mtype_id mon_zombie_scientist( "mon_zombie_scientist" ); -static const mtype_id mon_zombie_smoker( "mon_zombie_smoker" ); static const mtype_id mon_zombie_soldier( "mon_zombie_soldier" ); -static const mtype_id mon_zombie_spitter( "mon_zombie_spitter" ); static const mtype_id mon_zombie_tough( "mon_zombie_tough" ); class npc_template; @@ -293,10 +289,12 @@ static bool mx_house_wasp( map &m, const tripoint &loc ) } } } - m.add_spawn( mon_wasp, 1, tripoint( pod, loc.z ) ); + m.place_spawns( GROUP_WASP_GUARD, 1, pod, pod, 1, true ); + } + m.place_spawns( GROUP_WASP_QUEEN, 1, point_zero, point( SEEX, SEEY ), 1, true ); + if( one_in( 5 ) ) { + m.add_spawn( mon_dermatik, rng( 1, 3 ), tripoint( point( SEEX * 2 - 1, SEEY * 2 - 1 ), loc.z ) ); } - m.place_items( item_group_id( "rare" ), 70, point_zero, point( SEEX * 2 - 1, SEEY * 2 - 1 ), - false, calendar::start_of_cataclysm ); return true; } @@ -392,25 +390,21 @@ static bool mx_helicopter( map &m, const tripoint &abs_sub ) // Get the bounding box, centered on mount(0,0) bounding_box bbox = veh->get_bounding_box(); // Move the wreckage forward/backward half it's length so that it spawns more over the center of the debris area - int x_length = std::abs( bbox.p2.x - bbox.p1.x ); - int y_length = std::abs( bbox.p2.y - bbox.p1.y ); + point length( std::abs( bbox.p2.x - bbox.p1.x ), std::abs( bbox.p2.y - bbox.p1.y ) ); // cont. - int x_offset = veh->dir_vec().x * x_length / 2; - int y_offset = veh->dir_vec().y * y_length / 2; + point offset( veh->dir_vec().x * length.x / 2, veh->dir_vec().y * length.y / 2 ); - int x_min = std::abs( bbox.p1.x ) + 0; - int y_min = std::abs( bbox.p1.y ) + 0; + point min( std::abs( bbox.p1.x ) + 0, std::abs( bbox.p1.y ) + 0 ); int x_max = SEEX * 2 - bbox.p2.x - 1; int y_max = SEEY * 2 - bbox.p2.y - 1; // Clamp x1 & y1 such that no parts of the vehicle extend over the border of the submap. - int x1 = clamp( c.x + x_offset, x_min, x_max ); - int y1 = clamp( c.y + y_offset, y_min, y_max ); + point p1( clamp( c.x + offset.x, min.x, x_max ), clamp( c.y + offset.y, min.y, y_max ) ); vehicle *wreckage = m.add_vehicle( - crashed_hull, tripoint( x1, y1, abs_sub.z ), dir1, rng( 1, 33 ), 1 ); + crashed_hull, tripoint( p1, abs_sub.z ), dir1, rng( 1, 33 ), 1 ); const auto controls_at = []( vehicle * wreckage, const tripoint & pos ) { return !wreckage->get_parts_at( pos, "CONTROLS", part_status_flag::any ).empty() || @@ -916,133 +910,6 @@ static bool mx_bandits_block( map &m, const tripoint &abs_sub ) return false; } -static bool mx_drugdeal( map &m, const tripoint &abs_sub ) -{ - // Decide on a drug type - int num_drugs = 0; - itype_id drugtype; - switch( rng( 1, 10 ) ) { - case 1: - // Weed - num_drugs = rng( 20, 30 ); - drugtype = itype_weed; - break; - case 2: - case 3: - case 4: - case 5: - // Cocaine - num_drugs = rng( 10, 20 ); - drugtype = itype_coke; - break; - case 6: - case 7: - case 8: - // Meth - num_drugs = rng( 8, 14 ); - drugtype = itype_meth; - break; - case 9: - case 10: - // Heroin - num_drugs = rng( 6, 12 ); - drugtype = itype_heroin; - break; - } - int num_bodies_a = dice( 3, 3 ); - int num_bodies_b = dice( 3, 3 ); - bool north_south = one_in( 2 ); - bool a_has_drugs = one_in( 2 ); - - for( int i = 0; i < num_bodies_a; i++ ) { - point p; - point offset; - int tries = 0; - do { // Loop until we find a valid spot to dump a body, or we give up - if( north_south ) { - p.x = rng( 0, SEEX * 2 - 1 ); - p.y = rng( 0, SEEY - 4 ); - offset.x = 0; - offset.y = -1; - } else { - p.x = rng( 0, SEEX - 4 ); - p.y = rng( 0, SEEY * 2 - 1 ); - offset.x = -1; - offset.y = 0; - } - tries++; - } while( tries < 10 && m.impassable( p ) ); - - if( tries < 10 ) { // We found a valid spot! - if( a_has_drugs && num_drugs > 0 ) { - int drugs_placed = rng( 2, 6 ); - if( drugs_placed > num_drugs ) { - drugs_placed = num_drugs; - num_drugs = 0; - } - m.spawn_item( p, drugtype, 0, drugs_placed ); - } - if( one_in( 10 ) ) { - m.add_spawn( mon_zombie_spitter, 1, { p, abs_sub.z } ); - } else { - m.place_items( item_group_id( "map_extra_drugdeal" ), 100, p, p, true, - calendar::start_of_cataclysm ); - int splatter_range = rng( 1, 3 ); - for( int j = 0; j <= splatter_range; j++ ) { - m.add_field( p + tripoint( j * offset.x, j * offset.y, abs_sub.z ), fd_blood, 1, 0_turns ); - } - } - } - } - for( int i = 0; i < num_bodies_b; i++ ) { - point p2; - point offset2; - int tries = 0; - do { // Loop until we find a valid spot to dump a body, or we give up - if( north_south ) { - p2.x = rng( 0, SEEX * 2 - 1 ); - p2.y = rng( SEEY + 3, SEEY * 2 - 1 ); - offset2.x = 0; - offset2.y = 1; - } else { - p2.x = rng( SEEX + 3, SEEX * 2 - 1 ); - p2.y = rng( 0, SEEY * 2 - 1 ); - offset2.x = 1; - offset2.y = 0; - } - tries++; - } while( tries < 10 && m.impassable( p2 ) ); - - if( tries < 10 ) { // We found a valid spot! - if( one_in( 20 ) ) { - m.add_spawn( mon_zombie_smoker, 1, { p2, abs_sub.z } ); - } else { - m.place_items( item_group_id( "map_extra_drugdeal" ), 100, p2, p2, true, - calendar::start_of_cataclysm ); - int splatter_range = rng( 1, 3 ); - for( int j = 0; j <= splatter_range; j++ ) { - m.add_field( p2 + tripoint( j * offset2.x, j * offset2.y, abs_sub.z ), fd_blood, 1, 0_turns ); - } - if( !a_has_drugs && num_drugs > 0 ) { - int drugs_placed = rng( 2, 6 ); - if( drugs_placed > num_drugs ) { - drugs_placed = num_drugs; - num_drugs = 0; - } - m.spawn_item( p2, drugtype, 0, drugs_placed ); - } - } - } - } - int num_monsters = rng( 0, 3 ); - for( int i = 0; i < num_monsters; i++ ) { - point m2( rng( 1, SEEX * 2 - 2 ), rng( 1, SEEY * 2 - 2 ) ); - m.place_spawns( GROUP_NETHER_CAPTURED, 1, m2, m2, 1, true ); - } - - return true; -} - static bool mx_supplydrop( map &m, const tripoint &/*abs_sub*/ ) { const bool intact = x_in_y( 40, @@ -1181,7 +1048,6 @@ static bool mx_minefield( map &, const tripoint &abs_sub ) const int num_mines = rng( 6, 20 ); const std::string text = _( "DANGER! MINEFIELD!" ); - int x, y, x1, y1 = 0; bool did_something = false; @@ -1254,23 +1120,23 @@ static bool mx_minefield( map &, const tripoint &abs_sub ) //Spawn 6-20 mines in the lower submap. //Spawn ordinary mine on asphalt, otherwise spawn buried mine for( int i = 0; i < num_mines; i++ ) { - const int x = rng( 3, SEEX * 2 - 4 ), y = rng( SEEY, SEEY * 2 - 2 ); - if( m.has_flag( flag_DIGGABLE, point( x, y ) ) ) { - place_trap_if_clear( m, point( x, y ), tr_landmine_buried ); + const point p( rng( 3, SEEX * 2 - 4 ), rng( SEEY, SEEY * 2 - 2 ) ); + if( m.has_flag( flag_DIGGABLE, p ) ) { + place_trap_if_clear( m, p, tr_landmine_buried ); } else { - place_trap_if_clear( m, point( x, y ), tr_landmine ); + place_trap_if_clear( m, p, tr_landmine ); } } //Spawn 6-20 puddles of blood on tiles without mines for( int i = 0; i < num_mines; i++ ) { - const int x = rng( 3, SEEX * 2 - 4 ), y = rng( SEEY, SEEY * 2 - 2 ); - if( m.tr_at( { x, y, abs_sub.z } ).is_null() ) { - m.add_field( { x, y, abs_sub.z }, fd_blood, rng( 1, 3 ) ); + const point p2( rng( 3, SEEX * 2 - 4 ), rng( SEEY, SEEY * 2 - 2 ) ); + if( m.tr_at( { p2, abs_sub.z } ).is_null() ) { + m.add_field( { p2, abs_sub.z }, fd_blood, rng( 1, 3 ) ); //10% chance to spawn a corpse of dead people/zombie on a tile with blood if( one_in( 10 ) ) { - m.add_corpse( { x, y, abs_sub.z } ); - for( const auto &loc : m.points_in_radius( { x, y, abs_sub.z }, 1 ) ) { + m.add_corpse( { p2, abs_sub.z } ); + for( const auto &loc : m.points_in_radius( { p2, abs_sub.z }, 1 ) ) { //50% chance to spawn gibs in every tile around corpse in 1-tile radius if( one_in( 2 ) ) { m.add_field( { loc.xy(), abs_sub.z }, fd_gibs_flesh, rng( 1, 3 ) ); @@ -1281,8 +1147,8 @@ static bool mx_minefield( map &, const tripoint &abs_sub ) } //Set two warning signs on the last horizontal line of the submap - x = rng( 3, SEEX ); - x1 = rng( SEEX + 1, SEEX * 2 - 4 ); + const int x = rng( 3, SEEX ); + const int x1 = rng( SEEX + 1, SEEX * 2 - 4 ); m.furn_set( point( x, SEEY * 2 - 1 ), furn_str_id( "f_sign_warning" ) ); m.set_signage( tripoint( x, SEEY * 2 - 1, abs_sub.z ), text ); m.furn_set( point( x1, SEEY * 2 - 1 ), furn_str_id( "f_sign_warning" ) ); @@ -1357,23 +1223,23 @@ static bool mx_minefield( map &, const tripoint &abs_sub ) //Spawn 6-20 mines in the upper submap. //Spawn ordinary mine on asphalt, otherwise spawn buried mine for( int i = 0; i < num_mines; i++ ) { - const int x = rng( 3, SEEX * 2 - 4 ), y = rng( 1, SEEY ); - if( m.has_flag( flag_DIGGABLE, point( x, y ) ) ) { - place_trap_if_clear( m, point( x, y ), tr_landmine_buried ); + const point p3( rng( 3, SEEX * 2 - 4 ), rng( 1, SEEY ) ); + if( m.has_flag( flag_DIGGABLE, p3 ) ) { + place_trap_if_clear( m, p3, tr_landmine_buried ); } else { - place_trap_if_clear( m, point( x, y ), tr_landmine ); + place_trap_if_clear( m, p3, tr_landmine ); } } //Spawn 6-20 puddles of blood on tiles without mines for( int i = 0; i < num_mines; i++ ) { - const int x = rng( 3, SEEX * 2 - 4 ), y = rng( 1, SEEY ); - if( m.tr_at( { x, y, abs_sub.z } ).is_null() ) { - m.add_field( { x, y, abs_sub.z }, fd_blood, rng( 1, 3 ) ); + const point p4( rng( 3, SEEX * 2 - 4 ), rng( 1, SEEY ) ); + if( m.tr_at( { p4, abs_sub.z } ).is_null() ) { + m.add_field( { p4, abs_sub.z }, fd_blood, rng( 1, 3 ) ); //10% chance to spawn a corpse of dead people/zombie on a tile with blood if( one_in( 10 ) ) { - m.add_corpse( { x, y, abs_sub.z } ); - for( const auto &loc : m.points_in_radius( { x, y, abs_sub.z }, 1 ) ) { + m.add_corpse( { p4, abs_sub.z } ); + for( const auto &loc : m.points_in_radius( { p4, abs_sub.z }, 1 ) ) { //50% chance to spawn gibs in every tile around corpse in 1-tile radius if( one_in( 2 ) ) { m.add_field( { loc.xy(), abs_sub.z }, fd_gibs_flesh, rng( 1, 3 ) ); @@ -1384,8 +1250,8 @@ static bool mx_minefield( map &, const tripoint &abs_sub ) } //Set two warning signs on the first horizontal line of the submap - x = rng( 3, SEEX ); - x1 = rng( SEEX + 1, SEEX * 2 - 4 ); + const int x = rng( 3, SEEX ); + const int x1 = rng( SEEX + 1, SEEX * 2 - 4 ); m.furn_set( point( x, 0 ), furn_str_id( "f_sign_warning" ) ); m.set_signage( tripoint( x, 0, abs_sub.z ), text ); m.furn_set( point( x1, 0 ), furn_str_id( "f_sign_warning" ) ); @@ -1504,23 +1370,23 @@ static bool mx_minefield( map &, const tripoint &abs_sub ) //Spawn 6-20 mines in the rightmost submap. //Spawn ordinary mine on asphalt, otherwise spawn buried mine for( int i = 0; i < num_mines; i++ ) { - const int x = rng( SEEX + 1, SEEX * 2 - 2 ), y = rng( 3, SEEY * 2 - 4 ); - if( m.has_flag( flag_DIGGABLE, point( x, y ) ) ) { - place_trap_if_clear( m, point( x, y ), tr_landmine_buried ); + const point p5( rng( SEEX + 1, SEEX * 2 - 2 ), rng( 3, SEEY * 2 - 4 ) ); + if( m.has_flag( flag_DIGGABLE, p5 ) ) { + place_trap_if_clear( m, p5, tr_landmine_buried ); } else { - place_trap_if_clear( m, point( x, y ), tr_landmine ); + place_trap_if_clear( m, p5, tr_landmine ); } } //Spawn 6-20 puddles of blood on tiles without mines for( int i = 0; i < num_mines; i++ ) { - const int x = rng( SEEX + 1, SEEX * 2 - 2 ), y = rng( 3, SEEY * 2 - 4 ); - if( m.tr_at( { x, y, abs_sub.z } ).is_null() ) { - m.add_field( { x, y, abs_sub.z }, fd_blood, rng( 1, 3 ) ); + const point p6( rng( SEEX + 1, SEEX * 2 - 2 ), rng( 3, SEEY * 2 - 4 ) ); + if( m.tr_at( { p6, abs_sub.z } ).is_null() ) { + m.add_field( { p6, abs_sub.z }, fd_blood, rng( 1, 3 ) ); //10% chance to spawn a corpse of dead people/zombie on a tile with blood if( one_in( 10 ) ) { - m.add_corpse( { x, y, abs_sub.z } ); - for( const auto &loc : m.points_in_radius( { x, y, abs_sub.z }, 1 ) ) { + m.add_corpse( { p6, abs_sub.z } ); + for( const auto &loc : m.points_in_radius( { p6, abs_sub.z }, 1 ) ) { //50% chance to spawn gibs in every tile around corpse in 1-tile radius if( one_in( 2 ) ) { m.add_field( { loc.xy(), abs_sub.z }, fd_gibs_flesh, rng( 1, 3 ) ); @@ -1531,8 +1397,8 @@ static bool mx_minefield( map &, const tripoint &abs_sub ) } //Set two warning signs on the last vertical line of the submap - y = rng( 3, SEEY ); - y1 = rng( SEEY + 1, SEEY * 2 - 4 ); + const int y = rng( 3, SEEY ); + const int y1 = rng( SEEY + 1, SEEY * 2 - 4 ); m.furn_set( point( SEEX * 2 - 1, y ), furn_str_id( "f_sign_warning" ) ); m.set_signage( tripoint( SEEX * 2 - 1, y, abs_sub.z ), text ); m.furn_set( point( SEEX * 2 - 1, y1 ), furn_str_id( "f_sign_warning" ) ); @@ -1643,23 +1509,23 @@ static bool mx_minefield( map &, const tripoint &abs_sub ) //Spawn 6-20 mines in the leftmost submap. //Spawn ordinary mine on asphalt, otherwise spawn buried mine for( int i = 0; i < num_mines; i++ ) { - const int x = rng( 1, SEEX ), y = rng( 3, SEEY * 2 - 4 ); - if( m.has_flag( flag_DIGGABLE, point( x, y ) ) ) { - place_trap_if_clear( m, point( x, y ), tr_landmine_buried ); + const point p7( rng( 1, SEEX ), rng( 3, SEEY * 2 - 4 ) ); + if( m.has_flag( flag_DIGGABLE, p7 ) ) { + place_trap_if_clear( m, p7, tr_landmine_buried ); } else { - place_trap_if_clear( m, point( x, y ), tr_landmine ); + place_trap_if_clear( m, p7, tr_landmine ); } } //Spawn 6-20 puddles of blood on tiles without mines for( int i = 0; i < num_mines; i++ ) { - const int x = rng( 1, SEEX ), y = rng( 3, SEEY * 2 - 4 ); - if( m.tr_at( { x, y, abs_sub.z } ).is_null() ) { - m.add_field( { x, y, abs_sub.z }, fd_blood, rng( 1, 3 ) ); + const point p8( rng( 1, SEEX ), rng( 3, SEEY * 2 - 4 ) ); + if( m.tr_at( { p8, abs_sub.z } ).is_null() ) { + m.add_field( { p8, abs_sub.z }, fd_blood, rng( 1, 3 ) ); //10% chance to spawn a corpse of dead people/zombie on a tile with blood if( one_in( 10 ) ) { - m.add_corpse( { x, y, abs_sub.z } ); - for( const auto &loc : m.points_in_radius( { x, y, abs_sub.z }, 1 ) ) { + m.add_corpse( { p8, abs_sub.z } ); + for( const auto &loc : m.points_in_radius( { p8, abs_sub.z }, 1 ) ) { //50% chance to spawn gibs in every tile around corpse in 1-tile radius if( one_in( 2 ) ) { m.add_field( { loc.xy(), abs_sub.z }, fd_gibs_flesh, rng( 1, 3 ) ); @@ -1670,8 +1536,8 @@ static bool mx_minefield( map &, const tripoint &abs_sub ) } //Set two warning signs on the first vertical line of the submap - y = rng( 3, SEEY ); - y1 = rng( SEEY + 1, SEEY * 2 - 4 ); + const int y = rng( 3, SEEY ); + const int y1 = rng( SEEY + 1, SEEY * 2 - 4 ); m.furn_set( point( 0, y ), furn_str_id( "f_sign_warning" ) ); m.set_signage( tripoint( 0, y, abs_sub.z ), text ); m.furn_set( point( 0, y1 ), furn_str_id( "f_sign_warning" ) ); @@ -3122,7 +2988,6 @@ FunctionMap builtin_functions = { { "mx_null", mx_null }, { "mx_crater", mx_crater }, { "mx_collegekids", mx_collegekids }, - { "mx_drugdeal", mx_drugdeal }, { "mx_roadworks", mx_roadworks }, { "mx_mayhem", mx_mayhem }, { "mx_roadblock", mx_roadblock }, diff --git a/src/map_field.cpp b/src/map_field.cpp index ae320217bbc59..59d7b0e00f42e 100644 --- a/src/map_field.cpp +++ b/src/map_field.cpp @@ -72,8 +72,6 @@ static const itype_id itype_rock( "rock" ); static const species_id species_FUNGUS( "FUNGUS" ); -static const bionic_id bio_heatsink( "bio_heatsink" ); - static const efftype_id effect_badpoison( "badpoison" ); static const efftype_id effect_blind( "blind" ); static const efftype_id effect_corroding( "corroding" ); @@ -93,6 +91,8 @@ static const trait_id trait_M_SKIN3( "M_SKIN3" ); static const trait_id trait_THRESH_MARLOSS( "THRESH_MARLOSS" ); static const trait_id trait_THRESH_MYCUS( "THRESH_MYCUS" ); +static const json_character_flag json_flag_HEATSINK( "HEATSINK" ); + using namespace map_field_processing; void map::create_burnproducts( const tripoint &p, const item &fuel, const units::mass &burned_mass ) @@ -979,7 +979,7 @@ void field_processor_fd_fire( const tripoint &p, field_entry &cur, field_proc_da 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 ) ); + new_content.emplace_back( *it ); } } fuel = items_here.erase( fuel ); @@ -1472,7 +1472,7 @@ void map::player_in_field( player &u ) } if( ft == fd_fire ) { // Heatsink or suit prevents ALL fire damage. - if( !u.has_active_bionic( bio_heatsink ) && !u.is_wearing( itype_rm13_armor_on ) ) { + if( !u.has_flag( json_flag_HEATSINK ) && !u.is_wearing( itype_rm13_armor_on ) ) { // To modify power of a field based on... whatever is relevant for the effect. int adjusted_intensity = cur.get_field_intensity(); @@ -1516,19 +1516,19 @@ void map::player_in_field( player &u ) if( !u.is_on_ground() ) { switch( adjusted_intensity ) { case 3: - parts_burned.push_back( bodypart_id( "hand_l" ) ); - parts_burned.push_back( bodypart_id( "hand_r" ) ); - parts_burned.push_back( bodypart_id( "arm_l" ) ); - parts_burned.push_back( bodypart_id( "arm_r" ) ); + parts_burned.emplace_back( "hand_l" ); + parts_burned.emplace_back( "hand_r" ); + parts_burned.emplace_back( "arm_l" ); + parts_burned.emplace_back( "arm_r" ); /* fallthrough */ case 2: - parts_burned.push_back( bodypart_id( "torso" ) ); + parts_burned.emplace_back( "torso" ); /* fallthrough */ case 1: - parts_burned.push_back( bodypart_id( "foot_l" ) ); - parts_burned.push_back( bodypart_id( "foot_r" ) ); - parts_burned.push_back( bodypart_id( "leg_l" ) ); - parts_burned.push_back( bodypart_id( "leg_r" ) ); + parts_burned.emplace_back( "foot_l" ); + parts_burned.emplace_back( "foot_r" ); + parts_burned.emplace_back( "leg_l" ); + parts_burned.emplace_back( "leg_r" ); } } else { // Lying in the fire is BAAAD news, hits every body part. @@ -1587,7 +1587,7 @@ void map::player_in_field( player &u ) if( !inside ) { // Fireballs can't touch you inside a car. // Heatsink or suit stops fire. - if( !u.has_active_bionic( bio_heatsink ) && + if( !u.has_flag( json_flag_HEATSINK ) && !u.is_wearing( itype_rm13_armor_on ) ) { u.add_msg_player_or_npc( m_bad, _( "You're torched by flames!" ), _( " is torched by flames!" ) ); diff --git a/src/map_item_stack.cpp b/src/map_item_stack.cpp index 362ee16528200..4d18fb414bdd9 100644 --- a/src/map_item_stack.cpp +++ b/src/map_item_stack.cpp @@ -21,7 +21,7 @@ map_item_stack::item_group::item_group( const tripoint &p, const int arg_count, map_item_stack::map_item_stack() : example( nullptr ), totalcount( 0 ) { - vIG.push_back( item_group() ); + vIG.emplace_back( ); } map_item_stack::map_item_stack( const item *const it, const tripoint &pos ) : example( it ), diff --git a/src/mapbuffer.cpp b/src/mapbuffer.cpp index 3b212578c3526..19dc38e18fec5 100644 --- a/src/mapbuffer.cpp +++ b/src/mapbuffer.cpp @@ -43,36 +43,32 @@ static std::string find_dirname( const tripoint &om_addr ) mapbuffer MAPBUFFER; mapbuffer::mapbuffer() = default; +mapbuffer::~mapbuffer() = default; -mapbuffer::~mapbuffer() +void mapbuffer::clear() { - reset(); -} - -void mapbuffer::reset() -{ - for( auto &elem : submaps ) { - delete elem.second; - } submaps.clear(); } -bool mapbuffer::add_submap( const tripoint &p, submap *sm ) +bool mapbuffer::add_submap( const tripoint &p, std::unique_ptr &sm ) { - if( submaps.count( p ) != 0 ) { + if( submaps.count( p ) ) { return false; } - submaps[p] = sm; + submaps[p] = std::move( sm ); return true; } -bool mapbuffer::add_submap( const tripoint &p, std::unique_ptr &sm ) +bool mapbuffer::add_submap( const tripoint &p, submap *sm ) { - const bool result = add_submap( p, sm.get() ); - if( result ) { - sm.release(); + // FIXME: get rid of this overload and make submap ownership semantics sane. + std::unique_ptr temp( sm ); + bool result = add_submap( p, temp ); + if( !result ) { + // NOLINTNEXTLINE( bugprone-unused-return-value ) + temp.release(); } return result; } @@ -84,7 +80,6 @@ void mapbuffer::remove_submap( tripoint addr ) debugmsg( "Tried to remove non-existing submap %d,%d,%d", addr.x, addr.y, addr.z ); return; } - delete m_target->second; submaps.erase( m_target ); } @@ -102,7 +97,7 @@ submap *mapbuffer::lookup_submap( const tripoint &p ) return nullptr; } - return iter->second; + return iter->second.get(); } void mapbuffer::save( bool delete_after_save ) @@ -182,7 +177,7 @@ void mapbuffer::save_quad( const std::string &dirname, const std::string &filena submap_addr.x += offsets_offset.x; submap_addr.y += offsets_offset.y; submap_addrs.push_back( submap_addr ); - submap *sm = submaps[submap_addr]; + submap *sm = submaps[submap_addr].get(); if( sm != nullptr && !sm->is_uniform ) { all_uniform = false; } @@ -211,7 +206,7 @@ void mapbuffer::save_quad( const std::string &dirname, const std::string &filena continue; } - submap *sm = submaps[submap_addr]; + submap *sm = submaps[submap_addr].get(); if( sm == nullptr ) { continue; @@ -272,7 +267,7 @@ submap *mapbuffer::unserialize_submaps( const tripoint &p ) quad_path, p.x, p.y, p.z ); return nullptr; } - return submaps[ p ]; + return submaps[ p ].get(); } void mapbuffer::deserialize( JsonIn &jsin ) diff --git a/src/mapbuffer.h b/src/mapbuffer.h index c90bb587f681e..f2cd10cdbcdbf 100644 --- a/src/mapbuffer.h +++ b/src/mapbuffer.h @@ -28,7 +28,7 @@ class mapbuffer void save( bool delete_after_save = false ); /** Delete all buffered submaps. **/ - void reset(); + void clear(); /** Add a new submap to the buffer. * @@ -38,10 +38,10 @@ class mapbuffer * is released (set to NULL). * @return true if the submap has been stored here. False if there * is already a submap with the specified coordinates. The submap - * is not stored than and the caller must take of the submap object - * on their own (and properly delete it). + * is not stored and the given unique_ptr retains ownsership. */ bool add_submap( const tripoint &p, std::unique_ptr &sm ); + // Old overload that we should stop using, but it's complicated bool add_submap( const tripoint &p, submap *sm ); /** Get a submap stored in this buffer. @@ -55,7 +55,7 @@ class mapbuffer submap *lookup_submap( const tripoint &p ); private: - using submap_map_t = std::map; + using submap_map_t = std::map>; public: inline submap_map_t::iterator begin() { diff --git a/src/mapdata.cpp b/src/mapdata.cpp index c6d2a9e309b56..6bbbffebf27a3 100644 --- a/src/mapdata.cpp +++ b/src/mapdata.cpp @@ -16,6 +16,7 @@ #include "generic_factory.h" #include "harvest.h" #include "iexamine.h" +#include "iexamine_actors.h" #include "item_group.h" #include "json.h" #include "output.h" @@ -408,6 +409,11 @@ bool map_data_common_t::has_examine( iexamine_function_ref func ) const return examine_func == &func; } +bool map_data_common_t::has_examine( const std::string &action ) const +{ + return examine_actor->type == action; +} + void map_data_common_t::set_examine( iexamine_function_ref func ) { examine_func = &func; @@ -415,7 +421,11 @@ void map_data_common_t::set_examine( iexamine_function_ref func ) void map_data_common_t::examine( player &guy, const tripoint &examp ) const { - examine_func( guy, examp ); + if( !examine_actor ) { + examine_func( guy, examp ); + return; + } + examine_actor->call( guy, examp ); } void map_data_common_t::load_symbol( const JsonObject &jo ) @@ -551,6 +561,8 @@ ter_id t_null, t_wall_half, t_wall_wood, t_wall_wood_chipped, t_wall_wood_broken, t_wall, t_concrete_wall, t_brick_wall, t_wall_metal, + t_scrap_wall, + t_scrap_wall_halfway, t_wall_glass, t_wall_glass_alarm, t_reinforced_glass, t_reinforced_glass_shutter, t_reinforced_glass_shutter_open, @@ -562,6 +574,7 @@ ter_id t_null, t_rdoor_o, t_door_locked_interior, t_door_locked, t_door_locked_peep, t_door_locked_alarm, t_door_frame, t_chaingate_l, t_fencegate_c, t_fencegate_o, t_chaingate_c, t_chaingate_o, + t_retractable_gate_c, t_retractable_gate_l, t_retractable_gate_o, t_door_boarded, t_door_boarded_damaged, t_door_boarded_peep, t_rdoor_boarded, t_rdoor_boarded_damaged, t_door_boarded_damaged_peep, t_door_metal_c, t_door_metal_o, t_door_metal_locked, t_door_metal_pickable, t_mdoor_frame, @@ -643,8 +656,6 @@ ter_id t_null, t_railroad_track_on_tie, t_railroad_track_h_on_tie, t_railroad_track_v_on_tie, t_railroad_track_d_on_tie; -static ter_id t_nanofab, t_nanofab_body, t_wall_b, t_wall_g, t_wall_p, t_wall_r, t_wall_w; - // TODO: Put this crap into an inclusion, which should be generated automatically using JSON data void set_ter_ids() @@ -707,6 +718,8 @@ void set_ter_ids() t_concrete_wall = ter_id( "t_concrete_wall" ); t_brick_wall = ter_id( "t_brick_wall" ); t_wall_metal = ter_id( "t_wall_metal" ); + t_scrap_wall = ter_id( "t_scrap_wall" ); + t_scrap_wall_halfway = ter_id( "t_scrap_wall_halfway" ); t_wall_glass = ter_id( "t_wall_glass" ); t_wall_glass_alarm = ter_id( "t_wall_glass_alarm" ); t_reinforced_glass = ter_id( "t_reinforced_glass" ); @@ -718,11 +731,6 @@ void set_ter_ids() t_reinforced_door_glass_o = ter_id( "t_reinforced_door_glass_o" ); t_bars = ter_id( "t_bars" ); t_reb_cage = ter_id( "t_reb_cage" ); - t_wall_b = ter_id( "t_wall_b" ); - t_wall_g = ter_id( "t_wall_g" ); - t_wall_p = ter_id( "t_wall_p" ); - t_wall_r = ter_id( "t_wall_r" ); - t_wall_w = ter_id( "t_wall_w" ); t_door_c = ter_id( "t_door_c" ); t_door_c_peep = ter_id( "t_door_c_peep" ); t_door_b = ter_id( "t_door_b" ); @@ -743,6 +751,9 @@ void set_ter_ids() t_fencegate_o = ter_id( "t_fencegate_o" ); t_chaingate_c = ter_id( "t_chaingate_c" ); t_chaingate_o = ter_id( "t_chaingate_o" ); + t_retractable_gate_l = ter_id( "t_retractable_gate_l" ); + t_retractable_gate_c = ter_id( "t_retractable_gate_c" ); + t_retractable_gate_o = ter_id( "t_retractable_gate_o" ); t_door_boarded = ter_id( "t_door_boarded" ); t_door_boarded_damaged = ter_id( "t_door_boarded_damaged" ); t_door_boarded_peep = ter_id( "t_door_boarded_peep" ); @@ -886,8 +897,6 @@ void set_ter_ids() t_rootcellar = ter_id( "t_rootcellar" ); t_cvdbody = ter_id( "t_cvdbody" ); t_cvdmachine = ter_id( "t_cvdmachine" ); - t_nanofab = ter_id( "t_nanofab" ); - t_nanofab_body = ter_id( "t_nanofab_body" ); t_stairs_down = ter_id( "t_stairs_down" ); t_stairs_up = ter_id( "t_stairs_up" ); t_manhole = ter_id( "t_manhole" ); @@ -1013,9 +1022,6 @@ furn_id f_null, f_street_light, f_traffic_light, f_console, f_console_broken; -static furn_id f_ball_mach, f_bluebell, f_dahlia, f_dandelion, f_datura, f_floor_canvas, - f_indoor_plant_y, f_lane, f_statue; - void set_furn_ids() { f_null = furn_id( "f_null" ); @@ -1029,7 +1035,6 @@ void set_furn_ids() f_sandbag_wall = furn_id( "f_sandbag_wall" ); f_bulletin = furn_id( "f_bulletin" ); f_indoor_plant = furn_id( "f_indoor_plant" ); - f_indoor_plant_y = furn_id( "f_indoor_plant_y" ); f_bed = furn_id( "f_bed" ); f_toilet = furn_id( "f_toilet" ); f_makeshift_bed = furn_id( "f_makeshift_bed" ); @@ -1046,9 +1051,7 @@ void set_furn_ids() f_trashcan = furn_id( "f_trashcan" ); f_desk = furn_id( "f_desk" ); f_exercise = furn_id( "f_exercise" ); - f_ball_mach = furn_id( "f_ball_mach" ); f_bench = furn_id( "f_bench" ); - f_lane = furn_id( "f_lane" ); f_table = furn_id( "f_table" ); f_pool_table = furn_id( "f_pool_table" ); f_counter = furn_id( "f_counter" ); @@ -1087,10 +1090,6 @@ void set_furn_ids() f_fungal_mass = furn_id( "f_fungal_mass" ); f_fungal_clump = furn_id( "f_fungal_clump" ); f_flower_fungal = furn_id( "f_flower_fungal" ); - f_bluebell = furn_id( "f_bluebell" ); - f_dahlia = furn_id( "f_dahlia" ); - f_datura = furn_id( "f_datura" ); - f_dandelion = furn_id( "f_dandelion" ); f_cattails = furn_id( "f_cattails" ); f_lilypad = furn_id( "f_lilypad" ); f_lotus = furn_id( "f_lotus" ); @@ -1105,13 +1104,11 @@ void set_furn_ids() f_fvat_full = furn_id( "f_fvat_full" ); f_wood_keg = furn_id( "f_wood_keg" ); f_standing_tank = furn_id( "f_standing_tank" ); - f_statue = furn_id( "f_statue" ); f_egg_sackbw = furn_id( "f_egg_sackbw" ); f_egg_sackcs = furn_id( "f_egg_sackcs" ); f_egg_sackws = furn_id( "f_egg_sackws" ); f_egg_sacke = furn_id( "f_egg_sacke" ); f_flower_marloss = furn_id( "f_flower_marloss" ); - f_floor_canvas = furn_id( "f_floor_canvas" ); f_kiln_empty = furn_id( "f_kiln_empty" ); f_kiln_full = furn_id( "f_kiln_full" ); f_kiln_metal_empty = furn_id( "f_kiln_metal_empty" ); @@ -1166,10 +1163,37 @@ std::string enum_to_string( season_type data ) } } // namespace io +static std::map> examine_actors; + +static void add_actor( std::unique_ptr ptr ) +{ + std::string type = ptr->type; + examine_actors[type] = cata::clone_ptr( std::move( ptr ) ); +} + +static cata::clone_ptr iexamine_actor_from_jsobj( const JsonObject &jo ) +{ + std::string type = jo.get_string( "type" ); + try { + return examine_actors.at( type ); + } catch( const std::exception & ) { + jo.throw_error( string_format( "Invalid iexamine actor %s", type ) ); + } +} + +void init_mapdata() +{ + add_actor( std::make_unique() ); +} + void map_data_common_t::load( const JsonObject &jo, const std::string & ) { - if( jo.has_member( "examine_action" ) ) { + if( jo.has_string( "examine_action" ) ) { examine_func = iexamine_function_from_string( jo.get_string( "examine_action" ) ); + } else if( jo.has_object( "examine_action" ) ) { + JsonObject data = jo.get_object( "examine_action" ); + examine_actor = iexamine_actor_from_jsobj( data ); + examine_actor->load( data ); } else { examine_func = iexamine_function_from_string( "none" ); } @@ -1387,6 +1411,9 @@ void furn_t::load( const JsonObject &jo, const std::string &src ) void map_data_common_t::check() const { + if( examine_actor ) { + examine_actor->finalize(); + } for( const string_id &harvest : harvest_by_season ) { if( !harvest.is_null() && !can_examine() ) { debugmsg( "Harvest data defined without examine function for %s", name_ ); diff --git a/src/mapdata.h b/src/mapdata.h index 6a6fa91cd44d4..ade8b739e37a1 100644 --- a/src/mapdata.h +++ b/src/mapdata.h @@ -11,6 +11,7 @@ #include #include "calendar.h" +#include "clone_ptr.h" #include "color.h" #include "translations.h" #include "type_id.h" @@ -23,6 +24,7 @@ using ter_str_id = string_id; class JsonObject; class player; +struct iexamine_actor; struct furn_t; struct itype; struct tripoint; @@ -237,6 +239,8 @@ enum ter_connects : int { TERCONN_CANVAS_WALL, }; +void init_mapdata(); + struct map_data_common_t { map_bash_info bash; map_deconstruct_info deconstruct; @@ -252,6 +256,8 @@ struct map_data_common_t { // Hardcoded examination function iexamine_function examine_func; // What happens when the terrain/furniture is examined + // Data-driven examine actor + cata::clone_ptr examine_actor; private: std::set flags; // string flags which possibly refer to what's documented above. @@ -277,6 +283,7 @@ struct map_data_common_t { bool can_examine() const; bool has_examine( iexamine_function_ref func ) const; + bool has_examine( const std::string &action ) const; void set_examine( iexamine_function_ref func ); void examine( player &, const tripoint & ) const; @@ -441,6 +448,7 @@ provided for terrains added by mods. A string equivalent is always present, i.e. t_basalt "t_basalt" */ +// NOLINTNEXTLINE(cata-static-int_id-constants) extern ter_id t_null, t_hole, // Real nothingness; makes you fall a z-level // Ground @@ -466,6 +474,8 @@ extern ter_id t_null, t_wall_half, t_wall_wood, t_wall_wood_chipped, t_wall_wood_broken, t_wall, t_concrete_wall, t_brick_wall, t_wall_metal, + t_scrap_wall, + t_scrap_wall_halfway, t_wall_glass, t_wall_glass_alarm, t_reinforced_glass, t_reinforced_glass_shutter, t_reinforced_glass_shutter_open, @@ -476,6 +486,7 @@ extern ter_id t_null, t_door_c, t_door_c_peep, t_door_b, t_door_b_peep, t_door_o, t_door_o_peep, t_door_locked_interior, t_door_locked, t_door_locked_peep, t_door_locked_alarm, t_door_frame, t_chaingate_l, t_fencegate_c, t_fencegate_o, t_chaingate_c, t_chaingate_o, + t_retractable_gate_l, t_retractable_gate_c, t_retractable_gate_o, t_door_boarded, t_door_boarded_damaged, t_door_boarded_peep, t_rdoor_boarded, t_rdoor_boarded_damaged, t_door_boarded_damaged_peep, t_door_metal_c, t_door_metal_o, t_door_metal_locked, t_door_metal_pickable, @@ -564,6 +575,7 @@ runtime index: furn_id furn_id refers to a position in the furnlist[] where the furn_t struct is stored. See note about ter_id above. */ +// NOLINTNEXTLINE(cata-static-int_id-constants) extern furn_id f_null, f_hay, f_cattails, f_lotus, f_lilypad, f_rubble, f_rubble_rock, f_wreckage, f_ash, diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 87be7a15af895..8415a198a5b74 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -103,8 +103,6 @@ static const trait_id trait_NPC_STATIC_NPC( "NPC_STATIC_NPC" ); static constexpr int MON_RADIUS = 3; static void science_room( map *m, const point &p1, const point &p2, int z, int rotate ); -static void build_mine_room( room_type type, const point &p1, const point &p2, - const mapgendata &dat ); // (x,y,z) are absolute coordinates of a submap // x%2 and y%2 must be 0! @@ -593,7 +591,6 @@ mapgen_function_json_base::mapgen_function_json_base( const json_source_location &jsrcloc, const std::string &context ) : jsrcloc( jsrcloc ) , context_( context ) - , do_format( false ) , is_ready( false ) , mapgensize( SEEX * 2, SEEY * 2 ) , objects( m_offset, mapgensize ) @@ -1088,7 +1085,7 @@ class jmapgen_gaspump : public jmapgen_piece fuel = jsi.get_string( "fuel" ); // may want to not force this, if we want to support other fuels for some reason - if( fuel != "gasoline" && fuel != "diesel" && fuel != "jp8" ) { + if( fuel != "gasoline" && fuel != "diesel" && fuel != "jp8" && fuel != "avgas" ) { jsi.throw_error( "invalid fuel", "fuel" ); } } @@ -1200,7 +1197,11 @@ class jmapgen_loot : public jmapgen_piece if( group.is_empty() ) { // Migrations are applied to item *groups* on load, but single item spawns must be // migrated individually - result_group.add_item_entry( item_controller->migrate_id( ity ), 100 ); + std::string variant; + if( jsi.has_string( "variant" ) ) { + variant = jsi.get_string( "variant" ); + } + result_group.add_item_entry( item_controller->migrate_id( ity ), 100, variant ); } else { result_group.add_group_entry( group, 100 ); } @@ -1276,7 +1277,8 @@ class jmapgen_monster : public jmapgen_piece chance( jsi, "chance", 100, 100 ) , pack_size( jsi, "pack_size", 1, 1 ) , one_or_none( jsi.get_bool( "one_or_none", - !( jsi.has_member( "repeat" ) || jsi.has_member( "pack_size" ) ) ) ) + !( jsi.has_member( "repeat" ) || + jsi.has_member( "pack_size" ) ) ) ) , friendly( jsi.get_bool( "friendly", false ) ) , name( jsi.get_string( "name", "NONE" ) ) , target( jsi.get_bool( "target", false ) ) { @@ -1317,7 +1319,16 @@ class jmapgen_monster : public jmapgen_piece if( sd.has_array( "ammo" ) ) { const JsonArray &ammos = sd.get_array( "ammo" ); for( const JsonObject adata : ammos ) { - data.ammo.emplace( itype_id( adata.get_string( "ammo_id" ) ), jmapgen_int( adata, "qty" ) ); + data.ammo.emplace( itype_id( adata.get_string( "ammo_id" ) ), + jmapgen_int( adata, "qty" ) ); + } + } + if( sd.has_array( "patrol" ) ) { + const JsonArray &patrol_pts = sd.get_array( "patrol" ); + for( const JsonObject p_pt : patrol_pts ) { + jmapgen_int ptx = jmapgen_int( p_pt, "x" ); + jmapgen_int pty = jmapgen_int( p_pt, "y" ); + data.patrol_points_rel_ms.emplace_back( ptx.get(), pty.get() ); } } } @@ -1327,7 +1338,8 @@ class jmapgen_monster : public jmapgen_piece int raw_odds = chance.get(); - // Handle spawn density: Increase odds, but don't let the odds of absence go below half the odds at density 1. + // Handle spawn density: Increase odds, but don't let the odds of absence go below + // half the odds at density 1. // Instead, apply a multiplier to the number of monsters for really high densities. // For example, a 50% chance at spawn density 4 becomes a 75% chance of ~2.7 monsters. int odds_after_density = raw_odds * get_option( "SPAWN_DENSITY" ); @@ -1345,10 +1357,12 @@ class jmapgen_monster : public jmapgen_piece int spawn_count = roll_remainder( density_multiplier ); - if( one_or_none ) { // don't let high spawn density alone cause more than 1 to spawn. + // don't let high spawn density alone cause more than 1 to spawn. + if( one_or_none ) { spawn_count = std::min( spawn_count, 1 ); } - if( raw_odds == 100 ) { // don't spawn less than 1 if odds were 100%, even with low spawn density. + // don't spawn less than 1 if odds were 100%, even with low spawn density. + if( raw_odds == 100 ) { spawn_count = std::max( spawn_count, 1 ); } else { if( !x_in_y( odds_after_density, 100 ) ) { @@ -1426,6 +1440,7 @@ class jmapgen_spawn_item : public jmapgen_piece { public: itype_id type; + std::string variant; jmapgen_int amount; jmapgen_int chance; std::set flags; @@ -1434,6 +1449,9 @@ class jmapgen_spawn_item : public jmapgen_piece , amount( jsi, "amount", 1, 1 ) , chance( jsi, "chance", 100, 100 ) , flags( jsi.get_tags( "custom-flags" ) ) { + if( jsi.has_string( "variant" ) ) { + variant = jsi.get_string( "variant" ); + } // Itemgroups apply migrations when being loaded, but we need to migrate // individual items here. type = item_controller->migrate_id( type ); @@ -1451,7 +1469,7 @@ class jmapgen_spawn_item : public jmapgen_piece int spawn_count = ( c == 100 ) ? 1 : roll_remainder( c * spawn_rate / 100.0f ); for( int i = 0; i < spawn_count; i++ ) { dat.m.spawn_item( point( x.get(), y.get() ), type, amount.get(), - 0, calendar::start_of_cataclysm, 0, flags ); + 0, calendar::start_of_cataclysm, 0, flags, variant ); } } }; @@ -1519,6 +1537,11 @@ class jmapgen_terrain : public jmapgen_piece jmapgen_terrain( const JsonObject &jsi, const std::string &/*context*/ ) : jmapgen_terrain( jsi.get_string( "ter" ) ) {} explicit jmapgen_terrain( const std::string &tid ) : id( ter_id( tid ) ) {} + + bool is_nop() const override { + return id.id().is_null(); + } + void apply( const mapgendata &dat, const jmapgen_int &x, const jmapgen_int &y ) const override { dat.m.ter_set( point( x.get(), y.get() ), id ); @@ -2174,52 +2197,27 @@ void mapgen_palette::load_place_mapings( const JsonObject &jo, const std::string continue; } auto &vect = format_placings[ key ]; - ::load_place_mapings( sub.get_member( member_name ), vect, - member_name + " in mapping in " + context ); + std::string this_context = string_format( "%s in mapping in %s", member_name, context ); + ::load_place_mapings( sub.get_member( member_name ), vect, this_context ); } } if( !jo.has_object( member_name ) ) { return; } - /* This is kind of a hack. Loading furniture/terrain from `jo` is already done in - * mapgen_palette::load_temp, continuing here would load it again and cause trouble. - */ - if( member_name == "terrain" || member_name == "furniture" ) { - return; - } for( const JsonMember member : jo.get_object( member_name ) ) { const map_key key( member ); auto &vect = format_placings[ key ]; - ::load_place_mapings( - member, vect, member_name + " " + member.name() + " in " + context ); + std::string this_context = + string_format( "%s %s in %s", member_name, member.name(), context ); + ::load_place_mapings( member, vect, this_context ); } } static std::map palettes; -static bool check_furn( const furn_id &id, const std::string &context ) -{ - const furn_t &furn = id.obj(); - if( furn.has_flag( "PLANT" ) ) { - debugmsg( "json mapgen for %s specifies furniture %s, which has flag " - "PLANT. Such furniture must be specified in a \"sealed_item\" special.", - context, furn.id.str() ); - // Only report once per mapgen object, otherwise the reports are - // very repetitive - return true; - } - return false; -} - void mapgen_palette::check() { std::string context = "palette " + id; - for( const std::pair &p : format_furniture ) { - if( check_furn( p.second, context ) ) { - return; - } - } - for( const std::pair>> &p : format_placings ) { for( const shared_ptr_fast &j : p.second ) { @@ -2277,11 +2275,8 @@ void mapgen_palette::add( const mapgen_palette &rh ) for( const auto &placing : rh.format_placings ) { format_placings[ placing.first ] = placing.second; } - for( const auto &placing : rh.format_terrain ) { - format_terrain[ placing.first ] = placing.second; - } - for( const auto &placing : rh.format_furniture ) { - format_furniture[ placing.first ] = placing.second; + for( const auto &placing : rh.keys_with_terrain ) { + keys_with_terrain.insert( placing ); } } @@ -2290,8 +2285,7 @@ mapgen_palette mapgen_palette::load_internal( const JsonObject &jo, const std::s { mapgen_palette new_pal; auto &format_placings = new_pal.format_placings; - auto &format_terrain = new_pal.format_terrain; - auto &format_furniture = new_pal.format_furniture; + auto &keys_with_terrain = new_pal.keys_with_terrain; if( require_id ) { new_pal.id = jo.get_string( "id" ); } @@ -2308,39 +2302,17 @@ mapgen_palette mapgen_palette::load_internal( const JsonObject &jo, const std::s } // mandatory: every character in rows must have matching entry, unless fill_ter is set - // "terrain": { "a": "t_grass", "b": "t_lava" } + // "terrain": { "a": "t_grass", "b": "t_lava" }. To help enforce this we + // keep track of everything in the "terrain" object if( jo.has_member( "terrain" ) ) { for( const JsonMember member : jo.get_object( "terrain" ) ) { - const map_key key( member ); - if( member.test_string() ) { - format_terrain[key] = ter_id( member.get_string() ); - } else { - auto &vect = format_placings[ key ]; - ::load_place_mapings( - member, vect, "terrain " + member.name() + " in palette " + new_pal.id ); - if( !vect.empty() ) { - // Dummy entry to signal that this terrain is actually defined, because - // the code below checks that each square on the map has a valid terrain - // defined somehow. - format_terrain[key] = t_null; - } - } + keys_with_terrain.insert( map_key( member ) ); } } - if( jo.has_object( "furniture" ) ) { - for( const JsonMember member : jo.get_object( "furniture" ) ) { - const map_key key( member ); - if( member.test_string() ) { - format_furniture[key] = furn_id( member.get_string() ); - } else { - auto &vect = format_placings[ key ]; - ::load_place_mapings( - member, vect, "furniture " + member.name() + " in palette " + new_pal.id ); - } - } - } std::string c = "palette " + new_pal.id; + new_pal.load_place_mapings( jo, "terrain", format_placings, c ); + new_pal.load_place_mapings( jo, "furniture", format_placings, c ); new_pal.load_place_mapings( jo, "fields", format_placings, c ); new_pal.load_place_mapings( jo, "npcs", format_placings, c ); new_pal.load_place_mapings( jo, "signs", format_placings, c ); @@ -2368,6 +2340,15 @@ mapgen_palette mapgen_palette::load_internal( const JsonObject &jo, const std::s new_pal.load_place_mapings( jo, "ter_furn_transforms", format_placings, c ); new_pal.load_place_mapings( jo, "faction_owner_character", format_placings, c ); + + for( mapgen_palette::placing_map::value_type &p : format_placings ) { + p.second.erase( + std::remove_if( + p.second.begin(), p.second.end(), + []( const shared_ptr_fast &placing ) { + return placing->is_nop(); + } ), p.second.end() ); + } return new_pal; } @@ -2467,15 +2448,13 @@ bool mapgen_function_json_base::setup_common( const JsonObject &jo ) JsonArray sparray; JsonObject pjo; - format.resize( static_cast( mapgensize.x * mapgensize.y ) ); // just like mapf::basic_bind("stuff",blargle("foo", etc) ), only json input and faster when applying if( jo.has_array( "rows" ) ) { mapgen_palette palette = mapgen_palette::load_temp( jo, "dda" ); - auto &format_terrain = palette.format_terrain; - auto &format_furniture = palette.format_furniture; + auto &keys_with_terrain = palette.keys_with_terrain; auto &format_placings = palette.format_placings; - if( format_terrain.empty() ) { + if( palette.keys_with_terrain.empty() ) { return false; } @@ -2505,12 +2484,10 @@ bool mapgen_function_json_base::setup_common( const JsonObject &jo ) for( int i = m_offset.x; i < expected_dim.x; i++ ) { const point p = point( i, c ) - m_offset; const map_key key = row_keys[i]; - const auto iter_ter = format_terrain.find( key ); - const auto iter_furn = format_furniture.find( key ); + const auto iter_ter = keys_with_terrain.find( key ); const auto fpi = format_placings.find( key ); - const bool has_terrain = iter_ter != format_terrain.end(); - const bool has_furn = iter_furn != format_furniture.end(); + const bool has_terrain = iter_ter != keys_with_terrain.end(); const bool has_placing = fpi != format_placings.end(); if( !has_terrain && !fallback_terrain_exists ) { @@ -2519,8 +2496,7 @@ bool mapgen_function_json_base::setup_common( const JsonObject &jo ) "'%s' is not in 'terrain', and no 'fill_ter' is set!", c + 1, i + 1, key.str ), c, i + 1 ); } - if( !has_terrain && !has_furn && !has_placing && - key.str != " " && key.str != "." ) { + if( !has_terrain && !has_placing && key.str != " " && key.str != "." ) { try { parray.string_error( string_format( "format: rows: row %d column %d: " @@ -2530,12 +2506,6 @@ bool mapgen_function_json_base::setup_common( const JsonObject &jo ) debugmsg( "(json-error)\n%s", e.what() ); } } - if( has_terrain ) { - format[ calc_index( p ) ].ter = iter_ter->second; - } - if( has_furn ) { - format[ calc_index( p ) ].furn = iter_furn->second; - } if( has_placing ) { jmapgen_place where( p ); for( auto &what : fpi->second ) { @@ -2545,7 +2515,6 @@ bool mapgen_function_json_base::setup_common( const JsonObject &jo ) } } fallback_terrain_exists = true; - do_format = true; } // No fill_ter? No format? GTFO. @@ -2603,14 +2572,22 @@ void mapgen_function_json_nested::check() const check_common(); } -void mapgen_function_json_base::check_common() const +static bool check_furn( const furn_id &id, const std::string &context ) { - for( const ter_furn_id &id : format ) { - if( check_furn( id.furn, context_ ) ) { - return; - } + const furn_t &furn = id.obj(); + if( furn.has_flag( "PLANT" ) ) { + debugmsg( "json mapgen for %s specifies furniture %s, which has flag " + "PLANT. Such furniture must be specified in a \"sealed_item\" special.", + context, furn.id.str() ); + // Only report once per mapgen object, otherwise the reports are + // very repetitive + return true; } + return false; +} +void mapgen_function_json_base::check_common() const +{ for( const jmapgen_setmap &setmap : setmap_points ) { if( setmap.op != JMAPGEN_SETMAP_FURN && setmap.op != JMAPGEN_SETMAP_LINE_FURN && @@ -2733,12 +2710,11 @@ bool jmapgen_setmap::apply( const mapgendata &dat, const point &offset ) const } break; case JMAPGEN_SETMAP_SQUARE_RADIATION: { - const int cx = x_get(); - const int cy = y_get(); + const point c2( x_get(), y_get() ); const int cx2 = x2_get(); const int cy2 = y2_get(); - for( int tx = cx; tx <= cx2; tx++ ) { - for( int ty = cy; ty <= cy2; ty++ ) { + for( int tx = c2.x; tx <= cx2; tx++ ) { + for( int ty = c2.y; ty <= cy2; ty++ ) { m.set_radiation( point( tx, ty ), static_cast( val.get() ) ); } } @@ -2791,44 +2767,9 @@ bool jmapgen_setmap::has_vehicle_collision( const mapgendata &dat, const point & return false; } -void mapgen_function_json_base::formatted_set_incredibly_simple( map &m, const point &offset ) const -{ - for( int y = 0; y < mapgensize.y; y++ ) { - for( int x = 0; x < mapgensize.x; x++ ) { - point p( x, y ); - const size_t index = calc_index( p ); - const ter_furn_id &tdata = format[index]; - const point map_pos = p + offset; - if( tdata.furn != f_null ) { - if( tdata.ter != t_null ) { - m.set( map_pos, tdata.ter, tdata.furn ); - } else { - m.furn_set( map_pos, tdata.furn ); - } - } else if( tdata.ter != t_null ) { - m.ter_set( map_pos, tdata.ter ); - } - } - } -} - bool mapgen_function_json_base::has_vehicle_collision( const mapgendata &dat, const point &offset ) const { - if( do_format ) { - for( int y = 0; y < mapgensize.y; y++ ) { - for( int x = 0; x < mapgensize.x; x++ ) { - const point p( x, y ); - const ter_furn_id &tdata = format[calc_index( p )]; - const point map_pos = p + offset; - if( ( tdata.furn != f_null || tdata.ter != t_null ) && - dat.m.veh_at( tripoint( map_pos, dat.zlevel() ) ).has_value() ) { - return true; - } - } - } - } - for( const jmapgen_setmap &elem : setmap_points ) { if( elem.has_vehicle_collision( dat, offset ) ) { return true; @@ -2865,9 +2806,6 @@ void mapgen_function_json::generate( mapgendata &md ) m->rotate( ( -static_cast( md.terrain_type()->get_dir() ) + 4 ) % 4 ); } } - if( do_format ) { - formatted_set_incredibly_simple( *m, point_zero ); - } for( auto &elem : setmap_points ) { elem.apply( md, point_zero ); } @@ -2888,10 +2826,6 @@ void mapgen_function_json_nested::nest( const mapgendata &dat, const point &offs // TODO: Make rotation work for submaps, then pass this value into elem & objects apply. //int chosen_rotation = rotation.get() % 4; - if( do_format ) { - formatted_set_incredibly_simple( dat.m, offset ); - } - for( const jmapgen_setmap &elem : setmap_points ) { elem.apply( dat, offset ); } @@ -2970,8 +2904,6 @@ void map::draw_map( mapgendata &dat ) draw_triffid( dat ); } else if( is_ot_match( "spider", terrain_type, ot_match_type::prefix ) ) { draw_spider_pit( dat ); - } else if( is_ot_match( "spiral", terrain_type, ot_match_type::prefix ) ) { - draw_spiral( dat ); } else if( is_ot_match( "temple", terrain_type, ot_match_type::prefix ) ) { draw_temple( dat ); } else if( is_ot_match( "mine", terrain_type, ot_match_type::prefix ) ) { @@ -2999,6 +2931,7 @@ void map::draw_map( mapgendata &dat ) static const int SOUTH_EDGE = 2 * SEEY - 1; static const int EAST_EDGE = 2 * SEEX - 1; +// NOLINTNEXTLINE(readability-function-size) void map::draw_lab( mapgendata &dat ) { const oter_id &terrain_type = dat.terrain_type(); @@ -3290,37 +3223,37 @@ void map::draw_lab( mapgendata &dat ) if( is_ot_match( "stairs", terrain_type, ot_match_type::contains ) ) { // Stairs going down std::vector stair_points; if( tw != 0 ) { - stair_points.push_back( point( SEEX - 1, 2 ) ); - stair_points.push_back( point( SEEX - 1, 2 ) ); - stair_points.push_back( point( SEEX, 2 ) ); - stair_points.push_back( point( SEEX, 2 ) ); + stair_points.emplace_back( SEEX - 1, 2 ); + stair_points.emplace_back( SEEX - 1, 2 ); + stair_points.emplace_back( SEEX, 2 ); + stair_points.emplace_back( SEEX, 2 ); } if( rw != 1 ) { - stair_points.push_back( point( SEEX * 2 - 3, SEEY - 1 ) ); - stair_points.push_back( point( SEEX * 2 - 3, SEEY - 1 ) ); - stair_points.push_back( point( SEEX * 2 - 3, SEEY ) ); - stair_points.push_back( point( SEEX * 2 - 3, SEEY ) ); + stair_points.emplace_back( SEEX * 2 - 3, SEEY - 1 ); + stair_points.emplace_back( SEEX * 2 - 3, SEEY - 1 ); + stair_points.emplace_back( SEEX * 2 - 3, SEEY ); + stair_points.emplace_back( SEEX * 2 - 3, SEEY ); } if( bw != 1 ) { - stair_points.push_back( point( SEEX - 1, SEEY * 2 - 3 ) ); - stair_points.push_back( point( SEEX - 1, SEEY * 2 - 3 ) ); - stair_points.push_back( point( SEEX, SEEY * 2 - 3 ) ); - stair_points.push_back( point( SEEX, SEEY * 2 - 3 ) ); + stair_points.emplace_back( SEEX - 1, SEEY * 2 - 3 ); + stair_points.emplace_back( SEEX - 1, SEEY * 2 - 3 ); + stair_points.emplace_back( SEEX, SEEY * 2 - 3 ); + stair_points.emplace_back( SEEX, SEEY * 2 - 3 ); } if( lw != 0 ) { - stair_points.push_back( point( 2, SEEY - 1 ) ); - stair_points.push_back( point( 2, SEEY - 1 ) ); - stair_points.push_back( point( 2, SEEY ) ); - stair_points.push_back( point( 2, SEEY ) ); + stair_points.emplace_back( 2, SEEY - 1 ); + stair_points.emplace_back( 2, SEEY - 1 ); + stair_points.emplace_back( 2, SEEY ); + stair_points.emplace_back( 2, SEEY ); } - stair_points.push_back( point( static_cast( SEEX / 2 ), SEEY ) ); - stair_points.push_back( point( static_cast( SEEX / 2 ), SEEY - 1 ) ); - stair_points.push_back( point( static_cast( SEEX / 2 ) + SEEX, SEEY ) ); - stair_points.push_back( point( static_cast( SEEX / 2 ) + SEEX, SEEY - 1 ) ); - stair_points.push_back( point( SEEX, static_cast( SEEY / 2 ) ) ); - stair_points.push_back( point( SEEX + 2, static_cast( SEEY / 2 ) ) ); - stair_points.push_back( point( SEEX, static_cast( SEEY / 2 ) + SEEY ) ); - stair_points.push_back( point( SEEX + 2, static_cast( SEEY / 2 ) + SEEY ) ); + stair_points.emplace_back( static_cast( SEEX / 2 ), SEEY ); + stair_points.emplace_back( static_cast( SEEX / 2 ), SEEY - 1 ); + stair_points.emplace_back( static_cast( SEEX / 2 ) + SEEX, SEEY ); + stair_points.emplace_back( static_cast( SEEX / 2 ) + SEEX, SEEY - 1 ); + stair_points.emplace_back( SEEX, static_cast( SEEY / 2 ) ); + stair_points.emplace_back( SEEX + 2, static_cast( SEEY / 2 ) ); + stair_points.emplace_back( SEEX, static_cast( SEEY / 2 ) + SEEY ); + stair_points.emplace_back( SEEX + 2, static_cast( SEEY / 2 ) + SEEY ); const point p = random_entry( stair_points ); ter_set( p, t_stairs_down ); } @@ -3633,15 +3566,14 @@ void map::draw_lab( mapgendata &dat ) center.xy() + point_west, 1, true ); // damaged mininuke/plut thrown past edge of rubble so the player can see it. - int marker_x = center.x - 2 + 4 * rng( 0, 1 ); - int marker_y = center.y + rng( -2, 2 ); + point marker( center.xy() + point( -2 + 4 * rng( 0, 1 ), rng( -2, 2 ) ) ); if( one_in( 4 ) ) { - spawn_item( point( marker_x, marker_y ), + spawn_item( marker, "mininuke", 1, 1, calendar::turn_zero, rng( 2, 4 ) ); } else { item newliquid( "plut_slurry_dense", calendar::start_of_cataclysm ); newliquid.charges = 1; - add_item_or_charges( tripoint( marker_x, marker_y, get_abs_sub().z ), + add_item_or_charges( tripoint( marker, get_abs_sub().z ), newliquid ); } break; @@ -4157,26 +4089,27 @@ void map::draw_temple( const mapgendata &dat ) square( this, t_rock, point( SEEX + 2, 0 ), point( EAST_EDGE, 1 ) ); square( this, t_rock, point( SEEX + 2, SEEY * 2 - 2 ), point( EAST_EDGE, SOUTH_EDGE ) ); square( this, t_rock, point( SEEX + 5, 2 ), point( EAST_EDGE, SEEY * 2 - 3 ) ); - int x = rng( SEEX - 1, SEEX + 2 ), y = 2; + point p2( rng( SEEX - 1, SEEX + 2 ), 2 ); std::vector path; // Path, from end to start - while( x < SEEX - 1 || x > SEEX + 2 || y < SEEY * 2 - 2 ) { + while( p2.x < SEEX - 1 || p2.x > SEEX + 2 || p2.y < SEEY * 2 - 2 ) { static const std::vector terrains = { t_floor_red, t_floor_green, t_floor_blue, }; - path.push_back( point( x, y ) ); - ter_set( point( x, y ), random_entry( terrains ) ); - if( y == SEEY * 2 - 2 ) { - if( x < SEEX - 1 ) { - x++; - } else if( x > SEEX + 2 ) { - x--; + path.emplace_back( p2 ); + ter_set( p2, random_entry( terrains ) ); + if( p2.y == SEEY * 2 - 2 ) { + if( p2.x < SEEX - 1 ) { + p2.x++; + } else if( p2.x > SEEX + 2 ) { + p2.x--; + } } else { std::vector next; - for( int nx = x - 1; nx <= x + 1; nx++ ) { - for( int ny = y; ny <= y + 1; ny++ ) { + for( int nx = p2.x - 1; nx <= p2.x + 1; nx++ ) { + for( int ny = p2.y; ny <= p2.y + 1; ny++ ) { if( ter( point( nx, ny ) ) == t_rock_floor ) { - next.push_back( point( nx, ny ) ); + next.emplace_back( nx, ny ); } } } @@ -4184,8 +4117,8 @@ void map::draw_temple( const mapgendata &dat ) break; } else { const point p = random_entry( next ); - x = p.x; - y = p.y; + p2.x = p.x; + p2.y = p.y; } } } @@ -4254,48 +4187,7 @@ void map::draw_temple( const mapgendata &dat ) void map::draw_mine( mapgendata &dat ) { const oter_id &terrain_type = dat.terrain_type(); - if( terrain_type == "mine_entrance" ) { - dat.fill_groundcover(); - int tries = 0; - bool build_shaft = true; - do { - point p1( rng( 1, 2 * SEEX - 10 ), rng( 1, 2 * SEEY - 10 ) ); - point p2( p1 + point( rng( 4, 9 ), rng( 4, 9 ) ) ); - if( build_shaft ) { - build_mine_room( room_mine_shaft, p1, p2, dat ); - build_shaft = false; - } else { - bool okay = true; - for( int x = p1.x - 1; x <= p2.x + 1 && okay; x++ ) { - for( int y = p1.y - 1; y <= p2.y + 1 && okay; y++ ) { - okay = dat.is_groundcover( ter( point( x, y ) ) ); - } - } - if( okay ) { - room_type type = static_cast( rng( room_mine_office, room_mine_housing ) ); - build_mine_room( type, p1, p2, dat ); - tries = 0; - } else { - tries++; - } - } - } while( tries < 5 ); - int ladderx = rng( 0, EAST_EDGE ), laddery = rng( 0, SOUTH_EDGE ); - while( !dat.is_groundcover( ter( point( ladderx, laddery ) ) ) ) { - ladderx = rng( 0, EAST_EDGE ); - laddery = rng( 0, SOUTH_EDGE ); - } - ter_set( point( ladderx, laddery ), t_manhole_cover ); - } else if( terrain_type == "mine_shaft" ) { - // Not intended to actually be inhabited! - fill_background( this, t_rock ); - square( this, t_hole, point( SEEX - 3, SEEY - 3 ), point( SEEX + 2, SEEY + 2 ) ); - line( this, t_grate, point( SEEX - 3, SEEY - 4 ), point( SEEX + 2, SEEY - 4 ) ); - ter_set( point( SEEX - 3, SEEY - 5 ), t_ladder_up ); - ter_set( point( SEEX + 2, SEEY - 5 ), t_ladder_down ); - rotate( rng( 0, 3 ) ); - } else if( terrain_type == "mine" || - terrain_type == "mine_down" ) { + if( terrain_type == "mine" || terrain_type == "mine_down" ) { if( is_ot_match( "mine", dat.north(), ot_match_type::prefix ) ) { dat.n_fac = ( one_in( 10 ) ? 0 : -2 ); } else { @@ -4329,140 +4221,105 @@ void map::draw_mine( mapgendata &dat ) } } - if( dat.above() == "mine_shaft" ) { // We need the entrance room - square( this, t_floor, point( 10, 10 ), point( 15, 15 ) ); - line( this, t_wall, point( 9, 9 ), point( 16, 9 ) ); - line( this, t_wall, point( 9, 16 ), point( 16, 16 ) ); - line( this, t_wall, point( 9, 10 ), point( 9, 15 ) ); - line( this, t_wall, point( 16, 10 ), point( 16, 15 ) ); - line( this, t_wall, point( 10, 11 ), point( 12, 11 ) ); - ter_set( point( 10, 10 ), t_elevator_control ); - ter_set( point( 11, 10 ), t_elevator ); - ter_set( point( 10, 12 ), t_ladder_up ); - line_furn( this, f_counter, point( 10, 15 ), point( 15, 15 ) ); - place_items( item_group_id( "mine_equipment" ), 86, point( 10, 15 ), point( 15, 15 ), - false, calendar::start_of_cataclysm ); - if( one_in( 2 ) ) { - ter_set( point( 9, 12 ), t_door_c ); - } else { - ter_set( point( 16, 12 ), t_door_c ); - } - - } else { // Not an entrance; maybe some hazards! - switch( rng( 0, 6 ) ) { - case 0: - break; // Nothing! Lucky! + // Not an entrance; maybe some hazards! + switch( rng( 0, 6 ) ) { + case 0: + break; // Nothing! Lucky! - case 1: { // Toxic gas - int cx = rng( 9, 14 ); - int cy = rng( 9, 14 ); - ter_set( point( cx, cy ), t_rock ); - add_field( {cx, cy, abs_sub.z}, fd_gas_vent, 2 ); - } - break; + case 1: { // Toxic gas + point gas_vent_location( rng( 9, 14 ), rng( 9, 14 ) ); + ter_set( point( gas_vent_location ), t_rock ); + add_field( { gas_vent_location, abs_sub.z}, fd_gas_vent, 2 ); + } + break; - case 2: { // Lava - int x1 = rng( 6, SEEX ); - int y1 = rng( 6, SEEY ); - int x2 = rng( SEEX + 1, SEEX * 2 - 7 ); - int y2 = rng( SEEY + 1, SEEY * 2 - 7 ); - int num = rng( 2, 4 ); - for( int i = 0; i < num; i++ ) { - int lx1 = x1 + rng( -1, 1 ), lx2 = x2 + rng( -1, 1 ), - ly1 = y1 + rng( -1, 1 ), ly2 = y2 + rng( -1, 1 ); - line( this, t_lava, point( lx1, ly1 ), point( lx2, ly2 ) ); - } + case 2: { // Lava + point start_location( rng( 6, SEEX ), rng( 6, SEEY ) ); + point end_location( rng( SEEX + 1, SEEX * 2 - 7 ), rng( SEEY + 1, SEEY * 2 - 7 ) ); + const int num = rng( 2, 4 ); + for( int i = 0; i < num; i++ ) { + int lx1 = start_location.x + rng( -1, 1 ); + int lx2 = end_location.x + rng( -1, 1 ); + int ly1 = start_location.y + rng( -1, 1 ); + int ly2 = end_location.y + rng( -1, 1 ); + line( this, t_lava, point( lx1, ly1 ), point( lx2, ly2 ) ); } - break; + } + break; - case 3: { // Wrecked equipment - int x = rng( 9, 14 ); - int y = rng( 9, 14 ); - for( int i = x - 3; i < x + 3; i++ ) { - for( int j = y - 3; j < y + 3; j++ ) { - if( !one_in( 4 ) ) { - make_rubble( tripoint( i, j, abs_sub.z ), f_wreckage, true ); - } + case 3: { // Wrecked equipment + point wreck_location( rng( 9, 14 ), rng( 9, 14 ) ); + for( int i = wreck_location.x - 3; i < wreck_location.x + 3; i++ ) { + for( int j = wreck_location.y - 3; j < wreck_location.y + 3; j++ ) { + if( !one_in( 4 ) ) { + make_rubble( tripoint( i, j, abs_sub.z ), f_wreckage, true ); } } - place_items( item_group_id( "wreckage" ), 70, point( x - 3, y - 3 ), - point( x + 2, y + 2 ), false, calendar::start_of_cataclysm ); } - break; + place_items( item_group_id( "wreckage" ), 70, wreck_location + point( -3, -3 ), + wreck_location + point( 2, 2 ), false, calendar::start_of_cataclysm ); + } + break; - case 4: { // Dead miners - int num_bodies = rng( 4, 8 ); - for( int i = 0; i < num_bodies; i++ ) { - if( const auto body = random_point( *this, [this]( const tripoint & p ) { - return move_cost( p ) == 2; - } ) ) { - add_item( *body, item::make_corpse() ); - place_items( item_group_id( "mine_equipment" ), 60, *body, *body, - false, calendar::start_of_cataclysm ); - } + case 4: { // Dead miners + const int num_bodies = rng( 4, 8 ); + for( int i = 0; i < num_bodies; i++ ) { + if( const auto body = random_point( *this, [this]( const tripoint & p ) { + return move_cost( p ) == 2; + } ) ) { + add_item( *body, item::make_corpse() ); + place_items( item_group_id( "mine_equipment" ), 60, *body, *body, + false, calendar::start_of_cataclysm ); } } - break; + } + break; - case 5: { // Dark worm! - int num_worms = rng( 1, 5 ); - for( int i = 0; i < num_worms; i++ ) { - std::vector sides; - if( dat.n_fac == 6 ) { - sides.push_back( direction::NORTH ); - } - if( dat.e_fac == 6 ) { - sides.push_back( direction::EAST ); - } - if( dat.s_fac == 6 ) { - sides.push_back( direction::SOUTH ); - } - if( dat.w_fac == 6 ) { - sides.push_back( direction::WEST ); - } - if( sides.empty() ) { - place_spawns( GROUP_DARK_WYRM, 1, point( SEEX, SEEY ), point( SEEX, SEEY ), 1, true ); - i = num_worms; - } else { - point p; - switch( random_entry( sides ) ) { - case direction::NORTH: - p = point( rng( 1, SEEX * 2 - 2 ), rng( 1, 5 ) ); - break; - case direction::EAST: - p = point( SEEX * 2 - rng( 2, 6 ), rng( 1, SEEY * 2 - 2 ) ); - break; - case direction::SOUTH: - p = point( rng( 1, SEEX * 2 - 2 ), SEEY * 2 - rng( 2, 6 ) ); - break; - case direction::WEST: - p = point( rng( 1, 5 ), rng( 1, SEEY * 2 - 2 ) ); - break; - default: - break; - } - ter_set( p, t_rock_floor ); - place_spawns( GROUP_DARK_WYRM, 1, p, p, 1, true ); + case 5: { // Dark worm! + const int num_worms = rng( 1, 5 ); + for( int i = 0; i < num_worms; i++ ) { + std::vector sides; + if( dat.n_fac == 6 ) { + sides.push_back( direction::NORTH ); + } + if( dat.e_fac == 6 ) { + sides.push_back( direction::EAST ); + } + if( dat.s_fac == 6 ) { + sides.push_back( direction::SOUTH ); + } + if( dat.w_fac == 6 ) { + sides.push_back( direction::WEST ); + } + if( sides.empty() ) { + place_spawns( GROUP_DARK_WYRM, 1, point( SEEX, SEEY ), point( SEEX, SEEY ), 1, true ); + i = num_worms; + } else { + point p; + switch( random_entry( sides ) ) { + case direction::NORTH: + p = point( rng( 1, SEEX * 2 - 2 ), rng( 1, 5 ) ); + break; + case direction::EAST: + p = point( SEEX * 2 - rng( 2, 6 ), rng( 1, SEEY * 2 - 2 ) ); + break; + case direction::SOUTH: + p = point( rng( 1, SEEX * 2 - 2 ), SEEY * 2 - rng( 2, 6 ) ); + break; + case direction::WEST: + p = point( rng( 1, 5 ), rng( 1, SEEY * 2 - 2 ) ); + break; + default: + break; } + ter_set( p, t_rock_floor ); + place_spawns( GROUP_DARK_WYRM, 1, p, p, 1, true ); } } - break; - - case 6: { // Spiral - int orx = rng( SEEX - 4, SEEX ), ory = rng( SEEY - 4, SEEY ); - line( this, t_rock, point( orx, ory ), point( orx + 5, ory ) ); - line( this, t_rock, point( orx + 5, ory ), point( orx + 5, ory + 5 ) ); - line( this, t_rock, point( orx + 1, ory + 5 ), point( orx + 5, ory + 5 ) ); - line( this, t_rock, point( orx + 1, ory + 2 ), point( orx + 1, ory + 4 ) ); - line( this, t_rock, point( orx + 1, ory + 2 ), point( orx + 3, ory + 2 ) ); - ter_set( point( orx + 3, ory + 3 ), t_rock ); - add_item( point( orx + 2, ory + 3 ), item::make_corpse() ); - place_items( item_group_id( "mine_equipment" ), 60, point( orx + 2, ory + 3 ), - point( orx + 2, ory + 3 ), false, calendar::start_of_cataclysm ); - } - break; } + break; } + if( terrain_type == "mine_down" ) { // Don't forget to build a slope down! std::vector open; if( dat.n_fac == 4 ) { @@ -4626,28 +4483,27 @@ void map::draw_mine( mapgendata &dat ) // Now, pick and generate a type of finale! int rn = 0; if( face.empty() ) { - rn = rng( 1, 3 ); // Amigara fault is not valid + rn = rng( 1, 2 ); // Amigara fault is not valid } else { - rn = rng( 1, 4 ); + rn = rng( 1, 3 ); } computer *tmpcomp = nullptr; switch( rn ) { case 1: { // Wyrms - int x = rng( SEEX, SEEX + 1 ), y = rng( SEEY, SEEY + 1 ); - ter_set( point( x, y ), t_pedestal_wyrm ); - spawn_item( point( x, y ), "petrified_eye" ); + const point p2( rng( SEEX, SEEX + 1 ), rng( SEEY, SEEY + 1 ) ); + ter_set( p2, t_pedestal_wyrm ); + spawn_item( p2, "petrified_eye" ); } break; // That's it! game::examine handles the pedestal/wyrm spawns case 2: { // The Thing dog - int num_bodies = rng( 4, 8 ); + const int num_bodies = rng( 4, 8 ); for( int i = 0; i < num_bodies; i++ ) { - int x = rng( 4, SEEX * 2 - 5 ); - int y = rng( 4, SEEX * 2 - 5 ); - add_item( point( x, y ), item::make_corpse() ); - place_items( item_group_id( "mine_equipment" ), 60, point( x, y ), - point( x, y ), false, calendar::start_of_cataclysm ); + point p3( rng( 4, SEEX * 2 - 5 ), rng( 4, SEEX * 2 - 5 ) ); + add_item( p3, item::make_corpse() ); + place_items( item_group_id( "mine_equipment" ), 60, p3, + p3, false, calendar::start_of_cataclysm ); } place_spawns( GROUP_DOG_THING, 1, point( SEEX, SEEX ), point( SEEX + 1, SEEX + 1 ), 1, true, true ); spawn_artifact( tripoint( rng( SEEX, SEEX + 1 ), rng( SEEY, SEEY + 1 ), abs_sub.z ), @@ -4655,25 +4511,7 @@ void map::draw_mine( mapgendata &dat ) } break; - case 3: { // Spiral down - line( this, t_rock, point( 5, 5 ), point( 5, 18 ) ); - line( this, t_rock, point( 5, 5 ), point( 18, 5 ) ); - line( this, t_rock, point( 18, 5 ), point( 18, 18 ) ); - line( this, t_rock, point( 8, 18 ), point( 18, 18 ) ); - line( this, t_rock, point( 8, 8 ), point( 8, 18 ) ); - line( this, t_rock, point( 8, 8 ), point( 15, 8 ) ); - line( this, t_rock, point( 15, 8 ), point( 15, 15 ) ); - line( this, t_rock, point( 10, 15 ), point( 15, 15 ) ); - line( this, t_rock, point( 10, 10 ), point( 10, 15 ) ); - line( this, t_rock, point( 10, 10 ), point( 13, 10 ) ); - line( this, t_rock, point( 13, 10 ), point( 13, 13 ) ); - ter_set( point( 12, 13 ), t_rock ); - ter_set( point( 12, 12 ), t_slope_down ); - ter_set( point( 12, 11 ), t_slope_down ); - } - break; - - case 4: { // Amigara fault + case 3: { // Amigara fault // Construct the fault on the appropriate face switch( random_entry( face ) ) { case direction::NORTH: @@ -4708,63 +4546,6 @@ void map::draw_mine( mapgendata &dat ) } } -void map::draw_spiral( const mapgendata &dat ) -{ - const oter_id &terrain_type = dat.terrain_type(); - if( terrain_type == "spiral_hub" ) { - fill_background( this, t_rock_floor ); - line( this, t_rock, point( 23, 0 ), point( 23, 23 ) ); - line( this, t_rock, point( 2, 23 ), point( 23, 23 ) ); - line( this, t_rock, point( 2, 4 ), point( 2, 23 ) ); - line( this, t_rock, point( 2, 4 ), point( 18, 4 ) ); - line( this, t_rock, point( 18, 4 ), point( 18, 18 ) ); // bad - line( this, t_rock, point( 6, 18 ), point( 18, 18 ) ); - line( this, t_rock, point( 6, 7 ), point( 6, 18 ) ); - line( this, t_rock, point( 6, 7 ), point( 15, 7 ) ); - line( this, t_rock, point( 15, 7 ), point( 15, 15 ) ); - line( this, t_rock, point( 8, 15 ), point( 15, 15 ) ); - line( this, t_rock, point( 8, 9 ), point( 8, 15 ) ); - line( this, t_rock, point( 8, 9 ), point( 13, 9 ) ); - line( this, t_rock, point( 13, 9 ), point( 13, 13 ) ); - line( this, t_rock, point( 10, 13 ), point( 13, 13 ) ); - line( this, t_rock, point( 10, 11 ), point( 10, 13 ) ); - square( this, t_slope_up, point( 11, 11 ), point( 12, 12 ) ); - rotate( rng( 0, 3 ) ); - } else if( terrain_type == "spiral" ) { - fill_background( this, t_rock_floor ); - const int num_spiral = rng( 1, 4 ); - std::list offsets; - const int spiral_width = 8; - // Divide the room into quadrants, and place a spiral origin - // at a random offset within each quadrant. - for( int x = 0; x < 2; ++x ) { - for( int y = 0; y < 2; ++y ) { - const int x_jitter = rng( 0, SEEX - spiral_width ); - const int y_jitter = rng( 0, SEEY - spiral_width ); - offsets.push_back( point( ( x * SEEX ) + x_jitter, - ( y * SEEY ) + y_jitter ) ); - } - } - - // Randomly place from 1 - 4 of the spirals at the chosen offsets. - for( int i = 0; i < num_spiral; i++ ) { - const point chosen_point = random_entry_removed( offsets ); - const int orx = chosen_point.x; - const int ory = chosen_point.y; - - line( this, t_rock, point( orx, ory ), point( orx + 5, ory ) ); - line( this, t_rock, point( orx + 5, ory ), point( orx + 5, ory + 5 ) ); - line( this, t_rock, point( orx + 1, ory + 5 ), point( orx + 5, ory + 5 ) ); - line( this, t_rock, point( orx + 1, ory + 2 ), point( orx + 1, ory + 4 ) ); - line( this, t_rock, point( orx + 1, ory + 2 ), point( orx + 3, ory + 2 ) ); - ter_set( point( orx + 3, ory + 3 ), t_rock ); - ter_set( point( orx + 2, ory + 3 ), t_rock_floor ); - place_items( item_group_id( "spiral" ), 60, point( orx + 2, ory + 3 ), - point( orx + 2, ory + 3 ), false, calendar::turn_zero ); - } - } -} - void map::draw_spider_pit( const mapgendata &dat ) { const oter_id &terrain_type = dat.terrain_type(); @@ -4937,25 +4718,24 @@ void map::draw_triffid( const mapgendata &dat ) // Chance increases by 1 each turn, and gives the % chance of forcing a move // to the right or down. int chance = 0; - int x = 4; - int y = 4; + point p( 4, 4 ); do { - ter_set( point( x, y ), t_dirt ); + ter_set( p, t_dirt ); if( chance >= 10 && one_in( 10 ) ) { // Add a spawn - place_spawns( GROUP_TRIFFID, 1, point( x, y ), point( x, y ), 1, true ); + place_spawns( GROUP_TRIFFID, 1, p, p, 1, true ); } if( rng( 0, 99 ) < chance ) { // Force movement down or to the right - if( x >= 19 ) { - y++; - } else if( y >= 19 ) { - x++; + if( p.x >= 19 ) { + p.y++; + } else if( p.y >= 19 ) { + p.x++; } else { if( one_in( 2 ) ) { - x++; + p.x++; } else { - y++; + p.y++; } } } else { @@ -4966,31 +4746,31 @@ void map::draw_triffid( const mapgendata &dat ) int chance_north = 0; int chance_south = 0; for( int dist = 1; dist <= 5; dist++ ) { - if( ter( point( x - dist, y ) ) == t_root_wall ) { + if( ter( p + point( -dist, 0 ) ) == t_root_wall ) { chance_west++; } - if( ter( point( x + dist, y ) ) == t_root_wall ) { + if( ter( p + point( dist, 0 ) ) == t_root_wall ) { chance_east++; } - if( ter( point( x, y - dist ) ) == t_root_wall ) { + if( ter( p + point( 0, -dist ) ) == t_root_wall ) { chance_north++; } - if( ter( point( x, y + dist ) ) == t_root_wall ) { + if( ter( p + point( 0, dist ) ) == t_root_wall ) { chance_south++; } } int roll = rng( 0, chance_west + chance_east + chance_north + chance_south ); - if( roll < chance_west && x > 0 ) { - x--; - } else if( roll < chance_west + chance_east && x < EAST_EDGE ) { - x++; - } else if( roll < chance_west + chance_east + chance_north && y > 0 ) { - y--; - } else if( y < SOUTH_EDGE ) { - y++; + if( roll < chance_west && p.x > 0 ) { + p.x--; + } else if( roll < chance_west + chance_east && p.x < EAST_EDGE ) { + p.x++; + } else if( roll < chance_west + chance_east + chance_north && p.y > 0 ) { + p.y--; + } else if( p.y < SOUTH_EDGE ) { + p.y++; } } // Done with drunken walk - } while( x < 19 || y < 19 ); + } while( p.x < 19 || p.y < 19 ); // NOLINTNEXTLINE(cata-use-named-point-constants) square( this, t_slope_up, point( 1, 1 ), point( 2, 2 ) ); place_spawns( GROUP_TRIFFID_HEART, 1, point( 21, 21 ), point( 21, 21 ), 1, true ); @@ -5970,19 +5750,18 @@ void science_room( map *m, const point &p1, const point &p2, int z, int rotate ) case room_bionics: if( rotate % 2 == 0 ) { - int biox = p1.x + 2; - int bioy = static_cast( ( p1.y + p2.y ) / 2 ); - mapf::formatted_set_simple( m, point( biox - 1, bioy - 1 ), + point bio( p1.x + 2, static_cast( ( p1.y + p2.y ) / 2 ) ); + mapf::formatted_set_simple( m, bio + point_north_west, "---\n" "|c|\n" "-=-\n", mapf::ter_bind( "- | =", t_concrete_wall, t_concrete_wall, t_reinforced_glass ), mapf::furn_bind( "c", f_counter ) ); - m->place_items( item_group_id( "bionics_common" ), 70, point( biox, bioy ), - point( biox, bioy ), false, calendar::start_of_cataclysm ); + m->place_items( item_group_id( "bionics_common" ), 70, bio, + bio, false, calendar::start_of_cataclysm ); - m->furn_set( point( biox, bioy + 2 ), furn_str_id( "f_console" ) ); - computer *tmpcomp = m->add_computer( tripoint( biox, bioy + 2, z ), _( "Bionic access" ), 2 ); + m->furn_set( bio + point( 0, 2 ), furn_str_id( "f_console" ) ); + computer *tmpcomp = m->add_computer( tripoint( bio.x, bio.y + 2, z ), _( "Bionic access" ), 2 ); tmpcomp->add_option( _( "Manifest" ), COMPACT_LIST_BIONICS, 0 ); tmpcomp->add_option( _( "Open Chambers" ), COMPACT_RELEASE_BIONICS, 3 ); tmpcomp->add_failure( COMPFAIL_MANHACKS ); @@ -5990,18 +5769,18 @@ void science_room( map *m, const point &p1, const point &p2, int z, int rotate ) tmpcomp->set_access_denied_msg( _( "ERROR! Access denied! Unauthorized access will be met with lethal force!" ) ); - biox = p2.x - 2; - mapf::formatted_set_simple( m, point( biox - 1, bioy - 1 ), + bio.x = p2.x - 2; + mapf::formatted_set_simple( m, bio + point_north_west, "-=-\n" "|c|\n" "---\n", mapf::ter_bind( "- | =", t_concrete_wall, t_concrete_wall, t_reinforced_glass ), mapf::furn_bind( "c", f_counter ) ); - m->place_items( item_group_id( "bionics_common" ), 70, point( biox, bioy ), - point( biox, bioy ), false, calendar::start_of_cataclysm ); + m->place_items( item_group_id( "bionics_common" ), 70, bio, + bio, false, calendar::start_of_cataclysm ); - m->furn_set( point( biox, bioy - 2 ), furn_str_id( "f_console" ) ); - computer *tmpcomp2 = m->add_computer( tripoint( biox, bioy - 2, z ), _( "Bionic access" ), 2 ); + m->furn_set( bio + point( 0, -2 ), furn_str_id( "f_console" ) ); + computer *tmpcomp2 = m->add_computer( tripoint( bio.x, bio.y - 2, z ), _( "Bionic access" ), 2 ); tmpcomp2->add_option( _( "Manifest" ), COMPACT_LIST_BIONICS, 0 ); tmpcomp2->add_option( _( "Open Chambers" ), COMPACT_RELEASE_BIONICS, 3 ); tmpcomp2->add_failure( COMPFAIL_MANHACKS ); @@ -6126,191 +5905,6 @@ void science_room( map *m, const point &p1, const point &p2, int z, int rotate ) } } -void build_mine_room( room_type type, const point &p1, const point &p2, const mapgendata &dat ) -{ - map *const m = &dat.m; - std::vector possibilities; - point mid( static_cast( ( p1.x + p2.x ) / 2 ), static_cast( ( p1.y + p2.y ) / 2 ) ); - if( p2.x < SEEX ) { - possibilities.push_back( direction::EAST ); - } - if( p1.x > SEEX + 1 ) { - possibilities.push_back( direction::WEST ); - } - if( p1.y > SEEY + 1 ) { - possibilities.push_back( direction::NORTH ); - } - if( p2.y < SEEY ) { - possibilities.push_back( direction::SOUTH ); - } - - if( possibilities.empty() ) { // We're in the middle of the map! - if( mid.x <= SEEX ) { - possibilities.push_back( direction::EAST ); - } else { - possibilities.push_back( direction::WEST ); - } - if( mid.y <= SEEY ) { - possibilities.push_back( direction::SOUTH ); - } else { - possibilities.push_back( direction::NORTH ); - } - } - - const direction door_side = random_entry( possibilities ); - point door_point; - switch( door_side ) { - case direction::NORTH: - door_point.x = mid.x; - door_point.y = p1.y; - break; - case direction::EAST: - door_point.x = p2.x; - door_point.y = mid.y; - break; - case direction::SOUTH: - door_point.x = mid.x; - door_point.y = p2.y; - break; - case direction::WEST: - door_point.x = p1.x; - door_point.y = mid.y; - break; - default: - break; - } - square( m, t_floor, p1, p2 ); - line( m, t_wall, p1, point( p2.x, p1.y ) ); - line( m, t_wall, point( p1.x, p2.y ), p2 ); - line( m, t_wall, p1 + point_south, point( p1.x, p2.y - 1 ) ); - line( m, t_wall, point( p2.x, p1.y + 1 ), p2 + point_north ); - // Main build switch! - switch( type ) { - case room_mine_shaft: { - m->furn_set( p1 + point_south_east, furn_str_id( "f_console" ) ); - line( m, t_wall, point( p2.x - 2, p1.y + 2 ), point( p2.x - 1, p1.y + 2 ) ); - m->ter_set( point( p2.x - 2, p1.y + 1 ), t_elevator ); - m->ter_set( point( p2.x - 1, p1.y + 1 ), t_elevator_control_off ); - computer *tmpcomp = m->add_computer( p1 + tripoint( 1, 1, m->get_abs_sub().z ), - _( "NEPowerOS" ), 2 ); - tmpcomp->add_option( _( "Divert power to elevator" ), COMPACT_ELEVATOR_ON, 0 ); - tmpcomp->add_failure( COMPFAIL_ALARM ); - } - break; - - case room_mine_office: - line_furn( m, f_counter, point( mid.x, p1.y + 2 ), point( mid.x, p2.y - 2 ) ); - line( m, t_window, point( mid.x - 1, p1.y ), point( mid.x + 1, p1.y ) ); - line( m, t_window, point( mid.x - 1, p2.y ), point( mid.x + 1, p2.y ) ); - line( m, t_window, point( p1.x, mid.y - 1 ), point( p1.x, mid.y + 1 ) ); - line( m, t_window, point( p2.x, mid.y - 1 ), point( p2.x, mid.y + 1 ) ); - m->place_items( item_group_id( "office" ), 80, p1 + point_south_east, - p2 + point_north_west, false, calendar::start_of_cataclysm ); - break; - - case room_mine_storage: - m->place_items( item_group_id( "mine_storage" ), 85, p1 + point( 2, 2 ), - p2 + point( -2, -2 ), false, calendar::start_of_cataclysm ); - break; - - case room_mine_fuel: { - int spacing = rng( 2, 4 ); - if( door_side == direction::NORTH || door_side == direction::SOUTH ) { - int y = ( door_side == direction::NORTH ? p1.y + 2 : p2.y - 2 ); - for( int x = p1.x + 1; x <= p2.x - 1; x += spacing ) { - m->place_gas_pump( point( x, y ), rng( 10000, 50000 ) ); - } - } else { - int x = ( door_side == direction::EAST ? p2.x - 2 : p1.x + 2 ); - for( int y = p1.y + 1; y <= p2.y - 1; y += spacing ) { - m->place_gas_pump( point( x, y ), rng( 10000, 50000 ) ); - } - } - } - break; - - case room_mine_housing: - if( door_side == direction::NORTH || door_side == direction::SOUTH ) { - for( int y = p1.y + 2; y <= p2.y - 2; y += 2 ) { - m->ter_set( point( p1.x, y ), t_window ); - m->furn_set( point( p1.x + 1, y ), f_bed ); - m->place_items( item_group_id( "bed" ), 60, point( p1.x + 1, y ), - point( p1.x + 1, y ), false, calendar::start_of_cataclysm ); - m->furn_set( point( p1.x + 2, y ), f_bed ); - m->place_items( item_group_id( "bed" ), 60, point( p1.x + 2, y ), - point( p1.x + 2, y ), false, calendar::start_of_cataclysm ); - m->ter_set( point( p2.x, y ), t_window ); - m->furn_set( point( p2.x - 1, y ), f_bed ); - m->place_items( item_group_id( "bed" ), 60, point( p2.x - 1, y ), - point( p2.x - 1, y ), false, calendar::start_of_cataclysm ); - m->furn_set( point( p2.x - 2, y ), f_bed ); - m->place_items( item_group_id( "bed" ), 60, point( p2.x - 2, y ), - point( p2.x - 2, y ), false, calendar::start_of_cataclysm ); - m->furn_set( point( p1.x + 1, y + 1 ), f_dresser ); - m->place_items( item_group_id( "dresser" ), 78, point( p1.x + 1, y + 1 ), - point( p1.x + 1, y + 1 ), false, calendar::start_of_cataclysm ); - m->furn_set( point( p2.x - 1, y + 1 ), f_dresser ); - m->place_items( item_group_id( "dresser" ), 78, point( p2.x - 1, y + 1 ), - point( p2.x - 1, y + 1 ), false, calendar::start_of_cataclysm ); - } - } else { - for( int x = p1.x + 2; x <= p2.x - 2; x += 2 ) { - m->ter_set( point( x, p1.y ), t_window ); - m->furn_set( point( x, p1.y + 1 ), f_bed ); - m->place_items( item_group_id( "bed" ), 60, point( x, p1.y + 1 ), - point( x, p1.y + 1 ), false, calendar::start_of_cataclysm ); - m->furn_set( point( x, p1.y + 2 ), f_bed ); - m->place_items( item_group_id( "bed" ), 60, point( x, p1.y + 2 ), - point( x, p1.y + 2 ), false, calendar::start_of_cataclysm ); - m->ter_set( point( x, p2.y ), t_window ); - m->furn_set( point( x, p2.y - 1 ), f_bed ); - m->place_items( item_group_id( "bed" ), 60, point( x, p2.y - 1 ), - point( x, p2.y - 1 ), false, calendar::start_of_cataclysm ); - m->furn_set( point( x, p2.y - 2 ), f_bed ); - m->place_items( item_group_id( "bed" ), 60, point( x, p2.y - 2 ), - point( x, p2.y - 2 ), false, calendar::start_of_cataclysm ); - m->furn_set( point( x + 1, p1.y + 1 ), f_dresser ); - m->place_items( item_group_id( "dresser" ), 78, point( x + 1, p1.y + 1 ), - point( x + 1, p1.y + 1 ), false, calendar::start_of_cataclysm ); - m->furn_set( point( x + 1, p2.y - 1 ), f_dresser ); - m->place_items( item_group_id( "dresser" ), 78, point( x + 1, p2.y - 1 ), - point( x + 1, p2.y - 1 ), false, calendar::start_of_cataclysm ); - } - } - m->place_items( item_group_id( "bedroom" ), 65, p1 + point_south_east, - p2 + point_north_west, false, calendar::start_of_cataclysm ); - break; - default: - //Suppress warnings - break; - } - - if( type == room_mine_fuel ) { // Fuel stations are open on one side - switch( door_side ) { - case direction::NORTH: - line( m, t_floor, p1, point( p2.x, p1.y ) ); - break; - case direction::EAST: - line( m, t_floor, point( p2.x, p1.y + 1 ), p2 + point_north ); - break; - case direction::SOUTH: - line( m, t_floor, point( p1.x, p2.y ), p2 ); - break; - case direction::WEST: - line( m, t_floor, p1 + point_south, point( p1.x, p2.y - 1 ) ); - break; - default: - break; - } - } else { - if( type == room_mine_storage ) { // Storage has a locked door - m->ter_set( door_point, t_door_locked ); - } else { - m->ter_set( door_point, t_door_c ); - } - } -} - void map::create_anomaly( const tripoint &cp, artifact_natural_property prop, bool create_rubble ) { // TODO: Z diff --git a/src/mapgen.h b/src/mapgen.h index 4dda960cc54bc..34616459af352 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -130,6 +131,7 @@ struct jmapgen_setmap { struct spawn_data { std::map ammo; + std::vector patrol_points_rel_ms; }; /** @@ -158,6 +160,9 @@ class jmapgen_piece protected: jmapgen_piece() : repeat( 1, 1 ) { } public: + virtual bool is_nop() const { + return false; + } /** Sanity-check this piece */ virtual void check( const std::string &/*context*/ ) const { } /** Place something on the map from mapgendata &dat, at (x,y). */ @@ -225,8 +230,7 @@ class mapgen_palette using placing_map = std::unordered_map>>; - std::unordered_map format_terrain; - std::unordered_map format_furniture; + std::unordered_set keys_with_terrain; placing_map format_placings; template @@ -337,14 +341,10 @@ class mapgen_function_json_base void check_common() const; - void formatted_set_incredibly_simple( map &m, const point &offset ) const; - - bool do_format; bool is_ready; point mapgensize; point m_offset; - std::vector format; std::vector setmap_points; jmapgen_objects objects; @@ -458,11 +458,6 @@ enum room_type { room_bedroom, room_backyard, room_study, - room_mine_shaft, - room_mine_office, - room_mine_storage, - room_mine_fuel, - room_mine_housing, room_split }; diff --git a/src/mapgen_functions.cpp b/src/mapgen_functions.cpp index c9e85d53f6142..a2fc728ac5a8d 100644 --- a/src/mapgen_functions.cpp +++ b/src/mapgen_functions.cpp @@ -759,25 +759,21 @@ void mapgen_road( mapgendata &dat ) if( sidewalks_neswx[( dir + 3 ) % 4 ] || // has_sidewalk west? sidewalks_neswx[( dir + 3 ) % 4 + 4 ] || // has_sidewalk northwest? sidewalks_neswx[ dir ] ) { // has_sidewalk north? - int x1 = 0; - int y1 = 0; - int x2 = 3; - int y2 = SEEY - 1 + dead_end_extension; - coord_rotate_cw( x1, y1, dir ); - coord_rotate_cw( x2, y2, dir ); - square( m, t_sidewalk, point( x1, y1 ), point( x2, y2 ) ); + point p1; + point p2( 3, SEEY - 1 + dead_end_extension ); + coord_rotate_cw( p1.x, p1.y, dir ); + coord_rotate_cw( p2.x, p2.y, dir ); + square( m, t_sidewalk, p1, p2 ); } // sidewalk east of north road, etc if( sidewalks_neswx[( dir + 1 ) % 4 ] || // has_sidewalk east? sidewalks_neswx[ dir + 4 ] || // has_sidewalk northeast? sidewalks_neswx[ dir ] ) { // has_sidewalk north? - int x1 = SEEX * 2 - 5; - int y1 = 0; - int x2 = SEEX * 2 - 1; - int y2 = SEEY - 1 + dead_end_extension; - coord_rotate_cw( x1, y1, dir ); - coord_rotate_cw( x2, y2, dir ); - square( m, t_sidewalk, point( x1, y1 ), point( x2, y2 ) ); + point p12( SEEX * 2 - 5, 0 ); + point p22( SEEX * 2 - 1, SEEY - 1 + dead_end_extension ); + coord_rotate_cw( p12.x, p12.y, dir ); + coord_rotate_cw( p22.x, p22.y, dir ); + square( m, t_sidewalk, p12, p22 ); } } } @@ -792,17 +788,16 @@ void mapgen_road( mapgendata &dat ) // also corner pieces to curve towards diagonal neighbors for( int dir = 0; dir < 4; dir++ ) { if( roads_nesw[dir] ) { - int x1 = 4; - int y1 = 0; - int x2 = SEEX * 2 - 1 - 4; - int y2 = SEEY - 1 + dead_end_extension; - coord_rotate_cw( x1, y1, dir ); - coord_rotate_cw( x2, y2, dir ); - square( m, t_pavement, point( x1, y1 ), point( x2, y2 ) ); + point p13( 4, 0 ); + point p23( SEEX * 2 - 1 - 4, SEEY - 1 + dead_end_extension ); + coord_rotate_cw( p13.x, p13.y, dir ); + coord_rotate_cw( p23.x, p23.y, dir ); + square( m, t_pavement, p13, p23 ); if( curvedir_nesw[dir] != 0 ) { for( int x = 1; x < 4; x++ ) { for( int y = 0; y < x; y++ ) { - int ty = y, tx = ( curvedir_nesw[dir] == -1 ? x : SEEX * 2 - 1 - x ); + int ty = y; + int tx = ( curvedir_nesw[dir] == -1 ? x : SEEX * 2 - 1 - x ); coord_rotate_cw( tx, ty, dir ); m->ter_set( point( tx, ty ), t_pavement ); } @@ -822,10 +817,9 @@ void mapgen_road( mapgendata &dat ) for( int x = SEEX - 1; x <= SEEX; x++ ) { for( int y = 0; y < max_y; y++ ) { if( ( y + ( ( dir + rot ) / 2 % 2 ) ) % 4 ) { - int xn = x; - int yn = y; - coord_rotate_cw( xn, yn, dir ); - m->ter_set( point( xn, yn ), t_pavement_y ); + point n( x, y ); + coord_rotate_cw( n.x, n.y, dir ); + m->ter_set( n, t_pavement_y ); } } } @@ -2650,8 +2644,7 @@ void mapgen_forest( mapgendata &dat ) const int max_factor = get_max_sparseness_adjacency_factor(); // Our margins for blending divide the overmap terrain into nine sections. - static constexpr int margin_x = SEEX * 2 / 3; - static constexpr int margin_y = SEEY * 2 / 3; + static constexpr point margin( SEEX * 2 / 3, SEEY * 2 / 3 ); const auto get_blended_feature = [&no_ter_furn, &max_factor, &factor, &get_feature_for_neighbor, &dat]( const point & p ) { @@ -2687,25 +2680,25 @@ void mapgen_forest( mapgendata &dat ) // --------------- // SOUTH (SEEX * 2, SEEY * 2) - const int west_weight = std::max( margin_x - p.x, 0 ); - const int east_weight = std::max( p.x - ( SEEX * 2 - margin_x ) + 1, 0 ); - const int north_weight = std::max( margin_y - p.y, 0 ); - const int south_weight = std::max( p.y - ( SEEY * 2 - margin_y ) + 1, 0 ); + const int west_weight = std::max( margin.x - p.x, 0 ); + const int east_weight = std::max( p.x - ( SEEX * 2 - margin.x ) + 1, 0 ); + const int north_weight = std::max( margin.y - p.y, 0 ); + const int south_weight = std::max( p.y - ( SEEY * 2 - margin.y ) + 1, 0 ); // We'll build a weighted list of features to pull from at the end. weighted_int_list feature_pool; // W sections - if( p.x < margin_x ) { + if( p.x < margin.x ) { // NW corner - blend N, W, and self - if( p.y < margin_y ) { + if( p.y < margin.y ) { feature_pool.add( no_ter_furn, 3 * max_factor - ( dat.n_fac + dat.w_fac + factor * 2 ) ); feature_pool.add( self_feature, 1 ); feature_pool.add( west_feature, west_weight ); feature_pool.add( north_feature, north_weight ); } // SW corner - blend S, W, and self - else if( p.y > SEEY * 2 - margin_y ) { + else if( p.y > SEEY * 2 - margin.y ) { feature_pool.add( no_ter_furn, 3 * max_factor - ( dat.s_fac + dat.w_fac + factor * 2 ) ); feature_pool.add( self_feature, factor ); feature_pool.add( west_feature, west_weight ); @@ -2719,16 +2712,16 @@ void mapgen_forest( mapgendata &dat ) } } // E sections - else if( p.x > SEEX * 2 - margin_x ) { + else if( p.x > SEEX * 2 - margin.x ) { // NE corner - blend N, E, and self - if( p.y < margin_y ) { + if( p.y < margin.y ) { feature_pool.add( no_ter_furn, 3 * max_factor - ( dat.n_fac + dat.e_fac + factor * 2 ) ); feature_pool.add( self_feature, factor ); feature_pool.add( east_feature, east_weight ); feature_pool.add( north_feature, north_weight ); } // SE corner - blend S, E, and self - else if( p.y > SEEY * 2 - margin_y ) { + else if( p.y > SEEY * 2 - margin.y ) { feature_pool.add( no_ter_furn, 3 * max_factor - ( dat.s_fac + dat.e_fac + factor * 2 ) ); feature_pool.add( self_feature, factor ); feature_pool.add( east_feature, east_weight ); @@ -2744,13 +2737,13 @@ void mapgen_forest( mapgendata &dat ) // Central sections else { // N edge - blend N and self - if( p.y < margin_y ) { + if( p.y < margin.y ) { feature_pool.add( no_ter_furn, 2 * max_factor - ( dat.n_fac + factor * 2 ) ); feature_pool.add( self_feature, factor ); feature_pool.add( north_feature, north_weight ); } // S edge - blend S, and self - else if( p.y > SEEY * 2 - margin_y ) { + else if( p.y > SEEY * 2 - margin.y ) { feature_pool.add( no_ter_furn, 2 * max_factor - ( dat.s_fac + factor * 2 ) ); feature_pool.add( self_feature, factor ); feature_pool.add( south_feature, south_weight ); diff --git a/src/mapgendata.cpp b/src/mapgendata.cpp index af83b99ceffb3..461111506954a 100644 --- a/src/mapgendata.cpp +++ b/src/mapgendata.cpp @@ -25,6 +25,7 @@ mapgendata::mapgendata( oter_id north, oter_id east, oter_id south, oter_id west mapgendata::mapgendata( const tripoint_abs_omt &over, map &m, const float density, const time_point &when, ::mission *const miss ) +// NOLINTNEXTLINE( cata-unsequenced-calls ) : mapgendata( overmap_buffer.ter( over + tripoint_north ), overmap_buffer.ter( over + tripoint_east ), overmap_buffer.ter( over + tripoint_south ), diff --git a/src/material.cpp b/src/material.cpp index 32e8cbf57e267..132731241f61b 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -68,7 +68,7 @@ void material_type::load( const JsonObject &jsobj, const std::string & ) optional( jsobj, was_loaded, "specific_heat_liquid", _specific_heat_liquid ); optional( jsobj, was_loaded, "specific_heat_solid", _specific_heat_solid ); optional( jsobj, was_loaded, "latent_heat", _latent_heat ); - optional( jsobj, was_loaded, "freeze_point", _freeze_point ); + optional( jsobj, was_loaded, "freezing_point", _freeze_point ); assign( jsobj, "salvaged_into", _salvaged_into ); optional( jsobj, was_loaded, "repaired_with", _repaired_with, itype_id::NULL_ID() ); @@ -103,10 +103,6 @@ void material_type::load( const JsonObject &jsobj, const std::string & ) optional( jsobj, was_loaded, "fuel_data", fuel ); jsobj.read( "burn_products", _burn_products, true ); - - optional( jsobj, was_loaded, "compact_accepts", _compact_accepts, - auto_flags_reader() ); - optional( jsobj, was_loaded, "compacts_into", _compacts_into, auto_flags_reader() ); } void material_type::check() const @@ -123,17 +119,6 @@ void material_type::check() const if( !item::type_is_defined( _repaired_with ) ) { debugmsg( "invalid \"repaired_with\" %s for %s.", _repaired_with.c_str(), id.c_str() ); } - for( const material_id &ca : _compact_accepts ) { - if( !ca.is_valid() ) { - debugmsg( "invalid \"compact_accepts\" %s for %s.", ca.c_str(), id.c_str() ); - } - } - for( const itype_id &ci : _compacts_into ) { - if( !item::type_is_defined( ci ) || - !item( ci, calendar::turn_zero ).only_made_of( std::set { id } ) ) { - debugmsg( "invalid \"compacts_into\" %s for %s.", ci.c_str(), id.c_str() ); - } - } } material_id material_type::ident() const @@ -227,7 +212,7 @@ float material_type::latent_heat() const return _latent_heat; } -int material_type::freeze_point() const +float material_type::freeze_point() const { return _freeze_point; } @@ -272,16 +257,6 @@ const mat_burn_products &material_type::burn_products() const return _burn_products; } -const material_id_list &material_type::compact_accepts() const -{ - return _compact_accepts; -} - -const mat_compacts_into &material_type::compacts_into() const -{ - return _compacts_into; -} - void materials::load( const JsonObject &jo, const std::string &src ) { material_data.load( jo, src ); @@ -302,17 +277,6 @@ material_list materials::get_all() return material_data.get_all(); } -material_list materials::get_compactable() -{ - material_list all = get_all(); - material_list compactable; - std::copy_if( all.begin(), all.end(), - std::back_inserter( compactable ), []( const material_type & mt ) { - return !mt.compacts_into().empty(); - } ); - return compactable; -} - std::set materials::get_rotting() { static generic_factory::Version version; diff --git a/src/material.h b/src/material.h index c20e83c02128f..13cb439dbea4f 100644 --- a/src/material.h +++ b/src/material.h @@ -23,7 +23,6 @@ enum class damage_type : int; class JsonObject; using mat_burn_products = std::vector>; -using mat_compacts_into = std::vector; using material_list = std::vector; using material_id_list = std::vector; @@ -75,7 +74,7 @@ class material_type float _specific_heat_liquid = 4.186f; float _specific_heat_solid = 2.108f; float _latent_heat = 334.0f; - int _freeze_point = 32; // Fahrenheit + float _freeze_point = 0; // Celsius bool _edible = false; bool _rotting = false; bool _soft = false; @@ -94,9 +93,6 @@ class material_type //Burn products defined in JSON as "burn_products": [ [ "X", float efficiency ], [ "Y", float efficiency ] ] mat_burn_products _burn_products; - material_id_list _compact_accepts; - mat_compacts_into _compacts_into; - public: material_type(); @@ -127,7 +123,7 @@ class material_type float specific_heat_liquid() const; float specific_heat_solid() const; float latent_heat() const; - int freeze_point() const; + float freeze_point() const; int density() const; bool edible() const; bool rotting() const; @@ -143,8 +139,6 @@ class material_type const mat_burn_data &burn_data( size_t intensity ) const; const mat_burn_products &burn_products() const; - const material_id_list &compact_accepts() const; - const mat_compacts_into &compacts_into() const; }; namespace materials @@ -155,7 +149,6 @@ void check(); void reset(); material_list get_all(); -material_list get_compactable(); std::set get_rotting(); } // namespace materials diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index c0308f2b68647..a54ebc7400933 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -271,7 +271,7 @@ bool melee_actor::call( monster &z ) const z.mod_moves( -move_cost ); - add_msg_debug( "%s attempting to melee_attack %s", z.name(), + add_msg_debug( debugmode::DF_MATTACK, "%s attempting to melee_attack %s", z.name(), target->disp_name() ); const int acc = accuracy >= 0 ? accuracy : z.type->melee_skill; @@ -299,7 +299,7 @@ bool melee_actor::call( monster &z ) const dealt_damage.bp_hit = bp_hit.id(); int damage_total = dealt_damage.total_damage(); - add_msg_debug( "%s's melee_attack did %d damage", z.name(), damage_total ); + add_msg_debug( debugmode::DF_MATTACK, "%s's melee_attack did %d damage", z.name(), damage_total ); if( damage_total > 0 ) { on_damage( z, *target, dealt_damage ); } else { @@ -548,7 +548,7 @@ void gun_actor::shoot( monster &z, Creature &target, const gun_mode_id &mode ) c standard_npc tmp( _( "The " ) + z.name(), z.pos(), {}, 8, fake_str, fake_dex, fake_int, fake_per ); - tmp.worn.push_back( item( "backpack" ) ); + tmp.worn.emplace_back( "backpack" ); tmp.set_fake( true ); tmp.set_attitude( z.friendly ? NPCATT_FOLLOW : NPCATT_KILL ); tmp.recoil = 0; // no need to aim diff --git a/src/melee.cpp b/src/melee.cpp index 6692c0c5c755b..5a2aee40555ce 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -55,6 +55,7 @@ #include "pimpl.h" #include "player.h" #include "point.h" +#include "popup.h" #include "projectile.h" #include "rng.h" #include "sounds.h" @@ -84,7 +85,6 @@ static const skill_id skill_unarmed( "unarmed" ); static const skill_id skill_bashing( "bashing" ); static const skill_id skill_melee( "melee" ); -static const efftype_id effect_badpoison( "badpoison" ); static const efftype_id effect_beartrap( "beartrap" ); static const efftype_id effect_bouldering( "bouldering" ); static const efftype_id effect_contacts( "contacts" ); @@ -97,7 +97,6 @@ static const efftype_id effect_hit_by_player( "hit_by_player" ); static const efftype_id effect_incorporeal( "incorporeal" ); static const efftype_id effect_lightsnare( "lightsnare" ); static const efftype_id effect_narcosis( "narcosis" ); -static const efftype_id effect_poison( "poison" ); static const efftype_id effect_stunned( "stunned" ); static const efftype_id effect_venom_dmg( "venom_dmg" ); static const efftype_id effect_venom_weaken( "venom_weaken" ); @@ -540,9 +539,30 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, add_msg( m_bad, _( "This weapon is too unwieldy to attack with!" ) ); return false; } - int move_cost = attack_speed( *cur_weapon ); + if( is_avatar() && move_cost > 1000 && calendar::turn > melee_warning_turn ) { + const auto &action = query_popup() + .context( "CANCEL_ACTIVITY_OR_IGNORE_QUERY" ) + .message( _( "Attacking with your %1$s will take a long time. " + "Are you sure you want to continue?" ), + cur_weapon->display_name() ) + .option( "YES" ) + .option( "NO" ) + .option( "IGNORE" ) + .query() + .action; + + if( action == "NO" ) { + return false; + } + if( action == "IGNORE" ) { + if( melee_warning_turn == calendar::turn_zero || melee_warning_turn <= calendar::turn ) { + melee_warning_turn = calendar::turn + 50_turns; + } + } + } + const bool hits = hit_spread >= 0; if( monster *m = t.as_monster() ) { @@ -767,7 +787,7 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, const int deft_bonus = !hits && has_trait( trait_DEFT ) ? 50 : 0; mod_stamina( std::min( -50, mod_sta + melee + deft_bonus ) ); - add_msg_debug( "Stamina burn: %d", std::min( -50, mod_sta ) ); + add_msg_debug( debugmode::DF_MELEE, "Stamina burn: %d", std::min( -50, mod_sta ) ); // Weariness handling - 1 / the value, because it returns what % of the normal speed const float weary_mult = exertion_adjusted_move_multiplier( EXTRA_EXERCISE ); mod_moves( -move_cost * ( 1 / weary_mult ) ); @@ -1560,13 +1580,13 @@ static void print_damage_info( const damage_instance &di ) ss += name_by_dt( du.type ) + ":" + std::to_string( amount ) + ","; } - add_msg_debug( "%stotal: %d", ss, total ); + add_msg_debug( debugmode::DF_MELEE, "%stotal: %d", ss, total ); } void Character::perform_technique( const ma_technique &technique, Creature &t, damage_instance &di, int &move_cost ) { - add_msg_debug( "dmg before tec:" ); + add_msg_debug( debugmode::DF_MELEE, "dmg before tec:" ); print_damage_info( di ); for( damage_unit &du : di.damage_units ) { @@ -1580,7 +1600,7 @@ void Character::perform_technique( const ma_technique &technique, Creature &t, d du.res_pen += technique.armor_penetration( *this, du.type ); } - add_msg_debug( "dmg after tec:" ); + add_msg_debug( debugmode::DF_MELEE, "dmg after tec:" ); print_damage_info( di ); move_cost *= technique.move_cost_multiplier( *this ); @@ -1598,26 +1618,25 @@ void Character::perform_technique( const ma_technique &technique, Creature &t, d if( technique.side_switch ) { const tripoint b = t.pos(); - int newx; - int newy; + point new_; if( b.x > posx() ) { - newx = posx() - 1; + new_.x = posx() - 1; } else if( b.x < posx() ) { - newx = posx() + 1; + new_.x = posx() + 1; } else { - newx = b.x; + new_.x = b.x; } if( b.y > posy() ) { - newy = posy() - 1; + new_.y = posy() - 1; } else if( b.y < posy() ) { - newy = posy() + 1; + new_.y = posy() + 1; } else { - newy = b.y; + new_.y = b.y; } - const tripoint &dest = tripoint( newx, newy, b.z ); + const tripoint &dest = tripoint( new_, b.z ); if( g->is_empty( dest ) ) { t.setpos( dest ); } @@ -1630,9 +1649,9 @@ void Character::perform_technique( const ma_technique &technique, Creature &t, d 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 ); - const int kb_offset_y = rng( -technique.knockback_spread, technique.knockback_spread ); - tripoint kb_point( posx() + kb_offset_x, posy() + kb_offset_y, posz() ); + const point kb_offset( rng( -technique.knockback_spread, technique.knockback_spread ), + rng( -technique.knockback_spread, technique.knockback_spread ) ); + tripoint kb_point( posx() + kb_offset.x, posy() + kb_offset.y, posz() ); for( int dist = rng( 1, technique.knockback_dist ); dist > 0; dist-- ) { t.knock_back_from( kb_point ); } @@ -1676,7 +1695,8 @@ void Character::perform_technique( const ma_technique &technique, Creature &t, d } if( technique.disarms && p != nullptr && p->is_armed() ) { - here.add_item_or_charges( p->pos(), p->remove_weapon() ); + item weap = p->remove_weapon(); + here.add_item_or_charges( p->pos(), weap ); if( p->is_player() ) { add_msg_if_npc( _( " disarms you!" ) ); } else { @@ -2151,7 +2171,8 @@ std::vector Character::mutation_attacks( Creature &t ) const // Calculate actor ability value to be compared against mutation attack difficulty and add debug message const int proc_value = get_dex() + unarmed; - add_msg_debug( "%s proc chance: %d in %d", pr.c_str(), proc_value, mut_atk.chance ); + add_msg_debug( debugmode::DF_MELEE, "%s proc chance: %d in %d", pr.c_str(), proc_value, + mut_atk.chance ); // If the mutation attack fails to proc, bail out if( !x_in_y( proc_value, mut_atk.chance ) ) { continue; @@ -2162,7 +2183,7 @@ std::vector Character::mutation_attacks( Creature &t ) const [this]( const trait_id & blocker ) { return has_trait( blocker ); } ) ) { - add_msg_debug( "%s not procing: blocked", pr.c_str() ); + add_msg_debug( debugmode::DF_MELEE, "%s not procing: blocked", pr.c_str() ); continue; } @@ -2171,7 +2192,7 @@ std::vector Character::mutation_attacks( Creature &t ) const [this]( const trait_id & need ) { return has_trait( need ); } ) ) { - add_msg_debug( "%s not procing: unmet req", pr.c_str() ); + add_msg_debug( debugmode::DF_MELEE, "%s not procing: unmet req", pr.c_str() ); continue; } @@ -2200,7 +2221,7 @@ std::vector Character::mutation_attacks( Creature &t ) const if( tmp.damage.total_damage() > 0.0f ) { ret.emplace_back( tmp ); } else { - add_msg_debug( "%s not procing: zero damage", pr.c_str() ); + add_msg_debug( debugmode::DF_MELEE, "%s not procing: zero damage", pr.c_str() ); } } } @@ -2443,7 +2464,8 @@ double Character::weapon_value( const item &weap, int ammo ) const // A small bonus for guns you can also use to hit stuff with (bayonets etc.) const double my_val = more + ( less / 2.0 ); - add_msg_debug( "%s (%ld ammo) sum value: %.1f", weap.type->get_id().str(), ammo, my_val ); + add_msg_debug( debugmode::DF_MELEE, "%s (%ld ammo) sum value: %.1f", weap.type->get_id().str(), + ammo, my_val ); if( is_wielding( weap ) ) { cached_info.emplace( "weapon_value", my_val ); } @@ -2470,7 +2492,7 @@ double Character::melee_value( const item &weap ) const my_value *= 1.5; } - add_msg_debug( "%s as melee: %.1f", weap.type->get_id().str(), my_value ); + add_msg_debug( debugmode::DF_MELEE, "%s as melee: %.1f", weap.type->get_id().str(), my_value ); return std::max( 0.0, my_value ); } diff --git a/src/memorial_logger.cpp b/src/memorial_logger.cpp index 122180f7b8bd7..2475bb41d5c07 100644 --- a/src/memorial_logger.cpp +++ b/src/memorial_logger.cpp @@ -49,6 +49,7 @@ #include "skill.h" #include "stats_tracker.h" #include "translations.h" +#include "trap.h" #include "type_id.h" #include "units.h" @@ -57,36 +58,6 @@ static const efftype_id effect_datura( "datura" ); static const efftype_id effect_drunk( "drunk" ); static const efftype_id effect_jetinjector( "jetinjector" ); -static const trap_str_id tr_bubblewrap( "tr_bubblewrap" ); -static const trap_str_id tr_glass( "tr_glass" ); -static const trap_str_id tr_beartrap( "tr_beartrap" ); -static const trap_str_id tr_nailboard( "tr_nailboard" ); -static const trap_str_id tr_caltrops( "tr_caltrops" ); -static const trap_str_id tr_caltrops_glass( "tr_caltrops_glass" ); -static const trap_str_id tr_tripwire( "tr_tripwire" ); -static const trap_str_id tr_crossbow( "tr_crossbow" ); -static const trap_str_id tr_shotgun_2( "tr_shotgun_2" ); -static const trap_str_id tr_shotgun_1( "tr_shotgun_1" ); -static const trap_str_id tr_blade( "tr_blade" ); -static const trap_str_id tr_landmine( "tr_landmine" ); -static const trap_str_id tr_light_snare( "tr_light_snare" ); -static const trap_str_id tr_heavy_snare( "tr_heavy_snare" ); -static const trap_str_id tr_telepad( "tr_telepad" ); -static const trap_str_id tr_goo( "tr_goo" ); -static const trap_str_id tr_dissector( "tr_dissector" ); -static const trap_str_id tr_sinkhole( "tr_sinkhole" ); -static const trap_str_id tr_pit( "tr_pit" ); -static const trap_str_id tr_spike_pit( "tr_spike_pit" ); -static const trap_str_id tr_lava( "tr_lava" ); -static const trap_str_id tr_portal( "tr_portal" ); -static const trap_str_id tr_ledge( "tr_ledge" ); -static const trap_str_id tr_boobytrap( "tr_boobytrap" ); -static const trap_str_id tr_temple_flood( "tr_temple_flood" ); -static const trap_str_id tr_shadow( "tr_shadow" ); -static const trap_str_id tr_drain( "tr_drain" ); -static const trap_str_id tr_snake( "tr_snake" ); -static const trap_str_id tr_glass_pit( "tr_glass_pit" ); - static const trait_id trait_CANNIBAL( "CANNIBAL" ); static const trait_id trait_PSYCHOPATH( "PSYCHOPATH" ); static const trait_id trait_SAPIOVORE( "SAPIOVORE" ); @@ -159,7 +130,7 @@ void memorial_logger::add( const std::string &male_msg, const oter_type_str_id cur_oter_type = cur_ter->get_type_id(); const std::string &oter_name = cur_ter->get_name(); - log.push_back( { calendar::turn, cur_oter_type, oter_name, msg } ); + log.emplace_back( calendar::turn, cur_oter_type, oter_name, msg ); } /** @@ -169,7 +140,7 @@ void memorial_logger::add( const std::string &male_msg, * In new format the entries are stored as json. * @param fin The stream to read the memorial entries from. */ -void memorial_logger::load( std::istream &fin ) +void memorial_logger::load( std::istream &fin, const std::string &path ) { log.clear(); if( fin.peek() == '|' ) { @@ -184,7 +155,7 @@ void memorial_logger::load( std::istream &fin ) log.emplace_back( entry ); } } else { - JsonIn jsin( fin ); + JsonIn jsin( fin, path ); if( !jsin.read( log ) ) { debugmsg( "Error reading JSON memorial log" ); } @@ -655,87 +626,8 @@ void memorial_logger::notify( const cata::event &e ) character_id ch = e.get( "character" ); if( ch == avatar_id ) { trap_str_id trap = e.get( "trap" ); - if( trap == tr_bubblewrap ) { - add( pgettext( "memorial_male", "Stepped on bubble wrap." ), - pgettext( "memorial_female", "Stepped on bubble wrap." ) ); - } else if( trap == tr_glass ) { - add( pgettext( "memorial_male", "Stepped on glass." ), - pgettext( "memorial_female", "Stepped on glass." ) ); - } else if( trap == tr_beartrap ) { - add( pgettext( "memorial_male", "Caught by a beartrap." ), - pgettext( "memorial_female", "Caught by a beartrap." ) ); - } else if( trap == tr_nailboard ) { - add( pgettext( "memorial_male", "Stepped on a spiked board." ), - pgettext( "memorial_female", "Stepped on a spiked board." ) ); - } else if( trap == tr_caltrops ) { - add( pgettext( "memorial_male", "Stepped on a caltrop." ), - pgettext( "memorial_female", "Stepped on a caltrop." ) ); - } else if( trap == tr_caltrops_glass ) { - add( pgettext( "memorial_male", "Stepped on a glass caltrop." ), - pgettext( "memorial_female", "Stepped on a glass caltrop." ) ); - } else if( trap == tr_tripwire ) { - add( pgettext( "memorial_male", "Tripped on a tripwire." ), - pgettext( "memorial_female", "Tripped on a tripwire." ) ); - } else if( trap == tr_crossbow ) { - add( pgettext( "memorial_male", "Triggered a crossbow trap." ), - pgettext( "memorial_female", "Triggered a crossbow trap." ) ); - } else if( trap == tr_shotgun_1 || trap == tr_shotgun_2 ) { - add( pgettext( "memorial_male", "Triggered a shotgun trap." ), - pgettext( "memorial_female", "Triggered a shotgun trap." ) ); - } else if( trap == tr_blade ) { - add( pgettext( "memorial_male", "Triggered a blade trap." ), - pgettext( "memorial_female", "Triggered a blade trap." ) ); - } else if( trap == tr_light_snare ) { - add( pgettext( "memorial_male", "Triggered a light snare." ), - pgettext( "memorial_female", "Triggered a light snare." ) ); - } else if( trap == tr_heavy_snare ) { - add( pgettext( "memorial_male", "Triggered a heavy snare." ), - pgettext( "memorial_female", "Triggered a heavy snare." ) ); - } else if( trap == tr_landmine ) { - add( pgettext( "memorial_male", "Stepped on a land mine." ), - pgettext( "memorial_female", "Stepped on a land mine." ) ); - } else if( trap == tr_boobytrap ) { - add( pgettext( "memorial_male", "Triggered a booby trap." ), - pgettext( "memorial_female", "Triggered a booby trap." ) ); - } else if( trap == tr_telepad || trap == tr_portal ) { - add( pgettext( "memorial_male", "Triggered a teleport trap." ), - pgettext( "memorial_female", "Triggered a teleport trap." ) ); - } else if( trap == tr_goo ) { - add( pgettext( "memorial_male", "Stepped into thick goo." ), - pgettext( "memorial_female", "Stepped into thick goo." ) ); - } else if( trap == tr_dissector ) { - add( pgettext( "memorial_male", "Stepped into a dissector." ), - pgettext( "memorial_female", "Stepped into a dissector." ) ); - } else if( trap == tr_pit ) { - add( pgettext( "memorial_male", "Fell in a pit." ), - pgettext( "memorial_female", "Fell in a pit." ) ); - } else if( trap == tr_spike_pit ) { - add( pgettext( "memorial_male", "Fell into a spiked pit." ), - pgettext( "memorial_female", "Fell into a spiked pit." ) ); - } else if( trap == tr_glass_pit ) { - add( pgettext( "memorial_male", "Fell into a pit filled with glass shards." ), - pgettext( "memorial_female", "Fell into a pit filled with glass shards." ) ); - } else if( trap == tr_lava ) { - add( pgettext( "memorial_male", "Stepped into lava." ), - pgettext( "memorial_female", "Stepped into lava." ) ); - } else if( trap == tr_sinkhole ) { - add( pgettext( "memorial_male", "Stepped into a sinkhole." ), - pgettext( "memorial_female", "Stepped into a sinkhole." ) ); - } else if( trap == tr_ledge ) { - add( pgettext( "memorial_male", "Fell down a ledge." ), - pgettext( "memorial_female", "Fell down a ledge." ) ); - } else if( trap == tr_temple_flood ) { - add( pgettext( "memorial_male", "Triggered a flood trap." ), - pgettext( "memorial_female", "Triggered a flood trap." ) ); - } else if( trap == tr_shadow ) { - add( pgettext( "memorial_male", "Triggered a shadow trap." ), - pgettext( "memorial_female", "Triggered a shadow trap." ) ); - } else if( trap == tr_drain ) { - add( pgettext( "memorial_male", "Triggered a life-draining trap." ), - pgettext( "memorial_female", "Triggered a life-draining trap." ) ); - } else if( trap == tr_snake ) { - add( pgettext( "memorial_male", "Triggered a shadow snake trap." ), - pgettext( "memorial_female", "Triggered a shadow snake trap." ) ); + if( trap->has_memorial_msg() ) { + add( trap->memorial_msg( true ), trap->memorial_msg( false ) ); } } break; diff --git a/src/memorial_logger.h b/src/memorial_logger.h index c4f426f32d4b4..250ec0a8a7d4f 100644 --- a/src/memorial_logger.h +++ b/src/memorial_logger.h @@ -63,7 +63,7 @@ class memorial_logger : public event_subscriber } // Loads the memorial log from a file - void load( std::istream & ); + void load( std::istream &, const std::string &path ); void save( std::ostream & ) const; // Dumps all memorial events into a single newline-delimited string // (this is the content of the temporary file used to preserve the log diff --git a/src/messages.cpp b/src/messages.cpp index 02869d2b07a49..848dacb0fefe0 100644 --- a/src/messages.cpp +++ b/src/messages.cpp @@ -347,6 +347,18 @@ void Messages::add_msg( const game_message_params ¶ms, std::string msg ) player_messages.add_msg_string( std::move( msg ), params ); } +void Messages::add_msg_debug( debugmode::debug_filter type, std::string msg ) +{ + if( debug_mode && + std::find( + debugmode::enabled_filters.begin(), debugmode::enabled_filters.end(), + type ) == debugmode::enabled_filters.end() ) { + return; + } + + player_messages.add_msg_string( std::move( msg ), m_debug ); +} + void Messages::clear_messages() { player_messages.messages.clear(); @@ -560,7 +572,8 @@ void Messages::dialog::show() .apply( w ); // Range of window lines to print - size_t line_from = 0, line_to; + size_t line_from = 0; + size_t line_to; if( offset < folded_filtered.size() ) { line_to = std::min( max_lines, folded_filtered.size() - offset ); } else { @@ -886,6 +899,11 @@ void add_msg( const game_message_params ¶ms, std::string msg ) Messages::add_msg( params, std::move( msg ) ); } +void add_msg_debug( debugmode::debug_filter type, std::string msg ) +{ + Messages::add_msg_debug( type, std::move( msg ) ); +} + void add_msg_if_player_sees( const tripoint &target, std::string msg ) { if( get_player_view().sees( target ) ) { @@ -915,3 +933,19 @@ void add_msg_if_player_sees( const Creature &target, const game_message_params & Messages::add_msg( params, std::move( msg ) ); } } + +void add_msg_debug_if_player_sees( const tripoint &target, debugmode::debug_filter type, + std::string msg ) +{ + if( get_player_view().sees( target ) ) { + Messages::add_msg_debug( type, std::move( msg ) ); + } +} + +void add_msg_debug_if_player_sees( const Creature &target, debugmode::debug_filter type, + std::string msg ) +{ + if( get_player_view().sees( target ) ) { + Messages::add_msg_debug( type, std::move( msg ) ); + } +} diff --git a/src/messages.h b/src/messages.h index db482b35564c6..6270cb4259885 100644 --- a/src/messages.h +++ b/src/messages.h @@ -28,6 +28,7 @@ namespace Messages std::vector> recent_messages( size_t count ); void add_msg( std::string msg ); void add_msg( const game_message_params ¶ms, std::string msg ); +void add_msg_debug( debugmode::debug_filter type, std::string msg ); void clear_messages(); void deactivate(); size_t size(); @@ -56,12 +57,6 @@ inline void add_msg( const translation &msg, Args &&... args ) return add_msg( string_format( msg, std::forward( args )... ) ); } -// Prevent potentially expensive evaluation of arguments which won't be printed. -#define add_msg_debug( ... )\ - if( debug_mode ) {\ - add_msg( m_debug, __VA_ARGS__ );\ - }; - void add_msg( const game_message_params ¶ms, std::string msg ); template inline void add_msg( const game_message_params ¶ms, const std::string &msg, Args &&... args ) @@ -80,6 +75,23 @@ inline void add_msg( const game_message_params ¶ms, const char *const msg, A return add_msg( params, string_format( msg, std::forward( args )... ) ); } +void add_msg_debug( debugmode::debug_filter type, std::string msg ); +template +inline void add_msg_debug( debugmode::debug_filter type, const std::string &msg, Args &&... args ) +{ + // expanding for string formatting can be expensive + if( debug_mode ) { + return add_msg_debug( type, string_format( msg, std::forward( args )... ) ); + } +} +template +inline void add_msg_debug( debugmode::debug_filter type, const char *const msg, Args &&... args ) +{ + if( debug_mode ) { + return add_msg_debug( type, string_format( msg, std::forward( args )... ) ); + } +} + void add_msg_if_player_sees( const tripoint &target, std::string msg ); void add_msg_if_player_sees( const Creature &target, std::string msg ); template @@ -162,4 +174,46 @@ inline void add_msg_if_player_sees( const Creature &target, const game_message_p std::forward( args )... ) ); } +void add_msg_debug_if_player_sees( const tripoint &target, debugmode::debug_filter type, + std::string msg ); +void add_msg_debug_if_player_sees( const Creature &target, debugmode::debug_filter type, + std::string msg ); +template +inline void add_msg_debug_if_player_sees( const tripoint &target, debugmode::debug_filter type, + const std::string &msg, Args &&... args ) +{ + // expanding for string formatting can be expensive + if( debug_mode ) { + return add_msg_debug_if_player_sees( target, type, string_format( msg, + std::forward( args )... ) ); + } +} +template +inline void add_msg_debug_if_player_sees( const Creature &target, debugmode::debug_filter type, + const std::string &msg, Args &&... args ) +{ + if( debug_mode ) { + return add_msg_debug_if_player_sees( target, type, string_format( msg, + std::forward( args )... ) ); + } +} +template +inline void add_msg_debug_if_player_sees( const tripoint &target, debugmode::debug_filter type, + const char *const msg, Args &&... args ) +{ + if( debug_mode ) { + return add_msg_debug_if_player_sees( target, type, string_format( msg, + std::forward( args )... ) ); + } +} +template +inline void add_msg_debug_if_player_sees( const Creature &target, debugmode::debug_filter type, + const char *const msg, Args &&... args ) +{ + if( debug_mode ) { + return add_msg_debug_if_player_sees( target, type, string_format( msg, + std::forward( args )... ) ); + } +} + #endif // CATA_SRC_MESSAGES_H diff --git a/src/mission.cpp b/src/mission.cpp index b68858bf33cf3..5df75c4ecfa90 100644 --- a/src/mission.cpp +++ b/src/mission.cpp @@ -358,7 +358,7 @@ void mission::wrap_up() container, itype_id( "null" ), specific_container_required ); for( std::pair &cnt : matches ) { - comps.push_back( item_comp( cnt.first, cnt.second ) ); + comps.emplace_back( cnt.first, cnt.second ); } @@ -367,10 +367,10 @@ void mission::wrap_up() if( remove_container ) { std::vector container_comp = std::vector(); if( !empty_container.is_null() ) { - container_comp.push_back( item_comp( empty_container, type->item_count ) ); + container_comp.emplace_back( empty_container, type->item_count ); player_character.consume_items( container_comp ); } else { - container_comp.push_back( item_comp( container, type->item_count ) ); + container_comp.emplace_back( container, type->item_count ); player_character.consume_items( container_comp ); } } @@ -390,7 +390,7 @@ void mission::wrap_up() } } } else { - comps.push_back( item_comp( type->item_id, item_count ) ); + comps.emplace_back( type->item_id, item_count ); player_character.consume_items( comps ); } } @@ -733,7 +733,7 @@ character_id mission::get_assigned_player_id() const return player_id; } -std::string mission::name() +std::string mission::name() const { if( type == nullptr ) { return "NULL"; @@ -741,7 +741,7 @@ std::string mission::name() return type->tname(); } -mission_type_id mission::mission_id() +mission_type_id mission::mission_id() const { if( type == nullptr ) { return mission_type_id( "NULL" ); diff --git a/src/mission.h b/src/mission.h index 0792b5ad2c0f0..0e37a2d3f5917 100644 --- a/src/mission.h +++ b/src/mission.h @@ -352,8 +352,8 @@ class mission character_id player_id; public: - std::string name(); - mission_type_id mission_id(); + std::string name() const; + mission_type_id mission_id() const; void serialize( JsonOut &json ) const; void deserialize( JsonIn &jsin ); diff --git a/src/mission_companion.cpp b/src/mission_companion.cpp index 030cc39ea8667..9b158d9695340 100644 --- a/src/mission_companion.cpp +++ b/src/mission_companion.cpp @@ -154,10 +154,10 @@ void talk_function::scavenger_patrol( mission_data &mission_key, npc &p ) if( !npc_list.empty() ) { entry = _( "Profit: $25-$500\nDanger: Low\nTime: 10 hour missions\n\nPatrol Roster:\n" ); for( auto &elem : npc_list ) { - entry = entry + " " + elem->name + " [" + std::to_string( to_hours( calendar::turn - - elem->companion_mission_time ) ) + _( " hours]\n" ); + entry += " " + elem->name + " [" + std::to_string( to_hours( calendar::turn - + elem->companion_mission_time ) ) + _( " hours]\n" ); } - entry = entry + _( "\n\nDo you wish to bring your allies back into your party?" ); + entry += _( "\n\nDo you wish to bring your allies back into your party?" ); mission_key.add( "Retrieve Scavenging Patrol", _( "Retrieve Scavenging Patrol" ), entry ); } } @@ -176,10 +176,10 @@ void talk_function::scavenger_raid( mission_data &mission_key, npc &p ) entry = _( "Profit: $200-$1000\nDanger: Medium\nTime: 10 hour missions\n\n" "Raid Roster:\n" ); for( auto &elem : npc_list ) { - entry = entry + " " + elem->name + " [" + std::to_string( to_hours( calendar::turn - - elem->companion_mission_time ) ) + _( " hours]\n" ); + entry += " " + elem->name + " [" + std::to_string( to_hours( calendar::turn - + elem->companion_mission_time ) ) + _( " hours]\n" ); } - entry = entry + _( "\n\nDo you wish to bring your allies back into your party?" ); + entry += _( "\n\nDo you wish to bring your allies back into your party?" ); mission_key.add( "Retrieve Scavenging Raid", _( "Retrieve Scavenging Raid" ), entry ); } } @@ -194,10 +194,10 @@ void talk_function::commune_menial( mission_data &mission_key, npc &p ) "them basic skills and build reputation with the outpost. Don't expect " "much of a reward though.\n\nLabor Roster:\n" ); for( auto &elem : npc_list ) { - entry = entry + " " + elem->name + " [" + std::to_string( to_hours( calendar::turn - - elem->companion_mission_time ) ) + _( " hours]\n" ); + entry += " " + elem->name + " [" + std::to_string( to_hours( calendar::turn - + elem->companion_mission_time ) ) + _( " hours]\n" ); } - entry = entry + _( "\n\nDo you wish to bring your allies back into your party?" ); + entry += _( "\n\nDo you wish to bring your allies back into your party?" ); mission_key.add( "Recover Ally from Menial Labor", _( "Recover Ally from Menial Labor" ), entry ); } @@ -214,10 +214,10 @@ void talk_function::commune_carpentry( mission_data &mission_key, npc &p ) if( !npc_list.empty() ) { entry = _( "Profit: $12/hour\nDanger: Minimal\nTime: 1 hour minimum\n\nLabor Roster:\n" ); for( auto &elem : npc_list ) { - entry = entry + " " + elem->name + " [" + std::to_string( to_hours( calendar::turn - - elem->companion_mission_time ) ) + _( " hours]\n" ); + entry += " " + elem->name + " [" + std::to_string( to_hours( calendar::turn - + elem->companion_mission_time ) ) + _( " hours]\n" ); } - entry = entry + _( "\n\nDo you wish to bring your allies back into your party?" ); + entry += _( "\n\nDo you wish to bring your allies back into your party?" ); mission_key.add( "Recover Ally from Carpentry Work", _( "Recover Ally from Carpentry Work" ), entry ); } @@ -306,10 +306,10 @@ void talk_function::commune_forage( mission_data &mission_key, npc &p ) if( !npc_list.empty() ) { entry = _( "Profit: $10/hour\nDanger: Low\nTime: 4 hour minimum\n\nLabor Roster:\n" ); for( auto &elem : npc_list ) { - entry = entry + " " + elem->name + " [" + std::to_string( to_hours( calendar::turn - - elem->companion_mission_time ) ) + _( " hours]\n" ); + entry += " " + elem->name + " [" + std::to_string( to_hours( calendar::turn - + elem->companion_mission_time ) ) + _( " hours]\n" ); } - entry = entry + _( "\n\nDo you wish to bring your allies back into your party?" ); + entry += _( "\n\nDo you wish to bring your allies back into your party?" ); mission_key.add( "Recover Ally from Foraging", _( "Recover Ally from Foraging" ), entry ); } } @@ -333,13 +333,13 @@ void talk_function::commune_refuge_caravan( mission_data &mission_key, npc &p ) "\nRoster:\n" ); for( auto &elem : npc_list ) { if( elem->companion_mission_time == calendar::before_time_starts ) { - entry = entry + " " + elem->name + _( " [READY]\n" ); + entry += " " + elem->name + _( " [READY]\n" ); npc_list_aux.push_back( elem ); } else if( calendar::turn >= elem->companion_mission_time ) { - entry = entry + " " + elem->name + _( " [COMPLETE]\n" ); + entry += " " + elem->name + _( " [COMPLETE]\n" ); } else { - entry = entry + " " + elem->name + " [" + std::to_string( std::abs( to_hours - ( calendar::turn - elem->companion_mission_time ) ) ) + _( " Hours]\n" ); + entry += " " + elem->name + " [" + std::to_string( std::abs( to_hours + ( calendar::turn - elem->companion_mission_time ) ) ) + _( " Hours]\n" ); } } if( !npc_list_aux.empty() ) { @@ -348,7 +348,7 @@ void talk_function::commune_refuge_caravan( mission_data &mission_key, npc &p ) const std::string entry_suffix = _( " [READY]\n" ); for( auto &elem : npc_list_aux ) { if( elem->companion_mission_time == calendar::before_time_starts ) { - entry_aux = entry_aux + " " + elem->name + entry_suffix; + entry_aux += " " + elem->name + entry_suffix; } } entry_aux = entry_aux + _( "\n\n" @@ -357,7 +357,7 @@ void talk_function::commune_refuge_caravan( mission_data &mission_key, npc &p ) mission_key.add( "Begin Commune-Refugee Center Run", _( "Begin Commune-Refugee Center Run" ), entry ); } - entry = entry + _( "\n\nDo you wish to bring your allies back into your party?" ); + entry += _( "\n\nDo you wish to bring your allies back into your party?" ); mission_key.add( "Recover Commune-Refugee Center", _( "Recover Commune-Refugee Center" ), entry ); } @@ -2054,6 +2054,7 @@ npc_ptr talk_function::companion_choose_return( const tripoint_abs_omt &omt_pos, } std::vector npcs; + npcs.reserve( available.size() ); for( auto &elem : available ) { npcs.push_back( ( elem )->name ); } diff --git a/src/mission_ui.cpp b/src/mission_ui.cpp index 908f52e52e3d3..e2729e8dd187e 100644 --- a/src/mission_ui.cpp +++ b/src/mission_ui.cpp @@ -70,7 +70,8 @@ void game::list_missions() }; draw_tabs( w_missions, tabs, tab ); draw_border_below_tabs( w_missions ); - int x1 = 2, x2 = 2; + int x1 = 2; + int x2 = 2; for( const std::pair &t : tabs ) { x2 = x1 + utf8_width( t.second ) + 1; if( t.first == tab ) { diff --git a/src/mission_util.cpp b/src/mission_util.cpp index b3fa1e641feea..2458745988feb 100644 --- a/src/mission_util.cpp +++ b/src/mission_util.cpp @@ -529,7 +529,7 @@ bool mission_type::parse_funcs( const JsonObject &jo, std::functionfind_npc( miss->get_npc_id() ); diff --git a/src/mod_manager.cpp b/src/mod_manager.cpp index 7f3fcbbf21a1a..04586d7e09880 100644 --- a/src/mod_manager.cpp +++ b/src/mod_manager.cpp @@ -54,6 +54,7 @@ std::string MOD_INFORMATION::name() const const std::vector> &get_mod_list_categories() { static const std::vector> mod_list_categories = { + {"total_conversion", to_translation( "TOTAL CONVERSIONS" )}, {"content", to_translation( "CORE CONTENT PACKS" )}, {"items", to_translation( "ITEM ADDITION MODS" )}, {"creatures", to_translation( "CREATURE MODS" )}, @@ -290,7 +291,7 @@ bool mod_manager::copy_mod_contents( const t_mod_list &mods_to_copy, return true; } std::vector search_extensions; - search_extensions.push_back( ".json" ); + search_extensions.emplace_back( ".json" ); DebugLog( D_INFO, DC_ALL ) << "Copying mod contents into directory: " << output_base_path; @@ -341,8 +342,7 @@ bool mod_manager::copy_mod_contents( const t_mod_list &mods_to_copy, // trim file paths from full length down to just /data forward for( auto &input_file : input_files ) { - std::string output_path = input_file; - output_path = cur_mod_dir + output_path.substr( start_index ); + std::string output_path = cur_mod_dir + input_file.substr( start_index ); copy_file( input_file, output_path ); } } @@ -441,7 +441,8 @@ static inline bool compare_mod_by_name_and_category( const MOD_INFORMATION *cons void mod_manager::set_usable_mods() { - std::vector available_cores, available_supplementals; + std::vector available_cores; + std::vector available_supplementals; std::vector ordered_mods; std::vector mods; diff --git a/src/monattack.cpp b/src/monattack.cpp index 107960534e2c1..7b8af4eb154e8 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -985,7 +985,7 @@ bool mattack::resurrect( monster *z ) int raise_score = ( i.damage_level() + 1 ) * mt->hp + i.burnt; lowest_raise_score = std::min( lowest_raise_score, raise_score ); if( raise_score <= raising_level ) { - corpses.push_back( std::make_pair( p, &i ) ); + corpses.emplace_back( p, &i ); } } } @@ -2853,7 +2853,7 @@ bool mattack::stare( monster *z ) if( z->sees( player_character ) ) { //dimensional effects don't take against dimensionally anchored foes. if( player_character.worn_with_flag( flag_DIMENSIONAL_ANCHOR ) || - player_character.has_effect_with_flag( flag_DIMENSIONAL_ANCHOR ) ) { + player_character.has_flag( flag_DIMENSIONAL_ANCHOR ) ) { add_msg( m_warning, _( "You feel a strange reverberation across your body." ) ); return true; } @@ -3474,8 +3474,7 @@ bool mattack::searchlight( monster *z ) max_lamp_count--; } - const int zposx = z->posx(); - const int zposy = z->posy(); + const point zpos( z->posx(), z->posy() ); map &here = get_map(); //this searchlight is not initialized @@ -3493,16 +3492,16 @@ bool mattack::searchlight( monster *z ) 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 ) { + if( dest.x < zpos.x ) { settings.set_var( "SL_PREFER_LEFT", "FALSE" ); } - if( dest.x > zposx ) { + if( dest.x > zpos.x ) { settings.set_var( "SL_PREFER_RIGHT", "FALSE" ); } - if( dest.y < zposy ) { + if( dest.y < zpos.y ) { settings.set_var( "SL_PREFER_UP", "FALSE" ); } - if( dest.y > zposy ) { + if( dest.y > zpos.y ) { settings.set_var( "SL_PREFER_DOWN", "FALSE" ); } } @@ -3520,8 +3519,8 @@ bool mattack::searchlight( monster *z ) bool generator_ok = false; - for( int x = zposx - 24; x < zposx + 24; x++ ) { - for( int y = zposy - 24; y < zposy + 24; y++ ) { + for( int x = zpos.x - 24; x < zpos.x + 24; x++ ) { + for( int y = zpos.y - 24; y < zpos.y + 24; y++ ) { tripoint dest( x, y, z->posz() ); if( here.ter( dest ) == ter_str_id( "t_plut_generator" ) ) { generator_ok = true; @@ -3567,8 +3566,7 @@ bool mattack::searchlight( monster *z ) } } - int x = zposx + settings.get_var( "SL_SPOT_X", 0 ); - int y = zposy + settings.get_var( "SL_SPOT_Y", 0 ); + point p( zpos + point( settings.get_var( "SL_SPOT_X", 0 ), settings.get_var( "SL_SPOT_Y", 0 ) ) ); int shift = 0; for( int i = 0; i < rng( 1, 2 ); i++ ) { @@ -3578,32 +3576,32 @@ bool mattack::searchlight( monster *z ) switch( shift ) { case 0: - y--; + p.y--; break; case 1: - y--; - x++; + p.y--; + p.x++; break; case 2: - x++; + p.x++; break; case 3: - x++; - y++; + p.x++; + p.y++; break; case 4: - y++; + p.y++; break; case 5: - y++; - x--; + p.y++; + p.x--; break; case 6: - x--; + p.x--; break; case 7: - x--; - y--; + p.x--; + p.y--; break; default: @@ -3611,40 +3609,40 @@ bool mattack::searchlight( monster *z ) } } else { - if( x < player_character.posx() ) { - x++; + if( p.x < player_character.posx() ) { + p.x++; } - if( x > player_character.posx() ) { - x--; + if( p.x > player_character.posx() ) { + p.x--; } - if( y < player_character.posy() ) { - y++; + if( p.y < player_character.posy() ) { + p.y++; } - if( y > player_character.posy() ) { - y--; + if( p.y > player_character.posy() ) { + p.y--; } } - if( rl_dist( point( x, y ), point( zposx, zposy ) ) > 50 ) { - if( x > zposx ) { - x--; + if( rl_dist( p, zpos ) > 50 ) { + if( p.x > zpos.x ) { + p.x--; } - if( x < zposx ) { - x++; + if( p.x < zpos.x ) { + p.x++; } - if( y > zposy ) { - y--; + if( p.y > zpos.y ) { + p.y--; } - if( y < zposy ) { - y++; + if( p.y < zpos.y ) { + p.y++; } } } - settings.set_var( "SL_SPOT_X", x - zposx ); - settings.set_var( "SL_SPOT_Y", y - zposy ); + settings.set_var( "SL_SPOT_X", p.x - zpos.x ); + settings.set_var( "SL_SPOT_Y", p.y - zpos.y ); - here.add_field( tripoint( x, y, z->posz() ), field_type_id( "fd_spotlight" ), 1 ); + here.add_field( tripoint( p, z->posz() ), field_type_id( "fd_spotlight" ), 1 ); } return true; @@ -4290,7 +4288,8 @@ bool mattack::absorb_meat( monster *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_debug( "The %1$s now has %2$s out of %3$s hp", z->name(), z->get_hp(), + add_msg_debug( debugmode::DF_MATTACK, "The %1$s now has %2$s out of %3$s hp", z->name(), + z->get_hp(), z->get_hp_max() ); } return true; @@ -5407,7 +5406,7 @@ bool mattack::kamikaze( monster *z ) { if( z->ammo.empty() ) { // We somehow lost our ammo! Toggle this special off so we stop processing - add_msg_debug( "Missing ammo in kamikaze special for %s.", z->name() ); + add_msg_debug( debugmode::DF_MATTACK, "Missing ammo in kamikaze special for %s.", z->name() ); z->disable_special( "KAMIKAZE" ); return true; } @@ -5427,14 +5426,15 @@ bool mattack::kamikaze( monster *z ) const use_function *usage = bomb_type->get_use( "transform" ); if( usage == nullptr ) { // Invalid item usage, Toggle this special off so we stop processing - add_msg_debug( "Invalid bomb transform use in kamikaze special for %s.", z->name() ); + add_msg_debug( debugmode::DF_MATTACK, "Invalid bomb transform use in kamikaze special for %s.", + z->name() ); z->disable_special( "KAMIKAZE" ); return true; } const iuse_transform *actor = dynamic_cast( usage->get_actor_ptr() ); if( actor == nullptr ) { // Invalid bomb item, Toggle this special off so we stop processing - add_msg_debug( "Invalid bomb type in kamikaze special for %s.", z->name() ); + add_msg_debug( debugmode::DF_MATTACK, "Invalid bomb type in kamikaze special for %s.", z->name() ); z->disable_special( "KAMIKAZE" ); return true; } @@ -5459,7 +5459,8 @@ bool mattack::kamikaze( monster *z ) const use_function *use = act_bomb_type->get_use( "explosion" ); if( use == nullptr ) { // Invalid active bomb item usage, Toggle this special off so we stop processing - add_msg_debug( "Invalid active bomb explosion use in kamikaze special for %s.", + add_msg_debug( debugmode::DF_MATTACK, + "Invalid active bomb explosion use in kamikaze special for %s.", z->name() ); z->disable_special( "KAMIKAZE" ); return true; @@ -5467,7 +5468,8 @@ bool mattack::kamikaze( monster *z ) const explosion_iuse *exp_actor = dynamic_cast( use->get_actor_ptr() ); if( exp_actor == nullptr ) { // Invalid active bomb item, Toggle this special off so we stop processing - add_msg_debug( "Invalid active bomb type in kamikaze special for %s.", z->name() ); + add_msg_debug( debugmode::DF_MATTACK, "Invalid active bomb type in kamikaze special for %s.", + z->name() ); z->disable_special( "KAMIKAZE" ); return true; } @@ -5588,7 +5590,7 @@ static int grenade_helper( monster *const z, Creature *const target, const int d // if the player can see it if( get_player_view().sees( *z ) ) { if( data[att].message.empty() ) { - add_msg_debug( "Invalid ammo message in grenadier special." ); + add_msg_debug( debugmode::DF_MATTACK, "Invalid ammo message in grenadier special." ); } else { add_msg( m_bad, data[att].message, z->name() ); } @@ -5599,14 +5601,15 @@ static int grenade_helper( monster *const z, Creature *const target, const int d const use_function *usage = bomb_type->get_use( "place_monster" ); if( usage == nullptr ) { // Invalid bomb item usage, Toggle this special off so we stop processing - add_msg_debug( "Invalid bomb item usage in grenadier special for %s.", z->name() ); + add_msg_debug( debugmode::DF_MATTACK, "Invalid bomb item usage in grenadier special for %s.", + z->name() ); return -1; } const place_monster_iuse *actor = dynamic_cast ( usage->get_actor_ptr() ); if( actor == nullptr ) { // Invalid bomb item, Toggle this special off so we stop processing - add_msg_debug( "Invalid bomb type in grenadier special for %s.", z->name() ); + add_msg_debug( debugmode::DF_MATTACK, "Invalid bomb type in grenadier special for %s.", z->name() ); return -1; } @@ -5827,3 +5830,92 @@ bool mattack::speaker( monster *z ) SNIPPET.random_from_category( "speaker_warning" ).value_or( translation() ) ); return true; } + +bool mattack::dsa_drone_scan( monster *z ) +{ + z->moves -= 100; + constexpr int scan_range = 30; + // Select a target: the avatar or a nearby NPC. Must be visible and within scan range + Character *target = &get_player_character(); + Character &you = get_player_character(); + bool avatar_in_range = z->posz() == target->posz() && z->sees( target->pos() ) && + rl_dist( z->pos(), target->pos() ) <= scan_range; + const std::vector available = g->get_npcs_if( [&]( const npc & guy ) { + // TODO: Get rid of the z-level check when z-level vision gets "better" + return z->posz() == guy.posz() && z->sees( guy.pos() ) && + rl_dist( z->pos(), guy.pos() ) <= scan_range; + } ); + if( !avatar_in_range && available.empty() ) { + return true; + } + if( !available.empty() ) { + if( !avatar_in_range || + ( avatar_in_range && x_in_y( available.size(), available.size() + 1 ) ) ) { + target = random_entry( available ); + } + } + const std::string timestamp_str = "dsa_drone_scan_timestamp"; + const std::string weapons_str = "dsa_drone_scan_weapons_count"; + + // only check for weapons once every 10 seconds by timestap variable + int weapons_count = 0; + bool summon_reinforcements = false; + if( !target->get_value( weapons_str ).empty() ) { + weapons_count = std::stoi( target->get_value( weapons_str ) ); + } + + if( !target->get_value( timestamp_str ).empty() ) { + time_point last_check( std::stoi( target->get_value( timestamp_str ) ) ); + if( ( last_check + 10_seconds ) > calendar::turn ) { + return true; + } + // reset the weapons count if it has been more than an hour + if( ( last_check + 1_hours ) < calendar::turn ) { + weapons_count = 0; + } + } + target->set_value( timestamp_str, string_format( "%d", to_turn( calendar::turn ) ) ); + if( weapons_count < 3 ) { + if( target->weapon.is_gun() ) { + const gun_type_type &guntype = target->weapon.gun_type(); + if( guntype == gun_type_type( "rifle" ) || + guntype == gun_type_type( "shotgun" ) || + guntype == gun_type_type( "launcher" ) ) { + weapons_count += 2; + } else { + weapons_count += 1; + } + } else { + for( const item &worn_item : target->worn ) { + if( worn_item.is_gun() ) { + weapons_count += 1; + break; + } + } + } + summon_reinforcements = weapons_count >= 3; + } + target->set_value( weapons_str, string_format( "%d", weapons_count ) ); + + if( you.sees( z->pos() ) ) { + target->add_msg_player_or_npc( _( "The %s shines its light at you." ), + _( "The %s shines its light at ." ), + z->name() ); + } + std::string warning_signal = _( "a dull beep" ); + if( weapons_count == 1 ) { + warning_signal = _( "an ominous hum" ); + } else if( weapons_count == 2 ) { + warning_signal = _( "a threatening whirr" ); + } else if( weapons_count >= 3 ) { + warning_signal = _( "a high-pitched shriek" ); + } + warning_signal = string_format( _( "%s from the %s." ), warning_signal, z->name() ); + sounds::sound( z->pos(), 15 + 10 * weapons_count, sounds::sound_t::alarm, warning_signal ); + if( summon_reinforcements ) { + get_timed_events().add( timed_event_type::DSA_ALRP_SUMMON, + calendar::turn + rng( 5_turns, 10_turns ), + 0, target->global_sm_location() ); + } + return true; +} diff --git a/src/monattack.h b/src/monattack.h index 3f9e344d9ed80..3f976245600f6 100644 --- a/src/monattack.h +++ b/src/monattack.h @@ -107,6 +107,7 @@ bool grenadier( monster *z ); bool grenadier_elite( monster *z ); bool doot( monster *z ); bool zombie_fuse( monster *z ); +bool dsa_drone_scan( monster *z ); void taze( monster *z, Creature *target ); void rifle( monster *z, Creature *target ); // Automated M4 diff --git a/src/mondeath.cpp b/src/mondeath.cpp index 605ba0349402e..a23e51f0b22d3 100644 --- a/src/mondeath.cpp +++ b/src/mondeath.cpp @@ -720,6 +720,13 @@ void mdeath::smokeburst( monster &z ) get_map().emit_field( z.pos(), emit_id( "emit_smoke_blast" ) ); } +void mdeath::tearburst( 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" ); + get_map().emit_field( z.pos(), emit_id( "emit_tear_gas_blast" ) ); +} + void mdeath::fungalburst( monster &z ) { map &here = get_map(); diff --git a/src/mondeath.h b/src/mondeath.h index 79e289bbac656..a34c3b67088f4 100644 --- a/src/mondeath.h +++ b/src/mondeath.h @@ -66,6 +66,8 @@ void gas( monster &z ); void kill_breathers( monster &z ); // Explode like a huge smoke bomb. void smokeburst( monster &z ); +// Explode like a huge tear gas bomb. +void tearburst( monster &z ); // Explode releasing fungal haze. void fungalburst( monster &z ); // Snicker-snack! diff --git a/src/monexamine.cpp b/src/monexamine.cpp index 90168ad4cde80..29aa3a909ef21 100644 --- a/src/monexamine.cpp +++ b/src/monexamine.cpp @@ -44,8 +44,6 @@ static const quality_id qual_SHEAR( "SHEAR" ); static const efftype_id effect_sheared( "sheared" ); -static const activity_id ACT_PLAY_WITH_PET( "ACT_PLAY_WITH_PET" ); - static const efftype_id effect_controlled( "controlled" ); static const efftype_id effect_harnessed( "harnessed" ); static const efftype_id effect_has_bag( "has_bag" ); @@ -304,10 +302,10 @@ void monexamine::shear_animal( monster &z ) // pin the sheep in place if it isn't already if( !z.has_effect( effect_tied ) ) { z.add_effect( effect_tied, 1_turns, true ); - player_character.activity.str_values.push_back( "temp_tie" ); + player_character.activity.str_values.emplace_back( "temp_tie" ); } - player_character.activity.targets.push_back( item_location( player_character, - player_character.best_quality_item( qual_SHEAR ) ) ); + player_character.activity.targets.emplace_back( player_character, + player_character.best_quality_item( qual_SHEAR ) ); add_msg( _( "You start shearing the %s." ), z.get_name() ); } @@ -747,8 +745,7 @@ void monexamine::play_with( monster &z ) { std::string pet_name = z.get_name(); Character &player_character = get_player_character(); - player_character.assign_activity( ACT_PLAY_WITH_PET, rng( 50, 125 ) * 100 ); - player_character.activity.str_values.push_back( pet_name ); + player_character.assign_activity( player_activity( play_with_pet_activity_actor( pet_name ) ) ); } void monexamine::tie_or_untie( monster &z ) @@ -808,7 +805,7 @@ void monexamine::milk_source( monster &source_mon ) bool temp_tie = !source_mon.has_effect( effect_tied ); if( temp_tie ) { source_mon.add_effect( effect_tied, 1_turns, true ); - str_values.push_back( "temp_tie" ); + str_values.emplace_back( "temp_tie" ); } player_character.assign_activity( player_activity( milk_activity_actor( moves, coords, str_values ) ) ); diff --git a/src/mongroup.cpp b/src/mongroup.cpp index 02c84c8679e93..7c4ffcdaf57e7 100644 --- a/src/mongroup.cpp +++ b/src/mongroup.cpp @@ -25,6 +25,9 @@ MonsterGroupManager::t_string_set MonsterGroupManager::monster_blacklist; MonsterGroupManager::t_string_set MonsterGroupManager::monster_whitelist; MonsterGroupManager::t_string_set MonsterGroupManager::monster_categories_blacklist; MonsterGroupManager::t_string_set MonsterGroupManager::monster_categories_whitelist; +MonsterGroupManager::t_string_set MonsterGroupManager::monster_species_blacklist; +MonsterGroupManager::t_string_set MonsterGroupManager::monster_species_whitelist; + static bool monster_whitelist_is_exclusive = false; /** @relates string_id */ @@ -125,13 +128,13 @@ MonsterGroupResult MonsterGroupManager::GetResultFromGroup( //Collect valid time of day ranges if( elem == "DAY" || elem == "NIGHT" || elem == "DUSK" || elem == "DAWN" ) { if( elem == "DAY" ) { - valid_times_of_day.push_back( std::make_pair( sunrise, sunset ) ); + valid_times_of_day.emplace_back( sunrise, sunset ); } else if( elem == "NIGHT" ) { - valid_times_of_day.push_back( std::make_pair( sunset, sunrise ) ); + valid_times_of_day.emplace_back( sunset, sunrise ); } else if( elem == "DUSK" ) { - valid_times_of_day.push_back( std::make_pair( sunset - 1_hours, sunset + 1_hours ) ); + valid_times_of_day.emplace_back( sunset - 1_hours, sunset + 1_hours ); } else if( elem == "DAWN" ) { - valid_times_of_day.push_back( std::make_pair( sunrise - 1_hours, sunrise + 1_hours ) ); + valid_times_of_day.emplace_back( sunrise - 1_hours, sunrise + 1_hours ); } } @@ -266,6 +269,7 @@ void MonsterGroupManager::LoadMonsterBlacklist( const JsonObject &jo ) { add_array_to_set( monster_blacklist, jo, "monsters" ); add_array_to_set( monster_categories_blacklist, jo, "categories" ); + add_array_to_set( monster_species_blacklist, jo, "species" ); } void MonsterGroupManager::LoadMonsterWhitelist( const JsonObject &jo ) @@ -275,6 +279,8 @@ void MonsterGroupManager::LoadMonsterWhitelist( const JsonObject &jo ) } add_array_to_set( monster_whitelist, jo, "monsters" ); add_array_to_set( monster_categories_whitelist, jo, "categories" ); + add_array_to_set( monster_species_whitelist, jo, "species" ); + } bool MonsterGroupManager::monster_is_blacklisted( const mtype_id &m ) @@ -288,11 +294,21 @@ bool MonsterGroupManager::monster_is_blacklisted( const mtype_id &m ) return false; } } + for( const auto &elem : monster_species_whitelist ) { + if( mt.in_species( species_id( elem ) ) ) { + return false; + } + } for( const auto &elem : monster_categories_blacklist ) { if( mt.categories.count( elem ) > 0 ) { return true; } } + for( const auto &elem : monster_species_blacklist ) { + if( mt.in_species( species_id( elem ) ) ) { + return true; + } + } if( monster_blacklist.count( m.str() ) > 0 ) { return true; } diff --git a/src/mongroup.h b/src/mongroup.h index cd60f6aa344f2..8963df319754f 100644 --- a/src/mongroup.h +++ b/src/mongroup.h @@ -197,6 +197,9 @@ class MonsterGroupManager static t_string_set monster_whitelist; static t_string_set monster_categories_blacklist; static t_string_set monster_categories_whitelist; + static t_string_set monster_species_blacklist; + static t_string_set monster_species_whitelist; + }; #endif // CATA_SRC_MONGROUP_H diff --git a/src/monmove.cpp b/src/monmove.cpp index c8e8c3421e605..1687bc7cb05b3 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -77,7 +77,7 @@ static constexpr int MONSTER_FOLLOW_DIST = 8; bool monster::wander() { - return ( goal == pos() ); + return ( goal == pos() && patrol_route_abs_ms.empty() ); } bool monster::is_immune_field( const field_type_id &fid ) const @@ -326,9 +326,13 @@ void monster::plan() const bool angers_hostile_weak = type->has_anger_trigger( mon_trigger::HOSTILE_WEAK ); const int angers_hostile_near = type->has_anger_trigger( mon_trigger::HOSTILE_CLOSE ) ? 5 : 0; + const int angers_hostile_seen = type->has_anger_trigger( mon_trigger::HOSTILE_SEEN ) ? rng( 0, + 2 ) : 0; const int angers_mating_season = type->has_anger_trigger( mon_trigger::MATING_SEASON ) ? 3 : 0; const int angers_cub_threatened = type->has_anger_trigger( mon_trigger::PLAYER_NEAR_BABY ) ? 8 : 0; const int fears_hostile_near = type->has_fear_trigger( mon_trigger::HOSTILE_CLOSE ) ? 5 : 0; + const int fears_hostile_seen = type->has_fear_trigger( mon_trigger::HOSTILE_SEEN ) ? rng( 0, + 2 ) : 0; map &here = get_map(); std::bitset seen_levels = here.get_inter_level_visibility( pos().z ); @@ -342,6 +346,12 @@ void monster::plan() dist = rate_target( player_character, dist, smart_planning ); fleeing = fleeing || is_fleeing( player_character ); target = &player_character; + if( !fleeing && anger <= 20 ) { + anger += angers_hostile_seen; + } + if( !fleeing ) { + morale -= fears_hostile_seen; + } if( dist <= 5 ) { anger += angers_hostile_near; morale -= fears_hostile_near; @@ -407,7 +417,8 @@ void monster::plan() float rating = rate_target( who, dist, smart_planning ); bool fleeing_from = is_fleeing( who ); - if( rating == dist && ( fleeing || attitude( &who ) == MATT_ATTACK ) ) { + if( rating == dist && ( fleeing || attitude( &who ) == MATT_ATTACK || + attitude( &who ) == MATT_FOLLOW ) ) { ++valid_targets; if( one_in( valid_targets ) ) { target = &who; @@ -443,6 +454,12 @@ void monster::plan() } } } + if( !fleeing && anger <= 20 && valid_targets != 0 ) { + anger += angers_hostile_seen; + } + if( !fleeing && valid_targets != 0 ) { + morale -= fears_hostile_seen; + } } fleeing = fleeing || ( mood == MATT_FLEE ); @@ -486,6 +503,12 @@ void monster::plan() anger += angers_hostile_near; morale -= fears_hostile_near; } + if( !fleeing && anger <= 20 && valid_targets != 0 ) { + anger += angers_hostile_seen; + } + if( !fleeing && valid_targets != 0 ) { + morale -= fears_hostile_seen; + } } } } @@ -596,6 +619,17 @@ void monster::plan() anger += 10 - static_cast( hp_per / 10 ); } } + } else if( !patrol_route_abs_ms.empty() ) { + // If we have a patrol route and no target, find the current step on the route + tripoint next_stop_loc_ms = here.getlocal( patrol_route_abs_ms.at( next_patrol_point ) ); + + // if there is more than one patrol point, advance to the next one if we're almost there + // this handles impassable obstancles but patrollers can still get stuck + if( ( patrol_route_abs_ms.size() > 1 ) && rl_dist( next_stop_loc_ms, pos() ) < 2 ) { + next_patrol_point = ( next_patrol_point + 1 ) % patrol_route_abs_ms.size(); + next_stop_loc_ms = here.getlocal( patrol_route_abs_ms.at( next_patrol_point ) ); + } + set_dest( next_stop_loc_ms ); } else if( friendly > 0 && one_in( 3 ) ) { // Grow restless with no targets friendly--; @@ -818,7 +852,7 @@ void monster::move() } } - if( current_attitude == MATT_IGNORE || + if( ( current_attitude == MATT_IGNORE && patrol_route_abs_ms.empty() ) || ( current_attitude == MATT_FOLLOW && rl_dist( pos(), goal ) <= MONSTER_FOLLOW_DIST ) ) { moves = 0; stumble(); @@ -1367,7 +1401,7 @@ static std::vector get_bashing_zone( const tripoint &bashee, const tri for( const tripoint &p : path ) { std::vector swath = squares_in_direction( previous.xy(), p.xy() ); for( const point &q : swath ) { - zone.push_back( tripoint( q, bashee.z ) ); + zone.emplace_back( q, bashee.z ); } previous = p; @@ -1894,9 +1928,9 @@ void monster::stumble() for( const tripoint &dest : here.points_in_radius( pos(), 1 ) ) { if( dest != pos() ) { if( here.has_flag( TFLAG_RAMP_DOWN, dest ) ) { - valid_stumbles.push_back( tripoint( dest.xy(), dest.z - 1 ) ); + valid_stumbles.emplace_back( dest.xy(), dest.z - 1 ); } else if( here.has_flag( TFLAG_RAMP_UP, dest ) ) { - valid_stumbles.push_back( tripoint( dest.xy(), dest.z + 1 ) ); + valid_stumbles.emplace_back( dest.xy(), dest.z + 1 ); } else { valid_stumbles.push_back( dest ); } diff --git a/src/monster.cpp b/src/monster.cpp index 27fc927522a1a..df92274c6facf 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -109,7 +109,6 @@ static const species_id species_MAMMAL( "MAMMAL" ); static const species_id species_MOLLUSK( "MOLLUSK" ); static const species_id species_NETHER( "NETHER" ); static const species_id species_ROBOT( "ROBOT" ); -static const species_id species_SPIDER( "SPIDER" ); static const species_id species_ZOMBIE( "ZOMBIE" ); static const trait_id trait_ANIMALDISCORD( "ANIMALDISCORD" ); @@ -126,59 +125,6 @@ static const trait_id trait_PHEROMONE_MAMMAL( "PHEROMONE_MAMMAL" ); static const trait_id trait_TERRIFYING( "TERRIFYING" ); static const trait_id trait_THRESH_MYCUS( "THRESH_MYCUS" ); -static const mtype_id mon_ant( "mon_ant" ); -static const mtype_id mon_ant_fungus( "mon_ant_fungus" ); -static const mtype_id mon_ant_queen( "mon_ant_queen" ); -static const mtype_id mon_ant_soldier( "mon_ant_soldier" ); -static const mtype_id mon_beekeeper( "mon_beekeeper" ); -static const mtype_id mon_boomer( "mon_boomer" ); -static const mtype_id mon_boomer_fungus( "mon_boomer_fungus" ); -static const mtype_id mon_boomer_huge( "mon_boomer_huge" ); -static const mtype_id mon_fungaloid( "mon_fungaloid" ); -static const mtype_id mon_skeleton_brute( "mon_skeleton_brute" ); -static const mtype_id mon_skeleton_hulk( "mon_skeleton_hulk" ); -static const mtype_id mon_skeleton_hulk_fungus( "mon_skeleton_hulk_fungus" ); -static const mtype_id mon_spider_fungus( "mon_spider_fungus" ); -static const mtype_id mon_triffid( "mon_triffid" ); -static const mtype_id mon_triffid_queen( "mon_triffid_queen" ); -static const mtype_id mon_triffid_young( "mon_triffid_young" ); -static const mtype_id mon_zombie( "mon_zombie" ); -static const mtype_id mon_zombie_anklebiter( "mon_zombie_anklebiter" ); -static const mtype_id mon_zombie_bio_op( "mon_zombie_bio_op" ); -static const mtype_id mon_zombie_brute( "mon_zombie_brute" ); -static const mtype_id mon_zombie_brute_shocker( "mon_zombie_brute_shocker" ); -static const mtype_id mon_zombie_child( "mon_zombie_child" ); -static const mtype_id mon_zombie_child_fungus( "mon_zombie_child_fungus" ); -static const mtype_id mon_zombie_cop( "mon_zombie_cop" ); -static const mtype_id mon_zombie_creepy( "mon_zombie_creepy" ); -static const mtype_id mon_zombie_electric( "mon_zombie_electric" ); -static const mtype_id mon_zombie_fat( "mon_zombie_fat" ); -static const mtype_id mon_zombie_fireman( "mon_zombie_fireman" ); -static const mtype_id mon_zombie_fungus( "mon_zombie_fungus" ); -static const mtype_id mon_zombie_gasbag( "mon_zombie_gasbag" ); -static const mtype_id mon_zombie_gasbag_fungus( "mon_zombie_gasbag_fungus" ); -static const mtype_id mon_zombie_grabber( "mon_zombie_grabber" ); -static const mtype_id mon_zombie_hazmat( "mon_zombie_hazmat" ); -static const mtype_id mon_zombie_hulk( "mon_zombie_hulk" ); -static const mtype_id mon_zombie_hunter( "mon_zombie_hunter" ); -static const mtype_id mon_zombie_master( "mon_zombie_master" ); -static const mtype_id mon_zombie_necro( "mon_zombie_necro" ); -static const mtype_id mon_zombie_rot( "mon_zombie_rot" ); -static const mtype_id mon_zombie_scientist( "mon_zombie_scientist" ); -static const mtype_id mon_zombie_shrieker( "mon_zombie_shrieker" ); -static const mtype_id mon_zombie_shriekling( "mon_zombie_shriekling" ); -static const mtype_id mon_zombie_smoker( "mon_zombie_smoker" ); -static const mtype_id mon_zombie_smoker_fungus( "mon_zombie_smoker_fungus" ); -static const mtype_id mon_zombie_snotgobbler( "mon_zombie_snotgobbler" ); -static const mtype_id mon_zombie_soldier( "mon_zombie_soldier" ); -static const mtype_id mon_zombie_spitter( "mon_zombie_spitter" ); -static const mtype_id mon_zombie_sproglodyte( "mon_zombie_sproglodyte" ); -static const mtype_id mon_zombie_survivor( "mon_zombie_survivor" ); -static const mtype_id mon_zombie_swimmer( "mon_zombie_swimmer" ); -static const mtype_id mon_zombie_technician( "mon_zombie_technician" ); -static const mtype_id mon_zombie_tough( "mon_zombie_tough" ); -static const mtype_id mon_zombie_waif( "mon_zombie_waif" ); - struct pathfinding_settings; // Limit the number of iterations for next upgrade_time calculations. @@ -269,10 +215,10 @@ monster::monster( const mtype_id &id, const tripoint &p ) : monster( id ) } monster::monster( const monster & ) = default; -monster::monster( monster && ) = default; +monster::monster( monster && ) noexcept( map_is_noexcept ) = default; monster::~monster() = default; monster &monster::operator=( const monster & ) = default; -monster &monster::operator=( monster && ) = default; +monster &monster::operator=( monster && ) noexcept( string_is_noexcept ) = default; void monster::setpos( const tripoint &p ) { @@ -284,7 +230,8 @@ void monster::setpos( const tripoint &p ) g->update_zombie_pos( *this, p ); position = p; if( has_effect( effect_ridden ) && mounted_player && mounted_player->pos() != pos() ) { - add_msg_debug( "Ridden monster %s moved independently and dumped player", get_name() ); + add_msg_debug( debugmode::DF_MONSTER, "Ridden monster %s moved independently and dumped player", + get_name() ); mounted_player->forced_dismount(); } if( wandering ) { @@ -1082,6 +1029,16 @@ void monster::set_goal( const tripoint &p ) goal = p; } +void monster::set_patrol_route( const std::vector &patrol_pts_rel_ms ) +{ + map &here = get_map(); + tripoint base_abs_ms( real_coords( here.getabs( pos().xy() ) ).begin_om_pos(), posz() ); + for( const point &patrol_pt : patrol_pts_rel_ms ) { + patrol_route_abs_ms.push_back( base_abs_ms + patrol_pt ); + } + next_patrol_point = 0; +} + void monster::shift( const point &sm_shift ) { const point ms_shift = sm_to_ms_copy( sm_shift ); @@ -1092,7 +1049,7 @@ void monster::shift( const point &sm_shift ) } } -tripoint monster::move_target() +tripoint monster::move_target() const { return goal; } @@ -1497,7 +1454,7 @@ bool monster::block_hit( Creature *, bodypart_id &, damage_instance & ) void monster::absorb_hit( const bodypart_id &, damage_instance &dam ) { for( auto &elem : dam.damage_units ) { - add_msg_debug( "Dam Type: %s :: Ar Pen: %.1f :: Armor Mult: %.1f", + add_msg_debug( debugmode::DF_MONSTER, "Dam Type: %s :: Ar Pen: %.1f :: Armor Mult: %.1f", name_by_dt( elem.type ), elem.res_pen, elem.res_mult ); elem.amount -= std::min( resistances( *this ).get_effective_resist( elem ) + get_worn_armor_val( elem.type ), elem.amount ); @@ -2666,8 +2623,6 @@ bool monster::make_fungus() if( is_hallucination() ) { return true; } - char polypick = 0; - const mtype_id &tid = type->id; if( type->in_species( species_FUNGUS ) ) { // No friendly-fungalizing ;-) return true; } @@ -2677,73 +2632,15 @@ bool monster::make_fungus() // No fungalizing robots or weird stuff (mi-gos are technically fungi, blobs are goo) return true; } - if( tid == mon_ant || tid == mon_ant_soldier || tid == mon_ant_queen ) { - polypick = 1; - } else if( tid == mon_zombie || tid == mon_zombie_shrieker || tid == mon_zombie_electric || - tid == mon_zombie_spitter || tid == mon_zombie_brute || - tid == mon_zombie_hulk || tid == mon_zombie_soldier || tid == mon_zombie_tough || - tid == mon_zombie_scientist || tid == mon_zombie_hunter || tid == mon_skeleton_brute || - tid == mon_zombie_bio_op || tid == mon_zombie_survivor || tid == mon_zombie_fireman || - tid == mon_zombie_cop || tid == mon_zombie_fat || tid == mon_zombie_rot || - tid == mon_zombie_swimmer || tid == mon_zombie_grabber || tid == mon_zombie_technician || - tid == mon_zombie_brute_shocker ) { - polypick = 2; - } else if( tid == mon_zombie_necro || tid == mon_zombie_master || tid == mon_zombie_fireman || - tid == mon_zombie_hazmat || tid == mon_beekeeper ) { - // Necro and Master have enough Goo to resist conversion. - // Firefighter, hazmat, and scarred/beekeeper have the PPG on. - return true; - } else if( tid == mon_boomer || tid == mon_boomer_huge ) { - polypick = 3; - } else if( tid == mon_triffid || tid == mon_triffid_young || tid == mon_triffid_queen ) { - polypick = 4; - } else if( tid == mon_zombie_anklebiter || tid == mon_zombie_child || tid == mon_zombie_creepy || - tid == mon_zombie_shriekling || tid == mon_zombie_snotgobbler || tid == mon_zombie_sproglodyte || - tid == mon_zombie_waif ) { - polypick = 5; - } else if( tid == mon_skeleton_hulk ) { - polypick = 6; - } else if( tid == mon_zombie_smoker ) { - polypick = 7; - } else if( tid == mon_zombie_gasbag ) { - polypick = 8; - } else if( type->in_species( species_SPIDER ) && get_size() > creature_size::tiny ) { - polypick = 9; + if( type->has_flag( MF_NO_FUNG_DMG ) ) { + return true; // Retrun true when monster is immune to fungal damage. + } + if( type->fungalize_into.is_empty() ) { + return false; } const std::string old_name = name(); - switch( polypick ) { - case 1: - poly( mon_ant_fungus ); - break; - case 2: - // zombies, non-boomer - poly( mon_zombie_fungus ); - break; - case 3: - poly( mon_boomer_fungus ); - break; - case 4: - poly( mon_fungaloid ); - break; - case 5: - poly( mon_zombie_child_fungus ); - break; - case 6: - poly( mon_skeleton_hulk_fungus ); - break; - case 7: - poly( mon_zombie_smoker_fungus ); - break; - case 8: - poly( mon_zombie_gasbag_fungus ); - break; - case 9: - poly( mon_spider_fungus ); - break; - default: - return false; - } + poly( type->fungalize_into ); add_msg_if_player_sees( pos(), m_info, _( "The spores transform %1$s into a %2$s!" ), old_name, name() ); @@ -2824,13 +2721,25 @@ void monster::add_msg_if_npc( const game_message_params ¶ms, const std::stri add_msg_if_player_sees( *this, params, replace_with_npc_name( msg ) ); } +void monster::add_msg_debug_if_npc( debugmode::debug_filter type, const std::string &msg ) const +{ + add_msg_debug_if_player_sees( *this, type, replace_with_npc_name( msg ) ); +} + void monster::add_msg_player_or_npc( const game_message_params ¶ms, const std::string &/*player_msg*/, const std::string &npc_msg ) const { add_msg_if_player_sees( *this, params, replace_with_npc_name( npc_msg ) ); } -units::mass monster::get_carried_weight() +void monster::add_msg_debug_player_or_npc( debugmode::debug_filter type, + const std::string &/*player_msg*/, + const std::string &npc_msg ) const +{ + add_msg_debug_if_player_sees( *this, type, replace_with_npc_name( npc_msg ) ); +} + +units::mass monster::get_carried_weight() const { units::mass total_weight = 0_gram; if( tack_item ) { @@ -2848,7 +2757,7 @@ units::mass monster::get_carried_weight() return total_weight; } -units::volume monster::get_carried_volume() +units::volume monster::get_carried_volume() const { units::volume total_volume = 0_ml; for( const item &it : inv ) { @@ -3030,8 +2939,7 @@ void monster::hear_sound( const tripoint &source, const int vol, const int dist max_error = 1; } - int target_x = source.x + rng( -max_error, max_error ); - int target_y = source.y + rng( -max_error, max_error ); + point target( source.xy() + point( rng( -max_error, max_error ), rng( -max_error, max_error ) ) ); // target_z will require some special check due to soil muffling sounds int wander_turns = volume * ( goodhearing ? 6 : 1 ); @@ -3039,10 +2947,10 @@ void monster::hear_sound( const tripoint &source, const int vol, const int dist if( morale >= 0 && anger >= 10 ) { // TODO: Add a proper check for fleeing attitude // but cache it nicely, because this part is called a lot - wander_to( tripoint( target_x, target_y, source.z ), wander_turns ); + wander_to( tripoint( target, source.z ), wander_turns ); } else if( morale < 0 ) { // Monsters afraid of sound should not go towards sound - wander_to( tripoint( 2 * posx() - target_x, 2 * posy() - target_y, 2 * posz() - source.z ), + wander_to( -target + tripoint( 2 * posx(), 2 * posy(), 2 * posz() - source.z ), wander_turns ); } } @@ -3115,7 +3023,7 @@ void monster::on_load() healed_speed = get_speed_base() - old_speed; } - add_msg_debug( "on_load() by %s, %d turns, healed %d hp, %d speed", + add_msg_debug( debugmode::DF_MONSTER, "on_load() by %s, %d turns, healed %d hp, %d speed", name(), to_turns( dt ), healed, healed_speed ); } diff --git a/src/monster.h b/src/monster.h index f773b6205c4ee..843c3d7f1302d 100644 --- a/src/monster.h +++ b/src/monster.h @@ -16,6 +16,7 @@ #include "calendar.h" #include "character_id.h" #include "color.h" +#include "compatibility.h" #include "creature.h" #include "damage.h" #include "enums.h" @@ -90,10 +91,10 @@ class monster : public Creature explicit monster( const mtype_id &id ); monster( const mtype_id &id, const tripoint &pos ); monster( const monster & ); - monster( monster && ); + monster( monster && ) noexcept( map_is_noexcept ); ~monster() override; monster &operator=( const monster & ); - monster &operator=( monster && ); + monster &operator=( monster && ) noexcept( string_is_noexcept ); bool is_monster() const override { return true; @@ -172,12 +173,13 @@ class monster : public Creature void serialize( JsonOut &json ) const; void deserialize( JsonIn &jsin ); - tripoint move_target(); // Returns point at the end of the monster's current plans + tripoint move_target() const; // Returns point at the end of the monster's current plans Creature *attack_target(); // Returns the creature at the end of plans (if hostile) // Movement void shift( const point &sm_shift ); // Shifts the monster to the appropriate submap void set_goal( const tripoint &p ); + void set_patrol_route( const std::vector &patrol_pts_rel_ms ); // Updates current pos AND our plans bool wander(); // Returns true if we have no plans @@ -414,6 +416,7 @@ class monster : public Creature /** * Makes this monster into a fungus version * Returns false if no such monster exists + * Returns true if monster is immune or if it got fungalized */ bool make_fungus(); void make_friendly(); @@ -443,11 +446,16 @@ class monster : public Creature using Creature::add_msg_if_npc; void add_msg_if_npc( const std::string &msg ) const override; void add_msg_if_npc( const game_message_params ¶ms, const std::string &msg ) const override; + using Creature::add_msg_debug_if_npc; + void add_msg_debug_if_npc( debugmode::debug_filter type, const std::string &msg ) const override; using Creature::add_msg_player_or_npc; void add_msg_player_or_npc( const std::string &player_msg, const std::string &npc_msg ) const override; void add_msg_player_or_npc( const game_message_params ¶ms, const std::string &player_msg, const std::string &npc_msg ) const override; + using Creature::add_msg_debug_player_or_npc; + void add_msg_debug_player_or_npc( debugmode::debug_filter type, const std::string &player_msg, + const std::string &npc_msg ) const override; // TEMP VALUES tripoint wander_pos; // Wander destination - Just try to move in that direction int wandf = 0; // Urge to wander - Increased by sound, decrements each move @@ -460,8 +468,8 @@ class monster : public Creature cata::value_ptr armor_item; // item of armor the monster may be wearing cata::value_ptr storage_item; // storage item for monster carrying items cata::value_ptr battery_item; // item to power mechs - units::mass get_carried_weight(); - units::volume get_carried_volume(); + units::mass get_carried_weight() const; + units::volume get_carried_volume() const; void move_special_item_to_inv( cata::value_ptr &it ); // DEFINING VALUES @@ -567,6 +575,10 @@ class monster : public Creature monster_horde_attraction horde_attraction = MHA_NULL; /** Found path. Note: Not used by monsters that don't pathfind! **/ std::vector path; + /** patrol points for monsters that can pathfind and have a patrol route! **/ + std::vector patrol_route_abs_ms; + int next_patrol_point = -1; + std::bitset effect_cache; cata::optional summon_time_limit = cata::nullopt; int turns_since_target = 0; diff --git a/src/monstergenerator.cpp b/src/monstergenerator.cpp index 18132d4b894a5..92cef866b1c4f 100644 --- a/src/monstergenerator.cpp +++ b/src/monstergenerator.cpp @@ -52,6 +52,7 @@ std::string enum_to_string( mon_trigger data ) case mon_trigger::MEAT: return "MEAT"; case mon_trigger::HOSTILE_WEAK: return "PLAYER_WEAK"; case mon_trigger::HOSTILE_CLOSE: return "PLAYER_CLOSE"; + case mon_trigger::HOSTILE_SEEN: return "HOSTILE_SEEN"; case mon_trigger::HURT: return "HURT"; case mon_trigger::FIRE: return "FIRE"; case mon_trigger::FRIEND_DIED: return "FRIEND_DIED"; @@ -175,6 +176,7 @@ std::string enum_to_string( m_flag data ) case MF_MILKABLE: return "MILKABLE"; case MF_SHEARABLE: return "SHEARABLE"; case MF_NO_BREED: return "NO_BREED"; + case MF_NO_FUNG_DMG: return "NO_FUNG_DMG"; case MF_PET_WONT_FOLLOW: return "PET_WONT_FOLLOW"; case MF_DRIPS_NAPALM: return "DRIPS_NAPALM"; case MF_DRIPS_GASOLINE: return "DRIPS_GASOLINE"; @@ -527,6 +529,8 @@ void MonsterGenerator::init_death() death_map["BROKEN_AMMO"] = &mdeath::broken_ammo; // Explode like a huge smoke bomb. death_map["SMOKEBURST"] = &mdeath::smokeburst; + // Explode like a huge tear gas bomb. + death_map["TEARBURST"] = &mdeath::tearburst; // Explode with a cloud of fungal haze. death_map["FUNGALBURST"] = &mdeath::fungalburst; // Snicker-snack! @@ -649,6 +653,7 @@ void MonsterGenerator::init_attack() add_hardcoded_attack( "GRAB", mattack::grab ); add_hardcoded_attack( "GRAB_DRAG", mattack::grab_drag ); add_hardcoded_attack( "DOOT", mattack::doot ); + add_hardcoded_attack( "DSA_DRONE_SCAN", mattack::dsa_drone_scan ); add_hardcoded_attack( "ZOMBIE_FUSE", mattack::zombie_fuse ); } @@ -788,6 +793,8 @@ void mtype::load( const JsonObject &jo, const std::string &src ) optional( jo, was_loaded, "zombify_into", zombify_into, auto_flags_reader {}, mtype_id() ); + optional( jo, was_loaded, "fungalize_into", fungalize_into, auto_flags_reader {}, + mtype_id() ); // TODO: make this work with `was_loaded` if( jo.has_array( "melee_damage" ) ) { @@ -1220,6 +1227,10 @@ void MonsterGenerator::check_monster_definitions() const debugmsg( "monster %s has unknown zombify_into: %s", mon.id.c_str(), mon.zombify_into.c_str() ); } + if( !mon.fungalize_into.is_empty() && !mon.fungalize_into.is_valid() ) { + debugmsg( "monster %s has unknown fungalize_into: %s", mon.id.c_str(), + mon.fungalize_into.c_str() ); + } if( !mon.picture_id.is_empty() && !mon.picture_id.is_valid() ) { debugmsg( "monster %s has unknown ascii_picture: %s", mon.id.c_str(), mon.picture_id.c_str() ); diff --git a/src/mtype.h b/src/mtype.h index d5e09a9d623d7..96392f9002e52 100644 --- a/src/mtype.h +++ b/src/mtype.h @@ -42,6 +42,7 @@ enum class mon_trigger : int { MEAT, // Meat or a corpse nearby HOSTILE_WEAK, // Hurt hostile player/npc/monster seen HOSTILE_CLOSE, // Hostile creature within a few tiles + HOSTILE_SEEN, // Hostile creature in visual range HURT, // We are hurt FIRE, // Fire nearby FRIEND_DIED, // A monster of the same type died @@ -164,6 +165,7 @@ enum m_flag : int { MF_MILKABLE, // This monster is milkable. MF_SHEARABLE, // This monster is shearable. MF_NO_BREED, // This monster doesn't breed, even though it has breed data + MF_NO_FUNG_DMG, // This monster can't be damaged by fungal spores and can't be fungalized either. MF_PET_WONT_FOLLOW, // This monster won't follow the player automatically when tamed. MF_DRIPS_NAPALM, // This monster occasionally drips napalm on move MF_DRIPS_GASOLINE, // This monster occasionally drips gasoline on move @@ -311,6 +313,7 @@ struct mtype { mtype_id burn_into; mtype_id zombify_into; // mtype_id this monster zombifies into + mtype_id fungalize_into; // mtype_id this monster fungalize into // Monster reproduction variables cata::optional baby_timer; diff --git a/src/mutation.cpp b/src/mutation.cpp index 2b756a407af15..02456df4f4a4f 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -157,27 +157,7 @@ void Character::toggle_trait( const trait_id &trait_ ) } } -void Character::set_mutations( const std::vector &traits ) -{ - for( const trait_id &trait : traits ) { - const auto iter = my_mutations.find( trait ); - if( iter != my_mutations.end() ) { - continue; - } - my_mutations.emplace( trait, trait_data{} ); - cached_mutations.push_back( &trait.obj() ); - mutation_effect( trait, false ); - } - recalc_sight_limits(); - calc_encumbrance(); - - // If the stamina is higher than the max (Languorous), set it back to max - if( get_stamina() > get_stamina_max() ) { - set_stamina( get_stamina_max() ); - } -} - -void Character::set_mutation( const trait_id &trait ) +void Character::set_mutation_unsafe( const trait_id &trait ) { const auto iter = my_mutations.find( trait ); if( iter != my_mutations.end() ) { @@ -186,6 +166,10 @@ void Character::set_mutation( const trait_id &trait ) my_mutations.emplace( trait, trait_data{} ); cached_mutations.push_back( &trait.obj() ); mutation_effect( trait, false ); +} + +void Character::do_mutation_updates() +{ recalc_sight_limits(); calc_encumbrance(); @@ -195,6 +179,20 @@ void Character::set_mutation( const trait_id &trait ) } } +void Character::set_mutations( const std::vector &traits ) +{ + for( const trait_id &trait : traits ) { + set_mutation_unsafe( trait ); + } + do_mutation_updates(); +} + +void Character::set_mutation( const trait_id &trait ) +{ + set_mutation_unsafe( trait ); + do_mutation_updates(); +} + void Character::unset_mutation( const trait_id &trait_ ) { // Take copy of argument because it might be a reference into a container @@ -857,6 +855,10 @@ bool Character::mutation_ok( const trait_id &mutation, bool force_good, bool for return false; } } + + if( bid->mutation_conflicts.count( mutation ) != 0 ) { + return false; + } } const mutation_branch &mdata = mutation.obj(); @@ -1185,6 +1187,14 @@ bool Character::mutate_towards( const trait_id &mut ) return false; } + // Just prevent it when it conflicts with a CBM, for now + // TODO: Consequences? + for( const bionic_id &bid : get_bionics() ) { + if( bid->mutation_conflicts.count( mut ) != 0 ) { + return false; + } + } + for( size_t i = 0; !has_threshreq && i < threshreq.size(); i++ ) { if( has_trait( threshreq[i] ) ) { has_threshreq = true; diff --git a/src/mutation_data.cpp b/src/mutation_data.cpp index bf16334165fda..2600d3de911a1 100644 --- a/src/mutation_data.cpp +++ b/src/mutation_data.cpp @@ -504,7 +504,12 @@ void mutation_branch::load( const JsonObject &jo, const std::string & ) optional( jo, was_loaded, "active_flags", active_flags, flag_reader{} ); optional( jo, was_loaded, "inactive_flags", inactive_flags, flag_reader{} ); optional( jo, was_loaded, "types", types, string_reader{} ); - optional( jo, was_loaded, "enchantments", enchantments ); + + int enchant_num = 0; + for( JsonValue jv : jo.get_array( "enchantments" ) ) { + std::string enchant_name = "INLINE_ENCH_" + raw_name + "_" + std::to_string( enchant_num++ ); + enchantments.push_back( enchantment::load_inline_enchantment( jv, "", enchant_name ) ); + } for( const std::string s : jo.get_array( "no_cbm_on_bp" ) ) { no_cbm_on_bp.emplace( bodypart_str_id( s ) ); diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index ea7c626c3ebb6..d53e725ce3d19 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -69,6 +69,7 @@ static const std::string flag_SECRET( "SECRET" ); static const std::string type_hair_style( "hair_style" ); static const std::string type_skin_tone( "skin_tone" ); static const std::string type_facial_hair( "facial_hair" ); +static const std::string type_eye_color( "eye_color" ); static const flag_id json_flag_no_auto_equip( "no_auto_equip" ); static const flag_id json_flag_auto_wield( "auto_wield" ); @@ -205,7 +206,7 @@ void avatar::randomize( const bool random_scenario, points_left &points, bool pl if( random_scenario ) { std::vector scenarios; for( const auto &scen : scenario::get_all() ) { - if( !scen.has_flag( flag_CHALLENGE ) && + if( !scen.has_flag( flag_CHALLENGE ) && !scen.scen_is_blacklisted() && ( !scen.has_flag( flag_CITY_START ) || cities_enabled ) ) { scenarios.emplace_back( &scen ); } @@ -384,6 +385,7 @@ void avatar::randomize( const bool random_scenario, points_left &points, bool pl randomize_cosmetic_trait( type_hair_style ); randomize_cosmetic_trait( type_skin_tone ); + randomize_cosmetic_trait( type_eye_color ); //arbitrary 50% chance to add beard to male characters if( male && one_in( 2 ) ) { randomize_cosmetic_trait( type_facial_hair ); @@ -1231,7 +1233,11 @@ tab_direction set_traits( avatar &u, points_left &points ) } for( int iCurrentPage = 0; iCurrentPage < 3; iCurrentPage++ ) { - nc_color col_on_act, col_off_act, col_on_pas, col_off_pas, hi_on, hi_off, col_tr; + nc_color col_on_act; + nc_color col_off_act; + nc_color col_on_pas; + nc_color col_off_pas; + nc_color col_tr; switch( iCurrentPage ) { case 0: col_on_act = COL_TR_GOOD_ON_ACT; @@ -1239,8 +1245,6 @@ tab_direction set_traits( avatar &u, points_left &points ) col_on_pas = COL_TR_GOOD_ON_PAS; col_off_pas = COL_TR_GOOD_OFF_PAS; col_tr = COL_TR_GOOD; - hi_on = hilite( col_on_act ); - hi_off = hilite( col_off_act ); break; case 1: col_on_act = COL_TR_BAD_ON_ACT; @@ -1248,8 +1252,6 @@ tab_direction set_traits( avatar &u, points_left &points ) col_on_pas = COL_TR_BAD_ON_PAS; col_off_pas = COL_TR_BAD_OFF_PAS; col_tr = COL_TR_BAD; - hi_on = hilite( col_on_act ); - hi_off = hilite( col_off_act ); break; default: col_on_act = COL_TR_NEUT_ON_ACT; @@ -1257,10 +1259,10 @@ tab_direction set_traits( avatar &u, points_left &points ) col_on_pas = COL_TR_NEUT_ON_PAS; col_off_pas = COL_TR_NEUT_OFF_PAS; col_tr = COL_TR_NEUT; - hi_on = hilite( col_on_act ); - hi_off = hilite( col_off_act ); break; } + nc_color hi_on = hilite( col_on_act ); + nc_color hi_off = hilite( col_off_act ); int &start = iStartPos[iCurrentPage]; int current = iCurrentLine[iCurrentPage]; @@ -1448,6 +1450,7 @@ tab_direction set_traits( avatar &u, points_left &points ) // Grab a list of the names of the bionics that block this trait // So that the player know what is preventing them from taking it std::vector conflict_names; + conflict_names.reserve( cbms_blocking_trait.size() ); for( const bionic_id &conflict : cbms_blocking_trait ) { conflict_names.emplace_back( conflict->name.translated() ); } @@ -2081,7 +2084,7 @@ tab_direction set_skills( avatar &u, points_left &points ) } ); if( elem.first == currentSkill->name() ) { - rec_disp = "\n\n" + colorize( rec_temp, c_brown ) + rec_disp; + rec_disp = "\n\n" + colorize( rec_temp, c_brown ) + std::move( rec_disp ); } else { rec_disp += "\n\n" + colorize( "[" + elem.first + "]\n" + rec_temp, c_light_gray ); } @@ -2507,6 +2510,10 @@ tab_direction set_scenario( avatar &u, points_left &points, wprintz( w_flags, c_light_gray, _( "No starting NPC" ) ); wprintz( w_flags, c_light_gray, ( "\n" ) ); } + if( sorted_scens[cur_id]->has_flag( "BORDERED" ) ) { + wprintz( w_flags, c_light_gray, _( "Starting location is bordered by an immense wall" ) ); + wprintz( w_flags, c_light_gray, ( "\n" ) ); + } } draw_scrollbar( w, cur_id, iContentHeight, scens_length, point( 0, 5 ) ); @@ -2862,10 +2869,10 @@ tab_direction set_description( avatar &you, const bool allow_reroll, std::vector vStatNames; mvwprintz( w_stats, point_zero, COL_HEADER, _( "Stats:" ) ); - vStatNames.push_back( _( "Strength:" ) ); - vStatNames.push_back( _( "Dexterity:" ) ); - vStatNames.push_back( _( "Intelligence:" ) ); - vStatNames.push_back( _( "Perception:" ) ); + vStatNames.emplace_back( _( "Strength:" ) ); + vStatNames.emplace_back( _( "Dexterity:" ) ); + vStatNames.emplace_back( _( "Intelligence:" ) ); + vStatNames.emplace_back( _( "Perception:" ) ); int pos = 0; for( size_t i = 0; i < vStatNames.size(); i++ ) { pos = ( utf8_width( vStatNames[i] ) > pos ? diff --git a/src/npc.cpp b/src/npc.cpp index 15b76458f380a..55884b707aeb3 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -204,8 +204,8 @@ standard_npc::standard_npc( const std::string &name, const tripoint &pos, } } -npc::npc( npc && ) = default; -npc &npc::operator=( npc && ) = default; +npc::npc( npc && ) noexcept( map_is_noexcept ) = default; +npc &npc::operator=( npc && ) noexcept( list_is_noexcept ) = default; static std::map, npc_template> npc_templates; @@ -1356,7 +1356,8 @@ void npc::form_opinion( const player &u ) set_attitude( NPCATT_FLEE_TEMP ); } - add_msg_debug( "%s formed an opinion of u: %s", name, npc_attitude_id( attitude ) ); + add_msg_debug( debugmode::DF_NPC, "%s formed an opinion of u: %s", name, + npc_attitude_id( attitude ) ); } void npc::mutiny() @@ -2149,6 +2150,26 @@ bool npc::is_travelling() const Creature::Attitude npc::attitude_to( const Creature &other ) const { + const auto same_as = []( const Creature * lhs, const Creature * rhs ) { + return &lhs == &rhs; + }; + + for( const weak_ptr_fast &buddy : ai_cache.friends ) { + if( same_as( &other, buddy.lock().get() ) ) { + return Creature::Attitude::FRIENDLY; + } + } + for( const weak_ptr_fast &enemy : ai_cache.hostile_guys ) { + if( same_as( &other, enemy.lock().get() ) ) { + return Creature::Attitude::HOSTILE; + } + } + for( const weak_ptr_fast &neutral : ai_cache.neutral_guys ) { + if( same_as( &other, neutral.lock().get() ) ) { + return Creature::Attitude::NEUTRAL; + } + } + if( other.is_npc() || other.is_player() ) { const player &guy = dynamic_cast( other ); // check faction relationships first @@ -2200,7 +2221,8 @@ Creature::Attitude npc::attitude_to( const Creature &other ) const void npc::npc_dismount() { if( !mounted_creature || !has_effect( effect_riding ) ) { - add_msg_debug( "NPC %s tried to dismount, but they have no mount, or they are not riding", + add_msg_debug( debugmode::DF_NPC, + "NPC %s tried to dismount, but they have no mount, or they are not riding", disp_name() ); return; } @@ -2212,7 +2234,7 @@ void npc::npc_dismount() } } if( !pnt ) { - add_msg_debug( "NPC %s could not find a place to dismount.", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "NPC %s could not find a place to dismount.", disp_name() ); return; } remove_effect( effect_riding ); @@ -2646,6 +2668,11 @@ std::string npc_attitude_id( npc_attitude att ) return iter->second; } +int npc::closest_enemy_to_friendly_distance() const +{ + return ai_cache.closest_enemy_to_friendly_distance(); +} + std::string npc_attitude_name( npc_attitude att ) { switch( att ) { @@ -2718,6 +2745,11 @@ void npc::add_msg_if_npc( const game_message_params ¶ms, const std::string & add_msg( params, replace_with_npc_name( msg ) ); } +void npc::add_msg_debug_if_npc( debugmode::debug_filter type, const std::string &msg ) const +{ + add_msg_debug( type, replace_with_npc_name( msg ) ); +} + void npc::add_msg_player_or_npc( const game_message_params ¶ms, const std::string &/*player_msg*/, const std::string &npc_msg ) const @@ -2727,6 +2759,15 @@ void npc::add_msg_player_or_npc( const game_message_params ¶ms, } } +void npc::add_msg_debug_player_or_npc( debugmode::debug_filter type, + const std::string &/*player_msg*/, + const std::string &npc_msg ) const +{ + if( get_player_view().sees( *this ) ) { + add_msg_debug( type, replace_with_npc_name( npc_msg ) ); + } +} + void npc::add_msg_player_or_say( const std::string &/*player_msg*/, const std::string &npc_speech ) const { @@ -2779,7 +2820,7 @@ void npc::on_load() // TODO: Sleeping, healing etc. last_updated = calendar::turn; time_point cur = calendar::turn - dt; - add_msg_debug( "on_load() by %s, %d turns", name, to_turns( dt ) ); + add_msg_debug( debugmode::DF_NPC, "on_load() by %s, %d turns", name, to_turns( dt ) ); // First update with 30 minute granularity, then 5 minutes, then turns for( ; cur < calendar::turn - 30_minutes; cur += 30_minutes + 1_turns ) { update_body( cur, cur + 30_minutes ); @@ -2821,7 +2862,8 @@ void npc::on_load() if( const monster *const mon = g->critter_at( pos() ) ) { mounted_creature = g->shared_from( *mon ); } else { - add_msg_debug( "NPC is meant to be riding, though the mount is not found when %s is loaded", + add_msg_debug( debugmode::DF_NPC, + "NPC is meant to be riding, though the mount is not found when %s is loaded", disp_name() ); } } @@ -3207,7 +3249,7 @@ void npc::set_attitude( npc_attitude new_attitude ) add_effect( effect_npc_flee_player, 24_hours ); } - add_msg_debug( "%s changes attitude from %s to %s", + add_msg_debug( debugmode::DF_NPC, "%s changes attitude from %s to %s", 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 ); diff --git a/src/npc.h b/src/npc.h index f0a2abdc1e183..d613904b8c431 100644 --- a/src/npc.h +++ b/src/npc.h @@ -177,6 +177,7 @@ class job_data const std::pair &b ) { return a.second > b.second; } ); + ret.reserve( pairs.size() ); for( const std::pair &elem : pairs ) { ret.push_back( elem.first ); } @@ -573,11 +574,17 @@ struct npc_short_term_cache { double my_weapon_value = 0; // Use weak_ptr to avoid circular references between Creatures + // attitude of creatures the npc can see + std::vector> hostile_guys; + std::vector> neutral_guys; std::vector> friends; std::vector dangerous_explosives; std::map threat_map; // Cache of locations the NPC has searched recently in npc::find_item() lru_cache searched_tiles; + // returns the value of the distance between a friendly creature and the closest enemy to that friendly creature. + // returns -1 if not applicable + int closest_enemy_to_friendly_distance() const; }; // DO NOT USE! This is old, use strings as talk topic instead, e.g. "TALK_AGREE_FOLLOW" instead of @@ -755,9 +762,9 @@ class npc : public player npc(); npc( const npc & ) = delete; - npc( npc && ); + npc( npc && ) noexcept( map_is_noexcept ); npc &operator=( const npc & ) = delete; - npc &operator=( npc && ); + npc &operator=( npc && ) noexcept( list_is_noexcept ); ~npc() override; bool is_player() const override { @@ -766,6 +773,12 @@ class npc : public player bool is_npc() const override { return true; } + const npc *as_npc() override { + return this; + } + const npc *as_npc() const override { + return this; + } void load_npc_template( const string_id &ident ); void npc_dismount(); weak_ptr_fast chosen_mount; @@ -1186,15 +1199,23 @@ class npc : public player using player::add_msg_if_npc; void add_msg_if_npc( const std::string &msg ) const override; void add_msg_if_npc( const game_message_params ¶ms, const std::string &msg ) const override; + using player::add_msg_debug_if_npc; + void add_msg_debug_if_npc( debugmode::debug_filter type, const std::string &msg ) const override; using player::add_msg_player_or_npc; void add_msg_player_or_npc( const std::string &player_msg, const std::string &npc_msg ) const override; void add_msg_player_or_npc( const game_message_params ¶ms, const std::string &player_msg, const std::string &npc_msg ) const override; + using player::add_msg_debug_player_or_npc; + void add_msg_debug_player_or_npc( debugmode::debug_filter type, const std::string &player_msg, + const std::string &npc_msg ) const override; using player::add_msg_if_player; void add_msg_if_player( const std::string &/*msg*/ ) const override {} void add_msg_if_player( const game_message_params &/*type*/, const std::string &/*msg*/ ) const override {} + using player::add_msg_debug_if_player; + void add_msg_debug_if_player( debugmode::debug_filter /*type*/, + const std::string &/*msg*/ ) const override {} using player::add_msg_player_or_say; void add_msg_player_or_say( const std::string &player_msg, const std::string &npc_speech ) const override; @@ -1239,6 +1260,9 @@ class npc : public player std::vector miss_ids; cata::optional assigned_camp = cata::nullopt; + // accessors to ai_cache functions + int closest_enemy_to_friendly_distance() const; + private: npc_attitude attitude = NPCATT_NULL; // What we want to do to the player npc_attitude previous_attitude = NPCATT_NULL; diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 1134c0e69c436..2f3aa940d2244 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -371,6 +371,23 @@ static bool too_close( const tripoint &critter_pos, const tripoint &ally_pos, co return rl_dist( critter_pos, ally_pos ) <= def_radius; } +int npc_short_term_cache::closest_enemy_to_friendly_distance() const +{ + int distance = INT_MAX; + for( const weak_ptr_fast &buddy : friends ) { + if( buddy.expired() ) { + continue; + } + for( const weak_ptr_fast &enemy : hostile_guys ) { + if( enemy.expired() ) { + continue; + } + distance = std::min( distance, rl_dist( buddy.lock()->pos(), enemy.lock()->pos() ) ); + } + } + return distance; +} + void npc::assess_danger() { float assessment = 0.0f; @@ -454,7 +471,6 @@ void npc::assess_danger() } // find our Character friends and enemies - std::vector> hostile_guys; const bool clairvoyant = clairvoyance(); for( const npc &guy : g->all_npcs() ) { if( &guy == this ) { @@ -467,12 +483,12 @@ void npc::assess_danger() if( has_faction_relationship( guy, npc_factions::watch_your_back ) ) { ai_cache.friends.emplace_back( g->shared_from( guy ) ); } else if( attitude_to( guy ) != Attitude::NEUTRAL && sees( guy.pos() ) ) { - hostile_guys.emplace_back( g->shared_from( guy ) ); + ai_cache.hostile_guys.emplace_back( g->shared_from( guy ) ); } } if( sees( player_character.pos() ) ) { if( is_enemy() ) { - hostile_guys.emplace_back( g->shared_from( player_character ) ); + ai_cache.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 ) ); } @@ -488,11 +504,14 @@ void npc::assess_danger() continue; } if( att != Attitude::HOSTILE && ( critter.friendly || !is_enemy() ) ) { + ai_cache.neutral_guys.emplace_back( g->shared_from( critter ) ); continue; } if( !sees( critter ) ) { continue; } + + ai_cache.hostile_guys.emplace_back( g->shared_from( critter ) ); float critter_threat = evaluate_enemy( critter ); // warn and consider the odds for distant enemies int dist = rl_dist( pos(), critter.pos() ); @@ -548,7 +567,7 @@ void npc::assess_danger() } } - if( assessment == 0.0 && hostile_guys.empty() ) { + if( assessment == 0.0 && ai_cache.hostile_guys.empty() ) { ai_cache.danger_assessment = assessment; return; } @@ -594,7 +613,7 @@ void npc::assess_danger() return foe_threat; }; - for( const weak_ptr_fast &guy : hostile_guys ) { + for( const weak_ptr_fast &guy : ai_cache.hostile_guys ) { player *foe = dynamic_cast( guy.lock().get() ); if( foe && foe->is_npc() ) { assessment += handle_hostile( *foe, evaluate_enemy( *foe ), translate_marker( "bandit" ), @@ -603,11 +622,10 @@ void npc::assess_danger() } for( const weak_ptr_fast &guy : ai_cache.friends ) { - player *ally = dynamic_cast( guy.lock().get() ); - if( !( ally && ally->is_npc() ) ) { + if( !( guy.lock() && guy.lock()->is_npc() ) ) { continue; } - float guy_threat = evaluate_enemy( *ally ); + float guy_threat = evaluate_enemy( *guy.lock() ); float min_danger = assessment >= NPC_DANGER_VERY_LOW ? NPC_DANGER_VERY_LOW : -10.0f; assessment = std::max( min_danger, assessment - guy_threat * 0.5f ); } @@ -674,7 +692,7 @@ float npc::character_danger( const Character &uc ) const ret *= std::max( 0.5, u.get_speed() / 100.0 ); - add_msg_debug( "%s danger: %1f", u.disp_name(), ret ); + add_msg_debug( debugmode::DF_NPC, "%s danger: %1f", u.disp_name(), ret ); return ret; } @@ -702,6 +720,8 @@ void npc::regen_ai_cache() } float old_assessment = ai_cache.danger_assessment; ai_cache.friends.clear(); + ai_cache.hostile_guys.clear(); + ai_cache.neutral_guys.clear(); ai_cache.target = shared_ptr_fast(); ai_cache.ally = shared_ptr_fast(); ai_cache.can_heal.clear_all(); @@ -760,7 +780,7 @@ void npc::move() static const std::string no_target_str = "none"; const Creature *target = current_target(); const std::string &target_name = target != nullptr ? target->disp_name() : no_target_str; - add_msg_debug( "NPC %s: target = %s, danger = %.1f, range = %d", + add_msg_debug( debugmode::DF_NPC, "NPC %s: target = %s, danger = %.1f, range = %d", name, target_name, ai_cache.danger, weapon.is_gun() ? confident_shoot_range( weapon, recoil_total() ) : weapon.reach_range( *this ) ); @@ -770,7 +790,7 @@ void npc::move() if( is_player_ally() ) { mutiny(); } - add_msg_debug( "NPC %s turning hostile because is guaranteed_hostile()", name ); + add_msg_debug( debugmode::DF_NPC, "NPC %s turning hostile because is guaranteed_hostile()", name ); if( op_of_u.fear > 10 + personality.aggression + personality.bravery ) { set_attitude( NPCATT_FLEE_TEMP ); // We don't want to take u on! } else { @@ -845,12 +865,12 @@ void npc::move() } } if( action == npc_investigate_sound ) { - add_msg_debug( "NPC %s: investigating sound at x(%d) y(%d)", name, + add_msg_debug( debugmode::DF_NPC, "NPC %s: investigating sound at x(%d) y(%d)", name, ai_cache.s_abs_pos.x, ai_cache.s_abs_pos.y ); } } else if( ai_cache.sound_alerts.empty() && ai_cache.guard_pos ) { tripoint return_guard_pos = *ai_cache.guard_pos; - add_msg_debug( "NPC %s: returning to guard spot at x(%d) y(%d)", name, + add_msg_debug( debugmode::DF_NPC, "NPC %s: returning to guard spot at x(%d) y(%d)", name, return_guard_pos.x, return_guard_pos.y ); action = npc_return_to_guard_pos; } else { @@ -981,7 +1001,7 @@ void npc::move() action = method_of_attack(); } - add_msg_debug( "%s chose action %s.", name, npc_action_name( action ) ); + add_msg_debug( debugmode::DF_NPC, "%s chose action %s.", name, npc_action_name( action ) ); execute_action( action ); } @@ -1247,7 +1267,7 @@ void npc::execute_action( npc_action action ) my_spot = priority; } - seats.push_back( std::make_pair( priority, static_cast( vp.part_index() ) ) ); + seats.emplace_back( priority, static_cast( vp.part_index() ) ); } if( my_spot >= 3 ) { @@ -1323,7 +1343,7 @@ void npc::execute_action( npc_action action ) break; case npc_noop: - add_msg_debug( "%s skips turn (noop)", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "%s skips turn (noop)", disp_name() ); return; default: @@ -1331,7 +1351,7 @@ void npc::execute_action( npc_action action ) } if( oldmoves == moves ) { - add_msg_debug( "NPC didn't use its moves. Action %s (%d).", + add_msg_debug( debugmode::DF_NPC, "NPC didn't use its moves. Action %s (%d).", npc_action_name( action ), action ); } } @@ -1386,7 +1406,7 @@ npc_action npc::method_of_attack() std::vector> modes; if( rules.has_flag( ally_rule::use_guns ) || !is_player_ally() ) { for( const auto &e : weapon.gun_all_modes() ) { - modes.push_back( e ); + modes.emplace_back( e ); } modes.erase( std::remove_if( modes.begin(), modes.end(), @@ -1421,7 +1441,7 @@ npc_action npc::method_of_attack() } ); if( emergency() && alt_attack() ) { - add_msg_debug( "%s is trying an alternate attack", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "%s is trying an alternate attack", disp_name() ); return npc_noop; } @@ -1429,13 +1449,13 @@ npc_action npc::method_of_attack() int reach_range = weapon.reach_range( *this ); if( !trigdist ) { if( reach_range > 1 && reach_range >= dist && clear_shot_reach( pos(), tar ) ) { - add_msg_debug( "%s is trying a reach attack", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "%s is trying a reach attack", disp_name() ); return npc_reach_attack; } } else { if( reach_range > 1 && reach_range >= std::round( trig_dist( pos(), tar ) ) && clear_shot_reach( pos(), tar ) ) { - add_msg_debug( "%s is trying a reach attack", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "%s is trying a reach attack", disp_name() ); return npc_reach_attack; } } @@ -1444,25 +1464,25 @@ npc_action npc::method_of_attack() if( !modes.empty() && sees( *critter ) && has_los && confident_gun_mode_range( modes[ 0 ].second, cur_recoil ) >= dist ) { if( cbm_weapon_index > 0 && !weapon.ammo_sufficient() && can_reload_current() ) { - add_msg_debug( "%s is reloading", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "%s is reloading", disp_name() ); return npc_reload; } if( wont_hit_friend( tar, weapon, false ) ) { weapon.gun_set_mode( modes[ 0 ].first ); - add_msg_debug( "%s is trying to shoot someone", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "%s is trying to shoot someone", disp_name() ); return npc_shoot; } else { if( !dont_move_ff ) { - add_msg_debug( "%s is trying to avoid friendly fire", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "%s is trying to avoid friendly fire", disp_name() ); return npc_avoid_friendly_fire; } } } if( dist == 1 && same_z ) { - add_msg_debug( "%s is trying a melee attack", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "%s is trying a melee attack", disp_name() ); return npc_melee; } @@ -1470,12 +1490,12 @@ npc_action npc::method_of_attack() if( cbm_weapon_index < 0 ) { // TODO: Add a time check now that wielding takes a lot of time if( wield_better_weapon() ) { - add_msg_debug( "%s is changing weapons", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "%s is changing weapons", disp_name() ); return npc_noop; } if( !weapon.ammo_sufficient() && can_reload_current() ) { - add_msg_debug( "%s is reloading", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "%s is reloading", disp_name() ); return npc_reload; } } @@ -1483,10 +1503,10 @@ npc_action npc::method_of_attack() // TODO: Needs a check for transparent but non-passable tiles on the way if( !modes.empty() && sees( *critter ) && aim_per_move( weapon, recoil ) > 0 && confident_shoot_range( weapon, get_most_accurate_sight( weapon ) ) >= dist ) { - add_msg_debug( "%s is aiming" ); + add_msg_debug( debugmode::DF_NPC, "%s is aiming" ); return npc_aim; } - add_msg_debug( "%s can't figure out what to do", disp_name() ); + add_msg_debug( debugmode::DF_NPC, "%s can't figure out what to do", disp_name() ); return ( dont_move || !same_z ) ? npc_undecided : npc_melee; } @@ -1995,7 +2015,7 @@ npc_action npc::address_player() npc_action npc::long_term_goal_action() { - add_msg_debug( "long_term_goal_action()" ); + add_msg_debug( debugmode::DF_NPC, "long_term_goal_action()" ); if( mission == NPC_MISSION_SHOPKEEP || mission == NPC_MISSION_SHELTER || ( is_player_ally() && mission != NPC_MISSION_TRAVELLING ) ) { @@ -2063,7 +2083,7 @@ int npc::confident_gun_mode_range( const gun_mode &gun, int at_recoil ) const double max_dispersion = get_weapon_dispersion( *( gun.target ) ).max() + at_recoil; double even_chance_range = range_with_even_chance_of_good_hit( max_dispersion ); double confident_range = even_chance_range * confidence_mult(); - add_msg_debug( "confident_gun (%s<=%.2f) at %.1f", gun.tname(), confident_range, + add_msg_debug( debugmode::DF_NPC, "confident_gun (%s<=%.2f) at %.1f", gun.tname(), confident_range, max_dispersion ); return std::max( confident_range, 1 ); } @@ -2074,7 +2094,8 @@ int npc::confident_throw_range( const item &thrown, Creature *target ) const double even_chance_range = ( target == nullptr ? 0.5 : target->ranged_target_size() ) / average_dispersion; double confident_range = even_chance_range * confidence_mult(); - add_msg_debug( "confident_throw_range == %d", static_cast( confident_range ) ); + add_msg_debug( debugmode::DF_NPC, "confident_throw_range == %d", + static_cast( confident_range ) ); return static_cast( confident_range ); } @@ -2191,10 +2212,10 @@ bool npc::update_path( const tripoint &p, const bool no_bashing, bool force ) if( new_path.empty() ) { if( !ai_cache.sound_alerts.empty() ) { ai_cache.sound_alerts.erase( ai_cache.sound_alerts.begin() ); - add_msg_debug( "failed to path to sound alert %d,%d,%d->%d,%d,%d", + add_msg_debug( debugmode::DF_NPC, "failed to path to sound alert %d,%d,%d->%d,%d,%d", posx(), posy(), posz(), p.x, p.y, p.z ); } - add_msg_debug( "Failed to path %d,%d,%d->%d,%d,%d", + add_msg_debug( debugmode::DF_NPC, "Failed to path %d,%d,%d->%d,%d,%d", posx(), posy(), posz(), p.x, p.y, p.z ); } @@ -2460,7 +2481,7 @@ void npc::move_to_next() } if( path.empty() ) { - add_msg_debug( "npc::move_to_next() called with an empty path or path " + add_msg_debug( debugmode::DF_NPC, "npc::move_to_next() called with an empty path or path " "containing only current position" ); move_pause(); return; @@ -2960,7 +2981,7 @@ void npc::pick_up_item() } if( !rules.has_flag( ally_rule::allow_pick_up ) && is_player_ally() ) { - add_msg_debug( "%s::pick_up_item(); Canceling on player's request", name ); + add_msg_debug( debugmode::DF_NPC, "%s::pick_up_item(); Canceling on player's request", name ); fetching_item = false; moves -= 1; return; @@ -2978,11 +2999,11 @@ void npc::pick_up_item() // Or player who is leading us doesn't want us to pick it up fetching_item = false; move_pause(); - add_msg_debug( "Canceling pickup - no items or new zone" ); + add_msg_debug( debugmode::DF_NPC, "Canceling pickup - no items or new zone" ); return; } - add_msg_debug( "%s::pick_up_item(); [%d, %d, %d] => [%d, %d, %d]", name, + add_msg_debug( debugmode::DF_NPC, "%s::pick_up_item(); [ % d, % d, % d] => [ % d, % d, % d]", name, posx(), posy(), posz(), wanted_item_pos.x, wanted_item_pos.y, wanted_item_pos.z ); if( const cata::optional dest = nearest_passable( wanted_item_pos, pos() ) ) { update_path( *dest ); @@ -2990,13 +3011,13 @@ void npc::pick_up_item() const int dist_to_pickup = rl_dist( pos(), wanted_item_pos ); if( dist_to_pickup > 1 && !path.empty() ) { - add_msg_debug( "Moving; [%d, %d, %d] => [%d, %d, %d]", + add_msg_debug( debugmode::DF_NPC, "Moving; [%d, %d, %d] => [%d, %d, %d]", posx(), posy(), posz(), path[0].x, path[0].y, path[0].z ); move_to_next(); return; } else if( dist_to_pickup > 1 && path.empty() ) { - add_msg_debug( "Can't find path" ); + add_msg_debug( debugmode::DF_NPC, "Can't find path" ); // This can happen, always do something fetching_item = false; move_pause(); @@ -3093,7 +3114,8 @@ void npc::drop_items( const units::mass &drop_weight, const units::volume &drop_ /* Remove this when someone debugs it back to functionality */ return; - add_msg_debug( "%s is dropping items-%3.2f kg, %3.2f L (%d items, wgt %3.2f/%3.2f kg, " + add_msg_debug( debugmode::DF_NPC, + "%s is dropping items-%3.2f kg, %3.2f L (%d items, wgt %3.2f/%3.2f kg, " "vol %3.2f/%3.2f L)", name, units::to_kilogram( drop_weight ), units::to_liter( drop_volume ), inv->size(), units::to_kilogram( weight_carried() ), units::to_kilogram( weight_capacity() ), @@ -3101,7 +3123,9 @@ void npc::drop_items( const units::mass &drop_weight, const units::volume &drop_ units::mass weight_dropped = units::from_gram( 0 ); units::volume volume_dropped = units::from_liter( 0 ); - std::vector rWgt, rVol; // Weight/Volume to value ratios + // Weight/Volume to value ratios + std::vector rWgt; + std::vector rVol; // First fill our ratio vectors, so we know which things to drop first invslice slice = inv->slice(); @@ -3125,7 +3149,7 @@ void npc::drop_items( const units::mass &drop_weight, const units::volume &drop_ } } if( !added_wgt ) { - rWgt.push_back( ratio_index( wgt_ratio, i ) ); + rWgt.emplace_back( wgt_ratio, i ); } for( size_t j = 0; j < rVol.size() && !added_vol; j++ ) { if( vol_ratio > rVol[j].ratio ) { @@ -3134,7 +3158,7 @@ void npc::drop_items( const units::mass &drop_weight, const units::volume &drop_ } } if( !added_vol ) { - rVol.push_back( ratio_index( vol_ratio, i ) ); + rVol.emplace_back( vol_ratio, i ); } } @@ -3406,12 +3430,14 @@ bool npc::wield_better_weapon() // Until then, the NPCs should reload the guns as a last resort if( best == &weapon ) { - add_msg_debug( "Wielded %s is best at %.1f, not switching", best->type->get_id().str(), + add_msg_debug( debugmode::DF_NPC, "Wielded %s is best at %.1f, not switching", + best->type->get_id().str(), best_value ); return false; } - add_msg_debug( "Wielding %s at value %.1f", best->type->get_id().str(), best_value ); + add_msg_debug( debugmode::DF_NPC, "Wielding %s at value %.1f", best->type->get_id().str(), + best_value ); wield( *best ); return true; @@ -3419,7 +3445,7 @@ bool npc::wield_better_weapon() bool npc::scan_new_items() { - add_msg_debug( "%s scanning new items", name ); + add_msg_debug( debugmode::DF_NPC, "%s scanning new items", name ); if( wield_better_weapon() ) { return true; } else { @@ -4192,7 +4218,7 @@ void npc::go_to_omt_destination() } } if( goal == no_goal_point || omt_path.empty() ) { - add_msg_debug( "npc::go_to_destination with no goal" ); + add_msg_debug( debugmode::DF_NPC, "npc::go_to_destination with no goal" ); move_pause(); reach_omt_destination(); return; @@ -4240,7 +4266,7 @@ void npc::go_to_omt_destination() } } path = here.route( pos(), centre_sub, get_pathfinding_settings(), get_path_avoid() ); - add_msg_debug( "%s going %s->%s", name, omt_pos.to_string(), goal.to_string() ); + add_msg_debug( debugmode::DF_NPC, "%s going %s->%s", name, omt_pos.to_string(), goal.to_string() ); if( !path.empty() ) { move_to_next(); @@ -4318,7 +4344,7 @@ std::string npc_action_name( npc_action action ) void print_action( const char *prepend, npc_action action ) { if( action != npc_undecided ) { - add_msg_debug( prepend, npc_action_name( action ) ); + add_msg_debug( debugmode::DF_NPC, prepend, npc_action_name( action ) ); } } diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 886ed573b9319..7a7b671266463 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -80,6 +80,7 @@ static const activity_id ACT_WAIT_NPC( "ACT_WAIT_NPC" ); static const efftype_id effect_narcosis( "narcosis" ); static const efftype_id effect_riding( "riding" ); +static const efftype_id effect_sleep( "sleep" ); static const efftype_id effect_under_operation( "under_operation" ); static const itype_id fuel_type_animal( "animal" ); @@ -631,7 +632,8 @@ void npc::handle_sound( const sounds::sound_t spriority, const std::string &desc const tripoint s_abs_pos = here.getabs( spos ); const tripoint my_abs_pos = here.getabs( pos() ); - add_msg_debug( "%s heard '%s', priority %d at volume %d from %d:%d, my pos %d:%d", + add_msg_debug( debugmode::DF_NPC, + "%s heard '%s', priority %d at volume %d from %d:%d, my pos %d:%d", disp_name(), description, static_cast( spriority ), heard_volume, s_abs_pos.x, s_abs_pos.y, my_abs_pos.x, my_abs_pos.y ); @@ -652,11 +654,11 @@ void npc::handle_sound( const sounds::sound_t spriority, const std::string &desc // but only for bantering purposes, not for investigating. if( spriority < sounds::sound_t::alarm ) { if( player_ally ) { - add_msg_debug( "Allied NPC ignored same faction %s", name ); + add_msg_debug( debugmode::DF_NPC, "Allied NPC ignored same faction %s", name ); return; } if( npc_ally ) { - add_msg_debug( "NPC ignored same faction %s", name ); + add_msg_debug( debugmode::DF_NPC, "NPC ignored same faction %s", name ); return; } } @@ -664,7 +666,7 @@ void npc::handle_sound( const sounds::sound_t spriority, const std::string &desc // and listener is friendly and sound source is combat or alert only. if( spriority < sounds::sound_t::alarm && player_character.sees( spos ) ) { if( is_player_ally() ) { - add_msg_debug( "NPC %s ignored low priority noise that player can see", name ); + add_msg_debug( debugmode::DF_NPC, "NPC %s ignored low priority noise that player can see", name ); return; // discount if sound source is player, or seen by player, // listener is neutral and sound type is worth investigating. @@ -703,7 +705,7 @@ void npc::handle_sound( const sounds::sound_t spriority, const std::string &desc } } if( should_check ) { - add_msg_debug( "%s added noise at pos %d:%d", name, s_abs_pos.x, s_abs_pos.y ); + add_msg_debug( debugmode::DF_NPC, "%s added noise at pos %d:%d", name, s_abs_pos.x, s_abs_pos.y ); dangerous_sound temp_sound; temp_sound.abs_pos = s_abs_pos; temp_sound.volume = heard_volume; @@ -1341,7 +1343,7 @@ void parse_tags( std::string &phrase, const Character &u, const Character &me, void dialogue::add_topic( const std::string &topic_id ) { - topic_stack.push_back( talk_topic( topic_id ) ); + topic_stack.emplace_back( topic_id ); } void dialogue::add_topic( const talk_topic &topic ) @@ -1765,7 +1767,7 @@ void talk_effect_fun_t::set_u_buy_item( const itype_id &item_name, int cost, int // Update structure used by mission descriptions. if( cost <= 0 ) { - likely_rewards.push_back( std::pair( count, item_name ) ); + likely_rewards.emplace_back( count, item_name ); } } @@ -2060,6 +2062,136 @@ void talk_effect_fun_t::set_npc_first_topic( const std::string &chat_topic ) }; } +void talk_effect_fun_t::set_message( const JsonObject &jo, const std::string &member ) +{ + std::string message = jo.get_string( member ); + const bool snippet = jo.get_bool( "snippet", false ); + const bool outdoor_only = jo.get_bool( "outdoor_only", false ); + const bool sound = jo.get_bool( "sound", false ); + const bool popup_msg = jo.get_bool( "popup", false ); + game_message_type type = m_neutral; + std::string type_string = jo.get_string( "type", "neutral" ); + if( type_string == "good" ) { + type = m_good; + } else if( type_string == "neutral" ) { + type = m_neutral; + } else if( type_string == "bad" ) { + type = m_bad; + } else if( type_string == "mixed" ) { + type = m_mixed; + } else if( type_string == "warning" ) { + type = m_warning; + } else if( type_string == "info" ) { + type = m_info; + } else if( type_string == "debug" ) { + type = m_debug; + } else if( type_string == "headshot" ) { + type = m_headshot; + } else if( type_string == "critical" ) { + type = m_critical; + } else if( type_string == "grazing" ) { + type = m_grazing; + } else { + jo.throw_error( "Invalid message type." ); + } + + function = [message, outdoor_only, sound, snippet, type, popup_msg]( const dialogue & d ) { + std::string translated_message; + if( snippet ) { + translated_message = SNIPPET.random_from_category( message ).value_or( translation() ).translated(); + } else { + translated_message = _( message ); + } + Character *target = d.alpha->get_character(); + if( !target ) { + return; + } + if( sound ) { + bool display = false; + map &here = get_map(); + if( !target->has_effect( effect_sleep ) && !target->is_deaf() ) { + if( !outdoor_only || here.get_abs_sub().z >= 0 ) { + display = true; + } else if( one_in( std::max( roll_remainder( 2.0f * here.get_abs_sub().z / + target->mutation_value( "hearing_modifier" ) ), 1 ) ) ) { + display = true; + } + } + if( !display ) { + return; + } + } + if( popup_msg ) { + popup( translated_message, PF_NONE ); + } else { + target->add_msg_if_player( type, translated_message ); + } + + }; +} + + +void talk_effect_fun_t::set_mod_pain( const JsonObject &jo, const std::string &member, + bool is_npc ) +{ + int amount = jo.get_int( member ); + function = [is_npc, amount]( const dialogue & d ) { + d.actor( is_npc )->mod_pain( amount ); + }; +} + +void talk_effect_fun_t::set_add_wet( const JsonObject &jo, const std::string &member, + bool is_npc ) +{ + int amount = jo.get_int( member ); + function = [is_npc, amount]( const dialogue & d ) { + Character *target = d.actor( is_npc )->get_character(); + if( target ) { + wet_character( *target, amount ); + } + }; +} + +void talk_effect_fun_t::set_sound_effect( const JsonObject &jo, const std::string &member ) +{ + std::string variant = jo.get_string( member ); + std::string id = jo.get_string( "id" ); + const bool outdoor_event = jo.get_bool( "outdoor_event", false ); + const int volume = jo.get_int( "volume", -1 ); + function = [variant, id, outdoor_event, volume]( const dialogue & d ) { + map &here = get_map(); + int local_volume = volume; + Character *target = d.alpha->get_character(); + if( target && !target->has_effect( effect_sleep ) && !target->is_deaf() ) { + if( !outdoor_event || here.get_abs_sub().z >= 0 ) { + if( local_volume == -1 ) { + local_volume = 80; + } + sfx::play_variant_sound( id, variant, local_volume, random_direction() ); + } else if( one_in( std::max( roll_remainder( 2.0f * here.get_abs_sub().z / + target->mutation_value( "hearing_modifier" ) ), 1 ) ) ) { + if( local_volume == -1 ) { + local_volume = 80 * target->mutation_value( "hearing_modifier" ); + } + sfx::play_variant_sound( id, variant, local_volume, random_direction() ); + } + } + }; +} + +void talk_effect_fun_t::set_add_power( const JsonObject &jo, const std::string &member, + bool is_npc ) +{ + units::energy amount; + assign( jo, member, amount, false ); + function = [is_npc, amount]( const dialogue & d ) { + Character *target = d.actor( is_npc )->get_character(); + if( target ) { + target->mod_power_level( amount ); + } + }; +} + void talk_effect_t::set_effect_consequence( const talk_effect_fun_t &fun, dialogue_consequence con ) { @@ -2133,9 +2265,9 @@ talk_topic talk_effect_t::apply( dialogue &d ) const return next_topic; } -talk_effect_t::talk_effect_t( const JsonObject &jo ) +talk_effect_t::talk_effect_t( const JsonObject &jo, const std::string &member_name ) { - load_effect( jo ); + load_effect( jo, member_name ); if( jo.has_object( "topic" ) ) { next_topic = load_inline_topic( jo.get_object( "topic" ) ); } else if( jo.has_string( "topic" ) ) { @@ -2297,6 +2429,22 @@ void talk_effect_t::parse_sub_effect( const JsonObject &jo ) } else if( jo.has_string( "npc_first_topic" ) ) { const std::string chat_topic = jo.get_string( "npc_first_topic" ); subeffect_fun.set_npc_first_topic( chat_topic ); + } else if( jo.has_string( "sound_effect" ) ) { + subeffect_fun.set_sound_effect( jo, "sound_effect" ); + } else if( jo.has_string( "message" ) ) { + subeffect_fun.set_message( jo, "message" ); + } else if( jo.has_int( "u_mod_pain" ) ) { + subeffect_fun.set_mod_pain( jo, "u_mod_pain", false ); + } else if( jo.has_int( "npc_mod_pain" ) ) { + subeffect_fun.set_mod_pain( jo, "npc_mod_pain", false ); + } else if( jo.has_int( "u_add_wet" ) ) { + subeffect_fun.set_add_wet( jo, "u_add_wet", false ); + } else if( jo.has_int( "npc_add_wet" ) ) { + subeffect_fun.set_add_wet( jo, "npc_add_wet", true ); + } else if( jo.has_member( "u_add_power" ) ) { + subeffect_fun.set_add_power( jo, "u_add_power", false ); + } else if( jo.has_member( "npc_add_power" ) ) { + subeffect_fun.set_add_power( jo, "npc_add_power", true ); } else { jo.throw_error( "invalid sub effect syntax: " + jo.str() ); } @@ -2383,6 +2531,7 @@ void talk_effect_t::parse_string_effect( const std::string &effect_id, const Jso WRAP( npc_die ), WRAP( npc_thankful ), WRAP( clear_overrides ), + WRAP( lightning ), WRAP( nothing ) #undef WRAP } @@ -2413,7 +2562,7 @@ void talk_effect_t::parse_string_effect( const std::string &effect_id, const Jso jo.throw_error( "unknown effect string", effect_id ); } -void talk_effect_t::load_effect( const JsonObject &jo ) +void talk_effect_t::load_effect( const JsonObject &jo, const std::string &member_name ) { if( jo.has_member( "opinion" ) ) { JsonIn *ji = jo.get_raw( "opinion" ); @@ -2425,7 +2574,6 @@ void talk_effect_t::load_effect( const JsonObject &jo ) // Same format as when saving a game (-: mission_opinion.deserialize( *ji ); } - static const std::string member_name( "effect" ); if( !jo.has_member( member_name ) ) { return; } else if( jo.has_string( member_name ) ) { @@ -2486,11 +2634,11 @@ talk_response::talk_response( const JsonObject &jo ) } if( jo.has_member( "success" ) ) { JsonObject success_obj = jo.get_object( "success" ); - success = talk_effect_t( success_obj ); + success = talk_effect_t( success_obj, "effect" ); } else if( jo.has_string( "topic" ) ) { // This is for simple topic switching without a possible failure success.next_topic = talk_topic( jo.get_string( "topic" ) ); - success.load_effect( jo ); + success.load_effect( jo, "effect" ); } else if( jo.has_object( "topic" ) ) { success.next_topic = load_inline_topic( jo.get_object( "topic" ) ); } @@ -2499,7 +2647,7 @@ talk_response::talk_response( const JsonObject &jo ) } if( jo.has_member( "failure" ) ) { JsonObject failure_obj = jo.get_object( "failure" ); - failure = talk_effect_t( failure_obj ); + failure = talk_effect_t( failure_obj, "effect" ); } // TODO: mission_selected @@ -2766,7 +2914,7 @@ json_dynamic_line_effect::json_dynamic_line_effect( const JsonObject &jo, { std::function tmp_condition; read_condition( jo, "condition", tmp_condition, true ); - talk_effect_t tmp_effect = talk_effect_t( jo ); + talk_effect_t tmp_effect = talk_effect_t( jo, "effect" ); // if the topic has a sentinel, it means implicitly add a check for the sentinel value // and do not run the effects if it is set. if it is not set, run the effects and // set the sentinel diff --git a/src/npctalk.h b/src/npctalk.h index 4f02b0b3dee73..09746e814def4 100644 --- a/src/npctalk.h +++ b/src/npctalk.h @@ -68,6 +68,7 @@ void deny_train( npc & ); // p gets "asked_to_train" void deny_personal_info( npc & ); // p gets "asked_personal_info" void hostile( npc & ); // p turns hostile to u void flee( npc & ); +void lightning( npc &p ); void leave( npc & ); // p becomes indifferent void stop_following( npc & ); void stranger_neutral( npc & ); // p is now neutral towards you diff --git a/src/npctalk_funcs.cpp b/src/npctalk_funcs.cpp index be2aa274fac55..fe2ab3eb640f4 100644 --- a/src/npctalk_funcs.cpp +++ b/src/npctalk_funcs.cpp @@ -210,7 +210,7 @@ void spawn_animal( npc &p, const mtype_id &mon ) mon_ptr->add_effect( effect_pet, 1_turns, true ); } else { // TODO: handle this gracefully (return the money, proper in-character message from npc) - add_msg_debug( "No space to spawn purchased pet" ); + add_msg_debug( debugmode::DF_NPC, "No space to spawn purchased pet" ); } } @@ -801,6 +801,13 @@ void talk_function::flee( npc &p ) p.set_attitude( NPCATT_FLEE ); } +void talk_function::lightning( npc & ) +{ + if( get_player_character().posz() >= 0 ) { + get_weather().lightning_active = true; + } +} + void talk_function::leave( npc &p ) { add_msg( _( "%s leaves." ), p.name ); @@ -911,7 +918,8 @@ void talk_function::drop_weapon( npc &p ) if( p.is_hallucination() ) { return; } - get_map().add_item_or_charges( p.pos(), p.remove_weapon() ); + item weap = p.remove_weapon(); + get_map().add_item_or_charges( p.pos(), weap ); } void talk_function::player_weapon_away( npc &/*p*/ ) @@ -923,7 +931,8 @@ void talk_function::player_weapon_away( npc &/*p*/ ) void talk_function::player_weapon_drop( npc &/*p*/ ) { Character &player_character = get_player_character(); - get_map().add_item_or_charges( player_character.pos(), player_character.remove_weapon() ); + item weap = player_character.remove_weapon(); + get_map().add_item_or_charges( player_character.pos(), weap ); } void talk_function::lead_to_safety( npc &p ) diff --git a/src/npctrade.cpp b/src/npctrade.cpp index 7943dacd0faf3..cbbcb7bac3051 100644 --- a/src/npctrade.cpp +++ b/src/npctrade.cpp @@ -380,7 +380,7 @@ void trading_window::update_win( npc &np, const std::string &deal ) std::string itname = it->display_name(); if( np.will_exchange_items_freely() && ip.loc.where() != item_location::type::character ) { - itname = itname + " (" + ip.loc.describe( &player_character ) + ")"; + itname += " (" + ip.loc.describe( &player_character ) + ")"; color = c_light_blue; } diff --git a/src/options.cpp b/src/options.cpp index 4abcbb2278f9a..a49ff1bf754ad 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -33,6 +33,7 @@ #include "string_formatter.h" #include "string_input_popup.h" #include "translations.h" +#include "try_parse_integer.h" #include "ui_manager.h" #include "worldfactory.h" @@ -852,17 +853,36 @@ void options_manager::cOpt::setValue( const std::string &sSetIn ) bSet = sSetIn == "True" || sSetIn == "true" || sSetIn == "T" || sSetIn == "t"; } else if( sType == "int" ) { - iSet = atoi( sSetIn.c_str() ); + // Some integer values are stored with a '%', e.g. "100%". + std::string without_percent = sSetIn; + if( string_ends_with( without_percent, "%" ) ) { + without_percent.erase( without_percent.end() - 1 ); + } + ret_val val = try_parse_integer( without_percent, false ); + + if( val.success() ) { + iSet = val.value(); - if( iSet < iMin || iSet > iMax ) { + if( iSet < iMin || iSet > iMax ) { + iSet = iDefault; + } + } else { + debugmsg( "Error parsing option as integer: %s", val.str() ); iSet = iDefault; } } else if( sType == "int_map" ) { - iSet = atoi( sSetIn.c_str() ); + ret_val val = try_parse_integer( sSetIn, false ); + + if( val.success() ) { + iSet = val.value(); - auto item = findInt( iSet ); - if( !item ) { + auto item = findInt( iSet ); + if( !item ) { + iSet = iDefault; + } + } else { + debugmsg( "Error parsing option as integer: %s", val.str() ); iSet = iDefault; } @@ -893,9 +913,10 @@ static std::vector build_resource_list( resource_option.clear(); const auto resource_dirs = get_directories_with( filename, dirname, true ); + const std::string slash_filename = "/" + filename; for( const std::string &resource_dir : resource_dirs ) { - read_from_file( resource_dir + "/" + filename, [&]( std::istream & fin ) { + read_from_file( resource_dir + slash_filename, [&]( std::istream & fin ) { std::string resource_name; std::string view_name; // should only have 2 values inside it, otherwise is going to only load the last 2 values @@ -1399,6 +1420,16 @@ void options_manager::add_options_interface() add_empty_line(); + add( "SHOW_GUN_VARIANTS", "interface", to_translation( "Show gun brand names" ), + to_translation( "Show brand names for guns, intead of generic functional names - 'm4a1' or 'h&k416a5' instead of 'NATO assault rifle'." ), + false ); + add( "AMMO_IN_NAMES", "interface", to_translation( "Add ammo to weapon/magazine names" ), + to_translation( "If true, the default ammo is added to weapon and magazine names. For example \"Mosin-Nagant M44 (4/5)\" becomes \"Mosin-Nagant M44 (4/5 7.62x54mm)\"." ), + true + ); + + add_empty_line(); + add( "SDL_KEYBOARD_MODE", "interface", to_translation( "Use key code input mode" ), to_translation( "Use key code or symbol input on SDL. " "Symbol is recommended for non-qwerty layouts since currently " @@ -1651,10 +1682,6 @@ void options_manager::add_options_interface() to_translation( "If true, show item symbols in inventory and pick up menu." ), false ); - add( "AMMO_IN_NAMES", "interface", to_translation( "Add ammo to weapon/magazine names" ), - to_translation( "If true, the default ammo is added to weapon and magazine names. For example \"Mosin-Nagant M44 (4/5)\" becomes \"Mosin-Nagant M44 (4/5 7.62x54mm)\"." ), - true - ); add_empty_line(); @@ -3004,12 +3031,11 @@ std::string options_manager::show( bool ingame, const bool world_options_only, #if !defined(__ANDROID__) && (defined(TILES) || defined(_WIN32)) if( terminal_size_changed ) { int scaling_factor = get_scaling_factor(); - int TERMX = ::get_option( "TERMINAL_X" ); - int TERMY = ::get_option( "TERMINAL_Y" ); - TERMX -= TERMX % scaling_factor; - TERMY -= TERMY % scaling_factor; - get_option( "TERMINAL_X" ).setValue( std::max( FULL_SCREEN_WIDTH * scaling_factor, TERMX ) ); - get_option( "TERMINAL_Y" ).setValue( std::max( FULL_SCREEN_HEIGHT * scaling_factor, TERMY ) ); + point TERM( ::get_option( "TERMINAL_X" ), ::get_option( "TERMINAL_Y" ) ); + TERM.x -= TERM.x % scaling_factor; + TERM.y -= TERM.y % scaling_factor; + get_option( "TERMINAL_X" ).setValue( std::max( FULL_SCREEN_WIDTH * scaling_factor, TERM.x ) ); + get_option( "TERMINAL_Y" ).setValue( std::max( FULL_SCREEN_HEIGHT * scaling_factor, TERM.y ) ); save(); resize_term( ::get_option( "TERMINAL_X" ), ::get_option( "TERMINAL_Y" ) ); diff --git a/src/output.cpp b/src/output.cpp index 13872e51592c1..4d9f402a0d509 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -616,7 +616,7 @@ border_helper::border_info &border_helper::add_border() return border_info_list.front(); } -void border_helper::draw_border( const catacurses::window &win ) +void border_helper::draw_border( const catacurses::window &win, nc_color border_color ) { if( !border_connection_map.has_value() ) { border_connection_map.emplace(); @@ -654,7 +654,7 @@ void border_helper::draw_border( const catacurses::window &win ) for( const std::pair &conn : border_connection_map.value() ) { if( conn.first.x >= win_beg.x && conn.first.x < win_end.x && conn.first.y >= win_beg.y && conn.first.y < win_end.y ) { - mvwputch( win, conn.first - win_beg, BORDER_COLOR, conn.second.as_curses_line() ); + mvwputch( win, conn.first - win_beg, border_color, conn.second.as_curses_line() ); } } } @@ -719,11 +719,11 @@ bool query_int( int &result, const std::string &text ) string_input_popup popup; popup.title( text ); popup.text( "" ).only_digits( true ); - popup.query(); + int temp = popup.query_int(); if( popup.canceled() ) { return false; } - result = atoi( popup.text().c_str() ); + result = temp; return true; } @@ -870,11 +870,12 @@ void draw_item_filter_rules( const catacurses::window &win, int starty, int heig starty += fold_and_print( win, point( 1, starty ), len, c_white, _( "Search [c]ategory, [m]aterial, " - "[q]uality, [n]otes or " + "[q]uality, [n]otes, " + "[s]skill taught by books or " "[d]isassembled components." ) ); fold_and_print( win, point( 1, starty ), len, c_white, //~ An example of how to filter items based on category or material. - _( "Examples: c:food,m:iron,q:hammering,n:toolshelf,d:pipe" ) ); + _( "Examples: c:food,m:iron,q:hammering,n:toolshelf,d:pipe,s:devices" ) ); wnoutrefresh( win ); } @@ -2115,7 +2116,7 @@ void scrollingcombattext::add( const point &pos, direction p_oDir, iter->advanceStepOffset(); } } - vSCT.push_back( cSCT( pos, p_oDir, p_sText, p_gmt, p_sText2, p_gmt2, p_sType ) ); + vSCT.emplace_back( pos, p_oDir, p_sText, p_gmt, p_sText2, p_gmt2, p_sType ); } } @@ -2353,7 +2354,7 @@ std::vector string_split( const std::string &text_in, char delim_in } if( text_in.back() == delim_in ) { - elems.push_back( "" ); + elems.emplace_back( "" ); } return elems; diff --git a/src/output.h b/src/output.h index 634b76bb730ba..f4732ae2cc447 100644 --- a/src/output.h +++ b/src/output.h @@ -411,7 +411,7 @@ class border_helper }; border_info &add_border(); - void draw_border( const catacurses::window &win ); + void draw_border( const catacurses::window &win, nc_color border_color = BORDER_COLOR ); friend class border_info; private: diff --git a/src/overmap.cpp b/src/overmap.cpp index 79c745e2e6322..a8a5e35aa6324 100644 --- a/src/overmap.cpp +++ b/src/overmap.cpp @@ -59,7 +59,6 @@ static const species_id species_ZOMBIE( "ZOMBIE" ); static const mongroup_id GROUP_CHUD( "GROUP_CHUD" ); static const mongroup_id GROUP_RIVER( "GROUP_RIVER" ); static const mongroup_id GROUP_SEWER( "GROUP_SEWER" ); -static const mongroup_id GROUP_SPIRAL( "GROUP_SPIRAL" ); static const mongroup_id GROUP_SWAMP( "GROUP_SWAMP" ); static const mongroup_id GROUP_WORM( "GROUP_WORM" ); static const mongroup_id GROUP_ZOMBIE( "GROUP_ZOMBIE" ); @@ -78,13 +77,11 @@ using oter_type_id = int_id; using oter_type_str_id = string_id; //////////////// -static oter_id ot_null, - ot_crater, - ot_field, - ot_forest, - ot_forest_thick, - ot_forest_water, - ot_river_center; +static oter_id ot_null; +static const oter_str_id ot_forest( "ot_forest" ); +static const oter_str_id ot_forest_thick( "ot_forest_thick" ); +static const oter_str_id ot_forest_water( "ot_forest_water" ); +static const oter_str_id ot_river_center( "ot_river_center" ); const oter_type_t oter_type_t::null_type{}; @@ -328,13 +325,6 @@ bool operator!=( const int_id &lhs, const char *rhs ) static void set_oter_ids() // FIXME: constify { ot_null = oter_str_id::NULL_ID(); - // NOT required. - ot_crater = oter_id( "crater" ); - ot_field = oter_id( "field" ); - ot_forest = oter_id( "forest" ); - ot_forest_thick = oter_id( "forest_thick" ); - ot_forest_water = oter_id( "forest_water" ); - ot_river_center = oter_id( "river_center" ); } std::string overmap_land_use_code::get_symbol() const @@ -744,9 +734,7 @@ bool oter_t::is_hardcoded() const "looted_building", // pseudo-terrain "mine", "mine_down", - "mine_entrance", "mine_finale", - "mine_shaft", "office_tower_1", "office_tower_1_entrance", "office_tower_b", @@ -754,8 +742,6 @@ bool oter_t::is_hardcoded() const "slimepit", "slimepit_down", "spider_pit_under", - "spiral", - "spiral_hub", "temple", "temple_finale", "temple_stairs", @@ -866,7 +852,7 @@ bool overmap_special::can_belong_to_city( const tripoint_om_omt &p, const city & if( !cit || !city_size.contains( cit.size ) ) { return false; } - return city_distance.contains( cit.get_distance_from( p ) ); + return city_distance.contains( cit.get_distance_from( p ) - ( cit.size ) ); } void overmap_special::load( const JsonObject &jo, const std::string &src ) @@ -1513,7 +1499,6 @@ bool overmap::generate_sub( const int z ) std::vector central_lab_points; std::vector lab_train_points; std::vector central_lab_train_points; - std::vector shaft_points; std::vector mine_points; // These are so common that it's worth checking first as int. const oter_id skip_above[5] = { @@ -1560,44 +1545,38 @@ bool overmap::generate_sub( const int z ) sewer_points.emplace_back( i, j ); } else if( oter_above == "anthill" || oter_above == "acid_anthill" ) { const int size = rng( MIN_ANT_SIZE, MAX_ANT_SIZE ); - ant_points.push_back( city( p.xy(), size ) ); + ant_points.emplace_back( p.xy(), size ); } else if( oter_above == "slimepit_down" ) { const int size = rng( MIN_GOO_SIZE, MAX_GOO_SIZE ); - goo_points.push_back( city( p.xy(), size ) ); + goo_points.emplace_back( p.xy(), size ); } else if( oter_above == "forest_water" ) { ter_set( p, oter_id( "cavern" ) ); chip_rock( p ); } else if( oter_above == "lab_core" || ( z == -1 && oter_above == "lab_stairs" ) ) { - lab_points.push_back( city( p.xy(), rng( 1, 5 + z ) ) ); + lab_points.emplace_back( p.xy(), rng( 1, 5 + z ) ); } else if( oter_above == "lab_stairs" ) { ter_set( p, oter_id( "lab" ) ); } else if( oter_above == "ice_lab_core" || ( z == -1 && oter_above == "ice_lab_stairs" ) ) { - ice_lab_points.push_back( city( p.xy(), rng( 1, 5 + z ) ) ); + ice_lab_points.emplace_back( p.xy(), rng( 1, 5 + z ) ); } else if( oter_above == "ice_lab_stairs" ) { ter_set( p, oter_id( "ice_lab" ) ); } else if( oter_above == "central_lab_core" ) { - central_lab_points.push_back( city( p.xy(), rng( std::max( 1, 7 + z ), 9 + z ) ) ); + central_lab_points.emplace_back( p.xy(), rng( std::max( 1, 7 + z ), 9 + z ) ); } else if( oter_above == "central_lab_stairs" ) { ter_set( p, oter_id( "central_lab" ) ); } else if( is_ot_match( "hidden_lab_stairs", oter_above, ot_match_type::contains ) ) { - lab_points.push_back( city( p.xy(), rng( 1, 5 + z ) ) ); - } else if( oter_above == "mine_entrance" ) { - shaft_points.push_back( p.xy() ); - } else if( oter_above == "mine_shaft" || - oter_above == "mine_down" ) { + lab_points.emplace_back( p.xy(), rng( 1, 5 + z ) ); + } else if( is_ot_match( "mine_entrance", oter_ground, ot_match_type::prefix ) && z == -2 ) { + mine_points.emplace_back( ( p + tripoint_west ).xy(), rng( 6 + z, 10 + z ) ); + requires_sub = true; + } else if( oter_above == "mine_down" ) { ter_set( p, oter_id( "mine" ) ); - mine_points.push_back( city( p.xy(), rng( 6 + z, 10 + z ) ) ); + mine_points.emplace_back( p.xy(), rng( 6 + z, 10 + z ) ); // technically not all finales need a sub level, // but at this point we don't know requires_sub = true; - } else if( oter_above == "mine_finale" ) { - for( const tripoint_om_omt &q : points_in_radius( p, 1, 0 ) ) { - ter_set( q, oter_id( "spiral" ) ); - } - ter_set( p, oter_id( "spiral_hub" ) ); - add_mon_group( mongroup( GROUP_SPIRAL, tripoint( i * 2, j * 2, z ), 2, 200 ) ); } } } @@ -1746,10 +1725,6 @@ bool overmap::generate_sub( const int z ) build_mine( tripoint_om_omt( i.pos, z ), i.size ); } - for( auto &i : shaft_points ) { - ter_set( tripoint_om_omt( i, z ), oter_id( "mine_shaft" ) ); - requires_sub = true; - } for( auto &i : ant_points ) { const tripoint_om_omt p_loc( i.pos, z ); if( ter( p_loc ) != "empty_rock" ) { @@ -2132,11 +2107,11 @@ void overmap::signal_hordes( const tripoint_rel_sm &p_rel, const int sig_power ) const int min_inc_inter = 3; // Min interest increase to already targeted source const int inc_roll = rng( min_inc_inter, calculated_inter ); mg.inc_interest( inc_roll ); - add_msg_debug( "horde inc interest %d dist %d", inc_roll, dist ); + add_msg_debug( debugmode::DF_OVERMAP, "horde inc interest %d dist %d", inc_roll, dist ); } else { // New signal source mg.set_target( p.xy() ); mg.set_interest( min_capped_inter ); - add_msg_debug( "horde set interest %d dist %d", min_capped_inter, dist ); + add_msg_debug( debugmode::DF_OVERMAP, "horde set interest %d dist %d", min_capped_inter, dist ); } } } @@ -2629,10 +2604,10 @@ void overmap::place_rivers( const overmap *north, const overmap *east, const ove while( river_start.empty() || river_start.size() + 1 < river_end.size() ) { new_rivers.clear(); if( north == nullptr && one_in( river_chance ) ) { - new_rivers.push_back( point_om_omt( rng( 10, OMAPX - 11 ), 0 ) ); + new_rivers.emplace_back( rng( 10, OMAPX - 11 ), 0 ); } if( west == nullptr && one_in( river_chance ) ) { - new_rivers.push_back( point_om_omt( 0, rng( 10, OMAPY - 11 ) ) ); + new_rivers.emplace_back( 0, rng( 10, OMAPY - 11 ) ); } river_start.push_back( random_entry( new_rivers ) ); } @@ -2641,10 +2616,10 @@ void overmap::place_rivers( const overmap *north, const overmap *east, const ove while( river_end.empty() || river_end.size() + 1 < river_start.size() ) { new_rivers.clear(); if( south == nullptr && one_in( river_chance ) ) { - new_rivers.push_back( point_om_omt( rng( 10, OMAPX - 11 ), OMAPY - 1 ) ); + new_rivers.emplace_back( rng( 10, OMAPX - 11 ), OMAPY - 1 ); } if( east == nullptr && one_in( river_chance ) ) { - new_rivers.push_back( point_om_omt( OMAPX - 1, rng( 10, OMAPY - 11 ) ) ); + new_rivers.emplace_back( OMAPX - 1, rng( 10, OMAPY - 11 ) ); } river_end.push_back( random_entry( new_rivers ) ); } @@ -2675,8 +2650,8 @@ void overmap::place_rivers( const overmap *north, const overmap *east, const ove } } else if( !river_end.empty() ) { if( river_start.size() != river_end.size() ) { - river_start.push_back( point_om_omt( rng( OMAPX / 4, ( OMAPX * 3 ) / 4 ), - rng( OMAPY / 4, ( OMAPY * 3 ) / 4 ) ) ); + river_start.emplace_back( rng( OMAPX / 4, ( OMAPX * 3 ) / 4 ), + rng( OMAPY / 4, ( OMAPY * 3 ) / 4 ) ); } for( size_t i = 0; i < river_start.size(); i++ ) { place_river( river_start[i], river_end[i] ); @@ -4446,21 +4421,21 @@ void overmap::place_radios() // Since location have id such as "radio_tower_1_north", we must check the beginning of the id if( is_ot_match( "radio_tower", ter( pos_omt ), ot_match_type::prefix ) ) { if( one_in( 3 ) ) { - radios.push_back( radio_tower( pos_sm, strength(), "", radio_type::WEATHER_RADIO ) ); + radios.emplace_back( pos_sm, strength(), "", radio_type::WEATHER_RADIO ); } else { message = SNIPPET.expand( SNIPPET.random_from_category( "radio_archive" ).value_or( translation() ).translated() ); - radios.push_back( radio_tower( pos_sm, strength(), message ) ); + radios.emplace_back( pos_sm, strength(), message ); } } else if( is_ot_match( "lmoe", ter( pos_omt ), ot_match_type::prefix ) ) { message = string_format( _( "This is automated emergency shelter beacon %d%d." " Supplies, amenities and shelter are stocked." ), i, j ); - radios.push_back( radio_tower( pos_sm, strength() / 2, message ) ); + radios.emplace_back( pos_sm, strength() / 2, message ); } else if( is_ot_match( "fema_entrance", ter( pos_omt ), ot_match_type::prefix ) ) { message = string_format( _( "This is FEMA camp %d%d." " Supplies are limited, please bring supplemental food, water, and bedding." " This is FEMA camp %d%d. A designated long-term emergency shelter." ), i, j, i, j ); - radios.push_back( radio_tower( pos_sm, strength(), message ) ); + radios.emplace_back( pos_sm, strength(), message ); } } } diff --git a/src/overmap.h b/src/overmap.h index 65261344dc234..e1b64da5cfb50 100644 --- a/src/overmap.h +++ b/src/overmap.h @@ -137,9 +137,15 @@ class overmap_special_batch std::vector::iterator begin() { return placements.begin(); } + std::vector::const_iterator begin() const { + return placements.begin(); + } std::vector::iterator end() { return placements.end(); } + std::vector::const_iterator end() const { + return placements.end(); + } std::vector::iterator erase( std::vector::iterator pos ) { return placements.erase( pos ); diff --git a/src/overmap_ui.cpp b/src/overmap_ui.cpp index 79c97b7e89cd6..8a29ac4c1b97e 100644 --- a/src/overmap_ui.cpp +++ b/src/overmap_ui.cpp @@ -22,7 +22,11 @@ #include "activity_actor_definitions.h" #include "avatar.h" #include "basecamp.h" +#include "cached_options.h" #include "calendar.h" +#ifdef TILES +#include "cata_tiles.h" +#endif // TILES #include "cata_utility.h" #include "catacharset.h" #include "character.h" @@ -31,6 +35,7 @@ #include "coordinates.h" #include "cuboid_rectangle.h" #include "cursesdef.h" +#include "cursesport.h" #include "enums.h" #include "game.h" #include "game_constants.h" @@ -55,6 +60,7 @@ #include "point.h" #include "regional_settings.h" #include "rng.h" +#include "sdltiles.h" #include "sounds.h" #include "string_formatter.h" #include "string_input_popup.h" @@ -96,7 +102,7 @@ static const int npm_height = 3; namespace overmap_ui { // {note symbol, note color, offset to text} -static std::tuple get_note_display_info( const std::string ¬e ) +std::tuple get_note_display_info( const std::string ¬e ) { std::tuple result {'N', c_yellow, 0}; bool set_color = false; @@ -191,22 +197,21 @@ static void update_note_preview( const std::string ¬e, mvwputch( *w_preview_title, point( note_text_width, 1 ), c_white, LINE_XOOX ); wnoutrefresh( *w_preview_title ); - const int npm_offset_x = 1; - const int npm_offset_y = 1; + const point npm_offset( point_south_east ); werase( *w_preview_map ); draw_border( *w_preview_map, c_yellow ); for( int i = 0; i < npm_height; i++ ) { for( int j = 0; j < npm_width; j++ ) { const auto &ter = map_around[i * npm_width + j]; - mvwputch( *w_preview_map, point( j + npm_offset_x, i + npm_offset_y ), ter.first, ter.second ); + mvwputch( *w_preview_map, npm_offset + point( j, i ), ter.first, ter.second ); } } - mvwputch( *w_preview_map, point( npm_width / 2 + npm_offset_x, npm_height / 2 + npm_offset_y ), + mvwputch( *w_preview_map, npm_offset + point( npm_width / 2, npm_height / 2 ), note_color, symbol ); wnoutrefresh( *w_preview_map ); } -static weather_type_id get_weather_at_point( const tripoint_abs_omt &pos ) +weather_type_id get_weather_at_point( const tripoint_abs_omt &pos ) { // Weather calculation is a bit expensive, so it's cached here. static std::map weather_cache; @@ -220,8 +225,7 @@ static weather_type_id get_weather_at_point( const tripoint_abs_omt &pos ) // TODO: fix point types const tripoint abs_ms_pos = project_to( pos ).raw(); const auto &wgen = overmap_buffer.get_settings( pos ).weather; - const auto weather = wgen.get_weather_conditions( abs_ms_pos, calendar::turn, g->get_seed(), - g->weather.next_instance_allowed ); + const auto weather = wgen.get_weather_conditions( abs_ms_pos, calendar::turn, g->get_seed() ); iter = weather_cache.insert( std::make_pair( pos, weather ) ).first; } return iter->second; @@ -495,24 +499,23 @@ static point_abs_omt draw_notes( const tripoint_abs_omt &origin ) return result; } -void draw( - const catacurses::window &w, const catacurses::window &wbar, const tripoint_abs_omt ¢er, - const tripoint_abs_omt &orig, bool blink, bool show_explored, bool fast_scroll, - input_context *inp_ctxt, const draw_data_t &data ) +static void draw_ascii( + const catacurses::window &w, const tripoint_abs_omt ¢er, + const tripoint_abs_omt &orig, bool blink, bool show_explored, bool /* fast_scroll */, + input_context * /* inp_ctxt */, const draw_data_t &data ) { - const int om_map_width = OVERMAP_WINDOW_WIDTH; + + const int om_map_width = OVERMAP_WINDOW_WIDTH; const int om_map_height = OVERMAP_WINDOW_HEIGHT; const int om_half_width = om_map_width / 2; const int om_half_height = om_map_height / 2; const bool viewing_weather = - ( ( data.debug_weather || data.visible_weather ) && center.z() == 10 ); + ( ( uistate.overmap_debug_weather || uistate.overmap_visible_weather ) && center.z() == 10 ); avatar &player_character = get_avatar(); // Target of current mission const tripoint_abs_omt 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 = player_character.has_trait( trait_DEBUG_NIGHTVISION ); @@ -540,7 +543,7 @@ void draw( // If we're debugging monster groups, find the monster group we've selected const mongroup *mgroup = nullptr; std::vector mgroups; - if( data.debug_mongroup ) { + if( uistate.overmap_debug_mongroup ) { mgroups = overmap_buffer.monsters_at( center ); for( const auto &mgp : mgroups ) { mgroup = mgp; @@ -553,7 +556,7 @@ void draw( // A small LRU cache: most oter_id's occur in clumps like forests of swamps. // This cache helps avoid much more costly lookups in the full hashmap. constexpr size_t cache_size = 8; // used below to calculate the next index - std::array, cache_size> cache {{}}; + std::array, cache_size> cache{ {} }; size_t cache_next = 0; const auto set_color_and_symbol = [&]( const oter_id & cur_ter, const tripoint_abs_omt & omp, @@ -627,7 +630,7 @@ void draw( auto iter = npc_color.find( pos ); nc_color np_color = np->basic_symbol_color(); if( iter == npc_color.end() ) { - npc_color[ pos ] = { np_color, 1 }; + npc_color[pos] = { np_color, 1 }; } else { iter->second.count++; // Randomly change to new NPC's color @@ -655,7 +658,7 @@ void draw( npc *npc_to_add = elem.get(); if( npc_to_add->mission == NPC_MISSION_TRAVELLING && !npc_to_add->omt_path.empty() ) { for( auto &elem : npc_to_add->omt_path ) { - path_route.push_back( tripoint_abs_omt( elem.xy(), npc_to_add->posz() ) ); + path_route.emplace_back( elem.xy(), npc_to_add->posz() ); } } } @@ -671,7 +674,7 @@ void draw( auto iter = npc_color.find( pos ); nc_color np_color = np->basic_symbol_color(); if( iter == npc_color.end() ) { - npc_color[ pos ] = { np_color, 1 }; + npc_color[pos] = { np_color, 1 }; } else { iter->second.count++; // Randomly change to new NPC's color @@ -685,7 +688,6 @@ void draw( for( int i = 0; i < om_map_width; ++i ) { for( int j = 0; j < om_map_height; ++j ) { const tripoint_abs_omt omp = corner + point( i, j ); - oter_id cur_ter = oter_str_id::NULL_ID(); nc_color ter_color = c_black; std::string ter_sym = " "; @@ -710,7 +712,7 @@ void draw( // Display player pos, should always be visible ter_color = player_character.symbol_color(); ter_sym = "@"; - } else if( viewing_weather && ( data.debug_weather || los_sky ) ) { + } else if( viewing_weather && ( uistate.overmap_debug_weather || los_sky ) ) { const weather_type_id type = get_weather_at_point( omp ); ter_color = type->map_color; ter_sym = type->get_symbol(); @@ -732,15 +734,15 @@ void draw( } else if( !see ) { // All cases above ignore the seen-status, ter_color = c_dark_gray; - ter_sym = "#"; + ter_sym = "#"; // All cases below assume that see is true. } else if( blink && npc_color.count( omp ) != 0 ) { // Visible NPCs are cached already - ter_color = npc_color[ omp ].color; - ter_sym = "@"; + ter_color = npc_color[omp].color; + ter_sym = "@"; } else if( blink && mycount != 0 && g->debug_pathfinding ) { ter_color = c_red; - ter_sym = "!"; + ter_sym = "!"; } else if( blink && player_path_count ) { ter_color = c_blue; ter_sym = "!"; @@ -748,14 +750,14 @@ void draw( overmap_buffer.get_horde_size( omp ) >= HORDE_VISIBILITY_SIZE ) { // Display Hordes only when within player line-of-sight ter_color = c_green; - ter_sym = overmap_buffer.get_horde_size( omp ) > HORDE_VISIBILITY_SIZE * 2 ? "Z" : "z"; + ter_sym = overmap_buffer.get_horde_size( omp ) > HORDE_VISIBILITY_SIZE * 2 ? "Z" : "z"; } else if( blink && overmap_buffer.has_vehicle( omp ) ) { // Display Vehicles only when player can see the location ter_color = c_cyan; - ter_sym = "c"; + ter_sym = "c"; } else if( !sZoneName.empty() && tripointZone.xy() == omp.xy() ) { ter_color = c_yellow; - ter_sym = "Z"; + ter_sym = "Z"; } else if( !uistate.overmap_show_forest_trails && cur_ter && is_ot_match( "forest_trail", cur_ter, ot_match_type::type ) ) { // If forest trails shouldn't be displayed, and this is a forest trail, then @@ -767,7 +769,7 @@ void draw( } // Are we debugging monster groups? - if( blink && data.debug_mongroup ) { + if( blink && uistate.overmap_debug_mongroup ) { // Check if this tile is the target of the currently selected group // Convert to position within overmap @@ -830,7 +832,6 @@ void draw( } if( omp.xy() == center.xy() && !uistate.place_special ) { - csee = see; ccur_ter = cur_ter; mvwputch_hi( w, point( i, j ), ter_color, ter_sym ); } else { @@ -922,7 +923,7 @@ void draw( mvwputch( w, point( 1, corner_text.size() + 2 ), c_white, LINE_XXOO ); const std::string spacer( maxlen, ' ' ); for( size_t i = 0; i < corner_text.size(); i++ ) { - const auto &pr = corner_text[ i ]; + const auto &pr = corner_text[i]; // clear line, print line, print vertical line on each side. mvwputch( w, point( 1, i + 2 ), c_white, LINE_XOXO ); mvwprintz( w, point( 2, i + 2 ), c_yellow, spacer ); @@ -952,6 +953,46 @@ void draw( mvwputch( w, point( length, om_map_height - 1 ), c_white, LINE_XOXO ); } + // draw nice crosshair around the cursor + if( blink && !uistate.place_terrain && !uistate.place_special ) { + mvwputch( w, point( om_half_width - 1, om_half_height - 1 ), c_light_gray, LINE_OXXO ); + mvwputch( w, point( om_half_width + 1, om_half_height - 1 ), c_light_gray, LINE_OOXX ); + mvwputch( w, point( om_half_width - 1, om_half_height + 1 ), c_light_gray, LINE_XXOO ); + mvwputch( w, point( om_half_width + 1, om_half_height + 1 ), c_light_gray, LINE_XOOX ); + } + // Done with all drawing! + wmove( w, point( om_half_width, om_half_height ) ); + wnoutrefresh( w ); +} + +static void draw_om_sidebar( + const catacurses::window &wbar, const tripoint_abs_omt ¢er, + const tripoint_abs_omt &orig, bool /* blink */, bool fast_scroll, + input_context *inp_ctxt, const draw_data_t &data ) +{ + avatar &player_character = get_avatar(); + // Debug vision allows seeing everything + 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 ? + player_character.overmap_sight_range( g->light_level( player_character.posz() ) ) : + 100; + oter_id ccur_ter = overmap_buffer.ter( center ).id().id(); + const tripoint_abs_omt target = player_character.get_active_mission_target(); + const bool has_target = target != overmap::invalid_tripoint; + const bool viewing_weather = uistate.overmap_debug_weather || uistate.overmap_visible_weather; + + // If we're debugging monster groups, find the monster group we've selected + std::vector mgroups; + if( uistate.overmap_debug_mongroup ) { + mgroups = overmap_buffer.monsters_at( center ); + for( const auto &mgp : mgroups ) { + if( mgp->horde ) { + break; + } + } + } + // Draw the vertical line for( int j = 0; j < TERMY; j++ ) { mvwputch( wbar, point( 0, j ), c_white, LINE_XOXO ); @@ -966,7 +1007,7 @@ void draw( // Draw text describing the overmap tile at the cursor position. int lines = 1; - if( csee && !viewing_weather ) { + if( ( has_debug_vision || overmap_buffer.seen( center ) ) && !viewing_weather ) { if( !mgroups.empty() ) { int line_number = 6; for( const auto &mgroup : mgroups ) { @@ -995,7 +1036,7 @@ void draw( overmap_buffer.get_description_at( sm_pos ) ); } } else if( viewing_weather ) { - const bool weather_is_visible = ( data.debug_weather || + const bool weather_is_visible = ( uistate.overmap_debug_weather || player_character.overmap_los( center, sight_points * 2 ) ); if( weather_is_visible ) { // NOLINTNEXTLINE(cata-use-named-point-constants) @@ -1078,6 +1119,7 @@ void draw( print_hint( "TOGGLE_EXPLORED", is_explored ? c_pink : c_magenta ); print_hint( "TOGGLE_FAST_SCROLL", fast_scroll ? c_pink : c_magenta ); print_hint( "TOGGLE_FOREST_TRAILS", uistate.overmap_show_forest_trails ? c_pink : c_magenta ); + print_hint( "TOGGLE_OVERMAP_WEATHER", uistate.overmap_visible_weather ? c_pink : c_magenta ); print_hint( "HELP_KEYBINDINGS" ); print_hint( "QUIT" ); } @@ -1088,18 +1130,23 @@ void draw( std::tie( om, omt ) = project_remain( abs_omt ); mvwprintz( wbar, point( 1, getmaxy( wbar ) - 1 ), c_red, _( "LEVEL %i, %d'%d, %d'%d" ), center.z(), om.x(), omt.x(), om.y(), omt.y() ); + wnoutrefresh( wbar ); +} - // draw nice crosshair around the cursor - if( blink && !uistate.place_terrain && !uistate.place_special ) { - mvwputch( w, point( om_half_width - 1, om_half_height - 1 ), c_light_gray, LINE_OXXO ); - mvwputch( w, point( om_half_width + 1, om_half_height - 1 ), c_light_gray, LINE_OOXX ); - mvwputch( w, point( om_half_width - 1, om_half_height + 1 ), c_light_gray, LINE_XXOO ); - mvwputch( w, point( om_half_width + 1, om_half_height + 1 ), c_light_gray, LINE_XOOX ); +void draw( + const catacurses::window &w, const catacurses::window &wbar, const tripoint_abs_omt ¢er, + const tripoint_abs_omt &orig, bool blink, bool show_explored, bool fast_scroll, + input_context *inp_ctxt, const draw_data_t &data ) +{ + draw_om_sidebar( wbar, center, orig, blink, fast_scroll, inp_ctxt, data ); + if( !use_tiles ) { + draw_ascii( w, center, orig, blink, show_explored, fast_scroll, inp_ctxt, data ); + } else { +#ifdef TILES + cata_cursesport::WINDOW *const win = w.get(); + tilecontext->draw_om( win->pos, center, blink ); +#endif // TILES } - // Done with all drawing! - wnoutrefresh( wbar ); - wmove( w, point( om_half_width, om_half_height ) ); - wnoutrefresh( w ); } void create_note( const tripoint_abs_omt &curs ) @@ -1504,6 +1551,8 @@ static tripoint_abs_omt display( const tripoint_abs_omt &orig, ictxt.register_action( "CONFIRM" ); ictxt.register_action( "LEVEL_UP" ); ictxt.register_action( "LEVEL_DOWN" ); + ictxt.register_action( "ZOOM_OUT" ); + ictxt.register_action( "ZOOM_IN" ); ictxt.register_action( "HELP_KEYBINDINGS" ); ictxt.register_action( "MOUSE_MOVE" ); ictxt.register_action( "SELECT" ); @@ -1523,6 +1572,7 @@ static tripoint_abs_omt display( const tripoint_abs_omt &orig, ictxt.register_action( "TOGGLE_CITY_LABELS" ); ictxt.register_action( "TOGGLE_EXPLORED" ); ictxt.register_action( "TOGGLE_FAST_SCROLL" ); + ictxt.register_action( "TOGGLE_OVERMAP_WEATHER" ); ictxt.register_action( "TOGGLE_FOREST_TRAILS" ); ictxt.register_action( "MISSIONS" ); @@ -1575,6 +1625,10 @@ static tripoint_abs_omt display( const tripoint_abs_omt &orig, curs.z() -= 1; } else if( action == "LEVEL_UP" && curs.z() < OVERMAP_HEIGHT ) { curs.z() += 1; + } else if( action == "ZOOM_OUT" ) { + g->zoom_out(); + } else if( action == "ZOOM_IN" ) { + g->zoom_in(); } else if( action == "CONFIRM" ) { ret = curs; } else if( action == "QUIT" ) { @@ -1671,6 +1725,8 @@ static tripoint_abs_omt display( const tripoint_abs_omt &orig, uistate.overmap_show_city_labels = !uistate.overmap_show_city_labels; } else if( action == "TOGGLE_EXPLORED" ) { overmap_buffer.toggle_explored( curs ); + } else if( action == "TOGGLE_OVERMAP_WEATHER" ) { + uistate.overmap_visible_weather = !uistate.overmap_visible_weather; } else if( action == "TOGGLE_FAST_SCROLL" ) { fast_scroll = !fast_scroll; } else if( action == "TOGGLE_FOREST_TRAILS" ) { @@ -1706,26 +1762,29 @@ void ui::omap::display() void ui::omap::display_hordes() { overmap_ui::draw_data_t data; - data.debug_mongroup = true; + uistate.overmap_debug_mongroup = true; overmap_ui::display( get_player_character().global_omt_location(), data ); + uistate.overmap_debug_mongroup = false; } void ui::omap::display_weather() { overmap_ui::draw_data_t data; - data.debug_weather = true; + uistate.overmap_debug_weather = true; tripoint_abs_omt pos = get_player_character().global_omt_location(); pos.z() = 10; overmap_ui::display( pos, data ); + uistate.overmap_debug_weather = false; } void ui::omap::display_visible_weather() { overmap_ui::draw_data_t data; - data.visible_weather = true; + uistate.overmap_visible_weather = true; tripoint_abs_omt pos = get_player_character().global_omt_location(); pos.z() = 10; overmap_ui::display( pos, data ); + uistate.overmap_visible_weather = false; } void ui::omap::display_scents() diff --git a/src/overmap_ui.h b/src/overmap_ui.h index 59726b42b9a8d..c727571f76a30 100644 --- a/src/overmap_ui.h +++ b/src/overmap_ui.h @@ -3,6 +3,7 @@ #define CATA_SRC_OVERMAP_UI_H #include "coordinates.h" +#include "string_id.h" namespace catacurses { @@ -10,6 +11,10 @@ class window; } // namespace catacurses class input_context; +class nc_color; + +struct weather_type; +using weather_type_id = string_id; namespace ui { @@ -76,12 +81,6 @@ namespace overmap_ui { // drawing relevant data, e.g. what to draw. struct draw_data_t { - // draw monster groups on the overmap. - bool debug_mongroup = false; - // draw weather, e.g. clouds etc. - bool debug_weather = false; - // draw weather only around player position - bool visible_weather = false; // draw editor. bool debug_editor = false; // draw scent traces. @@ -91,6 +90,8 @@ struct draw_data_t { int iZoneIndex = -1; }; +weather_type_id get_weather_at_point( const tripoint_abs_omt &pos ); +std::tuple get_note_display_info( const std::string ¬e ); void draw( const catacurses::window &w, const catacurses::window &wbar, const tripoint_abs_omt ¢er, const tripoint_abs_omt &orig, bool blink, bool show_explored, bool fast_scroll, diff --git a/src/panels.cpp b/src/panels.cpp index 9132293f7551d..96569b066cf2b 100644 --- a/src/panels.cpp +++ b/src/panels.cpp @@ -247,8 +247,7 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const bool drew_mission = targ == overmap::invalid_tripoint; const int start_y = start_input.y; const int start_x = start_input.x; - const int mid_x = width / 2; - const int mid_y = height / 2; + const point mid( width / 2, height / 2 ); map &here = get_map(); for( int i = -( width / 2 ); i <= width - ( width / 2 ) - 1; i++ ) { @@ -372,9 +371,9 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const } if( i == 0 && j == 0 ) { // Highlight player character position in center of minimap - mvwputch_hi( w_minimap, point( mid_x + start_x, mid_y + start_y ), ter_color, ter_sym ); + mvwputch_hi( w_minimap, mid + point( start_x, start_y ), ter_color, ter_sym ); } else { - mvwputch( w_minimap, point( mid_x + i + start_x, mid_y + j + start_y ), ter_color, + mvwputch( w_minimap, mid + point( i + start_x, j + start_y ), ter_color, ter_sym ); } } @@ -389,37 +388,36 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const if( std::fabs( slope ) > 12 ) { // For any near-vertical slope, center the marker if( targ.y() > curs.y() ) { - mvwputch( w_minimap, point( mid_x + start_x, height - 1 + start_y ), c_red, '*' ); + mvwputch( w_minimap, point( mid.x + start_x, height - 1 + start_y ), c_red, '*' ); } else { - mvwputch( w_minimap, point( mid_x + start_x, 1 + start_y ), c_red, '*' ); + mvwputch( w_minimap, point( mid.x + start_x, 1 + start_y ), c_red, '*' ); } } else { - int arrowx = -1; - int arrowy = -1; + point arrow( point_north_west ); if( std::fabs( slope ) >= 1. ) { // If target to the north or south, arrow on top or bottom edge of minimap if( targ.y() > curs.y() ) { - arrowx = static_cast( ( 1. + ( 1. / slope ) ) * mid_x ); - arrowy = height - 1; + arrow.x = static_cast( ( 1. + ( 1. / slope ) ) * mid.x ); + arrow.y = height - 1; } else { - arrowx = static_cast( ( 1. - ( 1. / slope ) ) * mid_x ); - arrowy = 0; + arrow.x = static_cast( ( 1. - ( 1. / slope ) ) * mid.x ); + arrow.y = 0; } // Clip to left/right edges - arrowx = std::max( arrowx, 0 ); - arrowx = std::min( arrowx, width - 1 ); + arrow.x = std::max( arrow.x, 0 ); + arrow.x = std::min( arrow.x, width - 1 ); } else { // If target to the east or west, arrow on left or right edge of minimap if( targ.x() > curs.x() ) { - arrowx = width - 1; - arrowy = static_cast( ( 1. + slope ) * mid_y ); + arrow.x = width - 1; + arrow.y = static_cast( ( 1. + slope ) * mid.y ); } else { - arrowx = 0; - arrowy = static_cast( ( 1. - slope ) * mid_y ); + arrow.x = 0; + arrow.y = static_cast( ( 1. - slope ) * mid.y ); } // Clip to top/bottom edges - arrowy = std::max( arrowy, 0 ); - arrowy = std::min( arrowy, height - 1 ); + arrow.y = std::max( arrow.y, 0 ); + arrow.y = std::min( arrow.y, height - 1 ); } char glyph = '*'; if( targ.z() > you.posz() ) { @@ -428,7 +426,7 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const glyph = 'v'; } - mvwputch( w_minimap, point( arrowx + start_x, arrowy + start_y ), c_red, glyph ); + mvwputch( w_minimap, arrow + point( start_x, start_y ), c_red, glyph ); } } avatar &player_character = get_avatar(); @@ -444,7 +442,7 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const if( horde_size >= HORDE_VISIBILITY_SIZE ) { if( overmap_buffer.seen( omp ) && player_character.overmap_los( omp, sight_points ) ) { - mvwputch( w_minimap, point( i + mid_x, j + mid_y ), c_green, + mvwputch( w_minimap, mid + point( i, j ), c_green, horde_size > HORDE_VISIBILITY_SIZE * 2 ? 'Z' : 'z' ); } } @@ -1112,20 +1110,20 @@ static void draw_stealth( avatar &u, const catacurses::window &w ) static void draw_time_graphic( const catacurses::window &w ) { std::vector > vGlyphs; - vGlyphs.push_back( std::make_pair( '_', c_red ) ); - vGlyphs.push_back( std::make_pair( '_', c_cyan ) ); - vGlyphs.push_back( std::make_pair( '.', c_brown ) ); - vGlyphs.push_back( std::make_pair( ',', c_blue ) ); - vGlyphs.push_back( std::make_pair( '+', c_yellow ) ); - vGlyphs.push_back( std::make_pair( 'c', c_light_blue ) ); - vGlyphs.push_back( std::make_pair( '*', c_yellow ) ); - vGlyphs.push_back( std::make_pair( 'C', c_white ) ); - vGlyphs.push_back( std::make_pair( '+', c_yellow ) ); - vGlyphs.push_back( std::make_pair( 'c', c_light_blue ) ); - vGlyphs.push_back( std::make_pair( '.', c_brown ) ); - vGlyphs.push_back( std::make_pair( ',', c_blue ) ); - vGlyphs.push_back( std::make_pair( '_', c_red ) ); - vGlyphs.push_back( std::make_pair( '_', c_cyan ) ); + vGlyphs.emplace_back( '_', c_red ); + vGlyphs.emplace_back( '_', c_cyan ); + vGlyphs.emplace_back( '.', c_brown ); + vGlyphs.emplace_back( ',', c_blue ); + vGlyphs.emplace_back( '+', c_yellow ); + vGlyphs.emplace_back( 'c', c_light_blue ); + vGlyphs.emplace_back( '*', c_yellow ); + vGlyphs.emplace_back( 'C', c_white ); + vGlyphs.emplace_back( '+', c_yellow ); + vGlyphs.emplace_back( 'c', c_light_blue ); + vGlyphs.emplace_back( '.', c_brown ); + vGlyphs.emplace_back( ',', c_blue ); + vGlyphs.emplace_back( '_', c_red ); + vGlyphs.emplace_back( '_', c_cyan ); const int iHour = hour_of_day( calendar::turn ); wprintz( w, c_white, "[" ); diff --git a/src/past_games_info.cpp b/src/past_games_info.cpp index d34c2bd08a09a..fa35621131500 100644 --- a/src/past_games_info.cpp +++ b/src/past_games_info.cpp @@ -116,7 +116,7 @@ void past_games_info::ensure_loaded() std::istringstream iss( read_entire_file( filename ) ); try { JsonIn jsin( iss ); - info_.push_back( past_game_info( jsin ) ); + info_.emplace_back( jsin ); } catch( const JsonError &err ) { debugmsg( "Error reading memorial file %s: %s", filename, err.what() ); } catch( const too_old_memorial_file_error & ) { diff --git a/src/pathfinding.cpp b/src/pathfinding.cpp index e9a6d0b8f58c7..5b3e256a7af9e 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -226,22 +226,16 @@ std::vector map::route( const tripoint &f, const tripoint &t, bool sharpavoid = settings.avoid_sharp; const int pad = 16; // Should be much bigger - low value makes pathfinders dumb! - int minx = std::min( f.x, t.x ) - pad; - int miny = std::min( f.y, t.y ) - pad; - // TODO: Make this way bigger - int minz = std::min( f.z, t.z ); - int maxx = std::max( f.x, t.x ) + pad; - int maxy = std::max( f.y, t.y ) + pad; - // Same TODO: as above - int maxz = std::max( f.z, t.z ); - clip_to_bounds( minx, miny, minz ); - clip_to_bounds( maxx, maxy, maxz ); - - pathfinder pf( point( minx, miny ), point( maxx, maxy ) ); + tripoint min( std::min( f.x, t.x ) - pad, std::min( f.y, t.y ) - pad, std::min( f.z, t.z ) ); + tripoint max( std::max( f.x, t.x ) + pad, std::max( f.y, t.y ) + pad, std::max( f.z, t.z ) ); + clip_to_bounds( min.x, min.y, min.z ); + clip_to_bounds( max.x, max.y, max.z ); + + pathfinder pf( min.xy(), max.xy() ); // Make NPCs not want to path through player // But don't make player pathing stop working for( const auto &p : pre_closed ) { - if( p.x >= minx && p.x < maxx && p.y >= miny && p.y < maxy ) { + if( p.x >= min.x && p.x < max.x && p.y >= min.y && p.y < max.y ) { pf.close_point( p ); } } @@ -288,7 +282,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, const int index = flat_index( p.xy() ); // TODO: Remove this and instead have sentinels at the edges - if( p.x < minx || p.x >= maxx || p.y < miny || p.y >= maxy ) { + if( p.x < min.x || p.x >= max.x || p.y < min.y || p.y >= max.y ) { continue; } @@ -351,7 +345,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, } else if( part >= 0 && bash > 0 ) { // Car obstacle that isn't a door // TODO: Account for armor - int hp = veh->cpart( part ).hp(); + int hp = veh->part( part ).hp(); if( hp / 20 > bash ) { // Threshold damage thing means we just can't bash this down layer.state[index] = ASL_CLOSED; @@ -438,7 +432,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, const maptile &parent_tile = maptile_at_internal( cur ); const auto &parent_terrain = parent_tile.get_ter_t(); - if( settings.allow_climb_stairs && cur.z > minz && parent_terrain.has_flag( TFLAG_GOES_DOWN ) ) { + if( settings.allow_climb_stairs && cur.z > min.z && parent_terrain.has_flag( TFLAG_GOES_DOWN ) ) { tripoint dest( cur.xy(), cur.z - 1 ); if( vertical_move_destination( *this, dest ) ) { auto &layer = pf.get_layer( dest.z ); @@ -447,7 +441,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, cur, dest ); } } - if( settings.allow_climb_stairs && cur.z < maxz && parent_terrain.has_flag( TFLAG_GOES_UP ) ) { + if( settings.allow_climb_stairs && cur.z < max.z && parent_terrain.has_flag( TFLAG_GOES_UP ) ) { tripoint dest( cur.xy(), cur.z + 1 ); if( vertical_move_destination( *this, dest ) ) { auto &layer = pf.get_layer( dest.z ); @@ -456,7 +450,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, cur, dest ); } } - if( cur.z < maxz && parent_terrain.has_flag( TFLAG_RAMP ) && + if( cur.z < max.z && parent_terrain.has_flag( TFLAG_RAMP ) && valid_move( cur, tripoint( cur.xy(), cur.z + 1 ), false, true ) ) { auto &layer = pf.get_layer( cur.z + 1 ); for( size_t it = 0; it < 8; it++ ) { @@ -466,7 +460,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, cur, above ); } } - if( cur.z < maxz && parent_terrain.has_flag( TFLAG_RAMP_UP ) && + if( cur.z < max.z && 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++ ) { @@ -476,7 +470,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, cur, above ); } } - if( cur.z > minz && parent_terrain.has_flag( TFLAG_RAMP_DOWN ) && + if( cur.z > min.z && 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++ ) { diff --git a/src/pickup.cpp b/src/pickup.cpp index f283ceb1cc00f..cbcd2695e38e5 100644 --- a/src/pickup.cpp +++ b/src/pickup.cpp @@ -711,7 +711,15 @@ void Pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) if( cur_it < static_cast( matches.size() ) ) { int true_it = matches[cur_it]; const item &this_item = *stacked_here[true_it].front(); - nc_color icolor = this_item.color_in_inventory(); + + nc_color icolor; + if( this_item.is_food_container() && !this_item.is_craft() && + this_item.contents.num_item_stacks() == 1 ) { + icolor = this_item.contents.all_items_top().front()->color_in_inventory(); + } else { + icolor = this_item.color_in_inventory(); + } + if( cur_it == selected ) { icolor = hilite( c_white ); } diff --git a/src/pixel_minimap.cpp b/src/pixel_minimap.cpp index 1bd963238bff2..8a4968e39b1fa 100644 --- a/src/pixel_minimap.cpp +++ b/src/pixel_minimap.cpp @@ -334,7 +334,7 @@ void pixel_minimap::update_cache_at( const tripoint &sm_pos ) if( current_color != color ) { current_color = color; - cache_item.update_list.push_back( { x, y } ); + cache_item.update_list.emplace_back( x, y ); } } } @@ -496,8 +496,7 @@ void pixel_minimap::render_critters( const tripoint ¢er ) const level_cache &access_cache = get_map().access_cache( center.z ); - const int start_x = center.x - total_tiles_count.x / 2; - const int start_y = center.y - total_tiles_count.y / 2; + const point start( center.x - total_tiles_count.x / 2, center.y - total_tiles_count.y / 2 ); const point beacon_size = { std::max( projector->get_tile_size().x *settings.beacon_size / 2, 2 ), std::max( projector->get_tile_size().y *settings.beacon_size / 2, 2 ) @@ -505,7 +504,7 @@ void pixel_minimap::render_critters( const tripoint ¢er ) for( int y = 0; y < total_tiles_count.y; y++ ) { for( int x = 0; x < total_tiles_count.x; x++ ) { - const tripoint p = tripoint{ start_x + x, start_y + y, center.z }; + const tripoint p = start + tripoint( x, y, center.z ); const lit_level lighting = access_cache.visibility_cache[p.x][p.y]; if( lighting == lit_level::DARK || lighting == lit_level::BLANK ) { diff --git a/src/player.cpp b/src/player.cpp index f40ef895da110..e6c43ce3571ce 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -139,6 +139,8 @@ static const bionic_id bio_ground_sonar( "bio_ground_sonar" ); static const bionic_id bio_soporific( "bio_soporific" ); static const bionic_id bio_speed( "bio_speed" ); +static const json_character_flag json_flag_FEATHER_FALL( "FEATHER_FALL" ); + stat_mod player::get_pain_penalty() const { stat_mod ret; @@ -224,8 +226,8 @@ player::player() } player::~player() = default; -player::player( player && ) = default; -player &player::operator=( player && ) = default; +player::player( player && ) noexcept( map_is_noexcept ) = default; +player &player::operator=( player && ) noexcept( list_is_noexcept ) = default; void player::normalize() { @@ -833,7 +835,7 @@ int player::get_perceived_pain() const float player::fall_damage_mod() const { - if( has_effect_with_flag( flag_EFFECT_FEATHER_FALL ) ) { + if( has_flag( json_flag_FEATHER_FALL ) ) { return 0.0f; } float ret = 1.0f; @@ -1880,6 +1882,20 @@ player::wear( item_location item_wear, bool interactive ) was_weapon = false; } + const bool item_one_per_layer = to_wear_copy.has_flag( flag_id( "ONE_PER_LAYER" ) ); + for( const item &worn_item : worn ) { + const cata::optional sidedness_conflict = to_wear_copy.covers_overlaps( worn_item ); + if( sidedness_conflict && ( item_one_per_layer || + worn_item.has_flag( flag_id( "ONE_PER_LAYER" ) ) ) ) { + // we can assume both isn't an option because it'll be caught in can_wear + if( *sidedness_conflict == side::LEFT ) { + to_wear_copy.set_side( side::RIGHT ); + } else { + to_wear_copy.set_side( side::LEFT ); + } + } + } + auto result = wear_item( to_wear_copy, interactive ); if( !result ) { if( was_weapon ) { @@ -2236,8 +2252,8 @@ void player::gunmod_add( item &gun, item &mod ) const int moves = !has_trait( trait_DEBUG_HS ) ? mod.type->gunmod->install_time : 0; assign_activity( activity_id( "ACT_GUNMOD_ADD" ), moves, -1, 0, tool ); - activity.targets.push_back( item_location( *this, &gun ) ); - activity.targets.push_back( item_location( *this, &mod ) ); + activity.targets.emplace_back( *this, &gun ); + activity.targets.emplace_back( *this, &mod ); activity.values.push_back( 0 ); // dummy value activity.values.push_back( roll ); // chance of success (%) activity.values.push_back( risk ); // chance of damage (%) @@ -2539,6 +2555,11 @@ void player::add_msg_if_player( const game_message_params ¶ms, const std::st Messages::add_msg( params, msg ); } +void player::add_msg_debug_if_player( debugmode::debug_filter type, const std::string &msg ) const +{ + Messages::add_msg_debug( type, msg ); +} + void player::add_msg_player_or_npc( const game_message_params ¶ms, const std::string &player_msg, const std::string &/*npc_msg*/ ) const @@ -2546,6 +2567,13 @@ void player::add_msg_player_or_npc( const game_message_params ¶ms, Messages::add_msg( params, player_msg ); } +void player::add_msg_debug_player_or_npc( debugmode::debug_filter type, + const std::string &player_msg, + const std::string &/*npc_msg*/ ) const +{ + Messages::add_msg_debug( type, player_msg ); +} + void player::add_msg_player_or_say( const std::string &player_msg, const std::string &/*npc_speech*/ ) const { diff --git a/src/player.h b/src/player.h index bd78c984cbe31..8aeb88bb70c44 100644 --- a/src/player.h +++ b/src/player.h @@ -79,10 +79,10 @@ class player : public Character public: player(); player( const player & ) = delete; - player( player && ); + player( player && ) noexcept( map_is_noexcept ); ~player() override; player &operator=( const player & ) = delete; - player &operator=( player && ); + player &operator=( player && ) noexcept( list_is_noexcept ); /** Calls Character::normalize() * normalizes HP and body temperature @@ -365,11 +365,17 @@ class player : public Character using Character::add_msg_if_player; void add_msg_if_player( const std::string &msg ) const override; void add_msg_if_player( const game_message_params ¶ms, const std::string &msg ) const override; + using Character::add_msg_debug_if_player; + void add_msg_debug_if_player( debugmode::debug_filter type, + const std::string &msg ) const override; using Character::add_msg_player_or_npc; void add_msg_player_or_npc( const std::string &player_msg, const std::string &npc_str ) const override; void add_msg_player_or_npc( const game_message_params ¶ms, const std::string &player_msg, const std::string &npc_msg ) const override; + using Character::add_msg_debug_player_or_npc; + void add_msg_debug_player_or_npc( debugmode::debug_filter type, const std::string &player_msg, + const std::string &npc_msg ) const override; using Character::add_msg_player_or_say; void add_msg_player_or_say( const std::string &player_msg, const std::string &npc_speech ) const override; diff --git a/src/player_activity.cpp b/src/player_activity.cpp index f0417d4e5250f..e7e4a877c8b76 100644 --- a/src/player_activity.cpp +++ b/src/player_activity.cpp @@ -64,11 +64,11 @@ void player_activity::migrate_item_position( Character &guy ) type == ACT_ATM; if( simple_action_replace ) { - targets.push_back( item_location( guy, &guy.i_at( position ) ) ); + targets.emplace_back( guy, &guy.i_at( position ) ); } else if( type == ACT_GUNMOD_ADD ) { // this activity has two indices; "position" = gun and "values[0]" = mod - targets.push_back( item_location( guy, &guy.i_at( position ) ) ); - targets.push_back( item_location( guy, &guy.i_at( values[0] ) ) ); + targets.emplace_back( guy, &guy.i_at( position ) ); + targets.emplace_back( guy, &guy.i_at( values[0] ) ); } } diff --git a/src/player_activity.h b/src/player_activity.h index 82b2e5d9d21f9..d9811ef6d3dda 100644 --- a/src/player_activity.h +++ b/src/player_activity.h @@ -87,7 +87,7 @@ class player_activity player_activity( player_activity && ) noexcept = default; player_activity( const player_activity & ) = default; - player_activity &operator=( player_activity && ) = default; + player_activity &operator=( player_activity && ) noexcept = default; player_activity &operator=( const player_activity & ) = default; explicit operator bool() const { diff --git a/src/player_display.cpp b/src/player_display.cpp index 835f085759dd7..cc403e8e0c21b 100644 --- a/src/player_display.cpp +++ b/src/player_display.cpp @@ -1192,7 +1192,7 @@ void player::disp_info() if( tmp.empty() ) { continue; } - effect_name_and_text.push_back( { tmp, _effect_it.second.disp_desc() } ); + effect_name_and_text.emplace_back( tmp, _effect_it.second.disp_desc() ); } } if( get_perceived_pain() > 0 ) { @@ -1208,7 +1208,7 @@ void player::disp_info() add_if( ppen.intelligence, _( "Intelligence -%d" ) ); add_if( ppen.perception, _( "Perception -%d" ) ); add_if( ppen.speed, _( "Speed -%d %%" ) ); - effect_name_and_text.push_back( { _( "Pain" ), pain_text } ); + effect_name_and_text.emplace_back( _( "Pain" ), pain_text ); } const float bmi = get_bmi(); @@ -1237,30 +1237,30 @@ void player::disp_info() str_penalty * 50.0f ); } - effect_name_and_text.push_back( { starvation_name, starvation_text } ); + effect_name_and_text.emplace_back( starvation_name, starvation_text ); } 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" ) - } ); + effect_name_and_text.emplace_back( _( "In Sunlight" ), + _( "The sunlight irritates you.\n" + "Strength - 1; Dexterity - 1; Intelligence - 1; Perception - 1" ) + ); } else if( has_trait( trait_id( "TROGLO2" ) ) && g->is_in_sunlight( pos() ) ) { - effect_name_and_text.push_back( { _( "In Sunlight" ), - _( "The sunlight irritates you badly.\n" - "Strength - 2; Dexterity - 2; Intelligence - 2; Perception - 2" ) - } ); + effect_name_and_text.emplace_back( _( "In Sunlight" ), + _( "The sunlight irritates you badly.\n" + "Strength - 2; Dexterity - 2; Intelligence - 2; Perception - 2" ) + ); } else if( has_trait( trait_id( "TROGLO3" ) ) && g->is_in_sunlight( pos() ) ) { - effect_name_and_text.push_back( { _( "In Sunlight" ), - _( "The sunlight irritates you terribly.\n" - "Strength - 4; Dexterity - 4; Intelligence - 4; Perception - 4" ) - } ); + effect_name_and_text.emplace_back( _( "In Sunlight" ), + _( "The sunlight irritates you terribly.\n" + "Strength - 4; Dexterity - 4; Intelligence - 4; Perception - 4" ) + ); } for( auto &elem : addictions ) { if( elem.sated < 0_turns && elem.intensity >= MIN_ADDICTION_LEVEL ) { - effect_name_and_text.push_back( { addiction_name( elem ), addiction_text( elem ) } ); + effect_name_and_text.emplace_back( addiction_name( elem ), addiction_text( elem ) ); } } diff --git a/src/player_hardcoded_effects.cpp b/src/player_hardcoded_effects.cpp index 9ae508df29920..cc46a9a53e9ac 100644 --- a/src/player_hardcoded_effects.cpp +++ b/src/player_hardcoded_effects.cpp @@ -491,6 +491,649 @@ static void eff_fun_frostbite( Character &u, effect &it ) } } +static void eff_fun_teleglow( Character &u, effect &it ) +{ + // Default we get around 300 duration points per teleport (possibly more + // depending on the source). + // TODO: Include a chance to teleport to the nether realm. + // TODO: This with regards to NPCS + if( !u.is_player() ) { + // NO, no teleporting around the player because an NPC has teleglow! + return; + } + const time_duration dur = it.get_duration(); + Character &player_character = get_player_character(); + map &here = get_map(); + if( dur > 10_hours ) { + // 20 teleports (no decay; in practice at least 21) + if( one_in( 6000 - ( ( dur - 600_minutes ) / 1_minutes ) ) ) { + if( !u.is_npc() ) { + add_msg( _( "Glowing lights surround you, and you teleport." ) ); + } + teleport::teleport( u ); + get_event_bus().send( u.getID() ); + if( one_in( 10 ) ) { + // Set ourselves up for removal + it.set_duration( 0_turns ); + } + } + } + if( one_in( 7200 - ( dur - 360_minutes ) / 4_turns ) ) { + //Spawn a tindalos rift via effect_tindrift rather than it being hard-coded to teleglow + u.add_effect( effect_tindrift, 5_turns ); + + if( one_in( 2 ) ) { + // Set ourselves up for removal + it.set_duration( 0_turns ); + } + } + if( one_in( 7200 - ( ( dur - 600_minutes ) / 30_seconds ) ) && one_in( 20 ) ) { + u.add_msg_if_player( m_bad, _( "You pass out." ) ); + u.fall_asleep( 2_hours ); + if( one_in( 6 ) ) { + // Set ourselves up for removal + it.set_duration( 0_turns ); + } + } + if( dur > 6_hours ) { + // 12 teleports + if( one_in( 24000 - ( dur - 360_minutes ) / 4_turns ) ) { + tripoint dest( 0, 0, u.posz() ); + int &x = dest.x; + int &y = dest.y; + int tries = 0; + do { + x = u.posx() + rng( -4, 4 ); + y = u.posy() + rng( -4, 4 ); + tries++; + if( tries >= 10 ) { + break; + } + } while( g->critter_at( dest ) ); + if( tries < 10 ) { + 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( 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!" ) ); + } + if( one_in( 2 ) ) { + // Set ourselves up for removal + it.set_duration( 0_turns ); + } + } + } + if( one_in( 21000 - ( dur - 360_minutes ) / 4_turns ) ) { + u.add_msg_if_player( m_bad, _( "You shudder suddenly." ) ); + u.mutate(); + if( one_in( 4 ) ) { + // Set ourselves up for removal + it.set_duration( 0_turns ); + } + } + } + if( dur > 4_hours ) { + // 8 teleports + if( one_turn_in( 1000_minutes - dur ) && !u.has_effect( effect_valium ) ) { + u.add_effect( effect_shakes, rng( 4_minutes, 8_minutes ) ); + } + if( one_turn_in( 1200_minutes - dur ) ) { + u.add_msg_if_player( m_bad, _( "Your vision is filled with bright lights…" ) ); + u.add_effect( effect_blind, rng( 1_minutes, 2_minutes ) ); + if( one_in( 8 ) ) { + // Set ourselves up for removal + it.set_duration( 0_turns ); + } + } + if( one_in( 5000 ) && !u.has_effect( effect_hallu ) ) { + u.add_effect( effect_hallu, 6_hours ); + if( one_in( 5 ) ) { + // Set ourselves up for removal + it.set_duration( 0_turns ); + } + } + } + if( one_in( 4000 ) ) { + u.add_msg_if_player( m_bad, _( "You're suddenly covered in ectoplasm." ) ); + u.add_effect( effect_boomered, 10_minutes ); + if( one_in( 4 ) ) { + // Set ourselves up for removal + it.set_duration( 0_turns ); + } + } + if( one_in( 10000 ) ) { + if( !u.has_trait( trait_M_IMMUNE ) ) { + u.add_effect( effect_fungus, 1_turns, true ); + } else { + u.add_msg_if_player( m_info, _( "We have many colonists awaiting passage." ) ); + } + // Set ourselves up for removal + it.set_duration( 0_turns ); + } +} + +static void eff_fun_datura( Character &u, effect &it ) +{ + const time_duration dur = it.get_duration(); + if( dur > 100_minutes && u.get_focus() >= 1 && one_in( 24 ) ) { + u.mod_focus( -1 ); + } + if( dur > 200_minutes && one_in( 48 ) && u.get_stim() < 20 ) { + u.mod_stim( 1 ); + } + if( dur > 300_minutes && u.get_focus() >= 1 && one_in( 12 ) ) { + u.mod_focus( -1 ); + } + if( dur > 400_minutes && one_in( 384 ) ) { + u.mod_pain( rng( -1, -8 ) ); + } + if( ( !u.has_effect( effect_hallu ) ) && ( dur > 500_minutes && one_in( 24 ) ) ) { + u.add_effect( effect_hallu, 6_hours ); + } + if( dur > 600_minutes && one_in( 768 ) ) { + u.mod_pain( rng( -3, -24 ) ); + if( dur > 800_minutes && one_in( 16 ) ) { + u.add_msg_if_player( m_bad, + _( "You're experiencing loss of basic motor skills and blurred vision. Your mind recoils in horror, unable to communicate with your spinal column." ) ); + u.add_msg_if_player( m_bad, _( "You stagger and fall!" ) ); + u.add_effect( effect_downed, rng( 1_turns, 4_turns ), false, 0, true ); + if( one_in( 8 ) || x_in_y( u.vomit_mod(), 10 ) ) { + u.vomit(); + } + } + } + if( dur > 700_minutes && u.get_focus() >= 1 ) { + u.mod_focus( -1 ); + } + if( dur > 800_minutes && one_in( 1536 ) ) { + u.add_effect( effect_visuals, rng( 4_minutes, 20_minutes ) ); + u.mod_pain( rng( -8, -40 ) ); + } + if( dur > 1200_minutes && one_in( 1536 ) ) { + u.add_msg_if_player( m_bad, _( "There's some kind of big machine in the sky." ) ); + u.add_effect( effect_visuals, rng( 8_minutes, 40_minutes ) ); + if( one_in( 32 ) ) { + u.add_msg_if_player( m_bad, _( "It's some kind of electric snake, coming right at you!" ) ); + u.mod_pain( rng( 4, 40 ) ); + u.vomit(); + } + } + if( dur > 1400_minutes && one_in( 768 ) ) { + u.add_msg_if_player( m_bad, + _( "Order us some golf shoes, otherwise we'll never get out of this place alive." ) ); + u.add_effect( effect_visuals, rng( 40_minutes, 200_minutes ) ); + if( one_in( 8 ) ) { + u.add_msg_if_player( m_bad, + _( "The possibility of physical and mental collapse is now very real." ) ); + if( one_in( 2 ) || x_in_y( u.vomit_mod(), 10 ) ) { + u.add_msg_if_player( m_bad, _( "No one should be asked to handle this trip." ) ); + u.vomit(); + u.mod_pain( rng( 8, 40 ) ); + } + } + } + + if( dur > 1800_minutes && one_in( 300 * 512 ) ) { + if( !u.has_trait( trait_NOPAIN ) ) { + u.add_msg_if_player( m_bad, + _( "Your heart spasms painfully and stops, dragging you back to reality as you die." ) ); + } else { + u.add_msg_if_player( + _( "You dissolve into beautiful paroxysms of energy. Life fades from your nebulae and you are no more." ) ); + } + get_event_bus().send( u.getID(), it.get_id() ); + u.set_part_hp_cur( bodypart_id( "torso" ), 0 ); + } +} + +static void eff_fun_hypovolemia( Character &u, effect &it ) +{ + // hypovolemia and dehydration are closely related so it will pull water + // from your system to replenish blood quantity + int intense = it.get_intensity(); + + if( calendar::once_every( -u.vitamin_rate( vitamin_blood ) ) && one_in( 5 ) && + u.get_thirst() <= 240 ) { + u.mod_thirst( rng( 0, intense ) ); + } + // bleed out lambda + auto bleed_out = [&] { + if( u.has_effect( effect_bleed ) ) + { + u.add_msg_player_or_npc( m_bad, + _( "You bleed to death!" ), + _( " bleeds to death!" ) ); + get_event_bus().send( u.getID() ); + } else + { + u.add_msg_player_or_npc( m_bad, + _( "Your heart can't keep up the pace and fails!" ), + _( " has a sudden heart attack!" ) ); + get_event_bus().send( u.getID() ); + } + u.set_part_hp_cur( bodypart_id( "torso" ), 0 ); + }; + // this goes first because beyond minimum threshold you just die without delay, + // while stage 4 is on a timer check with an rng grace period + + if( u.vitamin_get( vitamin_blood ) == vitamin_blood->min() ) { + bleed_out(); + } + + // Hypovolemic shock + // stage 1 - early symptoms include headache, fatigue, weakness, thirst, and dizziness. + // stage 2 - person may begin sweating and feeling more anxious and restless. + // stage 3 - heart rate will increase to over 120 bpm; rapid breathing + // mental distress, including anxiety and agitation; skin is pale and cold + cyanosis, sweating + // stage 4 is a life threatening condition; extremely rapid heart rate, breathing very fast and difficult + // drifting in and out of consciousness, sweating heavily, feeling cool to the touch, looking extremely pale + + if( one_in( 1200 / intense ) && !u.in_sleep_state() ) { + std::string warning; + + if( one_in( 5 ) ) { + // no-effect message block + if( intense == 1 ) { + warning = _( "Your skin looks pale and you feel anxious and thirsty. Blood loss?" ); + } else if( intense == 2 ) { + warning = _( "Your pale skin is sweating, your heart is beating fast, and you feel restless. Maybe you lost too much blood?" ); + } else if( intense == 3 ) { + warning = _( "You're unsettlingly white, but your fingertips are bluish. You are agitated and your heart is racing. Your blood loss must be serious." ); + } else { //intense == 4 + warning = _( "You are pale as a ghost, dripping wet from the sweat, and sluggish - despite your heart racing like a train. You are on the brink of collapse from the effects of blood loss." ); + } + u.add_msg_if_player( m_bad, warning ); + } else { + // effect dice, with progression of effects, 3 possible effects per tier + int dice_roll = rng( 0, 2 ) + intense; + switch( dice_roll ) { + case 1: + warning = _( "You feel dizzy and lightheaded." ); + u.add_effect( effect_stunned, rng( 5_seconds * intense, 2_minutes * intense ) ); + break; + case 2: + warning = _( "You feel tired and you breathe heavily." ); + u.mod_fatigue( 3 * intense ); + break; + case 3: + warning = _( "You are anxious and cannot collect your thoughts." ); + u.mod_focus( -rng( 1, u.get_focus() * intense / it.get_max_intensity() ) ); + break; + case 4: + warning = _( "You are sweating profusely, but you feel cold." ); + u.mod_part_temp_conv( bodypart_id( "hand_l" ), - 1000 * intense ); + u.mod_part_temp_conv( bodypart_id( "hand_r" ), -1000 * intense ); + u.mod_part_temp_conv( bodypart_id( "foot_l" ), -1000 * intense ); + u.mod_part_temp_conv( bodypart_id( "foot_r" ), -1000 * intense ); + break; + case 5: + warning = _( "You huff and puff. Your breath is rapid and shallow." ); + u.mod_stamina( -500 * intense ); + break; + case 6: + if( one_in( 2 ) ) { + warning = _( "You drop to the ground, fighting to keep yourself conscious." ); + u.add_effect( effect_downed, rng( 1_minutes, 2_minutes ) ); + break; + } else { + warning = _( "Your mind slips away." ); + u.fall_asleep( rng( 2_minutes, 5_minutes ) ); + break; + } + } + u.add_msg_if_player( m_bad, warning ); + } + } + // this goes last because we don't want in_sleep_state to prevent you from dying + if( intense == 4 && one_in( 900 ) && + rng( 1, -vitamin_blood->min() * 3 / 5 ) > ( -vitamin_blood->min() + u.vitamin_get( + vitamin_blood ) ) ) { + bleed_out(); + } +} + +static void eff_fun_redcells_anemia( Character &u, effect &it ) +{ + // Lack of iron impairs production of hemoglobin and therefore ability to carry + // oxygen by red blood cells. Alternatively hemorrhage causes physical loss of red blood cells. + // This triggers variety of symptoms, focusing on weakness, + // fatigue, cold limbs, later in dizziness, soreness, breathlessness, + // and severe malaise and lethargy. + // Base anemia symptoms: fatigue, loss of stamina, loss of strength, impact on health + // are placed in effect JSON + + int intense = it.get_intensity(); + + // you can only lose as much red blood cells before your body fails to function + if( u.vitamin_get( vitamin_redcells ) <= vitamin_redcells->min() + 5 ) { + u.add_msg_player_or_npc( m_bad, + _( "You cannot breathe and your body gives out!" ), + _( " gasps for air and dies!" ) ); + get_event_bus().send( u.getID() ); + u.set_part_hp_cur( bodypart_id( "torso" ), 0 ); + } + if( one_in( 900 / intense ) && !u.in_sleep_state() ) { + // level 1 symptoms are cold limbs, pale skin, and weakness + switch( dice( 1, 9 ) ) { + case 1: + u.add_msg_if_player( m_bad, _( "Your hands feel unusually cold." ) ); + u.mod_part_temp_conv( bodypart_id( "hand_l" ), -2000 ); + u.mod_part_temp_conv( bodypart_id( "hand_r" ), -2000 ); + break; + case 2: + u.add_msg_if_player( m_bad, _( "Your feet feel unusually cold." ) ); + u.mod_part_temp_conv( bodypart_id( "foot_l" ), -2000 ); + u.mod_part_temp_conv( bodypart_id( "foot_r" ), -2000 ); + break; + case 3: + u.add_msg_if_player( m_bad, _( "Your skin looks very pale." ) ); + break; + case 4: + u.add_msg_if_player( m_bad, _( "You feel weak. Where has your strength gone?" ) ); + break; + case 5: + u.add_msg_if_player( m_bad, _( "You feel feeble. A gust of wind could make you stumble." ) ); + break; + case 6: + u.add_msg_if_player( m_bad, _( "There is an overwhelming aura of tiredness inside of you." ) ); + u.mod_fatigue( intense * 3 ); + break; + case 7: // 7-9 empty for variability, as messages stack on higher intensity + break; + case 8: + break; + case 9: + break; + } + // level 2 anemia introduces dizziness, shakes, headaches, cravings for non-comestibles, + // mouth and tongue soreness + if( intense > 1 ) { + switch( dice( 1, 9 ) ) { + case 1: + u.add_msg_if_player( m_bad, _( "Rest is what you want. Rest is what you need." ) ); + break; + case 2: + u.add_msg_if_player( m_bad, _( "You feel dizzy and can't coordinate the movement of your feet." ) ); + u.add_effect( effect_stunned, rng( 1_minutes, 2_minutes ) ); + break; + case 3: + u.add_msg_if_player( m_bad, _( "Your muscles are quivering." ) ); + u.add_effect( effect_shakes, rng( 4_minutes, 8_minutes ) ); + break; + case 4: + u.add_msg_if_player( m_bad, _( "You crave for ice. The dirt under your feet looks tasty too." ) ); + break; + case 5: + u.add_msg_if_player( m_bad, _( "Your whole mouth is sore, and your tongue is swollen." ) ); + break; + case 6: + u.add_msg_if_player( m_bad, _( "You feel lightheaded. A migraine follows." ) ); + u.mod_pain( intense * 9 ); + break; + case 7: // 7-9 empty for variability, as messages stack on higher intensity + break; + case 8: + break; + case 9: + break; + } + } + // level 3 anemia introduces restless legs, severe tiredness, breathlessness + if( intense > 2 ) { + switch( dice( 1, 9 ) ) { + case 1: + u.add_msg_if_player( m_bad, _( "Your legs are restless. The urge to move them is so strong." ) ); + break; + case 2: + u.add_msg_if_player( m_bad, _( "You feel like you could sleep on a rock." ) ); + u.mod_fatigue( intense * 3 ); + break; + case 3: + u.add_msg_if_player( m_bad, _( "You gasp for air!" ) ); + u.set_stamina( 0 ); + u.add_effect( effect_winded, rng( 30_seconds, 3_minutes ) ); + break; + case 4: + u.add_msg_if_player( m_bad, _( "Can't breathe. Must rest." ) ); + u.set_stamina( 0 ); + break; + case 5: + u.add_msg_if_player( m_bad, + _( "You can't take it any more. Rest first; everything else later." ) ); + u.add_effect( effect_lying_down, rng( 2_minutes, 5_minutes ) ); + break; + case 6: + u.add_msg_if_player( m_bad, _( "You must sit down for a moment. Just a moment." ) ); + u.add_effect( effect_downed, rng( 1_minutes, 2_minutes ) ); + break; + case 7: // 7-9 empty for variability, as messages stack on higher intensity + break; + case 8: + break; + case 9: + break; + } + } + } +} + +static void eff_fun_sleep( Character &u, effect &it ) +{ + int intense = it.get_intensity(); + map &here = get_map(); + + u.set_moves( 0 ); +#if defined(TILES) + if( u.is_player() ) { + SDL_PumpEvents(); + } +#endif // TILES + + if( intense < 1 ) { + it.set_intensity( 1 ); + } else if( intense < 24 ) { + it.mod_intensity( 1 ); + } + + if( u.has_effect( effect_narcosis ) && u.get_fatigue() <= 25 ) { + u.set_fatigue( 25 ); //Prevent us from waking up naturally while under anesthesia + } + + if( u.get_fatigue() < -25 && it.get_duration() > 3_minutes && !u.has_effect( effect_narcosis ) ) { + it.set_duration( 1_turns * dice( 3, 10 ) ); + } + + if( u.get_fatigue() <= 0 && u.get_fatigue() > -20 && !u.has_effect( effect_narcosis ) ) { + u.mod_fatigue( -25 ); + if( u.get_sleep_deprivation() < SLEEP_DEPRIVATION_HARMLESS ) { + u.add_msg_if_player( m_good, _( "You feel well rested." ) ); + } else { + u.add_msg_if_player( m_warning, + _( "You feel physically rested, but you haven't been able to catch up on your missed sleep yet." ) ); + } + it.set_duration( 1_turns * dice( 3, 100 ) ); + } + + // TODO: Move this to update_needs when NPCs can mutate + if( calendar::once_every( 10_minutes ) && ( u.has_trait( trait_CHLOROMORPH ) || + u.has_trait( trait_M_SKIN3 ) || u.has_trait( trait_WATERSLEEP ) ) && + here.is_outside( u.pos() ) ) { + if( u.has_trait( trait_CHLOROMORPH ) ) { + // Hunger and thirst fall before your Chloromorphic physiology! + if( g->natural_light_level( u.posz() ) >= 12 && + get_weather().weather_id->sun_intensity >= sun_intensity_type::light ) { + if( u.get_hunger() >= -30 ) { + u.mod_hunger( -5 ); + // photosynthesis warrants absorbing kcal directly + u.mod_stored_nutr( -5 ); + } + if( u.get_thirst() >= -30 ) { + u.mod_thirst( -5 ); + } + } + } + if( u.has_trait( trait_M_SKIN3 ) ) { + // Spores happen! + if( here.has_flag_ter_or_furn( "FUNGUS", u.pos() ) ) { + if( u.get_fatigue() >= 0 ) { + u.mod_fatigue( -5 ); // Local guides need less sleep on fungal soil + } + if( calendar::once_every( 1_hours ) ) { + u.spores(); // spawn some P O O F Y B O I S + } + } + } + if( u.has_trait( trait_WATERSLEEP ) ) { + u.mod_fatigue( -3 ); // Fish sleep less in water + } + } + + // Check mutation category strengths to see if we're mutated enough to get a dream + mutation_category_id highcat = u.get_highest_category(); + int highest = u.mutation_category_level[highcat]; + + // Determine the strength of effects or dreams based upon category strength + int strength = 0; // Category too weak for any effect or dream + if( u.crossed_threshold() ) { + strength = 4; // Post-human. + } else if( highest >= 20 && highest < 35 ) { + strength = 1; // Low strength + } else if( highest >= 35 && highest < 50 ) { + strength = 2; // Medium strength + } else if( highest >= 50 ) { + strength = 3; // High strength + } + + // Get a dream if category strength is high enough. + if( strength != 0 ) { + //Once every 6 / 3 / 2 hours, with a bit of randomness + if( calendar::once_every( 6_hours / strength ) && one_in( 3 ) ) { + // Select a dream + std::string dream = u.get_category_dream( highcat, strength ); + if( !dream.empty() ) { + u.add_msg_if_player( dream ); + } + // Mycus folks upgrade in their sleep. + if( u.has_trait( trait_THRESH_MYCUS ) ) { + if( one_in( 8 ) ) { + u.mutate_category( mutation_category_id( "MYCUS" ) ); + u.mod_stored_nutr( 10 ); + u.mod_thirst( 10 ); + u.mod_fatigue( 5 ); + } + } + } + } + + bool woke_up = false; + int tirednessVal = rng( 5, 200 ) + rng( 0, std::abs( u.get_fatigue() * 2 * 5 ) ); + if( !u.is_blind() && !u.has_effect( effect_narcosis ) ) { + // People who can see while sleeping are acclimated to the light. + if( !u.has_trait( trait_SEESLEEP ) ) { + if( u.has_trait( trait_HEAVYSLEEPER2 ) && !u.has_trait( trait_HIBERNATE ) ) { + // So you can too sleep through noon + if( ( tirednessVal * 1.25 ) < here.ambient_light_at( u.pos() ) && ( u.get_fatigue() < 10 || + one_in( u.get_fatigue() / 2 ) ) ) { + u.add_msg_if_player( _( "It's too bright to sleep." ) ); + // Set ourselves up for removal + it.set_duration( 0_turns ); + woke_up = true; + } + // Ursine hibernators would likely do so indoors. Plants, though, might be in the sun. + } else if( u.has_trait( trait_HIBERNATE ) ) { + if( ( tirednessVal * 5 ) < here.ambient_light_at( u.pos() ) && ( u.get_fatigue() < 10 || + one_in( u.get_fatigue() / 2 ) ) ) { + u.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 < here.ambient_light_at( u.pos() ) && ( u.get_fatigue() < 10 || + one_in( u.get_fatigue() / 2 ) ) ) { + u.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( u.has_active_mutation( trait_SEESLEEP ) ) { + Creature *hostile_critter = g->is_hostile_very_close(); + if( hostile_critter != nullptr ) { + u.add_msg_if_player( _( "You see %s approaching!" ), + hostile_critter->disp_name() ); + it.set_duration( 0_turns ); + woke_up = true; + } + } + } + + // Have we already woken up? + if( !woke_up && !u.has_effect( effect_narcosis ) ) { + // Cold or heat may wake you up. + // Player will sleep through cold or heat if fatigued enough + for( const bodypart_id &bp : u.get_all_body_parts() ) { + const int curr_temp = u.get_part_temp_cur( bp ); + if( curr_temp < BODYTEMP_VERY_COLD - u.get_fatigue() / 2 ) { + if( one_in( 30000 ) ) { + u.add_msg_if_player( _( "You toss and turn trying to keep warm." ) ); + } + if( curr_temp < BODYTEMP_FREEZING - u.get_fatigue() / 2 || + one_in( curr_temp * 6 + 30000 ) ) { + u.add_msg_if_player( m_bad, _( "It's too cold to sleep." ) ); + // Set ourselves up for removal + it.set_duration( 0_turns ); + woke_up = true; + break; + } + } else if( curr_temp > BODYTEMP_VERY_HOT + u.get_fatigue() / 2 ) { + if( one_in( 30000 ) ) { + u.add_msg_if_player( _( "You toss and turn in the heat." ) ); + } + if( curr_temp > BODYTEMP_SCORCHING + u.get_fatigue() / 2 || + one_in( 90000 - curr_temp ) ) { + u.add_msg_if_player( m_bad, _( "It's too hot to sleep." ) ); + // Set ourselves up for removal + it.set_duration( 0_turns ); + woke_up = true; + break; + } + } + } + if( u.has_trait( trait_SCHIZOPHRENIC ) && one_in( 43200 ) && u.is_player() ) { + if( one_in( 2 ) ) { + u.sound_hallu(); + } else { + int max_count = rng( 1, 3 ); + int count = 0; + for( const tripoint &mp : here.points_in_radius( u.pos(), 1 ) ) { + if( mp == u.pos() ) { + continue; + } + if( here.has_flag( "FLAT", mp ) && + here.pl_sees( mp, 2 ) ) { + g->spawn_hallucination( mp ); + if( ++count > max_count ) { + break; + } + } + } + } + it.set_duration( 0_turns ); + woke_up = true; + } + } + + // A bit of a hack: check if we are about to wake up for any reason, including regular + // timing out of sleep + if( it.get_duration() == 1_turns || woke_up ) { + u.wake_up(); + } +} + void Character::hardcoded_effects( effect &it ) { if( const ma_buff *buff = ma_buff::from_effect( it ) ) { @@ -513,6 +1156,11 @@ void Character::hardcoded_effects( effect &it ) { effect_cold, eff_fun_cold }, { effect_hot, eff_fun_hot }, { effect_frostbite, eff_fun_frostbite }, + { effect_teleglow, eff_fun_teleglow }, + { effect_datura, eff_fun_datura }, + { effect_hypovolemia, eff_fun_hypovolemia }, + { effect_redcells_anemia, eff_fun_redcells_anemia }, + { effect_sleep, eff_fun_sleep }, } }; const efftype_id &id = it.get_id(); @@ -651,126 +1299,6 @@ void Character::hardcoded_effects( effect &it ) remove_effect( effect_tindrift ); } } - } else if( id == effect_teleglow ) { - // Default we get around 300 duration points per teleport (possibly more - // depending on the source). - // TODO: Include a chance to teleport to the nether realm. - // TODO: This with regards to NPCS - if( !is_player() ) { - // NO, no teleporting around the player because an NPC has teleglow! - return; - } - if( dur > 10_hours ) { - // 20 teleports (no decay; in practice at least 21) - if( one_in( 6000 - ( ( dur - 600_minutes ) / 1_minutes ) ) ) { - if( !is_npc() ) { - add_msg( _( "Glowing lights surround you, and you teleport." ) ); - } - teleport::teleport( *this ); - get_event_bus().send( getID() ); - if( one_in( 10 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); - } - } - } - if( one_in( 7200 - ( dur - 360_minutes ) / 4_turns ) ) { - //Spawn a tindalos rift via effect_tindrift rather than it being hard-coded to teleglow - add_effect( effect_tindrift, 5_turns ); - - if( one_in( 2 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); - } - } - if( one_in( 7200 - ( ( dur - 600_minutes ) / 30_seconds ) ) && one_in( 20 ) ) { - add_msg_if_player( m_bad, _( "You pass out." ) ); - fall_asleep( 2_hours ); - if( one_in( 6 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); - } - } - if( dur > 6_hours ) { - // 12 teleports - if( one_in( 24000 - ( dur - 360_minutes ) / 4_turns ) ) { - tripoint dest( 0, 0, posz() ); - int &x = dest.x; - int &y = dest.y; - int tries = 0; - do { - x = posx() + rng( -4, 4 ); - y = posy() + rng( -4, 4 ); - tries++; - if( tries >= 10 ) { - break; - } - } while( g->critter_at( dest ) ); - if( tries < 10 ) { - 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( 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!" ) ); - } - if( one_in( 2 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); - } - } - } - if( one_in( 21000 - ( dur - 360_minutes ) / 4_turns ) ) { - add_msg_if_player( m_bad, _( "You shudder suddenly." ) ); - mutate(); - if( one_in( 4 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); - } - } - } - if( dur > 4_hours ) { - // 8 teleports - if( one_turn_in( 1000_minutes - dur ) && !has_effect( effect_valium ) ) { - add_effect( effect_shakes, rng( 4_minutes, 8_minutes ) ); - } - if( one_turn_in( 1200_minutes - dur ) ) { - add_msg_if_player( m_bad, _( "Your vision is filled with bright lights…" ) ); - add_effect( effect_blind, rng( 1_minutes, 2_minutes ) ); - if( one_in( 8 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); - } - } - if( one_in( 5000 ) && !has_effect( effect_hallu ) ) { - add_effect( effect_hallu, 6_hours ); - if( one_in( 5 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); - } - } - } - if( one_in( 4000 ) ) { - add_msg_if_player( m_bad, _( "You're suddenly covered in ectoplasm." ) ); - add_effect( effect_boomered, 10_minutes ); - if( one_in( 4 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); - } - } - if( one_in( 10000 ) ) { - if( !has_trait( trait_M_IMMUNE ) ) { - add_effect( effect_fungus, 1_turns, true ); - } else { - add_msg_if_player( m_info, _( "We have many colonists awaiting passage." ) ); - } - // Set ourselves up for removal - it.set_duration( 0_turns ); - } } else if( id == effect_asthma ) { if( has_effect( effect_adrenaline ) || has_effect( effect_datura ) ) { add_msg_if_player( m_good, _( "Your asthma attack stops." ) ); @@ -831,300 +1359,11 @@ void Character::hardcoded_effects( effect &it ) } } } - } else if( id == effect_datura ) { - if( dur > 100_minutes && get_focus() >= 1 && one_in( 24 ) ) { - mod_focus( -1 ); - } - if( dur > 200_minutes && one_in( 48 ) && get_stim() < 20 ) { - mod_stim( 1 ); - } - if( dur > 300_minutes && get_focus() >= 1 && one_in( 12 ) ) { - mod_focus( -1 ); - } - if( dur > 400_minutes && one_in( 384 ) ) { - mod_pain( rng( -1, -8 ) ); - } - if( ( !has_effect( effect_hallu ) ) && ( dur > 500_minutes && one_in( 24 ) ) ) { - add_effect( effect_hallu, 6_hours ); - } - if( dur > 600_minutes && one_in( 768 ) ) { - mod_pain( rng( -3, -24 ) ); - if( dur > 800_minutes && one_in( 16 ) ) { - add_msg_if_player( m_bad, - _( "You're experiencing loss of basic motor skills and blurred vision. Your mind recoils in horror, unable to communicate with your spinal column." ) ); - add_msg_if_player( m_bad, _( "You stagger and fall!" ) ); - add_effect( effect_downed, rng( 1_turns, 4_turns ), false, 0, true ); - if( one_in( 8 ) || x_in_y( vomit_mod(), 10 ) ) { - vomit(); - } - } - } - if( dur > 700_minutes && get_focus() >= 1 ) { - mod_focus( -1 ); - } - if( dur > 800_minutes && one_in( 1536 ) ) { - add_effect( effect_visuals, rng( 4_minutes, 20_minutes ) ); - mod_pain( rng( -8, -40 ) ); - } - if( dur > 1200_minutes && one_in( 1536 ) ) { - add_msg_if_player( m_bad, _( "There's some kind of big machine in the sky." ) ); - add_effect( effect_visuals, rng( 8_minutes, 40_minutes ) ); - if( one_in( 32 ) ) { - add_msg_if_player( m_bad, _( "It's some kind of electric snake, coming right at you!" ) ); - mod_pain( rng( 4, 40 ) ); - vomit(); - } - } - if( dur > 1400_minutes && one_in( 768 ) ) { - add_msg_if_player( m_bad, - _( "Order us some golf shoes, otherwise we'll never get out of this place alive." ) ); - add_effect( effect_visuals, rng( 40_minutes, 200_minutes ) ); - if( one_in( 8 ) ) { - add_msg_if_player( m_bad, - _( "The possibility of physical and mental collapse is now very real." ) ); - if( one_in( 2 ) || x_in_y( vomit_mod(), 10 ) ) { - add_msg_if_player( m_bad, _( "No one should be asked to handle this trip." ) ); - vomit(); - mod_pain( rng( 8, 40 ) ); - } - } - } - - if( dur > 1800_minutes && one_in( 300 * 512 ) ) { - if( !has_trait( trait_NOPAIN ) ) { - add_msg_if_player( m_bad, - _( "Your heart spasms painfully and stops, dragging you back to reality as you die." ) ); - } else { - add_msg_if_player( - _( "You dissolve into beautiful paroxysms of energy. Life fades from your nebulae and you are no more." ) ); - } - get_event_bus().send( getID(), id ); - set_part_hp_cur( bodypart_id( "torso" ), 0 ); - } - } else if( id == effect_hypovolemia ) { - // hypovolemia and dehydration are closely related so it will pull water - // from your system to replenish blood quantity - if( calendar::once_every( -vitamin_rate( vitamin_blood ) ) && one_in( 5 ) && get_thirst() <= 240 ) { - mod_thirst( rng( 0, intense ) ); - } - // bleed out lambda - auto bleed_out = [&] { - if( has_effect( effect_bleed ) ) - { - add_msg_player_or_npc( m_bad, - _( "You bleed to death!" ), - _( " bleeds to death!" ) ); - get_event_bus().send( getID() ); - } else - { - add_msg_player_or_npc( m_bad, - _( "Your heart can't keep up the pace and fails!" ), - _( " has a sudden heart attack!" ) ); - get_event_bus().send( getID() ); - } - set_part_hp_cur( bodypart_id( "torso" ), 0 ); - }; - // this goes first because beyond minimum threshold you just die without delay, - // while stage 4 is on a timer check with an rng grace period - - if( vitamin_get( vitamin_blood ) == vitamin_blood->min() ) { - bleed_out(); - } - - // Hypovolemic shock - // stage 1 - early symptoms include headache, fatigue, weakness, thirst, and dizziness. - // stage 2 - person may begin sweating and feeling more anxious and restless. - // stage 3 - heart rate will increase to over 120 bpm; rapid breathing - // mental distress, including anxiety and agitation; skin is pale and cold + cyanosis, sweating - // stage 4 is a life threatening condition; extremely rapid heart rate, breathing very fast and difficult - // drifting in and out of consciousness, sweating heavily, feeling cool to the touch, looking extremely pale - - if( one_in( 1200 / intense ) && !in_sleep_state() ) { - std::string warning; - - if( one_in( 5 ) ) { - // no-effect message block - if( intense == 1 ) { - warning = _( "Your skin looks pale and you feel anxious and thirsty. Blood loss?" ); - } else if( intense == 2 ) { - warning = _( "Your pale skin is sweating, your heart is beating fast, and you feel restless. Maybe you lost too much blood?" ); - } else if( intense == 3 ) { - warning = _( "You're unsettlingly white, but your fingertips are bluish. You are agitated and your heart is racing. Your blood loss must be serious." ); - } else { //intense == 4 - warning = _( "You are pale as a ghost, dripping wet from the sweat, and sluggish - despite your heart racing like a train. You are on the brink of collapse from the effects of blood loss." ); - } - add_msg_if_player( m_bad, warning ); - } else { - // effect dice, with progression of effects, 3 possible effects per tier - int dice_roll = rng( 0, 2 ) + intense; - switch( dice_roll ) { - case 1: - warning = _( "You feel dizzy and lightheaded." ); - add_effect( effect_stunned, rng( 5_seconds * intense, 2_minutes * intense ) ); - break; - case 2: - warning = _( "You feel tired and you breathe heavily." ); - mod_fatigue( 3 * intense ); - break; - case 3: - warning = _( "You are anxious and cannot collect your thoughts." ); - mod_focus( -rng( 1, get_focus() * intense / it.get_max_intensity() ) ); - break; - case 4: - warning = _( "You are sweating profusely, but you feel cold." ); - mod_part_temp_conv( bodypart_id( "hand_l" ), - 1000 * intense ); - mod_part_temp_conv( bodypart_id( "hand_r" ), -1000 * intense ); - mod_part_temp_conv( bodypart_id( "foot_l" ), -1000 * intense ); - mod_part_temp_conv( bodypart_id( "foot_r" ), -1000 * intense ); - break; - case 5: - warning = _( "You huff and puff. Your breath is rapid and shallow." ); - mod_stamina( -500 * intense ); - break; - case 6: - if( one_in( 2 ) ) { - warning = _( "You drop to the ground, fighting to keep yourself conscious." ); - add_effect( effect_downed, rng( 1_minutes, 2_minutes ) ); - break; - } else { - warning = _( "Your mind slips away." ); - fall_asleep( rng( 2_minutes, 5_minutes ) ); - break; - } - } - add_msg_if_player( m_bad, warning ); - } - } - // this goes last because we don't want in_sleep_state to prevent you from dying - if( intense == 4 && one_in( 900 ) && - rng( 1, -vitamin_blood->min() * 3 / 5 ) > ( -vitamin_blood->min() + vitamin_get( - vitamin_blood ) ) ) { - bleed_out(); - } } else if( id == effect_anemia ) { // effects: reduces effective redcells regen and depletes redcells at high intensity if( calendar::once_every( vitamin_rate( vitamin_redcells ) ) ) { vitamin_mod( vitamin_redcells, -rng( 0, intense ) ); } - } else if( id == effect_redcells_anemia ) { - // Lack of iron impairs production of hemoglobin and therefore ability to carry - // oxygen by red blood cells. Alternatively hemorrhage causes physical loss of red blood cells. - // This triggers variety of symptoms, focusing on weakness, - // fatigue, cold limbs, later in dizziness, soreness, breathlessness, - // and severe malaise and lethargy. - // Base anemia symptoms: fatigue, loss of stamina, loss of strength, impact on health - // are placed in effect JSON - - // you can only lose as much red blood cells before your body fails to function - if( vitamin_get( vitamin_redcells ) <= vitamin_redcells->min() + 5 ) { - add_msg_player_or_npc( m_bad, - _( "You cannot breathe and your body gives out!" ), - _( " gasps for air and dies!" ) ); - get_event_bus().send( getID() ); - set_part_hp_cur( bodypart_id( "torso" ), 0 ); - } - if( one_in( 900 / intense ) && !in_sleep_state() ) { - // level 1 symptoms are cold limbs, pale skin, and weakness - switch( dice( 1, 9 ) ) { - case 1: - add_msg_if_player( m_bad, _( "Your hands feel unusually cold." ) ); - mod_part_temp_conv( bodypart_id( "hand_l" ), -2000 ); - mod_part_temp_conv( bodypart_id( "hand_r" ), -2000 ); - break; - case 2: - add_msg_if_player( m_bad, _( "Your feet feel unusually cold." ) ); - mod_part_temp_conv( bodypart_id( "foot_l" ), -2000 ); - mod_part_temp_conv( bodypart_id( "foot_r" ), -2000 ); - break; - case 3: - add_msg_if_player( m_bad, _( "Your skin looks very pale." ) ); - break; - case 4: - add_msg_if_player( m_bad, _( "You feel weak. Where has your strength gone?" ) ); - break; - case 5: - add_msg_if_player( m_bad, _( "You feel feeble. A gust of wind could make you stumble." ) ); - break; - case 6: - add_msg_if_player( m_bad, _( "There is an overwhelming aura of tiredness inside of you." ) ); - mod_fatigue( intense * 3 ); - break; - case 7: // 7-9 empty for variability, as messages stack on higher intensity - break; - case 8: - break; - case 9: - break; - } - // level 2 anemia introduces dizziness, shakes, headaches, cravings for non-comestibles, - // mouth and tongue soreness - if( intense > 1 ) { - switch( dice( 1, 9 ) ) { - case 1: - add_msg_if_player( m_bad, _( "Rest is what you want. Rest is what you need." ) ); - break; - case 2: - add_msg_if_player( m_bad, _( "You feel dizzy and can't coordinate the movement of your feet." ) ); - add_effect( effect_stunned, rng( 1_minutes, 2_minutes ) ); - break; - case 3: - add_msg_if_player( m_bad, _( "Your muscles are quivering." ) ); - add_effect( effect_shakes, rng( 4_minutes, 8_minutes ) ); - break; - case 4: - add_msg_if_player( m_bad, _( "You crave for ice. The dirt under your feet looks tasty too." ) ); - break; - case 5: - add_msg_if_player( m_bad, _( "Your whole mouth is sore, and your tongue is swollen." ) ); - break; - case 6: - add_msg_if_player( m_bad, _( "You feel lightheaded. A migraine follows." ) ); - mod_pain( intense * 9 ); - break; - case 7: // 7-9 empty for variability, as messages stack on higher intensity - break; - case 8: - break; - case 9: - break; - } - } - // level 3 anemia introduces restless legs, severe tiredness, breathlessness - if( intense > 2 ) { - switch( dice( 1, 9 ) ) { - case 1: - add_msg_if_player( m_bad, _( "Your legs are restless. The urge to move them is so strong." ) ); - break; - case 2: - add_msg_if_player( m_bad, _( "You feel like you could sleep on a rock." ) ); - mod_fatigue( intense * 3 ); - break; - case 3: - add_msg_if_player( m_bad, _( "You gasp for air!" ) ); - set_stamina( 0 ); - add_effect( effect_winded, rng( 30_seconds, 3_minutes ) ); - break; - case 4: - add_msg_if_player( m_bad, _( "Can't breathe. Must rest." ) ); - set_stamina( 0 ); - break; - case 5: - add_msg_if_player( m_bad, _( "You can't take it any more. Rest first; everything else later." ) ); - add_effect( effect_lying_down, rng( 2_minutes, 5_minutes ) ); - break; - case 6: - add_msg_if_player( m_bad, _( "You must sit down for a moment. Just a moment." ) ); - add_effect( effect_downed, rng( 1_minutes, 2_minutes ) ); - break; - case 7: // 7-9 empty for variability, as messages stack on higher intensity - break; - case 8: - break; - case 9: - break; - } - } - } } else if( id == effect_grabbed ) { set_num_blocks_bonus( get_num_blocks_bonus() - 1 ); int zed_number = 0; @@ -1274,212 +1513,6 @@ void Character::hardcoded_effects( effect &it ) if( dur == 1_turns && !sleeping ) { add_msg_if_player( _( "You try to sleep, but can't…" ) ); } - } else if( id == effect_sleep ) { - set_moves( 0 ); -#if defined(TILES) - if( is_player() ) { - SDL_PumpEvents(); - } -#endif // TILES - - if( intense < 1 ) { - it.set_intensity( 1 ); - } else if( intense < 24 ) { - it.mod_intensity( 1 ); - } - - if( has_effect( effect_narcosis ) && get_fatigue() <= 25 ) { - set_fatigue( 25 ); //Prevent us from waking up naturally while under anesthesia - } - - if( get_fatigue() < -25 && it.get_duration() > 3_minutes && !has_effect( effect_narcosis ) ) { - it.set_duration( 1_turns * dice( 3, 10 ) ); - } - - if( get_fatigue() <= 0 && get_fatigue() > -20 && !has_effect( effect_narcosis ) ) { - mod_fatigue( -25 ); - if( get_sleep_deprivation() < SLEEP_DEPRIVATION_HARMLESS ) { - add_msg_if_player( m_good, _( "You feel well rested." ) ); - } else { - add_msg_if_player( m_warning, - _( "You feel physically rested, but you haven't been able to catch up on your missed sleep yet." ) ); - } - it.set_duration( 1_turns * dice( 3, 100 ) ); - } - - // TODO: Move this to update_needs when NPCs can mutate - if( calendar::once_every( 10_minutes ) && ( has_trait( trait_CHLOROMORPH ) || - has_trait( trait_M_SKIN3 ) || has_trait( trait_WATERSLEEP ) ) && - here.is_outside( pos() ) ) { - if( has_trait( trait_CHLOROMORPH ) ) { - // Hunger and thirst fall before your Chloromorphic physiology! - 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 - mod_stored_nutr( -5 ); - } - if( get_thirst() >= -30 ) { - mod_thirst( -5 ); - } - } - } - if( has_trait( trait_M_SKIN3 ) ) { - // Spores happen! - if( here.has_flag_ter_or_furn( "FUNGUS", pos() ) ) { - if( get_fatigue() >= 0 ) { - mod_fatigue( -5 ); // Local guides need less sleep on fungal soil - } - if( calendar::once_every( 1_hours ) ) { - spores(); // spawn some P O O F Y B O I S - } - } - } - if( has_trait( trait_WATERSLEEP ) ) { - mod_fatigue( -3 ); // Fish sleep less in water - } - } - - // Check mutation category strengths to see if we're mutated enough to get a dream - mutation_category_id highcat = get_highest_category(); - int highest = mutation_category_level[highcat]; - - // Determine the strength of effects or dreams based upon category strength - int strength = 0; // Category too weak for any effect or dream - if( crossed_threshold() ) { - strength = 4; // Post-human. - } else if( highest >= 20 && highest < 35 ) { - strength = 1; // Low strength - } else if( highest >= 35 && highest < 50 ) { - strength = 2; // Medium strength - } else if( highest >= 50 ) { - strength = 3; // High strength - } - - // Get a dream if category strength is high enough. - if( strength != 0 ) { - //Once every 6 / 3 / 2 hours, with a bit of randomness - if( calendar::once_every( 6_hours / strength ) && one_in( 3 ) ) { - // Select a dream - std::string dream = get_category_dream( highcat, strength ); - if( !dream.empty() ) { - add_msg_if_player( dream ); - } - // Mycus folks upgrade in their sleep. - if( has_trait( trait_THRESH_MYCUS ) ) { - if( one_in( 8 ) ) { - mutate_category( mutation_category_id( "MYCUS" ) ); - mod_stored_nutr( 10 ); - mod_thirst( 10 ); - mod_fatigue( 5 ); - } - } - } - } - - bool woke_up = false; - int tirednessVal = rng( 5, 200 ) + rng( 0, std::abs( get_fatigue() * 2 * 5 ) ); - if( !is_blind() && !has_effect( effect_narcosis ) ) { - if( !has_trait( - 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 ) < 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; - } - // Ursine hibernators would likely do so indoors. Plants, though, might be in the sun. - } else if( has_trait( trait_HIBERNATE ) ) { - 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 < 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( has_active_mutation( trait_SEESLEEP ) ) { - Creature *hostile_critter = g->is_hostile_very_close(); - if( hostile_critter != nullptr ) { - add_msg_if_player( _( "You see %s approaching!" ), - hostile_critter->disp_name() ); - it.set_duration( 0_turns ); - woke_up = true; - } - } - } - - // Have we already woken up? - if( !woke_up && !has_effect( effect_narcosis ) ) { - // Cold or heat may wake you up. - // Player will sleep through cold or heat if fatigued enough - for( const bodypart_id &bp : get_all_body_parts() ) { - const int curr_temp = get_part_temp_cur( bp ); - if( curr_temp < BODYTEMP_VERY_COLD - get_fatigue() / 2 ) { - if( one_in( 30000 ) ) { - add_msg_if_player( _( "You toss and turn trying to keep warm." ) ); - } - if( curr_temp < BODYTEMP_FREEZING - get_fatigue() / 2 || - one_in( curr_temp * 6 + 30000 ) ) { - add_msg_if_player( m_bad, _( "It's too cold to sleep." ) ); - // Set ourselves up for removal - it.set_duration( 0_turns ); - woke_up = true; - break; - } - } else if( curr_temp > BODYTEMP_VERY_HOT + get_fatigue() / 2 ) { - if( one_in( 30000 ) ) { - add_msg_if_player( _( "You toss and turn in the heat." ) ); - } - if( curr_temp > BODYTEMP_SCORCHING + get_fatigue() / 2 || - one_in( 90000 - curr_temp ) ) { - add_msg_if_player( m_bad, _( "It's too hot to sleep." ) ); - // Set ourselves up for removal - it.set_duration( 0_turns ); - woke_up = true; - break; - } - } - } - if( has_trait( trait_SCHIZOPHRENIC ) && one_in( 43200 ) && is_player() ) { - if( one_in( 2 ) ) { - sound_hallu(); - } else { - int max_count = rng( 1, 3 ); - int count = 0; - for( const tripoint &mp : here.points_in_radius( pos(), 1 ) ) { - if( mp == pos() ) { - continue; - } - if( here.has_flag( "FLAT", mp ) && - here.pl_sees( mp, 2 ) ) { - g->spawn_hallucination( mp ); - if( ++count > max_count ) { - break; - } - } - } - } - it.set_duration( 0_turns ); - woke_up = true; - } - } - - // A bit of a hack: check if we are about to wake up for any reason, including regular - // timing out of sleep - if( dur == 1_turns || woke_up ) { - wake_up(); - } } else if( id == effect_alarm_clock ) { if( in_sleep_state() ) { const bool asleep = has_effect( effect_sleep ); diff --git a/src/point.cpp b/src/point.cpp index b7216d791a488..bd21ad68e8a87 100644 --- a/src/point.cpp +++ b/src/point.cpp @@ -121,22 +121,21 @@ std::vector closest_points_first( const point ¢er, int min_dist, int result.push_back( center ); } - int x = std::max( min_dist, 1 ); - int y = 1 - x; + int x_init = std::max( min_dist, 1 ); + point p( x_init, 1 - x_init ); - int dx = 1; - int dy = 0; + point d( point_east ); for( int i = 0; i < n; i++ ) { - result.push_back( center + point{ x, y } ); + result.push_back( center + p ); - if( x == y || ( x < 0 && x == -y ) || ( x > 0 && x == 1 - y ) ) { - std::swap( dx, dy ); - dx = -dx; + if( p.x == p.y || ( p.x < 0 && p.x == -p.y ) || ( p.x > 0 && p.x == 1 - p.y ) ) { + std::swap( d.x, d.y ); + d.x = -d.x; } - x += dx; - y += dy; + p.x += d.x; + p.y += d.y; } return result; diff --git a/src/popup.cpp b/src/popup.cpp index 4129e15d5b12b..ec44966fa0ae0 100644 --- a/src/popup.cpp +++ b/src/popup.cpp @@ -211,9 +211,8 @@ void query_popup::init() const fullscr ? FULL_SCREEN_WIDTH : msg_width + border_width * 2 ); const int win_height = std::min( TERMY, fullscr ? FULL_SCREEN_HEIGHT : msg_height + border_width * 2 ); - const int win_x = ( TERMX - win_width ) / 2; - const int win_y = ontop ? 0 : ( TERMY - win_height ) / 2; - win = catacurses::newwin( win_height, win_width, point( win_x, win_y ) ); + const point win_pos( ( TERMX - win_width ) / 2, ontop ? 0 : ( TERMY - win_height ) / 2 ); + win = catacurses::newwin( win_height, win_width, win_pos ); std::shared_ptr ui = adaptor.lock(); if( ui ) { diff --git a/src/profession.cpp b/src/profession.cpp index 130596909dbfa..92f84f13f2139 100644 --- a/src/profession.cpp +++ b/src/profession.cpp @@ -7,6 +7,7 @@ #include #include +#include "game.h" #include "addiction.h" #include "avatar.h" #include "calendar.h" @@ -30,7 +31,6 @@ namespace { generic_factory all_profs( "profession" ); -const string_id generic_profession_id( "unemployed" ); } // namespace static class json_item_substitution @@ -238,6 +238,8 @@ void profession::load( const JsonObject &jo, const std::string & ) const profession *profession::generic() { + const string_id generic_profession_id( + get_option( "GENERIC_PROFESSION_ID" ) ); return &generic_profession_id.obj(); } @@ -333,6 +335,11 @@ void profession::check_definition() const bool profession::has_initialized() { + if( !g || g->new_game ) { + return false; + } + const string_id generic_profession_id( + get_option( "GENERIC_PROFESSION_ID" ) ); return generic_profession_id.is_valid(); } @@ -601,7 +608,7 @@ void json_item_substitution::load( const JsonObject &jo ) if( check_duplicate_item( old_it ) ) { sub.throw_error( "Duplicate definition of item" ); } - s.trait_reqs.present.push_back( trait_id( jo.get_string( "trait" ) ) ); + s.trait_reqs.present.emplace_back( jo.get_string( "trait" ) ); for( const JsonValue info : sub.get_array( "new" ) ) { s.infos.emplace_back( info ); } diff --git a/src/proficiency.cpp b/src/proficiency.cpp index 263c19121bc6b..88009471a21fa 100644 --- a/src/proficiency.cpp +++ b/src/proficiency.cpp @@ -120,11 +120,11 @@ std::vector proficiency_set::display() const std::vector> sorted_learning; for( const proficiency_id &cur : known ) { - sorted_known.push_back( { cur->name(), cur } ); + sorted_known.emplace_back( cur->name(), cur ); } for( const learning_proficiency &cur : learning ) { - sorted_learning.push_back( { cur.id->name(), cur.id } ); + sorted_learning.emplace_back( cur.id->name(), cur.id ); } std::sort( sorted_known.begin(), sorted_known.end(), localized_compare ); @@ -170,7 +170,7 @@ bool proficiency_set::practice( const proficiency_id &practicing, const time_dur return false; } if( !has_practiced( practicing ) ) { - learning.push_back( learning_proficiency( practicing, 0_seconds ) ); + learning.emplace_back( practicing, 0_seconds ); } learning_proficiency ¤t = fetch_learning( practicing ); diff --git a/src/projectile.cpp b/src/projectile.cpp index 25a0df1616f51..739eb003924e2 100644 --- a/src/projectile.cpp +++ b/src/projectile.cpp @@ -25,7 +25,7 @@ projectile::projectile() : projectile::~projectile() = default; -projectile::projectile( projectile && ) = default; +projectile::projectile( projectile && ) noexcept( set_is_noexcept ) = default; projectile::projectile( const projectile &other ) { diff --git a/src/projectile.h b/src/projectile.h index 09cd211cd5d12..b8b73206b8ce6 100644 --- a/src/projectile.h +++ b/src/projectile.h @@ -6,6 +6,7 @@ #include #include +#include "compatibility.h" #include "damage.h" #include "point.h" @@ -46,7 +47,7 @@ struct projectile { projectile(); projectile( const projectile & ); - projectile( projectile && ); + projectile( projectile && ) noexcept( set_is_noexcept ); projectile &operator=( const projectile & ); ~projectile(); diff --git a/src/ranged.cpp b/src/ranged.cpp index a6da99c1a2153..7966a97f9e30f 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -66,6 +66,7 @@ #include "string_formatter.h" #include "translations.h" #include "trap.h" +#include "try_parse_integer.h" #include "type_id.h" #include "ui.h" #include "ui_manager.h" @@ -113,6 +114,8 @@ static const std::string flag_MOUNTABLE( "MOUNTABLE" ); static const trait_id trait_PYROMANIA( "PYROMANIA" ); +static const std::set ferric = { material_id( "iron" ), material_id( "steel" ) }; + // Maximum duration of aim-and-fire loop, in turns static constexpr int AIF_DURATION_LIMIT = 10; @@ -700,7 +703,8 @@ void npc::pretend_fire( npc *source, int shots, item &gun ) add_msg_if_player_sees( *source, m_info, _( "%s shoots something." ), source->disp_name() ); } while( curshot != shots ) { - if( gun.ammo_consume( gun.ammo_required(), pos() ) != gun.ammo_required() ) { + const int required = gun.ammo_required(); + if( gun.ammo_consume( required, pos() ) != required ) { debugmsg( "Unexpected shortage of ammo whilst firing %s", gun.tname().c_str() ); break; } @@ -806,7 +810,8 @@ int player::fire_gun( const tripoint &target, int shots, item &gun ) } } - if( gun.ammo_consume( gun.ammo_required(), pos() ) != gun.ammo_required() ) { + const int required = gun.ammo_required(); + if( gun.ammo_consume( required, pos() ) != required ) { debugmsg( "Unexpected shortage of ammo whilst firing %s", gun.tname() ); break; } @@ -962,64 +967,112 @@ int Character::throwing_dispersion( const item &to_throw, Creature *critter, return std::max( 0, dispersion ); } -dealt_projectile_attack player::throw_item( const tripoint &target, const item &to_throw, - const cata::optional &blind_throw_from_pos ) +static cata::optional character_throw_assist( const Character &guy ) { - // Copy the item, we may alter it before throwing - item thrown = to_throw; - - const int move_cost = throw_cost( *this, to_throw ); - mod_moves( -move_cost ); - - const int throwing_skill = get_skill_level( skill_throw ); - units::volume volume = to_throw.volume(); - units::mass weight = to_throw.weight(); - - bool throw_assist = false; - int throw_assist_str = 0; - if( is_mounted() ) { - auto *mons = mounted_creature.get(); + cata::optional throw_assist = cata::nullopt; + if( guy.is_mounted() ) { + auto *mons = guy.mounted_creature.get(); if( mons->mech_str_addition() != 0 ) { - throw_assist = true; - throw_assist_str = mons->mech_str_addition(); + throw_assist = mons->mech_str_addition(); mons->use_mech_power( -3 ); } } - if( !throw_assist ) { - const int stamina_cost = get_standard_stamina_cost( &thrown ); - mod_stamina( stamina_cost + throwing_skill ); - } + return throw_assist; +} - const skill_id &skill_used = skill_throw; - int skill_level = std::min( MAX_SKILL, get_skill_level( skill_throw ) ); +static int throwing_skill_adjusted( const Character &guy ) +{ + int skill_level = std::min( MAX_SKILL, guy.get_skill_level( skill_throw ) ); // if you are lying on the floor, you can't really throw that well - if( has_effect( effect_downed ) ) { + if( guy.has_effect( effect_downed ) ) { skill_level = std::max( 0, skill_level - 5 ); } - // We'll be constructing a projectile - projectile proj; - proj.impact = thrown.base_damage_thrown(); - proj.speed = 10 + skill_level; - auto &impact = proj.impact; - auto &proj_effects = proj.proj_effects; - - static const std::set ferric = { material_id( "iron" ), material_id( "steel" ) }; + return skill_level; +} - bool do_railgun = has_active_bionic( bio_railgun ) && thrown.made_of_any( ferric ) && - !throw_assist; +int Character::thrown_item_adjusted_damage( const item &thrown ) const +{ + const cata::optional throw_assist = character_throw_assist( *this ); + const bool do_railgun = has_active_bionic( bio_railgun ) && thrown.made_of_any( ferric ) && + !throw_assist; // The damage dealt due to item's weight, player's strength, and skill level // Up to str/2 or weight/100g (lower), so 10 str is 5 damage before multipliers // Railgun doubles the effective strength ///\EFFECT_STR increases throwing damage double stats_mod = do_railgun ? get_str() : ( get_str() / 2.0 ); - stats_mod = throw_assist ? throw_assist_str / 2.0 : stats_mod; + stats_mod = throw_assist ? *throw_assist / 2.0 : stats_mod; // modify strength impact based on skill level, clamped to [0.15 - 1] // mod = mod * [ ( ( skill / max_skill ) * 0.85 ) + 0.15 ] stats_mod *= ( std::min( MAX_SKILL, get_skill_level( skill_throw ) ) / static_cast( MAX_SKILL ) ) * 0.85 + 0.15; - impact.add_damage( damage_type::BASH, std::min( weight / 100.0_gram, stats_mod ) ); + return stats_mod; +} + +projectile Character::thrown_item_projectile( const item &thrown ) const +{ + // We'll be constructing a projectile + projectile proj; + proj.impact = thrown.base_damage_thrown(); + proj.speed = 10 + throwing_skill_adjusted( *this ); + return proj; +} + +int Character::thrown_item_total_damage_raw( const item &thrown ) const +{ + projectile proj = thrown_item_projectile( thrown ); + const units::volume volume = thrown.volume(); + proj.impact.add_damage( damage_type::BASH, std::min( thrown.weight() / 100.0_gram, + static_cast( thrown_item_adjusted_damage( thrown ) ) ) ); + // Item will shatter upon landing, destroying the item, dealing damage, and making noise + if( !thrown.active && thrown.made_of( material_id( "glass" ) ) && + rng( 0, units::to_milliliter( 2_liter - volume ) ) < get_str() * 100 ) { + proj.impact.add_damage( damage_type::CUT, units::to_milliliter( volume ) / 500.0f ); + } + // Some minor (skill/2) armor piercing for skillful throws + // Not as much as in melee, though + const int skill_level = throwing_skill_adjusted( *this ); + for( damage_unit &du : proj.impact.damage_units ) { + du.res_pen += skill_level / 2.0f; + } + + int total_damage = 0; + for( damage_unit &du : proj.impact.damage_units ) { + total_damage += du.amount * du.damage_multiplier; + } + return total_damage; +} + +dealt_projectile_attack player::throw_item( const tripoint &target, const item &to_throw, + const cata::optional &blind_throw_from_pos ) +{ + // Copy the item, we may alter it before throwing + item thrown = to_throw; + + const int move_cost = throw_cost( *this, to_throw ); + mod_moves( -move_cost ); + + const int throwing_skill = get_skill_level( skill_throw ); + const units::volume volume = to_throw.volume(); + const units::mass weight = to_throw.weight(); + const cata::optional throw_assist = character_throw_assist( *this ); + + if( !throw_assist ) { + const int stamina_cost = get_standard_stamina_cost( &thrown ); + mod_stamina( stamina_cost + throwing_skill ); + } + + const int skill_level = throwing_skill_adjusted( *this ); + projectile proj = thrown_item_projectile( thrown ); + damage_instance &impact = proj.impact; + std::set &proj_effects = proj.proj_effects; + + const bool do_railgun = has_active_bionic( bio_railgun ) && thrown.made_of_any( ferric ) && + !throw_assist; + + impact.add_damage( damage_type::BASH, std::min( weight / 100.0_gram, + static_cast( thrown_item_adjusted_damage( thrown ) ) ) ); if( thrown.has_flag( flag_ACT_ON_RANGED_HIT ) ) { proj_effects.insert( "ACT_ON_RANGED_HIT" ); @@ -1096,7 +1149,7 @@ dealt_projectile_attack player::throw_item( const tripoint &target, const item & float range = rl_dist( throw_from, target ); proj.range = range; - int skill_lvl = get_skill_level( skill_used ); + int skill_lvl = get_skill_level( skill_throw ); // Avoid awarding tons of xp for lucky throws against hard to hit targets const float range_factor = std::min( range, skill_lvl + 3 ); // We're aiming to get a damaging hit, not just an accurate one - reward proper weapons @@ -1109,14 +1162,14 @@ dealt_projectile_attack player::throw_item( const tripoint &target, const item & const double missed_by = dealt_attack.missed_by; if( missed_by <= 0.1 && dealt_attack.hit_critter != nullptr ) { - practice( skill_used, final_xp_mult, MAX_SKILL ); + practice( skill_throw, final_xp_mult, MAX_SKILL ); // TODO: Check target for existence of head get_event_bus().send( getID() ); } else if( dealt_attack.hit_critter != nullptr && missed_by > 0.0f ) { - practice( skill_used, final_xp_mult / ( 1.0f + missed_by ), MAX_SKILL ); + practice( skill_throw, final_xp_mult / ( 1.0f + missed_by ), MAX_SKILL ); } else { // Pure grindy practice - cap gain at lvl 2 - practice( skill_used, 5, 2 ); + practice( skill_throw, 5, 2 ); } // Reset last target pos last_target_pos = cata::nullopt; @@ -1234,8 +1287,10 @@ static int print_ranged_chance( const player &p, const catacurses::window &w, in } std::string label_m = _( "Moves" ); - std::vector t_aims( 4 ), t_confidence( 20 ); - int aim_iter = 0, conf_iter = 0; + std::vector t_aims( 4 ); + std::vector t_confidence( 20 ); + int aim_iter = 0; + int conf_iter = 0; nc_color col = c_dark_gray; @@ -1541,8 +1596,15 @@ static projectile make_gun_projectile( const item &gun ) if( gun.ammo_data() ) { // Some projectiles have a chance of being recoverable bool recover = std::any_of( fx.begin(), fx.end(), []( const std::string & e ) { - int n; - return sscanf( e.c_str(), "RECOVER_%i", &n ) == 1 && !one_in( n ); + if( !string_starts_with( e, "RECOVER_" ) ) { + return false; + } + ret_val n = try_parse_integer( e.substr( 8 ), false ); + if( !n.success() ) { + debugmsg( "Error parsing ammo RECOVER_ denominator: %s", n.str() ); + return false; + } + return !one_in( n.value() ); } ); if( recover && !fx.count( "IGNITE" ) && !fx.count( "EXPLOSIVE" ) ) { @@ -1909,7 +1971,8 @@ double Character::gun_value( const item &weap, int ammo ) const double gun_value = damage_and_accuracy * capacity_factor; - add_msg_debug( "%s as gun: %.1f total, %.1f dispersion, %.1f damage, %.1f capacity", + add_msg_debug( debugmode::DF_RANGED, + "%s as gun: %.1f total, %.1f dispersion, %.1f damage, %.1f capacity", weap.type->get_id().str(), gun_value, dispersion_factor, damage_factor, capacity_factor ); return std::max( 0.0, gun_value ); @@ -2607,7 +2670,7 @@ std::vector> target_ui::list_friendlies_in_lof() ( cr->is_npc() && a != Creature::Attitude::HOSTILE ) || ( !cr->is_npc() && a == Creature::Attitude::FRIENDLY ) ) { - ret.push_back( g->shared_from( *cr ) ); + ret.emplace_back( g->shared_from( *cr ) ); } } } diff --git a/src/reachability_cache.cpp b/src/reachability_cache.cpp index adfdc1562ee03..db2d84aa165eb 100644 --- a/src/reachability_cache.cpp +++ b/src/reachability_cache.cpp @@ -60,7 +60,9 @@ void reachability_cache::rebuild( Q q, static_assert( MAPSIZE_X == SEEX * MAPSIZE, "reachability cache uses outdated map dimensions" ); // start is inclusive, end is exclusive (1 step outside of the range) - point dir, start, end; + point dir; + point start; + point end; if( q == Q::SW || q == Q::NW ) { std::tie( dir.x, start.x, end.x ) = std::make_tuple( 1, 0, MAPSIZE_X ); diff --git a/src/recipe.cpp b/src/recipe.cpp index 42332286e4193..12d05a400c797 100644 --- a/src/recipe.cpp +++ b/src/recipe.cpp @@ -806,7 +806,7 @@ std::string recipe::primary_skill_string( const Character *c, bool print_skill_l std::vector< std::pair > skillList; if( !skill_used.is_null() ) { - skillList.push_back( std::pair( skill_used, difficulty ) ); + skillList.emplace_back( skill_used, difficulty ); } return required_skills_as_string( skillList.begin(), skillList.end(), c, print_skill_level ); diff --git a/src/recipe_dictionary.cpp b/src/recipe_dictionary.cpp index b3e17b2368b74..7e8b837e0baed 100644 --- a/src/recipe_dictionary.cpp +++ b/src/recipe_dictionary.cpp @@ -189,6 +189,64 @@ std::vector recipe_subset::search( case search_type::proficiency: return lcmatch( r->recipe_proficiencies_string(), txt ); + case search_type::difficulty: { + std::string range_start; + std::string range_end; + bool use_range = false; + for( const char &chr : txt ) { + if( std::isdigit( chr ) ) { + if( use_range ) { + range_end += chr; + } else { + range_start += chr; + } + } else if( chr == '~' ) { + use_range = true; + } else { + // unexpected character + return true; + } + } + + int start = 0; + int end = INT_MAX; + + if( use_range ) { + if( !range_start.empty() ) { + start = std::stoi( range_start ); + } + + if( !range_end.empty() ) { + end = std::stoi( range_end ); + } + + if( range_start.empty() && range_end.empty() ) { + return true; + } + } else { + if( !range_start.empty() ) { + start = std::stoi( range_start ); + } + + if( range_start.empty() && range_end.empty() ) { + return true; + } + } + + if( use_range && start > end ) { + int swap = start; + start = end; + end = swap; + } + + if( use_range ) { + // check if number is between two numbers inclusive + return r->difficulty == clamp( r->difficulty, start, end ); + } else { + return r->difficulty == start; + } + } + default: return false; } diff --git a/src/recipe_dictionary.h b/src/recipe_dictionary.h index 8304cec064b38..d21b0c01f2dd4 100644 --- a/src/recipe_dictionary.h +++ b/src/recipe_dictionary.h @@ -137,6 +137,7 @@ class recipe_subset quality_result, description_result, proficiency, + difficulty, }; /** Find marked favorite recipes */ diff --git a/src/regional_settings.cpp b/src/regional_settings.cpp index 74d388f0a2b8c..87598ef911d07 100644 --- a/src/regional_settings.cpp +++ b/src/regional_settings.cpp @@ -11,6 +11,7 @@ #include "enum_conversions.h" #include "json.h" #include "options.h" +#include "output.h" #include "rng.h" #include "string_formatter.h" #include "translations.h" @@ -595,6 +596,39 @@ void load_region_settings( const JsonObject &jo ) region_settings_map[new_region.id] = new_region; } +void check_region_settings() +{ + for( const std::pair &p : region_settings_map ) { + const std::string ®ion_name = p.first; + const regional_settings ®ion = p.second; + for( const std::pair &p2 : region.region_extras ) { + const std::string extras_name = p.first; + const map_extras &extras = p2.second; + if( extras.chance == 0 ) { + continue; + } + const weighted_int_list &values = extras.values; + if( !values.is_valid() ) { + if( values.empty() ) { + debugmsg( "Invalid map extras for region \"%s\", extras \"%s\". " + "Extras have nonzero chance but no extras are listed.", + region_name, extras_name ); + } else { + std::string list_of_values = + enumerate_as_string( values, + []( const weighted_object &w ) { + return '"' + w.obj + '"'; + } ); + debugmsg( "Invalid map extras for region \"%s\", extras \"%s\". " + "Extras %s are listed, but all have zero weight.", + region_name, extras_name, + list_of_values ); + } + } + } + } +} + void reset_region_settings() { region_settings_map.clear(); diff --git a/src/regional_settings.h b/src/regional_settings.h index eb3352054c200..9335ff13f3760 100644 --- a/src/regional_settings.h +++ b/src/regional_settings.h @@ -267,6 +267,7 @@ using t_regional_settings_map_citr = t_regional_settings_map::const_iterator; extern t_regional_settings_map region_settings_map; void load_region_settings( const JsonObject &jo ); +void check_region_settings(); void reset_region_settings(); void load_region_overlay( const JsonObject &jo ); void apply_region_overlay( const JsonObject &jo, regional_settings ®ion ); diff --git a/src/requirements.cpp b/src/requirements.cpp index b60d8733beee0..b93d9293562b9 100644 --- a/src/requirements.cpp +++ b/src/requirements.cpp @@ -72,6 +72,7 @@ const requirement_data &string_id::obj() const std::vector requirement_data::get_all() { std::vector ret; + ret.reserve( requirements_all.size() ); for( const std::pair &pair : requirements_all ) { ret.push_back( pair.second ); } diff --git a/src/safe_reference.cpp b/src/safe_reference.cpp index d52082f7a93ee..6dd4c6e6ee924 100644 --- a/src/safe_reference.cpp +++ b/src/safe_reference.cpp @@ -1,5 +1,8 @@ #include "safe_reference.h" +static_assert( std::is_nothrow_move_constructible::value, "" ); +static_assert( std::is_nothrow_move_assignable::value, "" ); + safe_reference_anchor::safe_reference_anchor() { impl = std::make_shared(); @@ -9,8 +12,21 @@ safe_reference_anchor::safe_reference_anchor( const safe_reference_anchor & ) : safe_reference_anchor() {} +// Technically the move constructor and move assignment can throw bad_alloc, +// but we have no way to recover from that so mark it noexcept anyway; will +// cause terminate on out-of-memory. +safe_reference_anchor::safe_reference_anchor( safe_reference_anchor && ) noexcept : + safe_reference_anchor() +{} + safe_reference_anchor &safe_reference_anchor::operator=( const safe_reference_anchor & ) { impl = std::make_shared(); return *this; } + +safe_reference_anchor &safe_reference_anchor::operator=( safe_reference_anchor && ) noexcept +{ + impl = std::make_shared(); + return *this; +} diff --git a/src/safe_reference.h b/src/safe_reference.h index 2c660630e46f0..cdb1488ea1950 100644 --- a/src/safe_reference.h +++ b/src/safe_reference.h @@ -58,7 +58,9 @@ class safe_reference_anchor public: safe_reference_anchor(); safe_reference_anchor( const safe_reference_anchor & ); + safe_reference_anchor( safe_reference_anchor && ) noexcept; safe_reference_anchor &operator=( const safe_reference_anchor & ); + safe_reference_anchor &operator=( safe_reference_anchor && ) noexcept; template safe_reference reference_to( T *object ) { diff --git a/src/safemode_ui.cpp b/src/safemode_ui.cpp index 82a19efc45126..1b51dc0b2f6fc 100644 --- a/src/safemode_ui.cpp +++ b/src/safemode_ui.cpp @@ -303,16 +303,16 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in ) } } else if( action == "ADD_DEFAULT_RULESET" ) { changes_made = true; - current_tab.push_back( rules_class( "*", true, false, Creature::Attitude::HOSTILE, - get_option( "SAFEMODEPROXIMITY" ) - , Categories::HOSTILE_SPOTTED ) ); - current_tab.push_back( rules_class( "*", true, true, Creature::Attitude::HOSTILE, 5, - Categories::SOUND ) ); + current_tab.emplace_back( "*", true, false, Creature::Attitude::HOSTILE, + get_option( "SAFEMODEPROXIMITY" ) + , Categories::HOSTILE_SPOTTED ); + current_tab.emplace_back( "*", true, true, Creature::Attitude::HOSTILE, 5, + Categories::SOUND ); line = current_tab.size() - 1; } else if( action == "ADD_RULE" ) { changes_made = true; - current_tab.push_back( rules_class( "", true, false, Creature::Attitude::HOSTILE, - get_option( "SAFEMODEPROXIMITY" ), Categories::HOSTILE_SPOTTED ) ); + current_tab.emplace_back( "", true, false, Creature::Attitude::HOSTILE, + get_option( "SAFEMODEPROXIMITY" ), Categories::HOSTILE_SPOTTED ); line = current_tab.size() - 1; } else if( action == "REMOVE_RULE" && !current_tab.empty() ) { changes_made = true; @@ -439,7 +439,7 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in ) //Let the options class handle the validity of the new value options_manager::cOpt temp_option = get_options().get_option( "SAFEMODEPROXIMITY" ); temp_option.setValue( text ); - current_tab[line].proximity = atoi( temp_option.getValue().c_str() ); + current_tab[line].proximity = temp_option.value_as(); } } } else if( action == "ENABLE_RULE" && !current_tab.empty() ) { @@ -640,8 +640,8 @@ void safemode::add_rule( const std::string &rule_in, const Creature::Attitude at const int proximity_in, const rule_state state_in ) { - character_rules.push_back( rules_class( rule_in, true, ( state_in == rule_state::WHITELISTED ), - attitude_in, proximity_in, Categories::HOSTILE_SPOTTED ) ); + character_rules.emplace_back( rule_in, true, ( state_in == rule_state::WHITELISTED ), + attitude_in, proximity_in, Categories::HOSTILE_SPOTTED ); create_rules(); if( !get_option( "SAFEMODE" ) && @@ -891,8 +891,7 @@ void safemode::deserialize( JsonIn &jsin ) const Categories cat = jo.has_member( "category" ) ? static_cast ( jo.get_int( "category" ) ) : Categories::HOSTILE_SPOTTED; - temp_rules.push_back( - rules_class( rule, active, whitelist, attitude, proximity, cat ) - ); + temp_rules.emplace_back( rule, active, whitelist, attitude, proximity, cat + ); } } diff --git a/src/savegame.cpp b/src/savegame.cpp index 941fc16a7f334..86d99ff82e023 100644 --- a/src/savegame.cpp +++ b/src/savegame.cpp @@ -101,6 +101,26 @@ void game::serialize( std::ostream &fout ) json.member( "stats_tracker", *stats_tracker_ptr ); json.member( "achievements_tracker", *achievements_tracker_ptr ); + //save queued effect_on_conditions + std::vector temp_queue; + while( !g->queued_effect_on_conditions.empty() ) { + temp_queue.push_back( g->queued_effect_on_conditions.top() ); + g->queued_effect_on_conditions.pop(); + } + json.member( "queued_effect_on_conditions" ); + json.start_array(); + + for( const auto &queued : temp_queue ) { + g->queued_effect_on_conditions.push( queued ); + json.start_object(); + json.member( "time", queued.time ); + json.member( "eoc", queued.eoc ); + json.member( "recurring", queued.recurring ); + json.end_object(); + } + json.end_array(); + json.member( "inactive_eocs", inactive_effect_on_condition_vector ); + json.member( "player", u ); Messages::serialize( json ); @@ -155,7 +175,7 @@ static void chkversion( std::istream &fin ) /* * Parse an open .sav file. */ -void game::unserialize( std::istream &fin ) +void game::unserialize( std::istream &fin, const std::string &path ) { chkversion( fin ); int tmpturn = 0; @@ -163,7 +183,7 @@ void game::unserialize( std::istream &fin ) int tmprun = 0; tripoint_om_sm lev; point_abs_om com; - JsonIn jsin( fin ); + JsonIn jsin( fin, path ); try { JsonObject data = jsin.get_object(); @@ -232,6 +252,24 @@ void game::unserialize( std::istream &fin ) data.read( "player", u ); data.read( "stats_tracker", *stats_tracker_ptr ); data.read( "achievements_tracker", *achievements_tracker_ptr ); + + //load queued_eocs + for( JsonObject elem : data.get_array( "queued_effect_on_conditions" ) ) { + queued_eoc temp; + temp.time = time_point( elem.get_int( "time" ) ); + temp.eoc = effect_on_condition_id( elem.get_string( "eoc" ) ); + temp.recurring = elem.get_bool( "recurring" ); + g->queued_effect_on_conditions.push( temp ); + } + //load inactive queued_eocs + for( JsonObject elem : data.get_array( "inactive_effect_on_conditions" ) ) { + queued_eoc temp; + temp.time = time_point( elem.get_int( "time" ) ); + temp.eoc = effect_on_condition_id( elem.get_string( "eoc" ) ); + temp.recurring = elem.get_bool( "recurring" ); + g->queued_effect_on_conditions.push( temp ); + } + data.read( "inactive_eocs", inactive_effect_on_condition_vector ); Messages::deserialize( data ); } catch( const JsonError &jsonerr ) { @@ -265,9 +303,9 @@ void scent_map::deserialize( const std::string &data, bool is_type ) #if defined(__ANDROID__) ///// quick shortcuts -void game::load_shortcuts( std::istream &fin ) +void game::load_shortcuts( std::istream &fin, const std::string &path ) { - JsonIn jsin( fin ); + JsonIn jsin( fin, path ); try { JsonObject data = jsin.get_object(); @@ -348,7 +386,7 @@ void overmap::convert_terrain( 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" || - old == "s_lot" ) { + old == "s_lot" || old == "mine_entrance" ) { ter_set( pos, oter_id( old + "_north" ) ); } else if( old.compare( 0, 6, "bridge" ) == 0 ) { ter_set( pos, oter_id( old ) ); @@ -361,6 +399,8 @@ void overmap::convert_terrain( } } else if( old.compare( 0, 10, "mass_grave" ) == 0 ) { ter_set( pos, oter_id( "field" ) ); + } else if( old == "mine_shaft" ) { + ter_set( pos, oter_id( "mine_shaft_middle_north" ) ); } else if( old.compare( 0, 23, "office_tower_1_entrance" ) == 0 ) { ter_set( pos, oter_id( "office_tower_ne_north" ) ); ter_set( pos + point_west, oter_id( "office_tower_nw_north" ) ); diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 8a45ef8d831f2..11bdbba3cc6e7 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2100,6 +2100,10 @@ void monster::load( const JsonObject &data ) if( data.read( "wandz", wander_pos.z ) ) { wander_pos.z = position.z; } + if( data.has_int( "next_patrol_point" ) ) { + data.read( "next_patrol_point", next_patrol_point ); + data.read( "patrol_route", patrol_route_abs_ms ); + } if( data.has_object( "tied_item" ) ) { JsonIn *tied_item_json = data.get_raw( "tied_item" ); item newitem; @@ -2251,6 +2255,10 @@ void monster::store( JsonOut &json ) const json.member( "wandy", wander_pos.y ); json.member( "wandz", wander_pos.z ); json.member( "wandf", wandf ); + if( !patrol_route_abs_ms.empty() ) { + json.member( "patrol_route", patrol_route_abs_ms ); + json.member( "next_patrol_point", next_patrol_point ); + } json.member( "hp", hp ); json.member( "special_attacks", special_attacks ); json.member( "friendly", friendly ); @@ -2470,6 +2478,13 @@ void item::io( Archive &archive ) return i.id.str(); } ); archive.io( "craft_data", craft_data_, decltype( craft_data_ )() ); + const auto gvload = [this]( const std::string & variant ) { + set_gun_variant( variant ); + }; + const auto gvsave = []( const gun_variant_data * gv ) { + return gv->id; + }; + archive.io( "variant", _gun_variant, gvload, gvsave, false ); archive.io( "light", light.luminance, nolight.luminance ); archive.io( "light_width", light.width, nolight.width ); archive.io( "light_dir", light.direction, nolight.direction ); @@ -4286,7 +4301,8 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version ) int i = jsin.get_int(); int j = jsin.get_int(); const point p( i, j ); - std::string type, str; + std::string type; + std::string str; // Try to read as current format if( jsin.test_string() ) { type = jsin.get_string(); diff --git a/src/scenario.cpp b/src/scenario.cpp index f0afc2e829198..16a7caf257484 100644 --- a/src/scenario.cpp +++ b/src/scenario.cpp @@ -18,7 +18,6 @@ namespace { generic_factory all_scenarios( "scenario" ); -const string_id generic_scenario_id( "evacuee" ); } // namespace /** @relates string_id */ @@ -125,6 +124,8 @@ void scenario::load( const JsonObject &jo, const std::string & ) const scenario *scenario::generic() { + static const string_id generic_scenario_id( + get_option( "GENERIC_SCENARIO_ID" ) ); return &generic_scenario_id.obj(); } diff --git a/src/sdl_font.cpp b/src/sdl_font.cpp index 11c34b378407a..82c7cf03a998b 100644 --- a/src/sdl_font.cpp +++ b/src/sdl_font.cpp @@ -84,7 +84,7 @@ std::unique_ptr Font::load_font( SDL_Renderer_Ptr &renderer, SDL_PixelForm // line_id is one of the LINE_*_C constants // FG is a curses color -void Font::draw_ascii_lines( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, +void Font::draw_ascii_lines( const SDL_Renderer_Ptr &renderer, const GeometryRenderer_Ptr &geometry, unsigned char line_id, const point &p, unsigned char color ) const { SDL_Color sdl_color = palette[color]; @@ -278,7 +278,8 @@ CachedTTFFont::CachedTTFFont( TTF_SetFontStyle( font.get(), TTF_STYLE_NORMAL ); } -SDL_Texture_Ptr CachedTTFFont::create_glyph( SDL_Renderer_Ptr &renderer, const std::string &ch, +SDL_Texture_Ptr CachedTTFFont::create_glyph( const SDL_Renderer_Ptr &renderer, + const std::string &ch, const int color ) { const auto function = fontblending ? TTF_RenderUTF8_Blended : TTF_RenderUTF8_Solid; @@ -335,7 +336,7 @@ bool CachedTTFFont::isGlyphProvided( const std::string &ch ) const return TTF_GlyphIsProvided( font.get(), UTF8_getch( ch ) ); } -void CachedTTFFont::OutputChar( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &, +void CachedTTFFont::OutputChar( const SDL_Renderer_Ptr &renderer, const GeometryRenderer_Ptr &, const std::string &ch, const point &p, unsigned char color, const float opacity ) { @@ -410,7 +411,8 @@ BitmapFont::BitmapFont( } } -void BitmapFont::draw_ascii_lines( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, +void BitmapFont::draw_ascii_lines( const SDL_Renderer_Ptr &renderer, + const GeometryRenderer_Ptr &geometry, unsigned char line_id, const point &p, unsigned char color ) const { BitmapFont *t = const_cast( this ); @@ -485,7 +487,7 @@ bool BitmapFont::isGlyphProvided( const std::string &ch ) const } } -void BitmapFont::OutputChar( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, +void BitmapFont::OutputChar( const SDL_Renderer_Ptr &renderer, const GeometryRenderer_Ptr &geometry, const std::string &ch, const point &p, unsigned char color, const float opacity ) { @@ -493,7 +495,7 @@ void BitmapFont::OutputChar( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &g BitmapFont::OutputChar( renderer, geometry, t, p, color, opacity ); } -void BitmapFont::OutputChar( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, +void BitmapFont::OutputChar( const SDL_Renderer_Ptr &renderer, const GeometryRenderer_Ptr &geometry, const int t, const point &p, unsigned char color, const float opacity ) { @@ -584,7 +586,8 @@ bool FontFallbackList::isGlyphProvided( const std::string & ) const return true; } -void FontFallbackList::OutputChar( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, +void FontFallbackList::OutputChar( const SDL_Renderer_Ptr &renderer, + const GeometryRenderer_Ptr &geometry, const std::string &ch, const point &p, unsigned char color, const float opacity ) { diff --git a/src/sdl_font.h b/src/sdl_font.h index 72ca8b55ac400..da60d2be6136a 100644 --- a/src/sdl_font.h +++ b/src/sdl_font.h @@ -40,7 +40,7 @@ class Font /// @param p Point on the screen where to draw character /// @param color Curses color to use when drawing /// @param opacity Optional opacity of the character - virtual void OutputChar( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, + virtual void OutputChar( const SDL_Renderer_Ptr &renderer, const GeometryRenderer_Ptr &geometry, const std::string &ch, const point &p, unsigned char color, float opacity = 1.0f ) = 0; @@ -48,7 +48,8 @@ class Font /// @param line_id Character to draw /// @param point Point on the screen where to draw character /// @param color Curses color to use when drawing - virtual void draw_ascii_lines( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, + virtual void draw_ascii_lines( const SDL_Renderer_Ptr &renderer, + const GeometryRenderer_Ptr &geometry, unsigned char line_id, const point &p, unsigned char color ) const; /// Try to load a font by typeface (Bitmap or Truetype). @@ -79,11 +80,12 @@ class CachedTTFFont : public Font ~CachedTTFFont() override = default; bool isGlyphProvided( const std::string &ch ) const override; - void OutputChar( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, const std::string &ch, + void OutputChar( const SDL_Renderer_Ptr &renderer, const GeometryRenderer_Ptr &geometry, + const std::string &ch, const point &p, unsigned char color, float opacity = 1.0f ) override; protected: - SDL_Texture_Ptr create_glyph( SDL_Renderer_Ptr &renderer, const std::string &ch, int color ); + SDL_Texture_Ptr create_glyph( const SDL_Renderer_Ptr &renderer, const std::string &ch, int color ); TTF_Font_Ptr font; // Maps (character code, color) to SDL_Texture* @@ -130,12 +132,14 @@ class BitmapFont : public Font ~BitmapFont() override = default; bool isGlyphProvided( const std::string &ch ) const override; - void OutputChar( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, const std::string &ch, + void OutputChar( const SDL_Renderer_Ptr &renderer, const GeometryRenderer_Ptr &geometry, + const std::string &ch, const point &p, unsigned char color, float opacity = 1.0f ) override; - void OutputChar( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, int t, const point &p, + void OutputChar( const SDL_Renderer_Ptr &renderer, const GeometryRenderer_Ptr &geometry, int t, + const point &p, unsigned char color, float opacity = 1.0f ); - void draw_ascii_lines( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, + void draw_ascii_lines( const SDL_Renderer_Ptr &renderer, const GeometryRenderer_Ptr &geometry, unsigned char line_id, const point &p, unsigned char color ) const override; protected: std::array::COLOR_NAMES_COUNT> ascii; @@ -156,7 +160,8 @@ class FontFallbackList : public Font ~FontFallbackList() override = default; bool isGlyphProvided( const std::string &ch ) const override; - void OutputChar( SDL_Renderer_Ptr &renderer, GeometryRenderer_Ptr &geometry, const std::string &ch, + void OutputChar( const SDL_Renderer_Ptr &renderer, const GeometryRenderer_Ptr &geometry, + const std::string &ch, const point &p, unsigned char color, float opacity = 1.0f ) override; protected: diff --git a/src/sdlsound.cpp b/src/sdlsound.cpp index d083b41872123..a2252967b746c 100644 --- a/src/sdlsound.cpp +++ b/src/sdlsound.cpp @@ -465,7 +465,7 @@ void sfx::play_variant_sound( const std::string &id, const std::string &variant, return; } - add_msg_debug( "sound id: %s, variant: %s, volume: %d ", id, variant, volume ); + add_msg_debug( debugmode::DF_SOUND, "sound id: %s, variant: %s, volume: %d ", id, variant, volume ); if( !check_sound( volume ) ) { return; @@ -495,7 +495,7 @@ void sfx::play_variant_sound( const std::string &id, const std::string &variant, return; } - add_msg_debug( "sound id: %s, variant: %s, volume: %d ", id, variant, volume ); + add_msg_debug( debugmode::DF_SOUND, "sound id: %s, variant: %s, volume: %d ", id, variant, volume ); if( !check_sound( volume ) ) { return; diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index cbf28ef83481e..b0452d5d0241e 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -39,6 +39,7 @@ #include "catacharset.h" #include "color.h" #include "color_loader.h" +#include "cuboid_rectangle.h" #include "cursesport.h" #include "debug.h" #include "filesystem.h" @@ -50,10 +51,16 @@ #include "hash_utils.h" #include "input.h" #include "json.h" +#include "line.h" #include "map.h" +#include "mapbuffer.h" +#include "mission.h" +#include "npc.h" #include "optional.h" #include "options.h" #include "output.h" +#include "overmap_ui.h" +#include "overmapbuffer.h" #include "path_info.h" #include "point.h" #include "sdl_geometry.h" @@ -635,25 +642,23 @@ static void invalidate_framebuffer_proportion( cata_cursesport::WINDOW *win ) const int termpixel_y2 = termpixel.y + win->height * font->height - 1; if( map_font != nullptr && map_font->width != 0 && map_font->height != 0 ) { - const int mapfont_x = termpixel.x / map_font->width; - const int mapfont_y = termpixel.y / map_font->height; + const point mapfont( termpixel.x / map_font->width, termpixel.y / map_font->height ); const int mapfont_x2 = std::min( termpixel_x2 / map_font->width, oversized_width - 1 ); const int mapfont_y2 = std::min( termpixel_y2 / map_font->height, oversized_height - 1 ); - const int mapfont_width = mapfont_x2 - mapfont_x + 1; - const int mapfont_height = mapfont_y2 - mapfont_y + 1; - invalidate_framebuffer( oversized_framebuffer, point( mapfont_x, mapfont_y ), mapfont_width, + const int mapfont_width = mapfont_x2 - mapfont.x + 1; + const int mapfont_height = mapfont_y2 - mapfont.y + 1; + invalidate_framebuffer( oversized_framebuffer, mapfont, mapfont_width, mapfont_height ); } if( overmap_font != nullptr && overmap_font->width != 0 && overmap_font->height != 0 ) { - const int overmapfont_x = termpixel.x / overmap_font->width; - const int overmapfont_y = termpixel.y / overmap_font->height; + const point overmapfont( termpixel.x / overmap_font->width, termpixel.y / overmap_font->height ); const int overmapfont_x2 = std::min( termpixel_x2 / overmap_font->width, oversized_width - 1 ); const int overmapfont_y2 = std::min( termpixel_y2 / overmap_font->height, oversized_height - 1 ); - const int overmapfont_width = overmapfont_x2 - overmapfont_x + 1; - const int overmapfont_height = overmapfont_y2 - overmapfont_y + 1; - invalidate_framebuffer( oversized_framebuffer, point( overmapfont_x, overmapfont_y ), + const int overmapfont_width = overmapfont_x2 - overmapfont.x + 1; + const int overmapfont_height = overmapfont_y2 - overmapfont.y + 1; + invalidate_framebuffer( oversized_framebuffer, overmapfont, overmapfont_width, overmapfont_height ); } @@ -677,6 +682,372 @@ void clear_window_area( const catacurses::window &win_ ) win->width * fontwidth, win->height * fontheight, color_as_sdl( catacurses::black ) ); } +static cata::optional> get_mission_arrow( + const inclusive_cuboid &overmap_area, const tripoint_abs_omt ¢er ) +{ + if( get_avatar().get_active_mission() == nullptr ) { + return cata::nullopt; + } + if( !get_avatar().get_active_mission()->has_target() ) { + return cata::nullopt; + } + const tripoint_abs_omt mission_target = get_avatar().get_active_mission_target(); + + std::string mission_arrow_variant = "mission_cursor"; + if( overmap_area.contains( mission_target.raw() ) ) { + return std::make_pair( mission_target, mission_arrow_variant ); + } + + const std::vector mission_trajectory = line_to( center.raw(), mission_target.raw() ); + + cata::optional prev; + int z = 0; + for( const tripoint &traj_pt : mission_trajectory ) { + if( !overmap_area.contains( traj_pt ) ) { + z = prev->z - traj_pt.z; + break; + } + prev = traj_pt; + } + + if( !prev ) { + debugmsg( "ERROR: trajectory for mission in overmap failed" ); + return cata::nullopt; + } + + const int north_border_y = ( overmap_area.p_max.y - overmap_area.p_min.y ) / 3; + const int south_border_y = north_border_y * 2; + const int west_border_x = ( overmap_area.p_max.x - overmap_area.p_min.x ) / 3; + const int east_border_x = west_border_x * 2; + + tripoint north_pmax( overmap_area.p_max ); + north_pmax.y = overmap_area.p_min.y + north_border_y; + tripoint south_pmin( overmap_area.p_min ); + south_pmin.y += south_border_y; + tripoint west_pmax( overmap_area.p_max ); + west_pmax.x = overmap_area.p_min.x + west_border_x; + tripoint east_pmin( overmap_area.p_min ); + east_pmin.x += east_border_x; + + const inclusive_cuboid north_sector( overmap_area.p_min, north_pmax ); + const inclusive_cuboid south_sector( south_pmin, overmap_area.p_max ); + const inclusive_cuboid west_sector( overmap_area.p_min, west_pmax ); + const inclusive_cuboid east_sector( east_pmin, overmap_area.p_max ); + + mission_arrow_variant = "mission_arrow_"; + if( z == 0 ) { + if( north_sector.contains( *prev ) ) { + mission_arrow_variant += 'n'; + } else if( south_sector.contains( *prev ) ) { + mission_arrow_variant += 's'; + } + if( west_sector.contains( *prev ) ) { + mission_arrow_variant += 'w'; + } else if( east_sector.contains( *prev ) ) { + mission_arrow_variant += 'e'; + } + } else if( z > 0 ) { + mission_arrow_variant += "down"; + } else { + mission_arrow_variant += "up"; + } + + return std::make_pair( tripoint_abs_omt( *prev ), mission_arrow_variant ); +} + +int cata_tiles::get_omt_rotation( std::string &id ) +{ + if( id.length() < 5 ) { + return 0; + } + // save the id for later just in case we don't have a tile + const std::string first_id = id; + int rotation = 0; + std::string suffix = id.substr( id.length() - 5, id.length() - 1 ); + if( suffix == "_east" ) { + id = id.substr( 0, id.length() - 5 ); + rotation = 1; + } else if( suffix == "_west" ) { + id = id.substr( 0, id.length() - 5 ); + rotation = 3; + } + if( id.length() < 6 ) { + return rotation; + } + suffix = id.substr( id.length() - 6, id.length() - 1 ); + if( suffix == "_north" ) { + id = id.substr( 0, id.length() - 6 ); + rotation = 0; + } else if( suffix == "_south" ) { + id = id.substr( 0, id.length() - 6 ); + rotation = 2; + } + if( !find_tile_looks_like( id, TILE_CATEGORY::C_OVERMAP_TERRAIN ) ) { + //fallback tiles + id = first_id; + } + return rotation; +} + +void cata_tiles::draw_om( const point &dest, const tripoint_abs_omt ¢er_abs_omt, bool blink ) +{ + if( !g ) { + return; + } + +#if defined(__ANDROID__) + // Attempted bugfix for Google Play crash - prevent divide-by-zero if no tile + // width/height specified + if( tile_width == 0 || tile_height == 0 ) { + return; + } +#endif + + int width = ( TERMX - OVERMAP_LEGEND_WIDTH ) * font->width; + int height = OVERMAP_WINDOW_HEIGHT * font->height; + + { + //set clipping to prevent drawing over stuff we shouldn't + SDL_Rect clipRect = { dest.x, dest.y, width, height }; + printErrorIf( SDL_RenderSetClipRect( renderer.get(), &clipRect ) != 0, + "SDL_RenderSetClipRect failed" ); + + //fill render area with black to prevent artifacts where no new pixels are drawn + geometry->rect( renderer, clipRect, SDL_Color() ); + } + + // color blocks overlay; drawn on top of tiles and on top of overlay strings (if any). + color_block_overlay_container color_blocks; + // Strings with colors do be drawn with map_font on top of tiles. + std::multimap overlay_strings; + point s; + get_window_tile_counts( width, height, s.x, s.y ); + + + op = point( dest.x * fontwidth, dest.y * fontheight ); + // Rounding up to include incomplete tiles at the bottom/right edges + screentile_width = divide_round_up( width, tile_width ); + screentile_height = divide_round_up( height, tile_height ); + + const int min_col = 0; + const int max_col = s.x; + const int min_row = 0; + const int max_row = s.y; + int height_3d = 0; + avatar &you = get_avatar(); + const tripoint_abs_omt avatar_pos = you.global_omt_location(); + const tripoint_abs_omt corner_NW = center_abs_omt - point( max_col / 2, max_row / 2 ); + const tripoint_abs_omt corner_SE = corner_NW + point( max_col - 1, max_row - 1 ); + const inclusive_cuboid overmap_area( corner_NW.raw(), corner_SE.raw() ); + // Debug vision allows seeing everything + const bool has_debug_vision = you.has_trait( trait_id( "DEBUG_NIGHTVISION" ) ); + // sight_points is hoisted for speed reasons. + const int sight_points = !has_debug_vision ? + you.overmap_sight_range( g->light_level( you.posz() ) ) : + 100; + const bool showhordes = uistate.overmap_show_hordes; + const bool viewing_weather = uistate.overmap_debug_weather || uistate.overmap_visible_weather; + o = dest; + + const auto global_omt_to_draw_position = [&corner_NW]( const tripoint_abs_omt & omp ) { + // z position is hardcoded to 0 because the things this will be used to draw should not be skipped + return tripoint( ( omp - corner_NW ).raw().xy(), 0 ); + }; + + for( int row = min_row; row < max_row; row++ ) { + for( int col = min_col; col < max_col; col++ ) { + const tripoint pos( col + o.x, row + o.y, center_abs_omt.z() ); + const tripoint_abs_omt omp = corner_NW + point( col, row ); + + const bool see = overmap_buffer.seen( omp ); + const bool los = see && you.overmap_los( omp, sight_points ); + const oter_id &cur_ter = overmap_buffer.ter( omp ); + // the full string from the ter_id including _north etc. + std::string id; + + if( viewing_weather ) { + const tripoint_abs_omt omp_sky( omp.xy(), OVERMAP_HEIGHT ); + if( uistate.overmap_debug_weather || + you.overmap_los( omp_sky, sight_points * 2 ) ) { + id = overmap_ui::get_weather_at_point( omp_sky ).c_str(); + } else { + id = "unexplored_terrain"; + } + } else { + id = see ? cur_ter->id.c_str() : "unknown_terrain"; + + if( !uistate.overmap_show_forest_trails && + is_ot_match( "forest_trail", cur_ter, ot_match_type::type ) ) { + id = "forest"; + } + } + + const int rotation = get_omt_rotation( id ); + + const lit_level ll = overmap_buffer.is_explored( omp ) ? lit_level::LOW : lit_level::LIT; + // light level is now used for choosing between grayscale filter and normal lit tiles. + draw_from_id_string( id, TILE_CATEGORY::C_OVERMAP_TERRAIN, "overmap_terrain", pos, 0, rotation, + ll, false, height_3d ); + + if( see ) { + if( blink && uistate.overmap_debug_mongroup ) { + const std::vector mgroups = overmap_buffer.monsters_at( omp ); + if( !mgroups.empty() ) { + auto mgroup_iter = mgroups.begin(); + std::advance( mgroup_iter, rng( 0, mgroups.size() - 1 ) ); + draw_from_id_string( ( *mgroup_iter )->type->defaultMonster.str(), pos, 0, 0, lit_level::LIT, + false ); + } + } + const int horde_size = overmap_buffer.get_horde_size( omp ); + if( showhordes && los && horde_size >= HORDE_VISIBILITY_SIZE ) { + // a little bit of hardcoded fallbacks for hordes + if( find_tile_with_season( id ) ) { + draw_from_id_string( string_format( "overmap_horde_%d", HORDE_VISIBILITY_SIZE ), + pos, 0, 0, lit_level::LIT, false ); + } else { + switch( horde_size ) { + case HORDE_VISIBILITY_SIZE: + draw_from_id_string( "mon_zombie", pos, 0, 0, lit_level::LIT, false ); + break; + case HORDE_VISIBILITY_SIZE + 1: + draw_from_id_string( "mon_zombie_tough", pos, 0, 0, lit_level::LIT, false ); + break; + case HORDE_VISIBILITY_SIZE + 2: + draw_from_id_string( "mon_zombie_brute", pos, 0, 0, lit_level::LIT, false ); + break; + case HORDE_VISIBILITY_SIZE + 3: + draw_from_id_string( "mon_zombie_hulk", pos, 0, 0, lit_level::LIT, false ); + break; + case HORDE_VISIBILITY_SIZE + 4: + draw_from_id_string( "mon_zombie_necro", pos, 0, 0, lit_level::LIT, false ); + break; + default: + draw_from_id_string( "mon_zombie_master", pos, 0, 0, lit_level::LIT, false ); + break; + } + } + } + } + + if( uistate.place_terrain || uistate.place_special ) { + // Highlight areas that already have been generated + // TODO: fix point types + if( MAPBUFFER.lookup_submap( project_to( omp ).raw() ) ) { + draw_from_id_string( "highlight", pos, 0, 0, lit_level::LIT, false ); + } + } + + if( blink && uistate.overmap_show_map_notes && overmap_buffer.has_note( omp ) ) { + + nc_color ter_color = c_black; + std::string ter_sym = " "; + // Display notes in all situations, even when not seen + std::tie( ter_sym, ter_color, std::ignore ) = + overmap_ui::get_note_display_info( overmap_buffer.note( omp ) ); + + std::string note_name = "note_" + ter_sym + "_" + string_from_color( ter_color ); + draw_from_id_string( note_name, TILE_CATEGORY::C_OVERMAP_NOTE, "overmap_note", pos, 0, 0, + lit_level::LIT, false ); + } + } + } + + if( uistate.place_terrain ) { + const oter_str_id &terrain = uistate.place_terrain->id; + std::string id = terrain.c_str(); + const int rotation = get_omt_rotation( id ); + draw_from_id_string( id, global_omt_to_draw_position( center_abs_omt ), 0, rotation, + lit_level::LOW, true ); + } + if( uistate.place_special ) { + for( const overmap_special_terrain &s_ter : uistate.place_special->terrains ) { + if( s_ter.p.z == 0 ) { + // TODO: fix point types + const point_rel_omt rp( om_direction::rotate( s_ter.p.xy(), uistate.omedit_rotation ) ); + std::string id = s_ter.terrain->get_rotated( uistate.omedit_rotation ).id().c_str(); + const int rotation = get_omt_rotation( id ); + + draw_from_id_string( id, TILE_CATEGORY::C_OVERMAP_TERRAIN, "overmap_terrain", + global_omt_to_draw_position( center_abs_omt + rp ), 0, rotation, lit_level::LOW, true ); + } + } + } + + // draw nearby seen npcs + for( const shared_ptr_fast &guy : overmap_buffer.get_npcs_near_player( sight_points ) ) { + const tripoint_abs_omt &guy_loc = guy->global_omt_location(); + if( overmap_buffer.seen( guy_loc ) ) { + draw_entity_with_overlays( *guy, global_omt_to_draw_position( guy_loc ), lit_level::LIT, + height_3d ); + } + } + + draw_entity_with_overlays( get_player_character(), global_omt_to_draw_position( avatar_pos ), + lit_level::LIT, height_3d ); + draw_from_id_string( "cursor", global_omt_to_draw_position( center_abs_omt ), 0, 0, lit_level::LIT, + false ); + + if( blink ) { + // reduce the area where the map cursor is drawn so it doesn't get cut off + inclusive_cuboid map_cursor_area = overmap_area; + map_cursor_area.p_max.y--; + const cata::optional> mission_arrow = + get_mission_arrow( map_cursor_area, center_abs_omt ); + if( mission_arrow ) { + draw_from_id_string( mission_arrow->second, global_omt_to_draw_position( mission_arrow->first ), 0, + 0, lit_level::LIT, false ); + } + } + + // Labels need to be drawn last, as anything that attempts to draw a sprite after will fail. + if( !viewing_weather && uistate.overmap_show_city_labels ) { + + const auto abs_sm_to_draw_label = [&]( const tripoint_abs_sm & city_pos, const int label_length ) { + const tripoint tile_draw_pos = global_omt_to_draw_position( project_to( city_pos ) ); + point draw_point( tile_draw_pos.x * width / max_col, tile_draw_pos.y * height / max_row ); + draw_point.x -= label_length * font->width; + draw_point.x += width / max_col; + return draw_point; + }; + + // draws a black rectangle behind a label for visibility and legibility + const auto label_bg = [&]( const tripoint_abs_sm & pos, const std::string & name ) { + const int name_length = name.length(); + const point draw_pos = abs_sm_to_draw_label( pos, name_length ); + SDL_Rect clipRect = { draw_pos.x, draw_pos.y, name_length * fontwidth, fontheight }; + printErrorIf( SDL_RenderSetClipRect( renderer.get(), &clipRect ) != 0, + "SDL_RenderSetClipRect failed" ); + + geometry->rect( renderer, clipRect, SDL_Color() ); + + const point label_pos( draw_pos + point( -( name.length() * fontwidth / 2 ), 0 ) ); + map_font->OutputChar( renderer, geometry, name, label_pos, 11 ); + }; + + // the tiles on the overmap are overmap tiles, so we need to use + // coordinate conversions to make sure we're in the right place. + const int radius = coords::project_to( tripoint_abs_omt( std::min( max_col, max_row ), + 0, 0 ) ).x() / 2; + + for( const city_reference &city : overmap_buffer.get_cities_near( + coords::project_to( center_abs_omt ), radius ) ) { + const tripoint_abs_omt city_center = coords::project_to( city.abs_sm_pos ); + if( overmap_buffer.seen( city_center ) && overmap_area.contains( city_center.raw() ) ) { + label_bg( city.abs_sm_pos, city.city->name ); + } + } + + for( const camp_reference &camp : overmap_buffer.get_camps_near( + coords::project_to( center_abs_omt ), radius ) ) { + const tripoint_abs_omt camp_center = coords::project_to( camp.abs_sm_pos ); + if( overmap_buffer.seen( camp_center ) && overmap_area.contains( camp_center.raw() ) ) { + label_bg( camp.abs_sm_pos, camp.camp->name ); + } + } + } +} + static bool draw_window( Font_Ptr &font, const catacurses::window &w, const point &offset ) { if( scaling_factor > 1 ) { @@ -758,9 +1129,8 @@ static bool draw_window( Font_Ptr &font, const catacurses::window &w, const poin const cursecell &cell = win->line[j].chars[i]; - const int drawx = offset.x + i * font->width; - const int drawy = offset.y + j * font->height; - if( drawx + font->width > WindowWidth || drawy + font->height > WindowHeight ) { + const point draw( offset + point( i * font->width, j * font->height ) ); + if( draw.x + font->width > WindowWidth || draw.y + font->height > WindowHeight ) { // Outside of the display area, would not render anyway continue; } @@ -780,7 +1150,7 @@ static bool draw_window( Font_Ptr &font, const catacurses::window &w, const poin // Spaces are used a lot, so this does help noticeably if( cell.ch == space_string ) { - geometry->rect( renderer, point( drawx, drawy ), font->width, font->height, + geometry->rect( renderer, draw, font->width, font->height, color_as_sdl( cell.BG ) ); continue; } @@ -835,12 +1205,12 @@ static bool draw_window( Font_Ptr &font, const catacurses::window &w, const poin use_draw_ascii_lines_routine = false; break; } - geometry->rect( renderer, point( drawx, drawy ), font->width * cw, font->height, + geometry->rect( renderer, draw, font->width * cw, font->height, color_as_sdl( BG ) ); if( use_draw_ascii_lines_routine ) { - font->draw_ascii_lines( renderer, geometry, uc, point( drawx, drawy ), FG ); + font->draw_ascii_lines( renderer, geometry, uc, draw, FG ); } else { - font->OutputChar( renderer, geometry, cell.ch, point( drawx, drawy ), FG ); + font->OutputChar( renderer, geometry, cell.ch, draw, FG ); } } } @@ -930,20 +1300,18 @@ void cata_cursesport::curses_drawwindow( const catacurses::window &w ) int width = 0; for( size_t i = 0; i < text.size(); ++i ) { - const int x0 = win->pos.x * fontwidth; - const int y0 = win->pos.y * fontheight; - const int x = x0 + ( x_offset - alignment_offset + width ) * map_font->width + coord.x; - const int y = y0 + coord.y; + const point p0( win->pos.x * fontwidth, win->pos.y * fontheight ); + const point p( coord + p0 + point( ( x_offset - alignment_offset + width ) * map_font->width, 0 ) ); // Clip to window bounds. - if( x < x0 || x > x0 + ( TERRAIN_WINDOW_TERM_WIDTH - 1 ) * font->width - || y < y0 || y > y0 + ( TERRAIN_WINDOW_TERM_HEIGHT - 1 ) * font->height ) { + if( p.x < p0.x || p.x > p0.x + ( TERRAIN_WINDOW_TERM_WIDTH - 1 ) * font->width + || p.y < p0.y || p.y > p0.y + ( TERRAIN_WINDOW_TERM_HEIGHT - 1 ) * font->height ) { continue; } // TODO: draw with outline / BG color for better readability const uint32_t ch = text.at( i ); - map_font->OutputChar( renderer, geometry, utf32_to_utf8( ch ), point( x, y ), ft.color ); + map_font->OutputChar( renderer, geometry, utf32_to_utf8( ch ), p, ft.color ); width += mk_wcwidth( ch ); } @@ -2904,7 +3272,8 @@ static void init_term_size_and_scaling_factor() if( scaling_factor > 1 ) { - int max_width, max_height; + int max_width; + int max_height; int current_display_id = std::stoi( get_option( "DISPLAY" ) ); SDL_DisplayMode current_display; diff --git a/src/sdltiles.h b/src/sdltiles.h index d1bf340be691e..d8df852b2e8f8 100644 --- a/src/sdltiles.h +++ b/src/sdltiles.h @@ -15,7 +15,9 @@ class window; #include #include "color_loader.h" +#include "coordinates.h" #include "sdl_wrappers.h" +#include "string_id.h" #if defined(__APPLE__) // For TARGET_OS_IPHONE macro to test if is on iOS @@ -24,6 +26,10 @@ class window; class cata_tiles; +struct weather_type; + +using weather_type_id = string_id; + namespace catacurses { class window; diff --git a/src/simplexnoise.cpp b/src/simplexnoise.cpp index 0d3f05763f050..b0530f7df3d03 100644 --- a/src/simplexnoise.cpp +++ b/src/simplexnoise.cpp @@ -204,7 +204,10 @@ float raw_noise_2d( const float x, const float y ) // For the 2D case, the simplex shape is an equilateral triangle. // Determine which simplex we are in. - int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coordinates + + // Offsets for second (middle) corner of simplex in (i,j) coordinates + int i1; + int j1; if( x0 > y0 ) { i1 = 1; // lower triangle, XY order: (0,0)->(1,0)->(1,1) j1 = 0; @@ -261,7 +264,11 @@ float raw_noise_2d( const float x, const float y ) // 3D raw Simplex noise float raw_noise_3d( const float x, const float y, const float z ) { - float n0, n1, n2, n3; // Noise contributions from the four corners + // Noise contributions from the four corners + float n0; + float n1; + float n2; + float n3; // Skew the input space to determine which simplex cell we're in float F3 = 1.0f / 3.0f; @@ -281,8 +288,12 @@ float raw_noise_3d( const float x, const float y, const float z ) // For the 3D case, the simplex shape is a slightly irregular tetrahedron. // Determine which simplex we are in. - int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coordinates - int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coordinates + int i1; // Offsets for second corner of simplex in (i,j,k) coordinates + int j1; + int k1; + int i2; // Offsets for third corner of simplex in (i,j,k) coordinates + int j2; + int k2; if( x0 >= y0 ) { if( y0 >= z0 ) { @@ -399,7 +410,12 @@ float raw_noise_4d( const float x, const float y, const float z, const float w ) // The skewing and unskewing factors are hairy again for the 4D case static const float F4 = ( std::sqrt( 5.0f ) - 1.0f ) / 4.0f; static const float G4 = ( 5.0f - std::sqrt( 5.0f ) ) / 20.0f; - float n0, n1, n2, n3, n4; // Noise contributions from the five corners + // Noise contributions from the five corners + float n0; + float n1; + float n2; + float n3; + float n4; // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in float s = ( x + y + z + w ) * F4; // Factor for 4D skewing @@ -434,9 +450,21 @@ float raw_noise_4d( const float x, const float y, const float z, const float w ) int c6 = ( z0 > w0 ) ? 1 : 0; int c = c1 + c2 + c3 + c4 + c5 + c6; - int i1, j1, k1, l1; // The integer offsets for the second simplex corner - int i2, j2, k2, l2; // The integer offsets for the third simplex corner - int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner + // The integer offsets for the second simplex corner + int i1; + int j1; + int k1; + int l1; + // The integer offsets for the third simplex corner + int i2; + int j2; + int k2; + int l2; + // The integer offsets for the fourth simplex corner + int i3; + int j3; + int k3; + int l3; // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. // Many values of c will never occur, since e.g. x>y>z>w makes xengine_on ) { fade_audio_channel( ch, 100 ); - add_msg_debug( "STOP interior_engine_sound" ); + add_msg_debug( debugmode::DF_SOUND, "STOP interior_engine_sound" ); return; } @@ -727,9 +728,9 @@ void sfx::do_vehicle_engine_sfx() if( !is_channel_playing( ch ) ) { play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, sfx::get_heard_volume( player_character.pos() ), ch, 1000 ); - add_msg_debug( "START %s %s", id_and_variant.first, id_and_variant.second ); + add_msg_debug( debugmode::DF_SOUND, "START %s %s", id_and_variant.first, id_and_variant.second ); } else { - add_msg_debug( "PLAYING" ); + add_msg_debug( debugmode::DF_SOUND, "PLAYING" ); } int current_speed = veh->velocity; bool in_reverse = false; @@ -765,11 +766,11 @@ void sfx::do_vehicle_engine_sfx() if( current_gear > previous_gear ) { play_variant_sound( "vehicle", "gear_shift", get_heard_volume( player_character.pos() ), 0_degrees, 0.8, 0.8 ); - add_msg_debug( "GEAR UP" ); + add_msg_debug( debugmode::DF_SOUND, "GEAR UP" ); } else if( current_gear < previous_gear ) { play_variant_sound( "vehicle", "gear_shift", get_heard_volume( player_character.pos() ), 0_degrees, 1.2, 1.2 ); - add_msg_debug( "GEAR DOWN" ); + add_msg_debug( debugmode::DF_SOUND, "GEAR DOWN" ); } if( ( safe_speed != 0 ) ) { if( current_gear == 0 ) { @@ -786,10 +787,10 @@ void sfx::do_vehicle_engine_sfx() if( current_speed != previous_speed ) { Mix_HaltChannel( static_cast( ch ) ); - add_msg_debug( "STOP speed %d =/= %d", current_speed, previous_speed ); + add_msg_debug( debugmode::DF_SOUND, "STOP speed %d =/= %d", current_speed, previous_speed ); play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, sfx::get_heard_volume( player_character.pos() ), ch, 1000, pitch ); - add_msg_debug( "PITCH %f", pitch ); + add_msg_debug( debugmode::DF_SOUND, "PITCH %f", pitch ); } previous_speed = current_speed; previous_gear = current_gear; @@ -807,7 +808,7 @@ void sfx::do_vehicle_exterior_engine_sfx() // early bail-outs for efficiency if( player_character.in_vehicle ) { fade_audio_channel( ch, 300 ); - add_msg_debug( "STOP exterior_engine_sound, IN CAR" ); + add_msg_debug( debugmode::DF_SOUND, "STOP exterior_engine_sound, IN CAR" ); return; } if( player_character.in_sleep_state() && !audio_muted ) { @@ -835,7 +836,7 @@ void sfx::do_vehicle_exterior_engine_sfx() } if( !noise_factor || !veh ) { fade_audio_channel( ch, 300 ); - add_msg_debug( "STOP exterior_engine_sound, NO NOISE" ); + add_msg_debug( debugmode::DF_SOUND, "STOP exterior_engine_sound, NO NOISE" ); return; } @@ -864,26 +865,29 @@ void sfx::do_vehicle_exterior_engine_sfx() if( engine_external_id_and_variant == id_and_variant ) { Mix_SetPosition( ch_int, to_degrees( get_heard_angle( veh->global_pos3() ) ), 0 ); set_channel_volume( ch, vol ); - add_msg_debug( "PLAYING exterior_engine_sound, vol: ex:%d true:%d", vol, Mix_Volume( ch_int, - -1 ) ); + add_msg_debug( debugmode::DF_SOUND, "PLAYING exterior_engine_sound, vol: ex:%d true:%d", vol, + Mix_Volume( ch_int, + -1 ) ); } else { engine_external_id_and_variant = id_and_variant; Mix_HaltChannel( ch_int ); - add_msg_debug( "STOP exterior_engine_sound, change id/var" ); + add_msg_debug( debugmode::DF_SOUND, "STOP exterior_engine_sound, change id/var" ); play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, 128, ch, 0 ); Mix_SetPosition( ch_int, to_degrees( get_heard_angle( veh->global_pos3() ) ), 0 ); set_channel_volume( ch, vol ); - add_msg_debug( "START exterior_engine_sound %s %s vol: %d", id_and_variant.first, + add_msg_debug( debugmode::DF_SOUND, "START exterior_engine_sound %s %s vol: %d", + id_and_variant.first, id_and_variant.second, Mix_Volume( ch_int, -1 ) ); } } else { play_ambient_variant_sound( id_and_variant.first, id_and_variant.second, 128, ch, 0 ); - add_msg_debug( "Vol: %d %d", vol, Mix_Volume( ch_int, -1 ) ); + add_msg_debug( debugmode::DF_SOUND, "Vol: %d %d", vol, Mix_Volume( ch_int, -1 ) ); Mix_SetPosition( ch_int, to_degrees( get_heard_angle( veh->global_pos3() ) ), 0 ); - add_msg_debug( "Vol: %d %d", vol, Mix_Volume( ch_int, -1 ) ); + add_msg_debug( debugmode::DF_SOUND, "Vol: %d %d", vol, Mix_Volume( ch_int, -1 ) ); set_channel_volume( ch, vol ); - add_msg_debug( "START exterior_engine_sound NEW %s %s vol: ex:%d true:%d", id_and_variant.first, + add_msg_debug( debugmode::DF_SOUND, "START exterior_engine_sound NEW %s %s vol: ex:%d true:%d", + id_and_variant.first, id_and_variant.second, vol, Mix_Volume( ch_int, -1 ) ); } } diff --git a/src/string_input_popup.cpp b/src/string_input_popup.cpp index 3a1fdc8b7e817..0c034f98c982b 100644 --- a/src/string_input_popup.cpp +++ b/src/string_input_popup.cpp @@ -7,6 +7,7 @@ #include "output.h" #include "point.h" #include "translations.h" +#include "try_parse_integer.h" #include "ui.h" #include "ui_manager.h" #include "uistate.h" @@ -283,14 +284,31 @@ void string_input_popup::query( const bool loop, const bool draw_only ) query_string( loop, draw_only ); } +template +T query_int_impl( string_input_popup &p, const bool loop, const bool draw_only ) +{ + do { + ret_val result = try_parse_integer( p.query_string( loop, draw_only ), true ); + if( p.canceled() ) { + return 0; + } + if( result.success() ) { + return result.value(); + } + popup( result.str() ); + } while( loop ); + + return 0; +} + int string_input_popup::query_int( const bool loop, const bool draw_only ) { - return std::atoi( query_string( loop, draw_only ).c_str() ); + return query_int_impl( *this, loop, draw_only ); } int64_t string_input_popup::query_int64_t( const bool loop, const bool draw_only ) { - return std::atoll( query_string( loop, draw_only ).c_str() ); + return query_int_impl( *this, loop, draw_only ); } const std::string &string_input_popup::query_string( const bool loop, const bool draw_only ) @@ -535,25 +553,35 @@ void string_input_popup::edit( std::string &value ) } } +template +static void edit_integer( string_input_popup &p, T &value ) +{ + p.only_digits( true ); + while( true ) { + p.text( std::to_string( value ) ); + p.query(); + if( p.canceled() ) { + break; + } + ret_val parsed_val = try_parse_integer( p.text(), true ); + if( parsed_val.success() ) { + value = parsed_val.value(); + break; + } else { + popup( parsed_val.str() ); + } + } +} + // NOLINTNEXTLINE(cata-no-long) void string_input_popup::edit( long &value ) { - only_digits( true ); - text( std::to_string( value ) ); - query(); - if( !canceled() ) { - value = std::atol( text().c_str() ); - } + edit_integer( *this, value ); } void string_input_popup::edit( int &value ) { - only_digits( true ); - text( std::to_string( value ) ); - query(); - if( !canceled() ) { - value = std::atoi( text().c_str() ); - } + edit_integer( *this, value ); } string_input_popup &string_input_popup::text( const std::string &value ) diff --git a/src/submap.cpp b/src/submap.cpp index 07cbedf62a7c8..34c8db59332af 100644 --- a/src/submap.cpp +++ b/src/submap.cpp @@ -36,10 +36,10 @@ submap::submap() is_uniform = false; } -submap::submap( submap && ) = default; +submap::submap( submap && ) noexcept( map_is_noexcept ) = default; submap::~submap() = default; -submap &submap::operator=( submap && ) = default; +submap &submap::operator=( submap && ) noexcept = default; static const std::string COSMETICS_GRAFFITI( "GRAFFITI" ); static const std::string COSMETICS_SIGNAGE( "SIGNAGE" ); diff --git a/src/submap.h b/src/submap.h index 5f0d8cfa71316..cf78807fa9998 100644 --- a/src/submap.h +++ b/src/submap.h @@ -14,6 +14,7 @@ #include "active_item_cache.h" #include "calendar.h" #include "colony.h" +#include "compatibility.h" #include "computer.h" #include "construction.h" #include "field.h" @@ -65,10 +66,10 @@ class submap : maptile_soa { public: submap(); - submap( submap && ); + submap( submap && ) noexcept( map_is_noexcept ); ~submap(); - submap &operator=( submap && ); + submap &operator=( submap && ) noexcept; trap_id get_trap( const point &p ) const { return trp[p.x][p.y]; diff --git a/src/suffer.cpp b/src/suffer.cpp index 8731701f326c8..dcb0721b49e80 100644 --- a/src/suffer.cpp +++ b/src/suffer.cpp @@ -43,6 +43,7 @@ #include "npc.h" #include "optional.h" #include "options.h" +#include "output.h" #include "overmapbuffer.h" #include "pimpl.h" #include "player_activity.h" @@ -62,7 +63,6 @@ static const bionic_id bio_dis_acid( "bio_dis_acid" ); static const bionic_id bio_dis_shock( "bio_dis_shock" ); -static const bionic_id bio_drain( "bio_drain" ); static const bionic_id bio_geiger( "bio_geiger" ); static const bionic_id bio_gills( "bio_gills" ); static const bionic_id bio_glowy( "bio_glowy" ); @@ -138,7 +138,6 @@ static const trait_id trait_RADIOACTIVE2( "RADIOACTIVE2" ); static const trait_id trait_RADIOACTIVE3( "RADIOACTIVE3" ); static const trait_id trait_RADIOGENIC( "RADIOGENIC" ); static const trait_id trait_REGEN_LIZ( "REGEN_LIZ" ); -static const trait_id trait_ROOTS3( "ROOTS3" ); static const trait_id trait_SCHIZOPHRENIC( "SCHIZOPHRENIC" ); static const trait_id trait_SHARKTEETH( "SHARKTEETH" ); static const trait_id trait_SHELL2( "SHELL2" ); @@ -162,8 +161,6 @@ static const mtype_id mon_zombie_fat( "mon_zombie_fat" ); static const mtype_id mon_zombie_fireman( "mon_zombie_fireman" ); static const mtype_id mon_zombie_soldier( "mon_zombie_soldier" ); -static const std::string flag_PLOWABLE( "PLOWABLE" ); - static const json_character_flag json_flag_GLARE_RESIST( "GLARE_RESIST" ); static float addiction_scaling( float at_min, float at_max, float add_lvl ) @@ -807,26 +804,23 @@ void Character::suffer_from_sunburn() return; } - std::string sunlight_effect; if( has_trait( trait_ALBINO ) || has_effect( effect_datura ) ) { // Albinism and datura have the same effects, once per minute on average if( !one_turn_in( 1_minutes ) ) { return; } - sunlight_effect = _( "The sunlight is really irritating." ); } else if( has_trait( trait_SUNBURN ) ) { // Sunburn effects occur about 3 times per minute if( !one_turn_in( 20_seconds ) ) { return; } - sunlight_effect = _( "The sunlight burns!" ); } // Sunglasses can keep the sun off the eyes. if( !has_flag( json_flag_GLARE_RESIST ) && !( wearing_something_on( bodypart_id( "eyes" ) ) && ( worn_with_flag( flag_SUN_GLASSES ) || worn_with_flag( flag_BLIND ) ) ) ) { - add_msg_if_player( m_bad, _( "%s your eyes." ), sunlight_effect ); + add_msg_if_player( m_bad, _( "The sunlight is really irritating your eyes." ) ); // Pain (1/60) or loss of focus (59/60) if( one_turn_in( 1_minutes ) ) { mod_pain( 1 ); @@ -843,12 +837,8 @@ void Character::suffer_from_sunburn() // Minimum exposure threshold for pain const float MIN_EXPOSURE = 0.01f; - // Count how many body parts are above the threshold - int count_affected_bp = 0; - // Get the most exposed body part, and how exposed it is. This is to tell the player what body - // part is most irritated by sun, so they know what needs to be covered up better. - bodypart_id most_exposed_bp; - float max_exposure = 0.0f; + // Track body parts above the threshold + std::vector> affected_bodyparts; // Check each bodypart with exposure above the minimum for( const std::pair &bp_exp : bp_exposure ) { const float exposure = bp_exp.second; @@ -856,40 +846,60 @@ void Character::suffer_from_sunburn() if( exposure <= MIN_EXPOSURE || bp_exp.first == bodypart_id( "eyes" ) ) { continue; } - ++count_affected_bp; - if( exposure > max_exposure ) { - max_exposure = exposure; - most_exposed_bp = bp_exp.first; - } + affected_bodyparts.emplace_back( exposure, bp_exp.first ); } // If all body parts are protected, there is no suffering - if( count_affected_bp == 0 || most_exposed_bp == bodypart_str_id::NULL_ID() ) { + if( affected_bodyparts.empty() ) { return; } - // Check if both arms/legs are affected - int count_limbs = 1; - const bodypart_id &other_bp = most_exposed_bp->opposite_part; - const bodypart_id &other_bp_rev = other_bp->opposite_part; - // If these are different, we have a left/right part like a leg or arm. - // If same, it's a central body part with no opposite, like head or torso. - // Only used to generate a simpler message when both arms or both legs are affected. - if( other_bp != other_bp_rev ) { - const auto found = bp_exposure.find( other_bp ); - // Is opposite part exposed? - if( found != bp_exposure.end() && found->second > MIN_EXPOSURE ) { - ++count_limbs; - } - } - // Get singular or plural body part name; append "and other body parts" if appropriate - std::string bp_name = body_part_name( most_exposed_bp, count_limbs ); - if( count_affected_bp == count_limbs ) { - add_msg_if_player( m_bad, _( "%s your %s." ), sunlight_effect, bp_name ); - } else { - add_msg_if_player( m_bad, _( "%s your %s and other body parts." ), sunlight_effect, - bp_name ); + // Sort most affected bodyparts to the front + std::sort( affected_bodyparts.begin(), affected_bodyparts.end(), std::greater<> {} ); + + std::vector affected_part_names; + std::unordered_set excluded_other_parts; + + for( const std::pair &exp_bp : affected_bodyparts ) { + const bodypart_id &bp = exp_bp.second; + if( excluded_other_parts.count( bp ) ) { + continue; + } + const bodypart_id &opposite_bp = bp->opposite_part; + // If these are different, we have a left/right part like a leg or arm. + // If same, it's a central body part with no opposite, like head or torso. + // Used to generate a simpler message when both arms or both legs are affected. + int count_limbs = 1; + if( bp != opposite_bp ) { + const auto found = bp_exposure.find( opposite_bp ); + // Is opposite part exposed? + if( found != bp_exposure.end() && found->second > MIN_EXPOSURE ) { + ++count_limbs; + excluded_other_parts.insert( opposite_bp ); + } + } + // Get singular or plural body part name; append "and other body parts" if appropriate + std::string bp_name = body_part_name( bp, count_limbs ); + affected_part_names.push_back( bp_name ); + } + + std::string all_parts_list = enumerate_as_string( affected_part_names ); + + std::string message; + if( has_trait( trait_ALBINO ) || has_effect( effect_datura ) ) { + //~ %s is a list of body parts. The plurality integer is the total + //~ number of body parts + message = ngettext( "The sunlight is really irritating your %s.", + "The sunlight is really irritating your %s.", + affected_bodyparts.size() ); + } else if( has_trait( trait_SUNBURN ) ) { + //~ %s is a list of body parts. The plurality integer is the total + //~ number of body parts + message = ngettext( "The sunlight burns your %s.", + "The sunlight burns your %s.", + affected_bodyparts.size() ); } + add_msg_if_player( m_bad, message, all_parts_list ); // Wake up from skin irritation/burning if( has_effect( effect_sleep ) ) { @@ -1183,11 +1193,6 @@ void Character::suffer_from_bad_bionics() sfx::play_variant_sound( "bionics", "acid_discharge", 100 ); 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." ) ); - mod_power_level( -25_kJ ); - sfx::play_variant_sound( "bionics", "elec_crackle_low", 100 ); - } if( has_bionic( bio_noise ) && one_turn_in( 50_minutes ) && !has_effect( effect_narcosis ) ) { // TODO: NPCs with said bionic @@ -1702,7 +1707,7 @@ void Character::mend( int rate_multiplier ) needs_splint = false; } - add_msg_debug( "Limb mend healing factor: %.2f", healing_factor ); + add_msg_debug( debugmode::DF_CHAR_HEALTH, "Limb mend healing factor: %.2f", healing_factor ); if( healing_factor <= 0.0f ) { // The section below assumes positive healing rate return; @@ -1951,7 +1956,7 @@ void Character::add_addiction( add_type type, int strength ) i.intensity++; } - add_msg_debug( "Updating addiction: %d intensity, %d sated", + add_msg_debug( debugmode::DF_CHAR_HEALTH, "Updating addiction: %d intensity, %d sated", i.intensity, to_turns( i.sated ) ); return; @@ -1959,10 +1964,10 @@ void Character::add_addiction( add_type type, int strength ) // Add a new addiction const int roll = rng( 0, 100 ); - add_msg_debug( "Addiction: roll %d vs strength %d", roll, strength ); + add_msg_debug( debugmode::DF_CHAR_HEALTH, "Addiction: roll %d vs strength %d", roll, strength ); if( roll < strength ) { const std::string &type_name = addiction_type_name( type ); - add_msg_debug( "%s got addicted to %s", disp_name(), type_name ); + add_msg_debug( debugmode::DF_CHAR_HEALTH, "%s got addicted to %s", disp_name(), type_name ); addictions.emplace_back( type, 1 ); get_event_bus().send( getID(), type ); } diff --git a/src/talker.h b/src/talker.h index 6d9baaab14cf0..a548cc95274d7 100644 --- a/src/talker.h +++ b/src/talker.h @@ -314,5 +314,18 @@ class talker virtual bool is_safe() const { return true; } + virtual void mod_pain( int ) {} + virtual int pain_cur() const { + return 0; + } + virtual bool worn_with_flag( const flag_id & ) const { + return false; + } + virtual bool wielded_with_flag( const flag_id & ) const { + return false; + } + virtual units::energy power_cur() const { + return 0_kJ; + } }; #endif // CATA_SRC_TALKER_H diff --git a/src/talker_avatar.cpp b/src/talker_avatar.cpp index 3dd0706c37dd0..75d303e70627f 100644 --- a/src/talker_avatar.cpp +++ b/src/talker_avatar.cpp @@ -31,7 +31,7 @@ std::vector talker_avatar::get_topics( bool ) std::vector add_topics; if( has_trait( trait_PROF_FOODP ) && !( is_wearing( itype_id( "foodperson_mask" ) ) || is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { - add_topics.push_back( "TALK_NOFACE" ); + add_topics.emplace_back( "TALK_NOFACE" ); } return add_topics; } @@ -76,7 +76,7 @@ void talker_avatar::buy_monster( talker &seller, const mtype_id &mtype, int cost for( int i = 0; i < count; i++ ) { monster *const mon_ptr = g->place_critter_around( mtype, me_chr->pos(), 3 ); if( !mon_ptr ) { - add_msg_debug( "Cannot place u_buy_monster, no valid placement locations." ); + add_msg_debug( debugmode::DF_TALKER, "Cannot place u_buy_monster, no valid placement locations." ); break; } monster &tmp = *mon_ptr; diff --git a/src/talker_character.cpp b/src/talker_character.cpp index 449e405a8c1ab..b233df97b98a0 100644 --- a/src/talker_character.cpp +++ b/src/talker_character.cpp @@ -292,3 +292,29 @@ void talker_character::shout( const std::string &speech, bool order ) { me_chr->shout( speech, order ); } + +int talker_character::pain_cur() const +{ + return me_chr->get_pain(); +} + +void talker_character::mod_pain( int amount ) +{ + me_chr->mod_pain( amount ); +} + +bool talker_character::worn_with_flag( const flag_id &flag ) const +{ + return me_chr->worn_with_flag( flag ); +} + +bool talker_character::wielded_with_flag( const flag_id &flag ) const +{ + return me_chr->weapon.has_flag( flag ); +} + +units::energy talker_character::power_cur() const +{ + return me_chr->get_power_level(); +} + diff --git a/src/talker_character.h b/src/talker_character.h index 6364f02329648..782c0ca41483b 100644 --- a/src/talker_character.h +++ b/src/talker_character.h @@ -55,6 +55,8 @@ class talker_character: public talker int dex_cur() const override; int int_cur() const override; int per_cur() const override; + int pain_cur() const override; + units::energy power_cur() const override; bool has_trait( const trait_id &trait_to_check ) const override; void set_mutation( const trait_id &new_trait ) override; void unset_mutation( const trait_id &old_trait ) override; @@ -108,6 +110,11 @@ class talker_character: public talker // speaking void shout( const std::string &speech = "", bool order = false ) override; + bool worn_with_flag( const flag_id &flag ) const override; + bool wielded_with_flag( const flag_id &flag ) const override; + + void mod_pain( int amount ) override; + protected: talker_character() = default; player *me_chr; diff --git a/src/talker_npc.cpp b/src/talker_npc.cpp index a1e107f65761b..ac51ee3df2fcd 100644 --- a/src/talker_npc.cpp +++ b/src/talker_npc.cpp @@ -108,19 +108,19 @@ std::vector talker_npc::get_topics( bool radio_contact ) add_topics.push_back( me_npc->chatbin.first_topic ); if( radio_contact ) { - add_topics.push_back( "TALK_RADIO" ); + add_topics.emplace_back( "TALK_RADIO" ); } else if( me_npc->is_leader() ) { - add_topics.push_back( "TALK_LEADER" ); + add_topics.emplace_back( "TALK_LEADER" ); } else if( me_npc->is_player_ally() && ( me_npc->is_walking_with() || me_npc->has_activity() ) ) { - add_topics.push_back( "TALK_FRIEND" ); + add_topics.emplace_back( "TALK_FRIEND" ); } else if( me_npc->get_attitude() == NPCATT_RECOVER_GOODS ) { - add_topics.push_back( "TALK_STOLE_ITEM" ); + add_topics.emplace_back( "TALK_STOLE_ITEM" ); } int most_difficult_mission = 0; for( auto &mission : me_npc->chatbin.missions ) { const auto &type = mission->get_type(); if( type.urgent && type.difficulty > most_difficult_mission ) { - add_topics.push_back( "TALK_MISSION_DESCRIBE_URGENT" ); + add_topics.emplace_back( "TALK_MISSION_DESCRIBE_URGENT" ); me_npc->chatbin.mission_selected = mission; most_difficult_mission = type.difficulty; } @@ -136,7 +136,7 @@ std::vector talker_npc::get_topics( bool radio_contact ) if( ( type.urgent && !chosen_urgent ) || ( type.difficulty > most_difficult_mission && ( type.urgent || !chosen_urgent ) ) ) { chosen_urgent = type.urgent; - add_topics.push_back( "TALK_MISSION_INQUIRE" ); + add_topics.emplace_back( "TALK_MISSION_INQUIRE" ); me_npc->chatbin.mission_selected = mission; most_difficult_mission = type.difficulty; } @@ -144,13 +144,13 @@ std::vector talker_npc::get_topics( bool radio_contact ) // Needs if( me_npc->has_effect( effect_npc_suspend ) ) { - add_topics.push_back( "TALK_REBOOT" ); + add_topics.emplace_back( "TALK_REBOOT" ); } if( me_npc->has_effect( effect_sleep ) || me_npc->has_effect( effect_lying_down ) ) { if( me_npc->has_effect( effect_narcosis ) ) { - add_topics.push_back( "TALK_SEDATED" ); + add_topics.emplace_back( "TALK_SEDATED" ); } else { - add_topics.push_back( "TALK_WAKE_UP" ); + add_topics.emplace_back( "TALK_WAKE_UP" ); } } @@ -163,25 +163,25 @@ std::vector talker_npc::get_topics( bool radio_contact ) if( add_topics.back() == "TALK_MUG" || add_topics.back() == "TALK_STRANGER_AGGRESSIVE" ) { me_npc->make_angry(); - add_topics.push_back( "TALK_DEAF_ANGRY" ); + add_topics.emplace_back( "TALK_DEAF_ANGRY" ); } else { - add_topics.push_back( "TALK_DEAF" ); + add_topics.emplace_back( "TALK_DEAF" ); } } if( player_character.is_mute() ) { if( add_topics.back() == "TALK_MUG" || add_topics.back() == "TALK_STRANGER_AGGRESSIVE" ) { me_npc->make_angry(); - add_topics.push_back( "TALK_MUTE_ANGRY" ); + add_topics.emplace_back( "TALK_MUTE_ANGRY" ); } else { - add_topics.push_back( "TALK_MUTE" ); + add_topics.emplace_back( "TALK_MUTE" ); } } if( me_npc->has_trait( trait_PROF_FOODP ) && !( me_npc->is_wearing( itype_id( "foodperson_mask_on" ) ) || me_npc->is_wearing( itype_id( "foodperson_mask" ) ) ) ) { - add_topics.push_back( "TALK_NPC_NOFACE" ); + add_topics.emplace_back( "TALK_NPC_NOFACE" ); } me_npc->decide_needs(); @@ -515,9 +515,9 @@ std::string talker_npc::give_item_to( const bool to_use ) int new_ammo = me_npc->ammo_count_for( given ); const double new_weapon_value = me_npc->weapon_value( given, new_ammo ); const double cur_weapon_value = me_npc->weapon_value( me_npc->weapon, our_ammo ); - add_msg_debug( "NPC evaluates own %s (%d ammo): %0.1f", + add_msg_debug( debugmode::DF_TALKER, "NPC evaluates own %s (%d ammo): %0.1f", me_npc->weapon.typeId().str(), our_ammo, cur_weapon_value ); - add_msg_debug( "NPC evaluates your %s (%d ammo): %0.1f", + add_msg_debug( debugmode::DF_TALKER, "NPC evaluates your %s (%d ammo): %0.1f", given.typeId().str(), new_ammo, new_weapon_value ); if( to_use ) { // Eating first, to avoid evaluating bread as a weapon diff --git a/src/teleport.cpp b/src/teleport.cpp index 6db51beaeca5f..f4835d28be499 100644 --- a/src/teleport.cpp +++ b/src/teleport.cpp @@ -40,7 +40,7 @@ bool teleport::teleport( Creature &critter, int min_distance, int max_distance, map &here = get_map(); //The teleportee is dimensionally anchored so nothing happens if( p && ( p->worn_with_flag( json_flag_DIMENSIONAL_ANCHOR ) || - p->has_effect_with_flag( json_flag_DIMENSIONAL_ANCHOR ) ) ) { + p->has_flag( json_flag_DIMENSIONAL_ANCHOR ) ) ) { p->add_msg_if_player( m_warning, _( "You feel a strange, inwards force." ) ); return false; } @@ -76,7 +76,7 @@ bool teleport::teleport( Creature &critter, int min_distance, int max_distance, } return false; } else if( poor_player && ( poor_player->worn_with_flag( json_flag_DIMENSIONAL_ANCHOR ) || - poor_player->has_effect_with_flag( json_flag_DIMENSIONAL_ANCHOR ) ) ) { + poor_player->has_flag( json_flag_DIMENSIONAL_ANCHOR ) ) ) { poor_player->add_msg_if_player( m_warning, _( "You feel disjointed." ) ); return false; } else { diff --git a/src/timed_event.cpp b/src/timed_event.cpp index 983f521d77bd9..cc3536bdd20ae 100644 --- a/src/timed_event.cpp +++ b/src/timed_event.cpp @@ -8,6 +8,8 @@ #include "avatar.h" #include "avatar_action.h" #include "character.h" +#include "coordinate_conversions.h" +#include "coordinates.h" #include "debug.h" #include "enums.h" #include "event.h" @@ -15,7 +17,9 @@ #include "game.h" #include "game_constants.h" #include "line.h" +#include "magic.h" #include "map.h" +#include "map_extras.h" #include "map_iterator.h" #include "mapdata.h" #include "memorial_logger.h" @@ -35,6 +39,7 @@ static const mtype_id mon_amigara_horror( "mon_amigara_horror" ); static const mtype_id mon_copbot( "mon_copbot" ); static const mtype_id mon_dark_wyrm( "mon_dark_wyrm" ); static const mtype_id mon_dermatik( "mon_dermatik" ); +static const mtype_id mon_dsa_alien_dispatch( "mon_dsa_alien_dispatch" ); static const mtype_id mon_eyebot( "mon_eyebot" ); static const mtype_id mon_riotbot( "mon_riotbot" ); static const mtype_id mon_sewer_snake( "mon_sewer_snake" ); @@ -241,6 +246,24 @@ void timed_event::actualize() } break; + case timed_event_type::DSA_ALRP_SUMMON: { + const tripoint u_pos = player_character.global_sm_location(); + if( rl_dist( u_pos, map_point ) <= 4 ) { + const tripoint spot = here.getlocal( sm_to_ms_copy( map_point ) ); + monster dispatcher( mon_dsa_alien_dispatch ); + fake_spell summoning( spell_id( "dks_summon_alrp" ), true, 12 ); + summoning.get_spell().cast_all_effects( dispatcher, spot ); + } else { + tinymap mx_map; + tripoint_abs_sm map_pt( map_point ); + mx_map.load( map_pt, false ); + MapExtras::apply_function( "mx_dsa_alrp", mx_map, map_point ); + g->load_npcs(); + here.invalidate_map_cache( map_point.z ); + } + } + break; + default: // Nothing happens for other events break; diff --git a/src/timed_event.h b/src/timed_event.h index d408fa06477c2..bbf04553d576e 100644 --- a/src/timed_event.h +++ b/src/timed_event.h @@ -20,6 +20,7 @@ enum class timed_event_type : int { TEMPLE_SPAWN, DIM, ARTIFACT_LIGHT, + DSA_ALRP_SUMMON, NUM_TIMED_EVENT_TYPES }; diff --git a/src/trait_group.cpp b/src/trait_group.cpp index 4100a4f21e7f4..386914d0b6616 100644 --- a/src/trait_group.cpp +++ b/src/trait_group.cpp @@ -276,7 +276,6 @@ void Trait_group_collection::add_entry( std::unique_ptr ptr ptr->probability = std::min( 100, ptr->probability ); creators.push_back( std::move( ptr ) ); - ptr.release(); } void Trait_group_distribution::add_entry( std::unique_ptr ptr ) @@ -289,7 +288,6 @@ void Trait_group_distribution::add_entry( std::unique_ptr p sum_prob += ptr->probability; creators.push_back( std::move( ptr ) ); - ptr.release(); } Trait_list Trait_group_distribution::create( RecursionList &rec ) const diff --git a/src/translations.cpp b/src/translations.cpp index fa0ee479b2c93..645e1fc59ae43 100644 --- a/src/translations.cpp +++ b/src/translations.cpp @@ -434,7 +434,7 @@ std::string gettext_gendered( const GenderMap &genders, const std::string &msg ) sanity_check_genders( language_genders ); if( language_genders.empty() ) { - language_genders.push_back( "n" ); + language_genders.emplace_back( "n" ); } std::vector chosen_genders; diff --git a/src/translations.h b/src/translations.h index 63061314389c0..6f63bbe0df496 100644 --- a/src/translations.h +++ b/src/translations.h @@ -92,12 +92,16 @@ class local_translation_cache std::string cached_translation; public: const std::string &operator()( const std::string &arg ) { +#ifndef CATA_IN_TOOL if( cached_lang_version != get_current_language_version() || cached_arg != arg ) { cached_lang_version = get_current_language_version(); cached_arg = arg; cached_translation = _translate_internal( arg ); } return cached_translation; +#else + return arg; +#endif } }; @@ -111,6 +115,7 @@ class local_translation_cache const char *cached_translation = nullptr; public: const char *operator()( const char *arg ) { +#ifndef CATA_IN_TOOL if( cached_lang_version != get_current_language_version() || cached_arg != arg ) { cached_lang_version = get_current_language_version(); cached_translation = _translate_internal( arg ); @@ -120,6 +125,9 @@ class local_translation_cache // mimic gettext() behavior: return `arg` if no translation is found // `same_as_arg` is needed to ensure that the current `arg` is returned (not a cached one) return same_as_arg ? arg : cached_translation; +#else + return arg; +#endif } }; diff --git a/src/trap.cpp b/src/trap.cpp index d40d52ee58d58..3eb7195b13042 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -110,6 +110,17 @@ void trap::load( const JsonObject &jo, const std::string & ) mandatory( jo, was_loaded, "visibility", visibility ); mandatory( jo, was_loaded, "avoidance", avoidance ); mandatory( jo, was_loaded, "difficulty", difficulty ); + + optional( jo, was_loaded, "memorial_male", memorial_male ); + optional( jo, was_loaded, "memorial_female", memorial_female ); + + // Require either none, or both + if( !!memorial_male != !!memorial_female ) { + jo.throw_error( "Only one gender of memorial message specified for trap %s, but none or both required.", + id.str() ); + } + + optional( jo, was_loaded, "flags", _flags ); optional( jo, was_loaded, "trap_radius", trap_radius, 0 ); // TODO: Is there a generic_factory version of this? act = trap_function_from_string( jo.get_string( "action" ) ); @@ -195,8 +206,8 @@ bool trap::is_trivial_to_spot() const bool trap::detected_by_ground_sonar() const { - // @TODO make this a property - return loadid == tr_beartrap_buried || loadid == tr_landmine_buried || loadid == tr_sinkhole; + static const flag_id sonar_detectable = flag_id( "SONAR_DETECTABLE" ); + return has_flag( sonar_detectable ); } bool trap::detect_trap( const tripoint &pos, const Character &p ) const @@ -322,29 +333,28 @@ void trap::on_disarmed( map &m, const tripoint &p ) const ////////////////////////// // convenient int-lookup names for hard-coded functions -trap_id -tr_null, -tr_beartrap_buried, -tr_shotgun_2, -tr_shotgun_1, -tr_blade, -tr_landmine, -tr_landmine_buried, -tr_telepad, -tr_goo, -tr_dissector, -tr_sinkhole, -tr_pit, -tr_lava, -tr_portal, -tr_ledge, -tr_temple_flood, -tr_temple_toggle, -tr_glow, -tr_hum, -tr_shadow, -tr_drain, -tr_snake; +trap_id tr_null; +const trap_str_id tr_beartrap_buried( "tr_beartrap_buried" ); +const trap_str_id tr_shotgun_2( "tr_shotgun_2" ); +const trap_str_id tr_shotgun_1( "tr_shotgun_1" ); +const trap_str_id tr_blade( "tr_blade" ); +const trap_str_id tr_landmine( "tr_landmine" ); +const trap_str_id tr_landmine_buried( "tr_landmine_buried" ); +const trap_str_id tr_telepad( "tr_telepad" ); +const trap_str_id tr_goo( "tr_goo" ); +const trap_str_id tr_dissector( "tr_dissector" ); +const trap_str_id tr_sinkhole( "tr_sinkhole" ); +const trap_str_id tr_pit( "tr_pit" ); +const trap_str_id tr_lava( "tr_lava" ); +const trap_str_id tr_portal( "tr_portal" ); +const trap_str_id tr_ledge( "tr_ledge" ); +const trap_str_id tr_temple_flood( "tr_temple_flood" ); +const trap_str_id tr_temple_toggle( "tr_temple_toggle" ); +const trap_str_id tr_glow( "tr_glow" ); +const trap_str_id tr_hum( "tr_hum" ); +const trap_str_id tr_shadow( "tr_shadow" ); +const trap_str_id tr_drain( "tr_drain" ); +const trap_str_id tr_snake( "tr_snake" ); void trap::check_consistency() { @@ -378,31 +388,8 @@ void trap::finalize() funnel_traps.push_back( &t ); } } - const auto trapfind = []( const char *id ) { - return trap_str_id( id ).id(); - }; + tr_null = trap_str_id::NULL_ID().id(); - tr_beartrap_buried = trapfind( "tr_beartrap_buried" ); - tr_shotgun_2 = trapfind( "tr_shotgun_2" ); - tr_shotgun_1 = trapfind( "tr_shotgun_1" ); - tr_blade = trapfind( "tr_blade" ); - tr_landmine = trapfind( "tr_landmine" ); - tr_landmine_buried = trapfind( "tr_landmine_buried" ); - tr_telepad = trapfind( "tr_telepad" ); - tr_goo = trapfind( "tr_goo" ); - tr_dissector = trapfind( "tr_dissector" ); - tr_sinkhole = trapfind( "tr_sinkhole" ); - tr_pit = trapfind( "tr_pit" ); - tr_lava = trapfind( "tr_lava" ); - tr_portal = trapfind( "tr_portal" ); - tr_ledge = trapfind( "tr_ledge" ); - tr_temple_flood = trapfind( "tr_temple_flood" ); - tr_temple_toggle = trapfind( "tr_temple_toggle" ); - tr_glow = trapfind( "tr_glow" ); - tr_hum = trapfind( "tr_hum" ); - tr_shadow = trapfind( "tr_shadow" ); - tr_drain = trapfind( "tr_drain" ); - tr_snake = trapfind( "tr_snake" ); } std::string trap::debug_describe() const diff --git a/src/trap.h b/src/trap.h index 295d45eb92c33..91f7f55e728d9 100644 --- a/src/trap.h +++ b/src/trap.h @@ -137,6 +137,12 @@ struct trap { std::string map_regen; trap_function act; translation name_; + + cata::optional memorial_male; + cata::optional memorial_female; + + cata::flat_set _flags; + /** * If an item with this weight or more is thrown onto the trap, it triggers. */ @@ -166,6 +172,21 @@ struct trap { return loadid != id; } + bool has_flag( const flag_id &flag ) const { + return _flags.count( flag ); + } + + bool has_memorial_msg() const { + return memorial_male && memorial_female; + } + + std::string memorial_msg( bool male ) const { + if( male ) { + return memorial_male->translated(); + } + return memorial_female->translated(); + } + /** * Called when the player examines a tile. This is supposed to handled * all kind of interaction of the player with the trap, including removal. @@ -322,28 +343,27 @@ struct trap { const trap_function &trap_function_from_string( const std::string &function_name ); -extern trap_id -tr_null, -tr_beartrap_buried, -tr_shotgun_2, -tr_shotgun_1, -tr_blade, -tr_landmine, -tr_landmine_buried, -tr_telepad, -tr_goo, -tr_dissector, -tr_sinkhole, -tr_pit, -tr_lava, -tr_portal, -tr_ledge, -tr_temple_flood, -tr_temple_toggle, -tr_glow, -tr_hum, -tr_shadow, -tr_drain, -tr_snake; +extern trap_id tr_null; +extern const trap_str_id tr_beartrap_buried; +extern const trap_str_id tr_shotgun_2; +extern const trap_str_id tr_shotgun_1; +extern const trap_str_id tr_blade; +extern const trap_str_id tr_landmine; +extern const trap_str_id tr_landmine_buried; +extern const trap_str_id tr_telepad; +extern const trap_str_id tr_goo; +extern const trap_str_id tr_dissector; +extern const trap_str_id tr_sinkhole; +extern const trap_str_id tr_pit; +extern const trap_str_id tr_lava; +extern const trap_str_id tr_portal; +extern const trap_str_id tr_ledge; +extern const trap_str_id tr_temple_flood; +extern const trap_str_id tr_temple_toggle; +extern const trap_str_id tr_glow; +extern const trap_str_id tr_hum; +extern const trap_str_id tr_shadow; +extern const trap_str_id tr_drain; +extern const trap_str_id tr_snake; #endif // CATA_SRC_TRAP_H diff --git a/src/try_parse_integer.cpp b/src/try_parse_integer.cpp new file mode 100644 index 0000000000000..a4187920fa72b --- /dev/null +++ b/src/try_parse_integer.cpp @@ -0,0 +1,45 @@ +#include "try_parse_integer.h" + +#include + +#include "translations.h" + +template +ret_val try_parse_integer( const std::string &s, bool use_locale ) +{ + // Using stringstream-based parsing because that's the only one in the + // standard library for which it's possible to turn off the + // locale-dependency. Once we're using C++17 we could use a combination of + // std::from_chars and std::strto* functions, but this should be fine so + // long as the code is not performance-critical. + std::istringstream buffer( s ); +#ifdef __APPLE__ + // On Apple platforms we always use the classic locale, because the other + // locales seem to behave strangely. See + // https://github.com/CleverRaven/Cataclysm-DDA/pull/48431 for more + // discussion. + static_cast( use_locale ); + buffer.imbue( std::locale::classic() ); +#else + if( !use_locale ) { + buffer.imbue( std::locale::classic() ); + } +#endif + T result; + buffer >> result; + if( !buffer ) { + return ret_val::make_failure( + 0, string_format( _( "Could not convert '%s' to an integer" ), s ) ); + } + char c; + buffer >> c; + if( buffer ) { + return ret_val::make_failure( + 0, string_format( _( "Stray characters after integer in '%s'" ), s ) ); + } + return ret_val::make_success( result ); +} + +template ret_val try_parse_integer( const std::string &, bool use_locale ); +template ret_val try_parse_integer( const std::string &, bool use_locale ); +template ret_val try_parse_integer( const std::string &, bool use_locale ); diff --git a/src/try_parse_integer.h b/src/try_parse_integer.h new file mode 100644 index 0000000000000..64d3bfe516108 --- /dev/null +++ b/src/try_parse_integer.h @@ -0,0 +1,23 @@ +#pragma once +#ifndef CATA_SRC_TRY_PARSE_INTEGER_H +#define CATA_SRC_TRY_PARSE_INTEGER_H + +#include "ret_val.h" + +/** + * Convert a string to an integer or provide an error message indicating what + * went wrong in trying to do so. + * + * @param use_locale Whether to use a number-parsing function that might permit + * formats specific to the user's locale. Generally would be true for + * player-input values, and false for values from e.g. game data files. + */ +template +ret_val try_parse_integer( const std::string &, bool use_locale ); + +extern template ret_val try_parse_integer( const std::string &, bool use_locale ); +extern template ret_val try_parse_integer( const std::string &, bool use_locale ); +extern template ret_val try_parse_integer( + const std::string &, bool use_locale ); + +#endif // CATA_SRC_TRY_PARSE_INTEGER_H diff --git a/src/type_id.h b/src/type_id.h index b1f9ee6b4b2c7..363f8ae8bb117 100644 --- a/src/type_id.h +++ b/src/type_id.h @@ -36,6 +36,9 @@ using construction_group_str_id = string_id; struct clothing_mod; using clothing_mod_id = string_id; +struct effect_on_condition; +using effect_on_condition_id = string_id; + class effect_type; using efftype_id = string_id; diff --git a/src/ui.cpp b/src/ui.cpp index d99d07439716f..ff634f4eba8ac 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -62,74 +62,74 @@ static cata::optional hotkey_from_char( const int ch ) return input_event(); } -uilist_entry::uilist_entry( const std::string &T ) - : retval( -1 ), enabled( true ), hotkey( cata::nullopt ), txt( T ), +uilist_entry::uilist_entry( const std::string &txt ) + : retval( -1 ), enabled( true ), hotkey( cata::nullopt ), txt( txt ), text_color( c_red_red ) { } -uilist_entry::uilist_entry( const std::string &T, const std::string &D ) - : retval( -1 ), enabled( true ), hotkey( cata::nullopt ), txt( T ), - desc( D ), text_color( c_red_red ) +uilist_entry::uilist_entry( const std::string &txt, const std::string &desc ) + : retval( -1 ), enabled( true ), hotkey( cata::nullopt ), txt( txt ), + desc( desc ), text_color( c_red_red ) { } -uilist_entry::uilist_entry( const std::string &T, const int K ) - : retval( -1 ), enabled( true ), hotkey( hotkey_from_char( K ) ), txt( T ), +uilist_entry::uilist_entry( const std::string &txt, const int key ) + : retval( -1 ), enabled( true ), hotkey( hotkey_from_char( key ) ), txt( txt ), text_color( c_red_red ) { } -uilist_entry::uilist_entry( const std::string &T, const cata::optional &K ) - : retval( -1 ), enabled( true ), hotkey( K ), txt( T ), +uilist_entry::uilist_entry( const std::string &txt, const cata::optional &key ) + : retval( -1 ), enabled( true ), hotkey( key ), txt( txt ), text_color( c_red_red ) { } -uilist_entry::uilist_entry( const int R, const bool E, const int K, - const std::string &T ) - : retval( R ), enabled( E ), hotkey( hotkey_from_char( K ) ), txt( T ), +uilist_entry::uilist_entry( const int retval, const bool enabled, const int key, + const std::string &txt ) + : retval( retval ), enabled( enabled ), hotkey( hotkey_from_char( key ) ), txt( txt ), text_color( c_red_red ) { } -uilist_entry::uilist_entry( const int R, const bool E, - const cata::optional &K, - const std::string &T ) - : retval( R ), enabled( E ), hotkey( K ), txt( T ), +uilist_entry::uilist_entry( const int retval, const bool enabled, + const cata::optional &key, + const std::string &txt ) + : retval( retval ), enabled( enabled ), hotkey( key ), txt( txt ), text_color( c_red_red ) { } -uilist_entry::uilist_entry( const int R, const bool E, const int K, - const std::string &T, const std::string &D ) - : retval( R ), enabled( E ), hotkey( hotkey_from_char( K ) ), txt( T ), - desc( D ), text_color( c_red_red ) +uilist_entry::uilist_entry( const int retval, const bool enabled, const int key, + const std::string &txt, const std::string &desc ) + : retval( retval ), enabled( enabled ), hotkey( hotkey_from_char( key ) ), txt( txt ), + desc( desc ), text_color( c_red_red ) { } -uilist_entry::uilist_entry( const int R, const bool E, const int K, - const std::string &T, const std::string &D, - const std::string &C ) - : retval( R ), enabled( E ), hotkey( hotkey_from_char( K ) ), txt( T ), - desc( D ), ctxt( C ), text_color( c_red_red ) +uilist_entry::uilist_entry( const int retval, const bool enabled, const int key, + const std::string &txt, const std::string &desc, + const std::string &column ) + : retval( retval ), enabled( enabled ), hotkey( hotkey_from_char( key ) ), txt( txt ), + desc( desc ), ctxt( column ), text_color( c_red_red ) { } -uilist_entry::uilist_entry( const int R, const bool E, - const cata::optional &K, - const std::string &T, const std::string &D, - const std::string &C ) - : retval( R ), enabled( E ), hotkey( K ), txt( T ), - desc( D ), ctxt( C ), text_color( c_red_red ) +uilist_entry::uilist_entry( const int retval, const bool enabled, + const cata::optional &key, + const std::string &txt, const std::string &desc, + const std::string &column ) + : retval( retval ), enabled( enabled ), hotkey( key ), txt( txt ), + desc( desc ), ctxt( column ), text_color( c_red_red ) { } -uilist_entry::uilist_entry( const int R, const bool E, const int K, - const std::string &T, - const nc_color &H, const nc_color &C ) - : retval( R ), enabled( E ), hotkey( hotkey_from_char( K ) ), txt( T ), - hotkey_color( H ), text_color( C ) +uilist_entry::uilist_entry( const int retval, const bool enabled, const int key, + const std::string &txt, + const nc_color &keycolor, const nc_color &txtcolor ) + : retval( retval ), enabled( enabled ), hotkey( hotkey_from_char( key ) ), txt( txt ), + hotkey_color( keycolor ), text_color( txtcolor ) { } @@ -709,11 +709,10 @@ void uilist::show() // to be used. const utf8_wrapper entry = utf8_wrapper( ei == selected ? remove_color_tags( entries[ ei ].txt ) : entries[ ei ].txt ); - int x = pad_left + 4; - int y = estart + si; - entries[ei].drawn_rect.p_min = point( x, y ); - entries[ei].drawn_rect.p_max = point( x + max_entry_len - 1, y ); - trim_and_print( window, point( x, y ), max_entry_len, + point p( pad_left + 4, estart + si ); + entries[ei].drawn_rect.p_min = p; + entries[ei].drawn_rect.p_max = p + point( -1 + max_entry_len, 0 ); + trim_and_print( window, p, max_entry_len, co, _color_error, "%s", entry.str() ); if( max_column_len && !entries[ ei ].ctxt.empty() ) { @@ -997,45 +996,47 @@ void uilist::reset() init(); } -void uilist::addentry( const std::string &str ) +void uilist::addentry( const std::string &txt ) { - entries.emplace_back( str ); + entries.emplace_back( txt ); } -void uilist::addentry( int r, bool e, int k, const std::string &str ) +void uilist::addentry( int retval, bool enabled, int key, const std::string &txt ) { - entries.emplace_back( r, e, k, str ); + entries.emplace_back( retval, enabled, key, txt ); } -void uilist::addentry( const int r, const bool e, - const cata::optional &k, - const std::string &str ) +void uilist::addentry( const int retval, const bool enabled, + const cata::optional &key, + const std::string &txt ) { - entries.emplace_back( r, e, k, str ); + entries.emplace_back( retval, enabled, key, txt ); } -void uilist::addentry_desc( const std::string &str, const std::string &desc ) +void uilist::addentry_desc( const std::string &txt, const std::string &desc ) { - entries.emplace_back( str, desc ); + entries.emplace_back( txt, desc ); } -void uilist::addentry_desc( int r, bool e, int k, const std::string &str, const std::string &desc ) +void uilist::addentry_desc( int retval, bool enabled, int key, const std::string &txt, + const std::string &desc ) { - entries.emplace_back( r, e, k, str, desc ); + entries.emplace_back( retval, enabled, key, txt, desc ); } -void uilist::addentry_col( int r, bool e, int k, const std::string &str, const std::string &column, +void uilist::addentry_col( int retval, bool enabled, int key, const std::string &txt, + const std::string &column, const std::string &desc ) { - entries.emplace_back( r, e, k, str, desc, column ); + entries.emplace_back( retval, enabled, key, txt, desc, column ); } -void uilist::addentry_col( const int r, const bool e, - const cata::optional &k, - const std::string &str, const std::string &column, +void uilist::addentry_col( const int retval, const bool enabled, + const cata::optional &key, + const std::string &txt, const std::string &column, const std::string &desc ) { - entries.emplace_back( r, e, k, str, desc, column ); + entries.emplace_back( retval, enabled, key, txt, desc, column ); } void uilist::settext( const std::string &str ) diff --git a/src/ui.h b/src/ui.h index 4677604374f79..8129a0d9e4ccc 100644 --- a/src/ui.h +++ b/src/ui.h @@ -70,24 +70,87 @@ struct uilist_entry { nc_color text_color; mvwzstr extratxt; - // In the following constructors, int K only support letters (a-z, A-Z) and + // In the following constructors, int key only support letters (a-z, A-Z) and // digits (0-9), MENU_AUTOASSIGN, and 0 or ' ' (disable hotkey). Other // values may not work under keycode mode. - explicit uilist_entry( const std::string &T ); - uilist_entry( const std::string &T, const std::string &D ); - uilist_entry( const std::string &T, int K ); - uilist_entry( const std::string &T, const cata::optional &K ); - uilist_entry( int R, bool E, int K, const std::string &T ); - uilist_entry( int R, bool E, const cata::optional &K, - const std::string &T ); - uilist_entry( int R, bool E, int K, const std::string &T, const std::string &D ); - uilist_entry( int R, bool E, int K, const std::string &T, const std::string &D, - const std::string &C ); - uilist_entry( int R, bool E, const cata::optional &K, - const std::string &T, const std::string &D, - const std::string &C ); - uilist_entry( int R, bool E, int K, const std::string &T, - const nc_color &H, const nc_color &C ); + + /** + * @param txt string that will be displayed on the entry first column + */ + explicit uilist_entry( const std::string &txt ); + /** + * @param txt string that will be displayed on the entry first column + * @param desc entry description if menu desc_enabled is true + * @see uilist::desc_enabled + */ + uilist_entry( const std::string &txt, const std::string &desc ); + /** + * @param txt string that will be displayed on the entry first column + * @param key hotkey character that when pressed will return this entry return value + */ + uilist_entry( const std::string &txt, int key ); + /** + * @param txt string that will be displayed on the entry first column + * @param key hotkey character that when pressed will return this entry return value + */ + uilist_entry( const std::string &txt, const cata::optional &key ); + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + */ + uilist_entry( int retval, bool enabled, int key, const std::string &txt ); + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + */ + uilist_entry( int retval, bool enabled, const cata::optional &key, + const std::string &txt ); + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + * @param desc entry description if menu desc_enabled is true + * @see uilist::desc_enabled + */ + uilist_entry( int retval, bool enabled, int key, const std::string &txt, const std::string &desc ); + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + * @param desc entry description if menu desc_enabled is true + * @param column string that will be displayed on the entry second column + * @see uilist::desc_enabled + */ + uilist_entry( int retval, bool enabled, int key, const std::string &txt, const std::string &desc, + const std::string &column ); + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + * @param desc entry description if menu desc_enabled is true + * @param column string that will be displayed on the entry second column + * @see uilist::desc_enabled + */ + uilist_entry( int retval, bool enabled, const cata::optional &key, + const std::string &txt, const std::string &desc, + const std::string &column ); + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + * @param keycolor color of the hotkey character + * @param txtcolor entry text string color + */ + uilist_entry( int retval, bool enabled, int key, const std::string &txt, + const nc_color &keycolor, const nc_color &txtcolor ); template::value>> explicit uilist_entry( Enum e, Args && ... args ) : @@ -214,21 +277,78 @@ class uilist // NOLINT(cata-xy) // In add_entry/add_entry_desc/add_entry_col, int k only support letters // (a-z, A-Z) and digits (0-9), MENU_AUTOASSIGN, and 0 or ' ' (disable // hotkey). Other values may not work under keycode mode. - void addentry( const std::string &str ); - void addentry( int r, bool e, int k, const std::string &str ); - void addentry( int r, bool e, const cata::optional &k, - const std::string &str ); + + /** + * @param txt string that will be displayed on the entry first column + */ + void addentry( const std::string &txt ); + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + */ + void addentry( int retval, bool enabled, int key, const std::string &txt ); + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + */ + void addentry( int retval, bool enabled, const cata::optional &key, + const std::string &txt ); + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + * @param args list of parameters for string_format to format txt + */ template - void addentry( const int r, const bool e, K &&k, const char *const format, Args &&... args ) { - return addentry( r, e, std::forward( k ), + void addentry( const int retval, const bool enabled, K &&key, const char *const format, + Args &&... args ) { + return addentry( retval, enabled, std::forward( key ), string_format( format, std::forward( args )... ) ); } - void addentry_desc( const std::string &str, const std::string &desc ); - void addentry_desc( int r, bool e, int k, const std::string &str, const std::string &desc ); - void addentry_col( int r, bool e, int k, const std::string &str, const std::string &column, + /** + * @param txt string that will be displayed on the entry first column + * @param desc entry description if menu desc_enabled is true + * @see uilist::desc_enabled + */ + void addentry_desc( const std::string &txt, const std::string &desc ); + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + * @param desc entry description if menu desc_enabled is true + * @see uilist::desc_enabled + */ + void addentry_desc( int retval, bool enabled, int key, const std::string &txt, + const std::string &desc ); + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + * @param column string that will be displayed on the entry second column + * @param desc entry description if menu desc_enabled is true + * @see uilist::desc_enabled + */ + void addentry_col( int retval, bool enabled, int key, const std::string &txt, + const std::string &column, const std::string &desc = "" ); - void addentry_col( int r, bool e, const cata::optional &k, - const std::string &str, const std::string &column, + /** + * @param retval return value of this option when selected during menu query + * @param enable is entry enabled. disabled entries will be grayed out and won't be selectable + * @param key hotkey character that when pressed will return this entry return value + * @param txt string that will be displayed on the entry first column + * @param column string that will be displayed on the entry second column + * @param desc entry description if menu desc_enabled is true + * @see uilist::desc_enabled + */ + void addentry_col( int retval, bool enabled, const cata::optional &key, + const std::string &txt, const std::string &column, const std::string &desc = std::string() ); void settext( const std::string &str ); diff --git a/src/uistate.h b/src/uistate.h index f383a48c32163..81e1ae74e36f0 100644 --- a/src/uistate.h +++ b/src/uistate.h @@ -114,6 +114,10 @@ class uistatedata bool overmap_show_city_labels = true; bool overmap_show_hordes = true; bool overmap_show_forest_trails = true; + bool overmap_visible_weather = false; + bool overmap_debug_weather = false; + // draw monster groups on the overmap. + bool overmap_debug_mongroup = false; // V Menu Stuff int list_item_sort = 0; @@ -200,6 +204,8 @@ class uistatedata json.member( "favorite_recipes", favorite_recipes ); json.member( "recent_recipes", recent_recipes ); json.member( "bionic_ui_sort_mode", bionic_sort_mode ); + json.member( "overmap_debug_weather", overmap_debug_weather ); + json.member( "overmap_visible_weather", overmap_visible_weather ); json.member( "input_history" ); json.start_object(); @@ -246,6 +252,8 @@ class uistatedata jo.read( "favorite_recipes", favorite_recipes ); jo.read( "recent_recipes", recent_recipes ); jo.read( "bionic_ui_sort_mode", bionic_sort_mode ); + jo.read( "overmap_debug_weather", overmap_debug_weather ); + jo.read( "overmap_visible_weather", overmap_visible_weather ); if( !jo.read( "vmenu_show_items", vmenu_show_items ) ) { // This is an old save: 1 means view items, 2 means view monsters, diff --git a/src/value_ptr.h b/src/value_ptr.h index 5ade27a90c547..1266c7c7c619a 100644 --- a/src/value_ptr.h +++ b/src/value_ptr.h @@ -19,13 +19,13 @@ class value_ptr : public std::unique_ptr { public: value_ptr() = default; - value_ptr( value_ptr && ) = default; + value_ptr( value_ptr && ) noexcept = default; // NOLINTNEXTLINE(google-explicit-constructor) value_ptr( std::nullptr_t ) {} explicit value_ptr( T *value ) : std::unique_ptr( value ) {} value_ptr( const value_ptr &other ) : std::unique_ptr( other ? new T( *other ) : nullptr ) {} - value_ptr &operator=( value_ptr other ) { + value_ptr &operator=( value_ptr other ) noexcept { std::unique_ptr::operator=( std::move( other ) ); return *this; } diff --git a/src/veh_interact.cpp b/src/veh_interact.cpp index f834e953dfda0..8c207deaf0172 100644 --- a/src/veh_interact.cpp +++ b/src/veh_interact.cpp @@ -912,15 +912,15 @@ void veh_interact::do_install() for( const vpart_category &cat : vpart_category::all() ) { tab_list.push_back( cat ); if( cat.get_id() == "_all" ) { - tab_filters.push_back( []( const vpart_info * ) { + tab_filters.emplace_back( []( const vpart_info * ) { return true; } ); } else if( cat.get_id() == "_filter" ) { - tab_filters.push_back( [&filter]( const vpart_info * p ) { + tab_filters.emplace_back( [&filter]( const vpart_info * p ) { return lcmatch( p->name(), filter ); } ); } else { - tab_filters.push_back( [ &, cat = cat.get_id()]( const vpart_info * p ) { + tab_filters.emplace_back( [ &, cat = cat.get_id()]( const vpart_info * p ) { return p->has_category( cat ); } ); } @@ -1459,7 +1459,7 @@ void veh_interact::calc_overview() int offset = 1; std::string fmtstring = "%s %s %5.1fL"; if( pt.is_leaking() ) { - fmtstring = "%s %s " + leak_marker + "%5.1fL" + leak_marker; + fmtstring = str_cat( "%s %s ", leak_marker, "%5.1fL", leak_marker ); offset = 0; } right_print( w, y, offset, pt_ammo_cur->color, @@ -1467,7 +1467,7 @@ void veh_interact::calc_overview() round_up( units::to_liter( it.volume() ), 1 ) ) ); } else { if( pt.is_leaking() ) { - std::string outputstr = leak_marker + " " + leak_marker; + std::string outputstr = str_cat( leak_marker, " ", leak_marker ); right_print( w, y, 0, c_light_gray, outputstr ); } } @@ -1480,7 +1480,7 @@ void veh_interact::calc_overview() int offset = 1; std::string fmtstring = "%s %5.1fL"; if( pt.is_leaking() ) { - fmtstring = "%s " + leak_marker + "%5.1fL" + leak_marker; + fmtstring = str_cat( "%s ", leak_marker, "%5.1fL", leak_marker ); offset = 0; } right_print( w, y, offset, pt_ammo_cur->color, @@ -1507,7 +1507,7 @@ void veh_interact::calc_overview() int offset = 1; std::string fmtstring = "%i %3i%%"; if( pt.is_leaking() ) { - fmtstring = "%i " + leak_marker + "%3i%%" + leak_marker; + fmtstring = str_cat( "%i ", leak_marker, "%3i%%", leak_marker ); offset = 0; } right_print( w, y, offset, item::find_type( pt.ammo_current() )->color, @@ -1522,7 +1522,7 @@ void veh_interact::calc_overview() int offset = 1; std::string fmtstring = "%s %5i"; if( pt.is_leaking() ) { - fmtstring = "%s " + leak_marker + "%5i" + leak_marker; + fmtstring = str_cat( "%s ", leak_marker, "%5i", leak_marker ); offset = 0; } right_print( w, y, offset, item::find_type( pt.ammo_current() )->color, @@ -2383,7 +2383,9 @@ void veh_interact::display_stats() const const int extraw = ( ( TERMX - FULL_SCREEN_WIDTH ) / 4 ) * 2; // 3 * stats_h const int slots = 24; - int x[slots], y[slots], w[slots]; + int x[slots]; + int y[slots]; + int w[slots]; units::volume total_cargo = 0_ml; units::volume free_cargo = 0_ml; diff --git a/src/veh_type.cpp b/src/veh_type.cpp index 504f2875516d0..5398ae4fb1e69 100644 --- a/src/veh_type.cpp +++ b/src/veh_type.cpp @@ -267,7 +267,7 @@ void vpart_info::load_engine( cata::optional &eptr, const JsonObj if( !fuel_opts.empty() ) { e_info.fuel_opts.clear(); for( const std::string line : fuel_opts ) { - e_info.fuel_opts.push_back( itype_id( line ) ); + e_info.fuel_opts.emplace_back( line ); } } else if( e_info.fuel_opts.empty() && fuel_type != itype_id( "null" ) ) { e_info.fuel_opts.push_back( fuel_type ); @@ -758,6 +758,15 @@ void vpart_info::check() debugmsg( "vehicle part %s has the WHEEL flag, but base item %s is not a wheel. " "THIS WILL CRASH!", part.id.str(), part.base_item.str() ); } + + if( part.has_flag( "WHEEL" ) && !part.has_flag( "UNSTABLE_WHEEL" ) && !part.has_flag( "STABLE" ) ) { + debugmsg( "Wheel '%s' lacks either 'UNSTABLE_WHEEL' or 'STABLE' flag.", vp.first.str() ); + } + + if( part.has_flag( "UNSTABLE_WHEEL" ) && part.has_flag( "STABLE" ) ) { + debugmsg( "Wheel '%s' cannot be both an 'UNSTABLE_WHEEL' and 'STABLE'.", vp.first.str() ); + } + for( auto &q : part.qualities ) { if( !q.first.is_valid() ) { debugmsg( "vehicle part %s has undefined tool quality %s", part.id.c_str(), q.first.c_str() ); @@ -1065,10 +1074,11 @@ bool string_id::is_valid() const } vehicle_prototype::vehicle_prototype() = default; -vehicle_prototype::vehicle_prototype( vehicle_prototype && ) = default; +vehicle_prototype::vehicle_prototype( vehicle_prototype && ) noexcept = default; vehicle_prototype::~vehicle_prototype() = default; -vehicle_prototype &vehicle_prototype::operator=( vehicle_prototype && ) = default; +vehicle_prototype &vehicle_prototype::operator=( vehicle_prototype && + ) noexcept( string_is_noexcept ) = default; /** *Caches a vehicle definition from a JsonObject to be loaded after itypes is initialized. @@ -1158,16 +1168,21 @@ void vehicle_prototype::load( const JsonObject &jo ) spawn_info.read( "items", next_spawn.item_ids, true ); } else if( spawn_info.has_string( "items" ) ) { //Treat single item as array - next_spawn.item_ids.push_back( itype_id( spawn_info.get_string( "items" ) ) ); + // And read the gun variant (if it exists) + if( spawn_info.has_string( "variant" ) ) { + const std::string variant = spawn_info.get_string( "variant" ); + next_spawn.variant_ids.emplace_back( itype_id( spawn_info.get_string( "items" ) ), variant ); + } else { + next_spawn.item_ids.emplace_back( spawn_info.get_string( "items" ) ); + } } if( spawn_info.has_array( "item_groups" ) ) { //Pick from a group of items, just like map::place_items for( const std::string line : spawn_info.get_array( "item_groups" ) ) { - next_spawn.item_groups.push_back( item_group_id( line ) ); + next_spawn.item_groups.emplace_back( line ); } } else if( spawn_info.has_string( "item_groups" ) ) { - next_spawn.item_groups.push_back( - item_group_id( spawn_info.get_string( "item_groups" ) ) ); + next_spawn.item_groups.emplace_back( spawn_info.get_string( "item_groups" ) ); } vproto.item_spawns.push_back( std::move( next_spawn ) ); } diff --git a/src/veh_type.h b/src/veh_type.h index 475bd7b5cc5b9..1319c7c21e815 100644 --- a/src/veh_type.h +++ b/src/veh_type.h @@ -16,6 +16,7 @@ #include "calendar.h" #include "color.h" +#include "compatibility.h" #include "damage.h" #include "optional.h" #include "point.h" @@ -474,6 +475,8 @@ struct vehicle_item_spawn { /** Chance [0-100%] for items to spawn with their default magazine (if any) */ int with_magazine = 0; std::vector item_ids; + // item_ids, but for items with variants specified + std::vector> variant_ids; std::vector item_groups; }; @@ -493,10 +496,10 @@ struct vehicle_prototype { }; vehicle_prototype(); - vehicle_prototype( vehicle_prototype && ); + vehicle_prototype( vehicle_prototype && ) noexcept; ~vehicle_prototype(); - vehicle_prototype &operator=( vehicle_prototype && ); + vehicle_prototype &operator=( vehicle_prototype && ) noexcept( string_is_noexcept ); translation name; std::vector parts; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 7829fd934dd7a..67fd9a8949c59 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1654,7 +1654,8 @@ int vehicle::install_part( const point &dp, const vehicle_part &new_part ) return parts.size() - 1; } -bool vehicle::try_to_rack_nearby_vehicle( std::vector> &list_of_racks ) +bool vehicle::try_to_rack_nearby_vehicle( std::vector> &list_of_racks, + bool do_not_rack ) { map &here = get_map(); for( std::vector &this_bike_rack : list_of_racks ) { @@ -1685,7 +1686,11 @@ bool vehicle::try_to_rack_nearby_vehicle( std::vector> &list_of } partial_matches[ i ].insert( search_pos ); if( partial_matches[ i ] == test_veh->get_points() ) { - return merge_rackable_vehicle( test_veh, this_bike_rack ); + if( do_not_rack ) { + return true; + } else { + return merge_rackable_vehicle( test_veh, this_bike_rack ); + } } ++i; } @@ -2095,7 +2100,7 @@ bool vehicle::remove_carried_vehicle( const std::vector &carried_parts ) map &here = get_map(); vehicle *new_vehicle = here.add_vehicle( vproto_id( "none" ), new_pos3, new_dir ); if( new_vehicle == nullptr ) { - add_msg_debug( "Unable to unload bike rack, host face %d, new_dir %d!", + add_msg_debug( debugmode::DF_VEHICLE, "Unable to unload bike rack, host face %d, new_dir %d!", to_degrees( face.dir() ), to_degrees( new_dir ) ); return false; } @@ -3560,7 +3565,7 @@ int vehicle::ground_acceleration( const bool fueled, int at_vel_in_vmi ) const } int engine_power_ratio = total_power_w( fueled ) / weight; int accel_at_vel = 100 * 100 * engine_power_ratio / cmps; - add_msg_debug( "%s: accel at %d vimph is %d", name, target_vmiph, + add_msg_debug( debugmode::DF_VEHICLE, "%s: accel at %d vimph is %d", name, target_vmiph, cmps_to_vmiph( accel_at_vel ) ); return cmps_to_vmiph( accel_at_vel ); } @@ -3592,7 +3597,7 @@ int vehicle::water_acceleration( const bool fueled, int at_vel_in_vmi ) const } int engine_power_ratio = total_power_w( fueled ) / weight; int accel_at_vel = 100 * 100 * engine_power_ratio / cmps; - add_msg_debug( "%s: water accel at %d vimph is %d", name, target_vmiph, + add_msg_debug( debugmode::DF_VEHICLE, "%s: water accel at %d vimph is %d", name, target_vmiph, cmps_to_vmiph( accel_at_vel ) ); return cmps_to_vmiph( accel_at_vel ); } @@ -3678,7 +3683,8 @@ int vehicle::max_ground_velocity( const bool fueled ) const double max_in_mps = simple_cubic_solution( coeff_air_drag(), c_rolling_drag, c_rolling_drag * vehicles::rolling_constant_to_variable, -total_engine_w ); - add_msg_debug( "%s: power %d, c_air %3.2f, c_rolling %3.2f, max_in_mps %3.2f", + add_msg_debug( debugmode::DF_VEHICLE, + "%s: power %d, c_air %3.2f, c_rolling %3.2f, max_in_mps %3.2f", name, total_engine_w, coeff_air_drag(), c_rolling_drag, max_in_mps ); return mps_to_vmiph( max_in_mps ); } @@ -3698,7 +3704,8 @@ int vehicle::max_water_velocity( const bool fueled ) const int total_engine_w = total_power_w( fueled ); double total_drag = coeff_water_drag() + coeff_air_drag(); double max_in_mps = std::cbrt( total_engine_w / total_drag ); - add_msg_debug( "%s: power %d, c_air %3.2f, c_water %3.2f, water max_in_mps %3.2f", + add_msg_debug( debugmode::DF_VEHICLE, + "%s: power %d, c_air %3.2f, c_water %3.2f, water max_in_mps %3.2f", name, total_engine_w, coeff_air_drag(), coeff_water_drag(), max_in_mps ); return mps_to_vmiph( max_in_mps ); } @@ -3888,7 +3895,7 @@ void vehicle::noise_and_smoke( int load, time_duration time ) lvl++; } } - add_msg_debug( "VEH NOISE final: %d", static_cast( noise ) ); + add_msg_debug( debugmode::DF_VEHICLE, "VEH NOISE final: %d", static_cast( noise ) ); vehicle_noise = static_cast( noise ); sounds::sound( global_pos3(), noise, sounds::sound_t::movement, _( is_rotorcraft() ? heli_noise : sounds[lvl].first ), true ); @@ -4027,7 +4034,10 @@ double vehicle::coeff_air_drag() const // tally the results of each row and prorate them relative to vehicle width for( drag_column &dc : drag ) { // even as m_debug you rarely want to see this - // add_msg_debug( "veh %: pro %d, hboard %d, fboard %d, shield %d, seat %d, roof %d, aisle %d, turret %d, panel %d, exposed %d, last %d\n", name, dc.pro, dc.hboard, dc.fboard, dc.shield, dc.seat, dc.roof, dc.aisle, dc.turret, dc.panel, dc.exposed, dc.last ); + add_msg_debug( debugmode::DF_VEHICLE_DRAG, + "veh %: pro %d, hboard %d, fboard %d, shield %d, seat %d, roof %d, aisle %d, turret %d, panel %d, exposed %d, last %d\n", + name, dc.pro, dc.hboard, dc.fboard, dc.shield, dc.seat, dc.roof, dc.aisle, dc.turret, dc.panel, + dc.exposed, dc.last ); double c_air_drag_c = c_air_base; // rams in front of the vehicle mildly worsens air drag @@ -4077,7 +4087,8 @@ double vehicle::coeff_air_drag() const height /= width; c_air_drag /= width; double cross_area = height * tile_to_width( width ); - add_msg_debug( "%s: height %3.2fm, width %3.2fm (%d tiles), c_air %3.2f\n", name, height, + add_msg_debug( debugmode::DF_VEHICLE_DRAG, + "%s: height %3.2fm, width %3.2fm (%d tiles), c_air %3.2f\n", name, height, tile_to_width( width ), width, c_air_drag ); // F_air_drag = c_air_drag * cross_area * 1/2 * air_density * v^2 // coeff_air_resistance = c_air_drag * cross_area * 1/2 * air_density @@ -4162,9 +4173,9 @@ double vehicle::lift_thrust_of_rotorcraft( const bool fuelled, const bool safe ) // lift_thrust in lbthrust double lift_thrust = ( 8.8658 * std::pow( engine_power_in_hp / rotor_area_in_feet, -0.3107 ) ) * engine_power_in_hp; - add_msg_debug( - "lift thrust in lbs of %s = %f, rotor area in feet : %d, engine power in hp %f, thrust in newtons : %f", - name, lift_thrust, rotor_area_in_feet, engine_power_in_hp, engine_power_in_hp * 4.45 ); + add_msg_debug( debugmode::DF_VEHICLE, + "lift thrust in lbs of %s = %f, rotor area in feet : %d, engine power in hp %f, thrust in newtons : %f", + name, lift_thrust, rotor_area_in_feet, engine_power_in_hp, engine_power_in_hp * 4.45 ); // convert to newtons. return lift_thrust * 4.45; } @@ -4319,7 +4330,7 @@ float vehicle::k_traction( float wheel_traction_area ) const } const float mass_penalty = fraction_without_traction * to_kilogram( total_mass() ); float traction = std::min( 1.0f, wheel_traction_area / mass_penalty ); - add_msg_debug( "%s has traction %.2f", name, traction ); + add_msg_debug( debugmode::DF_VEHICLE, "%s has traction %.2f", name, traction ); // For now make it easy until it gets properly balanced: add a low cap of 0.1 return std::max( 0.1f, traction ); @@ -4681,9 +4692,9 @@ void vehicle::consume_fuel( int load, bool idling ) } player_character.mod_stamina( -( base_burn + mod ) ); - add_msg_debug( "Load: %d", load ); - add_msg_debug( "Mod: %d", mod ); - add_msg_debug( "Burn: %d", -( base_burn + mod ) ); + add_msg_debug( debugmode::DF_VEHICLE, "Load: %d", load ); + add_msg_debug( debugmode::DF_VEHICLE, "Mod: %d", mod ); + add_msg_debug( debugmode::DF_VEHICLE, "Burn: %d", -( base_burn + mod ) ); } } @@ -4897,7 +4908,8 @@ 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 battery_left, battery_capacity; + int battery_left; + int 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(); @@ -5054,7 +5066,7 @@ int vehicle::traverse_vehicle_graph( Vehicle *start_veh, int amount, Func action visited_vehs.insert( current_veh ); connected_vehs.pop(); - add_msg_debug( "Traversing graph with %d power", amount ); + add_msg_debug( debugmode::DF_VEHICLE, "Traversing graph with %d power", amount ); for( int p : current_veh->loose_parts ) { if( !current_veh->part_info( p ).has_flag( "POWER_TRANSFER" ) ) { @@ -5075,11 +5087,13 @@ 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.0f; - add_msg_debug( "Visiting remote %p with %d power (loss %f, which is %d percent)", + add_msg_debug( debugmode::DF_VEHICLE, + "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 ) ); - add_msg_debug( "After remote %p, %d power", static_cast( target_veh ), amount ); + add_msg_debug( debugmode::DF_VEHICLE, "After remote %p, %d power", + static_cast( target_veh ), amount ); if( amount < 1 ) { break; // No more charge to donate away. @@ -5119,7 +5133,7 @@ int vehicle::charge_battery( int amount, bool include_other_vehicles ) } auto charge_visitor = []( vehicle * veh, int amount, int lost ) { - add_msg_debug( "CH: %d", amount - lost ); + add_msg_debug( debugmode::DF_VEHICLE, "CH: %d", amount - lost ); return veh->charge_battery( amount - lost, false ); }; @@ -5156,7 +5170,7 @@ int vehicle::discharge_battery( int amount, bool recurse ) } auto discharge_visitor = []( vehicle * veh, int amount, int lost ) { - add_msg_debug( "CH: %d", amount + lost ); + add_msg_debug( debugmode::DF_VEHICLE, "CH: %d", amount + lost ); return veh->discharge_battery( amount + lost, false ); }; if( amount > 0 && recurse ) { // need more power! @@ -5462,7 +5476,7 @@ void vehicle::place_spawn_items() } const float spawn_rate = get_option( "ITEM_SPAWNRATE" ); - for( const vehicle_item_spawn &spawn : type.obj().item_spawns ) { + for( const vehicle_item_spawn &spawn : type->item_spawns ) { int part = part_with_feature( spawn.pos, "CARGO", false ); if( part < 0 ) { debugmsg( "No CARGO parts at (%d, %d) of %s!", spawn.pos.x, spawn.pos.y, name ); @@ -5482,6 +5496,13 @@ void vehicle::place_spawn_items() created.emplace_back( item( e ).in_its_container() ); } } + for( const std::pair &e : spawn.variant_ids ) { + if( rng_float( 0, 1 ) < spawn_rate ) { + item added = item( e.first ).in_its_container(); + added.set_gun_variant( e.second ); + created.push_back( added ); + } + } for( const item_group_id &e : spawn.item_groups ) { item_group::ItemList group_items = item_group::items_from( e, calendar::start_of_cataclysm, spawn_flags::use_spawn_rate ); @@ -6023,14 +6044,13 @@ void vehicle::do_towing_move() } else { nearby_destination = tower_tow_point; } - 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 ) ); + const tripoint destination_delta( here.getlocal( tower_tow_point ).xy() - nearby_destination.xy() + + tripoint( 0, 0, 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 ) ); here.move_vehicle( *towed_veh, move_destination, towed_veh->face ); - towed_veh->move = tileray( point( destination_delta_x, destination_delta_y ) ); + towed_veh->move = tileray( destination_delta.xy() ); } } @@ -6829,7 +6849,8 @@ std::list vehicle::use_charges( const vpart_position &vp, const itype_id & const cata::optional cargo_vp = vp.part_with_feature( "CARGO", true ); const auto tool_wants_battery = []( const itype_id & type ) { - item tool( type, calendar::turn_zero ), mag( tool.magazine_default() ); + item tool( type, calendar::turn_zero ); + item mag( tool.magazine_default() ); mag.contents.clear_items(); return tool.contents.insert_item( mag, item_pocket::pocket_type::MAGAZINE_WELL ).success() && @@ -7046,7 +7067,7 @@ void vehicle::update_time( const time_point &update_to ) double intensity = accum_weather.sunlight / default_daylight_level() / to_turns( elapsed ); int energy_bat = power_to_energy_bat( epower_w * intensity, elapsed ); if( energy_bat > 0 ) { - add_msg_debug( "%s got %d kJ energy from solar panels", name, energy_bat ); + add_msg_debug( debugmode::DF_VEHICLE, "%s got %d kJ energy from solar panels", name, energy_bat ); charge_battery( energy_bat ); } } @@ -7056,7 +7077,7 @@ void vehicle::update_time( const time_point &update_to ) int epower_w = total_wind_epower_w(); int energy_bat = power_to_energy_bat( epower_w, elapsed ); if( energy_bat > 0 ) { - add_msg_debug( "%s got %d kJ energy from wind turbines", name, energy_bat ); + add_msg_debug( debugmode::DF_VEHICLE, "%s got %d kJ energy from wind turbines", name, energy_bat ); charge_battery( energy_bat ); } } @@ -7064,7 +7085,7 @@ void vehicle::update_time( const time_point &update_to ) int epower_w = total_water_wheel_epower_w(); int energy_bat = power_to_energy_bat( epower_w, elapsed ); if( energy_bat > 0 ) { - add_msg_debug( "%s got %d kJ energy from water wheels", name, energy_bat ); + add_msg_debug( debugmode::DF_VEHICLE, "%s got %d kJ energy from water wheels", name, energy_bat ); charge_battery( energy_bat ); } } @@ -7189,7 +7210,7 @@ vehicle_part &vehicle::part( int part_num ) return parts[part_num]; } -const vehicle_part &vehicle::cpart( int part_num ) const +const vehicle_part &vehicle::part( int part_num ) const { return const_cast( parts[part_num] ); } diff --git a/src/vehicle.h b/src/vehicle.h index 5c22c5a847039..673db9febd9bf 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -910,7 +910,8 @@ class vehicle const std::string &variant = "", bool force = false ); // find a single tile wide vehicle adjacent to a list of part indices - bool try_to_rack_nearby_vehicle( std::vector> &list_of_racks ); + bool try_to_rack_nearby_vehicle( std::vector> &list_of_racks, + bool do_not_rack = false ); // merge a previously found single tile vehicle into this vehicle bool merge_rackable_vehicle( vehicle *carry_veh, const std::vector &rack_parts ); @@ -1783,6 +1784,7 @@ class vehicle void use_dishwasher( int p ); void use_monster_capture( int part, const tripoint &pos ); void use_bike_rack( int part ); + void clear_bike_racks( std::vector &racks ); void use_harness( int part, const tripoint &pos ); void interact_with( const vpart_position &vp ); @@ -1832,8 +1834,7 @@ class vehicle int part_count() const; // Returns the vehicle_part with the given part number vehicle_part &part( int part_num ); - // Same as vehicle::part() except with const binding - const vehicle_part &cpart( int part_num ) const; + const vehicle_part &part( int part_num ) const; // Determines whether the given part_num is valid for this vehicle bool valid_part( int part_num ) const; // Forcibly removes a part from this vehicle. Only exists to support faction_camp.cpp diff --git a/src/vehicle_move.cpp b/src/vehicle_move.cpp index fa8839d6a87fb..a1f8216389252 100644 --- a/src/vehicle_move.cpp +++ b/src/vehicle_move.cpp @@ -110,7 +110,8 @@ int vehicle::slowdown( int at_velocity ) const if( slowdown < 0 ) { debugmsg( "vehicle %s has negative drag slowdown %d\n", name, slowdown ); } - add_msg_debug( "%s at %d vimph, f_drag %3.2f, drag accel %d vmiph - extra drag %d", + add_msg_debug( debugmode::DF_VEHICLE_MOVE, + "%s at %d vimph, f_drag %3.2f, drag accel %d vmiph - extra drag %d", name, at_velocity, f_total_drag, slowdown, static_drag() ); // plows slow rolling vehicles, but not falling or floating vehicles if( !( is_falling || is_floating || is_flying ) ) { @@ -168,7 +169,8 @@ void vehicle::smart_controller_handle_turn( bool thrusting, return; } - int cur_battery_level, max_battery_level; + int cur_battery_level; + int 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; @@ -374,7 +376,7 @@ void vehicle::smart_controller_handle_turn( bool thrusting, smart_controller_state = cur_state; if( player_in_control( player_character ) ) { - add_msg_debug( _( "Smart controller optimizes engine state." ) ); + add_msg_debug( debugmode::DF_VEHICLE_MOVE, _( "Smart controller optimizes engine state." ) ); } } } else { @@ -744,7 +746,7 @@ bool vehicle::collision( std::vector &colls, colls.push_back( fake_coll ); velocity = 0; vertical_velocity = 0; - add_msg_debug( "Collision check on a dirty vehicle %s", name ); + add_msg_debug( debugmode::DF_VEHICLE_MOVE, "Collision check on a dirty vehicle %s", name ); return true; } @@ -971,7 +973,7 @@ veh_collision vehicle::part_collision( int part, const tripoint &p, continue; } - add_msg_debug( "Deformation energy: %.2f", d_E ); + add_msg_debug( debugmode::DF_VEHICLE_MOVE, "Deformation energy: %.2f", d_E ); // Damage calculation // Damage dealt overall dmg += d_E / 400; @@ -979,7 +981,7 @@ veh_collision vehicle::part_collision( int part, const tripoint &p, // Always if no critters, otherwise if critter is real if( critter == nullptr || !critter->is_hallucination() ) { part_dmg = dmg * k / 100.0f; - add_msg_debug( "Part collision damage: %.2f", part_dmg ); + add_msg_debug( debugmode::DF_VEHICLE_MOVE, "Part collision damage: %.2f", part_dmg ); } // Damage for object const float obj_dmg = dmg * ( 100.0f - k ) / 100.0f; @@ -1038,7 +1040,7 @@ veh_collision vehicle::part_collision( int part, const tripoint &p, critter->get_armor_bash( bodypart_id( "torso" ) ); dam = std::max( 0, dam - armor ); critter->apply_damage( driver, bodypart_id( "torso" ), dam ); - add_msg_debug( "Critter collision damage: %d", dam ); + add_msg_debug( debugmode::DF_VEHICLE_MOVE, "Critter collision damage: %d", dam ); } // Don't fling if vertical - critter got smashed into the ground @@ -1639,7 +1641,8 @@ bool vehicle::allow_manual_turn_on_rails( units::angle &corrected_turn_dir ) con if( turn_dir != face.dir() ) { corrected_turn_dir = get_corrected_turn_dir( turn_dir, face.dir() ); - int wheels_on_rail, turning_wheels_that_are_one_axis; + int wheels_on_rail; + int turning_wheels_that_are_one_axis; precalculate_vehicle_turning( corrected_turn_dir, true, TFLAG_RAIL, wheels_on_rail, turning_wheels_that_are_one_axis ); if( is_wheel_state_correct_to_turn_on_rails( wheels_on_rail, rail_wheelcache.size(), @@ -1656,19 +1659,22 @@ bool vehicle::allow_auto_turn_on_rails( units::angle &corrected_turn_dir ) const // check if autoturn is possible if( turn_dir == face.dir() ) { // precalculate wheels for every direction - int straight_wheels_on_rail, straight_turning_wheels_that_are_one_axis; + int straight_wheels_on_rail; + int straight_turning_wheels_that_are_one_axis; precalculate_vehicle_turning( face.dir(), true, TFLAG_RAIL, straight_wheels_on_rail, straight_turning_wheels_that_are_one_axis ); units::angle left_turn_dir = get_corrected_turn_dir( face.dir() - 45_degrees, face.dir() ); - int leftturn_wheels_on_rail, leftturn_turning_wheels_that_are_one_axis; + int leftturn_wheels_on_rail; + int leftturn_turning_wheels_that_are_one_axis; precalculate_vehicle_turning( left_turn_dir, true, TFLAG_RAIL, leftturn_wheels_on_rail, leftturn_turning_wheels_that_are_one_axis ); units::angle right_turn_dir = get_corrected_turn_dir( face.dir() + 45_degrees, face.dir() ); - int rightturn_wheels_on_rail, rightturn_turning_wheels_that_are_one_axis; + int rightturn_wheels_on_rail; + int rightturn_turning_wheels_that_are_one_axis; precalculate_vehicle_turning( right_turn_dir, true, TFLAG_RAIL, rightturn_wheels_on_rail, rightturn_turning_wheels_that_are_one_axis ); @@ -2019,7 +2025,7 @@ float map::vehicle_wheel_traction( const vehicle &veh, float traction_wheel_area = 0.0f; for( int p : wheel_indices ) { const tripoint &pp = veh.global_part_pos3( p ); - const int wheel_area = veh.cpart( p ).wheel_area(); + const int wheel_area = veh.part( p ).wheel_area(); const auto &tr = ter( pp ).obj(); // Deep water and air diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index 3d9fe9e5d017f..3b3f9c154e6b5 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -136,7 +136,7 @@ void vehicle::add_toggle_to_opts( std::vector &options, name ); options.emplace_back( -1, allow, key, msg ); - actions.push_back( [ = ] { + actions.emplace_back( [ = ] { for( const vpart_reference &vp : found ) { vehicle_part &e = vp.part(); @@ -324,14 +324,14 @@ void vehicle::set_electronics_menu_options( std::vector &options, if( has_part( "DOOR_MOTOR" ) ) { options.emplace_back( _( "Toggle doors" ), keybind( "TOGGLE_DOORS" ) ); - actions.push_back( [&] { control_doors(); refresh(); } ); + actions.emplace_back( [&] { control_doors(); refresh(); } ); } if( camera_on || ( has_part( "CAMERA" ) && has_part( "CAMERA_CONTROL" ) ) ) { options.emplace_back( camera_on ? colorize( _( "Turn off camera system" ), c_pink ) : _( "Turn on camera system" ), keybind( "TOGGLE_CAMERA" ) ); - actions.push_back( [&] { + actions.emplace_back( [&] { if( camera_on ) { camera_on = false; @@ -626,7 +626,7 @@ void vehicle::use_controls( const tripoint &pos ) if( remote ) { options.emplace_back( _( "Stop controlling" ), keybind( "RELEASE_CONTROLS" ) ); - actions.push_back( [&] { + actions.emplace_back( [&] { player_character.controlling_vehicle = false; g->setremoteveh( nullptr ); add_msg( _( "You stop controlling the vehicle." ) ); @@ -638,7 +638,7 @@ void vehicle::use_controls( const tripoint &pos ) } 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( [&] { + actions.emplace_back( [&] { player_character.controlling_vehicle = false; add_msg( _( "You let go of the controls." ) ); refresh(); @@ -661,7 +661,7 @@ void vehicle::use_controls( const tripoint &pos ) if( has_part( "ENGINE" ) ) { if( player_character.controlling_vehicle || ( remote && engine_on ) ) { options.emplace_back( _( "Stop driving" ), keybind( "TOGGLE_ENGINE" ) ); - actions.push_back( [&] { + actions.emplace_back( [&] { if( engine_on && has_engine_type_not( fuel_type_muscle, true ) ) { add_msg( _( "You turn the engine off and let go of the controls." ) ); @@ -704,7 +704,7 @@ void vehicle::use_controls( const tripoint &pos ) } else if( has_engine_type_not( fuel_type_muscle, true ) ) { options.emplace_back( engine_on ? _( "Turn off the engine" ) : _( "Turn on the engine" ), keybind( "TOGGLE_ENGINE" ) ); - actions.push_back( [&] { + actions.emplace_back( [&] { if( engine_on ) { engine_on = false; @@ -722,13 +722,13 @@ void vehicle::use_controls( const tripoint &pos ) if( has_part( "HORN" ) ) { options.emplace_back( _( "Honk horn" ), keybind( "SOUND_HORN" ) ); - actions.push_back( [&] { honk_horn(); refresh(); } ); + actions.emplace_back( [&] { honk_horn(); refresh(); } ); } if( has_part( "AUTOPILOT" ) && ( has_part( "CTRL_ELECTRONIC" ) || has_part( "REMOTE_CONTROLS" ) ) ) { options.emplace_back( _( "Control autopilot" ), keybind( "CONTROL_AUTOPILOT" ) ); - actions.push_back( [&] { toggle_autopilot(); refresh(); } ); + actions.emplace_back( [&] { toggle_autopilot(); refresh(); } ); } options.emplace_back( cruise_on ? _( "Disable cruise control" ) : _( "Enable cruise control" ), @@ -742,28 +742,28 @@ void vehicle::use_controls( const tripoint &pos ) if( has_electronic_controls ) { set_electronics_menu_options( options, actions ); options.emplace_back( _( "Control multiple electronics" ), keybind( "CONTROL_MANY_ELECTRONICS" ) ); - actions.push_back( [&] { control_electronics(); refresh(); } ); + actions.emplace_back( [&] { control_electronics(); refresh(); } ); } options.emplace_back( tracking_on ? _( "Forget vehicle position" ) : _( "Remember vehicle position" ), keybind( "TOGGLE_TRACKING" ) ); - actions.push_back( [&] { toggle_tracking(); } ); + actions.emplace_back( [&] { toggle_tracking(); } ); if( ( is_foldable() || tags.count( "convertible" ) ) && !remote ) { options.emplace_back( string_format( _( "Fold %s" ), name ), keybind( "FOLD_VEHICLE" ) ); - actions.push_back( [&] { fold_up(); } ); + actions.emplace_back( [&] { fold_up(); } ); } if( has_part( "ENGINE" ) ) { options.emplace_back( _( "Control individual engines" ), keybind( "CONTROL_ENGINES" ) ); - actions.push_back( [&] { control_engines(); refresh(); } ); + actions.emplace_back( [&] { control_engines(); refresh(); } ); } if( has_part( "SMART_ENGINE_CONTROLLER" ) ) { options.emplace_back( _( "Smart controller settings" ), keybind( "TOGGLE_SMART_ENGINE_CONTROLLER" ) ); - actions.push_back( [&] { + actions.emplace_back( [&] { if( !smart_controller_cfg ) { smart_controller_cfg = smart_controller_config(); @@ -783,11 +783,11 @@ void vehicle::use_controls( const tripoint &pos ) if( is_alarm_on ) { if( velocity == 0 && !remote ) { options.emplace_back( _( "Try to disarm alarm" ), keybind( "TOGGLE_ALARM" ) ); - actions.push_back( [&] { smash_security_system(); refresh(); } ); + actions.emplace_back( [&] { smash_security_system(); refresh(); } ); } else if( has_electronic_controls && has_part( "SECURITY" ) ) { options.emplace_back( _( "Trigger alarm" ), keybind( "TOGGLE_ALARM" ) ); - actions.push_back( [&] { + actions.emplace_back( [&] { is_alarm_on = true; add_msg( _( "You trigger the alarm" ) ); refresh(); @@ -797,21 +797,21 @@ void vehicle::use_controls( const tripoint &pos ) if( has_part( "TURRET" ) ) { options.emplace_back( _( "Set turret targeting modes" ), keybind( "TURRET_TARGET_MODE" ) ); - actions.push_back( [&] { turrets_set_targeting(); refresh(); } ); + actions.emplace_back( [&] { turrets_set_targeting(); refresh(); } ); options.emplace_back( _( "Set turret firing modes" ), keybind( "TURRET_FIRE_MODE" ) ); - actions.push_back( [&] { turrets_set_mode(); refresh(); } ); + actions.emplace_back( [&] { turrets_set_mode(); refresh(); } ); // We can also fire manual turrets with ACTION_FIRE while standing at the controls. options.emplace_back( _( "Aim turrets manually" ), keybind( "TURRET_MANUAL_AIM" ) ); - actions.push_back( [&] { turrets_aim_and_fire_all_manual( true ); refresh(); } ); + actions.emplace_back( [&] { turrets_aim_and_fire_all_manual( true ); refresh(); } ); // This lets us manually override and set the target for the automatic turrets instead. options.emplace_back( _( "Aim automatic turrets" ), keybind( "TURRET_MANUAL_OVERRIDE" ) ); - actions.push_back( [&] { turrets_override_automatic_aim(); refresh(); } ); + actions.emplace_back( [&] { turrets_override_automatic_aim(); refresh(); } ); options.emplace_back( _( "Aim individual turret" ), keybind( "TURRET_SINGLE_FIRE" ) ); - actions.push_back( [&] { turrets_aim_and_fire_single(); refresh(); } ); + actions.emplace_back( [&] { turrets_aim_and_fire_single(); refresh(); } ); } uilist menu; @@ -1733,7 +1733,7 @@ void vehicle::use_washing_machine( int p ) } std::vector detergent; - detergent.push_back( item_comp( det_types[chosen_detergent], 5 ) ); + detergent.emplace_back( det_types[chosen_detergent], 5 ); player_character.consume_items( detergent, 1, is_crafting_component ); add_msg( m_good, @@ -1790,7 +1790,7 @@ void vehicle::use_dishwasher( int p ) } std::vector detergent; - detergent.push_back( item_comp( itype_detergent, 5 ) ); + detergent.emplace_back( itype_detergent, 5 ); player_character.consume_items( detergent, 1, is_crafting_component ); add_msg( m_good, @@ -1941,14 +1941,14 @@ void vehicle::use_bike_rack( int part ) full_rack &= carry_size == rack_parts.size(); } int unload_carried = full_rack ? 0 : -1; - bool success = false; - + bool found_rackable_vehicle = try_to_rack_nearby_vehicle( racks_parts, true ); validate_carried_vehicles( carried_vehicles ); validate_carried_vehicles( carrying_racks ); - if( found_vehicle && !full_rack ) { uilist rack_menu; - rack_menu.addentry( 0, true, '0', _( "Load a vehicle on the rack" ) ); + if( found_rackable_vehicle ) { + rack_menu.addentry( 0, true, '0', _( "Load a vehicle on the rack" ) ); + } for( size_t i = 0; i < carried_vehicles.size(); i++ ) { rack_menu.addentry( i + 1, true, '1' + i, string_format( _( "Remove the %s from the rack" ), @@ -1957,21 +1957,24 @@ void vehicle::use_bike_rack( int part ) rack_menu.query(); unload_carried = rack_menu.ret - 1; } + + player_activity new_act; if( unload_carried > -1 ) { - success = remove_carried_vehicle( carried_vehicles[unload_carried] ); - if( success ) { - for( const int &rack_part : carrying_racks[unload_carried] ) { - parts[ rack_part ].remove_flag( vehicle_part::carrying_flag ); - parts[rack_part].remove_flag( vehicle_part::tracked_flag ); - } - } - } else { - success = try_to_rack_nearby_vehicle( racks_parts ); + new_act = player_activity( bikerack_unracking_activity_actor( to_moves( 5_minutes ), *this, + carried_vehicles[unload_carried], carrying_racks[unload_carried] ) ); + get_player_character().assign_activity( new_act, false ); + } else if( found_rackable_vehicle ) { + new_act = player_activity( bikerack_racking_activity_actor( to_moves( 5_minutes ), *this, + racks_parts ) ); + get_player_character().assign_activity( new_act, false ); } - if( success ) { - map &here = get_map(); - here.invalidate_map_cache( here.get_abs_sub().z ); - here.rebuild_vehicle_level_caches(); +} + +void vehicle::clear_bike_racks( std::vector &racks ) +{ + for( const int &rack_part : racks ) { + parts[rack_part].remove_flag( vehicle_part::carrying_flag ); + parts[rack_part].remove_flag( vehicle_part::tracked_flag ); } } diff --git a/src/visitable.cpp b/src/visitable.cpp index 5079bb5aa7c26..c4969073b901f 100644 --- a/src/visitable.cpp +++ b/src/visitable.cpp @@ -39,17 +39,13 @@ static const itype_id itype_apparatus( "apparatus" ); static const itype_id itype_adv_UPS_off( "adv_UPS_off" ); -static const itype_id itype_toolset( "toolset" ); static const itype_id itype_UPS( "UPS" ); static const itype_id itype_UPS_off( "UPS_off" ); static const quality_id qual_BUTCHER( "BUTCHER" ); -static const bionic_id bio_tools( "bio_tools" ); static const bionic_id bio_ups( "bio_ups" ); -static const json_character_flag json_flag_BIONIC_TOGGLED( "BIONIC_TOGGLED" ); - /** @relates visitable */ item *read_only_visitable::find_parent( const item &it ) const { @@ -132,11 +128,11 @@ static int has_quality_from_vpart( const vehicle &veh, int part, const quality_i { int qty = 0; - point pos = veh.cpart( part ).mount; + point pos = veh.part( part ).mount; for( const auto &n : veh.parts_at_relative( pos, true ) ) { // only unbroken parts can provide tool qualities - if( !veh.cpart( n ).is_broken() ) { + if( !veh.part( n ).is_broken() ) { auto tq = veh.part_info( n ).qualities; auto iter = tq.find( qual ); @@ -241,11 +237,11 @@ static int max_quality_from_vpart( const vehicle &veh, int part, const quality_i { int res = INT_MIN; - point pos = veh.cpart( part ).mount; + point pos = veh.part( part ).mount; for( const auto &n : veh.parts_at_relative( pos, true ) ) { // only unbroken parts can provide tool qualities - if( !veh.cpart( n ).is_broken() ) { + if( !veh.part( n ).is_broken() ) { auto tq = veh.part_info( n ).qualities; auto iter = tq.find( qual ); @@ -828,11 +824,10 @@ int Character::charges_of( const itype_id &what, int limit, { const player *p = dynamic_cast( this ); - if( what == itype_toolset ) { - if( p && p->has_active_bionic( bio_tools ) ) { + for( const auto &bio : *this->my_bionics ) { + const bionic_data &bid = bio.info(); + if( bid.fake_item == what && ( !bid.activated || bio.powered ) ) { return std::min( units::to_kilojoule( p->get_power_level() ), limit ); - } else { - return 0; } } diff --git a/src/weather.cpp b/src/weather.cpp index a4348ca4b75b6..ff04584a1674a 100644 --- a/src/weather.cpp +++ b/src/weather.cpp @@ -67,7 +67,7 @@ static const flag_id json_flag_SUN_GLASSES( "SUN_GLASSES" ); * @{ */ -static bool is_creature_outside( const Creature &target ) +bool is_creature_outside( const Creature &target ) { map &here = get_map(); return here.is_outside( point( target.posx(), target.posy() ) ) && here.get_abs_sub().z >= 0; @@ -169,7 +169,7 @@ weather_type_id current_weather( const tripoint &location, const time_point &t ) if( g->weather.weather_override != WEATHER_NULL ) { return g->weather.weather_override; } - return wgen.get_weather_conditions( location, t, g->get_seed(), g->weather.next_instance_allowed ); + return wgen.get_weather_conditions( location, t, g->get_seed() ); } ////// Funnels. @@ -401,10 +401,9 @@ static void fill_water_collectors( int mmPerHour, bool acid ) * @see map::decay_fields_and_scent * @see player::drench */ -void wet( Character &target, int amount ) +void wet_character( Character &target, int amount ) { - if( !is_creature_outside( target ) || - amount <= 0 || + if( amount <= 0 || target.has_trait( trait_FEATHERS ) || target.weapon.has_flag( json_flag_RAIN_PROTECT ) || ( !one_in( 50 ) && target.worn_with_flag( json_flag_RAINPROOF ) ) ) { @@ -469,7 +468,7 @@ void handle_weather_effects( const weather_type_id &w ) { //Possible TODO, make npc/monsters affected map &here = get_map(); - Character &player_character = get_player_character(); + Character &target = get_player_character(); if( w->rains && w->precip != precip_class::none ) { fill_water_collectors( precip_mm_per_hour( w->precip ), w->acidic ); @@ -486,129 +485,13 @@ void handle_weather_effects( const weather_type_id &w ) wetness = 60; } here.decay_fields_and_scent( decay_time ); - wet( player_character, wetness ); + // Coarse correction to get us back to previously intended soaking rate. + if( calendar::once_every( 6_seconds ) && is_creature_outside( target ) ) { + wet_character( target, wetness ); + } } glare( w ); g->weather.lightning_active = false; - - for( const weather_effect ¤t_effect : w->effects ) { - if( current_effect.must_be_outside && !is_creature_outside( player_character ) ) { - continue; - } - if( current_effect.time_between > 0_seconds && - !calendar::once_every( current_effect.time_between ) ) { - continue; - } - if( !one_in( current_effect.one_in_chance ) ) { - continue; - } - if( current_effect.lightning && here.get_abs_sub().z >= 0 ) { - g->weather.lightning_active = true; - } - if( current_effect.rain_proof ) { - int chance = 0; - if( w->precip <= precip_class::light ) { - chance = 2; - } else if( w->precip >= precip_class::heavy ) { - chance = 4; - } - if( player_character.weapon.has_flag( json_flag_RAIN_PROTECT ) && one_in( chance ) ) { - add_msg( _( "Your %s protects you from the weather." ), player_character.weapon.tname() ); - continue; - } else { - if( player_character.worn_with_flag( json_flag_RAINPROOF ) && one_in( chance * 2 ) ) { - add_msg( _( "Your clothing protects you from the weather." ) ); - continue; - } else { - bool has_helmet = false; - if( player_character.is_wearing_power_armor( &has_helmet ) && ( has_helmet || - one_in( chance * 2 ) ) ) { - add_msg( _( "Your power armor protects you from the weather." ) ); - continue; - } - } - } - } - if( player_character.get_pain() >= current_effect.pain_max ) { - continue; - } - - bool spawned = current_effect.spawns.empty(); - for( const spawn_type &spawn : current_effect.spawns ) { - monster target_monster; - if( spawn.target.is_empty() ) { - //grab a random nearby hostile creature to create a hallucination or copy of - Creature *copy = g->get_creature_if( [&spawn]( const Creature & critter ) -> bool { - bool not_self = get_player_character().pos() != critter.pos(); - bool in_range = std::round( rl_dist_exact( get_player_character().pos(), critter.pos() ) ) <= spawn.target_range; - bool valid_target = get_player_character().attitude_to( critter ) == Creature::Attitude::HOSTILE; - return not_self && in_range && valid_target; - } ); - if( copy == nullptr ) { - continue; - } - target_monster = *dynamic_cast( copy ); - } else { - target_monster = monster( spawn.target ); - } - - for( int i = 0; i < spawn.hallucination_count; i++ ) { - tripoint point; - if( g->find_nearby_spawn_point( player_character, target_monster.type->id, spawn.min_radius, - spawn.max_radius, point ) ) { - g->spawn_hallucination( point, target_monster.type->id ); - spawned = true; - } - } - for( int i = 0; i < spawn.real_count; i++ ) { - tripoint point; - if( g->find_nearby_spawn_point( player_character, target_monster.type->id, spawn.min_radius, - spawn.max_radius, point ) ) { - g->place_critter_at( target_monster.type->id, point ); - spawned = true; - } - } - } - if( !spawned ) { - continue; - } - for( const weather_field &field : current_effect.fields ) { - for( const tripoint &dest : get_map().points_in_radius( player_character.pos(), field.radius ) ) { - if( !field.outdoor_only || get_map().is_outside( dest ) ) { - get_map().add_field( dest, field.type, field.intensity, field.age ); - } - } - } - if( current_effect.effect_id.is_valid() ) { - if( current_effect.target_part.is_valid() ) { - player_character.add_effect( current_effect.effect_id, current_effect.effect_duration, - current_effect.target_part ); - } else { - player_character.add_effect( current_effect.effect_id, current_effect.effect_duration ); - } - } - if( current_effect.trait_id_to_add.is_valid() ) { - player_character.set_mutation( current_effect.trait_id_to_add ); - } - if( current_effect.trait_id_to_remove.is_valid() ) { - player_character.unset_mutation( current_effect.trait_id_to_remove ); - } - if( current_effect.damage.has_value() ) { - if( current_effect.target_part.is_valid() ) { - player_character.deal_damage( nullptr, current_effect.target_part, current_effect.damage.value() ); - } else { - for( const bodypart_id &bp : player_character.get_all_body_parts() ) { - player_character.deal_damage( nullptr, bp, current_effect.damage.value() ); - } - } - } - player_character.mod_healthy( current_effect.healthy ); - player_character.mod_rad( current_effect.radiation ); - wet( player_character, current_effect.wet ); - player_character.mod_pain( current_effect.pain ); - weather_sound( current_effect.sound_message, current_effect.sound_effect ); - player_character.add_msg_if_player( current_effect.message ); - } } static std::string to_string( const weekdays &d ) @@ -696,6 +579,7 @@ std::string weather_forecast( const point_abs_sm &abs_sm_pos ) // TODO: fix point types const tripoint abs_ms_pos = tripoint( project_to( abs_sm_pos ).raw(), 0 ); + w_point weatherPoint = *g->weather.weather_precise; // TODO: wind direction and speed const time_point last_hour = calendar::turn - ( calendar::turn - calendar::turn_zero ) % 1_hours; @@ -704,7 +588,8 @@ std::string weather_forecast( const point_abs_sm &abs_sm_pos ) const weather_generator wgen = get_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() ); - forecast = std::max( forecast, wgen.get_weather_conditions( w, g->weather.next_instance_allowed ) ); + *g->weather.weather_precise = w; + forecast = std::max( forecast, wgen.get_weather_conditions( w ) ); high = std::max( high, w.temperature ); low = std::min( low, w.temperature ); } @@ -729,6 +614,7 @@ std::string weather_forecast( const point_abs_sm &abs_sm_pos ) print_temperature( high ), print_temperature( low ) ); } + *g->weather.weather_precise = weatherPoint; return weather_report; } @@ -1058,13 +944,11 @@ void weather_manager::update_weather() g->get_seed() ); weather_type_id old_weather = weather_id; weather_id = weather_override == WEATHER_NULL ? - weather_gen.get_weather_conditions( w, next_instance_allowed ) + weather_gen.get_weather_conditions( w ) : weather_override; sfx::do_ambient(); temperature = w.temperature; lightning_active = false; - next_instance_allowed[weather_id] = calendar::turn + rng( weather_id->time_between_min, - weather_id->time_between_max ); nextweather = calendar::turn + rng( weather_id->duration_min, weather_id->duration_max ); map &here = get_map(); if( weather_id != old_weather && weather_id->dangerous && @@ -1084,6 +968,9 @@ void weather_manager::update_weather() } here.set_seen_cache_dirty( tripoint_zero ); } + if( weather_id != old_weather ) { + effect_on_conditions::process_reactivate(); + } } } diff --git a/src/weather.h b/src/weather.h index d63dc2e9cdf63..4e3a4c1803c8f 100644 --- a/src/weather.h +++ b/src/weather.h @@ -50,6 +50,7 @@ static constexpr int BODYTEMP_SCORCHING = 9500; #include class Character; +class Creature; class item; struct rl_vec2d; struct trap; @@ -82,7 +83,8 @@ struct weather_sum { float sunlight = 0.0f; int wind_amount = 0; }; - +bool is_creature_outside( const Creature &target ); +void wet_character( Character &target, int amount ); weather_type_id get_bad_weather(); std::string get_shortdirstring( int angle ); @@ -157,7 +159,6 @@ int incident_sunlight( const weather_type_id &wtype, const time_point &t = calendar::turn ); void weather_sound( const translation &sound_message, const std::string &sound_effect ); -void wet( Character &target, int amount ); class weather_manager { @@ -178,7 +179,6 @@ class weather_manager cata::optional wind_direction_override; cata::optional windspeed_override; weather_type_id weather_override; - std::map next_instance_allowed; // not only sets nextweather, but updates weather as well void set_nextweather( time_point t ); // The time at which weather will shift next. @@ -190,8 +190,6 @@ class weather_manager // Returns outdoor or indoor temperature of given location int get_temperature( const tripoint_abs_omt &location ); void clear_temp_cache(); - void on_load(); - static void serialize_all( JsonOut &json ); static void unserialize_all( JsonIn &jsin ); }; diff --git a/src/weather_gen.cpp b/src/weather_gen.cpp index 337ea17b9fe9a..bf2798df042f6 100644 --- a/src/weather_gen.cpp +++ b/src/weather_gen.cpp @@ -8,7 +8,11 @@ #include #include +#include "avatar.h" #include "cata_utility.h" +#include "condition.h" +#include "dialogue.h" +#include "game.h" #include "game_constants.h" #include "json.h" #include "math_defines.h" @@ -168,70 +172,37 @@ w_point weather_generator::get_weather( const tripoint &location, const time_poi } weather_type_id weather_generator::get_weather_conditions( const tripoint &location, - const time_point &t, unsigned seed, - std::map &next_instance_allowed ) const + const time_point &t, unsigned seed ) const { w_point w( get_weather( location, t, seed ) ); - weather_type_id wt = get_weather_conditions( w, next_instance_allowed ); + weather_type_id wt = get_weather_conditions( w ); return wt; } -weather_type_id weather_generator::get_weather_conditions( const w_point &w, - std::map &next_instance_allowed ) const +weather_type_id weather_generator::get_weather_conditions( const w_point & ) const { weather_type_id current_conditions = WEATHER_CLEAR; + dialogue d; + standard_npc default_npc( "Default" ); + d.alpha = get_talker_for( get_avatar() ); + d.beta = get_talker_for( default_npc ); for( const std::string &weather_type : weather_types ) { weather_type_id type = weather_type_id( weather_type ); - const weather_requirements &requires = type->requirements; - if( ( w.time < ( calendar::start_of_cataclysm + requires.time_passed_min ) ) || - ( requires.time_passed_max != 0_seconds && - ( w.time > ( calendar::start_of_cataclysm + requires.time_passed_max ) ) ) ) { - continue; - } - std::map::iterator instance = next_instance_allowed.find( type ); - if( instance != next_instance_allowed.end() && instance->second > calendar::turn ) { - continue; - } - - 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; - if( !( test_temperature && test_windspeed ) ) { - continue; - } - - if( !requires.required_weathers.empty() ) { - if( std::find( requires.required_weathers.begin(), requires.required_weathers.end(), - current_conditions ) == requires.required_weathers.end() ) { - continue; + bool required_weather = type->required_weathers.empty(); + if( !required_weather ) { + for( const weather_type_id &weather : type->required_weathers ) { + if( weather == current_conditions ) { + required_weather = true; + break; + } } } - 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 ) ) ) ) { + if( required_weather && type->condition( d ) ) { + current_conditions = type; continue; } - if( requires.one_in_chance != 0 && !one_in( requires.one_in_chance ) ) { - continue; - } - - current_conditions = type; } return current_conditions; } @@ -293,13 +264,13 @@ int weather_generator::get_water_temperature() const return water_temperature; } -void weather_generator::test_weather( unsigned seed, - std::map &next_instance_allowed ) const +void weather_generator::test_weather( unsigned seed ) const { // Outputs a Cata year's worth of weather data to a CSV file. // Usage: // weather_generator WEATHERGEN; // Instantiate the class. // WEATHERGEN.test_weather(); // Runs this test. + w_point weatherPoint = *g->weather.weather_precise; write_to_file( "weather.output", [&]( std::ostream & testfile ) { testfile << "|;year;season;day;hour;minute;temperature(F);humidity(%);pressure(mB);weatherdesc;windspeed(mph);winddirection" @@ -309,7 +280,8 @@ void weather_generator::test_weather( unsigned seed, 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, i, seed ); - weather_type_id conditions = get_weather_conditions( w, next_instance_allowed ); + weather_type_id conditions = get_weather_conditions( w ); + *g->weather.weather_precise = w; int year = to_turns( i - calendar::turn_zero ) / to_turns ( calendar::year_length() ) + 1; @@ -328,6 +300,7 @@ void weather_generator::test_weather( unsigned seed, } }, "weather test file" ); + *g->weather.weather_precise = weatherPoint; } weather_generator weather_generator::load( const JsonObject &jo ) diff --git a/src/weather_gen.h b/src/weather_gen.h index 972404d9da016..3f7baca8be077 100644 --- a/src/weather_gen.h +++ b/src/weather_gen.h @@ -55,15 +55,12 @@ 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_id get_weather_conditions( const tripoint &, const time_point &, - unsigned seed, std::map &next_instance_allowed ) const; - weather_type_id get_weather_conditions( const w_point &, - std::map &next_instance_allowed ) 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; - void test_weather( unsigned seed, - std::map &next_instance_allowed ) const; + void test_weather( unsigned seed ) const; double get_weather_temperature( const tripoint &, const time_point &, unsigned ) const; diff --git a/src/weather_type.cpp b/src/weather_type.cpp index 48a76f726e262..e48d9134024a5 100644 --- a/src/weather_type.cpp +++ b/src/weather_type.cpp @@ -4,6 +4,7 @@ #include #include "assign.h" +#include "condition.h" #include "debug.h" #include "generic_factory.h" #include "json.h" @@ -53,23 +54,6 @@ std::string enum_to_string( sun_intensity_type data ) 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 ) { @@ -91,7 +75,7 @@ std::string enum_to_string( weather_sound_category data case weather_sound_category::last: break; } - debugmsg( "Invalid time_requirement_type" ); + debugmsg( "Invalid weather sound category." ); abort(); } @@ -117,42 +101,12 @@ 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() ); + for( const auto &type : required_weathers ) { + if( !type.is_valid() ) { + debugmsg( "Weather type %s does not exist.", type.c_str() ); abort(); } } - for( const weather_effect &effect : effects ) { - if( !effect.effect_id.is_empty() && !effect.effect_id.is_valid() ) { - debugmsg( "Effect type %s does not exist.", effect.effect_id.c_str() ); - abort(); - } - if( !effect.trait_id_to_add.is_empty() && !effect.trait_id_to_add.is_valid() ) { - debugmsg( "Trait %s does not exist.", effect.trait_id_to_add.c_str() ); - abort(); - } - if( !effect.trait_id_to_remove.is_empty() && !effect.trait_id_to_remove.is_valid() ) { - debugmsg( "Trait %s does not exist.", effect.trait_id_to_remove.c_str() ); - abort(); - } - if( !effect.target_part.is_empty() && !effect.target_part.is_valid() ) { - debugmsg( "Target part %s does not exist.", effect.target_part.c_str() ); - abort(); - } - for( const spawn_type &spawn : effect.spawns ) { - if( !spawn.target.is_empty() && !spawn.target.is_valid() ) { - debugmsg( "Spawn target %s does not exist.", spawn.target.c_str() ); - abort(); - } - } - for( const weather_field &field : effect.fields ) { - if( !field.type.is_valid() ) { - debugmsg( "field type %s does not exist.", field.type.c_str() ); - abort(); - } - } - } } void weather_type::load( const JsonObject &jo, const std::string & ) @@ -182,61 +136,7 @@ void weather_type::load( const JsonObject &jo, const std::string & ) if( duration_min > duration_max ) { jo.throw_error( "duration_min must be less than or equal to duration_max" ); } - optional( jo, was_loaded, "time_between_min", time_between_min, 0_seconds ); - optional( jo, was_loaded, "time_between_max", time_between_max, 0_seconds ); - if( time_between_min > time_between_max ) { - jo.throw_error( "time_between_min must be less than or equal to time_between_max" ); - } - for( const JsonObject weather_effect_jo : jo.get_array( "effects" ) ) { - weather_effect effect; - - optional( weather_effect_jo, was_loaded, "message", effect.message ); - optional( weather_effect_jo, was_loaded, "sound_message", effect.sound_message ); - optional( weather_effect_jo, was_loaded, "sound_effect", effect.sound_effect, "" ); - mandatory( weather_effect_jo, was_loaded, "must_be_outside", effect.must_be_outside ); - optional( weather_effect_jo, was_loaded, "one_in_chance", effect.one_in_chance, -1 ); - optional( weather_effect_jo, was_loaded, "time_between", effect.time_between ); - optional( weather_effect_jo, was_loaded, "lightning", effect.lightning, false ); - optional( weather_effect_jo, was_loaded, "rain_proof", effect.rain_proof, false ); - optional( weather_effect_jo, was_loaded, "pain_max", effect.pain_max, INT_MAX ); - optional( weather_effect_jo, was_loaded, "pain", effect.pain, 0 ); - optional( weather_effect_jo, was_loaded, "wet", effect.wet, 0 ); - optional( weather_effect_jo, was_loaded, "radiation", effect.radiation, 0 ); - optional( weather_effect_jo, was_loaded, "healthy", effect.healthy, 0 ); - optional( weather_effect_jo, was_loaded, "effect_id", effect.effect_id ); - optional( weather_effect_jo, was_loaded, "effect_duration", effect.effect_duration ); - optional( weather_effect_jo, was_loaded, "trait_id_to_add", effect.trait_id_to_add ); - optional( weather_effect_jo, was_loaded, "trait_id_to_remove", effect.trait_id_to_remove ); - optional( weather_effect_jo, was_loaded, "target_part", effect.target_part ); - assign( weather_effect_jo, "damage", effect.damage ); - - for( const JsonObject field_jo : weather_effect_jo.get_array( "fields" ) ) { - weather_field new_field; - mandatory( field_jo, was_loaded, "type", new_field.type ); - mandatory( field_jo, was_loaded, "intensity", new_field.intensity ); - mandatory( field_jo, was_loaded, "age", new_field.age ); - optional( field_jo, was_loaded, "outdoor_only", new_field.outdoor_only, true ); - optional( field_jo, was_loaded, "radius", new_field.radius, 10000000 ); - - effect.fields.emplace_back( new_field ); - } - for( const JsonObject spawn_jo : weather_effect_jo.get_array( "spawns" ) ) { - spawn_type spawn; - mandatory( spawn_jo, was_loaded, "max_radius", spawn.max_radius ); - mandatory( spawn_jo, was_loaded, "min_radius", spawn.min_radius ); - if( spawn.min_radius > spawn.max_radius ) { - spawn_jo.throw_error( "min_radius must be less than or equal to max_radius" ); - } - optional( spawn_jo, was_loaded, "hallucination_count", spawn.hallucination_count, 0 ); - optional( spawn_jo, was_loaded, "real_count", spawn.real_count, 0 ); - optional( spawn_jo, was_loaded, "target", spawn.target ); - optional( spawn_jo, was_loaded, "target_range", spawn.target_range, 30 ); - - effect.spawns.emplace_back( spawn ); - } - effects.emplace_back( effect ); - } if( jo.has_member( "weather_animation" ) ) { JsonObject weather_animation_jo = jo.get_object( "weather_animation" ); mandatory( weather_animation_jo, was_loaded, "factor", weather_animation.factor ); @@ -246,35 +146,8 @@ void weather_type::load( const JsonObject &jo, const std::string & ) mandatory( weather_animation_jo, was_loaded, "sym", weather_animation.symbol, unicode_codepoint_from_symbol_reader ); } - - 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, "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 ) ); - } - optional( weather_requires, was_loaded, "time_passed_min", new_requires.time_passed_min, - 0_seconds ); - optional( weather_requires, was_loaded, "time_passed_max", new_requires.time_passed_max, - 0_seconds ); - optional( weather_requires, was_loaded, "one_in_chance", new_requires.one_in_chance, 0 ); - requirements = new_requires; - } + optional( jo, was_loaded, "required_weathers", required_weathers ); + read_condition( jo, "condition", condition, true ); } void weather_types::reset() diff --git a/src/weather_type.h b/src/weather_type.h index 0ca6d3de4fd83..d6658cf5e2a9e 100644 --- a/src/weather_type.h +++ b/src/weather_type.h @@ -13,6 +13,7 @@ #include "catacharset.h" #include "color.h" #include "damage.h" +#include "dialogue.h" #include "optional.h" #include "translations.h" #include "type_id.h" @@ -49,18 +50,7 @@ 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 class weather_sound_category : int { +enum weather_sound_category : int { silent, drizzle, rainy, @@ -88,64 +78,6 @@ struct weather_animation_t { } }; -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; - weather_time_requirement_type time = weather_time_requirement_type::both; - std::vector required_weathers{}; - time_duration time_passed_min = 0_turns; - time_duration time_passed_max = 0_turns; - int one_in_chance = 0; -}; - -struct weather_field { - field_type_str_id type; - int intensity = 0; - time_duration age = 0_turns; - int radius = 0; - bool outdoor_only = false; -}; - -struct spawn_type { - mtype_id target; - int target_range = 0; - int hallucination_count = 0; - int real_count = 0; - int min_radius = 0; - int max_radius = 0; -}; - -struct weather_effect { - int one_in_chance = 0; - time_duration time_between = 0_turns; - translation message; - bool must_be_outside = false; - translation sound_message; - std::string sound_effect; - bool lightning = false; - bool rain_proof = false; - int pain = 0; - int pain_max = 0; - int wet = 0; - int radiation = 0; - int healthy = 0; - efftype_id effect_id; - time_duration effect_duration = 0_turns; - trait_id trait_id_to_add; - trait_id trait_id_to_remove; - bodypart_str_id target_part; - cata::optional damage; - std::vector spawns; - std::vector fields; -}; - struct weather_type { public: friend class generic_factory; @@ -175,8 +107,6 @@ struct weather_type { bool rains = false; // Whether said precipitation is acidic. bool acidic = false; - // vector for weather effects. - std::vector effects{}; // string for tiles animation std::string tiles_animation; // Information for weather animations @@ -186,11 +116,10 @@ struct weather_type { // strength of the sun sun_intensity_type sun_intensity = sun_intensity_type::none; // when this weather should happen - weather_requirements requirements; + std::function condition; + std::vector required_weathers; time_duration duration_min = 0_turns; time_duration duration_max = 0_turns; - time_duration time_between_min = 0_turns; - time_duration time_between_max = 0_turns; void load( const JsonObject &jo, const std::string &src ); void finalize(); void check() const; diff --git a/src/weighted_list.h b/src/weighted_list.h index 065986e498b8e..228cf3a519b9b 100644 --- a/src/weighted_list.h +++ b/src/weighted_list.h @@ -19,6 +19,10 @@ template struct weighted_object { template struct weighted_list { weighted_list() : total_weight( 0 ) { } + weighted_list( const weighted_list & ) = default; + weighted_list( weighted_list && ) noexcept = default; + weighted_list &operator=( const weighted_list & ) = default; + weighted_list &operator=( weighted_list && ) noexcept = default; virtual ~weighted_list() = default; /** @@ -145,6 +149,10 @@ template struct weighted_list { return total_weight; } + bool is_valid() const { + return get_weight() > 0; + } + typename std::vector >::iterator begin() { return objects.begin(); } @@ -224,6 +232,8 @@ template struct weighted_int_list : public weighted_list { std::vector precalc_array; }; +static_assert( std::is_nothrow_move_constructible>::value, "" ); + template struct weighted_float_list : public weighted_list { // TODO: precalc using alias method diff --git a/src/wish.cpp b/src/wish.cpp index 0ca3fbe0cfd89..a6be648885d9d 100644 --- a/src/wish.cpp +++ b/src/wish.cpp @@ -601,7 +601,10 @@ void debug_menu::wishitem( player *p, const tripoint &pos ) } std::vector> opts; for( const itype *i : item_controller->all() ) { - opts.emplace_back( item( i, calendar::turn_zero ).tname( 1, false ), i ); + item option( i, calendar::turn_zero ); + // Only display the generic name if it has variants + option.clear_gun_variant(); + opts.emplace_back( option.tname( 1, false ), i ); } std::sort( opts.begin(), opts.end(), localized_compare ); std::vector itypes; @@ -842,7 +845,7 @@ void debug_menu::wishproficiency( player *p ) know_all = player_know; } - sorted_profs.push_back( { cur.prof_id(), player_know } ); + sorted_profs.emplace_back( cur.prof_id(), player_know ); } std::sort( sorted_profs.begin(), sorted_profs.end(), localized_compare ); diff --git a/src/worldfactory.cpp b/src/worldfactory.cpp index 73450d2d3edb1..532b99a6b871e 100644 --- a/src/worldfactory.cpp +++ b/src/worldfactory.cpp @@ -117,9 +117,9 @@ worldfactory::worldfactory() , mman_ui( *mman ) { // prepare tab display order - tabs.push_back( std::bind( &worldfactory::show_worldgen_tab_modselection, this, _1, _2, _3 ) ); - tabs.push_back( std::bind( &worldfactory::show_worldgen_tab_options, this, _1, _2, _3 ) ); - tabs.push_back( std::bind( &worldfactory::show_worldgen_tab_confirm, this, _1, _2, _3 ) ); + tabs.emplace_back( std::bind( &worldfactory::show_worldgen_tab_modselection, this, _1, _2, _3 ) ); + tabs.emplace_back( std::bind( &worldfactory::show_worldgen_tab_options, this, _1, _2, _3 ) ); + tabs.emplace_back( std::bind( &worldfactory::show_worldgen_tab_confirm, this, _1, _2, _3 ) ); } worldfactory::~worldfactory() = default; @@ -415,7 +415,8 @@ WORLDPTR worldfactory::pick_world( bool show_prompt ) mapLines[3] = true; std::map > world_pages; - size_t sel = 0, selpage = 0; + size_t sel = 0; + size_t selpage = 0; catacurses::window w_worlds_border; catacurses::window w_worlds_tooltip; @@ -741,19 +742,20 @@ void worldfactory::draw_mod_list( const catacurses::window &w, int &start, size_ } const mod_id &mod_entry_id = *iter; - std::string mod_entry_name = string_format( _( " [%s]" ), mod_entry_id.str() ); + std::string mod_entry_name; nc_color mod_entry_color = c_white; if( mod_entry_id.is_valid() ) { const MOD_INFORMATION &mod = *mod_entry_id; - mod_entry_name = mod.name() + mod_entry_name; + mod_entry_name = mod.name(); if( mod.obsolete ) { mod_entry_color = c_dark_gray; } } else { mod_entry_color = c_light_red; - mod_entry_name = _( "N/A" ) + mod_entry_name; + mod_entry_name = _( "N/A" ); } + mod_entry_name += string_format( _( " [%s]" ), mod_entry_id.str() ); trim_and_print( w, point( 4, iNum - start ), wwidth, mod_entry_color, mod_entry_name ); if( w_shift ) { @@ -966,8 +968,8 @@ int worldfactory::show_worldgen_tab_modselection( const catacurses::window &win, ui.on_screen_resize( init_windows ); std::vector headers; - headers.push_back( _( "Mod List" ) ); - headers.push_back( _( "Mod Load Order" ) ); + headers.emplace_back( _( "Mod List" ) ); + headers.emplace_back( _( "Mod Load Order" ) ); size_t active_header = 0; int startsel[2] = {0, 0}; @@ -1599,7 +1601,7 @@ void load_external_option( const JsonObject &jo ) } else { opt.setValue( "false" ); } - } else if( stype == "string" ) { + } else if( stype == "string" || stype == "string_input" ) { opt.setValue( jo.get_string( "value" ) ); } else { jo.throw_error( "Unknown or unsupported stype for external option", "stype" ); diff --git a/tests/active_item_cache_test.cpp b/tests/active_item_cache_test.cpp index 8307cd1bc0d1f..a92816bc935dd 100644 --- a/tests/active_item_cache_test.cpp +++ b/tests/active_item_cache_test.cpp @@ -1,7 +1,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "game_constants.h" #include "item.h" #include "map.h" diff --git a/tests/active_item_test.cpp b/tests/active_item_test.cpp index 5676d0f94d683..2a89e295f5097 100644 --- a/tests/active_item_test.cpp +++ b/tests/active_item_test.cpp @@ -3,7 +3,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "item.h" #include "map.h" #include "map_helpers.h" diff --git a/tests/activity_tracker_test.cpp b/tests/activity_tracker_test.cpp index e22e3a725daa5..3aeaf38ad981e 100644 --- a/tests/activity_tracker_test.cpp +++ b/tests/activity_tracker_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "activity_tracker.h" #include "calendar.h" diff --git a/tests/algo_test.cpp b/tests/algo_test.cpp index 73a7d42919d30..529955f1cb81b 100644 --- a/tests/algo_test.cpp +++ b/tests/algo_test.cpp @@ -1,6 +1,6 @@ #pragma GCC diagnostic ignored "-Wunused-macros" #define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER -#include "catch/catch.hpp" +#include "cata_catch.h" #include #include diff --git a/tests/ammo_set_test.cpp b/tests/ammo_set_test.cpp index e258ade67054c..84ce9572866ad 100644 --- a/tests/ammo_set_test.cpp +++ b/tests/ammo_set_test.cpp @@ -4,7 +4,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "debug.h" #include "item.h" #include "item_pocket.h" @@ -13,378 +13,386 @@ #include "type_id.h" #include "value_ptr.h" -TEST_CASE( "ammo_set", "[ammo_set][magazine][ammo]" ) +TEST_CASE( "ammo_set items with MAGAZINE pockets", "[ammo_set][magazine][ammo]" ) { - SECTION( "ammo_set items with MAGAZINE pockets" ) { - GIVEN( "empty 9mm CZ 75 20-round magazine" ) { - item cz75mag_20rd( "cz75mag_20rd" ); - REQUIRE( cz75mag_20rd.is_magazine() ); - REQUIRE( cz75mag_20rd.ammo_remaining() == 0 ); - REQUIRE( cz75mag_20rd.ammo_default() ); - itype_id ammo_default_id = cz75mag_20rd.ammo_default(); - itype_id ammo9mm_id( "9mm" ); - REQUIRE( ammo_default_id.str() == ammo9mm_id.str() ); - const ammotype &amtype = ammo9mm_id->ammo->type; - REQUIRE( cz75mag_20rd.ammo_capacity( amtype ) == 20 ); - WHEN( "set 9mm ammo in the magazine w/o quantity" ) { - cz75mag_20rd.ammo_set( ammo9mm_id ); - THEN( "magazine has 20 rounds of 9mm" ) { - CHECK( cz75mag_20rd.ammo_remaining() == 20 ); - CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); - } + GIVEN( "empty 9mm CZ 75 20-round magazine" ) { + item cz75mag_20rd( "cz75mag_20rd" ); + REQUIRE( cz75mag_20rd.is_magazine() ); + REQUIRE( cz75mag_20rd.ammo_remaining() == 0 ); + REQUIRE( cz75mag_20rd.ammo_default() ); + itype_id ammo_default_id = cz75mag_20rd.ammo_default(); + itype_id ammo9mm_id( "9mm" ); + REQUIRE( ammo_default_id.str() == ammo9mm_id.str() ); + const ammotype &amtype = ammo9mm_id->ammo->type; + REQUIRE( cz75mag_20rd.ammo_capacity( amtype ) == 20 ); + WHEN( "set 9mm ammo in the magazine w/o quantity" ) { + cz75mag_20rd.ammo_set( ammo9mm_id ); + THEN( "magazine has 20 rounds of 9mm" ) { + CHECK( cz75mag_20rd.ammo_remaining() == 20 ); + CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the magazine -1 quantity" ) { - cz75mag_20rd.ammo_set( ammo9mm_id, -1 ); - THEN( "magazine has 20 rounds of 9mm" ) { - CHECK( cz75mag_20rd.ammo_remaining() == 20 ); - CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the magazine -1 quantity" ) { + cz75mag_20rd.ammo_set( ammo9mm_id, -1 ); + THEN( "magazine has 20 rounds of 9mm" ) { + CHECK( cz75mag_20rd.ammo_remaining() == 20 ); + CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the magazine 21 quantity" ) { - cz75mag_20rd.ammo_set( ammo9mm_id, 21 ); - THEN( "magazine has 20 rounds of 9mm" ) { - CHECK( cz75mag_20rd.ammo_remaining() == 20 ); - CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the magazine 21 quantity" ) { + cz75mag_20rd.ammo_set( ammo9mm_id, 21 ); + THEN( "magazine has 20 rounds of 9mm" ) { + CHECK( cz75mag_20rd.ammo_remaining() == 20 ); + CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the magazine 20 quantity" ) { - cz75mag_20rd.ammo_set( ammo9mm_id, 20 ); - THEN( "magazine has 20 rounds of 9mm" ) { - CHECK( cz75mag_20rd.ammo_remaining() == 20 ); - CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the magazine 20 quantity" ) { + cz75mag_20rd.ammo_set( ammo9mm_id, 20 ); + THEN( "magazine has 20 rounds of 9mm" ) { + CHECK( cz75mag_20rd.ammo_remaining() == 20 ); + CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the magazine 12 quantity" ) { - cz75mag_20rd.ammo_set( ammo9mm_id, 12 ); - THEN( "magazine has 12 rounds of 9mm" ) { - CHECK( cz75mag_20rd.ammo_remaining() == 12 ); - CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the magazine 12 quantity" ) { + cz75mag_20rd.ammo_set( ammo9mm_id, 12 ); + THEN( "magazine has 12 rounds of 9mm" ) { + CHECK( cz75mag_20rd.ammo_remaining() == 12 ); + CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the magazine 1 quantity" ) { - cz75mag_20rd.ammo_set( ammo9mm_id, 1 ); - THEN( "magazine has 1 round of 9mm" ) { - CHECK( cz75mag_20rd.ammo_remaining() == 1 ); - CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the magazine 1 quantity" ) { + cz75mag_20rd.ammo_set( ammo9mm_id, 1 ); + THEN( "magazine has 1 round of 9mm" ) { + CHECK( cz75mag_20rd.ammo_remaining() == 1 ); + CHECK( cz75mag_20rd.ammo_current().str() == ammo9mm_id.str() ); + } + } + WHEN( "set 9mm ammo in the magazine 0 quantity" ) { + cz75mag_20rd.ammo_set( ammo9mm_id, 0 ); + THEN( "magazine has 0 round of null" ) { + CHECK( cz75mag_20rd.ammo_remaining() == 0 ); + CHECK( cz75mag_20rd.ammo_current().is_null() ); + } + } + WHEN( "set 9mm FMJ ammo in the magazine 15 quantity" ) { + itype_id ammo9mmfmj_id( "9mmfmj" ); + cz75mag_20rd.ammo_set( ammo9mmfmj_id, 15 ); + THEN( "magazine has 15 round of 9mm FMJ" ) { + CHECK( cz75mag_20rd.ammo_remaining() == 15 ); + CHECK( cz75mag_20rd.ammo_current().str() == ammo9mmfmj_id.str() ); } - WHEN( "set 9mm ammo in the magazine 0 quantity" ) { - cz75mag_20rd.ammo_set( ammo9mm_id, 0 ); - THEN( "magazine has 0 round of null" ) { + } + WHEN( "set 308 ammo in the 9mm magazine" ) { + itype_id ammo308_id( "308" ); + std::string dmsg = capture_debugmsg_during( [&cz75mag_20rd, &ammo308_id]() { + cz75mag_20rd.ammo_set( ammo308_id, 15 ); + } ); + THEN( "get debugmsg with \"Tried to set invalid ammo of 308 for cz75mag_20rd\"" ) { + CHECK_THAT( dmsg, Catch::EndsWith( "Tried to set invalid ammo of 308 for cz75mag_20rd" ) ); + AND_THEN( "magazine has 0 round of null" ) { CHECK( cz75mag_20rd.ammo_remaining() == 0 ); CHECK( cz75mag_20rd.ammo_current().is_null() ); } } - WHEN( "set 9mm FMJ ammo in the magazine 15 quantity" ) { - itype_id ammo9mmfmj_id( "9mmfmj" ); - cz75mag_20rd.ammo_set( ammo9mmfmj_id, 15 ); - THEN( "magazine has 15 round of 9mm FMJ" ) { - CHECK( cz75mag_20rd.ammo_remaining() == 15 ); - CHECK( cz75mag_20rd.ammo_current().str() == ammo9mmfmj_id.str() ); - } + } + } + GIVEN( "empty M24 gun with capacity of 5 .308 rounds" ) { + item m24( "M24" ); + REQUIRE( m24.is_gun() ); + REQUIRE( m24.is_magazine() ); + REQUIRE( m24.ammo_remaining() == 0 ); + REQUIRE( m24.ammo_default() ); + itype_id ammo_default_id = m24.ammo_default(); + itype_id ammo308_id( "308" ); + itype_id ammo762_51_id( "762_51" ); + REQUIRE( ammo_default_id.str() == ammo762_51_id.str() ); + const ammotype &amtype = ammo308_id->ammo->type; + REQUIRE( m24.ammo_capacity( amtype ) == 5 ); + WHEN( "set 308 ammo in the gun with internal magazine w/o quantity" ) { + m24.ammo_set( ammo308_id ); + THEN( "gun has 5 rounds of 308" ) { + CHECK( m24.ammo_remaining() == 5 ); + CHECK( m24.ammo_current().str() == ammo308_id.str() ); } - WHEN( "set 308 ammo in the 9mm magazine" ) { - itype_id ammo308_id( "308" ); - std::string dmsg = capture_debugmsg_during( [&cz75mag_20rd, &ammo308_id]() { - cz75mag_20rd.ammo_set( ammo308_id, 15 ); - } ); - THEN( "get debugmsg with \"Tried to set invalid ammo of 308 for cz75mag_20rd\"" ) { - CHECK_THAT( dmsg, Catch::EndsWith( "Tried to set invalid ammo of 308 for cz75mag_20rd" ) ); - AND_THEN( "magazine has 0 round of null" ) { - CHECK( cz75mag_20rd.ammo_remaining() == 0 ); - CHECK( cz75mag_20rd.ammo_current().is_null() ); - } - } + } + WHEN( "set 308 ammo in the gun with internal magazine -1 quantity" ) { + m24.ammo_set( ammo308_id, -1 ); + THEN( "gun has 5 rounds of 308" ) { + CHECK( m24.ammo_remaining() == 5 ); + CHECK( m24.ammo_current().str() == ammo308_id.str() ); } } - GIVEN( "empty M24 gun with capacity of 5 .308 rounds" ) { - item m24( "M24" ); - REQUIRE( m24.is_gun() ); - REQUIRE( m24.is_magazine() ); - REQUIRE( m24.ammo_remaining() == 0 ); - REQUIRE( m24.ammo_default() ); - itype_id ammo_default_id = m24.ammo_default(); - itype_id ammo308_id( "308" ); - itype_id ammo762_51_id( "762_51" ); - REQUIRE( ammo_default_id.str() == ammo762_51_id.str() ); - const ammotype &amtype = ammo308_id->ammo->type; - REQUIRE( m24.ammo_capacity( amtype ) == 5 ); - WHEN( "set 308 ammo in the gun with internal magazine w/o quantity" ) { - m24.ammo_set( ammo308_id ); - THEN( "gun has 5 rounds of 308" ) { - CHECK( m24.ammo_remaining() == 5 ); - CHECK( m24.ammo_current().str() == ammo308_id.str() ); - } + WHEN( "set 308 ammo in the gun with internal magazine 500 quantity" ) { + m24.ammo_set( ammo308_id, 500 ); + THEN( "gun has 5 rounds of 308" ) { + CHECK( m24.ammo_remaining() == 5 ); + CHECK( m24.ammo_current().str() == ammo308_id.str() ); } - WHEN( "set 308 ammo in the gun with internal magazine -1 quantity" ) { - m24.ammo_set( ammo308_id, -1 ); - THEN( "gun has 5 rounds of 308" ) { - CHECK( m24.ammo_remaining() == 5 ); - CHECK( m24.ammo_current().str() == ammo308_id.str() ); - } + } + WHEN( "set 308 ammo in the gun with internal magazine 5 quantity" ) { + m24.ammo_set( ammo308_id, 5 ); + THEN( "gun has 5 rounds of 308" ) { + CHECK( m24.ammo_remaining() == 5 ); + CHECK( m24.ammo_current().str() == ammo308_id.str() ); } - WHEN( "set 308 ammo in the gun with internal magazine 500 quantity" ) { - m24.ammo_set( ammo308_id, 500 ); - THEN( "gun has 5 rounds of 308" ) { - CHECK( m24.ammo_remaining() == 5 ); - CHECK( m24.ammo_current().str() == ammo308_id.str() ); - } + } + WHEN( "set 308 ammo in the gun with internal magazine 4 quantity" ) { + m24.ammo_set( ammo308_id, 4 ); + THEN( "gun has 4 rounds of 308" ) { + CHECK( m24.ammo_remaining() == 4 ); + CHECK( m24.ammo_current().str() == ammo308_id.str() ); } - WHEN( "set 308 ammo in the gun with internal magazine 5 quantity" ) { - m24.ammo_set( ammo308_id, 5 ); - THEN( "gun has 5 rounds of 308" ) { - CHECK( m24.ammo_remaining() == 5 ); - CHECK( m24.ammo_current().str() == ammo308_id.str() ); - } + } + WHEN( "set 308 ammo in the gun with internal magazine 1 quantity" ) { + m24.ammo_set( ammo308_id, 1 ); + THEN( "gun has 41rounds of 308" ) { + CHECK( m24.ammo_remaining() == 1 ); + CHECK( m24.ammo_current().str() == ammo308_id.str() ); } - WHEN( "set 308 ammo in the gun with internal magazine 4 quantity" ) { - m24.ammo_set( ammo308_id, 4 ); - THEN( "gun has 4 rounds of 308" ) { - CHECK( m24.ammo_remaining() == 4 ); - CHECK( m24.ammo_current().str() == ammo308_id.str() ); - } + } + WHEN( "set 308 ammo in the gun with internal magazine 0 quantity" ) { + m24.ammo_set( ammo308_id, 0 ); + THEN( "gun has 0 rounds of null" ) { + CHECK( m24.ammo_remaining() == 0 ); + CHECK( m24.ammo_current().is_null() ); } - WHEN( "set 308 ammo in the gun with internal magazine 1 quantity" ) { - m24.ammo_set( ammo308_id, 1 ); - THEN( "gun has 41rounds of 308" ) { - CHECK( m24.ammo_remaining() == 1 ); - CHECK( m24.ammo_current().str() == ammo308_id.str() ); - } + } + WHEN( "set 762_51 ammo in the gun with internal magazine 2 quantity" ) { + m24.ammo_set( ammo762_51_id, 2 ); + THEN( "gun has 2 rounds of 762_51" ) { + CHECK( m24.ammo_remaining() == 2 ); + CHECK( m24.ammo_current().str() == ammo762_51_id.str() ); } - WHEN( "set 308 ammo in the gun with internal magazine 0 quantity" ) { - m24.ammo_set( ammo308_id, 0 ); - THEN( "gun has 0 rounds of null" ) { + } + WHEN( "set 9mm ammo in ammo in the .308 gun" ) { + itype_id ammo9mm_id( "9mm" ); + std::string dmsg = capture_debugmsg_during( [&m24, &ammo9mm_id]() { + m24.ammo_set( ammo9mm_id, 2 ); + } ); + THEN( "get debugmsg with \"Tried to set invalid ammo of 9mm for M24\"" ) { + CHECK_THAT( dmsg, Catch::EndsWith( "Tried to set invalid ammo of 9mm for M24" ) ); + AND_THEN( "gun has 0 round of null" ) { CHECK( m24.ammo_remaining() == 0 ); CHECK( m24.ammo_current().is_null() ); } } - WHEN( "set 762_51 ammo in the gun with internal magazine 2 quantity" ) { - m24.ammo_set( ammo762_51_id, 2 ); - THEN( "gun has 2 rounds of 762_51" ) { - CHECK( m24.ammo_remaining() == 2 ); - CHECK( m24.ammo_current().str() == ammo762_51_id.str() ); - } + } + } +} + +TEST_CASE( "ammo_set items with MAGAZINE_WELL pockets with magazine", + "[ammo_set][magazine][ammo]" ) +{ + GIVEN( "CZ 75 B 9mm gun with empty 9mm CZ 75 20-round magazine" ) { + item cz75( "cz75" ); + item cz75mag_20rd( "cz75mag_20rd" ); + REQUIRE( cz75.is_gun() ); + REQUIRE_FALSE( cz75.is_magazine() ); + REQUIRE( cz75.magazine_current() == nullptr ); + REQUIRE( cz75.magazine_compatible().count( cz75mag_20rd.typeId() ) == 1 ); + REQUIRE( cz75mag_20rd.is_magazine() ); + REQUIRE( cz75mag_20rd.ammo_remaining() == 0 ); + REQUIRE( cz75mag_20rd.ammo_default() ); + itype_id ammo_default_id = cz75mag_20rd.ammo_default(); + itype_id ammo9mm_id( "9mm" ); + REQUIRE( ammo_default_id.str() == ammo9mm_id.str() ); + const ammotype &amtype = ammo9mm_id->ammo->type; + REQUIRE( cz75mag_20rd.ammo_capacity( amtype ) == 20 ); + cz75.put_in( cz75mag_20rd, item_pocket::pocket_type::MAGAZINE_WELL ); + REQUIRE( cz75.magazine_current()->typeId().str() == cz75mag_20rd.typeId().str() ); + REQUIRE( cz75.ammo_capacity( amtype ) == 20 ); + WHEN( "set 9mm ammo in the gun with magazine w/o quantity" ) { + cz75.ammo_set( ammo9mm_id ); + THEN( "gun and current magazine has 20 rounds of 9mm" ) { + CHECK( cz75.ammo_remaining() == 20 ); + CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 20 ); + CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in ammo in the .308 gun" ) { - itype_id ammo9mm_id( "9mm" ); - std::string dmsg = capture_debugmsg_during( [&m24, &ammo9mm_id]() { - m24.ammo_set( ammo9mm_id, 2 ); - } ); - THEN( "get debugmsg with \"Tried to set invalid ammo of 9mm for M24\"" ) { - CHECK_THAT( dmsg, Catch::EndsWith( "Tried to set invalid ammo of 9mm for M24" ) ); - AND_THEN( "gun has 0 round of null" ) { - CHECK( m24.ammo_remaining() == 0 ); - CHECK( m24.ammo_current().is_null() ); - } - } + } + WHEN( "set 9mm ammo in the gun with magazine -1 quantity" ) { + cz75.ammo_set( ammo9mm_id, -1 ); + THEN( "gun and current magazine has 20 rounds of 9mm" ) { + CHECK( cz75.ammo_remaining() == 20 ); + CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 20 ); + CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); } } - } - SECTION( "ammo_set items with MAGAZINE_WELL pockets" ) { - GIVEN( "CZ 75 B 9mm gun with empty 9mm CZ 75 20-round magazine" ) { - item cz75( "cz75" ); - item cz75mag_20rd( "cz75mag_20rd" ); - REQUIRE( cz75.is_gun() ); - REQUIRE_FALSE( cz75.is_magazine() ); - REQUIRE( cz75.magazine_current() == nullptr ); - REQUIRE( cz75.magazine_compatible().count( cz75mag_20rd.typeId() ) == 1 ); - REQUIRE( cz75mag_20rd.is_magazine() ); - REQUIRE( cz75mag_20rd.ammo_remaining() == 0 ); - REQUIRE( cz75mag_20rd.ammo_default() ); - itype_id ammo_default_id = cz75mag_20rd.ammo_default(); - itype_id ammo9mm_id( "9mm" ); - REQUIRE( ammo_default_id.str() == ammo9mm_id.str() ); - const ammotype &amtype = ammo9mm_id->ammo->type; - REQUIRE( cz75mag_20rd.ammo_capacity( amtype ) == 20 ); - cz75.put_in( cz75mag_20rd, item_pocket::pocket_type::MAGAZINE_WELL ); - REQUIRE( cz75.magazine_current()->typeId().str() == cz75mag_20rd.typeId().str() ); - REQUIRE( cz75.ammo_capacity( amtype ) == 20 ); - WHEN( "set 9mm ammo in the gun with magazine w/o quantity" ) { - cz75.ammo_set( ammo9mm_id ); - THEN( "gun and current magazine has 20 rounds of 9mm" ) { - CHECK( cz75.ammo_remaining() == 20 ); - CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 20 ); - CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); - } + WHEN( "set 9mm ammo in the gun with magazine 21 quantity" ) { + cz75.ammo_set( ammo9mm_id, 21 ); + THEN( "gun and current magazine has 20 rounds of 9mm" ) { + CHECK( cz75.ammo_remaining() == 20 ); + CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 20 ); + CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the gun with magazine -1 quantity" ) { - cz75.ammo_set( ammo9mm_id, -1 ); - THEN( "gun and current magazine has 20 rounds of 9mm" ) { - CHECK( cz75.ammo_remaining() == 20 ); - CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 20 ); - CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the gun with magazine 20 quantity" ) { + cz75.ammo_set( ammo9mm_id, 20 ); + THEN( "gun and current magazine has 20 rounds of 9mm" ) { + CHECK( cz75.ammo_remaining() == 20 ); + CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 20 ); + CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the gun with magazine 21 quantity" ) { - cz75.ammo_set( ammo9mm_id, 21 ); - THEN( "gun and current magazine has 20 rounds of 9mm" ) { - CHECK( cz75.ammo_remaining() == 20 ); - CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 20 ); - CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the gun with magazine 12 quantity" ) { + cz75.ammo_set( ammo9mm_id, 12 ); + THEN( "gun and current magazine has 12 rounds of 9mm" ) { + CHECK( cz75.ammo_remaining() == 12 ); + CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 12 ); + CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the gun with magazine 20 quantity" ) { - cz75.ammo_set( ammo9mm_id, 20 ); - THEN( "gun and current magazine has 20 rounds of 9mm" ) { - CHECK( cz75.ammo_remaining() == 20 ); - CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 20 ); - CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the current magazine of a gun 1 quantity" ) { + cz75.magazine_current()->ammo_set( ammo9mm_id, 1 ); + THEN( "gun and current magazine has 1 round of 9mm" ) { + CHECK( cz75.ammo_remaining() == 1 ); + CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 1 ); + CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the gun with magazine 12 quantity" ) { - cz75.ammo_set( ammo9mm_id, 12 ); - THEN( "gun and current magazine has 12 rounds of 9mm" ) { - CHECK( cz75.ammo_remaining() == 12 ); - CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 12 ); - CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the gun with magazine 0 quantity" ) { + cz75.ammo_set( ammo9mm_id, 0 ); + THEN( "gun and current magazine has 0 rounds of null" ) { + CHECK( cz75.ammo_remaining() == 0 ); + CHECK( cz75.ammo_current().is_null() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 0 ); + CHECK( cz75.magazine_current()->ammo_current().is_null() ); } - WHEN( "set 9mm ammo in the current magazine of a gun 1 quantity" ) { - cz75.magazine_current()->ammo_set( ammo9mm_id, 1 ); - THEN( "gun and current magazine has 1 round of 9mm" ) { - CHECK( cz75.ammo_remaining() == 1 ); - CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 1 ); - CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm FMJ ammo in the gun with magazine 10 quantity" ) { + itype_id ammo9mmfmj_id( "9mmfmj" ); + cz75.ammo_set( ammo9mmfmj_id, 10 ); + THEN( "gun and current magazine has 10 rounds of 9mm FMJ" ) { + CHECK( cz75.ammo_remaining() == 10 ); + CHECK( cz75.ammo_current().str() == ammo9mmfmj_id.str() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 10 ); + CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mmfmj_id.str() ); } - WHEN( "set 9mm ammo in the gun with magazine 0 quantity" ) { - cz75.ammo_set( ammo9mm_id, 0 ); - THEN( "gun and current magazine has 0 rounds of null" ) { + } + WHEN( "set 308 ammo in the 9mm gun with magazine" ) { + itype_id ammo308_id( "308" ); + std::string dmsg = capture_debugmsg_during( [&cz75, &ammo308_id]() { + cz75.ammo_set( ammo308_id, 15 ); + } ); + THEN( "get debugmsg with \"Tried to set invalid ammo of 308 for cz75\"" ) { + CHECK_THAT( dmsg, Catch::EndsWith( "Tried to set invalid ammo of 308 for cz75" ) ); + AND_THEN( "gun has 0 round of null" ) { CHECK( cz75.ammo_remaining() == 0 ); CHECK( cz75.ammo_current().is_null() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 0 ); - CHECK( cz75.magazine_current()->ammo_current().is_null() ); - } - } - WHEN( "set 9mm FMJ ammo in the gun with magazine 10 quantity" ) { - itype_id ammo9mmfmj_id( "9mmfmj" ); - cz75.ammo_set( ammo9mmfmj_id, 10 ); - THEN( "gun and current magazine has 10 rounds of 9mm FMJ" ) { - CHECK( cz75.ammo_remaining() == 10 ); - CHECK( cz75.ammo_current().str() == ammo9mmfmj_id.str() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 10 ); - CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mmfmj_id.str() ); - } - } - WHEN( "set 308 ammo in the 9mm gun with magazine" ) { - itype_id ammo308_id( "308" ); - std::string dmsg = capture_debugmsg_during( [&cz75, &ammo308_id]() { - cz75.ammo_set( ammo308_id, 15 ); - } ); - THEN( "get debugmsg with \"Tried to set invalid ammo of 308 for cz75\"" ) { - CHECK_THAT( dmsg, Catch::EndsWith( "Tried to set invalid ammo of 308 for cz75" ) ); - AND_THEN( "gun has 0 round of null" ) { - CHECK( cz75.ammo_remaining() == 0 ); - CHECK( cz75.ammo_current().is_null() ); - } } } } - GIVEN( "CZ 75 B 9mm gun w/o magazine" ) { - item cz75( "cz75" ); - itype_id cz75mag_12rd_id( "cz75mag_12rd" ); - itype_id cz75mag_20rd_id( "cz75mag_20rd" ); - itype_id cz75mag_26rd_id( "cz75mag_26rd" ); - itype_id ammo9mm_id( "9mm" ); - REQUIRE( cz75.is_gun() ); - REQUIRE_FALSE( cz75.is_magazine() ); - REQUIRE( cz75.magazine_current() == nullptr ); - REQUIRE( cz75.magazine_compatible().size() == 3 ); - REQUIRE( cz75.magazine_compatible().count( cz75mag_12rd_id ) == 1 ); - REQUIRE( cz75.magazine_compatible().count( cz75mag_20rd_id ) == 1 ); - REQUIRE( cz75.magazine_compatible().count( cz75mag_26rd_id ) == 1 ); - REQUIRE( cz75.magazine_default().str() == cz75mag_12rd_id.str() ); - const ammotype &amtype = ammo9mm_id->ammo->type; - REQUIRE( cz75.ammo_capacity( amtype ) == 0 ); - REQUIRE( !cz75.ammo_default().is_null() ); - REQUIRE( cz75.magazine_default()->magazine->default_ammo.str() == ammo9mm_id.str() ); - WHEN( "set 9mm ammo in the gun w/o magazine w/o quantity" ) { - cz75.ammo_set( ammo9mm_id ); - THEN( "gun with new cz75mag_12rd magazine has 12 rounds of 9mm" ) { - CHECK( cz75.ammo_remaining() == 12 ); - CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); - REQUIRE( cz75.magazine_current() != nullptr ); - CHECK( cz75.magazine_current()->typeId().str() == cz75mag_12rd_id.str() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 12 ); - CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); - } + } +} + +TEST_CASE( "ammo_set items with MAGAZINE_WELL pockets without magazine", + "[ammo_set][magazine][ammo]" ) +{ + GIVEN( "CZ 75 B 9mm gun w/o magazine" ) { + item cz75( "cz75" ); + itype_id cz75mag_12rd_id( "cz75mag_12rd" ); + itype_id cz75mag_20rd_id( "cz75mag_20rd" ); + itype_id cz75mag_26rd_id( "cz75mag_26rd" ); + itype_id ammo9mm_id( "9mm" ); + REQUIRE( cz75.is_gun() ); + REQUIRE_FALSE( cz75.is_magazine() ); + REQUIRE( cz75.magazine_current() == nullptr ); + REQUIRE( cz75.magazine_compatible().size() == 3 ); + REQUIRE( cz75.magazine_compatible().count( cz75mag_12rd_id ) == 1 ); + REQUIRE( cz75.magazine_compatible().count( cz75mag_20rd_id ) == 1 ); + REQUIRE( cz75.magazine_compatible().count( cz75mag_26rd_id ) == 1 ); + REQUIRE( cz75.magazine_default().str() == cz75mag_12rd_id.str() ); + const ammotype &amtype = ammo9mm_id->ammo->type; + REQUIRE( cz75.ammo_capacity( amtype ) == 0 ); + REQUIRE( !cz75.ammo_default().is_null() ); + REQUIRE( cz75.magazine_default()->magazine->default_ammo.str() == ammo9mm_id.str() ); + WHEN( "set 9mm ammo in the gun w/o magazine w/o quantity" ) { + cz75.ammo_set( ammo9mm_id ); + THEN( "gun with new cz75mag_12rd magazine has 12 rounds of 9mm" ) { + CHECK( cz75.ammo_remaining() == 12 ); + CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); + REQUIRE( cz75.magazine_current() != nullptr ); + CHECK( cz75.magazine_current()->typeId().str() == cz75mag_12rd_id.str() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 12 ); + CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the gun w/o magazine 19 quantity" ) { - cz75.ammo_set( ammo9mm_id, 19 ); - THEN( "gun with new cz75mag_20rd magazine has 19 rounds of 9mm" ) { - CHECK( cz75.ammo_remaining() == 19 ); - CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); - REQUIRE( cz75.magazine_current() != nullptr ); - CHECK( cz75.magazine_current()->typeId().str() == cz75mag_20rd_id.str() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 19 ); - CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the gun w/o magazine 19 quantity" ) { + cz75.ammo_set( ammo9mm_id, 19 ); + THEN( "gun with new cz75mag_20rd magazine has 19 rounds of 9mm" ) { + CHECK( cz75.ammo_remaining() == 19 ); + CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); + REQUIRE( cz75.magazine_current() != nullptr ); + CHECK( cz75.magazine_current()->typeId().str() == cz75mag_20rd_id.str() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 19 ); + CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the gun w/o magazine 21 quantity" ) { - cz75.ammo_set( ammo9mm_id, 21 ); - THEN( "gun with new cz75mag_26rd magazine has 21 rounds of 9mm" ) { - CHECK( cz75.ammo_remaining() == 21 ); - CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); - REQUIRE( cz75.magazine_current() != nullptr ); - CHECK( cz75.magazine_current()->typeId().str() == cz75mag_26rd_id.str() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 21 ); - CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the gun w/o magazine 21 quantity" ) { + cz75.ammo_set( ammo9mm_id, 21 ); + THEN( "gun with new cz75mag_26rd magazine has 21 rounds of 9mm" ) { + CHECK( cz75.ammo_remaining() == 21 ); + CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); + REQUIRE( cz75.magazine_current() != nullptr ); + CHECK( cz75.magazine_current()->typeId().str() == cz75mag_26rd_id.str() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 21 ); + CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 9mm ammo in the gun w/o magazine 9000 quantity" ) { - cz75.ammo_set( ammo9mm_id, 9000 ); - THEN( "gun with new cz75mag_26rd magazine has 26 rounds of 9mm" ) { - CHECK( cz75.ammo_remaining() == 26 ); - CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); - REQUIRE( cz75.magazine_current() != nullptr ); - CHECK( cz75.magazine_current()->typeId().str() == cz75mag_26rd_id.str() ); - CHECK( cz75.magazine_current()->ammo_remaining() == 26 ); - CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); - } + } + WHEN( "set 9mm ammo in the gun w/o magazine 9000 quantity" ) { + cz75.ammo_set( ammo9mm_id, 9000 ); + THEN( "gun with new cz75mag_26rd magazine has 26 rounds of 9mm" ) { + CHECK( cz75.ammo_remaining() == 26 ); + CHECK( cz75.ammo_current().str() == ammo9mm_id.str() ); + REQUIRE( cz75.magazine_current() != nullptr ); + CHECK( cz75.magazine_current()->typeId().str() == cz75mag_26rd_id.str() ); + CHECK( cz75.magazine_current()->ammo_remaining() == 26 ); + CHECK( cz75.magazine_current()->ammo_current().str() == ammo9mm_id.str() ); } - WHEN( "set 308 ammo in the 9mm gun w/o magazine 2 quantity" ) { - itype_id ammo308_id( "308" ); - std::string dmsg = capture_debugmsg_during( [&cz75, &ammo308_id]() { - cz75.ammo_set( ammo308_id, 2 ); - } ); - THEN( "get debugmsg with \"Tried to set invalid ammo of 308 for cz75\"" ) { - REQUIRE( !dmsg.empty() ); - CHECK_THAT( dmsg, Catch::EndsWith( "Tried to set invalid ammo of 308 for cz75" ) ); - AND_THEN( "gun w/o magazine has 0 round of null" ) { - CHECK( cz75.ammo_remaining() == 0 ); - CHECK( cz75.ammo_current().is_null() ); - CHECK( cz75.magazine_current() == nullptr ); - } + } + WHEN( "set 308 ammo in the 9mm gun w/o magazine 2 quantity" ) { + itype_id ammo308_id( "308" ); + std::string dmsg = capture_debugmsg_during( [&cz75, &ammo308_id]() { + cz75.ammo_set( ammo308_id, 2 ); + } ); + THEN( "get debugmsg with \"Tried to set invalid ammo of 308 for cz75\"" ) { + REQUIRE( !dmsg.empty() ); + CHECK_THAT( dmsg, Catch::EndsWith( "Tried to set invalid ammo of 308 for cz75" ) ); + AND_THEN( "gun w/o magazine has 0 round of null" ) { + CHECK( cz75.ammo_remaining() == 0 ); + CHECK( cz75.ammo_current().is_null() ); + CHECK( cz75.magazine_current() == nullptr ); } } } } - SECTION( "ammo_set items with CONTAINER pockets" ) { - GIVEN( "small box" ) { - item box( "box_small" ); - REQUIRE_FALSE( box.is_gun() ); - REQUIRE_FALSE( box.is_magazine() ); - REQUIRE( box.is_container_empty() ); - REQUIRE( box.magazine_current() == nullptr ); - REQUIRE( box.magazine_compatible().empty() ); - itype_id ammo9mm_id( "9mm" ); - WHEN( "set 9mm ammo in the small box" ) { - std::string dmsg = capture_debugmsg_during( [&box, &ammo9mm_id]() { - box.ammo_set( ammo9mm_id, 10 ); - } ); - THEN( "get debugmsg with \"Tried to set invalid ammo of 9mm for box_small\"" ) { - CHECK_THAT( dmsg, Catch::EndsWith( "Tried to set invalid ammo of 9mm for box_small" ) ); - AND_THEN( "small box still empty" ) { - REQUIRE_FALSE( box.is_gun() ); - REQUIRE_FALSE( box.is_magazine() ); - CHECK( box.is_container_empty() ); - } +} + +TEST_CASE( "ammo_set items with CONTAINER pockets", "[ammo_set][magazine][ammo]" ) +{ + GIVEN( "small box" ) { + item box( "box_small" ); + REQUIRE_FALSE( box.is_gun() ); + REQUIRE_FALSE( box.is_magazine() ); + REQUIRE( box.is_container_empty() ); + REQUIRE( box.magazine_current() == nullptr ); + REQUIRE( box.magazine_compatible().empty() ); + itype_id ammo9mm_id( "9mm" ); + WHEN( "set 9mm ammo in the small box" ) { + std::string dmsg = capture_debugmsg_during( [&box, &ammo9mm_id]() { + box.ammo_set( ammo9mm_id, 10 ); + } ); + THEN( "get debugmsg with \"Tried to set invalid ammo of 9mm for box_small\"" ) { + CHECK_THAT( dmsg, Catch::EndsWith( "Tried to set invalid ammo of 9mm for box_small" ) ); + AND_THEN( "small box still empty" ) { + REQUIRE_FALSE( box.is_gun() ); + REQUIRE_FALSE( box.is_magazine() ); + CHECK( box.is_container_empty() ); } } } diff --git a/tests/ammo_test.cpp b/tests/ammo_test.cpp index d33472650edce..ba7544744dfb8 100644 --- a/tests/ammo_test.cpp +++ b/tests/ammo_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include diff --git a/tests/archery_damage_test.cpp b/tests/archery_damage_test.cpp index 78f8a1d0cd2cf..34ee5ceeec6ba 100644 --- a/tests/archery_damage_test.cpp +++ b/tests/archery_damage_test.cpp @@ -16,7 +16,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "damage.h" #include "game_constants.h" #include "item.h" diff --git a/tests/assertion_helpers.h b/tests/assertion_helpers.h index b35d54f1bf433..f8cc5358a8187 100644 --- a/tests/assertion_helpers.h +++ b/tests/assertion_helpers.h @@ -2,7 +2,7 @@ #ifndef CATA_TESTS_ASSERTION_HELPERS_H #define CATA_TESTS_ASSERTION_HELPERS_H -#include "catch/catch.hpp" +#include "cata_catch.h" #include diff --git a/tests/battery_mod_test.cpp b/tests/battery_mod_test.cpp index 19c6f0d93eef6..62054945f75d6 100644 --- a/tests/battery_mod_test.cpp +++ b/tests/battery_mod_test.cpp @@ -7,7 +7,7 @@ #include #include "avatar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "debug.h" #include "item.h" #include "item_contents.h" diff --git a/tests/behavior_test.cpp b/tests/behavior_test.cpp index 145abed552152..c54ada29b7539 100644 --- a/tests/behavior_test.cpp +++ b/tests/behavior_test.cpp @@ -6,7 +6,7 @@ #include "behavior.h" #include "behavior_strategy.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character_oracle.h" #include "item.h" #include "item_location.h" @@ -160,7 +160,7 @@ TEST_CASE( "check_npc_behavior_tree", "[npc][behavior]" ) test_npc.update_bodytemp(); REQUIRE( oracle.needs_warmth_badly( "" ) == behavior::status_t::running ); CHECK( npc_needs.tick( &oracle ) == "idle" ); - test_npc.worn.push_back( item( "backpack" ) ); + test_npc.worn.emplace_back( "backpack" ); item &sweater = test_npc.i_add( item( itype_id( "sweater" ) ) ); CHECK( oracle.can_wear_warmer_clothes( "" ) == behavior::status_t::running ); CHECK( npc_needs.tick( &oracle ) == "wear_warmer_clothes" ); diff --git a/tests/bionics_test.cpp b/tests/bionics_test.cpp index 3474dd778715c..28f12d95e495b 100644 --- a/tests/bionics_test.cpp +++ b/tests/bionics_test.cpp @@ -7,7 +7,7 @@ #include "avatar.h" #include "bionics.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "item.h" #include "item_pocket.h" #include "npc.h" diff --git a/tests/calendar_test.cpp b/tests/calendar_test.cpp index 7ca3c60cbbb9d..925b3fe123c4a 100644 --- a/tests/calendar_test.cpp +++ b/tests/calendar_test.cpp @@ -2,7 +2,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" TEST_CASE( "time_duration_to_string", "[calendar]" ) { diff --git a/tests/cata_catch.h b/tests/cata_catch.h new file mode 100644 index 0000000000000..f03278aead309 --- /dev/null +++ b/tests/cata_catch.h @@ -0,0 +1,15 @@ +#pragma once +#ifndef CATA_TESTS_CATA_CATCH_H +#define CATA_TESTS_CATA_CATCH_H + +// To avoid ODR violations, it's important that whenever a file includes +// catch.hpp it also includes stringmaker.h, so that all specializations of +// StringMaker match. Therefore, all test code should include catch.hpp via +// this file. + +// IWYU pragma: begin_exports +#include "catch/catch.hpp" +#include "stringmaker.h" +// IWYU pragma: end_exports + +#endif // CATA_TESTS_CATA_CATCH_H diff --git a/tests/cata_generators.h b/tests/cata_generators.h index 85dbc1c6a0e9f..e6009f863ab90 100644 --- a/tests/cata_generators.h +++ b/tests/cata_generators.h @@ -4,7 +4,7 @@ // Some Catch2 Generators for generating our data types -#include "catch/catch.hpp" +#include "cata_catch.h" #include "game_constants.h" struct point; diff --git a/tests/cata_utility_test.cpp b/tests/cata_utility_test.cpp index bccb6b4c2bad0..50a32cb6fa466 100644 --- a/tests/cata_utility_test.cpp +++ b/tests/cata_utility_test.cpp @@ -9,7 +9,7 @@ #include "assertion_helpers.h" #include "cata_utility.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "debug_menu.h" #include "units.h" #include "units_utility.h" diff --git a/tests/cata_variant_test.cpp b/tests/cata_variant_test.cpp index cf7ec3673d9d9..dc2637d3cdae5 100644 --- a/tests/cata_variant_test.cpp +++ b/tests/cata_variant_test.cpp @@ -3,7 +3,7 @@ #include #include "cata_variant.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character_id.h" #include "debug_menu.h" #include "enum_conversions.h" diff --git a/tests/catacharset_test.cpp b/tests/catacharset_test.cpp index 76c10fe23fba3..77545b40a581f 100644 --- a/tests/catacharset_test.cpp +++ b/tests/catacharset_test.cpp @@ -8,7 +8,7 @@ #include #include "catacharset.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "translations.h" TEST_CASE( "utf8_width", "[catacharset]" ) diff --git a/tests/char_biometrics_test.cpp b/tests/char_biometrics_test.cpp index c68dd22bf88ce..bb81397dedb2b 100644 --- a/tests/char_biometrics_test.cpp +++ b/tests/char_biometrics_test.cpp @@ -4,7 +4,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "creature.h" #include "game_constants.h" #include "options.h" diff --git a/tests/char_edible_rating_test.cpp b/tests/char_edible_rating_test.cpp index 37cb87cea01f9..93d9e6594b0bb 100644 --- a/tests/char_edible_rating_test.cpp +++ b/tests/char_edible_rating_test.cpp @@ -4,7 +4,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "flag.h" #include "item.h" diff --git a/tests/char_exposure_test.cpp b/tests/char_exposure_test.cpp index ce631de3dc86c..7e41c320379c2 100644 --- a/tests/char_exposure_test.cpp +++ b/tests/char_exposure_test.cpp @@ -2,7 +2,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "item.h" #include "player_helpers.h" diff --git a/tests/char_healing_test.cpp b/tests/char_healing_test.cpp index d48aa99681517..98be7bd3b2f5d 100644 --- a/tests/char_healing_test.cpp +++ b/tests/char_healing_test.cpp @@ -2,7 +2,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "options.h" #include "player.h" diff --git a/tests/char_sight_test.cpp b/tests/char_sight_test.cpp index 9f1e80477fa82..fe788b1bec57e 100644 --- a/tests/char_sight_test.cpp +++ b/tests/char_sight_test.cpp @@ -2,7 +2,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "flag.h" #include "game.h" diff --git a/tests/char_stamina_test.cpp b/tests/char_stamina_test.cpp index 62be669f530dc..b561e14d4f6a1 100644 --- a/tests/char_stamina_test.cpp +++ b/tests/char_stamina_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "calendar.h" #include "character.h" diff --git a/tests/char_suffer_test.cpp b/tests/char_suffer_test.cpp index 1261d45fac566..ceba9cc43c70d 100644 --- a/tests/char_suffer_test.cpp +++ b/tests/char_suffer_test.cpp @@ -7,7 +7,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "creature.h" #include "flag.h" @@ -15,6 +15,7 @@ #include "item.h" #include "map_helpers.h" #include "player_helpers.h" +#include "test_statistics.h" #include "type_id.h" // Tests for Character suffering @@ -273,25 +274,30 @@ TEST_CASE( "suffering from sunburn", "[char][suffer][sunburn]" ) } - WHEN( "torso and arms are 90%% covered" ) { + WHEN( "torso and arms are 90% covered" ) { dummy.worn.clear(); dummy.wear_item( longshirt, false ); - THEN( "damage to torso is 90%% less than other parts" ) { - bp_hp_lost = test_suffer_bodypart_hp_lost( dummy, 10_minutes ); + THEN( "damage to torso is 90% less than other parts" ) { + time_duration t = 10_minutes; + int num_turns = t / 1_turns; + + bp_hp_lost = test_suffer_bodypart_hp_lost( dummy, t ); for( const bodypart_id &bp : body_parts_with_hp ) { CAPTURE( bp.id().str() ); if( bp.id().str() == "torso" ) { // Torso has only 10% chance losing 2 HP, 3x per minute - CHECK( bp_hp_lost[bp] == Approx( 6 ).margin( 12 ) ); + CHECK_THAT( bp_hp_lost[bp] / 2, + IsBinomialObservation( num_turns, 1.0 / 200 ) ); } else if( bp.id().str() == "arm_l" || bp.id().str() == "arm_r" ) { // Arms have 10% chance of losing 1 HP, 3x per minute (6 in 10m) // But hands are exposed, and still lose 1 HP, 3x per minute (30 in 10m) - CHECK( bp_hp_lost[bp] == Approx( 36 ).margin( 30 ) ); + CHECK_THAT( bp_hp_lost[bp], + IsBinomialObservation( num_turns, 1.0 / 200 + 1.0 / 20 ) ); } else { // All other parts lose 1 HP, 3x per minute (30 in 10m) // but legs+feet combine, and head+mouth combine (60 in 10m) - CHECK( bp_hp_lost[bp] == Approx( 60 ).margin( 40 ) ); + CHECK_THAT( bp_hp_lost[bp], IsBinomialObservation( num_turns, 2.0 / 20 ) ); } } } diff --git a/tests/char_validity_check_test.cpp b/tests/char_validity_check_test.cpp index e8b63149ffe1f..77425e53ab188 100644 --- a/tests/char_validity_check_test.cpp +++ b/tests/char_validity_check_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "char_validity_check.h" TEST_CASE( "char_validity_check" ) diff --git a/tests/clzones_test.cpp b/tests/clzones_test.cpp index 9e8fd6cd9ba02..ba98117dace0d 100644 --- a/tests/clzones_test.cpp +++ b/tests/clzones_test.cpp @@ -1,7 +1,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "clzones.h" #include "item.h" #include "item_category.h" @@ -12,7 +12,6 @@ #include "ret_val.h" #include "type_id.h" -static const zone_type_id zone_type_LOOT_UNSORTED( "LOOT_UNSORTED" ); static const zone_type_id zone_type_LOOT_FOOD( "LOOT_FOOD" ); static const zone_type_id zone_type_LOOT_PFOOD( "LOOT_PFOOD" ); static const zone_type_id zone_type_LOOT_DRINK( "LOOT_DRINK" ); diff --git a/tests/colony_test.cpp b/tests/colony_test.cpp index f210cd7e1f36a..c859a81cfbc86 100644 --- a/tests/colony_test.cpp +++ b/tests/colony_test.cpp @@ -4,7 +4,7 @@ #include #include // range-insert testing -#include "catch/catch.hpp" +#include "cata_catch.h" #include "colony.h" #include "colony_list_test_helpers.h" @@ -638,7 +638,11 @@ TEST_CASE( "colony range erase", "[colony]" ) CHECK( count == 400 ); CHECK( test_colony.size() == 400 ); - unsigned int size, range1, range2, loop_count, internal_loop_count; + unsigned int size; + unsigned int range1; + unsigned int range2; + unsigned int loop_count; + unsigned int internal_loop_count; for( loop_count = 0; loop_count < 50; ++loop_count ) { test_colony.clear(); @@ -887,7 +891,8 @@ TEST_CASE( "colony perfect forwarding", "[colony]" ) TEST_CASE( "colony emplace", "[colony]" ) { cata::colony test_colony; - int sum1 = 0, sum2 = 0; + int sum1 = 0; + int sum2 = 0; for( int i = 0; i < 100; ++i ) { test_colony.emplace( i ); @@ -949,7 +954,8 @@ TEST_CASE( "colony group size and capacity", "[colony]" ) TEST_CASE( "colony splice", "[colony]" ) { - cata::colony test_colony_1, test_colony_2; + cata::colony test_colony_1; + cata::colony test_colony_2; SECTION( "small splice 1" ) { int i = 0; diff --git a/tests/comestible_test.cpp b/tests/comestible_test.cpp index c00c217ba7f4c..1975c0e40b3a8 100644 --- a/tests/comestible_test.cpp +++ b/tests/comestible_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "item.h" #include "item_contents.h" diff --git a/tests/coordinate_test.cpp b/tests/coordinate_test.cpp index 80f1f1220579c..774fb7db8076f 100644 --- a/tests/coordinate_test.cpp +++ b/tests/coordinate_test.cpp @@ -4,7 +4,7 @@ #include #include "cata_generators.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "coordinate_conversions.h" #include "coordinates.h" #include "point.h" diff --git a/tests/crafting_test.cpp b/tests/crafting_test.cpp index 9b3f16f406483..9a0a6c99cedfc 100644 --- a/tests/crafting_test.cpp +++ b/tests/crafting_test.cpp @@ -14,7 +14,7 @@ #include "avatar.h" #include "calendar.h" #include "cata_utility.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "game.h" #include "inventory.h" @@ -165,7 +165,7 @@ TEST_CASE( "available_recipes", "[recipes]" ) } GIVEN( "an appropriate book" ) { - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &craftbook = dummy.i_add( item( "manual_electronics" ) ); REQUIRE( craftbook.is_book() ); REQUIRE_FALSE( craftbook.type->book->recipes.empty() ); @@ -209,7 +209,7 @@ TEST_CASE( "available_recipes", "[recipes]" ) GIVEN( "an eink pc with a sushi recipe" ) { const recipe *r2 = &recipe_id( "sushi_rice" ).obj(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &eink = dummy.i_add( item( "eink_tablet_pc" ) ); eink.set_var( "EIPC_RECIPES", ",sushi_rice," ); REQUIRE_FALSE( dummy.knows_recipe( r2 ) ); @@ -375,7 +375,8 @@ static int actually_test_craft( const recipe_id &rid, int interrupt_after_turns, // This really shouldn't be needed, but for some reason the tests fail for mingw builds without it player_character.learn_recipe( &rec ); - REQUIRE( player_character.has_recipe( &rec, player_character.crafting_inventory(), + const inventory &inv = player_character.crafting_inventory(); + REQUIRE( player_character.has_recipe( &rec, inv, player_character.get_crafting_helpers() ) != -1 ); player_character.remove_weapon(); REQUIRE( !player_character.is_armed() ); @@ -402,7 +403,7 @@ TEST_CASE( "UPS shows as a crafting component", "[crafting][ups]" ) { avatar dummy; clear_character( dummy ); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &ups = dummy.i_add( item( "UPS_off", calendar::turn_zero, 500 ) ); REQUIRE( dummy.has_item( ups ) ); REQUIRE( ups.charges == 500 ); diff --git a/tests/creature_effect_test.cpp b/tests/creature_effect_test.cpp index b3db98f732e38..fa15b090bec9f 100644 --- a/tests/creature_effect_test.cpp +++ b/tests/creature_effect_test.cpp @@ -1,6 +1,6 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "monster.h" #include "mtype.h" #include "type_id.h" @@ -314,7 +314,7 @@ TEST_CASE( "has_effect_with_flag", "[creature][effect][has][flag]" ) { const efftype_id effect_downed( "downed" ); const efftype_id effect_invisibility( "invisibility" ); - const flag_id invisibility_flag( "EFFECT_INVISIBLE" ); + const flag_id invisibility_flag( "INVISIBLE" ); monster mummy( mtype_id( "debug_mon" ) ); diff --git a/tests/creature_in_field_test.cpp b/tests/creature_in_field_test.cpp index 9838043d8d7a1..8bec189edfdee 100644 --- a/tests/creature_in_field_test.cpp +++ b/tests/creature_in_field_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "map.h" #include "map_helpers.h" #include "monster.h" diff --git a/tests/creature_test.cpp b/tests/creature_test.cpp index 8e37b9d108077..f8a7ffb2765da 100644 --- a/tests/creature_test.cpp +++ b/tests/creature_test.cpp @@ -3,7 +3,7 @@ #include #include "bodypart.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "creature.h" #include "enum_traits.h" diff --git a/tests/effect_test.cpp b/tests/effect_test.cpp index d0f691b89cc93..56f448e0a5990 100644 --- a/tests/effect_test.cpp +++ b/tests/effect_test.cpp @@ -4,7 +4,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "character_id.h" #include "damage.h" diff --git a/tests/effective_dps_test.cpp b/tests/effective_dps_test.cpp index 7c263f5bbf4e2..b1de9b8d8bf54 100644 --- a/tests/effective_dps_test.cpp +++ b/tests/effective_dps_test.cpp @@ -3,7 +3,7 @@ #include #include "avatar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "item.h" #include "melee.h" #include "monster.h" diff --git a/tests/enchantments_test.cpp b/tests/enchantments_test.cpp index e25833385fda0..e1e983347f6f0 100644 --- a/tests/enchantments_test.cpp +++ b/tests/enchantments_test.cpp @@ -1,5 +1,5 @@ #include "avatar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "field.h" #include "item.h" #include "item_location.h" diff --git a/tests/encumbrance_test.cpp b/tests/encumbrance_test.cpp index bb495a9eb030b..11de147f86488 100644 --- a/tests/encumbrance_test.cpp +++ b/tests/encumbrance_test.cpp @@ -5,7 +5,7 @@ #include #include "bodypart.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "item.h" #include "npc.h" @@ -61,8 +61,9 @@ static void test_encumbrance( { CAPTURE( clothing_types ); std::vector clothing; + clothing.reserve( clothing_types.size() ); for( const std::string &type : clothing_types ) { - clothing.push_back( item( type ) ); + clothing.emplace_back( type ); } test_encumbrance_items( clothing, body_part, expected_encumbrance ); } diff --git a/tests/event_test.cpp b/tests/event_test.cpp index bc96b9f9057aa..2480ea3536b23 100644 --- a/tests/event_test.cpp +++ b/tests/event_test.cpp @@ -3,7 +3,7 @@ #include "calendar.h" #include "cata_variant.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character_id.h" #include "event.h" #include "event_bus.h" diff --git a/tests/explosion_balance_test.cpp b/tests/explosion_balance_test.cpp index de46f5e2f077e..cc577c5f416ac 100644 --- a/tests/explosion_balance_test.cpp +++ b/tests/explosion_balance_test.cpp @@ -5,7 +5,7 @@ #include #include "avatar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "creature.h" #include "explosion.h" #include "game.h" diff --git a/tests/fake_messages.cpp b/tests/fake_messages.cpp index f38fd46c4ba49..1a5d41906a067 100644 --- a/tests/fake_messages.cpp +++ b/tests/fake_messages.cpp @@ -36,6 +36,11 @@ void Messages::add_msg( const game_message_params &, std::string m ) { add_msg( m ); } +void Messages::add_msg_debug( debugmode::debug_filter, std::string m ) +{ + // cata_test does not need filters + add_msg( m ); +} void Messages::clear_messages() { messages.clear(); @@ -62,6 +67,10 @@ void add_msg( const game_message_params &, std::string m ) { Messages::add_msg( m ); } +void add_msg_debug( debugmode::debug_filter, std::string m ) +{ + Messages::add_msg( m ); +} void add_msg_if_player_sees( const tripoint &, std::string m ) { Messages::add_msg( m ); @@ -78,3 +87,13 @@ void add_msg_if_player_sees( const Creature &, const game_message_params &, std: { Messages::add_msg( m ); } +void add_msg_debug_if_player_sees( const tripoint &, debugmode::debug_filter, + std::string m ) +{ + Messages::add_msg( m ); +} +void add_msg_debug_if_player_sees( const Creature &, debugmode::debug_filter, + std::string m ) +{ + Messages::add_msg( m ); +} diff --git a/tests/field_test.cpp b/tests/field_test.cpp index 61f5ee0ab0d04..f2050c4781796 100644 --- a/tests/field_test.cpp +++ b/tests/field_test.cpp @@ -3,7 +3,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "field.h" #include "field_type.h" #include "item.h" diff --git a/tests/flat_set_test.cpp b/tests/flat_set_test.cpp index d1c05285f11c0..e5fd16cebda3f 100644 --- a/tests/flat_set_test.cpp +++ b/tests/flat_set_test.cpp @@ -5,7 +5,7 @@ #include #include "assertion_helpers.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "flat_set.h" #if 0 diff --git a/tests/focus_test.cpp b/tests/focus_test.cpp index 1f8d76e9d979b..d1f50cacca873 100644 --- a/tests/focus_test.cpp +++ b/tests/focus_test.cpp @@ -1,7 +1,7 @@ #include #include "avatar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "player_helpers.h" #include "skill.h" #include "type_id.h" diff --git a/tests/fold_string_test.cpp b/tests/fold_string_test.cpp index 3ec6dedceb897..35cd74cca7b48 100644 --- a/tests/fold_string_test.cpp +++ b/tests/fold_string_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include #include diff --git a/tests/food_fun_for_test.cpp b/tests/food_fun_for_test.cpp index c1673a0a6ce8d..740163f4dc7cb 100644 --- a/tests/food_fun_for_test.cpp +++ b/tests/food_fun_for_test.cpp @@ -4,7 +4,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "flag.h" #include "item.h" #include "itype.h" @@ -142,29 +142,29 @@ TEST_CASE( "fun for cold food", "[fun_for][food][cold]" ) } GIVEN( "food that tastes bad, but better when cold" ) { - item sports( "sports_drink" ); - REQUIRE( sports.is_comestible() ); - int sports_fun = sports.get_comestible_fun(); + item rehydration( "rehydration_drink" ); + REQUIRE( rehydration.is_comestible() ); + int rehydration_fun = rehydration.get_comestible_fun(); - REQUIRE( sports_fun < 0 ); - REQUIRE( sports.has_flag( flag_EATEN_COLD ) ); + REQUIRE( rehydration_fun < 0 ); + REQUIRE( rehydration.has_flag( flag_EATEN_COLD ) ); WHEN( "it is cold" ) { - sports.set_flag( flag_COLD ); - REQUIRE( sports.has_flag( flag_COLD ) ); + rehydration.set_flag( flag_COLD ); + REQUIRE( rehydration.has_flag( flag_COLD ) ); THEN( "it doesn't taste bad" ) { - actual_fun = dummy.fun_for( sports ); + actual_fun = dummy.fun_for( rehydration ); CHECK( actual_fun.first > 0 ); } } WHEN( "it is not cold" ) { - REQUIRE_FALSE( sports.has_flag( flag_COLD ) ); + REQUIRE_FALSE( rehydration.has_flag( flag_COLD ) ); THEN( "it tastes as bad as usual" ) { - actual_fun = dummy.fun_for( sports ); - CHECK( actual_fun.first == sports_fun ); + actual_fun = dummy.fun_for( rehydration ); + CHECK( actual_fun.first == rehydration_fun ); } } } diff --git a/tests/generic_factory_test.cpp b/tests/generic_factory_test.cpp index 0da6ab03c8d6b..bd48552245b48 100644 --- a/tests/generic_factory_test.cpp +++ b/tests/generic_factory_test.cpp @@ -6,7 +6,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "colony_list_test_helpers.h" #include "flat_set.h" #include "generic_factory.h" @@ -347,7 +347,7 @@ TEST_CASE( "string_and_int_ids_benchmark", "[.][generic_factory][int_id][string_ std_uo_set_int_ids.emplace( int_id ); // do not add duplicates if( std_uo_set_int_ids.size() > vector_int_ids.size() ) { - vector_int_ids.push_back( flag ); + vector_int_ids.emplace_back( flag ); } } } item; @@ -380,8 +380,9 @@ TEST_CASE( "string_and_int_ids_benchmark", "[.][generic_factory][int_id][string_ } const int test_flags_size = test_flags.size(); std::vector test_dyn_str_ids; + test_dyn_str_ids.reserve( test_flags.size() ); for( const auto &f : test_flags ) { - test_dyn_str_ids.push_back( dyn_str_id( f.str() ) ); + test_dyn_str_ids.emplace_back( f.str() ); } DYNAMIC_SECTION( "number_ids_in_collection: " << flags_in_item << "; only_hits: " << hits ) { diff --git a/tests/ground_destroy_test.cpp b/tests/ground_destroy_test.cpp index d00b0b88af5f8..52d922eba3236 100644 --- a/tests/ground_destroy_test.cpp +++ b/tests/ground_destroy_test.cpp @@ -2,7 +2,7 @@ #include #include "avatar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "item.h" #include "itype.h" #include "map.h" diff --git a/tests/hash_test.cpp b/tests/hash_test.cpp index 6fd981a276119..c912d82007a58 100644 --- a/tests/hash_test.cpp +++ b/tests/hash_test.cpp @@ -4,7 +4,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "point.h" // A larger number for this would be GREAT, but the test isn't efficient enough to make it larger. diff --git a/tests/health_test.cpp b/tests/health_test.cpp index ee314c2e32fbd..9ee73fea642f2 100644 --- a/tests/health_test.cpp +++ b/tests/health_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include #include diff --git a/tests/iexamine_test.cpp b/tests/iexamine_test.cpp index a0f53f5be620b..9438eb5ae18bc 100644 --- a/tests/iexamine_test.cpp +++ b/tests/iexamine_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "iexamine.h" #include "mapdata.h" diff --git a/tests/invlet_test.cpp b/tests/invlet_test.cpp index be0fb49138c11..bb8aaf0db7dde 100644 --- a/tests/invlet_test.cpp +++ b/tests/invlet_test.cpp @@ -10,7 +10,7 @@ #include "activity_actor_definitions.h" #include "avatar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "inventory.h" #include "item.h" #include "item_location.h" @@ -464,7 +464,7 @@ static void invlet_test( player &dummy, const inventory_location from, const inv dummy.worn.clear(); dummy.remove_weapon(); get_map().i_clear( dummy.pos() ); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); // some two items that can be wielded, worn, and picked up item tshirt( "tshirt" ); @@ -513,8 +513,8 @@ static void invlet_test( player &dummy, const inventory_location from, const inv break; } - invlet_state final_first_invlet_state = check_invlet( dummy, *final_first, invlet ), - final_second_invlet_state = check_invlet( dummy, *final_second, invlet ); + invlet_state final_first_invlet_state = check_invlet( dummy, *final_first, invlet ); + invlet_state final_second_invlet_state = check_invlet( dummy, *final_second, invlet ); INFO( test_action_desc( action, from, to, first_invlet_state, second_invlet_state, expected_first_invlet_state, expected_second_invlet_state, final_first_invlet_state, @@ -546,7 +546,7 @@ static void stack_invlet_test( player &dummy, inventory_location from, inventory dummy.worn.clear(); dummy.remove_weapon(); get_map().i_clear( dummy.pos() ); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); // some stackable item that can be wielded and worn item tshirt1( "tshirt" ); @@ -682,7 +682,7 @@ static void merge_invlet_test( player &dummy, inventory_location from ) dummy.worn.clear(); dummy.remove_weapon(); get_map().i_clear( dummy.pos() ); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); // some stackable item item tshirt1( "tshirt" ); diff --git a/tests/item_contents_test.cpp b/tests/item_contents_test.cpp index 7a07eed26732e..17cf135c4a004 100644 --- a/tests/item_contents_test.cpp +++ b/tests/item_contents_test.cpp @@ -1,6 +1,6 @@ #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "item.h" #include "item_contents.h" #include "item_pocket.h" @@ -54,10 +54,11 @@ TEST_CASE( "item_contents" ) CHECK( tool_belt.weight() == tool_belt_weight + hammer.weight() + tongs.weight() + wrench.weight() + crowbar.weight() ); // check that the tool belt is "full" - CHECK( !tool_belt.contents.can_contain( hammer ).success() ); + CHECK( !tool_belt.contents.can_contain( crowbar ).success() ); - tool_belt.contents.force_insert_item( hammer, item_pocket::pocket_type::CONTAINER ); + tool_belt.contents.force_insert_item( crowbar, item_pocket::pocket_type::CONTAINER ); CHECK( tool_belt.contents.num_item_stacks() == 5 ); + tool_belt.contents.force_insert_item( crowbar, item_pocket::pocket_type::CONTAINER ); tool_belt.contents.overflow( tripoint_zero ); CHECK( tool_belt.contents.num_item_stacks() == 4 ); tool_belt.contents.overflow( tripoint_zero ); @@ -65,7 +66,7 @@ TEST_CASE( "item_contents" ) CHECK( tool_belt.contents.num_item_stacks() == 4 ); tool_belt.contents.remove_items_if( []( item & it ) { - return it.typeId() == itype_id( "hammer" ); + return it.typeId() == itype_id( "crowbar" ); } ); // check to see that removing an item works CHECK( tool_belt.contents.num_item_stacks() == 3 ); diff --git a/tests/item_group_test.cpp b/tests/item_group_test.cpp index 50fb442229de6..9ffa89d7d6704 100644 --- a/tests/item_group_test.cpp +++ b/tests/item_group_test.cpp @@ -6,7 +6,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "flag.h" #include "item.h" #include "item_contents.h" diff --git a/tests/item_location_test.cpp b/tests/item_location_test.cpp index 16c1fc0121187..814aa8dd72cda 100644 --- a/tests/item_location_test.cpp +++ b/tests/item_location_test.cpp @@ -2,7 +2,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "item.h" #include "item_contents.h" diff --git a/tests/item_stackable_test.cpp b/tests/item_stackable_test.cpp index 098d56eeba4f3..c410e31bcd249 100644 --- a/tests/item_stackable_test.cpp +++ b/tests/item_stackable_test.cpp @@ -2,7 +2,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "item_factory.h" #include "itype.h" #include "type_id.h" diff --git a/tests/item_test.cpp b/tests/item_test.cpp index ddf729b01c893..0c7614ebf5308 100644 --- a/tests/item_test.cpp +++ b/tests/item_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "item.h" #include @@ -286,3 +286,14 @@ TEST_CASE( "items spawn in their default containers", "[item]" ) check_spawning_in_container( "chem_black_powder" ); check_spawning_in_container( "software_useless" ); } + +TEST_CASE( "item variables round-trip accurately", "[item]" ) +{ + item i( "water" ); + i.set_var( "A", 17 ); + CHECK( i.get_var( "A", 0 ) == 17 ); + i.set_var( "B", 0.125 ); + CHECK( i.get_var( "B", 0.0 ) == 0.125 ); + i.set_var( "C", tripoint( 2, 3, 4 ) ); + CHECK( i.get_var( "C", tripoint() ) == tripoint( 2, 3, 4 ) ); +} diff --git a/tests/item_tname_test.cpp b/tests/item_tname_test.cpp index 0bd11764836e1..85428439d247c 100644 --- a/tests/item_tname_test.cpp +++ b/tests/item_tname_test.cpp @@ -4,7 +4,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "flag.h" #include "item.h" diff --git a/tests/item_type_name_test.cpp b/tests/item_type_name_test.cpp index be6bd8468821e..d1aece474e16f 100644 --- a/tests/item_type_name_test.cpp +++ b/tests/item_type_name_test.cpp @@ -2,7 +2,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "item.h" #include "type_id.h" diff --git a/tests/iteminfo_test.cpp b/tests/iteminfo_test.cpp index 7bd2648e7708b..422b8f86e8c17 100644 --- a/tests/iteminfo_test.cpp +++ b/tests/iteminfo_test.cpp @@ -8,7 +8,7 @@ #include "avatar.h" #include "bodypart.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "flag.h" #include "item.h" @@ -638,6 +638,44 @@ TEST_CASE( "techniques when wielded", "[iteminfo][weapon][techniques]" ) " Medium blocking ability\n" ); } +static std::vector bodyparts_to_check() +{ + return { + bodypart_id( "torso" ), + bodypart_id( "arm_l" ), + bodypart_id( "arm_r" ), + bodypart_id( "leg_l" ), + bodypart_id( "leg_r" ), + bodypart_id( "hand_l" ), + bodypart_id( "hand_r" ), + bodypart_id( "head" ), + bodypart_id( "mouth" ), + bodypart_id( "foot_l" ), + bodypart_id( "foot_r" ), + }; +} + +static void verify_item_coverage( const item &i, const std::map &expected ) +{ + CAPTURE( i.typeId() ); + REQUIRE( i.get_covered_body_parts().any() ); + for( const bodypart_id &bp : bodyparts_to_check() ) { + CAPTURE( bp.id() ); + REQUIRE( i.get_coverage( bp ) == expected.at( bp ) ); + } +} + +static void verify_item_encumbrance( const item &i, item::encumber_flags flags, int average, + const std::map &expected ) +{ + CAPTURE( i.typeId() ); + REQUIRE( i.get_avg_encumber( get_player_character(), flags ) == average ); + for( const bodypart_id &bp : bodyparts_to_check() ) { + CAPTURE( bp.id() ); + REQUIRE( i.get_encumber( get_player_character(), bp, flags ) == expected.at( bp ) ); + } +} + // Related JSON fields: // "covers" // "coverage" @@ -653,18 +691,21 @@ TEST_CASE( "armor coverage, warmth, and encumbrance", "[iteminfo][armor][coverag SECTION( "armor with coverage shows covered body parts, warmth, encumbrance, and protection values" ) { // Long-sleeved shirt covering torso and arms item longshirt( "test_longshirt" ); - REQUIRE( longshirt.get_covered_body_parts().any() ); - REQUIRE( longshirt.get_coverage( bodypart_id( "torso" ) ) == 90 ); - REQUIRE( longshirt.get_coverage( bodypart_id( "arm_l" ) ) == 90 ); - REQUIRE( longshirt.get_coverage( bodypart_id( "arm_r" ) ) == 90 ); - REQUIRE( longshirt.get_coverage( bodypart_id( "leg_l" ) ) == 0 ); - REQUIRE( longshirt.get_coverage( bodypart_id( "leg_r" ) ) == 0 ); - REQUIRE( longshirt.get_coverage( bodypart_id( "hand_l" ) ) == 0 ); - REQUIRE( longshirt.get_coverage( bodypart_id( "hand_r" ) ) == 0 ); - REQUIRE( longshirt.get_coverage( bodypart_id( "head" ) ) == 0 ); - REQUIRE( longshirt.get_coverage( bodypart_id( "mouth" ) ) == 0 ); - REQUIRE( longshirt.get_coverage( bodypart_id( "foot_l" ) ) == 0 ); - REQUIRE( longshirt.get_coverage( bodypart_id( "foot_r" ) ) == 0 ); + verify_item_coverage( + longshirt, { + { bodypart_id( "torso" ), 90 }, + { bodypart_id( "arm_l" ), 90 }, + { bodypart_id( "arm_r" ), 90 }, + { bodypart_id( "leg_l" ), 0 }, + { bodypart_id( "leg_r" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + { bodypart_id( "head" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + } + ); CHECK( item_info_str( longshirt, { iteminfo_parts::ARMOR_BODYPARTS } ) == "--\n" @@ -677,7 +718,9 @@ TEST_CASE( "armor coverage, warmth, and encumbrance", "[iteminfo][armor][coverag "Layer: Normal.\n" ); // Coverage and warmth are displayed together on a single line - std::vector cov_warm_shirt = { iteminfo_parts::ARMOR_COVERAGE, iteminfo_parts::ARMOR_WARMTH }; + std::vector cov_warm_shirt = { + iteminfo_parts::ARMOR_COVERAGE, iteminfo_parts::ARMOR_WARMTH + }; REQUIRE( longshirt.get_avg_coverage() == 90 ); REQUIRE( longshirt.get_warmth() == 5 ); CHECK( item_info_str( longshirt, cov_warm_shirt ) @@ -685,43 +728,37 @@ TEST_CASE( "armor coverage, warmth, and encumbrance", "[iteminfo][armor][coverag "--\n" "Average Coverage: 90% Warmth: 5\n" ); - REQUIRE( longshirt.get_avg_encumber( get_player_character() ) == 3 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "torso" ) ) == 3 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "arm_l" ) ) == 3 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "arm_r" ) ) == 3 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "leg_l" ) ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "leg_r" ) ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "hand_l" ) ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "hand_r" ) ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "head" ) ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "mouth" ) ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "foot_l" ) ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "foot_r" ) ) == 0 ); - - REQUIRE( longshirt.get_avg_encumber( get_player_character(), - item::encumber_flags::assume_full ) == 3 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "torso" ), - item::encumber_flags::assume_full ) == 3 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "arm_l" ), - item::encumber_flags::assume_full ) == 3 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "arm_r" ), - item::encumber_flags::assume_full ) == 3 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "leg_l" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "leg_r" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "hand_l" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "hand_r" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "head" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "mouth" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "foot_l" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( longshirt.get_encumber( get_player_character(), bodypart_id( "foot_r" ), - item::encumber_flags::assume_full ) == 0 ); + verify_item_encumbrance( + longshirt, item::encumber_flags::none, 3, { + { bodypart_id( "torso" ), 3 }, + { bodypart_id( "arm_l" ), 3 }, + { bodypart_id( "arm_r" ), 3 }, + { bodypart_id( "leg_l" ), 0 }, + { bodypart_id( "leg_r" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + { bodypart_id( "head" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + } + ); + + verify_item_encumbrance( + longshirt, item::encumber_flags::assume_full, 3, { + { bodypart_id( "torso" ), 3 }, + { bodypart_id( "arm_l" ), 3 }, + { bodypart_id( "arm_r" ), 3 }, + { bodypart_id( "leg_l" ), 0 }, + { bodypart_id( "leg_r" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + { bodypart_id( "head" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + } + ); CHECK( item_info_str( longshirt, { iteminfo_parts::ARMOR_ENCUMBRANCE } ) == "--\n" @@ -753,59 +790,56 @@ TEST_CASE( "armor coverage, warmth, and encumbrance", "[iteminfo][armor][coverag "--\n" "Average Coverage: 95% Warmth: 35\n" ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "torso" ) ) == 95 ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "leg_l" ) ) == 95 ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "leg_r" ) ) == 95 ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "arm_l" ) ) == 95 ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "arm_r" ) ) == 95 ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "head" ) ) == 0 ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "foot_l" ) ) == 0 ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "foot_r" ) ) == 0 ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "eyes" ) ) == 0 ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "mouth" ) ) == 0 ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "hand_r" ) ) == 0 ); - REQUIRE( swat_armor.get_coverage( bodypart_id( "hand_l" ) ) == 0 ); - - REQUIRE( swat_armor.get_avg_encumber( get_player_character() ) == 12 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "torso" ) ) == 12 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "leg_l" ) ) == 12 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "leg_r" ) ) == 12 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "arm_l" ) ) == 12 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "arm_r" ) ) == 12 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "head" ) ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "foot_l" ) ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "foot_r" ) ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "eyes" ) ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "mouth" ) ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "hand_l" ) ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "hand_r" ) ) == 0 ); - - REQUIRE( swat_armor.get_avg_encumber( get_player_character(), - item::encumber_flags::assume_full ) == 25 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "torso" ), - item::encumber_flags::assume_full ) == 25 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "leg_l" ), - item::encumber_flags::assume_full ) == 25 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "leg_r" ), - item::encumber_flags::assume_full ) == 25 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "arm_l" ), - item::encumber_flags::assume_full ) == 25 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "arm_r" ), - item::encumber_flags::assume_full ) == 25 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "foot_l" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "foot_r" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "head" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "eyes" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "mouth" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "hand_l" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( swat_armor.get_encumber( get_player_character(), bodypart_id( "hand_r" ), - item::encumber_flags::assume_full ) == 0 ); + verify_item_coverage( + swat_armor, { + { bodypart_id( "torso" ), 95 }, + { bodypart_id( "leg_l" ), 95 }, + { bodypart_id( "leg_r" ), 95 }, + { bodypart_id( "arm_l" ), 95 }, + { bodypart_id( "arm_r" ), 95 }, + { bodypart_id( "head" ), 0 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + { bodypart_id( "eyes" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + } + ); + + verify_item_encumbrance( + swat_armor, item::encumber_flags::none, 12, { + { bodypart_id( "torso" ), 12 }, + { bodypart_id( "leg_l" ), 12 }, + { bodypart_id( "leg_r" ), 12 }, + { bodypart_id( "arm_l" ), 12 }, + { bodypart_id( "arm_r" ), 12 }, + { bodypart_id( "head" ), 0 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + { bodypart_id( "eyes" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + } + ); + + verify_item_encumbrance( + swat_armor, item::encumber_flags::assume_full, 25, { + { bodypart_id( "torso" ), 25 }, + { bodypart_id( "leg_l" ), 25 }, + { bodypart_id( "leg_r" ), 25 }, + { bodypart_id( "arm_l" ), 25 }, + { bodypart_id( "arm_r" ), 25 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + { bodypart_id( "head" ), 0 }, + { bodypart_id( "eyes" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + } + ); CHECK( item_info_str( swat_armor, { iteminfo_parts::ARMOR_ENCUMBRANCE } ) == "--\n" @@ -842,55 +876,56 @@ TEST_CASE( "armor coverage, warmth, and encumbrance", "[iteminfo][armor][coverag "Average Coverage: 95% Warmth: 70\n" ); REQUIRE( faux_fur_pants.get_avg_coverage() == 95 ); - REQUIRE( faux_fur_pants.get_coverage( bodypart_id( "leg_l" ) ) == 95 ); - REQUIRE( faux_fur_pants.get_coverage( bodypart_id( "leg_r" ) ) == 95 ); - REQUIRE( faux_fur_pants.get_coverage( bodypart_id( "arm_l" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_coverage( bodypart_id( "arm_r" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_coverage( bodypart_id( "foot_r" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_coverage( bodypart_id( "foot_l" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_coverage( bodypart_id( "head" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_coverage( bodypart_id( "eyes" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_coverage( bodypart_id( "mouth" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_coverage( bodypart_id( "hand_l" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_coverage( bodypart_id( "hand_r" ) ) == 0 ); - - REQUIRE( faux_fur_pants.get_avg_encumber( get_player_character() ) == 16 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "leg_l" ) ) == 16 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "leg_r" ) ) == 16 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "arm_l" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "arm_r" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "foot_r" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "foot_l" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "head" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "eyes" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "mouth" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "hand_l" ) ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "hand_r" ) ) == 0 ); - - REQUIRE( faux_fur_pants.get_avg_encumber( get_player_character(), - item::encumber_flags::assume_full ) == 20 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "leg_l" ), - item::encumber_flags::assume_full ) == 20 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "leg_r" ), - item::encumber_flags::assume_full ) == 20 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "arm_l" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "arm_r" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "foot_r" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "foot_l" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "head" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "eyes" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "mouth" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "hand_l" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_pants.get_encumber( get_player_character(), bodypart_id( "hand_r" ), - item::encumber_flags::assume_full ) == 0 ); + verify_item_coverage( + faux_fur_pants, { + { bodypart_id( "torso" ), 0 }, + { bodypart_id( "leg_l" ), 95 }, + { bodypart_id( "leg_r" ), 95 }, + { bodypart_id( "arm_l" ), 0 }, + { bodypart_id( "arm_r" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "head" ), 0 }, + { bodypart_id( "eyes" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + } + ); + + verify_item_encumbrance( + faux_fur_pants, item::encumber_flags::none, 16, { + { bodypart_id( "torso" ), 0 }, + { bodypart_id( "leg_l" ), 16 }, + { bodypart_id( "leg_r" ), 16 }, + { bodypart_id( "arm_l" ), 0 }, + { bodypart_id( "arm_r" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "head" ), 0 }, + { bodypart_id( "eyes" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + } + ); + + verify_item_encumbrance( + faux_fur_pants, item::encumber_flags::assume_full, 20, { + { bodypart_id( "torso" ), 0 }, + { bodypart_id( "leg_l" ), 20 }, + { bodypart_id( "leg_r" ), 20 }, + { bodypart_id( "arm_l" ), 0 }, + { bodypart_id( "arm_r" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "head" ), 0 }, + { bodypart_id( "eyes" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + } + ); item faux_fur_suit( "test_portion_faux_fur_pants_suit" ); REQUIRE( faux_fur_suit.get_covered_body_parts().any() ); @@ -907,7 +942,9 @@ TEST_CASE( "armor coverage, warmth, and encumbrance", "[iteminfo][armor][coverag "--\n" "Layer: Normal.\n" ); - std::vector cov_warm_suit = { iteminfo_parts::ARMOR_COVERAGE, iteminfo_parts::ARMOR_WARMTH }; + std::vector cov_warm_suit = { + iteminfo_parts::ARMOR_COVERAGE, iteminfo_parts::ARMOR_WARMTH + }; REQUIRE( faux_fur_suit.get_avg_coverage() == 75 ); REQUIRE( faux_fur_suit.get_warmth() == 5 ); CHECK( item_info_str( faux_fur_suit, cov_warm_suit ) @@ -916,59 +953,56 @@ TEST_CASE( "armor coverage, warmth, and encumbrance", "[iteminfo][armor][coverag "Average Coverage: 75% Warmth: 5\n" ); REQUIRE( faux_fur_suit.get_avg_coverage() == 75 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "torso" ) ) == 100 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "leg_l" ) ) == 50 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "leg_r" ) ) == 100 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "arm_l" ) ) == 50 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "arm_r" ) ) == 100 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "head" ) ) == 50 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "eyes" ) ) == 0 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "mouth" ) ) == 0 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "hand_l" ) ) == 0 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "hand_r" ) ) == 0 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "foot_l" ) ) == 0 ); - REQUIRE( faux_fur_suit.get_coverage( bodypart_id( "foot_r" ) ) == 0 ); - - REQUIRE( faux_fur_suit.get_avg_encumber( get_player_character() ) == 7 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "torso" ) ) == 10 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "leg_l" ) ) == 5 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "leg_r" ) ) == 10 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "arm_l" ) ) == 5 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "arm_r" ) ) == 10 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "head" ) ) == 5 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "eyes" ) ) == 0 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "mouth" ) ) == 0 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "foot_l" ) ) == 0 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "foot_r" ) ) == 0 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "hand_r" ) ) == 0 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "hand_r" ) ) == 0 ); - - REQUIRE( faux_fur_suit.get_avg_encumber( get_player_character(), - item::encumber_flags::assume_full ) == 17 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "torso" ), - item::encumber_flags::assume_full ) == 25 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "leg_l" ), - item::encumber_flags::assume_full ) == 9 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "leg_r" ), - item::encumber_flags::assume_full ) == 25 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "arm_l" ), - item::encumber_flags::assume_full ) == 9 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "arm_r" ), - item::encumber_flags::assume_full ) == 25 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "head" ), - item::encumber_flags::assume_full ) == 9 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "eyes" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "mouth" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "hand_l" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "hand_r" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "foot_l" ), - item::encumber_flags::assume_full ) == 0 ); - REQUIRE( faux_fur_suit.get_encumber( get_player_character(), bodypart_id( "foot_r" ), - item::encumber_flags::assume_full ) == 0 ); + verify_item_coverage( + faux_fur_suit, { + { bodypart_id( "torso" ), 100 }, + { bodypart_id( "leg_l" ), 50 }, + { bodypart_id( "leg_r" ), 100 }, + { bodypart_id( "arm_l" ), 50 }, + { bodypart_id( "arm_r" ), 100 }, + { bodypart_id( "head" ), 50 }, + { bodypart_id( "eyes" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + } + ); + + verify_item_encumbrance( + faux_fur_suit, item::encumber_flags::none, 7, { + { bodypart_id( "torso" ), 10 }, + { bodypart_id( "leg_l" ), 5 }, + { bodypart_id( "leg_r" ), 10 }, + { bodypart_id( "arm_l" ), 5 }, + { bodypart_id( "arm_r" ), 10 }, + { bodypart_id( "head" ), 5 }, + { bodypart_id( "eyes" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + } + ); + + verify_item_encumbrance( + faux_fur_suit, item::encumber_flags::assume_full, 17, { + { bodypart_id( "torso" ), 25 }, + { bodypart_id( "leg_l" ), 9 }, + { bodypart_id( "leg_r" ), 25 }, + { bodypart_id( "arm_l" ), 9 }, + { bodypart_id( "arm_r" ), 25 }, + { bodypart_id( "head" ), 9 }, + { bodypart_id( "eyes" ), 0 }, + { bodypart_id( "mouth" ), 0 }, + { bodypart_id( "hand_l" ), 0 }, + { bodypart_id( "hand_r" ), 0 }, + { bodypart_id( "foot_l" ), 0 }, + { bodypart_id( "foot_r" ), 0 }, + } + ); CHECK( item_info_str( faux_fur_suit, { iteminfo_parts::ARMOR_ENCUMBRANCE } ) == "--\n" @@ -1041,9 +1075,8 @@ TEST_CASE( "armor fit and sizing", "[iteminfo][armor][fit]" ) "* This item can be worn on either side of the body.\n" ); item power_armor( "test_power_armor" ); - CHECK( item_info_str( power_armor, powerarmor ) == - "--\n" - "* This gear is a part of power armor.\n" ); + CHECK_THAT( item_info_str( power_armor, powerarmor ), + Catch::EndsWith( "* This gear is a part of power armor.\n" ) ); } static void expected_armor_values( const item &armor, float bash, float cut, float stab, @@ -2314,7 +2347,7 @@ TEST_CASE( "show available recipes with item as an ingredient", "[iteminfo][reci std::vector crafting = { iteminfo_parts::DESCRIPTION_APPLICABLE_RECIPES }; GIVEN( "character has a potassium iodide tablet and no skill" ) { - player_character.worn.push_back( item( "backpack" ) ); + player_character.worn.emplace_back( "backpack" ); item &iodine = player_character.i_add( item( "iodine" ) ); player_character.empty_skills(); REQUIRE( !player_character.knows_recipe( purtab ) ); @@ -2470,10 +2503,10 @@ TEST_CASE( "pocket info for a multi-pocket item", "[iteminfo][pocket][multiple]" CHECK( item_info_str( test_belt, pockets ) == "--\n" "Total capacity:\n" - "Volume: 6.00 L Weight: 4.00 kg\n" + "Volume: 6.00 L Weight: 4.80 kg\n" "--\n" "4 Pockets with capacity:\n" - "Volume: 1.50 L Weight: 1.00 kg\n" + "Volume: 1.50 L Weight: 1.20 kg\n" "Maximum item length: 70 cm\n" "Minimum item volume: 0.050 L\n" "Base moves to remove item: 50\n" @@ -2661,7 +2694,7 @@ TEST_CASE( "item debug info", "[iteminfo][debug][!mayfail][.]" ) nuts.debug_info( info_vec, &debug_query, 1, true ); // FIXME: "last rot" and "last temp" are expected to be 0, but may have values (ex. 43200) - // Neex to figure out what processing to do before this check to make them predictable + // Need to figure out what processing to do before this check to make them predictable CHECK( format_item_info( info_vec, {} ) == "age (hours): 8\n" "charges: 4\n" diff --git a/tests/itemname_test.cpp b/tests/itemname_test.cpp index 3ebd49c418019..d080e29ecaa60 100644 --- a/tests/itemname_test.cpp +++ b/tests/itemname_test.cpp @@ -3,7 +3,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "flag.h" #include "item.h" diff --git a/tests/iuse_actor_test.cpp b/tests/iuse_actor_test.cpp index f65e19a4edf61..0c86943101502 100644 --- a/tests/iuse_actor_test.cpp +++ b/tests/iuse_actor_test.cpp @@ -8,7 +8,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "colony.h" #include "game.h" #include "item.h" diff --git a/tests/iuse_test.cpp b/tests/iuse_test.cpp index 43f49654d56bb..00e66b1a820dd 100644 --- a/tests/iuse_test.cpp +++ b/tests/iuse_test.cpp @@ -5,7 +5,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "flag.h" #include "item.h" #include "itype.h" diff --git a/tests/json_test.cpp b/tests/json_test.cpp index 99985e66e6aec..1f31ca0671f41 100644 --- a/tests/json_test.cpp +++ b/tests/json_test.cpp @@ -13,7 +13,7 @@ #include "bodypart.h" #include "cached_options.h" #include "cata_utility.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "colony.h" #include "damage.h" #include "debug.h" diff --git a/tests/line_test.cpp b/tests/line_test.cpp index 742eee7936458..0efa68e05ad52 100644 --- a/tests/line_test.cpp +++ b/tests/line_test.cpp @@ -9,7 +9,7 @@ #include #include "cata_generators.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "coordinates.h" #include "line.h" #include "point.h" @@ -131,9 +131,9 @@ TEST_CASE( "test_normalized_angle", "[line]" ) CHECK( get_normalized_angle( point_zero, {-10, -10} ) == Approx( 1.0 ) ); } +// NOLINTNEXTLINE(readability-function-size) TEST_CASE( "Test bounds for mapping x/y/z/ offsets to direction enum", "[line]" ) { - // Test the unit cube, which are the only values this function is valid for. REQUIRE( make_xyz_unit( tripoint( -1, -1, 1 ) ) == direction::ABOVENORTHWEST ); REQUIRE( make_xyz_unit( tripoint_north_west ) == direction::NORTHWEST ); diff --git a/tests/list_test.cpp b/tests/list_test.cpp index 258b9d2ce1546..8c9327ef21a43 100644 --- a/tests/list_test.cpp +++ b/tests/list_test.cpp @@ -6,7 +6,7 @@ #include #include // range-insert testing -#include "catch/catch.hpp" +#include "cata_catch.h" #include "colony_list_test_helpers.h" #include "list.h" diff --git a/tests/magic_spell_effect_test.cpp b/tests/magic_spell_effect_test.cpp index db5ce789c30fe..842953eafddc0 100644 --- a/tests/magic_spell_effect_test.cpp +++ b/tests/magic_spell_effect_test.cpp @@ -1,7 +1,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "json.h" #include "magic.h" #include "magic_spell_effect_helpers.h" diff --git a/tests/magic_spell_test.cpp b/tests/magic_spell_test.cpp index 5654f69369bb3..cc2835ffcf6c6 100644 --- a/tests/magic_spell_test.cpp +++ b/tests/magic_spell_test.cpp @@ -3,7 +3,7 @@ #include #include "avatar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "game.h" #include "magic.h" #include "map_helpers.h" diff --git a/tests/make_static_test.cpp b/tests/make_static_test.cpp index a8f9fbf2590c7..7409f412b85c9 100644 --- a/tests/make_static_test.cpp +++ b/tests/make_static_test.cpp @@ -2,7 +2,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "make_static.h" #include "string_id.h" diff --git a/tests/map_extra_test.cpp b/tests/map_extra_test.cpp index 263acc80c8c2b..79cedaa631f94 100644 --- a/tests/map_extra_test.cpp +++ b/tests/map_extra_test.cpp @@ -4,7 +4,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "coordinates.h" #include "enums.h" #include "map.h" diff --git a/tests/map_iterator_test.cpp b/tests/map_iterator_test.cpp index 7b1706e0eb3c0..4d701f3d0a928 100644 --- a/tests/map_iterator_test.cpp +++ b/tests/map_iterator_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include #include diff --git a/tests/map_memory_test.cpp b/tests/map_memory_test.cpp index 993fdb1995964..fcc28128043ea 100644 --- a/tests/map_memory_test.cpp +++ b/tests/map_memory_test.cpp @@ -3,7 +3,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "game_constants.h" #include "json.h" #include "lru_cache.h" diff --git a/tests/map_test.cpp b/tests/map_test.cpp index 38c0ab96bc28f..57300e9dda09c 100644 --- a/tests/map_test.cpp +++ b/tests/map_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "map.h" #include diff --git a/tests/map_test_case.h b/tests/map_test_case.h index 3f31cbc71fff7..7bbf8f4937d99 100644 --- a/tests/map_test_case.h +++ b/tests/map_test_case.h @@ -7,7 +7,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "map.h" #include "mapdata.h" #include "optional.h" diff --git a/tests/mapgen_function_test.cpp b/tests/mapgen_function_test.cpp index 0334b2a92a364..943d87116b3a3 100644 --- a/tests/mapgen_function_test.cpp +++ b/tests/mapgen_function_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "mapgen.h" #include "type_id.h" diff --git a/tests/math_functions_test.cpp b/tests/math_functions_test.cpp index 7cbeb9b8bf757..69422d33e0e36 100644 --- a/tests/math_functions_test.cpp +++ b/tests/math_functions_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include #include diff --git a/tests/melee_dodge_hit_test.cpp b/tests/melee_dodge_hit_test.cpp index e3c505757cc6b..679ef342bdec3 100644 --- a/tests/melee_dodge_hit_test.cpp +++ b/tests/melee_dodge_hit_test.cpp @@ -4,7 +4,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "creature.h" #include "flag.h" #include "game.h" diff --git a/tests/melee_test.cpp b/tests/melee_test.cpp index 23ba262880001..d9da5a05602b9 100644 --- a/tests/melee_test.cpp +++ b/tests/melee_test.cpp @@ -3,7 +3,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "creature.h" #include "game_constants.h" #include "item.h" @@ -85,7 +85,7 @@ TEST_CASE( "Character attacking a zombie", "[.melee]" ) check_near( prob, 0.6f, 0.1f ); } - SECTION( "8/8/8/8, 3 all skills, two-by-four" ) { + SECTION( "8/8/8/8, 3 all skills, plank" ) { standard_npc dude( "TestCharacter", dude_pos, {}, 3, 8, 8, 8, 8 ); dude.weapon = item( "2x4" ); const float prob = brute_probability( dude, zed, num_iters ); @@ -114,7 +114,7 @@ TEST_CASE( "Character attacking a manhack", "[.melee]" ) check_near( prob, 0.2f, 0.05f ); } - SECTION( "8/8/8/8, 3 all skills, two-by-four" ) { + SECTION( "8/8/8/8, 3 all skills, plank" ) { standard_npc dude( "TestCharacter", dude_pos, {}, 3, 8, 8, 8, 8 ); dude.weapon = item( "2x4" ); const float prob = brute_probability( dude, manhack, num_iters ); diff --git a/tests/memorial_test.cpp b/tests/memorial_test.cpp index 41b976094de5b..eb8d06f3cc1ec 100644 --- a/tests/memorial_test.cpp +++ b/tests/memorial_test.cpp @@ -9,7 +9,7 @@ #include "achievement.h" #include "avatar.h" #include "bodypart.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character_id.h" #include "debug_menu.h" #include "event.h" @@ -294,7 +294,7 @@ TEST_CASE( "convert_legacy_memorial_log", "[memorial]" ) memorial_logger logger; { std::istringstream is( input ); - logger.load( is ); + logger.load( is, "" ); std::ostringstream os; logger.save( os ); CHECK( os.str() == json_value ); @@ -303,7 +303,7 @@ TEST_CASE( "convert_legacy_memorial_log", "[memorial]" ) // Then verify that the new format is unchanged { std::istringstream is( json_value ); - logger.load( is ); + logger.load( is, "" ); std::ostringstream os; logger.save( os ); CHECK( os.str() == json_value ); @@ -328,6 +328,6 @@ TEST_CASE( "memorial_log_dumping", "[memorial]" ) memorial_logger logger; std::istringstream is( json_value ); - logger.load( is ); + logger.load( is, "" ); CHECK( logger.dump() == expected_output ); } diff --git a/tests/modify_morale_test.cpp b/tests/modify_morale_test.cpp index 1651644d9f35f..cf5c4ea204837 100644 --- a/tests/modify_morale_test.cpp +++ b/tests/modify_morale_test.cpp @@ -6,7 +6,7 @@ #include #include "avatar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "flag.h" #include "item.h" #include "item_contents.h" @@ -46,7 +46,7 @@ static const trait_id trait_VEGETARIAN( "VEGETARIAN" ); TEST_CASE( "food enjoyability", "[food][modify_morale][fun]" ) { avatar dummy; - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); std::pair fun; GIVEN( "food with positive fun" ) { @@ -80,7 +80,7 @@ TEST_CASE( "dining with table and chair", "[food][modify_morale][table][chair]" dummy.set_body(); const tripoint avatar_pos( 60, 60, 0 ); dummy.setpos( avatar_pos ); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); // Morale bonus only applies to unspoiled food that is not junk item &bread = dummy.i_add( item( "sourdough_bread" ) ); @@ -194,7 +194,7 @@ TEST_CASE( "dining with table and chair", "[food][modify_morale][table][chair]" TEST_CASE( "eating hot food", "[food][modify_morale][hot]" ) { avatar dummy; - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); GIVEN( "some food that tastes better when hot" ) { item &bread = dummy.i_add( item( "sourdough_bread" ) ); @@ -268,7 +268,7 @@ TEST_CASE( "cannibalism", "[food][modify_morale][cannibal]" ) { avatar dummy; dummy.set_body(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &human = dummy.i_add( item( "bone_human" ) ); REQUIRE( human.has_flag( flag_CANNIBALISM ) ); @@ -344,7 +344,7 @@ TEST_CASE( "sweet junk food", "[food][modify_morale][junk][sweet]" ) { avatar dummy; dummy.set_body(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); GIVEN( "some sweet junk food" ) { item &necco = dummy.i_add( item( "neccowafers" ) ); @@ -398,7 +398,7 @@ TEST_CASE( "junk food that is not ingested", "[modify_morale][junk][no_ingest]" { avatar dummy; dummy.set_body(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &caff_gum = dummy.i_add( item( "caff_gum" ) ); @@ -464,7 +464,7 @@ TEST_CASE( "food allergies and intolerances", "[food][modify_morale][allergy]" ) { avatar dummy; dummy.set_body(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); int penalty = -75; GIVEN( "character is vegetarian" ) { @@ -551,7 +551,7 @@ TEST_CASE( "saprophage character", "[food][modify_morale][saprophage]" ) { avatar dummy; dummy.set_body(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); GIVEN( "character is a saprophage, preferring rotted food" ) { dummy.clear_morale(); @@ -588,7 +588,7 @@ TEST_CASE( "ursine honey", "[food][modify_morale][ursine][honey]" ) { avatar dummy; dummy.set_body(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &honeycomb = dummy.i_add( item( "honeycomb" ) ); REQUIRE( honeycomb.has_flag( flag_URSINE_HONEY ) ); diff --git a/tests/mondefense_test.cpp b/tests/mondefense_test.cpp index 26363f0587aca..fcc2691daf582 100644 --- a/tests/mondefense_test.cpp +++ b/tests/mondefense_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "creature.h" #include "item.h" diff --git a/tests/monfactions_test.cpp b/tests/monfactions_test.cpp index cff04907e751d..1204cbeb775e5 100644 --- a/tests/monfactions_test.cpp +++ b/tests/monfactions_test.cpp @@ -1,7 +1,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "monfaction.h" #include "type_id.h" diff --git a/tests/monster_attack_test.cpp b/tests/monster_attack_test.cpp index 6c3ea1a0dd1a1..5a12d0fceb10f 100644 --- a/tests/monster_attack_test.cpp +++ b/tests/monster_attack_test.cpp @@ -3,7 +3,7 @@ #include "cached_options.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "game.h" #include "line.h" diff --git a/tests/monster_test.cpp b/tests/monster_test.cpp index 3e391a9e27f01..68983eeb63016 100644 --- a/tests/monster_test.cpp +++ b/tests/monster_test.cpp @@ -10,7 +10,7 @@ #include #include "cata_utility.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "game.h" #include "game_constants.h" diff --git a/tests/monster_vision_test.cpp b/tests/monster_vision_test.cpp index 6785a193bdf53..6fb433b48b7a3 100644 --- a/tests/monster_vision_test.cpp +++ b/tests/monster_vision_test.cpp @@ -1,6 +1,6 @@ #include "cached_options.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "map.h" #include "map_helpers.h" #include "mapdata.h" diff --git a/tests/moon_test.cpp b/tests/moon_test.cpp index 65d7c14aae968..55ef7e7bd8f3a 100644 --- a/tests/moon_test.cpp +++ b/tests/moon_test.cpp @@ -2,7 +2,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "enum_conversions.h" // MOON TESTS diff --git a/tests/morale_test.cpp b/tests/morale_test.cpp index 5ecef1567e43b..c06d9bc3bf02e 100644 --- a/tests/morale_test.cpp +++ b/tests/morale_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "bodypart.h" #include "item.h" diff --git a/tests/mutation_test.cpp b/tests/mutation_test.cpp index 96cec2e7de7fb..8997db26b78a5 100644 --- a/tests/mutation_test.cpp +++ b/tests/mutation_test.cpp @@ -4,7 +4,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "mutation.h" #include "npc.h" diff --git a/tests/name_test.cpp b/tests/name_test.cpp index 1f525c93384c9..a6c7c6168b671 100644 --- a/tests/name_test.cpp +++ b/tests/name_test.cpp @@ -2,7 +2,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "enum_traits.h" #include "name.h" diff --git a/tests/new_character_test.cpp b/tests/new_character_test.cpp index 6a1ac5e14cf5d..579699eb3c47d 100644 --- a/tests/new_character_test.cpp +++ b/tests/new_character_test.cpp @@ -10,14 +10,13 @@ #include #include "avatar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "inventory.h" #include "item.h" #include "pimpl.h" #include "profession.h" #include "scenario.h" #include "string_formatter.h" -#include "stringmaker.h" // IWYU pragma: keep #include "type_id.h" #include "visitable.h" diff --git a/tests/npc_talk_test.cpp b/tests/npc_talk_test.cpp index 8da6c8e8a311c..1b5f9937793ba 100644 --- a/tests/npc_talk_test.cpp +++ b/tests/npc_talk_test.cpp @@ -8,7 +8,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "character_id.h" #include "coordinate_conversions.h" @@ -616,7 +616,7 @@ TEST_CASE( "npc_talk_items", "[npc_talk]" ) }; player_character.cash = 1000; player_character.int_cur = 8; - player_character.worn.push_back( item( "backpack" ) ); + player_character.worn.emplace_back( "backpack" ); d.add_topic( "TALK_TEST_EFFECTS" ); gen_response_lines( d, 19 ); // add and remove effect diff --git a/tests/npc_test.cpp b/tests/npc_test.cpp index 178747affd4fd..9ddd6e13526a0 100644 --- a/tests/npc_test.cpp +++ b/tests/npc_test.cpp @@ -7,7 +7,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "common_types.h" #include "faction.h" diff --git a/tests/optional_test.cpp b/tests/optional_test.cpp index ce0d6b575925b..3e72261b5d85e 100644 --- a/tests/optional_test.cpp +++ b/tests/optional_test.cpp @@ -1,7 +1,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "optional.h" TEST_CASE( "optional_assignment_works", "[optional]" ) diff --git a/tests/overmap_noise_test.cpp b/tests/overmap_noise_test.cpp index 979340ddd7ef4..7df43907f4295 100644 --- a/tests/overmap_noise_test.cpp +++ b/tests/overmap_noise_test.cpp @@ -1,6 +1,6 @@ #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "coordinates.h" #include "game_constants.h" #include "overmap_noise.h" diff --git a/tests/overmap_test.cpp b/tests/overmap_test.cpp index 9435dab132c36..ce609cbb1e69c 100644 --- a/tests/overmap_test.cpp +++ b/tests/overmap_test.cpp @@ -2,7 +2,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "common_types.h" #include "coordinates.h" #include "enums.h" diff --git a/tests/player_helpers.cpp b/tests/player_helpers.cpp index 6658c7490792f..7907569b12b1e 100644 --- a/tests/player_helpers.cpp +++ b/tests/player_helpers.cpp @@ -7,7 +7,7 @@ #include "avatar.h" #include "bionics.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "character_id.h" #include "character_martial_arts.h" @@ -116,6 +116,52 @@ void clear_character( player &dummy ) dummy.setpos( spot ); } +void arm_shooter( npc &shooter, const std::string &gun_type, + const std::vector &mods, + const std::string &ammo_type ) +{ + shooter.remove_weapon(); + // XL so arrows can fit. + if( !shooter.is_wearing( itype_id( "debug_backpack" ) ) ) { + shooter.worn.emplace_back( "debug_backpack" ); + } + + const itype_id &gun_id{ itype_id( gun_type ) }; + // Give shooter a loaded gun of the requested type. + item &gun = shooter.i_add( item( gun_id ) ); + itype_id ammo_id; + // if ammo is not supplied we want the default + if( ammo_type.empty() ) { + if( gun.ammo_default().is_null() ) { + ammo_id = item( gun.magazine_default() ).ammo_default(); + } else { + ammo_id = gun.ammo_default(); + } + } else { + ammo_id = itype_id( ammo_type ); + } + const ammotype &type_of_ammo = item::find_type( ammo_id )->ammo->type; + if( gun.magazine_integral() ) { + item &ammo = shooter.i_add( item( ammo_id, calendar::turn, gun.ammo_capacity( type_of_ammo ) ) ); + REQUIRE( gun.is_reloadable_with( ammo_id ) ); + REQUIRE( shooter.can_reload( gun, ammo_id ) ); + gun.reload( shooter, item_location( shooter, &ammo ), gun.ammo_capacity( type_of_ammo ) ); + } else { + const itype_id magazine_id = gun.magazine_default(); + item &magazine = shooter.i_add( item( magazine_id ) ); + item &ammo = shooter.i_add( item( ammo_id, calendar::turn, + magazine.ammo_capacity( type_of_ammo ) ) ); + REQUIRE( magazine.is_reloadable_with( ammo_id ) ); + REQUIRE( shooter.can_reload( magazine, ammo_id ) ); + magazine.reload( shooter, item_location( shooter, &ammo ), magazine.ammo_capacity( type_of_ammo ) ); + gun.reload( shooter, item_location( shooter, &magazine ), magazine.ammo_capacity( type_of_ammo ) ); + } + for( const auto &mod : mods ) { + gun.put_in( item( itype_id( mod ) ), item_pocket::pocket_type::MOD ); + } + shooter.wield( gun ); +} + void clear_avatar() { clear_character( get_avatar() ); diff --git a/tests/player_helpers.h b/tests/player_helpers.h index df2fecc801109..59e79903a0737 100644 --- a/tests/player_helpers.h +++ b/tests/player_helpers.h @@ -21,4 +21,8 @@ void give_and_activate_bionic( player &, bionic_id const & ); item tool_with_ammo( const std::string &tool, int qty ); +void arm_shooter( npc &shooter, const std::string &gun_type, + const std::vector &mods = {}, + const std::string &ammo_type = "" ); + #endif // CATA_TESTS_PLAYER_HELPERS_H diff --git a/tests/player_test.cpp b/tests/player_test.cpp index 584e7b54febe6..612a8a5b9e11f 100644 --- a/tests/player_test.cpp +++ b/tests/player_test.cpp @@ -3,7 +3,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "item.h" #include "type_id.h" diff --git a/tests/pocket_test.cpp b/tests/pocket_test.cpp index 490134c2426ce..e97efc2e7b938 100644 --- a/tests/pocket_test.cpp +++ b/tests/pocket_test.cpp @@ -8,7 +8,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "debug.h" #include "enums.h" #include "flag.h" @@ -1335,7 +1335,7 @@ TEST_CASE( "character best pocket", "[pocket][character][best]" ) TEST_CASE( "guns and gunmods", "[pocket][gunmod]" ) { - item m4a1( "m4a1" ); + item m4a1( "nato_assault_rifle" ); item strap( "shoulder_strap" ); // Guns cannot "contain" gunmods, but gunmods can be inserted into guns CHECK_FALSE( m4a1.contents.can_contain( strap ).success() ); diff --git a/tests/point_test.cpp b/tests/point_test.cpp index 59bee2c3bed23..355e0cfa3adf4 100644 --- a/tests/point_test.cpp +++ b/tests/point_test.cpp @@ -2,7 +2,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "coordinates.h" #include "cuboid_rectangle.h" #include "point.h" diff --git a/tests/projectile_test.cpp b/tests/projectile_test.cpp index 9d7793498d95f..7e3efc0fcc83c 100644 --- a/tests/projectile_test.cpp +++ b/tests/projectile_test.cpp @@ -5,7 +5,7 @@ #include #include "ballistics.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "damage.h" #include "dispersion.h" diff --git a/tests/ranged_balance_test.cpp b/tests/ranged_balance_test.cpp index 32c3e64d6294e..bc27c11728a90 100644 --- a/tests/ranged_balance_test.cpp +++ b/tests/ranged_balance_test.cpp @@ -11,7 +11,7 @@ #include "bodypart.h" #include "calendar.h" #include "cata_utility.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "creature.h" #include "dispersion.h" #include "game_constants.h" @@ -24,6 +24,7 @@ #include "map_helpers.h" #include "npc.h" #include "pimpl.h" +#include "player_helpers.h" #include "point.h" #include "ret_val.h" #include "test_statistics.h" @@ -76,52 +77,6 @@ std::ostream &operator<<( std::ostream &stream, const dispersion_sources &source return stream; } -static void arm_shooter( npc &shooter, const std::string &gun_type, - const std::vector &mods = {}, - const std::string &ammo_type = "" ) -{ - shooter.remove_weapon(); - // XL so arrows can fit. - if( !shooter.is_wearing( itype_id( "debug_backpack" ) ) ) { - shooter.worn.push_back( item( "debug_backpack" ) ); - } - - const itype_id &gun_id{ itype_id( gun_type ) }; - // Give shooter a loaded gun of the requested type. - item &gun = shooter.i_add( item( gun_id ) ); - itype_id ammo_id; - // if ammo is not supplied we want the default - if( ammo_type.empty() ) { - if( gun.ammo_default().is_null() ) { - ammo_id = item( gun.magazine_default() ).ammo_default(); - } else { - ammo_id = gun.ammo_default(); - } - } else { - ammo_id = itype_id( ammo_type ); - } - const ammotype &type_of_ammo = item::find_type( ammo_id )->ammo->type; - if( gun.magazine_integral() ) { - item &ammo = shooter.i_add( item( ammo_id, calendar::turn, gun.ammo_capacity( type_of_ammo ) ) ); - REQUIRE( gun.is_reloadable_with( ammo_id ) ); - REQUIRE( shooter.can_reload( gun, ammo_id ) ); - gun.reload( shooter, item_location( shooter, &ammo ), gun.ammo_capacity( type_of_ammo ) ); - } else { - const itype_id magazine_id = gun.magazine_default(); - item &magazine = shooter.i_add( item( magazine_id ) ); - item &ammo = shooter.i_add( item( ammo_id, calendar::turn, - magazine.ammo_capacity( type_of_ammo ) ) ); - REQUIRE( magazine.is_reloadable_with( ammo_id ) ); - REQUIRE( shooter.can_reload( magazine, ammo_id ) ); - magazine.reload( shooter, item_location( shooter, &ammo ), magazine.ammo_capacity( type_of_ammo ) ); - gun.reload( shooter, item_location( shooter, &magazine ), magazine.ammo_capacity( type_of_ammo ) ); - } - for( const auto &mod : mods ) { - gun.put_in( item( itype_id( mod ) ), item_pocket::pocket_type::MOD ); - } - shooter.wield( gun ); -} - static void equip_shooter( npc &shooter, const std::vector &apparel ) { CHECK( !shooter.in_vehicle ); @@ -162,6 +117,7 @@ static std::vector firing_test( const dispersion_sources &dis const int range, const std::vector< Threshold > &thresholds ) { std::vector firing_stats; + firing_stats.reserve( thresholds.size() ); for( const Threshold &pear : thresholds ) { firing_stats.push_back( firing_test( dispersion, range, pear ) ); } @@ -271,7 +227,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" ) ); + shooter.worn.emplace_back( "backpack" ); equip_shooter( shooter, { "bastsandals", "armguard_chitin", "armor_chitin", "beekeeping_gloves", "mask_guy_fawkes", "cowboy_hat" } ); assert_encumbrance( shooter, 10 ); diff --git a/tests/reachability_cache_test.cpp b/tests/reachability_cache_test.cpp index f6defcb9747ee..c5ec897f7d30f 100644 --- a/tests/reachability_cache_test.cpp +++ b/tests/reachability_cache_test.cpp @@ -4,7 +4,7 @@ #include #include "cached_options.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "map.h" #include "map_helpers.h" #include "map_iterator.h" diff --git a/tests/reading_test.cpp b/tests/reading_test.cpp index b2f6c48e20af6..a362594c8fb65 100644 --- a/tests/reading_test.cpp +++ b/tests/reading_test.cpp @@ -6,7 +6,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "item.h" #include "itype.h" @@ -44,7 +44,7 @@ TEST_CASE( "identifying unread books", "[reading][book][identify]" ) { clear_avatar(); Character &dummy = get_avatar(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); GIVEN( "character has some unidentified books" ) { item &book1 = dummy.i_add( item( "novel_western" ) ); @@ -70,7 +70,7 @@ TEST_CASE( "reading a book for fun", "[reading][book][fun]" ) clear_avatar(); Character &dummy = get_avatar(); dummy.set_body(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); GIVEN( "a fun book" ) { item &book = dummy.i_add( item( "novel_western" ) ); @@ -141,7 +141,7 @@ TEST_CASE( "character reading speed", "[reading][character][speed]" ) { clear_avatar(); Character &dummy = get_avatar(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); // Note: read_speed() returns number of moves; // 6000 == 60 seconds @@ -186,7 +186,7 @@ TEST_CASE( "character reading speed", "[reading][character][speed]" ) TEST_CASE( "estimated reading time for a book", "[reading][book][time]" ) { avatar dummy; - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); // Easy, medium, and hard books item &child = dummy.i_add( item( "child_book" ) ); @@ -274,7 +274,7 @@ TEST_CASE( "reasons for not being able to read", "[reading][reasons]" ) { avatar dummy; dummy.set_body(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); std::vector reasons; std::vector expect_reasons; @@ -369,7 +369,7 @@ TEST_CASE( "determining book mastery", "[reading][book][mastery]" ) avatar dummy; dummy.set_body(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &child = dummy.i_add( item( "child_book" ) ); item &alpha = dummy.i_add( item( "recipe_alpha" ) ); diff --git a/tests/recipe_test.cpp b/tests/recipe_test.cpp index 4cc53a1996901..49e3e1907329a 100644 --- a/tests/recipe_test.cpp +++ b/tests/recipe_test.cpp @@ -2,7 +2,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "json.h" #include "recipe.h" diff --git a/tests/reload_magazine_test.cpp b/tests/reload_magazine_test.cpp index c9bd8ce63a3cf..76fb218827abd 100644 --- a/tests/reload_magazine_test.cpp +++ b/tests/reload_magazine_test.cpp @@ -6,7 +6,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "inventory.h" #include "item.h" @@ -17,9 +17,10 @@ struct itype; +// NOLINTNEXTLINE(readability-function-size) TEST_CASE( "reload_magazine", "[magazine] [visitable] [item] [item_location]" ) { - const itype_id gun_id( "m4a1" ); + const itype_id gun_id( "nato_assault_rifle" ); const ammotype gun_ammo( "223" ); const itype_id ammo_id( "556" ); // any type of compatible ammo const itype_id alt_ammo( "223" ); // any alternative type of compatible ammo diff --git a/tests/reload_option_test.cpp b/tests/reload_option_test.cpp index cd894ee6cc95d..416649bbe26be 100644 --- a/tests/reload_option_test.cpp +++ b/tests/reload_option_test.cpp @@ -3,7 +3,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "item.h" #include "item_location.h" #include "item_pocket.h" @@ -15,7 +15,7 @@ TEST_CASE( "revolver_reload_option", "[reload],[reload_option],[gun]" ) { avatar dummy; - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &gun = dummy.i_add( item( "sw_619", calendar::turn_zero, 0 ) ); const ammotype &gun_ammo_type = item::find_type( gun.ammo_default() )->ammo->type; @@ -46,7 +46,7 @@ TEST_CASE( "revolver_reload_option", "[reload],[reload_option],[gun]" ) TEST_CASE( "magazine_reload_option", "[reload],[reload_option],[gun]" ) { avatar dummy; - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &magazine = dummy.i_add( item( "glockmag", calendar::turn_zero, 0 ) ); const ammotype &mag_ammo_type = item::find_type( magazine.ammo_default() )->ammo->type; @@ -69,7 +69,7 @@ TEST_CASE( "belt_reload_option", "[reload],[reload_option],[gun]" ) { avatar dummy; dummy.set_body(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &belt = dummy.i_add( item( "belt308", calendar::turn_zero, 0 ) ); const ammotype &belt_ammo_type = item::find_type( belt.ammo_default() )->ammo->type; @@ -96,7 +96,7 @@ TEST_CASE( "belt_reload_option", "[reload],[reload_option],[gun]" ) TEST_CASE( "canteen_reload_option", "[reload],[reload_option],[liquid]" ) { avatar dummy; - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &bottle = dummy.i_add( item( "bottle_plastic" ) ); item water( "water_clean", calendar::turn_zero, 2 ); diff --git a/tests/reloading_test.cpp b/tests/reloading_test.cpp index 29ab9e5895add..56b7b5d929c3a 100644 --- a/tests/reloading_test.cpp +++ b/tests/reloading_test.cpp @@ -6,7 +6,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "game.h" #include "item.h" #include "item_contents.h" @@ -211,7 +211,7 @@ TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) GIVEN( "a player wielding an unloaded gun, carrying an unloaded magazine, and carrying ammo for the magazine" ) { dummy.worn.clear(); - dummy.worn.push_back( item( "backpack" ) ); + dummy.worn.emplace_back( "backpack" ); item &ammo = dummy.i_add( item( "9mm", calendar::turn_zero, 50 ) ); const cata::value_ptr &ammo_type = ammo.type->ammo; REQUIRE( ammo_type ); diff --git a/tests/requirements_test.cpp b/tests/requirements_test.cpp index 367d97cf893f4..eb73a620fa326 100644 --- a/tests/requirements_test.cpp +++ b/tests/requirements_test.cpp @@ -3,7 +3,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "requirements.h" #include "type_id.h" diff --git a/tests/rewrite_vsnprintf_test.cpp b/tests/rewrite_vsnprintf_test.cpp index 69a83f49b5fe1..f1e17ae8cc29e 100644 --- a/tests/rewrite_vsnprintf_test.cpp +++ b/tests/rewrite_vsnprintf_test.cpp @@ -3,7 +3,7 @@ #include // the rewrite_vsnprintf function is explicitly defined for non-MS compilers in output.cpp -#include "catch/catch.hpp" +#include "cata_catch.h" #include "output.h" TEST_CASE( "Test vsnprintf_rewrite" ) diff --git a/tests/rng_test.cpp b/tests/rng_test.cpp index d1ba612048fd9..c19277b1ad77e 100644 --- a/tests/rng_test.cpp +++ b/tests/rng_test.cpp @@ -1,7 +1,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "optional.h" #include "rng.h" #include "test_statistics.h" @@ -54,11 +54,11 @@ static void check_x_in_y( double x, double y ) TEST_CASE( "x_in_y_distribution" ) { float y_increment = 0.01f; - // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter) + // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c) for( float y = 0.1f; y < 500.0f; y += y_increment ) { y_increment *= 1.1f; float x_increment = 0.1f; - // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter) + // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c) for( float x = 0.1f; x < y; x += x_increment ) { check_x_in_y( x, y ); x_increment *= 1.1f; diff --git a/tests/rot_test.cpp b/tests/rot_test.cpp index 4a9bd41abf721..3eb05e471575b 100644 --- a/tests/rot_test.cpp +++ b/tests/rot_test.cpp @@ -1,5 +1,5 @@ #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "enums.h" #include "item.h" #include "point.h" diff --git a/tests/safe_reference_test.cpp b/tests/safe_reference_test.cpp index a0b7681a8793e..2b0589073509f 100644 --- a/tests/safe_reference_test.cpp +++ b/tests/safe_reference_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include diff --git a/tests/shadowcasting_test.cpp b/tests/shadowcasting_test.cpp index a5813893e3cd4..c5c04c1368789 100644 --- a/tests/shadowcasting_test.cpp +++ b/tests/shadowcasting_test.cpp @@ -7,7 +7,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "cuboid_rectangle.h" #include "game_constants.h" #include "level_cache.h" diff --git a/tests/simple_pathfinding_test.cpp b/tests/simple_pathfinding_test.cpp index 617ed3a8121e5..621d756ed6c1c 100644 --- a/tests/simple_pathfinding_test.cpp +++ b/tests/simple_pathfinding_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "simple_pathfinding.h" #include "coordinates.h" diff --git a/tests/stats_tracker_test.cpp b/tests/stats_tracker_test.cpp index 3519444cb965a..3f1289b6a46f1 100644 --- a/tests/stats_tracker_test.cpp +++ b/tests/stats_tracker_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "stats_tracker.h" #include diff --git a/tests/stomach_contents_test.cpp b/tests/stomach_contents_test.cpp index f28cb10819775..90680b265b9bc 100644 --- a/tests/stomach_contents_test.cpp +++ b/tests/stomach_contents_test.cpp @@ -4,7 +4,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "item.h" #include "player.h" diff --git a/tests/string_formatter_test.cpp b/tests/string_formatter_test.cpp index 18e4d50bb0fc5..badd7eed5d87f 100644 --- a/tests/string_formatter_test.cpp +++ b/tests/string_formatter_test.cpp @@ -4,7 +4,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "string_formatter.h" // Same as @ref string_format, but does not swallow errors and throws them instead. @@ -101,8 +101,7 @@ void mingw_test( const char *const old_pattern, const char *const new_pattern, c CHECK( original_result == new_result ); } -// Marking mayfail due to failure in MXE's MinGW on Travis on Ubuntu Xenial. -TEST_CASE( "string_formatter", "[!mayfail]" ) +TEST_CASE( "string_formatter" ) { test_typed_printf( "%hhi", "%i" ); test_typed_printf( "%hhu", "%u" ); @@ -221,7 +220,10 @@ TEST_CASE( "string_formatter", "[!mayfail]" ) importet_test( 38, "42 ", "%0-15d", 42 ); importet_test( 39, "-42 ", "%0-15d", -42 ); importet_test( 43, "42.90", "%.2f", 42.8952 ); +#if !(defined(__MINGW32__) || defined(__MINGW64__)) + // Omit this one that fails on Mingw importet_test( 44, "42.90", "%.2F", 42.8952 ); +#endif importet_test( 45, "42.8952000000", "%.10f", 42.8952 ); importet_test( 46, "42.90", "%1.2f", 42.8952 ); importet_test( 47, " 42.90", "%6.2f", 42.8952 ); @@ -515,6 +517,8 @@ TEST_CASE( "string_formatter", "[!mayfail]" ) importet_test( 365, " 00edcb5433", "%20.10x", 3989525555U ); importet_test( 366, " 1234ABCD", "%20.5X", 305441741 ); importet_test( 367, " 00EDCB5433", "%20.10X", 3989525555U ); +#if !(defined(__MINGW32__) || defined(__MINGW64__)) + // Omit these ones that fail on Mingw importet_test( 369, " 01024", "%020.5d", 1024 ); importet_test( 370, " -01024", "%020.5d", -1024 ); importet_test( 371, " 01024", "%020.5i", 1024 ); @@ -527,6 +531,7 @@ TEST_CASE( "string_formatter", "[!mayfail]" ) importet_test( 378, " 00edcb5433", "%020.10x", 3989525555U ); importet_test( 379, " 1234ABCD", "%020.5X", 305441741 ); importet_test( 380, " 00EDCB5433", "%020.10X", 3989525555U ); +#endif importet_test( 381, "", "%.0s", "Hallo heimur" ); importet_test( 382, " ", "%20.0s", "Hallo heimur" ); importet_test( 383, "", "%.s", "Hallo heimur" ); diff --git a/tests/string_ids_test.cpp b/tests/string_ids_test.cpp index 24d06dfbd0e46..f8479470721a2 100644 --- a/tests/string_ids_test.cpp +++ b/tests/string_ids_test.cpp @@ -6,7 +6,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "field_type.h" #include "string_id_utils.h" #include "type_id.h" @@ -37,8 +37,9 @@ TEST_CASE( "string_ids_intern_test", "[string_id]" ) struct test_obj {}; std::vector> ids; // lots of ids to make sure that "interning" map gets expanded + ids.reserve( num_ids ); for( int i = 0; i < num_ids; ++i ) { - ids.push_back( string_id( "test_id" + std::to_string( i ) ) ); + ids.emplace_back( "test_id" + std::to_string( i ) ); } // check that interning works @@ -106,7 +107,7 @@ TEST_CASE( "string_id_sorting_test", "[string_id]" ) SECTION( "vector of pairs sorting" ) { std::vector> vec; for( int i = 9; i >= 0; i-- ) { - vec.push_back( {id( "id" + std::to_string( i ) ), i} ); + vec.emplace_back( id( "id" + std::to_string( i ) ), i ); } int i = 0; @@ -119,8 +120,9 @@ TEST_CASE( "string_id_sorting_test", "[string_id]" ) SECTION( "vector ids sorting" ) { std::vector vec; + vec.reserve( 10 ); for( int i = 0; i < 10; ++i ) { - vec.push_back( id( "id" + std::to_string( i ) ) ); + vec.emplace_back( "id" + std::to_string( i ) ); } int i = 0; @@ -150,6 +152,7 @@ TEST_CASE( "string_id_creation_benchmark", "[.][string_id][benchmark]" ) { static constexpr int num_test_strings = 30; std::vector test_strings; + test_strings.reserve( num_test_strings ); for( int i = 0; i < num_test_strings; ++i ) { test_strings.push_back( "some_test_string_" + std::to_string( i ) ); } diff --git a/tests/string_test.cpp b/tests/string_test.cpp index 4293374abc27e..7dab191d90caa 100644 --- a/tests/string_test.cpp +++ b/tests/string_test.cpp @@ -1,7 +1,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "output.h" static void test_remove_color_tags( const std::string &original, const std::string &expected ) @@ -38,3 +38,26 @@ TEST_CASE( "trim_by_length" ) CHECK( trim_by_length( "MRE 主菜(鸡肉意大利香蒜沙司通心粉)(新鲜)", 36 ) == "MRE 主菜(鸡肉意大利香蒜沙司通心粉…" ); } + +TEST_CASE( "str_cat" ) +{ + CHECK( str_cat( " " ) == " " ); + CHECK( str_cat( "a", "b", "c" ) == "abc" ); + CHECK( str_cat( "a", std::string( "b" ), "c" ) == "abc" ); + std::string a( "a" ); + std::string b( "b" ); + std::string result = str_cat( a, std::move( b ), a ); + CHECK( result == "aba" ); +} + +TEST_CASE( "str_append" ) +{ + std::string root( "a" ); + CHECK( str_append( root, " " ) == "a " ); + CHECK( str_append( root, "b", "c" ) == "a bc" ); + CHECK( str_append( root, std::string( "b" ), "c" ) == "a bcbc" ); + std::string a( "a" ); + std::string b( "b" ); + str_append( root, a, std::move( b ), a ); + CHECK( root == "a bcbcaba" ); +} diff --git a/tests/stringmaker.h b/tests/stringmaker.h index d5c6938869512..d0eac3c374539 100644 --- a/tests/stringmaker.h +++ b/tests/stringmaker.h @@ -3,7 +3,7 @@ #define CATA_TESTS_STRINGMAKER_H #include "cuboid_rectangle.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "cata_variant.h" #include "dialogue.h" #include "item.h" diff --git a/tests/submap_load_test.cpp b/tests/submap_load_test.cpp index 5493f52efba1e..38260a285c6b0 100644 --- a/tests/submap_load_test.cpp +++ b/tests/submap_load_test.cpp @@ -7,7 +7,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "colony.h" #include "construction.h" #include "field.h" @@ -926,7 +926,7 @@ TEST_CASE( "submap_furniture_load", "[submap][load]" ) REQUIRE( furn_ne == f_bookcase ); REQUIRE( furn_sw == f_dresser ); REQUIRE( furn_se == f_crate_o ); - REQUIRE( furn_ra == STATIC( furn_id( "f_gas_tank" ) ) ); + REQUIRE( furn_ra == STATIC( furn_str_id( "f_gas_tank" ) ) ); // Also, check we have no other furniture for( int x = 0; x < SEEX; ++x ) { @@ -963,11 +963,11 @@ TEST_CASE( "submap_trap_load", "[submap][load]" ) INFO( string_format( "se: %s", trap_se.id().str() ) ); INFO( string_format( "ra: %s", trap_ra.id().str() ) ); // Require to prevent the lower CHECK from being spammy - REQUIRE( trap_nw == STATIC( trap_id( "tr_rollmat" ) ) ); - REQUIRE( trap_ne == STATIC( trap_id( "tr_bubblewrap" ) ) ); - REQUIRE( trap_sw == STATIC( trap_id( "tr_beartrap" ) ) ); - REQUIRE( trap_se == STATIC( trap_id( "tr_funnel" ) ) ); - REQUIRE( trap_ra == STATIC( trap_id( "tr_landmine" ) ) ); + REQUIRE( trap_nw == STATIC( trap_str_id( "tr_rollmat" ) ) ); + REQUIRE( trap_ne == STATIC( trap_str_id( "tr_bubblewrap" ) ) ); + REQUIRE( trap_sw == STATIC( trap_str_id( "tr_beartrap" ) ) ); + REQUIRE( trap_se == STATIC( trap_str_id( "tr_funnel" ) ) ); + REQUIRE( trap_ra == STATIC( trap_str_id( "tr_landmine" ) ) ); // Also, check we have no other traps for( int x = 0; x < SEEX; ++x ) { @@ -1113,12 +1113,12 @@ TEST_CASE( "submap_field_load", "[submap][load]" ) const field &field_sw = sm.get_field( corner_sw ); const field &field_se = sm.get_field( corner_se ); const field &field_ra = sm.get_field( random_pt ); - const field_entry *fd_nw = field_nw.find_field( STATIC( field_type_id( "fd_web" ) ) ); - const field_entry *fd_ne = field_ne.find_field( STATIC( field_type_id( "fd_laser" ) ) ); - const field_entry *fd_sw = field_sw.find_field( STATIC( field_type_id( "fd_electricity" ) ) ); - const field_entry *fd_se = field_se.find_field( STATIC( field_type_id( "fd_acid" ) ) ); - const field_entry *fd_ra = field_ra.find_field( STATIC( field_type_id( "fd_nuke_gas" ) ) ); - const field_entry *fd_ow = field_nw.find_field( STATIC( field_type_id( "fd_smoke" ) ) ); + const field_entry *fd_nw = field_nw.find_field( STATIC( field_type_str_id( "fd_web" ) ) ); + const field_entry *fd_ne = field_ne.find_field( STATIC( field_type_str_id( "fd_laser" ) ) ); + const field_entry *fd_sw = field_sw.find_field( STATIC( field_type_str_id( "fd_electricity" ) ) ); + const field_entry *fd_se = field_se.find_field( STATIC( field_type_str_id( "fd_acid" ) ) ); + const field_entry *fd_ra = field_ra.find_field( STATIC( field_type_str_id( "fd_nuke_gas" ) ) ); + const field_entry *fd_ow = field_nw.find_field( STATIC( field_type_str_id( "fd_smoke" ) ) ); // No nullptrs for me REQUIRE( fd_nw != nullptr ); REQUIRE( fd_ow != nullptr ); diff --git a/tests/submap_test.cpp b/tests/submap_test.cpp index 2d63ec3b1120b..e38f81d375d88 100644 --- a/tests/submap_test.cpp +++ b/tests/submap_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "submap.h" #include "game_constants.h" diff --git a/tests/sun_test.cpp b/tests/sun_test.cpp index 7c067532b3464..5ffbe45abb807 100644 --- a/tests/sun_test.cpp +++ b/tests/sun_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "calendar.h" // IWYU pragma: associated #include diff --git a/tests/temp_crafting_inv_test.cpp b/tests/temp_crafting_inv_test.cpp index 7e91aeca5145a..0d9da91e815b7 100644 --- a/tests/temp_crafting_inv_test.cpp +++ b/tests/temp_crafting_inv_test.cpp @@ -1,6 +1,6 @@ #include "../src/temp_crafting_inventory.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "item.h" #include "type_id.h" diff --git a/tests/temperature_test.cpp b/tests/temperature_test.cpp index 24d1677b61e6f..c18262d9c49cf 100644 --- a/tests/temperature_test.cpp +++ b/tests/temperature_test.cpp @@ -1,6 +1,6 @@ #include "calendar.h" #include "cata_utility.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "enums.h" #include "flag.h" #include "game_constants.h" diff --git a/tests/test_main.cpp b/tests/test_main.cpp index a53e34208d546..f8c37bdbc2a29 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -17,7 +17,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "coordinates.h" #ifndef _WIN32 #include diff --git a/tests/test_statistics.h b/tests/test_statistics.h index 3cbb2fe779643..9d5f99b18ad1c 100644 --- a/tests/test_statistics.h +++ b/tests/test_statistics.h @@ -9,7 +9,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" // Z-value for confidence interval constexpr double Z95 = 1.96; @@ -223,7 +223,7 @@ class BinomialMatcher : public Catch::MatcherBase // distribution. Uses a normal approximation to the binomial, and permits a // deviation up to max_deviation (measured in standard deviations). inline BinomialMatcher IsBinomialObservation( - const int num_samples, const double p, const double max_deviation = Z99_99 ) + const int num_samples, const double p, const double max_deviation = Z99_999 ) { return BinomialMatcher( num_samples, p, max_deviation ); } diff --git a/tests/text_snippets_test.cpp b/tests/text_snippets_test.cpp index 8c6a6f50921ec..b5b98d70f580e 100644 --- a/tests/text_snippets_test.cpp +++ b/tests/text_snippets_test.cpp @@ -1,6 +1,6 @@ #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "optional.h" #include "text_snippets.h" #include "translations.h" diff --git a/tests/throwing_test.cpp b/tests/throwing_test.cpp index 4072d08fced58..3d947e1d66eec 100644 --- a/tests/throwing_test.cpp +++ b/tests/throwing_test.cpp @@ -5,7 +5,7 @@ #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "damage.h" #include "game.h" #include "game_constants.h" diff --git a/tests/translations_test.cpp b/tests/translations_test.cpp index 5293be188d3b4..c8516298de3e9 100644 --- a/tests/translations_test.cpp +++ b/tests/translations_test.cpp @@ -2,7 +2,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "translations.h" // wrapping in another macro to prevent collection of the test string for translation diff --git a/tests/try_parse_integer_test.cpp b/tests/try_parse_integer_test.cpp new file mode 100644 index 0000000000000..a40e9f48428b9 --- /dev/null +++ b/tests/try_parse_integer_test.cpp @@ -0,0 +1,115 @@ +#include "cata_catch.h" +#include "try_parse_integer.h" + +TEMPLATE_TEST_CASE( "try_parse_int_simple_parsing", "[try_parse_integer]", int, long, long long ) +{ + try { + std::locale::global( std::locale( "en_US.UTF-8" ) ); + } catch( std::runtime_error & ) { + // On platforms where we can't set the locale, the test should still + // pass + } + CAPTURE( setlocale( LC_ALL, nullptr ) ); + CAPTURE( std::locale().name() ); + + bool use_locale = GENERATE( false, true ); + CAPTURE( use_locale ); + + SECTION( "successes" ) { + { + ret_val result = try_parse_integer( "1234", use_locale ); + CAPTURE( result.str() ); + CHECK( result.success() ); + CHECK( result.value() == 1234 ); + } + { + ret_val result = try_parse_integer( "-1234", use_locale ); + CAPTURE( result.str() ); + CHECK( result.success() ); + CHECK( result.value() == -1234 ); + } + { + ret_val result = try_parse_integer( "+1234", use_locale ); + CAPTURE( result.str() ); + CHECK( result.success() ); + CHECK( result.value() == +1234 ); + } + { + ret_val result = try_parse_integer( " 1234", use_locale ); + CAPTURE( result.str() ); + CHECK( result.success() ); + CHECK( result.value() == 1234 ); + } + } + + SECTION( "errors" ) { + { + ret_val result = try_parse_integer( "", use_locale ); + CHECK( !result.success() ); + CHECK( result.str() == "Could not convert '' to an integer" ); + } + { + ret_val result = try_parse_integer( "a", use_locale ); + CHECK( !result.success() ); + CHECK( result.str() == "Could not convert 'a' to an integer" ); + } + { + // Verify that we detect overflow + ret_val result = + try_parse_integer( "999999999999999999999", use_locale ); + CHECK( !result.success() ); + CHECK( result.str() == "Could not convert '999999999999999999999' to an integer" ); + } + } +} + +TEMPLATE_TEST_CASE( "try_parse_int_locale_parsing", "[try_parse_integer]", int, long, long long ) +{ + SECTION( "de_DE" ) { + try { + std::locale::global( std::locale( "de_DE.UTF-8" ) ); + } catch( std::runtime_error & ) { + // On platforms where we can't set the locale, ignore this test + return; + } + CAPTURE( setlocale( LC_ALL, nullptr ) ); + CAPTURE( std::locale().name() ); + // Disabling on Apple; seems like their C library doesn't do localized integer + // parsing. +#ifndef __APPLE__ + { + ret_val result = try_parse_integer( "1.234", true ); + CHECK( result.success() ); + CHECK( result.value() == 1234 ); + } +#endif + { + ret_val result = try_parse_integer( "1.234", false ); + CHECK( !result.success() ); + CHECK( result.str() == "Stray characters after integer in '1.234'" ); + } + } + + SECTION( "en_US" ) { + try { + std::locale::global( std::locale( "en_US.UTF-8" ) ); + } catch( std::runtime_error & ) { + // On platforms where we can't set the locale, ignore this test + return; + } + CAPTURE( setlocale( LC_ALL, nullptr ) ); + CAPTURE( std::locale().name() ); +#ifndef __APPLE__ + { + ret_val result = try_parse_integer( "1,234", true ); + CHECK( result.success() ); + CHECK( result.value() == 1234 ); + } +#endif + { + ret_val result = try_parse_integer( "1,234", false ); + CHECK( !result.success() ); + CHECK( result.str() == "Stray characters after integer in '1,234'" ); + } + } +} diff --git a/tests/units_test.cpp b/tests/units_test.cpp index 0af40be98d23e..b849df756939a 100644 --- a/tests/units_test.cpp +++ b/tests/units_test.cpp @@ -4,7 +4,7 @@ #include "calendar.h" #include "cata_utility.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "json.h" #include "math_defines.h" #include "options_helpers.h" diff --git a/tests/unseal_and_spill_test.cpp b/tests/unseal_and_spill_test.cpp index 6fdcacdde764f..a3cd35838942c 100644 --- a/tests/unseal_and_spill_test.cpp +++ b/tests/unseal_and_spill_test.cpp @@ -9,7 +9,7 @@ #include "avatar.h" #include "cached_options.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "colony.h" #include "item.h" diff --git a/tests/value_ptr_test.cpp b/tests/value_ptr_test.cpp index 407507f1dfb75..187f2de2bba4d 100644 --- a/tests/value_ptr_test.cpp +++ b/tests/value_ptr_test.cpp @@ -1,6 +1,6 @@ #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "value_ptr.h" TEST_CASE( "value_ptr copy constructor", "[value_ptr]" ) diff --git a/tests/vehicle_drag_test.cpp b/tests/vehicle_drag_test.cpp index abce3fc33e947..2d5264ebd9f0c 100644 --- a/tests/vehicle_drag_test.cpp +++ b/tests/vehicle_drag_test.cpp @@ -4,7 +4,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "map.h" #include "map_helpers.h" @@ -239,28 +239,28 @@ TEST_CASE( "vehicle_drag", "[vehicle] [engine]" ) test_vehicle_drag( "superbike", 0.609525, 0.844294, 377.476562, 9912, 11797 ); test_vehicle_drag( "tandem", 0.609525, 0.010590, 19.990625, 1430, 1870 ); test_vehicle_drag( "unicycle", 0.690795, 0.002493, 25.100000, 1377, 1798 ); - test_vehicle_drag( "beetle", 0.785610, 1.802151, 1275.732812, 8969, 10710 ); - test_vehicle_drag( "bubble_car", 0.823988, 1.764712, 1189.742560, 9304, 9651 ); - test_vehicle_drag( "car", 0.294604, 2.473484, 1167.310417, 11916, 14350 ); - test_vehicle_drag( "car_mini", 0.294604, 1.817781, 1286.796875, 12157, 14580 ); - test_vehicle_drag( "car_sports", 0.294604, 2.549405, 1443.767500, 20848, 24904 ); - test_vehicle_drag( "car_sports_atomic", 0.294604, 3.788275, 1787.798958, 23696, 24593 ); - test_vehicle_drag( "car_sports_electric", 0.294604, 3.119641, 1766.701250, 23901, 24797 ); - test_vehicle_drag( "electric_car", 0.304763, 2.311289, 1090.765625, 16266, 16882 ); - test_vehicle_drag( "rara_x", 0.679185, 1.952527, 1092.094907, 9114, 9459 ); - test_vehicle_drag( "suv", 0.294604, 2.914201, 1178.826786, 13988, 16827 ); - test_vehicle_drag( "suv_electric", 0.304763, 2.461925, 995.875893, 16216, 16833 ); + test_vehicle_drag( "beetle", 0.785610, 1.584313, 1121.526562, 9005, 10744 ); + test_vehicle_drag( "bubble_car", 0.823988, 1.002279, 675.721726, 9424, 9769 ); + test_vehicle_drag( "car", 0.294604, 2.255646, 1064.506250, 11995, 14426 ); + test_vehicle_drag( "car_mini", 0.294604, 1.599943, 1132.590625, 12238, 14657 ); + test_vehicle_drag( "car_sports", 0.294604, 2.331567, 1320.402500, 20918, 24972 ); + test_vehicle_drag( "car_sports_atomic", 0.294604, 3.461518, 1633.592708, 23795, 24693 ); + test_vehicle_drag( "car_sports_electric", 0.294604, 2.901803, 1643.336250, 23968, 24864 ); + test_vehicle_drag( "electric_car", 0.304763, 2.093451, 987.961458, 16337, 16954 ); + test_vehicle_drag( "rara_x", 0.679185, 1.544080, 863.641204, 9191, 9535 ); + test_vehicle_drag( "suv", 0.294604, 2.696363, 1090.708929, 14063, 16899 ); + test_vehicle_drag( "suv_electric", 0.304763, 2.244087, 907.758036, 16288, 16904 ); test_vehicle_drag( "golf_cart", 0.943313, 1.472114, 926.312500, 7039, 7303 ); test_vehicle_drag( "golf_cart_4seat", 0.943313, 1.439476, 905.775000, 7044, 7308 ); - test_vehicle_drag( "hearse", 0.355556, 3.216780, 1301.223214, 11046, 13340 ); - test_vehicle_drag( "pickup_technical", 0.838097, 2.961637, 1198.015179, 10176, 12172 ); - test_vehicle_drag( "ambulance", 1.049323, 2.348252, 1927.320833, 11306, 13472 ); - test_vehicle_drag( "car_fbi", 0.457144, 2.743431, 1294.706250, 14625, 17490 ); + test_vehicle_drag( "hearse", 0.355556, 2.672185, 1080.928571, 11212, 13499 ); + test_vehicle_drag( "pickup_technical", 0.838097, 2.631835, 1064.606250, 10223, 12217 ); + test_vehicle_drag( "ambulance", 1.049323, 2.191681, 1798.815625, 11324, 13488 ); + test_vehicle_drag( "car_fbi", 0.457144, 2.525593, 1191.902083, 14675, 17538 ); test_vehicle_drag( "fire_engine", 2.305875, 3.544531, 2327.331250, 8697, 10364 ); test_vehicle_drag( "fire_truck", 1.056510, 8.175862, 5419.870313, 10648, 12841 ); - test_vehicle_drag( "policecar", 0.629843, 2.744534, 1295.227083, 13235, 15807 ); - test_vehicle_drag( "policesuv", 0.629843, 3.096796, 1252.688393, 13173, 15749 ); - test_vehicle_drag( "truck_swat", 0.808830, 7.607580, 6243.900000, 9929, 11682 ); + test_vehicle_drag( "policecar", 0.629843, 2.526697, 1192.422917, 13273, 15844 ); + test_vehicle_drag( "policesuv", 0.629843, 2.878958, 1164.570536, 13211, 15785 ); + test_vehicle_drag( "truck_swat", 0.808830, 8.226852, 6752.165625, 9846, 11602 ); test_vehicle_drag( "oldtractor", 0.537285, 0.893482, 1319.981250, 12446, 14408 ); test_vehicle_drag( "autotractor", 1.425450, 2.057851, 2026.770000, 7778, 8066 ); test_vehicle_drag( "tractor_plow", 0.609525, 0.918444, 1739.562500, 11941, 13822 ); @@ -268,34 +268,34 @@ TEST_CASE( "vehicle_drag", "[vehicle] [engine]" ) test_vehicle_drag( "tractor_seed", 0.609525, 0.804219, 1523.216346, 11963, 13843 ); test_vehicle_drag( "aapc-mg", 1.625400, 8.660271, 4378.969494, 8038, 9427 ); test_vehicle_drag( "apc", 1.625400, 8.538354, 4317.323661, 8047, 9436 ); - test_vehicle_drag( "humvee", 0.616297, 7.288356, 4913.700893, 12935, 15175 ); - test_vehicle_drag( "military_cargo_truck", 0.840757, 9.539216, 4401.797222, 11550, 13577 ); - test_vehicle_drag( "flatbed_truck", 0.776902, 4.698776, 2103.549432, 10156, 12219 ); - test_vehicle_drag( "pickup", 0.589208, 3.287708, 1329.914286, 11283, 13535 ); + test_vehicle_drag( "humvee", 0.616298, 7.938499, 5352.017857, 12830, 15073 ); + test_vehicle_drag( "military_cargo_truck", 0.840758, 10.682064, 4929.155556, 11411, 13443 ); + test_vehicle_drag( "flatbed_truck", 0.776902, 4.556718, 2039.952841, 10177, 12239 ); + test_vehicle_drag( "pickup", 0.589208, 2.852032, 1153.678571, 11367, 13615 ); test_vehicle_drag( "semi_truck", 0.792020, 10.185646, 5885.327500, 11661, 13732 ); - test_vehicle_drag( "truck_trailer", 1.196475, 13.367675, 5924.600000, 0, 0 ); - test_vehicle_drag( "tatra_truck", 0.968467, 8.492594, 4538.781977, 14044, 16394 ); - test_vehicle_drag( "animalctrl", 0.396191, 2.829350, 1144.503571, 12832, 15400 ); + test_vehicle_drag( "truck_trailer", 1.196475, 13.127154, 5818.000000, 0, 0 ); + test_vehicle_drag( "tatra_truck", 0.968467, 8.434611, 4507.793605, 14050, 16400 ); + test_vehicle_drag( "animalctrl", 0.396191, 2.611512, 1056.385714, 12891, 15457 ); test_vehicle_drag( "autosweeper", 0.986850, 1.844396, 1305.637500, 6884, 7144 ); - test_vehicle_drag( "excavator", 0.659728, 1.793523, 1269.625000, 13204, 15305 ); - test_vehicle_drag( "road_roller", 1.823738, 2.768976, 9199.604167, 9430, 10928 ); + test_vehicle_drag( "excavator", 0.659728, 2.439720, 1727.064062, 13095, 15200 ); + test_vehicle_drag( "road_roller", 1.823738, 2.768224, 9197.104167, 9430, 10928 ); test_vehicle_drag( "forklift", 0.565988, 1.510686, 712.937500, 8258, 8572 ); - test_vehicle_drag( "trencher", 0.520838, 1.173965, 1334.115865, 8559, 8881 ); - test_vehicle_drag( "armored_car", 0.844628, 7.041709, 4747.415179, 11845, 13859 ); - test_vehicle_drag( "cube_van", 0.518580, 2.748191, 2255.569792, 11843, 14188 ); - test_vehicle_drag( "cube_van_cheap", 0.518580, 2.684732, 2203.486458, 10016, 12030 ); - test_vehicle_drag( "hippie_van", 0.375874, 2.698624, 1091.623214, 11022, 13267 ); - test_vehicle_drag( "icecream_truck", 0.681673, 3.271602, 2225.536486, 10796, 12939 ); - test_vehicle_drag( "lux_rv", 1.630929, 4.105427, 2317.282456, 8390, 9758 ); + test_vehicle_drag( "trencher", 0.520838, 1.545528, 1756.367308, 8466, 8788 ); + test_vehicle_drag( "armored_car", 0.844627, 7.682788, 5179.621429, 11764, 13781 ); + test_vehicle_drag( "cube_van", 0.518580, 2.707603, 2222.257292, 11852, 14196 ); + test_vehicle_drag( "cube_van_cheap", 0.512775, 2.643764, 1905.244207, 10060, 12081 ); + test_vehicle_drag( "hippie_van", 0.375874, 2.480786, 1003.505357, 11086, 13328 ); + test_vehicle_drag( "icecream_truck", 0.681673, 2.971394, 2021.317399, 10847, 12988 ); + test_vehicle_drag( "lux_rv", 1.630929, 3.669265, 2071.093531, 8426, 9792 ); test_vehicle_drag( "meth_lab", 0.499230, 4.185080, 2538.355452, 11668, 14057 ); - test_vehicle_drag( "rv", 0.541800, 3.107252, 2127.031915, 11611, 13925 ); - test_vehicle_drag( "schoolbus", 0.445050, 4.731163, 2142.590241, 12300, 14425 ); - test_vehicle_drag( "security_van", 0.541800, 8.004081, 6569.327083, 11003, 13010 ); - test_vehicle_drag( "wienermobile", 1.281891, 2.613527, 2145.044792, 10576, 12602 ); + test_vehicle_drag( "rv", 0.541800, 2.939497, 2012.197473, 11645, 13958 ); + test_vehicle_drag( "schoolbus", 0.445050, 3.727552, 1688.087610, 12535, 14652 ); + test_vehicle_drag( "security_van", 0.541800, 8.623353, 7077.592708, 10891, 12901 ); + test_vehicle_drag( "wienermobile", 1.281891, 2.237757, 1836.632292, 10612, 12636 ); test_vehicle_drag( "canoe", 0.609525, 7.741047, 2.191938, 298, 628 ); test_vehicle_drag( "kayak", 0.609525, 2.935863, 1.108417, 710, 1314 ); test_vehicle_drag( "kayak_racing", 0.609525, 2.604776, 0.983417, 779, 1406 ); - test_vehicle_drag( "DUKW", 0.776902, 3.937748, 85.169941, 10270, 12326 ); + test_vehicle_drag( "DUKW", 0.776903, 3.602238, 77.913176, 10321, 12374 ); test_vehicle_drag( "raft", 0.997815, 9.743243, 5.517750, 239, 508 ); test_vehicle_drag( "inflatable_boat", 0.469560, 3.616690, 2.048188, 602, 1173 ); } diff --git a/tests/vehicle_efficiency_test.cpp b/tests/vehicle_efficiency_test.cpp index 4eec7efb70e6f..10943a343f4d2 100644 --- a/tests/vehicle_efficiency_test.cpp +++ b/tests/vehicle_efficiency_test.cpp @@ -11,7 +11,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "enums.h" #include "item.h" @@ -433,41 +433,41 @@ TEST_CASE( "make_vehicle_efficiency_case", "[.]" ) // Fix test for electric vehicles TEST_CASE( "vehicle_efficiency", "[vehicle] [engine]" ) { - test_vehicle( "beetle", 816469, 431300, 338700, 95610, 68060 ); - test_vehicle( "car", 1120618, 617500, 388600, 52730, 25170 ); - test_vehicle( "car_sports", 1155014, 352600, 267600, 36820, 22360 ); - test_vehicle( "electric_car", 1047135, 355300, 201600, 22400, 10780 ); - test_vehicle( "suv", 1320286, 1163000, 630000, 85540, 31840 ); + test_vehicle( "beetle", 717777, 444000, 386700, 111500, 91570 ); + test_vehicle( "car", 1021926, 644100, 436900, 59860, 30240 ); + test_vehicle( "car_sports", 1056322, 354700, 288200, 39690, 27210 ); + test_vehicle( "electric_car", 948443, 372500, 231100, 25020, 14280 ); + test_vehicle( "suv", 1221594, 1210000, 695900, 93430, 37220 ); test_vehicle( "motorcycle", 162585, 120300, 100900, 63320, 51130 ); test_vehicle( "quad_bike", 264845, 116100, 116100, 46770, 46770 ); test_vehicle( "scooter", 55441, 235900, 235900, 174700, 174700 ); - test_vehicle( "superbike", 241585, 109800, 65300, 41990, 24140 ); - test_vehicle( "ambulance", 1850228, 623000, 511100, 77700, 57910 ); - test_vehicle( "fire_engine", 2606611, 1895000, 1585000, 337800, 261900 ); - test_vehicle( "fire_truck", 6441903, 420800, 80000, 19080, 4063 ); - test_vehicle( "truck_swat", 5994144, 682900, 130200, 29610, 7604 ); + test_vehicle( "superbike", 241585, 109800, 65300, 42000, 24140 ); + test_vehicle( "ambulance", 1726863, 623000, 538800, 83190, 69710 ); + test_vehicle( "fire_engine", 2483246, 1912000, 1665000, 355000, 295200 ); + test_vehicle( "fire_truck", 6195173, 428300, 92250, 19710, 4700 ); + test_vehicle( "truck_swat", 6482079, 649500, 97380, 28180, 6083 ); test_vehicle( "tractor_plow", 723658, 681200, 681200, 132700, 132700 ); - test_vehicle( "apc", 5802483, 1626000, 1119000, 130800, 85590 ); - test_vehicle( "humvee", 5503345, 767900, 306900, 25620, 9171 ); - test_vehicle( "road_roller", 8831620, 602500, 147100, 22760, 6925 ); + test_vehicle( "apc", 5900070, 1607000, 1086000, 125600, 80390 ); + test_vehicle( "humvee", 5991280, 739500, 296600, 23790, 7337 ); + test_vehicle( "road_roller", 8658909, 584800, 156400, 22760, 6925 ); test_vehicle( "golf_cart", 444630, 96000, 69390, 35490, 14200 ); // in reverse - test_vehicle( "beetle", 816469, 58970, 58870, 44560, 43060, 0, 0, true ); - test_vehicle( "car", 1120618, 76060, 76060, 44230, 24920, 0, 0, true ); - test_vehicle( "car_sports", 1155014, 353200, 268000, 35220, 19540, 0, 0, true ); - test_vehicle( "electric_car", 1047135, 356400, 202300, 22450, 10810, 0, 0, true ); - test_vehicle( "suv", 1320286, 112000, 111700, 66880, 31640, 0, 0, true ); + test_vehicle( "beetle", 717777, 58800, 58800, 45900, 44560, 0, 0, true ); + test_vehicle( "car", 1021926, 76390, 76260, 48330, 30270, 0, 0, true ); + test_vehicle( "car_sports", 1056322, 355300, 288600, 38670, 24580, 0, 0, true ); + test_vehicle( "electric_car", 948443, 373700, 231800, 25070, 14310, 0, 0, true ); + test_vehicle( "suv", 1221594, 114900, 112400, 70400, 35200, 0, 0, true ); test_vehicle( "motorcycle", 162585, 20070, 19030, 15490, 14890, 0, 0, true ); test_vehicle( "quad_bike", 264845, 19650, 19650, 15440, 15440, 0, 0, true ); test_vehicle( "scooter", 55441, 58790, 58790, 46320, 46320, 0, 0, true ); test_vehicle( "superbike", 241585, 18380, 10570, 13100, 8497, 0, 0, true ); - test_vehicle( "ambulance", 1850228, 58460, 57740, 42480, 39100, 0, 0, true ); - test_vehicle( "fire_engine", 2606611, 258000, 257800, 185600, 179400, 0, 0, true ); - test_vehicle( "fire_truck", 6441903, 58760, 59170, 18580, 3447, 0, 0, true ); - test_vehicle( "truck_swat", 5994144, 129300, 130100, 29350, 7668, 0, 0, true ); + test_vehicle( "ambulance", 1726863, 58600, 57910, 42480, 40260, 0, 0, true ); + test_vehicle( "fire_engine", 2483246, 257400, 257100, 185600, 179400, 0, 0, true ); + test_vehicle( "fire_truck", 6195173, 58700, 58860, 19630, 4471, 0, 0, true ); + test_vehicle( "truck_swat", 6482079, 129900, 130900, 26920, 5308, 0, 0, true ); test_vehicle( "tractor_plow", 723658, 72490, 72490, 53700, 53700, 0, 0, true ); - test_vehicle( "apc", 5802483, 381500, 382100, 123600, 82000, 0, 0, true ); - test_vehicle( "humvee", 5503345, 89940, 89940, 25780, 9086, 0, 0, true ); - test_vehicle( "road_roller", 8831620, 97490, 97690, 22880, 6606, 0, 0, true ); + test_vehicle( "apc", 5900070, 382000, 382600, 123100, 82750, 0, 0, true ); + test_vehicle( "humvee", 5991280, 89490, 89490, 24180, 7595, 0, 0, true ); + test_vehicle( "road_roller", 8658909, 97090, 97190, 22880, 6545, 0, 0, true ); test_vehicle( "golf_cart", 444630, 96150, 28800, 35560, 11150, 0, 0, true ); } diff --git a/tests/vehicle_interact_test.cpp b/tests/vehicle_interact_test.cpp index 8e831385e3b6c..9830483ad66cd 100644 --- a/tests/vehicle_interact_test.cpp +++ b/tests/vehicle_interact_test.cpp @@ -4,7 +4,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "inventory.h" #include "item.h" diff --git a/tests/vehicle_part_test.cpp b/tests/vehicle_part_test.cpp index 1cc0113dcd578..4f8a4b8964982 100644 --- a/tests/vehicle_part_test.cpp +++ b/tests/vehicle_part_test.cpp @@ -9,7 +9,7 @@ #include "activity_type.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "damage.h" #include "game.h" @@ -31,8 +31,8 @@ #include "vpart_position.h" #include "vpart_range.h" -static time_point midnight = calendar::turn_zero + 0_hours; -static time_point midday = calendar::turn_zero + 12_hours; +static time_point midnight = calendar::turn_zero; +static time_point midday = midnight + 12_hours; static void set_time( const time_point &time ) { @@ -196,19 +196,19 @@ TEST_CASE( "craft_available_via_vehicle_rig", "[vehicle][vehicle_craft]" ) } SECTION( "cook oatmeal without battery" ) { std::vector items; - items.push_back( item( itype_id( "oatmeal" ) ) ); + items.emplace_back( itype_id( "oatmeal" ) ); test_craft_via_rig( items, 0, 0, 1, 1, recipe_id( "oatmeal_cooked" ).obj(), false ); } SECTION( "cook oatmeal without water" ) { std::vector items; - items.push_back( item( itype_id( "oatmeal" ) ) ); + items.emplace_back( itype_id( "oatmeal" ) ); test_craft_via_rig( items, 2, 2, 0, 0, recipe_id( "oatmeal_cooked" ).obj(), false ); } SECTION( "cook oatmeal successfully" ) { std::vector items; - items.push_back( item( itype_id( "oatmeal" ) ) ); + items.emplace_back( itype_id( "oatmeal" ) ); test_craft_via_rig( items, 2, 0, 1, 0, recipe_id( "oatmeal_cooked" ).obj(), true ); } diff --git a/tests/vehicle_power_test.cpp b/tests/vehicle_power_test.cpp index 33b9aa9f0ade5..027ec23060543 100644 --- a/tests/vehicle_power_test.cpp +++ b/tests/vehicle_power_test.cpp @@ -2,7 +2,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "map.h" #include "map_helpers.h" diff --git a/tests/vehicle_ramp_test.cpp b/tests/vehicle_ramp_test.cpp index b359e184444e1..b34a5983627a8 100644 --- a/tests/vehicle_ramp_test.cpp +++ b/tests/vehicle_ramp_test.cpp @@ -7,7 +7,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "game.h" #include "game_constants.h" diff --git a/tests/vehicle_split_test.cpp b/tests/vehicle_split_test.cpp index b890ec7565ddf..d9a343c70aeff 100644 --- a/tests/vehicle_split_test.cpp +++ b/tests/vehicle_split_test.cpp @@ -1,7 +1,7 @@ #include #include -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "map.h" #include "point.h" diff --git a/tests/vehicle_test.cpp b/tests/vehicle_test.cpp index 6428df91d9242..8a2d0374e8990 100644 --- a/tests/vehicle_test.cpp +++ b/tests/vehicle_test.cpp @@ -1,7 +1,7 @@ #include #include "avatar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "damage.h" #include "enums.h" diff --git a/tests/vehicle_turrets_test.cpp b/tests/vehicle_turrets_test.cpp index 37a42c74b6abc..5c9d76b5527e1 100644 --- a/tests/vehicle_turrets_test.cpp +++ b/tests/vehicle_turrets_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include #include diff --git a/tests/vision_test.cpp b/tests/vision_test.cpp index 94c7bff2a11a2..4557c9864a6e6 100644 --- a/tests/vision_test.cpp +++ b/tests/vision_test.cpp @@ -8,7 +8,7 @@ #include "cached_options.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "game.h" #include "item.h" diff --git a/tests/visitable_remove_test.cpp b/tests/visitable_remove_test.cpp index b8eae0f871eda..15544be8a2c4f 100644 --- a/tests/visitable_remove_test.cpp +++ b/tests/visitable_remove_test.cpp @@ -7,7 +7,7 @@ #include "calendar.h" #include "cata_utility.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "character.h" #include "inventory.h" #include "item.h" @@ -37,6 +37,7 @@ static int count_items( const T &src, const itype_id &id ) return n; } +// NOLINTNEXTLINE(readability-function-size) TEST_CASE( "visitable_remove", "[visitable]" ) { const itype_id liquid_id( "water" ); @@ -49,7 +50,7 @@ TEST_CASE( "visitable_remove", "[visitable]" ) Character &p = get_player_character(); p.worn.clear(); - p.worn.push_back( item( "backpack" ) ); + p.worn.emplace_back( "backpack" ); p.inv->clear(); p.remove_weapon(); p.wear_item( item( "backpack" ) ); // so we don't drop anything diff --git a/tests/visitable_test.cpp b/tests/visitable_test.cpp index 0bcfd78eee6d0..f69aa2f742f4f 100644 --- a/tests/visitable_test.cpp +++ b/tests/visitable_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include "calendar.h" #include "inventory.h" diff --git a/tests/weary_test.cpp b/tests/weary_test.cpp index 29824ccaad7ae..f804b5bd540c6 100644 --- a/tests/weary_test.cpp +++ b/tests/weary_test.cpp @@ -2,7 +2,7 @@ #include "activity_scheduling_helper.h" #include "avatar.h" #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "player_helpers.h" #include "point.h" #include "type_id.h" diff --git a/tests/weather_test.cpp b/tests/weather_test.cpp index 3b2b4bc3d28dd..2f6148594aaea 100644 --- a/tests/weather_test.cpp +++ b/tests/weather_test.cpp @@ -4,7 +4,7 @@ #include #include "calendar.h" -#include "catch/catch.hpp" +#include "cata_catch.h" #include "options_helpers.h" #include "point.h" #include "type_id.h" @@ -72,10 +72,10 @@ TEST_CASE( "weather realism" ) int minute = to_minutes( time_past_midnight( i ) ); temperature[day][minute] = w.temperature; int hour = to_hours( time_past_new_year( i ) ); - std::map next_instance_allowed; + *get_weather().weather_precise = w; hourly_precip[hour] += precip_mm_per_hour( - wgen.get_weather_conditions( w, next_instance_allowed )->precip ) + wgen.get_weather_conditions( w )->precip ) / 60; } diff --git a/tests/wield_times_test.cpp b/tests/wield_times_test.cpp index b304f25afe798..77f8ef8586743 100644 --- a/tests/wield_times_test.cpp +++ b/tests/wield_times_test.cpp @@ -1,4 +1,4 @@ -#include "catch/catch.hpp" +#include "cata_catch.h" #include diff --git a/tools/clang-tidy-plugin/AlmostNeverAutoCheck.h b/tools/clang-tidy-plugin/AlmostNeverAutoCheck.h index 8d1500eb9a4c3..9917d55e71fdd 100644 --- a/tools/clang-tidy-plugin/AlmostNeverAutoCheck.h +++ b/tools/clang-tidy-plugin/AlmostNeverAutoCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/AssertCheck.cpp b/tools/clang-tidy-plugin/AssertCheck.cpp index d09d2bf926384..8f7e89d3013a6 100644 --- a/tools/clang-tidy-plugin/AssertCheck.cpp +++ b/tools/clang-tidy-plugin/AssertCheck.cpp @@ -3,6 +3,9 @@ #include #include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/Token.h" using namespace clang::ast_matchers; @@ -46,10 +49,9 @@ class AssertMacroCallbacks : public PPCallbacks llvm::SmallPtrSet CataAssertLocations; }; -void AssertCheck::registerPPCallbacks( CompilerInstance &Compiler ) +void AssertCheck::registerPPCallbacks( const SourceManager &, Preprocessor *PP, Preprocessor * ) { - Compiler.getPreprocessor().addPPCallbacks( - llvm::make_unique( &Compiler.getPreprocessor(), this ) ); + PP->addPPCallbacks( std::make_unique( PP, this ) ); } void AssertCheck::registerMatchers( MatchFinder * /*Finder*/ ) diff --git a/tools/clang-tidy-plugin/AssertCheck.h b/tools/clang-tidy-plugin/AssertCheck.h index 4e0d1d253c156..82206016831a0 100644 --- a/tools/clang-tidy-plugin/AssertCheck.h +++ b/tools/clang-tidy-plugin/AssertCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { @@ -21,7 +22,7 @@ class AssertCheck : public ClangTidyCheck public: AssertCheck( StringRef Name, ClangTidyContext *Context ) : ClangTidyCheck( Name, Context ) {} - void registerPPCallbacks( CompilerInstance &Compiler ) override; + void registerPPCallbacks( const SourceManager &, Preprocessor *, Preprocessor * ) override; void registerMatchers( ast_matchers::MatchFinder *Finder ) override; void check( const ast_matchers::MatchFinder::MatchResult &Result ) override; }; diff --git a/tools/clang-tidy-plugin/CMakeLists.txt b/tools/clang-tidy-plugin/CMakeLists.txt index 4b493eeab6c5c..900ee496b43f1 100644 --- a/tools/clang-tidy-plugin/CMakeLists.txt +++ b/tools/clang-tidy-plugin/CMakeLists.txt @@ -17,12 +17,15 @@ add_library(CataAnalyzerPlugin MODULE PointInitializationCheck.cpp SimplifyPointConstructorsCheck.cpp StaticDeclarationsCheck.cpp + StaticIntIdConstantsCheck.cpp StaticStringIdConstantsCheck.cpp StringLiteralIterator.cpp TestFilenameCheck.cpp TestsMustRestoreGlobalStateCheck.cpp TextStyleCheck.cpp TranslatorCommentsCheck.cpp + UnsequencedCallsCheck.cpp + UnusedStaticsCheck.cpp UseLocalizedSortingCheck.cpp UseNamedPointConstantsCheck.cpp UsePointApisCheck.cpp @@ -35,12 +38,12 @@ target_include_directories(CataAnalyzerPlugin SYSTEM PRIVATE if ("${CATA_CLANG_TIDY_INCLUDE_DIR}" STREQUAL "") set(CTPS_RELEASES https://github.com/jbytheway/clang-tidy-plugin-support/releases/download) - set(CTPS_VERSION llvm-8.0.1-r12) + set(CTPS_VERSION llvm-12.0.0-r3) set(CTPS_SRC ${CMAKE_CURRENT_BINARY_DIR}/clang-tidy-plugin-support) ExternalProject_Add(clang-tidy-plugin-support URL ${CTPS_RELEASES}/${CTPS_VERSION}/clang-tidy-plugin-support-${CTPS_VERSION}.tar.xz - URL_HASH SHA256=00ffab0df11250f394830735514c62ae787bd2eb6eb9d5e97471206d270c54e2 + URL_HASH SHA256=c84aaf35b4d5bb9130b1cc309a92146ae5c62fd69db30a6254725beac9d60990 SOURCE_DIR ${CTPS_SRC} CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/tools/clang-tidy-plugin/CataTidyModule.cpp b/tools/clang-tidy-plugin/CataTidyModule.cpp index 716aa0b509023..76e4a05efd101 100644 --- a/tools/clang-tidy-plugin/CataTidyModule.cpp +++ b/tools/clang-tidy-plugin/CataTidyModule.cpp @@ -1,3 +1,4 @@ +#include #include #include "AlmostNeverAutoCheck.h" @@ -13,11 +14,14 @@ #include "PointInitializationCheck.h" #include "SimplifyPointConstructorsCheck.h" #include "StaticDeclarationsCheck.h" +#include "StaticIntIdConstantsCheck.h" #include "StaticStringIdConstantsCheck.h" #include "TestFilenameCheck.h" #include "TestsMustRestoreGlobalStateCheck.h" #include "TextStyleCheck.h" #include "TranslatorCommentsCheck.h" +#include "UnsequencedCallsCheck.h" +#include "UnusedStaticsCheck.h" #include "UseLocalizedSortingCheck.h" #include "UseNamedPointConstantsCheck.h" #include "UsePointApisCheck.h" @@ -35,6 +39,16 @@ class CataModule : public ClangTidyModule { public: void addCheckFactories( ClangTidyCheckFactories &CheckFactories ) override { + // Sanity check the clang version to verify that we're loaded into + // the same version we linked against + + std::string RuntimeVersion = getClangFullVersion(); + if( !StringRef( RuntimeVersion ).contains( "clang version " CLANG_VERSION_STRING ) ) { + llvm::report_fatal_error( + Twine( "clang version mismatch in CataTidyModule. Compiled against " + CLANG_VERSION_STRING " but loaded by ", RuntimeVersion ) ); + abort(); + } CheckFactories.registerCheck( "cata-almost-never-auto" ); CheckFactories.registerCheck( "cata-assert" ); CheckFactories.registerCheck( @@ -48,6 +62,8 @@ class CataModule : public ClangTidyModule CheckFactories.registerCheck( "cata-simplify-point-constructors" ); CheckFactories.registerCheck( "cata-static-declarations" ); + CheckFactories.registerCheck( + "cata-static-int_id-constants" ); CheckFactories.registerCheck( "cata-static-string_id-constants" ); CheckFactories.registerCheck( "cata-test-filename" ); @@ -55,6 +71,8 @@ class CataModule : public ClangTidyModule "cata-tests-must-restore-global-state" ); CheckFactories.registerCheck( "cata-text-style" ); CheckFactories.registerCheck( "cata-translator-comments" ); + CheckFactories.registerCheck( "cata-unsequenced-calls" ); + CheckFactories.registerCheck( "cata-unused-statics" ); CheckFactories.registerCheck( "cata-use-localized-sorting" ); CheckFactories.registerCheck( "cata-use-named-point-constants" ); @@ -67,6 +85,7 @@ class CataModule : public ClangTidyModule } // namespace cata // Register the MiscTidyModule using this statically initialized variable. +// NOLINTNEXTLINE(cata-unused-statics) static ClangTidyModuleRegistry::Add X( "cata-module", "Adds Cataclysm-DDA checks." ); diff --git a/tools/clang-tidy-plugin/CombineLocalsIntoPointCheck.cpp b/tools/clang-tidy-plugin/CombineLocalsIntoPointCheck.cpp index 73c9e77614b72..4e743d62bba4f 100644 --- a/tools/clang-tidy-plugin/CombineLocalsIntoPointCheck.cpp +++ b/tools/clang-tidy-plugin/CombineLocalsIntoPointCheck.cpp @@ -52,7 +52,7 @@ static bool nameExistsInContext( const DeclContext *Context, const std::string & { for( const Decl *D : Context->decls() ) { if( const NamedDecl *ND = dyn_cast( D ) ) { - if( ND->getName() == Name ) { + if( ND->getIdentifier() && ND->getName() == Name ) { return true; } } @@ -86,11 +86,6 @@ static void CheckDecl( CombineLocalsIntoPointCheck &Check, const MatchFinder::Ma return; } - // Only one modification per function - if( !Check.alteredFunctions.insert( getContainingFunction( Result, XDecl ) ).second ) { - return; - } - const Decl *NextDecl = XDecl->getNextDeclInContext(); const VarDecl *YDecl = dyn_cast_or_null( NextDecl ); if( !YDecl ) { @@ -135,6 +130,20 @@ static void CheckDecl( CombineLocalsIntoPointCheck &Check, const MatchFinder::Ma return; } + // Only one modification per function + const FunctionDecl *Func = getContainingFunction( Result, XDecl ); + + if( !Func ) { + return; + } + + auto FuncInsert = Check.alteredFunctions.emplace( + Func, FunctionReplacementData{ XDecl, YDecl, ZDecl, {}, {} } ); + if( !FuncInsert.second ) { + return; + } + FunctionReplacementData &FuncReplacement = FuncInsert.first->second; + if( ZDecl ) { const Stmt *ZGrandparentStmt = GetGrandparent( ZDecl ); @@ -144,8 +153,8 @@ static void CheckDecl( CombineLocalsIntoPointCheck &Check, const MatchFinder::Ma } const Expr *XInit = XDecl->getAnyInitializer(); - const Expr *YInit = YDecl->getInit(); - const Expr *ZInit = ZDecl ? ZDecl->getInit() : nullptr; + const Expr *YInit = YDecl->getAnyInitializer(); + const Expr *ZInit = ZDecl ? ZDecl->getAnyInitializer() : nullptr; if( !XInit || !YInit || ( ZDecl && !ZInit ) ) { return; @@ -213,16 +222,7 @@ static void CheckDecl( CombineLocalsIntoPointCheck &Check, const MatchFinder::Ma SourceLocation EndLoc = ZDecl ? ZDecl->getEndLoc() : YDecl->getEndLoc(); SourceRange RangeToReplace( XDecl->getBeginLoc(), EndLoc ); - std::string Message = ZDecl ? - "Variables %0, %1, and %2 could be combined into a single 'tripoint' variable." : - "Variables %0 and %1 could be combined into a single 'point' variable."; - Check.diag( XDecl->getBeginLoc(), Message ) - << XDecl << YDecl << ZDecl - << FixItHint::CreateReplacement( RangeToReplace, Replacement ); - Check.diag( YDecl->getLocation(), "%0 variable", DiagnosticIDs::Note ) << YDecl; - if( ZDecl ) { - Check.diag( ZDecl->getLocation(), "%0 variable", DiagnosticIDs::Note ) << ZDecl; - } + FuncReplacement.Fixits.push_back( FixItHint::CreateReplacement( RangeToReplace, Replacement ) ); } static void CheckDeclRef( CombineLocalsIntoPointCheck &Check, @@ -234,8 +234,8 @@ static void CheckDeclRef( CombineLocalsIntoPointCheck &Check, return; } - auto Replacement = Check.usageReplacements.find( Decl ); - if( Replacement == Check.usageReplacements.end() ) { + auto NewName = Check.usageReplacements.find( Decl ); + if( NewName == Check.usageReplacements.end() ) { return; } @@ -243,9 +243,17 @@ static void CheckDeclRef( CombineLocalsIntoPointCheck &Check, return; } - Check.diag( DeclRef->getBeginLoc(), "Update %0 to '%1'.", DiagnosticIDs::Note ) - << Decl << Replacement->second - << FixItHint::CreateReplacement( DeclRef->getSourceRange(), Replacement->second ); + auto Func = Check.alteredFunctions.find( getContainingFunction( Result, Decl ) ); + if( Func == Check.alteredFunctions.end() ) { + Check.diag( DeclRef->getBeginLoc(), "Internal check error finding replacement data for ref", + DiagnosticIDs::Error ); + return; + } + FunctionReplacementData &Replacement = Func->second; + Replacement.RefReplacements.push_back( + DeclRefReplacementData{ DeclRef, Decl, NewName->second } ); + Replacement.Fixits.push_back( + FixItHint::CreateReplacement( DeclRef->getSourceRange(), NewName->second ) ); } void CombineLocalsIntoPointCheck::check( const MatchFinder::MatchResult &Result ) @@ -254,6 +262,34 @@ void CombineLocalsIntoPointCheck::check( const MatchFinder::MatchResult &Result CheckDeclRef( *this, Result ); } +void CombineLocalsIntoPointCheck::onEndOfTranslationUnit() +{ + for( const std::pair &p : + alteredFunctions ) { + const FunctionReplacementData &data = p.second; + std::string Message = data.ZDecl ? + "Variables %0, %1, and %2 could be combined into a single 'tripoint' variable." : + "Variables %0 and %1 could be combined into a single 'point' variable."; + { + DiagnosticBuilder Diag = diag( data.XDecl->getBeginLoc(), Message ) + << data.XDecl << data.YDecl << data.ZDecl; + for( const FixItHint &FixIt : data.Fixits ) { + Diag << FixIt; + } + } + diag( data.YDecl->getLocation(), "%0 variable", DiagnosticIDs::Note ) << data.YDecl; + if( data.ZDecl ) { + diag( data.ZDecl->getLocation(), "%0 variable", DiagnosticIDs::Note ) + << data.ZDecl; + } + for( const DeclRefReplacementData &RefReplacment : data.RefReplacements ) { + diag( RefReplacment.DeclRef->getBeginLoc(), "Update %0 to '%1'.", + DiagnosticIDs::Note ) + << RefReplacment.Decl << RefReplacment.NewName; + } + } +} + } // namespace cata } // namespace tidy } // namespace clang diff --git a/tools/clang-tidy-plugin/CombineLocalsIntoPointCheck.h b/tools/clang-tidy-plugin/CombineLocalsIntoPointCheck.h index 7d489e54f4f49..ca0bd13134fc3 100644 --- a/tools/clang-tidy-plugin/CombineLocalsIntoPointCheck.h +++ b/tools/clang-tidy-plugin/CombineLocalsIntoPointCheck.h @@ -7,6 +7,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { @@ -18,6 +19,20 @@ class ClangTidyContext; namespace cata { +struct DeclRefReplacementData { + const DeclRefExpr *DeclRef; + const VarDecl *Decl; + std::string NewName; +}; + +struct FunctionReplacementData { + const VarDecl *XDecl; + const VarDecl *YDecl; + const VarDecl *ZDecl; + std::vector RefReplacements; + std::vector Fixits; +}; + class CombineLocalsIntoPointCheck : public ClangTidyCheck { public: @@ -25,10 +40,11 @@ class CombineLocalsIntoPointCheck : public ClangTidyCheck : ClangTidyCheck( Name, Context ) {} void registerMatchers( ast_matchers::MatchFinder *Finder ) override; void check( const ast_matchers::MatchFinder::MatchResult &Result ) override; + void onEndOfTranslationUnit() override; using ClangTidyCheck::getLangOpts; std::unordered_map usageReplacements; - std::unordered_set alteredFunctions; + std::unordered_map alteredFunctions; }; } // namespace cata diff --git a/tools/clang-tidy-plugin/DeterminismCheck.h b/tools/clang-tidy-plugin/DeterminismCheck.h index d848042ea0c14..baa5c014380b8 100644 --- a/tools/clang-tidy-plugin/DeterminismCheck.h +++ b/tools/clang-tidy-plugin/DeterminismCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/HeaderGuardCheck.cpp b/tools/clang-tidy-plugin/HeaderGuardCheck.cpp index e51841b191fbe..a733fa25e3441 100644 --- a/tools/clang-tidy-plugin/HeaderGuardCheck.cpp +++ b/tools/clang-tidy-plugin/HeaderGuardCheck.cpp @@ -3,12 +3,15 @@ #include #include +#include #if !defined(_MSC_VER) #include #include #endif +#include "Utils.h" + namespace clang { namespace tidy @@ -25,7 +28,7 @@ static std::string cleanPath( StringRef Path ) { SmallString<256> Result = Path; llvm::sys::path::remove_dots( Result, true ); - return Result.str(); + return Result.str().str(); } static bool pathExists( const std::string &path ) @@ -265,7 +268,7 @@ class HeaderGuardPPCallbacks : public PPCallbacks std::string CPPVar = getHeaderGuard( FileName ); if( CPPVar.empty() ) { - return CurHeaderGuard; + return CurHeaderGuard.str(); } if( Ifndef.isValid() && CurHeaderGuard != CPPVar ) { @@ -279,7 +282,7 @@ class HeaderGuardPPCallbacks : public PPCallbacks CPPVar ) ); return CPPVar; } - return CurHeaderGuard; + return CurHeaderGuard.str(); } /// \brief Checks the comment after the #endif of a header guard and fixes it @@ -360,12 +363,13 @@ class HeaderGuardPPCallbacks : public PPCallbacks Newlines = "\n"; } + std::string ToInsertHeader = StrCat( + "#ifndef ", CPPVar, "\n#define ", CPPVar, Newlines ); Check->diag( InsertLoc, "Header is missing header guard." ) - << FixItHint::CreateInsertion( - InsertLoc, "#ifndef " + CPPVar + "\n#define " + CPPVar + Newlines ) + << FixItHint::CreateInsertion( InsertLoc, ToInsertHeader ) << FixItHint::CreateInsertion( SM.getLocForEndOfFile( FID ), - "\n#" + formatEndIf( CPPVar ) + "\n" ); + StrCat( "\n#", formatEndIf( CPPVar ), "\n" ) ); } } private: @@ -376,11 +380,10 @@ class HeaderGuardPPCallbacks : public PPCallbacks CataHeaderGuardCheck *Check; }; -void CataHeaderGuardCheck::registerPPCallbacks( CompilerInstance &Compiler ) +void CataHeaderGuardCheck::registerPPCallbacks( + const SourceManager &, Preprocessor *PP, Preprocessor * ) { - Compiler.getPreprocessor().addPPCallbacks( - llvm::make_unique( &Compiler.getPreprocessor(), - this ) ); + PP->addPPCallbacks( std::make_unique( PP, this ) ); } } // namespace cata diff --git a/tools/clang-tidy-plugin/HeaderGuardCheck.h b/tools/clang-tidy-plugin/HeaderGuardCheck.h index d131504f578cd..5092472dfc6ee 100644 --- a/tools/clang-tidy-plugin/HeaderGuardCheck.h +++ b/tools/clang-tidy-plugin/HeaderGuardCheck.h @@ -1,7 +1,8 @@ #ifndef CATA_TOOLS_CLANG_TIDY_PLUGIN_HEADERGUARDCHECK_H #define CATA_TOOLS_CLANG_TIDY_PLUGIN_HEADERGUARDCHECK_H -#include +#include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { @@ -16,7 +17,7 @@ class CataHeaderGuardCheck : public ClangTidyCheck { public: CataHeaderGuardCheck( StringRef Name, ClangTidyContext *Context ); - void registerPPCallbacks( CompilerInstance &Compiler ) override; + void registerPPCallbacks( const SourceManager &, Preprocessor *, Preprocessor * ) override; }; } // namespace cata diff --git a/tools/clang-tidy-plugin/JsonTranslationInputCheck.h b/tools/clang-tidy-plugin/JsonTranslationInputCheck.h index 5a8147556444d..9459d027ab728 100644 --- a/tools/clang-tidy-plugin/JsonTranslationInputCheck.h +++ b/tools/clang-tidy-plugin/JsonTranslationInputCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/NoLongCheck.cpp b/tools/clang-tidy-plugin/NoLongCheck.cpp index e9d1282d00401..4fd84011d924a 100644 --- a/tools/clang-tidy-plugin/NoLongCheck.cpp +++ b/tools/clang-tidy-plugin/NoLongCheck.cpp @@ -55,10 +55,9 @@ class NoLongMacrosCallbacks : public PPCallbacks NoLongCheck *Check; }; -void NoLongCheck::registerPPCallbacks( CompilerInstance &Compiler ) +void NoLongCheck::registerPPCallbacks( const SourceManager &, Preprocessor *PP, Preprocessor * ) { - Compiler.getPreprocessor().addPPCallbacks( - llvm::make_unique( this ) ); + PP->addPPCallbacks( std::make_unique( this ) ); } void NoLongCheck::registerMatchers( MatchFinder *Finder ) @@ -87,6 +86,36 @@ static std::string AlternativesFor( QualType Type ) } } +static bool AnyDeclContextIsSpecialization( const Decl *D ) +{ + Decl::Kind contextKind = D->getDeclContext()->getDeclKind(); + TemplateSpecializationKind tsk = TSK_Undeclared; + const Decl *ContextDecl = nullptr; + if( contextKind == Decl::Function || contextKind == Decl::CXXMethod || + contextKind == Decl::CXXConstructor || contextKind == Decl::CXXConversion || + contextKind == Decl::CXXDestructor || contextKind == Decl::CXXDeductionGuide ) { + const FunctionDecl *C = static_cast( D->getDeclContext() ); + ContextDecl = C; + tsk = C->getTemplateSpecializationKind(); + } + if( contextKind == Decl::CXXRecord || contextKind == Decl::ClassTemplateSpecialization || + contextKind == Decl::ClassTemplatePartialSpecialization ) { + const CXXRecordDecl *C = static_cast( D->getDeclContext() ); + ContextDecl = C; + tsk = C->getTemplateSpecializationKind(); + } + if( !ContextDecl ) { + return false; + } + if( tsk != TSK_Undeclared ) { + // This happens for e.g. a parameter 'T a' to an instantiated + // template function where T is long. We don't want to report such + // cases. + return true; + } + return AnyDeclContextIsSpecialization( ContextDecl ); +} + static void CheckDecl( NoLongCheck &Check, const MatchFinder::MatchResult &Result ) { const ValueDecl *MatchedDecl = Result.Nodes.getNodeAs( "decl" ); @@ -103,19 +132,11 @@ static void CheckDecl( NoLongCheck &Check, const MatchFinder::MatchResult &Resul // generated function return; } - Decl::Kind contextKind = MatchedDecl->getDeclContext()->getDeclKind(); - if( contextKind == Decl::Function || contextKind == Decl::CXXMethod || - contextKind == Decl::CXXConstructor || contextKind == Decl::CXXConversion || - contextKind == Decl::CXXDestructor || contextKind == Decl::CXXDeductionGuide ) { - TemplateSpecializationKind tsk = - static_cast( - MatchedDecl->getDeclContext() )->getTemplateSpecializationKind(); - if( tsk == TSK_ImplicitInstantiation ) { - // This happens for e.g. a parameter 'T a' to an instantiated - // template function where T is long. We don't want to report such - // cases. - return; - } + if( AnyDeclContextIsSpecialization( MatchedDecl ) ) { + // This happens for e.g. a parameter 'T a' to an instantiated + // template function where T is long. We don't want to report such + // cases. + return; } Check.diag( MatchedDecl->getLocation(), "Variable %0 declared as %1. %2." ) << diff --git a/tools/clang-tidy-plugin/NoLongCheck.h b/tools/clang-tidy-plugin/NoLongCheck.h index 58b259abec9ab..6c90eff5e6966 100644 --- a/tools/clang-tidy-plugin/NoLongCheck.h +++ b/tools/clang-tidy-plugin/NoLongCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { @@ -22,7 +23,7 @@ class NoLongCheck : public ClangTidyCheck public: NoLongCheck( StringRef Name, ClangTidyContext *Context ) : ClangTidyCheck( Name, Context ) {} - void registerPPCallbacks( CompilerInstance &Compiler ) override; + void registerPPCallbacks( const SourceManager &, Preprocessor *, Preprocessor * ) override; void registerMatchers( ast_matchers::MatchFinder *Finder ) override; void check( const ast_matchers::MatchFinder::MatchResult &Result ) override; }; diff --git a/tools/clang-tidy-plugin/NoStaticGettextCheck.h b/tools/clang-tidy-plugin/NoStaticGettextCheck.h index 5f4242a2fea44..a37712d3e357c 100644 --- a/tools/clang-tidy-plugin/NoStaticGettextCheck.h +++ b/tools/clang-tidy-plugin/NoStaticGettextCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/PointInitializationCheck.h b/tools/clang-tidy-plugin/PointInitializationCheck.h index a5b899c7c846a..71065f1212cfa 100644 --- a/tools/clang-tidy-plugin/PointInitializationCheck.h +++ b/tools/clang-tidy-plugin/PointInitializationCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/SimplifyPointConstructorsCheck.cpp b/tools/clang-tidy-plugin/SimplifyPointConstructorsCheck.cpp index 3793781215654..79d4c112fa415 100644 --- a/tools/clang-tidy-plugin/SimplifyPointConstructorsCheck.cpp +++ b/tools/clang-tidy-plugin/SimplifyPointConstructorsCheck.cpp @@ -58,6 +58,9 @@ struct ExpressionCategory { ExpressionCategory( const MatchFinder::MatchResult &Result, const Expr *E ) : Replacement( getText( Result, E ) ) { + if( StringRef( Replacement ).endswith( "->" ) ) { + Replacement.erase( Replacement.end() - 2, Replacement.end() ); + } QualType EType = E->getType(); if( EType->isPointerType() ) { IsArrowRef = true; diff --git a/tools/clang-tidy-plugin/SimplifyPointConstructorsCheck.h b/tools/clang-tidy-plugin/SimplifyPointConstructorsCheck.h index 7e16c3ae133f5..bc32972a7e349 100644 --- a/tools/clang-tidy-plugin/SimplifyPointConstructorsCheck.h +++ b/tools/clang-tidy-plugin/SimplifyPointConstructorsCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/StaticDeclarationsCheck.h b/tools/clang-tidy-plugin/StaticDeclarationsCheck.h index 11265013085fa..60d2541d0e2c5 100644 --- a/tools/clang-tidy-plugin/StaticDeclarationsCheck.h +++ b/tools/clang-tidy-plugin/StaticDeclarationsCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/StaticIntIdConstantsCheck.cpp b/tools/clang-tidy-plugin/StaticIntIdConstantsCheck.cpp new file mode 100644 index 0000000000000..c7e54a18e1b57 --- /dev/null +++ b/tools/clang-tidy-plugin/StaticIntIdConstantsCheck.cpp @@ -0,0 +1,87 @@ +#include "StaticIntIdConstantsCheck.h" +#include "Utils.h" +#include + +using namespace clang::ast_matchers; + +namespace clang +{ +namespace tidy +{ +namespace cata +{ + +void StaticIntIdConstantsCheck::registerMatchers( MatchFinder *Finder ) +{ + Finder->addMatcher( + varDecl( + hasType( + namedDecl( + anyOf( + hasName( "int_id" ), + typedefNameDecl( + hasType( + hasCanonicalType( + hasDeclaration( namedDecl( hasName( "int_id" ) ) ) + ) + ) + ) + ) + ).bind( "typeDecl" ) + ) + ).bind( "varDecl" ), + this + ); +} + +static void CheckConstructor( StaticIntIdConstantsCheck &Check, + const MatchFinder::MatchResult &Result ) +{ + const VarDecl *IntIdVarDecl = Result.Nodes.getNodeAs( "varDecl" ); + const TypeDecl *IntIdTypeDecl = Result.Nodes.getNodeAs( "typeDecl" ); + if( !IntIdVarDecl || !IntIdTypeDecl ) { + return; + } + + StringRef VarName = IntIdVarDecl->getName(); + if( VarName.endswith( "null" ) || VarName.endswith( "NULL" ) ) { + // Null constants are OK because they probably don't vary + return; + } + + const VarDecl *PreviousDecl = dyn_cast_or_null( IntIdVarDecl->getPreviousDecl() ); + + if( PreviousDecl ) { + // Only complain about each variable once + return; + } + + std::string Adjective; + + if( IntIdVarDecl->hasGlobalStorage() ) { + Adjective = "Global"; + } + + if( IntIdVarDecl->isStaticDataMember() || IntIdVarDecl->isStaticLocal() ) { + Adjective = "Static"; + } + + if( Adjective.empty() ) { + return; + } + + Check.diag( + IntIdVarDecl->getBeginLoc(), + "%2 declaration of %0 is dangerous because %1 is a specialization of int_id and it " + "will not update automatically when game data changes. Consider switching to a string_id." + ) << IntIdVarDecl << IntIdTypeDecl << Adjective; +} + +void StaticIntIdConstantsCheck::check( const MatchFinder::MatchResult &Result ) +{ + CheckConstructor( *this, Result ); +} + +} // namespace cata +} // namespace tidy +} // namespace clang diff --git a/tools/clang-tidy-plugin/StaticIntIdConstantsCheck.h b/tools/clang-tidy-plugin/StaticIntIdConstantsCheck.h new file mode 100644 index 0000000000000..a8493f3ba2037 --- /dev/null +++ b/tools/clang-tidy-plugin/StaticIntIdConstantsCheck.h @@ -0,0 +1,33 @@ +#ifndef CATA_TOOLS_CLANG_TIDY_PLUGIN_STATICINTIDCONSTANTSCHECK_H +#define CATA_TOOLS_CLANG_TIDY_PLUGIN_STATICINTIDCONSTANTSCHECK_H + +#include +#include + +#include "ClangTidy.h" +#include "ClangTidyCheck.h" + +namespace clang +{ + +namespace tidy +{ +class ClangTidyContext; + +namespace cata +{ + +class StaticIntIdConstantsCheck : public ClangTidyCheck +{ + public: + StaticIntIdConstantsCheck( StringRef Name, ClangTidyContext *Context ) + : ClangTidyCheck( Name, Context ) {} + void registerMatchers( ast_matchers::MatchFinder *Finder ) override; + void check( const ast_matchers::MatchFinder::MatchResult &Result ) override; +}; + +} // namespace cata +} // namespace tidy +} // namespace clang + +#endif // CATA_TOOLS_CLANG_TIDY_PLUGIN_STATICINTIDCONSTANTSCHECK_H diff --git a/tools/clang-tidy-plugin/StaticStringIdConstantsCheck.cpp b/tools/clang-tidy-plugin/StaticStringIdConstantsCheck.cpp index 7249cb0ac137b..a57bb6c145b68 100644 --- a/tools/clang-tidy-plugin/StaticStringIdConstantsCheck.cpp +++ b/tools/clang-tidy-plugin/StaticStringIdConstantsCheck.cpp @@ -68,6 +68,7 @@ static std::string GetPrefixFor( const CXXRecordDecl *Type ) { "mutation_branch", "trait_" }, { "mutation_category_trait", "mutation_category_" }, { "npc_class", "" }, + { "oter_t", "" }, { "quality", "qual_" }, { "Skill", "skill_" }, { "ter_t", "ter_" }, diff --git a/tools/clang-tidy-plugin/StaticStringIdConstantsCheck.h b/tools/clang-tidy-plugin/StaticStringIdConstantsCheck.h index 7a56037f0ace3..a84de5b7495e2 100644 --- a/tools/clang-tidy-plugin/StaticStringIdConstantsCheck.h +++ b/tools/clang-tidy-plugin/StaticStringIdConstantsCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/TestFilenameCheck.cpp b/tools/clang-tidy-plugin/TestFilenameCheck.cpp index 9f739c43732e4..d7b6f9e08dc3f 100644 --- a/tools/clang-tidy-plugin/TestFilenameCheck.cpp +++ b/tools/clang-tidy-plugin/TestFilenameCheck.cpp @@ -29,8 +29,8 @@ namespace cata class TestFilenameCallbacks : public PPCallbacks { public: - TestFilenameCallbacks( TestFilenameCheck *Check, CompilerInstance *Compiler ) : - Check( Check ), Compiler( Compiler ) {} + TestFilenameCallbacks( TestFilenameCheck *Check, const SourceManager *SrcM ) : + Check( Check ), SM( SrcM ) {} void MacroExpands( const Token &MacroNameTok, const MacroDefinition &, @@ -39,8 +39,7 @@ class TestFilenameCallbacks : public PPCallbacks StringRef MacroName = MacroNameTok.getIdentifierInfo()->getName(); if( MacroName == "TEST_CASE" ) { - SourceManager &SM = Compiler->getSourceManager(); - StringRef Filename = SM.getBufferName( Range.getBegin() ); + StringRef Filename = SM->getBufferName( Range.getBegin() ); bool IsTestFilename = Filename.endswith( "_test.cpp" ); if( !IsTestFilename ) { @@ -52,13 +51,13 @@ class TestFilenameCallbacks : public PPCallbacks } private: TestFilenameCheck *Check; - CompilerInstance *Compiler; + const SourceManager *SM; }; -void TestFilenameCheck::registerPPCallbacks( CompilerInstance &Compiler ) +void TestFilenameCheck::registerPPCallbacks( const SourceManager &SM, Preprocessor *PP, + Preprocessor * ) { - Compiler.getPreprocessor().addPPCallbacks( - llvm::make_unique( this, &Compiler ) ); + PP->addPPCallbacks( std::make_unique( this, &SM ) ); } } // namespace cata diff --git a/tools/clang-tidy-plugin/TestFilenameCheck.h b/tools/clang-tidy-plugin/TestFilenameCheck.h index 74ebb865e5f5d..9464480e37115 100644 --- a/tools/clang-tidy-plugin/TestFilenameCheck.h +++ b/tools/clang-tidy-plugin/TestFilenameCheck.h @@ -4,6 +4,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { @@ -22,7 +23,7 @@ class TestFilenameCheck : public ClangTidyCheck TestFilenameCheck( StringRef Name, ClangTidyContext *Context ) : ClangTidyCheck( Name, Context ) {} - void registerPPCallbacks( CompilerInstance &Compiler ) override; + void registerPPCallbacks( const SourceManager &, Preprocessor *, Preprocessor * ) override; }; } // namespace cata diff --git a/tools/clang-tidy-plugin/TestsMustRestoreGlobalStateCheck.cpp b/tools/clang-tidy-plugin/TestsMustRestoreGlobalStateCheck.cpp index 1c73c216c90bc..87b9f218056cc 100644 --- a/tools/clang-tidy-plugin/TestsMustRestoreGlobalStateCheck.cpp +++ b/tools/clang-tidy-plugin/TestsMustRestoreGlobalStateCheck.cpp @@ -49,10 +49,10 @@ class TestsMustRestoreGlobalStateCallbacks : public PPCallbacks TestsMustRestoreGlobalStateCheck *Check; }; -void TestsMustRestoreGlobalStateCheck::registerPPCallbacks( CompilerInstance &Compiler ) +void TestsMustRestoreGlobalStateCheck::registerPPCallbacks( + const SourceManager &, Preprocessor *PP, Preprocessor * ) { - Compiler.getPreprocessor().addPPCallbacks( - llvm::make_unique( this ) ); + PP->addPPCallbacks( std::make_unique( this ) ); } void TestsMustRestoreGlobalStateCheck::registerMatchers( MatchFinder *Finder ) diff --git a/tools/clang-tidy-plugin/TestsMustRestoreGlobalStateCheck.h b/tools/clang-tidy-plugin/TestsMustRestoreGlobalStateCheck.h index 92bbea8016388..1093ae78e11d1 100644 --- a/tools/clang-tidy-plugin/TestsMustRestoreGlobalStateCheck.h +++ b/tools/clang-tidy-plugin/TestsMustRestoreGlobalStateCheck.h @@ -6,6 +6,8 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" +#include "Utils.h" namespace clang { @@ -36,12 +38,7 @@ namespace std template<> struct hash { std::size_t operator()( const clang::tidy::cata::RestoredDecl &r ) const noexcept { - hash function_hash; - hash decl_hash; - size_t result = function_hash( r.function ); - result ^= 0x9e3779b9 + ( result << 6 ) + ( result >> 2 ); - result ^= decl_hash( r.variable ); - return result; + return clang::tidy::cata::HashCombine( r.function, r.variable ); } }; } // namespace std @@ -58,7 +55,7 @@ class TestsMustRestoreGlobalStateCheck : public ClangTidyCheck public: TestsMustRestoreGlobalStateCheck( StringRef Name, ClangTidyContext *Context ); - void registerPPCallbacks( CompilerInstance &Compiler ) override; + void registerPPCallbacks( const SourceManager &, Preprocessor *, Preprocessor * ) override; void registerMatchers( ast_matchers::MatchFinder *Finder ) override; void check( const ast_matchers::MatchFinder::MatchResult &Result ) override; void onEndOfTranslationUnit() override; diff --git a/tools/clang-tidy-plugin/TextStyleCheck.h b/tools/clang-tidy-plugin/TextStyleCheck.h index 5d4101e006cec..951b3ee534463 100644 --- a/tools/clang-tidy-plugin/TextStyleCheck.h +++ b/tools/clang-tidy-plugin/TextStyleCheck.h @@ -6,6 +6,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/TranslatorCommentsCheck.cpp b/tools/clang-tidy-plugin/TranslatorCommentsCheck.cpp index ea658656902da..9ee4947f5cdac 100644 --- a/tools/clang-tidy-plugin/TranslatorCommentsCheck.cpp +++ b/tools/clang-tidy-plugin/TranslatorCommentsCheck.cpp @@ -199,13 +199,15 @@ class TranslatorCommentsCheck::TranslationMacroCallback : public PPCallbacks TranslatorCommentsCheck::TranslatorCommentsCheck( StringRef Name, ClangTidyContext *Context ) : ClangTidyCheck( Name, Context ), MatchingStarted( false ), - Handler( llvm::make_unique( *this ) ) {} + Handler( std::make_unique( *this ) ) {} -void TranslatorCommentsCheck::registerPPCallbacks( CompilerInstance &Compiler ) +TranslatorCommentsCheck::~TranslatorCommentsCheck() = default; + +void TranslatorCommentsCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *PP, Preprocessor * ) { - Compiler.getPreprocessor().addCommentHandler( Handler.get() ); - Compiler.getPreprocessor().addPPCallbacks( - llvm::make_unique( *this, Compiler.getSourceManager() ) ); + PP->addCommentHandler( Handler.get() ); + PP->addPPCallbacks( std::make_unique( *this, SM ) ); } void TranslatorCommentsCheck::registerMatchers( MatchFinder *Finder ) diff --git a/tools/clang-tidy-plugin/TranslatorCommentsCheck.h b/tools/clang-tidy-plugin/TranslatorCommentsCheck.h index c0da879456af9..1d4f716c3f1bb 100644 --- a/tools/clang-tidy-plugin/TranslatorCommentsCheck.h +++ b/tools/clang-tidy-plugin/TranslatorCommentsCheck.h @@ -2,6 +2,7 @@ #define CATA_TOOLS_CLANG_TIDY_PLUGIN_TRANSLATORCOMMENTSCHECK_H #include +#include #include #include #include @@ -23,8 +24,9 @@ class TranslatorCommentsCheck : public ClangTidyCheck { public: TranslatorCommentsCheck( StringRef Name, ClangTidyContext *Context ); + ~TranslatorCommentsCheck(); - void registerPPCallbacks( CompilerInstance &Compiler ) override; + void registerPPCallbacks( const SourceManager &, Preprocessor *, Preprocessor * ) override; void registerMatchers( ast_matchers::MatchFinder *Finder ) override; void check( const ast_matchers::MatchFinder::MatchResult &Result ) override; void onEndOfTranslationUnit() override; diff --git a/tools/clang-tidy-plugin/UnsequencedCallsCheck.cpp b/tools/clang-tidy-plugin/UnsequencedCallsCheck.cpp new file mode 100644 index 0000000000000..26fe17f41ec41 --- /dev/null +++ b/tools/clang-tidy-plugin/UnsequencedCallsCheck.cpp @@ -0,0 +1,208 @@ +#include "UnsequencedCallsCheck.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Utils.h" + +using namespace clang::ast_matchers; + +namespace clang +{ +namespace tidy +{ +namespace cata +{ + +void UnsequencedCallsCheck::registerMatchers( MatchFinder *Finder ) +{ + Finder->addMatcher( + cxxMemberCallExpr( + on( declRefExpr( to( namedDecl().bind( "on" ) ) ) ) + ).bind( "memberCall" ), + this + ); +} + +// Keep walking up the AST so long as the nodes are expressions. When we reach +// a non-expression or an expressions whose subexpressions we know to be +// sequenced (like a short-circuiting logical operator), return the previous expression. +// This is a vague approximation to finding the AST node which represents +// everything between two sequence points. +static const Expr *GetContainingSequenceStatement( ASTContext *Context, const Expr *Node ) +{ + for( const DynTypedNode &parent : Context->getParents( *Node ) ) { + if( parent.get() ) { + return Node; + } + if( parent.get() ) { + return Node; + } + if( const BinaryOperator *op = parent.get() ) { + if( op->isLogicalOp() ) { + return Node; + } + } + if( const CXXConstructExpr *construct = parent.get() ) { + if( construct->isListInitialization() ) { + return Node; + } + } + if( const Expr *Candidate = parent.get() ) { + return GetContainingSequenceStatement( Context, Candidate ); + } + } + + return Node; +} + +// Keep walking up the AST so long as the nodes are expressions. When we reach +// a non-expression, return the previous expression. +// This is a vague approximation to finding the AST node which represents +// everything between two sequence points. +static std::vector GetAncestorExpressions( ASTContext *Context, const Expr *Node ) +{ + const Expr *stop = GetContainingSequenceStatement( Context, Node ); + std::vector result; + const Expr *next; + do { + next = nullptr; + for( const DynTypedNode &parent : Context->getParents( *Node ) ) { + if( const Expr *candidate = parent.get() ) { + next = candidate; + break; + } + } + if( next ) { + result.push_back( next ); + Node = next; + } + } while( next && next != stop ); + + return result; +} + +void UnsequencedCallsCheck::CheckCall( const MatchFinder::MatchResult &Result ) +{ + const CXXMemberCallExpr *MemberCall = + Result.Nodes.getNodeAs( "memberCall" ); + const NamedDecl *On = + Result.Nodes.getNodeAs( "on" ); + if( !MemberCall || !On ) { + return; + } + + const Expr *ContainingStatement = GetContainingSequenceStatement( Result.Context, MemberCall ); + + CallContext context{ ContainingStatement, On }; + calls_[context].push_back( { MemberCall, Result.Context } ); +} + +static bool IsEffectivelyConstCall( const CXXMemberCallExpr *Call ) +{ + const CXXMethodDecl *method = Call->getMethodDecl(); + if( method->isConst() ) { + return true; + } + DeclarationName name = method->getDeclName(); + DeclContextLookupResult similar_functions = method->getParent()->lookup( name ); + for( const NamedDecl *similar : similar_functions ) { + if( const CXXMethodDecl *similar_function = dyn_cast( similar ) ) { + if( similar_function->isConst() ) { + return true; + } + } + } + return false; +} + +void UnsequencedCallsCheck::onEndOfTranslationUnit() +{ + for( std::pair> &p : calls_ ) { + const CallContext &context = p.first; + std::vector &calls = p.second; + std::sort( calls.begin(), calls.end() ); + calls.erase( std::unique( calls.begin(), calls.end() ), calls.end() ); + + if( calls.size() < 2 ) { + continue; + } + std::unordered_set non_const_calls; + for( const CallWithASTContext &member_call : calls ) { + if( !IsEffectivelyConstCall( member_call.call ) ) { + non_const_calls.insert( member_call.call ); + } + } + + if( non_const_calls.empty() ) { + continue; + } + + // If some of the calls are an ancestor of other of the calls then we + // might not need to report it. + std::vector to_delete; + for( const CallWithASTContext &member_call : calls ) { + for( const Expr *ancestor : + GetAncestorExpressions( member_call.context, member_call.call ) ) { + auto in_set = std::find( calls.begin(), calls.end(), ancestor ); + if( in_set == calls.end() ) { + continue; + } + // We have found a pair with an ancestor relationship. Delete + // the most const one. + if( non_const_calls.count( in_set->call ) ) { + to_delete.push_back( member_call ); + } else { + to_delete.push_back( *in_set ); + } + } + } + std::sort( to_delete.begin(), to_delete.end() ); + auto new_end = std::set_difference( + calls.begin(), calls.end(), to_delete.begin(), to_delete.end(), calls.begin() ); + calls.erase( new_end, calls.end() ); + if( calls.size() < 2 ) { + continue; + } + + const CXXRecordDecl *class_type = calls[0].call->getRecordDecl(); + + diag( + context.stmt->getBeginLoc(), + "Unsequenced calls to member functions of %0 (of type %1), at least one of which is " + "non-const." + ) << context.on << class_type; + + for( const CallWithASTContext &member_call : calls ) { + const CXXMethodDecl *method = member_call.call->getMethodDecl(); + if( IsEffectivelyConstCall( member_call.call ) ) { + diag( member_call.call->getBeginLoc(), "Call to const member function %0", + DiagnosticIDs::Note ) << method; + } else { + diag( member_call.call->getBeginLoc(), "Call to non-const member function %0", + DiagnosticIDs::Note ) << method; + } + } + } +} + +void UnsequencedCallsCheck::check( const MatchFinder::MatchResult &Result ) +{ + CheckCall( Result ); +} + +} // namespace cata +} // namespace tidy +} // namespace clang diff --git a/tools/clang-tidy-plugin/UnsequencedCallsCheck.h b/tools/clang-tidy-plugin/UnsequencedCallsCheck.h new file mode 100644 index 0000000000000..6baeb6e9484a7 --- /dev/null +++ b/tools/clang-tidy-plugin/UnsequencedCallsCheck.h @@ -0,0 +1,84 @@ +#ifndef CATA_TOOLS_CLANG_TIDY_PLUGIN_UNSEQUENCEDCALLSCHECK_H +#define CATA_TOOLS_CLANG_TIDY_PLUGIN_UNSEQUENCEDCALLSCHECK_H + +#include +#include +#include + +#include "ClangTidy.h" +#include "ClangTidyCheck.h" +#include "Utils.h" + +namespace clang +{ + +namespace tidy +{ +class ClangTidyContext; + +namespace cata +{ + +struct CallContext { + const Expr *stmt; + const NamedDecl *on; + + bool operator==( const CallContext &other ) const { + return stmt == other.stmt && on == other.on; + } +}; + +struct CallWithASTContext { + const CXXMemberCallExpr *call; + ASTContext *context; + + bool operator==( const CallWithASTContext &other ) const { + return call == other.call; + } + bool operator==( const Expr *other ) const { + return call == other; + } + bool operator<( const CallWithASTContext &other ) const { + return std::less<> {}( call, other.call ); + } +}; + +} // namespace cata +} // namespace tidy +} // namespace clang + +namespace std +{ +template<> +struct hash { + std::size_t operator()( const clang::tidy::cata::CallContext &c ) const noexcept { + return clang::tidy::cata::HashCombine( c.stmt, c.on ); + } +}; +} // namespace std + +namespace clang +{ +namespace tidy +{ +namespace cata +{ + +class UnsequencedCallsCheck : public ClangTidyCheck +{ + public: + UnsequencedCallsCheck( StringRef Name, ClangTidyContext *Context ) + : ClangTidyCheck( Name, Context ) {} + void registerMatchers( ast_matchers::MatchFinder *Finder ) override; + void check( const ast_matchers::MatchFinder::MatchResult &Result ) override; + void onEndOfTranslationUnit() override; + private: + void CheckCall( const ast_matchers::MatchFinder::MatchResult & ); + std::unordered_map> calls_; +}; + +} // namespace cata +} // namespace tidy +} // namespace clang + +#endif // CATA_TOOLS_CLANG_TIDY_PLUGIN_UNSEQUENCEDCALLSCHECK_H diff --git a/tools/clang-tidy-plugin/UnusedStaticsCheck.cpp b/tools/clang-tidy-plugin/UnusedStaticsCheck.cpp new file mode 100644 index 0000000000000..c69b60ddf2ff2 --- /dev/null +++ b/tools/clang-tidy-plugin/UnusedStaticsCheck.cpp @@ -0,0 +1,82 @@ +#include "UnusedStaticsCheck.h" +#include "Utils.h" +#include + +using namespace clang::ast_matchers; + +namespace clang +{ +namespace tidy +{ +namespace cata +{ + +void UnusedStaticsCheck::registerMatchers( MatchFinder *Finder ) +{ + Finder->addMatcher( + varDecl( + anyOf( hasParent( namespaceDecl() ), hasParent( translationUnitDecl() ) ), + hasStaticStorageDuration() + ).bind( "decl" ), + this + ); + Finder->addMatcher( + declRefExpr( to( varDecl().bind( "decl" ) ) ).bind( "ref" ), + this + ); +} + +void UnusedStaticsCheck::check( const MatchFinder::MatchResult &Result ) +{ + const VarDecl *ThisDecl = Result.Nodes.getNodeAs( "decl" ); + if( !ThisDecl ) { + return; + } + + const DeclRefExpr *Ref = Result.Nodes.getNodeAs( "ref" ); + if( Ref ) { + used_decls_.insert( ThisDecl ); + } + + const SourceManager &SM = *Result.SourceManager; + + // Ignore cases in header files + if( !SM.isInMainFile( ThisDecl->getBeginLoc() ) ) { + return; + } + + // Ignore cases that are not the first declaration + if( ThisDecl->getPreviousDecl() ) { + return; + } + + // Ignore cases that are not static linkage + Linkage Lnk = ThisDecl->getFormalLinkage(); + if( Lnk != InternalLinkage && Lnk != UniqueExternalLinkage ) { + return; + } + + SourceLocation DeclLoc = ThisDecl->getBeginLoc(); + SourceLocation ExpansionLoc = SM.getFileLoc( DeclLoc ); + if( DeclLoc != ExpansionLoc ) { + // We are inside a macro expansion + return; + } + + decls_.push_back( ThisDecl ); +} + +void UnusedStaticsCheck::onEndOfTranslationUnit() +{ + for( const VarDecl *V : decls_ ) { + if( used_decls_.count( V ) ) { + continue; + } + + diag( V->getBeginLoc(), "Variable %0 declared but not used." ) << V; + } +} + +} // namespace cata +} // namespace tidy +} // namespace clang diff --git a/tools/clang-tidy-plugin/UnusedStaticsCheck.h b/tools/clang-tidy-plugin/UnusedStaticsCheck.h new file mode 100644 index 0000000000000..85ddcede4cb82 --- /dev/null +++ b/tools/clang-tidy-plugin/UnusedStaticsCheck.h @@ -0,0 +1,37 @@ +#ifndef CATA_TOOLS_CLANG_TIDY_PLUGIN_UNUSEDSTATICSCHECK_H +#define CATA_TOOLS_CLANG_TIDY_PLUGIN_UNUSEDSTATICSCHECK_H + +#include +#include + +#include "ClangTidy.h" +#include "ClangTidyCheck.h" + +namespace clang +{ + +namespace tidy +{ +class ClangTidyContext; + +namespace cata +{ + +class UnusedStaticsCheck : public ClangTidyCheck +{ + public: + UnusedStaticsCheck( StringRef Name, ClangTidyContext *Context ) + : ClangTidyCheck( Name, Context ) {} + void registerMatchers( ast_matchers::MatchFinder *Finder ) override; + void check( const ast_matchers::MatchFinder::MatchResult &Result ) override; + void onEndOfTranslationUnit() override; + private: + std::unordered_set used_decls_; + std::vector decls_; +}; + +} // namespace cata +} // namespace tidy +} // namespace clang + +#endif // CATA_TOOLS_CLANG_TIDY_PLUGIN_UNUSEDSTATICSCHECK_H diff --git a/tools/clang-tidy-plugin/UseLocalizedSortingCheck.h b/tools/clang-tidy-plugin/UseLocalizedSortingCheck.h index 29586eba5e37e..b9e2a08829b7a 100644 --- a/tools/clang-tidy-plugin/UseLocalizedSortingCheck.h +++ b/tools/clang-tidy-plugin/UseLocalizedSortingCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/UseNamedPointConstantsCheck.cpp b/tools/clang-tidy-plugin/UseNamedPointConstantsCheck.cpp index 3423458d63677..4e9d08f3a6168 100644 --- a/tools/clang-tidy-plugin/UseNamedPointConstantsCheck.cpp +++ b/tools/clang-tidy-plugin/UseNamedPointConstantsCheck.cpp @@ -169,7 +169,7 @@ static void CheckConstructor( UseNamedPointConstantsCheck &Check, if( TempParent ) { SourceRangeToReplace = ConstructorCall->getSourceRange(); // Work around buggy source range for default parameters - const std::string ReplacedText = getText( Result, ConstructorCall ); + const StringRef ReplacedText = getText( Result, ConstructorCall ); if( ReplacedText.size() >= 2 && ReplacedText.substr( 0, 2 ) == "= " ) { Replacement = "= " + Replacement; } diff --git a/tools/clang-tidy-plugin/UseNamedPointConstantsCheck.h b/tools/clang-tidy-plugin/UseNamedPointConstantsCheck.h index 2e8bcf9861dd6..08dc965f4d998 100644 --- a/tools/clang-tidy-plugin/UseNamedPointConstantsCheck.h +++ b/tools/clang-tidy-plugin/UseNamedPointConstantsCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/UsePointApisCheck.h b/tools/clang-tidy-plugin/UsePointApisCheck.h index 5d14331872fd7..6cf6b0c6e09e4 100644 --- a/tools/clang-tidy-plugin/UsePointApisCheck.h +++ b/tools/clang-tidy-plugin/UsePointApisCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/UsePointArithmeticCheck.cpp b/tools/clang-tidy-plugin/UsePointArithmeticCheck.cpp index b28c7e4c4177c..2e92aa27bbce1 100644 --- a/tools/clang-tidy-plugin/UsePointArithmeticCheck.cpp +++ b/tools/clang-tidy-plugin/UsePointArithmeticCheck.cpp @@ -94,8 +94,9 @@ struct ExpressionComponent { coefficient( 1 ), isMember( false ), isArrowRef( false ), - isTripoint( false ) - {} + isTripoint( false ) { + complete_init(); + } ExpressionComponent( const MatchFinder::MatchResult &Result, const Expr *E, const CXXRecordDecl *MemberOf, bool IsArrowRef ) : @@ -103,8 +104,15 @@ struct ExpressionComponent { coefficient( 1 ), isMember( true ), isArrowRef( IsArrowRef ), - isTripoint( MemberOf->getName() == "tripoint" ) - {} + isTripoint( MemberOf->getName() == "tripoint" ) { + complete_init(); + } + + void complete_init() { + if( StringRef( objectRef ).endswith( "->" ) ) { + objectRef.erase( objectRef.end() - 2, objectRef.end() ); + } + } std::string objectRef; int coefficient; @@ -265,7 +273,7 @@ static std::vector decomposeExpr( const Expr *E, const std: } case Stmt::MaterializeTemporaryExprClass: { const MaterializeTemporaryExpr *Temp = cast( E ); - return decomposeExpr( Temp->GetTemporaryExpr(), Member, Result ); + return decomposeExpr( Temp->getSubExpr(), Member, Result ); } case Stmt::MemberExprClass: { const MemberExpr *MemEx = cast( E ); @@ -369,11 +377,11 @@ static void appendCoefficient( std::string &Result, int coefficient ) } } -static std::string writeConstructor( const std::string &TypeName, +static std::string writeConstructor( const StringRef TypeName, const std::set &Keys, std::map Args ) { - std::string Result = TypeName + "( "; + std::string Result = TypeName.str() + "( "; bool AnyLeftovers = false; for( const auto &Key : Keys ) { std::string &Leftover = Args[Key]; @@ -611,7 +619,7 @@ static void CheckConstructor( UsePointArithmeticCheck &Check, Keys.insert( Component.first ); } - std::string TargetTypeName; + StringRef TargetTypeName; if( Leftovers["z"].empty() ) { TargetTypeName = "point"; Keys.erase( "z" ); diff --git a/tools/clang-tidy-plugin/UsePointArithmeticCheck.h b/tools/clang-tidy-plugin/UsePointArithmeticCheck.h index f6010ea86bbbf..ca9c1dc08291c 100644 --- a/tools/clang-tidy-plugin/UsePointArithmeticCheck.h +++ b/tools/clang-tidy-plugin/UsePointArithmeticCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/Utils.cpp b/tools/clang-tidy-plugin/Utils.cpp index 00907ff7356f8..7bcc1196827a1 100644 --- a/tools/clang-tidy-plugin/Utils.cpp +++ b/tools/clang-tidy-plugin/Utils.cpp @@ -10,19 +10,19 @@ namespace cata NameConvention::NameConvention( StringRef xName ) { if( xName.endswith( "x" ) ) { - root = xName.drop_back(); + root = xName.drop_back().str(); capital = false; atEnd = true; } else if( xName.endswith( "X" ) ) { - root = xName.drop_back(); + root = xName.drop_back().str(); capital = true; atEnd = true; } else if( xName.startswith( "x" ) ) { - root = xName.drop_front(); + root = xName.drop_front().str(); capital = false; atEnd = false; } else if( xName.startswith( "X" ) ) { - root = xName.drop_front(); + root = xName.drop_front().str(); capital = true; atEnd = false; } else { diff --git a/tools/clang-tidy-plugin/Utils.h b/tools/clang-tidy-plugin/Utils.h index 5a39a1c45d8b2..cb132ebfc7a86 100644 --- a/tools/clang-tidy-plugin/Utils.h +++ b/tools/clang-tidy-plugin/Utils.h @@ -46,7 +46,7 @@ inline StringRef getText( const ast_matchers::MatchFinder::MatchResult &Result, template static const T *getParent( const ast_matchers::MatchFinder::MatchResult &Result, const U *Node ) { - for( const ast_type_traits::DynTypedNode &parent : Result.Context->getParents( *Node ) ) { + for( const DynTypedNode &parent : Result.Context->getParents( *Node ) ) { if( const T *Candidate = parent.get() ) { return Candidate; } @@ -59,7 +59,7 @@ template static const FunctionDecl *getContainingFunction( const ast_matchers::MatchFinder::MatchResult &Result, const T *Node ) { - for( const ast_type_traits::DynTypedNode &parent : Result.Context->getParents( *Node ) ) { + for( const DynTypedNode &parent : Result.Context->getParents( *Node ) ) { if( const Decl *Candidate = parent.get() ) { if( const FunctionDecl *ContainingFunction = dyn_cast( Candidate ) ) { return ContainingFunction; @@ -206,6 +206,27 @@ class NameConvention bool valid = true; }; +template +inline size_t HashCombine( const T &t, const U &u ) +{ + std::hash t_hash; + std::hash u_hash; + size_t result = t_hash( t ); + result ^= 0x9e3779b9 + ( result << 6 ) + ( result >> 2 ); + result ^= u_hash( u ); + return result; +} + +template +std::string StrCat( T0 &&a0, T &&...a ) +{ + std::string result( std::forward( a0 ) ); + // Using initializer list as a poor man's fold expression until C++17. + static_cast( + std::array { ( result.append( std::forward( a ) ), false )... } ); + return result; +} + } // namespace cata } // namespace tidy } // namespace clang diff --git a/tools/clang-tidy-plugin/XYCheck.h b/tools/clang-tidy-plugin/XYCheck.h index 4493cc25b2e94..64555fdcd2095 100644 --- a/tools/clang-tidy-plugin/XYCheck.h +++ b/tools/clang-tidy-plugin/XYCheck.h @@ -5,6 +5,7 @@ #include #include "ClangTidy.h" +#include "ClangTidyCheck.h" namespace clang { diff --git a/tools/clang-tidy-plugin/test/combine-locals-into-point.cpp b/tools/clang-tidy-plugin/test/combine-locals-into-point.cpp index 75355e52fa809..a33a398f3f1aa 100644 --- a/tools/clang-tidy-plugin/test/combine-locals-into-point.cpp +++ b/tools/clang-tidy-plugin/test/combine-locals-into-point.cpp @@ -143,8 +143,6 @@ void g10() void g11() { - // When multiple changes to be done in one function, only perform one (to - // avoid overlapping replacements) static constexpr int x = 0; // CHECK-MESSAGES: warning: Variables 'x' and 'y' could be combined into a single 'point' variable. [cata-combine-locals-into-point] // CHECK-FIXES: static constexpr point p( 0, 1 ); @@ -159,3 +157,12 @@ void g12() const unsigned x1 = false; const unsigned y1 = true; } + +void g13() +{ + // Check that suppressing the first warning suppresses the rest. + // NOLINTNEXTLINE(cata-combine-locals-into-point) + int rx = 7; + int ry = 14; + f0( point( rx, ry ) ); +} diff --git a/tools/clang-tidy-plugin/test/lit.cfg b/tools/clang-tidy-plugin/test/lit.cfg index 496804316adfa..7bacf442b3d47 100644 --- a/tools/clang-tidy-plugin/test/lit.cfg +++ b/tools/clang-tidy-plugin/test/lit.cfg @@ -17,6 +17,8 @@ else: config.plugin_build_root, 'clang-tidy-plugin-support', 'bin', 'check_clang_tidy.py') +check_clang_tidy += ' -std=c++14' + cata_include = os.path.join( config.cata_source_dir, "src" ) cata_plugin = os.path.join( diff --git a/tools/clang-tidy-plugin/test/no-long.cpp b/tools/clang-tidy-plugin/test/no-long.cpp index a0a7f1b8d87e6..4f6672c5785a8 100644 --- a/tools/clang-tidy-plugin/test/no-long.cpp +++ b/tools/clang-tidy-plugin/test/no-long.cpp @@ -105,3 +105,20 @@ void Bf() template long g1( T g1p0 ); // CHECK-MESSAGES: warning: Function 'g1' declared as returning 'long'. Prefer int or int64_t to long. [cata-no-long] + +template +struct A1 { + void f( T a1a ) { + } + T a1b; +}; + +template +A1 f5() +{ + return {}; +} + +// Would be nice to get warnings here but haven't found a way to do so. +extern template A1 f5(); +template A1 f5(); diff --git a/tools/clang-tidy-plugin/test/static-int_id-constants.cpp b/tools/clang-tidy-plugin/test/static-int_id-constants.cpp new file mode 100644 index 0000000000000..b6fc5530e313b --- /dev/null +++ b/tools/clang-tidy-plugin/test/static-int_id-constants.cpp @@ -0,0 +1,50 @@ +// RUN: %check_clang_tidy %s cata-static-int_id-constants %t -- -plugins=%cata_plugin -- -isystem %cata_include + +template +class int_id +{ + public: + int_id(); + explicit int_id( int ); +}; + +struct furn_t; +using furn_id = int_id; + +extern furn_id f_hay; +// CHECK-MESSAGES: warning: Global declaration of 'f_hay' is dangerous because 'furn_id' is a specialization of int_id and it will not update automatically when game data changes. Consider switching to a string_id. [cata-static-int_id-constants] + +// No warning for second decl os same variable. +furn_id f_hay; + +static int_id f_ball_mach; +// CHECK-MESSAGES: warning: Global declaration of 'f_ball_mach' is dangerous because 'int_id' is a specialization of int_id and it will not update automatically when game data changes. Consider switching to a string_id. [cata-static-int_id-constants] + +namespace foo +{ + +const furn_id f_dahlia; +// CHECK-MESSAGES: warning: Global declaration of 'f_dahlia' is dangerous because 'furn_id' is a specialization of int_id and it will not update automatically when game data changes. Consider switching to a string_id. [cata-static-int_id-constants] + +} // namespace foo + +class A +{ + static furn_id f_bluebell; + // CHECK-MESSAGES: warning: Static declaration of 'f_bluebell' is dangerous because 'furn_id' is a specialization of int_id and it will not update automatically when game data changes. Consider switching to a string_id. [cata-static-int_id-constants] + + // No warning for regular class data members + furn_id my_furn; +}; + +void f() +{ + static furn_id f_floor_canvas; + // CHECK-MESSAGES: warning: Static declaration of 'f_floor_canvas' is dangerous because 'furn_id' is a specialization of int_id and it will not update automatically when game data changes. Consider switching to a string_id. [cata-static-int_id-constants] + + // No warning for regular local variables + furn_id f; + + // No warning for other types + static int i; +} diff --git a/tools/clang-tidy-plugin/test/unsequenced-calls.cpp b/tools/clang-tidy-plugin/test/unsequenced-calls.cpp new file mode 100644 index 0000000000000..58e8f6478bccb --- /dev/null +++ b/tools/clang-tidy-plugin/test/unsequenced-calls.cpp @@ -0,0 +1,69 @@ +// RUN: %check_clang_tidy %s cata-unsequenced-calls %t -- -plugins=%cata_plugin -- + +struct A { + // Define a const and a non-const member function + int const_mf( int = 0 ) const; + int nonconst_mf( int = 0 ); + int begin(); + int end(); + int begin() const; + int end() const; +}; + +void g( int, int ); + +struct B { + B( int, int ); +}; + +void f0() +{ + A a; + // Two calls to const functions are fine + g( a.const_mf(), a.const_mf() ); + // Calls to begin/end are also fine + g( a.begin(), a.end() ); + // Calls in a ternary expression are fine + int n0 = a.nonconst_mf() ? a.nonconst_mf() : a.const_mf(); + // Or by short-circuiting boolean expressions + int n1 = a.nonconst_mf() && a.nonconst_mf(); + int n2 = a.nonconst_mf() || a.nonconst_mf(); + // Nested calls are OK + a.nonconst_mf( a.const_mf() ); + // But otherwise, if at least one is non-const, raise a warning + g( a.nonconst_mf(), a.const_mf() ); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: Unsequenced calls to member functions of 'a' (of type 'A'), at least one of which is non-const. [cata-unsequenced-calls] + g( a.const_mf(), a.nonconst_mf() ); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: Unsequenced calls to member functions of 'a' (of type 'A'), at least one of which is non-const. [cata-unsequenced-calls] + g( a.nonconst_mf(), a.nonconst_mf() ); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: Unsequenced calls to member functions of 'a' (of type 'A'), at least one of which is non-const. [cata-unsequenced-calls] + int n3 = a.nonconst_mf() | a.nonconst_mf(); + // CHECK-MESSAGES: [[@LINE-1]]:14: warning: Unsequenced calls to member functions of 'a' (of type 'A'), at least one of which is non-const. [cata-unsequenced-calls] +} + +void f1() +{ + A a; + // Calls in args to a 'regular' constructor are dangerous + B b0( a.nonconst_mf(), a.nonconst_mf() ); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: Unsequenced calls to member functions of 'a' (of type 'A'), at least one of which is non-const. [cata-unsequenced-calls] + + // ...but using an initializer-list style is OK, because those are + // sequenced. + B b1{ a.nonconst_mf(), a.nonconst_mf() }; + B b2 = { a.nonconst_mf(), a.nonconst_mf() }; +} + +struct B2 { + int a; + int b; +}; + +void f2() +{ + A a; + // Aggregate initialization is also OK. + B2 b1{ a.nonconst_mf(), a.nonconst_mf() }; + B2 b2 = { a.nonconst_mf(), a.nonconst_mf() }; + B2{ a.nonconst_mf(), a.nonconst_mf() }; +} diff --git a/tools/clang-tidy-plugin/test/unused-statics.cpp b/tools/clang-tidy-plugin/test/unused-statics.cpp new file mode 100644 index 0000000000000..e10370b7479c6 --- /dev/null +++ b/tools/clang-tidy-plugin/test/unused-statics.cpp @@ -0,0 +1,31 @@ +// RUN: %check_clang_tidy %s cata-unused-statics %t -- -plugins=%cata_plugin -- + +class A0 {}; + +static A0 a0; +// CHECK-MESSAGES: warning: Variable 'a0' declared but not used. [cata-unused-statics] + +namespace cata +{ + +class A1 {}; + +static A1 a1; +// CHECK-MESSAGES: warning: Variable 'a1' declared but not used. [cata-unused-statics] + +} + +namespace +{ + +class A2 {}; + +A2 a2; +// CHECK-MESSAGES: warning: Variable 'a2' declared but not used. [cata-unused-statics] + +} + +// No warnings inside macros +class A3 {}; +#define M3 static A3 a3; +M3 diff --git a/tools/clang-tidy-plugin/test/use-localized-sorting.cpp b/tools/clang-tidy-plugin/test/use-localized-sorting.cpp index 5914548437b96..d3b6c31bad7b5 100644 --- a/tools/clang-tidy-plugin/test/use-localized-sorting.cpp +++ b/tools/clang-tidy-plugin/test/use-localized-sorting.cpp @@ -63,7 +63,7 @@ bool f1( const NonString &l, const NonString &r ) bool f2( const std::pair &l, const std::pair &r ) { return l < r; - // CHECK-MESSAGES: warning: Raw comparison of 'const std::pair' (aka 'const pair >'). For UI purposes please use localized_compare from translations.h. [cata-use-localized-sorting] + // CHECK-MESSAGES: warning: Raw comparison of 'const std::pair' (aka 'const pair>'). For UI purposes please use localized_compare from translations.h. [cata-use-localized-sorting] } bool f3( const std::pair &l, const std::pair &r ) @@ -74,7 +74,7 @@ bool f3( const std::pair &l, const std::pair &r bool f4( const std::tuple &l, const std::tuple &r ) { return l < r; - // CHECK-MESSAGES: warning: Raw comparison of 'const std::tuple' (aka 'const tuple >'). For UI purposes please use localized_compare from translations.h. [cata-use-localized-sorting] + // CHECK-MESSAGES: warning: Raw comparison of 'const std::tuple' (aka 'const tuple>'). For UI purposes please use localized_compare from translations.h. [cata-use-localized-sorting] } bool f5( const std::tuple &l, const std::tuple &r ) @@ -86,7 +86,7 @@ bool f4( const std::tuple> &l, const std::tuple> &r ) { return l < r; - // CHECK-MESSAGES: warning: Raw comparison of 'const std::tuple >' (aka 'const tuple > >'). For UI purposes please use localized_compare from translations.h. [cata-use-localized-sorting] + // CHECK-MESSAGES: warning: Raw comparison of 'const std::tuple>' (aka 'const tuple>>'). For UI purposes please use localized_compare from translations.h. [cata-use-localized-sorting] } bool sort0( const std::string *start, const std::string *end ) @@ -103,7 +103,7 @@ bool sort1( const NonString *start, const NonString *end ) bool sortit0( iterator start, iterator end ) { std::sort( start, end ); - // CHECK-MESSAGES: warning: Raw sort of 'std::basic_string, std::allocator >'. For UI purposes please use localized_compare from translations.h. [cata-use-localized-sorting] + // CHECK-MESSAGES: warning: Raw sort of 'std::basic_string'. For UI purposes please use localized_compare from translations.h. [cata-use-localized-sorting] } bool sortit1( iterator start, iterator end ) diff --git a/tools/json_tools/convert_to_portions.py b/tools/json_tools/convert_to_portions.py new file mode 100755 index 0000000000000..2a039bfbd3ae1 --- /dev/null +++ b/tools/json_tools/convert_to_portions.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 + +import argparse +import json +import os + +args = argparse.ArgumentParser() +args.add_argument("dir", action="store", help="specify json directory") +args_dict = vars(args.parse_args()) + + +class portion_data: + encumbrance = -1 + max_encumbrance = -1 + coverage = -1 + covers = [] + sided = False + + +def portion_null(dat): + ret = dat.encumbrance == -1 + ret = ret and dat.max_encumbrance == -1 + ret = ret and dat.coverage == -1 + ret = ret and len(dat.covers) == 0 + ret = ret and not dat.sided + return ret + + +def gen_entry(dat): + added = dict() + + if dat.encumbrance != -1: + added["encumbrance"] = dat.encumbrance + if dat.max_encumbrance != -1: + added["encumbrance"] = [dat.encumbrance, dat.max_encumbrance] + if dat.coverage != -1: + added["coverage"] = dat.coverage + if len(dat.covers) != 0: + added["covers"] = dat.covers + if dat.sided: + added["sided"] = True + + return added + + +def portionize(jo): + dat = portion_data() + if "encumbrance" in jo: + dat.encumbrance = jo["encumbrance"] + del jo["encumbrance"] + if "max_encumbrance" in jo: + dat.max_encumbrance = jo["max_encumbrance"] + del jo["max_encumbrance"] + if "coverage" in jo: + dat.coverage = jo["coverage"] + del jo["coverage"] + if "covers" in jo: + dat.covers = jo["covers"] + del jo["covers"] + if "sided" in jo: + dat.sided = jo["sided"] + del jo["sided"] + + if portion_null(dat): + return None + + if "armor_portion_data" not in jo: + added = gen_entry(dat) + jo["armor_portion_data"] = [added] + else: + added = gen_entry(dat) + added["//0"] = "Autogenned for item with non-portion and portion data" + added["//1"] = "Ensure that this works correctly!" + jo["armor_portion_data"].insert(0, added) + + return jo + + +def gen_new(path): + change = False + # Having troubles with this script halting? + # Uncomment below to find the file it's halting on + #print(path) + with open(path, "r") as json_file: + json_data = json.load(json_file) + for jo in json_data: + if "type" not in jo: + continue + if (jo["type"] != "ARMOR" and + jo["type"] != "TOOL_ARMOR" and + "armor_data" not in jo): + continue + + if "armor_data" in jo: + new_data = portionize(jo["armor_data"]) + if new_data is not None: + jo["armor_data"] = new_data + change = True + continue + + # I don't know python - assiging to jo is not necessary here + # but is necessary for "armor_data" above + new_jo = portionize(jo) + if new_jo is not None: + change = True + + return json_data if change else None + + +for root, directories, filenames in os.walk(args_dict["dir"]): + for filename in filenames: + path = os.path.join(root, filename) + if path.endswith(".json"): + new = gen_new(path) + if new is not None: + with open(path, "w") as jf: + json.dump(new, jf, ensure_ascii=False) + os.system(f"./tools/format/json_formatter.cgi {path}") diff --git a/tools/llama/README.md b/tools/llama/README.md new file mode 100644 index 0000000000000..6fc22d8200887 --- /dev/null +++ b/tools/llama/README.md @@ -0,0 +1,38 @@ +## Using llama to accelerate your build + +[llama](https://github.com/nelhage/llama) is a CLI for outsourcing computation to AWS Lambda. + +You can use llama to accelerate your CDDA builds. To help you set that up, +this directory contains a suitable image for compiling CDDA on. + +After bootstrapping llama, you can use for example + +```bash +llama update-function --create --timeout=120s --memory=4096 --build=tools/llama/gcc-focal gcc +``` + +This will configure llama to use the `gcc-focal` image here. Note that it's +important to have larger-than-default timeout and memory settings. + +### Troubleshooting + +If, when building, you get errors about failed includes of SDL2 headers, then +you can work around these via a hack I discovered, by setting a carefully +chosen CXXFLAGS. Your build command might look something like this: + +```bash +CXXFLAGS=-I/x/../../../../usr/include/SDL2 CXX=llamac++ make PCH=0 TILES=1 -j100 +``` + +Or, if using cmake, something like this: +```bash +cmake \ + -DTILES=ON \ + -DSOUND=ON \ + -DCMAKE_C_COMPILER=llamacc \ + -DCMAKE_CXX_COMPILER=llamac++ \ + -DCMAKE_CXX_FLAGS="-I/x/../../../../usr/include/SDL2" \ + .. +``` + +(`/x` has to be a directory that doesn't exist on your system) diff --git a/tools/llama/gcc-focal/Dockerfile b/tools/llama/gcc-focal/Dockerfile new file mode 100644 index 0000000000000..fb72ff988c669 --- /dev/null +++ b/tools/llama/gcc-focal/Dockerfile @@ -0,0 +1,12 @@ +FROM ghcr.io/nelhage/llama as llama +FROM ubuntu:focal +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive \ + apt-get -y install \ + gcc g++ gcc-9 g++-9 ca-certificates \ + libncursesw5-dev libsdl2-dev libsdl2-ttf-dev libsdl2-image-dev \ + libsdl2-mixer-dev libpulse-dev gettext parallel && \ + apt-get clean +COPY --from=llama /llama_runtime /llama_runtime +WORKDIR / +ENTRYPOINT ["/llama_runtime"]