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