diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm index 98d009ef143..d9ca290f684 100644 --- a/code/__DEFINES/gamemode.dm +++ b/code/__DEFINES/gamemode.dm @@ -37,7 +37,7 @@ #define SPECIAL_ROLE_NUKEOPS "Syndicate" #define SPECIAL_ROLE_PYROCLASTIC_SLIME "Pyroclastic Anomaly Slime" #define SPECIAL_ROLE_REVENANT "Revenant" -#define SPECIAL_ROLE_SLAUGHTER_DEMON "Slaughter Demon" +#define SPECIAL_ROLE_DEMON "Demon" #define SPECIAL_ROLE_SUPER "Super" #define SPECIAL_ROLE_SYNDICATE_DEATHSQUAD "Syndicate Commando" #define SPECIAL_ROLE_TRAITOR "Traitor" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index bb4e7e4d30c..e7aae77bc2a 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -244,7 +244,8 @@ #define isnymph(A) (istype((A), /mob/living/simple_animal/diona)) #define ishostile(A) (istype((A), /mob/living/simple_animal/hostile)) #define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider)) -#define isslaughterdemon(A) (istype((A), /mob/living/simple_animal/slaughter)) +#define isslaughterdemon(A) (istype((A), /mob/living/simple_animal/demon/slaughter)) +#define isdemon(A) (istype((A), /mob/living/simple_animal/demon)) #define issilicon(A) (istype((A), /mob/living/silicon)) #define isAI(A) (istype((A), /mob/living/silicon/ai)) diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 89847fe1246..55d9dce7968 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -28,7 +28,7 @@ #define ROLE_TRADER "trader" #define ROLE_VAMPIRE "vampire" // Role tags for EVERYONE! -#define ROLE_DEMON "slaughter demon" +#define ROLE_DEMON "demon" #define ROLE_SENTIENT "sentient animal" #define ROLE_POSIBRAIN "positronic brain" #define ROLE_GUARDIAN "guardian" diff --git a/code/datums/spell_targeting/aoe.dm b/code/datums/spell_targeting/aoe.dm index e3369de737d..306a59798d1 100644 --- a/code/datums/spell_targeting/aoe.dm +++ b/code/datums/spell_targeting/aoe.dm @@ -8,8 +8,9 @@ /datum/spell_targeting/aoe/choose_targets(mob/user, obj/effect/proc_holder/spell/spell, params, atom/clicked_atom) var/list/targets = list() + var/spell_center = use_turf_of_user ? get_turf(user) : user - for(var/atom/target in view_or_range(range, user, selection_type)) + for(var/atom/target in view_or_range(range, spell_center, selection_type)) if(valid_target(target, user, spell, FALSE)) targets += target if(inner_radius >= 0) diff --git a/code/datums/spells/bloodcrawl.dm b/code/datums/spells/bloodcrawl.dm index d8299e43ac2..6c3b770e817 100644 --- a/code/datums/spells/bloodcrawl.dm +++ b/code/datums/spells/bloodcrawl.dm @@ -9,12 +9,13 @@ action_icon_state = "bloodcrawl" action_background_icon_state = "bg_demon" panel = "Demon" + var/allowed_type = /obj/effect/decal/cleanable var/phased = FALSE /obj/effect/proc_holder/spell/bloodcrawl/create_new_targeting() var/datum/spell_targeting/targeted/T = new() T.selection_type = SPELL_SELECTION_RANGE - T.allowed_type = /obj/effect/decal/cleanable + T.allowed_type = allowed_type T.random_target = TRUE T.range = 1 T.use_turf_of_user = TRUE @@ -31,11 +32,241 @@ return FALSE /obj/effect/proc_holder/spell/bloodcrawl/cast(list/targets, mob/living/user) - var/obj/effect/decal/cleanable/target = targets[1] // TODO Test this spell + var/atom/target = targets[1] if(phased) - if(user.phasein(target)) + if(phasein(target, user)) phased = FALSE else - if(user.phaseout(target)) + if(phaseout(target, user)) phased = TRUE cooldown_handler.start_recharge() + +//Travel through pools of blood. Slaughter Demon powers for everyone! +#define BLOODCRAWL 1 +#define BLOODCRAWL_EAT 2 + + +/obj/item/bloodcrawl + name = "blood crawl" + desc = "You are unable to hold anything while in this form." + icon = 'icons/effects/blood.dmi' + flags = NODROP|ABSTRACT + +/obj/effect/dummy/slaughter //Can't use the wizard one, blocked by jaunt/slow + name = "odd blood" + icon = 'icons/effects/effects.dmi' + icon_state = "nothing" + density = FALSE + anchored = TRUE + invisibility = 60 + resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/effect/dummy/slaughter/relaymove(mob/user, direction) + forceMove(get_step(src, direction)) + +/obj/effect/dummy/slaughter/ex_act() + return + +/obj/effect/dummy/slaughter/bullet_act() + return + +/obj/effect/dummy/slaughter/singularity_act() + return + + +/obj/effect/proc_holder/spell/bloodcrawl/proc/block_hands(mob/living/carbon/C) + if(C.l_hand || C.r_hand) + to_chat(C, "You may not hold items while blood crawling!") + return FALSE + var/obj/item/bloodcrawl/B1 = new(C) + var/obj/item/bloodcrawl/B2 = new(C) + B1.icon_state = "bloodhand_left" + B2.icon_state = "bloodhand_right" + C.put_in_hands(B1) + C.put_in_hands(B2) + C.regenerate_icons() + return TRUE + +/obj/effect/proc_holder/spell/bloodcrawl/proc/sink_animation(atom/A, mob/living/L) + var/turf/mob_loc = get_turf(L) + visible_message("[L] sinks into [A].") + playsound(mob_loc, 'sound/misc/enter_blood.ogg', 100, 1, -1) + var/atom/movable/overlay/animation = new(mob_loc) + animation.name = "odd blood" + animation.density = FALSE + animation.anchored = TRUE + animation.icon = 'icons/mob/mob.dmi' + animation.icon_state = "jaunt" + animation.layer = 5 + animation.master = mob_loc + animation.dir = L.dir + QDEL_IN(animation, 0.6 SECONDS) + +/obj/effect/proc_holder/spell/bloodcrawl/proc/handle_consumption(mob/living/L, mob/living/victim, atom/A, obj/effect/dummy/slaughter/holder) + if(!HAS_TRAIT(L, TRAIT_BLOODCRAWL_EAT)) + return + + if(!istype(victim)) + return + if(victim.stat == CONSCIOUS) + A.visible_message("[victim] kicks free of [A] just before entering it!") + L.stop_pulling() + return + + victim.forceMove(holder) + victim.emote("scream") + A.visible_message("[L] drags [victim] into [A]!") + L.stop_pulling() + to_chat(L, "You begin to feast on [victim]. You can not move while you are doing this.") + A.visible_message("Loud eating sounds come from the blood...") + var/sound + if(isslaughterdemon(L)) + var/mob/living/simple_animal/demon/slaughter/SD = L + sound = SD.feast_sound + else + sound = 'sound/misc/demon_consume.ogg' + + for(var/i in 1 to 3) + playsound(get_turf(L), sound, 100, 1) + sleep(3 SECONDS) + + if(!victim) + to_chat(L, "You happily devour... nothing? Your meal vanished at some point!") + return + + if(ishuman(victim) || isrobot(victim)) + to_chat(L, "You devour [victim]. Your health is fully restored.") + L.adjustBruteLoss(-1000) + L.adjustFireLoss(-1000) + L.adjustOxyLoss(-1000) + L.adjustToxLoss(-1000) + else + to_chat(L, "You devour [victim], but this measly meal barely sates your appetite!") + L.adjustBruteLoss(-25) + L.adjustFireLoss(-25) + + if(isslaughterdemon(L)) + var/mob/living/simple_animal/demon/slaughter/demon = L + demon.devoured++ + to_chat(victim, "You feel teeth sink into your flesh, and the--") + victim.adjustBruteLoss(1000) + victim.forceMove(demon) + demon.consumed_mobs.Add(victim) + if(ishuman(victim)) + var/mob/living/carbon/human/H = victim + if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = H.w_uniform + U.sensor_mode = SENSOR_OFF + else + victim.ghostize() + qdel(victim) + +/obj/effect/proc_holder/spell/bloodcrawl/proc/post_phase_in(mob/living/L, obj/effect/dummy/slaughter/holder) + L.notransform = FALSE + +/obj/effect/proc_holder/spell/bloodcrawl/proc/phaseout(obj/effect/decal/cleanable/B, mob/living/L) + + if(iscarbon(L) && !block_hands(L)) + return FALSE + + L.notransform = TRUE + INVOKE_ASYNC(src, PROC_REF(async_phase), B, L) + return TRUE + +/obj/effect/proc_holder/spell/bloodcrawl/proc/async_phase(obj/effect/decal/cleanable/B, mob/living/L) + var/turf/mobloc = get_turf(L) + sink_animation(B, L) + var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(mobloc) + L.forceMove(holder) + L.ExtinguishMob() + handle_consumption(L, L.pulling, B, holder) + post_phase_in(L, holder) + +/obj/effect/proc_holder/spell/bloodcrawl/proc/rise_animation(turf/tele_loc, mob/living/L, atom/A) + var/atom/movable/overlay/animation = new(tele_loc) + animation.name = "odd blood" + animation.density = FALSE + animation.anchored = TRUE + animation.icon = 'icons/mob/mob.dmi' + animation.icon_state = "jauntup" //Paradise Port:I reversed the jaunt animation so it looks like its rising up + animation.layer = 5 + animation.master = tele_loc + animation.dir = L.dir + if(prob(25) && isdemon(L)) + var/list/voice = list('sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/i_see_you1.ogg') + playsound(tele_loc, pick(voice),50, 1, -1) + A.visible_message("[L] rises out of [A]!") + playsound(get_turf(tele_loc), 'sound/misc/exit_blood.ogg', 100, 1, -1) + QDEL_IN(animation, 0.6 SECONDS) + +/obj/effect/proc_holder/spell/bloodcrawl/proc/unblock_hands(mob/living/carbon/C) + if(!istype(C)) + return + for(var/obj/item/bloodcrawl/BC in C) + qdel(BC) + +/obj/effect/proc_holder/spell/bloodcrawl/proc/rise_message(atom/A) + A.visible_message("[A] starts to bubble...") + +/obj/effect/proc_holder/spell/bloodcrawl/proc/post_phase_out(atom/A, mob/living/L) + if(isslaughterdemon(L)) + var/mob/living/simple_animal/demon/slaughter/S = L + S.speed = 0 + S.boost = world.time + 6 SECONDS + L.color = A.color + addtimer(VARSET_CALLBACK(L, color, null), 6 SECONDS) + + +/obj/effect/proc_holder/spell/bloodcrawl/proc/phasein(atom/A, mob/living/L) + + if(L.notransform) + to_chat(L, "Finish eating first!") + return FALSE + rise_message(A) + if(!do_after(L, 2 SECONDS, target = A)) + return FALSE + if(!A) + return FALSE + var/turf/tele_loc = isturf(A) ? A : A.loc + var/holder = L.loc + L.forceMove(tele_loc) + L.client.eye = L + + rise_animation(tele_loc, L, A) + + unblock_hands(L) + + QDEL_NULL(holder) + + post_phase_out(A, L) + return TRUE + +/obj/effect/proc_holder/spell/bloodcrawl/shadow_crawl + name = "Shadow Crawl" + desc = "Use darkness to phase out of existence." + allowed_type = /turf + action_background_icon_state = "shadow_demon_bg" + action_icon_state = "shadow_crawl" + +/obj/effect/proc_holder/spell/bloodcrawl/shadow_crawl/valid_target(turf/target, user) + return target.get_lumcount() < 0.2 + +/obj/effect/proc_holder/spell/bloodcrawl/shadow_crawl/rise_message(atom/A) + return + +/obj/effect/proc_holder/spell/bloodcrawl/shadow_crawl/rise_animation(turf/tele_loc, mob/living/L, atom/A) + return + +/obj/effect/proc_holder/spell/bloodcrawl/shadow_crawl/handle_consumption(mob/living/L, mob/living/victim, atom/A, obj/effect/dummy/slaughter/holder) + return + +/obj/effect/proc_holder/spell/bloodcrawl/shadow_crawl/sink_animation(atom/A, mob/living/L) + A.visible_message("[L] sinks into the shadows...") + +/obj/effect/proc_holder/spell/bloodcrawl/shadow_crawl/post_phase_in(mob/living/L, obj/effect/dummy/slaughter/holder) + ..() + if(!istype(L, /mob/living/simple_animal/demon/shadow)) + return + var/mob/living/simple_animal/demon/shadow/S = L + S.RegisterSignal(holder, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/mob/living/simple_animal/demon/shadow, check_darkness)) + diff --git a/code/game/gamemodes/cult/cult_actions.dm b/code/game/gamemodes/cult/cult_actions.dm index 449a109f315..26c08709a2d 100644 --- a/code/game/gamemodes/cult/cult_actions.dm +++ b/code/game/gamemodes/cult/cult_actions.dm @@ -47,7 +47,7 @@ var/title var/large = FALSE var/living_message - if(istype(user, /mob/living/simple_animal/slaughter/cult)) //Harbringers of the Slaughter + if(istype(user, /mob/living/simple_animal/demon/slaughter/cult)) //Harbringers of the Slaughter title = "Harbringer of the Slaughter" large = TRUE else diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 2b1487db3ed..22eeda637d8 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -303,7 +303,7 @@ if(curselimit > 1) to_chat(user, "We have exhausted our ability to curse the shuttle.") return - if(locate(/obj/singularity/narsie) in GLOB.poi_list || locate(/mob/living/simple_animal/slaughter/cult) in GLOB.mob_list) + if(locate(/obj/singularity/narsie) in GLOB.poi_list || locate(/mob/living/simple_animal/demon/slaughter/cult) in GLOB.mob_list) to_chat(user, "Nar'Sie or her avatars are already on this plane, there is no delaying the end of all things.") return diff --git a/code/game/gamemodes/miniantags/demons/demon.dm b/code/game/gamemodes/miniantags/demons/demon.dm new file mode 100644 index 00000000000..e75851d9c4a --- /dev/null +++ b/code/game/gamemodes/miniantags/demons/demon.dm @@ -0,0 +1,36 @@ +/mob/living/simple_animal/demon + name = "a generic demon" + desc = "you shouldnt be reading this, file a github report" + speak_emote = list("gurgles") + emote_hear = list("wails","screeches") + response_help = "thinks better of touching" + response_disarm = "flails at" + response_harm = "punches" + speed = 1 + a_intent = INTENT_HARM + mob_biotypes = MOB_ORGANIC | MOB_HUMANOID + stop_automated_movement = TRUE + status_flags = CANPUSH + attack_sound = 'sound/misc/demon_attack1.ogg' + death_sound = 'sound/misc/demon_dies.ogg' + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + maxbodytemp = INFINITY + faction = list("demon") + attacktext = "wildly tears into" + maxHealth = 200 + health = 200 + environment_smash = ENVIRONMENT_SMASH_STRUCTURES + obj_damage = 50 + melee_damage_lower = 30 + melee_damage_upper = 30 + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + del_on_death = TRUE + var/datum/action/innate/demon/whisper/whisper_action + +/mob/living/simple_animal/demon/Initialize(mapload) + . = ..() + whisper_action = new() + whisper_action.Grant(src) + diff --git a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm new file mode 100644 index 00000000000..34869d46d84 --- /dev/null +++ b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm @@ -0,0 +1,122 @@ +/mob/living/simple_animal/demon/shadow + name = "shadow demon" + desc = "A creature that's barely tangible, you can feel its gaze piercing you" + icon = 'icons/mob/mob.dmi' + icon_state = "shadow_demon" + icon_living = "shadow_demon" + move_resist = MOVE_FORCE_STRONG + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE // so they can tell where the darkness is + loot = list(/obj/item/organ/internal/heart/demon/shadow) + var/thrown_alert = FALSE + +/mob/living/simple_animal/demon/shadow/Life(seconds, times_fired) + . = ..() + var/lum_count = check_darkness() + var/damage_mod = istype(loc, /obj/effect/dummy/slaughter) ? 0.5 : 1 + if(lum_count > 0.2) + adjustBruteLoss(40 * damage_mod) // 10 seconds in light + SEND_SOUND(src, sound('sound/weapons/sear.ogg')) + to_chat(src, "The light scalds you!") + else + adjustBruteLoss(-20) + + +/mob/living/simple_animal/demon/shadow/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + if(isliving(AM)) // when a living creature is thrown at it, dont knock it back + return + ..() + + +/mob/living/simple_animal/demon/shadow/Initialize(mapload) + . = ..() + AddSpell(new /obj/effect/proc_holder/spell/fireball/shadow_grapple) + var/obj/effect/proc_holder/spell/bloodcrawl/shadow_crawl/S = new + AddSpell(S) + if(istype(loc, /obj/effect/dummy/slaughter)) + S.phased = TRUE + RegisterSignal(loc, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/mob/living/simple_animal/demon/shadow, check_darkness)) + RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(check_darkness)) + +/mob/living/simple_animal/demon/shadow/proc/check_darkness() + var/turf/T = get_turf(src) + var/lum_count = T.get_lumcount() + if(lum_count > 0.2) + if(!thrown_alert) + thrown_alert = TRUE + throw_alert("light", /obj/screen/alert/lightexposure) + alpha = 255 + else + if(thrown_alert) + thrown_alert = FALSE + clear_alert("light") + alpha = 125 + return lum_count + + +/obj/effect/proc_holder/spell/fireball/shadow_grapple + name = "Shadow Grapple" + desc = "Fire one of your hands, if it hits a person it pulls them in. If you hit a structure you get pulled to the structure." + base_cooldown = 10 SECONDS + fireball_type = /obj/item/projectile/magic/shadow_hand + + selection_activated_message = "You raise your hand, full of demonic energy! Left-click to cast at a target!" + selection_deactivated_message = "You re-absorb the energy...for now." + + action_background_icon_state = "shadow_demon_bg" + action_icon_state = "shadow_grapple" + panel = "Demon" + + sound = null + invocation_type = "none" + invocation = null + +/obj/effect/proc_holder/spell/fireball/shadow_grapple/update_icon_state() + return + +/obj/item/projectile/magic/shadow_hand + name = "shadow hand" + icon_state = "shadow_hand" + plane = FLOOR_PLANE + var/hit = FALSE + +/obj/item/projectile/magic/shadow_hand/fire(setAngle) + if(firer) + firer.Beam(src, icon_state = "grabber_beam", time = INFINITY, maxdistance = INFINITY, beam_sleep_time = 1, beam_type = /obj/effect/ebeam/floor) + return ..() + +/obj/item/projectile/magic/shadow_hand/on_hit(atom/target, blocked, hit_zone) + if(hit) + return + hit = TRUE // to prevent double hits from the pull + . = ..() + if(!isliving(target)) + firer.throw_at(get_step(target, get_dir(target, firer)), 50, 10) + else + var/mob/living/L = target + L.Immobilize(2 SECONDS) + L.apply_damage(40, BRUTE, BODY_ZONE_CHEST) + L.throw_at(get_step(firer, get_dir(firer, target)), 50, 10) + target.extinguish_light(TRUE) + +/obj/effect/ebeam/floor + plane = FLOOR_PLANE + +/obj/item/organ/internal/heart/demon/shadow + name = "heart of darkness" + desc = "It still beats furiously, emitting an aura of fear." + color = COLOR_BLACK + +/obj/item/organ/internal/heart/demon/shadow/attack_self(mob/living/user) + . = ..() + user.drop_item() + insert(user) + +/obj/item/organ/internal/heart/demon/shadow/insert(mob/living/carbon/M, special = 0) + . = ..() + if(M.mind) + M.mind.AddSpell(new /obj/effect/proc_holder/spell/fireball/shadow_grapple) + +/obj/item/organ/internal/heart/demon/shadow/remove(mob/living/carbon/M, special = 0) + ..() + if(M.mind) + M.mind.RemoveSpell(/obj/effect/proc_holder/spell/fireball/shadow_grapple) diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/demons/slaughter demon/slaughter.dm similarity index 86% rename from code/game/gamemodes/miniantags/slaughter/slaughter.dm rename to code/game/gamemodes/miniantags/demons/slaughter demon/slaughter.dm index 019cc987c05..6f00c71c91d 100644 --- a/code/game/gamemodes/miniantags/slaughter/slaughter.dm +++ b/code/game/gamemodes/miniantags/demons/slaughter demon/slaughter.dm @@ -1,42 +1,15 @@ //////////////////The Monster -/mob/living/simple_animal/slaughter +/mob/living/simple_animal/demon/slaughter name = "slaughter demon" real_name = "slaughter demon" desc = "A large, menacing creature covered in armored black scales. You should run." speak = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri", "orkan", "allaq") - speak_emote = list("gurgles") - emote_hear = list("wails","screeches") - response_help = "thinks better of touching" - response_disarm = "flails at" - response_harm = "punches" icon = 'icons/mob/mob.dmi' icon_state = "daemon" icon_living = "daemon" - speed = 1 - a_intent = INTENT_HARM - mob_biotypes = MOB_ORGANIC | MOB_HUMANOID - stop_automated_movement = TRUE - status_flags = CANPUSH - attack_sound = 'sound/misc/demon_attack1.ogg' - var/feast_sound = 'sound/misc/demon_consume.ogg' - death_sound = 'sound/misc/demon_dies.ogg' - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - maxbodytemp = INFINITY - faction = list("slaughter") - attacktext = "wildly tears into" - maxHealth = 200 - health = 200 - environment_smash = 1 - obj_damage = 50 - melee_damage_lower = 30 - melee_damage_upper = 30 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE var/boost = 0 - - + var/feast_sound = 'sound/misc/demon_consume.ogg' var/devoured = 0 var/list/consumed_mobs = list() @@ -44,7 +17,7 @@ var/cooldown = 0 var/gorecooldown = 0 var/vialspawned = FALSE - loot = list(/obj/effect/decal/cleanable/blood/innards, /obj/effect/decal/cleanable/blood, /obj/effect/gibspawner/generic, /obj/effect/gibspawner/generic, /obj/item/organ/internal/heart/demon) + loot = list(/obj/effect/decal/cleanable/blood/innards, /obj/effect/decal/cleanable/blood, /obj/effect/gibspawner/generic, /obj/effect/gibspawner/generic, /obj/item/organ/internal/heart/demon/slaughter) var/playstyle_string = "You are the Slaughter Demon, a terrible creature from another existence. You have a single desire: to kill. \ You may use the blood crawl icon when on blood pools to travel through them, appearing and dissapearing from the station at will. \ Pulling a dead or critical mob while you enter a pool will pull them in with you, allowing you to feast. \ @@ -52,30 +25,26 @@ del_on_death = TRUE deathmessage = "screams in anger as it collapses into a puddle of viscera!" - var/datum/action/innate/demon/whisper/whisper_action - -/mob/living/simple_animal/slaughter/New() +/mob/living/simple_animal/demon/slaughter/New() ..() remove_from_all_data_huds() ADD_TRAIT(src, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat") var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new AddSpell(bloodspell) - whisper_action = new() - whisper_action.Grant(src) if(istype(loc, /obj/effect/dummy/slaughter)) bloodspell.phased = TRUE addtimer(CALLBACK(src, PROC_REF(attempt_objectives)), 5 SECONDS) -/mob/living/simple_animal/slaughter/Life(seconds, times_fired) +/mob/living/simple_animal/demon/slaughter/Life(seconds, times_fired) ..() if(boost < world.time) speed = 1 else speed = 0 -/mob/living/simple_animal/slaughter/proc/attempt_objectives() +/mob/living/simple_animal/demon/slaughter/proc/attempt_objectives() if(mind) to_chat(src, src.playstyle_string) to_chat(src, "You are not currently in the same plane of existence as the station. Use the blood crawl action at a blood pool to manifest.") @@ -100,28 +69,24 @@ name = "pile of viscera" desc = "A repulsive pile of guts and gore." -/mob/living/simple_animal/slaughter/Destroy() +/mob/living/simple_animal/demon/slaughter/Destroy() // Only execute the below if we successfully died for(var/mob/living/M in consumed_mobs) release_consumed(M) . = ..() -/mob/living/simple_animal/slaughter/proc/release_consumed(mob/living/M) +/mob/living/simple_animal/demon/slaughter/proc/release_consumed(mob/living/M) M.forceMove(get_turf(src)) -/mob/living/simple_animal/slaughter/phasein() - . = ..() - speed = 0 - boost = world.time + 60 // Midround slaughter demon, less tanky -/mob/living/simple_animal/slaughter/lesser +/mob/living/simple_animal/demon/slaughter/lesser maxHealth = 130 health = 130 // Cult slaughter demon -/mob/living/simple_animal/slaughter/cult //Summoned as part of the cult objective "Bring the Slaughter" +/mob/living/simple_animal/demon/slaughter/cult //Summoned as part of the cult objective "Bring the Slaughter" name = "harbinger of the slaughter" real_name = "harbinger of the Slaughter" desc = "An awful creature from beyond the realms of madness." @@ -163,10 +128,10 @@ return 0 to_chat(user, "You sense a terrified soul at [A]. Show [A.p_them()] the error of [A.p_their()] ways.") -/mob/living/simple_animal/slaughter/cult/New() +/mob/living/simple_animal/demon/slaughter/cult/New() ..() spawn(5) - var/list/demon_candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, 10 SECONDS, source = /mob/living/simple_animal/slaughter/cult) + var/list/demon_candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, 10 SECONDS, source = /mob/living/simple_animal/demon/slaughter/cult) if(!demon_candidates.len) visible_message("[src] disappears in a flash of red light!") qdel(src) @@ -174,7 +139,7 @@ if(QDELETED(src)) // Just in case return var/mob/M = pick(demon_candidates) - var/mob/living/simple_animal/slaughter/cult/S = src + var/mob/living/simple_animal/demon/slaughter/cult/S = src if(!M || !M.client) visible_message("[src] disappears in a flash of red light!") qdel(src) @@ -240,7 +205,7 @@ //////////The Loot -//The loot from killing a slaughter demon - can be consumed to allow the user to blood crawl +// Demon heart base type /obj/item/organ/internal/heart/demon name = "demon heart" desc = "Still it beats furiously, emanating an aura of utter hate." @@ -254,11 +219,22 @@ /obj/item/organ/internal/heart/demon/prepare_eat() return // Just so people don't accidentally waste it +/obj/item/organ/internal/heart/demon/Stop() + return 0 // Always beating. + /obj/item/organ/internal/heart/demon/attack_self(mob/living/user) user.visible_message("[user] raises [src] to [user.p_their()] mouth and tears into it with [user.p_their()] teeth!", \ "An unnatural hunger consumes you. You raise [src] to your mouth and devour it!") playsound(user, 'sound/misc/demon_consume.ogg', 50, 1) +//////////The Loot + +//The loot from killing a slaughter demon - can be consumed to allow the user to blood crawl +/// SLAUGHTER DEMON HEART + +/obj/item/organ/internal/heart/demon/slaughter/attack_self(mob/living/user) + ..() + // Eating the heart for the first time. Gives basic bloodcrawling. This is the only time we need to insert the heart. if(!HAS_TRAIT(user, TRAIT_BLOODCRAWL)) user.visible_message("[user]'s eyes flare a deep crimson!", \ @@ -279,23 +255,19 @@ to_chat(user, "...and you don't feel any different.") qdel(src) -/obj/item/organ/internal/heart/demon/insert(mob/living/carbon/M, special = 0) +/obj/item/organ/internal/heart/demon/slaughter/insert(mob/living/carbon/M, special = 0) . = ..() if(M.mind) M.mind.AddSpell(new /obj/effect/proc_holder/spell/bloodcrawl(null)) -/obj/item/organ/internal/heart/demon/remove(mob/living/carbon/M, special = 0) +/obj/item/organ/internal/heart/demon/slaughter/remove(mob/living/carbon/M, special = 0) ..() if(M.mind) REMOVE_TRAIT(M, TRAIT_BLOODCRAWL, "bloodcrawl") REMOVE_TRAIT(M, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat") M.mind.RemoveSpell(/obj/effect/proc_holder/spell/bloodcrawl) -/obj/item/organ/internal/heart/demon/Stop() - return 0 // Always beating. - - -/mob/living/simple_animal/slaughter/laughter +/mob/living/simple_animal/demon/slaughter/laughter // The laughter demon! It's everyone's best friend! It just wants to hug // them so much, it wants to hug everyone at once! name = "laughter demon" @@ -324,7 +296,7 @@ deathmessage = "fades out, as all of its friends are released from its prison of hugs." loot = list(/mob/living/simple_animal/pet/cat/kitten{name = "Laughter"}) -/mob/living/simple_animal/slaughter/laughter/release_consumed(mob/living/M) +/mob/living/simple_animal/demon/slaughter/laughter/release_consumed(mob/living/M) if(M.revive()) M.grab_ghost(force = TRUE) playsound(get_turf(src), feast_sound, 50, 1, -1) @@ -348,7 +320,7 @@ for(var/datum/mind/M in get_owners()) if(!isslaughterdemon(M.current) || QDELETED(M.current)) continue - var/mob/living/simple_animal/slaughter/R = M.current + var/mob/living/simple_animal/demon/slaughter/R = M.current kill_count += R.devoured if(kill_count >= targetKill) return TRUE diff --git a/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm b/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm deleted file mode 100644 index 988ff1f4595..00000000000 --- a/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm +++ /dev/null @@ -1,180 +0,0 @@ -//Travel through pools of blood. Slaughter Demon powers for everyone! -#define BLOODCRAWL 1 -#define BLOODCRAWL_EAT 2 - -/mob/living/proc/phaseout(obj/effect/decal/cleanable/B) - - if(iscarbon(src)) - var/mob/living/carbon/C = src - if(C.l_hand || C.r_hand) - to_chat(C, "You may not hold items while blood crawling!") - return 0 - var/obj/item/bloodcrawl/B1 = new(C) - var/obj/item/bloodcrawl/B2 = new(C) - B1.icon_state = "bloodhand_left" - B2.icon_state = "bloodhand_right" - C.put_in_hands(B1) - C.put_in_hands(B2) - C.regenerate_icons() - - var/mob/living/kidnapped = null - var/turf/mobloc = get_turf(loc) - notransform = TRUE - spawn(0) - visible_message("[src] sinks into [B].") - playsound(get_turf(src), 'sound/misc/enter_blood.ogg', 100, 1, -1) - var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(mobloc) - var/atom/movable/overlay/animation = new /atom/movable/overlay(mobloc) - animation.name = "odd blood" - animation.density = FALSE - animation.anchored = TRUE - animation.icon = 'icons/mob/mob.dmi' - animation.icon_state = "jaunt" - animation.layer = 5 - animation.master = holder - animation.dir = dir - - ExtinguishMob() - if(pulling && HAS_TRAIT(src, TRAIT_BLOODCRAWL_EAT)) - if(isliving(pulling)) - var/mob/living/victim = pulling - if(victim.stat == CONSCIOUS) - visible_message("[victim] kicks free of [B] just before entering it!") - stop_pulling() - else - victim.forceMove(holder)//holder - victim.emote("scream") - visible_message("[src] drags [victim] into [B]!") - kidnapped = victim - stop_pulling() - flick("jaunt",animation) - - src.holder = holder - forceMove(holder) - - if(kidnapped) - to_chat(src, "You begin to feast on [kidnapped]. You can not move while you are doing this.") - visible_message("Loud eating sounds come from the blood...") - sleep(6) - if(animation) - qdel(animation) - var/sound - if(isslaughterdemon(src)) - var/mob/living/simple_animal/slaughter/SD = src - sound = SD.feast_sound - else - sound = 'sound/misc/demon_consume.ogg' - - for(var/i in 1 to 3) - playsound(get_turf(src), sound, 100, 1) - sleep(30) - - if(kidnapped) - if(ishuman(kidnapped) || isrobot(kidnapped)) - to_chat(src, "You devour [kidnapped]. Your health is fully restored.") - adjustBruteLoss(-1000) - adjustFireLoss(-1000) - adjustOxyLoss(-1000) - adjustToxLoss(-1000) - else - to_chat(src, "You devour [kidnapped], but this measly meal barely sates your appetite!") - adjustBruteLoss(-25) - adjustFireLoss(-25) - if(istype(src, /mob/living/simple_animal/slaughter)) //rason, do not want humans to get this - var/mob/living/simple_animal/slaughter/demon = src - demon.devoured++ - to_chat(kidnapped, "You feel teeth sink into your flesh, and the--") - kidnapped.adjustBruteLoss(1000) - kidnapped.forceMove(src) - demon.consumed_mobs.Add(kidnapped) - if(ishuman(kidnapped)) - var/mob/living/carbon/human/H = kidnapped - if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - U.sensor_mode = SENSOR_OFF - else - kidnapped.ghostize() - qdel(kidnapped) - else - to_chat(src, "You happily devour... nothing? Your meal vanished at some point!") - else - sleep(6) - if(animation) - qdel(animation) - notransform = FALSE - return 1 - -/obj/item/bloodcrawl - name = "blood crawl" - desc = "You are unable to hold anything while in this form." - icon = 'icons/effects/blood.dmi' - flags = NODROP|ABSTRACT - -/mob/living/proc/phasein(obj/effect/decal/cleanable/B) - - if(notransform) - to_chat(src, "Finish eating first!") - return 0 - B.visible_message("[B] starts to bubble...") - if(!do_after(src, 20, target = B)) - return - if(!B) - return - forceMove(B.loc) - client.eye = src - - var/atom/movable/overlay/animation = new /atom/movable/overlay( B.loc ) - animation.name = "odd blood" - animation.density = FALSE - animation.anchored = TRUE - animation.icon = 'icons/mob/mob.dmi' - animation.icon_state = "jauntup" //Paradise Port:I reversed the jaunt animation so it looks like its rising up - animation.layer = 5 - animation.master = B.loc - animation.dir = dir - - if(prob(25) && isslaughterdemon(src)) - var/list/voice = list('sound/hallucinations/behind_you1.ogg','sound/hallucinations/im_here1.ogg','sound/hallucinations/turn_around1.ogg','sound/hallucinations/i_see_you1.ogg') - playsound(get_turf(src), pick(voice),50, 1, -1) - visible_message("\The [src] rises out of \the [B]!") - playsound(get_turf(src), 'sound/misc/exit_blood.ogg', 100, 1, -1) - - flick("jauntup",animation) - QDEL_NULL(holder) - - if(iscarbon(src)) - var/mob/living/carbon/C = src - for(var/obj/item/bloodcrawl/BC in C) - C.flags = null - C.unEquip(BC) - qdel(BC) - - var/oldcolor = color - color = B.color - sleep(6)//wait for animation to finish - if(animation) - qdel(animation) - spawn(30) - color = oldcolor - return 1 - -/obj/effect/dummy/slaughter //Can't use the wizard one, blocked by jaunt/slow - name = "odd blood" - icon = 'icons/effects/effects.dmi' - icon_state = "nothing" - density = FALSE - anchored = TRUE - invisibility = 60 - resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - -/obj/effect/dummy/slaughter/relaymove(mob/user, direction) - forceMove(get_step(src,direction)) - -/obj/effect/dummy/slaughter/ex_act() - return - -/obj/effect/dummy/slaughter/bullet_act() - return - -/obj/effect/dummy/slaughter/singularity_act() - return diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index df331cda39c..e4ecff59ca6 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -30,7 +30,7 @@ return FALSE return TRUE -/obj/machinery/computer/extinguish_light() +/obj/machinery/computer/extinguish_light(force = FALSE) set_light(0) underlays.Cut() visible_message("[src] grows dim, its screen barely readable.") diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index dd349be81fe..68c16ce8be7 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -97,7 +97,7 @@ else set_light(1, LIGHTING_MINIMUM_POWER) -/obj/machinery/door/firedoor/extinguish_light() +/obj/machinery/door/firedoor/extinguish_light(force = FALSE) set_light(0) update_icon(UPDATE_OVERLAYS) diff --git a/code/game/machinery/dye_generator.dm b/code/game/machinery/dye_generator.dm index fb2551c3e45..8a0def9bb5b 100644 --- a/code/game/machinery/dye_generator.dm +++ b/code/game/machinery/dye_generator.dm @@ -26,7 +26,7 @@ set_light(0) update_icon(UPDATE_OVERLAYS) -/obj/machinery/dye_generator/extinguish_light() +/obj/machinery/dye_generator/extinguish_light(force = FALSE) set_light(0) underlays.Cut() diff --git a/code/game/machinery/floodlight.dm b/code/game/machinery/floodlight.dm index 635608a0fe9..b780768ad90 100644 --- a/code/game/machinery/floodlight.dm +++ b/code/game/machinery/floodlight.dm @@ -145,7 +145,7 @@ update_icon(UPDATE_ICON_STATE) return TRUE -/obj/machinery/floodlight/extinguish_light() +/obj/machinery/floodlight/extinguish_light(force = FALSE) on = FALSE set_light(0) update_icon(UPDATE_ICON_STATE) diff --git a/code/game/machinery/vendors/vending.dm b/code/game/machinery/vendors/vending.dm index 7470e1d1373..f3e354b37f7 100644 --- a/code/game/machinery/vendors/vending.dm +++ b/code/game/machinery/vendors/vending.dm @@ -785,7 +785,7 @@ if(shoot_inventory && prob(shoot_chance)) throw_item() -/obj/machinery/economy/vending/extinguish_light() +/obj/machinery/economy/vending/extinguish_light(force = FALSE) set_light(0) underlays.Cut() diff --git a/code/game/objects/effects/glowshroom.dm b/code/game/objects/effects/glowshroom.dm index 27e791024d4..562c7390b6c 100644 --- a/code/game/objects/effects/glowshroom.dm +++ b/code/game/objects/effects/glowshroom.dm @@ -13,7 +13,7 @@ var/floor = 0 var/obj/item/seeds/myseed = /obj/item/seeds/glowshroom -/obj/structure/glowshroom/extinguish_light() +/obj/structure/glowshroom/extinguish_light(force = FALSE) visible_message("[src] withers away!") qdel(src) @@ -29,7 +29,7 @@ icon_state = "shadowshroom" myseed = /obj/item/seeds/glowshroom/shadowshroom -/obj/structure/glowshroom/shadowshroom/extinguish_light() +/obj/structure/glowshroom/shadowshroom/extinguish_light(force = FALSE) return /obj/structure/glowshroom/Destroy() diff --git a/code/game/objects/items/candle.dm b/code/game/objects/items/candle.dm index c3d4c366d0e..dda80c6fef4 100644 --- a/code/game/objects/items/candle.dm +++ b/code/game/objects/items/candle.dm @@ -141,6 +141,13 @@ if(lit) set_light(CANDLE_LUM * 2) + +/obj/item/candle/extinguish_light(force) + if(!force) + return + infinite = FALSE + wax = 1 // next process will burn it out + #undef TALL_CANDLE #undef MID_CANDLE #undef SHORT_CANDLE diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 21eca98f149..ef4561b5720 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -82,7 +82,7 @@ else return ..() -/obj/item/flashlight/extinguish_light() +/obj/item/flashlight/extinguish_light(force = FALSE) if(on) on = FALSE update_brightness() @@ -283,8 +283,12 @@ new T(loc) qdel(src) // return INITIALIZE_HINT_QDEL <-- Doesn't work -/obj/item/flashlight/flare/extinguish_light() - visible_message("[src] dims slightly before scattering the shadows around it.") +/obj/item/flashlight/flare/extinguish_light(force = FALSE) + if(force) + fuel = 0 + visible_message("[src] burns up rapidly!") + else + visible_message("[src] dims slightly before scattering the shadows around it.") /obj/item/flashlight/flare/torch name = "torch" @@ -321,8 +325,12 @@ /obj/item/flashlight/slime/attack_self(mob/user) return //Bio-luminescence does not toggle. -/obj/item/flashlight/slime/extinguish_light() - visible_message("[src] dims slightly before scattering the shadows around it.") +/obj/item/flashlight/slime/extinguish_light(force = FALSE) + if(force) + visible_message("[src] withers away.") + qdel(src) + else + visible_message("[src] dims slightly before scattering the shadows around it.") /obj/item/flashlight/emp origin_tech = "magnets=3;syndicate=1" diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index 800f4163ed6..3610a8b4919 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -329,7 +329,7 @@ M.emp_act(severity) ..() -/obj/item/paicard/extinguish_light() +/obj/item/paicard/extinguish_light(force = FALSE) if(pai) pai.extinguish_light() set_light(0) diff --git a/code/game/objects/items/tools/welder.dm b/code/game/objects/items/tools/welder.dm index af6f10c8f66..9faa226cbd5 100644 --- a/code/game/objects/items/tools/welder.dm +++ b/code/game/objects/items/tools/welder.dm @@ -72,6 +72,13 @@ reagents.add_reagent("fuel", 1) ..() +/obj/item/weldingtool/extinguish_light(force) + if(!force) + return + if(!tool_enabled) + return + remove_fuel(maximum_fuel) + /obj/item/weldingtool/attack_self(mob/user) if(tool_enabled) //Turn off the welder if it's on to_chat(user, "You switch off [src].") diff --git a/code/game/objects/items/weapons/cigs.dm b/code/game/objects/items/weapons/cigs.dm index f789c869865..3ef91574a60 100644 --- a/code/game/objects/items/weapons/cigs.dm +++ b/code/game/objects/items/weapons/cigs.dm @@ -189,6 +189,11 @@ LIGHTERS ARE IN LIGHTERS.DM smoke() +/obj/item/clothing/mask/cigarette/extinguish_light(force) + if(!force) + return + die() + /obj/item/clothing/mask/cigarette/attack_self(mob/user) if(lit) user.visible_message("[user] calmly drops and treads on [src], putting it out instantly.") @@ -362,6 +367,9 @@ LIGHTERS ARE IN LIGHTERS.DM chem_volume = 200 list_reagents = list("nicotine" = 200) +/obj/item/clothing/mask/cigarette/pipe/die() + return + /obj/item/clothing/mask/cigarette/pipe/light(flavor_text = null) if(!lit) lit = TRUE diff --git a/code/game/objects/items/weapons/lighters.dm b/code/game/objects/items/weapons/lighters.dm index fae9a68e9ae..256f9656863 100644 --- a/code/game/objects/items/weapons/lighters.dm +++ b/code/game/objects/items/weapons/lighters.dm @@ -69,10 +69,16 @@ force = 0 attack_verb = null //human_defense.dm takes care of it - show_off_message(user) + if(user) + show_off_message(user) set_light(0) STOP_PROCESSING(SSobj, src) +/obj/item/lighter/extinguish_light(force) + if(!force) + return + turn_off_lighter() + /obj/item/lighter/proc/show_off_message(mob/living/user) to_chat(user, "You shut off [src].") @@ -124,6 +130,9 @@ /obj/item/lighter/zippo/turn_off_lighter(mob/living/user) . = ..() + if(!user) + return + if(world.time > next_off_message) user.visible_message("You hear a quiet click, as [user] shuts off [src] without even looking at what [user.p_theyre()] doing. Wow.") playsound(src.loc, 'sound/items/zippoclose.ogg', 25, 1) @@ -201,6 +210,11 @@ ..() matchignite() +/obj/item/match/extinguish_light(force) + if(!force) + return + matchburnout() + /obj/item/match/proc/matchignite() if(!lit && !burnt) lit = TRUE diff --git a/code/game/turfs/simulated/floor/light_floor.dm b/code/game/turfs/simulated/floor/light_floor.dm index 033e8ac8bef..5c8fd14852c 100644 --- a/code/game/turfs/simulated/floor/light_floor.dm +++ b/code/game/turfs/simulated/floor/light_floor.dm @@ -103,8 +103,7 @@ A.addStaticPower(100, STATIC_LIGHT) update_icon() - -/turf/simulated/floor/light/extinguish_light() +/turf/simulated/floor/light/extinguish_light(force = FALSE) toggle_light(FALSE) visible_message("[src] flickers and falls dark.") diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index 2d8d485df45..efd2d781047 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -165,7 +165,7 @@ var/veil_msg = "You sense a dark presence lurking \ just beyond the veil..." var/objective_verb = "Kill" - var/mob/living/demon_type = /mob/living/simple_animal/slaughter + var/mob/living/demon_type = /mob/living/simple_animal/demon/slaughter /obj/item/antag_spawner/slaughter_demon/attack_self(mob/user) if(level_blocks_magic(user.z)) //this is to make sure the wizard does NOT summon a demon from the Den.. @@ -179,7 +179,7 @@ to_chat(user, "You break the seal on the bottle, calling upon the dire spirits of the underworld...") var/type = "slaughter" - if(demon_type == /mob/living/simple_animal/slaughter/laughter) + if(demon_type == /mob/living/simple_animal/demon/slaughter/laughter) type = "laughter" var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [type] demon summoned by [user.real_name]?", ROLE_DEMON, TRUE, 10 SECONDS, source = demon_type) @@ -196,9 +196,8 @@ /obj/item/antag_spawner/slaughter_demon/spawn_antag(client/C, turf/T, type = "", mob/user) var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(T) - var/mob/living/simple_animal/slaughter/S = new demon_type(holder) + var/mob/living/simple_animal/demon/slaughter/S = new demon_type(holder) S.vialspawned = TRUE - S.holder = holder S.key = C.key S.mind.assigned_role = S.name S.mind.special_role = S.name @@ -226,7 +225,7 @@ veil_msg = "You sense an adorable presence \ lurking just beyond the veil..." objective_verb = "Hug and tickle" - demon_type = /mob/living/simple_animal/slaughter/laughter + demon_type = /mob/living/simple_animal/demon/slaughter/laughter ///////////MORPH diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index 0b198442e12..14fd62bb2b6 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -44,10 +44,10 @@ /obj/item/clothing/head/hardhat/proc/turn_off(mob/user) set_light(0) -/obj/item/clothing/head/hardhat/extinguish_light(mob/living/user) +/obj/item/clothing/head/hardhat/extinguish_light(force = FALSE) if(on) on = FALSE - turn_off(user) + turn_off() update_icon(UPDATE_ICON_STATE) visible_message("[src]'s light fades and turns off.") diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 28f39c2f5f7..7e09f75ab8d 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -68,7 +68,7 @@ var/datum/action/A = X A.UpdateButtonIcon() -/obj/item/clothing/head/helmet/space/hardsuit/extinguish_light() +/obj/item/clothing/head/helmet/space/hardsuit/extinguish_light(force = FALSE) if(on) toggle_light() visible_message("[src]'s light fades and turns off.") diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index 5bc889da767..b5f7cc3a583 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -82,7 +82,7 @@ set_light(brightness_on) -/obj/item/clothing/head/helmet/space/plasmaman/extinguish_light() +/obj/item/clothing/head/helmet/space/plasmaman/extinguish_light(force = FALSE) if(on) toggle_light() diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm index 6b21388b23b..8c9e77b8bc2 100644 --- a/code/modules/events/event_container.dm +++ b/code/modules/events/event_container.dm @@ -197,6 +197,7 @@ GLOBAL_LIST_EMPTY(event_last_fired) new /datum/event_meta(EVENT_LEVEL_MAJOR, "Traders", /datum/event/traders, 85, is_one_shot = TRUE), new /datum/event_meta(EVENT_LEVEL_MAJOR, "Terror Spiders", /datum/event/spider_terror, 20, list(ASSIGNMENT_SECURITY = 4), TRUE), new /datum/event_meta(EVENT_LEVEL_MAJOR, "Slaughter Demon", /datum/event/spawn_slaughter, 10, is_one_shot = TRUE), + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Shadow Demon", /datum/event/spawn_slaughter/shadow, 50, is_one_shot = TRUE) //new /datum/event_meta(EVENT_LEVEL_MAJOR, "Floor Cluwne", /datum/event/spawn_floor_cluwne, 15, is_one_shot = TRUE) ) diff --git a/code/modules/events/slaughterevent.dm b/code/modules/events/slaughterevent.dm index 018cfe636c9..30332a91e39 100644 --- a/code/modules/events/slaughterevent.dm +++ b/code/modules/events/slaughterevent.dm @@ -1,9 +1,9 @@ /datum/event/spawn_slaughter var/key_of_slaughter - var/demon = /mob/living/simple_animal/slaughter/lesser + var/mob/living/simple_animal/demon/demon = /mob/living/simple_animal/demon/slaughter/lesser /datum/event/spawn_slaughter/proc/get_slaughter() - var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, source = /mob/living/simple_animal/slaughter) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [initial(demon.name)]?", ROLE_DEMON, TRUE, source = demon) if(!length(candidates)) kill() return @@ -17,25 +17,41 @@ var/datum/mind/player_mind = new /datum/mind(key_of_slaughter) player_mind.active = TRUE - var/list/spawn_locs = list() + var/turf/spawn_loc = get_spawn_loc(player_mind.current) + var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(spawn_loc) + var/mob/living/simple_animal/demon/S = new demon(holder) + player_mind.transfer_to(S) + player_mind.assigned_role = "Demon" + player_mind.special_role = SPECIAL_ROLE_DEMON + message_admins("[key_name_admin(S)] has been made into a [S.name] by an event.") + log_game("[key_name_admin(S)] was spawned as a [S.name] by an event.") + +/datum/event/spawn_slaughter/proc/get_spawn_loc(mob/player) + RETURN_TYPE(/turf) + var/list/spawn_centers = list() for(var/obj/effect/landmark/spawner/rev/L in GLOB.landmarks_list) - spawn_locs += get_turf(L) - if(!spawn_locs) //If we can't find a good place, just spawn the revenant at the player's location - spawn_locs += get_turf(player_mind.current) - if(!spawn_locs) //If we can't find THAT, then give up + spawn_centers += get_turf(L) + if(!spawn_centers) //If we can't find a good place, just spawn the revenant at the player's location + spawn_centers += get_turf(player) + if(!spawn_centers) //If we can't find THAT, then give up kill() return - var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(pick(spawn_locs)) - var/mob/living/simple_animal/slaughter/S = new demon(holder) - S.holder = holder - player_mind.transfer_to(S) - player_mind.assigned_role = "Slaughter Demon" - player_mind.special_role = SPECIAL_ROLE_SLAUGHTER_DEMON - message_admins("[key_name_admin(S)] has been made into a Slaughter Demon by an event.") - log_game("[key_name_admin(S)] was spawned as a Slaughter Demon by an event.") + return pick(spawn_centers) + /datum/event/spawn_slaughter/start() INVOKE_ASYNC(src, PROC_REF(get_slaughter)) /datum/event/spawn_slaughter/greater - demon = /mob/living/simple_animal/slaughter + demon = /mob/living/simple_animal/demon/slaughter + +/datum/event/spawn_slaughter/shadow + demon = /mob/living/simple_animal/demon/shadow + +/datum/event/spawn_slaughter/shadow/get_spawn_loc() + var/turf/spawn_center = ..() + for(var/turf/T in range(50, spawn_center)) + if(T.get_lumcount()) // if the turf is not pitch black + continue + return T // return the first turf that is dark nearby. + kill() diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index 8582dba71de..6bd4cbc4b7c 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -115,7 +115,7 @@ if(old_stat != stat) update_icon(UPDATE_OVERLAYS) -/obj/machinery/smartfridge/extinguish_light() +/obj/machinery/smartfridge/extinguish_light(force = FALSE) set_light(0) underlays.Cut() diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index b3baeb01333..df6604dedb2 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -206,3 +206,9 @@ add_attack_logs(user, target, "[what_done] ([reagent_str] | [genes_str])") +/obj/item/reagent_containers/food/snacks/grown/extinguish_light(force = FALSE) + if(!force) + return + if(seed.get_gene(/datum/plant_gene/trait/glow/shadow)) + return + set_light(0) diff --git a/code/modules/hydroponics/growninedible.dm b/code/modules/hydroponics/growninedible.dm index e87af235d02..71063fde945 100644 --- a/code/modules/hydroponics/growninedible.dm +++ b/code/modules/hydroponics/growninedible.dm @@ -61,3 +61,10 @@ if(seed) for(var/datum/plant_gene/trait/T in seed.genes) T.on_throw_impact(src, hit_atom) + +/obj/item/grown/extinguish_light(force = FALSE) + if(!force) + return + if(seed.get_gene(/datum/plant_gene/trait/glow/shadow)) + return + set_light(0) diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index 3870fb5937e..d2d4c87eadd 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -54,7 +54,7 @@ else light = new/datum/light_source(src, .) -/atom/proc/extinguish_light() +/atom/proc/extinguish_light(force = FALSE) return // If we have opacity, make sure to tell (potentially) affected light sources. diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm index e328e3be8b8..2687876d69e 100644 --- a/code/modules/mob/dead/observer/orbit.dm +++ b/code/modules/mob/dead/observer/orbit.dm @@ -134,9 +134,9 @@ var/list/antag_serialized = serialized.Copy() antag_serialized["antag"] = "Xenomorph" antagonists += list(antag_serialized) - else if(isslaughterdemon(M)) + else if(isdemon(M)) var/list/antag_serialized = serialized.Copy() - antag_serialized["antag"] = "Slaughter Demon" + antag_serialized["antag"] = "Demon" antagonists += list(antag_serialized) else if(length(orbiters) >= 0.2 * length_of_ghosts) // If a bunch of people are orbiting an object, like the nuke disk. diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 6187846e42d..04fed84f41f 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1966,7 +1966,7 @@ Eyes need to have significantly high darksight to shine unless the mob has the X to_chat(src, "[pick(GLOB.boo_phrases)]") return TRUE -/mob/living/carbon/human/extinguish_light() +/mob/living/carbon/human/extinguish_light(force = FALSE) // Parent function handles stuff the human may be holding ..() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 47778fc9f91..c2ceab12a72 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1092,10 +1092,10 @@ /mob/living/proc/fakefire() return -/mob/living/extinguish_light() +/mob/living/extinguish_light(force = FALSE) for(var/atom/A in src) if(A.light_range > 0) - A.extinguish_light() + A.extinguish_light(force) /mob/living/vv_edit_var(var_name, var_value) switch(var_name) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index d46d69b08af..a7fbd9182f5 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -42,8 +42,6 @@ var/mob_biotypes = MOB_ORGANIC var/metabolism_efficiency = 1 //more or less efficiency to metabolize helpful/harmful reagents and regulate body temperature.. - var/holder = null //The holder for blood crawling - var/ventcrawler = 0 //0 No vent crawling, 1 vent crawling in the nude, 2 vent crawling always var/list/icon/pipes_shown = list() var/last_played_vent diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 5d1ea76cd4c..1edeb2a30f2 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -515,7 +515,7 @@ CRASH("pAI without card") loc = card -/mob/living/silicon/pai/extinguish_light() +/mob/living/silicon/pai/extinguish_light(force = FALSE) flashlight_on = FALSE set_light(0) card.set_light(0) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 97e03355bfc..60cdacafe29 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1473,7 +1473,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( overlays += "[base_icon]-shield" -/mob/living/silicon/robot/extinguish_light() +/mob/living/silicon/robot/extinguish_light(force = FALSE) update_headlamp(1, 150) /mob/living/silicon/robot/rejuvenate() diff --git a/code/modules/pda/PDA.dm b/code/modules/pda/PDA.dm index d6800fdf19a..099f88acfec 100644 --- a/code/modules/pda/PDA.dm +++ b/code/modules/pda/PDA.dm @@ -390,7 +390,7 @@ GLOBAL_LIST_EMPTY(PDAs) if(current_app) current_app.program_process() -/obj/item/pda/extinguish_light() +/obj/item/pda/extinguish_light(force = FALSE) var/datum/data/pda/utility/flashlight/FL = find_program(/datum/data/pda/utility/flashlight) if(FL && FL.fon) FL.start() diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 8b33d819f3b..d12a46a07c1 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -941,7 +941,7 @@ limb.droplimb(0, DROPLIMB_BURN) return FIRELOSS -/obj/machinery/light/extinguish_light() +/obj/machinery/light/extinguish_light(force = FALSE) on = FALSE extinguished = TRUE emergency_mode = FALSE diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index f786178214c..1735648efbf 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -368,14 +368,11 @@ if(!gun_light) return - - var/mob/living/carbon/human/user = usr - if(!isturf(user.loc)) - to_chat(user, "You cannot turn the light on while in this [user.loc]!") gun_light.on = !gun_light.on - to_chat(user, "You toggle the gun light [gun_light.on ? "on":"off"].") - - playsound(user, 'sound/weapons/empty.ogg', 100, 1) + var/mob/living/carbon/human/user = usr + if(user) + to_chat(user, "You toggle the gun light [gun_light.on ? "on":"off"].") + playsound(src, 'sound/weapons/empty.ogg', 100, 1) update_gun_light(user) /obj/item/gun/proc/update_gun_light(mob/user = null) @@ -401,7 +398,7 @@ knife_overlay = null return TRUE -/obj/item/gun/extinguish_light() +/obj/item/gun/extinguish_light(force = FALSE) if(gun_light?.on) toggle_gunlight() visible_message("[src]'s light fades and turns off.") diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi index f3195929c8c..24940ee6a83 100644 Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi index ef27b7fc377..4a939231c99 100644 Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi index c485661b91f..9d8d4181dce 100644 Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index a042e446cf7..7df80247c19 100644 Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ diff --git a/paradise.dme b/paradise.dme index 03271c7c0ba..32e2adb15ce 100644 --- a/paradise.dme +++ b/paradise.dme @@ -617,6 +617,9 @@ #include "code\game\gamemodes\miniantags\abduction\machinery\dispenser.dm" #include "code\game\gamemodes\miniantags\abduction\machinery\experiment.dm" #include "code\game\gamemodes\miniantags\abduction\machinery\pad.dm" +#include "code\game\gamemodes\miniantags\demons\demon.dm" +#include "code\game\gamemodes\miniantags\demons\shadow_demon\shadow_demon.dm" +#include "code\game\gamemodes\miniantags\demons\slaughter demon\slaughter.dm" #include "code\game\gamemodes\miniantags\guardian\guardian.dm" #include "code\game\gamemodes\miniantags\guardian\host_actions.dm" #include "code\game\gamemodes\miniantags\guardian\types\assassin.dm" @@ -638,8 +641,6 @@ #include "code\game\gamemodes\miniantags\revenant\revenant.dm" #include "code\game\gamemodes\miniantags\revenant\revenant_abilities.dm" #include "code\game\gamemodes\miniantags\revenant\revenant_spawn_event.dm" -#include "code\game\gamemodes\miniantags\slaughter\bloodcrawl.dm" -#include "code\game\gamemodes\miniantags\slaughter\slaughter.dm" #include "code\game\gamemodes\nuclear\nuclear.dm" #include "code\game\gamemodes\nuclear\nuclear_challenge.dm" #include "code\game\gamemodes\nuclear\nuclearbomb.dm"