diff --git a/code/game/gamemodes/antag_spawner.dm b/code/game/gamemodes/antag_spawner.dm index 549243bf86c..a621168182a 100644 --- a/code/game/gamemodes/antag_spawner.dm +++ b/code/game/gamemodes/antag_spawner.dm @@ -146,4 +146,52 @@ ticker.mode.syndicates += R.mind ticker.mode.update_synd_icons_added(R.mind) R.mind.special_role = "syndicate" - R.faction = list("syndicate") \ No newline at end of file + R.faction = list("syndicate") + + +/obj/item/weapon/antag_spawner/slaughter_demon //Warning edgiest item in the game + name = "vial of blood" + desc = "A magically infused bottle of blood, distilled from countless murder victims. Used in unholy rituals to attract horrifying creatures." + icon = 'icons/obj/wizard.dmi' + icon_state = "vial" + + +/obj/item/weapon/antag_spawner/slaughter_demon/attack_self(mob/user as mob) + var/list/demon_candidates = get_candidates(BE_ALIEN) + if(user.z != 1) + user << "You should probably wait until you reach the station." + return + if(demon_candidates.len > 0) + used = 1 + var/client/C = pick(demon_candidates) + spawn_antag(C, get_turf(src.loc), "Slaughter Demon") + user << "You shatter the bottle, no turning back now!" + user << "You sense a dark presence lurking just beyond the veil..." + playsound(user.loc, 'sound/effects/Glassbr1.ogg', 100, 1) + qdel(src) + else + user << "You can't seem to work up the nerve to shatter the bottle. Perhaps you should try again later." + + +/obj/item/weapon/antag_spawner/slaughter_demon/spawn_antag(var/client/C, var/turf/T, var/type = "") + + var /obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(T) + var/mob/living/simple_animal/slaughter/S = new /mob/living/simple_animal/slaughter/(holder) + S.phased = TRUE + S.key = C.key + S.mind.assigned_role = "Slaughter Demon" + S.mind.special_role = "Slaughter Demon" + ticker.mode.traitors += S.mind + var/datum/objective/assassinate/new_objective = new /datum/objective/assassinate + new_objective.owner = S:mind + new_objective:target = usr:mind + new_objective.explanation_text = "Kill [usr.real_name], the one who summoned you." + S.mind.objectives += new_objective + var/datum/objective/new_objective2 = new /datum/objective + new_objective2.owner = S:mind + new_objective2.explanation_text = "Kill everyone else while you're at it." + S.mind.objectives += new_objective2 + S << S.playstyle_string + S << "You are currently not currently in the same plane of existence as the station. Ctrl+Click a blood pool to manifest." + S << "Objective #[1]: [new_objective.explanation_text]" + S << "Objective #[2]: [new_objective2.explanation_text]" \ No newline at end of file diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index da2e49f4af7..2c9ea69ef97 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -246,7 +246,7 @@ /datum/spellbook_entry/item/scryingorb name = "Scrying Orb" desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to spy upon the station with ease. In addition, buying it will permanently grant you x-ray vision." - item_path = /obj/item/weapon/gun/magic/staff/chaos + item_path = /obj/item/weapon/scrying log_name = "SO" /datum/spellbook_entry/item/scryingorb/Buy(var/mob/living/carbon/human/user,var/obj/item/weapon/spellbook/book) @@ -297,6 +297,12 @@ item_path = /obj/item/weapon/antag_spawner/contract log_name = "CT" +/datum/spellbook_entry/item/bloodbottle + name = "Bottle of Blood" + desc = "A bottle of magically infused blood, the smell of which will attract extradimensional beings when broken. Be careful though, the kinds of creatures summoned by blood magic are indiscriminate in their killing, and you yourself may become a victim." + item_path = /obj/item/weapon/antag_spawner/slaughter_demon + log_name = "BB" + /datum/spellbook_entry/summon name = "Summon Stuff" category = "Rituals" @@ -421,6 +427,10 @@ user << "You feed the contract back into the spellbook, refunding your points." src.uses++ qdel(O) + if(istype(O, /obj/item/weapon/antag_spawner/slaughter_demon)) + user << "On second thought, maybe summoning a demon is a bad idea. You refund your points." + src.uses++ + qdel(O) /obj/item/weapon/spellbook/proc/GetCategoryHeader(var/category) var/dat = "" @@ -482,7 +492,7 @@ dat += "
  • Uses remaining : [uses]
  • " dat += "" - + var/datum/spellbook_entry/E for(var/i=1,i<=entries.len,i++) var/spell_info = "" diff --git a/code/modules/mob/living/simple_animal/slaughter/slaughter.dm b/code/modules/mob/living/simple_animal/slaughter/slaughter.dm new file mode 100644 index 00000000000..267af8c2dd7 --- /dev/null +++ b/code/modules/mob/living/simple_animal/slaughter/slaughter.dm @@ -0,0 +1,173 @@ +//////////////////The Monster + +/mob/living/simple_animal/slaughter + name = "Slaughter Demon" + real_name = "Slaughter Demon" + desc = "You should run." + 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" + speed = 0 + a_intent = "harm" + stop_automated_movement = 1 + status_flags = CANPUSH + attack_sound = 'sound/magic/demon_attack1.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 + faction = list("slaughter") + attacktext = "wildly tears into" + maxHealth = 250 + health = 250 + environment_smash = 1 + melee_damage_lower = 30 + melee_damage_upper = 30 + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_MINIMUM + var/devoured = 0 + var/phased = FALSE + var/holder = null + var/eating = FALSE + var/mob/living/kidnapped = null + var/playstyle_string = "You are the Slaughter Demon, a terible creature from another existence. You have a single desire: To kill. \ + You may Ctrl+Click on blood pools to travel through them, appearing and dissaapearing 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. " + + +/mob/living/simple_animal/slaughter/death() + ..(1) + new /obj/effect/decal/cleanable/blood (src.loc) + new /obj/item/weapon/demonheart (src.loc) + playsound(get_turf(src),'sound/magic/demon_dies.ogg', 200, 1) + visible_message("The [src] screams in anger as its form collapes into a pool of viscera.") + ghostize() + qdel(src) + return + + + +////////////////////The Powers + +/mob/living/simple_animal/slaughter/proc/phaseout(var/obj/effect/decal/cleanable/B) + var/turf/mobloc = get_turf(src.loc) + var/turf/bloodloc = get_turf(B.loc) + if(Adjacent(bloodloc)) + src.notransform = TRUE + spawn(0) + src.visible_message("The [src] sinks into the pool of blood.") + playsound(get_turf(src), 'sound/magic/enter_blood.ogg', 100, 1, -1) + var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter( mobloc ) + src.ExtinguishMob() + if(src.buckled) + src.buckled.unbuckle_mob() + if(src.pulling) + if(istype(src.pulling, /mob/living)) + var/mob/living/victim = src.pulling + if(victim.stat == CONSCIOUS) + src.visible_message("[victim] kicks free of the [src] at the last second!") + else + victim.loc = holder + src.visible_message("The [src] drags [victim] into the pool of blood!") + src.kidnapped = victim + src.loc = holder + src.phased = TRUE + src.holder = holder + if(src.kidnapped) + src << "You being to feast on [kidnapped]. You can not move while you are doing this." + src.eating = TRUE + playsound(get_turf(src),'sound/magic/Demon_consume.ogg', 100, 1) + sleep(30) + playsound(get_turf(src),'sound/magic/Demon_consume.ogg', 100, 1) + sleep(30) + playsound(get_turf(src),'sound/magic/Demon_consume.ogg', 100, 1) + sleep(30) + src << "You devour [kidnapped]. Your health is fully restored." + src.adjustBruteLoss(-1000) + kidnapped.ghostize() + qdel(kidnapped) + src.devoured++ + src.kidnapped = null + src.eating = FALSE + src.notransform = 0 + +/mob/living/simple_animal/slaughter/proc/phasein(var/obj/effect/decal/cleanable/B) + if(src.eating) + src << "Finish eating first!" + else + src.loc = B.loc + src.phased = FALSE + src.client.eye = src + src.visible_message("The [src] rises out of the pool of blood!") + playsound(get_turf(src), 'sound/magic/exit_blood.ogg', 100, 1, -1) + qdel(src.holder) + +/obj/effect/decal/cleanable/blood/CtrlClick(var/mob/user) + ..() + if(istype(user, /mob/living/simple_animal/slaughter)) + var/mob/living/simple_animal/slaughter/S = user + if(S.phased) + S.phasein(src) + else + S.phaseout(src) + + +/obj/effect/decal/cleanable/trail_holder/CtrlClick(var/mob/user) + ..() + if(istype(user, /mob/living/simple_animal/slaughter)) + var/mob/living/simple_animal/slaughter/S = user + if(S.phased) + S.phasein(src) + else + S.phaseout(src) + + + +/turf/CtrlClick(var/mob/user) + ..() + if(istype(user, /mob/living/simple_animal/slaughter)) + var/mob/living/simple_animal/slaughter/S = user + for(var/obj/effect/decal/cleanable/B in src.contents) + if(istype(B, /obj/effect/decal/cleanable/blood) || istype(B, /obj/effect/decal/cleanable/trail_holder)) + if(S.phased) + S.phasein(B) + break + else + S.phaseout(B) + break + +/obj/effect/dummy/slaughter //Can't use the wizard one, blocked by jaunt/slow + name = "water" + icon = 'icons/effects/effects.dmi' + icon_state = "nothing" + var/canmove = 1 + density = 0 + anchored = 1 + +obj/effect/dummy/slaughter/relaymove(var/mob/user, direction) + if (!src.canmove || !direction) return + var/turf/newLoc = get_step(src,direction) + loc = newLoc + src.canmove = 0 + spawn(1) + src.canmove = 1 + +/obj/effect/dummy/slaughter/ex_act(blah) + return +/obj/effect/dummy/slaughter/bullet_act(blah) + return + +/obj/effect/dummy/slaughter/singularity_act(blah) + return + + +//////////The Loot + +/obj/item/weapon/demonheart + name = "demon's heart" + desc = "It's still faintly beating with rage" + icon = 'icons/obj/surgery.dmi' + icon_state = "heart-on" + origin_tech = "combat=5;biotech=8" \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/slaughter/slaughterevent.dm b/code/modules/mob/living/simple_animal/slaughter/slaughterevent.dm new file mode 100644 index 00000000000..3db4b52bcfd --- /dev/null +++ b/code/modules/mob/living/simple_animal/slaughter/slaughterevent.dm @@ -0,0 +1,67 @@ + +/datum/round_event_control/slaughter + name = "Spawn Slaughter Demon" + typepath = /datum/round_event/slaughter + weight = 0 //Admin only + max_occurrences = 1 + earliest_start = 30 //Spawn later with more blood, hopefully + + + +/datum/round_event/slaughter + var/key_of_slaughter + + +/datum/round_event/slaughter/proc/get_slaughter(var/end_if_fail = 0) + key_of_slaughter = null + if(!key_of_slaughter) + var/list/candidates = get_candidates(BE_ALIEN) + if(!candidates.len) + if(end_if_fail) + return 0 + return find_slaughter() + var/client/C = pick(candidates) + key_of_slaughter = C.key + if(!key_of_slaughter) + if(end_if_fail) + return 0 + return find_slaughter() + var/datum/mind/player_mind = new /datum/mind(key_of_slaughter) + player_mind.active = 1 + var/list/spawn_locs = list() + for(var/obj/effect/landmark/L in landmarks_list) + if(isturf(L.loc)) + switch(L.name) + if("carpspawn") + spawn_locs += L.loc + if(!spawn_locs) + return find_slaughter() + var /obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(pick(spawn_locs)) + var/mob/living/simple_animal/slaughter/S = new /mob/living/simple_animal/slaughter/(holder) + S.phased = TRUE + player_mind.transfer_to(S) + player_mind.assigned_role = "Slaughter Demon" + player_mind.special_role = "Slaughter Demon" + ticker.mode.traitors |= player_mind + S << S.playstyle_string + S << "You are currently not currently in the same plane of existence as the station. Ctrl+Click a blood pool to manifest." + message_admins("[key_of_slaughter] has been made into a Slaughter Demon by an event.") + log_game("[key_of_slaughter] was spawned as a Slaughter Demon by an event.") + return 1 + + + +/datum/round_event/slaughter/start() + get_slaughter() + + + +/datum/round_event/slaughter/proc/find_slaughter() + message_admins("Attempted to spawn a Slaughter Demon but there was no players available. Will try again momentarily.") + spawn(50) + if(get_slaughter(1)) + message_admins("Situation has been resolved, [key_of_slaughter] has been spawned as a Slaughter Demon.") + log_game("[key_of_slaughter] was spawned as a Slaughter Demon by an event.") + return 0 + message_admins("Unfortunately, no candidates were available for becoming a Slaugter Demon. Shutting down.") + return kill() \ No newline at end of file diff --git a/html/changelogs/KorPhaeron-demon.yml b/html/changelogs/KorPhaeron-demon.yml new file mode 100644 index 00000000000..550bffea37a --- /dev/null +++ b/html/changelogs/KorPhaeron-demon.yml @@ -0,0 +1,37 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# spellcheck (typo fixes) +# experiment +# tgs (TG-ported fixes?) +################################# + +# Your name. +author: Kor + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "A terrible new creature stalks the halls of SS13." + - rscadd: "The wizard has a new artefact related to said creature." diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi index 719099312d0..3bbd054bce7 100644 Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ diff --git a/icons/obj/wizard.dmi b/icons/obj/wizard.dmi index e3eec0a88ac..3624a9b4bff 100644 Binary files a/icons/obj/wizard.dmi and b/icons/obj/wizard.dmi differ diff --git a/sound/magic/Demon_consume.ogg b/sound/magic/Demon_consume.ogg new file mode 100644 index 00000000000..ef9276e74c0 Binary files /dev/null and b/sound/magic/Demon_consume.ogg differ diff --git a/sound/magic/demon_attack1.ogg b/sound/magic/demon_attack1.ogg new file mode 100644 index 00000000000..e3c765afd0c Binary files /dev/null and b/sound/magic/demon_attack1.ogg differ diff --git a/sound/magic/demon_dies.ogg b/sound/magic/demon_dies.ogg new file mode 100644 index 00000000000..ad2a95f397d Binary files /dev/null and b/sound/magic/demon_dies.ogg differ diff --git a/sound/magic/enter_blood.ogg b/sound/magic/enter_blood.ogg new file mode 100644 index 00000000000..4deba11b465 Binary files /dev/null and b/sound/magic/enter_blood.ogg differ diff --git a/sound/magic/exit_blood.ogg b/sound/magic/exit_blood.ogg new file mode 100644 index 00000000000..c4399a2ecc6 Binary files /dev/null and b/sound/magic/exit_blood.ogg differ diff --git a/tgstation.dme b/tgstation.dme index bf38a2898bb..6f47e668fda 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1242,6 +1242,8 @@ #include "code\modules\mob\living\simple_animal\revenant\revenant.dm" #include "code\modules\mob\living\simple_animal\revenant\revenant_abilities.dm" #include "code\modules\mob\living\simple_animal\revenant\revenant_spawn_event.dm" +#include "code\modules\mob\living\simple_animal\slaughter\slaughter.dm" +#include "code\modules\mob\living\simple_animal\slaughter\slaughterevent.dm" #include "code\modules\mob\living\simple_animal\slime\death.dm" #include "code\modules\mob\living\simple_animal\slime\emote.dm" #include "code\modules\mob\living\simple_animal\slime\life.dm"