1 #include "player.h" // IWYU pragma: associated
2 
3 #include <array>
4 #include <cstdlib>
5 #include <memory>
6 #include <string>
7 
8 #include "activity_handlers.h"
9 #include "activity_type.h"
10 #include "bodypart.h"
11 #include "calendar.h"
12 #include "character.h"
13 #include "damage.h"
14 #include "effect.h"
15 #include "enums.h"
16 #include "event.h"
17 #include "event_bus.h"
18 #include "field_type.h"
19 #include "fungal_effects.h"
20 #include "game.h"
21 #include "make_static.h"
22 #include "map.h"
23 #include "map_iterator.h"
24 #include "mapdata.h"
25 #include "martialarts.h"
26 #include "messages.h"
27 #include "mongroup.h"
28 #include "monster.h"
29 #include "player_activity.h"
30 #include "rng.h"
31 #include "sounds.h"
32 #include "stomach.h"
33 #include "string_formatter.h"
34 #include "teleport.h"
35 #include "translations.h"
36 #include "units.h"
37 #include "vitamin.h"
38 #include "weather.h"
39 #include "weather_type.h"
40 
41 #if defined(TILES)
42 #   if defined(_MSC_VER) && defined(USE_VCPKG)
43 #       include <SDL2/SDL.h>
44 #   else
45 #       include <SDL.h>
46 #   endif
47 #endif // TILES
48 
49 #include <algorithm>
50 #include <functional>
51 
52 static const activity_id ACT_FIRSTAID( "ACT_FIRSTAID" );
53 
54 static const efftype_id effect_adrenaline( "adrenaline" );
55 static const efftype_id effect_alarm_clock( "alarm_clock" );
56 static const efftype_id effect_antibiotic( "antibiotic" );
57 static const efftype_id effect_anemia( "anemia" );
58 static const efftype_id effect_antifungal( "antifungal" );
59 static const efftype_id effect_asthma( "asthma" );
60 static const efftype_id effect_attention( "attention" );
61 static const efftype_id effect_bite( "bite" );
62 static const efftype_id effect_bleed( "bleed" );
63 static const efftype_id effect_blind( "blind" );
64 static const efftype_id effect_bloodworms( "bloodworms" );
65 static const efftype_id effect_boomered( "boomered" );
66 static const efftype_id effect_brainworms( "brainworms" );
67 static const efftype_id effect_cold( "cold" );
68 static const efftype_id effect_datura( "datura" );
69 static const efftype_id effect_dermatik( "dermatik" );
70 static const efftype_id effect_disabled( "disabled" );
71 static const efftype_id effect_downed( "downed" );
72 static const efftype_id effect_evil( "evil" );
73 static const efftype_id effect_formication( "formication" );
74 static const efftype_id effect_frostbite( "frostbite" );
75 static const efftype_id effect_fungus( "fungus" );
76 static const efftype_id effect_grabbed( "grabbed" );
77 static const efftype_id effect_grabbing( "grabbing" );
78 static const efftype_id effect_hallu( "hallu" );
79 static const efftype_id effect_hot( "hot" );
80 static const efftype_id effect_hypovolemia( "hypovolemia" );
81 static const efftype_id effect_infected( "infected" );
82 static const efftype_id effect_lying_down( "lying_down" );
83 static const efftype_id effect_mending( "mending" );
84 static const efftype_id effect_meth( "meth" );
85 static const efftype_id effect_motor_seizure( "motor_seizure" );
86 static const efftype_id effect_narcosis( "narcosis" );
87 static const efftype_id effect_onfire( "onfire" );
88 static const efftype_id effect_paincysts( "paincysts" );
89 static const efftype_id effect_panacea( "panacea" );
90 static const efftype_id effect_rat( "rat" );
91 static const efftype_id effect_recover( "recover" );
92 static const efftype_id effect_redcells_anemia( "redcells_anemia" );
93 static const efftype_id effect_shakes( "shakes" );
94 static const efftype_id effect_sleep( "sleep" );
95 static const efftype_id effect_slept_through_alarm( "slept_through_alarm" );
96 static const efftype_id effect_spores( "spores" );
97 static const efftype_id effect_strong_antibiotic( "strong_antibiotic" );
98 static const efftype_id effect_stunned( "stunned" );
99 static const efftype_id effect_tapeworm( "tapeworm" );
100 static const efftype_id effect_teleglow( "teleglow" );
101 static const efftype_id effect_tindrift( "tindrift" );
102 static const efftype_id effect_tetanus( "tetanus" );
103 static const efftype_id effect_toxin_buildup( "toxin_buildup" );
104 static const efftype_id effect_valium( "valium" );
105 static const efftype_id effect_visuals( "visuals" );
106 static const efftype_id effect_weak_antibiotic( "weak_antibiotic" );
107 static const efftype_id effect_winded( "winded" );
108 
109 static const vitamin_id vitamin_blood( "blood" );
110 static const vitamin_id vitamin_redcells( "redcells" );
111 
112 static const mongroup_id GROUP_NETHER( "GROUP_NETHER" );
113 
114 static const mtype_id mon_dermatik_larva( "mon_dermatik_larva" );
115 
116 static const trait_id trait_CHLOROMORPH( "CHLOROMORPH" );
117 static const trait_id trait_HEAVYSLEEPER( "HEAVYSLEEPER" );
118 static const trait_id trait_HEAVYSLEEPER2( "HEAVYSLEEPER2" );
119 static const trait_id trait_HIBERNATE( "HIBERNATE" );
120 static const trait_id trait_INFRESIST( "INFRESIST" );
121 static const trait_id trait_M_IMMUNE( "M_IMMUNE" );
122 static const trait_id trait_M_SKIN3( "M_SKIN3" );
123 static const trait_id trait_NOPAIN( "NOPAIN" );
124 static const trait_id trait_SEESLEEP( "SEESLEEP" );
125 static const trait_id trait_SCHIZOPHRENIC( "SCHIZOPHRENIC" );
126 static const trait_id trait_THRESH_MYCUS( "THRESH_MYCUS" );
127 static const trait_id trait_WATERSLEEP( "WATERSLEEP" );
128 
129 static const json_character_flag json_flag_ALARMCLOCK( "ALARMCLOCK" );
130 
eff_fun_onfire(Character & u,effect & it)131 static void eff_fun_onfire( Character &u, effect &it )
132 {
133     const int intense = it.get_intensity();
134     u.deal_damage( nullptr, it.get_bp(), damage_instance( damage_type::HEAT, rng( intense,
135                    intense * 2 ) ) );
136 }
eff_fun_spores(Character & u,effect & it)137 static void eff_fun_spores( Character &u, effect &it )
138 {
139     // Equivalent to X in 150000 + health * 100
140     const int intense = it.get_intensity();
141     if( ( !u.has_trait( trait_M_IMMUNE ) ) && ( one_in( 100 ) &&
142             x_in_y( intense, 900 + u.get_healthy() * 0.6 ) ) ) {
143         u.add_effect( effect_fungus, 1_turns, true );
144     }
145 }
eff_fun_antifungal(Character & u,effect &)146 static void eff_fun_antifungal( Character &u, effect & )
147 {
148     // antifungal drugs are deadly poison for marloss people
149     if( u.has_trait( trait_THRESH_MYCUS ) && one_in( 30 ) ) {
150         if( one_in( 10 ) ) {
151             u.add_msg_player_or_npc( m_bad, _( "Something burns you from the inside." ),
152                                      _( "<npcname> shivers from pain." ) );
153         }
154         u.mod_pain( 1 );
155         // not using u.get_random_body_part() as it is weighted & not fully random
156         std::vector<bodypart_id> bparts = u.get_all_body_parts( get_body_part_flags::only_main );
157         bodypart_id random_bpart = bparts[ rng( 0, bparts.size() - 1 ) ];
158         u.apply_damage( nullptr, random_bpart, 1 );
159     }
160 }
eff_fun_fungus(Character & u,effect & it)161 static void eff_fun_fungus( Character &u, effect &it )
162 {
163     const int intense = it.get_intensity();
164     const bool resists = u.resists_effect( it );
165     const int bonus = u.get_healthy() / 10 + ( resists ? 100 : 0 );
166 
167     // clock the progress
168     // hard reverse the clock if you resist fungus
169     if( resists ) {
170         it.mod_duration( -5_turns );
171     } else {
172         it.mod_duration( 1_turns );
173     }
174 
175     switch( intense ) {
176         case 1:
177             // 0-6 hours symptoms
178             if( one_in( 960 + bonus * 6 ) ) {
179                 u.cough( true );
180             }
181             if( one_in( 600 + bonus * 6 ) ) {
182                 u.add_msg_if_player( m_warning, _( "You feel nauseous." ) );
183             }
184             if( one_in( 600 + bonus * 6 ) ) {
185                 u.add_msg_if_player( m_warning, _( "You smell and taste mushrooms." ) );
186             }
187             break;
188         case 2:
189             // 6-12 hours of worse symptoms
190             if( one_in( 3600 + bonus * 18 ) ) {
191                 u.add_msg_if_player( m_bad,  _( "You spasm suddenly!" ) );
192                 u.moves -= 100;
193                 u.apply_damage( nullptr, bodypart_id( "torso" ), resists ? rng( 1, 5 ) : 5 );
194             }
195             if( x_in_y( u.vomit_mod(), ( 4800 + bonus * 24 ) ) || one_in( 12000 + bonus * 60 ) ) {
196                 u.add_msg_player_or_npc( m_bad, _( "You vomit a thick, gray goop." ),
197                                          _( "<npcname> vomits a thick, gray goop." ) );
198 
199                 const int awfulness = rng( 0, resists ? rng( 1, 70 ) : 70 );
200                 u.moves = -200;
201                 u.mod_hunger( awfulness );
202                 u.mod_thirst( awfulness );
203                 ///\EFFECT_STR decreases damage taken by fungus effect
204                 u.apply_damage( nullptr, bodypart_id( "torso" ), awfulness / std::max( u.str_cur,
205                                 1 ) ); // can't be healthy
206             }
207             break;
208         case 3:
209             // Permanent symptoms, 12+ hours
210             if( one_in( 6000 + bonus * 48 ) ) {
211                 u.add_msg_player_or_npc( m_bad,  _( "You vomit thousands of live spores!" ),
212                                          _( "<npcname> vomits thousands of live spores!" ) );
213 
214                 u.moves = -500;
215                 map &here = get_map();
216                 fungal_effects fe( *g, here );
217                 for( const tripoint &sporep : here.points_in_radius( u.pos(), 1 ) ) {
218                     if( sporep == u.pos() ) {
219                         continue;
220                     }
221                     fe.fungalize( sporep, &u, 0.25 );
222                 }
223                 // We're fucked
224             } else if( one_in( 36000 + bonus * 240 ) ) {
225                 if( u.is_limb_broken( bodypart_id( "arm_l" ) ) || u.is_limb_broken( bodypart_id( "arm_r" ) ) ) {
226                     if( u.is_limb_broken( bodypart_id( "arm_l" ) ) && u.is_limb_broken( bodypart_id( "arm_r" ) ) ) {
227                         u.add_msg_player_or_npc( m_bad,
228                                                  _( "The flesh on your broken arms bulges.  Fungus stalks burst through!" ),
229                                                  _( "<npcname>'s broken arms bulge.  Fungus stalks burst out of the bulges!" ) );
230                     } else {
231                         u.add_msg_player_or_npc( m_bad,
232                                                  _( "The flesh on your broken and unbroken arms bulge.  Fungus stalks burst through!" ),
233                                                  _( "<npcname>'s arms bulge.  Fungus stalks burst out of the bulges!" ) );
234                     }
235                 } else {
236                     u.add_msg_player_or_npc( m_bad, _( "Your hands bulge.  Fungus stalks burst through the bulge!" ),
237                                              _( "<npcname>'s hands bulge.  Fungus stalks burst through the bulge!" ) );
238                 }
239                 u.apply_damage( nullptr, bodypart_id( "arm_l" ), 999 );
240                 u.apply_damage( nullptr, bodypart_id( "arm_r" ), 999 );
241             }
242             break;
243     }
244 }
eff_fun_rat(Character & u,effect & it)245 static void eff_fun_rat( Character &u, effect &it )
246 {
247     const int dur = to_turns<int>( it.get_duration() );
248     it.set_intensity( dur / 10 );
249     if( rng( 0, 100 ) < dur / 10 ) {
250         if( !one_in( 5 ) ) {
251             u.mutate_category( mutation_category_id( "RAT" ) );
252             it.mult_duration( .2 );
253         } else {
254             u.mutate_category( mutation_category_id( "TROGLOBITE" ) );
255             it.mult_duration( .33 );
256         }
257     } else if( rng( 0, 100 ) < dur / 8 ) {
258         if( one_in( 3 ) ) {
259             u.vomit();
260             it.mod_duration( -1_minutes );
261         } else {
262             u.add_msg_if_player( m_bad, _( "You feel nauseous!" ) );
263             it.mod_duration( 3_turns );
264         }
265     }
266 }
eff_fun_bleed(Character & u,effect & it)267 static void eff_fun_bleed( Character &u, effect &it )
268 {
269     // Presuming that during the first-aid process you're putting pressure
270     // on the wound or otherwise suppressing the flow. (Kits contain either
271     // QuikClot or bandages per the recipe.)
272     const int intense = it.get_intensity();
273     // tourniquet reduces effective bleeding by 2/3 but doesn't modify the effect's intensity
274     bool tourniquet = u.worn_with_flag( STATIC( flag_id( "TOURNIQUET" ) ),  it.get_bp() );
275     if( !( tourniquet && one_in( 3 ) ) && u.activity.id() != ACT_FIRSTAID ) {
276         // Prolonged hemorrhage is a significant risk for developing anemia
277         u.vitamin_mod( vitamin_redcells, -intense );
278         u.vitamin_mod( vitamin_blood, -intense );
279         if( one_in( 400 / intense ) ) {
280             u.mod_pain( 1 );
281         }
282         if( one_in( 120 / intense ) ) {
283             u.bleed();
284             u.add_msg_player_or_npc( m_bad, _( "You lose some blood." ),
285                                      _( "<npcname> loses some blood." ) );
286         }
287     }
288 }
eff_fun_hallu(Character & u,effect & it)289 static void eff_fun_hallu( Character &u, effect &it )
290 {
291     // TODO: Redo this to allow for variable durations
292     // Time intervals are drawn from the old ones based on 3600 (6-hour) duration.
293     constexpr int maxDuration = 21600;
294     constexpr int comeupTime = static_cast<int>( maxDuration * 0.9 );
295     constexpr int noticeTime = static_cast<int>( comeupTime + ( maxDuration - comeupTime ) / 2 );
296     constexpr int peakTime = static_cast<int>( maxDuration * 0.8 );
297     constexpr int comedownTime = static_cast<int>( maxDuration * 0.3 );
298     const int dur = to_turns<int>( it.get_duration() );
299     // Baseline
300     if( dur == noticeTime ) {
301         u.add_msg_if_player( m_warning, _( "You feel a little strange." ) );
302     } else if( dur == comeupTime ) {
303         // Coming up
304         if( one_in( 2 ) ) {
305             u.add_msg_if_player( m_warning, _( "The world takes on a dreamlike quality." ) );
306         } else if( one_in( 3 ) ) {
307             u.add_msg_if_player( m_warning, _( "You have a sudden nostalgic feeling." ) );
308         } else if( one_in( 5 ) ) {
309             u.add_msg_if_player( m_warning, _( "Everything around you is starting to breathe." ) );
310         } else {
311             u.add_msg_if_player( m_warning, _( "Something feels very, very wrong." ) );
312         }
313     } else if( dur > peakTime && dur < comeupTime ) {
314         if( u.stomach.contains() > 0_ml && ( one_in( 1200 ) || x_in_y( u.vomit_mod(), 300 ) ) ) {
315             u.add_msg_if_player( m_bad, _( "You feel sick to your stomach." ) );
316             u.mod_hunger( -2 );
317             if( one_in( 6 ) ) {
318                 u.vomit();
319             }
320         }
321         if( u.is_npc() && one_in( 1200 ) ) {
322             static const std::array<std::string, 4> npc_hallu = {{
323                     translate_marker( "\"I think it's starting to kick in.\"" ),
324                     translate_marker( "\"Oh God, what's happening?\"" ),
325                     translate_marker( "\"Of course… it's all fractals!\"" ),
326                     translate_marker( "\"Huh?  What was that?\"" )
327                 }
328             };
329 
330             ///\EFFECT_STR_NPC increases volume of hallucination sounds (NEGATIVE)
331 
332             ///\EFFECT_INT_NPC decreases volume of hallucination sounds
333             int loudness = 20 + u.str_cur - u.int_cur;
334             loudness = ( loudness > 5 ? loudness : 5 );
335             loudness = ( loudness < 30 ? loudness : 30 );
336             sounds::sound( u.pos(), loudness, sounds::sound_t::speech, _( random_entry_ref( npc_hallu ) ),
337                            false, "speech",
338                            loudness < 15 ? ( u.male ? "NPC_m" : "NPC_f" ) : ( u.male ? "NPC_m_loud" : "NPC_f_loud" ) );
339         }
340     } else if( dur == peakTime ) {
341         // Visuals start
342         u.add_msg_if_player( m_bad, _( "Fractal patterns dance across your vision." ) );
343         u.add_effect( effect_visuals, time_duration::from_turns( peakTime - comedownTime ) );
344     } else if( dur > comedownTime && dur < peakTime ) {
345         // Full symptoms
346         u.mod_per_bonus( -2 );
347         u.mod_int_bonus( -1 );
348         u.mod_dex_bonus( -2 );
349         u.add_miss_reason( _( "Dancing fractals distract you." ), 2 );
350         u.mod_str_bonus( -1 );
351         if( u.is_player() && one_in( 50 ) ) {
352             g->spawn_hallucination( u.pos() + tripoint( rng( -10, 10 ), rng( -10, 10 ), 0 ) );
353         }
354     } else if( dur == comedownTime ) {
355         if( one_in( 42 ) ) {
356             u.add_msg_if_player( _( "Everything looks SO boring now." ) );
357         } else {
358             u.add_msg_if_player( _( "Things are returning to normal." ) );
359         }
360     }
361 }
362 
363 struct temperature_effect {
364     int str_pen;
365     int dex_pen;
366     int int_pen;
367     int per_pen;
368     translation msg;
369     int msg_chance;
370     translation miss_msg;
371 
temperature_effecttemperature_effect372     temperature_effect( int sp, int dp, int ip, int pp, const translation &ms, int mc,
373                         const translation &mm ) :
374         str_pen( sp ), dex_pen( dp ), int_pen( ip ), per_pen( pp ), msg( ms ),
375         msg_chance( mc ), miss_msg( mm ) {
376     }
377 
applytemperature_effect378     void apply( Character &u ) const {
379         if( str_pen > 0 ) {
380             u.mod_str_bonus( -str_pen );
381         }
382         if( dex_pen > 0 ) {
383             u.mod_dex_bonus( -dex_pen );
384             u.add_miss_reason( miss_msg.translated(), dex_pen );
385         }
386         if( int_pen > 0 ) {
387             u.mod_int_bonus( -int_pen );
388         }
389         if( per_pen > 0 ) {
390             u.mod_per_bonus( -per_pen );
391         }
392         if( !msg.empty() && !u.has_effect( effect_sleep ) && one_in( msg_chance ) ) {
393             u.add_msg_if_player( m_warning, "%s", msg.translated() );
394         }
395     }
396 };
397 
eff_fun_cold(Character & u,effect & it)398 static void eff_fun_cold( Character &u, effect &it )
399 {
400     // { body_part, intensity }, { str_pen, dex_pen, int_pen, per_pen, msg, msg_chance, miss_msg }
401     static const std::map<std::pair<bodypart_str_id, int>, temperature_effect> effs = {{
402             { { body_part_head, 3 }, { 0, 0, 3, 0, to_translation( "Your thoughts are unclear." ), 2400, translation() } },
403             { { body_part_head, 2 }, { 0, 0, 1, 0, translation(), 0, translation() } },
404             { { body_part_mouth, 3 }, { 0, 0, 0, 3, to_translation( "Your face is stiff from the cold." ), 2400, translation() } },
405             { { body_part_mouth, 2 }, { 0, 0, 0, 1, translation(), 0, translation() } },
406             { { body_part_torso, 3 }, { 0, 4, 0, 0, to_translation( "Your torso is freezing cold.  You should put on a few more layers." ), 400, to_translation( "You quiver from the cold." ) } },
407             { { body_part_torso, 2 }, { 0, 2, 0, 0, translation(), 0, to_translation( "Your shivering makes you unsteady." ) } },
408             { { body_part_arm_l, 3 }, { 0, 2, 0, 0, to_translation( "Your left arm is shivering." ), 4800, to_translation( "Your left arm trembles from the cold." ) } },
409             { { body_part_arm_l, 2 }, { 0, 1, 0, 0, to_translation( "Your left arm is shivering." ), 4800, to_translation( "Your left arm trembles from the cold." ) } },
410             { { body_part_arm_r, 3 }, { 0, 2, 0, 0, to_translation( "Your right arm is shivering." ), 4800, to_translation( "Your right arm trembles from the cold." ) } },
411             { { body_part_arm_r, 2 }, { 0, 1, 0, 0, to_translation( "Your right arm is shivering." ), 4800, to_translation( "Your right arm trembles from the cold." ) } },
412             { { body_part_hand_l, 3 }, { 0, 2, 0, 0, to_translation( "Your left hand feels like ice." ), 4800, to_translation( "Your left hand quivers in the cold." ) } },
413             { { body_part_hand_l, 2 }, { 0, 1, 0, 0, to_translation( "Your left hand feels like ice." ), 4800, to_translation( "Your left hand quivers in the cold." ) } },
414             { { body_part_hand_r, 3 }, { 0, 2, 0, 0, to_translation( "Your right hand feels like ice." ), 4800, to_translation( "Your right hand quivers in the cold." ) } },
415             { { body_part_hand_r, 2 }, { 0, 1, 0, 0, to_translation( "Your right hand feels like ice." ), 4800, to_translation( "Your right hand quivers in the cold." ) } },
416             { { body_part_leg_l, 3 }, { 2, 2, 0, 0, to_translation( "Your left leg trembles against the relentless cold." ), 4800, to_translation( "Your legs uncontrollably shake from the cold." ) } },
417             { { body_part_leg_l, 2 }, { 1, 1, 0, 0, to_translation( "Your left leg trembles against the relentless cold." ), 4800, to_translation( "Your legs uncontrollably shake from the cold." ) } },
418             { { body_part_leg_r, 3 }, { 2, 2, 0, 0, to_translation( "Your right leg trembles against the relentless cold." ), 4800, to_translation( "Your legs uncontrollably shake from the cold." ) } },
419             { { body_part_leg_r, 2 }, { 1, 1, 0, 0, to_translation( "Your right leg trembles against the relentless cold." ), 4800, to_translation( "Your legs uncontrollably shake from the cold." ) } },
420             { { body_part_foot_l, 3 }, { 2, 2, 0, 0, to_translation( "Your left foot feels frigid." ), 4800, to_translation( "Your left foot is as nimble as a block of ice." ) } },
421             { { body_part_foot_l, 2 }, { 1, 1, 0, 0, to_translation( "Your left foot feels frigid." ), 4800, to_translation( "Your freezing left foot messes up your balance." ) } },
422             { { body_part_foot_r, 3 }, { 2, 2, 0, 0, to_translation( "Your right foot feels frigid." ), 4800, to_translation( "Your right foot is as nimble as a block of ice." ) } },
423             { { body_part_foot_r, 2 }, { 1, 1, 0, 0, to_translation( "Your right foot feels frigid." ), 4800, to_translation( "Your freezing right foot messes up your balance." ) } },
424         }
425     };
426     const auto iter = effs.find( { it.get_bp().id(), it.get_intensity() } );
427     if( iter != effs.end() ) {
428         iter->second.apply( u );
429     }
430 }
431 
eff_fun_hot(Character & u,effect & it)432 static void eff_fun_hot( Character &u, effect &it )
433 {
434     // { body_part, intensity }, { str_pen, dex_pen, int_pen, per_pen, msg, msg_chance, miss_msg }
435     static const std::map<std::pair<bodypart_str_id, int>, temperature_effect> effs = {{
436             { { body_part_head, 3 }, { 0, 0, 0, 0, to_translation( "Your head is pounding from the heat." ), 2400, translation() } },
437             { { body_part_head, 2 }, { 0, 0, 0, 0, translation(), 0, translation() } },
438             { { body_part_torso, 3 }, { 2, 0, 0, 0, to_translation( "You are sweating profusely." ), 2400, translation() } },
439             { { body_part_torso, 2 }, { 1, 0, 0, 0, translation(), 0, translation() } },
440             { { body_part_hand_l, 3 }, { 0, 2, 0, 0, translation(), 0, to_translation( "Your left hand's too sweaty to grip well." ) } },
441             { { body_part_hand_l, 2 }, { 0, 1, 0, 0, translation(), 0, to_translation( "Your left hand's too sweaty to grip well." ) } },
442             { { body_part_hand_r, 3 }, { 0, 2, 0, 0, translation(), 0, to_translation( "Your right hand's too sweaty to grip well." ) } },
443             { { body_part_hand_r, 2 }, { 0, 1, 0, 0, translation(), 0, to_translation( "Your right hand's too sweaty to grip well." ) } },
444             { { body_part_leg_l, 3 }, { 0, 0, 0, 0, to_translation( "Your left leg is cramping up." ), 4800, translation() } },
445             { { body_part_leg_l, 2 }, { 0, 0, 0, 0, translation(), 0, translation() } },
446             { { body_part_leg_r, 3 }, { 0, 0, 0, 0, to_translation( "Your right leg is cramping up." ), 4800, translation() } },
447             { { body_part_leg_r, 2 }, { 0, 0, 0, 0, translation(), 0, translation() } },
448             { { body_part_foot_l, 3 }, { 0, 0, 0, 0, to_translation( "Your left foot is swelling in the heat." ), 4800, translation() } },
449             { { body_part_foot_l, 2 }, { 0, 0, 0, 0, translation(), 0, translation() } },
450             { { body_part_foot_r, 3 }, { 0, 0, 0, 0, to_translation( "Your right foot is swelling in the heat." ), 4800, translation() } },
451             { { body_part_foot_r, 2 }, { 0, 0, 0, 0, translation(), 0, translation() } },
452         }
453     };
454 
455     const bodypart_id bp = it.get_bp();
456     const int intense = it.get_intensity();
457     const auto iter = effs.find( { it.get_bp().id(), it.get_intensity() } );
458     if( iter != effs.end() ) {
459         iter->second.apply( u );
460     }
461     // Hothead effects are a special snowflake
462 
463     if( bp == bodypart_id( "head" ) && intense >= 2 ) {
464         if( one_in( std::max( 25, std::min( 89500,
465                                             90000 - u.get_part_temp_cur( bodypart_id( "head" ) ) ) ) ) ) {
466             u.vomit();
467         }
468         if( !u.has_effect( effect_sleep ) && one_in( 2400 ) ) {
469             u.add_msg_if_player( m_bad, _( "The heat is making you see things." ) );
470         }
471     }
472 }
473 
eff_fun_frostbite(Character & u,effect & it)474 static void eff_fun_frostbite( Character &u, effect &it )
475 {
476     // { body_part, intensity }, { str_pen, dex_pen, int_pen, per_pen, msg, msg_chance, miss_msg }
477     static const std::map<std::pair<bodypart_str_id, int>, temperature_effect> effs = {{
478             { { body_part_hand_l, 2 }, { 0, 2, 0, 0, translation(), 0, to_translation( "You have trouble grasping with your numb fingers." ) } },
479             { { body_part_hand_r, 2 }, { 0, 2, 0, 0, translation(), 0, to_translation( "You have trouble grasping with your numb fingers." ) } },
480             { { body_part_foot_l, 2 }, { 0, 0, 0, 0, to_translation( "Your foot has gone numb." ), 4800, translation() } },
481             { { body_part_foot_l, 1 }, { 0, 0, 0, 0, to_translation( "Your foot has gone numb." ), 4800, translation() } },
482             { { body_part_foot_r, 2 }, { 0, 0, 0, 0, to_translation( "Your foot has gone numb." ), 4800, translation() } },
483             { { body_part_foot_r, 1 }, { 0, 0, 0, 0, to_translation( "Your foot has gone numb." ), 4800, translation() } },
484             { { body_part_mouth, 2 }, { 0, 0, 0, 3, to_translation( "Your face feels numb." ), 4800, translation() } },
485             { { body_part_mouth, 1 }, { 0, 0, 0, 1, to_translation( "Your face feels numb." ), 4800, translation() } },
486         }
487     };
488     const auto iter = effs.find( { it.get_bp().id(), it.get_intensity() } );
489     if( iter != effs.end() ) {
490         iter->second.apply( u );
491     }
492 }
493 
hardcoded_effects(effect & it)494 void Character::hardcoded_effects( effect &it )
495 {
496     if( const ma_buff *buff = ma_buff::from_effect( it ) ) {
497         if( buff->is_valid_character( *this ) ) {
498             buff->apply_character( *this );
499         } else {
500             it.set_duration( 0_turns ); // removes the effect
501         }
502         return;
503     }
504     using hc_effect_fun = std::function<void( Character &, effect & )>;
505     static const std::map<efftype_id, hc_effect_fun> hc_effect_map = {{
506             { effect_onfire, eff_fun_onfire },
507             { effect_spores, eff_fun_spores },
508             { effect_fungus, eff_fun_fungus },
509             { effect_antifungal, eff_fun_antifungal },
510             { effect_rat, eff_fun_rat },
511             { effect_bleed, eff_fun_bleed },
512             { effect_hallu, eff_fun_hallu },
513             { effect_cold, eff_fun_cold },
514             { effect_hot, eff_fun_hot },
515             { effect_frostbite, eff_fun_frostbite },
516         }
517     };
518     const efftype_id &id = it.get_id();
519     const auto &iter = hc_effect_map.find( id );
520     if( iter != hc_effect_map.end() ) {
521         iter->second( *this, it );
522         return;
523     }
524 
525     const time_duration dur = it.get_duration();
526     int intense = it.get_intensity();
527     const bodypart_id &bp = it.get_bp();
528     bool sleeping = has_effect( effect_sleep );
529     map &here = get_map();
530     Character &player_character = get_player_character();
531     if( id == effect_dermatik ) {
532         bool triggered = false;
533         int formication_chance = 3600;
534         if( dur < 4_hours ) {
535             formication_chance += 14400 - to_turns<int>( dur );
536         }
537         if( one_in( formication_chance ) ) {
538             add_effect( effect_formication, 60_minutes, bp );
539         }
540         if( dur < 1_days && one_in( 14400 ) ) {
541             vomit();
542         }
543         if( dur > 1_days ) {
544             // Spawn some larvae!
545             // Choose how many insects; more for large characters
546             ///\EFFECT_STR_MAX increases number of insects hatched from dermatik infection
547             int num_insects = rng( 1, std::min( 3, str_max / 3 ) );
548             apply_damage( nullptr,  bp, rng( 2, 4 ) * num_insects );
549             // Figure out where they may be placed
550             add_msg_player_or_npc( m_bad,
551                                    _( "Your flesh crawls; insects tear through the flesh and begin to emerge!" ),
552                                    _( "Insects begin to emerge from <npcname>'s skin!" ) );
553             for( ; num_insects > 0; num_insects-- ) {
554                 if( monster *const grub = g->place_critter_around( mon_dermatik_larva, pos(), 1 ) ) {
555                     if( one_in( 3 ) ) {
556                         grub->friendly = -1;
557                     }
558                 }
559             }
560             get_event_bus().send<event_type::dermatik_eggs_hatch>( getID() );
561             remove_effect( effect_formication, bp );
562             moves -= 600;
563             triggered = true;
564         }
565         if( triggered ) {
566             // Set ourselves up for removal
567             it.set_duration( 0_turns );
568         } else {
569             // Count duration up
570             it.mod_duration( 1_turns );
571         }
572     } else if( id == effect_formication ) {
573         ///\EFFECT_INT decreases occurrence of itching from formication effect
574         if( x_in_y( intense, 600 + 300 * get_int() ) && !has_effect( effect_narcosis ) ) {
575             if( !is_npc() ) {
576                 //~ %s is bodypart in accusative.
577                 add_msg( m_warning, _( "You start scratching your %s!" ),
578                          body_part_name_accusative( bp ) );
579                 player_character.cancel_activity();
580             } else {
581                 //~ 1$s is NPC name, 2$s is bodypart in accusative.
582                 add_msg_if_player_sees( pos(), _( "%1$s starts scratching their %2$s!" ), name,
583                                         body_part_name_accusative( bp ) );
584             }
585             moves -= 150;
586             apply_damage( nullptr, bp, 1 );
587         }
588     } else if( id == effect_evil ) {
589         // Major effects, all bad.
590         mod_str_bonus( -( dur > 500_minutes ? 10.0 : dur / 50_minutes ) );
591         int dex_mod = -( dur > 600_minutes ? 10.0 : dur / 60_minutes );
592         mod_dex_bonus( dex_mod );
593         add_miss_reason( _( "Why waste your time on that insignificant speck?" ), -dex_mod );
594         mod_int_bonus( -( dur > 450_minutes ? 10.0 : dur / 45_minutes ) );
595         mod_per_bonus( -( dur > 400_minutes ? 10.0 : dur / 40_minutes ) );
596     } else if( id == effect_attention ) {
597         if( to_turns<int>( dur ) != 0 && one_in( 100000 / to_turns<int>( dur ) ) &&
598             one_in( 100000 / to_turns<int>( dur ) ) && one_in( 250 ) ) {
599             tripoint dest( 0, 0, posz() );
600             int tries = 0;
601             do {
602                 dest.x = posx() + rng( -4, 4 );
603                 dest.y = posy() + rng( -4, 4 );
604                 tries++;
605             } while( g->critter_at( dest ) && tries < 10 );
606             if( tries < 10 ) {
607                 if( here.impassable( dest ) ) {
608                     here.make_rubble( dest, f_rubble_rock, true );
609                 }
610                 MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup(
611                                                        GROUP_NETHER );
612                 g->place_critter_at( spawn_details.name, dest );
613                 if( player_character.sees( dest ) ) {
614                     g->cancel_activity_or_ignore_query( distraction_type::hostile_spotted_far,
615                                                         _( "A monster appears nearby!" ) );
616                     add_msg_if_player( m_warning, _( "A portal opens nearby, and a monster crawls through!" ) );
617                 }
618                 it.mult_duration( .25 );
619             }
620         }
621     } else if( id == effect_meth ) {
622         if( intense == 1 ) {
623             add_miss_reason( _( "The bees have started escaping your teeth." ), 2 );
624             if( one_in( 900 ) ) {
625                 add_msg_if_player( m_bad, _( "You feel paranoid.  They're watching you." ) );
626                 mod_pain( 1 );
627                 mod_fatigue( dice( 1, 6 ) );
628             } else if( one_in( 3000 ) ) {
629                 add_msg_if_player( m_bad,
630                                    _( "You feel like you need less teeth.  You pull one out, and it is rotten to the core." ) );
631                 mod_pain( 1 );
632             } else if( one_in( 3000 ) ) {
633                 add_msg_if_player( m_bad, _( "You notice a large abscess.  You pick at it." ) );
634                 const bodypart_id &itch = random_body_part( true );
635                 add_effect( effect_formication, 60_minutes, itch );
636                 mod_pain( 1 );
637             } else if( one_in( 3000 ) ) {
638                 add_msg_if_player( m_bad,
639                                    _( "You feel so sick, like you've been poisoned, but you need more.  So much more." ) );
640                 vomit();
641                 mod_fatigue( dice( 1, 6 ) );
642             }
643         }
644     } else if( id == effect_tindrift ) {
645         add_msg_if_player( m_bad, _( "You are beset with a vision of a prowling beast." ) );
646         for( const tripoint &dest : here.points_in_radius( pos(), 6 ) ) {
647             if( here.is_cornerfloor( dest ) ) {
648                 here.add_field( dest, fd_tindalos_rift, 3 );
649                 add_msg_if_player( m_info, _( "Your surroundings are permeated with a foul scent." ) );
650                 //Remove the effect, since it's done all it needs to do to the target.
651                 remove_effect( effect_tindrift );
652             }
653         }
654     } else if( id == effect_teleglow ) {
655         // Default we get around 300 duration points per teleport (possibly more
656         // depending on the source).
657         // TODO: Include a chance to teleport to the nether realm.
658         // TODO: This with regards to NPCS
659         if( !is_player() ) {
660             // NO, no teleporting around the player because an NPC has teleglow!
661             return;
662         }
663         if( dur > 10_hours ) {
664             // 20 teleports (no decay; in practice at least 21)
665             if( one_in( 6000 - ( ( dur - 600_minutes ) / 1_minutes ) ) ) {
666                 if( !is_npc() ) {
667                     add_msg( _( "Glowing lights surround you, and you teleport." ) );
668                 }
669                 teleport::teleport( *this );
670                 get_event_bus().send<event_type::teleglow_teleports>( getID() );
671                 if( one_in( 10 ) ) {
672                     // Set ourselves up for removal
673                     it.set_duration( 0_turns );
674                 }
675             }
676         }
677         if( one_in( 7200 - ( dur - 360_minutes ) / 4_turns ) ) {
678             //Spawn a tindalos rift via effect_tindrift rather than it being hard-coded to teleglow
679             add_effect( effect_tindrift, 5_turns );
680 
681             if( one_in( 2 ) ) {
682                 // Set ourselves up for removal
683                 it.set_duration( 0_turns );
684             }
685         }
686         if( one_in( 7200 - ( ( dur - 600_minutes ) / 30_seconds ) ) && one_in( 20 ) ) {
687             add_msg_if_player( m_bad, _( "You pass out." ) );
688             fall_asleep( 2_hours );
689             if( one_in( 6 ) ) {
690                 // Set ourselves up for removal
691                 it.set_duration( 0_turns );
692             }
693         }
694         if( dur > 6_hours ) {
695             // 12 teleports
696             if( one_in( 24000 - ( dur - 360_minutes ) / 4_turns ) ) {
697                 tripoint dest( 0, 0, posz() );
698                 int &x = dest.x;
699                 int &y = dest.y;
700                 int tries = 0;
701                 do {
702                     x = posx() + rng( -4, 4 );
703                     y = posy() + rng( -4, 4 );
704                     tries++;
705                     if( tries >= 10 ) {
706                         break;
707                     }
708                 } while( g->critter_at( dest ) );
709                 if( tries < 10 ) {
710                     if( here.impassable( dest ) ) {
711                         here.make_rubble( dest, f_rubble_rock, true );
712                     }
713                     MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup(
714                                                            GROUP_NETHER );
715                     g->place_critter_at( spawn_details.name, dest );
716                     if( player_character.sees( dest ) ) {
717                         g->cancel_activity_or_ignore_query( distraction_type::hostile_spotted_far,
718                                                             _( "A monster appears nearby!" ) );
719                         add_msg( m_warning, _( "A portal opens nearby, and a monster crawls through!" ) );
720                     }
721                     if( one_in( 2 ) ) {
722                         // Set ourselves up for removal
723                         it.set_duration( 0_turns );
724                     }
725                 }
726             }
727             if( one_in( 21000 - ( dur - 360_minutes ) / 4_turns ) ) {
728                 add_msg_if_player( m_bad, _( "You shudder suddenly." ) );
729                 mutate();
730                 if( one_in( 4 ) ) {
731                     // Set ourselves up for removal
732                     it.set_duration( 0_turns );
733                 }
734             }
735         }
736         if( dur > 4_hours ) {
737             // 8 teleports
738             if( one_turn_in( 1000_minutes - dur ) && !has_effect( effect_valium ) ) {
739                 add_effect( effect_shakes, rng( 4_minutes, 8_minutes ) );
740             }
741             if( one_turn_in( 1200_minutes - dur ) ) {
742                 add_msg_if_player( m_bad, _( "Your vision is filled with bright lights…" ) );
743                 add_effect( effect_blind, rng( 1_minutes, 2_minutes ) );
744                 if( one_in( 8 ) ) {
745                     // Set ourselves up for removal
746                     it.set_duration( 0_turns );
747                 }
748             }
749             if( one_in( 5000 ) && !has_effect( effect_hallu ) ) {
750                 add_effect( effect_hallu, 6_hours );
751                 if( one_in( 5 ) ) {
752                     // Set ourselves up for removal
753                     it.set_duration( 0_turns );
754                 }
755             }
756         }
757         if( one_in( 4000 ) ) {
758             add_msg_if_player( m_bad, _( "You're suddenly covered in ectoplasm." ) );
759             add_effect( effect_boomered, 10_minutes );
760             if( one_in( 4 ) ) {
761                 // Set ourselves up for removal
762                 it.set_duration( 0_turns );
763             }
764         }
765         if( one_in( 10000 ) ) {
766             if( !has_trait( trait_M_IMMUNE ) ) {
767                 add_effect( effect_fungus, 1_turns, true );
768             } else {
769                 add_msg_if_player( m_info, _( "We have many colonists awaiting passage." ) );
770             }
771             // Set ourselves up for removal
772             it.set_duration( 0_turns );
773         }
774     } else if( id == effect_asthma ) {
775         if( has_effect( effect_adrenaline ) || has_effect( effect_datura ) ) {
776             add_msg_if_player( m_good, _( "Your asthma attack stops." ) );
777             it.set_duration( 0_turns );
778         } else if( dur > 2_hours ) {
779             add_msg_if_player( m_bad, _( "Your asthma overcomes you.\nYou asphyxiate." ) );
780             get_event_bus().send<event_type::dies_from_asthma_attack>( getID() );
781             hurtall( 500, nullptr );
782         } else if( dur > 70_minutes ) {
783             if( one_in( 120 ) ) {
784                 add_msg_if_player( m_bad, _( "You wheeze and gasp for air." ) );
785             }
786         }
787     } else if( id == effect_brainworms ) {
788         if( one_in( 1536 ) ) {
789             add_msg_if_player( m_bad, _( "Your head aches faintly." ) );
790         }
791         if( one_in( 6144 ) ) {
792             mod_healthy_mod( -10, -100 );
793             apply_damage( nullptr, bodypart_id( "head" ), rng( 0, 1 ) );
794             if( !has_effect( effect_visuals ) ) {
795                 add_msg_if_player( m_bad, _( "Your vision is getting fuzzy." ) );
796                 add_effect( effect_visuals, rng( 1_minutes, 60_minutes ) );
797             }
798         }
799         if( one_in( 24576 ) ) {
800             mod_healthy_mod( -10, -100 );
801             apply_damage( nullptr, bodypart_id( "head" ), rng( 1, 2 ) );
802             if( !is_blind() && !sleeping ) {
803                 add_msg_if_player( m_bad, _( "Your vision goes black!" ) );
804                 add_effect( effect_blind, rng( 5_turns, 20_turns ) );
805             }
806         }
807     } else if( id == effect_tapeworm ) {
808         if( one_in( 3072 ) ) {
809             add_msg_if_player( m_bad, _( "Your bowels ache." ) );
810         }
811     } else if( id == effect_bloodworms ) {
812         if( one_in( 3072 ) ) {
813             add_msg_if_player( m_bad, _( "Your veins itch." ) );
814         }
815     } else if( id == effect_paincysts ) {
816         if( one_in( 3072 ) ) {
817             add_msg_if_player( m_bad, _( "Your muscles feel like they're knotted and tired." ) );
818         }
819     } else if( id == effect_tetanus ) {
820         if( one_in( 1536 ) ) {
821             add_msg_if_player( m_bad, _( "Your muscles are tight and sore." ) );
822         }
823         if( !has_effect( effect_valium ) ) {
824             add_miss_reason( _( "Your muscles are locking up and you can't fight effectively." ), 4 );
825             if( one_in( 3072 ) ) {
826                 add_msg_if_player( m_bad, _( "Your muscles spasm." ) );
827                 add_effect( effect_downed, rng( 1_turns, 4_turns ), false, 0, true );
828                 add_effect( effect_stunned, rng( 1_turns, 4_turns ) );
829                 if( one_in( 10 ) ) {
830                     mod_pain( rng( 1, 10 ) );
831                 }
832             }
833         }
834     } else if( id == effect_datura ) {
835         if( dur > 100_minutes && get_focus() >= 1 && one_in( 24 ) ) {
836             mod_focus( -1 );
837         }
838         if( dur > 200_minutes && one_in( 48 ) && get_stim() < 20 ) {
839             mod_stim( 1 );
840         }
841         if( dur > 300_minutes && get_focus() >= 1 && one_in( 12 ) ) {
842             mod_focus( -1 );
843         }
844         if( dur > 400_minutes && one_in( 384 ) ) {
845             mod_pain( rng( -1, -8 ) );
846         }
847         if( ( !has_effect( effect_hallu ) ) && ( dur > 500_minutes && one_in( 24 ) ) ) {
848             add_effect( effect_hallu, 6_hours );
849         }
850         if( dur > 600_minutes && one_in( 768 ) ) {
851             mod_pain( rng( -3, -24 ) );
852             if( dur > 800_minutes && one_in( 16 ) ) {
853                 add_msg_if_player( m_bad,
854                                    _( "You're experiencing loss of basic motor skills and blurred vision.  Your mind recoils in horror, unable to communicate with your spinal column." ) );
855                 add_msg_if_player( m_bad, _( "You stagger and fall!" ) );
856                 add_effect( effect_downed, rng( 1_turns, 4_turns ), false, 0, true );
857                 if( one_in( 8 ) || x_in_y( vomit_mod(), 10 ) ) {
858                     vomit();
859                 }
860             }
861         }
862         if( dur > 700_minutes && get_focus() >= 1 ) {
863             mod_focus( -1 );
864         }
865         if( dur > 800_minutes && one_in( 1536 ) ) {
866             add_effect( effect_visuals, rng( 4_minutes, 20_minutes ) );
867             mod_pain( rng( -8, -40 ) );
868         }
869         if( dur > 1200_minutes && one_in( 1536 ) ) {
870             add_msg_if_player( m_bad, _( "There's some kind of big machine in the sky." ) );
871             add_effect( effect_visuals, rng( 8_minutes, 40_minutes ) );
872             if( one_in( 32 ) ) {
873                 add_msg_if_player( m_bad, _( "It's some kind of electric snake, coming right at you!" ) );
874                 mod_pain( rng( 4, 40 ) );
875                 vomit();
876             }
877         }
878         if( dur > 1400_minutes && one_in( 768 ) ) {
879             add_msg_if_player( m_bad,
880                                _( "Order us some golf shoes, otherwise we'll never get out of this place alive." ) );
881             add_effect( effect_visuals, rng( 40_minutes, 200_minutes ) );
882             if( one_in( 8 ) ) {
883                 add_msg_if_player( m_bad,
884                                    _( "The possibility of physical and mental collapse is now very real." ) );
885                 if( one_in( 2 ) || x_in_y( vomit_mod(), 10 ) ) {
886                     add_msg_if_player( m_bad, _( "No one should be asked to handle this trip." ) );
887                     vomit();
888                     mod_pain( rng( 8, 40 ) );
889                 }
890             }
891         }
892 
893         if( dur > 1800_minutes && one_in( 300 * 512 ) ) {
894             if( !has_trait( trait_NOPAIN ) ) {
895                 add_msg_if_player( m_bad,
896                                    _( "Your heart spasms painfully and stops, dragging you back to reality as you die." ) );
897             } else {
898                 add_msg_if_player(
899                     _( "You dissolve into beautiful paroxysms of energy.  Life fades from your nebulae and you are no more." ) );
900             }
901             get_event_bus().send<event_type::dies_from_drug_overdose>( getID(), id );
902             set_part_hp_cur( bodypart_id( "torso" ), 0 );
903         }
904     } else if( id == effect_hypovolemia ) {
905         // hypovolemia and dehydration are closely related so it will pull water
906         // from your system to replenish blood quantity
907         if( calendar::once_every( -vitamin_rate( vitamin_blood ) ) && one_in( 5 ) && get_thirst() <= 240 ) {
908             mod_thirst( rng( 0, intense ) );
909         }
910         // bleed out lambda
911         auto bleed_out = [&] {
912             if( has_effect( effect_bleed ) )
913             {
914                 add_msg_player_or_npc( m_bad,
915                                        _( "You bleed to death!" ),
916                                        _( "<npcname> bleeds to death!" ) );
917                 get_event_bus().send<event_type::dies_from_bleeding>( getID() );
918             } else
919             {
920                 add_msg_player_or_npc( m_bad,
921                                        _( "Your heart can't keep up the pace and fails!" ),
922                                        _( "<npcname> has a sudden heart attack!" ) );
923                 get_event_bus().send<event_type::dies_from_hypovolemia>( getID() );
924             }
925             set_part_hp_cur( bodypart_id( "torso" ), 0 );
926         };
927         // this goes first because beyond minimum threshold you just die without delay,
928         // while stage 4 is on a timer check with an rng grace period
929 
930         if( vitamin_get( vitamin_blood ) == vitamin_blood->min() ) {
931             bleed_out();
932         }
933 
934         // Hypovolemic shock
935         // stage 1 - early symptoms include headache, fatigue, weakness, thirst, and dizziness.
936         // stage 2 - person may begin sweating and feeling more anxious and restless.
937         // stage 3 - heart rate will increase to over 120 bpm; rapid breathing
938         // mental distress, including anxiety and agitation; skin is pale and cold + cyanosis, sweating
939         // stage 4 is a life threatening condition; extremely rapid heart rate, breathing very fast and difficult
940         // drifting in and out of consciousness, sweating heavily, feeling cool to the touch, looking extremely pale
941 
942         if( one_in( 1200 / intense ) && !in_sleep_state() ) {
943             std::string warning;
944 
945             if( one_in( 5 ) ) {
946                 // no-effect message block
947                 if( intense == 1 ) {
948                     warning = _( "Your skin looks pale and you feel anxious and thirsty.  Blood loss?" );
949                 } else if( intense == 2 ) {
950                     warning = _( "Your pale skin is sweating, your heart is beating fast, and you feel restless.  Maybe you lost too much blood?" );
951                 } else if( intense == 3 ) {
952                     warning = _( "You're unsettlingly white, but your fingertips are bluish.  You are agitated and your heart is racing.  Your blood loss must be serious." );
953                 } else { //intense == 4
954                     warning = _( "You are pale as a ghost, dripping wet from the sweat, and sluggish - despite your heart racing like a train.  You are on the brink of collapse from the effects of blood loss." );
955                 }
956                 add_msg_if_player( m_bad, warning );
957             } else {
958                 // effect dice, with progression of effects, 3 possible effects per tier
959                 int dice_roll = rng( 0, 2 ) + intense;
960                 switch( dice_roll ) {
961                     case 1:
962                         warning = _( "You feel dizzy and lightheaded." );
963                         add_effect( effect_stunned, rng( 5_seconds * intense, 2_minutes * intense ) );
964                         break;
965                     case 2:
966                         warning = _( "You feel tired and you breathe heavily." );
967                         mod_fatigue( 3 * intense );
968                         break;
969                     case 3:
970                         warning = _( "You are anxious and cannot collect your thoughts." );
971                         mod_focus( -rng( 1, get_focus() * intense / it.get_max_intensity() ) );
972                         break;
973                     case 4:
974                         warning = _( "You are sweating profusely, but you feel cold." );
975                         mod_part_temp_conv( bodypart_id( "hand_l" ), - 1000 * intense );
976                         mod_part_temp_conv( bodypart_id( "hand_r" ), -1000 * intense );
977                         mod_part_temp_conv( bodypart_id( "foot_l" ), -1000 * intense );
978                         mod_part_temp_conv( bodypart_id( "foot_r" ), -1000 * intense );
979                         break;
980                     case 5:
981                         warning = _( "You huff and puff.  Your breath is rapid and shallow." );
982                         mod_stamina( -500 * intense );
983                         break;
984                     case 6:
985                         if( one_in( 2 ) ) {
986                             warning = _( "You drop to the ground, fighting to keep yourself conscious." );
987                             add_effect( effect_downed, rng( 1_minutes, 2_minutes ) );
988                             break;
989                         } else {
990                             warning = _( "Your mind slips away." );
991                             fall_asleep( rng( 2_minutes, 5_minutes ) );
992                             break;
993                         }
994                 }
995                 add_msg_if_player( m_bad, warning );
996             }
997         }
998         // this goes last because we don't want in_sleep_state to prevent you from dying
999         if( intense == 4 && one_in( 900 ) &&
1000             rng( 1, -vitamin_blood->min() * 3 / 5 ) > ( -vitamin_blood->min() + vitamin_get(
1001                         vitamin_blood ) ) ) {
1002             bleed_out();
1003         }
1004     } else if( id == effect_anemia ) {
1005         // effects: reduces effective redcells regen and depletes redcells at high intensity
1006         if( calendar::once_every( vitamin_rate( vitamin_redcells ) ) ) {
1007             vitamin_mod( vitamin_redcells, -rng( 0, intense ) );
1008         }
1009     } else if( id == effect_redcells_anemia ) {
1010         // Lack of iron impairs production of hemoglobin and therefore ability to carry
1011         // oxygen by red blood cells. Alternatively hemorrhage causes physical loss of red blood cells.
1012         // This triggers variety of symptoms, focusing on weakness,
1013         // fatigue, cold limbs, later in dizziness, soreness, breathlessness,
1014         // and severe malaise and lethargy.
1015         // Base anemia symptoms: fatigue, loss of stamina, loss of strength, impact on health
1016         // are placed in effect JSON
1017 
1018         // you can only lose as much red blood cells before your body fails to function
1019         if( vitamin_get( vitamin_redcells ) <= vitamin_redcells->min() + 5 ) {
1020             add_msg_player_or_npc( m_bad,
1021                                    _( "You cannot breathe and your body gives out!" ),
1022                                    _( "<npcname> gasps for air and dies!" ) );
1023             get_event_bus().send<event_type::dies_from_redcells_loss>( getID() );
1024             set_part_hp_cur( bodypart_id( "torso" ), 0 );
1025         }
1026         if( one_in( 900 / intense ) && !in_sleep_state() ) {
1027             // level 1 symptoms are cold limbs, pale skin, and weakness
1028             switch( dice( 1, 9 ) ) {
1029                 case 1:
1030                     add_msg_if_player( m_bad, _( "Your hands feel unusually cold." ) );
1031                     mod_part_temp_conv( bodypart_id( "hand_l" ), -2000 );
1032                     mod_part_temp_conv( bodypart_id( "hand_r" ), -2000 );
1033                     break;
1034                 case 2:
1035                     add_msg_if_player( m_bad, _( "Your feet feel unusually cold." ) );
1036                     mod_part_temp_conv( bodypart_id( "foot_l" ), -2000 );
1037                     mod_part_temp_conv( bodypart_id( "foot_r" ), -2000 );
1038                     break;
1039                 case 3:
1040                     add_msg_if_player( m_bad, _( "Your skin looks very pale." ) );
1041                     break;
1042                 case 4:
1043                     add_msg_if_player( m_bad, _( "You feel weak.  Where has your strength gone?" ) );
1044                     break;
1045                 case 5:
1046                     add_msg_if_player( m_bad, _( "You feel feeble.  A gust of wind could make you stumble." ) );
1047                     break;
1048                 case 6:
1049                     add_msg_if_player( m_bad, _( "There is an overwhelming aura of tiredness inside of you." ) );
1050                     mod_fatigue( intense * 3 );
1051                     break;
1052                 case 7: // 7-9 empty for variability, as messages stack on higher intensity
1053                     break;
1054                 case 8:
1055                     break;
1056                 case 9:
1057                     break;
1058             }
1059             // level 2 anemia introduces dizziness, shakes, headaches, cravings for non-comestibles,
1060             // mouth and tongue soreness
1061             if( intense > 1 ) {
1062                 switch( dice( 1, 9 ) ) {
1063                     case 1:
1064                         add_msg_if_player( m_bad, _( "Rest is what you want.  Rest is what you need." ) );
1065                         break;
1066                     case 2:
1067                         add_msg_if_player( m_bad, _( "You feel dizzy and can't coordinate the movement of your feet." ) );
1068                         add_effect( effect_stunned, rng( 1_minutes, 2_minutes ) );
1069                         break;
1070                     case 3:
1071                         add_msg_if_player( m_bad, _( "Your muscles are quivering." ) );
1072                         add_effect( effect_shakes, rng( 4_minutes, 8_minutes ) );
1073                         break;
1074                     case 4:
1075                         add_msg_if_player( m_bad, _( "You crave for ice.  The dirt under your feet looks tasty too." ) );
1076                         break;
1077                     case 5:
1078                         add_msg_if_player( m_bad, _( "Your whole mouth is sore, and your tongue is swollen." ) );
1079                         break;
1080                     case 6:
1081                         add_msg_if_player( m_bad, _( "You feel lightheaded.  A migraine follows." ) );
1082                         mod_pain( intense * 9 );
1083                         break;
1084                     case 7: // 7-9 empty for variability, as messages stack on higher intensity
1085                         break;
1086                     case 8:
1087                         break;
1088                     case 9:
1089                         break;
1090                 }
1091             }
1092             // level 3 anemia introduces restless legs, severe tiredness, breathlessness
1093             if( intense > 2 ) {
1094                 switch( dice( 1, 9 ) ) {
1095                     case 1:
1096                         add_msg_if_player( m_bad, _( "Your legs are restless.  The urge to move them is so strong." ) );
1097                         break;
1098                     case 2:
1099                         add_msg_if_player( m_bad, _( "You feel like you could sleep on a rock." ) );
1100                         mod_fatigue( intense * 3 );
1101                         break;
1102                     case 3:
1103                         add_msg_if_player( m_bad, _( "You gasp for air!" ) );
1104                         set_stamina( 0 );
1105                         add_effect( effect_winded, rng( 30_seconds, 3_minutes ) );
1106                         break;
1107                     case 4:
1108                         add_msg_if_player( m_bad, _( "Can't breathe.  Must rest." ) );
1109                         set_stamina( 0 );
1110                         break;
1111                     case 5:
1112                         add_msg_if_player( m_bad, _( "You can't take it any more.  Rest first; everything else later." ) );
1113                         add_effect( effect_lying_down, rng( 2_minutes, 5_minutes ) );
1114                         break;
1115                     case 6:
1116                         add_msg_if_player( m_bad, _( "You must sit down for a moment.  Just a moment." ) );
1117                         add_effect( effect_downed, rng( 1_minutes, 2_minutes ) );
1118                         break;
1119                     case 7: // 7-9 empty for variability, as messages stack on higher intensity
1120                         break;
1121                     case 8:
1122                         break;
1123                     case 9:
1124                         break;
1125                 }
1126             }
1127         }
1128     } else if( id == effect_grabbed ) {
1129         set_num_blocks_bonus( get_num_blocks_bonus() - 1 );
1130         int zed_number = 0;
1131         for( const tripoint &dest : here.points_in_radius( pos(), 1, 0 ) ) {
1132             const monster *const mon = g->critter_at<monster>( dest );
1133             if( mon && mon->has_effect( effect_grabbing ) ) {
1134                 zed_number += mon->get_grab_strength();
1135             }
1136         }
1137         if( zed_number > 0 ) {
1138             //If intensity isn't pass the cap, average it with # of zeds
1139             add_effect( effect_grabbed, 2_turns, bodypart_id( "torso" ), false, ( intense + zed_number ) / 2 );
1140         }
1141     } else if( id == effect_bite ) {
1142         bool recovered = false;
1143         /* Recovery chances, use binomial distributions if balancing here. Healing in the bite
1144          * stage provides additional benefits, so both the bite stage chance of healing and
1145          * the cumulative chances for spontaneous healing are both given.
1146          * Cumulative heal chances for the bite + infection stages:
1147          * -200 health - 38.6%
1148          *    0 health - 46.8%
1149          *  200 health - 53.7%
1150          *
1151          * Heal chances in the bite stage:
1152          * -200 health - 23.4%
1153          *    0 health - 28.3%
1154          *  200 health - 32.9%
1155          *
1156          * Cumulative heal chances the bite + infection stages with the resistant mutation:
1157          * -200 health - 82.6%
1158          *    0 health - 84.5%
1159          *  200 health - 86.1%
1160          *
1161          * Heal chances in the bite stage with the resistant mutation:
1162          * -200 health - 60.7%
1163          *    0 health - 63.2%
1164          *  200 health - 65.6%
1165          */
1166         if( dur % 10_turns == 0_turns )  {
1167             int recover_factor = 100;
1168             if( has_effect( effect_recover ) ) {
1169                 recover_factor -= get_effect_dur( effect_recover ) / 1_hours;
1170             }
1171             if( has_trait( trait_INFRESIST ) ) {
1172                 recover_factor += 200;
1173             }
1174             if( has_effect( effect_panacea ) ) {
1175                 recover_factor = 648000; //panacea is a guaranteed cure
1176             } else if( has_effect( effect_strong_antibiotic ) ) {
1177                 recover_factor += 400;
1178             } else if( has_effect( effect_antibiotic ) ) {
1179                 recover_factor += 200;
1180             } else if( has_effect( effect_weak_antibiotic ) ) {
1181                 recover_factor += 100;
1182             }
1183             recover_factor += get_healthy() / 10;
1184 
1185             if( x_in_y( recover_factor, 648000 ) ) {
1186                 //~ %s is bodypart name.
1187                 add_msg_if_player( m_good, _( "Your %s wound begins to feel better!" ),
1188                                    body_part_name( bp ) );
1189                 // Set ourselves up for removal
1190                 it.set_duration( 0_turns );
1191                 recovered = true;
1192             }
1193         }
1194         if( !recovered ) {
1195             // Move up to infection
1196             if( dur > 6_hours ) {
1197                 add_effect( effect_infected, 1_turns, bp, true );
1198                 // Set ourselves up for removal
1199                 it.set_duration( 0_turns );
1200             } else if( has_effect( effect_strong_antibiotic ) ) {
1201                 it.mod_duration( -1_turns ); //strong antibiotic reverses!
1202             } else if( has_effect( effect_antibiotic ) ) {
1203                 if( calendar::once_every( 8_turns ) ) {
1204                     it.mod_duration( 1_turns ); //normal antibiotic slows down progression by a factor of 8
1205                 }
1206             } else if( has_effect( effect_weak_antibiotic ) ) {
1207                 if( calendar::once_every( 2_turns ) ) {
1208                     it.mod_duration( 1_turns ); //weak antibiotic slows down by half
1209                 }
1210             } else {
1211                 it.mod_duration( 1_turns );
1212             }
1213         }
1214     } else if( id == effect_infected ) {
1215         bool recovered = false;
1216         // Recovery chance, use binomial distributions if balancing here.
1217         // See "bite" for balancing notes on this.
1218         if( dur % 10_turns == 0_turns )  {
1219             int recover_factor = 100;
1220             if( has_effect( effect_recover ) ) {
1221                 recover_factor -= get_effect_dur( effect_recover ) / 1_hours;
1222             }
1223             if( has_trait( trait_INFRESIST ) ) {
1224                 recover_factor += 200;
1225             }
1226             if( has_effect( effect_panacea ) ) {
1227                 recover_factor = 5184000;
1228             } else if( has_effect( effect_strong_antibiotic ) ) {
1229                 recover_factor += 400;
1230             } else if( has_effect( effect_antibiotic ) ) {
1231                 recover_factor += 200;
1232             } else if( has_effect( effect_weak_antibiotic ) ) {
1233                 recover_factor += 100;
1234             }
1235             recover_factor += get_healthy() / 10;
1236 
1237             if( x_in_y( recover_factor, 5184000 ) ) {
1238                 //~ %s is bodypart name.
1239                 add_msg_if_player( m_good, _( "Your %s wound begins to feel better!" ),
1240                                    body_part_name( bp ) );
1241                 add_effect( effect_recover, 4 * dur );
1242                 // Set ourselves up for removal
1243                 it.set_duration( 0_turns );
1244                 recovered = true;
1245             }
1246         }
1247         if( !recovered ) {
1248             // Death happens
1249             if( dur > 1_days ) {
1250                 add_msg_if_player( m_bad, _( "You succumb to the infection." ) );
1251                 get_event_bus().send<event_type::dies_of_infection>( getID() );
1252                 set_all_parts_hp_cur( 0 );
1253             } else if( has_effect( effect_strong_antibiotic ) ) {
1254                 it.mod_duration( -1_turns );
1255             } else if( has_effect( effect_antibiotic ) ) {
1256                 if( calendar::once_every( 8_turns ) ) {
1257                     it.mod_duration( 1_turns );
1258                 }
1259             } else if( has_effect( effect_weak_antibiotic ) ) {
1260                 if( calendar::once_every( 2_turns ) ) {
1261                     it.mod_duration( 1_turns );
1262                 }
1263             } else {
1264                 it.mod_duration( 1_turns );
1265             }
1266         }
1267     } else if( id == effect_lying_down ) {
1268         set_moves( 0 );
1269         if( can_sleep() ) {
1270             fall_asleep();
1271             // Set ourselves up for removal
1272             it.set_duration( 0_turns );
1273         }
1274         if( dur == 1_turns && !sleeping ) {
1275             add_msg_if_player( _( "You try to sleep, but can't…" ) );
1276         }
1277     } else if( id == effect_sleep ) {
1278         set_moves( 0 );
1279 #if defined(TILES)
1280         if( is_player() ) {
1281             SDL_PumpEvents();
1282         }
1283 #endif // TILES
1284 
1285         if( intense < 1 ) {
1286             it.set_intensity( 1 );
1287         } else if( intense < 24 ) {
1288             it.mod_intensity( 1 );
1289         }
1290 
1291         if( has_effect( effect_narcosis ) && get_fatigue() <= 25 ) {
1292             set_fatigue( 25 ); //Prevent us from waking up naturally while under anesthesia
1293         }
1294 
1295         if( get_fatigue() < -25 && it.get_duration() > 3_minutes && !has_effect( effect_narcosis ) ) {
1296             it.set_duration( 1_turns * dice( 3, 10 ) );
1297         }
1298 
1299         if( get_fatigue() <= 0 && get_fatigue() > -20 && !has_effect( effect_narcosis ) ) {
1300             mod_fatigue( -25 );
1301             if( get_sleep_deprivation() < SLEEP_DEPRIVATION_HARMLESS ) {
1302                 add_msg_if_player( m_good, _( "You feel well rested." ) );
1303             } else {
1304                 add_msg_if_player( m_warning,
1305                                    _( "You feel physically rested, but you haven't been able to catch up on your missed sleep yet." ) );
1306             }
1307             it.set_duration( 1_turns * dice( 3, 100 ) );
1308         }
1309 
1310         // TODO: Move this to update_needs when NPCs can mutate
1311         if( calendar::once_every( 10_minutes ) && ( has_trait( trait_CHLOROMORPH ) ||
1312                 has_trait( trait_M_SKIN3 ) || has_trait( trait_WATERSLEEP ) ) &&
1313             here.is_outside( pos() ) ) {
1314             if( has_trait( trait_CHLOROMORPH ) ) {
1315                 // Hunger and thirst fall before your Chloromorphic physiology!
1316                 if( g->natural_light_level( posz() ) >= 12 &&
1317                     get_weather().weather_id->sun_intensity >= sun_intensity_type::light ) {
1318                     if( get_hunger() >= -30 ) {
1319                         mod_hunger( -5 );
1320                         // photosynthesis warrants absorbing kcal directly
1321                         mod_stored_nutr( -5 );
1322                     }
1323                     if( get_thirst() >= -30 ) {
1324                         mod_thirst( -5 );
1325                     }
1326                 }
1327             }
1328             if( has_trait( trait_M_SKIN3 ) ) {
1329                 // Spores happen!
1330                 if( here.has_flag_ter_or_furn( "FUNGUS", pos() ) ) {
1331                     if( get_fatigue() >= 0 ) {
1332                         mod_fatigue( -5 ); // Local guides need less sleep on fungal soil
1333                     }
1334                     if( calendar::once_every( 1_hours ) ) {
1335                         spores(); // spawn some P O O F Y   B O I S
1336                     }
1337                 }
1338             }
1339             if( has_trait( trait_WATERSLEEP ) ) {
1340                 mod_fatigue( -3 ); // Fish sleep less in water
1341             }
1342         }
1343 
1344         // Check mutation category strengths to see if we're mutated enough to get a dream
1345         mutation_category_id highcat = get_highest_category();
1346         int highest = mutation_category_level[highcat];
1347 
1348         // Determine the strength of effects or dreams based upon category strength
1349         int strength = 0; // Category too weak for any effect or dream
1350         if( crossed_threshold() ) {
1351             strength = 4; // Post-human.
1352         } else if( highest >= 20 && highest < 35 ) {
1353             strength = 1; // Low strength
1354         } else if( highest >= 35 && highest < 50 ) {
1355             strength = 2; // Medium strength
1356         } else if( highest >= 50 ) {
1357             strength = 3; // High strength
1358         }
1359 
1360         // Get a dream if category strength is high enough.
1361         if( strength != 0 ) {
1362             //Once every 6 / 3 / 2 hours, with a bit of randomness
1363             if( calendar::once_every( 6_hours / strength ) && one_in( 3 ) ) {
1364                 // Select a dream
1365                 std::string dream = get_category_dream( highcat, strength );
1366                 if( !dream.empty() ) {
1367                     add_msg_if_player( dream );
1368                 }
1369                 // Mycus folks upgrade in their sleep.
1370                 if( has_trait( trait_THRESH_MYCUS ) ) {
1371                     if( one_in( 8 ) ) {
1372                         mutate_category( mutation_category_id( "MYCUS" ) );
1373                         mod_stored_nutr( 10 );
1374                         mod_thirst( 10 );
1375                         mod_fatigue( 5 );
1376                     }
1377                 }
1378             }
1379         }
1380 
1381         bool woke_up = false;
1382         int tirednessVal = rng( 5, 200 ) + rng( 0, std::abs( get_fatigue() * 2 * 5 ) );
1383         if( !is_blind() && !has_effect( effect_narcosis ) ) {
1384             if( !has_trait(
1385                     trait_SEESLEEP ) ) { // People who can see while sleeping are acclimated to the light.
1386                 if( has_trait( trait_HEAVYSLEEPER2 ) && !has_trait( trait_HIBERNATE ) ) {
1387                     // So you can too sleep through noon
1388                     if( ( tirednessVal * 1.25 ) < here.ambient_light_at( pos() ) && ( get_fatigue() < 10 ||
1389                             one_in( get_fatigue() / 2 ) ) ) {
1390                         add_msg_if_player( _( "It's too bright to sleep." ) );
1391                         // Set ourselves up for removal
1392                         it.set_duration( 0_turns );
1393                         woke_up = true;
1394                     }
1395                     // Ursine hibernators would likely do so indoors.  Plants, though, might be in the sun.
1396                 } else if( has_trait( trait_HIBERNATE ) ) {
1397                     if( ( tirednessVal * 5 ) < here.ambient_light_at( pos() ) && ( get_fatigue() < 10 ||
1398                             one_in( get_fatigue() / 2 ) ) ) {
1399                         add_msg_if_player( _( "It's too bright to sleep." ) );
1400                         // Set ourselves up for removal
1401                         it.set_duration( 0_turns );
1402                         woke_up = true;
1403                     }
1404                 } else if( tirednessVal < here.ambient_light_at( pos() ) && ( get_fatigue() < 10 ||
1405                            one_in( get_fatigue() / 2 ) ) ) {
1406                     add_msg_if_player( _( "It's too bright to sleep." ) );
1407                     // Set ourselves up for removal
1408                     it.set_duration( 0_turns );
1409                     woke_up = true;
1410                 }
1411             } else if( has_active_mutation( trait_SEESLEEP ) ) {
1412                 Creature *hostile_critter = g->is_hostile_very_close();
1413                 if( hostile_critter != nullptr ) {
1414                     add_msg_if_player( _( "You see %s approaching!" ),
1415                                        hostile_critter->disp_name() );
1416                     it.set_duration( 0_turns );
1417                     woke_up = true;
1418                 }
1419             }
1420         }
1421 
1422         // Have we already woken up?
1423         if( !woke_up && !has_effect( effect_narcosis ) ) {
1424             // Cold or heat may wake you up.
1425             // Player will sleep through cold or heat if fatigued enough
1426             for( const bodypart_id &bp : get_all_body_parts() ) {
1427                 const int curr_temp = get_part_temp_cur( bp );
1428                 if( curr_temp < BODYTEMP_VERY_COLD - get_fatigue() / 2 ) {
1429                     if( one_in( 30000 ) ) {
1430                         add_msg_if_player( _( "You toss and turn trying to keep warm." ) );
1431                     }
1432                     if( curr_temp < BODYTEMP_FREEZING - get_fatigue() / 2 ||
1433                         one_in( curr_temp * 6 + 30000 ) ) {
1434                         add_msg_if_player( m_bad, _( "It's too cold to sleep." ) );
1435                         // Set ourselves up for removal
1436                         it.set_duration( 0_turns );
1437                         woke_up = true;
1438                         break;
1439                     }
1440                 } else if( curr_temp > BODYTEMP_VERY_HOT + get_fatigue() / 2 ) {
1441                     if( one_in( 30000 ) ) {
1442                         add_msg_if_player( _( "You toss and turn in the heat." ) );
1443                     }
1444                     if( curr_temp > BODYTEMP_SCORCHING + get_fatigue() / 2 ||
1445                         one_in( 90000 - curr_temp ) ) {
1446                         add_msg_if_player( m_bad, _( "It's too hot to sleep." ) );
1447                         // Set ourselves up for removal
1448                         it.set_duration( 0_turns );
1449                         woke_up = true;
1450                         break;
1451                     }
1452                 }
1453             }
1454             if( has_trait( trait_SCHIZOPHRENIC ) && one_in( 43200 ) && is_player() ) {
1455                 if( one_in( 2 ) ) {
1456                     sound_hallu();
1457                 } else {
1458                     int max_count = rng( 1, 3 );
1459                     int count = 0;
1460                     for( const tripoint &mp : here.points_in_radius( pos(), 1 ) ) {
1461                         if( mp == pos() ) {
1462                             continue;
1463                         }
1464                         if( here.has_flag( "FLAT", mp ) &&
1465                             here.pl_sees( mp, 2 ) ) {
1466                             g->spawn_hallucination( mp );
1467                             if( ++count > max_count ) {
1468                                 break;
1469                             }
1470                         }
1471                     }
1472                 }
1473                 it.set_duration( 0_turns );
1474                 woke_up = true;
1475             }
1476         }
1477 
1478         // A bit of a hack: check if we are about to wake up for any reason, including regular
1479         // timing out of sleep
1480         if( dur == 1_turns || woke_up ) {
1481             wake_up();
1482         }
1483     } else if( id == effect_alarm_clock ) {
1484         if( in_sleep_state() ) {
1485             const bool asleep = has_effect( effect_sleep );
1486             if( has_flag( json_flag_ALARMCLOCK ) ) {
1487                 if( dur == 1_turns ) {
1488                     // Normal alarm is volume 12, tested against (2/3/6)d15 for
1489                     // normal/HEAVYSLEEPER/HEAVYSLEEPER2.
1490                     //
1491                     // It's much harder to ignore an alarm inside your own skull,
1492                     // so this uses an effective volume of 20.
1493                     const int volume = 20;
1494                     if( !asleep ) {
1495                         add_msg_if_player( _( "Your internal chronometer went off and you haven't slept a wink." ) );
1496                         activity.set_to_null();
1497                     } else if( ( !( has_trait( trait_HEAVYSLEEPER ) ||
1498                                     has_trait( trait_HEAVYSLEEPER2 ) ) &&
1499                                  dice( 2, 15 ) < volume ) ||
1500                                ( has_trait( trait_HEAVYSLEEPER ) && dice( 3, 15 ) < volume ) ||
1501                                ( has_trait( trait_HEAVYSLEEPER2 ) && dice( 6, 15 ) < volume ) ) {
1502                         // Secure the flag before wake_up() clears the effect
1503                         bool slept_through = has_effect( effect_slept_through_alarm );
1504                         wake_up();
1505                         if( slept_through ) {
1506                             add_msg_if_player( _( "Your internal chronometer finally wakes you up." ) );
1507                         } else {
1508                             add_msg_if_player( _( "Your internal chronometer wakes you up." ) );
1509                         }
1510                     } else {
1511                         if( !has_effect( effect_slept_through_alarm ) ) {
1512                             add_effect( effect_slept_through_alarm, 1_turns, true );
1513                         }
1514                         // 10 minute cyber-snooze
1515                         it.mod_duration( 10_minutes );
1516                     }
1517                 }
1518             } else {
1519                 if( asleep && dur == 1_turns ) {
1520                     if( !has_effect( effect_slept_through_alarm ) ) {
1521                         add_effect( effect_slept_through_alarm, 1_turns, true );
1522                     }
1523                     // 10 minute automatic snooze
1524                     it.mod_duration( 10_minutes );
1525                 } else if( dur == 2_turns ) {
1526                     // let the sound code handle the wake-up part
1527                     sounds::sound( pos(), 16, sounds::sound_t::alarm, _( "beep-beep-beep!" ), false, "tool",
1528                                    "alarm_clock" );
1529                 }
1530             }
1531         } else {
1532             if( dur == 1_turns ) {
1533                 if( player_character.has_alarm_clock() ) {
1534                     sounds::sound( player_character.pos(), 16, sounds::sound_t::alarm,
1535                                    _( "beep-beep-beep!" ), false, "tool", "alarm_clock" );
1536                     const std::string alarm = _( "Your alarm is going off." );
1537                     g->cancel_activity_or_ignore_query( distraction_type::noise, alarm );
1538                     add_msg( _( "Your alarm went off." ) );
1539                 }
1540             }
1541         }
1542     } else if( id == effect_mending ) {
1543         if( !is_limb_broken( bp ) ) {
1544             it.set_duration( 0_turns );
1545         }
1546     } else if( id == effect_disabled ) {
1547         if( !is_limb_broken( bp ) ) {
1548             // Just unpause, in case someone added it as a temporary effect (numbing poison etc.)
1549             it.unpause_effect();
1550         }
1551     } else if( id == effect_toxin_buildup ) {
1552         // Loosely based on toxic man-made compounds (mostly pesticides) which don't degrade
1553         // easily, leading to build-up in muscle and fat tissue through bioaccumulation.
1554         // Symptoms vary, and many are too long-term to be relevant in C:DDA (i.e. carcinogens),
1555         // but lowered immune response and neurotoxicity (i.e. seizures, migraines) are common.
1556 
1557         if( in_sleep_state() ) {
1558             return;
1559         }
1560         // Modifier for symptom frequency.
1561         // Each symptom is twice as frequent for each level of intensity above the one it first appears for.
1562         int mod = 1;
1563         switch( intense ) {
1564             case 3:
1565                 // Tonic-clonic seizure (full body convulsive seizure)
1566                 if( one_turn_in( 3_days ) && !has_effect( effect_valium ) ) {
1567                     add_msg_if_player( m_bad, _( "You lose control of your body as it begins to convulse!" ) );
1568                     time_duration td = rng( 30_seconds, 4_minutes );
1569                     add_effect( effect_motor_seizure, td );
1570                     add_effect( effect_downed, td );
1571                     add_effect( effect_stunned, td );
1572                     if( one_in( 3 ) ) {
1573                         add_msg_if_player( m_bad, _( "You lose consciousness!" ) );
1574                         fall_asleep( td );
1575                     }
1576                 }
1577                 mod *= 2;
1578             /* fallthrough */
1579             case 2:
1580                 // Myoclonic seizure (muscle spasm)
1581                 if( one_turn_in( 2_hours / mod ) && !has_effect( effect_valium ) ) {
1582                     std::string limb = random_entry<std::vector<std::string>>( {
1583                         translate_marker( "arm" ), translate_marker( "hand" ), translate_marker( "leg" )
1584                     } );
1585                     add_msg_if_player( m_bad, string_format(
1586                                            _( "Your %s suddenly jerks in an unexpected direction!" ), _( limb ) ) );
1587                     if( limb == "arm" ) {
1588                         mod_dex_bonus( -8 );
1589                         recoil = MAX_RECOIL;
1590                     } else if( limb == "hand" ) {
1591                         if( is_armed() && can_drop( weapon ).success() ) {
1592                             if( dice( 4, 4 ) > get_dex() ) {
1593                                 cancel_activity();  //Prevent segfaults from activities trying to access missing item
1594                                 put_into_vehicle_or_drop( *this, item_drop_reason::tumbling, { remove_weapon() } );
1595                             } else {
1596                                 add_msg_if_player( m_neutral, _( "However, you manage to keep hold of your weapon." ) );
1597                             }
1598                         }
1599                     } else if( limb == "leg" ) {
1600                         if( dice( 4, 4 ) > get_dex() ) {
1601                             add_effect( effect_downed, rng( 5_seconds, 10_seconds ) );
1602                         } else {
1603                             add_msg_if_player( m_neutral, _( "However, you manage to keep your footing." ) );
1604                         }
1605                     }
1606                 }
1607                 // Atonic seizure (a.k.a. drop seizure)
1608                 if( one_turn_in( 2_days / mod ) && !has_effect( effect_valium ) ) {
1609                     add_msg_if_player( m_bad,
1610                                        _( "You suddenly lose all muscle tone, and can't support your own weight!" ) );
1611                     add_effect( effect_motor_seizure, rng( 1_seconds, 2_seconds ) );
1612                     add_effect( effect_downed, rng( 5_seconds, 10_seconds ) );
1613                 }
1614                 mod *= 2;
1615             /* fallthrough */
1616             case 1:
1617                 // Migraine
1618                 if( one_turn_in( 2_days / mod ) ) {
1619                     add_msg_if_player( m_bad, _( "You have a splitting headache." ) );
1620                     mod_pain( 12 );
1621                 }
1622 
1623                 break;
1624         }
1625     }
1626 }
1627