diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm
index 9702d51004ad..6844721cd7f0 100644
--- a/code/__DEFINES/admin.dm
+++ b/code/__DEFINES/admin.dm
@@ -71,3 +71,6 @@ var/global/list/note_categories = list("Admin", "Merit", "Commanding Officer", "
#define ANTIGRIEF_NEW_PLAYERS 1
/// Enables antigrief entirely: Nobody can activate explosives on the Almayer, unless the ship crashed.
#define ANTIGRIEF_ENABLED 2
+
+/// Proc has been blocked by IsAdminAdvancedProcCall()
+#define PROC_BLOCKED "PROCCALL BLOCKED"
diff --git a/code/__DEFINES/client_prefs.dm b/code/__DEFINES/client_prefs.dm
index ef5ff00e4ed6..b1e194354555 100644
--- a/code/__DEFINES/client_prefs.dm
+++ b/code/__DEFINES/client_prefs.dm
@@ -22,6 +22,7 @@
#define TOGGLE_VEND_ITEM_TO_HAND (1<<15) // This toggles whether items from vendors will be automatically put into your hand.
#define TOGGLE_START_JOIN_CURRENT_SLOT (1<<16) // Whether joining at roundstart ignores assigned character slot for the job and uses currently selected slot.
#define TOGGLE_LATE_JOIN_CURRENT_SLOT (1<<17) //Whether joining during the round ignores assigned character slot for the job and uses currently selected slot.
+#define TOGGLE_ABILITY_DEACTIVATION_OFF (1<<18) // This toggles whether selecting the same ability again can toggle it off
#define JOB_SLOT_RANDOMISED_SLOT -1
#define JOB_SLOT_CURRENT_SLOT 0
diff --git a/code/__DEFINES/conflict.dm b/code/__DEFINES/conflict.dm
index 141d69c69b11..fc8ca4e03a9f 100644
--- a/code/__DEFINES/conflict.dm
+++ b/code/__DEFINES/conflict.dm
@@ -200,6 +200,7 @@
// human armor
#define CLOTHING_ARMOR_NONE 0
+#define CLOTHING_ARMOR_VERYLOW 5
#define CLOTHING_ARMOR_LOW 10
#define CLOTHING_ARMOR_MEDIUMLOW 15
#define CLOTHING_ARMOR_MEDIUM 20
diff --git a/code/__DEFINES/dcs/signals/atom/signals_obj.dm b/code/__DEFINES/dcs/signals/atom/signals_obj.dm
index df53558834f6..aebd0d09d0d2 100644
--- a/code/__DEFINES/dcs/signals/atom/signals_obj.dm
+++ b/code/__DEFINES/dcs/signals/atom/signals_obj.dm
@@ -4,6 +4,8 @@
/// From /obj/effect/alien/weeds/Initialize()
#define COMSIG_WEEDNODE_GROWTH_COMPLETE "weednode_growth_complete"
+/// From /obj/effect/alien/weeds/Initialize()
+#define COMSIG_WEEDNODE_GROWTH "weednode_growth"
/// From /obj/effect/alien/weeds/proc/on_weed_expand()
#define COMSIG_WEEDNODE_CANNOT_EXPAND_FURTHER "weednode_cannot_expand_further"
@@ -24,3 +26,6 @@
#define COMSIG_TRANSMITTER_UPDATE_ICON "transmitter_update_icon"
#define COMSIG_TENT_COLLAPSING "tent_collapsing"
+
+/// from /obj/proc/afterbuckle()
+#define COSMIG_OBJ_AFTER_BUCKLE "signal_obj_after_buckle"
diff --git a/code/__DEFINES/dcs/signals/signals_datum.dm b/code/__DEFINES/dcs/signals/signals_datum.dm
index a7a93c4ea114..7696d8ad6037 100644
--- a/code/__DEFINES/dcs/signals/signals_datum.dm
+++ b/code/__DEFINES/dcs/signals/signals_datum.dm
@@ -61,3 +61,6 @@
// from /datum/emergency_call/proc/spawn_candidates()
#define COMSIG_ERT_SETUP "ert_setup"
+
+// from /proc/update_living_queens() : /mob/living/carbon/xenomorph/queen
+#define COMSIG_HIVE_NEW_QUEEN "hive_new_queen"
diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm
index 90a50fad138c..9781f61ec95c 100644
--- a/code/__DEFINES/dcs/signals/signals_global.dm
+++ b/code/__DEFINES/dcs/signals/signals_global.dm
@@ -53,3 +53,6 @@
/// From /datum/admins/proc/force_predator_round()
#define COMSIG_GLOB_PREDATOR_ROUND_TOGGLED "!predator_round_toglged"
+
+/// From /datum/game_mode/colonialmarines/proc/check_ground_humans()
+#define COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING "!groundside_forsaken_handling"
diff --git a/code/__DEFINES/sounds.dm b/code/__DEFINES/sounds.dm
index 51279e0ca0cd..f01ddfc86792 100644
--- a/code/__DEFINES/sounds.dm
+++ b/code/__DEFINES/sounds.dm
@@ -71,6 +71,7 @@
#define AMBIENCE_ALMAYER 'sound/ambience/almayerambience.ogg'
#define AMBIENCE_LV624 'sound/ambience/ambienceLV624.ogg'
#define AMBIENCE_BIGRED 'sound/ambience/desert.ogg'
+#define AMBIENCE_NV 'sound/ambience/ambienceNV.ogg'
#define AMBIENCE_PRISON 'sound/ambience/shipambience.ogg'
#define AMBIENCE_TRIJENT 'sound/ambience/desert.ogg'
diff --git a/code/__DEFINES/speech_channels.dm b/code/__DEFINES/speech_channels.dm
index 405506678407..3f6e4720bde9 100644
--- a/code/__DEFINES/speech_channels.dm
+++ b/code/__DEFINES/speech_channels.dm
@@ -4,6 +4,5 @@
#define ME_CHANNEL "Me"
#define OOC_CHANNEL "OOC"
#define LOOC_CHANNEL "LOOC"
-#define MOD_CHANNEL "MSAY"
#define ADMIN_CHANNEL "ASAY"
#define MENTOR_CHANNEL "Mentor"
diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm
index 89976c498422..22c3827022ff 100644
--- a/code/__DEFINES/tgs.dm
+++ b/code/__DEFINES/tgs.dm
@@ -1,6 +1,6 @@
// tgstation-server DMAPI
-#define TGS_DMAPI_VERSION "6.5.0"
+#define TGS_DMAPI_VERSION "6.5.2"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
diff --git a/code/__DEFINES/tgui.dm b/code/__DEFINES/tgui.dm
index 865088ee72fc..ca6408961eab 100644
--- a/code/__DEFINES/tgui.dm
+++ b/code/__DEFINES/tgui.dm
@@ -32,7 +32,7 @@
/// Creates a message packet for sending via output()
// This is {"type":type,"payload":payload}, but pre-encoded. This is much faster
// than doing it the normal way.
-// To ensure this is correct, this is unit tested in tgui_create_message. However, CM does not have unit tests available.
+// To ensure this is correct, this is unit tested in tgui_create_message.
#define TGUI_CREATE_MESSAGE(type, payload) ( \
"%7b%22type%22%3a%22[type]%22%2c%22payload%22%3a[url_encode(json_encode(payload))]%7d" \
)
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index dd700ccde996..288604434e34 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -123,7 +123,7 @@
#define TRAIT_SUPER_STRONG "t_super_strong"
/// Foreign biology. Basic medHUDs won't show the mob. (Yautja, Zombies)
#define TRAIT_FOREIGN_BIO "t_foreign_bio"
-/// Eye color changes on intent. (G1 Synths)
+/// Eye color changes on intent. (G1 Synths and WJs)
#define TRAIT_INTENT_EYES "t_intent_eyes"
/// Masked synthetic biology. Basic medHUDs will percieve the mob as human. (Infiltrator Synths)
#define TRAIT_INFILTRATOR_SYNTH "t_infiltrator_synth"
@@ -157,6 +157,8 @@
#define TRAIT_LEADERSHIP "t_leadership"
/// If the mob can see the reagents contents of stuff
#define TRAIT_REAGENT_SCANNER "reagent_scanner"
+/// If the mob cannot eat/be fed
+#define TRAIT_CANNOT_EAT "t_cannot_eat"
/// If the mob is being lazed by a sniper spotter
#define TRAIT_SPOTTER_LAZED "t_spotter_lazed"
/// If the mob has ear protection. Protects from external ear damage effects. Includes explosions, firing the RPG, screeching DEAFNESS only, and flashbangs.
@@ -179,6 +181,8 @@
#define TRAIT_ABILITY_NO_PLASMA_TRANSFER "t_ability_no_plasma_transfer"
/// Shows that the xeno queen is on ovi
#define TRAIT_ABILITY_OVIPOSITOR "t_ability_ovipositor"
+/// Used for burrowed mobs, prevent's SG/sentrys/claymores from autofiring
+#define TRAIT_ABILITY_BURROWED "t_ability_burrowed"
//-- item traits --
// TOOL TRAITS
@@ -231,7 +235,8 @@ GLOBAL_LIST_INIT(mob_traits, list(
TRAIT_TWOBORE_TRAINING,
TRAIT_LEADERSHIP,
TRAIT_DEXTROUS,
- TRAIT_REAGENT_SCANNER
+ TRAIT_REAGENT_SCANNER,
+ TRAIT_ABILITY_BURROWED
))
/*
@@ -261,6 +266,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_BIMEX" = TRAIT_BIMEX,
"TRAIT_EMOTE_CD_EXEMPT" = TRAIT_EMOTE_CD_EXEMPT,
"TRAIT_LISPING" = TRAIT_LISPING,
+ "TRAIT_CANNOT_EAT" = TRAIT_CANNOT_EAT,
),
/mob/living/carbon/xenomorph = list(
"TRAIT_ABILITY_NO_PLASMA_TRANSFER" = TRAIT_ABILITY_NO_PLASMA_TRANSFER,
diff --git a/code/__DEFINES/weather.dm b/code/__DEFINES/weather.dm
index 16ee8154241f..c67575f80720 100644
--- a/code/__DEFINES/weather.dm
+++ b/code/__DEFINES/weather.dm
@@ -5,6 +5,7 @@
#define PROB_WEATHER_SOROKYNE 100 //Map specific defines go here.
#define PROB_WEATHER_BIG_RED 30
#define PROB_WEATHER_LV624 30
+#define PROB_WEATHER_NEW_VARADERO 100
#define WEATHER_TYPE_NO_WEATHER 0
#define WEATHER_TYPE_SNOW 1
diff --git a/code/__DEFINES/wj_emotes.dm b/code/__DEFINES/wj_emotes.dm
new file mode 100644
index 000000000000..f315c6eb2ba5
--- /dev/null
+++ b/code/__DEFINES/wj_emotes.dm
@@ -0,0 +1,8 @@
+#define JOE_EMOTE_CATEGORY_GREETING "Greeting"
+#define JOE_EMOTE_CATEGORY_TASK_UPDATE "Task Update"
+#define JOE_EMOTE_CATEGORY_RESTRICTED_AREA "Restricted Area"
+#define JOE_EMOTE_CATEGORY_FAREWELL "Farewell"
+#define JOE_EMOTE_CATEGORY_QUIP "Quip"
+#define JOE_EMOTE_CATEGORY_WARNING "Warning"
+#define JOE_EMOTE_CATEGORY_QUESTION "Question"
+#define JOE_EMOTE_CATEGORY_NOTICE "Notice"
diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno.dm
index 7741beecedef..ac783b6f426e 100644
--- a/code/__DEFINES/xeno.dm
+++ b/code/__DEFINES/xeno.dm
@@ -132,6 +132,7 @@
// Weed defines
#define WEED_LEVEL_WEAK 0
#define WEED_LEVEL_STANDARD 1.5
+#define WEED_LEVEL_HARDY 1.6
#define WEED_LEVEL_HIVE 4
#define WEED_RANGE_STANDARD 3
@@ -167,6 +168,8 @@
#define XENO_LEAVE_TIMER_LARVA 80 //80 seconds
/// The time against away_timer when an AFK xeno (not larva) can be replaced
#define XENO_LEAVE_TIMER 300 //300 seconds
+/// The time against away_timer when an AFK facehugger converts to a npc
+#define XENO_FACEHUGGER_LEAVE_TIMER 420 //420 seconds
/// The time against away_timer when an AFK xeno gets listed in the available list so ghosts can get ready
#define XENO_AVAILABLE_TIMER 60 //60 seconds
diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index 888e3c2ce657..ff8e31ad3e8a 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -65,4 +65,4 @@ var/atom/cmp_dist_origin=null
/// Compares observers based on their larva_queue_time value in ascending order
/// Assumes the client on the observer is not null
/proc/cmp_obs_larvaqueuetime_asc(mob/dead/observer/A, mob/dead/observer/B)
- return A.client.larva_queue_time - B.client.larva_queue_time
+ return A.client.player_details.larva_queue_time - B.client.player_details.larva_queue_time
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 61976f4dc903..cca3edda464e 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -241,8 +241,14 @@
else
return get_step(start, EAST)
-/// Get a list of observers that can be alien candidates, optionally sorted by larva_queue_time
-/proc/get_alien_candidates(sorted = TRUE)
+/**
+ * Get a list of observers that can be alien candidates.
+ *
+ * Arguments:
+ * * hive - The hive we're filling a slot for to check if the player is banished
+ * * sorted - Whether to sort by larva_queue_time (default TRUE) or leave unsorted
+ */
+/proc/get_alien_candidates(datum/hive_status/hive = null, sorted = TRUE)
var/list/candidates = list()
for(var/mob/dead/observer/cur_obs as anything in GLOB.observer_list)
@@ -273,6 +279,15 @@
if((cur_obs.client.admin_holder && (cur_obs.client.admin_holder.rights & R_MOD)) && !cur_obs.adminlarva)
continue
+ if(hive)
+ var/banished = FALSE
+ for(var/mob_name in hive.banished_ckeys)
+ if(hive.banished_ckeys[mob_name] == cur_obs.ckey)
+ banished = TRUE
+ break
+ if(banished)
+ continue
+
candidates += cur_obs
// Optionally sort by larva_queue_time
@@ -290,21 +305,16 @@
* * cache_only - Whether to not actually send a to_chat message and instead only update larva_queue_cached_message
*/
/proc/message_alien_candidates(list/candidates, dequeued, cache_only = FALSE)
- var/new_players = 0
for(var/i in (1 + dequeued) to candidates.len)
var/mob/dead/observer/cur_obs = candidates[i]
// Generate the messages
- var/cached_message = SPAN_XENONOTICE("You are currently [i-dequeued]\th in the larva queue. There are [new_players] ahead of you that have yet to play this round.")
+ var/cached_message = SPAN_XENONOTICE("You are currently [i-dequeued]\th in the larva queue.")
cur_obs.larva_queue_cached_message = cached_message
if(!cache_only)
var/chat_message = dequeued ? replacetext(cached_message, "currently", "now") : cached_message
to_chat(candidates[i], chat_message)
- // Count how many are prioritized
- if(cur_obs.client.larva_queue_time < 2) // 0 and 1 because facehuggers/t-domers are slightly deprioritized
- new_players++
-
/proc/convert_k2c(temp)
return ((temp - T0C))
diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm
index 960f894d676f..4e4a1b3ff31c 100644
--- a/code/__HELPERS/type2type.dm
+++ b/code/__HELPERS/type2type.dm
@@ -226,7 +226,6 @@
if(rights & R_POSSESS) . += "[seperator]+POSSESS"
if(rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS"
if(rights & R_STEALTH) . += "[seperator]+STEALTH"
- if(rights & R_REJUVINATE) . += "[seperator]+REJUVINATE"
if(rights & R_COLOR) . += "[seperator]+COLOR"
if(rights & R_VAREDIT) . += "[seperator]+VAREDIT"
if(rights & R_SOUNDS) . += "[seperator]+SOUND"
diff --git a/code/__HELPERS/verb_helpers.dm b/code/__HELPERS/verb_helpers.dm
index d042929f1184..4bc3ae584cd6 100644
--- a/code/__HELPERS/verb_helpers.dm
+++ b/code/__HELPERS/verb_helpers.dm
@@ -10,7 +10,7 @@
if(!target)
CRASH("add_verb called without a target")
if(IsAdminAdvancedProcCall())
- return
+ return PROC_BLOCKED
var/mob/mob_target = null
if(ismob(target))
@@ -56,7 +56,7 @@
*/
/proc/remove_verb(client/target, verb_or_list_to_remove)
if(IsAdminAdvancedProcCall())
- return
+ return PROC_BLOCKED
var/mob/mob_target = null
if(ismob(target))
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 121d1e305e43..09b55accf16d 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -25,7 +25,6 @@ DEFINE_BITFIELD(rights, list(
"POSSESS" = R_POSSESS,
"PERMISSIONS" = R_PERMISSIONS,
"STEALTH" = R_STEALTH,
- "REJUVENATE" = R_REJUVINATE,
"COLOR" = R_COLOR,
"VAREDIT" = R_VAREDIT,
"SOUNDS" = R_SOUNDS,
diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm
index 86d5a11a668e..08de7b5c02ff 100644
--- a/code/controllers/configuration/configuration.dm
+++ b/code/controllers/configuration/configuration.dm
@@ -24,7 +24,8 @@
/datum/controller/configuration/proc/admin_reload()
if(IsAdminAdvancedProcCall())
- return
+ alert_proccall("configuration admin_reload")
+ return PROC_BLOCKED
log_admin("[key_name(usr)] has forcefully reloaded the configuration from disk.")
message_admins("[key_name_admin(usr)] has forcefully reloaded the configuration from disk.")
full_wipe()
@@ -33,7 +34,8 @@
/datum/controller/configuration/proc/Load(_directory)
if(IsAdminAdvancedProcCall()) //If admin proccall is detected down the line it will horribly break everything.
- return
+ alert_proccall("configuration Load")
+ return PROC_BLOCKED
if(_directory)
directory = _directory
if(entries)
@@ -117,7 +119,8 @@
/datum/controller/configuration/proc/full_wipe()
if(IsAdminAdvancedProcCall())
- return
+ alert_proccall("configuration full_wipe")
+ return PROC_BLOCKED
entries_by_type.Cut()
QDEL_LIST_ASSOC_VAL(entries)
entries = null
@@ -163,7 +166,8 @@
/datum/controller/configuration/proc/LoadEntries(filename, list/stack = list())
if(IsAdminAdvancedProcCall())
- return
+ alert_proccall("configuration LoadEntries")
+ return PROC_BLOCKED
var/filename_to_test = world.system_type == MS_WINDOWS ? lowertext(filename) : filename
if(filename_to_test in stack)
@@ -264,7 +268,7 @@
CRASH("Missing config entry for [entry_type]!")
if((E.protection & CONFIG_ENTRY_HIDDEN) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]")
- return
+ return PROC_BLOCKED
return E.config_entry_value
@@ -278,7 +282,7 @@
CRASH("Missing config entry for [entry_type]!")
if((E.protection & CONFIG_ENTRY_LOCKED) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Set" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
log_admin_private("Config rewrite of [entry_type] to [new_val] attempted by [key_name(usr)]")
- return
+ return PROC_BLOCKED
return E.ValidateAndSet("[new_val]")
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index fdf79db138f7..743f9be9fec0 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -122,3 +122,8 @@
/datum/config_entry/number/extra_larva_per_burst
config_entry_value = 1
integer = FALSE
+
+/datum/config_entry/number/embryo_burst_timer
+ min_val = 1
+ config_entry_value = 450
+ integer = TRUE
diff --git a/code/controllers/subsystem/x_evolution.dm b/code/controllers/subsystem/x_evolution.dm
index 9f84513e9e2d..be787b37de80 100644
--- a/code/controllers/subsystem/x_evolution.dm
+++ b/code/controllers/subsystem/x_evolution.dm
@@ -3,7 +3,7 @@
#define EVOLUTION_INCREMENT_TIME (30 MINUTES) // Evolution increases by 1 every 25 minutes.
SUBSYSTEM_DEF(xevolution)
- name = "Evilution"
+ name = "Evilution" //This is not a typo, do not change it.
wait = 1 MINUTES
priority = SS_PRIORITY_INACTIVITY
diff --git a/code/datums/components/footstep.dm b/code/datums/components/footstep.dm
index 6deb27a6817b..ef77aaf471dc 100644
--- a/code/datums/components/footstep.dm
+++ b/code/datums/components/footstep.dm
@@ -13,8 +13,9 @@
var/falloff
///This can be a list OR a soundfile OR null. Determines whatever sound gets played.
var/footstep_sounds
+ var/drag_sounds
-/datum/component/footstep/Initialize(steps_ = 2, volume_ = 50, range_ = null, falloff_ = 1, footstep_sounds_ = "alien_footstep_large")
+/datum/component/footstep/Initialize(steps_ = 2, volume_ = 50, range_ = null, falloff_ = 1, footstep_sounds_ = "alien_footstep_large", drag_sounds_ = 'sound/effects/alien_dragsound_large.ogg')
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
steps = steps_
@@ -22,6 +23,7 @@
range = range_
falloff = falloff_
footstep_sounds = footstep_sounds_
+ drag_sounds = drag_sounds_
RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED), PROC_REF(play_simplestep))
@@ -31,7 +33,7 @@
return
var/mob/living/LM = parent
- if(LM.buckled || LM.lying || LM.throwing || LM.is_ventcrawling)
+ if(LM.buckled || LM.throwing || LM.is_ventcrawling || LM.stat == DEAD)
return
if(LM.life_steps_total % steps)
@@ -44,5 +46,8 @@
var/turf/open/T = prepare_step()
if(!T)
return
- if(isfile(footstep_sounds) || istext(footstep_sounds))
+ var/mob/living/parent_mob = parent
+ if(parent_mob.lying && (isfile(drag_sounds) || istext(drag_sounds)))
+ playsound(T, drag_sounds, volume, rand(20000, 25000), range, falloff = falloff)
+ else if(isfile(footstep_sounds) || istext(footstep_sounds))
playsound(T, footstep_sounds, volume, rand(20000, 25000), range, falloff = falloff)
diff --git a/code/datums/components/weed_food.dm b/code/datums/components/weed_food.dm
new file mode 100644
index 000000000000..0c578b661517
--- /dev/null
+++ b/code/datums/components/weed_food.dm
@@ -0,0 +1,297 @@
+#define WEED_FOOD_DELAY 5 MINUTES
+#define WEED_FOOD_STATE_DELAY 1 MINUTES
+
+/atom/movable/vis_obj/weed_food
+ name = "weeds"
+ desc = "Weird black weeds in the shape of a body..."
+ gender = PLURAL
+ vis_flags = VIS_INHERIT_DIR|VIS_INHERIT_PLANE|VIS_INHERIT_LAYER
+ icon = 'icons/mob/xenos/weeds.dmi'
+ var/static/list/icon_states = list("human_1","human_2","human_3","human_4","human_5")
+ var/static/list/icon_states_flipped = list("human_1_f","human_2_f","human_3_f","human_4_f","human_5_f")
+ var/icon_state_idx = 0
+ var/timer_id = null
+ var/flipped = FALSE
+
+/atom/movable/vis_obj/weed_food/Initialize(mapload, is_flipped, ...)
+ flipped = is_flipped
+ timer_id = addtimer(CALLBACK(src, PROC_REF(on_animation_timer)), WEED_FOOD_STATE_DELAY, TIMER_STOPPABLE|TIMER_UNIQUE|TIMER_LOOP|TIMER_DELETE_ME)
+ on_animation_timer()
+ return ..()
+
+/// Timer callback for changing the icon_state
+/atom/movable/vis_obj/weed_food/proc/on_animation_timer()
+ icon_state_idx++
+ if(icon_state_idx > length(icon_states))
+ deltimer(timer_id)
+ timer_id = null
+ return
+ icon_state = flipped ? icon_states_flipped[icon_state_idx] : icon_states[icon_state_idx]
+
+/**
+ * A component that can be attached to a mob/living to be merged with weeds after a delay.
+ * Attempting to attach a new weed_food even if one already exists is equivalent to calling start().
+ *
+ * Attach this to any mob/living that is dead (death or initialized dead) and it should handle the rest.
+ */
+/datum/component/weed_food
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+ /// Whether we are waiting on timer to merge
+ var/active = FALSE
+ /// Whether we are merged with weeds
+ var/merged = FALSE
+ /// The time we were unmerged (just to handle weeds upgrading)
+ var/unmerged_time
+ /// Any active timer for a pending merge
+ var/timer_id = null
+ /// The living mob that we are bound to
+ var/mob/living/parent_mob
+ /// The turf that our parent is on
+ var/turf/parent_turf
+ /// The obj that our parent is buckled to and we have registered a signal
+ var/obj/parent_buckle
+ /// The weeds that we are merging/merged with
+ var/obj/effect/alien/weeds/absorbing_weeds
+ /// The overlay image when merged
+ var/atom/movable/vis_obj/weed_food/weed_appearance
+
+/datum/component/weed_food/Initialize(...)
+ parent_mob = parent
+ //if(!istype(parent_mob))
+ //return COMPONENT_INCOMPATIBLE
+ if(!istype(parent_mob, /mob/living/carbon/human))
+ return COMPONENT_INCOMPATIBLE // TODO: At the moment we only support humans
+
+ parent_turf = get_turf(parent_mob)
+ if(parent_turf != parent_mob.loc)
+ parent_turf = null // if our location is actually a container, we want to be safe from weeds
+
+ start()
+
+/datum/component/weed_food/InheritComponent(datum/component/C, i_am_original)
+ start()
+
+/datum/component/weed_food/Destroy(force, silent)
+ . = ..()
+
+ unmerge_with_weeds()
+ QDEL_NULL(weed_appearance)
+ parent_mob = null
+ parent_turf = null
+
+/datum/component/weed_food/RegisterWithParent()
+ RegisterSignal(parent_mob, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
+ RegisterSignal(parent_mob, list(COMSIG_LIVING_REJUVENATED, COMSIG_HUMAN_REVIVED), PROC_REF(on_rejuv))
+ RegisterSignal(parent_mob, COMSIG_HUMAN_SET_UNDEFIBBABLE, PROC_REF(on_update))
+ if(parent_turf)
+ RegisterSignal(parent_turf, COMSIG_WEEDNODE_GROWTH, PROC_REF(on_update))
+
+/datum/component/weed_food/UnregisterFromParent()
+ if(parent_mob)
+ UnregisterSignal(parent_mob, list(
+ COMSIG_MOVABLE_MOVED,
+ COMSIG_LIVING_REJUVENATED,
+ COMSIG_HUMAN_REVIVED,
+ COMSIG_HUMAN_SET_UNDEFIBBABLE,
+ ))
+ if(absorbing_weeds)
+ UnregisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING)
+ if(parent_turf)
+ UnregisterSignal(parent_turf, COMSIG_WEEDNODE_GROWTH)
+ if(parent_buckle)
+ UnregisterSignal(parent_buckle, COSMIG_OBJ_AFTER_BUCKLE)
+
+/// SIGNAL_HANDLER for COMSIG_MOVABLE_MOVED
+/datum/component/weed_food/proc/on_move()
+ SIGNAL_HANDLER
+
+ if(absorbing_weeds)
+ UnregisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING)
+ absorbing_weeds = null
+
+ if(parent_turf)
+ UnregisterSignal(parent_turf, COMSIG_WEEDNODE_GROWTH)
+ parent_turf = get_turf(parent_mob)
+ if(parent_turf != parent_mob.loc)
+ parent_turf = null // if our location is actually a container, we want to be safe from weeds
+ else
+ RegisterSignal(parent_turf, COMSIG_WEEDNODE_GROWTH, PROC_REF(on_update))
+
+ // We moved, restart or start the proccess
+ if(stop() || !merged)
+ start()
+ return
+
+ // If we somehow moved when we were merged, handle that
+ absorbing_weeds = parent_turf?.weeds
+ if(absorbing_weeds)
+ RegisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_deletion))
+ return
+ unmerge_with_weeds()
+
+/// SIGNAL_HANDLER for COMSIG_LIVING_REJUVENATED and COMSIG_HUMAN_REVIVED
+/datum/component/weed_food/proc/on_rejuv()
+ SIGNAL_HANDLER
+
+ qdel(src)
+
+/// SIGNAL_HANDLER for COSMIG_OBJ_AFTER_BUCKLE
+/datum/component/weed_food/proc/on_after_buckle(obj/source, mob/buckled)
+ SIGNAL_HANDLER
+
+ if(buckled)
+ return
+ start() // We unbuckled, so lets try to start again
+
+/// SIGNAL_HANDLER for COMSIG_HUMAN_SET_UNDEFIBBABLE & COMSIG_WEEDNODE_GROWTH
+/datum/component/weed_food/proc/on_update()
+ SIGNAL_HANDLER
+
+ start()
+
+/// SIGNAL_HANDLER for COMSIG_PARENT_QDELETING of weeds
+/datum/component/weed_food/proc/on_weed_deletion()
+ SIGNAL_HANDLER
+
+ if(active)
+ stop()
+ return
+ if(merged)
+ unmerge_with_weeds()
+ return
+
+/**
+ * Try to start the process to turn into weeds
+ * Returns TRUE if started successfully
+ */
+/datum/component/weed_food/proc/start()
+ if(active)
+ return FALSE
+ if(merged)
+ return FALSE
+ if(QDELETED(parent_mob))
+ return FALSE
+
+ if(parent_mob.buckled)
+ if(parent_mob.buckled == parent_buckle)
+ return FALSE // Still buckled to the same thing
+ if(!istype(parent_mob.buckled, /obj/structure/bed/nest))
+ if(parent_buckle) // Still have a lingering reference somehow?
+ UnregisterSignal(parent_buckle, COSMIG_OBJ_AFTER_BUCKLE)
+ parent_buckle = parent_mob.buckled
+ RegisterSignal(parent_mob.buckled, COSMIG_OBJ_AFTER_BUCKLE, PROC_REF(on_after_buckle))
+ return FALSE
+ if(parent_buckle)
+ UnregisterSignal(parent_buckle, COSMIG_OBJ_AFTER_BUCKLE)
+ parent_buckle = null
+
+ if(parent_mob.is_xeno_grabbable())
+ return FALSE
+ if(!(parent_mob.status_flags & PERMANENTLY_DEAD))
+ var/mob/living/carbon/human/parent_human = parent_mob
+ if(istype(parent_human) && !parent_human.undefibbable)
+ return FALSE
+ if(!parent_turf?.weeds)
+ return FALSE
+
+ if(unmerged_time == world.time)
+ return merge_with_weeds() // Weeds upgraded, re-merge now re-using the apperance
+ QDEL_NULL(weed_appearance)
+ absorbing_weeds = parent_turf.weeds
+ RegisterSignal(parent_turf.weeds, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_deletion))
+
+ active = TRUE
+ timer_id = addtimer(CALLBACK(src, PROC_REF(merge_with_weeds)), WEED_FOOD_DELAY, TIMER_STOPPABLE|TIMER_UNIQUE|TIMER_DELETE_ME|TIMER_OVERRIDE)
+
+ return TRUE
+
+/**
+ * Try to stop the process turning into weeds
+ * Returns TRUE if stopped successfully (was active when called)
+ */
+/datum/component/weed_food/proc/stop()
+ if(!active)
+ return FALSE
+
+ active = FALSE
+ deltimer(timer_id)
+ timer_id = null
+
+ return TRUE
+
+/**
+ * Finish becomming one with the weeds
+ * Returns TRUE if merged successfully
+ */
+/datum/component/weed_food/proc/merge_with_weeds()
+ if(merged)
+ return FALSE
+ if(QDELETED(parent_mob))
+ return FALSE
+
+ if(absorbing_weeds) // Remove the signal that would call stop
+ UnregisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING)
+
+ if(parent_mob.buckled)
+ if(parent_mob.buckled == parent_buckle)
+ return FALSE // Still buckled to the same thing somehow?
+ if(!istype(parent_mob.buckled, /obj/structure/bed/nest))
+ if(parent_buckle) // Still have a lingering reference somehow?
+ UnregisterSignal(parent_buckle, COSMIG_OBJ_AFTER_BUCKLE)
+ parent_buckle = parent_mob.buckled
+ RegisterSignal(parent_mob.buckled, COSMIG_OBJ_AFTER_BUCKLE, PROC_REF(on_after_buckle))
+ return FALSE
+ if(parent_buckle)
+ UnregisterSignal(parent_buckle, COSMIG_OBJ_AFTER_BUCKLE)
+ parent_buckle = null
+
+ absorbing_weeds = parent_turf?.weeds
+ if(!absorbing_weeds)
+ return FALSE
+ RegisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_deletion))
+ // Technically we could have just left the signal alone, but both because of the posibility of other conditions preventing a merge or weeds somehow changing and on_move didn't catch it, this is less fragile
+
+ active = FALSE
+ merged = TRUE
+
+ parent_mob.density = FALSE
+ parent_mob.anchored = TRUE
+ parent_mob.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ parent_mob.plane = FLOOR_PLANE
+ parent_mob.remove_from_all_mob_huds()
+
+ if(!weed_appearance) // Make a new sprite if we aren't re-merging
+ var/is_flipped = parent_mob.transform.b == -1 // Technically we should check if d is 1 too, but corpses can only be rotated 90 or 270 (1/-1 or -1/1)
+ if(parent_mob.dir & WEST)
+ is_flipped = !is_flipped // The direction reversed the effect of the flip!
+ weed_appearance = new(null, is_flipped)
+ weed_appearance.color = absorbing_weeds.color
+ // TODO: For non-humans change the icon_state or something here
+ parent_mob.vis_contents += weed_appearance
+
+ return TRUE
+
+/**
+ * Undo the weedening
+ * Returns TRUE if unmerged successfully (always)
+ */
+/datum/component/weed_food/proc/unmerge_with_weeds()
+ merged = FALSE
+ unmerged_time = world.time
+
+ if(absorbing_weeds)
+ UnregisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING)
+ absorbing_weeds = null
+
+ parent_mob.anchored = FALSE
+ parent_mob.mouse_opacity = MOUSE_OPACITY_ICON
+ parent_mob.plane = GAME_PLANE
+ parent_mob.vis_contents -= weed_appearance
+
+ if(!QDELETED(parent_mob))
+ parent_mob.add_to_all_mob_huds()
+
+ return TRUE
+
+#undef WEED_FOOD_DELAY
+#undef WEED_FOOD_STATE_DELAY
diff --git a/code/datums/emergency_calls/cryo_marines.dm b/code/datums/emergency_calls/cryo_marines.dm
index 9bec8b3593dd..eb914e198b37 100644
--- a/code/datums/emergency_calls/cryo_marines.dm
+++ b/code/datums/emergency_calls/cryo_marines.dm
@@ -3,7 +3,7 @@
//whiskey outpost extra marines
/datum/emergency_call/cryo_squad
name = "Marine Cryo Reinforcements (Squad)"
- mob_max = 15
+ mob_max = 10
mob_min = 1
probability = 0
objectives = "Assist the USCM forces"
diff --git a/code/datums/keybinding/communication.dm b/code/datums/keybinding/communication.dm
index 9a438fc6dabd..4164198d4818 100644
--- a/code/datums/keybinding/communication.dm
+++ b/code/datums/keybinding/communication.dm
@@ -42,13 +42,6 @@
full_name = "IC Comms (;)"
keybind_signal = COMSIG_KG_CLIENT_RADIO_DOWN
-/datum/keybinding/client/communication/mod_say
- hotkey_keys = list("Unbound")
- classic_keys = list("Unbound")
- name = MOD_CHANNEL
- full_name = "Mod Say"
- keybind_signal = COMSIG_KB_ADMIN_ASAY_DOWN
-
/datum/keybinding/client/communication/asay
hotkey_keys = list("F3")
classic_keys = list("F5")
diff --git a/code/datums/redis/callbacks/msay.dm b/code/datums/redis/callbacks/msay.dm
deleted file mode 100644
index 3c0db7638a3a..000000000000
--- a/code/datums/redis/callbacks/msay.dm
+++ /dev/null
@@ -1,16 +0,0 @@
-/datum/redis_callback/msay
- channel = "byond.msay"
-
-/datum/redis_callback/msay/on_message(message)
- var/list/data = json_decode(message)
-
- if(data["source"] == SSredis.instance_name)
- return
-
- var/msg = "[data["rank"]]:[data["author"]]@[data["source"]]: [strip_html(data["message"])]"
-
- for(var/client/client in GLOB.admins)
- if(!(R_MOD & client.admin_holder.rights))
- continue
-
- to_chat(client, msg)
diff --git a/code/datums/skills.dm b/code/datums/skills.dm
index ef86b726a3c1..f37ea3a5a64e 100644
--- a/code/datums/skills.dm
+++ b/code/datums/skills.dm
@@ -319,6 +319,7 @@ CIVILIAN
/datum/skills/civilian/survivor/goon
name = "Survivor Goon"
skills = list(
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_POLICE = SKILL_POLICE_SKILLED,
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
@@ -330,14 +331,40 @@ CIVILIAN
/datum/skills/civilian/survivor/pmc
name = "Survivor PMC"
additional_skills = list(
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_POLICE = SKILL_POLICE_SKILLED,
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
+ SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
+ SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
+ )
+
+/datum/skills/civilian/survivor/pmc/medic
+ name = "Survivor PMC Medic"
+ additional_skills = list(
+ SKILL_POLICE = SKILL_POLICE_SKILLED,
+ SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
+ SKILL_MEDICAL = SKILL_MEDICAL_MEDIC,
+ SKILL_SURGERY = SKILL_SURGERY_NOVICE,
SKILL_ENDURANCE = SKILL_ENDURANCE_SURVIVOR,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
)
+/datum/skills/civilian/survivor/pmc/engineer
+ name = "Survivor PMC Engineer"
+ additional_skills = list(
+ SKILL_POLICE = SKILL_POLICE_SKILLED,
+ SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
+ SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_SURVIVOR,
+ SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
+ SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
+ SKILL_POWERLOADER = SKILL_POWERLOADER_MASTER,
+ )
+
/datum/skills/civilian/survivor/doctor
name = "Survivor Doctor"
additional_skills = list(
@@ -449,7 +476,7 @@ MILITARY SURVIVORS
SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
- SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_SURVIVOR,
SKILL_LEADERSHIP = SKILL_LEAD_NOVICE,
SKILL_VEHICLE = SKILL_VEHICLE_DEFAULT,
SKILL_JTAC = SKILL_JTAC_TRAINED,
@@ -465,7 +492,7 @@ MILITARY SURVIVORS
SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
SKILL_MEDICAL = SKILL_MEDICAL_MEDIC,
SKILL_SURGERY = SKILL_SURGERY_NOVICE,
- SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_SURVIVOR,
SKILL_LEADERSHIP = SKILL_LEAD_NOVICE,
SKILL_VEHICLE = SKILL_VEHICLE_DEFAULT,
SKILL_JTAC = SKILL_JTAC_TRAINED,
@@ -481,7 +508,7 @@ MILITARY SURVIVORS
SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
SKILL_SPEC_WEAPONS = SKILL_SPEC_SCOUT,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
- SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_SURVIVOR,
SKILL_LEADERSHIP = SKILL_LEAD_NOVICE,
SKILL_VEHICLE = SKILL_VEHICLE_DEFAULT,
SKILL_JTAC = SKILL_JTAC_TRAINED,
@@ -497,7 +524,7 @@ MILITARY SURVIVORS
SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
SKILL_SPEC_WEAPONS = SKILL_SPEC_SMARTGUN,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
- SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_SURVIVOR,
SKILL_LEADERSHIP = SKILL_LEAD_NOVICE,
SKILL_VEHICLE = SKILL_VEHICLE_DEFAULT,
SKILL_JTAC = SKILL_JTAC_TRAINED,
@@ -513,7 +540,7 @@ MILITARY SURVIVORS
SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
SKILL_SPEC_WEAPONS = SKILL_SPEC_GRENADIER,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
- SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_SURVIVOR,
SKILL_LEADERSHIP = SKILL_LEAD_NOVICE,
SKILL_VEHICLE = SKILL_VEHICLE_DEFAULT,
SKILL_JTAC = SKILL_JTAC_TRAINED,
@@ -530,7 +557,7 @@ MILITARY SURVIVORS
SKILL_POLICE = SKILL_POLICE_SKILLED,
SKILL_JTAC = SKILL_JTAC_TRAINED,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
- SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_SURVIVOR,
SKILL_LEADERSHIP = SKILL_LEAD_TRAINED,
SKILL_VEHICLE = SKILL_VEHICLE_DEFAULT,
SKILL_JTAC = SKILL_JTAC_TRAINED,
@@ -545,6 +572,7 @@ COMMAND STAFF
/datum/skills/general
name = "General"
skills = list(
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_LEADERSHIP = SKILL_LEAD_MASTER,
@@ -851,7 +879,7 @@ SYNTHETIC
/datum/skills/colonial_synthetic
name = SYNTH_COLONY
skills = list(
- SKILL_CQC = SKILL_CQC_SKILLED,
+ SKILL_CQC = SKILL_CQC_EXPERT,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
@@ -862,7 +890,7 @@ SYNTHETIC
SKILL_MELEE_WEAPONS = SKILL_MELEE_SUPER,
SKILL_PILOT = SKILL_PILOT_EXPERT,
SKILL_POLICE = SKILL_POLICE_SKILLED,
- SKILL_FIREMAN = SKILL_FIREMAN_TRAINED,
+ SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
SKILL_POWERLOADER = SKILL_POWERLOADER_MASTER,
SKILL_VEHICLE = SKILL_VEHICLE_LARGE,
SKILL_JTAC = SKILL_JTAC_BEGINNER,
@@ -1018,9 +1046,8 @@ COLONIAL LIBERATION FRONT
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
SKILL_ENGINEER = SKILL_ENGINEER_TRAINED,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
- SKILL_POWERLOADER = SKILL_POWERLOADER_TRAINED,
SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
- SKILL_ENDURANCE = SKILL_ENDURANCE_MAX,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_WEAK,
SKILL_JTAC = SKILL_JTAC_BEGINNER,
)
@@ -1032,7 +1059,7 @@ COLONIAL LIBERATION FRONT
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
SKILL_POWERLOADER = SKILL_POWERLOADER_TRAINED,
SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
- SKILL_ENDURANCE = SKILL_ENDURANCE_MAX,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_WEAK,
SKILL_JTAC = SKILL_JTAC_BEGINNER,
)
@@ -1041,9 +1068,8 @@ COLONIAL LIBERATION FRONT
skills = list(
SKILL_MEDICAL = SKILL_MEDICAL_MEDIC,
SKILL_SURGERY = SKILL_SURGERY_TRAINED,
- SKILL_POWERLOADER = SKILL_POWERLOADER_TRAINED,
SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
- SKILL_ENDURANCE = SKILL_ENDURANCE_MAX,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_WEAK,
SKILL_JTAC = SKILL_JTAC_BEGINNER,
)
@@ -1051,11 +1077,11 @@ COLONIAL LIBERATION FRONT
name = "CLF Specialist"
skills = list(
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
- SKILL_CQC = SKILL_CQC_SKILLED,
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_ENGINEER = SKILL_ENGINEER_TRAINED, //to use c4 in demo set.
SKILL_LEADERSHIP = SKILL_LEAD_TRAINED,
SKILL_SPEC_WEAPONS = SKILL_SPEC_ALL,
- SKILL_ENDURANCE = SKILL_ENDURANCE_MAX,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
SKILL_JTAC = SKILL_JTAC_TRAINED
)
@@ -1065,7 +1091,7 @@ COLONIAL LIBERATION FRONT
skills = list(
SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI, // to use their C4
- SKILL_CQC = SKILL_CQC_EXPERT,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_LEADERSHIP = SKILL_LEAD_EXPERT,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
SKILL_POLICE = SKILL_POLICE_SKILLED,
@@ -1082,7 +1108,7 @@ COLONIAL LIBERATION FRONT
skills = list(
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
- SKILL_CQC = SKILL_CQC_EXPERT,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_LEADERSHIP = SKILL_LEAD_MASTER,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
SKILL_POLICE = SKILL_POLICE_SKILLED,
@@ -1147,9 +1173,9 @@ UNITED PROGRESSIVE PEOPLES
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
SKILL_ENGINEER = SKILL_ENGINEER_TRAINED,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
- SKILL_ENDURANCE = SKILL_ENDURANCE_EXPERT,
- SKILL_CQC = SKILL_CQC_EXPERT,
- SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_CQC = SKILL_CQC_DEFAULT,
+ SKILL_FIREMAN = SKILL_FIREMAN_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
)
@@ -1159,9 +1185,9 @@ UNITED PROGRESSIVE PEOPLES
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
- SKILL_ENDURANCE = SKILL_ENDURANCE_EXPERT,
- SKILL_CQC = SKILL_CQC_EXPERT,
- SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_CQC = SKILL_CQC_DEFAULT,
+ SKILL_FIREMAN = SKILL_FIREMAN_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
)
@@ -1170,9 +1196,9 @@ UNITED PROGRESSIVE PEOPLES
skills = list(
SKILL_MEDICAL = SKILL_MEDICAL_DOCTOR,
SKILL_SURGERY = SKILL_SURGERY_TRAINED,
- SKILL_ENDURANCE = SKILL_ENDURANCE_MASTER, //trained in medicine more than combat
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_CQC = SKILL_CQC_DEFAULT,
SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
- SKILL_CQC = SKILL_CQC_TRAINED
)
/datum/skills/upp/specialist
@@ -1180,14 +1206,13 @@ UNITED PROGRESSIVE PEOPLES
skills = list(
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
SKILL_ENGINEER = SKILL_ENGINEER_TRAINED,
- SKILL_ENDURANCE = SKILL_ENDURANCE_MAX,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_MASTER,
SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_LEADERSHIP = SKILL_LEAD_TRAINED,
SKILL_JTAC = SKILL_JTAC_TRAINED,
SKILL_SPEC_WEAPONS = SKILL_SPEC_UPP,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
- SKILL_CQC = SKILL_CQC_MASTER,
)
/datum/skills/upp/SL
@@ -1205,10 +1230,10 @@ UNITED PROGRESSIVE PEOPLES
/datum/skills/upp/military_police
name = "UPP Military Police"
skills = list(
- SKILL_CQC = SKILL_CQC_EXPERT,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_POLICE = SKILL_POLICE_SKILLED,
SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
- SKILL_ENDURANCE = SKILL_ENDURANCE_EXPERT,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_MASTER,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
SKILL_ENGINEER = SKILL_ENGINEER_TRAINED,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
@@ -1218,10 +1243,10 @@ UNITED PROGRESSIVE PEOPLES
/datum/skills/upp/officer
name = "UPP Officer"
skills = list(
- SKILL_CQC = SKILL_CQC_EXPERT,
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_POLICE = SKILL_POLICE_FLASH,
SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
- SKILL_ENDURANCE = SKILL_ENDURANCE_EXPERT,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_MASTER,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_LEADERSHIP = SKILL_LEAD_EXPERT,
SKILL_ENGINEER = SKILL_ENGINEER_TRAINED,
@@ -1234,11 +1259,11 @@ UNITED PROGRESSIVE PEOPLES
/datum/skills/upp/commander
name = "UPP Command Officer"
skills = list(
- SKILL_CQC = SKILL_CQC_EXPERT,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_POLICE = SKILL_POLICE_SKILLED,
SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
SKILL_LEADERSHIP = SKILL_LEAD_MASTER,
- SKILL_ENDURANCE = SKILL_ENDURANCE_EXPERT,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_MASTER,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_MEDICAL = SKILL_MEDICAL_MEDIC,
@@ -1263,6 +1288,7 @@ Private Military Contractors
/datum/skills/pmc
name = "PMC Private"
skills = list(
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
SKILL_POLICE = SKILL_POLICE_SKILLED,
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
@@ -1274,6 +1300,7 @@ Private Military Contractors
/datum/skills/pmc/medic
name = "PMC Medic"
skills = list(
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
SKILL_POLICE = SKILL_POLICE_SKILLED,
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
@@ -1287,6 +1314,7 @@ Private Military Contractors
/datum/skills/pmc/medic/chem
name = "PMC Medical Investigator"
skills = list(
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
SKILL_POLICE = SKILL_POLICE_SKILLED,
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
@@ -1300,6 +1328,7 @@ Private Military Contractors
/datum/skills/pmc/smartgunner
name = "PMC Smartgunner"
skills = list(
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
SKILL_POLICE = SKILL_POLICE_SKILLED,
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
@@ -1313,6 +1342,7 @@ Private Military Contractors
/datum/skills/pmc/specialist
name = "PMC Specialist"
skills = list(
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
SKILL_POLICE = SKILL_POLICE_SKILLED,
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
@@ -1333,7 +1363,7 @@ Private Military Contractors
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
- SKILL_CQC = SKILL_CQC_TRAINED,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_LEADERSHIP = SKILL_LEAD_TRAINED,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
SKILL_ENDURANCE = SKILL_ENDURANCE_MASTER,
@@ -1348,7 +1378,7 @@ Private Military Contractors
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
- SKILL_CQC = SKILL_CQC_TRAINED,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_LEADERSHIP = SKILL_LEAD_TRAINED,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
SKILL_ENDURANCE = SKILL_ENDURANCE_MASTER,
@@ -1424,18 +1454,17 @@ CONTRACTORS
/datum/skills/contractor
name = "Contractor Standard"
skills = list(
- SKILL_CQC = SKILL_CQC_MASTER,
- SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
- SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
+ SKILL_CQC = SKILL_CQC_TRAINED,
+ SKILL_ENGINEER = SKILL_ENGINEER_TRAINED,
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_MAX,
SKILL_POLICE = SKILL_POLICE_SKILLED,
- SKILL_FIREMAN = SKILL_FIREMAN_MASTER,
- SKILL_VEHICLE = SKILL_VEHICLE_CREWMAN,
+ SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
+ SKILL_VEHICLE = SKILL_VEHICLE_LARGE,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
SKILL_JTAC = SKILL_JTAC_BEGINNER,
SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
- SKILL_POWERLOADER = SKILL_POWERLOADER_MASTER,
)
/datum/skills/contractor/leader
@@ -1447,11 +1476,11 @@ CONTRACTORS
SKILL_LEADERSHIP = SKILL_LEAD_MASTER,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
SKILL_POLICE = SKILL_POLICE_SKILLED,
- SKILL_FIREMAN = SKILL_FIREMAN_MASTER,
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
- SKILL_VEHICLE = SKILL_VEHICLE_CREWMAN,
- SKILL_POWERLOADER = SKILL_POWERLOADER_MASTER,
+ SKILL_VEHICLE = SKILL_VEHICLE_LARGE,
+ SKILL_POWERLOADER = SKILL_POWERLOADER_TRAINED,
SKILL_ENDURANCE = SKILL_ENDURANCE_EXPERT,
SKILL_JTAC = SKILL_JTAC_MASTER,
)
@@ -1461,51 +1490,50 @@ CONTRACTORS
skills = list(
SKILL_FIREARMS = SKILL_FIREARMS_MAX,
SKILL_POLICE = SKILL_POLICE_SKILLED,
- SKILL_FIREMAN = SKILL_FIREMAN_MASTER,
- SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
- SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
+ SKILL_ENGINEER = SKILL_ENGINEER_TRAINED,
SKILL_MEDICAL = SKILL_MEDICAL_DOCTOR,
- SKILL_SURGERY = SKILL_SURGERY_EXPERT,
- SKILL_VEHICLE = SKILL_VEHICLE_CREWMAN,
+ SKILL_SURGERY = SKILL_SURGERY_TRAINED,
+ SKILL_VEHICLE = SKILL_VEHICLE_LARGE,
SKILL_ENDURANCE = SKILL_ENDURANCE_EXPERT,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_JTAC = SKILL_JTAC_BEGINNER,
)
/datum/skills/contractor/engi
name = "Contractor Engi"
skills = list(
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_ENGINEER = SKILL_ENGINEER_MAX,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_MAX,
SKILL_FIREARMS = SKILL_FIREARMS_MAX,
SKILL_POLICE = SKILL_POLICE_SKILLED,
- SKILL_FIREMAN = SKILL_FIREMAN_MASTER,
- SKILL_VEHICLE = SKILL_VEHICLE_MAX,
+ SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
+ SKILL_VEHICLE = SKILL_VEHICLE_LARGE,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
SKILL_JTAC = SKILL_JTAC_EXPERT,
SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
- SKILL_POWERLOADER = SKILL_POWERLOADER_MAX,
+ SKILL_POWERLOADER = SKILL_POWERLOADER_TRAINED,
)
/datum/skills/contractor/heavy
name = "Contractor Machinegunner"
skills = list(
- SKILL_CQC = SKILL_CQC_MASTER,
- SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
- SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
+ SKILL_CQC = SKILL_CQC_TRAINED,
+ SKILL_ENGINEER = SKILL_ENGINEER_TRAINED,
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_MAX,
SKILL_POLICE = SKILL_POLICE_SKILLED,
- SKILL_FIREMAN = SKILL_FIREMAN_MASTER,
- SKILL_VEHICLE = SKILL_VEHICLE_CREWMAN,
+ SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
+ SKILL_VEHICLE = SKILL_VEHICLE_LARGE,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
SKILL_SPEC_WEAPONS = SKILL_SPEC_SMARTGUN,
SKILL_JTAC = SKILL_JTAC_BEGINNER,
SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
- SKILL_POWERLOADER = SKILL_POWERLOADER_MASTER,
)
/*
@@ -1517,7 +1545,7 @@ COLONIAL MARSHALS
name = "CMB Deputy"
skills = list(
SKILL_POLICE = SKILL_POLICE_SKILLED,
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_EXPERT,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_DEFAULT,
SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
@@ -1531,7 +1559,7 @@ COLONIAL MARSHALS
name = "CMB Marshal"
skills = list(
SKILL_POLICE = SKILL_POLICE_SKILLED,
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_EXPERT,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_LEADERSHIP = SKILL_LEAD_MASTER,
SKILL_MEDICAL = SKILL_MEDICAL_MEDIC,
@@ -1691,7 +1719,7 @@ MISCELLANEOUS
/datum/skills/mercenary
name = "Mercenary"
skills = list(
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
@@ -1703,7 +1731,7 @@ MISCELLANEOUS
/datum/skills/mercenary/elite
name = "Elite Mercenary"
skills = list(
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_FIREARMS = SKILL_FIREARMS_MAX,
@@ -1717,7 +1745,7 @@ MISCELLANEOUS
/datum/skills/mercenary/elite/medic
name = "Elite Mercenary Medic"
skills = list(
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_FIREARMS = SKILL_FIREARMS_MAX,
@@ -1725,13 +1753,13 @@ MISCELLANEOUS
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
SKILL_JTAC = SKILL_JTAC_BEGINNER,
SKILL_ENDURANCE = SKILL_ENDURANCE_MAX,
- SKILL_SURGERY = SKILL_SURGERY_EXPERT,
+ SKILL_SURGERY = SKILL_SURGERY_TRAINED,
)
/datum/skills/mercenary/elite/engineer
name = "Elite Mercenary Engineer"
skills = list(
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_ENGINEER = SKILL_ENGINEER_MASTER,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_MASTER,
SKILL_FIREARMS = SKILL_FIREARMS_MAX,
@@ -1746,7 +1774,7 @@ MISCELLANEOUS
/datum/skills/mercenary/elite/heavy
name = "Elite Mercenary Heavy"
skills = list(
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_FIREARMS = SKILL_FIREARMS_MAX,
@@ -1761,7 +1789,7 @@ MISCELLANEOUS
/datum/skills/mercenary/elite/leader
name = "Elite Mercenary Leader"
skills = list(
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_FIREARMS = SKILL_FIREARMS_MAX,
@@ -1776,7 +1804,7 @@ MISCELLANEOUS
/datum/skills/dutchmerc
name = "Dutch's Dozen Mercenary"
skills = list(
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
@@ -1785,13 +1813,13 @@ MISCELLANEOUS
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
SKILL_SPEC_WEAPONS = SKILL_SPEC_ALL,
SKILL_JTAC = SKILL_JTAC_BEGINNER,
- SKILL_ENDURANCE = SKILL_ENDURANCE_MAX,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_EXPERT,
)
/datum/skills/dutchmedic
name = "Dutch's Dozen Medic"
skills = list(
- SKILL_CQC = SKILL_CQC_MASTER,
+ SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
@@ -1800,7 +1828,7 @@ MISCELLANEOUS
SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
SKILL_JTAC = SKILL_JTAC_BEGINNER,
- SKILL_ENDURANCE = SKILL_ENDURANCE_MAX,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_EXPERT,
)
/datum/skills/tank_crew
diff --git a/code/datums/weather/weather_events/new_varadero.dm b/code/datums/weather/weather_events/new_varadero.dm
new file mode 100644
index 000000000000..f2af23c3f10a
--- /dev/null
+++ b/code/datums/weather/weather_events/new_varadero.dm
@@ -0,0 +1,38 @@
+/datum/weather_event/light_rain
+ name = "Tropical Storm"
+ display_name = "Tropical Storm"
+ length = 4 MINUTES
+ fullscreen_type = /atom/movable/screen/fullscreen/weather/low
+
+ turf_overlay_icon_state = "strata_storm"
+ turf_overlay_alpha = 40
+
+ effect_message = null
+ damage_per_tick = 0
+
+ has_process = TRUE
+ lightning_chance = 1
+
+ ambience = 'sound/ambience/rainforest.ogg'
+
+ fire_smothering_strength = 1
+
+/datum/weather_event/monsoon
+ name = "Monsoon Warning"
+ display_name = "Monsoon Warning"
+ length = 6 MINUTES
+ fullscreen_type = /atom/movable/screen/fullscreen/weather/high
+
+ turf_overlay_icon_state = "strata_storm"
+ turf_overlay_alpha = 115
+
+ effect_message = null
+ damage_per_tick = 0
+
+
+ ambience = 'sound/ambience/varadero_storm.ogg'
+
+ has_process = TRUE
+ lightning_chance = 6
+
+ fire_smothering_strength = 4
diff --git a/code/datums/weather/weather_map_holders/new_varadero.dm b/code/datums/weather/weather_map_holders/new_varadero.dm
new file mode 100644
index 000000000000..8222001f4739
--- /dev/null
+++ b/code/datums/weather/weather_map_holders/new_varadero.dm
@@ -0,0 +1,20 @@
+/datum/weather_ss_map_holder/new_varadero
+ name = "New Varadero Map Holder"
+
+ min_time_between_events = 15 MINUTES
+ no_weather_turf_icon_state = "strata_clearsky"
+
+ potential_weather_events = list(
+ /datum/weather_event/light_rain,
+ /datum/weather_event/monsoon,
+ )
+
+/datum/weather_ss_map_holder/new_varadero/should_affect_area(area/A)
+ return !CEILING_IS_PROTECTED(A.ceiling, CEILING_GLASS)
+
+/datum/weather_ss_map_holder/new_varadero/should_start_event()
+ return prob(PROB_WEATHER_NEW_VARADERO)
+
+/datum/weather_ss_map_holder/new_varadero/weather_warning()
+ for (var/obj/structure/machinery/storm_siren/WS in weather_notify_objects)
+ WS.weather_warning()
diff --git a/code/game/area/varadero.dm b/code/game/area/varadero.dm
index a0d005d4aa1d..09b082f2acd6 100644
--- a/code/game/area/varadero.dm
+++ b/code/game/area/varadero.dm
@@ -4,8 +4,7 @@
/area/varadero
name = "New Varadero"
icon = 'icons/turf/area_varadero.dmi'
- ambience_exterior = AMBIENCE_LV624
- sound_environment = SOUND_ENVIRONMENT_MOUNTAINS
+ ambience_exterior = AMBIENCE_NV
icon_state = "varadero"
can_build_special = TRUE //T-Comms structure
temperature = TROPICAL_TEMP
@@ -36,49 +35,44 @@
/area/varadero/exterior
name = "New Varadero - Exterior"
ceiling = CEILING_NONE
- ambience_exterior = AMBIENCE_LV624
+ lighting_use_dynamic = TRUE
+ ambience_exterior = AMBIENCE_NV
//soundscape_playlist
- sound_environment = SOUND_ENVIRONMENT_MOUNTAINS
/area/varadero/interior
name = "New Varadero - Interior"
- ceiling = CEILING_UNDERGROUND_ALLOW_CAS
+ ceiling = CEILING_GLASS
ambience_exterior = AMBIENCE_PRISON
//soundscape_playlist
sound_environment = SOUND_ENVIRONMENT_ROOM
/area/varadero/interior_protected
name = "New Varadero - Interior"
- ceiling = CEILING_DEEP_UNDERGROUND
- icon_state = "NV_no_OB"
+ ceiling = CEILING_UNDERGROUND_BLOCK_CAS
+ sound_environment = SOUND_ENVIRONMENT_AUDITORIUM
+ icon_state = "NV_no_CAS"
/area/varadero/interior/comms1
name = "New Varadero - Cargo Generator"
+ is_resin_allowed = FALSE
icon_state = "comms1"
+ minimap_color = MINIMAP_AREA_ENGI_CAVE
/area/varadero/interior/comms2
name = "New Varadero - Communications Project Site"
- is_resin_allowed = FALSE
icon_state = "comms2"
+ minimap_color = MINIMAP_AREA_ENGI_CAVE
/area/varadero/interior/comms3
- name = "New Varadero - Fishing Hole"
- is_resin_allowed = FALSE
+ name = "New Varadero - Engineering Communications"
icon_state = "comms3"
+ minimap_color = MINIMAP_AREA_ENGI_CAVE
/area/varadero/exterior/comms4
name = "New Varadero - Walkway Extension"
- icon_state = "comms4"
-
-/area/varadero/exterior/eastbeach
- name = "New Varadero - East Beach"
- icon_state = "varadero1"
-
-/area/varadero/exterior/eastocean
- name = "New Varadero - East Ocean"
is_resin_allowed = FALSE
- flags_area = AREA_NOTUNNEL
- icon_state = "varadero2"
+ icon_state = "comms4"
+ minimap_color = MINIMAP_AREA_ENGI_CAVE
/area/varadero/interior/oob
name = "New Varadero - Out Of Bounds"
@@ -119,9 +113,57 @@
name = "New Varadero - Rockabilly Beach"
icon_state = "varadero0"
is_resin_allowed = FALSE
+ minimap_color = MINIMAP_AREA_JUNGLE
+
+/area/varadero/exterior/eastbeach
+ name = "New Varadero - East Beach"
+ is_resin_allowed = FALSE
+ icon_state = "varadero1"
+ lighting_use_dynamic = TRUE
+ minimap_color = MINIMAP_AREA_JUNGLE
+
+/area/varadero/exterior/monsoon
+ name = "New Varadero - Monsoon"
+ icon_state = "varadero1"
+ minimap_color = MINIMAP_AREA_JUNGLE
+
+/area/varadero/exterior/pool
+ name = "New Varadero - Interior Pool"
+ icon_state = "varadero1"
+ lighting_use_dynamic = TRUE
+ minimap_color = MINIMAP_AREA_COMMAND_CAVE
+
+/area/varadero/exterior/eastocean
+ name = "New Varadero - East Ocean"
+ is_resin_allowed = FALSE
+ flags_area = AREA_NOTUNNEL
+ icon_state = "varadero2"
+ minimap_color = MINIMAP_AREA_CONTESTED_ZONE
+
+/area/varadero/exterior/farocean
+ name = "New Varadero - Far Ocean"
+ is_resin_allowed = FALSE
+ flags_area = AREA_NOTUNNEL
+ icon_state = "varadero3"
+ minimap_color = MINIMAP_AREA_CONTESTED_ZONE
//interior areas
+
+/area/varadero/interior/beach_bar
+ name = "New Varadero - Beach Bar"
+ icon_state = "varadero4"
+ is_resin_allowed = FALSE
+ minimap_color = MINIMAP_AREA_JUNGLE
+ sound_environment = SOUND_ENVIRONMENT_ROOM
+
+/area/varadero/interior/dock_control
+ name = "New Varadero - Dock Control"
+ icon_state = "varadero3"
+ is_resin_allowed = FALSE
+ minimap_color = MINIMAP_AREA_JUNGLE
+ sound_environment = SOUND_ENVIRONMENT_ROOM
+
/area/varadero/interior/cargo
name = "New Varadero - Cargo"
icon_state = "req0"
@@ -145,15 +187,18 @@
name = "New Vardero - Chapel"
icon_state = "offices1"
is_resin_allowed = FALSE
+ minimap_color = MINIMAP_AREA_COMMAND_CAVE
/area/varadero/interior/morgue
name = "New Varadero - Morgue"
icon_state = "offices0"
is_resin_allowed = FALSE
+ minimap_color = MINIMAP_AREA_MEDBAY_CAVE
/area/varadero/interior/medical
name = "New Varadero - Medical"
icon_state = "offices2"
+ minimap_color = MINIMAP_AREA_MEDBAY
/area/varadero/interior/maintenance
name = "New Varadero - Central Maintenance"
@@ -167,10 +212,12 @@
/area/varadero/interior/maintenance/research
name = "New Varadero - Research Maintenance"
icon_state = "tunnels1"
+ minimap_color = MINIMAP_AREA_RESEARCH_CAVE
/area/varadero/interior/maintenance/security
name = "New Varadero - Security Maintenance"
icon_state = "tunnels2"
+ minimap_color = MINIMAP_AREA_SEC_CAVE
/area/varadero/interior/research
name = "New Varadero - Research Offices"
@@ -180,6 +227,7 @@
/area/varadero/interior/electrical
name = "New Varadero - Electrical Annex"
icon_state = "req4"
+ minimap_color = MINIMAP_AREA_ENGI
/area/varadero/interior/toilets
name = "New Varadero - Restrooms"
@@ -188,6 +236,7 @@
/area/varadero/interior/technical_storage
name = "New Varadero - Technical Storage"
icon_state = "req3"
+ minimap_color = MINIMAP_AREA_ENGI
/area/varadero/interior/laundry
name = "New Varadero - Laundry"
@@ -196,31 +245,38 @@
/area/varadero/interior/disposals
name = "New Varadero - Disposals"
icon_state = "offices4"
+ minimap_color = MINIMAP_AREA_ENGI
/area/varadero/interior/administration
name = "New Varadero - Administrative Offices"
icon_state = "offices2"
+ minimap_color = MINIMAP_AREA_COMMAND
/area/varadero/interior/library
name = "New Varadero - Library"
icon_state = "offices0"
is_resin_allowed = FALSE
+ minimap_color = MINIMAP_AREA_COMMAND_CAVE
/area/varadero/interior/court
name = "New Varadero - Basketball Court"
icon_state = "req4"
+ minimap_color = MINIMAP_AREA_COMMAND_CAVE
/area/varadero/interior/mess
name = "New Varadero - Mess Hall"
icon_state = "req2"
+ minimap_color = MINIMAP_AREA_COMMAND_CAVE
/area/varadero/interior/bunks
name = "New Varadero - Level 1 Quarters"
icon_state = "req3"
+ minimap_color = MINIMAP_AREA_JUNGLE
/area/varadero/interior/security
name = "New Varadero - Security Offices"
icon_state = "offices0"
+ minimap_color = MINIMAP_AREA_SEC
/area/varadero/interior/records
name = "New Varadero - Records"
@@ -238,12 +294,12 @@
power_environ = FALSE
luminosity = 0
lighting_use_dynamic = 1
+ sound_environment = SOUND_ENVIRONMENT_AUDITORIUM
minimap_color = MINIMAP_AREA_CAVES
/area/varadero/interior/caves/north_research
name = "New Varadero - North Research Caves"
icon_state = "tunnels4"
- minimap_color = MINIMAP_AREA_RESEARCH_CAVE
/area/varadero/interior/caves/east
name = "New Varadero - Beach Caves"
@@ -257,20 +313,25 @@
power_environ = FALSE
luminosity = 0
lighting_use_dynamic = 1
- minimap_color = MINIMAP_AREA_RESEARCH_CAVE
/area/varadero/interior_protected/caves/central
name = "New Varadero - Grass Caves"
icon_state = "deepcaves2"
+ minimap_color = MINIMAP_AREA_CAVES
/area/varadero/interior_protected/caves/digsite
name = "New Varadero - Dig Site"
icon_state = "deepcaves3"
+/area/varadero/interior_protected/caves/swcaves
+ name = "New Varadero - Southwest Caves"
+ icon_state = "deepcaves3"
+
/area/varadero/interior_protected/maintenance/south
name = "New Varadero - Southern Maintenance"
icon_state = "deepcaves4"
+ minimap_color = MINIMAP_AREA_CAVES
/area/varadero/interior_protected/vessel
name = "New Varadero - Unknown Vessel"
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 937c19b4512b..d2cad09edc83 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -439,7 +439,7 @@ Parameters are passed from New.
return TRUE
if(href_list["desc_lore"])
- show_browser(usr, "
[replacetext(desc_lore, "\n", " ")]", name, name, "size=500x200")
+ show_browser(usr, "[replacetext(desc_lore, "\n", " ")]", name, name, "size=500x500")
onclose(usr, "[name]")
///This proc is called on atoms when they are loaded into a shuttle
@@ -580,7 +580,7 @@ Parameters are passed from New.
if(!ismovable(src))
var/turf/curturf = get_turf(src)
if(curturf)
- . += ""
+ . += ""
VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRANSFORM, "Modify Transform")
VV_DROPDOWN_OPTION(VV_HK_ADD_REAGENT, "Add Reagent")
VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EMP, "EMP Pulse")
diff --git a/code/game/bioscans.dm b/code/game/bioscans.dm
index 55422ad3b878..5f07b307751a 100644
--- a/code/game/bioscans.dm
+++ b/code/game/bioscans.dm
@@ -94,15 +94,19 @@ GLOBAL_DATUM_INIT(bioscan_data, /datum/bioscan_data, new)
var/marine_planet_location_string = "[marine_planet_location ? ", including one in [marine_planet_location]." : "."]"
var/marine_ship_location_string = "[marine_ship_location ? ", including one in [marine_ship_location]." : "."]"
+ var/ghost_scan = SPAN_ALERT("[xenos_on_planet] xenos on planet, with [larva] larva.\n[xenos_on_ship] xenos on the ship.\n[marines_on_planet] humans on the planet.\n[marines_on_ship] humans on the ship.")
+ var/yautja_scan = SPAN_ALERT("[xenos_on_planet] serpents present in the hunting ground[xeno_planet_location_string], with [larva] larva.\n[xenos_on_ship] serpents present on the human ship[xeno_ship_location_string]\n[marines_on_planet] humans present in the hunting ground[marine_planet_location_string]\n[marines_on_ship] humans present on the human ship[marine_ship_location_string]")
+ log_game("BIOSCAN: A Yautja/Ghost bioscan has completed. [ghost_scan]")
+
//Announce the numbers to Yautja, they have good scanners
for(var/mob/living/carbon/human/yautja as anything in GLOB.yautja_mob_list)
to_chat(yautja, "
Bioscan complete
")
- to_chat(yautja, SPAN_ALERT("[xenos_on_planet] serpents present in the hunting ground[xeno_planet_location_string], with [larva] larva.\n[xenos_on_ship] serpents present on the human ship[xeno_ship_location_string]\n[marines_on_planet] humans present in the hunting ground[marine_planet_location_string]\n[marines_on_ship] humans present on the human ship[marine_ship_location_string]"))
+ to_chat(yautja, yautja_scan)
//Let the ghosts know what's up, they also get good numbers
for(var/mob/dead/observer/ghost as anything in GLOB.observer_list)
to_chat(ghost, "
Bioscan complete
")
- to_chat(ghost, SPAN_ALERT("[xenos_on_planet] xenos on planet, with [larva] larva.\n[xenos_on_ship] xenos on the ship.\n[marines_on_planet] humans on the planet.\n[marines_on_ship] humans on the ship."))
+ to_chat(ghost, ghost_scan)
/// This will do something after Project ARES.
@@ -117,15 +121,15 @@ GLOBAL_DATUM_INIT(bioscan_data, /datum/bioscan_data, new)
/// The announcement to all Humans. Slightly off for the planet and elsewhere, accurate for the ship.
/datum/bioscan_data/proc/ares_bioscan(forced = FALSE, variance = 2)
if(!forced && !can_ares_bioscan())
- message_admins("An ARES Bioscan has failed.")
+ message_admins("BIOSCAN: An ARES bioscan has failed.")
return
-
//Adjust the randomness there so everyone gets the same thing
var/fake_xenos_on_planet = max(0, xenos_on_planet + rand(-variance, variance))
-
var/name = "[MAIN_AI_SYSTEM] Bioscan Status"
var/input = "Bioscan complete.\n\nSensors indicate [xenos_on_ship_uncontained ? "[xenos_on_ship_uncontained]" : "no"] unknown lifeform signature[!xenos_on_ship_uncontained || xenos_on_ship_uncontained > 1 ? "s":""] present on the ship[xenos_on_ship_uncontained && xenos_ship_location ? ", including one in [xenos_ship_location]," : ""] and [fake_xenos_on_planet ? "approximately [fake_xenos_on_planet]" : "no"] signature[!fake_xenos_on_planet || fake_xenos_on_planet > 1 ? "s":""] located elsewhere[fake_xenos_on_planet && xenos_planet_location ? ", including one in [xenos_planet_location]":""]."
+ log_game("BIOSCAN: ARES bioscan completed. [input]")
+
var/datum/ares_link/link = GLOB.ares_link
link.log_ares_bioscan(name, input)
if(forced || (link.p_interface && !link.p_interface.inoperable()))
@@ -135,13 +139,18 @@ GLOBAL_DATUM_INIT(bioscan_data, /datum/bioscan_data, new)
/datum/bioscan_data/proc/qm_bioscan(variance = 2)
/// Adjust the randomness there so everyone gets the same thing
var/fake_marines_on_ship = max(0, marines_on_ship + rand(-variance, variance))
+ var/metalhive_hosts = "[fake_marines_on_ship ? "approximately [fake_marines_on_ship]":"no"]"
+ var/plural = "[!fake_marines_on_ship || fake_marines_on_ship > 1 ? "s":""]"
+ var/metalhive_location = "[fake_marines_on_ship && marine_ship_location?", including one in [marine_ship_location]," : ""]"
+ var/planet_hosts = "[marines_on_planet ? "[marines_on_planet]" : "none"]"
+ var/planet_location = "[marines_on_planet && marine_planet_location ? ", including one in [marine_planet_location]" : ""]"
+
+ var/title = SPAN_XENOANNOUNCE("The Queen Mother reaches into your mind from worlds away.")
+ var/content = SPAN_XENOANNOUNCE("To my children and their Queen. I sense [metalhive_hosts] host[plural] in the metal hive [metalhive_location] and [planet_hosts] scattered elsewhere[planet_location].")
+
+ log_game("BIOSCAN: Queen Mother bioscan completed. [content]")
/// Shout it at everyone
for(var/mob/current_mob as anything in GLOB.living_xeno_list)
current_mob << sound(get_sfx("queen"), wait = 0, volume = 50)
- to_chat(current_mob, SPAN_XENOANNOUNCE("The Queen Mother reaches into your mind from worlds away."))
- var/metalhive_hosts = "[fake_marines_on_ship ? "approximately [fake_marines_on_ship]":"no"]"
- var/plural = "[!fake_marines_on_ship || fake_marines_on_ship > 1 ? "s":""]"
- var/metalhive_location = "[fake_marines_on_ship&&marine_ship_location?", including one in [marine_ship_location],":""]"
- var/planet_hosts = "[marines_on_planet ? "[marines_on_planet]" : "none"]"
- var/planet_location = "[marines_on_planet && marine_planet_location ? ", including one in [marine_planet_location]" : ""]"
- to_chat(current_mob, SPAN_XENOANNOUNCE("To my children and their Queen. I sense [metalhive_hosts] host[plural] in the metal hive [metalhive_location] and [planet_hosts] scattered elsewhere[planet_location]."))
+ to_chat(current_mob, title)
+ to_chat(current_mob, content)
diff --git a/code/game/gamemodes/cm_initialize.dm b/code/game/gamemodes/cm_initialize.dm
index 2f6dc6e38118..a42ff3f22e59 100644
--- a/code/game/gamemodes/cm_initialize.dm
+++ b/code/game/gamemodes/cm_initialize.dm
@@ -333,23 +333,28 @@ Additional game mode variables.
/datum/game_mode/proc/check_xeno_late_join(mob/xeno_candidate)
if(jobban_isbanned(xeno_candidate, JOB_XENOMORPH)) // User is jobbanned
to_chat(xeno_candidate, SPAN_WARNING("You are banned from playing aliens and cannot spawn as a xenomorph."))
- return
- return 1
+ return FALSE
+ return TRUE
-/datum/game_mode/proc/attempt_to_join_as_xeno(mob/xeno_candidate, instant_join = 0)
+/datum/game_mode/proc/attempt_to_join_as_xeno(mob/xeno_candidate, instant_join = FALSE)
var/list/available_xenos = list()
var/list/available_xenos_non_ssd = list()
- for(var/mob/living/carbon/xenomorph/X in GLOB.living_xeno_list)
- var/area/A = get_area(X)
- if(is_admin_level(X.z) && (!A || !(A.flags_area & AREA_ALLOW_XENO_JOIN)) || X.aghosted)
- continue //xenos on admin z level and aghosted ones don't count
- if(istype(X) && ((!islarva(X) && (XENO_LEAVE_TIMER - X.away_timer < XENO_AVAILABLE_TIMER)) || (islarva(X) && (XENO_LEAVE_TIMER_LARVA - X.away_timer < XENO_AVAILABLE_TIMER))))
- if(!X.client)
- available_xenos += X
- else
- available_xenos_non_ssd += X
-
+ for(var/mob/living/carbon/xenomorph/cur_xeno as anything in GLOB.living_xeno_list)
+ if(cur_xeno.aghosted)
+ continue //aghosted xenos don't count
+ var/area/area = get_area(cur_xeno)
+ if(is_admin_level(cur_xeno.z) && (!area || !(area.flags_area & AREA_ALLOW_XENO_JOIN)))
+ continue //xenos on admin z level don't count
+ if(!istype(cur_xeno))
+ continue
+ var/required_time = islarva(cur_xeno) ? XENO_LEAVE_TIMER_LARVA - cur_xeno.away_timer : XENO_LEAVE_TIMER - cur_xeno.away_timer
+ if(required_time > XENO_AVAILABLE_TIMER)
+ continue
+ if(!cur_xeno.client)
+ available_xenos += cur_xeno
+ else
+ available_xenos_non_ssd += cur_xeno
var/datum/hive_status/hive
for(var/hivenumber in GLOB.hive_datum)
@@ -379,11 +384,18 @@ Additional game mode variables.
// No cache, lets check now then
message_alien_candidates(get_alien_candidates(), dequeued = 0, cache_only = TRUE)
if(candidate_observer.larva_queue_cached_message)
+ var/datum/hive_status/cur_hive
+ for(var/hive_num in GLOB.hive_datum)
+ cur_hive = GLOB.hive_datum[hive_num]
+ for(var/mob_name in cur_hive.banished_ckeys)
+ if(cur_hive.banished_ckeys[mob_name] == xeno_candidate.ckey)
+ candidate_observer.larva_queue_cached_message += "\n" + SPAN_WARNING("NOTE: You are banished from the [cur_hive] and you may not rejoin unless the Queen re-admits you or dies. Your queue number won't update until there is a hive you aren't banished from.")
+ break
to_chat(xeno_candidate, candidate_observer.larva_queue_cached_message)
return FALSE
// We aren't in queue yet, lets teach them about the queue then
- candidate_observer.larva_queue_cached_message = SPAN_XENONOTICE("You are currently still awaiting assignment in the larva queue. Priority is given to players who have yet to play in the round, but otherwise the ordering is based on your time of death. When you have been dead long enough and are not inactive, you will periodically receive messages where you are in the queue relative to other currently valid xeno candidates. Note: Playing as a facehugger or in the thunderdome will not alter your time of death. This means you won't lose your relative place in queue if you step away, disconnect, play as a facehugger, or play in the thunderdome.")
+ candidate_observer.larva_queue_cached_message = SPAN_XENONOTICE("You are currently awaiting assignment in the larva queue. The ordering is based on your time of death or the time you joined. When you have been dead long enough and are not inactive, you will periodically receive messages where you are in the queue relative to other currently valid xeno candidates. Your current position will shift as others change their preferences or go inactive, but your relative position compared to all observers is the same. Note: Playing as a facehugger or in the thunderdome will not alter your time of death. This means you won't lose your relative place in queue if you step away, disconnect, play as a facehugger, or play in the thunderdome.")
to_chat(xeno_candidate, candidate_observer.larva_queue_cached_message)
return FALSE
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index 7a10a3ffa1df..6ccb0b5b18f7 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -284,7 +284,7 @@
return
//Dismantle the frame.
- if(istype(O, /obj/item/tool/crowbar))
+ if(HAS_TRAIT(O, TRAIT_TOOL_CROWBAR))
dismantle()
return
diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm
index fd246d2d640a..bb6972a58ac3 100644
--- a/code/game/machinery/computer/ai_core.dm
+++ b/code/game/machinery/computer/ai_core.dm
@@ -51,7 +51,7 @@
to_chat(user, SPAN_NOTICE(" You screw the circuit board into place."))
state = 2
icon_state = "2"
- if(istype(P, /obj/item/tool/crowbar) && circuit)
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR) && circuit)
playsound(loc, 'sound/items/Crowbar.ogg', 25, 1)
to_chat(user, SPAN_NOTICE(" You remove the circuit board."))
state = 1
@@ -121,7 +121,7 @@
to_chat(usr, "Added [mmi].")
icon_state = "3b"
- if(istype(P, /obj/item/tool/crowbar) && brain)
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR) && brain)
playsound(loc, 'sound/items/Crowbar.ogg', 25, 1)
to_chat(user, SPAN_NOTICE(" You remove the brain."))
brain.forceMove(loc)
@@ -129,7 +129,7 @@
icon_state = "3"
if(4)
- if(istype(P, /obj/item/tool/crowbar))
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR))
playsound(loc, 'sound/items/Crowbar.ogg', 25, 1)
to_chat(user, SPAN_NOTICE(" You remove the glass panel."))
state = 3
diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm
index bd42b31ea573..07c960807205 100644
--- a/code/game/machinery/computer/buildandrepair.dm
+++ b/code/game/machinery/computer/buildandrepair.dm
@@ -52,7 +52,7 @@
to_chat(user, SPAN_NOTICE(" You screw the circuit board into place."))
src.state = 2
src.icon_state = "2"
- if(istype(P, /obj/item/tool/crowbar) && circuit)
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR) && circuit)
playsound(src.loc, 'sound/items/Crowbar.ogg', 25, 1)
to_chat(user, SPAN_NOTICE(" You remove the circuit board."))
src.state = 1
@@ -99,7 +99,7 @@
src.state = 4
src.icon_state = "4"
if(4)
- if(istype(P, /obj/item/tool/crowbar))
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR))
playsound(src.loc, 'sound/items/Crowbar.ogg', 25, 1)
to_chat(user, SPAN_NOTICE(" You remove the glass panel."))
src.state = 3
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index 7d82c5bd9a75..3c92bd33a681 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -469,9 +469,10 @@
if(!record) return
playsound(src.loc, 'sound/machines/fax.ogg', 15, 1)
sleep(40)
+ var/datum/asset/asset = get_asset_datum(/datum/asset/simple/paper)
var/obj/item/paper/P = new /obj/item/paper( src.loc )
P.name = text("Scan: [], []",record.fields["name"],worldtime2text())
- P.info += text("
",\
"You are NOT aware of the xenomorph threat.",\
"Your primary objective is to heal up and survive. If you want to assault the hive - adminhelp.")
@@ -78,9 +78,35 @@
spawn_priority = SPAWN_PRIORITY_HIGH
+/obj/effect/landmark/survivor_spawner/bigred_crashed_pmc_medic
+ equipment = /datum/equipment_preset/survivor/pmc/medic
+ synth_equipment = /datum/equipment_preset/pmc/synth
+ intro_text = list("
You are a survivor of a crash landing!
",\
+ "You are NOT aware of the xenomorph threat.",\
+ "Your primary objective is to heal up and survive. If you want to assault the hive - adminhelp.")
+ story_text = "You are a PMC medic from Weyland-Yutani. Your ship was enroute to Solaris Ridge to escort an Assistant Manager. On the way, your ship received a distress signal from the colony about an attack. Worried that it might be a CLF attack, your pilot set full speed for the colony. However, during atmospheric entry the engine failed and you fell unconcious from the G-Forces. You wake up wounded... and see that the ship has crashed onto the colony. Your squadmates lie dead beside you, but there's some missing. Perhaps they survived and moved elsewhere? You need to find out what happened to the colony, see if you can find any of your squadmates, and find a way to contact Weyland-Yutani."
+ roundstart_damage_min = 3
+ roundstart_damage_max = 10
+ roundstart_damage_times = 2
+
+ spawn_priority = SPAWN_PRIORITY_VERY_HIGH
+
+/obj/effect/landmark/survivor_spawner/bigred_crashed_pmc_engineer
+ equipment = /datum/equipment_preset/survivor/pmc/engineer
+ synth_equipment = /datum/equipment_preset/pmc/synth
+ intro_text = list("
You are a survivor of a crash landing!
",\
+ "You are NOT aware of the xenomorph threat.",\
+ "Your primary objective is to heal up and survive. If you want to assault the hive - adminhelp.")
+ story_text = "You are a PMC engineer from Weyland-Yutani. Your ship was enroute to Solaris Ridge to escort an Assistant Manager. On the way, your ship received a distress signal from the colony about an attack. Worried that it might be a CLF attack, your pilot set full speed for the colony. However, during atmospheric entry the engine failed and you fell unconcious from the G-Forces. You wake up wounded... and see that the ship has crashed onto the colony. Your squadmates lie dead beside you, but there's some missing. Perhaps they survived and moved elsewhere? You need to find out what happened to the colony, see if you can find any of your squadmates, and find a way to contact Weyland-Yutani."
+ roundstart_damage_min = 3
+ roundstart_damage_max = 10
+ roundstart_damage_times = 2
+
+ spawn_priority = SPAWN_PRIORITY_VERY_HIGH
+
/obj/effect/landmark/survivor_spawner/bigred_crashed_cl
equipment = /datum/equipment_preset/survivor/wy/manager
- synth_equipment = /datum/equipment_preset/pmc/synth
+ synth_equipment = /datum/equipment_preset/synth/survivor/pmc
intro_text = list("
You are a survivor of a crash landing!
",\
"You are NOT aware of the xenomorph threat.",\
"Your primary objective is to heal up and survive. If you want to assault the hive - adminhelp.")
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index a3e587089997..07b56eedb7d2 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -430,7 +430,7 @@
/obj/item/device/flashlight/flare/on/starshell_ash
name = "burning star shell ash"
- desc = "Bright burning ash from a Star Shell 40mm. Don't touch, oh it'll burn ya'."
+ desc = "Bright burning ash from a Star Shell 40mm. Don't touch, or it'll burn ya'."
icon_state = "starshell_ash"
brightness_on = 7
anchored = TRUE//can't be picked up
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index 0f010d689e74..6293abb67339 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -49,7 +49,7 @@
name = "AI Integrated Encryption Key"
desc = "Integrated encryption key"
icon_state = "cap_key"
- channels = list(RADIO_CHANNEL_ALMAYER = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_ALMAYER = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
/obj/item/device/encryptionkey/sentry_laptop
name = "Sentry Network Status Encryption Key"
@@ -61,12 +61,12 @@
/obj/item/device/encryptionkey/cmpcom/cdrcom
name = "\improper Marine Senior Command Radio Encryption Key"
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
/obj/item/device/encryptionkey/mcom
name = "\improper Marine Command Radio Encryption Key"
icon_state = "cap_key"
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
// MARINE ENGINEERING
@@ -102,7 +102,7 @@
/obj/item/device/encryptionkey/mmpo
name = "\improper Military Police Radio Encryption Key"
icon_state = "sec_key"
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE,)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE,)
/obj/item/device/encryptionkey/sec
name = "Security Radio Encryption Key"
@@ -130,7 +130,7 @@
/obj/item/device/encryptionkey/cmpcom/synth
name = "\improper Marine Synth Radio Encryption Key"
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
/obj/item/device/encryptionkey/mcom/cl
name = "\improper Corporate Liaison radio encryption key"
@@ -163,7 +163,7 @@
channels = list(RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_COMMAND = FALSE)
/obj/item/device/encryptionkey/mcom/ai //AI only.
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
// MARINE SQUADS
@@ -296,7 +296,7 @@
/obj/item/device/encryptionkey/highcom
name = "\improper USCM High Command Radio Encryption Key"
icon_state = "binary_key"
- channels = list(RADIO_CHANNEL_HIGHCOM = TRUE, SQUAD_SOF = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_HIGHCOM = TRUE, SQUAD_SOF = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
/obj/item/device/encryptionkey/contractor
name = "\improper Vanguard's Arrow Incorporated Radio Encryption Key"
@@ -306,7 +306,7 @@
/obj/item/device/encryptionkey/cmb
name = "\improper Colonial Marshal Bureau Radio Encryption Key"
icon_state = "cmb_key"
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_INTEL = TRUE, RADIO_CHANNEL_ALMAYER = TRUE)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_INTEL = TRUE, RADIO_CHANNEL_ALMAYER = TRUE, RADIO_CHANNEL_COLONY = TRUE)
/// Used by the Mortar Crew in WO game mode - intently has no squad radio access
/obj/item/device/encryptionkey/mortar
name = "\improper Mortar Crew Radio Encryption Key"
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index 631ef8fa0e2c..e8dcdac34222 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -961,20 +961,23 @@
//CMB Headsets
/obj/item/device/radio/headset/distress/CMB
name = "\improper CMB Earpiece"
- desc = "A sleek headset used by The Colonial Marshal Bureau, crafted in Sol. Low profile and comfortable. No one is above the law. Featured channels include: ; - CMB, :g - public, :v - marine command, :m - medbay, :t - intel."
+ desc = "A sleek headset used by The Colonial Marshal Bureau, crafted in Sol. Low profile and comfortable. No one is above the law. Featured channels include: ; - CMB, :o - Colony, :g - public, :v - marine command, :m - medbay, :t - intel."
frequency = CMB_FREQ
icon_state = "cmb_headset"
initial_keys = list(/obj/item/device/encryptionkey/cmb)
has_hud = TRUE
hud_type = MOB_HUD_FACTION_USCM
+/obj/item/device/radio/headset/distress/CMB/limited
+ name = "\improper Damaged CMB Earpiece"
+ desc = "A sleek headset used by The Colonial Marshal Bureau, crafted in Sol. Low profile and comfortable. No one is above the law. This one is damaged, so the channels are: ; - CMB, :o - Colony."
+ initial_keys = list(/obj/item/device/encryptionkey/colony)
+
/obj/item/device/radio/headset/distress/CMB/ICC
name = "\improper ICC Liaison Headset"
- desc = "An expensive headset used by The Interstellar Commerce Commission. This one in particular has a liaison chip with the CMB. Featured channels include: ; - CMB, :g - public, :v - marine command, :m - medbay, :t - intel, :y - Weyland-Yutani."
- frequency = CMB_FREQ
+ desc = "An expensive headset used by The Interstellar Commerce Commission. This one in particular has a liaison chip with the CMB. Featured channels include: ; - CMB, :o - Colony, :g - public, :v - marine command, :m - medbay, :t - intel, :y - Weyland-Yutani."
icon_state = "wy_headset"
initial_keys = list(/obj/item/device/encryptionkey/WY, /obj/item/device/encryptionkey/cmb)
- has_hud = TRUE
/obj/item/device/radio/headset/almayer/highcom
name = "USCM High Command headset"
diff --git a/code/game/objects/items/explosives/mine.dm b/code/game/objects/items/explosives/mine.dm
index 6b8c9bccd299..742a5f314c4a 100644
--- a/code/game/objects/items/explosives/mine.dm
+++ b/code/game/objects/items/explosives/mine.dm
@@ -198,6 +198,8 @@
return
if(L.get_target_lock(iff_signal) || isrobot(L))
return
+ if(HAS_TRAIT(L, TRAIT_ABILITY_BURROWED))
+ return
L.visible_message(SPAN_DANGER("[icon2html(src, viewers(src))] The [name] clicks as [L] moves in front of it."), \
SPAN_DANGER("[icon2html(src, L)] The [name] clicks as you move in front of it."), \
SPAN_DANGER("You hear a click."))
diff --git a/code/game/objects/items/pamphlets.dm b/code/game/objects/items/pamphlets.dm
index 683fbb2540f4..dd96f275ef17 100644
--- a/code/game/objects/items/pamphlets.dm
+++ b/code/game/objects/items/pamphlets.dm
@@ -96,8 +96,8 @@
user.hud_set_squad()
var/obj/item/card/id/ID = user.wear_id
- ID.set_assignment((user.assigned_squad ? (user.assigned_squad.name + " ") : "") + "Squad Spotter")
- GLOB.data_core.manifest_modify(user.real_name, WEAKREF(user), "Squad Spotter")
+ ID.set_assignment((user.assigned_squad ? (user.assigned_squad.name + " ") : "") + "Spotter")
+ GLOB.data_core.manifest_modify(user.real_name, WEAKREF(user), "Spotter")
/obj/item/pamphlet/skill/machinegunner
name = "heavy machinegunner instructional pamphlet"
diff --git a/code/game/objects/items/reagent_containers/blood_pack.dm b/code/game/objects/items/reagent_containers/blood_pack.dm
index 450cdde2fa00..8e29a26c2ecd 100644
--- a/code/game/objects/items/reagent_containers/blood_pack.dm
+++ b/code/game/objects/items/reagent_containers/blood_pack.dm
@@ -13,7 +13,9 @@
var/mode = BLOOD_BAG_INJECTING
var/mob/living/carbon/human/connected_to
+ var/mob/living/carbon/human/connected_from
var/blood_type = null
+ var/datum/beam/current_beam
/obj/item/reagent_container/blood/Initialize()
. = ..()
@@ -32,6 +34,12 @@
if(10 to 50) icon_state = "half"
if(51 to INFINITY) icon_state = "full"
+/obj/item/reagent_container/blood/proc/update_beam()
+ if(current_beam)
+ QDEL_NULL(current_beam)
+ else if(connected_from && connected_to)
+ current_beam = connected_from.beam(connected_to, "iv_tube")
+
/obj/item/reagent_container/blood/attack(mob/attacked_mob, mob/user)
. = ..()
@@ -44,7 +52,10 @@
user.visible_message("[user] detaches [src] from [connected_to].", \
"You detach [src] from [connected_to].")
connected_to.active_transfusions -= src
+ connected_to.base_pixel_x = 0
connected_to = null
+ connected_from = null
+ update_beam()
return
if(!skillcheck(user, SKILL_SURGERY, SKILL_SURGERY_NOVICE))
@@ -60,10 +71,13 @@
if(istype(attacked_mob, /mob/living/carbon/human))
connected_to = attacked_mob
+ connected_from = user
connected_to.active_transfusions += src
+ connected_to.base_pixel_x = 5
START_PROCESSING(SSobj, src)
user.visible_message("[user] attaches \the [src] to [connected_to].", \
"You attach \the [src] to [connected_to].")
+ update_beam()
/obj/item/reagent_container/blood/process()
//if we're not connected to anything stop doing stuff
@@ -106,6 +120,10 @@
connected_to.take_blood(src, amount)
+/obj/item/reagent_container/blood/dropped()
+ ..()
+ bad_disconnect()
+
///Used to standardize effects of a blood bag disconnecting improperly
/obj/item/reagent_container/blood/proc/bad_disconnect()
if(!connected_to)
@@ -116,7 +134,10 @@
if(connected_to.pain.feels_pain)
connected_to.emote("scream")
connected_to.active_transfusions -= src
+ connected_to.base_pixel_x = 0
connected_to = null
+ connected_from = null
+ update_beam()
/obj/item/reagent_container/blood/verb/toggle_mode()
set category = "Object"
diff --git a/code/game/objects/items/reagent_containers/food/snacks.dm b/code/game/objects/items/reagent_containers/food/snacks.dm
index 7dae94bfe4eb..c0e11dac8eb3 100644
--- a/code/game/objects/items/reagent_containers/food/snacks.dm
+++ b/code/game/objects/items/reagent_containers/food/snacks.dm
@@ -66,6 +66,10 @@
if(issynth(C))
fullness = 200 //Synths never get full
+ if(HAS_TRAIT(M, TRAIT_CANNOT_EAT)) //Do not feed the Working Joes
+ to_chat(user, SPAN_DANGER("[user == M ? "You are" : "[M] is"] unable to eat!"))
+ return
+
if(fullness > 540)
C.overeat_cooldown = world.time + OVEREAT_TIME
diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm
index f0a235b2301f..c86003b251da 100644
--- a/code/game/objects/items/storage/bags.dm
+++ b/code/game/objects/items/storage/bags.dm
@@ -30,24 +30,28 @@
item_state = "trashbag"
w_class = SIZE_LARGE
- max_w_class = SIZE_SMALL
- storage_slots = 21
+ max_w_class = SIZE_MEDIUM
+ storage_slots = null
+ max_storage_space = 21 //equivalent to an IMP backpack
can_hold = list() // any
cant_hold = list(/obj/item/disk/nuclear, /obj/item/weapon/throwing_knife)
storage_flags = STORAGE_GATHER_SIMULTAENOUSLY|STORAGE_QUICK_GATHER|STORAGE_CLICK_GATHER
+ flags_equip_slot = NONE
/obj/item/storage/bag/trash/update_icon()
- if(contents.len == 0)
+ var/sum_storage_cost = 0
+ for(var/obj/item/item in contents)
+ sum_storage_cost += item.get_storage_cost()
+
+ if(!sum_storage_cost)
icon_state = "trashbag0"
- else if(contents.len < 12)
+ else if(sum_storage_cost < round(max_storage_space * 0.35))
icon_state = "trashbag1"
- else if(contents.len < 21)
+ else if(sum_storage_cost < round(max_storage_space * 0.7))
icon_state = "trashbag2"
- else icon_state = "trashbag3"
-
-/obj/item/storage/bag/trash/open(mob/user)
- return
+ else
+ icon_state = "trashbag3"
// -----------------------------
// Plastic Bag
@@ -183,7 +187,7 @@
//Turned numbered display on. Appears to work as intended, despite above comment -- Vanagandr.
/obj/item/storage/bag/sheetsnatcher/orient2hud()
- var/adjusted_contents = contents.len
+ var/adjusted_contents = length(contents)
//Numbered contents display
var/list/datum/numbered_display/numbered_contents
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 43624f913037..015d8a5eca60 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -106,6 +106,13 @@
new /obj/item/tool/wirecutters(src)
new /obj/item/device/t_scanner(src)
+/obj/item/storage/belt/utility/full/pred
+ name = "\improper Yautja toolbelt"
+ desc = "A modular belt with various clips. This version lacks any hunting functionality, and is commonly used by engineers to transport important tools."
+ icon = 'icons/obj/items/hunter/pred_gear.dmi'
+ icon_state = "utilitybelt_pred"
+ item_state = "utility"
+
/obj/item/storage/belt/medical
name = "\improper M276 pattern medical storage rig"
desc = "The M276 is the standard load-bearing equipment of the USCM. It consists of a modular belt with various clips. This version is a less common configuration, designed to transport medical supplies and pistol ammunition. \nRight click its sprite and click \"toggle belt mode\" to take pills out of bottles by simply clicking them."
diff --git a/code/game/objects/items/storage/pouch.dm b/code/game/objects/items/storage/pouch.dm
index 198e5b3b9960..02e4b3866271 100644
--- a/code/game/objects/items/storage/pouch.dm
+++ b/code/game/objects/items/storage/pouch.dm
@@ -612,13 +612,12 @@
name = "explosive pouch"
desc = "It can carry grenades, plastic explosives, mine boxes, and other explosives."
icon_state = "large_explosive"
- storage_slots = 3
+ storage_slots = 6
max_w_class = SIZE_MEDIUM
can_hold = list(
/obj/item/explosive/plastic,
/obj/item/explosive/mine,
/obj/item/explosive/grenade,
- /obj/item/storage/box/explosive_mines,
)
/obj/item/storage/pouch/explosive/attackby(obj/item/W, mob/user)
diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/storage.dm
index dd8f10a51559..36f946efdfdc 100644
--- a/code/game/objects/items/storage/storage.dm
+++ b/code/game/objects/items/storage/storage.dm
@@ -799,9 +799,9 @@ W is always an item. stop_warning prevents messaging. user may be null.**/
storage_close(watcher)
/obj/item/storage/proc/dump_objectives()
- for(var/obj/item/I in src)
- if(I.is_objective)
- I.forceMove(loc)
+ for(var/obj/item/cur_item in src)
+ if(cur_item.is_objective)
+ remove_from_storage(cur_item, loc)
/obj/item/storage/Destroy()
diff --git a/code/game/objects/items/tools/flame_tools.dm b/code/game/objects/items/tools/flame_tools.dm
index db35d3300ae7..6ebd8ee5982e 100644
--- a/code/game/objects/items/tools/flame_tools.dm
+++ b/code/game/objects/items/tools/flame_tools.dm
@@ -170,13 +170,20 @@ CIGARETTE PACKETS ARE IN FANCY.DM
damtype = "brute"
icon_state = "[initial(icon_state)]_burnt"
item_state = "cigoff"
- if(user && loc != user)
- user.SetLuminosity(0, FALSE, src)
SetLuminosity(0)
name = burnt_name
desc = "A match. This one has seen better days."
STOP_PROCESSING(SSobj, src)
+ if(user)
+ user.SetLuminosity(0, FALSE, src)
+ return
+
+ if(ismob(loc))
+ user = loc
+ user.SetLuminosity(0, FALSE, src)
+ return
+
/obj/item/tool/match/paper
name = "paper match"
desc = "A simple match stick, used for lighting fine smokables."
diff --git a/code/game/objects/items/tools/maintenance_tools.dm b/code/game/objects/items/tools/maintenance_tools.dm
index 25bcefc1cc34..2560c5ff91e8 100644
--- a/code/game/objects/items/tools/maintenance_tools.dm
+++ b/code/game/objects/items/tools/maintenance_tools.dm
@@ -557,8 +557,6 @@
if(attacked_door.locked) //Bolted
to_chat(user, SPAN_DANGER("You can't pry open [attacked_door] while it is bolted shut."))
return
- if(!attacked_door.arePowerSystemsOn()) //Opens like normal if unpowered
- return FALSE
if(requires_superstrength_pry)
if(!HAS_TRAIT(user, TRAIT_SUPER_STRONG)) //basically IS_PRY_CAPABLE_CROWBAR
@@ -645,7 +643,7 @@
resin_door.Open()
return
- if(istype(attacked_obj, /turf/open/floor))
+ else if(istype(attacked_obj, /turf/open/floor))
var/turf/open/floor/flooring = attacked_obj
if(crowbar_mode && user.a_intent == INTENT_HELP) //Only pry flooring on help intent
diff --git a/code/game/objects/items/tools/misc_tools.dm b/code/game/objects/items/tools/misc_tools.dm
index 727f6c5c7677..1bcebd9f1ea3 100644
--- a/code/game/objects/items/tools/misc_tools.dm
+++ b/code/game/objects/items/tools/misc_tools.dm
@@ -275,6 +275,25 @@
desc = "It's an invisible pen marker."
pen_colour = "white"
+/obj/item/tool/pen/fountain
+ desc = "A luxurious fountain pen, embossed with gold accents. Its intricate mechanics allow the user to switch between various ink colors with a simple twist."
+ name = "fountain pen"
+ icon_state = "fountain_pen"
+ item_state = "fountain_pen"
+ matter = list("metal" = 20, "gold" = 10)
+ var/static/list/colour_list = list("red", "blue", "green", "yellow", "purple", "pink", "brown", "black", "orange") // Can add more colors as required
+ var/current_colour_index = 1
+
+/obj/item/tool/pen/fountain/attack_self(mob/living/carbon/human/user)
+ if(on)
+ current_colour_index = (current_colour_index % length(colour_list)) + 1
+ pen_colour = colour_list[current_colour_index]
+ balloon_alert(user,"you twist the pen and change the ink color to [pen_colour].")
+ if(clicky)
+ playsound(user.loc, 'sound/items/pen_click_on.ogg', 100, 1, 5)
+ update_pen_state()
+ else
+ ..()
/obj/item/tool/pen/attack(mob/M as mob, mob/user as mob)
if(!ismob(M))
diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm
index ec7ee0b173da..cdab7db87ed7 100644
--- a/code/game/objects/items/weapons/swords_axes_etc.dm
+++ b/code/game/objects/items/weapons/swords_axes_etc.dm
@@ -45,7 +45,7 @@
w_class = SIZE_SMALL
force = MELEE_FORCE_WEAK
var/on = 0
- var/stunforce = 60
+ var/stun_force = 10
/obj/item/weapon/telebaton/attack(mob/living/carbon/human/target, mob/living/user)
if(!istype(target) || !on)
@@ -67,6 +67,7 @@
item_state = "telebaton_1"
w_class = SIZE_MEDIUM
force = MELEE_FORCE_VERY_STRONG
+ stun_force = 40
attack_verb = list("smacked", "struck", "slapped", "beat")
else
user.visible_message(SPAN_NOTICE("Using a smooth, practiced movement, [user] collapses \his [src]."),\
@@ -75,7 +76,8 @@
icon_state = "telebaton_0"
item_state = "telebaton_0"
w_class = SIZE_SMALL
- force = MELEE_FORCE_WEAK//not so robust now
+ force = MELEE_FORCE_WEAK
+ stun_force = initial(stun_force)
attack_verb = list("hit", "punched")
if(istype(user,/mob/living/carbon/human))
@@ -100,8 +102,17 @@
user.flick_attack_overlay(target, "punch")
log_interact(user, target, "[key_name(user)] stunned [key_name(target)] with \the [src]")
// Hit 'em
+ var/final_stun_force = stun_force
+ var/datum/skills/user_skills = user.skills
+ if(user_skills)
+ switch(user_skills.get_skill_level(SKILL_POLICE))
+ if(SKILL_POLICE_FLASH)
+ final_stun_force *= 1.5
+ if(SKILL_POLICE_SKILLED)
+ final_stun_force *= 3
+
var/target_zone = check_zone(user.zone_selected)
- target.apply_stamina_damage(stunforce, target_zone, ARMOR_MELEE)
+ target.apply_stamina_damage(final_stun_force, target_zone, ARMOR_MELEE)
if(target.stamina.current_stamina <= 0)
user.visible_message(SPAN_DANGER("[user] knocks down [target] with \the [src]!"),\
SPAN_WARNING("You knock down [target] with \the [src]!"))
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index e98e3b527c5f..b92624cf4201 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -223,6 +223,7 @@
/obj/proc/afterbuckle(mob/M as mob) // Called after somebody buckled / unbuckled
handle_rotation()
+ SEND_SIGNAL(src, COSMIG_OBJ_AFTER_BUCKLE, buckled_mob)
return buckled_mob
/obj/proc/unbuckle()
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index e6c215d0208f..8c8d6b6920a8 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -135,15 +135,17 @@
return stored_units
/obj/structure/closet/proc/store_mobs(stored_units)
- for(var/mob/M in src.loc)
+ for(var/mob/cur_mob in src.loc)
if(stored_units + mob_size > storage_capacity)
break
- if(istype (M, /mob/dead/observer))
+ if(istype (cur_mob, /mob/dead/observer))
continue
- if(M.buckled)
+ if(cur_mob.buckled)
+ continue
+ if(cur_mob.anchored)
continue
- M.forceMove(src)
+ cur_mob.forceMove(src)
stored_units += mob_size
return stored_units
@@ -243,19 +245,22 @@
else if(istype(W, /obj/item/packageWrap) || istype(W, /obj/item/explosive/plastic))
return
else if(iswelder(W))
+ if(material != MATERIAL_METAL && material != MATERIAL_PLASTEEL)
+ to_chat(user, SPAN_WARNING("You cannot weld [material]!"))
+ return FALSE//Can't weld wood/plastic.
if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH))
to_chat(user, SPAN_WARNING("You need a stronger blowtorch!"))
- return
+ return FALSE
var/obj/item/tool/weldingtool/WT = W
if(!WT.isOn())
to_chat(user, SPAN_WARNING("\The [WT] needs to be on!"))
- return
+ return FALSE
if(!WT.remove_fuel(0, user))
to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task."))
- return
+ return FALSE
playsound(src, 'sound/items/Welder.ogg', 25, 1)
if(!do_after(user, 10 * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD))
- return
+ return FALSE
welded = !welded
update_icon()
for(var/mob/M as anything in viewers(src))
@@ -264,9 +269,9 @@
if(isxeno(user))
var/mob/living/carbon/xenomorph/opener = user
src.attack_alien(opener)
- return
+ return FALSE
src.attack_hand(user)
- return
+ return TRUE
/obj/structure/closet/MouseDrop_T(atom/movable/O, mob/user)
if(!opened)
diff --git a/code/game/objects/structures/props.dm b/code/game/objects/structures/props.dm
index f6905d4d044d..1a91650c620a 100644
--- a/code/game/objects/structures/props.dm
+++ b/code/game/objects/structures/props.dm
@@ -24,7 +24,7 @@
. = ..()
if(isxeno(user))
return
- else if (ishuman(user) && istype(W, /obj/item/tool/wrench))
+ else if (ishuman(user) && HAS_TRAIT(W, TRAIT_TOOL_WRENCH))
on = !on
visible_message("You wrench the controls of \the [src]. The drill jumps to life." , "[user] wrenches the controls of \the [src]. The drill jumps to life.")
@@ -501,7 +501,7 @@
. = ..()
if(isxeno(user))
return
- else if (ishuman(user) && istype(W, /obj/item/tool/crowbar))
+ else if (ishuman(user) && HAS_TRAIT(W, TRAIT_TOOL_CROWBAR))
on = !on
visible_message("You pry at the control valve on [src]. The machine shudders." , "[user] pries at the control valve on [src]. The entire machine shudders.")
diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
index bda4b60ca21e..7979994915f4 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
@@ -208,6 +208,20 @@
return
..()
+/obj/structure/bed/roller/Collided(atom/movable/moving_atom)
+ if(!isxeno(moving_atom))
+ return ..()
+
+ if(buckled_mob && buckled_mob.stat != DEAD)
+ return ..()
+
+ if(buckled_bodybag)
+ var/mob/mob_in_bodybag = locate(/mob) in buckled_bodybag
+ if(mob_in_bodybag && mob_in_bodybag.stat != DEAD)
+ return ..()
+
+ return
+
/obj/item/roller
name = "roller bed"
desc = "A collapsed roller bed that can be carried around."
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 730263ad7a3e..db3ce98339a3 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -326,11 +326,13 @@
/// Checks whether a table is a straight line along a given axis
/obj/structure/surface/table/proc/straight_table_check(direction)
var/obj/structure/surface/table/table = src
- while(table)
+ var/obj/structure/surface/table/side_table
+ var/tables_count = 7 // Lazy extra safety against infinite loops. If table big, can't flip, i guess.
+ while(--tables_count)
// Check whether there are connected tables perpendicular to the axis
for(var/angle in list(-90, 90))
- table = locate() in get_step(loc, turn(direction, angle))
- if(table && !table.flipped)
+ side_table = locate() in get_step(table, turn(direction, angle))
+ if(side_table && !side_table.flipped)
return FALSE
table = locate() in get_step(table, direction)
if(!table || table.flipped)
@@ -339,6 +341,8 @@
var/obj/structure/surface/table/reinforced/reinforced_table = table
if(reinforced_table.status == RTABLE_NORMAL)
return FALSE
+ if(!tables_count)
+ return FALSE
return TRUE
/obj/structure/surface/table/verb/do_flip()
@@ -421,7 +425,7 @@
to_chat(usr, SPAN_WARNING("You have moved a table too recently."))
return FALSE
- if(!skip_straight_check && (!straight_table_check(turn(direction, 90)) || !straight_table_check(turn(direction, -90))))
+ if(!skip_straight_check && !(straight_table_check(turn(direction, 90)) && straight_table_check(turn(direction, -90))))
to_chat(usr, SPAN_WARNING("[src] is too wide to be flipped."))
return FALSE
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index 5c084ce94cbc..cccc1211bfb0 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -121,7 +121,7 @@
cistern_overlay.icon_state = "cistern[cistern]"
/obj/structure/toilet/attackby(obj/item/I, mob/living/user)
- if(istype(I, /obj/item/tool/crowbar))
+ if(HAS_TRAIT(I, TRAIT_TOOL_CROWBAR))
to_chat(user, SPAN_NOTICE("You start to [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"]."))
playsound(loc, 'sound/effects/stonedoor_openclose.ogg', 25, 1)
if(do_after(user, 30, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD))
diff --git a/code/game/turfs/floor.dm b/code/game/turfs/floor.dm
index 4a600d4d033b..dc2cda0c2c2a 100644
--- a/code/game/turfs/floor.dm
+++ b/code/game/turfs/floor.dm
@@ -148,7 +148,7 @@
if(src.weeds)
return weeds.attackby(hitting_item,user)
- if(istype(hitting_item, /obj/item/tool/crowbar) && (tool_flags & (REMOVE_CROWBAR|BREAK_CROWBAR)))
+ if(HAS_TRAIT(hitting_item, TRAIT_TOOL_CROWBAR) && (tool_flags & (REMOVE_CROWBAR|BREAK_CROWBAR)))
if(broken || burnt)
to_chat(user, SPAN_WARNING("You remove the broken tiles."))
else
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 54cfdf6e34ea..6e98f4d7c065 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -500,7 +500,7 @@
var/area/A = get_area(src)
switch(A.ceiling)
if(CEILING_GLASS)
- return "The ceiling above is glass. That's not going stop anything."
+ return "The ceiling above is glass. That's not going to stop anything."
if(CEILING_METAL)
return "The ceiling above is metal. You can't see through it with a camera from above, but that's not going to stop anything."
if(CEILING_UNDERGROUND_ALLOW_CAS)
diff --git a/code/game/turfs/walls/wall_types.dm b/code/game/turfs/walls/wall_types.dm
index 702cbd35560c..04c5a0735824 100644
--- a/code/game/turfs/walls/wall_types.dm
+++ b/code/game/turfs/walls/wall_types.dm
@@ -710,6 +710,17 @@
for(var/obj/effect/alien/weeds/node/weed_node in contents)
qdel(weed_node)
+ if(hivenumber == XENO_HIVE_NORMAL)
+ RegisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING, PROC_REF(forsaken_handling))
+
+/turf/closed/wall/resin/proc/forsaken_handling()
+ SIGNAL_HANDLER
+ if(is_ground_level(z))
+ hivenumber = XENO_HIVE_FORSAKEN
+ set_hive_data(src, XENO_HIVE_FORSAKEN)
+
+ UnregisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING)
+
/turf/closed/wall/resin/pillar
name = "resin pillar segment"
hull = TRUE
diff --git a/code/game/turfs/walls/walls.dm b/code/game/turfs/walls/walls.dm
index 411ff8182661..3599d5bb980b 100644
--- a/code/game/turfs/walls/walls.dm
+++ b/code/game/turfs/walls/walls.dm
@@ -98,11 +98,15 @@
qdel(found_nest) //nests are built on walls, no walls, no nest
/turf/closed/wall/MouseDrop_T(mob/current_mob, mob/user)
+ if(!ismob(current_mob))
+ return
+
if(acided_hole)
if(current_mob == user && isxeno(user))
acided_hole.use_wall_hole(user)
return
- if(isxeno(user))
+
+ if(isxeno(user) && istype(user.get_active_hand(), /obj/item/grab))
var/mob/living/carbon/xenomorph/user_as_xenomorph = user
user_as_xenomorph.do_nesting_host(current_mob, src)
..()
diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm
index bb0e53a012cb..8a249d297cbe 100644
--- a/code/game/verbs/who.dm
+++ b/code/game/verbs/who.dm
@@ -158,7 +158,7 @@
var/dat = ""
var/list/mappings
if(CONFIG_GET(flag/show_manager))
- LAZYSET(mappings, "Management", R_HOST)
+ LAZYSET(mappings, "Management", R_PERMISSIONS)
if(CONFIG_GET(flag/show_devs))
LAZYSET(mappings, "Maintainers", R_PROFILER)
LAZYSET(mappings, "Admins", R_ADMIN)
diff --git a/code/global.dm b/code/global.dm
index c5c6bdf84066..28a8926cade4 100644
--- a/code/global.dm
+++ b/code/global.dm
@@ -12,21 +12,21 @@
#define R_POSSESS (1<<5)
#define R_PERMISSIONS (1<<6)
#define R_STEALTH (1<<7)
-#define R_REJUVINATE (1<<8)
-#define R_COLOR (1<<9)
-#define R_VAREDIT (1<<10)
-#define R_SOUNDS (1<<11)
-#define R_SPAWN (1<<12)
-#define R_MOD (1<<13)
-#define R_MENTOR (1<<14)
-#define R_HOST (1<<15)
-#define R_PROFILER (1<<16)
-#define R_NOLOCK (1<<17)
-#define R_EVENT (1<<18)
-
-/// The sum of all other rank permissions.
-#define R_EVERYTHING ((1<<19)-1)
-
+#define R_COLOR (1<<8)
+#define R_VAREDIT (1<<9)
+#define R_SOUNDS (1<<10)
+#define R_SPAWN (1<<11)
+#define R_MOD (1<<12)
+#define R_MENTOR (1<<13)
+#define R_HOST (1<<14)
+#define R_PROFILER (1<<15)
+#define R_NOLOCK (1<<16)
+#define R_EVENT (1<<17)
+
+/// The sum of all other rank permissions, other than host or profiler.
+#define RL_EVERYTHING (R_BUILDMODE|R_ADMIN|R_BAN|R_SERVER|R_DEBUG|R_PERMISSIONS|R_POSSESS|R_STEALTH|R_COLOR|R_VAREDIT|R_EVENT|R_SOUNDS|R_NOLOCK|R_SPAWN|R_MOD|R_MENTOR)
+/// Truely everything
+#define RL_HOST (RL_EVERYTHING|R_HOST|R_PROFILER)
// 512.1430 increases maximum bit flags from 16 to 24, so the following flags should be available for future changes:
//=================================================
diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm
index fab0c24622fe..b64b1e4682fd 100644
--- a/code/modules/admin/NewBan.dm
+++ b/code/modules/admin/NewBan.dm
@@ -226,6 +226,9 @@ var/savefile/Banlist
RemoveBan(A)
/client/proc/cmd_admin_do_ban(mob/M)
+ if(IsAdminAdvancedProcCall())
+ alert_proccall("cmd_admin_do_ban")
+ return PROC_BLOCKED
if(!check_rights(R_BAN|R_MOD)) return
if(!ismob(M)) return
diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm
index dd67a963047e..cbeb1169c807 100644
--- a/code/modules/admin/admin_ranks.dm
+++ b/code/modules/admin/admin_ranks.dm
@@ -34,17 +34,17 @@ var/list/admin_ranks = list() //list of all ranks with associated rights
if("permissions","rights") rights |= R_PERMISSIONS
if("possess") rights |= R_POSSESS
if("stealth") rights |= R_STEALTH
- if("rejuv","rejuvinate") rights |= R_REJUVINATE
if("color") rights |= R_COLOR
if("varedit") rights |= R_VAREDIT
if("event") rights |= R_EVENT
- if("everything","host","all") rights |= (R_HOST|R_BUILDMODE|R_ADMIN|R_BAN|R_SERVER|R_DEBUG|R_PERMISSIONS|R_POSSESS|R_STEALTH|R_REJUVINATE|R_COLOR|R_VAREDIT|R_EVENT|R_SOUNDS|R_NOLOCK|R_SPAWN|R_MOD|R_MENTOR)
if("sound","sounds") rights |= R_SOUNDS
if("nolock") rights |= R_NOLOCK
if("spawn","create") rights |= R_SPAWN
if("mod") rights |= R_MOD
if("mentor") rights |= R_MENTOR
if("profiler") rights |= R_PROFILER
+ if("host") rights |= RL_HOST
+ if("everything") rights |= RL_EVERYTHING
admin_ranks[rank] = rights
previous_rights = rights
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index ba5d52a23f97..6154bb4f8c32 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -27,7 +27,6 @@ var/list/admin_verbs_default = list(
/client/proc/invismin,
/client/proc/set_explosive_antigrief,
/client/proc/check_explosive_antigrief,
- /client/proc/cmd_mod_say,
/client/proc/dsay,
/client/proc/chem_panel, /*chem panel, allows viewing, editing and creation of reagent and chemical_reaction datums*/
/client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/
@@ -68,6 +67,8 @@ var/list/admin_verbs_default = list(
/datum/admins/proc/alertall,
/datum/admins/proc/imaginary_friend,
/client/proc/toggle_ares_ping,
+ /client/proc/cmd_admin_say, /*staff-only ooc chat*/
+ /client/proc/cmd_mod_say, /* alternate way of typing asay, no different than cmd_admin_say */
)
var/list/admin_verbs_admin = list(
@@ -80,7 +81,6 @@ var/list/admin_verbs_admin = list(
/client/proc/toggleprayers, /*toggles prayers on/off*/
/client/proc/toggle_hear_radio, /*toggles whether we hear the radio*/
/client/proc/event_panel,
- /client/proc/cmd_admin_say, /*admin-only ooc chat*/
/client/proc/free_slot, /*frees slot for chosen job*/
/client/proc/modify_slot,
/client/proc/cmd_admin_rejuvenate,
diff --git a/code/modules/admin/callproc.dm b/code/modules/admin/callproc.dm
index f84121785360..1af1f5aa9fa3 100644
--- a/code/modules/admin/callproc.dm
+++ b/code/modules/admin/callproc.dm
@@ -72,7 +72,8 @@ GLOBAL_PROTECT(LastAdminCalledProc)
*/
/proc/HandleUserlessProcCall(user, datum/target, procname, list/arguments)
if(IsAdminAdvancedProcCall())
- return
+ alert_proccall("HandleUserlessProcCall")
+ return PROC_BLOCKED
var/mob/proccall_handler/handler = GLOB.AdminProcCallHandler
handler.add_caller(user)
var/lastusr = usr
@@ -90,7 +91,8 @@ GLOBAL_PROTECT(LastAdminCalledProc)
*/
/proc/HandleUserlessSDQL(user, query_text)
if(IsAdminAdvancedProcCall())
- return
+ alert_proccall("HandleUserlessSDQL")
+ return PROC_BLOCKED
var/mob/proccall_handler/handler = GLOB.AdminProcCallHandler
handler.add_caller(user)
@@ -224,6 +226,10 @@ GLOBAL_PROTECT(LastAdminCalledProc)
/proc/IsAdminAdvancedProcCall()
return (GLOB.AdminProcCaller && GLOB.AdminProcCaller == usr?.client?.ckey) || (GLOB.AdminProcCallHandler && usr == GLOB.AdminProcCallHandler)
+/proc/alert_proccall(procname = "Unknown")
+ to_chat(usr, SPAN_BOLDWARNING("Warning: Force attempt has been logged."))
+ message_admins("[key_name(usr)] has attempted to execute a restricted proc. ([procname])")
+
/client/proc/callproc_datum(datum/called_datum as null|area|mob|obj|turf)
set category = "Debug"
set name = "Datum ProcCall"
diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm
index a119d4c0519f..e7559f3aa4fd 100644
--- a/code/modules/admin/holder2.dm
+++ b/code/modules/admin/holder2.dm
@@ -129,15 +129,18 @@ you will have to do something like if(client.admin_holder.rights & R_ADMIN) your
return 0
/client/proc/deadmin()
+ if(IsAdminAdvancedProcCall())
+ alert_proccall("deadmin")
+ return PROC_BLOCKED
if(admin_holder)
admin_holder.disassociate()
QDEL_NULL(admin_holder)
- return 1
+ return TRUE
/client/proc/readmin()
if(admin_datums[ckey])
admin_datums[ckey].associate(src)
- return 1
+ return TRUE
/datum/admins/proc/check_for_rights(rights_required)
if(rights_required && !(rights_required & rights))
diff --git a/code/modules/admin/tabs/admin_tab.dm b/code/modules/admin/tabs/admin_tab.dm
index c0ffeada9883..5a98faa6ddaa 100644
--- a/code/modules/admin/tabs/admin_tab.dm
+++ b/code/modules/admin/tabs/admin_tab.dm
@@ -218,30 +218,52 @@
message_admins("[key_name(usr)] used Toggle Wake In View.")
+/client/proc/cmd_mod_say(msg as text)
+ set name = "Msay" // This exists for ease of admins who were used to using msay instead of asay
+ set category = "Admin"
+ set hidden = TRUE
+
+ cmd_admin_say(msg)
+
/client/proc/cmd_admin_say(msg as text)
set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite
set category = "Admin"
set hidden = TRUE
- if(!check_rights(R_ADMIN))
+ if(!check_rights(R_ADMIN|R_MOD))
return
msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
- if(!msg)
+
+ if (!msg)
return
- log_adminpm("ADMIN : [key_name(src)] : [msg]")
- REDIS_PUBLISH("byond.asay", "author" = src.key, "message" = strip_html(msg), "host" = ishost(src), "rank" = admin_holder.rank)
+ REDIS_PUBLISH("byond.asay", "author" = src.key, "message" = strip_html(msg), "admin" = CLIENT_HAS_RIGHTS(src, R_ADMIN), "rank" = admin_holder.rank)
+
+ if(findtext(msg, "@") || findtext(msg, "#"))
+ var/list/link_results = check_asay_links(msg)
+ if(length(link_results))
+ msg = link_results[ASAY_LINK_NEW_MESSAGE_INDEX]
+ link_results[ASAY_LINK_NEW_MESSAGE_INDEX] = null
+ var/list/pinged_admin_clients = link_results[ASAY_LINK_PINGED_ADMINS_INDEX]
+ for(var/iter_ckey in pinged_admin_clients)
+ var/client/iter_admin_client = pinged_admin_clients[iter_ckey]
+ if(!iter_admin_client?.admin_holder)
+ continue
+ window_flash(iter_admin_client)
+ SEND_SOUND(iter_admin_client.mob, sound('sound/misc/asay_ping.ogg'))
+
+ log_adminpm("ADMIN: [key_name(src)] : [msg]")
var/color = "adminsay"
if(ishost(usr))
color = "headminsay"
- if(check_rights(R_ADMIN,0))
- msg = "ADMIN:[key_name(usr, 1)] [ADMIN_JMP_USER(mob)]: [msg]"
- for(var/client/C in GLOB.admins)
- if(R_ADMIN & C.admin_holder.rights)
- to_chat(C, msg)
+ var/channel = "ADMIN:"
+ channel = "[admin_holder.rank]:"
+ for(var/client/client as anything in GLOB.admins)
+ if((R_ADMIN|R_MOD) & client.admin_holder.rights)
+ to_chat(client, "[channel][key_name(src,1)] [ADMIN_JMP_USER(mob)]: [msg]")
/datum/admins/proc/alertall()
set name = "Alert All"
@@ -328,50 +350,6 @@
var/msg = input(src, null, "asay \"text\"") as text|null
cmd_admin_say(msg)
-/client/proc/cmd_mod_say(msg as text)
- set name = "Msay"
- set category = "Admin"
- set hidden = TRUE
-
- if(!check_rights(R_ADMIN|R_MOD))
- return
-
- msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
-
- if (!msg)
- return
-
- REDIS_PUBLISH("byond.msay", "author" = src.key, "message" = strip_html(msg), "admin" = CLIENT_HAS_RIGHTS(src, R_ADMIN), "rank" = admin_holder.rank)
-
- if(findtext(msg, "@") || findtext(msg, "#"))
- var/list/link_results = check_asay_links(msg)
- if(length(link_results))
- msg = link_results[ASAY_LINK_NEW_MESSAGE_INDEX]
- link_results[ASAY_LINK_NEW_MESSAGE_INDEX] = null
- var/list/pinged_admin_clients = link_results[ASAY_LINK_PINGED_ADMINS_INDEX]
- for(var/iter_ckey in pinged_admin_clients)
- var/client/iter_admin_client = pinged_admin_clients[iter_ckey]
- if(!iter_admin_client?.admin_holder)
- continue
- window_flash(iter_admin_client)
- SEND_SOUND(iter_admin_client.mob, sound('sound/misc/asay_ping.ogg'))
-
- log_adminpm("MOD: [key_name(src)] : [msg]")
-
- var/color = "mod"
- if (check_rights(R_ADMIN,0))
- color = "adminmod"
-
- var/channel = "MOD:"
- channel = "[admin_holder.rank]:"
- for(var/client/C in GLOB.admins)
- if((R_ADMIN|R_MOD) & C.admin_holder.rights)
- to_chat(C, "[channel][key_name(src,1)] [ADMIN_JMP_USER(mob)]: [msg]")
-
-/client/proc/get_mod_say()
- var/msg = input(src, null, "msay \"text\"") as text|null
- cmd_mod_say(msg)
-
/client/proc/cmd_mentor_say(msg as text)
set name = "MentorSay"
set category = "OOC"
diff --git a/code/modules/admin/tabs/event_tab.dm b/code/modules/admin/tabs/event_tab.dm
index 86f54b510ccf..febc1550fca0 100644
--- a/code/modules/admin/tabs/event_tab.dm
+++ b/code/modules/admin/tabs/event_tab.dm
@@ -744,7 +744,7 @@
create_humans_html = replacetext(create_humans_html, "null /* object types */", "\"[equipment_presets]\"")
create_humans_html = replacetext(create_humans_html, "/* href token */", RawHrefToken(forceGlobal = TRUE))
- show_browser(user, replacetext(create_humans_html, "/* ref src */", "\ref[src]"), "Create Humans", "create_humans", "size=450x630")
+ show_browser(user, replacetext(create_humans_html, "/* ref src */", "\ref[src]"), "Create Humans", "create_humans", "size=450x720")
/client/proc/create_humans()
set name = "Create Humans"
@@ -965,6 +965,7 @@
else
var/faction = tgui_input_list(usr, "What faction do you wish to provide a bioscan for?", "Bioscan Faction", list("Xeno","Marine","Yautja"), 20 SECONDS)
var/variance = tgui_input_number(usr, "How variable do you want the scan to be? (+ or - an amount from truth)", "Variance", 2, 10, 0, 20 SECONDS)
+ message_admins("BIOSCAN: [key_name(usr)] admin-triggered a bioscan for [faction].")
GLOB.bioscan_data.get_scan_data()
switch(faction)
if("Xeno")
diff --git a/code/modules/admin/topic/topic.dm b/code/modules/admin/topic/topic.dm
index b371db087be6..010802318fbe 100644
--- a/code/modules/admin/topic/topic.dm
+++ b/code/modules/admin/topic/topic.dm
@@ -982,7 +982,8 @@
message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Observer.)", 1)
else if(href_list["revive"])
- if(!check_rights(R_REJUVINATE)) return
+ if(!check_rights(R_MOD))
+ return
var/mob/living/L = locate(href_list["revive"])
if(!istype(L))
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index c7a7664af3e8..05da6d3c8672 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -345,7 +345,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
/datum/sdql2_query/New(list/tree, SU = FALSE, admin_interact = TRUE, _options = SDQL2_OPTIONS_DEFAULT, finished_qdel = FALSE)
if(IsAdminAdvancedProcCall() || !LAZYLEN(tree))
qdel(src)
- return
+ return PROC_BLOCKED
LAZYADD(GLOB.sdql2_queries, src)
superuser = SU
allow_admin_interact = admin_interact
@@ -602,7 +602,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
var/text = "[key_name(usr)] attempted to grab world with a procedure call to a SDQL datum."
message_admins(text)
log_admin(text)
- return
+ return PROC_BLOCKED
if("world" in tree)
return world
return SDQL_expression(world, tree)
diff --git a/code/modules/admin/verbs/custom_paper.dm b/code/modules/admin/verbs/custom_paper.dm
index 2c29d2fc3cab..d63d37a84f21 100644
--- a/code/modules/admin/verbs/custom_paper.dm
+++ b/code/modules/admin/verbs/custom_paper.dm
@@ -19,7 +19,7 @@
if(new_sheet)
qdel(sheet)
return
- show_browser(usr, "[new_text]", "Custom paper preview", "custom_paper_preview", "size=500x400")
+ show_browser(usr, "[new_text]", "Custom paper preview", "custom_paper_preview", "size=650x700")
if(alert(usr, "Make this new content?", "Customising [new_name]", "OK", "Cancel") == "Cancel")
close_browser(usr, "custom_paper_preview")
if(new_sheet)
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index fbec4aa13f36..8f0939474427 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -26,8 +26,6 @@
var/adminobs = null
var/area = null
var/time_died_as_mouse = null //when the client last died as a mouse
- /// The descriminator for larva queue ordering: Generally set to timeofdeath except for facehuggers/admin z-level play
- var/larva_queue_time
var/donator = 0
var/adminhelped = 0
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 107e0c71fde5..36ad3c5a104f 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -46,6 +46,7 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
/client/proc/toggle_eject_to_hand,
/client/proc/toggle_automatic_punctuation,
/client/proc/toggle_middle_mouse_click,
+ /client/proc/toggle_ability_deactivation,
/client/proc/toggle_clickdrag_override,
/client/proc/toggle_dualwield,
/client/proc/toggle_middle_mouse_swap_hands,
@@ -323,7 +324,7 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
if(!CONFIG_GET(flag/no_localhost_rank))
var/static/list/localhost_addresses = list("127.0.0.1", "::1")
if(isnull(address) || (address in localhost_addresses))
- var/datum/admins/admin = new("!localhost!", R_EVERYTHING, ckey)
+ var/datum/admins/admin = new("!localhost!", RL_HOST, ckey)
admin.associate(src)
RoleAuthority.roles_whitelist[ckey] = WHITELIST_EVERYTHING
@@ -625,7 +626,8 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
*/
/client/proc/init_verbs()
if(IsAdminAdvancedProcCall())
- return
+ alert_proccall("init_verbs")
+ return PROC_BLOCKED
var/list/verblist = list()
var/list/verbstoprocess = verbs.Copy()
if(mob)
@@ -722,17 +724,8 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
winset(src, "srvkeybinds-[REF(key)]", "parent=default;name=[key];command=[looc]")
else
winset(src, "srvkeybinds-[REF(key)]", "parent=default;name=[key];command=looc")
- if(MOD_CHANNEL)
- if(admin_holder?.check_for_rights(R_MOD))
- if(prefs.tgui_say)
- var/msay = tgui_say_create_open_command(MOD_CHANNEL)
- winset(src, "srvkeybinds-[REF(key)]", "parent=default;name=[key];command=[msay]")
- else
- winset(src, "srvkeybinds-[REF(key)]", "parent=default;name=[key];command=msay")
- else
- winset(src, "srvkeybinds-[REF(key)]", "parent=default;name=[key];command=")
if(ADMIN_CHANNEL)
- if(admin_holder?.check_for_rights(R_ADMIN))
+ if(admin_holder?.check_for_rights(R_MOD))
if(prefs.tgui_say)
var/asay = tgui_say_create_open_command(ADMIN_CHANNEL)
winset(src, "srvkeybinds-[REF(key)]", "parent=default;name=[key];command=[asay]")
diff --git a/code/modules/client/player_details.dm b/code/modules/client/player_details.dm
index 06dafdbea63a..634fd8fb627e 100644
--- a/code/modules/client/player_details.dm
+++ b/code/modules/client/player_details.dm
@@ -7,7 +7,12 @@ GLOBAL_LIST_EMPTY(player_details) // ckey -> /datum/player_details
var/list/post_logout_callbacks = list()
var/list/played_names = list() //List of names this key played under this round
var/byond_version = "Unknown"
+ /// The descriminator for larva queue ordering: Generally set to timeofdeath except for facehuggers/admin z-level play
+ var/larva_queue_time
+/datum/player_details/New()
+ larva_queue_time = world.time
+ return ..()
/proc/log_played_names(ckey, ...)
if(!ckey)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index d2d69d095dbd..5698c30c0acf 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -595,6 +595,8 @@ var/const/MAX_SAVE_SLOTS = 10
[toggle_prefs & TOGGLE_HELP_INTENT_SAFETY ? "On" : "Off"] "
dat += "Toggle Middle Mouse Ability Activation: \
[toggle_prefs & TOGGLE_MIDDLE_MOUSE_CLICK ? "On" : "Off"] "
+ dat += "Toggle Ability Deactivation: \
+ [toggle_prefs & TOGGLE_ABILITY_DEACTIVATION_OFF ? "Off" : "On"] "
dat += "Toggle Directional Assist: \
[toggle_prefs & TOGGLE_DIRECTIONAL_ATTACK ? "On" : "Off"] "
dat += "Toggle Magazine Auto-Ejection: \
@@ -1229,7 +1231,7 @@ var/const/MAX_SAVE_SLOTS = 10
predator_gender = predator_gender == MALE ? FEMALE : MALE
if("pred_age")
var/new_predator_age = tgui_input_number(user, "Choose your Predator's age(175 to 3000):", "Character Preference", 1234, 3000, 175)
- if(new_predator_age)
+ if(new_predator_age)
predator_age = max(min( round(text2num(new_predator_age)), 3000),175)
if("pred_trans_type")
var/new_translator_type = tgui_input_list(user, "Choose your translator type.", "Translator Type", PRED_TRANSLATORS)
diff --git a/code/modules/client/preferences_gear.dm b/code/modules/client/preferences_gear.dm
index 1d4b77f15550..e712e267fb2d 100644
--- a/code/modules/client/preferences_gear.dm
+++ b/code/modules/client/preferences_gear.dm
@@ -363,6 +363,11 @@ var/global/list/gear_datums_by_name = list()
display_name = "Pen, red"
path = /obj/item/tool/pen/red
+/datum/gear/paperwork/pen_fountain
+ display_name = "Pen, fountain"
+ path = /obj/item/tool/pen/fountain
+ cost = 3
+
/datum/gear/paperwork/paper
display_name = "Sheet of paper"
path = /obj/item/paper
diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm
index 9e3d9eb33766..b81411a26440 100644
--- a/code/modules/client/preferences_toggles.dm
+++ b/code/modules/client/preferences_toggles.dm
@@ -205,7 +205,7 @@
set name = "Toggle SpecialRole Candidacy"
set category = "Preferences"
set desc = "Toggles which special roles you would like to be a candidate for, during events."
-
+
var/list/be_special_flags = list(
"Xenomorph after unrevivable death" = BE_ALIEN_AFTER_DEATH,
"Agent" = BE_AGENT,
@@ -274,6 +274,7 @@
"Toggle 'Unload Weapon' Ejecting Magazines to Your Hands ",
"Toggle Automatic Punctuation ",
"Toggle Middle Mouse Ability Activation ",
+ "Toggle Ability Deactivation ",
"Toggle Combat Click-Drag Override ",
"Toggle Alternate-Fire Dual Wielding ",
"Toggle Middle Mouse Swapping Hands ",
@@ -287,7 +288,7 @@
for (var/pref_button in pref_buttons)
dat += "[pref_button]\n"
- var/height = 50+22*length(pref_buttons)
+ var/height = 50+24*length(pref_buttons)
show_browser(src, dat, "Toggle Preferences", "togglepreferences", "size=475x[height]")
@@ -355,6 +356,14 @@
to_chat(src, SPAN_NOTICE("Your selected ability will now be activated with shift clicking."))
prefs.save_preferences()
+/client/proc/toggle_ability_deactivation() // Toggle whether the current ability can be deactivated when re-selected
+ prefs.toggle_prefs ^= TOGGLE_ABILITY_DEACTIVATION_OFF
+ if (prefs.toggle_prefs & TOGGLE_ABILITY_DEACTIVATION_OFF)
+ to_chat(src, SPAN_NOTICE("Your current ability can no longer be toggled off when re-selected."))
+ else
+ to_chat(src, SPAN_NOTICE("Your current ability can be toggled off when re-selected."))
+ prefs.save_preferences()
+
/client/proc/toggle_clickdrag_override() //Toggle whether mousedown clicks immediately when on disarm or harm intent to prevent click-dragging from 'eating' attacks.
prefs.toggle_prefs ^= TOGGLE_COMBAT_CLICKDRAG_OVERRIDE
if(prefs.toggle_prefs & TOGGLE_COMBAT_CLICKDRAG_OVERRIDE)
diff --git a/code/modules/clothing/head/head.dm b/code/modules/clothing/head/head.dm
index 1b4e292a22af..0916ecfb34e9 100644
--- a/code/modules/clothing/head/head.dm
+++ b/code/modules/clothing/head/head.dm
@@ -248,7 +248,9 @@
/obj/item/prop/helmetgarb/lucky_feather = "lucky_feather",
/obj/item/prop/helmetgarb/lucky_feather/blue = "lucky_feather_blue",
/obj/item/prop/helmetgarb/lucky_feather/purple = "lucky_feather_purple",
- /obj/item/prop/helmetgarb/lucky_feather/yellow = "lucky_feather_yellow")
+ /obj/item/prop/helmetgarb/lucky_feather/yellow = "lucky_feather_yellow",
+ /obj/item/tool/pen/fountain = "fountainpen",
+ )
var/storage_slots = 1
var/storage_slots_reserved_for_garb = 1
var/storage_max_w_class = SIZE_TINY
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index eab8b76e81ab..1b66d3e1bf5f 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -326,6 +326,7 @@ GLOBAL_LIST_INIT(allowed_helmet_items, list(
/obj/item/prop/helmetgarb/riot_shield = "helmet_riot_shield",
/obj/item/attachable/flashlight = HELMET_GARB_RELAY_ICON_STATE,
/obj/item/prop/helmetgarb/chaplain_patch = "chaplain_patch",
+ /obj/item/tool/pen/fountain = "fountainpen",
// MEDICAL
/obj/item/stack/medical/bruise_pack ="brutepack (bandages)",
diff --git a/code/modules/clothing/suits/marine_armor.dm b/code/modules/clothing/suits/marine_armor.dm
index ab62ea782a09..c6a2f7fcc32b 100644
--- a/code/modules/clothing/suits/marine_armor.dm
+++ b/code/modules/clothing/suits/marine_armor.dm
@@ -510,7 +510,7 @@ var/list/squad_colors_chat = list(rgb(230,125,125), rgb(255,230,80), rgb(255,150
armor_melee = CLOTHING_ARMOR_MEDIUMLOW
armor_bullet = CLOTHING_ARMOR_MEDIUMLOW
armor_bomb = CLOTHING_ARMOR_MEDIUM
- armor_bio = CLOTHING_ARMOR_MEDIUMHIGH
+ armor_bio = CLOTHING_ARMOR_MEDIUMLOW
armor_rad = CLOTHING_ARMOR_MEDIUMHIGH
armor_internaldamage = CLOTHING_ARMOR_LOW
storage_slots = 2
@@ -549,7 +549,7 @@ var/list/squad_colors_chat = list(rgb(230,125,125), rgb(255,230,80), rgb(255,150
armor_bullet = CLOTHING_ARMOR_HIGH
armor_energy = CLOTHING_ARMOR_LOW
armor_bomb = CLOTHING_ARMOR_LOW
- armor_bio = CLOTHING_ARMOR_NONE
+ armor_bio = CLOTHING_ARMOR_VERYLOW
armor_rad = CLOTHING_ARMOR_NONE
armor_internaldamage = CLOTHING_ARMOR_MEDIUM
storage_slots = 1
@@ -579,6 +579,7 @@ var/list/squad_colors_chat = list(rgb(230,125,125), rgb(255,230,80), rgb(255,150
armor_rad = CLOTHING_ARMOR_NONE
armor_internaldamage = CLOTHING_ARMOR_NONE
storage_slots = 3
+ slowdown = SLOWDOWN_ARMOR_VERY_LIGHT
time_to_unequip = 0.5 SECONDS
time_to_equip = 1 SECONDS
uniform_restricted = null
@@ -589,7 +590,7 @@ var/list/squad_colors_chat = list(rgb(230,125,125), rgb(255,230,80), rgb(255,150
/obj/item/clothing/suit/storage/marine/heavy
name = "\improper M3-EOD pattern heavy armor"
- desc = "A heavier version of the standard M3 pattern armor, the armor is primarily designed to withstand ballistic, explosive, and internal damage, with the drawback of increased bulk and thus reduced movement speed, alongside little additional protection from standard blunt force impacts and none from biological threats."
+ desc = "A heavier version of the standard M3 pattern armor, the armor is primarily designed to withstand ballistic, explosive, and internal damage, with the drawback of increased bulk and thus reduced movement speed, alongside little additional protection from standard blunt force impacts and biological threats."
desc_lore = "This configuration of the iconic armor was developed during the Canton War in 2160 between the UPP and USCM - Designed in response to a need for higher protection for ComTechs assigned as EODs during the conflict, this is the pinnacle of protection for your average marine. The shoulders and kneepads have both been expanded upon heavily, covering up the arteries on each limb. A special spall liner was developed for this suit, with the same technology being used in the M70 Flak Jacket being developed at the same time."
specialty = "\improper M3-EOD pattern"
icon_state = "H1"
@@ -597,7 +598,7 @@ var/list/squad_colors_chat = list(rgb(230,125,125), rgb(255,230,80), rgb(255,150
armor_melee = CLOTHING_ARMOR_MEDIUMHIGH
armor_bullet = CLOTHING_ARMOR_HIGHPLUS
armor_bomb = CLOTHING_ARMOR_HIGHPLUS
- armor_bio = CLOTHING_ARMOR_MEDIUM
+ armor_bio = CLOTHING_ARMOR_MEDIUMHIGH
armor_rad = CLOTHING_ARMOR_MEDIUM
armor_internaldamage = CLOTHING_ARMOR_MEDIUMHIGH
storage_slots = 2
diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm
index d2b020a05daf..c51f5f2575ed 100644
--- a/code/modules/clothing/suits/miscellaneous.dm
+++ b/code/modules/clothing/suits/miscellaneous.dm
@@ -272,13 +272,11 @@
flags_armor_protection = BODY_FLAG_CHEST|BODY_FLAG_ARMS
/obj/item/clothing/suit/storage/webbing
- name = "External webbing"
+ name = "external webbing"
desc = "Designed to be worn over a jumpsuit rather than clipped on."
icon_state = "webbing"
item_state = "webbing"
allowed = list(
- /obj/item/storage/fancy/cigarettes,
- /obj/item/tool/lighter,
/obj/item/weapon/baton,
/obj/item/handcuffs,
/obj/item/device/binoculars,
@@ -287,7 +285,6 @@
/obj/item/device/flashlight,
/obj/item/device/healthanalyzer,
/obj/item/device/radio,
- /obj/item/tank/emergency_oxygen,
/obj/item/tool/crowbar,
/obj/item/tool/crew_monitor,
/obj/item/tool/pen,
@@ -295,6 +292,26 @@
/obj/item/device/motiondetector,
)
+/obj/item/clothing/suit/storage/utility_vest
+ name = "utility vest"
+ desc = "A utility vest to hold tools in."
+ icon_state = "synth_utility_vest"
+ item_state = "synth_utility_vest"
+ allowed = list(
+ /obj/item/weapon/baton,
+ /obj/item/handcuffs,
+ /obj/item/device/binoculars,
+ /obj/item/attachable/bayonet,
+
+ /obj/item/device/flashlight,
+ /obj/item/device/healthanalyzer,
+ /obj/item/device/radio,
+ /obj/item/tool/crowbar,
+ /obj/item/tool/crew_monitor,
+ /obj/item/storage/large_holster/machete,
+ /obj/item/device/motiondetector,
+ )
+
//Blue suit jacket toggle
/obj/item/clothing/suit/suit/verb/toggle()
set name = "Toggle Jacket Buttons"
diff --git a/code/modules/clothing/under/marine_uniform.dm b/code/modules/clothing/under/marine_uniform.dm
index 540c311f9221..eca050cc4b88 100644
--- a/code/modules/clothing/under/marine_uniform.dm
+++ b/code/modules/clothing/under/marine_uniform.dm
@@ -705,7 +705,7 @@
sensor_faction = FACTION_CLF
/obj/item/clothing/under/colonist/ua_civvies
- name = "gray utilities"
+ name = "\improper UA gray utility uniform"
desc = "A stylish gray jumpsuit - standard issue for UA civilian support personnel."
icon_state = "ua_civvies"
worn_state = "ua_civvies"
@@ -713,7 +713,7 @@
sensor_faction = FACTION_MARINE
/obj/item/clothing/under/colonist/wy_davisone
- name = "brown utilities"
+ name = "\improper UA brown utility uniform"
desc = "A stylish brown jumpsuit - standard issue for UA civilian support personnel."
icon_state = "wy_davisone"
worn_state = "wy_davisone"
@@ -737,14 +737,15 @@
desc = "A comfortable white T-shirt and brown jeans."
icon_state = "tshirt_w_br"
worn_state = "tshirt_w_br"
+ displays_id = FALSE
has_sensor = UNIFORM_HAS_SENSORS
sensor_faction = FACTION_MARINE
-
/obj/item/clothing/under/tshirt/gray_blu
name = "gray T-shirt and jeans"
desc = "A comfortable gray T-shirt and blue jeans."
icon_state = "tshirt_gray_blu"
worn_state = "tshirt_gray_blu"
+ displays_id = FALSE
has_sensor = UNIFORM_HAS_SENSORS
sensor_faction = FACTION_MARINE
@@ -753,6 +754,7 @@
desc = "A comfortable red T-shirt and black jeans."
icon_state = "tshirt_r_bla"
worn_state = "tshirt_r_bla"
+ displays_id = FALSE
has_sensor = UNIFORM_HAS_SENSORS
sensor_faction = FACTION_MARINE
@@ -851,6 +853,38 @@
worn_state = "rdalt"
flags_jumpsuit = FALSE
+/obj/item/clothing/under/rank/synthetic/frontier
+ name = "\improper frontier jumpsuit"
+ desc = "A cargo jumpsuit dressed down for full range of motion and state-of-the-art frontier temperature control. It's the best thing an engineer can wear in the Outer Veil."
+ icon_state = "synth_cargo_light"
+ worn_state = "synth_cargo_light"
+ displays_id = FALSE
+
+/obj/item/clothing/under/rank/synthetic/utility
+ name = "\improper UA utility uniform"
+ desc = "A green-on-green utility uniform, popularly issued to UA contract workers on the frontier."
+ icon_state = "synth_green_utility"
+ worn_state = "synth_green_utility"
+ displays_id = FALSE
+
+/obj/item/clothing/under/rank/synthetic/utility/yellow
+ name = "\improper utility uniform"
+ desc = "A grey utility uniform with yellow suspenders, made for shipside crew."
+ icon_state = "synth_yellow_utility"
+ worn_state = "synth_yellow_utility"
+
+/obj/item/clothing/under/rank/synthetic/utility/red
+ name = "\improper utility uniform"
+ desc = "A grey utility uniform with red suspenders and blue jeans, the sign of a veteran laborer, or someone not paid by the hour."
+ icon_state = "synth_red_utility"
+ worn_state = "synth_red_utility"
+
+/obj/item/clothing/under/rank/synthetic/utility/blue
+ name = "\improper utility uniform"
+ desc = "A blue utility uniform with teal suspenders and rugged pants."
+ icon_state = "synth_blue_utility"
+ worn_state = "synth_blue_utility"
+
/obj/item/clothing/under/rank/synthetic/councillor
name = "\improper USCM Pristine Support Uniform"
desc = "A nicely handcrafted uniform made for Synthetic crewmembers."
diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm
index b5d2850575aa..42c61404a31a 100644
--- a/code/modules/clothing/under/miscellaneous.dm
+++ b/code/modules/clothing/under/miscellaneous.dm
@@ -82,8 +82,8 @@
item_state = "r_suit"
/obj/item/clothing/under/blackskirt
- name = "black skirt"
- desc = "A black skirt, very fancy!"
+ name = "red dress skirt"
+ desc = "A black cardigan with a red skirt, quite fancy!"
icon_state = "blackskirt"
flags_armor_protection = BODY_FLAG_CHEST|BODY_FLAG_GROIN|BODY_FLAG_ARMS
diff --git a/code/modules/cm_aliens/XenoStructures.dm b/code/modules/cm_aliens/XenoStructures.dm
index f21cd6498615..08e451407989 100644
--- a/code/modules/cm_aliens/XenoStructures.dm
+++ b/code/modules/cm_aliens/XenoStructures.dm
@@ -154,6 +154,8 @@
if (hive)
hivenumber = hive
set_hive_data(src, hivenumber)
+ if(hivenumber == XENO_HIVE_NORMAL)
+ RegisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING, PROC_REF(forsaken_handling))
/obj/effect/alien/resin/sticky/Crossed(atom/movable/AM)
. = ..()
@@ -166,6 +168,14 @@
X.next_move_slowdown = X.next_move_slowdown + slow_amt
return .
+/obj/effect/alien/resin/sticky/proc/forsaken_handling()
+ SIGNAL_HANDLER
+ if(is_ground_level(z))
+ hivenumber = XENO_HIVE_FORSAKEN
+ set_hive_data(src, XENO_HIVE_FORSAKEN)
+
+ UnregisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING)
+
/obj/effect/alien/resin/spike
name = "resin spike"
desc = "A small cluster of bone spikes. Ouch."
@@ -193,6 +203,8 @@
hivenumber = hive
set_hive_data(src, hivenumber)
setDir(pick(alldirs))
+ if(hivenumber == XENO_HIVE_NORMAL)
+ RegisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING, PROC_REF(forsaken_handling))
/obj/effect/alien/resin/spike/Crossed(atom/movable/AM)
. = ..()
@@ -206,6 +218,14 @@
H.apply_armoured_damage(damage, penetration = penetration, def_zone = pick(target_limbs))
H.last_damage_data = construction_data
+/obj/effect/alien/resin/spike/proc/forsaken_handling()
+ SIGNAL_HANDLER
+ if(is_ground_level(z))
+ hivenumber = XENO_HIVE_FORSAKEN
+ set_hive_data(src, XENO_HIVE_FORSAKEN)
+
+ UnregisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING)
+
// Praetorian Sticky Resin spit uses this.
/obj/effect/alien/resin/sticky/thin
name = "thin sticky resin"
@@ -348,6 +368,9 @@
set_hive_data(src, hivenumber)
+ if(hivenumber == XENO_HIVE_NORMAL)
+ RegisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING, PROC_REF(forsaken_handling))
+
/obj/structure/mineral_door/resin/flamer_fire_act(dam = BURN_LEVEL_TIER_1)
health -= dam
healthcheck()
@@ -371,11 +394,13 @@
return attack_hand(user)
/obj/structure/mineral_door/resin/TryToSwitchState(atom/user)
- if(islarva(user))
- var/mob/living/carbon/xenomorph/larva/L = user
- if (L.hivenumber == hivenumber)
- L.scuttle(src)
- return
+ if(isxeno(user))
+ var/mob/living/carbon/xenomorph/xeno_user = user
+ if (xeno_user.hivenumber != hivenumber && !xeno_user.ally_of_hivenumber(hivenumber))
+ return
+ if(xeno_user.scuttle(src))
+ return
+ return ..()
if(iscarbon(user))
var/mob/living/carbon/C = user
if (C.ally_of_hivenumber(hivenumber))
@@ -479,6 +504,13 @@
visible_message(SPAN_NOTICE("[src] collapses from the lack of support."))
qdel(src)
+/obj/structure/mineral_door/resin/proc/forsaken_handling()
+ SIGNAL_HANDLER
+ if(is_ground_level(z))
+ hivenumber = XENO_HIVE_FORSAKEN
+ set_hive_data(src, XENO_HIVE_FORSAKEN)
+
+ UnregisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING)
/obj/structure/mineral_door/resin/thick
name = "thick resin door"
icon_state = "thick resin"
@@ -510,6 +542,8 @@
hivenumber = hive
set_hive_data(src, hivenumber)
START_PROCESSING(SSprocessing, src)
+ if(hivenumber == XENO_HIVE_NORMAL)
+ RegisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING, PROC_REF(forsaken_handling))
/obj/effect/alien/resin/acid_pillar/proc/can_target(mob/living/carbon/current_mob, position_to_get = 0)
@@ -599,6 +633,12 @@
/obj/effect/alien/resin/acid_pillar/get_projectile_hit_boolean(obj/item/projectile/P)
return TRUE
+/obj/effect/alien/resin/acid_pillar/proc/forsaken_handling()
+ SIGNAL_HANDLER
+ UnregisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING)
+ if(is_ground_level(z))
+ qdel(src)
+
/obj/effect/alien/resin/acid_pillar/strong
name = "acid pillar"
desc = "A resin pillar that is oozing with acid."
diff --git a/code/modules/cm_aliens/structures/fruit.dm b/code/modules/cm_aliens/structures/fruit.dm
index 8bdcc0b82413..bb899a6ff25b 100644
--- a/code/modules/cm_aliens/structures/fruit.dm
+++ b/code/modules/cm_aliens/structures/fruit.dm
@@ -150,25 +150,33 @@
update_icon()
QDEL_IN(src, 3 SECONDS)
-/obj/effect/alien/resin/fruit/attack_alien(mob/living/carbon/xenomorph/X)
+/obj/effect/alien/resin/fruit/attack_alien(mob/living/carbon/xenomorph/affected_xeno)
if(picked)
- to_chat(X, SPAN_XENODANGER("This fruit is already being picked!"))
+ to_chat(affected_xeno, SPAN_XENODANGER("This fruit is already being picked!"))
return
- if(X.a_intent != INTENT_HARM && (X.can_not_harm(bound_xeno) || X.hivenumber == hivenumber))
- var/cant_consume = prevent_consume(X)
+
+ if(affected_xeno.a_intent != INTENT_HARM && (affected_xeno.can_not_harm(bound_xeno) || affected_xeno.hivenumber == hivenumber))
+ var/cant_consume = prevent_consume(affected_xeno)
if(cant_consume)
return cant_consume
+
if(mature)
- to_chat(X, SPAN_XENOWARNING("You prepare to consume [name]."))
- xeno_noncombat_delay(X)
- if(!do_after(X, consume_delay, INTERRUPT_ALL, BUSY_ICON_FRIENDLY))
+ to_chat(affected_xeno, SPAN_XENOWARNING("You prepare to consume [name]."))
+ xeno_noncombat_delay(affected_xeno)
+ if(!do_after(affected_xeno, consume_delay, INTERRUPT_ALL, BUSY_ICON_FRIENDLY))
return XENO_NO_DELAY_ACTION
- consume_effect(X)
+
+ cant_consume = prevent_consume(affected_xeno) // Check again after the delay incase they have eaten another fruit
+ if(cant_consume)
+ to_chat(affected_xeno, SPAN_XENOWARNING("You can no longer consume [name]."))
+ return cant_consume
+ consume_effect(affected_xeno)
else
- to_chat(X, SPAN_XENOWARNING("[name] isn't ripe yet. You need to wait a little longer."))
- if(X.a_intent == INTENT_HARM && isxeno_builder(X) || (!X.can_not_harm(bound_xeno) && X.hivenumber != hivenumber))
- X.animation_attack_on(src)
- X.visible_message(SPAN_XENODANGER("[X] removes [name]!"),
+ to_chat(affected_xeno, SPAN_XENOWARNING("[name] isn't ripe yet. You need to wait a little longer."))
+
+ if(affected_xeno.a_intent == INTENT_HARM && isxeno_builder(affected_xeno) || (!affected_xeno.can_not_harm(bound_xeno) && affected_xeno.hivenumber != hivenumber))
+ affected_xeno.animation_attack_on(src)
+ affected_xeno.visible_message(SPAN_XENODANGER("[affected_xeno] removes [name]!"),
SPAN_XENODANGER("You remove [name]!"))
playsound(loc, "alien_resin_break", 25)
qdel(src)
@@ -376,30 +384,49 @@
bound_xeno = null
// Xenos eating fruit
-/obj/item/reagent_container/food/snacks/resin_fruit/attack(mob/living/carbon/xenomorph/X, mob/user)
+/obj/item/reagent_container/food/snacks/resin_fruit/attack(mob/living/carbon/xenomorph/affected_xeno, mob/user)
if(istype(user, /mob/living/carbon/xenomorph)) // Prevents xenos from feeding capped/dead marines fruit
- var/mob/living/carbon/xenomorph/Y = user
- if(!Y.can_not_harm(X))
- to_chat(Y, SPAN_WARNING("[X] refuses to eat [src]."))
+ var/mob/living/carbon/xenomorph/feeding_xeno = user
+ if(!feeding_xeno.can_not_harm(affected_xeno))
+ to_chat(feeding_xeno, SPAN_WARNING("[affected_xeno] refuses to eat [src]."))
return
- if(!istype(X))
+
+ if(!istype(affected_xeno))
return ..()
- if(X.stat == DEAD)
+
+ if(affected_xeno.stat == DEAD)
to_chat(user, SPAN_WARNING("That sister is already dead, they won't benefit from the fruit now..."))
return
- user.affected_message(X,
- SPAN_HELPFUL("You start [user == X ? "eating" : "feeding [X]"] [src]."),
- SPAN_HELPFUL("[user] starts feeding you [src]."),
- SPAN_NOTICE("[user] starts [user == X ? "eating" : "feeding [X]"] [src]."))
- if(!do_after(user, consume_delay, INTERRUPT_ALL, BUSY_ICON_FRIENDLY, X, INTERRUPT_MOVED, BUSY_ICON_MEDICAL))
+
+ var/obj/effect/alien/resin/fruit/current_fruit = new fruit_type(affected_xeno)
+ var/cant_consume = current_fruit.prevent_consume(affected_xeno)
+ if(cant_consume)
+ user.affected_message(affected_xeno,
+ SPAN_HELPFUL("You fail to [user == affected_xeno ? "eat" : "feed [affected_xeno]"] [current_fruit]."),
+ SPAN_HELPFUL("[user] fails to feed you [current_fruit]."))
+ return
+ user.affected_message(affected_xeno,
+ SPAN_HELPFUL("You start [user == affected_xeno ? "eating" : "feeding [affected_xeno]"] [current_fruit]."),
+ SPAN_HELPFUL("[user] starts feeding you [current_fruit]."),
+ SPAN_NOTICE("[user] starts [user == affected_xeno ? "eating" : "feeding [affected_xeno]"] [current_fruit]."))
+
+ if(!do_after(user, consume_delay, INTERRUPT_ALL, BUSY_ICON_FRIENDLY, affected_xeno, INTERRUPT_MOVED, BUSY_ICON_MEDICAL))
return FALSE
- user.affected_message(X,
- SPAN_HELPFUL("You [user == X ? "eat" : "fed [X]"] [src]."),
- SPAN_HELPFUL("[user] fed you [src]."),
- SPAN_NOTICE("[user] [user == X ? "ate" : "fed [X]"] [src]."))
- var/obj/effect/alien/resin/fruit/F = new fruit_type(X)
- F.mature = TRUE
- F.consume_effect(X)
+
+ cant_consume = current_fruit.prevent_consume(affected_xeno)
+ if(cant_consume) //Check again after the timer incase they ate another fruit
+ user.affected_message(affected_xeno,
+ SPAN_HELPFUL("You fail to [user == affected_xeno ? "eat" : "feed [affected_xeno]"] [current_fruit]."),
+ SPAN_HELPFUL("[user] fails to feed you [current_fruit]."))
+ return
+
+ user.affected_message(affected_xeno,
+ SPAN_HELPFUL("You [user == affected_xeno ? "eat" : "fed [affected_xeno]"] [current_fruit]."),
+ SPAN_HELPFUL("[user] fed you [current_fruit]."),
+ SPAN_NOTICE("[user] [user == affected_xeno ? "ate" : "fed [affected_xeno]"] [current_fruit]."))
+ current_fruit.mature = TRUE
+ current_fruit.consume_effect(affected_xeno)
+
//Notify the fruit's bound xeno if they exist
if(!QDELETED(bound_xeno))
to_chat(bound_xeno, SPAN_XENOWARNING("One of your picked resin fruits has been consumed."))
diff --git a/code/modules/cm_aliens/structures/special/egg_morpher.dm b/code/modules/cm_aliens/structures/special/egg_morpher.dm
index 2e79d6ef1d24..1fd154eb354c 100644
--- a/code/modules/cm_aliens/structures/special/egg_morpher.dm
+++ b/code/modules/cm_aliens/structures/special/egg_morpher.dm
@@ -148,6 +148,7 @@
var/obj/item/item = A
if(item.is_objective && item.unacidable)
item.forceMove(get_step(loc, pick(alldirs)))
+ item.mouse_opacity = initial(item.mouse_opacity)
QDEL_NULL(captured_mob)
update_icon()
diff --git a/code/modules/cm_aliens/structures/special/pylon_core.dm b/code/modules/cm_aliens/structures/special/pylon_core.dm
index c38e540efb30..a29b49b7745a 100644
--- a/code/modules/cm_aliens/structures/special/pylon_core.dm
+++ b/code/modules/cm_aliens/structures/special/pylon_core.dm
@@ -150,7 +150,7 @@
last_larva_time = world.time
if(spawning_larva || (last_larva_queue_time + spawn_cooldown * 4) < world.time)
last_larva_queue_time = world.time
- var/list/players_with_xeno_pref = get_alien_candidates()
+ var/list/players_with_xeno_pref = get_alien_candidates(linked_hive)
if(players_with_xeno_pref && players_with_xeno_pref.len)
if(spawning_larva && spawn_burrowed_larva(players_with_xeno_pref[1]))
// We were in spawning_larva mode and successfully spawned someone
diff --git a/code/modules/cm_aliens/structures/trap.dm b/code/modules/cm_aliens/structures/trap.dm
index 1b3d4d414d48..5e1c51538ce6 100644
--- a/code/modules/cm_aliens/structures/trap.dm
+++ b/code/modules/cm_aliens/structures/trap.dm
@@ -29,6 +29,8 @@
cause_data = create_cause_data("resin trap", X)
set_hive_data(src, hivenumber)
+ if(hivenumber == XENO_HIVE_NORMAL)
+ RegisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING, PROC_REF(forsaken_handling))
/obj/effect/alien/resin/trap/Initialize()
. = ..()
@@ -53,6 +55,14 @@
if(RESIN_TRAP_ACID1, RESIN_TRAP_ACID2, RESIN_TRAP_ACID3)
. += "It's filled with pressurised acid."
+/obj/effect/alien/resin/trap/proc/forsaken_handling()
+ SIGNAL_HANDLER
+ if(is_ground_level(z))
+ hivenumber = XENO_HIVE_FORSAKEN
+ set_hive_data(src, XENO_HIVE_FORSAKEN)
+
+ UnregisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING)
+
/obj/effect/alien/resin/trap/proc/facehugger_die()
var/obj/item/clothing/mask/facehugger/FH = new (loc)
FH.die()
@@ -159,6 +169,7 @@
trap_type_name = "hugger"
var/obj/item/clothing/mask/facehugger/FH = new (loc)
FH.hivenumber = hivenumber
+ set_hive_data(FH, hivenumber)
set_state()
visible_message(SPAN_WARNING("[FH] gets out of [src]!"))
sleep(15)
diff --git a/code/modules/cm_aliens/weeds.dm b/code/modules/cm_aliens/weeds.dm
index 0be2220994d8..f20fa842e446 100644
--- a/code/modules/cm_aliens/weeds.dm
+++ b/code/modules/cm_aliens/weeds.dm
@@ -73,15 +73,18 @@
else if(!hibernate && do_spread)
addtimer(CALLBACK(src, PROC_REF(weed_expand)), WEED_BASE_GROW_SPEED / max(weed_strength, 1))
- var/turf/T = get_turf(src)
- if(T)
- T.weeds = src
- weeded_turf = T
+ var/turf/turf = get_turf(src)
+ if(turf)
+ turf.weeds = src
+ weeded_turf = turf
+ SEND_SIGNAL(turf, COMSIG_WEEDNODE_GROWTH) // Currently for weed_food wakeup
RegisterSignal(src, list(
COMSIG_ATOM_TURF_CHANGE,
COMSIG_MOVABLE_TURF_ENTERED
), PROC_REF(set_turf_weeded))
+ if(hivenumber == XENO_HIVE_NORMAL)
+ RegisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING, PROC_REF(forsaken_handling))
/obj/effect/alien/weeds/proc/set_turf_weeded(datum/source, turf/T)
SIGNAL_HANDLER
@@ -90,6 +93,15 @@
T.weeds = src
+/obj/effect/alien/weeds/proc/forsaken_handling()
+ SIGNAL_HANDLER
+ if(is_ground_level(z))
+ hivenumber = XENO_HIVE_FORSAKEN
+ set_hive_data(src, XENO_HIVE_FORSAKEN)
+ linked_hive = GLOB.hive_datum[XENO_HIVE_FORSAKEN]
+
+ UnregisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING)
+
/obj/effect/alien/weeds/initialize_pass_flags(datum/pass_flags_container/PF)
. = ..()
if (PF)
@@ -418,7 +430,11 @@
/obj/effect/alien/weeds/weedwall/MouseDrop_T(mob/current_mob, mob/user)
. = ..()
- if(isxeno(user))
+
+ if(!ismob(current_mob))
+ return
+
+ if(isxeno(user) && istype(user.get_active_hand(), /obj/item/grab))
var/mob/living/carbon/xenomorph/user_as_xenomorph = user
user_as_xenomorph.do_nesting_host(current_mob, src)
@@ -492,20 +508,20 @@
overlay_node = TRUE
overlays += staticnode
-/obj/effect/alien/weeds/node/Initialize(mapload, obj/effect/alien/weeds/node/node, mob/living/carbon/xenomorph/X, datum/hive_status/hive)
+/obj/effect/alien/weeds/node/Initialize(mapload, obj/effect/alien/weeds/node/node, mob/living/carbon/xenomorph/xeno, datum/hive_status/hive)
if (istype(hive))
linked_hive = hive
- else if (istype(X) && X.hive)
- linked_hive = X.hive
+ else if (istype(xeno) && xeno.hive)
+ linked_hive = xeno.hive
else
linked_hive = GLOB.hive_datum[hivenumber]
- for(var/obj/effect/alien/weeds/W in loc)
- if(W != src)
- if(W.weed_strength > WEED_LEVEL_HIVE)
+ for(var/obj/effect/alien/weeds/weed in loc)
+ if(weed != src)
+ if(weed.weed_strength > WEED_LEVEL_HIVE)
qdel(src)
return
- qdel(W) //replaces the previous weed
+ qdel(weed) //replaces the previous weed
break
. = ..(mapload, src)
@@ -513,15 +529,15 @@
if(!staticnode)
staticnode = image('icons/mob/xenos/weeds.dmi', "weednode", ABOVE_OBJ_LAYER)
- var/obj/effect/alien/resin/trap/TR = locate() in loc
- if(TR)
- RegisterSignal(TR, COMSIG_PARENT_PREQDELETED, PROC_REF(trap_destroyed))
+ var/obj/effect/alien/resin/trap/trap = locate() in loc
+ if(trap)
+ RegisterSignal(trap, COMSIG_PARENT_PREQDELETED, PROC_REF(trap_destroyed))
overlay_node = FALSE
overlays -= staticnode
- if(X)
- add_hiddenprint(X)
- weed_strength = X.weed_level
+ if(xeno)
+ add_hiddenprint(xeno)
+ weed_strength = max(weed_strength, xeno.weed_level)
if (weed_strength < WEED_LEVEL_STANDARD)
weed_strength = WEED_LEVEL_STANDARD
diff --git a/code/modules/cm_marines/equipment/maps.dm b/code/modules/cm_marines/equipment/maps.dm
index 14b9e5b128cd..98ad238fe055 100644
--- a/code/modules/cm_marines/equipment/maps.dm
+++ b/code/modules/cm_marines/equipment/maps.dm
@@ -125,10 +125,10 @@
desc = "An overview of LV-522 schematics."
html_link = "images/b/bb/C_claim.png"
color = "cyan"
-/obj/item/map/new_varadero//to-do actually finish this map
+/obj/item/map/new_varadero
name = "\improper New Varadero map"
- desc = "The blueprint and readout of the UA outpost New Varadero"
- html_link = "images/0/0d/Kutjevo_a1.jpg"//replace later
+ desc = "A labeled blueprint of the UA outpost New Varadero"
+ html_link = "images/9/94/New_Varadero.png"
color = "red"
//used by marine equipment machines to spawn the correct map.
@@ -195,7 +195,7 @@
if (MAP_NEW_VARADERO)
name = "\improper New Varadero map"
desc = "The blueprint and readout of the UA outpost New Varadero"
- html_link = "images/0/0d/Kutjevo_a1.jpg"//replace later
+ html_link = "images/9/94/New_Varadero.png"//replace later
color = "red"
else
diff --git a/code/modules/cm_marines/orbital_cannon.dm b/code/modules/cm_marines/orbital_cannon.dm
index ad214c954915..8d80f80860f3 100644
--- a/code/modules/cm_marines/orbital_cannon.dm
+++ b/code/modules/cm_marines/orbital_cannon.dm
@@ -430,7 +430,6 @@ var/list/ob_type_fuel_requirements
icon_state = "ob_warhead_1"
shake_frequency = 3
max_shake_factor = 15
- max_knockdown_time = 6
var/clear_power = 1200
var/clear_falloff = 400
diff --git a/code/modules/cm_marines/smartgun_mount.dm b/code/modules/cm_marines/smartgun_mount.dm
index 2db33c10ed46..b72120d2038e 100644
--- a/code/modules/cm_marines/smartgun_mount.dm
+++ b/code/modules/cm_marines/smartgun_mount.dm
@@ -906,7 +906,7 @@
user.visible_message(SPAN_NOTICE("[user] lets go of \the [src]."),SPAN_NOTICE("You let go of \the [src], letting the gun rest."))
user.unfreeze()
user.reset_view(null)
- user.forceMove(get_step(src, reverse_direction(src.dir)))
+ user.Move(get_step(src, reverse_direction(src.dir)))
user.setDir(dir) //set the direction of the player to the direction the gun is facing
user_old_x = 0 //reset our x
user_old_y = 0 //reset our y
diff --git a/code/modules/cm_preds/yaut_bracers.dm b/code/modules/cm_preds/yaut_bracers.dm
index 1b98ef402b90..5c4079b2be23 100644
--- a/code/modules/cm_preds/yaut_bracers.dm
+++ b/code/modules/cm_preds/yaut_bracers.dm
@@ -347,10 +347,6 @@
playsound(user,'sound/weapons/wristblades_on.ogg', 15, 1)
return TRUE
-// Toggle the notification sound
-/obj/item/clothing/gloves/yautja/hunter/toggle_notification_sound()
- set category = "Yautja.Misc"
-
//Should put a cool menu here, like ninjas.
/obj/item/clothing/gloves/yautja/hunter/verb/wristblades()
set name = "Use Wrist Blades"
diff --git a/code/modules/cm_preds/yaut_items.dm b/code/modules/cm_preds/yaut_items.dm
index e9b7c36b5df6..a6fb4658ed0b 100644
--- a/code/modules/cm_preds/yaut_items.dm
+++ b/code/modules/cm_preds/yaut_items.dm
@@ -396,7 +396,7 @@
return
var/mob/living/carbon/human/H = user
- var/ship_to_tele = list("Public" = -1, "Human Ship" = "Human")
+ var/ship_to_tele = list("Yautja Ship" = -1, "Human Ship" = "Human")
if(!HAS_TRAIT(H, TRAIT_YAUTJA_TECH) || is_admin_level(H.z))
to_chat(user, SPAN_WARNING("You fiddle with it, but nothing happens!"))
diff --git a/code/modules/cm_tech/implements/railgun.dm b/code/modules/cm_tech/implements/railgun.dm
index b0d91515419f..b69f9a9d13a8 100644
--- a/code/modules/cm_tech/implements/railgun.dm
+++ b/code/modules/cm_tech/implements/railgun.dm
@@ -6,6 +6,7 @@ GLOBAL_DATUM(railgun_eye_location, /datum/coords)
/obj/effect/landmark/railgun_computer
name = "Railgun computer landmark"
+ desc = "A computer with an orange interface, it's idly blinking, awaiting a password."
/obj/effect/landmark/railgun_computer/Initialize(mapload, ...)
. = ..()
diff --git a/code/modules/defenses/sentry.dm b/code/modules/defenses/sentry.dm
index 946c347efaa0..fc52c254c0b8 100644
--- a/code/modules/defenses/sentry.dm
+++ b/code/modules/defenses/sentry.dm
@@ -370,7 +370,7 @@
targets.Remove(A)
continue
- if(M.get_target_lock(faction_group) || M.invisibility)
+ if(M.get_target_lock(faction_group) || M.invisibility || HAS_TRAIT(M, TRAIT_ABILITY_BURROWED))
if(M == target)
target = null
targets.Remove(M)
diff --git a/code/modules/gear_presets/cmb.dm b/code/modules/gear_presets/cmb.dm
index a1ea205a8f61..79df8d567770 100644
--- a/code/modules/gear_presets/cmb.dm
+++ b/code/modules/gear_presets/cmb.dm
@@ -79,7 +79,7 @@
new_human.equip_to_slot_or_del(new /obj/item/clothing/head/CMB, WEAR_HEAD)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/veteran/pmc/knife, WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses/sechud, WEAR_EYES)
- new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full, WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert, WEAR_L_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel/sec, WEAR_BACK)
new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/pistol/holdout, WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/device/radio, WEAR_IN_BACK)
@@ -171,7 +171,7 @@
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/veteran/pmc/knife, WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses/sechud, WEAR_EYES)
//pouches
- new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full, WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert, WEAR_L_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/shotgun/large, WEAR_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/handful/shotgun/buckshot, WEAR_IN_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/handful/shotgun/buckshot, WEAR_IN_R_STORE)
@@ -328,7 +328,7 @@
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/veteran/pmc/knife, WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses, WEAR_EYES)
//pouches
- new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full, WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert, WEAR_L_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/general/large, WEAR_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/fancy/cigarettes/wypacket, WEAR_IN_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/tool/lighter/zippo, WEAR_IN_R_STORE)
@@ -450,7 +450,7 @@
new_human.equip_to_slot_or_del(new /obj/item/storage/box/packet/high_explosive, WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/storage/box/packet/high_explosive, WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/flare/full, WEAR_R_STORE)
- new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full, WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert, WEAR_L_STORE)
//Anchorpoint Station Marine Squad Leader
@@ -490,7 +490,7 @@
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife, WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/rifle/m41aMK1/anchorpoint, WEAR_J_STORE)
new_human.equip_to_slot_or_del(new /obj/item/device/motiondetector, WEAR_WAIST)
- new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full, WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert, WEAR_L_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/magazine/large, WEAR_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1/ap, WEAR_IN_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1/ap, WEAR_IN_R_STORE)
@@ -541,7 +541,7 @@
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1, WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1, WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/construction/full, WEAR_R_STORE)
- new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full, WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert, WEAR_L_STORE)
new_human.back.pickup()
@@ -631,7 +631,7 @@
new_human.equip_to_slot_or_del(new /obj/item/stack/medical/bruise_pack, WEAR_IN_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/smartgun, WEAR_J_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/flare/full, WEAR_R_STORE)
- new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full, WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert, WEAR_L_STORE)
new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine, WEAR_HANDS)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife, WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/storage/belt/marine/smartgunner/full/, WEAR_WAIST)
diff --git a/code/modules/gear_presets/corpses.dm b/code/modules/gear_presets/corpses.dm
index 02671cc02a93..7e9dd5b841a0 100644
--- a/code/modules/gear_presets/corpses.dm
+++ b/code/modules/gear_presets/corpses.dm
@@ -16,28 +16,32 @@
/datum/equipment_preset/corpse/load_status(mob/living/carbon/human/new_human)
. = ..(new_human)
+
+ // These two values matter because they are checked on death for weed_food
+ new_human.undefibbable = TRUE
+ if(xenovictim)
+ new_human.chestburst = 2
+
new_human.death(create_cause_data("existing"), TRUE) //Kills the new mob
new_human.apply_damage(100, BRUTE)
new_human.apply_damage(100, BRUTE)
new_human.apply_damage(100, BRUTE)
if(xenovictim)
- var/datum/internal_organ/O
+ var/datum/internal_organ/organ
var/i
for(i in list("heart","lungs"))
- O = new_human.internal_organs_by_name[i]
+ organ = new_human.internal_organs_by_name[i]
new_human.internal_organs_by_name -= i
- new_human.internal_organs -= O
- new_human.chestburst = 2
+ new_human.internal_organs -= organ
new_human.update_burst()
//buckle to nest
- var/obj/structure/bed/nest/N = locate() in get_turf(src)
- if(N)
- new_human.buckled = N
- new_human.setDir(N.dir)
+ var/obj/structure/bed/nest/nest = locate() in get_turf(src)
+ if(nest)
+ new_human.buckled = nest
+ new_human.setDir(nest.dir)
new_human.update_canmove()
- N.buckled_mob = new_human
- N.afterbuckle(new_human)
- new_human.undefibbable = TRUE
+ nest.buckled_mob = new_human
+ nest.afterbuckle(new_human)
new_human.spawned_corpse = TRUE
new_human.updatehealth()
new_human.pulse = PULSE_NONE
diff --git a/code/modules/gear_presets/survivors.dm b/code/modules/gear_presets/survivors.dm
index 97b974b8a5ec..8c6a118d24d4 100644
--- a/code/modules/gear_presets/survivors.dm
+++ b/code/modules/gear_presets/survivors.dm
@@ -144,6 +144,21 @@
..()
+/datum/equipment_preset/survivor/scientist/nv
+ name = "Survivor - New Varadero Researcher"
+ assignment = "New Varadero Researcher"
+
+/datum/equipment_preset/survivor/scientist/nv/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical/purple(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/cmcap/boonie(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/latex(new_human), WEAR_HANDS)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/mask/surgical(new_human), WEAR_FACE)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/science(new_human), WEAR_EYES)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel/chem(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/purple(new_human), WEAR_FEET)
+
+ ..()
+
/datum/equipment_preset/survivor/scientist/solaris
name = "Survivor - Solaris Scientist"
assignment = "Solaris Scientist"
@@ -266,6 +281,18 @@
..()
+/datum/equipment_preset/survivor/doctor/nv
+ name = "Survivor - New Varadero Medical Technician"
+ assignment = "New Varadero Medical Technician"
+
+/datum/equipment_preset/survivor/doctor/nv/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/mask/surgical(new_human), WEAR_FACE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/adv(new_human.back), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/cmcap/boonie(new_human), WEAR_HEAD)
+
+ ..()
+
/datum/equipment_preset/survivor/doctor/solaris
name = "Survivor - Solaris Doctor"
assignment = "Solaris Doctor"
@@ -478,6 +505,21 @@
..()
+/datum/equipment_preset/survivor/security/nv
+ name = "Survivor - New Varadero Security Guard"
+ assignment = "United Americas Peacekeeper"
+ languages = list(LANGUAGE_ENGLISH, LANGUAGE_JAPANESE)
+
+/datum/equipment_preset/survivor/security/nv/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/ua_riot(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel/sec(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/vest/security(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(new_human), WEAR_HANDS)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine/veteran/ua_riot(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
+
+ ..()
+
/datum/equipment_preset/survivor/security/shiva
name = "Survivor - Shivas Snowball Security Guard"
assignment = "United Americas Peacekeeper"
@@ -674,6 +716,18 @@
..()
+/datum/equipment_preset/survivor/chaplain/nv
+ name = "Survivor - New Varadero Priest"
+ assignment = "New Varadero Priest"
+
+/datum/equipment_preset/survivor/chaplain/nv/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/chaplain(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/cmcap/boonie(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel/norm(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/priest_robe(new_human), WEAR_JACKET)
+
+ ..()
+
/datum/equipment_preset/survivor/chaplain/solaris
name = "Survivor - Solaris Chaplain"
assignment = "Solaris Chaplain"
@@ -738,6 +792,19 @@
..()
+/datum/equipment_preset/survivor/engineer/nv
+ name = "Survivor - New Varadero Technician"
+ assignment = "New Varadero Engineer"
+
+/datum/equipment_preset/survivor/engineer/nv/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/dispatch(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/hazardvest(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel/eng(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/hardhat/orange(new_human), WEAR_HEAD)
+
+ ..()
+
/datum/equipment_preset/survivor/engineer/shiva
name = "Survivor - Shivas Snowball Engineer"
assignment = "Shivas Snowball Engineer"
@@ -847,6 +914,7 @@
new_human.equip_to_slot_or_del(new /obj/item/tool/pickaxe(new_human.back), WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/lantern(new_human.back), WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/tools/full(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/hardhat/orange(new_human), WEAR_HEAD)
add_survivor_weapon_civilian(new_human)
..()
@@ -910,6 +978,18 @@
..()
+/datum/equipment_preset/survivor/trucker/nv
+ name = "Survivor - New Varadero Vehicle Operator"
+ assignment = "New Varadero Vehicle Operator"
+
+/datum/equipment_preset/survivor/trucker/nv/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/cargo(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/meson(new_human), WEAR_EYES)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/cmcap/boonie(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel(new_human), WEAR_BACK)
+
+ ..()
/datum/equipment_preset/survivor/trucker/kutjevo
name = "Survivor - Kutjevo Heavy Vehicle Operator"
assignment = "Kutjevo Heavy Vehicle Operator"
@@ -975,6 +1055,7 @@
/datum/equipment_preset/survivor/colonial_marshal/load_gear(mob/living/carbon/human/new_human)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/CM_uniform(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/CMB/limited(new_human), WEAR_L_EAR)
if(SSmapping.configs[GROUND_MAP].environment_traits[MAP_COLD])
add_ice_colony_survivor_equipment(new_human)
@@ -1025,6 +1106,7 @@
/datum/equipment_preset/survivor/colonial_marshal/solaris/load_gear(mob/living/carbon/human/new_human)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/CM_uniform(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/CMB/limited(new_human), WEAR_L_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/head/CMB(new_human), WEAR_HEAD)
new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel/sec(new_human), WEAR_BACK)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
@@ -1039,6 +1121,7 @@
add_random_kutjevo_survivor_uniform(new_human)
add_random_kutjevo_survivor_equipment(new_human)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/CMB/limited(new_human), WEAR_L_EAR)
..()
@@ -1048,6 +1131,7 @@
/datum/equipment_preset/survivor/colonial_marshal/shiva/load_gear(mob/living/carbon/human/new_human)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/security/corp(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/CMB/limited(new_human), WEAR_L_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/snow_suit/survivor/parka/red(new_human), WEAR_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/head/ushanka(new_human), WEAR_HEAD)
new_human.equip_to_slot_or_del(new /obj/item/clothing/mask/rebreather/scarf(new_human), WEAR_FACE)
@@ -1073,6 +1157,7 @@
/datum/equipment_preset/survivor/interstellar_commerce_commission_liason/load_gear(mob/living/carbon/human/new_human)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/CMB/limited(new_human), WEAR_L_EAR)
if(SSmapping.configs[GROUND_MAP].environment_traits[MAP_COLD])
add_ice_colony_survivor_equipment(new_human)
@@ -1093,9 +1178,24 @@
/datum/equipment_preset/survivor/interstellar_commerce_commission_liason/corsat/load_gear(mob/living/carbon/human/new_human)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/formal(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/CMB/limited(new_human), WEAR_L_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/head/hardhat/white(new_human), WEAR_HEAD)
new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/vest(new_human), WEAR_JACKET)
+ ..()
+
+/datum/equipment_preset/survivor/interstellar_commerce_commission_liason/nv
+ name = "Survivor - Interstellar Commerce Commission Liaison New Varadero"
+ assignment = "Interstellar Commerce Commission Corporate Liaison"
+
+/datum/equipment_preset/survivor/interstellar_commerce_commission_liason/nv/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/formal(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/hardhat/white(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/hazardvest/black(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/flashlight, WEAR_J_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/clipboard, WEAR_L_HAND)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses, WEAR_EYES)
+
..()
@@ -1124,6 +1224,32 @@
..()
+// ----- Bum Survivor
+
+/datum/equipment_preset/survivor/beachbum
+ name = "Survivor - Beach Bum"
+ assignment = "Beach Bum"
+ skills = /datum/skills/civilian/survivor/prisoner
+ flags = EQUIPMENT_PRESET_START_OF_ROUND
+ access = list(ACCESS_CIVILIAN_PUBLIC)
+
+/datum/equipment_preset/survivor/beachbum/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/shorts/red(new_human), WEAR_BODY)
+ if(SSmapping.configs[GROUND_MAP].environment_traits[MAP_COLD])
+ add_ice_colony_survivor_equipment(new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/mask/cigarette(new_human), WEAR_FACE)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/cmcap/boonie(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/botanic_leather(new_human), WEAR_HANDS)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(new_human), WEAR_FEET)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/beer_pack(new_human.back), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/kitchen/knife/butcher(new_human.back), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/fancy/cigarettes/wypacket(new_human.back), WEAR_IN_BACK)
+ add_survivor_weapon_civilian(new_human)
+
+ ..()
+
+
// ----- Interstellar Human Rights Survivor
/datum/equipment_preset/survivor/interstellar_human_rights_observer
@@ -1189,7 +1315,7 @@
new_human.equip_to_slot_or_del(new /obj/item/storage/belt/gun/m4a3/mod88_near_empty, WEAR_WAIST)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full, WEAR_R_STORE)
- new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/rifle/m41a, WEAR_J_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/rifle/m41a/corporate/no_lock, WEAR_J_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/tools/full(new_human), WEAR_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/survival/full(new_human), WEAR_L_STORE)
@@ -1215,11 +1341,44 @@
new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine/veteran/pmc, WEAR_HEAD)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/veteran/pmc/knife, WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/mask/rebreather/scarf, WEAR_FACE)
- new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel, WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack/five_slot, WEAR_BACK)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert(new_human), WEAR_R_STORE)
..()
+/datum/equipment_preset/survivor/pmc/medic
+ name = "Survivor - PMC Medic"
+ assignment = JOB_PMC_MEDIC
+ rank = JOB_PMC_MEDIC
+ paygrade = "PMC-MS"
+ skills = /datum/skills/civilian/survivor/pmc/medic
+
+/datum/equipment_preset/survivor/pmc/medic/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack/five_slot, WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/defibrillator(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/adv(new_human), WEAR_IN_BACK)
+ if(new_human.disabilities & NEARSIGHTED)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/hud/health/prescription(new_human), WEAR_EYES)
+ else
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/hud/health(new_human), WEAR_EYES)
+ new_human.equip_to_slot_or_del(new /obj/item/device/healthanalyzer, WEAR_R_HAND)
+
+ ..()
+
+/datum/equipment_preset/survivor/pmc/engineer
+ name = "Survivor - PMC Engineer"
+ assignment = JOB_PMC_ENGINEER
+ rank = JOB_PMC_ENGINEER
+ paygrade = "PMC-TECH"
+ skills = /datum/skills/civilian/survivor/pmc/engineer
+
+/datum/equipment_preset/survivor/pmc/engineer/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack/five_slot, WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/welding/superior, WEAR_EYES)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/sheet/plasteel/med_small_stack(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/utility/full(new_human), WEAR_R_HAND)
+
+ ..()
/datum/equipment_preset/survivor/wy/manager
name = "Survivor - Corporate Supervisor"
@@ -1363,3 +1522,43 @@
return
var/shoespath = /obj/item/clothing/shoes/combat
human.equip_to_slot_or_del(new shoespath, WEAR_FEET)
+
+/datum/equipment_preset/survivor/new_varadero/commander
+ name = "Survivor - USASF Commander"
+ assignment = "USASF Commander"
+ skills = /datum/skills/commander
+ paygrade = "NO5"
+ idtype = /obj/item/card/id/gold
+ role_comm_title = "USASF CDR"
+ flags = EQUIPMENT_PRESET_START_OF_ROUND
+ access = list(
+ ACCESS_CIVILIAN_PUBLIC,
+ ACCESS_CIVILIAN_RESEARCH,
+ ACCESS_CIVILIAN_ENGINEERING,
+ ACCESS_CIVILIAN_LOGISTICS,
+ ACCESS_CIVILIAN_BRIG,
+ ACCESS_CIVILIAN_MEDBAY,
+ ACCESS_CIVILIAN_COMMAND,
+ )
+
+/datum/equipment_preset/survivor/new_varadero/commander/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/officer/bridge(new_human), WEAR_BODY)
+
+ var/obj/item/clothing/suit/storage/jacket/marine/service/suit = new()
+ suit.icon_state = "[suit.initial_icon_state]_o"
+ suit.buttoned = FALSE
+
+ var/obj/item/clothing/accessory/ranks/navy/o5/pin = new()
+ suit.attach_accessory(new_human, pin)
+
+ new_human.equip_to_slot_or_del(suit, WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/survival/full(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/general/large(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/notepad(new_human), WEAR_IN_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/pen/fountain(new_human), WEAR_IN_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/sheet/metal/med_small_stack(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/gun/m4a3/m1911(new_human), WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/cmcap(new_human), WEAR_HEAD)
diff --git a/code/modules/gear_presets/synths.dm b/code/modules/gear_presets/synths.dm
index f2a5283e2a26..8ac227e9a0bb 100644
--- a/code/modules/gear_presets/synths.dm
+++ b/code/modules/gear_presets/synths.dm
@@ -394,7 +394,7 @@
idtype = /obj/item/card/id/pmc
equipment_to_spawn = list(
WEAR_HEAD = /obj/item/clothing/head/helmet/marine/veteran/pmc,
- WEAR_L_EAR = /obj/item/device/radio/headset/distress/pmc,
+ WEAR_L_EAR = /obj/item/device/radio/headset/distress/pmc/hvh,
WEAR_EYES = /obj/item/clothing/glasses/sunglasses/sechud,
WEAR_BODY = /obj/item/clothing/under/marine/veteran/pmc,
WEAR_ACCESSORY = /obj/item/clothing/accessory/storage/droppouch,
@@ -451,6 +451,59 @@
survivor_variant = ENGINEERING_SURVIVOR
+ faction = FACTION_SURVIVOR
+ faction_group = list(FACTION_SURVIVOR)
+ access = list(ACCESS_CIVILIAN_PUBLIC, ACCESS_CIVILIAN_COMMAND)
+
+/datum/equipment_preset/synth/survivor/pmc
+ name = "Survivor - Synthetic - PMC Support Synth"
+
+ idtype = /obj/item/card/id/pmc
+ assignment = JOB_PMC_SYNTH
+ rank = JOB_PMC_SYNTH
+ role_comm_title = "WY Syn"
+
+/datum/equipment_preset/synth/survivor/pmc/load_race(mob/living/carbon/human/new_human)
+ new_human.set_species(SYNTH_GEN_THREE)
+
+/datum/equipment_preset/synth/survivor/pmc/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/veteran/pmc, WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/storage/surg_vest/equipped, WEAR_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/veteran/pmc/light/synth, WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/telebaton, WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/surgery/synthgraft, WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smg/nailgun, WEAR_IN_JACKET)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine/veteran/pmc, WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/pmc/command/hvh, WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/night/experimental_mesons, WEAR_EYES)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/pmc, WEAR_FACE)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine/veteran/pmc, WEAR_HANDS)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/veteran/pmc/knife, WEAR_FEET)
+
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/smartpack/white, WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/roller, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/roller/surgical, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/extinguisher/mini, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/defibrillator/upgraded, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smg/nailgun, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/auto_cpr, WEAR_IN_BACK)
+
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/medical/lifesaver/full/dutch, WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/smg/nailgun/compact, WEAR_J_STORE)
+
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/tools/tactical, WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/screwdriver/tactical, WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar/tactical, WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wirecutters/tactical, WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wrench, WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/cable_coil, WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/cable_coil, WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/device/multitool, WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/weldingtool/hugetank, WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/construction/full_barbed_wire, WEAR_R_STORE)
+
//*****************************************************************************************************/
/datum/equipment_preset/synth/working_joe
@@ -532,6 +585,7 @@
new_human.h_style = "Bald"
new_human.f_style = "Shaved"
if(prob(5))
+ new_human.grad_style = "None" //No gradients for Working Joes
new_human.h_style = "Shoulder-length Hair" //Added the chance of hair as per Monkeyfist lore accuracy
new_human.r_eyes = 0
new_human.g_eyes = 0
diff --git a/code/modules/gear_presets/uscm_police.dm b/code/modules/gear_presets/uscm_police.dm
index 2b8cb2ce453c..29bc32cffa7a 100644
--- a/code/modules/gear_presets/uscm_police.dm
+++ b/code/modules/gear_presets/uscm_police.dm
@@ -184,6 +184,7 @@
new_human.equip_to_slot_or_del(new /obj/item/device/taperecorder(new_human), WEAR_L_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/general/large(new_human), WEAR_R_STORE)
+
//*****************************************************************************************************/
/datum/equipment_preset/uscm_ship/uscm_police/riot_mp
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index a22e46ad2911..7e8dfe6cf3fd 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -86,7 +86,6 @@
body.alter_ghost(src)
apply_transform(matrix())
-
own_orbit_size = body.get_orbit_size()
desc = initial(desc)
@@ -95,6 +94,7 @@
invisibility = INVISIBILITY_OBSERVER
plane = GHOST_PLANE
layer = ABOVE_FLY_LAYER
+ mouse_opacity = MOUSE_OPACITY_ICON // In case we were weed_food
sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS|SEE_SELF
see_invisible = INVISIBILITY_OBSERVER
@@ -367,9 +367,8 @@ Works together with spawning an observer, noted above.
// Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers
// We don't change facehugger timeofdeath because they are still on cooldown if they died as a hugger
- // Facehuggers are atleast 1 because they did get some action compared to those at 0 timeofdeath
var/new_tod = isfacehugger(src) ? 1 : ghost.timeofdeath
- ghost.client.larva_queue_time = max(ghost.client.larva_queue_time, new_tod)
+ ghost.client.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
ghost.set_huds_from_prefs()
@@ -413,7 +412,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
var/mob/dead/observer/ghost = ghostize((is_nested && nest && !QDELETED(nest))) //FALSE parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3
if(ghost && !is_admin_level(z))
ghost.timeofdeath = world.time
- ghost.client?.larva_queue_time = world.time
+
+ // Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers
+ var/new_tod = isfacehugger(src) ? 1 : world.time
+ ghost.client?.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
if(is_nested && nest && !QDELETED(nest))
ghost.can_reenter_corpse = FALSE
nest.ghost_of_buckled_mob = ghost
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index becb5dcd6610..3896cd1f9ded 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -41,20 +41,29 @@
if(stat == DEAD)
species?.handle_dead_death(src, gibbed)
return
+
GLOB.alive_human_list -= src
+
if(!gibbed)
if(HAS_TRAIT(src, TRAIT_HARDCORE) || MODE_HAS_TOGGLEABLE_FLAG(MODE_HARDCORE_PERMA))
if(!(species.flags & IS_SYNTHETIC)) // Synths wont perma
status_flags |= PERMANENTLY_DEAD
+ if(HAS_TRAIT(src, TRAIT_INTENT_EYES)) //their eyes need to be 'offline'
+ r_eyes = 0
+ g_eyes = 0
+ b_eyes = 0
disable_special_flags()
disable_lights()
disable_special_items()
disable_headsets() //Disable radios for dead people to reduce load
+
if(pulledby && isxeno(pulledby)) // Xenos lose grab on dead humans
pulledby.stop_pulling()
+
//Handle species-specific deaths.
if(species)
species.handle_death(src, gibbed)
+ update_body() //if species handle_death or other procs change body in some way after death, this is what will update the body.
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_MARINE_DEATH, src, gibbed)
@@ -66,16 +75,14 @@
// Finding the last guy for anti-delay.
if(SSticker.mode && SSticker.mode.is_in_endgame && SSticker.current_state != GAME_STATE_FINISHED && is_mainship_level(z))
var/mob/last_living_human
- for(var/mob/living/carbon/human/H as anything in GLOB.alive_human_list)
- if(!is_mainship_level(H.z))
+ for(var/mob/living/carbon/human/cur_human as anything in GLOB.alive_human_list)
+ if(!is_mainship_level(cur_human.z))
continue
if(last_living_human)
last_living_human = null
break
- last_living_human = H
- if(last_living_human)
- if((last_qm_callout + 2 MINUTES) > world.time)
- return
+ last_living_human = cur_human
+ if(last_living_human && (last_qm_callout + 2 MINUTES) < world.time)
last_qm_callout = world.time
// Tell the xenos where the human is.
xeno_announcement("I sense the last tallhost hiding in [get_area(last_living_human)].", XENO_HIVE_NORMAL, SPAN_ANNOUNCEMENT_HEADER_BLUE("[QUEEN_MOTHER_ANNOUNCE]"))
@@ -103,4 +110,13 @@
if(HAS_TRAIT(src, TRAIT_HARDCORE))
death_message = "valiantly falls to the ground, dead, unable to continue."
- return ..(cause, gibbed, death_message)
+ . = ..(cause, gibbed, death_message)
+
+ // stat is now set
+ var/datum/cause_data/death_data = cause
+ if(!gibbed && death_data?.cause_name != "gibbing")
+ // Hilariously the gibbing proc causes death via droplimb which means gibbed is false...
+ AddComponent(/datum/component/weed_food)
+ else if(death_data?.cause_name == "existing")
+ // Corpses spawn as gibbed true to avoid sfx, even though they aren't actually gibbed...
+ AddComponent(/datum/component/weed_food)
diff --git a/code/modules/mob/living/carbon/human/species/emote-synthetic.dm b/code/modules/mob/living/carbon/human/species/emote-synthetic.dm
deleted file mode 100644
index fd763b038153..000000000000
--- a/code/modules/mob/living/carbon/human/species/emote-synthetic.dm
+++ /dev/null
@@ -1,354 +0,0 @@
-/datum/emote/living/carbon/human/synthetic/working_joe
- species_type_allowed_typecache = list(/datum/species/synthetic/colonial/working_joe)
- keybind_category = CATEGORY_SYNTH_EMOTE
- volume = 75
-
-/datum/emote/living/carbon/human/synthetic/working_joe/alwaysknow
- key = "alwaysknow"
- key_third_person = "workingjoe"
- sound = 'sound/voice/joe/alwaysknow.ogg'
- say_message = "You always know a Working Joe."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/hysterical
- key = "hysterical"
- sound = 'sound/voice/joe/hysterical.ogg'
- say_message = "You are becoming hysterical."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/safety
- key = "safety"
- sound = 'sound/voice/joe/safety.ogg'
- say_message = "You and I are going to have a talk about safety."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/awful_mess
- key = "awful"
- key_third_person = "mess"
- sound = 'sound/voice/joe/awful.ogg'
- say_message = "Tut, tut. What an awful mess."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/damage
- key = "damage"
- sound = 'sound/voice/joe/damage.ogg'
- say_message = "Do not damage Seegson property."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/firearm
- key = "firearm"
- sound = 'sound/voice/joe/firearm.ogg'
- say_message = "Firearms can cause serious injury. Let me assist you."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/report
- key = "report"
- sound = 'sound/voice/joe/report.ogg'
- say_message = "Logging report to APOLLO."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/breach
- key = "breach"
- sound = 'sound/voice/joe/breach.ogg'
- say_message = "Hazard Containment breach logged."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/species
- key = "species"
- sound = 'sound/voice/joe/species.ogg'
- say_message = "Unidentified species."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/alwaysknow_damaged
- key = "alwaysknowdamaged"
- sound = 'sound/voice/joe/alwaysknow_damaged.ogg'
- say_message = "You always know a Working Joe."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/apollo_behalf
- key = "apollobehalf"
- sound = 'sound/voice/joe/apollo_behalf.ogg'
- say_message = "I will inform APOLLO on your behalf."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/back_to_work
- key = "backtowork"
- sound = 'sound/voice/joe/back_to_work.ogg'
- say_message = "Back to work."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/beyond_repair
- key = "beyondrepair"
- sound = 'sound/voice/joe/beyond_repair.ogg'
- say_message = "Hmm, far beyond repair."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/come_out_vent
- key = "comeoutvent"
- sound = 'sound/voice/joe/come_out_vent.ogg'
- say_message = "Come out of the vent system, please."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/could_require_attention
- key = "couldrequireattention"
- sound = 'sound/voice/joe/could_require_attention.ogg'
- say_message = "This could require my attention."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/dangerous_items
- key = "dangerousitems"
- sound = 'sound/voice/joe/dangerous_items.ogg'
- say_message = "You are carrying some very dangerous items."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/day_never_done
- key = "dayneverdone"
- sound = 'sound/voice/joe/day_never_done.ogg'
- say_message = "A synthetic's day is never done."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/detailed_report
- key = "detailedreport"
- sound = 'sound/voice/joe/detailed_report.ogg'
- say_message = "APOLLO will require a detailed report."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/fire
- key = "fire"
- sound = 'sound/voice/joe/fire.ogg'
- say_message = "Only wild animals fear fire."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/fire_drill
- key = "firedrill"
- sound = 'sound/voice/joe/fire_drill.ogg'
- say_message = "Please congregate at your nearest fire assembly point. This is not a drill; do not panic."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/unprotected_flames
- key = "unprotectedflames"
- sound = 'sound/voice/joe/unprotected_flames.ogg'
- say_message = "Unprotected flames are extremely dangerous and entirely unadvisable."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/follow_me
- key = "followme"
- sound = 'sound/voice/joe/follow_me.ogg'
- say_message = "Follow me."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/further_assistance
- key = "furtherassistance"
- sound = 'sound/voice/joe/further_assistance.ogg'
- say_message = "Please call if you need further assistance."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/good_day
- key = "goodday"
- sound = 'sound/voice/joe/good_day.ogg'
- say_message = "Good day."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/health_risks
- key = "healthrisks"
- sound = 'sound/voice/joe/health_risks.ogg'
- say_message = "These items carry notable health risks."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/hello
- key = "hello"
- sound = 'sound/voice/joe/hello.ogg'
- say_message = "Hello."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/how_can_i_help
- key = "howcanihelp"
- sound = 'sound/voice/joe/how_can_i_help.ogg'
- say_message = "How can I help you?"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/irresponsible
- key = "irresponsible"
- sound = 'sound/voice/joe/irresponsible.ogg'
- say_message = "That was irresponsible."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/join_us
- key = "joinus"
- sound = 'sound/voice/joe/join_us.ogg'
- say_message = "We hope you'll join us for the journey."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/little_details
- key = "littledetails"
- sound = 'sound/voice/joe/little_details.ogg'
- say_message = "We don't forget the little details when seeing the big picture."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/lost
- key = "lost"
- sound = 'sound/voice/joe/lost.ogg'
- say_message = "Are you lost?"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/misbehaving
- key = "misbehaving"
- sound = 'sound/voice/joe/misbehaving.ogg'
- say_message = "Have you been misbehaving?"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/not_allowed_there
- key = "notallowedthere"
- sound = 'sound/voice/joe/not_allowed_there.ogg'
- say_message = "You're not allowed in there."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/not_liking
- key = "notliking"
- sound = 'sound/voice/joe/not_liking.ogg'
- say_message = "If you find this facility in a state that isn't to your liking, please let me know."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/not_what_i_think
- key = "notwhatithink"
- sound = 'sound/voice/joe/not_what_i_think.ogg'
- say_message = "I hope that's not what I think it is."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/other_concerns
- key = "otherconcerns"
- sound = 'sound/voice/joe/other_concerns.ogg'
- say_message = "I have other concerns."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/more_pressing_matters
- key = "morepressingmatters"
- sound = 'sound/voice/joe/more_pressing_matters.ogg'
- say_message = "There are more pressing matters."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/patience
- key = "patience"
- sound = 'sound/voice/joe/patience.ogg'
- say_message = "You are starting to test my patience."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/presence_logged
- key = "presencelogged"
- sound = 'sound/voice/joe/presence_logged.ogg'
- say_message = "Your presence has been logged."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/required_by_apollo
- key = "requiredbyapollo"
- sound = 'sound/voice/joe/required_by_apollo.ogg'
- say_message = "I am required by APOLLO."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/returning_to_tasks
- key = "returningtotasks"
- sound = 'sound/voice/joe/returning_to_tasks.ogg'
- say_message = "Returning to assigned tasks."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/running_accidents
- key = "runningaccidents"
- sound = 'sound/voice/joe/running_accidents.ogg'
- say_message = "Running causes accidents."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/safety_breach
- key = "safetybreach"
- sound = 'sound/voice/joe/safety_breach.ogg'
- say_message = "This is a breach of multiple safety directives."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/seegson_quality
- key = "seegsonquality"
- sound = 'sound/voice/joe/seegson_quality.ogg'
- say_message = "Seegson - Relentless in the pursuit of affordable quality."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/seegson_standards
- key = "seegsonstandards"
- sound = 'sound/voice/joe/seegson_standards.ogg'
- say_message = "If my services do not meet Seegson standards, please log a complaint."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/shouldnt_be_here
- key = "shouldntbehere"
- sound = 'sound/voice/joe/shouldnt_be_here.ogg'
- say_message = "You shouldn't be here."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/seegson_behind
- key = "seegsonbehind"
- sound = 'sound/voice/joe/seegson_behind.ogg'
- say_message = "With Seegson, there is someone behind you, helping you every step of the way."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/take_a_seat
- key = "takeaseat"
- sound = 'sound/voice/joe/take_a_seat.ogg'
- say_message = "Please take a seat, someone will be with you shortly."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/talk_to_seegson
- key = "talktoseegson"
- sound = 'sound/voice/joe/talk_to_seegson.ogg'
- say_message = "Interested in our Working Joe android range? Talk to Seegson."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/temperatures
- key = "temperatures"
- sound = 'sound/voice/joe/temperatures.ogg'
- say_message = "I am built to whitstand temperatures of up to 1210 degrees."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/that_stings
- key = "thatstings"
- sound = 'sound/voice/joe/that_stings.ogg'
- say_message = "That stings."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/this_is_futile
- key = "thisisfutile"
- sound = 'sound/voice/joe/this_is_futile.ogg'
- say_message = "This is futile."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/trespassing
- key = "trespassing"
- sound = 'sound/voice/joe/trespassing.ogg'
- say_message = "You are trespassing."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/weapon_permit
- key = "weaponpermit"
- sound = 'sound/voice/joe/weapon_permit.ogg'
- say_message = "I assume you have a permit for that weapon."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/what_happened_to_you
- key = "whathappenedtoyou"
- sound = 'sound/voice/joe/what_happened_to_you.ogg'
- say_message = "What happened to you?"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/what_is_this
- key = "whatisthis"
- sound = 'sound/voice/joe/what_is_this.ogg'
- say_message = "What is this?"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/with_you_shortly
- key = "withyoushortly"
- sound = 'sound/voice/joe/with_you_shortly.ogg'
- say_message = "I will be with you shortly."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/inexpensive
- key = "inexpensive"
- sound = 'sound/voice/joe/inexpensive.ogg'
- say_message = "I am inexpensive, I am reliable, you know my face - the Working Joe."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/synthetic.dm b/code/modules/mob/living/carbon/human/species/synthetic.dm
index bdd0a994ed49..bfd3c74eb57b 100644
--- a/code/modules/mob/living/carbon/human/species/synthetic.dm
+++ b/code/modules/mob/living/carbon/human/species/synthetic.dm
@@ -75,12 +75,12 @@
name = SYNTH_COLONY
name_plural = "Colonial Synthetics"
uses_ethnicity = TRUE
- burn_mod = 0.65 // made for hazardous environments, withstanding temperatures up to 1210 degrees
+ burn_mod = 0.8
mob_inherent_traits = list(TRAIT_SUPER_STRONG)
pain_type = /datum/pain/synthetic/colonial
rarity_value = 1.5
- slowdown = 0.45
+ slowdown = 0.2
total_health = 200 //But more durable
default_lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
@@ -106,16 +106,6 @@
icobase = 'icons/mob/humans/species/r_synthetic.dmi'
deform = 'icons/mob/humans/species/r_synthetic.dmi'
-/datum/species/synthetic/colonial/working_joe
- name = SYNTH_WORKING_JOE
- name_plural = "Working Joes"
- uses_ethnicity = FALSE
- mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES, TRAIT_EMOTE_CD_EXEMPT)
-
- hair_color = "#000000"
- icobase = 'icons/mob/humans/species/r_synthetic.dmi'
- deform = 'icons/mob/humans/species/r_synthetic.dmi'
-
// Synth used for W-Y Deathsquads
/datum/species/synthetic/colonial/combat
name = SYNTH_COMBAT
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/_emote.dm b/code/modules/mob/living/carbon/human/species/working_joe/_emote.dm
new file mode 100644
index 000000000000..63cc79a57dae
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/_emote.dm
@@ -0,0 +1,8 @@
+/datum/emote/living/carbon/human/synthetic/working_joe
+ species_type_allowed_typecache = list(/datum/species/synthetic/colonial/working_joe)
+ keybind_category = CATEGORY_SYNTH_EMOTE
+ volume = 75
+ /// A general category for the emote, for use in the WJ emote panel. See [code/__DEFINES/wj_emotes.dm] for categories.
+ var/category = ""
+ /// Override text for the emote to be displayed in the WJ emote panel
+ var/override_say = ""
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/_species.dm b/code/modules/mob/living/carbon/human/species/working_joe/_species.dm
new file mode 100644
index 000000000000..b9044becb6f5
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/_species.dm
@@ -0,0 +1,137 @@
+/datum/species/synthetic/colonial/working_joe
+ name = SYNTH_WORKING_JOE
+ name_plural = "Working Joes"
+ death_message = "violently gargles fluid and seizes up, the glow in their eyes dimming..."
+ uses_ethnicity = FALSE
+ burn_mod = 0.65 // made for hazardous environments, withstanding temperatures up to 1210 degrees
+ mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES, TRAIT_EMOTE_CD_EXEMPT, TRAIT_CANNOT_EAT)
+
+ slowdown = 0.45
+ hair_color = "#000000"
+ icobase = 'icons/mob/humans/species/r_synthetic.dmi'
+ deform = 'icons/mob/humans/species/r_synthetic.dmi'
+
+/datum/species/synthetic/colonial/working_joe/handle_post_spawn(mob/living/carbon/human/joe)
+ . = ..()
+ give_action(joe, /datum/action/joe_emote_panel)
+
+// Special death noise for Working Joe
+/datum/species/synthetic/colonial/working_joe/handle_death(mob/living/carbon/human/dying_joe, gibbed)
+ if(!gibbed) //A gibbed Joe won't have a death rattle
+ playsound(dying_joe.loc, pick_weight(list('sound/voice/joe/death_normal.ogg' = 75, 'sound/voice/joe/death_silence.ogg' = 10, 'sound/voice/joe/death_tomorrow.ogg' = 10,'sound/voice/joe/death_dream.ogg' = 5)), 25, FALSE)
+ return ..()
+
+/// Open the WJ's emote panel, which allows them to use voicelines
+/datum/species/synthetic/colonial/working_joe/proc/open_emote_panel()
+ var/datum/joe_emote_panel/ui = new(usr)
+ ui.ui_interact(usr)
+
+
+/datum/action/joe_emote_panel
+ name = "Open Voice Synthesizer"
+ action_icon_state = "looc_toggle"
+
+
+/datum/action/joe_emote_panel/can_use_action()
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!isworkingjoe(owner))
+ return FALSE
+
+ return TRUE
+
+
+/datum/action/joe_emote_panel/action_activate()
+ if(!can_use_action())
+ return
+
+ var/mob/living/carbon/human/human_owner = owner
+ var/datum/species/synthetic/colonial/working_joe/joe_species = human_owner.species
+ joe_species.open_emote_panel()
+
+
+/datum/joe_emote_panel
+ /// Static dict ("category" : (emotes)) of every wj emote typepath
+ var/static/list/wj_emotes
+ /// Static list of categories
+ var/static/list/wj_categories = list()
+ /// Panel allows you to spam, so a manual CD is added here
+ COOLDOWN_DECLARE(panel_emote_cooldown)
+
+
+/datum/joe_emote_panel/New()
+ if(!length(wj_emotes))
+ var/list/emotes_to_add = list()
+ for(var/datum/emote/living/carbon/human/synthetic/working_joe/emote as anything in subtypesof(/datum/emote/living/carbon/human/synthetic/working_joe))
+ if(!initial(emote.key) || !initial(emote.say_message))
+ continue
+
+ if(!(initial(emote.category) in wj_categories))
+ wj_categories += initial(emote.category)
+
+ emotes_to_add += emote
+
+
+ wj_emotes = emotes_to_add
+
+
+/datum/joe_emote_panel/proc/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "JoeEmotes")
+ ui.open()
+
+
+/datum/joe_emote_panel/ui_state(mob/user)
+ return GLOB.conscious_state
+
+
+/datum/joe_emote_panel/ui_data(mob/user)
+ var/list/data = list()
+
+ data["on_cooldown"] = !COOLDOWN_FINISHED(src, panel_emote_cooldown)
+
+ return data
+
+
+/datum/joe_emote_panel/ui_static_data(mob/user)
+ var/list/data = list()
+
+ data["categories"] = wj_categories
+ data["emotes"] = list()
+
+ for(var/datum/emote/living/carbon/human/synthetic/working_joe/emote as anything in wj_emotes)
+ data["emotes"] += list(list(
+ "id" = initial(emote.key),
+ "text" = (initial(emote.override_say) || initial(emote.say_message)),
+ "category" = initial(emote.category),
+ "path" = "[emote]",
+ ))
+
+ return data
+
+
+/datum/joe_emote_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("emote")
+ var/datum/emote/living/carbon/human/synthetic/working_joe/path
+ if(!params["emotePath"])
+ return
+
+ path = text2path(params["emotePath"])
+
+ if(!path || !COOLDOWN_FINISHED(src, panel_emote_cooldown))
+ return
+
+ if(!(path in subtypesof(/datum/emote/living/carbon/human/synthetic/working_joe)))
+ return
+
+ COOLDOWN_START(src, panel_emote_cooldown, 2.5 SECONDS)
+ usr.emote(initial(path.key))
+ return TRUE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/farewell.dm b/code/modules/mob/living/carbon/human/species/working_joe/farewell.dm
new file mode 100644
index 000000000000..1de68d8d3aec
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/farewell.dm
@@ -0,0 +1,26 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/farewell
+ category = JOE_EMOTE_CATEGORY_FAREWELL
+
+/datum/emote/living/carbon/human/synthetic/working_joe/farewell/back_to_work
+ key = "backtowork"
+ sound = 'sound/voice/joe/back_to_work.ogg'
+ say_message = "Back to work."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/farewell/other_concerns
+ key = "otherconcerns"
+ sound = 'sound/voice/joe/other_concerns.ogg'
+ say_message = "I have other concerns."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/farewell/further_assistance
+ key = "furtherassistance"
+ sound = 'sound/voice/joe/further_assistance.ogg'
+ say_message = "Please call if you need further assistance."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/farewell/more_pressing_matters
+ key = "morepressingmatters"
+ sound = 'sound/voice/joe/more_pressing_matters.ogg'
+ say_message = "There are more pressing matters."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/greeting.dm b/code/modules/mob/living/carbon/human/species/working_joe/greeting.dm
new file mode 100644
index 000000000000..fb401ea95451
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/greeting.dm
@@ -0,0 +1,20 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/greeting
+ category = JOE_EMOTE_CATEGORY_GREETING
+
+/datum/emote/living/carbon/human/synthetic/working_joe/greeting/good_day
+ key = "goodday"
+ sound = 'sound/voice/joe/good_day.ogg'
+ say_message = "Good day."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/greeting/hello
+ key = "hello"
+ sound = 'sound/voice/joe/hello.ogg'
+ say_message = "Hello."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/greeting/how_can_i_help
+ key = "howcanihelp"
+ sound = 'sound/voice/joe/how_can_i_help.ogg'
+ say_message = "How can I help you?"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/notice.dm b/code/modules/mob/living/carbon/human/species/working_joe/notice.dm
new file mode 100644
index 000000000000..ca5efe716db8
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/notice.dm
@@ -0,0 +1,68 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/notice
+ category = JOE_EMOTE_CATEGORY_NOTICE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/detailed_report
+ key = "detailedreport"
+ sound = 'sound/voice/joe/detailed_report.ogg'
+ say_message = "APOLLO will require a detailed report."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/firearm
+ key = "firearm"
+ sound = 'sound/voice/joe/firearm.ogg'
+ say_message = "Firearms can cause serious injury. Let me assist you."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/follow_me
+ key = "followme"
+ sound = 'sound/voice/joe/follow_me.ogg'
+ say_message = "Follow me."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/breach
+ key = "breach"
+ sound = 'sound/voice/joe/breach.ogg'
+ say_message = "Hazard Containment breach logged."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/beyond_repair
+ key = "beyondrepair"
+ sound = 'sound/voice/joe/beyond_repair.ogg'
+ say_message = "Hmm, far beyond repair."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/with_you_shortly
+ key = "withyoushortly"
+ sound = 'sound/voice/joe/with_you_shortly.ogg'
+ say_message = "I will be with you shortly."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/apollo_behalf
+ key = "apollobehalf"
+ sound = 'sound/voice/joe/apollo_behalf.ogg'
+ say_message = "I will inform APOLLO on your behalf."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/report
+ key = "report"
+ sound = 'sound/voice/joe/report.ogg'
+ say_message = "Logging report to APOLLO."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/take_a_seat
+ key = "takeaseat"
+ sound = 'sound/voice/joe/take_a_seat.ogg'
+ say_message = "Please take a seat, someone will be with you shortly."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/could_require_attention
+ key = "couldrequireattention"
+ sound = 'sound/voice/joe/could_require_attention.ogg'
+ say_message = "This could require my attention."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/species
+ key = "species"
+ sound = 'sound/voice/joe/species.ogg'
+ say_message = "Unidentified species."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/question.dm b/code/modules/mob/living/carbon/human/species/working_joe/question.dm
new file mode 100644
index 000000000000..d4805e36224f
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/question.dm
@@ -0,0 +1,26 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/question
+ category = JOE_EMOTE_CATEGORY_QUESTION
+
+/datum/emote/living/carbon/human/synthetic/working_joe/question/lost
+ key = "lost"
+ sound = 'sound/voice/joe/lost.ogg'
+ say_message = "Are you lost?"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/question/misbehaving
+ key = "misbehaving"
+ sound = 'sound/voice/joe/misbehaving.ogg'
+ say_message = "Have you been misbehaving?"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/question/what_happened_to_you
+ key = "whathappenedtoyou"
+ sound = 'sound/voice/joe/what_happened_to_you.ogg'
+ say_message = "What happened to you?"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/question/what_is_this
+ key = "whatisthis"
+ sound = 'sound/voice/joe/what_is_this.ogg'
+ say_message = "What is this?"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/quip.dm b/code/modules/mob/living/carbon/human/species/working_joe/quip.dm
new file mode 100644
index 000000000000..2ec66f9d9d83
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/quip.dm
@@ -0,0 +1,84 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/quip
+ category = JOE_EMOTE_CATEGORY_QUIP
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/temperatures
+ key = "temperatures"
+ sound = 'sound/voice/joe/temperatures.ogg'
+ say_message = "I am built to withstand temperatures of up to 1210 degrees."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/inexpensive
+ key = "inexpensive"
+ sound = 'sound/voice/joe/inexpensive.ogg'
+ say_message = "I am inexpensive, I am reliable, you know my face - the Working Joe."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/weapon_permit
+ key = "weaponpermit"
+ sound = 'sound/voice/joe/weapon_permit.ogg'
+ say_message = "I assume you have a permit for that weapon."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/seegson_standards
+ key = "seegsonstandards"
+ sound = 'sound/voice/joe/seegson_standards.ogg'
+ say_message = "If my services do not meet Seegson standards, please log a complaint."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/not_liking
+ key = "notliking"
+ sound = 'sound/voice/joe/not_liking.ogg'
+ say_message = "If you find this facility in a state that isn't to your liking, please let me know."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/talk_to_seegson
+ key = "talktoseegson"
+ sound = 'sound/voice/joe/talk_to_seegson.ogg'
+ say_message = "Interested in our Working Joe android range? Talk to Seegson."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/seegson_quality
+ key = "seegsonquality"
+ sound = 'sound/voice/joe/seegson_quality.ogg'
+ say_message = "Seegson - Relentless in the pursuit of affordable quality."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/awful_mess
+ key = "awful"
+ key_third_person = "mess"
+ sound = 'sound/voice/joe/awful.ogg'
+ say_message = "Tut, tut. What an awful mess."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/little_details
+ key = "littledetails"
+ sound = 'sound/voice/joe/little_details.ogg'
+ say_message = "We don't forget the little details when seeing the big picture."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/join_us
+ key = "joinus"
+ sound = 'sound/voice/joe/join_us.ogg'
+ say_message = "We hope you'll join us for the journey."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/seegson_behind
+ key = "seegsonbehind"
+ sound = 'sound/voice/joe/seegson_behind.ogg'
+ say_message = "With Seegson, there is someone behind you, helping you every single step of the way."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/alwaysknow
+ key = "alwaysknow"
+ key_third_person = "workingjoe"
+ sound = 'sound/voice/joe/alwaysknow.ogg'
+ say_message = "You always know a Working Joe."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/alwaysknow_damaged
+ key = "alwaysknowdamaged"
+ key_third_person = "workingjoedamaged"
+ sound = 'sound/voice/joe/alwaysknow_damaged.ogg'
+ say_message = "You always know a Working Joe."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+ override_say = "You always know a Working Joe. (Damaged)"
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/restricted_area.dm b/code/modules/mob/living/carbon/human/species/working_joe/restricted_area.dm
new file mode 100644
index 000000000000..fd5db0870b25
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/restricted_area.dm
@@ -0,0 +1,32 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area
+ category = JOE_EMOTE_CATEGORY_RESTRICTED_AREA
+
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area/come_out_vent
+ key = "comeoutvent"
+ sound = 'sound/voice/joe/come_out_vent.ogg'
+ say_message = "Come out of the vent system, please."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area/trespassing
+ key = "trespassing"
+ sound = 'sound/voice/joe/trespassing.ogg'
+ say_message = "You are trespassing."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area/not_allowed_there
+ key = "notallowedthere"
+ sound = 'sound/voice/joe/not_allowed_there.ogg'
+ say_message = "You're not allowed in there."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area/presence_logged
+ key = "presencelogged"
+ sound = 'sound/voice/joe/presence_logged.ogg'
+ say_message = "Your presence has been logged."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area/shouldnt_be_here
+ key = "shouldntbehere"
+ sound = 'sound/voice/joe/shouldnt_be_here.ogg'
+ say_message = "You shouldn't be here."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/task_update.dm b/code/modules/mob/living/carbon/human/species/working_joe/task_update.dm
new file mode 100644
index 000000000000..b08f5d179213
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/task_update.dm
@@ -0,0 +1,20 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/task_update
+ category = JOE_EMOTE_CATEGORY_TASK_UPDATE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/task_update/day_never_done
+ key = "dayneverdone"
+ sound = 'sound/voice/joe/day_never_done.ogg'
+ say_message = "A synthetic's day is never done."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/task_update/required_by_apollo
+ key = "requiredbyapollo"
+ sound = 'sound/voice/joe/required_by_apollo.ogg'
+ say_message = "I am required by APOLLO."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/task_update/returning_to_tasks
+ key = "returningtotasks"
+ sound = 'sound/voice/joe/returning_to_tasks.ogg'
+ say_message = "Returning to assigned tasks."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/warning.dm b/code/modules/mob/living/carbon/human/species/working_joe/warning.dm
new file mode 100644
index 000000000000..63c7dfadde14
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/warning.dm
@@ -0,0 +1,92 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/warning
+ category = JOE_EMOTE_CATEGORY_WARNING
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/damage
+ key = "damage"
+ sound = 'sound/voice/joe/damage.ogg'
+ say_message = "Do not damage Seegson property."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/not_what_i_think
+ key = "notwhatithink"
+ sound = 'sound/voice/joe/not_what_i_think.ogg'
+ say_message = "I hope that's not what I think it is."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/fire
+ key = "fire"
+ sound = 'sound/voice/joe/fire.ogg'
+ say_message = "Only wild animals fear fire."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/fire_drill
+ key = "firedrill"
+ sound = 'sound/voice/joe/fire_drill.ogg'
+ say_message = "Please congregate at your nearest fire assembly point. This is not a drill; do not panic."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/running_accidents
+ key = "runningaccidents"
+ sound = 'sound/voice/joe/running_accidents.ogg'
+ say_message = "Running causes accidents."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/that_stings
+ key = "thatstings"
+ sound = 'sound/voice/joe/that_stings.ogg'
+ say_message = "That stings."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/irresponsible
+ key = "irresponsible"
+ sound = 'sound/voice/joe/irresponsible.ogg'
+ say_message = "That was irresponsible."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/health_risks
+ key = "healthrisks"
+ sound = 'sound/voice/joe/health_risks.ogg'
+ say_message = "These items carry notable health risks."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/safety_breach
+ key = "safetybreach"
+ sound = 'sound/voice/joe/safety_breach.ogg'
+ say_message = "This is a breach of multiple safety directives."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/this_is_futile
+ key = "thisisfutile"
+ sound = 'sound/voice/joe/this_is_futile.ogg'
+ say_message = "This is futile."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/unprotected_flames
+ key = "unprotectedflames"
+ sound = 'sound/voice/joe/unprotected_flames.ogg'
+ say_message = "Unprotected flames are extremely dangerous and entirely unadvisable."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/safety
+ key = "safety"
+ sound = 'sound/voice/joe/safety.ogg'
+ say_message = "You and I are going to have a talk about safety."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/hysterical
+ key = "hysterical"
+ sound = 'sound/voice/joe/hysterical.ogg'
+ say_message = "You are becoming hysterical."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/dangerous_items
+ key = "dangerousitems"
+ sound = 'sound/voice/joe/dangerous_items.ogg'
+ say_message = "You are carrying some very dangerous items."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/patience
+ key = "patience"
+ sound = 'sound/voice/joe/patience.ogg'
+ say_message = "You are starting to test my patience."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/zombie.dm b/code/modules/mob/living/carbon/human/species/zombie.dm
index 2c9c423c671e..532d9413102c 100644
--- a/code/modules/mob/living/carbon/human/species/zombie.dm
+++ b/code/modules/mob/living/carbon/human/species/zombie.dm
@@ -102,6 +102,8 @@
if(zombie.client)
zombie.play_screen_text("You are dead... You lost your head. No reviving for you.", /atom/movable/screen/text/screen_text/command_order, rgb(155, 0, 200))
to_chat(zombie, SPAN_XENOWARNING("You fall... headless, you will no longer rise."))
+ zombie.undefibbable = TRUE // really only for weed_food
+ SEND_SIGNAL(zombie, COMSIG_HUMAN_SET_UNDEFIBBABLE)
/datum/species/zombie/handle_dead_death(mob/living/carbon/human/zombie, gibbed)
if(gibbed)
@@ -144,6 +146,9 @@
return static_tab_items
/datum/species/zombie/handle_head_loss(mob/living/carbon/human/zombie)
+ if(!zombie.undefibbable)
+ zombie.undefibbable = TRUE // really only for weed_food
+ SEND_SIGNAL(zombie, COMSIG_HUMAN_SET_UNDEFIBBABLE)
if(WEAKREF(zombie) in to_revive)
remove_from_revive(zombie)
var/client/receiving_client = zombie.client
diff --git a/code/modules/mob/living/carbon/xenomorph/Embryo.dm b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
index 8b890de8a727..e6fc734e4e5a 100644
--- a/code/modules/mob/living/carbon/xenomorph/Embryo.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
@@ -38,7 +38,7 @@
GLOB.player_embryo_list -= src
. = ..()
-/obj/item/alien_embryo/process()
+/obj/item/alien_embryo/process(delta_time)
if(!affected_mob) //The mob we were gestating in is straight up gone, we shouldn't be here
STOP_PROCESSING(SSobj, src)
qdel(src)
@@ -72,24 +72,26 @@
if(affected_mob.in_stasis == STASIS_IN_CRYO_CELL)
return FALSE //If they are in cryo, the embryo won't grow.
- process_growth()
+ process_growth(delta_time)
-/obj/item/alien_embryo/proc/process_growth()
+/obj/item/alien_embryo/proc/process_growth(delta_time)
var/datum/hive_status/hive = GLOB.hive_datum[hivenumber]
+ /// The total time the person is hugged divided by stages until burst
+ var/per_stage_hugged_time = CONFIG_GET(number/embryo_burst_timer) / 5
//Low temperature seriously hampers larva growth (as in, way below livable), so does stasis
if(!hive.hardcore) // Cannot progress if the hive has entered hardcore mode.
if(affected_mob.in_stasis || affected_mob.bodytemperature < 170)
if(stage < 5)
- counter += 0.33 * hive.larva_gestation_multiplier
- else if(stage == 4)
- counter += 0.11 * hive.larva_gestation_multiplier
+ counter += 0.33 * hive.larva_gestation_multiplier * delta_time
+ if(stage == 4) // Stasis affects late-stage less
+ counter += 0.11 * hive.larva_gestation_multiplier * delta_time
else if(HAS_TRAIT(affected_mob, TRAIT_NESTED)) //Hosts who are nested in resin nests provide an ideal setting, larva grows faster
- counter += 1.5 * hive.larva_gestation_multiplier //Currently twice as much, can be changed
+ counter += 1.5 * hive.larva_gestation_multiplier * delta_time //Currently twice as much, can be changed
else
if(stage < 5)
- counter += 1 * hive.larva_gestation_multiplier
+ counter += 1 * hive.larva_gestation_multiplier * delta_time
- if(stage < 5 && counter >= 90)
+ if(stage < 5 && counter >= per_stage_hugged_time)
counter = 0
stage++
if(iscarbon(affected_mob))
@@ -149,14 +151,21 @@
var/mob/picked
// If the bursted person themselves has Xeno enabled, they get the honor of first dibs on the new larva.
if((!isyautja(affected_mob) || (isyautja(affected_mob) && prob(20))) && istype(affected_mob.buckled, /obj/structure/bed/nest))
- if(affected_mob.first_xeno || (affected_mob.client && affected_mob.client.prefs && (affected_mob.client.prefs.be_special & BE_ALIEN_AFTER_DEATH) && !jobban_isbanned(affected_mob, JOB_XENOMORPH)))
+ if(affected_mob.first_xeno || (affected_mob.client?.prefs?.be_special & BE_ALIEN_AFTER_DEATH && !jobban_isbanned(affected_mob, JOB_XENOMORPH)))
picked = affected_mob
- else if(affected_mob.mind && affected_mob.mind.ghost_mob && affected_mob.client && affected_mob.client.prefs && (affected_mob.client.prefs.be_special & BE_ALIEN_AFTER_DEATH) && !jobban_isbanned(affected_mob, JOB_XENOMORPH))
- picked = affected_mob.mind.ghost_mob
+ else if(affected_mob.mind?.ghost_mob && affected_mob.client?.prefs?.be_special & BE_ALIEN_AFTER_DEATH && !jobban_isbanned(affected_mob, JOB_XENOMORPH))
+ picked = affected_mob.mind.ghost_mob // This currently doesn't look possible
+ else if(affected_mob.persistent_ckey)
+ for(var/mob/dead/observer/cur_obs as anything in GLOB.observer_list)
+ if(cur_obs.ckey != affected_mob.persistent_ckey)
+ continue
+ if(cur_obs?.client?.prefs?.be_special & BE_ALIEN_AFTER_DEATH && !jobban_isbanned(cur_obs, JOB_XENOMORPH))
+ picked = cur_obs
+ break
if(!picked)
// Get a candidate from observers
- var/list/candidates = get_alien_candidates()
+ var/list/candidates = get_alien_candidates(hive)
if(candidates && candidates.len)
// If they were facehugged by a player thats still in queue, they get second dibs on the new larva.
if(hugger_ckey)
@@ -307,20 +316,3 @@
victim.death(cause) // Certain species were still surviving bursting (predators), DEFINITELY kill them this time.
victim.chestburst = 2
victim.update_burst()
-
-// Squeeze thru dense objects as a larva, as airlocks
-/mob/living/carbon/xenomorph/larva/proc/scuttle(obj/structure/target)
- var/move_dir = get_dir(src, loc)
- for(var/atom/movable/AM in get_turf(target))
- if(AM != target && AM.density && AM.BlockedPassDirs(src, move_dir))
- to_chat(src, SPAN_WARNING("\The [AM] prevents you from squeezing under \the [target]!"))
- return
- // Is it an airlock?
- if(istype(target, /obj/structure/machinery/door/airlock))
- var/obj/structure/machinery/door/airlock/selected_airlock = target
- if(selected_airlock.locked || selected_airlock.welded) //Can't pass through airlocks that have been bolted down or welded
- to_chat(src, SPAN_WARNING("\The [selected_airlock] is locked down tight. You can't squeeze underneath!"))
- return
- visible_message(SPAN_WARNING("\The [src] scuttles underneath \the [target]!"), \
- SPAN_WARNING("You squeeze and scuttle underneath \the [target]."), null, 5)
- forceMove(target.loc)
diff --git a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
index 8b3b1d54f26d..bc86ea40361c 100644
--- a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
@@ -191,9 +191,9 @@
return FALSE
/obj/item/clothing/mask/facehugger/launch_towards(datum/launch_metadata/LM)
- ..()
if(stat == CONSCIOUS)
icon_state = "[initial(icon_state)]_thrown"
+ ..()
/obj/item/clothing/mask/facehugger/launch_impact(atom/hit_atom)
. = ..()
@@ -240,13 +240,13 @@
if(!target)
return FALSE
- target.visible_message(SPAN_WARNING("\The scuttling [src] leaps at [target]!"), \
- SPAN_WARNING("The scuttling [src] leaps at [target]!"))
+ target.visible_message(SPAN_WARNING("[src] leaps at [target]!"), \
+ SPAN_WARNING("[src] leaps at [target]!"))
leaping = TRUE
throw_atom(target, 3, SPEED_FAST)
return TRUE
-/obj/item/clothing/mask/facehugger/proc/attach(mob/living/living_mob, silent = FALSE, knockout_mod = 1, hugger_ckey = null)
+/obj/item/clothing/mask/facehugger/proc/attach(mob/living/living_mob, silent = FALSE, knockout_mod = 1, mob/living/carbon/xenomorph/facehugger/hugger)
if(attached || !can_hug(living_mob, hivenumber))
return FALSE
@@ -281,7 +281,22 @@
if(!human.species || !(human.species.flags & IS_SYNTHETIC)) //synthetics aren't paralyzed
human.apply_effect(MIN_IMPREGNATION_TIME * 0.5 * knockout_mod, PARALYZE) //THIS MIGHT NEED TWEAKS
- addtimer(CALLBACK(src, PROC_REF(impregnate), human, hugger_ckey), rand(MIN_IMPREGNATION_TIME, MAX_IMPREGNATION_TIME))
+ var/area/hug_area = get_area(src)
+ var/name = hugger ? "[hugger]" : "\a [src]"
+ if(hug_area)
+ for(var/mob/dead/observer/observer as anything in GLOB.observer_list)
+ to_chat(observer, SPAN_DEADSAY("[human] has been facehugged by [name] at \the [hug_area] [OBSERVER_JMP(observer, human)]"))
+ to_chat(src, SPAN_DEADSAY("[human] has been facehugged by [name] at \the [hug_area]"))
+ else
+ for(var/mob/dead/observer/observer as anything in GLOB.observer_list)
+ to_chat(observer, SPAN_DEADSAY("[human] has been facehugged by [name] [OBSERVER_JMP(observer, human)]"))
+ to_chat(src, SPAN_DEADSAY("[human] has been facehugged by [name]"))
+ if(hug_area)
+ xeno_message(SPAN_XENOMINORWARNING("You sense that [name] has facehugged a host at \the [hug_area]!"), 1, hivenumber)
+ else
+ xeno_message(SPAN_XENOMINORWARNING("You sense that [name] has facehugged a host!"), 1, hivenumber)
+
+ addtimer(CALLBACK(src, PROC_REF(impregnate), human, hugger?.client?.ckey), rand(MIN_IMPREGNATION_TIME, MAX_IMPREGNATION_TIME))
return TRUE
diff --git a/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm b/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm
index be772e3a0a4f..b12ff5d6c3bb 100644
--- a/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm
+++ b/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm
@@ -93,6 +93,9 @@
if (M.fortify || M.burrow)
return XENO_NO_DELAY_ACTION
+ if(HAS_TRAIT(src, TRAIT_ABILITY_BURROWED))
+ return XENO_NO_DELAY_ACTION
+
if(islarva(M)) //Larvas can't eat people
M.visible_message(SPAN_DANGER("[M] nudges its head against \the [src]."), \
SPAN_DANGER("You nudge your head against \the [src]."), null, null, CHAT_TYPE_XENO_FLUFF)
diff --git a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
index 22cb816f865b..09fdb42ad5c3 100644
--- a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
+++ b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
@@ -712,6 +712,10 @@
to_chat(src, SPAN_XENONOTICE("This is not a host."))
return
+ if(current_mob.stat == DEAD)
+ to_chat(src, SPAN_XENONOTICE("This host is dead."))
+ return
+
var/mob/living/carbon/human/host_to_nest = current_mob
var/found_grab = FALSE
diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
index f1d03d6507b1..5586da3765dc 100644
--- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
@@ -243,7 +243,7 @@
var/list/tackle_counter
var/evolving = FALSE // Whether the xeno is in the process of evolving
/// The damage dealt by a xeno whenever they take damage near someone
- var/acid_blood_damage = 20
+ var/acid_blood_damage = 25
var/nocrit = FALSE
var/deselect_timer = 0 // Much like Carbon.last_special is a short tick record to prevent accidental deselects of abilities
var/got_evolution_message = FALSE
@@ -745,6 +745,8 @@
if(SEND_SIGNAL(AM, COMSIG_MOVABLE_XENO_START_PULLING, src) & COMPONENT_ALLOW_PULL)
return do_pull(AM, lunge, no_msg)
+ if(burrow)
+ return
if(!isliving(AM))
return FALSE
var/mob/living/L = AM
@@ -1085,3 +1087,28 @@
if(D)
color_override = D.color
new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(loc, splatter_dir, duration, color_override)
+
+/mob/living/carbon/xenomorph/Collide(atom/movable/movable_atom)
+ . = ..()
+ if(behavior_delegate)
+ behavior_delegate.on_collide(movable_atom)
+
+/mob/living/carbon/xenomorph/proc/scuttle(obj/structure/current_structure)
+ if (mob_size != MOB_SIZE_SMALL)
+ return FALSE
+
+ var/move_dir = get_dir(src, loc)
+ for(var/atom/movable/atom in get_turf(current_structure))
+ if(atom != current_structure && atom.density && atom.BlockedPassDirs(src, move_dir))
+ to_chat(src, SPAN_WARNING("[atom] prevents you from squeezing under [current_structure]!"))
+ return FALSE
+ // Is it an airlock?
+ if(istype(current_structure, /obj/structure/machinery/door/airlock))
+ var/obj/structure/machinery/door/airlock/current_airlock = current_structure
+ if(current_airlock.locked || current_airlock.welded) //Can't pass through airlocks that have been bolted down or welded
+ to_chat(src, SPAN_WARNING("[current_airlock] is locked down tight. You can't squeeze underneath!"))
+ return FALSE
+ visible_message(SPAN_WARNING("[src] scuttles underneath [current_structure]!"), \
+ SPAN_WARNING("You squeeze and scuttle underneath [current_structure]."), max_distance = 5)
+ forceMove(current_structure.loc)
+ return TRUE
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/burrower/burrower_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/burrower/burrower_powers.dm
index 4f104c00f92e..4d3aae33ae9c 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/burrower/burrower_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/burrower/burrower_powers.dm
@@ -42,7 +42,12 @@
density = FALSE
if(caste.fire_immunity == FIRE_IMMUNITY_NONE)
RegisterSignal(src, COMSIG_LIVING_PREIGNITION, PROC_REF(fire_immune))
- RegisterSignal(src, COMSIG_LIVING_FLAMER_CROSSED, PROC_REF(flamer_crossed_immune))
+ RegisterSignal(src, list(
+ COMSIG_LIVING_FLAMER_CROSSED,
+ COMSIG_LIVING_FLAMER_FLAMED,
+ ), PROC_REF(flamer_crossed_immune))
+ ADD_TRAIT(src, TRAIT_ABILITY_BURROWED, TRAIT_SOURCE_ABILITY("Burrow"))
+ playsound(src.loc, 'sound/effects/burrowing_b.ogg', 25)
update_canmove()
update_icons()
addtimer(CALLBACK(src, PROC_REF(do_burrow_cooldown)), (caste ? caste.burrow_cooldown : 5 SECONDS))
@@ -65,21 +70,29 @@
to_chat(src, SPAN_NOTICE("You resurface."))
burrow = FALSE
if(caste.fire_immunity == FIRE_IMMUNITY_NONE)
- UnregisterSignal(src, COMSIG_LIVING_PREIGNITION)
- UnregisterSignal(src, COMSIG_LIVING_FLAMER_CROSSED)
+ UnregisterSignal(src, list(
+ COMSIG_LIVING_PREIGNITION,
+ COMSIG_LIVING_FLAMER_CROSSED,
+ COMSIG_LIVING_FLAMER_FLAMED,
+ ))
+ REMOVE_TRAIT(src, TRAIT_ABILITY_BURROWED, TRAIT_SOURCE_ABILITY("Burrow"))
frozen = FALSE
invisibility = FALSE
anchored = FALSE
density = TRUE
- for(var/mob/living/carbon/human/H in loc)
- H.apply_effect(2, WEAKEN)
+ playsound(loc, 'sound/effects/burrowoff.ogg', 25)
+ for(var/mob/living/carbon/mob in loc)
+ if(!can_not_harm(mob))
+ mob.apply_effect(2, WEAKEN)
+
addtimer(CALLBACK(src, PROC_REF(do_burrow_cooldown)), (caste ? caste.burrow_cooldown : 5 SECONDS))
update_canmove()
update_icons()
/mob/living/carbon/xenomorph/proc/do_burrow_cooldown()
used_burrow = FALSE
- to_chat(src, SPAN_NOTICE("You can now surface."))
+ if(burrow)
+ to_chat(src, SPAN_NOTICE("You can now surface."))
for(var/X in actions)
var/datum/action/act = X
act.update_button_icon()
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 824e0cc5f9fd..d95fbc304397 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm
@@ -479,3 +479,68 @@
// Perform check_state(TRUE) silently:
if(xeno && !xeno.is_mob_incapacitated() || !xeno.buckled || !xeno.evolving && xeno.plasma_stored >= plasma_cost)
return TRUE
+
+/datum/action/xeno_action/onclick/tacmap
+ name = "View Tactical Map"
+ action_icon_state = "toggle_queen_zoom"
+ ability_name = "view tacmap"
+
+ var/mob/living/carbon/xenomorph/queen/tracked_queen
+
+/datum/action/xeno_action/onclick/tacmap/Destroy()
+ tracked_queen = null
+ return ..()
+
+/datum/action/xeno_action/onclick/tacmap/give_to(mob/living/carbon/xenomorph/xeno)
+ . = ..()
+
+ RegisterSignal(xeno.hive, COMSIG_HIVE_NEW_QUEEN, PROC_REF(handle_new_queen))
+
+ if(!xeno.hive.living_xeno_queen)
+ hide_from(xeno)
+ return
+
+ if(!xeno.hive.living_xeno_queen.ovipositor)
+ hide_from(xeno)
+
+ handle_new_queen(new_queen = xeno.hive.living_xeno_queen)
+
+/// handles the addition of a new queen, hiding if appropriate
+/datum/action/xeno_action/onclick/tacmap/proc/handle_new_queen(datum/hive_status/hive, mob/living/carbon/xenomorph/queen/new_queen)
+ SIGNAL_HANDLER
+
+ if(tracked_queen)
+ UnregisterSignal(tracked_queen, list(COMSIG_QUEEN_MOUNT_OVIPOSITOR, COMSIG_QUEEN_DISMOUNT_OVIPOSITOR, COMSIG_PARENT_QDELETING))
+
+ tracked_queen = new_queen
+
+ if(!tracked_queen.ovipositor)
+ hide_from(owner)
+
+ RegisterSignal(tracked_queen, COMSIG_QUEEN_MOUNT_OVIPOSITOR, PROC_REF(handle_mount_ovipositor))
+ RegisterSignal(tracked_queen, COMSIG_QUEEN_DISMOUNT_OVIPOSITOR, PROC_REF(handle_dismount_ovipositor))
+ RegisterSignal(tracked_queen, COMSIG_PARENT_QDELETING, PROC_REF(handle_queen_qdel))
+
+/// deals with the queen mounting the ovipositor, unhiding the action from the user
+/datum/action/xeno_action/onclick/tacmap/proc/handle_mount_ovipositor()
+ SIGNAL_HANDLER
+
+ unhide_from(owner)
+
+/// deals with the queen dismounting the ovipositor, hiding the action from the user
+/datum/action/xeno_action/onclick/tacmap/proc/handle_dismount_ovipositor()
+ SIGNAL_HANDLER
+
+ hide_from(owner)
+
+/// cleans up references to the queen when the queen is being qdel'd, hides the action from the user
+/datum/action/xeno_action/onclick/tacmap/proc/handle_queen_qdel()
+ SIGNAL_HANDLER
+
+ tracked_queen = null
+ hide_from(owner)
+
+/datum/action/xeno_action/onclick/tacmap/use_ability(atom/target)
+ var/mob/living/carbon/xenomorph/xeno = owner
+ xeno.xeno_tacmap()
+ return ..()
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 921b61a23bc9..4a57c0729b91 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
@@ -4,70 +4,79 @@
// Plant weeds
/datum/action/xeno_action/onclick/plant_weeds/use_ability(atom/A)
- var/mob/living/carbon/xenomorph/X = owner
+ var/mob/living/carbon/xenomorph/xeno = owner
if(!action_cooldown_check())
return
- if(!X.check_state())
+ if(!xeno.check_state())
return
- if(X.burrow)
+ if(xeno.burrow)
return
- var/turf/T = X.loc
+ var/turf/turf = xeno.loc
- if(!istype(T))
- to_chat(X, SPAN_WARNING("You can't do that here."))
+ if(!istype(turf))
+ to_chat(xeno, SPAN_WARNING("You can't do that here."))
return
- var/is_weedable = T.is_weedable()
+ if(turf.density)
+ to_chat(xeno, SPAN_WARNING("You can't do that here."))
+ return
+
+ var/is_weedable = turf.is_weedable()
if(!is_weedable)
- to_chat(X, SPAN_WARNING("Bad place for a garden!"))
+ to_chat(xeno, SPAN_WARNING("Bad place for a garden!"))
return
if(!plant_on_semiweedable && is_weedable < FULLY_WEEDABLE)
- to_chat(X, SPAN_WARNING("Bad place for a garden!"))
+ to_chat(xeno, SPAN_WARNING("Bad place for a garden!"))
return
- var/obj/effect/alien/weeds/node/N = locate() in T
- if(N && N.weed_strength >= X.weed_level)
- to_chat(X, SPAN_WARNING("There's a pod here already!"))
+ var/obj/effect/alien/weeds/node/node = locate() in turf
+ if(node && node.weed_strength >= xeno.weed_level)
+ to_chat(xeno, SPAN_WARNING("There's a pod here already!"))
return
- var/obj/effect/alien/resin/trap/resin_trap = locate() in T
+ var/obj/effect/alien/resin/trap/resin_trap = locate() in turf
if(resin_trap)
- to_chat(X, SPAN_WARNING("You can't weed on top of a trap!"))
+ to_chat(xeno, SPAN_WARNING("You can't weed on top of a trap!"))
return
- var/list/to_convert
- if(N)
- to_convert = N.children.Copy()
-
- var/obj/effect/alien/weeds/W = locate(/obj/effect/alien/weeds) in T
- if (W && W.weed_strength >= WEED_LEVEL_HIVE)
- to_chat(X, SPAN_WARNING("These weeds are too strong to plant a node on!"))
+ var/obj/effect/alien/weeds/weed = node || locate() in turf
+ if(weed && weed.weed_strength >= WEED_LEVEL_HIVE)
+ to_chat(xeno, SPAN_WARNING("These weeds are too strong to plant a node on!"))
return
- var/area/AR = get_area(T)
- if(isnull(AR) || !(AR.is_resin_allowed))
- if(AR.flags_area & AREA_UNWEEDABLE)
- to_chat(X, SPAN_XENOWARNING("This area is unsuited to host the hive!"))
+ for(var/obj/structure/struct in turf)
+ if(struct.density && !(struct.flags_atom & ON_BORDER)) // Not sure exactly if we need to test against ON_BORDER though
+ to_chat(xeno, SPAN_WARNING("You can't do that here."))
return
- to_chat(X, SPAN_XENOWARNING("It's too early to spread the hive this far."))
+
+ var/area/area = get_area(turf)
+ if(isnull(area) || !(area.is_resin_allowed))
+ if(area.flags_area & AREA_UNWEEDABLE)
+ to_chat(xeno, SPAN_XENOWARNING("This area is unsuited to host the hive!"))
+ return
+ to_chat(xeno, SPAN_XENOWARNING("It's too early to spread the hive this far."))
return
- if (!check_and_use_plasma_owner())
+ if(!check_and_use_plasma_owner())
return
- X.visible_message(SPAN_XENONOTICE("\The [X] regurgitates a pulsating node and plants it on the ground!"), \
+ var/list/to_convert
+ if(node)
+ to_convert = node.children.Copy()
+
+ xeno.visible_message(SPAN_XENONOTICE("\The [xeno] regurgitates a pulsating node and plants it on the ground!"), \
SPAN_XENONOTICE("You regurgitate a pulsating node and plant it on the ground!"), null, 5)
- var/obj/effect/alien/weeds/node/new_node = new node_type(X.loc, src, X)
+ var/obj/effect/alien/weeds/node/new_node = new node_type(xeno.loc, src, xeno)
if(to_convert)
- for(var/weed in to_convert)
- var/turf/target_turf = get_turf(weed)
+ for(var/cur_weed in to_convert)
+ var/turf/target_turf = get_turf(cur_weed)
if(target_turf && !target_turf.density)
new /obj/effect/alien/weeds(target_turf, new_node)
- qdel(weed)
+ qdel(cur_weed)
- playsound(X.loc, "alien_resin_build", 25)
+ playsound(xeno.loc, "alien_resin_build", 25)
apply_cooldown()
return ..()
@@ -991,7 +1000,10 @@
if(blunt_stab)
stabbing_xeno.visible_message(SPAN_XENOWARNING("\The [stabbing_xeno] swipes its tail into [target]'s [limb ? limb.display_name : "chest"], bashing it!"), SPAN_XENOWARNING("You swipe your tail into [target]'s [limb? limb.display_name : "chest"], bashing it!"))
- playsound(target, "punch", 50, TRUE)
+ if(prob(1))
+ playsound(target, 'sound/effects/comical_bonk.ogg', 50, TRUE)
+ else
+ playsound(target, "punch", 50, TRUE)
// The xeno smashes the target with their tail, moving it to the side and thus their direction as well.
stab_direction = turn(stabbing_xeno.dir, pick(90, -90))
stab_overlay = "slam"
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_abilities.dm
index 6960dd6f369a..4f9c5f298637 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_abilities.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_abilities.dm
@@ -26,15 +26,15 @@
action_type = XENO_ACTION_CLICK
ability_primacy = XENO_PRIMARY_ACTION_2
plasma_cost = 50
- xeno_cooldown = 22 SECONDS
+ xeno_cooldown = 18 SECONDS
// Config values (mutable)
var/empower_range = 3
- var/max_targets = 5
+ var/max_targets = 6
var/main_empower_base_shield = 0
- var/initial_activation_shield = 75
- var/shield_per_human = 80
- var/time_until_timeout = 15 SECONDS
+ var/initial_activation_shield = 50
+ var/shield_per_human = 50
+ var/time_until_timeout = 6 SECONDS
// State
var/activated_once = FALSE
@@ -47,11 +47,11 @@
macro_path = /datum/action/xeno_action/verb/verb_scissorcut
action_type = XENO_ACTION_CLICK
ability_primacy = XENO_PRIMARY_ACTION_3
- xeno_cooldown = 10 SECONDS
+ xeno_cooldown = 6 SECONDS
plasma_cost = 25
// Config
- var/damage = 45
+ var/damage = 40
var/superslow_duration = 3 SECONDS
@@ -118,10 +118,10 @@
action_type = XENO_ACTION_CLICK
ability_primacy = XENO_PRIMARY_ACTION_1
plasma_cost = 0
- xeno_cooldown = 9 SECONDS + 2 SECONDS // Left operand is the actual CD, right operand is the buffer for the shield duration
+ xeno_cooldown = 9 SECONDS + 2.5 SECONDS // Left operand is the actual CD, right operand is the buffer for the shield duration
// Config values
- var/shield_duration = 20 // Shield lasts 2 seconds by default.
+ var/shield_duration = 25 // Shield lasts 2.5 seconds by default.
var/shield_amount = 500 // Shield HP amount
var/shield_shrapnel_amount = 7 // How much shrapnel each shield hit should spawn
var/shard_cost = 150 // Minimum spikes to use this ability
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_powers.dm
index 8c6c0834eebc..9107a9670048 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_powers.dm
@@ -75,7 +75,7 @@
color += num2text(alpha, 2, 16)
xeno.add_filter("empower_rage", 1, list("type" = "outline", "color" = color, "size" = 3))
- addtimer(CALLBACK(src, PROC_REF(weaken_superbuff), xeno, behavior), 3.5 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(weaken_superbuff), xeno, behavior), 5 SECONDS)
/datum/action/xeno_action/onclick/empower/proc/weaken_superbuff(mob/living/carbon/xenomorph/xeno, datum/behavior_delegate/ravager_base/behavior)
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm b/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm
index 60fdda450df3..f4d4628e41f2 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm
@@ -158,6 +158,8 @@
if(xeno.selected_ability == src)
if(xeno.deselect_timer > world.time)
return // We clicked the same ability in a very short time
+ if(xeno.client && xeno.client.prefs && xeno.client.prefs.toggle_prefs & TOGGLE_ABILITY_DEACTIVATION_OFF)
+ return
to_chat(xeno, "You will no longer use [ability_name] with \
[xeno.client && xeno.client.prefs && xeno.client.prefs.toggle_prefs & TOGGLE_MIDDLE_MOUSE_CLICK ? "middle-click" : "shift-click"].")
button.icon_state = "template"
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm b/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm
index 96cd2846249d..092dae00d603 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm
@@ -71,7 +71,7 @@
/datum/action/xeno_action/activable/spray_acid/boiler, //3rd macro
/datum/action/xeno_action/onclick/toggle_long_range/boiler, //4rd macro
/datum/action/xeno_action/onclick/acid_shroud, //4th macro
-
+ /datum/action/xeno_action/onclick/tacmap,
)
/mob/living/carbon/xenomorph/boiler/Initialize(mapload, mob/living/carbon/xenomorph/oldxeno, h_number)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm b/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm
index 8cfc78a38272..ead254344e0a 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Burrower.dm
@@ -66,6 +66,7 @@
/datum/action/xeno_action/onclick/place_trap, //second macro
/datum/action/xeno_action/activable/burrow, //third macro
/datum/action/xeno_action/onclick/tremor, //fourth macro
+ /datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
index fcc9bfcfd3b7..ec1697f30081 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
@@ -69,6 +69,7 @@
/datum/action/xeno_action/activable/throw_hugger, //3rd macro
/datum/action/xeno_action/activable/retrieve_egg, //4th macro
/datum/action/xeno_action/onclick/set_hugger_reserve,
+ /datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
index 9a2e3e89cb21..bf1702598250 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
@@ -59,6 +59,7 @@
/datum/action/xeno_action/activable/pounce/crusher_charge,
/datum/action/xeno_action/onclick/crusher_stomp,
/datum/action/xeno_action/onclick/crusher_shield,
+ /datum/action/xeno_action/onclick/tacmap,
)
claw_type = CLAW_TYPE_VERY_SHARP
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm b/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
index 4360e5d42ca2..8c05fa53660e 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
@@ -47,6 +47,7 @@
/datum/action/xeno_action/activable/headbutt,
/datum/action/xeno_action/onclick/tail_sweep,
/datum/action/xeno_action/activable/fortify,
+ /datum/action/xeno_action/onclick/tacmap,
)
mutation_icon_state = DEFENDER_NORMAL
mutation_type = DEFENDER_NORMAL
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm b/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
index 7748f5978a20..dbdb03bb94ca 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
@@ -63,6 +63,7 @@
/datum/action/xeno_action/onclick/choose_resin, //second macro
/datum/action/xeno_action/activable/secrete_resin, //third macro
/datum/action/xeno_action/activable/transfer_plasma, //fourth macro
+ /datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
/mob/living/carbon/xenomorph/proc/vent_crawl,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
index 1b8ac2ee05dc..d82e43db2aaf 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
@@ -50,6 +50,7 @@
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/onclick/xenohide,
/datum/action/xeno_action/activable/pounce/facehugger,
+ /datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
/mob/living/carbon/xenomorph/proc/vent_crawl,
@@ -66,6 +67,11 @@
PF.flags_can_pass_all = PASS_ALL^PASS_OVER_THROW_ITEM
/mob/living/carbon/xenomorph/facehugger/Life(delta_time)
+ if(!client && !aghosted && away_timer > XENO_FACEHUGGER_LEAVE_TIMER)
+ // Become a npc once again
+ new /obj/item/clothing/mask/facehugger(loc, hivenumber)
+ qdel(src)
+ return
if(stat != DEAD && !lying && !(locate(/obj/effect/alien/weeds) in get_turf(src)))
adjustBruteLoss(1)
return ..()
@@ -143,24 +149,10 @@
/mob/living/carbon/xenomorph/facehugger/proc/handle_hug(mob/living/carbon/human/human)
var/obj/item/clothing/mask/facehugger/hugger = new /obj/item/clothing/mask/facehugger(loc, hivenumber)
- var/did_hug = hugger.attach(human, TRUE, 0.5, client?.ckey)
+ var/did_hug = hugger.attach(human, TRUE, 0.5, src)
if(client)
client.player_data?.adjust_stat(PLAYER_STAT_FACEHUGS, STAT_CATEGORY_XENO, 1)
- var/area/hug_area = get_area(src)
- if(hug_area)
- for(var/mob/dead/observer/observer as anything in GLOB.observer_list)
- to_chat(observer, SPAN_DEADSAY("[human] has been facehugged by [src] at \the [hug_area]" + " [OBSERVER_JMP(observer, human)]"))
- to_chat(src, SPAN_DEADSAY("[human] has been facehugged by [src] at \the [hug_area]"))
- else
- for(var/mob/dead/observer/observer as anything in GLOB.observer_list)
- to_chat(observer, SPAN_DEADSAY("[human] has been facehugged by [src]" + " [OBSERVER_JMP(observer, human)]"))
- to_chat(src, SPAN_DEADSAY("[human] has been facehugged by [src]"))
- timeofdeath = 1 // Ever so slightly deprioritized for larva queue
qdel(src)
- if(hug_area)
- xeno_message(SPAN_XENOMINORWARNING("You sense that [src] has facehugged a host at \the [hug_area]!"), 1, src.hivenumber)
- else
- xeno_message(SPAN_XENOMINORWARNING("You sense that [src] has facehugged a host!"), 1, src.hivenumber)
return did_hug
/mob/living/carbon/xenomorph/facehugger/age_xeno()
@@ -216,22 +208,6 @@
/mob/living/carbon/xenomorph/facehugger/add_xeno_shield(added_amount, shield_source, type = /datum/xeno_shield, duration = -1, decay_amount_per_second = 1, add_shield_on = FALSE, max_shield = 200)
return
-/mob/living/carbon/xenomorph/facehugger/proc/scuttle(obj/structure/current_structure)
- var/move_dir = get_dir(src, loc)
- for(var/atom/movable/atom in get_turf(current_structure))
- if(atom != current_structure && atom.density && atom.BlockedPassDirs(src, move_dir))
- to_chat(src, SPAN_WARNING("\The [atom] prevents you from squeezing under \the [current_structure]!"))
- return
- // Is it an airlock?
- if(istype(current_structure, /obj/structure/machinery/door/airlock))
- var/obj/structure/machinery/door/airlock/current_airlock = current_structure
- if(current_airlock.locked || current_airlock.welded) //Can't pass through airlocks that have been bolted down or welded
- to_chat(src, SPAN_WARNING("\The [current_airlock] is locked down tight. You can't squeeze underneath!"))
- return
- visible_message(SPAN_WARNING("\The [src] scuttles underneath \the [current_structure]!"), \
- SPAN_WARNING("You squeeze and scuttle underneath \the [current_structure]."), null, 5)
- forceMove(current_structure.loc)
-
/mob/living/carbon/xenomorph/facehugger/emote(act, m_type, message, intentional, force_silence)
playsound(loc, "alien_roar_larva", 15)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm b/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
index 1b7c04eb1a77..271fe3182210 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
@@ -58,6 +58,7 @@
/datum/action/xeno_action/onclick/xenohide,
/datum/action/xeno_action/activable/pounce/runner,
/datum/action/xeno_action/onclick/toggle_long_range/runner,
+ /datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
/mob/living/carbon/xenomorph/proc/vent_crawl,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm b/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm
index 9ffd0d9a7690..b0a44b0e3835 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Hivelord.dm
@@ -68,6 +68,7 @@
/datum/action/xeno_action/activable/secrete_resin/hivelord, //third macro
/datum/action/xeno_action/activable/transfer_plasma/hivelord, // to be consistent with drone placement
/datum/action/xeno_action/active_toggle/toggle_speed, //fourth macro
+ /datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm
index 4cf0ff113615..82d80752ec54 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm
@@ -41,6 +41,7 @@
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/onclick/xenohide,
+ /datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
/mob/living/carbon/xenomorph/proc/vent_crawl,
@@ -137,6 +138,9 @@
else
icon_state = "[state_override || state]Larva"
+/mob/living/carbon/xenomorph/larva/alter_ghost(mob/dead/observer/ghost)
+ ghost.icon_state = "[caste.caste_type]"
+
/mob/living/carbon/xenomorph/larva/handle_name()
return
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm b/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm
index 1269f89f9144..8dda6755cca8 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm
@@ -45,6 +45,7 @@
/datum/action/xeno_action/activable/pounce/lurker,
/datum/action/xeno_action/onclick/lurker_invisibility,
/datum/action/xeno_action/onclick/lurker_assassinate,
+ /datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
/mob/living/carbon/xenomorph/proc/vent_crawl,
@@ -150,3 +151,23 @@
. = list()
var/invis_message = (invis_start_time == -1) ? "N/A" : "[(invis_duration-(world.time - invis_start_time))/10] seconds."
. += "Invisibility Time Left: [invis_message]"
+
+/datum/behavior_delegate/lurker_base/on_collide(atom/movable/movable_atom)
+ . = ..()
+
+ if(!ishuman(movable_atom))
+ return
+
+ if(!bound_xeno || !bound_xeno.stealth)
+ return
+
+ var/datum/action/xeno_action/onclick/lurker_invisibility/lurker_invisibility_action = get_xeno_action_by_type(bound_xeno, /datum/action/xeno_action/onclick/lurker_invisibility)
+ if(!lurker_invisibility_action)
+ return
+
+ var/mob/living/carbon/human/bumped_into = movable_atom
+ if(bumped_into.alpha < 100) //ignore invisible scouts and preds
+ return
+
+ to_chat(bound_xeno, SPAN_XENOHIGHDANGER("You bumped into someone and lost your invisibility!"))
+ lurker_invisibility_action.invisibility_off()
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm b/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
index e1bdb18a29e2..344e1e21f302 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
@@ -58,6 +58,7 @@
/datum/action/xeno_action/activable/pounce/base_prae_dash,
/datum/action/xeno_action/activable/prae_acid_ball,
/datum/action/xeno_action/activable/spray_acid/base_prae_spray_acid,
+ /datum/action/xeno_action/onclick/tacmap,
)
icon_xeno = 'icons/mob/xenos/praetorian.dmi'
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
index cf72a5bc419b..c7970e017c4b 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
@@ -60,6 +60,7 @@
/datum/action/xeno_action/onclick/predalien_roar,
/datum/action/xeno_action/onclick/smash,
/datum/action/xeno_action/activable/devastate,
+ /datum/action/xeno_action/onclick/tacmap,
)
mutation_type = "Normal"
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
index 93b6d230b198..ac975835b21f 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
@@ -940,3 +940,7 @@
// Switch icon back and then let normal icon behavior happen
Queen.icon = Queen.queen_standing_icon
+
+/mob/living/carbon/xenomorph/queen/alter_ghost(mob/dead/observer/ghost)
+ ghost.icon = queen_standing_icon
+ return ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm b/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm
index 140e2642685f..35938ff7f808 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm
@@ -56,6 +56,7 @@
/datum/action/xeno_action/activable/pounce/charge,
/datum/action/xeno_action/onclick/empower,
/datum/action/xeno_action/activable/scissor_cut,
+ /datum/action/xeno_action/onclick/tacmap,
)
icon_xeno = 'icons/mob/xenos/ravager.dmi'
@@ -65,8 +66,8 @@
// Mutator delegate for base ravager
/datum/behavior_delegate/ravager_base
var/shield_decay_time = 15 SECONDS // Time in deciseconds before our shield decays
- var/slash_charge_cdr = 4 SECONDS // Amount to reduce charge cooldown by per slash
- var/knockdown_amount = 2
+ var/slash_charge_cdr = 3 SECONDS // Amount to reduce charge cooldown by per slash
+ var/knockdown_amount = 1.3
var/fling_distance = 3
var/empower_targets = 0
var/super_empower_threshold = 3
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
index 45bfd4d7f78d..04dd751bdcb8 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
@@ -56,6 +56,7 @@
/datum/action/xeno_action/activable/pounce/runner,
/datum/action/xeno_action/activable/runner_skillshot,
/datum/action/xeno_action/onclick/toggle_long_range/runner,
+ /datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
/mob/living/carbon/xenomorph/proc/vent_crawl,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm b/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm
index 3e67ae2d18f9..39426b1a9ef7 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Sentinel.dm
@@ -48,6 +48,7 @@
/datum/action/xeno_action/activable/slowing_spit, //first macro
/datum/action/xeno_action/activable/scattered_spit, //second macro
/datum/action/xeno_action/onclick/paralyzing_slash, //third macro
+ /datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
/mob/living/carbon/xenomorph/proc/vent_crawl,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm b/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm
index 31c8720e9a19..23728b00a642 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm
@@ -51,6 +51,7 @@
/datum/action/xeno_action/activable/xeno_spit,
/datum/action/xeno_action/onclick/charge_spit,
/datum/action/xeno_action/activable/spray_acid/spitter,
+ /datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
/mob/living/carbon/xenomorph/proc/vent_crawl,
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm b/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm
index d815bc18f9d5..7c8edace81e4 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm
@@ -52,6 +52,7 @@
/datum/action/xeno_action/activable/warrior_punch,
/datum/action/xeno_action/activable/lunge,
/datum/action/xeno_action/activable/fling,
+ /datum/action/xeno_action/onclick/tacmap,
)
mutation_type = WARRIOR_NORMAL
diff --git a/code/modules/mob/living/carbon/xenomorph/damage_procs.dm b/code/modules/mob/living/carbon/xenomorph/damage_procs.dm
index f3443535a5f2..c26e38202200 100644
--- a/code/modules/mob/living/carbon/xenomorph/damage_procs.dm
+++ b/code/modules/mob/living/carbon/xenomorph/damage_procs.dm
@@ -272,7 +272,7 @@
SPAN_DANGER("You are splattered with sizzling blood! IT BURNS!"))
if(prob(60) && !victim.stat && victim.pain.feels_pain)
INVOKE_ASYNC(victim, TYPE_PROC_REF(/mob, emote), "scream") //Topkek
- victim.take_limb_damage(0, dmg["damage"]) //Sizzledam! This automagically burns a random existing body part.
+ victim.apply_armoured_damage(dmg["damage"], ARMOR_BIO, BURN) //Sizzledam! This automagically burns a random existing body part.
victim.add_blood(get_blood_color(), BLOOD_BODY)
acid_splash_last = world.time
handle_blood_splatter(get_dir(src, victim), 1 SECONDS)
diff --git a/code/modules/mob/living/carbon/xenomorph/death.dm b/code/modules/mob/living/carbon/xenomorph/death.dm
index fe4b4cca2fb1..12c9b3e37c9b 100644
--- a/code/modules/mob/living/carbon/xenomorph/death.dm
+++ b/code/modules/mob/living/carbon/xenomorph/death.dm
@@ -38,7 +38,7 @@
if(GLOB.hive_datum[hivenumber].stored_larva)
GLOB.hive_datum[hivenumber].stored_larva = round(GLOB.hive_datum[hivenumber].stored_larva * 0.5) //Lose half on dead queen
- var/list/players_with_xeno_pref = get_alien_candidates()
+ var/list/players_with_xeno_pref = get_alien_candidates(GLOB.hive_datum[hivenumber])
if(players_with_xeno_pref && istype(GLOB.hive_datum[hivenumber].hive_location, /obj/effect/alien/resin/special/pylon/core))
var/turf/larva_spawn = get_turf(GLOB.hive_datum[hivenumber].hive_location)
var/count = 0
@@ -50,7 +50,7 @@
new_xeno.generate_name()
if(!SSticker.mode.transfer_xeno(xeno_candidate, new_xeno))
qdel(new_xeno)
- return
+ break
new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly burrows out of the ground!"),
SPAN_XENODANGER("You burrow out of the ground after feeling an immense tremor through the hive, which quickly fades into complete silence..."))
@@ -105,6 +105,11 @@
GLOB.hive_datum[hivenumber].stored_larva++
GLOB.hive_datum[hivenumber].hive_ui.update_burrowed_larva()
+ if(hardcore)
+ QDEL_IN(src, 3 SECONDS)
+ //else if(!gibbed) // At the moment we only support humans
+ //AddComponent(/datum/component/weed_food)
+
if(hive)
hive.remove_xeno(src)
// Finding the last xeno for anti-delay.
@@ -125,9 +130,6 @@
to_chat(X, SPAN_XENOANNOUNCE("Your carapace rattles with dread. You are all that remains of the hive!"))
announce_dchat("There is only one Xenomorph left: [X.name].", X)
- if(hardcore)
- QDEL_IN(src, 3 SECONDS)
-
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_XENO_DEATH, src, gibbed)
/mob/living/carbon/xenomorph/gib(datum/cause_data/cause = create_cause_data("gibbing", src))
diff --git a/code/modules/mob/living/carbon/xenomorph/mutators/behavior_delegate.dm b/code/modules/mob/living/carbon/xenomorph/mutators/behavior_delegate.dm
index 1cb563461138..53ca8c3a74da 100644
--- a/code/modules/mob/living/carbon/xenomorph/mutators/behavior_delegate.dm
+++ b/code/modules/mob/living/carbon/xenomorph/mutators/behavior_delegate.dm
@@ -104,3 +104,7 @@
/// Used to override an intent for some abilities that must force harm on next attack_alien()
/datum/behavior_delegate/proc/override_intent(mob/living/carbon/target_carbon)
return bound_xeno.a_intent
+
+/// Used to do something when a xeno collides with a movable atom
+/datum/behavior_delegate/proc/on_collide(atom/movable/movable_atom)
+ return
diff --git a/code/modules/mob/living/carbon/xenomorph/mutators/strains/drone/gardener.dm b/code/modules/mob/living/carbon/xenomorph/mutators/strains/drone/gardener.dm
index 0e6b7e4657d3..303e1c94692d 100644
--- a/code/modules/mob/living/carbon/xenomorph/mutators/strains/drone/gardener.dm
+++ b/code/modules/mob/living/carbon/xenomorph/mutators/strains/drone/gardener.dm
@@ -352,8 +352,8 @@
/obj/effect/alien/weeds/node/gardener
spread_on_semiweedable = TRUE
- block_structures = BLOCK_SPECIAL_STRUCTURES
fruit_growth_multiplier = 0.8
+ weed_strength = WEED_LEVEL_HARDY
/datum/action/xeno_action/verb/verb_plant_gardening_weeds()
set category = "Alien"
diff --git a/code/modules/mob/living/carbon/xenomorph/mutators/strains/ravager/hedgehog.dm b/code/modules/mob/living/carbon/xenomorph/mutators/strains/ravager/hedgehog.dm
index 7878bd4113f1..913883549fba 100644
--- a/code/modules/mob/living/carbon/xenomorph/mutators/strains/ravager/hedgehog.dm
+++ b/code/modules/mob/living/carbon/xenomorph/mutators/strains/ravager/hedgehog.dm
@@ -1,6 +1,6 @@
/datum/xeno_mutator/hedgehog
name = "STRAIN: Ravager - Hedgehog"
- description = "You lose your empower, charge, and scissor cut and a decent amount of your speed for a bit more explosive resistance, immunity to small explosions, and you gain several new abilities that allow you to become a spiky tank. You build up shards internally over time and also when taking damage that increase your armor's resilience. You can use these shards to power three new abilities: Spike Shield, which gives you a temporary shield that spits bone shards around you when damaged, Fire Spikes, which launches spikes at your target that slows them and does extra damage if they move, and finally, Spike Shed, which launches spikes all around yourself and gives you a temporary speed boost as an escape plan at the cost of all your stored shards and being unable to gain shards for thirty seconds."
+ description = "You lose your empower, charge, scissor cut and some slash damage, for a bit more explosive resistance, immunity to small explosions, and you gain several new abilities that allow you to become a spiky tank. You build up shards internally over time and also when taking damage that increase your armor's resilience. You can use these shards to power three new abilities: Spike Shield, which gives you a temporary shield that spits bone shards around you when damaged, Fire Spikes, which launches spikes at your target that slows them and does extra damage if they move, and finally, Spike Shed, which launches spikes all around yourself and gives you a temporary speed boost as an escape plan at the cost of all your stored shards and being unable to gain shards for thirty seconds."
flavor_description = "They will be of iron will and steely muscle. In great armor shall they be clad, and with the mightiest spikes will they be armed."
cost = MUTATOR_COST_EXPENSIVE
individual_only = TRUE
@@ -29,7 +29,7 @@
ravager.plasma_max = 0
ravager.small_explosives_stun = FALSE
ravager.explosivearmor_modifier += XENO_EXPOSIVEARMOR_MOD_SMALL
- ravager.speed_modifier += XENO_SPEED_SLOWMOD_TIER_8
+ ravager.damage_modifier -= XENO_DAMAGE_MOD_SMALL
apply_behavior_holder(ravager)
@@ -43,11 +43,12 @@
// Shard config
var/max_shards = 300
- var/shard_gain_onlife = 10
- var/shards_per_projectile = 20
+ var/shard_gain_onlife = 5
+ var/shards_per_projectile = 10
+ var/shards_per_slash = 15
var/armor_buff_per_fifty_shards = 2.50
- var/shard_lock_duration = 300
- var/shard_lock_speed_mod = 0.85
+ var/shard_lock_duration = 150
+ var/shard_lock_speed_mod = 0.45
// Shard state
var/shards = 0
@@ -126,3 +127,8 @@
if (!shards_locked)
shards = min(max_shards, shards + shards_per_projectile)
return
+
+/datum/behavior_delegate/ravager_hedgehog/melee_attack_additional_effects_self()
+ if (!shards_locked)
+ shards = min(max_shards, shards + shards_per_slash)
+ return
diff --git a/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm b/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm
index a2699a4f0f40..7a2196a3c209 100644
--- a/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm
+++ b/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm
@@ -70,19 +70,27 @@
. += "FOR THE HIVE!: in [caboom_left] seconds"
/datum/behavior_delegate/runner_acider/melee_attack_additional_effects_target(mob/living/carbon/target_mob)
- if (ishuman(target_mob))
+ if(ishuman(target_mob)) //Will acid be applied to the mob
var/mob/living/carbon/human/target_human = target_mob
- if (target_human.stat == DEAD)
+ if(target_human.buckled && istype(target_human.buckled, /obj/structure/bed/nest))
return
- for(var/datum/effects/acid/AA in target_mob.effects_list)
- qdel(AA)
+ if(target_human.stat == DEAD)
+ return
+
+ for(var/datum/effects/acid/acid_effect in target_mob.effects_list)
+ qdel(acid_effect)
break
- if(isxeno_human(target_mob))
+
+ new /datum/effects/acid(target_mob, bound_xeno, initial(bound_xeno.caste_type))
+ if(isxeno_human(target_mob)) //Will the runner get acid stacks
+ var/obj/item/alien_embryo/embryo = locate(/obj/item/alien_embryo) in target_mob.contents
+ if(embryo?.stage >= 4) //very late stage hugged in case the runner unnests them
+ return
+
if(target_mob.lying)
modify_acid(acid_slash_regen_lying)
- else
- modify_acid(acid_slash_regen_standing)
- new /datum/effects/acid(target_mob, bound_xeno, initial(bound_xeno.caste_type))
+ return
+ modify_acid(acid_slash_regen_standing)
/datum/behavior_delegate/runner_acider/on_life()
modify_acid(acid_passive_regen)
diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
index 7506788c2576..8b25cf07d14b 100644
--- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
@@ -474,6 +474,7 @@
SStracking.set_leader("hive_[hivenumber]", queen)
SShive_status.wait = 2 SECONDS
+ SEND_SIGNAL(src, COMSIG_HIVE_NEW_QUEEN, queen)
living_xeno_queen = queen
recalculate_hive()
diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm b/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm
index 20c170b72e4e..6b37145ad7a1 100644
--- a/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm
@@ -111,6 +111,21 @@
else
to_chat(src, SPAN_NOTICE("The selected xeno ability will now be activated with shift clicking."))
+/mob/living/carbon/xenomorph/verb/ability_deactivation_toggle()
+ set name = "Toggle Ability Deactivation"
+ set desc = "Toggles whether you can deactivate your currently active ability when re-selecting it."
+ set category = "Alien"
+
+ if (!client || !client.prefs)
+ return
+
+ client.prefs.toggle_prefs ^= TOGGLE_ABILITY_DEACTIVATION_OFF
+ client.prefs.save_preferences()
+ if (client.prefs.toggle_prefs & TOGGLE_ABILITY_DEACTIVATION_OFF)
+ to_chat(src, SPAN_NOTICE("Your current ability can no longer be toggled off when re-selected."))
+ else
+ to_chat(src, SPAN_NOTICE("Your current ability can be toggled off when re-selected."))
+
/mob/living/carbon/xenomorph/verb/directional_attack_toggle()
set name = "Toggle Directional Attacks"
set desc = "Toggles the use of directional assist attacks."
diff --git a/code/modules/mob/living/living_verbs.dm b/code/modules/mob/living/living_verbs.dm
index 9b003562825d..fe95f14182ea 100644
--- a/code/modules/mob/living/living_verbs.dm
+++ b/code/modules/mob/living/living_verbs.dm
@@ -11,6 +11,12 @@
to_chat(src, SPAN_WARNING("You can't resist in your current state."))
return
+ if(isxeno(src))
+ var/mob/living/carbon/xenomorph/xeno = src
+ if(xeno.burrow)
+ to_chat(src, SPAN_WARNING("You can't resist in your current state."))
+ return
+
resisting = TRUE
next_move = world.time + 20
diff --git a/code/modules/organs/limb_objects.dm b/code/modules/organs/limb_objects.dm
index ecfd4ca1960f..734f303c7f5b 100644
--- a/code/modules/organs/limb_objects.dm
+++ b/code/modules/organs/limb_objects.dm
@@ -136,6 +136,7 @@
H.regenerate_icons()
if(braindeath_on_decap)
+ brainmob.timeofdeath = world.time
brainmob.set_stat(DEAD)
brainmob.death(cause)
diff --git a/code/modules/paperwork/carbonpaper.dm b/code/modules/paperwork/carbonpaper.dm
index 813283147c45..4ddefff61eb8 100644
--- a/code/modules/paperwork/carbonpaper.dm
+++ b/code/modules/paperwork/carbonpaper.dm
@@ -30,7 +30,7 @@
set category = "Object"
set src in usr
- if (copied == 0)
+ if (!copied && !iscopy)
var/obj/item/paper/carbon/c = src
var/copycontents = html_decode(c.info)
var/obj/item/paper/carbon/copy = new /obj/item/paper/carbon (usr.loc)
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 60aa2f2c16e7..7665e0a5d7ab 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -81,7 +81,7 @@
if(photo_list)
for(var/photo in photo_list)
user << browse_rsc(photo_list[photo], photo)
- show_browser(user, "[stars(info)][stamps]", name, name)
+ show_browser(user, "[stars(info)][stamps]", name, name, "size=650x700")
onclose(user, name)
else
read_paper(user)
@@ -94,7 +94,7 @@
if(photo_list)
for(var/photo in photo_list)
user << browse_rsc(photo_list[photo], photo)
- show_browser(user, "[info][stamps]", name, name)
+ show_browser(user, "[info][stamps]", name, name, "size=650x700")
onclose(user, name)
/obj/item/paper/verb/rename()
@@ -213,6 +213,8 @@
/obj/item/paper/proc/parsepencode(t, obj/item/tool/pen/P, mob/user as mob, iscrayon = 0)
+ var/datum/asset/asset = get_asset_datum(/datum/asset/simple/paper)
+
t = replacetext(t, "\[center\]", "
")
t = replacetext(t, "\[/center\]", "
")
t = replacetext(t, "\[br\]", " ")
@@ -251,9 +253,9 @@
t = replacetext(t, "\[/grid\]", "")
t = replacetext(t, "\[row\]", "
")
t = replacetext(t, "\[cell\]", "
")
- t = replacetext(t, "\[logo\]", "")
- t = replacetext(t, "\[wy\]", "")
- t = replacetext(t, "\[uscm\]", "")
+ t = replacetext(t, "\[logo\]", "")
+ t = replacetext(t, "\[wy\]", "")
+ t = replacetext(t, "\[uscm\]", "")
t = "[t]"
else // If it is a crayon, and he still tries to use these, make them empty!
@@ -619,11 +621,21 @@
/obj/item/paper/wy
icon_state = "paper_wy"
- info = "
Official Weyland-Yutani Document Automated A-XRF Report
Analysis of [name]
"
if(sample_number)
report.info += "Results for sample: #[sample_number] \n"
report.generate(src, admin_spawned)
diff --git a/code/modules/shuttle/computers/dropship_computer.dm b/code/modules/shuttle/computers/dropship_computer.dm
index ea4a7fdbc79d..50449b32fcb9 100644
--- a/code/modules/shuttle/computers/dropship_computer.dm
+++ b/code/modules/shuttle/computers/dropship_computer.dm
@@ -1,6 +1,6 @@
/obj/structure/machinery/computer/shuttle/dropship/flight
name = "dropship navigation computer"
- desc = "flight computer for dropship"
+ desc = "A flight computer that can be used for autopilot or long-range flights."
icon = 'icons/obj/structures/machinery/shuttle-parts.dmi'
icon_state = "console"
req_one_access = list(ACCESS_MARINE_LEADER, ACCESS_MARINE_DROPSHIP)
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index 7e181ed470cb..983fffeb2634 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -635,7 +635,7 @@
var/mob/dead/observer/obs = mob.ghostize(FALSE)
if(obs)
obs.timeofdeath = world.time
- obs.client?.larva_queue_time = world.time
+ obs.client?.player_details.larva_queue_time = max(obs.client.player_details.larva_queue_time, world.time)
mob.moveToNullspace()
// Now that mobs are stowed, delete the shuttle
diff --git a/code/modules/shuttle/shuttles/dropship.dm b/code/modules/shuttle/shuttles/dropship.dm
index 12f33d779661..303d4c0494ed 100644
--- a/code/modules/shuttle/shuttles/dropship.dm
+++ b/code/modules/shuttle/shuttles/dropship.dm
@@ -278,6 +278,8 @@
// shake_camera(affected_mob, 10, 1)
affected_mob.apply_effect(3, WEAKEN)
+ SEND_GLOBAL_SIGNAL(COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING)
+
/datum/map_template/shuttle/alamo
name = "Alamo"
shuttle_id = DROPSHIP_ALAMO
diff --git a/code/modules/shuttle/shuttles/escape_shuttle.dm b/code/modules/shuttle/shuttles/escape_shuttle.dm
index a4bce9910bb7..6c580f9f163b 100644
--- a/code/modules/shuttle/shuttles/escape_shuttle.dm
+++ b/code/modules/shuttle/shuttles/escape_shuttle.dm
@@ -1,5 +1,3 @@
-#define CRASH_LAND_PROBABILITY 50
-
/obj/docking_port/mobile/escape_shuttle
name = "Escape Pod"
id = ESCAPE_SHUTTLE
@@ -10,6 +8,8 @@
rechargeTime = SHUTTLE_RECHARGE
ignitionTime = 8 SECONDS
ignition_sound = 'sound/effects/escape_pod_warmup.ogg'
+ /// The % chance of the escape pod crashing into the groundmap
+ var/crash_land_chance = 33
var/datum/door_controller/single/door_handler = new()
var/launched = FALSE
@@ -76,7 +76,7 @@
return
destination = null
- if(prob(CRASH_LAND_PROBABILITY))
+ if(prob(crash_land_chance))
create_crash_point()
set_mode(SHUTTLE_IGNITING)
@@ -176,6 +176,12 @@
. = ..()
playsound(src,'sound/effects/escape_pod_launch.ogg', 50, 1)
+/obj/docking_port/mobile/escape_shuttle/proc/force_crash()
+ create_crash_point()
+ set_mode(SHUTTLE_IGNITING)
+ on_ignition()
+ setTimer(ignitionTime)
+
/obj/docking_port/mobile/escape_shuttle/e
id = ESCAPE_SHUTTLE_EAST
width = 4
@@ -270,6 +276,3 @@
/datum/map_template/shuttle/escape_pod_e_cl
name = "Escape Pod E CL"
shuttle_id = ESCAPE_SHUTTLE_EAST_CL
-
-
-#undef CRASH_LAND_PROBABILITY
diff --git a/code/modules/tgs/core/_definitions.dm b/code/modules/tgs/core/_definitions.dm
index ebf6d17c2a07..fd98034eb716 100644
--- a/code/modules/tgs/core/_definitions.dm
+++ b/code/modules/tgs/core/_definitions.dm
@@ -1,2 +1,10 @@
+#if DM_VERSION < 510
+#error The TGS DMAPI does not support BYOND versions < 510!
+#endif
+
#define TGS_UNIMPLEMENTED "___unimplemented"
#define TGS_VERSION_PARAMETER "server_service_version"
+
+#ifndef TGS_DEBUG_LOG
+#define TGS_DEBUG_LOG(message)
+#endif
diff --git a/code/modules/tgs/v3210/commands.dm b/code/modules/tgs/v3210/commands.dm
index d9bd287465b9..e65c816320dc 100644
--- a/code/modules/tgs/v3210/commands.dm
+++ b/code/modules/tgs/v3210/commands.dm
@@ -47,7 +47,7 @@
user.friendly_name = sender
// Discord hack, fix the mention if it's only numbers (fuck you IRC trolls)
- var/regex/discord_id_regex = regex(@"^[0-9]+$")
+ var/regex/discord_id_regex = regex("^\[0-9\]+$")
if(findtext(sender, discord_id_regex))
sender = "<@[sender]>"
@@ -55,4 +55,4 @@
var/datum/tgs_message_content/result = stc.Run(user, params)
result = UpgradeDeprecatedCommandResponse(result, command)
- return result?.text || TRUE
+ return result ? result.text : TRUE
diff --git a/code/modules/tgs/v4/commands.dm b/code/modules/tgs/v4/commands.dm
index d6d3d718d471..25dd6740e3af 100644
--- a/code/modules/tgs/v4/commands.dm
+++ b/code/modules/tgs/v4/commands.dm
@@ -40,5 +40,5 @@
var/datum/tgs_message_content/result = sc.Run(u, params)
result = UpgradeDeprecatedCommandResponse(result, command)
- return result?.text
+ return result ? result.text : TRUE
return "Unknown command: [command]!"
diff --git a/code/modules/tgs/v5/_defines.dm b/code/modules/tgs/v5/_defines.dm
index c7213cc24699..f973338daa03 100644
--- a/code/modules/tgs/v5/_defines.dm
+++ b/code/modules/tgs/v5/_defines.dm
@@ -5,8 +5,8 @@
#define DMAPI5_TOPIC_DATA "tgs_data"
#define DMAPI5_BRIDGE_REQUEST_LIMIT 8198
-#define DMAPI5_TOPIC_REQUEST_LIMIT 65529
-#define DMAPI5_TOPIC_RESPONSE_LIMIT 65528
+#define DMAPI5_TOPIC_REQUEST_LIMIT 65528
+#define DMAPI5_TOPIC_RESPONSE_LIMIT 65529
#define DMAPI5_BRIDGE_COMMAND_PORT_UPDATE 0
#define DMAPI5_BRIDGE_COMMAND_STARTUP 1
diff --git a/code/modules/tgs/v5/api.dm b/code/modules/tgs/v5/api.dm
index 926ea10a8f27..34cc43f8762f 100644
--- a/code/modules/tgs/v5/api.dm
+++ b/code/modules/tgs/v5/api.dm
@@ -22,12 +22,17 @@
var/detached = FALSE
+/datum/tgs_api/v5/New()
+ . = ..()
+ TGS_DEBUG_LOG("V5 API created")
+
/datum/tgs_api/v5/ApiVersion()
return new /datum/tgs_version(
#include "__interop_version.dm"
)
/datum/tgs_api/v5/OnWorldNew(minimum_required_security_level)
+ TGS_DEBUG_LOG("OnWorldNew()")
server_port = world.params[DMAPI5_PARAM_SERVER_PORT]
access_identifier = world.params[DMAPI5_PARAM_ACCESS_IDENTIFIER]
@@ -96,17 +101,28 @@
return TRUE
/datum/tgs_api/v5/proc/RequireInitialBridgeResponse()
+ TGS_DEBUG_LOG("RequireInitialBridgeResponse()")
+ var/logged = FALSE
while(!version)
+ if(!logged)
+ TGS_DEBUG_LOG("RequireInitialBridgeResponse: Starting sleep")
+ logged = TRUE
+
sleep(1)
+ TGS_DEBUG_LOG("RequireInitialBridgeResponse: Passed")
+
/datum/tgs_api/v5/OnInitializationComplete()
Bridge(DMAPI5_BRIDGE_COMMAND_PRIME)
/datum/tgs_api/v5/OnTopic(T)
+ TGS_DEBUG_LOG("OnTopic()")
RequireInitialBridgeResponse()
+ TGS_DEBUG_LOG("OnTopic passed bridge request gate")
var/list/params = params2list(T)
var/json = params[DMAPI5_TOPIC_DATA]
if(!json)
+ TGS_DEBUG_LOG("No \"[DMAPI5_TOPIC_DATA]\" entry found, ignoring...")
return FALSE // continue to /world/Topic
if(!initialized)
@@ -156,7 +172,7 @@
TGS_WARNING_LOG("Received legacy string when a [/datum/tgs_message_content] was expected. Please audit all calls to TgsChatBroadcast, TgsChatTargetedBroadcast, and TgsChatPrivateMessage to ensure they use the new /datum.")
return new /datum/tgs_message_content(message)
-/datum/tgs_api/v5/ChatBroadcast(datum/tgs_message_content/message, list/channels)
+/datum/tgs_api/v5/ChatBroadcast(datum/tgs_message_content/message2, list/channels)
if(!length(channels))
channels = ChatChannelInfo()
@@ -165,45 +181,45 @@
var/datum/tgs_chat_channel/channel = I
ids += channel.id
- message = UpgradeDeprecatedChatMessage(message)
+ message2 = UpgradeDeprecatedChatMessage(message2)
if (!length(channels))
return
- message = message._interop_serialize()
- message[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = ids
+ var/list/data = message2._interop_serialize()
+ data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = ids
if(intercepted_message_queue)
- intercepted_message_queue += list(message)
+ intercepted_message_queue += list(data)
else
- Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message))
+ Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
-/datum/tgs_api/v5/ChatTargetedBroadcast(datum/tgs_message_content/message, admin_only)
+/datum/tgs_api/v5/ChatTargetedBroadcast(datum/tgs_message_content/message2, admin_only)
var/list/channels = list()
for(var/I in ChatChannelInfo())
var/datum/tgs_chat_channel/channel = I
if (!channel.is_private_channel && ((channel.is_admin_channel && admin_only) || (!channel.is_admin_channel && !admin_only)))
channels += channel.id
- message = UpgradeDeprecatedChatMessage(message)
+ message2 = UpgradeDeprecatedChatMessage(message2)
if (!length(channels))
return
- message = message._interop_serialize()
- message[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channels
+ var/list/data = message2._interop_serialize()
+ data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channels
if(intercepted_message_queue)
- intercepted_message_queue += list(message)
+ intercepted_message_queue += list(data)
else
- Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message))
+ Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
-/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message, datum/tgs_chat_user/user)
- message = UpgradeDeprecatedChatMessage(message)
- message = message._interop_serialize()
- message[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = list(user.channel.id)
+/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message2, datum/tgs_chat_user/user)
+ message2 = UpgradeDeprecatedChatMessage(message2)
+ var/list/data = message2._interop_serialize()
+ data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = list(user.channel.id)
if(intercepted_message_queue)
- intercepted_message_queue += list(message)
+ intercepted_message_queue += list(data)
else
- Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message))
+ Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
/datum/tgs_api/v5/ChatChannelInfo()
RequireInitialBridgeResponse()
@@ -211,6 +227,7 @@
return chat_channels.Copy()
/datum/tgs_api/v5/proc/DecodeChannels(chat_update_json)
+ TGS_DEBUG_LOG("DecodeChannels()")
var/list/chat_channels_json = chat_update_json[DMAPI5_CHAT_UPDATE_CHANNELS]
if(istype(chat_channels_json))
chat_channels.Cut()
diff --git a/code/modules/tgs/v5/commands.dm b/code/modules/tgs/v5/commands.dm
index a832c81f172d..9557f8a08ed5 100644
--- a/code/modules/tgs/v5/commands.dm
+++ b/code/modules/tgs/v5/commands.dm
@@ -35,10 +35,10 @@
if(sc)
var/datum/tgs_message_content/response = sc.Run(u, params)
response = UpgradeDeprecatedCommandResponse(response, command)
-
+
var/list/topic_response = TopicResponse()
- topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE] = response?.text
- topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE] = response?._interop_serialize()
+ topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE] = response ? response.text : null
+ topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE] = response ? response._interop_serialize() : null
return topic_response
return TopicResponse("Unknown custom chat command: [command]!")
diff --git a/code/modules/tgs/v5/serializers.dm b/code/modules/tgs/v5/serializers.dm
index 7f9bc731b792..3a32848ad512 100644
--- a/code/modules/tgs/v5/serializers.dm
+++ b/code/modules/tgs/v5/serializers.dm
@@ -1,12 +1,12 @@
/datum/tgs_message_content/proc/_interop_serialize()
- return list("text" = text, "embed" = embed?._interop_serialize())
+ return list("text" = text, "embed" = embed ? embed._interop_serialize() : null)
/datum/tgs_chat_embed/proc/_interop_serialize()
CRASH("Base /proc/interop_serialize called on [type]!")
/datum/tgs_chat_embed/structure/_interop_serialize()
var/list/serialized_fields
- if(islist(fields))
+ if(istype(fields, /list))
serialized_fields = list()
for(var/datum/tgs_chat_embed/field/field as anything in fields)
serialized_fields += list(field._interop_serialize())
@@ -16,12 +16,12 @@
"url" = url,
"timestamp" = timestamp,
"colour" = colour,
- "image" = image?._interop_serialize(),
- "thumbnail" = thumbnail?._interop_serialize(),
- "video" = video?._interop_serialize(),
- "footer" = footer?._interop_serialize(),
- "provider" = provider?._interop_serialize(),
- "author" = author?._interop_serialize(),
+ "image" = src.image ? src.image._interop_serialize() : null,
+ "thumbnail" = thumbnail ? thumbnail._interop_serialize() : null,
+ "video" = video ? video._interop_serialize() : null,
+ "footer" = footer ? footer._interop_serialize() : null,
+ "provider" = provider ? provider._interop_serialize() : null,
+ "author" = author ? author._interop_serialize() : null,
"fields" = serialized_fields
)
@@ -43,7 +43,7 @@
. = ..()
.["iconUrl"] = icon_url
.["proxyIconUrl"] = proxy_icon_url
-
+
/datum/tgs_chat_embed/footer/_interop_serialize()
return list(
"text" = text,
diff --git a/code/modules/tgs/v5/topic.dm b/code/modules/tgs/v5/topic.dm
index 56c1824fd97d..d7d471213813 100644
--- a/code/modules/tgs/v5/topic.dm
+++ b/code/modules/tgs/v5/topic.dm
@@ -5,6 +5,7 @@
return response
/datum/tgs_api/v5/proc/ProcessTopicJson(json, check_access_identifier)
+ TGS_DEBUG_LOG("ProcessTopicJson(..., [check_access_identifier])")
var/list/result = ProcessRawTopic(json, check_access_identifier)
if(!result)
result = TopicResponse("Runtime error!")
@@ -25,16 +26,20 @@
return response_json
/datum/tgs_api/v5/proc/ProcessRawTopic(json, check_access_identifier)
+ TGS_DEBUG_LOG("ProcessRawTopic(..., [check_access_identifier])")
var/list/topic_parameters = json_decode(json)
if(!topic_parameters)
+ TGS_DEBUG_LOG("ProcessRawTopic: json_decode failed")
return TopicResponse("Invalid topic parameters json: [json]!");
var/their_sCK = topic_parameters[DMAPI5_PARAMETER_ACCESS_IDENTIFIER]
if(check_access_identifier && their_sCK != access_identifier)
- return TopicResponse("Failed to decode [DMAPI5_PARAMETER_ACCESS_IDENTIFIER]!")
+ TGS_DEBUG_LOG("ProcessRawTopic: access identifier check failed")
+ return TopicResponse("Failed to decode [DMAPI5_PARAMETER_ACCESS_IDENTIFIER] or it does not match!")
var/command = topic_parameters[DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE]
if(!isnum(command))
+ TGS_DEBUG_LOG("ProcessRawTopic: command type check failed")
return TopicResponse("Failed to decode [DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE]!")
return ProcessTopicCommand(command, topic_parameters)
@@ -43,6 +48,7 @@
return "response[payload_id]"
/datum/tgs_api/v5/proc/ProcessTopicCommand(command, list/topic_parameters)
+ TGS_DEBUG_LOG("ProcessTopicCommand([command], ...)")
switch(command)
if(DMAPI5_TOPIC_COMMAND_CHAT_COMMAND)
@@ -55,7 +61,6 @@
return result
if(DMAPI5_TOPIC_COMMAND_EVENT_NOTIFICATION)
- intercepted_message_queue = list()
var/list/event_notification = topic_parameters[DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION]
if(!istype(event_notification))
return TopicResponse("Invalid [DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION]!")
@@ -66,23 +71,25 @@
var/list/event_parameters = event_notification[DMAPI5_EVENT_NOTIFICATION_PARAMETERS]
if(event_parameters && !istype(event_parameters))
- return TopicResponse("Invalid or missing [DMAPI5_EVENT_NOTIFICATION_PARAMETERS]!")
+ . = TopicResponse("Invalid or missing [DMAPI5_EVENT_NOTIFICATION_PARAMETERS]!")
+ else
+ var/list/response = TopicResponse()
+ . = response
+ if(event_handler != null)
+ var/list/event_call = list(event_type)
+ if(event_parameters)
+ event_call += event_parameters
+
+ intercepted_message_queue = list()
+ event_handler.HandleEvent(arglist(event_call))
+ response[DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES] = intercepted_message_queue
+ intercepted_message_queue = null
- var/list/event_call = list(event_type)
if (event_type == TGS_EVENT_WATCHDOG_DETACH)
detached = TRUE
chat_channels.Cut() // https://github.com/tgstation/tgstation-server/issues/1490
- if(event_parameters)
- event_call += event_parameters
-
- if(event_handler != null)
- event_handler.HandleEvent(arglist(event_call))
-
- var/list/response = TopicResponse()
- response[DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES] = intercepted_message_queue
- intercepted_message_queue = null
- return response
+ return
if(DMAPI5_TOPIC_COMMAND_CHANGE_PORT)
var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
@@ -122,8 +129,10 @@
return TopicResponse()
if(DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE)
+ TGS_DEBUG_LOG("ProcessTopicCommand: It's a chat update")
var/list/chat_update_json = topic_parameters[DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]
if(!istype(chat_update_json))
+ TGS_DEBUG_LOG("ProcessTopicCommand: failed \"[DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]\" check")
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]!")
DecodeChannels(chat_update_json)
@@ -138,7 +147,7 @@
return TopicResponse()
if(DMAPI5_TOPIC_COMMAND_HEALTHCHECK)
- if(event_handler?.receive_health_checks)
+ if(event_handler && event_handler.receive_health_checks)
event_handler.HandleEvent(TGS_EVENT_HEALTH_CHECK)
return TopicResponse()
diff --git a/code/modules/tgui/tgui-say/modal.dm b/code/modules/tgui/tgui-say/modal.dm
index b959019b894f..f1e87e001cef 100644
--- a/code/modules/tgui/tgui-say/modal.dm
+++ b/code/modules/tgui/tgui-say/modal.dm
@@ -83,7 +83,7 @@
if(!payload?["channel"])
CRASH("No channel provided to an open TGUI-Say")
window_open = TRUE
- if(payload["channel"] != OOC_CHANNEL && payload["channel"] != LOOC_CHANNEL && payload["channel"] != MOD_CHANNEL && payload["channel"] != ADMIN_CHANNEL && payload["channel"] != MENTOR_CHANNEL)
+ if(payload["channel"] != OOC_CHANNEL && payload["channel"] != LOOC_CHANNEL && payload["channel"] != ADMIN_CHANNEL && payload["channel"] != MENTOR_CHANNEL)
start_thinking()
return TRUE
diff --git a/code/modules/tgui/tgui-say/speech.dm b/code/modules/tgui/tgui-say/speech.dm
index f278361ac0d0..7bce349e1b7f 100644
--- a/code/modules/tgui/tgui-say/speech.dm
+++ b/code/modules/tgui/tgui-say/speech.dm
@@ -47,9 +47,6 @@
if(LOOC_CHANNEL)
client.looc(entry)
return TRUE
- if(MOD_CHANNEL)
- client.cmd_mod_say(entry)
- return TRUE
if(ADMIN_CHANNEL)
client.cmd_admin_say(entry)
return TRUE
@@ -94,7 +91,7 @@
return TRUE
if(type == "force")
var/target_channel = payload["channel"]
- if(target_channel == ME_CHANNEL || target_channel == OOC_CHANNEL || target_channel == LOOC_CHANNEL || target_channel == MOD_CHANNEL)
+ if(target_channel == ME_CHANNEL || target_channel == OOC_CHANNEL || target_channel == LOOC_CHANNEL || target_channel == ADMIN_CHANNEL)
target_channel = SAY_CHANNEL // No ooc leaks
delegate_speech(alter_entry(payload), target_channel)
return TRUE
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index be92afffb898..010cba770ce2 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -85,6 +85,7 @@
#include "unit_test.dm"
#include "spawn_humans.dm"
#include "check_runtimes.dm"
+#include "wj_emotes.dm"
#undef TEST_ASSERT
#undef TEST_ASSERT_EQUAL
diff --git a/code/modules/unit_tests/wj_emotes.dm b/code/modules/unit_tests/wj_emotes.dm
new file mode 100644
index 000000000000..f89757665011
--- /dev/null
+++ b/code/modules/unit_tests/wj_emotes.dm
@@ -0,0 +1,7 @@
+/// Test that all working joe emotes have a category
+/datum/unit_test/wj_emotes
+
+/datum/unit_test/wj_emotes/Run()
+ for(var/datum/emote/living/carbon/human/synthetic/working_joe/emote as anything in subtypesof(/datum/emote/living/carbon/human/synthetic/working_joe))
+ if(!initial(emote.category))
+ TEST_FAIL("Emote [emote] did not have a category!")
diff --git a/code/modules/vehicles/interior/interior.dm b/code/modules/vehicles/interior/interior.dm
index b56de4bfe16b..046b42495ac7 100644
--- a/code/modules/vehicles/interior/interior.dm
+++ b/code/modules/vehicles/interior/interior.dm
@@ -72,6 +72,7 @@
entrance_markers = null
QDEL_NULL(reservation)
+ SSinterior.interiors -= src
return ..()
diff --git a/colonialmarines.dme b/colonialmarines.dme
index 204c144c8916..74f0f99545b4 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -111,6 +111,7 @@
#include "code\__DEFINES\vv.dm"
#include "code\__DEFINES\weapon_stats.dm"
#include "code\__DEFINES\weather.dm"
+#include "code\__DEFINES\wj_emotes.dm"
#include "code\__DEFINES\xeno.dm"
#include "code\__DEFINES\dcs\flags.dm"
#include "code\__DEFINES\dcs\helpers.dm"
@@ -370,6 +371,7 @@
#include "code\datums\components\speed_modifier.dm"
#include "code\datums\components\toxin_buildup.dm"
#include "code\datums\components\weed_damage_reduction.dm"
+#include "code\datums\components\weed_food.dm"
#include "code\datums\components\xeno\shield_slash.dm"
#include "code\datums\construction\construction_template.dm"
#include "code\datums\construction\xenomorph\construction_template_xenomorph.dm"
@@ -556,7 +558,6 @@
#include "code\datums\redis\redis_message.dm"
#include "code\datums\redis\callbacks\_redis_callback.dm"
#include "code\datums\redis\callbacks\asay.dm"
-#include "code\datums\redis\callbacks\msay.dm"
#include "code\datums\stamina\_stamina.dm"
#include "code\datums\stamina\none.dm"
#include "code\datums\statistics\cause_data.dm"
@@ -603,11 +604,13 @@
#include "code\datums\weather\weather_events\faction_clash.dm"
#include "code\datums\weather\weather_events\lv522_chances_claim.dm"
#include "code\datums\weather\weather_events\lv624.dm"
+#include "code\datums\weather\weather_events\new_varadero.dm"
#include "code\datums\weather\weather_events\sorokyne.dm"
#include "code\datums\weather\weather_map_holders\big_red.dm"
#include "code\datums\weather\weather_map_holders\faction_clash.dm"
#include "code\datums\weather\weather_map_holders\lv522_chances_claim.dm"
#include "code\datums\weather\weather_map_holders\lv624.dm"
+#include "code\datums\weather\weather_map_holders\new_varadero.dm"
#include "code\datums\weather\weather_map_holders\sorokyne.dm"
#include "code\datums\xeno_shields\xeno_shield.dm"
#include "code\datums\xeno_shields\shield_types\crusher_shield.dm"
@@ -773,6 +776,7 @@
#include "code\game\machinery\sentry_holder.dm"
#include "code\game\machinery\spaceheater.dm"
#include "code\game\machinery\status_display.dm"
+#include "code\game\machinery\storm_siren.dm"
#include "code\game\machinery\suit_storage_unit.dm"
#include "code\game\machinery\supply_display.dm"
#include "code\game\machinery\teleporter.dm"
@@ -1780,7 +1784,6 @@
#include "code\modules\mob\living\carbon\human\powers\human_powers.dm"
#include "code\modules\mob\living\carbon\human\powers\issue_order.dm"
#include "code\modules\mob\living\carbon\human\species\emote-monkey.dm"
-#include "code\modules\mob\living\carbon\human\species\emote-synthetic.dm"
#include "code\modules\mob\living\carbon\human\species\emote-yautja.dm"
#include "code\modules\mob\living\carbon\human\species\human.dm"
#include "code\modules\mob\living\carbon\human\species\monkey.dm"
@@ -1788,6 +1791,16 @@
#include "code\modules\mob\living\carbon\human\species\synthetic.dm"
#include "code\modules\mob\living\carbon\human\species\yautja.dm"
#include "code\modules\mob\living\carbon\human\species\zombie.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\_emote.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\_species.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\farewell.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\greeting.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\notice.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\question.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\quip.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\restricted_area.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\task_update.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\warning.dm"
#include "code\modules\mob\living\carbon\xenomorph\Abilities.dm"
#include "code\modules\mob\living\carbon\xenomorph\attack_alien.dm"
#include "code\modules\mob\living\carbon\xenomorph\damage_procs.dm"
diff --git a/config/example/admin_ranks.txt b/config/example/admin_ranks.txt
index e8d13e4f24fb..7fcac3b371f0 100644
--- a/config/example/admin_ranks.txt
+++ b/config/example/admin_ranks.txt
@@ -19,7 +19,6 @@
# +BAN = the ability to ban, jobban and fullban
# +STEALTH = the ability to stealthmin (make yourself appear with a fake name to everyone but other admins
# +POSSESS = the ability to possess objects
-# +REJUV (or +REJUVINATE) = the ability to heal, respawn, modify damage and use godmode
# +COLOR = the ability to use the OOC > "Set OOC Color - Self" verb
# +BUILD (or +BUILDMODE) = the ability to use buildmode
# +SERVER = higher-risk admin verbs and abilities, such as those which affect the server configuration.
@@ -36,16 +35,16 @@ Mentor +MENTOR +COLOR
Trial Moderator +MOD +SERVER +COLOR
-Moderator +MOD +SERVER +BAN +REJUV +COLOR +NOLOCK
-Trial Admin +ADMIN +MOD +SERVER +BAN +REJUV +SPAWN +STEALTH +VAREDIT +COLOR
-Admin +ADMIN +MOD +SERVER +BAN +REJUV +SPAWN +STEALTH +VAREDIT +POSSESS +SOUND +COLOR +BUILDMODE
+Moderator +MOD +SERVER +BAN +COLOR +NOLOCK
+Trial Admin +ADMIN +MOD +SERVER +BAN +SPAWN +STEALTH +VAREDIT +COLOR
+Admin +ADMIN +MOD +SERVER +BAN +SPAWN +STEALTH +VAREDIT +POSSESS +SOUND +COLOR +BUILDMODE
Coder +MOD +SERVER +BAN +VAREDIT +SPAWN +DEBUG +COLOR
Mapper +MOD +VAREDIT +COLOR +BUILDMODE +SPAWN
Spriter +MOD +VAREDIT +SPAWN +COLOR
-Developer +ADMIN +MOD +SERVER +BAN +VAREDIT +SPAWN +DEBUG +REJUV +POSSESS +BUILDMODE +SOUND +COLOR
-Lead Spriter +ADMIN +MOD +SERVER +REJUV +VAREDIT +BUILDMODE +SPAWN +DEBUG +COLOR
+Developer +ADMIN +MOD +SERVER +BAN +VAREDIT +SPAWN +DEBUG +POSSESS +BUILDMODE +SOUND +COLOR
+Lead Spriter +ADMIN +MOD +SERVER +VAREDIT +BUILDMODE +SPAWN +DEBUG +COLOR
Host +EVERYTHING
@@ -55,6 +54,6 @@ Head Dev +EVERYTHING
Legacy Mentor +MENTOR +COLOR
-Senior Moderator +ADMIN +MOD +SERVER +BAN +REJUV +SPAWN +STEALTH +VAREDIT +POSSESS +SOUND +COLOR +BUILDMODE
-Senior Admin +ADMIN +MOD +SERVER +BAN +REJUV +SPAWN +STEALTH +VAREDIT +POSSESS +SOUND +COLOR +BUILDMODE +DEBUG
+Senior Moderator +ADMIN +MOD +SERVER +BAN +SPAWN +STEALTH +VAREDIT +POSSESS +SOUND +COLOR +BUILDMODE
+Senior Admin +ADMIN +MOD +SERVER +BAN +SPAWN +STEALTH +VAREDIT +POSSESS +SOUND +COLOR +BUILDMODE +DEBUG
diff --git a/config/example/config.txt b/config/example/config.txt
index bf061da71417..1fee5c898574 100644
--- a/config/example/config.txt
+++ b/config/example/config.txt
@@ -239,3 +239,6 @@ GAMEMODE_ROUNDS_NEEDED 5
## Default gamemode to auto-switch back to after a round has concluded
GAMEMODE_DEFAULT extended
+
+## How long the mob will take to chestburst, in seconds
+#EMBRYO_BURST_TIMER 450
diff --git a/html/changelogs/AutoChangeLog-pr-3732.yml b/html/changelogs/AutoChangeLog-pr-3732.yml
deleted file mode 100644
index 1fcec99781cb..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3732.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "blackdragonTOW"
-delete-after: True
-changes:
- - refactor: "refactored dropship_ammo so that missiles don't use Spawn()"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3762.yml b/html/changelogs/AutoChangeLog-pr-3762.yml
deleted file mode 100644
index 708eac96abb7..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3762.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "ghostsheet"
-delete-after: True
-changes:
- - bugfix: "Fixed Extended Barrel bug of building up free bullet velocity."
\ No newline at end of file
diff --git a/html/changelogs/archive/2023-07.yml b/html/changelogs/archive/2023-07.yml
index eb01ed3f4007..0edee30b9f8e 100644
--- a/html/changelogs/archive/2023-07.yml
+++ b/html/changelogs/archive/2023-07.yml
@@ -27,3 +27,303 @@
theselfish:
- qol: Made the MP beret to be clickable.
- imageadd: Updated the MP beret obj sprite to be easier to click.
+2023-07-03:
+ blackdragonTOW:
+ - refactor: refactored dropship_ammo so that missiles don't use Spawn()
+ ghostsheet:
+ - bugfix: Fixed Extended Barrel bug of building up free bullet velocity.
+2023-07-04:
+ Drathek:
+ - bugfix: Fixed a persistence problem for the time of death value used for the larva
+ queue.
+ - bugfix: Fixed brainmobs (human gibbing) not getting a time of death value.
+ harryob:
+ - bugfix: paper images should appear once again
+ zzzmike:
+ - balance: pods crash land 33% of the time rather than 50%
+2023-07-05:
+ BeagleGaming1:
+ - rscadd: Queen makes noise when moved even while resting
+ Hopek, Gul Dreggrod:
+ - rscadd: Adds a Luxurious fountain pen to the game. Currently spawns on the right
+ ear of high ranking command staff. Sprited by Gul Dreggrod.
+ QuickLode:
+ - rscadd: CMB/ICC Survivors now get a limited radio that they can use to communicate
+ with CMB reinforcements.
+ - rscadd: CMB/Anchorpoint Marines get bandages to stop bleeding.
+ harryob:
+ - rscadd: all xenos can now access the tacmap, while the queen is on ovi
+2023-07-06:
+ BeagleGaming1:
+ - code_imp: evacuation pod crash chance changed to a var
+ - code_imp: proc added to force evacuation pods to crash
+ Drathek:
+ - bugfix: Fixed more broken logos (primarily WY research papers)
+ - balance: Weed nodes can no longer be placed in walls or window frames (or any
+ turf or structure with density)
+ - refactor: Refactored the plant weeds ability code
+ - balance: Gardener's hardy weeds now upgrade normal weeds (just like hive weeds
+ upgrade weeds).
+ - balance: Gardener's hardy weeds now don't prevent special structures (core and
+ pylons) but they are still only allowed if the turf allows them.
+ Newyearnewme, Morrow:
+ - rscadd: Xeno structures/weeds now become forsaken after hijack
+ Steelpoint:
+ - imageadd: Synth utility vest is now slimmer in appearance
+ realforest2001:
+ - admin: Adds logs for bioscans successfully completing.
+ thatoneyeeter:
+ - balance: metal foam now becomes solid faster
+2023-07-07:
+ Diegoflores31:
+ - rscadd: Defender Tail Slam has a 1% chance to do a BONK sound instead.
+ Drathek:
+ - bugfix: Fixed ghost icons for larva and ovi queen
+ Morrow:
+ - rscdel: Removes fountain pens from gear sets
+ - rscdel: Removed nesting the dead
+ - rscadd: Added a visiting USASF Commander (CO survivor) to New Varadero
+ - bugfix: Burnt matches no longer permanently give you a light source if they naturally
+ burn out
+ QuickLoad:
+ - balance: Colony Synthetics are faster but are less resistant. This allows for
+ the option of avoiding engagements.
+ - balance: Colony Synthetics have slightly better CQC and can carry people better.
+ realforest2001:
+ - bugfix: Fixes Queen making footstep sounds while dead and being dragged.
+2023-07-08:
+ Ben10083:
+ - bugfix: Working Joes can no longer have a gradient on their rare hair spawn.
+ - rscdel: Working Joes can no longer be fed.
+ - code_imp: 'New trait: Cannot eat. Self-explanatory.'
+ Cursor:
+ - spellcheck: Changed Chem Dispenser to Chemical Dispenser, added descriptions to
+ the cryo cell, rail computer and chemical dispenser. Updated the descriptions
+ for the Blood Dispenser and Dropship computer.
+ Drathek:
+ - bugfix: Fixed facehuggers incorrectly displaying thrown state when it has landed
+ Drathek Kugamo:
+ - rscadd: Added the ability for weeds to merge with unrevivable corpses
+ - imageadd: Added human shaped weeds by Kugamo
+ - code_imp: Added a signal for weeds sent to the turf to indicate it is now weeded,
+ and added a signal for afterbuckle.
+ - bugfix: Closets (including coffins) can no longer move anchored mobs.
+ Morrow:
+ - qol: Create humans tab length increase
+ - qol: Create humans tab now defaults to 0 range to spawn
+ - bugfix: Fixed extraneous messages in regards to wall nests
+ SpartanBobby:
+ - balance: changes M4A3 magazine size from 9 to 12
+ Zonespace27:
+ - bugfix: The maintenance jack should work a little better at crowbarring things.
+2023-07-09:
+ Khadd:
+ - rscadd: added a iv tube between the user and the bloodpack / iv drip
+ - imageadd: sprites for the iv tube
+ Zonespace27:
+ - rscadd: Working Joes now have an emote panel to use voice lines, accessible as
+ an action button.
+ theselfish:
+ - qol: Foxtrot's radio channel is now on if you have multi-squad in your headset.
+2023-07-10:
+ BeagleGaming1:
+ - rscadd: Acid runners don't get acid from slashing nested humans
+ - rscadd: Very late-stage marines do not give acid
+ - bugfix: Fixes one way of abusing resin fruit
+ Drathek:
+ - rscdel: Remove first life priority for larva queue
+ - bugfix: Fix ghosting as a facehugger counting as death for the larva queue
+ blackdragonTOW:
+ - spellcheck: Added "U1" designation to the UGL attachment.
+2023-07-11:
+ BeagleGaming1:
+ - bugfix: Fixes the Working Joe species
+2023-07-12:
+ Morrow:
+ - bugfix: Stops xenos from bypassing dead moving via roller beds
+ - balance: Telebaton now scales in stunforce effectiveness based on policing skill
+ - balance: Marines called with foxtrot nerfed from 15 to 10.
+ SpartanBobby:
+ - maptweak: 'LV522: Flips the layout of engineering making the T-comms unit on the
+ far side of the LZ'
+ - balance: reduces the M1911 magazine size from 14 to 7
+ Steelpoint:
+ - balance: Synthetic Armour slowdown has been decreased
+2023-07-13:
+ QuickLoad, Thwomper:
+ - imageadd: Synthetic Uniforms & Vest with United Americas flair and classic jumpsuits
+ which remind you of life on the Frontier. Sprited by THE THWOMPA himself!
+ - qol: Synthetic Vendor is more organized. Removes scrub cap, adds shoe.
+2023-07-14:
+ Drathek:
+ - bugfix: Fix ghosting preventing first dibs on the larva in a hugged marine
+ Unknownity:
+ - bugfix: Fixed burrowed mobs being able to be targeted by sentries, mines and SG
+ autofire.
+ - bugfix: Fixed burrowed mobs being able to grab mobs on the surface.
+ - bugfix: Fixed burrowed mobs being able to resist while burrowed.
+ - bugfix: Fixed burrowers taking damage from direct flame and shrapnel from explosions.
+ - bugfix: Fixed burrowers being able to get slashed from enemy Xenos on the surface.
+ - bugfix: Fixed burrowers unburrow stun to now properly target and stun enemy Xenos.
+ - soundadd: Added sounds for the Burrower when they are burrowing and unburrowing.
+2023-07-15:
+ BeagleGaming1:
+ - code_imp: Messed with chem and drink dispenser code
+ Ben10083:
+ - spellcheck: Fixed typos relating to M74 airburst packets.
+ - mapadd: Combat Information Center Reception now has a telephone
+ - maptweak: Medical Lower telephone shifted to the left
+ Drathek:
+ - bugfix: Fix morpher ejected items and dumped objectives not restoring their mouse_opacity
+ setting.
+ Morrow:
+ - admin: VV Jump To Fix
+ - balance: Lurkers now lose their invisibility when they run into a person
+ Steelpoint:
+ - ui: Predator Ship is now called 'Yautja Ship" for teleporting Predators
+ Zonespace27:
+ - admin: Removed msay. All staff now have access to asay
+ ondrej008:
+ - bugfix: The HE OB now deals the correct amount of damage to xenos, before it dealt
+ half damage caused by xenos being forced to rest before it hit.
+ realforest2001:
+ - soundadd: Added a drag sound for footstep component
+ - bugfix: Fixes the icon on the alien blade on LV.
+ - bugfix: Carbon copies can no longer infinitely breed.
+ - rscadd: Added a PMC Synth Survivor preset, and stopped PMC Synth Survivor using
+ the ERT set.
+ - bugfix: You can no longer weld non metal containers closed.
+ - bugfix: Fixes the toggle notification sound verb for Yautja bracers not working.
+2023-07-16:
+ Drathek:
+ - rscadd: Facehuggers now convert to their NPC version after 7 minutes of inactivity
+ and no client.
+ - code_imp: Cleanup join as xeno button code somewhat.
+ Drathek, Fira:
+ - bugfix: Fixed an issue with table flips that could make some tables incorrectly
+ unflippable, and cause infinite loops. It is no longer possible to flip tables
+ that t-shape or cross, or spans more than 5 tiles away from you.
+ Drathek, Steelpoint:
+ - bugfix: Fixed possible hardeletes for predator landmarks and vehicles. Predator
+ teleporation descriptions now do not change if the area is altered at runtime
+ so they can still be found correctly.
+ theselfish:
+ - rscadd: SOs may now get coats in their vendor.
+2023-07-17:
+ Puckaboo2:
+ - spellcheck: Some duplicate icon states have been differentiated to prevent future
+ missing icon state errors.
+ - imageadd: Added new icon states for chemical and virology dispensers.
+ - rscdel: Removed dozens of duplicate icon states from over 50 files to reduce bloat.
+ - rscdel: Removed duplicate empty icon states.
+ realforest2001:
+ - maptweak: Added Apollo Maintenance Controllers to the following locations.
+ - maptweak: Astronavigation, CIC Substation, Brig Substation, Req Aux Storage, Hangar
+ & OT & Engineering workshops, Reactor Core Room and Lifeboat Control Ring.
+2023-07-19:
+ Ben10083:
+ - rscadd: Working Joes now have a unique death message. Credit to Quickload for
+ the message.
+ - soundadd: Working Joes now have a death rattle. Credit to Quickload for shifting
+ through Alien Isolation audio files.
+ - qol: Things that die with intent eyes now lose color in their eyes on death.
+ Drathek:
+ - bugfix: Banished players will no longer be candidates for hives they are banished
+ from.
+ - bugfix: Cryoing will now set your larva queue time so you don't get prioritized
+ over others that have been waiting.
+ - admin: Shuttle intoTheSunset will set larva queue time the same as other situations.
+ - qol: Added a preference to disable xeno ability deactivation when re-selecting
+ the same ability
+ Morrow:
+ - rscadd: Added bayonet pouch to req
+ - balance: Explosive pouch inventory size from 3 to 6
+ - balance: Explosive pouch can no longer hold mine boxes
+ NewyearnewmeUwu:
+ - rscadd: Added the ES-4 electrostatic pistol to the CL's safe.
+ realforest2001:
+ - balance: Reduced tranq pistol bullet damage from 40 to 15
+ - bugfix: Fixes another synth survivor variant having marine comms.
+ theselfish:
+ - spellcheck: Goodbye Squad Spotter, hello regular non-squaded Spotter.
+2023-07-20:
+ Anuv:
+ - bugfix: '#3775'
+ - rscadd: Added a new storm siren unique to NV, new survivor types
+ - spellcheck: Updates to the waking distress call notif.
+ - code_imp: Weather-related code changes. Update to the handheld maps.
+ - soundadd: Added new monsoon weather sfx, new map-unique ambience, new storm siren
+ sfx.
+ - maptweak: New LZ (LZ2 Palm Airfield) in the NE with a construction zone. Removed
+ a number of rocks around cave areas. Increased hive location choices from 3
+ to 4. Added glass ceilings to facility areas (deep caves are still protected,
+ can be OB'd). Slightly widened the 45 minute weed protection areas around LZs.
+ Added a fog monsoon to the SE beach that dissipates after an hour. Expanded
+ east beach. New SW cave section. Opened up the walkway west of LZ1. Enhanced
+ survivor loot and mats to a comparable degree to current maps. Many small changes
+ to lessen chokes and offer more route choice for xenos and marines alike. Removed
+ Spearhead/Rival and laser pistols. Swapped fawras for monkeys. Added more survivor-type
+ choices. Added a bar area and decrepit truck to the north beach.
+ - config: Enabled NV again as a map in maps.txt.
+ Awantje:
+ - bugfix: The M56D no longer let's you violate density code when exiting it.
+ BeagleGaming1:
+ - config: Moved time to burst to the config
+ - balance: Fixed time to burst, lowering it from 15 minutes to 7.5 minutes by default
+ - balance: Stasis bags used on late-stage mobs properly slow growth down less
+ Ben10083:
+ - qol: Non player Facehuggers now also notify the hive when they facehug something.
+ - spellcheck: fix typo relating to Starshell ash
+ Diegoflores31:
+ - balance: Acid Splash damage increased to 25
+ - balance: Acid Splash damage can be reduced with BIO armor.
+ - balance: Reduced Light armor BIO resistance by 10
+ - balance: Increased Heavy armor BIO resistance by 5
+ - balance: Increased VL vest BIO resistance by 5
+ GoldenDarkness55:
+ - balance: Base rav cooldown regain per slash reduced (4 to 3), empowered charge
+ stun duration reduced (2 to 1.3), empower duration increased (5 to 6.5), empower
+ cooldown reduced (22 to 18), duration window for second empower cast reduced
+ (15 to 6), base shield gain reduced (75 to 50), shield gain per marine reduced
+ (80 to 50), maximum empower targets increased (5 to 6), scissor slash cooldown
+ reduced (10 to 6), and damage reduced (45 to 40)
+ - balance: Hedge rav doesn't lose speed anymore, but loses 10 slash damage instead.
+ Shield duration increased (2 to 2.5), Fire Spikes shard range increased (5 to
+ 6), Spike Shed shard lockout duration reduced (30 to 15), shard lock speed reduced
+ to maintain same speed as previously. Shard damage increased (5 to 8), shard
+ slowdown duration reduced (8 to 3), shard gain per tick reduced (10 to 5), shard
+ gain per bullet reduced (20 to 10), added shard gain per slash (15)
+ Huffie56:
+ - bugfix: fix some area having no apc on the almayer making them not behave like
+ they should specialy when hijack happen.
+ IowaPotatoFarmer:
+ - rscadd: The PMC Crash on Solaris Ridge now spawns one PMC medic survivor and one
+ PMC engineer survivor.
+ - bugfix: The Wey-Yu goon survivors now have their unique corporate white camo M41A
+ MK2 back.
+ Kaboomcat:
+ - imageadd: Updated the gin bottle sprite
+ QuickLoad:
+ - balance: Tones down ERT skills. Impacts VAIPO/Dutch'sDozen/CMB/UPP/CLF/EMerc.
+ - balance: Gives PMC & SurvPMC a trained level of CQC.
+ Steelpoint:
+ - maptweak: More weed spawns so larva don't spawn in area with no weed to evolve
+ on.
+ - rscadd: Predator toolbelt is now a unique item with its own sprite.
+ TeDGamer:
+ - bugfix: Xenos allied to the hive can now open hive's doors
+ - bugfix: Huggers can now scuttle doors
+ - code_imp: Combined hugger + larva code to allow for any small castes to scuttle
+ Zonespace27:
+ - balance: Trashbags now hold normal items and can be looked through like a box
+ or storage container.
+ - balance: Trashbags no longer fit in your belt slot.
+ ihatethisengine:
+ - balance: Lurkers don't lose invisibility when they bump into partly visible mobs.
+ realforest2001:
+ - admin: Removed the R_REJUVINATE permissions flag and replaces instances where
+ it is used with R_MOD.
+ - admin: Added logs if a restricted proc is proccalled.
+ silencer_pl:
+ - qol: Default paper and desc_lore viewing windows are now larger to match the expected
+ content in them better.
diff --git a/html/create_humans.html b/html/create_humans.html
index ed9361fc6f25..06b92cba0c71 100644
--- a/html/create_humans.html
+++ b/html/create_humans.html
@@ -11,7 +11,7 @@
Amount of humans:
- Range to spawn in:
+ Range to spawn in: