diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 498e9c70a2d..5cc0070658c 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -656,6 +656,23 @@ if(dreamer.mind?.has_antag_datum(/datum/antagonist/vampire)) var/mob/living/carbon/human/V = owner + if(istype(V.loc, /obj/structure/closet/coffin/vampire)) + var/obj/structure/closet/coffin/vampire/C = V.loc + if(C.vampire == V) + V.adjustBruteLoss(-3) + V.adjustFireLoss(-3) + V.adjustToxLoss(-3) + V.adjustOxyLoss(-3) + V.adjustCloneLoss(-1.5) + if(!isnull(V.viruses) && prob(25)) + for(var/datum/disease/virus in V.viruses) + virus.cure() + for(var/obj/item/organ/external/E in V.bodyparts) + if(E.status & (ORGAN_INT_BLEEDING | ORGAN_BROKEN | ORGAN_SPLINTED | ORGAN_BURNT)) + E.rejuvenate() + break // just one limb per sleep tick because vampires can just Rejuvenate out of sleep + for(var/obj/item/organ/internal/I in V.internal_organs) + I.heal_internal_damage(2, TRUE) if(istype(V.loc, /obj/structure/closet/coffin)) V.adjustBruteLoss(-1) V.adjustFireLoss(-1) diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 4386166e1ef..39253590e53 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1196,6 +1196,20 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) /datum/objective/download/check_completion() return TRUE +/datum/objective/lair + name = "Build a lair" + explanation_text = "Build a lair by placing a coffin in the middle of an unoccupied 3x3 area. This requires at least 150 total units of blood." + needs_target = FALSE + +/datum/objective/lair/check_completion() + if(..()) + return TRUE + for(var/datum/mind/M in get_owners()) + var/datum/antagonist/vampire/V = M.has_antag_datum(/datum/antagonist/vampire) + if(V.has_lair) + return TRUE + return FALSE + // Traders // These objectives have no check_completion, they exist only to tell Sol Traders what to aim for. diff --git a/code/game/objects/structures/crates_lockers/closets/coffin.dm b/code/game/objects/structures/crates_lockers/closets/coffin.dm index 2f132f3e2d0..86195d140f0 100644 --- a/code/game/objects/structures/crates_lockers/closets/coffin.dm +++ b/code/game/objects/structures/crates_lockers/closets/coffin.dm @@ -17,3 +17,71 @@ open_sound = 'sound/effects/stonedoor_openclose.ogg' close_sound = 'sound/effects/stonedoor_openclose.ogg' material_drop = /obj/item/stack/sheet/mineral/sandstone + +/obj/structure/closet/coffin/vampire + max_integrity = 500 + anchored = TRUE + armor = list(MELEE = 200, BULLET = 200, LASER = 80, ENERGY = 200, BOMB = 200, RAD = 200, FIRE = 80, ACID = 200) // just burn it + custom_fire_overlay = " " // gets rid of regular SSfires overlay, setting it on fire uses something completely different + /// Owner of the coffin + var/mob/vampire + /// Is the coffin being set on fire? + var/igniting = FALSE + /// Last time the vampire was warned of the attack + COOLDOWN_DECLARE(fire_act_cooldown) + +/obj/structure/closet/coffin/vampire/Initialize(mapload, mob/user) + . = ..() + name = "\proper the coffin of [user]" + desc += "
This coffin's owner may not actually have been dear to anyone, or even departed quite yet.
\ + It appears impervious to everything but lasers and fire! Especially fire!" + vampire = user + +/obj/structure/closet/coffin/vampire/welder_act(mob/user, obj/item/I) + if(igniting) + return ITEM_INTERACT_COMPLETE + if(!I.tool_use_check(user, 30)) // it's a cursed coffin, you will need something better than a maintenance welder to ignite it + return ITEM_INTERACT_COMPLETE + igniting = TRUE + to_chat(user, "You attempt to set [src] on fire with [I].") + to_chat(vampire, "Your lair is being attacked!") + if(do_after(user, 15 SECONDS, target = src)) + fire_act() + igniting = FALSE + return ITEM_INTERACT_COMPLETE + +/obj/structure/closet/coffin/vampire/bullet_act(obj/item/projectile/P) + if(!P.immolate) + return ..() + fire_act() + return ..() + +/obj/structure/closet/coffin/vampire/fire_act() + . = ..() + if(!COOLDOWN_FINISHED(src, fire_act_cooldown)) + return + to_chat(vampire, "Your lair is being attacked!") + switch(rand(1, 4)) + if(1) + visible_message("The wood howls as fire bursts out from seemingly nowhere!") + playsound(src, "sound/goonstation/voice/howl.ogg", 30) + if(2 to 3) + visible_message("The wood hisses as fire bursts out from seemingly nowhere!") + if(prob(50)) + playsound(src, "sound/effects/unathihiss.ogg", 30) + else + playsound(src, "sound/effects/tajaranhiss.ogg", 30) + if(4) + visible_message("The wood growls as fire bursts out from seemingly nowhere!") + playsound(src, 'sound/goonstation/voice/growl3.ogg', 30) + var/turf/new_fire = pick(oview(2, src)) + new /obj/effect/fire(get_turf(new_fire), T20C, 30 SECONDS, 1) + new /obj/effect/fire(loc, T20C, 30 SECONDS, 1) + COOLDOWN_START(src, fire_act_cooldown, 10 SECONDS) + +/obj/structure/closet/coffin/vampire/burn() + playsound(src, 'sound/hallucinations/wail.ogg', 20, extrarange = SOUND_RANGE_SET(5)) + visible_message("Fire bursts out from [name] as it falls apart!") + for(var/turf/T in range(1, src)) + new /obj/effect/fire(T, T20C, 30 SECONDS, 1) + ..() diff --git a/code/modules/antagonists/vampire/vamp_datum.dm b/code/modules/antagonists/vampire/vamp_datum.dm index ff54031d582..5adbe339dcf 100644 --- a/code/modules/antagonists/vampire/vamp_datum.dm +++ b/code/modules/antagonists/vampire/vamp_datum.dm @@ -25,11 +25,14 @@ RESTRICT_TYPE(/datum/antagonist/vampire) /datum/vampire_passive/vision = 100, /datum/spell/vampire/self/specialize = 150, /datum/spell/vampire/self/exfiltrate = 150, + /datum/spell/vampire/lair = 150, /datum/vampire_passive/regen = 200, /datum/vampire_passive/vision/advanced = 500) /// list of the peoples UIDs that we have drained, and how much blood from each one var/list/drained_humans = list() + /// did the vampire build a lair? + var/has_lair = FALSE blurb_text_color = COLOR_RED blurb_r = 255 blurb_g = 221 @@ -363,6 +366,7 @@ RESTRICT_TYPE(/datum/antagonist/vampire) add_antag_objective(/datum/objective/protect) else add_antag_objective(/datum/objective/steal) + add_antag_objective(/datum/objective/lair) if(prob(20)) // 20% chance of getting survive. 80% chance of getting escape. add_antag_objective(/datum/objective/survive) diff --git a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm index 0852be171d5..b584ccf6649 100644 --- a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm @@ -206,6 +206,65 @@ C.charge_duration = 2 SECONDS return C +/datum/spell/vampire/lair + name = "Lair" + desc = "Pick a coffin for yourself, the centrepiece of your new lair." + gain_desc = "You can now start a lair." + action_icon = 'icons/obj/closet.dmi' + action_icon_state = "coffin" + base_cooldown = 2 SECONDS + +/datum/spell/vampire/lair/create_new_targeting() + var/datum/spell_targeting/click/T = new + T.range = 1 + T.allowed_type = /obj/structure/closet/coffin + return T + +/datum/spell/vampire/lair/cast(list/targets, mob/user) + var/obj/structure/closet/coffin/C = targets[1] // this spell will basically always target a singular coffin unless you stack multiple on the same tile + if(!istype(C, /obj/structure/closet/coffin)) + to_chat(user, "This only works on coffins!") + return + if(istype(C, /obj/structure/closet/coffin/vampire)) + to_chat(user, "This coffin serves another and refuses to bend to your will!") + return + if(istype(C, /obj/structure/closet/coffin/sarcophagus)) + to_chat(user, "Making such a lavish lair would likely upset an ancient. You should really use a wooden coffin for now.") + return + for(var/turf/T in range(1, C)) + if(T.density) + to_chat(user, "You need more space around the coffin for the ritual!") + return + to_chat(user, "You begin marking the coffin!") + C.Beam(user, icon_state = "drainbeam", maxdistance = 1, time = 10 SECONDS) + playsound(C, 'sound/misc/enter_blood.ogg', 20) + for(var/obj/machinery/light/L in range(5, user)) + L.forced_flicker() + var/obj/effect/lair_rune/rune = new /obj/effect/lair_rune(get_turf(C), user) + if(!do_after(user, 10 SECONDS, target = C)) + qdel(rune) + return + playsound(user, 'sound/hallucinations/im_here1.ogg', 30) + new /obj/structure/closet/coffin/vampire(get_turf(C), user) + qdel(C) + var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) + V.has_lair = TRUE + user.mind.RemoveSpell(src) + +/obj/effect/lair_rune + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + plane = FLOOR_PLANE + layer = SIGIL_LAYER + icon = 'icons/effects/96x96.dmi' + icon_state = "vampiric_rune" + pixel_x = -34 + pixel_y = -38 + +/obj/effect/lair_rune/Initialize(mapload, mob/user) + . = ..() + if(user) + color = user.dna.species.blood_color + /// No deviation at all. Flashed from the front or front-left/front-right. Alternatively, flashed in direct view. #define DEVIATION_NONE 3 /// Partial deviation. Flashed from the side. Alternatively, flashed out the corner of your eyes. diff --git a/icons/effects/96x96.dmi b/icons/effects/96x96.dmi index b2f55cbbbee..d4f3711d6fa 100644 Binary files a/icons/effects/96x96.dmi and b/icons/effects/96x96.dmi differ