diff --git a/.github/workflows/astyle.yml b/.github/workflows/astyle.yml index 617b55293b624..3aaf4f30e9d7a 100644 --- a/.github/workflows/astyle.yml +++ b/.github/workflows/astyle.yml @@ -1,15 +1,23 @@ name: astyle -on: - pull_request: - paths: - - '**.cpp' - - '**.h' - - '**.c' +on: pull_request jobs: + skip-duplicates: + continue-on-error: true + runs-on: ubuntu-latest + # Map a step output to a job output + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - id: skip_check + uses: fkirc/skip-duplicate-actions@master + with: + paths: '["**.cpp", "**.h", "**.c"]' astyle-code: name: astyle check + needs: skip-duplicates + if: ${{ needs.skip-duplicates.outputs.should_skip != 'true' }} runs-on: ubuntu-latest diff --git a/.github/workflows/basic-build.yml b/.github/workflows/basic-build.yml new file mode 100644 index 0000000000000..9df365b5e32d3 --- /dev/null +++ b/.github/workflows/basic-build.yml @@ -0,0 +1,71 @@ +name: Basic Build + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + skip-duplicates: + continue-on-error: false + runs-on: ubuntu-latest + # Map a step output to a job output + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - id: skip_check + uses: fkirc/skip-duplicate-actions@master + with: + paths-ignore: '["android/**", "build-data/osx/**", "doc/**", "doxygen_doc/**", "lgtm/**", "msvc-**", "object_creator/**", "tools/**", "utilities/**"]' + basic-build: + needs: skip-duplicates + if: ${{ needs.skip-duplicates.outputs.should_skip != 'true' }} + + name: Basic Build and Test (GCC 9, Curses, LTO) + runs-on: ubuntu-latest + env: + COMPILER: g++-9 + TEST_STAGE: 1 + EXTRA_TEST_OPTS: --error-format=github-action + NATIVE: linux64 + GOLD: 1 + LTO: 1 + RELEASE: 1 + steps: + - name: checkout repository + uses: actions/checkout@v1 + with: + fetch-depth: 1 + - name: install dependencies (ubuntu) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install libncursesw5-dev ccache gettext parallel + - name: prepare + run: bash ./build-scripts/requirements.sh + - name: Get Date + id: get-date + run: | + echo "::set-output name=date::$(/bin/date -u "+%Y%m%d%H%M")" + shell: bash + - name: ccache cache files (ubuntu) + if: runner.os == 'Linux' + uses: actions/cache@v2 + with: + path: ~/.ccache + key: ccache-linux-g++-9-${{ steps.get-date.outputs.date }} + restore-keys: | + ccache-linux-g++-9- + - uses: ammaraskar/gcc-problem-matcher@master + - name: build and test + run: bash ./build-scripts/build.sh + - name: upload artifacts if failed + 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/json.yml b/.github/workflows/json.yml index b381f0cadedb2..267fb31f1a5c7 100644 --- a/.github/workflows/json.yml +++ b/.github/workflows/json.yml @@ -1,15 +1,25 @@ name: JSON Validation -on: - pull_request: - paths: - - '**.json' +on: pull_request jobs: + skip-duplicates: + continue-on-error: false + runs-on: ubuntu-latest + # Map a step output to a job output + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - id: skip_check + uses: fkirc/skip-duplicate-actions@master + with: + paths: '["**.json"]' style-json: name: JSON style check runs-on: ubuntu-latest + needs: skip-duplicates + if: ${{ needs.skip-duplicates.outputs.should_skip != 'true' }} steps: - uses: actions/checkout@v1 diff --git a/.github/workflows/matrix.yml b/.github/workflows/matrix.yml index d751bd8c98113..c90b85e4a21c3 100644 --- a/.github/workflows/matrix.yml +++ b/.github/workflows/matrix.yml @@ -4,49 +4,28 @@ on: push: branches: - master - - 0.F-dev - paths-ignore: - - 'android/**' - - 'build-data/osx/**' - - 'doc/**' - - 'doxygen_doc/**' - - 'lgtm/**' - - 'msvc-full-features/**' - - 'msvc-object_creator/**' - - 'object_creator/**' - - 'tools/**' - - 'utilities/**' pull_request: branches: - master - - 0.F-dev - paths-ignore: - - 'android/**' - - 'build-data/osx/**' - - 'doc/**' - - 'doxygen_doc/**' - - 'lgtm/**' - - 'msvc-full-features/**' - - 'msvc-object_creator/**' - - 'object_creator/**' - - 'tools/**' - - 'utilities/**' - jobs: + skip-duplicates: + continue-on-error: false + runs-on: ubuntu-latest + # Map a step output to a job output + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - id: skip_check + uses: fkirc/skip-duplicate-actions@master + with: + paths-ignore: '["android/**", "build-data/osx/**", "doc/**", "doxygen_doc/**", "lgtm/**", "msvc-**", "object_creator/**", "tools/**", "utilities/**"]' varied_builds: + needs: skip-duplicates + if: ${{ needs.skip-duplicates.outputs.should_skip != 'true' }} strategy: matrix: include: - - compiler: g++-9 - os: ubuntu-latest - cmake: 0 - tiles: 0 - test-stage: 1 - native: linux64 - gold: 1 - lto: 1 - title: GCC 9, Ubuntu, Curses, LTO - compiler: g++-7 os: ubuntu-latest cmake: 1 diff --git a/data/json/effect_on_condition.json b/data/json/effect_on_condition.json index 907a0eaf68c42..302cec0a06edf 100644 --- a/data/json/effect_on_condition.json +++ b/data/json/effect_on_condition.json @@ -269,5 +269,14 @@ ] } ] + }, + { + "type": "effect_on_condition", + "id": "bio_leaky", + "recurrence_min": "3 minutes", + "recurrence_max": "9 minutes", + "condition": { "u_has_bionics": "bio_leaky" }, + "deactivate_condition": { "not": { "u_has_bionics": "bio_leaky" } }, + "effect": [ { "u_mod_healthy": -1, "cap": -200 } ] } ] diff --git a/data/json/flags.json b/data/json/flags.json index d270a0b6e9b41..be2479e6f193c 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -827,6 +827,24 @@ "//": "Prevents the item from making the body part count as unfriendly to water and thus causing negative morale from being wet.", "info": "This clothing performs well even when soaking wet. This can feel good." }, + { + "id": "ITEM_BROKEN", + "type": "json_flag", + "context": [ ], + "info": "This item was broken and won't activate anymore." + }, + { + "id": "WATER_BREAK", + "type": "json_flag", + "context": [ ], + "info": "This item will get broken by water." + }, + { + "id": "WATER_DISSOLVE", + "type": "json_flag", + "context": [ ], + "info": "This item will get dissolved in water." + }, { "id": "WET", "type": "json_flag", diff --git a/data/json/furniture_and_terrain/furniture-appliances.json b/data/json/furniture_and_terrain/furniture-appliances.json index b3212250a5520..2dc4bf7fae858 100644 --- a/data/json/furniture_and_terrain/furniture-appliances.json +++ b/data/json/furniture_and_terrain/furniture-appliances.json @@ -355,7 +355,7 @@ "id": "f_oven", "name": "oven", "symbol": "#", - "description": "A standard convection-based oven, commonly used for heating and cooking food. Despite it no longer working, you could safely light a fire inside.", + "description": "A standard convection-based oven, commonly used for heating and cooking food. You could safely light a fire inside or power it with UPS.", "color": "dark_gray", "move_cost_mod": 2, "coverage": 60, diff --git a/data/json/furniture_and_terrain/furniture-medical.json b/data/json/furniture_and_terrain/furniture-medical.json index 4ea67bc1c909f..9a13fde58ce11 100644 --- a/data/json/furniture_and_terrain/furniture-medical.json +++ b/data/json/furniture_and_terrain/furniture-medical.json @@ -230,7 +230,7 @@ { "item": "pipe", "count": [ 6, 12 ] }, { "item": "cu_pipe", "count": [ 1, 4 ] }, { "item": "cable", "charges": [ 1, 4 ] }, - { "item": "plastic_chunk", "count": [ 50, 75 ] }, + { "item": "epoxy_chunk", "count": [ 50, 75 ] }, { "item": "sheet_metal", "count": [ 1, 2 ] }, { "item": "sheet_metal_small", "count": [ 0, 4 ] } ] @@ -245,7 +245,7 @@ { "item": "scrap", "count": [ 2, 8 ] }, { "item": "steel_chunk", "count": [ 2, 4 ] }, { "item": "sheet_metal_small", "count": [ 6, 10 ] }, - { "item": "plastic_chunk", "count": [ 30, 50 ] }, + { "item": "epoxy_chunk", "count": [ 30, 50 ] }, { "item": "pipe", "count": 1 }, { "item": "cable", "charges": [ 1, 3 ] }, { "item": "cu_pipe", "count": 1 } diff --git a/data/json/furniture_and_terrain/furniture-plumbing.json b/data/json/furniture_and_terrain/furniture-plumbing.json index 60e80d7b9acb8..34ffc4158824f 100644 --- a/data/json/furniture_and_terrain/furniture-plumbing.json +++ b/data/json/furniture_and_terrain/furniture-plumbing.json @@ -150,7 +150,7 @@ "id": "f_water_purifier", "looks_like": "f_water_heater", "name": "water purifier", - "description": "This devices effectively sterilizes water, though without a lot of power and proper plumbing, it's only good for parts now.", + "description": "This device effectively sterilizes water, though without power source, like UPS, it's only good for parts.", "symbol": "W", "bgcolor": "blue", "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 8c108583df019..1cf20c8c0d5e7 100644 --- a/data/json/furniture_and_terrain/furniture-tools.json +++ b/data/json/furniture_and_terrain/furniture-tools.json @@ -470,7 +470,7 @@ "id": "f_arc_furnace", "name": "electric arc furnace", "looks_like": "f_machinery_heavy", - "description": "Not the kind of furnace you'd heat your house with, this is a device for heating things to extreme temperatures as part of industrial fabrication processes.", + "description": "Not the kind of furnace you'd heat your house with, this is a device for heating things to extreme temperatures as part of industrial fabrication processes. Can be used in recipes instead of forge, when supplied with power from UPS.", "symbol": "0", "color": "white_red", "move_cost_mod": -1, @@ -505,7 +505,7 @@ "id": "f_drill_press", "name": "drill press", "looks_like": "f_machinery_light", - "description": "A powerful drill mounted on a slide that lets it drop precisely down. Useful in all kinds of projects from industrial fabrication to home woodworking.", + "description": "A powerful drill mounted on a slide that lets it drop precisely down. Useful in all kinds of projects from industrial fabrication to home woodworking, if supplied with power from UPS.", "symbol": "7", "color": "yellow_red", "move_cost_mod": -1, @@ -543,7 +543,7 @@ "id": "f_tablesaw", "name": "tablesaw", "looks_like": "f_machinery_light", - "description": "A rotating saw blade set into a large flat table, for making straight measured cuts. One of the key tools in a carpenter's arsenal.", + "description": "A rotating saw blade set into a large flat table, for making straight measured cuts, if supplied with power from UPS. One of the key tools in a carpenter's arsenal.", "symbol": "7", "color": "yellow_red", "move_cost_mod": 8, @@ -582,7 +582,7 @@ "id": "f_mitresaw", "name": "mitre saw", "looks_like": "f_machinery_light", - "description": "A circular saw blade on an arm that can slide and rotate in several directions, this is a staple tool for nearly any carpentry.", + "description": "A circular saw blade on an arm that can slide and rotate in several directions, this is a staple tool for nearly any carpentry, if supplied with power from UPS.", "symbol": "7", "color": "yellow_cyan", "move_cost_mod": -1, @@ -621,7 +621,7 @@ "id": "f_bandsaw", "name": "bandsaw", "looks_like": "f_machinery_light", - "description": "A ribbonlike sawblade runs in a single direction in this tool, allowing precise cuts at almost any angle.", + "description": "A ribbonlike sawblade runs in a single direction in this tool, allowing precise cuts at almost any angle, if supplied with power from UPS.", "symbol": "7", "color": "yellow_cyan", "move_cost_mod": -1, @@ -659,7 +659,7 @@ "id": "f_router", "name": "router table", "looks_like": "f_machinery_light", - "description": "This table has an inset router, a rotating motor with an exchangeable blade head for cutting specific profiles and grooves and stuff.", + "description": "This table has an inset router, a rotating motor with an exchangeable blade head for cutting specific profiles and grooves and stuff, if supplied with power from UPS.", "symbol": "7", "color": "yellow_green", "move_cost_mod": 8, @@ -697,7 +697,7 @@ "id": "f_planer", "name": "planer", "looks_like": "f_machinery_light", - "description": "A hefty tool that will take in a board and cut it smooth and flat to a specific width. Particularly great if working with raw lumber stock, but also good just for shaving wood down to size.", + "description": "A hefty tool that will take in a board and cut it smooth and flat to a specific width, if supplied with power from UPS. Particularly great if working with raw lumber stock, but also good just for shaving wood down to size.", "symbol": "7", "color": "yellow_white", "move_cost_mod": -1, @@ -735,7 +735,7 @@ "id": "f_jointer", "name": "jointer", "looks_like": "f_machinery_light", - "description": "A table-shaped tool with a rotating blade that will cut down, smooth out, and square off a board to make it very smooth and nice indeed.", + "description": "A table-shaped tool with a rotating blade that will cut down, smooth out, and square off a board to make it very smooth and nice indeed, if supplied with power from UPS.", "symbol": "7", "color": "yellow_magenta", "move_cost_mod": 8, @@ -773,7 +773,7 @@ "id": "f_hydraulic_press", "name": "hydraulic press", "looks_like": "f_machinery_light", - "description": "If you really want to squash something a lot, this would be exactly the right industrial tool for you. If, you know, it had power.", + "description": "If you really want to squash something a lot, this would be exactly the right industrial tool for you, if supplied with power from UPS.", "symbol": "9", "color": "black_red", "move_cost_mod": -1, @@ -811,7 +811,7 @@ "id": "f_heavy_lathe", "name": "power lathe", "looks_like": "f_machinery_light", - "description": "An industrial-grade lathe, for turning chunks of metal and other hard things into round chunks of metal and other hard things.", + "description": "An industrial-grade lathe, for turning chunks of metal and other hard things into round chunks of metal and other hard things, if supplied with power from UPS.", "symbol": "4", "color": "cyan_red", "move_cost_mod": -1, @@ -846,7 +846,7 @@ "id": "f_air_compressor", "name": "air compressor", "looks_like": "f_standing_tank", - "description": "This durable tank is topped with a motor that will cram as much air into the tank as possible.", + "description": "This durable tank is topped with a motor that will cram as much air into the tank as possible, if supplied with power from UPS.", "symbol": "8", "color": "black_yellow", "move_cost_mod": -1, diff --git a/data/json/items/armor/coats.json b/data/json/items/armor/coats.json index 5684cb22222a1..9c46b90eea6f0 100644 --- a/data/json/items/armor/coats.json +++ b/data/json/items/armor/coats.json @@ -1299,7 +1299,7 @@ { "id": "kimono", "type": "ARMOR", - "name": { "str": "kimono" }, + "name": { "str_sp": "kimono" }, "description": "A traditional, ankle-length Japanese robe, wrapped around the body with a sash.", "weight": "1200 g", "volume": "5 L", diff --git a/data/json/items/armor/helmets.json b/data/json/items/armor/helmets.json index 2ed565e6b4ef9..5d09928392454 100644 --- a/data/json/items/armor/helmets.json +++ b/data/json/items/armor/helmets.json @@ -306,12 +306,12 @@ "type": "ARMOR", "category": "armor", "name": { "str": "football helmet" }, - "description": "A heavy plastic helmet normally worn by football players.", + "description": "A heavy polycarbonate helmet normally worn by football players.", "weight": "1360 g", "volume": "2250 ml", "price": 19800, "price_postapoc": 250, - "material": [ "plastic" ], + "material": [ "thermo_resin" ], "symbol": "[", "looks_like": "helmet_motor", "color": "yellow", @@ -358,7 +358,7 @@ "id": "helmet_kabuto", "type": "ARMOR", "category": "armor", - "name": { "str": "kabuto" }, + "name": { "str_sp": "kabuto" }, "//": "Leaving this at $500 as it's either antique/priceless or player-made/Bartered, leastwise on the initial search.", "description": "A medieval Japanese helmet with a scowling facemask that provides excellent protection to the entire head and face.", "weight": "1625 g", diff --git a/data/json/items/armor/torso_armor.json b/data/json/items/armor/torso_armor.json index 9a5b479a3a504..e25f02c205922 100644 --- a/data/json/items/armor/torso_armor.json +++ b/data/json/items/armor/torso_armor.json @@ -310,12 +310,12 @@ "type": "ARMOR", "category": "armor", "name": { "str": "football armor" }, - "description": "Heavy plastic armor for your upper torso. Normally worn by football players.", + "description": "Heavy polycarbonate armor for your upper torso. Normally worn by football players.", "weight": "2810 g", "volume": "7 L", "price": 11000, "price_postapoc": 250, - "material": [ "plastic", "cotton" ], + "material": [ "thermo_resin", "cotton" ], "symbol": "[", "looks_like": "chestguard_hard", "color": "light_gray", diff --git a/data/json/items/comestibles/med.json b/data/json/items/comestibles/med.json index 3611f2c72db82..619ee03a3ad57 100644 --- a/data/json/items/comestibles/med.json +++ b/data/json/items/comestibles/med.json @@ -12,7 +12,7 @@ "stack_size": 30, "symbol": "!", "color": "yellow", - "flags": [ "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "use_action": { "type": "consume_drug", "activation_message": "You pop a melatonin tablet.", @@ -37,7 +37,7 @@ "fun": 10, "addiction_potential": 10, "addiction_type": "amphetamine", - "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "use_action": { "type": "consume_drug", "activation_message": "You take some stimulants." } }, { @@ -93,7 +93,7 @@ "symbol": "!", "color": "white", "use_action": [ "ANTIBIOTIC" ], - "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE" ] + "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ] }, { "id": "antifungal", @@ -111,7 +111,7 @@ "symbol": "!", "color": "white", "use_action": [ "ANTIFUNGAL" ], - "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE" ] + "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ] }, { "id": "antiparasitic", @@ -130,7 +130,7 @@ "color": "white", "healthy": -5, "use_action": [ "ANTIPARASITIC" ], - "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE" ] + "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ] }, { "id": "aspirin", @@ -148,6 +148,7 @@ "symbol": "!", "color": "white", "healthy": -1, + "flags": [ "WATER_DISSOLVE" ], "use_action": { "type": "consume_drug", "activation_message": "You take some aspirin.", @@ -218,7 +219,7 @@ "symbol": "!", "color": "white", "container": "bottle_plastic_small", - "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "use_action": { "type": "heal", "disinfectant_power": 4, "bite": 0.95, "move_cost": 100 } }, { @@ -256,6 +257,7 @@ "material": [ "powder" ], "symbol": "!", "color": "cyan", + "flags": [ "WATER_DISSOLVE" ], "stim": 12, "addiction_potential": 3, "addiction_type": "caffeine", @@ -301,7 +303,7 @@ "color": "white", "phase": "liquid", "container": "bottle_plastic_small", - "flags": [ "NO_INGEST" ], + "flags": [ "NO_INGEST", "WATER_DISSOLVE" ], "use_action": { "type": "heal", "disinfectant_power": 4, "bite": 0.95, "move_cost": 100 } }, { @@ -380,7 +382,7 @@ "color": "cyan", "stim": -2, "fun": 10, - "flags": [ "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "addiction_potential": 10, "addiction_type": "opiate", "use_action": { @@ -410,7 +412,7 @@ "fun": 25, "addiction_potential": 50, "addiction_type": "cocaine", - "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "use_action": [ "COKE" ] }, { @@ -437,7 +439,7 @@ "volume": "250 ml", "phase": "liquid", "charges": 4, - "flags": [ "EATEN_COLD" ], + "flags": [ "EATEN_COLD", "WATER_DISSOLVE" ], "freezing_point": -45, "fun": 30 }, @@ -499,7 +501,7 @@ "fun": 50, "addiction_potential": 80, "addiction_type": "crack", - "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "use_action": { "type": "consume_drug", "activation_message": "You smoke your crack rocks. Mother would be proud.", @@ -526,7 +528,7 @@ "phase": "liquid", "container": "bottle_plastic_small", "use_action": [ "FLUMED" ], - "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE" ] + "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ] }, { "id": "disinfectant", @@ -543,7 +545,7 @@ "color": "light_cyan", "phase": "liquid", "container": "bottle_plastic_small", - "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "use_action": { "type": "heal", "disinfectant_power": 4, "bite": 0.95, "move_cost": 100 } }, { @@ -553,7 +555,7 @@ "price_postapoc": 250, "copy-from": "disinfectant", "description": "Makeshift antiseptic made from ethanol. Can be used to disinfect a wound.", - "flags": [ "NO_INGEST" ], + "flags": [ "NO_INGEST", "WATER_DISSOLVE" ], "use_action": { "type": "heal", "disinfectant_power": 3, "bite": 0.95, "move_cost": 100 } }, { @@ -573,7 +575,7 @@ "stim": -8, "healthy": -1, "fun": 8, - "flags": [ "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "addiction_potential": 20, "addiction_type": "diazepam", "use_action": [ "ANTICONVULSANT" ] @@ -1083,7 +1085,7 @@ "fun": 50, "addiction_potential": 75, "addiction_type": "opiate", - "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "use_action": { "type": "consume_drug", "activation_message": "You shoot up.", @@ -1197,7 +1199,7 @@ "fun": 30, "addiction_potential": 50, "addiction_type": "amphetamine", - "flags": [ "NO_INGEST" ], + "flags": [ "NO_INGEST", "WATER_DISSOLVE" ], "use_action": [ "METH" ] }, { @@ -1333,7 +1335,7 @@ "color": "light_red", "stim": -8, "fun": 5, - "flags": [ "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "addiction_potential": 40, "addiction_type": "sleeping pill", "use_action": { @@ -1467,7 +1469,7 @@ "charges": 10, "stack_size": 200, "material": [ "powder" ], - "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "symbol": "!", "color": "blue", "use_action": { @@ -1492,7 +1494,7 @@ "symbol": "!", "color": "light_gray", "container": "bag_plastic", - "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "NO_INGEST", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "use_action": { "type": "heal", "bleed": 20, "move_cost": 100 } }, { @@ -1528,7 +1530,7 @@ "symbol": "!", "color": "light_red", "stim": -30, - "flags": [ "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "use_action": [ "THORAZINE" ] }, { @@ -1635,7 +1637,7 @@ "stack_size": 20, "symbol": "!", "color": "magenta", - "flags": [ "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "use_action": { "type": "consume_drug", "activation_message": "You take the %s.", @@ -1914,7 +1916,7 @@ "color": "white", "healthy": -2, "use_action": [ "WEAK_ANTIBIOTIC" ], - "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE" ] + "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ] }, { "id": "pepto", @@ -1958,7 +1960,7 @@ "activation_message": "You feel AMAZING!", "effects": [ { "id": "panacea", "duration": "1 m" }, { "id": "cureall" } ] }, - "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE" ] + "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ] }, { "id": "cattail_jelly", @@ -1976,7 +1978,7 @@ "phase": "liquid", "symbol": "~", "color": "light_blue", - "flags": [ "NO_INGEST" ], + "flags": [ "NO_INGEST", "WATER_DISSOLVE" ], "use_action": { "type": "heal", "disinfectant_power": 3, @@ -2000,7 +2002,7 @@ "symbol": "!", "color": "white", "healthy": -10, - "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE" ], + "flags": [ "NPC_SAFE", "IRREPLACEABLE_CONSUMABLE", "WATER_DISSOLVE" ], "use_action": { "type": "STRONG_ANTIBIOTIC" } }, { @@ -2021,7 +2023,7 @@ "container": "bottle_plastic_small", "addiction_potential": 5, "addiction_type": "cocaine", - "flags": [ "NO_INGEST" ], + "flags": [ "NO_INGEST", "WATER_DISSOLVE" ], "use_action": { "type": "heal", "bleed": 20, "move_cost": 100, "effects": [ { "id": "pkill1", "duration": 720 } ] } } ] diff --git a/data/json/items/fake.json b/data/json/items/fake.json index f5c472aeb03b5..1057836dd0fab 100644 --- a/data/json/items/fake.json +++ b/data/json/items/fake.json @@ -191,7 +191,7 @@ "id": "fake_drill_press", "type": "TOOL", "copy-from": "fake_power_tool", - "name": { "str": "drill press" } + "name": { "str": "drill press", "str_pl": "drill presses" } }, { "id": "fake_tablesaw", @@ -233,7 +233,7 @@ "id": "fake_hydraulic_press", "type": "TOOL", "copy-from": "fake_power_tool", - "name": { "str": "hydraulic press" } + "name": { "str": "hydraulic press", "str_pl": "hydraulic presses" } }, { "id": "fake_power_lathe", diff --git a/data/json/items/generic.json b/data/json/items/generic.json index cd71b557f1f53..77d6d9076bcdb 100644 --- a/data/json/items/generic.json +++ b/data/json/items/generic.json @@ -3026,7 +3026,7 @@ "id": "disassembly", "symbol": "%", "color": "white", - "name": { "str": "in progress disassembly" }, + "name": { "str": "in progress disassembly", "str_pl": "in progress disassemblies" }, "description": "This is an in progress disassembly.", "price": 0, "price_postapoc": 0, diff --git a/data/json/items/generic/toys_and_sports.json b/data/json/items/generic/toys_and_sports.json index d39878db21e59..cdfcd94d61c30 100644 --- a/data/json/items/generic/toys_and_sports.json +++ b/data/json/items/generic/toys_and_sports.json @@ -13,6 +13,7 @@ "symbol": "|", "color": "pink", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "charges_per_use": 1, "turns_per_charge": 20, "use_action": [ "DOLLCHAT" ], @@ -48,6 +49,7 @@ "symbol": "|", "color": "pink", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "charges_per_use": 1, "turns_per_charge": 20, "use_action": [ "DOLLCHAT" ], diff --git a/data/json/items/items_holiday.json b/data/json/items/items_holiday.json index 3e161fd33e76a..2262038a1f7f8 100644 --- a/data/json/items/items_holiday.json +++ b/data/json/items/items_holiday.json @@ -16,6 +16,7 @@ "initial_charges": 100, "max_charges": 100, "charges_per_use": 1, + "flags": [ "WATER_BREAK" ], "ammo": [ "battery" ], "use_action": { "target": "plastic_jack_o_lantern_lit", diff --git a/data/json/items/magazine/45.json b/data/json/items/magazine/45.json index 5ed6563fac0a8..07f326948aa25 100644 --- a/data/json/items/magazine/45.json +++ b/data/json/items/magazine/45.json @@ -174,8 +174,8 @@ "id": "ppq45mag", "looks_like": "glock17_17", "type": "MAGAZINE", - "name": { "str": "PPQ .45 12-round amgazine" }, - "description": "A 12 round steel box magazine for the PPQ pistol", + "name": { "str": "PPQ .45 12-round magazine" }, + "description": "A 12 round steel box magazine for the PPQ pistol.", "variants": [ { "id": "ppq45mag", diff --git a/data/json/items/melee/bludgeons.json b/data/json/items/melee/bludgeons.json index 7d6f391c00a16..7aebba2534615 100644 --- a/data/json/items/melee/bludgeons.json +++ b/data/json/items/melee/bludgeons.json @@ -211,7 +211,7 @@ "type": "GENERIC", "symbol": "/", "color": "brown", - "name": { "str": "bokken" }, + "name": { "str_sp": "bokken" }, "description": "This is a solid wood 'training' katana, exactingly crafted to mimic the weight and balance of the real thing. Despite its lack of a sharp metal edge, it's still quite capable of inflicting deadly wounds.", "price": 55000, "price_postapoc": 2500, @@ -234,7 +234,7 @@ "symbol": "/", "color": "brown", "looks_like": "bokken", - "name": { "str": "bokken" }, + "name": { "str_sp": "bokken" }, "description": "This is a solid wood 'training' katana, but feels far too light to make an effective weapon.", "price": 12500, "price_postapoc": 500, @@ -255,7 +255,7 @@ "symbol": "/", "color": "brown", "looks_like": "bokken", - "name": { "str": "bokken" }, + "name": { "str_sp": "bokken" }, "description": "This is a solid wood 'training' katana, but it looks to be mass-produced, and not quite as effective as the real deal.", "price": 12500, "price_postapoc": 750, @@ -1028,7 +1028,7 @@ "qualities": [ [ "HAMMER", 1 ] ], "techniques": [ "WBLOCK_2", "RAPID", "SWEEP" ], "use_action": [ "TAZER" ], - "flags": [ "NONCONDUCTIVE", "SHEATH_SPEAR", "ALWAYS_TWOHAND" ], + "flags": [ "NONCONDUCTIVE", "SHEATH_SPEAR", "ALWAYS_TWOHAND", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", diff --git a/data/json/items/melee/misc.json b/data/json/items/melee/misc.json index 9824040db4f42..abb7f6f400213 100644 --- a/data/json/items/melee/misc.json +++ b/data/json/items/melee/misc.json @@ -70,6 +70,7 @@ "symbol": ";", "color": "dark_gray", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "charges_per_use": 100, "use_action": [ "TAZER" ], "pocket_data": [ diff --git a/data/json/items/melee/spears_and_polearms.json b/data/json/items/melee/spears_and_polearms.json index 442d787531328..bf9f983455aa2 100644 --- a/data/json/items/melee/spears_and_polearms.json +++ b/data/json/items/melee/spears_and_polearms.json @@ -114,7 +114,7 @@ "id": "spear_spike", "type": "TOOL", "category": "weapons", - "name": { "str": "spike on a stick" }, + "name": { "str": "spike on a stick", "str_pl": "spikes on sticks" }, "description": "A flimsy pole made of wood with a basic metal spike tied to it. It's barely sharp, and crudely constructed, but it will keep the zombies out of arm's reach until you can find something better.", "weight": "1487 g", "volume": "1250 ml", @@ -284,7 +284,7 @@ { "type": "GENERIC", "id": "qiang", - "name": { "str": "qiang" }, + "name": { "str_sp": "qiang" }, "description": "An ancient Chinese spear, typically with a tassel just below the spearhead. One of the four major weapons in folklore, alongside the dao sabre, jian sword, and gun staff.", "weight": "1398 g", "price_postapoc": 3500, diff --git a/data/json/items/melee/swords_and_blades.json b/data/json/items/melee/swords_and_blades.json index e185acb8dfde3..b5982c4e9fc21 100644 --- a/data/json/items/melee/swords_and_blades.json +++ b/data/json/items/melee/swords_and_blades.json @@ -733,7 +733,7 @@ "id": "sword_xiphos", "type": "TOOL", "category": "weapons", - "name": { "str": "xiphos", "str_pl": "xiphoses" }, + "name": { "str": "xiphos", "str_pl": "xiphe" }, "description": "A bronze sword of ancient Greek origin, wielded as a sidearm to the dory spear.", "weight": "800 g", "volume": "2 L", @@ -851,7 +851,7 @@ "id": "tanto", "type": "TOOL", "category": "weapons", - "name": { "str": "tanto" }, + "name": { "str_sp": "tanto" }, "description": "Long Japanese knives like this more-modern remake were the samurai's backup weapon, before the advent of the larger wakizashi. It's still a deadly blade, even if it's smaller than its more famous relatives.", "weight": "558 g", "volume": "500 ml", @@ -956,7 +956,7 @@ "id": "tanto_fake", "type": "GENERIC", "category": "weapons", - "name": { "str": "tanto" }, + "name": { "str_sp": "tanto" }, "description": "This is a dull, cheaply-made replica of a long Japanese knife, typically used as a samurai's backup weapon.", "weight": "374 g", "volume": "500 ml", @@ -978,7 +978,7 @@ "id": "tanto_inferior", "type": "GENERIC", "category": "weapons", - "name": { "str": "tanto" }, + "name": { "str_sp": "tanto" }, "description": "Long Japanese knives like this more-modern remake were the samurai's backup weapon, before the advent of the larger wakizashi. This one doesn't feel well-balanced.", "weight": "3 g", "volume": "500 ml", @@ -1147,7 +1147,7 @@ "charges_per_use": 100, "price_postapoc": 1250, "use_action": [ "TAZER" ], - "extend": { "flags": [ "NONCONDUCTIVE" ] }, + "extend": { "flags": [ "NONCONDUCTIVE", "WATER_BREAK" ] }, "relative": { "volume": "250 ml", "weight": "151 g" }, "pocket_data": [ { @@ -1171,7 +1171,7 @@ "price_postapoc": 1250, "charges_per_use": 100, "use_action": [ "TAZER" ], - "extend": { "flags": [ "NONCONDUCTIVE" ] }, + "extend": { "flags": [ "NONCONDUCTIVE", "WATER_BREAK" ] }, "relative": { "volume": "250 ml", "weight": "151 g" }, "pocket_data": [ { @@ -1195,7 +1195,7 @@ "price_postapoc": 1250, "charges_per_use": 100, "use_action": [ "TAZER" ], - "extend": { "flags": [ "NONCONDUCTIVE" ] }, + "extend": { "flags": [ "NONCONDUCTIVE", "WATER_BREAK" ] }, "relative": { "volume": "250 ml", "weight": "151 g" }, "pocket_data": [ { @@ -1362,7 +1362,7 @@ "id": "katana", "type": "TOOL", "category": "weapons", - "name": { "str": "katana" }, + "name": { "str_sp": "katana" }, "description": "This is a rare sword from Japan. Deadly against unarmored targets, and still very effective against armor.", "weight": "1133 g", "volume": "2 L", @@ -1385,7 +1385,7 @@ "type": "GENERIC", "symbol": "/", "color": "light_gray", - "name": { "str": "katana" }, + "name": { "str_sp": "katana" }, "description": "This is a dull, cheaply-made replica of a rare sword from Japan.", "price": 5000, "price_postapoc": 50, @@ -1406,7 +1406,7 @@ "type": "TOOL", "symbol": "/", "color": "light_gray", - "name": { "str": "katana" }, + "name": { "str_sp": "katana" }, "description": "This is a rare sword from Japan. While it's got the right edge and weight, the pommel just snaps off, and the blade seems pretty worn.", "price": 98000, "price_postapoc": 250, @@ -1705,7 +1705,7 @@ "charges_per_use": 5, "use_action": [ "E_COMBATSAW_OFF" ], "techniques": [ "WBLOCK_1", "SWEEP" ], - "flags": [ "ALWAYS_TWOHAND", "FRAGILE_MELEE" ], + "flags": [ "ALWAYS_TWOHAND", "FRAGILE_MELEE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", diff --git a/data/json/items/resources/plastic.json b/data/json/items/resources/plastic.json index 7e3a7277c5d8d..1193003433b57 100644 --- a/data/json/items/resources/plastic.json +++ b/data/json/items/resources/plastic.json @@ -1,4 +1,34 @@ [ + { + "id": "thermo_resin_chunk", + "type": "TOOL", + "category": "spare_parts", + "name": { "str": "thermoplastic resin chunk" }, + "description": "This piece of tough thermoplastic resin could allow you to fabricate, repair, or reinforce items.", + "weight": "50 g", + "volume": "250 ml", + "price": 0, + "price_postapoc": 10, + "material": [ "thermo_resin" ], + "symbol": ",", + "color": "light_blue", + "flags": [ "NO_SALVAGE" ] + }, + { + "id": "epoxy_chunk", + "type": "TOOL", + "category": "spare_parts", + "name": { "str": "epoxy chunk" }, + "description": "This piece of epoxy is hardy and durable. You could potentially craft items with it.", + "weight": "50 g", + "volume": "250 ml", + "price": 0, + "price_postapoc": 10, + "material": [ "epoxy" ], + "symbol": ",", + "color": "light_blue", + "flags": [ "NO_SALVAGE" ] + }, { "id": "plastic_chunk", "type": "TOOL", diff --git a/data/json/items/tool/cooking.json b/data/json/items/tool/cooking.json index 1113ef3e9c028..2f4b3420d4869 100644 --- a/data/json/items/tool/cooking.json +++ b/data/json/items/tool/cooking.json @@ -86,7 +86,7 @@ "ammo": [ "battery" ], "charges_per_use": 5, "use_action": [ "CARVER_OFF" ], - "flags": [ "SHEATH_SWORD", "NONCONDUCTIVE" ], + "flags": [ "SHEATH_SWORD", "NONCONDUCTIVE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -245,6 +245,7 @@ "symbol": ";", "color": "light_gray", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "charges_per_use": 10, "qualities": [ [ "BOIL", 1 ] ], "use_action": [ "HOTPLATE" ], @@ -274,7 +275,7 @@ "symbol": ";", "color": "blue", "ammo": [ "battery" ], - "flags": [ "ALLOWS_REMOTE_USE" ], + "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -377,7 +378,7 @@ "symbol": "%", "color": "white", "ammo": [ "battery" ], - "flags": [ "ALLOWS_REMOTE_USE" ], + "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -480,6 +481,7 @@ "material": [ "aluminum", "plastic" ], "symbol": ";", "color": "green", + "flags": [ "WATER_BREAK" ], "ammo": [ "battery" ], "charges_per_use": 5, "use_action": [ "HOTPLATE" ], @@ -510,7 +512,7 @@ "symbol": ";", "color": "white", "ammo": [ "battery" ], - "flags": [ "ALLOWS_REMOTE_USE" ], + "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -538,6 +540,7 @@ "symbol": ";", "color": "green", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "sub": "hotplate", "charges_per_use": 5, "qualities": [ [ "COOK", 2 ], [ "BOIL", 2 ], [ "CONTAIN", 1 ] ], @@ -674,6 +677,7 @@ "symbol": ";", "color": "red", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "power_draw": 1500000, "qualities": [ [ "CONTAIN", 1 ] ], "use_action": [ "MULTICOOKER" ], @@ -956,7 +960,7 @@ "symbol": ";", "color": "white", "ammo": [ "battery" ], - "flags": [ "ALLOWS_REMOTE_USE" ], + "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", diff --git a/data/json/items/tool/electronics.json b/data/json/items/tool/electronics.json index 429e0ef6d3453..e9533b04e7c47 100644 --- a/data/json/items/tool/electronics.json +++ b/data/json/items/tool/electronics.json @@ -39,6 +39,7 @@ "symbol": ";", "color": "yellow", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "charges_per_use": 5, "use_action": [ "CAMERA" ], "pocket_data": [ @@ -76,7 +77,7 @@ "ammo": [ "battery" ], "charges_per_use": 5, "use_action": [ "CAMERA" ], - "flags": [ "CAMERA_PRO", "ALWAYS_TWOHAND" ], + "flags": [ "CAMERA_PRO", "ALWAYS_TWOHAND", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -118,7 +119,7 @@ "need_charges_msg": "The cellphone's batteries need more charge.", "type": "transform" }, - "flags": [ "WATCH", "ALARMCLOCK" ], + "flags": [ "WATCH", "ALARMCLOCK", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -146,7 +147,7 @@ "power_draw": 500, "revert_to": "cell_phone", "use_action": { "target": "cell_phone", "msg": "You stop lighting up the screen.", "menu_text": "Turn off", "type": "transform" }, - "flags": [ "WATCH", "LIGHT_8", "CHARGEDIM", "TRADER_AVOID" ] + "flags": [ "WATCH", "LIGHT_8", "CHARGEDIM", "TRADER_AVOID", "WATER_BREAK" ] }, { "id": "directional_antenna", @@ -181,7 +182,7 @@ "max_charges": 3000, "turns_per_charge": 1, "use_action": [ "EHANDCUFFS" ], - "flags": [ "ALWAYS_TWOHAND", "UNARMED_WEAPON", "TRADER_AVOID" ] + "flags": [ "ALWAYS_TWOHAND", "UNARMED_WEAPON", "TRADER_AVOID", "WATER_BREAK" ] }, { "id": "eink_tablet_pc", @@ -200,7 +201,7 @@ "ammo": [ "battery" ], "charges_per_use": 1, "use_action": [ "EINKTABLETPC" ], - "flags": [ "WATCH" ], + "flags": [ "WATCH", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -237,6 +238,7 @@ "symbol": ",", "color": "green", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -366,7 +368,7 @@ "type": "transform" } ], - "flags": [ "WATCH" ], + "flags": [ "WATCH", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -386,7 +388,7 @@ "power_draw": 1000, "revert_to": "laptop", "use_action": { "target": "laptop", "msg": "You stop lighting up the screen.", "menu_text": "Turn off", "type": "transform" }, - "flags": [ "WATCH", "LIGHT_10", "TRADER_AVOID" ] + "flags": [ "WATCH", "LIGHT_10", "TRADER_AVOID", "WATER_BREAK" ] }, { "id": "mp3", @@ -404,6 +406,7 @@ "ammo": [ "battery" ], "use_action": [ "MP3" ], "charges_per_use": 1, + "flags": [ "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -432,7 +435,7 @@ "power_draw": 1000, "revert_to": "mp3", "use_action": [ "MP3_ON" ], - "flags": [ "TRADER_AVOID" ] + "flags": [ "TRADER_AVOID", "WATER_BREAK" ] }, { "id": "noise_emitter", @@ -451,7 +454,7 @@ "ammo": [ "battery" ], "charges_per_use": 1, "use_action": [ "NOISE_EMITTER_OFF" ], - "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC" ], + "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -480,7 +483,7 @@ "power_draw": 10000, "revert_to": "noise_emitter", "use_action": [ "NOISE_EMITTER_ON" ], - "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "TRADER_AVOID" ] + "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "TRADER_AVOID", "WATER_BREAK" ] }, { "id": "portable_game", @@ -496,6 +499,7 @@ "color": "light_gray", "ammo": [ "battery" ], "charges_per_use": 1, + "flags": [ "WATER_BREAK" ], "use_action": [ "PORTABLE_GAME" ], "pocket_data": [ { @@ -546,7 +550,7 @@ "CAMERA", "MP3" ], - "flags": [ "WATCH", "ALARMCLOCK", "USE_UPS", "NO_UNLOAD", "NO_RELOAD" ] + "flags": [ "WATCH", "ALARMCLOCK", "USE_UPS", "NO_UNLOAD", "NO_RELOAD", "WATER_BREAK" ] }, { "id": "smartphone_music", @@ -557,7 +561,7 @@ "power_draw": 300, "revert_to": "smart_phone", "use_action": [ "MP3_ON" ], - "flags": [ "WATCH", "TRADER_AVOID", "ALARMCLOCK", "USE_UPS", "NO_UNLOAD", "NO_RELOAD" ] + "flags": [ "WATCH", "TRADER_AVOID", "ALARMCLOCK", "USE_UPS", "NO_UNLOAD", "NO_RELOAD", "WATER_BREAK" ] }, { "id": "smart_phone_flashlight", @@ -572,7 +576,7 @@ "menu_text": "Turn off flashlight", "type": "transform" }, - "flags": [ "WATCH", "LIGHT_20", "CHARGEDIM", "TRADER_AVOID", "ALARMCLOCK", "USE_UPS", "NO_UNLOAD", "NO_RELOAD" ] + "flags": [ "WATCH", "LIGHT_20", "CHARGEDIM", "TRADER_AVOID", "ALARMCLOCK", "USE_UPS", "NO_UNLOAD", "NO_RELOAD", "WATER_BREAK" ] }, { "id": "UPS_off", @@ -616,6 +620,7 @@ "ammo": [ "battery" ], "charges_per_use": 10, "use_action": [ "VIBE" ], + "flags": [ "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", diff --git a/data/json/items/tool/explosives.json b/data/json/items/tool/explosives.json index 6f2aebf46b195..d59dd53dc23f5 100644 --- a/data/json/items/tool/explosives.json +++ b/data/json/items/tool/explosives.json @@ -248,7 +248,7 @@ "menu_text": "Activate bomb", "type": "transform" }, - "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "BOMB", "GRENADE" ] + "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "BOMB", "GRENADE", "WATER_BREAK" ] }, { "id": "EMPbomb_act", @@ -280,7 +280,7 @@ "explosion": { "power": 600, "shrapnel": { "casing_mass": 3214, "fragment_mass": 0.5 } }, "no_deactivate_msg": "You've already activated the %s, try throwing it instead." }, - "flags": [ "TRADER_AVOID" ] + "flags": [ "TRADER_AVOID", "WATER_BREAK" ] }, { "id": "primitive_demolition_charge", @@ -703,7 +703,7 @@ "menu_text": "Pull pin", "type": "transform" }, - "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "BOMB", "GRENADE" ] + "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "BOMB", "GRENADE", "WATER_BREAK" ] }, { "id": "grenade_emp_act", @@ -734,7 +734,7 @@ "sound_msg": "Tick.", "no_deactivate_msg": "You've already pulled the %s's pin, try throwing it instead." }, - "flags": [ "TRADER_AVOID" ] + "flags": [ "TRADER_AVOID", "WATER_BREAK" ] }, { "id": "grenade_inc", diff --git a/data/json/items/tool/lighting.json b/data/json/items/tool/lighting.json index 9fa4dc2e878ba..4b2c7ef0f8489 100644 --- a/data/json/items/tool/lighting.json +++ b/data/json/items/tool/lighting.json @@ -152,7 +152,7 @@ "need_charges": 1, "need_charges_msg": "The lantern has no batteries." }, - "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "ALLOWS_REMOTE_USE" ], + "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -197,7 +197,7 @@ "price_postapoc": 100, "charges_per_use": 1, "ammo": [ "battery" ], - "flags": [ "BELT_CLIP" ], + "flags": [ "BELT_CLIP", "WATER_BREAK" ], "use_action": { "type": "transform", "msg": "You turn the flashlight on.", @@ -386,7 +386,7 @@ "color": "blue", "charges_per_use": 1, "ammo": [ "battery" ], - "flags": [ "BELT_CLIP" ], + "flags": [ "BELT_CLIP", "WATER_BREAK" ], "use_action": { "type": "transform", "msg": "You turn the heavy duty flashlight on.", @@ -584,6 +584,7 @@ "material": [ "plastic", "aluminum" ], "symbol": ";", "color": "white", + "flags": [ "WATER_BREAK" ], "ammo": [ "battery" ], "charges_per_use": 1, "use_action": { @@ -640,7 +641,7 @@ "need_charges_msg": "The smart lamp batteries are dead.", "type": "transform" }, - "flags": [ "ALLOWS_REMOTE_USE", "RADIO_ACTIVATION", "RADIO_INVOKE_PROC", "RADIOSIGNAL_2" ], + "flags": [ "ALLOWS_REMOTE_USE", "RADIO_ACTIVATION", "RADIO_INVOKE_PROC", "RADIOSIGNAL_2", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", diff --git a/data/json/items/tool/metalworking.json b/data/json/items/tool/metalworking.json index 1d1849cdccdfb..006ab9a810aad 100644 --- a/data/json/items/tool/metalworking.json +++ b/data/json/items/tool/metalworking.json @@ -159,7 +159,7 @@ "symbol": ";", "color": "light_gray", "ammo": [ "battery" ], - "flags": [ "ALLOWS_REMOTE_USE" ], + "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", diff --git a/data/json/items/tool/misc.json b/data/json/items/tool/misc.json index e64c0cafd7307..def8e6129e8e2 100644 --- a/data/json/items/tool/misc.json +++ b/data/json/items/tool/misc.json @@ -379,7 +379,7 @@ "symbol": ";", "color": "brown", "ammo": [ "battery" ], - "flags": [ "ALLOWS_REMOTE_USE" ], + "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "use_action": { "target": "large_space_heater_on", "msg": "You turn on the heater.", @@ -568,7 +568,7 @@ "symbol": ";", "color": "brown", "ammo": [ "battery" ], - "flags": [ "ALLOWS_REMOTE_USE" ], + "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "use_action": { "target": "small_space_heater_on", "msg": "You turn on the heater.", diff --git a/data/json/items/tool/pets.json b/data/json/items/tool/pets.json index aec3b78b1dab2..c04a3c5c9af6d 100644 --- a/data/json/items/tool/pets.json +++ b/data/json/items/tool/pets.json @@ -64,6 +64,7 @@ "color": "dark_gray", "qualities": [ [ "SHEAR", 3 ] ], "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "initial_charges": 100, "max_charges": 100, "charges_per_use": 25, diff --git a/data/json/items/tool/radio_tools.json b/data/json/items/tool/radio_tools.json index 9f571bed5699d..7e25102325688 100644 --- a/data/json/items/tool/radio_tools.json +++ b/data/json/items/tool/radio_tools.json @@ -7,6 +7,7 @@ "symbol": "#", "color": "yellow", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "charges_per_use": 1, "turns_per_charge": 5, "weight": "670 g", @@ -46,7 +47,7 @@ "price": 24000, "price_postapoc": 250, "use_action": [ "RADIOCAR" ], - "flags": [ "RADIO_CONTAINER" ], + "flags": [ "RADIO_CONTAINER", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "CONTAINER", @@ -106,6 +107,7 @@ "symbol": ";", "color": "light_gray", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "charges_per_use": 1, "use_action": [ "RADIO_OFF" ], "pocket_data": [ @@ -153,7 +155,7 @@ "color": "green", "ammo": [ "battery" ], "charges_per_use": 1, - "flags": [ "TWO_WAY_RADIO" ], + "flags": [ "TWO_WAY_RADIO", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -187,6 +189,7 @@ "symbol": "#", "color": "yellow", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "charges_per_use": 1, "turns_per_charge": 10, "use_action": [ "REMOTEVEH" ], diff --git a/data/json/items/tool/workshop.json b/data/json/items/tool/workshop.json index 886538ce7ae0e..a0d8acac22e75 100644 --- a/data/json/items/tool/workshop.json +++ b/data/json/items/tool/workshop.json @@ -77,7 +77,7 @@ "ammo": [ "battery" ], "charges_per_use": 1, "power_draw": 800000, - "flags": [ "NONCONDUCTIVE" ], + "flags": [ "NONCONDUCTIVE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -289,6 +289,7 @@ "symbol": ";", "color": "yellow", "ammo": [ "battery" ], + "flags": [ "WATER_BREAK" ], "qualities": [ [ "DRILL", 3 ], [ "SCREW", 1 ] ], "pocket_data": [ { @@ -340,7 +341,7 @@ "max_charges": 7000, "charges_per_use": 3500, "use_action": [ "JACKHAMMER" ], - "flags": [ "STAB", "DIG_TOOL", "POWERED", "USE_UPS", "NO_UNLOAD", "NO_RELOAD" ] + "flags": [ "STAB", "DIG_TOOL", "POWERED", "USE_UPS", "NO_UNLOAD", "NO_RELOAD", "WATER_BREAK" ] }, { "id": "hacksaw", @@ -505,7 +506,7 @@ "symbol": ";", "color": "dark_gray", "ammo": [ "battery" ], - "flags": [ "ALLOWS_REMOTE_USE" ], + "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -800,7 +801,7 @@ "symbol": ";", "color": "light_gray", "ammo": [ "battery" ], - "flags": [ "TRADER_AVOID" ], + "flags": [ "TRADER_AVOID", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -951,7 +952,7 @@ }, { "flame": false, "type": "cauterize" } ], - "flags": [ "SPEAR", "BELT_CLIP", "ALLOWS_REMOTE_USE" ], + "flags": [ "SPEAR", "BELT_CLIP", "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -1114,7 +1115,7 @@ "move_cost": 500 } ], - "flags": [ "ALLOWS_REMOTE_USE" ], + "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -1154,7 +1155,7 @@ "move_cost": 1000 } ], - "flags": [ "ALLOWS_REMOTE_USE" ], + "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", diff --git a/data/json/mapgen/basecamps/fbmc_sealab_small/fbmc_sealab_small_common.json b/data/json/mapgen/basecamps/fbmc_sealab_small/fbmc_sealab_small_common.json new file mode 100644 index 0000000000000..ac8b41f80dbfb --- /dev/null +++ b/data/json/mapgen/basecamps/fbmc_sealab_small/fbmc_sealab_small_common.json @@ -0,0 +1,8 @@ +[ + { + "type": "mapgen", + "update_mapgen_id": "fbmc_sealab_small_0", + "method": "json", + "object": { "set": [ { "point": "furniture", "id": "f_bulletin", "x": 10, "y": 12 } ] } + } +] diff --git a/data/json/mapgen/cave.json b/data/json/mapgen/cave.json index 7b64580e3501a..9eaa607df25d2 100644 --- a/data/json/mapgen/cave.json +++ b/data/json/mapgen/cave.json @@ -34,9 +34,9 @@ ], "terrain": { ".": [ [ "t_region_groundcover_forest", 4 ], [ "t_region_tree", 1 ] ], - "|": "t_soil", - ",": "t_dirt_underground", - "%": [ "t_soil", "t_region_groundcover_forest" ], + "|": "t_rock", + ",": "t_rock_roof", + "%": [ "t_rock", "t_region_groundcover_forest" ], ";": "t_dirt", "<": "t_slope_down" }, @@ -85,10 +85,10 @@ ], "terrain": { ".": [ [ "t_region_groundcover_forest", 4 ], [ "t_region_tree", 1 ] ], - "|": "t_soil", - "%": [ "t_soil", "t_region_groundcover_forest" ], + "|": "t_rock", + "%": [ "t_rock", "t_region_groundcover_forest" ], ";": "t_dirt", - ",": "t_dirt_underground", + ",": "t_rock_roof", "<": "t_slope_down" }, "furniture": { } @@ -129,11 +129,11 @@ ], "terrain": { ".": [ [ "t_region_groundcover_forest", 4 ], [ "t_region_tree", 1 ] ], - "|": "t_soil", - "%": [ "t_soil", "t_region_groundcover_forest" ], + "|": "t_rock", + "%": [ "t_rock", "t_region_groundcover_forest" ], ";": "t_dirt", - ",": "t_dirt_underground", - "W": "t_dirt_underground", + ",": "t_rock_roof", + "W": "t_rock_roof", "<": "t_slope_down" }, "furniture": { }, @@ -176,11 +176,11 @@ ], "terrain": { ".": [ [ "t_region_groundcover_forest", 4 ], [ "t_region_tree", 1 ] ], - "|": "t_soil", - "%": [ "t_soil", "t_region_groundcover_forest" ], + "|": "t_rock", + "%": [ "t_rock", "t_region_groundcover_forest" ], ";": "t_dirt", - ",": "t_dirt_underground", - "W": "t_dirt_underground", + ",": "t_rock_roof", + "W": "t_rock_roof", "<": "t_slope_down" }, "fields": { "W": { "field": "fd_web", "intensity": 1, "age": 10 } }, @@ -222,11 +222,11 @@ ], "terrain": { ".": [ [ "t_region_groundcover_forest", 4 ], [ "t_region_tree", 1 ] ], - "|": "t_soil", - "%": [ "t_soil", "t_region_groundcover_forest" ], + "|": "t_rock", + "%": [ "t_rock", "t_region_groundcover_forest" ], ";": "t_dirt", - ",": "t_dirt_underground", - "B": "t_dirt_underground", + ",": "t_rock_roof", + "B": "t_rock_roof", "<": "t_slope_down" }, "furniture": { }, @@ -268,12 +268,12 @@ ], "terrain": { ".": [ [ "t_region_groundcover_forest", 4 ], [ "t_region_tree", 1 ] ], - "|": "t_soil", - "%": [ "t_soil", "t_region_groundcover_forest" ], + "|": "t_rock", + "%": [ "t_rock", "t_region_groundcover_forest" ], ";": "t_dirt", - ",": "t_dirt_underground", - "B": "t_dirt_underground", - "W": "t_dirt_underground", + ",": "t_rock_roof", + "B": "t_rock_roof", + "W": "t_rock_roof", "<": "t_slope_down" }, "furniture": { }, @@ -316,12 +316,12 @@ ], "terrain": { ".": [ [ "t_region_groundcover_forest", 4 ], [ "t_region_tree", 1 ] ], - "|": "t_soil", - "%": [ "t_soil", "t_region_groundcover_forest" ], + "|": "t_rock", + "%": [ "t_rock", "t_region_groundcover_forest" ], ";": "t_dirt", - ",": "t_dirt_underground", - "B": "t_dirt_underground", - "W": "t_dirt_underground", + ",": "t_rock_roof", + "B": "t_rock_roof", + "W": "t_rock_roof", "<": "t_slope_down" }, "fields": { "W": { "field": "fd_web", "intensity": 1, "age": 10 } }, @@ -363,12 +363,12 @@ ], "terrain": { ".": [ [ "t_region_groundcover_forest", 4 ], [ "t_region_tree", 1 ] ], - "|": "t_soil", - "%": [ "t_soil", "t_region_groundcover_forest" ], + "|": "t_rock", + "%": [ "t_rock", "t_region_groundcover_forest" ], ";": "t_dirt", - ",": "t_dirt_underground", - "B": "t_dirt_underground", - "W": "t_dirt_underground", + ",": "t_rock_roof", + "B": "t_rock_roof", + "W": "t_rock_roof", "<": "t_slope_down" }, "furniture": { }, @@ -411,17 +411,17 @@ ], "terrain": { ".": [ [ "t_region_groundcover_forest", 4 ], [ "t_region_tree", 1 ] ], - "|": "t_soil", - "%": [ "t_soil", "t_region_groundcover_forest" ], - ";": "t_dirt", - "!": "t_dirt", - "?": "t_dirt", - "#": "t_dirt", - "~": "t_dirt", - "@": "t_dirt_underground", - ",": "t_dirt_underground", - "q": "t_dirt_underground", - "R": "t_dirt" + "|": "t_rock", + "%": [ "t_rock", "t_region_groundcover_forest" ], + ";": "t_rock_floor_no_roof", + "!": "t_rock_floor_no_roof", + "?": "t_rock_floor_no_roof", + "#": "t_rock_floor_no_roof", + "~": "t_rock_floor_no_roof", + "@": "t_rock_roof", + ",": "t_rock_roof", + "q": "t_rock_roof", + "R": "t_rock_floor_no_roof" }, "furniture": { "R": [ [ "f_boulder_small", 1 ], [ "f_null", 10 ] ] }, "item": { @@ -479,7 +479,7 @@ "............,...........", "............>..........." ], - "terrain": { ".": "t_soil", ",": "t_dirt", "W": "t_dirt", ">": "t_slope_up", "~": "t_water_dp" }, + "terrain": { ".": "t_soil", ",": "t_dirt_underground", "W": "t_dirt_underground", ">": "t_slope_up", "~": "t_water_dp" }, "furniture": { "W": "f_boulder_small" }, "item": { "W": { "item": "longsword", "chance": 100 } }, "monster": { @@ -497,7 +497,7 @@ "om_terrain": [ "cave_underground" ], "weight": 1000, "object": { - "fill_ter": "t_dirt", + "fill_ter": "t_dirt_underground", "rotation": [ 0, 3 ], "rows": [ "........................", @@ -525,7 +525,7 @@ "............,...........", "............>..........." ], - "terrain": { ".": "t_soil", ",": "t_dirt", ">": "t_slope_up" }, + "terrain": { ".": "t_soil", ",": "t_dirt_underground", ">": "t_slope_up" }, "furniture": { }, "items": { "~": [ { "item": "monparts", "chance": 2 }, { "item": "trash_forest", "chance": 2 } ] } } @@ -536,7 +536,7 @@ "om_terrain": [ "cave_underground" ], "weight": 800, "object": { - "fill_ter": "t_dirt", + "fill_ter": "t_dirt_underground", "rotation": [ 0, 3 ], "rows": [ "........................", @@ -564,7 +564,7 @@ "............ ...........", "............>..........." ], - "terrain": { ".": "t_soil", " ": "t_dirt", ">": "t_slope_up" }, + "terrain": { ".": "t_soil", " ": "t_dirt_underground", ">": "t_slope_up" }, "furniture": { }, "monster": { "M": { "monster": "mon_nakedmolerat_giant" }, " ": { "monster": "mon_nakedmolerat_giant", "chance": 2 } } } @@ -575,7 +575,7 @@ "om_terrain": [ "cave_underground" ], "weight": 500, "object": { - "fill_ter": "t_dirt", + "fill_ter": "t_dirt_underground", "rotation": [ 0, 3 ], "rows": [ "........................", @@ -603,7 +603,7 @@ "...........,,,..........", "............>..........." ], - "terrain": { ".": "t_soil", ",": "t_dirt", "!": "t_dirt", ">": "t_slope_up" }, + "terrain": { ".": "t_soil", ",": "t_dirt_underground", "!": "t_dirt_underground", ">": "t_slope_up" }, "furniture": { "C": "f_crate_c", "S": "f_utility_shelf", "G": [ "f_grave_stone_old", "f_grave_head", "f_grave_monument" ] }, "item": { "!": { "item": "material_soil", "chance": 40, "amount": [ 5, 10 ] }, @@ -618,7 +618,7 @@ "om_terrain": [ "cave_underground" ], "weight": 250, "object": { - "fill_ter": "t_dirt", + "fill_ter": "t_dirt_underground", "rotation": [ 0, 3 ], "rows": [ "........................", @@ -646,7 +646,7 @@ "............ ....F.7J G.", "............>..........." ], - "terrain": { ".": "t_soil", " ": "t_dirt", ">": "t_slope_up" }, + "terrain": { ".": "t_soil", " ": "t_dirt_underground", ">": "t_slope_up" }, "furniture": { "f": "f_firering", "c": "f_camp_chair", @@ -688,7 +688,7 @@ "om_terrain": [ "cave_underground" ], "weight": 700, "object": { - "fill_ter": "t_dirt", + "fill_ter": "t_dirt_underground", "rotation": [ 0, 3 ], "rows": [ "........................", @@ -716,7 +716,7 @@ "........... 2 ..........", "............>..........." ], - "terrain": { ".": "t_soil", " ": "t_dirt", ">": "t_slope_up", "2": "t_railroad_track_small" }, + "terrain": { ".": "t_soil", " ": "t_dirt_underground", ">": "t_slope_up", "2": "t_railroad_track_small" }, "furniture": { }, "item": { "1": { "item": "broken_molebot", "chance": 2 } }, "items": { "1": { "item": "cave_minerals", "chance": 40, "repeat": [ 1, 3 ] } }, @@ -728,7 +728,7 @@ "method": "json", "om_terrain": [ "cave_rat_underground" ], "object": { - "fill_ter": "t_dirt", + "fill_ter": "t_dirt_underground", "rotation": [ 0, 3 ], "rows": [ "1...........2...........", @@ -756,7 +756,7 @@ "...................... ", "...................... <" ], - "terrain": { ".": "t_soil", " ": "t_dirt", ">": "t_slope_up", "<": "t_slope_down" }, + "terrain": { ".": "t_soil", " ": "t_dirt_underground", ">": "t_slope_up", "<": "t_slope_down" }, "furniture": { }, "nested": { "1": { "chunks": [ "cave_nw" ] }, @@ -771,7 +771,7 @@ "method": "json", "om_terrain": [ "cave_rat" ], "object": { - "fill_ter": "t_dirt", + "fill_ter": "t_rock_floor", "rotation": [ 0, 3 ], "rows": [ "........................", @@ -800,10 +800,10 @@ "........................" ], "terrain": { - " ": "t_dirt", - ".": "t_soil", - "|": [ [ "t_soil", 20 ], [ "t_dirt", 80 ] ], - "%": [ "t_soil", "t_dirt" ], + " ": "t_rock_floor", + ".": "t_rock", + "|": [ [ "t_rock", 20 ], [ "t_rock_floor", 80 ] ], + "%": [ "t_rock", "t_rock_floor" ], "<": "t_slope_up" }, "furniture": { }, diff --git a/data/json/mapgen/lmoe.json b/data/json/mapgen/lmoe.json index f90e7672acd5d..2480488317281 100644 --- a/data/json/mapgen/lmoe.json +++ b/data/json/mapgen/lmoe.json @@ -117,7 +117,7 @@ "//2": "This particular 4-bed shelter is definitely homemade, mostly carved out of bare rock, and seems to be incomplete.", "weight": 100, "object": { - "fill_ter": "t_dirt", + "fill_ter": "t_dirt_underground", "rows": [ "########################", "########################", diff --git a/data/json/materials.json b/data/json/materials.json index a826a3a0cef5e..01d6b18017c57 100644 --- a/data/json/materials.json +++ b/data/json/materials.json @@ -1,4 +1,51 @@ [ + { + "abstract": "generic_polymer_resin", + "type": "material", + "name": "Polymer resin", + "specific_heat_liquid": 1.25, + "specific_heat_solid": 1.25, + "density": 1, + "latent_heat": 18, + "bash_resist": 4, + "cut_resist": 6, + "bullet_resist": 6, + "acid_resist": 10, + "fire_resist": 6, + "elec_resist": 6, + "chip_resist": 8, + "dmg_adj": [ "scratched", "cut", "cracked", "shattered" ], + "bash_dmg_verb": "dented", + "cut_dmg_verb": "gouged" + }, + { + "type": "material", + "id": "thermo_resin", + "name": "Thermoplastic resin", + "copy-from": "generic_polymer_resin", + "//": "This material ID is for plastics that can be reshaped and moulded by application of heat and return to their original properties when cooled, and these specific ones are quite hard and durable when in solid state.", + "repaired_with": "thermo_resin_chunk", + "salvaged_into": "thermo_resin_chunk", + "burn_data": [ + { "fuel": 1, "smoke": 2, "burn": 1, "volume_per_turn": "750 ml" }, + { "fuel": 1, "smoke": 3, "burn": 2 }, + { "fuel": 1, "smoke": 5, "burn": 5 } + ] + }, + { + "type": "material", + "id": "epoxy", + "name": "Epoxy", + "copy-from": "generic_polymer_resin", + "//": "'epoxy' is a general catch-all for strong, brittle polymers that cannot be reshaped with application of heat. Not all of these are necessarily going to be literally epoxy, but it's close enough.", + "repaired_with": "epoxy_chunk", + "salvaged_into": "epoxy_chunk", + "burn_data": [ + { "fuel": 1, "smoke": 2, "burn": 1, "volume_per_turn": "750 ml" }, + { "fuel": 1, "smoke": 3, "burn": 2 }, + { "fuel": 1, "smoke": 5, "burn": 5 } + ] + }, { "type": "material", "id": "pseudo_fuel", diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index e1dea797f3933..7f095a70c5709 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -1835,6 +1835,7 @@ "description": "You've grown a set of gills in your neck, allowing you to breathe underwater. Slightly increases wet benefits.", "category": [ "FISH" ], "cancels": [ "GILLS_CEPH" ], + "flags": [ "GILLS" ], "wet_protection": [ { "part": "head", "good": 1 } ] }, { @@ -1847,6 +1848,7 @@ "description": "You've grown a set of gills, running from your neck up behind your ears. They allow you to breathe underwater and slightly increase wet benefits.", "category": [ "CEPHALOPOD" ], "cancels": [ "GILLS" ], + "flags": [ "GILLS" ], "wet_protection": [ { "part": "head", "good": 1 } ] }, { diff --git a/data/json/npcs/robofac/NPC_ROBOFAC_INTERCOM.json b/data/json/npcs/robofac/NPC_ROBOFAC_INTERCOM.json index 102ea5095169b..c9fbe51e82560 100644 --- a/data/json/npcs/robofac/NPC_ROBOFAC_INTERCOM.json +++ b/data/json/npcs/robofac/NPC_ROBOFAC_INTERCOM.json @@ -464,6 +464,18 @@ "topic": "TALK_ROBOFAC_INTERCOM_SERVICES", "effect": "start_trade" }, + { + "text": "The traders at the refugee center asked me to deliver this drive of FEMA data…", + "condition": { + "and": [ + { + "not": { "u_has_var": "completed_free_merchants_hub_delivery_1", "type": "dialogue", "context": "intercom", "value": "yes" } + }, + { "u_has_mission": "MISSION_FREE_MERCHANTS_HUB_DELIVERY_1" } + ] + }, + "topic": "TALK_ROBOFAC_INTERCOM_FREE_MERCHANT_DELIVERY_1" + }, { "text": "What the hell were you testing out there?", "condition": { diff --git a/data/json/overmap/overmap_terrain/overmap_terrain_abstract.json b/data/json/overmap/overmap_terrain/overmap_terrain_abstract.json index 46a864135fea6..eb584318207a2 100644 --- a/data/json/overmap/overmap_terrain/overmap_terrain_abstract.json +++ b/data/json/overmap/overmap_terrain/overmap_terrain_abstract.json @@ -15,7 +15,8 @@ "type": "overmap_terrain", "abstract": "generic_forest", "name": "generic_forest", - "land_use_code": "forest" + "land_use_code": "forest", + "connect_group": "forest" }, { "type": "overmap_terrain", diff --git a/data/json/recipes/basecamps/fbmc_sealab_small/recipe_sealab_small_common.json b/data/json/recipes/basecamps/fbmc_sealab_small/recipe_sealab_small_common.json new file mode 100644 index 0000000000000..9c1b63deb7348 --- /dev/null +++ b/data/json/recipes/basecamps/fbmc_sealab_small/recipe_sealab_small_common.json @@ -0,0 +1,29 @@ +[ + { + "type": "recipe", + "result": "faction_base_sealab_small_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_sealab_small_0", + "blueprint_provides": [ + { "id": "fbmc_sealab_small_0" }, + { "id": "kitchen_recipes_1" }, + { "id": "kitchen_recipes_2" }, + { "id": "saltworks_recipes_1" }, + { "id": "fishing_recipes" }, + { "id": "kitchen" }, + { "id": "tool_storage" }, + { "id": "sorting" }, + { "id": "pantry" } + ], + "blueprint_resources": [ "fake_air_compressor", "fake_water_purifier" ], + "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "blueprint_name": "basic survey", + "check_blueprint_needs": false + } +] diff --git a/data/json/recipes/basecamps/recipe_groups.json b/data/json/recipes/basecamps/recipe_groups.json index 15e4df89f83d8..360fd6d8eaf77 100644 --- a/data/json/recipes/basecamps/recipe_groups.json +++ b/data/json/recipes/basecamps/recipe_groups.json @@ -25,6 +25,11 @@ "description": "Central Stairs Evac Shelter Base", "om_terrains": [ "shelter_2", "shelter_2_vandal" ] }, + { + "id": "faction_base_sealab_small_0", + "description": "Freshwater Research Station Base", + "om_terrains": [ "sealab_small_-3" ] + }, { "id": "faction_base_lightindustry_0", "description": "Light Industry Office Base", @@ -358,6 +363,17 @@ { "id": "rebar_npc_drop", "description": " Craft: Rebar, Drop Hammer" } ] }, + { + "type": "recipe_group", + "id": "power_saw_recipes", + "building_type": "SMITH", + "recipes": [ + { "id": "water_wheel_with_power_saw", "description": " Craft: Water Wheel, Power Saw" }, + { "id": "xl_water_wheel_with_power_saw", "description": " Craft: Large Water Wheel, Power Saw" }, + { "id": "wood_beam_with_planer", "description": " Craft: Wooden Beam, Planer" }, + { "id": "2x4_with_power_saw", "description": " Craft: Planks, Power Saw" } + ] + }, { "type": "recipe_group", "id": "fishing_recipes", diff --git a/data/json/recipes/other/parts_construction.json b/data/json/recipes/other/parts_construction.json index 1d771633e2f68..265a06eb0e262 100644 --- a/data/json/recipes/other/parts_construction.json +++ b/data/json/recipes/other/parts_construction.json @@ -30,6 +30,20 @@ "components": [ [ [ "log", 6 ] ] ], "//2": "This is a terrible stand-in for the fact that logs are only 10kg chunks and not big enough to hew into an 8' or longer wooden beam." }, + { + "type": "recipe", + "activity_level": "fake", + "result": "wood_beam", + "id_suffix": "with_planer", + "category": "CC_OTHER", + "skill_used": "fabrication", + "difficulty": 2, + "time": "10 m", + "autolearn": true, + "byproducts": [ [ "splinter", 20 ] ], + "tools": [ [ [ "fake_planer", 250 ] ] ], + "components": [ [ [ "log", 2 ] ] ] + }, { "type": "recipe", "activity_level": "MODERATE_EXERCISE", @@ -84,6 +98,20 @@ "//": "This should eventually require planing tools once implemented, and should replace the ability to turn a small chunk of log into a long wooden plank.", "components": [ [ [ "wood_beam", 1 ] ] ] }, + { + "type": "recipe", + "activity_level": "MODERATE_EXERCISE", + "result": "2x4", + "id_suffix": "with_power_saw", + "category": "CC_OTHER", + "skill_used": "fabrication", + "difficulty": 3, + "time": "10 m", + "autolearn": true, + "using": [ [ "power_saw_long", 5 ] ], + "byproducts": [ [ "splinter", 4 ], [ "2x4", 8 ] ], + "components": [ [ [ "wood_beam", 1 ] ] ] + }, { "type": "recipe", "activity_level": "MODERATE_EXERCISE", diff --git a/data/json/recipes/other/power_supplies.json b/data/json/recipes/other/power_supplies.json index 337fb140b334b..03ada284bede5 100644 --- a/data/json/recipes/other/power_supplies.json +++ b/data/json/recipes/other/power_supplies.json @@ -270,6 +270,22 @@ [ [ "wheel_wood_b", 2 ] ] ] }, + { + "type": "recipe", + "copy-from": "water_wheel", + "id_suffix": "with_power_saw", + "time": "30 m", + "using": [ [ "soldering_standard", 35 ], [ "power_saw_short", 20 ] ], + "qualities": [ { "id": "SCREW", "level": 1 }, { "id": "HAMMER", "level": 2 }, { "id": "SAW_M", "level": 2 } ], + "components": [ + [ [ "motor_small", 1 ], [ "alternator_bicycle", 1 ], [ "alternator_motorbike", 1 ] ], + [ [ "2x4", 20 ], [ "frame_wood", 3 ] ], + [ [ "sheet_metal_small", 6 ] ], + [ [ "nail", 80 ] ], + [ [ "pipe", 8 ] ], + [ [ "wheel_wood_b", 2 ] ] + ] + }, { "type": "recipe", "activity_level": "LIGHT_EXERCISE", @@ -313,6 +329,30 @@ [ [ "wheel_wood_b", 6 ] ] ] }, + { + "type": "recipe", + "copy-from": "xl_water_wheel", + "id_suffix": "with_power_saw", + "time": "2 h", + "using": [ [ "soldering_standard", 85 ], [ "power_saw_short", 50 ] ], + "qualities": [ { "id": "SCREW", "level": 1 }, { "id": "HAMMER", "level": 2 }, { "id": "SAW_M", "level": 2 } ], + "components": [ + [ [ "2x4", 50 ], [ "frame_wood", 9 ] ], + [ [ "sheet_metal_small", 18 ] ], + [ + [ "motor_small", 3 ], + [ "alternator_bicycle", 3 ], + [ "alternator_motorbike", 3 ], + [ "alternator_car", 2 ], + [ "alternator_truck", 1 ], + [ "motor", 2 ], + [ "motor_large", 1 ] + ], + [ [ "nail", 240 ] ], + [ [ "pipe", 24 ] ], + [ [ "wheel_wood_b", 6 ] ] + ] + }, { "type": "recipe", "activity_level": "LIGHT_EXERCISE", diff --git a/data/json/recipes/tools/tools_primitive.json b/data/json/recipes/tools/tools_primitive.json index bfa3732aadfee..8ea3741789cca 100644 --- a/data/json/recipes/tools/tools_primitive.json +++ b/data/json/recipes/tools/tools_primitive.json @@ -80,6 +80,30 @@ [ [ "nail", 2 ], [ "cordage_short", 2, "LIST" ], [ "filament", 100, "LIST" ], [ "duct_tape", 40 ] ] ] }, + { + "type": "recipe", + "activity_level": "MODERATE_EXERCISE", + "result": "primitive_shovel", + "id_suffix": "blade_from_log", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_TOOLS", + "skill_used": "fabrication", + "difficulty": 2, + "time": "3 h", + "autolearn": true, + "qualities": [ + { "id": "HAMMER", "level": 2 }, + { "id": "SAW_W", "level": 1 }, + { "id": "CUT", "level": 1 }, + { "id": "CHISEL_WOOD", "level": 1 } + ], + "proficiencies": [ { "proficiency": "prof_carving", "time_multiplier": 3, "fail_multiplier": 1 } ], + "components": [ + [ [ "stick", 1 ], [ "2x4", 1 ] ], + [ [ "log", 1 ] ], + [ [ "nail", 2 ], [ "cordage_short", 2, "LIST" ], [ "filament", 100, "LIST" ], [ "duct_tape", 40 ] ] + ] + }, { "type": "recipe", "activity_level": "MODERATE_EXERCISE", diff --git a/data/json/requirements/cooking_tools.json b/data/json/requirements/cooking_tools.json index 4d3d4ba5537ca..7cb8ce89c8598 100644 --- a/data/json/requirements/cooking_tools.json +++ b/data/json/requirements/cooking_tools.json @@ -3,7 +3,9 @@ "id": "surface_heat", "type": "requirement", "//": "Heat usable for heating a surface - for example a pot or frying pan.", - "tools": [ [ [ "hotplate", 1 ], [ "multi_cooker", 1 ], [ "char_smoker", 1 ], [ "toolset", 1 ], [ "fire", -1 ] ] ] + "tools": [ + [ [ "hotplate", 1 ], [ "multi_cooker", 1 ], [ "char_smoker", 1 ], [ "toolset", 1 ], [ "fake_oven", 1 ], [ "fire", -1 ] ] + ] }, { "id": "frying_oil", @@ -22,6 +24,7 @@ [ "char_smoker", 1 ], [ "toolset", 1 ], [ "coffeemaker", 1 ], + [ "fake_oven", 1 ], [ "atomic_coffeepot", -1 ], [ "fire", -1 ] ] diff --git a/data/json/requirements/toolsets.json b/data/json/requirements/toolsets.json index e1fb02f60b0a0..4ec2124a3c743 100644 --- a/data/json/requirements/toolsets.json +++ b/data/json/requirements/toolsets.json @@ -33,14 +33,14 @@ "id": "forging_standard", "type": "requirement", "//": "Forging of steel items (per steel chunk), charcoal forge is already a substitute for forge", - "tools": [ [ [ "forge", 20 ], [ "oxy_torch", 20 ] ] ] + "tools": [ [ [ "forge", 20 ], [ "oxy_torch", 20 ], [ "fake_arc_furnace", 10 ] ] ] }, { "id": "blacksmithing_standard", "type": "requirement", "//": "Includes forging resources as well as tools needed for most blacksmithing", "qualities": [ { "id": "ANVIL", "level": 3 }, { "id": "HAMMER", "level": 3 } ], - "tools": [ [ [ "forge", 20 ], [ "oxy_torch", 20 ] ], [ [ "tongs", -1 ] ] ] + "tools": [ [ [ "forge", 20 ], [ "oxy_torch", 20 ], [ "fake_arc_furnace", 10 ] ], [ [ "tongs", -1 ] ] ] }, { "id": "mutagen_production_standard", @@ -78,7 +78,7 @@ "tools": [ [ [ "pliers", -1 ], [ "multitool", -1 ] ], [ [ "boltcutters", -1 ], [ "toolset", -1 ] ], - [ [ "cordless_drill", 10 ] ] + [ [ "cordless_drill", 10 ], [ "fake_drill_press", 5 ] ] ] }, { @@ -88,6 +88,12 @@ "tools": [ [ [ "soldering_iron", 1 ], [ "toolset", 1 ] ] ], "components": [ [ [ "solder_wire", 1 ] ] ] }, + { + "id": "drilling_standard", + "type": "requirement", + "//": "Drilling holes in various materials, including metal", + "tools": [ [ [ "cordless_drill", 2 ], [ "toolset", 2 ], [ "fake_drill_press", 1 ] ] ] + }, { "id": "anesthetic", "type": "requirement", @@ -118,5 +124,17 @@ "type": "requirement", "//": "should contain all the ink pens", "tools": [ [ [ "pen", 1 ], [ "black_pen", 1 ], [ "blue_pen", 1 ], [ "green_pen", 1 ], [ "red_pen", 1 ] ] ] + }, + { + "id": "power_saw_long", + "type": "requirement", + "//": "Power tools able to make long, straigth cuts", + "tools": [ [ [ "fake_tablesaw", 50 ], [ "circsaw_off", 100 ], [ "fake_bandsaw", 50 ] ] ] + }, + { + "id": "power_saw_short", + "type": "requirement", + "//": "Power tools able to make short, precise cuts", + "tools": [ [ [ "fake_mitresaw", 10 ], [ "circsaw_off", 20 ], [ "fake_bandsaw", 10 ] ] ] } ] diff --git a/data/mods/Aftershock/items/armor/winter_masks.json b/data/mods/Aftershock/items/armor/winter_masks.json index 5c017c3a435c6..ea33b68fbe165 100644 --- a/data/mods/Aftershock/items/armor/winter_masks.json +++ b/data/mods/Aftershock/items/armor/winter_masks.json @@ -5,7 +5,7 @@ "type": "TOOL_ARMOR", "category": "armor", "looks_like": "helmet_motor", - "name": { "str": "Magellan helmet CA." }, + "name": { "str": "Magellan helmet CA.", "str_pl": "Magellan helmets 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", diff --git a/data/mods/Aftershock/mobs/aliens.json b/data/mods/Aftershock/mobs/aliens.json index f6ac6e32e6049..8f984524a66ca 100644 --- a/data/mods/Aftershock/mobs/aliens.json +++ b/data/mods/Aftershock/mobs/aliens.json @@ -146,5 +146,38 @@ "path_settings": { "max_dist": 10 }, "anger_triggers": [ "STALK", "FRIEND_ATTACKED", "FRIEND_DIED", "PLAYER_WEAK", "PLAYER_CLOSE" ], "flags": [ "SEES", "HEARS", "SMELLS", "ANIMAL", "PATH_AVOID_DANGER_1", "WARM", "KEENNOSE" ] + }, + { + "type": "MONSTER", + "id": "afs_titanis", + "name": { "str": "terror bird" }, + "species": "BIRD", + "default_faction": "bear", + "bodytype": "ostrich", + "symbol": "T", + "color": "blue_green", + "volume": "92500 ml", + "weight": "120000 g", + "material": "flesh", + "aggression": -10, + "morale": 60, + "speed": 150, + "melee_skill": 8, + "melee_dice": 1, + "melee_dice_sides": 8, + "melee_cut": 2, + "dodge": 2, + "armor_bash": 1, + "armor_cut": 1, + "luminance": 0, + "hp": 60, + "special_attacks": [ { "type": "leap", "cooldown": 5, "max_range": 5, "allow_no_target": true }, { "type": "bite", "cooldown": 10 } ], + "description": "A tall bird with two long, muscular legs, a long neck, and a long sharp beak.", + "flags": [ "SEES", "SMELLS", "HEARS", "ANIMAL", "PATH_AVOID_DANGER_1", "GRABS", "KEENNOSE", "WARM", "HIT_AND_RUN" ], + "biosignature": { "biosig_item": "feces_bird", "biosig_timer": 3 }, + "harvest": "bird_large", + "anger_triggers": [ "PLAYER_WEAK", "HURT", "FRIEND_ATTACKED", "PLAYER_CLOSE" ], + "fear_triggers": [ "FIRE", "FRIEND_DIED" ], + "categories": [ "WILDLIFE" ] } ] diff --git a/data/mods/Aftershock/mobs/monster_groups.json b/data/mods/Aftershock/mobs/monster_groups.json index 47fd836642599..eaa6ccfd5d660 100644 --- a/data/mods/Aftershock/mobs/monster_groups.json +++ b/data/mods/Aftershock/mobs/monster_groups.json @@ -60,6 +60,7 @@ "cost_multiplier": 10, "conditions": [ "SPRING", "SUMMER", "AUTUMN", "WINTER" ] }, + { "monster": "afs_titanis", "freq": 4, "cost_multiplier": 10 }, { "monster": "afs_runner", "freq": 1, diff --git a/data/mods/DinoMod/monsters/zinosaur_upgrade.json b/data/mods/DinoMod/monsters/zinosaur_upgrade.json index 829699fbcca05..4806c330de92b 100644 --- a/data/mods/DinoMod/monsters/zinosaur_upgrade.json +++ b/data/mods/DinoMod/monsters/zinosaur_upgrade.json @@ -329,7 +329,7 @@ { "type": "MONSTER", "id": "mon_zapatosaurus_brute", - "name": { "str": "Anabolic Apatosaurus" }, + "name": { "str_sp": "Anabolic Apatosaurus" }, "copy-from": "mon_zapatosaurus", "description": "Massive, long-necked, four-legged dinosaur corpse with a long, whip-like tail. Its entire body bulges with distended muscles and swollen, festering wounds.", "diff": 2, @@ -444,7 +444,7 @@ { "type": "MONSTER", "id": "mon_zalamosaurus_brute", - "name": { "str": "Anabolic Alamosaurus" }, + "name": { "str_sp": "Anabolic Alamosaurus" }, "copy-from": "mon_zalamosaurus", "description": "Gigantic, four-legged dinosaur corpse with a long neck and a spiked whip-like tail. Its entire body bulges with distended muscles and swollen, festering wounds.", "diff": 2, @@ -743,7 +743,7 @@ { "type": "MONSTER", "id": "mon_zentaceratops_brute", - "name": { "str": "Five Horn Death Punch" }, + "name": { "str": "Five Horn Death Punch", "str_pl": "Five Horn Death Punches" }, "copy-from": "mon_zentaceratops", "description": "A massive, shambling, rhino-like dinosaur corpse with a tall bony crest from which four long horns and a short nose horn emerge. Its entire body bulges with distended muscles and swollen, festering wounds.", "diff": 2, @@ -1357,7 +1357,7 @@ { "type": "MONSTER", "id": "mon_srontosaurus", - "name": { "str": "armored Brontosaurus" }, + "name": { "str_sp": "armored Brontosaurus" }, "copy-from": "mon_sapatosaurus", "proportional": { "hp": 0.8 }, "description": "This zombified Brontosaurus has grown dense bone armor, completely covering the massive, four-legged body and long, whip-like tail." @@ -1390,7 +1390,7 @@ { "type": "MONSTER", "id": "mon_samarasaurus", - "name": { "str": "armored Camarasaurus" }, + "name": { "str_sp": "armored Camarasaurus" }, "copy-from": "mon_sapatosaurus", "proportional": { "speed": 0.8 }, "description": "This zombified Camarasaurus has grown dense body armor, completely covering the massive, long-necked, four-legged body." @@ -1398,7 +1398,7 @@ { "type": "MONSTER", "id": "mon_srachiosaurus", - "name": { "str": "armored Brachiosaurus" }, + "name": { "str_sp": "armored Brachiosaurus" }, "copy-from": "mon_sapatosaurus", "proportional": { "speed": 0.6, "hp": 3 }, "description": "This zombified Brachiosaurus has grown dense body armor, completely covering the gigantic, long-necked, four-legged body." diff --git a/data/mods/DinoMod/mutations/mutations.json b/data/mods/DinoMod/mutations/mutations.json index 31fdd2dfd2405..d313474f4947b 100644 --- a/data/mods/DinoMod/mutations/mutations.json +++ b/data/mods/DinoMod/mutations/mutations.json @@ -218,12 +218,6 @@ "copy-from": "LIZ_EYE", "extend": { "category": [ "STEGO", "TYRANT" ] } }, - { - "type": "mutation", - "id": "LIZ_EYE", - "copy-from": "LIZ_EYE", - "extend": { "category": [ "STEGO", "TYRANT" ] } - }, { "type": "mutation", "id": "LIZ_IR", diff --git a/data/mods/Magiclysm/items/enchanted_melee.json b/data/mods/Magiclysm/items/enchanted_melee.json index d780059320633..b3c2694fd9e9a 100644 --- a/data/mods/Magiclysm/items/enchanted_melee.json +++ b/data/mods/Magiclysm/items/enchanted_melee.json @@ -3,7 +3,7 @@ "type": "GENERIC", "id": "cudgel_plus_one", "copy-from": "cudgel", - "name": "cudgel +1", + "name": { "str": "cudgel +1", "str_pl": "cudgels +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -11,7 +11,7 @@ "type": "GENERIC", "id": "cudgel_plus_two", "copy-from": "cudgel", - "name": "cudgel +2", + "name": { "str": "cudgel +2", "str_pl": "cudgels +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -19,7 +19,7 @@ "type": "GENERIC", "id": "q_staff_plus_one", "copy-from": "q_staff", - "name": "quarterstaff +1", + "name": { "str": "quarterstaff +1", "str_pl": "quarterstaves +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -27,7 +27,7 @@ "type": "GENERIC", "id": "q_staff_plus_two", "copy-from": "q_staff", - "name": "quarterstaff +2", + "name": { "str": "quarterstaff +2", "str_pl": "quarterstaves +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -35,7 +35,7 @@ "type": "GENERIC", "id": "i_staff_plus_one", "copy-from": "i_staff", - "name": "ironshod quarterstaff +1", + "name": { "str": "ironshod quarterstaff +1", "str_pl": "ironshod quarterstaves +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -43,7 +43,7 @@ "type": "GENERIC", "id": "i_staff_plus_two", "copy-from": "i_staff", - "name": "ironshod quarterstaff +2", + "name": { "str": "ironshod quarterstaff +2", "str_pl": "ironshod quarterstaves +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -51,7 +51,7 @@ "type": "GENERIC", "id": "longsword_plus_one", "copy-from": "longsword", - "name": "longsword +1", + "name": { "str": "longsword +1", "str_pl": "longswords +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -59,7 +59,7 @@ "type": "GENERIC", "id": "longsword_plus_two", "copy-from": "longsword", - "name": "longsword +2", + "name": { "str": "longsword +2", "str_pl": "longswords +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -67,7 +67,7 @@ "type": "GENERIC", "id": "sledge_plus_one", "copy-from": "hammer_sledge", - "name": "sledge hammer +1", + "name": { "str": "sledge hammer +1", "str_pl": "sledge hammers +1" }, "proportional": { "price": 9.0, "price_postapoc": 9.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -75,7 +75,7 @@ "type": "GENERIC", "id": "sledge_plus_two", "copy-from": "hammer_sledge", - "name": "sledge hammer +2", + "name": { "str": "sledge hammer +2", "str_pl": "sledge hammers +2" }, "proportional": { "price": 12.0, "price_postapoc": 12.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -83,7 +83,7 @@ "type": "GENERIC", "id": "sledge_heavy_plus_one", "copy-from": "hammer_sledge_heavy", - "name": "heavy sledge hammer +1", + "name": { "str": "heavy sledge hammer +1", "str_pl": "heavy sledge hammers +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -91,7 +91,7 @@ "type": "GENERIC", "id": "sledge_heavy_plus_two", "copy-from": "hammer_sledge_heavy", - "name": "heavy sledge hammer +2", + "name": { "str": "heavy sledge hammer +2", "str_pl": "heavy sledge hammers +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -99,7 +99,7 @@ "type": "GENERIC", "id": "warhammer_plus_one", "copy-from": "warhammer", - "name": "warhammer +1", + "name": { "str": "warhammer +1", "str_pl": "warhammers +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -107,7 +107,7 @@ "type": "GENERIC", "id": "warhammer_plus_two", "copy-from": "warhammer", - "name": "warhammer +2", + "name": { "str": "warhammer +2", "str_pl": "warhammers +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -115,7 +115,7 @@ "type": "GENERIC", "id": "bat_plus_one", "copy-from": "bat", - "name": { "str": "bat +1" }, + "name": { "str": "bat +1", "str_pl": "bats +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -123,7 +123,7 @@ "type": "GENERIC", "id": "bat_plus_two", "copy-from": "bat", - "name": { "str": "bat +2" }, + "name": { "str": "bat +2", "str_pl": "bats +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -131,7 +131,7 @@ "type": "GENERIC", "id": "bat_metal_plus_one", "copy-from": "bat_metal", - "name": { "str": "aluminum bat +1" }, + "name": { "str": "aluminum bat +1", "str_pl": "aluminum bats +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -139,7 +139,7 @@ "type": "GENERIC", "id": "bat_metal_plus_two", "copy-from": "bat_metal", - "name": { "str": "aluminum bat +2" }, + "name": { "str": "aluminum bat +2", "str_pl": "aluminum bats +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -147,7 +147,7 @@ "type": "GENERIC", "id": "spear_steel_plus_one", "copy-from": "spear_steel", - "name": "steel spear +1", + "name": { "str": "steel spear +1", "str_pl": "steel spears +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -155,7 +155,7 @@ "type": "GENERIC", "id": "spear_steel_plus_two", "copy-from": "spear_steel", - "name": "steel spear +2", + "name": { "str": "steel spear +2", "str_pl": "steel spears +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -163,7 +163,7 @@ "type": "GENERIC", "id": "qiang_plus_one", "copy-from": "qiang", - "name": "qiang +1", + "name": { "str_sp": "qiang +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -171,7 +171,7 @@ "type": "GENERIC", "id": "qiang_plus_two", "copy-from": "qiang", - "name": "qiang +2", + "name": { "str_sp": "qiang +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -179,7 +179,7 @@ "type": "GENERIC", "id": "halberd_plus_one", "copy-from": "halberd", - "name": "halberd +1", + "name": { "str": "halberd +1", "str_pl": "halberds +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -187,7 +187,7 @@ "type": "GENERIC", "id": "halberd_plus_two", "copy-from": "halberd", - "name": "halberd +2", + "name": { "str": "halberd +2", "str_pl": "halberds +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -195,7 +195,7 @@ "type": "GENERIC", "id": "glaive_plus_one", "copy-from": "glaive", - "name": "glaive +1", + "name": { "str": "glaive +1", "str_pl": "glaives +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -203,7 +203,7 @@ "type": "GENERIC", "id": "glaive_plus_two", "copy-from": "glaive", - "name": "glaive +2", + "name": { "str": "glaive +2", "str_pl": "glaives +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -211,7 +211,7 @@ "type": "GENERIC", "id": "naginata_plus_one", "copy-from": "naginata", - "name": "naginata +1", + "name": { "str_sp": "naginata +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -219,7 +219,7 @@ "type": "GENERIC", "id": "naginata_plus_two", "copy-from": "naginata", - "name": "naginata +2", + "name": { "str_sp": "naginata +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -227,7 +227,7 @@ "type": "GENERIC", "id": "mace_plus_one", "copy-from": "mace", - "name": "mace +1", + "name": { "str": "mace +1", "str_pl": "maces +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -235,7 +235,7 @@ "type": "GENERIC", "id": "mace_plus_two", "copy-from": "mace", - "name": "mace +2", + "name": { "str": "mace +2", "str_pl": "maces +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -243,7 +243,7 @@ "type": "GENERIC", "id": "morningstar_plus_one", "copy-from": "morningstar", - "name": "morningstar +1", + "name": { "str": "morningstar +1", "str_pl": "morningstars +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -251,7 +251,7 @@ "type": "GENERIC", "id": "morningstar_plus_two", "copy-from": "morningstar", - "name": "morningstar +2", + "name": { "str": "morningstar +2", "str_pl": "morningstars +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -259,7 +259,7 @@ "type": "GENERIC", "id": "jian_plus_one", "copy-from": "jian", - "name": "jian +1", + "name": { "str_sp": "jian +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -267,7 +267,7 @@ "type": "GENERIC", "id": "jian_plus_two", "copy-from": "jian", - "name": "jian +2", + "name": { "str_sp": "jian +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -275,7 +275,7 @@ "type": "GENERIC", "id": "scimitar_plus_one", "copy-from": "scimitar", - "name": "scimitar +1", + "name": { "str": "scimitar +1", "str_pl": "scimitars +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -283,7 +283,7 @@ "type": "GENERIC", "id": "scimitar_plus_two", "copy-from": "scimitar", - "name": "scimitar +2", + "name": { "str": "scimitar +2", "str_pl": "scimitars +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -291,7 +291,7 @@ "type": "GENERIC", "id": "estoc_plus_one", "copy-from": "estoc", - "name": "estoc +1", + "name": { "str": "estoc +1", "str_pl": "estocs +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -299,7 +299,7 @@ "type": "GENERIC", "id": "estoc_plus_two", "copy-from": "estoc", - "name": "estoc +2", + "name": { "str": "estoc +2", "str_pl": "estocs +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -307,7 +307,7 @@ "type": "GENERIC", "id": "arming_sword_plus_one", "copy-from": "arming_sword", - "name": { "str": "arming sword +1" }, + "name": { "str": "arming sword +1", "str_pl": "arming swords +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -315,7 +315,7 @@ "type": "GENERIC", "id": "arming_sword_plus_two", "copy-from": "arming_sword", - "name": { "str": "arming sword +2" }, + "name": { "str": "arming sword +2", "str_pl": "arming swords +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -323,7 +323,7 @@ "type": "GENERIC", "id": "broadsword_plus_one", "copy-from": "broadsword", - "name": "broadsword +1", + "name": { "str": "broadsword +1", "str_pl": "broadswords +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -331,7 +331,7 @@ "type": "GENERIC", "id": "broadsword_plus_two", "copy-from": "broadsword", - "name": "broadsword +2", + "name": { "str": "broadsword +2", "str_pl": "broadswords +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -339,7 +339,7 @@ "type": "GENERIC", "id": "battleaxe_plus_one", "copy-from": "battleaxe", - "name": { "str": "battle axe +1" }, + "name": { "str": "battle axe +1", "str_pl": "battle axes +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -347,7 +347,7 @@ "type": "GENERIC", "id": "battleaxe_plus_two", "copy-from": "battleaxe", - "name": { "str": "battle axe +2" }, + "name": { "str": "battle axe +2", "str_pl": "battle axes +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -355,7 +355,7 @@ "type": "GENERIC", "id": "cavalry_sabre_plus_one", "copy-from": "cavalry_sabre", - "name": "cavalry sabre +1", + "name": { "str": "cavalry sabre +1", "str_pl": "cavalry sabres +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -363,7 +363,7 @@ "type": "GENERIC", "id": "cavalry_sabre_plus_two", "copy-from": "cavalry_sabre", - "name": "cavalry sabre +2", + "name": { "str": "cavalry sabre +2", "str_pl": "cavalry sabres +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -371,7 +371,7 @@ "type": "GENERIC", "id": "crowbar_plus_one", "copy-from": "crowbar", - "name": "crowbar +1", + "name": { "str": "crowbar +1", "str_pl": "crowbars +1" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -379,7 +379,7 @@ "type": "GENERIC", "id": "crowbar_plus_two", "copy-from": "crowbar", - "name": "crowbar +2", + "name": { "str": "crowbar +2", "str_pl": "crowbars +2" }, "proportional": { "price": 9.0, "price_postapoc": 9.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -387,7 +387,7 @@ "type": "GENERIC", "id": "cutlass_plus_one", "copy-from": "cutlass", - "name": "cutlass +1", + "name": { "str": "cutlass +1", "str_pl": "cutlasses +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -395,7 +395,7 @@ "type": "GENERIC", "id": "cutlass_plus_two", "copy-from": "cutlass", - "name": "cutlass +2", + "name": { "str": "cutlass +2", "str_pl": "cutlasses +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -403,7 +403,7 @@ "type": "GENERIC", "id": "fire_ax_plus_one", "copy-from": "fire_ax", - "name": "fire axe +1", + "name": { "str": "fire axe +1", "str_pl": "fire axes +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -411,7 +411,7 @@ "type": "GENERIC", "id": "fire_ax_plus_two", "copy-from": "fire_ax", - "name": "fire axe +2", + "name": { "str": "fire axe +2", "str_pl": "fire axes +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -419,7 +419,7 @@ "type": "GENERIC", "id": "katana_plus_one", "copy-from": "katana", - "name": "katana +1", + "name": { "str_sp": "katana +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -427,7 +427,7 @@ "type": "GENERIC", "id": "katana_plus_two", "copy-from": "katana", - "name": "katana +2", + "name": { "str_sp": "katana +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -435,7 +435,7 @@ "type": "GENERIC", "id": "knife_combat_plus_one", "copy-from": "knife_combat", - "name": "combat knife +1", + "name": { "str": "combat knife +1", "str_pl": "combat knives +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -443,7 +443,7 @@ "type": "GENERIC", "id": "knife_combat_plus_two", "copy-from": "knife_combat", - "name": "combat knife +2", + "name": { "str": "combat knife +2", "str_pl": "combat knives +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -451,7 +451,7 @@ "type": "GENERIC", "id": "knife_hunting_plus_one", "copy-from": "knife_hunting", - "name": "hunting knife +1", + "name": { "str": "hunting knife +1", "str_pl": "hunting knives +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -459,7 +459,7 @@ "type": "GENERIC", "id": "knife_hunting_plus_two", "copy-from": "knife_hunting", - "name": "hunting knife +2", + "name": { "str": "hunting knife +2", "str_pl": "hunting knives +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -467,7 +467,7 @@ "type": "GENERIC", "id": "knife_rambo_plus_one", "copy-from": "knife_rambo", - "name": "survival knife +1", + "name": { "str": "survival knife +1", "str_pl": "survival knives +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -475,7 +475,7 @@ "type": "GENERIC", "id": "knife_rambo_plus_two", "copy-from": "knife_rambo", - "name": "survival knife +2", + "name": { "str": "survival knife +2", "str_pl": "survival knives +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -483,7 +483,7 @@ "type": "GENERIC", "id": "knife_trench_plus_one", "copy-from": "knife_trench", - "name": "trench knife +1", + "name": { "str": "trench knife +1", "str_pl": "trench knives +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -491,7 +491,7 @@ "type": "GENERIC", "id": "knife_trench_plus_two", "copy-from": "knife_trench", - "name": "trench knife +2", + "name": { "str": "trench knife +2", "str_pl": "trench knives +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -499,7 +499,7 @@ "type": "GENERIC", "id": "kris_plus_one", "copy-from": "kris", - "name": "kris +1", + "name": { "str": "kris +1", "str_pl": "krises +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -507,7 +507,7 @@ "type": "GENERIC", "id": "kris_plus_two", "copy-from": "kris", - "name": "kris +2", + "name": { "str": "kris +2", "str_pl": "krises +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -515,7 +515,7 @@ "type": "GENERIC", "id": "kukri_plus_one", "copy-from": "kukri", - "name": "kukri +1", + "name": { "str": "kukri +1", "str_pl": "kukris +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -523,7 +523,7 @@ "type": "GENERIC", "id": "kukri_plus_two", "copy-from": "kukri", - "name": "kukri +2", + "name": { "str": "kukri +2", "str_pl": "kukris +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -531,7 +531,7 @@ "type": "GENERIC", "id": "nodachi_plus_one", "copy-from": "nodachi", - "name": "nodachi +1", + "name": { "str_sp": "nodachi +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -539,7 +539,7 @@ "type": "GENERIC", "id": "nodachi_plus_two", "copy-from": "nodachi", - "name": "nodachi +2", + "name": { "str_sp": "nodachi +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -547,7 +547,7 @@ "type": "GENERIC", "id": "pickaxe_plus_one", "copy-from": "pickaxe", - "name": "pickaxe +1", + "name": { "str": "pickaxe +1", "str_pl": "pickaxes +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -555,7 +555,7 @@ "type": "GENERIC", "id": "pickaxe_plus_two", "copy-from": "pickaxe", - "name": "pickaxe +2", + "name": { "str": "pickaxe +2", "str_pl": "pickaxes +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -563,7 +563,7 @@ "type": "GENERIC", "id": "pike_plus_one", "copy-from": "pike", - "name": "pike +1", + "name": { "str": "pike +1", "str_pl": "pikes +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -571,7 +571,7 @@ "type": "GENERIC", "id": "pike_plus_two", "copy-from": "pike", - "name": "pike +2", + "name": { "str": "pike +2", "str_pl": "pikes +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -579,7 +579,7 @@ "type": "GENERIC", "id": "rapier_plus_one", "copy-from": "rapier", - "name": "rapier +1", + "name": { "str": "rapier +1", "str_pl": "rapiers +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -587,7 +587,7 @@ "type": "GENERIC", "id": "rapier_plus_two", "copy-from": "rapier", - "name": "rapier +2", + "name": { "str": "rapier +2", "str_pl": "rapiers +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -595,7 +595,7 @@ "type": "GENERIC", "id": "tanto_plus_one", "copy-from": "tanto", - "name": "tanto +1", + "name": { "str_sp": "tanto +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -603,7 +603,7 @@ "type": "GENERIC", "id": "tanto_plus_two", "copy-from": "tanto", - "name": "tanto +2", + "name": { "str_sp": "tanto +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -611,7 +611,7 @@ "type": "GENERIC", "id": "wakizashi_plus_one", "copy-from": "wakizashi", - "name": "wakizashi +1", + "name": { "str_sp": "wakizashi +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -619,7 +619,7 @@ "type": "GENERIC", "id": "wakizashi_plus_two", "copy-from": "wakizashi", - "name": "wakizashi +2", + "name": { "str_sp": "wakizashi +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -627,7 +627,7 @@ "type": "GENERIC", "id": "zweihander_plus_one", "copy-from": "zweihander", - "name": "zweihänder +1", + "name": { "str": "zweihänder +1", "str_pl": "zweihänders +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -635,7 +635,7 @@ "type": "GENERIC", "id": "zweihander_plus_two", "copy-from": "zweihander", - "name": "zweihänder +2", + "name": { "str": "zweihänder +2", "str_pl": "zweihänders +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -643,7 +643,7 @@ "type": "GENERIC", "id": "khopesh_plus_one", "copy-from": "khopesh", - "name": "khopesh +1", + "name": { "str": "khopesh +1", "str_pl": "khopeshes +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -651,7 +651,7 @@ "type": "GENERIC", "id": "khopesh_plus_two", "copy-from": "khopesh", - "name": "khopesh +2", + "name": { "str": "khopesh +2", "str_pl": "khopeshes +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -659,7 +659,7 @@ "type": "GENERIC", "id": "sword_xiphos_plus_one", "copy-from": "sword_xiphos", - "name": "xiphos +1", + "name": { "str": "xiphos +1", "str_pl": "xiphe +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -667,7 +667,7 @@ "type": "GENERIC", "id": "sword_xiphos_plus_two", "copy-from": "sword_xiphos", - "name": "xiphos +2", + "name": { "str": "xiphos +2", "str_pl": "xiphe +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -675,7 +675,7 @@ "type": "GENERIC", "id": "dao_plus_one", "copy-from": "dao", - "name": "dao +1", + "name": { "str_sp": "dao +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -683,7 +683,7 @@ "type": "GENERIC", "id": "dao_plus_two", "copy-from": "dao", - "name": "dao +2", + "name": { "str_sp": "dao +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -691,7 +691,7 @@ "type": "GENERIC", "id": "lucerne_plus_one", "copy-from": "lucern_hammer", - "name": "lucerne hammer +1", + "name": { "str": "lucerne hammer +1", "str_pl": "lucerne hammers +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -699,7 +699,7 @@ "type": "GENERIC", "id": "lucerne_plus_two", "copy-from": "lucern_hammer", - "name": "lucerne hammer +2", + "name": { "str": "lucerne hammer +2", "str_pl": "lucerne hammers +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, diff --git a/data/mods/Magiclysm/items/enchanted_unarmed.json b/data/mods/Magiclysm/items/enchanted_unarmed.json index 7a70afda62605..c1ca982ff9d93 100644 --- a/data/mods/Magiclysm/items/enchanted_unarmed.json +++ b/data/mods/Magiclysm/items/enchanted_unarmed.json @@ -4,7 +4,7 @@ "id": "cestus_plus_one", "copy-from": "cestus", "looks_like": "cestus", - "name": "cestus +1", + "name": { "str": "cestus +1", "str_pl": "cestuses +1" }, "proportional": { "price": 3.0, "price_postapoc": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -13,7 +13,7 @@ "id": "cestus_plus_two", "copy-from": "cestus", "looks_like": "cestus", - "name": "cestus +2", + "name": { "str": "cestus +2", "str_pl": "cestuses +2" }, "proportional": { "price": 6.0, "price_postapoc": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -38,7 +38,7 @@ "id": "flaming_fist_plus_one", "copy-from": "flaming_fist", "looks_like": "flaming_fist", - "name": "flaming fist +1", + "name": { "str": "flaming fist +1", "str_pl": "flaming fists +1" }, "proportional": { "price": 3.0, "bashing": 1.1, "cutting": 1.1, "weight": 0.9 }, "relative": { "to_hit": 1 } }, @@ -47,7 +47,7 @@ "id": "flaming_fist_plus_two", "copy-from": "flaming_fist", "looks_like": "flaming_fist", - "name": "flaming fist +2", + "name": { "str": "flaming fist +2", "str_pl": "flaming fists +2" }, "proportional": { "price": 6.0, "bashing": 1.2, "cutting": 1.2, "weight": 0.8 }, "relative": { "to_hit": 2 } }, @@ -71,7 +71,7 @@ "id": "shotcestus", "copy-from": "shotgun_base", "type": "GUN", - "name": { "str": "shotcestus", "str_pl": "shotcesti" }, + "name": { "str": "shotcestus", "str_pl": "shotcestuses" }, "description": "A sawn-off double-barrel shotgun mounted on a metal cestus. The lack of a stock to absorb recoil means some strength is required to fire.", "weight": "1566 g", "volume": "1500 ml", @@ -99,7 +99,7 @@ "id": "rune_earthshaper_weapon", "symbol": "3", "color": "light_gray", - "name": { "str": "Earthshaper cestus", "str_pl": "Earthshaper cesti" }, + "name": { "str": "Earthshaper cestus", "str_pl": "Earthshaper cestuses" }, "description": "A stone battle glove with carved runes encasing the hand, protecting it while increasing striking power. There is an Earthshaper rune embedded in the palm.", "material": [ "stone", "leather", "steel" ], "volume": "500 ml", diff --git a/data/mods/TEST_DATA/items.json b/data/mods/TEST_DATA/items.json index 69bcc5bc5a2e1..9d929bc3fa340 100644 --- a/data/mods/TEST_DATA/items.json +++ b/data/mods/TEST_DATA/items.json @@ -925,6 +925,34 @@ } ] }, + { + "type": "GENERIC", + "id": "test_waterproof_bag", + "category": "container", + "name": { "str": "body bag" }, + "looks_like": "bag_canvas", + "description": "A large, human size, rectangular bag made of strong plastic, with a zipper in the middle. Used to hold a dead body.", + "weight": "1500 g", + "volume": "1 L", + "longest_side": "40 cm", + "price": 0, + "price_postapoc": 10, + "to_hit": -5, + "material": [ "plastic" ], + "symbol": ")", + "pocket_data": [ + { + "pocket_type": "CONTAINER", + "watertight": true, + "max_contains_volume": "100 L", + "max_contains_weight": "100 kg", + "max_item_length": "200 cm", + "moves": 400 + } + ], + "color": "dark_gray", + "flags": [ "TRADER_AVOID" ] + }, { "id": "test_backpack", "type": "ARMOR", diff --git a/doc/COMPILING/COMPILING.md b/doc/COMPILING/COMPILING.md index 1fd441805bd69..e11ce27aaf658 100644 --- a/doc/COMPILING/COMPILING.md +++ b/doc/COMPILING/COMPILING.md @@ -597,39 +597,24 @@ Clang by default uses MSVC on Windows, but also supports the MinGW64 library. Si There are reports of CDDA building fine on recent OpenBSD and FreeBSD machines (with appropriately recent compilers), and there is some work being done on making the `Makefile` "just work", however we're far from that and BSDs support is mostly based on user contributions. Your mileage may vary. So far essentially all testing has been on amd64, but there is no (known) reason that other architectures shouldn't work, in principle. -### Building on FreeBSD/amd64 10.1 with the system compiler +### Building on FreeBSD/amd64 13.0 with the system compiler -FreeBSD uses clang as the default compiler as of 10.0, and combines it with libc++ to provide C++14 support out of the box. You will however need gmake (examples for binary packages): +FreeBSD uses clang as the default compiler as of 10.0, and combines it with libc++ to provide C++14 support out of the box. -`pkg install gmake` +Install the following with pkg (or from Ports): + +`pkg install gmake libiconv` Tiles builds will also require SDL2: -`pkg install sdl2 sdl2_image sdl2_mixer sdl2_ttf` +`pkg install sdl20 sdl2_image sdl2_mixer sdl2_ttf` -Then you should be able to build with something like this (you can of course set CXXFLAGS and LDFLAGS in your .profile or something): +Then you should be able to build with something like this: ``` -export CXXFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" -gmake # ncurses builds -gmake TILES=1 # tiles builds -``` - -The author has not tested tiles builds, as the build VM lacks X; they do at least compile/link successfully. - -### Building ncurses version on FreeBSD/amd64 9.3 with GCC 4.8.4 from ports - -For ncurses build add to `Makefile`, before `VERSION`: - -```Makefile -OTHERS += -D_GLIBCXX_USE_C99 -CXX = g++48 -CXXFLAGS += -I/usr/local/lib/gcc48/include -LDFLAGS += -rpath=/usr/local/lib/gcc48 +gmake RELEASE=1 # ncurses builds +gmake RELEASE=1 TILES=1 # tiles builds ``` -Note: or you can `setenv` the above (merging `OTHERS` into `CXXFLAGS`), but you knew that. - -And then build with `gmake LOCALIZE=0 RELEASE=1`. ### Building on OpenBSD/amd64 5.8 with GCC 4.9.2 from ports/packages diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index 5b226ee905485..e49b7ab31d7ee 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -649,6 +649,7 @@ These flags can be applied via JSON item definition to most items. Not to be co - ```FIREWOOD``` ... This item can serve as a firewood. Items with this flag are sorted out to "Loot: Wood" zone - ```FRAGILE_MELEE``` ... Fragile items that fall apart easily when used as a weapon due to poor construction quality and will break into components when broken. - ```GAS_DISCOUNT``` ... Discount cards for the automated gas stations. +- ```ITEM_BROKEN``` ... Item was broken and won't activate anymore. - ```IS_PET_ARMOR``` ... Is armor for a pet monster, not armor for a person - ```LEAK_ALWAYS``` ... Leaks (may be combined with "RADIOACTIVE"). - ```LEAK_DAM``` ... Leaks when damaged (may be combined with "RADIOACTIVE"). @@ -681,6 +682,8 @@ These flags can be applied via JSON item definition to most items. Not to be co - ```TRADER_KEEP_EQUIPPED``` ... NPCs will only trade this item if they aren't currently wearing or wielding it. - ```UNBREAKABLE_MELEE``` ... Never gets damaged when used as melee weapon. - ```UNRECOVERABLE``` ... Cannot be recovered from a disassembly. +- ```WATER_BREAK``` ... Item is broken in water. +- ```WATER_DISSOLVE``` ... Item is dissolved in water. - ```ZERO_WEIGHT``` Normally items with zero weight will generate an error. Use this flag to indicate that zero weight is intentional and suppress that error. @@ -1533,3 +1536,4 @@ Gun fault flags: - ```HEATSINK``` You are resistant to extreme heat. - ```THERMOMETER``` You always know what temperature it is. - ```CBQ_LEARN_BONUS``` You learn CBQ from the bionic bio_cqb faster. +- ```GILLS``` You can breathe underwater. diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index 3dedaa68345ff..95c109355bee7 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -89,6 +89,10 @@ Use the `Home` key to return to the top. - [Ammo](#ammo) - [Magazine](#magazine) - [Armor](#armor) + - [Armor Portion Data](#armor-portion-data) + - [Encumbrance](#encumbrance) + - [Coverage](#coverage) + - [Covers](#covers) - [Guidelines for thickness:](#guidelines-for-thickness) - [Pet Armor](#pet-armor) - [Books](#books) @@ -2392,7 +2396,6 @@ Armor can be defined like this: "type" : "ARMOR", // Defines this as armor ... // same entries as above for the generic item. // additional some armor specific entries: -"covers" : [ "foot_l", "foot_r" ], // Where it covers. Use bodypart_id defined in body_parts.json "warmth" : 10, // (Optional, default = 0) How much warmth clothing provides "environmental_protection" : 0, // (Optional, default = 0) How much environmental protection it affords "encumbrance" : 0, // Base encumbrance (unfitted value) @@ -2402,19 +2405,56 @@ Armor can be defined like this: "coverage" : 80, // What percentage of body part "material_thickness" : 1, // Thickness of material, in millimeter units (approximately). Ordinary clothes range from 0.1 to 0.5. Particularly rugged cloth may reach as high as 1-2mm, and armor or protective equipment can range as high as 10 or rarely more. "power_armor" : false, // If this is a power armor item (those are special). -"valid_mods" : ["steel_padded"] // List of valid clothing mods. Note that if the clothing mod doesn't have "restricted" listed, this isn't needed. +"valid_mods" : ["steel_padded"], // List of valid clothing mods. Note that if the clothing mod doesn't have "restricted" listed, this isn't needed. +"armor": [ ... ] ``` + +#### Armor Portion Data +Encumbrance and coverage can be defined on a piece of armor as such: + +```json +"armor": [ + { + "encumbrance": [ 2, 8 ], + "coverage": 95, + "covers": [ "torso" ] + }, + { + "encumbrance": 2, + "coverage": 80, + "covers": [ "arm_r", "arm_l" ] + } +] +``` + +##### Encumbrance +(integer, or array of 2 integers) +The value of this field (or, if it is an array, the first value in the array) is the base encumbrance (unfitted) of this item. +When specified as an array, the second value is the max encumbrance - when the pockets of this armor are completely full of items, the encumbrance of a non-rigid item will be set to this. Otherwise it'll be between the first value and the second value following this the equation: first value + (second value - first value) * non-rigid volume / non-rigid capacity. By default, the max encumbrance is the encumbrance + (non-rigid volume / 250ml). + +##### Coverage +(integer) +What percentage of time this piece of armor will be hit (and thus used as armor) when an attack hits the body parts in `covers`. + +##### Covers +(array of strings) +What body parts this section of the armor covers. See the bodypart_ids defined in body_parts.json for valid values. + Alternately, every item (book, tool, gun, even food) can be used as armor if it has armor_data: ```C++ "type" : "TOOL", // Or any other item type ... // same entries as for the type (e.g. same entries as for any tool), "armor_data" : { // additionally the same armor data like above - "covers" : [ "foot_l", "foot_r" ], "warmth" : 10, "environmental_protection" : 0, - "encumbrance" : 0, - "coverage" : 80, "material_thickness" : 1, + "armor": [ + { + "covers" : [ "foot_l", "foot_r" ], + "encumbrance" : 0, + "coverage" : 80, + } + ], "power_armor" : false } ``` diff --git a/doc/NPCs.md b/doc/NPCs.md index a7f57ca5b6c93..4a68af192b078 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -491,6 +491,7 @@ Effect | Description `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. `u_mod_fatigue: fatigue_int`
`npc_mod_fatigue: fatigue_int` | Your character or the NPC will have `fatigue_int` added or subtracted from its fatigue. `u_make_sound, npc_make_sound: message_string`, `volume: volume_int`, `type: type_string`, | A sound of description `message_string` will be made at your character or the NPC's location of volume `volume_int` and type `type_string`. Possible types are: background, weather, music, movement, speech, electronic_speech, activity, destructive_activity, alarm, combat, alert, or order +`u_mod_healthy, npc_mod_healthy : amount_int, cap: cap_int` | Your character or the NPC will have `amount_int` added or subtracted from its health value, but not beyond `cap_int`. #### Trade / Items diff --git a/doc/OVERMAP.md b/doc/OVERMAP.md index 860db38165212..19e9c7d3628ef 100644 --- a/doc/OVERMAP.md +++ b/doc/OVERMAP.md @@ -217,6 +217,7 @@ rotation for the referenced overmap terrains (e.g. the `_north` version for all) | `sym` | Symbol used when drawing the location, like `"F"` (or you may use an ASCII value like `70`). | | `color` | Color to draw the symbol in. See [COLOR.md](COLOR.md). | | `looks_like` | Id of another overmap terrain to be used for the graphical tile, if this doesn't have one. | +| `connect_group` | Specify that this overmap terrain might be graphically connected to its neighbours, should a tileset wish to. It will connect to any other `overmap_terrain` with the same `connect_group`. | | `see_cost` | Affects player vision on overmap. Higher values obstruct vision more. | | `travel_cost` | Affects pathfinding cost. Higher values are harder to travel through (reference: Forest = 10 ) | | `extras` | Reference to a named `map_extras` in region_settings, defines which map extras can be applied. | diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 318bd48f0c655..d08ed4fa08712 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -2791,7 +2791,7 @@ void shearing_activity_actor::start( player_activity &act, Character &who ) true ) : mon->unique_name; if( !mon->shearable() ) { - add_msg( _( "$1%s has nothing %2$s could shear." ), pet_name_capitalized, who.disp_name() ); + add_msg( _( "%1$s has nothing %2$s could shear." ), pet_name_capitalized, who.disp_name() ); if( shearing_tie ) { mon->remove_effect( effect_tied ); } diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index ccc0986c6ffb7..f056e7cbb6a2a 100644 --- a/src/avatar_action.cpp +++ b/src/avatar_action.cpp @@ -82,6 +82,61 @@ static const std::string flag_SWIMMABLE( "SWIMMABLE" ); #define dbg(x) DebugLog((x),D_SDL) << __FILE__ << ":" << __LINE__ << ": " +static bool check_water_affect_items( avatar &you ) +{ + std::vector dissolved; + std::vector destroyed; + + for( item_location &loc : you.all_items_loc() ) { + if( loc->has_flag( flag_WATER_DISSOLVE ) && !loc.protected_from_liquids() ) { + dissolved.emplace_back( loc ); + } else if( loc->has_flag( flag_WATER_BREAK ) && !loc->is_broken() + && !loc.protected_from_liquids() ) { + destroyed.emplace_back( loc ); + } + } + + if( dissolved.empty() && destroyed.empty() ) { + return query_yn( _( "Dive into the water?" ) ); + } + + uilist menu; + menu.title = _( "Diving will destroy the following items. Proceed?" ); + menu.text = _( "These items are not inside a waterproof container." ); + + menu.addentry( 0, true, 'N', _( "No" ) ); + menu.addentry( 1, true, 'Y', _( "Yes" ) ); + + auto add_header = [&menu]( const std::string & str ) { + menu.addentry( -1, false, -1, "" ); + uilist_entry header( -1, false, -1, str, c_yellow, c_yellow ); + header.force_color = true; + menu.entries.push_back( header ); + }; + + if( !dissolved.empty() ) { + add_header( _( "Will be dissolved:" ) ); + for( item_location &it : dissolved ) { + menu.addentry( -1, false, -1, it->display_name() ); + } + } + + if( !destroyed.empty() ) { + add_header( _( "Will be destroyed:" ) ); + for( item_location &it : destroyed ) { + menu.addentry( -1, false, -1, it->display_name() ); + } + } + + menu.query(); + if( menu.ret != 1 ) { + you.add_msg_if_player( _( "You back away from the water." ) ); + return false; + } + + return true; +} + bool avatar_action::move( avatar &you, map &m, const tripoint &d ) { if( ( !g->check_safe_mode_allowed() ) || you.has_active_mutation( trait_SHELL2 ) ) { @@ -350,7 +405,8 @@ bool avatar_action::move( avatar &you, map &m, const tripoint &d ) return false; } } - if( ( fromSwimmable && fromDeepWater && !fromBoat ) || query_yn( _( "Dive into the water?" ) ) ) { + if( ( fromSwimmable && fromDeepWater && !fromBoat ) || + check_water_affect_items( you ) ) { if( ( !fromDeepWater || fromBoat ) && you.swim_speed() < 500 ) { add_msg( _( "You start swimming." ) ); add_msg( m_info, _( "%s to dive underwater." ), @@ -515,6 +571,9 @@ void avatar_action::swim( map &m, avatar &you, const tripoint &p ) add_msg( _( "The water washes off the glowing goo!" ) ); you.remove_effect( effect_glowing ); } + + g->water_affect_items( you ); + int movecost = you.swim_speed(); you.practice( skill_swimming, you.is_underwater() ? 2 : 1 ); if( movecost >= 500 ) { diff --git a/src/cata_tiles.cpp b/src/cata_tiles.cpp index 51e3705048ba8..5b55e3e6223a0 100644 --- a/src/cata_tiles.cpp +++ b/src/cata_tiles.cpp @@ -1893,14 +1893,10 @@ 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 ); - const oter_type_str_id type_tmp( id ); + const oter_type_str_id tmp( id ); if( tmp.is_valid() ) { - sym = tmp->get_uint32_symbol(); - col = tmp->get_color(); - } else if( type_tmp.is_valid() ) { - sym = type_tmp->symbol; - col = type_tmp->color; + sym = tmp->symbol; + col = tmp->color; } } else if( category == C_OVERMAP_NOTE ) { sym = id[5]; @@ -2027,6 +2023,10 @@ bool cata_tiles::draw_from_id_string( const std::string &id, TILE_CATEGORY categ // translate from player-relative to screen relative tile position const point screen_pos = player_to_screen( pos.xy() ); + auto simple_point_hash = []( const auto & p ) { + return p.x + p.y * 65536; + }; + // seed the PRNG to get a reproducible random int // TODO: faster solution here unsigned int seed = 0; @@ -2037,7 +2037,7 @@ bool cata_tiles::draw_from_id_string( const std::string &id, TILE_CATEGORY categ case C_FIELD: case C_LIGHTING: // stationary map tiles, seed based on map coordinates - seed = here.getabs( pos ).x + here.getabs( pos ).y * 65536; + seed = simple_point_hash( here.getabs( pos ) ); break; case C_VEHICLE_PART: // vehicle parts, seed based on coordinates within the vehicle @@ -2050,12 +2050,12 @@ bool cata_tiles::draw_from_id_string( const std::string &id, TILE_CATEGORY categ const vpart_id &vp_id = std::get<0>( vp_override->second ); if( vp_id ) { const point &mount = std::get<4>( vp_override->second ); - seed = mount.x + mount.y * 65536; + seed = simple_point_hash( mount ); } } else { const optional_vpart_position vp = here.veh_at( pos ); if( vp ) { - seed = vp->mount().x + vp->mount().y * 65536; + seed = simple_point_hash( vp->mount() ); } } @@ -2075,18 +2075,19 @@ bool cata_tiles::draw_from_id_string( const std::string &id, TILE_CATEGORY categ if( fid.is_valid() ) { const furn_t &f = fid.obj(); if( !f.is_movable() ) { - seed = here.getabs( pos ).x + here.getabs( pos ).y * 65536; + seed = simple_point_hash( here.getabs( pos ) ); } } } break; + case C_OVERMAP_TERRAIN: + seed = simple_point_hash( pos ); case C_ITEM: case C_TRAP: case C_NONE: 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: diff --git a/src/character.cpp b/src/character.cpp index 8f4626d8d6b93..3a0ced3e8548d 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -8758,6 +8758,10 @@ bool Character::invoke_item( item *used, const std::string &method ) bool Character::invoke_item( item *used, const std::string &method, const tripoint &pt, int pre_obtain_moves ) { + if( used->is_broken() ) { + add_msg_if_player( m_bad, _( "Your %s was broken and won't turn on." ), used->tname() ); + return false; + } if( !has_enough_charges( *used, true ) ) { moves = pre_obtain_moves; return false; diff --git a/src/creature.cpp b/src/creature.cpp index 569c8aea3bb79..6f2fcfae8c61a 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -2440,7 +2440,7 @@ void Creature::process_damage_over_time() void Creature::check_dead_state() { if( is_dead_state() ) { - die( nullptr ); + die( killer ); } } diff --git a/src/dialogue.h b/src/dialogue.h index f094621a6a3f3..7610e3828657d 100644 --- a/src/dialogue.h +++ b/src/dialogue.h @@ -106,6 +106,7 @@ struct talk_effect_fun_t { void set_assign_mission( const JsonObject &jo, const std::string &member ); void set_make_sound( const JsonObject &jo, const std::string &member, bool is_npc ); void set_queue_effect_on_condition( const JsonObject &jo, const std::string &member ); + void set_mod_healthy( const JsonObject &jo, const std::string &member, bool is_npc ); void set_sound_effect( const JsonObject &jo, const std::string &member ); void set_mod_fatigue( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_add_var( const JsonObject &jo, const std::string &member, bool is_npc = false ); diff --git a/src/flag.cpp b/src/flag.cpp index 5b5b68364f9c0..09d28c1858701 100644 --- a/src/flag.cpp +++ b/src/flag.cpp @@ -134,6 +134,7 @@ const flag_id flag_IR_EFFECT( "IR_EFFECT" ); const flag_id flag_IS_ARMOR( "IS_ARMOR" ); const flag_id flag_IS_PET_ARMOR( "IS_PET_ARMOR" ); const flag_id flag_IS_UPS( "IS_UPS" ); +const flag_id flag_ITEM_BROKEN( "ITEM_BROKEN" ); const flag_id flag_LEAK_ALWAYS( "LEAK_ALWAYS" ); const flag_id flag_LEAK_DAM( "LEAK_DAM" ); const flag_id flag_LITCIG( "LITCIG" ); @@ -298,8 +299,10 @@ const flag_id flag_WAIST( "WAIST" ); const flag_id flag_WATCH( "WATCH" ); const flag_id flag_WATERPROOF( "WATERPROOF" ); const flag_id flag_WATERPROOF_GUN( "WATERPROOF_GUN" ); +const flag_id flag_WATER_BREAK( "WATER_BREAK" ); const flag_id flag_WATER_EXTINGUISH( "WATER_EXTINGUISH" ); const flag_id flag_WATER_FRIENDLY( "WATER_FRIENDLY" ); +const flag_id flag_WATER_DISSOLVE( "WATER_DISSOLVE" ); const flag_id flag_WET( "WET" ); const flag_id flag_WHIP( "WHIP" ); const flag_id flag_WIND_EXTINGUISH( "WIND_EXTINGUISH" ); diff --git a/src/flag.h b/src/flag.h index ce60d0a3f6580..30f286f6578eb 100644 --- a/src/flag.h +++ b/src/flag.h @@ -141,6 +141,7 @@ extern const flag_id flag_IR_EFFECT; extern const flag_id flag_IS_ARMOR; extern const flag_id flag_IS_PET_ARMOR; extern const flag_id flag_IS_UPS; +extern const flag_id flag_ITEM_BROKEN; extern const flag_id flag_LEAK_ALWAYS; extern const flag_id flag_LEAK_DAM; extern const flag_id flag_LITCIG; @@ -305,8 +306,10 @@ extern const flag_id flag_WAIST; extern const flag_id flag_WATCH; extern const flag_id flag_WATERPROOF; extern const flag_id flag_WATERPROOF_GUN; +extern const flag_id flag_WATER_BREAK; extern const flag_id flag_WATER_EXTINGUISH; extern const flag_id flag_WATER_FRIENDLY; +extern const flag_id flag_WATER_DISSOLVE; extern const flag_id flag_WET; extern const flag_id flag_WHIP; extern const flag_id flag_WIND_EXTINGUISH; diff --git a/src/game.cpp b/src/game.cpp index 2c9962121a542..af1b2f02ed708 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -246,6 +246,8 @@ static const itype_id itype_remotevehcontrol( "remotevehcontrol" ); static const itype_id itype_rm13_armor_on( "rm13_armor_on" ); static const itype_id itype_rope_30( "rope_30" ); static const itype_id itype_swim_fins( "swim_fins" ); +static const itype_id itype_towel( "towel" ); +static const itype_id itype_towel_wet( "towel_wet" ); static const trait_id trait_BADKNEES( "BADKNEES" ); static const trait_id trait_ILLITERATE( "ILLITERATE" ); @@ -2113,7 +2115,9 @@ static hint_rating rate_action_take_off( const avatar &you, const item &it ) static hint_rating rate_action_use( const avatar &you, const item &it ) { - if( it.is_tool() ) { + if( it.is_broken() ) { + return hint_rating::iffy; + } else if( it.is_tool() ) { return it.ammo_sufficient() ? hint_rating::good : hint_rating::iffy; } else if( it.is_gunmod() ) { /** @EFFECT_GUN >0 allows rating estimates for gun modifications */ @@ -9191,7 +9195,7 @@ void game::reload( item_location &loc, bool prompt, bool empty ) } bool use_loc = true; - if( !it->has_flag( flag_ALLOWS_REMOTE_USE ) ) { + if( !u.has_item( *it ) && !it->has_flag( flag_ALLOWS_REMOTE_USE ) ) { it = loc.obtain( u ).get_item(); if( !it ) { add_msg( _( "Never mind." ) ); @@ -10774,6 +10778,41 @@ void game::on_options_changed() #endif } +void game::water_affect_items( Character &ch ) const +{ + std::vector dissolved; + std::vector destroyed; + + for( item_location &loc : ch.all_items_loc() ) { + // check flag first because its cheaper + if( loc->has_flag( flag_WATER_DISSOLVE ) && !loc.protected_from_liquids() ) { + dissolved.emplace_back( loc ); + } else if( loc->has_flag( flag_WATER_BREAK ) && !loc->is_broken() + && !loc.protected_from_liquids() ) { + destroyed.emplace_back( loc ); + } else if( loc->typeId() == itype_towel && !loc.protected_from_liquids() ) { + loc->convert( itype_towel_wet ); + } + } + + if( dissolved.empty() && destroyed.empty() ) { + return; + } + + for( item_location &it : dissolved ) { + add_msg_if_player_sees( ch.pos(), m_bad, _( "%1$s %2$s dissolved in the water!" ), + ch.disp_name( true, true ), it->display_name() ); + it.remove_item(); + } + + for( item_location &it : destroyed ) { + add_msg_if_player_sees( ch.pos(), m_bad, _( "The water destroyed %1$s %2$s!" ), + ch.disp_name( true ), it->display_name() ); + it->deactivate(); + it->set_flag( flag_ITEM_BROKEN ); + } +} + void game::fling_creature( Creature *c, const units::angle &dir, float flvel, bool controlled ) { if( c == nullptr ) { @@ -10906,6 +10945,11 @@ void game::fling_creature( Creature *c, const units::angle &dir, float flvel, bo } } else { c->underwater = true; + + if( p != nullptr ) { + water_affect_items( *p ); + } + if( is_u ) { if( controlled ) { add_msg( _( "You dive into water." ) ); diff --git a/src/game.h b/src/game.h index 5b5e0cf6e223a..82704452e47fd 100644 --- a/src/game.h +++ b/src/game.h @@ -549,6 +549,9 @@ class game */ std::vector get_fishable_monsters( std::unordered_set &fishable_locations ); + /** Destroy / dissolve character items when in water. */ + void water_affect_items( Character &ch ) const; + /** Flings the input creature in the given direction. */ void fling_creature( Creature *c, const units::angle &dir, float flvel, bool controlled = false ); diff --git a/src/game_inventory.cpp b/src/game_inventory.cpp index 153b04c67ef40..bb79da56fcdb9 100644 --- a/src/game_inventory.cpp +++ b/src/game_inventory.cpp @@ -1139,6 +1139,10 @@ class activatable_inventory_preset : public pickup_inventory_preset return _( "Your biology is not compatible with that item." ); } + if( it.is_broken() ) { + return string_format( _( "Your %s was broken and won't turn on." ), it.tname() ); + } + if( !p.has_enough_charges( it, false ) ) { return string_format( ngettext( "Needs at least %d charge", diff --git a/src/item.cpp b/src/item.cpp index c39a5e8ccdb5d..4c9c72aae1fbe 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -4953,6 +4953,9 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t if( is_filthy() ) { tagtext += _( " (filthy)" ); } + if( is_broken() ) { + tagtext += _( " (broken)" ); + } if( is_bionic() && !has_flag( flag_NO_PACKED ) ) { if( !has_flag( flag_NO_STERILE ) ) { tagtext += _( " (sterile)" ); @@ -6880,6 +6883,37 @@ void item::select_gun_variant() set_gun_variant( *selected ); } +bool item::can_have_gun_variant() const +{ + if( is_gun() ) { + return !type->gun->variants.empty(); + } else if( !!type->magazine ) { + return !type->magazine->variants.empty(); + } + return false; +} + +bool item::possible_gun_variant( const std::string &test ) const +{ + if( !can_have_gun_variant() ) { + return false; + } + + const auto variant_looking_for = [&test]( const gun_variant_data & variant ) { + return variant.id == test; + }; + + if( is_gun() ) { + return std::find_if( type->gun->variants.begin(), type->gun->variants.end(), + variant_looking_for ) != type->gun->variants.end(); + } else if( !!type->magazine ) { + return std::find_if( type->magazine->variants.begin(), type->magazine->variants.end(), + variant_looking_for ) != type->magazine->variants.end(); + } + + return false; +} + bool item::has_gun_variant( bool check_option ) const { return _gun_variant != nullptr && @@ -7273,6 +7307,11 @@ bool item::is_irremovable() const return has_flag( flag_IRREMOVABLE ); } +bool item::is_broken() const +{ + return has_flag( flag_ITEM_BROKEN ); +} + std::set item::faults_potential() const { std::set res; diff --git a/src/item.h b/src/item.h index 1b4bacd24def5..b81767ec36d11 100644 --- a/src/item.h +++ b/src/item.h @@ -1248,6 +1248,9 @@ class item : public visitable bool is_faulty() const; bool is_irremovable() const; + /** Returns true if the item is broken and can't be activated or used in crafting */ + bool is_broken() const; + bool is_unarmed_weapon() const; //Returns true if the item should be considered unarmed bool has_temperature() const; @@ -2388,6 +2391,11 @@ class item : public visitable // Intended to be called when no explicit variant is set void select_gun_variant(); + bool can_have_gun_variant() const; + + // Does this have a variant with this id? + bool possible_gun_variant( const std::string &test ) const; + // If the item has a gun variant, this points to it const gun_variant_data *_gun_variant = nullptr; diff --git a/src/item_location.cpp b/src/item_location.cpp index 6c485a222fab8..cb90a95e86197 100644 --- a/src/item_location.cpp +++ b/src/item_location.cpp @@ -940,3 +940,23 @@ units::mass item_location::weight_capacity() const { return ptr->weight_capacity(); } + +bool item_location::protected_from_liquids() const +{ + // check if inside a watertight which is not an open_container + if( has_parent() ) { + item_location parent = parent_item(); + + // parent can protect the item against water + if( parent->is_watertight_container() && !parent->will_spill() ) { + return true; + } + + // check the parent's parent + return parent.protected_from_liquids(); + } + + // we recursively checked all containers + // none are closed watertight containers + return false; +} diff --git a/src/item_location.h b/src/item_location.h index d0bbcbf75a67a..f3a1320ccc2be 100644 --- a/src/item_location.h +++ b/src/item_location.h @@ -119,6 +119,11 @@ class item_location */ units::mass weight_capacity() const; + /** + * true if the item is inside a not open watertight container + **/ + bool protected_from_liquids() const; + bool parents_can_contain_recursive( item *it ) const; int max_charges_by_parent_recursive( const item &it ) const; diff --git a/src/iuse.cpp b/src/iuse.cpp index c4c1bee19a323..195649347f303 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -9803,6 +9803,9 @@ ret_val use_function::can_call( const Character &p, const item &it, bool t if( actor == nullptr ) { return ret_val::make_failure( _( "You can't do anything interesting with your %s." ), it.tname() ); + } else if( it.is_broken() ) { + return ret_val::make_failure( _( "Your %s is broken and won't activate." ), + it.tname() ); } return actor->can_use( p, it, t, pos ); diff --git a/src/map_memory.cpp b/src/map_memory.cpp index 27edbca39233a..3830483497d05 100644 --- a/src/map_memory.cpp +++ b/src/map_memory.cpp @@ -45,6 +45,7 @@ struct reg_coord_pair { }; mm_submap::mm_submap() = default; +mm_submap::mm_submap( bool make_valid ) : valid( make_valid ) {} mm_region::mm_region() : submaps {{ nullptr }} {} @@ -82,6 +83,9 @@ void map_memory::memorize_tile( const tripoint &pos, const std::string &ter, { coord_pair p( pos ); mm_submap &sm = get_submap( p.sm ); + if( !sm.is_valid() ) { + return; + } sm.set_tile( p.loc, memorized_terrain_tile{ ter, subtile, rotation } ); } @@ -96,6 +100,9 @@ void map_memory::memorize_symbol( const tripoint &pos, const int symbol ) { coord_pair p( pos ); mm_submap &sm = get_submap( p.sm ); + if( !sm.is_valid() ) { + return; + } sm.set_symbol( p.loc, symbol ); } @@ -103,6 +110,9 @@ void map_memory::clear_memorized_tile( const tripoint &pos ) { coord_pair p( pos ); mm_submap &sm = get_submap( p.sm ); + if( !sm.is_valid() ) { + return; + } sm.set_symbol( p.loc, mm_submap::default_symbol ); sm.set_tile( p.loc, mm_submap::default_tile ); } @@ -235,10 +245,15 @@ shared_ptr_fast map_memory::load_submap( const tripoint &sm_pos ) } static mm_submap null_mz_submap; +static mm_submap invalid_mz_submap{ false }; const mm_submap &map_memory::get_submap( const tripoint &sm_pos ) const { - point idx = ( sm_pos - cache_pos ).xy(); + if( cache_pos == tripoint_min ) { + debugmsg( "Called map_memory with an " ); + return invalid_mz_submap; + } + const point idx = ( sm_pos - cache_pos ).xy(); if( idx.x > 0 && idx.y > 0 && idx.x < cache_size.x && idx.y < cache_size.y ) { return *cached[idx.y * cache_size.x + idx.x]; } else { @@ -248,7 +263,10 @@ const mm_submap &map_memory::get_submap( const tripoint &sm_pos ) const mm_submap &map_memory::get_submap( const tripoint &sm_pos ) { - point idx = ( sm_pos - cache_pos ).xy(); + if( cache_pos == tripoint_min ) { + return invalid_mz_submap; + } + const point idx = ( sm_pos - cache_pos ).xy(); if( idx.x > 0 && idx.y > 0 && idx.x < cache_size.x && idx.y < cache_size.y ) { return *cached[idx.y * cache_size.x + idx.x]; } else { diff --git a/src/map_memory.h b/src/map_memory.h index e92e89ea4b0de..29d40f4b0f3dc 100644 --- a/src/map_memory.h +++ b/src/map_memory.h @@ -34,12 +34,18 @@ struct mm_submap { static const int default_symbol; mm_submap(); + explicit mm_submap( bool make_valid ); /** Whether this mm_submap is empty. Empty submaps are skipped during saving. */ bool is_empty() const { return tiles.empty() && symbols.empty(); } + // Whether this mm_submap is invalid, i.e. returned from an uninitialized region. + bool is_valid() const { + return valid; + } + inline const memorized_terrain_tile &tile( const point &p ) const { if( tiles.empty() ) { return default_tile; @@ -80,6 +86,7 @@ struct mm_submap { private: std::vector tiles; // holds either 0 or SEEX*SEEY elements std::vector symbols; // holds either 0 or SEEX*SEEY elements + bool valid = true; }; /** diff --git a/src/mapgen_functions.cpp b/src/mapgen_functions.cpp index 557965de65f27..02c65d19f993a 100644 --- a/src/mapgen_functions.cpp +++ b/src/mapgen_functions.cpp @@ -2253,6 +2253,7 @@ void mapgen_hellmouth( mapgendata &dat ) void mapgen_ants_curved( mapgendata &dat ) { static const ter_str_id t_soil( "t_soil" ); + static const ter_str_id t_dirt_underground( "t_dirt_underground" ); map *const m = &dat.m; point p( SEEX, 1 ); int rn = 0; @@ -2260,18 +2261,18 @@ void mapgen_ants_curved( mapgendata &dat ) fill_background( m, t_soil ); for( int i = SEEX - 2; i <= SEEX + 3; i++ ) { - m->ter_set( point( i, 0 ), t_dirt ); - m->ter_set( point( i, 1 ), t_dirt ); - m->ter_set( point( i, 2 ), t_dirt ); - m->ter_set( point( SEEX * 2 - 1, i ), t_dirt ); - m->ter_set( point( SEEX * 2 - 2, i ), t_dirt ); - m->ter_set( point( SEEX * 2 - 3, i ), t_dirt ); + m->ter_set( point( i, 0 ), t_dirt_underground ); + m->ter_set( point( i, 1 ), t_dirt_underground ); + m->ter_set( point( i, 2 ), t_dirt_underground ); + m->ter_set( point( SEEX * 2 - 1, i ), t_dirt_underground ); + m->ter_set( point( SEEX * 2 - 2, i ), t_dirt_underground ); + m->ter_set( point( SEEX * 2 - 3, i ), t_dirt_underground ); } do { for( int i = p.x - 2; i <= p.x + 3; i++ ) { for( int j = p.y - 2; j <= p.y + 3; j++ ) { if( i > 0 && i < SEEX * 2 - 1 && j > 0 && j < SEEY * 2 - 1 ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } } @@ -2293,7 +2294,7 @@ void mapgen_ants_curved( mapgendata &dat ) for( int i = p.x - 2; i <= p.x + 3; i++ ) { for( int j = p.y - 2; j <= p.y + 3; j++ ) { if( i > 0 && i < SEEX * 2 - 1 && j > 0 && j < SEEY * 2 - 1 ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } } @@ -2312,13 +2313,14 @@ void mapgen_ants_curved( mapgendata &dat ) void mapgen_ants_four_way( mapgendata &dat ) { static const ter_str_id t_soil( "t_soil" ); + static const ter_str_id t_dirt_underground( "t_dirt_underground" ); map *const m = &dat.m; fill_background( m, t_soil ); int x = SEEX; for( int j = 0; j < SEEY * 2; j++ ) { for( int i = x - 2; i <= x + 3; i++ ) { if( i >= 1 && i < SEEX * 2 - 1 ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } x += rng( -1, 1 ); @@ -2336,7 +2338,7 @@ void mapgen_ants_four_way( mapgendata &dat ) for( int i = 0; i < SEEX * 2; i++ ) { for( int j = y - 2; j <= y + 3; j++ ) { if( j >= 1 && j < SEEY * 2 - 1 ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } y += rng( -1, 1 ); @@ -2355,13 +2357,14 @@ void mapgen_ants_four_way( mapgendata &dat ) void mapgen_ants_straight( mapgendata &dat ) { static const ter_str_id t_soil( "t_soil" ); + static const ter_str_id t_dirt_underground( "t_dirt_underground" ); map *const m = &dat.m; int x = SEEX; fill_background( m, t_soil ); for( int j = 0; j < SEEY * 2; j++ ) { for( int i = x - 2; i <= x + 3; i++ ) { if( i >= 1 && i < SEEX * 2 - 1 ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } x += rng( -1, 1 ); @@ -2383,13 +2386,14 @@ void mapgen_ants_straight( mapgendata &dat ) void mapgen_ants_tee( mapgendata &dat ) { static const ter_str_id t_soil( "t_soil" ); + static const ter_str_id t_dirt_underground( "t_dirt_underground" ); map *const m = &dat.m; fill_background( m, t_soil ); int x = SEEX; for( int j = 0; j < SEEY * 2; j++ ) { for( int i = x - 2; i <= x + 3; i++ ) { if( i >= 1 && i < SEEX * 2 - 1 ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } x += rng( -1, 1 ); @@ -2406,7 +2410,7 @@ void mapgen_ants_tee( mapgendata &dat ) for( int i = SEEX; i < SEEX * 2; i++ ) { for( int j = y - 2; j <= y + 3; j++ ) { if( j >= 1 && j < SEEY * 2 - 1 ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } y += rng( -1, 1 ); @@ -2434,6 +2438,7 @@ void mapgen_ants_tee( mapgendata &dat ) static void mapgen_ants_generic( mapgendata &dat ) { static const ter_str_id t_soil( "t_soil" ); + static const ter_str_id t_dirt_underground( "t_dirt_underground" ); map *const m = &dat.m; for( int i = 0; i < SEEX * 2; i++ ) { @@ -2441,7 +2446,7 @@ static void mapgen_ants_generic( mapgendata &dat ) if( i < SEEX - 4 || i > SEEX + 5 || j < SEEY - 4 || j > SEEY + 5 ) { m->ter_set( point( i, j ), t_soil ); } else { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } } @@ -2456,7 +2461,7 @@ static void mapgen_ants_generic( mapgendata &dat ) for( int i = p.x - cw; i <= p.x + cw; i++ ) { for( int j = p.y - cw; j <= p.y + cw; j++ ) { if( trig_dist( p, point( i, j ) ) <= cw ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } } @@ -2465,7 +2470,7 @@ static void mapgen_ants_generic( mapgendata &dat ) is_ot_match( "ants_lab", dat.north(), ot_match_type::contains ) ) { for( int i = SEEX - 2; i <= SEEX + 3; i++ ) { for( int j = 0; j <= SEEY; j++ ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } } @@ -2473,7 +2478,7 @@ static void mapgen_ants_generic( mapgendata &dat ) is_ot_match( "ants_lab", dat.east(), ot_match_type::contains ) ) { for( int i = SEEX; i <= SEEX * 2 - 1; i++ ) { for( int j = SEEY - 2; j <= SEEY + 3; j++ ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } } @@ -2481,7 +2486,7 @@ static void mapgen_ants_generic( mapgendata &dat ) is_ot_match( "ants_lab", dat.south(), ot_match_type::contains ) ) { for( int i = SEEX - 2; i <= SEEX + 3; i++ ) { for( int j = SEEY; j <= SEEY * 2 - 1; j++ ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } } @@ -2489,7 +2494,7 @@ static void mapgen_ants_generic( mapgendata &dat ) is_ot_match( "ants_lab", dat.west(), ot_match_type::contains ) ) { for( int i = 0; i <= SEEX; i++ ) { for( int j = SEEY - 2; j <= SEEY + 3; j++ ) { - m->ter_set( point( i, j ), t_dirt ); + m->ter_set( point( i, j ), t_dirt_underground ); } } } diff --git a/src/monster.cpp b/src/monster.cpp index 50dfdd97cd3ad..381a39eee428a 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -2352,7 +2352,7 @@ void monster::die( Creature *nkiller ) if( death_drops && !no_extra_death_drops ) { drop_items_on_death(); } - if( nkiller != nullptr ) { + if( get_killer() != nullptr ) { // TODO: should actually be class Character Character *ch = get_killer()->as_character(); if( !is_hallucination() && ch != nullptr ) { diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 1c61bbdf8ad83..27f1a00cac871 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -2206,6 +2206,19 @@ void talk_effect_fun_t::set_add_power( const JsonObject &jo, const std::string & }; } +void talk_effect_fun_t::set_mod_healthy( const JsonObject &jo, const std::string &member, + bool is_npc ) +{ + int amount; + int cap; + mandatory( jo, false, member, amount ); + mandatory( jo, false, "cap", cap ); + + function = [is_npc, amount, cap]( const dialogue & d ) { + d.actor( is_npc )->mod_healthy_mod( amount, cap ); + }; +} + void talk_effect_fun_t::set_assign_mission( const JsonObject &jo, const std::string &member ) { std::string mission_name = jo.get_string( member ); @@ -2569,6 +2582,10 @@ void talk_effect_t::parse_sub_effect( const JsonObject &jo ) subeffect_fun.set_make_sound( jo, "npc_make_sound", true ); } else if( jo.has_array( "set_queue_effect_on_condition" ) ) { subeffect_fun.set_queue_effect_on_condition( jo, "set_queue_effect_on_condition" ); + } else if( jo.has_member( "u_mod_healthy" ) ) { + subeffect_fun.set_mod_healthy( jo, "u_mod_healthy", false ); + } else if( jo.has_member( "npc_mod_healthy" ) ) { + subeffect_fun.set_mod_healthy( jo, "npc_mod_healthy", true ); } else { jo.throw_error( "invalid sub effect syntax: " + jo.str() ); } diff --git a/src/omdata.h b/src/omdata.h index 9231ecce3a773..6d55c39341b3b 100644 --- a/src/omdata.h +++ b/src/omdata.h @@ -248,9 +248,18 @@ struct oter_type_t { return has_flag( oter_flags::line_drawing ); } + bool has_connections() const { + return !connect_group.empty(); + } + + bool connects_to( const oter_type_id &other ) const { + return has_connections() && connect_group == other->connect_group; + } + private: enum_bitset flags; std::vector directional_peers; + std::string connect_group; // Group for connection when rendering overmap tiles void register_terrain( const oter_t &peer, size_t n, size_t max_n ); }; diff --git a/src/overmap.cpp b/src/overmap.cpp index 87f9782b5da8e..a17496a6004f5 100644 --- a/src/overmap.cpp +++ b/src/overmap.cpp @@ -575,6 +575,8 @@ void oter_type_t::load( const JsonObject &jo, const std::string &src ) const auto flag_reader = make_flag_reader( oter_flags_map, "overmap terrain flag" ); optional( jo, was_loaded, "flags", flags, flag_reader ); + optional( jo, was_loaded, "connect_group", connect_group, string_reader{} ); + if( has_flag( oter_flags::line_drawing ) ) { if( has_flag( oter_flags::no_rotate ) ) { jo.throw_error( R"(Mutually exclusive flags: "NO_ROTATE" and "LINEAR".)" ); diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 4a5f97596cc2f..e921cce09ff5d 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2675,6 +2675,14 @@ void item::deserialize( JsonIn &jsin ) } } } + + if( !has_gun_variant( false ) && can_have_gun_variant() ) { + if( possible_gun_variant( typeId().str() ) ) { + set_gun_variant( typeId().str() ); + } else { + select_gun_variant(); + } + } } void item::serialize( JsonOut &json ) const diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index 2b60560ed669b..8a0d109937d8b 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -758,16 +758,49 @@ static cata::optional> get_mission_arro std::string cata_tiles::get_omt_id_rotation_and_subtile( const tripoint_abs_omt &omp, int &rota, int &subtile ) { - oter_id ot_id = overmap_buffer.ter( omp ); + auto oter_at = []( const tripoint_abs_omt & p ) { + const oter_id &cur_ter = overmap_buffer.ter( p ); - if( !uistate.overmap_show_forest_trails && - is_ot_match( "forest_trail", ot_id, ot_match_type::type ) ) { - ot_id = oter_id( "forest" ); - } + if( !uistate.overmap_show_forest_trails && + is_ot_match( "forest_trail", cur_ter, ot_match_type::type ) ) { + return oter_id( "forest" ); + } + + return cur_ter; + }; + oter_id ot_id = oter_at( omp ); const oter_t &ot = *ot_id; oter_type_id ot_type_id = ot.get_type_id(); - ot.get_rotation_and_subtile( rota, subtile ); + oter_type_t ot_type = *ot_type_id; + + if( ot_type.has_connections() ) { + // This would be for connected terrain + + // get terrain neighborhood + const oter_type_id neighborhood[4] = { + oter_at( omp + point_south )->get_type_id(), + oter_at( omp + point_east )->get_type_id(), + oter_at( omp + point_west )->get_type_id(), + oter_at( omp + point_north )->get_type_id() + }; + + char val = 0; + + // populate connection information + for( int i = 0; i < 4; ++i ) { + if( ot_type.connects_to( neighborhood[i] ) ) { + val += 1 << i; + } + } + + get_rotation_and_subtile( val, rota, subtile ); + } else { + // 'Regular', nonlinear terrain only needs to worry about rotation, not + // subtile + ot.get_rotation_and_subtile( rota, subtile ); + } + return ot_type_id.id().str(); } @@ -829,16 +862,15 @@ void cata_tiles::draw_om( const point &dest, const tripoint_abs_omt ¢er_abs_ 100; const bool showhordes = uistate.overmap_show_hordes; const bool viewing_weather = uistate.overmap_debug_weather || uistate.overmap_visible_weather; - o = dest; + o = corner_NW.raw().xy(); - const auto global_omt_to_draw_position = [&corner_NW]( const tripoint_abs_omt & omp ) { + const auto global_omt_to_draw_position = []( 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 ); + return tripoint( omp.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 ); @@ -864,7 +896,7 @@ void cata_tiles::draw_om( const point &dest, const tripoint_abs_omt ¢er_abs_ 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, + draw_from_id_string( id, TILE_CATEGORY::C_OVERMAP_TERRAIN, "overmap_terrain", omp.raw(), subtile, rotation, ll, false, height_3d ); if( see ) { @@ -873,8 +905,8 @@ void cata_tiles::draw_om( const point &dest, const tripoint_abs_omt ¢er_abs_ 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 ); + draw_from_id_string( ( *mgroup_iter )->type->defaultMonster.str(), + omp.raw(), 0, 0, lit_level::LIT, false ); } } const int horde_size = overmap_buffer.get_horde_size( omp ); @@ -882,26 +914,32 @@ void cata_tiles::draw_om( const point &dest, const tripoint_abs_omt ¢er_abs_ // a little bit of hardcoded fallbacks for hordes if( find_tile_with_season( id ) ) { draw_from_id_string( string_format( "overmap_horde_%d", horde_size ), - pos, 0, 0, lit_level::LIT, false ); + omp.raw(), 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 ); + draw_from_id_string( "mon_zombie", omp.raw(), 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 ); + draw_from_id_string( "mon_zombie_tough", omp.raw(), 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 ); + draw_from_id_string( "mon_zombie_brute", omp.raw(), 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 ); + draw_from_id_string( "mon_zombie_hulk", omp.raw(), 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 ); + draw_from_id_string( "mon_zombie_necro", omp.raw(), 0, 0, + lit_level::LIT, false ); break; default: - draw_from_id_string( "mon_zombie_master", pos, 0, 0, lit_level::LIT, false ); + draw_from_id_string( "mon_zombie_master", omp.raw(), 0, 0, + lit_level::LIT, false ); break; } } @@ -912,17 +950,17 @@ void cata_tiles::draw_om( const point &dest, const tripoint_abs_omt ¢er_abs_ // 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 ); + draw_from_id_string( "highlight", omp.raw(), 0, 0, lit_level::LIT, false ); } } if( blink && overmap_buffer.has_vehicle( omp ) ) { if( find_tile_looks_like( "overmap_remembered_vehicle", TILE_CATEGORY::C_OVERMAP_NOTE ) ) { draw_from_id_string( "overmap_remembered_vehicle", TILE_CATEGORY::C_OVERMAP_NOTE, - "overmap_note", pos, 0, 0, lit_level::LIT, false ); + "overmap_note", omp.raw(), 0, 0, lit_level::LIT, false ); } else { draw_from_id_string( "note_c_cyan", TILE_CATEGORY::C_OVERMAP_NOTE, - "overmap_note", pos, 0, 0, lit_level::LIT, false ); + "overmap_note", omp.raw(), 0, 0, lit_level::LIT, false ); } } @@ -935,8 +973,8 @@ void cata_tiles::draw_om( const point &dest, const tripoint_abs_omt ¢er_abs_ 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 ); + draw_from_id_string( note_name, TILE_CATEGORY::C_OVERMAP_NOTE, "overmap_note", + omp.raw(), 0, 0, lit_level::LIT, false ); } } } @@ -999,7 +1037,8 @@ void cata_tiles::draw_om( const point &dest, const tripoint_abs_omt ¢er_abs_ 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 ) ); + const tripoint tile_draw_pos = global_omt_to_draw_position( project_to + ( city_pos ) ) - o; 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; diff --git a/src/suffer.cpp b/src/suffer.cpp index 110322e0bf964..bf8749ca0ea92 100644 --- a/src/suffer.cpp +++ b/src/suffer.cpp @@ -66,7 +66,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_geiger( "bio_geiger" ); static const bionic_id bio_gills( "bio_gills" ); -static const bionic_id bio_leaky( "bio_leaky" ); static const bionic_id bio_power_weakness( "bio_power_weakness" ); static const efftype_id effect_adrenaline( "adrenaline" ); @@ -110,8 +109,6 @@ static const trait_id trait_CHAOTIC_BAD( "CHAOTIC_BAD" ); static const trait_id trait_CHEMIMBALANCE( "CHEMIMBALANCE" ); static const trait_id trait_DEBUG_NOTEMP( "DEBUG_NOTEMP" ); static const trait_id trait_FRESHWATEROSMOSIS( "FRESHWATEROSMOSIS" ); -static const trait_id trait_GILLS( "GILLS" ); -static const trait_id trait_GILLS_CEPH( "GILLS_CEPH" ); static const trait_id trait_JITTERY( "JITTERY" ); static const trait_id trait_KILLER( "KILLER" ); static const trait_id trait_LEAVES( "LEAVES" ); @@ -153,6 +150,7 @@ 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 json_character_flag json_flag_GILLS( "GILLS" ); static const json_character_flag json_flag_GLARE_RESIST( "GLARE_RESIST" ); static float addiction_scaling( float at_min, float at_max, float add_lvl ) @@ -232,7 +230,7 @@ void Character::suffer_mutation_power( const trait_id &mut_id ) void Character::suffer_while_underwater() { - if( !has_trait( trait_GILLS ) && !has_trait( trait_GILLS_CEPH ) ) { + if( !has_flag( json_flag_GILLS ) ) { oxygen--; } if( oxygen < 12 && worn_with_flag( flag_REBREATHER ) ) { @@ -1190,9 +1188,6 @@ void Character::suffer_from_bad_bionics() get_power_level() >= get_max_power_level() * .75 ) { mod_str_bonus( -3 ); } - if( has_bionic( bio_leaky ) && one_turn_in( 6_minutes ) ) { - mod_healthy_mod( -1, -200 ); - } } void Character::suffer_from_stimulants( const int current_stim ) diff --git a/src/talker.h b/src/talker.h index e28534e7f8fd7..81c3d91356615 100644 --- a/src/talker.h +++ b/src/talker.h @@ -332,5 +332,6 @@ class talker virtual units::energy power_cur() const { return 0_kJ; } + virtual void mod_healthy_mod( int, int ) {}; }; #endif // CATA_SRC_TALKER_H diff --git a/src/talker_character.cpp b/src/talker_character.cpp index 6816afcc62ac9..6d6297b41d417 100644 --- a/src/talker_character.cpp +++ b/src/talker_character.cpp @@ -335,3 +335,8 @@ void talker_character::mod_fatigue( int amount ) { me_chr->mod_fatigue( amount ); } + +void talker_character::mod_healthy_mod( int amount, int cap ) +{ + me_chr->mod_healthy_mod( amount, cap ); +} diff --git a/src/talker_character.h b/src/talker_character.h index cf039d40c29fa..c3ef1f2f016c5 100644 --- a/src/talker_character.h +++ b/src/talker_character.h @@ -114,9 +114,9 @@ class talker_character: public talker bool wielded_with_flag( const flag_id &flag ) const override; void mod_fatigue( int amount ) override; - void mod_pain( int amount ) override; bool can_see() const override; + void mod_healthy_mod( int, int ) override; protected: talker_character() = default; player *me_chr; diff --git a/src/veh_interact.cpp b/src/veh_interact.cpp index 611690cd7f951..1eaf2389273b2 100644 --- a/src/veh_interact.cpp +++ b/src/veh_interact.cpp @@ -363,11 +363,18 @@ shared_ptr_fast veh_interact::create_or_get_ui_adaptor() if( !current_ui ) { ui = current_ui = make_shared_fast(); current_ui->on_screen_resize( [this]( ui_adaptor & current_ui ) { + if( ui_hidden ) { + current_ui.position( point_zero, point_zero ); + return; + } allocate_windows(); current_ui.position_from_window( catacurses::stdscr ); } ); current_ui->mark_resize(); current_ui->on_redraw( [this]( const ui_adaptor & ) { + if( ui_hidden ) { + return; + } display_grid(); display_name(); display_stats(); @@ -428,6 +435,14 @@ shared_ptr_fast veh_interact::create_or_get_ui_adaptor() return current_ui; } +void veh_interact::hide_ui( const bool hide ) +{ + if( hide != ui_hidden ) { + ui_hidden = hide; + create_or_get_ui_adaptor()->mark_resize(); + } +} + void veh_interact::do_main_loop() { bool finish = false; @@ -1913,6 +1928,10 @@ void veh_interact::do_siphon() }; auto act = [&]( const vehicle_part & pt ) { + on_out_of_scope restore_ui( [&]() { + hide_ui( false ); + } ); + hide_ui( true ); const item &base = pt.get_base(); const int idx = veh->find_part( base ); item liquid( base.legacy_front() ); diff --git a/src/veh_interact.h b/src/veh_interact.h index 2c0fc2daf2c88..af4b76ace99f8 100644 --- a/src/veh_interact.h +++ b/src/veh_interact.h @@ -97,6 +97,7 @@ class veh_interact catacurses::window w_details; catacurses::window w_name; + bool ui_hidden = false; weak_ptr_fast ui; cata::optional title; @@ -122,6 +123,7 @@ class veh_interact units::mass max_jack; shared_ptr_fast create_or_get_ui_adaptor(); + void hide_ui( bool hide ); player_activity serialize_activity(); diff --git a/src/visitable.cpp b/src/visitable.cpp index 45fb6c6f1fe68..2d91f765816ee 100644 --- a/src/visitable.cpp +++ b/src/visitable.cpp @@ -740,7 +740,7 @@ static int charges_of_internal( const T &self, const M &main, const itype_id &id bool found_tool_with_UPS = false; self.visit_items( [&]( const item * e, item * ) { - if( filter( *e ) ) { + if( !e->is_broken() && filter( *e ) ) { if( e->is_tool() ) { if( e->typeId() == id ) { // includes charges from any included magazine. @@ -856,7 +856,8 @@ static int amount_of_internal( const T &self, const itype_id &id, bool pseudo, i { int qty = 0; self.visit_items( [&qty, &id, &pseudo, &limit, &filter]( const item * e, item * ) { - if( ( id == STATIC( itype_id( "any" ) ) || e->typeId() == id ) && filter( *e ) && + if( !e->has_flag( STATIC( flag_id( "ITEM_BROKEN" ) ) ) && + ( id == STATIC( itype_id( "any" ) ) || e->typeId() == id ) && filter( *e ) && ( pseudo || !e->has_flag( STATIC( flag_id( "PSEUDO" ) ) ) ) ) { qty = sum_no_wrap( qty, 1 ); } diff --git a/tests/crafting_test.cpp b/tests/crafting_test.cpp index ab3c775839648..5bb9203b9a07c 100644 --- a/tests/crafting_test.cpp +++ b/tests/crafting_test.cpp @@ -566,10 +566,47 @@ TEST_CASE( "tool_use", "[crafting][tool]" ) jar.put_in( item( "water", calendar::turn_zero, 2 ), item_pocket::pocket_type::CONTAINER ); tools.push_back( jar ); + prep_craft( recipe_id( "water_clean" ), tools, false ); + } + SECTION( "clean_water with broken tool" ) { + std::vector tools; + tools.push_back( tool_with_ammo( "hotplate", 20 ) ); + item plastic_bottle( "bottle_plastic" ); + plastic_bottle.put_in( + item( "water", calendar::turn_zero, 2 ), item_pocket::pocket_type::CONTAINER ); + tools.push_back( plastic_bottle ); + tools.emplace_back( "pot" ); + + tools.front().set_flag( flag_id( "ITEM_BROKEN" ) ); + REQUIRE( tools.front().is_broken() ); + prep_craft( recipe_id( "water_clean" ), tools, false ); } } +TEST_CASE( "broken component", "[crafting][component]" ) +{ + GIVEN( "a recipe with its required components" ) { + recipe_id test_recipe( "flashlight" ); + + std::vector tools; + tools.emplace_back( "amplifier" ); + tools.emplace_back( "bottle_glass" ); + tools.emplace_back( "light_bulb" ); + tools.insert( tools.end(), 10, item( "cable" ) ); + + WHEN( "one of its components is broken" ) { + tools.front().set_flag( flag_id( "ITEM_BROKEN" ) ); + REQUIRE( tools.front().is_broken() ); + + THEN( "it should not be able to craft it" ) { + prep_craft( test_recipe, tools, false ); + } + } + } +} + + // Resume the first in progress craft found in the player's inventory static int resume_craft() { diff --git a/tests/item_test.cpp b/tests/item_test.cpp index 8aa058395c9aa..9313ed32ba76e 100644 --- a/tests/item_test.cpp +++ b/tests/item_test.cpp @@ -7,14 +7,18 @@ #include #include +#include "avatar.h" #include "calendar.h" #include "enums.h" +#include "flag.h" +#include "game.h" #include "item_factory.h" #include "item_pocket.h" #include "itype.h" #include "math_defines.h" #include "monstergenerator.h" #include "mtype.h" +#include "player_helpers.h" #include "ret_val.h" #include "type_id.h" #include "units.h" @@ -297,3 +301,255 @@ TEST_CASE( "item variables round-trip accurately", "[item]" ) i.set_var( "C", tripoint( 2, 3, 4 ) ); CHECK( i.get_var( "C", tripoint() ) == tripoint( 2, 3, 4 ) ); } +TEST_CASE( "water affect items while swimming check", "[item][water][swimming]" ) +{ + avatar &guy = get_avatar(); + clear_avatar(); + + GIVEN( "an item with flag WATER_DISSOLVE" ) { + + REQUIRE( item( "aspirin" ).has_flag( flag_WATER_DISSOLVE ) ); + + WHEN( "item in hand" ) { + guy.unwield(); + guy.worn.clear(); + + item aspirin( "aspirin" ); + + REQUIRE( guy.wield( aspirin ) ); + + THEN( "should dissolve in water" ) { + g->water_affect_items( guy ); + CHECK_FALSE( guy.has_item_with_flag( flag_WATER_DISSOLVE ) ); + } + } + + WHEN( "item in backpack" ) { + guy.unwield(); + guy.worn.clear(); + + item aspirin( "aspirin" ); + item backpack( "backpack" ); + + backpack.put_in( aspirin, item_pocket::pocket_type::CONTAINER ); + + REQUIRE( guy.wear_item( backpack ) ); + + THEN( "should dissolve in water" ) { + g->water_affect_items( guy ); + CHECK_FALSE( guy.has_item_with_flag( flag_WATER_DISSOLVE ) ); + } + } + + WHEN( "item in small plastic bottle" ) { + guy.unwield(); + guy.worn.clear(); + + item aspirin( "aspirin" ); + item bottle_small( "bottle_plastic_small" ); + + bottle_small.put_in( aspirin, item_pocket::pocket_type::CONTAINER ); + + REQUIRE( guy.wield( bottle_small ) ); + + THEN( "should not dissolve in water" ) { + g->water_affect_items( guy ); + CHECK( guy.has_item_with_flag( flag_WATER_DISSOLVE ) ); + } + } + + WHEN( "item in backpack inside duffel bag" ) { + guy.unwield(); + guy.worn.clear(); + + item aspirin( "aspirin" ); + item backpack( "backpack" ); + item duffelbag( "duffelbag" ); + + backpack.put_in( aspirin, item_pocket::pocket_type::CONTAINER ); + duffelbag.put_in( backpack, item_pocket::pocket_type::CONTAINER ); + + REQUIRE( guy.wear_item( duffelbag ) ); + + THEN( "should dissolve in water" ) { + g->water_affect_items( guy ); + CHECK_FALSE( guy.has_item_with_flag( flag_WATER_DISSOLVE ) ); + } + } + + WHEN( "item in backpack inside body bag" ) { + guy.unwield(); + guy.worn.clear(); + + item aspirin( "aspirin" ); + item backpack( "backpack" ); + item body_bag( "test_waterproof_bag" ); + + backpack.put_in( aspirin, item_pocket::pocket_type::CONTAINER ); + body_bag.put_in( backpack, item_pocket::pocket_type::CONTAINER ); + + REQUIRE( guy.wield( body_bag ) ); + + THEN( "should not dissolve in water" ) { + g->water_affect_items( guy ); + CHECK( guy.has_item_with_flag( flag_WATER_DISSOLVE ) ); + } + } + } + + GIVEN( "an item with flag WATER_BREAK" ) { + + REQUIRE( item( "mp3" ).has_flag( flag_WATER_BREAK ) ); + + WHEN( "item in hand" ) { + guy.unwield(); + guy.worn.clear(); + + item mp3( "mp3" ); + + REQUIRE( guy.wield( mp3 ) ); + + THEN( "should be broken by water" ) { + g->water_affect_items( guy ); + CHECK( guy.has_item_with_flag( flag_ITEM_BROKEN ) ); + } + } + + WHEN( "item in backpack" ) { + guy.unwield(); + guy.worn.clear(); + + item mp3( "mp3" ); + item backpack( "backpack" ); + + backpack.put_in( mp3, item_pocket::pocket_type::CONTAINER ); + + REQUIRE( guy.wear_item( backpack ) ); + + THEN( "should be broken by water" ) { + g->water_affect_items( guy ); + CHECK( guy.has_item_with_flag( flag_ITEM_BROKEN ) ); + } + } + + WHEN( "item in zipper bag" ) { + guy.unwield(); + guy.worn.clear(); + + item mp3( "mp3" ); + item bag_zipper( "bag_zipper" ); + + bag_zipper.put_in( mp3, item_pocket::pocket_type::CONTAINER ); + + REQUIRE( guy.wield( bag_zipper ) ); + + THEN( "should not be broken by water" ) { + g->water_affect_items( guy ); + CHECK_FALSE( guy.has_item_with_flag( flag_ITEM_BROKEN ) ); + } + } + + WHEN( "item in backpack inside duffel bag" ) { + guy.unwield(); + guy.worn.clear(); + + item mp3( "mp3" ); + item backpack( "backpack" ); + item duffelbag( "duffelbag" ); + + backpack.put_in( mp3, item_pocket::pocket_type::CONTAINER ); + duffelbag.put_in( backpack, item_pocket::pocket_type::CONTAINER ); + + REQUIRE( guy.wear_item( duffelbag ) ); + + THEN( "should be broken by water" ) { + g->water_affect_items( guy ); + CHECK( guy.has_item_with_flag( flag_ITEM_BROKEN ) ); + } + } + + WHEN( "item in backpack inside body bag" ) { + guy.unwield(); + guy.worn.clear(); + + item mp3( "mp3" ); + item backpack( "backpack" ); + item body_bag( "test_waterproof_bag" ); + + backpack.put_in( mp3, item_pocket::pocket_type::CONTAINER ); + body_bag.put_in( backpack, item_pocket::pocket_type::CONTAINER ); + + REQUIRE( guy.wield( body_bag ) ); + + THEN( "should not be broken by water" ) { + g->water_affect_items( guy ); + CHECK_FALSE( guy.has_item_with_flag( flag_ITEM_BROKEN ) ); + } + } + } + + GIVEN( "a towel" ) { + + WHEN( "item in hand" ) { + guy.unwield(); + guy.worn.clear(); + + item towel( "towel" ); + + REQUIRE( guy.wield( towel ) ); + + THEN( "should get wet in water" ) { + g->water_affect_items( guy ); + CHECK( guy.has_item_with_flag( flag_WET ) ); + } + } + + WHEN( "wearing item" ) { + guy.unwield(); + guy.worn.clear(); + + item towel( "towel" ); + + REQUIRE( guy.wear_item( towel ) ); + + THEN( "should get wet in water" ) { + g->water_affect_items( guy ); + CHECK( guy.has_item_with_flag( flag_WET ) ); + } + } + + WHEN( "inside a backpack" ) { + guy.unwield(); + guy.worn.clear(); + + item towel( "towel" ); + item backpack( "backpack" ); + + backpack.put_in( towel, item_pocket::pocket_type::CONTAINER ); + + REQUIRE( guy.wield( backpack ) ); + + THEN( "should get wet in water" ) { + g->water_affect_items( guy ); + CHECK( guy.has_item_with_flag( flag_WET ) ); + } + } + + WHEN( "inside a body bag" ) { + guy.unwield(); + guy.worn.clear(); + + item towel( "towel" ); + item body_bag( "test_waterproof_bag" ); + + body_bag.put_in( towel, item_pocket::pocket_type::CONTAINER ); + + REQUIRE( guy.wield( body_bag ) ); + + THEN( "should not get wet in water" ) { + g->water_affect_items( guy ); + CHECK_FALSE( guy.has_item_with_flag( flag_WET ) ); + } + } + } +} diff --git a/tests/map_memory_test.cpp b/tests/map_memory_test.cpp index 0c9a1ffe9b176..847948ca26b0d 100644 --- a/tests/map_memory_test.cpp +++ b/tests/map_memory_test.cpp @@ -66,6 +66,13 @@ TEST_CASE( "map_memory_overwrites", "[map_memory]" ) CHECK( memory.get_symbol( p2 ) == 3 ); } +TEST_CASE( "map_memory_forgets", "[map_memory]" ) +{ + map_memory memory; + memory.memorize_symbol( tripoint_zero, 1 ); + memory.memorize_symbol( p3, 1 ); +} + // TODO: map memory save / load #include