mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 05:00:55 +01:00
The power of love: Obsessed are slightly stronger about their obsession (+ modernization) (#96421)
## About The Pull Request Notable changes: - Obsessed get a slight strength buff related to their obsession - When grabbing any of your targets (your obsession included), your grab is harder to escape from, and cause (slightly) more stamina damage from failed escapes - Obsessed also get a slight strength buff while standing near their obsession (for at least 20 seconds). This effect is applied instantly if the obsession is incapacitated. - The grab strength bonus applies to all mobs, rather than just your targets - You get a 25% reduction to incoming stamina damage - Psicodine or Alcohol will prevent Obsession stammering / emote tells, but also prevents these beneficial effects from activating - Witnessing the death of your obsession grants you a permanent positive moodlet (until they are revived) - Obsessed tells have been reworked a bit, being less obvious in general and having their chance to trigger reduced the longer you stand around your obsession (resetting after leaving their view) - Obsessed stammer was also reworked a bit, now being closet to Social Anxiety's stammer, also reducing in severity the longer you stand around your obsession (resetting after leaving their view) - Obsessed antag status is now lost when transferring out of the affected brain and gained when transferring into the affected brain, making it play nicer with slime clones or mindswap Other changes: - Modernized a bunch of obsession code - Replaced hardcoded checks for grab resistance with a signal ## Why It's Good For The Game I wanted to give Obsession just a *little* something to help enable them. I figure grab strength and pain resistance is a pretty fitting / flavorful place to start. The grab strength is intended to give them more ability to detain their targets (by means of wrestling). Even a passive grab isn't a guaranteed escape. The stamina damage resistance is intended to give them a slight chance to resist being detained by security. Not a significant one, just a slight one. ## Changelog 🆑 Melbert balance: Obsessions get moderately stronger grabs against their objective targets (this includes their obsession and any other kill/theft targets they may have). balance: While standing near their obsession (for 20 seconds or if they are incapacitated), Obsessions get slightly stronger grabs against anyone, rather than just their objective targets. balance: While standing near their obsession (for 20 seconds or if they are incapacitated), Obsessions get slight stamina damage resistance. balance: Obsession stammering has been reduced in severity a bit, and also becomes less likely the longer you stand around your obsession. balance: Obsession emotes have been made a bit less obvious, and also become less likely the longer you stand around your obsession. balance: Taking Psicodine or drinking alcohol will prevent Obsessions from stammering or emoting around their obsession, but also disables their stamina resistance and stronger grabs. balance: Obsessions get a "permanent" mood bonus for killing their obsession (until they are revived). refactor: Obsessed has been refactored significantly. It will now play a lot nicer with mindswaps. Report any oddities. refactor: Grab strength has been refactored a bit, primarily affects martial arts and drunken brawling. Report any oddities. /🆑
This commit is contained in:
@@ -86,6 +86,11 @@
|
||||
#define COMSIG_MOVABLE_SET_ANCHORED "movable_set_anchored"
|
||||
///from base of atom/movable/setGrabState(): (newstate)
|
||||
#define COMSIG_MOVABLE_SET_GRAB_STATE "living_set_grab_state"
|
||||
/// from base of mob/living/resist_grab(), to the grabber: (mob/living/grabbed, list/grab_stats)
|
||||
#define COMSIG_MOVABLE_GRABBED_RESISTING "movable_grabbed_resisting"
|
||||
#define GRAB_STAT_EFFECTIVE_STATE 1
|
||||
#define GRAB_STAT_FAIL_DAMAGE 2
|
||||
#define GRAB_STAT_ESCAPE_CHANCE 3
|
||||
///called when the movable's glide size is updated: (new_glide_size)
|
||||
#define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size"
|
||||
///Called when a movable is hit by a plunger in layer mode, from /obj/item/plunger/attack_atom()
|
||||
|
||||
@@ -10,14 +10,20 @@
|
||||
can_gain = TRUE
|
||||
random_gain = FALSE
|
||||
resilience = TRAUMA_RESILIENCE_LOBOTOMY
|
||||
var/mob/living/obsession
|
||||
var/datum/objective/spendtime/attachedobsessedobj
|
||||
var/datum/antagonist/obsessed/antagonist
|
||||
var/viewing = FALSE //it's a lot better to store if the owner is watching the obsession than checking it twice between two procs
|
||||
|
||||
var/total_time_creeping = 0 SECONDS //just for round end fun
|
||||
var/time_spent_away = 0 SECONDS
|
||||
var/obsession_hug_count = 0
|
||||
/// Reference to the actual mob we're obsessed with
|
||||
VAR_FINAL/mob/living/obsession
|
||||
/// Reference to our antag datum
|
||||
VAR_FINAL/datum/antagonist/obsessed/antagonist
|
||||
/// Tracks if the target is currently in view
|
||||
VAR_FINAL/viewing = FALSE
|
||||
/// Tracks the total amount of time spend near the target
|
||||
VAR_FINAL/total_time_creeping = 0 SECONDS
|
||||
/// Tracks the current period of time spent away from the target (resetting when near)
|
||||
VAR_FINAL/time_spent_away = 0 SECONDS
|
||||
/// Tracks the current period of time spent near the target (resetting when away)
|
||||
VAR_FINAL/time_spend_creeping = 0 SECONDS
|
||||
/// If we've seen our obsession be dead
|
||||
VAR_FINAL/witnessed_death = FALSE
|
||||
|
||||
/datum/brain_trauma/special/obsessed/on_gain()
|
||||
//setup, linking, etc//
|
||||
@@ -26,66 +32,90 @@
|
||||
if(!obsession)//we didn't find one
|
||||
lose_text = ""
|
||||
return FALSE
|
||||
|
||||
gain_text = span_warning("You hear a sickening, raspy voice in your head. It wants one small task of you...")
|
||||
owner.mind.add_antag_datum(/datum/antagonist/obsessed)
|
||||
antagonist = owner.mind.has_antag_datum(/datum/antagonist/obsessed)
|
||||
antagonist = owner.mind.add_antag_datum(/datum/antagonist/obsessed)
|
||||
antagonist.trauma = src
|
||||
RegisterSignal(obsession, COMSIG_MOB_EYECONTACT, PROC_REF(stare))
|
||||
RegisterSignal(obsession, COMSIG_QDELETING, PROC_REF(obession_deleted))
|
||||
. = ..()
|
||||
//antag stuff//
|
||||
antagonist.forge_objectives(obsession.mind)
|
||||
antagonist.greet()
|
||||
log_game("[key_name(antagonist)] has developed an obsession with [key_name(obsession)].")
|
||||
RegisterSignal(owner, COMSIG_CARBON_HELPED, PROC_REF(on_hug))
|
||||
RegisterSignal(owner, COMSIG_MOVABLE_GRABBED_RESISTING, PROC_REF(grab_resisting))
|
||||
RegisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, PROC_REF(on_damage_mod))
|
||||
RegisterSignal(owner, COMSIG_MOB_MIND_TRANSFERRED_OUT_OF, PROC_REF(on_mind_lost))
|
||||
RegisterSignal(owner, COMSIG_MOB_MIND_TRANSFERRED_INTO, PROC_REF(on_mind_gain))
|
||||
owner.apply_status_effect(/datum/status_effect/desensitized, REF(src), DESENSITIZED_THRESHOLD)
|
||||
owner.apply_status_effect(/datum/status_effect/speech/stutter/obsession, INFINITY)
|
||||
|
||||
/datum/brain_trauma/special/obsessed/on_life(seconds_per_tick)
|
||||
if(!obsession || obsession.stat == DEAD)
|
||||
viewing = FALSE//important, makes sure you no longer stutter when happy if you murdered them while viewing
|
||||
return
|
||||
if(get_dist(get_turf(owner), get_turf(obsession)) > 7)
|
||||
viewing = FALSE //they are further than our view range they are not viewing us
|
||||
out_of_view()
|
||||
return//so we're not searching everything in view every tick
|
||||
if(obsession in view(7, owner))
|
||||
viewing = TRUE
|
||||
else
|
||||
if(isnull(obsession))
|
||||
viewing = FALSE
|
||||
if(viewing)
|
||||
owner.add_mood_event("creeping", /datum/mood_event/creeping, obsession.name)
|
||||
total_time_creeping += seconds_per_tick SECONDS
|
||||
time_spent_away = 0 SECONDS
|
||||
if(attachedobsessedobj)//if an objective needs to tick down, we can do that since traumas coexist with the antagonist datum
|
||||
attachedobsessedobj.timer -= seconds_per_tick SECONDS //mob subsystem ticks every 2 seconds(?), remove 20 deciseconds from the timer. sure, that makes sense.
|
||||
else
|
||||
out_of_view()
|
||||
return
|
||||
|
||||
/datum/brain_trauma/special/obsessed/proc/out_of_view()
|
||||
time_spent_away += 2 SECONDS
|
||||
if(time_spent_away > 3 MINUTES) //3 minutes
|
||||
owner.add_mood_event("creeping", /datum/mood_event/notcreepingsevere, obsession.name)
|
||||
else
|
||||
owner.add_mood_event("creeping", /datum/mood_event/notcreeping, obsession.name)
|
||||
// viewing needs to be updated regardless of the obsession's state
|
||||
viewing = IN_GIVEN_RANGE(owner, obsession, 7) && (owner in viewers(7, obsession))
|
||||
if(!viewing)
|
||||
if(witnessed_death)
|
||||
return
|
||||
|
||||
time_spent_away += seconds_per_tick SECONDS
|
||||
time_spend_creeping = 0 SECONDS
|
||||
if(time_spent_away > 3 MINUTES) //3 minutes
|
||||
owner.add_mood_event("creeping", /datum/mood_event/notcreepingsevere, obsession.name)
|
||||
else
|
||||
owner.add_mood_event("creeping", /datum/mood_event/notcreeping, obsession.name)
|
||||
return
|
||||
|
||||
if(obsession.stat == DEAD)
|
||||
if(!witnessed_death)
|
||||
witnessed_death = TRUE
|
||||
owner.add_mood_event("creeping", /datum/mood_event/creeping/dead)
|
||||
return
|
||||
|
||||
witnessed_death = FALSE
|
||||
owner.add_mood_event("creeping", /datum/mood_event/creeping, obsession.name)
|
||||
total_time_creeping += seconds_per_tick SECONDS
|
||||
time_spend_creeping += seconds_per_tick SECONDS
|
||||
time_spent_away = 0 SECONDS
|
||||
for(var/datum/objective/spendtime/objective in antagonist?.objectives)
|
||||
objective.timer -= seconds_per_tick SECONDS
|
||||
|
||||
/datum/brain_trauma/special/obsessed/on_lose()
|
||||
..()
|
||||
. = ..()
|
||||
antagonist?.trauma = null
|
||||
antagonist = null
|
||||
if (owner.mind.remove_antag_datum(/datum/antagonist/obsessed))
|
||||
owner.mind.add_antag_datum(/datum/antagonist/former_obsessed)
|
||||
owner.clear_mood_event("creeping")
|
||||
if(obsession)
|
||||
if(!isnull(obsession))
|
||||
log_game("[key_name(owner)] is no longer obsessed with [key_name(obsession)].")
|
||||
UnregisterSignal(obsession, COMSIG_MOB_EYECONTACT)
|
||||
UnregisterSignal(obsession, list(
|
||||
COMSIG_MOB_EYECONTACT,
|
||||
COMSIG_QDELETING,
|
||||
))
|
||||
obsession = null
|
||||
|
||||
UnregisterSignal(owner, list(
|
||||
COMSIG_CARBON_HELPED,
|
||||
COMSIG_MOVABLE_GRABBED_RESISTING,
|
||||
COMSIG_MOB_APPLY_DAMAGE_MODIFIERS,
|
||||
COMSIG_MOB_MIND_TRANSFERRED_INTO,
|
||||
COMSIG_MOB_MIND_TRANSFERRED_OUT_OF,
|
||||
))
|
||||
owner.remove_status_effect(/datum/status_effect/desensitized, REF(src))
|
||||
owner.remove_status_effect(/datum/status_effect/speech/stutter/obsession)
|
||||
|
||||
/datum/brain_trauma/special/obsessed/proc/obession_deleted(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
obsession = null
|
||||
|
||||
/datum/brain_trauma/special/obsessed/handle_speech(datum/source, list/speech_args)
|
||||
if(!viewing)
|
||||
return
|
||||
if(prob(25)) // 25% chances to be nervous and stutter.
|
||||
if(prob(50)) // 12.5% chance (previous check taken into account) of doing something suspicious.
|
||||
addtimer(CALLBACK(src, PROC_REF(on_failed_social_interaction)), rand(1 SECONDS, 3 SECONDS))
|
||||
else if(!owner.has_status_effect(/datum/status_effect/speech/stutter))
|
||||
to_chat(owner, span_warning("Being near [obsession] makes you nervous and you begin to stutter..."))
|
||||
owner.set_stutter_if_lower(6 SECONDS)
|
||||
if(viewing && !witnessed_death && prob(12 * max(1 - (time_spend_creeping / (40 SECONDS)), 0.02) ))
|
||||
addtimer(CALLBACK(src, PROC_REF(do_something_nervous)), rand(1 SECONDS, 3 SECONDS))
|
||||
|
||||
/// Singal proc for [COMSIG_CARBON_HELPED], when our obsessed helps (hugs) our obsession, increases hug count
|
||||
/datum/brain_trauma/special/obsessed/proc/on_hug(datum/source, mob/living/hugged)
|
||||
@@ -94,26 +124,70 @@
|
||||
if(hugged != obsession)
|
||||
return
|
||||
|
||||
obsession_hug_count++
|
||||
for(var/datum/objective/hug/objective in antagonist?.objectives)
|
||||
objective.hugs_needed -= 1
|
||||
|
||||
/datum/brain_trauma/special/obsessed/proc/on_failed_social_interaction()
|
||||
if(QDELETED(owner) || owner.stat >= UNCONSCIOUS)
|
||||
/// Signal proc for [COMSIG_MOVABLE_GRABBED_RESISTING] to improve the obsessed's grabs
|
||||
/datum/brain_trauma/special/obsessed/proc/grab_resisting(datum/source, mob/living/grabbed, list/grab_stats)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(HAS_TRAIT(owner, TRAIT_FEARLESS))
|
||||
return
|
||||
switch(rand(1, 100))
|
||||
if(1 to 40)
|
||||
INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob, emote), pick("blink", "blink_r"))
|
||||
owner.set_eye_blur_if_lower(20 SECONDS)
|
||||
to_chat(owner, span_userdanger("You sweat profusely and have a hard time focusing..."))
|
||||
if(41 to 80)
|
||||
INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob, emote), "pale")
|
||||
shake_camera(owner, 15, 1)
|
||||
owner.adjust_stamina_loss(70)
|
||||
to_chat(owner, span_userdanger("You feel your heart lurching in your chest..."))
|
||||
if(81 to 100)
|
||||
|
||||
var/list/datum/mind/all_targets = list()
|
||||
for(var/datum/objective/objective in antagonist?.objectives)
|
||||
if(isnull(objective.target))
|
||||
continue
|
||||
all_targets |= objective.target
|
||||
|
||||
// We always grab our obsession, or similar targets, with extra strength
|
||||
if(grabbed == obsession || (grabbed.mind in all_targets))
|
||||
grab_stats[GRAB_STAT_EFFECTIVE_STATE] += 1
|
||||
grab_stats[GRAB_STAT_FAIL_DAMAGE] += 5
|
||||
|
||||
// If we're hanging with our obsession for a while, the bonus applies to any mob (though to a lesser extent)
|
||||
else if(is_defensive())
|
||||
grab_stats[GRAB_STAT_EFFECTIVE_STATE] += 1
|
||||
grab_stats[GRAB_STAT_ESCAPE_CHANCE] += 10 // 10% EASIER to escape
|
||||
|
||||
/// Signal proc for [COMSIG_MOB_APPLY_DAMAGE_MODIFIERS], take less stamina damage when near our obsession
|
||||
/datum/brain_trauma/special/obsessed/proc/on_damage_mod(datum/source, list/damage_mods, damage, damage_type, ...)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(HAS_TRAIT(owner, TRAIT_FEARLESS))
|
||||
return
|
||||
|
||||
if(damage_type == STAMINA && is_defensive())
|
||||
damage_mods += 0.75
|
||||
|
||||
/// Checks if if we're being defensive over our obsession
|
||||
/datum/brain_trauma/special/obsessed/proc/is_defensive()
|
||||
if(time_spend_creeping >= 20 SECONDS)
|
||||
return TRUE
|
||||
if(obsession.stat >= UNCONSCIOUS)
|
||||
return (owner in viewers(7, obsession))
|
||||
return FALSE
|
||||
|
||||
/datum/brain_trauma/special/obsessed/proc/do_something_nervous()
|
||||
if(QDELETED(owner) || owner.stat >= UNCONSCIOUS || HAS_TRAIT(owner, TRAIT_FEARLESS))
|
||||
return
|
||||
|
||||
|
||||
switch(rand(1, 10))
|
||||
if(1 to 4)
|
||||
owner.adjust_jitter_up_to(10 SECONDS, 20 SECONDS)
|
||||
owner.adjust_dizzy_up_to(10 SECONDS, 20 SECONDS)
|
||||
to_chat(owner, span_warning("You feel a bit nervous."))
|
||||
if(5 to 8)
|
||||
INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob, emote), "cough")
|
||||
owner.adjust_dizzy(20 SECONDS)
|
||||
owner.adjust_disgust(5)
|
||||
to_chat(owner, span_userdanger("You gag and swallow a bit of bile..."))
|
||||
to_chat(owner, span_warning("You clear your throat."))
|
||||
if(9)
|
||||
INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob, emote), "laugh")
|
||||
to_chat(owner, span_warning("You chuckle nervously."))
|
||||
if(10)
|
||||
INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob, emote), "blink")
|
||||
owner.adjust_eye_blur_up_to(10 SECONDS, 20 SECONDS)
|
||||
to_chat(owner, span_warning("You forget to blink for a moment."))
|
||||
|
||||
// if the creep examines first, then the obsession examines them, have a 50% chance to possibly blow their cover. wearing a mask avoids this risk
|
||||
/datum/brain_trauma/special/obsessed/proc/stare(datum/source, mob/living/examining_mob, triggering_examiner)
|
||||
@@ -125,36 +199,40 @@
|
||||
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), obsession, span_warning("You catch [examining_mob] staring at you..."), 3))
|
||||
return COMSIG_BLOCK_EYECONTACT
|
||||
|
||||
/datum/brain_trauma/special/obsessed/proc/on_mind_lost(datum/source, mob/new_body, datum/mind/the_mind)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
INVOKE_ASYNC(antagonist, TYPE_PROC_REF(/datum/antagonist, store_datum))
|
||||
|
||||
/datum/brain_trauma/special/obsessed/proc/on_mind_gain(datum/source, mob/new_body, datum/mind/the_mind)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
INVOKE_ASYNC(antagonist, TYPE_PROC_REF(/datum/antagonist, restore_datum), the_mind)
|
||||
|
||||
/datum/brain_trauma/special/obsessed/proc/find_obsession()
|
||||
var/list/viable_minds = list() //The first list, which excludes hijinks
|
||||
var/list/possible_targets = list() //The second list, which filters out silicons and simplemobs
|
||||
var/static/list/trait_obsessions = list(
|
||||
var/list/generic_pool = list()
|
||||
var/list/special_pool = list()
|
||||
|
||||
var/list/trait_obsessions = list(
|
||||
JOB_MIME = TRAIT_MIME_FAN,
|
||||
JOB_CLOWN = TRAIT_CLOWN_ENJOYER,
|
||||
JOB_CHAPLAIN = TRAIT_SPIRITUAL,
|
||||
) // Jobs and their corresponding quirks
|
||||
var/list/special_pool = list() //The special list, for quirk-based
|
||||
var/chosen_victim //The obsession target
|
||||
)
|
||||
|
||||
for(var/mob/player as anything in GLOB.player_list)//prevents crew members falling in love with nuke ops they never met, and other annoying hijinks
|
||||
if(!player.client || !player.mind || isnewplayer(player) || player.stat == DEAD || isbrain(player) || player == owner)
|
||||
for(var/datum/mind/crewmember as anything in get_crewmember_minds())
|
||||
if(!ishuman(crewmember.current) || crewmember.current.stat == DEAD || !GET_CLIENT(crewmember.current))
|
||||
continue
|
||||
if(!(player.mind.assigned_role.job_flags & JOB_CREW_MEMBER))
|
||||
continue
|
||||
viable_minds += player.mind
|
||||
for(var/datum/mind/possible_target as anything in viable_minds)
|
||||
if(possible_target != owner && ishuman(possible_target.current))
|
||||
var/job = possible_target.assigned_role.title
|
||||
if (trait_obsessions[job] != null && HAS_TRAIT(owner, trait_obsessions[job]))
|
||||
special_pool += possible_target.current
|
||||
possible_targets += possible_target.current
|
||||
|
||||
//Do we have any special target?
|
||||
var/job = crewmember.assigned_role.title
|
||||
if (trait_obsessions[job] && HAS_MIND_TRAIT(owner, trait_obsessions[job]))
|
||||
special_pool += crewmember.current
|
||||
|
||||
generic_pool += crewmember.current
|
||||
|
||||
if(length(special_pool))
|
||||
chosen_victim = pick(special_pool)
|
||||
return chosen_victim
|
||||
return pick(special_pool)
|
||||
|
||||
//If not, pick any other ordinary target
|
||||
if(possible_targets.len > 0)
|
||||
chosen_victim = pick(possible_targets)
|
||||
return chosen_victim
|
||||
if(length(generic_pool))
|
||||
return pick(generic_pool)
|
||||
|
||||
return null
|
||||
|
||||
@@ -119,6 +119,17 @@
|
||||
|
||||
return grab_act(source, grabbing)
|
||||
|
||||
/// Signal proc for [COMSIG_MOVABLE_GRABBED_RESISTING] to modify grab stats
|
||||
/datum/martial_art/proc/grabbed_resisting(mob/living/source, mob/living/grabbed, list/grab_stats)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!can_use(source))
|
||||
return
|
||||
|
||||
grab_stats[GRAB_STAT_EFFECTIVE_STATE] += grab_state_modifier
|
||||
grab_stats[GRAB_STAT_FAIL_DAMAGE] += grab_damage_modifier
|
||||
grab_stats[GRAB_STAT_ESCAPE_CHANCE] += grab_escape_chance_modifier
|
||||
|
||||
/**
|
||||
* Called when help-intenting on someone
|
||||
*
|
||||
@@ -384,6 +395,7 @@
|
||||
active = TRUE
|
||||
RegisterSignal(new_holder, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(unarmed_strike))
|
||||
RegisterSignal(new_holder, COMSIG_LIVING_GRAB, PROC_REF(attempt_grab))
|
||||
RegisterSignal(new_holder, COMSIG_MOVABLE_GRABBED_RESISTING, PROC_REF(grabbed_resisting))
|
||||
RegisterSignals(new_holder, list(COMSIG_LIVING_TABLE_SLAMMING, COMSIG_LIVING_TABLE_LIMB_SLAMMING), PROC_REF(smash_table))
|
||||
if(display_combos)
|
||||
RegisterSignal(new_holder, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
|
||||
@@ -396,7 +408,14 @@
|
||||
/datum/martial_art/proc/deactivate_style(mob/living/remove_from)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
active = FALSE
|
||||
UnregisterSignal(remove_from, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_LIVING_GRAB, COMSIG_LIVING_TABLE_SLAMMING, COMSIG_LIVING_TABLE_LIMB_SLAMMING))
|
||||
UnregisterSignal(remove_from, list(
|
||||
COMSIG_LIVING_UNARMED_ATTACK,
|
||||
COMSIG_LIVING_GRAB,
|
||||
COMSIG_LIVING_TABLE_SLAMMING,
|
||||
COMSIG_LIVING_TABLE_LIMB_SLAMMING,
|
||||
COMSIG_MOVABLE_GRABBED_RESISTING,
|
||||
COMSIG_MOB_HUD_CREATED,
|
||||
))
|
||||
remove_from.hud_used?.remove_screen_object(HUD_MOB_COMBO)
|
||||
|
||||
///Gives the owner of the martial art the combo HUD.
|
||||
|
||||
@@ -30,16 +30,20 @@
|
||||
/datum/mind/proc/remove_antag_datum(datum_type)
|
||||
if(!datum_type)
|
||||
return
|
||||
var/datum/antagonist/A = has_antag_datum(datum_type)
|
||||
if(A)
|
||||
A.on_removal()
|
||||
current?.log_message("has lost antag datum [A.name]([A.type]).", LOG_GAME)
|
||||
return TRUE
|
||||
var/datum/antagonist/antag = has_antag_datum(datum_type)
|
||||
if(isnull(antag))
|
||||
return
|
||||
|
||||
antag.on_removal()
|
||||
qdel(antag)
|
||||
current?.log_message("has lost antag datum [antag] ([antag.type]).", LOG_GAME)
|
||||
return TRUE
|
||||
|
||||
/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us.
|
||||
for(var/a in antag_datums)
|
||||
var/datum/antagonist/A = a
|
||||
A.on_removal()
|
||||
for(var/datum/antagonist/antag as anything in antag_datums)
|
||||
antag.on_removal()
|
||||
qdel(antag)
|
||||
|
||||
current?.log_message("has lost all antag datums.", LOG_GAME)
|
||||
|
||||
/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE)
|
||||
|
||||
@@ -230,6 +230,10 @@
|
||||
timeout = 3 SECONDS
|
||||
hidden = TRUE
|
||||
|
||||
/datum/mood_event/creeping/dead
|
||||
mood_change = 8
|
||||
timeout = 0
|
||||
|
||||
/datum/mood_event/revolution
|
||||
description = "VIVA LA REVOLUTION!"
|
||||
mood_change = 3
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
owner.add_mood_event(id, /datum/mood_event/drunk, drunk_value)
|
||||
owner.clear_mood_event("[id]_after")
|
||||
RegisterSignal(owner, COMSIG_MOB_FIRED_GUN, PROC_REF(drunk_gun_fired))
|
||||
RegisterSignal(owner, COMSIG_MOVABLE_GRABBED_RESISTING, PROC_REF(grabbed_resisting))
|
||||
|
||||
/datum/status_effect/inebriated/drunk/on_remove()
|
||||
clear_effects()
|
||||
@@ -125,6 +126,7 @@
|
||||
owner.sound_environment_override = SOUND_ENVIRONMENT_NONE
|
||||
|
||||
UnregisterSignal(owner, COMSIG_MOB_FIRED_GUN)
|
||||
UnregisterSignal(owner, COMSIG_MOVABLE_GRABBED_RESISTING)
|
||||
REMOVE_TRAIT(owner, TRAIT_FEARLESS, TRAIT_STATUS_EFFECT(id))
|
||||
|
||||
/datum/status_effect/inebriated/drunk/proc/drunk_gun_fired(datum/source, obj/item/gun/gun, atom/firing_at, params, zone, bonus_spread_values)
|
||||
@@ -138,6 +140,15 @@
|
||||
return
|
||||
bonus_spread_values[MAX_BONUS_SPREAD_INDEX] += (drunk_value * 0.5)
|
||||
|
||||
/datum/status_effect/inebriated/drunk/proc/grabbed_resisting(datum/source, mob/living/grabbed, list/grab_stats)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!HAS_TRAIT(owner, TRAIT_DRUNKEN_BRAWLER))
|
||||
return
|
||||
|
||||
grab_stats[GRAB_STAT_EFFECTIVE_STATE] += 1
|
||||
grab_stats[GRAB_STAT_FAIL_DAMAGE] += clamp((owner.get_fire_loss() + owner.get_brute_loss()) / 10, 3, 20)
|
||||
|
||||
/datum/status_effect/inebriated/drunk/set_drunk_value(set_to)
|
||||
. = ..()
|
||||
if(QDELETED(src))
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
make_tts_message_original = TRUE
|
||||
tts_filter = "tremolo=f=10:d=0.8,rubberband=tempo=0.5"
|
||||
|
||||
/// The probability of adding a stutter to any character
|
||||
/// The probability of adding a stutter to any word
|
||||
var/stutter_prob = 75
|
||||
/// The chance of a four character stutter
|
||||
var/four_char_chance = 10
|
||||
@@ -117,6 +117,52 @@
|
||||
stutter_prob = clamp(host_quirk?.calculate_mood_mod() * 0.5, 5, 50)
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/speech/stutter/obsession
|
||||
id = "obsession_stutter"
|
||||
stutter_prob = 25
|
||||
four_char_chance = 5
|
||||
three_char_chance = 25
|
||||
two_char_chance = 100
|
||||
remove_on_fullheal = FALSE
|
||||
|
||||
COOLDOWN_DECLARE(stutter_cooldown)
|
||||
|
||||
/datum/status_effect/speech/stutter/obsession/handle_message(datum/source, list/message_args)
|
||||
if(should_stutter())
|
||||
var/datum/brain_trauma/special/obsessed/obsession_trauma = get_obsession()
|
||||
stutter_prob = initial(stutter_prob)
|
||||
four_char_chance = initial(four_char_chance) * max(1 - (obsession_trauma.time_spend_creeping / (10 SECONDS)), 0.01)
|
||||
three_char_chance = initial(three_char_chance) * max(1 - (obsession_trauma.time_spend_creeping / (20 SECONDS)), 0.01)
|
||||
two_char_chance = initial(two_char_chance) * max(1 - (obsession_trauma.time_spend_creeping / (40 SECONDS)), 0.01)
|
||||
else
|
||||
stutter_prob = 0
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/speech/stutter/obsession/apply_speech(original_word, index)
|
||||
. = ..()
|
||||
if(. == original_word || !should_stutter())
|
||||
return
|
||||
var/datum/brain_trauma/special/obsessed/obsession_trauma = get_obsession()
|
||||
to_chat(owner, span_warning("Being near [obsession_trauma.obsession.real_name] makes you nervous and stutter..."))
|
||||
COOLDOWN_START(src, stutter_cooldown, rand(4, 8) SECONDS)
|
||||
|
||||
/datum/status_effect/speech/stutter/obsession/proc/get_obsession()
|
||||
if(!ishuman(owner))
|
||||
return null
|
||||
|
||||
var/mob/living/carbon/human/human_owner = owner
|
||||
return human_owner.has_trauma_type(/datum/brain_trauma/special/obsessed)
|
||||
|
||||
/datum/status_effect/speech/stutter/obsession/proc/should_stutter()
|
||||
if(HAS_TRAIT(owner, TRAIT_FEARLESS))
|
||||
return FALSE
|
||||
if(!COOLDOWN_FINISHED(src, stutter_cooldown))
|
||||
return FALSE
|
||||
var/datum/brain_trauma/special/obsessed/obsession_trauma = get_obsession()
|
||||
if(isnull(obsession_trauma) || !obsession_trauma.viewing || obsession_trauma.witnessed_death)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/status_effect/speech/stutter/derpspeech
|
||||
id = "derp_stutter"
|
||||
/// The probability of making our message entirely uppercase + adding exclamations
|
||||
|
||||
@@ -343,7 +343,6 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
SEND_SIGNAL(owner, COMSIG_ANTAGONIST_REMOVED, src)
|
||||
if(owner.current)
|
||||
SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAGONIST_REMOVED, src)
|
||||
qdel(src)
|
||||
|
||||
/**
|
||||
* Proc that sends fluff or instructional messages to the player when they are given this antag datum.
|
||||
@@ -628,3 +627,34 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
/// Return TRUE to prevent the antag's job from handling the respawn
|
||||
/datum/antagonist/proc/on_respawn(mob/new_character)
|
||||
return FALSE
|
||||
|
||||
/// Dissassociates the antag datum from its owner, without deleting it - allowing one datum and its objectives to be reused for another mind
|
||||
/datum/antagonist/proc/store_datum()
|
||||
if(isnull(owner))
|
||||
stack_trace("Tried to store an antagonist datum that already has no owner.")
|
||||
return FALSE
|
||||
|
||||
on_removal()
|
||||
var/datum/team/antag_team = get_team()
|
||||
antag_team?.remove_member(owner)
|
||||
LAZYREMOVE(owner.antag_datums, src)
|
||||
owner = null
|
||||
log_game("[key_name(owner)] has lost antag datum [src] ([type]).")
|
||||
QDEL_NULL(team_hud_ref)
|
||||
return TRUE
|
||||
|
||||
/// Reassociates the antag datum with a new mind - allowing one datum and its objectives to be reused for another mind
|
||||
/datum/antagonist/proc/restore_datum(datum/mind/new_owner)
|
||||
if(!isnull(owner))
|
||||
stack_trace("Tried to restore an antagonist datum that already has an owner.")
|
||||
return FALSE
|
||||
if(!can_be_owned(new_owner))
|
||||
return FALSE
|
||||
|
||||
owner = new_owner
|
||||
LAZYADD(owner.antag_datums, src)
|
||||
var/datum/team/antag_team = get_team()
|
||||
antag_team?.add_member(new_owner)
|
||||
on_gain()
|
||||
log_game("[key_name(new_owner)] has gained antag datum [src] ([type]).")
|
||||
return TRUE
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
/// Brain trauma that causes the obsession
|
||||
var/datum/brain_trauma/special/obsessed/trauma
|
||||
|
||||
/datum/antagonist/obsessed/can_be_owned(datum/mind/new_owner)
|
||||
return ..() && new_owner.current?.get_organ_by_type(/obj/item/organ/brain) // gotta have a brain to be obsessed!
|
||||
|
||||
/// Dummy antag datum that will show the cured obsessed to admins
|
||||
/datum/antagonist/former_obsessed
|
||||
name = "Former Obsessed"
|
||||
@@ -34,6 +37,7 @@
|
||||
antag_flags = ANTAG_FAKE|ANTAG_SKIP_GLOBAL_LIST
|
||||
silent = TRUE
|
||||
can_elimination_hijack = ELIMINATION_PREVENT
|
||||
ui_name = null
|
||||
|
||||
/datum/antagonist/obsessed/admin_add(datum/mind/new_owner,mob/admin)
|
||||
var/mob/living/carbon/C = new_owner.current
|
||||
@@ -53,9 +57,8 @@
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/obsessed/Destroy()
|
||||
if(trauma)
|
||||
qdel(trauma)
|
||||
. = ..()
|
||||
QDEL_NULL(trauma)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/obsessed/get_preview_icon()
|
||||
var/datum/universal_icon/obsessed_icon = render_preview_outfit(preview_outfit)
|
||||
@@ -94,14 +97,8 @@
|
||||
var/datum/objective/assassinate/obsessed/kill = new
|
||||
kill.owner = owner
|
||||
kill.target = obsessionmind
|
||||
var/obj/family_heirloom
|
||||
|
||||
for(var/datum/quirk/quirky in obsessionmind.current.quirks)
|
||||
if(istype(quirky, /datum/quirk/item_quirk/family_heirloom))
|
||||
var/datum/quirk/item_quirk/family_heirloom/heirloom_quirk = quirky
|
||||
family_heirloom = heirloom_quirk.heirloom?.resolve()
|
||||
break
|
||||
if(family_heirloom)
|
||||
if(obsessionmind.current?.has_quirk(/datum/quirk/item_quirk/family_heirloom))
|
||||
objectives_left += OBSESSED_OBJECTIVE_HEIRLOOM
|
||||
|
||||
// If they have no coworkers, jealousy will pick someone else on the station. This will never be a free objective.
|
||||
@@ -120,6 +117,7 @@
|
||||
var/datum/objective/polaroid/polaroid = new
|
||||
polaroid.owner = owner
|
||||
polaroid.target = obsessionmind
|
||||
polaroid.obsession_weakref = WEAKREF(obsessionmind.current)
|
||||
objectives += polaroid
|
||||
if(OBSESSED_OBJECTIVE_HUG)
|
||||
var/datum/objective/hug/hug = new
|
||||
@@ -129,13 +127,14 @@
|
||||
if(OBSESSED_OBJECTIVE_HEIRLOOM)
|
||||
var/datum/objective/steal/heirloom_thief/heirloom_thief = new
|
||||
heirloom_thief.owner = owner
|
||||
heirloom_thief.target = obsessionmind//while you usually wouldn't need this for stealing, we need the name of the obsession
|
||||
heirloom_thief.steal_target = family_heirloom
|
||||
heirloom_thief.target = obsessionmind
|
||||
heirloom_thief.find_target()
|
||||
objectives += heirloom_thief
|
||||
if(OBSESSED_OBJECTIVE_JEALOUS)
|
||||
var/datum/objective/assassinate/jealous/jealous = new
|
||||
jealous.owner = owner
|
||||
jealous.target = obsessionmind//will reroll into a coworker on the objective itself
|
||||
jealous.obsessed_target = obsessionmind
|
||||
jealous.find_target()
|
||||
objectives += jealous
|
||||
|
||||
objectives += kill//finally add the assassinate last, because you'd have to complete it last to greentext.
|
||||
@@ -183,107 +182,96 @@
|
||||
/datum/objective/assassinate/obsessed //just a creepy version of assassinate
|
||||
|
||||
/datum/objective/assassinate/obsessed/update_explanation_text()
|
||||
..()
|
||||
if(target?.current)
|
||||
explanation_text = "Murder [target.name], the [!target_role_type ? target.assigned_role.title : english_list(target.get_special_roles())]."
|
||||
explanation_text = "Murder [target.name], the [target_role_type ? english_list(target.get_special_roles()) : target.assigned_role.title]."
|
||||
else
|
||||
message_admins("WARNING! [ADMIN_LOOKUPFLW(owner)] obsessed objectives forged without an obsession!")
|
||||
explanation_text = "Free Objective"
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/assassinate/jealous //assassinate, but it changes the target to someone else in the previous target's department. cool, right?
|
||||
var/datum/mind/old //the target the coworker was picked from.
|
||||
var/datum/mind/obsessed_target
|
||||
|
||||
/datum/objective/assassinate/jealous/update_explanation_text()
|
||||
..()
|
||||
old = find_coworker(target)
|
||||
if(target?.current && old)
|
||||
explanation_text = "Murder [target.name], [old]'s coworker."
|
||||
if(target?.current && obsessed_target)
|
||||
explanation_text = "Murder [target.name], [obsessed_target]'s coworker."
|
||||
else
|
||||
explanation_text = "Free Objective"
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/assassinate/jealous/find_target(dupe_search_range, list/blacklist)
|
||||
if(is_unassigned_job(obsessed_target.assigned_role))
|
||||
return ..()
|
||||
|
||||
/datum/objective/assassinate/jealous/proc/find_coworker(datum/mind/oldmind)//returning null = free objective
|
||||
if(is_unassigned_job(oldmind.assigned_role))
|
||||
return
|
||||
var/list/viable_coworkers = list()
|
||||
var/list/all_coworkers = list()
|
||||
var/our_departments = oldmind.assigned_role.departments_bitflags
|
||||
for(var/mob/living/carbon/human/human_alive in GLOB.alive_mob_list)
|
||||
if(!human_alive.mind)
|
||||
continue
|
||||
if(human_alive == oldmind.current || human_alive.mind.assigned_role.faction != FACTION_STATION || human_alive.mind.has_antag_datum(/datum/antagonist/obsessed))
|
||||
continue //the jealousy target has to have a job, and not be the obsession or obsessed.
|
||||
all_coworkers += human_alive.mind
|
||||
if(!(our_departments & human_alive.mind.assigned_role.departments_bitflags))
|
||||
continue
|
||||
viable_coworkers += human_alive.mind
|
||||
var/our_departments = obsessed_target.assigned_role.departments_bitflags
|
||||
for(var/datum/mind/crewmember as anything in get_crewmember_minds())
|
||||
if(crewmember == obsessed_target || crewmember.has_antag_datum(/datum/antagonist/obsessed))
|
||||
continue // the jealousy target has to have a job, and not be the obsession or obsessed.
|
||||
|
||||
if(length(viable_coworkers))//find someone in the same department
|
||||
if(our_departments & crewmember.assigned_role.departments_bitflags)
|
||||
viable_coworkers += crewmember
|
||||
all_coworkers += crewmember
|
||||
|
||||
if(length(viable_coworkers)) // find someone in the same department
|
||||
target = pick(viable_coworkers)
|
||||
else if(length(all_coworkers))//find someone who works on the station
|
||||
else if(length(all_coworkers)) // find someone who works on the station
|
||||
target = pick(all_coworkers)
|
||||
return oldmind
|
||||
|
||||
update_explanation_text()
|
||||
return target
|
||||
|
||||
/datum/objective/spendtime //spend some time around someone, handled by the obsessed trauma since that ticks
|
||||
name = "spendtime"
|
||||
var/timer = 1800 //5 minutes
|
||||
var/timer = 0
|
||||
|
||||
/datum/objective/spendtime/update_explanation_text()
|
||||
if(timer == initial(timer))//just so admins can mess with it
|
||||
timer += pick(-600, 0)
|
||||
var/datum/antagonist/obsessed/creeper = owner.has_antag_datum(/datum/antagonist/obsessed)
|
||||
if(target?.current && creeper)
|
||||
creeper.trauma.attachedobsessedobj = src
|
||||
if(!timer)
|
||||
timer = 5 MINUTES + pick(-60 SECONDS, 0)
|
||||
|
||||
if(target?.current)
|
||||
explanation_text = "Spend [DisplayTimeText(timer)] around [target.name] while they're alive."
|
||||
else
|
||||
explanation_text = "Free Objective"
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/spendtime/check_completion()
|
||||
return timer <= 0 || explanation_text == "Free Objective"
|
||||
|
||||
return timer <= 0 || completed
|
||||
|
||||
/datum/objective/hug//this objective isn't perfect. hugging the correct amount of times, then switching bodies, might fail the objective anyway. maybe i'll come back and fix this sometime.
|
||||
name = "hugs"
|
||||
var/hugs_needed
|
||||
var/hugs_needed = 0
|
||||
|
||||
/datum/objective/hug/update_explanation_text()
|
||||
..()
|
||||
if(!hugs_needed)//just so admins can mess with it
|
||||
if(!hugs_needed)
|
||||
hugs_needed = rand(4,6)
|
||||
var/datum/antagonist/obsessed/creeper = owner.has_antag_datum(/datum/antagonist/obsessed)
|
||||
if(target?.current && creeper)
|
||||
|
||||
if(target?.current)
|
||||
explanation_text = "Hug [target.name] [hugs_needed] times while they're alive."
|
||||
|
||||
else
|
||||
explanation_text = "Free Objective"
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/hug/check_completion()
|
||||
var/datum/antagonist/obsessed/creeper = owner.has_antag_datum(/datum/antagonist/obsessed)
|
||||
if(!creeper || !creeper.trauma || !hugs_needed)
|
||||
return TRUE//free objective
|
||||
return creeper.trauma.obsession_hug_count >= hugs_needed
|
||||
return hugs_needed <= 0 || completed
|
||||
|
||||
/datum/objective/polaroid //take a picture of the target with you in it.
|
||||
name = "polaroid"
|
||||
/// Weakref to our obsession's original mob
|
||||
var/datum/weakref/obsession_weakref
|
||||
|
||||
/datum/objective/polaroid/update_explanation_text()
|
||||
..()
|
||||
if(target?.current)
|
||||
explanation_text = "Take a photo of [target.name] while they're alive, and keep it in your bag."
|
||||
else
|
||||
explanation_text = "Free Objective"
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/polaroid/check_completion()
|
||||
var/list/datum/mind/owners = get_owners()
|
||||
for(var/datum/mind/M in owners)
|
||||
if(!isliving(M.current))
|
||||
continue
|
||||
var/list/all_items = M.current.get_all_contents() //this should get things in cheesewheels, books, etc.
|
||||
for(var/obj/I in all_items) //Check for wanted items
|
||||
if(istype(I, /obj/item/photo))
|
||||
var/obj/item/photo/P = I
|
||||
if(P.picture && (WEAKREF(target.current) in P.picture.mobs_seen) && !(WEAKREF(target.current) in P.picture.dead_seen)) //Does the picture exist and is the target in it and is the target not dead
|
||||
return TRUE
|
||||
for(var/obj/item/photo/photo in owner.current?.get_all_contents())
|
||||
if((obsession_weakref in photo.picture?.mobs_seen) && !(obsession_weakref in photo.picture?.dead_seen))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -291,11 +279,27 @@
|
||||
name = "heirloomthief"
|
||||
|
||||
/datum/objective/steal/heirloom_thief/update_explanation_text()
|
||||
..()
|
||||
if(steal_target)
|
||||
explanation_text = "Steal [target.name]'s family heirloom, [steal_target] they cherish."
|
||||
explanation_text = "Steal [target]'s family heirloom, [steal_target] they cherish."
|
||||
else
|
||||
explanation_text = "Free Objective"
|
||||
completed = TRUE
|
||||
|
||||
/datum/objective/steal/heirloom_thief/find_target(dupe_search_range, list/blacklist)
|
||||
for(var/datum/quirk/item_quirk/family_heirloom/quirky in target?.current?.quirks)
|
||||
steal_target = quirky.heirloom?.resolve()
|
||||
RegisterSignal(steal_target, COMSIG_QDELETING, PROC_REF(steal_target_deleted))
|
||||
break
|
||||
|
||||
update_explanation_text()
|
||||
return steal_target
|
||||
|
||||
/datum/objective/steal/heirloom_thief/check_completion()
|
||||
return (!isnull(steal_target) && (steal_target in owner.current?.get_all_contents())) || completed
|
||||
|
||||
/datum/objective/steal/heirloom_thief/proc/steal_target_deleted()
|
||||
SIGNAL_HANDLER
|
||||
steal_target = null // it's gone, and so are our hopes and dreams
|
||||
|
||||
#undef OBSESSED_OBJECTIVE_SPEND_TIME
|
||||
#undef OBSESSED_OBJECTIVE_POLAROID
|
||||
|
||||
@@ -1165,12 +1165,20 @@
|
||||
/mob/living/resist_grab(moving_resist)
|
||||
. = TRUE
|
||||
|
||||
//Our effective grab state. GRAB_PASSIVE is equal to 0, so if we have no other altering factors to our grab state, we can break free immediately on resist.
|
||||
var/effective_grab_state = pulledby.grab_state
|
||||
//The amount of damage inflicted on a failed resist attempt.
|
||||
var/damage_on_resist_fail = rand(7, 13)
|
||||
// Base chance to escape a grab. Divided by effective grab state
|
||||
var/escape_chance = BASE_GRAB_RESIST_CHANCE
|
||||
var/list/grab_stats = list(
|
||||
// Our effective grab state.
|
||||
// GRAB_PASSIVE is equal to 0, so if we have no other altering factors to our grab state, we can break free immediately on resist.
|
||||
pulledby.grab_state,
|
||||
/// The amount of damage inflicted on a failed resist attempt.
|
||||
rand(7, 13),
|
||||
// Base chance to escape a grab. Divided by effective grab state.
|
||||
BASE_GRAB_RESIST_CHANCE,
|
||||
)
|
||||
SEND_SIGNAL(pulledby, COMSIG_MOVABLE_GRABBED_RESISTING, src, grab_stats)
|
||||
|
||||
var/effective_grab_state = grab_stats[GRAB_STAT_EFFECTIVE_STATE]
|
||||
var/damage_on_resist_fail = grab_stats[GRAB_STAT_FAIL_DAMAGE]
|
||||
var/escape_chance = grab_stats[GRAB_STAT_ESCAPE_CHANCE]
|
||||
|
||||
if(body_position == LYING_DOWN) //If prone, treat the grab state as one higher
|
||||
effective_grab_state++
|
||||
@@ -1184,8 +1192,8 @@
|
||||
if(HAS_TRAIT(src, TRAIT_GRABRESISTANCE)) //If we have grab resistance from some source, treat the grab state as one lower.
|
||||
effective_grab_state--
|
||||
|
||||
//If our puller is a human, and they have an active hand they're grabbing with (please don't ask how people grab without hands), then apply their unarmed values to the grab values
|
||||
if(pulledby && ishuman(pulledby))
|
||||
// If our puller is a human, and they have an active hand they're grabbing with (please don't ask how people grab without hands), then apply their unarmed values to the grab values
|
||||
if(ishuman(pulledby))
|
||||
var/mob/living/carbon/human/human_puller = pulledby
|
||||
var/obj/item/bodypart/grabbing_bodypart = human_puller.get_active_hand()
|
||||
if(grabbing_bodypart)
|
||||
@@ -1193,18 +1201,6 @@
|
||||
effective_grab_state += grabbing_bodypart.unarmed_grab_state_bonus
|
||||
escape_chance += grabbing_bodypart.unarmed_grab_escape_chance_bonus
|
||||
|
||||
//If our puller is a drunken brawler, they add more damage based on their own damage taken so long as they're drunk and treat the grab state as one higher
|
||||
var/puller_drunkenness = human_puller.get_drunk_amount()
|
||||
if(puller_drunkenness && HAS_TRAIT(human_puller, TRAIT_DRUNKEN_BRAWLER))
|
||||
damage_on_resist_fail += clamp((human_puller.get_fire_loss() + human_puller.get_brute_loss()) / 10, 3, 20)
|
||||
effective_grab_state++
|
||||
|
||||
var/datum/martial_art/puller_art = GET_ACTIVE_MARTIAL_ART(human_puller)
|
||||
if(puller_art?.can_use(human_puller))
|
||||
damage_on_resist_fail += puller_art.grab_damage_modifier
|
||||
effective_grab_state += puller_art.grab_state_modifier
|
||||
escape_chance += puller_art.grab_escape_chance_modifier
|
||||
|
||||
//We only resist our grab state if we are currently in a grab equal to or greater than GRAB_AGGRESSIVE (1). Otherwise, break out immediately!
|
||||
if(effective_grab_state >= GRAB_AGGRESSIVE)
|
||||
// see defines/combat.dm, this should be baseline 60%
|
||||
|
||||
Reference in New Issue
Block a user