diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index e8c02fd05d..88f9759940 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -266,6 +266,7 @@
#define COMSIG_MOB_GET_VISIBLE_MESSAGE "mob_get_visible_message" //from base of atom/visible_message(): (atom/A, msg, range, ignored_mobs)
#define COMPONENT_NO_VISIBLE_MESSAGE 1 //exactly what's said on the tin.
#define COMSIG_MOB_ANTAG_ON_GAIN "mob_antag_on_gain" //from base of /datum/antagonist/on_gain(): (antag_datum)
+#define COMSIG_MOB_APPLY_DAMAGE "mob_apply_damage" //from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness)
#define COMSIG_MOB_SPELL_CAN_CAST "mob_spell_can_cast" //from base of /obj/effect/proc_holder/spell/can_cast(): (spell)
#define COMSIG_MOB_SWAP_HANDS "mob_swap_hands" //from base of mob/swap_hand(): (obj/item)
@@ -303,6 +304,10 @@
#define COMSIG_LIVING_ACTIVE_PARRY_START "active_parry_start" //from base of mob/living/initiate_parry_sequence(): (parrying_method, datum/parrying_item_mob_or_art, list/backup_items, list/override)
#define COMPONENT_PREVENT_PARRY_START 1
+#define COMSIG_LIVING_ATTACKER_SET "living_attacker_set" // from base of /mob/living/set_last_attacker(): (attacker)
+
+#define COMSIG_LIVING_SET_AS_ATTACKER "living_set_as_attacker" // from base of /mob/living/set_last_attacker(): (target)
+
//ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS!
#define COMSIG_LIVING_STATUS_STUN "living_stun" //from base of mob/living/Stun() (amount, update, ignore)
#define COMSIG_LIVING_STATUS_KNOCKDOWN "living_knockdown" //from base of mob/living/Knockdown() (amount, update, ignore)
@@ -357,7 +362,6 @@
// /obj/item signals
#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user)
-#define COMSIG_MOB_APPLY_DAMGE "mob_apply_damage" //from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone)
#define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob)
#define COMPONENT_NO_INTERACT 1
#define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj" //from base of obj/item/attack_obj(): (/obj, /mob)
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index f63e594675..faf911e55b 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -94,8 +94,7 @@
else if(hitsound)
playsound(loc, hitsound, get_clamped_volume(), 1, -1)
- M.lastattacker = user.real_name
- M.lastattackerckey = user.ckey
+ M.set_last_attacker(user)
if(force && M == user && user.client)
user.client.give_award(/datum/award/achievement/misc/selfouch, user)
diff --git a/code/datums/components/activity.dm b/code/datums/components/activity.dm
new file mode 100644
index 0000000000..ae18ca01a2
--- /dev/null
+++ b/code/datums/components/activity.dm
@@ -0,0 +1,63 @@
+/datum/component/activity
+ var/activity_level = 0
+ var/not_moved_counter = 0
+ var/list/historical_activity_levels = list()
+
+/datum/component/activity/Initialize()
+ if(!isliving(parent))
+ return COMPONENT_INCOMPATIBLE
+ var/mob/living/L = parent
+
+ RegisterSignal(L, COMSIG_LIVING_SET_AS_ATTACKER, .proc/on_set_as_attacker)
+ RegisterSignal(L, COMSIG_LIVING_ATTACKER_SET, .proc/on_attacker_set)
+ RegisterSignal(L, COMSIG_MOB_DEATH, .proc/on_death)
+ RegisterSignal(L, COMSIG_EXIT_AREA, .proc/on_exit_area)
+ RegisterSignal(L, COMSIG_LIVING_LIFE, .proc/on_life)
+ RegisterSignal(L, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_ATTACK_RANGED, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_THROW, COMSIG_MOVABLE_TELEPORTED, COMSIG_LIVING_GUN_PROCESS_FIRE, COMSIG_MOB_APPLY_DAMAGE), .proc/minor_activity)
+
+/datum/component/activity/proc/log_activity()
+ historical_activity_levels[world.time] = activity_level
+
+/datum/component/activity/proc/minor_activity(datum/source)
+ activity_level += 1
+
+/datum/component/activity/proc/on_attacker_set(datum/source, mob/attacker)
+ activity_level += 10
+ if(attacker?.mind)
+ activity_level += 10
+ log_activity()
+
+/datum/component/activity/proc/on_set_as_attacker(datum/source, mob/target)
+ activity_level += 10
+ if(target?.mind)
+ activity_level += 20
+ log_activity()
+
+/datum/component/activity/proc/on_death(datum/source)
+ activity_level += 100 // dying means you're doing SOMETHING
+ log_activity()
+
+/datum/component/activity/proc/on_exit_area(datum/source)
+ activity_level += 1
+ not_moved_counter = 0
+
+/datum/component/activity/proc/on_life(datum/source, seconds, times_fired)
+ var/mob/living/L = source
+ if(L.stat >= UNCONSCIOUS) // can't expect the unconscious to move
+ return
+ not_moved_counter += seconds
+ var/should_log = FALSE
+ switch(not_moved_counter)
+ if(60 to 120)
+ activity_level -= 1
+ if(120 to 600)
+ activity_level -= 5
+ if(600 to 1200)
+ activity_level -= 10
+ should_log = TRUE
+ if(1200 to INFINITY)
+ activity_level -= 20
+ should_log = TRUE
+ activity_level = max(activity_level, 0)
+ if(should_log)
+ log_activity()
diff --git a/code/datums/components/explodable.dm b/code/datums/components/explodable.dm
index 2e5834c025..6e4be8497d 100644
--- a/code/datums/components/explodable.dm
+++ b/code/datums/components/explodable.dm
@@ -56,10 +56,10 @@
detonate()
/datum/component/explodable/proc/on_equip(datum/source, mob/equipper, slot)
- RegisterSignal(equipper, COMSIG_MOB_APPLY_DAMGE, .proc/explodable_attack_zone, TRUE)
+ RegisterSignal(equipper, COMSIG_MOB_APPLY_DAMAGE, .proc/explodable_attack_zone, TRUE)
/datum/component/explodable/proc/on_drop(datum/source, mob/user)
- UnregisterSignal(user, COMSIG_MOB_APPLY_DAMGE)
+ UnregisterSignal(user, COMSIG_MOB_APPLY_DAMAGE)
/// Checks if we're hitting the zone this component is covering
/datum/component/explodable/proc/is_hitting_zone(def_zone)
diff --git a/code/datums/elements/wuv.dm b/code/datums/elements/wuv.dm
index 6476a204cf..d4f1955afc 100644
--- a/code/datums/elements/wuv.dm
+++ b/code/datums/elements/wuv.dm
@@ -34,13 +34,13 @@
. = ..()
UnregisterSignal(source, COMSIG_MOB_ATTACK_HAND)
-/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user)
+/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user, act_intent)
var/mob/living/L = source
if(L.stat == DEAD)
return
//we want to delay the effect to be displayed after the mob is petted, not before.
- switch(user.a_intent)
+ switch(act_intent)
if(INTENT_HARM)
addtimer(CALLBACK(src, .proc/kick_the_dog, source, user), 1)
if(INTENT_HELP)
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 674787fbd4..fcfddb2788 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -412,6 +412,24 @@ If not set, defaults to check_completion instead. Set it. It's used by cryo.
counter++
return counter >= 8
+/datum/objective/freedom
+ name = "freedom"
+ explanation_text = "Don't get captured by nanotrasen."
+ team_explanation_text = "Have all members of your team free of nanotrasen custody."
+
+/datum/objective/freedom/check_completion()
+ var/list/datum/mind/owners = get_owners()
+ for(var/m in owners)
+ var/datum/mind/M = m
+ if(!considered_alive(M))
+ return FALSE
+ if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME)
+ return FALSE
+ var/turf/location = get_turf(M.current)
+ if(!location || istype(location, /turf/open/floor/plasteel/shuttle/red) || istype(location, /turf/open/floor/mineral/plastitanium/red/brig)) // Fails if they are in the shuttle brig
+ return FALSE
+ return TRUE
+
/datum/objective/escape
name = "escape"
explanation_text = "Escape on the shuttle or an escape pod alive and without being in custody."
diff --git a/code/game/objects/items/electrostaff.dm b/code/game/objects/items/electrostaff.dm
index 31aaff12b5..a1c06f8d23 100644
--- a/code/game/objects/items/electrostaff.dm
+++ b/code/game/objects/items/electrostaff.dm
@@ -210,8 +210,7 @@
target.apply_effect(EFFECT_STUTTER, stunforce)
SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
if(user)
- target.lastattacker = user.real_name
- target.lastattackerckey = user.ckey
+ target.set_last_attacker(user)
target.visible_message("[user] has shocked [target] with [src]!", \
"[user] has shocked you with [src]!")
log_combat(user, target, "stunned with an electrostaff")
@@ -237,8 +236,7 @@
target.adjustFireLoss(lethal_force) //good against ointment spam
SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
if(user)
- target.lastattacker = user.real_name
- target.lastattackerckey = user.ckey
+ target.set_last_attacker(user)
target.visible_message("[user] has seared [target] with [src]!", \
"[user] has seared you with [src]!")
log_combat(user, target, "burned with an electrostaff")
diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm
index cc01e6aaee..88146e4b4d 100644
--- a/code/game/objects/items/stunbaton.dm
+++ b/code/game/objects/items/stunbaton.dm
@@ -209,8 +209,7 @@
L.apply_effect(EFFECT_STUTTER, stamforce)
SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK)
if(user)
- L.lastattacker = user.real_name
- L.lastattackerckey = user.ckey
+ L.set_last_attacker(user)
L.visible_message("[user] has [disarming? "disarmed" : "stunned"] [L] with [src]!", \
"[user] has [disarming? "disarmed" : "stunned"] you with [src]!")
log_combat(user, L, disarming? "disarmed" : "stunned")
diff --git a/code/game/objects/structures/manned_turret.dm b/code/game/objects/structures/manned_turret.dm
index d89606417f..3d0b28d2f3 100644
--- a/code/game/objects/structures/manned_turret.dm
+++ b/code/game/objects/structures/manned_turret.dm
@@ -199,8 +199,7 @@
return TRUE
/obj/item/gun_control/attack(mob/living/M, mob/living/user)
- M.lastattacker = user.real_name
- M.lastattackerckey = user.ckey
+ M.set_last_attacker(user)
M.attacked_by(src, user)
add_fingerprint(user)
diff --git a/code/modules/admin/antag_panel.dm b/code/modules/admin/antag_panel.dm
index 88aab2c4ce..84071d76f4 100644
--- a/code/modules/admin/antag_panel.dm
+++ b/code/modules/admin/antag_panel.dm
@@ -101,6 +101,10 @@ GLOBAL_VAR(antag_prototypes)
out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
"
out += "Assigned role: [assigned_role]. Edit
"
out += "Faction and special role: [special_role]
"
+ var/datum/component/activity/activity = current.GetComponent(/datum/component/activity)
+ if(activity)
+ out += "Activity level: [activity.activity_level]
"
+ out += "Hasn't changed areas in approximately [activity.not_moved_counter] seconds"
var/special_statuses = get_special_statuses()
if(length(special_statuses))
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 14f7b34309..93b2e20271 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -111,6 +111,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/datum/skill_modifier/job/M = GLOB.skill_modifiers[GET_SKILL_MOD_ID(A, type)]
if(istype(M))
M.name = "[name] Training"
+ owner.AddComponent(/datum/component/activity)
SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src)
/datum/antagonist/proc/is_banned(mob/M)
@@ -141,6 +142,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/datum/team/team = get_team()
if(team)
team.remove_member(owner)
+ // we don't remove the activity component on purpose--no real point to it
qdel(src)
/datum/antagonist/proc/greet()
diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
index b90389094f..8484732d8d 100644
--- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm
+++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
@@ -511,8 +511,7 @@
/obj/item/abductor/baton/proc/StunAttack(mob/living/L,mob/living/user)
- L.lastattacker = user.real_name
- L.lastattackerckey = user.ckey
+ L.set_last_attacker(user)
L.adjustStaminaLoss(35) //because previously it took 5-6 hits to actually "incapacitate" someone for the purposes of the sleep inducement
L.DefaultCombatKnockdown(140)
diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm
index 901c83b1b7..568f01ab83 100644
--- a/code/modules/antagonists/cult/blood_magic.dm
+++ b/code/modules/antagonists/cult/blood_magic.dm
@@ -385,8 +385,7 @@
qdel(src)
return
log_combat(user, M, "used a cult spell on", source.name, "")
- M.lastattacker = user.real_name
- M.lastattackerckey = user.ckey
+ M.set_last_attacker(user)
/obj/item/melee/blood_magic/afterattack(atom/target, mob/living/carbon/user, proximity)
. = ..()
diff --git a/code/modules/antagonists/traitor/classes/assassin.dm b/code/modules/antagonists/traitor/classes/assassin.dm
index 8175ad9736..7fb58a7a74 100644
--- a/code/modules/antagonists/traitor/classes/assassin.dm
+++ b/code/modules/antagonists/traitor/classes/assassin.dm
@@ -1,30 +1,7 @@
-/datum/traitor_class/human/assassin
+/datum/traitor_class/human/subterfuge/assassin
name = "Donk Co Operative"
employer = "Donk Corporation"
- weight = 0
+ weight = 6
chaos = 1
threat = 2
-
-/datum/traitor_class/human/assassin/forge_single_objective(datum/antagonist/traitor/T)
- .=1
- var/permakill_prob = 20
- var/datum/game_mode/dynamic/mode
- if(istype(SSticker.mode,/datum/game_mode/dynamic))
- mode = SSticker.mode
- permakill_prob = max(0,mode.threat_level-50)
- var/list/active_ais = active_ais()
- if(active_ais.len && prob(100/GLOB.joined_player_list.len))
- var/datum/objective/destroy/destroy_objective = new
- destroy_objective.owner = T.owner
- destroy_objective.find_target()
- T.add_objective(destroy_objective)
- else if(prob(permakill_prob))
- var/datum/objective/assassinate/kill_objective = new
- kill_objective.owner = T.owner
- kill_objective.find_target()
- T.add_objective(kill_objective)
- else
- var/datum/objective/assassinate/once/kill_objective = new
- kill_objective.owner = T.owner
- kill_objective.find_target()
- T.add_objective(kill_objective)
+ assassin_prob = 70
diff --git a/code/modules/antagonists/traitor/classes/freeform.dm b/code/modules/antagonists/traitor/classes/freeform.dm
index 1ca385ef19..5c52e7f1c3 100644
--- a/code/modules/antagonists/traitor/classes/freeform.dm
+++ b/code/modules/antagonists/traitor/classes/freeform.dm
@@ -1,12 +1,12 @@
/datum/traitor_class/human/freeform
name = "Waffle Co Agent"
employer = "Waffle Company"
- weight = 0 // should not spawn in unless admins bus something in the traitor panel with setting traitor classes
+ weight = 5
chaos = 0
/datum/traitor_class/human/freeform/forge_objectives(datum/antagonist/traitor/T)
- var/datum/objective/escape/O = new
- O.explanation_text = "You have no explicit goals! While we don't approve of mindless slaughter, you may antagonize nanotrasen any way you wish! Make sure to escape alive and not in custody, though!"
+ var/datum/objective/freedom/O = new
+ O.explanation_text = "You have no explicit goals! While we don't approve of mindless slaughter, you may antagonize nanotrasen any way you wish! Don't get captured or killed, but if you've done nothing, you'll be in trouble!"
O.owner = T.owner
T.add_objective(O)
return
diff --git a/code/modules/antagonists/traitor/classes/human.dm b/code/modules/antagonists/traitor/classes/human.dm
index 16f65a63c3..f868d0e7e1 100644
--- a/code/modules/antagonists/traitor/classes/human.dm
+++ b/code/modules/antagonists/traitor/classes/human.dm
@@ -13,8 +13,12 @@
T.assign_exchange_role(SSticker.mode.exchange_blue)
objective_count += 1 //Exchange counts towards number of objectives
var/toa = CONFIG_GET(number/traitor_objectives_amount)
+ var/attempts = 0
for(var/i = objective_count, i < toa, i++)
- forge_single_objective(T)
+ var/success = FALSE
+ while(!success && attempts < max(toa*10, 100))
+ success = forge_single_objective(T)
+ attempts += 1
if(!(locate(/datum/objective/escape) in T.objectives))
var/datum/objective/escape/escape_objective = new
escape_objective.owner = T.owner
diff --git a/code/modules/antagonists/traitor/classes/subterfuge.dm b/code/modules/antagonists/traitor/classes/subterfuge.dm
index 73dc455a45..54f07eb23c 100644
--- a/code/modules/antagonists/traitor/classes/subterfuge.dm
+++ b/code/modules/antagonists/traitor/classes/subterfuge.dm
@@ -1,12 +1,11 @@
/datum/traitor_class/human/subterfuge
name = "MI13 Operative"
employer = "MI13"
- weight = 36
+ weight = 25
chaos = -5
+ var/assassin_prob = 25
/datum/traitor_class/human/subterfuge/forge_single_objective(datum/antagonist/traitor/T)
- .=1
- var/assassin_prob = 30
var/datum/game_mode/dynamic/mode
if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode
@@ -16,24 +15,31 @@
kill_objective.owner = T.owner
kill_objective.find_target()
T.add_objective(kill_objective)
+ return TRUE
else
var/list/weights = list()
- weights["sabo"] = length(subtypesof(/datum/sabotage_objective))
- weights["steal"] = length(subtypesof(/datum/objective_item/steal))
+ var/datum/objective/sabotage/sabotage_objective = new
+ sabotage_objective.owner = T.owner
+ if(sabotage_objective.find_target())
+ weights["sabo"] = length(subtypesof(/datum/objective_item/steal))
+ var/datum/objective/steal/steal_objective = new
+ steal_objective.owner = T.owner
+ if(steal_objective.find_target())
+ weights["steal"] = length(subtypesof(/datum/objective_item/steal))
weights["download"] = !(locate(/datum/objective/download) in T.objectives || (T.owner.assigned_role in list("Research Director", "Scientist", "Roboticist")))
switch(pickweight(weights))
if("sabo")
- var/datum/objective/sabotage/sabotage_objective = new
- sabotage_objective.owner = T.owner
- sabotage_objective.find_target()
T.add_objective(sabotage_objective)
+ qdel(steal_objective)
+ return TRUE
if("steal")
- var/datum/objective/steal/steal_objective = new
- steal_objective.owner = T.owner
- steal_objective.find_target()
T.add_objective(steal_objective)
+ qdel(sabotage_objective)
+ return TRUE
if("download")
var/datum/objective/download/download_objective = new
download_objective.owner = T.owner
download_objective.gen_amount_goal()
T.add_objective(download_objective)
+ return TRUE
+ return FALSE
diff --git a/code/modules/antagonists/traitor/classes/traitor_class.dm b/code/modules/antagonists/traitor/classes/traitor_class.dm
index 568619f59d..6aaa0b41c8 100644
--- a/code/modules/antagonists/traitor/classes/traitor_class.dm
+++ b/code/modules/antagonists/traitor/classes/traitor_class.dm
@@ -7,7 +7,8 @@ GLOBAL_LIST_EMPTY(traitor_classes)
var/chaos = 0
var/threat = 0
var/TC = 20
- /// Minimum players for this to randomly roll via get_random_traitor_class().
+ var/processing = FALSE
+ /// Minimum players for this to randomly roll via get_random_traitor_kind().
var/min_players = 0
var/list/uplink_filters
@@ -43,4 +44,6 @@ GLOBAL_LIST_EMPTY(traitor_classes)
/datum/traitor_class/proc/clean_up_traitor(datum/antagonist/traitor/T)
// Any effects that need to be cleaned up if traitor class is being swapped.
-
+
+/datum/traitor_class/proc/on_process(/datum/antagonist/traitor/T)
+ // only for processing traitor classes; runs once an SSprocessing tick
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 47a9c59274..f0bc6e5bfc 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -26,6 +26,8 @@
if(traitor_kind)
traitor_kind.remove_innate_effects(owner.current)
traitor_kind.clean_up_traitor(src)
+ if(traitor_kind.processing)
+ STOP_PROCESSING(SSprocessing, src)
swap_from_old = TRUE
traitor_kind = GLOB.traitor_classes[kind]
traitor_kind.apply_innate_effects(owner.current)
@@ -33,11 +35,16 @@
for(var/O in objectives)
qdel(O)
traitor_kind.forge_objectives(src)
+ if(traitor_kind.processing)
+ START_PROCESSING(SSprocessing, src)
if(swap_from_old)
traitor_kind.finalize_traitor(src)
traitor_kind.greet(src)
owner.announce_objectives()
+/datum/antagonist/traitor/process()
+ traitor_kind.on_process(src)
+
/proc/get_random_traitor_kind(var/list/blacklist = list())
var/chaos_weight = 0
if(istype(SSticker.mode,/datum/game_mode/dynamic))
diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm
index 1f3a174d0f..4167fdbde1 100644
--- a/code/modules/mob/living/carbon/damage_procs.dm
+++ b/code/modules/mob/living/carbon/damage_procs.dm
@@ -1,7 +1,7 @@
/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE)
- SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone)
+ SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone)
var/hit_percent = (100-blocked)/100
if(!forced && hit_percent <= 0)
return 0
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 986fc38cd3..c1a797dea0 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1620,8 +1620,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
"[user] [atk_verb]ed you!", null, COMBAT_MESSAGE_RANGE, null, \
user, "You [atk_verb]ed [target]!")
- target.lastattacker = user.real_name
- target.lastattackerckey = user.ckey
+ target.set_last_attacker(user)
user.dna.species.spec_unarmedattacked(user, target)
if(user.limb_destroyer)
@@ -2058,7 +2057,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
log_combat(user, target, "shoved", append_message)
/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE)
- SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal
+ SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal
var/hit_percent = (100-(blocked+armor))/100
hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100
if(!forced && hit_percent <= 0)
diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm
index e7426aa1fa..fcb0ffcefa 100644
--- a/code/modules/mob/living/damage_procs.dm
+++ b/code/modules/mob/living/damage_procs.dm
@@ -2,7 +2,7 @@
/**
* Applies damage to this mob
*
- * Sends [COMSIG_MOB_APPLY_DAMGE]
+ * Sends [COMSIG_MOB_APPLY_DAMAGE]
*
* Arguuments:
* * damage - amount of damage
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 486a0acf4e..5f3f530437 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -274,7 +274,7 @@
/mob/living/on_attack_hand(mob/user, act_intent = user.a_intent, attackchain_flags)
..() //Ignoring parent return value here.
- SEND_SIGNAL(src, COMSIG_MOB_ATTACK_HAND, user)
+ SEND_SIGNAL(src, COMSIG_MOB_ATTACK_HAND, user, act_intent)
if((user != src) && act_intent != INTENT_HELP && (mob_run_block(user, 0, user.name, ATTACK_TYPE_UNARMED | ATTACK_TYPE_MELEE | ((attackchain_flags & ATTACK_IS_PARRY_COUNTERATTACK)? ATTACK_TYPE_PARRY_COUNTERATTACK : NONE), null, user, check_zone(user.zone_selected), null) & BLOCK_SUCCESS))
log_combat(user, src, "attempted to touch")
visible_message("[user] attempted to touch [src]!",
@@ -561,3 +561,9 @@
/mob/living/proc/getFireLoss_nonProsthetic()
return getFireLoss()
+
+/mob/living/proc/set_last_attacker(mob/attacker)
+ lastattacker = attacker.real_name
+ lastattackerckey = attacker.ckey
+ SEND_SIGNAL(src, COMSIG_LIVING_ATTACKER_SET, attacker)
+ SEND_SIGNAL(attacker, COMSIG_LIVING_SET_AS_ATTACKER, src)
diff --git a/code/modules/ninja/suit/ninjaDrainAct.dm b/code/modules/ninja/suit/ninjaDrainAct.dm
index 4462987cbf..af0ca809da 100644
--- a/code/modules/ninja/suit/ninjaDrainAct.dm
+++ b/code/modules/ninja/suit/ninjaDrainAct.dm
@@ -267,8 +267,7 @@ They *could* go in their appropriate files, but this is supposed to be modular
apply_effect(EFFECT_STUTTER, G.stunforce)
SEND_SIGNAL(src, COMSIG_LIVING_MINOR_SHOCK)
- lastattacker = H.real_name
- lastattackerckey = H.ckey
+ set_last_attacker(H)
log_combat(H, src, "stunned")
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
diff --git a/code/modules/uplink/uplink_items/uplink_bundles.dm b/code/modules/uplink/uplink_items/uplink_bundles.dm
index d01c3ae96a..465f980804 100644
--- a/code/modules/uplink/uplink_items/uplink_bundles.dm
+++ b/code/modules/uplink/uplink_items/uplink_bundles.dm
@@ -172,17 +172,17 @@
/datum/uplink_item/bundles_TC/reroll
name = "Renegotiate Contract"
- desc = "Selecting this will inform your employers that you wish for new objectives. Can only be done once; no take-backs."
+ desc = "Selecting this will inform your employers that you wish for new objectives. Can only be done twice."
item = /obj/effect/gibspawner/generic
cost = 0
cant_discount = TRUE
restricted = TRUE
- limited_stock = 1
+ limited_stock = 2
/datum/uplink_item/bundles_TC/reroll/purchase(mob/user, datum/component/uplink/U)
var/datum/antagonist/traitor/T = user?.mind?.has_antag_datum(/datum/antagonist/traitor)
if(istype(T))
- T.set_traitor_kind(/datum/traitor_class/human/subterfuge)
+ T.set_traitor_kind(get_random_traitor_kind(blacklist = list(/datum/traitor_class/human/freeform, /datum/traitor_class/human/hijack, /datum/traitor_class/human/martyr)))
else
to_chat(user,"Invalid user for contract renegotiation.")
diff --git a/tgstation.dme b/tgstation.dme
index b8c87ede3e..e2fb300e64 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -453,6 +453,7 @@
#include "code\datums\brain_damage\special.dm"
#include "code\datums\brain_damage\split_personality.dm"
#include "code\datums\components\_component.dm"
+#include "code\datums\components\activity.dm"
#include "code\datums\components\anti_magic.dm"
#include "code\datums\components\armor_plate.dm"
#include "code\datums\components\bane.dm"