diff --git a/code/__DEFINES/dcs/signals/atom/mob/signals_mind.dm b/code/__DEFINES/dcs/signals/atom/mob/signals_mind.dm
new file mode 100644
index 0000000000..2cca5fe18c
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/atom/mob/signals_mind.dm
@@ -0,0 +1,2 @@
+///from mind/transfer_to. Sent after the mind has been transferred to a different body: (mob/previous_body)
+#define COMSIG_MIND_TRANSFERRED "mind_transferred"
diff --git a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
index 0271cd6334..dd995ba864 100644
--- a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
+++ b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
@@ -72,9 +72,13 @@
#define COMSIG_MOB_PRE_CLICK "mob_pre_click"
#define COMPONENT_INTERRUPT_CLICK (1<<0)
-///from base of /mob/Login(): ()
+/// From base of /mob/Login(), called when a client logs into this mob: ()
+/// Not to be confused with [COMSIG_MOB_LOGGED_IN]
#define COMSIG_MOB_LOGIN "mob_login"
-///from base of /mob/Logout(): ()
+/// From base of /mob/Login(), called after a client logs into this mob: ()
+/// Not to be confused with [COMSIG_MOB_LOGIN]
+#define COMSIG_MOB_LOGGED_IN "mob_logged_in"
+/// From base of /mob/Logout(): ()
#define COMSIG_MOB_LOGOUT "mob_logout"
//from /mob/proc/on_deafness_gain()
diff --git a/code/__DEFINES/dcs/signals/signals_client.dm b/code/__DEFINES/dcs/signals/signals_client.dm
index 6733e07035..e7e74771fc 100644
--- a/code/__DEFINES/dcs/signals/signals_client.dm
+++ b/code/__DEFINES/dcs/signals/signals_client.dm
@@ -19,8 +19,8 @@
/// Called after one or more verbs are added: (list of verbs added)
#define COMSIG_CLIENT_VERB_REMOVED "client_verb_removed"
-/// Called after a client logs into a mob: (mob)
-#define COMSIG_CLIENT_MOB_LOGIN "client_mob_changed"
+/// Called from /mob/Login() after a client logs into a mob: (mob)
+#define COMSIG_CLIENT_MOB_LOGGED_IN "client_mob_logged_in"
/// Called when something is added to a client's screen : /client/proc/add_to_screen(screen_add)
#define COMSIG_CLIENT_SCREEN_ADD "client_screen_add"
diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm
index 89904c5a1b..825f427695 100644
--- a/code/__DEFINES/dcs/signals/signals_global.dm
+++ b/code/__DEFINES/dcs/signals/signals_global.dm
@@ -34,9 +34,11 @@
#define COMSIG_GLOB_REMOVE_VOTE_BUTTON "!remove_vote_button"
-#define COMSIG_GLOB_CLIENT_LOGIN "!client_login"
+/// Called from /client/New() when a client logs in to the game: (client)
+#define COMSIG_GLOB_CLIENT_LOGGED_IN "!client_logged_in"
-#define COMSIG_GLOB_MOB_LOGIN "!mob_login"
+/// Called from /mob/Login() when a client logs into a mob: (mob)
+#define COMSIG_GLOB_MOB_LOGGED_IN "!mob_logged_in"
///from /datum/controller/subsystem/ticker/PostSetup
#define COMSIG_GLOB_POST_SETUP "!post_setup"
diff --git a/code/__DEFINES/sounds.dm b/code/__DEFINES/sounds.dm
index 6c5d2879f3..13a4468427 100644
--- a/code/__DEFINES/sounds.dm
+++ b/code/__DEFINES/sounds.dm
@@ -61,6 +61,9 @@
#define SOUND_ENVIRONMENT_DIZZY 24
#define SOUND_ENVIRONMENT_PSYCHOTIC 25
+#define SOUND_ECHO_REVERB_ON list(0, 0, 0, 0, 0, 0.0, 0, 0.25, 1.5, 1.0, 0, 1.0, 0, 0.0, 0.0, 0.0, 1.0, 0)
+#define SOUND_ECHO_REVERB_OFF list(0, 0, -10000, -10000, 0, 0.0, 0, 0.25, 1.5, 1.0, 0, 1.0, 0, 0.0, 0.0, 0.0, 1.0, 0) //-10000 to Room & RoomHF makes enviromental reverb effectively inaudible
+
#define AMBIENCE_SHIP 'sound/ambience/shipambience.ogg'
#define AMBIENCE_JUNGLE 'sound/ambience/ambienceLV624.ogg'
#define AMBIENCE_RIVER 'sound/ambience/ambienceriver.ogg'
diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm
index f84bac20b1..74f3149610 100644
--- a/code/_globalvars/misc.dm
+++ b/code/_globalvars/misc.dm
@@ -53,3 +53,5 @@ GLOBAL_VAR(xeno_queue_candidate_count)
GLOBAL_VAR(obfs_x)
/// A number between -500 and 500.
GLOBAL_VAR(obfs_y)
+
+GLOBAL_VAR_INIT(ai_xeno_weeding, TRUE)
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index d114aff6b7..18587c0545 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -59,9 +59,11 @@
/atom/movable/screen/action_button/attack_ghost(mob/dead/observer/user)
return
-/atom/movable/screen/action_button/clicked(mob/user)
+/atom/movable/screen/action_button/clicked(mob/user, list/mods)
if(!user || !source_action)
return TRUE
+ if(source_action.owner != user)
+ return TRUE
if(source_action.can_use_action())
source_action.action_activate()
@@ -97,7 +99,7 @@
icon_state = "hide"
var/hidden = 0
-/atom/movable/screen/action_button/hide_toggle/clicked(mob/user, mods)
+/atom/movable/screen/action_button/hide_toggle/clicked(mob/user, list/mods)
user.hud_used.action_buttons_hidden = !user.hud_used.action_buttons_hidden
hidden = user.hud_used.action_buttons_hidden
if(hidden)
@@ -107,7 +109,7 @@
name = "Hide Buttons"
icon_state = "hide"
user.update_action_buttons()
- return 1
+ return TRUE
/atom/movable/screen/action_button/ghost/minimap/get_button_screen_loc(button_number)
return "SOUTH:6,CENTER+1:24"
@@ -214,7 +216,7 @@
/atom/movable/screen/zone_sel/robot
icon = 'icons/mob/hud/screen1_robot.dmi'
-/atom/movable/screen/clicked(mob/user)
+/atom/movable/screen/clicked(mob/user, list/mods)
if(!user)
return TRUE
diff --git a/code/controllers/subsystem/minimap.dm b/code/controllers/subsystem/minimap.dm
index 1c2a6d3e24..a26d024b17 100644
--- a/code/controllers/subsystem/minimap.dm
+++ b/code/controllers/subsystem/minimap.dm
@@ -717,6 +717,7 @@ SUBSYSTEM_DEF(minimaps)
user.client.register_map_obj(map_holder.map)
ui = new(user, src, "TacticalMap")
ui.open()
+ RegisterSignal(user.mind, COMSIG_MIND_TRANSFERRED, PROC_REF(on_mind_transferred))
/datum/tacmap/drawing/tgui_interact(mob/user, datum/tgui/ui)
var/mob/living/carbon/xenomorph/xeno = user
@@ -755,6 +756,7 @@ SUBSYSTEM_DEF(minimaps)
tacmap_ready_time = SSminimaps.next_fire + 2 SECONDS
addtimer(CALLBACK(src, PROC_REF(on_tacmap_fire), faction), SSminimaps.next_fire - world.time + 1 SECONDS)
user.client.register_map_obj(map_holder.map)
+ RegisterSignal(user.mind, COMSIG_MIND_TRANSFERRED, PROC_REF(on_mind_transferred))
ui = new(user, src, "TacticalMap")
ui.open()
@@ -835,6 +837,9 @@ SUBSYSTEM_DEF(minimaps)
return data
+/datum/tacmap/ui_close(mob/user)
+ UnregisterSignal(user.mind, COMSIG_MIND_TRANSFERRED)
+
/datum/tacmap/drawing/ui_close(mob/user)
. = ..()
action_queue_change = 0
@@ -950,6 +955,11 @@ SUBSYSTEM_DEF(minimaps)
return UI_INTERACTIVE
+// This gets removed when the player changes bodies (i.e. xeno evolution), so re-register it when that happens.
+/datum/tacmap/proc/on_mind_transferred(datum/mind/source, mob/previous_body)
+ SIGNAL_HANDLER
+ source.current.client.register_map_obj(map_holder.map)
+
/datum/tacmap_holder
var/map_ref
var/atom/movable/screen/minimap/map
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index cfe66421c9..d3cb19c40e 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -180,7 +180,7 @@ SUBSYSTEM_DEF(ticker)
CHECK_TICK
mode.announce()
if(mode.taskbar_icon)
- RegisterSignal(SSdcs, COMSIG_GLOB_CLIENT_LOGIN, PROC_REF(handle_mode_icon))
+ RegisterSignal(SSdcs, COMSIG_GLOB_CLIENT_LOGGED_IN, PROC_REF(handle_mode_icon))
set_clients_taskbar_icon(mode.taskbar_icon)
if(GLOB.perf_flags & PERF_TOGGLE_LAZYSS)
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index a56e10636a..34350613ce 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -59,7 +59,7 @@ SUBSYSTEM_DEF(vote)
voting.Cut()
remove_action_buttons()
- UnregisterSignal(SSdcs, COMSIG_GLOB_CLIENT_LOGIN)
+ UnregisterSignal(SSdcs, COMSIG_GLOB_CLIENT_LOGGED_IN)
for(var/c in GLOB.player_list)
update_static_data(c)
@@ -370,7 +370,7 @@ SUBSYSTEM_DEF(vote)
if(send_clients_vote)
C.mob.vote()
- RegisterSignal(SSdcs, COMSIG_GLOB_CLIENT_LOGIN, PROC_REF(handle_client_joining))
+ RegisterSignal(SSdcs, COMSIG_GLOB_CLIENT_LOGGED_IN, PROC_REF(handle_client_joining))
SStgui.update_uis(src)
return TRUE
return FALSE
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 0510a43415..3a597ad262 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -11,9 +11,9 @@
var/cost = 0 // By default an action has no cost -> will be utilized by skill actions/xeno actions
var/action_flags = 0 // Check out __game.dm for flags
/// Whether the action is hidden from its owner
- /// Useful for when you want to preserve action state while preventing
- /// a mob from using said action
- var/hidden = FALSE
+ var/hidden = FALSE //Preserve action state while preventing mob from using action
+ ///Hide the action from the owner without preventing them from using it (incase of keybind listen_signal)
+ var/player_hidden = FALSE
var/unique = TRUE
/// A signal on the mob that will cause the action to activate
var/listen_signal
@@ -227,7 +227,7 @@
var/atom/movable/screen/action_button/B = A.button
if(reload_screen)
client.add_to_screen(B)
- if(A.hidden)
+ if(A.hidden || A.player_hidden)
B.screen_loc = null
continue
button_number++
diff --git a/code/datums/ammo/bullet/special_ammo.dm b/code/datums/ammo/bullet/special_ammo.dm
index 127e8b1ba1..3a592fc8b2 100644
--- a/code/datums/ammo/bullet/special_ammo.dm
+++ b/code/datums/ammo/bullet/special_ammo.dm
@@ -104,9 +104,9 @@
icon_state = "bullet" // Keeping it bog standard with the turret but allows it to be changed
accurate_range = 12
- damage = 35
+ damage = 40
penetration= ARMOR_PENETRATION_TIER_10 //Bumped the penetration to serve a different role from sentries, MGs are a bit more offensive
- accuracy = HIT_ACCURACY_TIER_3
+ accuracy = HIT_ACCURACY_TIER_5
/datum/ammo/bullet/machinegun/set_bullet_traits()
. = ..()
@@ -158,7 +158,7 @@
accurate_range = 12
/datum/ammo/bullet/m60
- name = "M60 bullet"
+ name = "Mk70 bullet"
headshot_state = HEADSHOT_OVERLAY_MEDIUM
accuracy = HIT_ACCURACY_TIER_2
diff --git a/code/datums/components/overlay_lighting.dm b/code/datums/components/overlay_lighting.dm
index e14066ffb7..225149ecb4 100644
--- a/code/datums/components/overlay_lighting.dm
+++ b/code/datums/components/overlay_lighting.dm
@@ -199,7 +199,7 @@
get_new_turfs()
-///Adds the luminosity and source for the afected movable atoms to keep track of their visibility.
+///Adds the luminosity and source for the affected movable atoms to keep track of their visibility.
/datum/component/overlay_lighting/proc/add_dynamic_lumi()
LAZYSET(current_holder.affected_movable_lights, src, lumcount_range + 1)
current_holder.underlays += visible_mask
@@ -207,7 +207,7 @@
if(directional)
current_holder.underlays += cone
-///Removes the luminosity and source for the afected movable atoms to keep track of their visibility.
+///Removes the luminosity and source for the affected movable atoms to keep track of their visibility.
/datum/component/overlay_lighting/proc/remove_dynamic_lumi()
LAZYREMOVE(current_holder.affected_movable_lights, src)
current_holder.underlays -= visible_mask
@@ -267,6 +267,9 @@
///Used to determine the new valid current_holder from the parent's loc.
/datum/component/overlay_lighting/proc/check_holder()
var/atom/movable/movable_parent = GET_PARENT
+ if(QDELETED(movable_parent))
+ set_holder(null)
+ return
if(isturf(movable_parent.loc))
set_holder(movable_parent)
return
@@ -275,13 +278,21 @@
set_holder(null)
return
if(isturf(inside.loc))
- set_holder(inside)
+ // storage items block light, also don't be moving into a qdeleted item
+ if(QDELETED(inside) || istype(inside, /obj/item/storage))
+ set_holder(null)
+ else
+ set_holder(inside)
return
set_holder(null)
///Called when the current_holder is qdeleted, to remove the light effect.
/datum/component/overlay_lighting/proc/on_holder_qdel(atom/movable/source, force)
+ SIGNAL_HANDLER
+ if(QDELETED(current_holder))
+ set_holder(null)
+ return
UnregisterSignal(current_holder, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED))
if(directional)
UnregisterSignal(current_holder, COMSIG_ATOM_DIR_CHANGE)
@@ -290,6 +301,7 @@
///Called when current_holder changes loc.
/datum/component/overlay_lighting/proc/on_holder_moved(atom/movable/source, OldLoc, Dir, Forced)
+ SIGNAL_HANDLER
if(!(overlay_lighting_flags & LIGHTING_ON))
return
make_luminosity_update()
@@ -450,8 +462,7 @@
. = lum_power
lum_power = new_lum_power
var/difference = . - lum_power
- for(var/t in affected_turfs)
- var/turf/lit_turf = t
+ for(var/turf/lit_turf as anything in affected_turfs)
lit_turf.dynamic_lumcount -= difference
///Here we append the behavior associated to changing lum_power.
diff --git a/code/datums/emergency_calls/cryo_marines.dm b/code/datums/emergency_calls/cryo_marines.dm
index 19f73a9843..f7a486bc04 100644
--- a/code/datums/emergency_calls/cryo_marines.dm
+++ b/code/datums/emergency_calls/cryo_marines.dm
@@ -48,7 +48,6 @@
leader = human
leaders++
human.client?.prefs.copy_all_to(human, JOB_SQUAD_LEADER, TRUE, TRUE)
- arm_equipment(human, /datum/equipment_preset/uscm/leader/cryo, mind == null, TRUE)
to_chat(human, SPAN_ROLE_HEADER("You are a Squad Leader in the USCM"))
to_chat(human, SPAN_ROLE_BODY("You are here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
to_chat(human, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
@@ -62,7 +61,6 @@
else if (medics < max_medics && (!mind || (HAS_FLAG(human.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(human.client, JOB_SQUAD_MEDIC, time_required_for_job))))
medics++
human.client?.prefs.copy_all_to(human, JOB_SQUAD_MEDIC, TRUE, TRUE)
- arm_equipment(human, /datum/equipment_preset/uscm/medic/cryo, mind == null, TRUE)
to_chat(human, SPAN_ROLE_HEADER("You are a Hospital Corpsman in the USCM"))
to_chat(human, SPAN_ROLE_BODY("You are here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
to_chat(human, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
@@ -75,7 +73,6 @@
to_chat(human, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
else
human.client?.prefs.copy_all_to(human, JOB_SQUAD_MARINE, TRUE, TRUE)
- arm_equipment(human, /datum/equipment_preset/uscm/pfc/cryo, mind == null, TRUE)
to_chat(human, SPAN_ROLE_HEADER("You are a Rifleman in the USCM"))
to_chat(human, SPAN_ROLE_BODY("You are here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
to_chat(human, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
diff --git a/code/datums/emergency_calls/cryo_marines_heavy.dm b/code/datums/emergency_calls/cryo_marines_heavy.dm
index f4fe3c9f2a..c733b9101e 100644
--- a/code/datums/emergency_calls/cryo_marines_heavy.dm
+++ b/code/datums/emergency_calls/cryo_marines_heavy.dm
@@ -39,7 +39,6 @@
if(leaders < cryo_squad.max_leaders && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(H.client, JOB_SQUAD_LEADER, time_required_for_job))
leader = H
leaders++
- arm_equipment(H, /datum/equipment_preset/uscm/leader_equipped/cryo, TRUE, TRUE)
to_chat(H, SPAN_ROLE_HEADER("You are a Squad Leader in the USCM"))
to_chat(H, SPAN_ROLE_BODY("Your squad is here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]."))
else if (heavies < max_heavies && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_HEAVY) && check_timelock(H.client, JOB_SQUAD_SPECIALIST, time_required_for_job))
@@ -49,7 +48,6 @@
to_chat(H, SPAN_ROLE_BODY("Your squad is here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]."))
else if(smartgunners < max_smartgunners && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_SMARTGUNNER) && check_timelock(H.client, JOB_SQUAD_SMARTGUN, time_required_for_job))
smartgunners++
- arm_equipment(H, /datum/equipment_preset/uscm/smartgunner_equipped/cryo, TRUE, TRUE)
to_chat(H, SPAN_ROLE_HEADER("You are a Smartgunner in the USCM"))
to_chat(H, SPAN_ROLE_BODY("Your squad is here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]."))
else if(engineers < max_engineers && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_ENGINEER) && check_timelock(H.client, JOB_SQUAD_ENGI, time_required_for_job))
@@ -59,11 +57,9 @@
to_chat(H, SPAN_ROLE_BODY("Your squad is here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]."))
else if (medics < max_medics && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(H.client, JOB_SQUAD_MEDIC, time_required_for_job))
medics++
- arm_equipment(H, /datum/equipment_preset/uscm/medic_equipped/cryo, TRUE, TRUE)
to_chat(H, SPAN_ROLE_HEADER("You are a Hospital Corpsman in the USCM"))
to_chat(H, SPAN_ROLE_BODY("Your squad is here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]."))
else
- arm_equipment(H, /datum/equipment_preset/uscm/private_equipped/cryo, TRUE, TRUE)
to_chat(H, SPAN_ROLE_HEADER("You are a Rifleman in the USCM"))
to_chat(H, SPAN_ROLE_BODY("Your squad is here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]."))
diff --git a/code/datums/fluff_emails.dm b/code/datums/fluff_emails.dm
index f7083541dd..a71ad083bb 100644
--- a/code/datums/fluff_emails.dm
+++ b/code/datums/fluff_emails.dm
@@ -3,11 +3,9 @@
var/title //email title
var/entry_text //email content
-
//emails for the Almayer computers
/datum/fluff_email/almayer
-
/datum/fluff_email/almayer/iwantout
title = "RE: I want out."
entry_text = {"
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index fe59699fb9..ae6304dbcd 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -32,11 +32,11 @@
msg_admin_niche("[key]/[ckey] has tried to transfer to deleted [new_character].")
return
- SEND_SIGNAL(current.client, COMSIG_CLIENT_MIND_TRANSFER, new_character)
-
+ var/mob/old_current = current
if(current)
current.mind = null //remove ourself from our old body's mind variable
nanomanager.user_transferred(current, new_character) // transfer active NanoUI instances to new user
+ SStgui.on_transfer(current, new_character) // and active TGUI instances
if(key)
if(new_character.key != key)
@@ -66,6 +66,7 @@
continue
player_entity = setup_player_entity(ckey)
+ SEND_SIGNAL(src, COMSIG_MIND_TRANSFERRED, old_current)
SEND_SIGNAL(new_character, COMSIG_MOB_NEW_MIND, current.client)
new_character.refresh_huds(current) //inherit the HUDs from the old body
diff --git a/code/datums/skills/uscm.dm b/code/datums/skills/uscm.dm
index c2d05bd786..2e2a53b610 100644
--- a/code/datums/skills/uscm.dm
+++ b/code/datums/skills/uscm.dm
@@ -310,6 +310,7 @@ COMMAND STAFF
SKILL_INTEL = SKILL_INTEL_TRAINED,
SKILL_SURGERY = SKILL_SURGERY_NOVICE,
SKILL_PILOT = SKILL_PILOT_EXPERT,
+ SKILL_NAVIGATIONS = SKILL_NAVIGATIONS_TRAINED,
)
/datum/skills/SEA
diff --git a/code/datums/soundOutput.dm b/code/datums/soundOutput.dm
index 85548d6c90..0fddd9b503 100644
--- a/code/datums/soundOutput.dm
+++ b/code/datums/soundOutput.dm
@@ -4,13 +4,25 @@
var/list/soundscape_playlist = list() //Updated on changing areas
var/ambience = null //The file currently being played as ambience
var/status_flags = 0 //For things like ear deafness, psychodelic effects, and other things that change how all sounds behave
- var/list/echo
-/datum/soundOutput/New(client/C)
- if(!C)
+
+ /// Currently applied environmental reverb.
+ VAR_PROTECTED/owner_environment = SOUND_ENVIRONMENT_NONE
+
+/datum/soundOutput/New(client/client)
+ if(!client)
qdel(src)
return
- owner = C
- . = ..()
+ owner = client
+ RegisterSignal(owner.mob, COMSIG_MOVABLE_MOVED, PROC_REF(on_mob_moved))
+ RegisterSignal(owner.mob, COMSIG_MOB_LOGOUT, PROC_REF(on_mob_logout))
+ RegisterSignal(owner, COMSIG_CLIENT_MOB_LOGGED_IN, PROC_REF(on_client_mob_logged_in))
+ return ..()
+
+/datum/soundOutput/Destroy()
+ UnregisterSignal(owner.mob, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_LOGOUT))
+ UnregisterSignal(owner, COMSIG_CLIENT_MOB_LOGGED_IN)
+ owner = null
+ return ..()
/datum/soundOutput/proc/process_sound(datum/sound_template/T)
var/sound/S = sound(T.file, T.repeat, T.wait)
@@ -24,7 +36,6 @@
S.offset = T.offset
S.pitch = T.pitch
S.status = T.status
- S.echo = T.echo
if(T.x && T.y && T.z)
var/turf/owner_turf = get_turf(owner.mob)
if(owner_turf)
@@ -40,16 +51,12 @@
S.x = T.x - owner_turf.x
S.y = 0
S.z = T.y - owner_turf.y
- var/area/A = owner_turf.loc
- S.environment = A.sound_environment
S.y += T.y_s_offset
S.x += T.x_s_offset
+ S.echo = SOUND_ECHO_REVERB_ON //enable environment reverb for positional sounds
if(owner.mob.ear_deaf > 0)
S.status |= SOUND_MUTE
- if(owner.mob.sound_environment_override != SOUND_ENVIRONMENT_NONE)
- S.environment = owner.mob.sound_environment_override
-
sound_to(owner,S)
/datum/soundOutput/proc/update_ambience(area/target_area, ambience_override, force_update = FALSE)
@@ -86,7 +93,6 @@
S.status = status_flags
if(target_area)
- S.environment = target_area.sound_environment
var/muffle
if(target_area.ceiling_muffle)
switch(target_area.ceiling)
@@ -130,6 +136,51 @@
S.status = SOUND_UPDATE
sound_to(owner, S)
+/// Pulls mob's area's sound_environment and applies if necessary and not overridden.
+/datum/soundOutput/proc/update_area_environment()
+ var/area/owner_area = get_area(owner.mob)
+ var/new_environment = owner_area.sound_environment
+
+ if(owner.mob.sound_environment_override != SOUND_ENVIRONMENT_NONE) //override in effect, can't apply
+ return
+
+ set_owner_environment(new_environment)
+
+/// Pulls mob's sound_environment_override and applies if necessary.
+/datum/soundOutput/proc/update_mob_environment_override()
+ var/new_environment_override = owner.mob.sound_environment_override
+
+ if(new_environment_override == SOUND_ENVIRONMENT_NONE) //revert to area environment
+ update_area_environment()
+ return
+
+ set_owner_environment(new_environment_override)
+
+/// Pushes new_environment to owner and updates owner_environment var.
+/datum/soundOutput/proc/set_owner_environment(new_environment = SOUND_ENVIRONMENT_NONE)
+ if(new_environment ~= src.owner_environment) //no need to change
+ return
+
+ var/sound/sound = sound()
+ sound.environment = new_environment
+ sound_to(owner, sound)
+
+ src.owner_environment = new_environment
+
+/datum/soundOutput/proc/on_mob_moved(datum/source, atom/oldloc, direction, Forced)
+ SIGNAL_HANDLER //COMSIG_MOVABLE_MOVED
+ update_area_environment()
+
+/datum/soundOutput/proc/on_mob_logout(datum/source)
+ SIGNAL_HANDLER //COMSIG_MOB_LOGOUT
+ UnregisterSignal(owner.mob, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_LOGOUT))
+
+/datum/soundOutput/proc/on_client_mob_logged_in(datum/source, mob/new_mob)
+ SIGNAL_HANDLER //COMSIG_CLIENT_MOB_LOGGED_IN
+ RegisterSignal(owner.mob, COMSIG_MOVABLE_MOVED, PROC_REF(on_mob_moved))
+ RegisterSignal(owner.mob, COMSIG_MOB_LOGOUT, PROC_REF(on_mob_logout))
+ update_mob_environment_override()
+
/client/proc/adjust_volume_prefs(volume_key, prompt = "", channel_update = 0)
volume_preferences[volume_key] = (tgui_input_number(src, prompt, "Volume", volume_preferences[volume_key]*100)) / 100
if(volume_preferences[volume_key] > 1)
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index cda3da5e2e..568c36e2c5 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -410,10 +410,6 @@ GLOBAL_LIST_INIT(frozen_items, list(SQUAD_MARINE_1 = list(), SQUAD_MARINE_2 = li
var/willing = null //We don't want to allow people to be forced into despawning.
var/mob/living/M = G.grabbed_thing
- if(M.stat == DEAD) //This mob is dead
- to_chat(user, SPAN_WARNING("[src] immediately rejects [M]. \He passed away!"))
- return
-
if(isxeno(M))
to_chat(user, SPAN_WARNING("There is no way [src] will accept [M]!"))
return
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index e6daba29ae..f00ba138ef 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -244,7 +244,7 @@
), PROC_REF(turn_on))
wearer = user
RegisterSignal(user, COMSIG_MOB_STAT_SET_ALIVE, PROC_REF(update_minimap_icon))
- RegisterSignal(user, COMSIG_MOB_LOGIN, PROC_REF(add_hud_tracker))
+ RegisterSignal(user, COMSIG_MOB_LOGGED_IN, PROC_REF(add_hud_tracker))
RegisterSignal(user, COMSIG_MOB_DEATH, PROC_REF(update_minimap_icon))
RegisterSignal(user, COMSIG_HUMAN_SET_UNDEFIBBABLE, PROC_REF(update_minimap_icon))
if(headset_hud_on)
@@ -261,7 +261,7 @@
UnregisterSignal(user, list(
COMSIG_LIVING_REJUVENATED,
COMSIG_HUMAN_REVIVED,
- COMSIG_MOB_LOGIN,
+ COMSIG_MOB_LOGGED_IN,
COMSIG_MOB_DEATH,
COMSIG_HUMAN_SET_UNDEFIBBABLE,
COMSIG_MOB_STAT_SET_ALIVE
diff --git a/code/game/sound.dm b/code/game/sound.dm
index f2574975a7..6bee74ba01 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -1,3 +1,5 @@
+/sound
+ echo = SOUND_ECHO_REVERB_OFF //disable enviroment reverb by default, soundOutput re-enables for positional sounds
/datum/sound_template //Basically a sound datum, but only serves as a way to carry info to soundOutput
//copied sound datum vars
@@ -23,8 +25,6 @@
var/falloff = 1
///Changes the environmental reverb for all 3D sounds until another environment is specified. The default value (-1) specifies no change in environment. A numeric value from 0 to 25 specifies a set of reverb presets for the environment.
var/environment = -1
- ///If set to an 18-element list, this value customizes reverbration settings for this sound only.
- var/list/echo
//custom vars
///The category of this sound for client volume purposes: VOLUME_SFX (Sound effects), VOLUME_AMB (Ambience and Soundscapes) and VOLUME_ADM (Admin sounds and some other stuff)
@@ -59,16 +59,40 @@
* * channel - use this only when you want to force the sound to play on a specific channel
* * status - combined bit flags: SOUND_MUTE, SOUND_PAUSED, SOUND_STREAM, SOUND_UPDATE
* * falloff - max range till sound volume starts dropping as distance increases
- * * echo - customizes reverbration settings for this sound
* * y_s_offset - vertical sound position offset
* * x_s_offset - horizontal sound position offset
*
* Returns selected channel on success, FALSE on failure
*/
-/proc/playsound(atom/source, sound/soundin, vol = 100, vary = FALSE, sound_range, vol_cat = VOLUME_SFX, channel = 0, status, falloff = 1, list/echo, y_s_offset, x_s_offset)
+/proc/playsound(atom/source, sound/soundin, vol = 100, vary = FALSE, sound_range, vol_cat = VOLUME_SFX, channel = 0, status, falloff = 1, y_s_offset, x_s_offset)
if(isarea(source))
error("[source] is an area and is trying to make the sound: [soundin]")
return FALSE
+ var/datum/sound_template/S = new()
+
+ var/sound/SD = soundin
+ if(istype(SD))
+ S.file = SD.file
+ S.wait = SD.wait
+ S.repeat = SD.repeat
+ else
+ S.file = get_sfx(soundin)
+ S.channel = channel ? channel : get_free_channel()
+ S.status = status
+ S.falloff = falloff
+ S.volume = vol
+ S.volume_cat = vol_cat
+ S.y_s_offset = y_s_offset
+ S.x_s_offset = x_s_offset
+ if(vary != FALSE)
+ if(vary > 1)
+ S.frequency = vary
+ else
+ S.frequency = GET_RANDOM_FREQ // Same frequency for everybody
+
+ if(!sound_range)
+ sound_range = floor(0.25*vol) //if no specific range, the max range is equal to a quarter of the volume.
+ S.range = sound_range
var/turf/turf_source = get_turf(source)
if(!turf_source?.z)
@@ -100,7 +124,6 @@
template.frequency = GET_RANDOM_FREQ // Same frequency for everybody
template.status = status
template.falloff = falloff
- template.echo = echo
template.volume_cat = vol_cat
template.range = sound_range || floor(0.25 * vol) //if no specific range, the max range is equal to a quarter of the volume.
@@ -147,13 +170,12 @@
* * vol_cat - the category of this sound for client volume purposes: VOLUME_SFX (Sound effects), VOLUME_AMB (Ambience and Soundscapes), VOLUME_ADM (Admin sounds)
* * channel - use this only when you want to force the sound to play on a specific channel
* * status - combined bit flags: SOUND_MUTE, SOUND_PAUSED, SOUND_STREAM, SOUND_UPDATE
- * * echo - customizes reverbration settings for this sound
* * y_s_offset - vertical sound position offset
* * x_s_offset - horizontal sound position offset
*
* Returns FALSE on failure
*/
-/proc/playsound_client(client/C, sound/soundin, atom/origin, vol = 100, random_freq, vol_cat = VOLUME_SFX, channel = 0, status, list/echo, y_s_offset, x_s_offset)
+/proc/playsound_client(client/C, sound/soundin, atom/origin, vol = 100, random_freq, vol_cat = VOLUME_SFX, channel = 0, status, y_s_offset, x_s_offset)
if(!istype(C) || !C.soundOutput) return FALSE
var/datum/sound_template/template = new()
@@ -179,7 +201,6 @@
if(random_freq)
template.frequency = GET_RANDOM_FREQ
template.status = status
- template.echo = echo
template.volume_cat = vol_cat
var/turf/turf_origin = get_turf(origin)
@@ -206,7 +227,7 @@
*
* Returns FALSE on failure
*/
-/proc/playsound_area(area/A, sound/soundin, vol = 100, channel = 0, status, vol_cat = VOLUME_SFX, list/echo, y_s_offset, x_s_offset)
+/proc/playsound_area(area/A, sound/soundin, vol = 100, channel = 0, status, vol_cat = VOLUME_SFX, y_s_offset, x_s_offset)
if(!isarea(A))
return FALSE
@@ -259,13 +280,12 @@
* * soundin - sound datum ( sound() ), sound file ('mysound.ogg'), or string to get a SFX ("male_warcry")
* * volume - the initial volume of the sound, 0 is no sound at all, 75 is loud queen screech.
* * vol_cat - the category of this sound for client volume purposes: VOLUME_SFX (Sound effects), VOLUME_AMB (Ambience and Soundscapes), VOLUME_ADM (Admin sounds)
- * * echo - customizes reverbration settings for this sound
* * y_s_offset - vertical sound position offset
* * x_s_offset - horizontal sound position offset
*
* Returns selected channel on success, FALSE on failure
*/
-/proc/playsound_z(list/z, sound/soundin, volume = 100, vol_cat = VOLUME_SFX, echo, y_s_offset, x_s_offset)
+/proc/playsound_z(list/z, sound/soundin, volume = 100, vol_cat = VOLUME_SFX, y_s_offset, x_s_offset)
var/datum/sound_template/template = new()
if(istype(soundin))
@@ -286,7 +306,6 @@
template.channel = SOUND_CHANNEL_Z
template.volume = volume
- template.echo = echo
template.volume_cat = vol_cat
template.x_s_offset = x_s_offset
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 5caf0cba02..0da273dfc9 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -335,8 +335,10 @@ var/list/roundstart_mod_verbs = list(
add_verb(src, /client/proc/toggle_join_xeno)
add_verb(src, /client/proc/game_master_rename_platoon)
add_verb(src, /client/proc/toggle_vehicle_blockers)
+ add_verb(src, /client/proc/toggle_ai_xeno_weeding)
add_verb(src, /client/proc/toggle_rappel_menu)
add_verb(src, /client/proc/toggle_xeno_kidnapping)
+ add_verb(src, /client/proc/toggle_fire_support_menu)
if(CLIENT_HAS_RIGHTS(src, R_SERVER))
add_verb(src, admin_verbs_server)
if(CLIENT_HAS_RIGHTS(src, R_DEBUG))
@@ -372,8 +374,10 @@ var/list/roundstart_mod_verbs = list(
/client/proc/toggle_join_xeno,
/client/proc/game_master_rename_platoon,
/client/proc/toggle_vehicle_blockers,
+ /client/proc/toggle_ai_xeno_weeding,
/client/proc/toggle_rappel_menu,
/client/proc/toggle_xeno_kidnapping,
+ /client/proc/toggle_fire_support_menu,
admin_verbs_admin,
admin_verbs_ban,
admin_verbs_minor_event,
diff --git a/code/modules/admin/game_master/extra_buttons/fire_support_menu.dm b/code/modules/admin/game_master/extra_buttons/fire_support_menu.dm
new file mode 100644
index 0000000000..c3469071f3
--- /dev/null
+++ b/code/modules/admin/game_master/extra_buttons/fire_support_menu.dm
@@ -0,0 +1,291 @@
+#define FIRE_SUPPORT_CLICK_INTERCEPT_ACTION "fire_support_click_intercept_action"
+
+//Various ordnance selections
+#define ORDNANCE_OPTIONS list("Banshee Missile", "Harpoon Missile", "Keeper Missile", "Napalm Missile", "Thermobaric Missile", "Widowmaker Missile", "Laser", "Minirocket", "Incendiary Minirocket", "Sentry Drop", "GAU-21", "Heavy GAU-21", "High Explosive", "Incendiary", "Cluster", "High Explosive", "Incendiary", "Fragmentation", "Flare")
+#define MISSILE_ORDNANCE list("Banshee Missile", "Harpoon Missile", "Keeper Missile", "Napalm Missile", "Thermobaric Missile", "Widowmaker Missile")
+#define ORBITAL_ORDNANCE list("High Explosive OB", "Incendiary OB", "Cluster OB")
+#define MORTAR_ORDNANCE list("High Explosive Shell", "Incendiary Shell", "Fragmentation Shell", "Flare Shell")
+#define MISC_ORDNANCE list("Laser", "Minirocket", "Incendiary Minirocket", "Sentry Drop", "GAU-21", "Heavy GAU-21")
+
+/client/proc/toggle_fire_support_menu()
+ set name = "Fire Support Menu"
+ set category = "Game Master.Extras"
+ if(!check_rights(R_ADMIN))
+ return
+ new /datum/fire_support_menu(mob)
+
+///The actual menu datum
+/datum/fire_support_menu
+ var/fire_support_click_intercept = FALSE
+ var/selected_ordnance = "Banshee Missile"
+ var/sound_cooldown = FALSE
+ ///Mortar to fire the abstract shells.
+ var/obj/structure/mortar/abstract_mortar = new()
+ var/client/holder
+
+/datum/fire_support_menu/New(user)
+ if(isclient(user))
+ holder = user
+ else
+ var/mob/mob = user
+ holder = mob.client
+
+ holder.click_intercept = src
+ tgui_interact(holder.mob)
+
+///Deletes the mortar when the menu is closed so we dont make a thousand of them.
+/datum/fire_support_menu/Destroy(force, ...)
+ QDEL_NULL(abstract_mortar)
+ holder = null
+ return ..()
+
+/datum/fire_support_menu/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "GameMasterFireSupportMenu", "Fire Support Menu")
+ ui.open()
+
+///Input all the options for the ordnance panel. Only fires once, as the available ammo types shouldnt change. And if they do, something's very wrong
+/datum/fire_support_menu/ui_static_data(mob/user)
+ . = ..()
+ var/list/data = list()
+
+ data["ordnance_options"] = ORDNANCE_OPTIONS
+
+ data["missile_ordnance_options"] = MISSILE_ORDNANCE
+ data["orbital_ordnance_options"] = ORBITAL_ORDNANCE
+ data["mortar_ordnance_options"] = MORTAR_ORDNANCE
+ data["misc_ordnance_options"] = MISC_ORDNANCE
+
+ return data
+
+//Input all the dynamic data, the selected ordnance, and whether it's armed or not.
+/datum/fire_support_menu/ui_data(mob/user)
+ . = ..()
+ var/list/data = list()
+
+ data["selected_ordnance"] = selected_ordnance
+ data["fire_support_click_intercept"] = fire_support_click_intercept
+
+ return data
+
+/datum/fire_support_menu/ui_act(action, params)
+ . = ..()
+ switch(action)
+ if("toggle_click_fire_support")
+ fire_support_click_intercept = !fire_support_click_intercept
+ return
+ if("set_selected_ordnance")
+ selected_ordnance = params["ordnance"]
+ return
+
+/datum/fire_support_menu/ui_status(mob/user, datum/ui_state/state)
+ return UI_INTERACTIVE
+
+/datum/fire_support_menu/ui_close(mob/user)
+ var/client/user_client = user.client
+ if(user_client?.click_intercept == src)
+ user_client.click_intercept = null
+
+ fire_support_click_intercept = FALSE
+ qdel(src)
+
+///Handles firing logic whenever the mouse is clicked, and the fire_support_click_intercept var is TRUE
+/datum/fire_support_menu/proc/InterceptClickOn(mob/user, params, atom/object)
+
+ var/turf/target_turf = get_turf(object)
+ if(fire_support_click_intercept)
+ switch(selected_ordnance)
+ //PREMADE ORDNANCE
+
+ //DS missiles
+ if("Banshee Missile")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/rocket/banshee/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ if("Harpoon Missile")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/rocket/harpoon/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ if("Keeper Missile")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/rocket/keeper/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ if("Napalm Missile")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/rocket/napalm/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ if("Thermobaric Missile")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/rocket/thermobaric/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ if("Widowmaker Missile")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/rocket/widowmaker/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ //Misc DS ammo
+ if("Laser")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/laser_battery/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ if("Minirocket")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/minirocket/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ if("Incendiary Minirocket")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/minirocket/incendiary/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ if("Sentry Drop")
+ if(istype(target_turf, /turf/closed))
+ to_chat(user, SPAN_WARNING("The selected drop site is a sheer wall!"))
+ return FALSE
+ else
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/sentry/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ if("GAU-21")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/heavygun/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ if("Heavy GAU-21")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/structure/ship_ammo/heavygun/antitank/ammo = new()
+
+ handle_dropship_ordnance(target_turf, ammo)
+
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ //Orbital Bombardments
+ if("High Explosive OB")
+ var/obj/structure/ob_ammo/warhead/explosive/ammo = new()
+
+ handle_orbital_ordnance(target_turf, ammo)
+ return TRUE
+
+ if("Incendiary OB")
+ var/obj/structure/ob_ammo/warhead/incendiary/ammo = new()
+
+ handle_orbital_ordnance(target_turf, ammo)
+ return TRUE
+
+ if("Cluster OB")
+ var/obj/structure/ob_ammo/warhead/cluster/ammo = new()
+
+ handle_orbital_ordnance(target_turf, ammo)
+ return TRUE
+
+ //Mortar Shelling
+ if("High Explosive Shell")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/item/mortar_shell/he/ammo = new()
+
+ abstract_mortar.handle_shell(target_turf, ammo)
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+
+ if("Incendiary Shell")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/item/mortar_shell/incendiary/ammo = new()
+
+ abstract_mortar.handle_shell(target_turf, ammo)
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+
+ if("Fragmentation Shell")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/item/mortar_shell/frag/ammo = new()
+
+ abstract_mortar.handle_shell(target_turf, ammo)
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ if("Flare Shell")
+ var/obj/effect/overlay/temp/blinking_laser/target_lase = new(target_turf)
+ var/obj/item/mortar_shell/flare/ammo = new()
+
+ abstract_mortar.handle_shell(target_turf, ammo)
+ QDEL_IN(target_lase, 5 SECONDS) //to stop "unused var" warnings
+ return TRUE
+
+ else
+ to_chat(user, SPAN_ANNOUNCEMENT_HEADER_ADMIN("Invalid ordnance selection! If this appears, yell at a coder!"))
+ return TRUE
+
+///Handles the dropship swooping sound effect, and makes sure it doesnt play 20 times a second.
+/datum/fire_support_menu/proc/handle_dropship_sound(target_turf)
+ if(!sound_cooldown)
+ playsound(target_turf, 'sound/weapons/dropship_sonic_boom.ogg', 100, 1, 60)
+ sound_cooldown = TRUE
+ addtimer(VARSET_CALLBACK(src, sound_cooldown, FALSE), 10 SECONDS)
+
+///Handles the noises and actual detonation of dropship ammo. Mainly it doesnt play the warning sound for ammo of the ship_ammo/heavygun/ type.
+/datum/fire_support_menu/proc/handle_dropship_ordnance(turf/target_turf, obj/structure/ship_ammo/ammo)
+ addtimer(CALLBACK(src, PROC_REF(handle_dropship_sound), target_turf), 0.5 SECONDS)
+ if(!istype(ammo, /obj/structure/ship_ammo/heavygun/))
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), target_turf, ammo.warning_sound, ammo.warning_sound_volume, 1, 15), 1.5 SECONDS)
+ addtimer(CALLBACK(ammo, TYPE_PROC_REF(/obj/structure/ship_ammo, detonate_on), target_turf), 2.5 SECONDS)
+ QDEL_IN(ammo, 5 SECONDS)
+
+/datum/fire_support_menu/proc/handle_orbital_ordnance(turf/target_turf, obj/structure/ob_ammo/warhead/ammo)
+ ammo.warhead_impact(target_turf)
+
+#undef ORDNANCE_OPTIONS
+#undef ORBITAL_ORDNANCE
+#undef MORTAR_ORDNANCE
+#undef MISC_ORDNANCE
+#undef FIRE_SUPPORT_CLICK_INTERCEPT_ACTION
diff --git a/code/modules/admin/game_master/extra_buttons/toggle_ai_xeno_weeding.dm b/code/modules/admin/game_master/extra_buttons/toggle_ai_xeno_weeding.dm
new file mode 100644
index 0000000000..6cf536564a
--- /dev/null
+++ b/code/modules/admin/game_master/extra_buttons/toggle_ai_xeno_weeding.dm
@@ -0,0 +1,10 @@
+/// For PvE CM a convenient button to enable/disable AI xenos weeding.
+/client/proc/toggle_ai_xeno_weeding()
+ set name = "Toggle AI Xeno Weeding"
+ set category = "Game Master.Flags"
+
+ if(!admin_holder || !check_rights(R_MOD, FALSE))
+ return
+
+ GLOB.ai_xeno_weeding = !GLOB.ai_xeno_weeding
+ message_admins("[src] has [GLOB.ai_xeno_weeding ? "enabled" : "disabled"] AI xeno weeding.")
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index d69833ec5c..8456b92463 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -464,7 +464,7 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
view = world_view_size
- SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CLIENT_LOGIN, src)
+ SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CLIENT_LOGGED_IN, src)
//////////////
//DISCONNECT//
@@ -820,3 +820,32 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
xeno_prefix = "XX"
if(!xeno_postfix || xeno_name_ban)
xeno_postfix = ""
+
+/client/verb/action_hide_menu()
+ set name = "Show/Hide Actions"
+ set category = "IC"
+
+ var/mob/user = usr
+
+ var/list/actions_list = list()
+ for(var/datum/action/action as anything in user.actions)
+ var/action_name = action.name
+ if(action.player_hidden)
+ action_name += " (Hidden)"
+ actions_list[action_name] += action
+
+ if(!LAZYLEN(actions_list))
+ to_chat(user, SPAN_WARNING("You have no actions available."))
+ return
+
+ var/selected_action_name = tgui_input_list(user, "Show or hide selected action", "Show/Hide Actions", actions_list, 30 SECONDS)
+ if(!selected_action_name)
+ to_chat(user, SPAN_WARNING("You did not select an action."))
+ return
+
+ var/datum/action/selected_action = actions_list[selected_action_name]
+ selected_action.player_hidden = !selected_action.player_hidden
+ user.update_action_buttons()
+
+ if(!selected_action.player_hidden && selected_action.hidden) //Inform the player that even if they are unhiding it, itll still not be visible
+ to_chat(user, SPAN_NOTICE("[selected_action] is forcefully hidden, bypassing player unhiding."))
diff --git a/code/modules/clothing/suits/marine_armor.dm b/code/modules/clothing/suits/marine_armor.dm
index d043bba007..6bdb55e7d7 100644
--- a/code/modules/clothing/suits/marine_armor.dm
+++ b/code/modules/clothing/suits/marine_armor.dm
@@ -468,6 +468,13 @@
/obj/item/storage/backpack/general_belt,
/obj/item/device/motiondetector,
/obj/item/device/walkman,
+ /obj/item/storage/large_holster/machete,
+ /obj/item/storage/belt/gun/type47,
+ )
+
+ smartgun_back = list(
+ /obj/item/storage/large_holster/machete,
+ /obj/item/ammo_box,
)
diff --git a/code/modules/cm_marines/smartgun_mount.dm b/code/modules/cm_marines/smartgun_mount.dm
index 32983f455c..1fb9868ed0 100644
--- a/code/modules/cm_marines/smartgun_mount.dm
+++ b/code/modules/cm_marines/smartgun_mount.dm
@@ -471,7 +471,7 @@
GUN_FIREMODE_AUTOMATIC,
)
/// A multiplier for how slow this gun should fire in automatic as opposed to burst. 1 is normal, 1.2 is 20% slower, 0.8 is 20% faster, etc.
- var/autofire_slow_mult = 1
+ var/autofire_slow_mult = 0.7
/// If the gun is currently burst firing
VAR_PROTECTED/burst_firing = FALSE
/// If the gun should display its ammo count
diff --git a/code/modules/cm_tech/implements/railgun.dm b/code/modules/cm_tech/implements/railgun.dm
index ebff2ac57e..f627894bcf 100644
--- a/code/modules/cm_tech/implements/railgun.dm
+++ b/code/modules/cm_tech/implements/railgun.dm
@@ -6,7 +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."
+ desc = "A computer with an orange interface, it's idly blinking, awaiting a password. The higher your altitude, the faster your reload, and slower the shots hit."
/obj/effect/landmark/railgun_computer/Initialize(mapload, ...)
. = ..()
@@ -26,6 +26,7 @@ GLOBAL_DATUM(railgun_eye_location, /datum/coords)
/obj/structure/machinery/computer/railgun
name = "railgun computer"
+ desc = "A computer with an orange interface, fires the standard railgun shells. The higher your altitude, the faster your reload, and slower the shots hit."
icon_state = "terminal"
@@ -36,13 +37,15 @@ GLOBAL_DATUM(railgun_eye_location, /datum/coords)
var/max_ammo = 10
var/ammo = 10
- var/ammo_recharge_time = 30 SECONDS
+ var/ammo_recharge_time = 30 SECONDS //How long it takes to get a new shot to your ammo counter
+ var/ammo_delay = 10 SECONDS //How long it takes to hit the earth
- var/fire_cooldown = 1.5 SECONDS
+ var/fire_cooldown = 1.5 SECONDS //Cooldown between shots
var/next_fire = 0
var/power = 900
var/range = 2
+ var/warning_color = "#0000ff"
/// Computer and Railgun can only be used if this variable is cleared
var/locked = TRUE
@@ -102,13 +105,13 @@ GLOBAL_DATUM(railgun_eye_location, /datum/coords)
return FALSE
if(locked)
- to_chat(H, SPAN_WARNING("Railgun Safeties are on, unable to fire!"))
+ to_chat(H, SPAN_WARNING("Safeties are on, unable to fire!"))
return FALSE
if(istype(T, /turf/open/space)) // No firing into space
return FALSE
- if(protected_by_pylon(TURF_PROTECTION_OB, T))
+ if(protected_by_pylon(TURF_PROTECTION_MORTAR, T))
to_chat(H, SPAN_WARNING("[icon2html(src)] This area is too reinforced to fire into."))
return FALSE
@@ -149,12 +152,13 @@ GLOBAL_DATUM(railgun_eye_location, /datum/coords)
next_fire = world.time + fire_cooldown
- addtimer(CALLBACK(src, PROC_REF(recharge_ammo)), ammo_recharge_time, TIMER_UNIQUE)
+ addtimer(CALLBACK(src, PROC_REF(recharge_ammo)), ammo_recharge_time*2-GLOB.ship_alt, TIMER_UNIQUE)
ammo--
to_chat(H, SPAN_NOTICE("[icon2html(src)] Firing shell. [SPAN_BOLD("([ammo]/[max_ammo] shells left).")]"))
var/obj/effect/warning/railgun/warning_zone = new(T)
+ warning_zone.color = warning_color
var/image/I = image(warning_zone.icon, warning_zone.loc, warning_zone.icon_state, warning_zone.layer)
I.color = warning_zone.color
@@ -162,7 +166,7 @@ GLOBAL_DATUM(railgun_eye_location, /datum/coords)
H.client.images += I
playsound_client(H.client, 'sound/machines/railgun/railgun_shoot.ogg')
- addtimer(CALLBACK(src, PROC_REF(land_shot), T, H.client, warning_zone, I), 10 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(land_shot), T, H.client, warning_zone, I), ammo_delay*GLOB.ship_alt)
/obj/structure/machinery/computer/railgun/proc/land_shot(turf/T, client/firer, obj/effect/warning/droppod/warning_zone, image/to_remove)
if(warning_zone)
@@ -269,3 +273,47 @@ GLOBAL_DATUM(railgun_eye_location, /datum/coords)
return COMPONENT_TURF_ALLOW_MOVEMENT
+
+
+/obj/structure/machinery/computer/railgun/gatling
+ name = "orbital gatling computer"
+ desc = "The younger sister to the railgun, this one is way weaker, however, it fires significantly faster. The higher your altitude, the faster your reload, and slower the shots hit."
+ max_ammo = 100
+ ammo = 100
+ ammo_recharge_time = 1 SECONDS
+ fire_cooldown = 0.1 SECONDS
+ ammo_delay = 3 SECONDS
+ power = 50
+ range = 1
+ warning_color = "#00ff00"
+
+
+/obj/structure/machinery/computer/railgun/orbital
+ name = "orbital computer"
+ desc = "An Orbital cannon with a very long recharge time. The higher your altitude, the faster your reload, and slower the shots hit."
+ max_ammo = 1
+ ammo = 1
+ ammo_recharge_time = 1 SECONDS
+ fire_cooldown = 10 MINUTES //So you know how long it takes betweenS
+ ammo_delay = 30 SECONDS
+ power = 1500
+ range = 15
+ warning_color = "#ff0000"
+
+/obj/structure/machinery/computer/railgun/napalm
+ name = "orbital napalm computer"
+ desc = "An Orbital cannon with a very long recharge time. The higher your altitude, the faster your reload, and slower the shots hit."
+ max_ammo = 5
+ ammo = 5
+ ammo_recharge_time = 45 SECONDS
+ ammo_delay = 5 SECONDS
+ warning_color = "#ff9100"
+
+/obj/structure/machinery/computer/railgun/napalm/land_shot(turf/T, client/firer, obj/effect/warning/droppod/warning_zone, image/to_remove)
+ if(warning_zone)
+ qdel(warning_zone)
+
+ if(firer)
+ firer.images -= to_remove
+ playsound(T, 'sound/machines/railgun/railgun_impact.ogg', sound_range = 75)
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flame_radius), create_cause_data("railgun", firer.mob), 5, T, BURN_TIME_TIER_5 + 5, BURN_LEVEL_TIER_2, FLAMESHAPE_DEFAULT, FIRE_VARIANT_TYPE_B)
diff --git a/code/modules/defenses/sentry_computer.dm b/code/modules/defenses/sentry_computer.dm
index bb6f55d72b..7cb4f7bb84 100644
--- a/code/modules/defenses/sentry_computer.dm
+++ b/code/modules/defenses/sentry_computer.dm
@@ -48,41 +48,18 @@
// Stuff needed to render the map
/// asset name for the game map
- var/map_name
-
- /// camera screen which renders the world
- var/atom/movable/screen/map_view/cam_screen
-
- /// camera screen which shows a blank error
- var/atom/movable/screen/background/cam_background
-
- var/list/cam_plane_masters
+ var/camera_map_name
/obj/item/device/sentry_computer/Initialize(mapload)
. = ..()
if(cell_type)
cell = new cell_type()
cell.charge = cell.maxcharge
- // set up cameras
- map_name = "sentry_computer_[REF(src)]_map"
- cam_screen = new
- cam_screen.name = "screen"
- cam_screen.assigned_map = map_name
- cam_screen.del_on_map_removal = FALSE
- cam_screen.screen_loc = "[map_name]:1,1"
- cam_background = new
- cam_background.assigned_map = map_name
- cam_background.del_on_map_removal = FALSE
-
- cam_plane_masters = list()
- for(var/plane in subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/blackness)
- var/atom/movable/screen/plane_master/instance = new plane()
- instance.assigned_map = map_name
- instance.del_on_map_removal = FALSE
- if(instance.blend_mode_override)
- instance.blend_mode = instance.blend_mode_override
- instance.screen_loc = "[map_name]:CENTER"
- cam_plane_masters += instance
+
+ RegisterSignal(src, COMSIG_CAMERA_MAPNAME_ASSIGNED, PROC_REF(camera_mapname_update))
+
+ AddComponent(/datum/component/camera_manager)
+ SEND_SIGNAL(src, COMSIG_CAMERA_CLEAR)
faction_group = FACTION_LIST_MARINE
transceiver.forceMove(src)
@@ -94,16 +71,20 @@
/obj/item/device/sentry_computer/Destroy()
. = ..()
+ UnregisterSignal(src, COMSIG_CAMERA_MAPNAME_ASSIGNED)
QDEL_NULL(cell)
- QDEL_NULL(cam_background)
- QDEL_NULL(cam_screen)
QDEL_NULL(transceiver)
QDEL_NULL(voice)
last_camera_turf = null
current = null
registered_tools = null
+ for(var/obj/structure/machinery/defenses/sentry/sentrygun as anything in paired_sentry)
+ unpair_sentry(sentrygun)
paired_sentry = null
+/obj/item/device/sentry_computer/proc/camera_mapname_update(source, value)
+ camera_map_name = value
+
/obj/item/device/sentry_computer/Move(NewLoc, direct)
..()
if(table_setup || open || on)
@@ -232,7 +213,7 @@
for(var/key_id in tool.encryption_keys)
var/datum/weakref/ref = tool.encryption_keys[key_id]
var/obj/item/device/sentry_computer/key_object = ref.resolve()
- key_object.registered_tools -= id
+ key_object?.registered_tools -= id
tool.encryption_keys = list()
to_chat(user, SPAN_NOTICE("Existing encryption keys cleared."))
to_chat(usr, SPAN_NOTICE("You begin encryption key to \the [tool]."))
@@ -321,7 +302,7 @@
if(current == target)
current = null
- update_active_camera()
+ SEND_SIGNAL(src, COMSIG_CAMERA_CLEAR)
/obj/item/device/sentry_computer/ui_status(mob/user, datum/ui_state/state)
. = ..()
@@ -332,19 +313,16 @@
/obj/item/device/sentry_computer/ui_close(mob/user)
-
- // Unregister map objects
- user.client.clear_map(map_name)
+ SEND_SIGNAL(src, COMSIG_CAMERA_UNREGISTER_UI, user)
/obj/item/device/sentry_computer/ui_static_data(mob/user)
. = list()
.["sentry_static"] = list()
- .["mapRef"] = map_name
+ .["mapRef"] = camera_map_name
var/index = 1
- for(var/sentry in paired_sentry)
+ for(var/obj/structure/machinery/defenses/sentry/sentrygun as anything in paired_sentry)
var/list/sentry_holder = list()
- var/obj/structure/machinery/defenses/sentry/sentrygun = sentry
sentry_holder["selection_menu"] = list()
sentry_holder["index"] = index
sentry_holder["name"] = sentrygun.name
@@ -396,13 +374,8 @@
/obj/item/device/sentry_computer/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
- update_active_camera()
if (!ui)
- // Register map objects
- user.client.register_map_obj(cam_background)
- user.client.register_map_obj(cam_screen)
- for(var/plane in cam_plane_masters)
- user.client.register_map_obj(plane)
+ SEND_SIGNAL(src, COMSIG_CAMERA_REGISTER_UI, user)
ui = new(user, src, "SentryGunUI", name)
ui.open()
@@ -426,7 +399,17 @@
if("set-camera")
current = sentry
playsound(src, get_sfx("terminal_button"), 25, FALSE)
- update_active_camera()
+ var/obj/structure/machinery/defenses/sentry/defense = sentry
+ if (defense.has_camera)
+ defense.set_watched_turfs()
+ var/list/turf/visible_turfs = defense.watching_turfs
+ var/list/bbox = get_bbox_of_atoms(visible_turfs)
+ var/center_x = (bbox[3] + bbox[1]) * 0.5
+ var/center_y = (bbox[4] + bbox[2]) * 0.5
+ var/size_x = bbox[3] - bbox[1] + 1
+ var/size_y = bbox[4] - bbox[2] + 1
+ SEND_SIGNAL(src, COMSIG_CAMERA_SET_AREA, center_x, center_y, defense.loc.z, size_x, size_y)
+
return TRUE
if("ping")
playsound(sentry, 'sound/machines/twobeep.ogg', 50, 1)
@@ -439,54 +422,8 @@
if("clear-camera")
current = null
playsound(src, get_sfx("terminal_button"), 25, FALSE)
- update_active_camera()
+ SEND_SIGNAL(src, COMSIG_CAMERA_CLEAR)
return TRUE
if("ui-interact")
playsound(src, get_sfx("terminal_button"), 25, FALSE)
return FALSE
-
-/**
- * Set the displayed camera to the static not-connected.
- */
-/obj/item/device/sentry_computer/proc/show_camera_static()
- cam_screen.vis_contents.Cut()
- last_camera_turf = null
- cam_background.icon_state = "scanline2"
- cam_background.fill_rect(1, 1, 15, 15)
-
-/**
- * Update camera settings and redraw camera on the current variable.
- */
-/obj/item/device/sentry_computer/proc/update_active_camera()
- // Show static if can't use the camera
- if(isnull(current) || !current.has_camera || current.placed != 1)
- show_camera_static()
- return
-
- // Is this camera located in or attached to a living thing, Vehicle or helmet? If so, assume the camera's loc is the living (or non) thing.
- var/cam_location = current
- if(isliving(current.loc) || isVehicle(current.loc))
- cam_location = current.loc
- else if(istype(current.loc, /obj/item/clothing/head/helmet/marine))
- var/obj/item/clothing/head/helmet/marine/helmet = current.loc
- cam_location = helmet.loc
- // If we're not forcing an update for some reason and the cameras are in the same location,
- // we don't need to update anything.
- // Most security cameras will end here as they're not moving.
- if(last_camera_turf == get_turf(cam_location))
- return
-
- // Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs.
- last_camera_turf = get_turf(cam_location)
-
- current.set_watched_turfs()
- var/list/turf/visible_turfs = current.watching_turfs
-
- var/list/bbox = get_bbox_of_atoms(visible_turfs)
- var/size_x = bbox[3] - bbox[1] + 1
- var/size_y = bbox[4] - bbox[2] + 1
- cam_screen.icon = null
- cam_screen.icon_state = "clear"
- cam_screen.vis_contents = visible_turfs
- cam_background.icon_state = "clear"
- cam_background.fill_rect(1, 1, size_x, size_y)
diff --git a/code/modules/escape_menu/escape_menu.dm b/code/modules/escape_menu/escape_menu.dm
index c31234678b..b61bbd5b3f 100644
--- a/code/modules/escape_menu/escape_menu.dm
+++ b/code/modules/escape_menu/escape_menu.dm
@@ -49,7 +49,7 @@ GLOBAL_LIST_EMPTY(escape_menus)
show_page()
RegisterSignal(client, COMSIG_PARENT_QDELETING, PROC_REF(on_client_qdel))
- RegisterSignal(client, COMSIG_CLIENT_MOB_LOGIN, PROC_REF(on_client_mob_login))
+ RegisterSignal(client, COMSIG_CLIENT_MOB_LOGGED_IN, PROC_REF(on_client_mob_login))
if (!isnull(ckey))
GLOB.escape_menus[ckey] = src
diff --git a/code/modules/gear_presets/clf.dm b/code/modules/gear_presets/clf.dm
index 9afc89ab65..91a44328e4 100644
--- a/code/modules/gear_presets/clf.dm
+++ b/code/modules/gear_presets/clf.dm
@@ -529,10 +529,10 @@
list("GUNNER KITS (CHOOSE 1)", 0, null, null, null),
list("SVD Sniper Kit", 0, /obj/effect/essentials_set/kit/svd, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_REGULAR),
list("Custom Built Shotgun Kit", 0, /obj/effect/essentials_set/kit/custom_shotgun, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_REGULAR),
- list("M60 Machine Gun Kit", 0, /obj/effect/essentials_set/kit/m60, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_REGULAR),
+ list("Mk70 Machine Gun Kit", 0, /obj/effect/essentials_set/kit/m60, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_REGULAR),
list("SPECIAL AMMUNITION", 0, null, null, null),
- list("M60 Ammo Box (7.62x51mm)", 15, /obj/item/ammo_magazine/m60, null, VENDOR_ITEM_REGULAR),
+ list("Mk70 Ammo Box (7.62x51mm)", 15, /obj/item/ammo_magazine/m60, null, VENDOR_ITEM_REGULAR),
list("Shotgun Incendiary Shells (Handful)", 15, /obj/item/ammo_magazine/handful/shotgun/incendiary, null, VENDOR_ITEM_REGULAR),
list("SVD Magazine (7.62x54mmR)", 15, /obj/item/ammo_magazine/sniper/svd, null, VENDOR_ITEM_REGULAR),
diff --git a/code/modules/gear_presets/uscm.dm b/code/modules/gear_presets/uscm.dm
index 5cc00b0f42..82ba128bcd 100644
--- a/code/modules/gear_presets/uscm.dm
+++ b/code/modules/gear_presets/uscm.dm
@@ -61,6 +61,38 @@
var/obj/item/device/radio/headset/almayer/marine/equipped_headset = new_human.wear_r_ear
equipped_headset.add_hud_tracker(new_human)
+/datum/equipment_preset/uscm/proc/spawn_marine_fluff_items(mob/living/carbon/human/new_human)
+ var/obj/item/helmet_accessory = pick(GLOB.allowed_helmet_items)
+ new_human.equip_to_slot_or_del(new helmet_accessory, WEAR_IN_HELMET)
+ if(prob(50))
+ var/obj/item/helmet_accessory_two = pick(GLOB.allowed_helmet_items)
+ new_human.equip_to_slot_or_del(new helmet_accessory_two, WEAR_IN_HELMET)
+ var/list/possible_masks = list(/obj/item/clothing/mask/gas) + subtypesof(/obj/item/clothing/mask/rebreather) + subtypesof(/obj/item/clothing/mask/tornscarf)
+ if(prob(50))
+ var/obj/item/clothing/mask/new_mask = pick(possible_masks)
+ new_human.equip_to_slot_or_del(new new_mask, WEAR_FACE)
+
+ var/list/possible_glasses = list(/obj/item/clothing/glasses/regular, /obj/item/clothing/glasses/regular/hipster, /obj/item/clothing/glasses/sunglasses, /obj/item/clothing/glasses/sunglasses/aviator, /obj/item/clothing/glasses/sunglasses/big) + subtypesof(/obj/item/clothing/glasses/mgoggles)
+ if(prob(50))
+ var/obj/item/clothing/mask/new_glasses = pick(possible_glasses)
+ new_human.equip_to_slot_or_del(new new_glasses, WEAR_EYES)
+
+/datum/equipment_preset/uscm/proc/spawn_marine_armor(mob/living/carbon/human/new_human)
+ if(prob(66))
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/medium, WEAR_JACKET)
+ else if(prob(50))
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/light, WEAR_JACKET)
+ else
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/heavy, WEAR_JACKET)
+
+/datum/equipment_preset/uscm/proc/spawn_marine_backpack(mob/living/carbon/human/new_human)
+ if(prob(75))
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/satchel, WEAR_BACK)
+ else
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine, WEAR_BACK)
+
+/datum/equipment_preset/uscm/proc/spawn_marine_sidearm(mob/living/carbon/human/new_human)
+ return
//*****************************************************************************************************/
/datum/equipment_preset/uscm/pfc
@@ -83,22 +115,6 @@
new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK)
-/datum/equipment_preset/uscm/pfc/cryo
- name = "USCM Cryo Squad Rifleman"
- auto_squad_name = SQUAD_MARINE_CRYO
-
-/datum/equipment_preset/uscm/pfc/cryo/load_gear(mob/living/carbon/human/new_human)
- ..()
- new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo(new_human), WEAR_L_EAR)
-
-/datum/equipment_preset/uscm/pfc/cryo
- name = "USCM Cryo Squad Rifleman"
- auto_squad_name = SQUAD_MARINE_CRYO
-
-/datum/equipment_preset/uscm/pfc/cryo/load_gear(mob/living/carbon/human/new_human)
- ..()
- new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo(new_human), WEAR_L_EAR)
-
/datum/equipment_preset/uscm/pfc/lesser_rank
paygrade = "ME1"
@@ -158,13 +174,6 @@
new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK)
-/datum/equipment_preset/uscm/sg/cryo
- name = "USCM Cryo Squad Smartgunner"
- auto_squad_name = SQUAD_MARINE_CRYO
-
-/datum/equipment_preset/uscm/sg/cryo/load_gear(mob/living/carbon/human/new_human)
- ..()
- new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo(new_human), WEAR_L_EAR)
/datum/equipment_preset/uscm/sg/lesser_rank
paygrade = "ME3"
@@ -433,14 +442,6 @@
new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK)
-/datum/equipment_preset/uscm/medic/cryo
- name = "USCM Cryo Squad Hospital Corpsman"
- auto_squad_name = SQUAD_MARINE_CRYO
-
-/datum/equipment_preset/uscm/medic/cryo/load_gear(mob/living/carbon/human/new_human)
- ..()
- new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo/med(new_human), WEAR_L_EAR)
-
/datum/equipment_preset/uscm/medic/lesser_rank
paygrade = "ME3"
@@ -500,14 +501,6 @@
new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK)
-/datum/equipment_preset/uscm/tl/cryo
- name = "USCM Cryo Squad Sergeant"
- auto_squad_name = SQUAD_MARINE_CRYO
-
-/datum/equipment_preset/uscm/tl/cryo/load_gear(mob/living/carbon/human/new_human)
- ..()
- new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo/tl(new_human), WEAR_L_EAR)
-
/datum/equipment_preset/uscm/tl/upp
name = "UPP Squad Sergeant"
paygrade = "UE5"
@@ -591,14 +584,6 @@
new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK)
-/datum/equipment_preset/uscm/leader/cryo
- name = "USCM Cryo Platoon Sergeant"
- auto_squad_name = SQUAD_MARINE_CRYO
-
-/datum/equipment_preset/uscm/leader/cryo/load_gear(mob/living/carbon/human/new_human)
- ..()
- new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo/lead(new_human), WEAR_L_EAR)
-
/datum/equipment_preset/uscm/leader/lesser_rank
paygrade = "ME6"
@@ -680,10 +665,26 @@
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BACK)
-/datum/equipment_preset/uscm/private_equipped/cryo
- name = "USCM Cryo Squad Rifleman (Equipped)"
- auto_squad_name = SQUAD_MARINE_CRYO
+/datum/equipment_preset/uscm/private_equipped/random
+ name = "USCM Squad Rifleman (Equipped Random)"
+/datum/equipment_preset/uscm/private_equipped/random/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine(new_human), WEAR_BODY)
+ spawn_marine_armor(new_human)
+ spawn_marine_backpack(new_human)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/flare/full(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine(new_human), WEAR_HANDS)
+
+ 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/storage/box/MRE(new_human), WEAR_IN_BACK)
+
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/rifle/m41aMK1(new_human), WEAR_J_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/marine/m41amk1(new_human), WEAR_WAIST)
+ spawn_marine_fluff_items(new_human)
//*****************************************************************************************************/
@@ -723,9 +724,26 @@
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BACK)
-/datum/equipment_preset/uscm/leader_equipped/cryo
- name = "USCM Cryo Platoon Sergeant (Equipped)"
- auto_squad_name = SQUAD_MARINE_CRYO
+/datum/equipment_preset/uscm/leader_equipped/random
+ name = "USCM Platoon Sergeant (Equipped Random)"
+
+/datum/equipment_preset/uscm/leader_equipped/random/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine(new_human), WEAR_BODY)
+ spawn_marine_armor(new_human)
+ spawn_marine_backpack(new_human)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine/leader(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo/lead(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/device/binoculars/range(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/flare/full(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine(new_human), WEAR_HANDS)
+
+ 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/storage/box/MRE(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/rifle/m41aMK1(new_human), WEAR_J_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/marine/m41amk1(new_human), WEAR_WAIST)
+ spawn_marine_fluff_items(new_human)
//*****************************************************************************************************/
@@ -761,10 +779,58 @@
new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo(new_human), WEAR_L_EAR)
-/datum/equipment_preset/uscm/smartgunner_equipped/cryo
- name = "USCM Cryo Squad Smartgunner (Equipped)"
- auto_squad_name = SQUAD_MARINE_CRYO
+/datum/equipment_preset/uscm/smartgunner_equipped/random
+ name = "USCM Squad Smartgunner (Equipped Random)"
+/datum/equipment_preset/uscm/smartgunner_equipped/random/spawn_marine_fluff_items(mob/living/carbon/human/new_human)
+ var/obj/item/helmet_accessory = pick(GLOB.allowed_helmet_items)
+ new_human.equip_to_slot_or_del(new helmet_accessory, WEAR_IN_HELMET)
+ if(prob(50))
+ var/obj/item/helmet_accessory_two = pick(GLOB.allowed_helmet_items)
+ new_human.equip_to_slot_or_del(new helmet_accessory_two, WEAR_IN_HELMET)
+ var/list/possible_masks = list(/obj/item/clothing/mask/gas) + subtypesof(/obj/item/clothing/mask/rebreather) + subtypesof(/obj/item/clothing/mask/tornscarf)
+ if(prob(50))
+ var/obj/item/clothing/mask/new_mask = pick(possible_masks)
+ new_human.equip_to_slot_or_del(new new_mask, WEAR_FACE)
+
+/datum/equipment_preset/uscm/smartgunner_equipped/random/spawn_marine_sidearm(mob/living/carbon/human/new_human)
+ var/sidearm = pick("m4a3", "mod88", "vp78", "m44")
+ switch(sidearm)
+ if("m4a3")
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/pistol/m4a3(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol(new_human), WEAR_IN_ACCESSORY)
+ if("mod88")
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/pistol/mod88(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/mod88(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/mod88(new_human), WEAR_IN_ACCESSORY)
+ if("vp78")
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/pistol/vp78(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/vp78(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/vp78(new_human), WEAR_IN_ACCESSORY)
+ if("m44")
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/revolver/m44(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/revolver(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/revolver(new_human), WEAR_IN_ACCESSORY)
+
+/datum/equipment_preset/uscm/smartgunner_equipped/random/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/storage/holster(new_human), WEAR_ACCESSORY)
+ spawn_marine_sidearm(new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/smartgunner(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/smartgun(new_human), WEAR_J_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/flare/full(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/general_belt(new_human), WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smartgun(new_human), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smartgun(new_human), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smartgun(new_human), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine(new_human), WEAR_HANDS)
+ 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/glasses/night/m56_goggles/no_nightvision(new_human), WEAR_EYES)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/box/MRE(new_human), WEAR_IN_JACKET)
+ spawn_marine_fluff_items(new_human)
//*****************************************************************************************************/
@@ -861,9 +927,46 @@
new_human.equip_to_slot_or_del(new /obj/item/tool/surgery/surgical_line(new_human), WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/tool/surgery/synthgraft(new_human), WEAR_IN_BACK)
-/datum/equipment_preset/uscm/medic_equipped/cryo
- name = "USCM Cryo Hospital Corpsman (Equipped)"
- auto_squad_name = SQUAD_MARINE_CRYO
+/datum/equipment_preset/uscm/medic_equipped/random
+ name = "USCM Squad Hospital Corpsman (Equipped Random)"
+
+/datum/equipment_preset/uscm/medic_equipped/random/spawn_marine_fluff_items(mob/living/carbon/human/new_human)
+ var/obj/item/helmet_accessory = pick(GLOB.allowed_helmet_items)
+ new_human.equip_to_slot_or_del(new helmet_accessory, WEAR_IN_HELMET)
+ if(prob(50))
+ var/obj/item/helmet_accessory_two = pick(GLOB.allowed_helmet_items)
+ new_human.equip_to_slot_or_del(new helmet_accessory_two, WEAR_IN_HELMET)
+
+ if(prob(50))
+ var/list/possible_masks = list(/obj/item/clothing/mask/gas) + subtypesof(/obj/item/clothing/mask/rebreather) + subtypesof(/obj/item/clothing/mask/tornscarf)
+ if(prob(25))
+ var/obj/item/clothing/mask/new_mask = pick(possible_masks)
+ new_human.equip_to_slot_or_del(new new_mask, WEAR_FACE)
+ else
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/mask/surgical(new_human), WEAR_FACE)
+
+/datum/equipment_preset/uscm/medic_equipped/random/load_gear(mob/living/carbon/human/new_human)
+ spawn_marine_backpack(new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/medic(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine/medic(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo/med(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/medium(new_human), WEAR_JACKET)
+ spawn_marine_fluff_items(new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/regular(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/adv(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/medical/lifesaver/full(new_human), WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/medkit/full(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/flare/full(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine(new_human), WEAR_HANDS)
+
+ 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/storage/box/MRE(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/rifle/m41aMK1(new_human), WEAR_J_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/surgery/surgical_line(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/surgery/synthgraft(new_human), WEAR_IN_BACK)
//*****************************************************************************************************/
@@ -972,9 +1075,29 @@
new_human.back.pickup(new_human)
-/datum/equipment_preset/uscm/tl_equipped/cryo
- name = "USCM Cryo Squad Sergeant (Equipped)"
- auto_squad_name = SQUAD_MARINE_CRYO
+/datum/equipment_preset/uscm/tl_equipped/random
+ name = "USCM Squad Sergeant (Equipped Random)"
+
+/datum/equipment_preset/uscm/tl_equipped/random/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/satchel/rto(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine(new_human), WEAR_BODY)
+ spawn_marine_armor(new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/device/binoculars/range(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine/rto(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo(new_human), WEAR_L_EAR)
+
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/flare/full(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine(new_human), WEAR_HANDS)
+
+ 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/storage/box/MRE(new_human), WEAR_IN_BACK)
+
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/rifle/m41aMK1(new_human), WEAR_J_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/marine/m41amk1(new_human), WEAR_WAIST)
+ spawn_marine_fluff_items(new_human)
+
+
//############ Marine Raiders #############
//Operator
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 409c88388f..cf225caadc 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -32,6 +32,7 @@
plane = GHOST_PLANE
layer = ABOVE_FLY_LAYER
stat = DEAD
+ mob_flags = KNOWS_TECHNOLOGY
var/adminlarva = FALSE
var/ghostvision = TRUE
var/can_reenter_corpse
@@ -189,37 +190,38 @@
clean_observe_target()
/// When the observer target gets a screen, our observer gets a screen minus some game screens we don't want the observer to touch
-/mob/dead/observer/proc/observe_target_screen_add(observe_target_mob_client, add_to_screen)
+/mob/dead/observer/proc/observe_target_screen_add(observe_target_mob_client, screen_add)
SIGNAL_HANDLER
- if(!client)
- return
-
- if(istype(add_to_screen, /atom/movable/screen/action_button))
- return
-
- if(istype(add_to_screen, /atom/movable/screen/fullscreen))
- return
+ var/static/list/excluded_types = typecacheof(list(
+ /atom/movable/screen/fullscreen,
+ /atom/movable/screen/click_catcher,
+ /atom/movable/screen/escape_menu,
+ /atom/movable/screen/buildmode,
+ /obj/effect/detector_blip,
+ ))
- if(istype(add_to_screen, /atom/movable/screen/click_catcher))
+ if(!client)
return
- if(istype(add_to_screen, /atom/movable/screen/escape_menu))
- return
+ // `screen_add` can sometimes be a list, so it's safest to just handle everything as one.
+ var/list/stuff_to_add = (islist(screen_add) ? screen_add : list(screen_add))
- if(istype(add_to_screen, /obj/effect/detector_blip))
- return
+ for(var/item in stuff_to_add)
+ // Ignore anything that's in `excluded_types`.
+ if(is_type_in_typecache(item, excluded_types))
+ continue
- client.add_to_screen(add_to_screen)
+ client.add_to_screen(screen_add)
/// When the observer target loses a screen, our observer loses it as well
-/mob/dead/observer/proc/observe_target_screen_remove(observe_target_mob_client, remove_from_screen)
+/mob/dead/observer/proc/observe_target_screen_remove(observe_target_mob_client, screen_remove)
SIGNAL_HANDLER
if(!client)
return
- client.remove_from_screen(remove_from_screen)
+ client.remove_from_screen(screen_remove)
/// When the observe target ghosts our observer disconnect from their screen updates
/mob/dead/observer/proc/observe_target_ghosting(mob/observer_target_mob)
@@ -236,8 +238,9 @@
if(observe_target_client != new_client)
observe_target_client = new_client
- RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_ADD, PROC_REF(observe_target_screen_add))
- RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_REMOVE, PROC_REF(observe_target_screen_remove))
+ // Override the signal from any previous targets.
+ RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_ADD, PROC_REF(observe_target_screen_add), TRUE)
+ RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_REMOVE, PROC_REF(observe_target_screen_remove), TRUE)
/// When the observe target logs in our observer connect to the new client
/mob/dead/observer/proc/observe_target_login(mob/living/new_character)
@@ -246,8 +249,9 @@
if(observe_target_client != new_character.client)
observe_target_client = new_character.client
- RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_ADD, PROC_REF(observe_target_screen_add))
- RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_REMOVE, PROC_REF(observe_target_screen_remove))
+ // Override the signal from any previous targets.
+ RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_ADD, PROC_REF(observe_target_screen_add), TRUE)
+ RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_REMOVE, PROC_REF(observe_target_screen_remove), TRUE)
///makes the ghost see the target hud and sets the eye at the target.
/mob/dead/observer/proc/do_observe(atom/movable/target)
@@ -257,52 +261,28 @@
ManualFollow(target)
reset_perspective()
- if(!ishuman(target) || !client.prefs?.auto_observe)
+ if(!iscarbon(target) || !client.prefs?.auto_observe)
return
- var/mob/living/carbon/human/human_target = target
-
- client.eye = human_target
-
- if(!human_target.hud_used)
+ var/mob/living/carbon/carbon_target = target
+ if(!carbon_target.hud_used)
return
client.clear_screen()
- LAZYINITLIST(human_target.observers)
- human_target.observers |= src
- human_target.hud_used.show_hud(human_target.hud_used.hud_version, src)
-
- var/list/target_contents = human_target.get_contents()
-
- //Handles any currently open storage containers the target is looking in when we observe
- for(var/obj/item/storage/checked_storage in target_contents)
- if(!(human_target in checked_storage.content_watchers))
- continue
-
- client.add_to_screen(checked_storage.closer)
- client.add_to_screen(checked_storage.contents)
+ client.eye = carbon_target
+ observe_target_mob = carbon_target
- if(checked_storage.storage_slots)
- client.add_to_screen(checked_storage.boxes)
- else
- client.add_to_screen(checked_storage.storage_start)
- client.add_to_screen(checked_storage.storage_continue)
- client.add_to_screen(checked_storage.storage_end)
+ carbon_target.auto_observed(src)
- break
-
- observe_target_mob = human_target
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(observer_move_react))
RegisterSignal(observe_target_mob, COMSIG_PARENT_QDELETING, PROC_REF(clean_observe_target))
RegisterSignal(observe_target_mob, COMSIG_MOB_GHOSTIZE, PROC_REF(observe_target_ghosting))
RegisterSignal(observe_target_mob, COMSIG_MOB_NEW_MIND, PROC_REF(observe_target_new_mind))
RegisterSignal(observe_target_mob, COMSIG_MOB_LOGIN, PROC_REF(observe_target_login))
- RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(observer_move_react))
-
- if(human_target.client)
- observe_target_client = human_target.client
+ if(observe_target_mob.client)
+ observe_target_client = observe_target_mob.client
RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_ADD, PROC_REF(observe_target_screen_add))
RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_REMOVE, PROC_REF(observe_target_screen_remove))
- return
/mob/dead/observer/reset_perspective(atom/A)
if(observe_target_mob)
@@ -620,7 +600,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
return
mind.transfer_to(mind.original, TRUE)
- SStgui.on_transfer(src, mind.current)
qdel(src)
return TRUE
@@ -758,12 +737,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(!tx || !ty || !tz)
return
following = null
- spawn(0)
- // To stop the ghost flickering.
- x = tx
- y = ty
- z = tz
- sleep(15)
+ forceMove(locate(tx, ty, tz))
/mob/dead/observer/verb/dead_teleport_mob() //Moves the ghost instead of just changing the ghosts's eye -Nodrak
set category = "Ghost"
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index b63ce0174a..4ea2f35aa6 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -416,6 +416,29 @@
"}
show_browser(user, dat, name, "mob[name]")
+/**
+ * Called by [/mob/dead/observer/proc/do_observe] when a carbon mob is observed by a ghost with [/datum/preferences/var/auto_observe] enabled.
+ *
+ * Any HUD changes past this point are handled by [/mob/dead/observer/proc/observe_target_screen_add]
+ * and [/mob/dead/observer/proc/observe_target_screen_remove].
+ *
+ * Override on subtype mobs if they have any extra HUD elements/behaviour.
+ */
+/mob/living/carbon/proc/auto_observed(mob/dead/observer/observer)
+ SHOULD_CALL_PARENT(TRUE)
+
+ LAZYINITLIST(observers)
+ observers |= observer
+ hud_used.show_hud(hud_used.hud_version, observer)
+
+ // Add the player's action buttons (not the actions themselves) to the observer's screen.
+ for(var/datum/action/action as anything in actions)
+ // Skip any hidden ones (of course).
+ if(action.hidden || action.player_hidden)
+ continue
+
+ observer.client.add_to_screen(action.button)
+
//generates realistic-ish pulse output based on preset levels
/mob/living/carbon/proc/get_pulse(method) //method 0 is for hands, 1 is for machines, more accurate
var/temp = 0 //see setup.dm:694
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index dfa646ef80..350bcec3e8 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -313,6 +313,29 @@
"}
show_browser(user, dat, name, "mob[name]")
+/**
+ * Handles any storage containers that the human is looking inside when auto-observed.
+ */
+/mob/living/carbon/human/auto_observed(mob/dead/observer/observer)
+ . = ..()
+
+ // If `src` doesn't have an inventory open.
+ if(!s_active)
+ return
+
+ // Add the storage interface to `observer`'s screen.
+ observer.client.add_to_screen(s_active.closer)
+ observer.client.add_to_screen(s_active.contents)
+
+ // If the storage has a set number of item slots.
+ if(s_active.storage_slots)
+ observer.client.add_to_screen(s_active.boxes)
+ // If the storage instead has a maximum combined item 'weight'.
+ else
+ observer.client.add_to_screen(s_active.storage_start)
+ observer.client.add_to_screen(s_active.storage_continue)
+ observer.client.add_to_screen(s_active.storage_end)
+
// called when something steps onto a human
// this handles mulebots and vehicles
/mob/living/carbon/human/Crossed(atom/movable/AM)
diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
index a89e2d0444..f27616508d 100644
--- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
@@ -797,6 +797,11 @@
var/datum/mob_hud/MH = huds[MOB_HUD_XENO_INFECTION]
MH.add_hud_to(src, src)
+// Transfer any observing players over to the xeno's new body (`target`) on evolve/de-evolve.
+/mob/living/carbon/xenomorph/transfer_observers_to(atom/target)
+ for(var/mob/dead/observer/observer as anything in observers)
+ observer.clean_observe_target()
+ observer.do_observe(target)
/mob/living/carbon/xenomorph/check_improved_pointing()
//xeno leaders get a big arrow and less cooldown
diff --git a/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm b/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm
index 263f354c78..cc330966f2 100644
--- a/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm
+++ b/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm
@@ -10,6 +10,9 @@
//drones expand the hive
/datum/xeno_ai_movement/drone/ai_move_idle(delta_time)
+ if(!GLOB.ai_xeno_weeding)
+ return ..()
+
var/mob/living/carbon/xenomorph/idle_xeno = parent
if(idle_xeno.throwing)
diff --git a/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm b/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm
index e420f2c765..337086434d 100644
--- a/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm
+++ b/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm
@@ -9,7 +9,6 @@
var/ai_move_delay = 0
var/path_update_period = (0.5 SECONDS)
- var/no_path_found = FALSE
var/ai_range = 16
var/max_travel_distance = 24
var/min_travel_distance = 1
@@ -27,9 +26,6 @@
/// The actual cooldown declaration for forceful retargeting, reference forced_retarget_time for time in between checks
COOLDOWN_DECLARE(forced_retarget_cooldown)
- /// Amount of times no path found has occured
- var/no_path_found_amount = 0
-
/// The time interval between calculating new paths if we cannot find a path
var/no_path_found_period = (2.5 SECONDS)
@@ -193,22 +189,12 @@
/mob/living/carbon/xenomorph/proc/set_path(list/path)
current_path = path
if(!path)
- no_path_found = TRUE
+ COOLDOWN_START(src, no_path_found_cooldown, no_path_found_period)
/mob/living/carbon/xenomorph/proc/move_to_next_turf(turf/T, max_range = ai_range)
if(!T)
return FALSE
- if(no_path_found)
-
- if(no_path_found_amount > 0)
- COOLDOWN_START(src, no_path_found_cooldown, no_path_found_period)
- no_path_found = FALSE
- no_path_found_amount++
- return FALSE
-
- no_path_found_amount = 0
-
if((!current_path || (next_path_generation < world.time && current_target_turf != T)) && COOLDOWN_FINISHED(src, no_path_found_cooldown))
if(!XENO_CALCULATING_PATH(src) || current_target_turf != T)
SSxeno_pathfinding.calculate_path(src, T, max_range, src, CALLBACK(src, PROC_REF(set_path)), list(src, current_target))
diff --git a/code/modules/mob/living/init_signals.dm b/code/modules/mob/living/init_signals.dm
index a2b92007d9..825fa4dcc9 100644
--- a/code/modules/mob/living/init_signals.dm
+++ b/code/modules/mob/living/init_signals.dm
@@ -26,6 +26,7 @@
if(stat < UNCONSCIOUS)
set_stat(UNCONSCIOUS)
sound_environment_override = SOUND_ENVIRONMENT_PSYCHOTIC
+ client?.soundOutput?.update_mob_environment_override()
/// Called when [TRAIT_KNOCKEDOUT] is removed from the mob.
/mob/living/proc/on_knockedout_trait_loss(datum/source)
@@ -33,6 +34,7 @@
if(stat <= UNCONSCIOUS)
update_stat()
sound_environment_override = SOUND_ENVIRONMENT_NONE
+ client?.soundOutput?.update_mob_environment_override()
/// Called when [TRAIT_IMMOBILIZED] is added to the mob.
/mob/living/proc/on_immobilized_trait_gain(datum/source)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 64c8513108..72fd380e58 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -485,12 +485,10 @@
if(CONSCIOUS)
if(stat >= UNCONSCIOUS)
ADD_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT)
- sound_environment_override = SOUND_ENVIRONMENT_PSYCHOTIC
add_traits(list(/*TRAIT_HANDS_BLOCKED, */ TRAIT_INCAPACITATED, TRAIT_FLOORED), STAT_TRAIT)
if(UNCONSCIOUS)
if(stat >= UNCONSCIOUS)
ADD_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT) //adding trait sources should come before removing to avoid unnecessary updates
- sound_environment_override = SOUND_ENVIRONMENT_PSYCHOTIC
if(DEAD)
SEND_SIGNAL(src, COMSIG_MOB_STAT_SET_ALIVE)
// remove_from_dead_mob_list()
@@ -500,12 +498,10 @@
if(CONSCIOUS)
if(. >= UNCONSCIOUS)
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT)
- sound_environment_override = SOUND_ENVIRONMENT_NONE
remove_traits(list(/*TRAIT_HANDS_BLOCKED, */ TRAIT_INCAPACITATED, TRAIT_FLOORED, /*TRAIT_CRITICAL_CONDITION*/), STAT_TRAIT)
if(UNCONSCIOUS)
if(. >= UNCONSCIOUS)
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT)
- sound_environment_override = SOUND_ENVIRONMENT_NONE
if(DEAD)
SEND_SIGNAL(src, COMSIG_MOB_STAT_SET_DEAD)
// REMOVE_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT)
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 775e69dc0b..38157a0673 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -18,6 +18,8 @@
update_Login_details()
+ SEND_SIGNAL(src, COMSIG_MOB_LOGIN)
+
client.images = null
client.screen = null //remove hud items just in case
if(!hud_used)
@@ -58,6 +60,6 @@
client.init_verbs()
- SEND_GLOBAL_SIGNAL(COMSIG_GLOB_MOB_LOGIN, src)
- SEND_SIGNAL(client, COMSIG_CLIENT_MOB_LOGIN, src)
- SEND_SIGNAL(src, COMSIG_MOB_LOGIN)
+ SEND_GLOBAL_SIGNAL(COMSIG_GLOB_MOB_LOGGED_IN, src)
+ SEND_SIGNAL(client, COMSIG_CLIENT_MOB_LOGGED_IN, src)
+ SEND_SIGNAL(src, COMSIG_MOB_LOGGED_IN)
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index f0e5bc48a8..e2f40506b2 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -28,7 +28,8 @@
I'll make some notes on where certain variable defines should probably go.
Changing this around would probably require a good look-over the pre-existing code.
*/
- var/list/observers //The list of people observing this mob.
+ /// The list of people observing this mob.
+ var/list/mob/dead/observer/observers
var/zone_selected = "chest"
var/use_me = 1 //Allows all mobs to use the me verb by default, will have to manually specify they cannot
@@ -433,4 +434,3 @@
return
src.regenerate_icons()
-
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 7bda746325..2664c14a8a 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -584,7 +584,7 @@ var/global/list/limb_types_by_name = list(
alert_overlay.layer = FLOAT_LAYER
alert_overlay.plane = FLOAT_PLANE
-
+ alert_overlay.underlays.Cut()
screen_alert.overlays += alert_overlay
/mob/proc/reset_lighting_alpha()
diff --git a/code/modules/projectiles/guns/misc.dm b/code/modules/projectiles/guns/misc.dm
index 098dafb5fd..c65d422344 100644
--- a/code/modules/projectiles/guns/misc.dm
+++ b/code/modules/projectiles/guns/misc.dm
@@ -63,8 +63,8 @@
//M60
/obj/item/weapon/gun/m60
- name = "\improper M60 General Purpose Machine Gun"
- desc = "The M60. The Pig. The Action Hero's wet dream. \nAlt-click it to open the feed cover and allow for reloading."
+ name = "\improper H-G Mk70 Machine Gun"
+ desc = "Part of the Henjin-Garcia repro line, the Mk70 found surprising niche in Frontier colony home defense against aggressive, largescale xenofauna. \nAlt-click to open the feed tray cover for handling reloads."
icon = 'icons/obj/items/weapons/guns/guns_by_faction/colony.dmi'
icon_state = "m60"
item_state = "m60"
diff --git a/code/modules/projectiles/magazines/misc.dm b/code/modules/projectiles/magazines/misc.dm
index 87568c9532..9e832a2a05 100644
--- a/code/modules/projectiles/magazines/misc.dm
+++ b/code/modules/projectiles/magazines/misc.dm
@@ -19,8 +19,8 @@
//M60
/obj/item/ammo_magazine/m60
- name = "M60 ammo box (7.62x51mm)"
- desc = "A blast from the past chambered in 7.62X51mm NATO."
+ name = "Mk70 belt box (7.62x51mm)"
+ desc = "Limited production run by Henjin-Garcia of old Earth weapons. A 100rnd belt box for their Mk70 reproduction of the M60 GPMG."
caliber = "7.62x51mm"
icon = 'icons/obj/items/weapons/guns/ammo_by_faction/colony.dmi'
icon_state = "m60" //PLACEHOLDER
diff --git a/code/modules/vehicles/van/van.dm b/code/modules/vehicles/van/van.dm
index 0b99d682c0..3dcc603445 100644
--- a/code/modules/vehicles/van/van.dm
+++ b/code/modules/vehicles/van/van.dm
@@ -82,7 +82,7 @@
icon_state = null
- RegisterSignal(SSdcs, COMSIG_GLOB_MOB_LOGIN, PROC_REF(add_default_image))
+ RegisterSignal(SSdcs, COMSIG_GLOB_MOB_LOGGED_IN, PROC_REF(add_default_image))
for(var/I in GLOB.player_list)
add_default_image(SSdcs, I)
@@ -124,7 +124,7 @@
mobs_under += L
RegisterSignal(L, COMSIG_PARENT_QDELETING, PROC_REF(remove_under_van))
- RegisterSignal(L, COMSIG_MOB_LOGIN, PROC_REF(add_client))
+ RegisterSignal(L, COMSIG_MOB_LOGGED_IN, PROC_REF(add_client))
RegisterSignal(L, COMSIG_MOVABLE_MOVED, PROC_REF(check_under_van))
if(L.client)
@@ -140,7 +140,7 @@
UnregisterSignal(L, list(
COMSIG_PARENT_QDELETING,
- COMSIG_MOB_LOGIN,
+ COMSIG_MOB_LOGGED_IN,
COMSIG_MOVABLE_MOVED,
))
diff --git a/colonialmarines.dme b/colonialmarines.dme
index a79cbe1f0e..d201d6567f 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -136,6 +136,7 @@
#include "code\__DEFINES\dcs\signals\atom\signals_obj.dm"
#include "code\__DEFINES\dcs\signals\atom\signals_projectile.dm"
#include "code\__DEFINES\dcs\signals\atom\signals_turf.dm"
+#include "code\__DEFINES\dcs\signals\atom\mob\signals_mind.dm"
#include "code\__DEFINES\dcs\signals\atom\mob\signals_mob.dm"
#include "code\__DEFINES\dcs\signals\atom\mob\living\signals_human.dm"
#include "code\__DEFINES\dcs\signals\atom\mob\living\signals_living.dm"
@@ -1403,8 +1404,10 @@
#include "code\modules\admin\game_master\game_master_submenu.dm"
#include "code\modules\admin\game_master\resin_panel.dm"
#include "code\modules\admin\game_master\sound_panel.dm"
+#include "code\modules\admin\game_master\extra_buttons\fire_support_menu.dm"
#include "code\modules\admin\game_master\extra_buttons\rappel_menu.dm"
#include "code\modules\admin\game_master\extra_buttons\rename_platoon.dm"
+#include "code\modules\admin\game_master\extra_buttons\toggle_ai_xeno_weeding.dm"
#include "code\modules\admin\game_master\extra_buttons\toggle_capture_crit.dm"
#include "code\modules\admin\game_master\extra_buttons\toggle_join_xeno.dm"
#include "code\modules\admin\game_master\extra_buttons\toggle_vehicle_blockers.dm"
diff --git a/html/changelogs/archive/2024-08.yml b/html/changelogs/archive/2024-08.yml
index db9a377dbb..232a1874b3 100644
--- a/html/changelogs/archive/2024-08.yml
+++ b/html/changelogs/archive/2024-08.yml
@@ -26,3 +26,37 @@
- rscadd: 'added more cigarette options in the loadout, along with a golden zippo
:cl:'
+2024-08-10:
+ Doubleumc:
+ - qol: Adds the ability to hide your action buttons
+ - bugfix: Fixed 'hidden' action buttons being shown to observing players.
+ - qol: Made any observers transfer over when a Xenomorph evolves/de-evolves.
+ - rscadd: Added 'observe' functionality to Xenomorphs, allowing observers to view
+ the target's UI.
+ - rscadd: Made observing a player also show their action buttons.
+ - spellcheck: Made observers see the marine version of CAS warnings, rather than
+ the xeno one.
+ - qol: Made open TGUI windows transfer over when a xeno player evolves.
+ - bugfix: Fixed observing a monkey/farwa/stok/etc. locking the camera onto it.
+ - bugfix: Observer minimap should no longer occasionally show wrong / no map.
+ - code_imp: environmental reverb applies more reliably and only to positional sounds
+ - bugfix: Notifications should no longer emit light.
+ - bugfix: Sprite-click shots onto Xenos are no longer affected by limb-targeting
+ penalty, because it was an accuracy debuff when there is no inherent benefit
+ to targeting Xeno limbs.
+ - rscadd: Locking down dropship's doors closes them before locking.
+ - bugfix: Fix handling of sentry_computer deletion
+ - refactor: Refactored the overlay_lighting component to better handle objects deleting
+ - bugfix: Fix putting lights in bags somereason keeping the light on
+ - refactor: sentry laptop now uses camera manager component
+ - bugfix: less lag from xenos pathfinding unreachable targets
+ - rscadd: Added GM button to toggle AI xeno weeding
+ Kitsunemitsu:
+ - rscadd: Added 3 more railgun types
+ - balance: Railgun now changes it's stats based off the altitude
+ sunofang:
+ - rscadd: Due to budget increases, Close Air Support now has ammo.
+ - admin: Added a fire support menu for GM's to use.
+2024-08-11:
+ private-tristan:
+ - balance: M56D has been buffed with more damage, accuracy, and firerate.
diff --git a/strings/metatips.txt b/strings/metatips.txt
index 17ffd607a7..e0ede9c626 100644
--- a/strings/metatips.txt
+++ b/strings/metatips.txt
@@ -12,6 +12,7 @@ As a mentor, you can become the imaginary friend of a new player to teach them!
You shouldn't ignore what your allies are up to. Sometimes they can be organizing a flank on the radio, sometimes they can be walking up behind you with a buck-loaded shotgun. Either way, it pays to be alert to what they're doing, as much to as what the enemies are.
The Wiki (https://cm-ss13.com/wiki) is a very useful repository of information about the game, such as weapons, equipment, xenomorph castes and their strains. It may not be fully up to date much of the time, but the basics are usually accurate.
As an observer, you may see how much remaining hijack time is left in the status panel.
+As an observer, you can quickly follow someone by ctrl-clicking on their sprite.
You can always AdminHelp with the F1 key to question a member of staff regarding rules or game bugs.
As ghost you are given extra tools for spectating the round: you can jump and follow specific players, get notifications about CAS and OB strikes, can see all health bars, and such.
You can press ESC key to bring up the game pause menu. It allows you change settings, AdminHelp and MentorHelp, and even access the Web Maps of game by clicking at top right.
diff --git a/tgui/packages/tgui/interfaces/GameMasterFireSupportMenu.jsx b/tgui/packages/tgui/interfaces/GameMasterFireSupportMenu.jsx
new file mode 100644
index 0000000000..f8be7b7f95
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/GameMasterFireSupportMenu.jsx
@@ -0,0 +1,98 @@
+import { auto } from '@popperjs/core';
+
+import { useBackend } from '../backend';
+import { Button, Collapsible, Section, Stack } from '../components';
+import { Window } from '../layouts';
+
+export const GameMasterFireSupportMenu = (props, context) => {
+ const { act, data } = useBackend();
+ return (
+
+
+
+
+
+
+
+
+
+
+ {data.missile_ordnance_options.map((ordnance, i) => (
+
+ ))}
+
+
+
+ {data.orbital_ordnance_options.map((ordnance, i) => (
+
+ ))}
+
+
+
+ {data.mortar_ordnance_options.map((ordnance, i) => (
+
+ ))}
+
+
+
+ {data.misc_ordnance_options.map((ordnance, i) => (
+
+ ))}
+
+
+
+
+ );
+};