Unlucky trait (#18463)

* this compiles

* more edits

* Upports OP21's immovable rod

Makes it NOT shit.

* actually enable this lol

* CLANG

* we all fall down

* break it

* stairs

* bye async

* makes doorcrushes less lethal

* more unluck!

* dice and stumble vore

* major version

* Update clang.dm

* More tweaks. BS Cracker

Makes bluespace cracker better code quality too

* Cut down on lists here

* Adds the traits

* glass shapnel

* Update unlucky.dm

* Modular Shock

* Charger and thrown

* Defib nat 1

* Gravity Falls

* gibby

* no longer gib, just hurt a LOT

* Better Washer

* Update washing_machine.dm

* Even less jank

* Moves some stuff around

* linters

* Update unlucky.dm

* Table stubbing

* fixes mirror break, evil only

* PIPEBOMB

* Update negative.dm

* Update mail.dm

* glasses fly off your face if you fall

* Update unlucky.dm

* evil beaker and spooky events in the dark

* Evil beaker spilling

* Unlucky people have custom maint loot

* Get sick while searching

* Update _lootable.dm

* whoop

* Update _lootable.dm

* washer will always wash

* Lowered to 5

* ash

* Update areas.dm

* get knocked

* picking up items

* Dice

* Update unlucky.dm

* Update code/game/objects/items/devices/defib.dm

Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com>

* Update code/modules/economy/vending.dm

Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com>

* Update code/game/area/areas.dm

Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com>

* Update code/datums/components/traits/unlucky.dm

Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com>

* Update code/datums/components/traits/unlucky.dm

Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com>

* Update code/datums/components/traits/unlucky.dm

Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com>

* Update code/datums/components/traits/unlucky.dm

Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com>

* Nicer damage

* these

* this

* mini DCS update

* Excludes

* Proper inherit

* Update unlucky.dm

* Update code/datums/components/traits/unlucky.dm

* These

* thes too

* user

* no hardrefs

* only these 2

---------

Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com>
This commit is contained in:
Cameron Lennox
2025-10-04 03:13:12 -04:00
committed by GitHub
parent 0befda8668
commit 7f4de78573
44 changed files with 1123 additions and 182 deletions
+1
View File
@@ -370,6 +370,7 @@
old_component.InheritComponent(arglist(arguments))
else
old_component.InheritComponent(new_component, TRUE)
QDEL_NULL(new_component)
if(COMPONENT_DUPE_SOURCES)
if(source in old_component.sources)
@@ -45,7 +45,7 @@
if(G.affecting)
G.affecting.electrocute_act(10 * siemens, src, 1.0, BP_TORSO, 0)
var/agony = 80 * siemens //Does more than if hit with an electric hand, since grabbing is slower.
G.affecting.stun_effect_act(0, agony, BP_TORSO, src)
G.affecting.stun_effect_act(0, agony, BP_TORSO, src, electric = TRUE)
add_attack_logs(src,G.affecting,"Changeling shocked")
+544
View File
@@ -0,0 +1,544 @@
/**
* Ripped from /tg/ with modifications.
* unlucky.dm: For when you want someone to have a really bad day
*
* When you attach an omen component to someone, they start running the risk of all sorts of bad environmental injuries, like nearby vending machines randomly falling on you (TBI),
* or hitting your head really hard when you slip and fall, or you get shocked by the tram rails at an unfortunate moment.
*
* Omens are removed once the victim is either maimed by one of the possible injuries, or if they receive a blessing (read: bashing with a bible) from the chaplain. (TBI)
*/
/datum/component/omen
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/// How many incidents are left. If 0 exactly, it will get deleted.
var/incidents_left = INFINITY
/// Base probability of negative events. Cursed are half as unlucky.
var/luck_mod = 1
/// Base damage from negative events. Cursed take 25% of this damage.
var/damage_mod = 1
/// If we want to do more evil events, such as spontaneous combustion
var/evil = TRUE
/// If our codebase has safe disposals or not
var/safe_disposals = FALSE
/// If we have vore interactions or not
var/vorish = TRUE
/datum/component/omen/Initialize(incidents_left, luck_mod, damage_mod, evil, safe_disposals, vorish)
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
if(!isnull(incidents_left))
src.incidents_left = incidents_left
if(!isnull(luck_mod))
src.luck_mod = luck_mod
if(!isnull(damage_mod))
src.damage_mod = damage_mod
if(!isnull(evil))
src.evil = evil
if(!isnull(safe_disposals))
src.safe_disposals = safe_disposals
if(!isnull(vorish))
src.vorish = vorish
ADD_TRAIT(parent, TRAIT_UNLUCKY, src)
/**
* This is a omen eat omen world! The stronger omen survives.
*/
/datum/component/omen/InheritComponent(
datum/component/omen/new_comp,
i_am_original,
incidents_left,
luck_mod,
damage_mod,
evil,
safe_disposals,
vorish,
)
// If we have more incidents left the new one gets deleted.
if(src.incidents_left > incidents_left)
return
src.incidents_left = incidents_left
// The new omen is weaker than our current omen? Let's split the difference.
if(src.luck_mod > luck_mod)
src.luck_mod += luck_mod * 0.5
if(src.damage_mod > damage_mod)
src.damage_mod += damage_mod * 0.5
// If the new omen has special modifiers, we take them on forever!
if(evil)
src.evil = TRUE
if(safe_disposals)
src.safe_disposals = TRUE
if(vorish)
src.vorish = TRUE
//This means weaker, longing lasting omens will take priority, but have some of the strength of the original.
/datum/component/omen/Destroy(force)
var/mob/living/person = parent
REMOVE_TRAIT(person, TRAIT_UNLUCKY, src)
to_chat(person, span_warning(span_green("You feel a horrible omen lifted off your shoulders!")))
return ..()
/datum/component/omen/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(check_accident))
RegisterSignal(parent, COMSIG_ON_CARBON_SLIP, PROC_REF(check_slip))
RegisterSignal(parent, COMSIG_MOVED_DOWN_STAIRS, PROC_REF(check_stairs))
RegisterSignal(parent, COMSIG_STUN_EFFECT_ACT, PROC_REF(check_taser))
RegisterSignal(parent, COMSIG_MOB_ROLLED_DICE, PROC_REF(check_roll))
RegisterSignal(parent, COMSIG_HUMAN_ON_CATCH_THROW, PROC_REF(check_throw))
RegisterSignal(parent, COMSIG_PICKED_UP_ITEM, PROC_REF(check_pickup))
/datum/component/omen/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_ON_CARBON_SLIP, COMSIG_MOVABLE_MOVED, COMSIG_STUN_EFFECT_ACT, COMSIG_MOVED_DOWN_STAIRS, COMSIG_MOB_ROLLED_DICE, COMSIG_HUMAN_ON_CATCH_THROW, COMSIG_PICKED_UP_ITEM))
/datum/component/omen/proc/consume_omen()
incidents_left--
if(incidents_left < 1)
qdel(src)
/**
* check_accident() is called each step we take
*
* While we're walking around, roll to see if there's any environmental hazards on one of the adjacent tiles we can trigger.
* We do the prob() at the beginning to A. add some tension for /when/ it will strike, and B. (more importantly) ameliorate the fact that we're checking up to 5 turfs's contents each time
*/
/datum/component/omen/proc/check_accident(atom/movable/our_guy)
SIGNAL_HANDLER
if(!isliving(our_guy) || isbelly(our_guy.loc))
return
var/mob/living/living_guy = our_guy
if(living_guy.is_incorporeal()) //no being unlucky if you don't even exist on the same plane.
return
if(evil && prob(0.0001) && (living_guy.stat != DEAD)) // 1 in a million
living_guy.visible_message(span_danger("[living_guy] suddenly bursts into flames!"), span_danger("You suddenly burst into flames!"))
living_guy.emote("scream")
living_guy.adjust_fire_stacks(20)
living_guy.ignite_mob()
consume_omen()
return
var/effective_luck = luck_mod
// If there's nobody to witness the misfortune, make it less likely.
// This way, we allow for people to be able to get into hilarious situations without making the game nigh unplayable most of the time.
var/has_watchers = FALSE
for(var/mob/viewer in viewers(our_guy, world.view))
if(viewer.client && !viewer.client.is_afk())
has_watchers = TRUE
break
if(!has_watchers)
effective_luck *= 0.5
if(!prob(2 * effective_luck))
return
var/turf/our_guy_pos = get_turf(our_guy)
if(!our_guy_pos)
return
if(evil)
for(var/obj/machinery/door/airlock/darth_airlock in our_guy_pos)
if(darth_airlock.locked || !darth_airlock.arePowerSystemsOn())
continue
to_chat(living_guy, span_warning("The airlock suddenly closes on you!"))
living_guy.Paralyse(1 SECONDS)
slam_airlock(darth_airlock)
consume_omen()
return
for(var/turf/the_turf as anything in our_guy_pos.AdjacentTurfs(check_blockage = FALSE)) //need false so we can check disposal units
if(the_turf.CanZPass(our_guy, DOWN))
to_chat(living_guy, span_warning("You lose your balance and slip towards the edge!"))
living_guy.Weaken(5)
living_guy.throw_at(the_turf, 1, 20)
consume_omen()
return
if(vorish)
for(var/mob/living/living_mob in the_turf)
if(living_mob == our_guy)
continue //Don't do anything to ourselves.
if(living_mob.stat)
continue
if(!living_mob.CanStumbleVore(living_guy) && !living_guy.CanStumbleVore(living_mob)) //Works both ways! Either way, someone's getting eaten!
continue
living_mob.stumble_into(living_guy) //logic reversed here because the game is DUMB. This means that living_guy is stumbling into the target!
living_guy.visible_message(span_danger("[living_guy] loses their balance and slips into [living_mob]!"), span_boldwarning("You lose your balance, slipping into [living_mob]!"))
consume_omen()
return
for(var/obj/machinery/washing_machine/evil_washer in the_turf)
if(evil_washer.state == 1) //Empty and open door
our_guy.visible_message(span_danger("[our_guy] slips near the [evil_washer] and falls in, the door shutting!"), span_boldwarning("You slip on a wet spot near the [evil_washer] and fall in, the door shutting! You're stuck!"))
our_guy.forceMove(evil_washer)
evil_washer.washing += our_guy
evil_washer.state = 4
evil_washer.visible_message(span_danger("[evil_washer] begins its spin cycle!"))
evil_washer.start(TRUE, damage_mod)
consume_omen()
return
if(evil || safe_disposals) //On servers without safe disposals, this is a death sentence. With servers with safe disposals, it's just funny.
for(var/obj/machinery/disposal/evil_disposal in the_turf)
if(evil_disposal.stat & (BROKEN|NOPOWER))
continue
if(evil_disposal.loc == living_guy.loc) //Let's not do a continual loop of them falling into it as soon as they climb out, as funny as that is.
continue
our_guy.visible_message(span_danger("[our_guy] slips on a spill near the [evil_disposal] and falls in!"), span_boldwarning("You slip on a spill near the [evil_disposal] and fall in!"))
living_guy.forceMove(evil_disposal)
evil_disposal.flush = TRUE
evil_disposal.update()
living_guy.Weaken(5)
consume_omen()
return
if(evil && prob(33)) //This has an additional 2 in 3 chance to not happen as there's a LOT of lights on stations. This should be rarer.
for(var/obj/machinery/light/evil_light in the_turf)
if((evil_light.status == LIGHT_BURNED || evil_light.status == LIGHT_BROKEN) || (living_guy.get_shock_protection() == 1)) // we can't do anything :(
to_chat(living_guy, span_warning("[evil_light] sparks weakly for a second."))
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread //this shit is copy pasted all over the code...this needs to just be made into a proc at this point jesus christ
s.set_up(4, FALSE, evil_light)
s.start()
//We don't clear the omen as nothing really happened.
break
to_chat(living_guy, span_warning("[evil_light] glows ominously...")) // ominously
evil_light.visible_message(span_boldwarning("[evil_light] suddenly flares brightly and sparks!"))
//evil_light.broken(skip_sound_and_sparks = FALSE) //Let's not break it actually.
evil_light.Beam(living_guy, icon_state = "lightning[rand(1,12)]", time = 0.5 SECONDS)
living_guy.electrocute_act(35 * (damage_mod * 0.5), evil_light, stun = TRUE) //Stun is binary and scales on damage..Lame.
living_guy.emote("scream")
consume_omen()
return
for(var/obj/machinery/vending/darth_vendor in the_turf)
if(darth_vendor.stat & (BROKEN|NOPOWER))
continue
to_chat(living_guy, span_warning("The delivery chute of [darth_vendor] raises up..."))
darth_vendor.throw_item(living_guy)
consume_omen()
return
for(var/obj/structure/mirror/evil_mirror in the_turf)
to_chat(living_guy, span_warning("You pass by the mirror and glance at it..."))
if(evil_mirror.shattered)
to_chat(living_guy, span_notice("You feel lucky, somehow."))
return
var/mirror_rand
if(evil)
mirror_rand = rand(1,5)
else
mirror_rand = rand(1,3)
switch(mirror_rand)
if(1)
to_chat(living_guy, span_boldwarning("You see your reflection, but it is grinning malevolently and staring directly at you!"))
living_guy.emote("scream")
if(2 to 3)
to_chat(living_guy, span_large(span_cult("Oh god, you can't see your reflection!!")))
living_guy.emote("scream")
if(4 to 5)
to_chat(living_guy, span_warning("The mirror explodes into a million pieces! Wait, does that mean you're even more unlucky?"))
evil_mirror.shatter()
if(prob(50 * effective_luck)) // sometimes
luck_mod += 0.25
damage_mod += 0.25
var/max_health_coefficient = (living_guy.maxHealth * 0.06)
for(var/obj/item/organ/external/limb in living_guy.organs)
living_guy.apply_damage(max_health_coefficient * damage_mod, BRUTE, limb.organ_tag, used_weapon = "glass shrapnel")
living_guy.make_jittery(250)
if(evil && prob(7 * effective_luck))
to_chat(living_guy, span_warning("You are completely shocked by this turn of events!"))
if(ishuman(living_guy))
var/mob/living/carbon/human/human_guy = living_guy
if(human_guy.should_have_organ(O_HEART))
for(var/obj/item/organ/internal/heart/heart in human_guy.internal_organs)
heart.bruise() //Closest thing we have to a heart attack.
to_chat(living_guy, span_boldwarning("You clutch at your heart!"))
consume_omen()
return
if(evil)
for(var/obj/item/reagent_containers/glass/beaker/evil_beaker in the_turf)
if(!evil_beaker.is_open_container() && (evil_beaker.reagents.total_volume > 0)) //A closed beaker is a safe beaker!
continue
living_guy.visible_message(span_danger("[evil_beaker] tilts, spilling its contents on [living_guy]!"), span_bolddanger("[evil_beaker] spills all over you!"))
evil_beaker.balloon_alert_visible("[evil_beaker]'s contents splashes onto [living_guy]!")
evil_beaker.reagents.splash(living_guy, evil_beaker.reagents.total_volume)
consume_omen()
return
for(var/obj/structure/table/evil_table in the_turf)
if(!evil_table.material) //We only want tables, not just table frames.
continue
var/datum/gender/gender = GLOB.gender_datums[living_guy.get_visible_gender()]
living_guy.visible_message(span_danger("[living_guy] stubs [gender.his] toe on [evil_table]!"), span_bolddanger("You stub your toe on [evil_table]!"))
living_guy.apply_damage(2 * damage_mod, BRUTE, pick(BP_L_FOOT, BP_R_FOOT), used_weapon = "blunt force trauma")
living_guy.adjustHalLoss(25) //It REALLY hurts.
living_guy.Weaken(3)
consume_omen()
return
//Ran out of turf options. Let's do more generic options.
if(prob(luck_mod * 5))
// In complete darkness
if(our_guy_pos.get_lumcount() <= LIGHTING_SOFT_THRESHOLD)
living_guy.Blind(5) //10 seconds of 'OH GOD WHAT'S HAPPENING'
living_guy.silent = 5
living_guy.Paralyse(5)
to_chat(living_guy, span_bolddanger("You feel the ground buckle underneath you, falling down, your vision going dark as you feel paralyzed in place!"))
consume_omen()
return
/datum/component/omen/proc/slam_airlock(obj/machinery/door/airlock/darth_airlock)
SIGNAL_HANDLER
. = darth_airlock.close(forced = TRUE, ignore_safties = TRUE, crush_damage = 15) //Not enough to cause any IB or massively injured organs.
if(.)
consume_omen()
/// If we get knocked down, see if we have a really bad slip and bash our head hard
/datum/component/omen/proc/check_slip(mob/living/our_guy, amount)
SIGNAL_HANDLER
if(prob(30)) // AAAA
our_guy.emote("scream")
to_chat(our_guy, span_cult("What a horrible night... To have a curse!"))
if(prob(30 * luck_mod) && our_guy.get_bodypart_name(BP_HEAD)) /// Bonk!
playsound(our_guy, 'sound/effects/tableheadsmash.ogg', 90, TRUE)
var/datum/gender/gender = GLOB.gender_datums[our_guy.get_visible_gender()]
our_guy.visible_message(span_danger("[our_guy] hits [gender.his] head really badly falling down!"), span_bolddanger("You hit your head really badly falling down!"))
var/max_health_coefficient = (our_guy.maxHealth * 0.5)
our_guy.apply_damage(max_health_coefficient * damage_mod, BRUTE, BP_HEAD, used_weapon = "slipping")
if(ishuman(our_guy))
var/mob/living/carbon/human/human_guy = our_guy
if(human_guy.should_have_organ(O_BRAIN))
for(var/obj/item/organ/internal/brain/brain in human_guy.internal_organs)
brain.take_damage(30 * damage_mod) //60 damage kills.
if(human_guy.glasses && human_guy.canUnEquip(human_guy.glasses))
var/turf/T = get_turf(human_guy)
if(T)
var/obj/item/our_glasses = human_guy.glasses
human_guy.unEquip(human_guy.glasses, target = T)
to_chat(human_guy, span_warning("Your glasses fly off as you hit the ground!"))
our_glasses.throw_at_random(FALSE, 3, 2)
consume_omen()
return
/datum/component/omen/proc/check_roll(mob/living/unlucky_soul, var/obj/item/dice/the_dice, silent, result)
SIGNAL_HANDLER
if(prob(20 * luck_mod))
//unlucky_soul.visible_message(span_danger("[unlucky_soul] rolls [the_dice] with it landing on the edge of [result] before tilting over!"), span_boldwarning("You feel dreadfully unlucky as you roll the dice!"))
//I had thought about making this have a notice that it happened.
//However, gaslighting the user by providing no visible notice is MUCH funnier.
return 1 // We override the roll to a 1.
///Returns TRUE and stops us from catching
/datum/component/omen/proc/check_throw(mob/living/unlucky_soul, source, speed)
SIGNAL_HANDLER
if(prob(30 * luck_mod)) //~9% chance
if(istype(source, /obj/item/grenade))
var/obj/item/grenade/bad_grenade = source
if(bad_grenade.active)
unlucky_soul.put_in_active_hand(bad_grenade)
unlucky_soul.visible_message(span_warning("[src] catches [source] as it goes off in their hand!"), span_bolddanger("You catch [source] and it goes off in your hand!"))
unlucky_soul.throw_mode_off()
bad_grenade.detonate()
return TRUE
else
unlucky_soul.visible_message(span_attack("[unlucky_soul] tries to catch [source] and fumbles it, getting thrown back!"))
unlucky_soul.Weaken(5)
return TRUE
/*
* Dynamic injury system for when you pick up objects!
* Some objects might cut, burn, or otherwise injure you if you pick them up!
* Genenerally more of an annoyance than anything.
* Variables that can be changed:
* injury_type, damage_to_inflict, damage_type, injury_verb, is_sharp, is_edge.
*/
/datum/component/omen/proc/check_pickup(mob/living/unlucky_soul, obj/item/item)
SIGNAL_HANDLER
if(prob(3 * luck_mod) && ishuman(unlucky_soul)) // ~3% chance
var/mob/living/carbon/human/unlucky_human = unlucky_soul
///What the injury will show up as on an autopsy.
var/injury_type = "injury"
///How much damage we'll inflect.
var/damage_to_inflict = 0
///What type of damage we'll inflict
var/damage_type = BRUTE
///If the item we get injured on has is sharp.
var/is_sharp = FALSE
///If the item we get injured on has an edge.
var/has_edge = FALSE
///What verb we use to describe the injury.
var/injury_verb = "cuts"
///What hand we are currently using, so we injure the correct one.
var/current_hand = BP_R_HAND
if(unlucky_human.hand)
current_hand = BP_L_HAND
if(istype(item, /obj/item/paper))
injury_verb = "cuts"
injury_type = "paper cut"
damage_to_inflict = 2
else if(istype(item, /obj/item/material/knife))
var/obj/item/material/knife = item
injury_verb = "cuts"
injury_type = "knife"
is_sharp = knife.sharp
has_edge = knife.edge
damage_to_inflict = knife.force
else if(istype(item, /obj/item/material/shard))
var/obj/item/material/shard/shard = item
injury_verb = "cuts"
injury_type = "shard"
is_sharp = shard.sharp
has_edge = shard.edge
damage_to_inflict = shard.force
else if(istype(item, /obj/item/flame/lighter))
var/obj/item/flame/lighter/lighter = item
if(!lighter.lit)
return
injury_verb = "burns"
injury_type = "lighter"
damage_type = BURN
damage_to_inflict = 5
else if(istype(item, /obj/item/tool/transforming/jawsoflife))
var/obj/item/tool/transforming/jawsoflife/jaws = item
injury_verb = "clamps"
injury_type = "industrial tool"
is_sharp = jaws.sharp
has_edge = jaws.edge
damage_to_inflict = jaws.force
else if(istype(item, /obj/item/tool/screwdriver))
var/obj/item/tool/screwdriver/screwdriver = item
injury_verb = "stabs"
injury_type = "industrial tool"
is_sharp = screwdriver.sharp
has_edge = screwdriver.edge
damage_to_inflict = screwdriver.force
else if(istype(item, /obj/item/tool/wirecutters))
var/obj/item/tool/wirecutters/wirecutters = item
injury_verb = "nips"
injury_type = "industrial tool"
is_sharp = wirecutters.sharp
has_edge = wirecutters.edge
damage_to_inflict = wirecutters.force
if(!damage_to_inflict)
return
var/datum/gender/gender = GLOB.gender_datums[unlucky_human.get_visible_gender()]
unlucky_human.visible_message(span_danger("[unlucky_human] accidentally [injury_verb] [gender.his] hand on [item]!"))
unlucky_human.apply_damage(damage_to_inflict * damage_mod, damage_type, current_hand, sharp = is_sharp, edge = has_edge, used_weapon = injury_type)
/datum/component/omen/proc/check_stairs(mob/living/unlucky_soul)
SIGNAL_HANDLER
if(prob(3 * luck_mod)) /// Bonk!
playsound(unlucky_soul, 'sound/effects/tableheadsmash.ogg', 90, TRUE)
unlucky_soul.visible_message(span_danger("One of the stairs give way as [unlucky_soul] steps onto it, tumbling them down to the bottom!"), span_bolddanger("A stair gives way and you trip to the bottom!"))
var/max_health_coefficient = (unlucky_soul.maxHealth * 0.09)
for(var/obj/item/organ/external/limb in unlucky_soul.organs) //In total, you should have 11 limbs (generally, unless you have an amputation). The full omen variant we want to leave you at 1 hp, the trait version less. As of writing, the trait version is 25% of the damage, so you take 24.75 across all limbs.
unlucky_soul.apply_damage(max_health_coefficient * damage_mod, BRUTE, limb.organ_tag, used_weapon = "slipping")
unlucky_soul.Weaken(5)
consume_omen()
/datum/component/omen/proc/check_taser(mob/living/unlucky_soul, stun_amount, agony_amount, def_zone, used_weapon, electric)
SIGNAL_HANDLER
if(!electric || !evil) //If it's not electric we don't care! Likewise, if we don't have the evil variant, don't care!
return
if(!ishuman(unlucky_soul))
return
if(prob(3 * luck_mod))
var/mob/living/carbon/human/human_guy = unlucky_soul
if(human_guy.should_have_organ(O_HEART))
for(var/obj/item/organ/internal/heart/heart in human_guy.internal_organs)
if(heart.robotic)
continue //Robotic hearts are immune to this.
heart.take_damage(10 * stun_amount * damage_mod)
heart.take_damage(0.25 * agony_amount * damage_mod)
playsound(src, 'sound/effects/singlebeat.ogg', 50, FALSE)
to_chat(unlucky_soul, span_bolddanger("You feel as though your heart stopped"))
human_guy.Stun(5)
consume_omen()
return
/**
* The trait omen. Permanent.
* Has only a 30% chance of bad things happening, and takes only 25% of normal damage.
* Evil is false, so you get less dramatic things happening.
*/
/datum/component/omen/trait
incidents_left = INFINITY
dupe_type = /datum/component/omen/trait
luck_mod = 0.3 // 30% chance of bad things happening
damage_mod = 0.25 // 25% of normal damage
evil = FALSE
safe_disposals = FALSE
///Major variant of the trait.
/datum/component/omen/trait/major
evil = TRUE
damage_mod = 0.75 //75% of normal damage
///Variant trait for downstreams that have safe disposals.
/datum/component/omen/trait/safe_disposals
safe_disposals = TRUE
/datum/component/omen/trait/safe_disposals/major
evil = TRUE
damage_mod = 0.75 //75% of normal damage
/**
* The bible omen.
* While it lasts, parent gets a cursed aura filter.
*/
/datum/component/omen/bible
incidents_left = 1
/datum/component/omen/bible/RegisterWithParent()
. = ..()
var/mob/living/living_parent = parent
living_parent.add_filter("omen", 2, list("type" = "drop_shadow", "color" = "#A50824", "alpha" = 0, "size" = 2))
var/filter = living_parent.get_filter("omen")
animate(filter, alpha = 255, time = 2 SECONDS, loop = -1)
animate(alpha = 0, time = 2 SECONDS)
/datum/component/omen/bible/UnregisterFromParent()
. = ..()
var/mob/living/living_parent = parent
living_parent.remove_filter("omen")
/**
* The dice omen.
* Single use omen from rolling a nat 1 on a cursed d20.
*/
/datum/component/omen/dice
incidents_left = 1
+15 -1
View File
@@ -9,6 +9,7 @@
var/loot_left = 0 // When this reaches zero, and loot_depleted is true, you can't obtain anymore loot.
var/delete_on_depletion = FALSE // If true, and if the loot gets depleted as above, the pile is deleted.
var/list/unlucky_loot = list() // Unlucky is the worst tier. Only people with the unlucky trait can get this stuff. Primed grenades, dangerous syringes, etc.
var/list/common_loot = list() // Common is generally less-than-useful junk and filler, at least for maint loot piles.
var/list/uncommon_loot = list() // Uncommon is actually maybe some useful items, usually the reason someone bothers looking inside.
var/list/rare_loot = list() // Rare is really powerful, or at least unique items.
@@ -47,7 +48,17 @@
var/obj/item/loot = null
var/span = "notice" // Blue
if(prob(chance_uncommon) && uncommon_loot.len) // You might still get something good.
if(HAS_TRAIT(L, TRAIT_UNLUCKY) && unlucky_loot.len) // If you're unlucky, you will always find bad stuff.
loot = produce_unlucky_item(source)
span = "cult" // Purple and bold.
if(prob(1))
to_chat(L, span_danger("You cut your hand on something in the trash!"))
L.apply_damage(2, BRUTE, pick(BP_L_HAND, BP_R_HAND), used_weapon = "sharp object")
var/datum/disease/advance/random/random_disease = new /datum/disease/advance/random()
random_disease.spread_flags |= DISEASE_SPREAD_NON_CONTAGIOUS
L.ForceContractDisease(random_disease)
else if(prob(chance_uncommon) && uncommon_loot.len) // You might still get something good.
loot = produce_uncommon_item(source)
span = "alium" // Green
@@ -96,6 +107,9 @@
if(delete_on_depletion)
qdel(source)
/datum/element/lootable/proc/produce_unlucky_item(atom/source)
var/path = pick(unlucky_loot)
return new path(source)
/datum/element/lootable/proc/produce_common_item(atom/source)
var/path = pick(common_loot)
+38
View File
@@ -1,6 +1,44 @@
// Contains loads of different types of boxes, which may have items inside!
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/element/lootable/boxes
unlucky_loot = list(
/obj/item/grenade/flashbang/clusterbang/primed,
/obj/item/storage/box/old_syringes,
/obj/item/storage/box/donut/empty,
/obj/item/grenade/smokebomb/primed,
/obj/item/storage/box,
/obj/item/storage/box/cups,
/obj/item/trash/candle,
/obj/item/trash/candy,
/obj/item/trash/candy/proteinbar,
/obj/item/trash/candy/gums,
/obj/item/trash/cheesie,
/obj/item/trash/chips,
/obj/item/trash/chips/bbq,
/obj/item/trash/liquidfood,
/obj/item/trash/pistachios,
/obj/item/trash/plate,
/obj/item/trash/popcorn,
/obj/item/trash/raisins,
/obj/item/trash/semki,
/obj/item/trash/snack_bowl,
/obj/item/trash/sosjerky,
/obj/item/trash/syndi_cakes,
/obj/item/trash/tastybread,
/obj/item/trash/coffee,
/obj/item/trash/tray,
/obj/item/trash/unajerky,
/obj/item/trash/waffles,
/obj/item/spacecash/c1,
/obj/item/card/emag_broken,
/obj/effect/decal/remains/lizard,
/obj/effect/decal/remains/mouse,
/obj/effect/decal/remains/robot,
/obj/item/pizzabox/old,
/obj/item/paper/crumpled
)
common_loot = list(
/obj/item/storage/box,
/obj/item/storage/box/beakers,
+74
View File
@@ -1,6 +1,43 @@
// Has large amounts of possible items, most of which may or may not be useful.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/element/lootable/maint/junk
unlucky_loot = list(
/obj/item/grenade/flashbang/clusterbang/primed,
/obj/item/storage/box/old_syringes,
/obj/item/storage/box/donut/empty,
/obj/item/grenade/smokebomb/primed,
/obj/item/storage/box,
/obj/item/storage/box/cups,
/obj/item/trash/candle,
/obj/item/trash/candy,
/obj/item/trash/candy/proteinbar,
/obj/item/trash/candy/gums,
/obj/item/trash/cheesie,
/obj/item/trash/chips,
/obj/item/trash/chips/bbq,
/obj/item/trash/liquidfood,
/obj/item/trash/pistachios,
/obj/item/trash/plate,
/obj/item/trash/popcorn,
/obj/item/trash/raisins,
/obj/item/trash/semki,
/obj/item/trash/snack_bowl,
/obj/item/trash/sosjerky,
/obj/item/trash/syndi_cakes,
/obj/item/trash/tastybread,
/obj/item/trash/coffee,
/obj/item/trash/tray,
/obj/item/trash/unajerky,
/obj/item/trash/waffles,
/obj/item/spacecash/c1,
/obj/item/card/emag_broken,
/obj/effect/decal/remains/lizard,
/obj/effect/decal/remains/mouse,
/obj/effect/decal/remains/robot,
/obj/item/pizzabox/old,
/obj/item/paper/crumpled
)
common_loot = list(
/obj/item/flashlight/flare,
/obj/item/flashlight/glowstick,
@@ -94,6 +131,43 @@
// Contains mostly useless garbage.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/element/lootable/maint/trash
unlucky_loot = list(
/obj/item/grenade/flashbang/clusterbang/primed,
/obj/item/storage/box/old_syringes,
/obj/item/storage/box/donut/empty,
/obj/item/grenade/smokebomb/primed,
/obj/item/storage/box,
/obj/item/storage/box/cups,
/obj/item/trash/candle,
/obj/item/trash/candy,
/obj/item/trash/candy/proteinbar,
/obj/item/trash/candy/gums,
/obj/item/trash/cheesie,
/obj/item/trash/chips,
/obj/item/trash/chips/bbq,
/obj/item/trash/liquidfood,
/obj/item/trash/pistachios,
/obj/item/trash/plate,
/obj/item/trash/popcorn,
/obj/item/trash/raisins,
/obj/item/trash/semki,
/obj/item/trash/snack_bowl,
/obj/item/trash/sosjerky,
/obj/item/trash/syndi_cakes,
/obj/item/trash/tastybread,
/obj/item/trash/coffee,
/obj/item/trash/tray,
/obj/item/trash/unajerky,
/obj/item/trash/waffles,
/obj/item/spacecash/c1,
/obj/item/card/emag_broken,
/obj/effect/decal/remains/lizard,
/obj/effect/decal/remains/mouse,
/obj/effect/decal/remains/robot,
/obj/item/pizzabox/old,
/obj/item/paper/crumpled
)
common_loot = list(
/obj/item/trash/candle,
/obj/item/trash/candy,
+37
View File
@@ -5,6 +5,43 @@
chance_rare = 2
chance_gamma = 1 // Special single drop table
unlucky_loot = list(
/obj/item/grenade/flashbang/clusterbang/primed,
/obj/item/storage/box/old_syringes,
/obj/item/storage/box/donut/empty,
/obj/item/grenade/smokebomb/primed,
/obj/item/storage/box,
/obj/item/storage/box/cups,
/obj/item/trash/candle,
/obj/item/trash/candy,
/obj/item/trash/candy/proteinbar,
/obj/item/trash/candy/gums,
/obj/item/trash/cheesie,
/obj/item/trash/chips,
/obj/item/trash/chips/bbq,
/obj/item/trash/liquidfood,
/obj/item/trash/pistachios,
/obj/item/trash/plate,
/obj/item/trash/popcorn,
/obj/item/trash/raisins,
/obj/item/trash/semki,
/obj/item/trash/snack_bowl,
/obj/item/trash/sosjerky,
/obj/item/trash/syndi_cakes,
/obj/item/trash/tastybread,
/obj/item/trash/coffee,
/obj/item/trash/tray,
/obj/item/trash/unajerky,
/obj/item/trash/waffles,
/obj/item/spacecash/c1,
/obj/item/card/emag_broken,
/obj/effect/decal/remains/lizard,
/obj/effect/decal/remains/mouse,
/obj/effect/decal/remains/robot,
/obj/item/pizzabox/old,
/obj/item/paper/crumpled
)
common_loot = list(
/obj/item/clothing/gloves/rainbow,
/obj/item/clothing/gloves/white,