Merge branch 'master' into hijack

This commit is contained in:
kevinz000
2020-02-19 22:25:31 -07:00
committed by GitHub
1848 changed files with 67548 additions and 23134 deletions
@@ -21,6 +21,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
@@ -71,6 +72,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()
@@ -118,6 +120,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
@@ -135,7 +149,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
@@ -211,7 +225,7 @@ GLOBAL_LIST_EMPTY(antagonists)
return
/datum/antagonist/proc/edit_memory(mob/user)
var/new_memo = copytext(trim(input(user,"Write new memory", "Memory", antag_memory) as null|message),1,MAX_MESSAGE_LEN)
var/new_memo = stripped_multiline_input(user, "Write new memory", "Memory", antag_memory, MAX_MESSAGE_LEN)
if (isnull(new_memo))
return
antag_memory = new_memo
+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>"
@@ -0,0 +1,34 @@
/datum/antagonist/abductee
name = "Abductee"
roundend_category = "abductees"
antagpanel_category = "Abductee"
var/datum/brain_trauma/abductee/brain_trauma
/datum/antagonist/abductee/on_gain()
give_objective()
. = ..()
/datum/antagonist/abductee/greet()
to_chat(owner, "<span class='warning'><b>Your mind snaps!</b></span>")
to_chat(owner, "<big><span class='warning'><b>You can't remember how you got here...</b></span></big>")
owner.announce_objectives()
/datum/antagonist/abductee/proc/give_objective()
var/mob/living/carbon/human/H = owner.current
if(istype(H))
H.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_LOBOTOMY)
var/objtype = (prob(75) ? /datum/objective/abductee/random : pick(subtypesof(/datum/objective/abductee/) - /datum/objective/abductee/random))
var/datum/objective/abductee/O = new objtype()
objectives += O
/datum/antagonist/abductee/apply_innate_effects(mob/living/mob_override)
update_abductor_icons_added(mob_override ? mob_override.mind : owner,"abductee")
var/mob/living/carbon/C = mob_override || owner?.current
if(istype(C))
if(brain_trauma)
qdel(brain_trauma) //make sure there's no lingering trauma
brain_trauma = C.gain_trauma(/datum/brain_trauma/abductee, TRAUMA_RESILIENCE_SURGERY)
/datum/antagonist/abductee/remove_innate_effects(mob/living/mob_override)
update_abductor_icons_removed(mob_override ? mob_override.mind : owner)
qdel(brain_trauma)
@@ -18,7 +18,7 @@
/datum/objective/abductee/paint/New()
var/color = pick(list("red", "blue", "green", "yellow", "orange", "purple", "black", "in rainbows", "in blood"))
explanation_text+= " [color]!"
explanation_text = " [color]!"
/datum/objective/abductee/speech
explanation_text = "Your brain is broken... you can only communicate in"
@@ -0,0 +1,18 @@
/datum/brain_trauma/abductee
name = "abductee mindsnapped"
desc = "The patient's brain has been scrambled by experimental procedures."
scan_desc = "brain scrambling"
gain_text = "<span class='danger'>Your mind snaps.. you feel fragmented.</span>"
lose_text = "<span class='boldnotice'>Your mind heals itself and you feel whole again.</span>"
random_gain = FALSE
clonable = TRUE
/datum/brain_trauma/abductee/on_gain()
. = ..()
if(owner.mind)
if(!owner.mind.has_antag_datum(/datum/antagonist/abductee))
owner.mind.add_antag_datum(/datum/antagonist/abductee)
/datum/brain_trauma/abductee/on_lose()
. = ..()
owner.mind?.remove_antag_datum(/datum/antagonist/abductee)
@@ -159,35 +159,6 @@
return "<div class='panel redborder'>[result.Join("<br>")]</div>"
/datum/antagonist/abductee
name = "Abductee"
roundend_category = "abductees"
antagpanel_category = "Abductee"
/datum/antagonist/abductee/on_gain()
give_objective()
. = ..()
/datum/antagonist/abductee/greet()
to_chat(owner, "<span class='warning'><b>Your mind snaps!</b></span>")
to_chat(owner, "<big><span class='warning'><b>You can't remember how you got here...</b></span></big>")
owner.announce_objectives()
/datum/antagonist/abductee/proc/give_objective()
var/mob/living/carbon/human/H = owner.current
if(istype(H))
H.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_LOBOTOMY)
var/objtype = (prob(75) ? /datum/objective/abductee/random : pick(subtypesof(/datum/objective/abductee/) - /datum/objective/abductee/random))
var/datum/objective/abductee/O = new objtype()
objectives += O
/datum/antagonist/abductee/apply_innate_effects(mob/living/mob_override)
update_abductor_icons_added(mob_override ? mob_override.mind : owner,"abductee")
/datum/antagonist/abductee/remove_innate_effects(mob/living/mob_override)
update_abductor_icons_removed(mob_override ? mob_override.mind : owner)
// LANDMARKS
/obj/effect/landmark/abductor
var/team_number = 1
@@ -29,6 +29,11 @@
var/stealth_armor = list("melee" = 15, "bullet" = 15, "laser" = 15, "energy" = 15, "bomb" = 15, "bio" = 15, "rad" = 15, "fire" = 70, "acid" = 70)
var/combat_armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 50, "rad" = 50, "fire" = 90, "acid" = 90)
/obj/item/clothing/suit/armor/abductor/vest/Initialize()
. = ..()
stealth_armor = getArmor(arglist(stealth_armor))
combat_armor = getArmor(arglist(combat_armor))
/obj/item/clothing/suit/armor/abductor/vest/proc/toggle_nodrop()
if(HAS_TRAIT_FROM(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT))
REMOVE_TRAIT(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT)
@@ -629,15 +634,12 @@
icon_state = "abductor_headset"
item_state = "abductor_headset"
keyslot2 = new /obj/item/encryptionkey/heads/captain
bowman = TRUE
/obj/item/radio/headset/abductor/Initialize(mapload)
. = ..()
make_syndie()
/obj/item/radio/headset/abductor/ComponentInitialize()
. = ..()
AddComponent(/datum/component/wearertargeting/earprotection, list(SLOT_EARS))
/obj/item/radio/headset/abductor/attackby(obj/item/W, mob/user, params)
if(W.tool_behaviour == TOOL_SCREWDRIVER)
return // Stops humans from disassembling abductor headsets.
@@ -33,7 +33,7 @@
if(IC)
user.visible_message("[user] pulls [IC] out of [target]'s [target_zone]!", "<span class='notice'>You pull [IC] out of [target]'s [target_zone].</span>")
user.put_in_hands(IC)
IC.Remove(target)
IC.Remove()
return 1
else
to_chat(user, "<span class='warning'>You don't find anything in [target]'s [target_zone]!</span>")
@@ -73,13 +73,14 @@
active_mind_control = FALSE
return TRUE
/obj/item/organ/heart/gland/Remove(mob/living/carbon/M, special = 0)
/obj/item/organ/heart/gland/Remove(special = FALSE)
active = 0
if(initial(uses) == 1)
uses = initial(uses)
var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR]
hud.remove_from_hud(owner)
clear_mind_control()
if(!QDELETED(owner))
var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR]
hud.remove_from_hud(owner)
clear_mind_control()
..()
/obj/item/organ/heart/gland/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
@@ -14,6 +14,7 @@
/obj/item/organ/heart/gland/access/proc/free_access(datum/source, obj/O)
return TRUE
/obj/item/organ/heart/gland/access/Remove(mob/living/carbon/M, special = 0)
UnregisterSignal(owner, COMSIG_MOB_ALLOWED)
..()
/obj/item/organ/heart/gland/access/Remove(special = FALSE)
if(!QDELETED(owner))
UnregisterSignal(owner, COMSIG_MOB_ALLOWED)
return ..()
@@ -11,9 +11,10 @@
..()
ADD_TRAIT(owner, TRAIT_SHOCKIMMUNE, "abductor_gland")
/obj/item/organ/heart/gland/electric/Remove(mob/living/carbon/M, special = 0)
REMOVE_TRAIT(owner, TRAIT_SHOCKIMMUNE, "abductor_gland")
..()
/obj/item/organ/heart/gland/electric/Remove(special = FALSE)
if(!QDELETED(owner))
REMOVE_TRAIT(owner, TRAIT_SHOCKIMMUNE, "abductor_gland")
return ..()
/obj/item/organ/heart/gland/electric/activate()
owner.visible_message("<span class='danger'>[owner]'s skin starts emitting electric arcs!</span>",\
@@ -65,14 +65,14 @@
/obj/item/organ/heart/gland/heal/proc/reject_implant(obj/item/organ/cyberimp/implant)
owner.visible_message("<span class='warning'>[owner] vomits up his [implant.name]!</span>", "<span class='userdanger'>You suddenly vomit up your [implant.name]!</span>")
owner.vomit(0, TRUE, TRUE, 1, FALSE, FALSE, FALSE, TRUE)
implant.Remove(owner)
implant.Remove()
implant.forceMove(owner.drop_location())
/obj/item/organ/heart/gland/heal/proc/replace_liver(obj/item/organ/liver/liver)
if(liver)
owner.visible_message("<span class='warning'>[owner] vomits up his [liver.name]!</span>", "<span class='userdanger'>You suddenly vomit up your [liver.name]!</span>")
owner.vomit(0, TRUE, TRUE, 1, FALSE, FALSE, FALSE, TRUE)
liver.Remove(owner)
liver.Remove()
liver.forceMove(owner.drop_location())
else
to_chat(owner, "<span class='warning'>You feel a weird rumble in your bowels...</span>")
@@ -87,7 +87,7 @@
if(lungs)
owner.visible_message("<span class='warning'>[owner] vomits up his [lungs.name]!</span>", "<span class='userdanger'>You suddenly vomit up your [lungs.name]!</span>")
owner.vomit(0, TRUE, TRUE, 1, FALSE, FALSE, FALSE, TRUE)
lungs.Remove(owner)
lungs.Remove()
lungs.forceMove(owner.drop_location())
else
to_chat(owner, "<span class='warning'>You feel a weird rumble inside your chest...</span>")
@@ -102,7 +102,7 @@
if(eyes)
owner.visible_message("<span class='warning'>[owner]'s [eyes.name] fall out of their sockets!</span>", "<span class='userdanger'>Your [eyes.name] fall out of their sockets!</span>")
playsound(owner, 'sound/effects/splat.ogg', 50, TRUE)
eyes.Remove(owner)
eyes.Remove()
eyes.forceMove(owner.drop_location())
else
to_chat(owner, "<span class='warning'>You feel a weird rumble behind your eye sockets...</span>")
@@ -203,7 +203,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
/mob/camera/blob/proc/blob_talk(message)
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
if (!message)
return
@@ -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 --
@@ -283,8 +283,8 @@
streak = ""
restraining = 0
streak = streak+element
if(length(streak) > max_streak_length)
streak = copytext(streak,2)
if(length_char(streak) > max_streak_length)
streak = streak[1]
return
@@ -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,14 @@
// 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.Knockdown(min(5, rand(10, 10 * powerlevel)) )
// Chance of KO
if(rand(6 + powerlevel) >= 6 && target.stat <= UNCONSCIOUS)
target.Unconscious(40)
// 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>", \
@@ -170,10 +170,10 @@
// Warn Feeder about Witnesses...
var/was_unnoticed = TRUE
for(var/mob/living/M in viewers(notice_range, owner))
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))
if(M != owner && M != target && iscarbon(M) && M.mind && !M.silicon_privileges && !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.Knockdown(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,47 +23,49 @@
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
var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
// 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))
if(M != owner && isliving(M) && M.mind && !M.has_unlimited_silicon_privilege && !M.eye_blind) // M.client <--- add this in after testing!
for(var/mob/living/M in viewers(get_turf(owner)))
if(M != owner && isliving(M) && M.mind && !M.silicon_privileges && !M.eye_blind) // M.client <--- add this in after testing!
am_seen = TRUE
if (!M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
drop_item = TRUE
@@ -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.resting = TRUE
//user.Unconscious(30,0)
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
@@ -107,11 +107,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)
@@ -15,7 +15,7 @@
to_chat(user, "<span class='notice'>We begin our stasis, preparing energy to arise once more.</span>")
if(user.stat != DEAD)
user.emote("deathgasp")
user.tod = STATION_TIME_TIMESTAMP("hh:mm:ss")
user.tod = STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)
user.fakedeath("changeling") //play dead
user.update_stat()
user.update_canmove()
@@ -18,7 +18,7 @@
var/list/organs = user.getorganszone(BODY_ZONE_HEAD, 1)
for(var/obj/item/organ/I in organs)
I.Remove(user, 1)
I.Remove(TRUE)
explosion(get_turf(user), 0, 0, 2, 0, TRUE)
for(var/mob/living/carbon/human/H in range(2,user))
@@ -22,7 +22,7 @@
if(!istype(O))
continue
O.Remove(user)
O.Remove()
if(iscarbon(user))
var/mob/living/carbon/C = user
C.vomit(0, toxic = TRUE)
@@ -68,8 +68,8 @@
/obj/item/clockwork/replica_fabricator/pre_attack(atom/target, mob/living/user, params)
if(!target || !user || !is_servant_of_ratvar(user) || istype(target, /obj/item/storage))
return TRUE
return fabricate(target, user)
return ..()
return !fabricate(target, user)
//A note here; return values are for if we CAN BE PUT ON A TABLE, not IF WE ARE SUCCESSFUL, unless no_table_check is TRUE
/obj/item/clockwork/replica_fabricator/proc/fabricate(atom/target, mob/living/user, silent, no_table_check)
@@ -104,6 +104,6 @@
transfer_personality(H)
brainmob.fully_replace_character_name(null, "[braintype] [H.real_name]")
name = "[initial(name)] ([brainmob.name])"
B.Remove(H)
B.Remove()
qdel(B)
H.update_hair()
@@ -83,7 +83,7 @@
return
if(client.handle_spam_prevention(message,MUTE_IC))
return
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
if(!message)
return
src.log_talk(message, LOG_SAY, tag="clockwork eminence")
@@ -8,7 +8,7 @@
name = "clockwork marauder"
desc = "The stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield."
icon_state = "clockwork_marauder"
mob_biotypes = list(MOB_INORGANIC, MOB_HUMANOID)
mob_biotypes = MOB_HUMANOID
health = 120
maxHealth = 120
force_threshold = 8
@@ -118,7 +118,7 @@
continue
if(ishostile(L))
var/mob/living/simple_animal/hostile/H = L
if(("ratvar" in H.faction) || (!H.mind && "neutral" in H.faction))
if(("ratvar" in H.faction) || (!H.mind && ("neutral" in H.faction)))
continue
if(ismegafauna(H) || (!H.mind && H.AIStatus == AI_OFF))
continue
@@ -120,7 +120,7 @@
hierophant_network.Grant(current)
current.throw_alert("clockinfo", /obj/screen/alert/clockwork/infodump)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G.active && ishuman(current))
if(G && G.active && ishuman(current))
current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER))
/datum/antagonist/clockcult/remove_innate_effects(mob/living/mob_override)
@@ -174,9 +174,12 @@
log_admin("[key_name(admin)] has made [new_owner.current] into a servant of Ratvar.")
/datum/antagonist/clockcult/admin_remove(mob/user)
remove_servant_of_ratvar(owner.current, TRUE)
message_admins("[key_name_admin(user)] has removed clockwork servant status from [owner.current].")
log_admin("[key_name(user)] has removed clockwork servant status from [owner.current].")
var/mob/target = owner.current
if(!target)
return
remove_servant_of_ratvar(target, TRUE)
message_admins("[key_name_admin(user)] has removed clockwork servant status from [target].")
log_admin("[key_name(user)] has removed clockwork servant status from [target].")
/datum/antagonist/clockcult/get_admin_commands()
. = ..()
@@ -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()
+12 -5
View File
@@ -143,7 +143,7 @@
//Cult Blood Spells
/datum/action/innate/cult/blood_spell/stun
name = "Stun"
desc = "A potent spell that will stun and mute victims upon contact."
desc = "A potent spell that will stun and mute victims upon contact. When the cult ascends, so does the spell, it burns and throws back the victim!"
button_icon_state = "hand"
magic_path = "/obj/item/melee/blood_magic/stun"
health_cost = 10
@@ -343,7 +343,7 @@
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "disintegrate"
item_state = null
item_flags = NEEDS_PERMIT | ABSTRACT | DROPDEL
item_flags = NEEDS_PERMIT | ABSTRACT | DROPDEL | NO_ATTACK_CHAIN_SOFT_STAMCRIT
w_class = WEIGHT_CLASS_HUGE
throwforce = 0
@@ -437,8 +437,15 @@
else
target.visible_message("<span class='warning'>[L] starts to glow in a halo of light!</span>", \
"<span class='userdanger'>A feeling of warmth washes over you, rays of holy light surround your body and protect you from the flash of light!</span>")
else
if(!iscultist(L))
else // cult doesn't stun any longer when halos are out, instead it does burn damage + knockback!
var/datum/antagonist/cult/user_antag = user.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
if(user_antag.cult_team.cult_ascendent)
if(!iscultist(L))
L.adjustFireLoss(20)
if(L.move_resist < MOVE_FORCE_STRONG)
var/atom/throw_target = get_edge_target_turf(L, user.dir)
L.throw_at(throw_target, 7, 1, user)
else if(!iscultist(L))
L.Knockdown(160)
L.adjustStaminaLoss(140) //Ensures hard stamcrit
L.flash_act(1,1)
@@ -753,7 +760,7 @@
var/turf/T = get_turf(target)
if(T)
for(var/obj/effect/decal/cleanable/blood/B in view(T, 2))
if(B.blood_state == "blood")
if(B.blood_state == BLOOD_STATE_BLOOD)
if(B.bloodiness == 100) //Bonus for "pristine" bloodpools, also to prevent cheese with footprint spam
temp += 30
else
+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)
+2 -2
View File
@@ -22,7 +22,7 @@ Runes can either be invoked by one's self or with many different cultists. Each
icon = 'icons/obj/rune.dmi'
icon_state = "1"
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
layer = LOW_OBJ_LAYER
layer = SIGIL_LAYER
color = RUNE_COLOR_RED
var/invocation = "Aiy ele-mayo." //This is said by cultists when the rune is invoked.
@@ -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")
+1 -1
View File
@@ -13,7 +13,7 @@
icon = 'icons/mob/mob.dmi'
icon_state = "imp"
icon_living = "imp"
mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
speed = 1
a_intent = INTENT_HARM
stop_automated_movement = 1
@@ -57,7 +57,7 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
var/short_desc = ""
var/long_desc = ""
var/stat_block = ""
var/threshold_block = ""
var/threshold_block = list()
var/category = ""
var/list/symptoms
@@ -76,7 +76,7 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
resistance += initial(S.resistance)
stage_speed += initial(S.stage_speed)
transmittable += initial(S.transmittable)
threshold_block += "<br><br>[initial(S.threshold_desc)]"
threshold_block += initial(S.threshold_desc)
stat_block = "Resistance: [resistance]<br>Stealth: [stealth]<br>Stage Speed: [stage_speed]<br>Transmissibility: [transmittable]<br><br>"
if(symptoms.len == 1) //lazy boy's dream
name = initial(S.name)
@@ -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
@@ -318,7 +318,11 @@ the new instance inside the host to be updated to the template's stats.
var/list/dat = list()
if(examining_ability)
dat += "<a href='byond://?src=[REF(src)];main_menu=1'>Back</a><br><h1>[examining_ability.name]</h1>[examining_ability.stat_block][examining_ability.long_desc][examining_ability.threshold_block]"
dat += "<a href='byond://?src=[REF(src)];main_menu=1'>Back</a><br>"
dat += "<h1>[examining_ability.name]</h1>"
dat += "[examining_ability.stat_block][examining_ability.long_desc][examining_ability.threshold_block]"
for(var/entry in examining_ability.threshold_block)
dat += "<b>[entry]</b>: [examining_ability.threshold_block[entry]]<br>"
else
dat += "<h1>Disease Statistics</h1><br>\
Resistance: [DT.totalResistance()]<br>\
+10 -16
View File
@@ -47,6 +47,7 @@
if(2) //steal
var/datum/objective/steal/special/O = new /datum/objective/steal/special()
O.owner = owner
O.find_target()
objectives += O
if(3) //protect/kill
@@ -57,10 +58,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()
@@ -68,23 +69,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
@@ -11,9 +11,10 @@
var/default_timer_set = 90
var/minimum_timer_set = 90
var/maximum_timer_set = 3600
var/ui_style = "nanotrasen"
ui_style = "nanotrasen"
var/numeric_input = ""
var/ui_mode = NUKEUI_AWAIT_DISK
var/timing = FALSE
var/exploding = FALSE
var/exploded = FALSE
@@ -97,6 +98,8 @@
if(!user.transferItemToLoc(I, src))
return
auth = I
update_ui_mode()
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
add_fingerprint(user)
return
@@ -233,113 +236,160 @@
var/volume = (get_time_left() <= 20 ? 30 : 5)
playsound(loc, 'sound/items/timer.ogg', volume, 0)
/obj/machinery/nuclearbomb/proc/update_ui_mode()
if(exploded)
ui_mode = NUKEUI_EXPLODED
return
if(!auth)
ui_mode = NUKEUI_AWAIT_DISK
return
if(timing)
ui_mode = NUKEUI_TIMING
return
if(!safety)
ui_mode = NUKEUI_AWAIT_ARM
return
if(!yes_code)
ui_mode = NUKEUI_AWAIT_CODE
return
ui_mode = NUKEUI_AWAIT_TIMER
/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state=GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "nuclear_bomb", name, 500, 600, master_ui, state)
ui = new(user, src, ui_key, "nuclear_bomb", name, 350, 442, master_ui, state)
ui.set_style(ui_style)
ui.open()
/obj/machinery/nuclearbomb/ui_data(mob/user)
var/list/data = list()
data["disk_present"] = auth
data["code_approved"] = yes_code
var/first_status
if(auth)
if(yes_code)
first_status = timing ? "Func/Set" : "Functional"
else
first_status = "Auth S2."
var/hidden_code = (ui_mode == NUKEUI_AWAIT_CODE && numeric_input != "ERROR")
var/current_code = ""
if(hidden_code)
while(length(current_code) < length(numeric_input))
current_code = "[current_code]*"
else
if(timing)
first_status = "Set"
else
first_status = "Auth S1."
var/second_status = exploded ? "Warhead triggered, thanks for flying Nanotrasen" : (safety ? "Safe" : "Engaged")
current_code = numeric_input
while(length(current_code) < 5)
current_code = "[current_code]-"
var/first_status
var/second_status
switch(ui_mode)
if(NUKEUI_AWAIT_DISK)
first_status = "DEVICE LOCKED"
if(timing)
second_status = "TIME: [get_time_left()]"
else
second_status = "AWAIT DISK"
if(NUKEUI_AWAIT_CODE)
first_status = "INPUT CODE"
second_status = "CODE: [current_code]"
if(NUKEUI_AWAIT_TIMER)
first_status = "INPUT TIME"
second_status = "TIME: [current_code]"
if(NUKEUI_AWAIT_ARM)
first_status = "DEVICE READY"
second_status = "TIME: [get_time_left()]"
if(NUKEUI_TIMING)
first_status = "DEVICE ARMED"
second_status = "TIME: [get_time_left()]"
if(NUKEUI_EXPLODED)
first_status = "DEVICE DEPLOYED"
second_status = "THANK YOU"
data["status1"] = first_status
data["status2"] = second_status
data["anchored"] = anchored
data["safety"] = safety
data["timing"] = timing
data["time_left"] = get_time_left()
data["timer_set"] = timer_set
data["timer_is_not_default"] = timer_set != default_timer_set
data["timer_is_not_min"] = timer_set != minimum_timer_set
data["timer_is_not_max"] = timer_set != maximum_timer_set
var/message = "AUTH"
if(auth)
message = "[numeric_input]"
if(yes_code)
message = "*****"
data["message"] = message
return data
/obj/machinery/nuclearbomb/ui_act(action, params)
if(..())
return
playsound(src, "terminal_type", 20, FALSE)
switch(action)
if("eject_disk")
if(auth && auth.loc == src)
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
auth.forceMove(get_turf(src))
auth = null
. = TRUE
if("insert_disk")
if(!auth)
else
var/obj/item/I = usr.is_holding_item_of_type(/obj/item/disk/nuclear)
if(I && disk_check(I) && usr.transferItemToLoc(I, src))
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
auth = I
. = TRUE
update_ui_mode()
if("keypad")
if(auth)
var/digit = params["digit"]
switch(digit)
if("R")
if("C")
if(auth && ui_mode == NUKEUI_AWAIT_ARM)
set_safety()
yes_code = FALSE
playsound(src, 'sound/machines/nuke/confirm_beep.ogg', 50, FALSE)
update_ui_mode()
else
playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
numeric_input = ""
yes_code = FALSE
. = TRUE
if("E")
if(numeric_input == r_code)
numeric_input = ""
yes_code = TRUE
. = TRUE
else
numeric_input = "ERROR"
switch(ui_mode)
if(NUKEUI_AWAIT_CODE)
if(numeric_input == r_code)
numeric_input = ""
yes_code = TRUE
playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
. = TRUE
else
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
numeric_input = "ERROR"
if(NUKEUI_AWAIT_TIMER)
var/number_value = text2num(numeric_input)
if(number_value)
timer_set = CLAMP(number_value, minimum_timer_set, maximum_timer_set)
playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
set_safety()
. = TRUE
else
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
update_ui_mode()
if("0","1","2","3","4","5","6","7","8","9")
if(numeric_input != "ERROR")
numeric_input += digit
if(length(numeric_input) > 5)
numeric_input = "ERROR"
else
playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
. = TRUE
if("timer")
if(auth && yes_code)
var/change = params["change"]
if(change == "reset")
timer_set = default_timer_set
else if(change == "decrease")
timer_set = max(minimum_timer_set, timer_set - 10)
else if(change == "increase")
timer_set = min(maximum_timer_set, timer_set + 10)
else if(change == "input")
var/user_input = input(usr, "Set time to detonation.", name) as null|num
if(!user_input)
return
var/N = text2num(user_input)
if(!N)
return
timer_set = CLAMP(N,minimum_timer_set,maximum_timer_set)
else
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
if("arm")
if(auth && yes_code && !safety && !exploded)
playsound(src, 'sound/machines/nuke/confirm_beep.ogg', 50, FALSE)
set_active()
update_ui_mode()
. = TRUE
if("safety")
if(auth && yes_code && !exploded)
set_safety()
else
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
if("anchor")
if(auth && yes_code)
playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
set_anchor()
if("toggle_timer")
if(auth && yes_code && !safety && !exploded)
set_active()
else
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
/obj/machinery/nuclearbomb/proc/set_anchor()
@@ -16,7 +16,7 @@
var/icon_stun = "revenant_stun"
var/icon_drain = "revenant_draining"
var/stasis = FALSE
mob_biotypes = list(MOB_SPIRIT)
mob_biotypes = MOB_SPIRIT
incorporeal_move = INCORPOREAL_MOVE_JAUNT
invisibility = INVISIBILITY_REVENANT
health = INFINITY //Revenants don't use health, they use essence instead
@@ -12,7 +12,7 @@
icon = 'icons/mob/mob.dmi'
icon_state = "daemon"
icon_living = "daemon"
mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
speed = 1
a_intent = INTENT_HARM
stop_automated_movement = 1
@@ -100,10 +100,9 @@
if(M.mind)
M.mind.AddSpell(new /obj/effect/proc_holder/spell/bloodcrawl(null))
/obj/item/organ/heart/demon/Remove(mob/living/carbon/M, special = 0)
..()
if(M.mind)
M.mind.RemoveSpell(/obj/effect/proc_holder/spell/bloodcrawl)
/obj/item/organ/heart/demon/Remove(special = FALSE)
owner?.mind?.RemoveSpell(/obj/effect/proc_holder/spell/bloodcrawl)
return ..()
/obj/item/organ/heart/demon/Stop()
return 0 // Always beating.
@@ -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!"
+4 -2
View File
@@ -19,6 +19,7 @@
job_description = "Swarmer"
death = FALSE
roundstart = FALSE
short_desc = "You are a swarmer, a weapon of a long dead civilization."
flavour_text = {"
<b>You are a swarmer, a weapon of a long dead civilization. Until further orders from your original masters are received, you must continue to consume and replicate.</b>
<b>Clicking on any object will try to consume it, either deconstructing it into its components, destroying it, or integrating any materials it has into you if successful.</b>
@@ -61,7 +62,7 @@
speak_emote = list("tones")
initial_language_holder = /datum/language_holder/swarmer
bubble_icon = "swarmer"
mob_biotypes = list(MOB_ROBOTIC)
mob_biotypes = MOB_ROBOTIC
health = 40
maxHealth = 40
status_flags = CANPUSH
@@ -274,7 +275,8 @@
/obj/machinery/camera/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DisIntegrate(src)
toggle_cam(S, 0)
if(!QDELETED(S)) //If it got blown up no need to turn it off.
toggle_cam(S, 0)
return TRUE
/obj/machinery/particle_accelerator/control_box/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
@@ -78,19 +78,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)
@@ -171,7 +175,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)
@@ -188,22 +192,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
@@ -370,11 +389,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)
@@ -22,11 +22,19 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
desc = "You aren't entirely sure what this does, but it's very beepy and boopy."
background_icon_state = "bg_tech_blue"
icon_icon = 'icons/mob/actions/actions_AI.dmi'
check_flags = AB_CHECK_CONSCIOUS //can't doomsday if dead.
var/mob/living/silicon/ai/owner_AI //The owner AI, so we don't have to typecast every time
var/uses //If we have multiple uses of the same power
var/auto_use_uses = TRUE //If we automatically use up uses on each activation
var/cooldown_period //If applicable, the time in deciseconds we have to wait before using any more modules
/datum/action/innate/ai/New()
..()
if(uses > 1)
desc = "[desc] It has [uses] use\s remaining."
button.desc = desc
/datum/action/innate/ai/Grant(mob/living/L)
. = ..()
if(!isAI(owner))
@@ -38,7 +46,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
/datum/action/innate/ai/IsAvailable()
. = ..()
if(owner_AI && owner_AI.malf_cooldown > world.time)
return
return FALSE
/datum/action/innate/ai/Trigger()
. = ..()
@@ -49,12 +57,16 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
/datum/action/innate/ai/proc/adjust_uses(amt, silent)
uses += amt
if(!silent && uses)
to_chat(owner, "<span class='notice'>[name] now has <b>[uses]</b> use[uses > 1 ? "s" : ""] remaining.</span>")
if(!uses)
if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
to_chat(owner, "<span class='warning'>[name] has run out of uses!</span>")
qdel(src)
if(uses)
if(!silent)
to_chat(owner, "<span class='notice'>[name] now has <b>[uses]</b> use[uses > 1 ? "s" : ""] remaining.</span>")
desc = "[initial(desc)] It has [uses] use\s remaining."
UpdateButtonIcon()
return
if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
to_chat(owner, "<span class='warning'>[name] has run out of uses!</span>")
qdel(src)
//Framework for ranged abilities that can have different effects by left-clicking stuff.
/datum/action/innate/ai/ranged
@@ -74,13 +86,16 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
/datum/action/innate/ai/ranged/adjust_uses(amt, silent)
uses += amt
if(!silent && uses)
to_chat(owner, "<span class='notice'>[name] now has <b>[uses]</b> use[uses > 1 ? "s" : ""] remaining.</span>")
if(!uses)
if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
to_chat(owner, "<span class='warning'>[name] has run out of uses!</span>")
Remove(owner)
QDEL_IN(src, 100) //let any active timers on us finish up
if(uses)
if(!silent)
to_chat(owner, "<span class='notice'>[name] now has <b>[uses]</b> use[uses > 1 ? "s" : ""] remaining.</span>")
desc = "[initial(desc)] It has [uses] use\s remaining."
UpdateButtonIcon()
return
if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
to_chat(owner, "<span class='warning'>[name] has run out of uses!</span>")
Remove(owner)
QDEL_IN(src, 100) //let any active timers on us finish up
/datum/action/innate/ai/ranged/Destroy()
QDEL_NULL(linked_ability)
@@ -97,7 +112,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
var/datum/action/innate/ai/ranged/attached_action
/obj/effect/proc_holder/ranged_ai/Destroy()
QDEL_NULL(attached_action)
attached_action = null
return ..()
/obj/effect/proc_holder/ranged_ai/proc/toggle(mob/user)
@@ -185,6 +200,8 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
A.playsound_local(A, AM.unlock_sound, 50, 0)
else //Adding uses to an existing module
action.uses += initial(action.uses)
action.desc = "[initial(action.desc)] It has [action.uses] use\s remaining."
action.UpdateButtonIcon()
temp = "Additional use[action.uses > 1 ? "s" : ""] added to [action.name]!"
processing_time -= AM.cost
@@ -238,6 +255,8 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
return
if(alert(owner, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", "confirm = TRUE;", "confirm = FALSE;") != "confirm = TRUE;")
return
if (active)
return //prevent the AI from activating an already active doomsday
active = TRUE
set_us_up_the_bomb(owner)
@@ -245,64 +264,64 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
set waitfor = FALSE
to_chat(owner, "<span class='small boldannounce'>run -o -a 'selfdestruct'</span>")
sleep(5)
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "<span class='small boldannounce'>Running executable 'selfdestruct'...</span>")
sleep(rand(10, 30))
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
owner.playsound_local(owner, 'sound/misc/bloblarm.ogg', 50, 0)
to_chat(owner, "<span class='userdanger'>!!! UNAUTHORIZED SELF-DESTRUCT ACCESS !!!</span>")
to_chat(owner, "<span class='boldannounce'>This is a class-3 security violation. This incident will be reported to Central Command.</span>")
for(var/i in 1 to 3)
sleep(20)
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "<span class='boldannounce'>Sending security report to Central Command.....[rand(0, 9) + (rand(20, 30) * i)]%</span>")
sleep(3)
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "<span class='small boldannounce'>auth 'akjv9c88asdf12nb' ******************</span>")
owner.playsound_local(owner, 'sound/items/timer.ogg', 50, 0)
sleep(30)
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "<span class='boldnotice'>Credentials accepted. Welcome, akjv9c88asdf12nb.</span>")
owner.playsound_local(owner, 'sound/misc/server-ready.ogg', 50, 0)
sleep(5)
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "<span class='boldnotice'>Arm self-destruct device? (Y/N)</span>")
owner.playsound_local(owner, 'sound/misc/compiler-stage1.ogg', 50, 0)
sleep(20)
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "<span class='small boldannounce'>Y</span>")
sleep(15)
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "<span class='boldnotice'>Confirm arming of self-destruct device? (Y/N)</span>")
owner.playsound_local(owner, 'sound/misc/compiler-stage2.ogg', 50, 0)
sleep(10)
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "<span class='small boldannounce'>Y</span>")
sleep(rand(15, 25))
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "<span class='boldnotice'>Please repeat password to confirm.</span>")
owner.playsound_local(owner, 'sound/misc/compiler-stage2.ogg', 50, 0)
sleep(14)
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "<span class='small boldannounce'>******************</span>")
sleep(40)
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "<span class='boldnotice'>Credentials accepted. Transmitting arming signal...</span>")
owner.playsound_local(owner, 'sound/misc/server-ready.ogg', 50, 0)
sleep(30)
if(!owner || QDELETED(owner))
if(QDELETED(owner) || owner.stat == DEAD)
return
priority_announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", "aimalf")
set_security_level("delta")
@@ -724,9 +743,10 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
/datum/action/innate/ai/blackout
name = "Blackout"
desc = "Overloads lights across the station."
desc = "Overloads random lights across the station."
button_icon_state = "blackout"
uses = 3
auto_use_uses = FALSE
/datum/action/innate/ai/blackout/Activate()
for(var/obj/machinery/power/apc/apc in GLOB.apcs_list)
@@ -736,6 +756,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
apc.overload++
to_chat(owner, "<span class='notice'>Overcurrent applied to the powernet.</span>")
owner.playsound_local(owner, "sparks", 50, 0)
adjust_uses(-1)
//Disable Emergency Lights
@@ -784,11 +805,6 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
auto_use_uses = FALSE
cooldown_period = 30
/datum/action/innate/ai/reactivate_cameras/New()
..()
desc = "[desc] There are 30 reactivations remaining."
button.desc = desc
/datum/action/innate/ai/reactivate_cameras/Activate()
var/fixed_cameras = 0
for(var/V in GLOB.cameranet.cameras)
@@ -803,8 +819,6 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
to_chat(owner, "<span class='notice'>Diagnostic complete! Cameras reactivated: <b>[fixed_cameras]</b>. Reactivations remaining: <b>[uses]</b>.</span>")
owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, 0)
adjust_uses(0, TRUE) //Checks the uses remaining
if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe
desc = "[initial(desc)] There are [uses] reactivations remaining."
//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision.
@@ -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
@@ -246,7 +246,7 @@
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
var/mob/living/carbon/human/target = null
var/list/mob/living/carbon/human/possible = list()
var/list/mob/living/carbon/human/possible
var/obj/item/voodoo_link = null
var/cooldown_time = 30 //3s
var/cooldown = 0
@@ -284,7 +284,7 @@
user.unset_machine()
/obj/item/voodoo/attack_self(mob/user)
if(!target && possible.len)
if(!target && length(possible))
target = input(user, "Select your victim!", "Voodoo") as null|anything in possible
return
@@ -324,12 +324,12 @@
cooldown = world.time + cooldown_time
/obj/item/voodoo/proc/update_targets()
LAZYINITLIST(possible)
possible = null
if(!voodoo_link)
return
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
if(md5(H.dna.uni_identity) in voodoo_link.fingerprints)
possible |= H
LAZYOR(possible, H)
/obj/item/voodoo/proc/GiveHint(mob/victim,force=0)
if(prob(50) || force)
@@ -163,7 +163,7 @@
/datum/spellbook_entry/timestop
name = "Time Stop"
spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/timestop
spell_type = /obj/effect/proc_holder/spell/aoe_turf/timestop
category = "Defensive"
/datum/spellbook_entry/smoke
+14 -7
View File
@@ -60,9 +60,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))
@@ -93,6 +93,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.")
@@ -108,7 +109,7 @@
var/wizard_name_second = pick(GLOB.wizard_second)
var/randomname = "[wizard_name_first] [wizard_name_second]"
var/mob/living/wiz_mob = owner.current
var/newname = copytext(sanitize(input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname) as null|text),1,MAX_NAME_LEN)
var/newname = reject_bad_name(stripped_input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname, MAX_NAME_LEN))
if (!newname)
newname = randomname
@@ -264,11 +265,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)