diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index 7e1dc3c3640..72e098737b4 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -857,6 +857,21 @@ ///from /datum/species/xenochimera/handle_environment_special() #define COMSIG_XENOCHIMERA_COMPONENT "xenochimera_component" + +// Spontaneous vore stuff. +///from /mob/living/stumble_into(mob/living/M) +#define COMSIG_LIVING_STUMBLED_INTO "living_stumbled_into" + ///Something has special handling. Don't continue. + #define CANCEL_STUMBLED_INTO (1<<0) +///from /mob/living/handle_fall(var/turf/landing) args: landing, drop_mob) +#define COMSIG_LIVING_FALLING_DOWN "living_falling_down" + //Special handling. Cancel the fall chain. + #define COMSIG_CANCEL_FALL (1<<0) +///from /mob/living/hitby(atom/movable/source, var/speed = THROWFORCE_SPEED_DIVISOR) +#define COMSIG_LIVING_HIT_BY_THROWN_ENTITY "hit_by_thrown_entity" + //Special handling. Cancel the hitby proc. + #define COMSIG_CANCEL_HITBY (1<<0) + //Unittest data update #ifdef UNIT_TEST #define COMSIG_UNITTEST_DATA "unittest_send_data" diff --git a/code/datums/components/traits/unlucky.dm b/code/datums/components/traits/unlucky.dm new file mode 100644 index 00000000000..10316314da7 --- /dev/null +++ b/code/datums/components/traits/unlucky.dm @@ -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(!CanStumbleVore(living_guy, living_mob) && !CanStumbleVore(living_mob, living_guy)) //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 diff --git a/code/datums/components/turfslip.dm b/code/datums/components/turfslip.dm new file mode 100644 index 00000000000..b55b03dad73 --- /dev/null +++ b/code/datums/components/turfslip.dm @@ -0,0 +1,93 @@ +/datum/component/turfslip + var/mob/living/owner + var/slip_dist = TURFSLIP_WET + var/dirtslip = FALSE + +/datum/component/turfslip/Initialize() + if (!isliving(parent)) + return COMPONENT_INCOMPATIBLE + owner = parent + RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(move_react)) + +/datum/component/turfslip/proc/start_slip(var/turf/simulated/start, var/is_dirt) + var/slip_stun = 6 + var/floor_type = "wet" + var/already_slipping = (slip_dist > 1) + + // Handle dirt slipping + dirtslip = is_dirt + if(dirtslip) + slip_stun = 10 + if(start.dirt > 50) + floor_type = "dirty" + else if(start.is_outdoors()) + floor_type = "uneven" + + // Proper sliding behavior + switch(start.wet) + if(TURFSLIP_LUBE) + floor_type = "slippery" + slip_dist = 99 //Skill issue. + slip_stun = 10 + dirtslip = FALSE + if(TURFSLIP_ICE) + floor_type = "icy" + slip_stun = 4 + slip_dist = rand(1,3) + dirtslip = FALSE + + // Only start the slip timer if we are not already sliding + if(!already_slipping) + owner.slip("the [floor_type] floor", slip_stun) + addtimer(CALLBACK(src, PROC_REF(next_slip)), 1) + +/datum/component/turfslip/proc/move_react(atom/source, atom/oldloc, direction, forced, list/old_locs, momentum_change) + SIGNAL_HANDLER + + // Can the mob slip? + if(QDELETED(owner) || isbelly(owner.loc)) + qdel(src) + return + + // Can the turf be slipped on? + var/turf/simulated/ground = get_turf(owner) + if(!ground || !ground.check_slipping(owner,dirtslip)) + qdel(src) + return + + addtimer(CALLBACK(src, PROC_REF(next_slip)), 1) + +/datum/component/turfslip/proc/next_slip() + // check tile for next slip + owner.is_slipping = TRUE + if(!step(owner, owner.dir) || dirtslip) // done sliding, failed to move, dirt also only slips once + qdel(src) + return + // Kill the slip if it's over + if(!--slip_dist) + qdel(src) + return + +/datum/component/turfslip/Destroy(force = FALSE) + UnregisterSignal(owner, COMSIG_MOVABLE_MOVED) + owner.inertia_dir = 0 + owner.is_slipping = FALSE + owner = null + slip_dist = 0 + . = ..() + + +//////////////////////////////////////////////////////////////////////////////////////// +// Helper proc +//////////////////////////////////////////////////////////////////////////////////////// +/turf/proc/check_slipping(var/mob/living/M) + return FALSE + +/turf/simulated/check_slipping(var/mob/living/M,var/dirtslip) + if(M.buckled) + return FALSE + if(!wet && !(dirtslip && (dirt > 50 || is_outdoors() == OUTDOORS_YES))) + return FALSE + if(wet == TURFSLIP_WET && M.m_intent == I_WALK) + return FALSE + return TRUE diff --git a/code/datums/elements/vore/spontaneous_vore.dm b/code/datums/elements/vore/spontaneous_vore.dm new file mode 100644 index 00000000000..629ccb59627 --- /dev/null +++ b/code/datums/elements/vore/spontaneous_vore.dm @@ -0,0 +1,193 @@ +/datum/element/spontaneous_vore + +/datum/element/spontaneous_vore/Attach(datum/target) + . = ..() + if(!isliving(target)) + return ELEMENT_INCOMPATIBLE + RegisterSignal(target, COMSIG_LIVING_STUMBLED_INTO, PROC_REF(handle_stumble)) + RegisterSignal(target, COMSIG_LIVING_FALLING_DOWN, PROC_REF(handle_fall)) + RegisterSignal(target, COMSIG_LIVING_HIT_BY_THROWN_ENTITY, PROC_REF(handle_hitby)) + RegisterSignal(target, COMSIG_MOVABLE_CROSS, PROC_REF(handle_crossed)) + +/datum/element/spontaneous_vore/Detach(datum/target) + . = ..() + UnregisterSignal(target, list(COMSIG_LIVING_STUMBLED_INTO, COMSIG_LIVING_FALLING_DOWN, COMSIG_LIVING_HIT_BY_THROWN_ENTITY, COMSIG_MOVABLE_CROSS)) + +///Source is the one being bumped into (Owner of this component) +///Target is the one bumping into us. +/datum/element/spontaneous_vore/proc/handle_stumble(mob/living/source, mob/living/target) + SIGNAL_HANDLER + + //snowflake protean code to prevent protean blobform from eating their human form and humanform from eating their protean blob...gross. + //We are trying to eat our blobform + if(istype(target, /mob/living/simple_mob/protean_blob)) + var/mob/living/simple_mob/protean_blob/PB = target + if(PB.humanform == source) + return + //Our blobform is trying to eat us + if(istype(source, /mob/living/simple_mob/protean_blob)) + var/mob/living/simple_mob/protean_blob/PB = source + if(PB.humanform == target) + return + + //We are able to eat the person stumbling into us. + if(CanStumbleVore(prey = target, pred = source)) //This is if the person stumbling into us is able to eat us! + source.visible_message(span_vwarning("[target] flops carelessly into [source]!")) + source.begin_instant_nom(source, prey = target, pred = source, belly = source.vore_selected) + target.stop_flying() + return CANCEL_STUMBLED_INTO + + //The person stumbling into us is able to eat us. + if(CanStumbleVore(prey = source, pred = target)) //This is if the person stumbling into us is able to be eaten by us! BROKEN! + source.visible_message(span_vwarning("[target] flops carelessly into [source]!")) + target.forceMove(get_turf(source)) + source.begin_instant_nom(target, prey = source, pred = target, belly = target.vore_selected) + source.stop_flying() + return CANCEL_STUMBLED_INTO + +//Source is the one dropping (us) +//Landing is the tile we're falling onto +//drop_mob is whatever mob is found in the turf we're dropping onto. +/datum/element/spontaneous_vore/proc/handle_fall(mob/living/source, turf/landing, mob/living/drop_mob) + SIGNAL_HANDLER + + if(!drop_mob || drop_mob == source) + return + + //pred = drop_mob + //prey = source + //result: source is eaten by drop_mob + if(CanDropVore(prey = source, pred = drop_mob)) + drop_mob.feed_grabbed_to_self_falling_nom(drop_mob, prey = source) + drop_mob.visible_message(span_vdanger("\The [drop_mob] falls right onto \the [source]!")) + return COMSIG_CANCEL_FALL + + //pred = source + //prey = drop_mob + //result: drop_mob is eaten by source + if(CanDropVore(prey = drop_mob, pred = source)) + source.feed_grabbed_to_self_falling_nom(source, prey = drop_mob) + source.Weaken(4) + source.visible_message(span_vdanger("\The [drop_mob] falls right into \the [source]!")) + return COMSIG_CANCEL_FALL + +/datum/element/spontaneous_vore/proc/handle_hitby(mob/living/source, atom/movable/hitby, speed) + SIGNAL_HANDLER + + //Handle object throw vore + if(isitem(hitby)) + var/obj/item/O = hitby + if(source.stat != DEAD && source.trash_catching && source.vore_selected) + if(source.adminbus_trash || is_type_in_list(O, GLOB.edible_trash) && O.trash_eatable && !is_type_in_list(O, GLOB.item_vore_blacklist)) + source.visible_message(span_vwarning("[O] is thrown directly into [source]'s [lowertext(source.vore_selected.name)]!")) + O.throwing = 0 + O.forceMove(source.vore_selected) + return COMSIG_CANCEL_HITBY + + //Throwing a prey into a pred takes priority. After that it checks to see if the person being thrown is a pred. + if(isliving(hitby)) + var/mob/living/thrown_mob = hitby + + //If we don't allow mobvore and the thrown mob is an NPC animal, stop here. + if(!source.allowmobvore && isanimal(thrown_mob) && !thrown_mob.ckey) + return + + //If we're an NPC animal and the person thrown into us doesn't allow mobvore, stop here. + if(!thrown_mob.allowmobvore && isanimal(source) && !source.ckey) + return + + // PERSON BEING HIT: CAN BE DROP PRED, ALLOWS THROW VORE. + // PERSON BEING THROWN: DEVOURABLE, ALLOWS THROW VORE, CAN BE DROP PREY. + if(CanThrowVore(prey = thrown_mob, pred = source)) + if(!source.vore_selected) + return + source.vore_selected.nom_mob(thrown_mob) //Eat them!!! + source.visible_message(span_vwarning("[thrown_mob] is thrown right into [source]'s [lowertext(source.vore_selected.name)]!")) + if(thrown_mob.loc != source.vore_selected) + thrown_mob.forceMove(source.vore_selected) //Double check. Should never happen but...Weirder things have happened! + source.on_throw_vore_special(TRUE, thrown_mob) + add_attack_logs(thrown_mob.thrower,source,"Devoured [thrown_mob.name] via throw vore.") + return //We can stop here. We don't need to calculate damage or anything else. They're eaten. + + // PERSON BEING HIT: CAN BE DROP PREY, ALLOWS THROW VORE, AND IS DEVOURABLE. + // PERSON BEING THROWN: CAN BE DROP PRED, ALLOWS THROW VORE. + else if(CanThrowVore(prey = source, pred = thrown_mob))//Pred thrown into prey. + if(!thrown_mob.vore_selected) + return + source.visible_message(span_vwarning("[source] suddenly slips inside of [thrown_mob]'s [lowertext(thrown_mob.vore_selected.name)] as [thrown_mob] flies into them!")) + thrown_mob.vore_selected.nom_mob(source) //Eat them!!! + if(source.loc != thrown_mob.vore_selected) + source.forceMove(thrown_mob.vore_selected) //Double check. Should never happen but...Weirder things have happened! + add_attack_logs(thrown_mob.LAssailant,source,"Was Devoured by [thrown_mob.name] via throw vore.") + return + +//source = person standing up +//crossed = person sliding +/datum/element/spontaneous_vore/proc/handle_crossed(mob/living/source, mob/living/crossed) + SIGNAL_HANDLER + + if(source == crossed || !istype(crossed)) + return + + //Person being slipped into eats the person slipping + if(can_slip_vore(pred = source, prey = crossed)) //If we can vore them go for it + source.begin_instant_nom(source, prey = crossed, pred = source, belly = source.vore_selected) + return COMPONENT_BLOCK_CROSS + + //The person slipping eats the person being slipped into + else if(can_slip_vore(pred = crossed, prey = source)) + source.begin_instant_nom(crossed, prey = source, pred = crossed, belly = crossed.vore_selected) //Must be + return //We DON'T block it here. Pred can slip onto the prey's tile, no problem. + + +///Helper Procs +/proc/CanStumbleVore(mob/living/prey, mob/living/pred) + if(!can_spontaneous_vore(pred, prey)) + return FALSE + if(!prey.stumble_vore || !pred.stumble_vore) + return FALSE + return TRUE + +/proc/CanDropVore(mob/living/prey, mob/living/pred) + if(!can_spontaneous_vore(pred, prey)) + return FALSE + if(!pred.drop_vore || !prey.drop_vore) + return FALSE + return TRUE + +/proc/CanThrowVore(mob/living/prey, mob/living/pred) + if(!can_spontaneous_vore(pred, prey)) + return FALSE + if(!pred.throw_vore || !prey.throw_vore) + return FALSE + return TRUE + +/proc/can_slip_vore(mob/living/pred, mob/living/prey) + if(!can_spontaneous_vore(pred, prey)) + return FALSE + if(!prey.is_slipping && !pred.is_slipping) //Obviously they have to be slipping to get slip vored + return FALSE + if(world.time <= prey.slip_protect) + return FALSE + if(!pred.slip_vore || !prey.slip_vore) + return FALSE + return TRUE + +///This is a general 'do we have the mechanical ability to do any type of spontaneous vore' without specialties. +/proc/can_spontaneous_vore(mob/living/pred, mob/living/prey) + if(!istype(pred) || !istype(prey)) + return FALSE + //Unfortunately, can_be_drop_prey is 'spontanous prey' var and can_be_drop_pred is 'spontaneous pred' var...horribly named imo. + if(!prey.can_be_drop_prey || !pred.can_be_drop_pred) + return FALSE + if(prey.is_incorporeal() || pred.is_incorporeal()) + return FALSE + if(!prey.devourable) + return FALSE + if(!is_vore_predator(pred)) //Check their bellies and stuff + return FALSE + if(!pred.vore_selected) //Gotta have one selected as well. + return FALSE + if(!prey.allowmobvore && isanimal(pred) && !pred.ckey || (!pred.allowmobvore && isanimal(prey) && !prey.ckey)) + return FALSE + return TRUE diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index 7146a3fa974..34562eb8282 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -126,7 +126,7 @@ if(has_buckled_mobs() && buckled_mobs.len >= max_buckled_mobs) for(var/mob/living/L in buckled_mobs) - if(istype(L) && M.CanStumbleVore(L)) + if(istype(L) && CanStumbleVore(prey = L, pred = M)) unbuckle_mob(L, TRUE) if(M == user) M.visible_message(span_warning("[M.name] sits down on [L.name]!")) @@ -215,7 +215,7 @@ if(has_buckled_mobs() && buckled_mobs.len >= max_buckled_mobs) //Handles trying to buckle yourself to the chair when someone is on it if(can_do_spont_vore && is_vore_predator(M) && M.vore_selected) for(var/mob/living/buckled in buckled_mobs) - if(M.CanStumbleVore(buckled)) + if(CanStumbleVore(prey = buckled, pred = M)) return TRUE to_chat(M, span_notice("\The [src] can't buckle any more people.")) return FALSE diff --git a/code/modules/mob/living/carbon/human/species/station/protean/protean_blob.dm b/code/modules/mob/living/carbon/human/species/station/protean/protean_blob.dm index b7b9a88360c..6d1621031c9 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean/protean_blob.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean/protean_blob.dm @@ -659,19 +659,6 @@ return 1 return 0 -//Don't eat yourself, idiot -/mob/living/simple_mob/protean_blob/CanStumbleVore(mob/living/target) - if(target == humanform) - return FALSE - return ..() - -/mob/living/carbon/human/CanStumbleVore(mob/living/target) - if(istype(target, /mob/living/simple_mob/protean_blob)) - var/mob/living/simple_mob/protean_blob/PB = target - if(PB.humanform == src) - return FALSE - return ..() - /mob/living/simple_mob/protean_blob/handle_mutations_and_radiation() if(!humanform) to_chat(src, span_giant(span_boldwarning("You are currently a blob without a humanform and should be deleted shortly Please report what you were doing when this error occurred to the admins."))) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 3c2f308831d..eae7ba038fb 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -18,6 +18,20 @@ selected_image = image(icon = GLOB.buildmode_hud, loc = src, icon_state = "ai_sel") +<<<<<<< HEAD +======= + AddElement(/datum/element/spontaneous_vore) + +/mob/living/proc/get_visible_name() + var/datum/component/shadekin/SK = get_shadekin_component() + if(SK && SK.in_phase) + return "Something" + if(real_name) + return real_name + else + return name + +>>>>>>> 11a4471110 ([MIRROR] Spontaneous Vore Element (#11785)) /mob/living/Destroy() SSradiation.listeners -= src remove_all_modifiers(TRUE) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 5887c3ad9c5..0f6f3991d48 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -265,6 +265,7 @@ /mob/living/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR)//Standardization and logging -Sieve if(is_incorporeal()) return +<<<<<<< HEAD if(istype(AM,/obj/)) var/obj/O = AM if(stat != DEAD && istype(O,/obj/item) && trash_catching && vore_selected) //ported from chompstation @@ -274,6 +275,12 @@ I.throwing = 0 I.forceMove(vore_selected) return +======= + if(SEND_SIGNAL(src, COMSIG_LIVING_HIT_BY_THROWN_ENTITY, source, speed) & COMSIG_CANCEL_HITBY) + return + if(isitem(source)) + var/obj/item/O = source +>>>>>>> 11a4471110 ([MIRROR] Spontaneous Vore Element (#11785)) var/dtype = O.damtype var/throw_damage = O.throwforce*(speed/THROWFORCE_SPEED_DIVISOR) @@ -333,6 +340,7 @@ src.anchored = TRUE src.pinned += O +<<<<<<< HEAD //VORESTATION EDIT START - Allows for thrown vore! //CHOMPEdit Start //Throwing a prey into a pred takes priority. After that it checks to see if the person being thrown is a pred. if(isliving(AM)) @@ -364,6 +372,8 @@ return //VORESTATION EDIT END - Allows for thrown vore! //CHOMPEdit End +======= +>>>>>>> 11a4471110 ([MIRROR] Spontaneous Vore Element (#11785)) /mob/living/proc/on_throw_vore_special(var/pred = TRUE, var/mob/living/target) return diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index fe93637465b..12878832dd5 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -24,33 +24,33 @@ return ..() // Parent call should make the mob move. /*one proc, four uses -swapping: if it's 1, the mobs are trying to switch, if 0, non-passive is pushing passive +swapping: if it's TRUE, the mobs are trying to switch, if FALSE, non-passive is pushing passive default behaviour is: - non-passive mob passes the passive version - passive mob checks to see if its mob_bump_flag is in the non-passive's mob_bump_flags - if si, the proc returns */ -/mob/living/proc/can_move_mob(var/mob/living/swapped, swapping = 0, passive = 0) +/mob/living/proc/can_move_mob(var/mob/living/swapped, swapping = FALSE, passive = FALSE) if(!swapped) - return 1 + return TRUE if(!passive) - return swapped.can_move_mob(src, swapping, 1) + return swapped.can_move_mob(src, swapping, TRUE) else - var/context_flags = 0 + var/context_flags = FALSE if(swapping) context_flags = swapped.mob_swap_flags else context_flags = swapped.mob_push_flags if(!mob_bump_flag) //nothing defined, go wild - return 1 + return TRUE if(mob_bump_flag & context_flags) - return 1 - return 0 + return TRUE + return FALSE /mob/living/Bump(atom/movable/AM) if(now_pushing || !loc || buckled == AM || AM.is_incorporeal()) return - now_pushing = 1 + now_pushing = TRUE if (isliving(AM)) var/mob/living/tmob = AM @@ -58,94 +58,92 @@ default behaviour is: spread_fire(tmob) for(var/mob/living/M in range(tmob, 1)) - if(tmob.pinned.len || ((M.pulling == tmob && ( tmob.restrained() && !( M.restrained() ) && M.stat == 0)) || locate(/obj/item/grab, tmob.grabbed_by.len)) ) + if(tmob.pinned.len || ((M.pulling == tmob && ( tmob.restrained() && !( M.restrained() ) && M.stat == CONSCIOUS)) || locate(/obj/item/grab, tmob.grabbed_by.len)) ) if ( !(world.time % 5) ) to_chat(src, span_warning("[tmob] is restrained, you cannot push past")) - now_pushing = 0 + now_pushing = FALSE return - if( tmob.pulling == M && ( M.restrained() && !( tmob.restrained() ) && tmob.stat == 0) ) + if( tmob.pulling == M && ( M.restrained() && !( tmob.restrained() ) && tmob.stat == CONSCIOUS) ) if ( !(world.time % 5) ) to_chat(src, span_warning("[tmob] is restraining [M], you cannot push past")) - now_pushing = 0 + now_pushing = FALSE return //BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller - var/can_swap = 1 + var/can_swap = TRUE if(loc.density || tmob.loc.density) - can_swap = 0 + can_swap = FALSE if(can_swap) for(var/atom/movable/A in loc) if(A == src) continue if(!A.CanPass(tmob, loc)) - can_swap = 0 + can_swap = FALSE if(!can_swap) break if(can_swap) for(var/atom/movable/A in tmob.loc) if(A == tmob) continue if(!A.CanPass(src, tmob.loc)) - can_swap = 0 + can_swap = FALSE if(!can_swap) break //Leaping mobs just land on the tile, no pushing, no anything. if(status_flags & LEAPING) loc = tmob.loc status_flags &= ~LEAPING - now_pushing = 0 + now_pushing = FALSE return if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && tmob.canmove && canmove && !tmob.buckled && !buckled && can_swap && can_move_mob(tmob, 1, 0)) // mutual brohugs all around! var/turf/oldloc = loc - //VOREstation Edit - Begin //check bumpnom chance, if it's a simplemob that's doing the bumping var/mob/living/simple_mob/srcsimp = src if(istype(srcsimp)) if(srcsimp.tryBumpNom(tmob)) - now_pushing = 0 + now_pushing = FALSE return //if it's a simplemob being bumped, and the above didn't make them start getting bumpnommed, they get a chance to bumpnom var/mob/living/simple_mob/tmobsimp = tmob if(istype(tmobsimp)) if(tmobsimp.tryBumpNom(src)) - now_pushing = 0 + now_pushing = FALSE return - //VOREstation Edit - End forceMove(tmob.loc) //CHOMPSTATION Edit - Making macro/micro step mechanics mandatory again for balance, but removing the fetish aspects if pref denied. //There's nothing fetishistic about politely stepping past someone. // In case of micros, we don't swap positions; instead occupying the same square! if(handle_micro_bump_helping(tmob)) - now_pushing = 0 + now_pushing = FALSE return // TODO - Check if we need to do something about the slime.UpdateFeed() we are skipping below. // CHOMPSTATION Edit - End tmob.forceMove(oldloc) - now_pushing = 0 + now_pushing = FALSE return else if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && canmove && can_swap && handle_micro_bump_helping(tmob)) forceMove(tmob.loc) - now_pushing = 0 + now_pushing = FALSE return - if(!can_move_mob(tmob, 0, 0)) - now_pushing = 0 + if(!can_move_mob(tmob, FALSE, FALSE)) + now_pushing = FALSE return if(a_intent == I_HELP || src.restrained()) - now_pushing = 0 + now_pushing = FALSE return // Plow that nerd. if(ishuman(tmob)) var/mob/living/carbon/human/H = tmob - if(H.species.lightweight == 1 && prob(50)) + if(H.species.lightweight == TRUE && prob(50)) if(HULK in H.mutations) //No knocking over the hulk return H.visible_message(span_warning("[src] bumps into [H], knocking them off balance!")) H.Weaken(5) - now_pushing = 0 + now_pushing = FALSE return //CHOMPSTATION edit Adding alternative to lightweight if(H.species.lightweight_light == 1 && H.a_intent == I_HELP) @@ -163,55 +161,45 @@ default behaviour is: if(ishuman(tmob) && (FAT in tmob.mutations)) if(prob(40) && !(FAT in src.mutations)) to_chat(src, span_danger("You fail to push [tmob]'s fat ass out of the way.")) - now_pushing = 0 + now_pushing = FALSE return if(tmob.r_hand && istype(tmob.r_hand, /obj/item/shield/riot)) if(prob(99)) - now_pushing = 0 + now_pushing = FALSE return if(tmob.l_hand && istype(tmob.l_hand, /obj/item/shield/riot)) if(prob(99)) - now_pushing = 0 + now_pushing = FALSE return if(!(tmob.status_flags & CANPUSH)) - now_pushing = 0 + now_pushing = FALSE return tmob.LAssailant = src - now_pushing = 0 + now_pushing = FALSE . = ..() if (!istype(AM, /atom/movable) || AM.anchored) - //VOREStation Edit - object-specific proc for running into things if(((confused || is_blind()) && stat == CONSCIOUS && prob(50) && m_intent==I_RUN) || flying) AM.stumble_into(src) - //VOREStation Edit End - /* VOREStation Removal - See above - if(confused && prob(50) && m_intent==I_RUN) - Weaken(2) - playsound(src, "punch", 25, 1, -1) - visible_message(span_warning("[src] [pick("ran", "slammed")] into \the [AM]!")) - src.apply_damage(5, BRUTE) - to_chat(src, span_warning("You just [pick("ran", "slammed")] into \the [AM]!")) - */ // VOREStation Removal End return if (!now_pushing) if(isobj(AM)) var/obj/I = AM if(!can_pull_size || can_pull_size < I.w_class) return - now_pushing = 1 + now_pushing = TRUE var/t = get_dir(src, AM) if (istype(AM, /obj/structure/window)) for(var/obj/structure/window/win in get_step(AM,t)) - now_pushing = 0 + now_pushing = FALSE return var/turf/T = AM.loc var/turf/T2 = get_step(AM,t) if(!T2) // Map edge - now_pushing = 0 + now_pushing = FALSE return var/move_time = movement_delay(loc, t) move_time = DS2NEARESTTICK(move_time) @@ -220,9 +208,9 @@ default behaviour is: if(ishuman(AM) && AM:grabbed_by) for(var/obj/item/grab/G in AM:grabbed_by) - step(G:assailant, get_dir(G:assailant, AM)) + step(G.assailant, get_dir(G.assailant, AM)) G.adjust_position() - now_pushing = 0 + now_pushing = FALSE /mob/living/CanPass(atom/movable/mover, turf/target) if(istype(mover, /obj/structure/blob) && faction == "blob") //Blobs should ignore things on their faction. diff --git a/code/modules/multiz/movement_vr.dm b/code/modules/multiz/movement_vr.dm index 603a9cfe209..1287d883a3d 100644 --- a/code/modules/multiz/movement_vr.dm +++ b/code/modules/multiz/movement_vr.dm @@ -1,77 +1,56 @@ /mob/living/handle_fall(var/turf/landing) - var/mob/drop_mob = locate(/mob/living, landing) + var/mob/living/drop_mob = locate(/mob/living, landing) if(locate(/obj/structure/stairs) in landing) for(var/atom/A in landing) - if(!A.CanPass(src, src.loc, 1, 0)) + if(!A.CanPass(src, src.loc)) return FALSE Move(landing) if(isliving(src)) var/mob/living/L = src if(L.pulling) L.pulling.forceMove(landing) - return 1 + return TRUE for(var/obj/O in loc) if(!O.CanFallThru(src, landing)) - return 1 - - if(drop_mob && !(drop_mob == src)) //Shitload of checks. This is because the game finds various ways to screw me over. - var/mob/living/drop_living = drop_mob - if(drop_living.dropped_onto(src)) - return - - // Then call parent to have us actually fall - return ..() -/mob/CheckFall(var/atom/movable/falling_atom) - return falling_atom.fall_impact(src) - -/mob/living/proc/dropped_onto(var/atom/hit_atom) - if(!isliving(hit_atom)) - return 0 + return TRUE - var/mob/living/pred = hit_atom - if(pred.is_incorporeal()) + if(SEND_SIGNAL(src, COMSIG_LIVING_FALLING_DOWN, landing, drop_mob) & COMSIG_CANCEL_FALL) return - var/safe_fall = FALSE - if(pred.softfall || (isanimal(pred) && pred.mob_size <= MOB_SMALL)) // TODO: add ability for mob below to be 'soft' and cushion fall - safe_fall = TRUE - if(ishuman(pred)) - var/mob/living/carbon/human/H = pred - if(H.species.soft_landing) + if(drop_mob && drop_mob != src) + ///Varible to tell if we take damage or not for falling. + var/safe_fall = FALSE + if(drop_mob.softfall || (isanimal(drop_mob) && drop_mob.mob_size <= MOB_SMALL)) safe_fall = TRUE - var/mob/living/prey = src - var/fallloc = prey.loc - if(pred.vore_selected && pred.can_be_drop_pred && prey.can_be_drop_prey && pred.drop_vore && prey.drop_vore) - pred.feed_grabbed_to_self_falling_nom(pred,prey) - pred.loc = fallloc - if(!safe_fall) - pred.Weaken(8) - pred.visible_message(span_vdanger("\The [pred] falls right onto \the [prey]!")) - else if(prey.vore_selected && prey.can_be_drop_pred && pred.can_be_drop_prey && pred.drop_vore && prey.drop_vore) - prey.feed_grabbed_to_self_falling_nom(prey,pred) - prey.Weaken(4) - prey.visible_message(span_vdanger("\The [pred] falls right into \the [prey]!")) - else - pred.loc = prey.loc + if(ishuman(drop_mob)) + var/mob/living/carbon/human/H = drop_mob + if(H.species.soft_landing) + safe_fall = TRUE + + forceMove(get_turf(drop_mob)) if(!safe_fall) - pred.Weaken(8) - prey.Weaken(8) + drop_mob.Weaken(8) + Weaken(8) playsound(src, "punch", 25, 1, -1) var/tdamage - for(var/i = 1 to 5) //Twice as less damage because cushioned fall, but both get damaged. + for(var/i = 1 to 5) //Twice as less damage because cushioned fall, but both get damaged. tdamage = rand(0, 5) - pred.adjustBruteLoss(tdamage) - prey.adjustBruteLoss(tdamage) - pred.updatehealth() - prey.updatehealth() - pred.visible_message(span_danger("\The [pred] falls onto \the [prey]!")) + drop_mob.adjustBruteLoss(tdamage) + adjustBruteLoss(tdamage) + drop_mob.updatehealth() + updatehealth() + drop_mob.visible_message(span_danger("\The [drop_mob] falls onto \the [src]!")) else - pred.visible_message(span_notice("\The [pred] safely brushes past \the [prey] as they land.")) - return 1 + drop_mob.visible_message(span_notice("\The [drop_mob] safely brushes past \the [src] as they land.")) + + // Then call parent to have us actually fall + return ..() +/mob/CheckFall(var/atom/movable/falling_atom) + return falling_atom.fall_impact(src) /mob/observer/dead/CheckFall() return diff --git a/code/modules/vore/eating/mob_vr.dm b/code/modules/vore/eating/mob_vr.dm index 4172b1e3d26..31e94fb9f6a 100644 --- a/code/modules/vore/eating/mob_vr.dm +++ b/code/modules/vore/eating/mob_vr.dm @@ -13,11 +13,11 @@ var/vore_smell = null // What the character smells like var/noisy = FALSE // Toggle audible hunger. var/permit_healbelly = TRUE - var/stumble_vore = TRUE //Enabled by default since you have to enable drop pred/prey to do this anyway - var/slip_vore = TRUE //Enabled by default since you have to enable drop pred/prey to do this anyway - var/drop_vore = TRUE //Enabled by default since you have to enable drop pred/prey to do this anyway - var/throw_vore = TRUE //Enabled by default since you have to enable drop pred/prey to do this anyway - var/food_vore = TRUE //Enabled by default since you have to enable drop pred/prey to do this anyway + var/stumble_vore = TRUE + var/slip_vore = TRUE + var/drop_vore = TRUE + var/throw_vore = TRUE + var/food_vore = TRUE var/consume_liquid_belly = FALSE //starting off because if someone is into that, they'll toggle it first time they get the error. Otherway around would be more pref breaky. var/digest_pain = TRUE var/can_be_drop_prey = FALSE diff --git a/code/modules/vore/eating/slipvore_vr.dm b/code/modules/vore/eating/slipvore_vr.dm index 73e3c31480f..027d365a7cb 100644 --- a/code/modules/vore/eating/slipvore_vr.dm +++ b/code/modules/vore/eating/slipvore_vr.dm @@ -2,8 +2,8 @@ /mob/living var/is_slipping = FALSE - var/slip_vore_in_progress = FALSE var/slip_protect = 1 +<<<<<<< HEAD /mob/living/proc/can_slip_vore(var/mob/living/target) if(!target.is_slipping) //Obviously they have to be slipping to get slip vored @@ -70,3 +70,5 @@ if(is_slipping && !lying) is_slipping = FALSE return . +======= +>>>>>>> 11a4471110 ([MIRROR] Spontaneous Vore Element (#11785)) diff --git a/code/modules/vore/eating/stumblevore_vr.dm b/code/modules/vore/eating/stumblevore_vr.dm index dcc65c2e399..91196644d65 100644 --- a/code/modules/vore/eating/stumblevore_vr.dm +++ b/code/modules/vore/eating/stumblevore_vr.dm @@ -1,18 +1,3 @@ -/mob/living/proc/CanStumbleVore(mob/living/target) - if(!can_be_drop_pred) - return FALSE - if(is_incorporeal() || target.is_incorporeal()) - return FALSE - if(!is_vore_predator(src)) - return FALSE - if(!target.devourable) - return FALSE - if(!target.can_be_drop_prey) - return FALSE - if(!target.stumble_vore || !stumble_vore) - return FALSE - return TRUE - /mob/living/Bump(atom/movable/AM) //. = ..() if(isliving(AM)) @@ -30,11 +15,21 @@ ..() /mob/living/stumble_into(mob/living/M) +<<<<<<< HEAD var/mob/living/carbon/human/S = src +======= + if(buckled || M.buckled) + return + + //Stumblevore occurs here. Look at the 'stumblevore' element for more information. + if(SEND_SIGNAL(src, COMSIG_LIVING_STUMBLED_INTO, M) & CANCEL_STUMBLED_INTO) + return +>>>>>>> 11a4471110 ([MIRROR] Spontaneous Vore Element (#11785)) playsound(src, "punch", 25, 1, -1) M.Weaken(4) M.stop_flying() +<<<<<<< HEAD if(CanStumbleVore(M)) //This is if the person stumbling into us is able to eat us! visible_message(span_vwarning("[M] flops carelessly into [src]!")) M.forceMove(get_turf(src)) @@ -55,6 +50,19 @@ stop_flying() apply_damage(0.5, BRUTE) return +======= + + if(ishuman(src)) + var/mob/living/carbon/human/S = src + if(S.species.lightweight == 1) + visible_message(span_vwarning("[M] carelessly bowls [src] over!")) + M.forceMove(get_turf(src)) + M.apply_damage(0.5, BRUTE) + Weaken(4) + stop_flying() + apply_damage(0.5, BRUTE) + return +>>>>>>> 11a4471110 ([MIRROR] Spontaneous Vore Element (#11785)) if(round(weight) > 474) var/throwtarget = get_edge_target_turf(M, reverse_direction(M.dir)) diff --git a/vorestation.dme b/vorestation.dme index 9eb9c19ab4f..d312e3ab766 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -607,6 +607,17 @@ #include "code\datums\elements\light_blocking.dm" #include "code\datums\elements\slosh.dm" #include "code\datums\elements\turf_transparency.dm" +<<<<<<< HEAD +======= +#include "code\datums\elements\lootable\_lootable.dm" +#include "code\datums\elements\lootable\boxes.dm" +#include "code\datums\elements\lootable\maint.dm" +#include "code\datums\elements\lootable\mecha.dm" +#include "code\datums\elements\lootable\misc.dm" +#include "code\datums\elements\lootable\surface.dm" +#include "code\datums\elements\lootable\trash.dm" +#include "code\datums\elements\vore\spontaneous_vore.dm" +>>>>>>> 11a4471110 ([MIRROR] Spontaneous Vore Element (#11785)) #include "code\datums\game_masters\_common.dm" #include "code\datums\helper_datums\construction_datum.dm" #include "code\datums\helper_datums\events.dm"