Bludsuckers

This commit is contained in:
Artur
2019-11-29 21:28:14 +02:00
parent 65bec77c5a
commit 02048ce009
67 changed files with 6397 additions and 9 deletions
@@ -0,0 +1,294 @@
/datum/game_mode
var/list/datum/mind/bloodsuckers = list() // List of minds belonging to this game mode.
var/list/datum/mind/vassals = list() // List of minds that have been turned into Vassals.
var/list/datum/mind/vamphunters = list() // List of minds hunting vampires.
var/obj/effect/sunlight/bloodsucker_sunlight // Sunlight Timer. Created on first Bloodsucker assign. Destroyed on last removed Bloodsucker.
// LISTS //
var/list/vassal_allowed_antags = list(/datum/antagonist/brother, /datum/antagonist/traitor, /datum/antagonist/traitor/internal_affairs, /datum/antagonist/survivalist, \
/datum/antagonist/rev, /datum/antagonist/nukeop, /datum/antagonist/pirate, /datum/antagonist/cult, /datum/antagonist/abductee)
// The antags you're allowed to be if turning Vassal.
/datum/game_mode/bloodsucker
name = "bloodsucker"
config_tag = "bloodsucker"
traitor_name = "Bloodsucker"
antag_flag = ROLE_BLOODSUCKER
false_report_weight = 1
restricted_jobs = list("AI","Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Deputy")
required_players = 0
required_enemies = 1
recommended_enemies = 4
reroll_friendly = 1
enemy_minimum_age = 7
announce_span = "danger"
announce_text = "Filthy, bloodsucking vampires are crawling around disguised as crewmembers!\n\
<span class='danger'>Bloodsuckers</span>: The crew are cattle, while you are both shepherd and slaughterhouse.\n\
<span class='notice'>Crew</span>: Put an end to the undead infestation before the station is overcome!"
// Seems to be run by game ONCE, and finds all potential players to be antag.
/datum/game_mode/bloodsucker/pre_setup()
// Set Restricted Jobs
if(CONFIG_GET(flag/protect_roles_from_antagonist))
restricted_jobs += protected_jobs
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
restricted_jobs += "Assistant"
// Set number of Vamps
recommended_enemies = CLAMP(round(num_players()/10), 1, 6);
// Select Antags
for(var/i = 0, i < recommended_enemies, i++)
if (!antag_candidates.len)
break
var/datum/mind/bloodsucker = pick(antag_candidates)
// Can we even BE a bloodsucker?
//if (can_make_bloodsucker(bloodsucker, display_warning=FALSE))
bloodsuckers += bloodsucker
bloodsucker.restricted_roles = restricted_jobs
log_game("[bloodsucker.key] (ckey) has been selected as a Bloodsucker.")
antag_candidates.Remove(bloodsucker) // Apparently you can also write antag_candidates -= bloodsucker
// FULPSTATION: Assign Hunters (as many as monsters, plus one)
assign_monster_hunters(bloodsuckers.len, TRUE, bloodsuckers) // FULP
// Do we have enough vamps to continue?
return bloodsuckers.len >= required_enemies
// Gamemode is all done being set up. We have all our Vamps. We now pick objectives and let them know what's happening.
/datum/game_mode/bloodsucker/post_setup()
// Sunlight (Creating Bloodsuckers manually will check to create this, too)
check_start_sunlight()
// Vamps
for(var/datum/mind/bloodsucker in bloodsuckers)
// spawn() --> Run block of code but game continues on past it.
// sleep() --> Run block of code and freeze code there (including whoever called us) until it's resolved.
//Clean Bloodsucker Species (racist?)
//clean_invalid_species(bloodsucker)
// TO-DO !!!
// Add Bloodsucker Antag Datum (or remove from list on Fail)
if (!make_bloodsucker(bloodsucker))
bloodsuckers -= bloodsucker
// NOTE: Hunters are done in ..() parent proc
return ..()
// Checking for ACTUALLY Dead Vamps
/datum/game_mode/bloodsucker/are_special_antags_dead()
// Bloodsucker not Final Dead
for(var/datum/mind/bloodsucker in bloodsuckers)
if(!bloodsucker.AmFinalDeath())
return FALSE
return TRUE
// Init Sunlight (called from datum_bloodsucker.on_gain(), in case game mode isn't even Bloodsucker
/datum/game_mode/proc/check_start_sunlight()
// Already Sunlight (and not about to cancel)
if (istype(bloodsucker_sunlight) && !bloodsucker_sunlight.cancel_me)
return
bloodsucker_sunlight = new ()
// End Sun (last bloodsucker removed)
/datum/game_mode/proc/check_cancel_sunlight()
// No Sunlight
if (!istype(bloodsucker_sunlight))
return
if (bloodsuckers.len <= 0)
bloodsucker_sunlight.cancel_me = TRUE
qdel(bloodsucker_sunlight)
bloodsucker_sunlight = null
/datum/game_mode/proc/is_daylight()
return istype(bloodsucker_sunlight) && bloodsucker_sunlight.amDay
//////////////////////////////////////////////////////////////////////////////
/datum/game_mode/proc/can_make_bloodsucker(datum/mind/bloodsucker, datum/mind/creator, display_warning=TRUE) // Creator is just here so we can display fail messages to whoever is turning us.
// No Mind
if(!bloodsucker || !bloodsucker.key) // KEY is client login?
//if(creator) // REMOVED. You wouldn't see their name if there is no mind, so why say anything?
// to_chat(creator, "<span class='danger'>[bloodsucker] isn't self-aware enough to be raised as a Bloodsucker!</span>")
return FALSE
// Current body is invalid
if(!ishuman(bloodsucker.current))// && !ismonkey(bloodsucker.current))
if(display_warning && creator)
to_chat(creator, "<span class='danger'>[bloodsucker] isn't evolved enough to be raised as a Bloodsucker!</span>")
return FALSE
// Species Must have a HEART (Sorry Plasmabois)
var/mob/living/carbon/human/H = bloodsucker.current
if(NOBLOOD in H.dna.species.species_traits)
if(display_warning && creator)
to_chat(creator, "<span class='danger'>[bloodsucker]'s DNA isn't compatible!</span>")
return FALSE
// Already a Non-Human Antag
if(bloodsucker.has_antag_datum(/datum/antagonist/abductor) || bloodsucker.has_antag_datum(/datum/antagonist/devil) || bloodsucker.has_antag_datum(/datum/antagonist/changeling))
return FALSE
// Already a vamp
if(bloodsucker.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
if(display_warning && creator)
to_chat(creator, "<span class='danger'>[bloodsucker] is already a Bloodsucker!</span>")
return FALSE
// Not High Enough
if(creator)
var/datum/antagonist/bloodsucker/creator_bloodsucker = creator.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
if(!istype(creator_bloodsucker) || creator_bloodsucker.vamplevel < BLOODSUCKER_LEVEL_TO_EMBRACE)
to_chat(creator, "<span class='danger'>Your blood is too thin to turn this corpse!</span>")
return FALSE
return TRUE
/datum/game_mode/proc/make_bloodsucker(datum/mind/bloodsucker, datum/mind/creator = null) // NOTE: This is a game_mode/proc, NOT a game_mode/bloodsucker/proc! We need to access this function despite the game mode.
if (!can_make_bloodsucker(bloodsucker))
return FALSE
// Create Datum: Fledgling
var/datum/antagonist/bloodsucker/A
// [FLEDGLING]
if (creator)
A = new ANTAG_DATUM_BLOODSUCKER(bloodsucker)
A.creator = creator
bloodsucker.add_antag_datum(A)
// Log
message_admins("[bloodsucker] has become a Bloodsucker, and was created by [creator].")
log_admin("[bloodsucker] has become a Bloodsucker, and was created by [creator].")
// [MASTER]
else
A = bloodsucker.add_antag_datum(ANTAG_DATUM_BLOODSUCKER)
return TRUE
/datum/game_mode/proc/remove_bloodsucker(datum/mind/bloodsucker)
bloodsucker.remove_antag_datum(ANTAG_DATUM_BLOODSUCKER)
/datum/game_mode/proc/clean_invalid_species(datum/mind/bloodsucker)
// Only checking for Humans here
if (!ishuman(bloodsucker.current) || !bloodsucker.current.client)
return
var/am_valid = TRUE
var/mob/living/carbon/human/H = bloodsucker.current
// Check if PLASMAMAN?
if(NOBLOOD in H.dna.species.species_traits)
am_valid = FALSE
// PROBLEM:
//
// Setting species leaves clothes on. If you were a plasmaman, we need to reassign your entire outfit. Otherwise
// everyone will wonder why you're a human with Plasma clothes (jk they'll know you're antag)
// Convert to HUMAN (along with ID and PDA)
if (!am_valid)
H.set_species(/datum/species/human)
H.real_name = H.client.prefs.custom_names["human"]
if (H.wear_id)
H.wear_id.GetID().registered_name = H.real_name
H.wear_id.GetID().update_label()
/datum/game_mode/proc/can_make_vassal(mob/living/target, datum/mind/creator, display_warning=TRUE)//, check_antag_or_loyal=FALSE)
// Not Correct Type: Abort
if (!iscarbon(target) || !creator)
return FALSE
if (target.stat > UNCONSCIOUS)
return FALSE
// Check Overdose: Am I even addicted to blood? Do I even have any in me?
//if (!target.reagents.addiction_list || !target.reagents.reagent_list)
//message_admins("DEBUG2: can_make_vassal() Abort: No reagents")
// return 0
// Check Overdose: Did my current volume go over the Overdose threshold?
//var/am_addicted = 0
//for (var/datum/reagent/blood/vampblood/blood in target.reagents.addiction_list) // overdosed is tracked in reagent_list, not addiction_list.
//message_admins("DEBUG3: can_make_vassal() Found Blood! [blood] [blood.overdose]")
//if (blood.overdosed)
// am_addicted = 1 // Blood is present in addiction? That's all we need.
// break
//if (!am_addicted)
//message_admins("DEBUG4: can_make_vassal() Abort: No Blood")
// return 0
// No Mind!
if (!target.mind || !target.mind.key)
if (display_warning)
to_chat(creator, "<span class='danger'>[target] isn't self-aware enough to be made into a Vassal.</span>")
return FALSE
// Already MY Vassal
var/datum/antagonist/vassal/V = target.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
if (istype(V) && V.master)
if (V.master.owner == creator)
if (display_warning)
to_chat(creator, "<span class='danger'>[target] is already your loyal Vassal!</span>")
else
if (display_warning)
to_chat(creator, "<span class='danger'>[target] is the loyal Vassal of another Bloodsucker!</span>")
return FALSE
// Already Antag or Loyal (Vamp Hunters count as antags)
if (target.mind.enslaved_to || AmInvalidAntag(target.mind)) //!VassalCheckAntagValid(target.mind, check_antag_or_loyal)) // HAS_TRAIT(target, TRAIT_MINDSHIELD, "implant") ||
if (display_warning)
to_chat(creator, "<span class='danger'>[target] resists the power of your blood to dominate their mind!</span>")
return FALSE
return TRUE
/datum/game_mode/proc/AmValidAntag(datum/mind/M)
// No List?
if(!islist(M.antag_datums) || M.antag_datums.len == 0)
return FALSE
// Am I NOT an invalid Antag? NOTE: We already excluded non-antags above. Don't worry about the "No List?" check in AmInvalidIntag()
return !AmInvalidAntag(M)
/datum/game_mode/proc/AmInvalidAntag(datum/mind/M)
// No List?
if(!islist(M.antag_datums) || M.antag_datums.len == 0)
return FALSE
// Does even ONE antag appear in this mind that isn't in the list? Then FAIL!
for(var/datum/antagonist/antag_datum in M.antag_datums)
if (!(antag_datum.type in vassal_allowed_antags)) // vassal_allowed_antags is a list stored in the game mode, above.
//message_admins("DEBUG VASSAL: Found Invalid: [antag_datum] // [antag_datum.type]")
return TRUE
//message_admins("DEBUG VASSAL: Valid Antags! (total of [M.antag_datums.len])")
// WHEN YOU DELETE THE ABOVE: Remove the 3 second timer on converting the vassal too.
return FALSE
/datum/game_mode/proc/make_vassal(mob/living/target, datum/mind/creator)
if (!can_make_vassal(target,creator))
return FALSE
// Make Vassal
var/datum/antagonist/vassal/V = new ANTAG_DATUM_VASSAL(target.mind)
var/datum/antagonist/bloodsucker/B = creator.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
V.master = B
target.mind.add_antag_datum(V, V.master.get_team())
// Update Bloodsucker Title (we're a daddy now)
B.SelectTitle(am_fledgling = FALSE) // Only works if you have no title yet.
// Log
message_admins("[target] has become a Vassal, and is enslaved to [creator].")
log_admin("[target] has become a Vassal, and is enslaved to [creator].")
return TRUE
/datum/game_mode/proc/remove_vassal(datum/mind/vassal)
vassal.remove_antag_datum(ANTAG_DATUM_VASSAL)
+49
View File
@@ -0,0 +1,49 @@
// Called from game mode pre_setup()
/datum/game_mode/proc/assign_monster_hunters(monster_count = 4, guaranteed_hunters = FALSE, list/datum/mind/exclude_from_hunter)
// Not all game modes GUARANTEE a hunter
if (rand(0,2) == 0) // 50% of the time, we get fewer or NO Hunters
if (!guaranteed_hunters)
return
else
monster_count /= 2
var/list/no_hunter_jobs = list("AI","Cyborg")
// Set Restricted Jobs
if(CONFIG_GET(flag/protect_roles_from_antagonist))
no_hunter_jobs += list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Deputy")
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
no_hunter_jobs += "Assistant"
// Find Valid Hunters
var/list/datum/mind/hunter_candidates = get_players_for_role(ROLE_MONSTERHUNTER)
// Assign Hunters (as many as vamps, plus one)
for(var/i = 1, i < monster_count, i++) // Start at 1 so we skip Hunters if there's only one sucker.
if (!hunter_candidates.len)
break
// Assign Hunter
var/datum/mind/hunter = pick(hunter_candidates)
hunter_candidates.Remove(hunter) // Remove Either Way
// Already Antag? Skip
if (islist(exclude_from_hunter) && (locate(hunter) in exclude_from_hunter)) //if (islist(hunter.antag_datums) && hunter.antag_datums.len)
i --
continue
// NOTE:
vamphunters += hunter
hunter.restricted_roles = no_hunter_jobs
log_game("[hunter.key] (ckey) has been selected as a Hunter.")
// Called from game mode post_setup()
/datum/game_mode/proc/finalize_monster_hunters(monster_count = 4)
var/amEvil = TRUE // First hunter is always an evil boi
for(var/datum/mind/hunter in vamphunters)
var/datum/antagonist/vamphunter/A = new ANTAG_DATUM_HUNTER(hunter)
A.bad_dude = amEvil
hunter.add_antag_datum(A)
amEvil = FALSE // Every other hunter is just a boring greytider
+1 -1
View File
@@ -409,7 +409,7 @@ SLIME SCANNER
var/mob/living/carbon/human/H = C
if(H.bleed_rate)
msg += "<span class='danger'>Subject is bleeding!</span>\n"
var/blood_percent = round((C.blood_volume / (BLOOD_VOLUME_NORMAL * C.blood_ratio))*100)
var/blood_percent = round((C.scan_blood_volume() / (BLOOD_VOLUME_NORMAL * C.blood_ratio))*100)
var/blood_type = C.dna.blood_type
if(blood_id != ("blood" || "jellyblood"))//special blood substance
var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
@@ -26,6 +26,9 @@
if(target.mind.has_antag_datum(/datum/antagonist/brainwashed))
target.mind.remove_antag_datum(/datum/antagonist/brainwashed)
if(target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
SSticker.mode.remove_vassal(target.mind)
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.unconvertable || target.mind.has_antag_datum(/datum/antagonist/gang/boss))
if(!silent)
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel something interfering with your mental conditioning, but you resist it!</span>")
+1 -2
View File
@@ -20,10 +20,9 @@
return
if(broken || !Adjacent(user))
return
if(ishuman(user))
var/mob/living/carbon/human/H = user
//see code/modules/mob/dead/new_player/preferences.dm at approx line 545 for comments!
//this is largely copypasted from there.