diff --git a/data/json/monsters/fish.json b/data/json/monsters/fish.json index 1c37745493e44..b17d790902af5 100644 --- a/data/json/monsters/fish.json +++ b/data/json/monsters/fish.json @@ -63,7 +63,7 @@ "baby_flags": [ "SPRING", "SUMMER" ], "fear_triggers": [ "PLAYER_CLOSE", "SOUND" ], "harvest": "fish_tiny", - "flags": [ "FISHABLE", "SEES", "SMELLS", "WARM", "SWIMS", "AQUATIC" ] + "flags": [ "FISHABLE", "SEES", "SMELLS", "SWIMS", "AQUATIC", "WATER_CAMOUFLAGE" ] }, { "id": "mon_fish_small", @@ -93,7 +93,7 @@ "baby_flags": [ "SPRING", "SUMMER" ], "fear_triggers": [ "PLAYER_CLOSE", "SOUND" ], "harvest": "fish_small", - "flags": [ "FISHABLE", "SEES", "SMELLS", "WARM", "SWIMS", "AQUATIC" ] + "flags": [ "FISHABLE", "SEES", "SMELLS", "SWIMS", "AQUATIC", "WATER_CAMOUFLAGE" ] }, { "id": "mon_fish_medium", @@ -123,7 +123,7 @@ "baby_flags": [ "SPRING", "SUMMER" ], "fear_triggers": [ "PLAYER_CLOSE", "SOUND" ], "harvest": "fish_small", - "flags": [ "FISHABLE", "SEES", "SMELLS", "WARM", "SWIMS", "AQUATIC" ] + "flags": [ "FISHABLE", "SEES", "SMELLS", "SWIMS", "AQUATIC", "WATER_CAMOUFLAGE" ] }, { "id": "mon_fish_large", @@ -153,7 +153,7 @@ "baby_flags": [ "SPRING", "SUMMER" ], "fear_triggers": [ "SOUND", "PLAYER_CLOSE" ], "harvest": "fish_large", - "flags": [ "FISHABLE", "SEES", "SMELLS", "WARM", "SWIMS", "AQUATIC" ] + "flags": [ "FISHABLE", "SEES", "SMELLS", "SWIMS", "AQUATIC", "WATER_CAMOUFLAGE" ] }, { "id": "mon_fish_huge", @@ -183,7 +183,7 @@ "baby_flags": [ "SPRING", "SUMMER" ], "fear_triggers": [ "PLAYER_CLOSE", "SOUND" ], "harvest": "fish_large", - "flags": [ "FISHABLE", "SEES", "SMELLS", "WARM", "SWIMS", "AQUATIC" ] + "flags": [ "FISHABLE", "SEES", "SMELLS", "SWIMS", "AQUATIC", "WATER_CAMOUFLAGE" ] }, { "id": "mon_fish_trout", @@ -502,7 +502,7 @@ "reproduction": { "baby_egg": "egg_fish", "baby_count": 2, "baby_timer": 180 }, "baby_flags": [ "SUMMER", "AUTUMN" ], "fear_triggers": [ "PLAYER_CLOSE", "SOUND" ], - "flags": [ "FISHABLE", "SEES", "SMELLS", "WARM", "SWIMS", "AQUATIC" ] + "flags": [ "FISHABLE", "SEES", "SMELLS", "SWIMS", "AQUATIC", "WATER_CAMOUFLAGE", "ARTHROPOD_BLOOD" ] }, { "id": "mon_fish_crayfish", @@ -531,7 +531,7 @@ "baby_flags": [ "AUTUMN" ], "fear_triggers": [ "PLAYER_CLOSE", "SOUND" ], "upgrades": { "half_life": 14, "into": "mon_crayfish_small" }, - "flags": [ "FISHABLE", "SEES", "SMELLS", "WARM", "SWIMS", "AQUATIC" ] + "flags": [ "FISHABLE", "SEES", "SMELLS", "SWIMS", "AQUATIC", "WATER_CAMOUFLAGE", "ARTHROPOD_BLOOD" ] }, { "id": "mon_fish_blinky", @@ -557,7 +557,7 @@ "luminance": 5, "fear_triggers": [ "PLAYER_CLOSE", "SOUND" ], "harvest": "fish_small", - "flags": [ "FISHABLE", "SEES", "SMELLS", "WARM", "SWIMS", "AQUATIC" ] + "flags": [ "FISHABLE", "SEES", "SMELLS", "SWIMS", "AQUATIC", "WATER_CAMOUFLAGE" ] }, { "id": "mon_fish_eel", @@ -583,7 +583,7 @@ "luminance": 0, "fear_triggers": [ "PLAYER_CLOSE", "SOUND" ], "harvest": "fish_small", - "flags": [ "FISHABLE", "SEES", "SMELLS", "WARM", "SWIMS", "AQUATIC" ] + "flags": [ "FISHABLE", "SEES", "SMELLS", "SWIMS", "AQUATIC", "WATER_CAMOUFLAGE" ] }, { "id": "mon_crayfish_small", @@ -705,7 +705,7 @@ "harvest": "mutant_fish", "reproduction": { "baby_egg": "egg_fish", "baby_count": 1, "baby_timer": 6 }, "baby_flags": [ "SPRING" ], - "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "SWIMS", "AQUATIC" ] + "flags": [ "SEES", "HEARS", "SMELLS", "SWIMS", "AQUATIC" ] }, { "id": "mon_mutant_salmon", @@ -733,7 +733,7 @@ "harvest": "mutant_fish", "reproduction": { "baby_egg": "egg_fish", "baby_count": 2, "baby_timer": 150 }, "baby_flags": [ "AUTUMN" ], - "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "SWIMS", "AQUATIC" ] + "flags": [ "SEES", "HEARS", "SMELLS", "SWIMS", "AQUATIC" ] }, { "id": "mon_sewer_fish", @@ -763,6 +763,6 @@ "reproduction": { "baby_egg": "egg_fish", "baby_count": 2, "baby_timer": 19 }, "baby_flags": [ "SPRING", "SUMMER", "AUTUMN", "WINTER" ], "path_settings": { "max_dist": 5 }, - "flags": [ "SEES", "SMELLS", "WARM", "AQUATIC" ] + "flags": [ "SEES", "SMELLS", "AQUATIC" ] } ] diff --git a/data/json/monsters/insect_spider.json b/data/json/monsters/insect_spider.json index ad17e45b1333d..ab8683fbd117e 100644 --- a/data/json/monsters/insect_spider.json +++ b/data/json/monsters/insect_spider.json @@ -618,7 +618,7 @@ "harvest": "arachnid", "anger_triggers": [ "PLAYER_CLOSE" ], "fear_triggers": [ "HURT" ], - "flags": [ "AQUATIC", "SEES" ], + "flags": [ "AQUATIC", "SEES", "WATER_CAMOUFLAGE" ], "upgrades": { "age_grow": 21, "into": "mon_dragonfly_giant" } }, { @@ -762,6 +762,7 @@ "harvest": "arachnid_flying", "fear_triggers": [ "PLAYER_CLOSE", "HURT", "FIRE" ], "upgrades": { "half_life": 21, "into": "mon_fly_mega" }, + "//": "LOUDMOVES is now part of the species INSECT_FLYING", "flags": [ "SEES", "SMELLS", "FLIES", "STUMBLES", "HIT_AND_RUN", "CANPLAY", "PATH_AVOID_FIRE" ] }, { diff --git a/data/json/monsters/mammal.json b/data/json/monsters/mammal.json index 2052bacb29627..21f77186bf4ef 100644 --- a/data/json/monsters/mammal.json +++ b/data/json/monsters/mammal.json @@ -124,7 +124,7 @@ "anger_triggers": [ "PLAYER_CLOSE", "HURT" ], "fear_triggers": [ "SOUND" ], "zombify_into": "mon_zombeaver", - "flags": [ "SEES", "HEARS", "SMELLS", "ANIMAL", "PATH_AVOID_DANGER_1", "SWIMS", "WARM" ] + "flags": [ "SEES", "HEARS", "SMELLS", "ANIMAL", "PATH_AVOID_DANGER_1", "SWIMS", "WARM", "WATER_CAMOUFLAGE" ] }, { "id": "mon_black_rat", @@ -1639,7 +1639,7 @@ "anger_triggers": [ "FRIEND_ATTACKED", "FRIEND_DIED" ], "fear_triggers": [ "SOUND", "PLAYER_CLOSE" ], "placate_triggers": [ "MEAT" ], - "flags": [ "SEES", "HEARS", "SMELLS", "ANIMAL", "PATH_AVOID_DANGER_1", "WARM", "HIT_AND_RUN", "KEENNOSE" ] + "flags": [ "SEES", "HEARS", "SMELLS", "ANIMAL", "PATH_AVOID_DANGER_1", "WARM", "HIT_AND_RUN", "KEENNOSE", "CLIMBS" ] }, { "id": "mon_fox_red", @@ -1814,7 +1814,7 @@ "anger_triggers": [ "FRIEND_ATTACKED", "FRIEND_DIED" ], "fear_triggers": [ "SOUND", "PLAYER_CLOSE" ], "placate_triggers": [ "MEAT" ], - "flags": [ "SEES", "HEARS", "SMELLS", "ANIMAL", "PATH_AVOID_DANGER_1", "WARM", "HIT_AND_RUN", "KEENNOSE" ] + "flags": [ "SEES", "HEARS", "SMELLS", "ANIMAL", "PATH_AVOID_DANGER_1", "WARM", "HIT_AND_RUN", "KEENNOSE", "SWIMS" ] }, { "id": "mon_moose", @@ -1970,7 +1970,7 @@ "dodge": 4, "harvest": "mammal_small_fur", "fear_triggers": [ "SOUND", "PLAYER_CLOSE" ], - "flags": [ "SEES", "HEARS", "SMELLS", "ANIMAL", "PATH_AVOID_DANGER_1", "SWIMS", "WARM" ] + "flags": [ "SEES", "HEARS", "SMELLS", "ANIMAL", "PATH_AVOID_DANGER_1", "SWIMS", "WARM", "WATER_CAMOUFLAGE" ] }, { "id": "mon_pig_piglet", diff --git a/data/json/monsters/triffid.json b/data/json/monsters/triffid.json index cf3d81b2ac195..347fddbe1c7b9 100644 --- a/data/json/monsters/triffid.json +++ b/data/json/monsters/triffid.json @@ -228,7 +228,7 @@ "armor_bash": 18, "bleed_rate": 10, "harvest": "triffid_small", - "flags": [ "HEARS", "GOODHEARING", "NOHEAD", "HARDTOSHOOT", "GRABS", "SWIMS", "PLASTIC" ] + "flags": [ "HEARS", "GOODHEARING", "NOHEAD", "HARDTOSHOOT", "GRABS", "SWIMS", "PLASTIC", "WATER_CAMOUFLAGE" ] }, { "id": "mon_fungal_fighter", @@ -268,7 +268,7 @@ "id": "mon_triffid_flower", "type": "MONSTER", "name": { "str": "triffid flower" }, - "description": "A giant plant with a thick stalk adorned by a purple flower. Its petals are open with ominous shine in center.", + "description": "A giant plant with a thick stalk adorned by a purple flower. Its petals are open with an ominous shine in the center.", "default_faction": "triffid", "species": [ "PLANT" ], "diff": 4, diff --git a/data/json/monsters/zed-animal.json b/data/json/monsters/zed-animal.json index 621abe09cd838..3f7de1d836a16 100644 --- a/data/json/monsters/zed-animal.json +++ b/data/json/monsters/zed-animal.json @@ -26,7 +26,19 @@ "armor_bullet": 2, "luminance": 0, "harvest": "zombie_leather", - "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "SWIMS", "AQUATIC", "POISON", "NO_BREATHE", "REVIVES", "FILTHY" ] + "flags": [ + "SEES", + "HEARS", + "SMELLS", + "WARM", + "SWIMS", + "AQUATIC", + "POISON", + "NO_BREATHE", + "REVIVES", + "FILTHY", + "WATER_CAMOUFLAGE" + ] }, { "id": "mon_zombie_dog", diff --git a/data/json/monsters/zed_misc.json b/data/json/monsters/zed_misc.json index 214068c777840..267ecdd733372 100644 --- a/data/json/monsters/zed_misc.json +++ b/data/json/monsters/zed_misc.json @@ -803,7 +803,8 @@ "REVIVES", "PUSH_MON", "SWIMS", - "FILTHY" + "FILTHY", + "WATER_CAMOUFLAGE" ] }, { @@ -1437,7 +1438,8 @@ "SWIMS", "REVIVES", "PUSH_MON", - "FILTHY" + "FILTHY", + "WATER_CAMOUFLAGE" ] }, { diff --git a/data/mods/Dark-Skies-Above/monsters/alien_fauna.json b/data/mods/Dark-Skies-Above/monsters/alien_fauna.json index 4a5f50db1cf51..9df74615bcea9 100644 --- a/data/mods/Dark-Skies-Above/monsters/alien_fauna.json +++ b/data/mods/Dark-Skies-Above/monsters/alien_fauna.json @@ -75,7 +75,7 @@ } ], "anger_triggers": [ "PLAYER_CLOSE", "PLAYER_WEAK" ], - "flags": [ "SEES", "HEARS", "SMELLS", "SWIMS", "AQUATIC", "POISON" ] + "flags": [ "SEES", "HEARS", "SMELLS", "SWIMS", "AQUATIC", "WATER_CAMOUFLAGE", "POISON" ] }, { "//": "not really a 'lizard' as we would understand them, rather an invasive alien, likely a prehistoric creature from another world, like a dinosaur. 'lizard' a survivor colloquialism for their shape and general behavior", diff --git a/data/mods/DinoMod/monsters/hatchling.json b/data/mods/DinoMod/monsters/hatchling.json index e1a5b5f163b9b..029b702f5eb39 100644 --- a/data/mods/DinoMod/monsters/hatchling.json +++ b/data/mods/DinoMod/monsters/hatchling.json @@ -398,7 +398,8 @@ "NO_BREED", "CANPLAY", "SWIMS", - "AQUATIC" + "AQUATIC", + "WATER_CAMOUFLAGE" ] } ] diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index d1ef06194fe2a..4578ee542fbda 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -984,6 +984,7 @@ Other monster flags. - ```VENOM``` Attack may poison the player. - ```VERMIN``` Obsolete flag for inconsequential monsters, now prevents loading. - ```WARM``` Warm blooded. +- ```WATER_CAMOUFLAGE``` If in water, stays invisible up to (current Perception, + base Perception if the character has the Spotting proficiency) tiles away, even in broad daylight. Monsters see it from the lower of `vision_day` and `vision_night` ranges. Can also make it harder to see in deep water or across Z-levels if it is underwater and the viewer is not. - ```WEBWALK``` Doesn't destroy webs and won't get caught in them. - ```WOOL``` May produce wool when butchered. diff --git a/src/creature.cpp b/src/creature.cpp index 9f53b1f7f6bfd..b8d09483c64a5 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -200,6 +200,12 @@ bool Creature::is_underwater() const return underwater; } +bool Creature::is_likely_underwater() const +{ + return is_underwater() || + ( has_flag( MF_AQUATIC ) && get_map().has_flag( ter_furn_flag::TFLAG_SWIMMABLE, pos() ) ); +} + bool Creature::is_ranged_attacker() const { if( has_flag( MF_RANGED_ATTACKER ) ) { @@ -243,6 +249,12 @@ bool Creature::is_dangerous_field( const field_entry &entry ) const return entry.is_dangerous() && !is_immune_field( entry.get_field_type() ); } +static bool majority_rule( const bool a_vote, const bool b_vote, const bool c_vote ) +{ + // Helper function suggested on discord by jbtw + return ( ( a_vote + b_vote + c_vote ) > 1 ); +} + bool Creature::sees( const Creature &critter ) const { // Creatures always see themselves (simplifies drawing). @@ -282,8 +294,17 @@ bool Creature::sees( const Creature &critter ) const } else if( ( wanted_range > 1 && critter.digging() && here.has_flag( ter_furn_flag::TFLAG_DIGGABLE, critter.pos() ) ) || ( critter.has_flag( MF_CAMOUFLAGE ) && wanted_range > this->get_eff_per() ) || + ( critter.has_flag( MF_WATER_CAMOUFLAGE ) && + wanted_range > this->get_eff_per() && + ( critter.is_likely_underwater() || + here.has_flag( ter_furn_flag::TFLAG_DEEP_WATER, critter.pos() ) || + ( here.has_flag( ter_furn_flag::TFLAG_SHALLOW_WATER, critter.pos() ) && + critter.get_size() < creature_size::medium ) ) ) || ( critter.has_flag( MF_NIGHT_INVISIBILITY ) && here.light_at( critter.pos() ) <= lit_level::LOW ) || - ( critter.is_underwater() && !is_underwater() && here.is_divable( critter.pos() ) ) || + ( !is_likely_underwater() && critter.is_likely_underwater() && + majority_rule( critter.has_flag( MF_WATER_CAMOUFLAGE ), + here.has_flag( ter_furn_flag::TFLAG_DEEP_WATER, critter.pos() ), + posz() != critter.posz() ) ) || ( here.has_flag_ter_or_furn( ter_furn_flag::TFLAG_HIDE_PLACE, critter.pos() ) && !( std::abs( posx() - critter.posx() ) <= 1 && std::abs( posy() - critter.posy() ) <= 1 && std::abs( posz() - critter.posz() ) <= 1 ) ) ) { diff --git a/src/creature.h b/src/creature.h index 5d7aed3747c2c..15e97ea783e25 100644 --- a/src/creature.h +++ b/src/creature.h @@ -474,6 +474,7 @@ class Creature : public location, public viewer virtual bool digging() const; virtual bool is_on_ground() const = 0; virtual bool is_underwater() const; + bool is_likely_underwater() const; // Should eventually be virtual, although not pure virtual bool is_warm() const; // is this creature warm, for IR vision, heat drain, etc virtual bool in_species( const species_id & ) const; diff --git a/src/monstergenerator.cpp b/src/monstergenerator.cpp index 6213362657ba9..94df040f2b75d 100644 --- a/src/monstergenerator.cpp +++ b/src/monstergenerator.cpp @@ -209,6 +209,7 @@ std::string enum_to_string( m_flag data ) case MF_INSECTICIDEPROOF: return "INSECTICIDEPROOF"; case MF_RANGED_ATTACKER: return "RANGED_ATTACKER"; case MF_CAMOUFLAGE: return "CAMOUFLAGE"; + case MF_WATER_CAMOUFLAGE: return "WATER_CAMOUFLAGE"; // *INDENT-ON* case m_flag::MF_MAX: break; @@ -1201,7 +1202,7 @@ void mtype::remove_regeneration_modifiers( const JsonObject &jo, const std::stri void MonsterGenerator::check_monster_definitions() const { - for( const auto &mon : mon_templates->get_all() ) { + for( const mtype &mon : mon_templates->get_all() ) { if( mon.harvest.is_null() && !mon.has_flag( MF_ELECTRONIC ) && !mon.id.is_null() ) { debugmsg( "monster %s has no harvest entry", mon.id.c_str(), mon.harvest.c_str() ); } @@ -1258,6 +1259,9 @@ void MonsterGenerator::check_monster_definitions() const if( !mon.harvest.is_valid() ) { debugmsg( "monster %s has invalid harvest_entry: %s", mon.id.c_str(), mon.harvest.c_str() ); } + if( mon.has_flag( MF_WATER_CAMOUFLAGE ) && !monster( mon.id ).can_submerge() ) { + debugmsg( "monster %s has WATER_CAMOUFLAGE but cannot submerge", mon.id.c_str() ); + } for( const scenttype_id &s_id : mon.scents_tracked ) { if( !s_id.is_empty() && !s_id.is_valid() ) { debugmsg( "monster %s has unknown scents_tracked %s", mon.id.c_str(), s_id.c_str() ); diff --git a/src/mtype.h b/src/mtype.h index 47cc9d237d04c..42e3ce2c23f0f 100644 --- a/src/mtype.h +++ b/src/mtype.h @@ -182,6 +182,7 @@ enum m_flag : int { MF_INSECTICIDEPROOF, // This monster is immune to insecticide, even though it's made of bug flesh MF_RANGED_ATTACKER, // This monster has any sort of ranged attack MF_CAMOUFLAGE, // This monster is hard to spot, even in broad daylight + MF_WATER_CAMOUFLAGE, // This monster is hard to spot if it is underwater, especially if you aren't MF_MAX // Sets the length of the flags - obviously must be LAST };