Merge branch 'master' into hijack
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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>\
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user