//OTHER DEBUFFS /datum/status_effect/his_wrath //does minor damage over time unless holding His Grace id = "his_wrath" duration = -1 tick_interval = 4 alert_type = /obj/screen/alert/status_effect/his_wrath /obj/screen/alert/status_effect/his_wrath name = "His Wrath" desc = "You fled from His Grace instead of feeding Him, and now you suffer." icon_state = "his_grace" alerttooltipstyle = "hisgrace" /datum/status_effect/his_wrath/tick() var/list/held_items = list() held_items += owner.l_hand held_items += owner.r_hand for(var/obj/item/his_grace/HG in held_items) qdel(src) return owner.adjustBruteLoss(0.1) owner.adjustFireLoss(0.1) owner.adjustToxLoss(0.2) /datum/status_effect/cultghost //is a cult ghost and can't use manifest runes, can see ghosts and dies if too far from summoner id = "cult_ghost" duration = -1 alert_type = null var/damage = 7.5 var/source_UID /datum/status_effect/cultghost/on_creation(mob/living/new_owner, mob/living/source) . = ..() source_UID = source.UID() /datum/status_effect/cultghost/tick() if(owner.reagents) owner.reagents.del_reagent("holywater") //can't be deconverted var/mob/living/summoner = locateUID(source_UID) if(get_dist_euclidian(summoner, owner) < 21) return owner.adjustBruteLoss(damage) to_chat(owner, "You are too far away from the summoner!") /datum/status_effect/crusher_mark id = "crusher_mark" duration = 300 //if you leave for 30 seconds you lose the mark, deal with it status_type = STATUS_EFFECT_REPLACE alert_type = null var/mutable_appearance/marked_underlay var/obj/item/kinetic_crusher/hammer_synced /datum/status_effect/crusher_mark/on_creation(mob/living/new_owner, obj/item/kinetic_crusher/new_hammer_synced) . = ..() if(.) hammer_synced = new_hammer_synced /datum/status_effect/crusher_mark/on_apply() if(owner.mob_size >= MOB_SIZE_LARGE) marked_underlay = mutable_appearance('icons/effects/effects.dmi', "shield2") marked_underlay.pixel_x = -owner.pixel_x marked_underlay.pixel_y = -owner.pixel_y owner.underlays += marked_underlay return TRUE return FALSE /datum/status_effect/crusher_mark/Destroy() hammer_synced = null if(owner) owner.underlays -= marked_underlay QDEL_NULL(marked_underlay) return ..() /datum/status_effect/crusher_mark/be_replaced() owner.underlays -= marked_underlay //if this is being called, we should have an owner at this point. ..() /datum/status_effect/saw_bleed id = "saw_bleed" duration = -1 //removed under specific conditions tick_interval = 6 alert_type = null var/mutable_appearance/bleed_overlay var/mutable_appearance/bleed_underlay var/bleed_amount = 3 var/bleed_buildup = 3 var/delay_before_decay = 5 var/bleed_damage = 200 var/needs_to_bleed = FALSE var/bleed_cap = 10 /datum/status_effect/saw_bleed/Destroy() if(owner) owner.cut_overlay(bleed_overlay) owner.underlays -= bleed_underlay QDEL_NULL(bleed_overlay) return ..() /datum/status_effect/saw_bleed/on_apply() if(owner.stat == DEAD) return FALSE bleed_overlay = mutable_appearance('icons/effects/bleed.dmi', "bleed[bleed_amount]") bleed_underlay = mutable_appearance('icons/effects/bleed.dmi', "bleed[bleed_amount]") var/icon/I = icon(owner.icon, owner.icon_state, owner.dir) var/icon_height = I.Height() bleed_overlay.pixel_x = -owner.pixel_x bleed_overlay.pixel_y = FLOOR(icon_height * 0.25, 1) bleed_overlay.transform = matrix() * (icon_height/world.icon_size) //scale the bleed overlay's size based on the target's icon size bleed_underlay.pixel_x = -owner.pixel_x bleed_underlay.transform = matrix() * (icon_height/world.icon_size) * 3 bleed_underlay.alpha = 40 owner.add_overlay(bleed_overlay) owner.underlays += bleed_underlay return ..() /datum/status_effect/saw_bleed/tick() if(owner.stat == DEAD) qdel(src) else add_bleed(-1) /datum/status_effect/saw_bleed/proc/add_bleed(amount) owner.cut_overlay(bleed_overlay) owner.underlays -= bleed_underlay bleed_amount += amount if(bleed_amount) if(bleed_amount >= bleed_cap) needs_to_bleed = TRUE qdel(src) else if(amount > 0) tick_interval += delay_before_decay bleed_overlay.icon_state = "bleed[bleed_amount]" bleed_underlay.icon_state = "bleed[bleed_amount]" owner.add_overlay(bleed_overlay) owner.underlays += bleed_underlay else qdel(src) /datum/status_effect/saw_bleed/on_remove() if(needs_to_bleed) var/turf/T = get_turf(owner) new /obj/effect/temp_visual/bleed/explode(T) for(var/d in GLOB.alldirs) new /obj/effect/temp_visual/dir_setting/bloodsplatter(T, d) playsound(T, "desceration", 200, 1, -1) owner.adjustBruteLoss(bleed_damage) else new /obj/effect/temp_visual/bleed(get_turf(owner)) /datum/status_effect/saw_bleed/bloodletting id = "bloodletting" bleed_cap = 7 bleed_damage = 25 //Seems weak (it is) but it also works on humans and bypasses armor SOOOO bleed_amount = 6 /datum/status_effect/stacking/ground_pound id = "ground_pound" tick_interval = 5 SECONDS stack_threshold = 3 max_stacks = 3 reset_ticks_on_stack = TRUE var/mob/living/simple_animal/hostile/asteroid/big_legion/latest_attacker /datum/status_effect/stacking/ground_pound/on_creation(mob/living/new_owner, stacks_to_apply, mob/living/attacker) . = ..() if(.) latest_attacker = attacker /datum/status_effect/stacking/ground_pound/add_stacks(stacks_added, mob/living/attacker) . = ..() if(.) latest_attacker = attacker if(stacks != stack_threshold) return TRUE /datum/status_effect/stacking/ground_pound/stacks_consumed_effect() flick("legion-smash", latest_attacker) addtimer(CALLBACK(latest_attacker, TYPE_PROC_REF(/mob/living/simple_animal/hostile/asteroid/big_legion, throw_mobs)), 1 SECONDS) /datum/status_effect/stacking/ground_pound/on_remove() latest_attacker = null /datum/status_effect/teleport_sickness id = "teleportation sickness" duration = 30 SECONDS status_type = STATUS_EFFECT_REFRESH alert_type = /obj/screen/alert/status_effect/teleport_sickness var/teleports = 1 /obj/screen/alert/status_effect/teleport_sickness name = "Teleportation sickness" desc = "You feel like you are going to throw up with all this teleporting." icon_state = "bluespace" /datum/status_effect/teleport_sickness/refresh() . = ..() if(ishuman(owner)) var/mob/living/carbon/human/M = owner teleports++ if(teleports < 3) return if(teleports < 6) to_chat(M, "You feel a bit sick!") M.vomit(lost_nutrition = 15, blood = 0, should_confuse = FALSE, distance = 0, message = 1) M.Weaken(2 SECONDS) else to_chat(M, "You feel really sick!") M.adjustBruteLoss(rand(0, teleports * 2)) M.vomit(lost_nutrition = 30, blood = 0, should_confuse = FALSE, distance = 0, message = 1) M.Weaken(6 SECONDS) /datum/status_effect/pacifism id = "pacifism_debuff" alert_type = null duration = 40 SECONDS /datum/status_effect/pacifism/on_apply() ADD_TRAIT(owner, TRAIT_PACIFISM, id) return ..() /datum/status_effect/pacifism/on_remove() REMOVE_TRAIT(owner, TRAIT_PACIFISM, id) /datum/status_effect/pacifism/batterer id = "pacifism_debuff_batterer" alert_type = null duration = 10 SECONDS // used to track if hitting someone with a cult dagger/sword should stamina crit. /datum/status_effect/cult_stun_mark id = "cult_stun" duration = 10 SECONDS // when the knockdown ends, the mark disappears. alert_type = null var/mutable_appearance/overlay /datum/status_effect/cult_stun_mark/on_apply() . = ..() if(!ishuman(owner)) return overlay = mutable_appearance('icons/effects/cult_effects.dmi', "cult-mark", ABOVE_MOB_LAYER) var/mob/living/carbon/human/H = owner H.add_overlay(overlay) /datum/status_effect/cult_stun_mark/on_remove() owner.cut_overlay(overlay) /datum/status_effect/cult_stun_mark/proc/trigger() owner.apply_damage(60, STAMINA) owner.Silence(6 SECONDS) // refresh the silence qdel(src) /datum/status_effect/bluespace_slowdown id = "bluespace_slowdown" alert_type = null duration = 15 SECONDS /datum/status_effect/bluespace_slowdown/on_apply() owner.next_move_modifier *= 2 return ..() /datum/status_effect/bluespace_slowdown/on_remove() owner.next_move_modifier /= 2 /datum/status_effect/shadow_boxing id = "shadow barrage" alert_type = null duration = 10 SECONDS tick_interval = 0.4 SECONDS var/damage = 8 var/source_UID /datum/status_effect/shadow_boxing/on_creation(mob/living/new_owner, mob/living/source) . = ..() source_UID = source.UID() /datum/status_effect/shadow_boxing/tick() var/mob/living/attacker = locateUID(source_UID) if(attacker in view(owner, 2)) attacker.do_attack_animation(owner, ATTACK_EFFECT_PUNCH) owner.apply_damage(damage, BRUTE) shadow_to_animation(get_turf(attacker), get_turf(owner), attacker) /datum/status_effect/cling_tentacle id = "cling_tentacle" alert_type = null duration = 3 SECONDS /datum/status_effect/cling_tentacle/on_apply() ADD_TRAIT(owner, TRAIT_IMMOBILIZED, "[id]") return ..() /datum/status_effect/cling_tentacle/on_remove() REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, "[id]") // start of `living` level status procs. /** * # Confusion * * Prevents moving straight, sometimes changing movement direction at random. * Decays at a rate of 1 per second. */ /datum/status_effect/transient/confusion id = "confusion" var/image/overlay /datum/status_effect/transient/confusion/tick() . = ..() if(!.) return if(!owner.stat) //add or remove the overlay if they are alive or unconscious/dead add_overlay() else if(overlay) owner.cut_overlay(overlay) overlay = null /datum/status_effect/transient/confusion/proc/add_overlay() if(overlay) return var/matrix/M = matrix() M.Scale(0.6) overlay = image('icons/effects/effects.dmi', "confusion", pixel_y = 20) overlay.transform = M owner.add_overlay(overlay) /datum/status_effect/transient/confusion/on_remove() owner.cut_overlay(overlay) overlay = null return ..() /** * # Dizziness * * Slightly offsets the client's screen randomly every tick. * Decays at a rate of 1 per second, or 5 when resting. */ /datum/status_effect/transient/dizziness id = "dizziness" var/px_diff = 0 var/py_diff = 0 /datum/status_effect/transient/dizziness/on_remove() if(owner.client) // smoothly back to normal animate(owner.client, 0.2 SECONDS, pixel_x = -px_diff, pixel_y = -py_diff, flags = ANIMATION_PARALLEL) return ..() /datum/status_effect/transient/dizziness/tick() . = ..() if(!.) return var/dir = sin(world.time) var/amplitude = min(strength * 0.003, 32) px_diff = cos(world.time * 3) * amplitude * dir py_diff = sin(world.time * 3) * amplitude * dir owner.client?.pixel_x = px_diff owner.client?.pixel_y = py_diff /datum/status_effect/transient/dizziness/calc_decay() return (-0.2 + (IS_HORIZONTAL(owner) ? -0.8 : 0)) SECONDS /** * # Drowsiness * * Slows down and causes eye blur, with a 5% chance of falling asleep for a short time. * Decays at a rate of 1 per second, or 5 when resting. */ /datum/status_effect/transient/drowsiness id = "drowsiness" /datum/status_effect/transient/drowsiness/tick() . = ..() if(!.) return owner.EyeBlurry(4 SECONDS) if(prob(0.5)) owner.AdjustSleeping(2 SECONDS) owner.Paralyse(10 SECONDS) /datum/status_effect/transient/drowsiness/calc_decay() return (-0.2 + (IS_HORIZONTAL(owner) ? -0.8 : 0)) SECONDS /** * # Drukenness * * Causes a myriad of status effects and other afflictions the stronger it is. * Decays at a rate of 1 per second if no alcohol remains inside. */ /datum/status_effect/transient/drunkenness id = "drunkenness" var/alert_thrown = FALSE // the number of seconds of the status effect required for each effect to kick in. #define THRESHOLD_SLUR 60 SECONDS #define THRESHOLD_BRAWLING 60 SECONDS #define THRESHOLD_CONFUSION 80 SECONDS #define THRESHOLD_SPARK 100 SECONDS #define THRESHOLD_VOMIT 120 SECONDS #define THRESHOLD_BLUR 150 SECONDS #define THRESHOLD_COLLAPSE 150 SECONDS #define THRESHOLD_FAINT 180 SECONDS #define THRESHOLD_BRAIN_DAMAGE 240 SECONDS #define DRUNK_BRAWLING /datum/martial_art/drunk_brawling /datum/status_effect/transient/drunkenness/on_remove() if(alert_thrown) alert_thrown = FALSE owner.clear_alert("drunk") owner.sound_environment_override = SOUND_ENVIRONMENT_NONE if(owner.mind && istype(owner.mind.martial_art, DRUNK_BRAWLING)) owner.mind.martial_art.remove(owner) return ..() /datum/status_effect/transient/drunkenness/tick() . = ..() if(!.) return // Adjust actual drunkenness based on trait and organ presence var/alcohol_resistance = 1 var/actual_strength = strength var/datum/mind/M = owner.mind var/is_robot = ismachineperson(owner) || issilicon(owner) if(HAS_TRAIT(owner, TRAIT_ALCOHOL_TOLERANCE)) alcohol_resistance = 2 actual_strength /= alcohol_resistance var/obj/item/organ/internal/liver/L if(!is_robot) L = owner.get_int_organ(/obj/item/organ/internal/liver) var/liver_multiplier = 5 // no liver? get shitfaced if(L) liver_multiplier = L.alcohol_intensity actual_strength *= liver_multiplier // THRESHOLD_SLUR (60 SECONDS) if(actual_strength >= THRESHOLD_SLUR) owner.Slur(actual_strength) if(!alert_thrown) alert_thrown = TRUE owner.throw_alert("drunk", /obj/screen/alert/drunk) owner.sound_environment_override = SOUND_ENVIRONMENT_PSYCHOTIC // THRESHOLD_BRAWLING (60 SECONDS) if(M) if(actual_strength >= THRESHOLD_BRAWLING) if(!istype(M.martial_art, DRUNK_BRAWLING)) var/datum/martial_art/drunk_brawling/MA = new MA.teach(owner, TRUE) else if(istype(M.martial_art, DRUNK_BRAWLING)) M.martial_art.remove(owner) // THRESHOLD_CONFUSION (80 SECONDS) if(actual_strength >= THRESHOLD_CONFUSION && prob(0.33)) owner.AdjustConfused(6 SECONDS / alcohol_resistance, bound_lower = 2 SECONDS, bound_upper = 1 MINUTES) // THRESHOLD_SPARK (100 SECONDS) if(is_robot && actual_strength >= THRESHOLD_SPARK && prob(0.25)) do_sparks(3, 1, owner) // THRESHOLD_VOMIT (120 SECONDS) if(!is_robot && actual_strength >= THRESHOLD_VOMIT && prob(0.08)) owner.fakevomit() // THRESHOLD_BLUR (150 SECONDS) if(actual_strength >= THRESHOLD_BLUR) owner.EyeBlurry(20 SECONDS / alcohol_resistance) // THRESHOLD_COLLAPSE (150 SECONDS) if(actual_strength >= THRESHOLD_COLLAPSE && prob(0.1)) owner.emote("collapse") do_sparks(3, 1, src) // THRESHOLD_FAINT (180 SECONDS) if(actual_strength >= THRESHOLD_FAINT && prob(0.1)) owner.Paralyse(10 SECONDS / alcohol_resistance) owner.Drowsy(60 SECONDS / alcohol_resistance) if(L) L.receive_damage(1, TRUE) if(!is_robot) owner.adjustToxLoss(1) // THRESHOLD_BRAIN_DAMAGE (240 SECONDS) if(actual_strength >= THRESHOLD_BRAIN_DAMAGE && prob(0.1)) owner.adjustBrainLoss(1) #undef THRESHOLD_SLUR #undef THRESHOLD_BRAWLING #undef THRESHOLD_CONFUSION #undef THRESHOLD_SPARK #undef THRESHOLD_VOMIT #undef THRESHOLD_BLUR #undef THRESHOLD_COLLAPSE #undef THRESHOLD_FAINT #undef THRESHOLD_BRAIN_DAMAGE #undef DRUNK_BRAWLING /datum/status_effect/transient/drunkenness/calc_decay() if(ishuman(owner)) var/mob/living/carbon/human/H = owner if(H.has_booze()) return 0 return -0.2 SECONDS /datum/status_effect/transient/cult_slurring id = "cult_slurring" /datum/status_effect/incapacitating tick_interval = 0 status_type = STATUS_EFFECT_REPLACE alert_type = null var/needs_update_stat = FALSE /datum/status_effect/incapacitating/on_creation(mob/living/new_owner, set_duration) if(isnum(set_duration)) if(ishuman(new_owner)) var/mob/living/carbon/human/H = new_owner set_duration = H.dna.species.spec_stun(H, set_duration) duration = set_duration if(!duration) return FALSE . = ..() if(. && (needs_update_stat || issilicon(owner))) owner.update_stat() /datum/status_effect/incapacitating/on_remove() if(needs_update_stat || issilicon(owner)) //silicons need stat updates owner.update_stat() return ..() //FLOORED - forces the victim prone. /datum/status_effect/incapacitating/floored id = "floored" /datum/status_effect/incapacitating/floored/on_apply() . = ..() if(!.) return ADD_TRAIT(owner, TRAIT_FLOORED, "[id]") /datum/status_effect/incapacitating/floored/on_remove() REMOVE_TRAIT(owner, TRAIT_FLOORED, "[id]") return ..() //STUN - prevents movement and actions, victim stays standing /datum/status_effect/incapacitating/stun id = "stun" /datum/status_effect/incapacitating/stun/on_apply() . = ..() if(!.) return ADD_TRAIT(owner, TRAIT_IMMOBILIZED, "[id]") ADD_TRAIT(owner, TRAIT_HANDS_BLOCKED, "[id]") /datum/status_effect/incapacitating/stun/on_remove() REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, "[id]") REMOVE_TRAIT(owner, TRAIT_HANDS_BLOCKED, "[id]") return ..() //IMMOBILIZED - prevents movement, victim can still stand and act /datum/status_effect/incapacitating/immobilized id = "immobilized" /datum/status_effect/incapacitating/immobilized/on_apply() . = ..() if(!.) return ADD_TRAIT(owner, TRAIT_IMMOBILIZED, "[id]") /datum/status_effect/incapacitating/immobilized/on_remove() REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, "[id]") return ..() //WEAKENED - prevents movement and action, victim falls over /datum/status_effect/incapacitating/weakened id = "weakened" /datum/status_effect/incapacitating/weakened/on_apply() . = ..() if(!.) return ADD_TRAIT(owner, TRAIT_IMMOBILIZED, "[id]") ADD_TRAIT(owner, TRAIT_FLOORED, "[id]") ADD_TRAIT(owner, TRAIT_HANDS_BLOCKED, "[id]") /datum/status_effect/incapacitating/weakened/on_remove() REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, "[id]") REMOVE_TRAIT(owner, TRAIT_FLOORED, "[id]") REMOVE_TRAIT(owner, TRAIT_HANDS_BLOCKED, "[id]") return ..() //PARALYZED - prevents movement and action, victim falls over, victim cannot hear or see. /datum/status_effect/incapacitating/paralyzed id = "paralyzed" needs_update_stat = TRUE /datum/status_effect/incapacitating/paralyzed/on_apply() . = ..() if(!.) return ADD_TRAIT(owner, TRAIT_KNOCKEDOUT, "[id]") /datum/status_effect/incapacitating/paralyzed/on_remove() REMOVE_TRAIT(owner, TRAIT_KNOCKEDOUT, "[id]") return ..() //SLEEPING - victim falls over, cannot act, cannot see or hear, heals under certain conditions. /datum/status_effect/incapacitating/sleeping id = "sleeping" tick_interval = 2 SECONDS needs_update_stat = TRUE /// Whether we decided to take a nap on our own. /// As opposed to being hard knocked out with N2O or similar. var/voluntary = FALSE /datum/status_effect/incapacitating/sleeping/on_creation(mob/living/new_owner, set_duration, voluntary = FALSE) ..() src.voluntary = voluntary /datum/status_effect/incapacitating/sleeping/on_apply() . = ..() if(!.) return ADD_TRAIT(owner, TRAIT_KNOCKEDOUT, "[id]") /datum/status_effect/incapacitating/sleeping/on_remove() REMOVE_TRAIT(owner, TRAIT_KNOCKEDOUT, "[id]") return ..() /datum/status_effect/incapacitating/sleeping/tick() if(!iscarbon(owner)) return var/mob/living/carbon/dreamer = owner if(dreamer.mind?.has_antag_datum(/datum/antagonist/vampire)) if(istype(dreamer.loc, /obj/structure/closet/coffin)) dreamer.adjustBruteLoss(-1, FALSE) dreamer.adjustFireLoss(-1, FALSE) dreamer.adjustToxLoss(-1) dreamer.handle_dreams() dreamer.adjustStaminaLoss(-10) var/comfort = 1 if(istype(dreamer.buckled, /obj/structure/bed)) var/obj/structure/bed/bed = dreamer.buckled comfort += bed.comfort for(var/obj/item/bedsheet/bedsheet in range(dreamer.loc,0)) if(bedsheet.loc != dreamer.loc) //bedsheets in your backpack/neck don't give you comfort continue comfort += bedsheet.comfort break //Only count the first bedsheet if(dreamer.get_drunkenness() > 0) comfort += 1 //Aren't naps SO much better when drunk? dreamer.AdjustDrunk(-0.4 SECONDS * comfort) //reduce drunkenness while sleeping. if(comfort > 1 && prob(3))//You don't heal if you're just sleeping on the floor without a blanket. dreamer.adjustBruteLoss(-1 * comfort, FALSE) dreamer.adjustFireLoss(-1 * comfort) if(prob(10) && dreamer.health && dreamer.health_hud_override != HEALTH_HUD_OVERRIDE_CRIT) dreamer.emote("snore") //SLOWED - slows down the victim for a duration and a given slowdown value. /datum/status_effect/incapacitating/slowed id = "slowed" var/slowdown_value = 10 // defaults to this value if none is specified /datum/status_effect/incapacitating/slowed/on_creation(mob/living/new_owner, set_duration, _slowdown_value) . = ..() if(isnum(_slowdown_value)) slowdown_value = _slowdown_value /datum/status_effect/transient/silence id = "silenced" /datum/status_effect/transient/silence/on_apply() . = ..() ADD_TRAIT(owner, TRAIT_MUTE, id) /datum/status_effect/transient/silence/on_remove() . = ..() REMOVE_TRAIT(owner, TRAIT_MUTE, id) /datum/status_effect/transient/silence/absolute // this one will mute all emote sounds including gasps id = "abssilenced" /datum/status_effect/transient/deaf id = "deafened" /datum/status_effect/transient/deaf/on_apply() . = ..() ADD_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE) /datum/status_effect/transient/deaf/on_remove() . = ..() REMOVE_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE) /datum/status_effect/transient/no_oxy_heal id = "no_oxy_heal" /datum/status_effect/transient/jittery id = "jittering" /datum/status_effect/transient/jittery/on_apply() . = ..() owner.do_jitter_animation(strength / 20, 1) /datum/status_effect/transient/jittery/tick() . = ..() if(!.) return owner.do_jitter_animation(strength / 20, 1) /datum/status_effect/transient/jittery/calc_decay() return (-0.2 + (IS_HORIZONTAL(owner) ? -0.8 : 0)) SECONDS /datum/status_effect/transient/stammering id = "stammer" /datum/status_effect/transient/slurring id = "slurring" /datum/status_effect/transient/lose_breath id = "lose_breath" #define HALLUCINATE_COOLDOWN_MIN 20 SECONDS #define HALLUCINATE_COOLDOWN_MAX 50 SECONDS /// This is multiplied with [/mob/var/hallucination] to determine the final cooldown. A higher hallucination value means shorter cooldown. #define HALLUCINATE_COOLDOWN_FACTOR 0.003 /// Percentage defining the chance at which an hallucination may spawn past the cooldown. #define HALLUCINATE_CHANCE 80 // Severity weights, should sum up to 100! #define HALLUCINATE_MINOR_WEIGHT 60 #define HALLUCINATE_MODERATE_WEIGHT 30 #define HALLUCINATE_MAJOR_WEIGHT 10 /datum/status_effect/transient/hallucination id = "hallucination" var/next_hallucination = 0 /datum/status_effect/transient/hallucination/tick() . = ..() if(!.) return if(next_hallucination > world.time) return next_hallucination = world.time + rand(HALLUCINATE_COOLDOWN_MIN, HALLUCINATE_COOLDOWN_MAX) / (strength * HALLUCINATE_COOLDOWN_FACTOR) if(!prob(HALLUCINATE_CHANCE)) return // Pick a severity var/severity = HALLUCINATE_MINOR switch(rand(100)) if(0 to HALLUCINATE_MINOR_WEIGHT) severity = HALLUCINATE_MINOR if((HALLUCINATE_MINOR_WEIGHT + 1) to (HALLUCINATE_MINOR_WEIGHT + HALLUCINATE_MODERATE_WEIGHT)) severity = HALLUCINATE_MODERATE if((HALLUCINATE_MINOR_WEIGHT + HALLUCINATE_MODERATE_WEIGHT + 1) to 100) severity = HALLUCINATE_MAJOR hallucinate(pickweight(GLOB.hallucinations[severity])) /** * Spawns an hallucination for the mob. * * Arguments: * * H - The type path of the hallucination to spawn. */ /datum/status_effect/transient/hallucination/proc/hallucinate(hallucination_type) ASSERT(ispath(hallucination_type)) if(owner.ckey) add_attack_logs(null, owner, "Received hallucination [hallucination_type]", ATKLOG_ALL) return new hallucination_type(get_turf(owner), owner) #undef HALLUCINATE_COOLDOWN_MIN #undef HALLUCINATE_COOLDOWN_MAX #undef HALLUCINATE_COOLDOWN_FACTOR #undef HALLUCINATE_CHANCE #undef HALLUCINATE_MINOR_WEIGHT #undef HALLUCINATE_MODERATE_WEIGHT #undef HALLUCINATE_MAJOR_WEIGHT /datum/status_effect/transient/eye_blurry id = "eye_blurry" /datum/status_effect/transient/eye_blurry/on_apply() owner.update_blurry_effects() . = ..() /datum/status_effect/transient/eye_blurry/on_remove() owner.update_blurry_effects() /datum/status_effect/transient/eye_blurry/calc_decay() if(ishuman(owner)) var/mob/living/carbon/human/H = owner if(isnull(H.dna.species.vision_organ)) //species has no eyes return ..() var/obj/item/organ/vision = H.get_int_organ(H.dna.species.vision_organ) if(!vision || vision.is_bruised() || vision.is_broken()) // doesn't decay if you have damaged eyesight. return 0 if(istype(H.glasses, /obj/item/clothing/glasses/sunglasses/blindfold)) // decays faster if you rest your eyes with a blindfold. return -1 SECONDS return ..() //default decay rate /datum/status_effect/transient/blindness id = "blindness" /datum/status_effect/transient/blindness/on_apply() . = ..() owner.update_blind_effects() /datum/status_effect/transient/blindness/on_remove() owner.update_blind_effects() /datum/status_effect/transient/blindness/calc_decay() if(ishuman(owner)) var/mob/living/carbon/human/H = owner if(HAS_TRAIT(owner, TRAIT_BLIND)) return 0 if(isnull(H.dna.species.vision_organ)) // species that have no eyes return ..() var/obj/item/organ/vision = H.get_int_organ(H.dna.species.vision_organ) if(!vision || vision.is_broken() || vision.is_bruised()) //got no eyes or broken eyes return 0 return ..() //default decay rate /datum/status_effect/transient/drugged id = "drugged" /datum/status_effect/transient/drugged/on_apply() . = ..() owner.update_druggy_effects() /datum/status_effect/transient/drugged/on_remove() owner.update_druggy_effects() #define FAKE_COLD 1 #define FAKE_FOOD_POISONING 2 #define FAKE_RETRO_VIRUS 3 #define FAKE_TURBERCULOSIS 4 #define FAKE_BRAINROT 5 /datum/status_effect/fake_virus id = "fake_virus" duration = 3 MINUTES status_type = STATUS_EFFECT_REPLACE tick_interval = 2 alert_type = null /// So you dont get the most intense messages immediately var/msg_stage = 0 /// Which disease we are going to fake? var/current_fake_disease /// Fake virus messages by with three stages var/list/fake_msg /// Fake virus emotes with three stages var/list/fake_emote /datum/status_effect/fake_virus/on_creation() current_fake_disease = pick(FAKE_COLD, FAKE_FOOD_POISONING, FAKE_RETRO_VIRUS, FAKE_TURBERCULOSIS, FAKE_BRAINROT) switch(current_fake_disease) if(FAKE_COLD) fake_msg = list( list("Your throat feels sore.", "Mucous runs down the back of your throat."), list("Your muscles ache.", "Your stomach hurts."), list("Your muscles ache.", "Your stomach hurts.") ) fake_emote = list( list("sneeze", "cough"), list("sneeze", "cough"), list("sneeze", "cough") ) if(FAKE_FOOD_POISONING) fake_msg = list( list("Your stomach feels weird.", "You feel queasy."), list("Your stomach aches.", "You feel nauseous."), list("Your stomach hurts.", "You feel sick.") ) fake_emote = list( list(), list("groan"), list("groan", "moan") ) if(FAKE_RETRO_VIRUS) fake_msg = list( list("Your head hurts.", "You feel a tingling sensation in your chest.", "You feel angry."), list("Your skin feels loose.", "You feel very strange.", "You feel a stabbing pain in your head!", "Your stomach churns."), list("Your entire body vibrates.") ) fake_emote = list( list(), list(), list() ) if(FAKE_TURBERCULOSIS) fake_msg = list( list("Your chest hurts.", "Your stomach violently rumbles!", "You feel a cold sweat form."), list("You feel a sharp pain from your lower chest!", "You feel air escape from your lungs painfully."), list("You feel uncomfortably hot...", "You feel like unzipping your jumpsuit", "You feel like taking off some clothes...") ) fake_emote = list( list("cough"), list("gasp"), list() ) else // FAKE_BRAINROT fake_msg = list( list("You don't feel like yourself."), list("Your try to remember something important...but can't."), list("Strange buzzing fills your head, removing all thoughts.") ) fake_emote = list( list("blink", "yawn"), list("stare", "drool"), list("stare", "drool") ) . = ..() /datum/status_effect/fake_virus/tick() var/selected_fake_msg var/selected_fake_emote switch(msg_stage) if(0 to 300) if(prob(1)) // First stage starts slow, stage 2 and 3 trigger fake msgs/emotes twice as often if(prob(50) || !length(fake_emote[1])) // 50% chance to trigger either a msg or emote, 100% if it doesnt have an emote selected_fake_msg = safepick(fake_msg[1]) else selected_fake_emote = safepick(fake_emote[1]) if(301 to 600) if(prob(2)) if(prob(50) || !length(fake_emote[2])) selected_fake_msg = safepick(fake_msg[2]) else selected_fake_emote = safepick(fake_emote[2]) else if(prob(2)) if(prob(50) || !length(fake_emote[3])) selected_fake_msg = safepick(fake_msg[3]) else selected_fake_emote = safepick(fake_emote[3]) if(selected_fake_msg) to_chat(owner, selected_fake_msg) else if(selected_fake_emote) owner.emote(selected_fake_emote) msg_stage++ #undef FAKE_COLD #undef FAKE_FOOD_POISONING #undef FAKE_RETRO_VIRUS #undef FAKE_TURBERCULOSIS #undef FAKE_BRAINROT /datum/status_effect/cryo_beam id = "cryo beam" alert_type = null duration = -1 //Kill it, get out of sight, or be killed. Jump boots are *required* tick_interval = 0.5 SECONDS var/damage = 0.75 var/source_UID /datum/status_effect/cryo_beam/on_creation(mob/living/new_owner, mob/living/source) . = ..() source_UID = source.UID() /datum/status_effect/cryo_beam/tick() var/mob/living/simple_animal/hostile/megafauna/ancient_robot/attacker = locateUID(source_UID) if(!(owner in view(attacker, 8))) qdel(src) return owner.apply_damage(damage, BURN) owner.bodytemperature = max(0, owner.bodytemperature - 20) owner.Beam(attacker.beam, icon_state = "medbeam", time = 0.5 SECONDS) for(var/datum/reagent/R in owner.reagents.reagent_list) owner.reagents.remove_reagent(R.id, 0.75) if(prob(10)) to_chat(owner, "Your blood freezes in your veins, get away!") /datum/status_effect/bubblegum_curse id = "bubblegum curse" alert_type = /obj/screen/alert/status_effect/bubblegum_curse duration = -1 //Kill it. There is no other option. tick_interval = 1 SECONDS /// The damage the status effect does per tick. var/damage = 0.75 var/source_UID /// Are we starting the process to check if the person has still gotten out of range of bubble / crossed zlvls. var/coward_checking = FALSE /datum/status_effect/bubblegum_curse/on_creation(mob/living/new_owner, mob/living/source) . = ..() source_UID = source.UID() owner.overlay_fullscreen("Bubblegum", /obj/screen/fullscreen/fog, 1) /datum/status_effect/bubblegum_curse/tick() var/mob/living/simple_animal/hostile/megafauna/bubblegum/attacker = locateUID(source_UID) if(!attacker || attacker.loc == null) qdel(src) if(attacker.health <= attacker.maxHealth / 2) owner.clear_fullscreen("Bubblegum") owner.overlay_fullscreen("Bubblegum", /obj/screen/fullscreen/fog, 2) if(!coward_checking) if(owner.z != attacker.z) addtimer(CALLBACK(src, PROC_REF(onstation_coward_callback)), 12 SECONDS) coward_checking = TRUE else if(get_dist(attacker, owner) >= 25) addtimer(CALLBACK(src, PROC_REF(runaway_coward_callback)), 12 SECONDS) coward_checking = TRUE owner.apply_damage(damage, BRUTE) if(ishuman(owner)) var/mob/living/carbon/human/H = owner H.bleed(0.33) if(prob(5)) to_chat(owner, "[pick("You feel your sins crawling on your back.", "You felt your sins weighing on your neck.", "You feel your blood pulsing inside you.", "YOU'LL NEVER ESCAPE ME", "YOU'LL DIE FOR INSULTING ME LIKE THIS")]") /datum/status_effect/bubblegum_curse/on_remove() owner.clear_fullscreen("Bubblegum") /datum/status_effect/bubblegum_curse/proc/onstation_coward_callback() coward_checking = FALSE var/mob/living/simple_animal/hostile/megafauna/bubblegum/attacker = locateUID(source_UID) if(owner.z != attacker.z) to_chat(owner, "YOU CHALLENGE ME LIKE THIS... AND YOU RUN WITH YOUR FALSE MAGICS?") else return SLEEP_CHECK_QDEL(2 SECONDS) to_chat(owner, "REALLY?") SLEEP_CHECK_QDEL(2 SECONDS) to_chat(owner, "SUCH INSOLENCE!") SLEEP_CHECK_QDEL(2 SECONDS) to_chat(owner, "SO PATHETIC...") SLEEP_CHECK_QDEL(2 SECONDS) to_chat(owner, "...SO FOOLISH!") get_over_here() /datum/status_effect/bubblegum_curse/proc/runaway_coward_callback() coward_checking = FALSE var/mob/living/simple_animal/hostile/megafauna/bubblegum/attacker = locateUID(source_UID) if(get_dist(attacker, owner) >= 25) to_chat(owner, "My my, you can run FAST.") else return SLEEP_CHECK_QDEL(2 SECONDS) to_chat(owner, "I thought you wanted a true fight?") SLEEP_CHECK_QDEL(2 SECONDS) to_chat(owner, "Perhaps I was mistaken.") SLEEP_CHECK_QDEL(2 SECONDS) to_chat(owner, "You are a coward who does not want a fight...") SLEEP_CHECK_QDEL(2 SECONDS) to_chat(owner, "...BUT I WANT YOU DEAD!") get_over_here() /datum/status_effect/bubblegum_curse/proc/get_over_here() var/mob/living/simple_animal/hostile/megafauna/bubblegum/attacker = locateUID(source_UID) if(!attacker) return //Let's not nullspace if(attacker.loc == null) return //Extra emergency safety. var/turf/TA = get_turf(owner) owner.Immobilize(3 SECONDS) new /obj/effect/decal/cleanable/blood/bubblegum(TA) new /obj/effect/temp_visual/bubblegum_hands/rightsmack(TA) sleep(6) var/turf/TB = get_turf(owner) to_chat(owner, "[attacker] rends you!") playsound(TB, attacker.attack_sound, 100, TRUE, -1) owner.adjustBruteLoss(10) new /obj/effect/decal/cleanable/blood/bubblegum(TB) new /obj/effect/temp_visual/bubblegum_hands/leftsmack(TB) sleep(6) var/turf/TC = get_turf(owner) to_chat(owner, "[attacker] rends you!") playsound(TC, attacker.attack_sound, 100, TRUE, -1) owner.adjustBruteLoss(10) new /obj/effect/decal/cleanable/blood/bubblegum(TC) new /obj/effect/temp_visual/bubblegum_hands/rightsmack(TC) sleep(6) var/turf/TD = get_turf(owner) to_chat(owner, "[attacker] rends you!") playsound(TD, attacker.attack_sound, 100, TRUE, -1) owner.adjustBruteLoss(10) new /obj/effect/temp_visual/bubblegum_hands/leftpaw(TD) new /obj/effect/temp_visual/bubblegum_hands/leftthumb(TD) sleep(8) to_chat(owner, "[attacker] drags you through the blood!") playsound(TD, 'sound/misc/enter_blood.ogg', 100, TRUE, -1) var/turf/targetturf = get_step(attacker, attacker.dir) owner.forceMove(targetturf) playsound(targetturf, 'sound/misc/exit_blood.ogg', 100, TRUE, -1) addtimer(CALLBACK(attacker, TYPE_PROC_REF(/mob/living/simple_animal/hostile/megafauna/bubblegum, FindTarget), list(owner), 1), 2) /obj/screen/alert/status_effect/bubblegum_curse name = "I SEE YOU" desc = "YOUR SOUL WILL BE MINE FOR YOUR INSOLENCE" icon_state = "bubblegumjumpscare" /obj/screen/alert/status_effect/bubblegum_curse/Initialize(mapload) . = ..() START_PROCESSING(SSobj, src) /obj/screen/alert/status_effect/bubblegum_curse/Destroy() STOP_PROCESSING(SSobj, src) return ..() /obj/screen/alert/status_effect/bubblegum_curse/process() var/new_filter = isnull(get_filter("ray")) ray_filter_helper(1, 40,"#ce3030", 6, 20) if(new_filter) animate(get_filter("ray"), offset = 10, time = 10 SECONDS, loop = -1) animate(offset = 0, time = 10 SECONDS) /datum/status_effect/abductor_cooldown id = "abductor_cooldown" alert_type = /obj/screen/alert/status_effect/abductor_cooldown duration = 10 SECONDS /obj/screen/alert/status_effect/abductor_cooldown name = "Teleportation cooldown" desc = "Per article A-113, all experimentors must wait 10000 milliseconds between teleports in order to ensure no long term genetic or mental damage happens to experimentor or test subjects." icon_state = "bluespace" #define DEFAULT_MAX_CURSE_COUNT 5 /// Status effect that gives the target miscellanous debuffs while throwing a status alert and causing them to smoke from the damage they're incurring. /// Purposebuilt for cursed slot machines. /datum/status_effect/cursed id = "cursed" alert_type = /obj/screen/alert/status_effect/cursed /// The max number of curses a target can incur with this status effect. var/max_curse_count = DEFAULT_MAX_CURSE_COUNT /// The amount of times we have been "applied" to the target. var/curse_count = 0 /// Raw probability we have to deal damage this tick. var/damage_chance = 10 /// Are we currently in the process of sending a monologue? var/monologuing = FALSE /// The hand we are branded to. var/obj/item/organ/external/branded_hand = null /datum/status_effect/cursed/on_apply() RegisterSignal(owner, COMSIG_MOB_STATCHANGE, PROC_REF(on_stat_changed)) RegisterSignal(owner, COMSIG_MOB_DEATH, PROC_REF(on_death)) RegisterSignal(owner, COMSIG_CURSED_SLOT_MACHINE_USE, PROC_REF(check_curses)) RegisterSignal(owner, COMSIG_CURSED_SLOT_MACHINE_LOST, PROC_REF(update_curse_count)) RegisterSignal(SSdcs, COMSIG_GLOB_CURSED_SLOT_MACHINE_WON, PROC_REF(clear_curses)) return ..() /datum/status_effect/cursed/Destroy() UnregisterSignal(SSdcs, COMSIG_GLOB_CURSED_SLOT_MACHINE_WON) branded_hand = null return ..() /// Checks the number of curses we have and returns information back to the slot machine. `max_curse_amount` is set by the slot machine itself. /datum/status_effect/cursed/proc/check_curses(mob/user, max_curse_amount) SIGNAL_HANDLER if(curse_count >= max_curse_amount) return SLOT_MACHINE_USE_CANCEL if(monologuing) to_chat(owner, "Your arm is resisting your attempts to pull the lever!") // listening to kitschy monologues to postpone your powergaming is the true curse here. return SLOT_MACHINE_USE_POSTPONE /// Handles the debuffs of this status effect and incrementing the number of curses we have. /datum/status_effect/cursed/proc/update_curse_count() SIGNAL_HANDLER curse_count++ linked_alert?.update_appearance() // we may have not initialized it yet addtimer(CALLBACK(src, PROC_REF(handle_after_effects), 1 SECONDS)) // give it a second to let the failure sink in before we exact our toll /// Makes a nice lorey message about the curse level we're at. I think it's nice /datum/status_effect/cursed/proc/handle_after_effects() if(QDELETED(src)) return monologuing = TRUE var/list/messages = list() switch(curse_count) if(1) // basically your first is a "freebie" that will still require urgent medical attention and will leave you smoking forever but could be worse tbh if(ishuman(owner)) var/mob/living/carbon/human/human_owner = owner playsound(human_owner, 'sound/weapons/sear.ogg', 50, TRUE) var/obj/item/organ/external/affecting = human_owner.get_active_hand() branded_hand = affecting messages += "Your hand burns, and you quickly let go of the lever! You feel a little sick as the nerves deaden in your hand..." messages += "Some smoke appears to be coming out of your hand now, but it's not too bad..." messages += "Fucking stupid machine." if(2) messages += "The machine didn't burn you this time, it must be some arcane work of the brand recognizing a source..." messages += "Blisters and boils start to appear over your skin. Each one hissing searing hot steam out of its own pocket..." messages += "You understand that the machine tortures you with its simplistic allure. It can kill you at any moment, but it derives a sick satisfaction at forcing you to keep going." messages += "If you could get away from here, you might be able to live with some medical supplies. Is it too late to stop now?" messages += "As you shut your eyes to dwell on this conundrum, the brand surges in pain. You shudder to think what might happen if you go unconscious." if(3) owner.emote("cough") messages += "Your throat becomes to feel like it's slowly caking up with sand and dust. You eject the contents of the back of your throat onto your sleeve." messages += "It is sand. Crimson red. You've never felt so thirsty in your life, yet you don't trust your own hand to carry the glass to your lips." messages += "You get the sneaking feeling that if someone else were to win, that it might clear your curse too. Saving your life is a noble cause." messages += "Of course, you might have to not speak on the nature of this machine, in case they scamper off to leave you to die." messages += "Is it truly worth it to condemn someone to this fate to cure the manifestation of your own hedonistic urges? You'll have to decide quickly." if(4) messages += "A migraine swells over your head as your thoughts become hazy. Your hand desperately inches closer towards the slot machine for one final pull..." messages += "The ultimate test of mind over matter. You can jerk your own muscle back in order to prevent a terrible fate, but your life already is worth so little now." messages += "This is what they want, is it not? To witness your failure against itself? The compulsion carries you forward as a sinking feeling of dread fills your stomach." messages += "Paradoxically, where there is hopelessness, there is elation. Elation at the fact that there's still enough power in you for one more pull." messages += "Your legs desperately wish to jolt away on the thought of running away from this wretched machination, but your own arm remains complacent in the thought of seeing spinning wheels." messages += "The toll has already been exacted. There is no longer death on 'your' terms. Is your dignity worth more than your life?" if(5 to INFINITY) if(max_curse_count != DEFAULT_MAX_CURSE_COUNT) // this probably will only happen through admin schenanigans letting people stack up infinite curses or something to_chat(owner, "Do you still think you're in control?") return to_chat(owner, "Why couldn't I get one more try?!") owner.gib() qdel(src) return for(var/message in messages) to_chat(owner, message) sleep(3 SECONDS) // yes yes a bit fast but it can be a lot of text and i want the whole thing to send before the cooldown on the slot machine might expire monologuing = FALSE /// Cleans ourselves up and removes our curses. Meant to be done in a "positive" way, when the curse is broken. Directly use qdel otherwise. /datum/status_effect/cursed/proc/clear_curses() SIGNAL_HANDLER owner.visible_message( "The smoke slowly clears from [owner.name]...", "Your skin finally settles down and your throat no longer feels as dry... The brand disappearing confirms that the curse has been lifted.",) qdel(src) /// If our owner's stat changes, rapidly surge the damage chance. /datum/status_effect/cursed/proc/on_stat_changed() SIGNAL_HANDLER if(owner.stat == CONSCIOUS || owner.stat == DEAD) // reset on these two states damage_chance = initial(damage_chance) return to_chat(owner, "As your body crumbles, you feel the curse of the slot machine surge through your body!") damage_chance += 75 //ruh roh raggy /// If our owner dies without getting gibbed, we gib them, because fuck you baltamore /datum/status_effect/cursed/proc/on_death(mob/living/source, gibbed) SIGNAL_HANDLER if(!ishuman(owner)) return if(gibbed) return var/mob/living/carbon/human/H = owner INVOKE_ASYNC(H, TYPE_PROC_REF(/mob, gib)) /datum/status_effect/cursed/tick() if(curse_count <= 1) return // you get one "freebie" (single damage) to nudge you into thinking this is a bad idea before the house begins to win. // the house won. var/ticked_coefficient = rand(15, 40) * 2 / 100 var/effective_percentile_chance = ((curse_count == 2 ? 1 : curse_count) * damage_chance * ticked_coefficient) if(prob(effective_percentile_chance)) owner.apply_damages( brute = (curse_count * ticked_coefficient), burn = (curse_count * ticked_coefficient), oxy = (curse_count * ticked_coefficient), ) /obj/screen/alert/status_effect/cursed name = "Cursed!" desc = "The brand on your hand reminds you of your greed, yet you seem to be okay otherwise." icon_state = "cursed_by_slots" /obj/screen/alert/status_effect/cursed/update_desc() . = ..() var/datum/status_effect/cursed/linked_effect = attached_effect var/curses = linked_effect.curse_count switch(curses) if(2) desc = "Your greed is catching up to you..." if(3) desc = "You really don't feel good right now... But why stop now?" if(4 to INFINITY) desc = "Real winners quit before they reach the ultimate prize." #undef DEFAULT_MAX_CURSE_COUNT