From 6067b6a1de60438b4b10cdccd54364c344f36a0d Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Tue, 9 Jan 2024 04:56:02 -0800 Subject: [PATCH 001/202] i am the night --- data/json/mutations/mutations.json | 126 +++++++++++++++++++++++++---- 1 file changed, 112 insertions(+), 14 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 62ddf1a3a849a..4e85351b38994 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -774,9 +774,39 @@ "description": "Your hearing is better than average, and you can hear distant sounds more easily.", "starting_trait": true, "types": [ "HEARING" ], - "category": [ "ALPHA", "MOUSE", "ELFA", "RABBIT" ], + "changes_to": [ "BATEARS" ], + "category": [ "ALPHA", "MOUSE", "ELFA", "RABBIT", "CHIROPTERAN" ], "hearing_modifier": 1.25 }, + { + "type": "mutation", + "id": "BATEARS", + "name": { "str": "Bat Ears" }, + "points": 1, + "description": "Your broad, pointed ears are like radar dishes. You can hear even the slightest sounds at great distance.", + "types": [ "HEARING" ], + "prereqs": [ "GOODHEARING" ] + "category": [ "CHIROPTERAN" ], + "hearing_modifier": 2.0 + }, + { + "type": "mutation", + "id": "ECHOLOCATION", + "name": { "str": "Echolocation" }, + "points": 3, + "description": "You've learned how to hunt without sight. As long as you're awake and able to hear, you'll never suffer penalties for fighting enemies you can't see." + "prereqs": [ "BATEARS" ], + "category": [ "CHIROPTERAN" ] + }, + { + "type": "mutation", + "id": "LEAFNOSE", + "name": { "str": "Leaf Nose" }, + "points": 2, + "flags": [ "INFRARED" ], + "ugliness": 3, + "description": "Your nose is upturned, ridged and flared out into a leaf shape that humans will likely find unsightly. On the plus side, thermoreceptors in your nostrils allow you to smell heat as long as your face is uncovered." + }, { "type": "mutation", "id": "FEYHEARING", @@ -851,7 +881,7 @@ "points": 3, "description": "You're just generally quick! You get a 10% bonus to action points.", "starting_trait": true, - "category": [ "FISH", "BIRD", "INSECT", "TROGLOBITE", "CHIMERA", "RAPTOR", "MOUSE", "RABBIT" ], + "category": [ "FISH", "BIRD", "INSECT", "TROGLOBITE", "CHIMERA", "RAPTOR", "MOUSE", "RABBIT", "CHIROPTERAN" ], "enchantments": [ { "condition": "ALWAYS", "values": [ { "value": "SPEED", "multiply": 0.1 } ] } ] }, { @@ -2366,7 +2396,7 @@ "description": "Your body hair is growing in thicker than it used to. It's almost like you're going through a second puberty or something.", "types": [ "SKIN", "BODY_ARMOR", "ARMOR", "BODY_ARMOR_MOD" ], "changes_to": [ "LIGHTFUR" ], - "category": [ "MOUSE", "BEAST", "CATTLE", "RAT", "FELINE", "LUPINE", "RABBIT", "URSINE" ] + "category": [ "MOUSE", "BEAST", "CATTLE", "RAT", "FELINE", "LUPINE", "RABBIT", "URSINE", "CHIROPTERAN" ] }, { "type": "mutation", @@ -3039,7 +3069,7 @@ "ugliness": 2, "bodytemp_sleep": 75, "description": "Light fur has grown to cover your entire body, providing marginal protection against attacks and slight protection from cold.", - "category": [ "MOUSE", "BEAST", "RAT", "LUPINE", "URSINE", "FELINE", "RABBIT", "CATTLE" ], + "category": [ "MOUSE", "BEAST", "RAT", "LUPINE", "URSINE", "FELINE", "RABBIT", "CATTLE", "CHIROPTERAN" ], "types": [ "SKIN" ], "prereqs": [ "HIRSUTE" ], "changes_to": [ "FUR", "FELINE_FUR", "LUPINE_FUR", "RABBIT_FUR", "LUPINE_FUR_SUMMER" ], @@ -4518,6 +4548,73 @@ "movecost_modifier": 0.88, "category": [ "BATRACHIAN", "RABBIT", "FELINE", "LUPINE", "SPIDER" ] }, + { + "type": "mutation", + "id": "BAT_FEET", + "name": { "str": "Bat Legs" }, + "points": -1, + "visibility": 2, + "ugliness": 2, + "description": "Your lower limbs have shortened and grown bow-legged, slowing your bipedal movement. But as long as you're not holding anything in your fingers, you're able to crawl quickly and quietly.", + "types": [ "FEET" ], + "prereqs": [ "PADDED_FEET", "WINGS_BAT" ], + "category": [ "CHIROPTERAN" ], + "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], + "restricts_gear": [ "foot_l", "foot_r" ], + "destroys_gear": true, + "triggers": [ + [ + { + "condition": { + "and": [ + { "u_has_move_mode": "run" }, + { "not": "u_can_drop_weapon" }, + { "u_has_trait": "WINGS_BAT" } + ] + }, + "msg_on": { "text": "You hunch down, using your wings to crawl." }, + "msg_off": { "text": "You rise up to walk on your hind feet." } + } + ] + ], + "enchantments": [ + { + "condition": { + "and": [ + { "u_has_move_mode": "crouch" }, + { "not": "u_can_drop_weapon" }, + { "u_has_trait": "WINGS_BAT" } + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.25 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ] + }, + { + "condition": { + "and": [ + { "u_has_move_mode": "run" }, + { "not": "u_can_drop_weapon" }, + { "u_has_trait": "WINGS_BAT" } + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ] + }, + { + "condition": { + "or": [ + "u_can_drop_weapon", + { "u_has_move_mode": "walk" }, + { + "and": [ + { "not": { "u_has_trait": "THRESH_CHIROPTERAN" } }, + ] + }, + { "not": { "u_has_trait": "WINGS_BAT" } } + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.1 } ] + } + ] + }, { "type": "mutation", "id": "ANIMAL_FEET", @@ -6539,7 +6636,8 @@ "ugliness": 4, "description": "You have a flattened nose and thin slits for nostrils, giving you a lizard-like appearance. This makes breathing slightly difficult and increases mouth encumbrance by 10.", "encumbrance_always": [ [ "mouth", 10 ] ], - "category": [ "LIZARD", "CEPHALOPOD", "RAPTOR" ] + "changes_to": [ "LEAFNOSE" ], + "category": [ "LIZARD", "CEPHALOPOD", "RAPTOR", "CHIROPTERAN" ] }, { "type": "mutation", @@ -6665,22 +6763,21 @@ "ugliness": 2, "description": "You have a pair of stubby little wings projecting from your shoulderblades. They can be wiggled at will, but are useless.", "types": [ "WINGS" ], - "changes_to": [ "WINGS_INSECT", "WINGS_BUTTERFLY", "WINGS_BAT" ], + "changes_to": [ "WINGS_INSECT", "WINGS_BUTTERFLY" ], "category": [ "INSECT", "BEAST" ] }, { "type": "mutation", "id": "WINGS_BAT", "name": { "str": "Bat Wings" }, - "points": -5, + "points": 4, "visibility": 9, "ugliness": 4, - "description": "You have a pair of large, leathery wings. You can move them a little, but they are useless, and in fact put you off balance, reducing your ability to dodge slightly.", - "types": [ "WINGS" ], - "prereqs": [ "WINGS_STUB" ], - "category": [ "BEAST" ], - "threshreq": [ "THRESH_BEAST" ], - "dodge_modifier": -2 + "description": "Most of your fingers have grown to tremendous length, becoming a broad pair of leathery wings. This allows you to arrest a fall or glide from a ledge if you aren't too weighed down, but naturally impedes your fine motor skills. They even keep you warm when you sleep.", + "types": [ "ARMS", "HANDS" ], + "prereqs": [ "WEBBED" ], + "category": [ "CHIROPTERAN" ], + "threshreq": [ "THRESH_CHIROPTERAN" ] }, { "type": "mutation", @@ -7337,8 +7434,9 @@ "types": [ "HANDS" ], "description": "Your hands are heavily webbed, reducing your Dexterity by 1 and causing problems with gloves. However, you can swim much faster. Slightly decreases wet penalties.", "leads_to": [ "WEBBED_FEET" ], - "category": [ "LIZARD", "FISH", "SLIME", "BATRACHIAN" ], + "category": [ "LIZARD", "FISH", "SLIME", "BATRACHIAN", "CHIROPTERAN" ], "flags": [ "WEBBED_HANDS" ], + "changes_to": [ "WINGS_BAT" ], "wet_protection": [ { "part": "hand_l", "good": 3 }, { "part": "hand_r", "good": 3 } ], "encumbrance_covered": [ [ "hand_l", 50 ], [ "hand_r", 50 ] ], "passive_mods": { "dex_mod": -1 } From dbf8492ab216a205ae1e5021caca2eaf7d5397e9 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Tue, 9 Jan 2024 05:34:47 -0800 Subject: [PATCH 002/202] Update mutations.json --- data/json/mutations/mutations.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 4e85351b38994..7fcc700c2753b 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -5688,7 +5688,7 @@ "threshreq": [ "THRESH_BIRD" ], "category": [ "BIRD" ], "restricts_gear": [ "arm_l", "arm_r", "hand_l", "hand_r", "arm_stub_r", "arm_stub_l" ], - "flags": [ "WINGS_2", "WING_GLIDE" ] + "flags": [ "WINGS_2", "WING_GLIDE", "ARM_WINGS" ] }, { "type": "mutation", @@ -6775,9 +6775,11 @@ "ugliness": 4, "description": "Most of your fingers have grown to tremendous length, becoming a broad pair of leathery wings. This allows you to arrest a fall or glide from a ledge if you aren't too weighed down, but naturally impedes your fine motor skills. They even keep you warm when you sleep.", "types": [ "ARMS", "HANDS" ], + "flags": [ "WINGGLIDE", "WINGS_2", "ARM_WINGS" ] "prereqs": [ "WEBBED" ], "category": [ "CHIROPTERAN" ], - "threshreq": [ "THRESH_CHIROPTERAN" ] + "restricts_gear": [ "arm_l", "arm_r", "hand_l", "hand_r" ], + "threshreq": [ "THRESH_CHIROPTERAN" ], }, { "type": "mutation", @@ -6863,7 +6865,8 @@ "RAT", "CHIMERA", "SPIDER", - "CRUSTACEAN" + "CRUSTACEAN", + "CHIROPTERAN" ] }, { From fc24310c97fa4b129bf3ec9d5547a5c8901bd7d5 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Tue, 9 Jan 2024 05:43:19 -0800 Subject: [PATCH 003/202] Update mutations.json --- data/json/mutations/mutations.json | 31 +++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 7fcc700c2753b..caf5ea05ba891 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -967,7 +967,7 @@ "cancels": [ "MET_RAT" ], "types": [ "METABOLISM" ], "changes_to": [ "GIZZARD" ], - "category": [ "FISH", "BIRD", "TROGLOBITE", "GASTROPOD", "CEPHALOPOD", "RAPTOR" ], + "category": [ "FISH", "BIRD", "TROGLOBITE", "CHIROPTERAN", "GASTROPOD", "CEPHALOPOD", "RAPTOR" ], "metabolism_modifier": -0.333 }, { @@ -1357,7 +1357,7 @@ "description": "It's very unlikely that you will catch ambient diseases like the cold or flu.", "starting_trait": true, "changes_to": [ "DISIMMUNE" ], - "category": [ "CATTLE", "RAT", "MEDICAL", "PLANT", "SLIME", "TROGLOBITE", "CHIMERA" ] + "category": [ "CATTLE", "RAT", "MEDICAL", "PLANT", "SLIME", "TROGLOBITE", "CHIMERA", "CHIROPTERAN" ] }, { "type": "mutation", @@ -1971,7 +1971,7 @@ "starting_trait": true, "cancels": [ "PROJUNK" ], "vitamins_absorb_multi": [ [ "junk", [ [ "vitC", 0 ], [ "calcium", 0 ], [ "iron", 0 ] ] ] ], - "category": [ "RAPTOR", "ALPHA", "ELFA", "RABBIT", "GASTROPOD" ] + "category": [ "RAPTOR", "ALPHA", "ELFA", "RABBIT", "GASTROPOD", "CHIROPTERAN" ] }, { "type": "mutation", @@ -1980,7 +1980,8 @@ "points": -2, "description": "You have a rare allergy that prevents you from eating wheat. It's possible for you to eat wheat-based products, but you will suffer morale penalties and obtain less nutrition from them.", "vitamins_absorb_multi": [ [ "wheat", [ [ "vitC", 0 ], [ "calcium", 0 ], [ "iron", 0 ] ] ] ], - "starting_trait": true + "starting_trait": true, + "category: [ "CHIROPTERAN" ] }, { "type": "mutation", @@ -2574,6 +2575,17 @@ "active": true, "starts_active": true }, + { + "type": "mutation", + "id": "MESOPIC", + "name": { "str": "Mesopic" }, + "points": -1, + "description": "You can't see very well in bright light.", + "types": [ "EYES", "VISION" ], + "cancels": [ "MYOPIC" ], + "category": [ "CHIROPTERAN" ], + "flags": [ "MYOPIC_IN_LIGHT" ] + }, { "type": "mutation", "id": "BIRD_EYE", @@ -3023,7 +3035,7 @@ "mixed_effect": true, "description": "Your bones are very light. This enables you to move and attack 10% faster, but also reduces your carrying weight by 20% and makes bashing attacks hurt a little more.", "types": [ "BONES" ], - "category": [ "MOUSE", "RABBIT", "BIRD", "SLIME", "ELFA", "SPIDER" ], + "category": [ "MOUSE", "RABBIT", "BIRD", "SLIME", "ELFA", "SPIDER", "CHIROPTERAN" ], "changes_to": [ "HOLLOW_BONES" ], "movecost_modifier": 0.9, "attackcost_modifier": 0.9, @@ -4331,7 +4343,7 @@ "description": "Your immune system is particularly good at resisting infections. You have an increased chance for bad wounds and infections to heal on their own, and only suffer reduced penalties from them.", "starting_trait": true, "changes_to": [ "INFIMMUNE" ], - "category": [ "TROGLOBITE", "RAT", "MEDICAL", "GASTROPOD", "CHIMERA", "SLIME" ] + "category": [ "TROGLOBITE", "RAT", "MEDICAL", "GASTROPOD", "CHIMERA", "SLIME", "CHIROPTERAN" ] }, { "type": "mutation", @@ -6239,6 +6251,7 @@ "changes_to": [ "DEX_UP_2" ], "category": [ "INSECT", + "CHIROPTERAN", "ALPHA", "LIZARD", "SPIDER", @@ -6263,7 +6276,7 @@ "types": [ "DEX" ], "prereqs": [ "DEX_UP" ], "changes_to": [ "DEX_UP_3", "DEX_ALPHA" ], - "category": [ "LIZARD", "SPIDER", "CHIMERA", "RAPTOR", "MOUSE", "RABBIT", "BIRD", "ELFA", "FELINE", "CEPHALOPOD", "FISH", "ALPHA" ], + "category": [ "LIZARD", "SPIDER", "CHIMERA", "RAPTOR", "MOUSE", "RABBIT", "BIRD", "ELFA", "FELINE", "CEPHALOPOD", "FISH", "ALPHA", "CHIROPTERAN" ], "passive_mods": { "dex_mod": 2 } }, { @@ -6275,8 +6288,8 @@ "types": [ "DEX" ], "prereqs": [ "DEX_UP_2" ], "changes_to": [ "DEX_UP_4" ], - "category": [ "BIRD", "ELFA", "FELINE", "CEPHALOPOD", "FISH" ], - "threshreq": [ "THRESH_BIRD", "THRESH_ELFA", "THRESH_FELINE", "THRESH_CEPHALOPOD", "THRESH_FISH" ], + "category": [ "BIRD", "ELFA", "FELINE", "CEPHALOPOD", "FISH", "CHIROPTERAN" ], + "threshreq": [ "THRESH_BIRD", "THRESH_ELFA", "THRESH_FELINE", "THRESH_CEPHALOPOD", "THRESH_FISH", "THRESH_CHIROPTERAN" ], "passive_mods": { "dex_mod": 4 } }, { From 7c2b6577ce7fd06bf956ec78226deae464d8f5f9 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Tue, 9 Jan 2024 21:46:50 -0800 Subject: [PATCH 004/202] commas --- data/json/mutations/mutation_category.json | 11 +++++++++++ data/json/mutations/mutations.json | 23 ++++++++++------------ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/data/json/mutations/mutation_category.json b/data/json/mutations/mutation_category.json index 0b2ed66435a7e..bf0215dc3682c 100644 --- a/data/json/mutations/mutation_category.json +++ b/data/json/mutations/mutation_category.json @@ -286,6 +286,17 @@ "base_removal_chance": 33, "base_removal_cost_mul": 2.5 }, + { + "type": "mutation_category", + "id": "CHIROPTERAN", + "name": "Chiropteran", + "threshold_mut": "THRESH_CHIROPTERAN", + "mutagen_message": "Your ears ring, and you thirst for blood.", + "memorial_message": "Became the night.", + "vitamin": "mutagen_crustacean", + "base_removal_chance": 33, + "base_removal_cost_mul": 2.5 + }, { "type": "mutation_category", "id": "MYCUS", diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index caf5ea05ba891..ee219cf7b09ae 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -785,7 +785,7 @@ "points": 1, "description": "Your broad, pointed ears are like radar dishes. You can hear even the slightest sounds at great distance.", "types": [ "HEARING" ], - "prereqs": [ "GOODHEARING" ] + "prereqs": [ "GOODHEARING" ], "category": [ "CHIROPTERAN" ], "hearing_modifier": 2.0 }, @@ -794,7 +794,7 @@ "id": "ECHOLOCATION", "name": { "str": "Echolocation" }, "points": 3, - "description": "You've learned how to hunt without sight. As long as you're awake and able to hear, you'll never suffer penalties for fighting enemies you can't see." + "description": "You've learned how to hunt without sight. As long as you're awake and able to hear, you'll never suffer penalties for fighting enemies you can't see.", "prereqs": [ "BATEARS" ], "category": [ "CHIROPTERAN" ] }, @@ -1981,7 +1981,7 @@ "description": "You have a rare allergy that prevents you from eating wheat. It's possible for you to eat wheat-based products, but you will suffer morale penalties and obtain less nutrition from them.", "vitamins_absorb_multi": [ [ "wheat", [ [ "vitC", 0 ], [ "calcium", 0 ], [ "iron", 0 ] ] ] ], "starting_trait": true, - "category: [ "CHIROPTERAN" ] + "category": [ "CHIROPTERAN" ] }, { "type": "mutation", @@ -4616,14 +4616,11 @@ "u_can_drop_weapon", { "u_has_move_mode": "walk" }, { - "and": [ - { "not": { "u_has_trait": "THRESH_CHIROPTERAN" } }, - ] - }, - { "not": { "u_has_trait": "WINGS_BAT" } } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.1 } ] + "and": [ { "not": { "u_has_trait": "WINGS_BAT" } } ] + } + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.1 } ] } ] }, @@ -6788,11 +6785,11 @@ "ugliness": 4, "description": "Most of your fingers have grown to tremendous length, becoming a broad pair of leathery wings. This allows you to arrest a fall or glide from a ledge if you aren't too weighed down, but naturally impedes your fine motor skills. They even keep you warm when you sleep.", "types": [ "ARMS", "HANDS" ], - "flags": [ "WINGGLIDE", "WINGS_2", "ARM_WINGS" ] + "flags": [ "WINGGLIDE", "WINGS_2", "ARM_WINGS" ], "prereqs": [ "WEBBED" ], "category": [ "CHIROPTERAN" ], "restricts_gear": [ "arm_l", "arm_r", "hand_l", "hand_r" ], - "threshreq": [ "THRESH_CHIROPTERAN" ], + "threshreq": [ "THRESH_CHIROPTERAN" ] }, { "type": "mutation", From 46aa9e6ba1fe31f59f587b0e75195b769d26cf0d Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 12 Jan 2024 07:04:03 -0800 Subject: [PATCH 005/202] teef --- data/json/items/armor/integrated.json | 28 +++++++++++++++++++++++++++ data/json/mutations/mutations.json | 24 +++++++++++++++++++++++ data/json/techniques.json | 13 +++++++++++++ src/martialarts.cpp | 2 +- 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index 9e2bf59bf914b..b498093bda51e 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1582,5 +1582,33 @@ "relic_data": { "passive_effects": [ { "has": "WORN", "condition": { "not": "u_has_weapon" }, "values": [ { "value": "ATTACK_SPEED", "add": -20 } ] } ] } + }, + { + "id": "integrated_vampire_fangs", + "type": "ARMOR", + "category": "armor", + "name": { "str_sp": "vampire fangs" }, + "description": "Useful for the hunt, suitable for maximum efficiency.", + "weight": "112 g", + "volume": "125 ml", + "price": 0, + "price_postapoc": 0, + "material": [ "bone" ], + "symbol": ",", + "color": "white", + "warmth": 5, + "to_hit": 1, + "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], + "techniques": [ "VAMPIRE_BITE" ], + "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "OUTER", "PADDED", "PROVIDES_TECHNIQUES" ], + "armor": [ + { + "material": [ { "type": "bone", "covered_by_mat": 100, "thickness": 3.2 } ], + "covers": [ "mouth" ], + "coverage": 10, + "encumbrance": 0 + } + ], + "melee_damage": { "stab": 20 } } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index ee219cf7b09ae..28ebf71ddcd2e 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -2772,6 +2772,20 @@ "base_damage": [ { "damage_type": "cut", "amount": 3 }, { "damage_type": "bash", "amount": 3 } ] } }, + { + "type": "mutation", + "id": "VAMPIRE_FANGS", + "name": { "str": "Vampire Fangs" }, + "points": 2, + "visibility": 3, + "ugliness": 3, + "description": "Your teeth are so sharp, you can use them to shave the fur from your victims before you feed. What's more, anticoagulants in your saliva will keep the blood flowing long after it should have stopped.", + "types": [ "TEETH" ], + "prereqs": [ "FANGS" ], + "threshreq": [ "THRESH_CHIROPTERAN" ], + "category": [ "CHIROPTERAN" ], + "integrated_armor": [ "integrated_vampire_fangs" ] + }, { "type": "mutation", "id": "MEMBRANE", @@ -8758,6 +8772,16 @@ "purifiable": false, "threshold": true }, + { + "type": "mutation", + "id": "THRESH_CHIROPTERAN", + "name": { "str": "Chiropteran" }, + "points": 1, + "description": "You long for a colony, the cover of darkness, and a meal on the wing.", + "valid": false, + "purifiable": false, + "threshold": true + }, { "type": "mutation", "id": "ACIDPROOF", diff --git a/data/json/techniques.json b/data/json/techniques.json index 37b04fd8cce5e..d707aaaf75de9 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3490,5 +3490,18 @@ } ], "attack_vectors": [ "WEAPON", "HAND" ] + }, + { + "type": "technique", + "id": "VAMPIRE_BITE", + "name": "Vampire Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -10, + "reach_ok": false, + "attack_override": true, + "crit_ok": true, + "attack_vectors": [ "MOUTH" ] } ] diff --git a/src/martialarts.cpp b/src/martialarts.cpp index ff073c4ce71d1..4c6a87819c4f4 100644 --- a/src/martialarts.cpp +++ b/src/martialarts.cpp @@ -1391,7 +1391,7 @@ bool character_martial_arts::can_use_attack_vector( const Character &user, bool healthy_arm = arm_r_hp > 0 || arm_l_hp > 0; bool healthy_arms = arm_r_hp > 0 && arm_l_hp > 0; bool healthy_legs = leg_r_hp > 0 && leg_l_hp > 0; - bool always_ok = av == "HEAD" || av == "TORSO"; + bool always_ok = av == "HEAD" || av == "MOUTH" || av == "TORSO"; bool weapon_ok = av == "WEAPON" && valid_weapon && healthy_arm; bool arm_ok = ( av == "HAND" || av == "FINGER" || av == "WRIST" || av == "ARM" || av == "ELBOW" || av == "HAND_BACK" || av == "PALM" || av == "SHOULDER" ) && healthy_arm; From b1c5402f59499930833b515b04005ea895982939 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 12 Jan 2024 07:07:46 -0800 Subject: [PATCH 006/202] Update integrated.json --- data/json/items/armor/integrated.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index b498093bda51e..b6013418eab5c 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1588,7 +1588,7 @@ "type": "ARMOR", "category": "armor", "name": { "str_sp": "vampire fangs" }, - "description": "Useful for the hunt, suitable for maximum efficiency.", + "description": "A wicked pair of fangs, pointed and surgically sharp.", "weight": "112 g", "volume": "125 ml", "price": 0, @@ -1603,12 +1603,12 @@ "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "OUTER", "PADDED", "PROVIDES_TECHNIQUES" ], "armor": [ { - "material": [ { "type": "bone", "covered_by_mat": 100, "thickness": 3.2 } ], + "material": [ { "type": "bone", "covered_by_mat": 0, "thickness": 3.2 } ], "covers": [ "mouth" ], - "coverage": 10, + "coverage": 0, "encumbrance": 0 } ], - "melee_damage": { "stab": 20 } + "melee_damage": { "pierce": 20 } } ] From 5c97f9344f35b53f65ddb937570d566a9b6d3853 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 12 Jan 2024 21:53:08 -0800 Subject: [PATCH 007/202] experimentation --- data/json/flags.json | 5 +++++ data/json/mutations/mutations.json | 5 +++++ data/json/techniques.json | 2 +- src/melee.cpp | 11 ++++++----- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/data/json/flags.json b/data/json/flags.json index 57748ac86ab26..7915d26386419 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2387,5 +2387,10 @@ "id": "BLEEDSLOW2", "type": "json_flag", "info": "The character bleeds even slower than normal, losing blood at 1/3rd the normal rate." + }, + { + "id": "QUADRUPED", + "type": "json_flag", + "info": "This character receives no penalty for crouching in melee." } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 28ebf71ddcd2e..7c973d940247d 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -8549,6 +8549,7 @@ "points": 1, "description": "It's about time you grew out. Now that you've matured, it is time to make something of yourself.", "valid": false, + "flags": [ "QUADRUPED" ], "purifiable": false, "threshold": true }, @@ -8559,6 +8560,7 @@ "points": 1, "description": "Stalking prey, eating well, and lying in the sun. Mmm, all you could ever desire.", "valid": false, + "flags": [ "QUADRUPED" ], "purifiable": false, "threshold": true }, @@ -8569,6 +8571,7 @@ "points": 1, "description": "You're the perfect candidate to lead a pack.", "valid": false, + "flags": [ "QUADRUPED" ], "purifiable": false, "threshold": true }, @@ -8779,6 +8782,7 @@ "points": 1, "description": "You long for a colony, the cover of darkness, and a meal on the wing.", "valid": false, + "flags": [ "QUADRUPED" ], "purifiable": false, "threshold": true }, @@ -9577,6 +9581,7 @@ "points": 1, "description": "You really want to eat some carrots. Welcome to the Lagomorpha family.", "valid": false, + "flags": [ "QUADRUPED" ], "purifiable": false, "threshold": true }, diff --git a/data/json/techniques.json b/data/json/techniques.json index d707aaaf75de9..d898166c4ab44 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3498,7 +3498,7 @@ "melee_allowed": true, "messages": [ "You bite %s!", " bites %s!" ], "unarmed_allowed": true, - "weighting": -10, + "weighting": -30, "reach_ok": false, "attack_override": true, "crit_ok": true, diff --git a/src/melee.cpp b/src/melee.cpp index 51d9eba527f6a..472df41451629 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -121,6 +121,7 @@ static const json_character_flag json_flag_HYPEROPIC( "HYPEROPIC" ); static const json_character_flag json_flag_NEED_ACTIVE_TO_MELEE( "NEED_ACTIVE_TO_MELEE" ); static const json_character_flag json_flag_NULL( "NULL" ); static const json_character_flag json_flag_UNARMED_BONUS( "UNARMED_BONUS" ); +static const json_character_flag json_flag_QUADRUPED( "QUADRUPED" ); static const limb_score_id limb_score_block( "block" ); static const limb_score_id limb_score_grip( "grip" ); @@ -376,7 +377,7 @@ float Character::hit_roll() const // Difficult to land a hit while prone if( is_on_ground() ) { hit -= 8.0f; - } else if( is_crouching() ) { + } else if( is_crouching() && !has_flag( json_flag_QUADRUPED ) ) { hit -= 2.0f; } @@ -775,7 +776,7 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, // being prone affects how much leverage you can use to deal damage if( is_on_ground() ) { d.mult_damage( 0.3 ); - } else if( is_crouching() ) { + } else if( is_crouching() && !has_flag( json_flag_QUADRUPED ) ) { d.mult_damage( 0.8 ); } @@ -946,7 +947,7 @@ int Character::get_total_melee_stamina_cost( const item *weap ) const { const int mod_sta = get_standard_stamina_cost( weap ); const int melee = round( get_skill_level( skill_melee ) ); - const int stance_malus = is_on_ground() ? 50 : ( is_crouching() ? 20 : 0 ); + const int stance_malus = is_on_ground() ? 50 : ( !has_flag( json_flag_QUADRUPED ) && is_crouching() ? 20 : 0 ); return std::min( -50, mod_sta + melee - stance_malus ); } @@ -1040,7 +1041,7 @@ int stumble( Character &u, const item_location &weap ) units::mass str_mod = u.get_arm_str() * 10_gram; if( u.is_on_ground() ) { str_mod /= 4; - } else if( u.is_crouching() ) { + } else if( u.is_crouching() && !u.has_flag( json_flag_QUADRUPED ) ) { str_mod /= 2; } @@ -2739,7 +2740,7 @@ int Character::attack_speed( const item &weap ) const if( is_on_ground() ) { move_cost *= 4.0; - } else if( is_crouching() ) { + } else if( is_crouching() && !has_flag( json_flag_QUADRUPED ) ) { move_cost *= 1.5; } From 6976aa291331d42c2c301f5d4f2e9c8b8204f0e5 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 12 Jan 2024 22:16:31 -0800 Subject: [PATCH 008/202] Update melee.cpp --- src/melee.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/melee.cpp b/src/melee.cpp index 472df41451629..f7b2ac85eb649 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -375,9 +375,10 @@ float Character::hit_roll() const } // Difficult to land a hit while prone + // Quadrupeds don't mind as long as they're unarmed if( is_on_ground() ) { hit -= 8.0f; - } else if( is_crouching() && !has_flag( json_flag_QUADRUPED ) ) { + } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) || attack_vector != "WEAPON" ) ) ) { hit -= 2.0f; } @@ -776,7 +777,7 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, // being prone affects how much leverage you can use to deal damage if( is_on_ground() ) { d.mult_damage( 0.3 ); - } else if( is_crouching() && !has_flag( json_flag_QUADRUPED ) ) { + } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) || attack_vector != "WEAPON" ) ) ) { d.mult_damage( 0.8 ); } @@ -947,7 +948,7 @@ int Character::get_total_melee_stamina_cost( const item *weap ) const { const int mod_sta = get_standard_stamina_cost( weap ); const int melee = round( get_skill_level( skill_melee ) ); - const int stance_malus = is_on_ground() ? 50 : ( !has_flag( json_flag_QUADRUPED ) && is_crouching() ? 20 : 0 ); + const int stance_malus = is_on_ground() ? 50 : ( ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) || attack_vector != "WEAPON" ) ) && is_crouching() ? 20 : 0 ); return std::min( -50, mod_sta + melee - stance_malus ); } @@ -1041,7 +1042,7 @@ int stumble( Character &u, const item_location &weap ) units::mass str_mod = u.get_arm_str() * 10_gram; if( u.is_on_ground() ) { str_mod /= 4; - } else if( u.is_crouching() && !u.has_flag( json_flag_QUADRUPED ) ) { + } else if( u.is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) || attack_vector != "WEAPON" ) ) ) { str_mod /= 2; } @@ -2740,7 +2741,7 @@ int Character::attack_speed( const item &weap ) const if( is_on_ground() ) { move_cost *= 4.0; - } else if( is_crouching() && !has_flag( json_flag_QUADRUPED ) ) { + } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) || attack_vector != "WEAPON" ) ) ) { move_cost *= 1.5; } From 99bc98f31415aa79476686e01360f58070f9a2e8 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 13 Jan 2024 01:16:15 -0800 Subject: [PATCH 009/202] syntax, flag --- data/json/flags.json | 7 ++++++- data/json/items/armor/integrated.json | 4 ++-- src/flag.cpp | 1 + src/flag.h | 1 + src/melee.cpp | 15 ++++++++------- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/data/json/flags.json b/data/json/flags.json index 7915d26386419..8e8ad9abe48a7 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2391,6 +2391,11 @@ { "id": "QUADRUPED", "type": "json_flag", - "info": "This character receives no penalty for crouching in melee." + "//": "This character receives no penalty for crouching in melee." + }, + { + "id": "NATURAL_WEAPON", + "type": "json_flag", + "//": "This item is a 'natural' weapon, such as a mutant's fangs or claws. Should not be applied to bionic weapons." } ] diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index b6013418eab5c..0f8f30671c9c5 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1600,10 +1600,10 @@ "to_hit": 1, "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], "techniques": [ "VAMPIRE_BITE" ], - "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "OUTER", "PADDED", "PROVIDES_TECHNIQUES" ], + "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], "armor": [ { - "material": [ { "type": "bone", "covered_by_mat": 0, "thickness": 3.2 } ], + "material": [ { "type": "bone", "covered_by_mat": 100, "thickness": 3.2 } ], "covers": [ "mouth" ], "coverage": 0, "encumbrance": 0 diff --git a/src/flag.cpp b/src/flag.cpp index cf887e7190956..8c95524cfc6ec 100644 --- a/src/flag.cpp +++ b/src/flag.cpp @@ -195,6 +195,7 @@ const flag_id flag_MYCUS_OK( "MYCUS_OK" ); const flag_id flag_NANOFAB_REPAIR( "NANOFAB_REPAIR" ); const flag_id flag_NANOFAB_TEMPLATE( "NANOFAB_TEMPLATE" ); const flag_id flag_NANOFAB_TEMPLATE_SINGLE_USE( "NANOFAB_TEMPLATE_SINGLE_USE" ); +const flag_id flag_NATURAL_WEAPON( "NATURAL_WEAPON" ); const flag_id flag_NEEDS_NO_LUBE( "NEEDS_NO_LUBE" ); const flag_id flag_NEEDS_UNFOLD( "NEEDS_UNFOLD" ); const flag_id flag_NEGATIVE_MONOTONY_OK( "NEGATIVE_MONOTONY_OK" ); diff --git a/src/flag.h b/src/flag.h index 5dedc6dc1b04f..c362a9d7236da 100644 --- a/src/flag.h +++ b/src/flag.h @@ -204,6 +204,7 @@ extern const flag_id flag_MYCUS_OK; extern const flag_id flag_NANOFAB_REPAIR; extern const flag_id flag_NANOFAB_TEMPLATE; extern const flag_id flag_NANOFAB_TEMPLATE_SINGLE_USE; +extern const flag_id flag_NATURAL_WEAPON; extern const flag_id flag_NEEDS_NO_LUBE; extern const flag_id flag_NEEDS_UNFOLD; extern const flag_id flag_NEGATIVE_MONOTONY_OK; diff --git a/src/melee.cpp b/src/melee.cpp index f7b2ac85eb649..1126dae82605b 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -376,9 +376,11 @@ float Character::hit_roll() const // Difficult to land a hit while prone // Quadrupeds don't mind as long as they're unarmed + item_location cur_weapon = used_weapon(); + item cur_weap = cur_weapon ? *cur_weapon : null_item_reference(); if( is_on_ground() ) { hit -= 8.0f; - } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) || attack_vector != "WEAPON" ) ) ) { + } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) { hit -= 2.0f; } @@ -777,7 +779,7 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, // being prone affects how much leverage you can use to deal damage if( is_on_ground() ) { d.mult_damage( 0.3 ); - } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) || attack_vector != "WEAPON" ) ) ) { + } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) { d.mult_damage( 0.8 ); } @@ -948,7 +950,7 @@ int Character::get_total_melee_stamina_cost( const item *weap ) const { const int mod_sta = get_standard_stamina_cost( weap ); const int melee = round( get_skill_level( skill_melee ) ); - const int stance_malus = is_on_ground() ? 50 : ( ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) || attack_vector != "WEAPON" ) ) && is_crouching() ? 20 : 0 ); + const int stance_malus = is_on_ground() ? 50 : ( ( !has_flag( json_flag_QUADRUPED ) && ( !weap->has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) && is_crouching() ? 20 : 0 ); return std::min( -50, mod_sta + melee - stance_malus ); } @@ -1038,11 +1040,11 @@ int stumble( Character &u, const item_location &weap ) if( !weap || u.has_trait( trait_DEFT ) ) { return 0; } - + item cur_weap = weap ? *weap : null_item_reference(); units::mass str_mod = u.get_arm_str() * 10_gram; if( u.is_on_ground() ) { str_mod /= 4; - } else if( u.is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) || attack_vector != "WEAPON" ) ) ) { + } else if( u.is_crouching() && ( !u.has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !u.unarmed_attack() ) ) ) { str_mod /= 2; } @@ -2738,10 +2740,9 @@ int Character::attack_speed( const item &weap ) const move_cost += ma_move_cost; move_cost *= mutation_value( "attackcost_modifier" ); - if( is_on_ground() ) { move_cost *= 4.0; - } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) || attack_vector != "WEAPON" ) ) ) { + } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) { move_cost *= 1.5; } From a808736353350a22c1f5364fbe24a8a5b9a1b82d Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 14 Jan 2024 08:20:37 -0800 Subject: [PATCH 010/202] stuff --- data/json/flags.json | 5 +++++ data/json/mutations/mutations.json | 31 +++++++++++++++++++++++------- src/flag.cpp | 1 + src/flag.h | 1 + src/melee.cpp | 13 +++++++------ 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/data/json/flags.json b/data/json/flags.json index 8e8ad9abe48a7..a78ccd95dcd94 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2397,5 +2397,10 @@ "id": "NATURAL_WEAPON", "type": "json_flag", "//": "This item is a 'natural' weapon, such as a mutant's fangs or claws. Should not be applied to bionic weapons." + }, + { + "id": "PSEUDOPOD_GRASP", + "type": "json_flag", + "//": "Removes some penalties for melee attacks made while prone or crouching." } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 7c973d940247d..70d5ecd6c0abb 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -4148,6 +4148,7 @@ "//": "Didn't integrate-ize tentacle rakes because they are hardcoded, waiting for limb rework.", "description": "Your upper tentacles have grown large, hook-barbed rakes on the ends. They're quite vicious, but really aren't suited for fine manipulation.", "encumbrance_always": [ [ "hand_l", 20 ], [ "hand_r", 20 ] ], + "flags": [ "PSEUDOPOD_GRASP" ], "prereqs": [ "ARM_TENTACLES", "ARM_TENTACLES_4", "ARM_TENTACLES_8" ], "threshreq": [ "THRESH_CEPHALOPOD" ], "category": [ "CEPHALOPOD" ] @@ -4473,13 +4474,14 @@ "visibility": 2, "ugliness": 2, "mixed_effect": true, - "description": "Your pseudopod-like limbs can fit into anything! +4 Dexterity, -4 Strength.", + "description": "Your pseudopod-like limbs can fit into anything! You also suffer reduced penalties to attacking while prone. +4 Dexterity, -4 Strength.", "purifiable": false, "prereqs": [ "BENDY2" ], "prereqs2": [ "AMORPHOUS" ], "cancels": [ "HUMAN_ARMS", "HUMAN_LEGS" ], "threshreq": [ "THRESH_SLIME" ], "category": [ "SLIME" ], + "flags": [ "PSEUDOPOD_GRASP" ], "passive_mods": { "dex_mod": 4, "str_mod": -4 }, "hp_adjustment": 6 }, @@ -4576,17 +4578,16 @@ }, { "type": "mutation", - "id": "BAT_FEET", + "id": "BAT_LEGS", "name": { "str": "Bat Legs" }, "points": -1, "visibility": 2, "ugliness": 2, "description": "Your lower limbs have shortened and grown bow-legged, slowing your bipedal movement. But as long as you're not holding anything in your fingers, you're able to crawl quickly and quietly.", - "types": [ "FEET" ], + "types": [ "LEGS" ], "prereqs": [ "PADDED_FEET", "WINGS_BAT" ], "category": [ "CHIROPTERAN" ], "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], - "restricts_gear": [ "foot_l", "foot_r" ], "destroys_gear": true, "triggers": [ [ @@ -4638,6 +4639,21 @@ } ] }, + { + "type": "mutation", + "id": "HOOKED_TOES", + "name": { "str": "Hooked Toes" }, + "points": 2, + "visibility": 2, + "ugliness": 2, + "description": "Walking on two legs is pretty awkward, but you can climb just about anything thanks to your powerful toe claws. You can even cling to walls you glide into!", + "types": [ "FEET" ], + "prereqs": [ "BAT_LEGS" ], + "category": [ "CHIROPTERAN" ], + "flags": [ "WALL_CLING" ], + "restricts_gear": [ "foot_l", "foot_r" ], + "destroys_gear": true + }, { "type": "mutation", "id": "ANIMAL_FEET", @@ -8287,12 +8303,13 @@ "visibility": 7, "ugliness": 4, "mixed_effect": true, - "description": "Your arms have transformed into tentacles, resulting in a +1 bonus to Dexterity, permanent hand encumbrance of 30, and an inability to wear gloves. Somewhat decreases wet penalties.", + "description": "Your arms have transformed into tentacles, resulting in a +1 bonus to Dexterity, permanent hand encumbrance of 30, and an inability to wear gloves. Reduces penalties for attacking while prone and somewhat decreases wet penalties.", "encumbrance_always": [ [ "hand_l", 30 ], [ "hand_r", 30 ] ], "restricts_gear": [ "hand_l", "hand_r", "arm_stub_r", "arm_stub_l" ], "types": [ "HANDS", "CLAWS" ], "leads_to": [ "CLAWS_TENTACLE" ], "changes_to": [ "ARM_TENTACLES_4" ], + "flags": [ "PSEUDOPOD_GRASP" ], "category": [ "CEPHALOPOD" ], "wet_protection": [ { "part": "arm_l", "neutral": 19 }, @@ -8325,7 +8342,7 @@ "points": 0, "visibility": 8, "ugliness": 5, - "description": "Your arms have transformed into four tentacles, resulting in a +1 bonus to Dexterity, permanent hand encumbrance of 30, and an inability to wear gloves. You can make up to 3 extra attacks with them. Somewhat decreases wet penalties.", + "description": "Your arms have transformed into four tentacles, resulting in a +1 bonus to Dexterity, permanent hand encumbrance of 30, and an inability to wear gloves. You can make up to 3 extra attacks with them, and you suffer reduced penalties to attacking while prone. Somewhat decreases wet penalties.", "encumbrance_always": [ [ "hand_l", 30 ], [ "hand_r", 30 ] ], "restricts_gear": [ "hand_l", "hand_r" ], "types": [ "HANDS", "CLAWS" ], @@ -8364,7 +8381,7 @@ "points": 0, "visibility": 9, "ugliness": 6, - "description": "Your arms have transformed into eight tentacles, resulting in a +1 bonus to Dexterity, permanent hand encumbrance of 30, and an inability to wear gloves. You can make up to 7 extra attacks with them. Somewhat decreases wet penalties.", + "description": "Your arms have transformed into eight tentacles, resulting in a +1 bonus to Dexterity, permanent hand encumbrance of 30, and an inability to wear gloves. You can make up to 7 extra attacks with them, and suffer reduced penalties to attacking while prone. Somewhat decreases wet penalties.", "encumbrance_always": [ [ "hand_l", 30 ], [ "hand_r", 30 ] ], "restricts_gear": [ "hand_l", "hand_r" ], "types": [ "HANDS", "CLAWS" ], diff --git a/src/flag.cpp b/src/flag.cpp index 8c95524cfc6ec..430e3bfb034be 100644 --- a/src/flag.cpp +++ b/src/flag.cpp @@ -248,6 +248,7 @@ const flag_id flag_PRIMITIVE_RANGED_WEAPON( "PRIMITIVE_RANGED_WEAPON" ); const flag_id flag_PROCESSING( "PROCESSING" ); const flag_id flag_PROCESSING_RESULT( "PROCESSING_RESULT" ); const flag_id flag_PSEUDO( "PSEUDO" ); +const flag_id flag_PSEUDOPOD_GRASP( "PSEUDOPOD_GRASP" ); const flag_id flag_PSYSHIELD_PARTIAL( "PSYSHIELD_PARTIAL" ); const flag_id flag_PULPED( "PULPED" ); const flag_id flag_PUMP_ACTION( "PUMP_ACTION" ); diff --git a/src/flag.h b/src/flag.h index c362a9d7236da..30ed2d9970fea 100644 --- a/src/flag.h +++ b/src/flag.h @@ -255,6 +255,7 @@ extern const flag_id flag_PRIMITIVE_RANGED_WEAPON; extern const flag_id flag_PROCESSING; extern const flag_id flag_PROCESSING_RESULT; extern const flag_id flag_PSEUDO; +extern const flag_id flag_PSEUDOPOD_GRASP; extern const flag_id flag_PSYSHIELD_PARTIAL; extern const flag_id flag_PULPED; extern const flag_id flag_PUMP_ACTION; diff --git a/src/melee.cpp b/src/melee.cpp index 1126dae82605b..2f8a194e3eaa1 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -120,6 +120,7 @@ static const json_character_flag json_flag_HARDTOHIT( "HARDTOHIT" ); static const json_character_flag json_flag_HYPEROPIC( "HYPEROPIC" ); static const json_character_flag json_flag_NEED_ACTIVE_TO_MELEE( "NEED_ACTIVE_TO_MELEE" ); static const json_character_flag json_flag_NULL( "NULL" ); +static const json_character_flag json_flag_PSEUDOPOD_GRASP( "PSEUDOPOD_GRASP" ); static const json_character_flag json_flag_UNARMED_BONUS( "UNARMED_BONUS" ); static const json_character_flag json_flag_QUADRUPED( "QUADRUPED" ); @@ -378,9 +379,9 @@ float Character::hit_roll() const // Quadrupeds don't mind as long as they're unarmed item_location cur_weapon = used_weapon(); item cur_weap = cur_weapon ? *cur_weapon : null_item_reference(); - if( is_on_ground() ) { + if( is_on_ground() && !has_flag( json_flag_PSEUDOPOD_GRASP ) ) { hit -= 8.0f; - } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) { + } else if( is_crouching() && ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) ) { hit -= 2.0f; } @@ -777,7 +778,7 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, d.mult_damage( 0.7 ); } // being prone affects how much leverage you can use to deal damage - if( is_on_ground() ) { + if( is_on_ground() && !has_flag( json_flag_PSEUDOPOD_GRASP ) ) { d.mult_damage( 0.3 ); } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) { d.mult_damage( 0.8 ); @@ -950,7 +951,7 @@ int Character::get_total_melee_stamina_cost( const item *weap ) const { const int mod_sta = get_standard_stamina_cost( weap ); const int melee = round( get_skill_level( skill_melee ) ); - const int stance_malus = is_on_ground() ? 50 : ( ( !has_flag( json_flag_QUADRUPED ) && ( !weap->has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) && is_crouching() ? 20 : 0 ); + const int stance_malus = ( is_on_ground() && !has_flag( json_flag_PSEUDOPOD_GRASP ) ) ? 50 : ( (!has_flag( json_flag_PSEUDOPOD_GRASP ) || ( !has_flag( json_flag_QUADRUPED ) && ( !weap->has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) && is_crouching() ? 20 : 0 ); return std::min( -50, mod_sta + melee - stance_malus ); } @@ -2740,9 +2741,9 @@ int Character::attack_speed( const item &weap ) const move_cost += ma_move_cost; move_cost *= mutation_value( "attackcost_modifier" ); - if( is_on_ground() ) { + if( is_on_ground() && !has_flag( json_flag_PSEUDOPOD_GRASP ) ) { move_cost *= 4.0; - } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) { + } else if( is_crouching() && ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || ( !has_flag( json_flag_QUADRUPED ) && ( !weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) ) { move_cost *= 1.5; } From 82784b26e328f53f6de3a2d73f42c306da82042a Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:15:50 -0800 Subject: [PATCH 011/202] climbin --- data/json/climbing.json | 2 +- data/json/items/armor/integrated.json | 2 +- data/json/mutations/mutations.json | 66 +++++++++++---------------- 3 files changed, 28 insertions(+), 42 deletions(-) diff --git a/data/json/climbing.json b/data/json/climbing.json index 7ba1108ac7682..1afdf360ada41 100644 --- a/data/json/climbing.json +++ b/data/json/climbing.json @@ -195,7 +195,7 @@ "//": "Allows players to safely climb up/down or hold position against a wall.", "copy-from": "generic_superpower", "down": { - "menu_text": "Crawl down the wall with your sticky pads.", + "menu_text": "Crawl down the wall.", "confirm_text": "Crawl down the wall?", "msg_after": "You crawl down the wall.", "//": "Can be used repeatedly to descend 1 floor at a time.", diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index 0f8f30671c9c5..519582c84bdd4 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1609,6 +1609,6 @@ "encumbrance": 0 } ], - "melee_damage": { "pierce": 20 } + "melee_damage": { "stab": 20 } } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 70d5ecd6c0abb..9db97240d3267 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -2282,7 +2282,8 @@ "RAT", "CHIMERA", "SPIDER", - "CRUSTACEAN" + "CRUSTACEAN", + "CHIROPTERAN" ], "cancels": [ "VANITY" ] }, @@ -2716,7 +2717,7 @@ "description": "Your teeth have grown into two-inch-long fangs, allowing you to make an extra attack when conditions favor it.", "types": [ "TEETH" ], "changes_to": [ "SABER_TEETH", "SHARKTEETH" ], - "category": [ "LIZARD", "FISH", "LUPINE", "FELINE", "CHIMERA" ], + "category": [ "LIZARD", "FISH", "LUPINE", "FELINE", "CHIMERA", "CHIROPTERAN" ], "attacks": [ { "attack_text_u": "You sink your fangs into %s!", @@ -4587,55 +4588,26 @@ "types": [ "LEGS" ], "prereqs": [ "PADDED_FEET", "WINGS_BAT" ], "category": [ "CHIROPTERAN" ], + "movecost_modifier": 1.2, "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], "destroys_gear": true, "triggers": [ [ { - "condition": { - "and": [ - { "u_has_move_mode": "run" }, - { "not": "u_can_drop_weapon" }, - { "u_has_trait": "WINGS_BAT" } - ] - }, - "msg_on": { "text": "You hunch down, using your wings to crawl." }, - "msg_off": { "text": "You rise up to walk on your hind feet." } + "condition": { "and": [ { "or": [ { "u_has_move_mode": "run" }, { "u_has_move_mode": "crouch" } ] }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, + "msg_on": { "text": "You hunch forward and crawl quickly on folded wings." } } ] ], "enchantments": [ { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { "u_has_trait": "WINGS_BAT" } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.25 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ] - }, - { - "condition": { - "and": [ - { "u_has_move_mode": "run" }, - { "not": "u_can_drop_weapon" }, - { "u_has_trait": "WINGS_BAT" } - ] - }, + "condition": { "and": [ { "u_has_move_mode": "crouch" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ] + }, { - "condition": { - "or": [ - "u_can_drop_weapon", - { "u_has_move_mode": "walk" }, - { - "and": [ { "not": { "u_has_trait": "WINGS_BAT" } } ] - } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.1 } ] + "condition": { "and": [ { "u_has_move_mode": "run" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, + "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.5 } ] } ] }, @@ -4646,7 +4618,7 @@ "points": 2, "visibility": 2, "ugliness": 2, - "description": "Walking on two legs is pretty awkward, but you can climb just about anything thanks to your powerful toe claws. You can even cling to walls you glide into!", + "description": "Your feet have grown into strong, curved claws. They won't fit into normal shoes and are not much help in combat, but they're perfect for climbing. You can even cling to walls you glide into!", "types": [ "FEET" ], "prereqs": [ "BAT_LEGS" ], "category": [ "CHIROPTERAN" ], @@ -6303,7 +6275,21 @@ "types": [ "DEX" ], "prereqs": [ "DEX_UP" ], "changes_to": [ "DEX_UP_3", "DEX_ALPHA" ], - "category": [ "LIZARD", "SPIDER", "CHIMERA", "RAPTOR", "MOUSE", "RABBIT", "BIRD", "ELFA", "FELINE", "CEPHALOPOD", "FISH", "ALPHA", "CHIROPTERAN" ], + "category": [ + "LIZARD", + "SPIDER", + "CHIMERA", + "RAPTOR", + "MOUSE", + "RABBIT", + "BIRD", + "ELFA", + "FELINE", + "CEPHALOPOD", + "FISH", + "ALPHA", + "CHIROPTERAN" + ], "passive_mods": { "dex_mod": 2 } }, { From 9c7981ecbc171c426efd902b7cb967194198272c Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:21:15 -0800 Subject: [PATCH 012/202] Update mutations.json --- data/json/mutations/mutations.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 9db97240d3267..c4d24c480b473 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -4588,7 +4588,7 @@ "types": [ "LEGS" ], "prereqs": [ "PADDED_FEET", "WINGS_BAT" ], "category": [ "CHIROPTERAN" ], - "movecost_modifier": 1.2, + "movecost_modifier": 1.32, "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], "destroys_gear": true, "triggers": [ @@ -4602,12 +4602,12 @@ "enchantments": [ { "condition": { "and": [ { "u_has_move_mode": "crouch" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, - "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ] + "values": [ { "value": "MOVE_COST", "multiply": -0.18 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ] }, { "condition": { "and": [ { "u_has_move_mode": "run" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, - "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.5 } ] + "values": [ { "value": "MOVE_COST", "multiply": -0.18 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.5 } ] } ] }, From 3e3dd8758c71e6b753d425ea183bca794a83d3fb Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Tue, 16 Jan 2024 12:31:18 -0800 Subject: [PATCH 013/202] bloooood --- data/json/flags.json | 15 +++++++++++++ data/json/mutations/mutations.json | 34 ++++++++++++++++++++++++++++-- src/consumption.cpp | 27 ++++++++++++++++++++++++ src/flag.cpp | 1 + src/flag.h | 1 + src/item_factory.cpp | 4 +++- 6 files changed, 79 insertions(+), 3 deletions(-) diff --git a/data/json/flags.json b/data/json/flags.json index a78ccd95dcd94..31546543e2376 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2402,5 +2402,20 @@ "id": "PSEUDOPOD_GRASP", "type": "json_flag", "//": "Removes some penalties for melee attacks made while prone or crouching." + }, + { + "id": "HEMOVORE", + "type": "json_flag", + "//": "You eat blood to survive." + }, + { + "id": "BLOODFEEDER", + "type": "json_flag", + "//": "You really like blood, and get less enjoyment out of other foods." + }, + { + "id": "HEMOVORE_FUN", + "type": "json_flag", + "//": "This item contains blood and satisfies a hemovore's requirements." } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index c4d24c480b473..4e86a40517e62 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -4594,7 +4594,13 @@ "triggers": [ [ { - "condition": { "and": [ { "or": [ { "u_has_move_mode": "run" }, { "u_has_move_mode": "crouch" } ] }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, + "condition": { + "and": [ + { "or": [ { "u_has_move_mode": "run" }, { "u_has_move_mode": "crouch" } ] }, + { "not": "u_can_drop_weapon" }, + { "u_has_trait": "WINGS_BAT" } + ] + }, "msg_on": { "text": "You hunch forward and crawl quickly on folded wings." } } ] @@ -4603,7 +4609,6 @@ { "condition": { "and": [ { "u_has_move_mode": "crouch" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.18 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ] - }, { "condition": { "and": [ { "u_has_move_mode": "run" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, @@ -7834,6 +7839,31 @@ "category": [ "LIZARD", "SPIDER", "CHIMERA", "RAPTOR", "FELINE", "BATRACHIAN", "BEAST", "LUPINE" ], "vitamin_rates": [ [ "vitC", -1200 ] ] }, + { + "type": "mutation", + "id": "HEMOVORE", + "name": { "str": "Hemovore" }, + "points": -4, + "description": "You can still eat other food, but you crave the taste of blood. If you choose not to drink blood or can't find any, you'd better hope you can find some iron supplements.", + "types": [ "DIET" ], + "flags": [ "HEMOVORE" ], + "cancels": [ "VEGAN" ], + "category": [ "CHIROPTERAN" ], + "changes_to": [ "BLOODFEEDER" ], + "vitamin_rates": [ [ "iron", 800 ] ] + }, + { + "type": "mutation", + "id": "BLOODFEEDER", + "name": { "str": "Bloodfeeder" }, + "points": -4, + "description": "Your craving for blood borders on obsession. You don't care if it's human or animal, all you know is that you've got to have it, and other foods just don't taste as good.", + "types": [ "DIET" ], + "flags": [ "HEMOVORE" ], + "cancels": [ "VEGAN", "HEMOVORE" ], + "category": [ "CHIROPTERAN" ], + "vitamin_rates": [ [ "iron", 1200 ] ] + }, { "type": "mutation", "id": "PONDEROUS1", diff --git a/src/consumption.cpp b/src/consumption.cpp index b8b22ab88f19c..0806b7f651529 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -100,6 +100,8 @@ static const itype_id itype_dab_pen_on( "dab_pen_on" ); static const itype_id itype_syringe( "syringe" ); static const json_character_flag json_flag_CANNIBAL( "CANNIBAL" ); +static const json_character_flag json_flag_BLOODFEEDER( "BLOODFEEDER" ); +static const json_character_flag json_flag_HEMOVORE( "HEMOVORE" ); static const json_character_flag json_flag_IMMUNE_SPOIL( "IMMUNE_SPOIL" ); static const json_character_flag json_flag_NUMB( "NUMB" ); static const json_character_flag json_flag_PARAIMMUNE( "PARAIMMUNE" ); @@ -509,6 +511,27 @@ std::pair Character::fun_for( const item &comest, bool ignore_already_ } } + // Cooked blood is OK, but it's really better raw. + if( comest.has_flag( flag_HEMOVORE_FUN ) ) { + if( has_flag( json_flag_BLOODFEEDER ) ) { + if( fun <= 0 && comest.made_of( phase_id::LIQUID ) ) { + fun += 25; + } else { + fun *= 1.2; + } + } else if( has_flag( json_flag_HEMOVORE ) ) { + if( fun <= 0 && comest.made_of( phase_id::LIQUID ) ) { + fun += 13; + } else { + fun *= 1.1; + } + } + } + + if( has_flag( json_flag_BLOODFEEDER ) && !comest.has_flag( flag_HEMOVORE_FUN ) && fun > 0 ) { + fun *= 0.5; + } + if( has_trait( trait_GOURMAND ) ) { if( fun < -1 ) { fun_max = fun; @@ -1295,11 +1318,13 @@ void Character::modify_morale( item &food, const int nutr ) // Sapiovores don't recognize humans as the same species. // But let them possibly feel cool about eating sapient stuff - treat like psycho // However, spiritual sapiovores should still recognize humans as having a soul or special for religious reasons + // Hemovores feel weird about human blood, bloodfeeders don't care unless they're also cannibals or w/e const bool cannibal = has_flag( json_flag_CANNIBAL ); const bool psycho = has_flag( json_flag_PSYCHOPATH ); const bool sapiovore = has_flag( json_flag_SAPIOVORE ); const bool spiritual = has_flag( json_flag_SPIRITUAL ); const bool numb = has_flag( json_flag_NUMB ); + const bool bloodfeeder = has_flag( json_flag_BLOODFEEDER ); if( cannibal && psycho && spiritual ) { add_msg_if_player( m_good, _( "You feast upon the human flesh, and in doing so, devour their spirit." ) ); @@ -1333,6 +1358,8 @@ void Character::modify_morale( item &food, const int nutr ) } else if( numb ) { add_msg_if_player( m_bad, _( "You find this meal distasteful, but necessary." ) ); add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); + } else if( bloodfeeder ) && food.has_flag( flag_HEMOVORE_FUN ) { + add_msg_if_player( _( "The human blood is as sweet as any other." ) ); } else { add_msg_if_player( m_bad, _( "You feel horrible for eating a person." ) ); add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); diff --git a/src/flag.cpp b/src/flag.cpp index 430e3bfb034be..aaed38a24a0b4 100644 --- a/src/flag.cpp +++ b/src/flag.cpp @@ -151,6 +151,7 @@ const flag_id flag_GIBBED( "GIBBED" ); const flag_id flag_GNV_EFFECT( "GNV_EFFECT" ); const flag_id flag_HARD( "HARD" ); const flag_id flag_HEAT_IMMUNE( "HEAT_IMMUNE" ); +const flag_id flag_HEMOVORE_FUN( "HEMOVORE_FUN" ); const flag_id flag_HIDDEN_HALLU( "HIDDEN_HALLU" ); const flag_id flag_HIDDEN_POISON( "HIDDEN_POISON" ); const flag_id flag_HOOD( "HOOD" ); diff --git a/src/flag.h b/src/flag.h index 30ed2d9970fea..3feeaa8466dfb 100644 --- a/src/flag.h +++ b/src/flag.h @@ -160,6 +160,7 @@ extern const flag_id flag_GIBBED; extern const flag_id flag_GNV_EFFECT; extern const flag_id flag_HARVEST_SEEDS; extern const flag_id flag_HEAT_IMMUNE; +extern const flag_id flag_HEMOVORE_FUN; extern const flag_id flag_HIDDEN_HALLU; extern const flag_id json_flag_HIDDEN_ITEM; extern const flag_id flag_HIDDEN_POISON; diff --git a/src/item_factory.cpp b/src/item_factory.cpp index a657d14f2e126..32c3f7036c5cd 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -3614,7 +3614,9 @@ void Item_factory::set_allergy_flags( itype &item_template ) { material_iflesh, flag_CARNIVORE_OK }, { material_blood, flag_CARNIVORE_OK }, { material_hblood, flag_CARNIVORE_OK }, - { material_honey, flag_URSINE_HONEY } + { material_honey, flag_URSINE_HONEY }, + { material_blood, flag_HEMOVORE_FUN }, + { material_hblood, flag_HEMOVORE_FUN } } }; From b3ca3532cb28fd53a85a96628a2e1936452d0bb3 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:03:25 -0800 Subject: [PATCH 014/202] more blood --- data/json/mutations/mutations.json | 8 ++++---- src/consumption.cpp | 6 +++--- src/item_factory.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 4e86a40517e62..11ab226616a7a 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -7850,19 +7850,19 @@ "cancels": [ "VEGAN" ], "category": [ "CHIROPTERAN" ], "changes_to": [ "BLOODFEEDER" ], - "vitamin_rates": [ [ "iron", 800 ] ] + "vitamin_rates": [ [ "iron", 1800 ] ] }, { "type": "mutation", "id": "BLOODFEEDER", "name": { "str": "Bloodfeeder" }, - "points": -4, + "points": -5, "description": "Your craving for blood borders on obsession. You don't care if it's human or animal, all you know is that you've got to have it, and other foods just don't taste as good.", "types": [ "DIET" ], - "flags": [ "HEMOVORE" ], + "flags": [ "BLOODFEEDER" ], "cancels": [ "VEGAN", "HEMOVORE" ], "category": [ "CHIROPTERAN" ], - "vitamin_rates": [ [ "iron", 1200 ] ] + "vitamin_rates": [ [ "iron", 2400 ] ] }, { "type": "mutation", diff --git a/src/consumption.cpp b/src/consumption.cpp index 0806b7f651529..ad3f333e5e872 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -514,13 +514,13 @@ std::pair Character::fun_for( const item &comest, bool ignore_already_ // Cooked blood is OK, but it's really better raw. if( comest.has_flag( flag_HEMOVORE_FUN ) ) { if( has_flag( json_flag_BLOODFEEDER ) ) { - if( fun <= 0 && comest.made_of( phase_id::LIQUID ) ) { + if( fun <= 0) { fun += 25; } else { fun *= 1.2; } } else if( has_flag( json_flag_HEMOVORE ) ) { - if( fun <= 0 && comest.made_of( phase_id::LIQUID ) ) { + if( fun <= 0 ) { fun += 13; } else { fun *= 1.1; @@ -1358,7 +1358,7 @@ void Character::modify_morale( item &food, const int nutr ) } else if( numb ) { add_msg_if_player( m_bad, _( "You find this meal distasteful, but necessary." ) ); add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); - } else if( bloodfeeder ) && food.has_flag( flag_HEMOVORE_FUN ) { + } else if( bloodfeeder && food.has_flag( flag_HEMOVORE_FUN ) ) { add_msg_if_player( _( "The human blood is as sweet as any other." ) ); } else { add_msg_if_player( m_bad, _( "You feel horrible for eating a person." ) ); diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 32c3f7036c5cd..10c15253ddfcc 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -3581,7 +3581,7 @@ void Item_factory::load_generic( const JsonObject &jo, const std::string &src ) // Set for all items (not just food and clothing) to avoid edge cases void Item_factory::set_allergy_flags( itype &item_template ) { - static const std::array, 29> all_pairs = { { + static const std::array, 31> all_pairs = { { // First allergens: // An item is an allergen even if it has trace amounts of allergenic material { material_hflesh, flag_CANNIBALISM }, From ad7f964470f7736628f47cf3f56a04e30447b129 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:14:24 -0800 Subject: [PATCH 015/202] Update dictionary.txt --- tools/spell_checker/dictionary.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/spell_checker/dictionary.txt b/tools/spell_checker/dictionary.txt index e9a55b7277cca..36363d8a58763 100644 --- a/tools/spell_checker/dictionary.txt +++ b/tools/spell_checker/dictionary.txt @@ -3066,6 +3066,7 @@ themm therizinosaurus thermochemistry thermoplastics +thermoreceptors theropod thescelosaurus thicknesses From f2427e814b75707d4295969159690ebd340270e3 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 17 Jan 2024 02:49:48 -0800 Subject: [PATCH 016/202] Update mutations.json --- data/json/mutations/mutations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 41c80ce9af54c..5589365885ef1 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -6491,7 +6491,7 @@ "ugliness": 4, "description": "Most of your fingers have grown to tremendous length, becoming a broad pair of leathery wings. This allows you to arrest a fall or glide from a ledge if you aren't too weighed down, but naturally impedes your fine motor skills. They even keep you warm when you sleep.", "types": [ "ARMS", "HANDS" ], - "flags": [ "WINGGLIDE", "WINGS_2", "ARM_WINGS" ], + "flags": [ "WING_GLIDE", "WINGS_2", "ARM_WINGS" ], "prereqs": [ "WEBBED" ], "category": [ "CHIROPTERAN" ], "restricts_gear": [ "arm_l", "arm_r", "hand_l", "hand_r" ], From 27f94a8410748c3a20698db771f6b999029dd723 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 17 Jan 2024 04:19:19 -0800 Subject: [PATCH 017/202] social --- .../mutation_eocs/mutation_effect_eocs.json | 42 ++++++++++++++ data/json/morale_types.json | 10 ++++ data/json/mutations/mutation_type.json | 4 ++ data/json/mutations/mutations.json | 55 ++++++++++++++++++- src/morale_types.cpp | 4 ++ src/morale_types.h | 2 + 6 files changed, 114 insertions(+), 3 deletions(-) diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index bfebd92b8b2ca..310b7a9dfce9c 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -695,5 +695,47 @@ { "u_activate_trait": "WINGS_INSECT_active" }, { "u_message": "You don't have the stamina to keep buzzing.", "type": "bad" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_social1_bonus", + "recurrence": [ "30 minutes", "1 hours" ], + "condition": { "and": [ { "u_has_trait": "SOCIAL1" }, { "player_see_NPC": true }, { "NPC_friend": true }, { "not": { "u_has_effect": "social1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_add_effect": "social_satisfied", "duration": "3 hours" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_social2_bonus", + "recurrence": [ "30 minutes", "1 hours" ], + "condition": { "and": [ { "u_has_trait": "SOCIAL2" }, { "player_see_NPC": true }, { "NPC_friend": true }, { "not": { "u_has_effect": "social2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_add_effect": "social_satisfied", "duration": "3 hours" }, { "u_lose_effect": "social_dissatisfied" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_social2_penalty", + "recurrence": [ "30 minutes", "1 hours" ], + "condition": { "and": [ { "u_has_trait": "SOCIAL2" }, { "player_see_NPC": false }, { "not": { "u_has_effect": "social_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_add_effect": "social_dissatisfied", "duration": "3 hours" }, { "u_lose_effect": "social_satisfied" }, { "u_message": "You could use some company.", "type": "bad" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_asocial1_bonus", + "recurrence": [ "30 minutes", "1 hours" ], + "condition": { "and": [ { "u_has_trait": "ASOCIAL1" }, { "player_see_NPC": false }, { "not": { "u_has_effect": "asocial1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_add_effect": "asocial_satisfied", "duration": "3 hours" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_asocial2_bonus", + "recurrence": [ "30 minutes", "1 hours" ], + "condition": { "and": [ { "u_has_trait": "ASOCIAL2" }, { "player_see_NPC": false }, { "not": { "u_has_effect": "asocial2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_add_effect": "asocial_satisfied", "duration": "3 hours" }, { "u_lose_effect": "asocial_dissatisfied" }, { "u_message": "You'd really rather be by yourself.", "type": "bad" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_asocial2_penalty", + "recurrence": [ "30 minutes", "1 hours" ], + "condition": { "and": [ { "u_has_trait": "ASOCIAL2" }, { "player_see_NPC": true }, { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_add_effect": "asocial_dissatisfied", "duration": "3 hours" }, { "u_lose_effect": "asocial_satisfied" } ] } ] diff --git a/data/json/morale_types.json b/data/json/morale_types.json index 2f5392ee282fd..12b7d4488200f 100644 --- a/data/json/morale_types.json +++ b/data/json/morale_types.json @@ -428,5 +428,15 @@ "id": "morale_afs_drugs", "type": "morale_type", "text": "Chemical enhancement moodswing" + }, + { + "id": "morale_social", + "type": "morale_type", + "text": "Spent time around others" + }, + { + "id": "morale_asocial", + "type": "morale_type", + "text": "Spent time around others" } ] diff --git a/data/json/mutations/mutation_type.json b/data/json/mutations/mutation_type.json index 1902dba5f0f14..0eb6ccfe831a9 100644 --- a/data/json/mutations/mutation_type.json +++ b/data/json/mutations/mutation_type.json @@ -262,5 +262,9 @@ { "type": "mutation_type", "id": "BODY_ARMOR_MOD" + }, + { + "type": "mutation_type", + "id": "SOCIAL" } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 5589365885ef1..178faeda17d42 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -495,8 +495,11 @@ "id": "LEAFNOSE", "name": { "str": "Leaf Nose" }, "points": 2, + "prereqs": [ "SLIT_NOSTRILS" ], "flags": [ "INFRARED" ], + "category": [ "CHIROPTERAN" ], "ugliness": 3, + "active": true, "description": "Your nose is upturned, ridged and flared out into a leaf shape that humans will likely find unsightly. On the plus side, thermoreceptors in your nostrils allow you to smell heat as long as your face is uncovered." }, { @@ -1115,6 +1118,52 @@ "starting_trait": true, "valid": false }, + { + "type": "mutation", + "id": "SOCIAL1", + "name": { "str": "Extrovert" }, + "points": 1, + "types": [ "SOCIAL" ], + "cancels": [ "ASOCIAL1", "ASOCIAL2" ], + "changes_to": [ "SOCIAL2" ], + "description": "You don't mind being alone, but you get a slight mood boost from being around people.", + "starting_trait": true, + "category": [ "CATTLE", "ELFA", "LUPINE", "MOUSE", "RAT", "CHIROPTERAN", "BIRD", "FISH" ] + }, + { + "type": "mutation", + "id": "SOCIAL2", + "name": { "str": "Sociable" }, + "points": 1, + "types": [ "SOCIAL" ], + "cancels": [ "ASOCIAL1", "ASOCIAL2" ], + "description": "You are a social creature. Without occasional human contact, you'll start to feel unhappy.", + "starting_trait": true, + "category": [ "CATTLE", "LUPINE", "MOUSE", "RAT", "CHIROPTERAN" ] + }, + { + "type": "mutation", + "id": "ASOCIAL1", + "name": { "str": "Introvert" }, + "points": 1, + "types": [ "SOCIAL" ], + "cancels": [ "SOCIAL1", "SOCIAL2" ], + "changes_to": [ "ASOCIAL2" ], + "description": "You can tolerate others, but you prefer to be alone. Spending time by yourself will boost your mood slightly.", + "starting_trait": true, + "category": [ "SPIDER", "URSINE", "FELINE", "LIZARD", "BEAST", "CHIMERA" ] + }, + { + "type": "mutation", + "id": "ASOCIAL2", + "name": { "str": "Loner" }, + "points": 1, + "types": [ "SOCIAL" ], + "cancels": [ "SOCIAL1", "SOCIAL2" ], + "description": "You'd really rather be by yourself. Spending time around other people will bring your mood down.", + "starting_trait": true, + "category": [ "SPIDER", "URSINE", "LIZARD", "BEAST" ] + }, { "type": "mutation", "id": "LIGHTSTEP", @@ -1995,7 +2044,7 @@ "starting_trait": true, "social_modifiers": { "intimidate": -2 }, "types": [ "DURABILITY" ], - "category": [ "MOUSE", "ELFA", "RABBIT" ], + "category": [ "MOUSE", "ELFA", "RABBIT", "CHIROPTERAN" ], "changes_to": [ "FLIMSY2" ], "cancels": [ "TOUGH", "TOUGH2", "TOUGH3" ], "hp_modifier": -0.25 @@ -4275,7 +4324,7 @@ "types": [ "LEGS" ], "prereqs": [ "PADDED_FEET", "WINGS_BAT" ], "category": [ "CHIROPTERAN" ], - "movecost_modifier": 1.32, + "movecost_modifier": 1.35, "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], "destroys_gear": true, "triggers": [ @@ -4310,7 +4359,7 @@ "points": 2, "visibility": 2, "ugliness": 2, - "description": "Your feet have grown into strong, curved claws. They won't fit into normal shoes and are not much help in combat, but they're perfect for climbing. You can even cling to walls you glide into!", + "description": "Your feet have grown into strong, curved claws. They won't fit into normal shoes and are not much help in combat, but they allow you to climb most vertical surfaces with ease. You can even cling to walls you glide into!", "types": [ "FEET" ], "prereqs": [ "BAT_LEGS" ], "category": [ "CHIROPTERAN" ], diff --git a/src/morale_types.cpp b/src/morale_types.cpp index a7333c3db1d82..dcc206182e899 100644 --- a/src/morale_types.cpp +++ b/src/morale_types.cpp @@ -92,6 +92,8 @@ const morale_type MORALE_TREE_COMMUNION( "morale_tree_communion" ); const morale_type MORALE_VEGETARIAN( "morale_vegetarian" ); const morale_type MORALE_VOMITED( "morale_vomited" ); const morale_type MORALE_WET( "morale_wet" ); +const morale_type MORALE_SOCIAL( "morale_social" ); +const morale_type MORALE_ASOCIAL( "morale_asocial" ); static const morale_type morale_accomplishment( "morale_accomplishment" ); static const morale_type morale_antifruit( "morale_antifruit" ); static const morale_type morale_antijunk( "morale_antijunk" ); @@ -166,6 +168,8 @@ static const morale_type morale_sweettooth( "morale_sweettooth" ); static const morale_type morale_vegetarian( "morale_vegetarian" ); static const morale_type morale_vomited( "morale_vomited" ); static const morale_type morale_wet( "morale_wet" ); +static const morale_type morale_social( "morale_social" ); +static const morale_type morale_asocial( "morale_asocial" ); const morale_type &morale_type_data::convert_legacy( int lmt ) { diff --git a/src/morale_types.h b/src/morale_types.h index 5953e42bb8249..2b0da768cb91d 100644 --- a/src/morale_types.h +++ b/src/morale_types.h @@ -120,5 +120,7 @@ extern const morale_type MORALE_FUNERAL; extern const morale_type MORALE_TREE_COMMUNION; extern const morale_type MORALE_ACCOMPLISHMENT; extern const morale_type MORALE_FAILURE; +extern const morale_type MORALE_SOCIAL; +extern const morale_type MORALE_ASOCIAL; #endif // CATA_SRC_MORALE_TYPES_H From c8eac1a23ced8cd750147ca21f513c361378b975 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 17 Jan 2024 04:29:43 -0800 Subject: [PATCH 018/202] social stuff --- data/json/effects.json | 44 +++++++++++++ .../effects_on_condition/effects_eocs.json | 64 +++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/data/json/effects.json b/data/json/effects.json index 1571cf27fba65..7e7b47142cd57 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -3709,6 +3709,50 @@ "//": "Mood debuff is handled separately by bad_food_mood_debuff EoC.", "rating": "bad" }, + { + "type": "effect_type", + "id": "social_satisfied", + "name": [ "Good Company" ], + "desc": [ + "It feels good to spend time with others." + ], + "max_intensity": 1, + "rating": "good" + }, + { + "type": "effect_type", + "id": "social_dissatisfied", + "name": [ "Lonely", "Very Lonely", "All Alone" ], + "desc": [ + "You feel a little lonely.", + "You could really use a friend right now.", + "What's the point of surviving if you're all alone?" + ], + "max_intensity": 3, + "rating": "bad" + }, + { + "type": "effect_type", + "id": "asocial_satisfied", + "name": [ "Alone" ], + "desc": [ + "Being on your own makes you feel good." + ], + "max_intensity": 1, + "rating": "good" + }, + { + "type": "effect_type", + "id": "asocial_dissatisfied", + "name": [ "Irritable", "Anxious", "Crowded" ], + "desc": [ + "Being around others has left you feeling drained.", + "OK, that's enough social time. You'd really rather be alone now.", + "Your nerves are frayed from all this socializing. You need some alone time to recharge." + ], + "max_intensity": 3, + "rating": "bad" + }, { "type": "effect_type", "id": "genetics_damaged", diff --git a/data/json/effects_on_condition/effects_eocs.json b/data/json/effects_on_condition/effects_eocs.json index 0da74fdccde14..34a02cb1cf75b 100644 --- a/data/json/effects_on_condition/effects_eocs.json +++ b/data/json/effects_on_condition/effects_eocs.json @@ -15,5 +15,69 @@ } ], "false_effect": [ { "u_lose_morale": "morale_bad_protein_bar" } ] + }, + { + "type": "effect_on_condition", + "id": "eoc_social_satisfied", + "recurrence": [ "1 minutes", "5 minutes" ], + "condition": { "u_has_effect": "social_satisfied" }, + "effect": [ + { + "u_add_morale": "morale_social", + "bonus": 6, + "max_bonus": 6, + "duration": "1 days", + "decay_start": "1 days" + } + ], + "false_effect": [ { "u_lose_morale": "morale_social" } ] + }, + { + "type": "effect_on_condition", + "id": "eoc_social_dissatisfied", + "recurrence": [ "1 minutes", "5 minutes" ], + "condition": { "u_has_effect": "social_dissatisfied" }, + "effect": [ + { + "u_add_morale": "morale_asocial", + "bonus": { "math": [ "u_effect_intensity('social_dissatisfied') * -7" ] }, + "max_bonus": -21, + "duration": "1 days", + "decay_start": "1 days" + } + ], + "false_effect": [ { "u_lose_morale": "morale_social_dissatisfied" } ] + }, + { + "type": "effect_on_condition", + "id": "eoc_asocial_satisfied", + "recurrence": [ "1 minutes", "5 minutes" ], + "condition": { "u_has_effect": "asocial_satisfied" }, + "effect": [ + { + "u_add_morale": "morale_asocial", + "bonus": 10, + "max_bonus": 10, + "duration": "1 days", + "decay_start": "1 days" + } + ], + "false_effect": [ { "u_lose_morale": "morale_asocial" } ] + }, + { + "type": "effect_on_condition", + "id": "eoc_asocial_dissatisfied", + "recurrence": [ "1 minutes", "5 minutes" ], + "condition": { "u_has_effect": "asocial_dissatisfied" }, + "effect": [ + { + "u_add_morale": "morale_social", + "bonus": { "math": [ "u_effect_intensity('asocial_dissatisfied') * -7" ] }, + "max_bonus": -21, + "duration": "1 days", + "decay_start": "1 days" + } + ], + "false_effect": [ { "u_lose_morale": "morale_asocial_dissatisfied" } ] } ] From abdac0655fff0a2faa9d22526a85bda90c256e09 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:59:01 -0800 Subject: [PATCH 019/202] friends --- .vscode/settings.json | 93 +++++++++++++++++++ .../mutation_eocs/mutation_effect_eocs.json | 20 ++-- data/json/morale_types.json | 4 +- data/json/mutations/mutations.json | 6 +- src/activity_actor.cpp | 2 +- src/condition.cpp | 32 ++++++- src/condition.h | 1 + 7 files changed, 141 insertions(+), 17 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 20bd09ea82c04..f2d79e00015e2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -64,4 +64,97 @@ "github-actions.remote-name": "upstream", // NOTE: This disable the plugins formatting so astyle is used. "C_Cpp.formatting": "disabled", + "files.associations": { + "algorithm": "cpp", + "array": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "cfenv": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "cinttypes": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "csetjmp": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cuchar": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "exception": "cpp", + "filesystem": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "functional": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "numeric": "cpp", + "optional": "cpp", + "ostream": "cpp", + "queue": "cpp", + "random": "cpp", + "ratio": "cpp", + "regex": "cpp", + "scoped_allocator": "cpp", + "set": "cpp", + "span": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "utility": "cpp", + "variant": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstddef": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp" + }, } diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index 310b7a9dfce9c..d0c407b346a6f 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -700,42 +700,42 @@ "type": "effect_on_condition", "id": "EOC_social1_bonus", "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_trait": "SOCIAL1" }, { "player_see_NPC": true }, { "NPC_friend": true }, { "not": { "u_has_effect": "social1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_trait": "SOCIAL1" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "social1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_add_effect": "social_satisfied", "duration": "3 hours" } ] }, { "type": "effect_on_condition", "id": "EOC_social2_bonus", "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_trait": "SOCIAL2" }, { "player_see_NPC": true }, { "NPC_friend": true }, { "not": { "u_has_effect": "social2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_trait": "SOCIAL2" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "social2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_add_effect": "social_satisfied", "duration": "3 hours" }, { "u_lose_effect": "social_dissatisfied" } ] }, { "type": "effect_on_condition", "id": "EOC_social2_penalty", - "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_trait": "SOCIAL2" }, { "player_see_NPC": false }, { "not": { "u_has_effect": "social_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_add_effect": "social_dissatisfied", "duration": "3 hours" }, { "u_lose_effect": "social_satisfied" }, { "u_message": "You could use some company.", "type": "bad" } ] + "recurrence": [ "12 hours", "48 hours" ], + "condition": { "and": [ { "u_has_trait": "SOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "social_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_add_effect": "social_dissatisfied", "duration": "3 hours", "intensity": { "math": [ "u_effect_intensity('social_dissatisfied') + 1" ] } }, { "u_lose_effect": "social_satisfied" }, { "u_message": "You could use some friendly company.", "type": "bad" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial1_bonus", "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_trait": "ASOCIAL1" }, { "player_see_NPC": false }, { "not": { "u_has_effect": "asocial1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_trait": "ASOCIAL1" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "asocial1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_add_effect": "asocial_satisfied", "duration": "3 hours" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial2_bonus", "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_trait": "ASOCIAL2" }, { "player_see_NPC": false }, { "not": { "u_has_effect": "asocial2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_trait": "ASOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "asocial2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_add_effect": "asocial_satisfied", "duration": "3 hours" }, { "u_lose_effect": "asocial_dissatisfied" }, { "u_message": "You'd really rather be by yourself.", "type": "bad" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial2_penalty", - "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_trait": "ASOCIAL2" }, { "player_see_NPC": true }, { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_add_effect": "asocial_dissatisfied", "duration": "3 hours" }, { "u_lose_effect": "asocial_satisfied" } ] + "recurrence": [ "12 hours", "48 hours" ], + "condition": { "and": [ { "u_has_trait": "ASOCIAL2" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_add_effect": "asocial_dissatisfied", "duration": "3 hours", "intensity": { "math": [ "u_effect_intensity('asocial_dissatisfied') + 1" ] } }, { "u_lose_effect": "asocial_satisfied" } ] } ] diff --git a/data/json/morale_types.json b/data/json/morale_types.json index 12b7d4488200f..008fc69d92e9b 100644 --- a/data/json/morale_types.json +++ b/data/json/morale_types.json @@ -432,11 +432,11 @@ { "id": "morale_social", "type": "morale_type", - "text": "Spent time around others" + "text": "Spent time around allies" }, { "id": "morale_asocial", "type": "morale_type", - "text": "Spent time around others" + "text": "Spent time alone" } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 178faeda17d42..23f0484e522d2 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -1126,7 +1126,7 @@ "types": [ "SOCIAL" ], "cancels": [ "ASOCIAL1", "ASOCIAL2" ], "changes_to": [ "SOCIAL2" ], - "description": "You don't mind being alone, but you get a slight mood boost from being around people.", + "description": "You don't mind being alone, but you get a slight mood boost from being around friends.", "starting_trait": true, "category": [ "CATTLE", "ELFA", "LUPINE", "MOUSE", "RAT", "CHIROPTERAN", "BIRD", "FISH" ] }, @@ -1137,9 +1137,9 @@ "points": 1, "types": [ "SOCIAL" ], "cancels": [ "ASOCIAL1", "ASOCIAL2" ], - "description": "You are a social creature. Without occasional human contact, you'll start to feel unhappy.", + "description": "You are a social creature. Without occasional friendly human contact, you'll start to feel unhappy.", "starting_trait": true, - "category": [ "CATTLE", "LUPINE", "MOUSE", "RAT", "CHIROPTERAN" ] + "category": [ "CATTLE", "LUPINE", "MOUSE", "RAT", "CHIROPTERAN", "ELFA" ] }, { "type": "mutation", diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 472e34fa111c5..4d0b156e856aa 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -1244,7 +1244,7 @@ void hacksaw_activity_actor::start( player_activity &act, Character &/*who*/ ) return; } - int qual; + int qual = 0; if( type.has_value() ) { item veh_tool = item( type.value(), calendar::turn ); for( const std::pair &quality : type.value()->qualities ) { diff --git a/src/condition.cpp b/src/condition.cpp index ea2d262c30fae..84dfcf7720f34 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -502,6 +502,35 @@ void conditional_t::set_has_trait( const JsonObject &jo, std::string_view member condition = [trait_to_check, is_npc]( dialogue const & d ) { return d.actor( is_npc )->has_trait( trait_id( trait_to_check.evaluate( d ) ) ); }; +} + + +void conditional_t::set_can_see_friends( const JsonObject &jo, std::string_view member, bool is_npc ) +{ + dbl_or_var dov = get_dbl_or_var( jo, member ); + condition = [ dov, is_npc ]( dialogue & d ) { + Character &player_character = get_player_character(); + const Character *ch = d.actor( is_npc )->get_character(); + int seen = 0; + if( is_npc ) { + if( ch->sees( player_character ) && ch->as_npc()->is_friendly( player_character ) ) { + seen += 1; + } + for( npc &guy : g->all_npcs() ) { + if( ch->sees( guy ) && guy.is_friendly( *ch ) ) { + seen += 1; + } + } + } else if( !is_npc ) { + int seen = 0; + for( npc &guy : g->all_npcs() ) { + if( get_player_view().sees( guy ) && guy.is_friendly( player_character ) ) { + seen += 1; + } + } + } + return seen >= dov.evaluate( d ); + }; } void conditional_t::set_has_visible_trait( const JsonObject &jo, std::string_view member, @@ -3522,6 +3551,7 @@ parsers = { {"compare_string", jarg::member, &conditional_t::set_compare_string }, {"get_condition", jarg::member, &conditional_t::set_get_condition }, {"get_game_option", jarg::member, &conditional_t::set_get_option }, + {"u_can_see_friends", "npc_can_see_friends", jarg::member | jarg::array, &conditional_t::set_can_see_friends }, }; // When updating this, please also update `dynamic_line_string_keys` in @@ -3582,7 +3612,7 @@ parsers_simple = { {"u_is_monster", "npc_is_monster", &conditional_t::set_is_monster }, {"u_is_item", "npc_is_item", &conditional_t::set_is_item }, {"u_is_furniture", "npc_is_furniture", &conditional_t::set_is_furniture }, - {"player_see_u", "player_see_npc", &conditional_t::set_player_see }, + {"player_see_u", "player_see_npc", &conditional_t::set_player_see } }; conditional_t::conditional_t( const JsonObject &jo ) diff --git a/src/condition.h b/src/condition.h index aea1323d7e422..62463e83a6a02 100644 --- a/src/condition.h +++ b/src/condition.h @@ -157,6 +157,7 @@ struct conditional_t { void set_mod_is_loaded( const JsonObject &jo, std::string_view member ); void set_mission_goal( const JsonObject &jo, std::string_view member, bool is_npc ); void set_has_faction_trust( const JsonObject &jo, std::string_view member ); + void set_can_see_friends( const JsonObject &jo, std::string_view member, bool is_npc = false ); void set_no_assigned_mission(); void set_has_assigned_mission(); void set_has_many_assigned_missions(); From 474905b66f6e5144add1d61bae47969276ba3ba3 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 17 Jan 2024 08:34:11 -0800 Subject: [PATCH 020/202] switch to flags --- data/json/effects.json | 2 +- .../mutation_eocs/mutation_effect_eocs.json | 12 ++++---- data/json/flags.json | 16 ++++++++++ data/json/mutations/mutations.json | 6 +++- src/activity_handlers.cpp | 30 +++++++++++++++++++ src/monattack.cpp | 6 ++++ 6 files changed, 64 insertions(+), 8 deletions(-) diff --git a/data/json/effects.json b/data/json/effects.json index 7e7b47142cd57..6872c11c9487f 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -3714,7 +3714,7 @@ "id": "social_satisfied", "name": [ "Good Company" ], "desc": [ - "It feels good to spend time with others." + "It feels good to spend time around others." ], "max_intensity": 1, "rating": "good" diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index d0c407b346a6f..5370e37656eeb 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -700,42 +700,42 @@ "type": "effect_on_condition", "id": "EOC_social1_bonus", "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_trait": "SOCIAL1" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "social1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_flag": "SOCIAL1" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "social1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_add_effect": "social_satisfied", "duration": "3 hours" } ] }, { "type": "effect_on_condition", "id": "EOC_social2_bonus", "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_trait": "SOCIAL2" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "social2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "social2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_add_effect": "social_satisfied", "duration": "3 hours" }, { "u_lose_effect": "social_dissatisfied" } ] }, { "type": "effect_on_condition", "id": "EOC_social2_penalty", "recurrence": [ "12 hours", "48 hours" ], - "condition": { "and": [ { "u_has_trait": "SOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "social_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "social_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_add_effect": "social_dissatisfied", "duration": "3 hours", "intensity": { "math": [ "u_effect_intensity('social_dissatisfied') + 1" ] } }, { "u_lose_effect": "social_satisfied" }, { "u_message": "You could use some friendly company.", "type": "bad" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial1_bonus", "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_trait": "ASOCIAL1" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "asocial1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_flag": "ASOCIAL1" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "asocial1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_add_effect": "asocial_satisfied", "duration": "3 hours" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial2_bonus", "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_trait": "ASOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "asocial2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "asocial2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_add_effect": "asocial_satisfied", "duration": "3 hours" }, { "u_lose_effect": "asocial_dissatisfied" }, { "u_message": "You'd really rather be by yourself.", "type": "bad" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial2_penalty", "recurrence": [ "12 hours", "48 hours" ], - "condition": { "and": [ { "u_has_trait": "ASOCIAL2" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_add_effect": "asocial_dissatisfied", "duration": "3 hours", "intensity": { "math": [ "u_effect_intensity('asocial_dissatisfied') + 1" ] } }, { "u_lose_effect": "asocial_satisfied" } ] } ] diff --git a/data/json/flags.json b/data/json/flags.json index 31546543e2376..02c33eaa78cbc 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2418,4 +2418,20 @@ "type": "json_flag", "//": "This item contains blood and satisfies a hemovore's requirements." } + { + "id": "SOCIAL1", + "type": "json_flag", + }, + { + "id": "SOCIAL2", + "type": "json_flag", + }, + { + "id": "ASOCIAL1", + "type": "json_flag", + }, + { + "id": "ASOCIAL2", + "type": "json_flag", + } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 23f0484e522d2..a4b9075536a25 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -1128,6 +1128,7 @@ "changes_to": [ "SOCIAL2" ], "description": "You don't mind being alone, but you get a slight mood boost from being around friends.", "starting_trait": true, + "flags": [ "SOCIAL1" ], "category": [ "CATTLE", "ELFA", "LUPINE", "MOUSE", "RAT", "CHIROPTERAN", "BIRD", "FISH" ] }, { @@ -1137,8 +1138,9 @@ "points": 1, "types": [ "SOCIAL" ], "cancels": [ "ASOCIAL1", "ASOCIAL2" ], - "description": "You are a social creature. Without occasional friendly human contact, you'll start to feel unhappy.", + "description": "You are a social creature. Without occasionally seeing a friendly face, you'll start to feel unhappy.", "starting_trait": true, + "flags": [ "SOCIAL2" ], "category": [ "CATTLE", "LUPINE", "MOUSE", "RAT", "CHIROPTERAN", "ELFA" ] }, { @@ -1151,6 +1153,7 @@ "changes_to": [ "ASOCIAL2" ], "description": "You can tolerate others, but you prefer to be alone. Spending time by yourself will boost your mood slightly.", "starting_trait": true, + "flags": [ "ASOCIAL1" ], "category": [ "SPIDER", "URSINE", "FELINE", "LIZARD", "BEAST", "CHIMERA" ] }, { @@ -1162,6 +1165,7 @@ "cancels": [ "SOCIAL1", "SOCIAL2" ], "description": "You'd really rather be by yourself. Spending time around other people will bring your mood down.", "starting_trait": true, + "flags": [ "ASOCIAL2" ], "category": [ "SPIDER", "URSINE", "LIZARD", "BEAST" ] }, { diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index d67c90b64b04f..02d4ae8658d4e 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -180,12 +180,16 @@ static const damage_type_id damage_bash( "bash" ); static const damage_type_id damage_cut( "cut" ); static const damage_type_id damage_stab( "stab" ); +static const efftype_id effect_asocial_dissatisfied( "asocial_dissatisfied" ); +static const efftype_id effect_asocial_satisfied( "asocial_satisfied" ); static const efftype_id effect_bleed( "bleed" ); static const efftype_id effect_blind( "blind" ); static const efftype_id effect_controlled( "controlled" ); static const efftype_id effect_narcosis( "narcosis" ); static const efftype_id effect_pet( "pet" ); static const efftype_id effect_sleep( "sleep" ); +static const efftype_id effect_social_dissatisfied( "social_dissatisfied" ); +static const efftype_id effect_social_satisfied( "social_satisfied" ); static const efftype_id effect_under_operation( "under_operation" ); static const harvest_drop_type_id harvest_drop_blood( "blood" ); @@ -200,10 +204,14 @@ static const itype_id itype_burnt_out_bionic( "burnt_out_bionic" ); static const itype_id itype_muscle( "muscle" ); static const itype_id itype_pseudo_magazine( "pseudo_magazine" ); +static const json_character_flag json_flag_ASOCIAL1( "ASOCIAL1" ); +static const json_character_flag json_flag_ASOCIAL2( "ASOCIAL2" ); static const json_character_flag json_flag_CANNIBAL( "CANNIBAL" ); static const json_character_flag json_flag_PAIN_IMMUNE( "PAIN_IMMUNE" ); static const json_character_flag json_flag_PSYCHOPATH( "PSYCHOPATH" ); static const json_character_flag json_flag_SAPIOVORE( "SAPIOVORE" ); +static const json_character_flag json_flag_SOCIAL1( "SOCIAL1" ); +static const json_character_flag json_flag_SOCIAL2( "SOCIAL2" ); static const json_character_flag json_flag_SILENT_SPELL( "SILENT_SPELL" ); static const mongroup_id GROUP_FISH( "GROUP_FISH" ); @@ -1610,6 +1618,17 @@ void activity_handlers::mutant_tree_communion_do_turn( player_activity *act, Cha } if( one_in( 128 ) ) { communioncycles += 1; + if( one_in( 256 ) ) { + if( you->has_effect( effect_social_dissatisfied ) ) { + you->remove_effect( effect_social_dissatisfied ); + } + if( ( you->has_flag( json_flag_SOCIAL1 ) || you->has_flag( json_flag_SOCIAL2 ) ) && !you->has_effect( effect_social_satisfied ) ) { + you->add_effect( effect_social_satisfied, 3_hours, false, 1 ); + } + if( ( you->has_flag( json_flag_ASOCIAL1 ) || you->has_flag( json_flag_ASOCIAL2 ) ) && !you->has_effect( effect_asocial_dissatisfied ) ) { + you->add_effect( effect_asocial_dissatisfied, 3_hours, false, 1 ); + } + } you->add_msg_if_player( "%s", SNIPPET.random_from_category( "mutant_tree_communion" ).value_or( translation() ) ); you->add_morale( MORALE_TREE_COMMUNION, 4, 30, 18_hours, 8_hours ); @@ -3778,6 +3797,17 @@ void activity_handlers::tree_communion_do_turn( player_activity *act, Character you->add_morale( MORALE_TREE_COMMUNION, 1, 15, 2_hours, 1_hours ); } if( one_in( 128 ) ) { + if( one_in( 256 ) ) { + if( you->has_effect( effect_social_dissatisfied ) ) { + you->remove_effect( effect_social_dissatisfied ); + } + if( ( you->has_flag( json_flag_SOCIAL1 ) || you->has_flag( json_flag_SOCIAL2 ) ) && !you->has_effect( effect_social_satisfied ) ) { + you->add_effect( effect_social_satisfied, 3_hours, false, 1 ); + } + if( ( you->has_flag( json_flag_ASOCIAL1 ) || you->has_flag( json_flag_ASOCIAL2 ) ) && !you->has_effect( effect_asocial_dissatisfied ) ) { + you->add_effect( effect_asocial_dissatisfied, 3_hours, false, 1 ); + } + } you->add_msg_if_player( "%s", SNIPPET.random_from_category( "tree_communion" ).value_or( translation() ) ); } diff --git a/src/monattack.cpp b/src/monattack.cpp index 7ac8d76dfdcd0..e06a58b1e24c7 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -138,6 +138,7 @@ static const efftype_id effect_raising( "raising" ); static const efftype_id effect_rat( "rat" ); static const efftype_id effect_shrieking( "shrieking" ); static const efftype_id effect_slimed( "slimed" ); +static const efftype_id effect_social_dissatisfied( "social_dissatisfied" ); static const efftype_id effect_stunned( "stunned" ); static const efftype_id effect_taint( "taint" ); static const efftype_id effect_targeted( "targeted" ); @@ -4678,6 +4679,11 @@ bool mattack::slimespring( monster *z ) add_msg( m_good, "%s", SNIPPET.random_from_category( "slime_cheers" ).value_or( translation() ) ); player_character.add_morale( MORALE_SUPPORT, 10, 50 ); } + // They will stave off loneliness, but aren't a substitute for real friends. + if( player_character.has_effect( effect_social_dissatisfied ) ) { + add_msg( m_good, "%s", SNIPPET.random_from_category( "slime_cheers" ).value_or( translation() ) ); + player_character.remove_effect( effect_social_dissatisfied ); + } if( rl_dist( z->pos(), player_character.pos() ) <= 3 && z->sees( player_character ) ) { if( player_character.has_effect( effect_bleed ) || player_character.has_effect( effect_bite ) ) { From 0cf4ee53629744c0198ad8c3c6c4b07f4710d872 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 17 Jan 2024 08:59:15 -0800 Subject: [PATCH 021/202] more friend stuff --- .../mutation_eocs/mutation_effect_eocs.json | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index 5370e37656eeb..97dee58cf8616 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -699,43 +699,50 @@ { "type": "effect_on_condition", "id": "EOC_social1_bonus", - "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_flag": "SOCIAL1" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "social1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_add_effect": "social_satisfied", "duration": "3 hours" } ] + "recurrence": [ "10 minutes", "30 minutes" ], + "condition": { "and": [ { "u_has_flag": "SOCIAL1" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_lose_effect": "social_dissatisfied" }, { "u_add_effect": "social_satisfied", "duration": "2 hours" } ] }, { "type": "effect_on_condition", "id": "EOC_social2_bonus", - "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "social2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_add_effect": "social_satisfied", "duration": "3 hours" }, { "u_lose_effect": "social_dissatisfied" } ] + "recurrence": [ "10 minutes", "30 minutes" ], + "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_lose_effect": "social_dissatisfied" }, { "u_add_effect": "social_satisfied", "duration": "6 hours" }, { "u_lose_effect": "social_dissatisfied" } ] }, { "type": "effect_on_condition", "id": "EOC_social2_penalty", "recurrence": [ "12 hours", "48 hours" ], "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "social_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_add_effect": "social_dissatisfied", "duration": "3 hours", "intensity": { "math": [ "u_effect_intensity('social_dissatisfied') + 1" ] } }, { "u_lose_effect": "social_satisfied" }, { "u_message": "You could use some friendly company.", "type": "bad" } ] + "effect": [ { "u_add_effect": "social_dissatisfied", "duration": "PERMANENT", "intensity": { "math": [ "u_effect_intensity('social_dissatisfied') + 1" ] } }, { "u_lose_effect": "social_satisfied" }, { "u_message": "You could use some friendly company.", "type": "bad" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial1_bonus", - "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_flag": "ASOCIAL1" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "asocial1_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_add_effect": "asocial_satisfied", "duration": "3 hours" } ] + "recurrence": [ "10 minutes", "30 minutes" ], + "condition": { "and": [ { "u_has_flag": "ASOCIAL1" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "2 hours" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial2_bonus", - "recurrence": [ "30 minutes", "1 hours" ], - "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "asocial2_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_add_effect": "asocial_satisfied", "duration": "3 hours" }, { "u_lose_effect": "asocial_dissatisfied" }, { "u_message": "You'd really rather be by yourself.", "type": "bad" } ] + "recurrence": [ "10 minutes", "30 minutes" ], + "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "6 hours" }, { "u_lose_effect": "asocial_dissatisfied" }, { "u_message": "You'd really rather be by yourself.", "type": "bad" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial2_penalty", "recurrence": [ "12 hours", "48 hours" ], "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_add_effect": "asocial_dissatisfied", "duration": "3 hours", "intensity": { "math": [ "u_effect_intensity('asocial_dissatisfied') + 1" ] } }, { "u_lose_effect": "asocial_satisfied" } ] + "effect": [ { "u_add_effect": "asocial_dissatisfied", "duration": "PERMANENT", "intensity": { "math": [ "u_effect_intensity('asocial_dissatisfied') + 1" ] } }, { "u_lose_effect": "asocial_satisfied" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_social_reset_sleep", + "recurrence": [ "30 minutes", "45 minutes" ], + "condition": { "and": { "u_has_effect": "narcosis" }, "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] }, + "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_lose_effect": "social_dissatisfied" } ] } ] From 706040eab304be172cea093cf7459840ca496e01 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:18:43 -0800 Subject: [PATCH 022/202] Update mutations.json --- data/json/mutations/mutations.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index a4b9075536a25..e1bd39c3b10e6 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -252,7 +252,7 @@ "player_display": false, "dummy": true, "category": [ "HUMAN" ], - "types": [ "ANIMAL_EMPATHY", "ATTITUDE", "HUMAN_EMPATHY", "MASOCHISM", "MEMORY", "SLEEPINESS", "WANDERLUST" ], + "types": [ "ANIMAL_EMPATHY", "SOCIAL", "ATTITUDE", "HUMAN_EMPATHY", "MASOCHISM", "MEMORY", "SLEEPINESS", "WANDERLUST" ], "cancels": [ "ADDICTIVE", "ADRENALINE", @@ -1140,6 +1140,7 @@ "cancels": [ "ASOCIAL1", "ASOCIAL2" ], "description": "You are a social creature. Without occasionally seeing a friendly face, you'll start to feel unhappy.", "starting_trait": true, + "mixed_effect": true, "flags": [ "SOCIAL2" ], "category": [ "CATTLE", "LUPINE", "MOUSE", "RAT", "CHIROPTERAN", "ELFA" ] }, @@ -1165,6 +1166,7 @@ "cancels": [ "SOCIAL1", "SOCIAL2" ], "description": "You'd really rather be by yourself. Spending time around other people will bring your mood down.", "starting_trait": true, + "mixed_effect": true, "flags": [ "ASOCIAL2" ], "category": [ "SPIDER", "URSINE", "LIZARD", "BEAST" ] }, @@ -9728,7 +9730,7 @@ "points": 0, "description": "You can't stand to prostrate yourself before your lessers and hate it when they don't immediately do what you say. Persuasion is much more difficult, but you're better at lying and intimidating.", "purifiable": false, - "types": [ "HUMAN_EMPATHY" ], + "types": [ "SOCIAL" ], "social_modifiers": { "lie": 10, "persuade": -25, "intimidate": 10 }, "category": [ "ALPHA" ], "prereqs": [ "INT_ALPHA", "PER_ALPHA" ], From e21fba21ad86750c2bbcea7cc00899677a5a60f8 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 18 Jan 2024 03:42:25 -0800 Subject: [PATCH 023/202] Update mutations.json --- data/json/mutations/mutations.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index e1bd39c3b10e6..4d08bb1439d0e 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -4078,10 +4078,10 @@ "name": { "str": "Mammal Pheromones" }, "points": 2, "description": "Your body produces low-level pheromones which put mammals at ease. They will be less likely to attack or flee from you.", - "prereqs": [ "SMELLY2" ], + "prereqs": [ "SMELLY2", "BAT_LEGS" ], "types": [ "PHEROMONE" ], - "category": [ "BEAST", "CATTLE" ], - "threshreq": [ "THRESH_BEAST", "THRESH_CATTLE" ] + "category": [ "BEAST", "CATTLE", "CHIROPTERAN" ], + "threshreq": [ "THRESH_BEAST", "THRESH_CATTLE", "THRESH_CHIROPTERAN" ] }, { "type": "mutation", From 448d7bdbb6b1d8888bec1fed30fb43e97a7dac14 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 18 Jan 2024 03:47:43 -0800 Subject: [PATCH 024/202] Update flags.json --- data/json/flags.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/data/json/flags.json b/data/json/flags.json index 02c33eaa78cbc..500ea162ca99b 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2417,21 +2417,21 @@ "id": "HEMOVORE_FUN", "type": "json_flag", "//": "This item contains blood and satisfies a hemovore's requirements." - } + }, { "id": "SOCIAL1", - "type": "json_flag", + "type": "json_flag" }, { "id": "SOCIAL2", - "type": "json_flag", + "type": "json_flag" }, { "id": "ASOCIAL1", - "type": "json_flag", + "type": "json_flag" }, { "id": "ASOCIAL2", - "type": "json_flag", + "type": "json_flag" } ] From 276d24d24e27a569f092ff36198bc62054122034 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 18 Jan 2024 04:43:26 -0800 Subject: [PATCH 025/202] fixes --- .../mutation_eocs/mutation_effect_eocs.json | 2 +- data/json/techniques.json | 10 ++++++++-- doc/MARTIALART_JSON.md | 1 + src/melee.cpp | 4 +--- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index 97dee58cf8616..7677633cff726 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -742,7 +742,7 @@ "type": "effect_on_condition", "id": "EOC_social_reset_sleep", "recurrence": [ "30 minutes", "45 minutes" ], - "condition": { "and": { "u_has_effect": "narcosis" }, "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] }, + "condition": { "and": [ { "u_has_effect": "narcosis" }, { "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] } ] }, "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_lose_effect": "social_dissatisfied" } ] } ] diff --git a/data/json/techniques.json b/data/json/techniques.json index ac01c058c2027..caa17342f1062 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3498,10 +3498,16 @@ "melee_allowed": true, "messages": [ "You bite %s!", " bites %s!" ], "unarmed_allowed": true, - "weighting": -30, + "weighting": -4, "reach_ok": false, "attack_override": true, "crit_ok": true, - "attack_vectors": [ "MOUTH" ] + "attack_vectors": [ "MOUTH" ], + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "movecost", "scale": 50 } + ] } ] diff --git a/doc/MARTIALART_JSON.md b/doc/MARTIALART_JSON.md index e44208f6aef14..8cd1fc11b152b 100644 --- a/doc/MARTIALART_JSON.md +++ b/doc/MARTIALART_JSON.md @@ -145,6 +145,7 @@ List of attack vectors is currently hardcoded, and contain: - `WEAPON` - Any technique the requires a held item to perform (see any weapon style). Can be used if the user is holding a valid style weapon for their martial art and at least one hand/arm is not broken. - `THROW` - Any technique that forcefully moves an opponent (judo throws, suplex). Can be used only if both hands/arms are not broken. - `GRAPPLE` - Any technique that maintains contact with an opponent and squeezes (chock, headlock), bends (Krav Maga's Arm Breaker), or twists (arm twist) some part of the opponent. Can be used only if both hands/arms are not broken. +- `MOUTH` - Any technique that uses the mouth, such as biting or spitting acid. Can be used if the mouth is uncovered. Integrated items do not count as cover for determining eligibility of this attack vector. ### Tech effects ```C++ diff --git a/src/melee.cpp b/src/melee.cpp index 2f8a194e3eaa1..f4a05ec5a8d7f 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -807,9 +807,7 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, std::string specialmsg; // Handles speed penalties to monster & us, etc if( !t.is_hallucination() ) { - if( technique.attack_override ) { - specialmsg = melee_special_effects( t, d, null_item_reference() ); - } else { + if( !technique.attack_override ) { specialmsg = melee_special_effects( t, d, cur_weap ); } } From ad023fd74aedc27d51b73b5d703de824dd163666 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 18 Jan 2024 10:20:08 -0800 Subject: [PATCH 026/202] bite bite bite --- data/json/effects.json | 6 ++ data/json/items/armor/integrated.json | 30 +++++- data/json/mutations/mutations.json | 83 ++++++--------- data/json/techniques.json | 145 +++++++++++++++++++++++++- doc/MARTIALART_JSON.md | 7 +- src/bonuses.cpp | 81 ++++++++++++++ src/bonuses.h | 16 +++ src/melee.cpp | 4 +- 8 files changed, 315 insertions(+), 57 deletions(-) diff --git a/data/json/effects.json b/data/json/effects.json index 6872c11c9487f..ad895d0dbef76 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -4699,5 +4699,11 @@ "desc": [ "You are descending at a safe speed." ], "flags": [ "FEATHER_FALL" ], "max_duration": "1 s" + }, + { + "type": "effect_type", + "id": "natural_stance", + "name": [ "Natural Stance" ], + "desc": [ "You are positioned to take advantage of your mutated anatomy, and will favor any natural attacks you may have." ] } ] diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index 519582c84bdd4..fdb564f817bd9 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1583,6 +1583,34 @@ "passive_effects": [ { "has": "WORN", "condition": { "not": "u_has_weapon" }, "values": [ { "value": "ATTACK_SPEED", "add": -20 } ] } ] } }, + { + "id": "integrated_fangs", + "type": "ARMOR", + "category": "armor", + "name": { "str_sp": "fangs" }, + "description": "A pair of fangs sharp and sturdy enough to use in combat.", + "weight": "100 g", + "volume": "112 ml", + "price": 0, + "price_postapoc": 0, + "material": [ "bone" ], + "symbol": ",", + "color": "white", + "warmth": 5, + "to_hit": 1, + "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], + "techniques": [ "FANGS_BITE", "FANGS_BITE_NATURAL", "FANGS_BITE_CRIT" ], + "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], + "armor": [ + { + "material": [ { "type": "bone", "covered_by_mat": 100, "thickness": 3.2 } ], + "covers": [ "mouth" ], + "coverage": 0, + "encumbrance": 0 + } + ], + "melee_damage": { "stab": 16 } + }, { "id": "integrated_vampire_fangs", "type": "ARMOR", @@ -1599,7 +1627,7 @@ "warmth": 5, "to_hit": 1, "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], - "techniques": [ "VAMPIRE_BITE" ], + "techniques": [ "VAMPIRE_BITE", "VAMPIRE_BITE_NATURAL", "VAMPIRE_BITE_CRIT" ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], "armor": [ { diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 4d08bb1439d0e..10d6cde8bf632 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -2456,44 +2456,11 @@ "points": 2, "visibility": 2, "ugliness": 2, - "description": "Your teeth have grown into two-inch-long fangs, allowing you to make an extra attack when conditions favor it.", + "description": "Your teeth have grown into long, sharp fangs. If your mouth is uncovered, you'll sometimes bite your enemies.", "types": [ "TEETH" ], - "changes_to": [ "SABER_TEETH", "SHARKTEETH" ], + "changes_to": [ "SABER_TEETH", "SHARKTEETH", "FANGS_VAMPIRE" ], "category": [ "LIZARD", "FISH", "LUPINE", "FELINE", "CHIMERA", "CHIROPTERAN" ], - "attacks": [ - { - "attack_text_u": "You sink your fangs into %s!", - "attack_text_npc": "%1$s sinks their fangs into %2$s!", - "blocker_mutations": [ "MUZZLE", "MUZZLE_LONG", "MUZZLE_RAT" ], - "body_part": "mouth", - "chance": 20, - "base_damage": { "damage_type": "stab", "amount": 20 } - }, - { - "attack_text_u": "You sink your fangs into %s!", - "attack_text_npc": "%1$s sinks their fangs into %2$s!", - "required_mutations": [ "MUZZLE" ], - "body_part": "mouth", - "chance": 18, - "base_damage": { "damage_type": "stab", "amount": 20 } - }, - { - "attack_text_u": "You sink your fangs into %s!", - "attack_text_npc": "%1$s sinks their fangs into %2$s!", - "required_mutations": [ "MUZZLE_LONG" ], - "body_part": "mouth", - "chance": 15, - "base_damage": { "damage_type": "stab", "amount": 20 } - }, - { - "attack_text_u": "You sink your fangs into %s!", - "attack_text_npc": "%1$s sinks their fangs into %2$s!", - "required_mutations": [ "MUZZLE_RAT" ], - "body_part": "mouth", - "chance": 19, - "base_damage": { "damage_type": "stab", "amount": 20 } - } - ] + "integrated_armor": [ "integrated_fangs" ] }, { "type": "mutation", @@ -2517,7 +2484,7 @@ }, { "type": "mutation", - "id": "VAMPIRE_FANGS", + "id": "FANGS_VAMPIRE", "name": { "str": "Vampire Fangs" }, "points": 2, "visibility": 3, @@ -4078,7 +4045,7 @@ "name": { "str": "Mammal Pheromones" }, "points": 2, "description": "Your body produces low-level pheromones which put mammals at ease. They will be less likely to attack or flee from you.", - "prereqs": [ "SMELLY2", "BAT_LEGS" ], + "prereqs": [ "SMELLY2", "LEGS_BAT" ], "types": [ "PHEROMONE" ], "category": [ "BEAST", "CATTLE", "CHIROPTERAN" ], "threshreq": [ "THRESH_BEAST", "THRESH_CATTLE", "THRESH_CHIROPTERAN" ] @@ -4321,12 +4288,12 @@ }, { "type": "mutation", - "id": "BAT_LEGS", - "name": { "str": "Bat Legs" }, + "id": "LEGS_BAT", + "name": { "str": "Bow-legged" }, "points": -1, "visibility": 2, "ugliness": 2, - "description": "Your lower limbs have shortened and grown bow-legged, slowing your bipedal movement. But as long as you're not holding anything in your fingers, you're able to crawl quickly and quietly.", + "description": "Your lower limbs have shortened and splayed, slowing your bipedal movement. As long as you're not wielding anything, you're able to crawl quickly and quietly when you run or crouch.", "types": [ "LEGS" ], "prereqs": [ "PADDED_FEET", "WINGS_BAT" ], "category": [ "CHIROPTERAN" ], @@ -4343,20 +4310,34 @@ { "u_has_trait": "WINGS_BAT" } ] }, - "msg_on": { "text": "You hunch forward and crawl quickly on folded wings." } + "msg_on": { "text": "You hunch forward to crawl quickly on folded wings." }, + "msg_off": { "text": "You rise up to walk like a human." } } ] ], "enchantments": [ { "condition": { "and": [ { "u_has_move_mode": "crouch" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, - "values": [ { "value": "MOVE_COST", "multiply": -0.18 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ] - }, + "values": [ { "value": "MOVE_COST", "multiply": -0.18 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] + }, { "condition": { "and": [ { "u_has_move_mode": "run" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, - "values": [ { "value": "MOVE_COST", "multiply": -0.18 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.5 } ] - } - ] + "values": [ { "value": "MOVE_COST", "multiply": -0.18 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.6 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] + }, + { + "condition": { + "or": [ + "u_can_drop_weapon", + { "u_has_move_mode": "walk" }, + { "not": { "u_has_trait": "LEGS_BAT" } }, + { "not": { "u_has_trait": "WINGS_BAT" } } + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.1 } ] + } + ] }, { "type": "mutation", @@ -4367,7 +4348,7 @@ "ugliness": 2, "description": "Your feet have grown into strong, curved claws. They won't fit into normal shoes and are not much help in combat, but they allow you to climb most vertical surfaces with ease. You can even cling to walls you glide into!", "types": [ "FEET" ], - "prereqs": [ "BAT_LEGS" ], + "prereqs": [ "LEGS_BAT" ], "category": [ "CHIROPTERAN" ], "flags": [ "WALL_CLING" ], "restricts_gear": [ "foot_l", "foot_r" ], @@ -4415,7 +4396,8 @@ { "u_has_trait": "PAWS" } ] }, - "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ] + "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] }, { "condition": { @@ -4426,7 +4408,8 @@ { "u_has_trait": "PAWS" } ] }, - "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ] + "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] }, { "condition": { diff --git a/data/json/techniques.json b/data/json/techniques.json index caa17342f1062..0e95e49af48e2 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3491,6 +3491,85 @@ ], "attack_vectors": [ "WEAPON", "HAND" ] }, + { + "type": "technique", + "id": "FANGS_BITE", + "name": "Fang Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "FANGS_BITE_NATURAL", + "name": "Fang Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "u_has_effect": "natural_stance" } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "FANGS_BITE_CRIT", + "name": "Fang Bite (crit)", + "melee_allowed": true, + "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 50 } + ] + }, { "type": "technique", "id": "VAMPIRE_BITE", @@ -3498,15 +3577,75 @@ "melee_allowed": true, "messages": [ "You bite %s!", " bites %s!" ], "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 21 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "VAMPIRE_BITE_NATURAL", + "name": "Vampire Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "u_has_effect": "natural_stance" } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 21 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "VAMPIRE_BITE_CRIT", + "name": "Vampire Bite (crit)", + "melee_allowed": true, + "messages": [ "You sink your fangs deep into %s!", " sinks their fangs deep into %s!" ], + "unarmed_allowed": true, "weighting": -4, "reach_ok": false, "attack_override": true, - "crit_ok": true, + "crit_tec": true, "attack_vectors": [ "MOUTH" ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scale": 25 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.4 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.2 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.4 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "movecost", "scale": 50 } ] } diff --git a/doc/MARTIALART_JSON.md b/doc/MARTIALART_JSON.md index 8cd1fc11b152b..0b54a3170335b 100644 --- a/doc/MARTIALART_JSON.md +++ b/doc/MARTIALART_JSON.md @@ -145,7 +145,7 @@ List of attack vectors is currently hardcoded, and contain: - `WEAPON` - Any technique the requires a held item to perform (see any weapon style). Can be used if the user is holding a valid style weapon for their martial art and at least one hand/arm is not broken. - `THROW` - Any technique that forcefully moves an opponent (judo throws, suplex). Can be used only if both hands/arms are not broken. - `GRAPPLE` - Any technique that maintains contact with an opponent and squeezes (chock, headlock), bends (Krav Maga's Arm Breaker), or twists (arm twist) some part of the opponent. Can be used only if both hands/arms are not broken. -- `MOUTH` - Any technique that uses the mouth, such as biting or spitting acid. Can be used if the mouth is uncovered. Integrated items do not count as cover for determining eligibility of this attack vector. +- `MOUTH` - Any technique that uses the mouth, such as biting or spitting acid. Can be used if the mouth is uncovered. Integrated items and personal auras do not count as cover for determining eligibility of this attack vector. ### Tech effects ```C++ @@ -201,7 +201,7 @@ The bonuses arrays contain any number of bonus entries like this: "stat": affected statistic, any of: "hit", "dodge", "block", "speed", "movecost", "damage", "armor", "arpen", "type": damage type for the affected statistic ("bash", "cut", "heat", etc.), only needed if the affected statistic is "damage", "armor", or "arpen". "scale": the value of the bonus itself. -"scaling-stat": scaling stat, any of: "str", "dex", "int", "per". Optional. If the scaling stat is specified, the value of the bonus is multiplied by the corresponding user stat. +"scaling-stat": scaling stat, any of: "str", "dex", "int", "per", "bashing", "cutting", "dodge", "melee", "stabbing", "athletics", "unarmed", "gun", "pistol", "rifle", "shotgun", "smg", "archery", "throw", "launcher", "drive". Optional. If the scaling stat is specified, the value of the bonus is multiplied by the corresponding user stat/skill. Bonuses must be written in the correct order. @@ -220,6 +220,9 @@ All cutting damage dealt is multiplied by `(10% of dexterity)*(damage)`: Move cost is decreased by 100% of strength value * `flat_bonuses: [ { "stat": "movecost", "scaling-stat": "str", "scale": -1.0 } ]` +Gain a bonus to accuracy based on your driving skill. Should probably only be used while in control of a vehicle: +* `flat_bonuses: [ { "stat": "hit", "scaling-stat": "drive", "scale": 0.3 } ]` + ### Place relevant items in the world and chargen Starting trait selection of your martial art goes in mutations.json. Place your art in the right category (self-defense, Shaolin animal form, melee style, etc) diff --git a/src/bonuses.cpp b/src/bonuses.cpp index 0fd54fcc31a41..d7ac7210e5c48 100644 --- a/src/bonuses.cpp +++ b/src/bonuses.cpp @@ -11,6 +11,23 @@ #include "string_formatter.h" #include "translations.h" +static const skill_id skill_bashing( "bashing" ); +static const skill_id skill_cutting( "cutting" ); +static const skill_id skill_dodge( "dodge" ); +static const skill_id skill_melee( "melee" ); +static const skill_id skill_stabbing( "stabbing" ); +static const skill_id skill_swimming( "swimming" ); +static const skill_id skill_unarmed( "unarmed" ); +static const skill_id skill_gun( "gun" ); +static const skill_id skill_pistol( "pistol" ); +static const skill_id skill_rifle( "rifle" ); +static const skill_id skill_shotgun( "shotgun" ); +static const skill_id skill_smg( "smg" ); +static const skill_id skill_throw( "throw" ); +static const skill_id skill_launcher( "launcher" ); +static const skill_id skill_archery( "archery" ); +static const skill_id skill_drive( "drive" ); + static bool needs_damage_type( affected_stat as ) { return as == affected_stat::DAMAGE || @@ -38,6 +55,22 @@ static const std::map scaling_stat_map = {{ std::make_pair( "dex", STAT_DEX ), std::make_pair( "int", STAT_INT ), std::make_pair( "per", STAT_PER ), + std::make_pair( "bash", SKILL_BASHING ), + std::make_pair( "cut", SKILL_CUTTING ), + std::make_pair( "dodge", SKILL_DODGE ), + std::make_pair( "melee", SKILL_MELEE ), + std::make_pair( "stab", SKILL_STABBING ), + std::make_pair( "athletics", SKILL_SWIMMING ), + std::make_pair( "unarmed", SKILL_UNARMED ), + std::make_pair( "marksmanship", SKILL_GUN ), + std::make_pair( "pistol", SKILL_PISTOL ), + std::make_pair( "rifle", SKILL_RIFLE ), + std::make_pair( "shotgun", SKILL_SHOTGUN ), + std::make_pair( "smg", SKILL_SMG ), + std::make_pair( "archery", SKILL_ARCHERY ), + std::make_pair( "throw", SKILL_THROW ), + std::make_pair( "launcher", SKILL_LAUNCHER ), + std::make_pair( "drive", SKILL_DRIVE ) } }; @@ -276,6 +309,54 @@ float effect_scaling::get( const Character &u ) const case STAT_PER: bonus = scale * u.get_per(); break; + case SKILL_BASHING: + bonus = scale * u.get_skill_level( skill_bashing ); + break; + case SKILL_CUTTING: + bonus = scale * u.get_skill_level( skill_cutting ); + break; + case SKILL_DODGE: + bonus = scale * u.get_skill_level( skill_dodge ); + break; + case SKILL_MELEE: + bonus = scale * u.get_skill_level( skill_melee ); + break; + case SKILL_STABBING: + bonus = scale * u.get_skill_level( skill_stabbing ); + break; + case SKILL_SWIMMING: + bonus = scale * u.get_skill_level( skill_swimming ); + break; + case SKILL_UNARMED: + bonus = scale * u.get_skill_level( skill_unarmed ); + break; + case SKILL_GUN: + bonus = scale * u.get_skill_level( skill_gun ); + break; + case SKILL_PISTOL: + bonus = scale * u.get_skill_level( skill_pistol ); + break; + case SKILL_RIFLE: + bonus = scale * u.get_skill_level( skill_rifle ); + break; + case SKILL_SHOTGUN: + bonus = scale * u.get_skill_level( skill_shotgun ); + break; + case SKILL_SMG: + bonus = scale * u.get_skill_level( skill_smg ); + break; + case SKILL_ARCHERY: + bonus = scale * u.get_skill_level( skill_archery ); + break; + case SKILL_THROW: + bonus = scale * u.get_skill_level( skill_throw ); + break; + case SKILL_LAUNCHER: + bonus = scale * u.get_skill_level( skill_launcher ); + break; + case SKILL_DRIVE: + bonus = scale * u.get_skill_level( skill_drive ); + break; case STAT_NULL: bonus = scale; case NUM_STATS: diff --git a/src/bonuses.h b/src/bonuses.h index 9949836589518..ea7f273684330 100644 --- a/src/bonuses.h +++ b/src/bonuses.h @@ -18,6 +18,22 @@ enum scaling_stat : int { STAT_DEX, STAT_INT, STAT_PER, + SKILL_BASHING, + SKILL_CUTTING, + SKILL_DODGE, + SKILL_MELEE, + SKILL_STABBING, + SKILL_SWIMMING, + SKILL_UNARMED, + SKILL_GUN, + SKILL_PISTOL, + SKILL_RIFLE, + SKILL_SHOTGUN, + SKILL_SMG, + SKILL_ARCHERY, + SKILL_THROW, + SKILL_LAUNCHER, + SKILL_DRIVE, NUM_STATS }; diff --git a/src/melee.cpp b/src/melee.cpp index f4a05ec5a8d7f..2f8a194e3eaa1 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -807,7 +807,9 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, std::string specialmsg; // Handles speed penalties to monster & us, etc if( !t.is_hallucination() ) { - if( !technique.attack_override ) { + if( technique.attack_override ) { + specialmsg = melee_special_effects( t, d, null_item_reference() ); + } else { specialmsg = melee_special_effects( t, d, cur_weap ); } } From 4083377901216ce6eebd24f3acb29e46b1b703ce Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:10:08 -0800 Subject: [PATCH 027/202] bitin --- data/json/items/armor/integrated.json | 63 +++- data/json/mutations/mutations.json | 55 +--- data/json/techniques.json | 406 +++++++++++++++++++++++++- 3 files changed, 467 insertions(+), 57 deletions(-) diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index fdb564f817bd9..8e0cd98bf4afe 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -547,6 +547,32 @@ } ] }, + { + "id": "integrated_beak", + "type": "ARMOR", + "category": "armor", + "name": { "str_sp": "beak" }, + "description": "Parts of the upper and lower jaws have been replaced by a beak hard enough to crack walnuts and sharp enough to slice through flesh.", + "weight": "200 g", + "volume": "200 ml", + "price": 0, + "price_postapoc": 0, + "material": [ "chitin" ], + "symbol": ";", + "color": "black", + "warmth": 2, + "qualities": [ [ "CUT", 1 ], [ "BUTCHER", 4 ], [ "HAMMER", 1 ] ], + "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "SKINTIGHT", "PADDED", "NO_SALVAGE", "PROVIDES_TECHNIQUES" ], + "techniques": [ "BEAK_BITE", "BEAK_BITE_NATURAL", "BEAK_BITE_CRIT", "BITE_MOUTH_TENTACLES", "BITE_MOUTH_TENTACLES_NATURAL", "BITE_MOUTH_TENTACLES_CRIT" ], + "armor": [ + { + "material": [ { "type": "chitin", "covered_by_mat": 100, "thickness": 5 } ], + "covers": [ "mouth" ], + "coverage": 60, + "encumbrance": 2 + } + ] + }, { "id": "integrated_mandibles", "type": "ARMOR", @@ -561,7 +587,9 @@ "symbol": ";", "color": "dark_gray", "warmth": 2, - "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "SKINTIGHT", "PADDED", "NO_SALVAGE" ], + "qualities": [ [ "CUT", 1 ], [ "BUTCHER", 4 ] ], + "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "SKINTIGHT", "PADDED", "NO_SALVAGE", "PROVIDES_TECHNIQUES" ], + "techniques": [ "MANDIBLE_BITE", "MANDIBLE_BITE_NATURAL", "MANDIBLE_BITE_CRIT" ], "armor": [ { "material": [ { "type": "chitin", "covered_by_mat": 100, "thickness": 3 } ], @@ -585,7 +613,8 @@ "symbol": ";", "color": "brown", "warmth": 2, - "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "WATER_FRIENDLY", "PADDED" ], + "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "WATER_FRIENDLY", "PADDED", "PROVIDES_TECHNIQUES" ], + "techniques": [ "FOLDING_FANGS_BITE", "FOLDING_FANGS_BITE_NATURAL", "FOLDING_FANGS_BITE_CRIT" ], "armor": [ { "material": [ { "type": "sclerotin", "covered_by_mat": 100, "thickness": 3 } ], @@ -1599,7 +1628,7 @@ "warmth": 5, "to_hit": 1, "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], - "techniques": [ "FANGS_BITE", "FANGS_BITE_NATURAL", "FANGS_BITE_CRIT" ], + "techniques": [ "FANGS_BITE", "FANGS_BITE_NATURAL", "FANGS_BITE_CRIT", "BITE_MUZZLE_BEAR", "BITE_MUZZLE_LONG", "BITE_MUZZLE_RAPTOR", "BITE_MUZZLE", "BITE_MUZZLE_BEAR_NATURAL", "BITE_MUZZLE_LONG_NATURAL", "BITE_MUZZLE_RAPTOR_NATURAL", "BITE_MUZZLE_NATURAL", "BITE_MUZZLE_BEAR_CRIT", "BITE_MUZZLE_LONG_CRIT", "BITE_MUZZLE_RAPTOR_CRIT", "BITE_MUZZLE_CRIT" ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], "armor": [ { @@ -1638,5 +1667,33 @@ } ], "melee_damage": { "stab": 20 } + }, + { + "id": "integrated_incisors", + "type": "ARMOR", + "category": "armor", + "name": { "str_sp": "incisors" }, + "description": "A long, sturdy pair of incisors. No matter how much you grind them down, they just keep growing.", + "weight": "112 g", + "volume": "125 ml", + "price": 0, + "price_postapoc": 0, + "material": [ "bone" ], + "symbol": ",", + "color": "white", + "warmth": 5, + "to_hit": 1, + "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ], [ "CHISEL_WOOD", 1 ] ], + "techniques": [ "INCISORS_BITE", "INCISORS_BITE_NATURAL", "INCISORS_BITE_CRIT", "BITE_MUZZLE_RAT", "BITE_MUZZLE_RAT_NATURAL", "BITE_MUZZLE_RAT_CRIT" ], + "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], + "armor": [ + { + "material": [ { "type": "bone", "covered_by_mat": 100, "thickness": 3.2 } ], + "covers": [ "mouth" ], + "coverage": 0, + "encumbrance": 0 + } + ], + "melee_damage": { "stab": 20 } } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 10d6cde8bf632..fd160e4667248 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -2469,18 +2469,12 @@ "points": 2, "visibility": 3, "ugliness": 3, - "description": "Your big teeth have grown in. Folks had best show some more respect.", + "description": "Your big teeth have grown in. Folks had best show some more respect. If your mouth is uncovered, you'll sometimes deliver a vicious bite in combat.", "types": [ "TEETH" ], "prereqs": [ "MUZZLE_RAT", "RABBIT_NOSE" ], "threshreq": [ "THRESH_RAT", "THRESH_RABBIT" ], "category": [ "RAT", "RABBIT" ], - "attacks": { - "attack_text_u": "You bite into %s with your ratlike incisors!", - "attack_text_npc": "%1$s bites %2$s with their ratlike incisors!", - "body_part": "mouth", - "chance": 18, - "base_damage": [ { "damage_type": "cut", "amount": 3 }, { "damage_type": "bash", "amount": 3 } ] - } + "integrated_armor": [ "integrated_incisors" ] }, { "type": "mutation", @@ -4734,14 +4728,7 @@ "restricts_gear": [ "mouth" ], "destroys_gear": true, "social_modifiers": { "intimidate": 15 }, - "attacks": { - "attack_text_u": "You tear into %s with your saber teeth!", - "attack_text_npc": "%1$s tears into %2$s with their saber teeth!", - "body_part": "mouth", - "chance": 20, - "base_damage": { "damage_type": "stab", "amount": 25 }, - "strength_damage": { "damage_type": "stab", "amount": 1 } - } + "integrated_armor": [ "integrated_saber_teeth" ] }, { "type": "mutation", @@ -5482,22 +5469,14 @@ "butchering_quality": 4, "consume_time_modifier": 0.5, "mixed_effect": true, - "description": "A set of insect-like mandibles have grown around your mouth. They allow you to eat faster and provide a slicing unarmed attack. Slightly reduces wet effects.", + "description": "A set of insect-like mandibles have grown around your mouth. They allow you to eat faster and, if uncovered, will allow you to sometimes bite your enemies. Slightly reduces wet effects.", "types": [ "TEETH", "MUZZLE" ], "prereqs": [ "MOUTH_FLAPS" ], "changes_to": [ "FANGS_SPIDER" ], "leads_to": [ "PROBOSCIS" ], - "category": [ "INSECT", "SPIDER" ], + "category": [ "INSECT" ], "wet_protection": [ { "part": "mouth", "ignored": 1 } ], - "integrated_armor": [ "integrated_mandibles" ], - "attacks": { - "attack_text_u": "You bite %s with your fangs!", - "attack_text_npc": "%1$s bites %2$s with their fangs!", - "blocker_mutations": [ "FANGS_SPIDER" ], - "body_part": "mouth", - "chance": 22, - "base_damage": { "damage_type": "cut", "amount": 12 } - } + "integrated_armor": [ "integrated_mandibles" ] }, { "type": "mutation", @@ -5506,7 +5485,7 @@ "points": 2, "visibility": 6, "ugliness": 6, - "description": "You have developed deadly extensible fangs, allowing you to bite quickly or ensure your venom goes home, as desired.", + "description": "You have developed deadly extensible fangs, allowing you to bite quickly or ensure your venom goes home, as desired. Just make sure you keep them uncovered if you intend to use them.", "types": [ "TEETH", "MUZZLE" ], "cancels": [ "MOUTH_TENTACLES" ], "prereqs": [ "MANDIBLES" ], @@ -5514,15 +5493,7 @@ "category": [ "SPIDER" ], "consume_time_modifier": 2.0, "wet_protection": [ { "part": "mouth", "ignored": 1 } ], - "integrated_armor": [ "integrated_fangs_spider" ], - "attacks": { - "attack_text_u": "You bite %s with your fangs!", - "attack_text_npc": "%1$s bites %2$s with their fangs!", - "blocker_mutations": [ "MANDIBLES" ], - "body_part": "mouth", - "chance": 22, - "base_damage": { "damage_type": "stab", "amount": 15 } - } + "integrated_armor": [ "integrated_fangs_spider" ] }, { "type": "mutation", @@ -7325,20 +7296,14 @@ "visibility": 8, "ugliness": 4, "mixed_effect": true, - "description": "You have a beak for a mouth. You can occasionally use it to peck at your enemies, but it is impossible for you to wear mouth gear. Slightly reduces wet effects.", + "description": "You have a beak for a mouth, and it's hard to find gear that fits over it. If you keep it uncovered, you will sometimes use it to bite in combat. Slightly reduces wet effects.", "types": [ "TEETH", "MUZZLE" ], "changes_to": [ "BEAK_HUM", "BEAK_PECK" ], "category": [ "BIRD", "CEPHALOPOD" ], "wet_protection": [ { "part": "mouth", "ignored": 1 } ], "restricts_gear": [ "mouth" ], "destroys_gear": true, - "attacks": { - "attack_text_u": "You peck %s!", - "attack_text_npc": "%1$s pecks %2$s!", - "body_part": "mouth", - "chance": 15, - "base_damage": { "damage_type": "stab", "amount": 15 } - } + "integrated_armor": [ "integrated_beak" ] }, { "type": "mutation", diff --git a/data/json/techniques.json b/data/json/techniques.json index 0e95e49af48e2..96633984083f1 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3505,12 +3505,19 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ - { "not": { "u_has_effect": "natural_stance" } } - ] + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scale": 14 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, @@ -3532,11 +3539,11 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ - { "u_has_effect": "natural_stance" } + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scale": 14 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, @@ -3559,7 +3566,7 @@ "crit_tec": true, "attack_vectors": [ "MOUTH" ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scale": 16 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, @@ -3584,12 +3591,19 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ - { "not": { "u_has_effect": "natural_stance" } } - ] + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] }, "flat_bonuses": [ { "stat": "damage", "type": "stab", "scale": 21 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, @@ -3611,7 +3625,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ - { "u_has_effect": "natural_stance" } + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } ] }, "flat_bonuses": [ @@ -3648,5 +3662,379 @@ { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "movecost", "scale": 50 } ] + }, + { + "type": "technique", + "id": "INCISORS_BITE", + "name": "Incisor Bite", + "melee_allowed": true, + "messages": [ "You bite %s with your sharp incisors!", " bites %s with their sharp incisors!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "INCISORS_BITE_NATURAL", + "name": "Incisor Bite", + "melee_allowed": true, + "messages": [ "You bite %s with your sharp incisors!", " bites %s with their sharp incisors!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "INCISORS_BITE_CRIT", + "name": "Incisor Bite (crit)", + "melee_allowed": true, + "messages": [ "You rip into %s with your incisors!", " rips into %s with their incisors!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "SHARKTEETH_BITE", + "name": "Shark Teeth Bite", + "melee_allowed": true, + "messages": [ "You chomp on %s!", " chomps on %s!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 30 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 66 } + ] + }, + { + "type": "technique", + "id": "SHARKTEETHBITE_NATURAL", + "name": "Shark Teeth Bite", + "melee_allowed": true, + "messages": [ "You chomp on %s!", " chomps on %s!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 30 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 66 } + ] + }, + { + "type": "technique", + "id": "SHARKTEETH_BITE_CRIT", + "name": "Shark Teeth Bite (crit)", + "melee_allowed": true, + "messages": [ "You sink your teeth into %s and thrash violently!", " sinks their teeth into %s and thrashes violently!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 34 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 66 } + ] + }, + { + "type": "technique", + "id": "BEAK_BITE", + "name": "Beak Bite", + "melee_allowed": true, + "messages": [ "You bite %s with your beak!", " bites %s with their beak!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "cut", "scale": 16 }, + { "stat": "damage", "type": "bash", "scale": 6 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "BEAK_BITE_NATURAL", + "name": "Beak Bite", + "melee_allowed": true, + "messages": [ "You bite %s with your sharp incisors!", " bites %s with their sharp incisors!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "cut", "scale": 16 }, + { "stat": "damage", "type": "bash", "scale": 6 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "BEAK_BITE_CRIT", + "name": "Incisor Bite (crit)", + "melee_allowed": true, + "messages": [ "You tear into %s with your beak!", " tears into %s with their beak!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + + "flat_bonuses": [ + { "stat": "damage", "type": "cut", "scale": 20 }, + { "stat": "damage", "type": "bash", "scale": 8 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.4 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MANDIBLES_BITE", + "name": "Mandible Bite", + "melee_allowed": true, + "messages": [ "You bite %s with your mandibles!", " bites %s with their mandibles!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "cut", "scale": 18 }, + { "stat": "damage", "type": "bash", "scale": 10 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 66 } + ] + }, + { + "type": "technique", + "id": "MANDIBLE_BITE_NATURAL", + "name": "Mandible Bite", + "melee_allowed": true, + "messages": [ "You bite %s with your mandibles!", " bites %s with their mandibles!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "cut", "scale": 18 }, + { "stat": "damage", "type": "bash", "scale": 10 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 66 } + ] + }, + { + "type": "technique", + "id": "MANDIBLE_BITE_CRIT", + "name": "Mandible Bite (crit)", + "melee_allowed": true, + "messages": [ "You tear into %s with your mandibles!", " tears into %s with their mandibles!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + + "flat_bonuses": [ + { "stat": "damage", "type": "cut", "scale": 24 }, + { "stat": "damage", "type": "bash", "scale": 14 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.4 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 66 } + ] } ] From 40e3c4528917f143a2689a4b2fba51b17e55ac45 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:53:09 -0800 Subject: [PATCH 028/202] blrlrlrlghh biting --- data/json/items/armor/integrated.json | 36 +- data/json/techniques.json | 709 +++++++++++++++++++++++++- 2 files changed, 738 insertions(+), 7 deletions(-) diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index 8e0cd98bf4afe..fb7a58f97e6e6 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -563,7 +563,7 @@ "warmth": 2, "qualities": [ [ "CUT", 1 ], [ "BUTCHER", 4 ], [ "HAMMER", 1 ] ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "SKINTIGHT", "PADDED", "NO_SALVAGE", "PROVIDES_TECHNIQUES" ], - "techniques": [ "BEAK_BITE", "BEAK_BITE_NATURAL", "BEAK_BITE_CRIT", "BITE_MOUTH_TENTACLES", "BITE_MOUTH_TENTACLES_NATURAL", "BITE_MOUTH_TENTACLES_CRIT" ], + "techniques": [ "BEAK_BITE", "BEAK_BITE_NATURAL", "BEAK_BITE_CRIT", "MOUTH_TENTACLES_BITE", "MOUTH_TENTACLES_BITE_NATURAL", "MOUTH_TENTACLES_BITE_CRIT" ], "armor": [ { "material": [ { "type": "chitin", "covered_by_mat": 100, "thickness": 5 } ], @@ -589,7 +589,7 @@ "warmth": 2, "qualities": [ [ "CUT", 1 ], [ "BUTCHER", 4 ] ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "SKINTIGHT", "PADDED", "NO_SALVAGE", "PROVIDES_TECHNIQUES" ], - "techniques": [ "MANDIBLE_BITE", "MANDIBLE_BITE_NATURAL", "MANDIBLE_BITE_CRIT" ], + "techniques": [ "MANDIBLES_BITE", "MANDIBLES_BITE_NATURAL", "MANDIBLES_BITE_CRIT" ], "armor": [ { "material": [ { "type": "chitin", "covered_by_mat": 100, "thickness": 3 } ], @@ -1628,7 +1628,35 @@ "warmth": 5, "to_hit": 1, "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], - "techniques": [ "FANGS_BITE", "FANGS_BITE_NATURAL", "FANGS_BITE_CRIT", "BITE_MUZZLE_BEAR", "BITE_MUZZLE_LONG", "BITE_MUZZLE_RAPTOR", "BITE_MUZZLE", "BITE_MUZZLE_BEAR_NATURAL", "BITE_MUZZLE_LONG_NATURAL", "BITE_MUZZLE_RAPTOR_NATURAL", "BITE_MUZZLE_NATURAL", "BITE_MUZZLE_BEAR_CRIT", "BITE_MUZZLE_LONG_CRIT", "BITE_MUZZLE_RAPTOR_CRIT", "BITE_MUZZLE_CRIT" ], + "techniques": [ "FANGS_BITE", "FANGS_BITE_NATURAL", "FANGS_BITE_CRIT", "MUZZLE_BEAR_BITE", "MUZZLE_LONG_BITE", "MUZZLE_RAPTOR_BITE", "MUZZLE_BITE", "MUZZLE_BEAR_BITE_NATURAL", "MUZZLE_LONG_BITE_NATURAL", "MUZZLE_RAPTOR_BITE_NATURAL", "MUZZLE_BITE_NATURAL", "MUZZLE_BEAR_BITE_CRIT", "MUZZLE_LONG_BITE_CRIT", "MUZZLE_RAPTOR_BITE_CRIT", "MUZZLE_BITE_CRIT" ], + "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], + "armor": [ + { + "material": [ { "type": "bone", "covered_by_mat": 100, "thickness": 3.2 } ], + "covers": [ "mouth" ], + "coverage": 0, + "encumbrance": 0 + } + ], + "melee_damage": { "stab": 16 } + }, + { + "id": "integrated_sharkteeth", + "type": "ARMOR", + "category": "armor", + "name": { "str_sp": "shark teeth" }, + "description": "A mouth full of multiple rows of very intimidating teeth.", + "weight": "100 g", + "volume": "112 ml", + "price": 0, + "price_postapoc": 0, + "material": [ "bone" ], + "symbol": ",", + "color": "white", + "warmth": 5, + "to_hit": 1, + "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], + "techniques": [ "SHARKTEETH_BITE", "SHARKTEETH_BITE_NATURAL", "SHARKTEETH_BITE_CRIT" ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], "armor": [ { @@ -1684,7 +1712,7 @@ "warmth": 5, "to_hit": 1, "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ], [ "CHISEL_WOOD", 1 ] ], - "techniques": [ "INCISORS_BITE", "INCISORS_BITE_NATURAL", "INCISORS_BITE_CRIT", "BITE_MUZZLE_RAT", "BITE_MUZZLE_RAT_NATURAL", "BITE_MUZZLE_RAT_CRIT" ], + "techniques": [ "INCISORS_BITE", "INCISORS_BITE_NATURAL", "INCISORS_BITE_CRIT", "MUZZLE_RAT_BITE", "MUZZLE_RAT_BITE_NATURAL", "MUZZLE_RAT_BITE_CRIT" ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], "armor": [ { diff --git a/data/json/techniques.json b/data/json/techniques.json index 96633984083f1..0264e75a38ab8 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3577,6 +3577,522 @@ { "stat": "movecost", "scale": 50 } ] }, + { + "type": "technique", + "id": "SABER_TEETH_BITE", + "name": "Saber Teeth Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "SABER_TEETH_BITE_NATURAL", + "name": "Saber Teeth Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "SABER_TEETH_BITE_CRIT", + "name": "Saber Teeth Bite (crit)", + "melee_allowed": true, + "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_BITE", + "name": "Muzzle Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_BITE_NATURAL", + "name": "Muzzle Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_BITE_CRIT", + "name": "Muzzle Bite (crit)", + "melee_allowed": true, + "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_RAPTOR_BITE", + "name": "Raptor Muzzle Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_RAPTOR_BITE_NATURAL", + "name": "Raptor Muzzle Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_RAPTOR_BITE_CRIT", + "name": "Raptor Muzzle Bite (crit)", + "melee_allowed": true, + "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_LONG_BITE", + "name": "Reptilian Muzzle Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_LONG_BITE_NATURAL", + "name": "Reptilian Muzzle Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_LONG_BITE_CRIT", + "name": "Reptilian Muzzle Bite (crit)", + "melee_allowed": true, + "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_BEAR_BITE", + "name": "Reptilian Muzzle Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_BEAR_BITE_NATURAL", + "name": "Reptilian Muzzle Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_BEAR_BITE_CRIT", + "name": "Reptilian Muzzle Bite (crit)", + "melee_allowed": true, + "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_RAT_BITE", + "name": "Reptilian Muzzle Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_RAT_BITE_NATURAL", + "name": "Reptilian Muzzle Bite", + "melee_allowed": true, + "messages": [ "You bite %s!", " bites %s!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 50 } + ] + }, + { + "type": "technique", + "id": "MUZZLE_RAT_BITE_CRIT", + "name": "Reptilian Muzzle Bite (crit)", + "melee_allowed": true, + "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 50 } + ] + }, { "type": "technique", "id": "VAMPIRE_BITE", @@ -3785,7 +4301,7 @@ }, { "type": "technique", - "id": "SHARKTEETHBITE_NATURAL", + "id": "SHARKTEETH_BITE_NATURAL", "name": "Shark Teeth Bite", "melee_allowed": true, "messages": [ "You chomp on %s!", " chomps on %s!" ], @@ -3936,6 +4452,107 @@ { "stat": "movecost", "scale": 50 } ] }, + { + "type": "technique", + "id": "MOUTH_TENTACLES_BITE", + "name": "Mouth Tentacles Bite", + "melee_allowed": true, + "messages": [ "You grab %s with your tentacles and bite down hard!", " grabs %s with their tentacles and bites down hard!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "cut", "scale": 22 }, + { "stat": "damage", "type": "bash", "scale": 8 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 66 } + ] + }, + { + "type": "technique", + "id": "MOUTH_TENTACLES_BITE_NATURAL", + "name": "Beak Bite", + "melee_allowed": true, + "messages": [ "You grab %s with your tentacles and bite down hard!", " grabs %s with their tentacles and bites down hard!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "cut", "scale": 22 }, + { "stat": "damage", "type": "bash", "scale": 8 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 66 } + ] + }, + { + "type": "technique", + "id": "MOUTH_TENTACLES_BITE_CRIT", + "name": "Incisor Bite (crit)", + "melee_allowed": true, + "messages": [ "You grab %s with your tentacles and deliver a vicious bite!", " grabs %s with their tentacles and delivers a vicious bite!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + + "flat_bonuses": [ + { "stat": "damage", "type": "cut", "scale": 24 }, + { "stat": "damage", "type": "bash", "scale": 10 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.4 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, + { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 66 } + ] + }, { "type": "technique", "id": "MANDIBLES_BITE", @@ -3977,7 +4594,7 @@ }, { "type": "technique", - "id": "MANDIBLE_BITE_NATURAL", + "id": "MANDIBLES_BITE_NATURAL", "name": "Mandible Bite", "melee_allowed": true, "messages": [ "You bite %s with your mandibles!", " bites %s with their mandibles!" ], @@ -4010,7 +4627,7 @@ }, { "type": "technique", - "id": "MANDIBLE_BITE_CRIT", + "id": "MANDIBLES_BITE_CRIT", "name": "Mandible Bite (crit)", "melee_allowed": true, "messages": [ "You tear into %s with your mandibles!", " tears into %s with their mandibles!" ], @@ -4036,5 +4653,91 @@ { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "movecost", "scale": 66 } ] + }, + { + "type": "technique", + "id": "FOLDING_FANGS_BITE", + "name": "Folding Fangs Bite", + "melee_allowed": true, + "messages": [ "You deliver a quick bite to %s!", " delivers a quick bite to %s!" ], + "unarmed_allowed": true, + "weighting": -5, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "not": { "u_has_effect": "natural_stance" } }, + { + "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] + } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 40 } + ] + }, + { + "type": "technique", + "id": "FOLDING_FANGS_BITE_NATURAL", + "name": "Folding Fangs Bite", + "melee_allowed": true, + "messages": [ "You deliver a quick bite to %s!", " delivers a quick bite to %s!" ], + "unarmed_allowed": true, + "weighting": -2, + "reach_ok": false, + "attack_override": true, + "crit_ok": false, + "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + ] + }, + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "movecost", "scale": 40 } + ] + }, + { + "type": "technique", + "id": "FOLDING_FANGS_BITE_CRIT", + "name": "Folding Fangs Bite", + "melee_allowed": true, + "messages": [ "You plunge your fangs deep into %s!", " plunges their fangs deep into %s!" ], + "unarmed_allowed": true, + "weighting": -4, + "reach_ok": false, + "attack_override": true, + "crit_tec": true, + "attack_vectors": [ "MOUTH" ], + "flat_bonuses": [ + { "stat": "damage", "type": "stab", "scale": 24 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.4 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.2 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.4 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 40 } + ] } ] From bbd307749db9cb9b60eac498b73a1ee6cabbf4ac Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 19 Jan 2024 04:34:05 -0800 Subject: [PATCH 029/202] bites done --- data/json/items/armor/integrated.json | 2 +- data/json/mutations/mutations.json | 11 +- data/json/techniques.json | 254 +++++++++++++------------- 3 files changed, 129 insertions(+), 138 deletions(-) diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index fb7a58f97e6e6..ba09b7faa8c33 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1712,7 +1712,7 @@ "warmth": 5, "to_hit": 1, "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ], [ "CHISEL_WOOD", 1 ] ], - "techniques": [ "INCISORS_BITE", "INCISORS_BITE_NATURAL", "INCISORS_BITE_CRIT", "MUZZLE_RAT_BITE", "MUZZLE_RAT_BITE_NATURAL", "MUZZLE_RAT_BITE_CRIT" ], + "techniques": [ "INCISORS_BITE", "INCISORS_BITE_NATURAL", "INCISORS_BITE_CRIT", "MUZZLE_RAT_BITE", "MUZZLE_RAT_BITE_NATURAL" ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], "armor": [ { diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index fd160e4667248..588a6c07f2f66 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -5474,7 +5474,7 @@ "prereqs": [ "MOUTH_FLAPS" ], "changes_to": [ "FANGS_SPIDER" ], "leads_to": [ "PROBOSCIS" ], - "category": [ "INSECT" ], + "category": [ "INSECT", "SPIDER" ], "wet_protection": [ { "part": "mouth", "ignored": 1 } ], "integrated_armor": [ "integrated_mandibles" ] }, @@ -8618,14 +8618,7 @@ "category": [ "FISH" ], "threshreq": [ "THRESH_FISH" ], "social_modifiers": { "intimidate": 5 }, - "attacks": { - "attack_text_u": "You tear into %s with your teeth!", - "attack_text_npc": "%1$s tears into %2$s with their teeth!", - "body_part": "mouth", - "blocker_mutations": [ "MUZZLE", "MUZZLE_LONG", "MUZZLE_RAT" ], - "chance": 20, - "base_damage": { "damage_type": "stab", "amount": 25 } - }, + "integrated_armor": [ "integrated_sharkteeth" ], "purifiable": false }, { diff --git a/data/json/techniques.json b/data/json/techniques.json index 0264e75a38ab8..20f0557b02d61 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3668,7 +3668,7 @@ "id": "MUZZLE_BITE", "name": "Muzzle Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You throw %s off balance with a savage bite!", " throws %s off balance with a savage bite!" ], "unarmed_allowed": true, "weighting": -5, "reach_ok": false, @@ -3677,23 +3677,26 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] + { "math": [ "u_val('size') + 1", ">=", "n_val('size')" ] }, + { "not": { "u_has_effect": "natural_stance" } }, + { "not": { "npc_has_effect": "downed" } }, + { "or": [ { "npc_bodytype": "human" }, { "npc_bodytype": "angel" }, { "npc_bodytype": "bear" }, { "npc_bodytype": "kangoroo" }, { "npc_bodytype": "horse" }, { "npc_bodytype": "pig" }, { "npc_bodytype": "migo" } ] }, + { "or": [ { "not": { "npc_has_flag": "FLIES" } }, { "npc_has_flag": "DISABLE_FLIGHT" } ] }, + { "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] } + ] }, + "down_dur": 2, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scale": 12 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, { "stat": "movecost", "scale": 50 } ] }, @@ -3702,7 +3705,7 @@ "id": "MUZZLE_BITE_NATURAL", "name": "Muzzle Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You throw %s off balance with a savage bite!", " throws %s off balance with a savage bite!" ], "unarmed_allowed": true, "weighting": -2, "reach_ok": false, @@ -3711,17 +3714,26 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } - ] + { "math": [ "u_val('size') + 1", ">=", "n_val('size')" ] }, + { "u_has_effect": "natural_stance" }, + { "not": { "npc_has_effect": "downed" } }, + { "or": [ { "npc_bodytype": "human" }, { "npc_bodytype": "angel" }, { "npc_bodytype": "bear" }, { "npc_bodytype": "kangoroo" }, { "npc_bodytype": "horse" }, { "npc_bodytype": "pig" }, { "npc_bodytype": "migo" } ] }, + { "or": [ { "not": { "npc_has_flag": "FLIES" } }, { "npc_has_flag": "DISABLE_FLIGHT" } ] }, + { "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] } + ] }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + "down_dur": 2, + "flat_bonuses": [ + { "stat": "damage", "type": "cut", "scale": 12 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, { "stat": "movecost", "scale": 50 } ] }, @@ -3730,22 +3742,34 @@ "id": "MUZZLE_BITE_CRIT", "name": "Muzzle Bite (crit)", "melee_allowed": true, - "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "messages": [ "You down %s with a ferocious bite!", " downs %s with a ferocious bite!" ], "unarmed_allowed": true, "weighting": -4, "reach_ok": false, "attack_override": true, "crit_tec": true, "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "math": [ "u_val('size') + 1", ">=", "n_val('size')" ] }, + { "not": { "npc_has_effect": "downed" } }, + { "or": [ { "npc_bodytype": "human" }, { "npc_bodytype": "angel" }, { "npc_bodytype": "bear" }, { "npc_bodytype": "kangoroo" }, { "npc_bodytype": "horse" }, { "npc_bodytype": "pig" }, { "npc_bodytype": "migo" }, { "npc_bodytype": "flying_insect" }, { "npc_bodytype": "crab" }, { "npc_bodytype": "spider" }, { "npc_bodytype": "bird" } ] }, + { "and": [ + { "not": { "npc_has_flag": "GRAB_FILTER" } }, + { "not": { "u_has_effect": "GRAB" } } + ] } + ] + }, + "down_dur": 2, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 16 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scale": 14 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "movecost", "scale": 50 } ] }, @@ -3754,7 +3778,7 @@ "id": "MUZZLE_RAPTOR_BITE", "name": "Raptor Muzzle Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You rip into %s with your sawlike teeth!", " rips into %s with their sawlike teeth!" ], "unarmed_allowed": true, "weighting": -5, "reach_ok": false, @@ -3773,13 +3797,12 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scale": 14 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.2 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, { "stat": "movecost", "scale": 50 } ] }, @@ -3788,7 +3811,7 @@ "id": "MUZZLE_RAPTOR_BITE_NATURAL", "name": "Raptor Muzzle Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You rip into %s with your sawlike teeth!", " rips into %s with their sawlike teeth!" ], "unarmed_allowed": true, "weighting": -2, "reach_ok": false, @@ -3801,13 +3824,12 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scale": 14 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.2 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, { "stat": "movecost", "scale": 50 } ] }, @@ -3816,7 +3838,7 @@ "id": "MUZZLE_RAPTOR_BITE_CRIT", "name": "Raptor Muzzle Bite (crit)", "melee_allowed": true, - "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "messages": [ "You shred %s with your sawlike teeth!", " shreds %s with their sawlike teeth!" ], "unarmed_allowed": true, "weighting": -4, "reach_ok": false, @@ -3824,14 +3846,13 @@ "crit_tec": true, "attack_vectors": [ "MOUTH" ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 16 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scale": 16 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.4 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 3.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "movecost", "scale": 50 } ] }, @@ -3840,7 +3861,7 @@ "id": "MUZZLE_LONG_BITE", "name": "Reptilian Muzzle Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You sink your teeth into %s and thrash violently!", " sinks their teeth into %s and thrashes violently!" ], "unarmed_allowed": true, "weighting": -5, "reach_ok": false, @@ -3859,14 +3880,13 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } + { "stat": "damage", "type": "cut", "scale": 18 }, + { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 66 } ] }, { @@ -3874,7 +3894,7 @@ "id": "MUZZLE_LONG_BITE_NATURAL", "name": "Reptilian Muzzle Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You sink your teeth into %s and thrash violently!", " sinks their teeth into %s and thrashes violently!" ], "unarmed_allowed": true, "weighting": -2, "reach_ok": false, @@ -3887,14 +3907,13 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scale": 18 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "movecost", "scale": 66 } ] }, { @@ -3902,7 +3921,7 @@ "id": "MUZZLE_LONG_BITE_CRIT", "name": "Reptilian Muzzle Bite (crit)", "melee_allowed": true, - "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "messages": [ "You sink your teeth into %s and thrash violently!", " sinks their teeth into %s and thrashes violently!" ], "unarmed_allowed": true, "weighting": -4, "reach_ok": false, @@ -3910,23 +3929,22 @@ "crit_tec": true, "attack_vectors": [ "MOUTH" ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 16 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.4 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, + { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 50 } + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.3 }, + { "stat": "movecost", "scale": 66 } ] }, { "type": "technique", "id": "MUZZLE_BEAR_BITE", - "name": "Reptilian Muzzle Bite", + "name": "Bear Muzzle Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You maul %s!", " mauls %s!" ], "unarmed_allowed": true, "weighting": -5, "reach_ok": false, @@ -3935,6 +3953,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ + { "or": [ { "npc_has_effect": "downed" }, { "and": [ { "npc_has_effect": "stunned" } ] } ] }, { "not": { "u_has_effect": "natural_stance" } }, { "and": [ @@ -3945,22 +3964,22 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scale": 32 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } + { "stat": "movecost", "scale": 100 } ] }, { "type": "technique", "id": "MUZZLE_BEAR_BITE_NATURAL", - "name": "Reptilian Muzzle Bite", + "name": "Bear Muzzle Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You maul %s!", " mauls %s!" ], "unarmed_allowed": true, "weighting": -2, "reach_ok": false, @@ -3973,22 +3992,22 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scale": 32 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } + { "stat": "movecost", "scale": 100 } ] }, { "type": "technique", "id": "MUZZLE_BEAR_BITE_CRIT", - "name": "Reptilian Muzzle Bite (crit)", + "name": "Bear Muzzle Bite (crit)", "melee_allowed": true, - "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "messages": [ "You maul %s!", " mauls %s!" ], "unarmed_allowed": true, "weighting": -4, "reach_ok": false, @@ -3996,7 +4015,7 @@ "crit_tec": true, "attack_vectors": [ "MOUTH" ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 16 }, + { "stat": "damage", "type": "stab", "scale": 38 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, @@ -4004,15 +4023,15 @@ { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 50 } + { "stat": "movecost", "scale": 100 } ] }, { "type": "technique", "id": "MUZZLE_RAT_BITE", - "name": "Reptilian Muzzle Bite", + "name": "Rat Muzzle Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You quickly bite %s!", " quickly bites %s!" ], "unarmed_allowed": true, "weighting": -5, "reach_ok": false, @@ -4031,22 +4050,22 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scale": 11 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } + { "stat": "movecost", "scale": 25 } ] }, { "type": "technique", "id": "MUZZLE_RAT_BITE_NATURAL", - "name": "Reptilian Muzzle Bite", + "name": "Rat Muzzle Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You quickly bite %s!", " quickly bites %s!" ], "unarmed_allowed": true, "weighting": -2, "reach_ok": false, @@ -4059,38 +4078,14 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scale": 11 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_RAT_BITE_CRIT", - "name": "Reptilian Muzzle Bite (crit)", - "melee_allowed": true, - "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 16 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 50 } + { "stat": "movecost", "scale": 25 } ] }, { @@ -4313,7 +4308,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + { "or": [ { "u_has_effect": "natural_stance" }, { "u_is_underwater": "true" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } ] }, "flat_bonuses": [ @@ -4478,6 +4473,7 @@ "flat_bonuses": [ { "stat": "damage", "type": "cut", "scale": 22 }, { "stat": "damage", "type": "bash", "scale": 8 }, + { "stat": "hit", "scale": 1 }, { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, @@ -4511,6 +4507,7 @@ "flat_bonuses": [ { "stat": "damage", "type": "cut", "scale": 22 }, { "stat": "damage", "type": "bash", "scale": 8 }, + { "stat": "hit", "scale": 1 }, { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, @@ -4540,6 +4537,7 @@ "flat_bonuses": [ { "stat": "damage", "type": "cut", "scale": 24 }, { "stat": "damage", "type": "bash", "scale": 10 }, + { "stat": "hit", "scale": 1 }, { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.4 }, { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, From 2263d57721e38eb5d919a4fc5f8ec99723dc37f6 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 19 Jan 2024 05:07:41 -0800 Subject: [PATCH 030/202] muzzle descs --- data/json/mutations/mutations.json | 69 ++++++++---------------------- data/json/techniques.json | 12 +++++- 2 files changed, 30 insertions(+), 51 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 588a6c07f2f66..330cd3f4ff82d 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -2459,7 +2459,7 @@ "description": "Your teeth have grown into long, sharp fangs. If your mouth is uncovered, you'll sometimes bite your enemies.", "types": [ "TEETH" ], "changes_to": [ "SABER_TEETH", "SHARKTEETH", "FANGS_VAMPIRE" ], - "category": [ "LIZARD", "FISH", "LUPINE", "FELINE", "CHIMERA", "CHIROPTERAN" ], + "category": [ "LIZARD", "FISH", "LUPINE", "FELINE", "CHIMERA", "CHIROPTERAN", "URSINE", "BEAST" ], "integrated_armor": [ "integrated_fangs" ] }, { @@ -2469,7 +2469,7 @@ "points": 2, "visibility": 3, "ugliness": 3, - "description": "Your big teeth have grown in. Folks had best show some more respect. If your mouth is uncovered, you'll sometimes deliver a vicious bite in combat.", + "description": "Your big teeth have grown in. Folks had best show some more respect, because as long as your mouth is uncovered, you'll sometimes deliver a vicious bite in combat.", "types": [ "TEETH" ], "prereqs": [ "MUZZLE_RAT", "RABBIT_NOSE" ], "threshreq": [ "THRESH_RAT", "THRESH_RABBIT" ], @@ -4979,20 +4979,12 @@ "visibility": 8, "ugliness": 8, "mixed_effect": true, - "description": "Your face has drastically mutated, with the bones of your jaws having extended forth into a long, tapered snout. The palate of your mouth is considerably vaulted, with curved, serrated teeth lining your saurian maw and reptilian scales covering your muzzle’s exterior. While paleobiology has evidently not evolved with mouth gear in mind (making such items unwearable), the bite wounds you can now inflict promise the ferocity of long-dead beasts.", + "description": "Your face has drastically mutated, with the bones of your jaws having extended forth into a long, tapered snout. The palate of your mouth is considerably vaulted, with curved, serrated teeth lining your saurian maw. While modern-day gas masks mostly don't account for paleobiology, if you manage to evolve a set of fangs, you will bite with the ferocity of long-dead beasts.", "types": [ "MUZZLE" ], "prereqs": [ "SNOUT" ], "category": [ "RAPTOR" ], "restricts_gear": [ "mouth" ], - "social_modifiers": { "intimidate": 20 }, - "attacks": { - "attack_text_u": "You dart your head and snap at %s!", - "attack_text_npc": "%1$s darts their head and snaps at %2$s!", - "blocker_mutations": [ "FANGS" ], - "body_part": "mouth", - "chance": 18, - "base_damage": { "damage_type": "stab", "amount": 18 } - } + "social_modifiers": { "intimidate": 20 } }, { "type": "mutation", @@ -5453,7 +5445,7 @@ "visibility": 8, "ugliness": 5, "consume_time_modifier": 0.5, - "description": "A set of tentacles surrounds your mouth. They allow you to eat twice as fast. Slightly decreases wet penalties.", + "description": "A set of tentacles surrounds your mouth. They allow you to eat twice as fast, and if you have a beak, will sometimes help you bite your prey. Slightly decreases wet penalties.", "types": [ "MUZZLE" ], "prereqs": [ "MOUTH_FLAPS" ], "category": [ "CEPHALOPOD" ], @@ -6708,7 +6700,7 @@ "visibility": 5, "ugliness": 6, "mixed_effect": true, - "description": "Your face resembles that of a bull, with a significant snout. It looks fearsome but prevents wearing mouthgear.", + "description": "Your face resembles that of a bull, with a significant snout. It looks fearsome but makes it difficult to find gear that covers your face.", "types": [ "MUZZLE" ], "prereqs": [ "SNOUT" ], "category": [ "CATTLE" ], @@ -6723,20 +6715,12 @@ "visibility": 5, "ugliness": 4, "mixed_effect": true, - "description": "Your jaw and nose have extended into a wolfish muzzle. It lends itself to biting in combat and looks impressive, but prevents wearing mouthgear.", + "description": "Your jaw and nose have extended into a wolfish muzzle, making it difficult to find masks that fit. If you have a set of fangs, you'll be able to trip enemies in combat by biting them.", "types": [ "MUZZLE" ], "prereqs": [ "SNOUT" ], "category": [ "BEAST", "LUPINE" ], "restricts_gear": [ "mouth" ], - "social_modifiers": { "intimidate": 6 }, - "attacks": { - "attack_text_u": "You nip at %s!", - "attack_text_npc": "%1$s nips and harries %2$s!", - "blocker_mutations": [ "FANGS" ], - "body_part": "mouth", - "chance": 18, - "base_damage": { "damage_type": "cut", "amount": 4 } - } + "social_modifiers": { "intimidate": 6 } }, { "type": "mutation", @@ -6746,19 +6730,11 @@ "visibility": 5, "ugliness": 4, "mixed_effect": true, - "description": "Your jaw and nose have extended into a bearish muzzle. You could bite with it, and it looks impressive, but it prevents wearing mouthgear.", + "description": "Your jaw and nose have extended into a bearish muzzle, which interferes with most face-covering gear. If you also have a set of fangs, you'll be able to maul downed enemies with your powerful jaws.", "types": [ "MUZZLE" ], "prereqs": [ "SNOUT" ], "category": [ "URSINE" ], - "restricts_gear": [ "mouth" ], - "attacks": { - "attack_text_u": "You bite %s!", - "attack_text_npc": "%1$s bites %2$s!", - "blocker_mutations": [ "FANGS" ], - "body_part": "mouth", - "chance": 20, - "base_damage": { "damage_type": "cut", "amount": 5 } - } + "restricts_gear": [ "mouth" ] }, { "type": "mutation", @@ -6767,7 +6743,7 @@ "points": -2, "visibility": 6, "ugliness": 4, - "description": "Your face and jaw have extended, giving you an alert and attentive appearance.", + "description": "Your face and jaw have extended, giving you an alert and attentive appearance. If you have a pair of matching incisors, you can bite much faster with them than any human ever could.", "types": [ "MUZZLE" ], "prereqs": [ "SNOUT" ], "category": [ "RAT", "MOUSE" ], @@ -6781,20 +6757,12 @@ "visibility": 8, "ugliness": 8, "mixed_effect": true, - "description": "Your face and jaws are a shorter version of those found on alligators. They look NASTY--as do the bite wounds they can inflict--but prevent wearing mouthgear.", + "description": "You have a long, powerful set of jaws, like a monitor or a crocodilian. This makes it hard to find a mask that fits, but with a set of fangs, you could really do some damage.", "types": [ "MUZZLE" ], "prereqs": [ "SNOUT" ], "category": [ "LIZARD" ], "restricts_gear": [ "mouth" ], - "social_modifiers": { "intimidate": 20 }, - "attacks": { - "attack_text_u": "You bite a chunk out of %s!", - "attack_text_npc": "%1$s bites a chunk out of %2$s!", - "blocker_mutations": [ "FANGS" ], - "body_part": "mouth", - "chance": 18, - "base_damage": { "damage_type": "stab", "amount": 18 } - } + "social_modifiers": { "intimidate": 20 } }, { "type": "mutation", @@ -6804,7 +6772,7 @@ "visibility": 8, "ugliness": 8, "mixed_effect": true, - "description": "Your face and jaws have widened like a frogs. It looks hideous but it allows you to eat food whole.", + "description": "You have an unsettlingly wide mouth, like a frog. This looks hideous, but allows you to swallow your food whole.", "types": [ "MUZZLE" ], "category": [ "BATRACHIAN" ], "wet_protection": [ { "part": "mouth", "good": 6 } ], @@ -7297,7 +7265,8 @@ "ugliness": 4, "mixed_effect": true, "description": "You have a beak for a mouth, and it's hard to find gear that fits over it. If you keep it uncovered, you will sometimes use it to bite in combat. Slightly reduces wet effects.", - "types": [ "TEETH", "MUZZLE" ], + "types": [ "TEETH" ], + "cancels": [ "MUZZLE", "MUZZLE_RAT", "MUZZLE_BEAR" ], "changes_to": [ "BEAK_HUM", "BEAK_PECK" ], "category": [ "BIRD", "CEPHALOPOD" ], "wet_protection": [ { "part": "mouth", "ignored": 1 } ], @@ -7314,8 +7283,8 @@ "ugliness": 5, "description": "Pecking at prey is part of your daily routine now. Slightly reduces wet effects.", "purifiable": false, - "types": [ "TEETH", "MUZZLE" ], - "cancels": [ "MOUTH_TENTACLES" ], + "types": [ "TEETH" ], + "cancels": [ "MOUTH_TENTACLES", "MUZZLE", "MUZZLE_RAT", "MUZZLE_LONG", "MUZZLE_RAPTOR", "MUZZLE_BEAR" ], "prereqs": [ "BEAK" ], "threshreq": [ "THRESH_BIRD" ], "category": [ "BIRD" ], @@ -7342,7 +7311,7 @@ "consume_time_modifier": 1.8, "description": "Though your beak's not suitable for pecking, those flowers out there are a good source of energy. Examine them to feed.", "purifiable": false, - "types": [ "TEETH", "MUZZLE" ], + "types": [ "TEETH" ], "cancels": [ "MOUTH_TENTACLES" ], "prereqs": [ "BEAK" ], "threshreq": [ "THRESH_BIRD" ], diff --git a/data/json/techniques.json b/data/json/techniques.json index 20f0557b02d61..5956b9f7bf1be 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3953,6 +3953,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ + { "u_has_trait": "MUZZLE_BEAR" }, { "or": [ { "npc_has_effect": "downed" }, { "and": [ { "npc_has_effect": "stunned" } ] } ] }, { "not": { "u_has_effect": "natural_stance" } }, { @@ -3988,7 +3989,9 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + { "u_has_trait": "MUZZLE_BEAR" }, + { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] }, + { "or": [ { "npc_has_effect": "downed" }, { "and": [ { "npc_has_effect": "stunned" } ] } ] } ] }, "flat_bonuses": [ @@ -4014,6 +4017,11 @@ "attack_override": true, "crit_tec": true, "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "u_has_trait": "MUZZLE_BEAR" }, + { "or": [ { "npc_has_effect": "downed" }, { "and": [ { "npc_has_effect": "stunned" } ] } ] } + ], "flat_bonuses": [ { "stat": "damage", "type": "stab", "scale": 38 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, @@ -4040,6 +4048,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ + { "u_has_trait": "MUZZLE_RAT" }, { "not": { "u_has_effect": "natural_stance" } }, { "and": [ @@ -4074,6 +4083,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ + { "u_has_trait": "MUZZLE_RAT" }, { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } ] }, From a9c58c0371cbdb1659f06b8c57f0e7fcf811cd45 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 19 Jan 2024 08:23:08 -0800 Subject: [PATCH 031/202] tech stuff --- data/json/techniques.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/data/json/techniques.json b/data/json/techniques.json index 5956b9f7bf1be..b46d5221d1f89 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3870,6 +3870,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ + { "u_has_trait": "MUZZLE_LONG" }, { "not": { "u_has_effect": "natural_stance" } }, { "and": [ @@ -3903,6 +3904,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ + { "u_has_trait": "MUZZLE_LONG" }, { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } ] }, @@ -3928,6 +3930,11 @@ "attack_override": true, "crit_tec": true, "attack_vectors": [ "MOUTH" ], + "condition": { + "and": [ + { "u_has_trait": "MUZZLE_LONG" } + ] + }, "flat_bonuses": [ { "stat": "damage", "type": "stab", "scale": 20 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.4 }, @@ -4511,7 +4518,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + { "or": [ { "u_has_effect": "natural_stance" }, { "u_is_underwater": "true" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } ] }, "flat_bonuses": [ From 27605706b2ed1f612359deec3017d70477829c68 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 19 Jan 2024 08:56:05 -0800 Subject: [PATCH 032/202] vectors --- data/json/techniques.json | 15 +++++++++++---- src/martialarts.cpp | 5 +++-- src/melee.cpp | 2 ++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/data/json/techniques.json b/data/json/techniques.json index b46d5221d1f89..665635cc0ea63 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3677,6 +3677,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ + { "u_has_trait": "MUZZLE" }, { "math": [ "u_val('size') + 1", ">=", "n_val('size')" ] }, { "not": { "u_has_effect": "natural_stance" } }, { "not": { "npc_has_effect": "downed" } }, @@ -3714,6 +3715,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ + { "u_has_trait": "MUZZLE" }, { "math": [ "u_val('size') + 1", ">=", "n_val('size')" ] }, { "u_has_effect": "natural_stance" }, { "not": { "npc_has_effect": "downed" } }, @@ -3751,6 +3753,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ + { "u_has_trait": "MUZZLE" }, { "math": [ "u_val('size') + 1", ">=", "n_val('size')" ] }, { "not": { "npc_has_effect": "downed" } }, { "or": [ { "npc_bodytype": "human" }, { "npc_bodytype": "angel" }, { "npc_bodytype": "bear" }, { "npc_bodytype": "kangoroo" }, { "npc_bodytype": "horse" }, { "npc_bodytype": "pig" }, { "npc_bodytype": "migo" }, { "npc_bodytype": "flying_insect" }, { "npc_bodytype": "crab" }, { "npc_bodytype": "spider" }, { "npc_bodytype": "bird" } ] }, @@ -3787,6 +3790,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ + { "u_has_trait": "MUZZLE_RAPTOR" }, { "not": { "u_has_effect": "natural_stance" } }, { "and": [ @@ -3820,6 +3824,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ + { "u_has_trait": "MUZZLE_RAPTOR" }, { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } ] }, @@ -3845,6 +3850,9 @@ "attack_override": true, "crit_tec": true, "attack_vectors": [ "MOUTH" ], + "and": [ + { "u_has_trait": "MUZZLE_RAPTOR" } + ], "flat_bonuses": [ { "stat": "damage", "type": "cut", "scale": 16 }, { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, @@ -4028,7 +4036,7 @@ "and": [ { "u_has_trait": "MUZZLE_BEAR" }, { "or": [ { "npc_has_effect": "downed" }, { "and": [ { "npc_has_effect": "stunned" } ] } ] } - ], + ] }, "flat_bonuses": [ { "stat": "damage", "type": "stab", "scale": 38 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, @@ -4507,7 +4515,7 @@ { "type": "technique", "id": "MOUTH_TENTACLES_BITE_NATURAL", - "name": "Beak Bite", + "name": "Mouth Tentacles Bite", "melee_allowed": true, "messages": [ "You grab %s with your tentacles and bite down hard!", " grabs %s with their tentacles and bites down hard!" ], "unarmed_allowed": true, @@ -4541,7 +4549,7 @@ { "type": "technique", "id": "MOUTH_TENTACLES_BITE_CRIT", - "name": "Incisor Bite (crit)", + "name": "Mouth Tentacles Bite (crit)", "melee_allowed": true, "messages": [ "You grab %s with your tentacles and deliver a vicious bite!", " grabs %s with their tentacles and delivers a vicious bite!" ], "unarmed_allowed": true, @@ -4652,7 +4660,6 @@ "attack_override": true, "crit_tec": true, "attack_vectors": [ "MOUTH" ], - "flat_bonuses": [ { "stat": "damage", "type": "cut", "scale": 24 }, { "stat": "damage", "type": "bash", "scale": 14 }, diff --git a/src/martialarts.cpp b/src/martialarts.cpp index 4c6a87819c4f4..de18a9e46cfda 100644 --- a/src/martialarts.cpp +++ b/src/martialarts.cpp @@ -1391,14 +1391,15 @@ bool character_martial_arts::can_use_attack_vector( const Character &user, bool healthy_arm = arm_r_hp > 0 || arm_l_hp > 0; bool healthy_arms = arm_r_hp > 0 && arm_l_hp > 0; bool healthy_legs = leg_r_hp > 0 && leg_l_hp > 0; - bool always_ok = av == "HEAD" || av == "MOUTH" || av == "TORSO"; + bool mouth_ok = ( av == "MOUTH" ) && !natural_attack_restricted_on( bodypart_id( "mouth" ) ); + bool always_ok = av == "HEAD" || av == "TORSO"; bool weapon_ok = av == "WEAPON" && valid_weapon && healthy_arm; bool arm_ok = ( av == "HAND" || av == "FINGER" || av == "WRIST" || av == "ARM" || av == "ELBOW" || av == "HAND_BACK" || av == "PALM" || av == "SHOULDER" ) && healthy_arm; bool arms_ok = ( av == "GRAPPLE" || av == "THROW" ) && healthy_arms; bool legs_ok = ( av == "FOOT" || av == "LOWER_LEG" || av == "KNEE" || av == "HIP" ) && healthy_legs; - return always_ok || weapon_ok || arm_ok || arms_ok || legs_ok; + return always_ok || weapon_ok || mouth_ok || arm_ok || arms_ok || legs_ok; } bool character_martial_arts::can_leg_block( const Character &owner ) const diff --git a/src/melee.cpp b/src/melee.cpp index 2f8a194e3eaa1..084f17ae72d66 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -1296,6 +1296,8 @@ static void roll_melee_damage_internal( const Character &u, const damage_type_id ( !u.natural_attack_restricted_on( sub_bodypart_id( "leg_hip_r" ) ) ); } else if( attack_vector == "HEAD" ) { bp_unrestricted = !u.natural_attack_restricted_on( bodypart_id( "head" ) ); + } else if( attack_vector == "MOUTH" ) { + bp_unrestricted = !u.natural_attack_restricted_on( bodypart_id( "mouth" ) ); } else if( attack_vector == "TORSO" ) { bp_unrestricted = !u.natural_attack_restricted_on( bodypart_id( "torso" ) ); } else { From 01edcc7d14cdeac6ba2941392890896a17fbde58 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 19 Jan 2024 08:57:35 -0800 Subject: [PATCH 033/202] Update MARTIALART_JSON.md --- doc/MARTIALART_JSON.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/MARTIALART_JSON.md b/doc/MARTIALART_JSON.md index 0b54a3170335b..fe5cfd01f8455 100644 --- a/doc/MARTIALART_JSON.md +++ b/doc/MARTIALART_JSON.md @@ -145,7 +145,7 @@ List of attack vectors is currently hardcoded, and contain: - `WEAPON` - Any technique the requires a held item to perform (see any weapon style). Can be used if the user is holding a valid style weapon for their martial art and at least one hand/arm is not broken. - `THROW` - Any technique that forcefully moves an opponent (judo throws, suplex). Can be used only if both hands/arms are not broken. - `GRAPPLE` - Any technique that maintains contact with an opponent and squeezes (chock, headlock), bends (Krav Maga's Arm Breaker), or twists (arm twist) some part of the opponent. Can be used only if both hands/arms are not broken. -- `MOUTH` - Any technique that uses the mouth, such as biting or spitting acid. Can be used if the mouth is uncovered. Integrated items and personal auras do not count as cover for determining eligibility of this attack vector. +- `MOUTH` - Any technique that uses the mouth, such as biting or spitting acid. Can be used if the mouth is uncovered. Items flagged with ALLOWS_NATURAL_ATTACKS are ignored when checking for coverage. ### Tech effects ```C++ From 4a48e56736a66f4c05a9ac5520fea64080c9a8a2 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:36:54 -0800 Subject: [PATCH 034/202] sonar --- data/json/mutations/mutations.json | 13 +++++++------ data/json/techniques.json | 10 ++++++---- src/character.cpp | 21 +++++++++++++++++++++ src/character.h | 3 +++ src/martialarts.cpp | 2 +- src/mutation.cpp | 3 +++ src/sounds.cpp | 8 ++++++-- src/sounds.h | 1 + 8 files changed, 48 insertions(+), 13 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 330cd3f4ff82d..14280439b5760 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -475,27 +475,28 @@ "id": "BATEARS", "name": { "str": "Bat Ears" }, "points": 1, - "description": "Your broad, pointed ears are like radar dishes. You can hear even the slightest sounds at great distance.", + "description": "Your broad, pointed ears are like radar dishes. You can hear even the slightest sounds at great distance, and most loud noises don't seem to bother you.", "types": [ "HEARING" ], "prereqs": [ "GOODHEARING" ], "category": [ "CHIROPTERAN" ], - "hearing_modifier": 2.0 + "hearing_modifier": 2.5 }, { "type": "mutation", "id": "ECHOLOCATION", "name": { "str": "Echolocation" }, "points": 3, - "description": "You've learned how to hunt without sight. As long as you're awake and able to hear, you'll never suffer penalties for fighting enemies you can't see.", + "description": "You've learned how to hunt without sight. Activate this mutation to make an ultrasonic sound that can reveal obstacles and enemies nearby.", "prereqs": [ "BATEARS" ], - "category": [ "CHIROPTERAN" ] + "category": [ "CHIROPTERAN" ], + "active": true }, { "type": "mutation", "id": "LEAFNOSE", "name": { "str": "Leaf Nose" }, "points": 2, - "prereqs": [ "SLIT_NOSTRILS" ], + "prereqs": [ "SLIT_NOSTRILS", "BLOODFEEDER" ], "flags": [ "INFRARED" ], "category": [ "CHIROPTERAN" ], "ugliness": 3, @@ -5477,7 +5478,7 @@ "points": 2, "visibility": 6, "ugliness": 6, - "description": "You have developed deadly extensible fangs, allowing you to bite quickly or ensure your venom goes home, as desired. Just make sure you keep them uncovered if you intend to use them.", + "description": "You have developed deadly extensible fangs, allowing you to bite quickly or ensure your venom goes home, especially if your victim is caught in a web. Just make sure you keep them uncovered if you intend to use them.", "types": [ "TEETH", "MUZZLE" ], "cancels": [ "MOUTH_TENTACLES" ], "prereqs": [ "MANDIBLES" ], diff --git a/data/json/techniques.json b/data/json/techniques.json index 665635cc0ea63..c6e1aaa28fc34 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3850,9 +3850,11 @@ "attack_override": true, "crit_tec": true, "attack_vectors": [ "MOUTH" ], - "and": [ - { "u_has_trait": "MUZZLE_RAPTOR" } - ], + "condition": { + "and": [ + { "u_has_trait": "MUZZLE_RAPTOR" } + ] + }, "flat_bonuses": [ { "stat": "damage", "type": "cut", "scale": 16 }, { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, @@ -4724,7 +4726,7 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + { "or": [ { "u_has_effect": "natural_stance" }, { "npc_has_effect": "webbed" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } ] }, "flat_bonuses": [ diff --git a/src/character.cpp b/src/character.cpp index badfb037ee30b..75cf6fec89c80 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10554,6 +10554,27 @@ std::vector Character::get_hostile_creatures( int range ) const } ); } +void Character::echo_pulse() +{ + map &here = get_map(); + sounds::sound( this->pos(), 25, sounds::sound_t::sensory, _( "click" ), true, + "none", "none" ); + for( tripoint origin : points_in_radius( pos(), 20 ) ) { + if ( here.impassable( origin ) && here.pl_line_of_sight( origin, 20 ) ) { + sounds::sound( origin, 25, sounds::sound_t::sensory, _( "click" ), true, + "none", "none" ); + + } + creature_tracker &creatures = get_creature_tracker(); + if ( creatures.creature_at( origin, true ) && here.pl_line_of_sight( origin, 20 ) ) { + sounds::sound( origin, 25, sounds::sound_t::sensory, _( "chk" ), false, + "none", "none" ); + } + } + // sounds::process_sounds(); + // sounds::process_sound_markers( this ); +} + bool Character::knows_trap( const tripoint &pos ) const { const tripoint p = get_map().getabs( pos ); diff --git a/src/character.h b/src/character.h index 30718348eb716..4c7c8fdb1621f 100644 --- a/src/character.h +++ b/src/character.h @@ -3652,6 +3652,9 @@ class Character : public Creature, public visitable /** Creates an auditory hallucination */ void sound_hallu(); + /** All nearby obstacles make a very quiet sound */ + void echo_pulse(); + /** Checks if a Character is driving */ bool is_driving() const; diff --git a/src/martialarts.cpp b/src/martialarts.cpp index de18a9e46cfda..25d2130530a6e 100644 --- a/src/martialarts.cpp +++ b/src/martialarts.cpp @@ -1391,7 +1391,7 @@ bool character_martial_arts::can_use_attack_vector( const Character &user, bool healthy_arm = arm_r_hp > 0 || arm_l_hp > 0; bool healthy_arms = arm_r_hp > 0 && arm_l_hp > 0; bool healthy_legs = leg_r_hp > 0 && leg_l_hp > 0; - bool mouth_ok = ( av == "MOUTH" ) && !natural_attack_restricted_on( bodypart_id( "mouth" ) ); + bool mouth_ok = ( av == "MOUTH" ) && !user.natural_attack_restricted_on( bodypart_id( "mouth" ) ); bool always_ok = av == "HEAD" || av == "TORSO"; bool weapon_ok = av == "WEAPON" && valid_weapon && healthy_arm; bool arm_ok = ( av == "HAND" || av == "FINGER" || av == "WRIST" || av == "ARM" || av == "ELBOW" || diff --git a/src/mutation.cpp b/src/mutation.cpp index 72459ee3c84f1..4c5609108f583 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -70,6 +70,7 @@ static const trait_id trait_BURROW( "BURROW" ); static const trait_id trait_BURROWLARGE( "BURROWLARGE" ); static const trait_id trait_CHAOTIC_BAD( "CHAOTIC_BAD" ); static const trait_id trait_DEX_ALPHA( "DEX_ALPHA" ); +static const trait_id trait_ECHOLOCATION( "ECHOLOCATION" ); static const trait_id trait_GASTROPOD_EXTREMITY2( "GASTROPOD_EXTREMITY2" ); static const trait_id trait_GASTROPOD_EXTREMITY3( "GASTROPOD_EXTREMITY3" ); static const trait_id trait_GLASSJAW( "GLASSJAW" ); @@ -876,6 +877,8 @@ void Character::activate_mutation( const trait_id &mut ) blossoms(); tdata.powered = false; return; + } else if( mut == trait_ECHOLOCATION ) { + echo_pulse(); } else if( mut == trait_TREE_COMMUNION || mut == trait_ARVORE_FOREST_MAPPING ) { tdata.powered = false; // Check for adjacent trees. diff --git a/src/sounds.cpp b/src/sounds.cpp index fe296333eaf63..24a804f600a56 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -218,6 +218,7 @@ std::string enum_to_string( sounds::sound_t data ) switch ( data ) { case sounds::sound_t::background: return "background"; case sounds::sound_t::weather: return "weather"; + case sounds::sound_t::sensory: return "sensory"; case sounds::sound_t::music: return "music"; case sounds::sound_t::movement: return "movement"; case sounds::sound_t::speech: return "speech"; @@ -288,6 +289,7 @@ static bool is_provocative( sounds::sound_t category ) switch( category ) { case sounds::sound_t::background: case sounds::sound_t::weather: + case sounds::sound_t::sensory: case sounds::sound_t::music: case sounds::sound_t::activity: case sounds::sound_t::destructive_activity: @@ -509,6 +511,7 @@ static bool describe_sound( sounds::sound_t category, bool from_player_position return false; case sounds::sound_t::background: case sounds::sound_t::weather: + case sounds::sound_t::sensory: case sounds::sound_t::music: // detailed music descriptions are printed in iuse::play_music case sounds::sound_t::movement: @@ -527,6 +530,7 @@ static bool describe_sound( sounds::sound_t category, bool from_player_position switch( category ) { case sounds::sound_t::background: case sounds::sound_t::weather: + case sounds::sound_t::sensory: case sounds::sound_t::music: case sounds::sound_t::movement: case sounds::sound_t::activity: @@ -707,8 +711,8 @@ void sounds::process_sound_markers( Character *you ) } // Place footstep markers. - if( pos == you->pos() || you->sees( pos ) ) { - // If we are or can see the source, don't draw a marker. + if( pos == you->pos() || ( you->sees( pos ) && ( sound.category != sound_t::sensory ) ) ) { + // If we are or can see the source, don't draw a marker, except for sonar etc continue; } diff --git a/src/sounds.h b/src/sounds.h index cf4d164ae76a0..3975683d76b3f 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -23,6 +23,7 @@ namespace sounds enum class sound_t : int { background = 0, weather, + sensory, // Sonar etc. Ensures this sound will usually get a visual marker so the player can see it. music, movement, speech, From 47b632289ae67b1075557a9695839eb35a6fbe22 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 20 Jan 2024 04:50:12 -0800 Subject: [PATCH 035/202] remove extra fang stuff for later PR --- data/json/items/armor/integrated.json | 88 +- data/json/mutations/mutations.json | 117 ++- data/json/techniques.json | 1131 +------------------------ 3 files changed, 112 insertions(+), 1224 deletions(-) diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index ba09b7faa8c33..b99fed6524350 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -547,32 +547,6 @@ } ] }, - { - "id": "integrated_beak", - "type": "ARMOR", - "category": "armor", - "name": { "str_sp": "beak" }, - "description": "Parts of the upper and lower jaws have been replaced by a beak hard enough to crack walnuts and sharp enough to slice through flesh.", - "weight": "200 g", - "volume": "200 ml", - "price": 0, - "price_postapoc": 0, - "material": [ "chitin" ], - "symbol": ";", - "color": "black", - "warmth": 2, - "qualities": [ [ "CUT", 1 ], [ "BUTCHER", 4 ], [ "HAMMER", 1 ] ], - "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "SKINTIGHT", "PADDED", "NO_SALVAGE", "PROVIDES_TECHNIQUES" ], - "techniques": [ "BEAK_BITE", "BEAK_BITE_NATURAL", "BEAK_BITE_CRIT", "MOUTH_TENTACLES_BITE", "MOUTH_TENTACLES_BITE_NATURAL", "MOUTH_TENTACLES_BITE_CRIT" ], - "armor": [ - { - "material": [ { "type": "chitin", "covered_by_mat": 100, "thickness": 5 } ], - "covers": [ "mouth" ], - "coverage": 60, - "encumbrance": 2 - } - ] - }, { "id": "integrated_mandibles", "type": "ARMOR", @@ -588,8 +562,7 @@ "color": "dark_gray", "warmth": 2, "qualities": [ [ "CUT", 1 ], [ "BUTCHER", 4 ] ], - "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "SKINTIGHT", "PADDED", "NO_SALVAGE", "PROVIDES_TECHNIQUES" ], - "techniques": [ "MANDIBLES_BITE", "MANDIBLES_BITE_NATURAL", "MANDIBLES_BITE_CRIT" ], + "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "SKINTIGHT", "PADDED", "NO_SALVAGE" ], "armor": [ { "material": [ { "type": "chitin", "covered_by_mat": 100, "thickness": 3 } ], @@ -613,8 +586,7 @@ "symbol": ";", "color": "brown", "warmth": 2, - "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "WATER_FRIENDLY", "PADDED", "PROVIDES_TECHNIQUES" ], - "techniques": [ "FOLDING_FANGS_BITE", "FOLDING_FANGS_BITE_NATURAL", "FOLDING_FANGS_BITE_CRIT" ], + "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "WATER_FRIENDLY", "PADDED" ], "armor": [ { "material": [ { "type": "sclerotin", "covered_by_mat": 100, "thickness": 3 } ], @@ -1640,34 +1612,6 @@ ], "melee_damage": { "stab": 16 } }, - { - "id": "integrated_sharkteeth", - "type": "ARMOR", - "category": "armor", - "name": { "str_sp": "shark teeth" }, - "description": "A mouth full of multiple rows of very intimidating teeth.", - "weight": "100 g", - "volume": "112 ml", - "price": 0, - "price_postapoc": 0, - "material": [ "bone" ], - "symbol": ",", - "color": "white", - "warmth": 5, - "to_hit": 1, - "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], - "techniques": [ "SHARKTEETH_BITE", "SHARKTEETH_BITE_NATURAL", "SHARKTEETH_BITE_CRIT" ], - "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], - "armor": [ - { - "material": [ { "type": "bone", "covered_by_mat": 100, "thickness": 3.2 } ], - "covers": [ "mouth" ], - "coverage": 0, - "encumbrance": 0 - } - ], - "melee_damage": { "stab": 16 } - }, { "id": "integrated_vampire_fangs", "type": "ARMOR", @@ -1695,33 +1639,5 @@ } ], "melee_damage": { "stab": 20 } - }, - { - "id": "integrated_incisors", - "type": "ARMOR", - "category": "armor", - "name": { "str_sp": "incisors" }, - "description": "A long, sturdy pair of incisors. No matter how much you grind them down, they just keep growing.", - "weight": "112 g", - "volume": "125 ml", - "price": 0, - "price_postapoc": 0, - "material": [ "bone" ], - "symbol": ",", - "color": "white", - "warmth": 5, - "to_hit": 1, - "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ], [ "CHISEL_WOOD", 1 ] ], - "techniques": [ "INCISORS_BITE", "INCISORS_BITE_NATURAL", "INCISORS_BITE_CRIT", "MUZZLE_RAT_BITE", "MUZZLE_RAT_BITE_NATURAL" ], - "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], - "armor": [ - { - "material": [ { "type": "bone", "covered_by_mat": 100, "thickness": 3.2 } ], - "covers": [ "mouth" ], - "coverage": 0, - "encumbrance": 0 - } - ], - "melee_damage": { "stab": 20 } } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 14280439b5760..d663d0b428d43 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -2470,12 +2470,18 @@ "points": 2, "visibility": 3, "ugliness": 3, - "description": "Your big teeth have grown in. Folks had best show some more respect, because as long as your mouth is uncovered, you'll sometimes deliver a vicious bite in combat.", + "description": "Your big teeth have grown in. Folks had best show some more respect.", "types": [ "TEETH" ], "prereqs": [ "MUZZLE_RAT", "RABBIT_NOSE" ], "threshreq": [ "THRESH_RAT", "THRESH_RABBIT" ], "category": [ "RAT", "RABBIT" ], - "integrated_armor": [ "integrated_incisors" ] + "attacks": { + "attack_text_u": "You bite into %s with your ratlike incisors!", + "attack_text_npc": "%1$s bites %2$s with their ratlike incisors!", + "body_part": "mouth", + "chance": 18, + "base_damage": [ { "damage_type": "cut", "amount": 3 }, { "damage_type": "bash", "amount": 3 } ] + } }, { "type": "mutation", @@ -4729,7 +4735,14 @@ "restricts_gear": [ "mouth" ], "destroys_gear": true, "social_modifiers": { "intimidate": 15 }, - "integrated_armor": [ "integrated_saber_teeth" ] + "attacks": { + "attack_text_u": "You tear into %s with your saber teeth!", + "attack_text_npc": "%1$s tears into %2$s with their saber teeth!", + "body_part": "mouth", + "chance": 20, + "base_damage": { "damage_type": "stab", "amount": 25 }, + "strength_damage": { "damage_type": "stab", "amount": 1 } + } }, { "type": "mutation", @@ -4980,12 +4993,20 @@ "visibility": 8, "ugliness": 8, "mixed_effect": true, - "description": "Your face has drastically mutated, with the bones of your jaws having extended forth into a long, tapered snout. The palate of your mouth is considerably vaulted, with curved, serrated teeth lining your saurian maw. While modern-day gas masks mostly don't account for paleobiology, if you manage to evolve a set of fangs, you will bite with the ferocity of long-dead beasts.", + "description": "Your face has drastically mutated, with the bones of your jaws having extended forth into a long, tapered snout. The palate of your mouth is considerably vaulted, with curved, serrated teeth lining your saurian maw and reptilian scales covering your muzzle’s exterior. While paleobiology has evidently not evolved with mouth gear in mind (making such items unwearable), the bite wounds you can now inflict promise the ferocity of long-dead beasts.", "types": [ "MUZZLE" ], "prereqs": [ "SNOUT" ], "category": [ "RAPTOR" ], "restricts_gear": [ "mouth" ], - "social_modifiers": { "intimidate": 20 } + "social_modifiers": { "intimidate": 20 }, + "attacks": { + "attack_text_u": "You dart your head and snap at %s!", + "attack_text_npc": "%1$s darts their head and snaps at %2$s!", + "blocker_mutations": [ "FANGS" ], + "body_part": "mouth", + "chance": 18, + "base_damage": { "damage_type": "stab", "amount": 18 } + } }, { "type": "mutation", @@ -5446,7 +5467,7 @@ "visibility": 8, "ugliness": 5, "consume_time_modifier": 0.5, - "description": "A set of tentacles surrounds your mouth. They allow you to eat twice as fast, and if you have a beak, will sometimes help you bite your prey. Slightly decreases wet penalties.", + "description": "A set of tentacles surrounds your mouth. They allow you to eat twice as fast. Slightly decreases wet penalties.", "types": [ "MUZZLE" ], "prereqs": [ "MOUTH_FLAPS" ], "category": [ "CEPHALOPOD" ], @@ -5462,14 +5483,22 @@ "butchering_quality": 4, "consume_time_modifier": 0.5, "mixed_effect": true, - "description": "A set of insect-like mandibles have grown around your mouth. They allow you to eat faster and, if uncovered, will allow you to sometimes bite your enemies. Slightly reduces wet effects.", + "description": "A set of insect-like mandibles have grown around your mouth. They allow you to eat faster and provide a slicing unarmed attack. Slightly reduces wet effects.", "types": [ "TEETH", "MUZZLE" ], "prereqs": [ "MOUTH_FLAPS" ], "changes_to": [ "FANGS_SPIDER" ], "leads_to": [ "PROBOSCIS" ], "category": [ "INSECT", "SPIDER" ], "wet_protection": [ { "part": "mouth", "ignored": 1 } ], - "integrated_armor": [ "integrated_mandibles" ] + "integrated_armor": [ "integrated_mandibles" ], + "attacks": { + "attack_text_u": "You bite %s with your fangs!", + "attack_text_npc": "%1$s bites %2$s with their fangs!", + "blocker_mutations": [ "FANGS_SPIDER" ], + "body_part": "mouth", + "chance": 22, + "base_damage": { "damage_type": "cut", "amount": 12 } + } }, { "type": "mutation", @@ -5478,7 +5507,7 @@ "points": 2, "visibility": 6, "ugliness": 6, - "description": "You have developed deadly extensible fangs, allowing you to bite quickly or ensure your venom goes home, especially if your victim is caught in a web. Just make sure you keep them uncovered if you intend to use them.", + "description": "You have developed deadly extensible fangs, allowing you to bite quickly or ensure your venom goes home, as desired.", "types": [ "TEETH", "MUZZLE" ], "cancels": [ "MOUTH_TENTACLES" ], "prereqs": [ "MANDIBLES" ], @@ -5486,7 +5515,15 @@ "category": [ "SPIDER" ], "consume_time_modifier": 2.0, "wet_protection": [ { "part": "mouth", "ignored": 1 } ], - "integrated_armor": [ "integrated_fangs_spider" ] + "integrated_armor": [ "integrated_fangs_spider" ], + "attacks": { + "attack_text_u": "You bite %s with your fangs!", + "attack_text_npc": "%1$s bites %2$s with their fangs!", + "blocker_mutations": [ "MANDIBLES" ], + "body_part": "mouth", + "chance": 22, + "base_damage": { "damage_type": "stab", "amount": 15 } + } }, { "type": "mutation", @@ -6716,12 +6753,20 @@ "visibility": 5, "ugliness": 4, "mixed_effect": true, - "description": "Your jaw and nose have extended into a wolfish muzzle, making it difficult to find masks that fit. If you have a set of fangs, you'll be able to trip enemies in combat by biting them.", + "description": "Your jaw and nose have extended into a wolfish muzzle. It lends itself to biting in combat and looks impressive, but prevents wearing mouthgear.", "types": [ "MUZZLE" ], "prereqs": [ "SNOUT" ], "category": [ "BEAST", "LUPINE" ], "restricts_gear": [ "mouth" ], - "social_modifiers": { "intimidate": 6 } + "social_modifiers": { "intimidate": 6 }, + "attacks": { + "attack_text_u": "You nip at %s!", + "attack_text_npc": "%1$s nips and harries %2$s!", + "blocker_mutations": [ "FANGS" ], + "body_part": "mouth", + "chance": 18, + "base_damage": { "damage_type": "cut", "amount": 4 } + } }, { "type": "mutation", @@ -6731,11 +6776,19 @@ "visibility": 5, "ugliness": 4, "mixed_effect": true, - "description": "Your jaw and nose have extended into a bearish muzzle, which interferes with most face-covering gear. If you also have a set of fangs, you'll be able to maul downed enemies with your powerful jaws.", + "description": "Your jaw and nose have extended into a bearish muzzle. You could bite with it, and it looks impressive, but it prevents wearing mouthgear.", "types": [ "MUZZLE" ], "prereqs": [ "SNOUT" ], "category": [ "URSINE" ], - "restricts_gear": [ "mouth" ] + "restricts_gear": [ "mouth" ], + "attacks": { + "attack_text_u": "You bite %s!", + "attack_text_npc": "%1$s bites %2$s!", + "blocker_mutations": [ "FANGS" ], + "body_part": "mouth", + "chance": 20, + "base_damage": { "damage_type": "cut", "amount": 5 } + } }, { "type": "mutation", @@ -6744,7 +6797,7 @@ "points": -2, "visibility": 6, "ugliness": 4, - "description": "Your face and jaw have extended, giving you an alert and attentive appearance. If you have a pair of matching incisors, you can bite much faster with them than any human ever could.", + "description": "Your face and jaw have extended, giving you an alert and attentive appearance.", "types": [ "MUZZLE" ], "prereqs": [ "SNOUT" ], "category": [ "RAT", "MOUSE" ], @@ -6758,12 +6811,20 @@ "visibility": 8, "ugliness": 8, "mixed_effect": true, - "description": "You have a long, powerful set of jaws, like a monitor or a crocodilian. This makes it hard to find a mask that fits, but with a set of fangs, you could really do some damage.", + "description": "Your face and jaws are a shorter version of those found on alligators. They look NASTY--as do the bite wounds they can inflict--but prevent wearing mouthgear.", "types": [ "MUZZLE" ], "prereqs": [ "SNOUT" ], "category": [ "LIZARD" ], "restricts_gear": [ "mouth" ], - "social_modifiers": { "intimidate": 20 } + "social_modifiers": { "intimidate": 20 }, + "attacks": { + "attack_text_u": "You bite a chunk out of %s!", + "attack_text_npc": "%1$s bites a chunk out of %2$s!", + "blocker_mutations": [ "FANGS" ], + "body_part": "mouth", + "chance": 18, + "base_damage": { "damage_type": "stab", "amount": 18 } + } }, { "type": "mutation", @@ -7265,15 +7326,20 @@ "visibility": 8, "ugliness": 4, "mixed_effect": true, - "description": "You have a beak for a mouth, and it's hard to find gear that fits over it. If you keep it uncovered, you will sometimes use it to bite in combat. Slightly reduces wet effects.", - "types": [ "TEETH" ], - "cancels": [ "MUZZLE", "MUZZLE_RAT", "MUZZLE_BEAR" ], + "description": "You have a beak for a mouth. You can occasionally use it to peck at your enemies, but it is impossible for you to wear mouth gear. Slightly reduces wet effects.", + "types": [ "TEETH", "MUZZLE" ], "changes_to": [ "BEAK_HUM", "BEAK_PECK" ], "category": [ "BIRD", "CEPHALOPOD" ], "wet_protection": [ { "part": "mouth", "ignored": 1 } ], "restricts_gear": [ "mouth" ], "destroys_gear": true, - "integrated_armor": [ "integrated_beak" ] + "attacks": { + "attack_text_u": "You peck %s!", + "attack_text_npc": "%1$s pecks %2$s!", + "body_part": "mouth", + "chance": 15, + "base_damage": { "damage_type": "stab", "amount": 15 } + } }, { "type": "mutation", @@ -8588,7 +8654,14 @@ "category": [ "FISH" ], "threshreq": [ "THRESH_FISH" ], "social_modifiers": { "intimidate": 5 }, - "integrated_armor": [ "integrated_sharkteeth" ], + "attacks": { + "attack_text_u": "You tear into %s with your teeth!", + "attack_text_npc": "%1$s tears into %2$s with their teeth!", + "body_part": "mouth", + "blocker_mutations": [ "MUZZLE", "MUZZLE_LONG", "MUZZLE_RAT" ], + "chance": 20, + "base_damage": { "damage_type": "stab", "amount": 25 } + }, "purifiable": false }, { diff --git a/data/json/techniques.json b/data/json/techniques.json index c6e1aaa28fc34..6876eba1b5231 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3579,720 +3579,10 @@ }, { "type": "technique", - "id": "SABER_TEETH_BITE", - "name": "Saber Teeth Bite", - "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "SABER_TEETH_BITE_NATURAL", - "name": "Saber Teeth Bite", - "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "SABER_TEETH_BITE_CRIT", - "name": "Saber Teeth Bite (crit)", - "melee_allowed": true, - "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 16 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_BITE", - "name": "Muzzle Bite", - "melee_allowed": true, - "messages": [ "You throw %s off balance with a savage bite!", " throws %s off balance with a savage bite!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE" }, - { "math": [ "u_val('size') + 1", ">=", "n_val('size')" ] }, - { "not": { "u_has_effect": "natural_stance" } }, - { "not": { "npc_has_effect": "downed" } }, - { "or": [ { "npc_bodytype": "human" }, { "npc_bodytype": "angel" }, { "npc_bodytype": "bear" }, { "npc_bodytype": "kangoroo" }, { "npc_bodytype": "horse" }, { "npc_bodytype": "pig" }, { "npc_bodytype": "migo" } ] }, - { "or": [ { "not": { "npc_has_flag": "FLIES" } }, { "npc_has_flag": "DISABLE_FLIGHT" } ] }, - { "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] } - ] - }, - "down_dur": 2, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 12 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_BITE_NATURAL", - "name": "Muzzle Bite", - "melee_allowed": true, - "messages": [ "You throw %s off balance with a savage bite!", " throws %s off balance with a savage bite!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE" }, - { "math": [ "u_val('size') + 1", ">=", "n_val('size')" ] }, - { "u_has_effect": "natural_stance" }, - { "not": { "npc_has_effect": "downed" } }, - { "or": [ { "npc_bodytype": "human" }, { "npc_bodytype": "angel" }, { "npc_bodytype": "bear" }, { "npc_bodytype": "kangoroo" }, { "npc_bodytype": "horse" }, { "npc_bodytype": "pig" }, { "npc_bodytype": "migo" } ] }, - { "or": [ { "not": { "npc_has_flag": "FLIES" } }, { "npc_has_flag": "DISABLE_FLIGHT" } ] }, - { "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] } - ] - }, - "down_dur": 2, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 12 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_BITE_CRIT", - "name": "Muzzle Bite (crit)", - "melee_allowed": true, - "messages": [ "You down %s with a ferocious bite!", " downs %s with a ferocious bite!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE" }, - { "math": [ "u_val('size') + 1", ">=", "n_val('size')" ] }, - { "not": { "npc_has_effect": "downed" } }, - { "or": [ { "npc_bodytype": "human" }, { "npc_bodytype": "angel" }, { "npc_bodytype": "bear" }, { "npc_bodytype": "kangoroo" }, { "npc_bodytype": "horse" }, { "npc_bodytype": "pig" }, { "npc_bodytype": "migo" }, { "npc_bodytype": "flying_insect" }, { "npc_bodytype": "crab" }, { "npc_bodytype": "spider" }, { "npc_bodytype": "bird" } ] }, - { "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] } - ] - }, - "down_dur": 2, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 14 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_RAPTOR_BITE", - "name": "Raptor Muzzle Bite", - "melee_allowed": true, - "messages": [ "You rip into %s with your sawlike teeth!", " rips into %s with their sawlike teeth!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE_RAPTOR" }, - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 14 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.2 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_RAPTOR_BITE_NATURAL", - "name": "Raptor Muzzle Bite", - "melee_allowed": true, - "messages": [ "You rip into %s with your sawlike teeth!", " rips into %s with their sawlike teeth!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE_RAPTOR" }, - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 14 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.2 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_RAPTOR_BITE_CRIT", - "name": "Raptor Muzzle Bite (crit)", - "melee_allowed": true, - "messages": [ "You shred %s with your sawlike teeth!", " shreds %s with their sawlike teeth!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE_RAPTOR" } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 16 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.4 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_LONG_BITE", - "name": "Reptilian Muzzle Bite", - "melee_allowed": true, - "messages": [ "You sink your teeth into %s and thrash violently!", " sinks their teeth into %s and thrashes violently!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE_LONG" }, - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 18 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_LONG_BITE_NATURAL", - "name": "Reptilian Muzzle Bite", - "melee_allowed": true, - "messages": [ "You sink your teeth into %s and thrash violently!", " sinks their teeth into %s and thrashes violently!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE_LONG" }, - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 18 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_LONG_BITE_CRIT", - "name": "Reptilian Muzzle Bite (crit)", - "melee_allowed": true, - "messages": [ "You sink your teeth into %s and thrash violently!", " sinks their teeth into %s and thrashes violently!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE_LONG" } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 20 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.4 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.3 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_BEAR_BITE", - "name": "Bear Muzzle Bite", - "melee_allowed": true, - "messages": [ "You maul %s!", " mauls %s!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE_BEAR" }, - { "or": [ { "npc_has_effect": "downed" }, { "and": [ { "npc_has_effect": "stunned" } ] } ] }, - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 32 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 100 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_BEAR_BITE_NATURAL", - "name": "Bear Muzzle Bite", - "melee_allowed": true, - "messages": [ "You maul %s!", " mauls %s!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE_BEAR" }, - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] }, - { "or": [ { "npc_has_effect": "downed" }, { "and": [ { "npc_has_effect": "stunned" } ] } ] } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 32 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 100 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_BEAR_BITE_CRIT", - "name": "Bear Muzzle Bite (crit)", - "melee_allowed": true, - "messages": [ "You maul %s!", " mauls %s!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE_BEAR" }, - { "or": [ { "npc_has_effect": "downed" }, { "and": [ { "npc_has_effect": "stunned" } ] } ] } - ] }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 38 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 100 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_RAT_BITE", - "name": "Rat Muzzle Bite", - "melee_allowed": true, - "messages": [ "You quickly bite %s!", " quickly bites %s!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE_RAT" }, - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 11 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 25 } - ] - }, - { - "type": "technique", - "id": "MUZZLE_RAT_BITE_NATURAL", - "name": "Rat Muzzle Bite", - "melee_allowed": true, - "messages": [ "You quickly bite %s!", " quickly bites %s!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "u_has_trait": "MUZZLE_RAT" }, - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 11 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 25 } - ] - }, - { - "type": "technique", - "id": "VAMPIRE_BITE", - "name": "Vampire Bite", - "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 21 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "VAMPIRE_BITE_NATURAL", - "name": "Vampire Bite", - "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 21 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "VAMPIRE_BITE_CRIT", - "name": "Vampire Bite (crit)", - "melee_allowed": true, - "messages": [ "You sink your fangs deep into %s!", " sinks their fangs deep into %s!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 25 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.4 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.2 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.4 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "INCISORS_BITE", - "name": "Incisor Bite", - "melee_allowed": true, - "messages": [ "You bite %s with your sharp incisors!", " bites %s with their sharp incisors!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 16 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "INCISORS_BITE_NATURAL", - "name": "Incisor Bite", - "melee_allowed": true, - "messages": [ "You bite %s with your sharp incisors!", " bites %s with their sharp incisors!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 16 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "INCISORS_BITE_CRIT", - "name": "Incisor Bite (crit)", - "melee_allowed": true, - "messages": [ "You rip into %s with your incisors!", " rips into %s with their incisors!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 20 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "SHARKTEETH_BITE", - "name": "Shark Teeth Bite", + "id": "VAMPIRE_BITE", + "name": "Vampire Bite", "melee_allowed": true, - "messages": [ "You chomp on %s!", " chomps on %s!" ], + "messages": [ "You bite %s!", " bites %s!" ], "unarmed_allowed": true, "weighting": -5, "reach_ok": false, @@ -4311,318 +3601,22 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 30 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "SHARKTEETH_BITE_NATURAL", - "name": "Shark Teeth Bite", - "melee_allowed": true, - "messages": [ "You chomp on %s!", " chomps on %s!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "u_is_underwater": "true" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 30 }, + { "stat": "damage", "type": "stab", "scale": 21 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "SHARKTEETH_BITE_CRIT", - "name": "Shark Teeth Bite (crit)", - "melee_allowed": true, - "messages": [ "You sink your teeth into %s and thrash violently!", " sinks their teeth into %s and thrashes violently!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 34 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "BEAK_BITE", - "name": "Beak Bite", - "melee_allowed": true, - "messages": [ "You bite %s with your beak!", " bites %s with their beak!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 16 }, - { "stat": "damage", "type": "bash", "scale": 6 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "BEAK_BITE_NATURAL", - "name": "Beak Bite", - "melee_allowed": true, - "messages": [ "You bite %s with your sharp incisors!", " bites %s with their sharp incisors!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 16 }, - { "stat": "damage", "type": "bash", "scale": 6 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } - ] - }, - { - "type": "technique", - "id": "BEAK_BITE_CRIT", - "name": "Incisor Bite (crit)", - "melee_allowed": true, - "messages": [ "You tear into %s with your beak!", " tears into %s with their beak!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 20 }, - { "stat": "damage", "type": "bash", "scale": 8 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.4 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "movecost", "scale": 50 } ] }, { "type": "technique", - "id": "MOUTH_TENTACLES_BITE", - "name": "Mouth Tentacles Bite", - "melee_allowed": true, - "messages": [ "You grab %s with your tentacles and bite down hard!", " grabs %s with their tentacles and bites down hard!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 22 }, - { "stat": "damage", "type": "bash", "scale": 8 }, - { "stat": "hit", "scale": 1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "MOUTH_TENTACLES_BITE_NATURAL", - "name": "Mouth Tentacles Bite", - "melee_allowed": true, - "messages": [ "You grab %s with your tentacles and bite down hard!", " grabs %s with their tentacles and bites down hard!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "u_is_underwater": "true" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 22 }, - { "stat": "damage", "type": "bash", "scale": 8 }, - { "stat": "hit", "scale": 1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "MOUTH_TENTACLES_BITE_CRIT", - "name": "Mouth Tentacles Bite (crit)", - "melee_allowed": true, - "messages": [ "You grab %s with your tentacles and deliver a vicious bite!", " grabs %s with their tentacles and delivers a vicious bite!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 24 }, - { "stat": "damage", "type": "bash", "scale": 10 }, - { "stat": "hit", "scale": 1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.4 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "MANDIBLES_BITE", - "name": "Mandible Bite", - "melee_allowed": true, - "messages": [ "You bite %s with your mandibles!", " bites %s with their mandibles!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 18 }, - { "stat": "damage", "type": "bash", "scale": 10 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "MANDIBLES_BITE_NATURAL", - "name": "Mandible Bite", + "id": "VAMPIRE_BITE_NATURAL", + "name": "Vampire Bite", "melee_allowed": true, - "messages": [ "You bite %s with your mandibles!", " bites %s with their mandibles!" ], + "messages": [ "You bite %s!", " bites %s!" ], "unarmed_allowed": true, "weighting": -2, "reach_ok": false, @@ -4635,117 +3629,22 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 18 }, - { "stat": "damage", "type": "bash", "scale": 10 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 2.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "MANDIBLES_BITE_CRIT", - "name": "Mandible Bite (crit)", - "melee_allowed": true, - "messages": [ "You tear into %s with your mandibles!", " tears into %s with their mandibles!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - "flat_bonuses": [ - { "stat": "damage", "type": "cut", "scale": 24 }, - { "stat": "damage", "type": "bash", "scale": 14 }, - { "stat": "damage", "type": "cut", "scaling-stat": "str", "scale": 0.4 }, - { "stat": "damage", "type": "cut", "scaling-stat": "dex", "scale": 0.3 }, - { "stat": "damage", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "cut", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.3 }, - { "stat": "damage", "type": "bash", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "bash", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "arpen", "type": "cut", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 66 } - ] - }, - { - "type": "technique", - "id": "FOLDING_FANGS_BITE", - "name": "Folding Fangs Bite", - "melee_allowed": true, - "messages": [ "You deliver a quick bite to %s!", " delivers a quick bite to %s!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 20 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 40 } - ] - }, - { - "type": "technique", - "id": "FOLDING_FANGS_BITE_NATURAL", - "name": "Folding Fangs Bite", - "melee_allowed": true, - "messages": [ "You deliver a quick bite to %s!", " delivers a quick bite to %s!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": false, - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "npc_has_effect": "webbed" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scale": 21 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 40 } + { "stat": "movecost", "scale": 50 } ] }, { "type": "technique", - "id": "FOLDING_FANGS_BITE_CRIT", - "name": "Folding Fangs Bite", + "id": "VAMPIRE_BITE_CRIT", + "name": "Vampire Bite (crit)", "melee_allowed": true, - "messages": [ "You plunge your fangs deep into %s!", " plunges their fangs deep into %s!" ], + "messages": [ "You sink your fangs deep into %s!", " sinks their fangs deep into %s!" ], "unarmed_allowed": true, "weighting": -4, "reach_ok": false, @@ -4753,7 +3652,7 @@ "crit_tec": true, "attack_vectors": [ "MOUTH" ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 24 }, + { "stat": "damage", "type": "stab", "scale": 25 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.4 }, @@ -4761,7 +3660,7 @@ { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.4 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 40 } + { "stat": "movecost", "scale": 50 } ] } ] From bb79d9470e3a4b78577cba641c1355b7737226b1 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 20 Jan 2024 05:34:34 -0800 Subject: [PATCH 036/202] Update mutations.json --- data/json/mutations/mutations.json | 31 ++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index d663d0b428d43..e524983c354b9 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -487,7 +487,17 @@ "name": { "str": "Echolocation" }, "points": 3, "description": "You've learned how to hunt without sight. Activate this mutation to make an ultrasonic sound that can reveal obstacles and enemies nearby.", - "prereqs": [ "BATEARS" ], + "prereqs": [ "BATEARS", "INDEFATIGABLE" ], + "category": [ "CHIROPTERAN" ], + "active": true + }, + { + "type": "mutation", + "id": "SHRIEK", + "name": { "str": "Shriek" }, + "points": 3, + "description": "Activate to let loose a high pitched screech, loud enough to shatter glass and daze anything in the immediate vicinity.", + "prereqs": [ "BATEARS", "INDEFATIGABLE" ], "category": [ "CHIROPTERAN" ], "active": true }, @@ -555,7 +565,7 @@ "starting_trait": true, "types": [ "CARDIO" ], "changes_to": [ "GOODCARDIO2" ], - "category": [ "FISH", "LUPINE", "MOUSE", "INSECT", "RABBIT" ], + "category": [ "FISH", "LUPINE", "MOUSE", "INSECT", "RABBIT", "CHIROPTERAN" ], "cardio_multiplier": 1.3 }, { @@ -1179,7 +1189,7 @@ "description": "You make less noise while walking. You're also less likely to set off traps.", "starting_trait": true, "cancels": [ "CLUMSY" ], - "category": [ "BIRD", "ELFA", "FELINE" ], + "category": [ "BIRD", "ELFA", "FELINE", "CHIROPTERAN" ], "noise_modifier": 0.4 }, { @@ -1452,7 +1462,7 @@ "description": "Without glasses, your seeing radius is severely reduced! However, you are guaranteed to start with a pair of glasses.", "starting_trait": true, "cancels": [ "URSINE_EYE" ], - "category": [ "BEAST", "TROGLOBITE" ], + "category": [ "BEAST" ], "flags": [ "MYOPIC" ] }, { @@ -2313,7 +2323,7 @@ "points": 1, "description": "Your visual processing has shifted: though you can see better in the dark, you're nearsighted in the light. Maybe glasses would help. Activate to toggle NV-visible areas on or off.", "types": [ "EYES", "VISION" ], - "cancels": [ "MYOPIC" ], + "cancels": [ "MYOPIC", "MESOPIC" ], "category": [ "URSINE" ], "flags": [ "MYOPIC_IN_LIGHT" ], "active": true, @@ -2324,10 +2334,10 @@ "id": "MESOPIC", "name": { "str": "Mesopic" }, "points": -1, - "description": "You can't see very well in bright light.", + "description": "You find it difficult to see very far in bright light. On its own, this doesn't necessarily make you any better in the dark.", "types": [ "EYES", "VISION" ], "cancels": [ "MYOPIC" ], - "category": [ "CHIROPTERAN" ], + "category": [ "CHIROPTERAN", "TROGLOBITE" ], "flags": [ "MYOPIC_IN_LIGHT" ] }, { @@ -2522,7 +2532,7 @@ "category": [ "BATRACHIAN" ], "flags": [ "EYE_MEMBRANE", "SEESLEEP", "MYOPIC_IN_LIGHT" ], "types": [ "EYES", "VISION" ], - "cancels": [ "MEMBRANE", "MYOPIC", "URSINE_EYE" ], + "cancels": [ "MEMBRANE", "MYOPIC", "URSINE_EYE", "MESOPIC" ], "threshreq": [ "THRESH_BATRACHIAN" ], "wet_protection": [ { "part": "eyes", "neutral": 1 } ], "armor": [ { "parts": "eyes", "cut": 3, "bash": 1 } ], @@ -7159,7 +7169,7 @@ "types": [ "SUNLIGHT" ], "changes_to": [ "TROGLO2" ], "cancels": [ "SEASONAL_AFFECTIVE" ], - "category": [ "BEAST", "INSECT", "SLIME", "SPIDER", "BATRACHIAN", "RAT", "TROGLOBITE" ] + "category": [ "BEAST", "INSECT", "SLIME", "SPIDER", "BATRACHIAN", "RAT", "TROGLOBITE", "CHIROPTERAN" ] }, { "type": "mutation", @@ -7327,7 +7337,8 @@ "ugliness": 4, "mixed_effect": true, "description": "You have a beak for a mouth. You can occasionally use it to peck at your enemies, but it is impossible for you to wear mouth gear. Slightly reduces wet effects.", - "types": [ "TEETH", "MUZZLE" ], + "types": [ "TEETH" ], + "cancels": [ "MUZZLE", "MUZZLE_RAT", "MUZZLE_BEAR" ], "changes_to": [ "BEAK_HUM", "BEAK_PECK" ], "category": [ "BIRD", "CEPHALOPOD" ], "wet_protection": [ { "part": "mouth", "ignored": 1 } ], From 8ae7a89178aba542cc8d19a551a26a211faaef54 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 20 Jan 2024 08:41:30 -0800 Subject: [PATCH 037/202] fixes --- src/character.cpp | 8 ++- src/condition.cpp | 134 +--------------------------------------------- src/sounds.cpp | 3 +- 3 files changed, 6 insertions(+), 139 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 865fa2b2414e7..6b82ee66de705 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10563,22 +10563,20 @@ std::vector Character::get_hostile_creatures( int range ) const void Character::echo_pulse() { map &here = get_map(); - sounds::sound( this->pos(), 25, sounds::sound_t::sensory, _( "click" ), true, + sounds::sound( this->pos(), 3, sounds::sound_t::movement, _( "chirp" ), true, "none", "none" ); for( tripoint origin : points_in_radius( pos(), 20 ) ) { if ( here.impassable( origin ) && here.pl_line_of_sight( origin, 20 ) ) { - sounds::sound( origin, 25, sounds::sound_t::sensory, _( "click" ), true, + sounds::sound( origin, 30, sounds::sound_t::sensory, _( "click" ), true, "none", "none" ); } creature_tracker &creatures = get_creature_tracker(); if ( creatures.creature_at( origin, true ) && here.pl_line_of_sight( origin, 20 ) ) { - sounds::sound( origin, 25, sounds::sound_t::sensory, _( "chk" ), false, + sounds::sound( origin, 30, sounds::sound_t::sensory, _( "chk" ), false, "none", "none" ); } } - // sounds::process_sounds(); - // sounds::process_sound_markers( this ); } bool Character::knows_trap( const tripoint &pos ) const diff --git a/src/condition.cpp b/src/condition.cpp index a7fb712164fe5..a376518658e84 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -3321,86 +3321,6 @@ double eoc_math::act( dialogue &d ) const static const std::vector parsers = { - {"u_has_any_trait", "npc_has_any_trait", jarg::array, &conditional_t::set_has_any_trait }, - {"u_has_trait", "npc_has_trait", jarg::member, &conditional_t::set_has_trait }, - {"u_has_visible_trait", "npc_has_visible_trait", jarg::member, &conditional_t::set_has_visible_trait }, - {"u_has_martial_art", "npc_has_martial_art", jarg::member, &conditional_t::set_has_martial_art }, - {"u_using_martial_art", "npc_using_martial_art", jarg::member, &conditional_t::set_using_martial_art }, - {"u_has_proficiency", "npc_has_proficiency", jarg::member, &conditional_t::set_has_proficiency }, - {"u_has_flag", "npc_has_flag", jarg::member, &conditional_t::set_has_flag }, - {"u_has_species", "npc_has_species", jarg::member, &conditional_t::set_has_species }, - {"u_bodytype", "npc_bodytype", jarg::member, &conditional_t::set_bodytype }, - {"u_has_class", "npc_has_class", jarg::member, &conditional_t::set_npc_has_class }, - {"u_has_activity", "npc_has_activity", jarg::string, &conditional_t::set_has_activity }, - {"u_is_riding", "npc_is_riding", jarg::string, &conditional_t::set_is_riding }, - {"u_has_mission", jarg::string, &conditional_t::set_u_has_mission }, - {"u_monsters_in_direction", jarg::string, &conditional_t::set_u_monsters_in_direction }, - {"u_safe_mode_trigger", jarg::member, &conditional_t::set_u_safe_mode_trigger }, - {"u_profession", jarg::string, &conditional_t::set_u_profession }, - {"u_has_strength", "npc_has_strength", jarg::member | jarg::array, &conditional_t::set_has_strength }, - {"u_has_dexterity", "npc_has_dexterity", jarg::member | jarg::array, &conditional_t::set_has_dexterity }, - {"u_has_intelligence", "npc_has_intelligence", jarg::member | jarg::array, &conditional_t::set_has_intelligence }, - {"u_has_perception", "npc_has_perception", jarg::member | jarg::array, &conditional_t::set_has_perception }, - {"u_has_hp", "npc_has_hp", jarg::member | jarg::array, &conditional_t::set_has_hp }, - {"u_has_part_temp", "npc_has_part_temp", jarg::member | jarg::array, &conditional_t::set_has_part_temp }, - {"u_is_wearing", "npc_is_wearing", jarg::member, &conditional_t::set_is_wearing }, - {"u_has_item", "npc_has_item", jarg::member, &conditional_t::set_has_item }, - {"u_has_item_with_flag", "npc_has_item_with_flag", jarg::member, &conditional_t::set_has_item_with_flag }, - {"u_has_items", "npc_has_items", jarg::member, &conditional_t::set_has_items }, - {"u_has_item_category", "npc_has_item_category", jarg::member, &conditional_t::set_has_item_category }, - {"u_has_bionics", "npc_has_bionics", jarg::member, &conditional_t::set_has_bionics }, - {"u_has_any_effect", "npc_has_any_effect", jarg::array, &conditional_t::set_has_any_effect }, - {"u_has_effect", "npc_has_effect", jarg::member, &conditional_t::set_has_effect }, - {"u_need", "npc_need", jarg::member, &conditional_t::set_need }, - {"u_query", "npc_query", jarg::member, &conditional_t::set_query }, - {"u_query_tile", "npc_query_tile", jarg::member, &conditional_t::set_query_tile }, - {"u_at_om_location", "npc_at_om_location", jarg::member, &conditional_t::set_at_om_location }, - {"u_near_om_location", "npc_near_om_location", jarg::member, &conditional_t::set_near_om_location }, - {"u_has_var", "npc_has_var", jarg::string, &conditional_t::set_has_var }, - {"expects_vars", jarg::member, &conditional_t::set_expects_vars }, - {"u_compare_var", "npc_compare_var", jarg::string, &conditional_t::set_compare_var }, - {"u_compare_time_since_var", "npc_compare_time_since_var", jarg::string, &conditional_t::set_compare_time_since_var }, - {"npc_role_nearby", jarg::string, &conditional_t::set_npc_role_nearby }, - {"npc_allies", jarg::member | jarg::array, &conditional_t::set_npc_allies }, - {"npc_allies_global", jarg::member | jarg::array, &conditional_t::set_npc_allies_global }, - {"u_service", "npc_service", jarg::member, &conditional_t::set_npc_service }, - {"u_has_cash", jarg::member | jarg::array, &conditional_t::set_u_has_cash }, - {"u_are_owed", jarg::member | jarg::array, &conditional_t::set_u_are_owed }, - {"u_aim_rule", "npc_aim_rule", jarg::member, &conditional_t::set_npc_aim_rule }, - {"u_engagement_rule", "npc_engagement_rule", jarg::member, &conditional_t::set_npc_engagement_rule }, - {"u_cbm_reserve_rule", "npc_cbm_reserve_rule", jarg::member, &conditional_t::set_npc_cbm_reserve_rule }, - {"u_cbm_recharge_rule", "npc_cbm_recharge_rule", jarg::member, &conditional_t::set_npc_cbm_recharge_rule }, - {"u_rule", "npc_rule", jarg::member, &conditional_t::set_npc_rule }, - {"u_override", "npc_override", jarg::member, &conditional_t::set_npc_override }, - {"days_since_cataclysm", jarg::member | jarg::array, &conditional_t::set_days_since }, - {"is_season", jarg::member, &conditional_t::set_is_season }, - {"u_mission_goal", "mission_goal", jarg::member, &conditional_t::set_mission_goal }, - {"u_mission_goal", "npc_mission_goal", jarg::member, &conditional_t::set_mission_goal }, - {"roll_contested", jarg::member, &conditional_t::set_roll_contested }, - {"u_know_recipe", jarg::member, &conditional_t::set_u_know_recipe }, - {"one_in_chance", jarg::member | jarg::array, &conditional_t::set_one_in_chance }, - {"x_in_y_chance", jarg::object, &conditional_t::set_x_in_y_chance }, - {"u_has_worn_with_flag", "npc_has_worn_with_flag", jarg::member, &conditional_t::set_has_worn_with_flag }, - {"u_has_wielded_with_flag", "npc_has_wielded_with_flag", jarg::member, &conditional_t::set_has_wielded_with_flag }, - {"u_has_wielded_with_weapon_category", "npc_has_wielded_with_weapon_category", jarg::member, &conditional_t::set_has_wielded_with_weapon_category }, - {"u_is_on_terrain", "npc_is_on_terrain", jarg::member, &conditional_t::set_is_on_terrain }, - {"u_is_on_terrain_with_flag", "npc_is_on_terrain_with_flag", jarg::member, &conditional_t::set_is_on_terrain_with_flag }, - {"u_is_in_field", "npc_is_in_field", jarg::member, &conditional_t::set_is_in_field }, - {"u_has_move_mode", "npc_has_move_mode", jarg::member, &conditional_t::set_has_move_mode }, - {"u_can_see_location", "npc_can_see_location", jarg::member, &conditional_t::set_can_see_location }, - {"is_weather", jarg::member, &conditional_t::set_is_weather }, - {"map_terrain_with_flag", jarg::member, &conditional_t::set_map_ter_furn_with_flag }, - {"map_furniture_with_flag", jarg::member, &conditional_t::set_map_ter_furn_with_flag }, - {"map_in_city", jarg::member, &conditional_t::set_map_in_city }, - {"mod_is_loaded", jarg::member, &conditional_t::set_mod_is_loaded }, - {"u_has_faction_trust", jarg::member | jarg::array, &conditional_t::set_has_faction_trust }, - {"compare_int", jarg::member, &conditional_t::set_compare_num }, - {"compare_num", jarg::member, &conditional_t::set_compare_num }, - {"math", jarg::member, &conditional_t::set_math }, - {"compare_string", jarg::member, &conditional_t::set_compare_string }, - {"get_condition", jarg::member, &conditional_t::set_get_condition }, - {"get_game_option", jarg::member, &conditional_t::set_get_option }, - {"u_can_see_friends", "npc_can_see_friends", jarg::member | jarg::array, &conditional_t::set_can_see_friends }, {"u_has_any_trait", "npc_has_any_trait", jarg::array, &conditional_fun::f_has_any_trait }, {"u_has_trait", "npc_has_trait", jarg::member, &conditional_fun::f_has_trait }, {"u_has_visible_trait", "npc_has_visible_trait", jarg::member, &conditional_fun::f_has_visible_trait }, @@ -3468,6 +3388,7 @@ parsers = { {"u_is_in_field", "npc_is_in_field", jarg::member, &conditional_fun::f_is_in_field }, {"u_has_move_mode", "npc_has_move_mode", jarg::member, &conditional_fun::f_has_move_mode }, {"u_can_see_location", "npc_can_see_location", jarg::member, &conditional_fun::f_can_see_location }, + {"u_can_see_friends", "npc_can_see_friends", jarg::member | jarg::array, &conditional_t::set_can_see_friends }, {"is_weather", jarg::member, &conditional_fun::f_is_weather }, {"map_terrain_with_flag", jarg::member, &conditional_fun::f_map_ter_furn_with_flag }, {"map_furniture_with_flag", jarg::member, &conditional_fun::f_map_ter_furn_with_flag }, @@ -3488,59 +3409,6 @@ parsers = { static const std::vector parsers_simple = { - {"u_male", "npc_male", &conditional_t::set_is_male }, - {"u_female", "npc_female", &conditional_t::set_is_female }, - {"has_no_assigned_mission", &conditional_t::set_no_assigned_mission }, - {"has_assigned_mission", &conditional_t::set_has_assigned_mission }, - {"has_many_assigned_missions", &conditional_t::set_has_many_assigned_missions }, - {"u_has_no_available_mission", "has_no_available_mission", &conditional_t::set_no_available_mission }, - {"u_has_no_available_mission", "npc_has_no_available_mission", &conditional_t::set_no_available_mission }, - {"u_has_available_mission", "has_available_mission", &conditional_t::set_has_available_mission }, - {"u_has_available_mission", "npc_has_available_mission", &conditional_t::set_has_available_mission }, - {"u_has_many_available_missions", "has_many_available_missions", &conditional_t::set_has_many_available_missions }, - {"u_has_many_available_missions", "npc_has_many_available_missions", &conditional_t::set_has_many_available_missions }, - {"u_mission_complete", "mission_complete", &conditional_t::set_mission_complete }, - {"u_mission_complete", "npc_mission_complete", &conditional_t::set_mission_complete }, - {"u_mission_incomplete", "mission_incomplete", &conditional_t::set_mission_incomplete }, - {"u_mission_incomplete", "npc_mission_incomplete", &conditional_t::set_mission_incomplete }, - {"u_mission_failed", "mission_failed", &conditional_t::set_mission_failed }, - {"u_mission_failed", "npc_mission_failed", &conditional_t::set_mission_failed }, - {"u_available", "npc_available", &conditional_t::set_npc_available }, - {"u_following", "npc_following", &conditional_t::set_npc_following }, - {"u_friend", "npc_friend", &conditional_t::set_npc_friend }, - {"u_hostile", "npc_hostile", &conditional_t::set_npc_hostile }, - {"u_train_skills", "npc_train_skills", &conditional_t::set_npc_train_skills }, - {"u_train_styles", "npc_train_styles", &conditional_t::set_npc_train_styles }, - {"u_train_spells", "npc_train_spells", &conditional_t::set_npc_train_spells }, - {"u_at_safe_space", "at_safe_space", &conditional_t::set_at_safe_space }, - {"u_at_safe_space", "npc_at_safe_space", &conditional_t::set_at_safe_space }, - {"u_can_stow_weapon", "npc_can_stow_weapon", &conditional_t::set_can_stow_weapon }, - {"u_can_drop_weapon", "npc_can_drop_weapon", &conditional_t::set_can_drop_weapon }, - {"u_has_weapon", "npc_has_weapon", &conditional_t::set_has_weapon }, - {"u_driving", "npc_driving", &conditional_t::set_is_driving }, - {"u_has_activity", "npc_has_activity", &conditional_t::set_has_activity }, - {"u_is_riding", "npc_is_riding", &conditional_t::set_is_riding }, - {"is_day", &conditional_t::set_is_day }, - {"u_has_stolen_item", "npc_has_stolen_item", &conditional_t::set_has_stolen_item }, - {"u_is_outside", "is_outside", &conditional_t::set_is_outside }, - {"u_is_outside", "npc_is_outside", &conditional_t::set_is_outside }, - {"u_is_underwater", "npc_is_underwater", &conditional_t::set_is_underwater }, - {"u_has_camp", &conditional_t::set_u_has_camp }, - {"u_has_pickup_list", "has_pickup_list", &conditional_t::set_has_pickup_list }, - {"u_has_pickup_list", "npc_has_pickup_list", &conditional_t::set_has_pickup_list }, - {"is_by_radio", &conditional_t::set_is_by_radio }, - {"has_reason", &conditional_t::set_has_reason }, - {"mission_has_generic_rewards", &conditional_t::set_mission_has_generic_rewards }, - {"u_can_see", "npc_can_see", &conditional_t::set_can_see }, - {"u_is_deaf", "npc_is_deaf", &conditional_t::set_is_deaf }, - {"u_is_alive", "npc_is_alive", &conditional_t::set_is_alive }, - {"u_is_avatar", "npc_is_avatar", &conditional_t::set_is_avatar }, - {"u_is_npc", "npc_is_npc", &conditional_t::set_is_npc }, - {"u_is_character", "npc_is_character", &conditional_t::set_is_character }, - {"u_is_monster", "npc_is_monster", &conditional_t::set_is_monster }, - {"u_is_item", "npc_is_item", &conditional_t::set_is_item }, - {"u_is_furniture", "npc_is_furniture", &conditional_t::set_is_furniture }, - {"player_see_u", "player_see_npc", &conditional_t::set_player_see } {"u_male", "npc_male", &conditional_fun::f_is_male }, {"u_female", "npc_female", &conditional_fun::f_is_female }, {"has_no_assigned_mission", &conditional_fun::f_no_assigned_mission }, diff --git a/src/sounds.cpp b/src/sounds.cpp index 24a804f600a56..7a2ecac8721ce 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -717,7 +717,8 @@ void sounds::process_sound_markers( Character *you ) } int err_offset; - if( ( heard_volume + distance_to_sound ) / distance_to_sound < 2 ) { + // Echolocation has to be at least somewhat precise. + if( ( heard_volume + distance_to_sound ) / distance_to_sound < 2 && ( sound.category != sound_t::sensory ) ) { err_offset = 3; } else if( ( heard_volume + distance_to_sound ) / distance_to_sound < 3 ) { err_offset = 2; From de2b51102a24e4d59d5d9978675c1f3141d0f602 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 20 Jan 2024 08:42:38 -0800 Subject: [PATCH 038/202] Update settings.json --- .vscode/settings.json | 92 ------------------------------------------- 1 file changed, 92 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f2d79e00015e2..ab729b661374d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -64,97 +64,5 @@ "github-actions.remote-name": "upstream", // NOTE: This disable the plugins formatting so astyle is used. "C_Cpp.formatting": "disabled", - "files.associations": { - "algorithm": "cpp", - "array": "cpp", - "atomic": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "cfenv": "cpp", - "charconv": "cpp", - "chrono": "cpp", - "cinttypes": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "codecvt": "cpp", - "compare": "cpp", - "complex": "cpp", - "concepts": "cpp", - "condition_variable": "cpp", - "csetjmp": "cpp", - "csignal": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cuchar": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "exception": "cpp", - "filesystem": "cpp", - "forward_list": "cpp", - "fstream": "cpp", - "functional": "cpp", - "future": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "ios": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "iterator": "cpp", - "limits": "cpp", - "list": "cpp", - "locale": "cpp", - "map": "cpp", - "memory": "cpp", - "mutex": "cpp", - "new": "cpp", - "numeric": "cpp", - "optional": "cpp", - "ostream": "cpp", - "queue": "cpp", - "random": "cpp", - "ratio": "cpp", - "regex": "cpp", - "scoped_allocator": "cpp", - "set": "cpp", - "span": "cpp", - "sstream": "cpp", - "stack": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "string": "cpp", - "system_error": "cpp", - "thread": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "typeindex": "cpp", - "typeinfo": "cpp", - "unordered_map": "cpp", - "unordered_set": "cpp", - "utility": "cpp", - "variant": "cpp", - "vector": "cpp", - "xfacet": "cpp", - "xhash": "cpp", - "xiosbase": "cpp", - "xlocale": "cpp", - "xlocbuf": "cpp", - "xlocinfo": "cpp", - "xlocmes": "cpp", - "xlocmon": "cpp", - "xlocnum": "cpp", - "xloctime": "cpp", - "xmemory": "cpp", - "xstddef": "cpp", - "xstring": "cpp", - "xtr1common": "cpp", - "xtree": "cpp", - "xutility": "cpp" }, } From bc0d2487df0a2a6960dee0006fc594650753f8dd Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 20 Jan 2024 08:43:34 -0800 Subject: [PATCH 039/202] Update settings.json --- .vscode/settings.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index ab729b661374d..20bd09ea82c04 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -64,5 +64,4 @@ "github-actions.remote-name": "upstream", // NOTE: This disable the plugins formatting so astyle is used. "C_Cpp.formatting": "disabled", - }, } From 875fede0a36681c045d6b2b8ea3cc77ebdc81ebe Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 20 Jan 2024 08:44:29 -0800 Subject: [PATCH 040/202] Update condition.h --- src/condition.h | 136 ------------------------------------------------ 1 file changed, 136 deletions(-) diff --git a/src/condition.h b/src/condition.h index e39a1fbb97489..24edfa2060c04 100644 --- a/src/condition.h +++ b/src/condition.h @@ -76,142 +76,6 @@ struct conditional_t { conditional_t() = default; explicit conditional_t( std::string_view type ); explicit conditional_t( const JsonObject &jo ); - - void set_has_any_trait( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_trait( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_visible_trait( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_martial_art( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_flag( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_species( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_proficiency( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_bodytype( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_var( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_expects_vars( const JsonObject &jo, std::string_view member ); - void set_compare_var( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_compare_time_since_var( const JsonObject &jo, std::string_view member, - bool is_npc = false ); - void set_has_activity( bool is_npc = false ); - void set_has_activity( const JsonObject &, std::string_view, bool is_npc = false ) { - set_has_activity( is_npc ); - } - void set_is_riding( bool is_npc = false ); - void set_is_riding( const JsonObject &, std::string_view, bool is_npc = false ) { - set_is_riding( is_npc ); - } - void set_npc_has_class( const JsonObject &jo, std::string_view member, bool is_npc ); - void set_u_has_mission( const JsonObject &jo, std::string_view member ); - void set_u_monsters_in_direction( const JsonObject &jo, std::string_view member ); - void set_u_safe_mode_trigger( const JsonObject &jo, std::string_view member ); - void set_u_profession( const JsonObject &jo, std::string_view member ); - void set_has_strength( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_dexterity( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_intelligence( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_perception( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_hp( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_part_temp( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_is_deaf( bool is_npc = false ); - void set_is_on_terrain( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_is_on_terrain_with_flag( const JsonObject &jo, std::string_view member, - bool is_npc = false ); - void set_is_in_field( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_one_in_chance( const JsonObject &jo, std::string_view member ); - void set_query( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_query_tile( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_x_in_y_chance( const JsonObject &jo, std::string_view member ); - void set_has_worn_with_flag( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_wielded_with_flag( const JsonObject &jo, std::string_view member, - bool is_npc = false ); - void set_has_wielded_with_weapon_category( const JsonObject &jo, std::string_view member, - bool is_npc = false ); - void set_is_wearing( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_item( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_items( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_item_with_flag( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_item_category( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_bionics( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_any_effect( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_effect( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_need( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_at_om_location( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_near_om_location( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_has_move_mode( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_can_see_location( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_using_martial_art( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_npc_role_nearby( const JsonObject &jo, std::string_view member ); - void set_npc_allies( const JsonObject &jo, std::string_view member ); - void set_npc_allies_global( const JsonObject &jo, std::string_view member ); - void set_u_has_cash( const JsonObject &jo, std::string_view member ); - void set_u_are_owed( const JsonObject &jo, std::string_view member ); - void set_npc_aim_rule( const JsonObject &jo, std::string_view member, bool is_npc ); - void set_npc_engagement_rule( const JsonObject &jo, std::string_view member, bool is_npc ); - void set_npc_cbm_reserve_rule( const JsonObject &jo, std::string_view member, bool is_npc ); - void set_npc_cbm_recharge_rule( const JsonObject &jo, std::string_view member, bool is_npc ); - void set_npc_rule( const JsonObject &jo, std::string_view member, bool is_npc ); - void set_npc_override( const JsonObject &jo, std::string_view member, bool is_npc ); - void set_days_since( const JsonObject &jo, std::string_view member ); - void set_is_season( const JsonObject &jo, std::string_view member ); - void set_is_weather( const JsonObject &jo, std::string_view member ); - void set_map_ter_furn_with_flag( const JsonObject &jo, std::string_view member ); - void set_map_in_city( const JsonObject &jo, std::string_view member ); - void set_mod_is_loaded( const JsonObject &jo, std::string_view member ); - void set_mission_goal( const JsonObject &jo, std::string_view member, bool is_npc ); - void set_has_faction_trust( const JsonObject &jo, std::string_view member ); - void set_can_see_friends( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_no_assigned_mission(); - void set_has_assigned_mission(); - void set_has_many_assigned_missions(); - void set_no_available_mission( bool is_npc ); - void set_has_available_mission( bool is_npc ); - void set_has_many_available_missions( bool is_npc ); - void set_mission_complete( bool is_npc ); - void set_mission_incomplete( bool is_npc ); - void set_mission_failed( bool is_npc ); - void set_npc_service( const JsonObject &, std::string_view, bool is_npc ); - void set_npc_available( bool is_npc ); - void set_npc_following( bool is_npc ); - void set_npc_friend( bool is_npc ); - void set_npc_hostile( bool is_npc ); - void set_npc_train_skills( bool is_npc ); - void set_npc_train_styles( bool is_npc ); - void set_npc_train_spells( bool is_npc ); - void set_at_safe_space( bool is_npc ); - void set_can_stow_weapon( bool is_npc = false ); - void set_can_drop_weapon( bool is_npc = false ); - void set_has_weapon( bool is_npc = false ); - void set_is_driving( bool is_npc = false ); - void set_is_day(); - void set_has_stolen_item( bool is_npc = false ); - void set_is_outside( bool is_npc = false ); - void set_is_underwater( bool is_npc = false ); - void set_is_by_radio(); - void set_u_has_camp(); - void set_has_pickup_list( bool is_npc ); - void set_has_reason(); - void set_is_alive( bool is_npc = false ); - void set_is_avatar( bool is_npc = false ); - void set_is_npc( bool is_npc = false ); - void set_is_character( bool is_npc = false ); - void set_is_monster( bool is_npc = false ); - void set_is_item( bool is_npc = false ); - void set_is_furniture( bool is_npc = false ); - void set_is_gender( bool is_male, bool is_npc = false ); - void set_is_male( bool is_npc = false ) { - set_is_gender( true, is_npc ); - } - void set_is_female( bool is_npc = false ) { - set_is_gender( false, is_npc ); - } - void set_has_skill( const JsonObject &jo, std::string_view member, bool is_npc = false ); - void set_roll_contested( const JsonObject &jo, std::string_view member ); - void set_u_know_recipe( const JsonObject &jo, std::string_view member ); - void set_mission_has_generic_rewards(); - void set_can_see( bool is_npc = false ); - void set_player_see( bool is_npc = false ); - void set_get_option( const JsonObject &jo, std::string_view member ); - void set_compare_string( const JsonObject &jo, std::string_view member ); - void set_get_condition( const JsonObject &jo, std::string_view member ); - void set_compare_num( const JsonObject &jo, std::string_view member ); - void set_math( const JsonObject &jo, std::string_view member ); static std::function get_get_string( const JsonObject &jo ); static std::function get_get_translation( const JsonObject &jo ); From 52bc49983430f186577384e5b9b1eaa6da272094 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 20 Jan 2024 16:27:08 -0800 Subject: [PATCH 041/202] echolocation --- data/json/items/armor/integrated.json | 2 +- src/character.cpp | 6 +++--- src/character.h | 2 +- src/condition.cpp | 6 +++--- src/lightmap.cpp | 20 +++++++++++--------- src/map.h | 5 +++++ src/sounds.cpp | 20 ++++++++++++++++---- 7 files changed, 40 insertions(+), 21 deletions(-) diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index b99fed6524350..4fc33c37a196e 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1600,7 +1600,7 @@ "warmth": 5, "to_hit": 1, "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], - "techniques": [ "FANGS_BITE", "FANGS_BITE_NATURAL", "FANGS_BITE_CRIT", "MUZZLE_BEAR_BITE", "MUZZLE_LONG_BITE", "MUZZLE_RAPTOR_BITE", "MUZZLE_BITE", "MUZZLE_BEAR_BITE_NATURAL", "MUZZLE_LONG_BITE_NATURAL", "MUZZLE_RAPTOR_BITE_NATURAL", "MUZZLE_BITE_NATURAL", "MUZZLE_BEAR_BITE_CRIT", "MUZZLE_LONG_BITE_CRIT", "MUZZLE_RAPTOR_BITE_CRIT", "MUZZLE_BITE_CRIT" ], + "techniques": [ "FANGS_BITE", "FANGS_BITE_NATURAL", "FANGS_BITE_CRIT" ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], "armor": [ { diff --git a/src/character.cpp b/src/character.cpp index 6b82ee66de705..d9e6a304ee28e 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10566,17 +10566,17 @@ void Character::echo_pulse() sounds::sound( this->pos(), 3, sounds::sound_t::movement, _( "chirp" ), true, "none", "none" ); for( tripoint origin : points_in_radius( pos(), 20 ) ) { - if ( here.impassable( origin ) && here.pl_line_of_sight( origin, 20 ) ) { + if ( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, 10 ) ) { sounds::sound( origin, 30, sounds::sound_t::sensory, _( "click" ), true, "none", "none" ); - } creature_tracker &creatures = get_creature_tracker(); - if ( creatures.creature_at( origin, true ) && here.pl_line_of_sight( origin, 20 ) ) { + if ( creatures.creature_at( origin, true ) && here.pl_line_of_sight( origin, 10 ) ) { sounds::sound( origin, 30, sounds::sound_t::sensory, _( "chk" ), false, "none", "none" ); } } + } bool Character::knows_trap( const tripoint &pos ) const diff --git a/src/character.h b/src/character.h index abf15e3883105..12814193e541a 100644 --- a/src/character.h +++ b/src/character.h @@ -137,7 +137,7 @@ enum vision_modes { URSINE_VISION, BOOMERED, DARKNESS, - IR_VISION, + TRANSPARENT_BLOCKED, VISION_CLAIRVOYANCE, VISION_CLAIRVOYANCE_PLUS, VISION_CLAIRVOYANCE_SUPER, diff --git a/src/condition.cpp b/src/condition.cpp index a376518658e84..cbf7738a3ccd1 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -567,10 +567,10 @@ conditional_t::func f_has_trait( const JsonObject &jo, std::string_view member, } -void conditional_t::set_can_see_friends( const JsonObject &jo, std::string_view member, bool is_npc ) +conditional_t::func f_can_see_friends( const JsonObject &jo, std::string_view member, bool is_npc ) { dbl_or_var dov = get_dbl_or_var( jo, member ); - condition = [ dov, is_npc ]( dialogue & d ) { + return [ dov, is_npc ]( dialogue & d ) { Character &player_character = get_player_character(); const Character *ch = d.actor( is_npc )->get_character(); int seen = 0; @@ -3388,7 +3388,7 @@ parsers = { {"u_is_in_field", "npc_is_in_field", jarg::member, &conditional_fun::f_is_in_field }, {"u_has_move_mode", "npc_has_move_mode", jarg::member, &conditional_fun::f_has_move_mode }, {"u_can_see_location", "npc_can_see_location", jarg::member, &conditional_fun::f_can_see_location }, - {"u_can_see_friends", "npc_can_see_friends", jarg::member | jarg::array, &conditional_t::set_can_see_friends }, + {"u_can_see_friends", "npc_can_see_friends", jarg::member | jarg::array, &conditional_fun::f_can_see_friends }, {"is_weather", jarg::member, &conditional_fun::f_is_weather }, {"map_terrain_with_flag", jarg::member, &conditional_fun::f_map_ter_furn_with_flag }, {"map_furniture_with_flag", jarg::member, &conditional_fun::f_map_ter_furn_with_flag }, diff --git a/src/lightmap.cpp b/src/lightmap.cpp index b64d11b788b7c..a4e824faec420 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -89,6 +89,7 @@ bool map::build_transparency_cache( const int zlev ) level_cache &map_cache = get_cache( zlev ); auto &transparent_cache_wo_fields = map_cache.transparent_cache_wo_fields; auto &transparency_cache = map_cache.transparency_cache; + //auto &transparency_cache_wo_solids = map_cache.transparent_cache_wo_solids; auto &outside_cache = map_cache.outside_cache; if( map_cache.transparency_cache_dirty.none() ) { @@ -142,15 +143,14 @@ bool map::build_transparency_cache( const int zlev ) value *= sight_penalty; } float value_wo_fields = value; - for( const auto &fld : cur_submap->get_field( sp ) ) { - const field_intensity_level &i_level = fld.second.get_intensity_level(); - if( i_level.transparent ) { - continue; + for( const auto &fld : cur_submap->get_field( sp ) ) { + const field_intensity_level &i_level = fld.second.get_intensity_level(); + if( i_level.transparent ) { + continue; + } + // Fields are either transparent or not, however we want some to be translucent + value = value * i_level.translucency; } - // Fields are either transparent or not, however we want some to be translucent - value = value * i_level.translucency; - } - // TODO: [lightmap] Have glass reduce light as well return std::make_pair( value, value_wo_fields ); }; @@ -209,6 +209,7 @@ bool map::build_vision_transparency_cache( const int zlev ) bool is_crouching = player_character.is_crouching(); bool is_runallfours = player_character.is_runallfours(); bool is_prone = player_character.is_prone(); + for( const tripoint &loc : points_in_radius( p, 1 ) ) { if( loc == p ) { // The tile player is standing on should always be visible @@ -809,8 +810,9 @@ bool map::pl_line_of_sight( const tripoint &t, const int max_range ) const return false; } +// Character &player_character = get_player_character(); const level_cache &map_cache = get_cache_ref( t.z ); - if( map_cache.camera_cache[t.x][t.y] > 0.075f ) { + if( map_cache.seen_cache[t.x][t.y] > 0.075f ) { return true; } diff --git a/src/map.h b/src/map.h index 3f477fdc230e9..466a8a5292f0b 100644 --- a/src/map.h +++ b/src/map.h @@ -1823,6 +1823,11 @@ class map * Used for infrared. */ bool pl_line_of_sight( const tripoint &t, int max_range ) const; + /** + * Basically line_of_sight, but blocked by transparent objects + * and other sound baffling + */ + bool pl_line_of_sound( const tripoint &t, int max_range ) const; std::set dirty_vehicle_list; /** return @ref abs_sub */ diff --git a/src/sounds.cpp b/src/sounds.cpp index 7a2ecac8721ce..5c44f37ef0bfb 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -717,13 +717,24 @@ void sounds::process_sound_markers( Character *you ) } int err_offset; - // Echolocation has to be at least somewhat precise. - if( ( heard_volume + distance_to_sound ) / distance_to_sound < 2 && ( sound.category != sound_t::sensory ) ) { + + if( ( heard_volume + distance_to_sound ) / distance_to_sound < 2 ) { err_offset = 3; } else if( ( heard_volume + distance_to_sound ) / distance_to_sound < 3 ) { err_offset = 2; } else { - err_offset = 1; + err_offset = rand() % 2; + } + + // Echolocation has to be fairly precise or it's worse than useless. + if ( sound.category == sound_t::sensory ) { + if( ( heard_volume + distance_to_sound ) / distance_to_sound < 2 ) { + err_offset = rand() % 3; + } else if( ( heard_volume + distance_to_sound ) / distance_to_sound < 3 ) { + err_offset = rand() % 2; + } else { + err_offset = 0; + } } // If Z-coordinate is different, draw even when you can see the source @@ -731,9 +742,10 @@ void sounds::process_sound_markers( Character *you ) // Enumerate the valid points the player *cannot* see. // Unless the source is on a different z-level, then any point is fine + // Also show sensory sounds like SONAR even if we can see the point. std::vector unseen_points; for( const tripoint &newp : get_map().points_in_radius( pos, err_offset ) ) { - if( diff_z || !you->sees( newp ) ) { + if( diff_z || sound.category == sound_t::sensory || !you->sees( newp ) ) { unseen_points.emplace_back( newp ); } } From 4a3d922530ebbbb5887ff6577de35d0d92da1681 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 21 Jan 2024 08:18:20 -0800 Subject: [PATCH 042/202] sonar cbm --- data/json/bionics.json | 7 ++ data/json/items/bionics.json | 12 ++++ data/json/traps.json | 14 ++++ src/character.cpp | 124 +++++++++++++++++++++++++++++++++-- src/character.h | 2 +- src/sounds.cpp | 11 +++- src/trap.cpp | 10 +++ 7 files changed, 169 insertions(+), 11 deletions(-) diff --git a/data/json/bionics.json b/data/json/bionics.json index 7868e5945248d..04f7949a11245 100644 --- a/data/json/bionics.json +++ b/data/json/bionics.json @@ -1600,6 +1600,13 @@ "flags": [ "BIONIC_NPC_USABLE" ], "enchantments": [ { "condition": "ALWAYS", "values": [ { "value": "WEAPON_DISPERSION", "multiply": -0.25 } ] } ] }, + { + "id": "bio_sonar", + "type": "bionic", + "name": { "str": "Subaquatic Navigation System" }, + "description": "While active, a tiny device in your inner ear periodically generates ultrasonic pulses which aid in navigation, provided you are underwater and able to hear.", + "occupied_bodyparts": [ [ "head", 1 ] ] + }, { "id": "bio_taser", "type": "bionic", diff --git a/data/json/items/bionics.json b/data/json/items/bionics.json index 2c1a8f6c41e7d..9e445f2cf9429 100644 --- a/data/json/items/bionics.json +++ b/data/json/items/bionics.json @@ -310,6 +310,18 @@ "weight": "500 g", "difficulty": 6 }, + { + "id": "bio_sonar", + "copy-from": "bionic_general", + "type": "BIONIC_ITEM", + "name": { "str": "Subaquatic Navigation System CBM" }, + "looks_like": "bio_int_enhancer", + "description": "A tiny device implanted in the inner ear. When active, it will periodically generate ultrasonic pulses which aid in navigation, provided the user is underwater and able to hear.", + "price": 250000, + "price_postapoc": 1250, + "weight": "50 g", + "difficulty": 4 + }, { "id": "bio_emp", "copy-from": "bionic_general", diff --git a/data/json/traps.json b/data/json/traps.json index 9766db24cc231..2c134da6da0b6 100644 --- a/data/json/traps.json +++ b/data/json/traps.json @@ -216,6 +216,7 @@ "difficulty": 3, "action": "beartrap", "drops": [ "beartrap" ], + "flags": [ "ECHOLOCATION_DETECTABLE" ], "vehicle_data": { "damage": 300, "sound_volume": 8, @@ -258,6 +259,7 @@ "color": "light_gray", "memorial_male": { "ctxt": "memorial_male", "str": "Stepped on a spiked board." }, "memorial_female": { "ctxt": "memorial_female", "str": "Stepped on a spiked board." }, + "flags": [ "ECHOLOCATION_DETECTABLE" ], "symbol": "_", "visibility": 1, "avoidance": 6, @@ -318,6 +320,7 @@ "color": "green", "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a crossbow trap." }, "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a crossbow trap." }, + "flags": [ "ECHOLOCATION_DETECTABLE" ], "symbol": "^", "visibility": 5, "avoidance": 4, @@ -343,6 +346,7 @@ "color": "red", "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a shotgun trap." }, "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a shotgun trap." }, + "flags": [ "ECHOLOCATION_DETECTABLE" ], "symbol": "^", "visibility": 4, "avoidance": 5, @@ -367,6 +371,7 @@ "color": "red", "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a shotgun trap." }, "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a shotgun trap." }, + "flags": [ "ECHOLOCATION_DETECTABLE" ], "symbol": "^", "visibility": 4, "avoidance": 5, @@ -392,6 +397,7 @@ "color": "red", "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a shotgun trap." }, "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a shotgun trap." }, + "flags": [ "ECHOLOCATION_DETECTABLE" ], "symbol": "^", "visibility": 4, "avoidance": 5, @@ -414,6 +420,7 @@ "id": "tr_engine", "trigger_weight": "200 g", "name": "spinning blade engine", + "flags": [ "ECHOLOCATION_DETECTABLE" ], "color": "light_red", "symbol": "_", "visibility": 0, @@ -430,6 +437,7 @@ "color": "cyan", "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a blade trap." }, "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a blade trap." }, + "flags": [ "ECHOLOCATION_DETECTABLE" ], "symbol": "\\", "visibility": 0, "avoidance": 4, @@ -445,6 +453,7 @@ "color": "red", "memorial_male": { "ctxt": "memorial_male", "str": "Stepped on a land mine." }, "memorial_female": { "ctxt": "memorial_female", "str": "Stepped on a land mine." }, + "flags": [ "ECHOLOCATION_DETECTABLE" ], "symbol": "^", "visibility": 1, "avoidance": 14, @@ -521,6 +530,7 @@ "color": "cyan", "memorial_male": { "ctxt": "memorial_male", "str": "Stepped into an exposed high-energy conduit." }, "memorial_female": { "ctxt": "memorial_female", "str": "Stepped into an exposed high-energy conduit." }, + "flags": [ "ECHOLOCATION_DETECTABLE" ], "symbol": "7", "visibility": 2, "avoidance": 20, @@ -550,6 +560,7 @@ "color": "brown", "memorial_male": { "ctxt": "memorial_male", "str": "Fell in a pit." }, "memorial_female": { "ctxt": "memorial_female", "str": "Fell in a pit." }, + "flags": [ "ECHOLOCATION_DETECTABLE" ], "symbol": "0", "visibility": 0, "avoidance": 8, @@ -565,6 +576,7 @@ "color": "blue", "memorial_male": { "ctxt": "memorial_male", "str": "Fell into a spiked pit." }, "memorial_female": { "ctxt": "memorial_female", "str": "Fell into a spiked pit." }, + "flags": [ "ECHOLOCATION_DETECTABLE" ], "symbol": "0", "visibility": 0, "avoidance": 8, @@ -612,6 +624,7 @@ "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a booby trap." }, "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a booby trap." }, "symbol": "^", + "flags": [ "ECHOLOCATION_DETECTABLE" ], "visibility": 5, "avoidance": 4, "difficulty": 7, @@ -634,6 +647,7 @@ "color": "light_gray", "memorial_male": { "ctxt": "memorial_male", "str": "Triggered a flood trap." }, "memorial_female": { "ctxt": "memorial_female", "str": "Triggered a flood trap." }, + "flags": [ "ECHOLOCATION_DETECTABLE" ], "symbol": "^", "visibility": 9, "avoidance": 20, diff --git a/src/character.cpp b/src/character.cpp index d9e6a304ee28e..03d0b8bef65ef 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -168,8 +168,10 @@ static const bionic_id bio_ods( "bio_ods" ); static const bionic_id bio_railgun( "bio_railgun" ); static const bionic_id bio_shock_absorber( "bio_shock_absorber" ); static const bionic_id bio_sleep_shutdown( "bio_sleep_shutdown" ); +static const bionic_id bio_sonar( "bio_sonar" ); static const bionic_id bio_soporific( "bio_soporific" ); static const bionic_id bio_synlungs( "bio_synlungs" ); +static const bionic_id bio_targeting( "bio_targeting" ); static const bionic_id bio_uncanny_dodge( "bio_uncanny_dodge" ); static const bionic_id bio_ups( "bio_ups" ); static const bionic_id bio_voice( "bio_voice" ); @@ -10563,20 +10565,125 @@ std::vector Character::get_hostile_creatures( int range ) const void Character::echo_pulse() { map &here = get_map(); - sounds::sound( this->pos(), 3, sounds::sound_t::movement, _( "chirp" ), true, + int echo_volume = 0; + int pulse_range = 10; + if( has_active_bionic( bio_sonar ) && is_underwater() ) { + pulse_range = 16; + sounds::sound( this->pos(), 5, sounds::sound_t::movement, _( "boop" ), true, "none", "none" ); - for( tripoint origin : points_in_radius( pos(), 20 ) ) { - if ( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, 10 ) ) { + } else { + sounds::sound( this->pos(), 5, sounds::sound_t::movement, _( "chirp" ), true, + "none", "none" ); + } + for( tripoint origin : points_in_radius( pos(), pulse_range ) ) { + if ( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, 2 ) ) { sounds::sound( origin, 30, sounds::sound_t::sensory, _( "click" ), true, "none", "none" ); } - creature_tracker &creatures = get_creature_tracker(); - if ( creatures.creature_at( origin, true ) && here.pl_line_of_sight( origin, 10 ) ) { - sounds::sound( origin, 30, sounds::sound_t::sensory, _( "chk" ), false, + const trap &tr = here.tr_at( origin ); + if( !knows_trap( origin ) && tr.detected_by_echolocation() ) { + const std::string direction = direction_name( direction_from( pos(), origin ) ); + add_msg_if_player( m_warning, _( "You detect a %1$s to the %2$s!" ), + tr.name(), direction ); + add_known_trap( origin, tr ); + } + Creature *critter = get_creature_tracker().creature_at( origin, true ); + if ( critter && here.pl_line_of_sight( origin, 1 ) ) { + switch( critter->get_size() ) { + case creature_size::tiny: + echo_volume = 1; + break; + case creature_size::small: + echo_volume = 2; + break; + case creature_size::medium: + echo_volume = 3; + break; + case creature_size::large: + echo_volume = 4; + break; + case creature_size::huge: + echo_volume = 5; + break; + case creature_size::num_sizes: + debugmsg( "ERROR: Invalid Creature size class." ); + break; + } + if( critter->has_flag( mon_flag_PLASTIC ) ) { + echo_volume -= std::max( 1, 1 ); + } + if( critter->has_flag( mon_flag_HARDTOSHOOT ) ) { + echo_volume -= std::max( 1, 1 ); + } + const char *echo_string = nullptr; + // bio_targeting has a HUD and automatically converts the audio data to visual feedback + if( has_active_bionic( bio_targeting ) && !has_effect( effect_blind ) && has_active_bionic( bio_sonar ) ) + switch( echo_volume ) { + case 1: + echo_string = _( "Entity: [Tiny]" ); + break; + case 2: + echo_string = _( "Entity: [Small]" ); + break; + case 3: + echo_string = _( "Entity: [Medium]" ); + break; + case 4: + echo_string = _( "Warning! Entity: [Large]" ); + break; + case 5: + echo_string = _( "Warning! Entity [Huge]" ); + break; + default: + debugmsg( "ERROR: Invalid echo string." ); + break; + } else if ( ( !has_active_bionic( bio_targeting ) || has_effect( effect_blind ) ) && has_active_bionic( bio_sonar ) ) { + switch( echo_volume ) { + case 1: + echo_string = _( "tick" ); + break; + case 2: + echo_string = _( "pii" ); + break; + case 3: + echo_string = _( "Ping" ); + break; + case 4: + echo_string = _( "Pong" ); + break; + case 5: + echo_string = _( "Booop" ); + break; + default: + debugmsg( "ERROR: Invalid echo string." ); + break; + } + } else { + switch( echo_volume ) { + case 1: + echo_string = _( "ch" ); + break; + case 2: + echo_string = _( "chk" ); + break; + case 3: + echo_string = _( "chhk" ); + break; + case 4: + echo_string = _( "chkch" ); + break; + case 5: + echo_string = _( "chkchh" ); + break; + default: + debugmsg( "ERROR: Invalid echo string." ); + break; + } + } + sounds::sound( origin, echo_volume, sounds::sound_t::sensory, _( echo_string ), false, "none", "none" ); } } - } bool Character::knows_trap( const tripoint &pos ) const @@ -12837,6 +12944,9 @@ void Character::search_surroundings() if( controlling_vehicle ) { return; } + if( has_active_bionic ( bio_sonar ) && is_underwater() && calendar::once_every( 5_turns ) ) { + echo_pulse(); + } map &here = get_map(); // Search for traps in a larger area than before because this is the only // way we can "find" traps that aren't marked as visible. diff --git a/src/character.h b/src/character.h index 12814193e541a..abf15e3883105 100644 --- a/src/character.h +++ b/src/character.h @@ -137,7 +137,7 @@ enum vision_modes { URSINE_VISION, BOOMERED, DARKNESS, - TRANSPARENT_BLOCKED, + IR_VISION, VISION_CLAIRVOYANCE, VISION_CLAIRVOYANCE_PLUS, VISION_CLAIRVOYANCE_SUPER, diff --git a/src/sounds.cpp b/src/sounds.cpp index 5c44f37ef0bfb..00e8a14432a2a 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -719,21 +719,26 @@ void sounds::process_sound_markers( Character *you ) int err_offset; if( ( heard_volume + distance_to_sound ) / distance_to_sound < 2 ) { - err_offset = 3; + err_offset = rand() % 4; } else if( ( heard_volume + distance_to_sound ) / distance_to_sound < 3 ) { - err_offset = 2; + err_offset = rand() % 3; } else { err_offset = rand() % 2; } // Echolocation has to be fairly precise or it's worse than useless. + // However, it is never perfect. if ( sound.category == sound_t::sensory ) { if( ( heard_volume + distance_to_sound ) / distance_to_sound < 2 ) { err_offset = rand() % 3; } else if( ( heard_volume + distance_to_sound ) / distance_to_sound < 3 ) { err_offset = rand() % 2; } else { - err_offset = 0; + if( one_in( 3 ) ) { + err_offset = rand() % 2; + } else { + err_offset = 0; + } } } diff --git a/src/trap.cpp b/src/trap.cpp index f795f8b83c5d1..3f1b62043f93f 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -22,6 +22,7 @@ #include "string_formatter.h" static const flag_id json_flag_SONAR_DETECTABLE( "SONAR_DETECTABLE" ); +static const flag_id json_flag_ECHOLOCATION_DETECTABLE( "ECHOLOCATION_DETECTABLE" ); static const proficiency_id proficiency_prof_spotting( "prof_spotting" ); static const proficiency_id proficiency_prof_traps( "prof_traps" ); @@ -246,11 +247,20 @@ bool trap::is_trivial_to_spot() const return visibility <= 0 && !is_always_invisible(); } +// SONAR refers to ground-penetrating sonar and detects traps buried in the ground bool trap::detected_by_ground_sonar() const { return has_flag( json_flag_SONAR_DETECTABLE ); } +// Echolocation refers to both bat-style echolocation and underwater SONAR, and +// detects traps which are solid and unburied objects, aboveground or underwater. +// Isn't fine enough to detect very small traps ie caltrops +bool trap::detected_by_echolocation() const +{ + return has_flag( json_flag_ECHOLOCATION_DETECTABLE ); +} + bool trap::detect_trap( const tripoint &pos, const Character &p ) const { // * Buried landmines, the silent killer, have a visibility of 10. From 89b633d641997085e7beab061466904586aa4925 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:08:46 -0800 Subject: [PATCH 043/202] holy shit paws --- data/json/bionics.json | 7 +- data/json/effects.json | 6 + .../effects_on_condition/bionic_eocs.json | 14 ++ .../mutation_eocs/mutation_effect_eocs.json | 112 ++++++++- data/json/flags.json | 15 ++ data/json/mutations/mutations.json | 227 ++++++------------ data/json/traps.json | 8 +- src/character.cpp | 68 +++--- src/lightmap.cpp | 3 + src/trap.h | 4 + 10 files changed, 266 insertions(+), 198 deletions(-) diff --git a/data/json/bionics.json b/data/json/bionics.json index 04f7949a11245..31f167edcf799 100644 --- a/data/json/bionics.json +++ b/data/json/bionics.json @@ -1605,7 +1605,12 @@ "type": "bionic", "name": { "str": "Subaquatic Navigation System" }, "description": "While active, a tiny device in your inner ear periodically generates ultrasonic pulses which aid in navigation, provided you are underwater and able to hear.", - "occupied_bodyparts": [ [ "head", 1 ] ] + "occupied_bodyparts": [ [ "head", 1 ] ], + "act_cost": "1 J", + "trigger_cost": "5 J", + "flags": [ "BIONIC_TOGGLED" ], + "activated_eocs": [ "EOC_BIO_SONAR_activated" ], + "deactivated_eocs": [ "EOC_BIO_SONAR_deactivated" ] }, { "id": "bio_taser", diff --git a/data/json/effects.json b/data/json/effects.json index f7fc804370421..c6bf9a5fcdd73 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -4763,5 +4763,11 @@ "id": "natural_stance", "name": [ "Natural Stance" ], "desc": [ "You are positioned to take advantage of your mutated anatomy, and will favor any natural attacks you may have." ] + }, + { + "type": "effect_type", + "id": "subaquatic_sonar", + "name": [ "Subaquatic SONAR" ], + "desc": [ "Every few seconds, an ultrasonic pulse is broadcast from your location. If you're underwater and your hearing is good enough, you may be able to navigate by the pings that come back." ] } ] diff --git a/data/json/effects_on_condition/bionic_eocs.json b/data/json/effects_on_condition/bionic_eocs.json index 2363e12e6c596..9b1f08db099e4 100644 --- a/data/json/effects_on_condition/bionic_eocs.json +++ b/data/json/effects_on_condition/bionic_eocs.json @@ -141,5 +141,19 @@ "condition": { "u_has_bionics": "bio_leaky" }, "deactivate_condition": { "not": { "u_has_bionics": "bio_leaky" } }, "effect": [ { "u_mod_healthy": -1, "cap": -200 } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_BIO_SONAR_activated", + "recurrence": [ "1 seconds", "1 seconds" ], + "condition": { "not": { "u_has_effect": "subaquatic_sonar" } }, + "effect": [ { "u_add_effect": "subaquatic_sonar", "duration": "PERMANENT" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_BIO_SONAR_deactivated", + "recurrence": [ "1 seconds", "1 seconds" ], + "condition": { "u_has_effect": "subaquatic_sonar" }, + "effect": [ { "u_lose_effect": "subaquatic_sonar" } ] } ] diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index 7677633cff726..00f8f8d206ce5 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -700,43 +700,44 @@ "type": "effect_on_condition", "id": "EOC_social1_bonus", "recurrence": [ "10 minutes", "30 minutes" ], - "condition": { "and": [ { "u_has_flag": "SOCIAL1" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "narcosis" } } ] }, + "//": "Alcohol and sedatives prevent the bad effects of these EOCs. Antidepressants prevent (but don't remove) both the good and bad effects, keeping you at a baseline state.", + "condition": { "and": [ { "u_has_flag": "SOCIAL1" }, { "not": { "u_has_effect": "took_prozac" } }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_lose_effect": "social_dissatisfied" }, { "u_add_effect": "social_satisfied", "duration": "2 hours" } ] }, { "type": "effect_on_condition", "id": "EOC_social2_bonus", "recurrence": [ "10 minutes", "30 minutes" ], - "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, { "not": { "u_has_effect": "took_prozac" } }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_lose_effect": "social_dissatisfied" }, { "u_add_effect": "social_satisfied", "duration": "6 hours" }, { "u_lose_effect": "social_dissatisfied" } ] }, { "type": "effect_on_condition", "id": "EOC_social2_penalty", "recurrence": [ "12 hours", "48 hours" ], - "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "social_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "took_prozac" } }, { "not": { "u_has_effect": "valium" } }, { "not": { "u_has_effect": "took_xanax" } }, { "not": { "u_has_effect": "drunk" } }, { "not": { "u_has_effect": "social_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_add_effect": "social_dissatisfied", "duration": "PERMANENT", "intensity": { "math": [ "u_effect_intensity('social_dissatisfied') + 1" ] } }, { "u_lose_effect": "social_satisfied" }, { "u_message": "You could use some friendly company.", "type": "bad" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial1_bonus", "recurrence": [ "10 minutes", "30 minutes" ], - "condition": { "and": [ { "u_has_flag": "ASOCIAL1" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { "and": [ { "u_has_flag": "ASOCIAL1" }, { "not": { "u_has_effect": "took_prozac" } }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "2 hours" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial2_bonus", "recurrence": [ "10 minutes", "30 minutes" ], - "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "6 hours" }, { "u_lose_effect": "asocial_dissatisfied" }, { "u_message": "You'd really rather be by yourself.", "type": "bad" } ] + "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "took_prozac" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "6 hours" }, { "u_lose_effect": "asocial_dissatisfied" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial2_penalty", "recurrence": [ "12 hours", "48 hours" ], - "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_add_effect": "asocial_dissatisfied", "duration": "PERMANENT", "intensity": { "math": [ "u_effect_intensity('asocial_dissatisfied') + 1" ] } }, { "u_lose_effect": "asocial_satisfied" } ] + "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "not": { "u_has_effect": "took_prozac" } }, { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "valium" } }, { "not": { "u_has_effect": "took_xanax" } }, { "not": { "u_has_effect": "drunk" } }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, + "effect": [ { "u_add_effect": "asocial_dissatisfied", "duration": "PERMANENT", "intensity": { "math": [ "u_effect_intensity('asocial_dissatisfied') + 1" ] } }, { "u_lose_effect": "asocial_satisfied" }, { "u_message": "You'd really rather be by yourself.", "type": "bad" } ] }, { "type": "effect_on_condition", @@ -744,5 +745,100 @@ "recurrence": [ "30 minutes", "45 minutes" ], "condition": { "and": [ { "u_has_effect": "narcosis" }, { "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] } ] }, "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_lose_effect": "social_dissatisfied" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_social_reset_drugs", + "recurrence": [ "1 seconds", "1 seconds" ], + "//": "Sedatives are effective at acutely treating social anxiety/autophobia. Enjoy your benzo addiction, survivors.", + "condition": { "and": [ { "or": [ { "u_has_effect": "valium" }, { "u_has_effect": "took_xanax" } ] }, { "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] } ] }, + "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_lose_effect": "social_dissatisfied" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_quadruped_movement", + "//": "Handles movement for quadrupeds with paws. Avoid applying this willy-nilly - movement modes are a distinguishing feature of some mutation lines and giving the same ones to every animal that happens to be a quadruped IRL will homogenize things and make the lines less distinct.", + "triggers": [ + [ + { + "condition": { + "and": [ + { "u_has_move_mode": "run" }, + { "not": "u_can_drop_weapon" }, + { "u_has_flag": [ "QUADRUPED_CROUCH" ] }, + { "u_has_flag": [ "QUADRUPED" ] } + ] + }, + "msg_on": { "text": "You drop down on all fours." }, + "msg_off": { "text": "You rise up to walk on your hind feet." } + } + ] + ], + "enchantments": [ + { + "condition": { + "and": [ + { "u_has_move_mode": "crouch" }, + { "not": "u_can_drop_weapon" }, + { "u_has_any_trait": [ "THRESH_LUPINE", "THRESH_FELINE", "THRESH_BEAST", "THRESH_RABBIT" ] }, + { "u_has_flag": [ "QUADRUPED_CROUCH" ] }, + { "u_has_flag": [ "QUADRUPED" ] } + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] + }, + { + "condition": { + "and": [ + { "u_has_move_mode": "run" }, + { "not": "u_can_drop_weapon" }, + { "u_has_any_trait": [ "THRESH_LUPINE", "THRESH_FELINE", "THRESH_BEAST", "THRESH_RABBIT" ] }, + { "u_has_flag": [ "QUADRUPED_CROUCH" ] }, + { "u_has_flag": [ "QUADRUPED" ] } + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] + } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_half_quadruped_movement", + "//": "Quadruped mutants who have forepaws but not hindpaws can crouch-walk, but not run, on all fours.", + "triggers": [ + [ + { + "condition": { + "and": [ + { "u_has_move_mode": "crouch" }, + { "not": "u_can_drop_weapon" }, + { "not": { "u_has_flag": [ "QUADRUPED_RUN" ] } }, + { + "u_has_flag": [ "QUADRUPED" ] + } + ] + }, + "msg_on": { "text": "You drop down on all fours." }, + "msg_off": { "text": "You rise up to walk on your hind feet." } + } + ] +], +"enchantments": [ + { + "condition": { + "and": [ + { "u_has_move_mode": "crouch" }, + { "not": "u_can_drop_weapon" }, + { "not": { "u_has_flag": [ "QUADRUPED_RUN" ] } }, + { + "u_has_flag": [ "QUADRUPED" ] + } + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.5 } ] + } + ] } ] diff --git a/data/json/flags.json b/data/json/flags.json index 500ea162ca99b..de9cf35ec3832 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2433,5 +2433,20 @@ { "id": "ASOCIAL2", "type": "json_flag" + }, + { + "id": "QUADRUPED", + "type": "json_flag", + "//": "This post-threshold mutant has had enough of their brain rewired to use their paws (if they have them) for quadrupedal movement." + } + { + "id": "QUADRUPED_CROUCH", + "type": "json_flag", + "//": "This character goes down on all fours when they crouch, if their hands are free. Used for paw mutations." + }, + { + "id": "QUADRUPED_RUN", + "type": "json_flag", + "//": "This character can run or crouch on all fours if they have QUADRUPED_CROUCH, and gains the natural_stance effect. Used for paw mutations." } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index e524983c354b9..6b212e49cde60 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -4311,6 +4311,7 @@ "movecost_modifier": 1.35, "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], "destroys_gear": true, + "//": "Uses bespoke triggers rather than QUADRUPED_CROUCH as bat-crawling gives different stats.", "triggers": [ [ { @@ -4377,69 +4378,11 @@ "prereqs": [ "PADDED_FEET" ], "category": [ "LUPINE", "FELINE", "BEAST", "RABBIT" ], "changes_to": [ "RABBIT_FEET" ], + "flags": [ "QUADRUPED_RUN" ], "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], "restricts_gear": [ "foot_l", "foot_r" ], "destroys_gear": true, - "weight_capacity_modifier": 0.8, - "triggers": [ - [ - { - "condition": { - "and": [ - { "u_has_move_mode": "run" }, - { "not": "u_can_drop_weapon" }, - { "u_has_any_trait": [ "THRESH_BEAST", "THRESH_FELINE", "THRESH_LUPINE" ] }, - { "u_has_trait": "PAWS" } - ] - }, - "msg_on": { "text": "You drop down on all fours." }, - "msg_off": { "text": "You rise up to walk on your hind feet." } - } - ] - ], - "enchantments": [ - { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { "u_has_any_trait": [ "THRESH_LUPINE", "THRESH_FELINE", "THRESH_BEAST" ] }, - { "u_has_trait": "PAWS" } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], - "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] - }, - { - "condition": { - "and": [ - { "u_has_move_mode": "run" }, - { "not": "u_can_drop_weapon" }, - { "u_has_any_trait": [ "THRESH_LUPINE", "THRESH_FELINE", "THRESH_BEAST" ] }, - { "u_has_trait": "PAWS" } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], - "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] - }, - { - "condition": { - "or": [ - "u_can_drop_weapon", - { "u_has_move_mode": "walk" }, - { - "and": [ - { "not": { "u_has_trait": "THRESH_LUPINE" } }, - { "not": { "u_has_trait": "THRESH_FELINE" } }, - { "not": { "u_has_trait": "THRESH_BEAST" } } - ] - }, - { "not": { "u_has_trait": "PAWS" } } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.1 } ] - } - ] + "weight_capacity_modifier": 0.8 }, { "type": "mutation", @@ -7236,39 +7179,9 @@ "types": [ "HANDS" ], "prereqs": [ "NAILS", "CLAWS", "CLAWS_RETRACT", "CLAWS_RAT" ], "cancels": [ "TALONS" ], + "flags": [ "QUADRUPED_CROUCH" ], "changes_to": [ "PAWS_LARGE" ], - "category": [ "LUPINE", "RAT", "FELINE", "BEAST", "URSINE" ], - "triggers": [ - [ - { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { - "u_has_any_trait": [ "THRESH_BEAST", "THRESH_FELINE", "THRESH_LUPINE", "THRESH_URSINE", "THRESH_RAT", "THRESH_MOUSE", "THRESH_RABBIT" ] - } - ] - }, - "msg_on": { "text": "You drop down on all fours." }, - "msg_off": { "text": "You rise up to walk on your hind feet." } - } - ] - ], - "enchantments": [ - { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { - "u_has_any_trait": [ "THRESH_BEAST", "THRESH_FELINE", "THRESH_LUPINE", "THRESH_URSINE", "THRESH_RAT", "THRESH_MOUSE", "THRESH_RABBIT" ] - } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.5 } ] - } - ] + "category": [ "LUPINE", "RAT", "FELINE", "BEAST", "URSINE" ] }, { "type": "mutation", @@ -7295,38 +7208,8 @@ "types": [ "HANDS" ], "prereqs": [ "PAWS" ], "cancels": [ "TALONS" ], - "category": [ "BEAST", "URSINE" ], - "triggers": [ - [ - { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { - "u_has_any_trait": [ "THRESH_BEAST", "THRESH_FELINE", "THRESH_LUPINE", "THRESH_URSINE", "THRESH_RAT", "THRESH_MOUSE", "THRESH_RABBIT" ] - } - ] - }, - "msg_on": { "text": "You drop down on all fours." }, - "msg_off": { "text": "You rise up to walk on your hind feet." } - } - ] - ], - "enchantments": [ - { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { - "u_has_any_trait": [ "THRESH_BEAST", "THRESH_FELINE", "THRESH_LUPINE", "THRESH_URSINE", "THRESH_RAT", "THRESH_MOUSE", "THRESH_RABBIT" ] - } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.5 } ] - } - ] + "flags": [ "QUADRUPED_CROUCH" ], + "category": [ "BEAST", "URSINE" ] }, { "type": "mutation", @@ -9412,7 +9295,67 @@ "attack_text_npc": "%1$s kicks %2$s with their large feet!", "chance": 15, "strength_damage": { "damage_type": "bash", "amount": 1.5 } - } + }, + "triggers": [ + [ + { + "condition": { + "and": [ + { "u_has_move_mode": "run" }, + { "not": "u_can_drop_weapon" }, + { "u_has_any_trait": [ "THRESH_BEAST", "THRESH_FELINE", "THRESH_LUPINE", "THRESH_RABBIT" ] }, + { "u_has_flag": "QUADRUPED_CROUCH" } + ] + }, + "msg_on": { "text": "You drop down on all fours." }, + "msg_off": { "text": "You rise up to walk on your hind feet." } + } + ] + ], + "enchantments": [ + { + "condition": { + "and": [ + { "u_has_move_mode": "crouch" }, + { "not": "u_can_drop_weapon" }, + { "u_has_any_trait": [ "THRESH_LUPINE", "THRESH_FELINE", "THRESH_BEAST", "THRESH_RABBIT" ] }, + { "u_has_flag": "QUADRUPED_CROUCH" } + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] + }, + { + "condition": { + "and": [ + { "u_has_move_mode": "run" }, + { "not": "u_can_drop_weapon" }, + { "u_has_any_trait": [ "THRESH_LUPINE", "THRESH_FELINE", "THRESH_BEAST", "THRESH_RABBIT" ] }, + { "u_has_flag": "QUADRUPED_CROUCH" } + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] + }, + { + "condition": { + "or": [ + "u_can_drop_weapon", + { "u_has_move_mode": "walk" }, + { + "and": [ + { "not": { "u_has_trait": "THRESH_LUPINE" } }, + { "not": { "u_has_trait": "THRESH_FELINE" } }, + { "not": { "u_has_trait": "THRESH_BEAST" } }, + { "not": { "u_has_trait": "THRESH_RABBIT" } } + ] + }, + { "not": { "u_has_flag": "QUADRUPED_CROUCH" } } + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.1 } ] + } + ] }, { "type": "mutation", @@ -9439,38 +9382,8 @@ "types": [ "HANDS" ], "prereqs": [ "NAILS" ], "cancels": [ "TALONS" ], - "category": [ "RABBIT" ], - "triggers": [ - [ - { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { - "u_has_any_trait": [ "THRESH_BEAST", "THRESH_FELINE", "THRESH_LUPINE", "THRESH_URSINE", "THRESH_RAT", "THRESH_MOUSE", "THRESH_RABBIT" ] - } - ] - }, - "msg_on": { "text": "You drop down on all fours." }, - "msg_off": { "text": "You rise up to walk on your hind feet." } - } - ] - ], - "enchantments": [ - { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { - "u_has_any_trait": [ "THRESH_BEAST", "THRESH_FELINE", "THRESH_LUPINE", "THRESH_URSINE", "THRESH_RAT", "THRESH_MOUSE", "THRESH_RABBIT" ] - } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.5 } ] - } - ] + "flags": [ "QUADRUPED_CROUCH" ], + "category": [ "RABBIT" ] }, { "type": "mutation", diff --git a/data/json/traps.json b/data/json/traps.json index 2c134da6da0b6..2369742a8719a 100644 --- a/data/json/traps.json +++ b/data/json/traps.json @@ -560,12 +560,11 @@ "color": "brown", "memorial_male": { "ctxt": "memorial_male", "str": "Fell in a pit." }, "memorial_female": { "ctxt": "memorial_female", "str": "Fell in a pit." }, - "flags": [ "ECHOLOCATION_DETECTABLE" ], + "flags": [ "ECHOLOCATION_DETECTABLE", "PIT" ], "symbol": "0", "visibility": 0, "avoidance": 8, "difficulty": 99, - "flags": [ "PIT" ], "action": "pit", "vehicle_data": { "damage": 500 } }, @@ -576,12 +575,11 @@ "color": "blue", "memorial_male": { "ctxt": "memorial_male", "str": "Fell into a spiked pit." }, "memorial_female": { "ctxt": "memorial_female", "str": "Fell into a spiked pit." }, - "flags": [ "ECHOLOCATION_DETECTABLE" ], + "flags": [ "ECHOLOCATION_DETECTABLE", "PIT" ], "symbol": "0", "visibility": 0, "avoidance": 8, "difficulty": 99, - "flags": [ "PIT" ], "action": "pit_spikes", "vehicle_data": { "damage": 500 } }, @@ -783,7 +781,7 @@ "visibility": 0, "avoidance": 8, "difficulty": 99, - "flags": [ "PIT" ], + "flags": [ "PIT", "ECHOLOCATION_DETECTABLE" ], "action": "pit_glass", "vehicle_data": { "damage": 500 } }, diff --git a/src/character.cpp b/src/character.cpp index 03d0b8bef65ef..1f5e0009511c3 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -168,7 +168,6 @@ static const bionic_id bio_ods( "bio_ods" ); static const bionic_id bio_railgun( "bio_railgun" ); static const bionic_id bio_shock_absorber( "bio_shock_absorber" ); static const bionic_id bio_sleep_shutdown( "bio_sleep_shutdown" ); -static const bionic_id bio_sonar( "bio_sonar" ); static const bionic_id bio_soporific( "bio_soporific" ); static const bionic_id bio_synlungs( "bio_synlungs" ); static const bionic_id bio_targeting( "bio_targeting" ); @@ -275,6 +274,7 @@ static const efftype_id effect_slept_through_alarm( "slept_through_alarm" ); static const efftype_id effect_slippery_terrain( "slippery_terrain" ); static const efftype_id effect_stumbled_into_invisible( "stumbled_into_invisible" ); static const efftype_id effect_stunned( "stunned" ); +static const efftype_id effect_subaquatic_sonar( "subaquatic_sonar" ); static const efftype_id effect_tapeworm( "tapeworm" ); static const efftype_id effect_tied( "tied" ); static const efftype_id effect_weed_high( "weed_high" ); @@ -451,6 +451,7 @@ static const trait_id trait_INFRESIST( "INFRESIST" ); static const trait_id trait_INSOMNIA( "INSOMNIA" ); static const trait_id trait_INT_SLIME( "INT_SLIME" ); static const trait_id trait_LEG_TENT_BRACE( "LEG_TENT_BRACE" ); +static const trait_id trait_LEGS_BAT( "LEGS_BAT" ); static const trait_id trait_LIGHTSTEP( "LIGHTSTEP" ); static const trait_id trait_LOVES_BOOKS( "LOVES_BOOKS" ); static const trait_id trait_MASOCHIST( "MASOCHIST" ); @@ -503,6 +504,7 @@ static const trait_id trait_WATERSLEEP( "WATERSLEEP" ); static const trait_id trait_WEB_SPINNER( "WEB_SPINNER" ); static const trait_id trait_WEB_WALKER( "WEB_WALKER" ); static const trait_id trait_WEB_WEAVER( "WEB_WEAVER" ); +static const trait_id trait_WINGS_BAT( "WINGS_BAT" ); static const vitamin_id vitamin_calcium( "calcium" ); static const vitamin_id vitamin_iron( "iron" ); @@ -2188,9 +2190,9 @@ bool Character::is_runallfours() const { bool allfour = false; if( move_mode->type() == move_mode_type::RUNNING && !is_armed() ) { - if( has_trait( trait_PAWS ) && ( has_trait( trait_THRESH_LUPINE ) || + if( ( has_trait( trait_PAWS ) && ( has_trait( trait_THRESH_LUPINE ) || has_trait( trait_THRESH_FELINE ) - || has_trait( trait_THRESH_BEAST ) ) ) { + || has_trait( trait_THRESH_BEAST ) ) || ( has_trait( trait_WINGS_BAT ) && has_trait( trait_LEGS_BAT ) ) ) ) { allfour = true; } } @@ -10567,17 +10569,22 @@ void Character::echo_pulse() map &here = get_map(); int echo_volume = 0; int pulse_range = 10; - if( has_active_bionic( bio_sonar ) && is_underwater() ) { + // Sound travels farther underwater + if( has_effect( effect_subaquatic_sonar ) && is_underwater() ) { pulse_range = 16; - sounds::sound( this->pos(), 5, sounds::sound_t::movement, _( "boop" ), true, + sounds::sound( this->pos(), 5, sounds::sound_t::movement, _( "boop." ), true, "none", "none" ); + } else if( !has_effect( effect_subaquatic_sonar ) && is_underwater() ) { + pulse_range = 0; + add_msg_if_player( m_warning, _( "You can't echolocate underwater!" ) ); + return; } else { - sounds::sound( this->pos(), 5, sounds::sound_t::movement, _( "chirp" ), true, + sounds::sound( this->pos(), 5, sounds::sound_t::movement, _( "chirp." ), true, "none", "none" ); - } + } for( tripoint origin : points_in_radius( pos(), pulse_range ) ) { if ( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, 2 ) ) { - sounds::sound( origin, 30, sounds::sound_t::sensory, _( "click" ), true, + sounds::sound( origin, 6, sounds::sound_t::sensory, _( "click." ), true, "none", "none" ); } const trap &tr = here.tr_at( origin ); @@ -10609,6 +10616,7 @@ void Character::echo_pulse() debugmsg( "ERROR: Invalid Creature size class." ); break; } + // Some monsters are harder to get a read on if( critter->has_flag( mon_flag_PLASTIC ) ) { echo_volume -= std::max( 1, 1 ); } @@ -10616,43 +10624,44 @@ void Character::echo_pulse() echo_volume -= std::max( 1, 1 ); } const char *echo_string = nullptr; - // bio_targeting has a HUD and automatically converts the audio data to visual feedback - if( has_active_bionic( bio_targeting ) && !has_effect( effect_blind ) && has_active_bionic( bio_sonar ) ) + // bio_targeting has a visual HUD and automatically interprets the raw audio data, + // but only for electronic SONAR. Its designers didn't anticipate bat mutations + if( has_bionic( bio_targeting ) && has_effect( effect_subaquatic_sonar ) ) switch( echo_volume ) { case 1: - echo_string = _( "Entity: [Tiny]" ); + echo_string = _( "Target [Tiny]." ); break; case 2: - echo_string = _( "Entity: [Small]" ); + echo_string = _( "Target [Small]." ); break; case 3: - echo_string = _( "Entity: [Medium]" ); + echo_string = _( "Target [Medium]." ); break; case 4: - echo_string = _( "Warning! Entity: [Large]" ); + echo_string = _( "Warning! Target [Large]." ); break; case 5: - echo_string = _( "Warning! Entity [Huge]" ); + echo_string = _( "Warning! Target [Huge]." ); break; default: debugmsg( "ERROR: Invalid echo string." ); break; - } else if ( ( !has_active_bionic( bio_targeting ) || has_effect( effect_blind ) ) && has_active_bionic( bio_sonar ) ) { + } else if ( !has_bionic( bio_targeting ) && has_effect( effect_subaquatic_sonar ) ) { switch( echo_volume ) { case 1: - echo_string = _( "tick" ); + echo_string = _( "tick." ); break; case 2: - echo_string = _( "pii" ); + echo_string = _( "pii." ); break; case 3: - echo_string = _( "Ping" ); + echo_string = _( "ping." ); break; case 4: - echo_string = _( "Pong" ); + echo_string = _( "pong." ); break; case 5: - echo_string = _( "Booop" ); + echo_string = _( "booop." ); break; default: debugmsg( "ERROR: Invalid echo string." ); @@ -10661,24 +10670,28 @@ void Character::echo_pulse() } else { switch( echo_volume ) { case 1: - echo_string = _( "ch" ); + echo_string = _( "ch." ); break; case 2: - echo_string = _( "chk" ); + echo_string = _( "chk." ); break; case 3: - echo_string = _( "chhk" ); + echo_string = _( "chhk." ); break; case 4: - echo_string = _( "chkch" ); + echo_string = _( "chkch." ); break; case 5: - echo_string = _( "chkchh" ); + echo_string = _( "chkchh." ); break; default: debugmsg( "ERROR: Invalid echo string." ); break; } + } + // It's not moving. Must be an obstacle + if( critter->has_flag( mon_flag_IMMOBILE ) ) { + echo_string = _( "click." ); } sounds::sound( origin, echo_volume, sounds::sound_t::sensory, _( echo_string ), false, "none", "none" ); @@ -12944,7 +12957,7 @@ void Character::search_surroundings() if( controlling_vehicle ) { return; } - if( has_active_bionic ( bio_sonar ) && is_underwater() && calendar::once_every( 5_turns ) ) { + if( has_effect ( effect_subaquatic_sonar ) && is_underwater() && calendar::once_every( 4_turns ) ) { echo_pulse(); } map &here = get_map(); @@ -12956,6 +12969,7 @@ void Character::search_surroundings() if( tr.is_null() || tp == pos() ) { continue; } + // Note that echolocation and SONAR also do this separately in echo_pulse() if( has_active_bionic( bio_ground_sonar ) && !knows_trap( tp ) && tr.detected_by_ground_sonar() ) { const std::string direction = direction_name( direction_from( pos(), tp ) ); add_msg_if_player( m_warning, _( "Your ground sonar detected a %1$s to the %2$s!" ), diff --git a/src/lightmap.cpp b/src/lightmap.cpp index a4e824faec420..c310189007ede 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -143,6 +143,8 @@ bool map::build_transparency_cache( const int zlev ) value *= sight_penalty; } float value_wo_fields = value; + Character &player_character = get_player_character(); + if( !player_character.vision_mode_cache[IR_VISION] ) { for( const auto &fld : cur_submap->get_field( sp ) ) { const field_intensity_level &i_level = fld.second.get_intensity_level(); if( i_level.transparent ) { @@ -151,6 +153,7 @@ bool map::build_transparency_cache( const int zlev ) // Fields are either transparent or not, however we want some to be translucent value = value * i_level.translucency; } + } return std::make_pair( value, value_wo_fields ); }; diff --git a/src/trap.h b/src/trap.h index d435046120b8e..518d9d423ea27 100644 --- a/src/trap.h +++ b/src/trap.h @@ -239,6 +239,10 @@ struct trap { * Whether this kind of trap will be detected by ground sonar (e.g. via the bionic). */ bool detected_by_ground_sonar() const; + /** + * Whether this kind of trap will be detected by regular sonar ( it is not buried and it's a solid object ). + */ + bool detected_by_echolocation() const; /** Player has not yet seen the trap and returns the variable chance, at this moment, of whether the trap is seen or not. */ bool detect_trap( const tripoint &pos, const Character &p ) const; From ea9f2f0aebe85083d3520015e23712b7c02cfbf9 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Mon, 22 Jan 2024 15:15:44 -0800 Subject: [PATCH 044/202] oh lord --- .vscode/settings.json | 3 + data/json/effects.json | 35 ++- .../effects_on_condition/effects_eocs.json | 20 +- .../mutation_eocs/mutation_effect_eocs.json | 220 ++++++++++++------ data/json/enchantments.json | 41 ++++ data/json/flags.json | 9 +- data/json/items/comestibles/carnivore.json | 4 +- data/json/mutations/mutations.json | 76 +----- src/activity_handlers.cpp | 12 +- src/character.cpp | 194 +++++++-------- src/character.h | 1 - src/condition.cpp | 28 +-- src/condition.h | 2 +- src/consumption.cpp | 12 +- src/creature.cpp | 6 +- src/lightmap.cpp | 13 +- src/melee.cpp | 50 +++- src/ranged.cpp | 3 +- src/sounds.cpp | 4 +- 19 files changed, 405 insertions(+), 328 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 20bd09ea82c04..4451dc963898c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -64,4 +64,7 @@ "github-actions.remote-name": "upstream", // NOTE: This disable the plugins formatting so astyle is used. "C_Cpp.formatting": "disabled", + "files.associations": { + "vector": "cpp" + }, } diff --git a/data/json/effects.json b/data/json/effects.json index c6bf9a5fcdd73..d57146d7adae1 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -348,10 +348,10 @@ "type": "effect_type", "id": "sneezing", "name": [ "Sneezing" ], - "desc": [ "Effect to briefly distract you while you sneeze. You should not be able to see this." ], + "rating": "bad", + "desc": [ "It's hard to pay attention to anything when you're in the middle of sneezing!" ], "max_duration": "1 s", - "base_mods": { "speed_mod": [ -20 ], "dex_mod": [ -2 ], "per_mod": [ -4 ] }, - "show_in_info": false + "base_mods": { "speed_mod": [ -20 ], "dex_mod": [ -2 ], "per_mod": [ -4 ] } }, { "type": "effect_type", @@ -3771,9 +3771,7 @@ "type": "effect_type", "id": "social_satisfied", "name": [ "Good Company" ], - "desc": [ - "It feels good to spend time around others." - ], + "desc": [ "It feels good to spend time around others." ], "max_intensity": 1, "rating": "good" }, @@ -3793,9 +3791,7 @@ "type": "effect_type", "id": "asocial_satisfied", "name": [ "Alone" ], - "desc": [ - "Being on your own makes you feel good." - ], + "desc": [ "Being on your own makes you feel good." ], "max_intensity": 1, "rating": "good" }, @@ -4758,16 +4754,33 @@ "flags": [ "FEATHER_FALL" ], "max_duration": "1 s" }, + { + "type": "effect_type", + "id": "quadruped_full", + "name": [ "" ], + "//": "Allows fully-quadrupedal mutants to run and crouch on all fours. For annoying reasons, this is handled via enchantments." + }, + { + "type": "effect_type", + "id": "quadruped_half", + "name": [ "" ], + "//": "Allows quadrupedal mutants with front paws only to crouch-walk slightly faster than normal. For annoying reasons, this is handled via enchantments." + }, { "type": "effect_type", "id": "natural_stance", "name": [ "Natural Stance" ], - "desc": [ "You are positioned to take advantage of your mutated anatomy, and will favor any natural attacks you may have." ] + "rating": "good", + "desc": [ + "You are positioned to take advantage of your mutated anatomy, and will favor any natural attacks you may have. Apart from potential changes to move speed, you receive no penalties for crouching in melee combat." + ] }, { "type": "effect_type", "id": "subaquatic_sonar", "name": [ "Subaquatic SONAR" ], - "desc": [ "Every few seconds, an ultrasonic pulse is broadcast from your location. If you're underwater and your hearing is good enough, you may be able to navigate by the pings that come back." ] + "desc": [ + "Every few seconds, an ultrasonic pulse is broadcast from your location. If you're underwater and your hearing is good enough, you may be able to navigate by the pings that come back." + ] } ] diff --git a/data/json/effects_on_condition/effects_eocs.json b/data/json/effects_on_condition/effects_eocs.json index 34a02cb1cf75b..3539252e21432 100644 --- a/data/json/effects_on_condition/effects_eocs.json +++ b/data/json/effects_on_condition/effects_eocs.json @@ -21,15 +21,7 @@ "id": "eoc_social_satisfied", "recurrence": [ "1 minutes", "5 minutes" ], "condition": { "u_has_effect": "social_satisfied" }, - "effect": [ - { - "u_add_morale": "morale_social", - "bonus": 6, - "max_bonus": 6, - "duration": "1 days", - "decay_start": "1 days" - } - ], + "effect": [ { "u_add_morale": "morale_social", "bonus": 6, "max_bonus": 6, "duration": "1 days", "decay_start": "1 days" } ], "false_effect": [ { "u_lose_morale": "morale_social" } ] }, { @@ -53,15 +45,7 @@ "id": "eoc_asocial_satisfied", "recurrence": [ "1 minutes", "5 minutes" ], "condition": { "u_has_effect": "asocial_satisfied" }, - "effect": [ - { - "u_add_morale": "morale_asocial", - "bonus": 10, - "max_bonus": 10, - "duration": "1 days", - "decay_start": "1 days" - } - ], + "effect": [ { "u_add_morale": "morale_asocial", "bonus": 10, "max_bonus": 10, "duration": "1 days", "decay_start": "1 days" } ], "false_effect": [ { "u_lose_morale": "morale_asocial" } ] }, { diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index 00f8f8d206ce5..eb652fe4e28af 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -701,49 +701,129 @@ "id": "EOC_social1_bonus", "recurrence": [ "10 minutes", "30 minutes" ], "//": "Alcohol and sedatives prevent the bad effects of these EOCs. Antidepressants prevent (but don't remove) both the good and bad effects, keeping you at a baseline state.", - "condition": { "and": [ { "u_has_flag": "SOCIAL1" }, { "not": { "u_has_effect": "took_prozac" } }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { + "and": [ + { "u_has_flag": "SOCIAL1" }, + { "not": { "u_has_effect": "took_prozac" } }, + { "u_can_see_friends": 1 }, + { "not": { "u_has_effect": "narcosis" } } + ] + }, "effect": [ { "u_lose_effect": "social_dissatisfied" }, { "u_add_effect": "social_satisfied", "duration": "2 hours" } ] }, { "type": "effect_on_condition", "id": "EOC_social2_bonus", "recurrence": [ "10 minutes", "30 minutes" ], - "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, { "not": { "u_has_effect": "took_prozac" } }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_lose_effect": "social_dissatisfied" }, { "u_add_effect": "social_satisfied", "duration": "6 hours" }, { "u_lose_effect": "social_dissatisfied" } ] + "condition": { + "and": [ + { "u_has_flag": "SOCIAL2" }, + { "not": { "u_has_effect": "took_prozac" } }, + { "u_can_see_friends": 1 }, + { "not": { "u_has_effect": "narcosis" } } + ] + }, + "effect": [ + { "u_lose_effect": "social_dissatisfied" }, + { "u_add_effect": "social_satisfied", "duration": "6 hours" }, + { "u_lose_effect": "social_dissatisfied" } + ] }, { "type": "effect_on_condition", "id": "EOC_social2_penalty", "recurrence": [ "12 hours", "48 hours" ], - "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "took_prozac" } }, { "not": { "u_has_effect": "valium" } }, { "not": { "u_has_effect": "took_xanax" } }, { "not": { "u_has_effect": "drunk" } }, { "not": { "u_has_effect": "social_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_add_effect": "social_dissatisfied", "duration": "PERMANENT", "intensity": { "math": [ "u_effect_intensity('social_dissatisfied') + 1" ] } }, { "u_lose_effect": "social_satisfied" }, { "u_message": "You could use some friendly company.", "type": "bad" } ] + "condition": { + "and": [ + { "u_has_flag": "SOCIAL2" }, + { "u_can_see_friends": 0 }, + { "not": { "u_has_effect": "took_prozac" } }, + { "not": { "u_has_effect": "valium" } }, + { "not": { "u_has_effect": "took_xanax" } }, + { "not": { "u_has_effect": "drunk" } }, + { "not": { "u_has_effect": "social_satisfied" } }, + { "not": { "u_has_effect": "narcosis" } } + ] + }, + "effect": [ + { + "u_add_effect": "social_dissatisfied", + "duration": "PERMANENT", + "intensity": { "math": [ "u_effect_intensity('social_dissatisfied') + 1" ] } + }, + { "u_lose_effect": "social_satisfied" }, + { "u_message": "You could use some friendly company.", "type": "bad" } + ] }, { "type": "effect_on_condition", "id": "EOC_asocial1_bonus", "recurrence": [ "10 minutes", "30 minutes" ], - "condition": { "and": [ { "u_has_flag": "ASOCIAL1" }, { "not": { "u_has_effect": "took_prozac" } }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "narcosis" } } ] }, + "condition": { + "and": [ + { "u_has_flag": "ASOCIAL1" }, + { "not": { "u_has_effect": "took_prozac" } }, + { "u_can_see_friends": 0 }, + { "not": { "u_has_effect": "narcosis" } } + ] + }, "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "2 hours" } ] }, { "type": "effect_on_condition", "id": "EOC_asocial2_bonus", "recurrence": [ "10 minutes", "30 minutes" ], - "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "u_can_see_friends": 0 }, { "not": { "u_has_effect": "took_prozac" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "6 hours" }, { "u_lose_effect": "asocial_dissatisfied" } ] + "condition": { + "and": [ + { "u_has_flag": "ASOCIAL2" }, + { "u_can_see_friends": 0 }, + { "not": { "u_has_effect": "took_prozac" } }, + { "not": { "u_has_effect": "narcosis" } } + ] + }, + "effect": [ + { "u_lose_effect": "asocial_dissatisfied" }, + { "u_add_effect": "asocial_satisfied", "duration": "6 hours" }, + { "u_lose_effect": "asocial_dissatisfied" } + ] }, { "type": "effect_on_condition", "id": "EOC_asocial2_penalty", "recurrence": [ "12 hours", "48 hours" ], - "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "not": { "u_has_effect": "took_prozac" } }, { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "valium" } }, { "not": { "u_has_effect": "took_xanax" } }, { "not": { "u_has_effect": "drunk" } }, { "u_can_see_friends": 1 }, { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_add_effect": "asocial_dissatisfied", "duration": "PERMANENT", "intensity": { "math": [ "u_effect_intensity('asocial_dissatisfied') + 1" ] } }, { "u_lose_effect": "asocial_satisfied" }, { "u_message": "You'd really rather be by yourself.", "type": "bad" } ] + "condition": { + "and": [ + { "u_has_flag": "ASOCIAL2" }, + { "not": { "u_has_effect": "took_prozac" } }, + { "not": { "u_has_effect": "asocial_satisfied" } }, + { "not": { "u_has_effect": "valium" } }, + { "not": { "u_has_effect": "took_xanax" } }, + { "not": { "u_has_effect": "drunk" } }, + { "u_can_see_friends": 1 }, + { "not": { "u_has_effect": "asocial_satisfied" } }, + { "not": { "u_has_effect": "narcosis" } } + ] + }, + "effect": [ + { + "u_add_effect": "asocial_dissatisfied", + "duration": "PERMANENT", + "intensity": { "math": [ "u_effect_intensity('asocial_dissatisfied') + 1" ] } + }, + { "u_lose_effect": "asocial_satisfied" }, + { "u_message": "You'd really rather be by yourself.", "type": "bad" } + ] }, { "type": "effect_on_condition", "id": "EOC_social_reset_sleep", "recurrence": [ "30 minutes", "45 minutes" ], - "condition": { "and": [ { "u_has_effect": "narcosis" }, { "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] } ] }, + "condition": { + "and": [ + { "u_has_effect": "narcosis" }, + { "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] } + ] + }, "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_lose_effect": "social_dissatisfied" } ] }, { @@ -751,94 +831,86 @@ "id": "EOC_social_reset_drugs", "recurrence": [ "1 seconds", "1 seconds" ], "//": "Sedatives are effective at acutely treating social anxiety/autophobia. Enjoy your benzo addiction, survivors.", - "condition": { "and": [ { "or": [ { "u_has_effect": "valium" }, { "u_has_effect": "took_xanax" } ] }, { "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] } ] }, + "condition": { + "and": [ + { "or": [ { "u_has_effect": "valium" }, { "u_has_effect": "took_xanax" } ] }, + { "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] } + ] + }, "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_lose_effect": "social_dissatisfied" } ] }, { "type": "effect_on_condition", "id": "EOC_quadruped_movement", - "//": "Handles movement for quadrupeds with paws. Avoid applying this willy-nilly - movement modes are a distinguishing feature of some mutation lines and giving the same ones to every animal that happens to be a quadruped IRL will homogenize things and make the lines less distinct.", + "//": "Quadrupedal post-thresh mutants with paws in front and back get these effects when they're running or crouching and empty-handed.", "triggers": [ [ { "condition": { "and": [ - { "u_has_move_mode": "run" }, + { "or": [ { "u_has_move_mode": "run" }, { "u_has_move_mode": "crouch" } ] }, { "not": "u_can_drop_weapon" }, { "u_has_flag": [ "QUADRUPED_CROUCH" ] }, + { "u_has_flag": [ "QUADRUPED_RUN" ] }, { "u_has_flag": [ "QUADRUPED" ] } ] }, - "msg_on": { "text": "You drop down on all fours." }, - "msg_off": { "text": "You rise up to walk on your hind feet." } + "effect": [ { "u_add_effect": "quadruped_full", "duration": "PERMANENT" }, { "u_add_effect": "natural_stance", "duration": "PERMANENT" }, { "u_message": "You drop down on all fours." } ] } ] - ], - "enchantments": [ - { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { "u_has_any_trait": [ "THRESH_LUPINE", "THRESH_FELINE", "THRESH_BEAST", "THRESH_RABBIT" ] }, - { "u_has_flag": [ "QUADRUPED_CROUCH" ] }, - { "u_has_flag": [ "QUADRUPED" ] } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], - "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] - }, - { - "condition": { - "and": [ - { "u_has_move_mode": "run" }, - { "not": "u_can_drop_weapon" }, - { "u_has_any_trait": [ "THRESH_LUPINE", "THRESH_FELINE", "THRESH_BEAST", "THRESH_RABBIT" ] }, - { "u_has_flag": [ "QUADRUPED_CROUCH" ] }, - { "u_has_flag": [ "QUADRUPED" ] } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], - "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] - } ] }, { "type": "effect_on_condition", - "id": "EOC_half_quadruped_movement", - "//": "Quadruped mutants who have forepaws but not hindpaws can crouch-walk, but not run, on all fours.", + "id": "EOC_quadruped_movement_half", + "//": "This is for post-thresh mutants with paws but not animal back legs. It's a much more minor buff, and only to crawling.", "triggers": [ - [ - { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { "not": { "u_has_flag": [ "QUADRUPED_RUN" ] } }, - { - "u_has_flag": [ "QUADRUPED" ] - } - ] - }, - "msg_on": { "text": "You drop down on all fours." }, - "msg_off": { "text": "You rise up to walk on your hind feet." } - } - ] -], -"enchantments": [ + [ + { + "condition": { + "and": [ + { "u_has_move_mode": "crouch" }, + { "not": "u_can_drop_weapon" }, + { "not": { "u_has_flag": [ "QUADRUPED_RUN" ] } }, + { "u_has_flag": [ "QUADRUPED_CROUCH" ] }, + { "u_has_flag": [ "QUADRUPED" ] } + ] + }, + "effect": [ { "u_add_effect": "quadruped_half", "duration": "PERMANENT" }, { "u_message": "You drop down on all fours." } ] + } + ] + ] + }, { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { "not": { "u_has_flag": [ "QUADRUPED_RUN" ] } }, + "type": "effect_on_condition", + "id": "EOC_quadruped_full_lose", + "triggers": [ + [ { - "u_has_flag": [ "QUADRUPED" ] + "condition": { + "and": [ { "u_has_effect": "quadruped_full" }, + { "or": [ { "not": { "or": [ { "u_has_move_mode": "crouch" }, { "u_has_move_mode": "run" } ] } }, { "not": "u_can_drop_weapon" } ] } + ] + }, + "effect": [ { "u_lose_effect": "quadruped_full", "duration": "PERMANENT" }, { "u_lose_effect": "natural_stance", "duration": "PERMANENT" }, { "u_message": "You stop crawling on all fours." } ] } ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.5 } ] - } - ] + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_quadruped_half_lose", + "triggers": [ + [ + { + "condition": { + "and": [ { "u_has_effect": "quadruped_half" }, + { "or": { "not": [ { "u_has_move_mode": "crouch" }, { "not": "u_can_drop_weapon" } ] } } + ] + }, + "effect": [ { "u_lose_effect": "quadruped_half", "duration": "PERMANENT" }, { "u_message": "You stop crawling on all fours." } ] + } + ] + ] } ] diff --git a/data/json/enchantments.json b/data/json/enchantments.json index 145dbb57f8c26..db2768c9dbb29 100644 --- a/data/json/enchantments.json +++ b/data/json/enchantments.json @@ -113,5 +113,46 @@ "description": "Your rapidly buzzing wings propel you with extra speed.", "condition": "ALWAYS", "values": [ { "value": "REGEN_STAMINA", "add": -110 } ] + }, + { + "id": "ench_quadruped_movement_crouch", + "//": "quadruped_movement_crouch and quadruped_movement_run are for quadrupeds with all four of their animal limbs.", + "type": "enchantment", + "condition": { + "and": [ + { "u_has_effect": "quadruped_full" }, + { "u_has_move_mode": "crouch" }, + { "not": "u_can_drop_weapon" }, + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] + }, + { + "id": "ench_quadruped_movement_run", + "type": "enchantment", + "condition": { + "and": [ + { "u_has_effect": "quadruped_full" }, + { "u_has_move_mode": "run" }, + { "not": "u_can_drop_weapon" }, + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], + "//": "Only apply all_fours here, as it's not needed when actually crouching.", + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 }, { "effect": "all_fours", "intensity": 1 } ] + }, + { + "id": "ench_quadruped_movement_half", + "//": "For quadrupeds who only have their front paws. Makes crouch-walking a bit faster, but that's all.", + "type": "enchantment", + "condition": { + "and": [ + { "u_has_effect": "quadruped_half" }, + { "u_has_move_mode": "crouch" }, + { "not": "u_can_drop_weapon" }, + ] + }, + "values": [ { "value": "MOVE_COST", "multiply": -0.5 } ] } ] diff --git a/data/json/flags.json b/data/json/flags.json index de9cf35ec3832..d0f79c4b4de46 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2388,11 +2388,6 @@ "type": "json_flag", "info": "The character bleeds even slower than normal, losing blood at 1/3rd the normal rate." }, - { - "id": "QUADRUPED", - "type": "json_flag", - "//": "This character receives no penalty for crouching in melee." - }, { "id": "NATURAL_WEAPON", "type": "json_flag", @@ -2437,8 +2432,8 @@ { "id": "QUADRUPED", "type": "json_flag", - "//": "This post-threshold mutant has had enough of their brain rewired to use their paws (if they have them) for quadrupedal movement." - } + "//": "This post-threshold mutant has had enough of their brain replaced to use their paws (if they have them) for quadrupedal movement." + }, { "id": "QUADRUPED_CROUCH", "type": "json_flag", diff --git a/data/json/items/comestibles/carnivore.json b/data/json/items/comestibles/carnivore.json index 2dbb42d6208db..0b9e86bf5e80e 100644 --- a/data/json/items/comestibles/carnivore.json +++ b/data/json/items/comestibles/carnivore.json @@ -1307,7 +1307,7 @@ "comestible_type": "DRINK", "symbol": "~", "price_postapoc": 10, - "description": "Blood, possibly that of a human. Disgusting!", + "description": "Blood extracted from a human being.", "price": 0, "material": [ "hblood" ], "volume": "250 ml", @@ -1479,7 +1479,7 @@ "type": "COMESTIBLE", "copy-from": "meat_tainted", "name": { "str_sp": "tainted blood" }, - "description": "Blood that's obviously unhealthy. You could eat it, but it will poison you.", + "description": "Blood that's obviously unhealthy. You could drink it, but it will poison you.", "comestible_type": "DRINK", "looks_like": "blood", "symbol": "~", diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 6b212e49cde60..01a2c87b0b73c 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -2500,7 +2500,7 @@ "points": 2, "visibility": 3, "ugliness": 3, - "description": "Your teeth are so sharp, you can use them to shave the fur from your victims before you feed. What's more, anticoagulants in your saliva will keep the blood flowing long after it should have stopped.", + "description": "Your teeth are so sharp, you can use them to shave the fur from your victims before you feed. What's more, anticoagulants in your saliva will keep their blood flowing long after it should have clotted.", "types": [ "TEETH" ], "prereqs": [ "FANGS" ], "threshreq": [ "THRESH_CHIROPTERAN" ], @@ -4308,10 +4308,11 @@ "types": [ "LEGS" ], "prereqs": [ "PADDED_FEET", "WINGS_BAT" ], "category": [ "CHIROPTERAN" ], + "threshreq": [ "THRESH_CHIROPTERAN" ], "movecost_modifier": 1.35, "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], "destroys_gear": true, - "//": "Uses bespoke triggers rather than QUADRUPED_CROUCH as bat-crawling gives different stats.", + "//": "Uses bespoke triggers rather than QUADRUPED_CROUCH as bat-crawling gives different buffs.", "triggers": [ [ { @@ -4336,7 +4337,7 @@ { "condition": { "and": [ { "u_has_move_mode": "run" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.18 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.6 } ], - "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 }, { "effect": "all_fours", "intensity": 1 } ] }, { "condition": { @@ -4362,6 +4363,7 @@ "types": [ "FEET" ], "prereqs": [ "LEGS_BAT" ], "category": [ "CHIROPTERAN" ], + "threshreq": [ "THRESH_CHIROPTERAN" ], "flags": [ "WALL_CLING" ], "restricts_gear": [ "foot_l", "foot_r" ], "destroys_gear": true @@ -7474,11 +7476,14 @@ "id": "BLOODFEEDER", "name": { "str": "Bloodfeeder" }, "points": -5, - "description": "Your craving for blood borders on obsession. You don't care if it's human or animal, all you know is that you've got to have it, and other foods just don't taste as good.", + "description": "Your craving for blood borders on obsession. Your system has gained a mild tolerance to the toxins polluting mutant flesh. When it comes to blood (and only blood), you need the stuff too much to care about whether it came from a human.", "types": [ "DIET" ], "flags": [ "BLOODFEEDER" ], "cancels": [ "VEGAN", "HEMOVORE" ], + "prereqs": [ "HEMOVORE" ], + "vitamins_absorb_multi": [ [ "all", [ [ "mutant_toxin", 0.5 ] ] ] ], "category": [ "CHIROPTERAN" ], + "threshreq": [ "THRESH_CHIROPTERAN" ], "vitamin_rates": [ [ "iron", 2400 ] ] }, { @@ -9285,6 +9290,7 @@ "description": "Your feet have grown into strong and large rabbit feet. This makes your kicks pack a little more oomph. Emphasis on little. It also offers some warmth, and the lucky part is but superstition. The downside is you are unable to wear shoes.", "types": [ "FEET" ], "category": [ "RABBIT" ], + "flags": [ "QUADRUPED_RUN" ], "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], "restricts_gear": [ "foot_l", "foot_r" ], "destroys_gear": true, @@ -9295,67 +9301,7 @@ "attack_text_npc": "%1$s kicks %2$s with their large feet!", "chance": 15, "strength_damage": { "damage_type": "bash", "amount": 1.5 } - }, - "triggers": [ - [ - { - "condition": { - "and": [ - { "u_has_move_mode": "run" }, - { "not": "u_can_drop_weapon" }, - { "u_has_any_trait": [ "THRESH_BEAST", "THRESH_FELINE", "THRESH_LUPINE", "THRESH_RABBIT" ] }, - { "u_has_flag": "QUADRUPED_CROUCH" } - ] - }, - "msg_on": { "text": "You drop down on all fours." }, - "msg_off": { "text": "You rise up to walk on your hind feet." } - } - ] - ], - "enchantments": [ - { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { "u_has_any_trait": [ "THRESH_LUPINE", "THRESH_FELINE", "THRESH_BEAST", "THRESH_RABBIT" ] }, - { "u_has_flag": "QUADRUPED_CROUCH" } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], - "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] - }, - { - "condition": { - "and": [ - { "u_has_move_mode": "run" }, - { "not": "u_can_drop_weapon" }, - { "u_has_any_trait": [ "THRESH_LUPINE", "THRESH_FELINE", "THRESH_BEAST", "THRESH_RABBIT" ] }, - { "u_has_flag": "QUADRUPED_CROUCH" } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], - "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] - }, - { - "condition": { - "or": [ - "u_can_drop_weapon", - { "u_has_move_mode": "walk" }, - { - "and": [ - { "not": { "u_has_trait": "THRESH_LUPINE" } }, - { "not": { "u_has_trait": "THRESH_FELINE" } }, - { "not": { "u_has_trait": "THRESH_BEAST" } }, - { "not": { "u_has_trait": "THRESH_RABBIT" } } - ] - }, - { "not": { "u_has_flag": "QUADRUPED_CROUCH" } } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.1 } ] - } - ] + } }, { "type": "mutation", diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 02d4ae8658d4e..1f49d6b92a563 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -1622,10 +1622,12 @@ void activity_handlers::mutant_tree_communion_do_turn( player_activity *act, Cha if( you->has_effect( effect_social_dissatisfied ) ) { you->remove_effect( effect_social_dissatisfied ); } - if( ( you->has_flag( json_flag_SOCIAL1 ) || you->has_flag( json_flag_SOCIAL2 ) ) && !you->has_effect( effect_social_satisfied ) ) { + if( ( you->has_flag( json_flag_SOCIAL1 ) || you->has_flag( json_flag_SOCIAL2 ) ) && + !you->has_effect( effect_social_satisfied ) ) { you->add_effect( effect_social_satisfied, 3_hours, false, 1 ); } - if( ( you->has_flag( json_flag_ASOCIAL1 ) || you->has_flag( json_flag_ASOCIAL2 ) ) && !you->has_effect( effect_asocial_dissatisfied ) ) { + if( ( you->has_flag( json_flag_ASOCIAL1 ) || you->has_flag( json_flag_ASOCIAL2 ) ) && + !you->has_effect( effect_asocial_dissatisfied ) ) { you->add_effect( effect_asocial_dissatisfied, 3_hours, false, 1 ); } } @@ -3801,10 +3803,12 @@ void activity_handlers::tree_communion_do_turn( player_activity *act, Character if( you->has_effect( effect_social_dissatisfied ) ) { you->remove_effect( effect_social_dissatisfied ); } - if( ( you->has_flag( json_flag_SOCIAL1 ) || you->has_flag( json_flag_SOCIAL2 ) ) && !you->has_effect( effect_social_satisfied ) ) { + if( ( you->has_flag( json_flag_SOCIAL1 ) || you->has_flag( json_flag_SOCIAL2 ) ) && + !you->has_effect( effect_social_satisfied ) ) { you->add_effect( effect_social_satisfied, 3_hours, false, 1 ); } - if( ( you->has_flag( json_flag_ASOCIAL1 ) || you->has_flag( json_flag_ASOCIAL2 ) ) && !you->has_effect( effect_asocial_dissatisfied ) ) { + if( ( you->has_flag( json_flag_ASOCIAL1 ) || you->has_flag( json_flag_ASOCIAL2 ) ) && + !you->has_effect( effect_asocial_dissatisfied ) ) { you->add_effect( effect_asocial_dissatisfied, 3_hours, false, 1 ); } } diff --git a/src/character.cpp b/src/character.cpp index 1f5e0009511c3..888f3737394cc 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -2186,20 +2186,6 @@ bool Character::is_crouching() const return move_mode->type() == move_mode_type::CROUCHING; } -bool Character::is_runallfours() const -{ - bool allfour = false; - if( move_mode->type() == move_mode_type::RUNNING && !is_armed() ) { - if( ( has_trait( trait_PAWS ) && ( has_trait( trait_THRESH_LUPINE ) || - has_trait( trait_THRESH_FELINE ) - || has_trait( trait_THRESH_BEAST ) ) || ( has_trait( trait_WINGS_BAT ) && has_trait( trait_LEGS_BAT ) ) ) ) { - allfour = true; - } - } - - return allfour; -} - bool Character::is_prone() const { return move_mode->type() == move_mode_type::PRONE; @@ -10572,18 +10558,18 @@ void Character::echo_pulse() // Sound travels farther underwater if( has_effect( effect_subaquatic_sonar ) && is_underwater() ) { pulse_range = 16; - sounds::sound( this->pos(), 5, sounds::sound_t::movement, _( "boop." ), true, - "none", "none" ); + sounds::sound( this->pos(), 5, sounds::sound_t::movement, _( "boop." ), true, + "none", "none" ); } else if( !has_effect( effect_subaquatic_sonar ) && is_underwater() ) { pulse_range = 0; add_msg_if_player( m_warning, _( "You can't echolocate underwater!" ) ); return; } else { - sounds::sound( this->pos(), 5, sounds::sound_t::movement, _( "chirp." ), true, - "none", "none" ); + sounds::sound( this->pos(), 5, sounds::sound_t::movement, _( "chirp." ), true, + "none", "none" ); } for( tripoint origin : points_in_radius( pos(), pulse_range ) ) { - if ( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, 2 ) ) { + if( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, 2 ) ) { sounds::sound( origin, 6, sounds::sound_t::sensory, _( "click." ), true, "none", "none" ); } @@ -10595,106 +10581,106 @@ void Character::echo_pulse() add_known_trap( origin, tr ); } Creature *critter = get_creature_tracker().creature_at( origin, true ); - if ( critter && here.pl_line_of_sight( origin, 1 ) ) { + if( critter && here.pl_line_of_sight( origin, 1 ) ) { switch( critter->get_size() ) { case creature_size::tiny: echo_volume = 1; - break; + break; case creature_size::small: echo_volume = 2; - break; + break; case creature_size::medium: echo_volume = 3; - break; + break; case creature_size::large: echo_volume = 4; - break; + break; case creature_size::huge: echo_volume = 5; - break; + break; case creature_size::num_sizes: debugmsg( "ERROR: Invalid Creature size class." ); break; } - // Some monsters are harder to get a read on - if( critter->has_flag( mon_flag_PLASTIC ) ) { - echo_volume -= std::max( 1, 1 ); - } - if( critter->has_flag( mon_flag_HARDTOSHOOT ) ) { - echo_volume -= std::max( 1, 1 ); - } - const char *echo_string = nullptr; - // bio_targeting has a visual HUD and automatically interprets the raw audio data, - // but only for electronic SONAR. Its designers didn't anticipate bat mutations - if( has_bionic( bio_targeting ) && has_effect( effect_subaquatic_sonar ) ) - switch( echo_volume ) { - case 1: - echo_string = _( "Target [Tiny]." ); - break; - case 2: - echo_string = _( "Target [Small]." ); - break; - case 3: - echo_string = _( "Target [Medium]." ); - break; - case 4: - echo_string = _( "Warning! Target [Large]." ); - break; - case 5: - echo_string = _( "Warning! Target [Huge]." ); - break; - default: - debugmsg( "ERROR: Invalid echo string." ); - break; - } else if ( !has_bionic( bio_targeting ) && has_effect( effect_subaquatic_sonar ) ) { - switch( echo_volume ) { - case 1: - echo_string = _( "tick." ); - break; - case 2: - echo_string = _( "pii." ); - break; - case 3: - echo_string = _( "ping." ); - break; - case 4: - echo_string = _( "pong." ); - break; - case 5: - echo_string = _( "booop." ); - break; - default: - debugmsg( "ERROR: Invalid echo string." ); - break; - } - } else { - switch( echo_volume ) { - case 1: - echo_string = _( "ch." ); - break; - case 2: - echo_string = _( "chk." ); - break; - case 3: - echo_string = _( "chhk." ); - break; - case 4: - echo_string = _( "chkch." ); - break; - case 5: - echo_string = _( "chkchh." ); - break; - default: - debugmsg( "ERROR: Invalid echo string." ); - break; - } - } - // It's not moving. Must be an obstacle - if( critter->has_flag( mon_flag_IMMOBILE ) ) { - echo_string = _( "click." ); - } + // Some monsters are harder to get a read on + if( critter->has_flag( mon_flag_PLASTIC ) ) { + echo_volume -= std::max( 1, 1 ); + } + if( critter->has_flag( mon_flag_HARDTOSHOOT ) ) { + echo_volume -= std::max( 1, 1 ); + } + const char *echo_string = nullptr; + // bio_targeting has a visual HUD and automatically interprets the raw audio data, + // but only for electronic SONAR. Its designers didn't anticipate bat mutations + if( has_bionic( bio_targeting ) && has_effect( effect_subaquatic_sonar ) ) + switch( echo_volume ) { + case 1: + echo_string = _( "Target [Tiny]." ); + break; + case 2: + echo_string = _( "Target [Small]." ); + break; + case 3: + echo_string = _( "Target [Medium]." ); + break; + case 4: + echo_string = _( "Warning! Target [Large]." ); + break; + case 5: + echo_string = _( "Warning! Target [Huge]." ); + break; + default: + debugmsg( "ERROR: Invalid echo string." ); + break; + } else if( !has_bionic( bio_targeting ) && has_effect( effect_subaquatic_sonar ) ) { + switch( echo_volume ) { + case 1: + echo_string = _( "tick." ); + break; + case 2: + echo_string = _( "pii." ); + break; + case 3: + echo_string = _( "ping." ); + break; + case 4: + echo_string = _( "pong." ); + break; + case 5: + echo_string = _( "booop." ); + break; + default: + debugmsg( "ERROR: Invalid echo string." ); + break; + } + } else { + switch( echo_volume ) { + case 1: + echo_string = _( "ch." ); + break; + case 2: + echo_string = _( "chk." ); + break; + case 3: + echo_string = _( "chhk." ); + break; + case 4: + echo_string = _( "chkch." ); + break; + case 5: + echo_string = _( "chkchh." ); + break; + default: + debugmsg( "ERROR: Invalid echo string." ); + break; + } + } + // It's not moving. Must be an obstacle + if( critter->has_flag( mon_flag_IMMOBILE ) ) { + echo_string = _( "click." ); + } sounds::sound( origin, echo_volume, sounds::sound_t::sensory, _( echo_string ), false, - "none", "none" ); + "none", "none" ); } } } @@ -12957,7 +12943,7 @@ void Character::search_surroundings() if( controlling_vehicle ) { return; } - if( has_effect ( effect_subaquatic_sonar ) && is_underwater() && calendar::once_every( 4_turns ) ) { + if( has_effect( effect_subaquatic_sonar ) && is_underwater() && calendar::once_every( 4_turns ) ) { echo_pulse(); } map &here = get_map(); diff --git a/src/character.h b/src/character.h index abf15e3883105..efdce5caa8327 100644 --- a/src/character.h +++ b/src/character.h @@ -1013,7 +1013,6 @@ class Character : public Creature, public visitable bool is_running() const; bool is_walking() const; bool is_crouching() const; - bool is_runallfours() const; bool is_prone() const; int footstep_sound() const; diff --git a/src/condition.cpp b/src/condition.cpp index cbf7738a3ccd1..e5768905277df 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -564,7 +564,7 @@ conditional_t::func f_has_trait( const JsonObject &jo, std::string_view member, return [trait_to_check, is_npc]( dialogue const & d ) { return d.actor( is_npc )->has_trait( trait_id( trait_to_check.evaluate( d ) ) ); }; -} +} conditional_t::func f_can_see_friends( const JsonObject &jo, std::string_view member, bool is_npc ) @@ -574,23 +574,23 @@ conditional_t::func f_can_see_friends( const JsonObject &jo, std::string_view me Character &player_character = get_player_character(); const Character *ch = d.actor( is_npc )->get_character(); int seen = 0; - if( is_npc ) { - if( ch->sees( player_character ) && ch->as_npc()->is_friendly( player_character ) ) { + if( is_npc ) { + if( ch->sees( player_character ) && ch->as_npc()->is_friendly( player_character ) ) { + seen += 1; + } + for( npc &guy : g->all_npcs() ) { + if( ch->sees( guy ) && guy.is_friendly( *ch ) ) { seen += 1; - } - for( npc &guy : g->all_npcs() ) { - if( ch->sees( guy ) && guy.is_friendly( *ch ) ) { - seen += 1; - } - } - } else if( !is_npc ) { - int seen = 0; - for( npc &guy : g->all_npcs() ) { - if( get_player_view().sees( guy ) && guy.is_friendly( player_character ) ) { + } + } + } else if( !is_npc ) { + int seen = 0; + for( npc &guy : g->all_npcs() ) { + if( get_player_view().sees( guy ) && guy.is_friendly( player_character ) ) { seen += 1; - } } } + } return seen >= dov.evaluate( d ); }; } diff --git a/src/condition.h b/src/condition.h index 24edfa2060c04..6898d61e20a9c 100644 --- a/src/condition.h +++ b/src/condition.h @@ -76,7 +76,7 @@ struct conditional_t { conditional_t() = default; explicit conditional_t( std::string_view type ); explicit conditional_t( const JsonObject &jo ); - + static std::function get_get_string( const JsonObject &jo ); static std::function get_get_translation( const JsonObject &jo ); template diff --git a/src/consumption.cpp b/src/consumption.cpp index ad3f333e5e872..864b17a97f3d3 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -165,6 +165,8 @@ static const trait_id trait_VEGAN( "VEGAN" ); static const trait_id trait_VEGETARIAN( "VEGETARIAN" ); static const trait_id trait_WATERSLEEP( "WATERSLEEP" ); +static const vitamin_id vitamin_mutant_toxin( "mutant_toxin" ); + // note: cannot use constants from flag.h (e.g. flag_ALLERGEN_VEGGY) here, as they // might be uninitialized at the time these const arrays are created static const std::array carnivore_blacklist {{ @@ -511,23 +513,25 @@ std::pair Character::fun_for( const item &comest, bool ignore_already_ } } - // Cooked blood is OK, but it's really better raw. + // Cooked blood is OK, but it's really better raw + // This is automatically handled by raw blood having negative fun if( comest.has_flag( flag_HEMOVORE_FUN ) ) { if( has_flag( json_flag_BLOODFEEDER ) ) { - if( fun <= 0) { - fun += 25; + if( fun <= 0 ) { + fun += 25; } else { fun *= 1.2; } } else if( has_flag( json_flag_HEMOVORE ) ) { if( fun <= 0 ) { - fun += 13; + fun += 13; } else { fun *= 1.1; } } } + // This cherry soda's just not the same... if( has_flag( json_flag_BLOODFEEDER ) && !comest.has_flag( flag_HEMOVORE_FUN ) && fun > 0 ) { fun *= 0.5; } diff --git a/src/creature.cpp b/src/creature.cpp index 5b6eef9a0b0ce..79a9f2a9676ae 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -72,6 +72,7 @@ static const damage_type_id damage_bash( "bash" ); static const damage_type_id damage_electric( "electric" ); static const damage_type_id damage_heat( "heat" ); +static const efftype_id effect_all_fours( "all_fours" ); static const efftype_id effect_blind( "blind" ); static const efftype_id effect_bounced( "bounced" ); static const efftype_id effect_downed( "downed" ); @@ -430,7 +431,8 @@ bool Creature::sees( const Creature &critter ) const return false; } if( ch != nullptr ) { - if( ch->is_crouching() || ch->is_prone() || pos().z != critter.pos().z ) { + if( ch->is_crouching() || ch->has_effect( effect_all_fours ) || ch->is_prone() || + pos().z != critter.pos().z ) { const int coverage = std::max( here.obstacle_coverage( pos(), critter.pos() ), here.ledge_coverage( *this, critter.pos() ) ); if( coverage < 30 ) { @@ -458,7 +460,7 @@ bool Creature::sees( const Creature &critter ) const } int profile = 120 / size_modifier; - if( ch->is_crouching() ) { + if( ch->is_crouching() || ch->has_effect( effect_all_fours ) ) { profile *= 0.5; } else if( ch->is_prone() ) { profile *= 0.275; diff --git a/src/lightmap.cpp b/src/lightmap.cpp index c310189007ede..de43311530e76 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -48,6 +48,7 @@ #include "weather.h" #include "weather_type.h" +static const efftype_id effect_quadruped_full( "quadruped_full" ); static const efftype_id effect_haslight( "haslight" ); static const efftype_id effect_onfire( "onfire" ); @@ -143,8 +144,8 @@ bool map::build_transparency_cache( const int zlev ) value *= sight_penalty; } float value_wo_fields = value; - Character &player_character = get_player_character(); - if( !player_character.vision_mode_cache[IR_VISION] ) { + Character &player_character = get_player_character(); + if( !player_character.vision_mode_cache[IR_VISION] ) { for( const auto &fld : cur_submap->get_field( sp ) ) { const field_intensity_level &i_level = fld.second.get_intensity_level(); if( i_level.transparent ) { @@ -153,7 +154,7 @@ bool map::build_transparency_cache( const int zlev ) // Fields are either transparent or not, however we want some to be translucent value = value * i_level.translucency; } - } + } return std::make_pair( value, value_wo_fields ); }; @@ -210,14 +211,14 @@ bool map::build_vision_transparency_cache( const int zlev ) bool dirty = false; bool is_crouching = player_character.is_crouching(); - bool is_runallfours = player_character.is_runallfours(); + bool low_profile = player_character.has_effect( effect_quadruped_full ) && player_character.is_running(); bool is_prone = player_character.is_prone(); for( const tripoint &loc : points_in_radius( p, 1 ) ) { if( loc == p ) { // The tile player is standing on should always be visible vision_transparency_cache[p.x][p.y] = LIGHT_TRANSPARENCY_OPEN_AIR; - } else if( ( is_crouching || is_prone || is_runallfours ) && coverage( loc ) >= 30 ) { + } else if( ( is_crouching || is_prone || low_profile ) && coverage( loc ) >= 30 ) { // If we're crouching or prone behind an obstacle, we can't see past it. vision_transparency_cache[loc.x][loc.y] = LIGHT_TRANSPARENCY_SOLID; dirty = true; @@ -813,7 +814,7 @@ bool map::pl_line_of_sight( const tripoint &t, const int max_range ) const return false; } -// Character &player_character = get_player_character(); + // Character &player_character = get_player_character(); const level_cache &map_cache = get_cache_ref( t.z ); if( map_cache.seen_cache[t.x][t.y] > 0.075f ) { return true; diff --git a/src/melee.cpp b/src/melee.cpp index 084f17ae72d66..f32d4e4e000bc 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -101,6 +101,7 @@ static const efftype_id effect_hit_by_player( "hit_by_player" ); static const efftype_id effect_incorporeal( "incorporeal" ); static const efftype_id effect_lightsnare( "lightsnare" ); static const efftype_id effect_narcosis( "narcosis" ); +static const efftype_id effect_natural_stance( "natural_stance" ); static const efftype_id effect_pet( "pet" ); static const efftype_id effect_stunned( "stunned" ); static const efftype_id effect_venom_dmg( "venom_dmg" ); @@ -122,7 +123,6 @@ static const json_character_flag json_flag_NEED_ACTIVE_TO_MELEE( "NEED_ACTIVE_TO static const json_character_flag json_flag_NULL( "NULL" ); static const json_character_flag json_flag_PSEUDOPOD_GRASP( "PSEUDOPOD_GRASP" ); static const json_character_flag json_flag_UNARMED_BONUS( "UNARMED_BONUS" ); -static const json_character_flag json_flag_QUADRUPED( "QUADRUPED" ); static const limb_score_id limb_score_block( "block" ); static const limb_score_id limb_score_grip( "grip" ); @@ -376,12 +376,19 @@ float Character::hit_roll() const } // Difficult to land a hit while prone - // Quadrupeds don't mind as long as they're unarmed + // Quadrupeds don't mind crouching as long as they're unarmed + // Tentacles and goo-limbs care even less item_location cur_weapon = used_weapon(); item cur_weap = cur_weapon ? *cur_weapon : null_item_reference(); - if( is_on_ground() && !has_flag( json_flag_PSEUDOPOD_GRASP ) ) { - hit -= 8.0f; - } else if( is_crouching() && ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) ) { + if( is_on_ground() ) { + if( has_flag( json_flag_PSEUDOPOD_GRASP ) ) { + hit -= 2.0f; + } else { + hit -= 8.0f; + } + } else if( is_crouching() && ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || + ( !has_effect( effect_natural_stance ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && + !unarmed_attack() ) ) ) ) { hit -= 2.0f; } @@ -778,9 +785,15 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, d.mult_damage( 0.7 ); } // being prone affects how much leverage you can use to deal damage - if( is_on_ground() && !has_flag( json_flag_PSEUDOPOD_GRASP ) ) { + // quadrupeds don't mind as much, tentacles and goo-limbs even less + if( is_on_ground() ) { + if( has_flag( json_flag_PSEUDOPOD_GRASP ) ) { + d.mult_damage( 0.8 ); + } d.mult_damage( 0.3 ); - } else if( is_crouching() && ( !has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) { + } else if( is_crouching() && ( ( !has_effect( effect_natural_stance ) && + ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) || + !has_flag( json_flag_PSEUDOPOD_GRASP ) ) ) { d.mult_damage( 0.8 ); } @@ -951,7 +964,11 @@ int Character::get_total_melee_stamina_cost( const item *weap ) const { const int mod_sta = get_standard_stamina_cost( weap ); const int melee = round( get_skill_level( skill_melee ) ); - const int stance_malus = ( is_on_ground() && !has_flag( json_flag_PSEUDOPOD_GRASP ) ) ? 50 : ( (!has_flag( json_flag_PSEUDOPOD_GRASP ) || ( !has_flag( json_flag_QUADRUPED ) && ( !weap->has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) && is_crouching() ? 20 : 0 ); + // Quadrupeds don't mind crouching, squids and slimes hardly care about even being prone + const int stance_malus = ( is_on_ground() && + !has_flag( json_flag_PSEUDOPOD_GRASP ) ) ? 50 : ( ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || + ( !has_effect( effect_natural_stance ) && ( !weap->has_flag( flag_NATURAL_WEAPON ) && + !unarmed_attack() ) ) ) && is_crouching() ? 20 : 0 ); return std::min( -50, mod_sta + melee - stance_malus ); } @@ -1043,9 +1060,12 @@ int stumble( Character &u, const item_location &weap ) } item cur_weap = weap ? *weap : null_item_reference(); units::mass str_mod = u.get_arm_str() * 10_gram; + // Ceph and Slime mutants still need good posture to prevent stumbling if( u.is_on_ground() ) { str_mod /= 4; - } else if( u.is_crouching() && ( !u.has_flag( json_flag_QUADRUPED ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !u.unarmed_attack() ) ) ) { + // but quadrupeds fight naturally on all fours + } else if( u.is_crouching() && ( !u.has_effect( effect_natural_stance ) && + ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !u.unarmed_attack() ) ) ) { str_mod /= 2; } @@ -2743,9 +2763,15 @@ int Character::attack_speed( const item &weap ) const move_cost += ma_move_cost; move_cost *= mutation_value( "attackcost_modifier" ); - if( is_on_ground() && !has_flag( json_flag_PSEUDOPOD_GRASP ) ) { - move_cost *= 4.0; - } else if( is_crouching() && ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || ( !has_flag( json_flag_QUADRUPED ) && ( !weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) ) ) { + if( is_on_ground() ) { + if( has_flag( json_flag_PSEUDOPOD_GRASP ) ) { + move_cost *= 1.5; + } else { + move_cost *= 4.0; + } + } else if( is_crouching() && ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || + ( !has_effect( effect_natural_stance ) && ( !weap.has_flag( flag_NATURAL_WEAPON ) && + !unarmed_attack() ) ) ) ) { move_cost *= 1.5; } diff --git a/src/ranged.cpp b/src/ranged.cpp index 0e0052a093535..ca54ad3c36dca 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -107,6 +107,7 @@ static const character_modifier_id character_modifier_thrown_dex_mod( "thrown_de static const damage_type_id damage_bash( "bash" ); static const damage_type_id damage_cut( "cut" ); +static const efftype_id effect_quadruped_full( "quadruped_full" ); static const efftype_id effect_downed( "downed" ); static const efftype_id effect_hit_by_player( "hit_by_player" ); static const efftype_id effect_on_roof( "on_roof" ); @@ -549,7 +550,7 @@ double Creature::ranged_target_size() const { double stance_factor = 1.0; if( const Character *character = this->as_character() ) { - if( character->is_crouching() ) { + if( character->is_crouching() || ( character->has_effect( effect_quadruped_full ) && character->is_running() ) ) { stance_factor = 0.6; } else if( character->is_prone() ) { stance_factor = 0.25; diff --git a/src/sounds.cpp b/src/sounds.cpp index 00e8a14432a2a..97ce4ccb6fb33 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -728,14 +728,14 @@ void sounds::process_sound_markers( Character *you ) // Echolocation has to be fairly precise or it's worse than useless. // However, it is never perfect. - if ( sound.category == sound_t::sensory ) { + if( sound.category == sound_t::sensory ) { if( ( heard_volume + distance_to_sound ) / distance_to_sound < 2 ) { err_offset = rand() % 3; } else if( ( heard_volume + distance_to_sound ) / distance_to_sound < 3 ) { err_offset = rand() % 2; } else { if( one_in( 3 ) ) { - err_offset = rand() % 2; + err_offset = rand() % 2; } else { err_offset = 0; } From 419690f5e14f102ad40f85df9b2e38ec84a26f2f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Mon, 22 Jan 2024 15:17:16 -0800 Subject: [PATCH 045/202] Update enchantments.json --- data/json/enchantments.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/json/enchantments.json b/data/json/enchantments.json index db2768c9dbb29..33240209ca8bd 100644 --- a/data/json/enchantments.json +++ b/data/json/enchantments.json @@ -139,8 +139,7 @@ ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], - "//": "Only apply all_fours here, as it's not needed when actually crouching.", - "ench_effects": [ { "effect": "natural_stance", "intensity": 1 }, { "effect": "all_fours", "intensity": 1 } ] + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] }, { "id": "ench_quadruped_movement_half", From a51f5f31e227ed3777bf60bfa4a1b77809d902fd Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Mon, 22 Jan 2024 18:51:21 -0800 Subject: [PATCH 046/202] oaaoaaaarfghghghhj --- data/json/effects.json | 2 +- .../mutation_eocs/mutation_effect_eocs.json | 76 +-------------- data/json/enchantments.json | 31 +++--- data/json/flags.json | 5 - data/json/mutations/mutations.json | 95 ++++++++++++++++--- src/avatar.cpp | 2 + src/npcmove.cpp | 2 + 7 files changed, 99 insertions(+), 114 deletions(-) diff --git a/data/json/effects.json b/data/json/effects.json index d57146d7adae1..b6535ef94aecd 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -4772,7 +4772,7 @@ "name": [ "Natural Stance" ], "rating": "good", "desc": [ - "You are positioned to take advantage of your mutated anatomy, and will favor any natural attacks you may have. Apart from potential changes to move speed, you receive no penalties for crouching in melee combat." + "You are positioned to take advantage of your mutated anatomy, and will favor any natural attacks you may have. You receive no penalties for crouching in melee combat." ] }, { diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index eb652fe4e28af..0665e33f8b042 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -820,7 +820,7 @@ "recurrence": [ "30 minutes", "45 minutes" ], "condition": { "and": [ - { "u_has_effect": "narcosis" }, + { "or": [ { "u_has_effect": "narcosis" }, { "u_has_effect": "sleep" } ] }, { "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] } ] }, @@ -838,79 +838,5 @@ ] }, "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_lose_effect": "social_dissatisfied" } ] - }, - { - "type": "effect_on_condition", - "id": "EOC_quadruped_movement", - "//": "Quadrupedal post-thresh mutants with paws in front and back get these effects when they're running or crouching and empty-handed.", - "triggers": [ - [ - { - "condition": { - "and": [ - { "or": [ { "u_has_move_mode": "run" }, { "u_has_move_mode": "crouch" } ] }, - { "not": "u_can_drop_weapon" }, - { "u_has_flag": [ "QUADRUPED_CROUCH" ] }, - { "u_has_flag": [ "QUADRUPED_RUN" ] }, - { "u_has_flag": [ "QUADRUPED" ] } - ] - }, - "effect": [ { "u_add_effect": "quadruped_full", "duration": "PERMANENT" }, { "u_add_effect": "natural_stance", "duration": "PERMANENT" }, { "u_message": "You drop down on all fours." } ] - } - ] - ] - }, - { - "type": "effect_on_condition", - "id": "EOC_quadruped_movement_half", - "//": "This is for post-thresh mutants with paws but not animal back legs. It's a much more minor buff, and only to crawling.", - "triggers": [ - [ - { - "condition": { - "and": [ - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - { "not": { "u_has_flag": [ "QUADRUPED_RUN" ] } }, - { "u_has_flag": [ "QUADRUPED_CROUCH" ] }, - { "u_has_flag": [ "QUADRUPED" ] } - ] - }, - "effect": [ { "u_add_effect": "quadruped_half", "duration": "PERMANENT" }, { "u_message": "You drop down on all fours." } ] - } - ] - ] - }, - { - "type": "effect_on_condition", - "id": "EOC_quadruped_full_lose", - "triggers": [ - [ - { - "condition": { - "and": [ { "u_has_effect": "quadruped_full" }, - { "or": [ { "not": { "or": [ { "u_has_move_mode": "crouch" }, { "u_has_move_mode": "run" } ] } }, { "not": "u_can_drop_weapon" } ] } - ] - }, - "effect": [ { "u_lose_effect": "quadruped_full", "duration": "PERMANENT" }, { "u_lose_effect": "natural_stance", "duration": "PERMANENT" }, { "u_message": "You stop crawling on all fours." } ] - } - ] - ] - }, - { - "type": "effect_on_condition", - "id": "EOC_quadruped_half_lose", - "triggers": [ - [ - { - "condition": { - "and": [ { "u_has_effect": "quadruped_half" }, - { "or": { "not": [ { "u_has_move_mode": "crouch" }, { "not": "u_can_drop_weapon" } ] } } - ] - }, - "effect": [ { "u_lose_effect": "quadruped_half", "duration": "PERMANENT" }, { "u_message": "You stop crawling on all fours." } ] - } - ] - ] } ] diff --git a/data/json/enchantments.json b/data/json/enchantments.json index 33240209ca8bd..7434c4fa87ff5 100644 --- a/data/json/enchantments.json +++ b/data/json/enchantments.json @@ -115,27 +115,16 @@ "values": [ { "value": "REGEN_STAMINA", "add": -110 } ] }, { - "id": "ench_quadruped_movement_crouch", - "//": "quadruped_movement_crouch and quadruped_movement_run are for quadrupeds with all four of their animal limbs.", + "id": "ench_quadruped_movement_full", + "//": "For quadrupeds with all four of their animal limbs. We should avoid giving this to every animal mutant, as it helps keep lines distinct.", "type": "enchantment", "condition": { "and": [ - { "u_has_effect": "quadruped_full" }, - { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], - "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] - }, - { - "id": "ench_quadruped_movement_run", - "type": "enchantment", - "condition": { - "and": [ - { "u_has_effect": "quadruped_full" }, - { "u_has_move_mode": "run" }, - { "not": "u_can_drop_weapon" }, + { "u_has_flag": "QUADRUPED_CROUCH" }, + { "u_has_any_trait": [ "THRESH_RABBIT", "THRESH_BEAST", "THRESH_LUPINE", "THRESH_FELINE" ] }, + { "u_has_flag": "QUADRUPED_RUN" }, + { "or": [ { "u_has_move_mode": "crouch" }, { "u_has_move_mode": "run" } ] }, + { "not": "u_can_drop_weapon" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.15 }, { "value": "CARRY_WEIGHT", "multiply": 0.35 } ], @@ -147,9 +136,11 @@ "type": "enchantment", "condition": { "and": [ - { "u_has_effect": "quadruped_half" }, + { "u_has_flag": "QUADRUPED_CROUCH" }, + { "u_has_any_trait": [ "THRESH_RABBIT", "THRESH_BEAST", "THRESH_LUPINE", "THRESH_FELINE" ] }, + { "not": { "u_has_flag": "QUADRUPED_RUN" } }, { "u_has_move_mode": "crouch" }, - { "not": "u_can_drop_weapon" }, + { "not": "u_can_drop_weapon" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.5 } ] diff --git a/data/json/flags.json b/data/json/flags.json index d0f79c4b4de46..3e70f2ee5cb1b 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2429,11 +2429,6 @@ "id": "ASOCIAL2", "type": "json_flag" }, - { - "id": "QUADRUPED", - "type": "json_flag", - "//": "This post-threshold mutant has had enough of their brain replaced to use their paws (if they have them) for quadrupedal movement." - }, { "id": "QUADRUPED_CROUCH", "type": "json_flag", diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 01a2c87b0b73c..3df906d46eed9 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -487,7 +487,7 @@ "name": { "str": "Echolocation" }, "points": 3, "description": "You've learned how to hunt without sight. Activate this mutation to make an ultrasonic sound that can reveal obstacles and enemies nearby.", - "prereqs": [ "BATEARS", "INDEFATIGABLE" ], + "prereqs": [ "BATEARS", "GOODCARDIO" ], "category": [ "CHIROPTERAN" ], "active": true }, @@ -497,7 +497,7 @@ "name": { "str": "Shriek" }, "points": 3, "description": "Activate to let loose a high pitched screech, loud enough to shatter glass and daze anything in the immediate vicinity.", - "prereqs": [ "BATEARS", "INDEFATIGABLE" ], + "prereqs": [ "BATEARS", "GOODCARDIO" ], "category": [ "CHIROPTERAN" ], "active": true }, @@ -4304,15 +4304,14 @@ "points": -1, "visibility": 2, "ugliness": 2, - "description": "Your lower limbs have shortened and splayed, slowing your bipedal movement. As long as you're not wielding anything, you're able to crawl quickly and quietly when you run or crouch.", + "description": "Your lower limbs have shortened and splayed, slowing your bipedal movement. As long as you're not wielding anything and you've got both wings, you're able to crawl quickly and quietly when you run or crouch.", "types": [ "LEGS" ], "prereqs": [ "PADDED_FEET", "WINGS_BAT" ], "category": [ "CHIROPTERAN" ], "threshreq": [ "THRESH_CHIROPTERAN" ], "movecost_modifier": 1.35, "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], - "destroys_gear": true, - "//": "Uses bespoke triggers rather than QUADRUPED_CROUCH as bat-crawling gives different buffs.", + "//": "Uses bespoke stuff here rather than the quadruped enchantments as bat-crawling gives different buffs.", "triggers": [ [ { @@ -4384,6 +4383,29 @@ "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], "restricts_gear": [ "foot_l", "foot_r" ], "destroys_gear": true, + "triggers": [ + [ + { + "condition": { + "and": [ + { "or": [ { "u_has_move_mode": "run" }, { "u_has_move_mode": "crouch" } ] }, + { "not": "u_can_drop_weapon" }, + { "u_has_any_trait": [ "THRESH_BEAST", "THRESH_LUPINE", "THRESH_FELINE", "THRESH_RABBIT" ] }, + { "u_has_flag": "QUADRUPED_CROUCH" } + ] + }, + "msg_on": { "text": "You drop down on all fours." }, + "msg_off": { "text": "You assume a less bestial posture." } + } + ] + ], + "enchantments": [ + "ench_quadruped_movement_full", + { + "condition": { "and": [ { "u_has_move_mode": "crouch" }, { "not": "u_can_drop_weapon" }, { "u_has_flag": "QUADRUPED_CROUCH" } ] }, + "values": [ { "value": "MOVE_COST", "multiply": -0.35 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] + } ], "weight_capacity_modifier": 0.8 }, { @@ -6474,7 +6496,7 @@ "description": "You have a pair of stubby little wings projecting from your shoulderblades. They can be wiggled at will, but are useless.", "types": [ "WINGS" ], "changes_to": [ "WINGS_INSECT", "WINGS_BUTTERFLY" ], - "category": [ "INSECT", "BEAST" ] + "category": [ "INSECT" ] }, { "type": "mutation", @@ -7183,6 +7205,23 @@ "cancels": [ "TALONS" ], "flags": [ "QUADRUPED_CROUCH" ], "changes_to": [ "PAWS_LARGE" ], + "triggers": [ + [ + { + "condition": { + "and": [ + { "u_has_move_mode": "crouch" }, + { "u_has_any_trait": [ "THRESH_BEAST", "THRESH_LUPINE", "THRESH_RABBIT", "THRESH_FELINE" ] }, + { "not": "u_can_drop_weapon" }, + { "not": { "u_has_flag": "QUADRUPED_RUN" } } + ] + }, + "msg_on": { "text": "You drop down on all fours." }, + "msg_off": { "text": "You come out of your quadrupedal posture." } + } + ] + ], + "enchantments": [ "ench_quadruped_movement_half" ], "category": [ "LUPINE", "RAT", "FELINE", "BEAST", "URSINE" ] }, { @@ -7211,6 +7250,23 @@ "prereqs": [ "PAWS" ], "cancels": [ "TALONS" ], "flags": [ "QUADRUPED_CROUCH" ], + "triggers": [ + [ + { + "condition": { + "and": [ + { "u_has_move_mode": "crouch" }, + { "u_has_any_trait": [ "THRESH_BEAST", "THRESH_LUPINE", "THRESH_RABBIT", "THRESH_FELINE" ] }, + { "not": "u_can_drop_weapon" }, + { "not": { "u_has_flag": "QUADRUPED_RUN" } } + ] + }, + "msg_on": { "text": "You drop down on all fours." }, + "msg_off": { "text": "You come out of your quadrupedal posture." } + } + ] + ], + "enchantments": [ "ench_quadruped_movement_half" ], "category": [ "BEAST", "URSINE" ] }, { @@ -8204,7 +8260,6 @@ "points": 1, "description": "It's about time you grew out. Now that you've matured, it is time to make something of yourself.", "valid": false, - "flags": [ "QUADRUPED" ], "purifiable": false, "threshold": true }, @@ -8215,7 +8270,6 @@ "points": 1, "description": "Stalking prey, eating well, and lying in the sun. Mmm, all you could ever desire.", "valid": false, - "flags": [ "QUADRUPED" ], "purifiable": false, "threshold": true }, @@ -8226,7 +8280,6 @@ "points": 1, "description": "You're the perfect candidate to lead a pack.", "valid": false, - "flags": [ "QUADRUPED" ], "purifiable": false, "threshold": true }, @@ -8236,7 +8289,6 @@ "name": { "str": "Bear" }, "points": 1, "description": "So the humans died; what's the worry? Now they won't ruin the woods.", - "cancels": [ "CARNIVORE" ], "valid": false, "purifiable": false, "threshold": true @@ -8437,7 +8489,6 @@ "points": 1, "description": "You long for a colony, the cover of darkness, and a meal on the wing.", "valid": false, - "flags": [ "QUADRUPED" ], "purifiable": false, "threshold": true }, @@ -9236,7 +9287,6 @@ "points": 1, "description": "You really want to eat some carrots. Welcome to the Lagomorpha family.", "valid": false, - "flags": [ "QUADRUPED" ], "purifiable": false, "threshold": true }, @@ -9301,7 +9351,9 @@ "attack_text_npc": "%1$s kicks %2$s with their large feet!", "chance": 15, "strength_damage": { "damage_type": "bash", "amount": 1.5 } - } + }, + "enchantments": [ "ench_quadruped_movement_full" ], + "weight_capacity_modifier": 0.8 }, { "type": "mutation", @@ -9329,6 +9381,23 @@ "prereqs": [ "NAILS" ], "cancels": [ "TALONS" ], "flags": [ "QUADRUPED_CROUCH" ], + "triggers": [ + [ + { + "condition": { + "and": [ + { "u_has_move_mode": "crouch" }, + { "u_has_any_trait": [ "THRESH_BEAST", "THRESH_LUPINE", "THRESH_RABBIT", "THRESH_FELINE" ] }, + { "not": "u_can_drop_weapon" }, + { "not": { "u_has_flag": "QUADRUPED_RUN" } } + ] + }, + "msg_on": { "text": "You drop down on all fours." }, + "msg_off": { "text": "You come out of your quadrupedal posture." } + } + ] + ], + "enchantments": [ "ench_quadruped_movement_half" ], "category": [ "RABBIT" ] }, { diff --git a/src/avatar.cpp b/src/avatar.cpp index 357f9193bf34f..457bab30ea4fd 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -1299,6 +1299,8 @@ void avatar::set_movement_mode( const move_mode_id &new_mode ) } add_msg( new_mode->change_message( true, get_steed_type() ) ); move_mode = new_mode; + // Enchantments based on move modes can stack inappropriately without a recalc here + recalculate_enchantment_cache(); // crouching affects visibility get_map().set_seen_cache_dirty( pos().z ); recoil = MAX_RECOIL; diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 09635de41946e..366164d795218 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -5294,5 +5294,7 @@ bool outfit::adjust_worn( npc &guy ) void npc::set_movement_mode( const move_mode_id &new_mode ) { + // Enchantments based on move modes can stack inappropriately without a recalc here + recalculate_enchantment_cache(); move_mode = new_mode; } From 44d4724909434fe1bea0ae1e05421ab1a7d1521a Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Tue, 23 Jan 2024 12:11:31 -0800 Subject: [PATCH 047/202] blooood --- data/json/effects.json | 10 +++++++ .../mutation_activation_eocs.json | 4 +-- data/json/mutations/mutations.json | 14 +++++++--- data/json/techniques.json | 27 +++++++++++++++++++ src/mutation.cpp | 1 + 5 files changed, 50 insertions(+), 6 deletions(-) diff --git a/data/json/effects.json b/data/json/effects.json index b6535ef94aecd..d07341576b319 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -2033,6 +2033,16 @@ ], "flags": [ "EFFECT_LIMB_SCORE_MOD" ] }, + { + "type": "effect_type", + "id": "anticoagulant_draculin", + "max_intensity": 3, + "max_duration": "30 m", + "rating": "bad", + "blood_analysis_description": "Draculin", + "//": "yes it's actually called that. It is also the strongest anti-clotting factor known to science.", + "effect_dur_scaling": [ { "effect_id": "bleed", "modifier": 2.0, "same_bp": false } ] + }, { "type": "effect_type", "id": "anticoagulant", diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_activation_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_activation_eocs.json index 97f7bda0c1017..071ccd6f6b9c9 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_activation_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_activation_eocs.json @@ -10,12 +10,12 @@ "condition": { "and": [ { "expects_vars": [ "prep_time", "spell_to_cast", "message_success", "message_fail" ] }, - { "math": [ "u_val('stamina')", ">", "_energy_amount" ] } + { "math": [ "u_val('stamina')", ">", "energy_amount" ] } ] }, "effect": [ { "u_assign_activity": "ACT_GENERIC_EOC", "duration": { "context_val": "prep_time" } }, - { "math": [ "stamina", "-=", "_energy_amount" ] }, + { "math": [ "stamina", "-=", "energy_amount" ] }, { "queue_eocs": [ "EOC_GENERIC_SPELL_MUTATION_ACT" ], "time_in_future": { "context_val": "prep_time" } } ], "false_effect": [ { "u_message": { "context_val": "message_fail" }, "type": "bad" } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 3df906d46eed9..d781747d88516 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -496,10 +496,12 @@ "id": "SHRIEK", "name": { "str": "Shriek" }, "points": 3, - "description": "Activate to let loose a high pitched screech, loud enough to shatter glass and daze anything in the immediate vicinity.", + "description": "Activate to let loose a high pitched screech, loud enough to shatter glass and daze anything in the immediate vicinity. Mouth-covering gear may hinder the effect.", "prereqs": [ "BATEARS", "GOODCARDIO" ], "category": [ "CHIROPTERAN" ], - "active": true + "active": true, + "activated_is_setup": false, + "activated_eocs": [ "EOC_SHRIEK" ] }, { "type": "mutation", @@ -1673,6 +1675,7 @@ "points": -1, "description": "Your skin is fragile. Cutting and bashing damage is slightly increased for you.", "types": [ "ARMOR" ], + "category": [ "CHIROPTERAN" ], "armor": [ { "part_types": [ "torso", "head", "arm", "hand", "leg", "foot", "mouth", "tail" ], "cut": -1, "bash": -1 } ] }, { @@ -4079,7 +4082,7 @@ "description": "Your immune system is particularly good at resisting infections. You have an increased chance for bad wounds and infections to heal on their own, and only suffer reduced penalties from them.", "starting_trait": true, "changes_to": [ "INFIMMUNE" ], - "category": [ "TROGLOBITE", "RAT", "MEDICAL", "GASTROPOD", "CHIMERA", "SLIME", "CHIROPTERAN" ] + "category": [ "TROGLOBITE", "RAT", "MEDICAL", "GASTROPOD", "CHIMERA", "SLIME" ] }, { "type": "mutation", @@ -4302,6 +4305,7 @@ "id": "LEGS_BAT", "name": { "str": "Bow-legged" }, "points": -1, + "mixed_effect": true, "visibility": 2, "ugliness": 2, "description": "Your lower limbs have shortened and splayed, slowing your bipedal movement. As long as you're not wielding anything and you've got both wings, you're able to crawl quickly and quietly when you run or crouch.", @@ -6505,6 +6509,7 @@ "points": 4, "visibility": 9, "ugliness": 4, + "mixed_effect": true, "description": "Most of your fingers have grown to tremendous length, becoming a broad pair of leathery wings. This allows you to arrest a fall or glide from a ledge if you aren't too weighed down, but naturally impedes your fine motor skills. They even keep you warm when you sleep.", "types": [ "ARMS", "HANDS" ], "flags": [ "WING_GLIDE", "WINGS_2", "ARM_WINGS" ], @@ -7531,7 +7536,8 @@ "type": "mutation", "id": "BLOODFEEDER", "name": { "str": "Bloodfeeder" }, - "points": -5, + "points": -2, + "mixed_effect": true, "description": "Your craving for blood borders on obsession. Your system has gained a mild tolerance to the toxins polluting mutant flesh. When it comes to blood (and only blood), you need the stuff too much to care about whether it came from a human.", "types": [ "DIET" ], "flags": [ "BLOODFEEDER" ], diff --git a/data/json/techniques.json b/data/json/techniques.json index 6876eba1b5231..c69b768990ec6 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3600,6 +3600,15 @@ } ] }, + "tech_effects": [ + { + "id": "anticoagulant_draculin", + "chance": 70, + "duration": 6000, + "on_damage": true, + "message": "Saliva glistens across %s's wound!" + } + ], "flat_bonuses": [ { "stat": "damage", "type": "stab", "scale": 21 }, { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, @@ -3623,6 +3632,15 @@ "attack_override": true, "crit_ok": false, "attack_vectors": [ "MOUTH" ], + "tech_effects": [ + { + "id": "anticoagulant_draculin", + "chance": 70, + "duration": 6000, + "on_damage": true, + "message": "Saliva glistens across %s's wound!" + } + ], "condition": { "and": [ { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } @@ -3650,6 +3668,15 @@ "reach_ok": false, "attack_override": true, "crit_tec": true, + "tech_effects": [ + { + "id": "anticoagulant_draculin", + "chance": 100, + "duration": 6000, + "on_damage": true, + "message": "Saliva glistens across %s's wound!" + } + ], "attack_vectors": [ "MOUTH" ], "flat_bonuses": [ { "stat": "damage", "type": "stab", "scale": 25 }, diff --git a/src/mutation.cpp b/src/mutation.cpp index 4c5609108f583..e9a778efe2981 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -879,6 +879,7 @@ void Character::activate_mutation( const trait_id &mut ) return; } else if( mut == trait_ECHOLOCATION ) { echo_pulse(); + deactivate_mutation( mut ); } else if( mut == trait_TREE_COMMUNION || mut == trait_ARVORE_FOREST_MAPPING ) { tdata.powered = false; // Check for adjacent trees. From 1d4ff76fab2d10b632e540dfd7daf6883d18b23d Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 28 Jan 2024 14:35:16 -0800 Subject: [PATCH 048/202] hm --- data/json/effects.json | 6 ++-- data/json/flags.json | 5 +++ data/json/mutations/mutations.json | 51 +++++++++------------------- data/json/species.json | 37 +++++++++++++------- data/json/techniques.json | 54 ++++++++++++++---------------- src/sounds.cpp | 7 ++-- 6 files changed, 80 insertions(+), 80 deletions(-) diff --git a/data/json/effects.json b/data/json/effects.json index d07341576b319..68159bd9646c7 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -2037,10 +2037,12 @@ "type": "effect_type", "id": "anticoagulant_draculin", "max_intensity": 3, - "max_duration": "30 m", + "max_duration": "20 m", "rating": "bad", + "show_in_info": true, "blood_analysis_description": "Draculin", - "//": "yes it's actually called that. It is also the strongest anti-clotting factor known to science.", + "//": "yes it's actually called that. It is also the strongest anti-clotting factor known to science. It inhibits the synthesis of thrombin, so most earth animals/mutants should be affected, except bugs.", + "immune_flags": [ "DRACULIN_IMMUNE" ], "effect_dur_scaling": [ { "effect_id": "bleed", "modifier": 2.0, "same_bp": false } ] }, { diff --git a/data/json/flags.json b/data/json/flags.json index 3e70f2ee5cb1b..1ef88fff5ff00 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2438,5 +2438,10 @@ "id": "QUADRUPED_RUN", "type": "json_flag", "//": "This character can run or crouch on all fours if they have QUADRUPED_CROUCH, and gains the natural_stance effect. Used for paw mutations." + }, + { + "id": "HEARING_PROTECTION", + "type": "json_flag", + "//": "This character treats received sounds as half as loud for the purposes of determing hearing damage." } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index d781747d88516..f888d52a7be90 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -475,34 +475,25 @@ "id": "BATEARS", "name": { "str": "Bat Ears" }, "points": 1, - "description": "Your broad, pointed ears are like radar dishes. You can hear even the slightest sounds at great distance, and most loud noises don't seem to bother you.", + "ugliness": 8, + "visibility": 8, + "description": "Your broad, pointed ears are like radar dishes. You can hear even the slightest sounds at great distance, and are less likely to be deafened by loud noises.", "types": [ "HEARING" ], + "flags": [ "HEARING_PROTECTION" ], "prereqs": [ "GOODHEARING" ], "category": [ "CHIROPTERAN" ], - "hearing_modifier": 2.5 + "hearing_modifier": 2 }, { "type": "mutation", "id": "ECHOLOCATION", "name": { "str": "Echolocation" }, "points": 3, - "description": "You've learned how to hunt without sight. Activate this mutation to make an ultrasonic sound that can reveal obstacles and enemies nearby.", + "description": "You've learned how to hunt without sight. Activate this mutation to make a quiet chirp that can reveal obstacles and enemies by its echo.", "prereqs": [ "BATEARS", "GOODCARDIO" ], "category": [ "CHIROPTERAN" ], "active": true }, - { - "type": "mutation", - "id": "SHRIEK", - "name": { "str": "Shriek" }, - "points": 3, - "description": "Activate to let loose a high pitched screech, loud enough to shatter glass and daze anything in the immediate vicinity. Mouth-covering gear may hinder the effect.", - "prereqs": [ "BATEARS", "GOODCARDIO" ], - "category": [ "CHIROPTERAN" ], - "active": true, - "activated_is_setup": false, - "activated_eocs": [ "EOC_SHRIEK" ] - }, { "type": "mutation", "id": "LEAFNOSE", @@ -512,8 +503,8 @@ "flags": [ "INFRARED" ], "category": [ "CHIROPTERAN" ], "ugliness": 3, - "active": true, - "description": "Your nose is upturned, ridged and flared out into a leaf shape that humans will likely find unsightly. On the plus side, thermoreceptors in your nostrils allow you to smell heat as long as your face is uncovered." + "description": "Your nose is upturned, ridged and flared out into a leaf shape that humans will likely find unsightly. On the plus side, thermoreceptors in your nostrils allow you to smell heat as long as you can breathe.", + "triggers": [ [ { "condition": { "not": { "or": [ { "u_has_effect": "smoke", "bodypart": "mouth" }, { "u_has_effect": "asthma" }, { "u_has_effect": "tpollen" }, { "u_has_effect": "asthma" }, { "u_has_effect": "hay_fever" }, "u_is_underwater" ] } }, "msg_on": { "text": "", "rating": "good" } } ] ] }, { "type": "mutation", @@ -4313,7 +4304,7 @@ "prereqs": [ "PADDED_FEET", "WINGS_BAT" ], "category": [ "CHIROPTERAN" ], "threshreq": [ "THRESH_CHIROPTERAN" ], - "movecost_modifier": 1.35, + "movecost_modifier": 1.38, "wet_protection": [ { "part": "foot_l", "neutral": 10 }, { "part": "foot_r", "neutral": 10 } ], "//": "Uses bespoke stuff here rather than the quadruped enchantments as bat-crawling gives different buffs.", "triggers": [ @@ -4334,25 +4325,14 @@ "enchantments": [ { "condition": { "and": [ { "u_has_move_mode": "crouch" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, - "values": [ { "value": "MOVE_COST", "multiply": -0.18 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ], + "values": [ { "value": "MOVE_COST", "multiply": -0.58 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ], "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] }, { "condition": { "and": [ { "u_has_move_mode": "run" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, - "values": [ { "value": "MOVE_COST", "multiply": -0.18 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.6 } ], - "ench_effects": [ { "effect": "natural_stance", "intensity": 1 }, { "effect": "all_fours", "intensity": 1 } ] - }, - { - "condition": { - "or": [ - "u_can_drop_weapon", - { "u_has_move_mode": "walk" }, - { "not": { "u_has_trait": "LEGS_BAT" } }, - { "not": { "u_has_trait": "WINGS_BAT" } } - ] - }, - "values": [ { "value": "MOVE_COST", "multiply": -0.1 } ] - } + "values": [ { "value": "MOVE_COST", "multiply": -0.20 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.6 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] + } ] }, { @@ -6500,7 +6480,7 @@ "description": "You have a pair of stubby little wings projecting from your shoulderblades. They can be wiggled at will, but are useless.", "types": [ "WINGS" ], "changes_to": [ "WINGS_INSECT", "WINGS_BUTTERFLY" ], - "category": [ "INSECT" ] + "category": [ "INSECT", "BIRD" ] }, { "type": "mutation", @@ -6513,6 +6493,7 @@ "description": "Most of your fingers have grown to tremendous length, becoming a broad pair of leathery wings. This allows you to arrest a fall or glide from a ledge if you aren't too weighed down, but naturally impedes your fine motor skills. They even keep you warm when you sleep.", "types": [ "ARMS", "HANDS" ], "flags": [ "WING_GLIDE", "WINGS_2", "ARM_WINGS" ], + "encumbrance_always": [ [ "arm_r", 20 ], [ "arm_l", 20 ], [ "hand_r", 40 ], [ "hand_l" , 40 ] ], "prereqs": [ "WEBBED" ], "category": [ "CHIROPTERAN" ], "restricts_gear": [ "arm_l", "arm_r", "hand_l", "hand_r" ], @@ -7541,7 +7522,7 @@ "description": "Your craving for blood borders on obsession. Your system has gained a mild tolerance to the toxins polluting mutant flesh. When it comes to blood (and only blood), you need the stuff too much to care about whether it came from a human.", "types": [ "DIET" ], "flags": [ "BLOODFEEDER" ], - "cancels": [ "VEGAN", "HEMOVORE" ], + "cancels": [ "VEGAN" ], "prereqs": [ "HEMOVORE" ], "vitamins_absorb_multi": [ [ "all", [ [ "mutant_toxin", 0.5 ] ] ] ], "category": [ "CHIROPTERAN" ], diff --git a/data/json/species.json b/data/json/species.json index 1c7b2600f8328..5b956a50d2037 100644 --- a/data/json/species.json +++ b/data/json/species.json @@ -24,6 +24,7 @@ "type": "SPECIES", "id": "CYBORG", "description": "an alien cyborg", + "flags": [ "DRACULIN_IMMUNE" ], "footsteps": "heavy thuds." }, { @@ -56,38 +57,44 @@ { "type": "SPECIES", "id": "NETHER", - "description": "a nether creature" + "description": "a nether creature", + "flags": [ "DRACULIN_IMMUNE" ] }, { "type": "SPECIES", "id": "NETHER_EMANATION", "description": "a reflection of another dimension", - "fear_triggers": [ "HURT" ] + "fear_triggers": [ "HURT" ], + "flags": [ "DRACULIN_IMMUNE" ] }, { "type": "SPECIES", "id": "MIGO", "description": "a creature from the mi-go homeworld", - "fear_triggers": [ "HURT" ] + "fear_triggers": [ "HURT" ], + "flags": [ "DRACULIN_IMMUNE" ] }, { "type": "SPECIES", "id": "SLIME", "description": "a slime", "footsteps": "plop.", - "bleeds": "fd_slime" + "bleeds": "fd_slime", + "flags": [ "DRACULIN_IMMUNE" ] }, { "type": "SPECIES", "id": "FUNGUS", "description": "a fungus", - "fear_triggers": [ "HURT", "FIRE" ] + "fear_triggers": [ "HURT", "FIRE" ], + "flags": [ "DRACULIN_IMMUNE" ] }, { "type": "SPECIES", "id": "LEECH_PLANT", "description": "a leech plant", - "fear_triggers": [ "HURT", "FIRE" ] + "fear_triggers": [ "HURT", "FIRE" ], + "flags": [ "DRACULIN_IMMUNE" ] }, { "type": "SPECIES", @@ -95,7 +102,8 @@ "description": "an insect", "anger_triggers": [ "FRIEND_DIED" ], "fear_triggers": [ "HURT", "FIRE" ], - "bleeds": "fd_blood_insect" + "bleeds": "fd_blood_insect", + "flags": [ "DRACULIN_IMMUNE" ] }, { "type": "SPECIES", @@ -103,7 +111,8 @@ "description": "a centipede", "footsteps": "skittering.", "fear_triggers": [ "FIRE" ], - "bleeds": "fd_blood_insect" + "bleeds": "fd_blood_insect", + "flags": [ "DRACULIN_IMMUNE" ] }, { "type": "SPECIES", @@ -111,7 +120,7 @@ "description": "a flying insect", "footsteps": "BZZZZZZZZZZZZZZZZZ", "anger_triggers": [ "FRIEND_DIED" ], - "flags": [ "LOUDMOVES" ], + "flags": [ "LOUDMOVES", "DRACULIN_IMMUNE" ], "bleeds": "fd_blood_insect" }, { @@ -120,20 +129,23 @@ "description": "a spider", "anger_triggers": [ "FRIEND_DIED" ], "fear_triggers": [ "HURT", "FIRE" ], - "bleeds": "fd_blood_insect" + "bleeds": "fd_blood_insect", + "flags": [ "DRACULIN_IMMUNE" ] }, { "type": "SPECIES", "id": "PLANT", "description": "a plant", "fear_triggers": [ "HURT", "FIRE" ], - "bleeds": "fd_blood_veggy" + "bleeds": "fd_blood_veggy", + "flags": [ "DRACULIN_IMMUNE" ] }, { "type": "SPECIES", "id": "MOLLUSK", "description": "a mollusk", "fear_triggers": [ "HURT", "FIRE" ], + "//": "Despite having nonstandard blood, mollusks have circulatory systems similar to fish and reptiles, so no DRACULIN_IMMUNE", "bleeds": "fd_blood_invertebrate" }, { @@ -142,7 +154,8 @@ "description": "a worm", "footsteps": "rustle.", "fear_triggers": [ "HURT" ], - "bleeds": "fd_blood_invertebrate" + "bleeds": "fd_blood_invertebrate", + "flags": [ "DRACULIN_IMMUNE" ] }, { "type": "SPECIES", diff --git a/data/json/techniques.json b/data/json/techniques.json index c69b768990ec6..3337a52e5f153 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3514,12 +3514,12 @@ } ] }, + "mult_bonuses": [ + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.08 } + ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "damage", "type": "stab", "scale": 18 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, { "stat": "movecost", "scale": 50 } @@ -3542,12 +3542,12 @@ { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } ] }, + "mult_bonuses": [ + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.08 } + ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.0 }, + { "stat": "damage", "type": "stab", "scale": 18 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, { "stat": "movecost", "scale": 50 } @@ -3565,15 +3565,15 @@ "attack_override": true, "crit_tec": true, "attack_vectors": [ "MOUTH" ], + "mult_bonuses": [ + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.18 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 0.74 } + ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 16 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.0 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.3 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scale": 18 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, { "stat": "movecost", "scale": 50 } ] }, @@ -3603,17 +3603,15 @@ "tech_effects": [ { "id": "anticoagulant_draculin", - "chance": 70, - "duration": 6000, + "chance": 100, + "duration": 3000, "on_damage": true, "message": "Saliva glistens across %s's wound!" } ], "flat_bonuses": [ { "stat": "damage", "type": "stab", "scale": 21 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, @@ -3635,8 +3633,8 @@ "tech_effects": [ { "id": "anticoagulant_draculin", - "chance": 70, - "duration": 6000, + "chance": 100, + "duration": 3000, "on_damage": true, "message": "Saliva glistens across %s's wound!" } @@ -3648,9 +3646,7 @@ }, "flat_bonuses": [ { "stat": "damage", "type": "stab", "scale": 21 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, @@ -3680,7 +3676,7 @@ "attack_vectors": [ "MOUTH" ], "flat_bonuses": [ { "stat": "damage", "type": "stab", "scale": 25 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.2 }, + { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.4 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.2 }, diff --git a/src/sounds.cpp b/src/sounds.cpp index 97ce4ccb6fb33..d68a386bb1ab6 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -92,6 +92,7 @@ static const itype_id fuel_type_muscle( "muscle" ); static const itype_id fuel_type_wind( "wind" ); static const itype_id itype_weapon_fire_suppressed( "weapon_fire_suppressed" ); +static const json_character_flag json_flag_HEARING_PROTECTION( "HEARING_PROTECTION" ); static const json_character_flag json_flag_PAIN_IMMUNE( "PAIN_IMMUNE" ); static const material_id material_bone( "bone" ); @@ -568,9 +569,11 @@ void sounds::process_sound_markers( Character *you ) // The felt volume of a sound is not affected by negative multipliers, such as already // deafened players or players with sub-par hearing to begin with. - const int felt_volume = static_cast( raw_volume * std::min( 1.0f, + int felt_volume = static_cast( raw_volume * std::min( 1.0f, volume_multiplier ) ) - distance_to_sound; - + if( you->has_flag( json_flag_HEARING_PROTECTION ) ){ + felt_volume /= 2; + } // Deafening is based on the felt volume, as a player may be too deaf to // hear the deafening sound but still suffer additional hearing loss. const bool is_sound_deafening = rng( felt_volume / 2, felt_volume ) >= 150; From 6efed8c5a89b1a137a2f03d014cd106207b12144 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:34:21 -0800 Subject: [PATCH 049/202] crit_tec_id --- .vscode/settings.json | 93 ++++++++++++++++++++++++++- data/json/items/armor/integrated.json | 2 +- data/json/techniques.json | 15 +++-- src/character.cpp | 2 +- src/martialarts.cpp | 4 +- src/martialarts.h | 5 ++ src/melee.cpp | 4 +- 7 files changed, 114 insertions(+), 11 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4451dc963898c..934864ecd9c63 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -65,6 +65,97 @@ // NOTE: This disable the plugins formatting so astyle is used. "C_Cpp.formatting": "disabled", "files.associations": { - "vector": "cpp" + "vector": "cpp", + "algorithm": "cpp", + "array": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "cfenv": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "cinttypes": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "csetjmp": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cuchar": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "exception": "cpp", + "resumable": "cpp", + "filesystem": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "functional": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "numeric": "cpp", + "optional": "cpp", + "ostream": "cpp", + "queue": "cpp", + "random": "cpp", + "ratio": "cpp", + "regex": "cpp", + "scoped_allocator": "cpp", + "set": "cpp", + "span": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "utility": "cpp", + "variant": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstddef": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp" }, } diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index 4fc33c37a196e..be0868d408d22 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1628,7 +1628,7 @@ "warmth": 5, "to_hit": 1, "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], - "techniques": [ "VAMPIRE_BITE", "VAMPIRE_BITE_NATURAL", "VAMPIRE_BITE_CRIT" ], + "techniques": [ "VAMPIRE_BITE", "VAMPIRE_BITE_NATURAL" ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], "armor": [ { diff --git a/data/json/techniques.json b/data/json/techniques.json index 3337a52e5f153..845e48427918b 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3587,7 +3587,8 @@ "weighting": -5, "reach_ok": false, "attack_override": true, - "crit_ok": false, + "crit_ok": true, + "crit_tec_id": "VAMPIRE_BITE_CRIT", "attack_vectors": [ "MOUTH" ], "condition": { "and": [ @@ -3606,7 +3607,8 @@ "chance": 100, "duration": 3000, "on_damage": true, - "message": "Saliva glistens across %s's wound!" + "message": "Saliva glistens across %s's wound!", + "req_flag": "DRACULIN_VENOM" } ], "flat_bonuses": [ @@ -3628,7 +3630,8 @@ "weighting": -2, "reach_ok": false, "attack_override": true, - "crit_ok": false, + "crit_ok": true, + "crit_tec_id": "VAMPIRE_BITE_CRIT", "attack_vectors": [ "MOUTH" ], "tech_effects": [ { @@ -3636,7 +3639,8 @@ "chance": 100, "duration": 3000, "on_damage": true, - "message": "Saliva glistens across %s's wound!" + "message": "Saliva glistens across %s's wound!", + "req_flag": "DRACULIN_VENOM" } ], "condition": { @@ -3670,7 +3674,8 @@ "chance": 100, "duration": 6000, "on_damage": true, - "message": "Saliva glistens across %s's wound!" + "message": "Saliva glistens across %s's wound!", + "req_flag": "DRACULIN_VENOM" } ], "attack_vectors": [ "MOUTH" ], diff --git a/src/character.cpp b/src/character.cpp index 888f3737394cc..e7fdb61a63851 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -361,7 +361,7 @@ static const limb_score_id limb_score_night_vis( "night_vis" ); static const limb_score_id limb_score_reaction( "reaction" ); static const limb_score_id limb_score_vision( "vision" ); -static const matec_id tec_none( "tec_none" ); +const matec_id tec_none( "tec_none" ); static const material_id material_budget_steel( "budget_steel" ); static const material_id material_ch_steel( "ch_steel" ); diff --git a/src/martialarts.cpp b/src/martialarts.cpp index 25d2130530a6e..73c6b2aa1532e 100644 --- a/src/martialarts.cpp +++ b/src/martialarts.cpp @@ -47,8 +47,6 @@ static const json_character_flag json_flag_NONSTANDARD_BLOCK( "NONSTANDARD_BLOCK static const limb_score_id limb_score_block( "block" ); -static const matec_id tec_none( "tec_none" ); - static const skill_id skill_unarmed( "unarmed" ); static const weapon_category_id weapon_category_OTHER_INVALID_WEAP_CAT( "OTHER_INVALID_WEAP_CAT" ); @@ -221,6 +219,7 @@ void ma_technique::load( const JsonObject &jo, const std::string &src ) optional( jo, was_loaded, "crit_tec", crit_tec, false ); optional( jo, was_loaded, "crit_ok", crit_ok, false ); + optional( jo, was_loaded, "crit_tec_id", crit_tec_id, tec_none ); optional( jo, was_loaded, "attack_override", attack_override, false ); optional( jo, was_loaded, "wall_adjacent", wall_adjacent, false ); optional( jo, was_loaded, "reach_tec", reach_tec, false ); @@ -823,6 +822,7 @@ ma_technique::ma_technique() { crit_tec = false; crit_ok = false; + crit_tec_id = tec_none; // if not tec_none, use this tech instead when a crit procs defensive = false; side_switch = false; // moves the target behind user dummy = false; diff --git a/src/martialarts.h b/src/martialarts.h index 4cb609a5d2b68..34c659aaf0c24 100644 --- a/src/martialarts.h +++ b/src/martialarts.h @@ -26,6 +26,8 @@ class effect; class item; struct itype; +extern const matec_id tec_none; + class weapon_category { public: @@ -150,6 +152,9 @@ class ma_technique bool dummy = false; bool crit_tec = false; bool crit_ok = false; + // performs the listed technique if this attack procs a crit. tec_none skips this behavior. + // requires crit_ok to be true + matec_id crit_tec_id = tec_none; bool reach_tec = false; // only possible to use during a reach attack bool reach_ok = false; // possible to use during a reach attack bool attack_override = false; // The attack replaces the one it triggered off of diff --git a/src/melee.cpp b/src/melee.cpp index f32d4e4e000bc..0b4a7f513adc5 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -132,7 +132,6 @@ static const matec_id WBLOCK_1( "WBLOCK_1" ); static const matec_id WBLOCK_2( "WBLOCK_2" ); static const matec_id WBLOCK_3( "WBLOCK_3" ); static const matec_id WHIP_DISARM( "WHIP_DISARM" ); -static const matec_id tec_none( "tec_none" ); static const material_id material_glass( "glass" ); static const material_id material_steel( "steel" ); @@ -737,6 +736,9 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, technique_id = force_technique; } else if( allow_special ) { technique_id = pick_technique( t, cur_weapon, critical_hit, false, false ); + if( critical_hit && technique_id.obj().crit_tec_id != tec_none ) { + technique_id = technique_id.obj().crit_tec_id; + } } else { technique_id = tec_none; } From e3ec8873097f952a2a013c28f1731f9e770df9fb Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:29:29 -0800 Subject: [PATCH 050/202] fangs --- data/json/flags.json | 8 ++ data/json/items/armor/integrated.json | 17 ++-- data/json/monsters/monster_flags.json | 5 + data/json/mutations/mutations.json | 11 ++ data/json/techniques.json | 139 ++++++++++++++++---------- doc/MARTIALART_JSON.md | 2 +- src/bonuses.cpp | 27 ++++- src/bonuses.h | 2 + src/mtype.cpp | 2 + src/mtype.h | 1 + 10 files changed, 150 insertions(+), 64 deletions(-) diff --git a/data/json/flags.json b/data/json/flags.json index 1ef88fff5ff00..b9841c36e9273 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2443,5 +2443,13 @@ "id": "HEARING_PROTECTION", "type": "json_flag", "//": "This character treats received sounds as half as loud for the purposes of determing hearing damage." + }, + { + "id": "DRACULIN_VENOM", + "type": "json_flag" + }, + { + "id": "VENOM1", + "type": "json_flag" } ] diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index be0868d408d22..9adfd9ec08920 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1590,17 +1590,17 @@ "category": "armor", "name": { "str_sp": "fangs" }, "description": "A pair of fangs sharp and sturdy enough to use in combat.", - "weight": "100 g", - "volume": "112 ml", + "//": "Weight and volume are arbitrarily high in order to give them 75 base moves per attack", + "weight": "275 g", + "volume": "400 ml", "price": 0, "price_postapoc": 0, "material": [ "bone" ], "symbol": ",", "color": "white", "warmth": 5, - "to_hit": 1, "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], - "techniques": [ "FANGS_BITE", "FANGS_BITE_NATURAL", "FANGS_BITE_CRIT" ], + "techniques": [ "FANGS_BITE", "FANGS_BITE_NATURAL" ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], "armor": [ { @@ -1610,7 +1610,8 @@ "encumbrance": 0 } ], - "melee_damage": { "stab": 16 } + "skill": "unarmed", + "melee_damage": { "stab": 14 } }, { "id": "integrated_vampire_fangs", @@ -1618,15 +1619,15 @@ "category": "armor", "name": { "str_sp": "vampire fangs" }, "description": "A wicked pair of fangs, pointed and surgically sharp.", - "weight": "112 g", - "volume": "125 ml", + "//": "Weight and volume are arbitrarily high in order to give them 75 base moves per attack", + "weight": "275 g", + "volume": "400 ml", "price": 0, "price_postapoc": 0, "material": [ "bone" ], "symbol": ",", "color": "white", "warmth": 5, - "to_hit": 1, "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], "techniques": [ "VAMPIRE_BITE", "VAMPIRE_BITE_NATURAL" ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], diff --git a/data/json/monsters/monster_flags.json b/data/json/monsters/monster_flags.json index 833f2b366516c..56d73809a1dce 100644 --- a/data/json/monsters/monster_flags.json +++ b/data/json/monsters/monster_flags.json @@ -628,5 +628,10 @@ "id": "GUILT_OTHERS", "type": "monster_flag", "//": "Warning: Do not use without death_guilt function. This helps count the all the other kills that do not fit the other three (e.g blood sacrifice) before player goes numb." + }, + { + "id": "DRACULIN_IMMUNE", + "type": "monster_flag", + "//": "Immune to Chiropteran saliva. Use for any creature with nonstandard blood (ie mi-go, insects, but probably not zombies or fungalized animals, unless they have acid blood or something)." } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index f888d52a7be90..03a6b7b8c853d 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -4125,6 +4125,7 @@ "description": "Your body produces a potent venom. Cutting or stabbing attacks from mutations have a chance to poison your target.", "prereqs": [ "POISRESIST" ], "changes_to": [ "POISONOUS2" ], + "flags": [ "VENOM1" ], "category": [ "SLIME", "TROGLOBITE", "SPIDER", "CEPHALOPOD", "GASTROPOD", "CHIMERA", "BATRACHIAN" ] }, { @@ -7529,6 +7530,16 @@ "threshreq": [ "THRESH_CHIROPTERAN" ], "vitamin_rates": [ [ "iron", 2400 ] ] }, + { + "type": "mutation", + "id": "BLOODLETTER", + "name": { "str": "Bloodletter" }, + "points": 2, + "description": "Your saliva contains powerful anticoagulants. If you have a bite attack, it will prolong the bleeding of most animals, including humans and the undead. It may be less effective against insects and stranger creatures.", + "flags": [ "DRACULIN_VENOM" ], + "prereqs": [ "HEMOVORE", "BLOODFEEDER" ], + "category": [ "CHIROPTERAN" ] + }, { "type": "mutation", "id": "PONDEROUS1", diff --git a/data/json/techniques.json b/data/json/techniques.json index 845e48427918b..31cfecbeba96b 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3496,12 +3496,13 @@ "id": "FANGS_BITE", "name": "Fang Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You bite %s", " bites %s!" ], "unarmed_allowed": true, "weighting": -5, "reach_ok": false, "attack_override": true, - "crit_ok": false, + "crit_ok": true, + "crit_tec_id": "FANGS_BITE_CRIT", "attack_vectors": [ "MOUTH" ], "condition": { "and": [ @@ -3514,15 +3515,24 @@ } ] }, - "mult_bonuses": [ - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.08 } + "tech_effects": [ + { + "id": "anticoagulant_draculin", + "chance": 70, + "duration": 3000, + "on_damage": true, + "message": "Saliva glistens across %s's wound!", + "req_flag": "DRACULIN_VENOM" + } ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 18 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, + { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 0.06 }, + { "stat": "movecost", "scale": 75 }, + { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, + { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } ] }, { @@ -3530,27 +3540,37 @@ "id": "FANGS_BITE_NATURAL", "name": "Fang Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You bite %s", " bites %s!" ], "unarmed_allowed": true, "weighting": -2, "reach_ok": false, "attack_override": true, - "crit_ok": false, + "crit_ok": true, + "crit_tec_id": "FANGS_BITE_CRIT", "attack_vectors": [ "MOUTH" ], "condition": { "and": [ { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } ] }, - "mult_bonuses": [ - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.08 } + "tech_effects": [ + { + "id": "anticoagulant_draculin", + "chance": 70, + "duration": 3000, + "on_damage": true, + "message": "Saliva glistens across %s's wound!", + "req_flag": "DRACULIN_VENOM" + } ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 18 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, + { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 0.06 }, + { "stat": "movecost", "scale": 75 }, + { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, + { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } ] }, { @@ -3558,23 +3578,32 @@ "id": "FANGS_BITE_CRIT", "name": "Fang Bite (crit)", "melee_allowed": true, - "messages": [ "You deliver a wicked bite to %s!", " delivers a wicked bite to %s!" ], + "messages": [ "You deliver a wicked bite to %s", " delivers a wicked bite to %s!" ], "unarmed_allowed": true, - "weighting": -4, "reach_ok": false, "attack_override": true, "crit_tec": true, "attack_vectors": [ "MOUTH" ], - "mult_bonuses": [ - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.18 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 0.74 } + "tech_effects": [ + { + "id": "anticoagulant_draculin", + "chance": 90, + "duration": 6000, + "on_damage": true, + "message": "Saliva glistens across %s's wound!", + "req_flag": "DRACULIN_VENOM" + } ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 18 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } + { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 4.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, + { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 2.1 }, + { "stat": "arpen", "type": "bash", "scaling-stat": "bash", "scale": 0.5 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 0.66 }, + { "stat": "movecost", "scale": 75 }, + { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, + { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } ] }, { @@ -3582,7 +3611,7 @@ "id": "VAMPIRE_BITE", "name": "Vampire Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You bite %s", " bites %s!" ], "unarmed_allowed": true, "weighting": -5, "reach_ok": false, @@ -3604,7 +3633,7 @@ "tech_effects": [ { "id": "anticoagulant_draculin", - "chance": 100, + "chance": 90, "duration": 3000, "on_damage": true, "message": "Saliva glistens across %s's wound!", @@ -3612,12 +3641,13 @@ } ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 21 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } + { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, + { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 0.06 }, + { "stat": "movecost", "scale": 75 }, + { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, + { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } ] }, { @@ -3625,7 +3655,7 @@ "id": "VAMPIRE_BITE_NATURAL", "name": "Vampire Bite", "melee_allowed": true, - "messages": [ "You bite %s!", " bites %s!" ], + "messages": [ "You bite %s", " bites %s!" ], "unarmed_allowed": true, "weighting": -2, "reach_ok": false, @@ -3636,7 +3666,7 @@ "tech_effects": [ { "id": "anticoagulant_draculin", - "chance": 100, + "chance": 90, "duration": 3000, "on_damage": true, "message": "Saliva glistens across %s's wound!", @@ -3649,12 +3679,13 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 21 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 2.2 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.1 }, - { "stat": "movecost", "scale": 50 } + { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, + { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 0.06 }, + { "stat": "movecost", "scale": 75 }, + { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, + { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } ] }, { @@ -3662,7 +3693,7 @@ "id": "VAMPIRE_BITE_CRIT", "name": "Vampire Bite (crit)", "melee_allowed": true, - "messages": [ "You sink your fangs deep into %s!", " sinks their fangs deep into %s!" ], + "messages": [ "You sink your fangs deep into %s", " sinks their fangs deep into %s!" ], "unarmed_allowed": true, "weighting": -4, "reach_ok": false, @@ -3680,15 +3711,15 @@ ], "attack_vectors": [ "MOUTH" ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 25 }, - { "stat": "damage", "type": "stab", "scaling-stat": "str", "scale": 0.6 }, - { "stat": "damage", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "damage", "type": "stab", "scaling-stat": "per", "scale": 0.4 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 3.2 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "per", "scale": 0.4 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "dex", "scale": 0.2 }, - { "stat": "movecost", "scale": 50 } + { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 4.0 }, + { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, + { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 3.0 }, + { "stat": "arpen", "type": "bash", "scaling-stat": "bash", "scale": 0.5 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 0.66 }, + { "stat": "movecost", "scale": 75 }, + { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, + { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } ] } ] diff --git a/doc/MARTIALART_JSON.md b/doc/MARTIALART_JSON.md index fe5cfd01f8455..8c21c1c5e1127 100644 --- a/doc/MARTIALART_JSON.md +++ b/doc/MARTIALART_JSON.md @@ -70,7 +70,7 @@ "id": "tec_debug_arpen", // Unique ID. Must be one continuous word "name": "phasing strike", // In-game name displayed "attack_vectors": [ "WEAPON", "HAND" ] // What attack vector would be used for this technique; this field is order dependend, meaning in this example the game will try to use WEAPON first, and, if not possible, reject it and will use HAND instead; For more info see Attack vectors below - "attack_vectors_random": [ "FOOT", "HEAD", "TORSO", "HEAD", "HEAD" ] // same as attack_vectors, but has no priority, and pick random vector from the list; it is used only if all choises from attack_vectors are rejected + "attack_vectors_random": [ "FOOT", "HEAD", "TORSO", "HEAD", "HEAD", "MOUTH" ] // same as attack_vectors, but has no priority, and pick random vector from the list; it is used only if all choises from attack_vectors are rejected "unarmed_allowed": true, // Can an unarmed character use this technique "unarmed_weapons_allowed": true, // Does this technique require the character to be actually unarmed or does it allow unarmed weapons "weapon_categories_allowed": [ "BLADES", "KNIVES" ], // Restrict technique to only these categories of weapons. If omitted, all weapon categories are allowed. diff --git a/src/bonuses.cpp b/src/bonuses.cpp index d7ac7210e5c48..68cce283b6500 100644 --- a/src/bonuses.cpp +++ b/src/bonuses.cpp @@ -27,6 +27,8 @@ static const skill_id skill_throw( "throw" ); static const skill_id skill_launcher( "launcher" ); static const skill_id skill_archery( "archery" ); static const skill_id skill_drive( "drive" ); +static const skill_id skill_firstaid( "firstaid" ); +static const skill_id skill_spellcraft( "spellcraft" ); static bool needs_damage_type( affected_stat as ) { @@ -70,7 +72,9 @@ static const std::map scaling_stat_map = {{ std::make_pair( "archery", SKILL_ARCHERY ), std::make_pair( "throw", SKILL_THROW ), std::make_pair( "launcher", SKILL_LAUNCHER ), - std::make_pair( "drive", SKILL_DRIVE ) + std::make_pair( "drive", SKILL_DRIVE ), + std::make_pair( "firstaid", SKILL_FIRSTAID ), + std::make_pair( "spellcraft", SKILL_SPELLCRAFT ) } }; @@ -120,6 +124,24 @@ static const std::map scaling_stat_map_translation = std::make_pair( STAT_DEX, translate_marker( "dexterity" ) ), std::make_pair( STAT_INT, translate_marker( "intelligence" ) ), std::make_pair( STAT_PER, translate_marker( "perception" ) ), + std::make_pair( SKILL_BASHING, translate_marker( "bashing" ) ), + std::make_pair( SKILL_CUTTING, translate_marker( "cutting" ) ), + std::make_pair( SKILL_DODGE, translate_marker( "dodging" ) ), + std::make_pair( SKILL_MELEE, translate_marker( "melee" ) ), + std::make_pair( SKILL_STABBING, translate_marker( "stabbing" ) ), + std::make_pair( SKILL_SWIMMING, translate_marker( "swimming" ) ), + std::make_pair( SKILL_UNARMED, translate_marker( "unarmed" ) ), + std::make_pair( SKILL_GUN, translate_marker( "marksmanship" ) ), + std::make_pair( SKILL_PISTOL, translate_marker( "pistols" ) ), + std::make_pair( SKILL_RIFLE, translate_marker( "rifles" ) ), + std::make_pair( SKILL_SHOTGUN, translate_marker( "shotguns" ) ), + std::make_pair( SKILL_SMG, translate_marker( "SMGs" ) ), + std::make_pair( SKILL_ARCHERY, translate_marker( "archery" ) ), + std::make_pair( SKILL_THROW, translate_marker( "throwing" ) ), + std::make_pair( SKILL_LAUNCHER, translate_marker( "launchers" ) ), + std::make_pair( SKILL_DRIVE, translate_marker( "driving" ) ), + std::make_pair( SKILL_FIRSTAID, translate_marker( "health care" ) ), + std::make_pair( SKILL_SPELLCRAFT, translate_marker( "spellcraft" ) ), } }; @@ -357,6 +379,9 @@ float effect_scaling::get( const Character &u ) const case SKILL_DRIVE: bonus = scale * u.get_skill_level( skill_drive ); break; + case SKILL_SPELLCRAFT: + bonus = scale * u.get_skill_level( skill_spellcraft ); + break; case STAT_NULL: bonus = scale; case NUM_STATS: diff --git a/src/bonuses.h b/src/bonuses.h index ea7f273684330..27f10f711c30f 100644 --- a/src/bonuses.h +++ b/src/bonuses.h @@ -34,6 +34,8 @@ enum scaling_stat : int { SKILL_THROW, SKILL_LAUNCHER, SKILL_DRIVE, + SKILL_FIRSTAID, + SKILL_SPELLCRAFT, NUM_STATS }; diff --git a/src/mtype.cpp b/src/mtype.cpp index 891205fb32e12..447261ec9875c 100644 --- a/src/mtype.cpp +++ b/src/mtype.cpp @@ -70,6 +70,7 @@ mon_flag_id mon_flag_ACIDPROOF, mon_flag_DOGFOOD, mon_flag_DORMANT, mon_flag_GEN_DORMANT, + mon_flag_DRACULIN_IMMUNE, mon_flag_DRIPS_GASOLINE, mon_flag_DRIPS_NAPALM, mon_flag_DROPS_AMMO, @@ -193,6 +194,7 @@ void set_mon_flag_ids() mon_flag_DOGFOOD = mon_flag_id( "DOGFOOD" ); mon_flag_DORMANT = mon_flag_id( "DORMANT" ); mon_flag_GEN_DORMANT = mon_flag_id( "GEN_DORMANT" ); + mon_flag_DRACULIN_IMMUNE = mon_flag_id( "DRACULIN_IMMUNE" ); mon_flag_DRIPS_GASOLINE = mon_flag_id( "DRIPS_GASOLINE" ); mon_flag_DRIPS_NAPALM = mon_flag_id( "DRIPS_NAPALM" ); mon_flag_DROPS_AMMO = mon_flag_id( "DROPS_AMMO" ); diff --git a/src/mtype.h b/src/mtype.h index c97bf3cc55ed3..fe67a3d7e037d 100644 --- a/src/mtype.h +++ b/src/mtype.h @@ -110,6 +110,7 @@ extern mon_flag_id mon_flag_ACIDPROOF, mon_flag_DOGFOOD, mon_flag_DORMANT, mon_flag_GEN_DORMANT, + mon_flag_DRACULIN_IMMUNE, mon_flag_DRIPS_GASOLINE, mon_flag_DRIPS_NAPALM, mon_flag_DROPS_AMMO, From 604ff1f5a02206b251478030c11cea5ea8f09b96 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:41:19 -0800 Subject: [PATCH 051/202] woo --- data/json/dreams.json | 42 ++++++++++++++++ data/json/effects.json | 25 ++++++++++ data/json/harvest_dissect.json | 24 +++++++++ data/json/itemgroups/Labs/labs_mutagen.json | 1 + .../harvest_dissection.json | 24 +++++++++ data/json/itemgroups/science_and_tech.json | 3 ++ data/json/items/armor/integrated.json | 1 - data/json/items/comestibles/mutagen.json | 28 +++++++++++ data/json/items/mutagen_ingredients.json | 10 ++++ data/json/monsters/mammal.json | 2 +- data/json/mutations/mutations.json | 10 ++-- data/json/recipes/chem/mutagens.json | 49 +++++++++++++++++++ data/json/recipes/nested.json | 2 + data/json/vitamin.json | 12 +++++ src/bonuses.cpp | 3 ++ src/consumption.cpp | 6 +-- 16 files changed, 232 insertions(+), 10 deletions(-) diff --git a/data/json/dreams.json b/data/json/dreams.json index 2f16b7e439f6d..ca6f6dcb10d46 100644 --- a/data/json/dreams.json +++ b/data/json/dreams.json @@ -35,6 +35,16 @@ "category": "GASTROPOD", "strength": 1 }, + { + "type": "dream", + "messages": [ + "You dream about the moon.", + "You dream you're safely at home with a bustling family.", + "You dream of chasing a beautiful butterfly." + ], + "category": "CHIROPTERAN", + "strength": 1 + }, { "type": "dream", "messages": [ @@ -207,6 +217,17 @@ "category": "RABBIT", "strength": 2 }, + { + "type": "dream", + "messages": [ + "You dream of flying.", + "You have an upsetting dream about falling and being unable to get back up.", + "Tinnitus follows you into your dreams, where it becomes a chorus of crickets.", + "You dream of warm breath and fresh blood." + ], + "category": "CHIROPTERAN", + "strength": 2 + }, { "type": "dream", "messages": [ @@ -442,6 +463,17 @@ "category": "BIRD", "strength": 3 }, + { + "type": "dream", + "messages": [ + "You have a nightmare of being all alone, but then your family flies home to comfort you.", + "You dream of sneaking into someone's home to listen to them sleep.", + "You taste iron all night long, like you've bitten your tongue.", + "You dream in sounds and smells, not images." + ], + "category": "CHIROPTERAN", + "strength": 3 + }, { "type": "dream", "messages": [ @@ -721,6 +753,16 @@ "category": "CATTLE", "strength": 4 }, + { + "type": "dream", + "messages": [ + "In your dreams, your fangs slip through flesh like scalpels. You quickly crawl away to let your clumsy prey bleed out.", + "You flutter through the air, listening for echoes to guide you through your dream.", + "You dream of friends and family crowded around, chattering noisily, grooming one another, and sharing blood." + ], + "category": "CHIROPTERAN", + "strength": 4 + }, { "type": "dream", "messages": [ diff --git a/data/json/effects.json b/data/json/effects.json index 68159bd9646c7..6536685f4dc3a 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -1631,6 +1631,31 @@ "rating": "bad", "blood_analysis_description": "Rat Primer Contamination" }, + { + "type": "effect_type", + "id": "mutagen_chiropteran", + "name": [ "Chiropteran Mutation", "Chiropteran Transformation", "Chiropteran Metamorphosis" ], + "desc": [ + "You consumed chiropteran primer.", + "You taste iron, and you want more.", + "Every sound is a painful screech in your ears. It's all too much!." + ], + "max_intensity": 3, + "resist_traits": [ "THRESH_CHIROPTERAN" ], + "base_mods": { + "hurt_min": [ 1 ], + "hurt_max": [ 2 ], + "hurt_chance": [ -22 ], + "hurt_tick": [ 150 ], + "pain_min": [ 1 ], + "pain_max": [ 2 ], + "pain_chance": [ 100 ], + "pain_tick": [ 75 ] + }, + "scaling_mods": { "dex_mod": [ 1 ], "int_mod": [ -2 ], "speed_mod": [ 3.5 ], "hurt_chance": [ 21, 0 ], "pain_chance": [ -30 ] }, + "rating": "bad", + "blood_analysis_description": "Chiropteran Primer Contamination" + }, { "type": "effect_type", "id": "mutagen_slime", diff --git a/data/json/harvest_dissect.json b/data/json/harvest_dissect.json index 46e10aa9c4e5c..534efa143ea99 100644 --- a/data/json/harvest_dissect.json +++ b/data/json/harvest_dissect.json @@ -671,6 +671,30 @@ { "drop": "bird_sample_single", "type": "mutagen_group" } ] }, + { + "id": "dissect_chiropteran_sample_single", + "type": "harvest", + "message": "With cuts and pulls equally similar to both extraction and vandalism, you scrape together the most important parts of the creature.", + "entries": [ { "drop": "chiropteran_sample_single", "type": "mutagen_group" } ] + }, + { + "id": "dissect_chiropteran_sample_small", + "type": "harvest", + "message": "With cuts and pulls equally similar to both extraction and vandalism, you scrape together the most important parts of the creature.", + "entries": [ { "drop": "chiropteran_sample_small", "type": "mutagen_group" } ] + }, + { + "id": "dissect_chiropteran_sample_large", + "type": "harvest", + "message": "With cuts and pulls equally similar to both extraction and vandalism, you scrape together the most important parts of the creature.", + "entries": [ { "drop": "chiropteran_sample_large", "type": "mutagen_group" } ] + }, + { + "id": "dissect_chiropteran_sample_huge", + "type": "harvest", + "message": "With cuts and pulls equally similar to both extraction and vandalism, you scrape together the most important parts of the creature.", + "entries": [ { "drop": "chiropteran_sample_huge", "type": "mutagen_group" } ] + }, { "id": "dissect_flying_rodent_small", "type": "harvest", diff --git a/data/json/itemgroups/Labs/labs_mutagen.json b/data/json/itemgroups/Labs/labs_mutagen.json index ca1b88cf662ce..4203e5061df54 100644 --- a/data/json/itemgroups/Labs/labs_mutagen.json +++ b/data/json/itemgroups/Labs/labs_mutagen.json @@ -39,6 +39,7 @@ { "item": "meat", "prob": 40, "count": [ 1, 3 ] }, { "item": "rabbit_sample", "prob": 40, "count": [ 1, 3 ] }, { "item": "rat_sample", "prob": 40, "count": [ 1, 3 ] }, + { "item": "chiropteran_sample", "prob": 40, "count": [ 1, 3 ] }, { "item": "mouse_sample", "prob": 40, "count": [ 1, 3 ] }, { "item": "fish_sample", "prob": 40, "count": [ 1, 3 ] }, { "item": "meat_tainted", "prob": 30, "count": [ 1, 3 ] }, diff --git a/data/json/itemgroups/Monsters_Animals_Lairs/harvest_dissection.json b/data/json/itemgroups/Monsters_Animals_Lairs/harvest_dissection.json index ae1174f7914b4..44e1d19fdd3af 100644 --- a/data/json/itemgroups/Monsters_Animals_Lairs/harvest_dissection.json +++ b/data/json/itemgroups/Monsters_Animals_Lairs/harvest_dissection.json @@ -167,6 +167,30 @@ "subtype": "collection", "entries": [ { "item": "chimera_sample", "count-min": 10, "count-max": 20 } ] }, + { + "id": "chiropteran_sample_single", + "type": "item_group", + "subtype": "collection", + "entries": [ { "item": "chiropteran_sample" } ] + }, + { + "id": "chiropteran_sample_small", + "type": "item_group", + "subtype": "collection", + "entries": [ { "item": "chiropteran_sample", "count-min": 1, "count-max": 3 } ] + }, + { + "id": "chiropteran_sample_large", + "type": "item_group", + "subtype": "collection", + "entries": [ { "item": "chiropteran_sample", "count-min": 3, "count-max": 6 } ] + }, + { + "id": "chiropteran_sample_huge", + "type": "item_group", + "subtype": "collection", + "entries": [ { "item": "chiropteran_sample", "count-min": 10, "count-max": 20 } ] + }, { "id": "elfa_sample_single", "type": "item_group", diff --git a/data/json/itemgroups/science_and_tech.json b/data/json/itemgroups/science_and_tech.json index cdd8c61b1cf1c..8a6a747736a25 100644 --- a/data/json/itemgroups/science_and_tech.json +++ b/data/json/itemgroups/science_and_tech.json @@ -308,6 +308,7 @@ { "item": "iv_mutagen_fish", "prob": 2 }, { "item": "iv_mutagen_slime", "prob": 2 }, { "item": "iv_mutagen_rat", "prob": 2 }, + { "item": "iv_mutagen_chiropteran", "prob": 2 }, { "item": "iv_mutagen_beast", "prob": 2 }, { "item": "iv_mutagen_cattle", "prob": 2 }, { "item": "iv_mutagen_cephalopod", "prob": 2 }, @@ -363,6 +364,7 @@ { "item": "iv_mutagen_fish", "prob": 2, "count": [ 4, 5 ] }, { "item": "iv_mutagen_slime", "prob": 2, "count": [ 4, 5 ] }, { "item": "iv_mutagen_rat", "prob": 2, "count": [ 4, 5 ] }, + { "item": "iv_mutagen_chiropteran", "prob": 2, "count": [ 4, 5 ] }, { "item": "iv_mutagen_beast", "prob": 2, "count": [ 4, 5 ] }, { "item": "iv_mutagen_cattle", "prob": 2, "count": [ 4, 5 ] }, { "item": "iv_mutagen_cephalopod", "prob": 2, "count": [ 4, 5 ] }, @@ -404,6 +406,7 @@ { "item": "mutagen_fish", "count": [ 7, 11 ], "prob": 2 }, { "item": "mutagen_slime", "count": [ 7, 11 ], "prob": 2 }, { "item": "mutagen_rat", "count": [ 7, 11 ], "prob": 2 }, + { "item": "mutagen_chiropteran", "count": [ 7, 11 ], "prob": 2 }, { "item": "mutagen_beast", "count": [ 7, 11 ], "prob": 2 }, { "item": "mutagen_cattle", "count": [ 7, 11 ], "prob": 2 }, { "item": "mutagen_cephalopod", "count": [ 7, 11 ], "prob": 2 }, diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index 9adfd9ec08920..88519cb506579 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1610,7 +1610,6 @@ "encumbrance": 0 } ], - "skill": "unarmed", "melee_damage": { "stab": 14 } }, { diff --git a/data/json/items/comestibles/mutagen.json b/data/json/items/comestibles/mutagen.json index a0671ab35a027..d347db7a0ea69 100644 --- a/data/json/items/comestibles/mutagen.json +++ b/data/json/items/comestibles/mutagen.json @@ -311,6 +311,21 @@ "vitamins": [ [ "mutagen_rat", 450, 550 ] ] } }, + { + "id": "iv_mutagen_chiropteran", + "copy-from": "iv_mutagen_flavor", + "type": "COMESTIBLE", + "name": { "str_sp": "chiropteran mutagenic primer" }, + "description": "A processed mutagenic primer, black as night.", + "looks_like": "iv_mutagen", + "color": "black", + "use_action": { + "type": "consume_drug", + "activation_message": "You inject the chiropteran mutagenic primer.", + "tools_needed": { "syringe": -1 }, + "vitamins": [ [ "mutagen_chiropteran", 450, 550 ] ] + } + }, { "id": "iv_mutagen_slime", "copy-from": "iv_mutagen_flavor", @@ -675,6 +690,19 @@ "vitamins": [ [ "mutagen_rat", 225 ], [ "mutagen", 125 ] ] } }, + { + "id": "mutagen_chiropteran", + "copy-from": "mutagen_flavor", + "type": "COMESTIBLE", + "name": { "str_sp": "chiropteran mutagen" }, + "description": "A murky black liquid that smells of blood.", + "looks_like": "mutagen", + "use_action": { + "type": "consume_drug", + "activation_message": "You drink the chiropteran mutagen.", + "vitamins": [ [ "mutagen_chiropteran", 225 ], [ "mutagen", 125 ] ] + } + }, { "id": "mutagen_slime", "copy-from": "mutagen_flavor", diff --git a/data/json/items/mutagen_ingredients.json b/data/json/items/mutagen_ingredients.json index 97350073d2d2b..36b7276af8df3 100644 --- a/data/json/items/mutagen_ingredients.json +++ b/data/json/items/mutagen_ingredients.json @@ -272,5 +272,15 @@ "description": "A slurry of toughness.", "looks_like": "medical_sample", "material": [ "flesh", "chitin" ] + }, + { + "type": "GENERIC", + "id": "chiropteran_sample", + "copy-from": "mutagen_sample", + "color": "brown", + "name": { "str": "chiropteran sample" }, + "description": "A slurry of silence.", + "looks_like": "medical_sample", + "material": [ "bone", "blood" ] } ] diff --git a/data/json/monsters/mammal.json b/data/json/monsters/mammal.json index 5c2f88e6c5b2e..3af30af58190d 100644 --- a/data/json/monsters/mammal.json +++ b/data/json/monsters/mammal.json @@ -57,7 +57,7 @@ "melee_damage": [ { "damage_type": "cut", "amount": 1 } ], "dodge": 8, "harvest": "mammal_tiny", - "dissect": "dissect_beast_sample_single", + "dissect": "dissect_chiropteran_sample_single", "families": [ "prof_intro_biology", "prof_physiology", "prof_wp_flying" ], "vision_day": 20, "vision_night": 20, diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 03a6b7b8c853d..86f5a0bd1b455 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -7506,13 +7506,13 @@ "id": "HEMOVORE", "name": { "str": "Hemovore" }, "points": -4, - "description": "You can still eat other food, but you crave the taste of blood. If you choose not to drink blood or can't find any, you'd better hope you can find some iron supplements.", + "description": "You can still eat other food, but you crave the taste of blood over everything else. Without a steady supply or some kind of iron supplement, you're prone to developing anemia.", "types": [ "DIET" ], "flags": [ "HEMOVORE" ], "cancels": [ "VEGAN" ], "category": [ "CHIROPTERAN" ], "changes_to": [ "BLOODFEEDER" ], - "vitamin_rates": [ [ "iron", 1800 ] ] + "vitamin_rates": [ [ "iron", 1000 ], [ "vitC", -300 ] ] }, { "type": "mutation", @@ -7520,15 +7520,15 @@ "name": { "str": "Bloodfeeder" }, "points": -2, "mixed_effect": true, - "description": "Your craving for blood borders on obsession. Your system has gained a mild tolerance to the toxins polluting mutant flesh. When it comes to blood (and only blood), you need the stuff too much to care about whether it came from a human.", + "description": "Your craving for blood borders on obsession. You need the stuff too much to care about whether it came from a human, and your body can filter out most of the toxins in mutant blood.", "types": [ "DIET" ], "flags": [ "BLOODFEEDER" ], "cancels": [ "VEGAN" ], "prereqs": [ "HEMOVORE" ], - "vitamins_absorb_multi": [ [ "all", [ [ "mutant_toxin", 0.5 ] ] ] ], + "vitamins_absorb_multi": [ [ "blood", [ [ "mutant_toxin", 0.25 ] ] ], [ "flesh", [ [ "mutant_toxin", 0.75 ] ] ] ], "category": [ "CHIROPTERAN" ], "threshreq": [ "THRESH_CHIROPTERAN" ], - "vitamin_rates": [ [ "iron", 2400 ] ] + "vitamin_rates": [ [ "iron", 800 ], [ "vitC", -600 ] ] }, { "type": "mutation", diff --git a/data/json/recipes/chem/mutagens.json b/data/json/recipes/chem/mutagens.json index 5e643faa6b945..69906fab33c6e 100644 --- a/data/json/recipes/chem/mutagens.json +++ b/data/json/recipes/chem/mutagens.json @@ -426,6 +426,55 @@ "components": [ [ [ "mutagen_rat", 2 ] ], [ [ "rat_sample", 1 ] ] ], "flags": [ "SECRET" ] }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "mutagen_chiropteran", + "category": "CC_*", + "subcategory": "CSC_*_NESTED", + "skill_used": "chemistry", + "skills_required": [ "firstaid", 1 ], + "difficulty": 7, + "time": "45 m", + "batch_time_factors": [ 80, 20 ], + "book_learn": [ [ "recipe_creepy", 6 ] ], + "proficiencies": [ + { "proficiency": "prof_intro_chemistry" }, + { "proficiency": "prof_organic_chemistry" }, + { "proficiency": "prof_intro_chem_synth" }, + { "proficiency": "prof_chem_synth" } + ], + "using": [ [ "mutagen_production_standard", 25 ] ], + "components": [ + [ [ "mutagen", 1 ] ], + [ [ "chiropteran_sample", 1 ] ], + [ [ "animal_blood", 6 ], [ "mutant_blood", 6 ] ], + [ [ "meal_bone_tainted", 1 ], [ "chiropteran_sample", 1 ] ] + ], + "flags": [ "SECRET" ] + }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "iv_mutagen_chiropteran", + "category": "CC_*", + "subcategory": "CSC_*_NESTED", + "skill_used": "chemistry", + "skills_required": [ "firstaid", 3 ], + "difficulty": 7, + "time": "2 h", + "batch_time_factors": [ 20, 5 ], + "book_learn": [ [ "recipe_serum", 6 ], [ "recipe_creepy", 6 ] ], + "proficiencies": [ + { "proficiency": "prof_intro_chemistry" }, + { "proficiency": "prof_organic_chemistry" }, + { "proficiency": "prof_intro_chem_synth" }, + { "proficiency": "prof_chem_synth" } + ], + "using": [ [ "serum_production_standard", 37 ] ], + "components": [ [ [ "mutagen_chiropteran", 2 ] ], [ [ "chiropteran_sample", 1 ] ] ], + "flags": [ "SECRET" ] + }, { "type": "recipe", "activity_level": "LIGHT_EXERCISE", diff --git a/data/json/recipes/nested.json b/data/json/recipes/nested.json index 734ff5cccce2b..34fe1b1568785 100644 --- a/data/json/recipes/nested.json +++ b/data/json/recipes/nested.json @@ -6905,6 +6905,7 @@ "iv_purifier", "iv_mutagen", "iv_mutagen_rat", + "iv_mutagen_chiropteran", "iv_mutagen_fish", "iv_mutagen_bird", "iv_mutagen_mouse", @@ -6945,6 +6946,7 @@ "purifier", "mutagen", "mutagen_rat", + "mutagen_chiropteran", "mutagen_fish", "mutagen_bird", "mutagen_mouse", diff --git a/data/json/vitamin.json b/data/json/vitamin.json index 0b30226d1af4b..0639271f9cc7b 100644 --- a/data/json/vitamin.json +++ b/data/json/vitamin.json @@ -123,6 +123,7 @@ [ "mutagen_rat", 1 ], [ "mutagen_spider", 1 ], [ "mutagen_troglobite", 1 ], + [ "mutagen_chiropteran", 1 ], [ "mutagen_ursine", 1 ], [ "instability", 1 ], [ "mutant_toxin", 2 ] @@ -421,6 +422,17 @@ "rate": "1 h", "disease_excess": [ [ 100, 500 ], [ 501, 2199 ], [ 2200, 2500 ] ] }, + { + "id": "mutagen_chiropteran", + "type": "vitamin", + "vit_type": "counter", + "name": { "str": "Chiropteran Primer" }, + "excess": "mutagen_chiropteran", + "min": 0, + "max": 2500, + "rate": "1 h", + "disease_excess": [ [ 100, 500 ], [ 501, 2199 ], [ 2200, 2500 ] ] + }, { "id": "mutagen_slime", "type": "vitamin", diff --git a/src/bonuses.cpp b/src/bonuses.cpp index 68cce283b6500..7479d3b67391d 100644 --- a/src/bonuses.cpp +++ b/src/bonuses.cpp @@ -379,6 +379,9 @@ float effect_scaling::get( const Character &u ) const case SKILL_DRIVE: bonus = scale * u.get_skill_level( skill_drive ); break; + case SKILL_FIRSTAID: + bonus = scale * u.get_skill_level( skill_firstaid ); + break; case SKILL_SPELLCRAFT: bonus = scale * u.get_skill_level( skill_spellcraft ); break; diff --git a/src/consumption.cpp b/src/consumption.cpp index 864b17a97f3d3..66e3e9cb86539 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -952,12 +952,12 @@ ret_val Character::will_eat( const item &food, bool interactive ) const bool food_is_human_flesh = food.has_flag( flag_CANNIBALISM ) || ( food.has_flag( flag_STRICT_HUMANITARIANISM ) && !has_flag( json_flag_STRICT_HUMANITARIAN ) ); - if( food_is_human_flesh && !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) ) { + if( ( food_is_human_flesh && !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM ); } if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && - !has_flag( json_flag_PARAIMMUNE ) ) { + !has_flag( json_flag_PARAIMMUNE ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { add_consequence( string_format( _( "Consuming this %s probably isn't very healthy." ), food.tname() ), PARASITES ); @@ -1174,7 +1174,7 @@ static bool eat( item &food, Character &you, bool force ) // Chance to become parasitised if( !will_vomit && !you.has_flag( json_flag_PARAIMMUNE ) ) { if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && - one_in( food.get_comestible()->parasites ) ) { + one_in( food.get_comestible()->parasites ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !you.has_flag( json_flag_HEMOVORE ) && !you.has_flag( json_flag_BLOODFEEDER ) ) ) ) { switch( rng( 0, 3 ) ) { case 0: if( !you.has_trait( trait_EATHEALTH ) ) { From 451f5f581cf1cffda03ce8d3ba2dbc0138c75817 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:45:44 -0800 Subject: [PATCH 052/202] Update settings.json --- .vscode/settings.json | 94 ------------------------------------------- 1 file changed, 94 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 934864ecd9c63..20bd09ea82c04 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -64,98 +64,4 @@ "github-actions.remote-name": "upstream", // NOTE: This disable the plugins formatting so astyle is used. "C_Cpp.formatting": "disabled", - "files.associations": { - "vector": "cpp", - "algorithm": "cpp", - "array": "cpp", - "atomic": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "cfenv": "cpp", - "charconv": "cpp", - "chrono": "cpp", - "cinttypes": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "codecvt": "cpp", - "compare": "cpp", - "complex": "cpp", - "concepts": "cpp", - "condition_variable": "cpp", - "csetjmp": "cpp", - "csignal": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cuchar": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "exception": "cpp", - "resumable": "cpp", - "filesystem": "cpp", - "forward_list": "cpp", - "fstream": "cpp", - "functional": "cpp", - "future": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "ios": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "iterator": "cpp", - "limits": "cpp", - "list": "cpp", - "locale": "cpp", - "map": "cpp", - "memory": "cpp", - "mutex": "cpp", - "new": "cpp", - "numeric": "cpp", - "optional": "cpp", - "ostream": "cpp", - "queue": "cpp", - "random": "cpp", - "ratio": "cpp", - "regex": "cpp", - "scoped_allocator": "cpp", - "set": "cpp", - "span": "cpp", - "sstream": "cpp", - "stack": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "string": "cpp", - "system_error": "cpp", - "thread": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "typeindex": "cpp", - "typeinfo": "cpp", - "unordered_map": "cpp", - "unordered_set": "cpp", - "utility": "cpp", - "variant": "cpp", - "xfacet": "cpp", - "xhash": "cpp", - "xiosbase": "cpp", - "xlocale": "cpp", - "xlocbuf": "cpp", - "xlocinfo": "cpp", - "xlocmes": "cpp", - "xlocmon": "cpp", - "xlocnum": "cpp", - "xloctime": "cpp", - "xmemory": "cpp", - "xstddef": "cpp", - "xstring": "cpp", - "xtr1common": "cpp", - "xtree": "cpp", - "xutility": "cpp" - }, } From 130f5b9118791d3db3ac3202f82ae5bad2eeeaba Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:15:00 -0800 Subject: [PATCH 053/202] Update data/json/mutations/mutations.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- data/json/mutations/mutations.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 8ee96527002a1..a6ec938ffddca 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -514,7 +514,25 @@ "category": [ "CHIROPTERAN" ], "ugliness": 3, "description": "Your nose is upturned, ridged and flared out into a leaf shape that humans will likely find unsightly. On the plus side, thermoreceptors in your nostrils allow you to smell heat as long as you can breathe.", - "triggers": [ [ { "condition": { "not": { "or": [ { "u_has_effect": "smoke", "bodypart": "mouth" }, { "u_has_effect": "asthma" }, { "u_has_effect": "tpollen" }, { "u_has_effect": "asthma" }, { "u_has_effect": "hay_fever" }, "u_is_underwater" ] } }, "msg_on": { "text": "", "rating": "good" } } ] ] + "triggers": [ + [ + { + "condition": { + "not": { + "or": [ + { "u_has_effect": "smoke", "bodypart": "mouth" }, + { "u_has_effect": "asthma" }, + { "u_has_effect": "tpollen" }, + { "u_has_effect": "asthma" }, + { "u_has_effect": "hay_fever" }, + "u_is_underwater" + ] + } + }, + "msg_on": { "text": "", "rating": "good" } + } + ] + ] }, { "type": "mutation", From 8680b584b1bc79b725495cf4ab12687e53cf1fcb Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:15:06 -0800 Subject: [PATCH 054/202] Update data/json/mutations/mutations.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- data/json/mutations/mutations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index a6ec938ffddca..39d0b97840eb4 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -4359,7 +4359,7 @@ }, { "condition": { "and": [ { "u_has_move_mode": "run" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, - "values": [ { "value": "MOVE_COST", "multiply": -0.20 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.6 } ], + "values": [ { "value": "MOVE_COST", "multiply": -0.2 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.6 } ], "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] } ] From f7293149f91955d8d1812b20954dfff2bca5b83f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:15:14 -0800 Subject: [PATCH 055/202] Update data/json/mutations/mutations.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- data/json/mutations/mutations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 39d0b97840eb4..6b0c89f7ae775 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -4362,7 +4362,7 @@ "values": [ { "value": "MOVE_COST", "multiply": -0.2 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.6 } ], "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] } - ] + ] }, { "type": "mutation", From bce0b890d1de0cb61e56a51685ae9f61852259d8 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:15:23 -0800 Subject: [PATCH 056/202] Update data/json/mutations/mutations.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- data/json/mutations/mutations.json | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 6b0c89f7ae775..07441fe8f0d0a 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -4412,13 +4412,14 @@ } ] ], - "enchantments": [ - "ench_quadruped_movement_full", - { - "condition": { "and": [ { "u_has_move_mode": "crouch" }, { "not": "u_can_drop_weapon" }, { "u_has_flag": "QUADRUPED_CROUCH" } ] }, - "values": [ { "value": "MOVE_COST", "multiply": -0.35 } ], - "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] - } ], + "enchantments": [ + "ench_quadruped_movement_full", + { + "condition": { "and": [ { "u_has_move_mode": "crouch" }, { "not": "u_can_drop_weapon" }, { "u_has_flag": "QUADRUPED_CROUCH" } ] }, + "values": [ { "value": "MOVE_COST", "multiply": -0.35 } ], + "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] + } + ], "weight_capacity_modifier": 0.8 }, { From 54f1139ae542885b409b768f58151b5327809c24 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:15:29 -0800 Subject: [PATCH 057/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 1a57da29936a5..3211818b25e55 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1174,7 +1174,8 @@ static bool eat( item &food, Character &you, bool force ) // Chance to become parasitised if( !will_vomit && !you.has_flag( json_flag_PARAIMMUNE ) ) { if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && - one_in( food.get_comestible()->parasites ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !you.has_flag( json_flag_HEMOVORE ) && !you.has_flag( json_flag_BLOODFEEDER ) ) ) ) { + one_in( food.get_comestible()->parasites ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || + ( !you.has_flag( json_flag_HEMOVORE ) && !you.has_flag( json_flag_BLOODFEEDER ) ) ) ) { switch( rng( 0, 3 ) ) { case 0: if( !you.has_trait( trait_EATHEALTH ) ) { From e6ae207363fb0142d9b34137930ed986e713ae26 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:15:35 -0800 Subject: [PATCH 058/202] Update src/lightmap.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/lightmap.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 337c16bfb7216..2dc207b9e2cde 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -211,7 +211,8 @@ bool map::build_vision_transparency_cache( const int zlev ) bool dirty = false; bool is_crouching = player_character.is_crouching(); - bool low_profile = player_character.has_effect( effect_quadruped_full ) && player_character.is_running(); + bool low_profile = player_character.has_effect( effect_quadruped_full ) && + player_character.is_running(); bool is_prone = player_character.is_prone(); for( const tripoint &loc : points_in_radius( p, 1 ) ) { From 00d642aad5ed31d6e4c69fa853fd2bb7e73b518b Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:15:54 -0800 Subject: [PATCH 059/202] Update src/ranged.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/ranged.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ranged.cpp b/src/ranged.cpp index a6738c63b0fd7..b2a38f3ce377c 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -548,7 +548,8 @@ double Creature::ranged_target_size() const { double stance_factor = 1.0; if( const Character *character = this->as_character() ) { - if( character->is_crouching() || ( character->has_effect( effect_quadruped_full ) && character->is_running() ) ) { + if( character->is_crouching() || ( character->has_effect( effect_quadruped_full ) && + character->is_running() ) ) { stance_factor = 0.6; } else if( character->is_prone() ) { stance_factor = 0.25; From 0653e46606f192a43a4df60720ce9c86f9887eae Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:16:08 -0800 Subject: [PATCH 060/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 3211818b25e55..bea339af06bd2 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -957,7 +957,8 @@ ret_val Character::will_eat( const item &food, bool interactive ) } if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && - !has_flag( json_flag_PARAIMMUNE ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { + !has_flag( json_flag_PARAIMMUNE ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || + ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { add_consequence( string_format( _( "Consuming this %s probably isn't very healthy." ), food.tname() ), PARASITES ); From aacff512290f79c29849f7e93eb418f424450b6d Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:16:15 -0800 Subject: [PATCH 061/202] Update data/json/mutations/mutations.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- data/json/mutations/mutations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 07441fe8f0d0a..92cb2b31607d0 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -4356,7 +4356,7 @@ "condition": { "and": [ { "u_has_move_mode": "crouch" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.58 }, { "value": "FOOTSTEP_NOISE", "multiply": 0 } ], "ench_effects": [ { "effect": "natural_stance", "intensity": 1 } ] - }, + }, { "condition": { "and": [ { "u_has_move_mode": "run" }, { "not": "u_can_drop_weapon" }, { "u_has_trait": "WINGS_BAT" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.2 }, { "value": "FOOTSTEP_NOISE", "multiply": -0.6 } ], From 2e8fd0568c9272cd42cae89e692482c725aec52c Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:38:51 -0800 Subject: [PATCH 062/202] itemgroups --- data/json/itemgroups/bionics.json | 2 ++ data/json/itemgroups/supplies.json | 1 + 2 files changed, 3 insertions(+) diff --git a/data/json/itemgroups/bionics.json b/data/json/itemgroups/bionics.json index 7764e3a59bc77..5509cc5bdaf26 100644 --- a/data/json/itemgroups/bionics.json +++ b/data/json/itemgroups/bionics.json @@ -94,6 +94,7 @@ [ "bio_ar", 10 ], [ "bio_fitnessband", 10 ], [ "bio_radio", 10 ], + [ "bio_sonar", 10 ], [ "bio_synlungs", 10 ], [ "bio_taser", 10 ] ] @@ -166,6 +167,7 @@ [ "bio_radscrubber", 10 ], [ "bio_ads", 10 ], [ "bio_ods", 10 ], + [ "bio_sonar", 10 ], [ "bio_uncanny_dodge", 10 ], [ "bio_laser", 10 ], [ "bio_emp", 10 ], diff --git a/data/json/itemgroups/supplies.json b/data/json/itemgroups/supplies.json index a2b0b45882fdc..474d7127ee35d 100644 --- a/data/json/itemgroups/supplies.json +++ b/data/json/itemgroups/supplies.json @@ -492,6 +492,7 @@ { "item": "mouse_sample", "prob": 40, "count": [ 1, 3 ] }, { "item": "rat_sample", "prob": 40, "count": [ 1, 3 ] }, { "item": "fish_sample", "prob": 40, "count": [ 1, 3 ] }, + { "item": "chiropteran_sample", "prob": 40, "count": [ 1, 3 ] }, { "item": "meat_tainted", "prob": 30, "count": [ 1, 3 ] }, { "item": "brain", "prob": 10, "count": [ 4, 12 ] }, { "item": "rabbit_sample", "prob": 10, "count": [ 1, 3 ] }, From 4f6ed0b2c19acf5bca0016acba43479bdcd6489f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:45:58 -0800 Subject: [PATCH 063/202] Update techniques.json --- data/json/techniques.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/json/techniques.json b/data/json/techniques.json index 31cfecbeba96b..dbe1779c8beca 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3576,7 +3576,7 @@ { "type": "technique", "id": "FANGS_BITE_CRIT", - "name": "Fang Bite (crit)", + "name": "Critical Fang Bite", "melee_allowed": true, "messages": [ "You deliver a wicked bite to %s", " delivers a wicked bite to %s!" ], "unarmed_allowed": true, @@ -3691,7 +3691,7 @@ { "type": "technique", "id": "VAMPIRE_BITE_CRIT", - "name": "Vampire Bite (crit)", + "name": "Critical Vampire Bite", "melee_allowed": true, "messages": [ "You sink your fangs deep into %s", " sinks their fangs deep into %s!" ], "unarmed_allowed": true, From 52489b55e1d12ee6ca7484a325da983b10ef599f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:50:53 -0800 Subject: [PATCH 064/202] Update data/json/techniques.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- data/json/techniques.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/json/techniques.json b/data/json/techniques.json index dbe1779c8beca..898af37c1fb82 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3675,7 +3675,9 @@ ], "condition": { "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + { + "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] + } ] }, "flat_bonuses": [ From f2a0bd239f392656ef8601c2c3f6d41977bb69a1 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:51:05 -0800 Subject: [PATCH 065/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index bea339af06bd2..eaedcaf6f36c0 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -952,7 +952,9 @@ ret_val Character::will_eat( const item &food, bool interactive ) const bool food_is_human_flesh = food.has_flag( flag_CANNIBALISM ) || ( food.has_flag( flag_STRICT_HUMANITARIANISM ) && !has_flag( json_flag_STRICT_HUMANITARIAN ) ); - if( ( food_is_human_flesh && !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { + if( ( food_is_human_flesh && !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) ) && + ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_HEMOVORE ) && + !has_flag( json_flag_BLOODFEEDER ) ) ) ) { add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM ); } From af0e6e10bdec7e79e0226bebac7618b1bcbd9642 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:51:18 -0800 Subject: [PATCH 066/202] Update data/json/techniques.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- data/json/techniques.json | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/data/json/techniques.json b/data/json/techniques.json index 898af37c1fb82..5330055e71997 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3622,13 +3622,8 @@ "condition": { "and": [ { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] + { "and": [ { "not": { "npc_has_flag": "GRAB_FILTER" } }, { "not": { "u_has_effect": "GRAB" } } ] } + ] }, "tech_effects": [ { From d9223ab81b1cacc2ce6ab8ef63922409168693a3 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:51:28 -0800 Subject: [PATCH 067/202] Update data/json/techniques.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- data/json/techniques.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/json/techniques.json b/data/json/techniques.json index 5330055e71997..129a129e5c07a 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3550,7 +3550,9 @@ "attack_vectors": [ "MOUTH" ], "condition": { "and": [ - { "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] } + { + "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] + } ] }, "tech_effects": [ From 07d1da3dedb06c130b4dd780bdbb4caf7e6c5dae Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:51:37 -0800 Subject: [PATCH 068/202] Update data/json/techniques.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- data/json/techniques.json | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/data/json/techniques.json b/data/json/techniques.json index 129a129e5c07a..c63579ccb8215 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3507,13 +3507,8 @@ "condition": { "and": [ { "not": { "u_has_effect": "natural_stance" } }, - { - "and": [ - { "not": { "npc_has_flag": "GRAB_FILTER" } }, - { "not": { "u_has_effect": "GRAB" } } - ] - } - ] + { "and": [ { "not": { "npc_has_flag": "GRAB_FILTER" } }, { "not": { "u_has_effect": "GRAB" } } ] } + ] }, "tech_effects": [ { From 345b337f15966ca7ff59d352641700ba4b4029a4 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:51:50 -0800 Subject: [PATCH 069/202] Update data/json/mutations/mutations.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- data/json/mutations/mutations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 92cb2b31607d0..e67823d68a612 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -6523,7 +6523,7 @@ "description": "Most of your fingers have grown to tremendous length, becoming a broad pair of leathery wings. This allows you to arrest a fall or glide from a ledge if you aren't too weighed down, but naturally impedes your fine motor skills. They even keep you warm when you sleep.", "types": [ "ARMS", "HANDS" ], "flags": [ "WING_GLIDE", "WINGS_2", "ARM_WINGS" ], - "encumbrance_always": [ [ "arm_r", 20 ], [ "arm_l", 20 ], [ "hand_r", 40 ], [ "hand_l" , 40 ] ], + "encumbrance_always": [ [ "arm_r", 20 ], [ "arm_l", 20 ], [ "hand_r", 40 ], [ "hand_l", 40 ] ], "prereqs": [ "WEBBED" ], "category": [ "CHIROPTERAN" ], "restricts_gear": [ "arm_l", "arm_r", "hand_l", "hand_r" ], From 788177020b8dc50a0d4ea64bcbea0061d8bbb810 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 2 Feb 2024 16:47:18 -0800 Subject: [PATCH 070/202] tests --- data/json/mutations/mutation_category.json | 2 +- data/json/mutations/mutations.json | 8 ++++++-- src/activity_handlers.cpp | 3 +-- src/bonuses.cpp | 18 +++++++++--------- src/character.cpp | 12 +++--------- src/consumption.cpp | 2 +- src/lightmap.cpp | 2 +- src/morale_types.cpp | 9 +++++---- src/ranged.cpp | 2 +- src/sounds.cpp | 16 ++++++++-------- src/trap.cpp | 2 +- 11 files changed, 37 insertions(+), 39 deletions(-) diff --git a/data/json/mutations/mutation_category.json b/data/json/mutations/mutation_category.json index bf0215dc3682c..4b6c22f4b91a7 100644 --- a/data/json/mutations/mutation_category.json +++ b/data/json/mutations/mutation_category.json @@ -293,7 +293,7 @@ "threshold_mut": "THRESH_CHIROPTERAN", "mutagen_message": "Your ears ring, and you thirst for blood.", "memorial_message": "Became the night.", - "vitamin": "mutagen_crustacean", + "vitamin": "mutagen_chiropteran", "base_removal_chance": 33, "base_removal_cost_mul": 2.5 }, diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index e67823d68a612..75a81077eb56e 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -138,6 +138,7 @@ "HIBERNATE", "PROJUNK", "QUICK", + "ANTIWHEAT", "SAPROVORE", "PICKYEATER" ], @@ -152,7 +153,7 @@ "dummy": true, "category": [ "HUMAN" ], "types": [ "ATTRACTIVENESS", "MUZZLE", "TEETH", "TONGUE" ], - "cancels": [ "SLIT_NOSTRILS" ], + "cancels": [ "SLIT_NOSTRILS", "LEAFNOSE", "BLOODLETTER" ], "description": "You have a human mouth with human teeth." }, { @@ -499,8 +500,10 @@ "id": "ECHOLOCATION", "name": { "str": "Echolocation" }, "points": 3, + "removable": false, "description": "You've learned how to hunt without sight. Activate this mutation to make a quiet chirp that can reveal obstacles and enemies by its echo.", - "prereqs": [ "BATEARS", "GOODCARDIO" ], + "prereqs": [ "BATEARS" ], + "threshreq": [ "THRESH_CHIROPTERAN" ], "category": [ "CHIROPTERAN" ], "active": true }, @@ -7182,6 +7185,7 @@ "points": 0, "visibility": 3, "ugliness": 2, + "mixed_effect": true, "types": [ "HANDS" ], "description": "Your hands are heavily webbed, reducing your Dexterity by 1 and causing problems with gloves. However, you can swim much faster. Slightly decreases wet penalties.", "leads_to": [ "WEBBED_FEET" ], diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 1f49d6b92a563..4ad8c75316ed2 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -181,7 +181,6 @@ static const damage_type_id damage_cut( "cut" ); static const damage_type_id damage_stab( "stab" ); static const efftype_id effect_asocial_dissatisfied( "asocial_dissatisfied" ); -static const efftype_id effect_asocial_satisfied( "asocial_satisfied" ); static const efftype_id effect_bleed( "bleed" ); static const efftype_id effect_blind( "blind" ); static const efftype_id effect_controlled( "controlled" ); @@ -210,9 +209,9 @@ static const json_character_flag json_flag_CANNIBAL( "CANNIBAL" ); static const json_character_flag json_flag_PAIN_IMMUNE( "PAIN_IMMUNE" ); static const json_character_flag json_flag_PSYCHOPATH( "PSYCHOPATH" ); static const json_character_flag json_flag_SAPIOVORE( "SAPIOVORE" ); +static const json_character_flag json_flag_SILENT_SPELL( "SILENT_SPELL" ); static const json_character_flag json_flag_SOCIAL1( "SOCIAL1" ); static const json_character_flag json_flag_SOCIAL2( "SOCIAL2" ); -static const json_character_flag json_flag_SILENT_SPELL( "SILENT_SPELL" ); static const mongroup_id GROUP_FISH( "GROUP_FISH" ); diff --git a/src/bonuses.cpp b/src/bonuses.cpp index 7479d3b67391d..60c5ec70472b1 100644 --- a/src/bonuses.cpp +++ b/src/bonuses.cpp @@ -11,24 +11,24 @@ #include "string_formatter.h" #include "translations.h" +static const skill_id skill_archery( "archery" ); static const skill_id skill_bashing( "bashing" ); static const skill_id skill_cutting( "cutting" ); +static const skill_id skill_drive( "drive" ); static const skill_id skill_dodge( "dodge" ); -static const skill_id skill_melee( "melee" ); -static const skill_id skill_stabbing( "stabbing" ); -static const skill_id skill_swimming( "swimming" ); -static const skill_id skill_unarmed( "unarmed" ); +static const skill_id skill_firstaid( "firstaid" ); static const skill_id skill_gun( "gun" ); +static const skill_id skill_launcher( "launcher" ); +static const skill_id skill_melee( "melee" ); static const skill_id skill_pistol( "pistol" ); static const skill_id skill_rifle( "rifle" ); static const skill_id skill_shotgun( "shotgun" ); static const skill_id skill_smg( "smg" ); -static const skill_id skill_throw( "throw" ); -static const skill_id skill_launcher( "launcher" ); -static const skill_id skill_archery( "archery" ); -static const skill_id skill_drive( "drive" ); -static const skill_id skill_firstaid( "firstaid" ); static const skill_id skill_spellcraft( "spellcraft" ); +static const skill_id skill_stabbing( "stabbing" ); +static const skill_id skill_swimming( "swimming" ); +static const skill_id skill_throw( "throw" ); +static const skill_id skill_unarmed( "unarmed" ); static bool needs_damage_type( affected_stat as ) { diff --git a/src/character.cpp b/src/character.cpp index 78f91b9d9af5d..ad5a258ad6740 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -440,7 +440,6 @@ static const trait_id trait_INFRESIST( "INFRESIST" ); static const trait_id trait_INSOMNIA( "INSOMNIA" ); static const trait_id trait_INT_SLIME( "INT_SLIME" ); static const trait_id trait_LEG_TENT_BRACE( "LEG_TENT_BRACE" ); -static const trait_id trait_LEGS_BAT( "LEGS_BAT" ); static const trait_id trait_LIGHTSTEP( "LIGHTSTEP" ); static const trait_id trait_LOVES_BOOKS( "LOVES_BOOKS" ); static const trait_id trait_MASOCHIST( "MASOCHIST" ); @@ -481,9 +480,6 @@ static const trait_id trait_SPIRITUAL( "SPIRITUAL" ); static const trait_id trait_STRONGBACK( "STRONGBACK" ); static const trait_id trait_SUNLIGHT_DEPENDENT( "SUNLIGHT_DEPENDENT" ); static const trait_id trait_THORNS( "THORNS" ); -static const trait_id trait_THRESH_BEAST( "THRESH_BEAST" ); -static const trait_id trait_THRESH_FELINE( "THRESH_FELINE" ); -static const trait_id trait_THRESH_LUPINE( "THRESH_LUPINE" ); static const trait_id trait_THRESH_SPIDER( "THRESH_SPIDER" ); static const trait_id trait_TRANSPIRATION( "TRANSPIRATION" ); static const trait_id trait_UNDINE_SLEEP_WATER( "UNDINE_SLEEP_WATER" ); @@ -493,7 +489,6 @@ static const trait_id trait_WATERSLEEP( "WATERSLEEP" ); static const trait_id trait_WEB_SPINNER( "WEB_SPINNER" ); static const trait_id trait_WEB_WALKER( "WEB_WALKER" ); static const trait_id trait_WEB_WEAVER( "WEB_WEAVER" ); -static const trait_id trait_WINGS_BAT( "WINGS_BAT" ); static const vitamin_id vitamin_calcium( "calcium" ); static const vitamin_id vitamin_iron( "iron" ); @@ -10557,7 +10552,6 @@ void Character::echo_pulse() sounds::sound( this->pos(), 5, sounds::sound_t::movement, _( "boop." ), true, "none", "none" ); } else if( !has_effect( effect_subaquatic_sonar ) && is_underwater() ) { - pulse_range = 0; add_msg_if_player( m_warning, _( "You can't echolocate underwater!" ) ); return; } else { @@ -10620,10 +10614,10 @@ void Character::echo_pulse() echo_string = _( "Target [Medium]." ); break; case 4: - echo_string = _( "Warning! Target [Large]." ); + echo_string = _( "Warning! Target [Large]." ); break; case 5: - echo_string = _( "Warning! Target [Huge]." ); + echo_string = _( "Warning! Target [Huge]." ); break; default: debugmsg( "ERROR: Invalid echo string." ); @@ -10643,7 +10637,7 @@ void Character::echo_pulse() echo_string = _( "pong." ); break; case 5: - echo_string = _( "booop." ); + echo_string = _( "bloop." ); break; default: debugmsg( "ERROR: Invalid echo string." ); diff --git a/src/consumption.cpp b/src/consumption.cpp index eaedcaf6f36c0..21a58485de73e 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -99,8 +99,8 @@ static const itype_id itype_apparatus( "apparatus" ); static const itype_id itype_dab_pen_on( "dab_pen_on" ); static const itype_id itype_syringe( "syringe" ); -static const json_character_flag json_flag_CANNIBAL( "CANNIBAL" ); static const json_character_flag json_flag_BLOODFEEDER( "BLOODFEEDER" ); +static const json_character_flag json_flag_CANNIBAL( "CANNIBAL" ); static const json_character_flag json_flag_HEMOVORE( "HEMOVORE" ); static const json_character_flag json_flag_IMMUNE_SPOIL( "IMMUNE_SPOIL" ); static const json_character_flag json_flag_NUMB( "NUMB" ); diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 2dc207b9e2cde..6ea7329ccc690 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -48,9 +48,9 @@ #include "weather.h" #include "weather_type.h" -static const efftype_id effect_quadruped_full( "quadruped_full" ); static const efftype_id effect_haslight( "haslight" ); static const efftype_id effect_onfire( "onfire" ); +static const efftype_id effect_quadruped_full( "quadruped_full" ); static constexpr int LIGHTMAP_CACHE_X = MAPSIZE_X; static constexpr int LIGHTMAP_CACHE_Y = MAPSIZE_Y; diff --git a/src/morale_types.cpp b/src/morale_types.cpp index dcc206182e899..13116fbbe803d 100644 --- a/src/morale_types.cpp +++ b/src/morale_types.cpp @@ -17,6 +17,7 @@ const morale_type MORALE_ANTIJUNK( "morale_antijunk" ); const morale_type MORALE_ANTIMEAT( "morale_antimeat" ); const morale_type MORALE_ANTIVEGGY( "morale_antiveggy" ); const morale_type MORALE_ANTIWHEAT( "morale_antiwheat" ); +const morale_type MORALE_ASOCIAL( "morale_asocial" ); const morale_type MORALE_ATE_WITHOUT_TABLE( "morale_ate_without_table" ); const morale_type MORALE_ATE_WITH_TABLE( "morale_ate_with_table" ); const morale_type MORALE_BOOK( "morale_book" ); @@ -86,18 +87,19 @@ const morale_type MORALE_PYROMANIA_NOFIRE( "morale_pyromania_nofire" ); const morale_type MORALE_PYROMANIA_STARTFIRE( "morale_pyromania_startfire" ); const morale_type MORALE_SCREAM( "morale_scream" ); const morale_type MORALE_SHAVE( "morale_shave" ); +const morale_type MORALE_SOCIAL( "morale_social" ); const morale_type MORALE_SUPPORT( "morale_support" ); const morale_type MORALE_SWEETTOOTH( "morale_sweettooth" ); const morale_type MORALE_TREE_COMMUNION( "morale_tree_communion" ); const morale_type MORALE_VEGETARIAN( "morale_vegetarian" ); const morale_type MORALE_VOMITED( "morale_vomited" ); const morale_type MORALE_WET( "morale_wet" ); -const morale_type MORALE_SOCIAL( "morale_social" ); -const morale_type MORALE_ASOCIAL( "morale_asocial" ); + static const morale_type morale_accomplishment( "morale_accomplishment" ); static const morale_type morale_antifruit( "morale_antifruit" ); static const morale_type morale_antijunk( "morale_antijunk" ); static const morale_type morale_antiwheat( "morale_antiwheat" ); +static const morale_type morale_asocial( "morale_asocial" ); static const morale_type morale_ate_with_table( "morale_ate_with_table" ); static const morale_type morale_ate_without_table( "morale_ate_without_table" ); static const morale_type morale_book( "morale_book" ); @@ -163,13 +165,12 @@ static const morale_type morale_pyromania_nofire( "morale_pyromania_nofire" ); static const morale_type morale_pyromania_startfire( "morale_pyromania_startfire" ); static const morale_type morale_scream( "morale_scream" ); static const morale_type morale_shave( "morale_shave" ); +static const morale_type morale_social( "morale_social" ); static const morale_type morale_support( "morale_support" ); static const morale_type morale_sweettooth( "morale_sweettooth" ); static const morale_type morale_vegetarian( "morale_vegetarian" ); static const morale_type morale_vomited( "morale_vomited" ); static const morale_type morale_wet( "morale_wet" ); -static const morale_type morale_social( "morale_social" ); -static const morale_type morale_asocial( "morale_asocial" ); const morale_type &morale_type_data::convert_legacy( int lmt ) { diff --git a/src/ranged.cpp b/src/ranged.cpp index b2a38f3ce377c..9c6b28779d599 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -107,10 +107,10 @@ static const character_modifier_id character_modifier_thrown_dex_mod( "thrown_de static const damage_type_id damage_bash( "bash" ); static const damage_type_id damage_cut( "cut" ); -static const efftype_id effect_quadruped_full( "quadruped_full" ); static const efftype_id effect_downed( "downed" ); static const efftype_id effect_hit_by_player( "hit_by_player" ); static const efftype_id effect_on_roof( "on_roof" ); +static const efftype_id effect_quadruped_full( "quadruped_full" ); static const fault_id fault_gun_blackpowder( "fault_gun_blackpowder" ); static const fault_id fault_gun_chamber_spent( "fault_gun_chamber_spent" ); diff --git a/src/sounds.cpp b/src/sounds.cpp index d68a386bb1ab6..d33f51f613a13 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -570,8 +570,8 @@ void sounds::process_sound_markers( Character *you ) // The felt volume of a sound is not affected by negative multipliers, such as already // deafened players or players with sub-par hearing to begin with. int felt_volume = static_cast( raw_volume * std::min( 1.0f, - volume_multiplier ) ) - distance_to_sound; - if( you->has_flag( json_flag_HEARING_PROTECTION ) ){ + volume_multiplier ) ) - distance_to_sound; + if( you->has_flag( json_flag_HEARING_PROTECTION ) ) { felt_volume /= 2; } // Deafening is based on the felt volume, as a player may be too deaf to @@ -722,23 +722,23 @@ void sounds::process_sound_markers( Character *you ) int err_offset; if( ( heard_volume + distance_to_sound ) / distance_to_sound < 2 ) { - err_offset = rand() % 4; + err_offset = rng( 0, 3 ); } else if( ( heard_volume + distance_to_sound ) / distance_to_sound < 3 ) { - err_offset = rand() % 3; + err_offset = rng( 0, 2 ); } else { - err_offset = rand() % 2; + err_offset = rng( 0, 1 ); } // Echolocation has to be fairly precise or it's worse than useless. // However, it is never perfect. if( sound.category == sound_t::sensory ) { if( ( heard_volume + distance_to_sound ) / distance_to_sound < 2 ) { - err_offset = rand() % 3; + err_offset = rng( 0, 3 ); } else if( ( heard_volume + distance_to_sound ) / distance_to_sound < 3 ) { - err_offset = rand() % 2; + err_offset = rng( 0, 2 ); } else { if( one_in( 3 ) ) { - err_offset = rand() % 2; + err_offset = rng( 0, 1 ); } else { err_offset = 0; } diff --git a/src/trap.cpp b/src/trap.cpp index 3f1b62043f93f..e3901125202c3 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -21,8 +21,8 @@ #include "rng.h" #include "string_formatter.h" -static const flag_id json_flag_SONAR_DETECTABLE( "SONAR_DETECTABLE" ); static const flag_id json_flag_ECHOLOCATION_DETECTABLE( "ECHOLOCATION_DETECTABLE" ); +static const flag_id json_flag_SONAR_DETECTABLE( "SONAR_DETECTABLE" ); static const proficiency_id proficiency_prof_spotting( "prof_spotting" ); static const proficiency_id proficiency_prof_traps( "prof_traps" ); From e64a1e768c30ec5aacf41a7b4a7bed28c9225ed9 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 2 Feb 2024 16:48:10 -0800 Subject: [PATCH 071/202] im dum --- data/json/mutations/mutations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 75a81077eb56e..195722b45f257 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -500,7 +500,7 @@ "id": "ECHOLOCATION", "name": { "str": "Echolocation" }, "points": 3, - "removable": false, + "purifiable": false, "description": "You've learned how to hunt without sight. Activate this mutation to make a quiet chirp that can reveal obstacles and enemies by its echo.", "prereqs": [ "BATEARS" ], "threshreq": [ "THRESH_CHIROPTERAN" ], From 170c6d7b880eb4461996049cd29a00834e6e973a Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:10:11 -0800 Subject: [PATCH 072/202] Update exodii_merchant_itemlist.json --- data/json/npcs/exodii/exodii_merchant_itemlist.json | 1 + 1 file changed, 1 insertion(+) diff --git a/data/json/npcs/exodii/exodii_merchant_itemlist.json b/data/json/npcs/exodii/exodii_merchant_itemlist.json index fdeea36ff8cd9..3afe112f94335 100644 --- a/data/json/npcs/exodii/exodii_merchant_itemlist.json +++ b/data/json/npcs/exodii/exodii_merchant_itemlist.json @@ -238,6 +238,7 @@ [ "bio_infrared", 10 ], [ "bio_lockpick", 10 ], [ "bio_night_vision", 10 ], + [ "bio_sonar", 10 ], [ "bio_power_storage_mkII", 10 ], [ "bio_scent_mask", 10 ], [ "bio_jointservo", 10 ], From 3311d34963801df37f8e1da2ba37b68841205497 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 2 Feb 2024 18:14:27 -0800 Subject: [PATCH 073/202] dictionary --- tools/spell_checker/dictionary.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/spell_checker/dictionary.txt b/tools/spell_checker/dictionary.txt index 7f5141ab090d1..3b1993ae15d99 100644 --- a/tools/spell_checker/dictionary.txt +++ b/tools/spell_checker/dictionary.txt @@ -517,6 +517,7 @@ cere cerium cervine cestus +ch chachalaca chainmail chambering @@ -540,6 +541,7 @@ chestpiece chestplate chestplates chestwrap +chhk chicharrones chickenbot chickenwire @@ -554,6 +556,8 @@ chitin chitinous chitinworking chk +chkch +chkchh chlorination cholla chonker @@ -2311,6 +2315,7 @@ pictograms pictographic pidgeon piezomechanical +pii pileus pinita pinnata From 50523f9a2b51e025df686f6a4bc6f480577bc7cc Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 2 Feb 2024 18:55:37 -0800 Subject: [PATCH 074/202] Update dictionary.txt --- tools/spell_checker/dictionary.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/spell_checker/dictionary.txt b/tools/spell_checker/dictionary.txt index 3b1993ae15d99..1929f8ebad420 100644 --- a/tools/spell_checker/dictionary.txt +++ b/tools/spell_checker/dictionary.txt @@ -378,6 +378,7 @@ boomin boonie booo booorrrring +boop bootable bootup boozeberry From 93bf6a0cfe1355473fa7374f52762743145bfeaa Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 2 Feb 2024 19:00:47 -0800 Subject: [PATCH 075/202] arpen fix --- data/json/techniques.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/json/techniques.json b/data/json/techniques.json index c63579ccb8215..afcb9eee952fd 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3597,7 +3597,7 @@ { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 2.1 }, { "stat": "arpen", "type": "bash", "scaling-stat": "bash", "scale": 0.5 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 0.66 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 0.33 }, { "stat": "movecost", "scale": 75 }, { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } @@ -3710,7 +3710,7 @@ { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 3.0 }, { "stat": "arpen", "type": "bash", "scaling-stat": "bash", "scale": 0.5 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 0.66 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 0.33 }, { "stat": "movecost", "scale": 75 }, { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } From b62223d1b90d032d7a073d2bdd82efd43d8c16c0 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 2 Feb 2024 22:14:50 -0800 Subject: [PATCH 076/202] balance --- data/json/items/melee/swords_and_blades.json | 1 - data/json/techniques.json | 30 ++++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/data/json/items/melee/swords_and_blades.json b/data/json/items/melee/swords_and_blades.json index d24ccaad1259a..2f3f926bf24fc 100644 --- a/data/json/items/melee/swords_and_blades.json +++ b/data/json/items/melee/swords_and_blades.json @@ -322,7 +322,6 @@ "install_time": "5 s" }, "min_skills": [ [ "weapon", 2 ], [ "melee", 1 ] ], - "techniques": [ "RAPID" ], "qualities": [ [ "CUT", 2 ], [ "SAW_W", 1 ], [ "CUT_FINE", 1 ], [ "BUTCHER", 19 ] ], "thrown_damage": [ { "damage_type": "stab", "amount": 14 } ], "flags": [ diff --git a/data/json/techniques.json b/data/json/techniques.json index afcb9eee952fd..9e6f0b671cca9 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3524,7 +3524,7 @@ { "stat": "damage", "type": "stab", "scale": 14 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 0.06 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.06 }, { "stat": "movecost", "scale": 75 }, { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } @@ -3564,7 +3564,7 @@ { "stat": "damage", "type": "stab", "scale": 14 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 0.06 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.06 }, { "stat": "movecost", "scale": 75 }, { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } @@ -3593,11 +3593,11 @@ ], "flat_bonuses": [ { "stat": "damage", "type": "stab", "scale": 14 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 4.0 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 4.4 }, { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 2.1 }, - { "stat": "arpen", "type": "bash", "scaling-stat": "bash", "scale": 0.5 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 0.33 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.24 }, + { "stat": "arpen", "type": "bash", "scaling-stat": "unar,ed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, { "stat": "movecost", "scale": 75 }, { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } @@ -3633,10 +3633,10 @@ } ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scale": 22 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 0.06 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.06 }, { "stat": "movecost", "scale": 75 }, { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } @@ -3673,10 +3673,10 @@ ] }, "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 20 }, + { "stat": "damage", "type": "stab", "scale": 22 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 0.06 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.06 }, { "stat": "movecost", "scale": 75 }, { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } @@ -3705,12 +3705,12 @@ ], "attack_vectors": [ "MOUTH" ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 20 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 4.0 }, + { "stat": "damage", "type": "stab", "scale": 22 }, + { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 4.4 }, { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "bash", "scale": 3.0 }, - { "stat": "arpen", "type": "bash", "scaling-stat": "bash", "scale": 0.5 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 0.33 }, + { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.24 }, + { "stat": "arpen", "type": "bash", "scaling-stat": "unarmed", "scale": 1 }, + { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, { "stat": "movecost", "scale": 75 }, { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } From c56a44105745701b7544ec570a51480d74411ea2 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 2 Feb 2024 22:18:55 -0800 Subject: [PATCH 077/202] Update swords_and_blades.json --- data/json/items/melee/swords_and_blades.json | 1 + 1 file changed, 1 insertion(+) diff --git a/data/json/items/melee/swords_and_blades.json b/data/json/items/melee/swords_and_blades.json index 2f3f926bf24fc..d24ccaad1259a 100644 --- a/data/json/items/melee/swords_and_blades.json +++ b/data/json/items/melee/swords_and_blades.json @@ -322,6 +322,7 @@ "install_time": "5 s" }, "min_skills": [ [ "weapon", 2 ], [ "melee", 1 ] ], + "techniques": [ "RAPID" ], "qualities": [ [ "CUT", 2 ], [ "SAW_W", 1 ], [ "CUT_FINE", 1 ], [ "BUTCHER", 19 ] ], "thrown_damage": [ { "damage_type": "stab", "amount": 14 } ], "flags": [ From 2a631764885060ed65eb50da8ea2574612c7a78e Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 2 Feb 2024 22:22:58 -0800 Subject: [PATCH 078/202] dont list damage on the item because it lies --- data/json/items/armor/integrated.json | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index 88519cb506579..f5461cb458d05 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1590,9 +1590,8 @@ "category": "armor", "name": { "str_sp": "fangs" }, "description": "A pair of fangs sharp and sturdy enough to use in combat.", - "//": "Weight and volume are arbitrarily high in order to give them 75 base moves per attack", - "weight": "275 g", - "volume": "400 ml", + "weight": "50 g", + "volume": "100 ml", "price": 0, "price_postapoc": 0, "material": [ "bone" ], @@ -1609,8 +1608,7 @@ "coverage": 0, "encumbrance": 0 } - ], - "melee_damage": { "stab": 14 } + ] }, { "id": "integrated_vampire_fangs", @@ -1618,9 +1616,8 @@ "category": "armor", "name": { "str_sp": "vampire fangs" }, "description": "A wicked pair of fangs, pointed and surgically sharp.", - "//": "Weight and volume are arbitrarily high in order to give them 75 base moves per attack", - "weight": "275 g", - "volume": "400 ml", + "weight": "100 g", + "volume": "200 ml", "price": 0, "price_postapoc": 0, "material": [ "bone" ], @@ -1637,7 +1634,6 @@ "coverage": 0, "encumbrance": 0 } - ], - "melee_damage": { "stab": 20 } + ] } ] From 847fa65231b5c1e368ddfd414a125746a3f9be06 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 3 Feb 2024 00:39:18 -0800 Subject: [PATCH 079/202] nerf fangs a bit --- data/json/techniques.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/json/techniques.json b/data/json/techniques.json index 9e6f0b671cca9..49f0e649e946c 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3521,7 +3521,7 @@ } ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scale": 12 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.06 }, @@ -3561,7 +3561,7 @@ } ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scale": 12 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.06 }, @@ -3592,7 +3592,7 @@ } ], "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 14 }, + { "stat": "damage", "type": "stab", "scale": 12 }, { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 4.4 }, { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.24 }, From 99a6d19b08d80b615907b1295cbcc9ca830d2a26 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 7 Feb 2024 08:30:03 -0800 Subject: [PATCH 080/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 24a8384738c57..09054f3f489df 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -952,7 +952,8 @@ ret_val Character::will_eat( const item &food, bool interactive ) const bool food_is_human_flesh = food.has_flag( flag_CANNIBALISM ) || ( food.has_flag( flag_STRICT_HUMANITARIANISM ) && !has_flag( json_flag_STRICT_HUMANITARIAN ) ); - if( ( food_is_human_flesh && !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) && !has_flag( json_flag_PSYCHOPATH ) ) && + if( ( food_is_human_flesh && !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) && + !has_flag( json_flag_PSYCHOPATH ) ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { if( food_is_human_flesh && ( !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) && From e8c8e616707a246a0591cacfa7008afc6baa9357 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 7 Feb 2024 22:27:17 -0800 Subject: [PATCH 081/202] astyle --- src/consumption.cpp | 1603 +++++++++++++++++++++---------------------- 1 file changed, 792 insertions(+), 811 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 09054f3f489df..adf1769a0a6ca 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -956,951 +956,932 @@ ret_val Character::will_eat( const item &food, bool interactive ) !has_flag( json_flag_PSYCHOPATH ) ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { - if( food_is_human_flesh && ( !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) && - !has_flag( json_flag_PSYCHOPATH ) ) ) { - add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM ); - } - - if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && - !has_flag( json_flag_PARAIMMUNE ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || - ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { - add_consequence( string_format( _( "Consuming this %s probably isn't very healthy." ), - food.tname() ), - PARASITES ); - } - - const bool edible = comest->comesttype == comesttype_FOOD || food.has_flag( flag_USE_EAT_VERB ); - - if( edible && has_effect( effect_nausea ) ) { - add_consequence( _( "You still feel nauseous and will probably puke it all up again." ), NAUSEA ); - } + if( food_is_human_flesh && ( !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) && + !has_flag( json_flag_PSYCHOPATH ) ) ) { + add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM ); + } - if( ( allergy_type( food ) != MORALE_NULL ) || ( carnivore && food.has_flag( flag_ALLERGEN_JUNK ) && - !food.has_flag( flag_CARNIVORE_OK ) ) ) { - add_consequence( _( "Your stomach won't be happy (allergy)." ), ALLERGY ); - } + if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && + !has_flag( json_flag_PARAIMMUNE ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || + ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { + add_consequence( string_format( _( "Consuming this %s probably isn't very healthy." ), + food.tname() ), + PARASITES ); + } - if( saprophage && edible && !food.rotten() && !food.has_flag( flag_FERTILIZER ) ) { - // Note: We're allowing all non-solid "food". This includes drugs - // Hard-coding fertilizer for now - should be a separate flag later - //~ No, we don't eat "rotten" food. We eat properly aged food, like a normal person. - //~ Semantic difference, but greatly facilitates people being proud of their character. - add_consequence( _( "Your stomach won't be happy (not rotten enough)." ), ALLERGY_WEAK ); - } + const bool edible = comest->comesttype == comesttype_FOOD || food.has_flag( flag_USE_EAT_VERB ); - if( food.is_food() && - ( food.charges_per_volume( stomach.stomach_remaining( *this ) ) < 1 || - has_effect( effect_hunger_full ) || has_effect( effect_hunger_engorged ) ) ) { - if( edible ) { - add_consequence( _( "You're full already and will be forcing yourself to eat." ), TOO_FULL ); - } else { - add_consequence( _( "You're full already and will be forcing yourself to drink." ), TOO_FULL ); + if( edible && has_effect( effect_nausea ) ) { + add_consequence( _( "You still feel nauseous and will probably puke it all up again." ), NAUSEA ); } - } - if( !consequences.empty() ) { - if( !interactive ) { - return consequences.front(); - } - std::string req; - for( const auto &elem : consequences ) { - req += elem.str() + "\n"; + if( ( allergy_type( food ) != MORALE_NULL ) || ( carnivore && food.has_flag( flag_ALLERGEN_JUNK ) && + !food.has_flag( flag_CARNIVORE_OK ) ) ) { + add_consequence( _( "Your stomach won't be happy (allergy)." ), ALLERGY ); } - const bool eat_verb = food.has_flag( flag_USE_EAT_VERB ); - std::string food_tame = food.tname(); - const nc_color food_color = food.color_in_inventory(); - if( eat_verb || comest->comesttype == comesttype_FOOD ) { - req += string_format( _( "Eat your %s anyway?" ), colorize( food_tame, food_color ) ); - } else if( !eat_verb && comest->comesttype == comesttype_DRINK ) { - req += string_format( _( "Drink your %s anyway?" ), colorize( food_tame, food_color ) ); - } else { - req += string_format( _( "Consume your %s anyway?" ), colorize( food_tame, food_color ) ); + if( saprophage && edible && !food.rotten() && !food.has_flag( flag_FERTILIZER ) ) { + // Note: We're allowing all non-solid "food". This includes drugs + // Hard-coding fertilizer for now - should be a separate flag later + //~ No, we don't eat "rotten" food. We eat properly aged food, like a normal person. + //~ Semantic difference, but greatly facilitates people being proud of their character. + add_consequence( _( "Your stomach won't be happy (not rotten enough)." ), ALLERGY_WEAK ); } - if( !query_yn( req ) ) { - return consequences.front(); + if( food.is_food() && + ( food.charges_per_volume( stomach.stomach_remaining( *this ) ) < 1 || + has_effect( effect_hunger_full ) || has_effect( effect_hunger_engorged ) ) ) { + if( edible ) { + add_consequence( _( "You're full already and will be forcing yourself to eat." ), TOO_FULL ); + } else { + add_consequence( _( "You're full already and will be forcing yourself to drink." ), TOO_FULL ); + } } - } - // All checks ended, it's edible (or we're pretending it is) - return ret_val::make_success(); -} -/** Eat a comestible. -* @return true if item consumed. -*/ -static bool eat( item &food, Character &you, bool force ) -{ - if( !food.is_food() ) { - return false; - } + if( !consequences.empty() ) { + if( !interactive ) { + return consequences.front(); + } + std::string req; + for( const auto &elem : consequences ) { + req += elem.str() + "\n"; + } - const auto ret = force ? you.can_eat( food ) : you.will_eat( food, you.is_avatar() ); - if( !ret.success() ) { - return false; - } + const bool eat_verb = food.has_flag( flag_USE_EAT_VERB ); + std::string food_tame = food.tname(); + const nc_color food_color = food.color_in_inventory(); + if( eat_verb || comest->comesttype == comesttype_FOOD ) { + req += string_format( _( "Eat your %s anyway?" ), colorize( food_tame, food_color ) ); + } else if( !eat_verb && comest->comesttype == comesttype_DRINK ) { + req += string_format( _( "Drink your %s anyway?" ), colorize( food_tame, food_color ) ); + } else { + req += string_format( _( "Consume your %s anyway?" ), colorize( food_tame, food_color ) ); + } - int charges_used = 0; - if( food.type->has_use() ) { - if( !food.type->can_use( "PETFOOD" ) ) { - charges_used = food.type->invoke( &you, food, you.pos() ).value_or( 0 ); - if( charges_used <= 0 ) { - return false; + if( !query_yn( req ) ) { + return consequences.front(); } } + // All checks ended, it's edible (or we're pretending it is) + return ret_val::make_success(); } - // Note: the block below assumes we decided to eat it - // No coming back from here + /** Eat a comestible. + * @return true if item consumed. + */ + static bool eat( item & food, Character & you, bool force ) { + if( !food.is_food() ) { + return false; + } - if( food.is_container() ) { - food.spill_contents( you ); - } + const auto ret = force ? you.can_eat( food ) : you.will_eat( food, you.is_avatar() ); + if( !ret.success() ) { + return false; + } - const bool hibernate = you.has_active_mutation( trait_HIBERNATE ); - const int nutr = you.nutrition_for( food ); - const int quench = food.get_comestible()->quench; - const bool spoiled = food.rotten(); + int charges_used = 0; + if( food.type->has_use() ) { + if( !food.type->can_use( "PETFOOD" ) ) { + charges_used = food.type->invoke( &you, food, you.pos() ).value_or( 0 ); + if( charges_used <= 0 ) { + return false; + } + } + } - // The item is solid food - const bool chew = food.get_comestible()->comesttype == comesttype_FOOD || - food.has_flag( flag_USE_EAT_VERB ); - // This item is a drink and not a solid food (and not a thick soup) - const bool drinkable = !chew && food.get_comestible()->comesttype == comesttype_DRINK; - // If neither of the above is true then it's a drug and shouldn't get mealtime penalty/bonus + // Note: the block below assumes we decided to eat it + // No coming back from here - if( hibernate && - ( you.get_hunger() > -60 && you.get_thirst() > -60 ) && - ( you.get_hunger() - nutr < -60 || you.get_thirst() - quench < -60 ) ) { - you.add_msg_if_player( - _( "You've begun stockpiling calories and liquid for hibernation. You get the feeling that you should prepare for bed, just in case, but… you're hungry again, and you could eat a whole week's worth of food RIGHT NOW." ) ); - } + if( food.is_container() ) { + food.spill_contents( you ); + } + + const bool hibernate = you.has_active_mutation( trait_HIBERNATE ); + const int nutr = you.nutrition_for( food ); + const int quench = food.get_comestible()->quench; + const bool spoiled = food.rotten(); + + // The item is solid food + const bool chew = food.get_comestible()->comesttype == comesttype_FOOD || + food.has_flag( flag_USE_EAT_VERB ); + // This item is a drink and not a solid food (and not a thick soup) + const bool drinkable = !chew && food.get_comestible()->comesttype == comesttype_DRINK; + // If neither of the above is true then it's a drug and shouldn't get mealtime penalty/bonus - const bool will_vomit = you.stomach.stomach_remaining( you ) < food.volume() && - rng( units::to_milliliter( you.stomach.capacity( you ) ) / 2, - units::to_milliliter( you.stomach.contains() ) ) > units::to_milliliter( - you.stomach.capacity( you ) ); - const bool saprophage = you.has_trait( trait_SAPROPHAGE ); - if( spoiled && !saprophage ) { - you.add_msg_if_player( m_bad, _( "Ick, this %s doesn't taste so good…" ), food.tname() ); - if( !you.has_flag( json_flag_IMMUNE_SPOIL ) ) { - you.add_effect( effect_foodpoison, rng( 6_minutes, ( nutr + 1 ) * 6_minutes ) ); + if( hibernate && + ( you.get_hunger() > -60 && you.get_thirst() > -60 ) && + ( you.get_hunger() - nutr < -60 || you.get_thirst() - quench < -60 ) ) { + you.add_msg_if_player( + _( "You've begun stockpiling calories and liquid for hibernation. You get the feeling that you should prepare for bed, just in case, but… you're hungry again, and you could eat a whole week's worth of food RIGHT NOW." ) ); } - } else if( spoiled && saprophage ) { - you.add_msg_if_player( m_good, _( "Mmm, this %s tastes delicious…" ), food.tname() ); - } - if( !you.consume_effects( food ) ) { - // Already consumed by using `food.type->invoke`? - if( charges_used > 0 ) { - if( food.count_by_charges() ) { - food.mod_charges( -charges_used ); + + const bool will_vomit = you.stomach.stomach_remaining( you ) < food.volume() && + rng( units::to_milliliter( you.stomach.capacity( you ) ) / 2, + units::to_milliliter( you.stomach.contains() ) ) > units::to_milliliter( + you.stomach.capacity( you ) ); + const bool saprophage = you.has_trait( trait_SAPROPHAGE ); + if( spoiled && !saprophage ) { + you.add_msg_if_player( m_bad, _( "Ick, this %s doesn't taste so good…" ), food.tname() ); + if( !you.has_flag( json_flag_IMMUNE_SPOIL ) ) { + you.add_effect( effect_foodpoison, rng( 6_minutes, ( nutr + 1 ) * 6_minutes ) ); } - return true; + } else if( spoiled && saprophage ) { + you.add_msg_if_player( m_good, _( "Mmm, this %s tastes delicious…" ), food.tname() ); + } + if( !you.consume_effects( food ) ) { + // Already consumed by using `food.type->invoke`? + if( charges_used > 0 ) { + if( food.count_by_charges() ) { + food.mod_charges( -charges_used ); + } + return true; + } + return false; } - return false; - } - if( food.count_by_charges() ) { - food.mod_charges( -1 ); - } - - const bool amorphous = you.has_trait( trait_AMORPHOUS ); - - // If it's poisonous... poison us. - // TODO: Move this to a flag - if( food.poison > 0 && - !you.has_trait( trait_EATDEAD ) ) { - if( food.poison >= rng( 2, 4 ) ) { - you.add_effect( effect_poison, food.poison * 10_minutes ); + if( food.count_by_charges() ) { + food.mod_charges( -1 ); } - you.add_effect( effect_foodpoison, food.poison * 30_minutes ); - } + const bool amorphous = you.has_trait( trait_AMORPHOUS ); - if( food.has_flag( flag_HIDDEN_HALLU ) ) { - if( !you.has_effect( effect_hallu ) ) { - you.add_effect( effect_hallu, 6_hours ); - } - } + // If it's poisonous... poison us. + // TODO: Move this to a flag + if( food.poison > 0 && + !you.has_trait( trait_EATDEAD ) ) { + if( food.poison >= rng( 2, 4 ) ) { + you.add_effect( effect_poison, food.poison * 10_minutes ); + } - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { + you.add_effect( effect_foodpoison, food.poison * 30_minutes ); + } - add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); - add_msg( _( "You drink your %s (rotten)." ), food.tname() ); - } else { - you.add_msg_player_or_npc( _( "You drink your %s." ), _( " drinks a %s." ), - food.tname() ); + if( food.has_flag( flag_HIDDEN_HALLU ) ) { + if( !you.has_effect( effect_hallu ) ) { + you.add_effect( effect_hallu, 6_hours ); + } } - } else if( chew ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { - add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); - add_msg( _( "You eat your %s (rotten)." ), food.tname() ); - } else { - you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), food.tname() ); - } - } + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { - if( item::find_type( food.get_comestible()->tool )->tool ) { - // Tools like lighters get used - you.use_charges( food.get_comestible()->tool, 1 ); - } + add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); + add_msg( _( "You drink your %s (rotten)." ), food.tname() ); + } else { + you.add_msg_player_or_npc( _( "You drink your %s." ), _( " drinks a %s." ), + food.tname() ); + } + } else if( chew ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { - if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && - you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { - you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); - } + add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); + add_msg( _( "You eat your %s (rotten)." ), food.tname() ); + } else { + you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + food.tname() ); + } + } - if( food.has_flag( flag_FUNGAL_VECTOR ) && !you.has_trait( trait_M_IMMUNE ) ) { - you.add_effect( effect_fungus, 1_turns, true ); - } + if( item::find_type( food.get_comestible()->tool )->tool ) { + // Tools like lighters get used + you.use_charges( food.get_comestible()->tool, 1 ); + } - // The fun changes for these effects are applied in fun_for(). - if( food.has_flag( flag_MUSHY ) ) { - you.add_msg_if_player( m_bad, - _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); - } - if( food.get_comestible_fun() > 0 ) { - if( you.has_effect( effect_common_cold ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); + if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && + you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { + you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); } - if( you.has_effect( effect_flu ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); + + if( food.has_flag( flag_FUNGAL_VECTOR ) && !you.has_trait( trait_M_IMMUNE ) ) { + you.add_effect( effect_fungus, 1_turns, true ); } - } - // Chance to become parasitised - if( !will_vomit && !you.has_flag( json_flag_PARAIMMUNE ) ) { - if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && - one_in( food.get_comestible()->parasites ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || - ( !you.has_flag( json_flag_HEMOVORE ) && !you.has_flag( json_flag_BLOODFEEDER ) ) ) ) { - switch( rng( 0, 3 ) ) { - case 0: - if( !you.has_trait( trait_EATHEALTH ) ) { - you.add_effect( effect_tapeworm, 1_turns, true ); - } - break; - case 1: - if( !you.has_trait( trait_ACIDBLOOD ) ) { - you.add_effect( effect_bloodworms, 1_turns, true ); - } - break; - case 2: - you.add_effect( effect_brainworms, 1_turns, true ); - break; - case 3: - you.add_effect( effect_paincysts, 1_turns, true ); + // The fun changes for these effects are applied in fun_for(). + if( food.has_flag( flag_MUSHY ) ) { + you.add_msg_if_player( m_bad, + _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); + } + if( food.get_comestible_fun() > 0 ) { + if( you.has_effect( effect_common_cold ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); + } + if( you.has_effect( effect_flu ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); } } - } - for( const std::pair &elem : food.get_comestible()->contamination ) { - if( rng( 1, 100 ) <= elem.second ) { - you.expose_to_disease( elem.first ); + // Chance to become parasitised + if( !will_vomit && !you.has_flag( json_flag_PARAIMMUNE ) ) { + if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && + one_in( food.get_comestible()->parasites ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || + ( !you.has_flag( json_flag_HEMOVORE ) && !you.has_flag( json_flag_BLOODFEEDER ) ) ) ) { + switch( rng( 0, 3 ) ) { + case 0: + if( !you.has_trait( trait_EATHEALTH ) ) { + you.add_effect( effect_tapeworm, 1_turns, true ); + } + break; + case 1: + if( !you.has_trait( trait_ACIDBLOOD ) ) { + you.add_effect( effect_bloodworms, 1_turns, true ); + } + break; + case 2: + you.add_effect( effect_brainworms, 1_turns, true ); + break; + case 3: + you.add_effect( effect_paincysts, 1_turns, true ); + } + } } - } - get_event_bus().send( you.getID(), food.typeId() ); + for( const std::pair &elem : food.get_comestible()->contamination ) { + if( rng( 1, 100 ) <= elem.second ) { + you.expose_to_disease( elem.first ); + } + } - if( will_vomit ) { - you.vomit(); - } + get_event_bus().send( you.getID(), food.typeId() ); - you.consumption_history.emplace_back( food ); - // Clean out consumption_history so it doesn't get bigger than needed. - while( you.consumption_history.front().time < calendar::turn - 2_days ) { - you.consumption_history.pop_front(); - } + if( will_vomit ) { + you.vomit(); + } - you.recoil = MAX_RECOIL; + you.consumption_history.emplace_back( food ); + // Clean out consumption_history so it doesn't get bigger than needed. + while( you.consumption_history.front().time < calendar::turn - 2_days ) { + you.consumption_history.pop_front(); + } - return true; -} + you.recoil = MAX_RECOIL; -void Character::modify_health( const islot_comestible &comest ) -{ - const int effective_health = comest.healthy; - // Effectively no cap on health modifiers from food and meds - const int health_cap = 200; - mod_daily_health( effective_health, effective_health >= 0 ? health_cap : -health_cap ); -} + return true; + } -void Character::modify_stimulation( const islot_comestible &comest ) -{ - if( comest.stim == 0 ) { - return; + void Character::modify_health( const islot_comestible & comest ) { + const int effective_health = comest.healthy; + // Effectively no cap on health modifiers from food and meds + const int health_cap = 200; + mod_daily_health( effective_health, effective_health >= 0 ? health_cap : -health_cap ); } - const int current_stim = get_stim(); - if( ( std::abs( comest.stim ) * 3 ) > std::abs( current_stim ) ) { - mod_stim( comest.stim ); - } else { - comest.stim > 0 ? mod_stim( std::max( comest.stim / 2, 1 ) ) : mod_stim( std::min( comest.stim / 2, - -1 ) ); - } - if( has_trait( trait_STIMBOOST ) && ( current_stim > 30 ) && - ( comest.addictions.count( STATIC( addiction_id( "caffeine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "amphetamine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "cocaine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "crack" ) ) ) ) ) { - int hallu_duration = ( current_stim - comest.stim < 30 ) ? current_stim - 30 : comest.stim; - add_effect( effect_visuals, hallu_duration * 30_minutes ); - add_msg_if_player( m_bad, SNIPPET.random_from_category( "comest_stimulant" ).value_or( - translation() ).translated() ); + + void Character::modify_stimulation( const islot_comestible & comest ) { + if( comest.stim == 0 ) { + return; + } + const int current_stim = get_stim(); + if( ( std::abs( comest.stim ) * 3 ) > std::abs( current_stim ) ) { + mod_stim( comest.stim ); + } else { + comest.stim > 0 ? mod_stim( std::max( comest.stim / 2, 1 ) ) : mod_stim( std::min( comest.stim / 2, + -1 ) ); + } + if( has_trait( trait_STIMBOOST ) && ( current_stim > 30 ) && + ( comest.addictions.count( STATIC( addiction_id( "caffeine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "amphetamine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "cocaine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "crack" ) ) ) ) ) { + int hallu_duration = ( current_stim - comest.stim < 30 ) ? current_stim - 30 : comest.stim; + add_effect( effect_visuals, hallu_duration * 30_minutes ); + add_msg_if_player( m_bad, SNIPPET.random_from_category( "comest_stimulant" ).value_or( + translation() ).translated() ); + } } -} -void Character::modify_fatigue( const islot_comestible &comest ) -{ - mod_fatigue( -comest.fatigue_mod ); -} + void Character::modify_fatigue( const islot_comestible & comest ) { + mod_fatigue( -comest.fatigue_mod ); + } -void Character::modify_addiction( const islot_comestible &comest ) -{ - for( const std::pair &add : comest.addictions ) { - add_addiction( add.first, add.second ); - if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { - rem_morale( add.first->get_craving_morale() ); + void Character::modify_addiction( const islot_comestible & comest ) { + for( const std::pair &add : comest.addictions ) { + add_addiction( add.first, add.second ); + if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { + rem_morale( add.first->get_craving_morale() ); + } } } -} -void Character::modify_morale( item &food, const int nutr ) -{ - time_duration morale_time = 2_hours; - if( food.has_flag( flag_HOT ) && food.has_flag( flag_EATEN_HOT ) ) { - morale_time = 3_hours; - int clamped_nutr = std::max( 5, std::min( 20, nutr / 10 ) ); - add_morale( MORALE_FOOD_HOT, clamped_nutr, 20, morale_time, morale_time / 2 ); - } - - std::pair fun = fun_for( food ); - if( fun.first < 0 ) { - add_morale( MORALE_FOOD_BAD, fun.first, fun.second, morale_time, morale_time / 2, false, - food.type ); - } else if( fun.first > 0 ) { - add_morale( MORALE_FOOD_GOOD, fun.first, fun.second, morale_time, morale_time / 2, false, - food.type ); - } - - // Morale bonus for eating unspoiled food with chair/table nearby - // Does not apply to non-ingested consumables like bandages or drugs, - // nor to drinks. - if( !food.has_flag( flag_NO_INGEST ) && - food.get_comestible()->comesttype != "MED" && - food.get_comestible()->comesttype != comesttype_DRINK ) { - map &here = get_map(); - if( here.has_nearby_chair( pos(), 1 ) && here.has_nearby_table( pos_bub(), 1 ) ) { - if( has_trait( trait_TABLEMANNERS ) ) { - rem_morale( MORALE_ATE_WITHOUT_TABLE ); - if( !food.rotten() ) { - add_morale( MORALE_ATE_WITH_TABLE, 3, 3, 3_hours, 2_hours, true ); + void Character::modify_morale( item & food, const int nutr ) { + time_duration morale_time = 2_hours; + if( food.has_flag( flag_HOT ) && food.has_flag( flag_EATEN_HOT ) ) { + morale_time = 3_hours; + int clamped_nutr = std::max( 5, std::min( 20, nutr / 10 ) ); + add_morale( MORALE_FOOD_HOT, clamped_nutr, 20, morale_time, morale_time / 2 ); + } + + std::pair fun = fun_for( food ); + if( fun.first < 0 ) { + add_morale( MORALE_FOOD_BAD, fun.first, fun.second, morale_time, morale_time / 2, false, + food.type ); + } else if( fun.first > 0 ) { + add_morale( MORALE_FOOD_GOOD, fun.first, fun.second, morale_time, morale_time / 2, false, + food.type ); + } + + // Morale bonus for eating unspoiled food with chair/table nearby + // Does not apply to non-ingested consumables like bandages or drugs, + // nor to drinks. + if( !food.has_flag( flag_NO_INGEST ) && + food.get_comestible()->comesttype != "MED" && + food.get_comestible()->comesttype != comesttype_DRINK ) { + map &here = get_map(); + if( here.has_nearby_chair( pos(), 1 ) && here.has_nearby_table( pos_bub(), 1 ) ) { + if( has_trait( trait_TABLEMANNERS ) ) { + rem_morale( MORALE_ATE_WITHOUT_TABLE ); + if( !food.rotten() ) { + add_morale( MORALE_ATE_WITH_TABLE, 3, 3, 3_hours, 2_hours, true ); + } + } else if( !food.rotten() ) { + add_morale( MORALE_ATE_WITH_TABLE, 1, 1, 3_hours, 2_hours, true ); + } + } else { + if( has_trait( trait_TABLEMANNERS ) ) { + rem_morale( MORALE_ATE_WITH_TABLE ); + add_morale( MORALE_ATE_WITHOUT_TABLE, -2, -4, 3_hours, 2_hours, true ); } - } else if( !food.rotten() ) { - add_morale( MORALE_ATE_WITH_TABLE, 1, 1, 3_hours, 2_hours, true ); } - } else { - if( has_trait( trait_TABLEMANNERS ) ) { - rem_morale( MORALE_ATE_WITH_TABLE ); - add_morale( MORALE_ATE_WITHOUT_TABLE, -2, -4, 3_hours, 2_hours, true ); + } + + if( food.has_flag( flag_HIDDEN_HALLU ) ) { + if( has_trait( trait_SPIRITUAL ) ) { + add_morale( MORALE_FOOD_GOOD, 36, 72, 2_hours, 1_hours, false ); + } else { + add_morale( MORALE_FOOD_GOOD, 18, 36, 1_hours, 30_minutes, false ); } } - } - if( food.has_flag( flag_HIDDEN_HALLU ) ) { - if( has_trait( trait_SPIRITUAL ) ) { - add_morale( MORALE_FOOD_GOOD, 36, 72, 2_hours, 1_hours, false ); - } else { - add_morale( MORALE_FOOD_GOOD, 18, 36, 1_hours, 30_minutes, false ); + const bool food_is_human_flesh = food.has_flag( flag_CANNIBALISM ) || + ( food.has_flag( flag_STRICT_HUMANITARIANISM ) && + !has_flag( json_flag_STRICT_HUMANITARIAN ) ); + if( food_is_human_flesh ) { + // Sapiovores don't recognize humans as the same species. + // But let them possibly feel cool about eating sapient stuff - treat like psycho + // However, spiritual sapiovores should still recognize humans as having a soul or special for religious reasons + // Hemovores feel weird about human blood, bloodfeeders don't care unless they're also cannibals or w/e + const bool cannibal = has_flag( json_flag_CANNIBAL ); + const bool psycho = has_flag( json_flag_PSYCHOPATH ); + const bool sapiovore = has_flag( json_flag_SAPIOVORE ); + const bool spiritual = has_flag( json_flag_SPIRITUAL ); + const bool numb = has_flag( json_flag_NUMB ); + const bool bloodfeeder = has_flag( json_flag_BLOODFEEDER ); + if( cannibal && psycho && spiritual ) { + add_msg_if_player( m_good, + _( "You feast upon the human flesh, and in doing so, devour their spirit." ) ); + // You're not really consuming anything special; you just think you are. + add_morale( MORALE_CANNIBAL, 25, 300 ); + } else if( cannibal && psycho ) { + add_msg_if_player( m_good, _( "You feast upon the human flesh." ) ); + add_morale( MORALE_CANNIBAL, 15, 200 ); + } else if( cannibal && spiritual ) { + add_msg_if_player( m_good, _( "You consume the sacred human flesh." ) ); + // Boosted because you understand the philosophical implications of your actions, and YOU LIKE THEM. + add_morale( MORALE_CANNIBAL, 15, 200 ); + } else if( sapiovore && spiritual ) { + add_msg_if_player( m_good, _( "You eat the human flesh, and in doing so, devour their spirit." ) ); + add_morale( MORALE_CANNIBAL, 10, 50 ); + } else if( cannibal ) { + add_msg_if_player( m_good, _( "You indulge your shameful hunger." ) ); + add_morale( MORALE_CANNIBAL, 10, 50 ); + } else if( psycho && spiritual ) { + add_msg_if_player( _( "You greedily devour the taboo meat." ) ); + // Small bonus for violating a taboo. + add_morale( MORALE_CANNIBAL, 5, 50 ); + } else if( psycho ) { + add_msg_if_player( _( "Meh. You've eaten worse." ) ); + } else if( sapiovore ) { + add_msg_if_player( _( "Mmh. Tastes like venison." ) ); + } else if( spiritual ) { + add_msg_if_player( m_bad, + _( "This is probably going to count against you if there's still an afterlife." ) ); + add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); + } else if( numb ) { + add_msg_if_player( m_bad, _( "You find this meal distasteful, but necessary." ) ); + add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); + } else if( bloodfeeder && food.has_flag( flag_HEMOVORE_FUN ) ) { + add_msg_if_player( _( "The human blood is as sweet as any other." ) ); + } else { + add_msg_if_player( m_bad, _( "You feel horrible for eating a person." ) ); + add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); + } } - } - const bool food_is_human_flesh = food.has_flag( flag_CANNIBALISM ) || - ( food.has_flag( flag_STRICT_HUMANITARIANISM ) && - !has_flag( json_flag_STRICT_HUMANITARIAN ) ); - if( food_is_human_flesh ) { - // Sapiovores don't recognize humans as the same species. - // But let them possibly feel cool about eating sapient stuff - treat like psycho - // However, spiritual sapiovores should still recognize humans as having a soul or special for religious reasons - // Hemovores feel weird about human blood, bloodfeeders don't care unless they're also cannibals or w/e - const bool cannibal = has_flag( json_flag_CANNIBAL ); - const bool psycho = has_flag( json_flag_PSYCHOPATH ); - const bool sapiovore = has_flag( json_flag_SAPIOVORE ); - const bool spiritual = has_flag( json_flag_SPIRITUAL ); - const bool numb = has_flag( json_flag_NUMB ); - const bool bloodfeeder = has_flag( json_flag_BLOODFEEDER ); - if( cannibal && psycho && spiritual ) { - add_msg_if_player( m_good, - _( "You feast upon the human flesh, and in doing so, devour their spirit." ) ); - // You're not really consuming anything special; you just think you are. - add_morale( MORALE_CANNIBAL, 25, 300 ); - } else if( cannibal && psycho ) { - add_msg_if_player( m_good, _( "You feast upon the human flesh." ) ); - add_morale( MORALE_CANNIBAL, 15, 200 ); - } else if( cannibal && spiritual ) { - add_msg_if_player( m_good, _( "You consume the sacred human flesh." ) ); - // Boosted because you understand the philosophical implications of your actions, and YOU LIKE THEM. - add_morale( MORALE_CANNIBAL, 15, 200 ); - } else if( sapiovore && spiritual ) { - add_msg_if_player( m_good, _( "You eat the human flesh, and in doing so, devour their spirit." ) ); - add_morale( MORALE_CANNIBAL, 10, 50 ); - } else if( cannibal ) { - add_msg_if_player( m_good, _( "You indulge your shameful hunger." ) ); - add_morale( MORALE_CANNIBAL, 10, 50 ); - } else if( psycho && spiritual ) { - add_msg_if_player( _( "You greedily devour the taboo meat." ) ); - // Small bonus for violating a taboo. - add_morale( MORALE_CANNIBAL, 5, 50 ); - } else if( psycho ) { - add_msg_if_player( _( "Meh. You've eaten worse." ) ); - } else if( sapiovore ) { - add_msg_if_player( _( "Mmh. Tastes like venison." ) ); - } else if( spiritual ) { - add_msg_if_player( m_bad, - _( "This is probably going to count against you if there's still an afterlife." ) ); - add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); - } else if( numb ) { - add_msg_if_player( m_bad, _( "You find this meal distasteful, but necessary." ) ); - add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); - } else if( bloodfeeder && food.has_flag( flag_HEMOVORE_FUN ) ) { - add_msg_if_player( _( "The human blood is as sweet as any other." ) ); - } else { - add_msg_if_player( m_bad, _( "You feel horrible for eating a person." ) ); - add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); - } - } - - // While raw flesh usually means negative morale, carnivores and cullers get a small bonus. - // Hunters, predators, and apex predators don't mind raw flesh at all, maybe even like it. - // Cooked flesh is unaffected, because people with these traits *prefer* it raw. Fat is unaffected. - // Organs are still usually negative due to fun values as low as -35. - // The PREDATOR_FUN flag shouldn't be on human flesh, to not interfere with sapiovores/cannibalism. - if( food.has_flag( flag_PREDATOR_FUN ) ) { - const bool carnivore = has_trait( trait_CARNIVORE ); - const bool culler = has_flag( json_flag_PRED1 ); - const bool hunter = has_flag( json_flag_PRED2 ); - const bool predator = has_flag( json_flag_PRED3 ); - const bool apex_predator = has_flag( json_flag_PRED4 ); - if( apex_predator ) { - // Largest bonus, balances out to around +5 or +10. Some organs may still be negative. - add_morale( MORALE_MEATARIAN, 20, 10 ); - add_msg_if_player( m_good, - _( "As you tear into the raw flesh, you feel satisfied with your meal." ) ); - } else if( predator || hunter ) { - // Should approximately balance the fun to 0 for normal meat. - add_morale( MORALE_MEATARIAN, 15, 5 ); - add_msg_if_player( m_good, - _( "Raw flesh doesn't taste all that bad, actually." ) ); - } else if( carnivore || culler ) { - // Only a small bonus (+5), still negative fun. - add_morale( MORALE_MEATARIAN, 5, 0 ); - add_msg_if_player( m_bad, - _( "This doesn't taste very good, but meat is meat." ) ); - } - } - - // Allergy check for food that is ingested (not gum) - if( !food.has_flag( flag_NO_INGEST ) ) { - const morale_type allergy = allergy_type( food ); - if( allergy != MORALE_NULL ) { - add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); - add_morale( allergy, -75, -400, 30_minutes, 24_minutes ); + // While raw flesh usually means negative morale, carnivores and cullers get a small bonus. + // Hunters, predators, and apex predators don't mind raw flesh at all, maybe even like it. + // Cooked flesh is unaffected, because people with these traits *prefer* it raw. Fat is unaffected. + // Organs are still usually negative due to fun values as low as -35. + // The PREDATOR_FUN flag shouldn't be on human flesh, to not interfere with sapiovores/cannibalism. + if( food.has_flag( flag_PREDATOR_FUN ) ) { + const bool carnivore = has_trait( trait_CARNIVORE ); + const bool culler = has_flag( json_flag_PRED1 ); + const bool hunter = has_flag( json_flag_PRED2 ); + const bool predator = has_flag( json_flag_PRED3 ); + const bool apex_predator = has_flag( json_flag_PRED4 ); + if( apex_predator ) { + // Largest bonus, balances out to around +5 or +10. Some organs may still be negative. + add_morale( MORALE_MEATARIAN, 20, 10 ); + add_msg_if_player( m_good, + _( "As you tear into the raw flesh, you feel satisfied with your meal." ) ); + } else if( predator || hunter ) { + // Should approximately balance the fun to 0 for normal meat. + add_morale( MORALE_MEATARIAN, 15, 5 ); + add_msg_if_player( m_good, + _( "Raw flesh doesn't taste all that bad, actually." ) ); + } else if( carnivore || culler ) { + // Only a small bonus (+5), still negative fun. + add_morale( MORALE_MEATARIAN, 5, 0 ); + add_msg_if_player( m_bad, + _( "This doesn't taste very good, but meat is meat." ) ); + } } - if( food.has_flag( flag_ALLERGEN_JUNK ) ) { - if( has_trait( trait_PROJUNK ) ) { - add_msg_if_player( m_good, _( "Mmm, junk food." ) ); - add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); + + // Allergy check for food that is ingested (not gum) + if( !food.has_flag( flag_NO_INGEST ) ) { + const morale_type allergy = allergy_type( food ); + if( allergy != MORALE_NULL ) { + add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); + add_morale( allergy, -75, -400, 30_minutes, 24_minutes ); } - if( has_trait( trait_PROJUNK2 ) ) { - if( !one_in( 100 ) ) { - add_msg_if_player( m_good, _( "When life's got you down, there's always sugar." ) ); - } else { - add_msg_if_player( m_good, _( "They may do what they must… you've already won." ) ); + if( food.has_flag( flag_ALLERGEN_JUNK ) ) { + if( has_trait( trait_PROJUNK ) ) { + add_msg_if_player( m_good, _( "Mmm, junk food." ) ); + add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); + } + if( has_trait( trait_PROJUNK2 ) ) { + if( !one_in( 100 ) ) { + add_msg_if_player( m_good, _( "When life's got you down, there's always sugar." ) ); + } else { + add_msg_if_player( m_good, _( "They may do what they must… you've already won." ) ); + } + add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes ); + } + // Carnivores CAN eat junk food, but they won't like it much. + // Pizza-scraping happens in consume_effects. + if( has_trait( trait_CARNIVORE ) && !food.has_flag( flag_CARNIVORE_OK ) ) { + add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); + add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); } - add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes ); - } - // Carnivores CAN eat junk food, but they won't like it much. - // Pizza-scraping happens in consume_effects. - if( has_trait( trait_CARNIVORE ) && !food.has_flag( flag_CARNIVORE_OK ) ) { - add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); - add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); } } - } - const bool chew = food.get_comestible()->comesttype == comesttype_FOOD || - food.has_flag( flag_USE_EAT_VERB ); - if( !food.rotten() && chew && has_trait( trait_SAPROPHAGE ) ) { - // It's OK to *drink* things that haven't rotted. Alternative is to ban water. D: - add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); - add_morale( MORALE_NO_DIGEST, -75, -400, 30_minutes, 24_minutes ); - } - if( food.has_flag( flag_URSINE_HONEY ) && ( !crossed_threshold() || - has_trait( trait_THRESH_URSINE ) ) && - mutation_category_level[mutation_category_URSINE] > 20 ) { - int honey_fun = std::min( mutation_category_level[mutation_category_URSINE] / 5, 20 ); - if( honey_fun < 10 ) { - add_msg_if_player( m_good, _( "You find the sweet taste of honey surprisingly palatable." ) ); - } else { - add_msg_if_player( m_good, _( "You feast upon the sweet honey." ) ); + const bool chew = food.get_comestible()->comesttype == comesttype_FOOD || + food.has_flag( flag_USE_EAT_VERB ); + if( !food.rotten() && chew && has_trait( trait_SAPROPHAGE ) ) { + // It's OK to *drink* things that haven't rotted. Alternative is to ban water. D: + add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); + add_morale( MORALE_NO_DIGEST, -75, -400, 30_minutes, 24_minutes ); + } + if( food.has_flag( flag_URSINE_HONEY ) && ( !crossed_threshold() || + has_trait( trait_THRESH_URSINE ) ) && + mutation_category_level[mutation_category_URSINE] > 20 ) { + int honey_fun = std::min( mutation_category_level[mutation_category_URSINE] / 5, 20 ); + if( honey_fun < 10 ) { + add_msg_if_player( m_good, _( "You find the sweet taste of honey surprisingly palatable." ) ); + } else { + add_msg_if_player( m_good, _( "You feast upon the sweet honey." ) ); + } + add_morale( MORALE_HONEY, honey_fun, 100 ); } - add_morale( MORALE_HONEY, honey_fun, 100 ); } -} -// Used when determining stomach fullness from eating. -double Character::compute_effective_food_volume_ratio( const item &food ) const -{ - const nutrients food_nutrients = compute_effective_nutrients( food ); - units::mass food_weight = ( food.weight() / std::max( 1, food.count() ) ); - double ratio = 1.0f; - if( units::to_gram( food_weight ) != 0 ) { - ratio = std::max( static_cast( food_nutrients.kcal() ) / units::to_gram( food_weight ), - 1.0 ); - if( ratio > 3.0f ) { - ratio = std::sqrt( 3 * ratio ); + // Used when determining stomach fullness from eating. + double Character::compute_effective_food_volume_ratio( const item & food ) const { + const nutrients food_nutrients = compute_effective_nutrients( food ); + units::mass food_weight = ( food.weight() / std::max( 1, food.count() ) ); + double ratio = 1.0f; + if( units::to_gram( food_weight ) != 0 ) { + ratio = std::max( static_cast( food_nutrients.kcal() ) / units::to_gram( food_weight ), + 1.0 ); + if( ratio > 3.0f ) { + ratio = std::sqrt( 3 * ratio ); + } } + return ratio; } - return ratio; -} -// Remove the water volume from the food, as that gets absorbed and used as water. -// If the remaining dry volume of the food is less dense than water, crunch it down to a density equal to water. -// These maths are made easier by the fact that 1 g = 1 mL. Thanks, metric system. -units::volume Character::masticated_volume( const item &food ) const -{ - units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? food.get_comestible()->quench * - 5_ml : 0_ml; - units::mass water_weight = units::from_gram( units::to_milliliter( water_vol ) ); - // handle the division by zero exception when the food count is 0 with std::max() - units::mass food_dry_weight = food.weight() / std::max( 1, food.count() ) - water_weight; - units::volume food_dry_volume = food.volume() / std::max( 1, food.count() ) - water_vol; + // Remove the water volume from the food, as that gets absorbed and used as water. + // If the remaining dry volume of the food is less dense than water, crunch it down to a density equal to water. + // These maths are made easier by the fact that 1 g = 1 mL. Thanks, metric system. + units::volume Character::masticated_volume( const item & food ) const { + units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? food.get_comestible()->quench * + 5_ml : 0_ml; + units::mass water_weight = units::from_gram( units::to_milliliter( water_vol ) ); + // handle the division by zero exception when the food count is 0 with std::max() + units::mass food_dry_weight = food.weight() / std::max( 1, food.count() ) - water_weight; + units::volume food_dry_volume = food.volume() / std::max( 1, food.count() ) - water_vol; - if( units::to_milliliter( food_dry_volume ) != 0 && - units::to_gram( food_dry_weight ) < units::to_milliliter( food_dry_volume ) ) { - food_dry_volume = units::from_milliliter( units::to_gram( food_dry_weight ) ); - } - - return food_dry_volume; -} + if( units::to_milliliter( food_dry_volume ) != 0 && + units::to_gram( food_dry_weight ) < units::to_milliliter( food_dry_volume ) ) { + food_dry_volume = units::from_milliliter( units::to_gram( food_dry_weight ) ); + } -// Used when displaying effective food satiation values. -int Character::compute_calories_per_effective_volume( const item &food, - const nutrients *nutrient /* = nullptr */ )const -{ - /* Understanding how Calories Per Effective Volume are calculated requires a dive into the - stomach fullness source code. Look at issue #44365*/ - int kcalories; - if( nutrient ) { - // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). - kcalories = nutrient->kcal(); - } else { - kcalories = compute_effective_nutrients( food ).kcal(); - } - double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); - const double energy_density_ratio = compute_effective_food_volume_ratio( food ); - const double effective_volume = food_vol * energy_density_ratio; - if( kcalories == 0 && effective_volume == 0.0 ) { - return 0; + return food_dry_volume; } - return std::round( kcalories / effective_volume ); -} -static void activate_consume_eocs( Character &you, item &target ) -{ - Character *char_ptr = nullptr; - if( avatar *u = you.as_avatar() ) { - char_ptr = u; - } else if( npc *n = you.as_npc() ) { - char_ptr = n; - } - item_location loc( you, &target ); - dialogue d( get_talker_for( char_ptr ), get_talker_for( loc ) ); - const islot_comestible &comest = *target.get_comestible(); - for( const effect_on_condition_id &eoc : comest.consumption_eocs ) { - eoc->activate( d ); + // Used when displaying effective food satiation values. + int Character::compute_calories_per_effective_volume( const item & food, + const nutrients * nutrient /* = nullptr */ )const { + /* Understanding how Calories Per Effective Volume are calculated requires a dive into the + stomach fullness source code. Look at issue #44365*/ + int kcalories; + if( nutrient ) { + // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). + kcalories = nutrient->kcal(); + } else { + kcalories = compute_effective_nutrients( food ).kcal(); + } + double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); + const double energy_density_ratio = compute_effective_food_volume_ratio( food ); + const double effective_volume = food_vol * energy_density_ratio; + if( kcalories == 0 && effective_volume == 0.0 ) { + return 0; + } + return std::round( kcalories / effective_volume ); } -} -bool Character::consume_effects( item &food ) -{ - if( !food.is_comestible() ) { - debugmsg( "called Character::consume_effects with non-comestible" ); - return false; + static void activate_consume_eocs( Character & you, item & target ) { + Character *char_ptr = nullptr; + if( avatar *u = you.as_avatar() ) { + char_ptr = u; + } else if( npc *n = you.as_npc() ) { + char_ptr = n; + } + item_location loc( you, &target ); + dialogue d( get_talker_for( char_ptr ), get_talker_for( loc ) ); + const islot_comestible &comest = *target.get_comestible(); + for( const effect_on_condition_id &eoc : comest.consumption_eocs ) { + eoc->activate( d ); + } } - if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { - // Was used to cap nutrition and thirst, but no longer does this - return false; - } - if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && - food.has_any_flag( herbivore_blacklist ) ) { - // No good can come of this. - return false; - } + bool Character::consume_effects( item & food ) { + if( !food.is_comestible() ) { + debugmsg( "called Character::consume_effects with non-comestible" ); + return false; + } - const islot_comestible &comest = *food.get_comestible(); + if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { + // Was used to cap nutrition and thirst, but no longer does this + return false; + } + if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && + food.has_any_flag( herbivore_blacklist ) ) { + // No good can come of this. + return false; + } - // Rotten food causes health loss - const float relative_rot = food.get_relative_rot(); - if( relative_rot > 1.0f && !has_flag( json_flag_IMMUNE_SPOIL ) ) { - const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f ); - // ~-1 health per 1 nutrition at halfway-rotten-away, ~0 at "just got rotten" - // But always round down - int h_loss = -rottedness * comest.get_default_nutr(); - mod_daily_health( h_loss, -200 ); - add_msg_debug( debugmode::DF_FOOD, "%d health from %0.2f%% rotten food", h_loss, rottedness ); - } - - // Used in hibernation messages. - const int nutr = nutrition_for( food ); - const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; - // We can handle junk just fine - if( !skip_health ) { - modify_health( comest ); - } - modify_stimulation( comest ); - modify_fatigue( comest ); - modify_addiction( comest ); - modify_morale( food, nutr ); - - const bool hibernate = has_active_mutation( trait_HIBERNATE ); - if( hibernate ) { - if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { - // Tell the player what's going on - add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); - if( one_in( 2 ) ) { - // 50% chance of the food tiring you - mod_fatigue( nutr ); + const islot_comestible &comest = *food.get_comestible(); + + // Rotten food causes health loss + const float relative_rot = food.get_relative_rot(); + if( relative_rot > 1.0f && !has_flag( json_flag_IMMUNE_SPOIL ) ) { + const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f ); + // ~-1 health per 1 nutrition at halfway-rotten-away, ~0 at "just got rotten" + // But always round down + int h_loss = -rottedness * comest.get_default_nutr(); + mod_daily_health( h_loss, -200 ); + add_msg_debug( debugmode::DF_FOOD, "%d health from %0.2f%% rotten food", h_loss, rottedness ); + } + + // Used in hibernation messages. + const int nutr = nutrition_for( food ); + const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; + // We can handle junk just fine + if( !skip_health ) { + modify_health( comest ); + } + modify_stimulation( comest ); + modify_fatigue( comest ); + modify_addiction( comest ); + modify_morale( food, nutr ); + + const bool hibernate = has_active_mutation( trait_HIBERNATE ); + if( hibernate ) { + if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { + // Tell the player what's going on + add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); + if( one_in( 2 ) ) { + // 50% chance of the food tiring you + mod_fatigue( nutr ); + } } - } - if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { - // Hibernation should cut burn to 60/day - add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); - if( one_in( 2 ) ) { - // And another 50%, intended cumulative - mod_fatigue( nutr ); + if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { + // Hibernation should cut burn to 60/day + add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); + if( one_in( 2 ) ) { + // And another 50%, intended cumulative + mod_fatigue( nutr ); + } } - } - if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { - add_msg_if_player( - _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); - if( !one_in( 3 ) ) { - // Third check, this one at 66% + if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { + add_msg_if_player( + _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); + if( !one_in( 3 ) ) { + // Third check, this one at 66% + mod_fatigue( nutr ); + } + } + if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { + add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); + // At this point, you're done. Schlaf gut. mod_fatigue( nutr ); } } - if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { - add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); - // At this point, you're done. Schlaf gut. - mod_fatigue( nutr ); - } - } - // Moved here and changed a bit - it was too complex - // Incredibly minor stuff like this shouldn't require complexity - if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && - ( get_healthy_kcal() < get_stored_kcal() + 4000 && - get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { - add_msg_if_player( m_mixed, - _( "You feel as though you're going to split open! In a good way?" ) ); - mod_pain( 5 ); - int numslime = 1; - for( int i = 0; i < numslime; i++ ) { - if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { - slime->friendly = -1; + // Moved here and changed a bit - it was too complex + // Incredibly minor stuff like this shouldn't require complexity + if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && + ( get_healthy_kcal() < get_stored_kcal() + 4000 && + get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { + add_msg_if_player( m_mixed, + _( "You feel as though you're going to split open! In a good way?" ) ); + mod_pain( 5 ); + int numslime = 1; + for( int i = 0; i < numslime; i++ ) { + if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { + slime->friendly = -1; + } } + mod_hunger( 40 ); + mod_thirst( 40 ); + //~ slimespawns have *small voices* which may be the Nice equivalent + //~ of the Rat King's ALL CAPS invective. Probably shared-brain telepathy. + add_msg_if_player( m_good, _( "hey, you look like me! let's work together!" ) ); + } + + nutrients food_nutrients = compute_effective_nutrients( food ); + const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? + food.get_comestible()->quench * + 5_ml : 0_ml; + units::volume food_vol = masticated_volume( food ); + if( food.count() == 0 ) { + debugmsg( "Tried to eat food with count of zero." ); + return false; } - mod_hunger( 40 ); - mod_thirst( 40 ); - //~ slimespawns have *small voices* which may be the Nice equivalent - //~ of the Rat King's ALL CAPS invective. Probably shared-brain telepathy. - add_msg_if_player( m_good, _( "hey, you look like me! let's work together!" ) ); - } + units::mass food_weight = ( food.weight() / food.count() ); + const double ratio = compute_effective_food_volume_ratio( food ); + food_summary ingested{ + water_vol, + food_vol * ratio, + food_nutrients + }; + add_msg_debug( debugmode::DF_FOOD, + "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", + units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, + food_nutrients.kcal(), units::to_gram( food_weight ) ); + // Maybe move tapeworm to digestion + if( has_effect( effect_tapeworm ) ) { + ingested.nutr /= 2; + } + // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? + activate_consume_eocs( *this, food ); - nutrients food_nutrients = compute_effective_nutrients( food ); - const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? - food.get_comestible()->quench * - 5_ml : 0_ml; - units::volume food_vol = masticated_volume( food ); - if( food.count() == 0 ) { - debugmsg( "Tried to eat food with count of zero." ); - return false; - } - units::mass food_weight = ( food.weight() / food.count() ); - const double ratio = compute_effective_food_volume_ratio( food ); - food_summary ingested{ - water_vol, - food_vol * ratio, - food_nutrients - }; - add_msg_debug( debugmode::DF_FOOD, - "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", - units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, - food_nutrients.kcal(), units::to_gram( food_weight ) ); - // Maybe move tapeworm to digestion - if( has_effect( effect_tapeworm ) ) { - ingested.nutr /= 2; - } - // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? - activate_consume_eocs( *this, food ); + // GET IN MAH BELLY! + stomach.ingest( ingested ); - // GET IN MAH BELLY! - stomach.ingest( ingested ); + // update speculative values + if( is_avatar() ) { + get_avatar().add_ingested_kcal( ingested.nutr.calories / 1000 ); + } + for( const auto &v : ingested.nutr.vitamins() ) { + // update the estimated values for daily vitamins + // actual vitamins happen during digestion + daily_vitamins[v.first].first += v.second; + } - // update speculative values - if( is_avatar() ) { - get_avatar().add_ingested_kcal( ingested.nutr.calories / 1000 ); - } - for( const auto &v : ingested.nutr.vitamins() ) { - // update the estimated values for daily vitamins - // actual vitamins happen during digestion - daily_vitamins[v.first].first += v.second; + return true; } - return true; -} - -bool Character::can_estimate_rot() const -{ - return get_greater_skill_or_knowledge_level( skill_cooking ) >= 3 || - get_greater_skill_or_knowledge_level( skill_survival ) >= 4; -} - -bool Character::can_consume_as_is( const item &it ) const -{ - if( it.is_comestible() ) { - return !it.has_flag( flag_FROZEN ) || it.has_flag( flag_EDIBLE_FROZEN ) || - it.has_flag( flag_MELTS ); + bool Character::can_estimate_rot() const { + return get_greater_skill_or_knowledge_level( skill_cooking ) >= 3 || + get_greater_skill_or_knowledge_level( skill_survival ) >= 4; } - return false; -} -item &Character::get_consumable_from( item &it ) const -{ - item *ret = nullptr; - it.visit_items( [&]( item * it, item * ) { - if( can_consume_as_is( *it ) ) { - ret = it; - return VisitResponse::ABORT; + bool Character::can_consume_as_is( const item & it ) const { + if( it.is_comestible() ) { + return !it.has_flag( flag_FROZEN ) || it.has_flag( flag_EDIBLE_FROZEN ) || + it.has_flag( flag_MELTS ); } - return VisitResponse::NEXT; - } ); - - if( ret != nullptr ) { - return *ret; + return false; } - static item null_comestible; - // Since it's not const. - null_comestible = item(); - return null_comestible; -} - -time_duration Character::get_consume_time( const item &it ) const -{ - const int charges = std::max( it.charges, 1 ); - int volume = units::to_milliliter( it.volume() ) / charges; - if( 0 == volume && it.type ) { - volume = units::to_milliliter( it.type->volume ); - } - time_duration time = time_duration::from_seconds( std::max( ( volume / - 5 ), 1 ) ); //Default 5 mL (1 tablespoon) per second - float consume_time_modifier = 1.0f;//only for food and drinks - const bool eat_verb = it.has_flag( flag_USE_EAT_VERB ); - const std::string comest_type = it.get_comestible() ? it.get_comestible()->comesttype : ""; - if( eat_verb || comest_type == "FOOD" ) { - time = time_duration::from_seconds( volume / 5 ); //Eat 5 mL (1 teaspoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } else if( !eat_verb && comest_type == "DRINK" ) { - time = time_duration::from_seconds( volume / 15 ); //Drink 15 mL (1 tablespoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } else if( use_function const *fun = it.type->get_use( "heal" ) ) { - time = time_duration::from_moves( dynamic_cast - ( fun->get_actor_ptr() )->move_cost ); - } else if( it.is_medication() ) { - const use_function *consume_drug = it.type->get_use( "consume_drug" ); - const use_function *smoking = it.type->get_use( "SMOKING" ); - const use_function *adrenaline_injector = it.type->get_use( "ADRENALINE_INJECTOR" ); - if( consume_drug != nullptr ) { //its a drug - const consume_drug_iuse *consume_drug_use = dynamic_cast - ( consume_drug->get_actor_ptr() ); - if( consume_drug_use->tools_needed.find( itype_syringe ) != consume_drug_use->tools_needed.end() && - has_bionic( bio_syringe ) ) { - time = time_duration::from_seconds( - 15 );//injections with the intradermal needle CBM are much quicker than with a normal syringe - } else if( consume_drug_use->tools_needed.find( itype_syringe ) != - consume_drug_use->tools_needed.end() ) { - time = time_duration::from_minutes( 5 );//sterile injections take 5 minutes - } else if( consume_drug_use->tools_needed.find( itype_apparatus ) != - consume_drug_use->tools_needed.end() || - consume_drug_use->tools_needed.find( itype_dab_pen_on ) != consume_drug_use->tools_needed.end() ) { - time = time_duration::from_seconds( 30 );//smoke a bowl + item &Character::get_consumable_from( item & it ) const { + item *ret = nullptr; + it.visit_items( [&]( item * it, item * ) { + if( can_consume_as_is( *it ) ) { + ret = it; + return VisitResponse::ABORT; + } + return VisitResponse::NEXT; + } ); + + if( ret != nullptr ) { + return *ret; + } + + static item null_comestible; + // Since it's not const. + null_comestible = item(); + return null_comestible; + } + + time_duration Character::get_consume_time( const item & it ) const { + const int charges = std::max( it.charges, 1 ); + int volume = units::to_milliliter( it.volume() ) / charges; + if( 0 == volume && it.type ) { + volume = units::to_milliliter( it.type->volume ); + } + time_duration time = time_duration::from_seconds( std::max( ( volume / + 5 ), 1 ) ); //Default 5 mL (1 tablespoon) per second + float consume_time_modifier = 1.0f;//only for food and drinks + const bool eat_verb = it.has_flag( flag_USE_EAT_VERB ); + const std::string comest_type = it.get_comestible() ? it.get_comestible()->comesttype : ""; + if( eat_verb || comest_type == "FOOD" ) { + time = time_duration::from_seconds( volume / 5 ); //Eat 5 mL (1 teaspoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } else if( !eat_verb && comest_type == "DRINK" ) { + time = time_duration::from_seconds( volume / 15 ); //Drink 15 mL (1 tablespoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } else if( use_function const *fun = it.type->get_use( "heal" ) ) { + time = time_duration::from_moves( dynamic_cast + ( fun->get_actor_ptr() )->move_cost ); + } else if( it.is_medication() ) { + const use_function *consume_drug = it.type->get_use( "consume_drug" ); + const use_function *smoking = it.type->get_use( "SMOKING" ); + const use_function *adrenaline_injector = it.type->get_use( "ADRENALINE_INJECTOR" ); + if( consume_drug != nullptr ) { //its a drug + const consume_drug_iuse *consume_drug_use = dynamic_cast + ( consume_drug->get_actor_ptr() ); + if( consume_drug_use->tools_needed.find( itype_syringe ) != consume_drug_use->tools_needed.end() && + has_bionic( bio_syringe ) ) { + time = time_duration::from_seconds( + 15 );//injections with the intradermal needle CBM are much quicker than with a normal syringe + } else if( consume_drug_use->tools_needed.find( itype_syringe ) != + consume_drug_use->tools_needed.end() ) { + time = time_duration::from_minutes( 5 );//sterile injections take 5 minutes + } else if( consume_drug_use->tools_needed.find( itype_apparatus ) != + consume_drug_use->tools_needed.end() || + consume_drug_use->tools_needed.find( itype_dab_pen_on ) != consume_drug_use->tools_needed.end() ) { + time = time_duration::from_seconds( 30 );//smoke a bowl + } else { + time = time_duration::from_seconds( 5 );//popping a pill is quick + } + } else if( smoking != nullptr ) { + time = time_duration::from_minutes( 1 );//about five minutes for a cig or joint so 1 minute a charge + } else if( adrenaline_injector != nullptr ) { + //epi-pens, and disinfectant are fairly quick + time = time_duration::from_seconds( 15 ); } else { - time = time_duration::from_seconds( 5 );//popping a pill is quick + time = time_duration::from_seconds( 5 ); //probably pills so quick } - } else if( smoking != nullptr ) { - time = time_duration::from_minutes( 1 );//about five minutes for a cig or joint so 1 minute a charge - } else if( adrenaline_injector != nullptr ) { - //epi-pens, and disinfectant are fairly quick - time = time_duration::from_seconds( 15 ); - } else { - time = time_duration::from_seconds( 5 ); //probably pills so quick + } else if( it.get_category_shallow().get_id() == item_category_chems ) { + time = time_duration::from_seconds( std::max( ( volume / 15 ), + 1 ) ); //Consume 15 mL (1 tablespoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); } - } else if( it.get_category_shallow().get_id() == item_category_chems ) { - time = time_duration::from_seconds( std::max( ( volume / 15 ), - 1 ) ); //Consume 15 mL (1 tablespoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } - // Minimum consumption time, without mutations, is always 1 second. - time = std::max( 1_seconds, time ); + // Minimum consumption time, without mutations, is always 1 second. + time = std::max( 1_seconds, time ); - return time * consume_time_modifier; -} + return time * consume_time_modifier; + } -static bool query_consume_ownership( item &target, Character &p ) -{ - if( !target.is_owned_by( p, true ) ) { - bool choice = true; - if( p.get_value( "THIEF_MODE" ) == "THIEF_ASK" ) { - choice = Pickup::query_thief(); - } - if( p.get_value( "THIEF_MODE" ) == "THIEF_HONEST" || !choice ) { - return false; - } - std::vector witnesses; - for( npc &elem : g->all_npcs() ) { - if( rl_dist( elem.pos(), p.pos() ) < MAX_VIEW_DISTANCE && elem.sees( p.pos() ) ) { - witnesses.push_back( &elem ); + static bool query_consume_ownership( item & target, Character & p ) { + if( !target.is_owned_by( p, true ) ) { + bool choice = true; + if( p.get_value( "THIEF_MODE" ) == "THIEF_ASK" ) { + choice = Pickup::query_thief(); } - } - for( npc *elem : witnesses ) { - elem->say( "", 7 ); - } - if( !witnesses.empty() && target.is_owned_by( p, true ) ) { - if( p.add_faction_warning( target.get_owner() ) ) { - for( npc *elem : witnesses ) { - elem->make_angry(); + if( p.get_value( "THIEF_MODE" ) == "THIEF_HONEST" || !choice ) { + return false; + } + std::vector witnesses; + for( npc &elem : g->all_npcs() ) { + if( rl_dist( elem.pos(), p.pos() ) < MAX_VIEW_DISTANCE && elem.sees( p.pos() ) ) { + witnesses.push_back( &elem ); + } + } + for( npc *elem : witnesses ) { + elem->say( "", 7 ); + } + if( !witnesses.empty() && target.is_owned_by( p, true ) ) { + if( p.add_faction_warning( target.get_owner() ) ) { + for( npc *elem : witnesses ) { + elem->make_angry(); + } } } } + return true; } - return true; -} -/** Consume medication. -* @return true if item consumed. -*/ -static bool consume_med( item &target, Character &you ) -{ - if( !target.is_medication() ) { - return false; - } + /** Consume medication. + * @return true if item consumed. + */ + static bool consume_med( item & target, Character & you ) { + if( !target.is_medication() ) { + return false; + } - const itype_id tool_type = target.get_comestible()->tool; - const itype *req_tool = item::find_type( tool_type ); + const itype_id tool_type = target.get_comestible()->tool; + const itype *req_tool = item::find_type( tool_type ); - if( req_tool->tool ) { - if( !( you.has_amount( tool_type, 1 ) && - you.has_charges( tool_type, req_tool->tool->charges_per_use ) ) ) { - you.add_msg_if_player( m_info, _( "You need a %s to consume that!" ), req_tool->nname( 1 ) ); - return false; + if( req_tool->tool ) { + if( !( you.has_amount( tool_type, 1 ) && + you.has_charges( tool_type, req_tool->tool->charges_per_use ) ) ) { + you.add_msg_if_player( m_info, _( "You need a %s to consume that!" ), req_tool->nname( 1 ) ); + return false; + } + you.use_charges( tool_type, req_tool->tool->charges_per_use ); } - you.use_charges( tool_type, req_tool->tool->charges_per_use ); - } - int amount_used = 1; - if( target.type->has_use() ) { - amount_used = target.type->invoke( &you, target, you.pos() ).value_or( 0 ); - if( amount_used <= 0 ) { - return false; + int amount_used = 1; + if( target.type->has_use() ) { + amount_used = target.type->invoke( &you, target, you.pos() ).value_or( 0 ); + if( amount_used <= 0 ) { + return false; + } } - } - // TODO: Get the target it was used on - // Otherwise injecting someone will give us addictions etc. - if( target.has_flag( flag_NO_INGEST ) ) { - const islot_comestible &comest = *target.get_comestible(); - // Assume that parenteral meds don't spoil, so don't apply rot - you.modify_health( comest ); - you.modify_stimulation( comest ); - you.modify_fatigue( comest ); - you.modify_addiction( comest ); - you.modify_morale( target ); - activate_consume_eocs( you, target ); - } else { - // Take by mouth - if( !you.consume_effects( target ) ) { + // TODO: Get the target it was used on + // Otherwise injecting someone will give us addictions etc. + if( target.has_flag( flag_NO_INGEST ) ) { + const islot_comestible &comest = *target.get_comestible(); + // Assume that parenteral meds don't spoil, so don't apply rot + you.modify_health( comest ); + you.modify_stimulation( comest ); + you.modify_fatigue( comest ); + you.modify_addiction( comest ); + you.modify_morale( target ); activate_consume_eocs( you, target ); + } else { + // Take by mouth + if( !you.consume_effects( target ) ) { + activate_consume_eocs( you, target ); + } } - } - - if( target.count_by_charges() ) { - target.mod_charges( -amount_used ); - } - return true; -} -trinary Character::consume( item &target, bool force ) -{ - if( target.is_null() ) { - add_msg_if_player( m_info, _( "You do not have that item." ) ); - return trinary::NONE; - } - if( ( !has_trait( trait_WATERSLEEP ) && !has_trait( trait_UNDINE_SLEEP_WATER ) ) && - cant_do_underwater() ) { - return trinary::NONE; + if( target.count_by_charges() ) { + target.mod_charges( -amount_used ); + } + return true; } - if( target.is_craft() ) { - add_msg_if_player( m_info, _( "You can't eat your %s." ), target.tname() ); - if( is_npc() ) { - debugmsg( "%s tried to eat a %s", get_name(), target.tname() ); + trinary Character::consume( item & target, bool force ) { + if( target.is_null() ) { + add_msg_if_player( m_info, _( "You do not have that item." ) ); + return trinary::NONE; + } + if( ( !has_trait( trait_WATERSLEEP ) && !has_trait( trait_UNDINE_SLEEP_WATER ) ) && + cant_do_underwater() ) { + return trinary::NONE; } - return trinary::NONE; - } - if( is_avatar() && !query_consume_ownership( target, *this ) ) { - return trinary::NONE; - } - if( consume_med( target, *this ) || eat( target, *this, force ) ) { + if( target.is_craft() ) { + add_msg_if_player( m_info, _( "You can't eat your %s." ), target.tname() ); + if( is_npc() ) { + debugmsg( "%s tried to eat a %s", get_name(), target.tname() ); + } + return trinary::NONE; + } + if( is_avatar() && !query_consume_ownership( target, *this ) ) { + return trinary::NONE; + } - get_event_bus().send( getID(), target.typeId() ); + if( consume_med( target, *this ) || eat( target, *this, force ) ) { - invalidate_weight_carried_cache(); - target.on_contents_changed(); - return !target.count_by_charges() || target.charges <= 0 ? trinary::ALL : trinary::SOME; - } + get_event_bus().send( getID(), target.typeId() ); - return trinary::NONE; -} + invalidate_weight_carried_cache(); + target.on_contents_changed(); + return !target.count_by_charges() || target.charges <= 0 ? trinary::ALL : trinary::SOME; + } -trinary Character::consume( item_location loc, bool force ) -{ - if( !loc ) { - debugmsg( "Null loc to consume." ); return trinary::NONE; } - contents_change_handler handler; - item &target = *loc; - trinary result = consume( target, force ); - if( result != trinary::NONE ) { - handler.unseal_pocket_containing( loc ); - } - if( result == trinary::ALL ) { - if( loc.where() == item_location::type::character ) { - i_rem( loc.get_item() ); - } else { - loc.remove_item(); + + trinary Character::consume( item_location loc, bool force ) { + if( !loc ) { + debugmsg( "Null loc to consume." ); + return trinary::NONE; + } + contents_change_handler handler; + item &target = *loc; + trinary result = consume( target, force ); + if( result != trinary::NONE ) { + handler.unseal_pocket_containing( loc ); } + if( result == trinary::ALL ) { + if( loc.where() == item_location::type::character ) { + i_rem( loc.get_item() ); + } else { + loc.remove_item(); + } + } + handler.handle_by( *this ); + return result; } - handler.handle_by( *this ); - return result; -} From ce704b0b8fa27d4ae855cf8eef969302e5db4f25 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 7 Feb 2024 22:58:00 -0800 Subject: [PATCH 082/202] Update consumption.cpp --- src/consumption.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index adf1769a0a6ca..4a8d4faed19f0 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -956,8 +956,6 @@ ret_val Character::will_eat( const item &food, bool interactive ) !has_flag( json_flag_PSYCHOPATH ) ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { - if( food_is_human_flesh && ( !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) && - !has_flag( json_flag_PSYCHOPATH ) ) ) { add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM ); } From 1f8990c37c040f5df8ffc5b1c1480b8212bf5324 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:23:33 -0800 Subject: [PATCH 083/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 4a8d4faed19f0..07c5490db7aaf 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -956,8 +956,8 @@ ret_val Character::will_eat( const item &food, bool interactive ) !has_flag( json_flag_PSYCHOPATH ) ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { - add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM ); - } + add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM ); + } if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && !has_flag( json_flag_PARAIMMUNE ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || From 788405cebea1e7c2980a40fdc067f6c3e3e36d3f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:23:40 -0800 Subject: [PATCH 084/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 07c5490db7aaf..d623711362231 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -959,13 +959,13 @@ ret_val Character::will_eat( const item &food, bool interactive ) add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM ); } - if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && - !has_flag( json_flag_PARAIMMUNE ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || - ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { - add_consequence( string_format( _( "Consuming this %s probably isn't very healthy." ), - food.tname() ), - PARASITES ); - } + if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && + !has_flag( json_flag_PARAIMMUNE ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || + ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { + add_consequence( string_format( _( "Consuming this %s probably isn't very healthy." ), + food.tname() ), + PARASITES ); + } const bool edible = comest->comesttype == comesttype_FOOD || food.has_flag( flag_USE_EAT_VERB ); From 58a23e5fc1ec30e1b09196e9881bf64159da39ec Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:23:46 -0800 Subject: [PATCH 085/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index d623711362231..859e56eee78a3 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -969,9 +969,9 @@ ret_val Character::will_eat( const item &food, bool interactive ) const bool edible = comest->comesttype == comesttype_FOOD || food.has_flag( flag_USE_EAT_VERB ); - if( edible && has_effect( effect_nausea ) ) { - add_consequence( _( "You still feel nauseous and will probably puke it all up again." ), NAUSEA ); - } + if( edible && has_effect( effect_nausea ) ) { + add_consequence( _( "You still feel nauseous and will probably puke it all up again." ), NAUSEA ); + } if( ( allergy_type( food ) != MORALE_NULL ) || ( carnivore && food.has_flag( flag_ALLERGEN_JUNK ) && !food.has_flag( flag_CARNIVORE_OK ) ) ) { From 3332df2e041b78ed9d730d794ef1cf2ec426e2ce Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:24:00 -0800 Subject: [PATCH 086/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 859e56eee78a3..0f2099ef45d94 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -967,7 +967,7 @@ ret_val Character::will_eat( const item &food, bool interactive ) PARASITES ); } - const bool edible = comest->comesttype == comesttype_FOOD || food.has_flag( flag_USE_EAT_VERB ); + const bool edible = comest->comesttype == comesttype_FOOD || food.has_flag( flag_USE_EAT_VERB ); if( edible && has_effect( effect_nausea ) ) { add_consequence( _( "You still feel nauseous and will probably puke it all up again." ), NAUSEA ); From e0989816ebcb160a2a4824e2da9f75ccfe215cc0 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:24:08 -0800 Subject: [PATCH 087/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 0f2099ef45d94..0307505213d97 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -973,10 +973,10 @@ ret_val Character::will_eat( const item &food, bool interactive ) add_consequence( _( "You still feel nauseous and will probably puke it all up again." ), NAUSEA ); } - if( ( allergy_type( food ) != MORALE_NULL ) || ( carnivore && food.has_flag( flag_ALLERGEN_JUNK ) && - !food.has_flag( flag_CARNIVORE_OK ) ) ) { - add_consequence( _( "Your stomach won't be happy (allergy)." ), ALLERGY ); - } + if( ( allergy_type( food ) != MORALE_NULL ) || ( carnivore && food.has_flag( flag_ALLERGEN_JUNK ) && + !food.has_flag( flag_CARNIVORE_OK ) ) ) { + add_consequence( _( "Your stomach won't be happy (allergy)." ), ALLERGY ); + } if( saprophage && edible && !food.rotten() && !food.has_flag( flag_FERTILIZER ) ) { // Note: We're allowing all non-solid "food". This includes drugs From 87d89243ff8fefebdd403b85513938272eac722a Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:24:24 -0800 Subject: [PATCH 088/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 0307505213d97..d4e7c75654121 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1110,7 +1110,12 @@ ret_val Character::will_eat( const item &food, bool interactive ) you.add_effect( effect_poison, food.poison * 10_minutes ); } - you.add_effect( effect_foodpoison, food.poison * 30_minutes ); + // If it's poisonous... poison us. + // TODO: Move this to a flag + if( food.poison > 0 && + !you.has_trait( trait_EATDEAD ) ) { + if( food.poison >= rng( 2, 4 ) ) { + you.add_effect( effect_poison, food.poison * 10_minutes ); } if( food.has_flag( flag_HIDDEN_HALLU ) ) { From 4b19965f0d258da3d0f448a9d099da953ac1e2c3 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:24:40 -0800 Subject: [PATCH 089/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/consumption.cpp b/src/consumption.cpp index d4e7c75654121..9fab8982c7a94 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1123,6 +1123,7 @@ ret_val Character::will_eat( const item &food, bool interactive ) you.add_effect( effect_hallu, 6_hours ); } } + } if( amorphous ) { you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), From ba423efb439902fb7cd9d428641499c2e478d923 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:24:58 -0800 Subject: [PATCH 090/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 9fab8982c7a94..d9dde44c37225 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1118,10 +1118,12 @@ ret_val Character::will_eat( const item &food, bool interactive ) you.add_effect( effect_poison, food.poison * 10_minutes ); } - if( food.has_flag( flag_HIDDEN_HALLU ) ) { - if( !you.has_effect( effect_hallu ) ) { - you.add_effect( effect_hallu, 6_hours ); - } + you.add_effect( effect_foodpoison, food.poison * 30_minutes ); + } + + if( food.has_flag( flag_HIDDEN_HALLU ) ) { + if( !you.has_effect( effect_hallu ) ) { + you.add_effect( effect_hallu, 6_hours ); } } From 5a43626c637fe9b94749b23ecaead9a882871cf3 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:25:49 -0800 Subject: [PATCH 091/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index d9dde44c37225..8a83be915826c 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1016,9 +1016,8 @@ ret_val Character::will_eat( const item &food, bool interactive ) req += string_format( _( "Consume your %s anyway?" ), colorize( food_tame, food_color ) ); } - if( !query_yn( req ) ) { - return consequences.front(); - } + if( !query_yn( req ) ) { + return consequences.front(); } // All checks ended, it's edible (or we're pretending it is) return ret_val::make_success(); From 71e4c907cee5688a52034096a9a6056990dd0c61 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:26:06 -0800 Subject: [PATCH 092/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 8a83be915826c..4d192aeb6b226 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1134,16 +1134,13 @@ ret_val Character::will_eat( const item &food, bool interactive ) !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && you.is_avatar() ) { - add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); - add_msg( _( "You drink your %s (rotten)." ), food.tname() ); - } else { - you.add_msg_player_or_npc( _( "You drink your %s." ), _( " drinks a %s." ), - food.tname() ); - } - } else if( chew ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); add_msg( _( "You eat your %s (rotten)." ), food.tname() ); From 3156bdf620b2c80e3ee79d92457cfee7ecbd5721 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:26:27 -0800 Subject: [PATCH 093/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 4d192aeb6b226..e828e82405640 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1260,12 +1260,17 @@ ret_val Character::will_eat( const item &food, bool interactive ) mod_fatigue( -comest.fatigue_mod ); } - void Character::modify_addiction( const islot_comestible & comest ) { - for( const std::pair &add : comest.addictions ) { - add_addiction( add.first, add.second ); - if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { - rem_morale( add.first->get_craving_morale() ); - } +void Character::modify_fatigue( const islot_comestible &comest ) +{ + mod_fatigue( -comest.fatigue_mod ); +} + +void Character::modify_addiction( const islot_comestible &comest ) +{ + for( const std::pair &add : comest.addictions ) { + add_addiction( add.first, add.second ); + if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { + rem_morale( add.first->get_craving_morale() ); } } From 34b5374235ee291d8074dd05ec6c90159dcf73f7 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:26:38 -0800 Subject: [PATCH 094/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 67 ++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index e828e82405640..e097af75c295d 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1274,43 +1274,36 @@ void Character::modify_addiction( const islot_comestible &comest ) } } - void Character::modify_morale( item & food, const int nutr ) { - time_duration morale_time = 2_hours; - if( food.has_flag( flag_HOT ) && food.has_flag( flag_EATEN_HOT ) ) { - morale_time = 3_hours; - int clamped_nutr = std::max( 5, std::min( 20, nutr / 10 ) ); - add_morale( MORALE_FOOD_HOT, clamped_nutr, 20, morale_time, morale_time / 2 ); - } - - std::pair fun = fun_for( food ); - if( fun.first < 0 ) { - add_morale( MORALE_FOOD_BAD, fun.first, fun.second, morale_time, morale_time / 2, false, - food.type ); - } else if( fun.first > 0 ) { - add_morale( MORALE_FOOD_GOOD, fun.first, fun.second, morale_time, morale_time / 2, false, - food.type ); - } - - // Morale bonus for eating unspoiled food with chair/table nearby - // Does not apply to non-ingested consumables like bandages or drugs, - // nor to drinks. - if( !food.has_flag( flag_NO_INGEST ) && - food.get_comestible()->comesttype != "MED" && - food.get_comestible()->comesttype != comesttype_DRINK ) { - map &here = get_map(); - if( here.has_nearby_chair( pos(), 1 ) && here.has_nearby_table( pos_bub(), 1 ) ) { - if( has_trait( trait_TABLEMANNERS ) ) { - rem_morale( MORALE_ATE_WITHOUT_TABLE ); - if( !food.rotten() ) { - add_morale( MORALE_ATE_WITH_TABLE, 3, 3, 3_hours, 2_hours, true ); - } - } else if( !food.rotten() ) { - add_morale( MORALE_ATE_WITH_TABLE, 1, 1, 3_hours, 2_hours, true ); - } - } else { - if( has_trait( trait_TABLEMANNERS ) ) { - rem_morale( MORALE_ATE_WITH_TABLE ); - add_morale( MORALE_ATE_WITHOUT_TABLE, -2, -4, 3_hours, 2_hours, true ); +void Character::modify_morale( item &food, const int nutr ) +{ + time_duration morale_time = 2_hours; + if( food.has_flag( flag_HOT ) && food.has_flag( flag_EATEN_HOT ) ) { + morale_time = 3_hours; + int clamped_nutr = std::max( 5, std::min( 20, nutr / 10 ) ); + add_morale( MORALE_FOOD_HOT, clamped_nutr, 20, morale_time, morale_time / 2 ); + } + + std::pair fun = fun_for( food ); + if( fun.first < 0 ) { + add_morale( MORALE_FOOD_BAD, fun.first, fun.second, morale_time, morale_time / 2, false, + food.type ); + } else if( fun.first > 0 ) { + add_morale( MORALE_FOOD_GOOD, fun.first, fun.second, morale_time, morale_time / 2, false, + food.type ); + } + + // Morale bonus for eating unspoiled food with chair/table nearby + // Does not apply to non-ingested consumables like bandages or drugs, + // nor to drinks. + if( !food.has_flag( flag_NO_INGEST ) && + food.get_comestible()->comesttype != "MED" && + food.get_comestible()->comesttype != comesttype_DRINK ) { + map &here = get_map(); + if( here.has_nearby_chair( pos(), 1 ) && here.has_nearby_table( pos_bub(), 1 ) ) { + if( has_trait( trait_TABLEMANNERS ) ) { + rem_morale( MORALE_ATE_WITHOUT_TABLE ); + if( !food.rotten() ) { + add_morale( MORALE_ATE_WITH_TABLE, 3, 3, 3_hours, 2_hours, true ); } } } From 10dfb049d033f4d6d063dffa3be5b7664e819069 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:26:49 -0800 Subject: [PATCH 095/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index e097af75c295d..0963a0fbf7b78 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1273,7 +1273,8 @@ void Character::modify_addiction( const islot_comestible &comest ) rem_morale( add.first->get_craving_morale() ); } } - +} + add_addiction( add.first, add.second ); void Character::modify_morale( item &food, const int nutr ) { time_duration morale_time = 2_hours; From fc81636c5f432678869928cc48bcd1b5edd38bda Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:26:59 -0800 Subject: [PATCH 096/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 0963a0fbf7b78..9d8cc435c4c5e 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1259,7 +1259,8 @@ ret_val Character::will_eat( const item &food, bool interactive ) void Character::modify_fatigue( const islot_comestible & comest ) { mod_fatigue( -comest.fatigue_mod ); } - +} + } void Character::modify_fatigue( const islot_comestible &comest ) { mod_fatigue( -comest.fatigue_mod ); From 8edcec986df9918c5790f6cacf4b9eb81615e0c3 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:34:57 -0800 Subject: [PATCH 097/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 9d8cc435c4c5e..205137f357204 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1536,18 +1536,15 @@ void Character::modify_morale( item &food, const int nutr ) return false; } - const islot_comestible &comest = *food.get_comestible(); - - // Rotten food causes health loss - const float relative_rot = food.get_relative_rot(); - if( relative_rot > 1.0f && !has_flag( json_flag_IMMUNE_SPOIL ) ) { - const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f ); - // ~-1 health per 1 nutrition at halfway-rotten-away, ~0 at "just got rotten" - // But always round down - int h_loss = -rottedness * comest.get_default_nutr(); - mod_daily_health( h_loss, -200 ); - add_msg_debug( debugmode::DF_FOOD, "%d health from %0.2f%% rotten food", h_loss, rottedness ); - } + if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { + // Was used to cap nutrition and thirst, but no longer does this + return false; + } + if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && + food.has_any_flag( herbivore_blacklist ) ) { + // No good can come of this. + return false; + } // Used in hibernation messages. const int nutr = nutrition_for( food ); From 8a6005c2808f9b89191664bbaa89d630779fefaf Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:35:09 -0800 Subject: [PATCH 098/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 205137f357204..75e1af4e6fbaf 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1526,15 +1526,11 @@ void Character::modify_morale( item &food, const int nutr ) return false; } - if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { - // Was used to cap nutrition and thirst, but no longer does this - return false; - } - if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && - food.has_any_flag( herbivore_blacklist ) ) { - // No good can come of this. - return false; - } + bool Character::consume_effects( item & food ) { + if( !food.is_comestible() ) { + debugmsg( "called Character::consume_effects with non-comestible" ); + return false; + } if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { // Was used to cap nutrition and thirst, but no longer does this From f955fc7b60bf3264223a12ba097bdc3fc8429cbd Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:35:32 -0800 Subject: [PATCH 099/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 75e1af4e6fbaf..96e99b04413dc 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1520,11 +1520,20 @@ void Character::modify_morale( item &food, const int nutr ) } } - bool Character::consume_effects( item & food ) { - if( !food.is_comestible() ) { - debugmsg( "called Character::consume_effects with non-comestible" ); - return false; - } + static void activate_consume_eocs( Character & you, item & target ) { + Character *char_ptr = nullptr; + if( avatar *u = you.as_avatar() ) { + char_ptr = u; + } else if( npc *n = you.as_npc() ) { + char_ptr = n; + } + item_location loc( you, &target ); + dialogue d( get_talker_for( char_ptr ), get_talker_for( loc ) ); + const islot_comestible &comest = *target.get_comestible(); + for( const effect_on_condition_id &eoc : comest.consumption_eocs ) { + eoc->activate( d ); + } + } bool Character::consume_effects( item & food ) { if( !food.is_comestible() ) { From c44d83e701a884099a596c45f0d653661d8a0eb7 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:35:45 -0800 Subject: [PATCH 100/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 96e99b04413dc..5848e047c9838 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1505,20 +1505,26 @@ void Character::modify_morale( item &food, const int nutr ) return std::round( kcalories / effective_volume ); } - static void activate_consume_eocs( Character & you, item & target ) { - Character *char_ptr = nullptr; - if( avatar *u = you.as_avatar() ) { - char_ptr = u; - } else if( npc *n = you.as_npc() ) { - char_ptr = n; - } - item_location loc( you, &target ); - dialogue d( get_talker_for( char_ptr ), get_talker_for( loc ) ); - const islot_comestible &comest = *target.get_comestible(); - for( const effect_on_condition_id &eoc : comest.consumption_eocs ) { - eoc->activate( d ); - } - } + // Used when displaying effective food satiation values. + int Character::compute_calories_per_effective_volume( const item & food, + const nutrients * nutrient /* = nullptr */ )const { + /* Understanding how Calories Per Effective Volume are calculated requires a dive into the + stomach fullness source code. Look at issue #44365*/ + int kcalories; + if( nutrient ) { + // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). + kcalories = nutrient->kcal(); + } else { + kcalories = compute_effective_nutrients( food ).kcal(); + } + double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); + const double energy_density_ratio = compute_effective_food_volume_ratio( food ); + const double effective_volume = food_vol * energy_density_ratio; + if( kcalories == 0 && effective_volume == 0.0 ) { + return 0; + } + return std::round( kcalories / effective_volume ); + } static void activate_consume_eocs( Character & you, item & target ) { Character *char_ptr = nullptr; From e36e959462838ce5439cc777ab2297c5aa700f13 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:37:07 -0800 Subject: [PATCH 101/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 5848e047c9838..a9c60f35412e6 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1150,10 +1150,29 @@ ret_val Character::will_eat( const item &food, bool interactive ) } } - if( item::find_type( food.get_comestible()->tool )->tool ) { - // Tools like lighters get used - you.use_charges( food.get_comestible()->tool, 1 ); - } + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { + + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { + + add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); + add_msg( _( "You eat your %s (rotten)." ), food.tname() ); + } else { + you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + food.tname() ); + } + } if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { From 3f8bc890371fbb05cc3fbe25fba2d47f8b3276f4 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:37:16 -0800 Subject: [PATCH 102/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index a9c60f35412e6..d31231b00e7f1 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1179,9 +1179,10 @@ ret_val Character::will_eat( const item &food, bool interactive ) you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); } - if( food.has_flag( flag_FUNGAL_VECTOR ) && !you.has_trait( trait_M_IMMUNE ) ) { - you.add_effect( effect_fungus, 1_turns, true ); - } + if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && + you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { + you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); + } // The fun changes for these effects are applied in fun_for(). if( food.has_flag( flag_MUSHY ) ) { From 277ffb4148b6f916fd7e518cae3e212f9e63a2a6 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:37:56 -0800 Subject: [PATCH 103/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 58 +++++---------------------------------------- 1 file changed, 6 insertions(+), 52 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index d31231b00e7f1..de4b1ca2950d3 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1338,58 +1338,12 @@ void Character::modify_morale( item &food, const int nutr ) } } - const bool food_is_human_flesh = food.has_flag( flag_CANNIBALISM ) || - ( food.has_flag( flag_STRICT_HUMANITARIANISM ) && - !has_flag( json_flag_STRICT_HUMANITARIAN ) ); - if( food_is_human_flesh ) { - // Sapiovores don't recognize humans as the same species. - // But let them possibly feel cool about eating sapient stuff - treat like psycho - // However, spiritual sapiovores should still recognize humans as having a soul or special for religious reasons - // Hemovores feel weird about human blood, bloodfeeders don't care unless they're also cannibals or w/e - const bool cannibal = has_flag( json_flag_CANNIBAL ); - const bool psycho = has_flag( json_flag_PSYCHOPATH ); - const bool sapiovore = has_flag( json_flag_SAPIOVORE ); - const bool spiritual = has_flag( json_flag_SPIRITUAL ); - const bool numb = has_flag( json_flag_NUMB ); - const bool bloodfeeder = has_flag( json_flag_BLOODFEEDER ); - if( cannibal && psycho && spiritual ) { - add_msg_if_player( m_good, - _( "You feast upon the human flesh, and in doing so, devour their spirit." ) ); - // You're not really consuming anything special; you just think you are. - add_morale( MORALE_CANNIBAL, 25, 300 ); - } else if( cannibal && psycho ) { - add_msg_if_player( m_good, _( "You feast upon the human flesh." ) ); - add_morale( MORALE_CANNIBAL, 15, 200 ); - } else if( cannibal && spiritual ) { - add_msg_if_player( m_good, _( "You consume the sacred human flesh." ) ); - // Boosted because you understand the philosophical implications of your actions, and YOU LIKE THEM. - add_morale( MORALE_CANNIBAL, 15, 200 ); - } else if( sapiovore && spiritual ) { - add_msg_if_player( m_good, _( "You eat the human flesh, and in doing so, devour their spirit." ) ); - add_morale( MORALE_CANNIBAL, 10, 50 ); - } else if( cannibal ) { - add_msg_if_player( m_good, _( "You indulge your shameful hunger." ) ); - add_morale( MORALE_CANNIBAL, 10, 50 ); - } else if( psycho && spiritual ) { - add_msg_if_player( _( "You greedily devour the taboo meat." ) ); - // Small bonus for violating a taboo. - add_morale( MORALE_CANNIBAL, 5, 50 ); - } else if( psycho ) { - add_msg_if_player( _( "Meh. You've eaten worse." ) ); - } else if( sapiovore ) { - add_msg_if_player( _( "Mmh. Tastes like venison." ) ); - } else if( spiritual ) { - add_msg_if_player( m_bad, - _( "This is probably going to count against you if there's still an afterlife." ) ); - add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); - } else if( numb ) { - add_msg_if_player( m_bad, _( "You find this meal distasteful, but necessary." ) ); - add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); - } else if( bloodfeeder && food.has_flag( flag_HEMOVORE_FUN ) ) { - add_msg_if_player( _( "The human blood is as sweet as any other." ) ); - } else { - add_msg_if_player( m_bad, _( "You feel horrible for eating a person." ) ); - add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); + void Character::modify_addiction( const islot_comestible & comest ) { + for( const std::pair &add : comest.addictions ) { + add_addiction( add.first, add.second ); + if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { + rem_morale( add.first->get_craving_morale() ); + } } } From d301e27b86937bed7377b7bf5964e22276752e02 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:38:16 -0800 Subject: [PATCH 104/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index de4b1ca2950d3..73ba233decf48 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1590,12 +1590,6 @@ void Character::modify_morale( item &food, const int nutr ) slime->friendly = -1; } } - mod_hunger( 40 ); - mod_thirst( 40 ); - //~ slimespawns have *small voices* which may be the Nice equivalent - //~ of the Rat King's ALL CAPS invective. Probably shared-brain telepathy. - add_msg_if_player( m_good, _( "hey, you look like me! let's work together!" ) ); - } nutrients food_nutrients = compute_effective_nutrients( food ); const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? From 03642aac1a987c9c3cbff043f3c1567c1a458352 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:38:47 -0800 Subject: [PATCH 105/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 73ba233decf48..70656078b8aab 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1246,12 +1246,8 @@ ret_val Character::will_eat( const item &food, bool interactive ) return true; } - void Character::modify_health( const islot_comestible & comest ) { - const int effective_health = comest.healthy; - // Effectively no cap on health modifiers from food and meds - const int health_cap = 200; - mod_daily_health( effective_health, effective_health >= 0 ? health_cap : -health_cap ); - } + return true; + } void Character::modify_stimulation( const islot_comestible & comest ) { if( comest.stim == 0 ) { From 488f117b39a0c4445bde4ffb6041f5ef89c710fb Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:38:55 -0800 Subject: [PATCH 106/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 70656078b8aab..6667d3cc02869 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1325,13 +1325,8 @@ void Character::modify_morale( item &food, const int nutr ) } } } - - if( food.has_flag( flag_HIDDEN_HALLU ) ) { - if( has_trait( trait_SPIRITUAL ) ) { - add_morale( MORALE_FOOD_GOOD, 36, 72, 2_hours, 1_hours, false ); - } else { - add_morale( MORALE_FOOD_GOOD, 18, 36, 1_hours, 30_minutes, false ); - } + void Character::modify_fatigue( const islot_comestible & comest ) { + mod_fatigue( -comest.fatigue_mod ); } void Character::modify_addiction( const islot_comestible & comest ) { From 7ea3dcfe5e2d8be7b30c6e59e82b9a01c28377fc Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:39:28 -0800 Subject: [PATCH 107/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 6667d3cc02869..206669cfdbd6e 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1609,8 +1609,15 @@ void Character::modify_morale( item &food, const int nutr ) // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? activate_consume_eocs( *this, food ); - // GET IN MAH BELLY! - stomach.ingest( ingested ); + if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { + // Was used to cap nutrition and thirst, but no longer does this + return false; + } + if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && + food.has_any_flag( herbivore_blacklist ) ) { + // No good can come of this. + return false; + } // update speculative values if( is_avatar() ) { From 90e43baba0bed8cf23575382794804bdd494393e Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:39:38 -0800 Subject: [PATCH 108/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 206669cfdbd6e..dc85f20992d97 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1619,15 +1619,36 @@ void Character::modify_morale( item &food, const int nutr ) return false; } - // update speculative values - if( is_avatar() ) { - get_avatar().add_ingested_kcal( ingested.nutr.calories / 1000 ); - } - for( const auto &v : ingested.nutr.vitamins() ) { - // update the estimated values for daily vitamins - // actual vitamins happen during digestion - daily_vitamins[v.first].first += v.second; - } + // Used in hibernation messages. + const int nutr = nutrition_for( food ); + const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; + // We can handle junk just fine + if( !skip_health ) { + modify_health( comest ); + } + modify_stimulation( comest ); + modify_fatigue( comest ); + modify_addiction( comest ); + modify_morale( food, nutr ); + + const bool hibernate = has_active_mutation( trait_HIBERNATE ); + if( hibernate ) { + if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { + // Tell the player what's going on + add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); + if( one_in( 2 ) ) { + // 50% chance of the food tiring you + mod_fatigue( nutr ); + } + } + if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { + // Hibernation should cut burn to 60/day + add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); + if( one_in( 2 ) ) { + // And another 50%, intended cumulative + mod_fatigue( nutr ); + } + } return true; } From 48e28f30f8d96c8a89b33ca4651fc39dfb65498c Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:40:18 -0800 Subject: [PATCH 109/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 100 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index dc85f20992d97..2423f70796906 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1198,20 +1198,92 @@ ret_val Character::will_eat( const item &food, bool interactive ) } } - // Chance to become parasitised - if( !will_vomit && !you.has_flag( json_flag_PARAIMMUNE ) ) { - if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && - one_in( food.get_comestible()->parasites ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || - ( !you.has_flag( json_flag_HEMOVORE ) && !you.has_flag( json_flag_BLOODFEEDER ) ) ) ) { - switch( rng( 0, 3 ) ) { - case 0: - if( !you.has_trait( trait_EATHEALTH ) ) { - you.add_effect( effect_tapeworm, 1_turns, true ); - } - break; - case 1: - if( !you.has_trait( trait_ACIDBLOOD ) ) { - you.add_effect( effect_bloodworms, 1_turns, true ); + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { + + add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); + add_msg( _( "You eat your %s (rotten)." ), food.tname() ); + } else { + you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + food.tname() ); + } + } + + if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && + you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { + you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); + } + + if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && + you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { + you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); + } + + // The fun changes for these effects are applied in fun_for(). + if( food.has_flag( flag_MUSHY ) ) { + you.add_msg_if_player( m_bad, + _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); + } + if( food.get_comestible_fun() > 0 ) { + if( you.has_effect( effect_common_cold ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); + } + if( you.has_effect( effect_flu ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); + } + } + + // Chance to become parasitised + if( !will_vomit && !you.has_flag( json_flag_PARAIMMUNE ) ) { + if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && + one_in( food.get_comestible()->parasites ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || + ( !you.has_flag( json_flag_HEMOVORE ) && !you.has_flag( json_flag_BLOODFEEDER ) ) ) ) { + switch( rng( 0, 3 ) ) { + case 0: + if( !you.has_trait( trait_EATHEALTH ) ) { + you.add_effect( effect_tapeworm, 1_turns, true ); + } + break; + case 1: + if( !you.has_trait( trait_ACIDBLOOD ) ) { + you.add_effect( effect_bloodworms, 1_turns, true ); + } + break; + case 2: + you.add_effect( effect_brainworms, 1_turns, true ); + break; + case 3: + you.add_effect( effect_paincysts, 1_turns, true ); + } + } + } + + for( const std::pair &elem : food.get_comestible()->contamination ) { + if( rng( 1, 100 ) <= elem.second ) { + you.expose_to_disease( elem.first ); + } + } + + get_event_bus().send( you.getID(), food.typeId() ); + + if( will_vomit ) { + you.vomit(); + } + + you.consumption_history.emplace_back( food ); + // Clean out consumption_history so it doesn't get bigger than needed. + while( you.consumption_history.front().time < calendar::turn - 2_days ) { + you.consumption_history.pop_front(); + } + + you.recoil = MAX_RECOIL; + + return true; } break; case 2: From 6d435c68a9418a99c8ffd9a78a030a76eee42500 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:40:28 -0800 Subject: [PATCH 110/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 50 ++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 2423f70796906..9551465155594 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1410,34 +1410,28 @@ void Character::modify_morale( item &food, const int nutr ) } } - // While raw flesh usually means negative morale, carnivores and cullers get a small bonus. - // Hunters, predators, and apex predators don't mind raw flesh at all, maybe even like it. - // Cooked flesh is unaffected, because people with these traits *prefer* it raw. Fat is unaffected. - // Organs are still usually negative due to fun values as low as -35. - // The PREDATOR_FUN flag shouldn't be on human flesh, to not interfere with sapiovores/cannibalism. - if( food.has_flag( flag_PREDATOR_FUN ) ) { - const bool carnivore = has_trait( trait_CARNIVORE ); - const bool culler = has_flag( json_flag_PRED1 ); - const bool hunter = has_flag( json_flag_PRED2 ); - const bool predator = has_flag( json_flag_PRED3 ); - const bool apex_predator = has_flag( json_flag_PRED4 ); - if( apex_predator ) { - // Largest bonus, balances out to around +5 or +10. Some organs may still be negative. - add_morale( MORALE_MEATARIAN, 20, 10 ); - add_msg_if_player( m_good, - _( "As you tear into the raw flesh, you feel satisfied with your meal." ) ); - } else if( predator || hunter ) { - // Should approximately balance the fun to 0 for normal meat. - add_morale( MORALE_MEATARIAN, 15, 5 ); - add_msg_if_player( m_good, - _( "Raw flesh doesn't taste all that bad, actually." ) ); - } else if( carnivore || culler ) { - // Only a small bonus (+5), still negative fun. - add_morale( MORALE_MEATARIAN, 5, 0 ); - add_msg_if_player( m_bad, - _( "This doesn't taste very good, but meat is meat." ) ); - } - } + void Character::modify_stimulation( const islot_comestible & comest ) { + if( comest.stim == 0 ) { + return; + } + const int current_stim = get_stim(); + if( ( std::abs( comest.stim ) * 3 ) > std::abs( current_stim ) ) { + mod_stim( comest.stim ); + } else { + comest.stim > 0 ? mod_stim( std::max( comest.stim / 2, 1 ) ) : mod_stim( std::min( comest.stim / 2, + -1 ) ); + } + if( has_trait( trait_STIMBOOST ) && ( current_stim > 30 ) && + ( comest.addictions.count( STATIC( addiction_id( "caffeine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "amphetamine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "cocaine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "crack" ) ) ) ) ) { + int hallu_duration = ( current_stim - comest.stim < 30 ) ? current_stim - 30 : comest.stim; + add_effect( effect_visuals, hallu_duration * 30_minutes ); + add_msg_if_player( m_bad, SNIPPET.random_from_category( "comest_stimulant" ).value_or( + translation() ).translated() ); + } + } // Allergy check for food that is ingested (not gum) if( !food.has_flag( flag_NO_INGEST ) ) { From 22f0dd2ad6df9db89ce0d5a553c0cd52dd3c8e80 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:40:41 -0800 Subject: [PATCH 111/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 119 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 17 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 9551465155594..dfa266fdbc4b1 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1870,23 +1870,108 @@ void Character::modify_morale( item &food, const int nutr ) } } - // TODO: Get the target it was used on - // Otherwise injecting someone will give us addictions etc. - if( target.has_flag( flag_NO_INGEST ) ) { - const islot_comestible &comest = *target.get_comestible(); - // Assume that parenteral meds don't spoil, so don't apply rot - you.modify_health( comest ); - you.modify_stimulation( comest ); - you.modify_fatigue( comest ); - you.modify_addiction( comest ); - you.modify_morale( target ); - activate_consume_eocs( you, target ); - } else { - // Take by mouth - if( !you.consume_effects( target ) ) { - activate_consume_eocs( you, target ); - } - } + // Used in hibernation messages. + const int nutr = nutrition_for( food ); + const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; + // We can handle junk just fine + if( !skip_health ) { + modify_health( comest ); + } + modify_stimulation( comest ); + modify_fatigue( comest ); + modify_addiction( comest ); + modify_morale( food, nutr ); + + const bool hibernate = has_active_mutation( trait_HIBERNATE ); + if( hibernate ) { + if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { + // Tell the player what's going on + add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); + if( one_in( 2 ) ) { + // 50% chance of the food tiring you + mod_fatigue( nutr ); + } + } + if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { + // Hibernation should cut burn to 60/day + add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); + if( one_in( 2 ) ) { + // And another 50%, intended cumulative + mod_fatigue( nutr ); + } + } + + if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { + add_msg_if_player( + _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); + if( !one_in( 3 ) ) { + // Third check, this one at 66% + mod_fatigue( nutr ); + } + } + if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { + add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); + // At this point, you're done. Schlaf gut. + mod_fatigue( nutr ); + } + } + // Moved here and changed a bit - it was too complex + // Incredibly minor stuff like this shouldn't require complexity + if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && + ( get_healthy_kcal() < get_stored_kcal() + 4000 && + get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { + add_msg_if_player( m_mixed, + _( "You feel as though you're going to split open! In a good way?" ) ); + mod_pain( 5 ); + int numslime = 1; + for( int i = 0; i < numslime; i++ ) { + if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { + slime->friendly = -1; + } + } + + nutrients food_nutrients = compute_effective_nutrients( food ); + const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? + food.get_comestible()->quench * + 5_ml : 0_ml; + units::volume food_vol = masticated_volume( food ); + if( food.count() == 0 ) { + debugmsg( "Tried to eat food with count of zero." ); + return false; + } + units::mass food_weight = ( food.weight() / food.count() ); + const double ratio = compute_effective_food_volume_ratio( food ); + food_summary ingested{ + water_vol, + food_vol * ratio, + food_nutrients + }; + add_msg_debug( debugmode::DF_FOOD, + "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", + units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, + food_nutrients.kcal(), units::to_gram( food_weight ) ); + // Maybe move tapeworm to digestion + if( has_effect( effect_tapeworm ) ) { + ingested.nutr /= 2; + } + // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? + activate_consume_eocs( *this, food ); + + // GET IN MAH BELLY! + stomach.ingest( ingested ); + + // update speculative values + if( is_avatar() ) { + get_avatar().add_ingested_kcal( ingested.nutr.calories / 1000 ); + } + for( const auto &v : ingested.nutr.vitamins() ) { + // update the estimated values for daily vitamins + // actual vitamins happen during digestion + daily_vitamins[v.first].first += v.second; + } + + return true; + } if( target.count_by_charges() ) { target.mod_charges( -amount_used ); From 0a9ca2aff91cffb1e7ba6bd7b3c5af5132b9fcc4 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:40:51 -0800 Subject: [PATCH 112/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index dfa266fdbc4b1..51e05d4036f3f 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -2000,7 +2000,66 @@ void Character::modify_morale( item &food, const int nutr ) return trinary::NONE; } - if( consume_med( target, *this ) || eat( target, *this, force ) ) { + time_duration Character::get_consume_time( const item & it ) const { + const int charges = std::max( it.charges, 1 ); + int volume = units::to_milliliter( it.volume() ) / charges; + if( 0 == volume && it.type ) { + volume = units::to_milliliter( it.type->volume ); + } + time_duration time = time_duration::from_seconds( std::max( ( volume / + 5 ), 1 ) ); //Default 5 mL (1 tablespoon) per second + float consume_time_modifier = 1.0f;//only for food and drinks + const bool eat_verb = it.has_flag( flag_USE_EAT_VERB ); + const std::string comest_type = it.get_comestible() ? it.get_comestible()->comesttype : ""; + if( eat_verb || comest_type == "FOOD" ) { + time = time_duration::from_seconds( volume / 5 ); //Eat 5 mL (1 teaspoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } else if( !eat_verb && comest_type == "DRINK" ) { + time = time_duration::from_seconds( volume / 15 ); //Drink 15 mL (1 tablespoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } else if( use_function const *fun = it.type->get_use( "heal" ) ) { + time = time_duration::from_moves( dynamic_cast + ( fun->get_actor_ptr() )->move_cost ); + } else if( it.is_medication() ) { + const use_function *consume_drug = it.type->get_use( "consume_drug" ); + const use_function *smoking = it.type->get_use( "SMOKING" ); + const use_function *adrenaline_injector = it.type->get_use( "ADRENALINE_INJECTOR" ); + if( consume_drug != nullptr ) { //its a drug + const consume_drug_iuse *consume_drug_use = dynamic_cast + ( consume_drug->get_actor_ptr() ); + if( consume_drug_use->tools_needed.find( itype_syringe ) != consume_drug_use->tools_needed.end() && + has_bionic( bio_syringe ) ) { + time = time_duration::from_seconds( + 15 );//injections with the intradermal needle CBM are much quicker than with a normal syringe + } else if( consume_drug_use->tools_needed.find( itype_syringe ) != + consume_drug_use->tools_needed.end() ) { + time = time_duration::from_minutes( 5 );//sterile injections take 5 minutes + } else if( consume_drug_use->tools_needed.find( itype_apparatus ) != + consume_drug_use->tools_needed.end() || + consume_drug_use->tools_needed.find( itype_dab_pen_on ) != consume_drug_use->tools_needed.end() ) { + time = time_duration::from_seconds( 30 );//smoke a bowl + } else { + time = time_duration::from_seconds( 5 );//popping a pill is quick + } + } else if( smoking != nullptr ) { + time = time_duration::from_minutes( 1 );//about five minutes for a cig or joint so 1 minute a charge + } else if( adrenaline_injector != nullptr ) { + //epi-pens, and disinfectant are fairly quick + time = time_duration::from_seconds( 15 ); + } else { + time = time_duration::from_seconds( 5 ); //probably pills so quick + } + } else if( it.get_category_shallow().get_id() == item_category_chems ) { + time = time_duration::from_seconds( std::max( ( volume / 15 ), + 1 ) ); //Consume 15 mL (1 tablespoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } + + // Minimum consumption time, without mutations, is always 1 second. + time = std::max( 1_seconds, time ); + + return time * consume_time_modifier; + } get_event_bus().send( getID(), target.typeId() ); From 54eab51e26119ca98ab349b4bcec4f1c23a0dc67 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:41:04 -0800 Subject: [PATCH 113/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 51e05d4036f3f..23be4a87364b5 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1989,16 +1989,25 @@ void Character::modify_morale( item &food, const int nutr ) return trinary::NONE; } - if( target.is_craft() ) { - add_msg_if_player( m_info, _( "You can't eat your %s." ), target.tname() ); - if( is_npc() ) { - debugmsg( "%s tried to eat a %s", get_name(), target.tname() ); - } - return trinary::NONE; - } - if( is_avatar() && !query_consume_ownership( target, *this ) ) { - return trinary::NONE; - } + item &Character::get_consumable_from( item & it ) const { + item *ret = nullptr; + it.visit_items( [&]( item * it, item * ) { + if( can_consume_as_is( *it ) ) { + ret = it; + return VisitResponse::ABORT; + } + return VisitResponse::NEXT; + } ); + + if( ret != nullptr ) { + return *ret; + } + + static item null_comestible; + // Since it's not const. + null_comestible = item(); + return null_comestible; + } time_duration Character::get_consume_time( const item & it ) const { const int charges = std::max( it.charges, 1 ); From 7cdae4723f34763930a182e5367fa10a5138c774 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:41:58 -0800 Subject: [PATCH 114/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 23be4a87364b5..65b0d9bf39cd2 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1174,10 +1174,10 @@ ret_val Character::will_eat( const item &food, bool interactive ) } } - if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && - you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { - you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); - } + if( item::find_type( food.get_comestible()->tool )->tool ) { + // Tools like lighters get used + you.use_charges( food.get_comestible()->tool, 1 ); + } if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { From c499612607d236b22707418b3a1858bf3431abe2 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:42:52 -0800 Subject: [PATCH 115/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 65b0d9bf39cd2..ba4ecc6e46faa 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1179,10 +1179,13 @@ ret_val Character::will_eat( const item &food, bool interactive ) you.use_charges( food.get_comestible()->tool, 1 ); } - if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && - you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { - you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); - } + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { // The fun changes for these effects are applied in fun_for(). if( food.has_flag( flag_MUSHY ) ) { From 3b6a59c463e2cab513fae7f156fefe64da62c9f3 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:43:09 -0800 Subject: [PATCH 116/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index ba4ecc6e46faa..3ef68a73326f3 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1813,8 +1813,8 @@ void Character::modify_morale( item &food, const int nutr ) // Minimum consumption time, without mutations, is always 1 second. time = std::max( 1_seconds, time ); - return time * consume_time_modifier; - } + // GET IN MAH BELLY! + stomach.ingest( ingested ); static bool query_consume_ownership( item & target, Character & p ) { if( !target.is_owned_by( p, true ) ) { From ebd8ec14d9d51bdf97fee48c446e25abe6bcb2cf Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:43:32 -0800 Subject: [PATCH 117/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 3ef68a73326f3..d0c74a97da3eb 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1810,8 +1810,32 @@ void Character::modify_morale( item &food, const int nutr ) consume_time_modifier = mutation_value( "consume_time_modifier" ); } - // Minimum consumption time, without mutations, is always 1 second. - time = std::max( 1_seconds, time ); + nutrients food_nutrients = compute_effective_nutrients( food ); + const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? + food.get_comestible()->quench * + 5_ml : 0_ml; + units::volume food_vol = masticated_volume( food ); + if( food.count() == 0 ) { + debugmsg( "Tried to eat food with count of zero." ); + return false; + } + units::mass food_weight = ( food.weight() / food.count() ); + const double ratio = compute_effective_food_volume_ratio( food ); + food_summary ingested{ + water_vol, + food_vol * ratio, + food_nutrients + }; + add_msg_debug( debugmode::DF_FOOD, + "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", + units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, + food_nutrients.kcal(), units::to_gram( food_weight ) ); + // Maybe move tapeworm to digestion + if( has_effect( effect_tapeworm ) ) { + ingested.nutr /= 2; + } + // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? + activate_consume_eocs( *this, food ); // GET IN MAH BELLY! stomach.ingest( ingested ); From d681b19285cce3b5e880c0c57663a419d38a485c Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:44:13 -0800 Subject: [PATCH 118/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index d0c74a97da3eb..7af6daf2a7960 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1719,8 +1719,40 @@ void Character::modify_morale( item &food, const int nutr ) } } - return true; - } + if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { + add_msg_if_player( + _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); + if( !one_in( 3 ) ) { + // Third check, this one at 66% + mod_fatigue( nutr ); + } + } + if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { + add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); + // At this point, you're done. Schlaf gut. + mod_fatigue( nutr ); + } + } + // Moved here and changed a bit - it was too complex + // Incredibly minor stuff like this shouldn't require complexity + if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && + ( get_healthy_kcal() < get_stored_kcal() + 4000 && + get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { + add_msg_if_player( m_mixed, + _( "You feel as though you're going to split open! In a good way?" ) ); + mod_pain( 5 ); + int numslime = 1; + for( int i = 0; i < numslime; i++ ) { + if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { + slime->friendly = -1; + } + } + mod_hunger( 40 ); + mod_thirst( 40 ); + //~ slimespawns have *small voices* which may be the Nice equivalent + //~ of the Rat King's ALL CAPS invective. Probably shared-brain telepathy. + add_msg_if_player( m_good, _( "hey, you look like me! let's work together!" ) ); + } bool Character::can_estimate_rot() const { return get_greater_skill_or_knowledge_level( skill_cooking ) >= 3 || From bfbacf22f4d91ef1bf0997def24815c89350daeb Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:45:17 -0800 Subject: [PATCH 119/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 7af6daf2a7960..77d334fa0d45c 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1174,10 +1174,13 @@ ret_val Character::will_eat( const item &food, bool interactive ) } } - if( item::find_type( food.get_comestible()->tool )->tool ) { - // Tools like lighters get used - you.use_charges( food.get_comestible()->tool, 1 ); - } + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { if( amorphous ) { you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), From 8d4697037dd517630361bd90619467303220b285 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:45:29 -0800 Subject: [PATCH 120/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 77d334fa0d45c..16299f4b61ac3 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1190,19 +1190,18 @@ ret_val Character::will_eat( const item &food, bool interactive ) !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && you.is_avatar() ) { - // The fun changes for these effects are applied in fun_for(). - if( food.has_flag( flag_MUSHY ) ) { - you.add_msg_if_player( m_bad, - _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); - } - if( food.get_comestible_fun() > 0 ) { - if( you.has_effect( effect_common_cold ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); - } - if( you.has_effect( effect_flu ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); - } - } + add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); + add_msg( _( "You eat your %s (rotten)." ), food.tname() ); + } else { + you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + food.tname() ); + } + } + + if( item::find_type( food.get_comestible()->tool )->tool ) { + // Tools like lighters get used + you.use_charges( food.get_comestible()->tool, 1 ); + } if( amorphous ) { you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), From f218f66dac038e2dd92367a88a0241c68ef67834 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:45:38 -0800 Subject: [PATCH 121/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 16299f4b61ac3..b29580672e09b 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1219,10 +1219,21 @@ ret_val Character::will_eat( const item &food, bool interactive ) } } - if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && - you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { - you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); - } + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { + + add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); + add_msg( _( "You eat your %s (rotten)." ), food.tname() ); + } else { + you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + food.tname() ); + } + } if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { From 9e46424ba824fbf5ad5e59fbba822f2b3b9e57cf Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:45:49 -0800 Subject: [PATCH 122/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/consumption.cpp b/src/consumption.cpp index b29580672e09b..2a0d4fc8f2a11 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1475,6 +1475,7 @@ void Character::modify_morale( item &food, const int nutr ) add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); } + add_morale( MORALE_HONEY, honey_fun, 100 ); } } const bool chew = food.get_comestible()->comesttype == comesttype_FOOD || From 1ee1fcd1c7cb352e3e25edf12607061972b52fb1 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:46:03 -0800 Subject: [PATCH 123/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 2a0d4fc8f2a11..6284a0ef269d4 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1485,12 +1485,37 @@ void Character::modify_morale( item &food, const int nutr ) add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); add_morale( MORALE_NO_DIGEST, -75, -400, 30_minutes, 24_minutes ); } - if( food.has_flag( flag_URSINE_HONEY ) && ( !crossed_threshold() || - has_trait( trait_THRESH_URSINE ) ) && - mutation_category_level[mutation_category_URSINE] > 20 ) { - int honey_fun = std::min( mutation_category_level[mutation_category_URSINE] / 5, 20 ); - if( honey_fun < 10 ) { - add_msg_if_player( m_good, _( "You find the sweet taste of honey surprisingly palatable." ) ); + + // Used when displaying effective food satiation values. + int Character::compute_calories_per_effective_volume( const item & food, + const nutrients * nutrient /* = nullptr */ )const { + /* Understanding how Calories Per Effective Volume are calculated requires a dive into the + stomach fullness source code. Look at issue #44365*/ + int kcalories; + if( nutrient ) { + // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). + kcalories = nutrient->kcal(); + } else { + kcalories = compute_effective_nutrients( food ).kcal(); + } + double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); + const double energy_density_ratio = compute_effective_food_volume_ratio( food ); + const double effective_volume = food_vol * energy_density_ratio; + if( kcalories == 0 && effective_volume == 0.0 ) { + return 0; + } + return std::round( kcalories / effective_volume ); + } + + // Used when displaying effective food satiation values. + int Character::compute_calories_per_effective_volume( const item & food, + const nutrients * nutrient /* = nullptr */ )const { + /* Understanding how Calories Per Effective Volume are calculated requires a dive into the + stomach fullness source code. Look at issue #44365*/ + int kcalories; + if( nutrient ) { + // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). + kcalories = nutrient->kcal(); } else { add_msg_if_player( m_good, _( "You feast upon the sweet honey." ) ); } From c250988cd4c0b79ded18bfc0f37f9540b41e764b Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:46:12 -0800 Subject: [PATCH 124/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 6284a0ef269d4..9dc5c3f9416e5 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1519,7 +1519,13 @@ void Character::modify_morale( item &food, const int nutr ) } else { add_msg_if_player( m_good, _( "You feast upon the sweet honey." ) ); } - add_morale( MORALE_HONEY, honey_fun, 100 ); + double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); + const double energy_density_ratio = compute_effective_food_volume_ratio( food ); + const double effective_volume = food_vol * energy_density_ratio; + if( kcalories == 0 && effective_volume == 0.0 ) { + return 0; + } + return std::round( kcalories / effective_volume ); } } From f9c64e235579fedac219e04fd1c392ff9bec892c Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:46:46 -0800 Subject: [PATCH 125/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 9dc5c3f9416e5..2f22d9d5e8679 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1142,11 +1142,10 @@ ret_val Character::will_eat( const item &food, bool interactive ) !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && you.is_avatar() ) { - add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); - add_msg( _( "You eat your %s (rotten)." ), food.tname() ); - } else { - you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), - food.tname() ); + if( food.has_flag( flag_HIDDEN_HALLU ) ) { + if( !you.has_effect( effect_hallu ) ) { + you.add_effect( effect_hallu, 6_hours ); + } } } From bd6c0f966c612858b80f51dd9195ffdd4bcf7f5b Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:47:09 -0800 Subject: [PATCH 126/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 2f22d9d5e8679..09b8678e35f5a 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1210,13 +1210,19 @@ ret_val Character::will_eat( const item &food, bool interactive ) !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && you.is_avatar() ) { - add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); - add_msg( _( "You eat your %s (rotten)." ), food.tname() ); - } else { - you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), - food.tname() ); - } - } + // The fun changes for these effects are applied in fun_for(). + if( food.has_flag( flag_MUSHY ) ) { + you.add_msg_if_player( m_bad, + _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); + } + if( food.get_comestible_fun() > 0 ) { + if( you.has_effect( effect_common_cold ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); + } + if( you.has_effect( effect_flu ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); + } + } if( amorphous ) { you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), From 4ff6d3f1df78866fd861cd97d50c273898510d14 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:47:19 -0800 Subject: [PATCH 127/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 09b8678e35f5a..5e06c073df4f1 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1483,12 +1483,39 @@ void Character::modify_morale( item &food, const int nutr ) add_morale( MORALE_HONEY, honey_fun, 100 ); } } - const bool chew = food.get_comestible()->comesttype == comesttype_FOOD || - food.has_flag( flag_USE_EAT_VERB ); - if( !food.rotten() && chew && has_trait( trait_SAPROPHAGE ) ) { - // It's OK to *drink* things that haven't rotted. Alternative is to ban water. D: - add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); - add_morale( MORALE_NO_DIGEST, -75, -400, 30_minutes, 24_minutes ); + + // Used when determining stomach fullness from eating. + double Character::compute_effective_food_volume_ratio( const item & food ) const { + const nutrients food_nutrients = compute_effective_nutrients( food ); + units::mass food_weight = ( food.weight() / std::max( 1, food.count() ) ); + double ratio = 1.0f; + if( units::to_gram( food_weight ) != 0 ) { + ratio = std::max( static_cast( food_nutrients.kcal() ) / units::to_gram( food_weight ), + 1.0 ); + if( ratio > 3.0f ) { + ratio = std::sqrt( 3 * ratio ); + } + } + return ratio; + } + + // Remove the water volume from the food, as that gets absorbed and used as water. + // If the remaining dry volume of the food is less dense than water, crunch it down to a density equal to water. + // These maths are made easier by the fact that 1 g = 1 mL. Thanks, metric system. + units::volume Character::masticated_volume( const item & food ) const { + units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? food.get_comestible()->quench * + 5_ml : 0_ml; + units::mass water_weight = units::from_gram( units::to_milliliter( water_vol ) ); + // handle the division by zero exception when the food count is 0 with std::max() + units::mass food_dry_weight = food.weight() / std::max( 1, food.count() ) - water_weight; + units::volume food_dry_volume = food.volume() / std::max( 1, food.count() ) - water_vol; + + if( units::to_milliliter( food_dry_volume ) != 0 && + units::to_gram( food_dry_weight ) < units::to_milliliter( food_dry_volume ) ) { + food_dry_volume = units::from_milliliter( units::to_gram( food_dry_weight ) ); + } + + return food_dry_volume; } // Used when displaying effective food satiation values. From 3e432e4f366579719a3d22dfe7608461e131d580 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:47:30 -0800 Subject: [PATCH 128/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 5e06c073df4f1..25d2f2eea03aa 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1549,7 +1549,7 @@ void Character::modify_morale( item &food, const int nutr ) // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). kcalories = nutrient->kcal(); } else { - add_msg_if_player( m_good, _( "You feast upon the sweet honey." ) ); + kcalories = compute_effective_nutrients( food ).kcal(); } double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); const double energy_density_ratio = compute_effective_food_volume_ratio( food ); From d55d6f3fc7fa799ff6d3a2cdcfe0eaed4c57a347 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:47:39 -0800 Subject: [PATCH 129/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 25d2f2eea03aa..3a9a08c67c0db 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1240,10 +1240,10 @@ ret_val Character::will_eat( const item &food, bool interactive ) } } - if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && - you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { - you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); - } + if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && + you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { + you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); + } // The fun changes for these effects are applied in fun_for(). if( food.has_flag( flag_MUSHY ) ) { From 37e2cc84dd77b9ad0fd069be2b264c118823fe2f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:49:41 -0800 Subject: [PATCH 130/202] Update src/consumption.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/consumption.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 3a9a08c67c0db..cc530b1b3aa1a 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1466,11 +1466,24 @@ void Character::modify_morale( item &food, const int nutr ) add_msg_if_player( m_good, _( "Mmm, junk food." ) ); add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); } - if( has_trait( trait_PROJUNK2 ) ) { - if( !one_in( 100 ) ) { - add_msg_if_player( m_good, _( "When life's got you down, there's always sugar." ) ); - } else { - add_msg_if_player( m_good, _( "They may do what they must… you've already won." ) ); + if( food.has_flag( flag_ALLERGEN_JUNK ) ) { + if( has_trait( trait_PROJUNK ) ) { + add_msg_if_player( m_good, _( "Mmm, junk food." ) ); + add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); + } + if( has_trait( trait_PROJUNK2 ) ) { + if( !one_in( 100 ) ) { + add_msg_if_player( m_good, _( "When life's got you down, there's always sugar." ) ); + } else { + add_msg_if_player( m_good, _( "They may do what they must… you've already won." ) ); + } + add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes ); + } + // Carnivores CAN eat junk food, but they won't like it much. + // Pizza-scraping happens in consume_effects. + if( has_trait( trait_CARNIVORE ) && !food.has_flag( flag_CARNIVORE_OK ) ) { + add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); + add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); } add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes ); } From 479e2a5ab3185f73fde9e231703b8100bfc74528 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Thu, 8 Feb 2024 01:29:16 -0800 Subject: [PATCH 131/202] github what the hell --- src/consumption.cpp | 1909 +++++++++++++++++++++---------------------- 1 file changed, 953 insertions(+), 956 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index cc530b1b3aa1a..99c1d8bad2125 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -978,43 +978,43 @@ ret_val Character::will_eat( const item &food, bool interactive ) add_consequence( _( "Your stomach won't be happy (allergy)." ), ALLERGY ); } - if( saprophage && edible && !food.rotten() && !food.has_flag( flag_FERTILIZER ) ) { - // Note: We're allowing all non-solid "food". This includes drugs - // Hard-coding fertilizer for now - should be a separate flag later - //~ No, we don't eat "rotten" food. We eat properly aged food, like a normal person. - //~ Semantic difference, but greatly facilitates people being proud of their character. - add_consequence( _( "Your stomach won't be happy (not rotten enough)." ), ALLERGY_WEAK ); - } + if( saprophage && edible && !food.rotten() && !food.has_flag( flag_FERTILIZER ) ) { + // Note: We're allowing all non-solid "food". This includes drugs + // Hard-coding fertilizer for now - should be a separate flag later + //~ No, we don't eat "rotten" food. We eat properly aged food, like a normal person. + //~ Semantic difference, but greatly facilitates people being proud of their character. + add_consequence( _( "Your stomach won't be happy (not rotten enough)." ), ALLERGY_WEAK ); + } - if( food.is_food() && - ( food.charges_per_volume( stomach.stomach_remaining( *this ) ) < 1 || - has_effect( effect_hunger_full ) || has_effect( effect_hunger_engorged ) ) ) { - if( edible ) { - add_consequence( _( "You're full already and will be forcing yourself to eat." ), TOO_FULL ); - } else { - add_consequence( _( "You're full already and will be forcing yourself to drink." ), TOO_FULL ); - } + if( food.is_food() && + ( food.charges_per_volume( stomach.stomach_remaining( *this ) ) < 1 || + has_effect( effect_hunger_full ) || has_effect( effect_hunger_engorged ) ) ) { + if( edible ) { + add_consequence( _( "You're full already and will be forcing yourself to eat." ), TOO_FULL ); + } else { + add_consequence( _( "You're full already and will be forcing yourself to drink." ), TOO_FULL ); } + } - if( !consequences.empty() ) { - if( !interactive ) { - return consequences.front(); - } - std::string req; - for( const auto &elem : consequences ) { - req += elem.str() + "\n"; - } + if( !consequences.empty() ) { + if( !interactive ) { + return consequences.front(); + } + std::string req; + for( const auto &elem : consequences ) { + req += elem.str() + "\n"; + } - const bool eat_verb = food.has_flag( flag_USE_EAT_VERB ); - std::string food_tame = food.tname(); - const nc_color food_color = food.color_in_inventory(); - if( eat_verb || comest->comesttype == comesttype_FOOD ) { - req += string_format( _( "Eat your %s anyway?" ), colorize( food_tame, food_color ) ); - } else if( !eat_verb && comest->comesttype == comesttype_DRINK ) { - req += string_format( _( "Drink your %s anyway?" ), colorize( food_tame, food_color ) ); - } else { - req += string_format( _( "Consume your %s anyway?" ), colorize( food_tame, food_color ) ); - } + const bool eat_verb = food.has_flag( flag_USE_EAT_VERB ); + std::string food_tame = food.tname(); + const nc_color food_color = food.color_in_inventory(); + if( eat_verb || comest->comesttype == comesttype_FOOD ) { + req += string_format( _( "Eat your %s anyway?" ), colorize( food_tame, food_color ) ); + } else if( !eat_verb && comest->comesttype == comesttype_DRINK ) { + req += string_format( _( "Drink your %s anyway?" ), colorize( food_tame, food_color ) ); + } else { + req += string_format( _( "Consume your %s anyway?" ), colorize( food_tame, food_color ) ); + } if( !query_yn( req ) ) { return consequences.front(); @@ -1109,45 +1109,22 @@ ret_val Character::will_eat( const item &food, bool interactive ) you.add_effect( effect_poison, food.poison * 10_minutes ); } - // If it's poisonous... poison us. - // TODO: Move this to a flag - if( food.poison > 0 && - !you.has_trait( trait_EATDEAD ) ) { - if( food.poison >= rng( 2, 4 ) ) { - you.add_effect( effect_poison, food.poison * 10_minutes ); - } - - you.add_effect( effect_foodpoison, food.poison * 30_minutes ); - } - - if( food.has_flag( flag_HIDDEN_HALLU ) ) { - if( !you.has_effect( effect_hallu ) ) { - you.add_effect( effect_hallu, 6_hours ); - } - } - - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { + // If it's poisonous... poison us. + // TODO: Move this to a flag + if( food.poison > 0 && + !you.has_trait( trait_EATDEAD ) ) { + if( food.poison >= rng( 2, 4 ) ) { + you.add_effect( effect_poison, food.poison * 10_minutes ); + } - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { + you.add_effect( effect_foodpoison, food.poison * 30_minutes ); + } if( food.has_flag( flag_HIDDEN_HALLU ) ) { if( !you.has_effect( effect_hallu ) ) { you.add_effect( effect_hallu, 6_hours ); } } - } if( amorphous ) { you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), @@ -1165,114 +1142,169 @@ ret_val Character::will_eat( const item &food, bool interactive ) !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && you.is_avatar() ) { - add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); - add_msg( _( "You eat your %s (rotten)." ), food.tname() ); - } else { - you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), - food.tname() ); + if( food.has_flag( flag_HIDDEN_HALLU ) ) { + if( !you.has_effect( effect_hallu ) ) { + you.add_effect( effect_hallu, 6_hours ); + } + } } - } - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { - - add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); - add_msg( _( "You eat your %s (rotten)." ), food.tname() ); - } else { - you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), food.tname() ); - } - } - - if( item::find_type( food.get_comestible()->tool )->tool ) { - // Tools like lighters get used - you.use_charges( food.get_comestible()->tool, 1 ); - } + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { - - // The fun changes for these effects are applied in fun_for(). - if( food.has_flag( flag_MUSHY ) ) { - you.add_msg_if_player( m_bad, - _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); - } - if( food.get_comestible_fun() > 0 ) { - if( you.has_effect( effect_common_cold ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); - } - if( you.has_effect( effect_flu ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); - } + add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); + add_msg( _( "You eat your %s (rotten)." ), food.tname() ); + } else { + you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + food.tname() ); } + } - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { - add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); - add_msg( _( "You eat your %s (rotten)." ), food.tname() ); - } else { - you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { + + add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); + add_msg( _( "You eat your %s (rotten)." ), food.tname() ); + } else { + you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + food.tname() ); + } } - } - if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && - you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { - you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); - } - - // The fun changes for these effects are applied in fun_for(). - if( food.has_flag( flag_MUSHY ) ) { - you.add_msg_if_player( m_bad, - _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); - } - if( food.get_comestible_fun() > 0 ) { - if( you.has_effect( effect_common_cold ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); - } - if( you.has_effect( effect_flu ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); - } - } + if( item::find_type( food.get_comestible()->tool )->tool ) { + // Tools like lighters get used + you.use_charges( food.get_comestible()->tool, 1 ); + } - // Chance to become parasitised - if( !will_vomit && !you.has_flag( json_flag_PARAIMMUNE ) ) { - if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && - one_in( food.get_comestible()->parasites ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || - ( !you.has_flag( json_flag_HEMOVORE ) && !you.has_flag( json_flag_BLOODFEEDER ) ) ) ) { - switch( rng( 0, 3 ) ) { - case 0: - if( !you.has_trait( trait_EATHEALTH ) ) { - you.add_effect( effect_tapeworm, 1_turns, true ); - } - break; - case 1: - if( !you.has_trait( trait_ACIDBLOOD ) ) { - you.add_effect( effect_bloodworms, 1_turns, true ); + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { + + // The fun changes for these effects are applied in fun_for(). + if( food.has_flag( flag_MUSHY ) ) { + you.add_msg_if_player( m_bad, + _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); + } + if( food.get_comestible_fun() > 0 ) { + if( you.has_effect( effect_common_cold ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); + } + if( you.has_effect( effect_flu ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); + } + } + + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { + + add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); + add_msg( _( "You eat your %s (rotten)." ), food.tname() ); + } else { + you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + food.tname() ); + } + } + + if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && + you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { + you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); + } + + // The fun changes for these effects are applied in fun_for(). + if( food.has_flag( flag_MUSHY ) ) { + you.add_msg_if_player( m_bad, + _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); + } + if( food.get_comestible_fun() > 0 ) { + if( you.has_effect( effect_common_cold ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); + } + if( you.has_effect( effect_flu ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); + } + } + + // Chance to become parasitised + if( !will_vomit && !you.has_flag( json_flag_PARAIMMUNE ) ) { + if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && + one_in( food.get_comestible()->parasites ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || + ( !you.has_flag( json_flag_HEMOVORE ) && !you.has_flag( json_flag_BLOODFEEDER ) ) ) ) { + switch( rng( 0, 3 ) ) { + case 0: + if( !you.has_trait( trait_EATHEALTH ) ) { + you.add_effect( effect_tapeworm, 1_turns, true ); + } + break; + case 1: + if( !you.has_trait( trait_ACIDBLOOD ) ) { + you.add_effect( effect_bloodworms, 1_turns, true ); + } + break; + case 2: + you.add_effect( effect_brainworms, 1_turns, true ); + break; + case 3: + you.add_effect( effect_paincysts, 1_turns, true ); + } + } + } + + for( const std::pair &elem : food.get_comestible()->contamination ) { + if( rng( 1, 100 ) <= elem.second ) { + you.expose_to_disease( elem.first ); + } + } + + get_event_bus().send( you.getID(), food.typeId() ); + + if( will_vomit ) { + you.vomit(); + } + + you.consumption_history.emplace_back( food ); + // Clean out consumption_history so it doesn't get bigger than needed. + while( you.consumption_history.front().time < calendar::turn - 2_days ) { + you.consumption_history.pop_front(); + } + + you.recoil = MAX_RECOIL; + + return true; } break; case 2: @@ -1280,156 +1312,35 @@ ret_val Character::will_eat( const item &food, bool interactive ) break; case 3: you.add_effect( effect_paincysts, 1_turns, true ); + } } } - } - for( const std::pair &elem : food.get_comestible()->contamination ) { - if( rng( 1, 100 ) <= elem.second ) { - you.expose_to_disease( elem.first ); + for( const std::pair &elem : food.get_comestible()->contamination ) { + if( rng( 1, 100 ) <= elem.second ) { + you.expose_to_disease( elem.first ); + } } - } - get_event_bus().send( you.getID(), food.typeId() ); + get_event_bus().send( you.getID(), food.typeId() ); - if( will_vomit ) { - you.vomit(); - } + if( will_vomit ) { + you.vomit(); + } - you.consumption_history.emplace_back( food ); - // Clean out consumption_history so it doesn't get bigger than needed. - while( you.consumption_history.front().time < calendar::turn - 2_days ) { - you.consumption_history.pop_front(); - } + you.consumption_history.emplace_back( food ); + // Clean out consumption_history so it doesn't get bigger than needed. + while( you.consumption_history.front().time < calendar::turn - 2_days ) { + you.consumption_history.pop_front(); + } + + you.recoil = MAX_RECOIL; - you.recoil = MAX_RECOIL; + return true; + } return true; } - break; - case 2: - you.add_effect( effect_brainworms, 1_turns, true ); - break; - case 3: - you.add_effect( effect_paincysts, 1_turns, true ); - } - } - } - - for( const std::pair &elem : food.get_comestible()->contamination ) { - if( rng( 1, 100 ) <= elem.second ) { - you.expose_to_disease( elem.first ); - } - } - - get_event_bus().send( you.getID(), food.typeId() ); - - if( will_vomit ) { - you.vomit(); - } - - you.consumption_history.emplace_back( food ); - // Clean out consumption_history so it doesn't get bigger than needed. - while( you.consumption_history.front().time < calendar::turn - 2_days ) { - you.consumption_history.pop_front(); - } - - you.recoil = MAX_RECOIL; - - return true; - } - - return true; - } - - void Character::modify_stimulation( const islot_comestible & comest ) { - if( comest.stim == 0 ) { - return; - } - const int current_stim = get_stim(); - if( ( std::abs( comest.stim ) * 3 ) > std::abs( current_stim ) ) { - mod_stim( comest.stim ); - } else { - comest.stim > 0 ? mod_stim( std::max( comest.stim / 2, 1 ) ) : mod_stim( std::min( comest.stim / 2, - -1 ) ); - } - if( has_trait( trait_STIMBOOST ) && ( current_stim > 30 ) && - ( comest.addictions.count( STATIC( addiction_id( "caffeine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "amphetamine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "cocaine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "crack" ) ) ) ) ) { - int hallu_duration = ( current_stim - comest.stim < 30 ) ? current_stim - 30 : comest.stim; - add_effect( effect_visuals, hallu_duration * 30_minutes ); - add_msg_if_player( m_bad, SNIPPET.random_from_category( "comest_stimulant" ).value_or( - translation() ).translated() ); - } - } - - void Character::modify_fatigue( const islot_comestible & comest ) { - mod_fatigue( -comest.fatigue_mod ); - } -} - } -void Character::modify_fatigue( const islot_comestible &comest ) -{ - mod_fatigue( -comest.fatigue_mod ); -} - -void Character::modify_addiction( const islot_comestible &comest ) -{ - for( const std::pair &add : comest.addictions ) { - add_addiction( add.first, add.second ); - if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { - rem_morale( add.first->get_craving_morale() ); - } - } -} - add_addiction( add.first, add.second ); -void Character::modify_morale( item &food, const int nutr ) -{ - time_duration morale_time = 2_hours; - if( food.has_flag( flag_HOT ) && food.has_flag( flag_EATEN_HOT ) ) { - morale_time = 3_hours; - int clamped_nutr = std::max( 5, std::min( 20, nutr / 10 ) ); - add_morale( MORALE_FOOD_HOT, clamped_nutr, 20, morale_time, morale_time / 2 ); - } - - std::pair fun = fun_for( food ); - if( fun.first < 0 ) { - add_morale( MORALE_FOOD_BAD, fun.first, fun.second, morale_time, morale_time / 2, false, - food.type ); - } else if( fun.first > 0 ) { - add_morale( MORALE_FOOD_GOOD, fun.first, fun.second, morale_time, morale_time / 2, false, - food.type ); - } - - // Morale bonus for eating unspoiled food with chair/table nearby - // Does not apply to non-ingested consumables like bandages or drugs, - // nor to drinks. - if( !food.has_flag( flag_NO_INGEST ) && - food.get_comestible()->comesttype != "MED" && - food.get_comestible()->comesttype != comesttype_DRINK ) { - map &here = get_map(); - if( here.has_nearby_chair( pos(), 1 ) && here.has_nearby_table( pos_bub(), 1 ) ) { - if( has_trait( trait_TABLEMANNERS ) ) { - rem_morale( MORALE_ATE_WITHOUT_TABLE ); - if( !food.rotten() ) { - add_morale( MORALE_ATE_WITH_TABLE, 3, 3, 3_hours, 2_hours, true ); - } - } - } - void Character::modify_fatigue( const islot_comestible & comest ) { - mod_fatigue( -comest.fatigue_mod ); - } - - void Character::modify_addiction( const islot_comestible & comest ) { - for( const std::pair &add : comest.addictions ) { - add_addiction( add.first, add.second ); - if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { - rem_morale( add.first->get_craving_morale() ); - } - } - } void Character::modify_stimulation( const islot_comestible & comest ) { if( comest.stim == 0 ) { @@ -1454,570 +1365,318 @@ void Character::modify_morale( item &food, const int nutr ) } } - // Allergy check for food that is ingested (not gum) - if( !food.has_flag( flag_NO_INGEST ) ) { - const morale_type allergy = allergy_type( food ); - if( allergy != MORALE_NULL ) { - add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); - add_morale( allergy, -75, -400, 30_minutes, 24_minutes ); - } - if( food.has_flag( flag_ALLERGEN_JUNK ) ) { - if( has_trait( trait_PROJUNK ) ) { - add_msg_if_player( m_good, _( "Mmm, junk food." ) ); - add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); - } - if( food.has_flag( flag_ALLERGEN_JUNK ) ) { - if( has_trait( trait_PROJUNK ) ) { - add_msg_if_player( m_good, _( "Mmm, junk food." ) ); - add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); - } - if( has_trait( trait_PROJUNK2 ) ) { - if( !one_in( 100 ) ) { - add_msg_if_player( m_good, _( "When life's got you down, there's always sugar." ) ); - } else { - add_msg_if_player( m_good, _( "They may do what they must… you've already won." ) ); + void Character::modify_fatigue( const islot_comestible & comest ) { + mod_fatigue( -comest.fatigue_mod ); } - add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes ); - } - // Carnivores CAN eat junk food, but they won't like it much. - // Pizza-scraping happens in consume_effects. - if( has_trait( trait_CARNIVORE ) && !food.has_flag( flag_CARNIVORE_OK ) ) { - add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); - add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); } - add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes ); - } - // Carnivores CAN eat junk food, but they won't like it much. - // Pizza-scraping happens in consume_effects. - if( has_trait( trait_CARNIVORE ) && !food.has_flag( flag_CARNIVORE_OK ) ) { - add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); - add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); } - add_morale( MORALE_HONEY, honey_fun, 100 ); - } - } - - // Used when determining stomach fullness from eating. - double Character::compute_effective_food_volume_ratio( const item & food ) const { - const nutrients food_nutrients = compute_effective_nutrients( food ); - units::mass food_weight = ( food.weight() / std::max( 1, food.count() ) ); - double ratio = 1.0f; - if( units::to_gram( food_weight ) != 0 ) { - ratio = std::max( static_cast( food_nutrients.kcal() ) / units::to_gram( food_weight ), - 1.0 ); - if( ratio > 3.0f ) { - ratio = std::sqrt( 3 * ratio ); + void Character::modify_fatigue( const islot_comestible & comest ) { + mod_fatigue( -comest.fatigue_mod ); } - } - return ratio; - } - - // Remove the water volume from the food, as that gets absorbed and used as water. - // If the remaining dry volume of the food is less dense than water, crunch it down to a density equal to water. - // These maths are made easier by the fact that 1 g = 1 mL. Thanks, metric system. - units::volume Character::masticated_volume( const item & food ) const { - units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? food.get_comestible()->quench * - 5_ml : 0_ml; - units::mass water_weight = units::from_gram( units::to_milliliter( water_vol ) ); - // handle the division by zero exception when the food count is 0 with std::max() - units::mass food_dry_weight = food.weight() / std::max( 1, food.count() ) - water_weight; - units::volume food_dry_volume = food.volume() / std::max( 1, food.count() ) - water_vol; - - if( units::to_milliliter( food_dry_volume ) != 0 && - units::to_gram( food_dry_weight ) < units::to_milliliter( food_dry_volume ) ) { - food_dry_volume = units::from_milliliter( units::to_gram( food_dry_weight ) ); - } - return food_dry_volume; - } - - // Used when displaying effective food satiation values. - int Character::compute_calories_per_effective_volume( const item & food, - const nutrients * nutrient /* = nullptr */ )const { - /* Understanding how Calories Per Effective Volume are calculated requires a dive into the - stomach fullness source code. Look at issue #44365*/ - int kcalories; - if( nutrient ) { - // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). - kcalories = nutrient->kcal(); - } else { - kcalories = compute_effective_nutrients( food ).kcal(); - } - double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); - const double energy_density_ratio = compute_effective_food_volume_ratio( food ); - const double effective_volume = food_vol * energy_density_ratio; - if( kcalories == 0 && effective_volume == 0.0 ) { - return 0; - } - return std::round( kcalories / effective_volume ); - } - - // Used when displaying effective food satiation values. - int Character::compute_calories_per_effective_volume( const item & food, - const nutrients * nutrient /* = nullptr */ )const { - /* Understanding how Calories Per Effective Volume are calculated requires a dive into the - stomach fullness source code. Look at issue #44365*/ - int kcalories; - if( nutrient ) { - // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). - kcalories = nutrient->kcal(); - } else { - kcalories = compute_effective_nutrients( food ).kcal(); - } - double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); - const double energy_density_ratio = compute_effective_food_volume_ratio( food ); - const double effective_volume = food_vol * energy_density_ratio; - if( kcalories == 0 && effective_volume == 0.0 ) { - return 0; - } - return std::round( kcalories / effective_volume ); - } - } - - // Used when determining stomach fullness from eating. - double Character::compute_effective_food_volume_ratio( const item & food ) const { - const nutrients food_nutrients = compute_effective_nutrients( food ); - units::mass food_weight = ( food.weight() / std::max( 1, food.count() ) ); - double ratio = 1.0f; - if( units::to_gram( food_weight ) != 0 ) { - ratio = std::max( static_cast( food_nutrients.kcal() ) / units::to_gram( food_weight ), - 1.0 ); - if( ratio > 3.0f ) { - ratio = std::sqrt( 3 * ratio ); - } - } - return ratio; - } - - // Remove the water volume from the food, as that gets absorbed and used as water. - // If the remaining dry volume of the food is less dense than water, crunch it down to a density equal to water. - // These maths are made easier by the fact that 1 g = 1 mL. Thanks, metric system. - units::volume Character::masticated_volume( const item & food ) const { - units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? food.get_comestible()->quench * - 5_ml : 0_ml; - units::mass water_weight = units::from_gram( units::to_milliliter( water_vol ) ); - // handle the division by zero exception when the food count is 0 with std::max() - units::mass food_dry_weight = food.weight() / std::max( 1, food.count() ) - water_weight; - units::volume food_dry_volume = food.volume() / std::max( 1, food.count() ) - water_vol; - - if( units::to_milliliter( food_dry_volume ) != 0 && - units::to_gram( food_dry_weight ) < units::to_milliliter( food_dry_volume ) ) { - food_dry_volume = units::from_milliliter( units::to_gram( food_dry_weight ) ); - } - - return food_dry_volume; - } - - // Used when displaying effective food satiation values. - int Character::compute_calories_per_effective_volume( const item & food, - const nutrients * nutrient /* = nullptr */ )const { - /* Understanding how Calories Per Effective Volume are calculated requires a dive into the - stomach fullness source code. Look at issue #44365*/ - int kcalories; - if( nutrient ) { - // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). - kcalories = nutrient->kcal(); - } else { - kcalories = compute_effective_nutrients( food ).kcal(); - } - double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); - const double energy_density_ratio = compute_effective_food_volume_ratio( food ); - const double effective_volume = food_vol * energy_density_ratio; - if( kcalories == 0 && effective_volume == 0.0 ) { - return 0; - } - return std::round( kcalories / effective_volume ); - } - - // Used when displaying effective food satiation values. - int Character::compute_calories_per_effective_volume( const item & food, - const nutrients * nutrient /* = nullptr */ )const { - /* Understanding how Calories Per Effective Volume are calculated requires a dive into the - stomach fullness source code. Look at issue #44365*/ - int kcalories; - if( nutrient ) { - // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). - kcalories = nutrient->kcal(); - } else { - kcalories = compute_effective_nutrients( food ).kcal(); - } - double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); - const double energy_density_ratio = compute_effective_food_volume_ratio( food ); - const double effective_volume = food_vol * energy_density_ratio; - if( kcalories == 0 && effective_volume == 0.0 ) { - return 0; + void Character::modify_addiction( const islot_comestible & comest ) { + for( const std::pair &add : comest.addictions ) { + add_addiction( add.first, add.second ); + if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { + rem_morale( add.first->get_craving_morale() ); + } } - return std::round( kcalories / effective_volume ); } - - static void activate_consume_eocs( Character & you, item & target ) { - Character *char_ptr = nullptr; - if( avatar *u = you.as_avatar() ) { - char_ptr = u; - } else if( npc *n = you.as_npc() ) { - char_ptr = n; - } - item_location loc( you, &target ); - dialogue d( get_talker_for( char_ptr ), get_talker_for( loc ) ); - const islot_comestible &comest = *target.get_comestible(); - for( const effect_on_condition_id &eoc : comest.consumption_eocs ) { - eoc->activate( d ); + add_addiction( add.first, add.second ); + void Character::modify_morale( item & food, const int nutr ) { + time_duration morale_time = 2_hours; + if( food.has_flag( flag_HOT ) && food.has_flag( flag_EATEN_HOT ) ) { + morale_time = 3_hours; + int clamped_nutr = std::max( 5, std::min( 20, nutr / 10 ) ); + add_morale( MORALE_FOOD_HOT, clamped_nutr, 20, morale_time, morale_time / 2 ); } - } - bool Character::consume_effects( item & food ) { - if( !food.is_comestible() ) { - debugmsg( "called Character::consume_effects with non-comestible" ); - return false; + std::pair fun = fun_for( food ); + if( fun.first < 0 ) { + add_morale( MORALE_FOOD_BAD, fun.first, fun.second, morale_time, morale_time / 2, false, + food.type ); + } else if( fun.first > 0 ) { + add_morale( MORALE_FOOD_GOOD, fun.first, fun.second, morale_time, morale_time / 2, false, + food.type ); } - if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { - // Was used to cap nutrition and thirst, but no longer does this - return false; - } - if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && - food.has_any_flag( herbivore_blacklist ) ) { - // No good can come of this. - return false; - } + // Morale bonus for eating unspoiled food with chair/table nearby + // Does not apply to non-ingested consumables like bandages or drugs, + // nor to drinks. + if( !food.has_flag( flag_NO_INGEST ) && + food.get_comestible()->comesttype != "MED" && + food.get_comestible()->comesttype != comesttype_DRINK ) { + map &here = get_map(); + if( here.has_nearby_chair( pos(), 1 ) && here.has_nearby_table( pos_bub(), 1 ) ) { + if( has_trait( trait_TABLEMANNERS ) ) { + rem_morale( MORALE_ATE_WITHOUT_TABLE ); + if( !food.rotten() ) { + add_morale( MORALE_ATE_WITH_TABLE, 3, 3, 3_hours, 2_hours, true ); + } + } + } + void Character::modify_fatigue( const islot_comestible & comest ) { + mod_fatigue( -comest.fatigue_mod ); + } - // Used in hibernation messages. - const int nutr = nutrition_for( food ); - const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; - // We can handle junk just fine - if( !skip_health ) { - modify_health( comest ); - } - modify_stimulation( comest ); - modify_fatigue( comest ); - modify_addiction( comest ); - modify_morale( food, nutr ); - - const bool hibernate = has_active_mutation( trait_HIBERNATE ); - if( hibernate ) { - if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { - // Tell the player what's going on - add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); - if( one_in( 2 ) ) { - // 50% chance of the food tiring you - mod_fatigue( nutr ); - } - } - if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { - // Hibernation should cut burn to 60/day - add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); - if( one_in( 2 ) ) { - // And another 50%, intended cumulative - mod_fatigue( nutr ); - } - } + void Character::modify_addiction( const islot_comestible & comest ) { + for( const std::pair &add : comest.addictions ) { + add_addiction( add.first, add.second ); + if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { + rem_morale( add.first->get_craving_morale() ); + } + } + } - if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { - add_msg_if_player( - _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); - if( !one_in( 3 ) ) { - // Third check, this one at 66% - mod_fatigue( nutr ); - } - } - if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { - add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); - // At this point, you're done. Schlaf gut. - mod_fatigue( nutr ); - } - } - // Moved here and changed a bit - it was too complex - // Incredibly minor stuff like this shouldn't require complexity - if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && - ( get_healthy_kcal() < get_stored_kcal() + 4000 && - get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { - add_msg_if_player( m_mixed, - _( "You feel as though you're going to split open! In a good way?" ) ); - mod_pain( 5 ); - int numslime = 1; - for( int i = 0; i < numslime; i++ ) { - if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { - slime->friendly = -1; - } - } + void Character::modify_stimulation( const islot_comestible & comest ) { + if( comest.stim == 0 ) { + return; + } + const int current_stim = get_stim(); + if( ( std::abs( comest.stim ) * 3 ) > std::abs( current_stim ) ) { + mod_stim( comest.stim ); + } else { + comest.stim > 0 ? mod_stim( std::max( comest.stim / 2, 1 ) ) : mod_stim( std::min( comest.stim / 2, + -1 ) ); + } + if( has_trait( trait_STIMBOOST ) && ( current_stim > 30 ) && + ( comest.addictions.count( STATIC( addiction_id( "caffeine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "amphetamine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "cocaine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "crack" ) ) ) ) ) { + int hallu_duration = ( current_stim - comest.stim < 30 ) ? current_stim - 30 : comest.stim; + add_effect( effect_visuals, hallu_duration * 30_minutes ); + add_msg_if_player( m_bad, SNIPPET.random_from_category( "comest_stimulant" ).value_or( + translation() ).translated() ); + } + } - nutrients food_nutrients = compute_effective_nutrients( food ); - const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? - food.get_comestible()->quench * - 5_ml : 0_ml; - units::volume food_vol = masticated_volume( food ); - if( food.count() == 0 ) { - debugmsg( "Tried to eat food with count of zero." ); - return false; - } - units::mass food_weight = ( food.weight() / food.count() ); - const double ratio = compute_effective_food_volume_ratio( food ); - food_summary ingested{ - water_vol, - food_vol * ratio, - food_nutrients - }; - add_msg_debug( debugmode::DF_FOOD, - "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", - units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, - food_nutrients.kcal(), units::to_gram( food_weight ) ); - // Maybe move tapeworm to digestion - if( has_effect( effect_tapeworm ) ) { - ingested.nutr /= 2; - } - // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? - activate_consume_eocs( *this, food ); + // Allergy check for food that is ingested (not gum) + if( !food.has_flag( flag_NO_INGEST ) ) { + const morale_type allergy = allergy_type( food ); + if( allergy != MORALE_NULL ) { + add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); + add_morale( allergy, -75, -400, 30_minutes, 24_minutes ); + } + if( food.has_flag( flag_ALLERGEN_JUNK ) ) { + if( has_trait( trait_PROJUNK ) ) { + add_msg_if_player( m_good, _( "Mmm, junk food." ) ); + add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); + } + if( food.has_flag( flag_ALLERGEN_JUNK ) ) { + if( has_trait( trait_PROJUNK ) ) { + add_msg_if_player( m_good, _( "Mmm, junk food." ) ); + add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); + } + if( has_trait( trait_PROJUNK2 ) ) { + if( !one_in( 100 ) ) { + add_msg_if_player( m_good, _( "When life's got you down, there's always sugar." ) ); + } else { + add_msg_if_player( m_good, _( "They may do what they must… you've already won." ) ); + } + add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes ); + } + // Carnivores CAN eat junk food, but they won't like it much. + // Pizza-scraping happens in consume_effects. + if( has_trait( trait_CARNIVORE ) && !food.has_flag( flag_CARNIVORE_OK ) ) { + add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); + add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); + } + add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes ); + } + // Carnivores CAN eat junk food, but they won't like it much. + // Pizza-scraping happens in consume_effects. + if( has_trait( trait_CARNIVORE ) && !food.has_flag( flag_CARNIVORE_OK ) ) { + add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); + add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); + } + add_morale( MORALE_HONEY, honey_fun, 100 ); + } + } - if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { - // Was used to cap nutrition and thirst, but no longer does this - return false; - } - if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && - food.has_any_flag( herbivore_blacklist ) ) { - // No good can come of this. - return false; - } + // Used when determining stomach fullness from eating. + double Character::compute_effective_food_volume_ratio( const item & food ) const { + const nutrients food_nutrients = compute_effective_nutrients( food ); + units::mass food_weight = ( food.weight() / std::max( 1, food.count() ) ); + double ratio = 1.0f; + if( units::to_gram( food_weight ) != 0 ) { + ratio = std::max( static_cast( food_nutrients.kcal() ) / units::to_gram( food_weight ), + 1.0 ); + if( ratio > 3.0f ) { + ratio = std::sqrt( 3 * ratio ); + } + } + return ratio; + } - // Used in hibernation messages. - const int nutr = nutrition_for( food ); - const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; - // We can handle junk just fine - if( !skip_health ) { - modify_health( comest ); - } - modify_stimulation( comest ); - modify_fatigue( comest ); - modify_addiction( comest ); - modify_morale( food, nutr ); - - const bool hibernate = has_active_mutation( trait_HIBERNATE ); - if( hibernate ) { - if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { - // Tell the player what's going on - add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); - if( one_in( 2 ) ) { - // 50% chance of the food tiring you - mod_fatigue( nutr ); + // Remove the water volume from the food, as that gets absorbed and used as water. + // If the remaining dry volume of the food is less dense than water, crunch it down to a density equal to water. + // These maths are made easier by the fact that 1 g = 1 mL. Thanks, metric system. + units::volume Character::masticated_volume( const item & food ) const { + units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? food.get_comestible()->quench * + 5_ml : 0_ml; + units::mass water_weight = units::from_gram( units::to_milliliter( water_vol ) ); + // handle the division by zero exception when the food count is 0 with std::max() + units::mass food_dry_weight = food.weight() / std::max( 1, food.count() ) - water_weight; + units::volume food_dry_volume = food.volume() / std::max( 1, food.count() ) - water_vol; + + if( units::to_milliliter( food_dry_volume ) != 0 && + units::to_gram( food_dry_weight ) < units::to_milliliter( food_dry_volume ) ) { + food_dry_volume = units::from_milliliter( units::to_gram( food_dry_weight ) ); + } + + return food_dry_volume; } - } - if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { - // Hibernation should cut burn to 60/day - add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); - if( one_in( 2 ) ) { - // And another 50%, intended cumulative - mod_fatigue( nutr ); + + // Used when displaying effective food satiation values. + int Character::compute_calories_per_effective_volume( const item & food, + const nutrients * nutrient /* = nullptr */ )const { + /* Understanding how Calories Per Effective Volume are calculated requires a dive into the + stomach fullness source code. Look at issue #44365*/ + int kcalories; + if( nutrient ) { + // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). + kcalories = nutrient->kcal(); + } else { + kcalories = compute_effective_nutrients( food ).kcal(); + } + double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); + const double energy_density_ratio = compute_effective_food_volume_ratio( food ); + const double effective_volume = food_vol * energy_density_ratio; + if( kcalories == 0 && effective_volume == 0.0 ) { + return 0; + } + return std::round( kcalories / effective_volume ); } - } - if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { - add_msg_if_player( - _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); - if( !one_in( 3 ) ) { - // Third check, this one at 66% - mod_fatigue( nutr ); + // Used when displaying effective food satiation values. + int Character::compute_calories_per_effective_volume( const item & food, + const nutrients * nutrient /* = nullptr */ )const { + /* Understanding how Calories Per Effective Volume are calculated requires a dive into the + stomach fullness source code. Look at issue #44365*/ + int kcalories; + if( nutrient ) { + // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). + kcalories = nutrient->kcal(); + } else { + kcalories = compute_effective_nutrients( food ).kcal(); + } + double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); + const double energy_density_ratio = compute_effective_food_volume_ratio( food ); + const double effective_volume = food_vol * energy_density_ratio; + if( kcalories == 0 && effective_volume == 0.0 ) { + return 0; + } + return std::round( kcalories / effective_volume ); } } - if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { - add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); - // At this point, you're done. Schlaf gut. - mod_fatigue( nutr ); - } - } - // Moved here and changed a bit - it was too complex - // Incredibly minor stuff like this shouldn't require complexity - if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && - ( get_healthy_kcal() < get_stored_kcal() + 4000 && - get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { - add_msg_if_player( m_mixed, - _( "You feel as though you're going to split open! In a good way?" ) ); - mod_pain( 5 ); - int numslime = 1; - for( int i = 0; i < numslime; i++ ) { - if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { - slime->friendly = -1; + + // Used when determining stomach fullness from eating. + double Character::compute_effective_food_volume_ratio( const item & food ) const { + const nutrients food_nutrients = compute_effective_nutrients( food ); + units::mass food_weight = ( food.weight() / std::max( 1, food.count() ) ); + double ratio = 1.0f; + if( units::to_gram( food_weight ) != 0 ) { + ratio = std::max( static_cast( food_nutrients.kcal() ) / units::to_gram( food_weight ), + 1.0 ); + if( ratio > 3.0f ) { + ratio = std::sqrt( 3 * ratio ); + } } + return ratio; } - mod_hunger( 40 ); - mod_thirst( 40 ); - //~ slimespawns have *small voices* which may be the Nice equivalent - //~ of the Rat King's ALL CAPS invective. Probably shared-brain telepathy. - add_msg_if_player( m_good, _( "hey, you look like me! let's work together!" ) ); - } - bool Character::can_estimate_rot() const { - return get_greater_skill_or_knowledge_level( skill_cooking ) >= 3 || - get_greater_skill_or_knowledge_level( skill_survival ) >= 4; - } - - bool Character::can_consume_as_is( const item & it ) const { - if( it.is_comestible() ) { - return !it.has_flag( flag_FROZEN ) || it.has_flag( flag_EDIBLE_FROZEN ) || - it.has_flag( flag_MELTS ); - } - return false; - } - - item &Character::get_consumable_from( item & it ) const { - item *ret = nullptr; - it.visit_items( [&]( item * it, item * ) { - if( can_consume_as_is( *it ) ) { - ret = it; - return VisitResponse::ABORT; - } - return VisitResponse::NEXT; - } ); - - if( ret != nullptr ) { - return *ret; - } - - static item null_comestible; - // Since it's not const. - null_comestible = item(); - return null_comestible; - } - - time_duration Character::get_consume_time( const item & it ) const { - const int charges = std::max( it.charges, 1 ); - int volume = units::to_milliliter( it.volume() ) / charges; - if( 0 == volume && it.type ) { - volume = units::to_milliliter( it.type->volume ); - } - time_duration time = time_duration::from_seconds( std::max( ( volume / - 5 ), 1 ) ); //Default 5 mL (1 tablespoon) per second - float consume_time_modifier = 1.0f;//only for food and drinks - const bool eat_verb = it.has_flag( flag_USE_EAT_VERB ); - const std::string comest_type = it.get_comestible() ? it.get_comestible()->comesttype : ""; - if( eat_verb || comest_type == "FOOD" ) { - time = time_duration::from_seconds( volume / 5 ); //Eat 5 mL (1 teaspoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } else if( !eat_verb && comest_type == "DRINK" ) { - time = time_duration::from_seconds( volume / 15 ); //Drink 15 mL (1 tablespoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } else if( use_function const *fun = it.type->get_use( "heal" ) ) { - time = time_duration::from_moves( dynamic_cast - ( fun->get_actor_ptr() )->move_cost ); - } else if( it.is_medication() ) { - const use_function *consume_drug = it.type->get_use( "consume_drug" ); - const use_function *smoking = it.type->get_use( "SMOKING" ); - const use_function *adrenaline_injector = it.type->get_use( "ADRENALINE_INJECTOR" ); - if( consume_drug != nullptr ) { //its a drug - const consume_drug_iuse *consume_drug_use = dynamic_cast - ( consume_drug->get_actor_ptr() ); - if( consume_drug_use->tools_needed.find( itype_syringe ) != consume_drug_use->tools_needed.end() && - has_bionic( bio_syringe ) ) { - time = time_duration::from_seconds( - 15 );//injections with the intradermal needle CBM are much quicker than with a normal syringe - } else if( consume_drug_use->tools_needed.find( itype_syringe ) != - consume_drug_use->tools_needed.end() ) { - time = time_duration::from_minutes( 5 );//sterile injections take 5 minutes - } else if( consume_drug_use->tools_needed.find( itype_apparatus ) != - consume_drug_use->tools_needed.end() || - consume_drug_use->tools_needed.find( itype_dab_pen_on ) != consume_drug_use->tools_needed.end() ) { - time = time_duration::from_seconds( 30 );//smoke a bowl - } else { - time = time_duration::from_seconds( 5 );//popping a pill is quick - } - } else if( smoking != nullptr ) { - time = time_duration::from_minutes( 1 );//about five minutes for a cig or joint so 1 minute a charge - } else if( adrenaline_injector != nullptr ) { - //epi-pens, and disinfectant are fairly quick - time = time_duration::from_seconds( 15 ); - } else { - time = time_duration::from_seconds( 5 ); //probably pills so quick - } - } else if( it.get_category_shallow().get_id() == item_category_chems ) { - time = time_duration::from_seconds( std::max( ( volume / 15 ), - 1 ) ); //Consume 15 mL (1 tablespoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } + // Remove the water volume from the food, as that gets absorbed and used as water. + // If the remaining dry volume of the food is less dense than water, crunch it down to a density equal to water. + // These maths are made easier by the fact that 1 g = 1 mL. Thanks, metric system. + units::volume Character::masticated_volume( const item & food ) const { + units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? food.get_comestible()->quench * + 5_ml : 0_ml; + units::mass water_weight = units::from_gram( units::to_milliliter( water_vol ) ); + // handle the division by zero exception when the food count is 0 with std::max() + units::mass food_dry_weight = food.weight() / std::max( 1, food.count() ) - water_weight; + units::volume food_dry_volume = food.volume() / std::max( 1, food.count() ) - water_vol; + + if( units::to_milliliter( food_dry_volume ) != 0 && + units::to_gram( food_dry_weight ) < units::to_milliliter( food_dry_volume ) ) { + food_dry_volume = units::from_milliliter( units::to_gram( food_dry_weight ) ); + } - nutrients food_nutrients = compute_effective_nutrients( food ); - const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? - food.get_comestible()->quench * - 5_ml : 0_ml; - units::volume food_vol = masticated_volume( food ); - if( food.count() == 0 ) { - debugmsg( "Tried to eat food with count of zero." ); - return false; - } - units::mass food_weight = ( food.weight() / food.count() ); - const double ratio = compute_effective_food_volume_ratio( food ); - food_summary ingested{ - water_vol, - food_vol * ratio, - food_nutrients - }; - add_msg_debug( debugmode::DF_FOOD, - "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", - units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, - food_nutrients.kcal(), units::to_gram( food_weight ) ); - // Maybe move tapeworm to digestion - if( has_effect( effect_tapeworm ) ) { - ingested.nutr /= 2; + return food_dry_volume; } - // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? - activate_consume_eocs( *this, food ); - - // GET IN MAH BELLY! - stomach.ingest( ingested ); - static bool query_consume_ownership( item & target, Character & p ) { - if( !target.is_owned_by( p, true ) ) { - bool choice = true; - if( p.get_value( "THIEF_MODE" ) == "THIEF_ASK" ) { - choice = Pickup::query_thief(); - } - if( p.get_value( "THIEF_MODE" ) == "THIEF_HONEST" || !choice ) { - return false; - } - std::vector witnesses; - for( npc &elem : g->all_npcs() ) { - if( rl_dist( elem.pos(), p.pos() ) < MAX_VIEW_DISTANCE && elem.sees( p.pos() ) ) { - witnesses.push_back( &elem ); - } - } - for( npc *elem : witnesses ) { - elem->say( "", 7 ); - } - if( !witnesses.empty() && target.is_owned_by( p, true ) ) { - if( p.add_faction_warning( target.get_owner() ) ) { - for( npc *elem : witnesses ) { - elem->make_angry(); + // Used when displaying effective food satiation values. + int Character::compute_calories_per_effective_volume( const item & food, + const nutrients * nutrient /* = nullptr */ )const { + /* Understanding how Calories Per Effective Volume are calculated requires a dive into the + stomach fullness source code. Look at issue #44365*/ + int kcalories; + if( nutrient ) { + // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). + kcalories = nutrient->kcal(); + } else { + kcalories = compute_effective_nutrients( food ).kcal(); + } + double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); + const double energy_density_ratio = compute_effective_food_volume_ratio( food ); + const double effective_volume = food_vol * energy_density_ratio; + if( kcalories == 0 && effective_volume == 0.0 ) { + return 0; + } + return std::round( kcalories / effective_volume ); } - } - } - } - return true; - } - /** Consume medication. - * @return true if item consumed. - */ - static bool consume_med( item & target, Character & you ) { - if( !target.is_medication() ) { - return false; - } + // Used when displaying effective food satiation values. + int Character::compute_calories_per_effective_volume( const item & food, + const nutrients * nutrient /* = nullptr */ )const { + /* Understanding how Calories Per Effective Volume are calculated requires a dive into the + stomach fullness source code. Look at issue #44365*/ + int kcalories; + if( nutrient ) { + // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). + kcalories = nutrient->kcal(); + } else { + kcalories = compute_effective_nutrients( food ).kcal(); + } + double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); + const double energy_density_ratio = compute_effective_food_volume_ratio( food ); + const double effective_volume = food_vol * energy_density_ratio; + if( kcalories == 0 && effective_volume == 0.0 ) { + return 0; + } + return std::round( kcalories / effective_volume ); + } - const itype_id tool_type = target.get_comestible()->tool; - const itype *req_tool = item::find_type( tool_type ); + static void activate_consume_eocs( Character & you, item & target ) { + Character *char_ptr = nullptr; + if( avatar *u = you.as_avatar() ) { + char_ptr = u; + } else if( npc *n = you.as_npc() ) { + char_ptr = n; + } + item_location loc( you, &target ); + dialogue d( get_talker_for( char_ptr ), get_talker_for( loc ) ); + const islot_comestible &comest = *target.get_comestible(); + for( const effect_on_condition_id &eoc : comest.consumption_eocs ) { + eoc->activate( d ); + } + } - if( req_tool->tool ) { - if( !( you.has_amount( tool_type, 1 ) && - you.has_charges( tool_type, req_tool->tool->charges_per_use ) ) ) { - you.add_msg_if_player( m_info, _( "You need a %s to consume that!" ), req_tool->nname( 1 ) ); - return false; - } - you.use_charges( tool_type, req_tool->tool->charges_per_use ); - } + bool Character::consume_effects( item & food ) { + if( !food.is_comestible() ) { + debugmsg( "called Character::consume_effects with non-comestible" ); + return false; + } - int amount_used = 1; - if( target.type->has_use() ) { - amount_used = target.type->invoke( &you, target, you.pos() ).value_or( 0 ); - if( amount_used <= 0 ) { - return false; - } - } + if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { + // Was used to cap nutrition and thirst, but no longer does this + return false; + } + if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && + food.has_any_flag( herbivore_blacklist ) ) { + // No good can come of this. + return false; + } // Used in hibernation messages. const int nutr = nutrition_for( food ); @@ -2106,147 +1765,485 @@ void Character::modify_morale( item &food, const int nutr ) // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? activate_consume_eocs( *this, food ); - // GET IN MAH BELLY! - stomach.ingest( ingested ); - - // update speculative values - if( is_avatar() ) { - get_avatar().add_ingested_kcal( ingested.nutr.calories / 1000 ); + if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { + // Was used to cap nutrition and thirst, but no longer does this + return false; } - for( const auto &v : ingested.nutr.vitamins() ) { - // update the estimated values for daily vitamins - // actual vitamins happen during digestion - daily_vitamins[v.first].first += v.second; + if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && + food.has_any_flag( herbivore_blacklist ) ) { + // No good can come of this. + return false; } - return true; - } + // Used in hibernation messages. + const int nutr = nutrition_for( food ); + const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; + // We can handle junk just fine + if( !skip_health ) { + modify_health( comest ); + } + modify_stimulation( comest ); + modify_fatigue( comest ); + modify_addiction( comest ); + modify_morale( food, nutr ); + + const bool hibernate = has_active_mutation( trait_HIBERNATE ); + if( hibernate ) { + if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { + // Tell the player what's going on + add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); + if( one_in( 2 ) ) { + // 50% chance of the food tiring you + mod_fatigue( nutr ); + } + } + if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { + // Hibernation should cut burn to 60/day + add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); + if( one_in( 2 ) ) { + // And another 50%, intended cumulative + mod_fatigue( nutr ); + } + } - if( target.count_by_charges() ) { - target.mod_charges( -amount_used ); - } - return true; - } + if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { + add_msg_if_player( + _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); + if( !one_in( 3 ) ) { + // Third check, this one at 66% + mod_fatigue( nutr ); + } + } + if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { + add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); + // At this point, you're done. Schlaf gut. + mod_fatigue( nutr ); + } + } + // Moved here and changed a bit - it was too complex + // Incredibly minor stuff like this shouldn't require complexity + if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && + ( get_healthy_kcal() < get_stored_kcal() + 4000 && + get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { + add_msg_if_player( m_mixed, + _( "You feel as though you're going to split open! In a good way?" ) ); + mod_pain( 5 ); + int numslime = 1; + for( int i = 0; i < numslime; i++ ) { + if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { + slime->friendly = -1; + } + } + mod_hunger( 40 ); + mod_thirst( 40 ); + //~ slimespawns have *small voices* which may be the Nice equivalent + //~ of the Rat King's ALL CAPS invective. Probably shared-brain telepathy. + add_msg_if_player( m_good, _( "hey, you look like me! let's work together!" ) ); + } - trinary Character::consume( item & target, bool force ) { - if( target.is_null() ) { - add_msg_if_player( m_info, _( "You do not have that item." ) ); - return trinary::NONE; - } - if( ( !has_trait( trait_WATERSLEEP ) && !has_trait( trait_UNDINE_SLEEP_WATER ) ) && - cant_do_underwater() ) { - return trinary::NONE; - } + bool Character::can_estimate_rot() const { + return get_greater_skill_or_knowledge_level( skill_cooking ) >= 3 || + get_greater_skill_or_knowledge_level( skill_survival ) >= 4; + } - item &Character::get_consumable_from( item & it ) const { - item *ret = nullptr; - it.visit_items( [&]( item * it, item * ) { - if( can_consume_as_is( *it ) ) { - ret = it; - return VisitResponse::ABORT; + bool Character::can_consume_as_is( const item & it ) const { + if( it.is_comestible() ) { + return !it.has_flag( flag_FROZEN ) || it.has_flag( flag_EDIBLE_FROZEN ) || + it.has_flag( flag_MELTS ); } - return VisitResponse::NEXT; - } ); - - if( ret != nullptr ) { - return *ret; + return false; } - static item null_comestible; - // Since it's not const. - null_comestible = item(); - return null_comestible; - } + item &Character::get_consumable_from( item & it ) const { + item *ret = nullptr; + it.visit_items( [&]( item * it, item * ) { + if( can_consume_as_is( *it ) ) { + ret = it; + return VisitResponse::ABORT; + } + return VisitResponse::NEXT; + } ); + + if( ret != nullptr ) { + return *ret; + } - time_duration Character::get_consume_time( const item & it ) const { - const int charges = std::max( it.charges, 1 ); - int volume = units::to_milliliter( it.volume() ) / charges; - if( 0 == volume && it.type ) { - volume = units::to_milliliter( it.type->volume ); + static item null_comestible; + // Since it's not const. + null_comestible = item(); + return null_comestible; } - time_duration time = time_duration::from_seconds( std::max( ( volume / - 5 ), 1 ) ); //Default 5 mL (1 tablespoon) per second - float consume_time_modifier = 1.0f;//only for food and drinks - const bool eat_verb = it.has_flag( flag_USE_EAT_VERB ); - const std::string comest_type = it.get_comestible() ? it.get_comestible()->comesttype : ""; - if( eat_verb || comest_type == "FOOD" ) { - time = time_duration::from_seconds( volume / 5 ); //Eat 5 mL (1 teaspoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } else if( !eat_verb && comest_type == "DRINK" ) { - time = time_duration::from_seconds( volume / 15 ); //Drink 15 mL (1 tablespoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } else if( use_function const *fun = it.type->get_use( "heal" ) ) { - time = time_duration::from_moves( dynamic_cast - ( fun->get_actor_ptr() )->move_cost ); - } else if( it.is_medication() ) { - const use_function *consume_drug = it.type->get_use( "consume_drug" ); - const use_function *smoking = it.type->get_use( "SMOKING" ); - const use_function *adrenaline_injector = it.type->get_use( "ADRENALINE_INJECTOR" ); - if( consume_drug != nullptr ) { //its a drug - const consume_drug_iuse *consume_drug_use = dynamic_cast - ( consume_drug->get_actor_ptr() ); - if( consume_drug_use->tools_needed.find( itype_syringe ) != consume_drug_use->tools_needed.end() && - has_bionic( bio_syringe ) ) { - time = time_duration::from_seconds( - 15 );//injections with the intradermal needle CBM are much quicker than with a normal syringe - } else if( consume_drug_use->tools_needed.find( itype_syringe ) != - consume_drug_use->tools_needed.end() ) { - time = time_duration::from_minutes( 5 );//sterile injections take 5 minutes - } else if( consume_drug_use->tools_needed.find( itype_apparatus ) != - consume_drug_use->tools_needed.end() || - consume_drug_use->tools_needed.find( itype_dab_pen_on ) != consume_drug_use->tools_needed.end() ) { - time = time_duration::from_seconds( 30 );//smoke a bowl + + time_duration Character::get_consume_time( const item & it ) const { + const int charges = std::max( it.charges, 1 ); + int volume = units::to_milliliter( it.volume() ) / charges; + if( 0 == volume && it.type ) { + volume = units::to_milliliter( it.type->volume ); + } + time_duration time = time_duration::from_seconds( std::max( ( volume / + 5 ), 1 ) ); //Default 5 mL (1 tablespoon) per second + float consume_time_modifier = 1.0f;//only for food and drinks + const bool eat_verb = it.has_flag( flag_USE_EAT_VERB ); + const std::string comest_type = it.get_comestible() ? it.get_comestible()->comesttype : ""; + if( eat_verb || comest_type == "FOOD" ) { + time = time_duration::from_seconds( volume / 5 ); //Eat 5 mL (1 teaspoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } else if( !eat_verb && comest_type == "DRINK" ) { + time = time_duration::from_seconds( volume / 15 ); //Drink 15 mL (1 tablespoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } else if( use_function const *fun = it.type->get_use( "heal" ) ) { + time = time_duration::from_moves( dynamic_cast + ( fun->get_actor_ptr() )->move_cost ); + } else if( it.is_medication() ) { + const use_function *consume_drug = it.type->get_use( "consume_drug" ); + const use_function *smoking = it.type->get_use( "SMOKING" ); + const use_function *adrenaline_injector = it.type->get_use( "ADRENALINE_INJECTOR" ); + if( consume_drug != nullptr ) { //its a drug + const consume_drug_iuse *consume_drug_use = dynamic_cast + ( consume_drug->get_actor_ptr() ); + if( consume_drug_use->tools_needed.find( itype_syringe ) != consume_drug_use->tools_needed.end() && + has_bionic( bio_syringe ) ) { + time = time_duration::from_seconds( + 15 );//injections with the intradermal needle CBM are much quicker than with a normal syringe + } else if( consume_drug_use->tools_needed.find( itype_syringe ) != + consume_drug_use->tools_needed.end() ) { + time = time_duration::from_minutes( 5 );//sterile injections take 5 minutes + } else if( consume_drug_use->tools_needed.find( itype_apparatus ) != + consume_drug_use->tools_needed.end() || + consume_drug_use->tools_needed.find( itype_dab_pen_on ) != consume_drug_use->tools_needed.end() ) { + time = time_duration::from_seconds( 30 );//smoke a bowl + } else { + time = time_duration::from_seconds( 5 );//popping a pill is quick + } + } else if( smoking != nullptr ) { + time = time_duration::from_minutes( 1 );//about five minutes for a cig or joint so 1 minute a charge + } else if( adrenaline_injector != nullptr ) { + //epi-pens, and disinfectant are fairly quick + time = time_duration::from_seconds( 15 ); } else { - time = time_duration::from_seconds( 5 );//popping a pill is quick + time = time_duration::from_seconds( 5 ); //probably pills so quick } - } else if( smoking != nullptr ) { - time = time_duration::from_minutes( 1 );//about five minutes for a cig or joint so 1 minute a charge - } else if( adrenaline_injector != nullptr ) { - //epi-pens, and disinfectant are fairly quick - time = time_duration::from_seconds( 15 ); - } else { - time = time_duration::from_seconds( 5 ); //probably pills so quick + } else if( it.get_category_shallow().get_id() == item_category_chems ) { + time = time_duration::from_seconds( std::max( ( volume / 15 ), + 1 ) ); //Consume 15 mL (1 tablespoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); } - } else if( it.get_category_shallow().get_id() == item_category_chems ) { - time = time_duration::from_seconds( std::max( ( volume / 15 ), - 1 ) ); //Consume 15 mL (1 tablespoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } - // Minimum consumption time, without mutations, is always 1 second. - time = std::max( 1_seconds, time ); + nutrients food_nutrients = compute_effective_nutrients( food ); + const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? + food.get_comestible()->quench * + 5_ml : 0_ml; + units::volume food_vol = masticated_volume( food ); + if( food.count() == 0 ) { + debugmsg( "Tried to eat food with count of zero." ); + return false; + } + units::mass food_weight = ( food.weight() / food.count() ); + const double ratio = compute_effective_food_volume_ratio( food ); + food_summary ingested{ + water_vol, + food_vol * ratio, + food_nutrients + }; + add_msg_debug( debugmode::DF_FOOD, + "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", + units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, + food_nutrients.kcal(), units::to_gram( food_weight ) ); + // Maybe move tapeworm to digestion + if( has_effect( effect_tapeworm ) ) { + ingested.nutr /= 2; + } + // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? + activate_consume_eocs( *this, food ); + + // GET IN MAH BELLY! + stomach.ingest( ingested ); - return time * consume_time_modifier; - } + static bool query_consume_ownership( item & target, Character & p ) { + if( !target.is_owned_by( p, true ) ) { + bool choice = true; + if( p.get_value( "THIEF_MODE" ) == "THIEF_ASK" ) { + choice = Pickup::query_thief(); + } + if( p.get_value( "THIEF_MODE" ) == "THIEF_HONEST" || !choice ) { + return false; + } + std::vector witnesses; + for( npc &elem : g->all_npcs() ) { + if( rl_dist( elem.pos(), p.pos() ) < MAX_VIEW_DISTANCE && elem.sees( p.pos() ) ) { + witnesses.push_back( &elem ); + } + } + for( npc *elem : witnesses ) { + elem->say( "", 7 ); + } + if( !witnesses.empty() && target.is_owned_by( p, true ) ) { + if( p.add_faction_warning( target.get_owner() ) ) { + for( npc *elem : witnesses ) { + elem->make_angry(); + } + } + } + } + return true; + } - get_event_bus().send( getID(), target.typeId() ); + /** Consume medication. + * @return true if item consumed. + */ + static bool consume_med( item & target, Character & you ) { + if( !target.is_medication() ) { + return false; + } - invalidate_weight_carried_cache(); - target.on_contents_changed(); - return !target.count_by_charges() || target.charges <= 0 ? trinary::ALL : trinary::SOME; - } + const itype_id tool_type = target.get_comestible()->tool; + const itype *req_tool = item::find_type( tool_type ); - return trinary::NONE; - } + if( req_tool->tool ) { + if( !( you.has_amount( tool_type, 1 ) && + you.has_charges( tool_type, req_tool->tool->charges_per_use ) ) ) { + you.add_msg_if_player( m_info, _( "You need a %s to consume that!" ), req_tool->nname( 1 ) ); + return false; + } + you.use_charges( tool_type, req_tool->tool->charges_per_use ); + } - trinary Character::consume( item_location loc, bool force ) { - if( !loc ) { - debugmsg( "Null loc to consume." ); - return trinary::NONE; - } - contents_change_handler handler; - item &target = *loc; - trinary result = consume( target, force ); - if( result != trinary::NONE ) { - handler.unseal_pocket_containing( loc ); - } - if( result == trinary::ALL ) { - if( loc.where() == item_location::type::character ) { - i_rem( loc.get_item() ); - } else { - loc.remove_item(); - } - } - handler.handle_by( *this ); - return result; - } + int amount_used = 1; + if( target.type->has_use() ) { + amount_used = target.type->invoke( &you, target, you.pos() ).value_or( 0 ); + if( amount_used <= 0 ) { + return false; + } + } + + // Used in hibernation messages. + const int nutr = nutrition_for( food ); + const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; + // We can handle junk just fine + if( !skip_health ) { + modify_health( comest ); + } + modify_stimulation( comest ); + modify_fatigue( comest ); + modify_addiction( comest ); + modify_morale( food, nutr ); + + const bool hibernate = has_active_mutation( trait_HIBERNATE ); + if( hibernate ) { + if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { + // Tell the player what's going on + add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); + if( one_in( 2 ) ) { + // 50% chance of the food tiring you + mod_fatigue( nutr ); + } + } + if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { + // Hibernation should cut burn to 60/day + add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); + if( one_in( 2 ) ) { + // And another 50%, intended cumulative + mod_fatigue( nutr ); + } + } + + if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { + add_msg_if_player( + _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); + if( !one_in( 3 ) ) { + // Third check, this one at 66% + mod_fatigue( nutr ); + } + } + if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { + add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); + // At this point, you're done. Schlaf gut. + mod_fatigue( nutr ); + } + } + // Moved here and changed a bit - it was too complex + // Incredibly minor stuff like this shouldn't require complexity + if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && + ( get_healthy_kcal() < get_stored_kcal() + 4000 && + get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { + add_msg_if_player( m_mixed, + _( "You feel as though you're going to split open! In a good way?" ) ); + mod_pain( 5 ); + int numslime = 1; + for( int i = 0; i < numslime; i++ ) { + if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { + slime->friendly = -1; + } + } + + nutrients food_nutrients = compute_effective_nutrients( food ); + const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? + food.get_comestible()->quench * + 5_ml : 0_ml; + units::volume food_vol = masticated_volume( food ); + if( food.count() == 0 ) { + debugmsg( "Tried to eat food with count of zero." ); + return false; + } + units::mass food_weight = ( food.weight() / food.count() ); + const double ratio = compute_effective_food_volume_ratio( food ); + food_summary ingested{ + water_vol, + food_vol * ratio, + food_nutrients + }; + add_msg_debug( debugmode::DF_FOOD, + "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", + units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, + food_nutrients.kcal(), units::to_gram( food_weight ) ); + // Maybe move tapeworm to digestion + if( has_effect( effect_tapeworm ) ) { + ingested.nutr /= 2; + } + // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? + activate_consume_eocs( *this, food ); + + // GET IN MAH BELLY! + stomach.ingest( ingested ); + + // update speculative values + if( is_avatar() ) { + get_avatar().add_ingested_kcal( ingested.nutr.calories / 1000 ); + } + for( const auto &v : ingested.nutr.vitamins() ) { + // update the estimated values for daily vitamins + // actual vitamins happen during digestion + daily_vitamins[v.first].first += v.second; + } + + return true; + } + + if( target.count_by_charges() ) { + target.mod_charges( -amount_used ); + } + return true; + } + + trinary Character::consume( item & target, bool force ) { + if( target.is_null() ) { + add_msg_if_player( m_info, _( "You do not have that item." ) ); + return trinary::NONE; + } + if( ( !has_trait( trait_WATERSLEEP ) && !has_trait( trait_UNDINE_SLEEP_WATER ) ) && + cant_do_underwater() ) { + return trinary::NONE; + } + + item &Character::get_consumable_from( item & it ) const { + item *ret = nullptr; + it.visit_items( [&]( item * it, item * ) { + if( can_consume_as_is( *it ) ) { + ret = it; + return VisitResponse::ABORT; + } + return VisitResponse::NEXT; + } ); + + if( ret != nullptr ) { + return *ret; + } + + static item null_comestible; + // Since it's not const. + null_comestible = item(); + return null_comestible; + } + + time_duration Character::get_consume_time( const item & it ) const { + const int charges = std::max( it.charges, 1 ); + int volume = units::to_milliliter( it.volume() ) / charges; + if( 0 == volume && it.type ) { + volume = units::to_milliliter( it.type->volume ); + } + time_duration time = time_duration::from_seconds( std::max( ( volume / + 5 ), 1 ) ); //Default 5 mL (1 tablespoon) per second + float consume_time_modifier = 1.0f;//only for food and drinks + const bool eat_verb = it.has_flag( flag_USE_EAT_VERB ); + const std::string comest_type = it.get_comestible() ? it.get_comestible()->comesttype : ""; + if( eat_verb || comest_type == "FOOD" ) { + time = time_duration::from_seconds( volume / 5 ); //Eat 5 mL (1 teaspoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } else if( !eat_verb && comest_type == "DRINK" ) { + time = time_duration::from_seconds( volume / 15 ); //Drink 15 mL (1 tablespoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } else if( use_function const *fun = it.type->get_use( "heal" ) ) { + time = time_duration::from_moves( dynamic_cast + ( fun->get_actor_ptr() )->move_cost ); + } else if( it.is_medication() ) { + const use_function *consume_drug = it.type->get_use( "consume_drug" ); + const use_function *smoking = it.type->get_use( "SMOKING" ); + const use_function *adrenaline_injector = it.type->get_use( "ADRENALINE_INJECTOR" ); + if( consume_drug != nullptr ) { //its a drug + const consume_drug_iuse *consume_drug_use = dynamic_cast + ( consume_drug->get_actor_ptr() ); + if( consume_drug_use->tools_needed.find( itype_syringe ) != consume_drug_use->tools_needed.end() && + has_bionic( bio_syringe ) ) { + time = time_duration::from_seconds( + 15 );//injections with the intradermal needle CBM are much quicker than with a normal syringe + } else if( consume_drug_use->tools_needed.find( itype_syringe ) != + consume_drug_use->tools_needed.end() ) { + time = time_duration::from_minutes( 5 );//sterile injections take 5 minutes + } else if( consume_drug_use->tools_needed.find( itype_apparatus ) != + consume_drug_use->tools_needed.end() || + consume_drug_use->tools_needed.find( itype_dab_pen_on ) != consume_drug_use->tools_needed.end() ) { + time = time_duration::from_seconds( 30 );//smoke a bowl + } else { + time = time_duration::from_seconds( 5 );//popping a pill is quick + } + } else if( smoking != nullptr ) { + time = time_duration::from_minutes( 1 );//about five minutes for a cig or joint so 1 minute a charge + } else if( adrenaline_injector != nullptr ) { + //epi-pens, and disinfectant are fairly quick + time = time_duration::from_seconds( 15 ); + } else { + time = time_duration::from_seconds( 5 ); //probably pills so quick + } + } else if( it.get_category_shallow().get_id() == item_category_chems ) { + time = time_duration::from_seconds( std::max( ( volume / 15 ), + 1 ) ); //Consume 15 mL (1 tablespoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } + + // Minimum consumption time, without mutations, is always 1 second. + time = std::max( 1_seconds, time ); + + return time * consume_time_modifier; + } + + get_event_bus().send( getID(), target.typeId() ); + + invalidate_weight_carried_cache(); + target.on_contents_changed(); + return !target.count_by_charges() || target.charges <= 0 ? trinary::ALL : trinary::SOME; + } + + return trinary::NONE; + } + + trinary Character::consume( item_location loc, bool force ) { + if( !loc ) { + debugmsg( "Null loc to consume." ); + return trinary::NONE; + } + contents_change_handler handler; + item &target = *loc; + trinary result = consume( target, force ); + if( result != trinary::NONE ) { + handler.unseal_pocket_containing( loc ); + } + if( result == trinary::ALL ) { + if( loc.where() == item_location::type::character ) { + i_rem( loc.get_item() ); + } else { + loc.remove_item(); + } + } + handler.handle_by( *this ); + return result; + } From 1aaedc98cf2c79b3f19fa5d6bacc61f784a59dba Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 01:53:41 -0800 Subject: [PATCH 132/202] math_parser_diag instead of condition --- data/json/effects.json | 4 +- .../mutation_eocs/mutation_effect_eocs.json | 24 +- src/condition.cpp | 30 - src/consumption.cpp | 2031 +++++++---------- src/math_parser_diag.cpp | 70 + 5 files changed, 924 insertions(+), 1235 deletions(-) diff --git a/data/json/effects.json b/data/json/effects.json index 0d35032fbbc4a..f9e0db9ec109b 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -3955,6 +3955,7 @@ "id": "social_satisfied", "name": [ "Good Company" ], "desc": [ "It feels good to spend time around others." ], + "max_duration": "6 h", "max_intensity": 1, "rating": "good" }, @@ -3976,12 +3977,13 @@ "name": [ "Alone" ], "desc": [ "Being on your own makes you feel good." ], "max_intensity": 1, + "max_duration": "6 h", "rating": "good" }, { "type": "effect_type", "id": "asocial_dissatisfied", - "name": [ "Irritable", "Anxious", "Crowded" ], + "name": [ "Irritable", "Annoyed", "Crowded" ], "desc": [ "Being around others has left you feeling drained.", "OK, that's enough social time. You'd really rather be alone now.", diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index 0665e33f8b042..34f83ee1e4c23 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -705,11 +705,11 @@ "and": [ { "u_has_flag": "SOCIAL1" }, { "not": { "u_has_effect": "took_prozac" } }, - { "u_can_see_friends": 1 }, + { "math": [ "u_friends_nearby('radius': 30)", ">", "0" ] }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_lose_effect": "social_dissatisfied" }, { "u_add_effect": "social_satisfied", "duration": "2 hours" } ] + "effect": [ { "u_lose_effect": "social_dissatisfied" }, { "u_lose_effect": "social_satisfied" }, { "u_add_effect": "social_satisfied", "duration": "2 hours" } ] }, { "type": "effect_on_condition", @@ -719,14 +719,14 @@ "and": [ { "u_has_flag": "SOCIAL2" }, { "not": { "u_has_effect": "took_prozac" } }, - { "u_can_see_friends": 1 }, + { "math": [ "u_friends_nearby('radius': 30)", ">", "0" ] }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_lose_effect": "social_dissatisfied" }, - { "u_add_effect": "social_satisfied", "duration": "6 hours" }, - { "u_lose_effect": "social_dissatisfied" } + { "u_lose_effect": "social_satisfied" }, + { "u_add_effect": "social_satisfied", "duration": "6 hours" } ] }, { @@ -736,7 +736,7 @@ "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, - { "u_can_see_friends": 0 }, + { "math": [ "u_friends_nearby('radius': 30)", "==", "0" ] }, { "not": { "u_has_effect": "took_prozac" } }, { "not": { "u_has_effect": "valium" } }, { "not": { "u_has_effect": "took_xanax" } }, @@ -763,11 +763,11 @@ "and": [ { "u_has_flag": "ASOCIAL1" }, { "not": { "u_has_effect": "took_prozac" } }, - { "u_can_see_friends": 0 }, + { "math": [ "u_friends_nearby('radius': 30)", "==", "0" ] }, { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "2 hours" } ] + "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_lose_effect": "asocial_satisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "2 hours" } ] }, { "type": "effect_on_condition", @@ -776,15 +776,14 @@ "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, - { "u_can_see_friends": 0 }, + { "math": [ "u_friends_nearby('radius': 30)", "==", "0" ] }, { "not": { "u_has_effect": "took_prozac" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, - { "u_add_effect": "asocial_satisfied", "duration": "6 hours" }, - { "u_lose_effect": "asocial_dissatisfied" } + { "u_add_effect": "asocial_satisfied", "duration": "6 hours" } ] }, { @@ -799,8 +798,7 @@ { "not": { "u_has_effect": "valium" } }, { "not": { "u_has_effect": "took_xanax" } }, { "not": { "u_has_effect": "drunk" } }, - { "u_can_see_friends": 1 }, - { "not": { "u_has_effect": "asocial_satisfied" } }, + { "math": [ "u_friends_nearby('radius': 30)", ">", "0" ] }, { "not": { "u_has_effect": "narcosis" } } ] }, diff --git a/src/condition.cpp b/src/condition.cpp index 62517c0f08e8e..7453df288a0a4 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -491,35 +491,6 @@ conditional_t::func f_has_trait( const JsonObject &jo, std::string_view member, }; } - -conditional_t::func f_can_see_friends( const JsonObject &jo, std::string_view member, bool is_npc ) -{ - dbl_or_var dov = get_dbl_or_var( jo, member ); - return [ dov, is_npc ]( dialogue & d ) { - Character &player_character = get_player_character(); - const Character *ch = d.actor( is_npc )->get_character(); - int seen = 0; - if( is_npc ) { - if( ch->sees( player_character ) && ch->as_npc()->is_friendly( player_character ) ) { - seen += 1; - } - for( npc &guy : g->all_npcs() ) { - if( ch->sees( guy ) && guy.is_friendly( *ch ) ) { - seen += 1; - } - } - } else if( !is_npc ) { - int seen = 0; - for( npc &guy : g->all_npcs() ) { - if( get_player_view().sees( guy ) && guy.is_friendly( player_character ) ) { - seen += 1; - } - } - } - return seen >= dov.evaluate( d ); - }; -} - conditional_t::func f_has_visible_trait( const JsonObject &jo, std::string_view member, bool is_npc ) { @@ -2309,7 +2280,6 @@ parsers = { {"u_is_in_field", "npc_is_in_field", jarg::member, &conditional_fun::f_is_in_field }, {"u_has_move_mode", "npc_has_move_mode", jarg::member, &conditional_fun::f_has_move_mode }, {"u_can_see_location", "npc_can_see_location", jarg::member, &conditional_fun::f_can_see_location }, - {"u_can_see_friends", "npc_can_see_friends", jarg::member | jarg::array, &conditional_fun::f_can_see_friends }, {"is_weather", jarg::member, &conditional_fun::f_is_weather }, {"map_terrain_with_flag", jarg::member, &conditional_fun::f_map_ter_furn_with_flag }, {"map_furniture_with_flag", jarg::member, &conditional_fun::f_map_ter_furn_with_flag }, diff --git a/src/consumption.cpp b/src/consumption.cpp index 99c1d8bad2125..a68aa5d1a0ff8 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -165,8 +165,6 @@ static const trait_id trait_VEGAN( "VEGAN" ); static const trait_id trait_VEGETARIAN( "VEGETARIAN" ); static const trait_id trait_WATERSLEEP( "WATERSLEEP" ); -static const vitamin_id vitamin_mutant_toxin( "mutant_toxin" ); - // note: cannot use constants from flag.h (e.g. flag_ALLERGEN_VEGGY) here, as they // might be uninitialized at the time these const arrays are created static const std::array carnivore_blacklist {{ @@ -953,7 +951,7 @@ ret_val Character::will_eat( const item &food, bool interactive ) ( food.has_flag( flag_STRICT_HUMANITARIANISM ) && !has_flag( json_flag_STRICT_HUMANITARIAN ) ); if( ( food_is_human_flesh && !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) && - !has_flag( json_flag_PSYCHOPATH ) ) && + !has_flag( json_flag_PSYCHOPATH ) && !has_flag( json_flag_SAPIOVORE ) ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_HEMOVORE ) && !has_flag( json_flag_BLOODFEEDER ) ) ) ) { add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM ); @@ -1019,1231 +1017,882 @@ ret_val Character::will_eat( const item &food, bool interactive ) if( !query_yn( req ) ) { return consequences.front(); } - // All checks ended, it's edible (or we're pretending it is) - return ret_val::make_success(); + } + // All checks ended, it's edible (or we're pretending it is) + return ret_val::make_success(); +} + +/** Eat a comestible. +* @return true if item consumed. +*/ +static bool eat( item &food, Character &you, bool force ) +{ + if( !food.is_food() ) { + return false; } - /** Eat a comestible. - * @return true if item consumed. - */ - static bool eat( item & food, Character & you, bool force ) { - if( !food.is_food() ) { - return false; + const auto ret = force ? you.can_eat( food ) : you.will_eat( food, you.is_avatar() ); + if( !ret.success() ) { + return false; + } + + int charges_used = 0; + if( food.type->has_use() ) { + if( !food.type->can_use( "PETFOOD" ) ) { + charges_used = food.type->invoke( &you, food, you.pos() ).value_or( 0 ); + if( charges_used <= 0 ) { + return false; + } } + } - const auto ret = force ? you.can_eat( food ) : you.will_eat( food, you.is_avatar() ); - if( !ret.success() ) { - return false; + // Note: the block below assumes we decided to eat it + // No coming back from here + + if( food.is_container() ) { + food.spill_contents( you ); + } + + const bool hibernate = you.has_active_mutation( trait_HIBERNATE ); + const int nutr = you.nutrition_for( food ); + const int quench = food.get_comestible()->quench; + const bool spoiled = food.rotten(); + + // The item is solid food + const bool chew = food.get_comestible()->comesttype == comesttype_FOOD || + food.has_flag( flag_USE_EAT_VERB ); + // This item is a drink and not a solid food (and not a thick soup) + const bool drinkable = !chew && food.get_comestible()->comesttype == comesttype_DRINK; + // If neither of the above is true then it's a drug and shouldn't get mealtime penalty/bonus + + if( hibernate && + ( you.get_hunger() > -60 && you.get_thirst() > -60 ) && + ( you.get_hunger() - nutr < -60 || you.get_thirst() - quench < -60 ) ) { + you.add_msg_if_player( + _( "You've begun stockpiling calories and liquid for hibernation. You get the feeling that you should prepare for bed, just in case, but… you're hungry again, and you could eat a whole week's worth of food RIGHT NOW." ) ); + } + + const bool will_vomit = you.stomach.stomach_remaining( you ) < food.volume() && + rng( units::to_milliliter( you.stomach.capacity( you ) ) / 2, + units::to_milliliter( you.stomach.contains() ) ) > units::to_milliliter( + you.stomach.capacity( you ) ); + const bool saprophage = you.has_trait( trait_SAPROPHAGE ); + if( spoiled && !saprophage ) { + you.add_msg_if_player( m_bad, _( "Ick, this %s doesn't taste so good…" ), food.tname() ); + if( !you.has_flag( json_flag_IMMUNE_SPOIL ) ) { + you.add_effect( effect_foodpoison, rng( 6_minutes, ( nutr + 1 ) * 6_minutes ) ); + } + } else if( spoiled && saprophage ) { + you.add_msg_if_player( m_good, _( "Mmm, this %s tastes delicious…" ), food.tname() ); + } + if( !you.consume_effects( food ) ) { + // Already consumed by using `food.type->invoke`? + if( charges_used > 0 ) { + if( food.count_by_charges() ) { + food.mod_charges( -charges_used ); + } + return true; } + return false; + } + if( food.count_by_charges() ) { + food.mod_charges( -1 ); + } - int charges_used = 0; - if( food.type->has_use() ) { - if( !food.type->can_use( "PETFOOD" ) ) { - charges_used = food.type->invoke( &you, food, you.pos() ).value_or( 0 ); - if( charges_used <= 0 ) { - return false; - } + const bool amorphous = you.has_trait( trait_AMORPHOUS ); + + // If it's poisonous... poison us. + // TODO: Move this to a flag + if( food.poison > 0 && + !you.has_trait( trait_EATDEAD ) ) { + if( food.poison >= rng( 2, 4 ) ) { + you.add_effect( effect_poison, food.poison * 10_minutes ); + } + + you.add_effect( effect_foodpoison, food.poison * 30_minutes ); + } + + if( food.has_flag( flag_HIDDEN_HALLU ) ) { + if( !you.has_effect( effect_hallu ) ) { + you.add_effect( effect_hallu, 6_hours ); + } + } + + if( amorphous ) { + you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), + food.tname() ); + } else if( drinkable ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { + + add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); + add_msg( _( "You drink your %s (rotten)." ), food.tname() ); + } else { + you.add_msg_player_or_npc( _( "You drink your %s." ), _( " drinks a %s." ), + food.tname() ); + } + } else if( chew ) { + if( you.has_trait( trait_SCHIZOPHRENIC ) && + !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && + you.is_avatar() ) { + + add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); + add_msg( _( "You eat your %s (rotten)." ), food.tname() ); + } else { + you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), + food.tname() ); + } + } + + if( item::find_type( food.get_comestible()->tool )->tool ) { + // Tools like lighters get used + you.use_charges( food.get_comestible()->tool, 1 ); + } + + if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && + you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { + you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); + } + + if( food.has_flag( flag_FUNGAL_VECTOR ) && !you.has_trait( trait_M_IMMUNE ) ) { + you.add_effect( effect_fungus, 1_turns, true ); + } + + // The fun changes for these effects are applied in fun_for(). + if( food.has_flag( flag_MUSHY ) ) { + you.add_msg_if_player( m_bad, + _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); + } + if( food.get_comestible_fun() > 0 ) { + if( you.has_effect( effect_common_cold ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); + } + if( you.has_effect( effect_flu ) ) { + you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); + } + } + + // Chance to become parasitised + if( !will_vomit && !you.has_flag( json_flag_PARAIMMUNE ) ) { + if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && + one_in( food.get_comestible()->parasites ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || + ( !you.has_flag( json_flag_HEMOVORE ) && !you.has_flag( json_flag_BLOODFEEDER ) ) ) ) { + switch( rng( 0, 3 ) ) { + case 0: + if( !you.has_trait( trait_EATHEALTH ) ) { + you.add_effect( effect_tapeworm, 1_turns, true ); + } + break; + case 1: + if( !you.has_trait( trait_ACIDBLOOD ) ) { + you.add_effect( effect_bloodworms, 1_turns, true ); + } + break; + case 2: + you.add_effect( effect_brainworms, 1_turns, true ); + break; + case 3: + you.add_effect( effect_paincysts, 1_turns, true ); } } + } + + for( const std::pair &elem : food.get_comestible()->contamination ) { + if( rng( 1, 100 ) <= elem.second ) { + you.expose_to_disease( elem.first ); + } + } + + get_event_bus().send( you.getID(), food.typeId() ); + + if( will_vomit ) { + you.vomit(); + } + + you.consumption_history.emplace_back( food ); + // Clean out consumption_history so it doesn't get bigger than needed. + while( you.consumption_history.front().time < calendar::turn - 2_days ) { + you.consumption_history.pop_front(); + } + + you.recoil = MAX_RECOIL; + + return true; +} + +void Character::modify_health( const islot_comestible &comest ) +{ + const int effective_health = comest.healthy; + // Effectively no cap on health modifiers from food and meds + const int health_cap = 200; + mod_daily_health( effective_health, effective_health >= 0 ? health_cap : -health_cap ); +} + +void Character::modify_stimulation( const islot_comestible &comest ) +{ + if( comest.stim == 0 ) { + return; + } + const int current_stim = get_stim(); + if( ( std::abs( comest.stim ) * 3 ) > std::abs( current_stim ) ) { + mod_stim( comest.stim ); + } else { + comest.stim > 0 ? mod_stim( std::max( comest.stim / 2, 1 ) ) : mod_stim( std::min( comest.stim / 2, + -1 ) ); + } + if( has_trait( trait_STIMBOOST ) && ( current_stim > 30 ) && + ( comest.addictions.count( STATIC( addiction_id( "caffeine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "amphetamine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "cocaine" ) ) ) || + comest.addictions.count( STATIC( addiction_id( "crack" ) ) ) ) ) { + int hallu_duration = ( current_stim - comest.stim < 30 ) ? current_stim - 30 : comest.stim; + add_effect( effect_visuals, hallu_duration * 30_minutes ); + add_msg_if_player( m_bad, SNIPPET.random_from_category( "comest_stimulant" ).value_or( + translation() ).translated() ); + } +} - // Note: the block below assumes we decided to eat it - // No coming back from here +void Character::modify_fatigue( const islot_comestible &comest ) +{ + mod_fatigue( -comest.fatigue_mod ); +} - if( food.is_container() ) { - food.spill_contents( you ); +void Character::modify_addiction( const islot_comestible &comest ) +{ + for( const std::pair &add : comest.addictions ) { + add_addiction( add.first, add.second ); + if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { + rem_morale( add.first->get_craving_morale() ); } + } +} - const bool hibernate = you.has_active_mutation( trait_HIBERNATE ); - const int nutr = you.nutrition_for( food ); - const int quench = food.get_comestible()->quench; - const bool spoiled = food.rotten(); +void Character::modify_morale( item &food, const int nutr ) +{ + time_duration morale_time = 2_hours; + if( food.has_flag( flag_HOT ) && food.has_flag( flag_EATEN_HOT ) ) { + morale_time = 3_hours; + int clamped_nutr = std::max( 5, std::min( 20, nutr / 10 ) ); + add_morale( MORALE_FOOD_HOT, clamped_nutr, 20, morale_time, morale_time / 2 ); + } + + std::pair fun = fun_for( food ); + if( fun.first < 0 ) { + add_morale( MORALE_FOOD_BAD, fun.first, fun.second, morale_time, morale_time / 2, false, + food.type ); + } else if( fun.first > 0 ) { + add_morale( MORALE_FOOD_GOOD, fun.first, fun.second, morale_time, morale_time / 2, false, + food.type ); + } + + // Morale bonus for eating unspoiled food with chair/table nearby + // Does not apply to non-ingested consumables like bandages or drugs, + // nor to drinks. + if( !food.has_flag( flag_NO_INGEST ) && + food.get_comestible()->comesttype != "MED" && + food.get_comestible()->comesttype != comesttype_DRINK ) { + map &here = get_map(); + if( here.has_nearby_chair( pos(), 1 ) && here.has_nearby_table( pos_bub(), 1 ) ) { + if( has_trait( trait_TABLEMANNERS ) ) { + rem_morale( MORALE_ATE_WITHOUT_TABLE ); + if( !food.rotten() ) { + add_morale( MORALE_ATE_WITH_TABLE, 3, 3, 3_hours, 2_hours, true ); + } + } else if( !food.rotten() ) { + add_morale( MORALE_ATE_WITH_TABLE, 1, 1, 3_hours, 2_hours, true ); + } + } else { + if( has_trait( trait_TABLEMANNERS ) ) { + rem_morale( MORALE_ATE_WITH_TABLE ); + add_morale( MORALE_ATE_WITHOUT_TABLE, -2, -4, 3_hours, 2_hours, true ); + } + } + } - // The item is solid food - const bool chew = food.get_comestible()->comesttype == comesttype_FOOD || - food.has_flag( flag_USE_EAT_VERB ); - // This item is a drink and not a solid food (and not a thick soup) - const bool drinkable = !chew && food.get_comestible()->comesttype == comesttype_DRINK; - // If neither of the above is true then it's a drug and shouldn't get mealtime penalty/bonus + if( food.has_flag( flag_HIDDEN_HALLU ) ) { + if( has_trait( trait_SPIRITUAL ) ) { + add_morale( MORALE_FOOD_GOOD, 36, 72, 2_hours, 1_hours, false ); + } else { + add_morale( MORALE_FOOD_GOOD, 18, 36, 1_hours, 30_minutes, false ); + } + } - if( hibernate && - ( you.get_hunger() > -60 && you.get_thirst() > -60 ) && - ( you.get_hunger() - nutr < -60 || you.get_thirst() - quench < -60 ) ) { - you.add_msg_if_player( - _( "You've begun stockpiling calories and liquid for hibernation. You get the feeling that you should prepare for bed, just in case, but… you're hungry again, and you could eat a whole week's worth of food RIGHT NOW." ) ); + const bool food_is_human_flesh = food.has_flag( flag_CANNIBALISM ) || + ( food.has_flag( flag_STRICT_HUMANITARIANISM ) && + !has_flag( json_flag_STRICT_HUMANITARIAN ) ); + if( food_is_human_flesh ) { + // Sapiovores don't recognize humans as the same species. + // But let them possibly feel cool about eating sapient stuff - treat like psycho + // However, spiritual sapiovores should still recognize humans as having a soul or special for religious reasons + const bool cannibal = has_flag( json_flag_CANNIBAL ); + const bool psycho = has_flag( json_flag_PSYCHOPATH ); + const bool sapiovore = has_flag( json_flag_SAPIOVORE ); + const bool spiritual = has_flag( json_flag_SPIRITUAL ); + const bool numb = has_flag( json_flag_NUMB ); + if( cannibal && psycho && spiritual ) { + add_msg_if_player( m_good, + _( "You feast upon the human flesh, and in doing so, devour their spirit." ) ); + // You're not really consuming anything special; you just think you are. + add_morale( MORALE_CANNIBAL, 25, 300 ); + } else if( cannibal && psycho ) { + add_msg_if_player( m_good, _( "You feast upon the human flesh." ) ); + add_morale( MORALE_CANNIBAL, 15, 200 ); + } else if( cannibal && spiritual ) { + add_msg_if_player( m_good, _( "You consume the sacred human flesh." ) ); + // Boosted because you understand the philosophical implications of your actions, and YOU LIKE THEM. + add_morale( MORALE_CANNIBAL, 15, 200 ); + } else if( sapiovore && spiritual ) { + add_msg_if_player( m_good, _( "You eat the human flesh, and in doing so, devour their spirit." ) ); + add_morale( MORALE_CANNIBAL, 10, 50 ); + } else if( cannibal ) { + add_msg_if_player( m_good, _( "You indulge your shameful hunger." ) ); + add_morale( MORALE_CANNIBAL, 10, 50 ); + } else if( psycho && spiritual ) { + add_msg_if_player( _( "You greedily devour the taboo meat." ) ); + // Small bonus for violating a taboo. + add_morale( MORALE_CANNIBAL, 5, 50 ); + } else if( psycho ) { + add_msg_if_player( _( "Meh. You've eaten worse." ) ); + } else if( sapiovore ) { + add_msg_if_player( _( "Mmh. Tastes like venison." ) ); + } else if( spiritual ) { + add_msg_if_player( m_bad, + _( "This is probably going to count against you if there's still an afterlife." ) ); + add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); + } else if( numb ) { + add_msg_if_player( m_bad, _( "You find this meal distasteful, but necessary." ) ); + add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); + } else { + add_msg_if_player( m_bad, _( "You feel horrible for eating a person." ) ); + add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); } + } - const bool will_vomit = you.stomach.stomach_remaining( you ) < food.volume() && - rng( units::to_milliliter( you.stomach.capacity( you ) ) / 2, - units::to_milliliter( you.stomach.contains() ) ) > units::to_milliliter( - you.stomach.capacity( you ) ); - const bool saprophage = you.has_trait( trait_SAPROPHAGE ); - if( spoiled && !saprophage ) { - you.add_msg_if_player( m_bad, _( "Ick, this %s doesn't taste so good…" ), food.tname() ); - if( !you.has_flag( json_flag_IMMUNE_SPOIL ) ) { - you.add_effect( effect_foodpoison, rng( 6_minutes, ( nutr + 1 ) * 6_minutes ) ); + // While raw flesh usually means negative morale, carnivores and cullers get a small bonus. + // Hunters, predators, and apex predators don't mind raw flesh at all, maybe even like it. + // Cooked flesh is unaffected, because people with these traits *prefer* it raw. Fat is unaffected. + // Organs are still usually negative due to fun values as low as -35. + // The PREDATOR_FUN flag shouldn't be on human flesh, to not interfere with sapiovores/cannibalism. + if( food.has_flag( flag_PREDATOR_FUN ) ) { + const bool carnivore = has_trait( trait_CARNIVORE ); + const bool culler = has_flag( json_flag_PRED1 ); + const bool hunter = has_flag( json_flag_PRED2 ); + const bool predator = has_flag( json_flag_PRED3 ); + const bool apex_predator = has_flag( json_flag_PRED4 ); + if( apex_predator ) { + // Largest bonus, balances out to around +5 or +10. Some organs may still be negative. + add_morale( MORALE_MEATARIAN, 20, 10 ); + add_msg_if_player( m_good, + _( "As you tear into the raw flesh, you feel satisfied with your meal." ) ); + } else if( predator || hunter ) { + // Should approximately balance the fun to 0 for normal meat. + add_morale( MORALE_MEATARIAN, 15, 5 ); + add_msg_if_player( m_good, + _( "Raw flesh doesn't taste all that bad, actually." ) ); + } else if( carnivore || culler ) { + // Only a small bonus (+5), still negative fun. + add_morale( MORALE_MEATARIAN, 5, 0 ); + add_msg_if_player( m_bad, + _( "This doesn't taste very good, but meat is meat." ) ); + } + } + + // Allergy check for food that is ingested (not gum) + if( !food.has_flag( flag_NO_INGEST ) ) { + const morale_type allergy = allergy_type( food ); + if( allergy != MORALE_NULL ) { + add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); + add_morale( allergy, -75, -400, 30_minutes, 24_minutes ); + } + if( food.has_flag( flag_ALLERGEN_JUNK ) ) { + if( has_trait( trait_PROJUNK ) ) { + add_msg_if_player( m_good, _( "Mmm, junk food." ) ); + add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); } - } else if( spoiled && saprophage ) { - you.add_msg_if_player( m_good, _( "Mmm, this %s tastes delicious…" ), food.tname() ); - } - if( !you.consume_effects( food ) ) { - // Already consumed by using `food.type->invoke`? - if( charges_used > 0 ) { - if( food.count_by_charges() ) { - food.mod_charges( -charges_used ); + if( has_trait( trait_PROJUNK2 ) ) { + if( !one_in( 100 ) ) { + add_msg_if_player( m_good, _( "When life's got you down, there's always sugar." ) ); + } else { + add_msg_if_player( m_good, _( "They may do what they must… you've already won." ) ); } - return true; + add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes ); + } + // Carnivores CAN eat junk food, but they won't like it much. + // Pizza-scraping happens in consume_effects. + if( has_trait( trait_CARNIVORE ) && !food.has_flag( flag_CARNIVORE_OK ) ) { + add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); + add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); } - return false; } - if( food.count_by_charges() ) { - food.mod_charges( -1 ); + } + const bool chew = food.get_comestible()->comesttype == comesttype_FOOD || + food.has_flag( flag_USE_EAT_VERB ); + if( !food.rotten() && chew && has_trait( trait_SAPROPHAGE ) ) { + // It's OK to *drink* things that haven't rotted. Alternative is to ban water. D: + add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); + add_morale( MORALE_NO_DIGEST, -75, -400, 30_minutes, 24_minutes ); + } + if( food.has_flag( flag_URSINE_HONEY ) && ( !crossed_threshold() || + has_trait( trait_THRESH_URSINE ) ) && + mutation_category_level[mutation_category_URSINE] > 20 ) { + int honey_fun = std::min( mutation_category_level[mutation_category_URSINE] / 5, 20 ); + if( honey_fun < 10 ) { + add_msg_if_player( m_good, _( "You find the sweet taste of honey surprisingly palatable." ) ); + } else { + add_msg_if_player( m_good, _( "You feast upon the sweet honey." ) ); + } + add_morale( MORALE_HONEY, honey_fun, 100 ); + } +} + +// Used when determining stomach fullness from eating. +double Character::compute_effective_food_volume_ratio( const item &food ) const +{ + const nutrients food_nutrients = compute_effective_nutrients( food ); + units::mass food_weight = ( food.weight() / std::max( 1, food.count() ) ); + double ratio = 1.0f; + if( units::to_gram( food_weight ) != 0 ) { + ratio = std::max( static_cast( food_nutrients.kcal() ) / units::to_gram( food_weight ), + 1.0 ); + if( ratio > 3.0f ) { + ratio = std::sqrt( 3 * ratio ); } + } + return ratio; +} - const bool amorphous = you.has_trait( trait_AMORPHOUS ); +// Remove the water volume from the food, as that gets absorbed and used as water. +// If the remaining dry volume of the food is less dense than water, crunch it down to a density equal to water. +// These maths are made easier by the fact that 1 g = 1 mL. Thanks, metric system. +units::volume Character::masticated_volume( const item &food ) const +{ + units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? food.get_comestible()->quench * + 5_ml : 0_ml; + units::mass water_weight = units::from_gram( units::to_milliliter( water_vol ) ); + // handle the division by zero exception when the food count is 0 with std::max() + units::mass food_dry_weight = food.weight() / std::max( 1, food.count() ) - water_weight; + units::volume food_dry_volume = food.volume() / std::max( 1, food.count() ) - water_vol; - // If it's poisonous... poison us. - // TODO: Move this to a flag - if( food.poison > 0 && - !you.has_trait( trait_EATDEAD ) ) { - if( food.poison >= rng( 2, 4 ) ) { - you.add_effect( effect_poison, food.poison * 10_minutes ); + if( units::to_milliliter( food_dry_volume ) != 0 && + units::to_gram( food_dry_weight ) < units::to_milliliter( food_dry_volume ) ) { + food_dry_volume = units::from_milliliter( units::to_gram( food_dry_weight ) ); + } + + return food_dry_volume; +} + +// Used when displaying effective food satiation values. +int Character::compute_calories_per_effective_volume( const item &food, + const nutrients *nutrient /* = nullptr */ )const +{ + /* Understanding how Calories Per Effective Volume are calculated requires a dive into the + stomach fullness source code. Look at issue #44365*/ + int kcalories; + if( nutrient ) { + // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). + kcalories = nutrient->kcal(); + } else { + kcalories = compute_effective_nutrients( food ).kcal(); + } + double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); + const double energy_density_ratio = compute_effective_food_volume_ratio( food ); + const double effective_volume = food_vol * energy_density_ratio; + if( kcalories == 0 && effective_volume == 0.0 ) { + return 0; + } + return std::round( kcalories / effective_volume ); +} + +static void activate_consume_eocs( Character &you, item &target ) +{ + Character *char_ptr = nullptr; + if( avatar *u = you.as_avatar() ) { + char_ptr = u; + } else if( npc *n = you.as_npc() ) { + char_ptr = n; + } + item_location loc( you, &target ); + dialogue d( get_talker_for( char_ptr ), get_talker_for( loc ) ); + const islot_comestible &comest = *target.get_comestible(); + for( const effect_on_condition_id &eoc : comest.consumption_eocs ) { + eoc->activate( d ); + } +} + +bool Character::consume_effects( item &food ) +{ + if( !food.is_comestible() ) { + debugmsg( "called Character::consume_effects with non-comestible" ); + return false; + } + + if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { + // Was used to cap nutrition and thirst, but no longer does this + return false; + } + if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && + food.has_any_flag( herbivore_blacklist ) ) { + // No good can come of this. + return false; + } + + const islot_comestible &comest = *food.get_comestible(); + + // Rotten food causes health loss + const float relative_rot = food.get_relative_rot(); + if( relative_rot > 1.0f && !has_flag( json_flag_IMMUNE_SPOIL ) ) { + const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f ); + // ~-1 health per 1 nutrition at halfway-rotten-away, ~0 at "just got rotten" + // But always round down + int h_loss = -rottedness * comest.get_default_nutr(); + mod_daily_health( h_loss, -200 ); + add_msg_debug( debugmode::DF_FOOD, "%d health from %0.2f%% rotten food", h_loss, rottedness ); + } + + // Used in hibernation messages. + const int nutr = nutrition_for( food ); + const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; + // We can handle junk just fine + if( !skip_health ) { + modify_health( comest ); + } + modify_stimulation( comest ); + modify_fatigue( comest ); + modify_addiction( comest ); + modify_morale( food, nutr ); + + const bool hibernate = has_active_mutation( trait_HIBERNATE ); + if( hibernate ) { + if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { + // Tell the player what's going on + add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); + if( one_in( 2 ) ) { + // 50% chance of the food tiring you + mod_fatigue( nutr ); + } + } + if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { + // Hibernation should cut burn to 60/day + add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); + if( one_in( 2 ) ) { + // And another 50%, intended cumulative + mod_fatigue( nutr ); } + } - // If it's poisonous... poison us. - // TODO: Move this to a flag - if( food.poison > 0 && - !you.has_trait( trait_EATDEAD ) ) { - if( food.poison >= rng( 2, 4 ) ) { - you.add_effect( effect_poison, food.poison * 10_minutes ); - } + if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { + add_msg_if_player( + _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); + if( !one_in( 3 ) ) { + // Third check, this one at 66% + mod_fatigue( nutr ); + } + } + if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { + add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); + // At this point, you're done. Schlaf gut. + mod_fatigue( nutr ); + } + } + // Moved here and changed a bit - it was too complex + // Incredibly minor stuff like this shouldn't require complexity + if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && + ( get_healthy_kcal() < get_stored_kcal() + 4000 && + get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { + add_msg_if_player( m_mixed, + _( "You feel as though you're going to split open! In a good way?" ) ); + mod_pain( 5 ); + int numslime = 1; + for( int i = 0; i < numslime; i++ ) { + if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { + slime->friendly = -1; + } + } + mod_hunger( 40 ); + mod_thirst( 40 ); + //~ slimespawns have *small voices* which may be the Nice equivalent + //~ of the Rat King's ALL CAPS invective. Probably shared-brain telepathy. + add_msg_if_player( m_good, _( "hey, you look like me! let's work together!" ) ); + } + + nutrients food_nutrients = compute_effective_nutrients( food ); + const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? + food.get_comestible()->quench * + 5_ml : 0_ml; + units::volume food_vol = masticated_volume( food ); + if( food.count() == 0 ) { + debugmsg( "Tried to eat food with count of zero." ); + return false; + } + units::mass food_weight = ( food.weight() / food.count() ); + const double ratio = compute_effective_food_volume_ratio( food ); + food_summary ingested{ + water_vol, + food_vol * ratio, + food_nutrients + }; + add_msg_debug( debugmode::DF_FOOD, + "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", + units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, + food_nutrients.kcal(), units::to_gram( food_weight ) ); + // Maybe move tapeworm to digestion + if( has_effect( effect_tapeworm ) ) { + ingested.nutr /= 2; + } + // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? + activate_consume_eocs( *this, food ); + + // GET IN MAH BELLY! + stomach.ingest( ingested ); - you.add_effect( effect_foodpoison, food.poison * 30_minutes ); + // update speculative values + if( is_avatar() ) { + get_avatar().add_ingested_kcal( ingested.nutr.calories / 1000 ); + } + for( const auto &v : ingested.nutr.vitamins() ) { + // update the estimated values for daily vitamins + // actual vitamins happen during digestion + daily_vitamins[v.first].first += v.second; + } + + return true; +} + +bool Character::can_estimate_rot() const +{ + return get_greater_skill_or_knowledge_level( skill_cooking ) >= 3 || + get_greater_skill_or_knowledge_level( skill_survival ) >= 4; +} + +bool Character::can_consume_as_is( const item &it ) const +{ + if( it.is_comestible() ) { + return !it.has_flag( flag_FROZEN ) || it.has_flag( flag_EDIBLE_FROZEN ) || + it.has_flag( flag_MELTS ); + } + return false; +} + +item &Character::get_consumable_from( item &it ) const +{ + item *ret = nullptr; + it.visit_items( [&]( item * it, item * ) { + if( can_consume_as_is( *it ) ) { + ret = it; + return VisitResponse::ABORT; + } + return VisitResponse::NEXT; + } ); + + if( ret != nullptr ) { + return *ret; + } + + static item null_comestible; + // Since it's not const. + null_comestible = item(); + return null_comestible; +} + +time_duration Character::get_consume_time( const item &it ) const +{ + const int charges = std::max( it.charges, 1 ); + int volume = units::to_milliliter( it.volume() ) / charges; + if( 0 == volume && it.type ) { + volume = units::to_milliliter( it.type->volume ); + } + time_duration time = time_duration::from_seconds( std::max( ( volume / + 5 ), 1 ) ); //Default 5 mL (1 tablespoon) per second + float consume_time_modifier = 1.0f;//only for food and drinks + const bool eat_verb = it.has_flag( flag_USE_EAT_VERB ); + const std::string comest_type = it.get_comestible() ? it.get_comestible()->comesttype : ""; + if( eat_verb || comest_type == "FOOD" ) { + time = time_duration::from_seconds( volume / 5 ); //Eat 5 mL (1 teaspoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } else if( !eat_verb && comest_type == "DRINK" ) { + time = time_duration::from_seconds( volume / 15 ); //Drink 15 mL (1 tablespoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } else if( use_function const *fun = it.type->get_use( "heal" ) ) { + time = time_duration::from_moves( dynamic_cast + ( fun->get_actor_ptr() )->move_cost ); + } else if( it.is_medication() ) { + const use_function *consume_drug = it.type->get_use( "consume_drug" ); + const use_function *smoking = it.type->get_use( "SMOKING" ); + const use_function *adrenaline_injector = it.type->get_use( "ADRENALINE_INJECTOR" ); + if( consume_drug != nullptr ) { //its a drug + const consume_drug_iuse *consume_drug_use = dynamic_cast + ( consume_drug->get_actor_ptr() ); + if( consume_drug_use->tools_needed.find( itype_syringe ) != consume_drug_use->tools_needed.end() && + has_bionic( bio_syringe ) ) { + time = time_duration::from_seconds( + 15 );//injections with the intradermal needle CBM are much quicker than with a normal syringe + } else if( consume_drug_use->tools_needed.find( itype_syringe ) != + consume_drug_use->tools_needed.end() ) { + time = time_duration::from_minutes( 5 );//sterile injections take 5 minutes + } else if( consume_drug_use->tools_needed.find( itype_apparatus ) != + consume_drug_use->tools_needed.end() || + consume_drug_use->tools_needed.find( itype_dab_pen_on ) != consume_drug_use->tools_needed.end() ) { + time = time_duration::from_seconds( 30 );//smoke a bowl + } else { + time = time_duration::from_seconds( 5 );//popping a pill is quick } + } else if( smoking != nullptr ) { + time = time_duration::from_minutes( 1 );//about five minutes for a cig or joint so 1 minute a charge + } else if( adrenaline_injector != nullptr ) { + //epi-pens, and disinfectant are fairly quick + time = time_duration::from_seconds( 15 ); + } else { + time = time_duration::from_seconds( 5 ); //probably pills so quick + } + } else if( it.get_category_shallow().get_id() == item_category_chems ) { + time = time_duration::from_seconds( std::max( ( volume / 15 ), + 1 ) ); //Consume 15 mL (1 tablespoon) per second + consume_time_modifier = mutation_value( "consume_time_modifier" ); + } + + // Minimum consumption time, without mutations, is always 1 second. + time = std::max( 1_seconds, time ); - if( food.has_flag( flag_HIDDEN_HALLU ) ) { - if( !you.has_effect( effect_hallu ) ) { - you.add_effect( effect_hallu, 6_hours ); + return time * consume_time_modifier; +} + +static bool query_consume_ownership( item &target, Character &p ) +{ + if( !target.is_owned_by( p, true ) ) { + bool choice = true; + if( p.get_value( "THIEF_MODE" ) == "THIEF_ASK" ) { + choice = Pickup::query_thief(); + } + if( p.get_value( "THIEF_MODE" ) == "THIEF_HONEST" || !choice ) { + return false; + } + std::vector witnesses; + for( npc &elem : g->all_npcs() ) { + if( rl_dist( elem.pos(), p.pos() ) < MAX_VIEW_DISTANCE && elem.sees( p.pos() ) ) { + witnesses.push_back( &elem ); + } + } + for( npc *elem : witnesses ) { + elem->say( "", 7 ); + } + if( !witnesses.empty() && target.is_owned_by( p, true ) ) { + if( p.add_faction_warning( target.get_owner() ) ) { + for( npc *elem : witnesses ) { + elem->make_angry(); } } + } + } + return true; +} - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { - - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { - - if( food.has_flag( flag_HIDDEN_HALLU ) ) { - if( !you.has_effect( effect_hallu ) ) { - you.add_effect( effect_hallu, 6_hours ); - } - } - } - - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { - - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { - - add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); - add_msg( _( "You eat your %s (rotten)." ), food.tname() ); - } else { - you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), - food.tname() ); - } - } - - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { - - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { - - add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); - add_msg( _( "You eat your %s (rotten)." ), food.tname() ); - } else { - you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), - food.tname() ); - } - } - - if( item::find_type( food.get_comestible()->tool )->tool ) { - // Tools like lighters get used - you.use_charges( food.get_comestible()->tool, 1 ); - } - - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { - - // The fun changes for these effects are applied in fun_for(). - if( food.has_flag( flag_MUSHY ) ) { - you.add_msg_if_player( m_bad, - _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); - } - if( food.get_comestible_fun() > 0 ) { - if( you.has_effect( effect_common_cold ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); - } - if( you.has_effect( effect_flu ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); - } - } - - if( amorphous ) { - you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), - food.tname() ); - } else if( drinkable ) { - if( you.has_trait( trait_SCHIZOPHRENIC ) && - !you.has_effect( effect_took_thorazine ) && one_in( 50 ) && !spoiled && food.goes_bad() && - you.is_avatar() ) { - - add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); - add_msg( _( "You eat your %s (rotten)." ), food.tname() ); - } else { - you.add_msg_player_or_npc( _( "You eat your %s." ), _( " eats a %s." ), - food.tname() ); - } - } - - if( you.has_active_bionic( bio_taste_blocker ) && food.get_comestible_fun() < 0 && - you.get_power_level() > units::from_kilojoule( std::abs( food.get_comestible_fun() ) ) ) { - you.mod_power_level( units::from_kilojoule( food.get_comestible_fun() ) ); - } - - // The fun changes for these effects are applied in fun_for(). - if( food.has_flag( flag_MUSHY ) ) { - you.add_msg_if_player( m_bad, - _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) ); - } - if( food.get_comestible_fun() > 0 ) { - if( you.has_effect( effect_common_cold ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) ); - } - if( you.has_effect( effect_flu ) ) { - you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) ); - } - } - - // Chance to become parasitised - if( !will_vomit && !you.has_flag( json_flag_PARAIMMUNE ) ) { - if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) && - one_in( food.get_comestible()->parasites ) && ( !food.has_flag( flag_HEMOVORE_FUN ) || - ( !you.has_flag( json_flag_HEMOVORE ) && !you.has_flag( json_flag_BLOODFEEDER ) ) ) ) { - switch( rng( 0, 3 ) ) { - case 0: - if( !you.has_trait( trait_EATHEALTH ) ) { - you.add_effect( effect_tapeworm, 1_turns, true ); - } - break; - case 1: - if( !you.has_trait( trait_ACIDBLOOD ) ) { - you.add_effect( effect_bloodworms, 1_turns, true ); - } - break; - case 2: - you.add_effect( effect_brainworms, 1_turns, true ); - break; - case 3: - you.add_effect( effect_paincysts, 1_turns, true ); - } - } - } - - for( const std::pair &elem : food.get_comestible()->contamination ) { - if( rng( 1, 100 ) <= elem.second ) { - you.expose_to_disease( elem.first ); - } - } - - get_event_bus().send( you.getID(), food.typeId() ); - - if( will_vomit ) { - you.vomit(); - } - - you.consumption_history.emplace_back( food ); - // Clean out consumption_history so it doesn't get bigger than needed. - while( you.consumption_history.front().time < calendar::turn - 2_days ) { - you.consumption_history.pop_front(); - } - - you.recoil = MAX_RECOIL; - - return true; - } - break; - case 2: - you.add_effect( effect_brainworms, 1_turns, true ); - break; - case 3: - you.add_effect( effect_paincysts, 1_turns, true ); - } - } - } - - for( const std::pair &elem : food.get_comestible()->contamination ) { - if( rng( 1, 100 ) <= elem.second ) { - you.expose_to_disease( elem.first ); - } - } - - get_event_bus().send( you.getID(), food.typeId() ); - - if( will_vomit ) { - you.vomit(); - } - - you.consumption_history.emplace_back( food ); - // Clean out consumption_history so it doesn't get bigger than needed. - while( you.consumption_history.front().time < calendar::turn - 2_days ) { - you.consumption_history.pop_front(); - } - - you.recoil = MAX_RECOIL; - - return true; - } - - return true; - } - - void Character::modify_stimulation( const islot_comestible & comest ) { - if( comest.stim == 0 ) { - return; - } - const int current_stim = get_stim(); - if( ( std::abs( comest.stim ) * 3 ) > std::abs( current_stim ) ) { - mod_stim( comest.stim ); - } else { - comest.stim > 0 ? mod_stim( std::max( comest.stim / 2, 1 ) ) : mod_stim( std::min( comest.stim / 2, - -1 ) ); - } - if( has_trait( trait_STIMBOOST ) && ( current_stim > 30 ) && - ( comest.addictions.count( STATIC( addiction_id( "caffeine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "amphetamine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "cocaine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "crack" ) ) ) ) ) { - int hallu_duration = ( current_stim - comest.stim < 30 ) ? current_stim - 30 : comest.stim; - add_effect( effect_visuals, hallu_duration * 30_minutes ); - add_msg_if_player( m_bad, SNIPPET.random_from_category( "comest_stimulant" ).value_or( - translation() ).translated() ); - } - } - - void Character::modify_fatigue( const islot_comestible & comest ) { - mod_fatigue( -comest.fatigue_mod ); - } - } - } - void Character::modify_fatigue( const islot_comestible & comest ) { - mod_fatigue( -comest.fatigue_mod ); - } +/** Consume medication. +* @return true if item consumed. +*/ +static bool consume_med( item &target, Character &you ) +{ + if( !target.is_medication() ) { + return false; + } - void Character::modify_addiction( const islot_comestible & comest ) { - for( const std::pair &add : comest.addictions ) { - add_addiction( add.first, add.second ); - if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { - rem_morale( add.first->get_craving_morale() ); - } - } - } - add_addiction( add.first, add.second ); - void Character::modify_morale( item & food, const int nutr ) { - time_duration morale_time = 2_hours; - if( food.has_flag( flag_HOT ) && food.has_flag( flag_EATEN_HOT ) ) { - morale_time = 3_hours; - int clamped_nutr = std::max( 5, std::min( 20, nutr / 10 ) ); - add_morale( MORALE_FOOD_HOT, clamped_nutr, 20, morale_time, morale_time / 2 ); - } + const itype_id tool_type = target.get_comestible()->tool; + const itype *req_tool = item::find_type( tool_type ); - std::pair fun = fun_for( food ); - if( fun.first < 0 ) { - add_morale( MORALE_FOOD_BAD, fun.first, fun.second, morale_time, morale_time / 2, false, - food.type ); - } else if( fun.first > 0 ) { - add_morale( MORALE_FOOD_GOOD, fun.first, fun.second, morale_time, morale_time / 2, false, - food.type ); - } + if( req_tool->tool ) { + if( !( you.has_amount( tool_type, 1 ) && + you.has_charges( tool_type, req_tool->tool->charges_per_use ) ) ) { + you.add_msg_if_player( m_info, _( "You need a %s to consume that!" ), req_tool->nname( 1 ) ); + return false; + } + you.use_charges( tool_type, req_tool->tool->charges_per_use ); + } - // Morale bonus for eating unspoiled food with chair/table nearby - // Does not apply to non-ingested consumables like bandages or drugs, - // nor to drinks. - if( !food.has_flag( flag_NO_INGEST ) && - food.get_comestible()->comesttype != "MED" && - food.get_comestible()->comesttype != comesttype_DRINK ) { - map &here = get_map(); - if( here.has_nearby_chair( pos(), 1 ) && here.has_nearby_table( pos_bub(), 1 ) ) { - if( has_trait( trait_TABLEMANNERS ) ) { - rem_morale( MORALE_ATE_WITHOUT_TABLE ); - if( !food.rotten() ) { - add_morale( MORALE_ATE_WITH_TABLE, 3, 3, 3_hours, 2_hours, true ); - } - } - } - void Character::modify_fatigue( const islot_comestible & comest ) { - mod_fatigue( -comest.fatigue_mod ); - } - - void Character::modify_addiction( const islot_comestible & comest ) { - for( const std::pair &add : comest.addictions ) { - add_addiction( add.first, add.second ); - if( !add.first.is_null() && add.first->get_craving_morale() != MORALE_NULL ) { - rem_morale( add.first->get_craving_morale() ); - } - } - } - - void Character::modify_stimulation( const islot_comestible & comest ) { - if( comest.stim == 0 ) { - return; - } - const int current_stim = get_stim(); - if( ( std::abs( comest.stim ) * 3 ) > std::abs( current_stim ) ) { - mod_stim( comest.stim ); - } else { - comest.stim > 0 ? mod_stim( std::max( comest.stim / 2, 1 ) ) : mod_stim( std::min( comest.stim / 2, - -1 ) ); - } - if( has_trait( trait_STIMBOOST ) && ( current_stim > 30 ) && - ( comest.addictions.count( STATIC( addiction_id( "caffeine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "amphetamine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "cocaine" ) ) ) || - comest.addictions.count( STATIC( addiction_id( "crack" ) ) ) ) ) { - int hallu_duration = ( current_stim - comest.stim < 30 ) ? current_stim - 30 : comest.stim; - add_effect( effect_visuals, hallu_duration * 30_minutes ); - add_msg_if_player( m_bad, SNIPPET.random_from_category( "comest_stimulant" ).value_or( - translation() ).translated() ); - } - } - - // Allergy check for food that is ingested (not gum) - if( !food.has_flag( flag_NO_INGEST ) ) { - const morale_type allergy = allergy_type( food ); - if( allergy != MORALE_NULL ) { - add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); - add_morale( allergy, -75, -400, 30_minutes, 24_minutes ); - } - if( food.has_flag( flag_ALLERGEN_JUNK ) ) { - if( has_trait( trait_PROJUNK ) ) { - add_msg_if_player( m_good, _( "Mmm, junk food." ) ); - add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); - } - if( food.has_flag( flag_ALLERGEN_JUNK ) ) { - if( has_trait( trait_PROJUNK ) ) { - add_msg_if_player( m_good, _( "Mmm, junk food." ) ); - add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); - } - if( has_trait( trait_PROJUNK2 ) ) { - if( !one_in( 100 ) ) { - add_msg_if_player( m_good, _( "When life's got you down, there's always sugar." ) ); - } else { - add_msg_if_player( m_good, _( "They may do what they must… you've already won." ) ); - } - add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes ); - } - // Carnivores CAN eat junk food, but they won't like it much. - // Pizza-scraping happens in consume_effects. - if( has_trait( trait_CARNIVORE ) && !food.has_flag( flag_CARNIVORE_OK ) ) { - add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); - add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); - } - add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes ); - } - // Carnivores CAN eat junk food, but they won't like it much. - // Pizza-scraping happens in consume_effects. - if( has_trait( trait_CARNIVORE ) && !food.has_flag( flag_CARNIVORE_OK ) ) { - add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); - add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); - } - add_morale( MORALE_HONEY, honey_fun, 100 ); - } - } - - // Used when determining stomach fullness from eating. - double Character::compute_effective_food_volume_ratio( const item & food ) const { - const nutrients food_nutrients = compute_effective_nutrients( food ); - units::mass food_weight = ( food.weight() / std::max( 1, food.count() ) ); - double ratio = 1.0f; - if( units::to_gram( food_weight ) != 0 ) { - ratio = std::max( static_cast( food_nutrients.kcal() ) / units::to_gram( food_weight ), - 1.0 ); - if( ratio > 3.0f ) { - ratio = std::sqrt( 3 * ratio ); - } - } - return ratio; - } - - // Remove the water volume from the food, as that gets absorbed and used as water. - // If the remaining dry volume of the food is less dense than water, crunch it down to a density equal to water. - // These maths are made easier by the fact that 1 g = 1 mL. Thanks, metric system. - units::volume Character::masticated_volume( const item & food ) const { - units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? food.get_comestible()->quench * - 5_ml : 0_ml; - units::mass water_weight = units::from_gram( units::to_milliliter( water_vol ) ); - // handle the division by zero exception when the food count is 0 with std::max() - units::mass food_dry_weight = food.weight() / std::max( 1, food.count() ) - water_weight; - units::volume food_dry_volume = food.volume() / std::max( 1, food.count() ) - water_vol; - - if( units::to_milliliter( food_dry_volume ) != 0 && - units::to_gram( food_dry_weight ) < units::to_milliliter( food_dry_volume ) ) { - food_dry_volume = units::from_milliliter( units::to_gram( food_dry_weight ) ); - } - - return food_dry_volume; - } - - // Used when displaying effective food satiation values. - int Character::compute_calories_per_effective_volume( const item & food, - const nutrients * nutrient /* = nullptr */ )const { - /* Understanding how Calories Per Effective Volume are calculated requires a dive into the - stomach fullness source code. Look at issue #44365*/ - int kcalories; - if( nutrient ) { - // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). - kcalories = nutrient->kcal(); - } else { - kcalories = compute_effective_nutrients( food ).kcal(); - } - double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); - const double energy_density_ratio = compute_effective_food_volume_ratio( food ); - const double effective_volume = food_vol * energy_density_ratio; - if( kcalories == 0 && effective_volume == 0.0 ) { - return 0; - } - return std::round( kcalories / effective_volume ); - } - - // Used when displaying effective food satiation values. - int Character::compute_calories_per_effective_volume( const item & food, - const nutrients * nutrient /* = nullptr */ )const { - /* Understanding how Calories Per Effective Volume are calculated requires a dive into the - stomach fullness source code. Look at issue #44365*/ - int kcalories; - if( nutrient ) { - // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). - kcalories = nutrient->kcal(); - } else { - kcalories = compute_effective_nutrients( food ).kcal(); - } - double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); - const double energy_density_ratio = compute_effective_food_volume_ratio( food ); - const double effective_volume = food_vol * energy_density_ratio; - if( kcalories == 0 && effective_volume == 0.0 ) { - return 0; - } - return std::round( kcalories / effective_volume ); - } - } + int amount_used = 1; + if( target.type->has_use() ) { + amount_used = target.type->invoke( &you, target, you.pos() ).value_or( 0 ); + if( amount_used <= 0 ) { + return false; + } + } - // Used when determining stomach fullness from eating. - double Character::compute_effective_food_volume_ratio( const item & food ) const { - const nutrients food_nutrients = compute_effective_nutrients( food ); - units::mass food_weight = ( food.weight() / std::max( 1, food.count() ) ); - double ratio = 1.0f; - if( units::to_gram( food_weight ) != 0 ) { - ratio = std::max( static_cast( food_nutrients.kcal() ) / units::to_gram( food_weight ), - 1.0 ); - if( ratio > 3.0f ) { - ratio = std::sqrt( 3 * ratio ); - } - } - return ratio; - } + // TODO: Get the target it was used on + // Otherwise injecting someone will give us addictions etc. + if( target.has_flag( flag_NO_INGEST ) ) { + const islot_comestible &comest = *target.get_comestible(); + // Assume that parenteral meds don't spoil, so don't apply rot + you.modify_health( comest ); + you.modify_stimulation( comest ); + you.modify_fatigue( comest ); + you.modify_addiction( comest ); + you.modify_morale( target ); + activate_consume_eocs( you, target ); + } else { + // Take by mouth + if( !you.consume_effects( target ) ) { + activate_consume_eocs( you, target ); + } + } - // Remove the water volume from the food, as that gets absorbed and used as water. - // If the remaining dry volume of the food is less dense than water, crunch it down to a density equal to water. - // These maths are made easier by the fact that 1 g = 1 mL. Thanks, metric system. - units::volume Character::masticated_volume( const item & food ) const { - units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? food.get_comestible()->quench * - 5_ml : 0_ml; - units::mass water_weight = units::from_gram( units::to_milliliter( water_vol ) ); - // handle the division by zero exception when the food count is 0 with std::max() - units::mass food_dry_weight = food.weight() / std::max( 1, food.count() ) - water_weight; - units::volume food_dry_volume = food.volume() / std::max( 1, food.count() ) - water_vol; - - if( units::to_milliliter( food_dry_volume ) != 0 && - units::to_gram( food_dry_weight ) < units::to_milliliter( food_dry_volume ) ) { - food_dry_volume = units::from_milliliter( units::to_gram( food_dry_weight ) ); - } - - return food_dry_volume; - } + if( target.count_by_charges() ) { + target.mod_charges( -amount_used ); + } + return true; +} - // Used when displaying effective food satiation values. - int Character::compute_calories_per_effective_volume( const item & food, - const nutrients * nutrient /* = nullptr */ )const { - /* Understanding how Calories Per Effective Volume are calculated requires a dive into the - stomach fullness source code. Look at issue #44365*/ - int kcalories; - if( nutrient ) { - // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). - kcalories = nutrient->kcal(); - } else { - kcalories = compute_effective_nutrients( food ).kcal(); - } - double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); - const double energy_density_ratio = compute_effective_food_volume_ratio( food ); - const double effective_volume = food_vol * energy_density_ratio; - if( kcalories == 0 && effective_volume == 0.0 ) { - return 0; - } - return std::round( kcalories / effective_volume ); - } +trinary Character::consume( item &target, bool force ) +{ + if( target.is_null() ) { + add_msg_if_player( m_info, _( "You do not have that item." ) ); + return trinary::NONE; + } + if( ( !has_trait( trait_WATERSLEEP ) && !has_trait( trait_UNDINE_SLEEP_WATER ) ) && + cant_do_underwater() ) { + return trinary::NONE; + } - // Used when displaying effective food satiation values. - int Character::compute_calories_per_effective_volume( const item & food, - const nutrients * nutrient /* = nullptr */ )const { - /* Understanding how Calories Per Effective Volume are calculated requires a dive into the - stomach fullness source code. Look at issue #44365*/ - int kcalories; - if( nutrient ) { - // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ). - kcalories = nutrient->kcal(); - } else { - kcalories = compute_effective_nutrients( food ).kcal(); - } - double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 ); - const double energy_density_ratio = compute_effective_food_volume_ratio( food ); - const double effective_volume = food_vol * energy_density_ratio; - if( kcalories == 0 && effective_volume == 0.0 ) { - return 0; - } - return std::round( kcalories / effective_volume ); - } + if( target.is_craft() ) { + add_msg_if_player( m_info, _( "You can't eat your %s." ), target.tname() ); + if( is_npc() ) { + debugmsg( "%s tried to eat a %s", get_name(), target.tname() ); + } + return trinary::NONE; + } + if( is_avatar() && !query_consume_ownership( target, *this ) ) { + return trinary::NONE; + } - static void activate_consume_eocs( Character & you, item & target ) { - Character *char_ptr = nullptr; - if( avatar *u = you.as_avatar() ) { - char_ptr = u; - } else if( npc *n = you.as_npc() ) { - char_ptr = n; - } - item_location loc( you, &target ); - dialogue d( get_talker_for( char_ptr ), get_talker_for( loc ) ); - const islot_comestible &comest = *target.get_comestible(); - for( const effect_on_condition_id &eoc : comest.consumption_eocs ) { - eoc->activate( d ); - } - } + if( consume_med( target, *this ) || eat( target, *this, force ) ) { + + get_event_bus().send( getID(), target.typeId() ); + + invalidate_weight_carried_cache(); + target.on_contents_changed(); + return !target.count_by_charges() || target.charges <= 0 ? trinary::ALL : trinary::SOME; + } - bool Character::consume_effects( item & food ) { - if( !food.is_comestible() ) { - debugmsg( "called Character::consume_effects with non-comestible" ); - return false; - } - - if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { - // Was used to cap nutrition and thirst, but no longer does this - return false; - } - if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && - food.has_any_flag( herbivore_blacklist ) ) { - // No good can come of this. - return false; - } - - // Used in hibernation messages. - const int nutr = nutrition_for( food ); - const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; - // We can handle junk just fine - if( !skip_health ) { - modify_health( comest ); - } - modify_stimulation( comest ); - modify_fatigue( comest ); - modify_addiction( comest ); - modify_morale( food, nutr ); - - const bool hibernate = has_active_mutation( trait_HIBERNATE ); - if( hibernate ) { - if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { - // Tell the player what's going on - add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); - if( one_in( 2 ) ) { - // 50% chance of the food tiring you - mod_fatigue( nutr ); - } - } - if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { - // Hibernation should cut burn to 60/day - add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); - if( one_in( 2 ) ) { - // And another 50%, intended cumulative - mod_fatigue( nutr ); - } - } - - if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { - add_msg_if_player( - _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); - if( !one_in( 3 ) ) { - // Third check, this one at 66% - mod_fatigue( nutr ); - } - } - if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { - add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); - // At this point, you're done. Schlaf gut. - mod_fatigue( nutr ); - } - } - // Moved here and changed a bit - it was too complex - // Incredibly minor stuff like this shouldn't require complexity - if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && - ( get_healthy_kcal() < get_stored_kcal() + 4000 && - get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { - add_msg_if_player( m_mixed, - _( "You feel as though you're going to split open! In a good way?" ) ); - mod_pain( 5 ); - int numslime = 1; - for( int i = 0; i < numslime; i++ ) { - if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { - slime->friendly = -1; - } - } - - nutrients food_nutrients = compute_effective_nutrients( food ); - const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? - food.get_comestible()->quench * - 5_ml : 0_ml; - units::volume food_vol = masticated_volume( food ); - if( food.count() == 0 ) { - debugmsg( "Tried to eat food with count of zero." ); - return false; - } - units::mass food_weight = ( food.weight() / food.count() ); - const double ratio = compute_effective_food_volume_ratio( food ); - food_summary ingested{ - water_vol, - food_vol * ratio, - food_nutrients - }; - add_msg_debug( debugmode::DF_FOOD, - "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", - units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, - food_nutrients.kcal(), units::to_gram( food_weight ) ); - // Maybe move tapeworm to digestion - if( has_effect( effect_tapeworm ) ) { - ingested.nutr /= 2; - } - // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? - activate_consume_eocs( *this, food ); - - if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) { - // Was used to cap nutrition and thirst, but no longer does this - return false; - } - if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && - food.has_any_flag( herbivore_blacklist ) ) { - // No good can come of this. - return false; - } - - // Used in hibernation messages. - const int nutr = nutrition_for( food ); - const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; - // We can handle junk just fine - if( !skip_health ) { - modify_health( comest ); - } - modify_stimulation( comest ); - modify_fatigue( comest ); - modify_addiction( comest ); - modify_morale( food, nutr ); - - const bool hibernate = has_active_mutation( trait_HIBERNATE ); - if( hibernate ) { - if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { - // Tell the player what's going on - add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); - if( one_in( 2 ) ) { - // 50% chance of the food tiring you - mod_fatigue( nutr ); - } - } - if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { - // Hibernation should cut burn to 60/day - add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); - if( one_in( 2 ) ) { - // And another 50%, intended cumulative - mod_fatigue( nutr ); - } - } - - if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { - add_msg_if_player( - _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); - if( !one_in( 3 ) ) { - // Third check, this one at 66% - mod_fatigue( nutr ); - } - } - if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { - add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); - // At this point, you're done. Schlaf gut. - mod_fatigue( nutr ); - } - } - // Moved here and changed a bit - it was too complex - // Incredibly minor stuff like this shouldn't require complexity - if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && - ( get_healthy_kcal() < get_stored_kcal() + 4000 && - get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { - add_msg_if_player( m_mixed, - _( "You feel as though you're going to split open! In a good way?" ) ); - mod_pain( 5 ); - int numslime = 1; - for( int i = 0; i < numslime; i++ ) { - if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { - slime->friendly = -1; - } - } - mod_hunger( 40 ); - mod_thirst( 40 ); - //~ slimespawns have *small voices* which may be the Nice equivalent - //~ of the Rat King's ALL CAPS invective. Probably shared-brain telepathy. - add_msg_if_player( m_good, _( "hey, you look like me! let's work together!" ) ); - } - - bool Character::can_estimate_rot() const { - return get_greater_skill_or_knowledge_level( skill_cooking ) >= 3 || - get_greater_skill_or_knowledge_level( skill_survival ) >= 4; - } - - bool Character::can_consume_as_is( const item & it ) const { - if( it.is_comestible() ) { - return !it.has_flag( flag_FROZEN ) || it.has_flag( flag_EDIBLE_FROZEN ) || - it.has_flag( flag_MELTS ); - } - return false; - } - - item &Character::get_consumable_from( item & it ) const { - item *ret = nullptr; - it.visit_items( [&]( item * it, item * ) { - if( can_consume_as_is( *it ) ) { - ret = it; - return VisitResponse::ABORT; - } - return VisitResponse::NEXT; - } ); - - if( ret != nullptr ) { - return *ret; - } - - static item null_comestible; - // Since it's not const. - null_comestible = item(); - return null_comestible; - } - - time_duration Character::get_consume_time( const item & it ) const { - const int charges = std::max( it.charges, 1 ); - int volume = units::to_milliliter( it.volume() ) / charges; - if( 0 == volume && it.type ) { - volume = units::to_milliliter( it.type->volume ); - } - time_duration time = time_duration::from_seconds( std::max( ( volume / - 5 ), 1 ) ); //Default 5 mL (1 tablespoon) per second - float consume_time_modifier = 1.0f;//only for food and drinks - const bool eat_verb = it.has_flag( flag_USE_EAT_VERB ); - const std::string comest_type = it.get_comestible() ? it.get_comestible()->comesttype : ""; - if( eat_verb || comest_type == "FOOD" ) { - time = time_duration::from_seconds( volume / 5 ); //Eat 5 mL (1 teaspoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } else if( !eat_verb && comest_type == "DRINK" ) { - time = time_duration::from_seconds( volume / 15 ); //Drink 15 mL (1 tablespoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } else if( use_function const *fun = it.type->get_use( "heal" ) ) { - time = time_duration::from_moves( dynamic_cast - ( fun->get_actor_ptr() )->move_cost ); - } else if( it.is_medication() ) { - const use_function *consume_drug = it.type->get_use( "consume_drug" ); - const use_function *smoking = it.type->get_use( "SMOKING" ); - const use_function *adrenaline_injector = it.type->get_use( "ADRENALINE_INJECTOR" ); - if( consume_drug != nullptr ) { //its a drug - const consume_drug_iuse *consume_drug_use = dynamic_cast - ( consume_drug->get_actor_ptr() ); - if( consume_drug_use->tools_needed.find( itype_syringe ) != consume_drug_use->tools_needed.end() && - has_bionic( bio_syringe ) ) { - time = time_duration::from_seconds( - 15 );//injections with the intradermal needle CBM are much quicker than with a normal syringe - } else if( consume_drug_use->tools_needed.find( itype_syringe ) != - consume_drug_use->tools_needed.end() ) { - time = time_duration::from_minutes( 5 );//sterile injections take 5 minutes - } else if( consume_drug_use->tools_needed.find( itype_apparatus ) != - consume_drug_use->tools_needed.end() || - consume_drug_use->tools_needed.find( itype_dab_pen_on ) != consume_drug_use->tools_needed.end() ) { - time = time_duration::from_seconds( 30 );//smoke a bowl - } else { - time = time_duration::from_seconds( 5 );//popping a pill is quick - } - } else if( smoking != nullptr ) { - time = time_duration::from_minutes( 1 );//about five minutes for a cig or joint so 1 minute a charge - } else if( adrenaline_injector != nullptr ) { - //epi-pens, and disinfectant are fairly quick - time = time_duration::from_seconds( 15 ); - } else { - time = time_duration::from_seconds( 5 ); //probably pills so quick - } - } else if( it.get_category_shallow().get_id() == item_category_chems ) { - time = time_duration::from_seconds( std::max( ( volume / 15 ), - 1 ) ); //Consume 15 mL (1 tablespoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } - - nutrients food_nutrients = compute_effective_nutrients( food ); - const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? - food.get_comestible()->quench * - 5_ml : 0_ml; - units::volume food_vol = masticated_volume( food ); - if( food.count() == 0 ) { - debugmsg( "Tried to eat food with count of zero." ); - return false; - } - units::mass food_weight = ( food.weight() / food.count() ); - const double ratio = compute_effective_food_volume_ratio( food ); - food_summary ingested{ - water_vol, - food_vol * ratio, - food_nutrients - }; - add_msg_debug( debugmode::DF_FOOD, - "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", - units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, - food_nutrients.kcal(), units::to_gram( food_weight ) ); - // Maybe move tapeworm to digestion - if( has_effect( effect_tapeworm ) ) { - ingested.nutr /= 2; - } - // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? - activate_consume_eocs( *this, food ); - - // GET IN MAH BELLY! - stomach.ingest( ingested ); - - static bool query_consume_ownership( item & target, Character & p ) { - if( !target.is_owned_by( p, true ) ) { - bool choice = true; - if( p.get_value( "THIEF_MODE" ) == "THIEF_ASK" ) { - choice = Pickup::query_thief(); - } - if( p.get_value( "THIEF_MODE" ) == "THIEF_HONEST" || !choice ) { - return false; - } - std::vector witnesses; - for( npc &elem : g->all_npcs() ) { - if( rl_dist( elem.pos(), p.pos() ) < MAX_VIEW_DISTANCE && elem.sees( p.pos() ) ) { - witnesses.push_back( &elem ); - } - } - for( npc *elem : witnesses ) { - elem->say( "", 7 ); - } - if( !witnesses.empty() && target.is_owned_by( p, true ) ) { - if( p.add_faction_warning( target.get_owner() ) ) { - for( npc *elem : witnesses ) { - elem->make_angry(); - } - } - } - } - return true; - } - - /** Consume medication. - * @return true if item consumed. - */ - static bool consume_med( item & target, Character & you ) { - if( !target.is_medication() ) { - return false; - } - - const itype_id tool_type = target.get_comestible()->tool; - const itype *req_tool = item::find_type( tool_type ); - - if( req_tool->tool ) { - if( !( you.has_amount( tool_type, 1 ) && - you.has_charges( tool_type, req_tool->tool->charges_per_use ) ) ) { - you.add_msg_if_player( m_info, _( "You need a %s to consume that!" ), req_tool->nname( 1 ) ); - return false; - } - you.use_charges( tool_type, req_tool->tool->charges_per_use ); - } - - int amount_used = 1; - if( target.type->has_use() ) { - amount_used = target.type->invoke( &you, target, you.pos() ).value_or( 0 ); - if( amount_used <= 0 ) { - return false; - } - } - - // Used in hibernation messages. - const int nutr = nutrition_for( food ); - const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0; - // We can handle junk just fine - if( !skip_health ) { - modify_health( comest ); - } - modify_stimulation( comest ); - modify_fatigue( comest ); - modify_addiction( comest ); - modify_morale( food, nutr ); - - const bool hibernate = has_active_mutation( trait_HIBERNATE ); - if( hibernate ) { - if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { - // Tell the player what's going on - add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) ); - if( one_in( 2 ) ) { - // 50% chance of the food tiring you - mod_fatigue( nutr ); - } - } - if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) { - // Hibernation should cut burn to 60/day - add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) ); - if( one_in( 2 ) ) { - // And another 50%, intended cumulative - mod_fatigue( nutr ); - } - } - - if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) { - add_msg_if_player( - _( "Mmm. You can still fit some more in… but maybe you should get comfortable and sleep." ) ); - if( !one_in( 3 ) ) { - // Third check, this one at 66% - mod_fatigue( nutr ); - } - } - if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) { - add_msg_if_player( _( "That filled a hole! Time for bed…" ) ); - // At this point, you're done. Schlaf gut. - mod_fatigue( nutr ); - } - } - // Moved here and changed a bit - it was too complex - // Incredibly minor stuff like this shouldn't require complexity - if( !is_npc() && has_trait( trait_SLIMESPAWNER ) && - ( get_healthy_kcal() < get_stored_kcal() + 4000 && - get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) { - add_msg_if_player( m_mixed, - _( "You feel as though you're going to split open! In a good way?" ) ); - mod_pain( 5 ); - int numslime = 1; - for( int i = 0; i < numslime; i++ ) { - if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { - slime->friendly = -1; - } - } - - nutrients food_nutrients = compute_effective_nutrients( food ); - const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? - food.get_comestible()->quench * - 5_ml : 0_ml; - units::volume food_vol = masticated_volume( food ); - if( food.count() == 0 ) { - debugmsg( "Tried to eat food with count of zero." ); - return false; - } - units::mass food_weight = ( food.weight() / food.count() ); - const double ratio = compute_effective_food_volume_ratio( food ); - food_summary ingested{ - water_vol, - food_vol * ratio, - food_nutrients - }; - add_msg_debug( debugmode::DF_FOOD, - "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d", - units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio, - food_nutrients.kcal(), units::to_gram( food_weight ) ); - // Maybe move tapeworm to digestion - if( has_effect( effect_tapeworm ) ) { - ingested.nutr /= 2; - } - // to do: reduce nutrition by a factor of the amount of muscle to be rebuilt? - activate_consume_eocs( *this, food ); - - // GET IN MAH BELLY! - stomach.ingest( ingested ); - - // update speculative values - if( is_avatar() ) { - get_avatar().add_ingested_kcal( ingested.nutr.calories / 1000 ); - } - for( const auto &v : ingested.nutr.vitamins() ) { - // update the estimated values for daily vitamins - // actual vitamins happen during digestion - daily_vitamins[v.first].first += v.second; - } - - return true; - } - - if( target.count_by_charges() ) { - target.mod_charges( -amount_used ); - } - return true; - } - - trinary Character::consume( item & target, bool force ) { - if( target.is_null() ) { - add_msg_if_player( m_info, _( "You do not have that item." ) ); - return trinary::NONE; - } - if( ( !has_trait( trait_WATERSLEEP ) && !has_trait( trait_UNDINE_SLEEP_WATER ) ) && - cant_do_underwater() ) { - return trinary::NONE; - } - - item &Character::get_consumable_from( item & it ) const { - item *ret = nullptr; - it.visit_items( [&]( item * it, item * ) { - if( can_consume_as_is( *it ) ) { - ret = it; - return VisitResponse::ABORT; - } - return VisitResponse::NEXT; - } ); - - if( ret != nullptr ) { - return *ret; - } - - static item null_comestible; - // Since it's not const. - null_comestible = item(); - return null_comestible; - } - - time_duration Character::get_consume_time( const item & it ) const { - const int charges = std::max( it.charges, 1 ); - int volume = units::to_milliliter( it.volume() ) / charges; - if( 0 == volume && it.type ) { - volume = units::to_milliliter( it.type->volume ); - } - time_duration time = time_duration::from_seconds( std::max( ( volume / - 5 ), 1 ) ); //Default 5 mL (1 tablespoon) per second - float consume_time_modifier = 1.0f;//only for food and drinks - const bool eat_verb = it.has_flag( flag_USE_EAT_VERB ); - const std::string comest_type = it.get_comestible() ? it.get_comestible()->comesttype : ""; - if( eat_verb || comest_type == "FOOD" ) { - time = time_duration::from_seconds( volume / 5 ); //Eat 5 mL (1 teaspoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } else if( !eat_verb && comest_type == "DRINK" ) { - time = time_duration::from_seconds( volume / 15 ); //Drink 15 mL (1 tablespoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } else if( use_function const *fun = it.type->get_use( "heal" ) ) { - time = time_duration::from_moves( dynamic_cast - ( fun->get_actor_ptr() )->move_cost ); - } else if( it.is_medication() ) { - const use_function *consume_drug = it.type->get_use( "consume_drug" ); - const use_function *smoking = it.type->get_use( "SMOKING" ); - const use_function *adrenaline_injector = it.type->get_use( "ADRENALINE_INJECTOR" ); - if( consume_drug != nullptr ) { //its a drug - const consume_drug_iuse *consume_drug_use = dynamic_cast - ( consume_drug->get_actor_ptr() ); - if( consume_drug_use->tools_needed.find( itype_syringe ) != consume_drug_use->tools_needed.end() && - has_bionic( bio_syringe ) ) { - time = time_duration::from_seconds( - 15 );//injections with the intradermal needle CBM are much quicker than with a normal syringe - } else if( consume_drug_use->tools_needed.find( itype_syringe ) != - consume_drug_use->tools_needed.end() ) { - time = time_duration::from_minutes( 5 );//sterile injections take 5 minutes - } else if( consume_drug_use->tools_needed.find( itype_apparatus ) != - consume_drug_use->tools_needed.end() || - consume_drug_use->tools_needed.find( itype_dab_pen_on ) != consume_drug_use->tools_needed.end() ) { - time = time_duration::from_seconds( 30 );//smoke a bowl - } else { - time = time_duration::from_seconds( 5 );//popping a pill is quick - } - } else if( smoking != nullptr ) { - time = time_duration::from_minutes( 1 );//about five minutes for a cig or joint so 1 minute a charge - } else if( adrenaline_injector != nullptr ) { - //epi-pens, and disinfectant are fairly quick - time = time_duration::from_seconds( 15 ); - } else { - time = time_duration::from_seconds( 5 ); //probably pills so quick - } - } else if( it.get_category_shallow().get_id() == item_category_chems ) { - time = time_duration::from_seconds( std::max( ( volume / 15 ), - 1 ) ); //Consume 15 mL (1 tablespoon) per second - consume_time_modifier = mutation_value( "consume_time_modifier" ); - } - - // Minimum consumption time, without mutations, is always 1 second. - time = std::max( 1_seconds, time ); - - return time * consume_time_modifier; - } - - get_event_bus().send( getID(), target.typeId() ); - - invalidate_weight_carried_cache(); - target.on_contents_changed(); - return !target.count_by_charges() || target.charges <= 0 ? trinary::ALL : trinary::SOME; - } - - return trinary::NONE; - } - - trinary Character::consume( item_location loc, bool force ) { - if( !loc ) { - debugmsg( "Null loc to consume." ); - return trinary::NONE; - } - contents_change_handler handler; - item &target = *loc; - trinary result = consume( target, force ); - if( result != trinary::NONE ) { - handler.unseal_pocket_containing( loc ); - } - if( result == trinary::ALL ) { - if( loc.where() == item_location::type::character ) { - i_rem( loc.get_item() ); - } else { - loc.remove_item(); - } - } - handler.handle_by( *this ); - return result; - } + return trinary::NONE; +} + +trinary Character::consume( item_location loc, bool force ) +{ + if( !loc ) { + debugmsg( "Null loc to consume." ); + return trinary::NONE; + } + contents_change_handler handler; + item &target = *loc; + trinary result = consume( target, force ); + if( result != trinary::NONE ) { + handler.unseal_pocket_containing( loc ); + } + if( result == trinary::ALL ) { + if( loc.where() == item_location::type::character ) { + i_rem( loc.get_item() ); + } else { + loc.remove_item(); + } + } + handler.handle_by( *this ); + return result; +} diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index 8c13059baaf92..c308626e24a97 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -565,6 +565,75 @@ std::function mod_order_eval( char /* scope */, }; } +enum class character_filter : int { + friends = 0, + not_friends, + both, +}; + +bool _friend_match_filter_character( Character const &beta, Character const &guy, + character_filter filter ) +{ + switch( filter ) { + case character_filter::friends: + return guy.is_ally( beta ) != 0; + case character_filter::not_friends: + return !guy.is_ally( beta ) == 0; + case character_filter::both: + return true; + } + return false; +} + +bool _filter_character( Character const &beta, Character const &guy, int radius, + tripoint_abs_ms const &loc, character_filter filter ) +{ + if( !guy.is_hallucination() && ( beta.getID() != guy.getID() ) ) { + return _friend_match_filter_character( beta, guy, filter ) && + radius >= rl_dist( guy.get_location(), loc ); + } + return false; +} + +std::function _friends_nearby_eval( char scope, + std::vector const ¶ms, diag_kwargs const &kwargs ) +{ + diag_value radius_val( 1000.0 ); + std::optional loc_var; + if( kwargs.count( "radius" ) != 0 ) { + radius_val = *kwargs.at( "radius" ); + } + if( kwargs.count( "location" ) != 0 ) { + loc_var = kwargs.at( "location" )->var(); + } else if( scope == 'g' ) { + throw std::invalid_argument( string_format( + R"("friends_nearby" needs either an actor scope (u/n) or a 'location' kwarg)" ) ); + } + + return [beta = is_beta( scope ), params, loc_var, radius_val]( dialogue & d ) { + tripoint_abs_ms loc; + if( loc_var.has_value() ) { + loc = get_tripoint_from_var( loc_var, d ); + } else { + loc = d.actor( beta )->global_pos(); + } + + int const radius = static_cast( radius_val.dbl( d ) ); + character_filter filter = character_filter::friends; + std::vector const targets = g->get_characters_if( [&beta, &d, &radius, + &loc, filter ]( const Character & guy ) { + return _filter_character( *d.actor( beta )->get_character(), guy, radius, loc, filter ); + } ); + return static_cast( targets.size() ); + }; +} + +std::function friends_nearby_eval( char scope, + std::vector const ¶ms, diag_kwargs const &kwargs ) +{ + return _friends_nearby_eval( scope, params, kwargs ); +} + template using f_monster_match = bool ( * )( Creature const &critter, ID const &id ); @@ -1257,6 +1326,7 @@ std::map const dialogue_eval_f{ { "faction_respect", { "g", 1, faction_respect_eval } }, { "faction_trust", { "g", 1, faction_trust_eval } }, { "field_strength", { "ung", 1, field_strength_eval } }, + { "friends_nearby", { "ung", -1, friends_nearby_eval } }, { "gun_damage", { "un", 1, gun_damage_eval } }, { "game_option", { "g", 1, option_eval } }, { "has_flag", { "un", 1, has_flag_eval } }, From 2d8ed2278ee4f547e9f0c406d05889b85513d423 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 01:55:01 -0800 Subject: [PATCH 133/202] Update data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../mutation_eocs/mutation_effect_eocs.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index 34f83ee1e4c23..3ba767c65c728 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -781,10 +781,7 @@ { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ - { "u_lose_effect": "asocial_dissatisfied" }, - { "u_add_effect": "asocial_satisfied", "duration": "6 hours" } - ] + "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "6 hours" } ] }, { "type": "effect_on_condition", From e723b37e3c87b2b99d5ea55672dee99d87b50248 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 01:55:09 -0800 Subject: [PATCH 134/202] Update data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../mutation_eocs/mutation_effect_eocs.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index 3ba767c65c728..f5416f26bb05a 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -767,7 +767,11 @@ { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_lose_effect": "asocial_satisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "2 hours" } ] + "effect": [ + { "u_lose_effect": "asocial_dissatisfied" }, + { "u_lose_effect": "asocial_satisfied" }, + { "u_add_effect": "asocial_satisfied", "duration": "2 hours" } + ] }, { "type": "effect_on_condition", From a75d0482f61df63c9bbeca124a1ce19b739e92a6 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 01:55:17 -0800 Subject: [PATCH 135/202] Update data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../mutation_eocs/mutation_effect_eocs.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index f5416f26bb05a..9839c2eb6baba 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -709,7 +709,11 @@ { "not": { "u_has_effect": "narcosis" } } ] }, - "effect": [ { "u_lose_effect": "social_dissatisfied" }, { "u_lose_effect": "social_satisfied" }, { "u_add_effect": "social_satisfied", "duration": "2 hours" } ] + "effect": [ + { "u_lose_effect": "social_dissatisfied" }, + { "u_lose_effect": "social_satisfied" }, + { "u_add_effect": "social_satisfied", "duration": "2 hours" } + ] }, { "type": "effect_on_condition", From 28e6e4127f47df9344dc213d6bd444f310e770d8 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 02:16:33 -0800 Subject: [PATCH 136/202] Update src/math_parser_diag.cpp Co-authored-by: andrei <68240139+andrei8l@users.noreply.github.com> --- src/math_parser_diag.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index c308626e24a97..4a6b1dfc54b36 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -1326,7 +1326,7 @@ std::map const dialogue_eval_f{ { "faction_respect", { "g", 1, faction_respect_eval } }, { "faction_trust", { "g", 1, faction_trust_eval } }, { "field_strength", { "ung", 1, field_strength_eval } }, - { "friends_nearby", { "ung", -1, friends_nearby_eval } }, + { "friends_nearby", { "ung", 0, friends_nearby_eval } }, { "gun_damage", { "un", 1, gun_damage_eval } }, { "game_option", { "g", 1, option_eval } }, { "has_flag", { "un", 1, has_flag_eval } }, From a1a6553a7b0bdadd175c2994da68bfa3b771a2c9 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 04:50:43 -0800 Subject: [PATCH 137/202] change to characters_nearby, document --- .vscode/settings.json | 4 ++++ doc/NPCs.md | 1 + src/math_parser_diag.cpp | 47 +++++++++++++++++++++++++++++----------- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 20bd09ea82c04..e78572d4bf61e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -64,4 +64,8 @@ "github-actions.remote-name": "upstream", // NOTE: This disable the plugins formatting so astyle is used. "C_Cpp.formatting": "disabled", + "files.associations": { + "regex": "cpp", + "sstream": "cpp" + }, } diff --git a/doc/NPCs.md b/doc/NPCs.md index b3e23965ca276..c817123f13d23 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -1315,6 +1315,7 @@ _some functions support array arguments or kwargs, denoted with square brackets | attack_speed() | ✅ | ❌ | u, n | Return the characters current adjusted attack speed with their current weapon.

Example:
`"condition": { "math": [ "u_attack_speed()", ">=", "10"] }`| | addiction_intensity(`s`/`v`) | ✅ | ❌ | u, n | Return the characters current intensity of the given addiction.
Argument is addiction type ID.

Example:
`"condition": { "math": [ "u_addiction_intensity('caffeine')", ">=", "1"] }`| | addiction_turns(`s`/`v`) | ✅ | ✅ | u, n | Return the characters current duration left (in turns) for the given addiction.
Argument is addiction type ID.

Example:
`"condition": { "math": [ "u_addiction_turns('caffeine')", ">=", "3600"] }`| +| characters_nearby(`s`/`v`...) | ✅ | ❌ | u, n, global | Return the number of nearby characters (that is, NPCs and the player who are not hallucinations).

Optional kwargs:
`radius`: `d`/`v` - limit to radius (rl_dist)
`location`: `v` - center search on this location
`attitude`: `s`/`v` - attitude filter. Must be one of `hostile`, `allies`, `not_allies`, `any`. Assumes `any` if not specified

The `location` kwarg is mandatory in the global scope.

Examples:
`"condition": { "math": [ "u_characters_nearby('radius': u_search_radius * 3, 'attitude': 'not_allies' )", ">", "0" ] }`

`"condition": { "math": [ "characters_nearby( 'radius': u_search_radius * 3, 'location': u_search_loc)", ">", "5" ] }`| | charge_count(`s`/`v`) | ✅ | ❌ | u, n | Return the charges of a given item in the character's inventory.
Argument is item ID.

Example:
`"condition": { "math": [ "u_charge_count('light_plus_battery_cell')", ">=", "100"] }`| | coverage(`s`/`v`) | ✅ | ❌ | u, n | Return the characters total coverage of a body part.
Argument is bodypart ID.
For items, returns typical coverage of the item.

Example:
`"condition": { "math": [ "u_coverage('torso')", ">", "0"] }`| | distance(`s`/`v`,`s`/`v`) | ✅ | ❌ | g | Return distance between two targets.
Arguments are location variables or special strings (`u`, `npc`). `u` means your location. `npc` means NPC's location.

Example:
`"condition": { "math": [ "distance('u', loc)", "<=", "50"] }`| diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index c308626e24a97..b560242178faa 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -566,20 +566,24 @@ std::function mod_order_eval( char /* scope */, } enum class character_filter : int { - friends = 0, - not_friends, - both, + allies = 0, + not_allies, + hostile, + any, }; bool _friend_match_filter_character( Character const &beta, Character const &guy, character_filter filter ) { switch( filter ) { - case character_filter::friends: + case character_filter::allies: return guy.is_ally( beta ) != 0; - case character_filter::not_friends: + case character_filter::not_allies: return !guy.is_ally( beta ) == 0; - case character_filter::both: + case character_filter::hostile: + return guy.attitude_to( beta ) == Creature::Attitude::HOSTILE || + ( beta.is_avatar() && guy.is_npc() && guy.as_npc()->guaranteed_hostile() ); + case character_filter::any: return true; } return false; @@ -595,22 +599,27 @@ bool _filter_character( Character const &beta, Character const &guy, int radius, return false; } -std::function _friends_nearby_eval( char scope, +std::function _characters_nearby_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { diag_value radius_val( 1000.0 ); + diag_value filter_val( std::string{ "any" } ); std::optional loc_var; if( kwargs.count( "radius" ) != 0 ) { radius_val = *kwargs.at( "radius" ); } + if( kwargs.count( "attitude" ) != 0 ) { + filter_val = *kwargs.at( "attitude" ); + } if( kwargs.count( "location" ) != 0 ) { loc_var = kwargs.at( "location" )->var(); + } else if( scope == 'g' ) { throw std::invalid_argument( string_format( - R"("friends_nearby" needs either an actor scope (u/n) or a 'location' kwarg)" ) ); + R"("characters_nearby" needs either an actor scope (u/n) or a 'location' kwarg)" ) ); } - return [beta = is_beta( scope ), params, loc_var, radius_val]( dialogue & d ) { + return [beta = is_beta( scope ), params, loc_var, filter_val, radius_val ]( dialogue & d ) { tripoint_abs_ms loc; if( loc_var.has_value() ) { loc = get_tripoint_from_var( loc_var, d ); @@ -619,7 +628,19 @@ std::function _friends_nearby_eval( char scope, } int const radius = static_cast( radius_val.dbl( d ) ); - character_filter filter = character_filter::friends; + std::string const filter_str = filter_val.str( d ); + character_filter filter = character_filter::any; + if( filter_str == "allies" ) { + filter = character_filter::allies; + } else if( filter_str == "not_allies" ) { + filter = character_filter::not_allies; + } else if( filter_str == "hostile" ) { + filter = character_filter::hostile; + } else if( filter_str != "any" ) { + debugmsg( R"(Unknown attitude filter "%s" for characters_nearby(), counting all characters)", + filter_str ); + } + std::vector const targets = g->get_characters_if( [&beta, &d, &radius, &loc, filter ]( const Character & guy ) { return _filter_character( *d.actor( beta )->get_character(), guy, radius, loc, filter ); @@ -628,10 +649,10 @@ std::function _friends_nearby_eval( char scope, }; } -std::function friends_nearby_eval( char scope, +std::function characters_nearby_eval( char scope, std::vector const ¶ms, diag_kwargs const &kwargs ) { - return _friends_nearby_eval( scope, params, kwargs ); + return _characters_nearby_eval( scope, params, kwargs ); } template @@ -1315,6 +1336,7 @@ std::map const dialogue_eval_f{ { "addiction_turns", { "un", 1, addiction_turns_eval } }, { "armor", { "un", 2, armor_eval } }, { "attack_speed", { "un", 0, attack_speed_eval } }, + { "characters_nearby", { "ung", -1, characters_nearby_eval } }, { "charge_count", { "un", 1, charge_count_eval } }, { "coverage", { "un", 1, coverage_eval } }, { "damage_level", { "un", 0, damage_level_eval } }, @@ -1326,7 +1348,6 @@ std::map const dialogue_eval_f{ { "faction_respect", { "g", 1, faction_respect_eval } }, { "faction_trust", { "g", 1, faction_trust_eval } }, { "field_strength", { "ung", 1, field_strength_eval } }, - { "friends_nearby", { "ung", -1, friends_nearby_eval } }, { "gun_damage", { "un", 1, gun_damage_eval } }, { "game_option", { "g", 1, option_eval } }, { "has_flag", { "un", 1, has_flag_eval } }, From ffe747764b2d1444325ec613e8775f8cd82859e3 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 05:14:00 -0800 Subject: [PATCH 138/202] Update doc/NPCs.md Co-authored-by: andrei <68240139+andrei8l@users.noreply.github.com> --- doc/NPCs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/NPCs.md b/doc/NPCs.md index c817123f13d23..8118ab84b1441 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -1315,7 +1315,7 @@ _some functions support array arguments or kwargs, denoted with square brackets | attack_speed() | ✅ | ❌ | u, n | Return the characters current adjusted attack speed with their current weapon.

Example:
`"condition": { "math": [ "u_attack_speed()", ">=", "10"] }`| | addiction_intensity(`s`/`v`) | ✅ | ❌ | u, n | Return the characters current intensity of the given addiction.
Argument is addiction type ID.

Example:
`"condition": { "math": [ "u_addiction_intensity('caffeine')", ">=", "1"] }`| | addiction_turns(`s`/`v`) | ✅ | ✅ | u, n | Return the characters current duration left (in turns) for the given addiction.
Argument is addiction type ID.

Example:
`"condition": { "math": [ "u_addiction_turns('caffeine')", ">=", "3600"] }`| -| characters_nearby(`s`/`v`...) | ✅ | ❌ | u, n, global | Return the number of nearby characters (that is, NPCs and the player who are not hallucinations).

Optional kwargs:
`radius`: `d`/`v` - limit to radius (rl_dist)
`location`: `v` - center search on this location
`attitude`: `s`/`v` - attitude filter. Must be one of `hostile`, `allies`, `not_allies`, `any`. Assumes `any` if not specified

The `location` kwarg is mandatory in the global scope.

Examples:
`"condition": { "math": [ "u_characters_nearby('radius': u_search_radius * 3, 'attitude': 'not_allies' )", ">", "0" ] }`

`"condition": { "math": [ "characters_nearby( 'radius': u_search_radius * 3, 'location': u_search_loc)", ">", "5" ] }`| +| characters_nearby() | ✅ | ❌ | u, n, global | Return the number of nearby characters (that is, the avatar and/or NPCs who are not hallucinations).

Optional kwargs:
`radius`: `d`/`v` - limit to radius (rl_dist)
`location`: `v` - center search on this location
`attitude`: `s`/`v` - attitude filter. Must be one of `hostile`, `allies`, `not_allies`, `any`. Assumes `any` if not specified

The `location` kwarg is mandatory in the global scope.

Examples:
`"condition": { "math": [ "u_characters_nearby('radius': u_search_radius * 3, 'attitude': 'not_allies' )", ">", "0" ] }`

`"condition": { "math": [ "characters_nearby( 'radius': u_search_radius * 3, 'location': u_search_loc)", ">", "5" ] }`| | charge_count(`s`/`v`) | ✅ | ❌ | u, n | Return the charges of a given item in the character's inventory.
Argument is item ID.

Example:
`"condition": { "math": [ "u_charge_count('light_plus_battery_cell')", ">=", "100"] }`| | coverage(`s`/`v`) | ✅ | ❌ | u, n | Return the characters total coverage of a body part.
Argument is bodypart ID.
For items, returns typical coverage of the item.

Example:
`"condition": { "math": [ "u_coverage('torso')", ">", "0"] }`| | distance(`s`/`v`,`s`/`v`) | ✅ | ❌ | g | Return distance between two targets.
Arguments are location variables or special strings (`u`, `npc`). `u` means your location. `npc` means NPC's location.

Example:
`"condition": { "math": [ "distance('u', loc)", "<=", "50"] }`| From d4988fff044e0513cc221ec0364251bcb54c3d4f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 05:48:48 -0800 Subject: [PATCH 139/202] weee --- doc/NPCs.md | 3 ++- src/math_parser_diag.cpp | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/doc/NPCs.md b/doc/NPCs.md index 8118ab84b1441..dd43b39c32f87 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -1315,7 +1315,7 @@ _some functions support array arguments or kwargs, denoted with square brackets | attack_speed() | ✅ | ❌ | u, n | Return the characters current adjusted attack speed with their current weapon.

Example:
`"condition": { "math": [ "u_attack_speed()", ">=", "10"] }`| | addiction_intensity(`s`/`v`) | ✅ | ❌ | u, n | Return the characters current intensity of the given addiction.
Argument is addiction type ID.

Example:
`"condition": { "math": [ "u_addiction_intensity('caffeine')", ">=", "1"] }`| | addiction_turns(`s`/`v`) | ✅ | ✅ | u, n | Return the characters current duration left (in turns) for the given addiction.
Argument is addiction type ID.

Example:
`"condition": { "math": [ "u_addiction_turns('caffeine')", ">=", "3600"] }`| -| characters_nearby() | ✅ | ❌ | u, n, global | Return the number of nearby characters (that is, the avatar and/or NPCs who are not hallucinations).

Optional kwargs:
`radius`: `d`/`v` - limit to radius (rl_dist)
`location`: `v` - center search on this location
`attitude`: `s`/`v` - attitude filter. Must be one of `hostile`, `allies`, `not_allies`, `any`. Assumes `any` if not specified

The `location` kwarg is mandatory in the global scope.

Examples:
`"condition": { "math": [ "u_characters_nearby('radius': u_search_radius * 3, 'attitude': 'not_allies' )", ">", "0" ] }`

`"condition": { "math": [ "characters_nearby( 'radius': u_search_radius * 3, 'location': u_search_loc)", ">", "5" ] }`| +| characters_nearby() | ✅ | ❌ | u, n, global | Return the number of nearby characters (that is, the avatar and/or NPCs who are not hallucinations).

Optional kwargs:
`radius`: `d`/`v` - limit to radius (rl_dist)
`location`: `v` - center search on this location
`attitude`: `s`/`v` - attitude filter. Must be one of `hostile`, `allies`, `not_allies`, `any`. Assumes `any` if not specified

The `location` kwarg is mandatory in the global scope.
`allow_hallucinations`: `d`/`v` - False by default. If set to any non-zero number, all hallucinated NPCs in range will be counted as well.

Examples:
`"condition": { "math": [ "u_characters_nearby('radius': u_search_radius * 3, 'attitude': 'not_allies' )", ">", "0" ] }`

`"condition": { "math": [ "characters_nearby( 'radius': u_search_radius * 3, 'location': u_search_loc)", ">", "5" ] }`| | charge_count(`s`/`v`) | ✅ | ❌ | u, n | Return the charges of a given item in the character's inventory.
Argument is item ID.

Example:
`"condition": { "math": [ "u_charge_count('light_plus_battery_cell')", ">=", "100"] }`| | coverage(`s`/`v`) | ✅ | ❌ | u, n | Return the characters total coverage of a body part.
Argument is bodypart ID.
For items, returns typical coverage of the item.

Example:
`"condition": { "math": [ "u_coverage('torso')", ">", "0"] }`| | distance(`s`/`v`,`s`/`v`) | ✅ | ❌ | g | Return distance between two targets.
Arguments are location variables or special strings (`u`, `npc`). `u` means your location. `npc` means NPC's location.

Example:
`"condition": { "math": [ "distance('u', loc)", "<=", "50"] }`| @@ -1357,6 +1357,7 @@ _some functions support array arguments or kwargs, denoted with square brackets | time_since(`v`)
time_since('cataclysm')
time_since('midnight') | ✅ | ❌ | N/A
(global) | Convenience function that returns a numeric value (in turns) for the time period since time point stored in variable.

Special values:
`cataclysm` - return time since start of cataclysm
`midnight` - return time since midnight

Optional kwargs:
`unit`: specify return unit. Assumes `turns` if unspecified or empty.

Returns -1 if the argument is an undefined variable

Example:
`{ "math": [ "time_since(u_timer_caravan_RandEnc)", ">", "time('1 h')" ] }`
`{ "math": [ "time_since('cataclysm', 'unit':'years') > 1" ] }`| | time_until_eoc(`s`/`v`) | ✅ | ❌ | N/A
(global) | Returns time until next scheduled run of an EOC. Argument is EOC id.

Optional kwargs:
`unit`: specify return unit

Returns -1 is the EOC is not scheduled. | | val(`s`) | ✅ | varies | u, n | Return or set a Character or item value. Argument is a [Character/item aspect](#list-of-character-and-item-aspects).

These are all in one function for legacy reasons and are generally poorly tested. If you need one of them, consider porting it to a native math function.

Example:
`{ "math": [ "u_val('strength')", "=", "2" ] }`| +| vision_range(`s`/`v`) | ✅ | ❌ | u, n | Return the character's visual range, adjusted by their mutations, effects, and other issues.

Example:
`"{ "math": [ "u_vision_range", "<", "30"] }`| | vitamin(`s`/`v`) | ✅ | ✅ | u, n | Return or set the characters vitamin level.
Argument is vitamin ID.

Example:
`{ "math": [ "u_vitamin('mutagen')", "=", "0" ] }`| | warmth(`s`/`v`) | ✅ | ❌ | u, n | Return the characters warmth on a body part.
Argument is bodypart ID.

Example:
The value displayed in-game is calculated as follows.
`"{ "math": [ "u_warmth_in_game", "=", "(u_warmth('torso') / 100) * 2 - 100"] }`| | weather(`s`) | ✅ | ✅ | N/A
(global) | Return or set a weather aspect

Aspect must be one of:
`temperature` (in Kelvin),
`humidity` (as percentage),
`pressure` (in millibar),
`windpower` (in mph).
`precipitation` (in mm / h) either 0.5 (very_light ), 1.5 (light), or 3 (heavy). Read only.

Temperature conversion functions are available: `celsius()`, `fahrenheit()`, `from_celsius()`, and `from_fahrenheit()`.

Examples:
`{ "math": [ "weather('temperature')", "<", "from_fahrenheit( 33 )" ] }`
`{ "math": [ "fahrenheit( weather('temperature') )", "==", "21" ] }`| diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index c2c85cf24a85a..0f6e6a48fdd04 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -590,9 +590,9 @@ bool _friend_match_filter_character( Character const &beta, Character const &guy } bool _filter_character( Character const &beta, Character const &guy, int radius, - tripoint_abs_ms const &loc, character_filter filter ) + tripoint_abs_ms const &loc, character_filter filter, bool allow_hallucinations ) { - if( !guy.is_hallucination() && ( beta.getID() != guy.getID() ) ) { + if( ( !guy.is_hallucination() || allow_hallucinations ) && ( beta.getID() != guy.getID() ) ) { return _friend_match_filter_character( beta, guy, filter ) && radius >= rl_dist( guy.get_location(), loc ); } @@ -604,6 +604,7 @@ std::function _characters_nearby_eval( char scope, { diag_value radius_val( 1000.0 ); diag_value filter_val( std::string{ "any" } ); + diag_value allow_hallucinations_val( 0.0 ); std::optional loc_var; if( kwargs.count( "radius" ) != 0 ) { radius_val = *kwargs.at( "radius" ); @@ -613,13 +614,16 @@ std::function _characters_nearby_eval( char scope, } if( kwargs.count( "location" ) != 0 ) { loc_var = kwargs.at( "location" )->var(); - + } + if( kwargs.count( "allow_hallucinations" ) != 0 ) { + allow_hallucinations_val = *kwargs.at( "allow_hallucinations" ); } else if( scope == 'g' ) { throw std::invalid_argument( string_format( R"("characters_nearby" needs either an actor scope (u/n) or a 'location' kwarg)" ) ); } - return [beta = is_beta( scope ), params, loc_var, filter_val, radius_val ]( dialogue & d ) { + return [beta = is_beta( scope ), params, loc_var, filter_val, radius_val, + allow_hallucinations_val ]( dialogue & d ) { tripoint_abs_ms loc; if( loc_var.has_value() ) { loc = get_tripoint_from_var( loc_var, d ); @@ -640,10 +644,16 @@ std::function _characters_nearby_eval( char scope, debugmsg( R"(Unknown attitude filter "%s" for characters_nearby(), counting all characters)", filter_str ); } + bool allow_hallucinations = false; + int const hallucinations_int = static_cast( allow_hallucinations_val.dbl( d ) ); + if( hallucinations_int != 0 ) { + allow_hallucinations = true; + } std::vector const targets = g->get_characters_if( [&beta, &d, &radius, - &loc, filter ]( const Character & guy ) { - return _filter_character( *d.actor( beta )->get_character(), guy, radius, loc, filter ); + &loc, filter, allow_hallucinations ]( const Character & guy ) { + return _filter_character( *d.actor( beta )->get_character(), guy, radius, loc, filter, + allow_hallucinations ); } ); return static_cast( targets.size() ); }; @@ -1233,6 +1243,19 @@ std::function value_or_eval( char /* scope */, }; } +std::function vision_range_eval( char scope, + std::vector const &/* params */, diag_kwargs const &/* kwargs */ ) +{ + return[beta = is_beta( scope )]( dialogue const & d ) { + if( d.actor( beta )->get_character() ) { + return static_cast( d.actor( beta ) ) + ->get_character() + ->unimpaired_range(); + } + return 0; + }; +} + std::function vitamin_eval( char scope, std::vector const ¶ms, diag_kwargs const &/* kwargs */ ) { @@ -1381,6 +1404,7 @@ std::map const dialogue_eval_f{ { "val", { "un", 1, u_val } }, { "value_or", { "g", 2, value_or_eval } }, { "vitamin", { "un", 1, vitamin_eval } }, + { "vision_range", { "un", 0, vision_range_eval } }, { "warmth", { "un", 1, warmth_eval } }, { "weather", { "g", 1, weather_eval } }, }; From 1e5f1b43a4c7c66f6b291e4b0c4ba7387ff67e09 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 09:22:29 -0800 Subject: [PATCH 140/202] updates --- .../effects_on_condition/bionic_eocs.json | 2 +- .../mutation_eocs/mutation_effect_eocs.json | 22 +++++++++---------- src/math_parser_diag.cpp | 8 +++---- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/data/json/effects_on_condition/bionic_eocs.json b/data/json/effects_on_condition/bionic_eocs.json index 9b1f08db099e4..57e730b083be8 100644 --- a/data/json/effects_on_condition/bionic_eocs.json +++ b/data/json/effects_on_condition/bionic_eocs.json @@ -146,7 +146,7 @@ "type": "effect_on_condition", "id": "EOC_BIO_SONAR_activated", "recurrence": [ "1 seconds", "1 seconds" ], - "condition": { "not": { "u_has_effect": "subaquatic_sonar" } }, + "condition": { "and": [ { "not": { "u_has_effect": "subaquatic_sonar" } }, { "u_has_bionics": "bio_sonar" } ] }, "effect": [ { "u_add_effect": "subaquatic_sonar", "duration": "PERMANENT" } ] }, { diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index 9839c2eb6baba..66c22f205bdb2 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -705,7 +705,7 @@ "and": [ { "u_has_flag": "SOCIAL1" }, { "not": { "u_has_effect": "took_prozac" } }, - { "math": [ "u_friends_nearby('radius': 30)", ">", "0" ] }, + { "math": [ "u_characters_nearby('radius': 30)", ">", "0" ] }, { "not": { "u_has_effect": "narcosis" } } ] }, @@ -723,7 +723,7 @@ "and": [ { "u_has_flag": "SOCIAL2" }, { "not": { "u_has_effect": "took_prozac" } }, - { "math": [ "u_friends_nearby('radius': 30)", ">", "0" ] }, + { "math": [ "u_characters_nearby('radius': 30, 'attitude': 'allies')", ">", "0" ] }, { "not": { "u_has_effect": "narcosis" } } ] }, @@ -736,26 +736,25 @@ { "type": "effect_on_condition", "id": "EOC_social2_penalty", - "recurrence": [ "12 hours", "48 hours" ], + "recurrence": [ "1 hours", "6 hours" ], "condition": { "and": [ { "u_has_flag": "SOCIAL2" }, - { "math": [ "u_friends_nearby('radius': 30)", "==", "0" ] }, + { "math": [ "u_characters_nearby('radius': 30, 'attitude': 'allies')", "==", "0" ] }, { "not": { "u_has_effect": "took_prozac" } }, { "not": { "u_has_effect": "valium" } }, { "not": { "u_has_effect": "took_xanax" } }, { "not": { "u_has_effect": "drunk" } }, - { "not": { "u_has_effect": "social_satisfied" } }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ + { "u_lose_effect": "social_satisfied" }, { "u_add_effect": "social_dissatisfied", "duration": "PERMANENT", "intensity": { "math": [ "u_effect_intensity('social_dissatisfied') + 1" ] } }, - { "u_lose_effect": "social_satisfied" }, { "u_message": "You could use some friendly company.", "type": "bad" } ] }, @@ -767,7 +766,7 @@ "and": [ { "u_has_flag": "ASOCIAL1" }, { "not": { "u_has_effect": "took_prozac" } }, - { "math": [ "u_friends_nearby('radius': 30)", "==", "0" ] }, + { "math": [ "u_characters_nearby('radius': 30)", "==", "0" ] }, { "not": { "u_has_effect": "narcosis" } } ] }, @@ -784,7 +783,7 @@ "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, - { "math": [ "u_friends_nearby('radius': 30)", "==", "0" ] }, + { "math": [ "u_characters_nearby('radius': 30)", "==", "0" ] }, { "not": { "u_has_effect": "took_prozac" } }, { "not": { "u_has_effect": "narcosis" } } ] @@ -794,26 +793,25 @@ { "type": "effect_on_condition", "id": "EOC_asocial2_penalty", - "recurrence": [ "12 hours", "48 hours" ], + "recurrence": [ "1 hours", "6 hours" ], "condition": { "and": [ { "u_has_flag": "ASOCIAL2" }, { "not": { "u_has_effect": "took_prozac" } }, - { "not": { "u_has_effect": "asocial_satisfied" } }, { "not": { "u_has_effect": "valium" } }, { "not": { "u_has_effect": "took_xanax" } }, { "not": { "u_has_effect": "drunk" } }, - { "math": [ "u_friends_nearby('radius': 30)", ">", "0" ] }, + { "math": [ "u_characters_nearby('radius': 30)", ">", "0" ] }, { "not": { "u_has_effect": "narcosis" } } ] }, "effect": [ + { "u_lose_effect": "asocial_satisfied" }, { "u_add_effect": "asocial_dissatisfied", "duration": "PERMANENT", "intensity": { "math": [ "u_effect_intensity('asocial_dissatisfied') + 1" ] } }, - { "u_lose_effect": "asocial_satisfied" }, { "u_message": "You'd really rather be by yourself.", "type": "bad" } ] }, diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index 0f6e6a48fdd04..595a3aa1bb977 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -577,9 +577,9 @@ bool _friend_match_filter_character( Character const &beta, Character const &guy { switch( filter ) { case character_filter::allies: - return guy.is_ally( beta ) != 0; + return guy.is_ally( beta ); case character_filter::not_allies: - return !guy.is_ally( beta ) == 0; + return !guy.is_ally( beta ); case character_filter::hostile: return guy.attitude_to( beta ) == Creature::Attitude::HOSTILE || ( beta.is_avatar() && guy.is_npc() && guy.as_npc()->guaranteed_hostile() ); @@ -623,7 +623,7 @@ std::function _characters_nearby_eval( char scope, } return [beta = is_beta( scope ), params, loc_var, filter_val, radius_val, - allow_hallucinations_val ]( dialogue & d ) { + allow_hallucinations_val ]( dialogue & d ) { tripoint_abs_ms loc; if( loc_var.has_value() ) { loc = get_tripoint_from_var( loc_var, d ); @@ -650,7 +650,7 @@ std::function _characters_nearby_eval( char scope, allow_hallucinations = true; } - std::vector const targets = g->get_characters_if( [&beta, &d, &radius, + std::vector const targets = g->get_characters_if( [ &beta, &d, &radius, &loc, filter, allow_hallucinations ]( const Character & guy ) { return _filter_character( *d.actor( beta )->get_character(), guy, radius, loc, filter, allow_hallucinations ); From e43b452c399b37b4b6b06abaa835a0f54aca53a0 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 09:24:52 -0800 Subject: [PATCH 141/202] Update mutations.json --- data/json/mutations/mutations.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index ef25c7f4895dc..cabd87ac4d7ee 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -1173,7 +1173,7 @@ "points": 1, "types": [ "SOCIAL" ], "cancels": [ "ASOCIAL1", "ASOCIAL2" ], - "description": "You are a social creature. Without occasionally seeing a friendly face, you'll start to feel unhappy.", + "description": "You are a social creature. Spending time with friends will improve your mood, but if you're alone too long, you'll start to feel unhappy.", "starting_trait": true, "mixed_effect": true, "flags": [ "SOCIAL2" ], @@ -1199,7 +1199,7 @@ "points": 1, "types": [ "SOCIAL" ], "cancels": [ "SOCIAL1", "SOCIAL2" ], - "description": "You'd really rather be by yourself. Spending time around other people will bring your mood down.", + "description": "You'd really rather be by yourself. Being alone will improve your mood, but spending too much time around other people can grate on your nerves.", "starting_trait": true, "mixed_effect": true, "flags": [ "ASOCIAL2" ], From a58b311f71e0d37fb280e4b9c0da2ea518d3d9fb Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 09:26:24 -0800 Subject: [PATCH 142/202] Update src/math_parser_diag.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/math_parser_diag.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index 595a3aa1bb977..7f2061e1466f5 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -623,7 +623,7 @@ std::function _characters_nearby_eval( char scope, } return [beta = is_beta( scope ), params, loc_var, filter_val, radius_val, - allow_hallucinations_val ]( dialogue & d ) { + allow_hallucinations_val ]( dialogue & d ) { tripoint_abs_ms loc; if( loc_var.has_value() ) { loc = get_tripoint_from_var( loc_var, d ); From 5bd338c0cd33eb6bc75d26a12d5189655b46ba3f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 18:39:32 -0800 Subject: [PATCH 143/202] Update .vscode/settings.json Co-authored-by: andrei <68240139+andrei8l@users.noreply.github.com> --- .vscode/settings.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e78572d4bf61e..20bd09ea82c04 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -64,8 +64,4 @@ "github-actions.remote-name": "upstream", // NOTE: This disable the plugins formatting so astyle is used. "C_Cpp.formatting": "disabled", - "files.associations": { - "regex": "cpp", - "sstream": "cpp" - }, } From a129f6d62a136a85925ad290c33f8c11853075eb Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 18:39:49 -0800 Subject: [PATCH 144/202] Update doc/NPCs.md Co-authored-by: andrei <68240139+andrei8l@users.noreply.github.com> --- doc/NPCs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/NPCs.md b/doc/NPCs.md index dd43b39c32f87..5493b989be66a 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -1357,7 +1357,7 @@ _some functions support array arguments or kwargs, denoted with square brackets | time_since(`v`)
time_since('cataclysm')
time_since('midnight') | ✅ | ❌ | N/A
(global) | Convenience function that returns a numeric value (in turns) for the time period since time point stored in variable.

Special values:
`cataclysm` - return time since start of cataclysm
`midnight` - return time since midnight

Optional kwargs:
`unit`: specify return unit. Assumes `turns` if unspecified or empty.

Returns -1 if the argument is an undefined variable

Example:
`{ "math": [ "time_since(u_timer_caravan_RandEnc)", ">", "time('1 h')" ] }`
`{ "math": [ "time_since('cataclysm', 'unit':'years') > 1" ] }`| | time_until_eoc(`s`/`v`) | ✅ | ❌ | N/A
(global) | Returns time until next scheduled run of an EOC. Argument is EOC id.

Optional kwargs:
`unit`: specify return unit

Returns -1 is the EOC is not scheduled. | | val(`s`) | ✅ | varies | u, n | Return or set a Character or item value. Argument is a [Character/item aspect](#list-of-character-and-item-aspects).

These are all in one function for legacy reasons and are generally poorly tested. If you need one of them, consider porting it to a native math function.

Example:
`{ "math": [ "u_val('strength')", "=", "2" ] }`| -| vision_range(`s`/`v`) | ✅ | ❌ | u, n | Return the character's visual range, adjusted by their mutations, effects, and other issues.

Example:
`"{ "math": [ "u_vision_range", "<", "30"] }`| +| vision_range() | ✅ | ❌ | u, n | Return the character's visual range, adjusted by their mutations, effects, and other issues.

Example:
`"{ "math": [ "u_vision_range", "<", "30"] }`| | vitamin(`s`/`v`) | ✅ | ✅ | u, n | Return or set the characters vitamin level.
Argument is vitamin ID.

Example:
`{ "math": [ "u_vitamin('mutagen')", "=", "0" ] }`| | warmth(`s`/`v`) | ✅ | ❌ | u, n | Return the characters warmth on a body part.
Argument is bodypart ID.

Example:
The value displayed in-game is calculated as follows.
`"{ "math": [ "u_warmth_in_game", "=", "(u_warmth('torso') / 100) * 2 - 100"] }`| | weather(`s`) | ✅ | ✅ | N/A
(global) | Return or set a weather aspect

Aspect must be one of:
`temperature` (in Kelvin),
`humidity` (as percentage),
`pressure` (in millibar),
`windpower` (in mph).
`precipitation` (in mm / h) either 0.5 (very_light ), 1.5 (light), or 3 (heavy). Read only.

Temperature conversion functions are available: `celsius()`, `fahrenheit()`, `from_celsius()`, and `from_fahrenheit()`.

Examples:
`{ "math": [ "weather('temperature')", "<", "from_fahrenheit( 33 )" ] }`
`{ "math": [ "fahrenheit( weather('temperature') )", "==", "21" ] }`| From 55fc9ec23a0a4fa893a48bf9e28fb05ddd2bb6e0 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 18:41:20 -0800 Subject: [PATCH 145/202] Update src/math_parser_diag.cpp Co-authored-by: andrei <68240139+andrei8l@users.noreply.github.com> --- src/math_parser_diag.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index 7f2061e1466f5..456460c9ddb2e 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -612,11 +612,11 @@ std::function _characters_nearby_eval( char scope, if( kwargs.count( "attitude" ) != 0 ) { filter_val = *kwargs.at( "attitude" ); } - if( kwargs.count( "location" ) != 0 ) { - loc_var = kwargs.at( "location" )->var(); - } if( kwargs.count( "allow_hallucinations" ) != 0 ) { allow_hallucinations_val = *kwargs.at( "allow_hallucinations" ); + } + if( kwargs.count( "location" ) != 0 ) { + loc_var = kwargs.at( "location" )->var(); } else if( scope == 'g' ) { throw std::invalid_argument( string_format( R"("characters_nearby" needs either an actor scope (u/n) or a 'location' kwarg)" ) ); From 27e311e92d558eb132b881f7047f9169e9330755 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 21:39:04 -0800 Subject: [PATCH 146/202] Update src/lightmap.cpp Co-authored-by: andrei <68240139+andrei8l@users.noreply.github.com> --- src/lightmap.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 6ea7329ccc690..67b51f801742c 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -90,7 +90,6 @@ bool map::build_transparency_cache( const int zlev ) level_cache &map_cache = get_cache( zlev ); auto &transparent_cache_wo_fields = map_cache.transparent_cache_wo_fields; auto &transparency_cache = map_cache.transparency_cache; - //auto &transparency_cache_wo_solids = map_cache.transparent_cache_wo_solids; auto &outside_cache = map_cache.outside_cache; if( map_cache.transparency_cache_dirty.none() ) { From 5f83ad2d92f4d4d876a8db60f08c4853c57a782f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 21:40:44 -0800 Subject: [PATCH 147/202] Update lightmap.cpp --- src/lightmap.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 67b51f801742c..27a594cc83bab 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -144,7 +144,6 @@ bool map::build_transparency_cache( const int zlev ) } float value_wo_fields = value; Character &player_character = get_player_character(); - if( !player_character.vision_mode_cache[IR_VISION] ) { for( const auto &fld : cur_submap->get_field( sp ) ) { const field_intensity_level &i_level = fld.second.get_intensity_level(); if( i_level.transparent ) { @@ -153,7 +152,6 @@ bool map::build_transparency_cache( const int zlev ) // Fields are either transparent or not, however we want some to be translucent value = value * i_level.translucency; } - } return std::make_pair( value, value_wo_fields ); }; From ec5f386fc950560916c39a83dd0d252ec069b3c2 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 22:19:52 -0800 Subject: [PATCH 148/202] Update lightmap.cpp --- src/lightmap.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 27a594cc83bab..dd3552e0d04dd 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -143,15 +143,15 @@ bool map::build_transparency_cache( const int zlev ) value *= sight_penalty; } float value_wo_fields = value; - Character &player_character = get_player_character(); - for( const auto &fld : cur_submap->get_field( sp ) ) { - const field_intensity_level &i_level = fld.second.get_intensity_level(); - if( i_level.transparent ) { - continue; - } - // Fields are either transparent or not, however we want some to be translucent - value = value * i_level.translucency; + for( const auto &fld : cur_submap->get_field( sp ) ) { + const field_intensity_level &i_level = fld.second.get_intensity_level(); + if( i_level.transparent ) { + continue; } + // Fields are either transparent or not, however we want some to be translucent + value = value * i_level.translucency; + } + // TODO: [lightmap] Have glass reduce light as well return std::make_pair( value, value_wo_fields ); }; From 59929ba5e2b98b3f569bc16c4a7e940a10333c07 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 22:22:44 -0800 Subject: [PATCH 149/202] Update lightmap.cpp --- src/lightmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lightmap.cpp b/src/lightmap.cpp index dd3552e0d04dd..8ef16cc51eef2 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -814,7 +814,7 @@ bool map::pl_line_of_sight( const tripoint &t, const int max_range ) const // Character &player_character = get_player_character(); const level_cache &map_cache = get_cache_ref( t.z ); - if( map_cache.seen_cache[t.x][t.y] > 0.075f ) { + if( map_cache.camera_cache[t.x][t.y] > 0.075f ) { return true; } From 09bd8bc9c2b3c6e083cfbb05f0f118e78120ab08 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 22:23:49 -0800 Subject: [PATCH 150/202] Update lightmap.cpp --- src/lightmap.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 8ef16cc51eef2..bae55808389d9 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -812,7 +812,6 @@ bool map::pl_line_of_sight( const tripoint &t, const int max_range ) const return false; } - // Character &player_character = get_player_character(); const level_cache &map_cache = get_cache_ref( t.z ); if( map_cache.camera_cache[t.x][t.y] > 0.075f ) { return true; From ff78f8be345ba0148ed8b89f976d068a87752a6b Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sat, 10 Feb 2024 22:26:40 -0800 Subject: [PATCH 151/202] Update map.h --- src/map.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/map.h b/src/map.h index a8b81007cc4a2..627382a354fe6 100644 --- a/src/map.h +++ b/src/map.h @@ -1830,11 +1830,6 @@ class map * Used for infrared. */ bool pl_line_of_sight( const tripoint &t, int max_range ) const; - /** - * Basically line_of_sight, but blocked by transparent objects - * and other sound baffling - */ - bool pl_line_of_sound( const tripoint &t, int max_range ) const; std::set dirty_vehicle_list; /** return @ref abs_sub */ From 795d44b62458b28b68036608ebb70df5e1c9c9e2 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 11 Feb 2024 01:16:38 -0800 Subject: [PATCH 152/202] Balance etc --- data/json/bionics.json | 1 + data/json/mutations/mutations.json | 5 +++-- src/character.cpp | 12 ++++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/data/json/bionics.json b/data/json/bionics.json index a915c6e05bfd6..b8ab0e02e72c7 100644 --- a/data/json/bionics.json +++ b/data/json/bionics.json @@ -815,6 +815,7 @@ "name": { "str": "Enhanced Hearing" }, "description": "When this bionic is active, your hearing will be drastically improved, allowing you to hear ten times better than the average person. Additionally, high-intensity sounds will be automatically dampened before they can damage your hearing.", "occupied_bodyparts": [ [ "head", 2 ] ], + "mutation_conflicts": [ "BATEARS" ], "flags": [ "BIONIC_TOGGLED", "IMMUNE_HEARING_DAMAGE" ], "active_flags": [ "SUPER_HEARING" ], "auto_deactivates": [ "bio_earplugs" ], diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index cabd87ac4d7ee..756c2746cdc92 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -490,10 +490,11 @@ "visibility": 8, "description": "Your broad, pointed ears are like radar dishes. You can hear even the slightest sounds at great distance, and are less likely to be deafened by loud noises.", "types": [ "HEARING" ], - "flags": [ "HEARING_PROTECTION" ], + "//": "Super_hearing multiplies hearing by 3.5 and let you crack safes. A bat mutant is a bit better at hearing overall than someone with GOODHEARING and a CBM.", + "flags": [ "HEARING_PROTECTION", "SUPER_HEARING" ], "prereqs": [ "GOODHEARING" ], "category": [ "CHIROPTERAN" ], - "hearing_modifier": 2 + "hearing_modifier": 1.8 }, { "type": "mutation", diff --git a/src/character.cpp b/src/character.cpp index cb7eb9d3d3732..46125cded6442 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10718,8 +10718,8 @@ void Character::echo_pulse() "none", "none" ); } for( tripoint origin : points_in_radius( pos(), pulse_range ) ) { - if( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, 2 ) ) { - sounds::sound( origin, 6, sounds::sound_t::sensory, _( "click." ), true, + if( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, pulse_range ) ) { + sounds::sound( origin, 7, sounds::sound_t::sensory, _( "click." ), true, "none", "none" ); } const trap &tr = here.tr_at( origin ); @@ -10730,7 +10730,7 @@ void Character::echo_pulse() add_known_trap( origin, tr ); } Creature *critter = get_creature_tracker().creature_at( origin, true ); - if( critter && here.pl_line_of_sight( origin, 1 ) ) { + if( critter && here.pl_line_of_sight( origin, pulse_range ) ) { switch( critter->get_size() ) { case creature_size::tiny: echo_volume = 1; @@ -10739,13 +10739,13 @@ void Character::echo_pulse() echo_volume = 2; break; case creature_size::medium: - echo_volume = 3; + echo_volume = 4; break; case creature_size::large: - echo_volume = 4; + echo_volume = 5; break; case creature_size::huge: - echo_volume = 5; + echo_volume = 7; break; case creature_size::num_sizes: debugmsg( "ERROR: Invalid Creature size class." ); From 20734848dbacbf0aa00ce2b6b71995d181950c42 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 11 Feb 2024 01:37:06 -0800 Subject: [PATCH 153/202] More little tweaks --- data/json/effects.json | 3 +-- src/character.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/data/json/effects.json b/data/json/effects.json index f9e0db9ec109b..184db33231c7a 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -2155,9 +2155,8 @@ "max_intensity": 3, "max_duration": "20 m", "rating": "bad", - "show_in_info": true, "blood_analysis_description": "Draculin", - "//": "yes it's actually called that. It is also the strongest anti-clotting factor known to science. It inhibits the synthesis of thrombin, so most earth animals/mutants should be affected, except bugs.", + "//": "yes it's actually called that. It is also the strongest anti-clotting factor known to science. It inhibits the synthesis of thrombin, so most earth animals/mutants should be affected, except maybe bugs.", "immune_flags": [ "DRACULIN_IMMUNE" ], "effect_dur_scaling": [ { "effect_id": "bleed", "modifier": 2.0, "same_bp": false } ] }, diff --git a/src/character.cpp b/src/character.cpp index 46125cded6442..b51cb268a4ad6 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10719,7 +10719,11 @@ void Character::echo_pulse() } for( tripoint origin : points_in_radius( pos(), pulse_range ) ) { if( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, pulse_range ) ) { - sounds::sound( origin, 7, sounds::sound_t::sensory, _( "click." ), true, + sounds::sound( origin, 5, sounds::sound_t::sensory, _( "clack." ), true, + "none", "none" ); + // This only counts obstacles which can be moved through, so we make a very quiet noise. + } else if( is_obstacle( origin ) && here.pl_line_of_sight( origin, pulse_range ) ) { + sounds::sound( origin, 1, sounds::sound_t::sensory, _( "click." ), true, "none", "none" ); } const trap &tr = here.tr_at( origin ); @@ -10739,13 +10743,13 @@ void Character::echo_pulse() echo_volume = 2; break; case creature_size::medium: - echo_volume = 4; + echo_volume = 3; break; case creature_size::large: - echo_volume = 5; + echo_volume = 4; break; case creature_size::huge: - echo_volume = 7; + echo_volume = 5; break; case creature_size::num_sizes: debugmsg( "ERROR: Invalid Creature size class." ); From b0860ead8802c98132389cfe6343dc09ab1b65ac Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 11 Feb 2024 02:10:52 -0800 Subject: [PATCH 154/202] Update src/character.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/character.cpp b/src/character.cpp index b51cb268a4ad6..a4fb590e558a4 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10721,7 +10721,7 @@ void Character::echo_pulse() if( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, pulse_range ) ) { sounds::sound( origin, 5, sounds::sound_t::sensory, _( "clack." ), true, "none", "none" ); - // This only counts obstacles which can be moved through, so we make a very quiet noise. + // This only counts obstacles which can be moved through, so we make a very quiet noise. } else if( is_obstacle( origin ) && here.pl_line_of_sight( origin, pulse_range ) ) { sounds::sound( origin, 1, sounds::sound_t::sensory, _( "click." ), true, "none", "none" ); From 516de481957dbc6490b393a58460e0982c6a3603 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 11 Feb 2024 02:11:39 -0800 Subject: [PATCH 155/202] Update character.cpp --- src/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/character.cpp b/src/character.cpp index a4fb590e558a4..6d9b8d55e5ceb 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10721,7 +10721,7 @@ void Character::echo_pulse() if( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, pulse_range ) ) { sounds::sound( origin, 5, sounds::sound_t::sensory, _( "clack." ), true, "none", "none" ); - // This only counts obstacles which can be moved through, so we make a very quiet noise. + // This only counts obstacles which can be moved through, so the echo is pretty quiet. } else if( is_obstacle( origin ) && here.pl_line_of_sight( origin, pulse_range ) ) { sounds::sound( origin, 1, sounds::sound_t::sensory, _( "click." ), true, "none", "none" ); From 0944f69667827caa6cd504523493039db6196c96 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Mon, 12 Feb 2024 08:08:05 -0800 Subject: [PATCH 156/202] Update bonuses.cpp --- src/bonuses.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bonuses.cpp b/src/bonuses.cpp index 60c5ec70472b1..6d0d7592b260f 100644 --- a/src/bonuses.cpp +++ b/src/bonuses.cpp @@ -14,8 +14,8 @@ static const skill_id skill_archery( "archery" ); static const skill_id skill_bashing( "bashing" ); static const skill_id skill_cutting( "cutting" ); -static const skill_id skill_drive( "drive" ); static const skill_id skill_dodge( "dodge" ); +static const skill_id skill_drive( "drive" ); static const skill_id skill_firstaid( "firstaid" ); static const skill_id skill_gun( "gun" ); static const skill_id skill_launcher( "launcher" ); From 5bdf6cddb554ca83fe59f255ba61ae0a3507f2bc Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Mon, 12 Feb 2024 08:31:11 -0800 Subject: [PATCH 157/202] Update consumption.cpp --- src/consumption.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/consumption.cpp b/src/consumption.cpp index a68aa5d1a0ff8..75c1d35dafe37 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1356,6 +1356,8 @@ void Character::modify_morale( item &food, const int nutr ) add_msg_if_player( _( "Meh. You've eaten worse." ) ); } else if( sapiovore ) { add_msg_if_player( _( "Mmh. Tastes like venison." ) ); + } else if( has_flag( json_flag_HEMOVORE ) && food.has_flag( flag_HEMOVORE_FUN ) ) { + add_msg_if_player( _( "The human blood tastes as good as any other." ) ); } else if( spiritual ) { add_msg_if_player( m_bad, _( "This is probably going to count against you if there's still an afterlife." ) ); From ca0b826b54a29b90fd02d5a2483277bc935d37bf Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:55:01 -0800 Subject: [PATCH 158/202] Update morale_types.cpp --- src/morale_types.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/morale_types.cpp b/src/morale_types.cpp index 13116fbbe803d..a607fb9bb2418 100644 --- a/src/morale_types.cpp +++ b/src/morale_types.cpp @@ -99,7 +99,6 @@ static const morale_type morale_accomplishment( "morale_accomplishment" ); static const morale_type morale_antifruit( "morale_antifruit" ); static const morale_type morale_antijunk( "morale_antijunk" ); static const morale_type morale_antiwheat( "morale_antiwheat" ); -static const morale_type morale_asocial( "morale_asocial" ); static const morale_type morale_ate_with_table( "morale_ate_with_table" ); static const morale_type morale_ate_without_table( "morale_ate_without_table" ); static const morale_type morale_book( "morale_book" ); @@ -165,7 +164,6 @@ static const morale_type morale_pyromania_nofire( "morale_pyromania_nofire" ); static const morale_type morale_pyromania_startfire( "morale_pyromania_startfire" ); static const morale_type morale_scream( "morale_scream" ); static const morale_type morale_shave( "morale_shave" ); -static const morale_type morale_social( "morale_social" ); static const morale_type morale_support( "morale_support" ); static const morale_type morale_sweettooth( "morale_sweettooth" ); static const morale_type morale_vegetarian( "morale_vegetarian" ); From 482cec0d476fd683154580cfa0472691233350e9 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 14 Feb 2024 02:07:29 -0800 Subject: [PATCH 159/202] Update mutations.json --- data/mods/Aftershock/mutations/mutations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/mods/Aftershock/mutations/mutations.json b/data/mods/Aftershock/mutations/mutations.json index c0dc455a8908b..f73785db4aed8 100644 --- a/data/mods/Aftershock/mutations/mutations.json +++ b/data/mods/Aftershock/mutations/mutations.json @@ -482,7 +482,7 @@ "description": "Your skin has patches of light fur. This has no impact on your life except marking you as not fully human.", "types": [ "SKIN", "BODY_ARMOR", "BODY_ARMOR_MOD", "ARMOR" ], "changes_to": [ "LIGHTFUR" ], - "category": [ "MASTODON", "SPIDER", "MOUSE", "BEAST", "CATTLE", "RAT", "FELINE", "LUPINE", "RABBIT", "INSECT", "URSINE" ] + "category": [ "MASTODON", "SPIDER", "MOUSE", "BEAST", "CATTLE", "RAT", "FELINE", "LUPINE", "RABBIT", "INSECT", "URSINE", "CHIROPTERAN" ] }, { "type": "mutation", From 9651b39f1f1af6e15b50d71645d339307d0003b4 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Wed, 14 Feb 2024 02:58:20 -0800 Subject: [PATCH 160/202] Update data/mods/Aftershock/mutations/mutations.json Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- data/mods/Aftershock/mutations/mutations.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/data/mods/Aftershock/mutations/mutations.json b/data/mods/Aftershock/mutations/mutations.json index f73785db4aed8..005d8b0847e3e 100644 --- a/data/mods/Aftershock/mutations/mutations.json +++ b/data/mods/Aftershock/mutations/mutations.json @@ -482,7 +482,20 @@ "description": "Your skin has patches of light fur. This has no impact on your life except marking you as not fully human.", "types": [ "SKIN", "BODY_ARMOR", "BODY_ARMOR_MOD", "ARMOR" ], "changes_to": [ "LIGHTFUR" ], - "category": [ "MASTODON", "SPIDER", "MOUSE", "BEAST", "CATTLE", "RAT", "FELINE", "LUPINE", "RABBIT", "INSECT", "URSINE", "CHIROPTERAN" ] + "category": [ + "MASTODON", + "SPIDER", + "MOUSE", + "BEAST", + "CATTLE", + "RAT", + "FELINE", + "LUPINE", + "RABBIT", + "INSECT", + "URSINE", + "CHIROPTERAN" + ] }, { "type": "mutation", From 7151f1a07cd24c502a225163d48973ef6453c7bd Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:39:26 -0800 Subject: [PATCH 161/202] Remove social and fang stuff --- data/json/effects.json | 11 -- data/json/flags.json | 24 --- data/json/items/armor/integrated.json | 52 ------ data/json/mutations/mutations.json | 159 +++++++++--------- data/json/techniques.json | 225 -------------------------- src/bonuses.cpp | 111 +------------ src/bonuses.h | 20 +-- src/martialarts.cpp | 9 +- src/martialarts.h | 5 +- 9 files changed, 84 insertions(+), 532 deletions(-) diff --git a/data/json/effects.json b/data/json/effects.json index 184db33231c7a..ad9491eec3af0 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -2149,17 +2149,6 @@ ], "flags": [ "EFFECT_LIMB_SCORE_MOD" ] }, - { - "type": "effect_type", - "id": "anticoagulant_draculin", - "max_intensity": 3, - "max_duration": "20 m", - "rating": "bad", - "blood_analysis_description": "Draculin", - "//": "yes it's actually called that. It is also the strongest anti-clotting factor known to science. It inhibits the synthesis of thrombin, so most earth animals/mutants should be affected, except maybe bugs.", - "immune_flags": [ "DRACULIN_IMMUNE" ], - "effect_dur_scaling": [ { "effect_id": "bleed", "modifier": 2.0, "same_bp": false } ] - }, { "type": "effect_type", "id": "anticoagulant", diff --git a/data/json/flags.json b/data/json/flags.json index 751db6345ca91..3f622efc95def 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2423,22 +2423,6 @@ "type": "json_flag", "//": "This item contains blood and satisfies a hemovore's requirements." }, - { - "id": "SOCIAL1", - "type": "json_flag" - }, - { - "id": "SOCIAL2", - "type": "json_flag" - }, - { - "id": "ASOCIAL1", - "type": "json_flag" - }, - { - "id": "ASOCIAL2", - "type": "json_flag" - }, { "id": "QUADRUPED_CROUCH", "type": "json_flag", @@ -2453,13 +2437,5 @@ "id": "HEARING_PROTECTION", "type": "json_flag", "//": "This character treats received sounds as half as loud for the purposes of determing hearing damage." - }, - { - "id": "DRACULIN_VENOM", - "type": "json_flag" - }, - { - "id": "VENOM1", - "type": "json_flag" } ] diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index f5461cb458d05..726d8d304aacb 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -1583,57 +1583,5 @@ "relic_data": { "passive_effects": [ { "has": "WORN", "condition": { "not": "u_has_weapon" }, "values": [ { "value": "ATTACK_SPEED", "add": -20 } ] } ] } - }, - { - "id": "integrated_fangs", - "type": "ARMOR", - "category": "armor", - "name": { "str_sp": "fangs" }, - "description": "A pair of fangs sharp and sturdy enough to use in combat.", - "weight": "50 g", - "volume": "100 ml", - "price": 0, - "price_postapoc": 0, - "material": [ "bone" ], - "symbol": ",", - "color": "white", - "warmth": 5, - "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], - "techniques": [ "FANGS_BITE", "FANGS_BITE_NATURAL" ], - "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], - "armor": [ - { - "material": [ { "type": "bone", "covered_by_mat": 100, "thickness": 3.2 } ], - "covers": [ "mouth" ], - "coverage": 0, - "encumbrance": 0 - } - ] - }, - { - "id": "integrated_vampire_fangs", - "type": "ARMOR", - "category": "armor", - "name": { "str_sp": "vampire fangs" }, - "description": "A wicked pair of fangs, pointed and surgically sharp.", - "weight": "100 g", - "volume": "200 ml", - "price": 0, - "price_postapoc": 0, - "material": [ "bone" ], - "symbol": ",", - "color": "white", - "warmth": 5, - "qualities": [ [ "CUT", 2 ], [ "BUTCHER", 4 ] ], - "techniques": [ "VAMPIRE_BITE", "VAMPIRE_BITE_NATURAL" ], - "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "PERSONAL", "PADDED", "PROVIDES_TECHNIQUES" ], - "armor": [ - { - "material": [ { "type": "bone", "covered_by_mat": 100, "thickness": 3.2 } ], - "covers": [ "mouth" ], - "coverage": 0, - "encumbrance": 0 - } - ] } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 756c2746cdc92..75faee59d2229 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -153,7 +153,7 @@ "dummy": true, "category": [ "HUMAN" ], "types": [ "ATTRACTIVENESS", "MUZZLE", "TEETH", "TONGUE" ], - "cancels": [ "SLIT_NOSTRILS", "LEAFNOSE", "BLOODLETTER" ], + "cancels": [ "SLIT_NOSTRILS", "LEAFNOSE" ], "description": "You have a human mouth with human teeth." }, { @@ -1154,58 +1154,6 @@ "starting_trait": true, "valid": false }, - { - "type": "mutation", - "id": "SOCIAL1", - "name": { "str": "Extrovert" }, - "points": 1, - "types": [ "SOCIAL" ], - "cancels": [ "ASOCIAL1", "ASOCIAL2" ], - "changes_to": [ "SOCIAL2" ], - "description": "You don't mind being alone, but you get a slight mood boost from being around friends.", - "starting_trait": true, - "flags": [ "SOCIAL1" ], - "category": [ "CATTLE", "ELFA", "LUPINE", "MOUSE", "RAT", "CHIROPTERAN", "BIRD", "FISH" ] - }, - { - "type": "mutation", - "id": "SOCIAL2", - "name": { "str": "Sociable" }, - "points": 1, - "types": [ "SOCIAL" ], - "cancels": [ "ASOCIAL1", "ASOCIAL2" ], - "description": "You are a social creature. Spending time with friends will improve your mood, but if you're alone too long, you'll start to feel unhappy.", - "starting_trait": true, - "mixed_effect": true, - "flags": [ "SOCIAL2" ], - "category": [ "CATTLE", "LUPINE", "MOUSE", "RAT", "CHIROPTERAN", "ELFA" ] - }, - { - "type": "mutation", - "id": "ASOCIAL1", - "name": { "str": "Introvert" }, - "points": 1, - "types": [ "SOCIAL" ], - "cancels": [ "SOCIAL1", "SOCIAL2" ], - "changes_to": [ "ASOCIAL2" ], - "description": "You can tolerate others, but you prefer to be alone. Spending time by yourself will boost your mood slightly.", - "starting_trait": true, - "flags": [ "ASOCIAL1" ], - "category": [ "SPIDER", "URSINE", "FELINE", "LIZARD", "BEAST", "CHIMERA" ] - }, - { - "type": "mutation", - "id": "ASOCIAL2", - "name": { "str": "Loner" }, - "points": 1, - "types": [ "SOCIAL" ], - "cancels": [ "SOCIAL1", "SOCIAL2" ], - "description": "You'd really rather be by yourself. Being alone will improve your mood, but spending too much time around other people can grate on your nerves.", - "starting_trait": true, - "mixed_effect": true, - "flags": [ "ASOCIAL2" ], - "category": [ "SPIDER", "URSINE", "LIZARD", "BEAST" ] - }, { "type": "mutation", "id": "LIGHTSTEP", @@ -2493,11 +2441,44 @@ "points": 2, "visibility": 2, "ugliness": 2, - "description": "Your teeth have grown into long, sharp fangs. If your mouth is uncovered, you'll sometimes bite your enemies.", + "description": "Your teeth have grown into two-inch-long fangs, allowing you to make an extra attack when conditions favor it.", "types": [ "TEETH" ], - "changes_to": [ "SABER_TEETH", "SHARKTEETH", "FANGS_VAMPIRE" ], - "category": [ "LIZARD", "FISH", "LUPINE", "FELINE", "CHIMERA", "CHIROPTERAN", "URSINE", "BEAST" ], - "integrated_armor": [ "integrated_fangs" ] + "changes_to": [ "SABER_TEETH", "SHARKTEETH", "VAMPIRE_FANGS" ], + "category": [ "LIZARD", "FISH", "LUPINE", "FELINE", "CHIMERA", "CHIROPTERAN" ], + "attacks": [ + { + "attack_text_u": "You sink your fangs into %s!", + "attack_text_npc": "%1$s sinks their fangs into %2$s!", + "blocker_mutations": [ "MUZZLE", "MUZZLE_LONG", "MUZZLE_RAT" ], + "body_part": "mouth", + "chance": 20, + "base_damage": { "damage_type": "stab", "amount": 20 } + }, + { + "attack_text_u": "You sink your fangs into %s!", + "attack_text_npc": "%1$s sinks their fangs into %2$s!", + "required_mutations": [ "MUZZLE" ], + "body_part": "mouth", + "chance": 18, + "base_damage": { "damage_type": "stab", "amount": 20 } + }, + { + "attack_text_u": "You sink your fangs into %s!", + "attack_text_npc": "%1$s sinks their fangs into %2$s!", + "required_mutations": [ "MUZZLE_LONG" ], + "body_part": "mouth", + "chance": 15, + "base_damage": { "damage_type": "stab", "amount": 20 } + }, + { + "attack_text_u": "You sink your fangs into %s!", + "attack_text_npc": "%1$s sinks their fangs into %2$s!", + "required_mutations": [ "MUZZLE_RAT" ], + "body_part": "mouth", + "chance": 19, + "base_damage": { "damage_type": "stab", "amount": 20 } + } + ] }, { "type": "mutation", @@ -2531,7 +2512,13 @@ "prereqs": [ "FANGS" ], "threshreq": [ "THRESH_CHIROPTERAN" ], "category": [ "CHIROPTERAN" ], - "integrated_armor": [ "integrated_vampire_fangs" ] + "attacks": { + "attack_text_u": "You sink your fangs into %s!", + "attack_text_npc": "%1$s sinks their fangs into %2$s!", + "body_part": "mouth", + "chance": 18, + "base_damage": [ { "damage_type": "cut", "amount": 3 }, { "damage_type": "bash", "amount": 2 } ] + } }, { "type": "mutation", @@ -7564,16 +7551,6 @@ "threshreq": [ "THRESH_CHIROPTERAN" ], "vitamin_rates": [ [ "iron", 800 ], [ "vitC", -600 ] ] }, - { - "type": "mutation", - "id": "BLOODLETTER", - "name": { "str": "Bloodletter" }, - "points": 2, - "description": "Your saliva contains powerful anticoagulants. If you have a bite attack, it will prolong the bleeding of most animals, including humans and the undead. It may be less effective against insects and stranger creatures.", - "flags": [ "DRACULIN_VENOM" ], - "prereqs": [ "HEMOVORE", "BLOODFEEDER" ], - "category": [ "CHIROPTERAN" ] - }, { "type": "mutation", "id": "PONDEROUS1", @@ -9678,20 +9655,38 @@ "category": [ "ALPHA" ], "starting_trait": true }, - { - "type": "mutation", - "id": "ALPHA_COMPLEX", - "name": { "str": "Superiority Complex" }, - "points": 0, - "description": "You can't stand to prostrate yourself before your lessers and hate it when they don't immediately do what you say. Persuasion is much more difficult, but you're better at lying and intimidating.", - "purifiable": false, - "types": [ "SOCIAL" ], - "social_modifiers": { "lie": 10, "persuade": -25, "intimidate": 10 }, - "category": [ "ALPHA" ], - "prereqs": [ "INT_ALPHA", "PER_ALPHA" ], - "prereqs2": [ "STR_ALPHA", "DEX_ALPHA" ], - "threshreq": [ "THRESH_ALPHA" ] - }, +{ + "type": "mutation", + "id": "ALPHA_COMPLEX", + "name": { + "str": "Superiority Complex" + }, + "points": 0, + "description": "You can't stand to prostrate yourself before your lessers and hate it when they don't immediately do what you say. Persuasion is much more difficult, but you're better at lying and intimidating.", + "purifiable": false, + "types": [ + "HUMAN_EMPATHY" + ], + "social_modifiers": { + "lie": 10, + "persuade": -25, + "intimidate": 10 + }, + "category": [ + "ALPHA" + ], + "prereqs": [ + "INT_ALPHA", + "PER_ALPHA" + ], + "prereqs2": [ + "STR_ALPHA", + "DEX_ALPHA" + ], + "threshreq": [ + "THRESH_ALPHA" + ] +}, { "type": "mutation", "id": "NOT_GLASS", diff --git a/data/json/techniques.json b/data/json/techniques.json index 0f08e767ee39a..44930ff0a915a 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -3490,230 +3490,5 @@ } ], "attack_vectors": [ "WEAPON", "HAND" ] - }, - { - "type": "technique", - "id": "FANGS_BITE", - "name": "Fang Bite", - "melee_allowed": true, - "messages": [ "You bite %s", " bites %s!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": true, - "crit_tec_id": "FANGS_BITE_CRIT", - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "not": { "u_has_effect": "natural_stance" } }, - { "and": [ { "not": { "npc_has_flag": "GRAB_FILTER" } }, { "not": { "u_has_effect": "GRAB" } } ] } - ] - }, - "tech_effects": [ - { - "id": "anticoagulant_draculin", - "chance": 70, - "duration": 3000, - "on_damage": true, - "message": "Saliva glistens across %s's wound!", - "req_flag": "DRACULIN_VENOM" - } - ], - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 12 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.06 }, - { "stat": "movecost", "scale": 75 }, - { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, - { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } - ] - }, - { - "type": "technique", - "id": "FANGS_BITE_NATURAL", - "name": "Fang Bite", - "melee_allowed": true, - "messages": [ "You bite %s", " bites %s!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": true, - "crit_tec_id": "FANGS_BITE_CRIT", - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { - "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] - } - ] - }, - "tech_effects": [ - { - "id": "anticoagulant_draculin", - "chance": 70, - "duration": 3000, - "on_damage": true, - "message": "Saliva glistens across %s's wound!", - "req_flag": "DRACULIN_VENOM" - } - ], - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 12 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.06 }, - { "stat": "movecost", "scale": 75 }, - { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, - { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } - ] - }, - { - "type": "technique", - "id": "FANGS_BITE_CRIT", - "name": "Critical Fang Bite", - "melee_allowed": true, - "messages": [ "You deliver a wicked bite to %s", " delivers a wicked bite to %s!" ], - "unarmed_allowed": true, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "attack_vectors": [ "MOUTH" ], - "tech_effects": [ - { - "id": "anticoagulant_draculin", - "chance": 90, - "duration": 6000, - "on_damage": true, - "message": "Saliva glistens across %s's wound!", - "req_flag": "DRACULIN_VENOM" - } - ], - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 12 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 4.4 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.24 }, - { "stat": "arpen", "type": "bash", "scaling-stat": "unar,ed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "movecost", "scale": 75 }, - { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, - { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } - ] - }, - { - "type": "technique", - "id": "VAMPIRE_BITE", - "name": "Vampire Bite", - "melee_allowed": true, - "messages": [ "You bite %s", " bites %s!" ], - "unarmed_allowed": true, - "weighting": -5, - "reach_ok": false, - "attack_override": true, - "crit_ok": true, - "crit_tec_id": "VAMPIRE_BITE_CRIT", - "attack_vectors": [ "MOUTH" ], - "condition": { - "and": [ - { "not": { "u_has_effect": "natural_stance" } }, - { "and": [ { "not": { "npc_has_flag": "GRAB_FILTER" } }, { "not": { "u_has_effect": "GRAB" } } ] } - ] - }, - "tech_effects": [ - { - "id": "anticoagulant_draculin", - "chance": 90, - "duration": 3000, - "on_damage": true, - "message": "Saliva glistens across %s's wound!", - "req_flag": "DRACULIN_VENOM" - } - ], - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 22 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.06 }, - { "stat": "movecost", "scale": 75 }, - { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, - { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } - ] - }, - { - "type": "technique", - "id": "VAMPIRE_BITE_NATURAL", - "name": "Vampire Bite", - "melee_allowed": true, - "messages": [ "You bite %s", " bites %s!" ], - "unarmed_allowed": true, - "weighting": -2, - "reach_ok": false, - "attack_override": true, - "crit_ok": true, - "crit_tec_id": "VAMPIRE_BITE_CRIT", - "attack_vectors": [ "MOUTH" ], - "tech_effects": [ - { - "id": "anticoagulant_draculin", - "chance": 90, - "duration": 3000, - "on_damage": true, - "message": "Saliva glistens across %s's wound!", - "req_flag": "DRACULIN_VENOM" - } - ], - "condition": { - "and": [ - { - "or": [ { "u_has_effect": "natural_stance" }, { "and": [ { "npc_has_flag": "GRAB_FILTER" }, { "u_has_flag": "GRAB" } ] } ] - } - ] - }, - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 22 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 1.0 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.06 }, - { "stat": "movecost", "scale": 75 }, - { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, - { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } - ] - }, - { - "type": "technique", - "id": "VAMPIRE_BITE_CRIT", - "name": "Critical Vampire Bite", - "melee_allowed": true, - "messages": [ "You sink your fangs deep into %s", " sinks their fangs deep into %s!" ], - "unarmed_allowed": true, - "weighting": -4, - "reach_ok": false, - "attack_override": true, - "crit_tec": true, - "tech_effects": [ - { - "id": "anticoagulant_draculin", - "chance": 100, - "duration": 6000, - "on_damage": true, - "message": "Saliva glistens across %s's wound!", - "req_flag": "DRACULIN_VENOM" - } - ], - "attack_vectors": [ "MOUTH" ], - "flat_bonuses": [ - { "stat": "damage", "type": "stab", "scale": 22 }, - { "stat": "damage", "type": "stab", "scaling-stat": "unarmed", "scale": 4.4 }, - { "stat": "damage", "type": "bash", "scaling-stat": "str", "scale": 0.75 }, - { "stat": "damage", "type": "bash", "scaling-stat": "unarmed", "scale": 0.24 }, - { "stat": "arpen", "type": "bash", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "arpen", "type": "stab", "scaling-stat": "unarmed", "scale": 1 }, - { "stat": "movecost", "scale": 75 }, - { "stat": "movecost", "scaling-stat": "melee", "scale": -1.25 }, - { "stat": "movecost", "scaling-stat": "dex", "scale": -0.5 } - ] } ] diff --git a/src/bonuses.cpp b/src/bonuses.cpp index 6d0d7592b260f..84a599ca481a6 100644 --- a/src/bonuses.cpp +++ b/src/bonuses.cpp @@ -11,25 +11,6 @@ #include "string_formatter.h" #include "translations.h" -static const skill_id skill_archery( "archery" ); -static const skill_id skill_bashing( "bashing" ); -static const skill_id skill_cutting( "cutting" ); -static const skill_id skill_dodge( "dodge" ); -static const skill_id skill_drive( "drive" ); -static const skill_id skill_firstaid( "firstaid" ); -static const skill_id skill_gun( "gun" ); -static const skill_id skill_launcher( "launcher" ); -static const skill_id skill_melee( "melee" ); -static const skill_id skill_pistol( "pistol" ); -static const skill_id skill_rifle( "rifle" ); -static const skill_id skill_shotgun( "shotgun" ); -static const skill_id skill_smg( "smg" ); -static const skill_id skill_spellcraft( "spellcraft" ); -static const skill_id skill_stabbing( "stabbing" ); -static const skill_id skill_swimming( "swimming" ); -static const skill_id skill_throw( "throw" ); -static const skill_id skill_unarmed( "unarmed" ); - static bool needs_damage_type( affected_stat as ) { return as == affected_stat::DAMAGE || @@ -57,24 +38,6 @@ static const std::map scaling_stat_map = {{ std::make_pair( "dex", STAT_DEX ), std::make_pair( "int", STAT_INT ), std::make_pair( "per", STAT_PER ), - std::make_pair( "bash", SKILL_BASHING ), - std::make_pair( "cut", SKILL_CUTTING ), - std::make_pair( "dodge", SKILL_DODGE ), - std::make_pair( "melee", SKILL_MELEE ), - std::make_pair( "stab", SKILL_STABBING ), - std::make_pair( "athletics", SKILL_SWIMMING ), - std::make_pair( "unarmed", SKILL_UNARMED ), - std::make_pair( "marksmanship", SKILL_GUN ), - std::make_pair( "pistol", SKILL_PISTOL ), - std::make_pair( "rifle", SKILL_RIFLE ), - std::make_pair( "shotgun", SKILL_SHOTGUN ), - std::make_pair( "smg", SKILL_SMG ), - std::make_pair( "archery", SKILL_ARCHERY ), - std::make_pair( "throw", SKILL_THROW ), - std::make_pair( "launcher", SKILL_LAUNCHER ), - std::make_pair( "drive", SKILL_DRIVE ), - std::make_pair( "firstaid", SKILL_FIRSTAID ), - std::make_pair( "spellcraft", SKILL_SPELLCRAFT ) } }; @@ -124,24 +87,6 @@ static const std::map scaling_stat_map_translation = std::make_pair( STAT_DEX, translate_marker( "dexterity" ) ), std::make_pair( STAT_INT, translate_marker( "intelligence" ) ), std::make_pair( STAT_PER, translate_marker( "perception" ) ), - std::make_pair( SKILL_BASHING, translate_marker( "bashing" ) ), - std::make_pair( SKILL_CUTTING, translate_marker( "cutting" ) ), - std::make_pair( SKILL_DODGE, translate_marker( "dodging" ) ), - std::make_pair( SKILL_MELEE, translate_marker( "melee" ) ), - std::make_pair( SKILL_STABBING, translate_marker( "stabbing" ) ), - std::make_pair( SKILL_SWIMMING, translate_marker( "swimming" ) ), - std::make_pair( SKILL_UNARMED, translate_marker( "unarmed" ) ), - std::make_pair( SKILL_GUN, translate_marker( "marksmanship" ) ), - std::make_pair( SKILL_PISTOL, translate_marker( "pistols" ) ), - std::make_pair( SKILL_RIFLE, translate_marker( "rifles" ) ), - std::make_pair( SKILL_SHOTGUN, translate_marker( "shotguns" ) ), - std::make_pair( SKILL_SMG, translate_marker( "SMGs" ) ), - std::make_pair( SKILL_ARCHERY, translate_marker( "archery" ) ), - std::make_pair( SKILL_THROW, translate_marker( "throwing" ) ), - std::make_pair( SKILL_LAUNCHER, translate_marker( "launchers" ) ), - std::make_pair( SKILL_DRIVE, translate_marker( "driving" ) ), - std::make_pair( SKILL_FIRSTAID, translate_marker( "health care" ) ), - std::make_pair( SKILL_SPELLCRAFT, translate_marker( "spellcraft" ) ), } }; @@ -331,60 +276,6 @@ float effect_scaling::get( const Character &u ) const case STAT_PER: bonus = scale * u.get_per(); break; - case SKILL_BASHING: - bonus = scale * u.get_skill_level( skill_bashing ); - break; - case SKILL_CUTTING: - bonus = scale * u.get_skill_level( skill_cutting ); - break; - case SKILL_DODGE: - bonus = scale * u.get_skill_level( skill_dodge ); - break; - case SKILL_MELEE: - bonus = scale * u.get_skill_level( skill_melee ); - break; - case SKILL_STABBING: - bonus = scale * u.get_skill_level( skill_stabbing ); - break; - case SKILL_SWIMMING: - bonus = scale * u.get_skill_level( skill_swimming ); - break; - case SKILL_UNARMED: - bonus = scale * u.get_skill_level( skill_unarmed ); - break; - case SKILL_GUN: - bonus = scale * u.get_skill_level( skill_gun ); - break; - case SKILL_PISTOL: - bonus = scale * u.get_skill_level( skill_pistol ); - break; - case SKILL_RIFLE: - bonus = scale * u.get_skill_level( skill_rifle ); - break; - case SKILL_SHOTGUN: - bonus = scale * u.get_skill_level( skill_shotgun ); - break; - case SKILL_SMG: - bonus = scale * u.get_skill_level( skill_smg ); - break; - case SKILL_ARCHERY: - bonus = scale * u.get_skill_level( skill_archery ); - break; - case SKILL_THROW: - bonus = scale * u.get_skill_level( skill_throw ); - break; - case SKILL_LAUNCHER: - bonus = scale * u.get_skill_level( skill_launcher ); - break; - case SKILL_DRIVE: - bonus = scale * u.get_skill_level( skill_drive ); - break; - case SKILL_FIRSTAID: - bonus = scale * u.get_skill_level( skill_firstaid ); - break; - case SKILL_SPELLCRAFT: - bonus = scale * u.get_skill_level( skill_spellcraft ); - break; case STAT_NULL: bonus = scale; case NUM_STATS: @@ -392,4 +283,4 @@ float effect_scaling::get( const Character &u ) const } return bonus; -} +} \ No newline at end of file diff --git a/src/bonuses.h b/src/bonuses.h index 27f10f711c30f..3810f82795dcc 100644 --- a/src/bonuses.h +++ b/src/bonuses.h @@ -18,24 +18,6 @@ enum scaling_stat : int { STAT_DEX, STAT_INT, STAT_PER, - SKILL_BASHING, - SKILL_CUTTING, - SKILL_DODGE, - SKILL_MELEE, - SKILL_STABBING, - SKILL_SWIMMING, - SKILL_UNARMED, - SKILL_GUN, - SKILL_PISTOL, - SKILL_RIFLE, - SKILL_SHOTGUN, - SKILL_SMG, - SKILL_ARCHERY, - SKILL_THROW, - SKILL_LAUNCHER, - SKILL_DRIVE, - SKILL_FIRSTAID, - SKILL_SPELLCRAFT, NUM_STATS }; @@ -109,4 +91,4 @@ class bonus_container bonus_map bonuses_mult; }; -#endif // CATA_SRC_BONUSES_H +#endif // CATA_SRC_BONUSES_H \ No newline at end of file diff --git a/src/martialarts.cpp b/src/martialarts.cpp index 307cb497ddc06..ef97c53127c74 100644 --- a/src/martialarts.cpp +++ b/src/martialarts.cpp @@ -45,6 +45,8 @@ static const json_character_flag json_flag_NONSTANDARD_BLOCK( "NONSTANDARD_BLOCK static const limb_score_id limb_score_block( "block" ); +static const matec_id tec_none( "tec_none" ); + static const skill_id skill_unarmed( "unarmed" ); static const weapon_category_id weapon_category_OTHER_INVALID_WEAP_CAT( "OTHER_INVALID_WEAP_CAT" ); @@ -217,7 +219,6 @@ void ma_technique::load( const JsonObject &jo, const std::string &src ) optional( jo, was_loaded, "crit_tec", crit_tec, false ); optional( jo, was_loaded, "crit_ok", crit_ok, false ); - optional( jo, was_loaded, "crit_tec_id", crit_tec_id, tec_none ); optional( jo, was_loaded, "attack_override", attack_override, false ); optional( jo, was_loaded, "wall_adjacent", wall_adjacent, false ); optional( jo, was_loaded, "reach_tec", reach_tec, false ); @@ -820,7 +821,6 @@ ma_technique::ma_technique() { crit_tec = false; crit_ok = false; - crit_tec_id = tec_none; // if not tec_none, use this tech instead when a crit procs defensive = false; side_switch = false; // moves the target behind user dummy = false; @@ -1389,7 +1389,6 @@ bool character_martial_arts::can_use_attack_vector( const Character &user, bool healthy_arm = arm_r_hp > 0 || arm_l_hp > 0; bool healthy_arms = arm_r_hp > 0 && arm_l_hp > 0; bool healthy_legs = leg_r_hp > 0 && leg_l_hp > 0; - bool mouth_ok = ( av == "MOUTH" ) && !user.natural_attack_restricted_on( bodypart_id( "mouth" ) ); bool always_ok = av == "HEAD" || av == "TORSO"; bool weapon_ok = av == "WEAPON" && valid_weapon && healthy_arm; bool arm_ok = ( av == "HAND" || av == "FINGER" || av == "WRIST" || av == "ARM" || av == "ELBOW" || @@ -1397,7 +1396,7 @@ bool character_martial_arts::can_use_attack_vector( const Character &user, bool arms_ok = ( av == "GRAPPLE" || av == "THROW" ) && healthy_arms; bool legs_ok = ( av == "FOOT" || av == "LOWER_LEG" || av == "KNEE" || av == "HIP" ) && healthy_legs; - return always_ok || weapon_ok || mouth_ok || arm_ok || arms_ok || legs_ok; + return always_ok || weapon_ok || arm_ok || arms_ok || legs_ok; } bool character_martial_arts::can_leg_block( const Character &owner ) const @@ -2187,4 +2186,4 @@ bool ma_style_callback::key( const input_context &ctxt, const input_event &event } while( true ); } return true; -} +} \ No newline at end of file diff --git a/src/martialarts.h b/src/martialarts.h index 34c659aaf0c24..a8000f7d001a8 100644 --- a/src/martialarts.h +++ b/src/martialarts.h @@ -152,9 +152,6 @@ class ma_technique bool dummy = false; bool crit_tec = false; bool crit_ok = false; - // performs the listed technique if this attack procs a crit. tec_none skips this behavior. - // requires crit_ok to be true - matec_id crit_tec_id = tec_none; bool reach_tec = false; // only possible to use during a reach attack bool reach_ok = false; // possible to use during a reach attack bool attack_override = false; // The attack replaces the one it triggered off of @@ -430,4 +427,4 @@ std::string martialart_difficulty( const matype_id &mstyle ); std::vector all_martialart_types(); std::vector autolearn_martialart_types(); -#endif // CATA_SRC_MARTIALARTS_H +#endif // CATA_SRC_MARTIALARTS_H \ No newline at end of file From 6373c0db8614fbbb0774dbdf214ca010dd267d0f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:04:38 -0800 Subject: [PATCH 162/202] cleanup --- data/json/mutations/mutations.json | 48 ++++++++++-------------------- src/martialarts.cpp | 2 -- src/melee.cpp | 3 -- 3 files changed, 15 insertions(+), 38 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 75faee59d2229..05acf975ed50d 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -2443,7 +2443,7 @@ "ugliness": 2, "description": "Your teeth have grown into two-inch-long fangs, allowing you to make an extra attack when conditions favor it.", "types": [ "TEETH" ], - "changes_to": [ "SABER_TEETH", "SHARKTEETH", "VAMPIRE_FANGS" ], + "changes_to": [ "SABER_TEETH", "SHARKTEETH", "FANGS_VAMPIRE" ], "category": [ "LIZARD", "FISH", "LUPINE", "FELINE", "CHIMERA", "CHIROPTERAN" ], "attacks": [ { @@ -9655,38 +9655,20 @@ "category": [ "ALPHA" ], "starting_trait": true }, -{ - "type": "mutation", - "id": "ALPHA_COMPLEX", - "name": { - "str": "Superiority Complex" - }, - "points": 0, - "description": "You can't stand to prostrate yourself before your lessers and hate it when they don't immediately do what you say. Persuasion is much more difficult, but you're better at lying and intimidating.", - "purifiable": false, - "types": [ - "HUMAN_EMPATHY" - ], - "social_modifiers": { - "lie": 10, - "persuade": -25, - "intimidate": 10 - }, - "category": [ - "ALPHA" - ], - "prereqs": [ - "INT_ALPHA", - "PER_ALPHA" - ], - "prereqs2": [ - "STR_ALPHA", - "DEX_ALPHA" - ], - "threshreq": [ - "THRESH_ALPHA" - ] -}, + { + "type": "mutation", + "id": "ALPHA_COMPLEX", + "name": { "str": "Superiority Complex" }, + "points": 0, + "description": "You can't stand to prostrate yourself before your lessers and hate it when they don't immediately do what you say. Persuasion is much more difficult, but you're better at lying and intimidating.", + "purifiable": false, + "types": [ "HUMAN_EMPATHY" ], + "social_modifiers": { "lie": 10, "persuade": -25, "intimidate": 10 }, + "category": [ "ALPHA" ], + "prereqs": [ "INT_ALPHA", "PER_ALPHA" ], + "prereqs2": [ "STR_ALPHA", "DEX_ALPHA" ], + "threshreq": [ "THRESH_ALPHA" ] + }, { "type": "mutation", "id": "NOT_GLASS", diff --git a/src/martialarts.cpp b/src/martialarts.cpp index ef97c53127c74..66ca665236692 100644 --- a/src/martialarts.cpp +++ b/src/martialarts.cpp @@ -45,8 +45,6 @@ static const json_character_flag json_flag_NONSTANDARD_BLOCK( "NONSTANDARD_BLOCK static const limb_score_id limb_score_block( "block" ); -static const matec_id tec_none( "tec_none" ); - static const skill_id skill_unarmed( "unarmed" ); static const weapon_category_id weapon_category_OTHER_INVALID_WEAP_CAT( "OTHER_INVALID_WEAP_CAT" ); diff --git a/src/melee.cpp b/src/melee.cpp index 04c0047013861..e5dff5eb5da95 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -740,9 +740,6 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, technique_id = force_technique; } else if( allow_special ) { technique_id = pick_technique( t, cur_weapon, critical_hit, false, false ); - if( critical_hit && technique_id.obj().crit_tec_id != tec_none ) { - technique_id = technique_id.obj().crit_tec_id; - } } else { technique_id = tec_none; } From b2f20191e75647207c0cb67bfe9720492eba4996 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:18:36 -0800 Subject: [PATCH 163/202] Update effects.json --- data/json/effects.json | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/data/json/effects.json b/data/json/effects.json index ad9491eec3af0..693fd328777ba 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -3938,48 +3938,6 @@ "//": "Mood debuff is handled separately by bad_food_mood_debuff EoC.", "rating": "bad" }, - { - "type": "effect_type", - "id": "social_satisfied", - "name": [ "Good Company" ], - "desc": [ "It feels good to spend time around others." ], - "max_duration": "6 h", - "max_intensity": 1, - "rating": "good" - }, - { - "type": "effect_type", - "id": "social_dissatisfied", - "name": [ "Lonely", "Very Lonely", "All Alone" ], - "desc": [ - "You feel a little lonely.", - "You could really use a friend right now.", - "What's the point of surviving if you're all alone?" - ], - "max_intensity": 3, - "rating": "bad" - }, - { - "type": "effect_type", - "id": "asocial_satisfied", - "name": [ "Alone" ], - "desc": [ "Being on your own makes you feel good." ], - "max_intensity": 1, - "max_duration": "6 h", - "rating": "good" - }, - { - "type": "effect_type", - "id": "asocial_dissatisfied", - "name": [ "Irritable", "Annoyed", "Crowded" ], - "desc": [ - "Being around others has left you feeling drained.", - "OK, that's enough social time. You'd really rather be alone now.", - "Your nerves are frayed from all this socializing. You need some alone time to recharge." - ], - "max_intensity": 3, - "rating": "bad" - }, { "type": "effect_type", "id": "genetics_damaged", From 7d2ccda39c362334e6dce30dfc0963f8446cc13a Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:19:50 -0800 Subject: [PATCH 164/202] Update morale_types.json --- data/json/morale_types.json | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/data/json/morale_types.json b/data/json/morale_types.json index 008fc69d92e9b..2f5392ee282fd 100644 --- a/data/json/morale_types.json +++ b/data/json/morale_types.json @@ -428,15 +428,5 @@ "id": "morale_afs_drugs", "type": "morale_type", "text": "Chemical enhancement moodswing" - }, - { - "id": "morale_social", - "type": "morale_type", - "text": "Spent time around allies" - }, - { - "id": "morale_asocial", - "type": "morale_type", - "text": "Spent time alone" } ] From 1a6b02c8a54e7fcdc3b17aa9c18a92a6d5f26b07 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:21:18 -0800 Subject: [PATCH 165/202] Update activity_handlers.cpp --- src/activity_handlers.cpp | 39 ++++----------------------------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index e46b8d19e6206..d25ee3f612bd4 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -180,15 +180,12 @@ static const damage_type_id damage_bash( "bash" ); static const damage_type_id damage_cut( "cut" ); static const damage_type_id damage_stab( "stab" ); -static const efftype_id effect_asocial_dissatisfied( "asocial_dissatisfied" ); static const efftype_id effect_bleed( "bleed" ); static const efftype_id effect_blind( "blind" ); static const efftype_id effect_controlled( "controlled" ); static const efftype_id effect_narcosis( "narcosis" ); static const efftype_id effect_pet( "pet" ); static const efftype_id effect_sleep( "sleep" ); -static const efftype_id effect_social_dissatisfied( "social_dissatisfied" ); -static const efftype_id effect_social_satisfied( "social_satisfied" ); static const efftype_id effect_under_operation( "under_operation" ); static const harvest_drop_type_id harvest_drop_blood( "blood" ); @@ -203,15 +200,11 @@ static const itype_id itype_burnt_out_bionic( "burnt_out_bionic" ); static const itype_id itype_muscle( "muscle" ); static const itype_id itype_pseudo_magazine( "pseudo_magazine" ); -static const json_character_flag json_flag_ASOCIAL1( "ASOCIAL1" ); -static const json_character_flag json_flag_ASOCIAL2( "ASOCIAL2" ); static const json_character_flag json_flag_CANNIBAL( "CANNIBAL" ); static const json_character_flag json_flag_PAIN_IMMUNE( "PAIN_IMMUNE" ); static const json_character_flag json_flag_PSYCHOPATH( "PSYCHOPATH" ); static const json_character_flag json_flag_SAPIOVORE( "SAPIOVORE" ); static const json_character_flag json_flag_SILENT_SPELL( "SILENT_SPELL" ); -static const json_character_flag json_flag_SOCIAL1( "SOCIAL1" ); -static const json_character_flag json_flag_SOCIAL2( "SOCIAL2" ); static const mongroup_id GROUP_FISH( "GROUP_FISH" ); @@ -225,6 +218,7 @@ static const skill_id skill_computer( "computer" ); static const skill_id skill_firstaid( "firstaid" ); static const skill_id skill_survival( "survival" ); +static const species_id species_FERAL( "FERAL" ); static const species_id species_HUMAN( "HUMAN" ); static const species_id species_ZOMBIE( "ZOMBIE" ); @@ -539,7 +533,8 @@ static void set_up_butchery( player_activity &act, Character &you, butcher_type } } - const bool is_human = corpse.id == mtype_id::NULL_ID() || ( corpse.in_species( species_HUMAN ) && + const bool is_human = corpse.id == mtype_id::NULL_ID() || ( ( corpse.in_species( species_HUMAN ) || + corpse.in_species( species_FERAL ) ) && !corpse.in_species( species_ZOMBIE ) ); // applies to all butchery actions except for dissections @@ -1617,19 +1612,6 @@ void activity_handlers::mutant_tree_communion_do_turn( player_activity *act, Cha } if( one_in( 128 ) ) { communioncycles += 1; - if( one_in( 256 ) ) { - if( you->has_effect( effect_social_dissatisfied ) ) { - you->remove_effect( effect_social_dissatisfied ); - } - if( ( you->has_flag( json_flag_SOCIAL1 ) || you->has_flag( json_flag_SOCIAL2 ) ) && - !you->has_effect( effect_social_satisfied ) ) { - you->add_effect( effect_social_satisfied, 3_hours, false, 1 ); - } - if( ( you->has_flag( json_flag_ASOCIAL1 ) || you->has_flag( json_flag_ASOCIAL2 ) ) && - !you->has_effect( effect_asocial_dissatisfied ) ) { - you->add_effect( effect_asocial_dissatisfied, 3_hours, false, 1 ); - } - } you->add_msg_if_player( "%s", SNIPPET.random_from_category( "mutant_tree_communion" ).value_or( translation() ) ); you->add_morale( MORALE_TREE_COMMUNION, 4, 30, 18_hours, 8_hours ); @@ -3799,19 +3781,6 @@ void activity_handlers::tree_communion_do_turn( player_activity *act, Character you->add_morale( MORALE_TREE_COMMUNION, 1, 15, 2_hours, 1_hours ); } if( one_in( 128 ) ) { - if( one_in( 256 ) ) { - if( you->has_effect( effect_social_dissatisfied ) ) { - you->remove_effect( effect_social_dissatisfied ); - } - if( ( you->has_flag( json_flag_SOCIAL1 ) || you->has_flag( json_flag_SOCIAL2 ) ) && - !you->has_effect( effect_social_satisfied ) ) { - you->add_effect( effect_social_satisfied, 3_hours, false, 1 ); - } - if( ( you->has_flag( json_flag_ASOCIAL1 ) || you->has_flag( json_flag_ASOCIAL2 ) ) && - !you->has_effect( effect_asocial_dissatisfied ) ) { - you->add_effect( effect_asocial_dissatisfied, 3_hours, false, 1 ); - } - } you->add_msg_if_player( "%s", SNIPPET.random_from_category( "tree_communion" ).value_or( translation() ) ); } @@ -3923,7 +3892,7 @@ void activity_handlers::spellcasting_finish( player_activity *act, Character *yo you->mod_stamina( -cost ); break; case magic_energy_type::bionic: - you->mod_power_level( -units::from_kilojoule( cost ) ); + you->mod_power_level( -units::from_kilojoule( static_cast( cost ) ) ); break; case magic_energy_type::hp: blood_magic( you, cost ); From 92aa78116ffcaa53d04ce5ec6daa07488720f4fb Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:22:44 -0800 Subject: [PATCH 166/202] Update mtype.h --- src/mtype.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mtype.h b/src/mtype.h index fe67a3d7e037d..c97bf3cc55ed3 100644 --- a/src/mtype.h +++ b/src/mtype.h @@ -110,7 +110,6 @@ extern mon_flag_id mon_flag_ACIDPROOF, mon_flag_DOGFOOD, mon_flag_DORMANT, mon_flag_GEN_DORMANT, - mon_flag_DRACULIN_IMMUNE, mon_flag_DRIPS_GASOLINE, mon_flag_DRIPS_NAPALM, mon_flag_DROPS_AMMO, From 35403755cc1ce89359a8fbd3fb04b2d33bba00c4 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:24:08 -0800 Subject: [PATCH 167/202] Update species.json --- data/json/species.json | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/data/json/species.json b/data/json/species.json index 5b956a50d2037..1c7b2600f8328 100644 --- a/data/json/species.json +++ b/data/json/species.json @@ -24,7 +24,6 @@ "type": "SPECIES", "id": "CYBORG", "description": "an alien cyborg", - "flags": [ "DRACULIN_IMMUNE" ], "footsteps": "heavy thuds." }, { @@ -57,44 +56,38 @@ { "type": "SPECIES", "id": "NETHER", - "description": "a nether creature", - "flags": [ "DRACULIN_IMMUNE" ] + "description": "a nether creature" }, { "type": "SPECIES", "id": "NETHER_EMANATION", "description": "a reflection of another dimension", - "fear_triggers": [ "HURT" ], - "flags": [ "DRACULIN_IMMUNE" ] + "fear_triggers": [ "HURT" ] }, { "type": "SPECIES", "id": "MIGO", "description": "a creature from the mi-go homeworld", - "fear_triggers": [ "HURT" ], - "flags": [ "DRACULIN_IMMUNE" ] + "fear_triggers": [ "HURT" ] }, { "type": "SPECIES", "id": "SLIME", "description": "a slime", "footsteps": "plop.", - "bleeds": "fd_slime", - "flags": [ "DRACULIN_IMMUNE" ] + "bleeds": "fd_slime" }, { "type": "SPECIES", "id": "FUNGUS", "description": "a fungus", - "fear_triggers": [ "HURT", "FIRE" ], - "flags": [ "DRACULIN_IMMUNE" ] + "fear_triggers": [ "HURT", "FIRE" ] }, { "type": "SPECIES", "id": "LEECH_PLANT", "description": "a leech plant", - "fear_triggers": [ "HURT", "FIRE" ], - "flags": [ "DRACULIN_IMMUNE" ] + "fear_triggers": [ "HURT", "FIRE" ] }, { "type": "SPECIES", @@ -102,8 +95,7 @@ "description": "an insect", "anger_triggers": [ "FRIEND_DIED" ], "fear_triggers": [ "HURT", "FIRE" ], - "bleeds": "fd_blood_insect", - "flags": [ "DRACULIN_IMMUNE" ] + "bleeds": "fd_blood_insect" }, { "type": "SPECIES", @@ -111,8 +103,7 @@ "description": "a centipede", "footsteps": "skittering.", "fear_triggers": [ "FIRE" ], - "bleeds": "fd_blood_insect", - "flags": [ "DRACULIN_IMMUNE" ] + "bleeds": "fd_blood_insect" }, { "type": "SPECIES", @@ -120,7 +111,7 @@ "description": "a flying insect", "footsteps": "BZZZZZZZZZZZZZZZZZ", "anger_triggers": [ "FRIEND_DIED" ], - "flags": [ "LOUDMOVES", "DRACULIN_IMMUNE" ], + "flags": [ "LOUDMOVES" ], "bleeds": "fd_blood_insect" }, { @@ -129,23 +120,20 @@ "description": "a spider", "anger_triggers": [ "FRIEND_DIED" ], "fear_triggers": [ "HURT", "FIRE" ], - "bleeds": "fd_blood_insect", - "flags": [ "DRACULIN_IMMUNE" ] + "bleeds": "fd_blood_insect" }, { "type": "SPECIES", "id": "PLANT", "description": "a plant", "fear_triggers": [ "HURT", "FIRE" ], - "bleeds": "fd_blood_veggy", - "flags": [ "DRACULIN_IMMUNE" ] + "bleeds": "fd_blood_veggy" }, { "type": "SPECIES", "id": "MOLLUSK", "description": "a mollusk", "fear_triggers": [ "HURT", "FIRE" ], - "//": "Despite having nonstandard blood, mollusks have circulatory systems similar to fish and reptiles, so no DRACULIN_IMMUNE", "bleeds": "fd_blood_invertebrate" }, { @@ -154,8 +142,7 @@ "description": "a worm", "footsteps": "rustle.", "fear_triggers": [ "HURT" ], - "bleeds": "fd_blood_invertebrate", - "flags": [ "DRACULIN_IMMUNE" ] + "bleeds": "fd_blood_invertebrate" }, { "type": "SPECIES", From 29f2ebe6631a371d0f7b56e879c3d0dd0b4ae9c6 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:27:24 -0800 Subject: [PATCH 168/202] Update mutation_effect_eocs.json --- .../mutation_eocs/mutation_effect_eocs.json | 144 ------------------ 1 file changed, 144 deletions(-) diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index 66c22f205bdb2..bfebd92b8b2ca 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -695,149 +695,5 @@ { "u_activate_trait": "WINGS_INSECT_active" }, { "u_message": "You don't have the stamina to keep buzzing.", "type": "bad" } ] - }, - { - "type": "effect_on_condition", - "id": "EOC_social1_bonus", - "recurrence": [ "10 minutes", "30 minutes" ], - "//": "Alcohol and sedatives prevent the bad effects of these EOCs. Antidepressants prevent (but don't remove) both the good and bad effects, keeping you at a baseline state.", - "condition": { - "and": [ - { "u_has_flag": "SOCIAL1" }, - { "not": { "u_has_effect": "took_prozac" } }, - { "math": [ "u_characters_nearby('radius': 30)", ">", "0" ] }, - { "not": { "u_has_effect": "narcosis" } } - ] - }, - "effect": [ - { "u_lose_effect": "social_dissatisfied" }, - { "u_lose_effect": "social_satisfied" }, - { "u_add_effect": "social_satisfied", "duration": "2 hours" } - ] - }, - { - "type": "effect_on_condition", - "id": "EOC_social2_bonus", - "recurrence": [ "10 minutes", "30 minutes" ], - "condition": { - "and": [ - { "u_has_flag": "SOCIAL2" }, - { "not": { "u_has_effect": "took_prozac" } }, - { "math": [ "u_characters_nearby('radius': 30, 'attitude': 'allies')", ">", "0" ] }, - { "not": { "u_has_effect": "narcosis" } } - ] - }, - "effect": [ - { "u_lose_effect": "social_dissatisfied" }, - { "u_lose_effect": "social_satisfied" }, - { "u_add_effect": "social_satisfied", "duration": "6 hours" } - ] - }, - { - "type": "effect_on_condition", - "id": "EOC_social2_penalty", - "recurrence": [ "1 hours", "6 hours" ], - "condition": { - "and": [ - { "u_has_flag": "SOCIAL2" }, - { "math": [ "u_characters_nearby('radius': 30, 'attitude': 'allies')", "==", "0" ] }, - { "not": { "u_has_effect": "took_prozac" } }, - { "not": { "u_has_effect": "valium" } }, - { "not": { "u_has_effect": "took_xanax" } }, - { "not": { "u_has_effect": "drunk" } }, - { "not": { "u_has_effect": "narcosis" } } - ] - }, - "effect": [ - { "u_lose_effect": "social_satisfied" }, - { - "u_add_effect": "social_dissatisfied", - "duration": "PERMANENT", - "intensity": { "math": [ "u_effect_intensity('social_dissatisfied') + 1" ] } - }, - { "u_message": "You could use some friendly company.", "type": "bad" } - ] - }, - { - "type": "effect_on_condition", - "id": "EOC_asocial1_bonus", - "recurrence": [ "10 minutes", "30 minutes" ], - "condition": { - "and": [ - { "u_has_flag": "ASOCIAL1" }, - { "not": { "u_has_effect": "took_prozac" } }, - { "math": [ "u_characters_nearby('radius': 30)", "==", "0" ] }, - { "not": { "u_has_effect": "narcosis" } } - ] - }, - "effect": [ - { "u_lose_effect": "asocial_dissatisfied" }, - { "u_lose_effect": "asocial_satisfied" }, - { "u_add_effect": "asocial_satisfied", "duration": "2 hours" } - ] - }, - { - "type": "effect_on_condition", - "id": "EOC_asocial2_bonus", - "recurrence": [ "10 minutes", "30 minutes" ], - "condition": { - "and": [ - { "u_has_flag": "ASOCIAL2" }, - { "math": [ "u_characters_nearby('radius': 30)", "==", "0" ] }, - { "not": { "u_has_effect": "took_prozac" } }, - { "not": { "u_has_effect": "narcosis" } } - ] - }, - "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_add_effect": "asocial_satisfied", "duration": "6 hours" } ] - }, - { - "type": "effect_on_condition", - "id": "EOC_asocial2_penalty", - "recurrence": [ "1 hours", "6 hours" ], - "condition": { - "and": [ - { "u_has_flag": "ASOCIAL2" }, - { "not": { "u_has_effect": "took_prozac" } }, - { "not": { "u_has_effect": "valium" } }, - { "not": { "u_has_effect": "took_xanax" } }, - { "not": { "u_has_effect": "drunk" } }, - { "math": [ "u_characters_nearby('radius': 30)", ">", "0" ] }, - { "not": { "u_has_effect": "narcosis" } } - ] - }, - "effect": [ - { "u_lose_effect": "asocial_satisfied" }, - { - "u_add_effect": "asocial_dissatisfied", - "duration": "PERMANENT", - "intensity": { "math": [ "u_effect_intensity('asocial_dissatisfied') + 1" ] } - }, - { "u_message": "You'd really rather be by yourself.", "type": "bad" } - ] - }, - { - "type": "effect_on_condition", - "id": "EOC_social_reset_sleep", - "recurrence": [ "30 minutes", "45 minutes" ], - "condition": { - "and": [ - { "or": [ { "u_has_effect": "narcosis" }, { "u_has_effect": "sleep" } ] }, - { "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] } - ] - }, - "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_lose_effect": "social_dissatisfied" } ] - }, - { - "type": "effect_on_condition", - "id": "EOC_social_reset_drugs", - "recurrence": [ "1 seconds", "1 seconds" ], - "//": "Sedatives are effective at acutely treating social anxiety/autophobia. Enjoy your benzo addiction, survivors.", - "condition": { - "and": [ - { "or": [ { "u_has_effect": "valium" }, { "u_has_effect": "took_xanax" } ] }, - { "or": [ { "u_has_effect": "social_dissatisfied" }, { "u_has_effect": "social_dissatisfied" } ] } - ] - }, - "effect": [ { "u_lose_effect": "asocial_dissatisfied" }, { "u_lose_effect": "social_dissatisfied" } ] } ] From e2839ed4e3ba9e184e77f259ac707dfaed9078f6 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:30:33 -0800 Subject: [PATCH 169/202] Update activity_handlers.cpp --- src/activity_handlers.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index d25ee3f612bd4..6c1422205b9f8 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -218,7 +218,6 @@ static const skill_id skill_computer( "computer" ); static const skill_id skill_firstaid( "firstaid" ); static const skill_id skill_survival( "survival" ); -static const species_id species_FERAL( "FERAL" ); static const species_id species_HUMAN( "HUMAN" ); static const species_id species_ZOMBIE( "ZOMBIE" ); @@ -533,8 +532,7 @@ static void set_up_butchery( player_activity &act, Character &you, butcher_type } } - const bool is_human = corpse.id == mtype_id::NULL_ID() || ( ( corpse.in_species( species_HUMAN ) || - corpse.in_species( species_FERAL ) ) && + const bool is_human = corpse.id == mtype_id::NULL_ID() || ( ( corpse.in_species( species_HUMAN ) ) && !corpse.in_species( species_ZOMBIE ) ); // applies to all butchery actions except for dissections @@ -3892,7 +3890,7 @@ void activity_handlers::spellcasting_finish( player_activity *act, Character *yo you->mod_stamina( -cost ); break; case magic_energy_type::bionic: - you->mod_power_level( -units::from_kilojoule( static_cast( cost ) ) ); + you->mod_power_level( -units::from_kilojoule( cost ) ); break; case magic_energy_type::hp: blood_magic( you, cost ); From 56fb0733b370b662e5fa42e93bd5c32ecf35b644 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:31:25 -0800 Subject: [PATCH 170/202] Update activity_handlers.cpp --- src/activity_handlers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 6c1422205b9f8..e8a8ace636003 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -532,7 +532,7 @@ static void set_up_butchery( player_activity &act, Character &you, butcher_type } } - const bool is_human = corpse.id == mtype_id::NULL_ID() || ( ( corpse.in_species( species_HUMAN ) ) && + const bool is_human = corpse.id == mtype_id::NULL_ID() || ( corpse.in_species( species_HUMAN ) && !corpse.in_species( species_ZOMBIE ) ); // applies to all butchery actions except for dissections From 987cc2d0868fa4c32e90fc0a56a6dd670f67fcf7 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:32:51 -0800 Subject: [PATCH 171/202] Update NPCs.md --- doc/NPCs.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/NPCs.md b/doc/NPCs.md index 5493b989be66a..b3e23965ca276 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -1315,7 +1315,6 @@ _some functions support array arguments or kwargs, denoted with square brackets | attack_speed() | ✅ | ❌ | u, n | Return the characters current adjusted attack speed with their current weapon.

Example:
`"condition": { "math": [ "u_attack_speed()", ">=", "10"] }`| | addiction_intensity(`s`/`v`) | ✅ | ❌ | u, n | Return the characters current intensity of the given addiction.
Argument is addiction type ID.

Example:
`"condition": { "math": [ "u_addiction_intensity('caffeine')", ">=", "1"] }`| | addiction_turns(`s`/`v`) | ✅ | ✅ | u, n | Return the characters current duration left (in turns) for the given addiction.
Argument is addiction type ID.

Example:
`"condition": { "math": [ "u_addiction_turns('caffeine')", ">=", "3600"] }`| -| characters_nearby() | ✅ | ❌ | u, n, global | Return the number of nearby characters (that is, the avatar and/or NPCs who are not hallucinations).

Optional kwargs:
`radius`: `d`/`v` - limit to radius (rl_dist)
`location`: `v` - center search on this location
`attitude`: `s`/`v` - attitude filter. Must be one of `hostile`, `allies`, `not_allies`, `any`. Assumes `any` if not specified

The `location` kwarg is mandatory in the global scope.
`allow_hallucinations`: `d`/`v` - False by default. If set to any non-zero number, all hallucinated NPCs in range will be counted as well.

Examples:
`"condition": { "math": [ "u_characters_nearby('radius': u_search_radius * 3, 'attitude': 'not_allies' )", ">", "0" ] }`

`"condition": { "math": [ "characters_nearby( 'radius': u_search_radius * 3, 'location': u_search_loc)", ">", "5" ] }`| | charge_count(`s`/`v`) | ✅ | ❌ | u, n | Return the charges of a given item in the character's inventory.
Argument is item ID.

Example:
`"condition": { "math": [ "u_charge_count('light_plus_battery_cell')", ">=", "100"] }`| | coverage(`s`/`v`) | ✅ | ❌ | u, n | Return the characters total coverage of a body part.
Argument is bodypart ID.
For items, returns typical coverage of the item.

Example:
`"condition": { "math": [ "u_coverage('torso')", ">", "0"] }`| | distance(`s`/`v`,`s`/`v`) | ✅ | ❌ | g | Return distance between two targets.
Arguments are location variables or special strings (`u`, `npc`). `u` means your location. `npc` means NPC's location.

Example:
`"condition": { "math": [ "distance('u', loc)", "<=", "50"] }`| @@ -1357,7 +1356,6 @@ _some functions support array arguments or kwargs, denoted with square brackets | time_since(`v`)
time_since('cataclysm')
time_since('midnight') | ✅ | ❌ | N/A
(global) | Convenience function that returns a numeric value (in turns) for the time period since time point stored in variable.

Special values:
`cataclysm` - return time since start of cataclysm
`midnight` - return time since midnight

Optional kwargs:
`unit`: specify return unit. Assumes `turns` if unspecified or empty.

Returns -1 if the argument is an undefined variable

Example:
`{ "math": [ "time_since(u_timer_caravan_RandEnc)", ">", "time('1 h')" ] }`
`{ "math": [ "time_since('cataclysm', 'unit':'years') > 1" ] }`| | time_until_eoc(`s`/`v`) | ✅ | ❌ | N/A
(global) | Returns time until next scheduled run of an EOC. Argument is EOC id.

Optional kwargs:
`unit`: specify return unit

Returns -1 is the EOC is not scheduled. | | val(`s`) | ✅ | varies | u, n | Return or set a Character or item value. Argument is a [Character/item aspect](#list-of-character-and-item-aspects).

These are all in one function for legacy reasons and are generally poorly tested. If you need one of them, consider porting it to a native math function.

Example:
`{ "math": [ "u_val('strength')", "=", "2" ] }`| -| vision_range() | ✅ | ❌ | u, n | Return the character's visual range, adjusted by their mutations, effects, and other issues.

Example:
`"{ "math": [ "u_vision_range", "<", "30"] }`| | vitamin(`s`/`v`) | ✅ | ✅ | u, n | Return or set the characters vitamin level.
Argument is vitamin ID.

Example:
`{ "math": [ "u_vitamin('mutagen')", "=", "0" ] }`| | warmth(`s`/`v`) | ✅ | ❌ | u, n | Return the characters warmth on a body part.
Argument is bodypart ID.

Example:
The value displayed in-game is calculated as follows.
`"{ "math": [ "u_warmth_in_game", "=", "(u_warmth('torso') / 100) * 2 - 100"] }`| | weather(`s`) | ✅ | ✅ | N/A
(global) | Return or set a weather aspect

Aspect must be one of:
`temperature` (in Kelvin),
`humidity` (as percentage),
`pressure` (in millibar),
`windpower` (in mph).
`precipitation` (in mm / h) either 0.5 (very_light ), 1.5 (light), or 3 (heavy). Read only.

Temperature conversion functions are available: `celsius()`, `fahrenheit()`, `from_celsius()`, and `from_fahrenheit()`.

Examples:
`{ "math": [ "weather('temperature')", "<", "from_fahrenheit( 33 )" ] }`
`{ "math": [ "fahrenheit( weather('temperature') )", "==", "21" ] }`| From 852295d0ba71d2da9e2a1c93b4c899678f680862 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:34:56 -0800 Subject: [PATCH 172/202] Update MARTIALART_JSON.md --- doc/MARTIALART_JSON.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/doc/MARTIALART_JSON.md b/doc/MARTIALART_JSON.md index 8c21c1c5e1127..a18abcc10b3d7 100644 --- a/doc/MARTIALART_JSON.md +++ b/doc/MARTIALART_JSON.md @@ -70,7 +70,7 @@ "id": "tec_debug_arpen", // Unique ID. Must be one continuous word "name": "phasing strike", // In-game name displayed "attack_vectors": [ "WEAPON", "HAND" ] // What attack vector would be used for this technique; this field is order dependend, meaning in this example the game will try to use WEAPON first, and, if not possible, reject it and will use HAND instead; For more info see Attack vectors below - "attack_vectors_random": [ "FOOT", "HEAD", "TORSO", "HEAD", "HEAD", "MOUTH" ] // same as attack_vectors, but has no priority, and pick random vector from the list; it is used only if all choises from attack_vectors are rejected + "attack_vectors_random": [ "FOOT", "HEAD", "TORSO", "HEAD", "HEAD" ] // same as attack_vectors, but has no priority, and pick random vector from the list; it is used only if all choises from attack_vectors are rejected "unarmed_allowed": true, // Can an unarmed character use this technique "unarmed_weapons_allowed": true, // Does this technique require the character to be actually unarmed or does it allow unarmed weapons "weapon_categories_allowed": [ "BLADES", "KNIVES" ], // Restrict technique to only these categories of weapons. If omitted, all weapon categories are allowed. @@ -201,7 +201,7 @@ The bonuses arrays contain any number of bonus entries like this: "stat": affected statistic, any of: "hit", "dodge", "block", "speed", "movecost", "damage", "armor", "arpen", "type": damage type for the affected statistic ("bash", "cut", "heat", etc.), only needed if the affected statistic is "damage", "armor", or "arpen". "scale": the value of the bonus itself. -"scaling-stat": scaling stat, any of: "str", "dex", "int", "per", "bashing", "cutting", "dodge", "melee", "stabbing", "athletics", "unarmed", "gun", "pistol", "rifle", "shotgun", "smg", "archery", "throw", "launcher", "drive". Optional. If the scaling stat is specified, the value of the bonus is multiplied by the corresponding user stat/skill. +"scaling-stat": scaling stat, any of: "str", "dex", "int", "per", "bashing", "cutting", "dodge", "melee", "stabbing". Optional. If the scaling stat is specified, the value of the bonus is multiplied by the corresponding user stat/skill. Bonuses must be written in the correct order. @@ -220,9 +220,6 @@ All cutting damage dealt is multiplied by `(10% of dexterity)*(damage)`: Move cost is decreased by 100% of strength value * `flat_bonuses: [ { "stat": "movecost", "scaling-stat": "str", "scale": -1.0 } ]` -Gain a bonus to accuracy based on your driving skill. Should probably only be used while in control of a vehicle: -* `flat_bonuses: [ { "stat": "hit", "scaling-stat": "drive", "scale": 0.3 } ]` - ### Place relevant items in the world and chargen Starting trait selection of your martial art goes in mutations.json. Place your art in the right category (self-defense, Shaolin animal form, melee style, etc) From 52e2fa1f21a4a1282399582b5868fe48b65dd27f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:35:46 -0800 Subject: [PATCH 173/202] Update MARTIALART_JSON.md --- doc/MARTIALART_JSON.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/MARTIALART_JSON.md b/doc/MARTIALART_JSON.md index a18abcc10b3d7..08fc81461f54e 100644 --- a/doc/MARTIALART_JSON.md +++ b/doc/MARTIALART_JSON.md @@ -201,7 +201,7 @@ The bonuses arrays contain any number of bonus entries like this: "stat": affected statistic, any of: "hit", "dodge", "block", "speed", "movecost", "damage", "armor", "arpen", "type": damage type for the affected statistic ("bash", "cut", "heat", etc.), only needed if the affected statistic is "damage", "armor", or "arpen". "scale": the value of the bonus itself. -"scaling-stat": scaling stat, any of: "str", "dex", "int", "per", "bashing", "cutting", "dodge", "melee", "stabbing". Optional. If the scaling stat is specified, the value of the bonus is multiplied by the corresponding user stat/skill. +"scaling-stat": scaling stat, any of: "str", "dex", "int", "per". Optional. If the scaling stat is specified, the value of the bonus is multiplied by the corresponding user stat. Bonuses must be written in the correct order. From 72cde715ae723de25d3d08ca0bf6a7ab4be5a09d Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:36:15 -0800 Subject: [PATCH 174/202] Update MARTIALART_JSON.md --- doc/MARTIALART_JSON.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/MARTIALART_JSON.md b/doc/MARTIALART_JSON.md index 08fc81461f54e..e44208f6aef14 100644 --- a/doc/MARTIALART_JSON.md +++ b/doc/MARTIALART_JSON.md @@ -145,7 +145,6 @@ List of attack vectors is currently hardcoded, and contain: - `WEAPON` - Any technique the requires a held item to perform (see any weapon style). Can be used if the user is holding a valid style weapon for their martial art and at least one hand/arm is not broken. - `THROW` - Any technique that forcefully moves an opponent (judo throws, suplex). Can be used only if both hands/arms are not broken. - `GRAPPLE` - Any technique that maintains contact with an opponent and squeezes (chock, headlock), bends (Krav Maga's Arm Breaker), or twists (arm twist) some part of the opponent. Can be used only if both hands/arms are not broken. -- `MOUTH` - Any technique that uses the mouth, such as biting or spitting acid. Can be used if the mouth is uncovered. Items flagged with ALLOWS_NATURAL_ATTACKS are ignored when checking for coverage. ### Tech effects ```C++ From 7b27fd48cd2a5f7bc01b8f2b63d4f625998c767c Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:37:16 -0800 Subject: [PATCH 175/202] Update effects_eocs.json --- .../effects_on_condition/effects_eocs.json | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/data/json/effects_on_condition/effects_eocs.json b/data/json/effects_on_condition/effects_eocs.json index 3539252e21432..0da74fdccde14 100644 --- a/data/json/effects_on_condition/effects_eocs.json +++ b/data/json/effects_on_condition/effects_eocs.json @@ -15,53 +15,5 @@ } ], "false_effect": [ { "u_lose_morale": "morale_bad_protein_bar" } ] - }, - { - "type": "effect_on_condition", - "id": "eoc_social_satisfied", - "recurrence": [ "1 minutes", "5 minutes" ], - "condition": { "u_has_effect": "social_satisfied" }, - "effect": [ { "u_add_morale": "morale_social", "bonus": 6, "max_bonus": 6, "duration": "1 days", "decay_start": "1 days" } ], - "false_effect": [ { "u_lose_morale": "morale_social" } ] - }, - { - "type": "effect_on_condition", - "id": "eoc_social_dissatisfied", - "recurrence": [ "1 minutes", "5 minutes" ], - "condition": { "u_has_effect": "social_dissatisfied" }, - "effect": [ - { - "u_add_morale": "morale_asocial", - "bonus": { "math": [ "u_effect_intensity('social_dissatisfied') * -7" ] }, - "max_bonus": -21, - "duration": "1 days", - "decay_start": "1 days" - } - ], - "false_effect": [ { "u_lose_morale": "morale_social_dissatisfied" } ] - }, - { - "type": "effect_on_condition", - "id": "eoc_asocial_satisfied", - "recurrence": [ "1 minutes", "5 minutes" ], - "condition": { "u_has_effect": "asocial_satisfied" }, - "effect": [ { "u_add_morale": "morale_asocial", "bonus": 10, "max_bonus": 10, "duration": "1 days", "decay_start": "1 days" } ], - "false_effect": [ { "u_lose_morale": "morale_asocial" } ] - }, - { - "type": "effect_on_condition", - "id": "eoc_asocial_dissatisfied", - "recurrence": [ "1 minutes", "5 minutes" ], - "condition": { "u_has_effect": "asocial_dissatisfied" }, - "effect": [ - { - "u_add_morale": "morale_social", - "bonus": { "math": [ "u_effect_intensity('asocial_dissatisfied') * -7" ] }, - "max_bonus": -21, - "duration": "1 days", - "decay_start": "1 days" - } - ], - "false_effect": [ { "u_lose_morale": "morale_asocial_dissatisfied" } ] } ] From 712de547baba119b4c8892b1009a5cb5ea9130be Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:40:14 -0800 Subject: [PATCH 176/202] Update math_parser_diag.cpp --- src/math_parser_diag.cpp | 100 --------------------------------------- 1 file changed, 100 deletions(-) diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index 456460c9ddb2e..731aabadcb6fb 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -565,106 +565,6 @@ std::function mod_order_eval( char /* scope */, }; } -enum class character_filter : int { - allies = 0, - not_allies, - hostile, - any, -}; - -bool _friend_match_filter_character( Character const &beta, Character const &guy, - character_filter filter ) -{ - switch( filter ) { - case character_filter::allies: - return guy.is_ally( beta ); - case character_filter::not_allies: - return !guy.is_ally( beta ); - case character_filter::hostile: - return guy.attitude_to( beta ) == Creature::Attitude::HOSTILE || - ( beta.is_avatar() && guy.is_npc() && guy.as_npc()->guaranteed_hostile() ); - case character_filter::any: - return true; - } - return false; -} - -bool _filter_character( Character const &beta, Character const &guy, int radius, - tripoint_abs_ms const &loc, character_filter filter, bool allow_hallucinations ) -{ - if( ( !guy.is_hallucination() || allow_hallucinations ) && ( beta.getID() != guy.getID() ) ) { - return _friend_match_filter_character( beta, guy, filter ) && - radius >= rl_dist( guy.get_location(), loc ); - } - return false; -} - -std::function _characters_nearby_eval( char scope, - std::vector const ¶ms, diag_kwargs const &kwargs ) -{ - diag_value radius_val( 1000.0 ); - diag_value filter_val( std::string{ "any" } ); - diag_value allow_hallucinations_val( 0.0 ); - std::optional loc_var; - if( kwargs.count( "radius" ) != 0 ) { - radius_val = *kwargs.at( "radius" ); - } - if( kwargs.count( "attitude" ) != 0 ) { - filter_val = *kwargs.at( "attitude" ); - } - if( kwargs.count( "allow_hallucinations" ) != 0 ) { - allow_hallucinations_val = *kwargs.at( "allow_hallucinations" ); - } - if( kwargs.count( "location" ) != 0 ) { - loc_var = kwargs.at( "location" )->var(); - } else if( scope == 'g' ) { - throw std::invalid_argument( string_format( - R"("characters_nearby" needs either an actor scope (u/n) or a 'location' kwarg)" ) ); - } - - return [beta = is_beta( scope ), params, loc_var, filter_val, radius_val, - allow_hallucinations_val ]( dialogue & d ) { - tripoint_abs_ms loc; - if( loc_var.has_value() ) { - loc = get_tripoint_from_var( loc_var, d ); - } else { - loc = d.actor( beta )->global_pos(); - } - - int const radius = static_cast( radius_val.dbl( d ) ); - std::string const filter_str = filter_val.str( d ); - character_filter filter = character_filter::any; - if( filter_str == "allies" ) { - filter = character_filter::allies; - } else if( filter_str == "not_allies" ) { - filter = character_filter::not_allies; - } else if( filter_str == "hostile" ) { - filter = character_filter::hostile; - } else if( filter_str != "any" ) { - debugmsg( R"(Unknown attitude filter "%s" for characters_nearby(), counting all characters)", - filter_str ); - } - bool allow_hallucinations = false; - int const hallucinations_int = static_cast( allow_hallucinations_val.dbl( d ) ); - if( hallucinations_int != 0 ) { - allow_hallucinations = true; - } - - std::vector const targets = g->get_characters_if( [ &beta, &d, &radius, - &loc, filter, allow_hallucinations ]( const Character & guy ) { - return _filter_character( *d.actor( beta )->get_character(), guy, radius, loc, filter, - allow_hallucinations ); - } ); - return static_cast( targets.size() ); - }; -} - -std::function characters_nearby_eval( char scope, - std::vector const ¶ms, diag_kwargs const &kwargs ) -{ - return _characters_nearby_eval( scope, params, kwargs ); -} - template using f_monster_match = bool ( * )( Creature const &critter, ID const &id ); From 37953e54f86534ab02432fae24ef503e83c21076 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:41:04 -0800 Subject: [PATCH 177/202] Update math_parser_diag.cpp --- src/math_parser_diag.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index 731aabadcb6fb..20e7ad80c8ffc 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -1143,19 +1143,6 @@ std::function value_or_eval( char /* scope */, }; } -std::function vision_range_eval( char scope, - std::vector const &/* params */, diag_kwargs const &/* kwargs */ ) -{ - return[beta = is_beta( scope )]( dialogue const & d ) { - if( d.actor( beta )->get_character() ) { - return static_cast( d.actor( beta ) ) - ->get_character() - ->unimpaired_range(); - } - return 0; - }; -} - std::function vitamin_eval( char scope, std::vector const ¶ms, diag_kwargs const &/* kwargs */ ) { @@ -1259,7 +1246,6 @@ std::map const dialogue_eval_f{ { "addiction_turns", { "un", 1, addiction_turns_eval } }, { "armor", { "un", 2, armor_eval } }, { "attack_speed", { "un", 0, attack_speed_eval } }, - { "characters_nearby", { "ung", 0, characters_nearby_eval } }, { "charge_count", { "un", 1, charge_count_eval } }, { "coverage", { "un", 1, coverage_eval } }, { "damage_level", { "un", 0, damage_level_eval } }, From 44e1f6a06d9d1e134029b3f2cc54bf5ad4286693 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:41:31 -0800 Subject: [PATCH 178/202] Update math_parser_diag.cpp --- src/math_parser_diag.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index 20e7ad80c8ffc..8c13059baaf92 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -1290,7 +1290,6 @@ std::map const dialogue_eval_f{ { "val", { "un", 1, u_val } }, { "value_or", { "g", 2, value_or_eval } }, { "vitamin", { "un", 1, vitamin_eval } }, - { "vision_range", { "un", 0, vision_range_eval } }, { "warmth", { "un", 1, warmth_eval } }, { "weather", { "g", 1, weather_eval } }, }; From 14bc3377bb5fda62f53f2142c1622eca1a54bdd1 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:41:58 -0800 Subject: [PATCH 179/202] Update morale_types.h --- src/morale_types.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/morale_types.h b/src/morale_types.h index 2b0da768cb91d..5953e42bb8249 100644 --- a/src/morale_types.h +++ b/src/morale_types.h @@ -120,7 +120,5 @@ extern const morale_type MORALE_FUNERAL; extern const morale_type MORALE_TREE_COMMUNION; extern const morale_type MORALE_ACCOMPLISHMENT; extern const morale_type MORALE_FAILURE; -extern const morale_type MORALE_SOCIAL; -extern const morale_type MORALE_ASOCIAL; #endif // CATA_SRC_MORALE_TYPES_H From 02bb954f19b1687a418ca5877a8aff3e5cdecccd Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:42:49 -0800 Subject: [PATCH 180/202] Update mtype.cpp --- src/mtype.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mtype.cpp b/src/mtype.cpp index 447261ec9875c..891205fb32e12 100644 --- a/src/mtype.cpp +++ b/src/mtype.cpp @@ -70,7 +70,6 @@ mon_flag_id mon_flag_ACIDPROOF, mon_flag_DOGFOOD, mon_flag_DORMANT, mon_flag_GEN_DORMANT, - mon_flag_DRACULIN_IMMUNE, mon_flag_DRIPS_GASOLINE, mon_flag_DRIPS_NAPALM, mon_flag_DROPS_AMMO, @@ -194,7 +193,6 @@ void set_mon_flag_ids() mon_flag_DOGFOOD = mon_flag_id( "DOGFOOD" ); mon_flag_DORMANT = mon_flag_id( "DORMANT" ); mon_flag_GEN_DORMANT = mon_flag_id( "GEN_DORMANT" ); - mon_flag_DRACULIN_IMMUNE = mon_flag_id( "DRACULIN_IMMUNE" ); mon_flag_DRIPS_GASOLINE = mon_flag_id( "DRIPS_GASOLINE" ); mon_flag_DRIPS_NAPALM = mon_flag_id( "DRIPS_NAPALM" ); mon_flag_DROPS_AMMO = mon_flag_id( "DROPS_AMMO" ); From c7d153ad33423395cc73e70f41877080a00f5a6e Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:44:09 -0800 Subject: [PATCH 181/202] Update integrated.json --- data/json/items/armor/integrated.json | 1 - 1 file changed, 1 deletion(-) diff --git a/data/json/items/armor/integrated.json b/data/json/items/armor/integrated.json index 726d8d304aacb..9e2bf59bf914b 100644 --- a/data/json/items/armor/integrated.json +++ b/data/json/items/armor/integrated.json @@ -561,7 +561,6 @@ "symbol": ";", "color": "dark_gray", "warmth": 2, - "qualities": [ [ "CUT", 1 ], [ "BUTCHER", 4 ] ], "flags": [ "INTEGRATED", "ALLOWS_NATURAL_ATTACKS", "UNBREAKABLE", "SKINTIGHT", "PADDED", "NO_SALVAGE" ], "armor": [ { From ec12358a594d9d5a89eca9741d08cdaf41002d6c Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:44:26 -0800 Subject: [PATCH 182/202] Update effects_eocs.json --- .../effects_on_condition/effects_eocs.json | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/data/json/effects_on_condition/effects_eocs.json b/data/json/effects_on_condition/effects_eocs.json index 3539252e21432..0da74fdccde14 100644 --- a/data/json/effects_on_condition/effects_eocs.json +++ b/data/json/effects_on_condition/effects_eocs.json @@ -15,53 +15,5 @@ } ], "false_effect": [ { "u_lose_morale": "morale_bad_protein_bar" } ] - }, - { - "type": "effect_on_condition", - "id": "eoc_social_satisfied", - "recurrence": [ "1 minutes", "5 minutes" ], - "condition": { "u_has_effect": "social_satisfied" }, - "effect": [ { "u_add_morale": "morale_social", "bonus": 6, "max_bonus": 6, "duration": "1 days", "decay_start": "1 days" } ], - "false_effect": [ { "u_lose_morale": "morale_social" } ] - }, - { - "type": "effect_on_condition", - "id": "eoc_social_dissatisfied", - "recurrence": [ "1 minutes", "5 minutes" ], - "condition": { "u_has_effect": "social_dissatisfied" }, - "effect": [ - { - "u_add_morale": "morale_asocial", - "bonus": { "math": [ "u_effect_intensity('social_dissatisfied') * -7" ] }, - "max_bonus": -21, - "duration": "1 days", - "decay_start": "1 days" - } - ], - "false_effect": [ { "u_lose_morale": "morale_social_dissatisfied" } ] - }, - { - "type": "effect_on_condition", - "id": "eoc_asocial_satisfied", - "recurrence": [ "1 minutes", "5 minutes" ], - "condition": { "u_has_effect": "asocial_satisfied" }, - "effect": [ { "u_add_morale": "morale_asocial", "bonus": 10, "max_bonus": 10, "duration": "1 days", "decay_start": "1 days" } ], - "false_effect": [ { "u_lose_morale": "morale_asocial" } ] - }, - { - "type": "effect_on_condition", - "id": "eoc_asocial_dissatisfied", - "recurrence": [ "1 minutes", "5 minutes" ], - "condition": { "u_has_effect": "asocial_dissatisfied" }, - "effect": [ - { - "u_add_morale": "morale_social", - "bonus": { "math": [ "u_effect_intensity('asocial_dissatisfied') * -7" ] }, - "max_bonus": -21, - "duration": "1 days", - "decay_start": "1 days" - } - ], - "false_effect": [ { "u_lose_morale": "morale_asocial_dissatisfied" } ] } ] From ecef2bbb02d47d79ec084acc38cf6fe03741c8be Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:52:28 -0800 Subject: [PATCH 183/202] Update monster_flags.json --- data/json/monsters/monster_flags.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/data/json/monsters/monster_flags.json b/data/json/monsters/monster_flags.json index 56d73809a1dce..833f2b366516c 100644 --- a/data/json/monsters/monster_flags.json +++ b/data/json/monsters/monster_flags.json @@ -628,10 +628,5 @@ "id": "GUILT_OTHERS", "type": "monster_flag", "//": "Warning: Do not use without death_guilt function. This helps count the all the other kills that do not fit the other three (e.g blood sacrifice) before player goes numb." - }, - { - "id": "DRACULIN_IMMUNE", - "type": "monster_flag", - "//": "Immune to Chiropteran saliva. Use for any creature with nonstandard blood (ie mi-go, insects, but probably not zombies or fungalized animals, unless they have acid blood or something)." } ] From 57c79feaa87e23abcc82f5654d588decdd6d294d Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:54:04 -0800 Subject: [PATCH 184/202] Update flags.json --- data/json/flags.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/data/json/flags.json b/data/json/flags.json index 3f622efc95def..8fd63478a82d7 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -2398,11 +2398,6 @@ "type": "json_flag", "info": "The character bleeds even slower than normal, losing blood at 1/3rd the normal rate." }, - { - "id": "NATURAL_WEAPON", - "type": "json_flag", - "//": "This item is a 'natural' weapon, such as a mutant's fangs or claws. Should not be applied to bionic weapons." - }, { "id": "PSEUDOPOD_GRASP", "type": "json_flag", From b93b7ddfe821f1bfa1ce6b729bc11079f8eee5c1 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:55:30 -0800 Subject: [PATCH 185/202] Update mutation_category.json --- data/json/mutations/mutation_category.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/mutations/mutation_category.json b/data/json/mutations/mutation_category.json index ab38a26152c29..8ee8ebe23faf4 100644 --- a/data/json/mutations/mutation_category.json +++ b/data/json/mutations/mutation_category.json @@ -291,7 +291,7 @@ "id": "CHIROPTERAN", "name": "Chiropteran", "threshold_mut": "THRESH_CHIROPTERAN", - "mutagen_message": "Your ears ring, and you thirst for blood.", + "mutagen_message": "Your ears ring and you taste iron.", "memorial_message": "Became the night.", "vitamin": "mutagen_chiropteran", "base_removal_chance": 33, From 217642805f8381277b1b7c6258463900754a1856 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:56:04 -0800 Subject: [PATCH 186/202] Update mutation_type.json --- data/json/mutations/mutation_type.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/data/json/mutations/mutation_type.json b/data/json/mutations/mutation_type.json index 0eb6ccfe831a9..1902dba5f0f14 100644 --- a/data/json/mutations/mutation_type.json +++ b/data/json/mutations/mutation_type.json @@ -262,9 +262,5 @@ { "type": "mutation_type", "id": "BODY_ARMOR_MOD" - }, - { - "type": "mutation_type", - "id": "SOCIAL" } ] From 5f00ff6b9d51c07f1de07c91bdcc727e0e44c9a5 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Fri, 16 Feb 2024 19:00:25 -0800 Subject: [PATCH 187/202] Update mutations.json --- data/json/mutations/mutations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 05acf975ed50d..1aea14fc637f7 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -253,7 +253,7 @@ "player_display": false, "dummy": true, "category": [ "HUMAN" ], - "types": [ "ANIMAL_EMPATHY", "SOCIAL", "ATTITUDE", "HUMAN_EMPATHY", "MASOCHISM", "MEMORY", "SLEEPINESS", "WANDERLUST" ], + "types": [ "ANIMAL_EMPATHY", "ATTITUDE", "HUMAN_EMPATHY", "MASOCHISM", "MEMORY", "SLEEPINESS", "WANDERLUST" ], "cancels": [ "ADDICTIVE", "ADRENALINE", From 99835de3de8fbe3bdb1ce0dbf383f64fce89bfc1 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 12:12:52 -0800 Subject: [PATCH 188/202] update sonar --- src/character.cpp | 14 ++++---------- src/lightmap.cpp | 20 -------------------- src/map.cpp | 28 ++++++++++++++++------------ src/map.h | 16 +++++++--------- 4 files changed, 27 insertions(+), 51 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 6d9b8d55e5ceb..ea1270e4f0af7 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10622,13 +10622,7 @@ bool Character::sees_with_infrared( const Creature &critter ) const map &here = get_map(); - if( is_avatar() || critter.is_avatar() ) { - // Players should not use map::sees - // Likewise, players should not be "looked at" with map::sees, not to break symmetry - return here.pl_line_of_sight( critter.pos(), unimpaired_range() ); - } - - return here.sees( pos(), critter.pos(), unimpaired_range() ); + return here.sees( pos(), critter.pos(), unimpaired_range(), false ); } bool Character::is_visible_in_range( const Creature &critter, const int range ) const @@ -10718,11 +10712,11 @@ void Character::echo_pulse() "none", "none" ); } for( tripoint origin : points_in_radius( pos(), pulse_range ) ) { - if( here.move_cost( origin ) == 0 && here.pl_line_of_sight( origin, pulse_range ) ) { + if( here.move_cost( origin ) == 0 && here.sees( pos(), origin, pulse_range, false ) ) { sounds::sound( origin, 5, sounds::sound_t::sensory, _( "clack." ), true, "none", "none" ); // This only counts obstacles which can be moved through, so the echo is pretty quiet. - } else if( is_obstacle( origin ) && here.pl_line_of_sight( origin, pulse_range ) ) { + } else if( is_obstacle( origin ) && here.sees( pos(), origin, pulse_range, false ) ) { sounds::sound( origin, 1, sounds::sound_t::sensory, _( "click." ), true, "none", "none" ); } @@ -10734,7 +10728,7 @@ void Character::echo_pulse() add_known_trap( origin, tr ); } Creature *critter = get_creature_tracker().creature_at( origin, true ); - if( critter && here.pl_line_of_sight( origin, pulse_range ) ) { + if( critter && here.sees( pos(), origin, pulse_range, false ) ) { switch( critter->get_size() ) { case creature_size::tiny: echo_volume = 1; diff --git a/src/lightmap.cpp b/src/lightmap.cpp index bae55808389d9..5088a896c0941 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -806,26 +806,6 @@ bool map::pl_sees( const tripoint &t, const int max_range ) const map_cache.sm[t.x][t.y] > 0.0 ); } -bool map::pl_line_of_sight( const tripoint &t, const int max_range ) const -{ - if( !inbounds( t ) ) { - return false; - } - - const level_cache &map_cache = get_cache_ref( t.z ); - if( map_cache.camera_cache[t.x][t.y] > 0.075f ) { - return true; - } - - if( max_range >= 0 && square_dist( t, get_player_character().pos() ) > max_range ) { - // Out of range! - return false; - } - - // Any epsilon > 0 is fine - it means lightmap processing visited the point - return map_cache.seen_cache[t.x][t.y] > 0.0f; -} - // For a direction vector defined by x, y, return the quadrant that's the // source of that direction. Assumes x != 0 && y != 0 // NOLINTNEXTLINE(cata-xy) diff --git a/src/map.cpp b/src/map.cpp index 17e4cb2d135be..9b0ad58b3c569 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -7226,10 +7226,10 @@ void map::draw_from_above( const catacurses::window &w, const tripoint &p, } } -bool map::sees( const tripoint &F, const tripoint &T, const int range ) const +bool map::sees( const tripoint &F, const tripoint &T, const int range, bool with_fields ) const { int dummy = 0; - return sees( F, T, range, dummy ); + return sees( F, T, range, dummy, with_fields ); } point map::sees_cache_key( const tripoint &from, const tripoint &to ) const @@ -7250,8 +7250,11 @@ point map::sees_cache_key( const tripoint &from, const tripoint &to ) const * This one is internal-only, we don't want to expose the slope tweaking ickiness outside the map class. **/ bool map::sees( const tripoint &F, const tripoint &T, const int range, - int &bresenham_slope ) const + int &bresenham_slope, bool with_fields ) const { + bool ( map::*f_transparent )( const tripoint & p ) const = + with_fields ? &map::is_transparent : &map::is_transparent_wo_fields; + lru_cache_t &skew_cache = with_fields ? skew_vision_cache : skew_vision_wo_fields_cache; if( std::abs( F.z - T.z ) > fov_3d_z_range || ( range >= 0 && range < rl_dist( F, T ) ) || !inbounds( T ) ) { @@ -7259,7 +7262,7 @@ bool map::sees( const tripoint &F, const tripoint &T, const int range, return false; // Out of range! } const point key = sees_cache_key( F, T ); - char cached = skew_vision_cache.get( key, -1 ); + char cached = skew_cache.get( key, -1 ); if( cached >= 0 ) { return cached > 0; } @@ -7268,24 +7271,24 @@ bool map::sees( const tripoint &F, const tripoint &T, const int range, // Ugly `if` for now if( F.z == T.z ) { bresenham( F.xy(), T.xy(), bresenham_slope, - [this, &visible, &T]( const point & new_point ) { + [this, f_transparent, &visible, &T]( const point & new_point ) { // Exit before checking the last square, it's still visible even if opaque. if( new_point.x == T.x && new_point.y == T.y ) { return false; } - if( !this->is_transparent( tripoint( new_point, T.z ) ) ) { + if( !( this->*f_transparent )( tripoint( new_point, T.z ) ) ) { visible = false; return false; } return true; } ); - skew_vision_cache.insert( 100000, key, visible ? 1 : 0 ); + skew_cache.insert( 100000, key, visible ? 1 : 0 ); return visible; } tripoint last_point = F; bresenham( F, T, bresenham_slope, 0, - [this, &visible, &T, &last_point]( const tripoint & new_point ) { + [this, f_transparent, &visible, &T, &last_point]( const tripoint & new_point ) { // Exit before checking the last square if it's not a vertical transition, // it's still visible even if opaque. if( new_point == T && last_point.z == T.z ) { @@ -7294,16 +7297,16 @@ bool map::sees( const tripoint &F, const tripoint &T, const int range, // TODO: Allow transparent floors (and cache them!) if( new_point.z == last_point.z ) { - if( !this->is_transparent( new_point ) ) { + if( !( this->*f_transparent )( new_point ) ) { visible = false; return false; } } else { const int max_z = std::max( new_point.z, last_point.z ); if( ( has_floor_or_support( {new_point.xy(), max_z} ) || - !is_transparent( {new_point.xy(), last_point.z} ) ) && + !( this->*f_transparent )( {new_point.xy(), last_point.z} ) ) && ( has_floor_or_support( {last_point.xy(), max_z} ) || - !is_transparent( {last_point.xy(), new_point.z} ) ) ) { + !( this->*f_transparent )( {last_point.xy(), new_point.z} ) ) ) { visible = false; return false; } @@ -7312,7 +7315,7 @@ bool map::sees( const tripoint &F, const tripoint &T, const int range, last_point = new_point; return true; } ); - skew_vision_cache.insert( 100000, key, visible ? 1 : 0 ); + skew_cache.insert( 100000, key, visible ? 1 : 0 ); return visible; } @@ -9413,6 +9416,7 @@ void map::build_map_cache( const int zlev, bool skip_lightmap ) if( seen_cache_dirty ) { skew_vision_cache.clear(); + skew_vision_wo_fields_cache.clear(); } avatar &u = get_avatar(); Character::moncam_cache_t mcache = u.get_active_moncams(); diff --git a/src/map.h b/src/map.h index 627382a354fe6..3aa7abe5bcfab 100644 --- a/src/map.h +++ b/src/map.h @@ -602,7 +602,7 @@ class map /** * Returns whether `F` sees `T` with a view range of `range`. */ - bool sees( const tripoint &F, const tripoint &T, int range ) const; + bool sees( const tripoint &F, const tripoint &T, int range, bool with_fields = true ) const; private: /** * Don't expose the slope adjust outside map functions. @@ -614,7 +614,8 @@ class map * the two points, and may subsequently be used to form a path between them. * Set to zero if the function returns false. **/ - bool sees( const tripoint &F, const tripoint &T, int range, int &bresenham_slope ) const; + bool sees( const tripoint &F, const tripoint &T, int range, int &bresenham_slope, + bool with_fields = true ) const; point sees_cache_key( const tripoint &from, const tripoint &to ) const; public: /** @@ -1824,12 +1825,7 @@ class map * Ignored if smaller than 0. */ bool pl_sees( const tripoint &t, int max_range ) const; - /** - * Uses the map cache to tell if the player could see the given square. - * pl_sees implies pl_line_of_sight - * Used for infrared. - */ - bool pl_line_of_sight( const tripoint &t, int max_range ) const; + std::set dirty_vehicle_list; /** return @ref abs_sub */ @@ -2262,7 +2258,9 @@ class map /** * Cache of coordinate pairs recently checked for visibility. */ - mutable lru_cache skew_vision_cache; + using lru_cache_t = lru_cache; + mutable lru_cache_t skew_vision_cache; + mutable lru_cache_t skew_vision_wo_fields_cache; // Note: no bounds check level_cache &get_cache( int zlev ) const { From 572e800ec750beb412b00dab8f0f81a81a313da9 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 12:32:31 -0800 Subject: [PATCH 189/202] remove NATURAL_WEAPON flag for now --- src/melee.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/melee.cpp b/src/melee.cpp index e5dff5eb5da95..d9641a67ea8b9 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -389,8 +389,8 @@ float Character::hit_roll() const hit -= 8.0f; } } else if( is_crouching() && ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || - ( !has_effect( effect_natural_stance ) && ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && - !unarmed_attack() ) ) ) ) { + ( !has_effect( effect_natural_stance ) && + !unarmed_attack() ) ) ) { hit -= 2.0f; } @@ -795,8 +795,8 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, } d.mult_damage( 0.3 ); } else if( is_crouching() && ( ( !has_effect( effect_natural_stance ) && - ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !unarmed_attack() ) ) || - !has_flag( json_flag_PSEUDOPOD_GRASP ) ) ) { + !unarmed_attack() ) ) || + !has_flag( json_flag_PSEUDOPOD_GRASP ) ) { d.mult_damage( 0.8 ); } @@ -970,8 +970,7 @@ int Character::get_total_melee_stamina_cost( const item *weap ) const // Quadrupeds don't mind crouching, squids and slimes hardly care about even being prone const int stance_malus = ( is_on_ground() && !has_flag( json_flag_PSEUDOPOD_GRASP ) ) ? 50 : ( ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || - ( !has_effect( effect_natural_stance ) && ( !weap->has_flag( flag_NATURAL_WEAPON ) && - !unarmed_attack() ) ) ) && is_crouching() ? 20 : 0 ); + ( !has_effect( effect_natural_stance ) && !unarmed_attack() ) ) && is_crouching() ? 20 : 0 ); return std::min( -50, mod_sta + melee - stance_malus ); } @@ -1068,7 +1067,7 @@ int stumble( Character &u, const item_location &weap ) str_mod /= 4; // but quadrupeds fight naturally on all fours } else if( u.is_crouching() && ( !u.has_effect( effect_natural_stance ) && - ( !cur_weap.has_flag( flag_NATURAL_WEAPON ) && !u.unarmed_attack() ) ) ) { + ( !u.unarmed_attack() ) ) ) { str_mod /= 2; } @@ -2773,8 +2772,8 @@ int Character::attack_speed( const item &weap ) const move_cost *= 4.0; } } else if( is_crouching() && ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || - ( !has_effect( effect_natural_stance ) && ( !weap.has_flag( flag_NATURAL_WEAPON ) && - !unarmed_attack() ) ) ) ) { + ( !has_effect( effect_natural_stance ) && + !unarmed_attack() ) ) ) { move_cost *= 1.5; } From ba7f79dc3368361f800032bd2d1536365b7437ba Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 12:34:06 -0800 Subject: [PATCH 190/202] redesc natural_stance for now --- data/json/effects.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/effects.json b/data/json/effects.json index 693fd328777ba..f9783ea772ffe 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -4903,7 +4903,7 @@ "name": [ "Natural Stance" ], "rating": "good", "desc": [ - "You are positioned to take advantage of your mutated anatomy, and will favor any natural attacks you may have. You receive no penalties for crouching in melee combat." + "You are positioned to take advantage of your mutated anatomy, and will receive no penalties for crouching in melee combat. Wielding a weapon will cause you to lose this effect." ] }, { From b1ca54db07bded5e65d60997b06762b19eb7df37 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 12:54:23 -0800 Subject: [PATCH 191/202] astyle --- src/melee.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/melee.cpp b/src/melee.cpp index d9641a67ea8b9..6fd87068dae20 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -390,7 +390,7 @@ float Character::hit_roll() const } } else if( is_crouching() && ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || ( !has_effect( effect_natural_stance ) && - !unarmed_attack() ) ) ) { + !unarmed_attack() ) ) ) { hit -= 2.0f; } @@ -795,8 +795,8 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, } d.mult_damage( 0.3 ); } else if( is_crouching() && ( ( !has_effect( effect_natural_stance ) && - !unarmed_attack() ) ) || - !has_flag( json_flag_PSEUDOPOD_GRASP ) ) { + !unarmed_attack() ) ) || + !has_flag( json_flag_PSEUDOPOD_GRASP ) ) { d.mult_damage( 0.8 ); } @@ -2773,7 +2773,7 @@ int Character::attack_speed( const item &weap ) const } } else if( is_crouching() && ( !has_flag( json_flag_PSEUDOPOD_GRASP ) || ( !has_effect( effect_natural_stance ) && - !unarmed_attack() ) ) ) { + !unarmed_attack() ) ) ) { move_cost *= 1.5; } From 67fa5817a79fedc6825967956b193a6e45f522b6 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:03:30 -0800 Subject: [PATCH 192/202] fix subaquatic sonar --- data/json/effects_on_condition/bionic_eocs.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/data/json/effects_on_condition/bionic_eocs.json b/data/json/effects_on_condition/bionic_eocs.json index 57e730b083be8..348368ed979da 100644 --- a/data/json/effects_on_condition/bionic_eocs.json +++ b/data/json/effects_on_condition/bionic_eocs.json @@ -145,14 +145,12 @@ { "type": "effect_on_condition", "id": "EOC_BIO_SONAR_activated", - "recurrence": [ "1 seconds", "1 seconds" ], "condition": { "and": [ { "not": { "u_has_effect": "subaquatic_sonar" } }, { "u_has_bionics": "bio_sonar" } ] }, "effect": [ { "u_add_effect": "subaquatic_sonar", "duration": "PERMANENT" } ] }, { "type": "effect_on_condition", "id": "EOC_BIO_SONAR_deactivated", - "recurrence": [ "1 seconds", "1 seconds" ], "condition": { "u_has_effect": "subaquatic_sonar" }, "effect": [ { "u_lose_effect": "subaquatic_sonar" } ] } From 208ca65ae98e6f6ea13324a7ec5a146664ffcf89 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:16:42 -0800 Subject: [PATCH 193/202] Update consumption.cpp --- src/consumption.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 75c1d35dafe37..c91921c956a93 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -952,8 +952,7 @@ ret_val Character::will_eat( const item &food, bool interactive ) !has_flag( json_flag_STRICT_HUMANITARIAN ) ); if( ( food_is_human_flesh && !has_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) && !has_flag( json_flag_PSYCHOPATH ) && !has_flag( json_flag_SAPIOVORE ) ) && - ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_HEMOVORE ) && - !has_flag( json_flag_BLOODFEEDER ) ) ) ) { + ( !food.has_flag( flag_HEMOVORE_FUN ) || ( !has_flag( json_flag_BLOODFEEDER ) ) ) ) { add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM ); } @@ -1352,12 +1351,16 @@ void Character::modify_morale( item &food, const int nutr ) add_msg_if_player( _( "You greedily devour the taboo meat." ) ); // Small bonus for violating a taboo. add_morale( MORALE_CANNIBAL, 5, 50 ); + } else if( has_flag( json_flag_BLOODFEEDER ) && food.has_flag( flag_HEMOVORE_FUN ) ) { + add_msg_if_player( _( "The human blood tastes as good as any other." ) ); } else if( psycho ) { add_msg_if_player( _( "Meh. You've eaten worse." ) ); } else if( sapiovore ) { add_msg_if_player( _( "Mmh. Tastes like venison." ) ); } else if( has_flag( json_flag_HEMOVORE ) && food.has_flag( flag_HEMOVORE_FUN ) ) { - add_msg_if_player( _( "The human blood tastes as good as any other." ) ); + add_msg_if_player( + _( "Despite your cravings, you still can't help feeling weird about drinking somebody's blood." ) ); + add_morale( MORALE_CANNIBAL, -10, -40, 40_minutes, 20_minutes ); } else if( spiritual ) { add_msg_if_player( m_bad, _( "This is probably going to count against you if there's still an afterlife." ) ); From 3ece0d9893f48c6b2e4ce74f1bb4e407d1586d4f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:18:22 -0800 Subject: [PATCH 194/202] Update melee.cpp --- src/melee.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/melee.cpp b/src/melee.cpp index 6fd87068dae20..7a5ac671f3870 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -794,9 +794,9 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, d.mult_damage( 0.8 ); } d.mult_damage( 0.3 ); - } else if( is_crouching() && ( ( !has_effect( effect_natural_stance ) && + } else if( is_crouching() && ( ( ( !has_effect( effect_natural_stance ) && !unarmed_attack() ) ) || - !has_flag( json_flag_PSEUDOPOD_GRASP ) ) { + !has_flag( json_flag_PSEUDOPOD_GRASP ) ) ) { d.mult_damage( 0.8 ); } From 7cedc4ae674a8695158ddd05810cdb951143559b Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:24:38 -0800 Subject: [PATCH 195/202] Update melee.cpp --- src/melee.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/melee.cpp b/src/melee.cpp index 7a5ac671f3870..3ed7f941fccbe 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -795,8 +795,8 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, } d.mult_damage( 0.3 ); } else if( is_crouching() && ( ( ( !has_effect( effect_natural_stance ) && - !unarmed_attack() ) ) || - !has_flag( json_flag_PSEUDOPOD_GRASP ) ) ) { + !unarmed_attack() ) ) || + !has_flag( json_flag_PSEUDOPOD_GRASP ) ) ) { d.mult_damage( 0.8 ); } From 8edddcc1fb5a1ad8c0677c54d9e1a20d00508b44 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:25:09 -0800 Subject: [PATCH 196/202] Update consumption.cpp --- src/consumption.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index c91921c956a93..26ffc4b96c006 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1360,7 +1360,7 @@ void Character::modify_morale( item &food, const int nutr ) } else if( has_flag( json_flag_HEMOVORE ) && food.has_flag( flag_HEMOVORE_FUN ) ) { add_msg_if_player( _( "Despite your cravings, you still can't help feeling weird about drinking somebody's blood." ) ); - add_morale( MORALE_CANNIBAL, -10, -40, 40_minutes, 20_minutes ); + add_morale( MORALE_CANNIBAL, -10, -30, 30_minutes, 15_minutes ); } else if( spiritual ) { add_msg_if_player( m_bad, _( "This is probably going to count against you if there's still an afterlife." ) ); From f10398ae2629dccadda6f0e624de3afc819057dc Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 14:06:59 -0800 Subject: [PATCH 197/202] Update monattack.cpp --- src/monattack.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/monattack.cpp b/src/monattack.cpp index 666f676ac8648..e052cf77f1e25 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -4708,11 +4708,6 @@ bool mattack::slimespring( monster *z ) add_msg( m_good, "%s", SNIPPET.random_from_category( "slime_cheers" ).value_or( translation() ) ); player_character.add_morale( MORALE_SUPPORT, 10, 50 ); } - // They will stave off loneliness, but aren't a substitute for real friends. - if( player_character.has_effect( effect_social_dissatisfied ) ) { - add_msg( m_good, "%s", SNIPPET.random_from_category( "slime_cheers" ).value_or( translation() ) ); - player_character.remove_effect( effect_social_dissatisfied ); - } if( rl_dist( z->pos(), player_character.pos() ) <= 3 && z->sees( player_character ) ) { if( player_character.has_effect( effect_bleed ) || player_character.has_effect( effect_bite ) ) { From a0b2cf81526fb8742c27ddc21046dc9dfcdf5fdb Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 14:08:44 -0800 Subject: [PATCH 198/202] Update monattack.cpp --- src/monattack.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/monattack.cpp b/src/monattack.cpp index e052cf77f1e25..0ad9acd5ee902 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -139,7 +139,6 @@ static const efftype_id effect_raising( "raising" ); static const efftype_id effect_rat( "rat" ); static const efftype_id effect_shrieking( "shrieking" ); static const efftype_id effect_slimed( "slimed" ); -static const efftype_id effect_social_dissatisfied( "social_dissatisfied" ); static const efftype_id effect_stunned( "stunned" ); static const efftype_id effect_taint( "taint" ); static const efftype_id effect_targeted( "targeted" ); From da7753ad2366d9f476c53b2a362c2ad04aed6b8f Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 14:10:34 -0800 Subject: [PATCH 199/202] Update morale_types.cpp --- src/morale_types.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/morale_types.cpp b/src/morale_types.cpp index a607fb9bb2418..9988b9b77b733 100644 --- a/src/morale_types.cpp +++ b/src/morale_types.cpp @@ -17,7 +17,6 @@ const morale_type MORALE_ANTIJUNK( "morale_antijunk" ); const morale_type MORALE_ANTIMEAT( "morale_antimeat" ); const morale_type MORALE_ANTIVEGGY( "morale_antiveggy" ); const morale_type MORALE_ANTIWHEAT( "morale_antiwheat" ); -const morale_type MORALE_ASOCIAL( "morale_asocial" ); const morale_type MORALE_ATE_WITHOUT_TABLE( "morale_ate_without_table" ); const morale_type MORALE_ATE_WITH_TABLE( "morale_ate_with_table" ); const morale_type MORALE_BOOK( "morale_book" ); @@ -87,7 +86,6 @@ const morale_type MORALE_PYROMANIA_NOFIRE( "morale_pyromania_nofire" ); const morale_type MORALE_PYROMANIA_STARTFIRE( "morale_pyromania_startfire" ); const morale_type MORALE_SCREAM( "morale_scream" ); const morale_type MORALE_SHAVE( "morale_shave" ); -const morale_type MORALE_SOCIAL( "morale_social" ); const morale_type MORALE_SUPPORT( "morale_support" ); const morale_type MORALE_SWEETTOOTH( "morale_sweettooth" ); const morale_type MORALE_TREE_COMMUNION( "morale_tree_communion" ); From 6c48fd4b7f836b42c7b413f297b51df486ca911c Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Sun, 18 Feb 2024 20:43:37 -0800 Subject: [PATCH 200/202] fixes --- data/json/mutations/mutations.json | 2 +- src/character.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 1aea14fc637f7..ab235f56383a3 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -5410,7 +5410,7 @@ "prereqs2": [ "HOLLOW_BONES" ], "threshreq": [ "THRESH_BIRD" ], "category": [ "BIRD" ], - "restricts_gear": [ "arm_l", "arm_r", "hand_l", "hand_r", "arm_stub_r", "arm_stub_l" ], + "restricts_gear": [ "arm_l", "arm_r", "hand_l", "hand_r" ], "flags": [ "WINGS_2", "WING_GLIDE", "ARM_WINGS" ] }, { diff --git a/src/character.cpp b/src/character.cpp index ea1270e4f0af7..e081d964612de 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10759,7 +10759,7 @@ void Character::echo_pulse() const char *echo_string = nullptr; // bio_targeting has a visual HUD and automatically interprets the raw audio data, // but only for electronic SONAR. Its designers didn't anticipate bat mutations - if( has_bionic( bio_targeting ) && has_effect( effect_subaquatic_sonar ) ) + if( has_bionic( bio_targeting ) && has_effect( effect_subaquatic_sonar ) ) { switch( echo_volume ) { case 1: echo_string = _( "Target [Tiny]." ); @@ -10779,7 +10779,8 @@ void Character::echo_pulse() default: debugmsg( "ERROR: Invalid echo string." ); break; - } else if( !has_bionic( bio_targeting ) && has_effect( effect_subaquatic_sonar ) ) { + } + } else if( !has_bionic( bio_targeting ) && has_effect( effect_subaquatic_sonar ) ) { switch( echo_volume ) { case 1: echo_string = _( "tick." ); From e4f5e4daf30b149f9ac8906bc060ebbee8cd6207 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Mon, 19 Feb 2024 05:46:40 -0800 Subject: [PATCH 201/202] Update melee.cpp --- src/melee.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/melee.cpp b/src/melee.cpp index 3ed7f941fccbe..89567c954e1c6 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -794,8 +794,8 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, d.mult_damage( 0.8 ); } d.mult_damage( 0.3 ); - } else if( is_crouching() && ( ( ( !has_effect( effect_natural_stance ) && - !unarmed_attack() ) ) || + } else if( is_crouching() && ( ( !has_effect( effect_natural_stance ) && + !unarmed_attack() ) || !has_flag( json_flag_PSEUDOPOD_GRASP ) ) ) { d.mult_damage( 0.8 ); } @@ -1067,7 +1067,7 @@ int stumble( Character &u, const item_location &weap ) str_mod /= 4; // but quadrupeds fight naturally on all fours } else if( u.is_crouching() && ( !u.has_effect( effect_natural_stance ) && - ( !u.unarmed_attack() ) ) ) { + !u.unarmed_attack() ) ) { str_mod /= 2; } From 71a196648ee8da746f3395328abacfd4e07c57d2 Mon Sep 17 00:00:00 2001 From: fairyarmadillo <94415528+fairyarmadillo@users.noreply.github.com> Date: Mon, 19 Feb 2024 06:05:00 -0800 Subject: [PATCH 202/202] Update melee.cpp --- src/melee.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/melee.cpp b/src/melee.cpp index 89567c954e1c6..fa4ac6e53c09a 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -795,7 +795,7 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, } d.mult_damage( 0.3 ); } else if( is_crouching() && ( ( !has_effect( effect_natural_stance ) && - !unarmed_attack() ) || + !unarmed_attack() ) || !has_flag( json_flag_PSEUDOPOD_GRASP ) ) ) { d.mult_damage( 0.8 ); }