Merge remote-tracking branch 'citadel/master' into mobility_flags

This commit is contained in:
kevinz000
2020-02-13 00:39:20 -07:00
481 changed files with 10666 additions and 9059 deletions
@@ -20,6 +20,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED
var/show_name_in_check_antagonists = FALSE //Will append antagonist name in admin listings - use for categories that share more than one antag type
var/list/blacklisted_quirks = list(/datum/quirk/nonviolent,/datum/quirk/mute) // Quirks that will be removed upon gaining this antag. Pacifist and mute are default.
/datum/antagonist/New()
GLOB.antagonists += src
@@ -70,6 +71,7 @@ GLOBAL_LIST_EMPTY(antagonists)
greet()
apply_innate_effects()
give_antag_moodies()
remove_blacklisted_quirks()
if(is_banned(owner.current) && replace_banned)
replace_banned_player()
@@ -117,6 +119,18 @@ GLOBAL_LIST_EMPTY(antagonists)
return
SEND_SIGNAL(owner.current, COMSIG_CLEAR_MOOD_EVENT, "antag_moodlet")
/datum/antagonist/proc/remove_blacklisted_quirks()
var/mob/living/L = owner.current
if(istype(L))
var/list/my_quirks = L.client?.prefs.all_quirks.Copy()
SSquirks.filter_quirks(my_quirks,blacklisted_quirks)
for(var/q in L.roundstart_quirks)
var/datum/quirk/Q = q
if(!(SSquirks.quirk_name_by_path(Q.type) in my_quirks))
if(initial(Q.antag_removal_text))
to_chat(L, "<span class='boldannounce'>[initial(Q.antag_removal_text)]</span>")
L.remove_quirk(Q.type)
//Returns the team antagonist belongs to if any.
/datum/antagonist/proc/get_team()
return
@@ -134,7 +148,7 @@ GLOBAL_LIST_EMPTY(antagonists)
if(objectives.len)
report += printobjectives(objectives)
for(var/datum/objective/objective in objectives)
if(!objective.check_completion())
if(objective.completable && !objective.check_completion())
objectives_complete = FALSE
break
+10 -4
View File
@@ -36,11 +36,17 @@
var/win = TRUE
var/objective_count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
else
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text]"
objective_count++
if(win)
report += "<span class='greentext'>The [name] was successful!</span>"
@@ -84,7 +84,7 @@
//It is called from your coffin on close (by you only)
if(poweron_masquerade == TRUE || owner.current.AmStaked())
return FALSE
owner.current.adjustStaminaLoss(-2 + (regenRate * -8) * mult, 0) // Humans lose stamina damage really quickly. Vamps should heal more.
owner.current.adjustStaminaLoss(-1.5 + (regenRate * -7) * mult, 0) // Humans lose stamina damage really quickly. Vamps should heal more.
owner.current.adjustCloneLoss(-0.1 * (regenRate * 2) * mult, 0)
owner.current.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1 * (regenRate * 4) * mult) //adjustBrainLoss(-1 * (regenRate * 4) * mult, 0)
// No Bleeding
@@ -125,7 +125,7 @@
C.adjustFireLoss(-fireheal * mult, forced = TRUE)
C.adjustToxLoss(-toxinheal * mult * 2, forced = TRUE) //Toxin healing because vamps arent immune
//C.heal_overall_damage(bruteheal * mult, fireheal * mult) // REMOVED: We need to FORCE this, because otherwise, vamps won't heal EVER. Swapped to above.
AddBloodVolume((bruteheal * -0.5 + fireheal * -1) / mult * costMult) // Costs blood to heal
AddBloodVolume((bruteheal * -0.5 + fireheal * -1 + toxinheal * -0.2) / mult * costMult) // Costs blood to heal
return TRUE // Healed! Done for this tick.
if(amInCoffinWhileTorpor) // Limbs? (And I have no other healing)
var/list/missing = owner.current.get_missing_limbs() // Heal Missing
@@ -189,7 +189,7 @@
/datum/antagonist/bloodsucker/proc/HandleDeath()
// FINAL DEATH
// Fire Damage? (above double health)
if(owner.current.getFireLoss_nonProsthetic() >= owner.current.getMaxHealth() * 1.5)
if(owner.current.getFireLoss_nonProsthetic() >= owner.current.maxHealth * 2.5)
FinalDeath()
return
// Staked while "Temp Death" or Asleep
@@ -32,11 +32,11 @@
//var/not_bloodsucker = FALSE // This goes to Vassals or Hunters, but NOT bloodsuckers.
/datum/action/bloodsucker/New()
if (bloodcost > 0)
if(bloodcost > 0)
desc += "<br><br><b>COST:</b> [bloodcost] Blood" // Modify description to add cost.
if (warn_constant_cost)
if(warn_constant_cost)
desc += "<br><br><i>Your over-time blood consumption increases while [name] is active.</i>"
if (amSingleUse)
if(amSingleUse)
desc += "<br><br><i>Useable once per night.</i>"
..()
@@ -47,35 +47,35 @@
/datum/action/bloodsucker/Trigger()
// Active? DEACTIVATE AND END!
if (active && CheckCanDeactivate(TRUE))
if(active && CheckCanDeactivate(TRUE))
DeactivatePower()
return
if (!CheckCanPayCost(TRUE) || !CheckCanUse(TRUE))
if(!CheckCanPayCost(TRUE) || !CheckCanUse(TRUE))
return
PayCost()
if (amToggle)
if(amToggle)
active = !active
UpdateButtonIcon()
if (!amToggle || !active)
if(!amToggle || !active)
StartCooldown() // Must come AFTER UpdateButton(), otherwise icon will revert.
ActivatePower() // NOTE: ActivatePower() freezes this power in place until it ends.
if (active) // Did we not manually disable? Handle it here.
if(active) // Did we not manually disable? Handle it here.
DeactivatePower()
if (amSingleUse)
if(amSingleUse)
RemoveAfterUse()
/datum/action/bloodsucker/proc/CheckCanPayCost(display_error)
if(!owner || !owner.mind)
return FALSE
// Cooldown?
if (cooldownUntil > world.time)
if (display_error)
if(cooldownUntil > world.time)
if(display_error)
to_chat(owner, "[src] is unavailable. Wait [(cooldownUntil - world.time) / 10] seconds.")
return FALSE
// Have enough blood?
var/mob/living/L = owner
if (L.blood_volume < bloodcost)
if (display_error)
if(L.blood_volume < bloodcost)
if(display_error)
to_chat(owner, "<span class='warning'>You need at least [bloodcost] blood to activate [name]</span>")
return FALSE
return TRUE
@@ -1,4 +1,3 @@
#define TIME_BLOODSUCKER_NIGHT 900 // 15 minutes
#define TIME_BLOODSUCKER_DAY_WARN 90 // 1.5 minutes
#define TIME_BLOODSUCKER_DAY_FINAL_WARN 25 // 25 sec
#define TIME_BLOODSUCKER_DAY 60 // 1.5 minutes // 10 is a second, 600 is a minute.
@@ -11,6 +10,7 @@
var/cancel_me = FALSE
var/amDay = FALSE
var/time_til_cycle = 0
var/nightime_duration = 900 //15 Minutes
/obj/effect/sunlight/Initialize()
countdown()
@@ -21,7 +21,7 @@
while(!cancel_me)
time_til_cycle = TIME_BLOODSUCKER_NIGHT
time_til_cycle = nightime_duration
// Part 1: Night (all is well)
while(time_til_cycle > TIME_BLOODSUCKER_DAY_WARN)
@@ -81,7 +81,9 @@
"<span class = 'announce'>The solar flare has ended, and the daylight danger has passed...for now.</span>")
amDay = FALSE
day_end() // Remove VANISHING ACT power from all vamps who have it! Clear Warnings (sunlight, locker protection)
message_admins("BLOODSUCKER NOTICE: Daylight Ended. Resetting to Night (Lasts for [TIME_BLOODSUCKER_NIGHT / 60] minutes.)")
nightime_duration += 100 //Each day makes the night a minute longer.
message_admins("BLOODSUCKER NOTICE: Daylight Ended. Resetting to Night (Lasts for [nightime_duration / 60] minutes.)")
/obj/effect/sunlight/proc/hud_tick()
@@ -97,7 +99,7 @@
sleep(10)
time_til_cycle --
/obj/effect/sunlight/proc/warn_daylight(danger_level=0, vampwarn = "", vassalwarn = "")
/obj/effect/sunlight/proc/warn_daylight(danger_level =0, vampwarn = "", vassalwarn = "")
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
if(!istype(M))
continue
@@ -22,7 +22,7 @@
// STATS
var/vamplevel = 0
var/vamplevel_unspent = 1
var/regenRate = 0.3 // How many points of Brute do I heal per tick?
var/regenRate = 0.4 // How many points of Brute do I heal per tick?
var/feedAmount = 15 // Amount of blood drawn from a target per tick.
var/maxBloodVolume = 600 // Maximum blood a Vamp can hold via feeding. // BLOOD_VOLUME_NORMAL 550 // BLOOD_VOLUME_SAFE 475 //BLOOD_VOLUME_OKAY 336 //BLOOD_VOLUME_BAD 224 // BLOOD_VOLUME_SURVIVE 122
// OBJECTIVES
@@ -34,6 +34,7 @@
var/warn_sun_locker = FALSE // So we only get the locker burn message once per day.
var/warn_sun_burn = FALSE // So we only get the sun burn message once per day.
var/had_toxlover = FALSE
var/level_bloodcost
// LISTS
var/static/list/defaultTraits = list (TRAIT_STABLEHEART, TRAIT_NOBREATH, TRAIT_SLEEPIMMUNE, TRAIT_NOCRITDAMAGE, TRAIT_RESISTCOLD, TRAIT_RADIMMUNE, TRAIT_NIGHT_VISION, \
TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_AGEUSIA, TRAIT_COLDBLOODED, TRAIT_NONATURALHEAL, TRAIT_NOMARROW, TRAIT_NOPULSE, TRAIT_VIRUSIMMUNE)
@@ -98,7 +99,7 @@
/datum/antagonist/bloodsucker/proc/SelectFirstName()
// Names (EVERYONE gets one))
if (owner.current.gender == MALE)
if(owner.current.gender == MALE)
vampname = pick("Desmond","Rudolph","Dracul","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
@@ -119,7 +120,7 @@
return
// Titles [Master]
if (!am_fledgling)
if (owner.current.gender == MALE)
if(owner.current.gender == MALE)
vamptitle = pick ("Count","Baron","Viscount","Prince","Duke","Tzar","Dreadlord","Lord","Master")
else
vamptitle = pick ("Countess","Baroness","Viscountess","Princess","Duchess","Tzarina","Dreadlady","Lady","Mistress")
@@ -130,18 +131,18 @@
/datum/antagonist/bloodsucker/proc/SelectReputation(am_fledgling = 0, forced=FALSE)
// Already have Reputation
if (!forced && vampreputation != null)
if(!forced && vampreputation != null)
return
// Reputations [Master]
if (!am_fledgling)
if(!am_fledgling)
vampreputation = 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")
if (owner.current.gender == MALE)
if (prob(10)) // Gender override
if(owner.current.gender == MALE)
if(prob(10)) // Gender override
vampreputation = pick("King of the Damned", "Blood King", "Emperor of Blades", "Sinlord", "God-King")
else
if (prob(10)) // Gender override
if(prob(10)) // Gender override
vampreputation = pick("Queen of the Damned", "Blood Queen", "Empress of Blades", "Sinlady", "God-Queen")
to_chat(owner, "<span class='announce'>You have earned a reputation! You are now known as <i>[ReturnFullName(TRUE)]</i>!</span>")
@@ -202,7 +203,7 @@
// Make Changes
H.physiology.brute_mod *= 0.8 // <-------------------- Start small, but burn mod increases based on rank!
H.physiology.cold_mod = 0
H.physiology.stun_mod *= 0.35
H.physiology.stun_mod *= 0.5
H.physiology.siemens_coeff *= 0.75 //base electrocution coefficient 1
//S.heatmod += 0.5 // Heat shouldn't affect. Only Fire.
//S.punchstunthreshold = 8 //damage at which punches from this race will stun 9
@@ -261,7 +262,7 @@
owner.hasSoul = TRUE
//owner.current.hellbound = FALSE
datum/antagonist/bloodsucker/proc/RankUp()
/datum/antagonist/bloodsucker/proc/RankUp()
set waitfor = FALSE
if(!owner || !owner.current)
return
@@ -272,21 +273,23 @@ datum/antagonist/bloodsucker/proc/RankUp()
else
to_chat(owner, "<EM><span class='notice'>You have grown more ancient! Sleep in a coffin that you have claimed to thicken your blood and become more powerful.</span></EM>")
if(vamplevel_unspent >= 2)
to_chat(owner, "<span class='announce'>Bloodsucker Tip: If you cannot find or steal a coffin to use, they can be built from wooden planks.</span><br>")
to_chat(owner, "<span class='announce'>Bloodsucker Tip: If you cannot find or steal a coffin to use, you can build one from wooden planks.</span><br>")
datum/antagonist/bloodsucker/proc/LevelUpPowers()
/datum/antagonist/bloodsucker/proc/LevelUpPowers()
for(var/datum/action/bloodsucker/power in powers)
power.level_current ++
datum/antagonist/bloodsucker/proc/SpendRank()
/datum/antagonist/bloodsucker/proc/SpendRank()
set waitfor = FALSE
if (vamplevel_unspent <= 0 || !owner || !owner.current || !owner.current.client)
if(vamplevel_unspent <= 0 || !owner || !owner.current || !owner.current.client || !isliving(owner.current))
return
/////////
// Powers
//TODO: Make this into a radial
var/mob/living/L = owner.current
level_bloodcost = maxBloodVolume * 0.2
//If the blood volume of the bloodsucker is lower than the cost to level up, return and inform the bloodsucker
//TODO: Make this into a radial, or perhaps a tgui next UI
// Purchase Power Prompt
var/list/options = list() // Taken from gasmask.dm, for Clown Masks.
var/list/options = list()
for(var/pickedpower in typesof(/datum/action/bloodsucker))
var/datum/action/bloodsucker/power = pickedpower
// If I don't own it, and I'm allowed to buy it.
@@ -295,7 +298,7 @@ datum/antagonist/bloodsucker/proc/SpendRank()
options["\[ Not Now \]"] = null
// Abort?
if(options.len > 1)
var/choice = input(owner.current, "You have the opportunity to grow more ancient. Select a power to advance your Rank.", "Your Blood Thickens...") in options
var/choice = input(owner.current, "You have the opportunity to grow more ancient at the cost of [level_bloodcost] units of blood. Select a power to advance your Rank.", "Your Blood Thickens...") in options
// Cheat-Safety: Can't keep opening/closing coffin to spam levels
if(vamplevel_unspent <= 0) // Already spent all your points, and tried opening/closing your coffin, pal.
return
@@ -305,10 +308,14 @@ datum/antagonist/bloodsucker/proc/SpendRank()
if(!choice || !options[choice] || (locate(options[choice]) in powers)) // ADDED: Check to see if you already have this power, due to window stacking.
to_chat(owner.current, "<span class='notice'>You prevent your blood from thickening just yet, but you may try again later.</span>")
return
if(L.blood_volume < level_bloodcost)
to_chat(owner.current, "<span class='warning'>You dont have enough blood to thicken your blood, you need [level_bloodcost - L.blood_volume] units more!</span>")
return
// Buy New Powers
var/datum/action/bloodsucker/P = options[choice]
AddBloodVolume(-level_bloodcost)
BuyPower(new P)
to_chat(owner.current, "<span class='notice'>You have learned [initial(P.name)]!</span>")
to_chat(owner.current, "<span class='notice'>You have used [level_bloodcost] units of blood and learned [initial(P.name)]!</span>")
else
to_chat(owner.current, "<span class='notice'>You grow more ancient by the night!</span>")
/////////
@@ -326,7 +333,7 @@ datum/antagonist/bloodsucker/proc/SpendRank()
// Vamp Stats
regenRate += 0.05 // Points of brute healed (starts at 0.3)
feedAmount += 2 // Increase how quickly I munch down vics (15)
maxBloodVolume += 50 // Increase my max blood (600)
maxBloodVolume += 100 // Increase my max blood (600)
/////////
vamplevel ++
vamplevel_unspent --
@@ -8,7 +8,7 @@
return SSticker.mode.make_vassal(C,owner)
/datum/antagonist/bloodsucker/proc/FreeAllVassals()
for (var/datum/antagonist/vassal/V in vassals)
for(var/datum/antagonist/vassal/V in vassals)
SSticker.mode.remove_vassal(V.owner)
/datum/antagonist/vassal
@@ -22,7 +22,7 @@
/datum/antagonist/vassal/can_be_owned(datum/mind/new_owner)
// If we weren't created by a bloodsucker, then we cannot be a vassal (assigned from antag panel)
if (!master)
if(!master)
return FALSE
return ..()
@@ -63,9 +63,9 @@
/datum/antagonist/vassal/on_removal()
SSticker.mode.vassals -= owner // Add if not already in here (and you might be, if you were picked at round start)
// Mindslave Remove
if (master && master.owner)
if(master && master.owner)
master.vassals -= src
if (owner.enslaved_to == master.owner.current)
if(owner.enslaved_to == master.owner.current)
owner.enslaved_to = null
// Master Pinpointer
owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer/vassal_edition)
@@ -60,6 +60,8 @@
breakout_time = 600
pryLidTimer = 400
resistance_flags = NONE
integrity_failure = 70
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/meatcoffin
name = "meat coffin"
@@ -75,7 +77,9 @@
resistance_flags = NONE
material_drop = /obj/item/reagent_containers/food/snacks/meat/slab
material_drop_amount = 3
integrity_failure = 40
armor = list("melee" = 70, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 100)
/obj/structure/closet/crate/coffin/metalcoffin
name = "metal coffin"
desc = "A big metal sardine can inside of another big metal sardine can, in space."
@@ -90,6 +94,8 @@
resistance_flags = NONE
material_drop = /obj/item/stack/sheet/metal
material_drop_amount = 5
integrity_failure = 60
armor = list("melee" = 40, "bullet" = 15, "laser" = 50, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 60)
//////////////////////////////////////////////
@@ -107,7 +107,7 @@
var/convert_progress = 3 // Resets on each new character to be added to the chair. Some effects should lower it...
var/disloyalty_confirm = FALSE // Command & Antags need to CONFIRM they are willing to lose their role (and will only do it if the Vassal'ing succeeds)
var/disloyalty_offered = FALSE // Has the popup been issued? Don't spam them.
var/convert_cost = 100
/obj/structure/bloodsucker/vassalrack/deconstruct(disassembled = TRUE)
new /obj/item/stack/sheet/metal(src.loc, 4)
@@ -116,10 +116,10 @@
/obj/structure/bloodsucker/vassalrack/examine(mob/user)
. = ..()
if((user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)) || isobserver(user))
if(isvamp(user) || isobserver(user))
. += {"<span class='cult'>This is the vassal rack, which allows you to thrall crewmembers into loyal minions in your service.</span>"}
. += {"<span class='cult'>You need to first secure the vassal rack by clicking on it while it is in your lair.</span>"}
. += {"<span class='cult'>Simply click and hold on a victim, and then drag their sprite on the vassal rack.</span>"}
. += {"<span class='cult'>Simply click and hold on a victim, and then drag their sprite on the vassal rack. Alt click on the vassal rack to unbuckle them.</span>"}
. += {"<span class='cult'>Make sure that the victim is handcuffed, or else they can simply run away or resist, as the process is not instant.</span>"}
. += {"<span class='cult'>To convert the victim, simply click on the vassal rack itself. Sharp weapons work faster than other tools.</span>"}
/* if(user.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
@@ -177,7 +177,7 @@
M.pixel_y = -2 //M.get_standard_pixel_y_offset(120)//180)
update_icon()
// Torture Stuff
convert_progress = 2 // Goes down unless you start over.
convert_progress = 4 // Goes down unless you start over.
disloyalty_confirm = FALSE // New guy gets the chance to say NO if he's special.
disloyalty_offered = FALSE // Prevents spamming torture window.
@@ -190,7 +190,7 @@
else
M.visible_message("<span class='danger'>[user] tries to pull [M] rack!</span>",\
"<span class='danger'>[user] attempts to release you from the rack!</span>") // For sound if not seen --> "<span class='italics'>You hear a squishy wet noise.</span>")
if(!do_mob(user, M, 100))
if(!do_mob(user, M, 200))
return
// Did the time. Now try to do it.
..()
@@ -248,7 +248,7 @@
// Bloodsucker Owner! Let the boy go.
if(C.mind)
var/datum/antagonist/vassal/vassaldatum = C.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
if (istype(vassaldatum) && vassaldatum.master == bloodsuckerdatum || C.stat >= DEAD)
if(istype(vassaldatum) && vassaldatum.master == bloodsuckerdatum || C.stat >= DEAD)
unbuckle_mob(C)
useLock = FALSE // Failsafe
return
@@ -256,7 +256,9 @@
torture_victim(user, C)
/obj/structure/bloodsucker/vassalrack/proc/torture_victim(mob/living/user, mob/living/target)
var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
// Check Bloodmob/living/M, force = FALSE, check_loc = TRUE
var/convert_cost = 200 + 200 * bloodsuckerdatum.vassals
if(user.blood_volume < convert_cost + 5)
to_chat(user, "<span class='notice'>You don't have enough blood to initiate the Dark Communion with [target].</span>")
return
@@ -275,7 +277,7 @@
// All done!
if(convert_progress <= 0)
// FAIL: Can't be Vassal
if(!SSticker.mode.can_make_vassal(target, user, display_warning=FALSE) || HAS_TRAIT(target, TRAIT_MINDSHIELD)) // If I'm an unconvertable Antag ONLY
if(!SSticker.mode.can_make_vassal(target, user, display_warning = FALSE) || HAS_TRAIT(target, TRAIT_MINDSHIELD)) // If I'm an unconvertable Antag ONLY
to_chat(user, "<span class='danger'>[target] doesn't respond to your persuasion. It doesn't appear they can be converted to follow you, they either have a mindshield or their external loyalties are too difficult for you to break.<i>\[ALT+click to release\]</span>")
convert_progress ++ // Pop it back up some. Avoids wasting Blood on a lost cause.
// SUCCESS: All done!
@@ -301,10 +303,9 @@
return
// Check: Blood
if(user.blood_volume < convert_cost)
to_chat(user, "<span class='notice'>You don't have enough blood to initiate the Dark Communion with [target].</span>")
to_chat(user, "<span class='notice'>You don't have enough blood to initiate the Dark Communion with [target], you need [convert_cost - user.blood_volume] units more!</span>")
useLock = FALSE
return
var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
bloodsuckerdatum.AddBloodVolume(-convert_cost)
target.add_mob_blood(user)
user.visible_message("<span class='notice'>[user] marks a bloody smear on [target]'s forehead and puts a wrist up to [target.p_their()] mouth!</span>", \
@@ -5,7 +5,7 @@
desc = "Snap restraints with ease, or deal terrible damage with your bare hands."
button_icon_state = "power_strength"
bloodcost = 10
cooldown = 130
cooldown = 90
target_range = 1
power_activates_immediately = TRUE
message_Trigger = ""//"Whom will you subvert to your will?"
@@ -66,16 +66,13 @@
// Target Type: Mob
if(isliving(target))
var/mob/living/carbon/user_C = user
var/hitStrength = user_C.dna.species.punchdamagehigh * 1.3 + 5
var/hitStrength = user_C.dna.species.punchdamagehigh * 1.4 + 15
// Knockdown!
var/powerlevel = min(5, 1 + level_current)
if(rand(5 + powerlevel) >= 5)
target.visible_message("<span class='danger'>[user] lands a vicious punch, sending [target] away!</span>", \
"<span class='userdanger'>[user] has landed a horrifying punch on you, sending you flying!!</span>", null, COMBAT_MESSAGE_RANGE)
target.DefaultCombatKnockdown(min(5, rand(10, 10 * powerlevel)) )
// Chance of KO
if(rand(6 + powerlevel) >= 6 && target.stat <= UNCONSCIOUS)
target.Unconscious(40)
target.DefaultCombatKnockdownKnockdown(min(5, rand(10, 10 * powerlevel)) )
// Attack!
playsound(get_turf(target), 'sound/weapons/punch4.ogg', 60, 1, -1)
user.do_attack_animation(target, ATTACK_EFFECT_SMASH)
@@ -31,12 +31,11 @@
if(was_running)
user.toggle_move_intent()
ADD_TRAIT(user, TRAIT_NORUNNING, "cloak of darkness")
while(bloodsuckerdatum && ContinueActive(user) || user.m_intent == MOVE_INTENT_RUN)
while(bloodsuckerdatum && ContinueActive(user))
// Pay Blood Toll (if awake)
owner.alpha = max(20, owner.alpha - min(75, 10 + 5 * level_current))
bloodsuckerdatum.AddBloodVolume(-0.2)
sleep(5) // Check every few ticks that we haven't disabled this power
// Return to Running (if you were before)
/datum/action/bloodsucker/cloak/ContinueActive(mob/living/user, mob/living/target)
if (!..())
@@ -22,12 +22,12 @@
return
// Wearing mask
var/mob/living/L = owner
if (L.is_mouth_covered())
if (display_error)
if(L.is_mouth_covered())
if(display_error)
to_chat(owner, "<span class='warning'>You cannot feed with your mouth covered! Remove your mask.</span>")
return FALSE
// Find my Target!
if (!FindMyTarget(display_error)) // Sets feed_target within after Validating
if(!FindMyTarget(display_error)) // Sets feed_target within after Validating
return FALSE
// Not in correct state
// DONE!
@@ -35,36 +35,36 @@
/datum/action/bloodsucker/feed/proc/ValidateTarget(mob/living/target, display_error) // Called twice: validating a subtle victim, or validating your grapple victim.
// Bloodsuckers + Animals MUST be grabbed aggressively!
if (!owner.pulling || target == owner.pulling && owner.grab_state < GRAB_AGGRESSIVE)
if(!owner.pulling || target == owner.pulling && owner.grab_state < GRAB_AGGRESSIVE)
// NOTE: It's OKAY that we are checking if(!target) below, AFTER animals here. We want passive check vs animal to warn you first, THEN the standard warning.
// Animals:
if (isliving(target) && !iscarbon(target))
if (display_error)
if(isliving(target) && !iscarbon(target))
if(display_error)
to_chat(owner, "<span class='warning'>Lesser beings require a tighter grip.</span>")
return FALSE
// Bloodsuckers:
else if (iscarbon(target) && target.mind && target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
if (display_error)
else if(iscarbon(target) && target.mind && target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
if(display_error)
to_chat(owner, "<span class='warning'>Other Bloodsuckers will not fall for your subtle approach.</span>")
return FALSE
// Must have Target
if (!target) // || !ismob(target)
if (display_error)
if(!target) // || !ismob(target)
if(display_error)
to_chat(owner, "<span class='warning'>You must be next to or grabbing a victim to feed from them.</span>")
return FALSE
// Not even living!
if (!isliving(target) || issilicon(target))
if (display_error)
if(!isliving(target) || issilicon(target))
if(display_error)
to_chat(owner, "<span class='warning'>You may only feed from living beings.</span>")
return FALSE
if (target.blood_volume <= 0)
if (display_error)
if(target.blood_volume <= 0)
if(display_error)
to_chat(owner, "<span class='warning'>Your victim has no blood to take.</span>")
return FALSE
if (ishuman(target))
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(NOBLOOD in H.dna.species.species_traits)// || owner.get_blood_id() != target.get_blood_id())
if (display_error)
if(display_error)
to_chat(owner, "<span class='warning'>Your victim's blood is not suitable for you to take.</span>")
return FALSE
return TRUE
@@ -75,9 +75,9 @@
feed_target = null
target_grappled = FALSE
// If you are pulling a mob, that's your target. If you don't like it, then release them.
if (owner.pulling && ismob(owner.pulling))
if(owner.pulling && ismob(owner.pulling))
// Check grapple target Valid
if (!ValidateTarget(owner.pulling, display_error)) // Grabbed targets display error.
if(!ValidateTarget(owner.pulling, display_error)) // Grabbed targets display error.
return FALSE
target_grappled = TRUE
feed_target = owner.pulling
@@ -86,11 +86,11 @@
var/list/mob/living/seen_targets = view(1, owner)
var/list/mob/living/seen_mobs = list()
for(var/mob/living/M in seen_targets)
if (isliving(M) && M != owner)
if(isliving(M) && M != owner)
seen_mobs += M
// None Seen!
if (seen_mobs.len == 0)
if (display_error)
if(seen_mobs.len == 0)
if(display_error)
to_chat(owner, "<span class='warning'>You must be next to or grabbing a victim to feed from them.</span>")
return FALSE
// Check Valids...
@@ -98,19 +98,19 @@
var/list/targets_dead = list()
for(var/mob/living/M in seen_mobs)
// Check adjecent Valid target
if (M != owner && ValidateTarget(M, display_error = FALSE)) // Do NOT display errors. We'll be doing this again in CheckCanUse(), which will rule out grabbed targets.
if(M != owner && ValidateTarget(M, display_error = FALSE)) // Do NOT display errors. We'll be doing this again in CheckCanUse(), which will rule out grabbed targets.
// Prioritize living, but remember dead as backup
if (M.stat < DEAD)
if(M.stat < DEAD)
targets_valid += M
else
targets_dead += M
// No Living? Try dead.
if (targets_valid.len == 0 && targets_dead.len > 0)
if(targets_valid.len == 0 && targets_dead.len > 0)
targets_valid = targets_dead
// No Targets
if (targets_valid.len == 0)
if(targets_valid.len == 0)
// Did I see targets? Then display at least one error
if (seen_mobs.len > 1)
if(seen_mobs.len > 1)
if (display_error)
to_chat(owner, "<span class='warning'>None of these are valid targets to feed from subtly.</span>")
else
@@ -136,28 +136,28 @@
// Initial Wait
var/feed_time = (amSilent ? 45 : 25) - (2.5 * level_current)
feed_time = max(15, feed_time)
if (amSilent)
if(amSilent)
to_chat(user, "<span class='notice'>You lean quietly toward [target] and secretly draw out your fangs...</span>")
else
to_chat(user, "<span class='warning'>You pull [target] close to you and draw out your fangs...</span>")
if (!do_mob(user, target, feed_time,0,1,extra_checks=CALLBACK(src, .proc/ContinueActive, user, target)))//sleep(10)
if(!do_mob(user, target, feed_time,0,1,extra_checks=CALLBACK(src, .proc/ContinueActive, user, target)))//sleep(10)
to_chat(user, "<span class='warning'>Your feeding was interrupted.</span>")
//DeactivatePower(user,target)
return
// Put target to Sleep (Bloodsuckers are immune to their own bite's sleep effect)
if (!amSilent)
if(!amSilent)
ApplyVictimEffects(target) // Sleep, paralysis, immobile, unconscious, and mute
if(target.stat <= UNCONSCIOUS)
sleep(1)
// Wait, then Cancel if Invalid
if (!ContinueActive(user,target)) // Cancel. They're gone.
if(!ContinueActive(user,target)) // Cancel. They're gone.
//DeactivatePower(user,target)
return
// Pull Target Close
if (!target.density) // Pull target to you if they don't take up space.
if(!target.density) // Pull target to you if they don't take up space.
target.Move(user.loc)
// Broadcast Message
if (amSilent)
if(amSilent)
//if (!iscarbon(target))
// user.visible_message("<span class='notice'>[user] shifts [target] closer to [user.p_their()] mouth.</span>", \
// "<span class='notice'>You secretly slip your fangs into [target]'s flesh.</span>", \
@@ -173,7 +173,7 @@
if(M != owner && M != target && iscarbon(M) && M.mind && !M.has_unlimited_silicon_privilege && !M.eye_blind && !M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
was_unnoticed = FALSE
break
if (was_unnoticed)
if(was_unnoticed)
to_chat(user, "<span class='notice'>You think no one saw you...</span>")
else
to_chat(user, "<span class='warning'>Someone may have noticed...</span>")
@@ -197,16 +197,16 @@
// FEEEEEEEEED!!! //
bloodsuckerdatum.poweron_feed = TRUE
while (bloodsuckerdatum && target && active)
while(bloodsuckerdatum && target && active)
//user.mobility_flags &= ~MOBILITY_MOVE // user.canmove = 0 // Prevents spilling blood accidentally.
// Abort? A bloody mistake.
if (!do_mob(user, target, 20, 0, 0, extra_checks=CALLBACK(src, .proc/ContinueActive, user, target)))
if(!do_mob(user, target, 20, 0, 0, extra_checks=CALLBACK(src, .proc/ContinueActive, user, target)))
// May have disabled Feed during do_mob
if (!active || !ContinueActive(user, target))
if(!active || !ContinueActive(user, target))
break
if (amSilent)
if(amSilent)
to_chat(user, "<span class='warning'>Your feeding has been interrupted...but [target.p_they()] didn't seem to notice you.<span>")
else
to_chat(user, "<span class='warning'>Your feeding has been interrupted!</span>")
@@ -214,11 +214,11 @@
"<span class='userdanger'>Your teeth are ripped from [target]'s throat. [target.p_their(TRUE)] blood sprays everywhere!</span>")
// Deal Damage to Target (should have been more careful!)
if (iscarbon(target))
if(iscarbon(target))
var/mob/living/carbon/C = target
C.bleed(15)
playsound(get_turf(target), 'sound/effects/splat.ogg', 40, 1)
if (ishuman(target))
if(ishuman(target))
var/mob/living/carbon/human/H = target
H.bleed_rate += 5
target.add_splatter_floor(get_turf(target))
@@ -228,7 +228,7 @@
target.emote("scream")
// Killed Target?
if (was_alive)
if(was_alive)
CheckKilledTarget(user,target)
return
@@ -237,40 +237,40 @@
// Handle Feeding! User & Victim Effects (per tick)
bloodsuckerdatum.HandleFeeding(target, blood_take_mult)
amount_taken += amSilent ? 0.3 : 1
if (!amSilent)
if(!amSilent)
ApplyVictimEffects(target) // Sleep, paralysis, immobile, unconscious, and mute
if (amount_taken > 5 && target.stat < DEAD && ishuman(target))
if(amount_taken > 5 && target.stat < DEAD && ishuman(target))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood) // GOOD // in bloodsucker_life.dm
///////////////////////////////////////////////////////////
// Not Human?
if (!ishuman(target))
if(!ishuman(target))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood_bad) // BAD // in bloodsucker_life.dm
if (!warning_target_inhuman)
if(!warning_target_inhuman)
to_chat(user, "<span class='notice'>You recoil at the taste of a lesser lifeform.</span>")
warning_target_inhuman = TRUE
// Dead Blood?
if (target.stat >= DEAD)
if (ishuman(target))
if(target.stat >= DEAD)
if(ishuman(target))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood_dead) // BAD // in bloodsucker_life.dm
if (!warning_target_dead)
if(!warning_target_dead)
to_chat(user, "<span class='notice'>Your victim is dead. [target.p_their(TRUE)] blood barely nourishes you.</span>")
warning_target_dead = TRUE
// Full?
if (!warning_full && user.blood_volume >= bloodsuckerdatum.maxBloodVolume)
if(!warning_full && user.blood_volume >= bloodsuckerdatum.maxBloodVolume)
to_chat(user, "<span class='notice'>You are full. Further blood will be wasted.</span>")
warning_full = TRUE
// Blood Remaining? (Carbons/Humans only)
if (iscarbon(target) && !target.AmBloodsucker(1))
if (target.blood_volume <= BLOOD_VOLUME_BAD && warning_target_bloodvol > BLOOD_VOLUME_BAD)
if(iscarbon(target) && !target.AmBloodsucker(1))
if(target.blood_volume <= BLOOD_VOLUME_BAD && warning_target_bloodvol > BLOOD_VOLUME_BAD)
to_chat(user, "<span class='warning'>Your victim's blood volume is fatally low!</span>")
else if (target.blood_volume <= BLOOD_VOLUME_OKAY && warning_target_bloodvol > BLOOD_VOLUME_OKAY)
else if(target.blood_volume <= BLOOD_VOLUME_OKAY && warning_target_bloodvol > BLOOD_VOLUME_OKAY)
to_chat(user, "<span class='warning'>Your victim's blood volume is dangerously low.</span>")
else if (target.blood_volume <= BLOOD_VOLUME_SAFE && warning_target_bloodvol > BLOOD_VOLUME_SAFE)
else if(target.blood_volume <= BLOOD_VOLUME_SAFE && warning_target_bloodvol > BLOOD_VOLUME_SAFE)
to_chat(user, "<span class='notice'>Your victim's blood is at an unsafe level.</span>")
warning_target_bloodvol = target.blood_volume // If we had a warning to give, it's been given by now.
// Done?
if (target.blood_volume <= 0)
if(target.blood_volume <= 0)
to_chat(user, "<span class='notice'>You have bled your victim dry.</span>")
break
@@ -279,7 +279,7 @@
// DONE!
//DeactivatePower(user,target)
if (amSilent)
if(amSilent)
to_chat(user, "<span class='notice'>You slowly release [target]'s wrist." + (target.stat == 0 ? " [target.p_their(TRUE)] face lacks expression, like you've already been forgotten.</span>" : ""))
else
user.visible_message("<span class='warning'>[user] unclenches their teeth from [target]'s neck.</span>", \
@@ -289,13 +289,13 @@
log_combat(owner, target, "fed on blood", addition="(and took [amount_taken] blood)")
// Killed Target?
if (was_alive)
if(was_alive)
CheckKilledTarget(user,target)
/datum/action/bloodsucker/feed/proc/CheckKilledTarget(mob/living/user, mob/living/target)
// Bad Vampire. You shouldn't do that.
if (target && target.stat >= DEAD && ishuman(target))
if(target && target.stat >= DEAD && ishuman(target))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankkilled", /datum/mood_event/drankkilled) // BAD // in bloodsucker_life.dm
/datum/action/bloodsucker/feed/ContinueActive(mob/living/user, mob/living/target)
@@ -304,18 +304,18 @@
/datum/action/bloodsucker/feed/proc/ApplyVictimEffects(mob/living/target)
// Bloodsuckers not affected by "the Kiss" of another vampire
if (!target.mind || !target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
if(!target.mind || !target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
target.Unconscious(50,0)
target.DefaultCombatKnockdown(40 + 5 * level_current,1)
// NOTE: THis is based on level of power!
if (ishuman(target))
if(ishuman(target))
target.adjustStaminaLoss(5, forced = TRUE)// Base Stamina Damage
/datum/action/bloodsucker/feed/DeactivatePower(mob/living/user = owner, mob/living/target)
..() // activate = FALSE
var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
// No longer Feeding
if (bloodsuckerdatum)
if(bloodsuckerdatum)
bloodsuckerdatum.poweron_feed = FALSE
feed_target = null
// My mouth is no longer full
@@ -6,7 +6,7 @@
name = "Fortitude"//"Cellular Emporium"
desc = "Withstand egregious physical wounds and walk away from attacks that would stun, pierce, and dismember lesser beings. You cannot run while active."
button_icon_state = "power_fortitude"
bloodcost = 5
bloodcost = 30
cooldown = 80
bloodsucker_can_buy = TRUE
amToggle = TRUE
@@ -23,7 +23,7 @@
ADD_TRAIT(user, TRAIT_NODISMEMBER, "fortitude")
ADD_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude")
ADD_TRAIT(user, TRAIT_NORUNNING, "fortitude")
if (ishuman(owner))
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
this_resist = max(0.3, 0.7 - level_current * 0.1)
H.physiology.brute_mod *= this_resist//0.5
@@ -34,7 +34,7 @@
user.toggle_move_intent()
while(bloodsuckerdatum && ContinueActive(user) || user.m_intent == MOVE_INTENT_RUN)
// Pay Blood Toll (if awake)
if (user.stat == CONSCIOUS)
if(user.stat == CONSCIOUS)
bloodsuckerdatum.AddBloodVolume(-0.5) // Used to be 0.3 blood per 2 seconds, but we're making it more expensive to keep on.
sleep(20) // Check every few ticks that we haven't disabled this power
// Return to Running (if you were before)
@@ -48,7 +48,7 @@
REMOVE_TRAIT(user, TRAIT_NODISMEMBER, "fortitude")
REMOVE_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude")
REMOVE_TRAIT(user, TRAIT_NORUNNING, "fortitude")
if (ishuman(owner))
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
H.physiology.brute_mod /= this_resist//0.5
H.physiology.burn_mod /= this_resist//0.5
@@ -7,7 +7,7 @@
background_icon_state_on = "vamp_power_off_oneshot" // Even though this never goes off.
background_icon_state_off = "vamp_power_off_oneshot"
bloodcost = 25
bloodcost = 100
cooldown = 99999 // It'll never come back.
amToggle = FALSE
amSingleUse = TRUE
@@ -23,11 +23,16 @@
return
// Have No Lair (NOTE: You only got this power if you had a lair, so this means it's destroyed)
var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
if (!istype(bloodsuckerdatum) || !bloodsuckerdatum.coffin)
if (display_error)
if(!istype(bloodsuckerdatum) || !bloodsuckerdatum.coffin)
if(display_error)
to_chat(owner, "<span class='warning'>Your coffin has been destroyed!</span>")
return FALSE
return TRUE
/datum/action/bloodsucker/gohome/proc/flicker_lights(var/flicker_range, var/beat_volume)
for(var/obj/machinery/light/L in view(flicker_range, get_turf(owner)))
playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', beat_volume, 1)
/datum/action/bloodsucker/gohome/ActivatePower()
var/mob/living/carbon/user = owner
@@ -35,34 +40,31 @@
// IMPORTANT: Check for lair at every step! It might get destroyed.
to_chat(user, "<span class='notice'>You focus on separating your consciousness from your physical form...</span>")
// STEP ONE: Flicker Lights
for(var/obj/machinery/light/L in view(3, get_turf(owner))) // /obj/machinery/light/proc/flicker(var/amount = rand(10, 20))
L.flicker(5)
playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', 20, 1)
flicker_lights(3, 20)
sleep(50)
for(var/obj/machinery/light/L in view(3, get_turf(owner))) // /obj/machinery/light/proc/flicker(var/amount = rand(10, 20))
L.flicker(5)
playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', 40, 1)
flicker_lights(4, 40)
sleep(50)
for(var/obj/machinery/light/L in view(6, get_turf(owner))) // /obj/machinery/light/proc/flicker(var/amount = rand(10, 20))
flicker_lights(4, 60)
for(var/obj/machinery/light/L in view(6, get_turf(owner)))
L.flicker(5)
playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', 60, 1)
// ( STEP TWO: Lights OFF? )
// CHECK: Still have Coffin?
if (!istype(bloodsuckerdatum) || !bloodsuckerdatum.coffin)
if(!istype(bloodsuckerdatum) || !bloodsuckerdatum.coffin)
to_chat(user, "<span class='warning'>Your coffin has been destroyed! You no longer have a destination.</span>")
return FALSE
if (!owner)
if(!owner)
return
// SEEN?: (effects ONLY if there are witnesses! Otherwise you just POOF)
// NOTE: Stolen directly from statue.dm, thanks guys!
var/am_seen = FALSE // Do Effects (seen by anyone)
var/drop_item = FALSE // Drop Stuff (seen by non-vamp)
if (isturf(owner.loc)) // Only check if I'm not in a Locker or something.
if(isturf(owner.loc)) // Only check if I'm not in a Locker or something.
// A) Check for Darkness (we can just leave)
var/turf/T = get_turf(user)
if(T && T.lighting_object && T.get_lumcount()>= 0.1)
// B) Check for Viewers
for(var/mob/living/M in viewers(owner))
for(var/mob/living/M in viewers(get_turf(owner)))
if(M != owner && isliving(M) && M.mind && !M.has_unlimited_silicon_privilege && !M.eye_blind) // M.client <--- add this in after testing!
am_seen = TRUE
if (!M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
@@ -76,7 +78,7 @@
var/obj/O = user.legcuffed
user.dropItemToGround(O)
// SEEN!
if (drop_item)
if(drop_item)
// DROP: Clothes, held items, and cuffs etc
// NOTE: Taken from unequip_everything() in inventory.dm. We need to
// *force* all items to drop, so we had to just gut the code out of it.
@@ -86,30 +88,28 @@
user.dropItemToGround(I,TRUE)
for(var/obj/item/I in owner.held_items) // drop_all_held_items()
user.dropItemToGround(I, TRUE)
if (am_seen)
if(am_seen)
// POOF EFFECTS
playsound(get_turf(owner), 'sound/magic/summon_karp.ogg', 60, 1)
var/datum/effect_system/steam_spread/puff = new /datum/effect_system/steam_spread/()
puff.effect_type = /obj/effect/particle_effect/smoke/vampsmoke
puff.set_up(3, 0, get_turf(owner))
puff.start()
//STEP FIVE: Create animal at prev location
var/mob/living/simple_animal/SA = pick(/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse, /mob/living/simple_animal/hostile/retaliate/bat) //prob(300) /mob/living/simple_animal/mouse,
new SA (owner.loc)
// TELEPORT: Move to Coffin & Close it!
do_teleport(owner, bloodsuckerdatum.coffin, no_effects = TRUE, forced = TRUE, channel = TELEPORT_CHANNEL_QUANTUM) // in teleport.dm?
// SLEEP
do_teleport(owner, bloodsuckerdatum.coffin, no_effects = TRUE, forced = TRUE, channel = TELEPORT_CHANNEL_QUANTUM)
user.set_resting(TRUE, TRUE, FALSE)
//user.Unconscious(30,0)
user.Stun(30, TRUE)
user.Stun(30,1)
// CLOSE LID: If fail, force me in.
if (!bloodsuckerdatum.coffin.close(owner))
if(!bloodsuckerdatum.coffin.close(owner))
bloodsuckerdatum.coffin.insert(owner) // Puts me inside.
// The following was taken from close() proc in closets.dm
// (but we had to do it this way because there is no way to force entry)
playsound(bloodsuckerdatum.coffin.loc, bloodsuckerdatum.coffin.close_sound, 15, 1, -3)
bloodsuckerdatum.coffin.opened = FALSE
bloodsuckerdatum.coffin.density = TRUE
bloodsuckerdatum.coffin.update_icon()
// Lock Coffin
bloodsuckerdatum.coffin.LockMe(owner)
// ( STEP FIVE: Create animal at prev location? )
//var/mob/living/simple_animal/SA = /mob/living/simple_animal/hostile/retaliate/bat // pick(/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse, /mob/living/simple_animal/hostile/retaliate/bat) //prob(300) /mob/living/simple_animal/mouse,
//new SA (owner.loc)
@@ -8,7 +8,7 @@
desc = "Dash somewhere with supernatural speed. Those nearby may be knocked away, stunned, or left empty-handed."
button_icon_state = "power_speed"
bloodcost = 6
cooldown = 30
cooldown = 50
target_range = 15
power_activates_immediately = TRUE
message_Trigger = ""//"Whom will you subvert to your will?"
@@ -6,7 +6,7 @@
desc = "Spring at your target and aggressively grapple them without warning. Attacks from concealment or the rear may even knock them down."
button_icon_state = "power_lunge"
bloodcost = 10
cooldown = 100
cooldown = 120
target_range = 3
power_activates_immediately = TRUE
message_Trigger = ""//"Whom will you subvert to your will?"
@@ -28,7 +28,7 @@
return TRUE
/datum/action/bloodsucker/targeted/lunge/CheckValidTarget(atom/A)
return isliving(A)
return iscarbon(A)
/datum/action/bloodsucker/targeted/lunge/CheckCanTarget(atom/A, display_error)
// Check: Self
@@ -10,7 +10,7 @@
desc = "Dominate the mind of a mortal who can see your eyes."
button_icon_state = "power_mez"
bloodcost = 30
cooldown = 200
cooldown = 300
target_range = 1
power_activates_immediately = FALSE
message_Trigger = "Whom will you subvert to your will?"
@@ -6,7 +6,7 @@
button_icon_state = "power_tres"
bloodcost = 10
cooldown = 60
cooldown = 80
amToggle = FALSE
//target_range = 2
+10 -4
View File
@@ -108,11 +108,17 @@
var/win = TRUE
var/objective_count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
else
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text]"
objective_count++
if(win)
parts += "<span class='greentext'>The blood brothers were successful!</span>"
@@ -54,8 +54,10 @@
var/honorific
if(owner.current.gender == FEMALE)
honorific = "Ms."
else
else if(owner.current.gender == MALE)
honorific = "Mr."
else
honorific = "Mx."
if(GLOB.possible_changeling_IDs.len)
changelingID = pick(GLOB.possible_changeling_IDs)
GLOB.possible_changeling_IDs -= changelingID
@@ -552,11 +554,17 @@
if(objectives.len)
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</b></span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
changelingwin = FALSE
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
changelingwin = 0
parts += "<B>Objective #[count]</B>: [objective.explanation_text]"
count++
if(changelingwin)
@@ -0,0 +1,19 @@
/datum/antagonist/collector
name = "Contraband Collector"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = FALSE
blacklisted_quirks = list() // no blacklist, these guys are harmless
/datum/antagonist/collector/proc/forge_objectives()
var/datum/objective/hoard/collector/O = new
O.owner = owner
O.find_target()
objectives += O
/datum/antagonist/collector/on_gain()
forge_objectives()
. = ..()
/datum/antagonist/collector/greet()
to_chat(owner, "<B>You are a contraband collector!</B>")
owner.announce_objectives()
+10 -4
View File
@@ -300,7 +300,7 @@
if(ishuman(cultist))
var/mob/living/carbon/human/H = cultist
H.eye_color = "f00"
H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
H.dna?.update_ui_block(DNA_EYE_COLOR_BLOCK)
ADD_TRAIT(H, TRAIT_CULT_EYES, "valid_cultist")
H.update_body()
@@ -425,10 +425,16 @@
parts += "<b>The cultists' objectives were:</b>"
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
parts += "<B>Objective #[count]</B>: [objective.explanation_text]"
count++
if(members.len)
+1 -1
View File
@@ -116,7 +116,7 @@ This file contains the cult dagger and rune list code
if(user.blood_volume)
user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
var/scribe_mod = initial(rune_to_scribe.scribe_delay)
if(istype(get_turf(user), /turf/open/floor/engine/cult))
if(istype(get_turf(user), /turf/open/floor/engine/cult) && !(ispath(rune_to_scribe, /obj/effect/rune/narsie)))
scribe_mod *= 0.5
if(!do_after(user, scribe_mod, target = get_turf(user)))
for(var/V in shields)
+1 -1
View File
@@ -885,7 +885,7 @@ structure_check() searches for nearby cultist structures required for the invoca
if(new_human)
new_human.visible_message("<span class='warning'>[new_human] suddenly dissolves into bones and ashes.</span>", \
"<span class='cultlarge'>Your link to the world fades. Your form breaks apart.</span>")
for(var/obj/I in new_human)
for(var/obj/item/I in new_human)
new_human.dropItemToGround(I, TRUE)
new_human.dust()
else if(choice == "Ascend as a Dark Spirit")
@@ -44,11 +44,17 @@
var/objectives_text = ""
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
else
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
result += "<B>Objective #[count]</B>: [objective.explanation_text]"
count++
result += objectives_text
+9 -16
View File
@@ -63,10 +63,10 @@
possible_targets.Cut(index,index+1)
if(is_bad_guy ^ helping_station) //kill (good-ninja + bad-guy or bad-ninja + good-guy)
var/datum/objective/assassinate/O = new /datum/objective/assassinate()
var/datum/objective/assassinate/once/O = new /datum/objective/assassinate()
O.owner = owner
O.target = M
O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]."
O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]. You may let [M.p_they()] live, if they come back from death."
objectives += O
else //protect
var/datum/objective/protect/O = new /datum/objective/protect()
@@ -74,23 +74,16 @@
O.target = M
O.explanation_text = "Protect \the [M.current.real_name], the [M.assigned_role], from harm."
objectives += O
if(4) //debrain/capture
if(!possible_targets.len) continue
var/selected = rand(1,possible_targets.len)
var/datum/mind/M = possible_targets[selected]
var/is_bad_guy = possible_targets[M]
possible_targets.Cut(selected,selected+1)
if(is_bad_guy ^ helping_station) //debrain (good-ninja + bad-guy or bad-ninja + good-guy)
var/datum/objective/debrain/O = new /datum/objective/debrain()
if(4) //flavor
if(helping_station)
var/datum/objective/flavor/ninja_helping/O = new /datum/objective/flavor/ninja_helping
O.owner = owner
O.target = M
O.explanation_text = "Steal the brain of [M.current.real_name]."
O.forge_objective()
objectives += O
else //capture
var/datum/objective/capture/O = new /datum/objective/capture()
else
var/datum/objective/flavor/ninja_syndie/O = new /datum/objective/flavor/ninja_helping
O.owner = owner
O.gen_amount_goal()
O.forge_objective()
objectives += O
else
break
+2 -2
View File
@@ -7,10 +7,10 @@
/datum/antagonist/nukeop/clownop/on_gain()
. = ..()
ADD_TRAIT(owner, TRAIT_CLOWN_MENTALITY, NUKEOP_ANTAGONIST)
ADD_TRAIT(owner, TRAIT_CLOWN_MENTALITY, CLOWNOP_TRAIT)
/datum/antagonist/nukeop/clownop/on_removal()
REMOVE_TRAIT(owner, TRAIT_CLOWN_MENTALITY, NUKEOP_ANTAGONIST)
REMOVE_TRAIT(owner, TRAIT_CLOWN_MENTALITY, CLOWNOP_TRAIT)
return ..()
/datum/antagonist/nukeop/leader/clownop
@@ -2,6 +2,7 @@
name = "Survivalist"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
blacklisted_quirks = list(/datum/quirk/nonviolent) // mutes are allowed
var/greet_message = ""
/datum/antagonist/survivalist/proc/forge_objectives()
@@ -19,20 +20,8 @@
owner.announce_objectives()
/datum/antagonist/survivalist/guns
greet_message = "Your own safety matters above all else, and the only way to ensure your safety is to stockpile weapons! Grab as many guns as possible, by any means necessary. Kill anyone who gets in your way."
/datum/antagonist/survivalist/guns/forge_objectives()
var/datum/objective/steal_five_of_type/summon_guns/guns = new
guns.owner = owner
objectives += guns
..()
greet_message = "Your own safety matters above all else, and the only way to ensure your safety is to stockpile weapons! Grab as many guns as possible, and don't let anyone take them!"
/datum/antagonist/survivalist/magic
name = "Amateur Magician"
greet_message = "Grow your newfound talent! Grab as many magical artefacts as possible, by any means necessary. Kill anyone who gets in your way."
/datum/antagonist/survivalist/magic/forge_objectives()
var/datum/objective/steal_five_of_type/summon_magic/magic = new
magic.owner = owner
objectives += magic
..()
greet_message = "This magic stuff is... so powerful. You want more. More! They want your power. They can't have it! Don't let them have it!"
@@ -77,19 +77,23 @@
var/is_hijacker = FALSE
var/datum/game_mode/dynamic/mode
var/is_dynamic = FALSE
var/hijack_prob = 0
if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode
is_dynamic = TRUE
if(mode.storyteller.flags & NO_ASSASSIN)
is_hijacker = FALSE
if(mode.threat >= CONFIG_GET(number/dynamic_hijack_cost))
hijack_prob = CLAMP(mode.threat_level-50,0,20)
if(GLOB.joined_player_list.len>=GLOB.dynamic_high_pop_limit)
is_hijacker = (prob(10) && mode.threat_level > CONFIG_GET(number/dynamic_hijack_high_population_requirement))
is_hijacker = (prob(hijack_prob) && mode.threat_level > CONFIG_GET(number/dynamic_hijack_high_population_requirement))
else
var/indice_pop = min(10,round(GLOB.joined_player_list.len/mode.pop_per_requirement)+1)
is_hijacker = (prob(10) && (mode.threat_level >= CONFIG_GET(number_list/dynamic_hijack_requirements)[indice_pop]))
is_hijacker = (prob(hijack_prob) && (mode.threat_level >= CONFIG_GET(number_list/dynamic_hijack_requirements)[indice_pop]))
if(mode.storyteller.flags & NO_ASSASSIN)
is_hijacker = FALSE
else if (GLOB.joined_player_list.len >= 30) // Less murderboning on lowpop thanks
hijack_prob = 10
is_hijacker = prob(10)
var/martyr_chance = prob(20)
var/martyr_chance = prob(hijack_prob*2)
var/objective_count = is_hijacker //Hijacking counts towards number of objectives
if(!SSticker.mode.exchange_blue && SSticker.mode.traitors.len >= 8) //Set up an exchange if there are enough traitors
if(!SSticker.mode.exchange_red)
@@ -170,7 +174,7 @@
if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode
is_dynamic = TRUE
assassin_prob = mode.threat_level*(2/3)
assassin_prob = max(0,mode.threat_level-20)
if(prob(assassin_prob))
if(is_dynamic)
var/threat_spent = CONFIG_GET(number/dynamic_assassinate_cost)
@@ -187,22 +191,37 @@
maroon_objective.owner = owner
maroon_objective.find_target()
add_objective(maroon_objective)
else
else if(prob(max(0,assassin_prob-20)))
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = owner
kill_objective.find_target()
add_objective(kill_objective)
else
var/datum/objective/assassinate/once/kill_objective = new
kill_objective.owner = owner
kill_objective.find_target()
add_objective(kill_objective)
else
if(prob(15) && !(locate(/datum/objective/download) in objectives) && !(owner.assigned_role in list("Research Director", "Scientist", "Roboticist")))
var/datum/objective/download/download_objective = new
download_objective.owner = owner
download_objective.gen_amount_goal()
add_objective(download_objective)
else
else if(prob(40)) // cum. not counting download: 40%.
var/datum/objective/steal/steal_objective = new
steal_objective.owner = owner
steal_objective.find_target()
add_objective(steal_objective)
else if(prob(100/3)) // cum. not counting download: 20%.
var/datum/objective/sabotage/sabotage_objective = new
sabotage_objective.owner = owner
sabotage_objective.find_target()
add_objective(sabotage_objective)
else // cum. not counting download: 40%
var/datum/objective/flavor/traitor/flavor_objective = new
flavor_objective.owner = owner
flavor_objective.forge_objective()
add_objective(flavor_objective)
/datum/antagonist/traitor/proc/forge_single_AI_objective()
.=1
@@ -369,11 +388,17 @@
if(objectives.len)//If the traitor had no objectives, don't need to process this.
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
traitorwin = FALSE
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
traitorwin = FALSE
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text]"
count++
if(uplink_true)
@@ -35,7 +35,7 @@
var/objectives_complete = TRUE
if(objectives.len)
for(var/datum/objective/objective in objectives)
if(!objective.check_completion())
if(objective.completable && !objective.check_completion())
objectives_complete = FALSE
break
+13 -6
View File
@@ -61,9 +61,9 @@
owner.current.forceMove(pick(GLOB.wizardstart))
/datum/antagonist/wizard/proc/create_objectives()
var/datum/objective/new_objective = new("Cause as much creative mayhem as you can aboard the station! The more outlandish your methods of achieving this, the better! Make sure there's a decent amount of crew alive to tell of your tale.")
new_objective.completed = TRUE //So they can greentext without admin intervention.
var/datum/objective/flavor/wizard/new_objective = new
new_objective.owner = owner
new_objective.forge_objective()
objectives += new_objective
if (!(locate(/datum/objective/escape) in objectives))
@@ -94,6 +94,7 @@
to_chat(owner, "<span class='boldannounce'>You are the Space Wizard!</span>")
to_chat(owner, "<B>The Space Wizards Federation has given you the following tasks:</B>")
owner.announce_objectives()
to_chat(owner, "<B>These are merely guidelines! The federation are your masters, but you forge your own path!</B>")
to_chat(owner, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.")
to_chat(owner, "The spellbook is bound to you, and others cannot use it.")
to_chat(owner, "In your pockets you will find a teleport scroll. Use it as needed.")
@@ -265,11 +266,17 @@
var/count = 1
var/wizardwin = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
wizardwin = FALSE
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
wizardwin = 0
parts += "<B>Objective #[count]</B>: [objective.explanation_text]"
count++
if(wizardwin)