mirror of
https://github.com/goonstation/goonstation-2020.git
synced 2026-07-15 00:52:25 +01:00
480 lines
16 KiB
Plaintext
480 lines
16 KiB
Plaintext
// Converted everything related to werewolves from client procs to ability holders and used
|
|
// the opportunity to do some clean-up as well (Convair880).
|
|
|
|
// Added kyle2143's werewolf patch (Gannets).
|
|
|
|
//////////////////////////////////////////// Setup //////////////////////////////////////////////////
|
|
|
|
/mob/proc/make_werewolf(var/force=0)
|
|
if (ishuman(src))
|
|
var/datum/abilityHolder/werewolf/A = src.get_ability_holder(/datum/abilityHolder/werewolf)
|
|
if (A && istype(A))
|
|
return
|
|
var/datum/abilityHolder/werewolf/W = src.add_ability_holder(/datum/abilityHolder/werewolf)
|
|
//W.addAbility(/datum/targetable/werewolf/werewolf_transform)
|
|
W.addAbility(/datum/targetable/werewolf/werewolf_feast)
|
|
W.addAbility(/datum/targetable/werewolf/werewolf_pounce)
|
|
W.addAbility(/datum/targetable/werewolf/werewolf_thrash)
|
|
W.addAbility(/datum/targetable/werewolf/werewolf_throw)
|
|
W.addAbility(/datum/targetable/werewolf/werewolf_tainted_saliva)
|
|
W.addAbility(/datum/targetable/werewolf/werewolf_defense)
|
|
// W.addAbility(/datum/targetable/werewolf/werewolf_spread_affliction) //not using for now, but could be fun later ish.
|
|
if (force)
|
|
W.addAbility(/datum/targetable/werewolf/werewolf_transform)
|
|
boutput(src, "<span style=\"color:red\">You are a full werewolf, you can transform immediately!</span>")
|
|
else
|
|
SPAWN_DBG(W.awaken_time)
|
|
handle_natural_werewolf(W)
|
|
|
|
src.resistances += /datum/ailment/disease/lycanthropy
|
|
|
|
if (src.mind && src.mind.special_role != "omnitraitor")
|
|
SHOW_WEREWOLF_TIPS(src)
|
|
|
|
else return
|
|
|
|
/mob/proc/handle_natural_werewolf(var/datum/abilityHolder/werewolf/W)
|
|
src.emote("shiver")
|
|
boutput(src, "<span style=\"color:red\"><b>You feel feral!</b></span>")
|
|
sleep(50)
|
|
if (!src.getStatusDuration("weakened") && !src.getStatusDuration("paralysis"))
|
|
boutput(src, "<span style=\"color:red\"><b>You suddenly feel very weak.</b></span>")
|
|
src.emote("collapse")
|
|
SPAWN_DBG(8 SECONDS)
|
|
if (!src.getStatusDuration("weakened"))
|
|
src.emote("collapse")
|
|
boutput(src, "<span style=\"color:red\"><b>Your body feels as if it's on fire! You think it's... IT'S CHANGING! You should probably get somewhere private!</b></span>")
|
|
SPAWN_DBG(rand(300, 500))
|
|
src.emote("scream")
|
|
if (!src.getStatusDuration("weakened") && !src.getStatusDuration("paralysis"))
|
|
src.emote("collapse")
|
|
W.addAbility(/datum/targetable/werewolf/werewolf_transform)
|
|
src.werewolf_transform(0, 0) // Not really a fan of this. I wish werewolves all suffered from lycanthropy and that should be how you pass it on, but w/e
|
|
|
|
////////////////////////////////////////////// Helper procs //////////////////////////////
|
|
|
|
// Avoids C&P code for that werewolf disease.
|
|
/mob/proc/werewolf_transform(var/source_is_lycanthrophy = 0, var/message_type = 0)
|
|
if (ishuman(src))
|
|
var/mob/living/carbon/human/M = src
|
|
var/which_way = 0
|
|
|
|
if (!M.mutantrace || source_is_lycanthrophy == 1)
|
|
M.jitteriness = 0
|
|
M.delStatus("stunned")
|
|
M.delStatus("weakened")
|
|
M.delStatus("paralysis")
|
|
M.delStatus("slowed")
|
|
M.delStatus("disorient")
|
|
M.change_misstep_chance(-INFINITY)
|
|
M.stuttering = 0
|
|
M.drowsyness = 0
|
|
|
|
|
|
playsound(M.loc, 'sound/impact_sounds/Slimy_Hit_4.ogg', 50, 1, -1)
|
|
SPAWN_DBG (5)
|
|
if (M && M.mutantrace && istype(M.mutantrace, /datum/mutantrace/werewolf))
|
|
M.emote("howl")
|
|
|
|
M.visible_message("<span style=\"color:red\"><B>[M] [pick("metamorphizes", "transforms", "changes")] into a werewolf! Holy shit!</B></span>")
|
|
if (message_type == 0)
|
|
boutput(M, __blue("<h3>You are now a werewolf.</h3>"))
|
|
else
|
|
boutput(M, __blue("<h3>You are now a werewolf. You can remain in this form indefinitely or change back at any time.</h3>"))
|
|
|
|
if (source_is_lycanthrophy == 1 && M.mutantrace)
|
|
qdel(M.mutantrace)
|
|
|
|
M.set_mutantrace(/datum/mutantrace/werewolf) //this proc handles body updates etc
|
|
|
|
//when in werewolf form, get more max health or regenerate
|
|
// M.maxhealth = 200
|
|
// M.health =
|
|
if (src.bioHolder)
|
|
src.bioHolder.AddEffect("regenerator")
|
|
boutput(src, "<span style=\"color:red\">You will now heal over time!</span>")
|
|
|
|
if (M.handcuffed)
|
|
if (M.handcuffed.werewolf_cant_rip())
|
|
boutput(M, __red("You can't seem to break free from these silver handcuffs."))
|
|
else
|
|
M.visible_message("<span style=\"color:red\"><B>[M] rips apart the handcuffs with pure brute strength!</b></span>")
|
|
qdel(M.handcuffed)
|
|
M.handcuffed = null
|
|
src.update_clothing()
|
|
|
|
which_way = 0
|
|
|
|
else
|
|
if (source_is_lycanthrophy == 1) // Werewolf disease is human -> WW only.
|
|
return
|
|
|
|
boutput(M, __blue("<h3>You transform back into your human form.</h3>"))
|
|
|
|
M.set_mutantrace(null) //this proc handles body updates etc
|
|
|
|
if (src.bioHolder)
|
|
src.bioHolder.RemoveEffect("regenerator")
|
|
boutput(src, "<span style=\"color:red\">You will no longer heal over time!</span>")
|
|
|
|
//Changing back removes all the implants in you, wolves should have a non-surgery way to remove bullets. considering silver is so harmful
|
|
for(var/obj/item/implant/I in M)
|
|
// if (istype(I, /obj/item/implant/projectile))
|
|
boutput(M, "<span style=\"color:red\">\an [I] falls out of your abdomen.</span>")
|
|
I.on_remove(M)
|
|
M.implant.Remove(I)
|
|
I.set_loc(M.loc)
|
|
continue
|
|
|
|
which_way = 1
|
|
|
|
logTheThing("combat", M, null, "[which_way == 0 ? "transforms into a werewolf" : "changes back into human form"] at [log_loc(M)].")
|
|
return
|
|
|
|
// There used to be more stuff here, most of which was moved to limb datums.
|
|
/mob/proc/werewolf_attack(var/mob/target = null, var/attack_type = "")
|
|
if (!iswerewolf(src))
|
|
return 0
|
|
|
|
var/mob/living/carbon/human/M = src
|
|
if (!ishuman(M))
|
|
return 0
|
|
|
|
if (!target || !ismob(target))
|
|
return 0
|
|
|
|
if (target == M)
|
|
return 0
|
|
|
|
if (check_target_immunity(target) == 1)
|
|
target.visible_message("<span style=\"color:red\"><B>[M]'s swipe bounces off of [target] uselessly!</B></span>")
|
|
return 0
|
|
M.werewolf_tainted_saliva_transfer(target)
|
|
|
|
var/damage = 0
|
|
var/send_flying = 0 // 1: a little bit | 2: across the room
|
|
|
|
switch (attack_type)
|
|
if ("feast") // Only used by the feast ability.
|
|
var/mob/living/carbon/human/HH = target
|
|
|
|
if (!HH || !ishuman(HH))
|
|
return 0
|
|
|
|
var/healing = 0
|
|
|
|
damage += rand(5,15)
|
|
healing = damage - 5
|
|
|
|
if (prob(40))
|
|
HH.spread_blood_clothes(HH)
|
|
M.spread_blood_hands(HH)
|
|
|
|
var/obj/decal/cleanable/blood/gibs/G = null // For forensics.
|
|
G = make_cleanable(/obj/decal/cleanable/blood/gibs,HH.loc)
|
|
if (HH.bioHolder && HH.bioHolder.Uid && HH.bioHolder.bloodType)
|
|
G.blood_DNA = HH.bioHolder.Uid
|
|
G.blood_type = HH.bioHolder.bloodType
|
|
|
|
M.visible_message("<span style=\"color:red\"><B>[M] messily [pick("rips", "tears")] out and [pick("eats", "devours", "wolfs down", "chows on")] some of [HH]'s [pick("guts", "intestines", "entrails")]!</B></span>")
|
|
|
|
else
|
|
HH.spread_blood_clothes(HH)
|
|
|
|
M.visible_message("<span style=\"color:red\"><B>[M] [pick("chomps on", "chews off a chunk of", "gnaws on")] [HH]'s [pick("right arm", "left arm", "head", "right leg", "left leg")]!</B></span>")
|
|
|
|
if (ismonkey(HH) || HH.bioHolder && HH.bioHolder.HasEffect("monkey"))
|
|
boutput(M, __red("Monkey flesh just isn't the real deal..."))
|
|
healing /= 2
|
|
else if (isdead(HH))
|
|
boutput(M, __red("Fresh meat would be much preferable to this cadaver..."))
|
|
healing /= 2
|
|
else if (HH.health < -150)
|
|
boutput(M, __red("[target] is pretty mangled. There's not a lot of flesh left..."))
|
|
healing /= 1.5
|
|
else
|
|
if (iscluwne(HH))
|
|
boutput(M, __red("That tasted awful!"))
|
|
healing /= 2
|
|
M.take_toxin_damage(5)
|
|
else if (iswerewolf(HH) || ishunter(HH) || isabomination(HH))
|
|
boutput(M, __blue("That tasted fantastic!"))
|
|
healing *= 2
|
|
else if (HH.nutrition > 100 || HH.bioHolder && HH.bioHolder.HasEffect("fat"))
|
|
boutput(M, __blue("That tasted amazing!"))
|
|
M.unlock_medal("Space Ham", 1)
|
|
healing *= 2
|
|
else if (HH.mind && HH.mind.assigned_role == "Clown")
|
|
boutput(M, __blue("That tasted funny, huh."))
|
|
M.unlock_medal("That tasted funny", 1)
|
|
else
|
|
boutput(M, __blue("That tasted good!"))
|
|
|
|
HH.add_fingerprint(M) // Just put 'em on the mob itself, like pulling does. Simplifies forensic analysis a bit.
|
|
M.werewolf_audio_effects(HH, "feast")
|
|
|
|
HH.changeStatus("weakened", 2 SECONDS)
|
|
if (prob(33) && !isdead(HH))
|
|
HH.emote("scream")
|
|
|
|
M.remove_stamina(60) // Werewolves have a very large stamina and stamina regen boost.
|
|
if (healing > 0)
|
|
M.HealDamage("All", healing, healing)
|
|
M.add_stamina(healing)
|
|
M.updatehealth()
|
|
|
|
if ("spread")
|
|
var/mob/living/carbon/human/HH = target
|
|
if (!HH || !ishuman(HH))
|
|
return 0
|
|
if (!HH.canmove)
|
|
damage += rand(5,15)
|
|
if (prob(40))
|
|
HH.spread_blood_clothes(HH)
|
|
M.spread_blood_hands(HH)
|
|
var/obj/decal/cleanable/blood/gibs/G = null // For forensics.
|
|
G = make_cleanable(/obj/decal/cleanable/blood/gibs, HH.loc)
|
|
if (HH.bioHolder && HH.bioHolder.Uid && HH.bioHolder.bloodType)
|
|
G.blood_DNA = HH.bioHolder.Uid
|
|
G.blood_type = HH.bioHolder.bloodType
|
|
M.visible_message("<span style=\"color:red\"><B>[M] sinks its teeth into [target]! !</B></span>")
|
|
HH.add_fingerprint(M) // Just put 'em on the mob itself, like pulling does. Simplifies forensic analysis a bit.
|
|
M.werewolf_audio_effects(HH, "feast")
|
|
HH.setStatus("weakened",rand(30,60))
|
|
if (prob(70) && HH.stat != 2)
|
|
HH.emote("scream")
|
|
if ("pounce")
|
|
wrestler_knockdown(M, target, 1)
|
|
M.visible_message("<span style=\"color:red\"><B>[M] barrels through the air, slashing [target]!</B></span>")
|
|
damage += rand(2,8)
|
|
playsound(M.loc, pick('sound/voice/animal/werewolf_attack1.ogg', 'sound/voice/animal/werewolf_attack2.ogg', 'sound/voice/animal/werewolf_attack3.ogg'), 50, 1)
|
|
if (prob(33) && target.stat != 2)
|
|
target.emote("scream")
|
|
if ("thrash")
|
|
if (prob(75))
|
|
wrestler_knockdown(M, target, 1)
|
|
damage += rand(2,8)
|
|
else
|
|
wrestler_backfist(M, target)
|
|
damage += rand(5,15)
|
|
|
|
if (prob(60)) playsound(M.loc, pick('sound/voice/animal/werewolf_attack1.ogg', 'sound/voice/animal/werewolf_attack2.ogg', 'sound/voice/animal/werewolf_attack3.ogg'), 50, 1)
|
|
if (prob(75)) target.setStatus("weakened",30)
|
|
if (prob(33) && target.stat != 2)
|
|
target.emote("scream")
|
|
|
|
else
|
|
return 0
|
|
|
|
switch (send_flying)
|
|
if (1)
|
|
wrestler_knockdown(M, target)
|
|
|
|
if (2)
|
|
wrestler_backfist(M, target)
|
|
|
|
if (damage > 0)
|
|
random_brute_damage(target, damage)
|
|
target.updatehealth()
|
|
target.UpdateDamageIcon()
|
|
target.set_clothing_icon_dirty()
|
|
|
|
return 1
|
|
|
|
// Also called by limb datums.
|
|
/mob/proc/werewolf_audio_effects(var/mob/target = null, var/type = "disarm")
|
|
if (!src || !ismob(src) || !target || !ismob(target))
|
|
return
|
|
|
|
var/sound_playing = 0
|
|
|
|
switch (type)
|
|
if ("disarm")
|
|
playsound(src.loc, pick('sound/voice/animal/werewolf_attack1.ogg', 'sound/voice/animal/werewolf_attack2.ogg', 'sound/voice/animal/werewolf_attack3.ogg'), 50, 1)
|
|
SPAWN_DBG (1)
|
|
if (src) playsound(src.loc, "swing_hit", 50, 1)
|
|
|
|
if ("swipe")
|
|
if (prob(50))
|
|
playsound(src.loc, pick('sound/voice/animal/werewolf_attack1.ogg', 'sound/voice/animal/werewolf_attack2.ogg', 'sound/voice/animal/werewolf_attack3.ogg'), 50, 1)
|
|
else
|
|
playsound(src.loc, pick('sound/impact_sounds/Flesh_Tear_1.ogg', 'sound/impact_sounds/Flesh_Tear_2.ogg'), 50, 1, -1)
|
|
|
|
SPAWN_DBG (1)
|
|
if (src) playsound(src.loc, "sound/impact_sounds/Flesh_Tear_3.ogg", 40, 1, -1)
|
|
|
|
if ("feast")
|
|
if (sound_playing == 0) // It's a long audio clip.
|
|
playsound(src.loc, "sound/voice/animal/wendigo_maul.ogg", 80, 1)
|
|
sound_playing = 1
|
|
SPAWN_DBG (60)
|
|
sound_playing = 0
|
|
|
|
playsound(src.loc, pick('sound/impact_sounds/Flesh_Tear_1.ogg', 'sound/impact_sounds/Flesh_Tear_2.ogg'), 50, 1, -1)
|
|
playsound(src.loc, "sound/items/eatfood.ogg", 50, 1, -1)
|
|
if (prob(40))
|
|
playsound(target.loc, "sound/impact_sounds/Slimy_Splat_1.ogg", 50, 1)
|
|
SPAWN_DBG (10)
|
|
if (src && ishuman(src) && prob(50))
|
|
src.emote("burp")
|
|
|
|
return
|
|
|
|
//////////////////////////////////////////// Ability holder /////////////////////////////////////////
|
|
|
|
/obj/screen/ability/topBar/werewolf
|
|
clicked(params)
|
|
var/datum/targetable/werewolf/spell = owner
|
|
if (!istype(spell))
|
|
return
|
|
if (!spell.holder)
|
|
return
|
|
if (!isturf(owner.holder.owner.loc))
|
|
boutput(owner.holder.owner, "<span style=\"color:red\">You can't use this ability here.</span>")
|
|
return
|
|
if (spell.targeted && usr:targeting_spell == owner)
|
|
usr:targeting_spell = null
|
|
usr.update_cursor()
|
|
return
|
|
if (spell.targeted)
|
|
if (world.time < spell.last_cast)
|
|
return
|
|
owner.holder.owner.targeting_spell = owner
|
|
owner.holder.owner.update_cursor()
|
|
else
|
|
SPAWN_DBG(0)
|
|
spell.handleCast()
|
|
return
|
|
|
|
/datum/abilityHolder/werewolf
|
|
usesPoints = 0
|
|
regenRate = 0
|
|
tabName = "Werewolf"
|
|
notEnoughPointsMessage = "<span style=\"color:red\">You aren't strong enough to use this ability.</span>"
|
|
var/datum/objective/specialist/werewolf/feed/feed_objective = null
|
|
var/datum/reagents/tainted_saliva_reservior = null
|
|
var/awaken_time //don't really need this here, but admins might want to know when the werewolf's awaken time is.
|
|
|
|
New()
|
|
..()
|
|
awaken_time = rand(5, 10)*100
|
|
src.tainted_saliva_reservior = new/datum/reagents(500)
|
|
|
|
onAbilityStat() // In the 'Werewolf' tab.
|
|
..()
|
|
|
|
if (src.owner && src.owner.mind && src.owner.mind.special_role == "werewolf")
|
|
for (var/datum/objective/specialist/werewolf/feed/O in src.owner.mind.objectives)
|
|
src.feed_objective = O
|
|
|
|
if (src.feed_objective && istype(src.feed_objective))
|
|
stat("No. of victims:", src.feed_objective.feed_count)
|
|
|
|
return
|
|
|
|
//percent, give number 0.0-1.0
|
|
/datum/abilityHolder/proc/lower_cooldowns(var/percent)
|
|
for (var/datum/targetable/werewolf/A in src.abilities)
|
|
A.cooldown = A.cooldown * (1-percent)
|
|
|
|
/////////////////////////////////////////////// Werewolf spell parent ////////////////////////////
|
|
|
|
/datum/targetable/werewolf
|
|
icon = 'icons/mob/werewolf_ui.dmi'
|
|
icon_state = "template" // No custom sprites yet.
|
|
cooldown = 0
|
|
last_cast = 0
|
|
pointCost = 0
|
|
preferred_holder_type = /datum/abilityHolder/werewolf
|
|
var/when_stunned = 0 // 0: Never | 1: Ignore mob.stunned and mob.weakened | 2: Ignore all incapacitation vars
|
|
var/not_when_handcuffed = 0
|
|
var/werewolf_only = 0
|
|
|
|
New()
|
|
..()
|
|
var/obj/screen/ability/topBar/werewolf/B = new /obj/screen/ability/topBar/werewolf(null)
|
|
B.icon = src.icon
|
|
B.icon_state = src.icon_state
|
|
B.owner = src
|
|
B.name = src.name
|
|
B.desc = src.desc
|
|
src.object = B
|
|
return
|
|
|
|
updateObject()
|
|
..()
|
|
if (!src.object)
|
|
src.object = new /obj/screen/ability/topBar/werewolf()
|
|
object.icon = src.icon
|
|
object.owner = src
|
|
if (src.last_cast > world.time)
|
|
var/pttxt = ""
|
|
if (pointCost)
|
|
pttxt = " \[[pointCost]\]"
|
|
object.name = "[src.name][pttxt] ([round((src.last_cast-world.time)/10)])"
|
|
object.icon_state = src.icon_state + "_cd"
|
|
else
|
|
var/pttxt = ""
|
|
if (pointCost)
|
|
pttxt = " \[[pointCost]\]"
|
|
object.name = "[src.name][pttxt]"
|
|
object.icon_state = src.icon_state
|
|
return
|
|
|
|
proc/incapacitation_check(var/stunned_only_is_okay = 0)
|
|
if (!holder)
|
|
return 0
|
|
|
|
var/mob/living/M = holder.owner
|
|
if (!M || !ismob(M))
|
|
return 0
|
|
|
|
switch (stunned_only_is_okay)
|
|
if (0)
|
|
if (!isalive(M) || M.getStatusDuration("stunned") > 0 || M.getStatusDuration("paralysis") > 0 || M.getStatusDuration("weakened"))
|
|
return 0
|
|
else
|
|
return 1
|
|
if (1)
|
|
if (!isalive(M) || M.getStatusDuration("paralysis") > 0)
|
|
return 0
|
|
else
|
|
return 1
|
|
else
|
|
return 1
|
|
|
|
castcheck()
|
|
if (!holder)
|
|
return 0
|
|
|
|
var/mob/living/carbon/human/M = holder.owner
|
|
|
|
if (!M)
|
|
return 0
|
|
|
|
if (!ishuman(M)) // Only humans use mutantrace datums.
|
|
boutput(M, __red("You cannot use any powers in your current form."))
|
|
return 0
|
|
|
|
if (M.transforming)
|
|
boutput(M, __red("You can't use any powers right now."))
|
|
return 0
|
|
|
|
if (werewolf_only == 1 && !iswerewolf(M))
|
|
boutput(M, __red("You must be in your wolf form to use this ability."))
|
|
return 0
|
|
|
|
if (incapacitation_check(src.when_stunned) != 1)
|
|
boutput(M, __red("You can't use this ability while incapacitated!"))
|
|
return 0
|
|
|
|
if (src.not_when_handcuffed == 1 && M.restrained())
|
|
boutput(M, __red("You can't use this ability when restrained!"))
|
|
return 0
|
|
|
|
return 1
|
|
|
|
cast(atom/target)
|
|
. = ..()
|
|
actions.interrupt(holder.owner, INTERRUPT_ACT)
|
|
return |