diff --git a/code/__DEFINES/client_prefs.dm b/code/__DEFINES/client_prefs.dm
index 56775bea8888..1090b6776b35 100644
--- a/code/__DEFINES/client_prefs.dm
+++ b/code/__DEFINES/client_prefs.dm
@@ -15,7 +15,7 @@
#define TOGGLE_AUTOMATIC_PUNCTUATION (1<<7) // Whether your sentences will automatically be punctuated with a period
#define TOGGLE_COMBAT_CLICKDRAG_OVERRIDE (1<<8) // Whether disarm/harm intents cause clicks to trigger immediately when the mouse button is depressed.
#define TOGGLE_ALTERNATING_DUAL_WIELD (1<<9) // Whether dual-wielding fires both guns at once or swaps between them, OUTDATED, used to update savefiles, now dual_wield_pref
-#define TOGGLE_FULLSCREEN (1<<10) // See /client/proc/toggle_fullscreen in client_procs.dm
+#define TOGGLE_FULLSCREEN (1<<10) // See /client/proc/update_fullscreen in client_procs.dm
#define TOGGLE_MEMBER_PUBLIC (1<<11) //determines if you get a byond logo by your name in ooc if you're a member or not
#define TOGGLE_OOC_FLAG (1<<12) // determines if your country flag appears by your name in ooc chat
#define TOGGLE_MIDDLE_MOUSE_SWAP_HANDS (1<<13) //Toggle whether middle click swaps your hands
diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm
index 0ea0f5bd7fb7..0173a55e2d17 100644
--- a/code/__DEFINES/job.dm
+++ b/code/__DEFINES/job.dm
@@ -13,6 +13,7 @@
#define SQUAD_SOF "SOF"
#define SQUAD_CBRN "CBRN"
#define SQUAD_FORECON "FORECON"
+#define SQUAD_SOLAR "Solar Devils"
// Job name defines
#define JOB_SQUAD_MARINE "Rifleman"
@@ -124,6 +125,8 @@ GLOBAL_LIST_INIT(job_command_roles, JOB_COMMAND_ROLES_LIST)
#define JOB_GENERAL "USCM General"
#define JOB_ACMC "Assistant Commandant of the Marine Corps"
#define JOB_CMC "Commandant of the Marine Corps"
+#define JOB_PLT_MED "Platoon Corpsman"
+#define JOB_PLT_SL "Platoon Squad Leader"
#define JOB_SQUAD_TECH "Reconnaissance Support Technician"
// Used to add a timelock to a job. Will be passed onto derivatives
@@ -335,11 +338,15 @@ GLOBAL_LIST_INIT(job_command_roles, JOB_COMMAND_ROLES_LIST)
#define JOB_PROVOST_TML "Provost Team Leader"
#define JOB_PROVOST_ADVISOR "Provost Advisor"
#define JOB_PROVOST_INSPECTOR "Provost Inspector"
+#define JOB_PROVOST_CINSPECTOR "Provost Chief Inspector"
+#define JOB_PROVOST_UNDERCOVER "Provost Undercover Inspector"
+
+#define JOB_PROVOST_DMARSHAL "Provost Deputy Marshal"
#define JOB_PROVOST_MARSHAL "Provost Marshal"
#define JOB_PROVOST_SMARSHAL "Provost Sector Marshal"
#define JOB_PROVOST_CMARSHAL "Provost Chief Marshal"
-#define PROVOST_JOB_LIST list(JOB_PROVOST_ENFORCER, JOB_PROVOST_TML, JOB_PROVOST_ADVISOR, JOB_PROVOST_INSPECTOR, JOB_PROVOST_MARSHAL, JOB_PROVOST_SMARSHAL, JOB_PROVOST_CMARSHAL)
+#define PROVOST_JOB_LIST list(JOB_PROVOST_ENFORCER, JOB_PROVOST_TML, JOB_PROVOST_ADVISOR, JOB_PROVOST_INSPECTOR, JOB_PROVOST_CINSPECTOR, JOB_PROVOST_DMARSHAL, JOB_PROVOST_MARSHAL, JOB_PROVOST_SMARSHAL, JOB_PROVOST_CMARSHAL)
#define JOB_RIOT "Riot Control"
#define JOB_RIOT_CHIEF "Chief Riot Control"
diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm
index 4b8fd9bd58b3..3f4c90eaf7b9 100644
--- a/code/__DEFINES/keybinding.dm
+++ b/code/__DEFINES/keybinding.dm
@@ -33,6 +33,7 @@
//Client
#define COMSIG_KB_CLIENT_GETHELP_DOWN "keybinding_client_gethelp_down"
#define COMSIG_KB_CLIENT_SCREENSHOT_DOWN "keybinding_client_screenshot_down"
+#define COMSIG_KB_CLIENT_TOGGLEFULLSCREEN_DOWN "keybinding_client_togglefullscreen_down"
#define COMSIG_KB_CLIENT_MINIMALHUD_DOWN "keybinding_client_minimalhud_down"
//Communication
diff --git a/code/__DEFINES/paygrade_defs/provost.dm b/code/__DEFINES/paygrade_defs/provost.dm
index 5b2121642d52..c85fabc27bfe 100644
--- a/code/__DEFINES/paygrade_defs/provost.dm
+++ b/code/__DEFINES/paygrade_defs/provost.dm
@@ -1,9 +1,18 @@
// Paygrade shorthand defines, to allow clearer designation.
// PROVOST OFFICE
+/// PvI, Provost Advisor
+#define PAY_SHORT_PVA"PvA"
+
/// PvI, Provost Inspector
#define PAY_SHORT_PVI "PvI"
+/// PvCI, Provost Chief Inspector
+#define PAY_SHORT_PVCI "PvCI"
+
+/// PvDM, Provost Deputy Marshal
+#define PAY_SHORT_PVDM "PvDM"
+
/// PvM, Provost Marshal
#define PAY_SHORT_PVM "PvM"
diff --git a/code/controllers/subsystem/communications.dm b/code/controllers/subsystem/communications.dm
index 282605cf6e84..c4661ff5a673 100644
--- a/code/controllers/subsystem/communications.dm
+++ b/code/controllers/subsystem/communications.dm
@@ -174,6 +174,7 @@ GLOBAL_LIST_INIT(radiochannels, list(
SQUAD_SOF = SOF_FREQ,
SQUAD_CBRN = CBRN_FREQ,
SQUAD_FORECON = FORECON_FREQ,
+ SQUAD_SOLAR = SOF_FREQ,
RADIO_CHANNEL_ALAMO = DS1_FREQ,
RADIO_CHANNEL_NORMANDY = DS2_FREQ,
diff --git a/code/controllers/subsystem/decorator.dm b/code/controllers/subsystem/decorator.dm
index ac63fca47ac5..e8120cc09658 100644
--- a/code/controllers/subsystem/decorator.dm
+++ b/code/controllers/subsystem/decorator.dm
@@ -29,7 +29,7 @@ SUBSYSTEM_DEF(decorator)
var/list/datum/weakref/currentrun = list()
/datum/controller/subsystem/decorator/Initialize()
- var/list/all_decors = typesof(/datum/decorator) - list(/datum/decorator) - typesof(/datum/decorator/manual)
+ var/list/all_decors = typesof(/datum/decorator) - list(/datum/decorator) - typesof(/datum/decorator/manual) - typesof(/datum/decorator/gamemode)
for(var/decor_type in all_decors)
var/datum/decorator/decor = new decor_type()
if(!decor.is_active_decor())
@@ -43,6 +43,8 @@ SUBSYSTEM_DEF(decorator)
registered_decorators[app_type] = list()
registered_decorators[app_type] += decor
+ RegisterSignal(SSdcs, COMSIG_GLOB_MODE_PRESETUP, PROC_REF(handle_mode_specific))
+
for(var/i in registered_decorators)
registered_decorators[i] = sortDecorators(registered_decorators[i])
@@ -71,6 +73,24 @@ SUBSYSTEM_DEF(decorator)
if(MC_TICK_CHECK)
return
+/datum/controller/subsystem/decorator/proc/handle_mode_specific()
+ SIGNAL_HANDLER
+
+ for(var/decorator_type in typesof(/datum/decorator/gamemode))
+ var/datum/decorator/gamemode/gamemode_decorator = new decorator_type()
+
+ if(!istype(SSticker.mode, gamemode_decorator.gamemode))
+ continue
+
+ var/applicable_types = gamemode_decorator.get_decor_types()
+ if(!length(applicable_types))
+ continue
+
+ active_decorators |= gamemode_decorator
+
+ for(var/applicable_type in applicable_types)
+ LAZYADD(registered_decorators[applicable_type], gamemode_decorator)
+
/datum/controller/subsystem/decorator/proc/add_decorator(decor_type, ...)
var/list/arguments = list()
if (length(args) > 1)
diff --git a/code/datums/ammo/bullet/special_ammo.dm b/code/datums/ammo/bullet/special_ammo.dm
index 97c1bf5735f9..cd30f8db1a78 100644
--- a/code/datums/ammo/bullet/special_ammo.dm
+++ b/code/datums/ammo/bullet/special_ammo.dm
@@ -149,15 +149,10 @@
RegisterSignal(SSdcs, COMSIG_GLOB_MODE_PRESETUP, PROC_REF(setup_hvh_damage))
/datum/ammo/bullet/minigun/proc/setup_hvh_damage()
+ SIGNAL_HANDLER
if(MODE_HAS_FLAG(MODE_FACTION_CLASH))
damage = 15
-/datum/ammo/bullet/minigun/tank
- accuracy = -HIT_ACCURACY_TIER_1
- accuracy_var_low = PROJECTILE_VARIANCE_TIER_8
- accuracy_var_high = PROJECTILE_VARIANCE_TIER_8
- accurate_range = 12
-
/datum/ammo/bullet/m60
name = "M60 bullet"
headshot_state = HEADSHOT_OVERLAY_MEDIUM
diff --git a/code/datums/ammo/bullet/tank.dm b/code/datums/ammo/bullet/tank.dm
index 70a953c6e273..1607c1ae6734 100644
--- a/code/datums/ammo/bullet/tank.dm
+++ b/code/datums/ammo/bullet/tank.dm
@@ -4,9 +4,12 @@
//======
*/
+//Autocannon Ammo//
+
/datum/ammo/bullet/tank/flak
name = "flak autocannon bullet"
icon_state = "autocannon"
+ sound_hit = 'sound/weapons/sting_boom_small1.ogg'
damage_falloff = 0
flags_ammo_behavior = AMMO_BALLISTIC
accurate_range_min = 4
@@ -72,3 +75,30 @@
for(var/mob/living/carbon/L in T)
if(L.stat == CONSCIOUS && L.mob_size <= MOB_SIZE_XENO)
shake_camera(L, 1, 1)
+
+//Minigun Ammo//
+
+/datum/ammo/bullet/tank/minigun
+ name = "minigun bullet"
+ headshot_state = HEADSHOT_OVERLAY_MEDIUM
+ icon_state = "bullet_large"
+
+ accuracy = -HIT_ACCURACY_TIER_1
+ accuracy_var_low = PROJECTILE_VARIANCE_TIER_8
+ accuracy_var_high = PROJECTILE_VARIANCE_TIER_8
+ accurate_range = 12
+ damage = 40
+ penetration = ARMOR_PENETRATION_TIER_6
+ damage_armor_punch = 1
+
+/datum/ammo/bullet/tank/minigun/New()
+ ..()
+ if(SSticker.mode && MODE_HAS_FLAG(MODE_FACTION_CLASH))
+ damage = 15
+ else if(SSticker.current_state < GAME_STATE_PLAYING)
+ RegisterSignal(SSdcs, COMSIG_GLOB_MODE_PRESETUP, PROC_REF(setup_hvh_damage))
+
+/datum/ammo/bullet/tank/minigun/proc/setup_hvh_damage()
+ SIGNAL_HANDLER
+ if(MODE_HAS_FLAG(MODE_FACTION_CLASH))
+ damage = 15
diff --git a/code/datums/decorators/gamemode_decorator.dm b/code/datums/decorators/gamemode_decorator.dm
new file mode 100644
index 000000000000..bc89d7a20632
--- /dev/null
+++ b/code/datums/decorators/gamemode_decorator.dm
@@ -0,0 +1,46 @@
+#define GAMEMODE_DECORATOR(type, mode, list_edits) \
+/datum/decorator/gamemode##type##mode { \
+ gamemode = mode; \
+ apply_type = type; \
+ edits = list_edits; \
+}
+
+#define VARIABLE_EDIT(type, variable, value) nameof(type::variable) = value
+#define ARMOR_EDIT(variable, value) VARIABLE_EDIT(/obj/item/clothing/suit/storage/marine, variable, value)
+#define GUN_EDIT(variable, value) VARIABLE_EDIT(/obj/item/weapon/gun, variable, value)
+#define AMMO_EDIT(variable, value) VARIABLE_EDIT(/obj/item/ammo_magazine, variable, value)
+
+/**
+ * Gamemode decorators allow us to make changes to edits on specific gamemodes,
+ * to assist in balancing varied gameplay in different modes
+ *
+ * They can be manually defined, and procs overridden. Alternatively,
+ * using the GAMEMODE_DECORATOR define, you can quickly make a decorator for
+ * a specific type and subtypes.
+ *
+ * eg:
+ * ```
+ * GAMEMODE_DECORATOR(/obj/item/clothing/suit/storage/marine/smartgunner, /datum/game_mode/extended/faction_clash, list(
+ * ARMOR_EDIT(armor_bullet, CLOTHING_ARMOR_HIGH),
+ * ARMOR_EDIT(armor_internaldamage, CLOTHING_ARMOR_HIGH)
+ * ))
+ * ```
+ *
+ * If you need to edit different types, make a new define using VARIABLE_EDIT, and provide the parent path for the type.
+ */
+/datum/decorator/gamemode
+ /// The gamemode type this should apply to
+ var/gamemode
+
+ /// Which type this should apply edits to
+ var/apply_type
+
+ /// The list of edits to make
+ var/list/edits
+
+/datum/decorator/gamemode/get_decor_types()
+ return typesof(apply_type)
+
+/datum/decorator/gamemode/decorate(atom/object)
+ for(var/edit in edits)
+ object.vars[edit] = edits[edit]
diff --git a/code/datums/emergency_calls/solar_devils.dm b/code/datums/emergency_calls/solar_devils.dm
new file mode 100644
index 000000000000..6f3323f3cd6d
--- /dev/null
+++ b/code/datums/emergency_calls/solar_devils.dm
@@ -0,0 +1,91 @@
+/datum/emergency_call/solar_devils
+ name = "USCM Solar Devils (Half Squad)"
+ arrival_message = "This is the Solar Devils of the USCM 2nd Division, responding to your distress beacon. Don't worry, the grown-ups are here to clean up your mess."
+ objectives = "Assist local Marine forces in dealing with whatever issue they can't handle. Further orders may be forthcoming."
+ home_base = /datum/lazy_template/ert/uscm_station
+ probability = 0
+ mob_min = 3
+ mob_max = 5
+
+ max_medics = 1
+ max_smartgunners = 1
+
+/datum/emergency_call/solar_devils/create_member(datum/mind/new_mind, turf/override_spawn_loc)
+ var/turf/spawn_loc = override_spawn_loc ? override_spawn_loc : get_spawn_point()
+
+ if(!istype(spawn_loc))
+ return //Didn't find a useable spawn point.
+
+ var/mob/living/carbon/human/mob = new(spawn_loc)
+ new_mind.transfer_to(mob, TRUE)
+
+ if(!leader && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(mob.client, JOB_SQUAD_LEADER, time_required_for_job))
+ leader = mob
+ arm_equipment(mob, /datum/equipment_preset/uscm/tl_pve, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are the Solar Devils Team Leader!"))
+
+ else if(medics < max_medics && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(mob.client, JOB_SQUAD_MEDIC, time_required_for_job))
+ medics++
+ arm_equipment(mob, /datum/equipment_preset/uscm/medic_pve, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are the Solar Devils Platoon Corpsman!"))
+
+ else if(smartgunners < max_smartgunners && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_SMARTGUNNER) && check_timelock(mob.client, JOB_SQUAD_SMARTGUN))
+ smartgunners++
+ to_chat(mob, SPAN_ROLE_HEADER("You are the Solar Devils Smartgunner!"))
+ arm_equipment(mob, /datum/equipment_preset/uscm/sg_pve, TRUE, TRUE)
+
+ else
+ arm_equipment(mob, /datum/equipment_preset/uscm/rifleman_pve, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are a Solar Devils Rifleman!"))
+
+ to_chat(mob, SPAN_ROLE_BODY("You are a member of the 3rd Battalion 'Solar Devils', part of the USCM's 2nd Division, 1st Regiment. Unlike most of the USS Almayer's troops, you are well-trained and properly-equipped career marines. Semper Fidelis."))
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS)
+
+/datum/emergency_call/solar_devils_full
+ name = "USCM Solar Devils (Full Squad)"
+ arrival_message = "This is the Solar Devils of the USCM 2nd Division, responding to your distress beacon. Don't worry, the grown-ups are here to clean up your mess."
+ objectives = "Assist local Marine forces in dealing with whatever issue they can't handle. Further orders may be forthcoming."
+ home_base = /datum/lazy_template/ert/uscm_station
+ probability = 0
+ mob_min = 3
+ mob_max = 10
+
+ max_engineers = 2
+ max_medics = 1
+ max_smartgunners = 2
+
+/datum/emergency_call/solar_devils_full/create_member(datum/mind/new_mind, turf/override_spawn_loc)
+ var/turf/spawn_loc = override_spawn_loc ? override_spawn_loc : get_spawn_point()
+
+ if(!istype(spawn_loc))
+ return //Didn't find a useable spawn point.
+
+ var/mob/living/carbon/human/mob = new(spawn_loc)
+ new_mind.transfer_to(mob, TRUE)
+
+ if(!leader && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(mob.client, JOB_SQUAD_LEADER, time_required_for_job))
+ leader = mob
+ arm_equipment(mob, /datum/equipment_preset/uscm/sl_pve, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are the Solar Devils Platoon Leader!"))
+
+ else if(engineers < max_engineers && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_ENGINEER) && check_timelock(mob.client, JOB_SQUAD_LEADER, time_required_for_job))
+ engineers++
+ arm_equipment(mob, /datum/equipment_preset/uscm/tl_pve, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are a Solar Devils Team Leader!"))
+
+ else if(medics < max_medics && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(mob.client, JOB_SQUAD_MEDIC, time_required_for_job))
+ medics++
+ arm_equipment(mob, /datum/equipment_preset/uscm/medic_pve, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are the Solar Devils Platoon Corpsman!"))
+
+ else if(smartgunners < max_smartgunners && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_SMARTGUNNER) && check_timelock(mob.client, JOB_SQUAD_SMARTGUN))
+ smartgunners++
+ to_chat(mob, SPAN_ROLE_HEADER("You are a Solar Devils Smartgunner!"))
+ arm_equipment(mob, /datum/equipment_preset/uscm/sg_pve, TRUE, TRUE)
+
+ else
+ arm_equipment(mob, /datum/equipment_preset/uscm/rifleman_pve, TRUE, TRUE)
+ to_chat(mob, SPAN_ROLE_HEADER("You are a Solar Devils Rifleman!"))
+
+ to_chat(mob, SPAN_ROLE_BODY("You are a member of the 3rd Battalion 'Solar Devils', part of the USCM's 2nd Division, 1st Regiment. Unlike most of the USS Almayer's troops, you are well-trained and properly-equipped career marines. Semper Fidelis."))
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS)
diff --git a/code/datums/factions/uscm.dm b/code/datums/factions/uscm.dm
index 0df37c2f0c3c..6a4b61289ea3 100644
--- a/code/datums/factions/uscm.dm
+++ b/code/datums/factions/uscm.dm
@@ -88,6 +88,10 @@
if(JOB_GENERAL, JOB_COLONEL, JOB_ACMC, JOB_CMC)
marine_rk = "general"
border_rk = "command"
+ if(JOB_PLT_MED)
+ marine_rk = "med"
+ if(JOB_PLT_SL)
+ marine_rk = "leader"
if(JOB_SQUAD_TECH)
marine_rk = "tech"
if(JOB_INTEL)
@@ -145,9 +149,18 @@
if(JOB_PROVOST_INSPECTOR)
marine_rk = "pvi"
border_rk = "command"
+ if(JOB_PROVOST_UNDERCOVER)
+ marine_rk = "pvuc"
+ border_rk = "command"
+ if(JOB_PROVOST_CINSPECTOR)
+ marine_rk = "pvci"
+ border_rk = "command"
if(JOB_PROVOST_ADVISOR)
marine_rk = "pva"
border_rk = "command"
+ if(JOB_PROVOST_DMARSHAL)
+ marine_rk = "pvdm"
+ border_rk = "command"
if(JOB_PROVOST_MARSHAL, JOB_PROVOST_CMARSHAL, JOB_PROVOST_SMARSHAL)
marine_rk = "pvm"
border_rk = "command"
diff --git a/code/datums/keybinding/client.dm b/code/datums/keybinding/client.dm
index 752287882277..d7e90f61683a 100644
--- a/code/datums/keybinding/client.dm
+++ b/code/datums/keybinding/client.dm
@@ -34,6 +34,21 @@
winset(user, null, "command=.screenshot [!user.keys_held["shift"] ? "auto" : ""]")
return TRUE
+/datum/keybinding/client/toggle_fullscreen
+ hotkey_keys = list("F11")
+ classic_keys = list("F11")
+ name = "toggle_fullscreen"
+ full_name = "Toggle Fullscreen"
+ description = "Toggles whether the game window will be true fullscreen or normal."
+ keybind_signal = COMSIG_KB_CLIENT_TOGGLEFULLSCREEN_DOWN
+
+/datum/keybinding/client/toggle_fullscreen/down(client/user)
+ . = ..()
+ if(.)
+ return
+ user.toggle_fullscreen_preference()
+ return TRUE
+
/datum/keybinding/client/minimal_hud
hotkey_keys = list("F12")
classic_keys = list("F12")
diff --git a/code/datums/pain/_pain.dm b/code/datums/pain/_pain.dm
index fd4dfbf0bbb3..826773504151 100644
--- a/code/datums/pain/_pain.dm
+++ b/code/datums/pain/_pain.dm
@@ -193,7 +193,7 @@
if(!isnull(threshold_horrible))
activate_horrible()
- if(new_level >= PAIN_LEVEL_SEVERE)
+ if(new_level >= PAIN_LEVEL_SEVERE && feels_pain)
RegisterSignal(source_mob, COMSIG_MOB_DRAGGED, PROC_REF(oxyloss_drag), override = TRUE)
RegisterSignal(source_mob, COMSIG_MOB_DEVOURED, PROC_REF(handle_devour), override = TRUE)
RegisterSignal(source_mob, COMSIG_MOVABLE_PRE_THROW, PROC_REF(oxy_kill), override = TRUE)
diff --git a/code/datums/paygrades/factions/uscm/provost.dm b/code/datums/paygrades/factions/uscm/provost.dm
index c7e529d9fcb4..a89e3b7fa588 100644
--- a/code/datums/paygrades/factions/uscm/provost.dm
+++ b/code/datums/paygrades/factions/uscm/provost.dm
@@ -10,6 +10,19 @@
rank_pin = /obj/item/clothing/accessory/ranks/special/insp
officer_grade = GRADE_FLAG //Not really a flag officer, but they have special access to things for their job.
+/datum/paygrade/provost/inspector/chief
+ paygrade = PAY_SHORT_PVCI
+ name = "Provost Chief Inspector"
+ prefix = "Chief Insp."
+ rank_pin = /obj/item/clothing/accessory/ranks/special/insp
+ officer_grade = GRADE_FLAG //Not really a flag officer, but they have special access to things for their job.
+
+/datum/paygrade/provost/marshal/deputy
+ paygrade = PAY_SHORT_PVDM
+ name = "Provost Deputy Marshal"
+ prefix = "Dep. Marshal"
+ officer_grade = GRADE_FLAG
+
/datum/paygrade/provost/marshal
paygrade = PAY_SHORT_PVM
name = "Provost Marshal"
diff --git a/code/datums/skills/uscm.dm b/code/datums/skills/uscm.dm
index 1bfb58996b4e..01866b82a9d1 100644
--- a/code/datums/skills/uscm.dm
+++ b/code/datums/skills/uscm.dm
@@ -426,3 +426,80 @@ COMMAND STAFF
SKILL_JTAC = SKILL_JTAC_EXPERT,
SKILL_INTEL = SKILL_INTEL_TRAINED,
)
+
+/*
+---------------------
+SOLAR DEVILS (PvE Marines)
+---------------------
+*/
+
+/datum/skills/rifleman_pve
+ name = "Private"
+ skills = list(
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
+ SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
+ SKILL_POWERLOADER = SKILL_POWERLOADER_TRAINED,
+ SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
+ SKILL_JTAC = SKILL_JTAC_BEGINNER,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ )
+
+/datum/skills/combat_medic_pve
+ name = "Combat Medic"
+ skills = list(
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
+ SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
+ SKILL_POWERLOADER = SKILL_POWERLOADER_TRAINED,
+ SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
+ SKILL_JTAC = SKILL_JTAC_BEGINNER,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_MEDICAL = SKILL_MEDICAL_MEDIC,
+ SKILL_SURGERY = SKILL_SURGERY_NOVICE,
+ )
+
+/datum/skills/smartgunner_pve
+ name = "Smartgunner"
+ skills = list(
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
+ SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
+ SKILL_POWERLOADER = SKILL_POWERLOADER_TRAINED,
+ SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
+ SKILL_JTAC = SKILL_JTAC_BEGINNER,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_SPEC_WEAPONS = SKILL_SPEC_SMARTGUN,
+ )
+
+/datum/skills/tl_pve
+ name = "Fireteam Leader"
+ skills = list(
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
+ SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
+ SKILL_POWERLOADER = SKILL_POWERLOADER_TRAINED,
+ SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
+ SKILL_JTAC = SKILL_JTAC_TRAINED,
+ SKILL_LEADERSHIP = SKILL_LEAD_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_PILOT = SKILL_PILOT_TRAINED,
+ )
+
+/datum/skills/sl_pve
+ name = "Squad Leader"
+ skills = list(
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
+ SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
+ SKILL_POWERLOADER = SKILL_POWERLOADER_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_MASTER,
+ SKILL_CQC = SKILL_CQC_TRAINED,
+ SKILL_LEADERSHIP = SKILL_LEAD_EXPERT,
+ SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
+ SKILL_SURGERY = SKILL_SURGERY_NOVICE,
+ SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
+ SKILL_JTAC = SKILL_JTAC_TRAINED,
+ SKILL_INTEL = SKILL_INTEL_TRAINED,
+ SKILL_PILOT = SKILL_PILOT_EXPERT,
+ )
diff --git a/code/game/area/kutjevo.dm b/code/game/area/kutjevo.dm
index d0a4a30118b0..1180e10a761e 100644
--- a/code/game/area/kutjevo.dm
+++ b/code/game/area/kutjevo.dm
@@ -7,6 +7,7 @@
//ambience = list('figuresomethingout.ogg')
icon_state = "kutjevo"
can_build_special = TRUE //T-Comms structure
+ powernet_name = "ground"
temperature = 308.7 //kelvin, 35c, 95f
minimap_color = MINIMAP_AREA_ENGI
diff --git a/code/game/area/shiva.dm b/code/game/area/shiva.dm
index fc7424f8146b..dee9eb5f0aea 100644
--- a/code/game/area/shiva.dm
+++ b/code/game/area/shiva.dm
@@ -6,6 +6,7 @@
//ambience = list('figuresomethingout.ogg')
icon_state = "shiva"
can_build_special = TRUE //T-Comms structure
+ powernet_name = "ground"
temperature = ICE_COLONY_TEMPERATURE
minimap_color = MINIMAP_AREA_COLONY
diff --git a/code/game/area/strata.dm b/code/game/area/strata.dm
index 117cffa600d4..2bb17ea5d60d 100644
--- a/code/game/area/strata.dm
+++ b/code/game/area/strata.dm
@@ -12,6 +12,7 @@ EXTERIOR is FUCKING FREEZING, and refers to areas out in the open and or exposed
//ambience = list('figuresomethingout.ogg')
icon_state = "strata"
can_build_special = TRUE //T-Comms structure
+ powernet_name = "ground"
temperature = SOROKYNE_TEMPERATURE //If not in a building, it'll be cold. All interior areas are set to T20C
minimap_color = MINIMAP_AREA_COLONY
diff --git a/code/game/area/varadero.dm b/code/game/area/varadero.dm
index aac37bdf942a..b0e5d283fdcd 100644
--- a/code/game/area/varadero.dm
+++ b/code/game/area/varadero.dm
@@ -7,6 +7,7 @@
ambience_exterior = AMBIENCE_NV
icon_state = "varadero"
can_build_special = TRUE //T-Comms structure
+ powernet_name = "ground"
temperature = TROPICAL_TEMP
minimap_color = MINIMAP_AREA_COLONY
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 52a35b715b1a..be68dddff10f 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -31,6 +31,9 @@
///Highest-intensity light affecting us, which determines our visibility.
var/affecting_dynamic_lumi = 0
+ /// Holds a reference to the emissive blocker overlay
+ var/emissive_overlay
+
//===========================================================================
/atom/movable/Destroy(force)
for(var/atom/movable/I in contents)
@@ -39,6 +42,7 @@
pulledby.stop_pulling()
QDEL_NULL(launch_metadata)
QDEL_NULL(em_block)
+ QDEL_NULL(emissive_overlay)
if(loc)
loc.on_stored_atom_del(src) //things that container need to do when a movable atom inside it is deleted
@@ -79,45 +83,33 @@
/atom/movable/Initialize(mapload, ...)
. = ..()
+
+ update_emissive_block()
+
+ if(opacity)
+ AddElement(/datum/element/light_blocking)
+ if(light_system == MOVABLE_LIGHT)
+ AddComponent(/datum/component/overlay_lighting)
+ if(light_system == DIRECTIONAL_LIGHT)
+ AddComponent(/datum/component/overlay_lighting, is_directional = TRUE)
+
+/atom/movable/proc/update_emissive_block()
+ if(emissive_overlay)
+ overlays -= emissive_overlay
+
switch(blocks_emissive)
if(EMISSIVE_BLOCK_GENERIC)
var/mutable_appearance/gen_emissive_blocker = mutable_appearance(icon, icon_state, plane = EMISSIVE_PLANE, alpha = src.alpha)
gen_emissive_blocker.color = GLOB.em_block_color
gen_emissive_blocker.dir = dir
gen_emissive_blocker.appearance_flags |= appearance_flags
+ emissive_overlay = gen_emissive_blocker
overlays += gen_emissive_blocker
if(EMISSIVE_BLOCK_UNIQUE)
render_target = ref(src)
em_block = new(src, render_target)
+ emissive_overlay = em_block
overlays += list(em_block)
- if(opacity)
- AddElement(/datum/element/light_blocking)
- if(light_system == MOVABLE_LIGHT)
- AddComponent(/datum/component/overlay_lighting)
- if(light_system == DIRECTIONAL_LIGHT)
- AddComponent(/datum/component/overlay_lighting, is_directional = TRUE)
-
-/*
-
-///Updates this movables emissive overlay
-/atom/movable/proc/update_emissive_block()
- if(!blocks_emissive)
- return
- else if (blocks_emissive == EMISSIVE_BLOCK_GENERIC)
- var/mutable_appearance/gen_emissive_blocker = emissive_blocker(icon, icon_state, alpha = src.alpha, appearance_flags = src.appearance_flags)
- gen_emissive_blocker.dir = dir
- if(blocks_emissive == EMISSIVE_BLOCK_UNIQUE)
- if(!em_block)
- render_target = ref(src)
- em_block = new(src, render_target)
- return em_block
-
-/atom/movable/update_overlays()
- . = ..()
-
- . += update_emissive_block()
-
-*/
/atom/movable/vv_get_dropdown()
. = ..()
diff --git a/code/game/jobs/job/marine/squads.dm b/code/game/jobs/job/marine/squads.dm
index 75f6622506a9..26cca9019639 100644
--- a/code/game/jobs/job/marine/squads.dm
+++ b/code/game/jobs/job/marine/squads.dm
@@ -232,6 +232,17 @@
roundstart = FALSE
locked = TRUE
+/datum/squad/marine/solardevils
+ name = SQUAD_SOLAR
+ equipment_color = "#5a2c2c"
+ chat_color = "#5a2c2c"
+ radio_freq = SOF_FREQ
+ minimap_color = "#5a2c2c"
+
+ active = FALSE
+ roundstart = FALSE
+ locked = TRUE
+
//############################### UPP Squads
/datum/squad/upp
diff --git a/code/game/jobs/job/special/provost.dm b/code/game/jobs/job/special/provost.dm
index c746e9f500b0..d5dd9dc3940f 100644
--- a/code/game/jobs/job/special/provost.dm
+++ b/code/game/jobs/job/special/provost.dm
@@ -14,6 +14,14 @@
/datum/job/special/provost/inspector
title = JOB_PROVOST_INSPECTOR
+//Provost Inspector
+/datum/job/special/provost/inspector/chief
+ title = JOB_PROVOST_CINSPECTOR
+
+//Provost Marshal
+/datum/job/special/provost/marshal/deputy
+ title = JOB_PROVOST_DMARSHAL
+
//Provost Marshal
/datum/job/special/provost/marshal
title = JOB_PROVOST_MARSHAL
diff --git a/code/game/objects/effects/landmarks/landmarks.dm b/code/game/objects/effects/landmarks/landmarks.dm
index 2d76357db895..f5f0e0b50c05 100644
--- a/code/game/objects/effects/landmarks/landmarks.dm
+++ b/code/game/objects/effects/landmarks/landmarks.dm
@@ -121,6 +121,15 @@
GLOB.monkey_spawns -= src
return ..()
+/obj/effect/landmark/lizard_spawn
+ name = "lizard spawn"
+ icon_state = "lizard_spawn"
+
+/obj/effect/landmark/lizard_spawn/Initialize(mapload, ...)
+ . = ..()
+ if(prob(66))
+ new /mob/living/simple_animal/hostile/retaliate/giant_lizard(loc)
+
/obj/effect/landmark/latewhiskey
name = "Whiskey Outpost Late join"
diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm
index 73e5b86a69eb..0017e41219e5 100644
--- a/code/game/objects/items/devices/multitool.dm
+++ b/code/game/objects/items/devices/multitool.dm
@@ -52,10 +52,32 @@
next_scan = world.time + 15
var/area/A = get_area(src)
- var/APC = A? A.get_apc() : null
+ var/atom/APC = A? A.get_apc() : null
if(APC)
to_chat(user, SPAN_NOTICE("The local APC is located at [SPAN_BOLD("[get_dist(src, APC)] units [dir2text(Get_Compass_Dir(src, APC))]")]."))
user.balloon_alert(user, "[get_dist(src, APC)] units [dir2text(Get_Compass_Dir(src, APC))]")
+ if(user.client)
+ //Create the appearance so we have something to apply the filter to.
+ var/mutable_appearance/apc_appearance = new(APC)
+ apc_appearance.filters += list("type" = "outline", "size" = 1, "color" = COLOR_GREEN)
+ //Make it an image we can give to the client
+ var/image/final_image = image(apc_appearance)
+
+ final_image.layer = WALL_OBJ_LAYER
+ final_image.plane = GAME_PLANE
+ final_image.loc = get_turf(APC)
+ final_image.dir = apc_appearance.dir
+ final_image.alpha = 225
+ user.client.images += final_image
+ addtimer(CALLBACK(src, PROC_REF(remove_apc_highlight), user.client, final_image), 1.4 SECONDS)
+
+
else
to_chat(user, SPAN_WARNING("ERROR: Could not locate local APC."))
user.balloon_alert(user, "could not locate!")
+
+/obj/item/device/multitool/proc/remove_apc_highlight(client/user_client, image/highlight_image)
+ if(!user_client)
+ return
+ user_client.images -= highlight_image
+
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index 58d8cd8d8d5f..95fc3bb76f2c 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -88,7 +88,7 @@
/obj/item/device/encryptionkey/cmo
name = "Chief Medical Officer's Encryption Key"
icon_state = "cmo_key"
- channels = list(RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_INTEL = TRUE, SQUAD_MARINE_1 = FALSE, SQUAD_MARINE_2 = FALSE, SQUAD_MARINE_3 = FALSE, SQUAD_MARINE_4 = FALSE, SQUAD_MARINE_5 = FALSE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE)
/obj/item/device/encryptionkey/med
name = "Medical Radio Encryption Key"
diff --git a/code/game/objects/items/reagent_containers/food.dm b/code/game/objects/items/reagent_containers/food.dm
index f4b2213904d2..b4e895956aee 100644
--- a/code/game/objects/items/reagent_containers/food.dm
+++ b/code/game/objects/items/reagent_containers/food.dm
@@ -10,9 +10,16 @@
volume = 50 //Sets the default container amount for all food items.
flags_atom = CAN_BE_SYRINGED
var/filling_color = COLOR_WHITE //Used by sandwiches.
+ var/last_dropped_by
/obj/item/reagent_container/food/Initialize()
. = ..()
if (!pixel_x && !pixel_y)
src.pixel_x = rand(-6.0, 6) //Randomizes postion
src.pixel_y = rand(-6.0, 6)
+ RegisterSignal(src, COMSIG_ITEM_DROPPED, PROC_REF(update_last_dropped_by))
+
+/obj/item/reagent_container/food/proc/update_last_dropped_by(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ last_dropped_by = user
diff --git a/code/game/objects/items/storage/toolkit.dm b/code/game/objects/items/storage/toolkit.dm
index 4339723cac5b..d54201f73650 100644
--- a/code/game/objects/items/storage/toolkit.dm
+++ b/code/game/objects/items/storage/toolkit.dm
@@ -42,5 +42,15 @@
else
icon_state = icon_full
+/obj/item/storage/toolkit/full/fill_preset_inventory()
+ new /obj/item/stack/cable_coil/random(src)
+ new /obj/item/circuitboard/apc(src)
+ new /obj/item/circuitboard/apc(src)
+ new /obj/item/circuitboard/apc(src)
+ new /obj/item/cell/high(src)
+ new /obj/item/cell/high(src)
+ new /obj/item/clothing/glasses/welding(src)
+
+
/obj/item/storage/toolkit/empty/fill_preset_inventory()
return
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 6f721d9725e5..fab66dc1a1a5 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -398,6 +398,10 @@
sound = pick('sound/voice/pred_pain1.ogg','sound/voice/pred_pain2.ogg','sound/voice/pred_pain3.ogg','sound/voice/pred_pain4.ogg','sound/voice/pred_pain5.ogg',5;'sound/voice/pred_pain_rare1.ogg')
if("clownstep")
sound = pick('sound/effects/clownstep1.ogg', 'sound/effects/clownstep2.ogg')
+ if("giant_lizard_growl")
+ sound = pick('sound/effects/giant_lizard_growl1.ogg', 'sound/effects/giant_lizard_growl2.ogg')
+ if("giant_lizard_hiss")
+ sound = pick('sound/effects/giant_lizard_hiss1.ogg', 'sound/effects/giant_lizard_hiss2.ogg')
return sound
/client/proc/generate_sound_queues()
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index aa87f157173c..2270685c0cf6 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -93,8 +93,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list(
/datum/admins/proc/admin_force_selfdestruct,
/client/proc/check_round_statistics,
/client/proc/force_teleporter,
- /client/proc/matrix_editor,
- /datum/admins/proc/open_shuttlepanel
+ /client/proc/matrix_editor
))
GLOBAL_LIST_INIT(admin_verbs_ban, list(
@@ -143,6 +142,7 @@ GLOBAL_LIST_INIT(admin_verbs_minor_event, list(
/client/proc/toggle_hardcore_perma,
/client/proc/toggle_bypass_joe_restriction,
/client/proc/toggle_joe_respawns,
+ /datum/admins/proc/open_shuttlepanel
))
GLOBAL_LIST_INIT(admin_verbs_major_event, list(
@@ -220,6 +220,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list(
/datum/admins/proc/view_href_log, /*shows the server HREF log for this round*/
/datum/admins/proc/view_tgui_log, /*shows the server TGUI log for this round*/
/client/proc/admin_blurb,
+ /datum/admins/proc/open_shuttlepanel,
))
GLOBAL_LIST_INIT(admin_verbs_debug_advanced, list(
diff --git a/code/modules/admin/tabs/event_tab.dm b/code/modules/admin/tabs/event_tab.dm
index a155f2a37614..fa0f43780031 100644
--- a/code/modules/admin/tabs/event_tab.dm
+++ b/code/modules/admin/tabs/event_tab.dm
@@ -718,7 +718,7 @@
set name = "Toggle Remote Control"
set category = "Admin.Events"
- if(!check_rights(R_SPAWN))
+ if(!check_rights(R_MOD|R_DEBUG))
return
remote_control = !remote_control
diff --git a/code/modules/admin/verbs/shuttlepanel.dm b/code/modules/admin/verbs/shuttlepanel.dm
index 9ae9cedd7992..72d69511aff6 100644
--- a/code/modules/admin/verbs/shuttlepanel.dm
+++ b/code/modules/admin/verbs/shuttlepanel.dm
@@ -3,7 +3,7 @@
set name = "Shuttle Manipulator"
set desc = "Opens the shuttle manipulator UI."
- if(!check_rights(R_ADMIN|R_DEBUG|R_HOST))
+ if(!check_rights(R_EVENT|R_DEBUG|R_HOST))
return
SSshuttle.tgui_interact(usr)
diff --git a/code/modules/autowiki/autowiki.dm b/code/modules/autowiki/autowiki.dm
index 5f8fe0a10a1f..0b0d1212dd82 100644
--- a/code/modules/autowiki/autowiki.dm
+++ b/code/modules/autowiki/autowiki.dm
@@ -40,7 +40,7 @@
for(var/list in output)
all_page_names += autowiki.include_template(list["title"])
- total_output += json_encode(list("title" = autowiki.page, "text" = all_page_names))
+ total_output += json_encode(list("title" = autowiki.page, "text" = all_page_names.Join(" "))) + "\n"
continue
var/output = autowiki.generate()
diff --git a/code/modules/autowiki/pages/xeno_stats.dm b/code/modules/autowiki/pages/xeno_stats.dm
new file mode 100644
index 000000000000..720f4fe04898
--- /dev/null
+++ b/code/modules/autowiki/pages/xeno_stats.dm
@@ -0,0 +1,54 @@
+/datum/autowiki/xeno_stats
+ generate_multiple = TRUE
+ page = "Template:Autowiki/Content/XenoStats"
+
+/datum/autowiki/xeno_stats/generate_multiple()
+ var/output = list()
+
+ for(var/mob/living/carbon/xenomorph/xeno as anything in subtypesof(/mob/living/carbon/xenomorph))
+ if(IS_AUTOWIKI_SKIP(xeno))
+ continue
+
+ var/mob/living/carbon/xenomorph/xeno_instance = new xeno()
+
+ var/strains = list(null) + xeno_instance.caste.available_strains
+ for(var/datum/xeno_strain/strain as anything in strains)
+ var/datum/xeno_strain/strain_instance = null
+ if(!isnull(strain))
+ strain_instance = new strain()
+
+ output += template_from_xeno(xeno_instance, strain_instance)
+
+ qdel(strain_instance)
+
+ qdel(xeno_instance)
+
+ return output
+
+/datum/autowiki/xeno_stats/proc/template_from_xeno(mob/living/carbon/xenomorph/xeno, datum/xeno_strain/strain)
+ var/name = xeno.caste_type
+ if(!isnull(strain))
+ strain.apply_strain(xeno)
+ name = "[strain.name] [name]"
+
+ var/xeno_data = list(
+ "name" = name,
+ "health" = xeno.maxHealth,
+ "armor" = xeno.armor_deflection,
+ "plasma" = xeno.plasma_max,
+ "plasma_regeneration" = xeno.plasma_gain,
+ "minimum_slash_damage" = xeno.melee_damage_lower,
+ "maximum_slash_damage" = xeno.melee_damage_upper,
+ "claw_strength" = xeno.claw_type,
+ "evasion" = xeno.evasion,
+ // Mob speed is relatively non-obvious, we we convert it into a very intuitive
+ // range for wiki-readability.
+ "speed" = humanize_speed(xeno.speed),
+ "explosion_resistance" = xeno.caste.xeno_explosion_resistance,
+ )
+
+ var/sanitized_name = url_encode(replacetext(name, " ", "_"))
+ return list(list(title = "Template:Autowiki/Content/XenoStats/[sanitized_name]", text = include_template("Autowiki/XenoStats", xeno_data)))
+
+/datum/autowiki/xeno_stats/proc/humanize_speed(speed)
+ return speed * -1 + 1
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 4d0ef56b9fdd..52aa1be1408b 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -419,11 +419,7 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
if(prefs.lastchangelog != GLOB.changelog_hash) //bolds the changelog button on the interface so we know there are updates.
winset(src, "infowindow.changelog", "background-color=#ED9F9B;font-style=bold")
- if(prefs.toggle_prefs & TOGGLE_FULLSCREEN)
- toggle_fullscreen(TRUE)
- else
- toggle_fullscreen(FALSE)
-
+ update_fullscreen()
var/file = file2text("config/donators.txt")
var/lines = splittext(file, "\n")
@@ -733,12 +729,16 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
if(WHISPER_CHANNEL)
winset(src, "srvkeybinds-[REF(key)]", "parent=default;name=[key];command=whisper")
-/client/proc/toggle_fullscreen(new_value)
- if(new_value)
- winset(src, "mainwindow", "is-maximized=false;can-resize=false;titlebar=false;menu=menu")
+/client/proc/update_fullscreen()
+ if(prefs.toggle_prefs & TOGGLE_FULLSCREEN)
+ winset(src, "mainwindow", "is-fullscreen=true;menu=")
else
- winset(src, "mainwindow", "is-maximized=false;can-resize=true;titlebar=true;menu=menu")
- winset(src, "mainwindow", "is-maximized=true")
+ winset(src, "mainwindow", "is-fullscreen=false;menu=menu")
+
+ if(prefs.adaptive_zoom)
+ adaptive_zoom()
+ else if(prefs.auto_fit_viewport)
+ fit_viewport()
/// Attempts to make the client orbit the given object, for administrative purposes.
/// If they are not an observer, will try to aghost them.
diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm
index cb3114f6c5a4..97449e5952ae 100644
--- a/code/modules/client/preferences_toggles.dm
+++ b/code/modules/client/preferences_toggles.dm
@@ -226,7 +226,7 @@
prefs.toggle_prefs ^= TOGGLE_FULLSCREEN
prefs.save_preferences()
- toggle_fullscreen(prefs.toggle_prefs & TOGGLE_FULLSCREEN)
+ update_fullscreen()
/client/verb/toggle_ambient_occlusion()
set name = "Toggle Ambient Occlusion"
diff --git a/code/modules/clothing/gloves/marine_gloves.dm b/code/modules/clothing/gloves/marine_gloves.dm
index 6da362da30f4..d36073bb48a0 100644
--- a/code/modules/clothing/gloves/marine_gloves.dm
+++ b/code/modules/clothing/gloves/marine_gloves.dm
@@ -42,6 +42,12 @@
item_state = "insulated"
siemens_coefficient = 0
+/obj/item/clothing/gloves/marine/insulated/black
+ name = "marine insulated black gloves"
+ desc = "These marine gloves will protect the wearer from electric shocks and shrapnal. Standard issue for properly-equipped Marines."
+ icon_state = "black"
+ item_state = "black"
+
/obj/item/clothing/gloves/marine/black
name = "marine black combat gloves"
adopts_squad_color = FALSE
diff --git a/code/modules/clothing/head/head.dm b/code/modules/clothing/head/head.dm
index a441256ae450..f9c8adbf9a77 100644
--- a/code/modules/clothing/head/head.dm
+++ b/code/modules/clothing/head/head.dm
@@ -489,6 +489,10 @@
icon_state = "coblackberet"
desc = "A beret with the USCM Military Police insignia emblazoned on it."
+/obj/item/clothing/head/beret/marine/mp/provost/chief
+ name = "\improper USCM provost command beret"
+ icon_state = "pvciberet"
+
/obj/item/clothing/head/beret/marine/mp/tis
name = "\improper UAAC-TIS Special Agent Beret"
icon_state = "berettis"
diff --git a/code/modules/clothing/suits/marine_coat.dm b/code/modules/clothing/suits/marine_coat.dm
index a69f0922bf5b..fc2a76698029 100644
--- a/code/modules/clothing/suits/marine_coat.dm
+++ b/code/modules/clothing/suits/marine_coat.dm
@@ -253,6 +253,11 @@
flags_atom = NO_SNOW_TYPE|NO_NAME_OVERRIDE
valid_accessory_slots = list(ACCESSORY_SLOT_ARMBAND, ACCESSORY_SLOT_RANK, ACCESSORY_SLOT_DECOR)
+/obj/item/clothing/suit/storage/jacket/marine/provost/chief
+ name = "\improper Provost Command Jacket"
+ desc = "A crisp jacket with the Provost sigil."
+ icon_state = "provostci_jacket"
+
/obj/item/clothing/suit/storage/jacket/marine/provost/coat
name = "\improper Provost Coat"
desc = "The crisp coat of a Provost Officer."
diff --git a/code/modules/clothing/under/marine_uniform.dm b/code/modules/clothing/under/marine_uniform.dm
index 52635c63600a..a9aec9544641 100644
--- a/code/modules/clothing/under/marine_uniform.dm
+++ b/code/modules/clothing/under/marine_uniform.dm
@@ -381,6 +381,12 @@
icon_state = "provost_tml"
worn_state = "provost_tml"
+/obj/item/clothing/under/marine/mp/provost/chief
+ name = "\improper Provost Command Uniform"
+ desc = "The crisp uniform of a commanding member of the Provost Office."
+ icon_state = "provost_ci"
+ worn_state = "provost_ci"
+
/obj/item/clothing/under/marine/mp/provost/marshal
name = "\improper Provost Marshal Uniform"
desc = "The crisp uniform of a Provost Marshal."
diff --git a/code/modules/clothing/under/ties.dm b/code/modules/clothing/under/ties.dm
index 4e4041971807..cea9e26869b0 100644
--- a/code/modules/clothing/under/ties.dm
+++ b/code/modules/clothing/under/ties.dm
@@ -393,6 +393,11 @@
desc = "A fire-resistant shoulder patch, worn by the men and women of the Falling Falcons, the 2nd battalion of the 4th brigade of the USCM."
icon_state = "fallingfalconspatch"
+/obj/item/clothing/accessory/patch/devils
+ name = "USCM Solar Devils patch"
+ desc = "A fire-resistant shoulder patch, worn by the men and women of the 3rd Battalion 'Solar Devils', part of the USCM 2nd Division, 1st Regiment."
+ icon_state = "solardevilspatch"
+
/obj/item/clothing/accessory/patch/forecon
name = "USCM Force Reconnaissance patch"
desc = "A fire-resistant shoulder patch, worn by the men and women of the USS Hanyut, USCM FORECON."
diff --git a/code/modules/cm_marines/marines_consoles.dm b/code/modules/cm_marines/marines_consoles.dm
index bd7becf8f63e..778f3f4f18bf 100644
--- a/code/modules/cm_marines/marines_consoles.dm
+++ b/code/modules/cm_marines/marines_consoles.dm
@@ -925,9 +925,11 @@ GLOBAL_LIST_EMPTY_TYPED(crewmonitor, /datum/crewmonitor)
JOB_PROVOST_CMARSHAL = 00,
JOB_GENERAL = 00,
JOB_PROVOST_SMARSHAL = 01,//Grade O9
- JOB_PROVOST_MARSHAL = 02,//Grade O8
+ JOB_PROVOST_MARSHAL = 02,//Grade O7
+ JOB_PROVOST_DMARSHAL = 03,//Grade O6
JOB_COLONEL = 04,//Grade O6
- JOB_PROVOST_INSPECTOR = 04,
+ JOB_PROVOST_CINSPECTOR = 05,
+ JOB_PROVOST_INSPECTOR = 06,
// 10-19: Command
JOB_CO = 10,
JOB_XO = 11,
diff --git a/code/modules/cm_marines/specialist.dm b/code/modules/cm_marines/specialist.dm
index 9fbb240a1178..72247641c488 100644
--- a/code/modules/cm_marines/specialist.dm
+++ b/code/modules/cm_marines/specialist.dm
@@ -131,6 +131,7 @@
name = "Demolitionist Set"
role_name = "Demo"
skill_to_give = SKILL_SPEC_ROCKET
+ trait_to_give = "demo"
kit_typepath = /obj/item/storage/box/spec/demolitionist
/datum/specialist_set/sadar/redeem_set(mob/living/redeemer, kit)
@@ -146,6 +147,7 @@
name = "Scout Set"
role_name = "Scout"
skill_to_give = SKILL_SPEC_SCOUT
+ trait_to_give = "scout"
kit_typepath = /obj/item/storage/box/spec/scout
/datum/specialist_set/scout/redeem_set(mob/living/redeemer, kit)
@@ -161,6 +163,7 @@
name = "Sniper Set"
role_name = "Sniper"
skill_to_give = SKILL_SPEC_SNIPER
+ trait_to_give = "sniper"
kit_typepath = /obj/item/storage/box/spec/sniper
incompatible_sets = list(
/datum/specialist_set/anti_mat_sniper,
@@ -170,6 +173,7 @@
name = "Anti-Materiel Sniper Set"
role_name = "Heavy Sniper"
skill_to_give = SKILL_SPEC_SNIPER
+ trait_to_give = "antimat_sniper"
kit_typepath = /obj/item/storage/box/spec/sniper/anti_materiel
incompatible_sets = list(
/datum/specialist_set/sniper,
@@ -179,10 +183,12 @@
name = "Heavy Grenadier Set"
role_name = "Grenadier"
skill_to_give = SKILL_SPEC_GRENADIER
+ trait_to_give = "grenadier"
kit_typepath = /obj/item/storage/box/spec/heavy_grenadier
/datum/specialist_set/pyro
name = "Pyro Set"
role_name = "Pyro"
skill_to_give = SKILL_SPEC_PYRO
+ trait_to_give = "pyro"
kit_typepath = /obj/item/storage/box/spec/pyro
diff --git a/code/modules/gear_presets/uscm.dm b/code/modules/gear_presets/uscm.dm
index 0450e74353ef..eedd0f79f4a7 100644
--- a/code/modules/gear_presets/uscm.dm
+++ b/code/modules/gear_presets/uscm.dm
@@ -962,3 +962,304 @@
dress_under = list(/obj/item/clothing/under/marine/dress/blues/senior)
dress_over = list(/obj/item/clothing/suit/storage/jacket/marine/dress/blues/officer)
dress_hat = list(/obj/item/clothing/head/marine/dress_cover/officer)
+
+//############ Solar Devils (PvE Marines) #############
+//## Random Headware/Mask Setup ##//
+/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)
+
+//## Rifleman ##//
+/datum/equipment_preset/uscm/rifleman_pve
+ name = "USCM Solar Devils Rifleman"
+ flags = EQUIPMENT_PRESET_EXTRA|EQUIPMENT_PRESET_MARINE
+
+ access = list(ACCESS_MARINE_PREP)
+ assignment = JOB_SQUAD_MARINE
+ rank = JOB_SQUAD_MARINE
+ paygrades = list(PAY_SHORT_ME1 = JOB_PLAYTIME_TIER_0, PAY_SHORT_ME2 = JOB_PLAYTIME_TIER_1, PAY_SHORT_ME3 = JOB_PLAYTIME_TIER_3)
+ role_comm_title = "RFN"
+ skills = /datum/skills/rifleman_pve
+ auto_squad_name = SQUAD_SOLAR
+
+ minimap_icon = "private"
+ dress_under = list(/obj/item/clothing/under/marine/dress/blues)
+ dress_over = list(/obj/item/clothing/suit/storage/jacket/marine/dress/blues)
+
+/datum/equipment_preset/uscm/rifleman_pve/load_status(mob/living/carbon/human/new_human)
+ new_human.nutrition = NUTRITION_MAX
+
+/datum/equipment_preset/uscm/rifleman_pve/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/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/tool/screwdriver/tactical(new_human), WEAR_R_EAR)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/medium(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/binoculars/range/designator(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/medical/bruise_pack(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/medical/splint(new_human), WEAR_IN_JACKET)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/storage/webbing/five_slots(new_human), WEAR_ACCESSORY)
+
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar/tactical(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wirecutters/tactical(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wrench(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/multitool(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/weldingtool/largetank(new_human), WEAR_IN_ACCESSORY)
+
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/satchel/intel/chestrig(new_human), WEAR_BACK)
+ 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/insulated/black(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/toolkit/full(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/box/packet/high_explosive(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/box/packet/incendiary(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/motiondetector(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/sheet/metal/large_stack(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/sheet/plasteel/medium_stack(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/devils(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(new_human), WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1/ap(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1/ap(new_human.back), WEAR_IN_BELT)
+ spawn_marine_fluff_items(new_human)
+
+//## Corpsman ##//
+/datum/equipment_preset/uscm/medic_pve
+ name = "USCM Solar Devils Platoon Corpsman"
+ flags = EQUIPMENT_PRESET_EXTRA|EQUIPMENT_PRESET_MARINE
+
+ access = list(ACCESS_MARINE_PREP, ACCESS_MARINE_MEDPREP, ACCESS_MARINE_MEDBAY)
+ assignment = JOB_PLT_MED
+ rank = JOB_SQUAD_MEDIC
+ paygrades = list(PAY_SHORT_ME2 = JOB_PLAYTIME_TIER_0, PAY_SHORT_ME3 = JOB_PLAYTIME_TIER_1, PAY_SHORT_ME4 = JOB_PLAYTIME_TIER_3)
+ role_comm_title = "HM"
+ skills = /datum/skills/combat_medic_pve
+ auto_squad_name = SQUAD_SOLAR
+
+ minimap_icon = "medic"
+
+ utility_under = list(/obj/item/clothing/under/marine/medic)
+ dress_under = list(/obj/item/clothing/under/marine/dress/blues)
+ dress_over = list(/obj/item/clothing/suit/storage/jacket/marine/dress/blues)
+
+/datum/equipment_preset/uscm/medic_pve/load_status(mob/living/carbon/human/new_human)
+ new_human.nutrition = NUTRITION_NORMAL
+
+/datum/equipment_preset/uscm/medic_pve/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/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/tool/screwdriver/tactical(new_human), WEAR_R_EAR)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/medium(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/binoculars/range/designator(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/medical/bruise_pack(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/medical/splint(new_human), WEAR_IN_JACKET)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/storage/webbing/five_slots(new_human), WEAR_ACCESSORY)
+
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar/tactical(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wirecutters/tactical(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wrench(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/multitool(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/weldingtool/largetank(new_human), WEAR_IN_ACCESSORY)
+
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/satchel/intel/chestrig(new_human), WEAR_BACK)
+ 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/magazine/large(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1/ap(new_human.back), WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1/ap(new_human.back), WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine/insulated/black(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/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/firstaid/adv(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/surgical(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/surgery/synthgraft, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/defibrillator(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/healthanalyzer(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/devils(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/medical/lifesaver/full(new_human), WEAR_WAIST)
+ spawn_marine_fluff_items(new_human)
+
+//## Smartgunner ##//
+/datum/equipment_preset/uscm/sg_pve
+ name = "USCM Solar Devils Smartgunner"
+ flags = EQUIPMENT_PRESET_EXTRA|EQUIPMENT_PRESET_MARINE
+
+ access = list(ACCESS_MARINE_PREP, ACCESS_MARINE_SMARTPREP)
+ assignment = JOB_SQUAD_SMARTGUN
+ rank = JOB_SQUAD_SMARTGUN
+ paygrades = list(PAY_SHORT_ME3 = JOB_PLAYTIME_TIER_0, PAY_SHORT_ME4 = JOB_PLAYTIME_TIER_1, PAY_SHORT_ME5 = JOB_PLAYTIME_TIER_3)
+ role_comm_title = "SG"
+ skills = /datum/skills/smartgunner_pve
+ auto_squad_name = SQUAD_SOLAR
+
+ minimap_icon = "smartgunner"
+ dress_under = list(/obj/item/clothing/under/marine/dress/blues)
+ dress_over = list(/obj/item/clothing/suit/storage/jacket/marine/dress/blues)
+
+/datum/equipment_preset/uscm/sg_pve/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/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/tool/screwdriver/tactical(new_human), WEAR_R_EAR)
+
+ 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/clothing/accessory/patch/devils(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/binoculars/range/designator(new_human), WEAR_IN_JACKET)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/storage/webbing/five_slots(new_human), WEAR_ACCESSORY)
+
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar/tactical(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wirecutters/tactical(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wrench(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/multitool(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/weldingtool/simple(new_human), WEAR_IN_ACCESSORY)
+
+ 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/belt/gun/smartgunner/full(new_human), WEAR_WAIST)
+ 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/gloves/marine/insulated/black(new_human), WEAR_HANDS)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/night/m56_goggles(new_human), WEAR_EYES)
+ 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/pouch/firstaid/full/alternate(new_human), WEAR_R_STORE)
+ spawn_marine_fluff_items(new_human)
+
+/datum/equipment_preset/uscm/sg_pve/load_status(mob/living/carbon/human/new_human)
+
+//## Team Leader ##//
+/datum/equipment_preset/uscm/tl_pve
+ name = "USCM Solar Devils Team Leader"
+ flags = EQUIPMENT_PRESET_EXTRA|EQUIPMENT_PRESET_MARINE
+
+ access = list(ACCESS_MARINE_PREP, ACCESS_MARINE_TL_PREP, ACCESS_MARINE_DROPSHIP)
+ assignment = JOB_SQUAD_TEAM_LEADER
+ rank = JOB_SQUAD_TEAM_LEADER
+ paygrades = list(PAY_SHORT_ME3 = JOB_PLAYTIME_TIER_0, PAY_SHORT_ME4 = JOB_PLAYTIME_TIER_1, PAY_SHORT_ME5 = JOB_PLAYTIME_TIER_3)
+ role_comm_title = "FTL"
+ skills = /datum/skills/tl_pve
+ auto_squad_name = SQUAD_SOLAR
+
+ minimap_icon = "tl"
+
+/datum/equipment_preset/uscm/tl_pve/load_status(mob/living/carbon/human/new_human)
+ new_human.nutrition = NUTRITION_NORMAL
+
+/datum/equipment_preset/uscm/tl_pve/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/head/helmet/marine/leader(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo/tl(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/screwdriver/tactical(new_human), WEAR_R_EAR)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/medium(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/binoculars/range/designator(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/medical/bruise_pack(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/medical/splint(new_human), WEAR_IN_JACKET)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/storage/webbing/five_slots(new_human), WEAR_ACCESSORY)
+
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar/tactical(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wirecutters/tactical(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wrench(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/multitool(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/weldingtool/largetank(new_human), WEAR_IN_ACCESSORY)
+
+ 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/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/insulated/black(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/toolkit/full(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/box/packet/high_explosive(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/motiondetector(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/defenses/handheld/sentry(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/devils(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(new_human), WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1/ap(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1/ap(new_human.back), WEAR_IN_BELT)
+ spawn_marine_fluff_items(new_human)
+
+//## Squad Leader ##//
+/datum/equipment_preset/uscm/sl_pve
+ name = "USCM Solar Devils Platoon Leader"
+ flags = EQUIPMENT_PRESET_EXTRA|EQUIPMENT_PRESET_MARINE
+
+ access = list(ACCESS_MARINE_PREP, ACCESS_MARINE_LEADER, ACCESS_MARINE_DROPSHIP)
+ assignment = JOB_PLT_SL
+ rank = JOB_SQUAD_LEADER
+ paygrades = list(PAY_SHORT_ME5 = JOB_PLAYTIME_TIER_0, PAY_SHORT_ME6 = JOB_PLAYTIME_TIER_1, PAY_SHORT_ME7 = JOB_PLAYTIME_TIER_3)
+ role_comm_title = "SL"
+ skills = /datum/skills/sl_pve
+ auto_squad_name = SQUAD_SOLAR
+
+ minimap_icon = "sl"
+
+/datum/equipment_preset/uscm/sl_pve/load_status(mob/living/carbon/human/new_human)
+ new_human.nutrition = NUTRITION_NORMAL
+
+/datum/equipment_preset/uscm/sl_pve/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/head/helmet/marine/leader(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/marine/cryo/tl(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/screwdriver/tactical(new_human), WEAR_R_EAR)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/medium/leader(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/binoculars/range/designator(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/medical/bruise_pack(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/medical/splint(new_human), WEAR_IN_JACKET)
+
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/storage/webbing/five_slots(new_human), WEAR_ACCESSORY)
+
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar/tactical(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wirecutters/tactical(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/wrench(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/multitool(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/weldingtool/largetank(new_human), WEAR_IN_ACCESSORY)
+
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/satchel/intel/chestrig(new_human), WEAR_BACK)
+ 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/insulated/black(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/toolkit/full(new_human), WEAR_IN_BACK)
+ 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/firstaid/adv(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/surgical(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/motiondetector(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/sheet/metal/large_stack(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/devils(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(new_human), WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1/ap(new_human.back), WEAR_IN_BELT)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/m41aMK1/ap(new_human.back), WEAR_IN_BELT)
+ spawn_marine_fluff_items(new_human)
diff --git a/code/modules/gear_presets/uscm_event.dm b/code/modules/gear_presets/uscm_event.dm
index 96ebb63d3089..9ef15ff955ae 100644
--- a/code/modules/gear_presets/uscm_event.dm
+++ b/code/modules/gear_presets/uscm_event.dm
@@ -328,6 +328,46 @@
new_human.equip_to_slot_or_del(new /obj/item/device/flash(new_human), WEAR_IN_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/restraint/handcuffs(new_human), WEAR_IN_JACKET)
+/datum/equipment_preset/uscm_event/provost/inspector/chief
+ name = "Provost Chief Inspector"
+
+ assignment = JOB_PROVOST_CINSPECTOR
+ rank = JOB_PROVOST_CINSPECTOR
+ paygrades = list(PAY_SHORT_PVCI = JOB_PLAYTIME_TIER_0)
+ role_comm_title = "PvCI"
+ flags = EQUIPMENT_PRESET_EXTRA
+
+ dress_under = list(/obj/item/clothing/under/marine/dress/blues/senior)
+ dress_over = list(/obj/item/clothing/suit/storage/jacket/marine/dress/blues/officer)
+ dress_hat = list(/obj/item/clothing/head/marine/dress_cover/officer)
+
+/datum/equipment_preset/uscm_event/provost/inspector/chief/load_gear(mob/living/carbon/human/new_human)
+ var/back_item = /obj/item/storage/backpack/satchel/sec
+ if (new_human.client && new_human.client.prefs && (new_human.client.prefs.backbag == 1))
+ back_item = /obj/item/storage/backpack/security
+
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/provost(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/mp/provost/chief(new_human), WEAR_BODY)
+ 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/gloves/black(new_human), WEAR_HANDS)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/gun/m4a3/mod88(new_human), WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/jacket/marine/provost/chief(new_human), WEAR_JACKET)
+ if(new_human.disabilities & NEARSIGHTED)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses/sechud/prescription(new_human), WEAR_EYES)
+ else
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses/sechud(new_human), WEAR_EYES)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/beret/marine/mp/provost/chief(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/taperecorder(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/general/large(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/listening_bug/radio_linked/hc/pvst(new_human), WEAR_IN_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/listening_bug/radio_linked/hc/pvst(new_human), WEAR_IN_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/listening_bug/radio_linked/hc/pvst(new_human), WEAR_IN_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/MP/provost/light/flexi(new_human.back), WEAR_IN_BACK)
+
+ new_human.equip_to_slot_or_del(new /obj/item/device/flash(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/restraint/handcuffs(new_human), WEAR_IN_JACKET)
+
/datum/equipment_preset/uscm_event/provost/inspector/advisor
name = "Provost Advisor"
@@ -337,8 +377,19 @@
role_comm_title = "PvA"
flags = EQUIPMENT_PRESET_EXTRA
+/datum/equipment_preset/uscm_event/provost/marshal/deputy
+ name = "Provost Deputy Marshal (MO6)"
+ minimum_age = 45
+ skills = /datum/skills/general
+
+ assignment = JOB_PROVOST_DMARSHAL
+ rank = JOB_PROVOST_DMARSHAL
+ paygrades = list(PAY_SHORT_PVDM = JOB_PLAYTIME_TIER_0)
+ role_comm_title = PAY_SHORT_PVDM
+ flags = EQUIPMENT_PRESET_EXTRA
+
/datum/equipment_preset/uscm_event/provost/marshal
- name = "Provost Marshal (MO6)"
+ name = "Provost Marshal (MO7)"
minimum_age = 45
skills = /datum/skills/general
@@ -376,7 +427,7 @@
new_human.equip_to_slot_or_del(new /obj/item/handheld_distress_beacon/provost(new_human.back), WEAR_IN_BACK)
/datum/equipment_preset/uscm_event/provost/marshal/sector
- name = "Provost Sector Marshal (MO7)"
+ name = "Provost Sector Marshal (MO9)"
minimum_age = 50
assignment = JOB_PROVOST_SMARSHAL
diff --git a/code/modules/mob/living/carbon/human/human_abilities.dm b/code/modules/mob/living/carbon/human/human_abilities.dm
index 9976fe37a4ff..58e4b617a19c 100644
--- a/code/modules/mob/living/carbon/human/human_abilities.dm
+++ b/code/modules/mob/living/carbon/human/human_abilities.dm
@@ -118,6 +118,33 @@
/datum/action/human_action/smartpack/repair_form/cooldown_check(obj/item/storage/backpack/marine/smartpack/S)
return S.repairing
+
+/datum/action/human_action/psychic_whisper
+ name = "Psychic Whipser"
+ action_icon_state = "cultist_channel_hivemind"
+
+/datum/action/human_action/psychic_whisper/action_activate()
+ . = ..()
+ if(!ishuman(owner))
+ return
+ var/mob/living/carbon/human/human_owner = owner
+
+ if(human_owner.client.prefs.muted & MUTE_IC)
+ to_chat(human_owner, SPAN_DANGER("You cannot whisper (muted)."))
+ return
+
+ var/list/target_list = list()
+ for(var/mob/living/carbon/possible_target in view(7, human_owner))
+ if(possible_target == human_owner || !possible_target.client)
+ continue
+ target_list += possible_target
+
+ var/mob/living/carbon/target_mob = tgui_input_list(human_owner, "Target", "Send a Psychic Whisper to whom?", target_list, theme = "hive_status")
+ if(!target_mob)
+ return
+
+ human_owner.psychic_whisper(target_mob)
+
/*
CULT
*/
diff --git a/code/modules/mob/living/carbon/human/powers/human_powers.dm b/code/modules/mob/living/carbon/human/powers/human_powers.dm
index fef87c2f3263..8daf9a7488d7 100644
--- a/code/modules/mob/living/carbon/human/powers/human_powers.dm
+++ b/code/modules/mob/living/carbon/human/powers/human_powers.dm
@@ -186,7 +186,7 @@
var/whisper = strip_html(input("Message:", "Psychic Whisper") as text|null)
if(whisper)
log_say("PsychicWhisper: [key_name(src)]->[target_mob.key] : [whisper] (AREA: [get_area_name(loc)])")
- to_chat(target_mob, SPAN_XENOWARNING(" You hear a strange, alien voice in your head... [whisper]"))
+ to_chat(target_mob, SPAN_XENOWARNING(" You hear a strange, alien voice in your head... [SPAN_PSYTALK(whisper)]"))
to_chat(src, SPAN_XENOWARNING(" You said: \"[whisper]\" to [target_mob]"))
for (var/mob/dead/observer/ghost as anything in GLOB.observer_list)
if(!ghost.client || isnewplayer(ghost))
@@ -195,7 +195,7 @@
var/rendered_message
var/human_track = "(F)"
var/target_track = "(F)"
- rendered_message = SPAN_XENOLEADER("PsychicWhisper: [real_name][human_track] to [target_mob.real_name][target_track], '[whisper]'")
+ rendered_message = SPAN_XENOLEADER("PsychicWhisper: [real_name][human_track] to [target_mob.real_name][target_track], '[SPAN_PSYTALK(whisper)]'")
ghost.show_message(rendered_message, SHOW_MESSAGE_AUDIBLE)
return
diff --git a/code/modules/mob/living/carbon/xenomorph/Abilities.dm b/code/modules/mob/living/carbon/xenomorph/Abilities.dm
index 6c220f41ad45..35b532136f90 100644
--- a/code/modules/mob/living/carbon/xenomorph/Abilities.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Abilities.dm
@@ -210,9 +210,9 @@
if(whisper)
log_say("PsychicWhisper: [key_name(xeno_player)]->[target_mob.key] : [whisper] (AREA: [get_area_name(target_mob)])")
if(!istype(target_mob, /mob/living/carbon/xenomorph))
- to_chat(target_mob, SPAN_XENOQUEEN("You hear a strange, alien voice in your head. \"[whisper]\""))
+ to_chat(target_mob, SPAN_XENOQUEEN("You hear a strange, alien voice in your head. \"[SPAN_PSYTALK(whisper)]\""))
else
- to_chat(target_mob, SPAN_XENOQUEEN("You hear the voice of [xeno_player] resonate in your head. \"[whisper]\""))
+ to_chat(target_mob, SPAN_XENOQUEEN("You hear the voice of [xeno_player] resonate in your head. \"[SPAN_PSYTALK(whisper)]\""))
to_chat(xeno_player, SPAN_XENONOTICE("You said: \"[whisper]\" to [target_mob]"))
for(var/mob/dead/observer/ghost as anything in GLOB.observer_list)
@@ -222,7 +222,7 @@
var/rendered_message
var/xeno_track = "(F)"
var/target_track = "(F)"
- rendered_message = SPAN_XENOLEADER("PsychicWhisper: [xeno_player.real_name][xeno_track] to [target_mob.real_name][target_track], '[whisper]'")
+ rendered_message = SPAN_XENOLEADER("PsychicWhisper: [xeno_player.real_name][xeno_track] to [target_mob.real_name][target_track], '[SPAN_PSYTALK(whisper)]'")
ghost.show_message(rendered_message, SHOW_MESSAGE_AUDIBLE)
return ..()
@@ -254,9 +254,9 @@
continue
target_list += possible_target
if(!istype(possible_target, /mob/living/carbon/xenomorph))
- to_chat(possible_target, SPAN_XENOQUEEN("You hear a strange, alien voice in your head. \"[whisper]\""))
+ to_chat(possible_target, SPAN_XENOQUEEN("You hear a strange, alien voice in your head. \"[SPAN_PSYTALK(whisper)]\""))
else
- to_chat(possible_target, SPAN_XENOQUEEN("You hear the voice of [xeno_player] resonate in your head. \"[whisper]\""))
+ to_chat(possible_target, SPAN_XENOQUEEN("You hear the voice of [xeno_player] resonate in your head. \"[SPAN_PSYTALK(whisper)]\""))
FOR_DVIEW_END
if(!length(target_list))
return
@@ -269,7 +269,7 @@
if(ghost.client.prefs.toggles_chat & CHAT_GHOSTHIVEMIND)
var/rendered_message
var/xeno_track = "(F)"
- rendered_message = SPAN_XENOLEADER("PsychicRadiance: [xeno_player.real_name][xeno_track] to [targetstring], '[whisper]'")
+ rendered_message = SPAN_XENOLEADER("PsychicRadiance: [xeno_player.real_name][xeno_track] to [targetstring], '[SPAN_PSYTALK(whisper)]'")
ghost.show_message(rendered_message, SHOW_MESSAGE_AUDIBLE)
return ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/Embryo.dm b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
index 61ba87cd001b..4a82c4fb0b86 100644
--- a/code/modules/mob/living/carbon/xenomorph/Embryo.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
@@ -341,7 +341,7 @@
if(burstcount)
step(larva_embryo, pick(GLOB.cardinals))
- if(GLOB.round_statistics)
+ if(GLOB.round_statistics && (ishuman(victim)) && (SSticker.current_state == GAME_STATE_PLAYING) && (ROUND_TIME > 1 MINUTES))
GLOB.round_statistics.total_larva_burst++
GLOB.larva_burst_by_hive[hive] = (GLOB.larva_burst_by_hive[hive] || 0) + 1
burstcount++
diff --git a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
index 63ade0cbbe3d..0db94bc8ed45 100644
--- a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
+++ b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
@@ -258,14 +258,16 @@
if(M.frenzy_aura > 0)
damage += (M.frenzy_aura * FRENZY_DAMAGE_MULTIPLIER)
+ M.animation_attack_on(src)
//Somehow we will deal no damage on this attack
if(!damage)
playsound(M.loc, 'sound/weapons/alien_claw_swipe.ogg', 25, 1)
- M.animation_attack_on(src)
+
M.visible_message(SPAN_DANGER("[M] lunges at [src]!"), \
SPAN_DANGER("We lunge at [src]!"), null, 5, CHAT_TYPE_XENO_COMBAT)
return XENO_ATTACK_ACTION
+ handle_blood_splatter(get_dir(M.loc, loc))
last_damage_data = create_cause_data(initial(M.name), M)
M.visible_message(SPAN_DANGER("[M] [M.slashes_verb] [src]!"), \
SPAN_DANGER("We [M.slash_verb] [src]!"), null, 5, CHAT_TYPE_XENO_COMBAT)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm b/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
index e6c4a76c9353..12d94f0d5ec2 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Drone.dm
@@ -83,6 +83,7 @@
weed_food_states_flipped = list("Drone_1","Drone_2","Drone_3")
/mob/living/carbon/xenomorph/drone/tutorial
+ AUTOWIKI_SKIP(TRUE)
/mob/living/carbon/xenomorph/drone/tutorial/gib(datum/cause_data/cause = create_cause_data("gibbing", src))
death(cause, 1)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
index 9d42eb982d2e..17ec90a96bda 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
@@ -18,6 +18,8 @@
minimap_icon = "facehugger"
/mob/living/carbon/xenomorph/facehugger
+ AUTOWIKI_SKIP(TRUE)
+
name = XENO_CASTE_FACEHUGGER
caste_type = XENO_CASTE_FACEHUGGER
speak_emote = list("hisses")
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm b/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
index 7df87f63cf3a..6ad08817f84a 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
@@ -30,6 +30,8 @@
minimap_icon = "hellhound"
/mob/living/carbon/xenomorph/hellhound
+ AUTOWIKI_SKIP(TRUE)
+
caste_type = XENO_CASTE_HELLHOUND
name = XENO_CASTE_HELLHOUND
desc = "A disgusting beast from hell, it has four menacing spikes growing from its head."
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm
index 8dc427e2c55e..3f873f3635c3 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm
@@ -25,6 +25,8 @@
minimum_evolve_time = 0
/mob/living/carbon/xenomorph/larva
+ AUTOWIKI_SKIP(TRUE)
+
name = XENO_CASTE_LARVA
caste_type = XENO_CASTE_LARVA
speak_emote = list("hisses")
@@ -68,24 +70,38 @@
pass_flags.flags_can_pass_all = PASS_ALL^PASS_OVER_THROW_ITEM
/mob/living/carbon/xenomorph/larva/corrupted
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_CORRUPTED
/mob/living/carbon/xenomorph/larva/alpha
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_ALPHA
/mob/living/carbon/xenomorph/larva/bravo
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_BRAVO
/mob/living/carbon/xenomorph/larva/charlie
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_CHARLIE
/mob/living/carbon/xenomorph/larva/delta
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_DELTA
/mob/living/carbon/xenomorph/larva/mutated
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_MUTATED
/mob/living/carbon/xenomorph/larva/predalien
+ AUTOWIKI_SKIP(TRUE)
+
icon_xeno = 'icons/mob/xenos/predalien_larva.dmi'
icon_state = "Predalien Larva"
caste_type = XENO_CASTE_PREDALIEN_LARVA
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
index b60f150c442d..8279d1b2ba2e 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
@@ -30,6 +30,8 @@
minimap_icon = "predalien"
/mob/living/carbon/xenomorph/predalien
+ AUTOWIKI_SKIP(TRUE)
+
caste_type = XENO_CASTE_PREDALIEN
name = "Abomination" //snowflake name
desc = "A strange looking creature with fleshy strands on its head. It appears like a mixture of armor and flesh, smooth, but well carapaced."
@@ -114,6 +116,8 @@ You must still listen to the queen.
. += "It has [predalienkills.kills] kills to its name!"
/mob/living/carbon/xenomorph/predalien/tutorial
+ AUTOWIKI_SKIP(TRUE)
+
should_announce_spawn = FALSE
/mob/living/carbon/xenomorph/predalien/tutorial/gib(datum/cause_data/cause = create_cause_data("gibbing", src))
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
index db682e161e87..037c154ab2dc 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
@@ -252,6 +252,8 @@
return ..()
/mob/living/carbon/xenomorph/queen
+ AUTOWIKI_SKIP(TRUE)
+
caste_type = XENO_CASTE_QUEEN
name = XENO_CASTE_QUEEN
desc = "A huge, looming alien creature. The biggest and the baddest."
@@ -370,28 +372,44 @@
return "heart_t3"
/mob/living/carbon/xenomorph/queen/corrupted
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_CORRUPTED
/mob/living/carbon/xenomorph/queen/forsaken
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_FORSAKEN
/mob/living/carbon/xenomorph/queen/forsaken/combat_ready
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_FORSAKEN
queen_aged = TRUE
/mob/living/carbon/xenomorph/queen/alpha
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_ALPHA
/mob/living/carbon/xenomorph/queen/bravo
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_BRAVO
/mob/living/carbon/xenomorph/queen/charlie
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_CHARLIE
/mob/living/carbon/xenomorph/queen/delta
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_DELTA
/mob/living/carbon/xenomorph/queen/mutated
+ AUTOWIKI_SKIP(TRUE)
+
hivenumber = XENO_HIVE_MUTATED
/mob/living/carbon/xenomorph/queen/combat_ready
diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm
index 9c8b54dad483..131859d0d129 100644
--- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm
+++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm
@@ -1199,8 +1199,8 @@
reporting_id = "renegade"
hivenumber = XENO_HIVE_RENEGADE
prefix = "Renegade "
- color = "#9c7a4d"
- ui_color ="#80705c"
+ color = "#ffae00"
+ ui_color ="#ad732c"
dynamic_evolution = FALSE
allow_queen_evolve = FALSE
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/giant_lizard.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_lizard.dm
new file mode 100644
index 000000000000..6bb0202c2deb
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_lizard.dm
@@ -0,0 +1,756 @@
+#define ATTACK_SLASH 0
+#define ATTACK_BITE 1
+#define LIZARD_SPEED_NORMAL 2.8
+#define LIZARD_SPEED_RETREAT 2.5
+#define LIZARD_SPEED_NORMAL_CLIENT -1
+#define LIZARD_SPEED_RETREAT_CLIENT -1.5
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard
+ name = "giant lizard"
+ desc = "A large, wolf-like reptile. Its eyes are keenly focused on yours."
+ icon = 'icons/mob/mob_64.dmi'
+ icon_state = "Giant Lizard Running"
+ icon_living = "Giant Lizard Running"
+ icon_dead = "Giant Lizard Dead"
+ mob_size = MOB_SIZE_XENO_SMALL
+ maxHealth = 350
+ health = 350
+ icon_size = 64
+ pixel_x = -16
+ old_x = -16
+ base_pixel_x = 0
+ base_pixel_y = -20
+ mobility_flags = MOBILITY_FLAGS_REST_CAPABLE_DEFAULT
+ affected_by_fire = TRUE
+ move_to_delay = LIZARD_SPEED_NORMAL
+ speed = LIZARD_SPEED_NORMAL_CLIENT //speed and move_to_delay are not the same in simplemob code (wow!)
+
+ response_help = "pets"
+ response_disarm = "tries to push away"
+ response_harm = "punches"
+ friendly = "nuzzles"
+ see_in_dark = 50
+
+ speak_chance = 2
+ speak_emote = "hisses"
+ emote_hear = list("hisses.", "growls.", "roars.", "bellows.")
+ emote_see = list("shakes its head.", "wags its tail.", "flicks its tongue.", "yawns.")
+
+ melee_damage_lower = 20
+ melee_damage_upper = 25
+ attack_same = FALSE
+ langchat_color = LIGHT_COLOR_GREEN
+
+ ///Reference to the ZZzzz sleep overlay when resting.
+ var/sleep_overlay
+
+ ///If 0, moves the mob out of attacking into idle state. Used to prevent the mob from chasing down targets that did not mean to hurt it.
+ var/aggression_value = 0
+
+ ///Emotes to play when being pet by a friend.
+ var/list/pet_emotes = list("closes its eyes.", "growls happily.", "wags its tail.", "rolls on the ground.")
+ ///Cooldown to stop generic emote spam.
+ COOLDOWN_DECLARE(emote_cooldown)
+
+ ///Collision callbacks for the pounce proc.
+ var/list/pounce_callbacks = list()
+ ///Are we currently mauling a mob after pouncing them? Used to stop normal attacks on pounced targets.
+ var/is_ravaging = FALSE
+ ///Length of the cooldown for pouncing.
+ var/pounce_cooldown_length = 9 SECONDS
+ ///Cooldown for the pounce ability.
+ COOLDOWN_DECLARE(pounce_cooldown)
+
+ ///How many times the mob is going bleed in the Life() proc.
+ var/bleed_ticks = 0
+ ///Chance of the mob laying down/getting up.
+ var/chance_to_rest = 0
+ ///Is the mob currently running away from a target?
+ var/is_retreating = FALSE
+ ///How many times have we attempted to retreat?
+ var/retreat_attempts = 0
+ ///Tied directly to retreat_attempts. If our retreat fail, then we will completely stop trying to retreat for the length of this cooldown.
+ COOLDOWN_DECLARE(retreat_cooldown)
+
+ ///The food object that the mob is trying to eat.
+ var/food_target
+ ///A list of foods the mob is interested in eating.
+ var/list/acceptable_foods = list(/obj/item/reagent_container/food/snacks/meat, /obj/item/reagent_container/food/snacks/packaged_meal, /obj/item/reagent_container/food/snacks/resin_fruit)
+ ///Is the mob currently eating the food_target?
+ var/is_eating = FALSE
+ ///Cooldown dictating how long the mob will wait between eating food.
+ COOLDOWN_DECLARE(food_cooldown)
+
+ ///Cooldown for the growl emote.
+ COOLDOWN_DECLARE(growl_message)
+ ///Cooldown for when the mob calms down, so the mob doesn't start attacking immediately after calming down.
+ COOLDOWN_DECLARE(calm_cooldown)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/Initialize()
+ . = ..()
+ change_real_name(src, "[name] ([rand(1, 999)])")
+ pounce_callbacks[/mob] = DYNAMIC(/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/pounced_mob_wrapper)
+ pounce_callbacks[/turf] = DYNAMIC(/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/pounced_turf_wrapper)
+ pounce_callbacks[/obj] = DYNAMIC(/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/pounced_obj_wrapper)
+
+//regular pain datum will make the mob die when trying to pounce after taking enough damage.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/initialize_pain()
+ pain = new /datum/pain/xeno(src)
+
+///Proc for growling.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/growl(target_mob, ignore_cooldown = FALSE)
+ if(!COOLDOWN_FINISHED(src, growl_message) && !ignore_cooldown)
+ return
+ if(target_mob)
+ manual_emote("growls at [target_mob].")
+ else
+ manual_emote("growls.")
+ playsound(loc, "giant_lizard_growl", 60)
+ COOLDOWN_START(src, growl_message, rand(10, 14) SECONDS)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/get_status_tab_items()
+ . = ..()
+ . += ""
+ . += "Health: [floor(health)]/[floor(maxHealth)]"
+ if(!COOLDOWN_FINISHED(src, pounce_cooldown))
+ . += "Pounce Cooldown: [COOLDOWN_SECONDSLEFT(src, pounce_cooldown)] seconds"
+
+//the AI gets funky when it gets stunned midcombat. this will help them get back into the fight more organically.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/on_immobilized_trait_loss(datum/source)
+ . = ..()
+ find_target_on_trait_loss()
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/on_knockedout_trait_loss(datum/source)
+ . = ..()
+ find_target_on_trait_loss()
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/on_incapacitated_trait_loss(datum/source)
+ . = ..()
+ find_target_on_trait_loss()
+
+///Proc for handling the AI post-status effect.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/find_target_on_trait_loss()
+ is_retreating = FALSE
+ if(stance > HOSTILE_STANCE_ALERT)
+ FindTarget()
+ MoveToTarget()
+
+//procs for handling sleeping icons when resting
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/AddSleepingIcon()
+ var/image/sleeping_icon = new('icons/mob/hud/hud.dmi', "slept_icon_centered")
+ if(sleep_overlay)
+ return
+ sleep_overlay = sleeping_icon
+ overlays += sleep_overlay
+ addtimer(CALLBACK(src, PROC_REF(RemoveSleepingIcon)), 6 SECONDS)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/RemoveSleepingIcon()
+ if(sleep_overlay)
+ overlays -= sleep_overlay
+ sleep_overlay = null
+
+//The parent proc sets the stance to IDLE which will break the AI if it's in combat
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/stop_moving()
+ walk_to(src, 0)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/update_transform(instant_update = FALSE)
+ if(stat == DEAD)
+ icon_state = icon_dead
+ else if(body_position == LYING_DOWN)
+ if(!HAS_TRAIT(src, TRAIT_INCAPACITATED) && !HAS_TRAIT(src, TRAIT_FLOORED))
+ icon_state = "Giant Lizard Sleeping"
+ else
+ icon_state = "Giant Lizard Knocked Down"
+ else
+ icon_state = icon_living
+ return ..()
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/get_examine_text(mob/user)
+ . = ..()
+ if(stat == DEAD || user == src)
+ desc = "A large, wolf-like reptile."
+ if(user == src)
+ . += SPAN_NOTICE("\nRest on the ground to restore 5% of your health every second.")
+ . += SPAN_NOTICE("You're able to pounce targets by using [client && client.prefs && client.prefs.toggle_prefs & TOGGLE_MIDDLE_MOUSE_CLICK ? "middle-click" : "shift-click"].")
+ . += SPAN_NOTICE("You will aggressively maul targets that are prone. Any click on yourself will be passed down to mobs below you, so feel free to click on your sprite in order to attack pounced targets.")
+ else if((user.faction in faction_group))
+ desc = "[initial(desc)] There's a hint of warmth in them."
+ else
+ desc = initial(desc)
+ if(isxeno(user)) //simplemobs aren't coded to handle larva infection so we'll just let them know
+ . += SPAN_DANGER("This is an unsuitable host for the hive.")
+ return .
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/set_resting(new_resting, silent, instant)
+ . = ..()
+ if(!resting)
+ RemoveSleepingIcon()
+ update_transform()
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/death()
+ playsound(loc, 'sound/effects/giant_lizard_death.ogg', 70)
+ manual_emote("lets out a waning growl.")
+ return ..()
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/attack_hand(mob/living/carbon/human/attacking_mob)
+ . = ..()
+ process_attack_hand(attacking_mob)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/attack_alien(mob/living/carbon/xenomorph/attacking_mob)
+ . = ..()
+ process_attack_hand(attacking_mob)
+
+///Proc for handling attacking the lizard with a hand for BOTH XENOS AND HUMANS.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/process_attack_hand(mob/living/carbon/attacking_mob)
+ if(stat == DEAD)
+ return
+
+ if(!(attacking_mob.faction in faction_group) && !is_eating)
+ Retaliate()
+
+ if(attacking_mob.a_intent == INTENT_HELP && (attacking_mob.faction in faction_group))
+ if(on_fire)
+ adjust_fire_stacks(-5, min_stacks = 0)
+ playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 7)
+ visible_message(SPAN_DANGER("[attacking_mob] tries to put out the fire on [src]!"), \
+ SPAN_WARNING("You try to put out the fire on [src]!"), null, 5)
+ if(fire_stacks <= 0)
+ visible_message(SPAN_DANGER("[attacking_mob] has successfully extinguished the fire on [src]!"), \
+ SPAN_NOTICE("You extinguished the fire on [src]."), null, 5)
+ return
+ if(!resting)
+ chance_to_rest += 15
+ if(resting)
+ chance_to_rest = 0
+ if(COOLDOWN_FINISHED(src, emote_cooldown))
+ COOLDOWN_START(src, emote_cooldown, rand(5, 8) SECONDS)
+ manual_emote(pick(pick(pet_emotes), "stares at [attacking_mob].", "nuzzles [attacking_mob].", "licks [attacking_mob]'s hand."), "nibbles [attacking_mob]'s arm.", "flicks its tongue at [attacking_mob].")
+ if(prob(50))
+ playsound(loc, "giant_lizard_hiss", 25)
+ if(attacking_mob.a_intent == INTENT_DISARM && prob(75))
+ step_to(src, get_step(loc, attacking_mob.dir), 0, LIZARD_SPEED_NORMAL)
+
+//apply blood splatter when attacked by a sufficently damaging sharp weapon
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/attackby(obj/item/weapon, mob/living/carbon/human/attacker)
+ if(weapon.force > 10 && weapon.sharp && attacker.a_intent != INTENT_HELP)
+ handle_blood_splatter(get_dir(attacker.loc, loc))
+ return ..()
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/apply_damage(damage, damagetype, def_zone, used_weapon, sharp, edge, force)
+ Retaliate()
+ aggression_value = clamp(aggression_value + 5, 0, 30)
+ . = ..()
+ var/retreat_chance = abs((health / maxHealth * 100) - 100)
+ if(prob(retreat_chance) && health <= maxHealth * 0.66 && COOLDOWN_FINISHED(src, retreat_cooldown))
+ if(client && !is_retreating)
+ is_retreating = TRUE
+ to_chat(src, SPAN_USERDANGER("Your fight or flight response kicks in, run!"))
+ speed = LIZARD_SPEED_RETREAT_CLIENT
+ addtimer(VARSET_CALLBACK(src, speed, LIZARD_SPEED_NORMAL_CLIENT), 8 SECONDS)
+ addtimer(VARSET_CALLBACK(src, is_retreating, FALSE), 8 SECONDS)
+ else
+ MoveTo(target_mob, 12, TRUE, 8 SECONDS)
+ if(damage >= 10 && damagetype == BRUTE)
+ add_splatter_floor(loc, TRUE)
+ bleed_ticks = clamp(bleed_ticks + ceil(damage / 10), 0, 30)
+
+///Proc that forces the mob to disengage and try to extinguish itself. Will not be called if the mob is already retreating.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/try_to_extinguish()
+ if(is_retreating || !on_fire || client)
+ return
+
+ //forget EVERYTHING. we need to stop the flames!!!
+ stance = HOSTILE_STANCE_ALERT
+ target_mob = null
+ food_target = null
+ is_eating = FALSE
+ manual_emote("hisses in agony!")
+ playsound(src, "giant_lizard_hiss", 40)
+ MoveTo(null, 9, TRUE, 4 SECONDS, FALSE)
+ COOLDOWN_START(src, calm_cooldown, 8 SECONDS)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/IgniteMob()
+ . = ..()
+ if(on_fire)
+ try_to_extinguish()
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/handle_fire()
+ . = ..()
+ if(on_fire)
+ try_to_extinguish()
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/Life(delta_time)
+ //simplemobs don't have innate knockdown reduction so we'll manually lower it here.
+ AdjustKnockDown(-0.5)
+ AdjustStun(-0.5)
+ if(aggression_value > 0)
+ aggression_value--
+
+ if(resting && stat != DEAD)
+ health += maxHealth * 0.05
+ if(prob(33) && !HAS_TRAIT(src, TRAIT_INCAPACITATED) && !HAS_TRAIT(src, TRAIT_FLOORED))
+ AddSleepingIcon()
+
+ if(bleed_ticks)
+ var/is_small_pool = FALSE
+ if(bleed_ticks < 10)
+ is_small_pool = TRUE
+ bleed_ticks--
+ add_splatter_floor(loc, is_small_pool)
+
+ . = ..()
+
+ if(client)
+ return
+
+ //if we haven't gotten hurt in a while, calm down and go back to idling
+ if(aggression_value == 0 && stance == HOSTILE_STANCE_ATTACKING)
+ enemies = list()
+ LoseTarget()
+ if(COOLDOWN_FINISHED(src, emote_cooldown))
+ manual_emote("calms down.")
+ COOLDOWN_START(src, calm_cooldown, 4 SECONDS)
+ COOLDOWN_START(src, emote_cooldown, 3 SECONDS)
+
+ //no longer interested in food when we're in combat
+ if(stance > HOSTILE_STANCE_ALERT)
+ is_eating = FALSE
+
+ if(stance == HOSTILE_STANCE_IDLE)
+ stop_automated_movement = FALSE
+ //if there's a friend on the same tile as us, don't bother getting up (cute!)
+ var/mob/living/carbon/friend = locate(/mob/living/carbon) in get_turf(src)
+ if((friend?.faction in faction_group) && resting)
+ chance_to_rest = 0
+
+ if(prob(chance_to_rest))
+ set_resting(!resting)
+ chance_to_rest = 0
+
+ chance_to_rest += rand(1, 2)
+
+ //if we're resting and something catches our interest, get up
+ if(stance != HOSTILE_STANCE_IDLE && resting)
+ set_resting(FALSE)
+
+ if(target_mob && !is_retreating && target_mob.stat == CONSCIOUS && stance == HOSTILE_STANCE_ATTACKING && COOLDOWN_FINISHED(src, pounce_cooldown) && (prob(75) || get_dist(src, target_mob) <= 5) && (target_mob in view(5, src)))
+ pounce(target_mob)
+
+ if(target_mob || on_fire)
+ return
+
+ //if we are retreating, but we don't have any targets or we're not on fire, stop retreating
+ if(is_retreating)
+ stop_moving()
+ stance = HOSTILE_STANCE_IDLE
+
+ //if we're hungry and we don't have already have our eyes on a snack, try eating food if possible
+ if(!food_target && COOLDOWN_FINISHED(src, food_cooldown))
+ for(var/obj/item/reagent_container/food/snacks/food in view(6, src))
+ if(!is_type_in_list(food, acceptable_foods))
+ continue
+ food_target = food
+ stance = HOSTILE_STANCE_ALERT
+ stop_automated_movement = TRUE
+ MoveTo(food_target)
+ break
+
+ //handling mobs that are invading our personal space
+ if(stance <= HOSTILE_STANCE_ALERT && !food_target && COOLDOWN_FINISHED(src, calm_cooldown))
+ var/intruder_in_sight = FALSE
+ for(var/mob/living/carbon/intruder in view(5, src))
+ if((intruder.faction in faction_group) || intruder.stat != CONSCIOUS || ismonkey(intruder) || intruder.alpha <= 200)
+ continue
+
+ intruder_in_sight = TRUE
+ face_atom(intruder)
+ stance = HOSTILE_STANCE_ALERT
+ stop_automated_movement = TRUE
+ if(get_dist(src, intruder) == 3)
+ growl(intruder)
+ else if(get_dist(src, intruder) <= 2)
+ Retaliate()
+ COOLDOWN_START(src, pounce_cooldown, 1 SECONDS)
+ break
+
+ if(!intruder_in_sight && stance == HOSTILE_STANCE_ALERT)
+ stance = HOSTILE_STANCE_IDLE
+
+ //if we have a snack that we want to eat, but we're not munching on it currently, check if it's close to us.
+ if(food_target && !is_eating)
+ if(!(food_target in view(5, src)))
+ stop_moving()
+ lose_food()
+ //if the food is next to us AND not in the hands of a mob, start eating
+ else if(!check_food_loc(food_target) && Adjacent(food_target))
+ INVOKE_ASYNC(src, PROC_REF(handle_food), food_target)
+
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/bullet_act(obj/projectile/projectile)
+ . = ..()
+ if(projectile.damage)
+ handle_blood_splatter(get_dir(projectile.starting, src))
+ add_splatter_floor(loc, FALSE)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/handle_blood_splatter(splatter_dir)
+ var/obj/effect/temp_visual/dir_setting/bloodsplatter/human/bloodsplatter = new(loc, splatter_dir)
+ bloodsplatter.pixel_y = -2
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/AttackingTarget(inherited_target = target_mob)
+ if(!Adjacent(inherited_target) || is_ravaging || body_position == LYING_DOWN)
+ return
+
+ if(isliving(inherited_target))
+ var/mob/living/target = inherited_target
+ if(target.stat == DEAD)
+ to_chat(src, SPAN_WARNING("[target] is dead. There's nothing interesting about a corpse."))
+ return
+ //decimate mobs that are on the ground
+ if(target.body_position == LYING_DOWN)
+ ravagingattack(target)
+ return target
+
+ face_atom(target)
+ var/attack_type = pick(ATTACK_SLASH, ATTACK_BITE)
+ attacktext = attack_type ? "claws" : "bites"
+ flick_attack_overlay(target, attack_type ? "slash" : "animalbite")
+ playsound(loc, attack_type ? "alien_claw_flesh" : "alien_bite", 25, 1)
+ target.attack_animal(src)
+ animation_attack_on(target)
+
+ if(prob(33))
+ if(client && !is_retreating)
+ is_retreating = TRUE
+ to_chat(src, SPAN_USERDANGER("You gain a rush of speed!"))
+ speed = LIZARD_SPEED_RETREAT_CLIENT
+ addtimer(VARSET_CALLBACK(src, speed, LIZARD_SPEED_NORMAL_CLIENT), 2 SECONDS)
+ addtimer(VARSET_CALLBACK(src, is_retreating, FALSE), 2 SECONDS)
+ else
+ MoveTo(target_mob, 8, TRUE, 2 SECONDS, TRUE) //skirmish around our target
+ return target
+
+//Used to handle attacks when a client is in the mob. Otherwise we'd default to a generic animal attack.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/UnarmedAttack(atom/target)
+ var/tile_attack = FALSE
+ if(target == src) //Clicking self.
+ target = get_turf(src)
+ tile_attack = TRUE
+
+ if(isturf(target) && tile_attack)
+ var/turf/our_turf = target
+ for(var/mob/living/targets in our_turf)
+ if(targets == src)
+ continue
+ target = targets
+ break
+
+ if(isliving(target))
+ AttackingTarget(target)
+ next_move = world.time + 8
+
+///Proc for when the mob finds food and starts DEVOURING it.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/handle_food(obj/item/reagent_container/food/snacks/food)
+ manual_emote("starts gnawing [food].")
+ is_eating = TRUE
+ for(var/times_to_eat = rand(4, 6), times_to_eat--)
+ sleep(rand(1.7, 2.5) SECONDS)
+ if(check_food_loc(food) || stance > HOSTILE_STANCE_ALERT || stat == DEAD)
+ return
+ face_atom(food)
+ playsound(loc,'sound/items/eatfood.ogg', 25, 1)
+
+ for(var/mob/living/carbon/nearest_mob in view(7, src))
+ if(nearest_mob != food.last_dropped_by || (nearest_mob in faction_group))
+ continue
+ face_atom(nearest_mob)
+ manual_emote("stares curiously at [nearest_mob].")
+ faction_group += nearest_mob.faction_group
+ break
+
+ qdel(food)
+ food_target = null
+ is_eating = FALSE
+ stance = HOSTILE_STANCE_IDLE
+ COOLDOWN_START(src, food_cooldown, 30 SECONDS)
+
+///Proc for checking if someone picked our food target.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/check_food_loc(obj/food)
+ if(!ismob(food.loc))
+ return
+
+ var/mob/living/food_holder = food.loc
+ stop_moving()
+ COOLDOWN_START(src, food_cooldown, 15 SECONDS)
+ food_target = null
+ is_eating = FALSE
+ //snagging the food while you're right next to the mob makes it very angry
+ if(get_dist(src, food_holder) <= 2 && !(food_holder.faction in faction_group))
+ Retaliate()
+ return TRUE
+
+ growl(food.loc)
+ stance = HOSTILE_STANCE_IDLE
+ return TRUE
+
+//Proc for when we lose our food target.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/lose_food()
+ stance = HOSTILE_STANCE_IDLE
+ food_target = null
+ is_eating = FALSE
+ COOLDOWN_START(src, food_cooldown, 15 SECONDS)
+
+//Do not stop hunting targets even if they're not visible anymore.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/ListTargets(dist = 9)
+ if(!length(enemies))
+ return list()
+ var/list/see = orange(src, dist)
+ see &= enemies
+ return see
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/evaluate_target(mob/living/target)
+ //we need to check for monkeys else these guys will tear up all the small hosts for xenos
+ if((target.faction == faction || (target.faction in faction_group)) && !attack_same || ismonkey(target) || (target in friends))
+ return FALSE
+ if(target.stat != DEAD)
+ return target
+
+//Mobs in critical state are now fair game. Rip and tear.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/SA_attackable(target_mob)
+ if(isliving(target_mob))
+ var/mob/living/target = target_mob
+ //invisible mobs will still randomly be attacked regardless of this check if the lizard is in combat (intended)
+ if(target.stat == DEAD || target.alpha <= 200)
+ return TRUE //TRUE means it's unattackable (amazing code!)
+ return FALSE
+
+//Immediately retaliate after being attacked.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/Retaliate()
+ if(stat == DEAD || target_mob || on_fire)
+ return
+ aggression_value = clamp(aggression_value + 5, 0, 15)
+
+ . = ..()
+
+ target_mob = FindTarget()
+ if(target_mob)
+ growl(target_mob)
+ MoveToTarget()
+
+ //basic pack behaviour
+ for(var/mob/living/simple_animal/hostile/retaliate/giant_lizard/pack_member in view(7, src))
+ if(pack_member == src || pack_member.target_mob)
+ continue
+ pack_member.Retaliate()
+
+///Proc for moving to targets. walk_to() doesn't check for resting and status effects so we will do it ourselves.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/MoveTo(target, distance = 1, retreat = FALSE, time = 6 SECONDS, return_to_combat = FALSE)
+ if(stat == DEAD || HAS_TRAIT(src, TRAIT_INCAPACITATED) || HAS_TRAIT(src, TRAIT_FLOORED))
+ return
+ if(resting)
+ set_resting(FALSE)
+ if(!retreat)
+ walk_to(src, target ? target : get_turf(src), distance, move_to_delay)
+ return
+ if(!is_retreating)
+ is_retreating = TRUE
+ stop_automated_movement = TRUE
+ stance = HOSTILE_STANCE_ALERT
+ walk_away(src, target ? target : get_turf(src), distance, LIZARD_SPEED_RETREAT)
+ addtimer(CALLBACK(src, PROC_REF(stop_retreat), return_to_combat), time)
+
+//Proc that's called after the retreat has run its course.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/stop_retreat(return_to_combat = FALSE)
+ is_retreating = FALSE
+ //extinguishing is top priority
+ if(on_fire)
+ resist_fire()
+ return
+ if(!return_to_combat)
+ //can't retreat? go back to fighting
+ if(retreat_attempts >= 2)
+ FindTarget()
+ MoveToTarget()
+ retreat_attempts = 0
+ //seems like it's a life or death situation. we will stop trying to run away.
+ COOLDOWN_START(src, retreat_cooldown, 20 SECONDS)
+ return
+ //don't stop retreating if there are non-friendly carbons in view
+ for(var/mob/living/carbon/hostile_mob in view(7, src))
+ if(hostile_mob.faction in faction_group)
+ continue
+ MoveTo(hostile_mob, 10, TRUE, 3 SECONDS, FALSE)
+ retreat_attempts++
+ return
+ retreat_attempts = 0
+ LoseTarget()
+ else
+ FindTarget()
+ MoveToTarget()
+
+//Replaced walk_to() with MoveTo().
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/MoveToTarget()
+ stop_automated_movement = TRUE
+ if(!target_mob || SA_attackable(target_mob))
+ stance = HOSTILE_STANCE_IDLE
+ if(target_mob in ListTargets(10))
+ stance = HOSTILE_STANCE_ATTACKING
+ MoveTo(target_mob)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/resist_fire()
+ visible_message(SPAN_NOTICE("[src] rolls frantically on the ground to extinguish itself!"))
+ adjust_fire_stacks(-10)
+ KnockDown(2)
+ Stun(2)
+
+///Ravaging attack, used for when a mob gets pounced or is on the ground.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/ravagingattack(mob/living/target)
+ if(is_ravaging || !isliving(target))
+ return
+ is_ravaging = TRUE
+ visible_message(SPAN_DANGER("[src] tears into [target] repeatedly!"))
+
+ var/successful_attacks = 0
+ for(var/times_to_attack = 3, times_to_attack > 0, times_to_attack--)
+ if(Adjacent(target))
+ var/damage = rand(melee_damage_lower, melee_damage_upper) * 0.4
+ var/attack_type = pick(ATTACK_SLASH, ATTACK_BITE)
+ attacktext = attack_type ? "claws" : "bites"
+ flick_attack_overlay(target, attack_type ? "slash" : "animalbite")
+ playsound(loc, attack_type ? "alien_claw_flesh" : "alien_bite", 25, 1)
+ target.handle_blood_splatter(get_dir(src.loc, target.loc))
+
+ if(target.body_position == LYING_DOWN)
+ target.apply_damage(damage, BRUTE)
+ target.apply_effect(1, DAZE)
+ shake_camera(target, 1, 2)
+
+ animation_attack_on(target)
+ face_atom(target)
+ sleep(0.5 SECONDS)
+ successful_attacks++
+ if(successful_attacks == 3 && !COOLDOWN_FINISHED(src, pounce_cooldown))
+ to_chat(src, SPAN_BOLDWARNING("The bloodlust invigorates you! You will be ready to pounce much sooner."))
+ COOLDOWN_START(src, pounce_cooldown, COOLDOWN_TIMELEFT(src, pounce_cooldown) * 0.5)
+ is_ravaging = FALSE
+
+//POUNCE PROCS AND WRAPPERS
+///////////////////////////
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/pounce(target)
+ if(stat == DEAD || HAS_TRAIT(src, TRAIT_INCAPACITATED) || HAS_TRAIT(src, TRAIT_FLOORED))
+ return
+ if(!COOLDOWN_FINISHED(src, pounce_cooldown))
+ to_chat(src, SPAN_WARNING("You can't pounce again that fast! You need to wait [COOLDOWN_SECONDSLEFT(src, pounce_cooldown)] seconds."))
+ return
+
+ COOLDOWN_START(src, pounce_cooldown, pounce_cooldown_length)
+ var/pounce_distance = clamp((get_dist(src, target)), 1, 5)
+ manual_emote("pounces at [target]!")
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/atom/movable, throw_atom), target, pounce_distance, SPEED_FAST, src, null, LOW_LAUNCH, PASS_OVER_THROW_MOB, null, pounce_callbacks)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/pounced_mob_wrapper(mob/living/pounced_mob)
+ pounced_mob(pounced_mob)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/pounced_mob(mob/living/pounced_mob)
+ if(stat == DEAD || pounced_mob.stat == DEAD || pounced_mob.mob_size >= MOB_SIZE_BIG || pounced_mob == src)
+ throwing = FALSE
+ return
+
+ if(ishuman(pounced_mob) && (pounced_mob.dir in reverse_nearby_direction(dir)))
+ var/mob/living/carbon/human/human = pounced_mob
+ if(human.check_shields(15, "the pounce")) //Human shield block.
+ visible_message(SPAN_DANGER("[src] slams into [human]!"))
+ KnockDown(1)
+ Stun(1)
+ throwing = FALSE //Reset throwing manually.
+ playsound(human, "bonk", 75, FALSE) //bonk
+ return
+
+ if(isyautja(human))
+ if(human.check_shields(0, "the pounce", 1))
+ visible_message(SPAN_DANGER("[human] blocks the pounce of [src] with the combistick!"))
+ apply_effect(3, WEAKEN)
+ throwing = FALSE
+ playsound(human, "bonk", 75, FALSE)
+ return
+ else if(prob(75)) //Body slam.
+ visible_message(SPAN_DANGER("[human] body slams [src]!"))
+ KnockDown(3)
+ Stun(3)
+ throwing = FALSE
+ playsound(loc, 'sound/weapons/alien_knockdown.ogg', 25, 1)
+ return
+ if(iscolonysynthetic(human) && prob(60))
+ visible_message(SPAN_DANGER("[human] withstands being pounced and slams down [src]!"))
+ KnockDown(1.5)
+ Stun(1.5)
+ throwing = FALSE
+ playsound(loc, 'sound/weapons/alien_knockdown.ogg', 25, 1)
+ return
+
+ playsound(loc, "giant_lizard_hiss", 25)
+ pounced_mob.KnockDown(0.5)
+ step_to(src, pounced_mob)
+ if(!client)
+ ravagingattack(pounced_mob)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/pounced_turf(turf/turf_target)
+ if(!turf_target.density)
+ for(var/mob/living/mob in turf_target)
+ pounced_mob(mob)
+ break
+ else
+ turf_launch_collision(turf_target)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/pounced_turf_wrapper(turf/turf_target)
+ pounced_turf(turf_target)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/pounced_obj_wrapper(obj/O)
+ pounced_obj(O)
+
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/proc/pounced_obj(obj/O)
+ // Unconscious or dead, or not throwing but used pounce
+ if(stat != CONSCIOUS)
+ obj_launch_collision(O)
+ return
+
+ if(!istype(O, /obj/structure/surface/table) && !istype(O, /obj/structure/surface/rack))
+ O.hitby(src) //This resets throwing.
+
+//Middle mouse button/shift click to pounce.
+/mob/living/simple_animal/hostile/retaliate/giant_lizard/click(atom/clicked_atom, list/mods)
+ if(mods["shift"] && !mods["middle"])
+ if(client && client.prefs && !(client.prefs.toggle_prefs & TOGGLE_MIDDLE_MOUSE_CLICK))
+ pounce(clicked_atom)
+ return TRUE
+
+ if(mods["middle"] && !mods["shift"])
+ if(client && client.prefs && client.prefs.toggle_prefs & TOGGLE_MIDDLE_MOUSE_CLICK)
+ pounce(clicked_atom)
+ return TRUE
+ return ..()
+
+
+///CLIENT EMOTES
+////////////////
+
+/datum/emote/living/giant_lizard
+ mob_type_allowed_typecache = list(/mob/living/simple_animal/hostile/retaliate/giant_lizard)
+
+/datum/emote/living/giant_lizard/growl
+ key = "growl"
+ message = "growls."
+ sound = "giant_lizard_growl"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/giant_lizard/hiss
+ key = "hiss"
+ message = "hisses."
+ sound = "giant_lizard_hiss"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+#undef ATTACK_SLASH
+#undef ATTACK_BITE
+#undef LIZARD_SPEED_NORMAL
+#undef LIZARD_SPEED_RETREAT
+#undef LIZARD_SPEED_NORMAL_CLIENT
+#undef LIZARD_SPEED_RETREAT_CLIENT
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm
index 4c16c60d4835..64ab6541b2d2 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm
@@ -27,7 +27,7 @@
continue
if(isliving(A))
var/mob/living/M = A
- if(!attack_same && M.faction != faction)
+ if(evaluate_target(M))
enemies |= M
for(var/mob/living/simple_animal/hostile/retaliate/H in around)
@@ -35,6 +35,7 @@
H.enemies |= enemies
return 0
-/mob/living/simple_animal/hostile/retaliate/adjustBruteLoss(damage)
- ..(damage)
+/mob/living/simple_animal/hostile/retaliate/apply_damage(damage, damagetype, def_zone, used_weapon, sharp, edge, force)
Retaliate()
+ return ..()
+
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index f3b8da1a2d76..878212e52d32 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -1,26 +1,49 @@
+#define OVERLAY_FIRE_LAYER -1
+
/mob/living/simple_animal
name = "animal"
icon = 'icons/mob/animal.dmi'
health = 20
maxHealth = 20
+ ///Higher speed is slower, negative speed is faster.
+ speed = 0
+
+
+ melee_damage_lower = 0
+ melee_damage_upper = 0
+ attacktext = "attacks"
+ attack_sound = null
+ //Attacktext is the mob deal 0 damaage.
+ friendly = "nuzzles"
+ can_crawl = FALSE
+ black_market_value = 25
+ dead_black_market_value = 0
+
+ mobility_flags = MOBILITY_FLAGS_LYING_CAPABLE_DEFAULT
+
var/icon_living = ""
var/icon_dead = ""
- var/icon_gib = null //We only try to show a gibbing animation if this exists.
+ var/icon_gib = null
var/list/speak = list()
var/speak_chance = 0
- var/list/emote_hear = list() //Hearable emotes
- var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps
+ ///Emotes that can be heard by other mobs.
+ var/list/emote_hear = list()
+ ///Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps.
+ var/list/emote_see = list()
var/turns_per_move = 1
var/turns_since_move = 0
universal_speak = 0 //No, just no.
var/meat_amount = 0
var/meat_type
- var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals.
- var/wander = 1 // Does the mob wander around when idle?
- var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it.
+ ///Use this to temporarely stop random movement or to if you write special movement code for animals.
+ var/stop_automated_movement = 0
+ ///Does the mob wander around when idle?
+ var/wander = 1
+ ///When set to 1 this stops the animal from moving when someone is pulling it.
+ var/stop_automated_movement_when_pulled = 1
//Interaction
var/response_help = "tries to help"
@@ -31,32 +54,27 @@
//Temperature effect
var/minbodytemp = 250
var/maxbodytemp = 350
- var/heat_damage_per_tick = 3 //amount of damage applied if animal's body temperature is higher than maxbodytemp
- var/cold_damage_per_tick = 2 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp
+ ///amount of damage applied if animal's body temperature is higher than maxbodytemp
+ var/heat_damage_per_tick = 3
+ ///same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp
+ var/cold_damage_per_tick = 2
+
+ ///Will this mob be affected by fire/napalm? Set to FALSE for all mobs as the implications could be weird due to not being tested for all simple mobs.
+ var/affected_by_fire = FALSE
//Atmos effect - Yes, you can make creatures that require phoron or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage
+ //Leaving something at 0 means it's off - has no maximum
var/min_oxy = 5
- var/max_oxy = 0 //Leaving something at 0 means it's off - has no maximum
+ var/max_oxy = 0
var/min_tox = 0
var/max_tox = 1
var/min_co2 = 0
var/max_co2 = 5
var/min_n2 = 0
var/max_n2 = 0
- var/unsuitable_atoms_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above
- speed = 0 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster
-
- //LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly
- melee_damage_lower = 0
- melee_damage_upper = 0
- attacktext = "attacks"
- attack_sound = null
- friendly = "nuzzles" //If the mob does no damage with it's attack
- can_crawl = FALSE
- black_market_value = 25
- dead_black_market_value = 0
-
- mobility_flags = MOBILITY_FLAGS_LYING_CAPABLE_DEFAULT
+ ///This damage is taken when atmos doesn't fit all the requirements above
+ var/unsuitable_atoms_damage = 2
+ var/fire_overlay
/mob/living/simple_animal/Initialize()
. = ..()
@@ -74,8 +92,63 @@
/mob/living/simple_animal/updatehealth()
return
-/mob/living/simple_animal/Life(delta_time)
+/mob/living/simple_animal/rejuvenate()
+ health = maxHealth
+ SSmob.living_misc_mobs += src
+ return ..()
+/mob/living/simple_animal/get_examine_text(mob/user)
+ . = ..()
+ if(stat == DEAD)
+ . += SPAN_BOLDWARNING("[user == src ? "You are" : "It is"] DEAD. Kicked the bucket.")
+ else
+ var/percent = floor((health / maxHealth * 100))
+ switch(percent)
+ if(95 to INFINITY)
+ . += SPAN_NOTICE("[user == src ? "You look" : "It looks"] quite healthy.")
+ if(75 to 94)
+ . += SPAN_NOTICE("[user == src ? "You look" : "It looks"] slightly injured.")
+ if(50 to 74)
+ . += SPAN_DANGER("[user == src ? "You look" : "It looks"] injured.")
+ if(25 to 49)
+ . += SPAN_DANGER("[user == src ? "You are bleeding" : "It bleeds"] with gory wounds.")
+ if(-INFINITY to 24)
+ . += SPAN_BOLDWARNING("[user == src ? "You are" : "It is"] heavily injured and limping badly.")
+
+/mob/living/simple_animal/handle_fire()
+ if(..())
+ return
+ apply_damage(fire_reagent.intensityfire * 0.5, BURN)
+
+/mob/living/simple_animal/IgniteMob()
+ if(!affected_by_fire)
+ return
+ return ..()
+
+/mob/living/simple_animal/update_fire()
+ if(!on_fire)
+ overlays -= fire_overlay
+ if(on_fire && fire_reagent)
+ var/image/fire_overlay_image
+ if(mob_size >= MOB_SIZE_BIG)
+ if((body_position != LYING_DOWN))
+ fire_overlay_image = image("icon"='icons/mob/xenos/overlay_effects64x64.dmi', "icon_state"="alien_fire", "layer" = OVERLAY_FIRE_LAYER)
+ else
+ fire_overlay_image = image("icon"='icons/mob/xenos/overlay_effects64x64.dmi', "icon_state"="alien_fire_lying", "layer" = OVERLAY_FIRE_LAYER)
+ else
+ fire_overlay_image = image("icon" = 'icons/mob/xenos/effects.dmi', "icon_state"="alien_fire", "layer" = OVERLAY_FIRE_LAYER)
+
+ fire_overlay_image.pixel_y -= pixel_y
+ fire_overlay_image.pixel_x -= pixel_x
+ fire_overlay_image.appearance_flags |= RESET_COLOR|RESET_ALPHA
+ fire_overlay_image.color = fire_reagent.burncolor
+ fire_overlay_image.color = fire_reagent.burncolor
+ overlays += fire_overlay_image
+ fire_overlay = fire_overlay_image
+
+/mob/living/simple_animal/Life(delta_time)
+ if(affected_by_fire)
+ handle_fire()
//Health
if(stat == DEAD)
if(health > 0)
@@ -240,32 +313,46 @@
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
apply_damage(damage, BRUTE)
-
-/mob/living/simple_animal/attack_hand(mob/living/carbon/human/M as mob)
+/mob/living/simple_animal/attack_hand(mob/living/carbon/human/attacking_mob)
..()
- switch(M.a_intent)
-
+ switch(attacking_mob.a_intent)
if(INTENT_HELP)
if (health > 0)
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(SPAN_NOTICE("[M] [response_help] [src]"), SHOW_MESSAGE_VISIBLE)
+ playsound(loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 7)
+ visible_message(SPAN_NOTICE("[attacking_mob] [response_help] [src]."))
+ return 1
if(INTENT_GRAB)
- if(M == src || anchored)
- return 0
- M.start_pulling(src)
+ attacking_mob.start_pulling(src)
+ return 1
+ if(INTENT_DISARM)
+ visible_message(SPAN_NOTICE("[attacking_mob] [response_disarm] [src]."))
+ attacking_mob.animation_attack_on(src)
+ attacking_mob.flick_attack_overlay(src, "disarm")
return 1
- if(INTENT_HARM, INTENT_DISARM)
- apply_damage(harm_intent_damage, BRUTE)
- for(var/mob/O in viewers(src, null))
- if ((O.client && !( O.blinded )))
- O.show_message(SPAN_DANGER("[M] [response_harm] [src]"), SHOW_MESSAGE_VISIBLE)
+ if(INTENT_HARM)
+ var/datum/unarmed_attack/attack = attacking_mob.species.unarmed
+ if(!attack.is_usable(attacking_mob))
+ attack = attacking_mob.species.secondary_unarmed
+ return 0
- return
+ attacking_mob.animation_attack_on(src)
+ attacking_mob.flick_attack_overlay(src, "punch")
+
+ var/extra_cqc_dmg = 0
+ if(attacking_mob.skills)
+ extra_cqc_dmg = attacking_mob.skills?.get_skill_level(SKILL_CQC)
+ var/final_damage = 0
+
+ playsound(loc, attack.attack_sound, 25, 1)
+ visible_message(SPAN_DANGER("[attacking_mob] [response_harm] [src]!"), null, null, 5)
+
+ final_damage = attack.damage + extra_cqc_dmg + harm_intent_damage
+ apply_damage(final_damage, BRUTE, src, sharp = attack.sharp, edge = attack.edge)
+ return 1
/mob/living/simple_animal/can_be_pulled_by(mob/pulling_mob)
if(locate(/obj/item/explosive/plastic) in contents)
@@ -328,6 +415,9 @@
/mob/living/simple_animal/adjustBruteLoss(damage)
health = clamp(health - damage, 0, maxHealth)
+/mob/living/simple_animal/adjustFireLoss(damage)
+ health = clamp(health - damage, 0, maxHealth)
+
/mob/living/simple_animal/proc/SA_attackable(target_mob)
if (isliving(target_mob))
var/mob/living/L = target_mob
@@ -378,3 +468,5 @@
if(user && error_msg)
to_chat(user, SPAN_WARNING("You aren't sure how to inject this animal!"))
return FALSE
+
+#undef OVERLAY_FIRE_LAYER
diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm
index 4c2c83d18257..c11d07d64a23 100644
--- a/code/modules/mob/new_player/preferences_setup.dm
+++ b/code/modules/mob/new_player/preferences_setup.dm
@@ -188,6 +188,10 @@
var/J = job_pref_to_gear_preset()
if(isnull(preview_dummy))
preview_dummy = new()
+
+ preview_dummy.blocks_emissive = FALSE
+ preview_dummy.update_emissive_block()
+
clear_equipment()
if(refresh_limb_status)
for(var/obj/limb/L in preview_dummy.limbs)
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index aff7deeae3b5..80fce329e8cf 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -1420,7 +1420,7 @@ and you're good to go.
if(projectile_to_fire.ammo.bonus_projectiles_amount)
var/obj/projectile/BP
for(var/i in 1 to projectile_to_fire.ammo.bonus_projectiles_amount)
- BP = new /obj/projectile(attacked_mob.loc, create_cause_data(initial(name), user))
+ BP = new /obj/projectile(null, create_cause_data(initial(name), user))
BP.generate_bullet(GLOB.ammo_list[projectile_to_fire.ammo.bonus_projectiles_type], 0, NO_FLAGS)
BP.accuracy = floor(BP.accuracy * projectile_to_fire.accuracy/initial(projectile_to_fire.accuracy)) //Modifies accuracy of pellets per fire_bonus_projectiles.
BP.damage *= damage_buff
diff --git a/code/modules/projectiles/gun_attachables.dm b/code/modules/projectiles/gun_attachables.dm
index e2108364e9ac..757ccd10394b 100644
--- a/code/modules/projectiles/gun_attachables.dm
+++ b/code/modules/projectiles/gun_attachables.dm
@@ -2386,6 +2386,33 @@ Defined in conflicts.dm of the #defines folder.
/obj/item/attachable/stock/type71/New()
..()
+/obj/item/attachable/stock/m60
+ name = "M60 stock"
+ desc = "This isn't supposed to be separated from the gun, how'd this happen?"
+ icon = 'icons/obj/items/weapons/guns/attachments/stock.dmi'
+ icon_state = "m60_stock"
+ attach_icon = "m60_stock"
+ slot = "stock"
+ wield_delay_mod = WIELD_DELAY_NONE
+ flags_attach_features = NO_FLAGS
+ melee_mod = 15
+ size_mod = 0
+
+
+/obj/item/attachable/stock/ppsh
+ name = "PPSh-17b stock"
+ desc = "This isn't supposed to be separated from the gun, how'd this happen?"
+ icon = 'icons/obj/items/weapons/guns/attachments/stock.dmi'
+ icon_state = "ppsh17b_stock"
+ attach_icon = "ppsh17b_stock"
+ slot = "stock"
+ wield_delay_mod = WIELD_DELAY_NONE
+ flags_attach_features = NO_FLAGS
+ melee_mod = 10
+ size_mod = 0
+
+
+
/obj/item/attachable/stock/smg
name = "submachinegun stock"
desc = "A rare ARMAT stock distributed in small numbers to USCM forces. Compatible with the M39, this stock reduces recoil and improves accuracy, but at a reduction to handling and agility. Seemingly a bit more effective in a brawl"
diff --git a/code/modules/projectiles/guns/boltaction.dm b/code/modules/projectiles/guns/boltaction.dm
index a1b8705240a4..a165a112f354 100644
--- a/code/modules/projectiles/guns/boltaction.dm
+++ b/code/modules/projectiles/guns/boltaction.dm
@@ -42,7 +42,7 @@
var/has_openbolt_icon = TRUE
/obj/item/weapon/gun/boltaction/set_gun_attachment_offsets()
- attachable_offset = list("muzzle_x" = 32, "muzzle_y" = 17,"rail_x" = 5, "rail_y" = 18, "under_x" = 25, "under_y" = 14, "stock_x" = 18, "stock_y" = 10)
+ attachable_offset = list("muzzle_x" = 32, "muzzle_y" = 17,"rail_x" = 5, "rail_y" = 18, "under_x" = 25, "under_y" = 14, "stock_x" = 20, "stock_y" = 9)
/obj/item/weapon/gun/boltaction/Initialize(mapload, spawn_empty)
. = ..()
diff --git a/code/modules/projectiles/guns/misc.dm b/code/modules/projectiles/guns/misc.dm
index 5503ab03a1da..f61c0735ade9 100644
--- a/code/modules/projectiles/guns/misc.dm
+++ b/code/modules/projectiles/guns/misc.dm
@@ -83,6 +83,7 @@
starting_attachment_types = list(
/obj/item/attachable/m60barrel,
/obj/item/attachable/bipod/m60,
+ /obj/item/attachable/stock/m60,
)
start_semiauto = FALSE
start_automatic = TRUE
@@ -95,7 +96,7 @@
load_into_chamber()
/obj/item/weapon/gun/m60/set_gun_attachment_offsets()
- attachable_offset = list("muzzle_x" = 34, "muzzle_y" = 16,"rail_x" = 0, "rail_y" = 0, "under_x" = 39, "under_y" = 7, "stock_x" = 0, "stock_y" = 0)
+ attachable_offset = list("muzzle_x" = 37, "muzzle_y" = 16, "rail_x" = 0, "rail_y" = 0, "under_x" = 27, "under_y" = 12, "stock_x" = 10, "stock_y" = 14)
/obj/item/weapon/gun/m60/set_gun_config_values()
@@ -147,9 +148,9 @@
/obj/item/weapon/gun/m60/update_icon()
. = ..()
if(cover_open)
- overlays += "+[base_gun_icon]_cover_open"
+ overlays += image("+[base_gun_icon]_cover_open", pixel_x = -2, pixel_y = 8)
else
- overlays += "+[base_gun_icon]_cover_closed"
+ overlays += image("+[base_gun_icon]_cover_closed", pixel_x = -10, pixel_y = 0)
/obj/item/weapon/gun/m60/able_to_fire(mob/living/user)
. = ..()
diff --git a/code/modules/projectiles/guns/revolvers.dm b/code/modules/projectiles/guns/revolvers.dm
index 180dfac18a9e..2f762cc7aac8 100644
--- a/code/modules/projectiles/guns/revolvers.dm
+++ b/code/modules/projectiles/guns/revolvers.dm
@@ -498,7 +498,7 @@
desc = "A lean .38 made by Smith & Wesson. A timeless classic, from antiquity to the future. This specific model is known to be wildly inaccurate, yet extremely lethal."
icon = 'icons/obj/items/weapons/guns/guns_by_faction/colony.dmi'
icon_state = "sw357"
- item_state = "ny762" //PLACEHOLDER
+ item_state = "sw357"
fire_sound = 'sound/weapons/gun_44mag2.ogg'
current_mag = /obj/item/ammo_magazine/internal/revolver/small
force = 6
diff --git a/code/modules/projectiles/guns/smgs.dm b/code/modules/projectiles/guns/smgs.dm
index 6d125e6915c3..6be0f979c7ad 100644
--- a/code/modules/projectiles/guns/smgs.dm
+++ b/code/modules/projectiles/guns/smgs.dm
@@ -265,10 +265,11 @@
fire_sound = 'sound/weapons/smg_heavy.ogg'
current_mag = /obj/item/ammo_magazine/smg/ppsh
flags_gun_features = GUN_CAN_POINTBLANK|GUN_ANTIQUE
+ starting_attachment_types = list(/obj/item/attachable/stock/ppsh)
var/jammed = FALSE
/obj/item/weapon/gun/smg/ppsh/set_gun_attachment_offsets()
- attachable_offset = list("muzzle_x" = 33, "muzzle_y" = 17,"rail_x" = 15, "rail_y" = 19, "under_x" = 26, "under_y" = 15, "stock_x" = 26, "stock_y" = 15)
+ attachable_offset = list("muzzle_x" = 33, "muzzle_y" = 17,"rail_x" = 15, "rail_y" = 19, "under_x" = 26, "under_y" = 15, "stock_x" = 18, "stock_y" = 15)
/obj/item/weapon/gun/smg/ppsh/set_gun_config_values()
..()
@@ -460,7 +461,7 @@
aim_slowdown = SLOWDOWN_ADS_NONE
/obj/item/weapon/gun/smg/mac15/set_gun_attachment_offsets()
- attachable_offset = list("muzzle_x" = 32, "muzzle_y" = 20,"rail_x" = 16, "rail_y" = 22, "under_x" = 22, "under_y" = 16, "stock_x" = 22, "stock_y" = 16)
+ attachable_offset = list("muzzle_x" = 29, "muzzle_y" = 21,"rail_x" = 13, "rail_y" = 24, "under_x" = 19, "under_y" = 18, "stock_x" = 22, "stock_y" = 16)
/obj/item/weapon/gun/smg/mac15/set_gun_config_values()
..()
diff --git a/code/modules/reagents/chemistry_properties/prop_neutral.dm b/code/modules/reagents/chemistry_properties/prop_neutral.dm
index 7bebb786aa34..9410f6f43ce7 100644
--- a/code/modules/reagents/chemistry_properties/prop_neutral.dm
+++ b/code/modules/reagents/chemistry_properties/prop_neutral.dm
@@ -531,11 +531,11 @@
..()
chem_host.pain.recalculate_pain()
- remove_verb(chem_host, /mob/living/carbon/human/proc/psychic_whisper)
+ remove_action(chem_host, /datum/action/human_action/psychic_whisper)
to_chat(chem_host, SPAN_NOTICE("The pain in your head subsides, and you are left feeling strangely alone."))
/datum/chem_property/neutral/encephalophrasive/reaction_mob(mob/chem_host, method=INGEST, volume, potency)
- add_verb(chem_host, /mob/living/carbon/human/proc/psychic_whisper)
+ give_action(chem_host, /datum/action/human_action/psychic_whisper)
to_chat(chem_host, SPAN_NOTICE("A terrible headache manifests, and suddenly it feels as though your mind is outside of your skull."))
/datum/chem_property/neutral/encephalophrasive/process(mob/living/chem_host, potency = 1, delta_time)
diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm
index 1a4881904801..c566e20d1f9f 100644
--- a/code/modules/security_levels/keycard_authentication.dm
+++ b/code/modules/security_levels/keycard_authentication.dm
@@ -1,6 +1,6 @@
/obj/structure/machinery/keycard_auth
name = "Keycard Authentication Device"
- desc = "This device is used to trigger station functions, which require more than one ID card to authenticate."
+ desc = "This device is used to trigger station functions, which require multiple swipes of an ID card to authenticate."
icon = 'icons/obj/structures/machinery/monitors.dmi'
icon_state = "auth_off"
unacidable = TRUE
@@ -36,9 +36,6 @@
if(active == 1)
//This is not the device that made the initial request. It is the device confirming the request.
if(event_source)
- if(event_source.event_triggered_by == user)
- user.visible_message(SPAN_DANGER("Your ID is rejected, as it is the one that triggered the event!"))
- return
event_source.confirmed = 1
event_source.event_confirmed_by = user
else if(screen == 2)
diff --git a/code/modules/shuttle/shuttles/ert.dm b/code/modules/shuttle/shuttles/ert.dm
index f0224addadf6..e526c89b5bd9 100644
--- a/code/modules/shuttle/shuttles/ert.dm
+++ b/code/modules/shuttle/shuttles/ert.dm
@@ -169,6 +169,7 @@
port_direction = NORTH
width = 17
height = 29
+ dwidth = 8
var/port_door
var/starboard_door
diff --git a/code/modules/vehicles/hardpoints/hardpoint_ammo/minigun_ammo.dm b/code/modules/vehicles/hardpoints/hardpoint_ammo/minigun_ammo.dm
index 78222cdb3a40..b3f31d824ebf 100644
--- a/code/modules/vehicles/hardpoints/hardpoint_ammo/minigun_ammo.dm
+++ b/code/modules/vehicles/hardpoints/hardpoint_ammo/minigun_ammo.dm
@@ -5,6 +5,6 @@
caliber = "7.62x51mm" //Correlates to miniguns
icon_state = "painless"
w_class = SIZE_LARGE //Primary weapon ammo should probably all be the same w_class
- default_ammo = /datum/ammo/bullet/minigun/tank
+ default_ammo = /datum/ammo/bullet/tank/minigun
max_rounds = 500
gun_type = /obj/item/hardpoint/primary/minigun
diff --git a/code/modules/vehicles/hardpoints/primary/minigun.dm b/code/modules/vehicles/hardpoints/primary/minigun.dm
index 8bc7c55bbb8a..be44b86e2435 100644
--- a/code/modules/vehicles/hardpoints/primary/minigun.dm
+++ b/code/modules/vehicles/hardpoints/primary/minigun.dm
@@ -26,16 +26,16 @@
"8" = list(-77, 0)
)
- scatter = 7
+ scatter = 18
gun_firemode = GUN_FIREMODE_AUTOMATIC
gun_firemode_list = list(
GUN_FIREMODE_AUTOMATIC,
)
- fire_delay = 0.8 SECONDS //base fire rate, modified by stage_delay_mult
+ fire_delay = 0.7 SECONDS //base fire rate, modified by stage_delay_mult
activation_sounds = list('sound/weapons/gun_minigun.ogg')
/// Active firing time to reach max spin_stage.
- var/spinup_time = 8 SECONDS
+ var/spinup_time = 10 SECONDS
/// Grace period before losing spin_stage.
var/spindown_grace_time = 2 SECONDS
COOLDOWN_DECLARE(spindown_grace_cooldown)
@@ -52,7 +52,7 @@
/obj/item/hardpoint/primary/minigun/set_fire_delay(value)
fire_delay = value
- SEND_SIGNAL(src, COMSIG_GUN_AUTOFIREDELAY_MODIFIED, fire_delay * stage_delay_mult)
+ SEND_SIGNAL(src, COMSIG_GUN_AUTOFIREDELAY_MODIFIED, fire_delay * stage_delay_mult, scatter * stage_delay_mult)
/obj/item/hardpoint/primary/minigun/set_fire_cooldown()
calculate_stage_delay_mult() //needs to check grace_cooldown before refreshed
@@ -79,5 +79,11 @@
var/new_stage_rate = stage_rate[floor(spin_stage)]
if(old_stage_rate != new_stage_rate)
+ scatter = initial(scatter) * (1/new_stage_rate)
stage_delay_mult = 1 / new_stage_rate
SEND_SIGNAL(src, COMSIG_GUN_AUTOFIREDELAY_MODIFIED, fire_delay * stage_delay_mult)
+
+/obj/item/hardpoint/primary/minigun/set_bullet_traits()
+ LAZYADD(traits_to_give, list(
+ BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_iff)
+ ))
diff --git a/code/span_macros.dm b/code/span_macros.dm
index 218f333d7df9..f0d78bba617e 100644
--- a/code/span_macros.dm
+++ b/code/span_macros.dm
@@ -19,6 +19,8 @@
#define SPAN_XENOWARNING(X) "[X]"
#define SPAN_XENOMINORWARNING(X) "[X]"
+#define SPAN_PSYTALK(X) "[X]"
+
// Yautja related
#define SPAN_YAUTJABOLD(X) "[X]"
#define SPAN_YAUTJABOLDBIG(X) "[X]"
diff --git a/code/stylesheet.dm b/code/stylesheet.dm
index 145229e23424..8104be1f57e5 100644
--- a/code/stylesheet.dm
+++ b/code/stylesheet.dm
@@ -123,6 +123,8 @@ h1.alert, h2.alert {color: #000000;}
.xeno {color: #900090; font-style: italic;}
.xenoleader {color: #730d73; font-style: italic; font-size: 3;}
.xenoqueen {color: #730d73; font-style: italic; font-weight: bold; font-size: 3;}
+.psy_talk {color: #a70090; font-style: italic; font-weight: bold; font-size: 3;}
+
.newscaster {color: #800000;}
.role_header {color: #db0000 text-align: center; font-weight: bold; font-family: trebuchet-ms; font-size: 2;}
diff --git a/colonialmarines.dme b/colonialmarines.dme
index 6ac47d3b6913..48996a93ec30 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -432,6 +432,7 @@
#include "code\datums\construction\construction_template.dm"
#include "code\datums\construction\xenomorph\construction_template_xenomorph.dm"
#include "code\datums\decorators\decorator.dm"
+#include "code\datums\decorators\gamemode_decorator.dm"
#include "code\datums\diseases\addiction.dm"
#include "code\datums\diseases\beesease.dm"
#include "code\datums\diseases\black_goo.dm"
@@ -536,6 +537,7 @@
#include "code\datums\emergency_calls\provost.dm"
#include "code\datums\emergency_calls\riot.dm"
#include "code\datums\emergency_calls\royal_marines.dm"
+#include "code\datums\emergency_calls\solar_devils.dm"
#include "code\datums\emergency_calls\souto.dm"
#include "code\datums\emergency_calls\supplies.dm"
#include "code\datums\emergency_calls\tank_crew.dm"
@@ -1515,6 +1517,7 @@
#include "code\modules\autowiki\autowiki.dm"
#include "code\modules\autowiki\pages\_page.dm"
#include "code\modules\autowiki\pages\guns.dm"
+#include "code\modules\autowiki\pages\xeno_stats.dm"
#include "code\modules\buildmode\bm-mode.dm"
#include "code\modules\buildmode\buildmode.dm"
#include "code\modules\buildmode\buttons.dm"
@@ -2125,6 +2128,7 @@
#include "code\modules\mob\living\simple_animal\hostile\tree.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm"
+#include "code\modules\mob\living\simple_animal\hostile\retaliate\giant_lizard.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm"
#include "code\modules\mob\new_player\body.dm"
#include "code\modules\mob\new_player\login.dm"
diff --git a/html/changelogs/AutoChangeLog-pr-6647.yml b/html/changelogs/AutoChangeLog-pr-6647.yml
deleted file mode 100644
index 7a1429aa9619..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6647.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Blundir"
-delete-after: True
-changes:
- - imageadd: "unique non placeholder CO armor sprites"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6819.yml b/html/changelogs/AutoChangeLog-pr-6819.yml
deleted file mode 100644
index b016285f2b47..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6819.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-author: "CapCamIII"
-delete-after: True
-changes:
- - rscadd: "Adds a tactical HPR to VAISO and VAIPO machinegunners"
- - rscadd: "Adds an HPR AP mag available to the above"
- - balance: "MAR50 has normal LMG level firerate now"
- - spellcheck: "HPR HEAP mag now has correct name formatting"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6885.yml b/html/changelogs/AutoChangeLog-pr-6885.yml
deleted file mode 100644
index 6cb30d23257f..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6885.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kiVts"
-delete-after: True
-changes:
- - code_imp: "Research datum now supports more broad ideas in the future."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6891.yml b/html/changelogs/AutoChangeLog-pr-6891.yml
deleted file mode 100644
index 1677743be450..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6891.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "BeagleGaming1"
-delete-after: True
-changes:
- - rscdel: "Removes APTFT verb from reagent containers"
- - code_imp: "Updated reagent container switch transfer amount and examination code"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6918.yml b/html/changelogs/AutoChangeLog-pr-6918.yml
deleted file mode 100644
index 644957bebde5..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6918.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "zzzmike"
-delete-after: True
-changes:
- - soundadd: "revive notification has new sound (rather than the ahelp sound)"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6934.yml b/html/changelogs/AutoChangeLog-pr-6934.yml
deleted file mode 100644
index 4cfc2867ad27..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6934.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "MistChristmas"
-delete-after: True
-changes:
- - spellcheck: "places smartgun after DV9 to be DV9 smartgun battery"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6973.yml b/html/changelogs/AutoChangeLog-pr-6973.yml
deleted file mode 100644
index d6c3af60cc79..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6973.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Blundir"
-delete-after: True
-changes:
- - rscadd: "Stompers boots and box for them"
- - rscadd: "new civillian survivor preset resembling Ripley"
- - maptweak: "stomper boots box on New Varadero, Solaris Ridge, Kutjevo Refinery"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6993.yml b/html/changelogs/AutoChangeLog-pr-6993.yml
deleted file mode 100644
index 6822859260db..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6993.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Mister-moon1"
-delete-after: True
-changes:
- - balance: "increased feral smash stun to 3 seconds"
- - balance: "removed the possibility of \"missing\" a feral smash attack"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6999.yml b/html/changelogs/AutoChangeLog-pr-6999.yml
deleted file mode 100644
index 854ccd5cf457..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6999.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Doubleumc"
-delete-after: True
-changes:
- - rscdel: "Removed fancy locker"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-7000.yml b/html/changelogs/AutoChangeLog-pr-7000.yml
deleted file mode 100644
index 82069a9d1c1b..000000000000
--- a/html/changelogs/AutoChangeLog-pr-7000.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Git-Nivrak"
-delete-after: True
-changes:
- - qol: "APCs are now emissive"
- - code_imp: "Added emissive blockers to a bunch of stuff"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-7001.yml b/html/changelogs/AutoChangeLog-pr-7001.yml
deleted file mode 100644
index f9baa47cff9a..000000000000
--- a/html/changelogs/AutoChangeLog-pr-7001.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "efzapa"
-delete-after: True
-changes:
- - qol: "Changes the M44 heavy speed loader description to reflect it's actual use-case."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-7007.yml b/html/changelogs/AutoChangeLog-pr-7007.yml
deleted file mode 100644
index dd636b3045c9..000000000000
--- a/html/changelogs/AutoChangeLog-pr-7007.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "private-tristan"
-delete-after: True
-changes:
- - qol: "IOs now late join into their officer bunks instead of the general wakeup area"
- - qol: "Late joiners will no longer wake up in thin air in the main cryo area."
\ No newline at end of file
diff --git a/html/changelogs/archive/2024-08.yml b/html/changelogs/archive/2024-08.yml
index 3b2b410ded97..b16979aec4e2 100644
--- a/html/changelogs/archive/2024-08.yml
+++ b/html/changelogs/archive/2024-08.yml
@@ -357,3 +357,98 @@
use Officer title.
zzzmike:
- qol: fingerprint scanner description is more newbie-friendly
+2024-08-24:
+ BeagleGaming1:
+ - rscdel: Removes APTFT verb from reagent containers
+ - code_imp: Updated reagent container switch transfer amount and examination code
+ Blundir:
+ - imageadd: unique non placeholder CO armor sprites
+ - bugfix: s&w revolver now has belt/pouch and inhand icons
+ - imageadd: resprite of mac15, m60, ppsh, s&w revolver, basira bolt action rifle
+ - rscadd: mortar camo variants
+ - rscadd: Stompers boots and box for them
+ - rscadd: new civillian survivor preset resembling Ripley
+ - maptweak: stomper boots box on New Varadero, Solaris Ridge, Kutjevo Refinery
+ CapCamIII:
+ - rscadd: Adds a tactical HPR to VAISO and VAIPO machinegunners
+ - rscadd: Adds an HPR AP mag available to the above
+ - balance: MAR50 has normal LMG level firerate now
+ - spellcheck: HPR HEAP mag now has correct name formatting
+ Doubleumc:
+ - rscdel: Removed fancy locker
+ Git-Nivrak:
+ - qol: APCs are now emissive
+ - code_imp: Added emissive blockers to a bunch of stuff
+ ItsVyzo:
+ - rscadd: Provost Chief Inspector
+ - rscadd: missing Provost Presets per ML Hierarchy
+ - imageadd: added PCI sprites
+ - imageadd: PCI and Dep. Marshal HUD icons
+ MistChristmas:
+ - spellcheck: places smartgun after DV9 to be DV9 smartgun battery
+ Mister-moon1:
+ - balance: increased feral smash stun to 3 seconds
+ - balance: removed the possibility of "missing" a feral smash attack
+ Noname995:
+ - bugfix: Varadero, Strata, Kutjevo, Shiva maps now don't have infinity energy
+ - rscadd: Added SMES on Shiva generators points
+ Steelpoint:
+ - rscadd: In observance of the merger of the PvP and PvE servers of CM-SS13. The
+ 'Solar Devils', the USCM battalion that appear in the PvE server, have been
+ added as an admin only Emergency Response Team.
+ - rscadd: The Solar Devils are highly trained and well equipped by Marine standards,
+ they will also spawn as part of their own squad.
+ - imageadd: A 'Solar Devils' uniform patch accessory has been added.
+ TheManWithNoHands:
+ - balance: IFF, scatter based on minigun rev up, rev up time increase, firing delay
+ decrease
+ - code_imp: Places minigun aMmo in the Tank ammo File
+ - soundadd: adds a hit sound for 20mm flak
+ efzapa:
+ - qol: Changes the M44 heavy speed loader description to reflect it's actual use-case.
+ kiVts:
+ - code_imp: Research datum now supports more broad ideas in the future.
+ private-tristan:
+ - qol: IOs now late join into their officer bunks instead of the general wakeup
+ area
+ - qol: Late joiners will no longer wake up in thin air in the main cryo area.
+ zzzmike:
+ - soundadd: revive notification has new sound (rather than the ahelp sound)
+2024-08-25:
+ Doubleumc:
+ - bugfix: fixed ERT boarding shuttle able to spawn and dock
+ - rscadd: Added toggle fullscreen hotkey, default F11
+ - qol: fullscreening hides the menu bar
+ - bugfix: toggling fullscreen preserves your non-fullscreened window options
+ Git-Nivrak:
+ - bugfix: Fixes not being able to see the preview in the character selection screen
+ Kitsunemitsu:
+ - balance: CMO now has access of squad comms.
+ Mister-moon1:
+ - balance: Keycard swipers no longer require two unique keycards to function.
+ - bugfix: fixed the description text for the card swipe to reduce confusion
+ TheGamerdk:
+ - rscadd: APCs are now highlighted when found via a Multitool/Security Access Tuner.
+ Unknownity:
+ - code_imp: The renegade hive is now more visually distinct from the normal hive
+ to decrease the chance of marines accidentally attacking them.
+ VileBeggar, SubjectD9341:
+ - rscadd: LV-624 now contains giant lizards. You can feed them with specific food
+ items in order to make them non-hostile to your faction.
+ - code_imp: Simple mobs can now process fire, though the functionality is turned
+ off by default.
+ realforest2001:
+ - rscadd: Added visual flavour to use of psychic whisper or radiance. It now flickers
+ in the chat window like the Yautja Translator.
+ - rscadd: Added an ability button for the human version of psychic whisper, and
+ replaces the existing verb for it in the ESP chem property.
+ zzzmike:
+ - bugfix: bursting+facehugger stats disregard NPCs
+2024-08-26:
+ Zonespace27:
+ - bugfix: Specialists cryoing now refunds their sets correctly.
+ realforest2001:
+ - admin: All staff now have Admin Announcement and Toggle Remote Control verbs.
+ - admin: Moved shuttle manipulator to minor event perms rather than admin.
+ - code_imp: Changed rights check on Toggle Remote Control and Shuttle Manipulator
+ to check for mod/minor event perms respectively, or debug.
diff --git a/icons/landmarks.dmi b/icons/landmarks.dmi
index 08b23758beaa..32fe9e0790a6 100644
Binary files a/icons/landmarks.dmi and b/icons/landmarks.dmi differ
diff --git a/icons/mob/hud/hud.dmi b/icons/mob/hud/hud.dmi
index 8d89fb781264..999afedb037b 100644
Binary files a/icons/mob/hud/hud.dmi and b/icons/mob/hud/hud.dmi differ
diff --git a/icons/mob/hud/marine_hud.dmi b/icons/mob/hud/marine_hud.dmi
index 7d19ff2d852f..8be1ae31ba68 100644
Binary files a/icons/mob/hud/marine_hud.dmi and b/icons/mob/hud/marine_hud.dmi differ
diff --git a/icons/mob/hud/sec_hud.dmi b/icons/mob/hud/sec_hud.dmi
index f0fb3b318d0a..493727afac1b 100644
Binary files a/icons/mob/hud/sec_hud.dmi and b/icons/mob/hud/sec_hud.dmi differ
diff --git a/icons/mob/humans/onmob/back.dmi b/icons/mob/humans/onmob/back.dmi
index 8937592c7887..f7dffb722a89 100644
Binary files a/icons/mob/humans/onmob/back.dmi and b/icons/mob/humans/onmob/back.dmi differ
diff --git a/icons/mob/humans/onmob/head_1.dmi b/icons/mob/humans/onmob/head_1.dmi
index 5b2cbecdff22..5ee07890f72a 100644
Binary files a/icons/mob/humans/onmob/head_1.dmi and b/icons/mob/humans/onmob/head_1.dmi differ
diff --git a/icons/mob/humans/onmob/items_lefthand_1.dmi b/icons/mob/humans/onmob/items_lefthand_1.dmi
index 45a3b51ecdf2..bc243d0228c3 100644
Binary files a/icons/mob/humans/onmob/items_lefthand_1.dmi and b/icons/mob/humans/onmob/items_lefthand_1.dmi differ
diff --git a/icons/mob/humans/onmob/items_righthand_1.dmi b/icons/mob/humans/onmob/items_righthand_1.dmi
index a2e7e96bac13..29f8cd8e1043 100644
Binary files a/icons/mob/humans/onmob/items_righthand_1.dmi and b/icons/mob/humans/onmob/items_righthand_1.dmi differ
diff --git a/icons/mob/humans/onmob/suit_1.dmi b/icons/mob/humans/onmob/suit_1.dmi
index 65040e9eef4f..c028f6eae887 100644
Binary files a/icons/mob/humans/onmob/suit_1.dmi and b/icons/mob/humans/onmob/suit_1.dmi differ
diff --git a/icons/mob/humans/onmob/suit_slot.dmi b/icons/mob/humans/onmob/suit_slot.dmi
index a9142060ab75..cde35b456048 100644
Binary files a/icons/mob/humans/onmob/suit_slot.dmi and b/icons/mob/humans/onmob/suit_slot.dmi differ
diff --git a/icons/mob/humans/onmob/uniform_0.dmi b/icons/mob/humans/onmob/uniform_0.dmi
index c957814e9f3b..36551630209d 100644
Binary files a/icons/mob/humans/onmob/uniform_0.dmi and b/icons/mob/humans/onmob/uniform_0.dmi differ
diff --git a/icons/mob/mob_64.dmi b/icons/mob/mob_64.dmi
new file mode 100644
index 000000000000..8ac2194b3eb2
Binary files /dev/null and b/icons/mob/mob_64.dmi differ
diff --git a/icons/obj/items/clothing/belts.dmi b/icons/obj/items/clothing/belts.dmi
index ff405cbf5e8e..80070b88b560 100644
Binary files a/icons/obj/items/clothing/belts.dmi and b/icons/obj/items/clothing/belts.dmi differ
diff --git a/icons/obj/items/clothing/cm_hats.dmi b/icons/obj/items/clothing/cm_hats.dmi
index 3c11412ee69c..4710917b0870 100644
Binary files a/icons/obj/items/clothing/cm_hats.dmi and b/icons/obj/items/clothing/cm_hats.dmi differ
diff --git a/icons/obj/items/clothing/cm_suits.dmi b/icons/obj/items/clothing/cm_suits.dmi
index 691059e9cf6d..67590892bf67 100644
Binary files a/icons/obj/items/clothing/cm_suits.dmi and b/icons/obj/items/clothing/cm_suits.dmi differ
diff --git a/icons/obj/items/clothing/ties.dmi b/icons/obj/items/clothing/ties.dmi
index 63c7010db55a..496dd3dc07e5 100644
Binary files a/icons/obj/items/clothing/ties.dmi and b/icons/obj/items/clothing/ties.dmi differ
diff --git a/icons/obj/items/clothing/uniforms.dmi b/icons/obj/items/clothing/uniforms.dmi
index d08183faf920..19a8671a4faf 100644
Binary files a/icons/obj/items/clothing/uniforms.dmi and b/icons/obj/items/clothing/uniforms.dmi differ
diff --git a/icons/obj/items/weapons/guns/ammo_by_faction/colony.dmi b/icons/obj/items/weapons/guns/ammo_by_faction/colony.dmi
index 2adece3b1ab1..8d8ef7cb0163 100644
Binary files a/icons/obj/items/weapons/guns/ammo_by_faction/colony.dmi and b/icons/obj/items/weapons/guns/ammo_by_faction/colony.dmi differ
diff --git a/icons/obj/items/weapons/guns/ammo_by_faction/upp.dmi b/icons/obj/items/weapons/guns/ammo_by_faction/upp.dmi
index 7327bf6a611b..32a8aeec4d17 100644
Binary files a/icons/obj/items/weapons/guns/ammo_by_faction/upp.dmi and b/icons/obj/items/weapons/guns/ammo_by_faction/upp.dmi differ
diff --git a/icons/obj/items/weapons/guns/attachments/barrel.dmi b/icons/obj/items/weapons/guns/attachments/barrel.dmi
index 138b9b658fb6..2400465e4bf4 100644
Binary files a/icons/obj/items/weapons/guns/attachments/barrel.dmi and b/icons/obj/items/weapons/guns/attachments/barrel.dmi differ
diff --git a/icons/obj/items/weapons/guns/attachments/rail.dmi b/icons/obj/items/weapons/guns/attachments/rail.dmi
index 68cb2648ebd1..f53187160e92 100644
Binary files a/icons/obj/items/weapons/guns/attachments/rail.dmi and b/icons/obj/items/weapons/guns/attachments/rail.dmi differ
diff --git a/icons/obj/items/weapons/guns/attachments/stock.dmi b/icons/obj/items/weapons/guns/attachments/stock.dmi
index 0867f60d6430..9f28383c0bad 100644
Binary files a/icons/obj/items/weapons/guns/attachments/stock.dmi and b/icons/obj/items/weapons/guns/attachments/stock.dmi differ
diff --git a/icons/obj/items/weapons/guns/attachments/under.dmi b/icons/obj/items/weapons/guns/attachments/under.dmi
index 5a9be7542348..1138e0044c34 100644
Binary files a/icons/obj/items/weapons/guns/attachments/under.dmi and b/icons/obj/items/weapons/guns/attachments/under.dmi differ
diff --git a/icons/obj/items/weapons/guns/guns_by_faction/colony.dmi b/icons/obj/items/weapons/guns/guns_by_faction/colony.dmi
index 24f9f9b63871..bfc4587f4104 100644
Binary files a/icons/obj/items/weapons/guns/guns_by_faction/colony.dmi and b/icons/obj/items/weapons/guns/guns_by_faction/colony.dmi differ
diff --git a/icons/obj/items/weapons/guns/guns_by_faction/upp.dmi b/icons/obj/items/weapons/guns/guns_by_faction/upp.dmi
index 669efcfc59c4..dcdc441fcb53 100644
Binary files a/icons/obj/items/weapons/guns/guns_by_faction/upp.dmi and b/icons/obj/items/weapons/guns/guns_by_faction/upp.dmi differ
diff --git a/icons/obj/items/weapons/projectiles.dmi b/icons/obj/items/weapons/projectiles.dmi
index c3aa143def9b..21c210c14910 100644
Binary files a/icons/obj/items/weapons/projectiles.dmi and b/icons/obj/items/weapons/projectiles.dmi differ
diff --git a/maps/map_files/Ice_Colony_v3/Shivas_Snowball.dmm b/maps/map_files/Ice_Colony_v3/Shivas_Snowball.dmm
index 9f5c20a4e8e9..51f7589f4987 100644
--- a/maps/map_files/Ice_Colony_v3/Shivas_Snowball.dmm
+++ b/maps/map_files/Ice_Colony_v3/Shivas_Snowball.dmm
@@ -12692,11 +12692,11 @@
/turf/open/auto_turf/snow/layer0,
/area/shiva/interior/warehouse/caves)
"jWL" = (
-/obj/structure/machinery/space_heater,
/obj/structure/machinery/light/double{
dir = 1;
pixel_y = 9
},
+/obj/structure/machinery/power/smes/buildable,
/turf/open/floor/shiva/yellowfull/west,
/area/shiva/interior/aux_power)
"jXD" = (
@@ -15502,6 +15502,10 @@
},
/turf/open/auto_turf/ice/layer1,
/area/shiva/interior/caves/research_caves)
+"niD" = (
+/obj/structure/machinery/space_heater,
+/turf/open/floor/shiva/floor3,
+/area/shiva/interior/aux_power)
"niL" = (
/obj/structure/platform/strata{
dir = 8
@@ -19150,6 +19154,9 @@
dir = 1;
pixel_y = 9
},
+/obj/structure/machinery/power/smes/buildable{
+ pixel_x = 1
+ },
/turf/open/floor/shiva/yellowfull/west,
/area/shiva/interior/aux_power)
"rUW" = (
@@ -23648,6 +23655,10 @@
/obj/item/tool/warning_cone,
/turf/open/floor/shiva,
/area/shiva/interior/colony/research_hab)
+"xEU" = (
+/obj/structure/machinery/power/smes/buildable,
+/turf/open/floor/shiva/yellowfull/west,
+/area/shiva/interior/garage)
"xFp" = (
/obj/structure/bed/chair/comfy/orange{
dir = 4
@@ -33924,7 +33935,7 @@ puZ
puZ
anc
anc
-ctz
+niD
cJu
cJu
nTC
@@ -50679,11 +50690,11 @@ ktd
ktd
ktd
ktd
-ktd
+xEU
tXd
ado
pSD
-ktd
+xEU
ktd
ktd
ktd
diff --git a/maps/map_files/LV624/LV624.dmm b/maps/map_files/LV624/LV624.dmm
index 87a83cf09d19..1183e3e97051 100644
--- a/maps/map_files/LV624/LV624.dmm
+++ b/maps/map_files/LV624/LV624.dmm
@@ -13368,6 +13368,11 @@
/obj/structure/flora/bush/ausbushes/ppflowers,
/turf/open/gm/dirtgrassborder/grassdirt_corner2/south_west,
/area/lv624/ground/caves/sand_temple)
+"hUK" = (
+/obj/structure/flora/bush/ausbushes/lavendergrass,
+/obj/effect/landmark/lizard_spawn,
+/turf/open/gm/grass/grass1,
+/area/lv624/ground/barrens/south_eastern_jungle_barrens)
"hWj" = (
/turf/open/gm/coast/beachcorner/south_east,
/area/lv624/ground/river/east_river)
@@ -16917,6 +16922,10 @@
"pcA" = (
/turf/open/gm/dirtgrassborder/grassdirt_corner2/north_east,
/area/lv624/ground/jungle/south_central_jungle)
+"pcL" = (
+/obj/effect/landmark/lizard_spawn,
+/turf/open/gm/dirt,
+/area/lv624/ground/jungle/south_west_jungle/ceiling)
"pfl" = (
/obj/effect/decal/cleanable/blood,
/obj/effect/landmark/corpsespawner/chef,
@@ -17226,6 +17235,10 @@
/obj/effect/landmark/crap_item,
/turf/open/floor/dark,
/area/lv624/lazarus/quartstorage)
+"pKC" = (
+/obj/effect/landmark/lizard_spawn,
+/turf/open/gm/dirtgrassborder/south,
+/area/lv624/ground/jungle/south_east_jungle)
"pKS" = (
/obj/structure/flora/jungle/vines/heavy,
/obj/structure/flora/jungle/vines/light_2,
@@ -18987,6 +19000,14 @@
},
/turf/open/gm/dirt,
/area/lv624/ground/jungle/north_west_jungle)
+"tkN" = (
+/obj/effect/landmark/lizard_spawn,
+/turf/open/gm/grass/grass1,
+/area/lv624/ground/barrens/south_eastern_jungle_barrens)
+"tkS" = (
+/obj/effect/landmark/lizard_spawn,
+/turf/open/gm/dirt,
+/area/lv624/ground/jungle/south_east_jungle)
"tlk" = (
/obj/structure/flora/bush/ausbushes/reedbush,
/turf/open/auto_turf/strata_grass/layer1,
@@ -19599,6 +19620,10 @@
/obj/structure/flora/bush/ausbushes/var3/fullgrass,
/turf/open/auto_turf/strata_grass/layer1,
/area/lv624/ground/caves/south_west_caves)
+"ury" = (
+/obj/effect/landmark/lizard_spawn,
+/turf/open/floor/whiteyellowfull/east,
+/area/lv624/ground/jungle/south_west_jungle/ceiling)
"urR" = (
/turf/open/floor/corsat/squareswood/north,
/area/lv624/ground/caves/sand_temple)
@@ -23033,7 +23058,7 @@ aKb
aKb
lyz
aWl
-qBW
+pcL
aWO
hMd
aWl
@@ -23946,7 +23971,7 @@ auy
aVS
aWl
aWO
-aWO
+ury
aWO
qBW
aVS
@@ -59290,7 +59315,7 @@ lBw
eTQ
opS
opS
-gUq
+pKC
wpw
lBw
lBw
@@ -59744,7 +59769,7 @@ qjt
wpw
wpw
eTQ
-opS
+tkS
lnV
qgA
lBw
@@ -60110,7 +60135,7 @@ jxR
uve
uve
uve
-vty
+tkN
git
vty
moM
@@ -60568,7 +60593,7 @@ uve
vty
vty
nED
-glS
+hUK
vty
uve
uve
diff --git a/maps/map_files/LV624/standalone/sandtemple-jungle.dmm b/maps/map_files/LV624/standalone/sandtemple-jungle.dmm
index 724344cd0743..9e51f09fb628 100644
--- a/maps/map_files/LV624/standalone/sandtemple-jungle.dmm
+++ b/maps/map_files/LV624/standalone/sandtemple-jungle.dmm
@@ -63,6 +63,10 @@
},
/turf/open/gm/grass/grass2,
/area/lv624/ground/jungle/south_west_jungle)
+"lZ" = (
+/obj/effect/landmark/lizard_spawn,
+/turf/open/gm/dirtgrassborder/grassdirt_corner/north_east,
+/area/lv624/ground/jungle/south_west_jungle)
"ny" = (
/obj/structure/flora/bush/ausbushes/lavendergrass,
/turf/open/gm/grass/grass1,
@@ -206,6 +210,10 @@
/obj/structure/flora/jungle/vines/light_1,
/turf/open/gm/grass/grass2,
/area/lv624/ground/jungle/south_west_jungle)
+"Ng" = (
+/obj/effect/landmark/lizard_spawn,
+/turf/open/gm/dirt,
+/area/lv624/ground/jungle/south_west_jungle)
"Nq" = (
/obj/structure/flora/bush/ausbushes/var3/sunnybush,
/turf/open/gm/grass/grass1,
@@ -336,7 +344,7 @@ oD
LW
dy
Ok
-NU
+Ng
NU
Ge
AQ
@@ -383,7 +391,7 @@ uj
uj
uj
eV
-Lz
+lZ
Kn
IP
oD
diff --git a/maps/map_files/LV624/standalone/sandtemple-se.dmm b/maps/map_files/LV624/standalone/sandtemple-se.dmm
index 76d29d2d01ba..7e1db79bab61 100644
--- a/maps/map_files/LV624/standalone/sandtemple-se.dmm
+++ b/maps/map_files/LV624/standalone/sandtemple-se.dmm
@@ -74,6 +74,10 @@
/obj/structure/flora/jungle/vines/light_2,
/turf/open/gm/grass/grass1,
/area/lv624/ground/jungle/south_east_jungle)
+"vt" = (
+/obj/effect/landmark/lizard_spawn,
+/turf/open/gm/dirt,
+/area/lv624/ground/jungle/south_east_jungle)
"wQ" = (
/obj/structure/flora/jungle/vines/heavy{
pixel_x = -28
@@ -109,6 +113,10 @@
/obj/structure/flora/jungle/vines/light_3,
/turf/open/floor/sandstone/runed,
/area/lv624/ground/jungle/south_east_jungle)
+"Kj" = (
+/obj/effect/landmark/lizard_spawn,
+/turf/open/floor/sandstone/runed,
+/area/lv624/ground/jungle/south_east_jungle)
"LV" = (
/turf/open/gm/grass/grass1,
/area/lv624/lazarus/landing_zones/lz1)
@@ -317,7 +325,7 @@ Wt
gU
Xz
Xy
-Xl
+vt
Xl
Xl
Ze
@@ -347,7 +355,7 @@ Tl
aK
Wl
yg
-Xy
+Kj
Xl
br
EZ
diff --git a/sound/ATTRIBUTION.txt b/sound/ATTRIBUTION.txt
new file mode 100644
index 000000000000..03dd02d49db4
--- /dev/null
+++ b/sound/ATTRIBUTION.txt
@@ -0,0 +1,3 @@
+giant_lizard_growl1.ogg, giant_lizard_growl2.ogg by D.jones -- https://freesound.org/s/527844/ -- License: Creative Commons 0
+giant_lizard_death.wav by craigsmith -- https://freesound.org/s/437933/ -- License: Creative Commons 0
+giant_lizard_hiss1.ogg, giant_lizard_hiss2.ogg by ZapSplat -- https://www.zapsplat.com/sound-effect-category/alligators-and-crocodiles/ -- License: ZapSplat Standard License
diff --git a/sound/effects/giant_lizard_death.ogg b/sound/effects/giant_lizard_death.ogg
new file mode 100644
index 000000000000..fe8cf05dc1ee
Binary files /dev/null and b/sound/effects/giant_lizard_death.ogg differ
diff --git a/sound/effects/giant_lizard_growl1.ogg b/sound/effects/giant_lizard_growl1.ogg
new file mode 100644
index 000000000000..7dfd52b211ed
Binary files /dev/null and b/sound/effects/giant_lizard_growl1.ogg differ
diff --git a/sound/effects/giant_lizard_growl2.ogg b/sound/effects/giant_lizard_growl2.ogg
new file mode 100644
index 000000000000..00d85d78ecb6
Binary files /dev/null and b/sound/effects/giant_lizard_growl2.ogg differ
diff --git a/sound/effects/giant_lizard_hiss1.ogg b/sound/effects/giant_lizard_hiss1.ogg
new file mode 100644
index 000000000000..105a0edb6d74
Binary files /dev/null and b/sound/effects/giant_lizard_hiss1.ogg differ
diff --git a/sound/effects/giant_lizard_hiss2.ogg b/sound/effects/giant_lizard_hiss2.ogg
new file mode 100644
index 000000000000..5a0d143ef15f
Binary files /dev/null and b/sound/effects/giant_lizard_hiss2.ogg differ
diff --git a/sound/weapons/sting_boom_small1.ogg b/sound/weapons/sting_boom_small1.ogg
new file mode 100644
index 000000000000..fb02d14a68fa
Binary files /dev/null and b/sound/weapons/sting_boom_small1.ogg differ
diff --git a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
index 5ea797fe2230..a430d06b7a00 100644
--- a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
+++ b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
@@ -1449,6 +1449,14 @@ em {
animation: glitch 0.5s infinite;
}
+.psy_talk {
+ color: #c400aa;
+ font-weight: bold;
+
+ /*Animation*/
+ animation: psy_glitch 0.5s infinite;
+}
+
/*Keyframes*/
@keyframes glitch {
@@ -1473,6 +1481,28 @@ em {
}
}
+@keyframes psy_glitch {
+ 25% {
+ color: #c400aa;
+ transform: translate(-2px, -1px);
+ }
+
+ 50% {
+ color: #91007d;
+ transform: translate(1px, -2px);
+ }
+
+ 75% {
+ color: #750066;
+ transform: translate(-1px, 2px);
+ }
+
+ 100% {
+ color: #7c016c;
+ transform: translate(1px, 1px);
+ }
+}
+
.examine_block {
background: #1b1c1e;
border: 1px solid #a4bad6;
diff --git a/tgui/packages/tgui-panel/styles/goon/chat-light.scss b/tgui/packages/tgui-panel/styles/goon/chat-light.scss
index 87beb202a5ae..23ea05b1c9c1 100644
--- a/tgui/packages/tgui-panel/styles/goon/chat-light.scss
+++ b/tgui/packages/tgui-panel/styles/goon/chat-light.scss
@@ -1471,6 +1471,14 @@ h2.alert {
animation: glitch 0.5s infinite;
}
+.psy_talk {
+ color: #a70090;
+ font-weight: bold;
+
+ /*Animation*/
+ animation: psy_glitch 0.5s infinite;
+}
+
/*Keyframes*/
@keyframes glitch {
@@ -1495,6 +1503,28 @@ h2.alert {
}
}
+@keyframes psy_glitch {
+ 25% {
+ color: #a70090;
+ transform: translate(-2px, -1px);
+ }
+
+ 50% {
+ color: #91007d;
+ transform: translate(1px, -2px);
+ }
+
+ 75% {
+ color: #750066;
+ transform: translate(-1px, 2px);
+ }
+
+ 100% {
+ color: #7c016c;
+ transform: translate(1px, 1px);
+ }
+}
+
.examine_block {
background: #f2f7fa;
border: 1px solid #111a27;