diff --git a/.github/workflows/conflicts.yml b/.github/workflows/conflicts.yml
index 02d9da40342b..b65a5213ab4a 100644
--- a/.github/workflows/conflicts.yml
+++ b/.github/workflows/conflicts.yml
@@ -12,4 +12,6 @@ jobs:
- uses: eps1lon/actions-label-merge-conflict@v2.1.0
with:
dirtyLabel: 'Merge Conflict'
+ commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."
+ commentOnClean: "Conflicts have been resolved. A maintainer will review the pull request shortly."
repoToken: ${{ secrets.BOT_TOKEN_CM || secrets.GITHUB_TOKEN }}
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 60c4116df330..31df07648fc0 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -20,3 +20,18 @@
#define DEFAULT_MESSAGE_RANGE 7
#define BAYONET_DRAW_DELAY (1 SECONDS)
+
+//Predator decloak multpliers based on the standard.
+#define DECLOAK_STANDARD (10 SECONDS)
+/// Forced for any unspecified reason.
+#define DECLOAK_FORCED 1
+/// Caused by being worn by non humans.
+#define DECLOAK_SPECIES 0.75
+/// Caused by fire extinguisher.
+#define DECLOAK_EXTINGUISHER 1.5
+/// Caused by predalien screech.
+#define DECLOAK_PREDALIEN 2
+/// Caused by being in a body of water.
+#define DECLOAK_SUBMERGED 2
+/// Caused by an EMP.
+#define DECLOAK_EMP 3
diff --git a/code/__DEFINES/equipment.dm b/code/__DEFINES/equipment.dm
index 4212f6a52301..461eae27a2a3 100644
--- a/code/__DEFINES/equipment.dm
+++ b/code/__DEFINES/equipment.dm
@@ -82,7 +82,6 @@
#define NOTABLEMERGE (1<<13)
/// Has heat source but isn't 'on fire' and thus can be stored
#define IGNITING_ITEM (1<<14)
-
//==========================================================================================
diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm
index 422edb05508b..1878ca63f34e 100644
--- a/code/__DEFINES/keybinding.dm
+++ b/code/__DEFINES/keybinding.dm
@@ -189,6 +189,7 @@
#define COMSIG_KB_OBSERVER_JOIN_XENO "keybinding_observer_join_as_xeno"
#define COMSIG_KB_OBSERVER_JOIN_ERT "keybinding_observer_join_ert"
#define COMSIG_KB_OBSERVER_JOIN_PREDATOR "keybinding_observer_join_pred"
+#define COMSIG_KB_OBSERVER_JOIN_LESSER_DRONE "keybinding_observer_join_lesser_drone"
#define CATEGORY_CLIENT "CLIENT"
#define CATEGORY_EMOTE "EMOTE"
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index bed2ceeced7d..4464a0b16a1d 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -284,6 +284,8 @@
#define COOLDOWN_COMM_CENTRAL 30 SECONDS
#define COOLDOWN_COMM_DESTRUCT 5 MINUTES
+///Cooldown for pred recharge
+#define COOLDOWN_BRACER_CHARGE 3 MINUTES
// magic value to use for indicating a proc slept
#define PROC_RETURN_SLEEP -1
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index ba928e202cf0..673bb4fc6d81 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -221,6 +221,7 @@
//Mob sizes
#define MOB_SIZE_SMALL 0
#define MOB_SIZE_HUMAN 1
+#define MOB_SIZE_XENO_VERY_SMALL 1.5
#define MOB_SIZE_XENO_SMALL 2
#define MOB_SIZE_XENO 3
#define MOB_SIZE_BIG 4
diff --git a/code/__DEFINES/typecheck/humanoids.dm b/code/__DEFINES/typecheck/humanoids.dm
index 58d245c486e4..7076cf67c95c 100644
--- a/code/__DEFINES/typecheck/humanoids.dm
+++ b/code/__DEFINES/typecheck/humanoids.dm
@@ -25,7 +25,7 @@
#define isspeciessynth(A) (A.species?.group == SPECIES_SYNTHETIC)
//Size checks for carbon to use instead of typechecks. (Hellhounds are deprecated)
-#define iscarbonsizexeno(A) (A.mob_size >= MOB_SIZE_XENO_SMALL)
+#define iscarbonsizexeno(A) (A.mob_size >= MOB_SIZE_XENO_VERY_SMALL)
#define iscarbonsizehuman(A) (A.mob_size <= MOB_SIZE_HUMAN)
//job/role helpers
diff --git a/code/__DEFINES/typecheck/xenos.dm b/code/__DEFINES/typecheck/xenos.dm
index 4d1b7819bdf1..d313090e8305 100644
--- a/code/__DEFINES/typecheck/xenos.dm
+++ b/code/__DEFINES/typecheck/xenos.dm
@@ -14,6 +14,7 @@
#define islarva(A) (istype(A, /mob/living/carbon/xenomorph/larva))
#define ispredalienlarva(A) (istype(A, /mob/living/carbon/xenomorph/larva/predalien))
#define isfacehugger(A) (istype(A, /mob/living/carbon/xenomorph/facehugger))
+#define islesserdrone(A) (istype(A, /mob/living/carbon/xenomorph/lesser_drone))
#define ispraetorian(A) (istype(A, /mob/living/carbon/xenomorph/praetorian))
#define isqueen(A) (istype(A, /mob/living/carbon/xenomorph/queen))
#define isravager(A) (istype(A, /mob/living/carbon/xenomorph/ravager))
diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno.dm
index ac783b6f426e..82237cd793b2 100644
--- a/code/__DEFINES/xeno.dm
+++ b/code/__DEFINES/xeno.dm
@@ -70,6 +70,7 @@
#define HUD_ARMOR_STATES_XENO 10
/// Multiplier for time taken for a xeno to place down a resin structure
+#define BUILD_TIME_MULT_LESSER_DRONE 2
#define BUILD_TIME_MULT_XENO 1
#define BUILD_TIME_MULT_BUILDER 1
#define BUILD_TIME_MULT_HIVELORD 0.5
@@ -206,6 +207,7 @@
// Health bands
#define XENO_HEALTH_LARVA 35 * XENO_UNIVERSAL_HPMULT
+#define XENO_HEALTH_LESSER_DRONE 160 * XENO_UNIVERSAL_HPMULT
#define XENO_HEALTH_RUNNER 230 * XENO_UNIVERSAL_HPMULT // Killed by 1 PB
#define XENO_HEALTH_TIER_1 250 * XENO_UNIVERSAL_HPMULT
#define XENO_HEALTH_TIER_2 300 * XENO_UNIVERSAL_HPMULT
@@ -603,7 +605,8 @@
#define XENO_CASTE_LARVA "Bloody Larva"
#define XENO_CASTE_PREDALIEN_LARVA "Predalien Larva"
#define XENO_CASTE_FACEHUGGER "Facehugger"
-#define XENO_T0_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER)
+#define XENO_CASTE_LESSER_DRONE "Lesser Drone"
+#define XENO_T0_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER, XENO_CASTE_LESSER_DRONE)
//t1
#define XENO_CASTE_DRONE "Drone"
@@ -631,7 +634,7 @@
#define XENO_CASTE_HELLHOUND "Hellhound"
#define XENO_SPECIAL_CASTES list(XENO_CASTE_QUEEN, XENO_CASTE_PREDALIEN, XENO_CASTE_HELLHOUND)
-#define ALL_XENO_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER, XENO_CASTE_DRONE, XENO_CASTE_RUNNER, XENO_CASTE_SENTINEL, XENO_CASTE_DEFENDER, XENO_CASTE_BURROWER, XENO_CASTE_CARRIER, XENO_CASTE_HIVELORD, XENO_CASTE_LURKER, XENO_CASTE_WARRIOR, XENO_CASTE_SPITTER, XENO_CASTE_BOILER, XENO_CASTE_PRAETORIAN, XENO_CASTE_CRUSHER, XENO_CASTE_RAVAGER, XENO_CASTE_QUEEN, XENO_CASTE_PREDALIEN, XENO_CASTE_HELLHOUND)
+#define ALL_XENO_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER, XENO_CASTE_LESSER_DRONE, XENO_CASTE_DRONE, XENO_CASTE_RUNNER, XENO_CASTE_SENTINEL, XENO_CASTE_DEFENDER, XENO_CASTE_BURROWER, XENO_CASTE_CARRIER, XENO_CASTE_HIVELORD, XENO_CASTE_LURKER, XENO_CASTE_WARRIOR, XENO_CASTE_SPITTER, XENO_CASTE_BOILER, XENO_CASTE_PRAETORIAN, XENO_CASTE_CRUSHER, XENO_CASTE_RAVAGER, XENO_CASTE_QUEEN, XENO_CASTE_PREDALIEN, XENO_CASTE_HELLHOUND)
// Checks if two hives are allied to each other.
// PARAMETERS:
@@ -695,3 +698,6 @@
#define TAILSTAB_AIRLOCK_DAMAGE_MULTIPLIER 2
#define FRENZY_DAMAGE_MULTIPLIER 2
+
+#define JOIN_AS_FACEHUGGER_DELAY (3 MINUTES)
+#define JOIN_AS_LESSER_DRONE_DELAY (30 SECONDS)
diff --git a/code/_globalvars/global_lists.dm b/code/_globalvars/global_lists.dm
index 586d5e71a92d..7e65cfecd8b0 100644
--- a/code/_globalvars/global_lists.dm
+++ b/code/_globalvars/global_lists.dm
@@ -50,6 +50,12 @@ GLOBAL_LIST_EMPTY(mainship_pipes)
// Resin constructions parameters
GLOBAL_LIST_INIT_TYPED(resin_constructions_list, /datum/resin_construction, setup_resin_constructions())
+GLOBAL_LIST_INIT(resin_build_order_lesser_drone, list(
+ /datum/resin_construction/resin_turf/wall,
+ /datum/resin_construction/resin_turf/membrane,
+ /datum/resin_construction/resin_obj/door,
+))
+
GLOBAL_LIST_INIT(resin_build_order_drone, list(
/datum/resin_construction/resin_turf/wall,
/datum/resin_construction/resin_turf/membrane,
diff --git a/code/game/gamemodes/cm_initialize.dm b/code/game/gamemodes/cm_initialize.dm
index a42ff3f22e59..a7e8ab612bb4 100644
--- a/code/game/gamemodes/cm_initialize.dm
+++ b/code/game/gamemodes/cm_initialize.dm
@@ -548,6 +548,38 @@ Additional game mode variables.
return TRUE
+/datum/game_mode/proc/attempt_to_join_as_lesser_drone(mob/xeno_candidate)
+ var/list/active_hives = list()
+ var/datum/hive_status/hive
+ var/last_active_hive = 0
+ for(var/hivenumber in GLOB.hive_datum)
+ hive = GLOB.hive_datum[hivenumber]
+ if(hive.totalXenos.len <= 0)
+ continue
+ active_hives[hive.name] = hive.hivenumber
+ last_active_hive = hive.hivenumber
+
+ if(active_hives.len <= 0)
+ to_chat(xeno_candidate, SPAN_WARNING("There aren't any Hives active at this point for you to join."))
+ return FALSE
+
+ if(active_hives.len > 1)
+ var/hive_picked = tgui_input_list(xeno_candidate, "Select which Hive to attempt joining.", "Hive Choice", active_hives, theme="hive_status")
+ if(!hive_picked)
+ to_chat(xeno_candidate, SPAN_ALERT("Hive choice error. Aborting."))
+ return
+ hive = GLOB.hive_datum[active_hives[hive_picked]]
+ else
+ hive = GLOB.hive_datum[last_active_hive]
+
+ if(!hive.hive_location)
+ to_chat(xeno_candidate, SPAN_WARNING("The selected hive does not have a hive core to spawn from!"))
+ return
+
+ hive.hive_location.spawn_lesser_drone(xeno_candidate)
+
+ return TRUE
+
/datum/game_mode/proc/transfer_xeno(xeno_candidate, mob/living/new_xeno)
if(!xeno_candidate || !isxeno(new_xeno) || QDELETED(new_xeno))
return FALSE
diff --git a/code/game/jobs/role_authority.dm b/code/game/jobs/role_authority.dm
index b909c38cd9e6..e7697d54f0de 100644
--- a/code/game/jobs/role_authority.dm
+++ b/code/game/jobs/role_authority.dm
@@ -762,6 +762,8 @@ I hope it's easier to tell what the heck this proc is even doing, unlike previou
M = /mob/living/carbon/xenomorph/larva/predalien
if(XENO_CASTE_FACEHUGGER)
M = /mob/living/carbon/xenomorph/facehugger
+ if(XENO_CASTE_LESSER_DRONE)
+ M = /mob/living/carbon/xenomorph/lesser_drone
if(XENO_CASTE_RUNNER)
M = /mob/living/carbon/xenomorph/runner
if(XENO_CASTE_DRONE)
diff --git a/code/game/machinery/vending/vendor_types/requisitions.dm b/code/game/machinery/vending/vendor_types/requisitions.dm
index b8e3e55f1d89..f85657e887a8 100644
--- a/code/game/machinery/vending/vendor_types/requisitions.dm
+++ b/code/game/machinery/vending/vendor_types/requisitions.dm
@@ -92,26 +92,27 @@
list("POUCHES", -1, null, null),
list("Autoinjector Pouch", round(scale * 2), /obj/item/storage/pouch/autoinjector, VENDOR_ITEM_REGULAR),
- list("Bayonet Pouch", round(scale * 2), /obj/item/storage/pouch/bayonet, VENDOR_ITEM_REGULAR),
+ list("Medkit Pouch", round(scale * 2), /obj/item/storage/pouch/medkit, VENDOR_ITEM_REGULAR),
+ list("Medical Pouch", round(scale * 2), /obj/item/storage/pouch/medical, VENDOR_ITEM_REGULAR),
+ list("First-Aid Pouch (Full)", round(scale * 5), /obj/item/storage/pouch/firstaid/full, VENDOR_ITEM_REGULAR),
+ list("First Responder Pouch", round(scale * 2), /obj/item/storage/pouch/first_responder, VENDOR_ITEM_REGULAR),
+ list("Syringe Pouch", round(scale * 2), /obj/item/storage/pouch/syringe, VENDOR_ITEM_REGULAR),
+ list("Tools Pouch (Full)", round(scale * 2), /obj/item/storage/pouch/tools/full, VENDOR_ITEM_REGULAR),
list("Construction Pouch", round(scale * 2), /obj/item/storage/pouch/construction, VENDOR_ITEM_REGULAR),
- list("Document Pouch", round(scale * 2), /obj/item/storage/pouch/document/small, VENDOR_ITEM_REGULAR),
list("Electronics Pouch", round(scale * 2), /obj/item/storage/pouch/electronics, VENDOR_ITEM_REGULAR),
list("Explosive Pouch", round(scale * 2), /obj/item/storage/pouch/explosive, VENDOR_ITEM_REGULAR),
- list("First-Aid Pouch (Full)", round(scale * 5), /obj/item/storage/pouch/firstaid/full, VENDOR_ITEM_REGULAR),
- list("First Responder Pouch", round(scale * 2), /obj/item/storage/pouch/first_responder, VENDOR_ITEM_REGULAR),
list("Flare Pouch (Full)", round(scale * 5), /obj/item/storage/pouch/flare/full, VENDOR_ITEM_REGULAR),
- list("Fuel Tank Strap Pouch", round(scale * 4), /obj/item/storage/pouch/flamertank, VENDOR_ITEM_REGULAR),
- list("Large Pistol Magazine Pouch", round(scale * 5), /obj/item/storage/pouch/magazine/pistol/large, VENDOR_ITEM_REGULAR),
- list("Magazine Pouch", round(scale * 5), /obj/item/storage/pouch/magazine, VENDOR_ITEM_REGULAR),
- list("Shotgun Shell Pouch", round(scale * 5), /obj/item/storage/pouch/shotgun, VENDOR_ITEM_REGULAR),
+ list("Document Pouch", round(scale * 2), /obj/item/storage/pouch/document/small, VENDOR_ITEM_REGULAR),
+ list("Sling Pouch", round(scale * 2), /obj/item/storage/pouch/sling, VENDOR_ITEM_REGULAR),
list("Machete Pouch (Full)", round(scale * 4), /obj/item/storage/pouch/machete/full, VENDOR_ITEM_REGULAR),
- list("Medical Pouch", round(scale * 2), /obj/item/storage/pouch/medical, VENDOR_ITEM_REGULAR),
+ list("Bayonet Pouch", round(scale * 2), /obj/item/storage/pouch/bayonet, VENDOR_ITEM_REGULAR),
list("Medium General Pouch", round(scale * 2), /obj/item/storage/pouch/general/medium, VENDOR_ITEM_REGULAR),
- list("Medkit Pouch", round(scale * 2), /obj/item/storage/pouch/medkit, VENDOR_ITEM_REGULAR),
+ list("Magazine Pouch", round(scale * 5), /obj/item/storage/pouch/magazine, VENDOR_ITEM_REGULAR),
+ list("Shotgun Shell Pouch", round(scale * 5), /obj/item/storage/pouch/shotgun, VENDOR_ITEM_REGULAR),
list("Sidearm Pouch", round(scale * 5), /obj/item/storage/pouch/pistol, VENDOR_ITEM_REGULAR),
- list("Syringe Pouch", round(scale * 2), /obj/item/storage/pouch/syringe, VENDOR_ITEM_REGULAR),
- list("Tools Pouch (Full)", round(scale * 2), /obj/item/storage/pouch/tools/full, VENDOR_ITEM_REGULAR),
- list("Sling Pouch", round(scale * 2), /obj/item/storage/pouch/sling, VENDOR_ITEM_REGULAR),
+ list("Large Pistol Magazine Pouch", round(scale * 5), /obj/item/storage/pouch/magazine/pistol/large, VENDOR_ITEM_REGULAR),
+ list("Fuel Tank Strap Pouch", round(scale * 4), /obj/item/storage/pouch/flamertank, VENDOR_ITEM_REGULAR),
+ list("Large General Pouch", round(scale * 2), /obj/item/storage/pouch/general/large, VENDOR_ITEM_REGULAR),
list("Large Magazine Pouch", round(scale * 2), /obj/item/storage/pouch/magazine/large, VENDOR_ITEM_REGULAR),
list("Large Shotgun Shell Pouch", round(scale * 2), /obj/item/storage/pouch/shotgun/large, VENDOR_ITEM_REGULAR),
diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm
index 55f3c32cebba..3b84d2433e88 100644
--- a/code/game/objects/items/bodybag.dm
+++ b/code/game/objects/items/bodybag.dm
@@ -122,25 +122,27 @@
/obj/structure/closet/bodybag/store_mobs(stored_units) // overriding this
var/list/dead_mobs = list()
- for(var/mob/living/M in loc)
- if(M.buckled)
+ for(var/mob/living/mob in loc)
+ if(mob.buckled)
continue
- if(M.stat != DEAD) // covers alive mobs
+ if(mob.stat != DEAD) // covers alive mobs
continue
- if(!ishuman(M)) // all the dead other shit
- dead_mobs += M
+ if(!ishuman(mob)) // all the dead other shit
+ dead_mobs += mob
continue
- var/mob/living/carbon/human/H = M
- if(H.check_tod() || issynth(H) || H.is_revivable() && H.get_ghost()) // revivable
+ var/mob/living/carbon/human/human = mob
+ if(issynth(human))
continue
- dead_mobs += M
+ if(human.check_tod() && human.is_revivable()) // revivable
+ continue
+ dead_mobs += mob
var/mob/living/mob_to_store
if(dead_mobs.len)
mob_to_store = pick(dead_mobs)
mob_to_store.forceMove(src)
stored_units += mob_size
- for(var/obj/item/limb/L in loc)
- L.forceMove(src)
+ for(var/obj/item/limb/limb in loc)
+ limb.forceMove(src)
return stored_units
/obj/structure/closet/bodybag/attack_hand(mob/living/user)
diff --git a/code/game/objects/items/explosives/plastic.dm b/code/game/objects/items/explosives/plastic.dm
index 4c2a1774d4db..830df9659070 100644
--- a/code/game/objects/items/explosives/plastic.dm
+++ b/code/game/objects/items/explosives/plastic.dm
@@ -213,23 +213,6 @@
return TRUE
-/obj/item/explosive/plastic/breaching_charge/can_place(mob/user, atom/target)
- if(!is_type_in_list(target, breachable))//only items on the list are allowed
- to_chat(user, SPAN_WARNING("You cannot plant \the [name] on \the [target]!"))
- return FALSE
-
- if(SSinterior.in_interior(target))// vehicle checks again JUST IN CASE
- to_chat(user, SPAN_WARNING("It's too cramped in here to deploy \the [src]."))
- return FALSE
-
- if(istype(target, /obj/structure/window))//no breaching charges on the briefing windows / brig / CIC e.e
- var/obj/structure/window/W = target
- if(W.not_damageable)
- to_chat(user, SPAN_WARNING("[W] is much too tough for you to do anything to it with [src].")) //On purpose to mimic wall message
- return FALSE
-
- return TRUE
-
/obj/item/explosive/plastic/proc/calculate_pixel_offset(mob/user, atom/target)
switch(get_dir(user, target))
if(NORTH)
@@ -312,13 +295,6 @@
cell_explosion(target_turf, 120, 30, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, cause_data)
qdel(src)
-/obj/item/explosive/plastic/breaching_charge/handle_explosion(turf/target_turf, dir, cause_data)
- var/explosion_target = get_step(target_turf, dir)
- create_shrapnel(explosion_target, 40, dir, angle,/datum/ammo/bullet/shrapnel/metal, cause_data)
- sleep(1)// prevents explosion from eating shrapnel
- cell_explosion(target_turf, 60, 60, EXPLOSION_FALLOFF_SHAPE_EXPONENTIAL, dir, cause_data)
- qdel(src)
-
/obj/item/explosive/plastic/proc/delayed_prime(turf/target_turf)
prime(TRUE)
@@ -342,3 +318,65 @@
min_timer = 3
penetration = 0.60
deploying_time = 10
+ var/shrapnel_volume = 40
+
+/obj/item/explosive/plastic/breaching_charge/can_place(mob/user, atom/target)
+ if(!is_type_in_list(target, breachable))//only items on the list are allowed
+ to_chat(user, SPAN_WARNING("You cannot plant [name] on [target]!"))
+ return FALSE
+
+ if(SSinterior.in_interior(target))// vehicle checks again JUST IN CASE
+ to_chat(user, SPAN_WARNING("It's too cramped in here to deploy [src]."))
+ return FALSE
+
+ if(istype(target, /obj/structure/window))//no breaching charges on the briefing windows / brig / CIC e.e
+ var/obj/structure/window/window = target
+ if(window.not_damageable)
+ to_chat(user, SPAN_WARNING("[window] is much too tough for you to do anything to it with [src].")) //On purpose to mimic wall message
+ return FALSE
+
+ if(istype(target, /turf/closed/wall))
+ var/turf/closed/wall/targeted_wall = target
+ if(targeted_wall.hull)
+ to_chat(user, SPAN_WARNING("You are unable to stick [src] to [targeted_wall]!"))
+ return FALSE
+
+ return TRUE
+
+/obj/item/explosive/plastic/breaching_charge/handle_explosion(turf/target_turf, dir, cause_data)
+ var/explosion_target = get_step(target_turf, dir)
+ create_shrapnel(explosion_target, shrapnel_volume, dir, angle,/datum/ammo/bullet/shrapnel/metal, cause_data)
+ addtimer(CALLBACK(src, PROC_REF(trigger_explosion), target_turf, dir, cause_data), 1)
+
+/obj/item/explosive/plastic/breaching_charge/proc/trigger_explosion(turf/target_turf, dir, cause_data)
+ cell_explosion(target_turf, 60, 60, EXPLOSION_FALLOFF_SHAPE_EXPONENTIAL, dir, cause_data)
+ qdel(src)
+
+/obj/item/explosive/plastic/breaching_charge/plasma
+ name = "plasma charge"
+ desc = "An alien explosive device. Who knows what it might do."
+ icon_state = "plasma-charge"
+ overlay_image = "plasma-active"
+ w_class = SIZE_SMALL
+ angle = 55
+ timer = 5
+ min_timer = 5
+ penetration = 0.60
+ deploying_time = 10
+ flags_item = NOBLUDGEON|ITEM_PREDATOR
+ shrapnel_volume = 10
+
+/obj/item/explosive/plastic/breaching_charge/plasma/can_place(mob/user, atom/target)
+ if(!HAS_TRAIT(user, TRAIT_YAUTJA_TECH))
+ to_chat(user, SPAN_WARNING("You don't quite understand how the device works..."))
+ return FALSE
+ . = ..()
+
+/obj/item/explosive/plastic/breaching_charge/plasma/handle_explosion(turf/target_turf, dir, cause_data)
+ var/explosion_target = get_step(target_turf, dir)
+ create_shrapnel(explosion_target, shrapnel_volume, dir, angle,/datum/ammo/bullet/shrapnel/plasma, cause_data)
+ addtimer(CALLBACK(src, PROC_REF(trigger_explosion), target_turf, dir, cause_data), 1)
+
+/obj/item/explosive/plastic/breaching_charge/plasma/trigger_explosion(turf/target_turf, dir, cause_data)
+ cell_explosion(target_turf, 90, 90, EXPLOSION_FALLOFF_SHAPE_EXPONENTIAL, dir, cause_data)
+ qdel(src)
diff --git a/code/game/objects/items/storage/pouch.dm b/code/game/objects/items/storage/pouch.dm
index 7a49f48cdc92..dc3ee0ba1506 100644
--- a/code/game/objects/items/storage/pouch.dm
+++ b/code/game/objects/items/storage/pouch.dm
@@ -58,29 +58,12 @@
max_w_class = SIZE_MEDIUM
cant_hold = list( //Prevent inventory bloat
/obj/item/storage/firstaid,
- /obj/item/storage/bible
+ /obj/item/storage/bible,
+ /obj/item/storage/box,
)
storage_slots = null
max_storage_space = 2
-/obj/item/storage/pouch/general/attackby(obj/item/W, mob/user)
- if(istype(W, /obj/item/ammo_magazine/shotgun))
- var/obj/item/ammo_magazine/shotgun/M = W
- dump_ammo_to(M,user)
- else if(istype(W, /obj/item/storage/box/nade_box) || istype(W, /obj/item/storage/box/m94))
- dump_into(W, user)
- else
- return ..()
-
-/obj/item/storage/pouch/general/can_be_inserted(obj/item/W, stop_messages)
- . = ..()
- if(. && W.w_class == SIZE_MEDIUM)
- for(var/obj/item/I in return_inv())
- if(I.w_class >= SIZE_MEDIUM)
- if(!stop_messages)
- to_chat(usr, SPAN_NOTICE("[src] is already too bulky with [I]."))
- return FALSE
-
/obj/item/storage/pouch/general/medium
name = "medium general pouch"
desc = "A general-purpose pouch used to carry a variety of differently sized items."
@@ -468,13 +451,6 @@
for(var/i = 1 to storage_slots)
new /obj/item/ammo_magazine/pistol/vp78(src)
-/obj/item/storage/pouch/magazine/shotgun/attackby(obj/item/W, mob/living/user)
- if(istype(W, /obj/item/ammo_magazine/shotgun))
- var/obj/item/ammo_magazine/shotgun/M = W
- dump_ammo_to(M, user, M.transfer_handful_amount)
- else
- return ..()
-
/obj/item/storage/pouch/magazine/pulse_rifle/fill_preset_inventory()
for(var/i = 1 to storage_slots)
new /obj/item/ammo_magazine/rifle(src)
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index 3f7192b0090b..41ac80bfdc58 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -517,7 +517,7 @@
var/obj/item/clothing/gloves/yautja/hunter/Y = H.gloves
if(Y && istype(Y) && Y.cloaked)
to_chat(H, SPAN_WARNING(" Your bracers hiss and spark as they short out!"))
- Y.decloak(H, TRUE)
+ Y.decloak(H, TRUE, DECLOAK_SUBMERGED)
else if(isxeno(C))
river_slowdown -= 0.7
diff --git a/code/modules/admin/player_panel/actions/transform.dm b/code/modules/admin/player_panel/actions/transform.dm
index 11dd7525bb07..185165357e05 100644
--- a/code/modules/admin/player_panel/actions/transform.dm
+++ b/code/modules/admin/player_panel/actions/transform.dm
@@ -38,6 +38,11 @@ GLOBAL_LIST_INIT(pp_transformables, list(
name = "Facehugger",
key = /mob/living/carbon/xenomorph/facehugger,
color = "purple"
+ ),
+ list(
+ name = "Lesser Drone",
+ key = /mob/living/carbon/xenomorph/lesser_drone,
+ color = "purple"
)
),
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 5698c30c0acf..16afa8d1b4f2 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -590,7 +590,7 @@ var/const/MAX_SAVE_SLOTS = 10
dat += "
"
dat += "
Gameplay Toggles:
"
dat += "
Toggle Being Able to Hurt Yourself: \
- [toggle_prefs & TOGGLE_IGNORE_SELF ? "On" : "Off"]"
+
[toggle_prefs & TOGGLE_IGNORE_SELF ? "Off" : "On"]"
dat += "
Toggle Help Intent Safety: \
[toggle_prefs & TOGGLE_HELP_INTENT_SAFETY ? "On" : "Off"]"
dat += "
Toggle Middle Mouse Ability Activation: \
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 0a1b54112f18..0f482fa7f894 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -199,22 +199,22 @@
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
UI_style = sanitize_inlist(UI_style, list("white", "dark", "midnight", "orange", "old"), initial(UI_style))
tgui_say = sanitize_integer(tgui_say, FALSE, TRUE, TRUE)
- be_special = sanitize_integer(be_special, 0, 65535, initial(be_special))
+ be_special = sanitize_integer(be_special, 0, SHORT_REAL_LIMIT, initial(be_special))
default_slot = sanitize_integer(default_slot, 1, MAX_SAVE_SLOTS, initial(default_slot))
- toggles_chat = sanitize_integer(toggles_chat, 0, 65535, initial(toggles_chat))
- chat_display_preferences = sanitize_integer(chat_display_preferences, 0, 65535, initial(chat_display_preferences))
- toggles_ghost = sanitize_integer(toggles_ghost, 0, 65535, initial(toggles_ghost))
- toggles_langchat = sanitize_integer(toggles_langchat, 0, 65535, initial(toggles_langchat))
- toggles_sound = sanitize_integer(toggles_sound, 0, 65535, initial(toggles_sound))
- toggle_prefs = sanitize_integer(toggle_prefs, 0, 65535, initial(toggle_prefs))
- toggles_flashing= sanitize_integer(toggles_flashing, 0, 65535, initial(toggles_flashing))
- toggles_ert = sanitize_integer(toggles_ert, 0, 65535, initial(toggles_ert))
- toggles_admin = sanitize_integer(toggles_admin, 0, 65535, initial(toggles_admin))
+ toggles_chat = sanitize_integer(toggles_chat, 0, SHORT_REAL_LIMIT, initial(toggles_chat))
+ chat_display_preferences = sanitize_integer(chat_display_preferences, 0, SHORT_REAL_LIMIT, initial(chat_display_preferences))
+ toggles_ghost = sanitize_integer(toggles_ghost, 0, SHORT_REAL_LIMIT, initial(toggles_ghost))
+ toggles_langchat = sanitize_integer(toggles_langchat, 0, SHORT_REAL_LIMIT, initial(toggles_langchat))
+ toggles_sound = sanitize_integer(toggles_sound, 0, SHORT_REAL_LIMIT, initial(toggles_sound))
+ toggle_prefs = sanitize_integer(toggle_prefs, 0, SHORT_REAL_LIMIT, initial(toggle_prefs))
+ toggles_flashing= sanitize_integer(toggles_flashing, 0, SHORT_REAL_LIMIT, initial(toggles_flashing))
+ toggles_ert = sanitize_integer(toggles_ert, 0, SHORT_REAL_LIMIT, initial(toggles_ert))
+ toggles_admin = sanitize_integer(toggles_admin, 0, SHORT_REAL_LIMIT, initial(toggles_admin))
UI_style_color = sanitize_hexcolor(UI_style_color, initial(UI_style_color))
UI_style_alpha = sanitize_integer(UI_style_alpha, 0, 255, initial(UI_style_alpha))
item_animation_pref_level = sanitize_integer(item_animation_pref_level, SHOW_ITEM_ANIMATIONS_NONE, SHOW_ITEM_ANIMATIONS_ALL, SHOW_ITEM_ANIMATIONS_ALL)
pain_overlay_pref_level = sanitize_integer(pain_overlay_pref_level, PAIN_OVERLAY_BLURRY, PAIN_OVERLAY_LEGACY, PAIN_OVERLAY_BLURRY)
- window_skin = sanitize_integer(window_skin, 0, 65535, initial(window_skin))
+ window_skin = sanitize_integer(window_skin, 0, SHORT_REAL_LIMIT, initial(window_skin))
ghost_vision_pref = sanitize_inlist(ghost_vision_pref, list(GHOST_VISION_LEVEL_NO_NVG, GHOST_VISION_LEVEL_MID_NVG, GHOST_VISION_LEVEL_FULL_NVG), GHOST_VISION_LEVEL_MID_NVG)
ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit))
playtime_perks = sanitize_integer(playtime_perks, 0, 1, 1)
diff --git a/code/modules/cm_aliens/structures/special/pylon_core.dm b/code/modules/cm_aliens/structures/special/pylon_core.dm
index a29b49b7745a..993d4f833fa6 100644
--- a/code/modules/cm_aliens/structures/special/pylon_core.dm
+++ b/code/modules/cm_aliens/structures/special/pylon_core.dm
@@ -327,5 +327,22 @@
// Tell admins that this condition is reached so they know what has happened if it fails somehow
return
+/obj/effect/alien/resin/special/pylon/core/proc/spawn_lesser_drone(mob/xeno_candidate)
+ if(!linked_hive.can_spawn_as_lesser_drone(xeno_candidate))
+ return FALSE
+
+ var/mob/living/carbon/xenomorph/lesser_drone/new_drone = new /mob/living/carbon/xenomorph/lesser_drone(loc, null, linked_hive.hivenumber)
+ xeno_candidate.mind.transfer_to(new_drone, TRUE)
+ new_drone.visible_message(SPAN_XENODANGER("A lesser drone emerges out of [src]!"), SPAN_XENODANGER("You emerge out of [src] and awaken from your slumber. For the Hive!"))
+ playsound(new_drone, 'sound/effects/xeno_newlarva.ogg', 25, TRUE)
+ new_drone.generate_name()
+
+ return TRUE
+
+/obj/effect/alien/resin/special/pylon/core/attack_ghost(mob/dead/observer/user)
+ . = ..()
+ if(SSticker.mode.check_xeno_late_join(user))
+ SSticker.mode.attempt_to_join_as_lesser_drone(user)
+
#undef PYLON_REPAIR_TIME
#undef PYLON_WEEDS_REGROWTH_TIME
diff --git a/code/modules/cm_preds/yaut_bracers.dm b/code/modules/cm_preds/yaut_bracers.dm
index 5c4079b2be23..9642b4b9f5a5 100644
--- a/code/modules/cm_preds/yaut_bracers.dm
+++ b/code/modules/cm_preds/yaut_bracers.dm
@@ -31,6 +31,10 @@
var/notification_sound = TRUE // Whether the bracer pings when a message comes or not
var/charge = 1500
var/charge_max = 1500
+ /// The amount charged per process
+ var/charge_rate = 30
+ /// Cooldown on draining power from APC
+ var/charge_cooldown = COOLDOWN_BRACER_CHARGE
var/cloaked = 0
var/cloak_timer = 0
var/cloak_malfunction = 0
@@ -41,6 +45,7 @@
var/mob/living/carbon/human/owner //Pred spawned on, or thrall given to.
var/obj/item/clothing/gloves/yautja/linked_bracer //Bracer linked to this one (thrall or mentor).
+ COOLDOWN_DECLARE(bracer_recharge)
/obj/item/clothing/gloves/yautja/equipped(mob/user, slot)
. = ..()
@@ -75,11 +80,27 @@
if(!ishuman(loc))
STOP_PROCESSING(SSobj, src)
return
- var/mob/living/carbon/human/H = loc
+ var/mob/living/carbon/human/human_holder = loc
- charge = min(charge + 30, charge_max)
- var/perc_charge = (charge / charge_max * 100)
- H.update_power_display(perc_charge)
+ if(charge < charge_max)
+ var/charge_increase = charge_rate
+ if(is_ground_level(human_holder.z))
+ charge_increase = charge_rate / 6
+ else if(is_mainship_level(human_holder.z))
+ charge_increase = charge_rate / 3
+
+ charge = min(charge + charge_increase, charge_max)
+ var/perc_charge = (charge / charge_max * 100)
+ human_holder.update_power_display(perc_charge)
+
+ //Non-Yautja have a chance to get stunned with each power drain
+ if(!cloaked)
+ return
+ if(human_holder.stat == DEAD)
+ decloak(human_holder, TRUE)
+ if(!HAS_TRAIT(human_holder, TRAIT_YAUTJA_TECH) && !human_holder.hunter_data.thralled && prob(15))
+ decloak(human_holder)
+ shock_user(human_holder)
/// handles decloaking only on HUNTER gloves
/obj/item/clothing/gloves/yautja/proc/decloak()
@@ -101,15 +122,6 @@
var/perc = (charge / charge_max * 100)
human.update_power_display(perc)
- //Non-Yautja have a chance to get stunned with each power drain
- if(!HAS_TRAIT(human, TRAIT_YAUTJA_TECH) && !human.hunter_data.thralled)
- if(prob(15))
- if(cloaked)
- decloak(human)
- cloak_timer = world.time + 5 SECONDS
- shock_user(human)
- return FALSE
-
return TRUE
/obj/item/clothing/gloves/yautja/proc/shock_user(mob/living/carbon/human/M)
@@ -242,7 +254,7 @@
if(wearer.gloves == src)
wearer.visible_message(SPAN_DANGER("You hear a hiss and crackle!"), SPAN_DANGER("Your bracers hiss and spark!"), SPAN_DANGER("You hear a hiss and crackle!"))
if(cloaked)
- decloak(wearer)
+ decloak(wearer, TRUE, DECLOAK_EMP)
else
var/turf/our_turf = get_turf(src)
our_turf.visible_message(SPAN_DANGER("You hear a hiss and crackle!"), SPAN_DANGER("You hear a hiss and crackle!"))
@@ -282,29 +294,25 @@
var/mob/living/carbon/human/human = loc
- if(cloaked)
- charge = max(charge - 10, 0)
- if(charge <= 0)
- decloak(loc)
- //Non-Yautja have a chance to get stunned with each power drain
- if(!isyautja(human))
- if(prob(15))
- decloak(human)
- shock_user(human)
- return
+ //Non-Yautja have a chance to get stunned with each power drain
+ if((!HAS_TRAIT(human, TRAIT_YAUTJA_TECH) && !human.hunter_data.thralled) && prob(15))
+ if(cloaked)
+ decloak(human, TRUE, DECLOAK_SPECIES)
+ shock_user(human)
+
return ..()
/obj/item/clothing/gloves/yautja/hunter/dropped(mob/user)
move_chip_to_bracer()
if(cloaked)
- decloak(user)
+ decloak(user, TRUE)
..()
/obj/item/clothing/gloves/yautja/hunter/on_enter_storage(obj/item/storage/S)
if(ishuman(loc))
var/mob/living/carbon/human/human = loc
if(cloaked)
- decloak(human)
+ decloak(human, TRUE)
. = ..()
//We use this to activate random verbs for non-Yautja
@@ -421,15 +429,17 @@
var/gear_on_almayer = 0
var/gear_low_orbit = 0
var/closest = 10000
+ /// The item itself, to be referenced so Yautja know what to look for.
+ var/obj/closest_item
var/direction = -1
var/atom/areaLoc = null
- for(var/obj/item/I as anything in GLOB.loose_yautja_gear)
- var/atom/loc = get_true_location(I)
- if(I.anchored)
+ for(var/obj/item/tracked_item as anything in GLOB.loose_yautja_gear)
+ var/atom/loc = get_true_location(tracked_item)
+ if(tracked_item.anchored)
continue
- if(is_honorable_carrier(recursive_holder_check(I)))
+ if(is_honorable_carrier(recursive_holder_check(tracked_item)))
continue
- if(istype(get_area(I), /area/yautja))
+ if(istype(get_area(tracked_item), /area/yautja))
continue
if(is_reserved_level(loc.z))
gear_low_orbit++
@@ -441,6 +451,7 @@
var/dist = get_dist(M,loc)
if(dist < closest)
closest = dist
+ closest_item = tracked_item
direction = get_dir(M,loc)
areaLoc = loc
for(var/mob/living/carbon/human/Y as anything in GLOB.yautja_mob_list)
@@ -472,9 +483,9 @@
output = TRUE
var/areaName = get_area_name(areaLoc)
if(closest == 0)
- to_chat(M, SPAN_NOTICE("You are directly on top of the closest signature."))
+ to_chat(M, SPAN_NOTICE("You are directly on top of the[closest_item ? " [closest_item.name]'s" : ""] signature."))
else
- to_chat(M, SPAN_NOTICE("The closest signature is [closest > 10 ? "approximately [round(closest, 10)]" : "[closest]"] paces [dir2text(direction)] in [areaName]."))
+ to_chat(M, SPAN_NOTICE("The closest signature[closest_item ? ", a [closest_item.name]" : ""], is [closest > 10 ? "approximately [round(closest, 10)]" : "[closest]"] paces [dir2text(direction)] in [areaName]."))
if(!output)
to_chat(M, SPAN_NOTICE("There are no signatures that require your attention."))
return TRUE
@@ -528,7 +539,6 @@
if(true_cloak)
M.invisibility = INVISIBILITY_LEVEL_ONE
M.see_invisible = SEE_INVISIBLE_LEVEL_ONE
- new_alpha = 75
log_game("[key_name_admin(usr)] has enabled their cloaking device.")
M.visible_message(SPAN_WARNING("[M] vanishes into thin air!"), SPAN_NOTICE("You are now invisible to normal detection."))
@@ -553,17 +563,18 @@
sparks.set_up(5, 4, src)
sparks.start()
- decloak(wearer, TRUE)
+ decloak(wearer, TRUE, DECLOAK_EXTINGUISHER)
-/obj/item/clothing/gloves/yautja/hunter/decloak(mob/user, forced)
+/obj/item/clothing/gloves/yautja/hunter/decloak(mob/user, forced, force_multipler = DECLOAK_FORCED)
if(!user)
return
UnregisterSignal(user, COMSIG_HUMAN_EXTINGUISH)
UnregisterSignal(user, COMSIG_HUMAN_PRE_BULLET_ACT)
+ var/decloak_timer = (DECLOAK_STANDARD * force_multipler)
if(forced)
- cloak_malfunction = world.time + 10 SECONDS
+ cloak_malfunction = world.time + decloak_timer
cloaked = FALSE
log_game("[key_name_admin(usr)] has disabled their cloaking device.")
@@ -573,7 +584,7 @@
if(true_cloak)
user.invisibility = initial(user.invisibility)
user.see_invisible = initial(user.see_invisible)
- cloak_timer = world.time + 5 SECONDS
+ cloak_timer = world.time + (DECLOAK_STANDARD / 2)
var/datum/mob_hud/security/advanced/SA = huds[MOB_HUD_SECURITY_ADVANCED]
SA.add_to_hud(user)
diff --git a/code/modules/cm_preds/yaut_items.dm b/code/modules/cm_preds/yaut_items.dm
index 8a3306817078..47b2408c4f37 100644
--- a/code/modules/cm_preds/yaut_items.dm
+++ b/code/modules/cm_preds/yaut_items.dm
@@ -327,6 +327,7 @@
unacidable = TRUE
ignore_z = TRUE
black_market_value = 100
+ flags_item = ITEM_PREDATOR
/obj/item/device/radio/headset/yautja/talk_into(mob/living/M as mob, message, channel, verb = "commands", datum/language/speaking)
if(!isyautja(M)) //Nope.
@@ -338,9 +339,6 @@
to_chat(hellhound, "\[Radio\]: [M.real_name] [verb], '[message]'.")
..()
-/obj/item/device/radio/headset/yautja/attackby()
- return
-
/obj/item/device/radio/headset/yautja/elder //primarily for use in another MR
name = "\improper Elder Communicator"
volume_settings = list(RADIO_VOLUME_QUIET_STR, RADIO_VOLUME_RAISED_STR, RADIO_VOLUME_IMPORTANT_STR, RADIO_VOLUME_CRITICAL_STR)
@@ -697,6 +695,7 @@
var/tether_range = 5
var/mob/trapped_mob
layer = LOWER_ITEM_LAYER
+ flags_item = ITEM_PREDATOR
/obj/item/hunting_trap/Destroy()
cleanup_tether()
diff --git a/code/modules/cm_preds/yaut_machines.dm b/code/modules/cm_preds/yaut_machines.dm
index adba69043dcc..a1782ca22b85 100644
--- a/code/modules/cm_preds/yaut_machines.dm
+++ b/code/modules/cm_preds/yaut_machines.dm
@@ -5,7 +5,7 @@
icon_state = "globe"
breakable = FALSE
- minimap_type = MINIMAP_FLAG_XENO|MINIMAP_FLAG_USCM
+ minimap_type = MINIMAP_FLAG_ALL
/obj/structure/machinery/autolathe/yautja
name = "yautja autolathe"
diff --git a/code/modules/cm_preds/yaut_shield.dm b/code/modules/cm_preds/yaut_shield.dm
index 2e036b4a5357..7b84f935f4c5 100644
--- a/code/modules/cm_preds/yaut_shield.dm
+++ b/code/modules/cm_preds/yaut_shield.dm
@@ -45,10 +45,10 @@
M.apply_effect(3, DAZE)
M.apply_effect(5, SLOW)
-/obj/item/weapon/shield/riot/yautja/attackby(obj/item/I, mob/user)
+/obj/item/weapon/shield/riot/yautja/attackby(obj/item/attacking_item, mob/user)
if(cooldown < world.time - 25)
- if(istype(I, /obj/item/weapon) && (I.flags_item & ITEM_PREDATOR))
- user.visible_message(SPAN_WARNING("[user] bashes \the [src] with \the [I]!"))
+ if(istype(attacking_item, /obj/item/weapon) && (attacking_item.flags_item & ITEM_PREDATOR))
+ user.visible_message(SPAN_WARNING("[user] bashes [src] with [attacking_item]!"))
playsound(user.loc, 'sound/effects/shieldbash.ogg', 25, 1)
cooldown = world.time
else
diff --git a/code/modules/cm_preds/yaut_weapons.dm b/code/modules/cm_preds/yaut_weapons.dm
index 40006dafe60b..986b30c27ddf 100644
--- a/code/modules/cm_preds/yaut_weapons.dm
+++ b/code/modules/cm_preds/yaut_weapons.dm
@@ -891,7 +891,7 @@
/obj/item/weapon/gun/energy/yautja/plasmarifle/load_into_chamber()
if(charge_time >= 80)
ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/rifle/blast]
- charge_time = 0
+ charge_time -= 80
else
ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/rifle/bolt]
charge_time -= 10
@@ -912,6 +912,8 @@
if(refund) charge_time *= 2
return TRUE
+#define FIRE_MODE_STANDARD "Standard"
+#define FIRE_MODE_INCENDIARY "Incendiary"
/obj/item/weapon/gun/energy/yautja/plasmapistol
name = "plasma pistol"
desc = "A plasma pistol capable of rapid fire. It has an integrated battery. Can be used to set fires, either to braziers or on people."
@@ -924,7 +926,12 @@
ammo = /datum/ammo/energy/yautja/pistol
muzzle_flash = null // TO DO, add a decent one.
w_class = SIZE_MEDIUM
+ /// Max amount of shots
var/charge_time = 40
+ /// Amount of charge_time drained per shot
+ var/shot_cost = 1
+ /// standard (sc = 1) or incendiary (sc = 5)
+ var/mode = FIRE_MODE_STANDARD
flags_gun_features = GUN_UNUSUAL_DESIGN
flags_item = ITEM_PREDATOR|IGNITING_ITEM|TWOHANDED
@@ -965,9 +972,14 @@
if(isyautja(user))
. = ..()
. += SPAN_NOTICE("It currently has [charge_time]/40 charge.")
+
+ if(mode == FIRE_MODE_INCENDIARY)
+ . += SPAN_RED("It is set to fire incendiary plasma bolts.")
+ else
+ . += SPAN_ORANGE("It is set to fire plasma bolts.")
else
. = list()
- . += SPAN_NOTICE("This thing looks like an alien rifle of some kind. Strange.")
+ . += SPAN_NOTICE("This thing looks like an alien gun of some kind. Strange.")
/obj/item/weapon/gun/energy/yautja/plasmapistol/able_to_fire(mob/user)
@@ -983,7 +995,7 @@
var/obj/item/projectile/projectile = create_bullet(ammo, initial(name))
projectile.SetLuminosity(1)
in_chamber = projectile
- charge_time--
+ charge_time -= shot_cost
return in_chamber
/obj/item/weapon/gun/energy/yautja/plasmapistol/has_ammunition()
@@ -995,9 +1007,30 @@
/obj/item/weapon/gun/energy/yautja/plasmapistol/delete_bullet(obj/item/projectile/projectile_to_fire, refund = 0)
qdel(projectile_to_fire)
- if(refund) charge_time *= 2
+ if(refund)
+ charge_time += shot_cost
+ log_debug("Plasma Pistol refunded shot.")
return TRUE
+/obj/item/weapon/gun/energy/yautja/plasmapistol/use_unique_action()
+ switch(mode)
+ if(FIRE_MODE_STANDARD)
+ mode = FIRE_MODE_INCENDIARY
+ shot_cost = 5
+ fire_delay = FIRE_DELAY_TIER_5
+ to_chat(usr, SPAN_NOTICE("[src] will now fire incendiary plasma bolts."))
+ ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/pistol/incendiary]
+
+ if(FIRE_MODE_INCENDIARY)
+ mode = FIRE_MODE_STANDARD
+ shot_cost = 1
+ fire_delay = FIRE_DELAY_TIER_7
+ to_chat(usr, SPAN_NOTICE("[src] will now fire plasma bolts."))
+ ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/pistol]
+
+#undef FIRE_MODE_STANDARD
+#undef FIRE_MODE_INCENDIARY
+
/obj/item/weapon/gun/energy/yautja/plasma_caster
name = "plasma caster"
desc = "A powerful, shoulder-mounted energy weapon."
diff --git a/code/modules/mob/dead/observer/actions.dm b/code/modules/mob/dead/observer/actions.dm
index 49a3890088f6..ff897db4a3f6 100644
--- a/code/modules/mob/dead/observer/actions.dm
+++ b/code/modules/mob/dead/observer/actions.dm
@@ -81,6 +81,22 @@
if(SSticker.mode.check_xeno_late_join(owner))
SSticker.mode.attempt_to_join_as_xeno(owner)
+/datum/action/observer_action/join_lesser_drone
+ name = "Join as Lesser Drone"
+ action_icon_state = "join_lesser_drone"
+ listen_signal = COMSIG_KB_OBSERVER_JOIN_LESSER_DRONE
+
+/datum/action/observer_action/join_lesser_drone/action_activate()
+ if(!owner.client)
+ return
+
+ if(SSticker.current_state < GAME_STATE_PLAYING || !SSticker.mode)
+ owner.balloon_alert(owner, "game must start!")
+ return
+
+ if(SSticker.mode.check_xeno_late_join(owner))
+ SSticker.mode.attempt_to_join_as_lesser_drone(owner)
+
/datum/keybinding/observer
category = CATEGORY_OBSERVER
weight = WEIGHT_DEAD
@@ -108,3 +124,10 @@
name = "join_pred"
full_name = "Join the Hunt"
keybind_signal = COMSIG_KB_OBSERVER_JOIN_PREDATOR
+
+/datum/keybinding/observer/join_lesser_drone
+ hotkey_keys = list("Unbound")
+ classic_keys = list("Unbound")
+ name = "join_lesser_drone"
+ full_name = "Join as Lesser Drone"
+ keybind_signal = COMSIG_KB_OBSERVER_JOIN_LESSER_DRONE
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 3a35eecd8557..9e8fa264af1d 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -54,7 +54,7 @@
var/datum/health_scan/last_health_display
var/ghost_orbit = GHOST_ORBIT_CIRCLE
var/own_orbit_size = 0
- var/observer_actions = list(/datum/action/observer_action/join_xeno)
+ var/observer_actions = list(/datum/action/observer_action/join_xeno, /datum/action/observer_action/join_lesser_drone)
var/datum/action/minimap/observer/minimap
var/larva_queue_cached_message
///Used to bypass time of death checks such as when being selected for larva.
@@ -368,12 +368,13 @@ Works together with spawning an observer, noted above.
if(ghost.client.player_data)
ghost.client.player_data.load_timestat_data()
- // Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers
- // We don't change facehugger timeofdeath because they are still on cooldown if they died as a hugger
- var/new_tod = isfacehugger(src) ? 1 : ghost.timeofdeath
- // if they died as facehugger, bypass typical TOD checks
- ghost.bypass_time_of_death_checks = isfacehugger(src)
- ghost.client.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
+ // Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers or lesser drone
+ var/new_tod = (isfacehugger(src) || islesserdrone(src)) ? 1 : ghost.timeofdeath
+
+ // if they died as facehugger or lesser drone, bypass typical TOD checks
+ ghost.bypass_time_of_death_checks = (isfacehugger(src) || islesserdrone(src))
+
+ ghost.client?.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
ghost.set_huds_from_prefs()
@@ -418,9 +419,12 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(ghost && !is_admin_level(z))
ghost.timeofdeath = world.time
- // Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers
- var/new_tod = isfacehugger(src) ? 1 : world.time
- ghost.bypass_time_of_death_checks = isfacehugger(src)
+ // Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers or lesser drone
+ var/new_tod = (isfacehugger(src) || islesserdrone(src)) ? 1 : ghost.timeofdeath
+
+ // if they died as facehugger or lesser drone, bypass typical TOD checks
+ ghost.bypass_time_of_death_checks = (isfacehugger(src) || islesserdrone(src))
+
ghost.client?.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
if(is_nested && nest && !QDELETED(nest))
ghost.can_reenter_corpse = FALSE
@@ -779,6 +783,21 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(SSticker.mode.check_xeno_late_join(src))
SSticker.mode.attempt_to_join_as_facehugger(src)
+/mob/dead/verb/join_as_lesser_drone()
+ set category = "Ghost.Join"
+ set name = "Join as a Lesser Drone"
+ set desc = "Try joining as a Lesser Drone to support the hive."
+
+ if (!client)
+ return
+
+ if(SSticker.current_state < GAME_STATE_PLAYING || !SSticker.mode)
+ to_chat(src, SPAN_WARNING("The game hasn't started yet!"))
+ return
+
+ if(SSticker.mode.check_xeno_late_join(src))
+ SSticker.mode.attempt_to_join_as_lesser_drone(src)
+
/mob/dead/verb/join_as_zombie() //Adapted from join as hellhoud
set category = "Ghost.Join"
set name = "Join as Zombie"
diff --git a/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm b/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm
index b12ff5d6c3bb..6361ff595b10 100644
--- a/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm
+++ b/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm
@@ -185,7 +185,7 @@
var/is_shover_queen = isqueen(M)
var/can_resist_shove = M.hivenumber != src.hivenumber || ((isqueen(src) || IS_XENO_LEADER(src)) && !is_shover_queen)
var/can_mega_shove = is_shover_queen || IS_XENO_LEADER(M)
- if(can_mega_shove && !can_resist_shove)
+ if(can_mega_shove && !can_resist_shove || (mob_size < MOB_SIZE_XENO_SMALL && M.mob_size >= MOB_SIZE_XENO_SMALL))
playsound(loc, 'sound/weapons/alien_knockdown.ogg', 25, 1)
M.visible_message(SPAN_WARNING("\The [M] shoves \the [src] out of her way!"), \
SPAN_WARNING("You shove \the [src] out of your way!"), null, 5, CHAT_TYPE_XENO_COMBAT)
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_abilities.dm
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_abilities.dm
@@ -0,0 +1 @@
+
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_macros.dm b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_macros.dm
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_macros.dm
@@ -0,0 +1 @@
+
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_powers.dm
new file mode 100644
index 000000000000..515efea23d53
--- /dev/null
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_powers.dm
@@ -0,0 +1,6 @@
+/datum/action/xeno_action/onclick/plant_weeds/lesser/use_ability(atom/A)
+ if(!(locate(/obj/effect/alien/weeds/node) in orange(4, owner)))
+ to_chat(owner, SPAN_XENONOTICE("You can only plant resin nodes near other resin nodes!"))
+ return
+
+ . = ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
index 058e643f5c64..5c1584c565c6 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
@@ -25,7 +25,7 @@
var/obj/item/clothing/gloves/yautja/hunter/YG = locate(/obj/item/clothing/gloves/yautja/hunter) in human
if(isyautja(human) && YG)
if(YG.cloaked)
- YG.decloak(human)
+ YG.decloak(human, TRUE, DECLOAK_PREDALIEN)
YG.cloak_timer = xeno_cooldown * 0.1
else if(isxeno(carbon) && xeno.can_not_harm(carbon))
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
index ec1697f30081..6b847a6a4fec 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
@@ -82,15 +82,20 @@
var/list/hugger_image_index = list()
var/mutable_appearance/hugger_overlays_icon
+ var/mutable_appearance/eggsac_overlays_icon
/mob/living/carbon/xenomorph/carrier/update_icons()
. = ..()
-
- update_hugger_overlays()
+ if (mutation_type == CARRIER_NORMAL)
+ update_hugger_overlays()
+ if (mutation_type == CARRIER_EGGSAC)
+ update_eggsac_overlays()
/mob/living/carbon/xenomorph/carrier/proc/update_hugger_overlays()
if(!hugger_overlays_icon)
return
+ if(mutation_type != CARRIER_NORMAL)
+ return
overlays -= hugger_overlays_icon
hugger_overlays_icon.overlays.Cut()
@@ -99,7 +104,7 @@
hugger_image_index.Cut()
return
- update_icon_maths(round(( huggers_cur / huggers_max ) * 3.999) + 1)
+ update_clinger_maths(round(( huggers_cur / huggers_max ) * 3.999) + 1)
for(var/i in hugger_image_index)
if(stat == DEAD)
@@ -114,8 +119,8 @@
overlays += hugger_overlays_icon
-/mob/living/carbon/xenomorph/carrier/proc/update_icon_maths(number)
- var/funny_list = list(1,2,3,4)
+/mob/living/carbon/xenomorph/carrier/proc/update_clinger_maths(number)
+ var/clinger_list = list(1,2,3,4)
if(length(hugger_image_index) != number)
if(length(hugger_image_index) > number)
while(length(hugger_image_index) != number)
@@ -123,20 +128,56 @@
else
while(length(hugger_image_index) != number)
for(var/i in hugger_image_index)
- if(locate(i) in funny_list)
- funny_list -= i
- hugger_image_index += funny_list[rand(1,length(funny_list))]
+ if(locate(i) in clinger_list)
+ clinger_list -= i
+ hugger_image_index += clinger_list[rand(1,length(clinger_list))]
+
+/mob/living/carbon/xenomorph/carrier/proc/update_eggsac_overlays()
+ if(!eggsac_overlays_icon)
+ return
+ if(mutation_type != CARRIER_EGGSAC)
+ return
+
+ overlays -= eggsac_overlays_icon
+ eggsac_overlays_icon.overlays.Cut()
+
+ if(!eggs_cur)
+ return
+
+ ///Simplified image index change.
+ var/i = 0
+ if(eggs_cur > 8)
+ i = 3
+ else if (eggs_cur > 4)
+ i = 2
+ else if (eggs_cur > 0)
+ i = 1
+
+ if(stat != DEAD)
+ if(lying)
+ if((resting || sleeping) && (!knocked_down && !knocked_out && health > 0))
+ eggsac_overlays_icon.overlays += icon(icon, "eggsac_[i] Sleeping")
+ else
+ eggsac_overlays_icon.overlays +=icon(icon, "eggsac_[i] Knocked Down")
+ else
+ eggsac_overlays_icon.overlays +=icon(icon, "eggsac_[i]")
+
+ overlays += eggsac_overlays_icon
/mob/living/carbon/xenomorph/carrier/Initialize(mapload, mob/living/carbon/xenomorph/oldxeno, h_number)
. = ..()
hugger_overlays_icon = mutable_appearance('icons/mob/xenos/overlay_effects64x64.dmi',"empty")
+ eggsac_overlays_icon = mutable_appearance('icons/mob/xenos/overlay_effects64x64.dmi',"empty")
/mob/living/carbon/xenomorph/carrier/death(cause, gibbed)
. = ..(cause, gibbed)
if(.)
- var/chance = 75
+ var/chance = 75 //75% to drop an egg or hugger.
+ if(mutation_type == CARRIER_EGGSAC)
+ visible_message(SPAN_XENOWARNING("[src] throes as its eggsac bursts into a mess of acid!"))
+ playsound(src.loc, 'sound/effects/alien_egg_burst.ogg', 25, 1)
- if (huggers_cur)
+ if(huggers_cur)
//Hugger explosion, like an egg morpher
var/obj/item/clothing/mask/facehugger/hugger
visible_message(SPAN_XENOWARNING("The chittering mass of tiny aliens is trying to escape [src]!"))
@@ -145,10 +186,15 @@
hugger = new(loc, hivenumber)
step_away(hugger, src, 1)
- while (eggs_cur > 0)
+ var/eggs_dropped = FALSE
+ for(var/i in 1 to eggs_cur)
if(prob(chance))
new /obj/item/xeno_egg(loc, hivenumber)
- eggs_cur--
+ eggs_dropped = TRUE
+ eggs_cur = 0
+
+ if(eggs_dropped) //Checks whether or not to announce egg drop.
+ xeno_message(SPAN_XENOANNOUNCE("[src] has dropped some precious eggs!"), 2, hive.hivenumber)
/mob/living/carbon/xenomorph/carrier/get_status_tab_items()
. = ..()
@@ -272,6 +318,7 @@
if(eggs_cur < eggs_max)
if(stat == CONSCIOUS)
eggs_cur++
+ update_icons()
to_chat(src, SPAN_NOTICE("You store the egg and carry it for safekeeping. Now sheltering: [eggs_cur] / [eggs_max]."))
qdel(E)
else
@@ -306,6 +353,7 @@
return
E = new(src, hivenumber)
eggs_cur--
+ update_icons()
put_in_active_hand(E)
to_chat(src, SPAN_XENONOTICE("You grab one of the eggs in your storage. Now sheltering: [eggs_cur] / [eggs_max]."))
return
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm b/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
new file mode 100644
index 000000000000..48bf0d95ddcf
--- /dev/null
+++ b/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
@@ -0,0 +1,97 @@
+/datum/caste_datum/lesser_drone
+ caste_type = XENO_CASTE_LESSER_DRONE
+ tier = 1
+ melee_damage_lower = XENO_DAMAGE_TIER_1
+ melee_damage_upper = XENO_DAMAGE_TIER_1
+ melee_vehicle_damage = XENO_DAMAGE_TIER_1
+ max_health = XENO_HEALTH_LESSER_DRONE
+ plasma_gain = XENO_PLASMA_GAIN_TIER_7
+ plasma_max = XENO_PLASMA_TIER_3
+ crystal_max = XENO_CRYSTAL_LOW
+ xeno_explosion_resistance = XENO_NO_EXPLOSIVE_ARMOR
+ armor_deflection = XENO_NO_ARMOR
+ evasion = XENO_EVASION_LOW
+ speed = XENO_SPEED_TIER_6
+
+ evolution_allowed = FALSE
+ can_be_revived = FALSE
+
+ build_time_mult = BUILD_TIME_MULT_LESSER_DRONE
+
+ caste_desc = "A builder of hives."
+ can_hold_facehuggers = 1
+ can_hold_eggs = CAN_HOLD_TWO_HANDS
+ acid_level = 1
+ weed_level = WEED_LEVEL_STANDARD
+ max_build_dist = 1
+
+ tackle_min = 4
+ tackle_max = 5
+
+ aura_strength = 1
+
+ minimap_icon = "lesser_drone"
+
+/datum/caste_datum/lesser_drone/New()
+ . = ..()
+
+ resin_build_order = GLOB.resin_build_order_lesser_drone
+
+/mob/living/carbon/xenomorph/lesser_drone
+ caste_type = XENO_CASTE_LESSER_DRONE
+ name = XENO_CASTE_LESSER_DRONE
+ desc = "An alien drone. Looks... smaller."
+ icon = 'icons/mob/xenos/drone.dmi'
+ icon_size = 48
+ icon_state = "Lesser Drone Walking"
+ plasma_types = list(PLASMA_PURPLE)
+ tier = 0
+ mob_flags = NOBIOSCAN
+ mob_size = MOB_SIZE_XENO_VERY_SMALL
+ life_value = 0
+ default_honor_value = 0
+ show_only_numbers = TRUE
+ counts_for_slots = FALSE
+ counts_for_roundend = FALSE
+ refunds_larva_if_banished = FALSE
+ crit_health = 0
+ gib_chance = 100
+ acid_blood_damage = 15
+ base_actions = list(
+ /datum/action/xeno_action/onclick/xeno_resting,
+ /datum/action/xeno_action/onclick/regurgitate,
+ /datum/action/xeno_action/watch_xeno,
+ /datum/action/xeno_action/activable/tail_stab,
+ /datum/action/xeno_action/activable/corrosive_acid/weak,
+ /datum/action/xeno_action/onclick/emit_pheromones,
+ /datum/action/xeno_action/onclick/plant_weeds/lesser, //first macro
+ /datum/action/xeno_action/onclick/choose_resin, //second macro
+ /datum/action/xeno_action/activable/secrete_resin, //third macro
+ /datum/action/xeno_action/onclick/tacmap,
+ )
+ inherent_verbs = list(
+ /mob/living/carbon/xenomorph/proc/vent_crawl,
+ /mob/living/carbon/xenomorph/proc/rename_tunnel,
+ /mob/living/carbon/xenomorph/proc/set_hugger_reserve_for_morpher,
+ )
+
+ mutation_type = DRONE_NORMAL
+
+ icon_xeno = 'icons/mob/xenos/lesser_drone.dmi'
+ icon_xenonid = 'icons/mob/xenonids/lesser_drone.dmi'
+
+/mob/living/carbon/xenomorph/lesser_drone/age_xeno()
+ if(stat == DEAD || !caste || QDELETED(src) || !client)
+ return
+
+ age = XENO_NORMAL
+
+ hud_update()
+
+ xeno_jitter(25)
+
+/mob/living/carbon/xenomorph/lesser_drone/initialize_pass_flags(datum/pass_flags_container/PF)
+ ..()
+ if (PF)
+ PF.flags_pass = PASS_MOB_IS_XENO|PASS_MOB_THRU_XENO
+ PF.flags_can_pass_all = PASS_MOB_IS_XENO|PASS_MOB_THRU_XENO
diff --git a/code/modules/mob/living/carbon/xenomorph/mutators/strains/carrier/eggsac.dm b/code/modules/mob/living/carbon/xenomorph/mutators/strains/carrier/eggsac.dm
index c8e6b768f2bc..436adb79e5aa 100644
--- a/code/modules/mob/living/carbon/xenomorph/mutators/strains/carrier/eggsac.dm
+++ b/code/modules/mob/living/carbon/xenomorph/mutators/strains/carrier/eggsac.dm
@@ -36,6 +36,8 @@
playsound(carrier.loc, 'sound/voice/alien_facehugger_dies.ogg', 25, 1)
carrier.huggers_cur = 0
carrier.huggers_max = 0
+ carrier.update_hugger_overlays()
+ carrier.update_eggsac_overlays()
carrier.eggs_max = 12
carrier.extra_build_dist = 1
return TRUE
@@ -73,3 +75,4 @@
if(egg_generation_progress >= 15)
egg_generation_progress = 0
xeno.eggs_cur++
+ xeno.update_icons()
diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
index 060c3aea2814..eaff5a66309e 100644
--- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
@@ -1,5 +1,3 @@
-#define JOIN_AS_FACEHUGGER_DELAY 3 MINUTES
-
// Actual caste datum basedef
/datum/caste_datum
var/caste_type = ""
@@ -352,6 +350,9 @@
/// How many huggers can the hive support
var/playable_hugger_limit = 0
+ /// How many lesser drones the hive can support
+ var/lesser_drone_limit = 0
+
var/datum/tacmap/xeno/tacmap
var/minimap_type = MINIMAP_FLAG_XENO
@@ -893,8 +894,11 @@
qdel(S)
for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos)
if(get_area(xeno) != hijacked_dropship && xeno.loc && is_ground_level(xeno.loc.z))
- if(isfacehugger(xeno))
+ if(isfacehugger(xeno) || islesserdrone(xeno))
to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind."))
+ if(xeno.stomach_contents.len)
+ xeno.devour_timer = 0
+ xeno.handle_stomach_contents()
qdel(xeno)
continue
if(xeno.hunter_data.hunted && !isqueen(xeno))
@@ -908,7 +912,7 @@
qdel(xeno)
stored_larva++
continue
- if(!isfacehugger(xeno))
+ if(xeno.tier >= 1)
xenos_count++
for(var/i in GLOB.alive_mob_list)
var/mob/living/potential_host = i
@@ -1036,8 +1040,12 @@
to_chat(user, SPAN_WARNING("\The [GLOB.hive_datum[hivenumber]] cannot support more facehuggers! Limit: [current_hugger_count]/[playable_hugger_limit]"))
return FALSE
- if(alert(user, "Are you sure you want to become a facehugger?", "Confirmation", "Yes", "No") != "Yes")
+ if(tgui_alert(user, "Are you sure you want to become a facehugger?", "Confirmation", list("Yes", "No")) != "Yes")
return FALSE
+
+ if(!user.client)
+ return FALSE
+
return TRUE
/datum/hive_status/proc/spawn_as_hugger(mob/dead/observer/user, atom/A)
@@ -1047,6 +1055,53 @@
playsound(hugger, 'sound/effects/xeno_newlarva.ogg', 25, TRUE)
hugger.generate_name()
+/datum/hive_status/proc/update_lesser_drone_limit()
+ lesser_drone_limit = Ceiling(totalXenos.len / 3)
+
+/datum/hive_status/proc/can_spawn_as_lesser_drone(mob/dead/observer/user)
+ if(!GLOB.hive_datum || ! GLOB.hive_datum[hivenumber])
+ return FALSE
+
+ if(jobban_isbanned(user, JOB_XENOMORPH)) // User is jobbanned
+ to_chat(user, SPAN_WARNING("You are banned from playing aliens and cannot spawn as a xenomorph."))
+ return FALSE
+
+ if(world.time - user.timeofdeath < JOIN_AS_LESSER_DRONE_DELAY)
+ var/time_left = round((user.timeofdeath + JOIN_AS_LESSER_DRONE_DELAY - world.time) / 10)
+ to_chat(user, SPAN_WARNING("You ghosted too recently. You cannot become a lesser drone until 30 seconds have passed ([time_left] seconds remaining)."))
+ return FALSE
+
+ if(totalXenos.len <= 0)
+ to_chat(user, SPAN_WARNING("The hive has fallen, you can't join it!"))
+ return FALSE
+
+ if(!living_xeno_queen)
+ to_chat(user, SPAN_WARNING("The selected hive does not have a Queen!"))
+ return FALSE
+
+ if(!living_xeno_queen.ovipositor && !SSticker.mode.is_in_endgame)
+ to_chat(user, SPAN_WARNING("The selected hive does not have a Queen on Ovipositor!"))
+ return FALSE
+
+ update_lesser_drone_limit()
+
+ var/current_lesser_drone_count = 0
+ for(var/mob/mob as anything in totalXenos)
+ if(islesserdrone(mob))
+ current_lesser_drone_count++
+
+ if(lesser_drone_limit <= current_lesser_drone_count)
+ to_chat(user, SPAN_WARNING("[GLOB.hive_datum[hivenumber]] cannot support more lesser drones! Limit: [current_lesser_drone_count]/[lesser_drone_limit]"))
+ return FALSE
+
+ if(tgui_alert(user, "Are you sure you want to become a lesser drone?", "Confirmation", list("Yes", "No")) != "Yes")
+ return FALSE
+
+ if(!user.client)
+ return FALSE
+
+ return TRUE
+
///Called by /obj/item/alien_embryo when a host is bursting to determine extra larva per burst
/datum/hive_status/proc/increase_larva_after_burst()
var/extra_per_burst = CONFIG_GET(number/extra_larva_per_burst)
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index 7247e9b87b16..e7a160095705 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -676,9 +676,9 @@ GLOBAL_LIST_INIT(apc_wire_descriptions, list(
var/turf/T = get_turf(src)
var/obj/structure/cable/N = T.get_cable_node()
if(prob(50) && electrocute_mob(usr, N, N))
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(5, 1, src)
- s.start()
+ var/datum/effect_system/spark_spread/spark = new /datum/effect_system/spark_spread
+ spark.set_up(5, 1, src)
+ spark.start()
return
if(C.use(10))
user.visible_message(SPAN_NOTICE("[user] wires [src]'s frame."),
@@ -700,9 +700,9 @@ GLOBAL_LIST_INIT(apc_wire_descriptions, list(
to_chat(user, SPAN_WARNING("\The [src] lacks a terminal to remove."))
return
if (prob(50) && electrocute_mob(user, terminal.powernet, terminal))
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(5, 1, src)
- s.start()
+ var/datum/effect_system/spark_spread/spark = new /datum/effect_system/spark_spread
+ spark.set_up(5, 1, src)
+ spark.start()
return
new /obj/item/stack/cable_coil(loc,10)
user.visible_message(SPAN_NOTICE("[user] removes [src]'s wiring and terminal."),
@@ -798,41 +798,87 @@ GLOBAL_LIST_INIT(apc_wire_descriptions, list(
//Human mob special interaction goes here.
if(ishuman(user))
- var/mob/living/carbon/human/H = user
+ var/mob/living/carbon/human/grabber = user
- if(H.species.flags & IS_SYNTHETIC && H.a_intent == INTENT_GRAB)
- if(H.action_busy)
- return
-
- if(!do_after(H, 20, INTERRUPT_ALL, BUSY_ICON_GENERIC))
- return
+ if(grabber.a_intent == INTENT_GRAB)
- playsound(src.loc, 'sound/effects/sparks2.ogg', 25, 1)
+ //Synthpack recharge
+ if((grabber.species.flags & IS_SYNTHETIC) && istype(grabber.back, /obj/item/storage/backpack/marine/smartpack))
+ var/obj/item/storage/backpack/marine/smartpack/s_pack = grabber.back
+ if(grabber.action_busy)
+ return
- if(stat & BROKEN)
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(3, 1, src)
- s.start()
- to_chat(H, SPAN_DANGER("The APC's power currents surge eratically, damaging your chassis!"))
- H.apply_damage(10,0, BURN)
- else if(cell && cell.charge > 0)
- if(!istype(H.back, /obj/item/storage/backpack/marine/smartpack))
+ if(!do_after(grabber, 20, INTERRUPT_ALL, BUSY_ICON_GENERIC))
return
- var/obj/item/storage/backpack/marine/smartpack/S = H.back
- if(S.battery_charge < SMARTPACK_MAX_POWER_STORED)
- var/charge_to_use = min(cell.charge, SMARTPACK_MAX_POWER_STORED - S.battery_charge)
- if(!(cell.use(charge_to_use)))
+ playsound(src.loc, 'sound/effects/sparks2.ogg', 25, 1)
+
+ if(stat & BROKEN)
+ var/datum/effect_system/spark_spread/spark = new()
+ spark.set_up(3, 1, src)
+ spark.start()
+ to_chat(grabber, SPAN_DANGER("The APC's power currents surge eratically, damaging your chassis!"))
+ grabber.apply_damage(10,0, BURN)
+ else if(cell && cell.charge > 0)
+ if(!istype(s_pack))
return
- S.battery_charge += charge_to_use
- to_chat(user, SPAN_NOTICE("You slot your fingers into the APC interface and siphon off some of the stored charge. [S.name] now has [S.battery_charge]/[SMARTPACK_MAX_POWER_STORED]"))
- charging = APC_CHARGING
+
+ if(s_pack.battery_charge < SMARTPACK_MAX_POWER_STORED)
+ var/charge_to_use = min(cell.charge, SMARTPACK_MAX_POWER_STORED - s_pack.battery_charge)
+ if(!(cell.use(charge_to_use)))
+ return
+ s_pack.battery_charge += charge_to_use
+ to_chat(user, SPAN_NOTICE("You slot your fingers into the APC interface and siphon off some of the stored charge. [s_pack.name] now has [s_pack.battery_charge]/[SMARTPACK_MAX_POWER_STORED]"))
+ charging = APC_CHARGING
+ else
+ to_chat(user, SPAN_WARNING("[s_pack.name] is already fully charged."))
else
- to_chat(user, SPAN_WARNING("[S.name] is already fully charged."))
- else
- to_chat(user, SPAN_WARNING("There is no charge to draw from that APC."))
- return
- else if(H.species.can_shred(H))
+ to_chat(user, SPAN_WARNING("There is no charge to draw from that APC."))
+ return
+
+ // Yautja Bracer Recharge
+ var/obj/item/clothing/gloves/yautja/bracer = grabber.gloves
+ if(istype(bracer))
+ if(grabber.action_busy)
+ return FALSE
+ if(!COOLDOWN_FINISHED(bracer, bracer_recharge))
+ to_chat(user, SPAN_WARNING("It is too soon for [bracer.name] to siphon power again. Wait [COOLDOWN_SECONDSLEFT(bracer, bracer_recharge)] seconds."))
+ return FALSE
+ to_chat(user, SPAN_NOTICE("You rest your bracer against the APC interface and begin to siphon off some of the stored energy."))
+ if(!do_after(grabber, 20, INTERRUPT_ALL, BUSY_ICON_HOSTILE))
+ return FALSE
+
+ if(stat & BROKEN)
+ var/datum/effect_system/spark_spread/spark = new()
+ spark.set_up(3, 1, src)
+ spark.start()
+ to_chat(grabber, SPAN_DANGER("The APC's power currents surge eratically, super-heating your bracer!"))
+ playsound(src.loc, 'sound/effects/sparks2.ogg', 25, 1)
+ grabber.apply_damage(10,0, BURN)
+ return FALSE
+ if(!cell || cell.charge <= 0)
+ to_chat(user, SPAN_WARNING("There is no charge to draw from that APC."))
+ return FALSE
+
+ if(bracer.charge_max <= bracer.charge)
+ to_chat(user, SPAN_WARNING("[bracer.name] is already fully charged."))
+ return FALSE
+
+ var/charge_to_use = min(cell.charge, bracer.charge_max - bracer.charge)
+ if(!(cell.use(charge_to_use)))
+ return FALSE
+ playsound(src.loc, 'sound/effects/sparks2.ogg', 25, 1)
+ bracer.charge += charge_to_use
+ COOLDOWN_START(bracer, bracer_recharge, bracer.charge_cooldown)
+ to_chat(grabber, SPAN_YAUTJABOLD("[icon2html(bracer)] \The [bracer] beep: Power siphon complete. Charge at [bracer.charge]/[bracer.charge_max]."))
+ if(bracer.notification_sound)
+ playsound(bracer.loc, 'sound/items/pred_bracer.ogg', 75, 1)
+ charging = APC_CHARGING
+ set_broken() // Breaks the APC
+
+ return TRUE
+
+ else if(grabber.species.can_shred(grabber))
var/allcut = TRUE
for(var/wire = 1; wire < length(get_wire_descriptions()); wire++)
if(!isWireCut(wire))
@@ -1011,9 +1057,9 @@ GLOBAL_LIST_INIT(apc_wire_descriptions, list(
smoke.set_up(1, 0, loc)
smoke.attach(src)
smoke.start()
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(1, 1, src)
- s.start()
+ var/datum/effect_system/spark_spread/spark = new()
+ spark.set_up(1, 1, src)
+ spark.start()
visible_message(SPAN_WARNING("[src] suddenly lets out a blast of smoke and some sparks!"))
/obj/structure/machinery/power/apc/surplus()
diff --git a/code/modules/projectiles/ammo_datums.dm b/code/modules/projectiles/ammo_datums.dm
index 7114c924fe61..f97195d5ac32 100644
--- a/code/modules/projectiles/ammo_datums.dm
+++ b/code/modules/projectiles/ammo_datums.dm
@@ -2217,12 +2217,27 @@
damage = 40
shell_speed = AMMO_SPEED_TIER_2
-/datum/ammo/energy/yautja/pistol/set_bullet_traits()
+/datum/ammo/energy/yautja/pistol/incendiary
+ damage = 10
+
+/datum/ammo/energy/yautja/pistol/incendiary/set_bullet_traits()
. = ..()
LAZYADD(traits_to_give, list(
BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_incendiary)
))
+/datum/ammo/bullet/shrapnel/plasma
+ name = "plasma wave"
+ shrapnel_chance = 0
+ penetration = ARMOR_PENETRATION_TIER_10
+ accuracy = HIT_ACCURACY_TIER_MAX
+ damage = 15
+ icon_state = "shrapnel_plasma"
+ damage_type = BURN
+
+/datum/ammo/bullet/shrapnel/plasma/on_hit_mob(mob/hit_mob, obj/item/projectile/hit_projectile)
+ hit_mob.apply_effect(2, WEAKEN)
+
/datum/ammo/energy/yautja/caster
name = "root caster bolt"
icon_state = "ion"
diff --git a/code/modules/tgui/tgui_number_input.dm b/code/modules/tgui/tgui_number_input.dm
index 9c447ecd5a03..aa189b1d2039 100644
--- a/code/modules/tgui/tgui_number_input.dm
+++ b/code/modules/tgui/tgui_number_input.dm
@@ -31,7 +31,7 @@
qdel(number_input)
///A clone of tgui_input_number that defaults to accepting negative inputs too.
-/proc/tgui_input_real_number(mob/user, message, title = "Number Input", default = 0, max_value = 16777216, min_value = -16777216, timeout = 0, integer_only = FALSE)
+/proc/tgui_input_real_number(mob/user, message, title = "Number Input", default = 0, max_value = SHORT_REAL_LIMIT, min_value = -SHORT_REAL_LIMIT, timeout = 0, integer_only = FALSE)
return tgui_input_number(user, message, title, default, max_value, min_value, timeout, integer_only)
/**
* Creates an asynchronous TGUI number input window with an associated callback.
diff --git a/colonialmarines.dme b/colonialmarines.dme
index b241992cf172..4a5798995693 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -1855,6 +1855,9 @@
#include "code\modules\mob\living\carbon\xenomorph\abilities\facehugger\facehugger_powers.dm"
#include "code\modules\mob\living\carbon\xenomorph\abilities\hivelord\hivelord_abilities.dm"
#include "code\modules\mob\living\carbon\xenomorph\abilities\hivelord\hivelord_powers.dm"
+#include "code\modules\mob\living\carbon\xenomorph\abilities\lesser_drone\lesser_drone_abilities.dm"
+#include "code\modules\mob\living\carbon\xenomorph\abilities\lesser_drone\lesser_drone_macros.dm"
+#include "code\modules\mob\living\carbon\xenomorph\abilities\lesser_drone\lesser_drone_powers.dm"
#include "code\modules\mob\living\carbon\xenomorph\abilities\lurker\lurker_abilities.dm"
#include "code\modules\mob\living\carbon\xenomorph\abilities\lurker\lurker_macros.dm"
#include "code\modules\mob\living\carbon\xenomorph\abilities\lurker\lurker_powers.dm"
@@ -1891,6 +1894,7 @@
#include "code\modules\mob\living\carbon\xenomorph\castes\Hellhound.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Hivelord.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Larva.dm"
+#include "code\modules\mob\living\carbon\xenomorph\castes\lesser_drone.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Lurker.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Praetorian.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Predalien.dm"
diff --git a/html/changelogs/AutoChangeLog-pr-3455.yml b/html/changelogs/AutoChangeLog-pr-3455.yml
new file mode 100644
index 000000000000..c3189ad233bd
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3455.yml
@@ -0,0 +1,7 @@
+author: "realforest2001"
+delete-after: True
+changes:
+ - rscadd: "Adds a plasma breaching charge that detonates a plasma wave stunning those opposite it."
+ - rscadd: "Adds the name of the tracked item to the Yautja gear tracker."
+ - rscadd: "Added an alternate mode for the Plasma Pistol and moved the incendiary property to it."
+ - rscadd: "Added MINIMAP_FLAG_ALL to Yautja globe map."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3757.yml b/html/changelogs/AutoChangeLog-pr-3757.yml
new file mode 100644
index 000000000000..4cc6cc928423
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3757.yml
@@ -0,0 +1,4 @@
+author: "realforest2001"
+delete-after: True
+changes:
+ - rscadd: "Made Yautja cloak cost no power to operate, and created multipliers for disabled duration based upon what caused the Yautja to decloak."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3764.yml b/html/changelogs/AutoChangeLog-pr-3764.yml
new file mode 100644
index 000000000000..ecb789daaf91
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3764.yml
@@ -0,0 +1,7 @@
+author: "ghostsheet"
+delete-after: True
+changes:
+ - rscadd: "Large General Pouch has stricter restriction against internal boxes."
+ - rscadd: "Large General Pouch no longer restricted to 1 medium item."
+ - rscadd: "Large General Pouch added to REQ."
+ - rscadd: "Shuffled REQ pouch order into Meds, Engi, Misc, Ammo."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3788.yml b/html/changelogs/AutoChangeLog-pr-3788.yml
deleted file mode 100644
index 0efdf59a6d3e..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3788.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "JackieEstegado"
-delete-after: True
-changes:
- - maptweak: "Made LV-624's \"Fully-locked\" Research nightmare insert no longer have indestructible blast doors."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3868.yml b/html/changelogs/AutoChangeLog-pr-3868.yml
new file mode 100644
index 000000000000..4bd11e9a6b0e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3868.yml
@@ -0,0 +1,6 @@
+author: "MarpleJones, ihatethisengine2"
+delete-after: True
+changes:
+ - rscadd: "Added new sprites for the Eggsac Carrier. Includes an additional death sound for the eggsac bursting."
+ - rscadd: "Added a hive announcement for when a Carrier dies with eggs."
+ - bugfix: "Carrier egg drop chance upon death now works as intended."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3939.yml b/html/changelogs/AutoChangeLog-pr-3939.yml
new file mode 100644
index 000000000000..97bac6e085de
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3939.yml
@@ -0,0 +1,4 @@
+author: "Morrow"
+delete-after: True
+changes:
+ - rscadd: "Added lesser drones"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3949.yml b/html/changelogs/AutoChangeLog-pr-3949.yml
new file mode 100644
index 000000000000..eca0cae6dc4a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3949.yml
@@ -0,0 +1,4 @@
+author: "Morrow"
+delete-after: True
+changes:
+ - bugfix: "Stops multiple facehuggers spawning from one client"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3964.yml b/html/changelogs/AutoChangeLog-pr-3964.yml
new file mode 100644
index 000000000000..ccd54d9eda5f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3964.yml
@@ -0,0 +1,4 @@
+author: "Drathek"
+delete-after: True
+changes:
+ - bugfix: "Fix bodybags not accepting warm (recent) dead bodies even if unreviveable"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3975.yml b/html/changelogs/AutoChangeLog-pr-3975.yml
new file mode 100644
index 000000000000..6f871b20bbb6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3975.yml
@@ -0,0 +1,5 @@
+author: "Drathek"
+delete-after: True
+changes:
+ - bugfix: "Fixed ability deactivation and late join current slot toggles not persisting."
+ - bugfix: "Toggle the Ability to Hurt Yourself now says On when you can hurt yourself."
\ No newline at end of file
diff --git a/html/changelogs/archive/2023-07.yml b/html/changelogs/archive/2023-07.yml
index 217a2059e0dd..99120b3a13c4 100644
--- a/html/changelogs/archive/2023-07.yml
+++ b/html/changelogs/archive/2023-07.yml
@@ -403,3 +403,9 @@
system. Their types are also now distinguishable at a glance.
- code_imp: Reworked the way blood bag sprites work behind the scenes to use the
overlay/underlay system.
+2023-07-24:
+ JackieEstegado:
+ - maptweak: Made LV-624's "Fully-locked" Research nightmare insert no longer have
+ indestructible blast doors.
+ Morrow:
+ - bugfix: Fixes wood window numbering
diff --git a/icons/mob/hud/actions.dmi b/icons/mob/hud/actions.dmi
index 4d0697733207..9f885c44f50f 100644
Binary files a/icons/mob/hud/actions.dmi and b/icons/mob/hud/actions.dmi differ
diff --git a/icons/mob/xenonids/lesser_drone.dmi b/icons/mob/xenonids/lesser_drone.dmi
new file mode 100644
index 000000000000..bff44e659162
Binary files /dev/null and b/icons/mob/xenonids/lesser_drone.dmi differ
diff --git a/icons/mob/xenos/carrier.dmi b/icons/mob/xenos/carrier.dmi
index 6832bbba8716..4b69cffd5313 100644
Binary files a/icons/mob/xenos/carrier.dmi and b/icons/mob/xenos/carrier.dmi differ
diff --git a/icons/mob/xenos/lesser_drone.dmi b/icons/mob/xenos/lesser_drone.dmi
new file mode 100644
index 000000000000..134ec0c4ae7c
Binary files /dev/null and b/icons/mob/xenos/lesser_drone.dmi differ
diff --git a/icons/obj/items/assemblies.dmi b/icons/obj/items/assemblies.dmi
index 668d62d23d4e..522e0fb5e55d 100644
Binary files a/icons/obj/items/assemblies.dmi and b/icons/obj/items/assemblies.dmi differ
diff --git a/icons/turf/walls/windows.dmi b/icons/turf/walls/windows.dmi
index 85f822873e68..a3f2fd1d4198 100644
Binary files a/icons/turf/walls/windows.dmi and b/icons/turf/walls/windows.dmi differ
diff --git a/icons/ui_icons/map_blips.dmi b/icons/ui_icons/map_blips.dmi
index 85ef9959027f..829d9b8a43b0 100644
Binary files a/icons/ui_icons/map_blips.dmi and b/icons/ui_icons/map_blips.dmi differ