--- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -220,6 +220,7 @@ static const species_id HUMAN( "HUMAN" ); static const species_id ZOMBIE( "ZOMBIE" ); +static const std::string trait_flag_VAMPIRE( "CANNIBAL" ); static const std::string trait_flag_CANNIBAL( "CANNIBAL" ); static const std::string trait_flag_PSYCHOPATH( "PSYCHOPATH" ); static const std::string trait_flag_SAPIOVORE( "SAPIOVORE" ); @@ -259,6 +260,8 @@ static const std::string flag_SUPPORTS_ROOF( "SUPPORTS_ROOF" ); static const std::string flag_TREE( "TREE" ); static const std::string flag_USE_UPS( "USE_UPS" ); +static const std::string flag_VAMPIRISM( "VAMPIRE" ); +static const std::string flag_VAMPIRISM_OK( "VAMPIRISM_OK" ); using namespace activity_handlers; @@ -651,7 +654,8 @@ !corpse.in_species( ZOMBIE ) ); if( is_human && !( u.has_trait_flag( trait_flag_CANNIBAL ) || u.has_trait_flag( trait_flag_PSYCHOPATH ) || - u.has_trait_flag( trait_flag_SAPIOVORE ) ) ) { + u.has_trait_flag( trait_flag_SAPIOVORE ) || + u.has_trait_flag( trait_flag_VAMPIRE ) ) ) { need_confirm( _( "Would you dare desecrate the mortal remains of a fellow human being?" ), butcherable_rating::warn_cannibalism ); } --- a/src/character.cpp +++ b/src/character.cpp @@ -288,9 +288,11 @@ static const trait_id trait_THRESH_INSECT( "THRESH_INSECT" ); static const trait_id trait_THRESH_PLANT( "THRESH_PLANT" ); static const trait_id trait_THRESH_SPIDER( "THRESH_SPIDER" ); +static const trait_id trait_THRESH_VAMP( "THRESH_VAMP" ); static const trait_id trait_TOUGH_FEET( "TOUGH_FEET" ); static const trait_id trait_TRANSPIRATION( "TRANSPIRATION" ); static const trait_id trait_URSINE_EYE( "URSINE_EYE" ); +static const trait_id trait_VAMP_HUNGER( "VAMP_HUNGER" ); static const trait_id trait_VISCOUS( "VISCOUS" ); static const trait_id trait_WATERSLEEP( "WATERSLEEP" ); static const trait_id trait_WEBBED( "WEBBED" ); @@ -4545,6 +4547,7 @@ const bool npc_no_food = is_npc() && get_option( "NO_NPC_FOOD" ); const bool foodless = debug_ls || npc_no_food; const bool mouse = has_trait( trait_NO_THIRST ); + const bool vamp = has_trait( trait_VAMP_HUNGER ); const bool mycus = has_trait( trait_M_DEPENDENT ); const float kcal_per_time = bmr() / ( 12.0f * 24.0f ); const int five_mins = ticks_between( from, to, 5_minutes ); @@ -4568,7 +4571,7 @@ } // Mycus and Metabolic Rehydration makes thirst unnecessary // since water is not limited by intake but by absorption, we can just set thirst to zero - if( mycus || mouse ) { + if( mycus || mouse || vamp ) { set_thirst( 0 ); } } @@ -4985,7 +4988,7 @@ void Character::update_bodytemp( const map &m, weather_manager &weather ) { - if( has_trait( trait_DEBUG_NOTEMP ) ) { + if( has_trait( trait_DEBUG_NOTEMP ) || has_trait( trait_THRESH_VAMP ) ) { temp_cur.fill( BODYTEMP_NORM ); temp_conv.fill( BODYTEMP_NORM ); return; --- a/src/character.h +++ b/src/character.h @@ -163,6 +163,8 @@ allergy_weak, // Cannibalism (unless psycho/cannibal) cannibalism, + // Vampirism (unless psycho/cannibal) + vampirism, // Rotten or not rotten enough (for saprophages) rotten, // Can provoke vomiting if you already feel nauseous. --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -108,6 +108,7 @@ static const trait_id trait_THRESH_LUPINE( "THRESH_LUPINE" ); static const trait_id trait_THRESH_PLANT( "THRESH_PLANT" ); static const trait_id trait_THRESH_URSINE( "THRESH_URSINE" ); +static const trait_id trait_VAMP_HUNGER( "VAMP_HUNGER" ); static const trait_id trait_VEGETARIAN( "VEGETARIAN" ); static const trait_id trait_WATERSLEEP( "WATERSLEEP" ); @@ -114,6 +114,7 @@ static const std::string flag_HIDDEN_HALLU( "HIDDEN_HALLU" ); static const std::string flag_ALLERGEN_EGG( "ALLERGEN_EGG" ); static const std::string flag_ALLERGEN_FRUIT( "ALLERGEN_FRUIT" ); +static const std::string flag_ALLERGEN_HONEY( "ALLERGEN_HONEY" ); static const std::string flag_ALLERGEN_JUNK( "ALLERGEN_JUNK" ); static const std::string flag_ALLERGEN_MEAT( "ALLERGEN_MEAT" ); static const std::string flag_ALLERGEN_MILK( "ALLERGEN_MILK" ); @@ -143,11 +144,17 @@ static const std::string flag_RAW( "RAW" ); static const std::string flag_URSINE_HONEY( "URSINE_HONEY" ); static const std::string flag_USE_EAT_VERB( "USE_EAT_VERB" ); +static const std::string flag_VAMPIRISM( "VAMPIRISM" ); +static const std::string flag_VAMPIRISM_OK( "VAMPIRISM_OK" ); const std::vector carnivore_blacklist {{ flag_ALLERGEN_VEGGY, flag_ALLERGEN_FRUIT, flag_ALLERGEN_WHEAT, flag_ALLERGEN_NUT, } }; +const std::vector vamp_blacklist {{ + flag_ALLERGEN_VEGGY, flag_ALLERGEN_FRUIT, flag_ALLERGEN_WHEAT, flag_ALLERGEN_NUT, flag_ALLERGEN_MEAT, flag_ALLERGEN_EGG, flag_ALLERGEN_JUNK, flag_ALLERGEN_MILK, flag_ALLERGEN_HONEY, + } +}; // This ugly temp array is here because otherwise it goes // std::vector(char*, char*)->vector(InputIterator,InputIterator) or some such const std::array temparray {{flag_ALLERGEN_MEAT, flag_ALLERGEN_EGG}}; @@ -719,6 +726,12 @@ _( "Eww. Inedible plant stuff!" ) ); } + if( has_trait( trait_VAMP_HUNGER ) && nutrition_for( food ) > 0 && + food.has_any_flag( vamp_blacklist ) && !food.has_flag( flag_VAMPIRISM_OK ) ) { + return ret_val::make_failure( edible_rating::inedible_mutation, + _( "Bleh. This isn't blood!" ) ); + } + if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) && food.has_any_flag( herbivore_blacklist ) ) { // Like non-cannibal, but more strict! @@ -767,6 +780,12 @@ edible_rating::cannibalism ); } + if( food.has_flag( flag_VAMPIRISM ) && ( !has_trait_flag( "VAMPIRE" ) && + !has_trait_flag( "CANNIBAL" ) ) ) { + add_consequence( _( "The thought of drinking human blood makes you feel sick." ), + edible_rating::vampirism ); + } + const bool edible = comest->comesttype == comesttype_FOOD || food.has_flag( flag_USE_EAT_VERB ); int food_kcal = compute_effective_nutrients( food ).kcal; @@ -1116,6 +1135,25 @@ } } + if( food.has_flag( flag_VAMPIRISM ) ) { + const bool cannibal = has_trait( trait_CANNIBAL ); + const bool psycho = has_trait( trait_PSYCHOPATH ); + const bool sapiovore = has_trait( trait_SAPIOVORE ); + const bool vamp = has_trait( trait_VAMP_HUNGER ); + if( cannibal ) { + add_msg_if_player( m_good, _( "You indulge your shameful hunger." ) ); + add_morale( MORALE_CANNIBAL, 20, 200 ); + } else if( vamp ) { + add_msg_if_player( m_good, _( "You relish drinking the human blood." ) ); + add_morale( MORALE_VAMPIRE, 30, 300 ); + } else if( psycho || sapiovore ) { + // Nothing - doesn't care enough to print a message + } else { + add_msg_if_player( m_bad, _( "You feel horrible for drinking the blood of a person." ) ); + add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes ); + } + } + // Allergy check for food that is ingested (not gum) if( !food.has_flag( "NO_INGEST" ) ) { const auto allergy = allergy_type( food ); @@ -1123,6 +1161,11 @@ add_msg_if_player( m_bad, _( "Yuck! How can anybody eat this stuff?" ) ); add_morale( allergy, -75, -400, 30_minutes, 24_minutes ); } + if( allergy != MORALE_NULL && has_trait( trait_VAMP_HUNGER ) ) { + add_msg_if_player( m_bad, _( "I can't stomach anything but blood!" ) ); + add_morale( allergy, -75, -400, 30_minutes, 24_minutes ); + vomit(); + } if( food.has_flag( flag_ALLERGEN_JUNK ) ) { if( has_trait( trait_PROJUNK ) ) { add_msg_if_player( m_good, _( "Mmm, junk food." ) ); --- a/src/game.cpp +++ b/src/game.cpp @@ -1008,8 +1008,9 @@ int iInfoLine = 0; if( u.has_amount( "holybook_bible1", 1 ) || u.has_amount( "holybook_bible2", 1 ) || - u.has_amount( "holybook_bible3", 1 ) ) { - if( !( u.has_trait( trait_id( "CANNIBAL" ) ) || u.has_trait( trait_id( "PSYCHOPATH" ) ) ) ) { + u.has_amount( "holybook_bible3", 1 ) || u.has_trait( trait_id( "THRESH_VAMP" ) ) ) { + if( !( u.has_trait( trait_id( "CANNIBAL" ) ) || u.has_trait( trait_id( "PSYCHOPATH" ) ) || + u.has_trait( trait_id( "THRESH_VAMP" ) ) ) ) { vRip.emplace_back( " _______ ___" ); vRip.emplace_back( " < `/ |" ); vRip.emplace_back( " > _ _ (" ); --- a/src/item.cpp +++ b/src/item.cpp @@ -3947,6 +3947,7 @@ case edible_rating::allergy: case edible_rating::allergy_weak: case edible_rating::cannibalism: + case edible_rating::vampirism: ret = c_red; break; case edible_rating::rotten: --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -2219,6 +2219,7 @@ // First allergens: // An item is an allergen even if it has trace amounts of allergenic material std::make_pair( material_id( "hflesh" ), "CANNIBALISM" ), + std::make_pair( material_id( "blood" ), "VAMPIRISM" ), std::make_pair( material_id( "hflesh" ), "ALLERGEN_MEAT" ), std::make_pair( material_id( "iflesh" ), "ALLERGEN_MEAT" ), @@ -2233,10 +2234,13 @@ std::make_pair( material_id( "mushroom" ), "ALLERGEN_VEGGY" ), std::make_pair( material_id( "milk" ), "ALLERGEN_MILK" ), std::make_pair( material_id( "egg" ), "ALLERGEN_EGG" ), + std::make_pair( material_id( "honey" ), "ALLERGEN_HONEY" ), std::make_pair( material_id( "junk" ), "ALLERGEN_JUNK" ), // Not food, but we can keep it here std::make_pair( material_id( "wool" ), "ALLERGEN_WOOL" ), // Now "made of". Those flags should not be passed + std::make_pair( material_id( "blood" ), "VAMPIRISM_OK" ), + std::make_pair( material_id( "blood" ), "CARNIVORE_OK" ), std::make_pair( material_id( "flesh" ), "CARNIVORE_OK" ), std::make_pair( material_id( "hflesh" ), "CARNIVORE_OK" ), std::make_pair( material_id( "iflesh" ), "CARNIVORE_OK" ), --- a/src/iteminfo_query.h +++ b/src/iteminfo_query.h @@ -36,6 +36,7 @@ FOOD_VITAMINS, FOOD_VIT_EFFECTS, FOOD_CANNIBALISM, + FOOD_VAMPIRISM, FOOD_TAINT, FOOD_POISON, FOOD_HALLUCINOGENIC, --- a/src/map_field.cpp +++ b/src/map_field.cpp @@ -88,6 +88,7 @@ static const trait_id trait_M_SKIN3( "M_SKIN3" ); static const trait_id trait_THRESH_MARLOSS( "THRESH_MARLOSS" ); static const trait_id trait_THRESH_MYCUS( "THRESH_MYCUS" ); +static const trait_id trait_THRESH_VAMP( "THRESH_VAMP" ); static const trait_id trait_WEB_WALKER( "WEB_WALKER" ); void map::create_burnproducts( const tripoint &p, const item &fuel, const units::mass &burned_mass ) @@ -1395,7 +1396,8 @@ if( ( cur.get_field_intensity() > 1 || !one_in( 3 ) ) && ( !inside || one_in( 3 ) ) ) { u.add_env_effect( effect_teargas, bp_mouth, 5, 20_seconds ); } - if( cur.get_field_intensity() > 1 && ( !inside || one_in( 3 ) ) ) { + if( !( u.has_trait( trait_THRESH_VAMP ) ) && cur.get_field_intensity() > 1 && ( !inside || + one_in( 3 ) ) ) { u.add_env_effect( effect_blind, bp_eyes, cur.get_field_intensity() * 2, 10_seconds ); } } @@ -1526,8 +1528,9 @@ // The gas won't harm you inside a vehicle. if( !inside ) { // Full body suits protect you from the effects of the gas. - if( !( u.worn_with_flag( flag_GAS_PROOF ) && u.get_env_resist( bodypart_id( "mouth" ) ) >= 15 && - u.get_env_resist( bodypart_id( "eyes" ) ) >= 15 ) ) { + if( !( u.has_trait( trait_THRESH_VAMP ) ) && !( u.worn_with_flag( flag_GAS_PROOF ) && + u.get_env_resist( bodypart_id( "mouth" ) ) >= 15 && + u.get_env_resist( bodypart_id( "eyes" ) ) >= 15 ) ) { const int intensity = cur.get_field_intensity(); bool inhaled = u.add_env_effect( effect_poison, bp_mouth, 5, intensity * 1_minutes ); if( u.has_trait( trait_THRESH_MYCUS ) || u.has_trait( trait_THRESH_MARLOSS ) || --- a/src/memorial_logger.cpp +++ b/src/memorial_logger.cpp @@ -80,6 +80,7 @@ static const trap_str_id tr_snake( "tr_snake" ); static const trap_str_id tr_glass_pit( "tr_glass_pit" ); +static const trait_id trait_THRESH_VAMP( "THRESH_VAMP" ); static const trait_id trait_CANNIBAL( "CANNIBAL" ); static const trait_id trait_PSYCHOPATH( "PSYCHOPATH" ); static const trait_id trait_SAPIOVORE( "SAPIOVORE" ); @@ -486,6 +487,7 @@ character_id ch = e.get( "killer" ); if( ch == g->u.getID() ) { std::string name = e.get( "victim_name" ); + bool vampire = g->u.has_trait( trait_THRESH_VAMP ); bool cannibal = g->u.has_trait( trait_CANNIBAL ); bool psycho = g->u.has_trait( trait_PSYCHOPATH ); if( g->u.has_trait( trait_SAPIOVORE ) ) { @@ -509,6 +511,10 @@ add( pgettext( "memorial_male", "Killed an innocent, %s." ), pgettext( "memorial_female", "Killed an innocent, %s." ), name ); + } else if( vampire ) { + add( pgettext( "memorial_male", "Killed an innocent human, %s." ), + pgettext( "memorial_female", "Killed an innocent human, %s." ), + name ); } else { add( pgettext( "memorial_male", "Killed an innocent person, %s, in cold blood and " --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -143,6 +143,7 @@ static const trait_id trait_TAIL_CATTLE( "TAIL_CATTLE" ); static const trait_id trait_THRESH_MARLOSS( "THRESH_MARLOSS" ); static const trait_id trait_THRESH_MYCUS( "THRESH_MYCUS" ); +static const trait_id trait_THRESH_VAMP( "THRESH_VAMP" ); static const mtype_id mon_ant_acid_larva( "mon_ant_acid_larva" ); static const mtype_id mon_ant_acid_queen( "mon_ant_acid_queen" ); @@ -2758,6 +2759,9 @@ damage_instance dam = damage_instance(); dam.add_damage( DT_STAB, 6, 10, 0.6, 1 ); bool hit = sting_shoot( z, target, dam, range ); + if( target->has_trait( trait_THRESH_VAMP ) ) { + return true; + } if( hit ) { //Add checks if previous NPC/player conditions are removed dynamic_cast( target )->mutate(); --- a/src/morale_types.cpp +++ b/src/morale_types.cpp @@ -138,6 +139,7 @@ const morale_type MORALE_CRAVING_MARLOSS( "morale_craving_marloss" ); const morale_type MORALE_FOOD_BAD( "morale_food_bad" ); const morale_type MORALE_CANNIBAL( "morale_cannibal" ); +const morale_type MORALE_VAMPIRE( "morale_vampire" ); const morale_type MORALE_VEGETARIAN( "morale_vegetarian" ); const morale_type MORALE_MEATARIAN( "morale_meatarian" ); const morale_type MORALE_ANTIFRUIT( "morale_antifruit" ); --- a/src/morale_types.h +++ b/src/morale_types.h @@ -62,6 +62,7 @@ extern const morale_type MORALE_CRAVING_MARLOSS; extern const morale_type MORALE_FOOD_BAD; extern const morale_type MORALE_CANNIBAL; +extern const morale_type MORALE_VAMPIRE; extern const morale_type MORALE_VEGETARIAN; extern const morale_type MORALE_MEATARIAN; extern const morale_type MORALE_ANTIFRUIT; --- a/src/npc.cpp +++ b/src/npc.cpp @@ -116,6 +116,7 @@ static const trait_id trait_SAPIOVORE( "SAPIOVORE" ); static const trait_id trait_SCHIZOPHRENIC( "SCHIZOPHRENIC" ); static const trait_id trait_TERRIFYING( "TERRIFYING" ); +static const trait_id trait_THRESH_VAMP( "THRESH_VAMP" ); static const std::string flag_NPC_SAFE( "NPC_SAFE" ); @@ -2486,12 +2487,15 @@ } if( killer == &g->u && ( !guaranteed_hostile() || hit_by_player ) ) { + bool vampire = g->u.has_trait( trait_THRESH_VAMP ); bool cannibal = g->u.has_trait( trait_CANNIBAL ); bool psycho = g->u.has_trait( trait_PSYCHOPATH ); if( g->u.has_trait( trait_SAPIOVORE ) || psycho ) { // No morale effect } else if( cannibal ) { g->u.add_morale( MORALE_KILLED_INNOCENT, -5, 0, 2_days, 3_hours ); + } else if( vampire ) { + g->u.add_morale( MORALE_KILLED_INNOCENT, -5, 0, 2_days, 3_hours ); } else { g->u.add_morale( MORALE_KILLED_INNOCENT, -100, 0, 2_days, 3_hours ); } --- a/src/player_hardcoded_effects.cpp +++ b/src/player_hardcoded_effects.cpp @@ -114,7 +114,9 @@ static const trait_id trait_NOPAIN( "NOPAIN" ); static const trait_id trait_SEESLEEP( "SEESLEEP" ); static const trait_id trait_SCHIZOPHRENIC( "SCHIZOPHRENIC" ); +static const trait_id trait_THRESH_GYNOID( "THRESH_GYNOID" ); static const trait_id trait_THRESH_MYCUS( "THRESH_MYCUS" ); +static const trait_id trait_THRESH_VAMP( "THRESH_VAMP" ); static const trait_id trait_WATERSLEEP( "WATERSLEEP" ); static void eff_fun_onfire( player &u, effect &it ) @@ -1095,6 +1097,11 @@ // Determine the strength of effects or dreams based upon category strength int strength = 0; // Category too weak for any effect or dream if( crossed_threshold() ) { + if( has_trait( trait_THRESH_VAMP ) ) { + highcat = "VAMP"; + } else if( has_trait( trait_THRESH_GYNOID ) ) { + highcat = "GYNOID"; + } strength = 4; // Post-human. } else if( highest >= 20 && highest < 35 ) { strength = 1; // Low strength --- a/src/suffer.cpp +++ b/src/suffer.cpp @@ -143,10 +143,13 @@ static const trait_id trait_SHOUT3( "SHOUT3" ); static const trait_id trait_SORES( "SORES" ); static const trait_id trait_SUNBURN( "SUNBURN" ); +static const trait_id trait_THRESH_GYNOID( "THRESH_GYNOID" ); +static const trait_id trait_THRESH_VAMP( "THRESH_VAMP" ); static const trait_id trait_TROGLO( "TROGLO" ); static const trait_id trait_TROGLO2( "TROGLO2" ); static const trait_id trait_TROGLO3( "TROGLO3" ); static const trait_id trait_UNSTABLE( "UNSTABLE" ); +static const trait_id trait_VAMP_CURSE( "VAMP_CURSE" ); static const trait_id trait_VOMITOUS( "VOMITOUS" ); static const trait_id trait_WEB_SPINNER( "WEB_SPINNER" ); static const trait_id trait_WEB_WEAVER( "WEB_WEAVER" ); @@ -243,7 +246,8 @@ void Character::suffer_while_underwater() { - if( !has_trait( trait_GILLS ) && !has_trait( trait_GILLS_CEPH ) ) { + if( !has_trait( trait_GILLS ) && !has_trait( trait_GILLS_CEPH ) && + !has_trait( trait_THRESH_GYNOID ) && !has_trait( trait_THRESH_VAMP ) ) { oxygen--; } if( oxygen < 12 && worn_with_flag( "REBREATHER" ) ) { @@ -759,6 +763,17 @@ } } + if( has_trait( trait_VAMP_CURSE ) && one_in( 10 ) ) { + if( !( weapon.has_flag( "RAIN_PROTECT" ) ) ) { + add_msg_if_player( m_bad, _( "The sunlight sears your skin!" ) ); + if( has_effect( effect_sleep ) && !has_effect( effect_narcosis ) ) { + wake_up(); + } + mod_pain( 5 ); + hurtall( 5, nullptr ); + } + } + if( ( has_trait( trait_TROGLO ) || has_trait( trait_TROGLO2 ) ) && g->weather.weather == WEATHER_SUNNY ) { mod_str_bonus( -1 ); --- a/data/json/field_type.json +++ b/data/json/field_type.json @@ -303,7 +303,7 @@ "dirty_transparency_cache": true, "percent_spread": 10, "outdoor_age_speedup": "0 turns", - "immunity_data": { "body_part_env_resistance": [ [ "mouth", 7 ] ] }, + "immunity_data": { "body_part_env_resistance": [ [ "mouth", 7 ] ], "traits": [ "THRESH_VAMP" ] }, "priority": 8, "half_life": "2 minutes", "phase": "gas", @@ -389,7 +389,7 @@ "outdoor_age_speedup": "3 minutes", "dirty_transparency_cache": true, "has_fume": true, - "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ] }, + "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ], "traits": [ "THRESH_VAMP" ] }, "priority": 8, "half_life": "10 minutes", "phase": "gas", @@ -418,7 +418,7 @@ "outdoor_age_speedup": "0 turns", "dirty_transparency_cache": true, "has_fume": true, - "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ] }, + "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ], [ "eyes", 15 ] ], "traits": [ "THRESH_VAMP" ] }, "priority": 8, "half_life": "5 minutes", "phase": "gas", @@ -464,7 +464,7 @@ "wandering_field": "fd_toxic_gas", "gas_absorption_factor": 15, "dirty_transparency_cache": true, - "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ] }, + "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ], "traits": [ "THRESH_VAMP" ] }, "phase": "gas", "display_items": false, "display_field": true, @@ -956,7 +956,7 @@ "outdoor_age_speedup": "5 minutes", "dirty_transparency_cache": true, "has_fume": true, - "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ] }, + "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ], "traits": [ "THRESH_VAMP" ] }, "priority": 8, "half_life": "50 minutes", "phase": "gas", @@ -978,7 +978,7 @@ "outdoor_age_speedup": "1 minutes", "dirty_transparency_cache": true, "has_fume": true, - "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ] }, + "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ], "traits": [ "THRESH_VAMP" ] }, "priority": 8, "half_life": "15 minutes", "phase": "gas", @@ -1233,7 +1233,7 @@ "outdoor_age_speedup": "3 minutes", "dirty_transparency_cache": true, "has_fume": true, - "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ] }, + "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ], "traits": [ "THRESH_VAMP" ] }, "priority": 8, "half_life": "10 minutes", "phase": "gas" @@ -1253,7 +1253,7 @@ "outdoor_age_speedup": "1 minutes", "dirty_transparency_cache": true, "has_fume": true, - "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ] }, + "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ], "traits": [ "THRESH_VAMP" ] }, "priority": 8, "half_life": "30 minutes", "phase": "gas", @@ -1275,7 +1275,7 @@ "outdoor_age_speedup": "1 minutes", "dirty_transparency_cache": true, "has_fume": true, - "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ] }, + "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ], "traits": [ "THRESH_VAMP" ] }, "priority": 8, "half_life": "30 minutes", "phase": "gas", --- a/data/json/flags.json +++ b/data/json/flags.json @@ -1093,6 +1093,11 @@ "context": [ "COMESTIBLE" ] }, { + "id": "ALLERGEN_HONEY", + "type": "json_flag", + "context": [ "COMESTIBLE" ] + }, + { "id": "ALLERGEN_JUNK", "type": "json_flag", "context": [ "COMESTIBLE" ] @@ -1173,6 +1173,16 @@ "context": [ ] }, { + "id": "VAMPIRISM", + "type": "json_flag", + "context": [ ] + }, + { + "id": "VAMPIRISM_OK", + "type": "json_flag", + "context": [ ] + }, + { "id": "CASING", "type": "json_flag", "context": [ ] --- a/data/json/morale_types.json +++ b/data/json/morale_types.json @@ -125,6 +125,11 @@ "text": "Ate Human Flesh" }, { + "id": "morale_vampire", + "type": "morale_type", + "text": "Drank Human Blood" + }, + { "id": "morale_vegetarian", "type": "morale_type", "text": "Ate Meat" --- a/data/json/mutations/mutation_ordering.json +++ b/data/json/mutations/mutation_ordering.json @@ -73,6 +73,7 @@ "TROGLO2", "TROGLO3", "URSINE_FUR", + "VAMP_SKIN", "VISCOUS" ], "order": 1500