diff --git a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
index f4df347c62db..9d3d7549dca9 100644
--- a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
+++ b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
@@ -45,9 +45,9 @@
#define COMSIG_MOB_ATTEMPTING_EQUIP "mob_attempting_equip"
#define COMPONENT_MOB_CANCEL_ATTEMPT_EQUIP (1<<0)
-/// For when a mob is devoured by a Xeno
-#define COMSIG_MOB_DEVOURED "mob_devoured"
- #define COMPONENT_CANCEL_DEVOUR (1<<0)
+/// For when a mob is hauled by a Xeno
+#define COMSIG_MOB_HAULED "mob_hauled"
+ #define COMPONENT_CANCEL_HAUL (1<<0)
// Reserved for tech trees
#define COMSIG_MOB_ENTER_TREE "mob_enter_tree"
#define COMPONENT_CANCEL_TREE_ENTRY (1<<0)
diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm
index b99bce50519d..c33c8be76873 100644
--- a/code/__DEFINES/job.dm
+++ b/code/__DEFINES/job.dm
@@ -63,6 +63,7 @@ GLOBAL_LIST_INIT(job_squad_roles, JOB_SQUAD_ROLES_LIST)
#define JOB_RESEARCHER "Researcher"
#define JOB_MEDIC_ROLES /datum/timelock/medic
#define JOB_MEDIC_ROLES_LIST list(JOB_SQUAD_MEDIC, JOB_CMO, JOB_DOCTOR, JOB_FIELD_DOCTOR, JOB_NURSE, JOB_RESEARCHER, JOB_SURGEON)
+#define JOB_DOCTOR_ROLES_LIST list(JOB_CMO, JOB_DOCTOR, JOB_SURGEON)
#define JOB_CORPORATE_LIAISON "Corporate Liaison"
diff --git a/code/__DEFINES/text.dm b/code/__DEFINES/text.dm
index 26567be26255..dc58682d9483 100644
--- a/code/__DEFINES/text.dm
+++ b/code/__DEFINES/text.dm
@@ -24,3 +24,5 @@
#define MAX_PAPER_MESSAGE_LEN 3072
#define MAX_BOOK_MESSAGE_LEN 9216
#define MAX_NAME_LEN 28
+
+#define MAX_MESSAGE_CHUNKS 20
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 12e105c14655..cf48dabcb9b9 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -163,6 +163,8 @@
#define TRAIT_TEMPORARILY_MUTED "temporarily_muted"
/// Mob wont get hit by stray projectiles
#define TRAIT_NO_STRAY "trait_no_stray"
+/// When a Xeno hauls us. We can take out our knife or gun if hauled though we are immobilized, also shielded from most damage.
+#define TRAIT_HAULED "hauled"
// only used by valkyrie
#define TRAIT_VALKYRIE_ARMORED "trait_valkyrie_armored"
@@ -439,6 +441,8 @@ GLOBAL_LIST(trait_name_map)
#define TRAIT_SOURCE_STRAIN "t_s_strain"
///Status trait coming from being buckled.
#define TRAIT_SOURCE_BUCKLE "t_s_buckle"
+//Status trait coming from being hauled by a xeno.
+#define TRAIT_SOURCE_XENO_HAUL "t_s_xeno_haul"
///Status trait coming from being assigned as [acting] squad leader.
#define TRAIT_SOURCE_SQUAD_LEADER "t_s_squad_leader"
///Status trait coming from their job
diff --git a/code/__DEFINES/typecheck/xenos.dm b/code/__DEFINES/typecheck/xenos.dm
index 34b70ac92f45..c84aa1e30426 100644
--- a/code/__DEFINES/typecheck/xenos.dm
+++ b/code/__DEFINES/typecheck/xenos.dm
@@ -41,6 +41,9 @@
var/datum/hive_status/corrupted/renegade/renegade_hive = hive
return renegade_hive.iff_protection_check(src, attempt_harm_mob)
+ if(HAS_TRAIT(attempt_harm_mob, TRAIT_HAULED))
+ return TRUE
+
return hive.is_ally(attempt_harm_mob)
// need this to set the data for walls/eggs/huggers when they are initialized
diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno.dm
index e160a334bce5..a0b957c05a75 100644
--- a/code/__DEFINES/xeno.dm
+++ b/code/__DEFINES/xeno.dm
@@ -8,8 +8,10 @@
#define TUNNEL_ENTER_BIG_XENO_DELAY 120
#define TUNNEL_ENTER_LARVA_DELAY 10
-/// The duration it takes a player controlled facehugger to leap or hug adjacently
-#define FACEHUGGER_WINDUP_DURATION 1 SECONDS
+/// The duration it takes a player controlled facehugger to leap
+#define FACEHUGGER_LEAP_DURATION 2 SECONDS
+/// The duration it takes a player controlled facehugger to hug a target lying down by clicking on it
+#define FACEHUGGER_CLIMB_DURATION 1 SECONDS
// Defines for action types and click delays used by xenomorph/unarmedattack() and attack_alien().
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index dcdabe1068a8..fae9d1a82fe6 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -438,22 +438,37 @@
return target
-/proc/urange(dist=0, atom/center=usr, orange=0, areas=0)
+/**
+ * An alternative to orange() that should perform better for distances >= 5 because
+ * of ORANGE_TURFS utilizing RECT_TURFS.
+ *
+ * Returns a list of turfs and those turf's contents, excluding center (and
+ * its contents). Ignores visibility (like orange).
+ */
+/proc/long_orange(dist=0, atom/center)
if(!dist)
- if(!orange)
- return list(center)
- else
- return list()
+ return list()
+ var/list/turfs = ORANGE_TURFS(dist, center)
+ . = list()
+ for(var/turf/cur_turf as anything in turfs)
+ . += cur_turf
+ . += cur_turf.contents
+ . -= center // If center isn't a turf
+
+/**
+ * An alternative to range() that should perform better for distances >= 5 because
+ * of RANGE_TURFS utilizing RECT_TURFS.
+ *
+ * Returns a list of turfs and those turf's contents, including center (and its
+ * contents if a turf). Ignores visibility (like range).
+ */
+/proc/long_range(dist=0, atom/center)
var/list/turfs = RANGE_TURFS(dist, center)
- if(orange)
- turfs -= get_turf(center)
. = list()
- for(var/turf/T as anything in turfs)
- . += T
- . += T.contents
- if(areas)
- . |= T.loc
+ for(var/turf/cur_turf as anything in turfs)
+ . += cur_turf
+ . += cur_turf.contents
// returns turf relative to A in given direction at set range
// result is bounded to map size
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 9c1d39ebbf25..31e26bd0498d 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -145,6 +145,9 @@
return
/mob/proc/click_adjacent(atom/targeted_atom, obj/item/used_item, mods)
+ if(HAS_TRAIT(src, TRAIT_HAULED))
+ if(!isstorage(targeted_atom) && !isclothing(targeted_atom) && !isweapon(targeted_atom) && !isgun(targeted_atom))
+ return
if(used_item)
var/attackby_result = targeted_atom.attackby(used_item, src, mods)
var/afterattack_result
diff --git a/code/_onclick/human.dm b/code/_onclick/human.dm
index ffa7b376acca..d30fe12992b0 100644
--- a/code/_onclick/human.dm
+++ b/code/_onclick/human.dm
@@ -70,7 +70,7 @@
/mob/living/carbon/human/UnarmedAttack(atom/A, proximity, click_parameters)
- if(body_position == LYING_DOWN) //No attacks while laying down
+ if(body_position == LYING_DOWN && !HAS_TRAIT(src, TRAIT_HAULED)) //No attacks while laying down
return 0
var/obj/item/clothing/gloves/G = gloves // not typecast specifically enough in defines
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 1631bc7bd12e..d1208fb45bb9 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -1,6 +1,8 @@
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
/obj/item/proc/attack_self(mob/user)
+ if(HAS_TRAIT(user, TRAIT_HAULED))
+ return
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user)
SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK_SELF, src)
@@ -35,6 +37,8 @@
else if(initiate_surgery_moment(I, src, null, user))
return TRUE
*/
+ if(HAS_TRAIT(user, TRAIT_HAULED))
+ return
if(istype(I) && ismob(user))
return I.attack(src, user)
diff --git a/code/datums/ammo/shrapnel.dm b/code/datums/ammo/shrapnel.dm
index 02a57202ff53..f98c9723f1b1 100644
--- a/code/datums/ammo/shrapnel.dm
+++ b/code/datums/ammo/shrapnel.dm
@@ -6,26 +6,24 @@
/datum/ammo/bullet/shrapnel
name = "shrapnel"
icon_state = "buckshot"
+ accurate_range_min = 5
flags_ammo_behavior = AMMO_BALLISTIC|AMMO_STOPPED_BY_COVER
- accuracy = HIT_ACCURACY_TIER_4
- accurate_range = 7
- max_range = 10
- damage = 30
- penetration = ARMOR_PENETRATION_TIER_2
+ accuracy = HIT_ACCURACY_TIER_3
+ accurate_range = 32
+ max_range = 8
+ damage = 25
+ damage_var_low = -PROJECTILE_VARIANCE_TIER_6
+ damage_var_high = PROJECTILE_VARIANCE_TIER_6
+ penetration = ARMOR_PENETRATION_TIER_4
shell_speed = AMMO_SPEED_TIER_2
+ shrapnel_chance = 5
/datum/ammo/bullet/shrapnel/on_hit_obj(obj/O, obj/projectile/P)
if(istype(O, /obj/structure/barricade))
var/obj/structure/barricade/B = O
- B.health -= rand(5, 10)
+ B.health -= rand(2, 5)
B.update_health(1)
-/datum/ammo/bullet/shrapnel/on_hit_mob(mob/living/carbon/xeno, obj/projectile/projectile, mob/user)
- if(!shrapnel_chance) // no shrapnell , no special effects
- return
- if(isxeno(xeno))
- xeno.apply_effect(4, SLOW) // multiple hits dont stack they just renew the duration
- xeno.apply_armoured_damage(damage * 0.6, ARMOR_BULLET, BRUTE, , penetration) // xenos have a lot of HP
/datum/ammo/bullet/shrapnel/breaching/set_bullet_traits()
. = ..()
@@ -49,7 +47,7 @@
name = ".22 hornet round"
icon_state = "hornet_round"
flags_ammo_behavior = AMMO_BALLISTIC
- damage = 10
+ damage = 8
shrapnel_chance = 0
shell_speed = AMMO_SPEED_TIER_3//she fast af boi
penetration = ARMOR_PENETRATION_TIER_5
@@ -70,7 +68,7 @@
icon_state = "beanbag" // looks suprisingly a lot like flaming shrapnel chunks
flags_ammo_behavior = AMMO_STOPPED_BY_COVER
shell_speed = AMMO_SPEED_TIER_1
- damage = 30
+ damage = 20
penetration = ARMOR_PENETRATION_TIER_4
/datum/ammo/bullet/shrapnel/incendiary/set_bullet_traits()
@@ -95,6 +93,7 @@
/datum/ammo/bullet/shrapnel/metal
name = "metal shrapnel"
icon_state = "shrapnelshot_bit"
+ flags_ammo_behavior = AMMO_STOPPED_BY_COVER|AMMO_BALLISTIC
shell_speed = AMMO_SPEED_TIER_1
damage = 30
shrapnel_chance = 15
diff --git a/code/datums/ammo/xeno.dm b/code/datums/ammo/xeno.dm
index 653beba573e9..11873a7202d5 100644
--- a/code/datums/ammo/xeno.dm
+++ b/code/datums/ammo/xeno.dm
@@ -162,7 +162,7 @@
/datum/ammo/xeno/acid/on_hit_mob(mob/M, obj/projectile/P)
if(iscarbon(M))
var/mob/living/carbon/C = M
- if(C.status_flags & XENO_HOST && HAS_TRAIT(C, TRAIT_NESTED) || C.stat == DEAD)
+ if(C.status_flags & XENO_HOST && HAS_TRAIT(C, TRAIT_NESTED) || C.stat == DEAD || HAS_TRAIT(C, TRAIT_HAULED))
return FALSE
..()
@@ -252,7 +252,7 @@
/datum/ammo/xeno/boiler_gas/on_hit_mob(mob/moob, obj/projectile/proj)
if(iscarbon(moob))
var/mob/living/carbon/carbon = moob
- if(carbon.status_flags & XENO_HOST && HAS_TRAIT(carbon, TRAIT_NESTED) || carbon.stat == DEAD)
+ if(carbon.status_flags & XENO_HOST && HAS_TRAIT(carbon, TRAIT_NESTED) || carbon.stat == DEAD || HAS_TRAIT(carbon, TRAIT_HAULED))
return
var/datum/effects/neurotoxin/neuro_effect = locate() in moob.effects_list
if(!neuro_effect)
@@ -302,7 +302,7 @@
/datum/ammo/xeno/boiler_gas/acid/on_hit_mob(mob/moob, obj/projectile/proj)
if(iscarbon(moob))
var/mob/living/carbon/carbon = moob
- if(carbon.status_flags & XENO_HOST && HAS_TRAIT(carbon, TRAIT_NESTED) || carbon.stat == DEAD)
+ if(carbon.status_flags & XENO_HOST && HAS_TRAIT(carbon, TRAIT_NESTED) || carbon.stat == DEAD || HAS_TRAIT(carbon, TRAIT_HAULED))
return
to_chat(moob,SPAN_HIGHDANGER("Acid covers your body! Oh fuck!"))
playsound(moob,"acid_strike",75,1)
@@ -330,7 +330,7 @@
/datum/ammo/xeno/bone_chips/on_hit_mob(mob/living/M, obj/projectile/P)
if(iscarbon(M))
var/mob/living/carbon/C = M
- if((HAS_FLAG(C.status_flags, XENO_HOST) && HAS_TRAIT(C, TRAIT_NESTED)) || C.stat == DEAD)
+ if((HAS_FLAG(C.status_flags, XENO_HOST) && HAS_TRAIT(C, TRAIT_NESTED)) || C.stat == DEAD || HAS_TRAIT(C, TRAIT_HAULED))
return
if(ishuman_strict(M) || isxeno(M))
playsound(M, 'sound/effects/spike_hit.ogg', 25, 1, 1)
@@ -360,7 +360,7 @@
/datum/ammo/xeno/bone_chips/spread/runner/on_hit_mob(mob/living/M, obj/projectile/P)
if(iscarbon(M))
var/mob/living/carbon/C = M
- if((HAS_FLAG(C.status_flags, XENO_HOST) && HAS_TRAIT(C, TRAIT_NESTED)) || C.stat == DEAD)
+ if((HAS_FLAG(C.status_flags, XENO_HOST) && HAS_TRAIT(C, TRAIT_NESTED)) || C.stat == DEAD || HAS_TRAIT(C, TRAIT_HAULED))
return
if(ishuman_strict(M) || isxeno(M))
playsound(M, 'sound/effects/spike_hit.ogg', 25, 1, 1)
diff --git a/code/datums/diseases/xeno_transformation.dm b/code/datums/diseases/xeno_transformation.dm
index 9bd5b965c2cc..9f49c2f1259a 100644
--- a/code/datums/diseases/xeno_transformation.dm
+++ b/code/datums/diseases/xeno_transformation.dm
@@ -31,13 +31,13 @@
if (prob(4))
to_chat(affected_mob, SPAN_DANGER("You can feel something move...inside."))
if (prob(5))
- to_chat(affected_mob, "\italic " + pick("Soon we will be one...", "Must... devour...", "Capture...", "We are perfect."))
+ to_chat(affected_mob, "\italic " + pick("Soon we will be one...", "Must... evolve...", "Capture...", "We are perfect."))
if(4)
if (prob(10))
to_chat(affected_mob, pick(SPAN_DANGER("Your skin feels very tight."), SPAN_DANGER("Your blood boils!")))
affected_mob.take_limb_damage(3)
if (prob(5))
- affected_mob.whisper(pick("Soon we will be one...", "Must... devour...", "Capture...", "We are perfect.", "Hsssshhhhh!"))
+ affected_mob.whisper(pick("Soon we will be one...", "Must... evolve...", "Capture...", "We are perfect.", "Hsssshhhhh!"))
if (prob(8))
to_chat(affected_mob, SPAN_DANGER("You can feel... something...inside you."))
if(5)
diff --git a/code/datums/effects/acid.dm b/code/datums/effects/acid.dm
index 8d800ef0bfa0..f98afe012ef4 100644
--- a/code/datums/effects/acid.dm
+++ b/code/datums/effects/acid.dm
@@ -41,7 +41,7 @@
if(ishuman(A))
var/mob/living/carbon/human/H = A
- if(H.status_flags & XENO_HOST && HAS_TRAIT(H, TRAIT_NESTED) || H.stat == DEAD)
+ if(H.status_flags & XENO_HOST && HAS_TRAIT(H, TRAIT_NESTED) || H.stat == DEAD || HAS_TRAIT(H, TRAIT_HAULED))
return FALSE
. = ..()
diff --git a/code/datums/pain/_pain.dm b/code/datums/pain/_pain.dm
index 6c494fc65ed2..a648eb8cf427 100644
--- a/code/datums/pain/_pain.dm
+++ b/code/datums/pain/_pain.dm
@@ -195,7 +195,7 @@
if(new_level >= PAIN_LEVEL_SEVERE && feels_pain)
RegisterSignal(source_mob, COMSIG_MOB_DRAGGED, PROC_REF(oxyloss_drag), override = TRUE)
- RegisterSignal(source_mob, COMSIG_MOB_DEVOURED, PROC_REF(handle_devour), override = TRUE)
+ RegisterSignal(source_mob, COMSIG_MOB_HAULED, PROC_REF(handle_haul), override = TRUE)
RegisterSignal(source_mob, COMSIG_MOVABLE_PRE_THROW, PROC_REF(oxy_kill), override = TRUE)
last_level = new_level
@@ -232,7 +232,7 @@
if(new_level < PAIN_LEVEL_SEVERE)
UnregisterSignal(source_mob, list(
COMSIG_MOB_DRAGGED,
- COMSIG_MOB_DEVOURED,
+ COMSIG_MOB_HAULED,
COMSIG_MOVABLE_PRE_THROW
))
@@ -294,14 +294,14 @@
if(H.species.flags & HAS_HARDCRIT)
source.apply_damage(20, OXY)
-/datum/pain/proc/handle_devour(mob/living/source, mob/living/carbon/xenomorph/devourer)
+/datum/pain/proc/handle_haul(mob/living/source, mob/living/carbon/xenomorph/hauler)
SIGNAL_HANDLER
if(source.chestburst)
return
- if(source.ally_of_hivenumber(devourer.hivenumber))
+ if(source.ally_of_hivenumber(hauler.hivenumber))
return
oxy_kill(source)
- return COMPONENT_CANCEL_DEVOUR
+ return COMPONENT_CANCEL_HAUL
/datum/pain/proc/oxy_kill(mob/living/source)
SIGNAL_HANDLER
diff --git a/code/datums/pain/pain_yautja.dm b/code/datums/pain/pain_yautja.dm
index 94f5e8d33650..e2acce0ea2e4 100644
--- a/code/datums/pain/pain_yautja.dm
+++ b/code/datums/pain/pain_yautja.dm
@@ -17,7 +17,7 @@
/datum/pain/yautja/oxyloss_drag(mob/living/source, mob/puller)
return
-/datum/pain/yautja/handle_devour(mob/living/source)
+/datum/pain/yautja/handle_haul(mob/living/source)
return
/datum/pain/yautja/oxy_kill(mob/living/source)
diff --git a/code/datums/tutorial/xenomorph/xenomorph_basic.dm b/code/datums/tutorial/xenomorph/xenomorph_basic.dm
index ed9a9b810b45..c5e62d543a18 100644
--- a/code/datums/tutorial/xenomorph/xenomorph_basic.dm
+++ b/code/datums/tutorial/xenomorph/xenomorph_basic.dm
@@ -209,15 +209,15 @@
SIGNAL_HANDLER
TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
UnregisterSignal(human_dummy, COMSIG_MOVABLE_XENO_START_PULLING)
- message_to_player("Well done. Now devour the human by clicking on your character with the grab selected in your hand. You must not move during this process.")
- update_objective("Devour the grabbed human by clicking on them with the grab in-hand.")
- RegisterSignal(human_dummy, COMSIG_MOB_DEVOURED, PROC_REF(nest_cap_phase_five))
+ message_to_player("Well done. Now haul the human by clicking on your character with the grab selected in your hand. You must not move during this process.")
+ update_objective("Haul the grabbed human by clicking on them with the grab in-hand.")
+ RegisterSignal(human_dummy, COMSIG_MOB_HAULED, PROC_REF(nest_cap_phase_five))
/datum/tutorial/xenomorph/basic/proc/nest_cap_phase_five()
SIGNAL_HANDLER
- message_to_player("Well done, you can reguritate the human using the new ability you have gained.")
- message_to_player("Be careful. Real humans may put up a fight and can try to cut out of you from inside!")
- give_action(xeno, /datum/action/xeno_action/onclick/regurgitate)
+ message_to_player("Well done, you can release the human using the new ability you have gained.")
+ message_to_player("Be careful. Real humans may put up a fight and can try to cut out of your grip, killing you!")
+ give_action(xeno, /datum/action/xeno_action/onclick/release_haul)
addtimer(CALLBACK(src, PROC_REF(nest_cap_phase_six)), 15 SECONDS)
/datum/tutorial/xenomorph/basic/proc/nest_cap_phase_six()
@@ -227,7 +227,7 @@
/datum/tutorial/xenomorph/basic/proc/nest_cap_phase_seven()
TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
- UnregisterSignal(human_dummy, COMSIG_MOB_DEVOURED)
+ UnregisterSignal(human_dummy, COMSIG_MOB_HAULED)
RegisterSignal(human_dummy, COMSIG_MOB_NESTED, PROC_REF(on_mob_nested))
message_to_player("Nest the captive human!")
update_objective("Nest the captive human!")
diff --git a/code/game/jobs/job/civilians/other/liaison.dm b/code/game/jobs/job/civilians/other/liaison.dm
index 765235233e85..26799242cfd2 100644
--- a/code/game/jobs/job/civilians/other/liaison.dm
+++ b/code/game/jobs/job/civilians/other/liaison.dm
@@ -6,7 +6,7 @@
selection_class = "job_cl"
flags_startup_parameters = ROLE_ADD_TO_DEFAULT
gear_preset = /datum/equipment_preset/uscm_ship/liaison
- entry_message_body = "As a representative of Weyland-Yutani Corporation, your job requires you to stay in character at all times. While in the AO (Area of Operation), you are subject to orders given by military personnel. On ship, you are subject to orders only by the Command and Security departments. You are not required to follow any orders but you can be arrested if you do not. Your primary job is to observe and report back your findings to Weyland-Yutani. Follow regular game rules unless told otherwise by your superiors. Use your office fax machine to communicate with corporate headquarters or to acquire new directives. You may not receive anything back, and this is normal."
+ entry_message_body = "As a representative of Weyland-Yutani Corporation from the Corporate Relations Department, your job requires you to stay in character at all times. While in the AO (Area of Operation), you are subject to orders given by military personnel. On ship, you are subject to orders only by the Command and Security departments. You are not required to follow any orders but you can be arrested if you do not. Your primary job is to observe and report back your findings to Weyland-Yutani. Follow regular game rules unless told otherwise by your superiors. Use your office fax machine to communicate with corporate headquarters or to acquire new directives. You may not receive anything back, and this is normal."
var/mob/living/carbon/human/active_liaison
/datum/job/civilian/liaison/generate_entry_conditions(mob/living/liaison, whitelist_status)
diff --git a/code/game/jobs/job/civilians/support/field_doctor.dm b/code/game/jobs/job/civilians/support/field_doctor.dm
index 0dfa442ca3ea..9c715bb31884 100644
--- a/code/game/jobs/job/civilians/support/field_doctor.dm
+++ b/code/game/jobs/job/civilians/support/field_doctor.dm
@@ -12,7 +12,7 @@
entry_message_body = "You are a Field Doctor tasked with keeping the marines healthy and strong in the field, usually in the form of surgery. You must stay onboard the Almayer medical bay if there are no other doctors present and until the FOB is secured. Your superiors may also delay your deployment to the field."
AddTimelock(/datum/job/civilian/field_doctor, list(
- JOB_MEDIC_ROLES = 5 HOURS
+ JOB_DOCTOR_ROLES = 5 HOURS
))
/obj/effect/landmark/start/field_doctor
diff --git a/code/game/machinery/autolathe_datums.dm b/code/game/machinery/autolathe_datums.dm
index fcff34a86f8e..a9eee084b34c 100644
--- a/code/game/machinery/autolathe_datums.dm
+++ b/code/game/machinery/autolathe_datums.dm
@@ -67,6 +67,11 @@
path = /obj/item/tool/wrench
category = AUTOLATHE_CATEGORY_TOOLS
+/datum/autolathe/recipe/lightreplacer
+ name = "light replacer"
+ path = /obj/item/device/lightreplacer/empty
+ category = AUTOLATHE_CATEGORY_TOOLS
+
/datum/autolathe/recipe/mop
name = "mop"
path = /obj/item/tool/mop
diff --git a/code/game/machinery/vending/cm_vending.dm b/code/game/machinery/vending/cm_vending.dm
index 6940fbdd5355..5f4515167995 100644
--- a/code/game/machinery/vending/cm_vending.dm
+++ b/code/game/machinery/vending/cm_vending.dm
@@ -844,7 +844,7 @@ GLOBAL_LIST_EMPTY(vending_products)
icon_state = "gear"
use_points = TRUE
vendor_theme = VENDOR_THEME_USCM
- vend_flags = VEND_CLUTTER_PROTECTION|VEND_CATEGORY_CHECK|VEND_TO_HAND
+ vend_flags = VEND_CLUTTER_PROTECTION|VEND_CATEGORY_CHECK|VEND_UNIFORM_AUTOEQUIP
/obj/structure/machinery/cm_vending/gear/ui_static_data(mob/user)
. = ..(user)
diff --git a/code/game/machinery/vending/vendor_types/engineering.dm b/code/game/machinery/vending/vendor_types/engineering.dm
index 35e78e5cc962..1fa773f9cf65 100644
--- a/code/game/machinery/vending/vendor_types/engineering.dm
+++ b/code/game/machinery/vending/vendor_types/engineering.dm
@@ -40,7 +40,8 @@
list("ME3 Hand Welder", floor(scale * 2), /obj/item/tool/weldingtool/simple, VENDOR_ITEM_REGULAR),
list("Screwdriver", floor(scale * 4), /obj/item/tool/screwdriver, VENDOR_ITEM_REGULAR),
list("Wirecutters", floor(scale * 4), /obj/item/tool/wirecutters, VENDOR_ITEM_REGULAR),
- list("Wrench", floor(scale * 4), /obj/item/tool/wrench, VENDOR_ITEM_REGULAR)
+ list("Wrench", floor(scale * 4), /obj/item/tool/wrench, VENDOR_ITEM_REGULAR),
+ list("Light Replacer", floor(scale * 4), /obj/item/device/lightreplacer, VENDOR_ITEM_REGULAR)
)
/obj/structure/machinery/cm_vending/sorted/tech/comtech_tools
@@ -65,6 +66,7 @@
list("Wrench", floor(scale * 4), /obj/item/tool/wrench, VENDOR_ITEM_REGULAR),
list("Multitool", floor(scale * 4), /obj/item/device/multitool, VENDOR_ITEM_REGULAR),
list("ME3 Hand Welder", floor(scale * 2), /obj/item/tool/weldingtool/simple, VENDOR_ITEM_REGULAR),
+ list("Light Replacer", floor(scale * 4), /obj/item/device/lightreplacer, VENDOR_ITEM_REGULAR),
list("UTILITY", -1, null, null),
list("Sentry Gun Network Laptop", 4, /obj/item/device/sentry_computer, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/intelligence_officer.dm b/code/game/machinery/vending/vendor_types/intelligence_officer.dm
index cddebc9562bf..49554f4c78c4 100644
--- a/code/game/machinery/vending/vendor_types/intelligence_officer.dm
+++ b/code/game/machinery/vending/vendor_types/intelligence_officer.dm
@@ -28,6 +28,9 @@ GLOBAL_LIST_INIT(cm_vending_gear_intelligence_officer, list(
list("VP78 Pistol", 15, /obj/item/storage/box/guncase/vp78, null, VENDOR_ITEM_REGULAR),
list("SU-6 Smart Pistol", 15, /obj/item/storage/box/guncase/smartpistol, null, VENDOR_ITEM_REGULAR),
+ list("RESTRICTED GEAR", 0, null, null, null),
+ list("M276 Pattern Combat Toolbelt Rig", 15, /obj/item/storage/belt/gun/utility, null, VENDOR_ITEM_RECOMMENDED),
+
list("POUCHES", 0, null, null, null),
list("Large Magazine Pouch", 10, /obj/item/storage/pouch/magazine/large, null, VENDOR_ITEM_REGULAR),
list("Large Shotgun Shell Pouch", 10, /obj/item/storage/pouch/shotgun/large, null, VENDOR_ITEM_REGULAR),
diff --git a/code/game/objects/effects/effect_system/smoke.dm b/code/game/objects/effects/effect_system/smoke.dm
index 4c7752538515..1ad4ed34127a 100644
--- a/code/game/objects/effects/effect_system/smoke.dm
+++ b/code/game/objects/effects/effect_system/smoke.dm
@@ -543,7 +543,7 @@
return FALSE
if(isyautja(affected_mob) && prob(75))
return FALSE
- if(HAS_TRAIT(affected_mob, TRAIT_NESTED) && affected_mob.status_flags & XENO_HOST)
+ if(HAS_TRAIT(affected_mob, TRAIT_NESTED) && affected_mob.status_flags & XENO_HOST || HAS_TRAIT(affected_mob, TRAIT_HAULED))
return FALSE
affected_mob.last_damage_data = cause_data
@@ -600,7 +600,7 @@
return FALSE
if(isyautja(moob))
return FALSE
- if(HAS_TRAIT(moob, TRAIT_NESTED) && moob.status_flags & XENO_HOST)
+ if(HAS_TRAIT(moob, TRAIT_NESTED) && moob.status_flags & XENO_HOST || HAS_TRAIT(moob, TRAIT_HAULED))
return FALSE
var/mob/living/carbon/human/human_moob
@@ -658,7 +658,7 @@
return FALSE
if(isyautja(moob) && prob(75))
return FALSE
- if(HAS_TRAIT(moob, TRAIT_NESTED) && moob.status_flags & XENO_HOST)
+ if(HAS_TRAIT(moob, TRAIT_NESTED) && moob.status_flags & XENO_HOST || HAS_TRAIT(moob, TRAIT_HAULED))
return FALSE
var/mob/living/carbon/human/human_moob
diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm
index 42af8347a5ee..dcf2ad5c17e4 100644
--- a/code/game/objects/items/devices/lightreplacer.dm
+++ b/code/game/objects/items/devices/lightreplacer.dm
@@ -41,7 +41,7 @@
/obj/item/device/lightreplacer
name = "light replacer"
- desc = "A device to automatically replace lights. Refill with working lightbulbs."
+ desc = "A device to automatically replace lights. Can be refill with working lightbulbs and sheets of glass, and can recycle broken lightbulbs."
icon = 'icons/obj/janitor.dmi'
icon_state = "lightreplacer0"
@@ -54,51 +54,59 @@
flags_atom = FPRINT|CONDUCT
flags_equip_slot = SLOT_WAIST
+ matter = list("metal" = 20,"glass" = 50)
var/max_uses = 50
- var/uses = 0
+ var/uses = 50
var/failmsg = ""
var/charge = 1
+ var/recycle = 0
+ var/max_recycle = 3
+
+/obj/item/device/lightreplacer/empty
+ uses = 0
/obj/item/device/lightreplacer/Initialize()
. = ..()
- uses = max_uses
failmsg = "The [name]'s refill light blinks red."
/obj/item/device/lightreplacer/get_examine_text(mob/user)
. = ..()
- . += "It has [uses] lights remaining."
+ . += "It has [uses] lights remaining, and [recycle] broken lights stored."
/obj/item/device/lightreplacer/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/stack/sheet/glass))
- var/obj/item/stack/sheet/glass/G = W
+ var/obj/item/stack/sheet/glass/glass = W
if(uses >= max_uses)
- to_chat(user, SPAN_WARNING("[src.name] is full."))
+ to_chat(user, SPAN_WARNING("[src] is full."))
return
- else if(G.use(1))
+ else if(glass.use(1))
AddUses(5)
- to_chat(user, SPAN_NOTICE("You insert a piece of glass into the [src.name]. You have [uses] lights remaining."))
+ to_chat(user, SPAN_NOTICE("You insert a piece of glass into the [src]. You have [uses] lights remaining."))
return
else
to_chat(user, SPAN_WARNING("You need one sheet of glass to replace lights."))
if(istype(W, /obj/item/light_bulb))
- var/obj/item/light_bulb/L = W
- if(L.status == 0) // LIGHT OKAY
+ var/obj/item/light_bulb/bulb = W
+ if(bulb.status == 0) // LIGHT OKAY
if(uses < max_uses)
AddUses(1)
- to_chat(user, "You insert the [L.name] into the [src.name]. You have [uses] lights remaining.")
+ to_chat(user, SPAN_NOTICE("You insert the [bulb] into [src]. You have [uses] lights remaining."))
user.drop_held_item()
- qdel(L)
+ qdel(bulb)
return
else
- to_chat(user, "You need a working light.")
+ Recycle()
+ to_chat(user, SPAN_NOTICE("You insert the [bulb] into [src] for recycling."))
+ user.drop_held_item()
+ qdel(bulb)
return
/obj/item/device/lightreplacer/attack_self(mob/user)
..()
- to_chat(usr, "It has [uses] lights remaining.")
+ to_chat(usr, "It has [uses] lights remaining, and has [recycle] broken lights stored.")
/obj/item/device/lightreplacer/update_icon()
icon_state = "lightreplacer0"
@@ -106,12 +114,12 @@
/obj/item/device/lightreplacer/proc/Use(mob/user)
- playsound(src.loc, 'sound/machines/click.ogg', 25, 1)
AddUses(-1)
return 1
// Negative numbers will subtract
/obj/item/device/lightreplacer/proc/AddUses(amount = 1)
+ playsound(src, 'sound/machines/click.ogg', 25, 1)
uses = min(max(uses + amount, 0), max_uses)
/obj/item/device/lightreplacer/proc/Charge(mob/user)
@@ -120,6 +128,16 @@
AddUses(1)
charge = 1
+/obj/item/device/lightreplacer/proc/Recycle(mob/living/U)
+ if(recycle == max_recycle)
+ recycle = 0
+ AddUses(1)
+ playsound(src, 'sound/machines/ding.ogg', 5, 1)
+ return
+ else
+ playsound(src, 'sound/machines/click.ogg', 25, 1)
+ recycle += 1
+
/obj/item/device/lightreplacer/proc/ReplaceLight(obj/structure/machinery/light/target, mob/living/U)
if(target.status != LIGHT_OK)
@@ -130,33 +148,28 @@
if(target.status != LIGHT_EMPTY)
- var/obj/item/light_bulb/L1 = new target.light_type(target.loc)
- L1.status = target.status
- L1.rigged = target.rigged
- L1.brightness = target.brightness
- L1.switchcount = target.switchcount
target.switchcount = 0
- L1.update()
-
target.status = LIGHT_EMPTY
target.update()
- var/obj/item/light_bulb/L2 = new target.light_type()
+ Recycle()
+
+ var/obj/item/light_bulb/bulb = new target.light_type()
- target.status = L2.status
- target.switchcount = L2.switchcount
+ target.status = bulb.status
+ target.switchcount = bulb.switchcount
target.rigged = FALSE
- target.brightness = L2.brightness
+ target.brightness = bulb.brightness
target.on = target.has_power()
target.update()
- qdel(L2)
+ qdel(bulb)
if(target.on && target.rigged)
target.explode()
return
else
- to_chat(U, failmsg)
+ to_chat(U, SPAN_DANGER(failmsg))
return
else
to_chat(U, "There is a working [target.fitting] already inserted.")
diff --git a/code/game/objects/items/devices/radio/listening_bugs.dm b/code/game/objects/items/devices/radio/listening_bugs.dm
index a87c19245ac3..a3a381997795 100644
--- a/code/game/objects/items/devices/radio/listening_bugs.dm
+++ b/code/game/objects/items/devices/radio/listening_bugs.dm
@@ -279,6 +279,11 @@
subspace_switchable = FALSE
broadcasting = TRUE
bug_broadcast_level = LISTENING_BUG_NEVER //Don't want fax responder devices broadcasting to ghosts because it will duplicate a lot of messages every round all the time.
+ alpha = 0
+ mouse_opacity = 0
+ explo_proof = TRUE
+ unacidable = TRUE
+ emp_proof = TRUE
/obj/item/device/radio/listening_bug/radio_linked/fax/wy
frequency = FAX_WY_FREQ
diff --git a/code/game/objects/items/explosives/grenades/grenade.dm b/code/game/objects/items/explosives/grenades/grenade.dm
index f157d7f8d931..2ddd588890d1 100644
--- a/code/game/objects/items/explosives/grenades/grenade.dm
+++ b/code/game/objects/items/explosives/grenades/grenade.dm
@@ -44,6 +44,9 @@
to_chat(user, SPAN_WARNING("Your programming prevents you from using this!"))
return FALSE
+ if(HAS_TRAIT(user, TRAIT_HAULED)) // If somehow they have a grenade in hand while hauled, we don't want them to prime it
+ return FALSE
+
return TRUE
/obj/item/explosive/grenade/dropped(mob/user)
diff --git a/code/game/objects/items/reagent_containers/hypospray.dm b/code/game/objects/items/reagent_containers/hypospray.dm
index 36762ba1ea90..b220382ac9ec 100644
--- a/code/game/objects/items/reagent_containers/hypospray.dm
+++ b/code/game/objects/items/reagent_containers/hypospray.dm
@@ -35,7 +35,8 @@
/obj/item/reagent_container/hypospray/attack_self(mob/user)
..()
-
+ if(HAS_TRAIT(user, TRAIT_HAULED))
+ return
if(next_inject > world.time)
return
next_inject = world.time + inject_cd
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 8ed279867678..590c8d68e6ec 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -96,7 +96,7 @@ GLOBAL_LIST_INIT_TYPED(metal_recipes, /datum/stack_recipe, list ( \
* Plasteel
*/
GLOBAL_LIST_INIT_TYPED(plasteel_recipes, /datum/stack_recipe, list ( \
- new/datum/stack_recipe("folding plasteel barricade", /obj/structure/barricade/plasteel, 8, time = 4 SECONDS, one_per_turf = ONE_TYPE_PER_TURF, on_floor = 1, skill_req = SKILL_CONSTRUCTION, skill_lvl = SKILL_CONSTRUCTION_ENGI, min_time = 2 SECONDS),
+ new/datum/stack_recipe("folding plasteel barricade", /obj/structure/barricade/plasteel, 8, time = 4 SECONDS, one_per_turf = ONE_TYPE_PER_BORDER, on_floor = 1, skill_req = SKILL_CONSTRUCTION, skill_lvl = SKILL_CONSTRUCTION_ENGI, min_time = 2 SECONDS),
new/datum/stack_recipe("plasteel barricade", /obj/structure/barricade/metal/plasteel, 6, time = 8 SECONDS, one_per_turf = ONE_TYPE_PER_BORDER, on_floor = 1, skill_req = SKILL_CONSTRUCTION, skill_lvl = SKILL_CONSTRUCTION_ENGI, min_time = 2 SECONDS),
null, \
new/datum/stack_recipe("reinforced window frame", /obj/structure/window_frame/colony/reinforced, 5, time = 40, one_per_turf = ONE_TYPE_PER_TURF, on_floor = 1, skill_req = SKILL_CONSTRUCTION, skill_lvl = SKILL_CONSTRUCTION_ENGI),
diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/storage.dm
index 75c067c7f037..b4203f14c95f 100644
--- a/code/game/objects/items/storage/storage.dm
+++ b/code/game/objects/items/storage/storage.dm
@@ -396,14 +396,21 @@ GLOBAL_LIST_EMPTY_TYPED(item_storage_box_cache, /datum/item_storage_box)
///Returns TRUE if there is room for the given item. W_class_override allows checking for just a generic W_class, meant for checking shotgun handfuls without having to spawn and delete one just to check.
/obj/item/storage/proc/has_room(obj/item/new_item, W_class_override = null)
for(var/obj/item/cur_item in contents)
- if(!istype(cur_item, /obj/item/stack) || !istype(new_item, /obj/item/stack))
- continue
+ if(istype(cur_item, /obj/item/stack) && istype(new_item, /obj/item/stack))
+ var/obj/item/stack/cur_stack = cur_item
+ var/obj/item/stack/new_stack = new_item
- var/obj/item/stack/cur_stack = cur_item
- var/obj/item/stack/new_stack = new_item
+ if(cur_stack.amount < cur_stack.max_amount && new_stack.stack_id == cur_stack.stack_id)
+ return TRUE
- if(cur_stack.amount < cur_stack.max_amount && new_stack.stack_id == cur_stack.stack_id)
- return TRUE
+ else if(istype(cur_item, /obj/item/ammo_magazine/handful) && istype(new_item, /obj/item/ammo_magazine/handful))
+ var/obj/item/ammo_magazine/handful/cur_handful = cur_item
+ var/obj/item/ammo_magazine/handful/new_handful = new_item
+
+ if(cur_handful.is_transferable(new_handful))
+ return TRUE
+ else
+ continue
if(storage_slots != null && length(contents) < storage_slots)
return TRUE //At least one open slot.
@@ -522,6 +529,21 @@ user can be null, it refers to the potential mob doing the insertion.**/
return FALSE
else
return TRUE
+ else if(istype(new_item, /obj/item/ammo_magazine/handful))
+ var/obj/item/ammo_magazine/handful/new_handful = new_item
+
+ for(var/obj/item/cur_item in contents)
+ if(!istype(cur_item, /obj/item/ammo_magazine/handful))
+ continue
+ var/obj/item/ammo_magazine/handful/cur_handful = cur_item
+ if(cur_handful.is_transferable(new_handful))
+ cur_handful.transfer_ammo(new_handful, user, new_handful.current_rounds)
+
+ if(!QDELETED(new_handful) && can_be_inserted(new_handful, user))
+ if(!user.drop_inv_item_to_loc(new_handful, src))
+ return FALSE
+ else
+ return TRUE
if(!user.drop_inv_item_to_loc(new_item, src))
return FALSE
@@ -616,6 +638,8 @@ W is always an item. stop_warning prevents messaging. user may be null.**/
return handle_item_insertion(W, prevent_warning, user)
/obj/item/storage/attack_hand(mob/user, mods)
+ if(HAS_TRAIT(user, TRAIT_HAULED))
+ return
if (loc == user)
if((mods && mods["alt"] || storage_flags & STORAGE_USING_DRAWING_METHOD) && ishuman(user) && length(contents)) //Alt mod can reach attack_hand through the clicked() override.
var/obj/item/I
diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm
index e72bf008927a..68a5f4ed074e 100644
--- a/code/game/objects/items/weapons/twohanded.dm
+++ b/code/game/objects/items/weapons/twohanded.dm
@@ -253,7 +253,7 @@
item_icons = list(
WEAR_L_HAND = 'icons/mob/humans/onmob/inhands/weapons/melee/spears_lefthand.dmi',
WEAR_R_HAND = 'icons/mob/humans/onmob/inhands/weapons/melee/spears_righthand.dmi',
- WEAR_BACK = 'icons/mob/humans/onmob/clothing/back/misc.dmi'
+ WEAR_BACK = 'icons/mob/humans/onmob/clothing/back/melee_weapons.dmi'
)
w_class = SIZE_LARGE
flags_equip_slot = SLOT_BACK
@@ -350,7 +350,7 @@
item_icons = list(
WEAR_L_HAND = 'icons/mob/humans/onmob/inhands/equipment/tools_lefthand.dmi',
WEAR_R_HAND = 'icons/mob/humans/onmob/inhands/equipment/tools_righthand.dmi',
- WEAR_BACK = 'icons/mob/humans/onmob/clothing/back/misc.dmi'
+ WEAR_BACK = 'icons/mob/humans/onmob/clothing/back/melee_weapons.dmi'
)
icon_state = "d2_breacher"
item_state = "d2_breacher"
diff --git a/code/game/objects/structures/barricade/folding.dm b/code/game/objects/structures/barricade/folding.dm
index 1169c6c9129d..3b8336a5d511 100644
--- a/code/game/objects/structures/barricade/folding.dm
+++ b/code/game/objects/structures/barricade/folding.dm
@@ -219,8 +219,8 @@
if(recentlyflipped)
to_chat(user, SPAN_NOTICE("[src] has been flipped too recently!"))
return
- user.visible_message(SPAN_NOTICE("[user] flips [src] open."),
- SPAN_NOTICE("You flip [src] open."))
+ user.visible_message(SPAN_NOTICE("[user] flips [src] closed."),
+ SPAN_NOTICE("You flip [src] closed."))
open(src)
recentlyflipped = TRUE
spawn(10)
@@ -231,8 +231,8 @@
if(recentlyflipped)
to_chat(user, SPAN_NOTICE("[src] has been flipped too recently!"))
return
- user.visible_message(SPAN_NOTICE("[user] flips [src] closed."),
- SPAN_NOTICE("You flip [src] closed."))
+ user.visible_message(SPAN_NOTICE("[user] flips [src] open."),
+ SPAN_NOTICE("You flip [src] open."))
close(src)
recentlyflipped = TRUE
spawn(10)
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index 8ba5c0a721c6..adb45256a919 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -708,6 +708,8 @@ ICEY GRASS. IT LOOKS LIKE IT'S MADE OF ICE.
if(ishuman(L))
var/mob/living/carbon/human/H = L
var/stuck = rand(0,10)
+ if(HAS_TRAIT(L, TRAIT_HAULED))
+ return
switch(stuck)
if(0 to 4)
var/new_slowdown = H.next_move_slowdown + rand(2,3)
diff --git a/code/game/turfs/auto_turf.dm b/code/game/turfs/auto_turf.dm
index 8edd13f58612..e965323d86ed 100644
--- a/code/game/turfs/auto_turf.dm
+++ b/code/game/turfs/auto_turf.dm
@@ -235,12 +235,13 @@
slow_amount = 0.15
can_stuck = 0
var/new_slowdown = C.next_move_slowdown + (slow_amount * bleed_layer)
- if(prob(2))
- to_chat(C, SPAN_WARNING("Moving through [src] slows you down.")) //Warning only
- else if(can_stuck && bleed_layer == 4 && prob(2))
- to_chat(C, SPAN_WARNING("You get stuck in [src] for a moment!"))
- new_slowdown += 10
- C.next_move_slowdown = new_slowdown
+ if(!HAS_TRAIT(C, TRAIT_HAULED))
+ if(prob(2))
+ to_chat(C, SPAN_WARNING("Moving through [src] slows you down.")) //Warning only
+ else if(can_stuck && bleed_layer == 4 && prob(2))
+ to_chat(C, SPAN_WARNING("You get stuck in [src] for a moment!"))
+ new_slowdown += 10
+ C.next_move_slowdown = new_slowdown
..()
/turf/open/auto_turf/snow/layer0 //still have to manually define the layers for the editor
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index 66770163bf18..862402919ded 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -812,7 +812,8 @@
if(world.time % 5)
if(ismob(AM))
var/mob/rivermob = AM
- to_chat(rivermob, SPAN_WARNING("Moving through the incredibly deep ocean slows you down a lot!"))
+ if(!HAS_TRAIT(rivermob, TRAIT_HAULED))
+ to_chat(rivermob, SPAN_WARNING("Moving through the incredibly deep ocean slows you down a lot!"))
/turf/open/gm/coast
name = "coastline"
diff --git a/code/game/turfs/snow.dm b/code/game/turfs/snow.dm
index c8afd734e862..e6954fc9cd38 100644
--- a/code/game/turfs/snow.dm
+++ b/code/game/turfs/snow.dm
@@ -51,12 +51,13 @@
slow_amount = 0.25
can_stuck = 0
var/new_slowdown = C.next_move_slowdown + (slow_amount * bleed_layer)
- if(prob(2))
- to_chat(C, SPAN_WARNING("Moving through [src] slows you down.")) //Warning only
- else if(can_stuck && bleed_layer == 3 && prob(2))
- to_chat(C, SPAN_WARNING("You get stuck in [src] for a moment!"))
- new_slowdown += 10
- C.next_move_slowdown = new_slowdown
+ if(!HAS_TRAIT(C, TRAIT_HAULED))
+ if(prob(2))
+ to_chat(C, SPAN_WARNING("Moving through [src] slows you down.")) //Warning only
+ else if(can_stuck && bleed_layer == 3 && prob(2))
+ to_chat(C, SPAN_WARNING("You get stuck in [src] for a moment!"))
+ new_slowdown += 10
+ C.next_move_slowdown = new_slowdown
..()
diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm
index 8b516ced2e6e..8ee544e475ee 100644
--- a/code/game/verbs/ooc.dm
+++ b/code/game/verbs/ooc.dm
@@ -82,27 +82,25 @@
var/byond = icon('icons/effects/effects.dmi', "byondlogo")
prefix += "[icon2html(byond, GLOB.clients)]"
if(CONFIG_GET(flag/ooc_country_flags) && (prefs.toggle_prefs & TOGGLE_OOC_FLAG))
- prefix += "[country2chaticon(src.country, GLOB.clients)]"
+ prefix += "[country2chaticon(country, GLOB.clients)]"
if(donator)
- prefix += "[icon2html('icons/ooc.dmi', GLOB.clients, "Donator")]"
+ prefix += "[icon2html(GLOB.ooc_rank_dmi, GLOB.clients, "Donator")]"
if(isCouncil(src))
- prefix += "[icon2html('icons/ooc.dmi', GLOB.clients, "WhitelistCouncil")]"
+ prefix += "[icon2html(GLOB.ooc_rank_dmi, GLOB.clients, "WhitelistCouncil")]"
var/comm_award = find_community_award_icons()
if(comm_award)
prefix += comm_award
if(admin_holder)
- var/list/rank_icons = icon_states('icons/ooc.dmi')
- var/rankname = admin_holder.rank
- if(rankname in rank_icons)
- prefix += "[icon2html('icons/ooc.dmi', GLOB.clients, admin_holder.rank)]"
-
if(length(admin_holder.extra_titles))
- var/list/extra_rank_icons = icon_states('icons/ooc.dmi')
- var/ooc_icon_state
- for(var/srank in admin_holder.extra_titles)
- ooc_icon_state = trim(srank)
- if(ooc_icon_state in extra_rank_icons)
- prefix += "[icon2html('icons/ooc.dmi', GLOB.clients, ooc_icon_state)]"
+ var/extra_title_state
+ for(var/extra_title in admin_holder.extra_titles)
+ extra_title_state = ckeyEx(extra_title)
+ if(extra_title_state in GLOB.ooc_rank_iconstates)
+ prefix += "[icon2html(GLOB.ooc_rank_dmi, GLOB.clients, extra_title_state)]"
+
+ if(admin_holder.rank in GLOB.ooc_rank_iconstates)
+ prefix += "[icon2html(GLOB.ooc_rank_dmi, GLOB.clients, admin_holder.rank)]"
+
if(prefix)
prefix = "[prefix] "
return prefix
diff --git a/code/modules/admin/autoreply.dm b/code/modules/admin/autoreply.dm
index 24a67256acd8..ee655f86394d 100644
--- a/code/modules/admin/autoreply.dm
+++ b/code/modules/admin/autoreply.dm
@@ -144,9 +144,9 @@ ON_CONFIG_LOAD(/datum/autoreply/mentor/macros)
Rangefinders allow you to get tile coordinates (longitude and latitude) by lasing it while zoomed in (produces a GREEN laser). Ctrl + Click on any open tile to start lasing. Ctrl + Click on your rangefinders to stop lasing without zooming out. Coordinates can be used by Staff Officers to send supply drops or to perform Orbital Bombardment. You also can use them to call mortar fire if there are engineers with a mortar. \
Laser Designators have a second mode (produces a RED laser) that allows highlighting targets for Close Air Support performed by dropship pilots. They also have a fixed ID number that is shown on the pilot's weaponry console. Examine the laser designator to check its ID. Red laser must be maintained as long as needed in order for the dropship pilot to bomb the designated area. To switch between lasing modes, Alt + Click the laser designator. Alternatively, Right + Click it in hand and click \"Toggle Mode\"."
-/datum/autoreply/mentor/devour
- title = "X: Devour as Xeno"
- message = "Devouring is useful to quickly transport incapacitated hosts from one place to another. In order to devour a host as a Xeno, grab the mob (CTRL+Click) and then click on yourself to begin devouring. The host can break out of your stomach, which will result in your death so make sure your target is incapacitated. After approximately 1 minute host will be automatically regurgitated. To release your target voluntary, click 'Regurgitate' on the HUD to throw them back up."
+/datum/autoreply/mentor/haul
+ title = "X: Haul as Xeno"
+ message = "Hauling is useful to quickly transport incapacitated hosts from one place to another. In order to haul a host as a Xeno, grab the mob (CTRL+Click) and then click on yourself to begin hauling. The host can break out of your grip, which will result in your death so make sure your target is incapacitated. After approximately 1 minute host will be automatically released. To release your target voluntary, click 'Release' on the HUD to throw them back up."
/datum/autoreply/mentor/plasma
title = "X: No plasma regen"
diff --git a/code/modules/admin/tabs/admin_tab.dm b/code/modules/admin/tabs/admin_tab.dm
index b949ef42e653..38abdea4ee4d 100644
--- a/code/modules/admin/tabs/admin_tab.dm
+++ b/code/modules/admin/tabs/admin_tab.dm
@@ -351,11 +351,11 @@
var/list/subtle_message_options = list(SUBTLE_MESSAGE_IN_HEAD, SUBTLE_MESSAGE_WEYLAND, SUBTLE_MESSAGE_USCM, SUBTLE_MESSAGE_FACTION)
var/message_option = tgui_input_list(usr, "Choose the method of subtle messaging", "", subtle_message_options)
+ if(!message_option)
+ return
if(message_option == SUBTLE_MESSAGE_FACTION)
var/faction = input("Choose which faction", "") as text|null
- if(!faction)
- return
message_option = faction
var/input = input("Contents of the message", text("Subtle PM to In View")) as text|null
diff --git a/code/modules/admin/tabs/event_tab.dm b/code/modules/admin/tabs/event_tab.dm
index bf434d1fd142..3dbfed7b38ed 100644
--- a/code/modules/admin/tabs/event_tab.dm
+++ b/code/modules/admin/tabs/event_tab.dm
@@ -1143,6 +1143,8 @@
var/duration = 5 SECONDS
var/message = "ADMIN TEST"
var/text_input = tgui_input_text(usr, "Announcement message", "Message Contents", message, timeout = 5 MINUTES)
+ if(!text_input)
+ return // Early return here so people dont have to go through the whole process just to cancel it.
message = text_input
duration = tgui_input_number(usr, "Set the duration of the alert in deci-seconds.", "Duration", 5 SECONDS, 5 MINUTES, 5 SECONDS, 20 SECONDS)
var/confirm = tgui_alert(usr, "Are you sure you wish to send '[message]' to all players for [(duration / 10)] seconds?", "Confirm", list("Yes", "No"), 20 SECONDS)
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
index 64e6bb85bed6..a99d27faf0d8 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
@@ -266,6 +266,13 @@
/proc/_turn(dir, angle)
return turn(dir, angle)
+/proc/_icon_states(icon/thing, mode)
+ if(istype(thing))
+ return icon_states(thing, mode)
+ if(isatom(thing))
+ var/atom/atom_thing = thing
+ return icon_states(atom_thing.icon, mode)
+
/// Auxtools REALLY doesn't know how to handle filters as values;
/// when passed as arguments to auxtools-called procs, they aren't simply treated as nulls -
/// they don't even count towards the length of args.
diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm
index 05bde482f504..deef7119c926 100644
--- a/code/modules/admin/verbs/playsound.dm
+++ b/code/modules/admin/verbs/playsound.dm
@@ -124,7 +124,7 @@
if("Ghosts")
targets = GLOB.observer_list + GLOB.dead_mob_list
if("All In View Range")
- var/list/atom/ranged_atoms = urange(usr.client.view, get_turf(usr))
+ var/list/atom/ranged_atoms = long_range(usr.client.view, get_turf(usr))
for(var/mob/receiver in ranged_atoms)
targets += receiver
if("Single Mob")
diff --git a/code/modules/animations/animation_library.dm b/code/modules/animations/animation_library.dm
index e7cb49c930d4..777fae6977ad 100644
--- a/code/modules/animations/animation_library.dm
+++ b/code/modules/animations/animation_library.dm
@@ -177,8 +177,8 @@ Can look good elsewhere as well.*/
if(A.clone)
if(src.Adjacent(A.clone))
A = A.clone
- if(buckled || anchored)
- return //it would look silly.
+ if(buckled || anchored || HAS_TRAIT(src, TRAIT_HAULED)) //it would look silly.
+ return
var/pixel_x_diff = 0
var/pixel_y_diff = 0
var/direction = get_dir(src, A)
@@ -207,7 +207,6 @@ Can look good elsewhere as well.*/
animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2, flags = ANIMATION_PARALLEL)
animate(pixel_x = initial(pixel_x), pixel_y = initial(pixel_y), time = 2)
-
/atom/proc/animation_spin(speed = 5, loop_amount = -1, clockwise = TRUE, sections = 3, angular_offset = 0, pixel_fuzz = 0)
if(!sections)
return
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 8faba744ea17..953c9f021a75 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -942,8 +942,17 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
winset(src, "default.Shift", "is-disabled=true")
winset(src, "default.ShiftUp", "is-disabled=true")
+GLOBAL_VAR(ooc_rank_dmi)
+GLOBAL_LIST_INIT(ooc_rank_iconstates, setup_ooc_rank_icons())
GLOBAL_LIST_INIT(community_awards, get_community_awards())
+/proc/setup_ooc_rank_icons()
+ var/ooc_dmi_path = "config/ooc.dmi"
+ if(!fexists(ooc_dmi_path))
+ return list()
+ GLOB.ooc_rank_dmi = icon(file(ooc_dmi_path))
+ return icon_states(GLOB.ooc_rank_dmi)
+
/proc/get_community_awards()
var/list/awards_file = file2list("config/community_awards.txt")
var/list/processed_awards = list()
@@ -978,5 +987,5 @@ GLOBAL_LIST_INIT(community_awards, get_community_awards())
if(GLOB.community_awards[ckey])
var/full_prefix = ""
for(var/award in GLOB.community_awards[ckey])
- full_prefix += "[icon2html('icons/ooc.dmi', GLOB.clients, award)]"
+ full_prefix += "[icon2html(GLOB.ooc_rank_dmi, GLOB.clients, award)]"
return full_prefix
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 1ae304b0e271..8a6bc24a3561 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -257,6 +257,7 @@ GLOBAL_LIST_INIT(be_special_flags, list(
var/tgui_fancy = TRUE
var/tgui_lock = FALSE
+ var/tgui_scale = TRUE
var/hear_vox = TRUE
@@ -578,6 +579,7 @@ GLOBAL_LIST_INIT(be_special_flags, list(
dat += "Tooltips:[tooltips ? "Enabled" : "Disabled"] "
dat += "tgui Window Mode:[(tgui_fancy) ? "Fancy (default)" : "Compatible (slower)"] "
dat += "tgui Window Placement:[(tgui_lock) ? "Primary monitor" : "Free (default)"] "
+ dat += "tgui Scaling:[tgui_scale ? "Larger windows (default)" : "Smaller zoom"] "
dat += "Play Admin Sounds:[(toggles_sound & SOUND_MIDI) ? "Yes" : "No"] "
dat += "Play Announcement Sounds As Ghost:[(toggles_sound & SOUND_OBSERVER_ANNOUNCEMENTS) ? "Yes" : "No"] "
dat += "Play Fax Sounds As Ghost:[(toggles_sound & SOUND_FAX_MACHINE) ? "Yes" : "No"] "
@@ -1988,6 +1990,8 @@ GLOBAL_LIST_INIT(be_special_flags, list(
tgui_fancy = !tgui_fancy
if("tgui_lock")
tgui_lock = !tgui_lock
+ if("tgui_scale")
+ tgui_scale = !tgui_scale
if("change_menu")
current_menu = href_list["menu"]
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 9b1885a71cf4..6bfbf18c53e0 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -361,6 +361,10 @@
S["tooltips"] >> tooltips
S["key_bindings"] >> key_bindings
+ S["tgui_lock"] >> tgui_lock
+ S["tgui_fancy"] >> tgui_fancy
+ S["tgui_scale"] >> tgui_scale
+
var/tutorial_string = ""
S["completed_tutorials"] >> tutorial_string
tutorial_savestring_to_list(tutorial_string)
@@ -440,6 +444,10 @@
yautja_status = sanitize_inlist(yautja_status, GLOB.whitelist_hierarchy + list("Elder"), initial(yautja_status))
synth_status = sanitize_inlist(synth_status, GLOB.whitelist_hierarchy, initial(synth_status))
+ tgui_scale = sanitize_integer(tgui_scale, FALSE, TRUE, initial(tgui_scale))
+ tgui_lock = sanitize_integer(tgui_lock, FALSE, TRUE, initial(tgui_lock))
+ tgui_fancy = sanitize_integer(tgui_fancy, FALSE, TRUE, initial(tgui_fancy))
+
fax_name_uscm = fax_name_uscm ? sanitize_text(fax_name_uscm, initial(fax_name_uscm)) : generate_name(FACTION_MARINE)
fax_name_pvst = fax_name_pvst ? sanitize_text(fax_name_pvst, initial(fax_name_pvst)) : generate_name(FACTION_MARINE)
fax_name_wy = fax_name_wy ? sanitize_text(fax_name_wy, initial(fax_name_wy)) : generate_name(FACTION_WY)
@@ -606,6 +614,10 @@
S["job_loadout"] << save_loadout(loadout)
S["job_loadout_names"] << loadout_slot_names
+ S["tgui_fancy"] << tgui_fancy
+ S["tgui_lock"] << tgui_lock
+ S["tgui_scale"] << tgui_scale
+
return TRUE
/datum/preferences/proc/load_character(slot)
diff --git a/code/modules/cm_aliens/XenoStructures.dm b/code/modules/cm_aliens/XenoStructures.dm
index 970d5e043787..1fb5bd2efadc 100644
--- a/code/modules/cm_aliens/XenoStructures.dm
+++ b/code/modules/cm_aliens/XenoStructures.dm
@@ -225,6 +225,9 @@
if(H.ally_of_hivenumber(hivenumber))
return
+ if(HAS_TRAIT(H, TRAIT_HAULED))
+ return
+
H.apply_armoured_damage(damage, penetration = penetration, def_zone = pick(target_limbs))
H.last_damage_data = construction_data
@@ -445,6 +448,8 @@
/obj/structure/mineral_door/resin/proc/close_blocked()
for(var/turf/turf in locs)
for(var/mob/living/living_mob in turf)
+ if(living_mob.stat == DEAD)
+ continue
if(!HAS_TRAIT(living_mob, TRAIT_MERGED_WITH_WEEDS))
return TRUE
return FALSE
@@ -628,7 +633,7 @@
return
var/mob/living/carbon/target = null
var/furthest_distance = INFINITY
- for(var/mob/living/carbon/C in urange(range, get_turf(loc)))
+ for(var/mob/living/carbon/C in long_range(range, get_turf(loc)))
if(!can_target(C))
continue
var/distance_between = get_dist(src, C)
@@ -712,7 +717,7 @@
START_PROCESSING(SSshield_pillar, src)
/obj/effect/alien/resin/shield_pillar/process()
- for(var/mob/living/carbon/xenomorph/X in urange(range, src))
+ for(var/mob/living/carbon/xenomorph/X in long_range(range, src))
if((X.hivenumber != hivenumber) || X.stat == DEAD)
continue
X.add_xeno_shield(shield_to_give, XENO_SHIELD_SOURCE_SHIELD_PILLAR, decay_amount_per_second = 1, add_shield_on = TRUE, duration = 1 SECONDS)
@@ -992,7 +997,7 @@
for(var/obj/effect/alien/resin/special/pylon/pylon as anything in hive.active_endgame_pylons)
pylon.protection_level = TURF_PROTECTION_OB
pylon.update_icon()
-
+
if(hatched)
STOP_PROCESSING(SSobj, src)
return
@@ -1098,7 +1103,7 @@
if(!candidate.client)
return FALSE
-
+
return candidate.client.prefs.be_special & BE_KING
#undef KING_PLAYTIME_HOURS
@@ -1208,7 +1213,7 @@
rolling_candidates = FALSE
return
message_admins("Failed to find a client for the King, releasing as freed mob.")
-
+
/// Starts the hatching in twenty seconds, otherwise immediately if expedited
/obj/effect/alien/resin/king_cocoon/proc/start_hatching(expedite = FALSE)
diff --git a/code/modules/cm_marines/dropship_ammo.dm b/code/modules/cm_marines/dropship_ammo.dm
index 4ba8c38b8640..2ebb12e1bb34 100644
--- a/code/modules/cm_marines/dropship_ammo.dm
+++ b/code/modules/cm_marines/dropship_ammo.dm
@@ -442,7 +442,7 @@
qdel(src)
/obj/structure/ship_ammo/sentry/can_fire_at(turf/impact, mob/user)
- for(var/obj/structure/machinery/defenses/def in urange(4, impact))
+ for(var/obj/structure/machinery/defenses/def in range(4, impact))
to_chat(user, SPAN_WARNING("The selected drop site is too close to another deployed defense!"))
return FALSE
if(istype(impact, /turf/closed))
diff --git a/code/modules/cm_marines/orbital_cannon.dm b/code/modules/cm_marines/orbital_cannon.dm
index f73fedaf13da..675d310ed103 100644
--- a/code/modules/cm_marines/orbital_cannon.dm
+++ b/code/modules/cm_marines/orbital_cannon.dm
@@ -395,7 +395,7 @@ GLOBAL_LIST_EMPTY(orbital_cannon_cancellation)
message_admins(FONT_SIZE_XL("CLICK TO CANCEL THIS OB"))
var/relative_dir
- for(var/mob/M in urange(30, target))
+ for(var/mob/M in long_range(30, target))
if(get_turf(M) == target)
relative_dir = 0
else
@@ -406,7 +406,7 @@ GLOBAL_LIST_EMPTY(orbital_cannon_cancellation)
)
sleep(OB_TRAVEL_TIMING/3)
- for(var/mob/M in urange(25, target))
+ for(var/mob/M in long_range(25, target))
if(get_turf(M) == target)
relative_dir = 0
else
@@ -417,7 +417,7 @@ GLOBAL_LIST_EMPTY(orbital_cannon_cancellation)
)
sleep(OB_TRAVEL_TIMING/3)
- for(var/mob/M in urange(15, target))
+ for(var/mob/M in long_range(15, target))
M.show_message( \
SPAN_HIGHDANGER("OH GOD THE SKY WILL EXPLODE!!!"), SHOW_MESSAGE_VISIBLE, \
SPAN_HIGHDANGER("YOU SHOULDN'T BE HERE!"), SHOW_MESSAGE_AUDIBLE \
@@ -435,7 +435,7 @@ GLOBAL_LIST_EMPTY(orbital_cannon_cancellation)
var/radius_size = 30
- for(var/mob/living/user in urange(radius_size, epicenter))
+ for(var/mob/living/user in long_range(radius_size, epicenter))
var/distance = get_accurate_dist(get_turf(user), epicenter)
var/distance_percent = ((radius_size - distance) / radius_size)
diff --git a/code/modules/cm_marines/smartgun_mount.dm b/code/modules/cm_marines/smartgun_mount.dm
index ac113325d1b5..5aa3ff34bcfe 100644
--- a/code/modules/cm_marines/smartgun_mount.dm
+++ b/code/modules/cm_marines/smartgun_mount.dm
@@ -94,7 +94,7 @@
/obj/item/device/m56d_gun/attack_self(mob/user)
..()
- for(var/obj/structure/machinery/machine in urange(defense_check_range, loc))
+ for(var/obj/structure/machinery/machine in long_range(defense_check_range, loc))
if(istype(machine, /obj/structure/machinery/m56d_hmg) || istype(machine, /obj/structure/machinery/m56d_post))
to_chat(user, SPAN_WARNING("This is too close to [machine]!"))
return
@@ -142,7 +142,7 @@
if(!do_after(user, 1 SECONDS, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD))
return
- for(var/obj/structure/machinery/machine in urange(defense_check_range, loc))
+ for(var/obj/structure/machinery/machine in long_range(defense_check_range, loc))
if(istype(machine, /obj/structure/machinery/m56d_hmg) || istype(machine, /obj/structure/machinery/m56d_post))
to_chat(user, SPAN_WARNING("This is too close to [machine]!"))
return
@@ -338,7 +338,7 @@
if(istype(O,/obj/item/device/m56d_gun)) //lets mount the MG onto the mount.
var/obj/item/device/m56d_gun/MG = O
- for(var/obj/structure/machinery/machine in urange(MG.defense_check_range, loc, TRUE))
+ for(var/obj/structure/machinery/machine in long_orange(MG.defense_check_range, loc))
if(istype(machine, /obj/structure/machinery/m56d_hmg) || istype(machine, /obj/structure/machinery/m56d_post))
to_chat(user, SPAN_WARNING("This is too close to [machine]!"))
return
diff --git a/code/modules/defenses/defenses.dm b/code/modules/defenses/defenses.dm
index 5539bbca0dba..834e258db8ea 100644
--- a/code/modules/defenses/defenses.dm
+++ b/code/modules/defenses/defenses.dm
@@ -387,7 +387,7 @@
if(!turned_on)
if(!can_be_near_defense)
- for(var/obj/structure/machinery/defenses/def in urange(defense_check_range, loc))
+ for(var/obj/structure/machinery/defenses/def in long_range(defense_check_range, loc))
if(def != src && def.turned_on && !def.can_be_near_defense)
to_chat(user, SPAN_WARNING("This is too close to \a [def]!"))
return
diff --git a/code/modules/desert_dam/filtration/filtration.dm b/code/modules/desert_dam/filtration/filtration.dm
index 35799e08b5e2..0061d9d2a60e 100644
--- a/code/modules/desert_dam/filtration/filtration.dm
+++ b/code/modules/desert_dam/filtration/filtration.dm
@@ -127,7 +127,6 @@ Each var depends on others
if(isliving(A))
var/mob/living/M = A
- // Inside a xeno for example
if(!istype(M.loc, /turf))
return
@@ -136,6 +135,9 @@ Each var depends on others
to_chat(M, SPAN_WARNING("The current forces you to release [M.pulling]!"))
M.stop_pulling()
+ if(HAS_TRAIT(M, TRAIT_HAULED))
+ return
+
cause_damage(M)
START_PROCESSING(SSobj, src)
return
diff --git a/code/modules/gear_presets/_select_equipment.dm b/code/modules/gear_presets/_select_equipment.dm
index 720bc410943b..85d57341b8a1 100644
--- a/code/modules/gear_presets/_select_equipment.dm
+++ b/code/modules/gear_presets/_select_equipment.dm
@@ -93,7 +93,7 @@
/datum/equipment_preset/proc/load_age(mob/living/carbon/human/new_human, client/mob_client)
if(minimum_age && new_human.age < minimum_age)
- new_human.age = minimum_age
+ new_human.age = minimum_age + 2
/datum/equipment_preset/proc/load_rank(mob/living/carbon/human/new_human, client/mob_client)//Beagle-Code
if(paygrades.len == 1)
diff --git a/code/modules/gear_presets/uscm.dm b/code/modules/gear_presets/uscm.dm
index 86bca90ffa5f..1c255d0c841b 100644
--- a/code/modules/gear_presets/uscm.dm
+++ b/code/modules/gear_presets/uscm.dm
@@ -436,7 +436,7 @@
rank = JOB_SQUAD_LEADER
paygrades = list(PAY_SHORT_ME5 = JOB_PLAYTIME_TIER_0, PAY_SHORT_ME6 = JOB_PLAYTIME_TIER_1, PAY_SHORT_ME7 = JOB_PLAYTIME_TIER_3)
role_comm_title = "SL"
- minimum_age = 27
+ minimum_age = 25
skills = /datum/skills/SL
minimap_icon = "leader"
@@ -513,7 +513,7 @@
rank = JOB_SQUAD_LEADER
paygrades = list(PAY_SHORT_ME5 = JOB_PLAYTIME_TIER_0, PAY_SHORT_ME6 = JOB_PLAYTIME_TIER_1, PAY_SHORT_ME7 = JOB_PLAYTIME_TIER_3)
role_comm_title = "SL"
- minimum_age = 27
+ minimum_age = 25
skills = /datum/skills/SL
minimap_icon = "leader"
diff --git a/code/modules/gear_presets/uscm_police.dm b/code/modules/gear_presets/uscm_police.dm
index 8322b4e808c0..793c035e4c6f 100644
--- a/code/modules/gear_presets/uscm_police.dm
+++ b/code/modules/gear_presets/uscm_police.dm
@@ -2,7 +2,7 @@
name = "USCM (police roles)"
faction = FACTION_MARINE
minimap_background = "background_mp"
- minimum_age = 27
+ minimum_age = 21
//*****************************************************************************************************/
@@ -88,6 +88,7 @@
rank = JOB_WARDEN
paygrades = list(PAY_SHORT_ME5 = JOB_PLAYTIME_TIER_0, PAY_SHORT_ME6 = JOB_PLAYTIME_TIER_1, PAY_SHORT_ME7 = JOB_PLAYTIME_TIER_3)
role_comm_title = "MW"
+ minimum_age = 25
skills = /datum/skills/MW
minimap_icon = "warden"
@@ -148,6 +149,7 @@
assignment = JOB_CHIEF_POLICE
rank = JOB_CHIEF_POLICE
paygrades = list(PAY_SHORT_MO1 = JOB_PLAYTIME_TIER_0, PAY_SHORT_MO2 = JOB_PLAYTIME_TIER_1)
+ minimum_age = 23
role_comm_title = "CMP"
skills = /datum/skills/CMP
diff --git a/code/modules/gear_presets/uscm_ship.dm b/code/modules/gear_presets/uscm_ship.dm
index bfe972d462fc..f914a4f4ef1c 100644
--- a/code/modules/gear_presets/uscm_ship.dm
+++ b/code/modules/gear_presets/uscm_ship.dm
@@ -180,7 +180,7 @@
rank = JOB_CHIEF_ENGINEER
paygrades = list(PAY_SHORT_MO1 = JOB_PLAYTIME_TIER_0, PAY_SHORT_MO2 = JOB_PLAYTIME_TIER_3)
role_comm_title = "CE"
- minimum_age = 27
+ minimum_age = 25
skills = /datum/skills/CE
minimap_icon = "ce"
@@ -297,7 +297,7 @@
rank = JOB_CHIEF_REQUISITION
paygrades = list(PAY_SHORT_ME6 = JOB_PLAYTIME_TIER_0, PAY_SHORT_ME7 = JOB_PLAYTIME_TIER_1, PAY_SHORT_ME8 = JOB_PLAYTIME_TIER_3)
role_comm_title = "QM"
- minimum_age = 27
+ minimum_age = 25
skills = /datum/skills/RO
minimap_icon = "cargo"
@@ -370,7 +370,7 @@
rank = JOB_XO
paygrades = list(PAY_SHORT_MO3 = JOB_PLAYTIME_TIER_0)
role_comm_title = "XO"
- minimum_age = 35
+ minimum_age = 27
skills = /datum/skills/XO
minimap_icon = "xo"
@@ -409,7 +409,7 @@
rank = JOB_SO
paygrades = list(PAY_SHORT_MO1 = JOB_PLAYTIME_TIER_0)
role_comm_title = "SO"
- minimum_age = 25
+ minimum_age = 23
skills = /datum/skills/SO
minimap_icon = "so"
@@ -488,7 +488,7 @@
rank = JOB_AUXILIARY_OFFICER
paygrades = list(PAY_SHORT_MO2 = JOB_PLAYTIME_TIER_0, PAY_SHORT_MO3 = JOB_PLAYTIME_TIER_3)
role_comm_title = "ASO"
- minimum_age = 27
+ minimum_age = 25
skills = /datum/skills/auxiliary_officer
minimap_icon = "aso"
@@ -547,6 +547,7 @@
rank = JOB_CAS_PILOT
paygrades = list(PAY_SHORT_MO1 = JOB_PLAYTIME_TIER_0)
role_comm_title = "GP"
+ minimum_age = 23
skills = /datum/skills/pilot
minimap_icon = "gp"
@@ -598,6 +599,7 @@
rank = JOB_DROPSHIP_PILOT
paygrades = list(PAY_SHORT_MO1 = JOB_PLAYTIME_TIER_0)
role_comm_title = "DP"
+ minimum_age = 23
skills = /datum/skills/pilot
minimap_icon = "pilot"
@@ -703,7 +705,7 @@
rank = "USCM Officer"
paygrades = list(PAY_SHORT_MO3 = JOB_PLAYTIME_TIER_0)
role_comm_title = "Cpt"
- minimum_age = 40
+ minimum_age = 25
skills = /datum/skills/commander
utility_hat = list(/obj/item/clothing/head/beret/cm)
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 1ca06f709a9b..0d48bb22186f 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -169,6 +169,9 @@
if(is_mob_incapacitated())
return
+ if(HAS_TRAIT(src, TRAIT_HAULED))
+ return
+
if(pickup_recent_item_on_turf(user_turf))
return
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 81199dc11b54..6e4a4a17abd8 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -15,7 +15,6 @@
remove_all_indicators()
/mob/living/carbon/Destroy()
- stomach_contents?.Cut()
view_change_sources = null
active_transfusions = null
. = ..()
@@ -40,50 +39,30 @@
/mob/living/carbon/relaymove(mob/user, direction)
if(user.is_mob_incapacitated(TRUE))
return
- if(user in src.stomach_contents)
- if(user.client)
- user.client.next_movement = world.time + 20
- if(prob(30))
- for(var/mob/mobs_can_hear in hearers(4, src))
- if(mobs_can_hear.client)
- mobs_can_hear.show_message(SPAN_DANGER("You hear something rumbling inside [src]'s stomach..."), SHOW_MESSAGE_AUDIBLE)
- var/obj/item/item_in_hand = user.get_active_hand()
- if(item_in_hand && item_in_hand.force)
- var/damage_of_item = rand(floor(item_in_hand.force / 4), item_in_hand.force)
- if(istype(src, /mob/living/carbon/human))
- var/mob/living/carbon/human/human_mob = src
- var/organ = human_mob.get_limb("chest")
- if(istype(organ, /obj/limb))
- var/obj/limb/organs_in_human = organ
- if(organs_in_human.take_damage(damage_of_item, 0))
- human_mob.UpdateDamageIcon()
- human_mob.updatehealth()
- else
- src.take_limb_damage(damage_of_item)
- for(var/mob/mobs_in_view as anything in viewers(user, null))
- if(mobs_in_view.client)
- mobs_in_view.show_message(text(SPAN_DANGER("[user] attacks [src]'s stomach wall with the [item_in_hand.name]!")), SHOW_MESSAGE_AUDIBLE)
- user.track_hit(initial(item_in_hand.name))
- playsound(user.loc, 'sound/effects/attackblob.ogg', 25, 1)
-
- if(prob(max(4*(100*getBruteLoss()/maxHealth - 75),0))) //4% at 24% health, 80% at 5% health
- last_damage_data = create_cause_data("chestbursting", user)
- gib(last_damage_data)
- else if(!chestburst && (status_flags & XENO_HOST) && islarva(user))
+ if(!chestburst && (status_flags & XENO_HOST) && islarva(user))
var/mob/living/carbon/xenomorph/larva/larva_burst = user
larva_burst.chest_burst(src)
/mob/living/carbon/ex_act(severity, direction, datum/cause_data/cause_data)
+ last_damage_data = istype(cause_data) ? cause_data : create_cause_data(cause_data)
+ var/gibbing = FALSE
+
+ if(severity >= health && severity >= EXPLOSION_THRESHOLD_GIB)
+ gibbing = TRUE
if(body_position == LYING_DOWN && direction)
severity *= EXPLOSION_PRONE_MULTIPLIER
+ if(HAS_TRAIT(src, TRAIT_HAULED) && !gibbing) // We still probably wanna gib them as well if they were supposed to be gibbed by the explosion in the first place
+ visible_message(SPAN_WARNING("[src] is shielded from the blast!"), SPAN_WARNING("You are shielded from the blast!"))
+ return
+
if(severity >= 30)
flash_eyes()
last_damage_data = istype(cause_data) ? cause_data : create_cause_data(cause_data)
- if(severity >= health && severity >= EXPLOSION_THRESHOLD_GIB)
+ if(gibbing)
gib(last_damage_data)
return
@@ -98,23 +77,6 @@
/mob/living/carbon/gib(datum/cause_data/cause = create_cause_data("gibbing", src))
if(legcuffed)
drop_inv_item_on_ground(legcuffed)
-
- var/turf/my_turf = get_turf(src)
-
- for(var/atom/movable/A in stomach_contents)
- stomach_contents.Remove(A)
- A.forceMove(my_turf)
- A.acid_damage = 0 //Reset the acid damage
- if(ismob(A))
- visible_message(SPAN_DANGER("[A] bursts out of [src]!"))
-
- for(var/atom/movable/A in contents_recursive())
- if(isobj(A))
- var/obj/O = A
- if(O.unacidable)
- O.forceMove(my_turf)
- O.throw_atom(pick(RANGE_TURFS(1, src)), 1, SPEED_FAST)
-
. = ..(cause)
/mob/living/carbon/revive()
@@ -128,37 +90,69 @@
recalculate_move_delay = TRUE
..()
-/mob/living/carbon/human/attackby(obj/item/W, mob/living/user)
+/mob/living/carbon/human/attackby(obj/item/weapon, mob/living/user)
if(user.mob_flags & SURGERY_MODE_ON)
switch(user.a_intent)
if(INTENT_HELP)
//Attempt to dig shrapnel first, if any. dig_out_shrapnel_check() will fail if user is not human, which may be possible in future.
- if(W.flags_item & CAN_DIG_SHRAPNEL && (locate(/obj/item/shard) in src.embedded_items) && W.dig_out_shrapnel_check(src, user))
+ if(weapon.flags_item & CAN_DIG_SHRAPNEL && (locate(/obj/item/shard) in src.embedded_items) && weapon.dig_out_shrapnel_check(src, user))
return TRUE
var/datum/surgery/current_surgery = active_surgeries[user.zone_selected]
if(current_surgery)
- if(current_surgery.attempt_next_step(user, W))
+ if(current_surgery.attempt_next_step(user, weapon))
return TRUE //Cancel attack.
else
var/obj/limb/affecting = get_limb(check_zone(user.zone_selected))
- if(initiate_surgery_moment(W, src, affecting, user))
+ if(initiate_surgery_moment(weapon, src, affecting, user))
return TRUE
if(INTENT_DISARM) //Same as help but without the shrapnel dig attempt.
var/datum/surgery/current_surgery = active_surgeries[user.zone_selected]
if(current_surgery)
- if(current_surgery.attempt_next_step(user, W))
+ if(current_surgery.attempt_next_step(user, weapon))
return TRUE
else
var/obj/limb/affecting = get_limb(check_zone(user.zone_selected))
- if(initiate_surgery_moment(W, src, affecting, user))
+ if(initiate_surgery_moment(weapon, src, affecting, user))
return TRUE
- else if(W.flags_item & CAN_DIG_SHRAPNEL && W.dig_out_shrapnel_check(src, user))
+ else if(weapon.flags_item & CAN_DIG_SHRAPNEL && weapon.dig_out_shrapnel_check(src, user))
return TRUE
. = ..()
+/mob/living/carbon/human/proc/handle_haul_resist()
+ if(world.time <= next_haul_resist)
+ return
+
+ if(is_mob_incapacitated())
+ return
+
+ var/mob/living/carbon/xenomorph/xeno = hauling_xeno
+ next_haul_resist = world.time + 1.4 SECONDS
+ if(istype(get_active_hand(), /obj/item))
+ var/obj/item/item = get_active_hand()
+ if(item.force)
+ var/damage_of_item = rand(floor(item.force / 4), item.force)
+ xeno.take_limb_damage(damage_of_item)
+ for(var/mob/mobs_in_view as anything in viewers(src, null))
+ if(mobs_in_view.client)
+ mobs_in_view.show_message(text(SPAN_DANGER("[src] attacks [xeno]'s carapace with the [item.name]!")), SHOW_MESSAGE_AUDIBLE)
+ track_hit(initial(item.name))
+ if(item.sharp)
+ playsound(loc, 'sound/weapons/slash.ogg', 25, 1)
+ else
+ var/hit_sound = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg')
+ playsound(loc, hit_sound, 25, 1)
+ if(prob(max(4*(100*xeno.getBruteLoss()/xeno.maxHealth - 75),0))) //4% at 24% health, 80% at 5% health
+ xeno.last_damage_data = create_cause_data("scuffling", src)
+ xeno.gib(last_damage_data)
+ else
+ for(var/mob/mobs_can_hear in hearers(4, xeno))
+ if(mobs_can_hear.client)
+ mobs_can_hear.show_message(SPAN_DANGER("You hear [src] struggling against [xeno]'s grip..."), SHOW_MESSAGE_AUDIBLE)
+ return
+
/mob/living/carbon/attack_hand(mob/target_mob as mob)
if(!istype(target_mob, /mob/living/carbon))
return
@@ -368,8 +362,8 @@
return
if(stat || !target)
return
- if(!istype(loc, /turf)) // In some mob/object (i.e. devoured or tank)
- to_chat(src, SPAN_WARNING("You cannot throw anything while inside of \the [loc.name]."))
+ if(!istype(loc, /turf) || HAS_TRAIT(src, TRAIT_HAULED)) // In some mob/object (i.e. hauled or tank)
+ to_chat(src, SPAN_WARNING("You cannot throw anything right now."))
return
if(target.type == /atom/movable/screen)
return
@@ -502,16 +496,51 @@
if(!HAS_TRAIT(src, TRAIT_FLOORED)) // just watch this break in the most horrible way possible
break
+// Adding traits, etc after xeno restrains and hauls us
+/mob/living/carbon/human/proc/handle_haul(mob/living/carbon/xenomorph/xeno)
+ ADD_TRAIT(src, TRAIT_FLOORED, TRAIT_SOURCE_XENO_HAUL)
+ ADD_TRAIT(src, TRAIT_HAULED, TRAIT_SOURCE_XENO_HAUL)
+ ADD_TRAIT(src, TRAIT_NO_STRAY, TRAIT_SOURCE_XENO_HAUL)
+
+ hauling_xeno = xeno
+ RegisterSignal(xeno, COMSIG_MOB_DEATH, PROC_REF(release_haul_death))
+ RegisterSignal(src, COMSIG_LIVING_PREIGNITION, PROC_REF(haul_fire_shield))
+ RegisterSignal(src, list(COMSIG_LIVING_FLAMER_CROSSED, COMSIG_LIVING_FLAMER_FLAMED), PROC_REF(haul_fire_shield_callback))
+ layer = LYING_BETWEEN_MOB_LAYER
+ add_filter("hauled_shadow", 1, color_matrix_filter(rgb(95, 95, 95)))
+ pixel_y = -7
+ next_haul_resist = 0
+
+/mob/living/carbon/human/proc/release_haul_death()
+ SIGNAL_HANDLER
+ handle_unhaul()
+
+/mob/living/carbon/human/proc/haul_fire_shield(mob/living/burning_mob) //Stealing it from the pyro spec armor, xenos shield us from fire
+ SIGNAL_HANDLER
+ return COMPONENT_CANCEL_IGNITION
+
+
+/mob/living/carbon/human/proc/haul_fire_shield_callback(mob/living/burning_mob)
+ SIGNAL_HANDLER
+ . = COMPONENT_NO_IGNITE|COMPONENT_NO_BURN
+
+// Removing traits and other stuff after xeno releases us from haul
+/mob/living/carbon/human/proc/handle_unhaul()
+ var/location = get_turf(loc)
+ remove_traits(list(TRAIT_HAULED, TRAIT_NO_STRAY, TRAIT_FLOORED, TRAIT_IMMOBILIZED), TRAIT_SOURCE_XENO_HAUL)
+ pixel_y = 0
+ UnregisterSignal(src, list(COMSIG_LIVING_PREIGNITION, COMSIG_LIVING_FLAMER_CROSSED, COMSIG_LIVING_FLAMER_FLAMED))
+ UnregisterSignal(hauling_xeno, COMSIG_MOB_DEATH)
+ hauling_xeno = null
+ layer = MOB_LAYER
+ remove_filter("hauled_shadow")
+ forceMove(location)
+ for(var/obj/object in location)
+ if(istype(object, /obj/effect/alien/resin/trap) || istype(object, /obj/effect/alien/egg))
+ object.HasProximity(src)
+ next_haul_resist = 0
-/mob/living/carbon/on_stored_atom_del(atom/movable/AM)
- ..()
- if(length(stomach_contents) && ismob(AM))
- for(var/X in stomach_contents)
- if(AM == X)
- stomach_contents -= AM
- break
-
/mob/living/carbon/proc/extinguish_mob(mob/living/carbon/C)
adjust_fire_stacks(-5, min_stacks = 0)
playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 7)
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index 03a8abef22af..f3a1134557a3 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -1,7 +1,6 @@
/mob/living/carbon
gender = MALE
mobility_flags = MOBILITY_FLAGS_CARBON_DEFAULT
- var/list/stomach_contents = list()
var/life_tick = 0 // The amount of life ticks that have processed on this mob.
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 289d6afd6e05..1ba66199b950 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -138,8 +138,6 @@
if(body_position == LYING_DOWN && direction)
severity *= EXPLOSION_PRONE_MULTIPLIER
-
-
var/b_loss = 0
var/f_loss = 0
@@ -158,6 +156,10 @@
create_shrapnel(oldloc, rand(5, 9), direction, 45, /datum/ammo/bullet/shrapnel/light/human/var2, last_damage_data)
return
+ if(HAS_TRAIT(src, TRAIT_HAULED)) // We still probably wanna gib them as well if they were supposed to be gibbed by the explosion in the first place
+ visible_message(SPAN_WARNING("[src] is shielded from the blast!"), SPAN_WARNING("You are shielded from the blast!"))
+ return
+
if(!HAS_TRAIT(src, TRAIT_EAR_PROTECTION))
ear_damage += severity * 0.15
AdjustEarDeafness(severity * 0.5)
diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm
index fcd22e5bfa41..2e35209ccd5f 100644
--- a/code/modules/mob/living/carbon/human/human_attackhand.dm
+++ b/code/modules/mob/living/carbon/human/human_attackhand.dm
@@ -4,6 +4,9 @@
if(..())
return TRUE
+ if(HAS_TRAIT(attacking_mob, TRAIT_HAULED))
+ return
+
SEND_SIGNAL(attacking_mob, COMSIG_LIVING_ATTACKHAND_HUMAN, src)
if((attacking_mob != src) && check_shields(0, attacking_mob.name))
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 2a2661acc500..43c964b9e58a 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -176,6 +176,12 @@
// Are we currently using inherent zoom vision?
var/is_zoomed = FALSE
+ // Xenomorph that is hauling us if we are hauled
+ var/mob/living/carbon/xenomorph/hauling_xeno
+
+ // Haul resist cooldown
+ var/next_haul_resist
+
/client/var/cached_human_playtime
/client/proc/get_total_human_playtime(skip_cache = FALSE)
diff --git a/code/modules/mob/living/carbon/human/life/life_helpers.dm b/code/modules/mob/living/carbon/human/life/life_helpers.dm
index 96f147d52883..a66374dc4680 100644
--- a/code/modules/mob/living/carbon/human/life/life_helpers.dm
+++ b/code/modules/mob/living/carbon/human/life/life_helpers.dm
@@ -209,6 +209,7 @@
last_damage_data = null
statistic_tracked = FALSE
tod = null
+ revive_grace_period = initial(revive_grace_period)
set_stat(UNCONSCIOUS)
emote("gasp")
regenerate_icons()
diff --git a/code/modules/mob/living/carbon/xenomorph/Evolution.dm b/code/modules/mob/living/carbon/xenomorph/Evolution.dm
index df7e2825ee22..cc5ca0f8beef 100644
--- a/code/modules/mob/living/carbon/xenomorph/Evolution.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Evolution.dm
@@ -17,6 +17,10 @@ GLOBAL_LIST_EMPTY(deevolved_ckeys)
/mob/living/carbon/xenomorph/proc/do_evolve()
if(!evolve_checks())
return
+ var/mob/living/carbon/human/user = hauled_mob?.resolve()
+ if(user)
+ to_chat(src, "Release [user] before evolving!")
+ return
var/list/castes_available = caste.evolves_to.Copy()
diff --git a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
index 4b37be23fc6c..3fac8948b905 100644
--- a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
@@ -474,7 +474,8 @@
/proc/can_hug(mob/living/carbon/M, hivenumber)
if(!istype(M) || isxeno(M) || issynth(M) || iszombie(M) || isHellhound(M) || M.stat == DEAD || !M.huggable)
return FALSE
-
+ if(HAS_TRAIT(M, TRAIT_HAULED))
+ return FALSE
if(M.ally_of_hivenumber(hivenumber))
return FALSE
diff --git a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
index 85574c60319b..a58a8a8be244 100644
--- a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
+++ b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
@@ -215,6 +215,31 @@
/mob/living/carbon/xenomorph/proc/gain_armor_percent(value)
armor_integrity = min(armor_integrity + value, 100)
+/mob/living/carbon/xenomorph/animation_attack_on(atom/A, pixel_offset)
+ if(hauled_mob?.resolve())
+ return
+ . = ..()
+
+/mob/living/carbon/xenomorph/Move(NewLoc, direct)
+ . = ..()
+ var/mob/user = hauled_mob?.resolve()
+ if(user)
+ user.forceMove(loc)
+
+/mob/living/carbon/xenomorph/forceMove(atom/destination)
+ . = ..()
+ var/mob/user = hauled_mob?.resolve()
+ if(user)
+ if(!isturf(destination))
+ user.forceMove(src)
+ else
+ user.forceMove(loc)
+
+/mob/living/carbon/xenomorph/relaymove(mob/user, direction)
+ . = ..()
+ if(HAS_TRAIT(user, TRAIT_HAULED))
+ var/mob/living/carbon/human/hauled_mob = user
+ hauled_mob.handle_haul_resist()
//Strip all inherent xeno verbs from your caste. Used in evolution.
/mob/living/carbon/xenomorph/proc/remove_inherent_verbs()
@@ -368,17 +393,6 @@
/mob/living/carbon/xenomorph/proc/pounced_turf_wrapper(turf/T)
pounced_turf(T)
-//Bleuugh
-/mob/living/carbon/xenomorph/proc/empty_gut()
- if(length(stomach_contents))
- for(var/atom/movable/S in stomach_contents)
- stomach_contents.Remove(S)
- S.acid_damage = 0 //Reset the acid damage
- S.forceMove(get_true_turf(src))
-
- if(length(contents)) //Get rid of anything that may be stuck inside us as well
- for(var/atom/movable/A in contents)
- A.forceMove(get_true_turf(src))
/mob/living/carbon/xenomorph/proc/toggle_nightvision()
see_in_dark = 12
@@ -393,22 +407,58 @@
lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
update_sight()
-/mob/living/carbon/xenomorph/proc/regurgitate(mob/living/victim, stuns = FALSE)
- if(length(stomach_contents))
- if(victim)
- stomach_contents.Remove(victim)
- victim.acid_damage = 0
- victim.forceMove(get_true_turf(loc))
-
- visible_message(SPAN_XENOWARNING("[src] hurls out the contents of their stomach!"),
- SPAN_XENOWARNING("We hurl out the contents of our stomach!"), null, 5)
- playsound(get_true_location(loc), 'sound/voice/alien_drool2.ogg', 50, 1)
- log_interact(src, victim, "[key_name(src)] regurgitated [key_name(victim)] at [get_area_name(loc)]")
+/mob/living/carbon/xenomorph/proc/haul(mob/living/carbon/human/victim)
+ visible_message(SPAN_WARNING("[src] restrains [victim], hauling them effortlessly!"),
+ SPAN_WARNING("We fully restrain [victim] and start hauling them!"), null, 5)
+ log_interact(src, victim, "[key_name(src)] started hauling [key_name(victim)] at [get_area_name(src)]")
+ playsound(loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 7)
+
+ if(ishuman(victim))
+ var/mob/living/carbon/human/pulled_human = victim
+ pulled_human.disable_lights()
+ hauled_mob = WEAKREF(victim)
+ victim.forceMove(loc, get_dir(victim.loc, loc))
+ victim.handle_haul(src)
+ RegisterSignal(victim, COMSIG_MOB_DEATH, PROC_REF(release_dead_haul))
+ haul_timer = addtimer(CALLBACK(src, PROC_REF(about_to_release_hauled)), 40 SECONDS + rand(0 SECONDS, 20 SECONDS), TIMER_STOPPABLE)
+
+/mob/living/carbon/xenomorph/proc/about_to_release_hauled()
+ var/mob/living/carbon/human/user = hauled_mob?.resolve()
+ if(!user)
+ deltimer(haul_timer)
+ return
+ to_chat(src, SPAN_XENOWARNING("We feel our grip loosen on [user], we will have to release them soon."))
+ playsound(src, 'sound/voice/alien_hiss2.ogg', 15)
+ haul_timer = addtimer(CALLBACK(src, PROC_REF(release_haul)), 10 SECONDS, TIMER_STOPPABLE)
- if (stuns)
- victim.adjust_effect(2, STUN)
- else
- to_chat(src, SPAN_WARNING("There's nothing in our belly that needs regurgitating."))
+// Releasing a dead hauled mob
+/mob/living/carbon/xenomorph/proc/release_dead_haul()
+ SIGNAL_HANDLER
+ deltimer(haul_timer)
+ var/mob/living/carbon/human/user = hauled_mob?.resolve()
+ to_chat(src, SPAN_XENOWARNING("[user] is dead. No more use for them now."))
+ user.handle_unhaul()
+ UnregisterSignal(user, COMSIG_MOB_DEATH)
+ UnregisterSignal(src, COMSIG_ATOM_DIR_CHANGE)
+ hauled_mob = null
+
+// Releasing a hauled mob
+/mob/living/carbon/xenomorph/proc/release_haul(stuns = FALSE)
+ deltimer(haul_timer)
+ var/mob/living/carbon/human/user = hauled_mob?.resolve()
+ if(!user)
+ to_chat(src, SPAN_WARNING("We are not hauling anyone."))
+ return
+ user.handle_unhaul()
+ visible_message(SPAN_XENOWARNING("[src] releases [user] from their grip!"),
+ SPAN_XENOWARNING("We release [user] from our grip!"), null, 5)
+ playsound(src, 'sound/voice/alien_growl1.ogg', 15)
+ log_interact(src, user, "[key_name(src)] released [key_name(user)] at [get_area_name(loc)]")
+ if(stuns)
+ user.adjust_effect(2, STUN)
+ UnregisterSignal(user, COMSIG_MOB_DEATH)
+ UnregisterSignal(src, COMSIG_ATOM_DIR_CHANGE)
+ hauled_mob = null
/mob/living/carbon/xenomorph/proc/check_alien_construction(turf/current_turf, check_blockers = TRUE, silent = FALSE, check_doors = TRUE, ignore_nest = FALSE)
var/has_obstacle
diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
index fce6ac148acc..513633209c56 100644
--- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
@@ -60,6 +60,9 @@
var/obj/item/clothing/head/head = null
var/obj/item/r_store = null
var/obj/item/l_store = null
+ // Mob we are hauling
+ var/datum/weakref/hauled_mob
+ var/haul_timer
var/obj/item/iff_tag/iff_tag = null
@@ -276,8 +279,6 @@
/// 0/FALSE - upright, 1/TRUE - all fours
var/agility = FALSE
var/ripping_limb = FALSE
- /// The world.time at which we will regurgitate our currently-vored victim
- var/devour_timer = 0
/// For drones/hivelords. Extends the maximum build range they have
var/extra_build_dist = 0
/// tiles from self you can plant eggs.
@@ -394,7 +395,8 @@
//If we're holding things drop them
for(var/obj/item/item in old_xeno.contents) //Drop stuff
old_xeno.drop_inv_item_on_ground(item)
- old_xeno.empty_gut()
+ if(old_xeno.hauled_mob?.resolve())
+ old_xeno.release_haul(old_xeno.hauled_mob.resolve())
if(old_xeno.iff_tag)
iff_tag = old_xeno.iff_tag
@@ -690,6 +692,10 @@
/mob/living/carbon/xenomorph/Destroy()
GLOB.living_xeno_list -= src
GLOB.xeno_mob_list -= src
+ var/mob/living/carbon/human/user = hauled_mob?.resolve()
+ if(user)
+ user.handle_unhaul()
+ hauled_mob = null
if(tracked_marker)
tracked_marker.xenos_tracking -= src
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm
deleted file mode 100644
index 3bef23de8dd0..000000000000
--- a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm
+++ /dev/null
@@ -1,304 +0,0 @@
-
-/datum/action/xeno_action/activable/pounce/crusher_charge/additional_effects_always()
- var/mob/living/carbon/xenomorph/X = owner
- if (!istype(X))
- return
-
- for (var/mob/living/carbon/H in orange(1, get_turf(X)))
- if(X.can_not_harm(H))
- continue
-
- new /datum/effects/xeno_slow(H, X, null, null, 3.5 SECONDS)
- to_chat(H, SPAN_XENODANGER("You are slowed as the impact of [X] shakes the ground!"))
-
-/datum/action/xeno_action/activable/pounce/crusher_charge/additional_effects(mob/living/L)
- if (!isxeno_human(L))
- return
-
- var/mob/living/carbon/H = L
- if (H.stat == DEAD)
- return
-
- var/mob/living/carbon/xenomorph/X = owner
- if (!istype(X))
- return
-
- X.emote("roar")
- L.apply_effect(2, WEAKEN)
- X.visible_message(SPAN_XENODANGER("[X] overruns [H], brutally trampling them underfoot!"), SPAN_XENODANGER("We brutalize [H] as we crush them underfoot!"))
-
- H.apply_armoured_damage(get_xeno_damage_slash(H, direct_hit_damage), ARMOR_MELEE, BRUTE)
- X.throw_carbon(H, X.dir, 3)
-
- H.last_damage_data = create_cause_data(X.caste_type, X)
- return
-
-/datum/action/xeno_action/activable/pounce/crusher_charge/pre_windup_effects()
- RegisterSignal(owner, COMSIG_XENO_PRE_CALCULATE_ARMOURED_DAMAGE_PROJECTILE, PROC_REF(check_directional_armor))
-
- var/mob/living/carbon/xenomorph/xeno_owner = owner
- if(!istype(xeno_owner))
- return
-
- var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate
- if(!istype(crusher_delegate))
- return
-
- crusher_delegate.is_charging = TRUE
- xeno_owner.update_icons()
-
-/datum/action/xeno_action/activable/pounce/crusher_charge/post_windup_effects(interrupted)
- ..()
- UnregisterSignal(owner, COMSIG_XENO_PRE_CALCULATE_ARMOURED_DAMAGE_PROJECTILE)
- var/mob/living/carbon/xenomorph/xeno_owner = owner
- if(!istype(xeno_owner))
- return
-
- var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate
- if(!istype(crusher_delegate))
- return
-
- addtimer(CALLBACK(src, PROC_REF(undo_charging_icon)), 0.5 SECONDS) // let the icon be here for a bit, it looks cool
-
-/datum/action/xeno_action/activable/pounce/crusher_charge/proc/undo_charging_icon()
- var/mob/living/carbon/xenomorph/xeno_owner = owner
- if(!istype(xeno_owner))
- return
-
- var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate
- if(!istype(crusher_delegate))
- return
-
- crusher_delegate.is_charging = FALSE
- xeno_owner.update_icons()
-
-/datum/action/xeno_action/activable/pounce/crusher_charge/proc/check_directional_armor(mob/living/carbon/xenomorph/X, list/damagedata)
- SIGNAL_HANDLER
- var/projectile_direction = damagedata["direction"]
- if(X.dir & REVERSE_DIR(projectile_direction))
- // During the charge windup, crusher gets an extra 15 directional armor in the direction its charging
- damagedata["armor"] += frontal_armor
-
-
-// This ties the pounce/throwing backend into the old collision backend
-/mob/living/carbon/xenomorph/crusher/pounced_obj(obj/O)
- var/datum/action/xeno_action/activable/pounce/crusher_charge/CCA = get_action(src, /datum/action/xeno_action/activable/pounce/crusher_charge)
- if (istype(CCA) && !CCA.action_cooldown_check() && !(O.type in CCA.not_reducing_objects))
- CCA.reduce_cooldown(50)
-
- gain_plasma(10)
-
- if (!handle_collision(O)) // Check old backend
- obj_launch_collision(O)
-
-/mob/living/carbon/xenomorph/crusher/pounced_turf(turf/T)
- T.ex_act(EXPLOSION_THRESHOLD_VLOW, , create_cause_data(caste_type, src))
- ..(T)
-
-/datum/action/xeno_action/onclick/crusher_stomp/use_ability(atom/Atom)
- var/mob/living/carbon/xenomorph/xeno = owner
-
- if (!action_cooldown_check())
- return
-
- if (!xeno.check_state())
- return
-
- if(!check_and_use_plasma_owner())
- return
-
- playsound(xeno, 'sound/effects/bang.ogg', 25)
- xeno.visible_message(SPAN_XENODANGER("[xeno] smashes into the ground!"), SPAN_XENODANGER("We smash into the ground!"))
- xeno.create_stomp()
-
-
- for (var/mob/living/carbon/targets in orange(distance, xeno))
-
- if (targets.stat == DEAD || xeno.can_not_harm(targets))
- continue
-
- if(targets in get_turf(xeno))
- targets.apply_armoured_damage(get_xeno_damage_slash(targets, damage), ARMOR_MELEE, BRUTE)
-
- if(targets.mob_size < MOB_SIZE_BIG)
- targets.apply_effect(get_xeno_stun_duration(targets, 0.2), WEAKEN)
-
- new /datum/effects/xeno_slow(targets, xeno, ttl = get_xeno_stun_duration(targets, effect_duration))
- targets.apply_effect(get_xeno_stun_duration(targets, 0.2), WEAKEN)
- to_chat(targets, SPAN_XENOHIGHDANGER("You are slowed as [xeno] knocks you off balance!"))
-
- apply_cooldown()
- return ..()
-
-/datum/action/xeno_action/onclick/crusher_stomp/charger/use_ability()
- var/mob/living/carbon/xenomorph/Xeno = owner
- var/mob/living/carbon/Targeted
- if (!istype(Xeno))
- return
-
- if (!action_cooldown_check())
- return
-
- if (!Xeno.check_state())
- return
-
- if (!check_and_use_plasma_owner())
- return
-
- playsound(get_turf(Xeno), 'sound/effects/bang.ogg', 25, 0)
- Xeno.visible_message(SPAN_XENODANGER("[Xeno] smashes into the ground!"), SPAN_XENODANGER("We smash into the ground!"))
- Xeno.create_stomp()
-
- for (var/mob/living/carbon/Human in get_turf(Xeno)) // MOBS ONTOP
- if (Human.stat == DEAD || Xeno.can_not_harm(Human))
- continue
-
- new effect_type_base(Human, Xeno, , , get_xeno_stun_duration(Human, effect_duration))
- to_chat(Human, SPAN_XENOHIGHDANGER("You are BRUTALLY crushed and stomped on by [Xeno]!!!"))
- shake_camera(Human, 10, 2)
- if(Human.mob_size < MOB_SIZE_BIG)
- Human.apply_effect(get_xeno_stun_duration(Human, 0.2), WEAKEN)
-
- Human.apply_armoured_damage(get_xeno_damage_slash(Human, damage), ARMOR_MELEE, BRUTE,"chest", 3)
- Human.apply_armoured_damage(15, BRUTE) // random
- Human.last_damage_data = create_cause_data(Xeno.caste_type, Xeno)
- Human.emote("pain")
- Targeted = Human
- for (var/mob/living/carbon/Human in orange(distance, get_turf(Xeno))) // MOBS AROUND
- if (Human.stat == DEAD || Xeno.can_not_harm(Human))
- continue
- if(Human.client)
- shake_camera(Human, 10, 2)
- if(Targeted)
- to_chat(Human, SPAN_XENOHIGHDANGER("You watch as [Targeted] gets crushed by [Xeno]!"))
- to_chat(Human, SPAN_XENOHIGHDANGER("You are shaken as [Xeno] quakes the earth!"))
-
- apply_cooldown()
- return ..()
-
-/datum/action/xeno_action/onclick/crusher_shield/use_ability(atom/Target)
- var/mob/living/carbon/xenomorph/xeno = owner
-
- if (!istype(xeno))
- return
-
- if (!action_cooldown_check())
- return
-
- if (!xeno.check_state())
- return
-
- if (!check_and_use_plasma_owner())
- return
-
- xeno.visible_message(SPAN_XENOWARNING("[xeno] hunkers down and bolsters its defenses!"), SPAN_XENOHIGHDANGER("We hunker down and bolster our defenses!"))
- button.icon_state = "template_active"
-
- xeno.create_crusher_shield()
-
- xeno.add_xeno_shield(shield_amount, XENO_SHIELD_SOURCE_CRUSHER, /datum/xeno_shield/crusher)
- xeno.overlay_shields()
-
- xeno.explosivearmor_modifier += 1000
- xeno.recalculate_armor()
-
- addtimer(CALLBACK(src, PROC_REF(remove_explosion_immunity)), explosion_immunity_dur)
- addtimer(CALLBACK(src, PROC_REF(remove_shield)), shield_dur)
-
- apply_cooldown()
- return ..()
-
-/datum/action/xeno_action/onclick/crusher_shield/proc/remove_explosion_immunity()
- var/mob/living/carbon/xenomorph/xeno = owner
- if (!istype(xeno))
- return
-
- xeno.explosivearmor_modifier -= 1000
- xeno.recalculate_armor()
- to_chat(xeno, SPAN_XENODANGER("Our immunity to explosion damage ends!"))
-
-/datum/action/xeno_action/onclick/crusher_shield/proc/remove_shield()
- var/mob/living/carbon/xenomorph/xeno = owner
- if (!istype(xeno))
- return
-
- var/datum/xeno_shield/found
- for (var/datum/xeno_shield/shield in xeno.xeno_shields)
- if (shield.shield_source == XENO_SHIELD_SOURCE_CRUSHER)
- found = shield
- break
-
- if (istype(found))
- found.on_removal()
- qdel(found)
- to_chat(xeno, SPAN_XENOHIGHDANGER("We feel our enhanced shield end!"))
- button.icon_state = "template"
-
- xeno.overlay_shields()
-
-/datum/action/xeno_action/onclick/charger_charge/use_ability(atom/Target)
- var/mob/living/carbon/xenomorph/Xeno = owner
-
- activated = !activated
- var/will_charge = "[activated ? "now" : "no longer"]"
- to_chat(Xeno, SPAN_XENONOTICE("We will [will_charge] charge when moving."))
- if(activated)
- RegisterSignal(Xeno, COMSIG_MOVABLE_MOVED, PROC_REF(handle_movement))
- RegisterSignal(Xeno, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(handle_position_change))
- RegisterSignal(Xeno, COMSIG_ATOM_DIR_CHANGE, PROC_REF(handle_dir_change))
- RegisterSignal(Xeno, COMSIG_XENO_RECALCULATE_SPEED, PROC_REF(update_speed))
- RegisterSignal(Xeno, COMSIG_XENO_STOP_MOMENTUM, PROC_REF(stop_momentum))
- RegisterSignal(Xeno, COMSIG_MOVABLE_ENTERED_RIVER, PROC_REF(handle_river))
- RegisterSignal(Xeno, COMSIG_LIVING_PRE_COLLIDE, PROC_REF(handle_collision))
- RegisterSignal(Xeno, COMSIG_XENO_START_CHARGING, PROC_REF(start_charging))
- button.icon_state = "template_active"
- else
- stop_momentum()
- UnregisterSignal(Xeno, list(
- COMSIG_MOVABLE_MOVED,
- COMSIG_LIVING_SET_BODY_POSITION,
- COMSIG_ATOM_DIR_CHANGE,
- COMSIG_XENO_RECALCULATE_SPEED,
- COMSIG_MOVABLE_ENTERED_RIVER,
- COMSIG_LIVING_PRE_COLLIDE,
- COMSIG_XENO_STOP_MOMENTUM,
- COMSIG_XENO_START_CHARGING,
- ))
- button.icon_state = "template"
- return ..()
-
-/datum/action/xeno_action/activable/tumble/use_ability(atom/Target)
- if(!action_cooldown_check())
- return
- var/mob/living/carbon/xenomorph/Xeno = owner
- if (!Xeno.check_state())
- return
- if(Xeno.plasma_stored <= plasma_cost)
- return
- var/target_dist = get_dist(Xeno, Target)
- var/dir_between = get_dir(Xeno, Target)
- var/target_dir
- for(var/perpen_dir in get_perpen_dir(Xeno.dir))
- if(dir_between & perpen_dir)
- target_dir = perpen_dir
- break
-
- if(!target_dir)
- return
-
- Xeno.visible_message(SPAN_XENOWARNING("[Xeno] tumbles over to the side!"), SPAN_XENOHIGHDANGER("We tumble over to the side!"))
- Xeno.spin(5,1) // note: This spins the sprite and DOES NOT affect directional armor
- var/start_charging = HAS_TRAIT(Xeno, TRAIT_CHARGING)
- SEND_SIGNAL(Xeno, COMSIG_XENO_STOP_MOMENTUM)
- Xeno.flags_atom |= DIRLOCK
- playsound(Xeno,"alien_tail_swipe", 50, 1)
-
- Xeno.use_plasma(plasma_cost)
-
- var/target = get_step(get_step(Xeno, target_dir), target_dir)
- var/list/collision_callbacks = list(/mob/living/carbon/human = CALLBACK(src, PROC_REF(handle_mob_collision)))
- var/list/end_throw_callbacks = list(CALLBACK(src, PROC_REF(on_end_throw), start_charging))
- Xeno.throw_atom(target, target_dist, SPEED_FAST, launch_type = LOW_LAUNCH, pass_flags = PASS_CRUSHER_CHARGE, end_throw_callbacks = end_throw_callbacks, collision_callbacks = collision_callbacks)
-
- apply_cooldown()
- return ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/facehugger/facehugger_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/facehugger/facehugger_abilities.dm
index d5ce19d4a532..5e3c95007733 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/facehugger/facehugger_abilities.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/facehugger/facehugger_abilities.dm
@@ -12,7 +12,7 @@
knockdown = TRUE
knockdown_duration = 0.5
windup = TRUE
- windup_duration = FACEHUGGER_WINDUP_DURATION
+ windup_duration = FACEHUGGER_LEAP_DURATION
freeze_self = TRUE
freeze_time = 5
freeze_play_sound = FALSE
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm
index fe96e5958a13..f3287592954b 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm
@@ -59,12 +59,12 @@
if(X && !X.buckled && !X.is_mob_incapacitated())
return TRUE
-// Regurgitate
-/datum/action/xeno_action/onclick/regurgitate
- name = "Regurgitate"
- action_icon_state = "regurgitate"
+// release_haul
+/datum/action/xeno_action/onclick/release_haul
+ name = "Release"
+ action_icon_state = "release_haul"
plasma_cost = 0
- macro_path = /datum/action/xeno_action/verb/verb_regurgitate
+ macro_path = /datum/action/xeno_action/verb/verb_release_haul
action_type = XENO_ACTION_CLICK
// Choose Resin
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_ability_macros.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_ability_macros.dm
index 5e22064cfeb2..ae0736e62c2d 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_ability_macros.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_ability_macros.dm
@@ -30,11 +30,11 @@
var/action_name = "Toggle Spit Type"
handle_xeno_macro(src, action_name)
-/datum/action/xeno_action/verb/verb_regurgitate()
+/datum/action/xeno_action/verb/verb_release_haul()
set category = "Alien"
- set name = "Regurgitate"
+ set name = "Release"
set hidden = TRUE
- var/action_name = "Regurgitate"
+ var/action_name = "Release"
handle_xeno_macro(src, action_name)
/datum/action/xeno_action/verb/verb_choose_resin_structure()
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
index abcae5bf8a60..ccdb42baffaf 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
@@ -123,19 +123,16 @@
button.overlays += image('icons/mob/hud/actions_xeno.dmi', button, "shift_spit_[X.ammo.icon_state]")
return ..()
-/datum/action/xeno_action/onclick/regurgitate/use_ability(atom/A)
+/datum/action/xeno_action/onclick/release_haul/use_ability(atom/A)
var/mob/living/carbon/xenomorph/X = owner
if(!X.check_state())
return
if(!isturf(X.loc))
- to_chat(X, SPAN_WARNING("We cannot regurgitate here."))
+ to_chat(X, SPAN_WARNING("We cannot put them down here."))
return
- if(length(X.stomach_contents))
- for(var/mob/living/M in X.stomach_contents)
- // Also has good reason to be a proc on all Xenos
- X.regurgitate(M, TRUE)
+ X.release_haul(TRUE)
return ..()
@@ -441,7 +438,7 @@
pre_pounce_effects()
X.pounce_distance = get_dist(X, A)
- X.throw_atom(A, distance, throw_speed, X, launch_type = LOW_LAUNCH, pass_flags = pounce_pass_flags, collision_callbacks = pounce_callbacks)
+ X.throw_atom(A, distance, throw_speed, X, launch_type = LOW_LAUNCH, pass_flags = pounce_pass_flags, collision_callbacks = pounce_callbacks, tracking=TRUE)
X.update_icons()
additional_effects_always()
@@ -932,6 +929,8 @@
/datum/action/xeno_action/activable/tail_stab/use_ability(atom/targetted_atom)
var/mob/living/carbon/xenomorph/stabbing_xeno = owner
+ if(HAS_TRAIT(targetted_atom, TRAIT_HAULED))
+ return
if(HAS_TRAIT(stabbing_xeno, TRAIT_ABILITY_BURROWED) || stabbing_xeno.is_ventcrawling)
to_chat(stabbing_xeno, SPAN_XENOWARNING("We must be above ground to do this."))
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
index be3cad44922b..6cba9882d316 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
@@ -235,7 +235,7 @@
apply_cooldown()
- predalien_smash.throw_atom(get_step_towards(affected_atom, predalien_smash), grab_range, SPEED_FAST, predalien_smash)
+ predalien_smash.throw_atom(get_step_towards(affected_atom, predalien_smash), grab_range, SPEED_FAST, predalien_smash, tracking=TRUE)
if(predalien_smash.Adjacent(carbon) && predalien_smash.start_pulling(carbon, TRUE))
playsound(carbon.pulledby, 'sound/voice/predalien_growl.ogg', 75, 0, status = 0) // bang and roar for dramatic effect
diff --git a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
index b07730340525..ccfdf0702266 100644
--- a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
+++ b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
@@ -12,6 +12,10 @@
if(attacking_xeno.fortify || HAS_TRAIT(attacking_xeno, TRAIT_ABILITY_BURROWED))
return XENO_NO_DELAY_ACTION
+ if(HAS_TRAIT(src, TRAIT_HAULED))
+ to_chat(attacking_xeno, SPAN_WARNING("[src] is being hauled, we cannot do anything to them."))
+ return
+
var/intent = attacking_xeno.a_intent
if(attacking_xeno.behavior_delegate)
@@ -592,13 +596,19 @@
if(M.is_mob_incapacitated() || M.body_position != STANDING_UP)
return XENO_NO_DELAY_ACTION
- var/delay
+ var/delay = 4 SECONDS
if(!arePowerSystemsOn())
delay = 1 SECONDS
playsound(loc, "alien_doorpry", 25, TRUE)
else
- delay = 4 SECONDS
+ switch(M.mob_size)
+ if(MOB_SIZE_XENO_SMALL, MOB_SIZE_XENO_VERY_SMALL)
+ delay = 4 SECONDS
+ if(MOB_SIZE_BIG)
+ delay = 1 SECONDS
+ if(MOB_SIZE_XENO)
+ delay = 3 SECONDS
playsound(loc, "alien_doorpry", 25, TRUE)
M.visible_message(SPAN_WARNING("[M] digs into [src] and begins to pry it open."),
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm b/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm
index c010ec224b97..7ae9b4d61c88 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm
@@ -66,7 +66,7 @@
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab/boiler,
/datum/action/xeno_action/activable/corrosive_acid/strong,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm b/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm
index daf98a07849e..7b8962e44fc2 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm
@@ -56,7 +56,7 @@
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/corrosive_acid,
@@ -146,6 +146,12 @@
invisibility = 101
alpha = 100
anchored = TRUE
+
+ var/mob/living/carbon/human/hauled = hauled_mob?.resolve()
+
+ if(hauled)
+ hauled.forceMove(src)
+
if(caste.fire_immunity == FIRE_IMMUNITY_NONE)
RegisterSignal(src, COMSIG_LIVING_PREIGNITION, PROC_REF(fire_immune))
RegisterSignal(src, list(
@@ -183,6 +189,11 @@
invisibility = FALSE
alpha = initial(alpha)
anchored = FALSE
+
+ var/mob/living/carbon/human/hauled = hauled_mob?.resolve()
+ if(hauled)
+ hauled.forceMove(loc)
+
playsound(loc, 'sound/effects/burrowoff.ogg', 25)
for(var/mob/living/carbon/mob in loc)
if(!can_not_harm(mob))
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
index a1b68dc1b09c..21a4c6db0b36 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
@@ -62,7 +62,7 @@
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/place_construction,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
index 97f554fbb8b2..18b38fffa029 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
@@ -54,7 +54,7 @@
organ_value = 3000
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/pounce/crusher_charge,
@@ -288,3 +288,238 @@
if(bound_xeno.throwing || is_charging) //Let it build up a bit so we're not changing icons every single turf
bound_xeno.icon_state = "[bound_xeno.get_strain_icon()] Crusher Charging"
return TRUE
+
+/datum/action/xeno_action/activable/pounce/crusher_charge/additional_effects_always()
+ var/mob/living/carbon/xenomorph/xeno = owner
+ if (!istype(xeno))
+ return
+
+ for (var/mob/living/carbon/target in orange(1, get_turf(xeno)))
+ if(xeno.can_not_harm(target))
+ continue
+
+ new /datum/effects/xeno_slow(target, xeno, null, null, 3.5 SECONDS)
+ to_chat(target, SPAN_XENODANGER("You are slowed as the impact of [xeno] shakes the ground!"))
+
+/datum/action/xeno_action/activable/pounce/crusher_charge/additional_effects(mob/living/target)
+ var/mob/living/carbon/xenomorph/xeno = owner
+ var/mob/living/carbon/target_to_check = target
+ if (!isxeno_human(target))
+ return
+
+ if (target_to_check.stat == DEAD)
+ return
+
+ xeno.emote("roar")
+ target.apply_effect(2, WEAKEN)
+ xeno.visible_message(SPAN_XENODANGER("[xeno] overruns [target_to_check], brutally trampling them underfoot!"), SPAN_XENODANGER("We brutalize [target_to_check] as we crush them underfoot!"))
+
+ target_to_check.apply_armoured_damage(get_xeno_damage_slash(target_to_check, direct_hit_damage), ARMOR_MELEE, BRUTE)
+ xeno.throw_carbon(target_to_check, xeno.dir, 3)
+
+ target_to_check.last_damage_data = create_cause_data(xeno.caste_type, xeno)
+ return
+
+/datum/action/xeno_action/activable/pounce/crusher_charge/pre_windup_effects()
+ RegisterSignal(owner, COMSIG_XENO_PRE_CALCULATE_ARMOURED_DAMAGE_PROJECTILE, PROC_REF(check_directional_armor))
+
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ if(!istype(xeno_owner))
+ return
+
+ var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate
+ if(!istype(crusher_delegate))
+ return
+
+ crusher_delegate.is_charging = TRUE
+ xeno_owner.update_icons()
+
+/datum/action/xeno_action/activable/pounce/crusher_charge/post_windup_effects(interrupted)
+ ..()
+ UnregisterSignal(owner, COMSIG_XENO_PRE_CALCULATE_ARMOURED_DAMAGE_PROJECTILE)
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ if(!istype(xeno_owner))
+ return
+
+ var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate
+ if(!istype(crusher_delegate))
+ return
+
+ addtimer(CALLBACK(src, PROC_REF(undo_charging_icon)), 0.5 SECONDS) // let the icon be here for a bit, it looks cool
+
+/datum/action/xeno_action/activable/pounce/crusher_charge/proc/undo_charging_icon()
+ var/mob/living/carbon/xenomorph/xeno_owner = owner
+ if(!istype(xeno_owner))
+ return
+
+ var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate
+ if(!istype(crusher_delegate))
+ return
+
+ crusher_delegate.is_charging = FALSE
+ xeno_owner.update_icons()
+
+/datum/action/xeno_action/activable/pounce/crusher_charge/proc/check_directional_armor(mob/living/carbon/xenomorph/X, list/damagedata)
+ SIGNAL_HANDLER
+ var/projectile_direction = damagedata["direction"]
+ if(X.dir & REVERSE_DIR(projectile_direction))
+ // During the charge windup, crusher gets an extra 15 directional armor in the direction its charging
+ damagedata["armor"] += frontal_armor
+
+
+// This ties the pounce/throwing backend into the old collision backend
+/mob/living/carbon/xenomorph/crusher/pounced_obj(obj/pounced_object)
+ var/datum/action/xeno_action/activable/pounce/crusher_charge/crusher_charge_action = get_action(src, /datum/action/xeno_action/activable/pounce/crusher_charge)
+ if (istype(crusher_charge_action) && !crusher_charge_action.action_cooldown_check() && !(pounced_object.type in crusher_charge_action.not_reducing_objects))
+ crusher_charge_action.reduce_cooldown(50)
+
+ gain_plasma(10)
+
+ if (!handle_collision(pounced_object)) // Check old backend
+ obj_launch_collision(pounced_object)
+
+/mob/living/carbon/xenomorph/crusher/pounced_turf(turf/pounced_turf)
+ pounced_turf.ex_act(EXPLOSION_THRESHOLD_VLOW, , create_cause_data(caste_type, src))
+ ..(pounced_turf)
+
+/datum/action/xeno_action/onclick/crusher_stomp/use_ability(atom/Atom)
+ var/mob/living/carbon/xenomorph/xeno = owner
+
+ if (!action_cooldown_check())
+ return
+
+ if (!xeno.check_state())
+ return
+
+ if(!check_and_use_plasma_owner())
+ return
+
+ playsound(xeno, 'sound/effects/bang.ogg', 25)
+ xeno.visible_message(SPAN_XENODANGER("[xeno] smashes into the ground!"), SPAN_XENODANGER("We smash into the ground!"))
+ xeno.create_stomp()
+
+
+ for (var/mob/living/carbon/targets in orange(distance, xeno))
+
+ if (targets.stat == DEAD || xeno.can_not_harm(targets))
+ continue
+
+ if(targets in get_turf(xeno))
+ targets.apply_armoured_damage(get_xeno_damage_slash(targets, damage), ARMOR_MELEE, BRUTE)
+
+ if(targets.mob_size < MOB_SIZE_BIG)
+ targets.apply_effect(get_xeno_stun_duration(targets, 0.2), WEAKEN)
+
+ new /datum/effects/xeno_slow(targets, xeno, ttl = get_xeno_stun_duration(targets, effect_duration))
+ targets.apply_effect(get_xeno_stun_duration(targets, 0.2), WEAKEN)
+ to_chat(targets, SPAN_XENOHIGHDANGER("You are slowed as [xeno] knocks you off balance!"))
+
+ apply_cooldown()
+ return ..()
+
+/datum/action/xeno_action/onclick/crusher_stomp/charger/use_ability()
+ var/mob/living/carbon/xenomorph/xeno = owner
+ var/mob/living/carbon/stomped_carbon
+
+ if (!istype(xeno))
+ return
+
+ if (!action_cooldown_check())
+ return
+
+ if (!xeno.check_state())
+ return
+
+ if (!check_and_use_plasma_owner())
+ return
+
+ playsound(get_turf(xeno), 'sound/effects/bang.ogg', 25, 0)
+ xeno.visible_message(SPAN_XENODANGER("[xeno] smashes into the ground!"), SPAN_XENODANGER("We smash into the ground!"))
+ xeno.create_stomp()
+
+ for (var/mob/living/carbon/target_to_stomp in get_turf(xeno)) // MOBS ONTOP
+ if (target_to_stomp.stat == DEAD || xeno.can_not_harm(target_to_stomp))
+ continue
+
+ new effect_type_base(target_to_stomp, xeno, null, null, get_xeno_stun_duration(target_to_stomp, effect_duration))
+ to_chat(target_to_stomp, SPAN_XENOHIGHDANGER("You are BRUTALLY crushed and stomped on by [xeno]!!!"))
+ shake_camera(target_to_stomp, 10, 2)
+ if(target_to_stomp.mob_size < MOB_SIZE_BIG)
+ target_to_stomp.apply_effect(get_xeno_stun_duration(target_to_stomp, 0.2), WEAKEN)
+
+ target_to_stomp.apply_armoured_damage(get_xeno_damage_slash(target_to_stomp, damage), ARMOR_MELEE, BRUTE,"chest", 3)
+ target_to_stomp.apply_armoured_damage(15, BRUTE) // random
+ target_to_stomp.last_damage_data = create_cause_data(xeno.caste_type, xeno)
+ target_to_stomp.emote("pain")
+ target_to_stomp = stomped_carbon
+ for (var/mob/living/carbon/targets_to_get in orange(distance, get_turf(xeno))) // MOBS AROUND
+ if (targets_to_get.stat == DEAD || xeno.can_not_harm(targets_to_get))
+ continue
+ if(targets_to_get.client)
+ shake_camera(targets_to_get, 10, 2)
+ if(stomped_carbon)
+ to_chat(targets_to_get, SPAN_XENOHIGHDANGER("You watch as [stomped_carbon] gets crushed by [xeno]!"))
+ to_chat(targets_to_get, SPAN_XENOHIGHDANGER("You are shaken as [xeno] quakes the earth!"))
+
+ apply_cooldown()
+ return ..()
+
+/datum/action/xeno_action/onclick/crusher_shield/use_ability(atom/Target)
+ var/mob/living/carbon/xenomorph/xeno = owner
+
+ if (!istype(xeno))
+ return
+
+ if (!action_cooldown_check())
+ return
+
+ if (!xeno.check_state())
+ return
+
+ if (!check_and_use_plasma_owner())
+ return
+
+ xeno.visible_message(SPAN_XENOWARNING("[xeno] hunkers down and bolsters its defenses!"), SPAN_XENOHIGHDANGER("We hunker down and bolster our defenses!"))
+ button.icon_state = "template_active"
+
+ xeno.create_crusher_shield()
+
+ xeno.add_xeno_shield(shield_amount, XENO_SHIELD_SOURCE_CRUSHER, /datum/xeno_shield/crusher)
+ xeno.overlay_shields()
+
+ xeno.explosivearmor_modifier += 1000
+ xeno.recalculate_armor()
+
+ addtimer(CALLBACK(src, PROC_REF(remove_explosion_immunity)), explosion_immunity_dur)
+ addtimer(CALLBACK(src, PROC_REF(remove_shield)), shield_dur)
+
+ apply_cooldown()
+ return ..()
+
+/datum/action/xeno_action/onclick/crusher_shield/proc/remove_explosion_immunity()
+ var/mob/living/carbon/xenomorph/xeno = owner
+ if (!istype(xeno))
+ return
+
+ xeno.explosivearmor_modifier -= 1000
+ xeno.recalculate_armor()
+ to_chat(xeno, SPAN_XENODANGER("Our immunity to explosion damage ends!"))
+
+/datum/action/xeno_action/onclick/crusher_shield/proc/remove_shield()
+ var/mob/living/carbon/xenomorph/xeno = owner
+ if (!istype(xeno))
+ return
+
+ var/datum/xeno_shield/found
+ for (var/datum/xeno_shield/shield in xeno.xeno_shields)
+ if (shield.shield_source == XENO_SHIELD_SOURCE_CRUSHER)
+ found = shield
+ break
+
+ if (istype(found))
+ found.on_removal()
+ qdel(found)
+ to_chat(xeno, SPAN_XENOHIGHDANGER("We feel our enhanced shield end!"))
+ button.icon_state = "template"
+
+ xeno.overlay_shields()
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm b/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
index cf378b241ea9..f810624f1d7e 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
@@ -43,7 +43,7 @@
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab/slam,
/datum/action/xeno_action/onclick/toggle_crest,
@@ -168,7 +168,7 @@
if(!fendy.crest_defense)
apply_cooldown()
- fendy.throw_atom(get_step_towards(carbone, fendy), 3, SPEED_SLOW, fendy)
+ fendy.throw_atom(get_step_towards(carbone, fendy), 3, SPEED_SLOW, fendy, tracking=TRUE)
if(!fendy.Adjacent(carbone))
on_cooldown_end()
return
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm b/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
index 26c6943cd5df..ff9b3bc862e5 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
@@ -58,7 +58,7 @@
old_x = -12
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/corrosive_acid/weak,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
index 8de39dc98548..1ff8747ff2e0 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
@@ -145,7 +145,7 @@
to_chat(src, SPAN_WARNING("You can't infect \the [human]..."))
return
visible_message(SPAN_WARNING("\The [src] starts climbing onto \the [human]'s face..."), SPAN_XENONOTICE("You start climbing onto \the [human]'s face..."))
- if(!do_after(src, FACEHUGGER_WINDUP_DURATION, INTERRUPT_ALL, BUSY_ICON_HOSTILE, human, INTERRUPT_MOVED, BUSY_ICON_HOSTILE))
+ if(!do_after(src, FACEHUGGER_CLIMB_DURATION, INTERRUPT_ALL, BUSY_ICON_HOSTILE, human, INTERRUPT_MOVED, BUSY_ICON_HOSTILE))
return
if(human.body_position != LYING_DOWN)
to_chat(src, SPAN_WARNING("You can't reach \the [human], they need to be lying down."))
@@ -264,5 +264,5 @@
name = "Base Facehugger Behavior Delegate"
/datum/behavior_delegate/facehugger_base/on_life()
- if(bound_xeno.body_position == STANDING_UP && !(locate(/obj/effect/alien/weeds) in get_turf(bound_xeno)))
- bound_xeno.adjustBruteLoss(1)
+ if(!(locate(/obj/effect/alien/weeds) in get_turf(bound_xeno)))
+ bound_xeno.adjustBruteLoss(2)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm b/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
index eb01499a7fb4..a49ff2d96a88 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
@@ -62,7 +62,7 @@
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/onclick/xenohide,
/datum/action/xeno_action/activable/pounce/gorge,
/datum/action/xeno_action/onclick/sense_owner,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm b/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm
index cd4f90ce02fe..91b386a7f02f 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm
@@ -60,7 +60,7 @@
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/corrosive_acid,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/King.dm b/code/modules/mob/living/carbon/xenomorph/castes/King.dm
index ca16bc1d628a..46aa1ff78699 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/King.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/King.dm
@@ -49,7 +49,7 @@
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/onclick/rend,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm b/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm
index 66aeb358b813..e74c162e3927 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm
@@ -41,7 +41,7 @@
organ_value = 2000
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/pounce/lurker,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm b/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
index 43c7c97c7a1f..808a49233d0c 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
@@ -56,7 +56,7 @@
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/corrosive_acid,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
index d658d2e83195..379c5c90b8dc 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
@@ -56,7 +56,7 @@
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/onclick/feralrush,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
index c34901808437..f86e7d5e31ae 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
@@ -297,7 +297,7 @@
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/place_construction/not_primary, //normally fifth macro but not as important for queen
@@ -327,7 +327,7 @@
var/list/mobile_abilities = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/place_construction/not_primary, //normally fifth macro but not as important for queen
@@ -757,7 +757,9 @@
/mob/living/carbon/xenomorph/queen/proc/queen_gut(atom/target)
if(!iscarbon(target))
return FALSE
-
+ if(HAS_TRAIT(target, TRAIT_HAULED))
+ to_chat(src, SPAN_XENOWARNING("[target] needs to be released first."))
+ return FALSE
var/mob/living/carbon/victim = target
if(get_dist(src, victim) > 1)
@@ -849,7 +851,7 @@
var/list/immobile_abilities = list(
// These already have their placement locked in:
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/place_construction/not_primary,
/datum/action/xeno_action/onclick/emit_pheromones,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm b/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm
index f46617cfe838..8a6bc4e0d4c7 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm
@@ -53,7 +53,7 @@
organ_value = 3000
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/pounce/charge,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
index 7cbe9e8caef7..5be771cea2df 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
@@ -53,7 +53,7 @@
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/onclick/xenohide,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm b/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm
index d0e6ea1c85fe..b4b5ffea998d 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm
@@ -42,7 +42,7 @@
organ_value = 800
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/corrosive_acid/weak,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm b/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm
index 0f2022f38e34..e0990d9c3ed7 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm
@@ -44,7 +44,7 @@
tier = 2
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab/spitter,
/datum/action/xeno_action/activable/corrosive_acid,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm b/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm
index b0066024b5a2..4faf76b710b4 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm
@@ -46,7 +46,7 @@
organ_value = 2000
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/warrior_punch,
@@ -285,7 +285,7 @@
lunge_user.visible_message(SPAN_XENOWARNING("[lunge_user] lunges towards [carbon]!"), SPAN_XENOWARNING("We lunge at [carbon]!"))
- lunge_user.throw_atom(get_step_towards(affected_atom, lunge_user), grab_range, SPEED_FAST, lunge_user)
+ lunge_user.throw_atom(get_step_towards(affected_atom, lunge_user), grab_range, SPEED_FAST, lunge_user, tracking=TRUE)
if (lunge_user.Adjacent(carbon))
lunge_user.start_pulling(carbon,1)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm b/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
index 531f7f2d9b14..1ddf4919ae66 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
@@ -59,7 +59,7 @@
acid_blood_damage = 15
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
- /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/onclick/release_haul,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/corrosive_acid/weak,
diff --git a/code/modules/mob/living/carbon/xenomorph/damage_procs.dm b/code/modules/mob/living/carbon/xenomorph/damage_procs.dm
index a09212a46dd5..5bed5dfd1b59 100644
--- a/code/modules/mob/living/carbon/xenomorph/damage_procs.dm
+++ b/code/modules/mob/living/carbon/xenomorph/damage_procs.dm
@@ -54,9 +54,6 @@
last_damage_data = istype(cause_data) ? cause_data : create_cause_data(cause_data)
- if(severity > EXPLOSION_THRESHOLD_LOW && length(stomach_contents))
- for(var/mob/M in stomach_contents)
- M.ex_act(severity - EXPLOSION_THRESHOLD_LOW, last_damage_data, pierce)
var/b_loss = 0
var/f_loss = 0
@@ -276,6 +273,8 @@
for(var/mob/living/carbon/human/victim in orange(radius, src)) //Loop through all nearby victims, including the tile.
splash_chance = 65 - (i * 5)
+ if(HAS_TRAIT(victim, TRAIT_HAULED))
+ continue
if(victim.loc == loc)
splash_chance += 30 //Same tile? BURN
if(victim.species?.acid_blood_dodge_chance)
diff --git a/code/modules/mob/living/carbon/xenomorph/death.dm b/code/modules/mob/living/carbon/xenomorph/death.dm
index 1243d3badcdc..3e72aa9fe85d 100644
--- a/code/modules/mob/living/carbon/xenomorph/death.dm
+++ b/code/modules/mob/living/carbon/xenomorph/death.dm
@@ -107,10 +107,6 @@ GLOBAL_VAR_INIT(total_dead_xenos, 0)
if(behavior_delegate)
behavior_delegate.handle_death(src)
- for(var/atom/movable/A in stomach_contents)
- stomach_contents.Remove(A)
- A.acid_damage = 0 //Reset the acid damage
- A.forceMove(loc)
// Banished xeno provide a burrowed larva on death to compensate
if(banished && refunds_larva_if_banished)
diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm
index bddd3bb58c6c..6d21db1cb71f 100644
--- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm
+++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm
@@ -747,9 +747,6 @@
if(get_area(xeno) != hijacked_dropship && xeno.loc && is_ground_level(xeno.loc.z))
if(isfacehugger(xeno) || islesserdrone(xeno))
to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind."))
- if(length(xeno.stomach_contents))
- xeno.devour_timer = 0
- xeno.handle_stomach_contents()
qdel(xeno)
continue
if(xeno.hunter_data.hunted && !isqueen(xeno))
@@ -757,9 +754,8 @@
xeno.set_hive_and_update(XENO_HIVE_FORSAKEN)
else
to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind."))
- if(length(xeno.stomach_contents))
- xeno.devour_timer = 0
- xeno.handle_stomach_contents()
+ if(xeno.hauled_mob?.resolve())
+ xeno.release_haul(xeno.hauled_mob.resolve())
qdel(xeno)
stored_larva++
continue
diff --git a/code/modules/mob/living/carbon/xenomorph/life.dm b/code/modules/mob/living/carbon/xenomorph/life.dm
index 2a3f8ed3bfdb..13a4870f7b48 100644
--- a/code/modules/mob/living/carbon/xenomorph/life.dm
+++ b/code/modules/mob/living/carbon/xenomorph/life.dm
@@ -25,7 +25,6 @@
handle_xeno_fire()
handle_pheromones()
handle_regular_status_updates()
- handle_stomach_contents()
handle_overwatch() // For new Xeno hivewide overwatch - Fourk, 6/24/19
update_icons()
handle_luminosity()
@@ -221,23 +220,6 @@
return TRUE
-/mob/living/carbon/xenomorph/proc/handle_stomach_contents()
- //Deal with dissolving/damaging stuff in stomach.
- if(length(stomach_contents))
- for(var/atom/movable/M in stomach_contents)
- if(ishuman(M))
- if(world.time > devour_timer - 50 && world.time < devour_timer - 30)
- to_chat(src, SPAN_WARNING("We're about to regurgitate [M]..."))
- playsound(loc, 'sound/voice/alien_drool1.ogg', 50, 1)
- var/mob/living/carbon/human/H = M
- if(world.time > devour_timer || (H.stat == DEAD && !H.chestburst))
- regurgitate(H)
-
- M.acid_damage++
- if(M.acid_damage > 300)
- to_chat(src, SPAN_XENODANGER("\The [M] is dissolved in our gut with a gurgle."))
- stomach_contents.Remove(M)
- qdel(M)
/mob/living/carbon/xenomorph/proc/handle_regular_hud_updates()
if(!mind)
diff --git a/code/modules/mob/living/carbon/xenomorph/resin_constructions.dm b/code/modules/mob/living/carbon/xenomorph/resin_constructions.dm
index 1bf817392ab9..c91344c34d4a 100644
--- a/code/modules/mob/living/carbon/xenomorph/resin_constructions.dm
+++ b/code/modules/mob/living/carbon/xenomorph/resin_constructions.dm
@@ -68,7 +68,7 @@
return FALSE
if(range_between_constructions)
- for(var/i in urange(range_between_constructions, T))
+ for(var/i in long_range(range_between_constructions, T))
var/atom/A = i
if(A.type == build_path)
to_chat(X, SPAN_WARNING("This is too close to another similar structure!"))
diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/crusher/charger.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/crusher/charger.dm
index 7eebd3c6ec90..02c659ad06c3 100644
--- a/code/modules/mob/living/carbon/xenomorph/strains/castes/crusher/charger.dm
+++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/crusher/charger.dm
@@ -652,6 +652,74 @@
charger_ability.stop_momentum()
+/datum/action/xeno_action/onclick/charger_charge/use_ability(atom/Target)
+ var/mob/living/carbon/xenomorph/Xeno = owner
+
+ activated = !activated
+ var/will_charge = "[activated ? "now" : "no longer"]"
+ to_chat(Xeno, SPAN_XENONOTICE("We will [will_charge] charge when moving."))
+ if(activated)
+ RegisterSignal(Xeno, COMSIG_MOVABLE_MOVED, PROC_REF(handle_movement))
+ RegisterSignal(Xeno, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(handle_position_change))
+ RegisterSignal(Xeno, COMSIG_ATOM_DIR_CHANGE, PROC_REF(handle_dir_change))
+ RegisterSignal(Xeno, COMSIG_XENO_RECALCULATE_SPEED, PROC_REF(update_speed))
+ RegisterSignal(Xeno, COMSIG_XENO_STOP_MOMENTUM, PROC_REF(stop_momentum))
+ RegisterSignal(Xeno, COMSIG_MOVABLE_ENTERED_RIVER, PROC_REF(handle_river))
+ RegisterSignal(Xeno, COMSIG_LIVING_PRE_COLLIDE, PROC_REF(handle_collision))
+ RegisterSignal(Xeno, COMSIG_XENO_START_CHARGING, PROC_REF(start_charging))
+ button.icon_state = "template_active"
+ else
+ stop_momentum()
+ UnregisterSignal(Xeno, list(
+ COMSIG_MOVABLE_MOVED,
+ COMSIG_LIVING_SET_BODY_POSITION,
+ COMSIG_ATOM_DIR_CHANGE,
+ COMSIG_XENO_RECALCULATE_SPEED,
+ COMSIG_MOVABLE_ENTERED_RIVER,
+ COMSIG_LIVING_PRE_COLLIDE,
+ COMSIG_XENO_STOP_MOMENTUM,
+ COMSIG_XENO_START_CHARGING,
+ ))
+ button.icon_state = "template"
+ return ..()
+
+/datum/action/xeno_action/activable/tumble/use_ability(atom/Target)
+ if(!action_cooldown_check())
+ return
+ var/mob/living/carbon/xenomorph/Xeno = owner
+ if (!Xeno.check_state())
+ return
+ if(Xeno.plasma_stored <= plasma_cost)
+ return
+ var/target_dist = get_dist(Xeno, Target)
+ var/dir_between = get_dir(Xeno, Target)
+ var/target_dir
+ for(var/perpen_dir in get_perpen_dir(Xeno.dir))
+ if(dir_between & perpen_dir)
+ target_dir = perpen_dir
+ break
+
+ if(!target_dir)
+ return
+
+ Xeno.visible_message(SPAN_XENOWARNING("[Xeno] tumbles over to the side!"), SPAN_XENOHIGHDANGER("We tumble over to the side!"))
+ Xeno.spin(5,1) // note: This spins the sprite and DOES NOT affect directional armor
+ var/start_charging = HAS_TRAIT(Xeno, TRAIT_CHARGING)
+ SEND_SIGNAL(Xeno, COMSIG_XENO_STOP_MOMENTUM)
+ Xeno.flags_atom |= DIRLOCK
+ playsound(Xeno,"alien_tail_swipe", 50, 1)
+
+ Xeno.use_plasma(plasma_cost)
+
+ var/target = get_step(get_step(Xeno, target_dir), target_dir)
+ var/list/collision_callbacks = list(/mob/living/carbon/human = CALLBACK(src, PROC_REF(handle_mob_collision)))
+ var/list/end_throw_callbacks = list(CALLBACK(src, PROC_REF(on_end_throw), start_charging))
+ Xeno.throw_atom(target, target_dist, SPEED_FAST, launch_type = LOW_LAUNCH, pass_flags = PASS_CRUSHER_CHARGE, end_throw_callbacks = end_throw_callbacks, collision_callbacks = collision_callbacks)
+
+ apply_cooldown()
+ return ..()
+
+
#undef CHARGER_DESTROY
#undef CHARGER_DAMAGE_CADE
diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/facehugger/watcher.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/facehugger/watcher.dm
index c5e1cff29c63..86e385bdd9f6 100644
--- a/code/modules/mob/living/carbon/xenomorph/strains/castes/facehugger/watcher.dm
+++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/facehugger/watcher.dm
@@ -22,10 +22,8 @@
/datum/behavior_delegate/facehugger_watcher/on_life()
// Sap health if we're standing, not on weeds, and not zoomed out
- if(bound_xeno.body_position != STANDING_UP)
- return
if(bound_xeno.is_zoomed)
return
if(locate(/obj/effect/alien/weeds) in get_turf(bound_xeno))
return
- bound_xeno.adjustBruteLoss(1)
+ bound_xeno.adjustBruteLoss(2)
diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_helpers.dm b/code/modules/mob/living/carbon/xenomorph/xeno_helpers.dm
index 294ea27b6260..2bca2ca224fd 100644
--- a/code/modules/mob/living/carbon/xenomorph/xeno_helpers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xeno_helpers.dm
@@ -2,11 +2,11 @@
return (mob_size < MOB_SIZE_BIG && caste.can_vent_crawl)
/mob/living/carbon/xenomorph/ventcrawl_carry()
- if(length(stomach_contents))
- for(var/mob/living/carbon/human/H in stomach_contents)
- if(!isspeciesmonkey(H))
- to_chat(src, SPAN_XENOWARNING("You cannot ventcrawl with [H] inside you!"))
- return FALSE
+ var/mob/living/carbon/human/user = hauled_mob?.resolve()
+ if(user)
+ if(!isspeciesmonkey(user))
+ to_chat(src, SPAN_XENOWARNING("You cannot ventcrawl while hauling [user]!"))
+ return FALSE
return TRUE
/mob/living/carbon/xenomorph/can_inject()
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 9bb5357ede55..f5c44160db89 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -415,7 +415,7 @@
..()
-/mob/living/launch_towards(datum/launch_metadata/LM)
+/mob/living/launch_towards(datum/launch_metadata/LM, tracking = FALSE)
if(src)
SEND_SIGNAL(src, COMSIG_MOB_MOVE_OR_LOOK, TRUE, dir, dir)
if(!istype(LM) || !LM.target || !src)
@@ -694,11 +694,13 @@
// legacy procs
/mob/living/put_in_l_hand(obj/item/W)
if(body_position == LYING_DOWN)
- return
+ if(!HAS_TRAIT(src, TRAIT_HAULED))
+ return
return ..()
/mob/living/put_in_r_hand(obj/item/W)
if(body_position == LYING_DOWN)
- return
+ if(!HAS_TRAIT(src, TRAIT_HAULED))
+ return
return ..()
/mob/living/onZImpact(turf/impact_turf, height)
diff --git a/code/modules/mob/living/living_verbs.dm b/code/modules/mob/living/living_verbs.dm
index 62c15d12f7f0..e762b2e02376 100644
--- a/code/modules/mob/living/living_verbs.dm
+++ b/code/modules/mob/living/living_verbs.dm
@@ -1,7 +1,7 @@
/mob/living/can_resist()
if(next_move > world.time)
return FALSE
- if(HAS_TRAIT(src, TRAIT_INCAPACITATED))
+ if(HAS_TRAIT(src, TRAIT_INCAPACITATED) || HAS_TRAIT(src, TRAIT_HAULED))
return FALSE
return TRUE
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index b15dd898e875..7eb52c3a5df7 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -386,7 +386,7 @@
if(ishuman(mob))
squad = mob.assigned_squad
if(!check_improved_pointing()) //Squad Leaders and above have reduced cooldown and get a bigger arrow
- recently_pointed_to = world.time + 50
+ recently_pointed_to = world.time + 2.5 SECONDS
new /obj/effect/overlay/temp/point(T, src, A)
else
recently_pointed_to = world.time + 10
diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm
index 993b4ccfed0f..a768d17f8f9d 100644
--- a/code/modules/mob/mob_grab.dm
+++ b/code/modules/mob/mob_grab.dm
@@ -118,7 +118,7 @@
if(!istype(pulled))
return
if(isxeno(pulled) || issynth(pulled))
- to_chat(xeno, SPAN_WARNING("That wouldn't taste very good."))
+ to_chat(xeno, SPAN_WARNING("That wouldn't serve a purpose."))
return 0
if(pulled.buckled)
to_chat(xeno, SPAN_WARNING("[pulled] is buckled to something."))
@@ -126,45 +126,34 @@
if(pulled.stat == DEAD && !pulled.chestburst)
to_chat(xeno, SPAN_WARNING("Ew, [pulled] is already starting to rot."))
return 0
- if(length(xeno.stomach_contents)) //Only one thing in the stomach at a time, please
- to_chat(xeno, SPAN_WARNING("We already have something in our stomach, there's no way that will fit."))
+ if(xeno.hauled_mob?.resolve()) // We can't carry more than one mob
+ to_chat(xeno, SPAN_WARNING("You already are carrying something, there's no way that will work."))
return 0
- /* Saving this in case we want to allow devouring of dead bodies UNLESS their client is still online somewhere
+ if(HAS_TRAIT(pulled, TRAIT_HAULED))
+ to_chat(xeno, SPAN_WARNING("They are already being hauled by someone else."))
+ return 0
+ /* Saving this in case we want to allow hauling of dead bodies UNLESS their client is still online somewhere
if(pulled.client) //The client is still inside the body
else // The client is observing
for(var/mob/dead/observer/G in player_list)
if(ckey(G.mind.original.ckey) == pulled.ckey)
- to_chat(src, "You start to devour [pulled] but realize \he is already dead.")
+ to_chat(src, "You start to haul [pulled] but realize \he is already dead.")
return */
if(user.action_busy)
to_chat(xeno, SPAN_WARNING("We are already busy with something."))
return
SEND_SIGNAL(xeno, COMSIG_MOB_EFFECT_CLOAK_CANCEL)
- xeno.visible_message(SPAN_DANGER("[xeno] starts to devour [pulled]!"),
- SPAN_DANGER("We start to devour [pulled]!"), null, 5)
+ xeno.visible_message(SPAN_DANGER("[xeno] starts to restrain [pulled]!"),
+ SPAN_DANGER("We start restraining [pulled]!"), null, 5)
if(HAS_TRAIT(xeno, TRAIT_CLOAKED)) //cloaked don't show the visible message, so we gotta work around
- to_chat(pulled, FONT_SIZE_HUGE(SPAN_DANGER("[xeno] is trying to devour you!")))
+ to_chat(pulled, FONT_SIZE_HUGE(SPAN_DANGER("[xeno] is trying to restrain you!")))
if(do_after(xeno, 50, INTERRUPT_NO_NEEDHAND, BUSY_ICON_HOSTILE))
- if(isxeno(pulled.loc) && !length(xeno.stomach_contents))
- to_chat(xeno, SPAN_WARNING("Someone already ate \the [pulled]."))
+ if((isxeno(pulled.loc) && !xeno.hauled_mob) || HAS_TRAIT(pulled, TRAIT_HAULED))
+ to_chat(xeno, SPAN_WARNING("Someone already took \the [pulled]."))
return 0
- if(xeno.pulling == pulled && !pulled.buckled && (pulled.stat != DEAD || pulled.chestburst) && !length(xeno.stomach_contents)) //make sure you've still got them in your claws, and alive
- if(SEND_SIGNAL(pulled, COMSIG_MOB_DEVOURED, xeno) & COMPONENT_CANCEL_DEVOUR)
+ if(xeno.pulling == pulled && !pulled.buckled && (pulled.stat != DEAD || pulled.chestburst) && !xeno.hauled_mob?.resolve()) //make sure you've still got them in your claws, and alive
+ if(SEND_SIGNAL(pulled, COMSIG_MOB_HAULED, xeno) & COMPONENT_CANCEL_HAUL)
return FALSE
+ xeno.haul(pulled)
+ xeno.stop_pulling()
- xeno.visible_message(SPAN_WARNING("[xeno] devours [pulled]!"),
- SPAN_WARNING("We devour [pulled]!"), null, 5)
- log_interact(xeno, pulled, "[key_name(xeno)] devoured [key_name(pulled)] at [get_area_name(xeno)]")
-
- if(ishuman(pulled))
- var/mob/living/carbon/human/pulled_human = pulled
- pulled_human.disable_lights()
-
- //Then, we place the mob where it ought to be
- xeno.stomach_contents.Add(pulled)
- xeno.devour_timer = world.time + 500 + rand(0,200) // 50-70 seconds
- pulled.forceMove(xeno)
- return TRUE
- if(!(pulled in xeno.stomach_contents))
- to_chat(xeno, SPAN_WARNING("We stop devouring [pulled]. They probably tasted gross anyways."))
- return 0
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 5ca6841bd78a..e6d968ed13b3 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -96,6 +96,12 @@
if(isliving(mob))
living_mob = mob
+ if(ishuman(living_mob)) // Might as well just do it here than set movement delay to 0
+ var/mob/living/carbon/human/human = living_mob
+ if(HAS_TRAIT(human, TRAIT_HAULED))
+ human.handle_haul_resist()
+ return
+
if(world.time < next_movement)
return
if(living_mob && living_mob.body_position == LYING_DOWN && mob.crawling)
diff --git a/code/modules/movement/launching/launching.dm b/code/modules/movement/launching/launching.dm
index bb32a8e14ba3..c598482f3227 100644
--- a/code/modules/movement/launching/launching.dm
+++ b/code/modules/movement/launching/launching.dm
@@ -130,7 +130,7 @@
return TRUE
// Proc for throwing items (should only really be used for throw)
-/atom/movable/proc/throw_atom(atom/target, range, speed = 0, atom/thrower, spin, launch_type = NORMAL_LAUNCH, pass_flags = NO_FLAGS, list/end_throw_callbacks, list/collision_callbacks)
+/atom/movable/proc/throw_atom(atom/target, range, speed = 0, atom/thrower, spin, launch_type = NORMAL_LAUNCH, pass_flags = NO_FLAGS, list/end_throw_callbacks, list/collision_callbacks, tracking = FALSE)
var/temp_pass_flags = pass_flags
switch (launch_type)
if (NORMAL_LAUNCH)
@@ -155,7 +155,7 @@
flags_atom |= NO_ZFALL
- launch_towards(LM)
+ launch_towards(LM, tracking)
flags_atom &= ~NO_ZFALL
var/turf/end_turf = get_turf(src)
@@ -164,7 +164,7 @@
// Proc for throwing or propelling movable atoms towards a target
-/atom/movable/proc/launch_towards(datum/launch_metadata/LM)
+/atom/movable/proc/launch_towards(datum/launch_metadata/LM, tracking = FALSE)
if (!istype(LM))
CRASH("invalid launch_metadata passed to launch_towards")
if (!LM.target || !src)
@@ -222,6 +222,8 @@
if(A == LM.target)
hit_atom = A
break
+ if(!hit_atom && tracking && get_dist(src, LM.target) <= 1 && get_dist(start_turf, LM.target) <= 1) // If we missed, but we are tracking and the target is still next to us and the turf we launched from, then we still count it as a hit
+ hit_atom = LM.target
launch_impact(hit_atom)
if (loc)
throwing = FALSE
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 316cd795980d..d5bb62715438 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -577,7 +577,7 @@
/obj/item/paper/prison_station/test_log
name = "paper- 'Test Log'"
- info = "
TEST LOG
SPECIMEN: Bioweapon candidate Kappa. Individual 3
\n
-
PROCEDURE: Observation
RESULTS: Specimen paces around cell. Appears agitated. Vocalisations.
-
PROCEDURE: Simian test subject
RESULTS: Devoured by specimen. No significant difference from last simian test.
Note: Time to amp it up
-
PROCEDURE: Human test subject (D-1). Instructed to \"pet it like a dog\"
RESULTS: Specimen and D-1 stare at each other for approximately two seconds. D-1 screams and begins pounding on observation window, begging to be released. Specimen pounces on D-1. Specimen kills D-1 with multiple slashes from its foreclaws.
Note: Promising!
-
PROCEDURE: Two human test subjects (D-2, D-3). Instructed to subdue specimen
RESULTS: D-2 and D-3 slowly approach specimen. D-3 punches specimen on forehead to no noticeable effect. Specimen pounces on D-3, then kills him with multiple slashes from its foreclaws. D-2 screams and begins pounding on observation window. Specimen pounces on D-2, then kills him with multiple slashes from its foreclaws.
Specimen begins slashing at observation access doors. Exhibiting an unexpected amount of strength, it is able to d~
"
+ info = "
TEST LOG
SPECIMEN: Bioweapon candidate Kappa. Individual 3
\n
-
PROCEDURE: Observation
RESULTS: Specimen paces around cell. Appears agitated. Vocalisations.
-
PROCEDURE: Simian test subject
RESULTS: Nested by specimen. No significant difference from last simian test.
Note: Time to amp it up
-
PROCEDURE: Human test subject (D-1). Instructed to \"pet it like a dog\"
RESULTS: Specimen and D-1 stare at each other for approximately two seconds. D-1 screams and begins pounding on observation window, begging to be released. Specimen pounces on D-1. Specimen kills D-1 with multiple slashes from its foreclaws.
Note: Promising!
-
PROCEDURE: Two human test subjects (D-2, D-3). Instructed to subdue specimen
RESULTS: D-2 and D-3 slowly approach specimen. D-3 punches specimen on forehead to no noticeable effect. Specimen pounces on D-3, then kills him with multiple slashes from its foreclaws. D-2 screams and begins pounding on observation window. Specimen pounces on D-2, then kills him with multiple slashes from its foreclaws.
Specimen begins slashing at observation access doors. Exhibiting an unexpected amount of strength, it is able to d~
"
dat += "A PR301b Delayed Action Order (DAO) allows Military Police to reprosecute a defendant for charges they are previously released from due to the inability to adhere to the required timeframes. "
dat += "Request of a DAO requires the defendant be released from detainment, the 'PR301b Requested' section on this document completed, and a PR301b form sent by fax to the Provost Office. "
- dat += "Upon receipt of a response from the DOA fax this document must be updated to indicate the Provost Office's decision. Should a DAO be approved the defendant must be presented with the response from the Provost Office upon, or prior to, being detained."
+ dat += "Upon receipt of a response from the DAO fax this document must be updated to indicate the Provost Office's decision. Should a DAO be approved the defendant must be presented with the response from the Provost Office upon, or prior to, being detained."
dat += "
"
dat += " "
dat += " "
diff --git a/code/modules/paperwork/prefab_papers/wey_yu/liaison/liability.dm b/code/modules/paperwork/prefab_papers/wey_yu/liaison/liability.dm
index 187d0b1b4cf8..95daec5014af 100644
--- a/code/modules/paperwork/prefab_papers/wey_yu/liaison/liability.dm
+++ b/code/modules/paperwork/prefab_papers/wey_yu/liaison/liability.dm
@@ -120,7 +120,7 @@
dat += ""
dat += ""
dat += "
"
- dat += "
I, , forfeit all right to bring a suit against the Weyland Yutani (Space) Corporation for any reason on the site. This agreement releases the Weyland Yutani (Space) Corporation from all liability relating to injuries and financial responsibilities for injuries that may occur on the site.
"
+ dat += "
I, , forfeit all right to bring a suit against the Weyland Yutani Corporation for any reason on the site. This agreement releases the Weyland Yutani Corporation from all liability relating to injuries and financial responsibilities for injuries that may occur on the site.
"
- dat += "This Non-disclosure Agreement is made effective as of [time2text(REALTIMEOFDAY, "Day DD Month [GLOB.game_year]")] by and between The Company and the United States Colonial Marines (the \"recipient\") of property and information regarding all operational procedures of ."
+ dat += "This Non-disclosure Agreement is made effective as of [time2text(REALTIMEOFDAY, "Day DD Month [GLOB.game_year]")] by and between The Company and [preset_recipient] (the \"recipient\") of property and information regarding all operational procedures of ."
dat += ""
dat += "
I . Confidential Information. The term \"Confidential Information\" means by any information which is proprietary to the Owner, whether or not owned or developed by the Owner, which is generally known other than by the Owner, which the Recipient may obtain through any direct or indirect contact with the owner, regardless of whether specifically identified as confidential or proprietary, Confidential Information shall include any information provided by the Owner concerning the business, technology and information of the Owner and any third party with which the owner deals, including, without limitation, business records and plans, trade secrets, technical data, product are ideas, contracts, financial information, pricing structure, health discounts, computer programs, listings and unknown wildlife, are all copyright and intellectual property. The nature of the information and the manner of disclosure are such that a reasonable person would understand is confidential.
"
dat += ""
@@ -152,9 +156,9 @@
dat += ""
dat += "
X . TERM. The obligations of this agreement shall survive 03/30/2293 from the Effective date or until the Owner sends the Recipient written notice releasing the Recipient from this Agreement. After that, the Recipient must continue to protect the confidential information that was received during the term of this agreement from unauthorized use or disclosure for a additional time of the Owners Choosing.
"
dat += ""
- dat += "
XI. Signatories. This Agreement shall be executed by the named representative, on behalf of The Company and USS Almayer's Commander, on behalf of United States Colonial Marines and delivered in the manner prescribed by law as of the date first written above.
"
+ dat += "
XI. Signatories. This Agreement shall be executed by the named corporate representative, on behalf of The Company, and the recipient's named representative, and delivered in the manner prescribed by law as of the date first written above.
"
dat += "
A. The Company. Date: [time2text(REALTIMEOFDAY, "Day DD Month [GLOB.game_year]")] Representative: Title: Signature:
B. The Recipient. Date: [time2text(REALTIMEOFDAY, "Day DD Month [GLOB.game_year]")] Representative: Rank: Signature:
"
dat += "
"
dat += ""
dat += "
"
@@ -169,3 +173,7 @@
contents = dat
+
+/datum/prefab_document/wey_yu/liaison/nda_long/uscm
+ preset_recipient = "the United States Colonial Marines"
+ doc_code = "WY442-B"
diff --git a/code/modules/paperwork/prefab_papers/wey_yu/liaison/nda_short.dm b/code/modules/paperwork/prefab_papers/wey_yu/liaison/nda_short.dm
index 32f10f303361..d71a28a2f874 100644
--- a/code/modules/paperwork/prefab_papers/wey_yu/liaison/nda_short.dm
+++ b/code/modules/paperwork/prefab_papers/wey_yu/liaison/nda_short.dm
@@ -120,7 +120,7 @@
dat += ""
dat += ""
dat += "
"
- dat += "
I, , agree to a Confidentiality Agreement with the Weyland-Yutani (Space) Corporation that no information regarding the events that took place on the facility named and subsequently the USS Almayer is to be delivered publicly. Only qualified Corporate personnel whose identities have been confirmed will receive such information.
"
+ dat += "
I, , agree to a Confidentiality Agreement with the Weyland-Yutani Corporation that no information regarding the events that took place on the facility named or any directly supporting facility in the immediate region is to be delivered publicly. Only qualified Corporate personnel whose identities have been confirmed will receive such information.
"
dat += "
"
dat += "Signature: "
dat += "Liaison Signature: "
diff --git a/code/modules/paperwork/prefab_papers/wey_yu/liaison/ops_report.dm b/code/modules/paperwork/prefab_papers/wey_yu/liaison/ops_report.dm
index f8419d468267..857ecbcb96a7 100644
--- a/code/modules/paperwork/prefab_papers/wey_yu/liaison/ops_report.dm
+++ b/code/modules/paperwork/prefab_papers/wey_yu/liaison/ops_report.dm
@@ -111,7 +111,7 @@
dat += ""
dat += ""
dat += "
"
- dat += "
Liaison Operations Report
"
+ dat += "
Local Operations Report
"
dat += "
WY435
"
dat += ""
dat += "
"
diff --git a/code/modules/paperwork/prefab_papers/wey_yu/liaison/preserve_intent.dm b/code/modules/paperwork/prefab_papers/wey_yu/liaison/preserve_intent.dm
index 81af52075b77..cfcc4f3d6472 100644
--- a/code/modules/paperwork/prefab_papers/wey_yu/liaison/preserve_intent.dm
+++ b/code/modules/paperwork/prefab_papers/wey_yu/liaison/preserve_intent.dm
@@ -120,7 +120,7 @@
dat += ""
dat += ""
dat += "
"
- dat += "
I, , do hereby swear and affirm that they I will do everything within my power to preserve and protect the integrity and value of all proprietary interests of the Weyland Yutani (Space) Corporation on the surface of or above the surface of .
"
+ dat += "
I, , do hereby swear and affirm that they I will do everything within my power to preserve and protect the integrity and value of all proprietary interests of the Weyland Yutani Corporation on, or above, the surface of .