diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 199204e75208..d5bf78436e26 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -202,9 +202,6 @@ /// Checks if the given mob is a changeling #define IS_CHANGELING(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/changeling)) -/// Checks if the given mob is a vampire -#define IS_VAMPIRE(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/vampire)) - // Antag resource defines #define ANTAG_RESOURCE_DARKSPAWN "psi" -#define ANTAG_RESOURCE_VAMPIRE "blood" +#define ANTAG_RESOURCE_BLOODSUCKER "blood" diff --git a/code/__DEFINES/bloodsuckers.dm b/code/__DEFINES/bloodsuckers.dm index aea15aeadb05..5dbc61e521e7 100644 --- a/code/__DEFINES/bloodsuckers.dm +++ b/code/__DEFINES/bloodsuckers.dm @@ -18,9 +18,7 @@ #define BLOODSUCKER_DRINK_NORMAL "bloodsucker_drink_normal" ///Drinks blood but is snobby, refusing to drink from mindless #define BLOODSUCKER_DRINK_SNOBBY "bloodsucker_drink_snobby" -///Drinks blood from disgusting creatures without Humanity consequences. -#define BLOODSUCKER_DRINK_INHUMANELY "bloodsucker_drink_imhumanely" -///Drinks blood only from aggressive or higher grabs, no silent feeding. +///Drinks blood only from grabs, no silent feeding. #define BLOODSUCKER_DRINK_PAINFUL "bloodsucker_drink_painful" ///Controls blood, just like all Bloodsuckers do @@ -32,8 +30,6 @@ ///Controls the inner machinations of one's being, being able to use the vassalrack and sculpt flesh monsters #define BLOODSUCKER_CONTROL_FLESH 8 -#define BLOODSUCKER_RANK_UP_NORMAL "bloodsucker_rank_up_normal" - #define DANGER_LEVEL_FIRST_WARNING 1 #define DANGER_LEVEL_SECOND_WARNING 2 #define DANGER_LEVEL_THIRD_WARNING 3 @@ -61,7 +57,7 @@ /** * Clan defines */ -#define CLAN_NONE "Caitiff" +#define CLAN_CAITIFF "Caitiff" #define CLAN_BRUJAH "Brujah Clan" #define CLAN_NOSFERATU "Nosferatu Clan" #define CLAN_TREMERE "Tremere Clan" @@ -72,6 +68,7 @@ #define CLAN_LASOMBRA "Lasombra Clan" #define CLAN_TZIMISCE "Tzimisce Clan" #define CLAN_HECATA "Hecata Clan" +#define CLAN_TEPES "Tepes Clan" #define TREMERE_VASSAL "tremere_vassal" #define FAVORITE_VASSAL "favorite_vassal" @@ -81,7 +78,6 @@ #define ARMMY_MONSTER "Armmy (100 Blood)" #define CALCIUM_MONSTER "Calcium (150 Blood)" #define HUSK_MONSTER "Husk" -#define TOREADOR_MAX_HUMANITY_LOSS 10 /** * Power defines @@ -120,7 +116,7 @@ #define BP_AM_SINGLEUSE (1<<1) /// This Power has a Static cooldown #define BP_AM_STATIC_COOLDOWN (1<<2) -/// This Power doesn't cost bloot to run while unconscious +/// This Power doesn't cost blood to run while unconscious #define BP_AM_COSTLESS_UNCONSCIOUS (1<<3) /** @@ -136,11 +132,12 @@ ///Called when a new Vassal is successfully made: (datum/bloodsucker_datum) #define BLOODSUCKER_MADE_VASSAL "bloodsucker_made_vassal" -///Called when a Bloodsucker exits Torpor. -#define BLOODSUCKER_EXIT_TORPOR "bloodsucker_exit_torpor" ///Called when a Bloodsucker reaches Final Death. #define BLOODSUCKER_FINAL_DEATH "bloodsucker_final_death" +///Called when a Bloodsucker reaches low enough blood to trigger frenzy +#define BLOODSUCKER_TRIGGER_FRENZY "bloodsucker_trigger_frenzy" + #define COMSIG_BLOODSUCKER_BROKE_MASQUERADE "comsig_bloodsucker_broke_masquerade" ///Whether the Bloodsucker should not be dusted when arriving Final Death diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index f684c3abaace..bf924e0c4450 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -37,7 +37,6 @@ #define ROLE_LAVALAND "Lavaland" #define ROLE_INTERNAL_AFFAIRS "Internal Affairs Agent" #define ROLE_FUGITIVE "Fugitive" -#define ROLE_VAMPIRE "Vampire" // Yogs #define ROLE_DARKSPAWN "Darkspawn" // Yogs #define ROLE_HOLOPARASITE "Holoparasite" // Yogs #define ROLE_HORROR "Eldritch Horror" // Yogs @@ -83,7 +82,6 @@ GLOBAL_LIST_INIT(special_roles, list( ROLE_SERVANT_OF_RATVAR = /datum/antagonist/clockcult, //Roundstart or Midround - ROLE_VAMPIRE = /datum/antagonist/vampire, // Yogs ROLE_BLOODSUCKER = /datum/antagonist/bloodsucker, ROLE_TRAITOR = /datum/antagonist/traitor, ROLE_CHANGELING = /datum/antagonist/changeling, diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 37087056663f..754e4a9279d0 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -194,8 +194,6 @@ #define STATUS_EFFECT_PROGENITORCURSE /datum/status_effect/progenitor_curse -#define STATUS_EFFECT_MASQUERADE /datum/status_effect/masquerade - #define STATUS_EFFECT_EXPLOSION_PRIME /datum/status_effect/explosion_prime ///////////// diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 21d8168e317f..41db886a0bd2 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -41,7 +41,6 @@ #define TRAIT_BLOODY_MESS_LITE "bloody_mess_lite" //weak heparin, otherwise the same #define TRAIT_NO_BLOOD_REGEN "no_blood_regen" //prevents regenerating blood #define TRAIT_NOPULSE "nopulse" // Your heart doesn't beat -#define TRAIT_MASQUERADE "masquerade" // Falsifies Health analyzer blood levels #define TRAIT_NOCLONE "noclone" // No cloning #define TRAIT_NODEFIB "nodefib" // No defibbing #define TRAIT_COLDBLOODED "coldblooded" // Your body is literal room temperature. Does not make you immune to the temp diff --git a/code/__DEFINES/{yogs_defines}/antagonists.dm b/code/__DEFINES/{yogs_defines}/antagonists.dm index 313f948a75f5..1190bba323a9 100644 --- a/code/__DEFINES/{yogs_defines}/antagonists.dm +++ b/code/__DEFINES/{yogs_defines}/antagonists.dm @@ -1,4 +1,3 @@ -#define ANTAG_DATUM_VAMPIRE /datum/antagonist/vampire #define ANTAG_DATUM_DARKSPAWN /datum/antagonist/darkspawn #define ANTAG_DATUM_THRALL /datum/antagonist/thrall #define ANTAG_DATUM_INFILTRATOR /datum/antagonist/infiltrator diff --git a/code/datums/components/mood.dm b/code/datums/components/mood.dm index 588d0d85b154..667c88285a94 100644 --- a/code/datums/components/mood.dm +++ b/code/datums/components/mood.dm @@ -136,14 +136,9 @@ highest_absolute_mood = absmood if(!conflicting_moodies.len && owner.mind) //no special icons- go to the normal icon states - var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(/datum/antagonist/bloodsucker) //bloodsucker edit if(sanity < 25) screen_obj.icon_state = "mood_insane" - if(IS_BLOODSUCKER(owner) && bloodsuckerdatum.my_clan?.get_clan() == CLAN_TOREADOR) - screen_obj.add_overlay("teeth_insane") else - if(IS_BLOODSUCKER(owner) && bloodsuckerdatum.my_clan?.get_clan() == CLAN_TOREADOR) - screen_obj.add_overlay("teeth[mood_level]") screen_obj.icon_state = "mood[mood_level]" screen_obj_sanity.icon_state = "sanity[sanity_level]" return diff --git a/code/datums/mutations/olfaction.dm b/code/datums/mutations/olfaction.dm deleted file mode 100644 index edbd26a803da..000000000000 --- a/code/datums/mutations/olfaction.dm +++ /dev/null @@ -1,184 +0,0 @@ -/datum/mutation/human/olfaction - name = "Transcendent Olfaction" - desc = "Your sense of smell is comparable to that of a canine." - quality = POSITIVE - difficulty = 12 - text_gain_indication = span_notice("Smells begin to make more sense...") - text_lose_indication = span_notice("Your sense of smell goes back to normal.") - instability = 30 - synchronizer_coeff = 1 - - var/datum/action/cooldown/bloodsucker/olfaction/acquire_scent/lesser/smelling - -/datum/mutation/human/olfaction/on_acquiring(mob/living/carbon/human/owner) - . = ..() - if(!smelling) - smelling = new() - - if(!owner.actions.Find(smelling)) - smelling.Grant(owner) - -/datum/mutation/human/olfaction/modify() - if(smelling) - smelling.sensitivity = GET_MUTATION_SYNCHRONIZER(src) - -/datum/mutation/human/olfaction/on_losing(mob/living/carbon/human/owner) - . = ..() - smelling = locate(smelling) in owner.actions - if(smelling) - smelling.Remove(owner) - -/obj/effect/temp_visual/scent_trail - name = "/improper scent trail" - icon = 'icons/effects/genetics.dmi' - var/img_name = "smelly" - anchored = TRUE - resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF - randomdir = FALSE - duration = 2 SECONDS - ///we cannot use icon_state bc we are invisible, this is the same thing but can be not visible - var/image_state = "scent_trail" - ///whomst doing the sniffing, we need this because the scent lines will only be visible to them - var/mob/living/sniffer - ///tracker image - var/image/img - - var/datum/atom_hud/alternate_appearance/basic/scent_hunter/smell_hud - - var/scent_color - -/obj/effect/temp_visual/scent_trail/Initialize(mapload, dir = SOUTH, mob/living/user, flip_icon, color) - . = ..() - if(user) - sniffer = user - RegisterSignal(src, COMSIG_MOVABLE_CROSSED, PROC_REF(Sniffed)) - if(!scent_color || color) - scent_color = sanitize_hexcolor(color, 6, TRUE, COLOR_RED) - - var/icon/trail = icon(icon, image_state, dir) - if(flip_icon) - trail.Flip(dir) - trail.Turn(180) - - img = image(trail, loc = src) - img.plane = HUD_PLANE - img.appearance_flags = NO_CLIENT_COLOR - img.alpha = 0 - img.color = sanitize_hexcolor(scent_color, 6, TRUE) - img.name = img_name - smell_hud = add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/scent_hunter, "smelly", img, FALSE) - - animate(img, alpha = 255, time = 0.5 SECONDS, easing = EASE_IN) //fade IN - -/obj/effect/temp_visual/scent_trail/proc/Sniffed(datum/source, atom/movable/AM) - //whoever is following the trail sniffs it so hard the trail is dissipated. this is for balance reasons so you can's sit on a trail for speed boost - if(sniffer && sniffer == AM && sniffer?.has_status_effect(STATUS_EFFECT_BLOOD_HUNTER)) - sniffer.apply_status_effect(STATUS_EFFECT_BLOODTHIRSTY) - Destroy() - -/obj/effect/temp_visual/scent_trail/Destroy() - UnregisterSignal(src, COMSIG_MOVABLE_CROSSED) - animate(img, alpha = 0, time = 1 SECONDS, easing = EASE_OUT) //fade out - INVOKE_ASYNC(src, PROC_REF(Fade)) - return ..() - -/obj/effect/temp_visual/scent_trail/proc/Fade() - sleep(1 SECONDS) - sniffer.remove_alt_appearance("smelly") - img = null - -/datum/effect_system/trail_follow/scent - effect_type = /obj/effect/temp_visual/scent_trail - qdel_in_time = 20 - fade = FALSE - nograv_required = FALSE - var/trail_color - ///the person who made the trail exist, not the atom it's attatched to - var/mob/living/sniffer - - var/turf/trail_step_prev - var/turf/trail_step_current - - ///It's a surprise tool that will help us later - var/turf/trail_step_prev_prev - var/flip = FALSE - var/turf/vert_test - var/turf/horiz_test - -/datum/effect_system/trail_follow/scent/set_up(atom/atom, mob/living/user, color = COLOR_RED) - . = ..() - trail_color = sanitize_hexcolor(color, 6, TRUE, COLOR_RED) - sniffer = user - -/datum/effect_system/trail_follow/scent/generate_effect() - if(!check_conditions()) - return stop() - if(oldposition && !(oldposition == get_turf(holder))) - if(!oldposition.has_gravity() || !nograv_required) - if(trail_step_prev) - trail_step_prev_prev = trail_step_prev - trail_step_prev = oldposition - trail_step_current = get_turf(holder) - var/trail_dir = get_dir(trail_step_prev, trail_step_current) - flip = FALSE - vert_test = null - horiz_test = null - switch(trail_dir) - if(SOUTHEAST) - vert_test = get_step(trail_step_prev, SOUTH) - horiz_test = get_step(trail_step_prev, EAST) - if(!vert_test.density) - new effect_type(vert_test, trail_dir, sniffer, flip, trail_color) - trail_step_prev = vert_test - else if(!horiz_test.density) - new effect_type(horiz_test, trail_dir, sniffer, flip, trail_color) - trail_step_prev = horiz_test - - flip = TRUE - trail_dir = get_dir(trail_step_prev_prev, trail_step_prev) - if(SOUTHWEST) - vert_test = get_step(trail_step_prev, SOUTH) - horiz_test = get_step(trail_step_prev, WEST) - if(!vert_test.density) - new effect_type(vert_test, trail_dir, sniffer, flip, trail_color) - trail_step_prev = vert_test - else if(!horiz_test.density) - new effect_type(horiz_test, trail_dir, sniffer, flip, trail_color) - trail_step_prev = horiz_test - flip = TRUE - trail_dir = get_dir(trail_step_prev_prev, trail_step_prev) - if(NORTHEAST) - vert_test = get_step(trail_step_prev, NORTH) - horiz_test = get_step(trail_step_prev, EAST) - if(!vert_test.density) - new effect_type(vert_test, trail_dir, sniffer, flip, trail_color) - trail_step_prev = vert_test - else if(!horiz_test.density) - new effect_type(horiz_test, trail_dir, sniffer, flip, trail_color) - trail_step_prev = horiz_test - flip = TRUE - trail_dir = get_dir(trail_step_prev_prev, trail_step_prev) - if(NORTHWEST) - vert_test = get_step(trail_step_prev, NORTH) - horiz_test = get_step(trail_step_prev, WEST) - if(!vert_test.density) - new effect_type(vert_test, trail_dir, sniffer, flip, trail_color) - trail_step_prev = vert_test - else if(!horiz_test.density) - new effect_type(horiz_test, trail_dir, sniffer, flip, trail_color) - trail_step_prev = horiz_test - flip = TRUE - trail_dir = get_dir(trail_step_prev_prev, trail_step_prev) - else - if(trail_step_prev_prev) - if(get_dir(trail_step_prev_prev, trail_step_prev) != get_dir(trail_step_prev, trail_step_current) && trail_step_prev_prev.Adjacent(trail_step_current)) - trail_dir = get_dir(trail_step_prev_prev, trail_step_current) - if(get_dir(trail_step_prev_prev, trail_step_prev) == EAST || get_dir(trail_step_prev_prev, trail_step_prev) == WEST && get_dir(trail_step_prev, trail_step_current) == NORTH || get_dir(trail_step_prev, trail_step_current) == SOUTH) - //this is neccessary because if you get the direction while travelling horizontally, the resulting directional sprite needs to be flipped - //travelling south and turning east gives you the correct south to east bend - //but travelling east and turning south gives you another south to east bend - //if(get_dir(trail_step, trail_step_next) == NORTH || get_dir(trail_step, trail_step_next) == SOUTH) - flip = TRUE - new effect_type(oldposition, trail_dir, sniffer, flip, trail_color) - - oldposition = get_turf(holder) diff --git a/code/game/alternate_appearance.dm b/code/game/alternate_appearance.dm index 5d0a50143254..fb2efa9f11fd 100644 --- a/code/game/alternate_appearance.dm +++ b/code/game/alternate_appearance.dm @@ -168,19 +168,19 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances) seer = M show_to(seer) -/datum/atom_hud/alternate_appearance/basic/scent_hunter - -/datum/atom_hud/alternate_appearance/basic/scent_hunter/New() - ..() - for(var/mob in GLOB.player_list) - if(mobShouldSee(mob)) - show_to(mob) - -/datum/atom_hud/alternate_appearance/basic/scent_hunter/mobShouldSee(mob/M) - if(isliving(M)) - var/mob/living/L = M - if(L.has_status_effect(STATUS_EFFECT_SCENT_HUNTER) || L.has_status_effect(STATUS_EFFECT_BLOOD_HUNTER)) - return TRUE - if(isobserver(M)) - return TRUE - return FALSE +// /datum/atom_hud/alternate_appearance/basic/scent_hunter + +// /datum/atom_hud/alternate_appearance/basic/scent_hunter/New() +// ..() +// for(var/mob in GLOB.player_list) +// if(mobShouldSee(mob)) +// show_to(mob) + +// /datum/atom_hud/alternate_appearance/basic/scent_hunter/mobShouldSee(mob/M) +// if(isliving(M)) +// var/mob/living/L = M +// if(L.has_status_effect(STATUS_EFFECT_SCENT_HUNTER) || L.has_status_effect(STATUS_EFFECT_BLOOD_HUNTER)) +// return TRUE +// if(isobserver(M)) +// return TRUE +// return FALSE diff --git a/code/game/machinery/mindmachine.dm b/code/game/machinery/mindmachine.dm index e74a06d33465..3910397e85bb 100644 --- a/code/game/machinery/mindmachine.dm +++ b/code/game/machinery/mindmachine.dm @@ -496,8 +496,7 @@ /datum/antagonist/rev, // Additional /datum/antagonist/clockcult, // Same as bloodcult. - /datum/antagonist/bloodsucker, - /datum/antagonist/vampire + /datum/antagonist/bloodsucker ) for(var/antag_datum in blacklisted_antag_datums) if(firstOccupant.mind?.has_antag_datum(antag_datum) || secondOccupant.mind?.has_antag_datum(antag_datum)) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 3e2644baeeec..469db4462b77 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -431,9 +431,7 @@ GENE SCANNER blood_type = R.name else blood_type = blood_id - if(HAS_TRAIT(M, TRAIT_MASQUERADE)) //bloodsuckers - combined_msg += span_info("Blood level 100%, 560 cl, type: [blood_type]") - else if(C.blood_volume <= BLOOD_VOLUME_SAFE(C) && C.blood_volume > BLOOD_VOLUME_OKAY(C)) + if(C.blood_volume <= BLOOD_VOLUME_SAFE(C) && C.blood_volume > BLOOD_VOLUME_OKAY(C)) combined_msg += "[span_danger("LOW blood level [blood_percent] %, [C.blood_volume] cl,")] [span_info("type: [blood_type]")]" else if(C.blood_volume <= BLOOD_VOLUME_OKAY(C)) combined_msg += "[span_danger("CRITICAL blood level [blood_percent] %, [C.blood_volume] cl,")] [span_info("type: [blood_type]")]" diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm index 747b52904c15..ecf33a3feea9 100644 --- a/code/game/objects/items/dna_injector.dm +++ b/code/game/objects/items/dna_injector.dm @@ -389,11 +389,11 @@ /obj/item/dnainjector/olfaction name = "\improper DNA injector (Olfaction)" - add_mutations = list(OLFACTION) + //add_mutations = list(OLFACTION) /obj/item/dnainjector/antiolfaction name = "\improper DNA injector (Anti-Olfaction)" - remove_mutations = list(OLFACTION) + //remove_mutations = list(OLFACTION) /obj/item/dnainjector/insulated name = "\improper DNA injector (Insulated)" diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 75adc237ab8b..cee6acb342ec 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -7,7 +7,6 @@ GLOBAL_LIST_EMPTY(antagonists) var/prevent_roundtype_conversion = TRUE //If false, the roundtype will still convert with this antag active var/datum/mind/owner //Mind that owns this datum var/silent = FALSE //Silent will prevent the gain/lose texts to show - var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with var/job_rank var/replace_banned = TRUE //Should replace jobbanned player with ghosts if granted. diff --git a/code/modules/antagonists/bloodsuckers/bloodsucker_assets.dm b/code/modules/antagonists/bloodsuckers/bloodsucker_assets.dm deleted file mode 100644 index 77f8501eff1d..000000000000 --- a/code/modules/antagonists/bloodsuckers/bloodsucker_assets.dm +++ /dev/null @@ -1,15 +0,0 @@ -/datum/asset/simple/bloodsucker_icons - -/datum/asset/simple/bloodsucker_icons/register() - for(var/datum/bloodsucker_clan/clans as anything in typesof(/datum/bloodsucker_clan)) - if(!initial(clans.joinable_clan)) - continue - add_bloodsucker_icon(initial(clans.join_icon), initial(clans.join_icon_state)) - - for(var/datum/action/cooldown/bloodsucker/power as anything in subtypesof(/datum/action/cooldown/bloodsucker)) - add_bloodsucker_icon(initial(power.button_icon), initial(power.button_icon_state)) - - return ..() - -/datum/asset/simple/bloodsucker_icons/proc/add_bloodsucker_icon(bloodsucker_icon, bloodsucker_icon_state) - assets[sanitize_filename("bloodsucker.[bloodsucker_icon_state].png")] = icon(bloodsucker_icon, bloodsucker_icon_state) diff --git a/code/modules/antagonists/bloodsuckers/bloodsucker_flavour.dm b/code/modules/antagonists/bloodsuckers/bloodsucker_flavour.dm new file mode 100644 index 000000000000..f55e4ae76766 --- /dev/null +++ b/code/modules/antagonists/bloodsuckers/bloodsucker_flavour.dm @@ -0,0 +1,149 @@ +/datum/antagonist/bloodsucker + ///Used for assigning your title + var/bloodsucker_title + ///Used for assigning your reputation + var/bloodsucker_reputation + +///Called when you get the antag datum, called only ONCE per antagonist. +/datum/antagonist/bloodsucker/on_gain() + . = ..() + SelectTitle(am_fledgling = TRUE) + SelectReputation(am_fledgling = TRUE) + +//////////////////////////////////////////////////////////////////////////////////// +//--------------------------------Credits flavour---------------------------------// +//////////////////////////////////////////////////////////////////////////////////// +/// Evaluates the conditions of the bloodsucker at the end of each round to pick a flavor message to add +/datum/antagonist/bloodsucker/proc/get_flavor(objectives_complete, optional_objectives_complete) + var/list/flavor = list() + var/flavor_message + var/alive = owner?.current?.stat != DEAD //Technically not necessary because of Final Death objective? + var/escaped = ((owner.current.onCentCom() || owner.current.onSyndieBase()) && alive) + flavor += "
Epilogue: " + var/message_color = "#ef2f3c" + + flavor_message += "flavour texts are a work in progress, stay tuned." + // if(objectives_complete) + // if(optional_objectives_complete) + // message_color = "#008000" + // if(broke_masquerade) + // if(escaped) + // flavor_message += pick(list( + // "What matters of the Masquerade to you? Let it crumble into dust as your tyranny whips forward to dine on more stations. + // News of your butchering exploits will quickly spread, and you know what will encompass the minds of mortals and undead alike. Fear." + // )) + // else if(alive) + // flavor_message += pick(list( + // "Blood still pumps in your veins as you lay stranded on the station. No doubt the wake of chaos left in your path will attract danger, but greater power than you've ever felt courses through your body. + // Let the Camarilla and the witchers come. You will be waiting." + // )) + // else + // if(escaped) + // flavor_message += pick(list( + // "You step off the spacecraft with a mark of pride at a superbly completed mission. Upon arriving back at CentCom, an unassuming assistant palms you an invitation stamped with the Camarilla seal. + // High society awaits: a delicacy you have earned." + // )) + // else if(alive) + // flavor_message += pick(list( + // "This station has become your own slice of paradise. Your mission completed, you turn on the others who were stranded, ripe for your purposes. + // Who knows? If they prove to elevate your power enough, perhaps a new bloodline might be founded here." + // )) + // else + // message_color = "#517fff" + // if(broke_masquerade) + // if(escaped) + // flavor_message += pick(list( + // "Your mission accomplished, you step off the spacecraft, feeling the mark of exile on your neck. Your allies gone, your veins thrum with a singular purpose: survival." + // )) + // else if(alive) + // flavor_message += pick(list( + // "You survived, but you broke the Masquerade, your blood-stained presence clear and your power limited. No doubt death in the form of claw or stake hails its approach. Perhaps it's time to understand the cattles' fascinations with the suns." + // )) + // else + // if(escaped) + // flavor_message += pick(list( + // "A low profile has always suited you best, conspiring enough to satiate the clan and keep your head low. It's not luxorious living, though death is a less kind alternative. On to the next station." + // )) + // else if(alive) + // flavor_message += pick(list( + // "You completed your mission and kept your identity free of heresy, though your mark here is not strong enough to lay a claim. Best stow away when the next shuttle comes around." + // )) + + if(!message_color || (!alive && !escaped)) //perish or just fuck up and fail your primary objectives + message_color = "#ef2f3c" + flavor_message += pick(list( + "Thus ends the story of [return_full_name(TRUE)]. No doubt future generations will look back on your legacy and reflect on the lessons of the past. If they remember you at all." + )) + + flavor += "[flavor_message]
" + return "
[flavor.Join("
")]
" + + +/* + * # Bloodsucker Names + * + * All Bloodsuckers get a name, and gets a better one when they hit Rank 4. + */ +/datum/antagonist/bloodsucker/proc/SelectTitle(am_fledgling = 0, forced = FALSE) + // Already have Title + if(!forced && bloodsucker_title != null) + return + // Titles [Master] + if(!am_fledgling) + if(owner.current.gender == MALE) + bloodsucker_title = pick ("Count","Baron","Viscount","Prince","Duke","Tzar","Dreadlord","Lord","Master") + else + bloodsucker_title = pick ("Countess","Baroness","Viscountess","Princess","Duchess","Tzarina","Dreadlady","Lady","Mistress") + to_chat(owner, span_announce("You have earned a title! You are now known as [return_full_name(TRUE)]!")) + // Titles [Fledgling] + else + bloodsucker_title = null + + + +/datum/antagonist/bloodsucker/proc/AmFledgling() + return !bloodsucker_title + +/datum/antagonist/bloodsucker/proc/return_full_name(include_rep = FALSE) + + var/fullname = owner.current.name + // Name First + // Title + if(bloodsucker_title) + fullname = bloodsucker_title + " " + fullname + // Rep + if(include_rep && bloodsucker_reputation) + fullname = fullname + " the " + bloodsucker_reputation + + return fullname + +/datum/antagonist/bloodsucker/proc/SelectReputation(am_fledgling = FALSE, forced = FALSE) + // Already have Reputation + if(!forced && bloodsucker_reputation != null) + return + + if(am_fledgling) + bloodsucker_reputation = pick( + "Crude","Callow","Unlearned","Neophyte","Novice","Unseasoned", + "Fledgling","Young","Neonate","Scrapling","Untested","Unproven", + "Unknown","Newly Risen","Born","Scavenger","Unknowing","Unspoiled", + "Disgraced","Defrocked","Shamed","Meek","Timid","Broken","Fresh", + ) + else if(owner.current.gender == MALE && prob(10)) + bloodsucker_reputation = pick("King of the Damned", "Blood King", "Emperor of Blades", "Sinlord", "God-King") + else if(owner.current.gender == FEMALE && prob(10)) + bloodsucker_reputation = pick("Queen of the Damned", "Blood Queen", "Empress of Blades", "Sinlady", "God-Queen") + else + bloodsucker_reputation = pick( + "Butcher","Blood Fiend","Crimson","Red","Black","Terror", + "Nightman","Feared","Ravenous","Fiend","Malevolent","Wicked", + "Ancient","Plaguebringer","Sinister","Forgotten","Wretched","Baleful", + "Inqisitor","Harvester","Reviled","Robust","Betrayer","Destructor", + "Damned","Accursed","Terrible","Vicious","Profane","Vile", + "Depraved","Foul","Slayer","Manslayer","Sovereign","Slaughterer", + "Forsaken","Mad","Dragon","Savage","Villainous","Nefarious", + "Inquisitor","Marauder","Horrible","Immortal","Undying","Overlord", + "Corrupt","Hellspawn","Tyrant","Sanguineous", + ) + + to_chat(owner, span_announce("You have earned a reputation! You are now known as [return_full_name(TRUE)]!")) diff --git a/code/modules/antagonists/bloodsuckers/bloodsucker_flaws.dm b/code/modules/antagonists/bloodsuckers/bloodsucker_flaws.dm deleted file mode 100644 index 49d158c23254..000000000000 --- a/code/modules/antagonists/bloodsuckers/bloodsucker_flaws.dm +++ /dev/null @@ -1,23 +0,0 @@ -/datum/antagonist/bloodsucker/proc/assign_clan_and_bane() - if(my_clan) - return - var/list/options = list() - var/list/radial_display = list() - for(var/datum/bloodsucker_clan/all_clans as anything in typesof(/datum/bloodsucker_clan)) - if(!initial(all_clans.joinable_clan)) //flavortext only - continue - options[initial(all_clans.name)] = all_clans - var/datum/radial_menu_choice/option = new - option.image = image(icon = initial(all_clans.join_icon), icon_state = initial(all_clans.join_icon_state)) - option.info = "[initial(all_clans.name)] - [span_boldnotice(initial(all_clans.join_description))]" - radial_display[initial(all_clans.name)] = option - - var/chosen_clan = show_radial_menu(owner.current, owner.current, radial_display) - chosen_clan = options[chosen_clan] - if(QDELETED(src) || QDELETED(owner.current)) - return FALSE - if(!chosen_clan) - to_chat(owner, span_announce("You choose to remain ignorant, for now.")) - return - my_clan = new chosen_clan(src) - owner.announce_objectives() diff --git a/code/modules/antagonists/bloodsuckers/bloodsucker_frenzy.dm b/code/modules/antagonists/bloodsuckers/bloodsucker_frenzy.dm index 76bc4a6b4b8c..2c2edeeb39b0 100644 --- a/code/modules/antagonists/bloodsuckers/bloodsucker_frenzy.dm +++ b/code/modules/antagonists/bloodsuckers/bloodsucker_frenzy.dm @@ -1,20 +1,3 @@ - -/** - * # FrenzyGrab - * - * The martial art given to Bloodsuckers so they can instantly aggressively grab people. - */ -/datum/martial_art/frenzygrab - name = "Frenzy Grab" - id = MARTIALART_FRENZYGRAB - -/datum/martial_art/frenzygrab/grab_act(mob/living/user, mob/living/target) - if(user != target) - target.grabbedby(user) - target.grippedby(user, instant = TRUE) - return TRUE - return ..() - /** * # Status effect * @@ -41,16 +24,14 @@ icon_state = "power_recover" alerttooltipstyle = "cult" -/atom/movable/screen/alert/status_effect/masquerade/MouseEntered(location,control,params) - desc = initial(desc) - return ..() - /datum/status_effect/frenzy/on_apply() var/mob/living/carbon/human/user = owner bloodsuckerdatum = IS_BLOODSUCKER(user) + if(!bloodsuckerdatum) + return FALSE + // Disable ALL Powers and notify their entry - bloodsuckerdatum.DisableAllPowers(forced = TRUE) to_chat(owner, span_userdanger("Blood! You need Blood, now! You enter a total Frenzy! Your skin starts sizzling....")) to_chat(owner, span_announce("* Bloodsucker Tip: While in Frenzy, you instantly Aggresively grab, have stun resistance, and cannot speak, hear, or use any powers outside of Feed and Trespass (If you have it).")) owner.balloon_alert(owner, "you enter a frenzy!") @@ -60,11 +41,12 @@ // Give the other Frenzy effects owner.add_traits(list(TRAIT_MUTE, TRAIT_DEAF, TRAIT_IGNOREDAMAGESLOWDOWN, TRAIT_STUNIMMUNE), FRENZY_TRAIT) + if(user.IsAdvancedToolUser()) was_tooluser = TRUE ADD_TRAIT(owner, TRAIT_MONKEYLIKE, SPECIES_TRAIT) + owner.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-0.4, blacklisted_movetypes=(FLYING|FLOATING)) - bloodsuckerdatum.frenzygrab.teach(user, TRUE) owner.add_client_colour(/datum/client_colour/cursed_heart_blood) var/obj/item/cuffs = user.get_item_by_slot(ITEM_SLOT_HANDCUFFED) var/obj/item/legcuffs = user.get_item_by_slot(ITEM_SLOT_LEGCUFFED) @@ -72,8 +54,6 @@ user.clear_cuffs(cuffs, TRUE, TRUE) user.clear_cuffs(legcuffs, TRUE, TRUE) // Keep track of how many times we've entered a Frenzy. - bloodsuckerdatum.frenzies++ - bloodsuckerdatum.frenzied = TRUE return ..() /datum/status_effect/frenzy/on_remove() @@ -84,13 +64,10 @@ REMOVE_TRAIT(owner, TRAIT_MONKEYLIKE, SPECIES_TRAIT) was_tooluser = FALSE owner.remove_movespeed_modifier(type) - bloodsuckerdatum.frenzygrab.remove(user) owner.remove_client_colour(/datum/client_colour/cursed_heart_blood) owner.adjust_dizzy(3 SECONDS) owner.Paralyze(2 SECONDS) user.physiology.stamina_mod /= 0.4 - - bloodsuckerdatum.frenzied = FALSE return ..() /datum/status_effect/frenzy/tick() @@ -101,6 +78,4 @@ user.clear_cuffs(cuffs, TRUE, TRUE) user.clear_cuffs(legcuffs, TRUE, TRUE) user.balloon_alert_to_viewers("snaps [user.p_their()] restraints!", "you break free of your restraints!") - if(!bloodsuckerdatum.frenzied) - return - user.adjustFireLoss(min(0.5 + (bloodsuckerdatum.humanity_lost / 15), bloodsuckerdatum.my_clan?.get_clan() == CLAN_GANGREL ? 2 : 100)) //:D + user.adjustFireLoss(0.5) diff --git a/code/modules/antagonists/bloodsuckers/bloodsucker_integration.dm b/code/modules/antagonists/bloodsuckers/bloodsucker_integration.dm index 4e019487700e..50cfb50ea95a 100644 --- a/code/modules/antagonists/bloodsuckers/bloodsucker_integration.dm +++ b/code/modules/antagonists/bloodsuckers/bloodsucker_integration.dm @@ -1,23 +1,6 @@ - /* * OVERWRITES */ - -/datum/species/jelly/slime/spec_life(mob/living/carbon/human/user) - // Prevents Slimeperson 'gaming - if(IS_BLOODSUCKER(user)) - return - . = ..() - -// Used when analyzing a Bloodsucker, Masquerade will hide brain traumas -/mob/living/carbon/get_traumas() - if(!mind) - return ..() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = IS_BLOODSUCKER(src) - if(bloodsuckerdatum && HAS_TRAIT(src, TRAIT_MASQUERADE)) - return - . = ..() - // Used to keep track of how much Blood we've drank so far /mob/living/get_status_tab_items() . = ..() @@ -25,28 +8,10 @@ var/datum/antagonist/bloodsucker/bloodsuckerdatum = mind.has_antag_datum(/datum/antagonist/bloodsucker) if(bloodsuckerdatum) . += "" - . += "Current Frenzy Enter: [FRENZY_THRESHOLD_ENTER + bloodsuckerdatum.humanity_lost * 10]" - . += "Current Frenzy Leave: [FRENZY_THRESHOLD_EXIT + bloodsuckerdatum.humanity_lost * 10]" - . += "Blood Drank: [bloodsuckerdatum.total_blood_drank]" - if(bloodsuckerdatum.has_task) - . += "Task Blood Drank: [bloodsuckerdatum.task_blood_drank]" - -// INTEGRATION: Adding Procs and Datums to existing "classes" // - -/mob/living/proc/HaveBloodsuckerBodyparts(displaymessage = "") // displaymessage can be something such as "rising from death" for Torpid Sleep. givewarningto is the person receiving messages. - if(!getorganslot(ORGAN_SLOT_HEART)) - if(displaymessage != "") - to_chat(src, span_warning("Without a heart, you are incapable of [displaymessage].")) - return FALSE - if(!get_bodypart(BODY_ZONE_HEAD)) - if(displaymessage != "") - to_chat(src, span_warning("Without a head, you are incapable of [displaymessage].")) - return FALSE - if(!getorgan(/obj/item/organ/brain)) // NOTE: This is mostly just here so we can do one scan for all needed parts when creating a vamp. You probably won't be trying to use powers w/out a brain. - if(displaymessage != "") - to_chat(src, span_warning("Without a brain, you are incapable of [displaymessage].")) - return FALSE - return TRUE + . += "Current Frenzy Enter: [FRENZY_THRESHOLD_ENTER]" + . += "Current Frenzy Leave: [FRENZY_THRESHOLD_EXIT]" + // if(bloodsuckerdatum.has_task) + // . += "Task Blood Drank: [bloodsuckerdatum.task_blood_drank]" // EXAMINING /mob/living/carbon/proc/return_vamp_examine(mob/living/viewer) @@ -64,7 +29,7 @@ return returnIcon + returnString // Viewer not a Vamp AND not the target's vassal? if(!viewer.mind.has_antag_datum((/datum/antagonist/bloodsucker)) && !(viewer in bloodsuckerdatum.vassals)) - if(!(HAS_TRAIT(viewer, TRAIT_BLOODSUCKER_HUNTER) && bloodsuckerdatum.broke_masquerade)) + if(!HAS_TRAIT(viewer, TRAIT_BLOODSUCKER_HUNTER)) return "" // Default String var/returnString = "\[[bloodsuckerdatum.return_full_name(1)]\]" @@ -95,7 +60,7 @@ returnIcon = "[icon2html('icons/mob/vampiric.dmi', world, "vassal")]" // Am I someone ELSE'S Vassal? else if(IS_BLOODSUCKER(viewer) || IS_MONSTERHUNTER(viewer)) - returnString += "This [dna.species.name] bears the mark of [vassaldatum.master.return_full_name(vassaldatum.master.owner.current,TRUE)][vassaldatum.master.broke_masquerade ? " who has broken the Masquerade" : ""]" + returnString += "This [dna.species.name] bears the mark of [vassaldatum.master.return_full_name(vassaldatum.master.owner.current,TRUE)]" returnIcon = "[icon2html('icons/mob/vampiric.dmi', world, "vassal_grey")]" // Are you serving the same master as I am? else if(viewer.mind.has_antag_datum(/datum/antagonist/vassal) in vassaldatum?.master.vassals) @@ -110,21 +75,3 @@ returnString += "\]" // \n" Don't need spacers. Using . += "" in examine.dm does this on its own. return returnIcon + returnString - -/// Am I "pale" when examined? - Bloodsuckers on Masquerade will hide this. -/mob/living/carbon/human/proc/ShowAsPaleExamine(mob/living/user, blood_volume) - if(!mind) - return BLOODSUCKER_SHOW_BLOOD - var/datum/antagonist/bloodsucker/bloodsuckerdatum = mind.has_antag_datum(/datum/antagonist/bloodsucker) - // Not a Bloodsucker? - if(!bloodsuckerdatum) - return BLOODSUCKER_SHOW_BLOOD - // Blood level too low to be hidden? - if(blood_volume <= BLOOD_VOLUME_BAD(user) || bloodsuckerdatum.frenzied) - return BLOODSUCKER_SHOW_BLOOD - // Special check: Nosferatu will always be Pale Death - if(HAS_TRAIT(src, TRAIT_MASQUERADE)) - return BLOODSUCKER_HIDE_BLOOD - return BLOODSUCKER_SHOW_BLOOD - // If a Bloodsucker is malnourished, AND if his temperature matches his surroundings (aka he hasn't fed recently and looks COLD) -// return blood_volume < BLOOD_VOLUME_OKAY // && !(bodytemperature <= get_temperature() + 2) diff --git a/code/modules/antagonists/bloodsuckers/bloodsucker_mobs.dm b/code/modules/antagonists/bloodsuckers/bloodsucker_mobs.dm index 68a2e413ee5b..0368355d95cd 100644 --- a/code/modules/antagonists/bloodsuckers/bloodsucker_mobs.dm +++ b/code/modules/antagonists/bloodsuckers/bloodsucker_mobs.dm @@ -185,82 +185,82 @@ /// Werewolf /// /////////////////////////// -/mob/living/simple_animal/hostile/bloodsucker/werewolf/Life(delta_time = (SSmobs.wait/10), times_fired) - . = ..() - if(bloodsucker) - if(ishuman(bloodsucker)) - var/mob/living/carbon/human/user = bloodsucker - var/datum/antagonist/bloodsucker/bloodsuckerdatum = src.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(user.blood_volume < FRENZY_THRESHOLD_EXIT + bloodsuckerdatum.humanity_lost * 10) - user.blood_volume += 10 - adjustFireLoss(2.5) - updatehealth() //3 minutes to die +// /mob/living/simple_animal/hostile/bloodsucker/werewolf/Life(delta_time = (SSmobs.wait/10), times_fired) +// . = ..() +// if(bloodsucker) +// if(ishuman(bloodsucker)) +// var/mob/living/carbon/human/user = bloodsucker +// var/datum/antagonist/bloodsucker/bloodsuckerdatum = src.mind.has_antag_datum(/datum/antagonist/bloodsucker) +// if(user.blood_volume < FRENZY_THRESHOLD_EXIT) +// user.blood_volume += 10 +// adjustFireLoss(2.5) +// updatehealth() //3 minutes to die - if(satiation >= 3) - to_chat(src, span_notice("It has been fed. You turn back to normal.")) - qdel(src) - return +// if(satiation >= 3) +// to_chat(src, span_notice("It has been fed. You turn back to normal.")) +// qdel(src) +// return -/mob/living/simple_animal/hostile/bloodsucker/werewolf/Destroy() - . = ..() - if(ishuman(bloodsucker)) - var/mob/living/carbon/human/user = bloodsucker - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - var/datum/species/user_species = user.dna.species - var/mutation = "" - var/slot = "" - var/additionalmessage = "" - bloodsuckerdatum.clanprogress++ - switch(bloodsuckerdatum.clanprogress) - if(1) - additionalmessage = "You have mutated a collar made out of fur!" - user_species.armor += 10 - mutation = /obj/item/clothing/neck/wolfcollar - slot = ITEM_SLOT_NECK - if(2) - additionalmessage = "You have mutated werewolf ears!" - mutation = /obj/item/radio/headset/wolfears - slot = ITEM_SLOT_EARS - if(3) - additionalmessage = "You have mutated werewolf claws!" - user.physiology.punchdamagehigh_bonus += 2.5 - user.physiology.punchdamagelow_bonus += 2.5 - mutation = /obj/item/clothing/gloves/wolfclaws - slot = ITEM_SLOT_GLOVES - if(4) - additionalmessage = "You have mutated werewolf legs!" - mutation = /obj/item/clothing/shoes/wolflegs - slot = ITEM_SLOT_FEET - if(HAS_TRAIT(user, TRAIT_DIGITIGRADE)) - mutation = /obj/item/clothing/shoes/xeno_wraps/wolfdigilegs - if(5 to INFINITY) - to_chat(user, span_danger("The beast inside of you seems satisfied with your current form.")) - return - to_chat(user, span_danger("After returning to normal, you feel strange. [additionalmessage]")) - var/obj/item/pastdrip = user.get_item_by_slot(slot) - user.dropItemToGround(pastdrip) - user.equip_to_slot_or_del(new mutation(user), slot) +// /mob/living/simple_animal/hostile/bloodsucker/werewolf/Destroy() +// . = ..() +// if(ishuman(bloodsucker)) +// var/mob/living/carbon/human/user = bloodsucker +// var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) +// var/datum/species/user_species = user.dna.species +// var/mutation = "" +// var/slot = "" +// var/additionalmessage = "" +// bloodsuckerdatum.clanprogress++ +// switch(bloodsuckerdatum.clanprogress) +// if(1) +// additionalmessage = "You have mutated a collar made out of fur!" +// user_species.armor += 10 +// mutation = /obj/item/clothing/neck/wolfcollar +// slot = ITEM_SLOT_NECK +// if(2) +// additionalmessage = "You have mutated werewolf ears!" +// mutation = /obj/item/radio/headset/wolfears +// slot = ITEM_SLOT_EARS +// if(3) +// additionalmessage = "You have mutated werewolf claws!" +// user.physiology.punchdamagehigh_bonus += 2.5 +// user.physiology.punchdamagelow_bonus += 2.5 +// mutation = /obj/item/clothing/gloves/wolfclaws +// slot = ITEM_SLOT_GLOVES +// if(4) +// additionalmessage = "You have mutated werewolf legs!" +// mutation = /obj/item/clothing/shoes/wolflegs +// slot = ITEM_SLOT_FEET +// if(HAS_TRAIT(user, TRAIT_DIGITIGRADE)) +// mutation = /obj/item/clothing/shoes/xeno_wraps/wolfdigilegs +// if(5 to INFINITY) +// to_chat(user, span_danger("The beast inside of you seems satisfied with your current form.")) +// return +// to_chat(user, span_danger("After returning to normal, you feel strange. [additionalmessage]")) +// var/obj/item/pastdrip = user.get_item_by_slot(slot) +// user.dropItemToGround(pastdrip) +// user.equip_to_slot_or_del(new mutation(user), slot) -//////////////////////// -/// Armor /// -//////////////////////// -/mob/living/simple_animal/hostile/bloodsucker/possessedarmor/ListTargets() - . = ..() - for(var/mob/living/carbon/letsnotmeet in .) - if(!istype(letsnotmeet)) - continue - if(IS_BLOODSUCKER(letsnotmeet) || IS_VASSAL(letsnotmeet)) //don't attack our owners! - . -= letsnotmeet - if(letsnotmeet.restrained()) //or any guests! - . -= letsnotmeet +// //////////////////////// +// /// Armor /// +// //////////////////////// +// /mob/living/simple_animal/hostile/bloodsucker/possessedarmor/ListTargets() +// . = ..() +// for(var/mob/living/carbon/letsnotmeet in .) +// if(!istype(letsnotmeet)) +// continue +// if(IS_BLOODSUCKER(letsnotmeet) || IS_VASSAL(letsnotmeet)) //don't attack our owners! +// . -= letsnotmeet +// if(letsnotmeet.restrained()) //or any guests! +// . -= letsnotmeet -/mob/living/simple_animal/hostile/bloodsucker/possessedarmor/death() - . = ..() - if(upgraded) - new /obj/structure/bloodsucker/possessedarmor/upgraded(src.loc) - else - new /obj/structure/bloodsucker/possessedarmor(src.loc) - qdel(src) +// /mob/living/simple_animal/hostile/bloodsucker/possessedarmor/death() +// . = ..() +// if(upgraded) +// new /obj/structure/bloodsucker/possessedarmor/upgraded(src.loc) +// else +// new /obj/structure/bloodsucker/possessedarmor(src.loc) +// qdel(src) //Wraith - Hecata mob diff --git a/code/modules/antagonists/bloodsuckers/bloodsuckers.dm b/code/modules/antagonists/bloodsuckers/bloodsuckers.dm index c7257b6d44ea..1f1503e299c6 100644 --- a/code/modules/antagonists/bloodsuckers/bloodsuckers.dm +++ b/code/modules/antagonists/bloodsuckers/bloodsuckers.dm @@ -3,11 +3,10 @@ show_in_antagpanel = TRUE roundend_category = "bloodsuckers" antagpanel_category = "Bloodsucker" - job_rank = ROLE_BLOODSUCKER antag_hud_name = "bloodsucker" + job_rank = ROLE_BLOODSUCKER show_to_ghosts = TRUE show_name_in_check_antagonists = TRUE - can_coexist_with_others = FALSE ui_name = "AntagInfoBloodsucker" preview_outfit = /datum/outfit/bloodsucker_outfit count_towards_antag_cap = TRUE @@ -18,30 +17,15 @@ ///Timer between alerts for Healing messages COOLDOWN_DECLARE(static/bloodsucker_spam_healing) - ///Used for assigning your name - var/bloodsucker_name - ///Used for assigning your title - var/bloodsucker_title - ///Used for assigning your reputation - var/bloodsucker_reputation - - ///Amount of Humanity lost - var/humanity_lost = 0 - ///Have we been broken the Masquerade? - var/broke_masquerade = FALSE - ///How many Masquerade Infractions do we have? - var/masquerade_infractions = 0 ///If we are currently in a Frenzy var/frenzied = FALSE - ///If we have a task assigned - var/has_task = FALSE + ///The current task we have assigned, if any + var/datum/altar_task/current_task ///How many times have we used a blood altar var/altar_uses = 0 - ///ALL Powers currently owned - var/list/datum/action/cooldown/bloodsucker/powers = list() - ///Frenzy Grab Martial art given to Bloodsuckers in a Frenzy - var/datum/martial_art/frenzygrab/frenzygrab = new + // give TRAIT_STRONG_GRABBER during frenzy + ///How many clan points you have -> Used in clans in order to assert certain limits // Upgrades and stuff var/clanpoints = 0 ///How much progress have you done on your clan @@ -52,10 +36,11 @@ ///Special vassals I own, to not have double of the same type. var/list/datum/antagonist/vassal/special_vassals = list() - var/bloodsucker_level + var/bloodsucker_level = 1 var/bloodsucker_level_unspent = 1 + var/passive_blood_drain = -0.1 - var/additional_regen + var/additional_regen = 0 var/bloodsucker_regen_rate = 0.3 /// How much blood we have, starting off at default blood levels. var/bloodsucker_blood_volume = BLOOD_VOLUME_GENERIC @@ -67,12 +52,6 @@ // Used for Bloodsucker Objectives var/area/bloodsucker_lair_area var/obj/structure/closet/crate/coffin - var/total_blood_drank = 0 - var/frenzy_blood_drank = 0 - var/task_heart_required = 0 - var/task_blood_required = 0 - var/task_blood_drank = 0 - var/frenzies = 0 ///Blood display HUD var/atom/movable/screen/bloodsucker/blood_counter/blood_display ///Vampire level display HUD @@ -80,12 +59,11 @@ ///Sunlight timer HUD var/atom/movable/screen/bloodsucker/sunlight_counter/sunlight_display - /// Static typecache of all bloodsucker powers. - var/static/list/all_bloodsucker_powers = typecacheof(/datum/action/cooldown/bloodsucker, TRUE) /// Antagonists that cannot be Vassalized no matter what var/static/list/vassal_banned_antags = list( /datum/antagonist/bloodsucker, - /datum/antagonist/monsterhunter, + /datum/antagonist/darkspawn, + /datum/antagonist/monsterhunter ) ///Default Bloodsucker traits var/static/list/bloodsucker_traits = list( @@ -105,8 +83,30 @@ TRAIT_TOXIMMUNE, TRAIT_HARDLY_WOUNDED, TRAIT_RESISTDAMAGESLOWDOWN, + TRAIT_UNHOLY ) +//////////////////////////////////////////////////////////////////////////////////// +//--------------------------------------Gain--------------------------------------// +//////////////////////////////////////////////////////////////////////////////////// +///Called when you get the antag datum, called only ONCE per antagonist. +/datum/antagonist/bloodsucker/on_gain() + RegisterSignal(SSsunlight, COMSIG_SOL_RANKUP_BLOODSUCKERS, PROC_REF(sol_rank_up)) + RegisterSignal(SSsunlight, COMSIG_SOL_NEAR_START, PROC_REF(sol_near_start)) + RegisterSignal(SSsunlight, COMSIG_SOL_END, PROC_REF(on_sol_end)) + RegisterSignal(SSsunlight, COMSIG_SOL_RISE_TICK, PROC_REF(handle_sol)) + RegisterSignal(SSsunlight, COMSIG_SOL_WARNING_GIVEN, PROC_REF(give_warning)) + + RegisterSignal(owner, COMSIG_MIND_CHECK_ANTAG_RESOURCE, PROC_REF(has_blood)) + RegisterSignal(owner, COMSIG_MIND_SPEND_ANTAG_RESOURCE, PROC_REF(use_blood)) + + // Start Sunlight if first Bloodsucker + check_start_sunlight() + // Objectives + forge_bloodsucker_objectives() + + return ..() + /** * Apply innate effects is everything given to the mob * When a body is tranferred, this is called on the new mob @@ -118,20 +118,35 @@ RegisterSignal(current_mob, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) RegisterSignal(current_mob, COMSIG_LIVING_LIFE, PROC_REF(LifeTick)) RegisterSignal(current_mob, COMSIG_LIVING_DEATH, PROC_REF(on_death)) + current_mob.add_traits(bloodsucker_traits, type) + handle_clown_mutation(current_mob, mob_override ? null : "As a vampiric clown, you are no longer a danger to yourself. Your clownish nature has been subdued by your thirst for blood.") add_team_hud(current_mob) - ADD_TRAIT(current_mob, TRAIT_UNHOLY, type) if(current_mob.hud_used) on_hud_created() else RegisterSignal(current_mob, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created)) + #ifdef BLOODSUCKER_TESTING var/turf/user_loc = get_turf(current_mob) new /obj/structure/closet/crate/coffin(user_loc) new /obj/structure/bloodsucker/vassalrack(user_loc) #endif + + +//////////////////////////////////////////////////////////////////////////////////// +//------------------------------------Removal-------------------------------------// +//////////////////////////////////////////////////////////////////////////////////// +/// Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion. +/datum/antagonist/bloodsucker/on_removal() + /// End Sunlight? (if last Vamp) + UnregisterSignal(SSsunlight, list(COMSIG_SOL_RANKUP_BLOODSUCKERS, COMSIG_SOL_NEAR_START, COMSIG_SOL_END, COMSIG_SOL_RISE_TICK, COMSIG_SOL_WARNING_GIVEN)) + UnregisterSignal(owner, list(COMSIG_MIND_CHECK_ANTAG_RESOURCE, COMSIG_MIND_SPEND_ANTAG_RESOURCE)) + check_cancel_sunlight() //check if sunlight should end + return ..() + /** * Remove innate effects is everything given to the mob * When a body is tranferred, this is called on the new mob @@ -142,7 +157,7 @@ . = ..() var/mob/living/current_mob = mob_override || owner.current UnregisterSignal(current_mob, list(COMSIG_LIVING_LIFE, COMSIG_ATOM_EXAMINE, COMSIG_LIVING_DEATH)) - REMOVE_TRAIT(current_mob, TRAIT_UNHOLY, type) + current_mob.remove_traits(bloodsucker_traits, type) if(current_mob.hud_used) var/datum/hud/hud_used = current_mob.hud_used @@ -155,6 +170,23 @@ QDEL_NULL(vamprank_display) QDEL_NULL(sunlight_display) +//////////////////////////////////////////////////////////////////////////////////// +//------------------------------Greet and farewell--------------------------------// +//////////////////////////////////////////////////////////////////////////////////// +/datum/antagonist/bloodsucker/greet() + . = ..() + var/fullname = return_full_name(TRUE) + to_chat(owner, span_userdanger("You are [fullname], a strain of vampire known as a Bloodsucker!")) + owner.announce_objectives() + owner.current.playsound_local(null, 'sound/ambience/antag/bloodsuckeralert.ogg', 100, FALSE, pressure_affected = FALSE) + antag_memory += "Although you were born a mortal, in undeath you earned the name [fullname].
" + +/datum/antagonist/bloodsucker/farewell() + to_chat(owner.current, span_userdanger("With a snap, your curse has ended. You are no longer a Bloodsucker. You live once more!")) + +//////////////////////////////////////////////////////////////////////////////////// +//----------------------------------Hud Handling----------------------------------// +//////////////////////////////////////////////////////////////////////////////////// /datum/antagonist/bloodsucker/proc/on_hud_created(datum/source) SIGNAL_HANDLER @@ -171,16 +203,46 @@ INVOKE_ASYNC(bloodsucker_hud, TYPE_PROC_REF(/datum/hud/, show_hud), bloodsucker_hud.hud_version) +//////////////////////////////////////////////////////////////////////////////////// +//-----------------------------------Ability use----------------------------------// +//////////////////////////////////////////////////////////////////////////////////// +/datum/antagonist/bloodsucker/proc/has_blood(datum/mind, flag = ANTAG_RESOURCE_BLOODSUCKER, amt) + SIGNAL_HANDLER + if(flag != ANTAG_RESOURCE_BLOODSUCKER) + return FALSE + return bloodsucker_blood_volume >= amt + +/datum/antagonist/bloodsucker/proc/use_blood(datum/mind, list/resource_costs) + SIGNAL_HANDLER + if(!LAZYLEN(resource_costs)) + return + var/amount = resource_costs[ANTAG_RESOURCE_BLOODSUCKER] + if(!amount) + return + if(!has_blood(amt = amount)) + return + bloodsucker_blood_volume -= amount + update_hud() + return TRUE +//////////////////////////////////////////////////////////////////////////////////// +//--------------------------------Admin Tools-------------------------------------// +//////////////////////////////////////////////////////////////////////////////////// +// Called when using admin tools to give antag status, admin spawned bloodsuckers don't get turned human if plasmaman. +/datum/antagonist/bloodsucker/admin_add(datum/mind/new_owner, mob/admin) + var/levels = input("How many unspent Ranks would you like [new_owner] to have?","Bloodsucker Rank", bloodsucker_level_unspent) as null | num + var/msg = " made [key_name_admin(new_owner)] into \a [name]" + if(levels > 1) + bloodsucker_level_unspent = levels + msg += " with [levels] extra unspent Ranks." + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + new_owner.add_antag_datum(src) + /datum/antagonist/bloodsucker/get_admin_commands() . = ..() - .["Give Level"] = CALLBACK(src, PROC_REF(RankUp)) - if(bloodsucker_level_unspent >= 1) - .["Remove Level"] = CALLBACK(src, PROC_REF(RankDown)) - - if(broke_masquerade) - .["Fix Masquerade"] = CALLBACK(src, PROC_REF(fix_masquerade)) - else - .["Break Masquerade"] = CALLBACK(src, PROC_REF(break_masquerade)) + // .["Give Level"] = CALLBACK(src, PROC_REF(RankUp)) + // if(bloodsucker_level_unspent >= 1) + // .["Remove Level"] = CALLBACK(src, PROC_REF(RankDown)) if(!my_clan) .["Force Clan"] = CALLBACK(src, PROC_REF(force_clan)) @@ -209,113 +271,34 @@ my_clan = new chosen(src) owner.announce_objectives() -///Called when you get the antag datum, called only ONCE per antagonist. -/datum/antagonist/bloodsucker/on_gain() - RegisterSignal(SSsunlight, COMSIG_SOL_RANKUP_BLOODSUCKERS, PROC_REF(sol_rank_up)) - RegisterSignal(SSsunlight, COMSIG_SOL_NEAR_START, PROC_REF(sol_near_start)) - RegisterSignal(SSsunlight, COMSIG_SOL_END, PROC_REF(on_sol_end)) - RegisterSignal(SSsunlight, COMSIG_SOL_RISE_TICK, PROC_REF(handle_sol)) - RegisterSignal(SSsunlight, COMSIG_SOL_WARNING_GIVEN, PROC_REF(give_warning)) - - if(IS_FAVORITE_VASSAL(owner.current)) // Vassals shouldnt be getting the same benefits as Bloodsuckers. - bloodsucker_level_unspent = 0 - show_in_roundend = FALSE - else - // Start Sunlight if first Bloodsucker - check_start_sunlight() - // Name and Titles - SelectFirstName() - SelectTitle(am_fledgling = TRUE) - SelectReputation(am_fledgling = TRUE) - // Objectives - forge_bloodsucker_objectives() - - . = ..() - // Assign Powers - give_starting_powers() - assign_starting_stats() - -/// Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion. -/datum/antagonist/bloodsucker/on_removal() - /// End Sunlight? (if last Vamp) - UnregisterSignal(SSsunlight, list(COMSIG_SOL_RANKUP_BLOODSUCKERS, COMSIG_SOL_NEAR_START, COMSIG_SOL_END, COMSIG_SOL_RISE_TICK, COMSIG_SOL_WARNING_GIVEN)) - clear_powers_and_stats() - check_cancel_sunlight() //check if sunlight should end - return ..() - -/datum/antagonist/bloodsucker/on_body_transfer(mob/living/old_body, mob/living/new_body) - . = ..() - if(istype(new_body, /mob/living/simple_animal/hostile/bloodsucker) || istype(old_body, /mob/living/simple_animal/hostile/bloodsucker)) +/datum/antagonist/bloodsucker/proc/assign_clan_and_bane() + if(my_clan) return - for(var/datum/action/cooldown/bloodsucker/all_powers as anything in powers) - all_powers.Remove(old_body) - all_powers.Grant(new_body) - var/old_punchdamagelow - var/old_punchdamagehigh - var/old_punchstunchance - var/old_species_punchdamagelow - var/old_species_punchdamagehigh - var/old_species_punchstunchance - if(ishuman(old_body)) - var/mob/living/carbon/human/old_user = old_body - var/datum/species/old_species = old_user.dna.species - old_species.species_traits -= DRINKSBLOOD - //Keep track of what they were - old_punchdamagelow = old_species.punchdamagelow - old_punchdamagehigh = old_species.punchdamagehigh - old_punchstunchance = old_species.punchstunchance - //Then reset them - old_species.punchdamagelow = initial(old_species.punchdamagelow) - old_species.punchdamagehigh = initial(old_species.punchdamagehigh) - old_species.punchstunchance = initial(old_species.punchstunchance) - //Then save the new, old, original species values so we can use them in the next part. This is starting to get convoluted. - old_species_punchdamagelow = old_species.punchdamagelow - old_species_punchdamagehigh = old_species.punchdamagehigh - old_species_punchstunchance = old_species.punchstunchance - if(ishuman(new_body)) - var/mob/living/carbon/human/new_user = new_body - var/datum/species/new_species = new_user.dna.species - new_species.species_traits += DRINKSBLOOD - //Adjust new species punch damage - new_species.punchdamagelow += (old_punchdamagelow - old_species_punchdamagelow) //Takes whatever DIFFERENCE you had between your punch damage and that of the baseline species - new_species.punchdamagehigh += (old_punchdamagehigh - old_species_punchdamagehigh) //and adds it to your new species, thus preserving whatever bonuses you got - new_species.punchstunchance += (old_punchstunchance - old_species_punchstunchance) - - //Give Bloodsucker Traits - if(old_body) - old_body.remove_traits(bloodsucker_traits, BLOODSUCKER_TRAIT) - new_body.add_traits(bloodsucker_traits, BLOODSUCKER_TRAIT) - - -/datum/antagonist/bloodsucker/greet() - . = ..() - var/fullname = return_full_name(TRUE) - to_chat(owner, span_userdanger("You are [fullname], a strain of vampire known as a Bloodsucker!")) + var/list/options = list() + var/list/radial_display = list() + for(var/datum/bloodsucker_clan/all_clans as anything in typesof(/datum/bloodsucker_clan)) + if(!initial(all_clans.joinable_clan)) //flavortext only + continue + options[initial(all_clans.name)] = all_clans + var/datum/radial_menu_choice/option = new + option.image = image(icon = initial(all_clans.join_icon), icon_state = initial(all_clans.join_icon_state)) + option.info = "[initial(all_clans.name)] - [span_boldnotice(initial(all_clans.join_description))]" + radial_display[initial(all_clans.name)] = option + + var/chosen_clan = show_radial_menu(owner.current, owner.current, radial_display) + chosen_clan = options[chosen_clan] + if(QDELETED(src) || QDELETED(owner.current)) + return FALSE + if(!chosen_clan) + to_chat(owner, span_announce("You choose to remain ignorant, for now.")) + return + my_clan = new chosen_clan(src) owner.announce_objectives() - if(bloodsucker_level_unspent >= 2) - to_chat(owner, span_announce("As a latejoiner, you have [bloodsucker_level_unspent] bonus Ranks, entering your claimed coffin allows you to spend a Rank.")) - owner.current.playsound_local(null, 'sound/ambience/antag/bloodsuckeralert.ogg', 100, FALSE, pressure_affected = FALSE) - antag_memory += "Although you were born a mortal, in undeath you earned the name [fullname].
" -/datum/antagonist/bloodsucker/farewell() - to_chat(owner.current, span_userdanger("With a snap, your curse has ended. You are no longer a Bloodsucker. You live once more!")) - // Refill with Blood so they don't instantly die. - if(ishuman(owner.current)) - var/mob/living/carbon/user = owner.current - if(!LAZYFIND(user.dna.species.species_traits, NOBLOOD)) - owner.current.blood_volume = max(owner.current.blood_volume, BLOOD_VOLUME_NORMAL(owner.current)) - -// Called when using admin tools to give antag status, admin spawned bloodsuckers don't get turned human if plasmaman. -/datum/antagonist/bloodsucker/admin_add(datum/mind/new_owner, mob/admin) - var/levels = input("How many unspent Ranks would you like [new_owner] to have?","Bloodsucker Rank", bloodsucker_level_unspent) as null | num - var/msg = " made [key_name_admin(new_owner)] into \a [name]" - if(levels > 1) - bloodsucker_level_unspent = levels - msg += " with [levels] extra unspent Ranks." - message_admins("[key_name_admin(usr)][msg]") - log_admin("[key_name(usr)][msg]") - new_owner.add_antag_datum(src) +//////////////////////////////////////////////////////////////////////////////////// +//-------------------------------Bloodsucker UI-----------------------------------// +//////////////////////////////////////////////////////////////////////////////////// /datum/antagonist/bloodsucker/ui_data(mob/user) var/list/data = list() @@ -328,26 +311,17 @@ data["clan"] += list(clan_data) - return data - -/datum/antagonist/bloodsucker/ui_static_data(mob/user) - var/list/data = list() //we don't need to update this that much. - for(var/datum/action/cooldown/bloodsucker/power as anything in powers) - var/list/power_data = list() - - power_data["power_name"] = power.name - power_data["power_explanation"] = power.power_explanation - power_data["power_icon"] = power.button_icon_state + // for(var/datum/action/cooldown/bloodsucker/power as anything in powers) + // var/list/power_data = list() - data["power"] += list(power_data) + // power_data["power_name"] = power.name + // power_data["power_explanation"] = power.power_explanation + // power_data["power_icon"] = power.button_icon_state - return data + ..() + // data["power"] += list(power_data) -/datum/antagonist/bloodsucker/ui_assets(mob/user) - return list( - get_asset_datum(/datum/asset/simple/bloodsucker_icons), - ) + return data /datum/antagonist/bloodsucker/ui_act(action, params, datum/tgui/ui) . = ..() @@ -360,11 +334,19 @@ ui?.send_full_update(force = TRUE) return +/datum/antagonist/bloodsucker/ui_assets(mob/user) + // return list( + // get_asset_datum(/datum/asset/simple/bloodsucker_icons), + // ) + +//////////////////////////////////////////////////////////////////////////////////// +//------------------------------Roundend report-----------------------------------// +//////////////////////////////////////////////////////////////////////////////////// /datum/antagonist/bloodsucker/roundend_report() var/list/report = list() // Vamp name - report += "
[span_header("\[[return_full_name(TRUE)]\]")]" + report += "
[span_header("\[name\]")]" report += printplayer(owner) if(my_clan) report += "They were part of the [my_clan.name]!" @@ -410,73 +392,6 @@ return report.Join("
") -/// Evaluates the conditions of the bloodsucker at the end of each round to pick a flavor message to add -/datum/antagonist/bloodsucker/proc/get_flavor(objectives_complete, optional_objectives_complete) - var/list/flavor = list() - var/flavor_message - var/alive = owner?.current?.stat != DEAD //Technically not necessary because of Final Death objective? - var/escaped = ((owner.current.onCentCom() || owner.current.onSyndieBase()) && alive) - flavor += "
Epilogue: " - var/message_color = "#ef2f3c" - //i used pick() in case anyone wants to add more messages as time goes on - if(objectives_complete && optional_objectives_complete && broke_masquerade && escaped) - //finish all objectives, break masquerade, evac - flavor_message += pick(list( - "What matters of the Masquerade to you? Let it crumble into dust as your tyranny whips forward to dine on more stations. \ - News of your butchering exploits will quickly spread, and you know what will encompass the minds of mortals and undead alike. Fear." - )) - message_color = "#008000" - else if(objectives_complete && optional_objectives_complete && broke_masquerade && alive) - //finish all objectives, break masquerade, don't evac - flavor_message += pick(list( - "Blood still pumps in your veins as you lay stranded on the station. No doubt the wake of chaos left in your path will attract danger, but greater power than you've ever felt courses through your body. \ - Let the Camarilla and the witchers come. You will be waiting." - )) - message_color = "#008000" - else if(objectives_complete && optional_objectives_complete && !broke_masquerade && escaped) - //finish all objectives, don't break masquerade, escape - flavor_message += pick(list( - "You step off the spacecraft with a mark of pride at a superbly completed mission. Upon arriving back at CentCom, an unassuming assistant palms you an invitation stamped with the Camarilla seal. \ - High society awaits: a delicacy you have earned." - )) - message_color = "#008000" - else if(objectives_complete && optional_objectives_complete && !broke_masquerade && alive) - //finish all objectives, don't break masquerade, don't escape - flavor_message += pick(list( - "This station has become your own slice of paradise. Your mission completed, you turn on the others who were stranded, ripe for your purposes. \ - Who knows? If they prove to elevate your power enough, perhaps a new bloodline might be founded here." - )) - message_color = "#008000" - else if(objectives_complete && !optional_objectives_complete && broke_masquerade && escaped) - //finish primary objectives only, break masquerade, escape - flavor_message += pick(list( - "Your mission accomplished, you step off the spacecraft, feeling the mark of exile on your neck. Your allies gone, your veins thrum with a singular purpose: survival." - )) - message_color = "#517fff" - else if(objectives_complete && !optional_objectives_complete && broke_masquerade && alive) - //finish primary objectives only, break masquerade, don't escape - flavor_message += pick(list( - "You survived, but you broke the Masquerade, your blood-stained presence clear and your power limited. No doubt death in the form of claw or stake hails its approach. Perhaps it's time to understand the cattles' fascinations with the suns." - )) - else if(objectives_complete && !optional_objectives_complete && !broke_masquerade && escaped) - //finish primary objectives only, don't break masquerade, escape - flavor_message += pick(list( - "A low profile has always suited you best, conspiring enough to satiate the clan and keep your head low. It's not luxorious living, though death is a less kind alternative. On to the next station." - )) - message_color = "#517fff" - else if(objectives_complete && !optional_objectives_complete && !broke_masquerade && alive) - //finish primary objectives only, don't break masquerade, don't escape - flavor_message += pick(list( - "You completed your mission and kept your identity free of heresy, though your mark here is not strong enough to lay a claim. Best stow away when the next shuttle comes around." - )) - message_color = "#517fff" - else - //perish or just fuck up and fail your primary objectives - flavor_message += pick(list( - "Thus ends the story of [return_full_name(TRUE)]. No doubt future generations will look back on your legacy and reflect on the lessons of the past. If they remember you at all." - )) - flavor += "[flavor_message]
" - return "
[flavor.Join("
")]
" /** * # Vampire Clan @@ -490,167 +405,29 @@ /datum/antagonist/bloodsucker/proc/sol_rank_up(atom/source) SIGNAL_HANDLER - INVOKE_ASYNC(src, PROC_REF(RankUp)) + //INVOKE_ASYNC(src, PROC_REF(RankUp)) ///Called when Sol is near starting. /datum/antagonist/bloodsucker/proc/sol_near_start(atom/source) SIGNAL_HANDLER - if(bloodsucker_lair_area && !(locate(/datum/action/cooldown/bloodsucker/gohome) in powers)) - BuyPower(new /datum/action/cooldown/bloodsucker/gohome) - if(my_clan?.get_clan() == CLAN_GANGREL && !(locate(/datum/action/cooldown/bloodsucker/gangrel/transform) in powers)) - BuyPower(new /datum/action/cooldown/bloodsucker/gangrel/transform) + // if(bloodsucker_lair_area && !(locate(/datum/action/cooldown/bloodsucker/gohome) in powers)) + // BuyPower(new /datum/action/cooldown/bloodsucker/gohome) + // if(my_clan?.get_clan() == CLAN_GANGREL && !(locate(/datum/action/cooldown/bloodsucker/gangrel/transform) in powers)) + // BuyPower(new /datum/action/cooldown/bloodsucker/gangrel/transform) ///Called when Sol first ends. /datum/antagonist/bloodsucker/proc/on_sol_end(atom/source) SIGNAL_HANDLER INVOKE_ASYNC(src, PROC_REF(check_end_torpor)) - for(var/datum/action/cooldown/bloodsucker/power in powers) - if(istype(power, /datum/action/cooldown/bloodsucker/gohome)) - RemovePower(power) - if(altar_uses) - to_chat(owner, span_boldnotice("Your Altar uses have been reset!")) - altar_uses = 0 - -/// Buying powers -/datum/antagonist/bloodsucker/proc/BuyPower(datum/action/cooldown/bloodsucker/power) - for(var/datum/action/cooldown/bloodsucker/current_powers as anything in powers) - if(current_powers.type == power.type) - return FALSE - powers += power - power.Grant(owner.current) - return TRUE - -/datum/antagonist/bloodsucker/proc/RemovePower(datum/action/cooldown/bloodsucker/power) - if(power.active) - power.DeactivatePower() - powers -= power - power.Remove(owner.current) + // for(var/datum/action/cooldown/bloodsucker/power in powers) + // if(istype(power, /datum/action/cooldown/bloodsucker/gohome)) + // RemovePower(power) -/datum/antagonist/bloodsucker/proc/give_starting_powers() - for(var/datum/action/cooldown/bloodsucker/all_powers as anything in all_bloodsucker_powers) - if(!(initial(all_powers.purchase_flags) & BLOODSUCKER_DEFAULT_POWER)) - continue - BuyPower(new all_powers) - -/datum/antagonist/bloodsucker/proc/assign_starting_stats() - // Traits: Species - var/mob/living/carbon/human/user = owner.current - if(ishuman(owner.current)) - var/datum/species/user_species = user.dna.species - user_species.species_traits += DRINKSBLOOD - user.dna?.remove_all_mutations() - user_species.punchdamagelow += 1 //lowest possible punch damage - 0 - user_species.punchdamagehigh += 1 //highest possible punch damage - 9 - user_species.punchstunchance += 1 //To not change rng knockdowns - /// Give Bloodsucker Traits - owner.current.add_traits(bloodsucker_traits, BLOODSUCKER_TRAIT) - /// No Skittish "People" allowed - if(HAS_TRAIT(owner.current, TRAIT_SKITTISH)) - REMOVE_TRAIT(owner.current, TRAIT_SKITTISH, ROUNDSTART_TRAIT) - // Tongue & Language - owner.current.grant_all_languages(FALSE, FALSE, TRUE) - owner.current.grant_language(/datum/language/vampiric) - /// Clear Disabilities & Organs - heal_vampire_organs() - -/** - * ##clear_power_and_stats() - * - * Removes all Bloodsucker related Powers/Stats changes, setting them back to pre-Bloodsucker - * Order of steps and reason why: - * Remove clan - Clans like Nosferatu give Powers on removal, we have to make sure this is given before removing Powers. - * Powers - Remove all Powers, so things like Masquerade are off. - * Species traits, Traits, MaxHealth, Language - Misc stuff, has no priority. - * Organs - At the bottom to ensure everything that changes them has reverted themselves already. - * Update Sight - Done after Eyes are regenerated. - */ -/datum/antagonist/bloodsucker/proc/clear_powers_and_stats() - // Remove clan first - if(my_clan) - QDEL_NULL(my_clan) - // Powers - for(var/datum/action/cooldown/bloodsucker/all_powers as anything in powers) - RemovePower(all_powers) - /// Stats - if(ishuman(owner.current)) - var/mob/living/carbon/human/human_user = owner.current - var/datum/species/user_species = human_user.dna.species - user_species.species_traits -= DRINKSBLOOD - // Clown - if(istype(human_user) && owner.assigned_role == "Clown") - human_user.dna.add_mutation(CLOWNMUT) - // Remove all bloodsucker traits - owner.current.remove_traits(bloodsucker_traits, BLOODSUCKER_TRAIT) - // Update Health - owner.current.setMaxHealth(initial(owner.current.maxHealth)) - // Language - owner.current.remove_language(/datum/language/vampiric) - // Heart & Eyes - var/mob/living/carbon/user = owner.current - var/obj/item/organ/heart/newheart = owner.current.getorganslot(ORGAN_SLOT_HEART) - if(newheart) - newheart.beating = initial(newheart.beating) - var/obj/item/organ/eyes/user_eyes = user.getorganslot(ORGAN_SLOT_EYES) - if(user_eyes) - user_eyes.flash_protect = initial(user_eyes.flash_protect) - user_eyes.sight_flags = initial(user_eyes.sight_flags) - user_eyes.color_cutoffs = initial(user_eyes.color_cutoffs) - user.update_sight() - -/datum/antagonist/bloodsucker/proc/give_masquerade_infraction() - if(broke_masquerade) - return - masquerade_infractions++ - if(masquerade_infractions >= 3) - break_masquerade() - else - to_chat(owner.current, span_cultbold("You violated the Masquerade! Break the Masquerade [3 - masquerade_infractions] more times and you will become a criminal to the Bloodsucker's Cause!")) + // if(altar_uses) + // to_chat(owner, span_boldnotice("Your Altar uses have been reset!")) + // altar_uses = 0 -/datum/antagonist/bloodsucker/proc/RankUp() - if(!owner || !owner.current || IS_FAVORITE_VASSAL(owner.current)) - return - bloodsucker_level_unspent++ - if(!my_clan) - to_chat(owner.current, span_notice("You have gained a rank. Join a Clan to spend it.")) - return - passive_blood_drain -= 0.03 * bloodsucker_level //do something. It's here because if you are gaining points through other means you are doing good - // Spend Rank Immediately? - if(!istype(owner.current.loc, /obj/structure/closet/crate/coffin)) - to_chat(owner, span_notice("You have grown more ancient! Sleep in a coffin that you have claimed to thicken your blood and become more powerful.")) - if(bloodsucker_level_unspent >= 2) - to_chat(owner, span_announce("Bloodsucker Tip: If you cannot find or steal a coffin to use, you can build one from wood or metal.")) - return - SpendRank() - -/datum/antagonist/bloodsucker/proc/RankDown() - bloodsucker_level_unspent-- - -/datum/antagonist/bloodsucker/proc/remove_nondefault_powers(return_levels = FALSE) - for(var/datum/action/cooldown/bloodsucker/power as anything in powers) - if(istype(power, /datum/action/cooldown/bloodsucker/feed) || istype(power, /datum/action/cooldown/bloodsucker/masquerade) || istype(power, /datum/action/cooldown/bloodsucker/veil)) - continue - RemovePower(power) - if(return_levels) - bloodsucker_level_unspent++ - -/datum/antagonist/bloodsucker/proc/LevelUpPowers() - for(var/datum/action/cooldown/bloodsucker/power as anything in powers) - power.level_current++ - power.UpdateDesc() - -///Disables all powers, accounting for torpor -/datum/antagonist/bloodsucker/proc/DisableAllPowers(forced = FALSE) - for(var/datum/action/cooldown/bloodsucker/power as anything in powers) - if(forced || ((power.check_flags & BP_CANT_USE_IN_TORPOR) && HAS_TRAIT(owner.current, TRAIT_NODEATH))) - if(power.active) - power.DeactivatePower() - -/datum/antagonist/bloodsucker/proc/SpendRank(mob/living/carbon/human/target, cost_rank = TRUE, blood_cost, ask = TRUE) - if(!owner || !owner.current || !owner.current.client || (cost_rank && bloodsucker_level_unspent <= 0.5)) - return - SEND_SIGNAL(src, BLOODSUCKER_RANK_UP, target, cost_rank, blood_cost, ask) - //////////////////////////////////////////////////////////////////////////////////////////////// /datum/antagonist/bloodsucker/proc/forge_bloodsucker_objectives() @@ -670,7 +447,7 @@ /// Name shown on antag list /datum/antagonist/bloodsucker/antag_listing_name() - return ..() + "([return_full_name(TRUE)])" + return ..() + "(name)" /// Whatever interesting things happened to the antag admins should know about /// Include additional information about antag in this part @@ -679,121 +456,6 @@ return "Final Death" return ..() -/* - * # Bloodsucker Names - * - * All Bloodsuckers get a name, and gets a better one when they hit Rank 4. - */ - -/// Names -/datum/antagonist/bloodsucker/proc/SelectFirstName() - if(owner.current.gender == MALE) - bloodsucker_name = pick( - "Desmond","Rudolph","Dracula","Vlad","Pyotr","Gregor", - "Cristian","Christoff","Marcu","Andrei","Constantin", - "Gheorghe","Grigore","Ilie","Iacob","Luca","Mihail","Pavel", - "Vasile","Octavian","Sorin","Sveyn","Aurel","Alexe","Iustin", - "Theodor","Dimitrie","Octav","Damien","Magnus","Caine","Abel", // Romanian/Ancient - "Lucius","Gaius","Otho","Balbinus","Arcadius","Romanos","Alexios","Vitellius", // Latin - "Melanthus","Teuthras","Orchamus","Amyntor","Axion", // Greek - "Thoth","Thutmose","Osorkon,","Nofret","Minmotu","Khafra", // Egyptian - "Dio", - ) - else - bloodsucker_name = pick( - "Islana","Tyrra","Greganna","Pytra","Hilda", - "Andra","Crina","Viorela","Viorica","Anemona", - "Camelia","Narcisa","Sorina","Alessia","Sophia", - "Gladda","Arcana","Morgan","Lasarra","Ioana","Elena", - "Alina","Rodica","Teodora","Denisa","Mihaela", - "Svetla","Stefania","Diyana","Kelssa","Lilith", // Romanian/Ancient - "Alexia","Athanasia","Callista","Karena","Nephele","Scylla","Ursa", // Latin - "Alcestis","Damaris","Elisavet","Khthonia","Teodora", // Greek - "Nefret","Ankhesenpep", // Egyptian - ) - -/datum/antagonist/bloodsucker/proc/SelectTitle(am_fledgling = 0, forced = FALSE) - // Already have Title - if(!forced && bloodsucker_title != null) - return - // Titles [Master] - if(!am_fledgling) - if(owner.current.gender == MALE) - bloodsucker_title = pick ("Count","Baron","Viscount","Prince","Duke","Tzar","Dreadlord","Lord","Master") - else - bloodsucker_title = pick ("Countess","Baroness","Viscountess","Princess","Duchess","Tzarina","Dreadlady","Lady","Mistress") - to_chat(owner, span_announce("You have earned a title! You are now known as [return_full_name(TRUE)]!")) - // Titles [Fledgling] - else - bloodsucker_title = null - -/datum/antagonist/bloodsucker/proc/SelectReputation(am_fledgling = FALSE, forced = FALSE) - // Already have Reputation - if(!forced && bloodsucker_reputation != null) - return - - if(am_fledgling) - bloodsucker_reputation = pick( - "Crude","Callow","Unlearned","Neophyte","Novice","Unseasoned", - "Fledgling","Young","Neonate","Scrapling","Untested","Unproven", - "Unknown","Newly Risen","Born","Scavenger","Unknowing","Unspoiled", - "Disgraced","Defrocked","Shamed","Meek","Timid","Broken","Fresh", - ) - else if(owner.current.gender == MALE && prob(10)) - bloodsucker_reputation = pick("King of the Damned", "Blood King", "Emperor of Blades", "Sinlord", "God-King") - else if(owner.current.gender == FEMALE && prob(10)) - bloodsucker_reputation = pick("Queen of the Damned", "Blood Queen", "Empress of Blades", "Sinlady", "God-Queen") - else - bloodsucker_reputation = pick( - "Butcher","Blood Fiend","Crimson","Red","Black","Terror", - "Nightman","Feared","Ravenous","Fiend","Malevolent","Wicked", - "Ancient","Plaguebringer","Sinister","Forgotten","Wretched","Baleful", - "Inqisitor","Harvester","Reviled","Robust","Betrayer","Destructor", - "Damned","Accursed","Terrible","Vicious","Profane","Vile", - "Depraved","Foul","Slayer","Manslayer","Sovereign","Slaughterer", - "Forsaken","Mad","Dragon","Savage","Villainous","Nefarious", - "Inquisitor","Marauder","Horrible","Immortal","Undying","Overlord", - "Corrupt","Hellspawn","Tyrant","Sanguineous", - ) - - to_chat(owner, span_announce("You have earned a reputation! You are now known as [return_full_name(TRUE)]!")) - - -/datum/antagonist/bloodsucker/proc/AmFledgling() - return !bloodsucker_title - -/datum/antagonist/bloodsucker/proc/return_full_name(include_rep = FALSE) - - var/fullname - // Name First - fullname = (bloodsucker_name ? bloodsucker_name : owner.current.name) - // Title - if(bloodsucker_title) - fullname = bloodsucker_title + " " + fullname - // Rep - if(include_rep && bloodsucker_reputation) - fullname = fullname + " the " + bloodsucker_reputation - - return fullname - -///When a Bloodsucker breaks the Masquerade, they get their HUD icon changed. -/datum/antagonist/bloodsucker/proc/break_masquerade() - if(broke_masquerade) - return - owner.current.playsound_local(null, 'sound/effects/lunge_warn.ogg', 100, FALSE, pressure_affected = FALSE) - to_chat(owner.current, span_cultboldtalic("You have broken the Masquerade!")) - to_chat(owner.current, span_warning("Bloodsucker Tip: When you break the Masquerade, you become open for termination by fellow Bloodsuckers, and your Vassals are no longer completely loyal to you, as other Bloodsuckers can steal them for themselves!")) - broke_masquerade = TRUE - antag_hud_name = "masquerade_broken" - add_team_hud(owner.current) - SEND_GLOBAL_SIGNAL(COMSIG_BLOODSUCKER_BROKE_MASQUERADE) - -///This is admin-only of reverting a broken masquerade. -/datum/antagonist/bloodsucker/proc/fix_masquerade() - if(!broke_masquerade) - return - to_chat(owner.current, span_cultboldtalic("You have re-entered the Masquerade.")) - broke_masquerade = FALSE /datum/antagonist/bloodsucker/get_preview_icon() var/icon/final_icon = render_preview_outfit(/datum/outfit/bloodsucker_outfit) @@ -812,3 +474,20 @@ enrico.update_body() enrico.update_hair() + + +// /datum/asset/simple/bloodsucker_icons + +// /datum/asset/simple/bloodsucker_icons/register() +// for(var/datum/bloodsucker_clan/clans as anything in typesof(/datum/bloodsucker_clan)) +// if(!initial(clans.joinable_clan)) +// continue +// add_bloodsucker_icon(initial(clans.join_icon), initial(clans.join_icon_state)) + +// for(var/datum/action/cooldown/bloodsucker/power as anything in subtypesof(/datum/action/cooldown/bloodsucker)) +// add_bloodsucker_icon(initial(power.button_icon), initial(power.button_icon_state)) + +// return ..() + +// /datum/asset/simple/bloodsucker_icons/proc/add_bloodsucker_icon(bloodsucker_icon, bloodsucker_icon_state) +// assets[sanitize_filename("bloodsucker.[bloodsucker_icon_state].png")] = icon(bloodsucker_icon, bloodsucker_icon_state) diff --git a/code/modules/antagonists/bloodsuckers/bloodsuckers_objects.dm b/code/modules/antagonists/bloodsuckers/bloodsuckers_objects.dm index fdae977124e4..a05f98e8ce72 100644 --- a/code/modules/antagonists/bloodsuckers/bloodsuckers_objects.dm +++ b/code/modules/antagonists/bloodsuckers/bloodsuckers_objects.dm @@ -352,14 +352,8 @@ in_use = FALSE var/datum/antagonist/bloodsucker/bloodsuckerdatum = IS_BLOODSUCKER(target) // Are we a Bloodsucker | Are we on Masquerade. If one is true, they will fail. - if(IS_BLOODSUCKER(target) && !HAS_TRAIT(target, TRAIT_MASQUERADE)) - if(bloodsuckerdatum.broke_masquerade) - to_chat(user, span_warning("[target], also known as '[bloodsuckerdatum.return_full_name()]', is indeed a Bloodsucker, but you already knew this.")) - return + if(IS_BLOODSUCKER(target)) to_chat(user, span_warning("[target], also known as '[bloodsuckerdatum.return_full_name()]', [bloodsuckerdatum.my_clan ? "is part of the [bloodsuckerdatum.my_clan]!" : "is not part of a clan."] You quickly note this information down, memorizing it.")) - bloodsuckerdatum.break_masquerade() - else - to_chat(user, span_notice("You fail to draw any conclusions to [target] being a Bloodsucker.")) /obj/item/book/kindred/attack_self(mob/living/user) ui_interact(user) diff --git a/code/modules/antagonists/bloodsuckers/clans/_clan.dm b/code/modules/antagonists/bloodsuckers/clans/_clan.dm index 0ffafd146f6f..0de18176e16c 100644 --- a/code/modules/antagonists/bloodsuckers/clans/_clan.dm +++ b/code/modules/antagonists/bloodsuckers/clans/_clan.dm @@ -8,66 +8,73 @@ ///The bloodsucker datum that owns this clan. Use this over 'source', because while it's the same thing, this is more consistent (and used for deletion). var/datum/antagonist/bloodsucker/bloodsuckerdatum ///The name of the clan we're in. - var/name = CLAN_NONE + var/name = CLAN_CAITIFF ///Description of what the clan is, given when joining and through your antag UI. var/description = "The Caitiff is as basic as you can get with Bloodsuckers. \n\ Entirely Clan-less, they are blissfully unaware of who they really are. \n\ No additional abilities is gained, nothing is lost, if you want a plain Bloodsucker, this is it. \n\ The Favorite Vassal will gain the Brawn ability, to help in combat." - ///The clan objective that is required to greentext. - var/datum/objective/clan_objective + ///Whether the clan can be joined by players. FALSE for flavortext-only clans. + var/joinable_clan = TRUE + ///Description shown when trying to join the clan. + var/join_description = "The default, Classic Bloodsucker." + ///The icon of the radial icon to join this clan. var/join_icon = 'icons/mob/bloodsucker_clan_icons.dmi' ///Same as join_icon, but the state var/join_icon_state = "caitiff" - ///Description shown when trying to join the clan. - var/join_description = "The default, Classic Bloodsucker." - ///Whether the clan can be joined by players. FALSE for flavortext-only clans. - var/joinable_clan = TRUE - ///How the Bloodsucker ranks up, if they do. - var/rank_up_type = BLOODSUCKER_RANK_UP_NORMAL - ///Whether they become entirely stun immune when entering Frenzy. - var/frenzy_stun_immune = FALSE - ///The clan's manipulation specialty. + var/max_vassals = 1 + + ///The clan objective that is required to greentext. + var/datum/objective/clan_objective + + ///The clan's manipulation specialty. for the sake of crafting var/control_type = BLOODSUCKER_CONTROL_BLOOD ///How we will drink blood using Feed. var/blood_drink_type = BLOODSUCKER_DRINK_NORMAL + ///Abilities our clan will start with. Granted to the owning bloodsucker on initialization + var/list/datum/bloodsucker_power/starting_abilities = list() + ///Abilities the bloodsucker has learned + var/list/datum/bloodsucker_power/learned_abilities = list() + +////////////////////////////////////////////////////////////////////////// +//------------------------New clan is created---------------------------// +////////////////////////////////////////////////////////////////////////// /datum/bloodsucker_clan/New(datum/antagonist/bloodsucker/owner_datum) . = ..() src.bloodsuckerdatum = owner_datum RegisterSignal(bloodsuckerdatum, COMSIG_BLOODSUCKER_ON_LIFETICK, PROC_REF(handle_clan_life)) - RegisterSignal(bloodsuckerdatum, BLOODSUCKER_RANK_UP, PROC_REF(on_spend_rank)) + RegisterSignal(bloodsuckerdatum, BLOODSUCKER_TRIGGER_FRENZY, PROC_REF(handle_clan_frenzy)) + //RegisterSignal(bloodsuckerdatum, BLOODSUCKER_RANK_UP, PROC_REF(on_spend_rank)) - RegisterSignal(bloodsuckerdatum, BLOODSUCKER_PRE_MAKE_FAVORITE, PROC_REF(on_offer_favorite)) - RegisterSignal(bloodsuckerdatum, BLOODSUCKER_MAKE_FAVORITE, PROC_REF(on_favorite_vassal)) + // RegisterSignal(bloodsuckerdatum, BLOODSUCKER_PRE_MAKE_FAVORITE, PROC_REF(on_offer_favorite)) + // RegisterSignal(bloodsuckerdatum, BLOODSUCKER_MAKE_FAVORITE, PROC_REF(on_favorite_vassal)) RegisterSignal(bloodsuckerdatum, BLOODSUCKER_MADE_VASSAL, PROC_REF(on_vassal_made)) - RegisterSignal(bloodsuckerdatum, BLOODSUCKER_EXIT_TORPOR, PROC_REF(on_exit_torpor)) RegisterSignal(bloodsuckerdatum, BLOODSUCKER_FINAL_DEATH, PROC_REF(on_final_death)) give_clan_objective() +////////////////////////////////////////////////////////////////////////// +//---------------------------Clan is destroyed--------------------------// +////////////////////////////////////////////////////////////////////////// /datum/bloodsucker_clan/Destroy(force) UnregisterSignal(bloodsuckerdatum, list( COMSIG_BLOODSUCKER_ON_LIFETICK, + BLOODSUCKER_TRIGGER_FRENZY, BLOODSUCKER_RANK_UP, - BLOODSUCKER_PRE_MAKE_FAVORITE, - BLOODSUCKER_MAKE_FAVORITE, + // BLOODSUCKER_PRE_MAKE_FAVORITE, + // BLOODSUCKER_MAKE_FAVORITE, BLOODSUCKER_MADE_VASSAL, - BLOODSUCKER_EXIT_TORPOR, BLOODSUCKER_FINAL_DEATH, )) remove_clan_objective() bloodsuckerdatum = null return ..() -///legacy code support -/datum/bloodsucker_clan/proc/get_clan() - return name - /datum/bloodsucker_clan/proc/give_clan_objective() if(isnull(clan_objective)) return @@ -82,13 +89,43 @@ QDEL_NULL(clan_objective) bloodsuckerdatum.owner.announce_objectives() -/** - * Called when a Bloodsucker exits Torpor - * args: - * source - the Bloodsucker exiting Torpor - */ -/datum/bloodsucker_clan/proc/on_exit_torpor(datum/antagonist/bloodsucker/source) - SIGNAL_HANDLER + +////////////////////////////////////////////////////////////////////////// +//----------------------------Ability handler---------------------------// +////////////////////////////////////////////////////////////////////////// +/datum/bloodsucker_clan/proc/get_purchasable_abilities() + var/list/datum/bloodsucker_power/available_abilities = list() + for(var/datum/bloodsucker_power/ability as anything in GLOB.all_bloodsucker_powers) + if(locate(ability.type) in learned_abilities) + continue + if(!(name in ability.clans_purchasable)) + continue + available_abilities += ability + return available_abilities + +/datum/bloodsucker_clan/proc/gain_power(atom/source, datum/bloodsucker_power/power_typepath, silent = FALSE) + var/owner = bloodsuckerdatum.owner + if(!ispath(power_typepath)) + CRASH("[owner] tried to gain [power_typepath] which is not a valid darkspawn ability") + if(locate(power_typepath) in learned_abilities) + return + + var/datum/bloodsucker_power/new_power = new power_typepath() + if((name in new_power.clans_purchasable) && new_power.on_purchase(owner, silent)) + learned_abilities += new_power + else + qdel(new_power) + +/datum/bloodsucker_clan/proc/full_refund() + for(var/i in learned_abilities) + lose_power(i, TRUE) + +/datum/bloodsucker_clan/proc/lose_power(datum/bloodsucker_power/power, refund = FALSE) + if(!locate(power) in learned_abilities) + CRASH("[bloodsuckerdatum.owner] tried to lose [power] which they haven't learned") + + learned_abilities -= power + power.remove(refund) /** @@ -108,103 +145,27 @@ /datum/bloodsucker_clan/proc/handle_clan_life(datum/antagonist/bloodsucker/source) SIGNAL_HANDLER + /** - * Called when a Bloodsucker successfully Vassalizes someone. + * Called during Bloodsucker's LifeTick * args: * bloodsuckerdatum - the antagonist datum of the Bloodsucker running this. */ -/datum/bloodsucker_clan/proc/on_vassal_made(datum/antagonist/bloodsucker/source, mob/living/user, mob/living/target) +/datum/bloodsucker_clan/proc/handle_clan_frenzy(datum/antagonist/bloodsucker/source) SIGNAL_HANDLER - user.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE) - target.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, TRUE) - target.do_jitter_animation(15) - INVOKE_ASYNC(target, TYPE_PROC_REF(/mob, emote), "laugh") /** - * Called when a Bloodsucker successfully starts spending their Rank + * Called when a Bloodsucker successfully Vassalizes someone. * args: * bloodsuckerdatum - the antagonist datum of the Bloodsucker running this. - * target - The Vassal (if any) we are upgrading. - * cost_rank - TRUE/FALSE on whether this will cost us a rank when we go through with it. - * blood_cost - A number saying how much it costs to rank up - * ask - If they want to automatically spend the rest of their ranks */ -/datum/bloodsucker_clan/proc/on_spend_rank(datum/antagonist/bloodsucker/source, mob/living/carbon/target, cost_rank = TRUE, blood_cost, ask) +/datum/bloodsucker_clan/proc/on_vassal_made(datum/antagonist/bloodsucker/source, mob/living/user, mob/living/target) SIGNAL_HANDLER + user.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE) + target.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, TRUE) + target.do_jitter_animation(15) + INVOKE_ASYNC(target, TYPE_PROC_REF(/mob, emote), "laugh") - INVOKE_ASYNC(src, PROC_REF(spend_rank), bloodsuckerdatum, cost_rank, blood_cost, ask) - -/datum/bloodsucker_clan/proc/spend_rank(datum/antagonist/bloodsucker/source, cost_rank = TRUE, blood_cost, ask) - // Purchase Power Prompt - var/list/options = list() - for(var/datum/action/cooldown/bloodsucker/power as anything in bloodsuckerdatum.all_bloodsucker_powers) - if(initial(power.purchase_flags) & BLOODSUCKER_CAN_BUY && !(locate(power) in bloodsuckerdatum.powers)) - options[initial(power.name)] = power - - if(!LAZYLEN(options)) - to_chat(bloodsuckerdatum.owner.current, span_notice("You grow more ancient by the night!")) - else - // Give them the UI to purchase a power. - var/choice = tgui_input_list(bloodsuckerdatum.owner.current, "You have the opportunity to grow more ancient. Select a power to advance your Rank.", "Your Blood Thickens...", options) - // Prevent Bloodsuckers from closing/reopning their coffin to spam Levels. - if(cost_rank && bloodsuckerdatum.bloodsucker_level_unspent <= 0) - return - // Did you choose a power? - if(!choice || !options[choice]) - to_chat(bloodsuckerdatum.owner.current, span_notice("You prevent your blood from thickening just yet, but you may try again later.")) - return - // Prevent Bloodsuckers from closing/reopning their coffin to spam Levels. - if(locate(options[choice]) in bloodsuckerdatum.powers) - to_chat(bloodsuckerdatum.owner.current, span_notice("You prevent your blood from thickening just yet, but you may try again later.")) - return - // Prevent Bloodsuckers from purchasing a power while outside of their Coffin. - if(!istype(bloodsuckerdatum.owner.current.loc, /obj/structure/closet/crate/coffin)) - to_chat(bloodsuckerdatum.owner.current, span_warning("You must be in your Coffin to purchase Powers.")) - return - - // Good to go - Buy Power! - var/datum/action/cooldown/bloodsucker/purchased_power = options[choice] - bloodsuckerdatum.BuyPower(new purchased_power) - bloodsuckerdatum.owner.current.balloon_alert(bloodsuckerdatum.owner.current, "learned [choice]!") - to_chat(bloodsuckerdatum.owner.current, span_notice("You have learned how to use [choice]!")) - - finalize_spend_rank(bloodsuckerdatum, cost_rank, blood_cost, ask) - -/datum/bloodsucker_clan/proc/finalize_spend_rank(datum/antagonist/bloodsucker/source, cost_rank = TRUE, blood_cost, ask) - bloodsuckerdatum.LevelUpPowers() - bloodsuckerdatum.bloodsucker_regen_rate += 0.05 - bloodsuckerdatum.max_blood_volume += 100 - - // Misc. Stats Upgrades - if(ishuman(bloodsuckerdatum.owner.current)) - var/mob/living/carbon/human/user = bloodsuckerdatum.owner.current - var/datum/species/user_species = user.dna.species - user_species.punchdamagelow += 0.5 - // This affects the hitting power of Brawn. - user_species.punchdamagehigh += 0.5 - - // We're almost done - Spend your Rank now. - bloodsuckerdatum.bloodsucker_level++ - if(cost_rank) - bloodsuckerdatum.bloodsucker_level_unspent-- - if(blood_cost) - bloodsuckerdatum.AddBloodVolume(-blood_cost) - - // Ranked up enough to get your true Reputation? - if(bloodsuckerdatum.bloodsucker_level == 4) - bloodsuckerdatum.SelectReputation(am_fledgling = FALSE, forced = TRUE) - - to_chat(bloodsuckerdatum.owner.current, span_notice("You are now a rank [bloodsuckerdatum.bloodsucker_level] Bloodsucker. \ - Your strength, health, feed rate, regen rate, and maximum blood capacity have all increased! \n\ - * Your existing powers have all ranked up as well!")) - if(ask) //please no - bloodsuckerdatum.owner.current.playsound_local(null, 'sound/effects/pope_entry.ogg', 25, TRUE, pressure_affected = FALSE) - bloodsuckerdatum.update_hud() - if(bloodsuckerdatum.bloodsucker_level_unspent && cost_rank) - if(ask) - if(tgui_alert(bloodsuckerdatum.owner.current, "You have leftover ranks, do you want to spend them all?", "Time Management Team", list("Yes", "No")) == "No") - return - spend_rank(bloodsuckerdatum, cost_rank, blood_cost, FALSE) /** * Called when we are trying to turn someone into a Favorite Vassal @@ -212,50 +173,50 @@ * bloodsuckerdatum - the antagonist datum of the Bloodsucker performing this. * vassaldatum - the antagonist datum of the Vassal being offered up. */ -/datum/bloodsucker_clan/proc/on_offer_favorite(datum/antagonist/bloodsucker/source, datum/antagonist/vassal/vassaldatum) - SIGNAL_HANDLER - - INVOKE_ASYNC(src, PROC_REF(offer_favorite), bloodsuckerdatum, vassaldatum) - -/datum/bloodsucker_clan/proc/offer_favorite(datum/antagonist/bloodsucker/source, datum/antagonist/vassal/vassaldatum) - if(vassaldatum.special_type) - to_chat(bloodsuckerdatum.owner.current, span_notice("This Vassal was already assigned a special position.")) - return FALSE - if(!vassaldatum.owner.can_make_special(creator = bloodsuckerdatum.owner)) - to_chat(bloodsuckerdatum.owner.current, span_notice("This Vassal is unable to gain a Special rank due to innate features.")) - return FALSE - - var/list/options = list() - var/list/radial_display = list() - for(var/datum/antagonist/vassal/vassaldatums as anything in subtypesof(/datum/antagonist/vassal)) - if(bloodsuckerdatum.special_vassals[initial(vassaldatums.special_type)]) - continue - options[initial(vassaldatums.name)] = vassaldatums - - var/datum/radial_menu_choice/option = new - option.image = image(icon = initial(vassaldatums.hud_icon), icon_state = initial(vassaldatums.antag_hud_name)) - option.info = "[initial(vassaldatums.name)] - [span_boldnotice(initial(vassaldatums.vassal_description))]" - radial_display[initial(vassaldatums.name)] = option - - if(!options.len) - return - - to_chat(bloodsuckerdatum.owner.current, span_notice("You can change who this Vassal is, who are they to you?")) - var/vassal_response = show_radial_menu(bloodsuckerdatum.owner.current, vassaldatum.owner.current, radial_display) - if(!vassal_response) - return - vassal_response = options[vassal_response] - if(QDELETED(src) || QDELETED(bloodsuckerdatum.owner.current) || QDELETED(vassaldatum.owner.current)) - return FALSE - vassaldatum.make_special(vassal_response) - bloodsuckerdatum.bloodsucker_blood_volume -= 150 - -/** - * Called when we are successfully turn a Vassal into a Favorite Vassal - * args: - * bloodsuckerdatum - antagonist datum of the Bloodsucker who turned them into a Vassal. - * vassaldatum - the antagonist datum of the Vassal being offered up. - */ -/datum/bloodsucker_clan/proc/on_favorite_vassal(datum/antagonist/bloodsucker/source, datum/antagonist/vassal/vassaldatum) - SIGNAL_HANDLER - vassaldatum.BuyPower(new /datum/action/cooldown/bloodsucker/targeted/brawn) +// /datum/bloodsucker_clan/proc/on_offer_favorite(datum/antagonist/bloodsucker/source, datum/antagonist/vassal/vassaldatum) +// SIGNAL_HANDLER + +// INVOKE_ASYNC(src, PROC_REF(offer_favorite), bloodsuckerdatum, vassaldatum) + +// /datum/bloodsucker_clan/proc/offer_favorite(datum/antagonist/bloodsucker/source, datum/antagonist/vassal/vassaldatum) +// if(vassaldatum.special_type) +// to_chat(bloodsuckerdatum.owner.current, span_notice("This Vassal was already assigned a special position.")) +// return FALSE +// if(!vassaldatum.owner.can_make_special(creator = bloodsuckerdatum.owner)) +// to_chat(bloodsuckerdatum.owner.current, span_notice("This Vassal is unable to gain a Special rank due to innate features.")) +// return FALSE + +// var/list/options = list() +// var/list/radial_display = list() +// for(var/datum/antagonist/vassal/vassaldatums as anything in subtypesof(/datum/antagonist/vassal)) +// if(bloodsuckerdatum.special_vassals[initial(vassaldatums.special_type)]) +// continue +// options[initial(vassaldatums.name)] = vassaldatums + +// var/datum/radial_menu_choice/option = new +// option.image = image(icon = initial(vassaldatums.hud_icon), icon_state = initial(vassaldatums.antag_hud_name)) +// option.info = "[initial(vassaldatums.name)] - [span_boldnotice(initial(vassaldatums.vassal_description))]" +// radial_display[initial(vassaldatums.name)] = option + +// if(!options.len) +// return + +// to_chat(bloodsuckerdatum.owner.current, span_notice("You can change who this Vassal is, who are they to you?")) +// var/vassal_response = show_radial_menu(bloodsuckerdatum.owner.current, vassaldatum.owner.current, radial_display) +// if(!vassal_response) +// return +// vassal_response = options[vassal_response] +// if(QDELETED(src) || QDELETED(bloodsuckerdatum.owner.current) || QDELETED(vassaldatum.owner.current)) +// return FALSE +// vassaldatum.make_special(vassal_response) +// bloodsuckerdatum.bloodsucker_blood_volume -= 150 + +// /** +// * Called when we are successfully turn a Vassal into a Favorite Vassal +// * args: +// * bloodsuckerdatum - antagonist datum of the Bloodsucker who turned them into a Vassal. +// * vassaldatum - the antagonist datum of the Vassal being offered up. +// */ +// /datum/bloodsucker_clan/proc/on_favorite_vassal(datum/antagonist/bloodsucker/source, datum/antagonist/vassal/vassaldatum) +// SIGNAL_HANDLER +// //vassaldatum.BuyPower(new /datum/action/cooldown/bloodsucker/targeted/brawn) diff --git a/code/modules/antagonists/bloodsuckers/clans/clan_flavortext.dm b/code/modules/antagonists/bloodsuckers/clans/clan_flavortext.dm deleted file mode 100644 index 729efcbe9a1b..000000000000 --- a/code/modules/antagonists/bloodsuckers/clans/clan_flavortext.dm +++ /dev/null @@ -1,102 +0,0 @@ -/datum/bloodsucker_clan/ventrue - name = CLAN_VENTRUE - description = "The Ventrue Clan is extremely snobby with their meals, and refuse to drink blood from people without a mind. \n\ - There is additionally no way to rank themselves up, instead will have to rank their Favorite vassal through a Persuasion Rack. \n\ - The Favorite Vassal will slowly turn into a Bloodsucker this way, until they finally lose their last bits of Humanity." - joinable_clan = FALSE - clan_objective = /datum/objective/ventrue_clan_objective - join_icon_state = "ventrue" - join_description = "Lose the ability to drink from mindless mobs, can't level up or gain new powers, \ - instead you raise a vassal into a Bloodsucker." -// rank_up_type = BLOODSUCKER_RANK_UP_VASSAL - blood_drink_type = BLOODSUCKER_DRINK_SNOBBY - -/** - * Clan Objective - * Ventrue's Clan objective is to upgrade the Favorite Vassal - * enough to make them a Bloodsucker. - */ -/datum/objective/ventrue_clan_objective - name = "embrace" - explanation_text = "Use the Candelabrum to Rank your Favorite Vassal up enough to become a Bloodsucker and keep them alive until the end." - martyr_compatible = TRUE - -/datum/objective/ventrue_clan_objective/check_completion() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.current.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(!bloodsuckerdatum) - return FALSE - for(var/datum/antagonist/vassal/vassaldatum as anything in bloodsuckerdatum.vassals) - if(!vassaldatum.owner || !vassaldatum.owner.current) - continue - if(IS_FAVORITE_VASSAL(vassaldatum.owner.current) && vassaldatum.owner.has_antag_datum(/datum/antagonist/bloodsucker)) - return TRUE - return FALSE - -/datum/bloodsucker_clan/tremere - name = CLAN_TREMERE - description = "The Tremere Clan is extremely weak to True Faith, and will burn when entering areas considered such, like the Chapel. \n\ - Additionally, a whole new moveset is learned, built on Blood magic rather than Blood abilities, which are upgraded overtime. \n\ - More ranks can be gained by Vassalizing crewmembers. \n\ - The Favorite Vassal gains the Batform spell, being able to morph themselves at will." - joinable_clan = FALSE -// clan_objective = /datum/objective/tremere_clan_objective - join_icon_state = "tremere" - join_description = "You will burn if you enter the Chapel, lose all default powers, \ - but gain Blood Magic instead, powers you level up overtime." - -/** - * Clan Objective - * Tremere's Clan objective is to upgrade a power to max - * clan not implemented yet -/datum/objective/tremere_clan_objective - name = "tremerepower" - explanation_text = "Upgrade a Blood Magic power to the maximum level, remember that Vassalizing gives more Ranks!" - -/datum/objective/tremere_clan_objective/check_completion() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.has_antag_datum(/datum/antagonist/bloodsucker) - if(!bloodsuckerdatum) - return FALSE - for(var/datum/action/cooldown/bloodsucker/targeted/tremere/tremere_powers in bloodsuckerdatum.powers) - if(tremere_powers.level_current >= 5) - return TRUE - return FALSE -*/ - -/datum/bloodsucker_clan/nosferatu - name = CLAN_NOSFERATU - description = "The Nosferatu Clan is unable to blend in with the crew, with no abilities such as Masquerade and Veil. \n\ - Additionally, has a permanent bad back and looks like a Bloodsucker upon a simple examine, and is entirely unidentifiable, \n\ - they can fit in the vents regardless of their form and equipment. \n\ - The Favorite Vassal is permanetly disfigured, and can also ventcrawl, but only while entirely nude." - joinable_clan = FALSE - clan_objective = /datum/objective/nosferatu_clan_objective - join_icon_state = "nosferatu" - join_description = "You are permanetly disfigured, look like a Bloodsucker to all who examine you, \ - lose your Masquerade ability, but gain the ability to Ventcrawl even while clothed." - blood_drink_type = BLOODSUCKER_DRINK_INHUMANELY - -/** - * Clan objective - * Nosferatu's objective is to steal the Curator's Archives of the Kindred. - */ -/datum/objective/nosferatu_clan_objective - name = "steal kindred" - explanation_text = "Ensure Nosferatu steals and keeps control over the Archive of the Kindred." - -/datum/objective/nosferatu_clan_objective/check_completion() - for(var/datum/mind/bloodsucker_minds as anything in get_antag_minds(/datum/antagonist/bloodsucker)) - var/obj/item/book/kindred/the_book = locate() in bloodsucker_minds.current.get_all_contents() - if(the_book) - return TRUE - return FALSE - -/datum/bloodsucker_clan/malkavian - name = CLAN_MALKAVIAN - description = "Little is documented about Malkavians. Complete insanity is the most common theme. \n\ - The Favorite Vassal will suffer the same fate as the Master." - join_icon_state = "malkavian" - join_description = "Completely insane. You gain constant hallucinations, become a prophet with unintelligable rambling, \ - and become the enforcer of the Masquerade code." - joinable_clan = FALSE - frenzy_stun_immune = TRUE - blood_drink_type = BLOODSUCKER_DRINK_INHUMANELY diff --git a/code/modules/antagonists/bloodsuckers/clans/clan_gangrel.dm b/code/modules/antagonists/bloodsuckers/clans/clan_gangrel.dm deleted file mode 100644 index c192266b7164..000000000000 --- a/code/modules/antagonists/bloodsuckers/clans/clan_gangrel.dm +++ /dev/null @@ -1,43 +0,0 @@ -/datum/bloodsucker_clan/gangrel - name = CLAN_GANGREL - description = "Closer to Animals than Bloodsuckers, known as Werewolves waiting to happen, \n\ - these are the most fearful of True Faith, being the most lethal thing they would ever see the night of. \n\ - Full Moons do not seem to have an effect, despite common-told stories. \n\ - The Favorite Vassal turns into a Werewolf whenever their Master does." - clan_objective = /datum/objective/gangrel_clan_objective - join_icon_state = "gangrel" - join_description = "Purely animalistic, full of transformation abilities, and special frenzy, an active threat at all times." - frenzy_stun_immune = TRUE - blood_drink_type = BLOODSUCKER_DRINK_INHUMANELY - -/datum/bloodsucker_clan/gangrel/New(datum/antagonist/bloodsucker/owner_datum) - . = ..() - bloodsuckerdatum.AddHumanityLost(16.8) - bloodsuckerdatum.BuyPower(new /datum/action/cooldown/bloodsucker/gangrel/transform) - bloodsuckerdatum.owner.current.faction |= "bloodhungry" //i love animals i love animals - for(var/datum/action/cooldown/bloodsucker/masquerade/masquerade_power in bloodsuckerdatum.powers) - bloodsuckerdatum.RemovePower(masquerade_power) - -/datum/bloodsucker_clan/gangrel/on_favorite_vassal(datum/antagonist/bloodsucker/source, datum/antagonist/vassal/vassaldatum) - var/datum/action/cooldown/spell/shapeshift/bat/batform = new(vassaldatum.owner || vassaldatum.owner.current) - batform.Grant(vassaldatum.owner.current) - -/// Enter Frenzy repeatedly -/datum/objective/gangrel_clan_objective - name = "frenzy" - -/datum/objective/gangrel_clan_objective/New() - target_amount = rand(1, 2) - ..() - -/datum/objective/gangrel_clan_objective/update_explanation_text() - . = ..() - explanation_text = "Enter Frenzy [target_amount == 1 ? "at least once" : "2 times"] without succumbing to Final Death." - -/datum/objective/gangrel_clan_objective/check_completion() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.current.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(!bloodsuckerdatum) - return FALSE - if(bloodsuckerdatum.frenzies >= target_amount) - return TRUE - return FALSE diff --git a/code/modules/antagonists/bloodsuckers/clans/clan_hecata.dm b/code/modules/antagonists/bloodsuckers/clans/clan_hecata.dm deleted file mode 100644 index 9b2e44390555..000000000000 --- a/code/modules/antagonists/bloodsuckers/clans/clan_hecata.dm +++ /dev/null @@ -1,43 +0,0 @@ -/datum/bloodsucker_clan/hecata - name = CLAN_HECATA - description = "This Clan is composed of curious practioners of dark magic who enjoy toying with the dead. \n\ - Often compared to the Lasombra, they sometimes act in similar ways and draw power from the void. \n\ - However, they are also very different, and place an emphasis on creating zombie like puppets from the dead. \n\ - They are able to raise the dead as temporary vassals, permanently revive dead vassals, communicate to their vassals from afar, and summon wraiths. \n\ - Their Favorite Vassal also has inherited a small fraction of their power, being able to call wraiths into the world as well." - clan_objective = /datum/objective/hecata_clan_objective - join_icon_state = "hecata" - join_description = "Raise zombie hordes from the dead, and then coordinate them from anywhere anytime." - blood_drink_type = BLOODSUCKER_DRINK_PAINFUL - -/datum/bloodsucker_clan/hecata/New(datum/antagonist/bloodsucker/owner_datum) - . = ..() - bloodsuckerdatum.BuyPower(new /datum/action/cooldown/bloodsucker/targeted/hecata/necromancy) - bloodsuckerdatum.BuyPower(new /datum/action/cooldown/bloodsucker/hecata/spiritcall) - bloodsuckerdatum.BuyPower(new /datum/action/cooldown/bloodsucker/hecata/communion) - bloodsuckerdatum.owner.current.faction |= "bloodhungry" - bloodsuckerdatum.owner.current.update_body() - -/datum/bloodsucker_clan/hecata/on_favorite_vassal(datum/antagonist/bloodsucker/source, datum/antagonist/vassal/vassaldatum) - vassaldatum.BuyPower(new /datum/action/cooldown/bloodsucker/hecata/spiritcall) - - -/datum/objective/hecata_clan_objective - name = "necromance" - -/datum/objective/hecata_clan_objective/New() - target_amount = rand(4,5) - ..() - update_explanation_text() - -/datum/objective/hecata_clan_objective/update_explanation_text() - . = ..() - explanation_text = "Using Necromancy, revive [target_amount] people." - -/datum/objective/hecata_clan_objective/check_completion() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.current.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(!bloodsuckerdatum) - return FALSE - if(bloodsuckerdatum.clanprogress >= target_amount) - return TRUE - return FALSE diff --git a/code/modules/antagonists/bloodsuckers/clans/clan_lasombra.dm b/code/modules/antagonists/bloodsuckers/clans/clan_lasombra.dm deleted file mode 100644 index 0123e6763971..000000000000 --- a/code/modules/antagonists/bloodsuckers/clans/clan_lasombra.dm +++ /dev/null @@ -1,69 +0,0 @@ -/datum/bloodsucker_clan/lasombra - name = CLAN_LASOMBRA - description = "This Clan seems to adore living in the Shadows, worshipping it's secrets. \n\ - They take their research and vanity seriously, they are always very proud of themselves after even minor achievements. \n\ - They appear to be in search of a station with a veil weakness to be able to channel their shadow's abyssal powers. \n\ - Thanks to this, they have also evolved a dark liquid in their veins, which makes them able to manipulate shadows. \n\ - Their Favorite Vassal appears to have been imbued with abyssal essence and is able to blend in with the shadows." - clan_objective = /datum/objective/lasombra_clan_objective - join_icon_state = "lasombra" - join_description = "Heal more on the dark, transform abilties into upgraded ones, become one with the darkness." - control_type = BLOODSUCKER_CONTROL_SHADOWS - -/datum/objective/lasombra_clan_objective - name = "hierarchy" - -/datum/objective/lasombra_clan_objective/New() - target_amount = rand(1, 2) - ..() - update_explanation_text() - -/datum/objective/lasombra_clan_objective/update_explanation_text() - . = ..() - explanation_text = "Ascend [target_amount == 1 ? "at least 1 ability" : "2 abilities"] using a Resting Place altar." - -/datum/objective/lasombra_clan_objective/check_completion() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.current.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(!bloodsuckerdatum) - return FALSE - if(bloodsuckerdatum.clanprogress >= target_amount) - return TRUE - return FALSE - -/datum/bloodsucker_clan/lasombra/New(datum/antagonist/bloodsucker/owner_datum) - . = ..() - bloodsuckerdatum.BuyPower(new /datum/action/cooldown/bloodsucker/targeted/lasombra) - if(ishuman(bloodsuckerdatum.owner.current)) - var/mob/living/carbon/human/human_user = bloodsuckerdatum.owner.current - human_user.eye_color = BLOODCULT_EYE - human_user.update_body() - human_user.updateappearance() - ADD_TRAIT(bloodsuckerdatum.owner.current, CULT_EYES, BLOODSUCKER_TRAIT) - bloodsuckerdatum.owner.current.faction |= "bloodhungry" - bloodsuckerdatum.owner.current.update_body() - var/obj/item/organ/heart/nightmare/nightmarish_heart = new - nightmarish_heart.Insert(bloodsuckerdatum.owner.current) - nightmarish_heart.Stop() - for(var/obj/item/light_eater/blade in bloodsuckerdatum.owner.current.held_items) - QDEL_NULL(blade) - GLOB.reality_smash_track.AddMind(bloodsuckerdatum.owner) - to_chat(bloodsuckerdatum.owner.current, span_notice("You have also learned how to channel the abyss's power into an iron knight's armor that can be build in the structure tab and activated as a trap for your lair.")) - bloodsuckerdatum.owner.teach_crafting_recipe(/datum/crafting_recipe/possessedarmor) - bloodsuckerdatum.owner.teach_crafting_recipe(/datum/crafting_recipe/restingplace) - -/datum/bloodsucker_clan/lasombra/on_favorite_vassal(datum/antagonist/bloodsucker/source, datum/antagonist/vassal/vassaldatum) - vassaldatum.BuyPower(new /datum/action/cooldown/spell/pointed/seize/lesser) - vassaldatum.BuyPower(new /datum/action/cooldown/spell/jaunt/shadow_walk) - if(ishuman(vassaldatum.owner.current)) - var/mob/living/carbon/human/vassal = vassaldatum.owner.current - vassal.eye_color = BLOODCULT_EYE - vassal.dna.update_ui_block(DNA_EYE_COLOR_BLOCK) - ADD_TRAIT(vassal, CULT_EYES, BLOODSUCKER_TRAIT) - var/obj/item/organ/eyes/current_eyes = vassal.getorganslot(ORGAN_SLOT_EYES) - if(current_eyes) - current_eyes.color_cutoffs = list(25, 8, 5) - current_eyes.lighting_cutoff = LIGHTING_CUTOFF_REAL_LOW - - vassal.update_body() - vassal.update_sight() - vassal.update_appearance() diff --git a/code/modules/antagonists/bloodsuckers/clans/clan_toreador.dm b/code/modules/antagonists/bloodsuckers/clans/clan_toreador.dm deleted file mode 100644 index f8ad10ff8342..000000000000 --- a/code/modules/antagonists/bloodsuckers/clans/clan_toreador.dm +++ /dev/null @@ -1,60 +0,0 @@ -/datum/bloodsucker_clan/toreador - name = CLAN_TOREADOR - description = "The most charming Clan of them all, allowing them to very easily disguise among the crew. \n\ - More in touch with their morals, they suffer and benefit more strongly from humanity cost or gain of their actions. \n\ - Known as 'The most humane kind of vampire', they have an obsession with perfectionism and beauty \n\ - The Favorite Vassal gains the Mesmerize ability." - clan_objective = /datum/objective/toreador_clan_objective - join_icon_state = "toreador" - join_description = "Powerful Mesmerize, build statues to boost your status and mood, gather a large audience for your performance." - blood_drink_type = BLOODSUCKER_DRINK_SNOBBY - control_type = BLOODSUCKER_CONTROL_METAL - - -/datum/objective/toreador_clan_objective - name = "leader" - martyr_compatible = TRUE - -/datum/objective/toreador_clan_objective/New() - target_amount = rand(2, 3) - ..() - update_explanation_text() - -/datum/objective/toreador_clan_objective/check_completion() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.current.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(LAZYLEN(bloodsuckerdatum?.vassals) >= target_amount) - return TRUE - return FALSE - - -/datum/bloodsucker_clan/toreador/New(datum/antagonist/bloodsucker/owner_datum) - . = ..() - RegisterSignal(SSdcs, COMSIG_BLOODSUCKER_BROKE_MASQUERADE, PROC_REF(on_bloodsucker_broke_masquerade)) - if(bloodsuckerdatum.owner.current && ishuman(bloodsuckerdatum.owner.current) && !bloodsuckerdatum.owner.current.GetComponent(/datum/component/mood)) - bloodsuckerdatum.owner.current.AddComponent(/datum/component/mood) //You are not a emotionless beast! //trolled! -// user.mind.teach_crafting_recipe(/datum/crafting_recipe/bloodybrush) - bloodsuckerdatum.owner.teach_crafting_recipe(/datum/crafting_recipe/moldingstone) - bloodsuckerdatum.owner.teach_crafting_recipe(/datum/crafting_recipe/chisel) - - for(var/datum/action/cooldown/bloodsucker/masquerade/masquarade_spell in bloodsuckerdatum.powers) - if(!istype(masquarade_spell)) - continue - masquarade_spell.bloodcost = 0 - masquarade_spell.constant_bloodcost = 0 - -/datum/bloodsucker_clan/toreador/Destroy(force) - UnregisterSignal(SSdcs, COMSIG_BLOODSUCKER_BROKE_MASQUERADE) - return ..() - -/datum/bloodsucker_clan/toreador/on_favorite_vassal(datum/antagonist/bloodsucker/source, datum/antagonist/vassal/vassaldatum) - vassaldatum.BuyPower(new /datum/action/cooldown/bloodsucker/targeted/mesmerize) - -/datum/bloodsucker_clan/toreador/proc/on_bloodsucker_broke_masquerade(datum/antagonist/bloodsucker/masquerade_breaker) - SIGNAL_HANDLER - to_chat(bloodsuckerdatum.owner.current, span_userdanger("[masquerade_breaker.owner.current] has broken the Masquerade! Ensure [masquerade_breaker.owner.current.p_they()] [masquerade_breaker.owner.current.p_are()] eliminated at all costs!")) - var/datum/objective/assassinate/masquerade_objective = new() - masquerade_objective.target = masquerade_breaker.owner.current - masquerade_objective.objective_name = "Clan Objective" - masquerade_objective.explanation_text = "Ensure [masquerade_breaker.owner.current], who has broken the Masquerade, succumbs to Final Death." - bloodsuckerdatum.objectives += masquerade_objective - bloodsuckerdatum.owner.announce_objectives() diff --git a/code/modules/antagonists/bloodsuckers/clans/clan_tzimisce.dm b/code/modules/antagonists/bloodsuckers/clans/clan_tzimisce.dm deleted file mode 100644 index 6385291432e5..000000000000 --- a/code/modules/antagonists/bloodsuckers/clans/clan_tzimisce.dm +++ /dev/null @@ -1,33 +0,0 @@ -/datum/bloodsucker_clan/tzimisce - name = CLAN_TZIMISCE - description = "The page is covered in blood..." - join_icon_state = "tzimisce" -// clan_objective = TBD - joinable_clan = FALSE //important - blood_drink_type = BLOODSUCKER_DRINK_INHUMANELY - control_type = BLOODSUCKER_CONTROL_FLESH - -/datum/bloodsucker_clan/tzimisce/New(datum/antagonist/bloodsucker/owner_datum) - . = ..() - bloodsuckerdatum.AddHumanityLost(5.6) - bloodsuckerdatum.BuyPower(new /datum/action/cooldown/bloodsucker/targeted/dice) - bloodsuckerdatum.owner.current.faction |= "bloodhungry" //flesh monster's clan - var/list/powerstoremove = list(/datum/action/cooldown/bloodsucker/veil, /datum/action/cooldown/bloodsucker/masquerade) - for(var/datum/action/cooldown/bloodsucker/banned_power in bloodsuckerdatum.powers) - if(is_type_in_list(banned_power, powerstoremove)) - bloodsuckerdatum.RemovePower(banned_power) - -/datum/bloodsucker_clan/tzimisce/on_favorite_vassal(datum/antagonist/bloodsucker/source, datum/antagonist/vassal/vassaldatum) - if(!ishuman(vassaldatum.owner.current)) - return - var/mob/living/carbon/human/vassal = vassaldatum.owner.current - if(!INVOKE_ASYNC(src, PROC_REF(slash_vassal), bloodsuckerdatum.owner.current, 1 SECONDS, vassal)) - return - playsound(vassal.loc, 'sound/weapons/slash.ogg', 50, TRUE, -1) - if(!INVOKE_ASYNC(src, PROC_REF(slash_vassal), bloodsuckerdatum.owner.current, 1 SECONDS, vassal)) - return - playsound(vassal.loc, 'sound/effects/splat.ogg', 50, TRUE) - INVOKE_ASYNC(vassal, TYPE_PROC_REF(/mob/, set_species), /datum/species/szlachta) - -/datum/bloodsucker_clan/tzimisce/proc/slash_vassal(mob/living/bloodsucker, time, mob/living/vassal) - do_after(bloodsucker, time, vassal, timed_action_flags = IGNORE_USER_LOC_CHANGE|IGNORE_HELD_ITEM) //necessary becaues of how signal handler works diff --git a/code/modules/antagonists/bloodsuckers/powers/_powers.dm b/code/modules/antagonists/bloodsuckers/powers/_powers.dm index 946cfea4bfb3..440b51a76c77 100644 --- a/code/modules/antagonists/bloodsuckers/powers/_powers.dm +++ b/code/modules/antagonists/bloodsuckers/powers/_powers.dm @@ -1,236 +1,68 @@ -/datum/action/cooldown/bloodsucker - name = "Vampiric Gift" - desc = "A vampiric gift." - background_icon = 'icons/mob/actions/actions_bloodsucker.dmi' - background_icon_state = "vamp_power_off" - button_icon = 'icons/mob/actions/actions_bloodsucker.dmi' - button_icon_state = "power_feed" - buttontooltipstyle = "cult" - transparent_when_unavailable = TRUE - - /// Cooldown you'll have to wait between each use, decreases depending on level. - cooldown_time = 2 SECONDS - - ///Background icon when the Power is active. - active_background_icon_state = "vamp_power_on" - ///Background icon when the Power is NOT active. - base_background_icon_state = "vamp_power_off" - - /// The text that appears when using the help verb, meant to explain how the Power changes when ranking up. - var/power_explanation = "" - ///The owner's stored Bloodsucker datum - var/datum/antagonist/bloodsucker/bloodsuckerdatum_power - - // FLAGS // - /// The effects on this Power (Toggled/Single Use/Static Cooldown) - var/power_flags = BP_AM_TOGGLE|BP_AM_SINGLEUSE|BP_AM_STATIC_COOLDOWN|BP_AM_COSTLESS_UNCONSCIOUS - /// Requirement flags for checks - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_STAKED|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS - /// Who can purchase the Power - var/purchase_flags = NONE // BLOODSUCKER_CAN_BUY|LASOMBRA_CAN_BUY|GANGREL_CAN_BUY|VASSAL_CAN_BUY|HUNTER_CAN_BUY - - // VARS // - /// If the Power is currently active. - var/active = FALSE - ///Can increase to yield new abilities - Each Power ranks up each Rank - var/level_current = 0 - ///The cost to ACTIVATE this Power - var/bloodcost = 0 - ///The cost to MAINTAIN this Power - Only used for Constant Cost Powers - var/constant_bloodcost = 0 - ///If the Power has any additional descriptions coming from either 3rd partys or the power itself - var/additional_text = "" - ///Path to the ascended version of the power. For Lasombra bloodsucker powers - var/ascended_power = null - -// Modify description to add cost. -/datum/action/cooldown/bloodsucker/New(Target) - . = ..() - UpdateDesc() - -/datum/action/cooldown/bloodsucker/proc/UpdateDesc() - desc = initial(desc) - if(length(additional_text) > 0) - desc += "

ASCENDED: [additional_text]" - if(bloodcost > 0) - desc += "

COST: [bloodcost] Blood" - if(constant_bloodcost > 0) - desc += "

CONSTANT COST: [name] costs [constant_bloodcost] Blood maintain active." - if(power_flags & BP_AM_SINGLEUSE) - desc += "

SINGLE USE:
[name] can only be used once per night." - if(level_current > 0) - desc += "

LEVEL: [name] is currently level [level_current]." - if(cooldown_time > 0) - desc += "

COOLDOWN: [name] has a cooldown of [cooldown_time / 10] seconds." - -/datum/action/cooldown/bloodsucker/Destroy() - bloodsuckerdatum_power = null - return ..() - -/datum/action/cooldown/bloodsucker/IsAvailable(feedback = FALSE) - return next_use_time <= world.time - -/datum/action/cooldown/bloodsucker/Grant(mob/user) - . = ..() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = IS_BLOODSUCKER(owner) - if(bloodsuckerdatum) - bloodsuckerdatum_power = bloodsuckerdatum - -//This is when we CLICK on the ability Icon, not USING. -/datum/action/cooldown/bloodsucker/Trigger(trigger_flags) - if(active && can_deactivate()) // Active? DEACTIVATE AND END! - DeactivatePower() - return FALSE - if(!can_pay_cost() || !CanUse(owner)) - return FALSE - pay_cost() - ActivatePower() - if(!(power_flags & BP_AM_TOGGLE) || !active) - StartCooldown() - return TRUE - -/datum/action/cooldown/bloodsucker/proc/can_pay_cost() - if(!owner || !owner.mind) - return FALSE - // Cooldown? - if(!COOLDOWN_FINISHED(src, next_use_time)) - owner.balloon_alert(owner, "power unavailable!") - to_chat(owner, "[src] on cooldown!") - return FALSE - if(!bloodsuckerdatum_power) - var/mob/living/living_owner = owner - if(living_owner.blood_volume < bloodcost) - to_chat(owner, span_warning("You need at least [bloodcost] blood to activate [name]")) - return FALSE - return TRUE - - // Have enough blood? Bloodsuckers in a Frenzy don't need to pay them - if(bloodsuckerdatum_power.frenzied) - return TRUE - if(bloodsuckerdatum_power.bloodsucker_blood_volume < bloodcost) - to_chat(owner, span_warning("You need at least [bloodcost] blood to activate [name]")) - return FALSE - return TRUE - -///Called when the Power is upgraded. -/datum/action/cooldown/bloodsucker/proc/upgrade_power() - level_current++ - -///Checks if the Power is available to use. -/datum/action/cooldown/bloodsucker/proc/CanUse(mob/living/carbon/user) - if(!owner) - return FALSE - if(!isliving(user)) - return FALSE - // Torpor? - if((check_flags & BP_CANT_USE_IN_TORPOR) && HAS_TRAIT(user, TRAIT_NODEATH)) - to_chat(user, span_warning("Not while you're in Torpor.")) - return FALSE - // Frenzy? - if((check_flags & BP_CANT_USE_IN_FRENZY) && (bloodsuckerdatum_power?.frenzied)) - to_chat(user, span_warning("You cannot use powers while in a Frenzy!")) - return FALSE - // Stake? - if((check_flags & BP_CANT_USE_WHILE_STAKED) && user.am_staked()) - to_chat(user, span_warning("You have a stake in your chest! Your powers are useless.")) - return FALSE - // Conscious? -- We use our own (AB_CHECK_CONSCIOUS) here so we can control it more, like the error message. - if((check_flags & BP_CANT_USE_WHILE_UNCONSCIOUS) && user.stat != CONSCIOUS) - to_chat(user, span_warning("You can't do this while you are unconscious!")) - return FALSE - // Incapacitated? - if((check_flags & BP_CANT_USE_WHILE_INCAPACITATED) && (user.incapacitated(ignore_restraints = TRUE, ignore_grab = TRUE))) - to_chat(user, span_warning("Not while you're incapacitated!")) - return FALSE - // Constant Cost (out of blood) - if(!bloodsuckerdatum_power) - var/mob/living/living_owner = owner - if(living_owner.blood_volume < bloodcost) - to_chat(owner, span_warning("You need at least [bloodcost] blood to activate [name]")) - return FALSE - return TRUE - if(constant_bloodcost && bloodsuckerdatum_power.bloodsucker_blood_volume <= 0) - to_chat(user, span_warning("You don't have the blood to upkeep [src].")) - return FALSE - return TRUE - -/// NOTE: With this formula, you'll hit half cooldown at level 8 for that power. -/datum/action/cooldown/bloodsucker/StartCooldown() - // Calculate Cooldown (by power's level) - if(power_flags & BP_AM_STATIC_COOLDOWN) - cooldown_time = initial(cooldown_time) - else - cooldown_time = max(initial(cooldown_time) / 2, initial(cooldown_time) - (initial(cooldown_time) / 16 * (level_current-1))) - - return ..() - -/datum/action/cooldown/bloodsucker/proc/can_deactivate() - return TRUE - -/datum/action/cooldown/bloodsucker/is_action_active() - return active - -/datum/action/cooldown/bloodsucker/proc/pay_cost() - // Non-bloodsuckers will pay in other ways. - if(!bloodsuckerdatum_power) - var/mob/living/carbon/living_owner = owner - if(!LAZYFIND(living_owner.dna.species.species_traits, NOBLOOD)) - living_owner.blood_volume -= bloodcost +GLOBAL_LIST_INIT(all_bloodsucker_powers, generate_bloodsucker_power_list()) + +/proc/generate_bloodsucker_power_list() + var/list/powers = list() + for(var/path in subtypesof(/datum/bloodsucker_power)) + powers += new path() + return + +/datum/bloodsucker_power + var/ability_path + ///Name of the effect + var/name = "Basic knowledge" + ///Description of the effect + var/desc = "Basic knowledge of forbidden arts." + ///Fancy description about the effect + var/lore_description = "" + ///Icon file used for the tgui menu + var/icon = 'yogstation/icons/mob/actions/actions_darkspawn.dmi' + ///Specific icon used for the tgui menu + var/icon_state = null + ///What clan can buy this + var/list/clans_purchasable = list() + ///what ability is granted if any + var/list/datum/action/learned_abilities = list() + ///The owner of the power datum that effects will be applied to + var/datum/mind/owner + ///The antag datum of the owner(used for modifying) + var/datum/antagonist/bloodsucker/bloodsucker + +///When the button to purchase is clicked +/datum/bloodsucker_power/proc/on_purchase(datum/mind/user, silent = FALSE) + if(!istype(user, /datum/mind)) return - // Bloodsuckers in a Frenzy don't have enough Blood to pay it, so just don't. - if(bloodsuckerdatum_power.frenzied) + owner = user + bloodsucker = owner.has_antag_datum(/datum/antagonist/bloodsucker) + if(!bloodsucker) + CRASH("[owner] tried to gain an ability datum despite not being a bloodsucker") + if(bloodsucker.bloodsucker_level_unspent <= 0) return - bloodsuckerdatum_power.bloodsucker_blood_volume -= bloodcost - bloodsuckerdatum_power?.update_hud() -/datum/action/cooldown/bloodsucker/proc/ActivatePower() - active = TRUE - if(power_flags & BP_AM_TOGGLE) - START_PROCESSING(SSprocessing, src) + bloodsucker.bloodsucker_level_unspent -- - owner.log_message("used [src][bloodcost != 0 ? " at the cost of [bloodcost]" : ""].", LOG_ATTACK, color="red") - build_all_button_icons() + if(!silent) + to_chat(user, span_velvet("You have unlocked [name]")) + for(var/ability in learned_abilities) + if(ispath(ability, /datum/action)) + var/datum/action/action = new ability(owner) + action.Grant(owner.current) + return TRUE -/datum/action/cooldown/bloodsucker/proc/DeactivatePower() - if(!active) //Already inactive? Return - return - if(power_flags & BP_AM_TOGGLE) - STOP_PROCESSING(SSprocessing, src) - if(power_flags & BP_AM_SINGLEUSE) - remove_after_use() - return - active = FALSE - StartCooldown() - build_all_button_icons() +/datum/bloodsucker_power/proc/remove(refund = FALSE) + for(var/ability in learned_abilities) + if(ispath(ability, /datum/action)) + var/datum/action/action = locate(ability) in owner.current.actions + if(action) + action.Remove(owner.current) + qdel(action) -///Used by powers that are continuously active (That have BP_AM_TOGGLE flag) -/datum/action/cooldown/bloodsucker/process(seconds_per_tick) - SHOULD_CALL_PARENT(TRUE) //Need this to call parent so the cooldown system works - . = ..() - if(!ContinueActive(owner)) // We can't afford the Power? Deactivate it. - DeactivatePower() - return FALSE - // We can keep this up (For now), so Pay Cost! - if(!(power_flags & BP_AM_COSTLESS_UNCONSCIOUS) && owner.stat != CONSCIOUS) - if(bloodsuckerdatum_power) - bloodsuckerdatum_power.AddBloodVolume(-constant_bloodcost) - else - var/mob/living/living_owner = owner - living_owner.blood_volume -= constant_bloodcost - return TRUE + if(refund && bloodsucker) + bloodsucker.bloodsucker_level_unspent ++ + + return QDEL_HINT_QUEUE -/// Checks to make sure this power can stay active -/datum/action/cooldown/bloodsucker/proc/ContinueActive(mob/living/user, mob/living/target) - if(!user) - return FALSE - if(!constant_bloodcost > 0) - return TRUE - if(bloodsuckerdatum_power?.bloodsucker_blood_volume) - return TRUE - if(user.blood_volume) - return TRUE +/datum/bloodsucker_power/proc/ascend() -/// Used to unlearn Single-Use Powers -/datum/action/cooldown/bloodsucker/proc/remove_after_use() - bloodsuckerdatum_power?.powers -= src - Remove(owner) +/datum/bloodsucker_power/Destroy(force, ...) + remove() + return ..() diff --git a/code/modules/antagonists/bloodsuckers/powers/cloak.dm b/code/modules/antagonists/bloodsuckers/powers/cloak.dm deleted file mode 100644 index a0e72d0acb5c..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/cloak.dm +++ /dev/null @@ -1,126 +0,0 @@ -/datum/action/cooldown/bloodsucker/cloak - name = "Cloak of Darkness" - desc = "Blend into the shadows and become invisible to the untrained and Artificial eye." - button_icon_state = "power_cloak" - power_explanation = "Cloak of Darkness<:\n\ - Activate this Power in the shadows and you will slowly turn nearly invisible.\n\ - While using Cloak of Darkness, attempting to run will crush you.\n\ - Additionally, while Cloak is active, you are completely invisible to the AI.\n\ - Higher levels will increase how invisible you are." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_UNCONSCIOUS - purchase_flags = BLOODSUCKER_CAN_BUY|VASSAL_CAN_BUY - bloodcost = 5 - constant_bloodcost = 0.2 - cooldown_time = 5 SECONDS - ascended_power = /datum/action/cooldown/bloodsucker/cloak/shadow - var/was_running - var/runbound = TRUE - -/// Must have nobody around to see the cloak -/datum/action/cooldown/bloodsucker/cloak/CanUse(mob/living/carbon/user) - . = ..() - if(!.) - return FALSE - for(var/mob/living/watchers in viewers(9, owner) - owner) - owner.balloon_alert(owner, "you can only vanish unseen.") - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/cloak/ActivatePower() - . = ..() - var/mob/living/user = owner - was_running = (user.m_intent == MOVE_INTENT_RUN) - if(runbound) - if(was_running) - user.toggle_move_intent() - user.digitalinvis = 1 - user.digitalcamo = 1 - user.balloon_alert(user, "cloak turned on.") - -/datum/action/cooldown/bloodsucker/cloak/process() - // Checks that we can keep using this. - . = ..() - if(!.) - return - if(!active) - return - var/mob/living/user = owner - animate(user, alpha = max(25, owner.alpha - min(75, 10 + 5 * level_current)), time = 1.5 SECONDS) - // Prevents running while on Cloak of Darkness - if(runbound) - if(user.m_intent != MOVE_INTENT_WALK) - owner.balloon_alert(owner, "you attempt to run, crushing yourself.") - user.toggle_move_intent() - user.adjustBruteLoss(rand(5,15)) - -/datum/action/cooldown/bloodsucker/cloak/ContinueActive(mob/living/user, mob/living/target) - . = ..() - if(!.) - return FALSE - /// Must be CONSCIOUS - if(user.stat != CONSCIOUS) - to_chat(owner, span_warning("Your Cloak of Darkness fell off due to you falling unconscious!")) - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/cloak/DeactivatePower() - . = ..() - var/mob/living/user = owner - animate(user, alpha = 255, time = 1 SECONDS) - user.digitalinvis = 0 - user.digitalcamo = 0 - if(runbound) - if(was_running && user.m_intent == MOVE_INTENT_WALK) - user.toggle_move_intent() - user.balloon_alert(user, "cloak turned off.") - -/datum/action/cooldown/bloodsucker/cloak/shadow - name = "Cloak of Shadows" - desc = "Empowered to the abyss, fortitude will now grant you a shadow armor, making your grip harder to escape and reduce projectile damage while in darkness." - background_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - active_background_icon_state = "lasombra_power_on" - base_background_icon_state = "lasombra_power_off" - button_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - button_icon_state = "power_state" - additional_text = "Additionally allows you to run during cloak and gain a physical cloak while in darkness." - purchase_flags = LASOMBRA_CAN_BUY - constant_bloodcost = 0.3 - runbound = FALSE - ascended_power = null - -/obj/item/clothing/neck/yogs/sith_cloak/cloak - name = "cloak of shadows" - desc = "Fancy stuff." - icon = 'icons/obj/vamp_obj.dmi' - worn_icon = 'icons/obj/vamp_obj.dmi' - icon_state = "cloak" - item_state = "cloak" - armor = list(MELEE = 0, BULLET = 0, LASER = 10, ENERGY = 10, BOMB = 0, BIO = 0, RAD = 0, FIRE = 10, ACID = 100) //good if you haven nothing - -/obj/item/clothing/neck/yogs/sith_cloak/cloak/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, BLOODSUCKER_TRAIT) - START_PROCESSING(SSobj, src) - -/obj/item/clothing/neck/yogs/sith_cloak/cloak/process() - var/turf/T = get_turf(src) - var/light_amount = T.get_lumcount() - if(light_amount > LIGHTING_TILE_IS_DARK) - qdel(src) - STOP_PROCESSING(SSobj, src) - src.visible_message(span_warning("The cape desintegrates as the light contacts it's surface!")) - -/datum/action/cooldown/bloodsucker/cloak/shadow/ActivatePower() - . = ..() - var/turf/T = get_turf(owner) - var/light_amount = T.get_lumcount() - if(light_amount <= LIGHTING_TILE_IS_DARK) - if(!owner.get_item_by_slot(ITEM_SLOT_NECK)) - owner.equip_to_slot_or_del( new /obj/item/clothing/neck/yogs/sith_cloak/cloak(null), ITEM_SLOT_NECK) - -/datum/action/cooldown/bloodsucker/cloak/shadow/DeactivatePower() - . = ..() - var/obj/item/I = owner.get_item_by_slot(ITEM_SLOT_NECK) - if(istype(I, /obj/item/clothing/neck/yogs/sith_cloak/cloak)) - qdel(I) diff --git a/code/modules/antagonists/bloodsuckers/powers/distress.dm b/code/modules/antagonists/bloodsuckers/powers/distress.dm deleted file mode 100644 index 230dae54d067..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/distress.dm +++ /dev/null @@ -1,22 +0,0 @@ -/datum/action/cooldown/bloodsucker/distress - name = "Distress" - desc = "Injure yourself, allowing you to make a desperate call for help to your Master." - button_icon_state = "power_distress" - power_explanation = "Distress:\n\ - Use this Power from anywhere and your Master Bloodsucker will instnatly be alerted of your location." - power_flags = NONE - check_flags = NONE - purchase_flags = NONE - bloodcost = 10 - cooldown_time = 10 SECONDS - -/datum/action/cooldown/bloodsucker/distress/ActivatePower() - . = ..() - var/turf/open/floor/target_area = get_area(owner) - var/datum/antagonist/vassal/vassaldatum = owner.mind.has_antag_datum(/datum/antagonist/vassal) - - to_chat(owner, "You call out for your master!") - to_chat(vassaldatum.master.owner, "[owner], your loyal Vassal, is desperately calling for aid at [target_area]!") - - var/mob/living/user = owner - user.adjustBruteLoss(10) diff --git a/code/modules/antagonists/bloodsuckers/powers/feed.dm b/code/modules/antagonists/bloodsuckers/powers/feed.dm deleted file mode 100644 index 4b62c8dbc97d..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/feed.dm +++ /dev/null @@ -1,250 +0,0 @@ -#define FEED_NOTICE_RANGE 2 -#define FEED_DEFAULT_TIMER (10 SECONDS) - -/datum/action/cooldown/bloodsucker/feed - name = "Feed" - desc = "Feed blood off of a living creature." - button_icon_state = "power_feed" - power_explanation = "Feed:\n\ - Feed can't be used until you reach your first Bloodsucker level.\n\ - Activate Feed while next to someone and you will begin to feed blood off of them.\n\ - The time needed before you start feeding speeds up the higher level you are.\n\ - Feeding off of someone while you have them aggressively grabbed will put them to sleep.\n\ - While feeding, you can't speak, as your mouth is covered.\n\ - Feeding while nearby (2 tiles away from) a mortal who is unaware of Bloodsuckers' existence, will cause a Masquerade Infraction\n\ - If you get too many Masquerade Infractions, you will break the Masquerade.\n\ - If you are in desperate need of blood, mice can be fed off of, at a cost." - power_flags = BP_AM_TOGGLE|BP_AM_STATIC_COOLDOWN - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_WHILE_STAKED|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS - purchase_flags = BLOODSUCKER_CAN_BUY|BLOODSUCKER_DEFAULT_POWER - bloodcost = 0 - cooldown_time = 15 SECONDS - ///Amount of blood taken, reset after each Feed. Used for logging. - var/blood_taken = 0 - ///The amount of Blood a target has since our last feed, this loops and lets us not spam alerts of low blood. - var/warning_target_bloodvol = BLOOD_VOLUME_MAX_LETHAL - ///Reference to the target we've fed off of - var/datum/weakref/target_ref - ///Are we feeding with passive grab or not? - var/silent_feed = TRUE - -/datum/action/cooldown/bloodsucker/feed/CanUse(mob/living/carbon/user) - . = ..() - if(!.) - return FALSE - if(target_ref) //already sucking blood. - return FALSE - if(!level_current) - owner.balloon_alert(owner, "too weak!") - to_chat(owner, span_warning("You can't use [src] until you level up.")) - return FALSE - if(user.is_mouth_covered() && !isplasmaman(user)) - owner.balloon_alert(owner, "mouth covered!") - return FALSE - if(bloodsuckerdatum_power.my_clan.blood_drink_type == BLOODSUCKER_DRINK_PAINFUL && owner.grab_state <= GRAB_PASSIVE) - owner.balloon_alert(owner, "can't silent feed!") - return FALSE - //Find target, it will alert what the problem is, if any. - if(!find_target()) - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/feed/ContinueActive(mob/living/user, mob/living/target) - if(!target) - return FALSE - if(!user.Adjacent(target)) - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/feed/DeactivatePower() - var/mob/living/user = owner - var/mob/living/feed_target = target_ref?.resolve() - if(isnull(feed_target)) - log_combat(user, user, "fed on blood (target not found)", addition="(and took [blood_taken] blood)") - else - log_combat(user, feed_target, "fed on blood", addition="(and took [blood_taken] blood)") - user.balloon_alert(owner, "feed stopped") - to_chat(user, span_notice("You slowly release [feed_target].")) - if(feed_target.stat == DEAD) - SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankkilled", /datum/mood_event/drankkilled) - bloodsuckerdatum_power.AddHumanityLost(10) - - target_ref = null - warning_target_bloodvol = BLOOD_VOLUME_MAX_LETHAL - blood_taken = 0 - REMOVE_TRAIT(user, TRAIT_IMMOBILIZED, FEED_TRAIT) - REMOVE_TRAIT(user, TRAIT_MUTE, FEED_TRAIT) - return ..() - -/datum/action/cooldown/bloodsucker/feed/ActivatePower() - var/mob/living/feed_target = target_ref.resolve() - if(istype(feed_target, /mob/living/simple_animal/mouse || istype(feed_target, /mob/living/simple_animal/hostile/rat))) - to_chat(owner, span_notice("You recoil at the taste of a lesser lifeform.")) - if(bloodsuckerdatum_power.my_clan.blood_drink_type != BLOODSUCKER_DRINK_INHUMANELY) - SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood_bad) - bloodsuckerdatum_power.AddHumanityLost(1) - bloodsuckerdatum_power.AddBloodVolume(25) - DeactivatePower() - feed_target.death() - return - var/feed_timer = clamp(round(FEED_DEFAULT_TIMER / (1.25 * level_current)), 1 SECONDS, FEED_DEFAULT_TIMER) - if(bloodsuckerdatum_power.frenzied) - feed_timer = 2 SECONDS - - owner.balloon_alert(owner, "feeding off [feed_target]...") - if(!do_after(owner, feed_timer, feed_target, NONE, TRUE)) - DeactivatePower() - return - if(owner.pulling == feed_target && owner.grab_state >= GRAB_AGGRESSIVE) - if(!IS_BLOODSUCKER(feed_target) && !IS_VASSAL(feed_target) && !IS_MONSTERHUNTER(feed_target)) - feed_target.Unconscious((5 + level_current) SECONDS) - if(!feed_target.density) - feed_target.Move(owner.loc) - silent_feed = FALSE //no more mr nice guy - owner.visible_message( - span_warning("[owner] closes [owner.p_their()] mouth around [feed_target]'s neck!"), - span_warning("You sink your fangs into [feed_target]'s neck.")) - else - // Only people who AREN'T the target will notice this action. - var/dead_message = feed_target.stat != DEAD ? " [feed_target.p_they(TRUE)] looks dazed, and will not remember this." : "" - owner.visible_message( - span_notice("[owner] puts [feed_target]'s wrist up to [owner.p_their()] mouth."), \ - span_notice("You slip your fangs into [feed_target]'s wrist.[dead_message]"), \ - vision_distance = FEED_NOTICE_RANGE, ignored_mobs = feed_target) - - //check if we were seen - for(var/mob/living/watchers in oviewers(FEED_NOTICE_RANGE) - feed_target) - if(!watchers.client) - continue - if(watchers.has_unlimited_silicon_privilege) - continue - if(watchers.stat >= DEAD) - continue - if(HAS_TRAIT(watchers, TRAIT_BLIND) || HAS_TRAIT(watchers, TRAIT_NEARSIGHT)) - continue - if(IS_BLOODSUCKER(watchers) || IS_VASSAL(watchers) || HAS_TRAIT(watchers.mind, TRAIT_BLOODSUCKER_HUNTER)) - continue - owner.balloon_alert(owner, "feed noticed!") - bloodsuckerdatum_power.give_masquerade_infraction() - break - - ADD_TRAIT(owner, TRAIT_MUTE, FEED_TRAIT) - ADD_TRAIT(owner, TRAIT_IMMOBILIZED, FEED_TRAIT) - return ..() - -/datum/action/cooldown/bloodsucker/feed/process() - if(!active) //If we aren't active (running on SSfastprocess) - return ..() //Manage our cooldown timers - var/mob/living/user = owner - var/mob/living/feed_target = target_ref?.resolve() - if(!ContinueActive(user, feed_target)) - if(!silent_feed) - user.visible_message( - span_warning("[user] is ripped from [feed_target]'s throat. [feed_target.p_their(TRUE)] blood sprays everywhere!"), - span_warning("Your teeth are ripped from [feed_target]'s throat. [feed_target.p_their(TRUE)] blood sprays everywhere!")) - // Deal Damage to Target (should have been more careful!) - if(iscarbon(feed_target)) - var/mob/living/carbon/carbon_target = feed_target - carbon_target.bleed(15) - playsound(get_turf(feed_target), 'sound/effects/splat.ogg', 40, TRUE) - if(ishuman(feed_target)) - var/mob/living/carbon/human/target_user = feed_target - var/obj/item/bodypart/head_part = target_user.get_bodypart(BODY_ZONE_HEAD) - if(head_part) - head_part.generic_bleedstacks += 5 - feed_target.add_splatter_floor(get_turf(feed_target)) - user.add_mob_blood(feed_target) // Put target's blood on us. The donor goes in the ( ) - feed_target.add_mob_blood(feed_target) - feed_target.apply_damage(10, BRUTE, BODY_ZONE_HEAD, wound_bonus = CANT_WOUND) - INVOKE_ASYNC(feed_target, TYPE_PROC_REF(/mob, emote), "scream") - DeactivatePower() - return - var/feed_strength_mult = 0 - if(bloodsuckerdatum_power.frenzied) - feed_strength_mult = 2 - else if(owner.pulling == feed_target && owner.grab_state >= GRAB_AGGRESSIVE) - feed_strength_mult = 1 - else - feed_strength_mult = 0.3 - blood_taken += bloodsuckerdatum_power.handle_feeding(feed_target, feed_strength_mult, level_current) - if(feed_strength_mult > 5 && feed_target.stat < DEAD) - SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood) - if(bloodsuckerdatum_power.my_clan.blood_drink_type == BLOODSUCKER_DRINK_SNOBBY && !feed_target.mind) - SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood_bad) - if(feed_target.stat >= DEAD) - SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood_dead) - if(!IS_BLOODSUCKER(feed_target)) - if(feed_target.blood_volume <= BLOOD_VOLUME_BAD(feed_target) && warning_target_bloodvol > BLOOD_VOLUME_BAD(feed_target)) - owner.balloon_alert(owner, "your victim's blood is fatally low!") - else if(feed_target.blood_volume <= BLOOD_VOLUME_OKAY(feed_target) && warning_target_bloodvol > BLOOD_VOLUME_OKAY(feed_target)) - owner.balloon_alert(owner, "your victim's blood is dangerously low.") - else if(feed_target.blood_volume <= BLOOD_VOLUME_SAFE(feed_target) && warning_target_bloodvol > BLOOD_VOLUME_SAFE(feed_target)) - owner.balloon_alert(owner, "your victim's blood is at an unsafe level.") - warning_target_bloodvol = feed_target.blood_volume - - if(user.blood_volume >= bloodsuckerdatum_power.max_blood_volume) - DeactivatePower() - return - if(feed_target.blood_volume <= 0) - DeactivatePower() - return - owner.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, TRUE) - //play sound to target to show they're dying. - if(owner.pulling == feed_target && owner.grab_state >= GRAB_AGGRESSIVE) - feed_target.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, TRUE) - -/datum/action/cooldown/bloodsucker/feed/proc/find_target() - if(owner.pulling && isliving(owner.pulling)) - if(!can_feed_from(owner.pulling, give_warnings = TRUE)) - return FALSE - target_ref = WEAKREF(owner.pulling) - return TRUE - var/list/close_living_mobs = list() - var/list/close_dead_mobs = list() - for(var/mob/living/near_targets in oview(1, owner)) - if(!owner.Adjacent(near_targets)) - continue - if(near_targets.stat) - close_living_mobs |= near_targets - else - close_dead_mobs |= near_targets - //Check living first - for(var/mob/living/suckers in close_living_mobs) - if(can_feed_from(suckers)) - target_ref = WEAKREF(suckers) - return TRUE - //If not, check dead - for(var/mob/living/suckers in close_dead_mobs) - if(can_feed_from(suckers)) - target_ref = WEAKREF(suckers) - return TRUE - //No one to suck blood from. - return FALSE - -/datum/action/cooldown/bloodsucker/feed/proc/can_feed_from(mob/living/target, give_warnings = FALSE) - if(istype(target, /mob/living/simple_animal/mouse) || istype(target, /mob/living/simple_animal/hostile/rat)) - if(bloodsuckerdatum_power.my_clan.blood_drink_type == BLOODSUCKER_DRINK_SNOBBY) - if(give_warnings) - owner.balloon_alert(owner, "too disgusting!") - return FALSE - return TRUE - //Mice check done, only humans are otherwise allowed - if(!ishuman(target)) - return FALSE - var/mob/living/carbon/human/target_user = target - if(!(target_user.dna?.species) || !(target_user.mob_biotypes & MOB_ORGANIC)) - if(give_warnings) - owner.balloon_alert(owner, "no blood!") - return FALSE - if(!target_user.can_inject(owner, BODY_ZONE_HEAD)) - if(give_warnings) - owner.balloon_alert(owner, "suit too thick!") - return FALSE - if((bloodsuckerdatum_power.my_clan.blood_drink_type == BLOODSUCKER_DRINK_SNOBBY) && !target_user.mind && !bloodsuckerdatum_power.frenzied) - if(give_warnings) - owner.balloon_alert(owner, "cant drink from mindless!") - return FALSE - return TRUE - -#undef FEED_NOTICE_RANGE -#undef FEED_DEFAULT_TIMER diff --git a/code/modules/antagonists/bloodsuckers/powers/fortitude.dm b/code/modules/antagonists/bloodsuckers/powers/fortitude.dm deleted file mode 100644 index 9aa2837e81f1..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/fortitude.dm +++ /dev/null @@ -1,126 +0,0 @@ -/datum/action/cooldown/bloodsucker/fortitude - name = "Fortitude" - desc = "Withstand egregious physical wounds and walk away from attacks that would stun, pierce, and dismember lesser beings." - button_icon_state = "power_fortitude" - power_explanation = "Fortitude:\n\ - Activating Fortitude will provide pierce, stun and dismember immunity.\n\ - You will additionally gain resistance to Brute and Stamina damge, scaling with level.\n\ - While using Fortitude, attempting to run will crush you.\n\ - At level 4, you gain complete stun immunity.\n\ - Higher levels will increase Brute and Stamina resistance." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_AM_COSTLESS_UNCONSCIOUS - purchase_flags = BLOODSUCKER_CAN_BUY|VASSAL_CAN_BUY - bloodcost = 30 - cooldown_time = 8 SECONDS - constant_bloodcost = 0.2 - ascended_power = /datum/action/cooldown/bloodsucker/fortitude/shadow - var/was_running - var/fortitude_resist // So we can raise and lower your brute resist based on what your level_current WAS. - -/datum/action/cooldown/bloodsucker/fortitude/ActivatePower() - . = ..() - owner.balloon_alert(owner, "fortitude turned on.") - to_chat(owner, span_notice("Your flesh, skin, and muscles become as steel.")) - // Traits & Effects - owner.add_traits(list(TRAIT_PIERCEIMMUNE, TRAIT_NODISMEMBER, TRAIT_PUSHIMMUNE), BLOODSUCKER_TRAIT) - if(level_current >= 2) - ADD_TRAIT(owner, TRAIT_STUNIMMUNE, BLOODSUCKER_TRAIT) // They'll get stun resistance + this, who cares. - var/mob/living/carbon/human/bloodsucker_user = owner - if(IS_BLOODSUCKER(owner) || IS_VASSAL(owner)) - fortitude_resist = max(0.3, 0.7 - level_current * 0.1) - bloodsucker_user.physiology.brute_mod *= fortitude_resist - bloodsucker_user.physiology.stamina_mod *= fortitude_resist - if(IS_MONSTERHUNTER(owner)) - bloodsucker_user.physiology.brute_mod *= 0.4 - bloodsucker_user.physiology.burn_mod *= 0.4 - ADD_TRAIT(owner, TRAIT_STUNIMMUNE, BLOODSUCKER_TRAIT) - - was_running = (owner.m_intent == MOVE_INTENT_RUN) - if(was_running) - bloodsucker_user.toggle_move_intent() - -/datum/action/cooldown/bloodsucker/fortitude/process() - // Checks that we can keep using this. - . = ..() - if(!.) - return - if(!active) - return - var/mob/living/carbon/user = owner - /// Prevents running while on Fortitude - if(user.m_intent != MOVE_INTENT_WALK) - user.toggle_move_intent() - user.balloon_alert(user, "you attempt to run, crushing yourself.") - user.adjustBruteLoss(rand(5,15)) - /// We don't want people using fortitude being able to use vehicles - if(user.buckled && istype(user.buckled, /obj/vehicle)) - user.buckled.unbuckle_mob(src, force=TRUE) - -/datum/action/cooldown/bloodsucker/fortitude/DeactivatePower() - if(!ishuman(owner)) - return - var/mob/living/carbon/human/bloodsucker_user = owner - if(IS_BLOODSUCKER(owner) || IS_VASSAL(owner)) - bloodsucker_user.physiology.brute_mod /= fortitude_resist - if(!HAS_TRAIT_FROM(bloodsucker_user, TRAIT_STUNIMMUNE, BLOODSUCKER_TRAIT)) - bloodsucker_user.physiology.stamina_mod /= fortitude_resist - if(IS_MONSTERHUNTER(owner)) - bloodsucker_user.physiology.brute_mod /= 0.4 - bloodsucker_user.physiology.burn_mod /= 0.4 - // Remove Traits & Effects - owner.remove_traits(list(TRAIT_PIERCEIMMUNE, TRAIT_NODISMEMBER, TRAIT_PUSHIMMUNE, TRAIT_STUNIMMUNE), BLOODSUCKER_TRAIT) - - if(was_running && bloodsucker_user.m_intent == MOVE_INTENT_WALK) - bloodsucker_user.toggle_move_intent() - owner.balloon_alert(owner, "fortitude turned off.") - return ..() - -/// Monster Hunter version -/datum/action/cooldown/bloodsucker/fortitude/hunter - name = "Flow" - desc = "Use the arts to Flow, giving shove and stun immunity, as well as brute, burn, dismember and pierce resistance. You cannot run while this is active." - purchase_flags = HUNTER_CAN_BUY - -/datum/action/cooldown/bloodsucker/fortitude/shadow - name = "Shadow Armor" - desc = "Empowered to the abyss, fortitude will now grant you a shadow armor, making your grip harder to escape and reduce projectile damage while in darkness." - background_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - active_background_icon_state = "lasombra_power_on" - base_background_icon_state = "lasombra_power_off" - button_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - button_icon_state = "power_armor" - additional_text = "Additionally gives you extra damage while fortitude'd and agro grab while in darkness." - purchase_flags = LASOMBRA_CAN_BUY - constant_bloodcost = 0.3 - ascended_power = null - -/datum/action/cooldown/bloodsucker/fortitude/shadow/ActivatePower() - . = ..() - var/mob/living/carbon/human/user = owner - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - var/turf/T = get_turf(owner) - var/light_amount = T.get_lumcount() - if(light_amount <= LIGHTING_TILE_IS_DARK) - bloodsuckerdatum.frenzygrab.teach(user, TRUE) - to_chat(user, span_notice("Shadow tentacles form and attach themselves to your body, you feel as if your muscles have merged with the shadows!")) - user.physiology.punchdamagehigh_bonus += 0.5 * level_current - user.physiology.punchdamagelow_bonus += 0.5 * level_current - -/datum/action/cooldown/bloodsucker/fortitude/shadow/process() - . = ..() - var/turf/T = get_turf(owner) - var/light_amount = T.get_lumcount() - var/mob/living/carbon/user = owner - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(light_amount > LIGHTING_TILE_IS_DARK) - bloodsuckerdatum.frenzygrab.remove(user) - to_chat(user, span_warning("As you enter in contact with the light, the tentacles dissipate!")) - -/datum/action/cooldown/bloodsucker/fortitude/shadow/DeactivatePower() - . = ..() - var/mob/living/carbon/human/user = owner - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - bloodsuckerdatum.frenzygrab.remove(user) - user.physiology.punchdamagehigh_bonus -= 0.5 * level_current - user.physiology.punchdamagelow_bonus -= 0.5 * level_current diff --git a/code/modules/antagonists/bloodsuckers/powers/gangrel.dm b/code/modules/antagonists/bloodsuckers/powers/gangrel.dm deleted file mode 100644 index 3b125a3f16dc..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/gangrel.dm +++ /dev/null @@ -1,662 +0,0 @@ -/datum/action/cooldown/bloodsucker/gangrel - background_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - button_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - background_icon_state = "gangrel_power_off" - active_background_icon_state = "gangrel_power_on" - base_background_icon_state = "gangrel_power_off" - purchase_flags = GANGREL_CAN_BUY - power_flags = BP_AM_TOGGLE|BP_AM_STATIC_COOLDOWN - check_flags = BP_AM_COSTLESS_UNCONSCIOUS - cooldown_time = 10 SECONDS - -/datum/action/cooldown/bloodsucker/gangrel/transform - name = "Transform" - desc = "Allows you to unleash your inner form and turn into something greater." - button_icon_state = "power_gangrel" - power_explanation = "Transform:\n\ - A gangrel only power, will turn you into a feral being depending on your blood sucked.\n\ - May have unforseen consequences if used on low blood sucked, upgrades every 500 units.\n\ - Some forms have special abilites to them depending on what abilites you have.\n\ - Be wary of your blood status when using it, takes 10 seconds of standing still to transform!" - power_flags = BP_AM_SINGLEUSE|BP_AM_STATIC_COOLDOWN - bloodcost = 100 - -/datum/action/cooldown/bloodsucker/gangrel/transform/ActivatePower() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(/datum/antagonist/bloodsucker) - var/mob/living/carbon/human/user = owner - var/list/radial_display = list() - //get our options, switches are kinda weird here cause wwe ant to stack them - if(bloodsuckerdatum) //makes the icons for the options - var/datum/radial_menu_choice/option = new - user.setDir(SOUTH) - var/icon/icon_to_mix = getFlatIcon(user) - icon_to_mix.Blend(icon('icons/mob/mutant_bodyparts.dmi', "m_ears_cat_FRONT"), ICON_OVERLAY) - option.image = icon_to_mix - option.info = "[iscatperson(user) ? "Lizard" : "Felinid"]: Increased agility, speed and interaction speed, but decreased defense." - radial_display["Lizard/Felinid"] = option //haha yeah - if(bloodsuckerdatum.total_blood_drank >= 250) - var/datum/radial_menu_choice/option = new - var/icon/body = icon('yogstation/icons/mob/human_parts.dmi', "gorilla_r_leg") //procedurally generated icons? don't mind if i do - body.Blend(icon('yogstation/icons/mob/human_parts.dmi', "gorilla_l_leg"), ICON_OVERLAY) //it may seem kinda big but it's worth it ngl - body.Blend(icon('yogstation/icons/mob/human_parts.dmi', "gorilla_r_arm"), ICON_OVERLAY) - body.Blend(icon('yogstation/icons/mob/human_parts.dmi', "gorilla_l_arm"), ICON_OVERLAY) - body.Blend(icon('yogstation/icons/mob/human_parts.dmi', "gorilla_r_hand"), ICON_OVERLAY) - body.Blend(icon('yogstation/icons/mob/human_parts.dmi', "gorilla_l_hand"), ICON_OVERLAY) - body.Blend(icon('yogstation/icons/mob/human_parts.dmi', "gorilla_chest_m"), ICON_OVERLAY) - body.Blend(icon('yogstation/icons/mob/human_parts.dmi', "gorilla_head_m"), ICON_OVERLAY) - option.image = body - option.info = "Gorilla: Increased durability and strength, but less speed and interaction speed." - radial_display["Gorilla"] = option - if(bloodsuckerdatum.total_blood_drank >= 750) - var/datum/radial_menu_choice/option = new - option.image = icon('icons/mob/bloodsucker_mobs.dmi', "batform") - option.info = "Turn into a giant bat simple mob with unique abilities." - radial_display["Bat"] = option - var/chosen_transform = show_radial_menu(user, user, radial_display) - if(!chosen_transform || !do_after(user, 10 SECONDS)) - return - transform(chosen_transform) //actually transform - return ..() - -/datum/action/cooldown/bloodsucker/gangrel/transform/proc/transform(chosen_transform) - var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(/datum/antagonist/bloodsucker) - var/mob/living/carbon/human/user = owner - switch(chosen_transform) - if("Lizard/Felinid") - if(iscatperson(user)) - user.set_species(/datum/species/lizard) - playsound(user.loc, 'sound/voice/lizard/hiss.ogg', 50) - else - user.set_species(/datum/species/human/felinid) - playsound(user.loc, 'sound/voice/feline/meow1.ogg', 50) - user.dna.species.armor -= 20 //careful - user.dna.species.speedmod = -0.5 - user.dna.species.action_speed_coefficient *= 0.7 - bloodsuckerdatum.AddBloodVolume(75) - if("Gorilla") - user.set_species(/datum/species/gorilla) - playsound(user.loc, 'sound/creatures/gorilla.ogg', 50) - user.dna.species.punchdamagelow += 10 - user.dna.species.punchdamagehigh += 10 //very stronk - user.dna.species.punchstunchance += 10 - user.dna.species.action_speed_coefficient *= 1.3 - user.dna.species.armor += 15 - bloodsuckerdatum.AddBloodVolume(50) - if("Bat") - var/mob/living/simple_animal/hostile/bloodsucker/giantbat/gb - if(!gb || gb.stat == DEAD) - gb = new /mob/living/simple_animal/hostile/bloodsucker/giantbat(user.loc) - user.forceMove(gb) - gb.bloodsucker = user - user.status_flags |= GODMODE //sad! - user.mind.transfer_to(gb) - var/list/bat_powers = list(new /datum/action/cooldown/bloodsucker/gangrel/transform_back, ) - for(var/datum/action/cooldown/bloodsucker/power in bloodsuckerdatum.powers) - if(istype(power, /datum/action/cooldown/bloodsucker/targeted/haste)) - bat_powers += new /datum/action/cooldown/bloodsucker/targeted/haste/batdash - if(istype(power, /datum/action/cooldown/bloodsucker/targeted/mesmerize)) - bat_powers += new /datum/action/cooldown/bloodsucker/targeted/bloodbolt - if(istype(power, /datum/action/cooldown/bloodsucker/targeted/brawn)) - bat_powers += new /datum/action/cooldown/bloodsucker/gangrel/wingslam - for(var/datum/action/cooldown/bloodsucker/power in bat_powers) - power.Grant(gb) - playsound(gb.loc, 'sound/items/toysqueak1.ogg', 50, TRUE) - return //early to not mess with vampire organs proc - - bloodsuckerdatum.heal_vampire_organs() //regives you the stuff - -/datum/action/cooldown/bloodsucker/gangrel/transform_back - name = "Transform" - desc = "Regress back into a human." - button_icon_state = "power_gangrel" - power_explanation = "Transform:\n\ - Regress back to your humanoid form early, requires you to stand still.\n\ - Beware you will not be able to transform again until the night passes!" - -/datum/action/cooldown/bloodsucker/gangrel/transform_back/ActivatePower() - if(!do_after(owner, 10 SECONDS)) - return - if(istype(owner, /mob/living/simple_animal/hostile/bloodsucker)) - qdel(owner) - return ..() -/* -////////////////||\\\\\\\\\\\\\\\\ -\\ Bat Only // -// Powers \\ -\\\\\\\\\\\\\\\\||//////////////// -*/ -/datum/action/cooldown/bloodsucker/targeted/haste/batdash - name = "Flying Haste" - desc = "Propulse yourself into a position of advantage." - background_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - button_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - button_icon_state = "power_baste" - active_background_icon_state = "bat_power_on" - base_background_icon_state = "bat_power_off" - power_explanation = "Flying Haste<:\n\ - Makes you dash into the air, creating a smoke cloud at the end.\n\ - Helpful in situations where you either need to run away or engage in a crowd of people, works over tables.\n\ - Created from your Immortal Haste ability." - power_flags = BP_AM_TOGGLE|BP_AM_STATIC_COOLDOWN - check_flags = NONE - purchase_flags = GANGREL_CAN_BUY - bloodcost = 0 - cooldown_time = 15 SECONDS - -/datum/action/cooldown/bloodsucker/targeted/haste/batdash/CanUse(mob/living/carbon/user) - var/mob/living/L = user - if(L.stat == DEAD) - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/targeted/haste/batdash/FireTargetedPower(atom/target_atom) - . = ..() - do_smoke(2, owner.loc, smoke_type = /obj/effect/particle_effect/fluid/smoke/transparent) //so you can attack people after hasting - -/datum/action/cooldown/bloodsucker/targeted/bloodbolt - name = "Blood Bolt" - desc = "Shoot a blood bolt to damage your foes." - background_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - button_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - button_icon_state = "power_bolt" - active_background_icon_state = "bat_power_on" - base_background_icon_state = "bat_power_off" - power_explanation = "Blood Bolt<:\n\ - Shoots a blood bolt that does moderate damage to your foes.\n\ - Helpful in situations where you get outranged or just extra damage.\n\ - Created from your Mesmerize ability." - power_flags = BP_AM_TOGGLE|BP_AM_STATIC_COOLDOWN - check_flags = NONE - purchase_flags = GANGREL_CAN_BUY - bloodcost = 0 - cooldown_time = 12.5 SECONDS - -/datum/action/cooldown/bloodsucker/targeted/bloodbolt/CanUse(mob/living/carbon/user) - var/mob/living/L = user - if(L.stat == DEAD) - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/targeted/bloodbolt/FireTargetedPower(atom/target_atom) - . = ..() - var/mob/living/user = owner - to_chat(user, span_warning("You fire a blood bolt!")) - user.changeNext_move(CLICK_CD_RANGE) - user.newtonian_move(get_dir(target_atom, user)) - var/obj/projectile/magic/arcane_barrage/bloodsucker/magic_9ball = new(user.loc) - magic_9ball.bloodsucker_power = src - magic_9ball.firer = user - magic_9ball.def_zone = ran_zone(user.zone_selected) - magic_9ball.preparePixelProjectile(target_atom, user) - INVOKE_ASYNC(magic_9ball, TYPE_PROC_REF(/obj/projectile, fire)) - playsound(user, 'sound/magic/wand_teleport.ogg', 60, TRUE) - power_activated_sucessfully() - -/obj/projectile/magic/arcane_barrage/bloodsucker - name = "blood bolt" - icon_state = "bloodbolt" - damage_type = BURN - damage = 30 - var/datum/action/cooldown/bloodsucker/targeted/bloodbolt/bloodsucker_power - -/obj/projectile/magic/arcane_barrage/bloodsucker/on_hit(target) - if(ismob(target)) - qdel(src) - if(iscarbon(target)) - var/mob/living/carbon/C = target - C.Knockdown(0.1) - return BULLET_ACT_HIT - return ..() - -/datum/action/cooldown/bloodsucker/gangrel/wingslam - name = "Wing Slam" - desc = "Slams all foes next to you." - button_icon_state = "power_wingslam" - active_background_icon_state = "bat_power_on" - base_background_icon_state = "bat_power_off" - power_explanation = "Wing Slam:\n\ - Knocksback and immobilizes people adjacent to you.\n\ - Has a low recharge time and may be helpful in meelee situations!\n\ - Created from your Brawn ability." - check_flags = NONE - bloodcost = 0 - -/datum/action/cooldown/bloodsucker/gangrel/wingslam/ActivatePower() - . = ..() - var/mob/living/user = owner - var/list/choices = list() - for(var/mob/living/carbon/C in view(1, user)) - choices += C - - if(!choices.len) - return - - for(var/mob/living/carbon/M in range(1, user)) - if(!M || !M.Adjacent(user)) - return - if(M.loc == user) - continue - M.visible_message( - span_danger("[user] flaps their wings viciously, sending [M] flying away!"), \ - span_userdanger("You were sent flying by the flap of [user]'s wings!"), - ) - to_chat(user, span_warning("You flap your wings, sending [M] flying!")) - playsound(user.loc, 'sound/weapons/punch4.ogg', 60, TRUE, -1) - M.adjustBruteLoss(10) - M.Knockdown(4 SECONDS) - user.do_attack_animation(M, ATTACK_EFFECT_SMASH) - var/send_dir = get_dir(user, M) - var/turf/turf_thrown_at = get_ranged_target_turf(M, send_dir, 5) - M.throw_at(turf_thrown_at, 5, TRUE, user) - -/* //\\ //\\ -////////////////||\\\\\\\\\\\\\\\\ -\\ Wolf Only // -// Powers \\ -\\\\\\\\\\\\\\\\||//////////////// -*/ - -/datum/action/cooldown/bloodsucker/targeted/feast - name = "Feast" - desc = "DEVOUR THE WEAKLINGS, CAUSE THEM HARM. FEED. ME." - background_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - button_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - button_icon_state = "power_feast" - active_background_icon_state = "wolf_power_on" - base_background_icon_state = "wolf_power_off" - power_explanation = "Feast:\n\ - Feasting on a dead person will give you a satiation point and gib them.\n\ - Satiation points are essential for overcoming frenzy, after gathering 3 you'll turn back to normal.\n\ - Feasting on someone while they are alive will bite them and make them bleed.\n\ - Has a medium recharge time to be helpful in combat.\n\ - There might be some consequences after coming back from frenzy though.." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_AM_COSTLESS_UNCONSCIOUS - purchase_flags = GANGREL_CAN_BUY - bloodcost = 0 - cooldown_time = 10 SECONDS - target_range = 1 - power_activates_immediately = TRUE - prefire_message = "WHOM SHALL BE DEVOURED." - -/datum/action/cooldown/bloodsucker/targeted/feast/CheckValidTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - return isliving(target_atom) - -/datum/action/cooldown/bloodsucker/targeted/feast/CheckCanTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - // Target Type: Living - if(isliving(target_atom)) - return TRUE - -/datum/action/cooldown/bloodsucker/targeted/feast/FireTargetedPower(atom/target_atom) - if(isturf(target_atom)) - return - owner.face_atom(target_atom) - var/mob/living/simple_animal/hostile/bloodsucker/werewolf/user = owner - var/mob/living/carbon/human/target = target_atom - if(target.stat == DEAD) - user.devour(target) - power_activated_sucessfully() - return - user.do_attack_animation(target, ATTACK_EFFECT_BITE) - var/affecting = pick(BODY_ZONE_CHEST, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) - playsound(get_turf(target), 'sound/weapons/bite.ogg', 60, 1, -1) - target.apply_damage(35, BRUTE, affecting, target.run_armor_check(affecting, MELEE, armour_penetration = 10), sharpness = SHARP_EDGED) - target.visible_message(span_danger("[user] takes a large bite out of [target]!"), \ - span_userdanger("[user] takes a large bite out of you!")) - power_activated_sucessfully() - -/datum/action/cooldown/bloodsucker/gangrel/wolfortitude - name = "Wolftitude" - desc = "WITHSTAND THEIR ATTACKS. DESTROY. THEM. ALL!" - button_icon_state = "power_wort" - active_background_icon_state = "wolf_power_on" - base_background_icon_state = "wolf_power_off" - power_explanation = "Fortitude:\n\ - Activating Wolftitude will provide more attack damage, and more overall health.\n\ - It will give you a minor health buff while it stands, but slow you down severely.\n\ - It has a decent cooldown time to allow yourself to turn it off and run away for a while.\n\ - Created from your Fortitude ability." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_AM_COSTLESS_UNCONSCIOUS - purchase_flags = GANGREL_CAN_BUY - bloodcost = 0 - cooldown_time = 8 SECONDS - -/datum/action/cooldown/bloodsucker/gangrel/wolfortitude/ActivatePower() - . = ..() - to_chat(owner, span_notice("Your fur and claws harden, becoming as hard as steel.")) - var/mob/living/simple_animal/hostile/A = owner - A.maxHealth *= 1.2 - A.health *= 1.2 - A.set_varspeed(initial(A.speed) + 2) // slower - A.melee_damage_lower += 10 - A.melee_damage_upper += 10 - -/datum/action/cooldown/bloodsucker/gangrel/wolfortitude/DeactivatePower() - . = ..() - var/mob/living/simple_animal/hostile/A = owner - A.maxHealth /= 1.2 - A.health /= 1.2 - A.set_varspeed(initial(A.speed)) - A.melee_damage_lower -= 10 - A.melee_damage_upper -= 10 - -/datum/action/cooldown/bloodsucker/targeted/pounce - name = "Pounce" - desc = "TACKLE THE LIVING TO THE GROUND. FEAST ON CORPSES." - background_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - button_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - button_icon_state = "power_pounce" - active_background_icon_state = "wolf_power_on" - base_background_icon_state = "wolf_power_off" - power_explanation = "Pounce:\n\ - Click any player to instantly dash at them, knocking them down and paralyzing them for a short while.\n\ - Additionally if they are dead you'll consume their corpse to gain satiation and get closer to leaving frenzy.\n\ - Created from your Predatory Lunge ability." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS - purchase_flags = GANGREL_CAN_BUY - bloodcost = 0 - cooldown_time = 10 SECONDS - target_range = 6 - power_activates_immediately = FALSE - -/datum/action/cooldown/bloodsucker/targeted/pounce/ActivatePower() - . = ..() - var/mob/living/simple_animal/hostile/bloodsucker/werewolf/A = owner - A.icon_state = initial(A.icon_state) + "_pounce" - A.icon_living = initial(A.icon_state) + "_pounce" - A.update_body() - -/datum/action/cooldown/bloodsucker/targeted/pounce/DeactivatePower() - . = ..() - var/mob/living/simple_animal/hostile/bloodsucker/werewolf/A = owner - A.icon_state = initial(A.icon_state) - A.icon_living = initial(A.icon_state) - A.update_body() - -/datum/action/cooldown/bloodsucker/targeted/pounce/FireTargetedPower(atom/target_atom) - . = ..() - var/mob/living/simple_animal/hostile/bloodsucker/werewolf/user = owner - owner.face_atom(target_atom) - if(iscarbon(target_atom)) - var/mob/living/carbon/target = target_atom - var/turf/targeted_turf = get_turf(target) - var/safety = get_dist(user, targeted_turf) * 3 + 1 - var/consequetive_failures = 0 - while(--safety && !target.Adjacent(user)) - if(!step_to(user, targeted_turf)) - consequetive_failures++ - if(consequetive_failures >= 3) // If 3 steps don't work, just stop. - break - if(target.stat == DEAD) - if(!user.Adjacent(target)) - return - user.devour(target) - power_activated_sucessfully() - return - target.Knockdown(6 SECONDS) - target.Paralyze(1 SECONDS) - power_activated_sucessfully() - -/datum/action/cooldown/bloodsucker/targeted/pounce/CheckValidTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - return isliving(target_atom) - -/datum/action/cooldown/bloodsucker/targeted/pounce/CheckCanTarget(atom/target_atom) - // DEFAULT CHECKS (Distance) - . = ..() - // Target Type: Living - if(isliving(target_atom)) - return TRUE - return FALSE - -/datum/action/cooldown/bloodsucker/gangrel/howl - name = "Howl" - desc = "LET THEM KNOW WHAT HUNTS THEM. KNOCKDOWNS AND CONFUSES NEARBY WEAKLINGS." - button_icon_state = "power_howl" - active_background_icon_state = "wolf_power_on" - base_background_icon_state = "wolf_power_off" - power_explanation = "Howl:\n\ - Activating Howl will start up a 2 and a half second charge up.\n\ - After the charge up you'll knockdown anyone adjacent to you.\n\ - Additionally, you'll confuse and deafen anyone in a 3 tile range.\n\ - Created from your Cloak of Darkness ability." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_AM_COSTLESS_UNCONSCIOUS - purchase_flags = GANGREL_CAN_BUY - bloodcost = 0 - cooldown_time = 15 SECONDS - -/datum/action/cooldown/bloodsucker/gangrel/howl/ActivatePower() - . = ..() - var/mob/living/simple_animal/hostile/bloodsucker/werewolf/A = owner - A.visible_message(span_danger("[A] inhales a ton of air!"), span_warning("You prepare to howl!")) - if(!do_after(A, 2.5 SECONDS)) - return - playsound(A.loc, 'yogstation/sound/creatures/darkspawn_howl.ogg', 50, TRUE) - A.visible_message(span_userdanger("[A] lets out a chilling howl!"), span_boldwarning("You howl, confusing and deafening nearby mortals.")) - for(var/mob/target in range(3, A)) - if(target == (A || A.bloodsucker)) - continue - if(IS_BLOODSUCKER(target) || IS_VASSAL(target)) - continue - if(iscarbon(target)) - var/mob/living/carbon/M = target - M.adjust_confusion(15 SECONDS) - M.adjustEarDamage(0, 50) - if(target.Adjacent(A)) - M.Knockdown(4 SECONDS) - M.Paralyze(0.1 SECONDS) - DeactivatePower() - -/datum/action/cooldown/bloodsucker/gangrel/rabidism - name = "Rabidism" - desc = "FLAIL WILLDY, INJURING ALL WHO APPROACH AND SAVAGING STRUCTURES." - button_icon_state = "power_rabid" - active_background_icon_state = "wolf_power_on" - base_background_icon_state = "wolf_power_off" - power_explanation = "Rabidism:\n\ - Rabidism will deal reduced damage to everyone in range including you.\n\ - During Rabidism's ten second rage you'll deal a lot more damage to structures.\n\ - Be aware of it's long cooldown time.\n\ - Created from your Tresspass ability" - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_AM_COSTLESS_UNCONSCIOUS - purchase_flags = GANGREL_CAN_BUY - bloodcost = 0 - cooldown_time = 20 SECONDS - -/datum/action/cooldown/bloodsucker/gangrel/rabidism/ActivatePower() - . = ..() - var/mob/living/simple_animal/hostile/bloodsucker/werewolf/A = owner - A.environment_smash = ENVIRONMENT_SMASH_RWALLS - A.obj_damage *= 3 - addtimer(CALLBACK(src, PROC_REF(DeactivatePower)), 10 SECONDS) - -/datum/action/cooldown/bloodsucker/gangrel/rabidism/ContinueActive() - return TRUE - -/datum/action/cooldown/bloodsucker/gangrel/rabidism/process() - . = ..() - var/mob/living/simple_animal/hostile/bloodsucker/werewolf/A = owner - for(var/mob/living/all_targets in dview(1, get_turf(A))) - if(all_targets == A || all_targets == A.bloodsucker) - continue - A.UnarmedAttack(all_targets) //byongcontrol - -/datum/action/cooldown/bloodsucker/gangrel/rabidism/DeactivatePower() - . = ..() - var/mob/living/simple_animal/hostile/bloodsucker/werewolf/A = owner - A.environment_smash = initial(A.environment_smash) - A.obj_damage = initial(A.obj_damage) - -/datum/action/cooldown/bloodsucker/targeted/tear - name = "Tear" - desc = "Ruthlessly tear into an enemy, dealing massive damage to them if successful." - button_icon_state = "power_tear" - background_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - button_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - active_background_icon_state = "gangrel_power_on" - base_background_icon_state = "gangrel_power_off" - power_explanation = "Tear:\n\ - Tear will make your first attack start up a bleeding process.\n\ - Bleeding process will only work if the target stands still.\n\ - When it's done it will damage the target severely." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_AM_COSTLESS_UNCONSCIOUS - purchase_flags = GANGREL_CAN_BUY - bloodcost = 10 - cooldown_time = 7 SECONDS - var/mob/living/mauled - -/datum/action/cooldown/bloodsucker/targeted/tear/FireTargetedPower(atom/target_atom) - . = ..() - var/mob/living/carbon/human/user = owner - var/mob/living/target = target_atom - user.do_attack_animation(target, ATTACK_EFFECT_CLAW) - if(iscarbon(target)) - var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected)) - playsound(get_turf(target), 'sound/weapons/slash.ogg', 60, TRUE, -1) - target.apply_damage(15, BRUTE, affecting, target.run_armor_check(affecting, MELEE, armour_penetration = 10), sharpness = SHARP_EDGED) - user.visible_message(span_danger("[user] slashes wildly at [target]!"), span_warning("You tear into [target]!")) - mauled = target - Mawl(target) - -/datum/action/cooldown/bloodsucker/targeted/tear/proc/Mawl(mob/living/target) - var/mob/living/carbon/user = owner - - if(!do_after(user, 1 SECONDS, target)) - return - - var/datum/status_effect/saw_bleed/B = target.has_status_effect(STATUS_EFFECT_SAWBLEED) - while(!target.stat) - if(!target.Adjacent(user) || !do_after(user, 0.8 SECONDS, target)) - break - var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected)) - user.do_attack_animation(target, ATTACK_EFFECT_CLAW) - playsound(get_turf(target), 'sound/weapons/slash.ogg', 60, TRUE, -1) - target.apply_damage(5, BRUTE, affecting, target.run_armor_check(affecting, MELEE, armour_penetration = 10), sharpness = SHARP_EDGED) - user.visible_message(span_danger("[user] slashes wildly at [target]!"), span_warning("You continue to eviscerate [target]...")) - if(!B) - B = target.apply_status_effect(STATUS_EFFECT_SAWBLEED) - else - B.add_bleed(B.bleed_buildup) - -/datum/action/cooldown/bloodsucker/targeted/tear/CheckValidTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - return isliving(target_atom) - -/datum/action/cooldown/bloodsucker/targeted/tear/CheckCanTarget(atom/target_atom) - // DEFAULT CHECKS (Distance) - . = ..() - // Target Type: Living - if(isliving(target_atom)) - return TRUE - return FALSE - -/obj/item/clothing/neck/wolfcollar - name = "Wolf Collar" - desc = "Hopefully no more neck snaps!" - icon_state = "collar" - item_state = "collar" - icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - worn_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - armor = list("melee" = 0, "bullet" = 0, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 10, "acid" = 100) - body_parts_covered = NECK|HEAD - -/obj/item/radio/headset/wolfears - name = "Wolf Ears" - desc = "If only you had a encoder to speak through the channels." - icon_state = "ears" - item_state = "ears" - icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - worn_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - armor = list("melee" = 0, "bullet" = 0, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 10, "acid" = 100) - flags_inv = HIDEHAIR|HIDEFACE - alternate_worn_layer = ABOVE_BODY_FRONT_LAYER - body_parts_covered = HEAD - -/obj/item/clothing/gloves/wolfclaws - name = "Wolf Claws" - desc = "Tear them to shreds!" - icon_state = "claws" - item_state = "claws" - icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - worn_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - body_parts_covered = ARMS|HANDS - flags_inv = HIDEJUMPSUIT - var/datum/action/cooldown/bloodsucker/targeted/tear/tearaction = new - -/obj/item/clothing/shoes/wolflegs - name = "Wolf Legs" - desc = "At least they make you go faster." - icon_state = "legs" - item_state = "legs" - icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - worn_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - slowdown = SHOES_SLOWDOWN - 0.5 - body_parts_covered = GROIN|LEGS|FEET - -/obj/item/clothing/shoes/xeno_wraps/wolfdigilegs - name = "Wolf Legs" - desc = "At least they make you go faster. Oh wait you probably didn't mind anyways..." - icon_state = "digilegs" - item_state = "digilegs" - icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - worn_icon = 'icons/mob/actions/actions_gangrel_bloodsucker.dmi' - slowdown = SHOES_SLOWDOWN - 0.5 - xenoshoe = YES_DIGIT - body_parts_covered = GROIN|LEGS|FEET - -/obj/item/clothing/neck/wolfcollar/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, BLOODSUCKER_TRAIT) - -/obj/item/radio/headset/wolfears/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, BLOODSUCKER_TRAIT) - AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS)) - make_syndie() - -/obj/item/clothing/gloves/wolfclaws/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, BLOODSUCKER_TRAIT) - -/obj/item/clothing/shoes/wolflegs/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, BLOODSUCKER_TRAIT) - -/obj/item/clothing/shoes/xeno_wraps/wolfdigilegs/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, BLOODSUCKER_TRAIT) - -/obj/item/clothing/gloves/wolfclaws/equipped(mob/user, slot) - . = ..() - if(!ishuman(user)) - return - if(!IS_BLOODSUCKER(user)) - return - if(slot == ITEM_SLOT_GLOVES) - var/mob/living/carbon/human/H = user - tearaction.Grant(H) - -/obj/item/clothing/gloves/wolfclaws/dropped(mob/user) - . = ..() - if(!ishuman(user)) - return - if(!IS_BLOODSUCKER(user)) - return - var/mob/living/carbon/human/H = user - if(H.get_item_by_slot(ITEM_SLOT_GLOVES) == src) - tearaction.Remove(H) diff --git a/code/modules/antagonists/bloodsuckers/powers/gohome.dm b/code/modules/antagonists/bloodsuckers/powers/gohome.dm deleted file mode 100644 index 7d65fceea0f1..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/gohome.dm +++ /dev/null @@ -1,127 +0,0 @@ -#define GOHOME_START 0 -#define GOHOME_FLICKER_ONE 2 -#define GOHOME_FLICKER_TWO 4 -#define GOHOME_TELEPORT 6 - -/datum/action/cooldown/bloodsucker/gohome - name = "Vanishing Act" - desc = "As dawn aproaches, disperse into mist and return directly to your Lair.
WARNING: You will drop ALL of your possessions if observed by mortals." - button_icon_state = "power_gohome" - active_background_icon_state = "vamp_power_off_oneshot" - base_background_icon_state = "vamp_power_off_oneshot" - power_explanation = "Vanishing Act: \n\ - Activating Vanishing Act will, after a short delay, teleport the user to their Claimed Coffin. \n\ - The power will cancel out if the Claimed Coffin is somehow destroyed. \n\ - Immediately after activating, lights around the user will begin to flicker. \n\ - Once the user teleports to their coffin, in their place will be a Rat or Bat." - power_flags = BP_AM_TOGGLE|BP_AM_SINGLEUSE|BP_AM_STATIC_COOLDOWN - check_flags = BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_STAKED - // You only get this once you've claimed a lair and Sol is near. - purchase_flags = NONE - constant_bloodcost = 2 - bloodcost = 100 - cooldown_time = 100 SECONDS - ///What stage of the teleportation are we in - var/teleporting_stage = GOHOME_START - ///The types of mobs that will drop post-teleportation. - var/static/list/spawning_mobs = list( - /mob/living/simple_animal/mouse = 3, - /mob/living/simple_animal/hostile/retaliate/bat = 1, - ) - -/datum/action/cooldown/bloodsucker/gohome/CanUse(mob/living/carbon/user) - . = ..() - if(!.) - return FALSE - /// Have No Lair (NOTE: You only got this power if you had a lair, so this means it's destroyed) - if(!istype(bloodsuckerdatum_power) || !bloodsuckerdatum_power.coffin) - owner.balloon_alert(owner, "coffin was destroyed!") - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/gohome/ActivatePower() - . = ..() - owner.balloon_alert(owner, "preparing to teleport...") - -/datum/action/cooldown/bloodsucker/gohome/process() - . = ..() - if(!.) - return FALSE - - switch(teleporting_stage) - if(GOHOME_START) - INVOKE_ASYNC(src, PROC_REF(flicker_lights), 3, 2 SECONDS) - if(GOHOME_FLICKER_ONE) - INVOKE_ASYNC(src, PROC_REF(flicker_lights), 4, 4 SECONDS) - if(GOHOME_FLICKER_TWO) - INVOKE_ASYNC(src, PROC_REF(flicker_lights), 4, 6 SECONDS) - if(GOHOME_TELEPORT) - INVOKE_ASYNC(src, PROC_REF(teleport_to_coffin), owner) - teleporting_stage++ - -/datum/action/cooldown/bloodsucker/gohome/ContinueActive(mob/living/user, mob/living/target) - . = ..() - if(!.) - return FALSE - if(!isturf(user.loc)) - return FALSE - if(!bloodsuckerdatum_power.coffin) - to_chat(user, span_warning("Your coffin has been destroyed! You no longer have a destination.")) - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/gohome/proc/flicker_lights(flicker_range, beat_volume) - for(var/obj/machinery/light/nearby_lights in view(flicker_range, get_turf(owner))) - nearby_lights.flicker(5) - playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', beat_volume, TRUE) - -/datum/action/cooldown/bloodsucker/gohome/proc/teleport_to_coffin(mob/living/carbon/user) - var/drop_item = FALSE - var/turf/current_turf = get_turf(user) - // If we aren't in the dark, anyone watching us will cause us to drop out stuff - if(current_turf && current_turf.lighting_object && current_turf.get_lumcount() >= 0.2) - for(var/mob/living/watchers in viewers(world.view, get_turf(user)) - user) - if(!watchers.client) - continue - if(watchers.has_unlimited_silicon_privilege) - continue - if(watchers.eye_blind) - continue - if(!IS_BLOODSUCKER(watchers) && !IS_VASSAL(watchers)) - drop_item = TRUE - break - // Drop all necessary items (handcuffs, legcuffs, items if seen) - if(user.handcuffed) - var/obj/item/handcuffs = user.handcuffed - user.dropItemToGround(handcuffs) - if(user.legcuffed) - var/obj/item/legcuffs = user.legcuffed - user.dropItemToGround(legcuffs) - if(drop_item) - for(var/obj/item/literally_everything in user) - user.dropItemToGround(literally_everything, TRUE) - - playsound(current_turf, 'sound/magic/summon_karp.ogg', 60, TRUE) - var/datum/effect_system/steam_spread/bloodsucker/puff = new /datum/effect_system/steam_spread/bloodsucker() - puff.set_up(3, 0, current_turf) - puff.start() - - /// STEP FIVE: Create animal at prev location - var/mob/living/simple_animal/new_mob = pick(spawning_mobs) - new new_mob(current_turf) - /// TELEPORT: Move to Coffin & Close it! - user.set_resting(TRUE, TRUE, FALSE) - do_teleport(user, bloodsuckerdatum_power.coffin, no_effects = TRUE, forced = TRUE, channel = TELEPORT_CHANNEL_QUANTUM) - bloodsuckerdatum_power.coffin.close(owner) - bloodsuckerdatum_power.coffin.take_contents() - playsound(bloodsuckerdatum_power.coffin.loc, bloodsuckerdatum_power.coffin.close_sound, 15, TRUE, -3) - - DeactivatePower() - -/datum/effect_system/steam_spread/bloodsucker - effect_type = /obj/effect/particle_effect/fluid/smoke/vampsmoke - -#undef GOHOME_START -#undef GOHOME_FLICKER_ONE -#undef GOHOME_FLICKER_TWO -#undef GOHOME_TELEPORT diff --git a/code/modules/antagonists/bloodsuckers/powers/masquerade.dm b/code/modules/antagonists/bloodsuckers/powers/masquerade.dm deleted file mode 100644 index 0b7b43f08b0d..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/masquerade.dm +++ /dev/null @@ -1,135 +0,0 @@ -/** - * # WITHOUT THIS POWER: - * - * - Mid-Blood: SHOW AS PALE - * - Low-Blood: SHOW AS DEAD - * - No Heartbeat - * - Examine shows actual blood - * - Thermal homeostasis (ColdBlooded) - * WITH THIS POWER: - * - Normal body temp -- remove Cold Blooded (return on deactivate) - */ - -/datum/action/cooldown/bloodsucker/masquerade - name = "Masquerade" - desc = "Feign the vital signs of a mortal, and escape both casual and medical notice as the monster you truly are." - button_icon_state = "power_human" - power_explanation = "Masquerade:\n\ - Activating Masquerade will forge your identity to be practically identical to that of a human;\n\ - - You lose nearly all Bloodsucker benefits, including healing, sleep, radiation, crit, virus and cold immunity.\n\ - - Your eyes turn to that of a regular human as your heart begins to beat.\n\ - - You gain a Genetic sequence, and appear to have 100% blood when scanned by a Health Analyzer.\n\ - - You will not appear as Pale when examined. Anything further than Pale, however, will not be hidden.\n\ - At the end of a Masquerade, you will re-gain your Vampiric abilities, as well as lose any Disease & Gene you might have." - power_flags = BP_AM_TOGGLE|BP_AM_STATIC_COOLDOWN - check_flags = BP_CANT_USE_IN_FRENZY|BP_AM_COSTLESS_UNCONSCIOUS - purchase_flags = BLOODSUCKER_CAN_BUY|BLOODSUCKER_DEFAULT_POWER - bloodcost = 10 - cooldown_time = 5 SECONDS - constant_bloodcost = 0.1 - var/list/theqdeld = list() - -/datum/action/cooldown/bloodsucker/masquerade/ActivatePower() - . = ..() - var/mob/living/carbon/user = owner - to_chat(user, span_notice("Your heart beats falsely within your lifeless chest. You may yet pass for a mortal.")) - to_chat(user, span_warning("Your vampiric healing is halted while imitating life.")) - // Remove Clan-specific stuff - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(bloodsuckerdatum.my_clan) - switch(bloodsuckerdatum.my_clan.get_clan()) - if(CLAN_TZIMISCE) - bloodsuckerdatum.antag_hud_name = "bloodsucker" - if(CLAN_GANGREL) - if(bloodsuckerdatum.clanprogress >= 1) // change this if we get more stuff to include other clans - var/obj/item/clothing/neck/neckdrip = user.get_item_by_slot(ITEM_SLOT_NECK) - if(istype(neckdrip, /obj/item/clothing/neck/wolfcollar)) - theqdeld += neckdrip - if(bloodsuckerdatum.clanprogress >= 2) - var/obj/item/earsdrip = user.get_item_by_slot(ITEM_SLOT_EARS) - if(istype(earsdrip, /obj/item/radio/headset/wolfears)) - theqdeld += earsdrip - if(bloodsuckerdatum.clanprogress >= 3) - var/obj/item/clothing/gloves/glovesdrip = user.get_item_by_slot(ITEM_SLOT_GLOVES) - if(istype(glovesdrip, /obj/item/clothing/gloves/wolfclaws)) - theqdeld += glovesdrip - if(bloodsuckerdatum.clanprogress >= 4) - var/obj/item/clothing/shoes/shoesdrip = user.get_item_by_slot(ITEM_SLOT_FEET) - if(istype(shoesdrip , /obj/item/clothing/shoes/wolflegs)) - theqdeld += shoesdrip - QDEL_LIST(theqdeld) - // Remove Bloodsucker traits - user.remove_traits(bloodsuckerdatum_power.bloodsucker_traits, BLOODSUCKER_TRAIT) - // Falsifies Health & Genetic Analyzers - ADD_TRAIT(user, TRAIT_MASQUERADE, BLOODSUCKER_TRAIT) - // Organs - var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES) - eyes.flash_protect = initial(eyes.flash_protect) - var/obj/item/organ/heart/vampheart = user.getorganslot(ORGAN_SLOT_HEART) - vampheart.beating = TRUE - user.apply_status_effect(STATUS_EFFECT_MASQUERADE) - -/datum/action/cooldown/bloodsucker/masquerade/DeactivatePower() - . = ..() // activate = FALSE - var/mob/living/carbon/user = owner - user.remove_status_effect(STATUS_EFFECT_MASQUERADE) - user.add_traits(bloodsuckerdatum_power.bloodsucker_traits, BLOODSUCKER_TRAIT) - REMOVE_TRAIT(user, TRAIT_MASQUERADE, BLOODSUCKER_TRAIT) - // Remove genes, then make unable to get new ones. - user.dna.remove_all_mutations() - // Organs - var/obj/item/organ/heart/vampheart = user.getorganslot(ORGAN_SLOT_HEART) - vampheart.beating = FALSE - var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES) - if(eyes) - eyes.flash_protect = max(initial(eyes.flash_protect) - 1, - 1) - // Remove all diseases - for(var/thing in user.diseases) - var/datum/disease/disease = thing - disease.cure() - // Adds Clan-specific stuff - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(bloodsuckerdatum.my_clan) - switch(bloodsuckerdatum.my_clan.get_clan()) - if(CLAN_TZIMISCE) - bloodsuckerdatum.antag_hud_name = "tzimisce" - if(CLAN_GANGREL) - if(bloodsuckerdatum.clanprogress >= 1) // change this if we get more stuff to include other clans - var/obj/item/clothing/neck/previousdrip = user.get_item_by_slot(ITEM_SLOT_NECK) - user.dropItemToGround(previousdrip) - user.equip_to_slot_or_del(new /obj/item/clothing/neck/wolfcollar(user), ITEM_SLOT_NECK) - if(bloodsuckerdatum.clanprogress >= 2) - var/obj/item/clothing/ears/previousdrip = user.get_item_by_slot(ITEM_SLOT_EARS) - user.dropItemToGround(previousdrip) - user.equip_to_slot_or_del(new /obj/item/radio/headset/wolfears(user), ITEM_SLOT_EARS) - if(bloodsuckerdatum.clanprogress >= 3) - var/obj/item/clothing/gloves/previousdrip = user.get_item_by_slot(ITEM_SLOT_GLOVES) - user.dropItemToGround(previousdrip) - user.equip_to_slot_or_del(new /obj/item/clothing/gloves/wolfclaws(user), ITEM_SLOT_GLOVES) - if(bloodsuckerdatum.clanprogress >= 4) - var/obj/item/clothing/shoes/previousdrip = user.get_item_by_slot(ITEM_SLOT_FEET) - user.dropItemToGround(previousdrip) - user.equip_to_slot_or_del(new /obj/item/clothing/shoes/wolflegs(user), ITEM_SLOT_FEET) - to_chat(user, span_notice("Your heart beats one final time, while your skin dries out and your icy pallor returns.")) - -/** - * # Status effect - * - * This is what the Masquerade power gives, handles their bonuses and gives them a neat icon to tell them they're on Masquerade. - */ - -/datum/status_effect/masquerade - id = "masquerade" - duration = -1 - tick_interval = -1 - alert_type = /atom/movable/screen/alert/status_effect/masquerade - -/atom/movable/screen/alert/status_effect/masquerade - name = "Masquerade" - desc = "You are currently hiding your identity using the Masquerade power. This halts Vampiric healing." - icon = 'icons/mob/actions/actions_bloodsucker.dmi' - icon_state = "power_human" - -/atom/movable/screen/alert/status_effect/masquerade/MouseEntered(location,control,params) - desc = initial(desc) - return ..() diff --git a/code/modules/antagonists/bloodsuckers/powers/olfaction.dm b/code/modules/antagonists/bloodsuckers/powers/olfaction.dm deleted file mode 100644 index f97c5fe9fd69..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/olfaction.dm +++ /dev/null @@ -1,273 +0,0 @@ -#define TRACKING_SCENT (1<<0) -#define TRACKING_BLOOD (1<<1) - -/datum/action/cooldown/bloodsucker/olfaction - name = "Sanguine Olfaction" - desc = "Smells blood." - button_icon_state = "power_olfac" - power_explanation = "Olfaction:\n\ - Here's the sniffer.\n\ - You shouldn't see this text, please scream and cry in the discord chat or ooc about it after the round if you do.\n\ - Or make a bug report." - power_flags = BP_AM_STATIC_COOLDOWN - cooldown_time = 20 SECONDS - -/datum/action/cooldown/bloodsucker/olfaction/acquire_scent - name = "Sanguine Olfaction" - desc = "Acquire a scent from the environment immediately around you to track." - power_explanation = "Sanguine Olfaction:\n\ - Activating this power will search around you in a 1-tile radius for bloodied objects and floors.\n\ - If these objects are covered in blood from another humanoid being, you will see the option to track one of those scents.\n\ - Selecting one of those scents will grant you a new ability to follow that scent. This will create a visible trail to follow to the owner of that blood.\n\ - WARNING: it will be difficult to see around you while following a scent trail due to the the color being drained from all your vision except for the trail and your target." - - var/mob/living/carbon/tracking_target - var/list/mob/living/carbon/possible = list() - - ///the coefficient for disgust stacks and gauging if the air is miasma free enough. synchronizer chromo makes it more miasma tolerant for genetics olfaction - var/sensitivity = 1 - ///whether or not to even check for miasma in the air. since bloodsucker olfac is based on smelling blood, they don't care about miasma - var/sensitive = TRUE - ///tracking blood or scents. blood for bloodsuckers, scent for genetics olfaction - var/tracking_flags = TRACKING_SCENT - - var/datum/action/cooldown/bloodsucker/olfaction/follow_scent/follow = new /datum/action/cooldown/bloodsucker/olfaction/follow_scent() - -/datum/action/cooldown/bloodsucker/olfaction/acquire_scent/Grant(mob/user, tracking) - . = ..() - if(!iscarbon(user)) - to_chat(owner, span_warning("Your olfactory senses aren't developed enough to utilize this ability!")) - qdel(src) - return - if(tracking) - tracking_flags = tracking - -/datum/action/cooldown/bloodsucker/olfaction/acquire_scent/Trigger() - if(!..()) - return FALSE - DeactivatePower() - var/datum/gas_mixture/air = owner.loc.return_air() - if(air.get_moles(GAS_MIASMA) >= 0.1/sensitivity && sensitive) - owner.adjust_disgust(sensitivity * 45) - to_chat(owner, span_warning("With your overly sensitive nose, you get a whiff of stench and feel sick! Try moving to a cleaner area!")) - return - - var/old_target = tracking_target - possible = list() - - var/list/blood_samples = list() - var/list/scents = list() - for(var/obj/samples in range(1, owner)) - if(tracking_flags & TRACKING_BLOOD) - blood_samples |= samples.return_blood_DNA() - if(tracking_flags & TRACKING_SCENT) - scents |= samples.return_scents() - for(var/mob/living/carbon/C in GLOB.carbon_list) - if(blood_samples.Find(C.dna.unique_enzymes) && !possible.Find(C)) - possible |= C - if(IS_BLOODSUCKER(C)) // Bloodsuckers have no scent, and finding their lair with this during Sol would be OP - continue - if(scents[md5(C.dna.unique_identity)] && !possible.Find(C)) - var/datum/job/J = SSjob.GetJob(C.job) - if(!J) - J = new() - var/stink_string = "[J.smells_like] and [C.dna.species.smells_like]" - possible |= stink_string - possible[stink_string] = C - - if(!length(possible)) - to_chat(owner,span_warning("Despite your best efforts, there are no scents to be found here")) - return - tracking_target = tgui_input_list(owner, "Choose a scent to focus in on.", "Scent Tracking", possible) - if(tracking_flags & TRACKING_SCENT) - tracking_target = possible[tracking_target] - if(!tracking_target) - to_chat(owner,span_warning("You decide against focusing on any scents. Instead, you notice your own nose in your peripheral vision. This goes on to remind you of that one time you started breathing manually and couldn't stop. What an awful day that was.")) - return - - if(tracking_target == owner) - to_chat(owner,span_warning("Wait a minute, this scent is familiar. Too familiar. It's yours.")) - return - - if(tracking_target == old_target) - to_chat(owner,span_warning("Wait a minute, this is the same scent as before.")) - return - - if(!follow) - follow = new() - - if(!owner.actions.Find(follow)) - follow.Grant(owner) - - follow = locate(follow) in owner.actions - - follow.tracking_target = tracking_target - - return TRUE - -/datum/action/cooldown/bloodsucker/olfaction/acquire_scent/Remove(mob/M) - follow = locate(follow) in owner.actions - if(follow) - follow.Remove(owner) - return ..() - -/datum/action/cooldown/bloodsucker/olfaction/acquire_scent/sanguine - sensitive = FALSE - tracking_flags = TRACKING_BLOOD - purchase_flags = BLOODSUCKER_CAN_BUY - - follow = new /datum/action/cooldown/bloodsucker/olfaction/follow_scent/sanguine() - -/datum/action/cooldown/bloodsucker/olfaction/acquire_scent/lesser - name = "Transcendent Olfaction" - - background_icon = 'icons/mob/actions/backgrounds.dmi' - background_icon_state = "bg_spell" - active_background_icon_state = "bg_spell" - base_background_icon_state = "bg_spell" - - button_icon = 'icons/mob/actions/actions_spells.dmi' - button_icon_state = "nose" - - buttontooltipstyle = "" - - sensitive = TRUE - tracking_flags = TRACKING_SCENT - power_explanation = "Transcendent Olfaction:\n\ - Activating this power will search all objects and items in a 1-tile radius around you for scents.\n\ - If these objects or items have been used by humanoid beings and still have their scent, you will see the option to track one of those scents.\n\ - Selecting one of those scents will grant you a new ability to follow that scent. This will create a visible trail to follow to the owner of that scent.\n\ - WARNING: it will be difficult to see around you while following a scent trail due to the the color being drained from all your vision except for the trail and your target." - - cooldown_time = 60 SECONDS - - follow = new /datum/action/cooldown/bloodsucker/olfaction/follow_scent/lesser() - -/datum/action/cooldown/bloodsucker/olfaction/follow_scent - name = "Follow the Scent" - desc = "Begin following the scent of your target." - button_icon_state = "power_olfac" - power_explanation = "Follow the Scent:\n\ - Activating this power will create a trail you can follow to the target scent you selected with Olfaction.\n\ - During this time, the target you're tracking will also leave a trail behind them as they move.\n\ - WARNING: The path created for you to follow may bring you to doors you can't open and areas you don't have access to. If this happens, try again later or from a new area. \n\ - Color will be drained from everything in your vision except for the trail and your target.\n\ - Moving on the trail will dissipate each segment as you move along it, but grant you a speed boost.\n\ - This effect will stay active for 20 seconds." - var/mob/living/carbon/sniffer - var/mob/living/carbon/tracking_target - ///which status effect is being applied on use - var/status_effect = STATUS_EFFECT_BLOOD_HUNTER - -/datum/action/cooldown/bloodsucker/olfaction/follow_scent/Grant(mob/user, tracking) - . = ..() - if(!iscarbon(user)) - to_chat(owner, span_warning("Your olfactory senses aren't developed enough to utilize this ability!")) - qdel(src) - return - sniffer = user - if(tracking) - tracking_target = tracking - -/datum/action/cooldown/bloodsucker/olfaction/follow_scent/Trigger() - if(!..()) - return FALSE - DeactivatePower() - if(!tracking_target) - to_chat(owner, span_warning("You're not tracking a scent, but the game thought you were. Something's gone wrong! Report this as a bug.")) - return - if(tracking_target == owner) - to_chat(owner,span_warning("You smell out the trail to yourself. Yep, it's you.")) - return - var/turf/pos = get_turf(tracking_target) - if(usr.z < pos.z) - to_chat(owner,span_warning("The trail leads... way up above you? Huh. They must be really, really far away.")) - return - else if(usr.z > pos.z) - to_chat(owner,span_warning("The trail leads... way down below you? Huh. They must be really, really far away.")) - return - - var/access_card = new /obj/item/card/id/captains_spare() - var/list/trail = get_path_to(owner, get_turf(pos), /turf/proc/Distance_cardinal, 0, 0, 0, /turf/proc/reachableTurftest, access_card, simulated_only = FALSE, get_best_attempt = TRUE) - - if(!trail || trail.len <= 2) - to_chat(owner,span_warning("You can't get a good read on the trail, maybe you should try again from a different spot.")) - return - - var/scent_color - //if there's no scent color we will use the tracked target's mutant_color from their dna, provided it's of a vibrant enough color to see - if(iscarbon(tracking_target)) - var/mob/living/carbon/carbon_target = tracking_target - if(round((ReadHSV(RGBtoHSV(carbon_target.dna.features["mcolor"]))[2]/255)*100) > 40) - var/mcolor = carbon_target.dna.features["mcolor"] - if(length(mcolor) < 6) - mcolor = repeat_string(2, mcolor[1]) + repeat_string(2, mcolor[2])+repeat_string(2, mcolor[3]) - scent_color = sanitize_hexcolor(mcolor, 6, TRUE) - - if(ishuman(tracking_target) && !scent_color) - var/mob/living/carbon/human/human_target = tracking_target - if(round((ReadHSV(RGBtoHSV(human_target.eye_color))[2]/255)*100) > 40) - var/eyecolor = human_target.eye_color - if(length(eyecolor) < 6) - eyecolor = repeat_string(2, eyecolor[1]) + repeat_string(2, eyecolor[2])+repeat_string(2, eyecolor[3]) - scent_color = sanitize_hexcolor(eyecolor, 6, TRUE) - if(!scent_color) - scent_color = sanitize_hexcolor(pick(COLOR_RED, COLOR_ORANGE, COLOR_YELLOW, COLOR_GREEN, COLOR_BLUE, COLOR_PURPLE), 6, TRUE) - if(status_effect == STATUS_EFFECT_BLOOD_HUNTER) - scent_color = COLOR_RED - sniffer.apply_status_effect(status_effect, tracking_target, scent_color) - for(var/i in 1 to (trail.len)) - var/turf/trail_step = trail[i] - var/turf/trail_step_prev - var/turf/trail_step_next - var/invert = FALSE - - var/scent_dir - if (i == 1) - //if we're at the beginning of the list, we'll skip the first location because otherwise the starting scent spawns on top of you instead of in front - continue - else if (i == trail.len) - //if we're at the end of the list, we'll determine the orientation of the last sprite by using the previous location - trail_step_prev = trail[i-1] - scent_dir = get_dir(trail_step_prev, trail_step) - else - //we're not at the end of the list so we're going to use the next and previous location to determine sprite orientation - trail_step_prev = trail[i-1] - trail_step_next = trail[i+1] - if(get_dir(trail_step_prev, trail_step) == EAST || get_dir(trail_step_prev, trail_step) == WEST && get_dir(trail_step, trail_step_next) == NORTH || get_dir(trail_step, trail_step_next) == SOUTH) - //this is neccessary because if you get the direction while travelling horizontally, the resulting directional sprite needs to be flipped - //travelling south and turning east gives you the correct south to east bend - //but travelling east and turning south gives you another south to east bend - invert = TRUE - scent_dir = get_dir(trail_step_next, trail_step_prev) - else - scent_dir = get_dir(trail_step_prev, trail_step_next) - scent_dir = get_dir(trail_step_prev, trail_step_next) - - if((locate(/obj/structure/falsewall) in get_turf(trail_step))) - continue - new /obj/effect/temp_visual/scent_trail(trail_step, scent_dir, sniffer, invert, scent_color) - sleep(0.1 SECONDS) - -/datum/action/cooldown/bloodsucker/olfaction/follow_scent/sanguine - name = "Follow the Scent" - desc = "Begin following the scent of your target." - - status_effect = STATUS_EFFECT_BLOOD_HUNTER - -/datum/action/cooldown/bloodsucker/olfaction/follow_scent/lesser - name = "Follow the Scent" - desc = "Begin following the scent of your target." - - background_icon = 'icons/mob/actions/backgrounds.dmi' - background_icon_state = "bg_spell" - active_background_icon_state = "bg_spell" - base_background_icon_state = "bg_spell" - buttontooltipstyle = "" - - button_icon = 'icons/mob/actions/actions_spells.dmi' - button_icon_state = "nose" - - status_effect = STATUS_EFFECT_SCENT_HUNTER - - cooldown_time = 60 SECONDS diff --git a/code/modules/antagonists/bloodsuckers/powers/recuperate.dm b/code/modules/antagonists/bloodsuckers/powers/recuperate.dm deleted file mode 100644 index 08cb9a819ef1..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/recuperate.dm +++ /dev/null @@ -1,61 +0,0 @@ -/// Used by Vassals -/datum/action/cooldown/bloodsucker/recuperate - name = "Sanguine Recuperation" - desc = "Slowly heals you overtime using your master's blood, in exchange for some of your own blood and effort." - button_icon_state = "power_recup" - power_explanation = "Recuperate:\n\ - Activating this Power will begin to heal your wounds.\n\ - You will heal Brute and Toxin damage, at the cost of Stamina damage, and blood from both you and your Master.\n\ - If you aren't a bloodless race, you will additionally heal Burn damage.\n\ - The power will cancel out if you are incapacitated or dead." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS - purchase_flags = NONE - bloodcost = 1.5 - cooldown_time = 10 SECONDS - -/datum/action/cooldown/bloodsucker/recuperate/CanUse(mob/living/carbon/user) - . = ..() - if(!.) - return - if(user.stat >= DEAD || user.incapacitated()) - to_chat(user, "You are incapacitated...") - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/recuperate/ActivatePower() - . = ..() - to_chat(owner, span_notice("Your muscles clench as your master's immortal blood mixes with your own, knitting your wounds.")) - -/datum/action/cooldown/bloodsucker/recuperate/process() - . = ..() - if(!.) - return - - if(!active) - return - var/mob/living/carbon/user = owner - var/datum/antagonist/vassal/vassaldatum = IS_VASSAL(user) - vassaldatum.master.AddBloodVolume(-1) - user.adjust_jitter(5 SECONDS) - user.adjustStaminaLoss(bloodcost * 1.1) - user.adjustBruteLoss(-2.5) - user.adjustToxLoss(-2, forced = TRUE) - // Plasmamen won't lose blood, they don't have any, so they don't heal from Burn. - if(!LAZYFIND(user.dna.species.species_traits, NOBLOOD)) - user.blood_volume -= bloodcost - user.adjustFireLoss(-1.5) - // Stop Bleeding - if(istype(user) && user.is_bleeding()) - for(var/obj/item/bodypart/part in user.bodyparts) - part.generic_bleedstacks-- - -/datum/action/cooldown/bloodsucker/recuperate/ContinueActive(mob/living/user, mob/living/target) - if(user.stat >= DEAD) - return FALSE - if(user.incapacitated()) - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/recuperate/DeactivatePower() - . = ..() diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/_powers_targeted.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/_powers_targeted.dm deleted file mode 100644 index e73ae5fbfd33..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/_powers_targeted.dm +++ /dev/null @@ -1,92 +0,0 @@ -// NOTE: All Targeted spells are Toggles! We just don't bother checking here. -/datum/action/cooldown/bloodsucker/targeted - power_flags = BP_AM_TOGGLE - - ///If set, how far the target has to be for the power to work. - var/target_range - ///Message sent to chat when clicking on the power, before you use it. - var/prefire_message - ///Most powers happen the moment you click. Some, like Mesmerize, require time and shouldn't cost you if they fail. - var/power_activates_immediately = TRUE - ///Is this power LOCKED due to being used? - var/power_in_use = FALSE - -/// Modify description to add notice that this is aimed. -/datum/action/cooldown/bloodsucker/targeted/New(Target) - desc += "
\[Targeted Power\]" - return ..() - -/datum/action/cooldown/bloodsucker/targeted/Remove(mob/living/remove_from) - . = ..() - if(remove_from.click_intercept == src) - unset_click_ability(remove_from) - -/datum/action/cooldown/bloodsucker/targeted/Trigger(trigger_flags, atom/target) - if(active && can_deactivate()) - DeactivatePower() - return FALSE - if(!can_pay_cost(owner) || !CanUse(owner, trigger_flags)) - return FALSE - - if(prefire_message) - to_chat(owner, span_announce("[prefire_message]")) - - ActivatePower(trigger_flags) - if(target) - return InterceptClickOn(owner, null, target) - - return set_click_ability(owner) - -/datum/action/cooldown/bloodsucker/targeted/DeactivatePower() - if(power_flags & BP_AM_TOGGLE) - STOP_PROCESSING(SSprocessing, src) - active = FALSE - build_all_button_icons() - unset_click_ability(owner) -// ..() // we don't want to pay cost here - -/// Check if target is VALID (wall, turf, or character?) -/datum/action/cooldown/bloodsucker/targeted/proc/CheckValidTarget(atom/target_atom) - if(target_atom == owner) - return FALSE - return TRUE - -/// Check if valid target meets conditions -/datum/action/cooldown/bloodsucker/targeted/proc/CheckCanTarget(atom/target_atom) - if(target_range) - // Out of Range - if(!(target_atom in view(target_range, owner))) - if(target_range > 1) // Only warn for range if it's greater than 1. Brawn doesn't need to announce itself. - owner.balloon_alert(owner, "out of range.") - return FALSE - return istype(target_atom) - -/// Click Target -/datum/action/cooldown/bloodsucker/targeted/proc/click_with_power(atom/target_atom) - // CANCEL RANGED TARGET check - if(power_in_use || !CheckValidTarget(target_atom)) - return FALSE - // Valid? (return true means DON'T cancel power!) - if(!can_pay_cost() || !CanUse(owner) || !CheckCanTarget(target_atom)) - return TRUE - power_in_use = TRUE // Lock us into this ability until it successfully fires off. Otherwise, we pay the blood even if we fail. - FireTargetedPower(target_atom) // We use this instead of ActivatePower(trigger_flags), which has no input - // Skip this part so we can return TRUE right away. - if(power_activates_immediately) - power_activated_sucessfully() // Mesmerize pays only after success. - power_in_use = FALSE - return TRUE - -/// Like ActivatePower, but specific to Targeted (and takes an atom input). We don't use ActivatePower for targeted. -/datum/action/cooldown/bloodsucker/targeted/proc/FireTargetedPower(atom/target_atom) - log_combat(owner, target_atom, "used [name] on") - -/// The power went off! We now pay the cost of the power. -/datum/action/cooldown/bloodsucker/targeted/proc/power_activated_sucessfully() - unset_click_ability(owner) - pay_cost() - StartCooldown() - DeactivatePower() - -/datum/action/cooldown/bloodsucker/targeted/InterceptClickOn(mob/living/caller, params, atom/target) - click_with_power(target) diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm deleted file mode 100644 index 1959d68ad505..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm +++ /dev/null @@ -1,231 +0,0 @@ -/datum/action/cooldown/bloodsucker/targeted/brawn - name = "Brawn" - desc = "Snap restraints, break lockers and doors, or deal terrible damage with your bare hands." - button_icon_state = "power_strength" - power_explanation = "Brawn:\n\ - Click any person to bash into them, break restraints you have or knocking a grabber down. Only one of these can be done per use.\n\ - Punching a Cyborg will heavily EMP them in addition to deal damage.\n\ - At level 3, you get the ability to break closets open, additionally can both break restraints AND knock a grabber down in the same use.\n\ - At level 4, you get the ability to bash airlocks open, as long as they aren't bolted.\n\ - Higher levels will increase the damage and knockdown when punching someone." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS - purchase_flags = BLOODSUCKER_CAN_BUY|VASSAL_CAN_BUY - bloodcost = 8 - cooldown_time = 9 SECONDS - target_range = 1 - power_activates_immediately = TRUE - prefire_message = "Select a target." - ascended_power = /datum/action/cooldown/bloodsucker/targeted/brawn/shadow - -/datum/action/cooldown/bloodsucker/targeted/brawn/ActivatePower(trigger_flags) - // Did we break out of our handcuffs? - if(break_restraints()) - power_activated_sucessfully() - return FALSE - // Did we knock a grabber down? We can only do this while not also breaking restraints if strong enough. - if(level_current >= 3 && escape_puller()) - power_activated_sucessfully() - return FALSE - // Did neither, now we can PUNCH. - return ..() - -// Look at 'biodegrade.dm' for reference -/datum/action/cooldown/bloodsucker/targeted/brawn/proc/break_restraints() - var/mob/living/carbon/human/user = owner - ///Only one form of shackles removed per use - var/used = FALSE - - // Breaks out of lockers - if(istype(user.loc, /obj/structure/closet)) - var/obj/structure/closet/closet = user.loc - if(!istype(closet)) - return FALSE - closet.visible_message( - span_warning("[closet] tears apart as [user] bashes it open from within!"), - span_warning("[closet] tears apart as you bash it open from within!"), - ) - to_chat(user, span_warning("We bash [closet] wide open!")) - addtimer(CALLBACK(src, PROC_REF(break_closet), user, closet), 0.1 SECONDS) - used = TRUE - - // Remove both Handcuffs & Legcuffs - var/obj/cuffs = user.get_item_by_slot(ITEM_SLOT_HANDCUFFED) - var/obj/legcuffs = user.get_item_by_slot(ITEM_SLOT_LEGCUFFED) - if(!used && (istype(cuffs) || istype(legcuffs))) - user.visible_message( - span_warning("[user] discards their restraints like it's nothing!"), - span_warning("We break through our restraints!"), - ) - user.clear_cuffs(cuffs, TRUE, TRUE) - user.clear_cuffs(legcuffs, TRUE, TRUE) - used = TRUE - - // Remove Straightjackets - if(user.wear_suit?.breakouttime && !used) - var/obj/item/clothing/suit/straightjacket = user.get_item_by_slot(ITEM_SLOT_OCLOTHING) - if(!istype(straightjacket)) - return - user.visible_message( - span_warning("[user] rips straight through [user.p_their()] [straightjacket.name]!"), - span_warning("We tear through our [straightjacket.name]!"), - ) - if(straightjacket && user.wear_suit == straightjacket) - new /obj/item/stack/sheet/cloth(user.loc, 3) - qdel(straightjacket) - used = TRUE - - // Did we end up using our ability? If so, play the sound effect and return TRUE - if(used) - playsound(get_turf(user), 'sound/effects/grillehit.ogg', 80, TRUE, -1) - return used - -// This is its own proc because its done twice, to repeat code copypaste. -/datum/action/cooldown/bloodsucker/targeted/brawn/proc/break_closet(mob/living/carbon/human/user, obj/structure/closet/closet) - if(closet) - closet.welded = FALSE - closet.locked = FALSE - closet.broken = TRUE - closet.open() - -/datum/action/cooldown/bloodsucker/targeted/brawn/proc/escape_puller() - if(!owner.pulledby) // || owner.pulledby.grab_state <= GRAB_PASSIVE) - return FALSE - var/mob/pulled_mob = owner.pulledby - var/pull_power = pulled_mob.grab_state - playsound(get_turf(pulled_mob), 'sound/effects/woodhit.ogg', 75, TRUE, -1) - // Knock Down (if Living) - if(isliving(pulled_mob)) - var/mob/living/hit_target = pulled_mob - hit_target.Knockdown(pull_power * 10 + 20) - // Knock Back (before Knockdown, which probably cancels pull) - var/send_dir = get_dir(owner, pulled_mob) - var/turf/turf_thrown_at = get_ranged_target_turf(pulled_mob, send_dir, pull_power) - owner.newtonian_move(send_dir) // Bounce back in 0 G - pulled_mob.throw_at(turf_thrown_at, pull_power, TRUE, owner, FALSE) // Throw distance based on grab state! Harder grabs punished more aggressively. - // /proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null) - log_combat(owner, pulled_mob, "used Brawn power") - owner.visible_message( - span_warning("[owner] tears free of [pulled_mob]'s grasp!"), - span_warning("You shrug off [pulled_mob]'s grasp!"), - ) - owner.pulledby = null // It's already done, but JUST IN CASE. - return TRUE - -/datum/action/cooldown/bloodsucker/targeted/brawn/FireTargetedPower(atom/target_atom) - . = ..() - var/mob/living/user = owner - // Target Type: Mob - if(isliving(target_atom)) - var/mob/living/target = target_atom - var/mob/living/carbon/carbonuser = user - var/hitStrength = carbonuser.dna.species.punchdamagehigh * 1.25 + level_current - // Knockdown! - var/powerlevel = min(5, 1 + level_current) - if(rand(5 + powerlevel) >= 5) - target.visible_message( - span_danger("[user] lands a vicious punch, sending [target] away!"), \ - span_userdanger("[user] has landed a horrifying punch on you, sending you flying!"), - ) - target.Knockdown(min(5 SECONDS, rand(1 SECONDS, 1 SECONDS * powerlevel))) - // Attack! - to_chat(owner, span_warning("You punch [target]!")) - playsound(get_turf(target), 'sound/weapons/punch4.ogg', 60, TRUE, -1) - user.do_attack_animation(target, ATTACK_EFFECT_SMASH) - var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(target.zone_selected)) - var/blocked = target.run_armor_check(affecting, MELEE, armour_penetration = 20) //20 AP, will ignore light armor but not heavy stuff - target.apply_damage(hitStrength, BRUTE, affecting, blocked) - // Knockback - var/send_dir = get_dir(owner, target) - var/turf/turf_thrown_at = get_ranged_target_turf(target, send_dir, powerlevel) - owner.newtonian_move(send_dir) // Bounce back in 0 G - target.throw_at(turf_thrown_at, powerlevel, TRUE, owner) //new /datum/forced_movement(target, get_ranged_target_turf(target, send_dir, (hitStrength / 4)), 1, FALSE) - // Target Type: Cyborg (Also gets the effects above) - if(issilicon(target)) - target.emp_act(EMP_HEAVY) - // Target Type: Locker - else if(istype(target_atom, /obj/structure/closet) && level_current >= 3) - var/obj/structure/closet/target_closet = target_atom - to_chat(user, span_warning("You prepare to bash [target_closet] open...")) - if(!do_after(user, 2.5 SECONDS, target_closet)) - return FALSE - target_closet.visible_message(span_danger("[target_closet] breaks open as [user] bashes it!")) - addtimer(CALLBACK(src, PROC_REF(break_closet), user, target_closet), 1) - playsound(get_turf(user), 'sound/effects/grillehit.ogg', 80, TRUE, -1) - // Target Type: Door - else if(istype(target_atom, /obj/machinery/door) && level_current >= 4) - var/obj/machinery/door/target_airlock = target_atom - playsound(get_turf(user), 'sound/machines/airlock_alien_prying.ogg', 40, TRUE, -1) - to_chat(owner, span_warning("You prepare to tear open [target_airlock]...")) - if(!do_after(user, 2.5 SECONDS, target_airlock)) - return FALSE - if(target_airlock.Adjacent(user)) - target_airlock.visible_message(span_danger("[target_airlock] breaks open as [user] bashes it!")) - user.Stun(10) - user.do_attack_animation(target_airlock, ATTACK_EFFECT_SMASH) - playsound(get_turf(target_airlock), 'sound/effects/bang.ogg', 30, TRUE, -1) - target_airlock.open(2) // open(2) is like a crowbar or jaws of life. - -/datum/action/cooldown/bloodsucker/targeted/brawn/CheckValidTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - return isliving(target_atom) || istype(target_atom, /obj/machinery/door) || istype(target_atom, /obj/structure/closet) - -/datum/action/cooldown/bloodsucker/targeted/brawn/CheckCanTarget(atom/target_atom) - // DEFAULT CHECKS (Distance) - . = ..() - if(!.) // Disable range notice for Brawn. - return FALSE - // Must be outside Closet to target anyone! - if(!isturf(owner.loc)) - return FALSE - // Target Type: Living - if(isliving(target_atom)) - return TRUE - // Target Type: Door - if(istype(target_atom, /obj/machinery/door)) - if(level_current < 4) - owner.balloon_alert(owner, "you need [4 - level_current] more levels!") - return FALSE - return TRUE - // Target Type: Locker - if(istype(target_atom, /obj/structure/closet)) - if(level_current < 3) - owner.balloon_alert(owner, "you need [3 - level_current] more levels!") - return FALSE - return TRUE - return FALSE - -/datum/action/cooldown/bloodsucker/targeted/brawn/shadow - name = "Obliterate" - background_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - active_background_icon_state = "lasombra_power_on" - base_background_icon_state = "lasombra_power_off" - button_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - button_icon_state = "power_obliterate" - additional_text = "Additionally afflicts the target with a shadow curse while in darkness and disables any lights they may possess." - purchase_flags = LASOMBRA_CAN_BUY - ascended_power = null - -/datum/action/cooldown/bloodsucker/targeted/brawn/shadow/FireTargetedPower(atom/target_atom) - var/mob/living/carbon/human/H = target_atom - H.apply_status_effect(STATUS_EFFECT_SHADOWAFFLICTED) - for(var/datum/light_source/LS in target_atom.light_sources) - var/atom/LO = LS.source_atom - if(isitem(LO)) - var/obj/item/I = LO - if(istype(I, /obj/item/clothing/head/helmet/space/hardsuit)) - var/obj/item/clothing/head/helmet/space/hardsuit/HA = I - HA.set_light_on(FALSE) - if(istype(I, /obj/item/clothing/head/helmet/space/plasmaman)) - var/obj/item/clothing/head/helmet/space/plasmaman/PA = I - PA.set_light_on(FALSE) - if(istype(I, /obj/item/flashlight)) - var/obj/item/flashlight/F = I - F.set_light_on(FALSE) - if(istype(LO, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/borg = LO - if(!borg.lamp_cooldown) - borg.smash_headlamp() - . = ..() diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/haste.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/haste.dm deleted file mode 100644 index 556cb14c93c7..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/haste.dm +++ /dev/null @@ -1,130 +0,0 @@ -/* Level 1: Speed to location - * Level 2: Dodge Bullets - * Level 3: Stun People Passed - */ - -/datum/action/cooldown/bloodsucker/targeted/haste - name = "Immortal Haste" - desc = "Dash somewhere with supernatural speed. Those nearby may be knocked away, stunned, or left empty-handed." - button_icon_state = "power_speed" - power_explanation = "Immortal Haste:\n\ - Click anywhere to immediately dash towards that location.\n\ - The Power will not work if you are lying down, in no gravity, or are aggressively grabbed.\n\ - Anyone in your way during your Haste will be knocked down and Payalyzed, moreso if they are using Flow.\n\ - Higher levels will increase the knockdown dealt to enemies." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS - purchase_flags = BLOODSUCKER_CAN_BUY|VASSAL_CAN_BUY - bloodcost = 6 - cooldown_time = 12 SECONDS - target_range = 15 - power_activates_immediately = TRUE - ascended_power = /datum/action/cooldown/bloodsucker/targeted/haste/shadow - /// Current hit, set while power is in use as we can't pass the list as an extra calling argument in registersignal. - var/list/hit = list() - /// If set, uses this speed in deciseconds instead of world.tick_lag - var/speed_override - -/datum/action/cooldown/bloodsucker/targeted/haste/CanUse(mob/living/carbon/user) - . = ..() - if(!.) - return FALSE - // Being Grabbed - if(user.pulledby && user.pulledby.grab_state >= GRAB_AGGRESSIVE) - to_chat(user, span_warning("You're being grabbed!")) - return FALSE - if(!user.has_gravity(user.loc)) //We dont want people to be able to use this to fly around in space - to_chat(user, span_warning("You cannot dash while floating!")) - return FALSE - if(!(user.mobility_flags & MOBILITY_STAND)) - to_chat(user, span_warning("You must be standing to tackle!")) - return FALSE - return TRUE - -/// Anything will do, if it's not me or my square -/datum/action/cooldown/bloodsucker/targeted/haste/CheckValidTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - return target_atom.loc != owner.loc - -/// This is a non-async proc to make sure the power is "locked" until this finishes. -/datum/action/cooldown/bloodsucker/targeted/haste/FireTargetedPower(atom/target_atom) - . = ..() - hit = list() - RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) - var/mob/living/user = owner - var/turf/targeted_turf = isturf(target_atom) ? target_atom : get_turf(target_atom) - // Pulled? Not anymore. - user.pulledby?.stop_pulling() - // Go to target turf - // DO NOT USE WALK TO. - to_chat(owner, span_notice("You dash into the air!")) - playsound(get_turf(owner), 'sound/weapons/punchmiss.ogg', 25, 1, -1) - var/safety = get_dist(user, targeted_turf) * 3 + 1 - var/consequetive_failures = 0 - var/speed = isnull(speed_override)? world.tick_lag : speed_override - while(--safety && (get_turf(user) != targeted_turf)) - var/success = step_towards(user, targeted_turf) //This does not try to go around obstacles. - if(!success) - success = step_to(user, targeted_turf) //this does - if(!success) - if(++consequetive_failures >= 3) //if 3 steps don't work - break //just stop - else - consequetive_failures = 0 - if(user.resting) - user.setDir(turn(user.dir, 90)) //down? spin2win? - if(user.incapacitated(ignore_restraints = TRUE, ignore_grab = TRUE)) //actually down? stop. - break - if(success) //don't sleep if we failed to move. - sleep(speed) - UnregisterSignal(owner, COMSIG_MOVABLE_MOVED) - hit = null - -/datum/action/cooldown/bloodsucker/targeted/haste/proc/on_move() - for(var/mob/living/all_targets in dview(1, get_turf(owner))) - if(!hit[all_targets] && (all_targets != owner)) - hit[all_targets] = TRUE - playsound(all_targets, "sound/weapons/punch[rand(1,4)].ogg", 15, 1, -1) - all_targets.Knockdown(10 + level_current * 5) - all_targets.Paralyze(0.1) - all_targets.spin(10, 1) - if(IS_MONSTERHUNTER(all_targets) && HAS_TRAIT(all_targets, TRAIT_STUNIMMUNE)) - to_chat(all_targets, "Knocked down!") - for(var/datum/action/cooldown/bloodsucker/power in all_targets.actions) - if(power.active) - power.DeactivatePower() - all_targets.set_timed_status_effect(8 SECONDS, /datum/status_effect/jitter, only_if_higher = TRUE) - all_targets.set_timed_status_effect(8 SECONDS, /datum/status_effect/confusion, only_if_higher = TRUE) - all_targets.adjust_timed_status_effect(8 SECONDS, /datum/status_effect/speech/stutter) - all_targets.Knockdown(10 + level_current * 5) // Re-knock them down, the first one didn't work due to stunimmunity - -/datum/action/cooldown/bloodsucker/targeted/haste/shadow - name = "Blow" - background_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - active_background_icon_state = "lasombra_power_on" - base_background_icon_state = "lasombra_power_off" - button_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - button_icon_state = "power_bomb" - additional_text = "Additionally disables lightframes in range and confuses nearby mortals." - purchase_flags = LASOMBRA_CAN_BUY - ascended_power = null - -/datum/action/cooldown/bloodsucker/targeted/haste/shadow/on_move() - . = ..() - var/mob/living/carbon/human/user = owner - for(var/obj/machinery/light/L in range(5, user)) - L.on = FALSE - L.update(0) - L.set_light(0) - for(var/mob/target in range(5, user)) - if(target == user) - continue - if(IS_BLOODSUCKER(target) || IS_VASSAL(target)) - continue - if(iscarbon(target)) - var/mob/living/carbon/M = target - to_chat(M, span_danger("As a figure passes by, you feel your head spike up!")) - M.adjust_confusion(4 SECONDS) - M.adjustEarDamage(0, 15) diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/hecata.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/hecata.dm deleted file mode 100644 index 042c59614e16..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/hecata.dm +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Hecata, the unified clan of death. - * - * Focuses more on their vassals than other clans. - * Has to rely more on vassals for ranks and blood, as they cannot use the blood altar for leveling up and cannot silent feed. - * In exchange, they can raise the dead as temporary vassals to do their bidding, or permanently revive dead existing vassals. - * In addition, they can summon Wraiths (shades) around them for both offense and defense - * And can send messages to vassals anywhere globally via Dark Communion. - * In addition, raising the dead with Necromancy turns them into Sanguine Zombies - * They are pseudo zombies, which have high punch damage and special resistances, but aren't infectious nor can they use guns. - */ -/datum/action/cooldown/bloodsucker/targeted/hecata - purchase_flags = HECATA_CAN_BUY - background_icon = 'icons/mob/actions/actions_hecata_bloodsucker.dmi' - button_icon = 'icons/mob/actions/actions_hecata_bloodsucker.dmi' - active_background_icon_state = "hecata_power_on" - base_background_icon_state = "hecata_power_off" - -/datum/action/cooldown/bloodsucker/targeted/hecata/necromancy - name = "Necromancy" - button_icon_state = "power_necromancy" - desc = "Raise the dead as temporary vassals, or revive a dead vassal as a zombie permanently. Temporary vassals last longer as this ability ranks up. Mindshielded people will take far longer to necromance." - power_explanation = "Necromancy:\n\ - Click on a corpse in order to attempt to resurrect them.\n\ - Non-vassals will become temporary zombies that will follow your orders. Dead vassals are also turned, but last permanently.\n\ - Temporary vassals tend to not last long, their form quickly falling apart, make sure you set them out to do what you want as soon as possible.\n\ - Vassaling people this way does not grant ranks. In addition, after their time is up they will die and no longer be your vassal." - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_UNCONSCIOUS - power_flags = BP_AM_STATIC_COOLDOWN - bloodcost = 35 - cooldown_time = 45 SECONDS - target_range = 1 - prefire_message = "Select a target." - var/list/zombies = list() - var/ghost_searching = FALSE - -/datum/action/cooldown/bloodsucker/targeted/hecata/necromancy/Grant(mob/user) - . = ..() - RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(die)) - -/datum/action/cooldown/bloodsucker/targeted/hecata/necromancy/proc/die() - for(var/dude in zombies) - if(isliving(dude)) //sanity check - var/mob/living/alive = dude - end_necromance(alive) - -/datum/action/cooldown/bloodsucker/targeted/hecata/necromancy/CheckValidTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - return isliving(target_atom) - -/datum/action/cooldown/bloodsucker/targeted/hecata/necromancy/CheckCanTarget(mob/living/carbon/target_atom) - . = ..() - if(!.) - return FALSE - if(ghost_searching) - to_chat(owner, span_notice("Your previous use of necromancy is still doing it's work.")) - return FALSE - // Bloodsucker - if(IS_BLOODSUCKER(target_atom)) - to_chat(owner, span_notice("Bloodsuckers are immune to [src].")) - return FALSE - // Alive - if(target_atom.stat != DEAD) - to_chat(owner, span_notice("[target_atom] is still alive.")) - return FALSE - // Mindshield blocks it unless they're on a persuasion rack - var/obj/structure/bloodsucker/vassalrack/rack = locate() in target_atom.loc - if(HAS_TRAIT(target_atom, TRAIT_MINDSHIELD) && !rack) - to_chat(owner, span_warning("[target_atom]'s mindshield interferes with [src], put [target_atom.p_them()] on a persuasion rack first.")) - return FALSE - // No mind - if(!target_atom.grab_ghost())//only call for a ghost if the existing one is gone - ghost_searching = TRUE - to_chat(owner, span_warning("Attempting to call a spirit from beyond the grave to possess [target_atom].")) - var/list/candidates = pollGhostCandidates("Would you like to play as a hecata zombie?", ROLE_BLOODSUCKER, null, null, poll_time = 5 SECONDS) - ghost_searching = FALSE - if(!LAZYLEN(candidates)) - to_chat(owner, span_warning("You were unable to call a spirit back from the afterlife.")) - return FALSE - var/mob/dead/observer/C = pick(candidates) - target_atom.key = C.key - return TRUE - -/datum/action/cooldown/bloodsucker/targeted/hecata/necromancy/FireTargetedPower(atom/target_atom) - . = ..() - if(!iscarbon(target_atom)) - return - if(!isliving(owner)) - return - var/mob/living/carbon/target = target_atom - var/mob/living/user = owner - if(!IS_BLOODSUCKER(owner)) //sanity check - return - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind?.has_antag_datum(/datum/antagonist/bloodsucker) - - if(target.stat != DEAD) - owner.balloon_alert(owner, "not dead.") - return FALSE - - if(!user.Adjacent(target)) - owner.balloon_alert(owner, "out of range.") - return FALSE - - - owner.balloon_alert(owner, "attempting to revive...") - if(HAS_TRAIT(target, TRAIT_MINDSHIELD)) //if they have a mindshield - owner.balloon_alert(owner, "mindshield detected, this will take longer...") - if(!do_after(user, 12 SECONDS, target)) - return FALSE - for(var/obj/item/implant/mindshield/L in target) - qdel(L) - owner.balloon_alert(owner, "mindshield destroyed, proceeding with revival...") - if(!do_after(user, 6 SECONDS, target)) - return FALSE - - if(IS_MONSTERHUNTER(target)) - owner.balloon_alert(owner, "their body refuses to react...") - DeactivatePower() - return - - bloodsuckerdatum.clanprogress++ //counts a succesful necromancy towards your objective progress - zombify(target) - to_chat(user, span_warning("We revive [target]!")) - if(!IS_VASSAL(target)) - var/living_time = (2 + min(level_current * 3, 15)) MINUTES //in general, they don't last long, make the most of them. - addtimer(CALLBACK(src, PROC_REF(end_necromance), target), living_time) - bloodsuckerdatum.make_vassal(target) - RegisterSignal(target, COMSIG_LIVING_DEATH, PROC_REF(remove_vassal), target) //only remove vassal from those that were initially not one - power_activated_sucessfully() - DeactivatePower() - -/datum/action/cooldown/bloodsucker/targeted/hecata/necromancy/proc/zombify(mob/living/carbon/user) - if(!user.mind || !istype(user)) - return - user.mind.grab_ghost() - zombies |= user - if(user.dna?.species?.type) - zombies[user] = user.dna.species.type - user.set_species(/datum/species/zombie/hecata) //imitation zombies that shamble around and beat people with their fists - user.revive(full_heal = TRUE, admin_revive = TRUE) - user.visible_message(span_danger("[user] suddenly convulses, as [user.p_they()] stagger to their feet and gain a ravenous hunger in [user.p_their()] eyes!"), span_alien("You RISE!")) - playsound(owner.loc, 'sound/hallucinations/far_noise.ogg', 50, 1) - to_chat(user, span_warning("Your broken form is picked up by strange shadows. If you were previously not a vassal, it is unlikely these shadows will be strong enough to keep you going for very long.")) - to_chat(user, span_notice("You are resilient to many things like the vacuum of space, can punch harder, and can take more damage before dropping. However, you are unable to use guns and are slower.")) - -/datum/action/cooldown/bloodsucker/targeted/hecata/necromancy/proc/remove_vassal(mob/living/carbon/user) - if(user.mind) - user.mind.remove_antag_datum(/datum/antagonist/vassal) - -/datum/action/cooldown/bloodsucker/targeted/hecata/necromancy/proc/end_necromance(mob/living/carbon/user) - if(!istype(user) || !(user in zombies)) - return - to_chat(user, span_warning("You feel the shadows around you weaken, your form falling limp like a puppet cut from its strings!")) - user.set_species(zombies[user] ? zombies[user] : /datum/species/krokodil_addict) //go back to original species, or lesser zombie if they somehow didn't have one - user.death() - zombies -= user - -/datum/action/cooldown/bloodsucker/hecata - purchase_flags = HECATA_CAN_BUY - background_icon = 'icons/mob/actions/actions_hecata_bloodsucker.dmi' - button_icon = 'icons/mob/actions/actions_hecata_bloodsucker.dmi' - active_background_icon_state = "hecata_power_on" - base_background_icon_state = "hecata_power_off" - -///summon wraiths (weakened shades) to attack anyone who isn't a zombie. This includes non-zombified vassals. However, you can get around this by zombifying your vassals. -///to do this, you can make someone your favorite vassal, or you can kill them and then revive them with necromancy. -/datum/action/cooldown/bloodsucker/hecata/spiritcall - name = "Spirit Call" - desc = "Summon angry wraiths which will attack anyone whose flesh is still alive. Summon amount increases as this ability levels up." - button_icon_state = "power_spiritcall" - power_explanation = "Spirit Call:\n\ - Summon angry wraiths which enact vengeance from beyond the grave on those still connected to this world.\n\ - Note, that includes any of your vassals who are not undead, as wraiths will seek to kill them too!\n\ - Summons more wraiths as this ability ranks up.\n\ - These wraiths aren't very powerful, and best serve as a distraction, but in a pinch can help in a fight. \n\ - The spirits will eventually return back to their realm if not otherwise destroyed." - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_UNCONSCIOUS - power_flags = BP_AM_STATIC_COOLDOWN - bloodcost = 15 - cooldown_time = 60 SECONDS - ///How many spirits should be summoned when cast - var/num_spirits = 1 - -/datum/action/cooldown/bloodsucker/hecata/spiritcall/vassal //this variant has to exist, as hecata favorite vassals are technically in 'torpor' - check_flags = BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS - -/datum/action/cooldown/bloodsucker/hecata/spiritcall/ActivatePower(mob/user = owner) - . = ..() - switch(level_current) - if(0) - num_spirits = 1 - if(1) - num_spirits = 2 - if(2) - num_spirits = 3 - if(3 to 99) - num_spirits = 4 - var/list/turf/locs = list() - for(var/direction in GLOB.alldirs) //looking for spirit spawns - if(locs.len == num_spirits) //we found the number of spots needed and thats all we need - break - var/turf/T = get_step(owner, direction) //getting a loc in that direction - if(AStar(user, T, /turf/proc/Distance, 1, simulated_only = 0)) // if a path exists, so no dense objects in the way its valid salid - locs += T - // pad with player location - for(var/i = locs.len + 1 to num_spirits) - locs += user.loc - summon_wraiths(locs, user = owner) - cast_effect() // POOF - DeactivatePower() - -/datum/action/cooldown/bloodsucker/hecata/spiritcall/proc/summon_wraiths(list/targets, mob/living/user) - for(var/T in targets) - new /mob/living/simple_animal/hostile/bloodsucker/wraith(T) - -/datum/action/cooldown/bloodsucker/hecata/spiritcall/proc/cast_effect() //same as veil of many faces, makes smoke and stuff when casted - // Effect - playsound(get_turf(owner), 'sound/magic/smoke.ogg', 20, TRUE) - var/datum/effect_system/steam_spread/puff = new /datum/effect_system/steam_spread/bloodsucker() - puff.effect_type = /obj/effect/particle_effect/fluid/smoke/vampsmoke - puff.set_up(3, 0, get_turf(owner)) - puff.attach(owner) //OPTIONAL - puff.start() - owner.spin(8, 1) //Spin around like a loon. - -/datum/action/cooldown/bloodsucker/hecata/communion - name = "Deathly Communion" - desc = "Send a message to all your vassals." - button_icon_state = "power_communion" - power_explanation = "Deathly Communion:\n\ - Send a message directly to all vassals under your control, temporary or permanent.\n\ - They will not be able to respond to you through any supernatural means in the way you can.\n\ - Note, nearby humans can hear you talk when using this.\n\ - The cooldown of Communion will decrease as it levels up." - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY - power_flags = NONE - bloodcost = 8 - cooldown_time = 30 SECONDS - -/datum/action/cooldown/bloodsucker/hecata/communion/ActivatePower() - . = ..() - var/input = sanitize(tgui_input_text(usr, "Enter a message to tell your vassals.", "Voice of Blood")) - if(!input || !IsAvailable()) - return FALSE - deathly_commune(usr, input) - -/datum/action/cooldown/bloodsucker/hecata/communion/proc/deathly_commune(mob/living/user, message) //code from Fulpstation - var/rendered = span_cultlarge("Master [user.real_name] announces: [message]") - user.log_talk(message, LOG_SAY, tag=ROLE_BLOODSUCKER) - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - for(var/datum/antagonist/vassal/receiver as anything in bloodsuckerdatum.vassals) - if(!receiver.owner.current) - continue - var/mob/receiver_mob = receiver.owner.current - to_chat(receiver_mob, rendered) - to_chat(user, rendered) // tell yourself, too. - for(var/mob/dead_mob in GLOB.dead_mob_list) - var/link = FOLLOW_LINK(dead_mob, user) - to_chat(dead_mob, "[link] [rendered]") - DeactivatePower() diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/lasombra.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/lasombra.dm deleted file mode 100644 index 08e30015435f..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/lasombra.dm +++ /dev/null @@ -1,103 +0,0 @@ -/datum/action/cooldown/bloodsucker/targeted/lasombra - name = "Shadow Control" - desc = "Submit shadows to your bidding, making darkness much scarier than before." - background_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - button_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - active_background_icon_state = "lasombra_power_on" - base_background_icon_state = "lasombra_power_off" - button_icon_state = "power_shadow" - power_explanation = "Shadow Control:\n\ - Shadow Control allows you to do different things based on level:\n\ - Level 1 - Click lights to instantly break them;\n\ - Level 2 - Click a person near darkness to shut off any lights they have.\n\ - Level 3 - Click doors to bolt them down for a while, scales with level;\n\ - Level 4 - Click tiles to make a temporary fence on them that blocks movement, scales with level." - power_flags = BP_AM_TOGGLE|BP_AM_STATIC_COOLDOWN - check_flags = BP_CANT_USE_IN_FRENZY - purchase_flags = LASOMBRA_CAN_BUY - bloodcost = 20 - cooldown_time = 15 SECONDS - -/datum/action/cooldown/bloodsucker/targeted/lasombra/CheckValidTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - return isliving(target_atom) || istype(target_atom, /obj/machinery/door) || istype(target_atom, /obj/machinery/light) || isopenturf(target_atom) - -/datum/action/cooldown/bloodsucker/targeted/lasombra/CheckCanTarget(atom/target_atom) - . = ..() - if(isopenturf(target_atom)) - if(level_current < 4) - owner.balloon_alert(owner, "you need [4 - level_current] more levels!") - return FALSE - return TRUE - if(istype(target_atom, /obj/machinery/door)) - if(level_current < 3) - owner.balloon_alert(owner, "you need [3 - level_current] more levels!") - return FALSE - return TRUE - if(isliving(target_atom)) - if(level_current < 2) - owner.balloon_alert(owner, "you need 1 more level!") - return FALSE - return TRUE - if(istype(target_atom, /obj/machinery/light)) - return TRUE - return FALSE - -/datum/action/cooldown/bloodsucker/targeted/lasombra/FireTargetedPower(atom/target_atom) - . = ..() - if(istype(target_atom, /obj/machinery/light)) - for(var/obj/machinery/light/light_bulbs in range(5, target_atom)) //break nearby lights - light_bulbs.on = TRUE - light_bulbs.break_light_tube() - - if(isliving(target_atom)) - var/mob/living/L = target_atom - if(isethereal(L)) - L.emp_act(EMP_LIGHT) - for(var/obj/item/O in L.get_all_contents()) - if(O.light_range && O.light_power) - disintegrate(O) - if(L.pulling && L.pulling.light_range && isitem(L.pulling)) - disintegrate(L.pulling) - - if(istype(target_atom, /obj/machinery/door)) - var/obj/structure/window/shadow/full/friend = new /obj/structure/window/shadow(get_turf(target_atom)) - QDEL_IN(friend, (2 + level_current) SECONDS) - target_atom.visible_message(span_warning("Shadows leap at the door, blocking it!")) - - if(isopenturf(target_atom)) - var/set_direction = get_dir(owner, target_atom) - var/obj/structure/window/shadow/friend = new /obj/structure/window/shadow(get_turf(target_atom)) - friend.dir = set_direction - QDEL_IN(friend, (3 + level_current) SECONDS) - target_atom.visible_message(span_warning("Shadows harden into a translucent wall, blocking passage!")) - -/datum/action/cooldown/bloodsucker/targeted/lasombra/proc/disintegrate(obj/item/O) - if(istype(O, /obj/item/modular_computer/tablet/pda)) - var/obj/item/modular_computer/tablet/pda/PDA = O - PDA.set_light_on(FALSE) - PDA.update_appearance(UPDATE_ICON) - O.visible_message(span_danger("The light in [PDA] shorts out!")) - else - O.visible_message(span_danger("[O] is disintegrated by [src]!")) - O.burn() - playsound(src, 'sound/items/welder.ogg', 50, 1) - -/obj/structure/window/shadow - name = "shadow barrier" - desc = "Basic shadow fence meant to stop idiots like you from passing." - icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - icon_state = "shadowfence" - can_be_unanchored = FALSE - -/obj/structure/window/shadow/can_be_rotated() - return FALSE - -/obj/structure/window/shadow/full - name = "shadow barrier" //original - desc = "Not a window." - icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - icon_state = "shadowlock" - fulltile = TRUE diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/lunge.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/lunge.dm deleted file mode 100644 index a431e7c234f1..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/lunge.dm +++ /dev/null @@ -1,196 +0,0 @@ -/datum/action/cooldown/bloodsucker/targeted/lunge - name = "Predatory Lunge" - desc = "Spring at your target to grapple them without warning, or tear the dead's heart out. Attacks from concealment or the rear may even knock them down if strong enough." - button_icon_state = "power_lunge" - power_explanation = "Predatory Lunge:\n\ - Click any player to start spinning wildly and, after a short delay, dash at them.\n\ - When lunging at someone, you will grab them, immediately starting off at aggressive.\n\ - Riot gear and Monster Hunters are protected and will only be passively grabbed.\n\ - You cannot use the Power if you are already grabbing someone, or are being grabbed.\n\ - If you grab from behind, or from darkness (Cloak of Darkness works), you will knock the target down.\n\ - If used on a dead body, will tear their heart out.\n\ - Higher levels increase the knockdown dealt to enemies.\n\ - At level 4, you will no longer spin, but you will be limited to tackling from only 6 tiles away." - power_flags = NONE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS - purchase_flags = BLOODSUCKER_CAN_BUY|VASSAL_CAN_BUY - bloodcost = 10 - cooldown_time = 10 SECONDS - power_activates_immediately = FALSE - ascended_power = /datum/action/cooldown/bloodsucker/targeted/lunge/shadow - -/datum/action/cooldown/bloodsucker/targeted/lunge/upgrade_power() - . = ..() - //range is lowered when you get stronger. - if(level_current > 3) - target_range = 6 - -/* - * Level 1: Grapple level 2 - * Level 2: Grapple 3 from Behind - * Level 3: Grapple 3 from Shadows - */ - -/datum/action/cooldown/bloodsucker/targeted/lunge/CanUse(mob/living/carbon/user) - . = ..() - if(!.) - return FALSE - // Are we being grabbed? - if(user.pulledby && user.pulledby.grab_state >= GRAB_AGGRESSIVE) - owner.balloon_alert(user, "grabbed!") - return FALSE - if(user.pulling) - owner.balloon_alert(user, "grabbing someone!") - return FALSE - return TRUE - -/// Check: Are we lunging at a person? -/datum/action/cooldown/bloodsucker/targeted/lunge/CheckValidTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - return isliving(target_atom) - -/datum/action/cooldown/bloodsucker/targeted/lunge/CheckCanTarget(atom/target_atom) - // Default Checks - . = ..() - if(!.) - return FALSE - // Check: Turf - var/mob/living/turf_target = target_atom - if(!isturf(turf_target.loc)) - return FALSE - // Check: can the Bloodsucker even move? - var/mob/living/user = owner - if(!(user.mobility_flags & MOBILITY_STAND) || user.IsImmobilized()) - to_chat(user, span_warning("You need to be standing and aware to lunge!")) - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/targeted/lunge/can_deactivate() - return !(datum_flags & DF_ISPROCESSING) //only if you aren't lunging - -/datum/action/cooldown/bloodsucker/targeted/lunge/FireTargetedPower(atom/target_atom) - . = ..() - owner.face_atom(target_atom) - if(level_current > 3) - do_lunge(target_atom) - return - - prepare_target_lunge(target_atom) - return TRUE - -///Starts processing the power and prepares the lunge by spinning, calls lunge at the end of it. -/datum/action/cooldown/bloodsucker/targeted/lunge/proc/prepare_target_lunge(atom/target_atom) - START_PROCESSING(SSprocessing, src) - owner.balloon_alert(owner, "lunge started!") - //animate them shake - var/base_x = owner.base_pixel_x - var/base_y = owner.base_pixel_y - animate(owner, pixel_x = base_x, pixel_y = base_y, time = 0.1 SECONDS, loop = -1) - for(var/i in 1 to 25) - var/x_offset = base_x + rand(-3, 3) - var/y_offset = base_y + rand(-3, 3) - animate(pixel_x = x_offset, pixel_y = y_offset, time = 0.1 SECONDS) - if(!do_after(owner, 4 SECONDS, timed_action_flags = IGNORE_USER_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(CheckCanTarget), target_atom))) - end_target_lunge(base_x, base_y) - - return FALSE - - end_target_lunge() - do_lunge(target_atom) - return TRUE - -///When preparing to lunge ends, this clears it up. -/datum/action/cooldown/bloodsucker/targeted/lunge/proc/end_target_lunge(base_x, base_y) - animate(owner, pixel_x = base_x, pixel_y = base_y, time = 0.1 SECONDS) - STOP_PROCESSING(SSprocessing, src) - -/datum/action/cooldown/bloodsucker/targeted/lunge/process() - if(!active) //If running SSfasprocess (on cooldown) - return ..() //Manage our cooldown timers - if(prob(75)) - owner.spin(8, 1) - owner.balloon_alert_to_viewers("spins wildly!", "you spin!") - return - do_smoke(0, owner.loc, smoke_type = /obj/effect/particle_effect/fluid/smoke/transparent) - -///Actually lunges the target, then calls lunge end. -/datum/action/cooldown/bloodsucker/targeted/lunge/proc/do_lunge(atom/hit_atom) - var/turf/targeted_turf = get_turf(hit_atom) - - var/safety = get_dist(owner, targeted_turf) * 3 + 1 - var/consequetive_failures = 0 - while(--safety && !hit_atom.Adjacent(owner)) - if(!step_to(owner, targeted_turf)) - consequetive_failures++ - if(consequetive_failures >= 3) // If 3 steps don't work, just stop. - break - - lunge_end(hit_atom, targeted_turf) - -/datum/action/cooldown/bloodsucker/targeted/lunge/proc/lunge_end(atom/hit_atom, turf/target_turf) - power_activated_sucessfully() - // Am I next to my target to start giving the effects? - if(!owner.Adjacent(hit_atom)) - return - - var/mob/living/user = owner - var/mob/living/carbon/target = hit_atom - - // Did I slip or get knocked unconscious? - if(!(user.mobility_flags & MOBILITY_STAND) || user.incapacitated()) - var/send_dir = get_dir(user, target_turf) - new /datum/forced_movement(user, get_ranged_target_turf(user, send_dir, 1), 1, FALSE) - user.spin(10) - return - // Is my target a Monster hunter? - var/mob/living/carbon/human/H = target - if(IS_MONSTERHUNTER(target) || H?.is_shove_knockdown_blocked()) - owner.balloon_alert(owner, "pushed away!") - target.grabbedby(owner) - return - - owner.balloon_alert(owner, "you lunge at [target]!") - if(target.stat == DEAD) - var/obj/item/bodypart/chest = target.get_bodypart(BODY_ZONE_CHEST) - var/datum/wound/slash/moderate/crit_wound = new - crit_wound.apply_wound(chest) - owner.visible_message( - span_warning("[owner] tears into [target]'s chest!"), - span_warning("You tear into [target]'s chest!")) - - var/obj/item/organ/heart/myheart_now = locate() in target.internal_organs - if(myheart_now) - myheart_now.Remove(target) - user.put_in_hands(myheart_now) - - else - target.grabbedby(owner) - target.grippedby(owner, instant = TRUE) - // Did we knock them down? - if(!is_A_facing_B(target, owner) || owner.alpha <= 40) - target.Knockdown(10 + level_current * 5) - target.Paralyze(0.1) - -/datum/action/cooldown/bloodsucker/targeted/lunge/DeactivatePower() - var/mob/living/O = owner - O.SetImmobilized(0) - return ..() - -/datum/action/cooldown/bloodsucker/targeted/lunge/shadow - name = "Dark Embrace" - background_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - active_background_icon_state = "lasombra_power_on" - base_background_icon_state = "lasombra_power_off" - button_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - button_icon_state = "power_embrace" - additional_text = "Additionally makes the target walk." - purchase_flags = LASOMBRA_CAN_BUY - ascended_power = null - -/datum/action/cooldown/bloodsucker/targeted/lunge/shadow/lunge_end(atom/hit_atom) - . = ..() - var/mob/living/carbon/target = hit_atom - if(target.m_intent != MOVE_INTENT_WALK) - target.toggle_move_intent() diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/mesmerize.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/mesmerize.dm deleted file mode 100644 index ee7869840bfa..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/mesmerize.dm +++ /dev/null @@ -1,146 +0,0 @@ -/** - * MEZMERIZE - * Locks a target in place for a certain amount of time. - * - * Level 2: Additionally mutes - * Level 3: Can be used through face protection - * Level 5: Doesn't need to be facing you anymore - */ - -/datum/action/cooldown/bloodsucker/targeted/mesmerize - name = "Mesmerize" - desc = "Dominate the mind of a mortal who can see your eyes." - button_icon_state = "power_mez" - power_explanation = "Mesmerize:\n\ - Click any player to attempt to mesmerize them. This process takes 5 seconds and will be interrupted on movement.\n\ - You cannot wear anything covering your face, and both parties must be facing eachother. Obviously, both parties need to not be blind. \n\ - If your target is already mesmerized or a Monster Hunter, the Power will fail.\n\ - Once mesmerized, the target will be unable to move for a certain amount of time, scaling with level.\n\ - At level 1, your target will additionally be muted.\n\ - At level 3, you will be able to use the power through items covering your face.\n\ - At level 5, you will be able to mesmerize regardless of your target's direction.\n\ - Higher levels will increase the time of the mesmerize's freeze." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS - purchase_flags = BLOODSUCKER_CAN_BUY|VASSAL_CAN_BUY - bloodcost = 30 - cooldown_time = 20 SECONDS - target_range = 8 - power_activates_immediately = FALSE - prefire_message = "Whom will you subvert to your will?" - ascended_power = /datum/action/cooldown/bloodsucker/targeted/mesmerize/shadow - var/mesmerizingtime = 5 SECONDS - -/datum/action/cooldown/bloodsucker/targeted/mesmerize/CanUse(mob/living/carbon/user) - . = ..() - if(!.) // Default checks - return FALSE - if(!user.getorganslot(ORGAN_SLOT_EYES)) - to_chat(user, span_warning("You have no eyes with which to mesmerize.")) - return FALSE - // Check: Eyes covered? - if(istype(user) && (user.is_eyes_covered() && level_current <= 2) || !isturf(user.loc)) - to_chat(user, span_warning("Your eyes are concealed from sight.")) - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/targeted/mesmerize/CheckValidTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - return isliving(target_atom) - -/datum/action/cooldown/bloodsucker/targeted/mesmerize/CheckCanTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - var/mob/living/current_target = target_atom // We already know it's carbon due to CheckValidTarget() - // No mind - if(!current_target.mind) - to_chat(owner, span_warning("[current_target] is mindless.")) - return FALSE - // Bloodsucker - if(IS_BLOODSUCKER(current_target)) - to_chat(owner, span_notice("Bloodsuckers are immune to [src].")) - return FALSE - // Dead/Unconscious - if(current_target.stat > CONSCIOUS) - to_chat(owner, "[current_target] is not [(current_target.stat == DEAD || HAS_TRAIT(current_target, TRAIT_FAKEDEATH)) ? "alive" : "conscious"].") - return FALSE - // Target has eyes? - if(!current_target.getorganslot(ORGAN_SLOT_EYES)) - to_chat(owner, span_warning("[current_target] has no eyes.")) - return FALSE - // Target blind? - if(current_target.eye_blind > 0) - to_chat(owner, span_warning("[current_target] is blind.")) - return FALSE - //Facing target? - if(!is_A_facing_B(owner, current_target)) // in unsorted.dm - to_chat(owner, span_warning("You must be facing [current_target].")) - return FALSE - // Target facing me? (On the floor, they're facing everyone) - if(((current_target.mobility_flags & MOBILITY_STAND) && !is_A_facing_B(current_target, owner) && level_current <= 4)) - to_chat(owner, span_warning("[current_target] must be facing you.")) - return FALSE - return TRUE - -/datum/action/cooldown/bloodsucker/targeted/mesmerize/FireTargetedPower(atom/target_atom) - . = ..() - var/mob/living/target = target_atom - var/mob/living/user = owner - to_chat(owner, span_notice("Attempting to hypnotically gaze [target]...")) - if(!power_activates_immediately) - if(!do_after(user, mesmerizingtime, target)) - return - - power_activated_sucessfully() // PAY COST! BEGIN COOLDOWN! - var/power_time = 90 + level_current * 15 - if(IS_MONSTERHUNTER(target)) - to_chat(target, span_warning("You feel your eyes burn for a while, but it passes.")) - return - if(HAS_TRAIT_FROM(target, TRAIT_MUTE, BLOODSUCKER_TRAIT)) - to_chat(owner, span_notice("[target] is already in a hypnotic gaze.")) - return - if(iscarbon(target)) - var/mob/living/carbon/mesmerized = target - to_chat(owner, span_notice("Successfully mesmerized [mesmerized].")) - if(level_current >= 1) - ADD_TRAIT(mesmerized, TRAIT_MUTE, BLOODSUCKER_TRAIT) - mesmerized.Immobilize(power_time) - //mesmerized.silent += power_time / 10 // Silent isn't based on ticks. - mesmerized.next_move = world.time + power_time // <--- Use direct change instead. We want an unmodified delay to their next move // mesmerized.changeNext_move(power_time) // check click.dm - mesmerized.notransform = TRUE // <--- Fuck it. We tried using next_move, but they could STILL resist. We're just doing a hard freeze. - addtimer(CALLBACK(src, PROC_REF(end_mesmerize), user, target), power_time) - if(issilicon(target)) - var/mob/living/silicon/mesmerized = target - mesmerized.emp_act(EMP_HEAVY) - DeactivatePower() - -/datum/action/cooldown/bloodsucker/targeted/mesmerize/proc/end_mesmerize(mob/living/user, mob/living/target) - target.notransform = FALSE - REMOVE_TRAIT(target, TRAIT_MUTE, BLOODSUCKER_TRAIT) - // They Woke Up! (Notice if within view) - if(istype(user) && target.stat == CONSCIOUS && (target in view(6, get_turf(user)))) - to_chat(owner, span_warning("[target] snapped out of their trance.")) - -/datum/action/cooldown/bloodsucker/targeted/mesmerize/shadow - name = "Glare" - background_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - active_background_icon_state = "lasombra_power_on" - base_background_icon_state = "lasombra_power_off" - button_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - button_icon_state = "power_glare" - additional_text = "Additionally makes the stun downtime based on distance, being instant when adjacent." - purchase_flags = LASOMBRA_CAN_BUY - ascended_power = null - -/datum/action/cooldown/bloodsucker/targeted/mesmerize/shadow/FireTargetedPower(atom/target_atom) - var/mob/living/target = target_atom - var/mob/living/user = owner - if(target.Adjacent(user)) - power_activates_immediately = TRUE - else - mesmerizingtime = initial(mesmerizingtime) - ((-get_dist(target, user) + 8 )/2) SECONDS //won't screw you up that bad if you miss it barely - power_activates_immediately = FALSE - return ..() diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/trespass.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/trespass.dm deleted file mode 100644 index 4b43aa9cf921..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/trespass.dm +++ /dev/null @@ -1,127 +0,0 @@ -/datum/action/cooldown/bloodsucker/targeted/trespass - name = "Trespass" - desc = "Become mist and advance two tiles in one direction. Useful for skipping past doors and barricades." - button_icon_state = "power_tres" - power_explanation = "Trespass:\n\ - Click anywhere from 1-2 tiles away from you to teleport.\n\ - This power goes through all obstacles except Walls.\n\ - Higher levels decrease the sound played from using the Power, and increase the speed of the transition." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS - purchase_flags = BLOODSUCKER_CAN_BUY|VASSAL_CAN_BUY - bloodcost = 10 - cooldown_time = 7 SECONDS - prefire_message = "Select a destination." - ascended_power = /datum/action/cooldown/bloodsucker/targeted/trespass/shadow - //target_range = 2 - var/turf/target_turf // We need to decide where we're going based on where we clicked. It's not actually the tile we clicked. - var/wallbound = TRUE - var/soliddelay = 0.1 SECONDS - -/datum/action/cooldown/bloodsucker/targeted/trespass/CanUse(mob/living/carbon/user) - . = ..() - if(!.) - return FALSE - if(user.notransform || !get_turf(user)) - return FALSE - return TRUE - - -/datum/action/cooldown/bloodsucker/targeted/trespass/CheckValidTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - // Can't target my tile - if(target_atom == get_turf(owner) || get_turf(target_atom) == get_turf(owner)) - return FALSE - return TRUE // All we care about is destination. Anything you click is fine. - - -/datum/action/cooldown/bloodsucker/targeted/trespass/CheckCanTarget(atom/target_atom) - // NOTE: Do NOT use ..()! We don't want to check distance or anything. - - // Get clicked tile - var/final_turf = isturf(target_atom) ? target_atom : get_turf(target_atom) - - // Are either tiles WALLS? - var/turf/from_turf = get_turf(owner) - var/this_dir // = get_dir(from_turf, target_turf) - for(var/i = 1 to 2) - // Keep Prev Direction if we've reached final turf - if(from_turf != final_turf) - this_dir = get_dir(from_turf, final_turf) // Recalculate dir so we don't overshoot on a diagonal. - from_turf = get_step(from_turf, this_dir) - // ERROR! Wall! - if(iswallturf(from_turf)) - if(wallbound) - var/wallwarning = (i == 1) ? "in the way" : "at your destination" - to_chat(owner, span_warning("There is a wall [wallwarning].")) - return FALSE - if(!wallbound) - to_chat(owner, span_notice("You begin passing through the wall, this will take a while and take more energy.")) - soliddelay = 2 - // Done - target_turf = from_turf - - return TRUE - -/datum/action/cooldown/bloodsucker/targeted/trespass/FireTargetedPower(atom/target_atom) - . = ..() - - // Find target turf, at or below Atom - var/mob/living/carbon/user = owner - var/turf/my_turf = get_turf(owner) - - user.uncuff() - user.visible_message( - span_warning("[user]'s form dissipates into a cloud of mist!"), - span_notice("You dissipate into formless mist."), - ) - // Effect Origin - var/sound_strength = max(60, 70 - level_current * 10) - var/sound_dist = max(-6, -level_current) // world.view is 7, so 7-6 = hearing distance of 1 - playsound(get_turf(owner), 'sound/magic/summon_karp.ogg', sound_strength, 1, sound_dist) - var/datum/effect_system/steam_spread/bloodsucker/puff = new /datum/effect_system/steam_spread() - puff.set_up(3, 0, my_turf) - puff.start() - - var/mist_delay = max(5, 20 * soliddelay - level_current * 2.5) // Level up and do this faster. - - // Freeze Me - user.Stun(mist_delay, ignore_canstun = TRUE) - user.density = FALSE - var/invis_was = user.invisibility - user.invisibility = INVISIBILITY_MAXIMUM - - // Wait... - sleep(mist_delay / 2) - // Move & Freeze - if(isturf(target_turf)) - do_teleport(owner, target_turf, no_effects=TRUE, channel = TELEPORT_CHANNEL_QUANTUM) // in teleport.dm? - user.Stun(mist_delay / 2, ignore_canstun = TRUE) - - // Wait... - sleep(mist_delay / 2) - // Un-Hide & Freeze - user.dir = get_dir(my_turf, target_turf) - user.Stun(mist_delay / 2, ignore_canstun = TRUE) - user.density = 1 - user.invisibility = invis_was - // Effect Destination - playsound(get_turf(owner), 'sound/magic/summon_karp.ogg', sound_strength, 1, sound_dist) - puff = new /datum/effect_system/steam_spread/() - puff.effect_type = /obj/effect/particle_effect/fluid/smoke/vampsmoke - puff.set_up(3, 0, target_turf) - puff.start() - -/datum/action/cooldown/bloodsucker/targeted/trespass/shadow - name = "Manifest" - background_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - active_background_icon_state = "lasombra_power_on" - base_background_icon_state = "lasombra_power_off" - button_icon = 'icons/mob/actions/actions_lasombra_bloodsucker.dmi' - button_icon_state = "power_manifest" - additional_text = "Additionally allows you pass through walls, albeit at a slower rate." - purchase_flags = LASOMBRA_CAN_BUY - wallbound = FALSE - ascended_power = null diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/tzimisce.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/tzimisce.dm deleted file mode 100644 index 7a5cb4209821..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/tzimisce.dm +++ /dev/null @@ -1,182 +0,0 @@ -#define SIZE_SMALL 1 -#define SIZE_MEDIUM 2 -#define SIZE_BIG 4 - -/datum/action/cooldown/bloodsucker/targeted/dice - name = "Dice" - desc = "Slice, cut, sever. The Flesh obeys as my fingers lay touch on it." - background_icon = 'icons/mob/actions/actions_tzimisce_bloodsucker.dmi' - button_icon = 'icons/mob/actions/actions_tzimisce_bloodsucker.dmi' - background_icon_state = "tzimisce_power_off" - active_background_icon_state = "tzimisce_power_on" - base_background_icon_state = "tzimisce_power_off" - button_icon_state = "power_dice" - power_explanation = "Dice:\n\ - Use on a dead corpse to extract muscle from it to be able to feed it to a vassalrack.\n\ - This won't take long and is your primary source of muscle acquiring, necessary for future endeavours.\n\ - This ability takes well to leveling up, higher levels will increase your mastery over a person's flesh while using the ability for it's combat purpose.\n\ - You shouldn't use this on your allies.." - power_flags = BP_AM_TOGGLE|BP_AM_STATIC_COOLDOWN - bloodcost = 10 - purchase_flags = TZIMISCE_CAN_BUY - check_flags = BP_AM_COSTLESS_UNCONSCIOUS - target_range = 1 - cooldown_time = 10 SECONDS - -/obj/item/muscle - name = "muscle" - desc = "Weird flex but ok" - icon = 'icons/mob/actions/actions_tzimisce_bloodsucker.dmi' - icon_state = "muscle_all" - var/size - -/obj/item/muscle/small - name = "small muscle piece" - desc = "This one sure wasn't important to it's owner." - icon_state = "muscle_small" - size = SIZE_SMALL - w_class = WEIGHT_CLASS_TINY - -/obj/item/muscle/medium - name = "medium muscle piece" - desc = "This would make for a great meal if it wasn't still twitching." - icon_state = "muscle_medium" - size = SIZE_MEDIUM - w_class = WEIGHT_CLASS_SMALL - -/obj/item/muscle/big - name = "big muscle piece" - desc = "I'm pretty sure it's owner needed this to live." - icon_state = "muscle_big" - size = SIZE_BIG - -/obj/item/muscle/examine(mob/user) - . = ..() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(IS_BLOODSUCKER(user) && bloodsuckerdatum.my_clan?.control_type == BLOODSUCKER_CONTROL_FLESH) - . += span_cult("It will yield [size] points for ritual usage.") - -/obj/item/muscle/attackby(obj/item/I, mob/user, params) // handles muscle crafting - var/newsize = 0 - var/quantity = 1 - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(!(IS_BLOODSUCKER(user) && bloodsuckerdatum.my_clan?.control_type == BLOODSUCKER_CONTROL_FLESH)) - return - if(istype(I, /obj/item/muscle)) - var/obj/item/muscle/muscle2 = I - newsize = size + muscle2.size - if(newsize > SIZE_BIG) //so you only have to change defines if you want to balance muscles - to_chat(user, span_warning("You can't make [src] any bigger!")) - return - to_chat(user, span_notice("You merge [src] and [muscle2] into a bigger piece.")) - qdel(muscle2) - if(I.sharpness == SHARP_EDGED) - newsize = size / 2 - quantity = 2 - if(newsize < SIZE_SMALL) - to_chat(user, span_warning("You can't cut [src] anymore!")) - return - to_chat(user, span_notice("You cut [src] into smaller pieces.")) - switch(newsize) - if(0) - return ..() - if(SIZE_SMALL) - new /obj/item/muscle/small(user.drop_location()) - if(quantity == 2) // don't want to make it stackable - new /obj/item/muscle/small(user.drop_location()) - if(SIZE_SMALL + 0.5) - new /obj/item/muscle/small(user.drop_location()) - new /obj/item/muscle/medium(user.drop_location()) - if(SIZE_MEDIUM) - new /obj/item/muscle/medium(user.drop_location()) - if(quantity == 2) - new /obj/item/muscle/medium(user.drop_location()) - if(SIZE_MEDIUM + 1) - size += 1 - return - if(SIZE_BIG) - new /obj/item/muscle/big(user.drop_location()) - qdel(src) - -/datum/action/cooldown/bloodsucker/targeted/dice/FireTargetedPower(atom/target_atom) - var/mob/living/target = target_atom - var/mob/living/carbon/user = owner - user.face_atom(target) - if(target.stat != DEAD) - if(iscarbon(target)) - var/mob/living/carbon/Ctarget = target - var/selected_zone = user.zone_selected - var/list/viable_zones = list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM) - if(!viable_zones.Find(selected_zone)) - selected_zone = pick(viable_zones) - var/obj/item/bodypart/target_part = Ctarget.get_bodypart(selected_zone) - user.do_attack_animation(Ctarget, ATTACK_EFFECT_PUNCH) - playsound(usr.loc, "sound/weapons/slice.ogg", 50, TRUE) - if(!target_part) - to_chat(user, span_warning("[Ctarget] has no limb there!")) - Ctarget.adjustBruteLoss(15 * level_current / 2) - return - switch(level_current) - if(0 to 3) - Ctarget.apply_damage(50, STAMINA, selected_zone) - to_chat(user, span_warning("You swiftly disable the nerves in [Ctarget]'s [target_part] with a precise strike.")) - if(3 to 6) - Ctarget.apply_damage(30, STAMINA, selected_zone) - Ctarget.apply_damage(20, BRUTE, selected_zone) - Ctarget.drop_all_held_items() - to_chat(user, span_warning("You hastly damage the ligaments in [Ctarget]'s [target_part] with a fierce blow.")) - if(6 to INFINITY) - if(target_part.dismemberable && (target.getarmor(target_part, MELEE) < 40)) //heavy armor, such as riot armor, stops the dismember - target_part.dismember() - to_chat(user, span_warning("You sever [Ctarget]'s [target_part] with a clean swipe.")) - else - Ctarget.apply_damage(20, STAMINA, selected_zone) - Ctarget.apply_damage(30, BRUTE, selected_zone) - Ctarget.drop_all_held_items() - to_chat(user, span_warning("As [Ctarget]'s [target_part] is too tough to chop in a single action!")) - else - target.adjustBruteLoss(25) - return - playsound(usr.loc, "sound/weapons/slice.ogg", 50, TRUE) - if(!do_after(usr, 2.5 SECONDS, target)) - return - if(ishuman(target)) - var/mob/living/carbon/human/H = target - for(var/obj/item/bodypart/bodypart in H.bodyparts) - if(bodypart.body_part & (HEAD|CHEST)) - if(bodypart.dismemberable) - bodypart.dismember() - qdel(bodypart) - new /obj/item/muscle/medium(H.loc) - else - to_chat(user, span_warning("You can't dismember this [bodypart] of [target]")) - return - if(iscarbon(target)) - var/mob/living/carbon/C = target - for(var/obj/item/bodypart/bodypart in C.bodyparts) - if(!(bodypart.body_part & (HEAD|CHEST))) - if(bodypart.dismemberable) - bodypart.dismember() - qdel(bodypart) - new /obj/item/muscle/small(C.loc) - return - target.gib() - new /obj/item/muscle/medium(target.loc) - -/datum/action/cooldown/bloodsucker/targeted/dice/CheckValidTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - return isliving(target_atom) - -/datum/action/cooldown/bloodsucker/targeted/dice/CheckCanTarget(atom/target_atom) - . = ..() - if(!.) - return FALSE - if(isliving(target_atom)) - return TRUE - return FALSE - -#undef SIZE_SMALL -#undef SIZE_MEDIUM -#undef SIZE_BIG diff --git a/code/modules/antagonists/bloodsuckers/powers/vassal_fold.dm b/code/modules/antagonists/bloodsuckers/powers/vassal_fold.dm deleted file mode 100644 index d71b419def6e..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/vassal_fold.dm +++ /dev/null @@ -1,91 +0,0 @@ -/datum/action/cooldown/bloodsucker/vassal_blood - name = "Help Vassal" - desc = "Bring an ex-Vassal back into the fold." - button_icon_state = "power_torpor" - power_explanation = "Help Vassal:\n\ - Use this power while you have an ex-Vassal grabbed to bring them back into the fold. \ - Right-Click will show the status of all Vassals." - power_flags = NONE - check_flags = NONE - purchase_flags = NONE - bloodcost = 10 - cooldown_time = 10 SECONDS - - ///Bloodbag we have in our hands. - var/obj/item/reagent_containers/blood/bloodbag - ///Weakref to a target we're bringing into the fold. - var/datum/weakref/target_ref - ///If we are analyze or helping - var/analyzing - -/datum/action/cooldown/bloodsucker/vassal_blood/CanUse(mob/living/carbon/user) - . = ..() - if(!.) - return FALSE - var/datum/antagonist/vassal/revenge/revenge_vassal = owner.mind.has_antag_datum(/datum/antagonist/ex_vassal) - if(revenge_vassal) - return FALSE - - if(tgui_alert(owner, "What would you like to do?", "Lost and Found", list("Analyze", "Help")) == "Analyze") - if(!revenge_vassal.ex_vassals.len) - owner.balloon_alert(owner, "no vassals!") - return FALSE - analyzing = TRUE - return TRUE - - if(owner.pulling && isliving(owner.pulling)) - var/mob/living/pulled_target = owner.pulling - var/datum/antagonist/ex_vassal/former_vassal = pulled_target.mind.has_antag_datum(/datum/antagonist/ex_vassal) - if(!former_vassal) - owner.balloon_alert(owner, "not a former vassal!") - return FALSE - target_ref = WEAKREF(owner.pulling) - return TRUE - - var/blood_bag = locate(/obj/item/reagent_containers/blood) in user.held_items - if(!blood_bag) - owner.balloon_alert(owner, "blood bag needed!") - return FALSE - if(istype(blood_bag, /obj/item/reagent_containers/blood/o_minus/bloodsucker)) - owner.balloon_alert(owner, "already bloodsucker blood!") - - bloodbag = blood_bag - return TRUE - -/datum/action/cooldown/bloodsucker/vassal_blood/ActivatePower() - . = ..() - var/datum/antagonist/vassal/revenge/revenge_vassal = owner.mind.has_antag_datum(/datum/antagonist/vassal/revenge) - if(analyzing) - for(var/datum/antagonist/ex_vassal/former_vassals as anything in revenge_vassal.ex_vassals) - var/information = "[former_vassals.owner.current]" - information += " - has [round(COOLDOWN_TIMELEFT(former_vassals, blood_timer) / 600)] minutes left of Blood" - var/turf/open/floor/target_area = get_area(owner) - if(target_area) - information += " - currently at [target_area]." - if(former_vassals.owner.current.stat >= DEAD) - information += " - DEAD." - - to_chat(owner, "[information]") - - DeactivatePower() - return - - if(target_ref) - var/mob/living/target = target_ref.resolve() - var/datum/antagonist/ex_vassal/former_vassal = target.mind.has_antag_datum(/datum/antagonist/ex_vassal) - if(!former_vassal || former_vassal.revenge_vassal) - target_ref = null - return - if(do_after(owner, 5 SECONDS, target)) - former_vassal.return_to_fold(revenge_vassal) - target_ref = null - DeactivatePower() - return - - if(bloodbag) - var/mob/living/living_owner = owner - living_owner.blood_volume -= 150 - QDEL_NULL(bloodbag) - var/obj/item/reagent_containers/blood/o_minus/bloodsucker/new_bag = new(owner.loc) - owner.put_in_active_hand(new_bag) - DeactivatePower() diff --git a/code/modules/antagonists/bloodsuckers/powers/veil.dm b/code/modules/antagonists/bloodsuckers/powers/veil.dm deleted file mode 100644 index 00dc396f3817..000000000000 --- a/code/modules/antagonists/bloodsuckers/powers/veil.dm +++ /dev/null @@ -1,140 +0,0 @@ -/datum/action/cooldown/bloodsucker/veil - name = "Veil of Many Faces" - desc = "Disguise yourself in the illusion of another identity." - button_icon_state = "power_veil" - power_explanation = "Veil of Many Faces:\n\ - Activating Veil of Many Faces will shroud you in smoke and forge you a new identity.\n\ - Your name and appearance will be completely randomized, and turning the ability off again will undo it all.\n\ - Clothes, gear, and Security/Medical HUD status is kept the same while this power is active." - power_flags = BP_AM_TOGGLE - check_flags = BP_CANT_USE_IN_FRENZY - purchase_flags = VASSAL_CAN_BUY|BLOODSUCKER_CAN_BUY|BLOODSUCKER_DEFAULT_POWER - bloodcost = 15 - constant_bloodcost = 0.1 - cooldown_time = 10 SECONDS - // Outfit Vars -// var/list/original_items = list() - // Identity Vars - var/prev_gender - var/prev_skin_tone - var/prev_hair_style - var/prev_facial_hair_style - var/prev_hair_color - var/prev_facial_hair_color - var/prev_underwear - var/prev_undershirt - var/prev_socks - var/prev_disfigured - var/list/prev_features // For lizards and such - -/datum/action/cooldown/bloodsucker/veil/ActivatePower() - . = ..() - cast_effect() // POOF -// if(blahblahblah) -// Disguise_Outfit() - veil_user() - owner.balloon_alert(owner, "veil turned on.") - -/* // Meant to disguise your character's clothing into fake ones. -/datum/action/cooldown/bloodsucker/veil/proc/Disguise_Outfit() - return - // Step One: Back up original items -*/ - -/datum/action/cooldown/bloodsucker/veil/proc/veil_user() - // Change Name/Voice - var/mob/living/carbon/human/user = owner - user.name_override = user.dna.species.random_name(user.gender) - user.name = user.name_override - user.SetSpecialVoice(user.name_override) - to_chat(owner, span_warning("You mystify the air around your person. Your identity is now altered.")) - - // Store Prev Appearance - prev_gender = user.gender - prev_skin_tone = user.skin_tone - prev_hair_style = user.hair_style - prev_facial_hair_style = user.facial_hair_style - prev_hair_color = user.hair_color - prev_facial_hair_color = user.facial_hair_color - prev_underwear = user.underwear - prev_undershirt = user.undershirt - prev_socks = user.socks -// prev_eye_color - prev_disfigured = HAS_TRAIT(user, TRAIT_DISFIGURED) // I was disfigured! //prev_disabilities = user.disabilities - prev_features = user.dna.features - - // Change Appearance - user.gender = pick(MALE, FEMALE, PLURAL) - user.skin_tone = random_skin_tone() - user.hair_style = random_hair_style(user.gender) - user.facial_hair_style = pick(random_facial_hair_style(user.gender), "Shaved") - user.hair_color = "#[random_color()]" - user.facial_hair_color = user.hair_color - user.underwear = random_underwear(user.gender) - user.undershirt = random_undershirt(user.gender) - user.socks = random_socks(user.gender) - //user.eye_color = random_eye_color() - if(prev_disfigured) - REMOVE_TRAIT(user, TRAIT_DISFIGURED, null) - user.dna.features = random_features() - - // Apply Appearance - user.update_body() // Outfit and underware, also body. - user.update_mutant_bodyparts() // Lizard tails etc - user.update_hair() - user.update_body_parts() - -/datum/action/cooldown/bloodsucker/veil/DeactivatePower() - . = ..() - if(!ishuman(owner)) - return - var/mob/living/carbon/human/user = owner - - // Revert Identity - user.UnsetSpecialVoice() - user.name_override = null - user.name = user.real_name - - // Revert Appearance - user.gender = prev_gender - user.skin_tone = prev_skin_tone - user.hair_style = prev_hair_style - user.facial_hair_style = prev_facial_hair_style - user.hair_color = prev_hair_color - user.facial_hair_color = prev_facial_hair_color - user.underwear = prev_underwear - user.undershirt = prev_undershirt - user.socks = prev_socks - - //user.disabilities = prev_disabilities // Restore HUSK, CLUMSY, etc. - if(prev_disfigured) - //We are ASSUMING husk. // user.status_flags |= DISFIGURED // Restore "Unknown" disfigurement - ADD_TRAIT(user, TRAIT_DISFIGURED, TRAIT_HUSK) - user.dna.features = prev_features - - // Apply Appearance - user.update_body() // Outfit and underware, also body. - user.update_hair() - user.update_body_parts() // Body itself, maybe skin color? - - cast_effect() // POOF - owner.balloon_alert(owner, "veil turned off.") - - -// CAST EFFECT // General effect (poof, splat, etc) when you cast. Doesn't happen automatically! -/datum/action/cooldown/bloodsucker/veil/proc/cast_effect() - // Effect - playsound(get_turf(owner), 'sound/magic/smoke.ogg', 20, TRUE) - var/datum/effect_system/steam_spread/puff = new /datum/effect_system/steam_spread/() - puff.effect_type = /obj/effect/particle_effect/fluid/smoke/vampsmoke - puff.set_up(3, 0, get_turf(owner)) - puff.attach(owner) //OPTIONAL - puff.start() - owner.spin(8, 1) //Spin around like a loon. - -/obj/effect/particle_effect/fluid/smoke/vampsmoke - opacity = FALSE - lifetime = 0 - -/obj/effect/particle_effect/fluid/smoke/vampsmoke/fade_out(frames = 6) - ..(frames) diff --git a/code/modules/antagonists/bloodsuckers/structures/bloodsucker_coffin.dm b/code/modules/antagonists/bloodsuckers/structures/bloodsucker_coffin.dm deleted file mode 100644 index a8b39c468ea3..000000000000 --- a/code/modules/antagonists/bloodsuckers/structures/bloodsucker_coffin.dm +++ /dev/null @@ -1,261 +0,0 @@ -/datum/antagonist/bloodsucker/proc/claim_coffin(obj/structure/closet/crate/claimed, area/current_area) - // ALREADY CLAIMED - if(claimed.resident) - if(claimed.resident == owner.current) - to_chat(owner, "This is your [claimed.name].") - else - to_chat(owner, "This [claimed.name] has already been claimed by another.") - return FALSE - if(!LAZYFIND(GLOB.the_station_areas, current_area.type)) - claimed.balloon_alert(owner.current, "not part of station!") - return - // This is my Lair - coffin = claimed - bloodsucker_lair_area = current_area - if(!(/datum/crafting_recipe/vassalrack in owner?.learned_recipes)) - owner.teach_crafting_recipe(/datum/crafting_recipe/vassalrack) - owner.teach_crafting_recipe(/datum/crafting_recipe/candelabrum) - //owner.teach_crafting_recipe(/datum/crafting_recipe/bloodthrone) - owner.teach_crafting_recipe(/datum/crafting_recipe/meatcoffin) - owner.teach_crafting_recipe(/datum/crafting_recipe/staketrap) - owner.teach_crafting_recipe(/datum/crafting_recipe/woodenducky) - if(my_clan?.get_clan() != CLAN_TZIMISCE) // better things to do - owner.teach_crafting_recipe(/datum/crafting_recipe/bloodaltar) - to_chat(owner, span_danger("You learned new recipes - You can view them in the Structure and Weaponry section of the crafting menu!")) - to_chat(owner, span_userdanger("You have claimed the [claimed] as your place of immortal rest! Your lair is now [bloodsucker_lair_area].")) - to_chat(owner, span_announce("Bloodsucker Tip: Find new lair recipes in the structure tab of the Crafting Menu, including the Persuasion Rack for converting crew into Vassals and the Blood Altar which lets you gain two tasks per night to Rank Up.")) - return TRUE - -/obj/structure/closet/crate/coffin/examine(mob/user) - . = ..() - if(user == resident) - . += span_cult("This is your Claimed Coffin.") - . += span_cult("Rest in it while injured to enter Torpor. Entering it with unspent Ranks will allow you to spend one.") - . += span_cult("Alt Click while inside the Coffin to Lock/Unlock.") - . += span_cult("Alt Click while outside of your Coffin to Unclaim it, unwrenching it and all your other structures as a result.") - -/obj/structure/closet/crate/coffin/blackcoffin - name = "black coffin" - desc = "For those departed who are not so dear." - icon_state = "coffin" - icon = 'icons/obj/vamp_obj.dmi' - breakout_time = 30 SECONDS - pry_lid_timer = 20 SECONDS - resistance_flags = NONE - material_drop = /obj/item/stack/sheet/metal - material_drop_amount = 2 - armor = list(MELEE = 50, BULLET = 20, LASER = 30, ENERGY = 0, BOMB = 50, BIO = 0, RAD = 0, FIRE = 70, ACID = 60) - -/obj/structure/closet/crate/coffin/securecoffin - name = "secure coffin" - desc = "For those too scared of having their place of rest disturbed." - icon_state = "securecoffin" - icon = 'icons/obj/vamp_obj.dmi' - open_sound = 'sound/effects/coffin_open.ogg' - close_sound = 'sound/effects/coffin_close.ogg' - breakout_time = 35 SECONDS - pry_lid_timer = 35 SECONDS - resistance_flags = FIRE_PROOF | LAVA_PROOF | ACID_PROOF - material_drop = /obj/item/stack/sheet/metal - material_drop_amount = 2 - armor = list(MELEE = 35, BULLET = 20, LASER = 20, ENERGY = 0, BOMB = 100, BIO = 0, RAD = 100, FIRE = 100, ACID = 100) - -/obj/structure/closet/crate/coffin/meatcoffin - name = "meat coffin" - desc = "When you're ready to meat your maker, the steaks can never be too high." - icon_state = "meatcoffin" - icon = 'icons/obj/vamp_obj.dmi' - resistance_flags = FIRE_PROOF - open_sound = 'sound/effects/footstep/slime1.ogg' - close_sound = 'sound/effects/footstep/slime1.ogg' - breakout_time = 25 SECONDS - pry_lid_timer = 20 SECONDS - material_drop = /obj/item/reagent_containers/food/snacks/meat/slab - material_drop_amount = 3 - armor = list(MELEE = 70, BULLET = 10, LASER = 10, ENERGY = 0, BOMB = 70, BIO = 0, RAD = 0, FIRE = 70, ACID = 60) - -/obj/structure/closet/crate/coffin/metalcoffin - name = "metal coffin" - desc = "A big metal sardine can inside of another big metal sardine can, in space." - icon_state = "metalcoffin" - icon = 'icons/obj/vamp_obj.dmi' - resistance_flags = FIRE_PROOF | LAVA_PROOF - open_sound = 'sound/effects/pressureplate.ogg' - close_sound = 'sound/effects/pressureplate.ogg' - breakout_time = 25 SECONDS - pry_lid_timer = 30 SECONDS - material_drop = /obj/item/stack/sheet/metal - armor = list(MELEE = 40, BULLET = 15, LASER = 50, ENERGY = 0, BOMB = 10, BIO = 0, RAD = 50, FIRE = 70, ACID = 60) - -////////////////////////////////////////////// - -/// NOTE: This can be any coffin that you are resting AND inside of. -/obj/structure/closet/crate/coffin/proc/claim_coffin(mob/living/claimant, area/current_area) - var/datum/antagonist/bloodsucker/bloodsuckerdatum = claimant.mind.has_antag_datum(/datum/antagonist/bloodsucker) - // Successfully claimed? - if(bloodsuckerdatum?.claim_coffin(src, current_area)) - resident = claimant - anchored = TRUE - START_PROCESSING(SSprocessing, src) - -/obj/structure/closet/crate/coffin/examine(mob/user) - . = ..() - if(user == resident) - . += span_cult("This is your Claimed Coffin.") - . += span_cult("Rest in it while injured to enter Torpor. Entering it with unspent Ranks will allow you to spend one.") - . += span_cult("Alt-Click while inside the Coffin to Lock/Unlock.") - . += span_cult("Alt-Click while outside of your Coffin to Unclaim it, unwrenching it and all your other structures as a result.") - -/obj/structure/closet/crate/coffin/Destroy() - unclaim_coffin() - STOP_PROCESSING(SSprocessing, src) - return ..() - -/obj/structure/closet/crate/coffin/process(mob/living/user) - . = ..() - if(!.) - return FALSE - if(user in src) - var/list/turf/area_turfs = get_area_turfs(get_area(src)) - // Create Dirt etc. - var/turf/T_Dirty = pick(area_turfs) - if(T_Dirty && !T_Dirty.density) - // Default: Dirt - // STEP ONE: COBWEBS - // CHECK: Wall to North? - var/turf/check_N = get_step(T_Dirty, NORTH) - if(istype(check_N, /turf/closed/wall)) - // CHECK: Wall to West? - var/turf/check_W = get_step(T_Dirty, WEST) - if(istype(check_W, /turf/closed/wall)) - new /obj/effect/decal/cleanable/cobweb(T_Dirty) - // CHECK: Wall to East? - var/turf/check_E = get_step(T_Dirty, EAST) - if(istype(check_E, /turf/closed/wall)) - new /obj/effect/decal/cleanable/cobweb/cobweb2(T_Dirty) - new /obj/effect/decal/cleanable/dirt(T_Dirty) - -/obj/structure/closet/crate/proc/unclaim_coffin(manual = FALSE) - // Unanchor it (If it hasn't been broken, anyway) - anchored = FALSE - if(!resident || !resident.mind) - return - // Unclaiming - var/datum/antagonist/bloodsucker/bloodsuckerdatum = resident.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(bloodsuckerdatum && bloodsuckerdatum.coffin == src) - bloodsuckerdatum.coffin = null - bloodsuckerdatum.bloodsucker_lair_area = null - for(var/obj/structure/bloodsucker/bloodsucker_structure in get_area(src)) - if(bloodsucker_structure.owner == resident) - bloodsucker_structure.unbolt() - if(manual) - to_chat(resident, span_cultitalic("You have unclaimed your coffin! This also unclaims all your other Bloodsucker structures!")) - else - to_chat(resident, span_cultitalic("You sense that the link with your coffin and your sacred lair, has been broken! You will need to seek another.")) - // Remove resident. Because this object isnt removed from the game immediately (GC?) we need to give them a way to see they don't have a home anymore. - resident = null - -/// You cannot lock in/out a coffin's owner. SORRY. -/obj/structure/closet/crate/coffin/can_open(mob/living/user) - if(!locked) - return ..() - if(user == resident) - if(welded) - welded = FALSE - update_appearance(UPDATE_ICON) - locked = FALSE - return TRUE - playsound(get_turf(src), 'sound/machines/door_locked.ogg', 20, 1) - to_chat(user, span_notice("[src] is locked tight from the inside.")) - -/obj/structure/closet/crate/coffin/close(mob/living/user) - . = ..() - if(!.) - return FALSE - // Only the User can put themself into Torpor. If already in it, you'll start to heal. - if(user in src) - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(!bloodsuckerdatum) - return FALSE - var/area/current_area = get_area(src) - if(!bloodsuckerdatum.coffin && !resident) - switch(tgui_alert(user, "Do you wish to claim this as your coffin? [current_area] will be your lair.", "Claim Lair", list("Yes", "No"))) - if("Yes") - claim_coffin(user, current_area) - if("No") - return - LockMe(user) - //Level up if possible. - if(!bloodsuckerdatum.my_clan) - to_chat(user, span_notice("You must enter a Clan to rank up.")) - return - if(bloodsuckerdatum.my_clan.rank_up_type == BLOODSUCKER_RANK_UP_NORMAL) - bloodsuckerdatum.SpendRank() - // You're in a Coffin, everything else is done, you're likely here to heal. Let's offer them the oppertunity to do so. - bloodsuckerdatum.check_begin_torpor() - return TRUE - -/// You cannot weld or deconstruct an owned coffin. Only the owner can destroy their own coffin. -/obj/structure/closet/crate/coffin/welder_act(mob/living/user, obj/item/tool) - if(!user.combat_mode && resident && resident != user) - to_chat(user, span_notice("This is a much more complex mechanical structure than you thought. You don't know where to begin cutting [src].")) - return TRUE - return ..() - -/obj/structure/closet/crate/coffin/wirecutter_act(mob/living/user, obj/item/tool) - if(!user.combat_mode && resident && resident != user && tool.tool_behaviour == cutting_tool) - to_chat(user, span_notice("This is a much more complex mechanical structure than you thought. You don't know where to begin cutting [src].")) - return TRUE - return ..() - -/obj/structure/closet/crate/coffin/crowbar_act(mob/living/user, obj/item/tool) - if(locked && resident) - user.visible_message( - span_notice("[user] tries to pry the lid off of [src] with [tool]."), - span_notice("You begin prying the lid off of [src] with [tool]. This should take about [DisplayTimeText(pry_lid_timer)].")) - if(!tool.use_tool(src, user, pry_lid_timer)) // Pry speed must be affected by the speed of the tool. - return TRUE - bust_open() - user.visible_message( - span_notice("[user] snaps the door of [src] wide open."), - span_notice("The door of [src] snaps open.")) - return TRUE - return FALSE - -/obj/structure/closet/crate/coffin/wrench_act(mob/living/user, obj/item/tool) - if(anchored && resident) - to_chat(user, span_danger("The coffin won't come unanchored from the floor.[user == resident ? " You can Alt-Click to unclaim and unwrench your Coffin." : ""]")) - return TRUE - return ..() - -/// Distance Check (Inside Of) -/obj/structure/closet/crate/coffin/AltClick(mob/user) - . = ..() - if(user in src) - LockMe(user, !locked) - return - - if(user == resident && user.Adjacent(src)) - balloon_alert(user, "unclaim coffin?") - var/static/list/unclaim_options = list( - "Yes" = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_yes"), - "No" = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_no")) - var/unclaim_response = show_radial_menu(user, src, unclaim_options, radius = 36, require_near = TRUE) - switch(unclaim_response) - if("Yes") - unclaim_coffin(TRUE) - -/obj/structure/closet/crate/proc/LockMe(mob/user, inLocked = TRUE) - if(user == resident) - if(!broken) - locked = inLocked - to_chat(user, span_notice("You flip a secret latch and [locked?"":"un"]lock yourself inside [src].")) - return - // Broken? Let's fix it. - to_chat(resident, span_notice("The secret latch to lock [src] from the inside is broken. You set it back into place...")) - if(!do_after(resident, 5 SECONDS, src)) - to_chat(resident, span_notice("You fail to fix [src]'s mechanism.")) - return - to_chat(resident, span_notice("You fix the mechanism and lock it.")) - broken = FALSE - locked = TRUE diff --git a/code/modules/antagonists/bloodsuckers/structures/bloodsucker_crypt.dm b/code/modules/antagonists/bloodsuckers/structures/bloodsucker_crypt.dm deleted file mode 100644 index d869bbd05ad9..000000000000 --- a/code/modules/antagonists/bloodsuckers/structures/bloodsucker_crypt.dm +++ /dev/null @@ -1,1422 +0,0 @@ -/obj/structure/bloodsucker - icon = 'icons/obj/vamp_obj.dmi' - ///Who owns this structure? - var/mob/living/owner - /* - * # Descriptions - * - * We use vars to add descriptions to items. - * This way we don't have to make a new /examine for each structure - * And it's easier to edit. - */ - var/Ghost_desc - var/Vamp_desc - var/Vassal_desc - var/Hunter_desc - -/obj/structure/bloodsucker/examine(mob/user) - . = ..() - if(!user.mind && Ghost_desc != "") - . += span_cult(Ghost_desc) - if(IS_BLOODSUCKER(user) && Vamp_desc) - if(!owner) - . += span_cult("It is unsecured. Click on [src] while in your lair to secure it in place to get its full potential.") - return - . += span_cult(Vamp_desc) - if(IS_VASSAL(user) && Vassal_desc != "") - . += span_cult(Vassal_desc) - if(IS_MONSTERHUNTER(user) && Hunter_desc != "") - . += span_cult(Hunter_desc) - -/// This handles bolting down the structure. -/obj/structure/bloodsucker/proc/bolt(mob/user) - to_chat(user, span_danger("You have secured [src] in place.")) - to_chat(user, span_announce("* Bloodsucker Tip: Examine [src] to understand how it functions!")) - owner = user - -/// This handles unbolting of the structure. -/obj/structure/bloodsucker/proc/unbolt(mob/user) - to_chat(user, span_danger("You have unsecured [src].")) - owner = null - -/obj/structure/bloodsucker/attackby(obj/item/item, mob/living/user, params) - /// If a Bloodsucker tries to wrench it in place, yell at them. - if(item.tool_behaviour == TOOL_WRENCH && !anchored && IS_BLOODSUCKER(user)) - user.playsound_local(null, 'sound/machines/buzz-sigh.ogg', 40, FALSE, pressure_affected = FALSE) - to_chat(user, span_announce("* Bloodsucker Tip: Examine Bloodsucker structures to understand how they function!")) - return - . = ..() - -/obj/structure/bloodsucker/attack_hand(mob/user, list/modifiers) -// . = ..() // Don't call parent, else they will handle unbuckling. - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - /// Claiming the Rack instead of using it? - if(istype(bloodsuckerdatum) && !owner) - if(!bloodsuckerdatum.bloodsucker_lair_area) - to_chat(user, span_danger("You don't have a lair. Claim a coffin to make that location your lair.")) - return FALSE - if(bloodsuckerdatum.bloodsucker_lair_area != get_area(src)) - to_chat(user, span_danger("You may only activate this structure in your lair: [bloodsuckerdatum.bloodsucker_lair_area].")) - return FALSE - - /// Radial menu for securing your Persuasion rack in place. - to_chat(user, span_notice("Do you wish to secure [src] here?")) - var/static/list/secure_options = list( - "Yes" = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_yes"), - "No" = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_no")) - var/secure_response = show_radial_menu(user, src, secure_options, radius = 36, require_near = TRUE) - if(!secure_response) - return FALSE - switch(secure_response) - if("Yes") - user.playsound_local(null, 'sound/items/ratchet.ogg', 70, FALSE, pressure_affected = FALSE) - bolt(user) - return FALSE - return FALSE - return TRUE - -/obj/structure/bloodsucker/AltClick(mob/user) - . = ..() - if(user == owner && user.Adjacent(src)) - balloon_alert(user, "unbolt [src]?") - var/static/list/unclaim_options = list( - "Yes" = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_yes"), - "No" = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_no") - ) - var/unclaim_response = show_radial_menu(user, src, unclaim_options, radius = 36, require_near = TRUE) - switch(unclaim_response) - if("Yes") - unbolt(user) - -//////////////////////////////////////////////////// - -#define ALTAR_RANKS_PER_DAY 2 -/obj/structure/bloodsucker/bloodaltar - name = "blood altar" - desc = "It is made of marble, lined with basalt, and radiates an unnerving chill that puts your skin on edge." - icon_state = "bloodaltar" - density = TRUE - anchored = FALSE - pass_flags = LETPASSTHROW - can_buckle = FALSE - var/sacrifices = 0 - var/sacrificialtask = FALSE - var/organ_name = "" - var/suckamount = 0 - var/heartamount = 0 - Ghost_desc = "This is a Blood Altar, where bloodsuckers can get two tasks per night to get more ranks." - Vamp_desc = "This is a Blood Altar, which allows you to do two tasks per day to advance your ranks.\n\ - Interact with the Altar by clicking on it after it's bolted to get a task.\n\ - By checking your notes or the chat you can see what task needs to be done.\n\ - Remember you only get two tasks per night." - Vassal_desc = "This is the blood altar, where your master does bounties to advanced their bloodsucking powers.\n\ - Aid your master by bringing them what they need for these bounties or help getting them." - Hunter_desc = "This is a blood altar, where monsters usually practice a sort of bounty system to advanced their powers.\n\ - They normally sacrifice hearts or blood in exchange for these ranks, forcing them to move out of their lair.\n\ - It can only be used twice per night and it needs to be interacted it to be claimed, making bloodsuckers come back twice a night." - -/obj/structure/bloodsucker/bloodaltar/Initialize(mapload) - . = ..() - AddElement(/datum/element/climbable) - -/obj/structure/bloodsucker/bloodaltar/bolt() - . = ..() - anchored = TRUE - -/obj/structure/bloodsucker/bloodaltar/unbolt() - . = ..() - anchored = FALSE - -/obj/structure/bloodsucker/bloodaltar/attack_hand(mob/user, list/modifiers) - . = ..() - if(!.) - return - if(!IS_BLOODSUCKER(user)) //not bloodsucker - to_chat(user, span_warning("You can't figure out how this works.")) - return - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(bloodsuckerdatum.has_task && !check_completion(user)) //not done but has a task? put them on their way - to_chat(user, span_warning("You already have a rank up task!")) - return - if(bloodsuckerdatum.altar_uses >= ALTAR_RANKS_PER_DAY) //used the altar already - to_chat(user, span_notice("You have done all tasks for the night, come back tomorrow for more.")) - return - var/want_rank = tgui_alert(user, "Do you want to gain a task? This will cost 50 Blood.", "Task Manager", list("Yes", "No")) - if(want_rank != "Yes" || QDELETED(src)) - return - generate_task(user) //generate - -/obj/structure/bloodsucker/bloodaltar/proc/generate_task(mob/living/user) - var/task //just like amongus - var/mob/living/carbon/crewmate = user - var/datum/antagonist/bloodsucker/bloodsuckerdatum = crewmate.mind.has_antag_datum(/datum/antagonist/bloodsucker) - suckamount = bloodsuckerdatum.task_blood_required - heartamount = bloodsuckerdatum.task_heart_required - if(!suckamount && !heartamount) // Generate random amounts if we don't already have them set - switch(bloodsuckerdatum.bloodsucker_level + bloodsuckerdatum.bloodsucker_level_unspent) - if(0 to 3) - suckamount = rand(100, 200) - heartamount = rand(1,2) - if(3 to 8) - suckamount = rand(200, 300) - heartamount = rand(1,2) - if(8 to INFINITY) - suckamount = rand(500, 600) - heartamount = rand(5,6) - if(crewmate.blood_volume < 50) - to_chat(user, span_danger("You don't have enough blood to gain a task!")) - return - bloodsuckerdatum.AddBloodVolume(-50) - switch(rand(1, 3)) - if(1,2) - bloodsuckerdatum.task_blood_required = suckamount - task = "Suck [suckamount] units of pure blood." - if(3) - bloodsuckerdatum.task_heart_required = heartamount - task = "Sacrifice [heartamount] hearts by using them on the altar." - sacrificialtask = TRUE - bloodsuckerdatum.task_memory += "Current Rank Up Task: [task]
" - bloodsuckerdatum.has_task = TRUE - to_chat(user, span_boldnotice("You have gained a new Task! [task] Remember to collect it by using the blood altar!")) - -/obj/structure/bloodsucker/bloodaltar/proc/check_completion(mob/living/user) - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(bloodsuckerdatum.task_blood_drank < bloodsuckerdatum.task_blood_required || sacrifices < bloodsuckerdatum.task_heart_required) - return FALSE - bloodsuckerdatum.task_memory = null - bloodsuckerdatum.has_task = FALSE - bloodsuckerdatum.bloodsucker_level_unspent++ - bloodsuckerdatum.altar_uses++ - bloodsuckerdatum.task_blood_drank = 0 - bloodsuckerdatum.task_blood_required = 0 - bloodsuckerdatum.task_heart_required = 0 - sacrifices = 0 - to_chat(user, span_notice("You have sucessfully done a task and gained a rank!")) - sacrificialtask = FALSE - return TRUE - -/obj/structure/bloodsucker/bloodaltar/examine(mob/user) - . = ..() - if(sacrificialtask) - if(sacrifices) - . += span_boldnotice("It currently contains [sacrifices] [organ_name].") - else - return ..() - -/obj/structure/bloodsucker/bloodaltar/attackby(obj/item/H, mob/user, params) - if(!IS_BLOODSUCKER(user) && !IS_VASSAL(user)) - return ..() - if(sacrificialtask) - if(istype(H, /obj/item/organ/heart)) - if(istype(H, /obj/item/organ/heart/gland)) - to_chat(usr, span_warning("This type of organ doesn't have blood to sustain the altar!")) - return ..() - organ_name = H.name - balloon_alert(user, "heart fed!") - qdel(H) - sacrifices++ - return - return ..() -#undef ALTAR_RANKS_PER_DAY - -//////////////////////////////////////////////////// - -/obj/structure/bloodsucker/bloodaltar/restingplace - name = "resting place" - desc = "This seem to hold a bit of significance." - icon_state = "restingplace" - var/awoken = FALSE - can_buckle = TRUE - Ghost_desc = "This is a Resting Place, where Lasombra bloodsucker can ascend their powers." - Vamp_desc = "This is a Resting Place, which allows you to ascend your powers by gaining points using your ranks or blood.\n\ - Interact with the Altar by clicking on it after you have fed it a abyssal essence, acquirable through influences or sacrifices done on it.\n\ - Remember most ascended powers have benefits if used in the dark.\n\ - It only seems to speak to elders of 4 or higher ranks." - Vassal_desc = "This is the resting place, where your master does rituals to ascend their bloodsucking powers.\n\ - Aid your master by bringing them what they need for these or by help getting them." - Hunter_desc = "This is a blood altar, where monsters ascend their powers to shadowy levels.\n\ - They normally need ranks or blood in exchange for power, forcing them to move out of their lair and weakening them." - -/obj/structure/bloodsucker/bloodaltar/restingplace/deconstruct(disassembled = TRUE) - . = ..() - if(awoken) - new /obj/item/bloodsucker/abyssal_essence(loc) - qdel(src) - -/obj/structure/bloodsucker/bloodaltar/restingplace/attackby(obj/item/H, mob/user, params) - if(!IS_BLOODSUCKER(user) && !IS_VASSAL(user)) - return ..() - if(!awoken) - if(istype(H, /obj/item/bloodsucker/abyssal_essence)) - to_chat(usr, span_notice("As you touch [src] with the [H], you start sensing something different coming from [src]!")) - qdel(H) - awoken = TRUE - else - to_chat(user, span_cult("Seems like you need a direct link to the abyss to awaken [src]. Maybe searching a spacial influence would yield something.")) - return - return ..() - -/obj/effect/reality_smash/attack_hand(mob/user, list/modifiers) // this is important - if(!IS_BLOODSUCKER(user)) //only bloodsucker will attack this with their hand - return - if(DOING_INTERACTION(user, src)) - return - if(user.mind in src.siphoners) - balloon_alert(user, "already harvested!") - return - balloon_alert(user, "harvesting...") - if(do_after(user, 10 SECONDS, src)) - user.put_in_hands(new /obj/item/bloodsucker/abyssal_essence) - to_chat(user, span_notice("You finish harvesting the energy of [src]!")) - src.siphoners |= user.mind - -/obj/structure/bloodsucker/bloodaltar/restingplace/attack_hand(mob/user, list/modifiers) - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(!IS_BLOODSUCKER(user)) - return ..() - - if(!anchored) - return ..() - - if(LAZYLEN(buckled_mobs)) - do_sacrifice(buckled_mobs, user) - return - - if(bloodsuckerdatum.my_clan?.control_type != BLOODSUCKER_CONTROL_SHADOWS) - return ..() - - if(bloodsuckerdatum.bloodsucker_level < 4) - to_chat(user, span_warning("Uncontent with your age, the resting place blocks its secrets. (You need to be rank 4)")) - return ..() - - if(bloodsuckerdatum.clanpoints) - var/list/upgradablepowers = list() - var/list/unupgradablepowers = list(/datum/action/cooldown/bloodsucker/feed, /datum/action/cooldown/bloodsucker/masquerade, /datum/action/cooldown/bloodsucker/veil) - for(var/datum/action/cooldown/bloodsucker/power as anything in bloodsuckerdatum.powers) - if(initial(power.purchase_flags) & BLOODSUCKER_CAN_BUY) - upgradablepowers += power - if(is_type_in_list(power, unupgradablepowers)) - upgradablepowers -= power - if(initial(power.ascended_power) == null) - upgradablepowers -= power - - var/datum/action/cooldown/bloodsucker/choice = tgui_input_list(user, "What Power do you wish to ascend?", "Darkness Manager", upgradablepowers) - if(!choice) - return - if((locate(upgradablepowers[choice]) in bloodsuckerdatum.powers)) - return - var/datum/action/cooldown/bloodsucker/granted = new choice.ascended_power - bloodsuckerdatum.BuyPower(granted) - granted.level_current = rand(3, 4) - granted.UpdateDesc() - qdel(choice) - to_chat(user, span_boldnotice("You have ascended [choice]!")) - bloodsuckerdatum.clanpoints-- - return - - if(!awoken) //don't want this to affect power upgrading if you make another one - to_chat(user, span_cult("Seems like you need a direct link to the abyss to awaken [src]. Maybe searching a spacial influence would yield something.")) - return - - icon_state = initial(icon_state) + (awoken ? "_idle" : "_awaken") - update_appearance(UPDATE_ICON) - var/rankspent - switch(bloodsuckerdatum.clanprogress) - if(0) - bloodsuckerdatum.clanprogress++ - bloodsuckerdatum.clanpoints++ - to_chat(user, span_notice("As you touch the [src] you feel a slight pulse flow through you... You have gained a point!")) - return - if(1 to 3) - rankspent = 1 - if(4 to 6) - rankspent = 2 - if(7) - rankspent = 3 - if(8 to INFINITY) - to_chat(user, span_notice("You have evolved all abilities possible.")) - return - var/want_clantask = tgui_alert(user, "Do you want to spend a rank to gain a shadowpoint? This will cost [rankspent] ranks.", "Dark Manager", list("Yes", "No")) - if(want_clantask == "No" || QDELETED(src)) - return - if(bloodsuckerdatum.bloodsucker_level_unspent < rankspent) - var/another_shot = tgui_alert(user, "It seems like you don't have enough ranks, spend 550 blood instead?", "Dark Manager", list("Yes", "No")) - if(another_shot == "No" || QDELETED(src)) - return - if(bloodsuckerdatum.bloodsucker_blood_volume < 550) - to_chat(user, span_danger("You don't have enough blood to gain a shadowpoint!")) - return - bloodsuckerdatum.AddBloodVolume(-550) - else - bloodsuckerdatum.bloodsucker_level_unspent -= rankspent - bloodsuckerdatum.clanpoints++ - bloodsuckerdatum.clanprogress++ - -/obj/structure/bloodsucker/bloodaltar/restingplace/proc/do_sacrifice(list/pig, mob/living/carbon/user) - var/mob/living/carbon/sacrifice = pick(pig) - if(!sacrifice.mind) - balloon_alert(user, "not worthy to sacrifice!") - return - if(sacrifice.stat == DEAD) - balloon_alert(user, "[sacrifice.p_theyre()] already dead...") - return - balloon_alert(user, "starting sacrifice...") - if(!do_after(user, 10 SECONDS, sacrifice)) - balloon_alert(user, "interrupted!") - return - playsound(get_turf(sacrifice), 'sound/weapons/slash.ogg', 50, TRUE, -1) - sacrifice.adjustBruteLoss(200) - balloon_alert(user, "success!") - new /obj/item/bloodsucker/abyssal_essence(get_turf(src)) - -#define METALLIMIT 50 - -/obj/structure/bloodsucker/moldingstone - name = "molding stone" - desc = "Not made of marble, but will have to do." - icon_state = "molding_stone" - anchored = FALSE - density = TRUE - can_buckle = TRUE - buckle_lying = 180 - var/metal = 0 - -/obj/structure/bloodsucker/moldingstone/examine(mob/user) - . = ..() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(bloodsuckerdatum.my_clan?.control_type >= BLOODSUCKER_CONTROL_METAL) - if(metal) - . += span_boldnotice("It currently contains [metal] metal to use in sculpting.") - else - return ..() - -/obj/structure/bloodsucker/moldingstone/bolt() - . = ..() - anchored = TRUE - -/obj/structure/bloodsucker/moldingstone/unbolt() - . = ..() - anchored = FALSE - -/obj/structure/bloodsucker/moldingstone/update_overlays() - . = ..() - switch(metal) - if(1 to 5) - . += "metal" - if(6 to 20) - . += "metal_2" - if(21 to 50) - . += "metal_3" - -/obj/structure/bloodsucker/moldingstone/attackby(obj/item/I, mob/user, params) - if(!anchored) - return - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(!bloodsuckerdatum) - return ..() - if(bloodsuckerdatum.my_clan?.control_type < BLOODSUCKER_CONTROL_METAL) - return ..() - if(istype(I, /obj/item/stack/sheet/metal)) - if(metal >= METALLIMIT) - balloon_alert(user, "full!") - return - var/obj/item/stack/sheet/metal/M = I - if(metal + M.amount > METALLIMIT) - M.use(METALLIMIT - metal) - metal = METALLIMIT - else - metal = M.amount - qdel(M) - balloon_alert(user, "added [metal] metal") - if(istype(I, /obj/item/bloodsucker/chisel)) - start_sculpiting(user) - update_appearance(UPDATE_ICON) - -/obj/structure/bloodsucker/moldingstone/proc/start_sculpiting(mob/living/artist) - if(metal < 10) - balloon_alert(artist, "not enough metal!") - return - var/list/possible_statues = list() - for(var/obj/structure/bloodsucker/bloodstatue/statues_available as anything in subtypesof(/obj/structure/bloodsucker/bloodstatue)) - possible_statues[statues_available::name] = statues_available - var/obj/structure/bloodsucker/bloodstatue/what_type = tgui_input_list(artist, "What kind of statue would you like to make?", "Artist Manual", possible_statues) - if(!do_after(artist, 10 SECONDS, src)) - artist.balloon_alert(artist, "ruined!") - metal -= rand(5, 10) - update_appearance(UPDATE_ICON) - - return - artist.balloon_alert(artist, "done, a masterpiece!") - new what_type(get_turf(src)) - -/obj/structure/bloodsucker/moldingstone/CtrlClick(mob/user) - if(!anchored) - return ..() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(!bloodsuckerdatum) - return ..() - if(bloodsuckerdatum.my_clan?.control_type < BLOODSUCKER_CONTROL_METAL) - return ..() - if(metal) - var/count = input("How much metal would you like to retrieve from [src]?","Fine Metal", metal) as null | num - if(count > METALLIMIT) - count = METALLIMIT - if(count > metal) - count = metal - metal -= count - new /obj/item/stack/sheet/metal(get_turf(user), count) - else - to_chat(user, span_warning("There's no metal to retrieve in [src].")) - update_appearance(UPDATE_ICON) - return TRUE - -#undef METALLIMIT - -/obj/structure/bloodsucker/bloodstatue - name = "bloody countenance" - desc = "It looks upsettingly familiar..." - icon_state = "statue" - -/obj/structure/bloodsucker/bloodstatue/Initialize(mapload) - . = ..() - AddComponent(/datum/component/art, 30) - -/obj/structure/bloodsucker/bloodstatue/bolt() - . = ..() - anchored = TRUE - START_PROCESSING(SSprocessing, src) - -/obj/structure/bloodsucker/bloodstatue/unbolt() - . = ..() - anchored = FALSE - STOP_PROCESSING(SSprocessing, src) - -/obj/structure/bloodsucker/bloodstatue/command - name = "captain bust" - desc = "It fills you with an eerie sense of patriotism." - icon_state = "statue_command" - -/obj/structure/bloodsucker/bloodstatue/command/attack_hand(mob/user, list/modifiers) //get rid of area requirement - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(istype(bloodsuckerdatum) && !owner) - if(!bloodsuckerdatum.bloodsucker_lair_area) - to_chat(user, span_danger("You don't have a lair. Claim a coffin to make that location your lair.")) - return FALSE - to_chat(user, span_notice("Do you wish to secure [src] here?")) - var/static/list/secure_options = list( - "Yes" = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_yes"), - "No" = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_no")) - var/secure_response = show_radial_menu(user, src, secure_options, radius = 36, require_near = TRUE) - if(!secure_response) - return FALSE - switch(secure_response) - if("Yes") - user.playsound_local(null, 'sound/items/ratchet.ogg', 70, FALSE, pressure_affected = FALSE) - bolt(user) - return FALSE - return FALSE - return TRUE - -/obj/structure/bloodsucker/bloodstatue/command/bolt() - . = ..() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner?.mind?.has_antag_datum(/datum/antagonist/bloodsucker) - var/area/current_area = get_area(src) - if(current_area == bloodsuckerdatum.bloodsucker_lair_area) - return - bloodsuckerdatum.bloodsucker_lair_area.turfs_by_zlevel[z] += current_area.turfs_by_zlevel[z] - -/obj/structure/bloodsucker/bloodstatue/command/unbolt() - . = ..() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner?.mind?.has_antag_datum(/datum/antagonist/bloodsucker) - var/area/current_area = get_area(src) - if(current_area == bloodsuckerdatum.bloodsucker_lair_area) - return - bloodsuckerdatum.bloodsucker_lair_area.turfs_to_uncontain_by_zlevel[z] += current_area.turfs_by_zlevel[z] - -/obj/structure/bloodsucker/bloodstatue/greytide - name = "greytider bust" - desc = "Despite its simple attire it looks quite menancing." - icon_state = "statue_assist" - -/obj/structure/bloodsucker/bloodstatue/greytide/process() - for(var/mob/living/carbon/carbon_in_area in get_area(src)) - if(IS_VASSAL(carbon_in_area) || IS_BLOODSUCKER(carbon_in_area) || carbon_in_area.has_status_effect(STATUS_EFFECT_EXPOSED)) - continue - carbon_in_area.apply_status_effect(STATUS_EFFECT_EXPOSED) - -//////////////////////////////////////////////////// - -/*/obj/structure/bloodsucker/bloodportrait - name = "oil portrait" - desc = "A disturbingly familiar face stares back at you. Those reds don't seem to be painted in oil..." -/obj/structure/bloodsucker/bloodbrazier - name = "lit brazier" - desc = "It burns slowly, but doesn't radiate any heat." -/obj/structure/bloodsucker/bloodmirror - name = "faded mirror" - desc = "You get the sense that the foggy reflection looking back at you has an alien intelligence to it."*/ - -//////////////////////////////////////////////////// - -/obj/structure/bloodsucker/possessedarmor - name = "knight's armor" - desc = "I swear I saw its eyes move..." - icon_state = "posarmor" - anchored = FALSE - density = TRUE - Ghost_desc = "This Knight's armor will come alive once non-bloodsuckers get close to it." - Vamp_desc = "This is a possesed knight's armor, it will come alive once mortals get close to it.\n\ - You can reinforce it with 5 silver bars.\n\ - Good for immediate defense of your lair." - Vassal_desc = "This is a possesed knight's armor, it will protect your master if people get too close to it." - Hunter_desc = "This is a suspicious knight's armor. These things shouldn't be here, I shouldn't get too close." - var/upgraded = FALSE - -/obj/structure/bloodsucker/possessedarmor/upgraded - name = "shiny knight's armor" - upgraded = TRUE - -/obj/structure/bloodsucker/possessedarmor/bolt() - . = ..() - anchored = TRUE - START_PROCESSING(SSprocessing, src) - -/obj/structure/bloodsucker/possessedarmor/unbolt() - . = ..() - anchored = FALSE - STOP_PROCESSING(SSprocessing, src) - -/obj/structure/bloodsucker/possessedarmor/AltClick(mob/user) - if(!anchored) - setDir(turn(dir,-90)) - else - return ..() - -/obj/structure/bloodsucker/possessedarmor/attackby(obj/item/I, mob/user, params) - if(upgraded) - to_chat(user, span_warning("[src] is already reinforced!")) - return - if(istype(I, /obj/item/stack/sheet/mineral/silver)) - var/obj/item/stack/sheet/mineral/silver/S = I - if(S.amount < 5) - to_chat(user, span_warning("You need at least five silver bars to reinforce [src]!")) - return - else - to_chat(user, span_notice("You start adding [I] to [src]...")) - if(do_after(user, 5 SECONDS, src)) - S.use(5) - new /obj/structure/bloodsucker/possessedarmor/upgraded(src.loc) - qdel(src) - return - return ..() - -/obj/structure/bloodsucker/possessedarmor/Destroy() - . = ..() - STOP_PROCESSING(SSprocessing, src) - -/obj/structure/bloodsucker/possessedarmor/process() - for(var/mob/living/passerby in dview(1, get_turf(src))) - if(IS_BLOODSUCKER(passerby) || IS_VASSAL(passerby) || passerby.restrained()) - continue - to_chat(passerby, span_warning("The armor starts moving!")) - if(upgraded) - new /mob/living/simple_animal/hostile/bloodsucker/possessedarmor/upgraded(src.loc) - else - new /mob/living/simple_animal/hostile/bloodsucker/possessedarmor(src.loc) - qdel(src) - -//////////////////////////////////////////////////// - -/obj/structure/bloodsucker/vassalrack - name = "persuasion rack" - desc = "If this wasn't meant for torture, then someone has some fairly horrifying hobbies." - icon_state = "vassalrack" - anchored = FALSE - /// Start dense. Once fixed in place, go non-dense. - density = TRUE - can_buckle = TRUE - buckle_lying = 180 - Ghost_desc = "This is a Vassal rack, which allows Bloodsuckers to thrall crewmembers into loyal minions." - Vamp_desc = "This is the Vassal rack, which allows you to thrall crewmembers into loyal minions in your service.\n\ - Simply click and hold on a victim, and then drag their sprite on the vassal rack. Click on help intent on the vassal rack to unbuckle them.\n\ - To convert into a Vassal, repeatedly click on the persuasion rack while NOT on help intent. The time required scales with the tool in your off hand. This costs Blood to do.\n\ - Vassals can be turned into special ones by continuing to torture them once converted." - Vassal_desc = "This is the vassal rack, which allows your master to thrall crewmembers into their minions.\n\ - Aid your master in bringing their victims here and keeping them secure.\n\ - You can secure victims to the vassal rack by click dragging the victim onto the rack while it is secured." - Hunter_desc = "This is the vassal rack, which monsters use to brainwash crewmembers into their loyal slaves.\n\ - They usually ensure that victims are handcuffed, to prevent them from running away.\n\ - Their rituals take time, allowing us to disrupt it." - /// Resets on each new character to be added to the chair. Some effects should lower it... - var/convert_progress = 3 - /// Mindshielded and Antagonists willingly have to accept you as their Master. - var/disloyalty_confirm = FALSE - /// Prevents popup spam. - var/disloyalty_offered = FALSE - /// For Tzimisce bloodsuckers' rituals - var/meat_points = 0 - var/bigmeat = 0 - var/intermeat = 0 - var/mediummeat = 0 - var/smallmeat = 0 - var/meat_amount = 0 - -/obj/structure/bloodsucker/vassalrack/deconstruct(disassembled = TRUE) - . = ..() - new /obj/item/stack/sheet/metal(src.loc, 4) - new /obj/item/stack/rods(loc, 4) - qdel(src) - -/obj/structure/bloodsucker/vassalrack/examine(mob/user) - . = ..() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(bloodsuckerdatum.my_clan?.control_type == BLOODSUCKER_CONTROL_FLESH) - if(meat_amount) - . += span_boldnotice("It currently contains [meat_points] points to use in rituals.") - . += span_boldnotice("You can add meat points to the rack by using muscle, acquired from Dicing corpses, on it.") - else - return ..() - -/obj/structure/bloodsucker/vassalrack/bolt() - . = ..() - density = FALSE - anchored = TRUE - -/obj/structure/bloodsucker/vassalrack/unbolt() - . = ..() - density = TRUE - anchored = FALSE - -/obj/structure/bloodsucker/vassalrack/MouseDrop_T(atom/movable/movable_atom, mob/user) - var/mob/living/living_target = movable_atom - if(!anchored && IS_BLOODSUCKER(user)) - to_chat(user, span_danger("Until this rack is secured in place, it cannot serve its purpose.")) - to_chat(user, span_announce("* Bloodsucker Tip: Examine the Persuasion Rack to understand how it functions!")) - return - // Default checks - if(!isliving(movable_atom) || !living_target.Adjacent(src) || living_target == user || !isliving(user) || has_buckled_mobs() || user.incapacitated() || living_target.buckled) - return - // Don't buckle Silicon to it please. - if(issilicon(living_target)) - to_chat(user, span_danger("You realize that Silicon cannot be vassalized, therefore it is useless to buckle them.")) - return - if(do_after(user, 5 SECONDS, living_target)) - attach_victim(living_target, user) - -/obj/structure/bloodsucker/vassalrack/proc/attach_victim(mob/living/target, mob/living/user) - // Standard Buckle Check - target.forceMove(get_turf(src)) - if(!buckle_mob(target)) - return - user.visible_message( - span_notice("[user] straps [target] into the rack, immobilizing them."), - span_boldnotice("You secure [target] tightly in place. They won't escape you now."), - ) - - playsound(loc, 'sound/effects/pop_expl.ogg', 25, 1) - density = TRUE - update_appearance(UPDATE_ICON) - - // Set up Torture stuff now - convert_progress = 3 - disloyalty_confirm = FALSE - disloyalty_offered = FALSE - -/// Attempt Release (Owner vs Non Owner) -/obj/structure/bloodsucker/vassalrack/user_unbuckle_mob(mob/living/buckled_mob, mob/user) - if(IS_BLOODSUCKER(user) || IS_VASSAL(user)) - return ..() - - if(buckled_mob == user) - user.visible_message( - span_danger("[user] tries to release themself from the rack!"), - span_danger("You attempt to release yourself from the rack!"), - span_hear("You hear a squishy wet noise.")) - if(!do_after(user, 20 SECONDS, user)) - return - else - buckled_mob.visible_message( - span_danger("[user] tries to pull [buckled_mob] rack!"), - span_danger("[user] tries to pull [buckled_mob] rack!"), - span_hear("You hear a squishy wet noise.")) - if(!do_after(user, 10 SECONDS, buckled_mob)) - return - - return ..() - -/obj/structure/bloodsucker/vassalrack/unbuckle_mob(mob/living/buckled_mob, force = FALSE, can_fall = TRUE) - . = ..() - if(!.) - return FALSE - visible_message(span_danger("[buckled_mob][buckled_mob.stat == DEAD ? "'s corpse" : ""] slides off of the rack.")) - density = FALSE - buckled_mob.Paralyze(2 SECONDS) - update_appearance(UPDATE_ICON) - return TRUE - -/obj/structure/bloodsucker/vassalrack/attack_hand(mob/living/user, list/modifiers) - . = ..() - if(!.) - return FALSE - // Is there anyone on the rack & If so, are they being tortured? - if(!has_buckled_mobs()) - return FALSE - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - var/mob/living/carbon/buckled_carbons = pick(buckled_mobs) - if(!user.combat_mode) - if(istype(bloodsuckerdatum)) - unbuckle_mob(buckled_carbons) - return FALSE - user_unbuckle_mob(buckled_carbons, user) - return - if(!bloodsuckerdatum.my_clan) - to_chat(user, span_warning("You can't vassalize people until you enter a Clan (Through your Antagonist UI button)")) - user.balloon_alert(user, "join a clan first!") - return - /// If I'm not a Bloodsucker, try to unbuckle them. - var/datum/antagonist/vassal/vassaldatum = IS_VASSAL(buckled_carbons) - // Are they our Vassal, or Dead? - if(buckled_carbons.stat == DEAD) - if(bloodsuckerdatum.my_clan?.control_type < BLOODSUCKER_CONTROL_FLESH) - balloon_alert(user, "[buckled_carbons.p_theyre()] dead!") - return - do_ritual(user, buckled_carbons) - return - if(vassaldatum && (vassaldatum in bloodsuckerdatum.vassals)) - SEND_SIGNAL(bloodsuckerdatum, BLOODSUCKER_PRE_MAKE_FAVORITE, vassaldatum) - return - - // Not our Vassal, but Alive & We're a Bloodsucker, good to torture! - torture_victim(user, buckled_carbons) - -#define MEATLIMIT 3 - -/obj/structure/bloodsucker/vassalrack/attackby(obj/item/I, mob/user, params) //Tzimisce stuff - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(bloodsuckerdatum.my_clan?.control_type < BLOODSUCKER_CONTROL_FLESH) - return ..() // only gamers - if(istype(I, /obj/item/muscle)) - if(meat_amount >= MEATLIMIT) - to_chat(user, span_warning("You can't fit more meat into [src]")) - return - var/obj/item/muscle/M = I - meat_points += M.size - switch(M.size) - if(4) - bigmeat++ - if(3) - intermeat++ - if(2) - mediummeat++ - if(1) - smallmeat++ - meat_amount = bigmeat + intermeat + mediummeat + smallmeat - qdel(I) - update_appearance(UPDATE_ICON) -#undef MEATLIMIT - -/obj/structure/bloodsucker/vassalrack/update_overlays() - . = ..() - if(bigmeat) - . += "bigmeat_[bigmeat]" - if(intermeat) - . += "mediummeat_[intermeat]" - . += "smallmeat_[intermeat]" - if(mediummeat) - . += "mediummeat_[mediummeat + intermeat]" - if(smallmeat) - . += "smallmeat_[smallmeat + intermeat]" - -/obj/structure/bloodsucker/vassalrack/CtrlClick(mob/user) - if(!anchored) - return ..() - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(bloodsuckerdatum.my_clan?.control_type < BLOODSUCKER_CONTROL_FLESH) - return ..() - if(meat_amount) - if(smallmeat) - new /obj/item/muscle/small(user.drop_location()) - smallmeat-- - meat_points -= 1 - if(mediummeat) - new /obj/item/muscle/medium(user.drop_location()) - mediummeat-- - meat_points -= 2 - if(intermeat) - new /obj/item/muscle/medium(user.drop_location()) - new /obj/item/muscle/small(user.drop_location()) - intermeat-- - meat_points -= 3 - if(bigmeat) - new /obj/item/muscle/big(user.drop_location()) - bigmeat-- - meat_points -= 4 - else - to_chat(user, span_warning("There's no meat to retrieve in [src]")) - meat_amount = bigmeat + intermeat + mediummeat + smallmeat - update_appearance(UPDATE_ICON) - return TRUE - -/** - * Step One: Tick Down Conversion from 3 to 0 - * Step Two: Break mindshielding/antag (on approve) - * Step Three: Blood Ritual - */ - -/obj/structure/bloodsucker/vassalrack/proc/torture_victim(mob/living/user, mob/living/target) - if(DOING_INTERACTION(user, target)) - balloon_alert(user, "already interacting!") - return - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(IS_VASSAL(target)) - var/datum/antagonist/vassal/vassaldatum = target.mind.has_antag_datum(/datum/antagonist/vassal) - if(!vassaldatum.master.broke_masquerade) - balloon_alert(user, "someone else's vassal!") - return FALSE - - var/disloyalty_requires = RequireDisloyalty(user, target) - if(disloyalty_requires == VASSALIZATION_BANNED) - balloon_alert(user, "can't be vassalized!") - return FALSE - - // Conversion Process - if(convert_progress) - balloon_alert(user, "spilling blood...") - bloodsuckerdatum.AddBloodVolume(-TORTURE_BLOOD_HALF_COST) - if(!do_torture(user, target)) - return FALSE - bloodsuckerdatum.AddBloodVolume(-TORTURE_BLOOD_HALF_COST) - // Prevent them from unbuckling themselves as long as we're torturing. - target.Paralyze(1 SECONDS) - convert_progress-- - - // We're done? Let's see if they can be Vassal. - if(convert_progress) - balloon_alert(user, "needs more persuasion...") - return - - if(disloyalty_requires) - balloon_alert(user, "has external loyalties! more persuasion required!") - else - balloon_alert(user, "ready for communion!") - return - - if(!disloyalty_confirm && disloyalty_requires) - if(!do_disloyalty(user, target)) - return - if(!disloyalty_confirm) - balloon_alert(user, "refused persuasion!") - else - balloon_alert(user, "ready for communion!") - return - - user.balloon_alert_to_viewers("smears blood...", "painting bloody marks...") - if(!do_after(user, 5 SECONDS, target)) - balloon_alert(user, "interrupted!") - return - if(HAS_TRAIT(target, TRAIT_MINDSHIELD)) - to_chat(user, span_danger("They're mindshielded! Break their mindshield with a candelabrum or surgery before continuing!")) - return VASSALIZATION_DISLOYAL - // Convert to Vassal! - bloodsuckerdatum.AddBloodVolume(-TORTURE_CONVERSION_COST) - if(bloodsuckerdatum.make_vassal(target)) - SEND_SIGNAL(bloodsuckerdatum, BLOODSUCKER_MADE_VASSAL, user, target) - -/obj/structure/bloodsucker/vassalrack/proc/do_torture(mob/living/user, mob/living/carbon/target, mult = 1) - // Fifteen seconds if you aren't using anything. Shorter with weapons and such. - var/torture_time = 15 - var/torture_dmg_brute = 2 - var/torture_dmg_burn = 0 - var/obj/item/bodypart/selected_bodypart = pick(target.bodyparts) - // Get Weapon - var/obj/item/held_item = user.get_inactive_held_item() - /// Weapon Bonus - if(held_item) - torture_time -= held_item.force / 4 - if(!held_item.use_tool(src, user, 0, volume = 5)) - return - switch(held_item.damtype) - if(BRUTE) - torture_dmg_brute = held_item.force / 4 - torture_dmg_burn = 0 - if(BURN) - torture_dmg_brute = 0 - torture_dmg_burn = held_item.force / 4 - switch(held_item.sharpness) - if(SHARP_EDGED) - torture_time -= 2 - if(SHARP_POINTY) - torture_time -= 3 - - // Minimum 5 seconds. - torture_time = max(5 SECONDS, torture_time * 10) - // Now run process. - if(!do_after(user, (torture_time * mult), target)) - return FALSE - - if(held_item) - playsound(loc, held_item.hitsound, 30, 1, -1) - held_item.play_tool_sound(target) - target.visible_message( - span_danger("[user] performs a ritual, spilling some of [target]'s blood from their [selected_bodypart.name] and shaking them up!"), - span_userdanger("[user] performs a ritual, spilling some blood from your [selected_bodypart.name], shaking you up!")) - - INVOKE_ASYNC(target, TYPE_PROC_REF(/mob, emote), "scream") - target.adjust_jitter(5 SECONDS) - target.apply_damages(brute = torture_dmg_brute, burn = torture_dmg_burn, def_zone = selected_bodypart.body_zone) - return TRUE - -/// Offer them the oppertunity to join now. -/obj/structure/bloodsucker/vassalrack/proc/do_disloyalty(mob/living/user, mob/living/target) - if(disloyalty_offered) - return FALSE - - disloyalty_offered = TRUE - to_chat(user, span_notice("[target] has been given the opportunity for servitude. You await their decision...")) - var/alert_response = tgui_alert( - user = target, \ - message = "You are being tortured! Do you want to give in and pledge your undying loyalty to [user]? \n\ - You will not lose your current objectives, but they come second to the will of your new master!", \ - title = "THE HORRIBLE PAIN! WHEN WILL IT END?!", - buttons = list("Accept", "Refuse"), - timeout = 10 SECONDS, \ - autofocus = TRUE, \ - ) - switch(alert_response) - if("Accept") - disloyalty_confirm = TRUE - else - target.balloon_alert_to_viewers("stares defiantly", "refused vassalization!") - disloyalty_offered = FALSE - - return TRUE - -/obj/structure/bloodsucker/vassalrack/proc/RequireDisloyalty(mob/living/user, mob/living/target) -#ifdef BLOODSUCKER_TESTING - if(!target || !target.mind) -#else - if(!target || !target.client) -#endif - balloon_alert(user, "target has no mind!") - return VASSALIZATION_BANNED - - var/datum/antagonist/bloodsucker/bloodsuckerdatum = IS_BLOODSUCKER(user) - return bloodsuckerdatum.AmValidAntag(target) - -/obj/structure/bloodsucker/vassalrack/proc/do_ritual(mob/living/user, mob/living/target) - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(!target.mind) - to_chat(user, span_warning("[target] is catatonic!")) - /// To deal with Blood - var/mob/living/carbon/human/B = user - var/mob/living/carbon/human/H = target - - /// Due to the checks leding up to this, if they fail this, they're dead & Not our vassal. - if(!IS_VASSAL(target)) //remind me to refactor this later - to_chat(user, span_notice("Do you wish to rebuild this body? This will remove any restraints they might have, and will cost 150 Blood!")) - var/revive_response = tgui_alert(usr, "Would you like to revive [target]?", "Ghetto Medbay", list("Yes", "No")) - if(revive_response == "Yes") - if(!do_after(user, 7 SECONDS, src)) - to_chat(user, span_danger("The ritual has been interrupted!")) - return - if(prob(70 - bloodsuckerdatum.bloodsucker_level * 7)) //calculation, stops going wrong at level 10 - to_chat(user, span_danger("Something has gone terribly wrong! You have accidentally turned [target] into a High-Functioning Zombie!")) - to_chat(target, span_announce("As Blood drips over your body, your heart fails to beat... But you still wake up.")) - H.set_species(/datum/species/zombie) - else - to_chat(user, span_danger("You have brought [target] back from the Dead!")) - to_chat(target, span_announce("As Blood drips over your body, your heart begins to beat... You live again!")) - B.blood_volume -= 150 - target.revive(full_heal = TRUE, admin_revive = FALSE) - return - to_chat(user, span_danger("You decide not to revive [target].")) - // Unbuckle them now. - unbuckle_mob(B) - return - var/list/races = list(HUSK_MONSTER) - switch(bloodsuckerdatum.bloodsucker_level) - if(1 to 3) - races += ARMMY_MONSTER - if(3 to 5) - races += ARMMY_MONSTER - races += CALCIUM_MONSTER - if(5 to INFINITY) - races += ARMMY_MONSTER - races += CALCIUM_MONSTER - races += TRIPLECHEST_MONSTER - var/list/options = list() - options = races - var/answer = tgui_input_list(user, "We have the chance to mutate our Vassal, how should we mutilate their corpse? This will cost us blood.", "What do we do with our Vassal?", options) - var/meat_cost = 0 - var/blood_gained - if(!answer) - to_chat(user, span_notice("You decide to leave your Vassal just the way they are.")) - return - to_chat(user, span_warning("You start mutating your Vassal into a [answer]...")) - if(!do_after(user, 5 SECONDS, src)) - to_chat(user, span_danger("The ritual has been interrupted!")) - return - playsound(target.loc, 'sound/weapons/slash.ogg', 50, TRUE, -1) - switch(answer) - if(HUSK_MONSTER) - if(HAS_TRAIT(target, TRAIT_HUSK)) - to_chat(user, span_warning("[target] is already a Husk!")) - return - if(!do_after(user, 1 SECONDS, target)) - return - playsound(target.loc, 'sound/weapons/slash.ogg', 50, TRUE, -1) - if(!do_after(user, 1 SECONDS, target)) - return - to_chat(user, span_notice("You suck all the blood out of [target], turning them into a Living Husk!")) - to_chat(target, span_notice("Your master has mutated you into a Living Husk!")) - playsound(target.loc, 'sound/magic/mutate.ogg', 50, TRUE, -1) - /// Just take it all - blood_gained = 250 - target.remove_all_languages() - target.grant_language(/datum/language/vampiric) - H.become_husk() - bloodsuckerdatum.bloodsucker_level_unspent++ - if(ARMMY_MONSTER) - meat_cost = 4 - var/mob/living/simple_animal/hostile/bloodsucker/tzimisce/armmy/A - if(!(HAS_TRAIT(target, TRAIT_HUSK))) - to_chat(user, span_warning("You need to mutilate [target] into a husk first before doing this.")) - return - if(meat_points < meat_cost) - to_chat(user, span_warning("You need at least [meat_cost - meat_points] more meat points to do this.")) - return - if(!do_after(user, 1 SECONDS, target)) - return - playsound(target.loc, 'sound/weapons/slash.ogg', 50, TRUE, -1) - to_chat(user, span_notice("You transfer your blood and toy with [target]'s flesh, leaving their body as a head and arm almalgam.")) - to_chat(target, span_notice("Your master has mutated you into a tiny arm monster!")) - B.blood_volume -= 100 - A = new /mob/living/simple_animal/hostile/bloodsucker/tzimisce/armmy(target.loc) - target.forceMove(A) - target.mind.transfer_to(A) - A.bloodsucker = target - /// Chance to give Bat form, or turn them into a bat. - if(CALCIUM_MONSTER) - meat_cost = 8 - var/mob/living/simple_animal/hostile/bloodsucker/tzimisce/calcium/C - if(!(HAS_TRAIT(target, TRAIT_HUSK))) - to_chat(user, span_warning("You need to mutilate [target] into a husk first before doing this.")) - return - if(meat_points < meat_cost) - to_chat(user, span_warning("You need at least [meat_cost - meat_points] more meat points to do this.")) - return - if(!do_after(user, 1 SECONDS, target)) - return - playsound(target.loc, 'sound/weapons/slash.ogg', 50, TRUE, -1) - to_chat(user, span_notice("You transfer your blood and toy with [target]'s flesh and bones, leaving their body as a boney and flesh amalgam.")) - to_chat(target, span_notice("Your master has mutated you into a fractured monster!")) - B.blood_volume -= 150 - C = new /mob/living/simple_animal/hostile/bloodsucker/tzimisce/calcium(target.loc) - target.forceMove(C) - target.mind.transfer_to(C) - C.bloodsucker = target - if(TRIPLECHEST_MONSTER) - meat_cost = 12 - var/mob/living/simple_animal/hostile/bloodsucker/tzimisce/triplechest/T - if(!(HAS_TRAIT(target, TRAIT_HUSK))) - to_chat(user, span_warning("You need to mutilate [target] into a husk first before doing this.")) - return - if(meat_points < meat_cost) - to_chat(user, span_warning("You need at least [meat_cost - meat_points] more meat points to do this.")) - return - if(!do_after(user, 1 SECONDS, target)) - return - playsound(target.loc, 'sound/weapons/slash.ogg', 50, TRUE, -1) - if(!do_after(user, 1 SECONDS, target)) - return - to_chat(user, span_notice("You transfer your blood and toy with [target]'s flesh and bones, leaving their body as a huge pile of flesh and organs.")) - to_chat(target, span_notice("Your master has mutated you into a gargantuan monster!")) - B.blood_volume -= 300 - T = new /mob/living/simple_animal/hostile/bloodsucker/tzimisce/triplechest(target.loc) - target.forceMove(T) - target.mind.transfer_to(T) - T.bloodsucker = target - if(blood_gained) - user.blood_volume += blood_gained - var/meatlost = 0 - while(meat_cost) - meat_points-- - meat_cost-- - meatlost++ - if(smallmeat && meatlost == 1) - smallmeat-- - meatlost-- - if(mediummeat && meatlost == 2) - mediummeat-- - meatlost -= 2 - if(intermeat && meatlost == 3) - intermeat-- - meatlost -= 3 - if(bigmeat && meatlost == 4) - bigmeat-- - meatlost -= 4 - update_appearance(UPDATE_ICON) - meat_amount = bigmeat + intermeat + mediummeat + smallmeat - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/obj/structure/bloodsucker/candelabrum - name = "candelabrum" - desc = "It burns slowly, but doesn't radiate any heat." - icon_state = "candelabrum" - light_color = "#66FFFF"//LIGHT_COLOR_BLUEGREEN // lighting.dm - light_power = 3 - light_range = 0 // to 2 - density = FALSE - can_buckle = TRUE - anchored = FALSE - Ghost_desc = "This is a magical candle which drains at the sanity of non Bloodsuckers and Vassals.\n\ - Vassals can also turn the candle on." - Vamp_desc = "This is a magical candle which drains at the sanity of mortals who are not under your command while it is active.\n\ - You can click on it to turn it on, clicking on it with a mindshielded individual buckled will start to disable their mindshields." - Vassal_desc = "This is a magical candle which drains at the sanity of the fools who havent yet accepted your master, as long as it is active.\n\ - You can turn it on and off by clicking on it while you are next to it." - Hunter_desc = "This is a blue Candelabrum, which causes insanity to those near it while active." - var/lit = FALSE - -/obj/structure/bloodsucker/candelabrum/deconstruct(disassembled = TRUE) - . = ..() - new /obj/item/candle(loc, 1) - new /obj/item/stack/rods(loc, 4) - qdel(src) - -/obj/structure/bloodsucker/candelabrum/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/structure/bloodsucker/candelabrum/update_icon_state() - . = ..() - icon_state = "candelabrum[lit ? "_lit" : ""]" - -/obj/structure/bloodsucker/candelabrum/examine(mob/user) - . = ..() - -/obj/structure/bloodsucker/candelabrum/bolt() - . = ..() - anchored = TRUE - density = TRUE - -/obj/structure/bloodsucker/candelabrum/unbolt() - . = ..() - anchored = FALSE - density = FALSE - -/obj/structure/bloodsucker/candelabrum/proc/toggle(mob/user) - lit = !lit - if(lit) - set_light(2, 3, "#66FFFF") - START_PROCESSING(SSobj, src) - else - set_light(0) - STOP_PROCESSING(SSobj, src) - update_appearance(UPDATE_ICON) - -/obj/structure/bloodsucker/candelabrum/process() - if(!lit) - return - for(var/mob/living/carbon/nearly_people in viewers(7, src)) - /// We dont want Bloodsuckers or Vassals affected by this - if(IS_VASSAL(nearly_people) || IS_BLOODSUCKER(nearly_people)) - continue - nearly_people.adjust_hallucinations(5 SECONDS) - if(nearly_people.getStaminaLoss() >= 100) - continue - if(nearly_people.getStaminaLoss() >= 60) - spawn(10) - nearly_people.adjustStaminaLoss(1) // keeps the slowness by constantly updating it - else - nearly_people.adjustStaminaLoss(10) - SEND_SIGNAL(nearly_people, COMSIG_ADD_MOOD_EVENT, "vampcandle", /datum/mood_event/vampcandle) - to_chat(nearly_people, span_warning("You start to feel extremely weak and drained.")) - -/// Mindshield breaking -/obj/structure/bloodsucker/candelabrum/attack_hand(mob/living/user, list/modifiers) - . = ..() - if(!.) - return - if(!anchored) - return - // Checks: They're Buckled & Alive. - if(IS_BLOODSUCKER(user) && has_buckled_mobs()) - var/mob/living/carbon/target = pick(buckled_mobs) - if(target.stat >= DEAD || !user.combat_mode) - unbuckle_mob(target) - return - if(HAS_TRAIT(target, TRAIT_MINDSHIELD)) - if(user.blood_volume >= 50) - switch(input("Do you wish to spend 50 Blood to deactivate [target]'s mindshield?") in list("Yes", "No")) - if("Yes") - user.blood_volume -= 50 - if(!do_after(user, 20 SECONDS, target)) - to_chat(user, span_danger("The ritual has been interrupted!")) - return FALSE - remove_loyalties(target) - to_chat(user, span_boldnotice("You deactivated [target]'s mindshield!")) - else - to_chat(user, span_danger("You don't have enough Blood to deactivate [target]'s mindshield.")) - return - if(IS_VASSAL(user) || IS_BLOODSUCKER(user)) - toggle() - -/// Buckling someone in -/obj/structure/bloodsucker/candelabrum/MouseDrop_T(mob/living/target, mob/user) - if(!anchored && IS_BLOODSUCKER(user)) - to_chat(user, span_danger("Until the candelabrum is secured in place, it cannot serve its purpose.")) - return - /// Default checks - if(!target.Adjacent(src) || target == user || !isliving(user) || has_buckled_mobs() || user.incapacitated() || target.buckled) - return - /// Are they mindshielded or a bloodsucker/vassal? - if(!HAS_TRAIT(target, TRAIT_MINDSHIELD)) - to_chat(user, span_warning("[target] doesn't have a mindshield for you to turn off!")) - return - /// Good to go - Buckle them! - if(do_after(user, 5 SECONDS, target)) - attach_mob(target, user) - -/obj/structure/bloodsucker/candelabrum/proc/attach_mob(mob/living/target, mob/living/user) - user.visible_message( - span_notice("[user] lifts and buckles [target] onto the candelabrum."), - span_boldnotice("You buckle [target] onto the candelabrum."), - ) - - playsound(src.loc, 'sound/effects/pop_expl.ogg', 25, 1) - target.forceMove(get_turf(src)) - - if(!buckle_mob(target)) - return - update_appearance(UPDATE_ICON) - -/obj/structure/bloodsucker/candelabrum/proc/remove_loyalties(mob/living/target, mob/living/user) - // Find Mindshield implant & destroy, takes a good while. - for(var/obj/item/implant/all_implants as anything in target.implants) - if(all_implants.type == /obj/item/implant/mindshield) - all_implants.removed(target, silent = TRUE) - -/// Attempt Unbuckle -/obj/structure/bloodsucker/candelabrum/unbuckle_mob(mob/living/buckled_mob, force = FALSE, can_fall = TRUE) - . = ..() - src.visible_message(span_danger("[buckled_mob][buckled_mob.stat==DEAD?"'s corpse":""] slides off of the candelabrum.")) - update_appearance(UPDATE_ICON) - -/// Blood Throne - Allows Bloodsuckers to remotely speak with their Vassals. - Code (Mostly) stolen from comfy chairs (armrests) and chairs (layers) -/* broken currently -/obj/structure/bloodsucker/bloodthrone - name = "wicked throne" - desc = "Twisted metal shards jut from the arm rests. Very uncomfortable looking. It would take a masochistic sort to sit on this jagged piece of furniture." - icon = 'icons/obj/vamp_obj_64.dmi' - icon_state = "throne" - buckle_lying = 0 - anchored = FALSE - density = TRUE - can_buckle = TRUE - Ghost_desc = "This is a Bloodsucker throne, any Bloodsucker sitting on it can remotely speak to their Vassals by attempting to speak aloud." - Vamp_desc = "This is a Blood throne, sitting on it will allow you to telepathically speak to your vassals by simply speaking." - Vassal_desc = "This is a Blood throne, it allows your Master to telepathically speak to you and others like you." - Hunter_desc = "This is a chair that hurts those that try to buckle themselves onto it, though the Undead have no problem latching on.\n\ - While buckled, Monsters can use this to telepathically communicate with eachother." - var/mutable_appearance/armrest - -// Add rotating and armrest -/obj/structure/bloodsucker/bloodthrone/Initialize(mapload) - AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE) - armrest = GetArmrest() - armrest.layer = ABOVE_MOB_LAYER - return ..() - -/obj/structure/bloodsucker/bloodthrone/Destroy() - QDEL_NULL(armrest) - return ..() - -/obj/structure/bloodsucker/bloodthrone/bolt() - . = ..() - anchored = TRUE - -/obj/structure/bloodsucker/bloodthrone/unbolt() - . = ..() - anchored = FALSE - -// Armrests -/obj/structure/bloodsucker/bloodthrone/proc/GetArmrest() - return mutable_appearance('icons/obj/vamp_obj_64.dmi', "thronearm") - -/obj/structure/bloodsucker/bloodthrone/proc/update_armrest() - if(has_buckled_mobs()) - add_overlay(armrest) - else - cut_overlay(armrest) - -// Rotating -/obj/structure/bloodsucker/bloodthrone/setDir(newdir) - . = ..() - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - buckled_mob.setDir(newdir) - - if(has_buckled_mobs() && dir == NORTH) - layer = ABOVE_MOB_LAYER - else - layer = OBJ_LAYER - -// Buckling -/obj/structure/bloodsucker/bloodthrone/buckle_mob(mob/living/user, force = FALSE, check_loc = TRUE) - if(!anchored) - to_chat(user, span_announce("[src] is not bolted to the ground!")) - return - user.visible_message( - span_notice("[user] sits down on [src]."), - span_boldnotice("You sit down onto [src]."), - ) - if(IS_BLOODSUCKER(user)) - RegisterSignal(user, COMSIG_MOB_SAY, PROC_REF(handle_speech)) - else - user.Paralyze(6 SECONDS) - to_chat(user, span_cult("The power of the blood throne overwhelms you!")) - user.apply_damage(10, BRUTE) - unbuckle_mob(user) - return - return ..() - -/obj/structure/bloodsucker/bloodthrone/post_buckle_mob(mob/living/target) - . = ..() - update_armrest() - target.pixel_y += 2 - -// Unbuckling -/obj/structure/bloodsucker/bloodthrone/unbuckle_mob(mob/living/user, force = FALSE, can_fall = TRUE) - src.visible_message(span_danger("[user] unbuckles themselves from [src].")) - if(IS_BLOODSUCKER(user)) - UnregisterSignal(user, COMSIG_MOB_SAY) - return ..() - -/obj/structure/bloodsucker/bloodthrone/post_unbuckle_mob(mob/living/target) - target.pixel_y -= 2 - -// The speech itself -/obj/structure/bloodsucker/bloodthrone/proc/handle_speech(datum/source, mob/speech_args) - - var/message = speech_args[SPEECH_MESSAGE] - var/mob/living/carbon/human/user = source - var/rendered = span_cultlarge("[user.real_name]: [message]") - user.log_talk(message, LOG_SAY, tag=ROLE_BLOODSUCKER) - for(var/mob/living/carbon/human/vassals in GLOB.player_list) - var/datum/antagonist/vassal/vassaldatum = vassals.mind.has_antag_datum(/datum/antagonist/vassal) - if(vassals == user) // Just so they can hear themselves speak. - to_chat(vassals, rendered) - if(!istype(vassaldatum)) - continue - if(vassaldatum.master.owner == user.mind) - to_chat(vassals, rendered) - - for(var/mob/dead_mob in GLOB.dead_mob_list) - var/link = FOLLOW_LINK(dead_mob, user) - to_chat(dead_mob, "[link] [rendered]") - - speech_args[SPEECH_MESSAGE] = "" -*/ diff --git a/code/modules/antagonists/bloodsuckers/structures/bloodsucker_life.dm b/code/modules/antagonists/bloodsuckers/structures/bloodsucker_life.dm index 485d15541dbb..1482e4c10d4e 100644 --- a/code/modules/antagonists/bloodsuckers/structures/bloodsucker_life.dm +++ b/code/modules/antagonists/bloodsuckers/structures/bloodsucker_life.dm @@ -31,7 +31,6 @@ // Standard Updates SEND_SIGNAL(src, COMSIG_BLOODSUCKER_ON_LIFETICK) INVOKE_ASYNC(src, PROC_REF(HandleStarving)) - INVOKE_ASYNC(src, PROC_REF(update_blood)) INVOKE_ASYNC(src, PROC_REF(update_hud)) @@ -60,64 +59,15 @@ var/vamp_examine = carbon_source.return_vamp_examine(examiner) examine_text += vamp_examine -/datum/antagonist/bloodsucker/proc/AddHumanityLost(value) - if(humanity_lost >= 500) - to_chat(owner.current, span_warning("You hit the maximum amount of lost Humanity, you are far from Human.")) - return - if(my_clan?.get_clan() == CLAN_TOREADOR) - if(humanity_lost >= TOREADOR_MAX_HUMANITY_LOSS) - to_chat(owner.current, span_warning("Your morals prevent you from becoming more inhuman.")) - SEND_SIGNAL(owner.current, COMSIG_ADD_MOOD_EVENT, /datum/mood_event/toreador_inhuman2) - return - SEND_SIGNAL(owner.current, COMSIG_ADD_MOOD_EVENT, /datum/mood_event/toreador_inhuman) - humanity_lost += value - to_chat(owner.current, span_warning("You feel as if you lost some of your humanity, you will now enter Frenzy at [FRENZY_THRESHOLD_ENTER + humanity_lost * 10] Blood.")) - -/// mult: SILENT feed is 1/3 the amount -/datum/antagonist/bloodsucker/proc/handle_feeding(mob/living/carbon/target, mult=1, power_level) - // Starts at 15 (now 8 since we doubled the Feed time) - var/feed_amount = 15 + (power_level * 2) - var/blood_taken = min(feed_amount, target.blood_volume) * mult - target.blood_volume -= blood_taken - - /////////// - // Shift Body Temp (toward Target's temp, by volume taken) - owner.current.bodytemperature = ((bloodsucker_blood_volume * owner.current.bodytemperature) + (blood_taken * target.bodytemperature)) / (bloodsucker_blood_volume + blood_taken) - // our volume * temp, + their volume * temp, / total volume - /////////// - // Reduce Value Quantity - if(target.stat == DEAD) // Penalty for Dead Blood - blood_taken /= 4 - if(!ishuman(target)) // Penalty for Non-Human Blood - blood_taken /= 3 - if(!target.mind && !target.client) - blood_taken /= 5 // Penalty for Catatonics / Braindead - //if (!iscarbon(target)) // Penalty for Animals (they're junk food) - // Apply to Volume - AddBloodVolume(blood_taken) - // Reagents (NOT Blood!) - if(target.reagents && target.reagents.total_volume) - target.reagents.trans_to(owner.current, INGEST, 1) // Run transfer of 1 unit of reagent from them to me. - owner.current.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, 1) // Play THIS sound for user only. The "null" is where turf would go if a location was needed. Null puts it right in their head. - total_blood_drank += blood_taken - if(frenzied) - frenzy_blood_drank += blood_taken - if(has_task) - if(target.mind) - task_blood_drank += blood_taken - else - to_chat(owner, span_warning("[target] is catatonic and won't yield any usable blood for tasks!")) - return blood_taken /** * ## HEALING */ - /// Constantly runs on Bloodsucker's LifeTick, and is increased by being in Torpor/Coffins /datum/antagonist/bloodsucker/proc/HandleHealing(mult = 1) var/actual_regen = bloodsucker_regen_rate + additional_regen - // Don't heal if I'm staked or on Masquerade (+ not in a Coffin). Masqueraded Bloodsuckers in a Coffin however, will heal. - if(owner.current.am_staked() || (HAS_TRAIT(owner.current, TRAIT_MASQUERADE) && !HAS_TRAIT(owner.current, TRAIT_NODEATH) && my_clan?.get_clan() != CLAN_TOREADOR)) + + if(owner.current.am_staked() || !HAS_TRAIT(owner.current, TRAIT_NODEATH)) return FALSE owner.current.adjustCloneLoss(-1 * (actual_regen * 4) * mult, 0) owner.current.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1 * (actual_regen * 4) * mult) //adjustBrainLoss(-1 * (actual_regen * 4) * mult, 0) @@ -130,10 +80,6 @@ /// Checks if you're in a coffin here, additionally checks for Torpor right below it. var/amInCoffin = istype(user.loc, /obj/structure/closet/crate/coffin) if(amInCoffin && HAS_TRAIT(user, TRAIT_NODEATH)) - if(HAS_TRAIT(owner.current, TRAIT_MASQUERADE) && (COOLDOWN_FINISHED(src, bloodsucker_spam_healing))) - to_chat(user, span_alert("You do not heal while your Masquerade ability is active.")) - COOLDOWN_START(src, bloodsucker_spam_healing, BLOODSUCKER_SPAM_MASQUERADE) - return fireheal = min(user.getFireLoss(), actual_regen) mult *= 8 // Increase multiplier if we're sleeping in a coffin. costMult *= 0 // No cost if we're sleeping in a coffin. @@ -208,9 +154,6 @@ // Step 2 NOTE: Giving passive organ regeneration will cause Torpor to spam /datum/client_colour/monochrome at the Bloodsucker, permanently making them colorblind! for(var/obj/item/organ/organ as anything in bloodsuckeruser.internal_organs) organ.setOrganDamage(0) - if(!HAS_TRAIT(bloodsuckeruser, TRAIT_MASQUERADE)) - var/obj/item/organ/heart/current_heart = bloodsuckeruser.getorganslot(ORGAN_SLOT_HEART) - current_heart.beating = FALSE var/obj/item/organ/eyes/current_eyes = bloodsuckeruser.getorganslot(ORGAN_SLOT_EYES) if(current_eyes) current_eyes.flash_protect = max(initial(current_eyes.flash_protect) - 1, - 1) @@ -218,10 +161,6 @@ current_eyes.sight_flags = SEE_MOBS current_eyes.setOrganDamage(0) //making sure - if(my_clan?.get_clan() == CLAN_LASOMBRA && ishuman(bloodsuckeruser)) - var/mob/living/carbon/human/bloodsucker = bloodsuckeruser - bloodsucker.eye_color = BLOODCULT_EYE - bloodsuckeruser.update_body() bloodsuckeruser.update_sight() // Step 3 @@ -229,6 +168,7 @@ bloodsuckeruser.revive(full_heal = FALSE, admin_revive = FALSE) for(var/datum/wound/iter_wound as anything in bloodsuckeruser.all_wounds) iter_wound.remove_wound() + // From [powers/panacea.dm] var/list/bad_organs = list( bloodsuckeruser.getorgan(/obj/item/organ/body_egg), @@ -241,37 +181,9 @@ // Good to go! -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// DEATH - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/// FINAL DEATH -/datum/antagonist/bloodsucker/proc/HandleDeath() - // Not "Alive"? - if(!owner.current) - FinalDeath() - return - // Fire Damage and Fledgeling? (above double health) - if(owner.current.getFireLoss() >= owner.current.maxHealth * 2 && bloodsucker_level < 4) - FinalDeath() - return - // Fire Damage and Daytime? - if(owner.current.getFireLoss() >= owner.current.maxHealth * 2 && (SSsunlight.sunlight_active || frenzied)) - FinalDeath() - return - // Staked while "Temp Death" or Asleep - if(owner.current.StakeCanKillMe() && owner.current.am_staked()) - FinalDeath() - return - // Temporary Death? Convert to Torpor. - if(HAS_TRAIT(owner.current, TRAIT_NODEATH)) - return - to_chat(owner.current, span_danger("Your immortal body will not yet relinquish your soul to the abyss. You enter Torpor.")) - check_begin_torpor(TRUE) - - +//////////////////////////////////////////////////////////////////////////////////// +//----------------------------------Starvation------------------------------------// +//////////////////////////////////////////////////////////////////////////////////// /datum/antagonist/bloodsucker/proc/HandleStarving() // I am thirsty for blood! // Nutrition - The amount of blood is how full we are. owner.current.set_nutrition(min(bloodsucker_blood_volume, NUTRITION_LEVEL_FED)) @@ -279,59 +191,20 @@ // BLOOD_VOLUME_GOOD: [336] - Pale // handled in bloodsucker_integration.dm // BLOOD_VOLUME_EXIT: [560] - Exit Frenzy (If in one) This is high because we want enough to kill the poor soul they feed off of. - if(bloodsucker_blood_volume >= (FRENZY_THRESHOLD_EXIT + humanity_lost * 10) && frenzied) + if(bloodsucker_blood_volume >= FRENZY_THRESHOLD_EXIT && frenzied) owner.current.remove_status_effect(STATUS_EFFECT_FRENZY) // BLOOD_VOLUME_BAD: [224] - Jitter - if(bloodsucker_blood_volume < BLOOD_VOLUME_BAD(owner.current) && prob(0.5) && !HAS_TRAIT(owner.current, TRAIT_NODEATH) && !HAS_TRAIT(owner.current, TRAIT_MASQUERADE)) + if(bloodsucker_blood_volume < BLOOD_VOLUME_BAD(owner.current) && prob(0.5) && !HAS_TRAIT(owner.current, TRAIT_NODEATH)) owner.current.adjust_jitter(3 SECONDS) // BLOOD_VOLUME_SURVIVE: [122] - Blur Vision if(bloodsucker_blood_volume < BLOOD_VOLUME_SURVIVE(owner.current)) owner.current.adjust_eye_blur((8 - 8 * (bloodsucker_blood_volume / BLOOD_VOLUME_BAD(owner.current)))* 2 SECONDS) // The more blood, the better the Regeneration, get too low blood, and you enter Frenzy. - if(bloodsucker_blood_volume < (FRENZY_THRESHOLD_ENTER + humanity_lost * 10) && !frenzied) + if(bloodsucker_blood_volume < FRENZY_THRESHOLD_ENTER && !frenzied) if(!iscarbon(owner.current)) return - if(my_clan?.get_clan() == CLAN_GANGREL) - var/mob/living/carbon/user = owner.current - switch(frenzies) - if(0) - owner.current.apply_status_effect(STATUS_EFFECT_FRENZY) - if(1) - to_chat(owner, span_warning("You start feeling hungrier, you feel like a normal frenzy won't satiate it enough anymore.")) - owner.current.apply_status_effect(STATUS_EFFECT_FRENZY) - if(2 to INFINITY) - if(do_after(user, 2 SECONDS, user, IGNORE_ALL)) - playsound(user.loc, 'sound/weapons/slash.ogg', 25, 1) - to_chat(user, span_warning("You skin rips and tears.")) - if(do_after(user, 1 SECONDS, user, IGNORE_ALL)) - playsound(user.loc, 'sound/weapons/slashmiss.ogg', 25, 1) - to_chat(user, span_warning("You heart pumps blackened blood into your veins as your skin turns into fur.")) - if(do_after(user, 1 SECONDS, user, IGNORE_ALL)) - playsound(user.loc, 'sound/weapons/slice.ogg', 25, 1) - to_chat(user, span_boldnotice("YOU HAVE AWOKEN.")) - var/mob/living/simple_animal/hostile/bloodsucker/werewolf/ww - if(!ww || ww.stat == DEAD) - AddBloodVolume(560 - user.blood_volume) //so it doesn't happen multiple times and refills your blood when you get out again - ww = new /mob/living/simple_animal/hostile/bloodsucker/werewolf(user.loc) - user.forceMove(ww) - ww.bloodsucker = user - user.mind.transfer_to(ww) - var/list/wolf_powers = list(new /datum/action/cooldown/bloodsucker/targeted/feast,) - for(var/datum/action/cooldown/bloodsucker/power in powers) - if(istype(power, /datum/action/cooldown/bloodsucker/fortitude)) - wolf_powers += new /datum/action/cooldown/bloodsucker/gangrel/wolfortitude - if(istype(power, /datum/action/cooldown/bloodsucker/targeted/lunge)) - wolf_powers += new /datum/action/cooldown/bloodsucker/targeted/pounce - if(istype(power, /datum/action/cooldown/bloodsucker/cloak)) - wolf_powers += new /datum/action/cooldown/bloodsucker/gangrel/howl - if(istype(power, /datum/action/cooldown/bloodsucker/targeted/trespass)) - wolf_powers += new /datum/action/cooldown/bloodsucker/gangrel/rabidism - for(var/datum/action/cooldown/bloodsucker/power in wolf_powers) - power.Grant(ww) - frenzies ++ - else - owner.current.apply_status_effect(STATUS_EFFECT_FRENZY) + INVOKE_ASYNC(src, PROC_REF(enter_frenzy)) else if(bloodsucker_blood_volume < BLOOD_VOLUME_BAD(owner.current)) additional_regen = 0.1 else if(bloodsucker_blood_volume < BLOOD_VOLUME_OKAY(owner.current)) @@ -343,6 +216,12 @@ else additional_regen = 0.5 +/datum/antagonist/bloodsucker/proc/enter_frenzy() + to_chat(world, "frenzy not implemented") + +//////////////////////////////////////////////////////////////////////////////////// +//--------------------------------Day night cycle---------------------------------// +//////////////////////////////////////////////////////////////////////////////////// /// Cycle through all vamp antags and check if they're inside a closet. /datum/antagonist/bloodsucker/proc/handle_sol() SIGNAL_HANDLER @@ -438,6 +317,9 @@ message_admins("Sol has been deleted due to the lack of Bloodsuckers") SSsunlight.can_fire = FALSE +//////////////////////////////////////////////////////////////////////////////////// +//------------------------------------Torpor--------------------------------------// +//////////////////////////////////////////////////////////////////////////////////// /datum/antagonist/bloodsucker/proc/check_begin_torpor(SkipChecks = FALSE) /// Are we entering Torpor via Sol/Death? Then entering it isnt optional! if(SkipChecks) @@ -468,48 +350,58 @@ torpor_end() /datum/antagonist/bloodsucker/proc/torpor_begin() - var/mob/living/carbon/human/bloodsucker = owner.current to_chat(owner.current, span_notice("You enter the horrible slumber of deathless Torpor. You will heal until you are renewed.")) + + var/mob/living/carbon/human/bloodsucker = owner.current + if(istype(bloodsucker)) + bloodsucker.physiology.brute_mod *= 0 + bloodsucker.physiology.burn_mod *= 0.75 + // Force them to go to sleep REMOVE_TRAIT(owner.current, TRAIT_SLEEPIMMUNE, BLOODSUCKER_TRAIT) // Without this, you'll just keep dying while you recover. owner.current.add_traits(list(TRAIT_NODEATH, TRAIT_FAKEDEATH, TRAIT_DEATHCOMA, TRAIT_RESISTLOWPRESSURE, TRAIT_RESISTHIGHPRESSURE), BLOODSUCKER_TRAIT) - bloodsucker.physiology.brute_mod *= 0 - bloodsucker.physiology.burn_mod *= 0.75 owner.current.set_timed_status_effect(0 SECONDS, /datum/status_effect/jitter, only_if_higher = TRUE) - // Disable ALL Powers - DisableAllPowers() /datum/antagonist/bloodsucker/proc/torpor_end() var/mob/living/carbon/human/bloodsucker = owner.current - owner.current.grab_ghost() - to_chat(owner.current, span_warning("You have recovered from Torpor.")) - bloodsucker.physiology.brute_mod = initial(bloodsucker.physiology.brute_mod) - bloodsucker.physiology.burn_mod = initial(bloodsucker.physiology.brute_mod) + if(istype(bloodsucker)) + bloodsucker.physiology.brute_mod = initial(bloodsucker.physiology.brute_mod) + bloodsucker.physiology.burn_mod = initial(bloodsucker.physiology.brute_mod) + owner.current.remove_traits(list(TRAIT_NODEATH, TRAIT_FAKEDEATH, TRAIT_DEATHCOMA, TRAIT_RESISTLOWPRESSURE, TRAIT_RESISTHIGHPRESSURE), BLOODSUCKER_TRAIT) - if(!HAS_TRAIT(owner.current, TRAIT_MASQUERADE)) - ADD_TRAIT(owner.current, TRAIT_SLEEPIMMUNE, BLOODSUCKER_TRAIT) - heal_vampire_organs() + ADD_TRAIT(owner.current, TRAIT_SLEEPIMMUNE, BLOODSUCKER_TRAIT) - SEND_SIGNAL(src, BLOODSUCKER_EXIT_TORPOR) + heal_vampire_organs() + owner.current.grab_ghost() + to_chat(owner.current, span_warning("You have recovered from Torpor.")) -/// Makes your blood_volume look like your bloodsucker blood, unless you're Masquerading. -/datum/antagonist/bloodsucker/proc/update_blood() - if(!iscarbon(owner.current)) +//////////////////////////////////////////////////////////////////////////////////// +//------------------------------------DEATH---------------------------------------// +//////////////////////////////////////////////////////////////////////////////////// +/// FINAL DEATH +/datum/antagonist/bloodsucker/proc/HandleDeath() + // Not "Alive"? + if(!owner.current) + FinalDeath() return - var/mob/living/carbon/bloodsucker = owner.current - if(LAZYFIND(bloodsucker.dna.species.species_traits, NOBLOOD)) + // Fire Damage and Fledgeling? (above double health) + if(owner.current.getFireLoss() >= owner.current.maxHealth * 2 && bloodsucker_level < 4) + FinalDeath() return - //If we're on Masquerade, we appear to have full blood, unless we are REALLY low, in which case we don't look as bad. - if(HAS_TRAIT(owner.current, TRAIT_MASQUERADE)) - if(bloodsucker_blood_volume >= BLOOD_VOLUME_OKAY(owner.current)) - owner.current.blood_volume = initial(bloodsucker_blood_volume) - else if(bloodsucker_blood_volume >= BLOOD_VOLUME_BAD(owner.current)) - owner.current.blood_volume = BLOOD_VOLUME_SAFE(owner.current) - else - owner.current.blood_volume = BLOOD_VOLUME_OKAY(owner.current) + // Fire Damage and Daytime? + if(owner.current.getFireLoss() >= owner.current.maxHealth * 2 && (SSsunlight.sunlight_active || frenzied)) + FinalDeath() return - owner.current.blood_volume = bloodsucker_blood_volume + // Staked while "Temp Death" or Asleep + if(owner.current.StakeCanKillMe() && owner.current.am_staked()) + FinalDeath() + return + // Temporary Death? Convert to Torpor. + if(HAS_TRAIT(owner.current, TRAIT_NODEATH)) + return + to_chat(owner.current, span_danger("Your immortal body will not yet relinquish your soul to the abyss. You enter Torpor.")) + check_begin_torpor(TRUE) /// Gibs the Bloodsucker, roundremoving them. /datum/antagonist/bloodsucker/proc/FinalDeath() @@ -518,16 +410,17 @@ return free_all_vassals() - DisableAllPowers(forced = TRUE) if(!iscarbon(owner.current)) owner.current.gib(TRUE, FALSE, FALSE) return + // Drop anything in us and play a tune var/mob/living/carbon/user = owner.current - owner.current.drop_all_held_items() - owner.current.unequip_everything() + user.drop_all_held_items() + user.unequip_everything() user.remove_all_embedded_objects() - playsound(owner.current, 'sound/effects/tendril_destroyed.ogg', 40, TRUE) + playsound(user, 'sound/effects/tendril_destroyed.ogg', 40, TRUE) + var/unique_death = SEND_SIGNAL(src, BLOODSUCKER_FINAL_DEATH) if(unique_death & DONT_DUST) return @@ -547,6 +440,9 @@ user.gib(TRUE, FALSE, FALSE) +//////////////////////////////////////////////////////////////////////////////////// +//----------------------------------MOODLETS--------------------------------------// +//////////////////////////////////////////////////////////////////////////////////// // Bloodsuckers moodlets // /datum/mood_event/drankblood description = "I have fed greedily from that which nourishes me.\n" diff --git a/code/modules/antagonists/bloodsuckers/structures/bloodsucker_recipes.dm b/code/modules/antagonists/bloodsuckers/structures/bloodsucker_recipes.dm deleted file mode 100644 index 83da1840720f..000000000000 --- a/code/modules/antagonists/bloodsuckers/structures/bloodsucker_recipes.dm +++ /dev/null @@ -1,233 +0,0 @@ -/// From recipes.dm - -///////////////////////// -/// Coffins /// -///////////////////////// - -/datum/crafting_recipe/blackcoffin - name = "Black Coffin" - result = /obj/structure/closet/crate/coffin/blackcoffin - tool_behaviors = list(TOOL_WELDER, TOOL_SCREWDRIVER) - reqs = list( - /obj/item/stack/sheet/cloth = 1, - /obj/item/stack/sheet/mineral/wood = 5, - /obj/item/stack/sheet/metal = 1, - ) - time = 15 SECONDS - category = CAT_STRUCTURES - -/datum/crafting_recipe/securecoffin - name = "Secure Coffin" - result = /obj/structure/closet/crate/coffin/securecoffin - tool_behaviors = list(TOOL_WELDER, TOOL_SCREWDRIVER) - reqs = list( - /obj/item/stack/rods = 1, - /obj/item/stack/sheet/plasteel = 5, - /obj/item/stack/sheet/metal = 5, - ) - time = 15 SECONDS - category = CAT_STRUCTURES - -/datum/crafting_recipe/meatcoffin - name = "Meat Coffin" - result = /obj/structure/closet/crate/coffin/meatcoffin - tool_behaviors = list(TOOL_WELDER, TOOL_WRENCH) - reqs = list( - /obj/item/reagent_containers/food/snacks/meat/slab = 5, - /obj/item/restraints/handcuffs/cable = 1, - ) - time = 15 SECONDS - category = CAT_STRUCTURES - always_available = FALSE //The sacred coffin! - -/datum/crafting_recipe/metalcoffin - name = "Metal Coffin" - result = /obj/structure/closet/crate/coffin/metalcoffin - reqs = list( - /obj/item/stack/sheet/metal = 6, - /obj/item/stack/rods = 2, - ) - time = 10 SECONDS - category = CAT_STRUCTURES - -//////////////////////////// -/// Structures /// -//////////////////////////// - -/datum/crafting_recipe/bloodaltar - name = "Blood Altar" - result = /obj/structure/bloodsucker/bloodaltar - tool_behaviors = list(TOOL_WELDER, TOOL_WRENCH) - reqs = list( - /obj/item/stack/rods = 5, - /obj/item/stack/sheet/metal = 5, - /datum/reagent/ash = 30, - ) - time = 13 SECONDS - category = CAT_STRUCTURES - always_available = FALSE - -/datum/crafting_recipe/restingplace - name = "Resting Place" - result = /obj/structure/bloodsucker/bloodaltar/restingplace - tool_behaviors = list(TOOL_WRENCH, TOOL_SCREWDRIVER) - reqs = list( - /obj/item/stack/rods = 5, - /obj/item/stack/sheet/metal = 5, - /obj/item/stack/sheet/cloth = 2, //that's right it comes with bones FREE OF CHARGE - ) - time = 15 SECONDS - category = CAT_STRUCTURES - always_available = FALSE - -/datum/crafting_recipe/vassalrack - name = "Persuasion Rack" - result = /obj/structure/bloodsucker/vassalrack - tool_behaviors = list(TOOL_WELDER, TOOL_WRENCH) - reqs = list( - /obj/item/stack/sheet/mineral/wood = 3, - /obj/item/stack/sheet/metal = 2, - /obj/item/restraints/handcuffs/cable = 2, - ) - time = 15 SECONDS - category = CAT_STRUCTURES - always_available = FALSE - -/datum/crafting_recipe/staketrap - name = "Stake Trap" - result = /obj/item/restraints/legcuffs/beartrap/bloodsucker - tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_HATCHET) - reqs = list( - /obj/item/stake = 2, - /obj/item/stack/sheet/mineral/wood = 2, - /obj/item/restraints/handcuffs/cable = 1, - ) - time = 12.5 SECONDS - category = CAT_STRUCTURES - always_available = FALSE - -/datum/crafting_recipe/candelabrum - name = "Candelabrum" - result = /obj/structure/bloodsucker/candelabrum - tool_behaviors = list(TOOL_WELDER, TOOL_WRENCH) - reqs = list( - /obj/item/stack/sheet/metal = 3, - /obj/item/stack/rods = 1, - /obj/item/candle = 1, - ) - time = 10 SECONDS - category = CAT_STRUCTURES - always_available = FALSE - -/datum/crafting_recipe/moldingstone - name = "Molding Stone" - result = /obj/structure/bloodsucker/moldingstone - tool_behaviors = list(TOOL_WELDER) - tool_paths = list(/obj/item/bloodsucker/chisel) - reqs = list( - /obj/item/stack/sheet/metal = 5, - /obj/item/stack/rods = 6, - ) - time = 8 SECONDS - category = CAT_STRUCTURES - always_available = FALSE - -/* -/datum/crafting_recipe/bloodthrone - name = "Blood Throne" - result = /obj/structure/bloodsucker/bloodthrone - tool_behaviors = list(TOOL_WRENCH) - reqs = list( - /obj/item/stack/sheet/cloth = 3, - /obj/item/stack/sheet/metal = 5, - /obj/item/stack/sheet/mineral/wood = 1, - ) - time = 5 SECONDS - category = CAT_STRUCTURES - always_available = FALSE -*/ - -/datum/crafting_recipe/possessedarmor - name = "Subservent Armor" - result = /obj/structure/bloodsucker/possessedarmor - tool_behaviors = list(TOOL_WRENCH, TOOL_WELDER, TOOL_SCREWDRIVER) - reqs = list( - /obj/item/stack/rods = 5, - /obj/item/stack/sheet/metal = 15, - ) - time = 10 SECONDS - category = CAT_STRUCTURES - always_available = FALSE - -//////////////////////// -/// Stakes /// -//////////////////////// - -/datum/crafting_recipe/stake - name = "Stake" - result = /obj/item/stake - reqs = list(/obj/item/stack/sheet/mineral/wood = 3) - time = 8 SECONDS - category = CAT_WEAPON_MELEE - -/datum/crafting_recipe/woodenducky - name = "Wooden Ducky" - result = /obj/item/stake/ducky - tool_behaviors = list(TOOL_HATCHET) - reqs = list( - /obj/item/stake = 1, - /obj/item/bikehorn/rubberducky = 1, - ) - time = 6 SECONDS - category = CAT_WEAPON_MELEE - always_available = FALSE - -/datum/crafting_recipe/hardened_stake - name = "Hardened Stake" - result = /obj/item/stake/hardened - tool_behaviors = list(TOOL_WELDER) - reqs = list(/obj/item/stack/rods = 1) - time = 6 SECONDS - category = CAT_WEAPON_MELEE - always_available = FALSE - -/datum/crafting_recipe/silver_stake - name = "Silver Stake" - result = /obj/item/stake/hardened/silver - tool_behaviors = list(TOOL_WELDER) - reqs = list( - /obj/item/stack/sheet/mineral/silver = 1, - /obj/item/stake/hardened = 1, - ) - time = 8 SECONDS - category = CAT_WEAPON_MELEE - always_available = FALSE - -//////////////////////// -/// Tools /// -//////////////////////// - -/datum/crafting_recipe/chisel - name = "Chisel" - result = /obj/item/bloodsucker/chisel - tool_behaviors = list(TOOL_WELDER, TOOL_SCREWDRIVER) - reqs = list( - /obj/item/stack/sheet/metal = 3 - ) - time = 5 SECONDS - category = CAT_TOOLS - always_available = FALSE - -/* -/datum/crafting_recipe/bloodybrush - name = "Artist's Brush" - result = /obj/item/bloodsucker/bloodybrush - tool_behaviors = list(TOOL_HATCHET) - reqs = list( - /obj/item/stack/sheet/mineral/wood = 2, - /obj/item/stack/sheet/cloth = 1, - ) - time = 5 SECONDS - category = CAT_TOOLS - always_available = FALSE -*/ diff --git a/code/modules/antagonists/bloodsuckers/vassal/bloodsucker_conversion.dm b/code/modules/antagonists/bloodsuckers/vassal/bloodsucker_conversion.dm index a25ab010d966..4eb1cea37dea 100644 --- a/code/modules/antagonists/bloodsuckers/vassal/bloodsucker_conversion.dm +++ b/code/modules/antagonists/bloodsuckers/vassal/bloodsucker_conversion.dm @@ -37,10 +37,6 @@ var/mob/living/master = conversion_target.mind.enslaved_to?.resolve() if(!master || (master == owner.current)) return TRUE - var/datum/antagonist/bloodsucker/bloodsuckerdatum = master.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(bloodsuckerdatum?.broke_masquerade) - //vassal stealing - return TRUE to_chat(owner.current, span_danger("[conversion_target]'s mind is overwhelmed with too much external force to put your own!")) return FALSE diff --git a/code/modules/antagonists/bloodsuckers/vassal/revenge_vassal.dm b/code/modules/antagonists/bloodsuckers/vassal/revenge_vassal.dm index b4379f50ed6f..1ea126322a9d 100644 --- a/code/modules/antagonists/bloodsuckers/vassal/revenge_vassal.dm +++ b/code/modules/antagonists/bloodsuckers/vassal/revenge_vassal.dm @@ -43,14 +43,14 @@ /datum/antagonist/vassal/revenge/ui_static_data(mob/user) var/list/data = list() - for(var/datum/action/cooldown/bloodsucker/power as anything in powers) - var/list/power_data = list() + // for(var/datum/action/cooldown/bloodsucker/power as anything in powers) + // var/list/power_data = list() - power_data["power_name"] = power.name - power_data["power_explanation"] = power.power_explanation - power_data["power_icon"] = power.button_icon_state + // power_data["power_name"] = power.name + // power_data["power_explanation"] = power.power_explanation + // power_data["power_icon"] = power.button_icon_state - data["power"] += list(power_data) + // data["power"] += list(power_data) return data + ..() @@ -60,12 +60,12 @@ show_in_roundend = TRUE for(var/datum/objective/all_objectives as anything in objectives) objectives -= all_objectives - BuyPower(new /datum/action/cooldown/bloodsucker/vassal_blood) - for(var/datum/action/cooldown/bloodsucker/master_powers as anything in bloodsuckerdatum.powers) - if(master_powers.purchase_flags & BLOODSUCKER_DEFAULT_POWER) - continue - master_powers.Grant(owner.current) - owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer/vassal_edition) + // BuyPower(new /datum/action/cooldown/bloodsucker/vassal_blood) + // for(var/datum/action/cooldown/bloodsucker/master_powers as anything in bloodsuckerdatum.powers) + // if(master_powers.purchase_flags & BLOODSUCKER_DEFAULT_POWER) + // continue + // master_powers.Grant(owner.current) + // owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer/vassal_edition) var/datum/objective/survive/new_objective = new new_objective.name = "Avenge Bloodsucker" diff --git a/code/modules/antagonists/bloodsuckers/vassal/vassal.dm b/code/modules/antagonists/bloodsuckers/vassal/vassal.dm index fa7c6168b3d6..8256f784f497 100644 --- a/code/modules/antagonists/bloodsuckers/vassal/vassal.dm +++ b/code/modules/antagonists/bloodsuckers/vassal/vassal.dm @@ -82,7 +82,7 @@ owner.enslave_mind_to_creator(master.owner.current) owner.current.log_message("has been vassalized by [master.owner.current]!", LOG_ATTACK, color="#960000") /// Give Recuperate Power - BuyPower(new /datum/action/cooldown/bloodsucker/recuperate) + //BuyPower(new /datum/action/cooldown/bloodsucker/recuperate) /// Give Objectives var/datum/objective/vassal_objective/vassal_objective = new vassal_objective.owner = owner @@ -104,20 +104,14 @@ for(var/all_status_traits in owner.current._status_traits) REMOVE_TRAIT(owner.current, all_status_traits, BLOODSUCKER_TRAIT) //Remove Recuperate Power - while(powers.len) - var/datum/action/cooldown/bloodsucker/power = pick(powers) - powers -= power - power.Remove(owner.current) + // while(powers.len) + // var/datum/action/cooldown/bloodsucker/power = pick(powers) + // powers -= power + // power.Remove(owner.current) //Remove Language & Hud owner.current.remove_language(/datum/language/vampiric) return ..() -/datum/antagonist/vassal/on_body_transfer(mob/living/old_body, mob/living/new_body) - . = ..() - for(var/datum/action/cooldown/bloodsucker/all_powers as anything in powers) - all_powers.Remove(old_body) - all_powers.Grant(new_body) - /datum/antagonist/vassal/proc/add_objective(datum/objective/added_objective) objectives += added_objective @@ -170,11 +164,11 @@ new_owner.add_antag_datum(src) to_chat(choice, span_notice("Through divine intervention, you've gained a new vassal!")) -/datum/antagonist/vassal/proc/toreador_levelup_mesmerize() //Don't need stupid args - for(var/datum/action/cooldown/bloodsucker/targeted/mesmerize/mesmerize_power in powers) - if(!istype(mesmerize_power)) - continue - mesmerize_power.level_current = max(master.bloodsucker_level, 1) +// /datum/antagonist/vassal/proc/toreador_levelup_mesmerize() //Don't need stupid args +// for(var/datum/action/cooldown/bloodsucker/targeted/mesmerize/mesmerize_power in powers) +// if(!istype(mesmerize_power)) +// continue +// mesmerize_power.level_current = max(master.bloodsucker_level, 1) /// If we weren't created by a bloodsucker, then we cannot be a vassal (assigned from antag panel) /datum/antagonist/vassal/can_be_owned(datum/mind/new_owner) @@ -200,13 +194,13 @@ vassal.remove_antag_datum(/datum/antagonist/vassal) /// Used when your Master teaches you a new Power. -/datum/antagonist/vassal/proc/BuyPower(datum/action/cooldown/bloodsucker/power) - powers += power - power.Grant(owner.current) +// /datum/antagonist/vassal/proc/BuyPower(datum/action/cooldown/bloodsucker/power) +// powers += power +// power.Grant(owner.current) -/datum/antagonist/vassal/proc/LevelUpPowers() - for(var/datum/action/cooldown/bloodsucker/power in powers) - power.level_current++ +// /datum/antagonist/vassal/proc/LevelUpPowers() +// for(var/datum/action/cooldown/bloodsucker/power in powers) +// power.level_current++ /// Called when we are made into the Favorite Vassal /datum/antagonist/vassal/proc/make_special(datum/antagonist/vassal/vassal_type) @@ -373,7 +367,7 @@ returnIcon = "[icon2html('icons/mob/vampiric.dmi', world, "vassal")]" // Am I someone ELSE'S Vassal? else if(IS_BLOODSUCKER(viewer) || IS_MONSTERHUNTER(viewer)) - returnString += "This [carbon_current.dna.species.name] bears the mark of [master.return_full_name()][master.broke_masquerade ? " who has broken the Masquerade" : ""]" + returnString += "This [carbon_current.dna.species.name] bears the mark of [master.return_full_name()]" returnIcon = "[icon2html('icons/mob/vampiric.dmi', world, "vassal_grey")]" // Are you serving the same master as I am? else if(viewer.mind.has_antag_datum(/datum/antagonist/vassal) in master.vassals) diff --git a/code/modules/antagonists/eldritch_cult/eldritch_effects.dm b/code/modules/antagonists/eldritch_cult/eldritch_effects.dm index 80e587eea151..196a258c3356 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_effects.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_effects.dm @@ -211,10 +211,9 @@ if(!ishuman(user)) return ..() var/mob/living/carbon/human/human_user = user - var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(/datum/antagonist/bloodsucker) if(IS_HERETIC(human_user)) to_chat(human_user, span_boldwarning("You know better than to tempt forces out of your control!")) - if(IS_BLOODSUCKER(human_user) || bloodsuckerdatum.my_clan?.get_clan() == CLAN_LASOMBRA) + if(IS_BLOODSUCKER(human_user)) to_chat(human_user, span_boldwarning("This shard has already been harvested!")) else var/obj/item/bodypart/arm = human_user.get_active_hand() diff --git a/code/modules/antagonists/monsterhunter/monsterhunter.dm b/code/modules/antagonists/monsterhunter/monsterhunter.dm index e148d751f89a..b074ddca2e04 100644 --- a/code/modules/antagonists/monsterhunter/monsterhunter.dm +++ b/code/modules/antagonists/monsterhunter/monsterhunter.dm @@ -13,145 +13,7 @@ var/list/datum/action/powers = list() var/datum/martial_art/hunterfu/my_kungfu = new var/give_objectives = TRUE - var/datum/action/cooldown/bloodsucker/trackvamp = new /datum/action/cooldown/bloodsucker/trackvamp() - var/datum/action/cooldown/bloodsucker/fortitude = new /datum/action/cooldown/bloodsucker/fortitude/hunter() -/datum/antagonist/monsterhunter/apply_innate_effects(mob/living/mob_override) - . = ..() - var/mob/living/current_mob = mob_override || owner.current - ADD_TRAIT(current_mob, TRAIT_NOSOFTCRIT, BLOODSUCKER_TRAIT) - ADD_TRAIT(current_mob, TRAIT_NOCRITDAMAGE, BLOODSUCKER_TRAIT) - owner.unconvertable = TRUE - my_kungfu.teach(current_mob, make_temporary = FALSE) - -/datum/antagonist/monsterhunter/remove_innate_effects(mob/living/mob_override) - . = ..() - var/mob/living/current_mob = mob_override || owner.current - REMOVE_TRAIT(current_mob, TRAIT_NOSOFTCRIT, BLOODSUCKER_TRAIT) - REMOVE_TRAIT(current_mob, TRAIT_NOCRITDAMAGE, BLOODSUCKER_TRAIT) - owner.unconvertable = FALSE - if(my_kungfu) - my_kungfu.remove(current_mob) - - -/datum/antagonist/monsterhunter/on_gain() - //Give Monster Hunter powers - trackvamp.Grant(owner.current) - fortitude.Grant(owner.current) - if(give_objectives) - //Give Hunter Objective - var/datum/objective/monsterhunter/monsterhunter_objective = new - monsterhunter_objective.owner = owner - objectives += monsterhunter_objective - //Give Theft Objective - var/datum/objective/steal/steal_objective = new - steal_objective.owner = owner - steal_objective.find_target() - objectives += steal_objective - - //Teach Stake crafting - owner.teach_crafting_recipe(/datum/crafting_recipe/hardened_stake) - owner.teach_crafting_recipe(/datum/crafting_recipe/silver_stake) - return ..() - -/datum/antagonist/monsterhunter/on_removal() - //Remove Monster Hunter powers - trackvamp.Remove(owner.current) - fortitude.Remove(owner.current) - to_chat(owner.current, span_userdanger("Your hunt has ended: You enter retirement once again, and are no longer a Monster Hunter.")) - return ..() - -/datum/antagonist/monsterhunter/on_body_transfer(mob/living/old_body, mob/living/new_body) - . = ..() - for(var/datum/action/cooldown/bloodsucker/all_powers as anything in powers) - all_powers.Remove(old_body) - all_powers.Grant(new_body) - -/// Mind version -/datum/mind/proc/make_monsterhunter() - var/datum/antagonist/monsterhunter/monsterhunterdatum = has_antag_datum(/datum/antagonist/monsterhunter) - if(!monsterhunterdatum) - monsterhunterdatum = add_antag_datum(/datum/antagonist/monsterhunter) - special_role = ROLE_MONSTERHUNTER - return monsterhunterdatum - -/datum/mind/proc/remove_monsterhunter() - var/datum/antagonist/monsterhunter/monsterhunterdatum = has_antag_datum(/datum/antagonist/monsterhunter) - if(monsterhunterdatum) - remove_antag_datum(/datum/antagonist/monsterhunter) - special_role = null - -/// Called when using admin tools to give antag status -/datum/antagonist/monsterhunter/admin_add(datum/mind/new_owner, mob/admin) - message_admins("[key_name_admin(admin)] made [key_name_admin(new_owner)] into [name].") - log_admin("[key_name(admin)] made [key_name(new_owner)] into [name].") - new_owner.add_antag_datum(src) - -/// Called when removing antagonist using admin tools -/datum/antagonist/monsterhunter/admin_remove(mob/user) - if(!user) - return - message_admins("[key_name_admin(user)] has removed [name] antagonist status from [key_name_admin(owner)].") - log_admin("[key_name(user)] has removed [name] antagonist status from [key_name(owner)].") - on_removal() - -/datum/antagonist/monsterhunter/proc/add_objective(datum/objective/added_objective) - objectives += added_objective - -/datum/antagonist/monsterhunter/proc/remove_objectives(datum/objective/removed_objective) - objectives -= removed_objective - -/datum/antagonist/monsterhunter/greet() - . = ..() - to_chat(owner.current, span_userdanger("After witnessing recent events on the station, we return to your old profession, we are a Monster Hunter!")) - to_chat(owner.current, span_announce("While we can kill anyone in our way to destroy the monsters lurking around, causing property damage is unacceptable.")) - to_chat(owner.current, span_announce("However, security WILL detain us if they discover our mission.")) - to_chat(owner.current, span_announce("In exchange for our services, it shouldn't matter if a few items are gone missing for our... personal collection.")) - owner.current.playsound_local(null, 'sound/effects/his_grace_ascend.ogg', 100, FALSE, pressure_affected = FALSE) - owner.announce_objectives() - -////////////////////////////////////////////////////////////////////////// -// Monster Hunter Pinpointer -////////////////////////////////////////////////////////////////////////// - -/// TAKEN FROM: /datum/action/changeling/pheromone_receptors // pheromone_receptors.dm for a version of tracking that Changelings have! -/datum/status_effect/agent_pinpointer/hunter_edition - alert_type = /atom/movable/screen/alert/status_effect/agent_pinpointer/hunter_edition - minimum_range = HUNTER_SCAN_MIN_DISTANCE - tick_interval = HUNTER_SCAN_PING_TIME - duration = 10 SECONDS - range_fuzz_factor = 5 //PINPOINTER_EXTRA_RANDOM_RANGE - -/atom/movable/screen/alert/status_effect/agent_pinpointer/hunter_edition - name = "Monster Tracking" - desc = "You always know where the hellspawn are." - -/datum/status_effect/agent_pinpointer/hunter_edition/scan_for_target() - var/turf/my_loc = get_turf(owner) - - var/list/mob/living/carbon/monsters = list() - for(var/datum/antagonist/monster in GLOB.antagonists) - var/datum/mind/brain = monster.owner - if(brain == owner || !brain) - continue - if(IS_HERETIC(brain.current) || IS_BLOODSUCKER(brain.current) || iscultist(brain.current) || is_servant_of_ratvar(brain.current) || IS_WIZARD(brain.current)) - monsters += brain - if(brain.has_antag_datum(/datum/antagonist/changeling)) - monsters += brain - if(brain.has_antag_datum(/datum/antagonist/ashwalker)) - monsters += brain - - if(monsters.len) - /// Point at a 'random' monster, biasing heavily towards closer ones. - scan_target = pickweight(monsters) - to_chat(owner, span_warning("You detect signs of monsters to the [dir2text(get_dir(my_loc,get_turf(scan_target)))]!")) - else - scan_target = null - -/datum/status_effect/agent_pinpointer/hunter_edition/Destroy() - if(scan_target) - to_chat(owner, span_notice("You've lost the trail.")) - . = ..() /datum/outfit/monsterhunter name = "Monster Hunter" diff --git a/code/modules/antagonists/monsterhunter/monstertrack.dm b/code/modules/antagonists/monsterhunter/monstertrack.dm deleted file mode 100644 index 8d93ebfcd290..000000000000 --- a/code/modules/antagonists/monsterhunter/monstertrack.dm +++ /dev/null @@ -1,81 +0,0 @@ -/// From 'Cellular Emporium'... somehow? -/datum/action/cooldown/bloodsucker/trackvamp - name = "Track Monster" - desc = "Take a moment to look for clues of any nearby monsters.
These creatures are slippery, and often look like the crew." - background_icon = 'icons/mob/actions/actions_bloodsucker.dmi' - button_icon = 'icons/mob/actions/actions_bloodsucker.dmi' - background_icon_state = "vamp_power_off" - button_icon_state = "power_hunter" - power_flags = BP_AM_STATIC_COOLDOWN - check_flags = BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS - purchase_flags = NONE - cooldown_time = 30 SECONDS - bloodcost = 0 - /// Removed, set to TRUE to re-add, either here to be a default function, or in-game through VV for neat Admin stuff -Willard - var/give_pinpointer = FALSE - -/datum/action/cooldown/bloodsucker/trackvamp/ActivatePower() - . = ..() - /// Return text indicating direction - to_chat(owner, span_notice("You look around, scanning your environment and discerning signs of any filthy, wretched affronts to the natural order...")) - if(!do_after(owner, 6 SECONDS)) - to_chat(owner,span_warning("You were interrupted and lost the tracks!")) - DeactivatePower() - return - if(give_pinpointer) - var/mob/living/user = owner - user.apply_status_effect(/datum/status_effect/agent_pinpointer/hunter_edition) - display_proximity() - DeactivatePower() - -/datum/action/cooldown/bloodsucker/trackvamp/proc/display_proximity() - /// Pick target - var/turf/my_loc = get_turf(owner) - var/closest_dist = 9999 - var/mob/living/closest_monster - - /// Track ALL living Monsters. - var/list/datum/mind/monsters = list() - for(var/mob/living/carbon/all_carbons in GLOB.alive_mob_list) - if(all_carbons == owner) //don't track ourselves! - continue - if(!all_carbons.mind) - continue - var/datum/mind/carbon_minds = all_carbons.mind - if(IS_HERETIC(all_carbons) || IS_BLOODSUCKER(all_carbons) || iscultist(all_carbons) || is_servant_of_ratvar(all_carbons) || IS_WIZARD(all_carbons)) - monsters += carbon_minds - if(carbon_minds.has_antag_datum(/datum/antagonist/changeling)) - monsters += carbon_minds - if(carbon_minds.has_antag_datum(/datum/antagonist/sinfuldemon)) - monsters += carbon_minds - if(carbon_minds.has_antag_datum(/datum/antagonist/ashwalker)) - monsters += carbon_minds - if(carbon_minds.has_antag_datum(/datum/antagonist/vampire)) //yogs, still supporting vampires! - monsters += carbon_minds - if(carbon_minds.has_antag_datum(/datum/antagonist/darkspawn)) //yogs, darkspawns - monsters += carbon_minds - - for(var/datum/mind/monster_minds in monsters) - if(!monster_minds.current || monster_minds.current == owner) // || !get_turf(M.current) || !get_turf(owner)) - continue - for(var/antag_datums in monster_minds.antag_datums) - var/datum/antagonist/antag_datum = antag_datums - if(!istype(antag_datum)) - continue - var/their_loc = get_turf(monster_minds.current) - var/distance = get_dist_euclidian(my_loc, their_loc) - /// Found One: Closer than previous/max distance - if(distance < closest_dist && distance <= HUNTER_SCAN_MAX_DISTANCE) - closest_dist = distance - closest_monster = monster_minds.current - /// Stop searching through my antag datums and go to the next guy - break - - /// Found one! - if(closest_monster) - var/distString = closest_dist <= HUNTER_SCAN_MAX_DISTANCE / 2 ? "somewhere nearby!" : "somewhere in the distance." - to_chat(owner, span_warning("You detect signs of monsters [distString]")) - - /// Will yield a "?" - else - to_chat(owner, span_notice("There are no monsters nearby.")) diff --git a/code/modules/events/tzimisce.dm b/code/modules/events/tzimisce.dm deleted file mode 100644 index d9ff50451421..000000000000 --- a/code/modules/events/tzimisce.dm +++ /dev/null @@ -1,83 +0,0 @@ -/datum/round_event_control/tzimisce - name = "Spawn Tzimisce" - typepath = /datum/round_event/ghost_role/tzimisce - max_occurrences = 2 - min_players = 25 - earliest_start = 45 MINUTES - track = EVENT_TRACK_ROLESET - tags = list(TAG_SPOOKY, TAG_MAGICAL, TAG_COMBAT) - description = "spawns a tzimisce bloodsucker from ghost roles." - -/datum/round_event_control/tzimisce/canSpawnEvent(players_amt, allow_magic, fake_check) - . = ..() - if(.) - for(var/mob/living/carbon/human/all_players in GLOB.player_list) - if(IS_BLOODSUCKER(all_players) || IS_MONSTERHUNTER(all_players)) - return TRUE - return FALSE - -/datum/round_event/ghost_role/tzimisce - minimum_required = 1 - role_name = "Tzimisce" - fakeable = FALSE - -/datum/round_event/ghost_role/tzimisce/spawn_role() - //selecting a spawn_loc - if(!SSjob.latejoin_trackers.len) - return MAP_ERROR - - //selecting a candidate player - var/list/candidates = get_candidates(ROLE_BLOODSUCKER, null, ROLE_BLOODSUCKER) - if(!candidates.len) - return NOT_ENOUGH_PLAYERS - - var/mob/dead/selected_candidate = pick_n_take(candidates) - var/key = selected_candidate.key - - var/datum/mind/tzimisce_mind = create_tzimisce_mind(key) - tzimisce_mind.active = TRUE - - var/mob/living/carbon/human/tzimisce = spawn_event_tzimisce() - tzimisce_mind.transfer_to(tzimisce) - tzimisce_mind.add_antag_datum(/datum/antagonist/bloodsucker) - var/datum/antagonist/bloodsucker/bloodsuckerdatum = tzimisce.mind.has_antag_datum(/datum/antagonist/bloodsucker) - bloodsuckerdatum.antag_hud_name = "tzimisce" - bloodsuckerdatum.add_team_hud(tzimisce) - bloodsuckerdatum.bloodsucker_level_unspent += round(world.time / (15 MINUTES), 1) - bloodsuckerdatum.my_clan = new /datum/bloodsucker_clan/tzimisce(bloodsuckerdatum) - bloodsuckerdatum.owner.announce_objectives() - - spawned_mobs += tzimisce - message_admins("[ADMIN_LOOKUPFLW(tzimisce)] has been made into a tzimisce bloodsucker an event.") - log_game("[key_name(tzimisce)] was spawned as a tzimisce bloodsucker by an event.") - var/datum/job/jobdatum = SSjob.GetJob(pick("Assistant", "Botanist", "Station Engineer", "Medical Doctor", "Scientist", "Cargo Technician", "Cook")) - if(SSshuttle.arrivals) - SSshuttle.arrivals.QueueAnnounce(tzimisce, jobdatum.title) - tzimisce_mind.assigned_role = jobdatum.title //sets up the manifest properly - jobdatum.equip(tzimisce) - var/obj/item/card/id/id = tzimisce.get_item_by_slot(ITEM_SLOT_ID) - if(!istype(id)) //pda on ID slot - var/obj/item/modular_computer/tablet/PDA = tzimisce.get_item_by_slot(ITEM_SLOT_ID) - var/obj/item/computer_hardware/card_slot/card_slot2 = PDA.all_components[MC_CARD2] - var/obj/item/computer_hardware/card_slot/card_slot = PDA.all_components[MC_CARD] - id = card_slot2?.stored_card || card_slot?.stored_card //check both slots, priority on 2nd - id.assignment = jobdatum.title - id.originalassignment = jobdatum.title - id.update_label() - GLOB.data_core.manifest_inject(tzimisce, force = TRUE) - tzimisce.update_move_intent_slowdown() //prevents you from going super duper fast - announce_to_ghosts(tzimisce) - return SUCCESSFUL_SPAWN - - -/datum/round_event/ghost_role/tzimisce/proc/spawn_event_tzimisce() - var/mob/living/carbon/human/new_tzimisce = new() - SSjob.SendToLateJoin(new_tzimisce) - new_tzimisce.randomize_human_appearance(~(RANDOMIZE_SPECIES)) - new_tzimisce.dna.update_dna_identity() - return new_tzimisce - -/datum/round_event/ghost_role/tzimisce/proc/create_tzimisce_mind(key) - var/datum/mind/tzimisce_mind = new /datum/mind(key) - tzimisce_mind.special_role = ROLE_BLOODSUCKER - return tzimisce_mind diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm index ba6ef66ff7aa..937a2acf7191 100644 --- a/code/modules/jobs/job_types/clown.dm +++ b/code/modules/jobs/job_types/clown.dm @@ -85,8 +85,8 @@ if(visualsOnly) return - if(H.mind) - H.mind.teach_crafting_recipe(/datum/crafting_recipe/woodenducky) + // if(H.mind) + // H.mind.teach_crafting_recipe(/datum/crafting_recipe/woodenducky) H.fully_replace_character_name(H.real_name, pick(GLOB.clown_names)) //rename the mob AFTER they're equipped so their ID gets updated properly. H.dna.add_mutation(CLOWNMUT) for(var/datum/mutation/human/clumsy/M in H.dna.mutations) diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 9d0c37b91246..9b39bf634c67 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -28,7 +28,7 @@ if((NOBLOOD in dna.species.species_traits) || bleedsuppress || (HAS_TRAIT(src, TRAIT_FAKEDEATH)) || (STABLEBLOOD in dna.species.species_traits)) return - if(mind && IS_BLOODSUCKER(src)) // Prevents Bloodsuckers from naturally regenerating Blood - Even while on masquerade + if(mind && IS_BLOODSUCKER(src)) // Prevents Bloodsuckers from naturally regenerating Blood return if(HAS_TRAIT(src, TRAIT_NOPULSE)) // Fulpstation Bloodsuckers edit - Dont regenerate blood, damnmit! diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 14dfc26ea694..ac023ddd0cac 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -281,22 +281,13 @@ if(DISGUST_LEVEL_DISGUSTED to INFINITY) msg += "[t_He] look[p_s()] extremely disgusted.\n" - - var/apparent_blood_volume = blood_volume - if(skin_tone == "albino") - apparent_blood_volume -= 150 // enough to knock you down one tier - // Fulp edit START - Bloodsuckers - var/bloodDesc = ShowAsPaleExamine(user, apparent_blood_volume) - if(bloodDesc == BLOODSUCKER_SHOW_BLOOD) // BLOODSUCKER_SHOW_BLOOD: Explicitly show the correct blood amount - switch(get_blood_state()) - if(BLOOD_OKAY) - msg += "[t_He] [t_has] pale skin.\n" - if(BLOOD_BAD) - msg += "[t_He] look[p_s()] like pale death.\n" - if(BLOOD_DEAD to BLOOD_SURVIVE) - msg += "[t_He] resemble[p_s()] a crushed, empty juice pouch.\n" - else if(bloodDesc != BLOODSUCKER_HIDE_BLOOD) // BLOODSUCKER_HIDE_BLOOD: Always show full blood - msg += bloodDesc // Else: Show custom blood message + switch(get_blood_state()) + if(BLOOD_OKAY) + msg += "[t_He] [t_has] pale skin.\n" + if(BLOOD_BAD) + msg += "[t_He] look[p_s()] like pale death.\n" + if(BLOOD_DEAD to BLOOD_SURVIVE) + msg += "[t_He] resemble[p_s()] a crushed, empty juice pouch.\n" if(bleedsuppress) msg += "[t_He] [t_is] imbued with a power that defies bleeding.\n" // only statues and highlander sword can cause this so whatever diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 20aae3b90130..9cf4047fa406 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -178,8 +178,6 @@ return if(ishuman(user)) var/mob/living/carbon/human/H = user - if(H.combat_mode && handle_vamp_biting(H)) // yogs start -- vampire biting - return // yogs end if(H.combat_mode) last_damage = "fist" dna.species.spec_attack_hand(H, src, user.mind?.martial_art, modifiers) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index d6d5df7b91da..0ebdbc368d49 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -288,21 +288,6 @@ M.remove_status_effect(/datum/status_effect/speech/stutter) holder.remove_reagent(type, volume) // maybe this is a little too perfect and a max() cap on the statuses would be better?? return - if(ishuman(M) && IS_VAMPIRE(M) && prob(80)) // Yogs Start - var/datum/antagonist/vampire/V = M.mind.has_antag_datum(ANTAG_DATUM_VAMPIRE) - if(!V.get_ability(/datum/vampire_passive/full)) - switch(data) - if(1 to 4) - to_chat(M, span_warning("Something sizzles in your veins!")) - M.adjustFireLoss(0.5) - if(5 to 12) - to_chat(M, span_danger("You feel an intense burning inside of you!")) - M.adjustFireLoss(1) - if(13 to INFINITY) - M.visible_message("[M] suddenly bursts into flames!", span_userdanger("You suddenly ignite in a holy fire!")) - M.adjust_fire_stacks(3) - M.ignite_mob() //Only problem with igniting people is currently the commonly availible fire suits make you immune to being on fire - M.adjustFireLoss(3) //Hence the other damages... ain't I a bastard? // Yogs End if(ishuman(M) && is_sinfuldemon(M) && prob(80)) switch(data) if(1 to 4) diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index 29c85c0ec534..da47f902b6db 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -40,9 +40,6 @@ span_notice("[user] puts the [src] up to their mouth."), span_notice("You take a sip from the [src]."), ) - if(IS_VAMPIRE(user)) - var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) - V.usable_blood += BLOODBAG_GULP_SIZE / 4 //they should really be drinking from people, yknow, be antagonistic? reagents.reaction(user, INGEST, fraction) reagents.trans_to(user, BLOODBAG_GULP_SIZE, transfered_by = user) diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 1d954cd3f646..e31fcf5499a6 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -345,7 +345,7 @@ SEND_SIGNAL(src, COMSIG_SPELL_AFTER_CAST, cast_on) /// Called after the effect happens, whether that's after the button press or after hitting someone with a touch ability -/datum/action/cooldown/spell/proc/consume_resource() //to-do: rework vampire blood use into using this proc +/datum/action/cooldown/spell/proc/consume_resource() if(!bypass_cost && owner.mind && LAZYLEN(resource_costs)) SEND_SIGNAL(owner.mind, COMSIG_MIND_SPEND_ANTAG_RESOURCE, resource_costs) diff --git a/code/modules/spells/spell_types/toggle/_toggle.dm b/code/modules/spells/spell_types/toggle/_toggle.dm index d1dd5ccf19a7..2af71619be64 100644 --- a/code/modules/spells/spell_types/toggle/_toggle.dm +++ b/code/modules/spells/spell_types/toggle/_toggle.dm @@ -2,6 +2,8 @@ /datum/action/cooldown/spell/toggle /// Whether we're active or not var/active = FALSE + /// An associative list of all resource costs for maintaining this ability + var/list/maintain_costs /datum/action/cooldown/spell/toggle/New() . = ..() @@ -19,6 +21,15 @@ /datum/action/cooldown/spell/toggle/process() build_all_button_icons(ALL) //so as to be consistent with situational requirements, keep the button updated + if(active && owner.mind && !bypass_cost && LAZYLEN(maintain_costs)) + for(var/i in maintain_costs) + if(SEND_SIGNAL(owner.mind, COMSIG_MIND_CHECK_ANTAG_RESOURCE, i, maintain_costs[i])) + SEND_SIGNAL(owner.mind, COMSIG_MIND_SPEND_ANTAG_RESOURCE, maintain_costs) + continue + Activate(owner) + return FALSE + return TRUE + /datum/action/cooldown/spell/toggle/cast(atom/cast_on) active = !active if(active) diff --git a/strings/tips.txt b/strings/tips.txt index 38e2f11a1805..aeb753525090 100644 --- a/strings/tips.txt +++ b/strings/tips.txt @@ -257,7 +257,6 @@ When crafting most items, you can either manually combine parts or use the craft Suit storage units not only remove blood and dirt from clothing, but also radiation! Remote devices will work when used through cameras. For example: Bluespace RPEDs and door remotes. You can light a cigar on a supermatter crystal. -YOU CAN SUCK BLOOD AS A VAMPIRE BY HARM INTENTING THE HEAD Click the ‘resist’ button to get out of a chair. Using a multitool on a flux-wave anomaly does not fix it. Stop shocking yourself on it. Normal analysers can determine a blob's type, the code to neutralize an anomaly, and the composition of gas in both the atmosphere or a tank/canister. diff --git a/tgui/packages/tgui/interfaces/AntagInfoBloodsucker.tsx b/tgui/packages/tgui/interfaces/AntagInfoBloodsucker.tsx index a5ef5802ddd6..e395b08e511f 100644 --- a/tgui/packages/tgui/interfaces/AntagInfoBloodsucker.tsx +++ b/tgui/packages/tgui/interfaces/AntagInfoBloodsucker.tsx @@ -113,10 +113,6 @@ const BloodsuckerIntro = () => { Frenzy!
- - Avoid using your Feed ability while near others, or else you - will risk breaking the Masquerade! - @@ -129,9 +125,6 @@ const BloodsuckerIntro = () => {
Examine your new structures to see how they function!
- Medical and Genetic Analyzers can sell you out, your Masquerade - ability will hide your identity to prevent this. -
diff --git a/tgui/packages/tgui/interfaces/AntagInfoVampire.tsx b/tgui/packages/tgui/interfaces/AntagInfoVampire.tsx deleted file mode 100644 index fab01ccd9ab6..000000000000 --- a/tgui/packages/tgui/interfaces/AntagInfoVampire.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { useBackend } from '../backend'; -import { Section, Stack } from '../components'; -import { BooleanLike } from 'common/react'; -import { Window } from '../layouts'; - -type Objective = { - count: number; - name: string; - explanation: string; - complete: BooleanLike; - was_uncompleted: BooleanLike; - reward: number; -}; - -type Info = { - antag_name: string; - objectives: Objective[]; - loud: boolean; -}; - -const vampireRed = { - fontWeight: 'bold', - color: 'red', -}; - -export const AntagInfoVampire = (props, context) => { - const { data } = useBackend(context); - const { antag_name } = data; - const { loud } = data; - return ( - - -
- - - You are the {antag_name}! - - -
You can consume blood from humanoid life by punching their head while on the harm intent.
-
Your bloodsucking speed depends on grab strength.
-
Having a neck grab or stronger increases blood drain rate by 50%.
-
This WILL alert everyone who can see it, as well as make a noise.
-
{loud ? '' : 'You can extract blood '}{loud ? '' : 'stealthily'}{loud ? '' : ' by initiating without a grab.'}
-
{loud ? '' : 'This will reduce the amount of blood taken by 50%.'}
-
{loud ? 'You can no longer drain blood stealthily.' : 'The ability to drain blood stealthily will be lost above 150 total blood.'}
-
Note that you cannot draw blood from catatonics or corpses.
-
- - - -
-
-
-
- ); -}; - -const ObjectivePrintout = (props, context) => { - const { data } = useBackend(context); - const { objectives } = data; - return ( - - Your objectives: - - {(!objectives && 'None!') || - objectives.map((objective) => ( - - #{objective.count}: {objective.explanation} - - ))} - - - ); -}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodsucker.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodsucker.ts index e82558d523f5..8edfe77b4838 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodsucker.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/bloodsucker.ts @@ -4,7 +4,7 @@ import { multiline } from "common/string"; export const BLOODSUCKER_MECHANICAL_DESCRIPTION = multiline` Develop your own power and establish Vassals to accomplish your goals. - Avoid Sol's harsh rays, siphon blood, and maintain the Masquerade to excel. + Avoid Sol's harsh rays, and siphon blood to excel. `; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/vampire.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/vampire.ts deleted file mode 100644 index 82ad53f85633..000000000000 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/vampire.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Antagonist, Category } from "../base"; -import { multiline } from "common/string"; - -export const VAMPIRE_MECHANICAL_DESCRIPTION - = multiline` - Use powers of the night to complete your objectives. Suck blood from individuals to fuel your supernatural abilities and grow in power. - `; - - -const Vampire: Antagonist = { - key: "vampire", - name: "Vampire", - description: [ - multiline` - The gift of Lilith courses through you; you've never known such power before! - But immortality carries a price, and the Thirst drives you to action. - You've observed your colleagues for some weeks; it's time to feed. - `, - VAMPIRE_MECHANICAL_DESCRIPTION, - ], - category: Category.Roundstart, - priority: -1, -}; - -export default Vampire; diff --git a/yogstation.dme b/yogstation.dme index faec584ad8e7..6a9de9654035 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -871,7 +871,6 @@ #include "code\datums\mutations\fire_breath.dm" #include "code\datums\mutations\heat_adaptation.dm" #include "code\datums\mutations\hulk.dm" -#include "code\datums\mutations\olfaction.dm" #include "code\datums\mutations\radiantburst.dm" #include "code\datums\mutations\radioactive.dm" #include "code\datums\mutations\radproof.dm" @@ -1794,8 +1793,7 @@ #include "code\modules\antagonists\blob\structures\resource.dm" #include "code\modules\antagonists\blob\structures\shield.dm" #include "code\modules\antagonists\blood_contract\blood_contract.dm" -#include "code\modules\antagonists\bloodsuckers\bloodsucker_assets.dm" -#include "code\modules\antagonists\bloodsuckers\bloodsucker_flaws.dm" +#include "code\modules\antagonists\bloodsuckers\bloodsucker_flavour.dm" #include "code\modules\antagonists\bloodsuckers\bloodsucker_frenzy.dm" #include "code\modules\antagonists\bloodsuckers\bloodsucker_hud.dm" #include "code\modules\antagonists\bloodsuckers\bloodsucker_integration.dm" @@ -1805,37 +1803,8 @@ #include "code\modules\antagonists\bloodsuckers\bloodsuckers_objects.dm" #include "code\modules\antagonists\bloodsuckers\subsystem_sunlight.dm" #include "code\modules\antagonists\bloodsuckers\clans\_clan.dm" -#include "code\modules\antagonists\bloodsuckers\clans\clan_flavortext.dm" -#include "code\modules\antagonists\bloodsuckers\clans\clan_gangrel.dm" -#include "code\modules\antagonists\bloodsuckers\clans\clan_hecata.dm" -#include "code\modules\antagonists\bloodsuckers\clans\clan_lasombra.dm" -#include "code\modules\antagonists\bloodsuckers\clans\clan_toreador.dm" -#include "code\modules\antagonists\bloodsuckers\clans\clan_tzimisce.dm" #include "code\modules\antagonists\bloodsuckers\powers\_powers.dm" -#include "code\modules\antagonists\bloodsuckers\powers\cloak.dm" -#include "code\modules\antagonists\bloodsuckers\powers\distress.dm" -#include "code\modules\antagonists\bloodsuckers\powers\feed.dm" -#include "code\modules\antagonists\bloodsuckers\powers\fortitude.dm" -#include "code\modules\antagonists\bloodsuckers\powers\gangrel.dm" -#include "code\modules\antagonists\bloodsuckers\powers\gohome.dm" -#include "code\modules\antagonists\bloodsuckers\powers\masquerade.dm" -#include "code\modules\antagonists\bloodsuckers\powers\olfaction.dm" -#include "code\modules\antagonists\bloodsuckers\powers\recuperate.dm" -#include "code\modules\antagonists\bloodsuckers\powers\vassal_fold.dm" -#include "code\modules\antagonists\bloodsuckers\powers\veil.dm" -#include "code\modules\antagonists\bloodsuckers\powers\targeted\_powers_targeted.dm" -#include "code\modules\antagonists\bloodsuckers\powers\targeted\brawn.dm" -#include "code\modules\antagonists\bloodsuckers\powers\targeted\haste.dm" -#include "code\modules\antagonists\bloodsuckers\powers\targeted\hecata.dm" -#include "code\modules\antagonists\bloodsuckers\powers\targeted\lasombra.dm" -#include "code\modules\antagonists\bloodsuckers\powers\targeted\lunge.dm" -#include "code\modules\antagonists\bloodsuckers\powers\targeted\mesmerize.dm" -#include "code\modules\antagonists\bloodsuckers\powers\targeted\trespass.dm" -#include "code\modules\antagonists\bloodsuckers\powers\targeted\tzimisce.dm" -#include "code\modules\antagonists\bloodsuckers\structures\bloodsucker_coffin.dm" -#include "code\modules\antagonists\bloodsuckers\structures\bloodsucker_crypt.dm" #include "code\modules\antagonists\bloodsuckers\structures\bloodsucker_life.dm" -#include "code\modules\antagonists\bloodsuckers\structures\bloodsucker_recipes.dm" #include "code\modules\antagonists\bloodsuckers\vassal\bloodsucker_conversion.dm" #include "code\modules\antagonists\bloodsuckers\vassal\favorite_vassal.dm" #include "code\modules\antagonists\bloodsuckers\vassal\revenge_vassal.dm" @@ -2004,7 +1973,6 @@ #include "code\modules\antagonists\magic_servant\servant.dm" #include "code\modules\antagonists\malf\malf.dm" #include "code\modules\antagonists\monsterhunter\monsterhunter.dm" -#include "code\modules\antagonists\monsterhunter\monstertrack.dm" #include "code\modules\antagonists\morph\morph.dm" #include "code\modules\antagonists\morph\morph_antag.dm" #include "code\modules\antagonists\nightmare\nightmare.dm" @@ -2460,7 +2428,6 @@ #include "code\modules\events\stray_cargo.dm" #include "code\modules\events\supermatter_surge.dm" #include "code\modules\events\swarmer.dm" -#include "code\modules\events\tzimisce.dm" #include "code\modules\events\wormholes.dm" #include "code\modules\events\zombie_infection.dm" #include "code\modules\events\anomaly\anomaly.dm" @@ -4020,7 +3987,6 @@ #include "yogstation\code\_globalvars\lists\telecomms.dm" #include "yogstation\code\_onclick\adjacent.dm" #include "yogstation\code\_onclick\hud\darkspawn.dm" -#include "yogstation\code\_onclick\hud\vampire.dm" #include "yogstation\code\controllers\configuration\entries\general.dm" #include "yogstation\code\controllers\configuration\entries\yogstation_config.dm" #include "yogstation\code\controllers\subsystem\bluespace_locker.dm" @@ -4263,13 +4229,6 @@ #include "yogstation\code\modules\antagonists\traitor\backstory\traitor_backstory_ui.dm" #include "yogstation\code\modules\antagonists\traitor\backstory\traitor_datum_backstory.dm" #include "yogstation\code\modules\antagonists\traitor\backstory\traitor_factions.dm" -#include "yogstation\code\modules\antagonists\vampire\vampire.dm" -#include "yogstation\code\modules\antagonists\vampire\vampire_hud.dm" -#include "yogstation\code\modules\antagonists\vampire\vampire_objectives.dm" -#include "yogstation\code\modules\antagonists\vampire\vampire_other.dm" -#include "yogstation\code\modules\antagonists\vampire\abilities\vampire_bat.dm" -#include "yogstation\code\modules\antagonists\vampire\abilities\vampire_bite.dm" -#include "yogstation\code\modules\antagonists\vampire\abilities\vampire_powers.dm" #include "yogstation\code\modules\assembly\signaler.dm" #include "yogstation\code\modules\atmospherics\airalarm.dm" #include "yogstation\code\modules\atmospherics\auxgm\breathing_classes.dm" @@ -4465,7 +4424,6 @@ #include "yogstation\code\modules\mob\living\silicon\ai\life.dm" #include "yogstation\code\modules\mob\living\silicon\ai\vox_sounds.dm" #include "yogstation\code\modules\mob\living\silicon\ai\freelook\eye.dm" -#include "yogstation\code\modules\mob\living\silicon\robot\login.dm" #include "yogstation\code\modules\mob\living\silicon\robot\robot.dm" #include "yogstation\code\modules\mob\living\silicon\robot\robot_modules.dm" #include "yogstation\code\modules\mob\living\silicon\robot\robot_movement.dm" @@ -4581,7 +4539,6 @@ #include "yogstation\code\modules\storytellers\converted_events\solo\obsessed.dm" #include "yogstation\code\modules\storytellers\converted_events\solo\revolutionary.dm" #include "yogstation\code\modules\storytellers\converted_events\solo\traitor.dm" -#include "yogstation\code\modules\storytellers\converted_events\solo\vampire.dm" #include "yogstation\code\modules\storytellers\converted_events\solo\wizard.dm" #include "yogstation\code\modules\storytellers\converted_events\solo\ghosts\nightmare.dm" #include "yogstation\code\modules\storytellers\converted_events\solo\ghosts\nuclear_operative_ghost.dm" diff --git a/yogstation/code/_onclick/hud/vampire.dm b/yogstation/code/_onclick/hud/vampire.dm deleted file mode 100644 index 064e517ec833..000000000000 --- a/yogstation/code/_onclick/hud/vampire.dm +++ /dev/null @@ -1,6 +0,0 @@ -/atom/movable/screen/vampire - icon = 'icons/mob/screen_gen.dmi' - icon_state = "power_display" - name = "usable blood" - screen_loc = ui_lingchemdisplay - invisibility = INVISIBILITY_ABSTRACT \ No newline at end of file diff --git a/yogstation/code/datums/mind.dm b/yogstation/code/datums/mind.dm index 93e60886359e..217c3b3a01ea 100644 --- a/yogstation/code/datums/mind.dm +++ b/yogstation/code/datums/mind.dm @@ -2,44 +2,6 @@ var/quiet_round = FALSE //Won't be picked as target in most cases var/accent_name = null // The name of the accent this guy has. NULL implies no accent - -/datum/mind/proc/vampire_hook() - var/text = "vampire" - text = "[text]: " - if(IS_VAMPIRE(current)) - text += "VAMPIRE | human | full-power" - else - text += "vampire | HUMAN | full-power" - if(current && current.client && (ROLE_VAMPIRE in current.client.prefs.be_special)) - text += " | Enabled in Prefs" - else - text += " | Disabled in Prefs" - return text - -/datum/mind/proc/vampire_href(href, mob/M) - switch(href) - if("clear") - remove_vampire(current) - message_admins("[key_name_admin(usr)] has de-vampired [current].") - log_admin("[key_name(usr)] has de-vampired [current].") - if("vampire") - if(!IS_VAMPIRE(current)) - message_admins("[key_name_admin(usr)] has vampired [current].") - log_admin("[key_name(usr)] has vampired [current].") - add_vampire(current) - else - to_chat(usr, span_warning("[current] is already a vampire!")) - if("full") - message_admins("[key_name_admin(usr)] has full-vampired [current].") - log_admin("[key_name(usr)] has full-vampired [current].") - if(!IS_VAMPIRE(current)) - add_vampire(current) - var/datum/antagonist/vampire/V = has_antag_datum(ANTAG_DATUM_VAMPIRE) - if(V) - V.total_blood = 1500 - V.usable_blood = 1500 - V.check_vampire_upgrade() - /datum/mind/proc/handle_speech(datum/source, mob/speech_args) var/static/list/accents_name2regexes // Key is the name of the accent, value is a length-2 list of lists. //The first list contains all the regexes marked as being word or phrase replacements. They are replaced first. diff --git a/yogstation/code/datums/status_effects/neutral.dm b/yogstation/code/datums/status_effects/neutral.dm index 809cb5ab86c2..e4e86046d91c 100644 --- a/yogstation/code/datums/status_effects/neutral.dm +++ b/yogstation/code/datums/status_effects/neutral.dm @@ -13,93 +13,95 @@ . = ..() void_effect.unvanish(owner) -/datum/status_effect/scent_hunter - id = "smelly" - duration = 20 SECONDS - status_type = STATUS_EFFECT_REFRESH - alert_type = null + + +// /datum/status_effect/scent_hunter +// id = "smelly" +// duration = 20 SECONDS +// status_type = STATUS_EFFECT_REFRESH +// alert_type = null - ///the person doing the tracking. but here's the - var/mob/living/sniffer +// ///the person doing the tracking. but here's the +// var/mob/living/sniffer - ///the tracked target - var/mob/living/sniffee +// ///the tracked target +// var/mob/living/sniffee - var/scent_color = COLOR_RED +// var/scent_color = COLOR_RED - var/datum/effect_system/trail_follow/scent/scent_trail +// var/datum/effect_system/trail_follow/scent/scent_trail - var/datum/atom_hud/alternate_appearance/basic/scent_hunter/smell_hud +// var/datum/atom_hud/alternate_appearance/basic/scent_hunter/smell_hud -/datum/status_effect/scent_hunter/on_creation(mob/living/owner, mob/living/target, target_color) - if(target) - sniffee = target - if(target_color) - scent_color = sanitize_hexcolor(target_color, 6, TRUE, COLOR_RED) - return ..() +// /datum/status_effect/scent_hunter/on_creation(mob/living/owner, mob/living/target, target_color) +// if(target) +// sniffee = target +// if(target_color) +// scent_color = sanitize_hexcolor(target_color, 6, TRUE, COLOR_RED) +// return ..() -/datum/status_effect/scent_hunter/on_apply() - . = ..() - sniffer = owner - sniffer.add_client_colour(/datum/client_colour/monochrome) - if(sniffee) - var/icon/temp = icon(sniffee.icon, sniffee.icon_state) - var/image/scent_glow = image(temp, layer = ABOVE_MOB_LAYER, loc = sniffee) - scent_glow.copy_overlays(sniffee) - scent_glow.plane = HUD_PLANE - scent_glow.appearance_flags = NO_CLIENT_COLOR - scent_glow.color = scent_color - scent_glow.name = id - smell_hud = sniffee.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/scent_hunter, id, scent_glow, FALSE) +// /datum/status_effect/scent_hunter/on_apply() +// . = ..() +// sniffer = owner +// sniffer.add_client_colour(/datum/client_colour/monochrome) +// if(sniffee) +// var/icon/temp = icon(sniffee.icon, sniffee.icon_state) +// var/image/scent_glow = image(temp, layer = ABOVE_MOB_LAYER, loc = sniffee) +// scent_glow.copy_overlays(sniffee) +// scent_glow.plane = HUD_PLANE +// scent_glow.appearance_flags = NO_CLIENT_COLOR +// scent_glow.color = scent_color +// scent_glow.name = id +// smell_hud = sniffee.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/scent_hunter, id, scent_glow, FALSE) - scent_trail = new() - scent_trail.set_up(sniffee, owner, scent_color) - scent_trail.start() - -/datum/status_effect/scent_hunter/on_remove() - sniffer.remove_client_colour(/datum/client_colour/monochrome) - sniffer.remove_alt_appearance(id) - if(sniffee) - sniffee.remove_alt_appearance(id) - if(scent_trail) - scent_trail.Destroy() - if(smell_hud) - smell_hud.hide_from(owner) - if(sniffer?.client?.images) - for(var/image/I in sniffer.client.images) - if(I.name==id) - sniffer.client.images -= I - -/datum/status_effect/scent_hunter/blood - id = "blood_smelly" - -/datum/status_effect/bloodthirsty - id = "bloodthirsty" - duration = 1.5 SECONDS - status_type = STATUS_EFFECT_REFRESH - alert_type = /atom/movable/screen/alert/status_effect/bloodthirsty - var/mob/living/thirster - //that sweet blood sings to you and makes your screen reddish tinged - var/atom/movable/screen/fullscreen/brute/red_thirst - -/datum/status_effect/bloodthirsty/on_apply() - . = ..() - thirster = owner - owner.add_movespeed_modifier(src, update=TRUE, priority=100, multiplicative_slowdown=-0.8, blacklisted_movetypes=(FLOATING)) - if(!red_thirst) - red_thirst = owner.overlay_fullscreen("thirsting", /atom/movable/screen/fullscreen/brute, 4) - red_thirst.alpha = 0 - red_thirst.plane = HUD_PLANE - animate(red_thirst, alpha = 255, time = 1 SECONDS, easing = EASE_IN) //fade IN - to_chat(owner, span_userdanger("As the scent of your prey overwhelms your sense of smell, the thrill of the hunt empowers you!")) - -/datum/status_effect/bloodthirsty/on_remove() - owner.remove_movespeed_modifier(src) - owner.clear_fullscreen("thirsting") - -/atom/movable/screen/alert/status_effect/bloodthirsty - icon = 'yogstation/icons/mob/screen_alert.dmi' - icon_state = "default" - name = "Bloodthirsty" - desc = "You smell blood in the air." - icon_state = "bloodthirsty" +// scent_trail = new() +// scent_trail.set_up(sniffee, owner, scent_color) +// scent_trail.start() + +// /datum/status_effect/scent_hunter/on_remove() +// sniffer.remove_client_colour(/datum/client_colour/monochrome) +// sniffer.remove_alt_appearance(id) +// if(sniffee) +// sniffee.remove_alt_appearance(id) +// if(scent_trail) +// scent_trail.Destroy() +// if(smell_hud) +// smell_hud.hide_from(owner) +// if(sniffer?.client?.images) +// for(var/image/I in sniffer.client.images) +// if(I.name==id) +// sniffer.client.images -= I + +// /datum/status_effect/scent_hunter/blood +// id = "blood_smelly" + +// /datum/status_effect/bloodthirsty +// id = "bloodthirsty" +// duration = 1.5 SECONDS +// status_type = STATUS_EFFECT_REFRESH +// alert_type = /atom/movable/screen/alert/status_effect/bloodthirsty +// var/mob/living/thirster +// //that sweet blood sings to you and makes your screen reddish tinged +// var/atom/movable/screen/fullscreen/brute/red_thirst + +// /datum/status_effect/bloodthirsty/on_apply() +// . = ..() +// thirster = owner +// owner.add_movespeed_modifier(src, update=TRUE, priority=100, multiplicative_slowdown=-0.8, blacklisted_movetypes=(FLOATING)) +// if(!red_thirst) +// red_thirst = owner.overlay_fullscreen("thirsting", /atom/movable/screen/fullscreen/brute, 4) +// red_thirst.alpha = 0 +// red_thirst.plane = HUD_PLANE +// animate(red_thirst, alpha = 255, time = 1 SECONDS, easing = EASE_IN) //fade IN +// to_chat(owner, span_userdanger("As the scent of your prey overwhelms your sense of smell, the thrill of the hunt empowers you!")) + +// /datum/status_effect/bloodthirsty/on_remove() +// owner.remove_movespeed_modifier(src) +// owner.clear_fullscreen("thirsting") + +// /atom/movable/screen/alert/status_effect/bloodthirsty +// icon = 'yogstation/icons/mob/screen_alert.dmi' +// icon_state = "default" +// name = "Bloodthirsty" +// desc = "You smell blood in the air." +// icon_state = "bloodthirsty" diff --git a/yogstation/code/game/gamemodes/clean_this_shit_up/other.dm b/yogstation/code/game/gamemodes/clean_this_shit_up/other.dm index 795e4b8d2161..39829c0bcf67 100644 --- a/yogstation/code/game/gamemodes/clean_this_shit_up/other.dm +++ b/yogstation/code/game/gamemodes/clean_this_shit_up/other.dm @@ -23,9 +23,6 @@ var/list/target_list = list() var/list/late_joining_list = list() - //vampire - var/list/datum/mind/vampires = list() - //devils var/list/datum/mind/devils = list() var/devil_ascended = 0 // Number of arch devils on station diff --git a/yogstation/code/modules/antagonists/darkspawn/darkspawn_abilities/fighter_abilities.dm b/yogstation/code/modules/antagonists/darkspawn/darkspawn_abilities/fighter_abilities.dm index a8a710b44360..e64b85ef6b06 100644 --- a/yogstation/code/modules/antagonists/darkspawn/darkspawn_abilities/fighter_abilities.dm +++ b/yogstation/code/modules/antagonists/darkspawn/darkspawn_abilities/fighter_abilities.dm @@ -318,15 +318,7 @@ antimagic_flags = NONE check_flags = AB_CHECK_CONSCIOUS spell_requirements = SPELL_REQUIRES_HUMAN - resource_costs = list(ANTAG_RESOURCE_DARKSPAWN = 1) - -/datum/action/cooldown/spell/toggle/creep/process() - if(active) - if(!SEND_SIGNAL(owner.mind, COMSIG_MIND_CHECK_ANTAG_RESOURCE, ANTAG_RESOURCE_DARKSPAWN, resource_costs[ANTAG_RESOURCE_DARKSPAWN])) - Activate(owner) - else - SEND_SIGNAL(owner.mind, COMSIG_MIND_SPEND_ANTAG_RESOURCE, resource_costs) - return ..() + maintain_costs = list(ANTAG_RESOURCE_DARKSPAWN = 1) /datum/action/cooldown/spell/toggle/creep/Enable() owner.balloon_alert(owner, "Odeahz") @@ -360,21 +352,16 @@ check_flags = AB_CHECK_CONSCIOUS spell_requirements = NONE cooldown_time = 1 SECONDS - resource_costs = list(ANTAG_RESOURCE_DARKSPAWN = 1) + maintain_costs = list(ANTAG_RESOURCE_DARKSPAWN = 1) ///Boolean, if the user was running before activating this spell var/was_running ///List of traits applied during the effect var/list/traits = list(TRAIT_STUNIMMUNE, TRAIT_PUSHIMMUNE, TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_NODEATH, TRAIT_IGNOREDAMAGESLOWDOWN) /datum/action/cooldown/spell/toggle/indomitable/process() - if(active) - if(!SEND_SIGNAL(owner.mind, COMSIG_MIND_CHECK_ANTAG_RESOURCE, ANTAG_RESOURCE_DARKSPAWN, resource_costs[ANTAG_RESOURCE_DARKSPAWN])) - Activate(owner) - else - SEND_SIGNAL(owner.mind, COMSIG_MIND_SPEND_ANTAG_RESOURCE, resource_costs) + . = ..() if(active && owner.m_intent != MOVE_INTENT_WALK) owner.toggle_move_intent() - return ..() /datum/action/cooldown/spell/toggle/indomitable/Enable() owner.balloon_alert(owner, "Zhaedo") diff --git a/yogstation/code/modules/antagonists/darkspawn/darkspawn_abilities/thrall_spells.dm b/yogstation/code/modules/antagonists/darkspawn/darkspawn_abilities/thrall_spells.dm index 84203a019fbb..860a55de120f 100644 --- a/yogstation/code/modules/antagonists/darkspawn/darkspawn_abilities/thrall_spells.dm +++ b/yogstation/code/modules/antagonists/darkspawn/darkspawn_abilities/thrall_spells.dm @@ -414,11 +414,8 @@ target.resting = FALSE target.apply_status_effect(STATUS_EFFECT_SPEEDBOOST, -0.5, 15 SECONDS, type) target.visible_message(span_danger("Streaks of velvet light crack out of [target]'s skin."), span_velvet("Power roars through you like a raging storm, pushing you to your absolute limits.")) - var/obj/item/cuffs = target.get_item_by_slot(ITEM_SLOT_HANDCUFFED) - var/obj/item/legcuffs = target.get_item_by_slot(ITEM_SLOT_LEGCUFFED) - if(target.handcuffed || target.legcuffed) - target.clear_cuffs(cuffs, TRUE, TRUE) - target.clear_cuffs(legcuffs, TRUE, TRUE) + if(target.uncuff()) + target.uncuff() playsound(get_turf(target),'yogstation/sound/creatures/darkspawn_death.ogg', 80, 1) var/datum/antagonist/darkspawn/darkspawn = isdarkspawn(owner) if(darkspawn) diff --git a/yogstation/code/modules/antagonists/darkspawn/darkspawn_thrall.dm b/yogstation/code/modules/antagonists/darkspawn/darkspawn_thrall.dm index b7fbecd6c2ce..0901ace5373d 100644 --- a/yogstation/code/modules/antagonists/darkspawn/darkspawn_thrall.dm +++ b/yogstation/code/modules/antagonists/darkspawn/darkspawn_thrall.dm @@ -178,7 +178,7 @@ to_chat(owner, span_notice("Use .[MODE_KEY_DARKSPAWN] before your messages to speak over the Mindlink.")) to_chat(owner, span_notice("Blending in with regular crewmembers will generate willpower for your masters.")) to_chat(owner, span_notice("Ask for help from your masters or fellows if you're new to this role.")) - SEND_SOUND(owner.current, sound ('yogstation/sound/ambience/antag/become_veil.ogg', volume = 50)) + SEND_SOUND(owner.current, sound ('yogstation/sound/ambience/antag/thrall.ogg', volume = 50)) flash_color(owner, flash_color = COLOR_VELVET, flash_time = 10 SECONDS) /datum/antagonist/thrall/roundend_report() diff --git a/yogstation/code/modules/antagonists/vampire/abilities/vampire_bat.dm b/yogstation/code/modules/antagonists/vampire/abilities/vampire_bat.dm deleted file mode 100644 index 6fd7f25128a6..000000000000 --- a/yogstation/code/modules/antagonists/vampire/abilities/vampire_bat.dm +++ /dev/null @@ -1,54 +0,0 @@ -/mob/living/simple_animal/hostile/vampire_bat - name = "vampire bat" - desc = "A bat that sucks blood. Keep away from medical bays." - icon_state = "bat" - icon_living = "bat" - icon_dead = "bat_dead" - icon_gib = "bat_dead" - turns_per_move = 1 - response_help = "brushes aside" - response_disarm = "flails at" - response_harm = "hits" - speak_chance = 0 - maxHealth = 20 - health = 20 - speed = 0 - harm_intent_damage = 7 - melee_damage_lower = 5 - melee_damage_upper = 7 - attacktext = "bites" - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 1) - pass_flags = PASSTABLE | PASSCOMPUTER - faction = list("hostile", "vampire") - attack_sound = 'sound/weapons/bite.ogg' - obj_damage = 0 - environment_smash = ENVIRONMENT_SMASH_NONE - ventcrawler = VENTCRAWLER_ALWAYS - mob_size = MOB_SIZE_TINY - movement_type = FLYING - speak_emote = list("squeaks") - - var/mob/living/controller - - - //Space bats need no air to fly in. - 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 - -/mob/living/simple_animal/hostile/vampire_bat/CanAttack(atom/the_target) - . = ..() - if(isliving(the_target)) - var/mob/living/check = the_target - if(IS_VAMPIRE(check)) - return FALSE - -/mob/living/simple_animal/hostile/vampire_bat/death() - if(isliving(controller)) - controller.forceMove(loc) - mind.transfer_to(controller) - controller.status_flags &= ~GODMODE - controller.Knockdown(120) - controller.adjustBruteLoss(20) - to_chat(controller, span_userdanger("The force of being exiled from your bat form painfully throws you to the ground!")) - qdel() - . = ..() diff --git a/yogstation/code/modules/antagonists/vampire/abilities/vampire_bite.dm b/yogstation/code/modules/antagonists/vampire/abilities/vampire_bite.dm deleted file mode 100644 index 21781c2fcda5..000000000000 --- a/yogstation/code/modules/antagonists/vampire/abilities/vampire_bite.dm +++ /dev/null @@ -1,20 +0,0 @@ -/mob/living/carbon/human/proc/handle_vamp_biting(mob/living/carbon/human/M) - if(!IS_VAMPIRE(M) || M == src || M.zone_selected != "head") - return FALSE - var/datum/antagonist/vampire/V = M.mind.has_antag_datum(/datum/antagonist/vampire) - if((NOBLOOD in dna.species.species_traits) || dna.species.exotic_blood || !blood_volume) - to_chat(M, span_warning("They have no blood!")) - return FALSE - if(IS_VAMPIRE(src)) - to_chat(M, span_warning("Your fangs fail to pierce [name]'s cold flesh")) - return FALSE - if(dna.species.name == "skeleton") - to_chat(M, span_warning("There is no blood in a skeleton!")) - return FALSE - if(!ckey) - to_chat(M, span_warning("[src]'s blood is stale and useless.")) - return FALSE - if(V.draining) - return FALSE - V.handle_bloodsucking(src) - return TRUE diff --git a/yogstation/code/modules/antagonists/vampire/abilities/vampire_powers.dm b/yogstation/code/modules/antagonists/vampire/abilities/vampire_powers.dm deleted file mode 100644 index 6e7772719c7d..000000000000 --- a/yogstation/code/modules/antagonists/vampire/abilities/vampire_powers.dm +++ /dev/null @@ -1,517 +0,0 @@ -/datum/action/cooldown/spell - var/gain_desc - var/vamp_req = FALSE - -/datum/action/cooldown/spell/can_cast_spell(feedback = TRUE) - if(vamp_req) - if(!IS_VAMPIRE(owner)) - return FALSE - return ..() - -/datum/action/cooldown/spell/New() - . = ..() - if(vamp_req) - spell_requirements = NONE - -/datum/action/cooldown/spell/before_cast(atom/cast_on) - if(vamp_req) - // sanity check before we cast - if(!IS_VAMPIRE(owner)) - return - return ..() - -/datum/action/cooldown/spell/is_valid_target(mob/living/target) - if(vamp_req && !IS_VAMPIRE(target)) - return FALSE - return ..() - -/datum/vampire_passive - var/gain_desc - -/datum/vampire_passive/New() - . = ..() - if(!gain_desc) - gain_desc = span_notice("You have gained \the [src] ability.") - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/vampire_passive/nostealth - gain_desc = span_warning("You are no longer able to conceal yourself while sucking blood.") //gets a warning span because it's a downgrade - -/datum/vampire_passive/regen - gain_desc = span_notice("Your innate regenerative abilities have been improved, granting passive healing. Rejuvenate now also helps to reduce disabling effects.") - -/datum/vampire_passive/vision - gain_desc = span_notice("Your vampiric vision has improved.") - -/datum/vampire_passive/full - gain_desc = span_notice("You have reached your full potential and are no longer weak to the effects of anything holy and your vision has been improved greatly.") - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/datum/action/cooldown/spell/rejuvenate - name = "Rejuvenate (20)" - desc= "Flush your system with some spare blood to restore stamina over time." - button_icon_state = "rejuv" - button_icon = 'yogstation/icons/mob/vampire.dmi' - background_icon_state = "bg_vampire" - overlay_icon_state = "bg_vampire_border" - - school = SCHOOL_RESTORATION - - check_flags = NONE - cooldown_time = 20 SECONDS - resource_costs = list(ANTAG_RESOURCE_VAMPIRE = 20) - vamp_req = TRUE - -/datum/action/cooldown/spell/rejuvenate/cast(mob/living/user) - . = ..() - if(!iscarbon(user)) - return FALSE - var/mob/living/carbon/U = user - heal(U) - -/datum/action/cooldown/spell/rejuvenate/proc/heal(mob/living/carbon/user, iterations = 1) - if(iterations > 5)//1 2 3 4 5 return, 5 total instances of stam heal each split by 1 second - return - user.remove_status_effect(/datum/status_effect/speech/stutter) - - var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) - if(!V) //sanity check - return - user.adjustStaminaLoss(-50) - if(V.get_ability(/datum/vampire_passive/regen)) - user.AdjustAllImmobility(-1 SECONDS) - addtimer(CALLBACK(src, PROC_REF(heal), user, iterations + 1), 1 SECONDS) - -/datum/action/cooldown/spell/pointed/gaze - name = "Vampiric Gaze" - desc = "Paralyze your target with fear." - ranged_mousepointer = 'icons/effects/mouse_pointers/gaze_target.dmi' - button_icon = 'yogstation/icons/mob/vampire.dmi' - background_icon_state = "bg_vampire" - overlay_icon_state = "bg_vampire_border" - button_icon_state = "gaze" - - school = SCHOOL_SANGUINE - - cooldown_time = 30 SECONDS - active_msg = "You prepare your vampiric gaze." - deactive_msg = "You stop preparing your vampiric gaze." - vamp_req = TRUE - -/datum/action/cooldown/spell/pointed/gaze/is_valid_target(atom/target) - . = ..() - if(!.) - return FALSE - if(!target) - return FALSE - if(!ishuman(target)) - to_chat(owner, span_warning("Gaze will not work on this being.")) - return FALSE - var/mob/living/carbon/human/T = target - - if(T.stat == DEAD) - to_chat(owner,"You cannot gaze at corpses... \ - or maybe you could if you really wanted to.") - return FALSE - - return TRUE - -/datum/action/cooldown/spell/pointed/gaze/InterceptClickOn(mob/living/user, params, atom/target_atom) - . = ..() - if(!.) - return FALSE - if(!ishuman(target_atom)) - return FALSE - - var/mob/living/carbon/human/T = target_atom - user.visible_message(span_warning("[user]'s eyes flash red."),\ - span_warning("your eyes flash red.")) - - var/protection = T.get_eye_protection() - switch(protection) - - if(0) - to_chat(target, span_userdanger("You are paralyzed with fear!")) - to_chat(user, span_notice("You paralyze [T].")) - T.Stun(5 SECONDS) - if(1 to INFINITY) - T.adjust_confusion(5 SECONDS) - return TRUE - if(INFINITY) - to_chat(user, span_vampirewarning("[T] is blind and is unaffected by your gaze!")) - return FALSE - return TRUE - - -/datum/action/cooldown/spell/pointed/hypno - name = "Hypnotize (20)" - desc = "Knock out your target." - button_icon_state = "hypnotize" - ranged_mousepointer = 'icons/effects/mouse_pointers/hypnotize_target.dmi' - button_icon = 'yogstation/icons/mob/vampire.dmi' - background_icon_state = "bg_vampire" - overlay_icon_state = "bg_vampire_border" - - school = SCHOOL_SANGUINE - - cooldown_time = 30 SECONDS - resource_costs = list(ANTAG_RESOURCE_VAMPIRE = 20) - active_msg = span_warning("You prepare your hypnosis technique.") - deactive_msg = span_warning("You stop preparing your hypnosis.") - vamp_req = TRUE - -/datum/action/cooldown/spell/pointed/hypno/is_valid_target(atom/target) - if(!..()) - return FALSE - if(!target) - return FALSE - if(!ishuman(target)) - to_chat(owner, span_warning("Hypnotize will not work on this being.")) - return FALSE - - var/mob/living/carbon/human/T = target - if(T.IsSleeping()) - to_chat(owner, span_warning("[T] is already asleep!.")) - return FALSE - return TRUE - -/datum/action/cooldown/spell/pointed/hypno/InterceptClickOn(mob/living/user, params, atom/target_atom) - . = ..() - if(!.) - return FALSE - if(!ishuman(target_atom)) - return FALSE - - var/mob/living/carbon/human/T = target_atom - user.visible_message(span_warning("[user] twirls their finger in a circular motion."),\ - span_warning("You twirl your finger in a circular motion.")) - - - var/protection = T.get_eye_protection() - var/sleep_duration = 30 SECONDS - switch(protection) - if(1 to INFINITY) - to_chat(user, span_vampirewarning("Your hypnotic powers are dampened by [T]'s eye protection.")) - sleep_duration = 10 SECONDS - if(INFINITY) - to_chat(user, span_vampirewarning("[T] is blind and is unaffected by hypnosis!")) - return FALSE - - to_chat(T, span_boldwarning("Your knees suddenly feel heavy. Your body begins to sink to the floor.")) - to_chat(user, span_notice("[T] is now under your spell. In four seconds they will be rendered unconscious as long as they are within close range.")) - if(do_after(user, 4 SECONDS, T)) // 4 seconds... - if(get_dist(user, T) <= 3) - flash_color(T, flash_color="#472040", flash_time=3 SECONDS) // it's the vampires color! - T.SetSleeping(sleep_duration) - to_chat(user, span_warning("[T] has fallen asleep!")) - else - to_chat(T, span_notice("You feel a whole lot better now.")) - - return TRUE - -/datum/action/cooldown/spell/cloak - name = "Cloak of Darkness" - desc = "Toggles whether you are currently cloaking yourself in darkness." - gain_desc = "You have gained the Cloak of Darkness ability which when toggled makes you near invisible in the shroud of darkness." - button_icon_state = "cloak" - button_icon = 'yogstation/icons/mob/vampire.dmi' - background_icon_state = "bg_vampire" - overlay_icon_state = "bg_vampire_border" - - school = SCHOOL_CONJURATION - - cooldown_time = 1 SECONDS - vamp_req = TRUE - -/datum/action/cooldown/spell/cloak/New() - . = ..() - update_name() - -/datum/action/cooldown/spell/cloak/proc/update_name() - var/mob/living/user = owner - if(!ishuman(user) || !IS_VAMPIRE(user)) - return - var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) - name = "[initial(name)] ([V.iscloaking ? "Deactivate" : "Activate"])" - -/datum/action/cooldown/spell/cloak/cast(mob/living/user) - . = ..() - var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) - if(!V) - return - V.iscloaking = !V.iscloaking - update_name() - to_chat(user, span_notice("You will now be [V.iscloaking ? "hidden" : "seen"] in darkness.")) - -/datum/action/cooldown/spell/revive - name = "Revive" - gain_desc = "You have gained the ability to revive after death... However you can still be cremated/gibbed, and you will disintegrate if you're in the chapel and not yet strong enough!" - desc = "Revives you, provided you are not in the chapel!" - button_icon = 'yogstation/icons/mob/vampire.dmi' - button_icon_state = "coffin" - background_icon_state = "bg_vampire" - overlay_icon_state = "bg_vampire_border" - - school = SCHOOL_SANGUINE - - check_flags = NONE - cooldown_time = 0 //no cooldown to the ability - var/actual_cooldown = 1 MINUTES //cooldown applied when revived - vamp_req = TRUE - var/reviving = FALSE - var/revive_timer - -/datum/action/cooldown/spell/revive/cast(mob/living/user) - . = ..() - if(!IS_VAMPIRE(user) || !isliving(user)) - return - if(user.stat != DEAD) - to_chat(user, span_notice("We aren't dead enough to do that yet!")) - return - if(user.reagents.has_reagent("holywater")) - to_chat(user, span_danger("We cannot revive, holy water is in our system!")) - return - var/mob/living/L = user - reviving = !reviving - if(reviving) - to_chat(L, span_notice("We begin to reanimate... this will take 1 minute.")) - deltimer(revive_timer) - revive_timer = addtimer(CALLBACK(src, PROC_REF(revive), L), 1 MINUTES, TIMER_UNIQUE | TIMER_STOPPABLE) - else - to_chat(L, span_notice("We stop our reanimation.")) - deltimer(revive_timer) - -/datum/action/cooldown/spell/revive/proc/revive(mob/living/user) - if(istype(get_area(user.loc), /area/chapel)) - var/datum/antagonist/vampire/V = IS_VAMPIRE(user) - if(V && V.get_ability(/datum/vampire_passive/full)) //full blooded vampire doesn't get dusted if they try to res, it still doesn't work though - to_chat(user, span_danger("The holy energies of this place prevent our revival!")) - return - else //yes yes, it's an else after a return, it just feels wrong without it though - user.visible_message(span_warning("[user] disintegrates into dust!"), span_userdanger("Holy energy seeps into our very being, disintegrating us instantly!"), "You hear sizzling.") - new /obj/effect/decal/remains/human(user.loc) - user.dust() - return - if(user.stat != DEAD) //if they somehow revive before it goes off - return - StartCooldownSelf(actual_cooldown)//start the cooldown when the revive actually happens - var/list/missing = user.get_missing_limbs() - if(missing.len) - playsound(user, 'sound/magic/demon_consume.ogg', 50, 1) - user.visible_message(span_warning("Shadowy matter takes the place of [user]'s missing limbs as they reform!")) - user.regenerate_limbs() - user.regenerate_organs() - user.revive(full_heal = TRUE) - user.visible_message(span_warning("[user] reanimates from death!"), span_notice("We get back up.")) - - -/datum/action/cooldown/spell/aoe/screech - name = "Chiropteran Screech (20)" - desc = "An extremely loud shriek that stuns nearby humans and breaks windows as well." - gain_desc = "You have gained the Chiropteran Screech ability which stuns anything with ears in a large radius and shatters glass in the process." - button_icon_state = "reeee" - button_icon = 'yogstation/icons/mob/vampire.dmi' - background_icon_state = "bg_vampire" - overlay_icon_state = "bg_vampire_border" - - school = SCHOOL_SANGUINE - - aoe_radius = 4 - resource_costs = list(ANTAG_RESOURCE_VAMPIRE = 20) - vamp_req = TRUE - -/datum/action/cooldown/spell/aoe/screech/get_things_to_cast_on(atom/center) - var/list/things = list() - for(var/atom/nearby_thing in hearers(aoe_radius, center)) - if(nearby_thing == owner || nearby_thing == center) - continue - - if(!isliving(nearby_thing)) - continue - - things += nearby_thing - - return things - -/datum/action/cooldown/spell/aoe/screech/cast(atom/cast_on) - . = ..() - owner.visible_message(span_warning("[owner] lets out an ear piercing shriek!"), span_warning("You let out a loud shriek."), span_warning("You hear a loud painful shriek!")) - playsound(owner.loc, 'sound/effects/screech.ogg', 100, TRUE) - for(var/obj/structure/window/W in view(aoe_radius, owner)) - W.take_damage(75) - -/datum/action/cooldown/spell/aoe/screech/cast_on_thing_in_aoe(mob/living/target, mob/living/carbon/user) - if(!target == user || !IS_VAMPIRE(target)) - if(ishuman(target) && target.soundbang_act(1, 0)) - var/mob/living/carbon/human/human_target = target - to_chat(target, span_warning("You hear a ear piercing shriek and your senses dull!")) - human_target.Knockdown(40) - human_target.adjustEarDamage(0, 30) - human_target.adjust_stutter(30 SECONDS) - human_target.Paralyze(40) - human_target.adjust_jitter(2.5 MINUTES) - -/datum/action/cooldown/spell/bats - name = "Summon Bats (30)" - desc = "You summon a pair of space bats who attack nearby targets until they or their target is dead." - gain_desc = "You have gained the Summon Bats ability." - button_icon_state = "bats" - button_icon = 'yogstation/icons/mob/vampire.dmi' - background_icon_state = "bg_vampire" - overlay_icon_state = "bg_vampire_border" - - school = SCHOOL_CONJURATION - - cooldown_time = 2 MINUTES - vamp_req = TRUE - resource_costs = list(ANTAG_RESOURCE_VAMPIRE = 30) - var/num_bats = 2 - -/datum/action/cooldown/spell/bats/cast(mob/living/user) - . = ..() - var/list/turf/locs = new - for(var/direction in GLOB.alldirs) //looking for bat spawns - if(locs.len == num_bats) //we found 2 locations and thats all we need - break - var/turf/T = get_step(usr, direction) //getting a loc in that direction - if(AStar(user, T, /turf/proc/Distance, 1, simulated_only = 0)) // if a path exists, so no dense objects in the way its valid salid - locs += T - - // pad with player location - for(var/i = locs.len + 1 to num_bats) - locs += user.loc - - for(var/T in locs) - new /mob/living/simple_animal/hostile/vampire_bat(T) - -/datum/action/cooldown/spell/jaunt/ethereal_jaunt/mistform - name = "Mist Form (30)" - gain_desc = "You have gained the Mist Form ability which allows you to take on the form of mist for a short period and pass over any obstacle in your path." - background_icon_state = "bg_vampire" - overlay_icon_state = "bg_vampire_border" - - resource_costs = list(ANTAG_RESOURCE_VAMPIRE = 30) - vamp_req = TRUE - -/datum/action/cooldown/spell/pointed/vampirize - name = "Lilith's Pact (300)" - desc = "You drain a victim's blood, and fill them with new blood, blessed by Lilith, turning them into a new vampire." - gain_desc = "You have gained the ability to force someone, given time, to become a vampire." - button_icon = 'yogstation/icons/mob/vampire.dmi' - background_icon_state = "bg_vampire" - overlay_icon_state = "bg_vampire_border" - button_icon_state = "oath" - ranged_mousepointer = 'icons/effects/mouse_pointers/bite_target.dmi' //big win - - school = SCHOOL_SANGUINE - - resource_costs = list(ANTAG_RESOURCE_VAMPIRE = 300) - vamp_req = TRUE - -/datum/action/cooldown/spell/pointed/vampirize/InterceptClickOn(mob/living/user, params, atom/target_atom) - . = ..() - if(!.) - return FALSE - if(!iscarbon(target_atom)) - return - var/mob/living/carbon/target = target_atom - var/datum/antagonist/vampire/vamp = user.mind.has_antag_datum(/datum/antagonist/vampire) - if(IS_VAMPIRE(target)) - to_chat(user, span_warning("They're already a vampire!")) - SEND_SIGNAL(user.mind, COMSIG_MIND_SPEND_ANTAG_RESOURCE, list(ANTAG_RESOURCE_VAMPIRE = -300)) // Refund cost - return FALSE - if(HAS_TRAIT(target, TRAIT_MINDSHIELD)) - to_chat(user, span_warning("[target]'s mind is too strong!")) - SEND_SIGNAL(user.mind, COMSIG_MIND_SPEND_ANTAG_RESOURCE, list(ANTAG_RESOURCE_VAMPIRE = -300)) // Refund cost - return FALSE - user.visible_message(span_warning("[user] latches onto [target]'s neck, pure dread eminating from them."), span_warning("You latch onto [target]'s neck, preparing to transfer your unholy blood to them."), span_warning("A dreadful feeling overcomes you")) - target.reagents.add_reagent(/datum/reagent/medicine/salbutamol, 10) //incase you're choking the victim - for(var/progress = 0, progress <= 3, progress++) - switch(progress) - if(1) - to_chat(target, span_danger("Wicked shadows invade your sight, beckoning to you.")) - to_chat(user, span_notice("We begin to drain [target]'s blood in, so Lilith can bless it.")) - if(2) - to_chat(target, span_danger("Demonic whispers fill your mind, and they become irressistible...")) - if(3) - to_chat(target, span_danger("The world blanks out, and you see a demo- no ange- demon- lil- glory- blessing... Lilith.")) - to_chat(user, span_notice("Excitement builds up in you as [target] sees the blessing of Lilith.")) - if(!do_after(user, 7 SECONDS, target)) - to_chat(user, span_danger("The pact has failed! [target] has not became a vampire.")) - to_chat(target, span_notice("The visions stop, and you relax.")) - SEND_SIGNAL(user.mind, COMSIG_MIND_SPEND_ANTAG_RESOURCE, list(ANTAG_RESOURCE_VAMPIRE = -300)) // Refund cost - return FALSE - if(!QDELETED(user) && !QDELETED(target)) - to_chat(user, span_notice(". . .")) - to_chat(target, span_italics("Come to me, child.")) - sleep(1 SECONDS) - to_chat(target, span_italics("The world hasn't treated you well, has it?")) - sleep(1.5 SECONDS) - to_chat(target, span_italics("Strike fear into their hearts...")) - to_chat(user, "They have signed the pact!") - to_chat(target, span_userdanger("You sign Lilith's Pact.")) - target.mind.store_memory("[user] showed you the glory of Lilith. You are not required to obey [user], however, you have gained a respect for them.") - target.Sleeping(600) - target.blood_volume = 560 - add_vampire(target, FALSE) - vamp.converted ++ - - return TRUE - -/datum/action/cooldown/spell/summon_coat - name = "Summon Dracula Coat (100)" - desc = "Allows you to summon a Vampire Coat providing passive usable blood restoration." - gain_desc = "Now that you have reached full power, you can now pull a vampiric coat out of thin air!" - button_icon = 'yogstation/icons/mob/vampire.dmi' - button_icon_state = "coat" - background_icon_state = "bg_vampire" - overlay_icon_state = "bg_vampire_border" - - school = SCHOOL_CONJURATION - - resource_costs = list(ANTAG_RESOURCE_VAMPIRE = 100) - vamp_req = TRUE - -/datum/action/cooldown/spell/summon_coat/cast(mob/living/user) - . = ..() - if(!IS_VAMPIRE(user) || !isliving(user)) - return - var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) - if(!V) - return - if(QDELETED(V.coat) || !V.coat) - V.coat = new /obj/item/clothing/suit/draculacoat(user.loc) - else if(get_dist(V.coat, user) > 1 || !(V.coat in user.get_all_contents())) - V.coat.forceMove(user.loc) - user.put_in_hands(V.coat) - to_chat(user, span_notice("You summon your dracula coat.")) - -/datum/action/cooldown/spell/shapeshift/vampire - name = "Bat Form (15)" - gain_desc = "You now have the Bat Form ability, which allows you to turn into a bat (and back!)" - desc = "Transform into a bat!" - button_icon_state = "bat" - button_icon = 'yogstation/icons/mob/vampire.dmi' - background_icon_state = "bg_vampire" - overlay_icon_state = "bg_vampire_border" - - school = SCHOOL_TRANSMUTATION - - cooldown_time = 20 SECONDS - die_with_shapeshifted_form = FALSE - convert_damage_type = STAMINA - resource_costs = list(ANTAG_RESOURCE_VAMPIRE = 15) - vamp_req = TRUE - check_flags = AB_CHECK_CONSCIOUS | AB_CHECK_INCAPACITATED - possible_shapes = list(/mob/living/simple_animal/hostile/vampire_bat) - -/datum/action/cooldown/spell/shapeshift/vampire/can_cast_spell() - if(ishuman(owner)) - bypass_cost = FALSE - else - bypass_cost = TRUE - return ..() diff --git a/yogstation/code/modules/antagonists/vampire/vampire.dm b/yogstation/code/modules/antagonists/vampire/vampire.dm deleted file mode 100644 index daaf5eaacb9f..000000000000 --- a/yogstation/code/modules/antagonists/vampire/vampire.dm +++ /dev/null @@ -1,501 +0,0 @@ -#define ALL_POWERS_UNLOCKED 800 -#define BLOOD_SUCK_BASE 25 - -/datum/antagonist/vampire - - name = "Vampire" - antagpanel_category = "Vampire" - roundend_category = "vampires" - job_rank = ROLE_VAMPIRE - antag_hud_name = "vampire" - - ui_name = "AntagInfoVampire" - count_towards_antag_cap = TRUE - - var/usable_blood = 0 - var/total_blood = 0 - var/converted = 0 - var/fullpower = FALSE - var/full_vampire = TRUE - var/draining - var/list/objectives_given = list() - - var/iscloaking = FALSE - - var/list/powers = list() // list of current powers - - var/obj/item/clothing/suit/draculacoat/coat - - var/list/upgrade_tiers = list( - /datum/action/cooldown/spell/rejuvenate = 0, - /datum/action/cooldown/spell/pointed/gaze = 0, - /datum/action/cooldown/spell/pointed/hypno = 0, - /datum/vampire_passive/vision = 75, - /datum/action/cooldown/spell/cloak = 100, - /datum/action/cooldown/spell/revive = 100, - /datum/vampire_passive/nostealth = 150, //only lose the ability to stealth once you get a proper way to escape - /datum/action/cooldown/spell/shapeshift/vampire = 150, - /datum/action/cooldown/spell/aoe/screech = 200, - /datum/action/cooldown/spell/bats = 250, - /datum/vampire_passive/regen = 250, - /datum/action/cooldown/spell/jaunt/ethereal_jaunt/mistform = 300, - /datum/action/cooldown/spell/summon_coat = 400, - /datum/vampire_passive/full = 400, - /datum/action/cooldown/spell/pointed/vampirize = 450) - -/datum/antagonist/vampire/ui_static_data(mob/user) - var/list/data = list() - data["antag_name"] = name - data["objectives"] = get_objectives() - data["loud"] = get_ability(/datum/vampire_passive/nostealth) - return data - -/datum/antagonist/vampire/new_blood - full_vampire = FALSE - roundend_category = "new bloods" - show_in_antagpanel = FALSE - -/datum/antagonist/vampire/get_admin_commands() - . = ..() - .["Full Power"] = CALLBACK(src, PROC_REF(admin_set_full_power)) - .["Set Blood Amount"] = CALLBACK(src, PROC_REF(admin_set_blood)) - -/datum/antagonist/vampire/proc/admin_set_full_power(mob/admin) - usable_blood = ALL_POWERS_UNLOCKED - total_blood = ALL_POWERS_UNLOCKED - check_vampire_upgrade() - message_admins("[key_name_admin(admin)] made [owner.current] a full-power vampire.") - log_admin("[key_name(admin)] made [owner.current] a full-power vampire.") - -/datum/antagonist/vampire/proc/admin_set_blood(mob/admin) - total_blood = input(admin, "Set Vampire Total Blood", "Total Blood", total_blood) as null|num - usable_blood = min((input(admin, "Set Vampire Usable Blood", "Usable Blood", usable_blood) as null|num), total_blood) - check_vampire_upgrade() - message_admins("[key_name_admin(admin)] set [owner.current]'s total blood to [total_blood], and usable blood to [usable_blood].") - log_admin("[key_name(admin)] set [owner.current]'s total blood to [total_blood], and usable blood to [usable_blood].") - -/datum/antagonist/vampire/on_gain() - SSgamemode.vampires += owner - give_objectives() - check_vampire_upgrade() - owner.special_role = "vampire" - owner.current.faction += "vampire" - RegisterSignal(owner, COMSIG_MIND_CHECK_ANTAG_RESOURCE, PROC_REF(has_blood)) - RegisterSignal(owner, COMSIG_MIND_SPEND_ANTAG_RESOURCE, PROC_REF(use_blood)) - if(ishuman(owner.current)) - var/mob/living/carbon/human/H = owner.current - RegisterSignal(H, COMSIG_HUMAN_BURNING, PROC_REF(handle_fire)) - var/obj/item/organ/brain/B = H.getorganslot(ORGAN_SLOT_BRAIN) - if(B) - B.organ_flags &= ~ORGAN_VITAL - B.decoy_override = TRUE - return ..() - -/datum/antagonist/vampire/on_removal() - remove_vampire_powers() - owner.current.faction -= "vampire" - SSgamemode.vampires -= owner - owner.special_role = null - UnregisterSignal(owner, COMSIG_MIND_CHECK_ANTAG_RESOURCE) - UnregisterSignal(owner, COMSIG_MIND_SPEND_ANTAG_RESOURCE) - if(ishuman(owner.current)) - var/mob/living/carbon/human/H = owner.current - UnregisterSignal(H, COMSIG_HUMAN_BURNING) - if(owner && H.hud_used && H.hud_used.vamp_blood_display) - H.hud_used.vamp_blood_display.invisibility = INVISIBILITY_ABSTRACT - for(var/O in objectives_given) - objectives -= O - LAZYCLEARLIST(objectives_given) - if(owner.current) - to_chat(owner.current,span_userdanger("Your powers have been quenched! You are no longer a vampire")) - owner.special_role = null - var/mob/living/carbon/human/C = owner.current - if(istype(C)) - var/obj/item/organ/brain/B = C.getorganslot(ORGAN_SLOT_BRAIN) - if(B && (B.decoy_override != initial(B.decoy_override))) - B.organ_flags |= ORGAN_VITAL - B.decoy_override = FALSE - return ..() - -/datum/antagonist/vampire/greet() - to_chat(owner, span_userdanger("You are a Vampire!")) - to_chat(owner, "You are a creature of the night -- holy water, the chapel, and space will cause you to burn.") - to_chat(owner, span_userdanger("Hit someone in the head with harm intent and an open hand to start sucking their blood. However, only blood from living, non-vampiric creatures is usable!")) - to_chat(owner, "Coffins will heal you.") - if(full_vampire == FALSE) - to_chat(owner, "You are not required to obey other vampires, however, you have gained a respect for them.") - if(LAZYLEN(objectives_given)) - owner.announce_objectives() - if(full_vampire == FALSE) - if(prob(10)) - owner.current.playsound_local(get_turf(owner.current), 'yogstation/sound/ambience/antag/lilithspact_alt.ogg',80,0) - else - owner.current.playsound_local(get_turf(owner.current), 'yogstation/sound/ambience/antag/lilithspact.ogg',80,0) - else - if(prob(10)) - owner.current.playsound_local(get_turf(owner.current), 'yogstation/sound/ambience/antag/newvampire_alt.ogg',80,0) - else - owner.current.playsound_local(get_turf(owner.current), 'yogstation/sound/ambience/antag/newvampire.ogg',80,0) - -/datum/antagonist/vampire/proc/give_objectives() - if(full_vampire) - for(var/i = 1, i < CONFIG_GET(number/traitor_objectives_amount), i++) - forge_single_objective() - if(prob(50)) - var/datum/objective/convert/convert_objective = new - convert_objective.owner = owner - convert_objective.gen_amount_goal() - add_objective(convert_objective) - else - var/datum/objective/blood/blood_objective = new - blood_objective.owner = owner - blood_objective.gen_amount_goal() - add_objective(blood_objective) - else - var/datum/objective/blood/blood_objective = new - blood_objective.owner = owner - blood_objective.gen_amount_goal() - add_objective(blood_objective) - - if(!(locate(/datum/objective/escape) in objectives)) - var/datum/objective/escape/escape_objective = new - escape_objective.owner = owner - add_objective(escape_objective) - return - -/datum/antagonist/vampire/proc/add_objective(datum/objective/O) - objectives += O - objectives_given += O - -/datum/antagonist/vampire/proc/forge_single_objective() //Returns how many objectives are added - .=1 - if(prob(50)) - var/list/active_ais = active_ais() - if(active_ais.len && prob(100/GLOB.joined_player_list.len)) - var/datum/objective/destroy/destroy_objective = new - destroy_objective.owner = owner - destroy_objective.find_target() - add_objective(destroy_objective) - else if(prob(30)) - var/datum/objective/maroon/maroon_objective = new - maroon_objective.owner = owner - maroon_objective.find_target() - add_objective(maroon_objective) - else - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = owner - kill_objective.find_target() - add_objective(kill_objective) - else - var/datum/objective/steal/steal_objective = new - steal_objective.owner = owner - steal_objective.find_target() - add_objective(steal_objective) - -/datum/antagonist/vampire/proc/has_blood(datum/mind, flag = ANTAG_RESOURCE_VAMPIRE, amt) - SIGNAL_HANDLER - if(flag != ANTAG_RESOURCE_VAMPIRE) - return FALSE - return usable_blood >= amt - -/datum/antagonist/vampire/proc/use_blood(datum/mind, list/resource_costs) - SIGNAL_HANDLER - if(!LAZYLEN(resource_costs)) - return - var/amount = resource_costs[ANTAG_RESOURCE_VAMPIRE] - if(!amount) - return - if(!has_blood(amt = amount)) - return - usable_blood -= amount - to_chat(owner.current, span_notice("You have [usable_blood] left to use.")) - -/datum/antagonist/vampire/proc/vamp_burn(severe_burn = FALSE) - var/mob/living/L = owner.current - if(!L) - return - var/burn_chance = severe_burn ? 35 : 8 - if(prob(burn_chance) && L.health >= 50) - switch(L.health) - if(75 to 100) - L.visible_message(span_warning("[L]'s skin begins to flake!"), span_danger("Your skin flakes away...")) - if(50 to 75) - L.visible_message(span_warning("[L]'s skin sizzles loudly!"), span_danger("Your skin sizzles!"), "You hear sizzling.") - L.adjustFireLoss(3) - else if(L.health < 50) - if(!L.on_fire) - L.visible_message(span_warning("[L] catches fire!"), span_danger("Your skin catches fire!")) - L.emote("scream") - else - L.visible_message(span_warning("[L] continues to burn!"), span_danger("Your continue to burn!")) - L.adjust_fire_stacks(5) - L.ignite_mob() - return - -/datum/antagonist/vampire/proc/handle_fire() - var/mob/living/carbon/human/dude = owner.current - if(dude.on_fire && dude.stat == DEAD && !get_ability(/datum/vampire_passive/full)) - dude.dust() - -/datum/antagonist/vampire/proc/check_sun() - var/mob/living/carbon/C = owner.current - if(!C) - return - var/ax = C.x - var/ay = C.y - - for(var/i = 1 to 20) - ax += round(sin(SSsun.azimuth), 0.01) - ay += round(cos(SSsun.azimuth), 0.01) - - var/turf/T = locate(round(ax, 0.5), round(ay, 0.5), C.z) - - if(T.x == 1 || T.x == world.maxx || T.y == 1 || T.y == world.maxy) - break - - if(T.density) - return - vamp_burn(TRUE) - -/datum/antagonist/vampire/apply_innate_effects(mob/living/mob_override) - var/mob/living/current_mob = mob_override || owner.current - handle_clown_mutation(current_mob, mob_override ? null : "Your bloodlusting desire overcomes your clownish heritage, you are able to use weapons!") - RegisterSignal(current_mob, COMSIG_LIVING_LIFE, PROC_REF(vampire_life)) - ADD_TRAIT(current_mob, TRAIT_UNHOLY, type) - -/datum/antagonist/vampire/remove_innate_effects(mob/living/mob_override) - var/mob/living/current_mob = mob_override || owner.current - UnregisterSignal(current_mob, COMSIG_LIVING_LIFE) - REMOVE_TRAIT(current_mob, TRAIT_UNHOLY, type) - return ..() - -/datum/antagonist/vampire/proc/vampire_life(mob/living/source, seconds_per_tick, times_fired) - var/mob/living/carbon/C = source - if(!C) - return - if(owner && C.hud_used && C.hud_used.vamp_blood_display) - C.hud_used.vamp_blood_display.invisibility = FALSE - C.hud_used.vamp_blood_display.maptext = ANTAG_MAPTEXT(usable_blood, COLOR_CHANGELING_CHEMICALS) - handle_vampire_cloak() - if(get_ability(/datum/vampire_passive/regen)) - C.heal_overall_damage(1, 1, 0, BODYPART_ANY) //advanced vampire powers give regen to even robotic limbs - C.adjustToxLoss(-1, TRUE, TRUE) - C.adjustOxyLoss(-2.5) - - if(istype(C.loc, /obj/structure/closet/crate/coffin)) - C.heal_overall_damage(4, 4, 0, BODYPART_ORGANIC) //sleepy in coffin doesn't - C.adjustToxLoss(-4, TRUE, TRUE) - C.adjustOxyLoss(-4) - C.adjustCloneLoss(-4) - return - if(!get_ability(/datum/vampire_passive/full) && istype(get_area(C.loc), /area/chapel)) - vamp_burn() - if(isspaceturf(C.loc)) - check_sun() - - -/datum/antagonist/vampire/proc/handle_bloodsucking(mob/living/carbon/human/H) - draining = H - var/mob/living/carbon/human/O = owner.current - var/blood = 0 - var/blood_coeff = 1 //how much blood is gained as a % from the amount drained, currently changed by how dead the victim is - var/old_bloodtotal = 0 //used to see if we increased our blood total - var/old_bloodusable = 0 //used to see if we increased our blood usable - var/silent = FALSE //if the succ gives a message/sounds - var/warned = FALSE //has the vampire been warned they're about to alert a target while stealth sucking? - var/blood_to_take = BLOOD_SUCK_BASE //how much blood should be removed per succ? changes depending on grab state - log_attack("[O] ([O.ckey]) bit [H] ([H.ckey]) in the neck") - if(!(O.pulling == H) && !get_ability(/datum/vampire_passive/nostealth)) - silent = TRUE - blood_to_take *= 0.5 //half blood from targets that aren't being pulled, but they also don't get warned until it starts to cause damage - else if(O.grab_state >= GRAB_NECK) - blood_to_take *= 1.5 //50% more blood from targets that are being neck grabbed or above - if(!silent) - O.visible_message(span_danger("[O] grabs [H]'s neck harshly and sinks in their fangs!"), span_danger("You sink your fangs into [H] and begin to [blood_to_take > BLOOD_SUCK_BASE ? "quickly " : ""]drain their blood."), span_notice("You hear a soft puncture and a wet sucking noise.")) - playsound(O.loc, 'sound/weapons/bite.ogg', 50, 1) - else - to_chat(O, span_notice("You stealthily begin to drain blood from [H]. Be careful, as they will notice if their blood gets too low.")) - O.playsound_local(O, 'sound/weapons/bite.ogg', 50, 1) - if(!iscarbon(owner)) - H.LAssailant = null - else - H.LAssailant = WEAKREF(O) - while(do_after(O, 5 SECONDS, H)) - if(!IS_VAMPIRE(O)) - to_chat(O, span_warning("Your fangs have disappeared!")) - return - if(blood_to_take > BLOOD_SUCK_BASE && (!(O.pulling == H) || O.grab_state < GRAB_NECK))//smooth movement from aggressive suck to normal suck - blood_to_take = BLOOD_SUCK_BASE - to_chat(O, span_warning("You lose your grip on [H], reducing your bloodsucking speed.")) - if(blood_to_take == BLOOD_SUCK_BASE && (O.pulling == H && O.grab_state >= GRAB_NECK))//smooth movement from normal suck to aggressive suck - blood_to_take *= 1.5 - to_chat(O, span_warning("Your enhanced grip on [H] allows you to extract blood faster.")) - if(silent && O.pulling == H) //smooth movement from stealth suck to normal suck - silent = FALSE - blood_to_take = BLOOD_SUCK_BASE - O.visible_message(span_danger("[O] grabs [H]'s neck harshly and sinks in their fangs!"), span_danger("You sink your fangs into [H] and begin to drain their blood."), span_notice("You hear a soft puncture and a wet sucking noise.")) - playsound(O.loc, 'sound/weapons/bite.ogg', 50, 1) - old_bloodtotal = total_blood - old_bloodusable = usable_blood - if(!H.blood_volume) - to_chat(O, span_warning("They've got no blood left to give.")) - break - blood_coeff = 0.8 //20 blood gain at base for living, 30 with aggressive grab, 10 with stealth - if(H.stat == DEAD || !H.client) - blood_coeff = 0.2 //5 blood gain at base for dead or uninhabited, 7 with aggressive grab, 2 with stealth - blood = round(min(blood_to_take * blood_coeff, H.blood_volume)) //if the victim has less than the amount of blood left to take, just take all of it. - total_blood += blood //get total blood 100% efficiency because fuck waiting out 5 fucking minutes and 1500 actual blood to get your 600 blood for the objective - usable_blood += blood * 0.75 //75% usable blood since it's actually used for stuff - check_vampire_upgrade() - if(old_bloodtotal != total_blood) - to_chat(O, span_notice("You have accumulated [total_blood] [total_blood > 1 ? "units" : "unit"] of blood[usable_blood != old_bloodusable ? ", and have [usable_blood] left to use" : ""].")) - H.blood_volume = max(H.blood_volume - blood_to_take, 0) - if(silent && !warned && (H.blood_volume <= (BLOOD_VOLUME_SAFE(H) + 20))) - to_chat(O, span_boldwarning("Their blood is at a dangerously low level, they will likely begin to feel the effects if you continue...")) - warned = TRUE - if(ishuman(O)) - O.nutrition = min(O.nutrition + (blood * 0.5), NUTRITION_LEVEL_WELL_FED) - if(!silent) - playsound(O.loc, 'sound/items/eatfood.ogg', 40, 1, extrarange = -4)//have to be within 3 tiles to hear the sucking - else if(H.get_blood_state() <= BLOOD_OKAY) - to_chat(H, span_warning("You notice [O] standing oddly close...")) - if(get_ability(/datum/vampire_passive/nostealth) && silent) - to_chat(O, span_boldwarning("You can no longer suck blood silently!")) - break - - draining = null - to_chat(owner, span_notice("You stop draining [H.name] of blood.")) - -/datum/antagonist/vampire/proc/force_add_ability(path) - var/datum/action/cooldown/spell/hi_how_are_you = new path(owner) - if(istype(hi_how_are_you)) - hi_how_are_you.Grant(owner.current) - powers += hi_how_are_you - -/datum/antagonist/vampire/proc/get_ability(path) - for(var/datum/power as anything in powers) - if(power.type == path) - return power - return null - -/datum/antagonist/vampire/proc/add_ability(path) - if(!get_ability(path)) - force_add_ability(path) - -/datum/antagonist/vampire/proc/remove_ability(ability) - if(ability && (ability in powers)) - powers -= ability - owner.current.actions.Remove(ability) - qdel(ability) - - -/datum/antagonist/vampire/proc/remove_vampire_powers() - for(var/datum/power as anything in powers) - remove_ability(power) - owner.current.alpha = 255 - -/datum/antagonist/vampire/proc/check_vampire_upgrade(announce = TRUE) - var/list/old_powers = powers.Copy() - for(var/ptype in upgrade_tiers) - var/level = upgrade_tiers[ptype] - if(total_blood >= level) - add_ability(ptype) - if(announce) - announce_new_power(old_powers) - owner.current.update_sight() //deal with sight abilities - -/datum/antagonist/vampire/proc/announce_new_power(list/old_powers) - for(var/p in powers) - if(!(p in old_powers)) - if(istype(p, /datum/action/cooldown/spell)) - var/datum/action/cooldown/spell/power = p - to_chat(owner.current, span_notice("[power.gain_desc]")) - else if(istype(p, /datum/vampire_passive)) - var/datum/vampire_passive/power = p - to_chat(owner, power.gain_desc) - -/datum/antagonist/vampire/proc/handle_vampire_cloak() - if(!ishuman(owner.current)) - owner.current.alpha = 255 - return - var/mob/living/carbon/human/H = owner.current - var/turf/T = get_turf(H) - var/light_available = T.get_lumcount() - - if(!istype(T)) - return 0 - - if(!iscloaking) - H.alpha = 255 - return 0 - - if(light_available <= 0.25) - H.alpha = round((255 * 0.15)) - return 1 - else - H.alpha = round((255 * 0.80)) - -/datum/antagonist/vampire/roundend_report() - var/list/result = list() - - var/vampwin = TRUE - - result += printplayer(owner) - - var/objectives_text = "" - if(objectives_given.len)//If the vampire had no objectives, don't need to process this. - var/count = 1 - for(var/datum/objective/objective in objectives_given) - if(objective.check_completion()) - objectives_text += "
Objective #[count]: [objective.explanation_text] [span_greentext("Success!")]" - else - objectives_text += "
Objective #[count]: [objective.explanation_text] [span_redtext("Fail.")]" - vampwin = FALSE - count++ - - result += objectives_text - - if(vampwin) - result += span_greentext("The vampire was successful!") - SSachievements.unlock_achievement(/datum/achievement/greentext/vampire, owner.current.client) - else - result += span_redtext("The vampire has failed!") - SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg') - - return result.Join("
") -#undef BLOOD_SUCK_BASE -#undef ALL_POWERS_UNLOCKED - -/datum/antagonist/vampire/get_preview_icon() - var/icon/vampire_icon = icon('icons/mob/animal.dmi', "bat") - - vampire_icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE) - - return vampire_icon - - - -//non antag stuff -/proc/add_vampire(mob/living/L, full_vampire = TRUE) - if(!L || !L.mind || IS_VAMPIRE(L)) - return FALSE - var/datum/antagonist/vampire/vamp - if(full_vampire == TRUE) - vamp = L.mind.add_antag_datum(/datum/antagonist/vampire) - else - vamp = L.mind.add_antag_datum(/datum/antagonist/vampire/new_blood) - return vamp - -/proc/remove_vampire(mob/living/L) - if(!L || !L.mind || !IS_VAMPIRE(L)) - return FALSE - var/datum/antagonist/vamp = L.mind.has_antag_datum(/datum/antagonist/vampire) - vamp.on_removal() - return TRUE - -/mob/living/carbon/human/get_status_tab_items() - . = ..() - var/datum/antagonist/vampire/vamp = mind.has_antag_datum(/datum/antagonist/vampire) - if(vamp) - . += "Total Blood: [vamp.total_blood]" - . += "Usable Blood: [vamp.usable_blood]" diff --git a/yogstation/code/modules/antagonists/vampire/vampire_hud.dm b/yogstation/code/modules/antagonists/vampire/vampire_hud.dm deleted file mode 100644 index f43b025c1d78..000000000000 --- a/yogstation/code/modules/antagonists/vampire/vampire_hud.dm +++ /dev/null @@ -1,15 +0,0 @@ -/datum/hud - var/atom/movable/screen/vampire/vamp_blood_display - -/datum/hud/New(mob/owner, ui_style = 'icons/mob/screen_midnight.dmi') - . = ..() - vamp_blood_display = new /atom/movable/screen/vampire(src) - -/datum/hud/human/New(mob/living/carbon/human/owner, ui_style = 'icons/mob/screen_midnight.dmi') - . = ..() - vamp_blood_display = new /atom/movable/screen/vampire(src) - infodisplay += vamp_blood_display - -/datum/hud/Destroy() - . = ..() - vamp_blood_display = null diff --git a/yogstation/code/modules/antagonists/vampire/vampire_objectives.dm b/yogstation/code/modules/antagonists/vampire/vampire_objectives.dm deleted file mode 100644 index 04c64fa05f35..000000000000 --- a/yogstation/code/modules/antagonists/vampire/vampire_objectives.dm +++ /dev/null @@ -1,34 +0,0 @@ -/datum/objective/blood/proc/gen_amount_goal(lowbound = 450, highbound = 700) - target_amount = rand (lowbound,highbound) - explanation_text = "Extract [target_amount] units of blood." - return target_amount - -/datum/objective/blood/check_completion() - if(..()) - return TRUE - if(!owner) - return FALSE - var/datum/antagonist/vampire/vamp = owner.has_antag_datum(/datum/antagonist/vampire) - if(vamp && target_amount <= vamp.total_blood) - return TRUE - else - return FALSE - -/datum/objective/convert/proc/gen_amount_goal(lowbound = 1, highbound = 3) - target_amount = rand (lowbound,highbound) - if(target_amount == 1) - explanation_text = "Turn a crew member into a vampire using Lilith's Pact." - else - explanation_text = "Turn [target_amount] crew members into vampires using Lilith's Pact." - return target_amount - -/datum/objective/convert/check_completion() - if(..()) - return TRUE - if(!owner) - return FALSE - var/datum/antagonist/vampire/vamp = owner.has_antag_datum(/datum/antagonist/vampire) - if(vamp && target_amount <= vamp.converted) - return TRUE - else - return FALSE diff --git a/yogstation/code/modules/antagonists/vampire/vampire_other.dm b/yogstation/code/modules/antagonists/vampire/vampire_other.dm deleted file mode 100644 index 8b3c69732153..000000000000 --- a/yogstation/code/modules/antagonists/vampire/vampire_other.dm +++ /dev/null @@ -1,69 +0,0 @@ -/* - In this file: - various vampire interactions and items -*/ - - -/obj/item/clothing/suit/draculacoat - name = "Vampire Coat" - desc = "What is a man? A miserable little pile of secrets." - worn_icon = 'yogstation/icons/mob/clothing/suit/suit.dmi' - icon = 'icons/obj/clothing/suits/suits.dmi' - icon_state = "draculacoat" - item_state = "draculacoat" - body_parts_covered = CHEST|GROIN|LEGS|ARMS - slowdown = -0.2 //very minor speedboost - allowed = null - var/blood_regen_delay = 10 SECONDS - COOLDOWN_DECLARE(regen_cooldown) - var/dodge_delay = 10 SECONDS - COOLDOWN_DECLARE(dodge_cooldown) - -/obj/item/clothing/suit/draculacoat/Initialize(mapload) - if(!allowed) - allowed = GLOB.security_vest_allowed - START_PROCESSING(SSobj, src) - return ..() - -/obj/item/clothing/suit/draculacoat/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/clothing/suit/draculacoat/equipped(mob/user, slot) - . = ..() - if(slot == ITEM_SLOT_OCLOTHING) - RegisterSignal(user, COMSIG_HUMAN_CHECK_SHIELDS, PROC_REF(dodge)) - else - UnregisterSignal(user, COMSIG_HUMAN_CHECK_SHIELDS) - -/obj/item/clothing/suit/draculacoat/dropped(mob/user) - if(user.get_item_by_slot(ITEM_SLOT_OCLOTHING) == src) - UnregisterSignal(user, COMSIG_HUMAN_CHECK_SHIELDS) - return ..() - -/obj/item/clothing/suit/draculacoat/proc/dodge(mob/living/carbon/human/user, atom/movable/incoming, damage, attack_text) - if(user.incapacitated() || !COOLDOWN_FINISHED(src, dodge_cooldown) || !IS_VAMPIRE(user)) - return NONE - COOLDOWN_START(src, dodge_cooldown, dodge_delay) - - user.balloon_alert_to_viewers("dodged!", "dodged!", COMBAT_MESSAGE_RANGE) - user.visible_message(span_danger("With inhuman speed [user] dodges [attack_text]!"), span_userdanger("You dodge [attack_text]"), null, COMBAT_MESSAGE_RANGE) - playsound(user, 'sound/effects/space_wind_big.ogg', 50, 1) - return SHIELD_DODGE - -/obj/item/clothing/suit/draculacoat/process() - var/mob/living/carbon/human/user = src.loc - if(user && ishuman(user) && IS_VAMPIRE(user) && (user.wear_suit == src)) - if(COOLDOWN_FINISHED(src, regen_cooldown)) - COOLDOWN_START(src, regen_cooldown, blood_regen_delay) - var/datum/antagonist/vampire/vampire = IS_VAMPIRE(user) - if (vampire.total_blood >= 5 && vampire.usable_blood < vampire.total_blood) - vampire.usable_blood = min(vampire.usable_blood + 5, vampire.total_blood) // 5 units every 10 seconds - -/obj/item/storage/book/bible/attack(mob/living/M, mob/living/carbon/human/user, heal_mode = TRUE) - . = ..() - if(!(user.mind && user.mind.holy_role) && IS_VAMPIRE(user)) - to_chat(user, span_danger("[deity_name] channels through \the [src] and sets you ablaze for your blasphemy!")) - user.adjust_fire_stacks(5) - user.ignite_mob() - user.emote("scream", 1) diff --git a/yogstation/code/modules/mob/living/carbon/carbon.dm b/yogstation/code/modules/mob/living/carbon/carbon.dm index 8fb7a45ef6f1..60e0c94ecd7f 100644 --- a/yogstation/code/modules/mob/living/carbon/carbon.dm +++ b/yogstation/code/modules/mob/living/carbon/carbon.dm @@ -1,14 +1,3 @@ -/mob/living/carbon/update_sight() - if(mind) - var/datum/antagonist/vampire/V = mind.has_antag_datum(/datum/antagonist/vampire) - if(V) - if(V.get_ability(/datum/vampire_passive/full)) - ADD_TRAIT(src, TRAIT_TRUE_NIGHT_VISION, "full_vamp") - ADD_TRAIT(src, TRAIT_XRAY_VISION, "full_vamp") - else if(V.get_ability(/datum/vampire_passive/vision)) - ADD_TRAIT(src, TRAIT_THERMAL_VISION, "partial_vamp") - return ..() - /mob/living/carbon/relaymove(mob/user, direction) if(user in src.stomach_contents) if(prob(40)) diff --git a/yogstation/code/modules/mob/living/carbon/human/species_types/plantpeople.dm b/yogstation/code/modules/mob/living/carbon/human/species_types/plantpeople.dm index fe373d9fd019..dcdd0d0c3cf2 100644 --- a/yogstation/code/modules/mob/living/carbon/human/species_types/plantpeople.dm +++ b/yogstation/code/modules/mob/living/carbon/human/species_types/plantpeople.dm @@ -64,8 +64,6 @@ /datum/species/pod/spec_life(mob/living/carbon/human/H) if(H.stat == DEAD || H.stat == UNCONSCIOUS) return - if(IS_BLOODSUCKER(H) && !HAS_TRAIT(H, TRAIT_MASQUERADE)) - return var/turf/T = get_turf(H) if(!T) return diff --git a/yogstation/code/modules/mob/living/silicon/robot/login.dm b/yogstation/code/modules/mob/living/silicon/robot/login.dm deleted file mode 100644 index 279698547d22..000000000000 --- a/yogstation/code/modules/mob/living/silicon/robot/login.dm +++ /dev/null @@ -1,4 +0,0 @@ -/mob/living/silicon/robot/Login() - ..() - if(mind) - remove_vampire() diff --git a/yogstation/code/modules/storytellers/converted_events/solo/monsterhunter.dm b/yogstation/code/modules/storytellers/converted_events/solo/monsterhunter.dm index 7dd372905ea9..b17033ebcd5b 100644 --- a/yogstation/code/modules/storytellers/converted_events/solo/monsterhunter.dm +++ b/yogstation/code/modules/storytellers/converted_events/solo/monsterhunter.dm @@ -4,7 +4,6 @@ GLOBAL_LIST_INIT(monster_hunter_prey_antags, typecacheof(list( /datum/antagonist/bloodsucker, /datum/antagonist/changeling, /datum/antagonist/heretic, - /datum/antagonist/vampire, /datum/antagonist/darkspawn ))) diff --git a/yogstation/code/modules/storytellers/converted_events/solo/vampire.dm b/yogstation/code/modules/storytellers/converted_events/solo/vampire.dm deleted file mode 100644 index 7ec581dadf7a..000000000000 --- a/yogstation/code/modules/storytellers/converted_events/solo/vampire.dm +++ /dev/null @@ -1,34 +0,0 @@ -/datum/round_event_control/antagonist/solo/vampire - antag_flag = ROLE_VAMPIRE - tags = list(TAG_COMBAT, TAG_MAGICAL, TAG_CREW_ANTAG) - antag_datum = /datum/antagonist/vampire - protected_roles = list( - JOB_CAPTAIN, - JOB_HEAD_OF_PERSONNEL, - JOB_CHIEF_ENGINEER, - JOB_CHIEF_MEDICAL_OFFICER, - JOB_RESEARCH_DIRECTOR, - JOB_DETECTIVE, - JOB_HEAD_OF_SECURITY, - JOB_SECURITY_OFFICER, - JOB_WARDEN, - JOB_BRIG_PHYSICIAN, - JOB_CHAPLAIN - ) - restricted_roles = list( - JOB_AI, - JOB_CYBORG, - ) - min_players = 15 - weight = 10 - maximum_antags = 2 - -/datum/round_event_control/antagonist/solo/vampire/roundstart - name = "Vampires" - roundstart = TRUE - title_icon = "vampires" - earliest_start = 0 SECONDS - -/datum/round_event_control/antagonist/solo/vampire/midround - name = "Vampiric Accident" - max_occurrences = 1 diff --git a/yogstation/sound/ambience/antag/become_veil.ogg b/yogstation/sound/ambience/antag/become_veil.ogg deleted file mode 100644 index de8379d2cbd1..000000000000 Binary files a/yogstation/sound/ambience/antag/become_veil.ogg and /dev/null differ diff --git a/yogstation/sound/ambience/antag/sling.ogg b/yogstation/sound/ambience/antag/sling.ogg deleted file mode 100644 index de8379d2cbd1..000000000000 Binary files a/yogstation/sound/ambience/antag/sling.ogg and /dev/null differ