diff --git a/data/json/effects.json b/data/json/effects.json index cfbc1884949a..aab27d0a2868 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -1193,7 +1193,14 @@ }, { "type": "effect_type", - "id": "attention" + "id": "attention", + "name": [ "Being Watched" ], + "desc": [ "You can't shake the feeling that something out there is watching you. Something big…" ], + "remove_message": "You no longer feel like you're being watched.", + "rating": "bad", + "max_intensity": 8, + "int_add_val": 1, + "max_duration": "12 h" }, { "type": "effect_type", @@ -1313,7 +1320,14 @@ }, { "type": "effect_type", - "id": "teleglow" + "id": "teleglow", + "name": [ "Dimensional Stress" ], + "desc": [ "The fabric of reality currently feels thinner around you." ], + "remove_message": "Reality around you returns to normal, for now.", + "rating": "bad", + "max_intensity": 8, + "int_add_val": 1, + "max_duration": "12 h" }, { "type": "effect_type", diff --git a/data/json/snippets/snippets.json b/data/json/snippets/snippets.json index aa1e2ea94c9e..c23d99e3371f 100644 --- a/data/json/snippets/snippets.json +++ b/data/json/snippets/snippets.json @@ -192,5 +192,22 @@ "text": "This is General Baker. Today I received a top-secret letter with new orders from high command. These orders described a new set of coordinates for our ICBM. My men deciphered it, and it became crystal-clear that coordinates are pointing to a certain place inside our country. I requested re-confirmation, and after a short while I received it with the same coordinates, so it wasn't an weird error as I originally thought. I don't know what's on the minds of these guys in the government, but I'm not gonna bomb innocent people of my country. We're just one step away from starting a war, so failure to comply with an order will obviously result in a execution. And it's just a matter of time when they come for me. So actually I'm already a dead man. Whomever may find this. Please tell my wife Jane I love her and Michael Jr. And tell her I'm sorry. General Michael Baker" } ] + }, + { + "type": "snippet", + "category": "nether_attention_watching", + "text": [ + "You feel the weight of your actions on your shoulders.", + "Everything just feels so futile for some reason…", + "For a brief moment, everything just feels WRONG.", + "You are being watched by someone. No, some THING…", + "The whole world seems like just a speck of dust drifting in an infinite void.", + "There's a whole world inside your head, and they want in.", + "You feel empty inside. But that emptiness seems more alive and wondrous than anything that lives.", + "The infinite nothingness out there is inviting you in. You can hear it inside you.", + "Your brain feels like it's squirming inside your skull, like something's straining to get out.", + "The air seems to shift, like something massive just reacted to your presence.", + "Insect. That's what it feels like, like a bug under the magnifying glass of something out there." + ] } ] diff --git a/src/character.cpp b/src/character.cpp index be1bd3f33830..0c8c71393c57 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10593,3 +10593,9 @@ bool Character::can_learn_by_disassembly( const recipe &rec ) const return !rec.learn_by_disassembly.empty() && meets_skill_requirements( rec.learn_by_disassembly ); } + +bool has_psy_protection( const Character &c, int partial_chance ) +{ + return c.has_artifact_with( AEP_PSYSHIELD ) || + ( c.worn_with_flag( "PSYSHIELD_PARTIAL" ) && one_in( partial_chance ) ); +} \ No newline at end of file diff --git a/src/character.h b/src/character.h index 94b511558ae9..862664e98ea2 100644 --- a/src/character.h +++ b/src/character.h @@ -2244,4 +2244,7 @@ std::map wind_resistance_from_clothing( const std::map> &clothing_map ); } // namespace warmth +/** Returns true if the player has a psyshield artifact, or sometimes if wearing tinfoil */ +bool has_psy_protection( const Character &c, int partial_chance ); + #endif // CATA_SRC_CHARACTER_H diff --git a/src/monattack.cpp b/src/monattack.cpp index d50e6c7976df..5d39c68c3ee4 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -87,6 +87,7 @@ static const ammo_effect_str_id ammo_effect_APPLY_SAP( "APPLY_SAP" ); static const efftype_id effect_ai_controlled( "ai_controlled" ); static const efftype_id effect_assisted( "assisted" ); +static const efftype_id effect_attention( "attention" ); static const efftype_id effect_bite( "bite" ); static const efftype_id effect_bleed( "bleed" ); static const efftype_id effect_blind( "blind" ); @@ -120,7 +121,6 @@ static const efftype_id effect_shrieking( "shrieking" ); static const efftype_id effect_slimed( "slimed" ); static const efftype_id effect_stunned( "stunned" ); static const efftype_id effect_targeted( "targeted" ); -static const efftype_id effect_teleglow( "teleglow" ); static const efftype_id effect_under_op( "under_operation" ); static const itype_id itype_ant_egg( "ant_egg" ); @@ -2885,7 +2885,7 @@ bool mattack::stare( monster *z ) } else { add_msg( m_bad, _( "You feel like you're being watched, it makes you sick." ) ); } - g->u.add_effect( effect_teleglow, 80_minutes ); + g->u.add_effect( effect_attention, 80_minutes ); } return true; @@ -2903,8 +2903,7 @@ bool mattack::fear_paralyze( monster *z ) } if( g->u.sees( *z ) && !g->u.has_effect( effect_fearparalyze ) ) { - if( g->u.has_artifact_with( AEP_PSYSHIELD ) || ( g->u.worn_with_flag( "PSYSHIELD_PARTIAL" ) && - one_in( 4 ) ) ) { + if( has_psy_protection( get_player_character(), 4 ) ) { add_msg( _( "The %s probes your mind, but is rebuffed!" ), z->name() ); ///\EFFECT_INT decreases chance of being paralyzed by fear attack } else if( rng( 0, 20 ) > g->u.get_int() ) { diff --git a/src/player_hardcoded_effects.cpp b/src/player_hardcoded_effects.cpp index c85a18b76a79..b98adea95e23 100644 --- a/src/player_hardcoded_effects.cpp +++ b/src/player_hardcoded_effects.cpp @@ -6,6 +6,7 @@ #include "activity_handlers.h" #include "avatar.h" +#include "character.h" #include "damage.h" #include "effect.h" #include "enums.h" @@ -19,6 +20,7 @@ #include "mapdata.h" #include "martialarts.h" #include "messages.h" +#include "morale_types.h" #include "mongroup.h" #include "monster.h" #include "mutation_data.h" @@ -30,6 +32,7 @@ #include "stomach.h" #include "string_formatter.h" #include "teleport.h" +#include "text_snippets.h" #include "translations.h" #include "weather.h" #include "vitamin.h" @@ -53,10 +56,12 @@ static const efftype_id effect_boomered( "boomered" ); static const efftype_id effect_brainworms( "brainworms" ); static const efftype_id effect_cold( "cold" ); static const efftype_id effect_datura( "datura" ); +static const efftype_id effect_dazed( "dazed" ); static const efftype_id effect_dermatik( "dermatik" ); static const efftype_id effect_disabled( "disabled" ); static const efftype_id effect_downed( "downed" ); static const efftype_id effect_evil( "evil" ); +static const efftype_id effect_fearparalyze( "fearparalyze" ); static const efftype_id effect_formication( "formication" ); static const efftype_id effect_frostbite( "frostbite" ); static const efftype_id effect_fungus( "fungus" ); @@ -75,7 +80,6 @@ static const efftype_id effect_paincysts( "paincysts" ); static const efftype_id effect_panacea( "panacea" ); static const efftype_id effect_rat( "rat" ); static const efftype_id effect_recover( "recover" ); -static const efftype_id effect_shakes( "shakes" ); static const efftype_id effect_sleep( "sleep" ); static const efftype_id effect_slept_through_alarm( "slept_through_alarm" ); static const efftype_id effect_spores( "spores" ); @@ -83,7 +87,6 @@ static const efftype_id effect_strong_antibiotic( "strong_antibiotic" ); static const efftype_id effect_tapeworm( "tapeworm" ); static const efftype_id effect_teleglow( "teleglow" ); static const efftype_id effect_toxin_buildup( "toxin_buildup" ); -static const efftype_id effect_valium( "valium" ); static const efftype_id effect_visuals( "visuals" ); static const efftype_id effect_weak_antibiotic( "weak_antibiotic" ); @@ -586,42 +589,98 @@ void player::hardcoded_effects( effect &it ) mod_per_bonus( -( dur > 400_minutes ? 10.0 : dur / 40_minutes ) ); } } else if( id == effect_attention ) { - if( to_turns( dur ) != 0 && one_in( 100000 / to_turns( dur ) ) && - one_in( 100000 / to_turns( dur ) ) && one_in( 250 ) ) { - tripoint dest( 0, 0, posz() ); - int tries = 0; - do { - dest.x = posx() + rng( -4, 4 ); - dest.y = posy() + rng( -4, 4 ); - tries++; - } while( g->critter_at( dest ) && tries < 10 ); - if( tries < 10 ) { - if( g->m.impassable( dest ) ) { - g->m.make_rubble( dest, f_rubble_rock, true ); + if( intense > 6 ) { + if( one_in( 7200 - ( intense * 450 ) ) ) { + add_msg_if_player( m_bad, + _( "You feel something reaching out to you, before reality around you frays!" ) ); + if( has_psy_protection( *this, 10 ) ) { + // Transfers half of remaining duration of nether attention, tinfoil only sometimes helps + add_effect( effect_teleglow, ( dur / 2 ), num_bp, ( intense / 2 ) ); + } else { + // Transfers all remaining duration of nether attention to dimensional instability + add_effect( effect_teleglow, dur, num_bp, intense ); + } + it.set_duration( 0_turns ); + } + if( one_in( 8000 - ( intense * 500 ) ) && one_in( 2 ) ) { + if( !is_npc() ) { + add_msg( m_bad, _( "You pass out from the strain of something bearing down on your mind." ) ); } - MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup( - GROUP_NETHER ); - g->place_critter_at( spawn_details.name, dest ); - if( g->u.sees( dest ) ) { - g->cancel_activity_or_ignore_query( distraction_type::hostile_spotted_far, - _( "A monster appears nearby!" ) ); - add_msg_if_player( m_warning, _( "A portal opens nearby, and a monster crawls through!" ) ); + fall_asleep( 2_hours ); + if( one_in( 10 ) ) { + it.set_duration( 0_turns ); } - it.mult_duration( .25 ); + it.mod_duration( -20_minutes * intense ); + it.mod_intensity( -1 ); + } + } + if( intense > 4 ) { + if( one_in( 6000 - ( intense * 375 ) ) ) { + if( has_psy_protection( *this, 4 ) ) { + add_msg_if_player( m_bad, _( "You feel something probing your mind, but it is rebuffed!" ) ); + } else { + add_msg_if_player( m_bad, _( "A terrifying image in the back out your mind paralyzes you." ) ); + add_effect( effect_fearparalyze, 5_turns ); + moves -= 4 * get_speed(); + } + it.mod_duration( -10_minutes * intense ); + if( one_in( 2 ) ) { + it.mod_intensity( -1 ); + } + } + if( one_turn_in( 1200_minutes - ( intense * 90_minutes ) ) ) { + if( has_psy_protection( *this, 4 ) ) { + add_msg_if_player( m_bad, _( "You feel a buzzing in the back of your mind, but it passes." ) ); + } else { + add_msg_if_player( m_bad, _( "You feel something scream in the back of your mind!" ) ); + add_effect( effect_dazed, rng( 1_minutes, 2_minutes ) ); + } + it.mod_duration( -10_minutes * intense ); + if( one_in( 3 ) ) { + it.mod_intensity( -1 ); + } + } + } + if( intense > 2 ) { + if( one_turn_in( 1200_minutes - ( intense * 90_minutes ) ) ) { + add_msg_if_player( m_bad, _( "Your vision is filled with bright lights…" ) ); + add_effect( effect_blind, rng( 1_minutes, 2_minutes ) ); + it.mod_duration( -10_minutes * intense ); + if( one_in( 4 ) ) { + it.mod_intensity( -1 ); + } + } + if( one_in( 5000 ) && !has_effect( effect_nausea ) ) { + add_msg_if_player( m_bad, _( "A wave of nausea passes over you." ) ); + add_effect( effect_nausea, 5_minutes ); + } + } + if( one_in( 5000 ) && !has_effect( effect_hallu ) ) { + add_msg_if_player( m_bad, _( "Shifting shapes dance on the edge of your vision." ) ); + add_effect( effect_hallu, 4_hours ); + it.mod_duration( -10_minutes * intense ); + } + if( one_turn_in( 40_minutes ) ) { + if( has_psy_protection( *this, 4 ) ) { + add_msg_if_player( m_bad, _( "You feel weird for a moment, but it passes." ) ); + } else { + // Less morale drop and faster decay than Psychosis negative messages, but more frequent + const translation snip = SNIPPET.random_from_category( "nether_attention_watching" ).value_or( + translation() ); + add_msg_if_player( m_warning, "%s", snip ); + add_morale( MORALE_FEELING_BAD, -10, -50, 60_minutes, 20_minutes, true ); } } } else if( id == effect_teleglow ) { - // Default we get around 300 duration points per teleport (possibly more - // depending on the source). + // Each teleportation increases intensity by 1, 2 intensities per tier of effect. // TODO: Include a chance to teleport to the nether realm. // TODO: This with regards to NPCS if( !is_player() ) { // NO, no teleporting around the player because an NPC has teleglow! return; } - if( dur > 10_hours ) { - // 20 teleports (no decay; in practice at least 21) - if( one_in( 6000 - ( ( dur - 600_minutes ) / 1_minutes ) ) ) { + if( intense > 6 ) { + if( one_in( 6000 - ( intense * 250 ) ) ) { if( !is_npc() ) { add_msg( _( "Glowing lights surround you, and you teleport." ) ); } @@ -631,8 +690,11 @@ void player::hardcoded_effects( effect &it ) // Set ourselves up for removal it.set_duration( 0_turns ); } + // Since teleporting grants 1 intensity and 30 minutes duration, + // if it doesn't remove it'll get more intense but shorter. + it.mod_duration( -20_minutes * intense ); } - if( one_in( 7200 - ( dur - 360_minutes ) / 4_turns ) ) { + if( one_in( 7200 - ( intense * 250 ) ) ) { add_msg_if_player( m_bad, _( "You are beset with a vision of a prowling beast." ) ); for( const tripoint &dest : g->m.points_in_radius( pos(), 6 ) ) { if( g->m.is_cornerfloor( dest ) ) { @@ -645,21 +707,12 @@ void player::hardcoded_effects( effect &it ) // Set ourselves up for removal it.set_duration( 0_turns ); } - } - if( one_in( 7200 - ( ( dur - 600_minutes ) / 30_seconds ) ) && one_in( 20 ) ) { - if( !is_npc() ) { - add_msg( m_bad, _( "You pass out." ) ); - } - fall_asleep( 2_hours ); - if( one_in( 6 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); - } + it.mod_intensity( -1 ); } } - if( dur > 6_hours ) { - // 12 teleports - if( one_in( 24000 - ( dur - 360_minutes ) / 4_turns ) ) { + if( intense > 4 ) { + // Once every 4 hours baseline, once every 2 hours max + if( one_turn_in( 14_hours - ( intense * 90_minutes ) ) ) { tripoint dest( 0, 0, posz() ); int &x = dest.x; int &y = dest.y; @@ -684,58 +737,58 @@ void player::hardcoded_effects( effect &it ) _( "A monster appears nearby!" ) ); add_msg( m_warning, _( "A portal opens nearby, and a monster crawls through!" ) ); } - if( one_in( 2 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); - } + it.mod_duration( -10_minutes * intense ); + it.mod_intensity( -1 ); } } - if( one_in( 21000 - ( dur - 360_minutes ) / 4_turns ) ) { + if( one_in( 21000 - ( intense * 1125 ) ) ) { add_msg_if_player( m_bad, _( "You shudder suddenly." ) ); mutate(); - if( one_in( 4 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); + it.mod_duration( -10_minutes * intense ); + if( one_in( 2 ) ) { + it.mod_intensity( -1 ); } } } - if( dur > 4_hours ) { - // 8 teleports - if( one_turn_in( 1000_minutes - dur ) && !has_effect( effect_valium ) ) { - add_effect( effect_shakes, rng( 4_minutes, 8_minutes ) ); - } - if( one_turn_in( 1200_minutes - dur ) ) { - add_msg_if_player( m_bad, _( "Your vision is filled with bright lights…" ) ); - add_effect( effect_blind, rng( 1_minutes, 2_minutes ) ); - if( one_in( 8 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); + if( intense > 2 ) { + if( one_in( 10000 ) ) { + if( !has_trait( trait_M_IMMUNE ) ) { + add_effect( effect_fungus, 1_turns, num_bp ); + add_msg_if_player( m_bad, _( "You smell mold, and your skin itches." ) ); + } else { + add_msg_if_player( m_info, _( "We have many colonists awaiting passage." ) ); } + // Set ourselves up for removal + it.set_duration( 0_turns ); } - if( one_in( 5000 ) && !has_effect( effect_hallu ) ) { - add_effect( effect_hallu, 6_hours ); - if( one_in( 5 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); + if( one_in( 5000 ) ) { + // Like with the glow anomaly trap, but lower max and bypasses radsuits + add_msg_if_player( m_bad, _( "A blue flash of radiation permeates your vision briefly!" ) ); + irradiate( rng( 10, 20 ), true ); + it.mod_duration( -10_minutes * intense ); + if( one_in( 4 ) ) { + it.mod_intensity( -1 ); } } } if( one_in( 4000 ) ) { add_msg_if_player( m_bad, _( "You're suddenly covered in ectoplasm." ) ); add_effect( effect_boomered, 10_minutes ); - if( one_in( 4 ) ) { - // Set ourselves up for removal - it.set_duration( 0_turns ); - } - } - if( one_in( 10000 ) ) { - if( !has_trait( trait_M_IMMUNE ) ) { - add_effect( effect_fungus, 1_turns, num_bp ); + it.mod_duration( -10_minutes * intense ); + } + if( one_in( 5000 ) ) { + add_msg_if_player( m_bad, _( "A strange sound reverberates around the edges of reality." ) ); + // Comparable to the humming anomaly trap, with a narrower range + int volume = rng( 25, 150 ); + std::string sfx; + if( volume <= 50 ) { + sfx = _( "hrmmm" ); + } else if( volume <= 100 ) { + sfx = _( "HRMMM" ); } else { - add_msg_if_player( m_info, _( "We have many colonists awaiting passage." ) ); + sfx = _( "VRMMMMMM" ); } - // Set ourselves up for removal - it.set_duration( 0_turns ); + sounds::sound( pos(), volume, sounds::sound_t::activity, sfx, false, "humming", "machinery" ); } } else if( id == effect_asthma ) { if( has_effect( effect_adrenaline ) || has_effect( effect_datura ) ) {