diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm
index 3fcbfcc8e54..3a7cbc96fe0 100644
--- a/_maps/map_files/Birdshot/birdshot.dmm
+++ b/_maps/map_files/Birdshot/birdshot.dmm
@@ -25274,7 +25274,7 @@
dir = 9
},
/obj/structure/cable,
-/obj/machinery/atmospherics/components/trinary/filter,
+/obj/machinery/atmospherics/components/trinary/filter/critical,
/turf/open/floor/iron/smooth,
/area/station/engineering/supermatter/room)
"jeg" = (
diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm
index 393b1a2a2de..8252794b3db 100644
--- a/_maps/map_files/NorthStar/north_star.dmm
+++ b/_maps/map_files/NorthStar/north_star.dmm
@@ -20971,20 +20971,6 @@
dir = 1
},
/area/station/command/bridge)
-"fBE" = (
-/obj/structure/disposalpipe/segment{
- dir = 1
- },
-/obj/structure/cable,
-/obj/machinery/door/airlock/mining/glass{
- name = "Mail Sorting"
- },
-/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
-/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/obj/machinery/door/firedoor,
-/obj/effect/mapping_helpers/airlock/access/any/supply/general,
-/turf/open/floor/iron/smooth,
-/area/station/cargo/storage)
"fBM" = (
/obj/effect/turf_decal/delivery,
/turf/open/floor/iron/dark,
@@ -32703,6 +32689,13 @@
/obj/machinery/firealarm/directional/east,
/turf/open/floor/catwalk_floor,
/area/station/maintenance/floor3/starboard/fore)
+"iDz" = (
+/obj/machinery/atmospherics/components/trinary/filter/critical{
+ dir = 4
+ },
+/obj/machinery/status_display/evac/directional/north,
+/turf/open/floor/engine,
+/area/station/engineering/supermatter/room)
"iDP" = (
/obj/structure/cable/multilayer/multiz,
/turf/open/floor/plating,
@@ -75518,13 +75511,6 @@
/obj/structure/closet/emcloset/anchored,
/turf/open/floor/pod/dark,
/area/station/maintenance/floor3/port)
-"tLH" = (
-/obj/machinery/atmospherics/components/trinary/filter{
- dir = 4
- },
-/obj/machinery/status_display/evac/directional/north,
-/turf/open/floor/engine,
-/area/station/engineering/supermatter/room)
"tMd" = (
/obj/effect/turf_decal/siding/wood/corner{
dir = 1
@@ -86658,6 +86644,20 @@
/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4,
/turf/open/floor/plating,
/area/station/medical/abandoned)
+"wEN" = (
+/obj/structure/disposalpipe/segment{
+ dir = 1
+ },
+/obj/structure/cable,
+/obj/machinery/door/airlock/mining/glass{
+ name = "Mail Sorting"
+ },
+/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
+/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
+/obj/machinery/door/firedoor,
+/obj/effect/mapping_helpers/airlock/access/any/supply/general,
+/turf/open/floor/iron/smooth,
+/area/station/cargo/storage)
"wEQ" = (
/obj/structure/stairs/east,
/turf/open/floor/pod/dark,
@@ -119139,7 +119139,7 @@ cMA
mnR
acv
hNz
-fBE
+wEN
nlu
aAZ
iRK
@@ -140228,7 +140228,7 @@ uyD
uyD
uyD
uyD
-tLH
+iDz
kfo
mvg
dEc
diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm
index 7e09a7ea178..4b625097d23 100644
--- a/code/__DEFINES/jobs.dm
+++ b/code/__DEFINES/jobs.dm
@@ -236,9 +236,19 @@
#define JOB_CAN_BE_INTERN (1<<8)
/// This job cannot have more slots opened by the Head of Personnel (but admins or other random events can still do this).
#define JOB_CANNOT_OPEN_SLOTS (1<<9)
+/// This job will not display on the job menu when there are no slots available, instead of appearing greyed out
+#define JOB_HIDE_WHEN_EMPTY (1<<10)
+/// This job cannot be signed up for at round start or recorded in your preferences
+#define JOB_LATEJOIN_ONLY (1<<11)
+/// This job is a head of staff.
+#define JOB_HEAD_OF_STAFF (1<<12)
/// Combination flag for jobs which are considered regular crew members of the station.
#define STATION_JOB_FLAGS (JOB_ANNOUNCE_ARRIVAL|JOB_CREW_MANIFEST|JOB_EQUIP_RANK|JOB_CREW_MEMBER|JOB_NEW_PLAYER_JOINABLE|JOB_REOPEN_ON_ROUNDSTART_LOSS|JOB_ASSIGN_QUIRKS|JOB_CAN_BE_INTERN)
+/// Combination flag for jobs which are considered heads of staff.
+#define HEAD_OF_STAFF_JOB_FLAGS (JOB_BOLD_SELECT_TEXT|JOB_CANNOT_OPEN_SLOTS|JOB_HEAD_OF_STAFF)
+/// Combination flag for jobs which are enabled by station traits.
+#define STATION_TRAIT_JOB_FLAGS (JOB_CANNOT_OPEN_SLOTS|JOB_HIDE_WHEN_EMPTY|JOB_LATEJOIN_ONLY&~JOB_REOPEN_ON_ROUNDSTART_LOSS)
#define FACTION_NONE "None"
#define FACTION_STATION "Station"
@@ -251,3 +261,6 @@
#define SUPERVISOR_HOS "the Head of Security"
#define SUPERVISOR_QM "the Quartermaster"
#define SUPERVISOR_RD "the Research Director"
+
+/// Mind traits that should be shared by every head of staff. has to be this way cause byond lists lol
+#define HEAD_OF_STAFF_MIND_TRAITS TRAIT_FAST_TYING, TRAIT_HIGH_VALUE_RANSOM
diff --git a/code/__DEFINES/mecha.dm b/code/__DEFINES/mecha.dm
index 5a37f60aba6..0f900f72ee4 100644
--- a/code/__DEFINES/mecha.dm
+++ b/code/__DEFINES/mecha.dm
@@ -6,7 +6,7 @@
#define PANEL_OPEN (1<<0)
#define ID_LOCK_ON (1<<1)
-#define CANSTRAFE (1<<2)
+#define CAN_STRAFE (1<<2)
#define LIGHTS_ON (1<<3)
#define SILICON_PILOT (1<<4)
#define IS_ENCLOSED (1<<5)
diff --git a/code/__DEFINES/organ_movement.dm b/code/__DEFINES/organ_movement.dm
new file mode 100644
index 00000000000..16f003ede81
--- /dev/null
+++ b/code/__DEFINES/organ_movement.dm
@@ -0,0 +1,4 @@
+/// Delete the organ if replaced
+#define DELETE_IF_REPLACED (1<<0)
+/// When deleting a brain, we don't delete the identity and the player can keep playing
+#define NO_ID_TRANSFER (1<<1)
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 09652b363a9..e0f042ccc45 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -491,6 +491,15 @@
/// Is the mob standing on an elevated surface? This prevents them from dropping down if not elevated first.
#define TRAIT_ON_ELEVATED_SURFACE "on_elevated_surface"
+/// Prevents you from twohanding weapons.
+#define TRAIT_NO_TWOHANDING "no_twohanding"
+
+/// Halves the time of tying a tie.
+#define TRAIT_FAST_TYING "fast_tying"
+
+/// Sells for more money on the pirate bounty pad.
+#define TRAIT_HIGH_VALUE_RANSOM "high_value_ransom"
+
// METABOLISMS
// Various jobs on the station have historically had better reactions
// to various drinks and foodstuffs. Security liking donuts is a classic
diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm
index f0b67a95b92..f9cdbe4326d 100644
--- a/code/__DEFINES/traits/sources.dm
+++ b/code/__DEFINES/traits/sources.dm
@@ -275,5 +275,7 @@
#define ANALYZER_TRAIT "analyzer_trait"
+/// Trait from an organ being inside a bodypart
+#define ORGAN_INSIDE_BODY_TRAIT "organ_inside_body"
/// Trait when something was labelled by a pen.
#define PEN_LABEL_TRAIT "pen_label"
diff --git a/code/__DEFINES/~nova_defines/colony_fabricator_misc.dm b/code/__DEFINES/~nova_defines/colony_fabricator_misc.dm
index 1b358da8b11..0ebd9a14240 100644
--- a/code/__DEFINES/~nova_defines/colony_fabricator_misc.dm
+++ b/code/__DEFINES/~nova_defines/colony_fabricator_misc.dm
@@ -35,4 +35,6 @@ GLOBAL_LIST_INIT(colonist_suit_allowed, list(
/obj/item/resonator,
/obj/item/t_scanner,
/obj/item/analyzer,
+ /obj/item/storage/medkit,
+ /obj/item/fireaxe/metal_h2_axe,
))
diff --git a/code/__DEFINES/~nova_defines/manufacturer_strings.dm b/code/__DEFINES/~nova_defines/manufacturer_strings.dm
index 3e9baa97952..b9cdddc78b0 100644
--- a/code/__DEFINES/~nova_defines/manufacturer_strings.dm
+++ b/code/__DEFINES/~nova_defines/manufacturer_strings.dm
@@ -22,5 +22,6 @@
#define COMPANY_ABDUCTOR "It has [span_abductor("✌︎︎♌︎︎♎︎︎◆︎︎♍︎︎⧫︎︎❄︎♏︎♍︎♒︎")] engraved into it."
#define COMPANY_FRONTIER "It has a small label with [span_engradio("Akhter Company Frontier Equipment")] printed on it, alongside various xerxian proof-marks."
+#define COMPANY_KAHRAMAN "It has a [span_red("red label")] with [span_red("K - A - S")] printed on it, alongside various xerxian proof-marks."
#define COMPANY_REMOVED "It has had [span_grey("all identifying marks scrubbed off")]."
diff --git a/code/__HELPERS/announcements.dm b/code/__HELPERS/announcements.dm
index 5d7d0f65408..7a27be43a75 100644
--- a/code/__HELPERS/announcements.dm
+++ b/code/__HELPERS/announcements.dm
@@ -18,7 +18,6 @@
* * sender_override - optional, modifies the sender of the announcement
* * encode_title - if TRUE, the title will be HTML encoded
* * encode_text - if TRUE, the text will be HTML encoded
- * * color_override - optional, set a color for the announcement box
*/
/proc/send_ooc_announcement(
@@ -43,25 +42,25 @@
if(!length(text))
return
- announcement_strings += span_major_announcement_title(sender_override)
- announcement_strings += span_subheader_announcement_text(title)
- announcement_strings += span_ooc_announcement_text(text)
- var/finalized_announcement = create_ooc_announcement_div(jointext(announcement_strings, ""))
+ announcement_strings += span_major_announcement_title(sender_override)
+ announcement_strings += span_subheader_announcement_text(title)
+ announcement_strings += span_ooc_announcement_text(text)
+ var/finalized_announcement = create_ooc_announcement_div(jointext(announcement_strings, ""))
- if(islist(players))
- for(var/mob/target in players)
- to_chat(target, finalized_announcement)
- if(play_sound && target.client?.prefs.read_preference(/datum/preference/toggle/sound_announcements))
- SEND_SOUND(target, sound(sound_override))
- else
- to_chat(world, finalized_announcement)
+ if(islist(players))
+ for(var/mob/target in players)
+ to_chat(target, finalized_announcement)
+ if(play_sound && target.client?.prefs.read_preference(/datum/preference/toggle/sound_announcements))
+ SEND_SOUND(target, sound(sound_override))
+ else
+ to_chat(world, finalized_announcement)
- if(!play_sound)
- return
+ if(!play_sound)
+ return
- for(var/mob/player in GLOB.player_list)
- if(player.client?.prefs.read_preference(/datum/preference/toggle/sound_announcements))
- SEND_SOUND(player, sound(sound_override))
+ for(var/mob/player in GLOB.player_list)
+ if(player.client?.prefs.read_preference(/datum/preference/toggle/sound_announcements))
+ SEND_SOUND(player, sound(sound_override))
/**
* Inserts a span styled message into an alert box div
@@ -74,5 +73,12 @@
/proc/create_announcement_div(message, color = "default")
return "
[message]
"
+/**
+ * Inserts a span styled message into an OOC alert style div
+ *
+ *
+ * Arguments
+ * * message - required, the message contents
+ */
/proc/create_ooc_announcement_div(message)
return "
[message]
"
diff --git a/code/__HELPERS/areas.dm b/code/__HELPERS/areas.dm
index 7b21ee3f0ef..dec768a6a0c 100644
--- a/code/__HELPERS/areas.dm
+++ b/code/__HELPERS/areas.dm
@@ -1,9 +1,11 @@
#define BP_MAX_ROOM_SIZE 300
-GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(/area/station/engineering/main, \
- /area/station/engineering/supermatter, \
- /area/station/engineering/atmospherics_engine, \
- /area/station/ai_monitored/turret_protected/ai))
+GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(list(
+ /area/station/engineering/main,
+ /area/station/engineering/supermatter,
+ /area/station/engineering/atmospherics_engine,
+ /area/station/ai_monitored/turret_protected/ai,
+)))
// Gets an atmos isolated contained space
// Returns an associative list of turf|dirs pairs
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 99c7dd562ea..0aaca9a0907 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -271,7 +271,7 @@
if(!current_apc.cell || !SSmapping.level_trait(current_apc.z, ZTRAIT_STATION))
continue
var/area/apc_area = current_apc.area
- if(GLOB.typecache_powerfailure_safe_areas[apc_area.type])
+ if(is_type_in_typecache(apc_area, GLOB.typecache_powerfailure_safe_areas))
continue
var/duration = rand(duration_min,duration_max)
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 19a062bb156..c35c591bc7c 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -707,11 +707,6 @@ GLOBAL_LIST_EMPTY(species_list)
if(isliving(occupant))
mob_occupant = occupant
- else if(isbodypart(occupant))
- var/obj/item/bodypart/head/head = occupant
-
- mob_occupant = head.brainmob
-
else if(isorgan(occupant))
var/obj/item/organ/internal/brain/brain = occupant
mob_occupant = brain.brainmob
diff --git a/code/__HELPERS/type_processing.dm b/code/__HELPERS/type_processing.dm
index d2ab6d5e054..117067a0578 100644
--- a/code/__HELPERS/type_processing.dm
+++ b/code/__HELPERS/type_processing.dm
@@ -4,31 +4,84 @@
. = list()
for(var/type in types)
var/typename = "[type]"
+ // Longest paths comes first
var/static/list/TYPES_SHORTCUTS = list(
/obj/effect/decal/cleanable = "CLEANABLE",
/obj/item/bodypart = "BODYPART",
/obj/item/radio/headset = "HEADSET",
- /obj/item/clothing/head/helmet/space = "SPESSHELMET",
+ /obj/item/clothing/accessory = "ACCESSORY",
+ /obj/item/clothing/mask/gas = "GASMASK",
+ /obj/item/clothing/mask = "MASK",
+ /obj/item/clothing/gloves = "GLOVES",
+ /obj/item/clothing/shoes = "SHOES",
+ /obj/item/clothing/under/plasmaman = "PLASMAMAN_SUIT",
+ /obj/item/clothing/under = "JUMPSUIT",
+ /obj/item/clothing/suit/armor = "ARMOR",
+ /obj/item/clothing/suit = "SUIT",
+ /obj/item/clothing/head/helmet = "HELMET",
+ /obj/item/clothing/head = "HEAD",
+ /obj/item/clothing/neck = "NECK",
+ /obj/item/clothing = "CLOTHING",
+ /obj/item/storage/backpack = "BACKPACK",
+ /obj/item/storage/belt = "BELT",
/obj/item/book/manual = "MANUAL",
- /obj/item/reagent_containers/cup/glass = "DRINK", //longest paths comes first
+ /obj/item/storage/pill_bottle = "PILL_BOTTLE",
+ /obj/item/reagent_containers/pill/patch = "MEDPATCH",
+ /obj/item/reagent_containers/pill = "PILL",
+ /obj/item/reagent_containers/hypospray/medipen = "MEDIPEN",
+ /obj/item/reagent_containers/cup/glass = "DRINK",
/obj/item/food = "FOOD",
/obj/item/reagent_containers = "REAGENT_CONTAINERS",
/obj/machinery/atmospherics = "ATMOS_MECH",
/obj/machinery/portable_atmospherics = "PORT_ATMOS",
- /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack = "MECHA_MISSILE_RACK",
+ /obj/item/mecha_parts/mecha_equipment/weapon = "MECHA_WEAPON",
/obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP",
/obj/item/organ = "ORGAN",
+ /obj/item/mod/control = "MODSUIT",
+ /obj/item/mod/module = "MODSUIT_MOD",
+ /obj/item/gun/ballistic/automatic = "GUN_AUTOMATIC",
+ /obj/item/gun/ballistic/revolver = "GUN_REVOLVER",
+ /obj/item/gun/ballistic/rifle = "GUN_RIFLE",
+ /obj/item/gun/ballistic/shotgun = "GUN_SHOTGUN",
+ /obj/item/gun/ballistic = "GUN_BALLISTIC",
+ /obj/item/gun/energy/laser = "GUN_LASER",
+ /obj/item/gun/energy = "GUN_ENERGY",
+ /obj/item/gun/magic = "GUN_MAGIC",
+ /obj/item/gun = "GUN",
+ /obj/item/stack/sheet/mineral = "MINERAL_SHEET",
+ /obj/item/stack/sheet = "SHEET",
+ /obj/item/stack/ore = "ORE",
+ /obj/item/ai_module = "AI_LAW_MODULE",
+ /obj/item/circuitboard/machine = "MACHINE_BOARD",
+ /obj/item/circuitboard/computer = "COMPUTER_BOARD",
+ /obj/item/circuitboard = "CIRCUITBOARD",
/obj/item = "ITEM",
+ /obj/structure/closet/crate/secure = "LOCKED_CRATE",
+ /obj/structure/closet/crate = "CRATE",
+ /obj/structure/closet/secure_closet = "LOCKED_CLOSET",
+ /obj/structure/closet = "CLOSET",
+ /obj/structure = "STRUCTURE",
+ /obj/machinery/door/airlock = "AIRLOCK",
+ /obj/machinery/door = "DOOR",
+ /obj/machinery/rnd/production = "RND_FABRICATOR",
+ /obj/machinery/computer/camera_advanced/shuttle_docker = "DOCKING_COMPUTER",
+ /obj/machinery/computer = "COMPUTER",
+ /obj/machinery/vending/wardrobe = "JOBDROBE",
+ /obj/machinery/vending = "VENDING",
/obj/machinery = "MACHINERY",
/obj/effect = "EFFECT",
+ /obj/projectile = "PROJECTILE",
/obj = "O",
/datum = "D",
/turf/open = "OPEN",
/turf/closed = "CLOSED",
/turf = "T",
+ /mob/living/carbon/human = "HUMANOID",
/mob/living/carbon = "CARBON",
/mob/living/simple_animal = "SIMPLE",
/mob/living/basic = "BASIC",
+ /mob/living/silicon/robot = "CYBORG",
+ /mob/living/silicon = "SILICON",
/mob/living = "LIVING",
/mob = "M",
)
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index d2ff2e7b542..276d4d7dce2 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -235,7 +235,7 @@ DEFINE_BITFIELD(internal_damage, list(
DEFINE_BITFIELD(mecha_flags, list(
"ID_LOCK_ON" = ID_LOCK_ON,
- "CANSTRAFE" = CANSTRAFE,
+ "CAN_STRAFE" = CAN_STRAFE,
"LIGHTS_ON" = LIGHTS_ON,
"SILICON_PILOT" = SILICON_PILOT,
"IS_ENCLOSED" = IS_ENCLOSED,
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index febe38f7022..7075bf9912f 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -188,6 +188,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_EXPANDED_FOV" = TRAIT_EXPANDED_FOV,
"TRAIT_EXTROVERT" = TRAIT_EXTROVERT,
"TRAIT_FAKEDEATH" = TRAIT_FAKEDEATH,
+ "TRAIT_FAST_TYING" = TRAIT_FAST_TYING,
"TRAIT_FASTMED" = TRAIT_FASTMED,
"TRAIT_FAT" = TRAIT_FAT,
"TRAIT_FEARLESS" = TRAIT_FEARLESS,
@@ -231,6 +232,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_HEAVY_DRINKER" = TRAIT_HEAVY_DRINKER,
"TRAIT_HEAVY_SLEEPER" = TRAIT_HEAVY_SLEEPER,
"TRAIT_HIDE_EXTERNAL_ORGANS" = TRAIT_HIDE_EXTERNAL_ORGANS,
+ "TRAIT_HIGH_VALUE_RANSOM" = TRAIT_HIGH_VALUE_RANSOM,
"TRAIT_HOLY" = TRAIT_HOLY,
"TRAIT_HOPELESSLY_ADDICTED" = TRAIT_HOPELESSLY_ADDICTED,
"TRAIT_HOT_SPRING_CURSED" = TRAIT_HOT_SPRING_CURSED,
@@ -307,6 +309,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NO_SOUL" = TRAIT_NO_SOUL,
"TRAIT_NO_STRIP" = TRAIT_NO_STRIP,
"TRAIT_NO_TRANSFORM" = TRAIT_NO_TRANSFORM,
+ "TRAIT_NO_TWOHANDING" = TRAIT_NO_TWOHANDING,
"TRAIT_NOCRITDAMAGE" = TRAIT_NOCRITDAMAGE,
"TRAIT_NO_UNDERWEAR" = TRAIT_NO_UNDERWEAR,
"TRAIT_NO_ZOMBIFY" = TRAIT_NO_ZOMBIFY,
diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets_latejoin.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_latejoin.dm
index 24347783847..5024737fa3e 100644
--- a/code/controllers/subsystem/dynamic/dynamic_rulesets_latejoin.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_latejoin.dm
@@ -136,7 +136,7 @@
return FALSE
var/head_check = 0
for(var/mob/player in GLOB.alive_player_list)
- if (player.mind.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if (player.mind.assigned_role.job_flags & JOB_HEAD_OF_STAFF)
head_check++
return (head_check >= required_heads_of_staff)
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 70c6e2a6523..38ef544e04c 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -743,7 +743,7 @@ SUBSYSTEM_DEF(job)
/datum/controller/subsystem/job/proc/get_living_heads()
. = list()
for(var/datum/mind/head as anything in get_crewmember_minds())
- if(!(head.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if(!(head.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
if(isnull(head.current) || head.current.stat == DEAD)
continue
@@ -753,7 +753,7 @@ SUBSYSTEM_DEF(job)
/datum/controller/subsystem/job/proc/get_all_heads()
. = list()
for(var/datum/mind/head as anything in get_crewmember_minds())
- if(head.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(head.assigned_role.job_flags & JOB_HEAD_OF_STAFF)
. += head
/// Returns a list of minds of all security members who are alive
diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm
index a13cc8b36fa..b525f66576c 100644
--- a/code/datums/ai/_ai_controller.dm
+++ b/code/datums/ai/_ai_controller.dm
@@ -435,6 +435,25 @@ multiple modular subtrees with behaviors
blackboard[key] = thing
post_blackboard_key_set(key)
+/**
+ * Helper to force a key to be a certain thing no matter what's already there
+ *
+ * Useful for if you're overriding a list with a new list entirely,
+ * as otherwise it would throw a runtime error from trying to override a list
+ *
+ * Not necessary to use if you aren't dealing with lists, as set_blackboard_key will clear the existing value
+ * in that case already, but may be useful for clarity.
+ *
+ * * key - A blackboard key
+ * * thing - a value to set the blackboard key to.
+ */
+/datum/ai_controller/proc/override_blackboard_key(key, thing)
+ if(blackboard[key] == thing)
+ return
+
+ clear_blackboard_key(key)
+ set_blackboard_key(key, thing)
+
/**
* Sets the key at index thing to the passed value
*
diff --git a/code/datums/ai/generic/generic_behaviors.dm b/code/datums/ai/generic/generic_behaviors.dm
index b769bc529e2..43e37f66e8c 100644
--- a/code/datums/ai/generic/generic_behaviors.dm
+++ b/code/datums/ai/generic/generic_behaviors.dm
@@ -360,6 +360,10 @@
continue
if(thing.IsObscured())
continue
+ if(isitem(thing))
+ var/obj/item/item = thing
+ if(item.item_flags & ABSTRACT)
+ continue
possible_targets += thing
if(!possible_targets.len)
finish_action(controller, FALSE)
diff --git a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm
index f2b82e7a2c8..d64a4e3b57f 100644
--- a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm
+++ b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm
@@ -93,26 +93,26 @@
CRASH("External organ has no feature list, it will render invisible")
///Give the organ its color. Force will override the existing one.
-/datum/bodypart_overlay/mutant/proc/inherit_color(obj/item/bodypart/ownerlimb, force)
- if(isnull(ownerlimb))
+/datum/bodypart_overlay/mutant/proc/inherit_color(obj/item/bodypart/bodypart_owner, force)
+ if(isnull(bodypart_owner))
draw_color = null
- alpha = 255 // NOVA EDIT - Mutant bodyparts transparency are based on limb transparency
+ alpha = 255 // NOVA EDIT ADDITION - Mutant bodyparts transparency are based on limb transparency
return TRUE
if(draw_color && !force)
return FALSE
- alpha = ownerlimb.alpha // NOVA EDIT - Mutant bodyparts transparency are based on limb transparency
+ alpha = bodypart_owner.alpha // NOVA EDIT ADDITION - Mutant bodyparts transparency are based on limb transparency
switch(color_source)
if(ORGAN_COLOR_OVERRIDE)
- draw_color = override_color(ownerlimb.draw_color)
+ draw_color = override_color(bodypart_owner.draw_color)
if(ORGAN_COLOR_INHERIT)
- draw_color = ownerlimb.draw_color
+ draw_color = bodypart_owner.draw_color
if(ORGAN_COLOR_HAIR)
- if(!ishuman(ownerlimb.owner))
+ if(!ishuman(bodypart_owner.owner))
return
- var/mob/living/carbon/human/human_owner = ownerlimb.owner
- var/obj/item/bodypart/head/my_head = human_owner.get_bodypart(BODY_ZONE_HEAD) //not always the same as ownerlimb
+ var/mob/living/carbon/human/human_owner = bodypart_owner.owner
+ var/obj/item/bodypart/head/my_head = human_owner.get_bodypart(BODY_ZONE_HEAD) //not always the same as bodypart_owner
//head hair color takes priority, owner hair color is a backup if we lack a head or something
if(my_head)
draw_color = my_head.hair_color
diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm
index e16ab582b77..832ad22698b 100644
--- a/code/datums/components/crafting/crafting.dm
+++ b/code/datums/components/crafting/crafting.dm
@@ -106,6 +106,10 @@
for(var/atom/movable/AM in range(radius_range, a))
if((AM.flags_1 & HOLOGRAM_1) || (blacklist && (AM.type in blacklist)))
continue
+ if(isitem(AM))
+ var/obj/item/item = AM
+ if(item.item_flags & ABSTRACT) //let's not tempt fate, shall we?
+ continue
. += AM
/datum/component/personal_crafting/proc/get_surroundings(atom/a, list/blacklist=null)
diff --git a/code/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm
index f3006d3111d..b2ba8674d79 100644
--- a/code/datums/components/listen_and_repeat.dm
+++ b/code/datums/components/listen_and_repeat.dm
@@ -82,7 +82,7 @@
if(!LAZYLEN(speech_buffer)) // what? well whatever let's just move on
return
- controller.set_blackboard_key(BB_EXPORTABLE_STRING_BUFFER_LIST, speech_buffer.Copy())
+ controller.override_blackboard_key(BB_EXPORTABLE_STRING_BUFFER_LIST, speech_buffer.Copy())
#undef MAX_SPEECH_BUFFER_SIZE
#undef RADIO_IGNORE_CHANCE
diff --git a/code/datums/diseases/gastrolisis.dm b/code/datums/diseases/gastrolisis.dm
index c3a24903b81..aab0fcded1d 100644
--- a/code/datums/diseases/gastrolisis.dm
+++ b/code/datums/diseases/gastrolisis.dm
@@ -40,7 +40,7 @@
var/obj/item/organ/internal/eyes/eyes = locate(/obj/item/organ/internal/eyes/snail) in affected_mob.organs
if(!eyes && SPT_PROB(2.5, seconds_per_tick))
var/obj/item/organ/internal/eyes/snail/new_eyes = new()
- new_eyes.Insert(affected_mob, drop_if_replaced = TRUE)
+ new_eyes.Insert(affected_mob)
affected_mob.visible_message(span_warning("[affected_mob]'s eyes fall out, with snail eyes taking its place!"), \
span_userdanger("You scream in pain as your eyes are pushed out by your new snail eyes!"))
affected_mob.emote("scream")
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 0eb811cc8bd..120c69bca1d 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -604,7 +604,6 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
update_body(is_creating = TRUE)
update_mutations_overlay()// no lizard with human hulk overlay please.
-
/mob/proc/has_dna()
return
diff --git a/code/datums/elements/haunted.dm b/code/datums/elements/haunted.dm
index d678083dd19..04f985c0d89 100644
--- a/code/datums/elements/haunted.dm
+++ b/code/datums/elements/haunted.dm
@@ -23,7 +23,6 @@
QDEL_NULL(master.ai_controller)
REMOVE_TRAIT(master, TRAIT_MOVE_FLYING, ELEMENT_TRAIT(type))
master.RemoveElement(/datum/element/movetype_handler)
- return ..()
/atom/movable/proc/make_haunted(source, color) //if not haunted, make haunted
if(!HAS_TRAIT(src, TRAIT_HAUNTED))
diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm
index 92859902137..5ba64d31168 100644
--- a/code/datums/mutations/body.dm
+++ b/code/datums/mutations/body.dm
@@ -524,14 +524,16 @@
var/obj/item/organ/internal/brain/brain = owner.get_organ_slot(ORGAN_SLOT_BRAIN)
if(brain)
+ brain.Remove(owner, special = TRUE)
brain.zone = BODY_ZONE_CHEST
+ brain.Insert(owner, special = TRUE)
var/obj/item/bodypart/head/head = owner.get_bodypart(BODY_ZONE_HEAD)
if(head)
owner.visible_message(span_warning("[owner]'s head splatters with a sickening crunch!"), ignored_mobs = list(owner))
new /obj/effect/gibspawner/generic(get_turf(owner), owner)
- head.dismember(dam_type = BRUTE, silent = TRUE)
head.drop_organs()
+ head.dismember(dam_type = BRUTE, silent = TRUE)
qdel(head)
RegisterSignal(owner, COMSIG_ATTEMPT_CARBON_ATTACH_LIMB, PROC_REF(abort_attachment))
@@ -539,14 +541,18 @@
. = ..()
if(.)
return TRUE
- var/obj/item/organ/internal/brain/brain = owner.get_organ_slot(ORGAN_SLOT_BRAIN)
- if(brain) //so this doesn't instantly kill you. we could delete the brain, but it lets people cure brain issues they /really/ shouldn't be
- brain.zone = initial(brain.zone)
+
UnregisterSignal(owner, COMSIG_ATTEMPT_CARBON_ATTACH_LIMB)
var/successful = owner.regenerate_limb(BODY_ZONE_HEAD)
if(!successful)
stack_trace("HARS mutation head regeneration failed! (usually caused by headless syndrome having a head)")
return TRUE
+ var/obj/item/organ/internal/brain/brain = owner.get_organ_slot(ORGAN_SLOT_BRAIN)
+ if(brain)
+ brain.Remove(owner, special = TRUE)
+ brain.zone = initial(brain.zone)
+ brain.Insert(owner, special = TRUE)
+
owner.dna.species.regenerate_organs(owner, replace_current = FALSE, excluded_zones = list(BODY_ZONE_CHEST)) //replace_current needs to be FALSE to prevent weird adding and removing mutation healing
owner.apply_damage(damage = 50, damagetype = BRUTE, def_zone = BODY_ZONE_HEAD) //and this to DISCOURAGE organ farming, or at least not make it free.
owner.visible_message(span_warning("[owner]'s head returns with a sickening crunch!"), span_warning("Your head regrows with a sickening crack! Ouch."))
diff --git a/code/datums/quirks/negative_quirks/junkie.dm b/code/datums/quirks/negative_quirks/junkie.dm
index 269f6d2d96e..2ce29f743c3 100644
--- a/code/datums/quirks/negative_quirks/junkie.dm
+++ b/code/datums/quirks/negative_quirks/junkie.dm
@@ -134,7 +134,7 @@
smoker_lungs = /obj/item/organ/internal/lungs/smoker_lungs
if(!isnull(smoker_lungs))
smoker_lungs = new smoker_lungs
- smoker_lungs.Insert(carbon_holder, special = TRUE, drop_if_replaced = FALSE)
+ smoker_lungs.Insert(carbon_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/quirk/item_quirk/junkie/smoker/process(seconds_per_tick)
. = ..()
diff --git a/code/datums/quirks/negative_quirks/prosthetic_organ.dm b/code/datums/quirks/negative_quirks/prosthetic_organ.dm
index 6330035b5a7..0fd5061a787 100644
--- a/code/datums/quirks/negative_quirks/prosthetic_organ.dm
+++ b/code/datums/quirks/negative_quirks/prosthetic_organ.dm
@@ -49,7 +49,7 @@
medical_record_text = "During physical examination, patient was found to have a low-budget prosthetic [slot_string]. \
Removal of these organs is known to be dangerous to the patient as well as the practitioner."
old_organ = human_holder.get_organ_slot(organ_slot)
- if(prosthetic.Insert(human_holder, special = TRUE, drop_if_replaced = TRUE))
+ if(prosthetic.Insert(human_holder, special = TRUE))
old_organ.moveToNullspace()
STOP_PROCESSING(SSobj, old_organ)
diff --git a/code/datums/quirks/negative_quirks/tin_man.dm b/code/datums/quirks/negative_quirks/tin_man.dm
index a1e88288877..e6d411bc772 100644
--- a/code/datums/quirks/negative_quirks/tin_man.dm
+++ b/code/datums/quirks/negative_quirks/tin_man.dm
@@ -30,7 +30,7 @@
for(var/organ_slot in possible_organ_slots)
var/organ_path = possible_organ_slots[organ_slot]
var/obj/item/organ/new_organ = new organ_path()
- new_organ.Insert(human_holder, special = TRUE, drop_if_replaced = FALSE)
+ new_organ.Insert(human_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/quirk/tin_man/post_add()
to_chat(quirk_holder, span_boldannounce("Most of your internal organs have been replaced with surplus prosthetics. They are fragile and will easily come apart under duress. \
diff --git a/code/datums/station_traits/positive_traits.dm b/code/datums/station_traits/positive_traits.dm
index 7b3ed0a0b15..c652c55400a 100644
--- a/code/datums/station_traits/positive_traits.dm
+++ b/code/datums/station_traits/positive_traits.dm
@@ -272,7 +272,7 @@
ai.eyeobj.relay_speech = TRUE //surveillance upgrade. the ai gets cybernetics too.
return
var/obj/item/organ/internal/cybernetic = new cybernetic_type()
- cybernetic.Insert(spawned, special = TRUE, drop_if_replaced = FALSE)
+ cybernetic.Insert(spawned, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/station_trait/luxury_escape_pods
name = "Luxury Escape Pods"
diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm
index 50c93e7db8f..84f30ab7855 100644
--- a/code/datums/status_effects/neutral.dm
+++ b/code/datums/status_effects/neutral.dm
@@ -522,7 +522,7 @@
return
if(prob(1))//low chance of the alternative reality returning to monkey
var/obj/item/organ/external/tail/monkey/monkey_tail = new ()
- monkey_tail.Insert(human_mob, drop_if_replaced = FALSE)
+ monkey_tail.Insert(human_mob, movement_flags = DELETE_IF_REPLACED)
var/datum/species/human_species = human_mob.dna?.species
if(human_species)
human_species.randomize_active_features(human_mob)
diff --git a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm
index cbe6e1c31c7..afbb8404060 100644
--- a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm
@@ -48,7 +48,7 @@
AddElement(/datum/element/noticable_organ, "teeth are big and sharp.", BODY_ZONE_PRECISE_MOUTH)
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/carp)
-/obj/item/organ/internal/tongue/carp/on_insert(mob/living/carbon/tongue_owner)
+/obj/item/organ/internal/tongue/carp/on_mob_insert(mob/living/carbon/tongue_owner, special, movement_flags)
. = ..()
if(!ishuman(tongue_owner))
return
@@ -57,12 +57,14 @@
return
var/datum/species/rec_species = human_receiver.dna.species
rec_species.update_no_equip_flags(tongue_owner, rec_species.no_equip_flags | ITEM_SLOT_MASK)
- var/obj/item/bodypart/head/head = human_receiver.get_bodypart(BODY_ZONE_HEAD)
- head.unarmed_damage_low = 10
- head.unarmed_damage_high = 15
- head.unarmed_effectiveness = 15
-/obj/item/organ/internal/tongue/carp/on_remove(mob/living/carbon/tongue_owner)
+/obj/item/organ/internal/tongue/carp/on_bodypart_insert(obj/item/bodypart/limb)
+ . = ..()
+ limb.unarmed_damage_low = 10
+ limb.unarmed_damage_high = 15
+ limb.unarmed_effectiveness = 15
+
+/obj/item/organ/internal/tongue/carp/on_mob_remove(mob/living/carbon/tongue_owner)
. = ..()
if(!ishuman(tongue_owner))
return
@@ -71,7 +73,10 @@
return
var/datum/species/rec_species = human_receiver.dna.species
rec_species.update_no_equip_flags(tongue_owner, initial(rec_species.no_equip_flags))
- var/obj/item/bodypart/head/head = human_receiver.get_bodypart(BODY_ZONE_HEAD)
+
+/obj/item/organ/internal/tongue/carp/on_bodypart_remove(obj/item/bodypart/head)
+ . = ..()
+
head.unarmed_damage_low = initial(head.unarmed_damage_low)
head.unarmed_damage_high = initial(head.unarmed_damage_high)
head.unarmed_effectiveness = initial(head.unarmed_effectiveness)
@@ -110,13 +115,13 @@
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/carp)
AddElement(/datum/element/noticable_organ, "seem%PRONOUN_S unable to stay still.")
-/obj/item/organ/internal/brain/carp/on_insert(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/carp/on_mob_insert(mob/living/carbon/brain_owner)
. = ..()
cooldown_timer = addtimer(CALLBACK(src, PROC_REF(unsatisfied_nomad)), cooldown_time, TIMER_STOPPABLE|TIMER_OVERRIDE|TIMER_UNIQUE)
RegisterSignal(brain_owner, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(satisfied_nomad))
//technically you could get around the mood issue by extracting and reimplanting the brain but it will be far easier to just go one z there and back
-/obj/item/organ/internal/brain/carp/on_remove(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/carp/on_mob_remove(mob/living/carbon/brain_owner)
. = ..()
UnregisterSignal(brain_owner, COMSIG_MOVABLE_Z_CHANGED)
deltimer(cooldown_timer)
diff --git a/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm
index cb5347c8a51..58494346f62 100644
--- a/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm
@@ -8,7 +8,7 @@
//NOVA EDIT REMOVAL BEGIN - CUSTOMIZATION
/*
-/obj/item/organ/internal/ears/fox/on_insert(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/fox/on_mob_insert(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner) && ear_owner.dna)
color = ear_owner.hair_color
@@ -16,7 +16,7 @@
ear_owner.dna.update_uf_block(DNA_EARS_BLOCK)
ear_owner.update_body()
-/obj/item/organ/internal/ears/fox/on_remove(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/fox/on_mob_remove(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner) && ear_owner.dna)
color = ear_owner.hair_color
diff --git a/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm b/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm
index 6cd753eb760..477b461bdf3 100644
--- a/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm
@@ -66,7 +66,7 @@
AddElement(/datum/element/noticable_organ, "arm is just a mass of plate and tendrils.", BODY_ZONE_CHEST)
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/goliath)
-/obj/item/organ/internal/brain/goliath/on_insert(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/goliath/on_mob_insert(mob/living/carbon/brain_owner)
. = ..()
if(!ishuman(brain_owner))
return
@@ -78,7 +78,7 @@
hammer = new/obj/item/goliath_infuser_hammer
brain_owner.put_in_hands(hammer)
-/obj/item/organ/internal/brain/goliath/on_remove(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/goliath/on_mob_remove(mob/living/carbon/brain_owner)
. = ..()
UnregisterSignal(brain_owner)
if(!ishuman(brain_owner))
diff --git a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm
index 515c56622c4..2a5c776709c 100644
--- a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm
@@ -33,7 +33,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/gondola)
AddElement(/datum/element/noticable_organ, "radiate%PRONOUN_S an aura of serenity.")
-/obj/item/organ/internal/heart/gondola/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/internal/heart/gondola/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(!(FACTION_HOSTILE in receiver.faction))
factions_to_remove += FACTION_HOSTILE
@@ -41,7 +41,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
factions_to_remove += FACTION_MINING
receiver.faction |= list(FACTION_HOSTILE, FACTION_MINING)
-/obj/item/organ/internal/heart/gondola/Remove(mob/living/carbon/heartless, special)
+/obj/item/organ/internal/heart/gondola/Remove(mob/living/carbon/heartless, special, movement_flags)
. = ..()
for(var/faction in factions_to_remove)
heartless.faction -= faction
@@ -63,11 +63,11 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
AddElement(/datum/element/noticable_organ, "mouth is permanently affixed into a relaxed smile.", BODY_ZONE_PRECISE_MOUTH)
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/gondola)
-/obj/item/organ/internal/tongue/gondola/Insert(mob/living/carbon/tongue_owner, special, drop_if_replaced)
+/obj/item/organ/internal/tongue/gondola/Insert(mob/living/carbon/tongue_owner, special, movement_flags)
. = ..()
tongue_owner.add_mood_event("gondola_zen", /datum/mood_event/gondola_serenity)
-/obj/item/organ/internal/tongue/gondola/Remove(mob/living/carbon/tongue_owner, special)
+/obj/item/organ/internal/tongue/gondola/Remove(mob/living/carbon/tongue_owner, special, movement_flags)
tongue_owner.clear_mood_event("gondola_zen")
return ..()
@@ -89,7 +89,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
AddElement(/datum/element/noticable_organ, "right arm has small needles breaching the skin all over it.", BODY_ZONE_R_ARM)
pax_hugs = new
-/obj/item/organ/internal/liver/gondola/Insert(mob/living/carbon/liver_owner, special, drop_if_replaced)
+/obj/item/organ/internal/liver/gondola/Insert(mob/living/carbon/liver_owner, special, movement_flags)
. = ..()
var/has_left = liver_owner.has_left_hand(check_disabled = FALSE)
var/has_right = liver_owner.has_right_hand(check_disabled = FALSE)
@@ -104,7 +104,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
RegisterSignal(liver_owner, COMSIG_HUMAN_EQUIPPING_ITEM, PROC_REF(on_owner_equipping_item))
RegisterSignal(liver_owner, COMSIG_LIVING_TRY_PULL, PROC_REF(on_owner_try_pull))
-/obj/item/organ/internal/liver/gondola/Remove(mob/living/carbon/liver_owner, special)
+/obj/item/organ/internal/liver/gondola/Remove(mob/living/carbon/liver_owner, special, movement_flags)
. = ..()
pax_hugs.remove(liver_owner)
UnregisterSignal(liver_owner, list(COMSIG_HUMAN_EQUIPPING_ITEM, COMSIG_LIVING_TRY_PULL))
diff --git a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
index 96c33871a40..092a4c7889a 100644
--- a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
@@ -63,7 +63,7 @@
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/rat)
AddElement(/datum/element/noticable_organ, "hunch%PRONOUN_ES over unnaturally!")
-/obj/item/organ/internal/heart/rat/on_insert(mob/living/carbon/receiver)
+/obj/item/organ/internal/heart/rat/on_mob_insert(mob/living/carbon/receiver)
. = ..()
if(!. || !ishuman(receiver))
return
@@ -75,7 +75,7 @@
if(human_receiver.physiology)
human_receiver.physiology.damage_resistance -= 50
-/obj/item/organ/internal/heart/rat/on_remove(mob/living/carbon/heartless, special)
+/obj/item/organ/internal/heart/rat/on_mob_remove(mob/living/carbon/heartless, special)
. = ..()
if(!ishuman(heartless))
return
@@ -113,11 +113,11 @@
if(message == "hi?")
speech_args[SPEECH_MESSAGE] = "Um... cheesed to meet you?"
-/obj/item/organ/internal/tongue/rat/on_insert(mob/living/carbon/tongue_owner, special, drop_if_replaced)
+/obj/item/organ/internal/tongue/rat/on_mob_insert(mob/living/carbon/tongue_owner, special, movement_flags)
. = ..()
RegisterSignal(tongue_owner, COMSIG_CARBON_ITEM_GIVEN, PROC_REF(its_on_the_mouse))
-/obj/item/organ/internal/tongue/rat/on_remove(mob/living/carbon/tongue_owner)
+/obj/item/organ/internal/tongue/rat/on_mob_remove(mob/living/carbon/tongue_owner)
. = ..()
UnregisterSignal(tongue_owner, COMSIG_CARBON_ITEM_GIVEN)
diff --git a/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm b/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm
index f10c9b039f2..0644bca0354 100644
--- a/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm
@@ -71,7 +71,7 @@
QDEL_NULL(roach_shell)
return ..()
-/obj/item/organ/internal/heart/roach/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/heart/roach/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner))
return
@@ -82,11 +82,11 @@
RegisterSignal(human_owner, COMSIG_MOB_AFTER_APPLY_DAMAGE, PROC_REF(do_block_effect))
human_owner.physiology.knockdown_mod *= 3
- var/obj/item/bodypart/chest/chest = human_owner.get_bodypart(BODY_ZONE_CHEST)
- chest.add_bodypart_overlay(roach_shell)
- human_owner.update_body_parts()
+/obj/item/organ/internal/heart/roach/on_bodypart_insert(obj/item/bodypart/limb)
+ . = ..()
+ limb.add_bodypart_overlay(roach_shell)
-/obj/item/organ/internal/heart/roach/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/heart/roach/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner) || QDELETED(organ_owner))
return
@@ -96,9 +96,10 @@
UnregisterSignal(human_owner, list(COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, COMSIG_MOB_AFTER_APPLY_DAMAGE))
human_owner.physiology.knockdown_mod /= 3
- var/obj/item/bodypart/chest/chest = human_owner.get_bodypart(BODY_ZONE_CHEST)
- chest.remove_bodypart_overlay(roach_shell)
- human_owner.update_body_parts()
+/obj/item/organ/internal/heart/roach/on_bodypart_remove(obj/item/bodypart/limb)
+ . = ..()
+
+ limb.remove_bodypart_overlay(roach_shell)
/**
* Signal proc for [COMSIG_MOB_APPLY_DAMAGE_MODIFIERS]
@@ -193,7 +194,7 @@
. = ..()
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/roach)
-/obj/item/organ/internal/liver/roach/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/liver/roach/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner))
return
@@ -201,13 +202,13 @@
var/mob/living/carbon/human/human_owner = owner
human_owner.physiology.tox_mod *= 2
-/obj/item/organ/internal/liver/roach/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/liver/roach/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner) || QDELETED(organ_owner))
return
var/mob/living/carbon/human/human_owner = organ_owner
- human_owner.physiology.tox_mod /= 2
+ human_owner.physiology.tox_mod *= 0.5
/// Roach appendix:
/// No appendicitus! weee!
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index 6d272de65b3..76b1b73ba53 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -558,7 +558,7 @@ DEFINE_BITFIELD(turret_flags, list(
// If we aren't shooting heads then return a threatcount of 0
if (!(turret_flags & TURRET_FLAG_SHOOT_HEADS))
var/datum/job/apparent_job = SSjob.GetJob(perp.get_assignment())
- if(apparent_job?.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(apparent_job?.job_flags & JOB_HEAD_OF_STAFF)
return 0
if(turret_flags & TURRET_FLAG_AUTH_WEAPONS) //check for weapon authorization
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index f2f5b1b0fec..a4deb63ae21 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -157,6 +157,10 @@
var/obj/item/mmi/as_mmi = thing
if(istype(thing, /obj/item/organ/internal/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || istype(thing, /obj/item/dullahan_relay))
living_detected = TRUE
+ if(isitem(as_object))
+ var/obj/item/as_item = as_object
+ if(as_item.item_flags & ABSTRACT) //also catches organs and bodyparts *stares*
+ continue
nom += thing
else if(isliving(thing))
living_detected = TRUE
diff --git a/code/game/objects/items/body_egg.dm b/code/game/objects/items/body_egg.dm
index f95b9f2e164..d244d8c55cc 100644
--- a/code/game/objects/items/body_egg.dm
+++ b/code/game/objects/items/body_egg.dm
@@ -15,7 +15,7 @@
if(iscarbon(loc))
Insert(loc)
-/obj/item/organ/internal/body_egg/Insert(mob/living/carbon/egg_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/body_egg/Insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
if(!.)
return
@@ -23,7 +23,7 @@
egg_owner.med_hud_set_status()
INVOKE_ASYNC(src, PROC_REF(AddInfectionImages), egg_owner)
-/obj/item/organ/internal/body_egg/Remove(mob/living/carbon/egg_owner, special = FALSE)
+/obj/item/organ/internal/body_egg/Remove(mob/living/carbon/egg_owner, special, movement_flags)
. = ..()
egg_owner.remove_traits(list(TRAIT_XENO_HOST, TRAIT_XENO_IMMUNE), ORGAN_TRAIT)
egg_owner.med_hud_set_status()
diff --git a/code/game/objects/items/food/mexican.dm b/code/game/objects/items/food/mexican.dm
index b4e32587bc6..396e351ff4b 100644
--- a/code/game/objects/items/food/mexican.dm
+++ b/code/game/objects/items/food/mexican.dm
@@ -305,6 +305,7 @@
/datum/reagent/consumable/nutriment/vitamin = 6,
/datum/reagent/consumable/nutriment/protein = 4,
)
+ trash_type = /obj/item/reagent_containers/cup/bowl
tastes = list("mashed beans" = 1, "onion" = 3,)
foodtypes = VEGETABLES | FRIED
w_class = WEIGHT_CLASS_SMALL
@@ -319,6 +320,7 @@
/datum/reagent/consumable/nutriment = 6,
/datum/reagent/consumable/nutriment/vitamin = 6,
)
+ trash_type = /obj/item/reagent_containers/cup/bowl
tastes = list("zesty rice" = 1, "tomato sauce" = 3,)
foodtypes = VEGETABLES
w_class = WEIGHT_CLASS_SMALL
diff --git a/code/game/objects/items/robot/items/storage.dm b/code/game/objects/items/robot/items/storage.dm
index 4995c7d9df6..c12f816fa15 100644
--- a/code/game/objects/items/robot/items/storage.dm
+++ b/code/game/objects/items/robot/items/storage.dm
@@ -58,22 +58,22 @@
/obj/item/borg/apparatus/pre_attack(atom/atom, mob/living/user, params)
if(!stored)
// Borgs should not be grabbing their own modules
- if(!istype(atom.loc, /mob/living/silicon/robot))
- var/itemcheck = FALSE
- for(var/storable_type in storable)
- if(istype(atom, storable_type))
- itemcheck = TRUE
- break
- if(itemcheck)
- var/obj/item/item = atom
- item.forceMove(src)
- stored = item
- RegisterSignal(stored, COMSIG_ATOM_UPDATED_ICON, PROC_REF(on_stored_updated_icon))
- update_appearance()
- return TRUE
- else
- stored.melee_attack_chain(user, atom, params)
- return TRUE
+ if(istype(atom.loc, /mob/living/silicon/robot) || istype(atom.loc, /obj/item/robot_model) || HAS_TRAIT(atom, TRAIT_NODROP))
+ stored.melee_attack_chain(user, atom, params)
+ return TRUE
+
+ var/itemcheck = FALSE
+ for(var/storable_type in storable)
+ if(istype(atom, storable_type))
+ itemcheck = TRUE
+ break
+ if(itemcheck)
+ var/obj/item/item = atom
+ item.forceMove(src)
+ stored = item
+ RegisterSignal(stored, COMSIG_ATOM_UPDATED_ICON, PROC_REF(on_stored_updated_icon))
+ update_appearance()
+ return TRUE
return ..()
/**
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index 9ec5f22746d..4ccc93ff213 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -75,7 +75,11 @@
return patient.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE)
/// In which we print the message that we're starting to heal someone, then we try healing them. Does the do_after whether or not it can actually succeed on a targeted mob
-/obj/item/stack/medical/proc/try_heal(mob/living/patient, mob/user, silent = FALSE)
+/obj/item/stack/medical/proc/try_heal(mob/living/patient, mob/user, silent = FALSE, looping = FALSE)
+ if(!try_heal_checks(patient, user, heal_brute, heal_burn, looping))
+ return
+ var/new_self_delay = looping ? clamp((self_delay-(1 SECONDS)), 0, self_delay) : self_delay
+ var/new_other_delay = looping ? clamp((other_delay-(1 SECONDS)), 0, other_delay) : other_delay
if(patient == user)
if(!silent)
user.visible_message(
@@ -84,7 +88,7 @@
)
if(!do_after(
user,
- self_delay,
+ new_self_delay,
patient,
extra_checks = CALLBACK(src, PROC_REF(can_heal), patient, user),
))
@@ -98,7 +102,7 @@
)
if(!do_after(
user,
- other_delay,
+ new_other_delay,
patient,
extra_checks = CALLBACK(src, PROC_REF(can_heal), patient, user),
))
@@ -111,7 +115,7 @@
return
if(!can_heal(patient, user))
return
- try_heal(patient, user, silent = TRUE)
+ try_heal(patient, user, silent = TRUE, looping = TRUE)
/// Apply the actual effects of the healing if it's a simple animal, goes to [/obj/item/stack/medical/proc/heal_carbon] if it's a carbon, returns TRUE if it works, FALSE if it doesn't
/obj/item/stack/medical/proc/heal(mob/living/patient, mob/user)
@@ -132,8 +136,11 @@
return heal_carbon(patient, user, heal_brute, heal_burn)
patient.balloon_alert(user, "can't heal that!")
-/// The healing effects on a carbon patient. Since we have extra details for dealing with bodyparts, we get our own fancy proc. Still returns TRUE on success and FALSE on fail
-/obj/item/stack/medical/proc/heal_carbon(mob/living/carbon/patient, mob/user, brute, burn)
+/obj/item/stack/medical/proc/try_heal_checks(mob/living/carbon/patient, mob/user, brute, burn, looping = FALSE)
+ if(looping)
+ balloon_alert(user, "assessing damage...")
+ if(!do_after(user, 1 SECONDS, patient))
+ return FALSE
var/obj/item/bodypart/affecting = patient.get_bodypart(check_zone(user.zone_selected))
if(!affecting) //Missing limb?
patient.balloon_alert(user, "no [parse_zone(user.zone_selected)]!")
@@ -141,18 +148,25 @@
if(!IS_ORGANIC_LIMB(affecting)) //Limb must be organic to be healed - RR
patient.balloon_alert(user, "it's not organic!")
return FALSE
- if(affecting.brute_dam && brute || affecting.burn_dam && burn)
- user.visible_message(
- span_infoplain(span_green("[user] applies [src] on [patient]'s [parse_zone(affecting.body_zone)].")),
- span_infoplain(span_green("You apply [src] on [patient]'s [parse_zone(affecting.body_zone)]."))
- )
- var/previous_damage = affecting.get_damage()
- if(affecting.heal_damage(brute, burn))
- patient.update_damage_overlays()
- post_heal_effects(max(previous_damage - affecting.get_damage(), 0), patient, user)
- return TRUE
- patient.balloon_alert(user, "can't heal that!")
- return FALSE
+ if(!(affecting.brute_dam && brute) && !(affecting.burn_dam && burn))
+ patient.balloon_alert(user, "can't heal [affecting]!")
+ return FALSE
+ return TRUE
+
+/// The healing effects on a carbon patient. Since we have extra details for dealing with bodyparts, we get our own fancy proc. Still returns TRUE on success and FALSE on fail
+/obj/item/stack/medical/proc/heal_carbon(mob/living/carbon/patient, mob/user, brute, burn)
+ var/obj/item/bodypart/affecting = patient.get_bodypart(check_zone(user.zone_selected))
+ if(!try_heal_checks(patient, user, brute, burn))
+ return FALSE
+ user.visible_message(
+ span_infoplain(span_green("[user] applies [src] on [patient]'s [parse_zone(affecting.body_zone)].")),
+ span_infoplain(span_green("You apply [src] on [patient]'s [parse_zone(affecting.body_zone)]."))
+ )
+ var/previous_damage = affecting.get_damage()
+ if(affecting.heal_damage(brute, burn))
+ patient.update_damage_overlays()
+ post_heal_effects(max(previous_damage - affecting.get_damage(), 0), patient, user)
+ return TRUE
///Override this proc for special post heal effects.
/obj/item/stack/medical/proc/post_heal_effects(amount_healed, mob/living/carbon/healed_mob, mob/user)
@@ -203,7 +217,7 @@
gauzed_bodypart = null
// gauze is only relevant for wounds, which are handled in the wounds themselves
-/obj/item/stack/medical/gauze/try_heal(mob/living/patient, mob/user, silent)
+/obj/item/stack/medical/gauze/try_heal(mob/living/patient, mob/user, silent, looping)
var/treatment_delay = (user == patient ? self_delay : other_delay)
@@ -375,7 +389,7 @@
return ..()
icon_state = "regen_mesh_closed"
-/obj/item/stack/medical/mesh/try_heal(mob/living/patient, mob/user, silent = FALSE)
+/obj/item/stack/medical/mesh/try_heal(mob/living/patient, mob/user, silent = FALSE, looping)
if(!is_open)
balloon_alert(user, "open it first!")
return
diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/crowbar.dm
index 9265dc7cb4e..bc4e0a94c16 100644
--- a/code/game/objects/items/tools/crowbar.dm
+++ b/code/game/objects/items/tools/crowbar.dm
@@ -235,7 +235,7 @@
var/mech_dir = mech.dir
mech.balloon_alert(user, "prying open...")
playsound(mech, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE)
- if(!use_tool(mech, user, mech.enclosed ? 5 SECONDS : 3 SECONDS, volume = 0, extra_checks = CALLBACK(src, PROC_REF(extra_checks), mech, mech_dir)))
+ if(!use_tool(mech, user, (mech.mecha_flags & IS_ENCLOSED) ? 5 SECONDS : 3 SECONDS, volume = 0, extra_checks = CALLBACK(src, PROC_REF(extra_checks), mech, mech_dir)))
mech.balloon_alert(user, "interrupted!")
return
user.log_message("pried open [mech], located at [loc_name(mech)], which is currently occupied by [mech.occupants.Join(", ")].", LOG_ATTACK)
diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm
index 7c7c321f0e2..1a174723d85 100644
--- a/code/game/turfs/open/lava.dm
+++ b/code/game/turfs/open/lava.dm
@@ -420,7 +420,7 @@
if(BODY_ZONE_HEAD)
plasmalimb = new /obj/item/bodypart/head/plasmaman
- burn_human.del_and_replace_bodypart(plasmalimb)
+ burn_human.del_and_replace_bodypart(plasmalimb, special = TRUE)
burn_human.update_body_parts()
burn_human.emote("scream")
burn_human.visible_message(span_warning("[burn_human]'s [burn_limb.plaintext_zone] melts down to the bone!"), \
diff --git a/code/modules/admin/verbs/admin.dm b/code/modules/admin/verbs/admin.dm
index d97ad84273e..d78014e84d2 100644
--- a/code/modules/admin/verbs/admin.dm
+++ b/code/modules/admin/verbs/admin.dm
@@ -174,10 +174,8 @@
if(confirm != "Yes")
return
- for(var/obj/item/W in M)
- if(!M.dropItemToGround(W))
- qdel(W)
- M.regenerate_icons()
+ M.drop_everything(del_on_drop = FALSE, force = TRUE, del_if_nodrop = TRUE)
+ M.regenerate_icons()
log_admin("[key_name(usr)] made [key_name(M)] drop everything!")
var/msg = "[key_name_admin(usr)] made [ADMIN_LOOKUPFLW(M)] drop everything!"
diff --git a/code/modules/admin/verbs/anonymousnames.dm b/code/modules/admin/verbs/anonymousnames.dm
index 6f01b88f113..9a71d68637a 100644
--- a/code/modules/admin/verbs/anonymousnames.dm
+++ b/code/modules/admin/verbs/anonymousnames.dm
@@ -155,7 +155,7 @@ GLOBAL_DATUM(current_anonymous_theme, /datum/anonymous_theme)
priority_announce("As punishment for this station's poor productivity when compared to neighbor stations, names and identities will be restricted until further notice.", "Finance Report", SSstation.announcer.get_rand_alert_sound())
/datum/anonymous_theme/employees/anonymous_name(mob/target)
- var/is_head_of_staff = target.mind.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND
+ var/is_head_of_staff = target.mind.assigned_role.job_flags & JOB_HEAD_OF_STAFF
var/name = "[is_head_of_staff ? "Manager" : "Employee"] "
for(var/i in 1 to 6)
if(prob(30) || i == 1)
diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm
index d49c7715462..f9879428ddf 100644
--- a/code/modules/admin/verbs/secrets.dm
+++ b/code/modules/admin/verbs/secrets.dm
@@ -506,8 +506,8 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller)
if(H.dna.features["tail_human"] == "None" || H.dna.features["ears"] == "None")
var/obj/item/organ/internal/ears/cat/ears = new
var/obj/item/organ/external/tail/cat/tail = new
- ears.Insert(H, drop_if_replaced=FALSE)
- tail.Insert(H, drop_if_replaced=FALSE)
+ ears.Insert(H, movement_flags = DELETE_IF_REPLACED)
+ tail.Insert(H, movement_flags = DELETE_IF_REPLACED)
var/list/honorifics = list("[MALE]" = list("kun"), "[FEMALE]" = list("chan","tan"), "[NEUTER]" = list("san"), "[PLURAL]" = list("san")) //John Robust -> Robust-kun
var/list/names = splittext(H.real_name," ")
var/forename = names.len > 1 ? names[2] : names[1]
diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm
index e29388c9fd6..960851280e3 100644
--- a/code/modules/antagonists/abductor/equipment/gland.dm
+++ b/code/modules/antagonists/abductor/equipment/gland.dm
@@ -84,7 +84,7 @@
active_mind_control = FALSE
return TRUE
-/obj/item/organ/internal/heart/gland/Remove(mob/living/carbon/gland_owner, special = FALSE)
+/obj/item/organ/internal/heart/gland/Remove(mob/living/carbon/gland_owner, special, movement_flags)
. = ..()
active = FALSE
if(initial(uses) == 1)
@@ -93,7 +93,7 @@
hud.remove_atom_from_hud(gland_owner)
clear_mind_control()
-/obj/item/organ/internal/heart/gland/Insert(mob/living/carbon/gland_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/heart/gland/Insert(mob/living/carbon/gland_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
if(!.)
return
diff --git a/code/modules/antagonists/abductor/equipment/glands/electric.dm b/code/modules/antagonists/abductor/equipment/glands/electric.dm
index a3107af0217..a5ec2cfde21 100644
--- a/code/modules/antagonists/abductor/equipment/glands/electric.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/electric.dm
@@ -7,11 +7,11 @@
mind_control_uses = 2
mind_control_duration = 900
-/obj/item/organ/internal/heart/gland/electric/on_insert(mob/living/carbon/gland_owner)
+/obj/item/organ/internal/heart/gland/electric/on_mob_insert(mob/living/carbon/gland_owner)
. = ..()
ADD_TRAIT(gland_owner, TRAIT_SHOCKIMMUNE, ABDUCTOR_GLAND_TRAIT)
-/obj/item/organ/internal/heart/gland/electric/on_remove(mob/living/carbon/gland_owner)
+/obj/item/organ/internal/heart/gland/electric/on_mob_remove(mob/living/carbon/gland_owner)
. = ..()
REMOVE_TRAIT(gland_owner, TRAIT_SHOCKIMMUNE, ABDUCTOR_GLAND_TRAIT)
diff --git a/code/modules/antagonists/abductor/equipment/glands/slime.dm b/code/modules/antagonists/abductor/equipment/glands/slime.dm
index 60e680bc2a9..853f0bb6473 100644
--- a/code/modules/antagonists/abductor/equipment/glands/slime.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/slime.dm
@@ -7,12 +7,12 @@
mind_control_uses = 1
mind_control_duration = 2400
-/obj/item/organ/internal/heart/gland/slime/on_insert(mob/living/carbon/gland_owner)
+/obj/item/organ/internal/heart/gland/slime/on_mob_insert(mob/living/carbon/gland_owner)
. = ..()
gland_owner.faction |= FACTION_SLIME
gland_owner.grant_language(/datum/language/slime, source = LANGUAGE_GLAND)
-/obj/item/organ/internal/heart/gland/slime/on_remove(mob/living/carbon/gland_owner)
+/obj/item/organ/internal/heart/gland/slime/on_mob_remove(mob/living/carbon/gland_owner)
. = ..()
gland_owner.faction -= FACTION_SLIME
gland_owner.remove_language(/datum/language/slime, source = LANGUAGE_GLAND)
diff --git a/code/modules/antagonists/changeling/powers/headcrab.dm b/code/modules/antagonists/changeling/powers/headcrab.dm
index f608d1620b8..30970832df0 100644
--- a/code/modules/antagonists/changeling/powers/headcrab.dm
+++ b/code/modules/antagonists/changeling/powers/headcrab.dm
@@ -6,10 +6,12 @@
chemical_cost = 20
dna_cost = 1
req_human = TRUE
+ req_stat = DEAD
+ ignores_fakedeath = TRUE
/datum/action/changeling/headcrab/sting_action(mob/living/user)
set waitfor = FALSE
- var/confirm = tgui_alert(user, "Are we sure we wish to kill ourself and create a headslug?", "Last Resort", list("Yes", "No"))
+ var/confirm = tgui_alert(user, "Are we sure we wish to destroy our body and create a headslug?", "Last Resort", list("Yes", "No"))
if(confirm != "Yes")
return
@@ -23,9 +25,9 @@
if(!eyes || blinded_human.is_blind())
continue
to_chat(blinded_human, span_userdanger("You are blinded by a shower of blood!"))
- blinded_human.Stun(2 SECONDS)
+ blinded_human.Stun(4 SECONDS)
blinded_human.set_eye_blur_if_lower(40 SECONDS)
- blinded_human.adjust_confusion(3 SECONDS)
+ blinded_human.adjust_confusion(12 SECONDS)
for(var/mob/living/silicon/blinded_silicon in range(2,user))
to_chat(blinded_silicon, span_userdanger("Your sensors are disabled by a shower of blood!"))
@@ -39,6 +41,7 @@
. = TRUE
addtimer(CALLBACK(src, PROC_REF(spawn_headcrab), stored_mind, user_turf, organs), 1 SECONDS)
+/// Creates the headrab to occupy
/datum/action/changeling/headcrab/proc/spawn_headcrab(datum/mind/stored_mind, turf/spawn_location, list/organs)
var/mob/living/basic/headslug/crab = new(spawn_location)
for(var/obj/item/organ/I in organs)
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index c445e73e90e..bed29dbefd5 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -222,52 +222,58 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/convert/invoke(list/invokers)
if(rune_in_use)
return
+
var/list/myriad_targets = list()
- var/turf/T = get_turf(src)
- for(var/mob/living/M in T)
- if(!IS_CULTIST(M))
- myriad_targets |= M
- if(!length(myriad_targets))
+ for(var/mob/living/non_cultist in loc)
+ if(!IS_CULTIST(non_cultist))
+ myriad_targets += non_cultist
+
+ if(!length(myriad_targets) && !try_spawn_sword())
fail_invoke()
- log_game("Offer rune failed - no eligible targets.")
return
+
rune_in_use = TRUE
visible_message(span_warning("[src] pulses blood red!"))
var/oldcolor = color
color = RUNE_COLOR_DARKRED
- var/mob/living/L = pick(myriad_targets)
-
- var/mob/living/F = invokers[1]
- var/datum/antagonist/cult/C = F.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
- var/datum/team/cult/Cult_team = C.cult_team
- var/is_convertable = is_convertable_to_cult(L, C.cult_team)
- if(L.stat != DEAD && is_convertable)
- invocation = "Mah'weyh pleggh at e'ntrath!"
- ..()
- if(is_convertable)
- do_convert(L, invokers, Cult_team)
+
+ if(length(myriad_targets))
+ var/mob/living/new_convertee = pick(myriad_targets)
+ var/mob/living/first_invoker = invokers[1]
+ var/datum/antagonist/cult/first_invoker_datum = first_invoker.mind.has_antag_datum(/datum/antagonist/cult)
+ var/datum/team/cult/cult_team = first_invoker_datum.get_team()
+
+ var/is_convertable = is_convertable_to_cult(new_convertee, cult_team)
+ if(new_convertee.stat != DEAD && is_convertable)
+ invocation = "Mah'weyh pleggh at e'ntrath!"
+ ..()
+ do_convert(new_convertee, invokers, cult_team)
+
+ else
+ invocation = "Barhah hra zar'garis!"
+ ..()
+ do_sacrifice(new_convertee, invokers, cult_team)
+
+ cult_team.check_size() // Triggers the eye glow or aura effects if the cult has grown large enough relative to the crew
+
else
- invocation = "Barhah hra zar'garis!"
- ..()
- do_sacrifice(L, invokers)
- animate(src, color = oldcolor, time = 5)
- addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_atom_colour)), 5)
- Cult_team.check_size() // Triggers the eye glow or aura effects if the cult has grown large enough relative to the crew
+ do_invoke_glow()
+
+ animate(src, color = oldcolor, time = 0.5 SECONDS)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_atom_colour)), 0.5 SECONDS)
rune_in_use = FALSE
/obj/effect/rune/convert/proc/do_convert(mob/living/convertee, list/invokers, datum/team/cult/cult_team)
ASSERT(convertee.mind)
if(length(invokers) < 2)
- for(var/M in invokers)
- to_chat(M, span_warning("You need at least two invokers to convert [convertee]!"))
- log_game("Offer rune with [convertee] on it failed - tried conversion with one invoker.")
+ for(var/invoker in invokers)
+ to_chat(invoker, span_warning("You need at least two invokers to convert [convertee]!"))
return FALSE
if(convertee.can_block_magic(MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY, charge_cost = 0)) //No charge_cost because it can be spammed
- for(var/M in invokers)
- to_chat(M, span_warning("Something is shielding [convertee]'s mind!"))
- log_game("Offer rune with [convertee] on it failed - convertee had anti-magic.")
+ for(var/invoker in invokers)
+ to_chat(invoker, span_warning("Something is shielding [convertee]'s mind!"))
return FALSE
var/brutedamage = convertee.getBruteLoss()
@@ -314,19 +320,11 @@ structure_check() searches for nearby cultist structures required for the invoca
convertee.name = convertee.real_name
return TRUE
-/obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers)
- var/mob/living/first_invoker = invokers[1]
- if(!first_invoker)
- return FALSE
- var/datum/antagonist/cult/C = first_invoker.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
- if(!C)
- return FALSE
-
+/obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers, datum/team/cult/cult_team)
var/big_sac = FALSE
- if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || C.cult_team.is_sacrifice_target(sacrificial.mind)) && length(invokers) < 3)
- for(var/M in invokers)
- to_chat(M, span_cultitalic("[sacrificial] is too greatly linked to the world! You need three acolytes!"))
- log_game("Offer rune with [sacrificial] on it failed - not enough acolytes and target is living or sac target")
+ if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || cult_team.is_sacrifice_target(sacrificial.mind)) && length(invokers) < 3)
+ for(var/invoker in invokers)
+ to_chat(invoker, span_cultitalic("[sacrificial] is too greatly linked to the world! You need three acolytes!"))
return FALSE
var/signal_result = SEND_SIGNAL(sacrificial, COMSIG_LIVING_CULT_SACRIFICED, invokers)
@@ -335,7 +333,7 @@ structure_check() searches for nearby cultist structures required for the invoca
if(sacrificial.mind)
LAZYADD(GLOB.sacrificed, WEAKREF(sacrificial.mind))
- for(var/datum/objective/sacrifice/sac_objective in C.cult_team.objectives)
+ for(var/datum/objective/sacrifice/sac_objective in cult_team.objectives)
if(sac_objective.target == sacrificial.mind)
sac_objective.sacced = TRUE
sac_objective.clear_sacrifice()
@@ -344,7 +342,7 @@ structure_check() searches for nearby cultist structures required for the invoca
else
LAZYADD(GLOB.sacrificed, WEAKREF(sacrificial))
- new /obj/effect/temp_visual/cult/sac(get_turf(src))
+ new /obj/effect/temp_visual/cult/sac(loc)
if(!(signal_result & SILENCE_SACRIFICE_MESSAGE))
for(var/invoker in invokers)
@@ -357,26 +355,70 @@ structure_check() searches for nearby cultist structures required for the invoca
to_chat(invoker, span_cultlarge("\"I accept this meager sacrifice.\""))
if(iscyborg(sacrificial))
- var/construct_class = show_radial_menu(first_invoker, sacrificial, GLOB.construct_radial_images, require_near = TRUE, tooltips = TRUE)
+ var/construct_class = show_radial_menu(invokers[1], sacrificial, GLOB.construct_radial_images, require_near = TRUE, tooltips = TRUE)
if(QDELETED(sacrificial) || !construct_class)
return FALSE
sacrificial.grab_ghost()
- make_new_construct_from_class(construct_class, THEME_CULT, sacrificial, first_invoker, TRUE, get_turf(src))
+ make_new_construct_from_class(construct_class, THEME_CULT, sacrificial, invokers[1], TRUE, get_turf(src))
var/mob/living/silicon/robot/sacriborg = sacrificial
sacrificial.log_message("was sacrificed as a cyborg.", LOG_GAME)
sacriborg.mmi = null
qdel(sacrificial)
return TRUE
- var/obj/item/soulstone/stone = new /obj/item/soulstone(get_turf(src))
+
+ var/obj/item/soulstone/stone = new(loc)
if(sacrificial.mind && !HAS_TRAIT(sacrificial, TRAIT_SUICIDED))
- stone.capture_soul(sacrificial, first_invoker, TRUE)
+ stone.capture_soul(sacrificial, invokers[1], forced = TRUE)
if(sacrificial)
playsound(sacrificial, 'sound/magic/disintegrate.ogg', 100, TRUE)
sacrificial.investigate_log("has been sacrificially gibbed by the cult.", INVESTIGATE_DEATHS)
sacrificial.gib(DROP_ALL_REMAINS)
+
+ try_spawn_sword() // after sharding and gibbing, which potentially dropped a null rod
return TRUE
+/// Tries to convert a null rod over the rune to a cult sword
+/obj/effect/rune/convert/proc/try_spawn_sword()
+ for(var/obj/item/nullrod/rod in loc)
+ if(rod.anchored || (rod.resistance_flags & INDESTRUCTIBLE))
+ continue
+
+ var/num_slain = LAZYLEN(rod.cultists_slain)
+ var/displayed_message = "[rod] glows an unholy red and begins to transform..."
+ if(GET_ATOM_BLOOD_DNA_LENGTH(rod))
+ displayed_message += " The blood of [num_slain] fallen cultist[num_slain == 1 ? "":"s"] is absorbed into [rod]!"
+
+ rod.visible_message(span_cultitalic(displayed_message))
+ switch(num_slain)
+ if(0, 1)
+ animate_spawn_sword(rod, /obj/item/melee/cultblade/dagger)
+ if(2)
+ animate_spawn_sword(rod, /obj/item/melee/cultblade)
+ else
+ animate_spawn_sword(rod, /obj/item/cult_bastard)
+ return TRUE
+
+ return FALSE
+
+/// Does an animation of a null rod transforming into a cult sword
+/obj/effect/rune/convert/proc/animate_spawn_sword(obj/item/nullrod/former_rod, new_blade_typepath)
+ playsound(src, 'sound/effects/magic.ogg', 33, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 0.66)
+ former_rod.anchored = TRUE
+ former_rod.Shake()
+ animate(former_rod, alpha = 0, transform = matrix(former_rod.transform).Scale(0.01), time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL)
+ QDEL_IN(former_rod, 2 SECONDS)
+
+ var/obj/item/new_blade = new new_blade_typepath(loc)
+ var/matrix/blade_matrix_on_spawn = matrix(new_blade.transform)
+ new_blade.name = "converted [new_blade.name]"
+ new_blade.anchored = TRUE
+ new_blade.alpha = 0
+ new_blade.transform = matrix(new_blade.transform).Scale(0.01)
+ new_blade.Shake()
+ animate(new_blade, alpha = 255, transform = blade_matrix_on_spawn, time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL)
+ addtimer(VARSET_CALLBACK(new_blade, anchored, FALSE), 2 SECONDS)
+
/obj/effect/rune/empower
cultist_name = "Empower"
cultist_desc = "allows cultists to prepare greater amounts of blood magic at far less of a cost."
diff --git a/code/modules/antagonists/fugitive/fugitive_outfits.dm b/code/modules/antagonists/fugitive/fugitive_outfits.dm
index ba1fe87bfbb..3efab657101 100644
--- a/code/modules/antagonists/fugitive/fugitive_outfits.dm
+++ b/code/modules/antagonists/fugitive/fugitive_outfits.dm
@@ -67,7 +67,7 @@
if(visualsOnly)
return
var/obj/item/organ/internal/eyes/robotic/glow/eyes = new()
- eyes.Insert(H, drop_if_replaced = FALSE)
+ eyes.Insert(H, movement_flags = DELETE_IF_REPLACED)
/datum/outfit/invisible_man
name = "Invisible Man"
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index 1b73918ba62..44111fb0ea8 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -231,7 +231,6 @@
RegisterSignal(our_mob, COMSIG_MOB_ITEM_AFTERATTACK, PROC_REF(on_item_afterattack))
RegisterSignal(our_mob, COMSIG_MOB_LOGIN, PROC_REF(fix_influence_network))
RegisterSignal(our_mob, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(after_fully_healed))
- RegisterSignal(our_mob, COMSIG_LIVING_CULT_SACRIFICED, PROC_REF(on_cult_sacrificed))
/datum/antagonist/heretic/remove_innate_effects(mob/living/mob_override)
var/mob/living/our_mob = mob_override || owner.current
@@ -247,7 +246,6 @@
COMSIG_MOB_ITEM_AFTERATTACK,
COMSIG_MOB_LOGIN,
COMSIG_LIVING_POST_FULLY_HEAL,
- COMSIG_LIVING_CULT_SACRIFICED
))
/datum/antagonist/heretic/on_body_transfer(mob/living/old_body, mob/living/new_body)
@@ -395,15 +393,6 @@
var/datum/heretic_knowledge/living_heart/heart_knowledge = get_knowledge(/datum/heretic_knowledge/living_heart)
heart_knowledge.on_research(source, src)
-/// Signal proc for [COMSIG_LIVING_CULT_SACRIFICED] to reward cultists for sacrificing a heretic
-/datum/antagonist/heretic/proc/on_cult_sacrificed(mob/living/source, list/invokers)
- SIGNAL_HANDLER
-
- new /obj/item/cult_bastard(source.loc)
- for(var/mob/living/cultist as anything in invokers)
- to_chat(cultist, span_cultlarge("\"A follower of the forgotten gods! You must be rewarded for such a valuable sacrifice.\""))
- return SILENCE_SACRIFICE_MESSAGE
-
/**
* Create our objectives for our heretic.
*/
@@ -414,7 +403,7 @@
var/num_heads = 0
for(var/mob/player in GLOB.alive_player_list)
- if(player.mind.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(player.mind.assigned_role.job_flags & JOB_HEAD_OF_STAFF)
num_heads++
var/datum/objective/minor_sacrifice/sac_objective = new()
diff --git a/code/modules/antagonists/heretic/knowledge/blade_lore.dm b/code/modules/antagonists/heretic/knowledge/blade_lore.dm
index 03b9a716d53..257f87d7c9f 100644
--- a/code/modules/antagonists/heretic/knowledge/blade_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/blade_lore.dm
@@ -227,10 +227,7 @@
During this process, you will rapidly regenerate stamina and quickly recover from stuns, however, you will be unable to attack. \
This spell can be cast in rapid succession, but doing so will increase the cooldown."
gain_text = "In the flurry of death, he found peace within himself. Despite insurmountable odds, he forged on."
- next_knowledge = list(
- /datum/heretic_knowledge/duel_stance,
- /datum/heretic_knowledge/rifle,
- )
+ next_knowledge = list(/datum/heretic_knowledge/duel_stance)
spell_to_add = /datum/action/cooldown/spell/realignment
cost = 1
route = PATH_BLADE
@@ -250,6 +247,7 @@
/datum/heretic_knowledge/reroll_targets,
/datum/heretic_knowledge/rune_carver,
/datum/heretic_knowledge/crucible,
+ /datum/heretic_knowledge/rifle,
)
cost = 1
route = PATH_BLADE
diff --git a/code/modules/antagonists/heretic/knowledge/lock_lore.dm b/code/modules/antagonists/heretic/knowledge/lock_lore.dm
index cf425563c13..99629365679 100644
--- a/code/modules/antagonists/heretic/knowledge/lock_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/lock_lore.dm
@@ -103,27 +103,12 @@
)
result_atoms = list(/obj/item/card/id/advanced/heretic)
next_knowledge = list(
- /datum/heretic_knowledge/limited_amount/concierge_rite,
+ /datum/heretic_knowledge/mark/lock_mark,
/datum/heretic_knowledge/spell/mind_gate,
)
cost = 1
route = PATH_LOCK
-/datum/heretic_knowledge/limited_amount/concierge_rite // item that creates 3 max at a time heretic only barriers, probably should limit to 1 only, holy people can also pass
- name = "Concierge's Rite"
- desc = "Allows you to transmute a white crayon, a wooden plank, and a multitool to create a Labyrinth Handbook. \
- It can materialize a barricade at range that only you and people resistant to magic can pass. 3 uses."
- gain_text = "The Concierge scribbled my name into the Handbook. \"Welcome to your new home, fellow Steward.\""
- required_atoms = list(
- /obj/item/toy/crayon/white = 1,
- /obj/item/stack/sheet/mineral/wood = 1,
- /obj/item/multitool = 1,
- )
- result_atoms = list(/obj/item/heretic_labyrinth_handbook)
- next_knowledge = list(/datum/heretic_knowledge/mark/lock_mark)
- cost = 1
- route = PATH_LOCK
-
/datum/heretic_knowledge/mark/lock_mark
name = "Mark of Lock"
desc = "Your Mansus Grasp now applies the Mark of Lock. \
@@ -135,7 +120,22 @@
mark_type = /datum/status_effect/eldritch/lock
/datum/heretic_knowledge/knowledge_ritual/lock
+ next_knowledge = list(/datum/heretic_knowledge/limited_amount/concierge_rite)
+ route = PATH_LOCK
+
+/datum/heretic_knowledge/limited_amount/concierge_rite // item that creates 3 max at a time heretic only barriers, probably should limit to 1 only, holy people can also pass
+ name = "Concierge's Rite"
+ desc = "Allows you to transmute a white crayon, a wooden plank, and a multitool to create a Labyrinth Handbook. \
+ It can materialize a barricade at range that only you and people resistant to magic can pass. 3 uses."
+ gain_text = "The Concierge scribbled my name into the Handbook. \"Welcome to your new home, fellow Steward.\""
+ required_atoms = list(
+ /obj/item/toy/crayon/white = 1,
+ /obj/item/stack/sheet/mineral/wood = 1,
+ /obj/item/multitool = 1,
+ )
+ result_atoms = list(/obj/item/heretic_labyrinth_handbook)
next_knowledge = list(/datum/heretic_knowledge/spell/burglar_finesse)
+ cost = 1
route = PATH_LOCK
/datum/heretic_knowledge/spell/burglar_finesse
@@ -203,8 +203,7 @@
for(var/mob/living/carbon/human/body in atoms)
if(body.stat != DEAD)
continue
- var/obj/item/bodypart/chest = body.get_bodypart(BODY_ZONE_CHEST)
- if(LAZYLEN(chest.get_organs()))
+ if(LAZYLEN(body.get_organs_for_zone(BODY_ZONE_CHEST)))
to_chat(user, span_hierophant_warning("[body] has organs in their chest."))
continue
diff --git a/code/modules/antagonists/heretic/knowledge/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
index 6817dc9de44..8d577b1992a 100644
--- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
@@ -161,10 +161,7 @@
Anyone overtop the wall will be throw aside (or upwards) and sustain damage."
gain_text = "Images of foreign and ominous structures began to dance in my mind. Covered head to toe in thick rust, \
they no longer looked man made. Or perhaps they never were in the first place."
- next_knowledge = list(
- /datum/heretic_knowledge/spell/area_conversion,
- /datum/heretic_knowledge/rifle,
- )
+ next_knowledge = list(/datum/heretic_knowledge/spell/area_conversion)
spell_to_add = /datum/action/cooldown/spell/pointed/rust_construction
cost = 1
route = PATH_RUST
@@ -179,6 +176,7 @@
/datum/heretic_knowledge/reroll_targets,
/datum/heretic_knowledge/curse/corrosion,
/datum/heretic_knowledge/crucible,
+ /datum/heretic_knowledge/rifle,
)
spell_to_add = /datum/action/cooldown/spell/aoe/rust_conversion
cost = 1
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
index b66ec468e71..797d754ea0b 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
@@ -130,7 +130,7 @@
// First target, any command.
for(var/datum/mind/head_mind as anything in shuffle(valid_targets))
- if(head_mind.assigned_role?.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(head_mind.assigned_role?.job_flags & JOB_HEAD_OF_STAFF)
final_targets += head_mind
valid_targets -= head_mind
break
@@ -187,8 +187,8 @@
heretic_datum.remove_sacrifice_target(sacrifice)
var/feedback = "Your patrons accept your offer"
- var/sac_department_flag = sacrifice.mind?.assigned_role?.departments_bitflags | sacrifice.last_mind?.assigned_role?.departments_bitflags
- if(sac_department_flag & DEPARTMENT_BITFLAG_COMMAND)
+ var/sac_job_flag = sacrifice.mind?.assigned_role?.job_flags | sacrifice.last_mind?.assigned_role?.job_flags
+ if(sac_job_flag & JOB_HEAD_OF_STAFF)
heretic_datum.knowledge_points++
heretic_datum.high_value_sacrifices++
feedback += " graciously"
diff --git a/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm b/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm
index d7dfd75a144..e8c14d16abe 100644
--- a/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm
@@ -46,8 +46,8 @@
gain_text = "I met an old man in an anique shop who wielded a very unusual weapon. \
I could not purchase it at the time, but they showed me how they made it ages ago."
next_knowledge = list(
- /datum/heretic_knowledge/spell/realignment,
- /datum/heretic_knowledge/spell/rust_construction,
+ /datum/heretic_knowledge/duel_stance,
+ /datum/heretic_knowledge/spell/area_conversion,
/datum/heretic_knowledge/rifle_ammo,
)
required_atoms = list(
diff --git a/code/modules/antagonists/heretic/knowledge/starting_lore.dm b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
index eb766392290..f1b5f7f55ea 100644
--- a/code/modules/antagonists/heretic/knowledge/starting_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
@@ -237,6 +237,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
cost = 1
route = PATH_START
priority = MAX_KNOWLEDGE_PRIORITY - 3 // Least priority out of the starting knowledges, as it's an optional boon.
+ var/static/list/non_mob_bindings = typecacheof(list(/obj/item/stack/sheet/leather, /obj/item/stack/sheet/animalhide))
/datum/heretic_knowledge/codex_cicatrix/parse_required_item(atom/item_path, number_of_things)
if(item_path == /obj/item/pen)
@@ -248,12 +249,16 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
if(!.)
return FALSE
- for(var/mob/living/body in atoms)
- if(body.stat != DEAD)
- continue
-
- selected_atoms += body
- return TRUE
+ for(var/thingy in atoms)
+ if(is_type_in_typecache(thingy, non_mob_bindings))
+ selected_atoms += thingy
+ return TRUE
+ else if(isliving(thingy))
+ var/mob/living/body = thingy
+ if(body.stat != DEAD)
+ continue
+ selected_atoms += body
+ return TRUE
return FALSE
/datum/heretic_knowledge/codex_cicatrix/cleanup_atoms(list/selected_atoms)
diff --git a/code/modules/antagonists/heretic/transmutation_rune.dm b/code/modules/antagonists/heretic/transmutation_rune.dm
index 31c252eacf1..2479ec715e0 100644
--- a/code/modules/antagonists/heretic/transmutation_rune.dm
+++ b/code/modules/antagonists/heretic/transmutation_rune.dm
@@ -80,6 +80,10 @@
for(var/atom/close_atom as anything in range(1, src))
if(!ismovable(close_atom))
continue
+ if(isitem(close_atom))
+ var/obj/item/close_item = close_atom
+ if(close_item.item_flags & ABSTRACT) //woops sacrificed your own head
+ continue
if(close_atom.invisibility)
continue
if(close_atom == user)
diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm
index 98659ef1941..077bd2158b8 100644
--- a/code/modules/antagonists/highlander/highlander.dm
+++ b/code/modules/antagonists/highlander/highlander.dm
@@ -52,9 +52,8 @@
if(!istype(H))
return
- for(var/obj/item/I in H)
- if(!H.dropItemToGround(I))
- qdel(I)
+ H.drop_everything(del_on_drop = FALSE, force = TRUE, del_if_nodrop = TRUE)
+
H.regenerate_icons()
H.revive(ADMIN_HEAL_ALL)
H.equip_to_slot_or_del(new /obj/item/clothing/under/costume/kilt/highlander(H), ITEM_SLOT_ICLOTHING)
diff --git a/code/modules/antagonists/nightmare/nightmare_organs.dm b/code/modules/antagonists/nightmare/nightmare_organs.dm
index 50bfd8e1619..c06f1e322a2 100644
--- a/code/modules/antagonists/nightmare/nightmare_organs.dm
+++ b/code/modules/antagonists/nightmare/nightmare_organs.dm
@@ -15,7 +15,7 @@
///Our associated terrorize spell, for antagonist nightmares
var/datum/action/cooldown/spell/pointed/terrorize/terrorize_spell
-/obj/item/organ/internal/brain/shadow/nightmare/on_insert(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/shadow/nightmare/on_mob_insert(mob/living/carbon/brain_owner)
. = ..()
if(brain_owner.dna.species.id != SPECIES_NIGHTMARE)
@@ -29,7 +29,7 @@
terrorize_spell = new(src)
terrorize_spell.Grant(brain_owner)
-/obj/item/organ/internal/brain/shadow/nightmare/on_remove(mob/living/carbon/brain_owner)
+/obj/item/organ/internal/brain/shadow/nightmare/on_mob_remove(mob/living/carbon/brain_owner)
. = ..()
QDEL_NULL(our_jaunt)
QDEL_NULL(terrorize_spell)
@@ -91,13 +91,13 @@
user.temporarilyRemoveItemFromInventory(src, TRUE)
Insert(user)
-/obj/item/organ/internal/heart/nightmare/on_insert(mob/living/carbon/heart_owner, special)
+/obj/item/organ/internal/heart/nightmare/on_mob_insert(mob/living/carbon/heart_owner, special)
. = ..()
if(special != HEART_SPECIAL_SHADOWIFY)
blade = new/obj/item/light_eater
heart_owner.put_in_hands(blade)
-/obj/item/organ/internal/heart/nightmare/on_remove(mob/living/carbon/heart_owner, special)
+/obj/item/organ/internal/heart/nightmare/on_mob_remove(mob/living/carbon/heart_owner, special)
. = ..()
respawn_progress = 0
if(blade && special != HEART_SPECIAL_SHADOWIFY)
diff --git a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
index cd1ad0b02c0..84f3a151dd2 100644
--- a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
+++ b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
@@ -395,7 +395,7 @@
return 0
else if(FACTION_PIRATE in ransomee.faction) //can't ransom your fellow pirates to CentCom!
return 0
- else if(ransomee.mind.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ else if(HAS_TRAIT(ransomee, TRAIT_HIGH_VALUE_RANSOM))
return 3000
else
return 1000
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index 6ee5ba5dcc7..708f94c3fc3 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -12,7 +12,7 @@
var/deconversion_source
/datum/antagonist/rev/can_be_owned(datum/mind/new_owner)
- if(new_owner.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(new_owner.assigned_role.job_flags & JOB_HEAD_OF_STAFF)
return FALSE
if(new_owner.unconvertable)
return FALSE
diff --git a/code/modules/antagonists/traitor/objectives/assassination.dm b/code/modules/antagonists/traitor/objectives/assassination.dm
index f3b76874213..9fae7ed3352 100644
--- a/code/modules/antagonists/traitor/objectives/assassination.dm
+++ b/code/modules/antagonists/traitor/objectives/assassination.dm
@@ -201,10 +201,10 @@
continue
//removes heads of staff from being targets from non heads of staff assassinations, and vice versa
if(heads_of_staff)
- if(!(possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if(!(possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
else
- if((possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if((possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
possible_targets += possible_target
for(var/datum/traitor_objective/target_player/objective as anything in possible_duplicates)
diff --git a/code/modules/antagonists/traitor/objectives/eyesnatching.dm b/code/modules/antagonists/traitor/objectives/eyesnatching.dm
index 5cf95e90384..5773c4e90e0 100644
--- a/code/modules/antagonists/traitor/objectives/eyesnatching.dm
+++ b/code/modules/antagonists/traitor/objectives/eyesnatching.dm
@@ -74,10 +74,10 @@
continue
if(heads_of_staff)
- if(!(possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if(!(possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
else
- if(possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ if(possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF)
continue
var/mob/living/carbon/human/targets_current = possible_target.current
diff --git a/code/modules/antagonists/traitor/objectives/infect.dm b/code/modules/antagonists/traitor/objectives/infect.dm
index b1bb868e903..3ea27192c17 100644
--- a/code/modules/antagonists/traitor/objectives/infect.dm
+++ b/code/modules/antagonists/traitor/objectives/infect.dm
@@ -102,10 +102,10 @@
continue
//removes heads of staff from being targets from non heads of staff assassinations, and vice versa
if(heads_of_staff)
- if(!(possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if(!(possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
else
- if((possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
+ if((possible_target.assigned_role.job_flags & JOB_HEAD_OF_STAFF))
continue
possible_targets += possible_target
for(var/datum/traitor_objective/target_player/objective as anything in possible_duplicates)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 3be90e397c6..8aa3258c120 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -624,7 +624,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
/datum/preferences/proc/should_be_random_hardcore(datum/job/job, datum/mind/mind)
if(!read_preference(/datum/preference/toggle/random_hardcore))
return FALSE
- if(job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) //No command staff
+ if(job.job_flags & JOB_HEAD_OF_STAFF) //No heads of staff
return FALSE
for(var/datum/antagonist/antag as anything in mind.antag_datums)
if(antag.get_team()) //No team antags
diff --git a/code/modules/client/preferences/language.dm b/code/modules/client/preferences/language.dm
index d26f9a15ffb..7e8e6cb7113 100644
--- a/code/modules/client/preferences/language.dm
+++ b/code/modules/client/preferences/language.dm
@@ -20,7 +20,7 @@
//we add uncommon as it's foreigner-only.
var/datum/language/uncommon/uncommon_language = /datum/language/uncommon
values += initial(uncommon_language.name)
- values += /datum/language/common::name // NOVA EDIT ADDITION START - Let's you select common
+ values += /datum/language/common::name // NOVA EDIT ADDITION START - Lets you select common
for(var/datum/language/language_type as anything in GLOB.uncommon_roundstart_languages)
if(initial(language_type.name) in values)
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index b87a3f7b221..a4c6393ed6f 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -81,10 +81,10 @@
var/tie_timer_actual = tie_timer
// Mirrors give you a boost to your tying speed. I realize this stacks and I think that's hilarious.
for(var/obj/structure/mirror/reflection in view(2, user))
- tie_timer_actual /= 1.25
+ tie_timer_actual *= 0.8
// Heads of staff are experts at tying their ties.
- if(user.mind?.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
- tie_timer_actual /= 2
+ if(HAS_TRAIT(user, TRAIT_FAST_TYING))
+ tie_timer_actual *= 0.5
// Tie/Untie our tie
if(!do_after(user, tie_timer_actual))
to_chat(user, span_notice("Your fingers fumble away from [src] as your concentration breaks."))
diff --git a/code/modules/food_and_drinks/machinery/microwave.dm b/code/modules/food_and_drinks/machinery/microwave.dm
index 0471f5e92ef..c4cc6d378e3 100644
--- a/code/modules/food_and_drinks/machinery/microwave.dm
+++ b/code/modules/food_and_drinks/machinery/microwave.dm
@@ -486,7 +486,7 @@
/obj/machinery/microwave/CtrlClick(mob/user)
. = ..()
- if(cell_powered && !isnull(cell) && anchored)
+ if(user.can_perform_action(src) && cell_powered && !isnull(cell) && anchored)
user.put_in_hands(cell)
balloon_alert(user, "removed cell")
cell = null
diff --git a/code/modules/food_and_drinks/machinery/smartfridge.dm b/code/modules/food_and_drinks/machinery/smartfridge.dm
index c4ec48f4dd6..ae06fb93f83 100644
--- a/code/modules/food_and_drinks/machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/machinery/smartfridge.dm
@@ -6,6 +6,7 @@
desc = "Keeps cold things cold and hot things cold."
icon = 'icons/obj/machines/smartfridge.dmi'
icon_state = "smartfridge"
+ base_icon_state = "plant"
layer = BELOW_OBJ_LAYER
density = TRUE
circuit = /obj/item/circuitboard/machine/smartfridge
@@ -17,8 +18,6 @@
var/base_build_path = /obj/machinery/smartfridge
/// Maximum number of items that can be loaded into the machine
var/max_n_of_items = 1500
- /// The overlay for this fridge when it is filled with stuff
- var/contents_icon_state = "plant"
/// List of items that the machine starts with upon spawn
var/list/initial_contents
/// If the machine shows an approximate number of its contents on its sprite
@@ -226,9 +225,10 @@
/obj/machinery/smartfridge/update_overlays()
. = ..()
+ var/initial_icon_state = initial(icon_state)
var/shown_contents_length = visible_items()
if(visible_contents && shown_contents_length)
- var/content_level = "[initial(icon_state)]-[contents_icon_state]"
+ var/content_level = "[initial_icon_state]-[base_icon_state]"
switch(shown_contents_length)
if(1 to 25)
content_level += "-1"
@@ -238,10 +238,10 @@
content_level += "-3"
. += mutable_appearance(icon, content_level)
- . += mutable_appearance(icon, "[initial(icon_state)]-glass[(machine_stat & BROKEN) ? "-broken" : ""]")
+ . += mutable_appearance(icon, "[initial_icon_state]-glass[(machine_stat & BROKEN) ? "-broken" : ""]")
if(!machine_stat && has_emissive)
- . += emissive_appearance(icon, "[initial(icon_state)]-light-mask", src, alpha = src.alpha)
+ . += emissive_appearance(icon, "[initial_icon_state]-light-mask", src, alpha = src.alpha)
/obj/machinery/smartfridge/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
@@ -584,7 +584,7 @@
name = "drink showcase"
desc = "A refrigerated storage unit for tasty tasty alcohol."
base_build_path = /obj/machinery/smartfridge/drinks
- contents_icon_state = "drink"
+ base_icon_state = "drink"
/obj/machinery/smartfridge/drinks/accept_check(obj/item/weapon)
//not an item or valid container
@@ -604,7 +604,7 @@
/obj/machinery/smartfridge/food
desc = "A refrigerated storage unit for food."
base_build_path = /obj/machinery/smartfridge/food
- contents_icon_state = "food"
+ base_icon_state = "food"
/obj/machinery/smartfridge/food/accept_check(obj/item/weapon)
if(weapon.w_class >= WEIGHT_CLASS_BULKY)
@@ -622,7 +622,7 @@
name = "smart slime extract storage"
desc = "A refrigerated storage unit for slime extracts."
base_build_path = /obj/machinery/smartfridge/extract
- contents_icon_state = "slime"
+ base_icon_state = "slime"
/obj/machinery/smartfridge/extract/accept_check(obj/item/weapon)
return (istype(weapon, /obj/item/slime_extract) || istype(weapon, /obj/item/slime_scanner))
@@ -637,7 +637,7 @@
name = "smart petri dish storage"
desc = "A refrigerated storage unit for petri dishes."
base_build_path = /obj/machinery/smartfridge/petri
- contents_icon_state = "petri"
+ base_icon_state = "petri"
/obj/machinery/smartfridge/petri/accept_check(obj/item/weapon)
return istype(weapon, /obj/item/petri_dish)
@@ -653,7 +653,7 @@
desc = "A refrigerated storage unit for organ storage."
max_n_of_items = 20 //vastly lower to prevent processing too long
base_build_path = /obj/machinery/smartfridge/organ
- contents_icon_state = "organ"
+ base_icon_state = "organ"
/// The rate at which this fridge will repair damaged organs
var/repair_rate = 0
@@ -706,7 +706,7 @@
name = "smart chemical storage"
desc = "A refrigerated storage unit for medicine storage."
base_build_path = /obj/machinery/smartfridge/chemistry
- contents_icon_state = "chem"
+ base_icon_state = "chem"
/obj/machinery/smartfridge/chemistry/accept_check(obj/item/weapon)
// not an item or reagent container
@@ -757,7 +757,7 @@
name = "smart virus storage"
desc = "A refrigerated storage unit for volatile sample storage."
base_build_path = /obj/machinery/smartfridge/chemistry/virology
- contents_icon_state = "viro"
+ base_icon_state = "viro"
/obj/machinery/smartfridge/chemistry/virology/preloaded
initial_contents = list(
diff --git a/code/modules/hallucination/delusions.dm b/code/modules/hallucination/delusions.dm
index 2ef224ddf97..106988f7327 100644
--- a/code/modules/hallucination/delusions.dm
+++ b/code/modules/hallucination/delusions.dm
@@ -18,8 +18,13 @@
var/delusion_icon_file
/// The icon state of the delusion image
var/delusion_icon_state
- /// Do we use a generated icon? If yes no icon file or state needed.
- var/dynamic_icon = FALSE
+
+ /// Do we use an appearance/generated icon? If yes no icon file or state needed.
+ var/dynamic_delusion = FALSE
+ /// Appearance to use as a source for our image
+ /// If this exists we'll ignore the icon/state from above
+ var/mutable_appearance/delusion_appearance
+
/// The name of the delusion image
var/delusion_name
@@ -96,7 +101,11 @@
return TRUE
/datum/hallucination/delusion/proc/make_delusion_image(mob/over_who)
- var/image/funny_image = image(delusion_icon_file, over_who, dynamic_icon ? "" : delusion_icon_state)
+ var/image/funny_image
+ if(delusion_appearance)
+ funny_image = image(delusion_appearance, over_who)
+ else
+ funny_image = image(delusion_icon_file, over_who, delusion_icon_state)
funny_image.name = delusion_name
funny_image.override = TRUE
return funny_image
@@ -197,14 +206,14 @@
return funny_image
/datum/hallucination/delusion/preset/syndies
+ dynamic_delusion = TRUE
random_hallucination_weight = 1
- dynamic_icon = TRUE
delusion_name = "Syndicate"
affects_others = TRUE
affects_us = FALSE
/datum/hallucination/delusion/preset/syndies/make_delusion_image(mob/over_who)
- delusion_icon_file = getFlatIcon(get_dynamic_human_appearance(
+ delusion_appearance = get_dynamic_human_appearance(
mob_spawn_path = pick(
/obj/effect/mob_spawn/corpse/human/syndicatesoldier,
/obj/effect/mob_spawn/corpse/human/syndicatecommando,
@@ -217,7 +226,7 @@
/obj/item/gun/ballistic/automatic/c20r,
/obj/item/gun/ballistic/shotgun/bulldog,
),
- ))
+ )
return ..()
@@ -241,18 +250,19 @@
// Hallucination used by heretic paintings
/datum/hallucination/delusion/preset/heretic
+ dynamic_delusion = TRUE
random_hallucination_weight = 0
- dynamic_icon = TRUE
delusion_name = "Heretic"
affects_others = TRUE
affects_us = FALSE
duration = 11 SECONDS
/datum/hallucination/delusion/preset/heretic/make_delusion_image(mob/over_who)
- var/static/icon/heretic_icon
- if(isnull(heretic_icon))
- heretic_icon = getFlatIcon(get_dynamic_human_appearance(/datum/outfit/heretic, r_hand = NO_REPLACE))
- delusion_icon_file = heretic_icon
+ // This code is dummy hot for DUMB reasons so let's not make a mob constantly yeah?
+ var/static/mutable_appearance/heretic_appearance
+ if(isnull(heretic_appearance))
+ heretic_appearance = get_dynamic_human_appearance(/datum/outfit/heretic, r_hand = NO_REPLACE)
+ delusion_appearance = heretic_appearance
return ..()
/datum/hallucination/delusion/preset/heretic/gate
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index ce3f55da9b5..19f592910f2 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -103,7 +103,7 @@
/// List of family heirlooms this job can get with the family heirloom quirk. List of types.
var/list/family_heirlooms
- /// All values = (JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN | JOB_CANNOT_OPEN_SLOTS)
+ /// All values = (JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN | JOB_CANNOT_OPEN_SLOTS | JOB_HEAD_OF_STAFF)
var/job_flags = NONE
/// Multiplier for general usage of the voice of god.
@@ -533,7 +533,7 @@
if(!player_client)
return // Disconnected while checking for the appearance ban.
- var/require_human = CONFIG_GET(flag/enforce_human_authority) && (job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ var/require_human = CONFIG_GET(flag/enforce_human_authority) && (job.job_flags & JOB_HEAD_OF_STAFF)
if(require_human)
var/all_authority_require_human = CONFIG_GET(flag/enforce_human_authority_on_everyone)
if(!all_authority_require_human && job.ignore_human_authority)
diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm
index 954ec3f1726..b5d715ab679 100755
--- a/code/modules/jobs/job_types/captain.dm
+++ b/code/modules/jobs/job_types/captain.dm
@@ -23,6 +23,7 @@
paycheck = PAYCHECK_COMMAND
paycheck_department = ACCOUNT_CMD // NOVA EDIT - Original: paycheck_department = ACCOUNT_SEC
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_ROYAL_METABOLISM)
display_order = JOB_DISPLAY_ORDER_CAPTAIN
@@ -42,7 +43,7 @@
/obj/item/skillchip/sabrage = 5,
)
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
rpg_title = "Star Duke"
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
index 13f05add3ec..97688417aac 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
@@ -20,6 +20,8 @@
var/chaplain_spawnable = TRUE
/// Short description of what this item is capable of, for radial menu uses.
var/menu_description = "A standard chaplain's weapon. Fits in pockets. Can be worn on the belt."
+ /// Lazylist, tracks refs()s to all cultists which have been crit or killed by this nullrod.
+ var/list/cultists_slain
/obj/item/nullrod/Initialize(mapload)
. = ..()
@@ -66,6 +68,27 @@
user.visible_message(span_suicide("[user] is killing [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to get closer to god!"))
return (BRUTELOSS|FIRELOSS)
+/obj/item/nullrod/attack(mob/living/target_mob, mob/living/user, params)
+ if(!user.mind?.holy_role)
+ return ..()
+ if(!IS_CULTIST(target_mob) || istype(target_mob, /mob/living/carbon/human/cult_ghost))
+ return ..()
+
+ var/old_stat = target_mob.stat
+ . = ..()
+ if(old_stat < target_mob.stat)
+ LAZYOR(cultists_slain, REF(target_mob))
+ return .
+
+/obj/item/nullrod/examine(mob/user)
+ . = ..()
+ if(!IS_CULTIST(user) || !GET_ATOM_BLOOD_DNA_LENGTH(src))
+ return
+
+ var/num_slain = LAZYLEN(cultists_slain)
+ . += span_cultitalic("It has the blood of [num_slain] fallen cultist[num_slain == 1 ? "" : "s"] on it. \
+ Offering it to Nar'sie will transform it into a [num_slain >= 3 ? "powerful" : "standard"] cult weapon.")
+
/obj/item/nullrod/godhand
name = "god hand"
desc = "This hand of yours glows with an awesome power!"
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
index 6832e3d66d4..74b1cdcf627 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
@@ -10,7 +10,7 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and
desc = "This shard seems to be directly linked to some sinister entity. It might be your god! It also gives you a really horrible rash when you hold onto it for too long."
items_to_create = list(/obj/item/vorpalscythe)
-/obj/item/organ/internal/cyberimp/arm/shard/scythe/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/internal/cyberimp/arm/shard/scythe/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(receiver.mind)
ADD_TRAIT(receiver.mind, TRAIT_MORBID, ORGAN_TRAIT)
diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm
index fcb26731ca1..fd568464bb6 100644
--- a/code/modules/jobs/job_types/chief_engineer.dm
+++ b/code/modules/jobs/job_types/chief_engineer.dm
@@ -27,6 +27,7 @@
paycheck = PAYCHECK_COMMAND
paycheck_department = ACCOUNT_ENG
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_ENGINEER_METABOLISM, TRAIT_ROYAL_METABOLISM)
display_order = JOB_DISPLAY_ORDER_CHIEF_ENGINEER
@@ -43,7 +44,7 @@
/obj/effect/spawner/random/engineering/tool_advanced = 3
)
rpg_title = "Head Crystallomancer"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm
index 998c19a215d..e20ef7c19ed 100644
--- a/code/modules/jobs/job_types/chief_medical_officer.dm
+++ b/code/modules/jobs/job_types/chief_medical_officer.dm
@@ -27,6 +27,7 @@
paycheck = PAYCHECK_COMMAND
paycheck_department = ACCOUNT_MED
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_MEDICAL_METABOLISM, TRAIT_ROYAL_METABOLISM)
display_order = JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER
@@ -40,7 +41,7 @@
)
family_heirlooms = list(/obj/item/storage/medkit/ancient/heirloom, /obj/item/scalpel, /obj/item/hemostat, /obj/item/circular_saw, /obj/item/retractor, /obj/item/cautery, /obj/item/statuebust/hippocratic)
rpg_title = "High Cleric"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm
index c8a36586292..e863a782d9b 100644
--- a/code/modules/jobs/job_types/head_of_personnel.dm
+++ b/code/modules/jobs/job_types/head_of_personnel.dm
@@ -28,6 +28,7 @@
paycheck_department = ACCOUNT_SRV
bounty_types = CIV_JOB_RANDOM
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_ROYAL_METABOLISM)
display_order = JOB_DISPLAY_ORDER_HEAD_OF_PERSONNEL
@@ -39,7 +40,7 @@
family_heirlooms = list(/obj/item/reagent_containers/cup/glass/trophy/silver_cup)
rpg_title = "Guild Questgiver"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index 825add17d89..afb39bf7c1a 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -24,6 +24,7 @@
/datum/job_department/command,
)
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM, TRAIT_ROYAL_METABOLISM)
paycheck = PAYCHECK_COMMAND
@@ -34,7 +35,7 @@
family_heirlooms = list(/obj/item/book/manual/wiki/security_space_law)
rpg_title = "Guard Leader"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm
index 0ca4b0a1052..858ce8b6455 100644
--- a/code/modules/jobs/job_types/quartermaster.dm
+++ b/code/modules/jobs/job_types/quartermaster.dm
@@ -20,6 +20,7 @@
paycheck = PAYCHECK_COMMAND
paycheck_department = ACCOUNT_CAR
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_ROYAL_METABOLISM) // finally upgraded
display_order = JOB_DISPLAY_ORDER_QUARTERMASTER
@@ -33,7 +34,7 @@
/obj/item/circuitboard/machine/emitter = 3
)
rpg_title = "Steward"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
ignore_human_authority = TRUE
diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm
index f2994388838..1142ba033ff 100644
--- a/code/modules/jobs/job_types/research_director.dm
+++ b/code/modules/jobs/job_types/research_director.dm
@@ -28,6 +28,7 @@
paycheck = PAYCHECK_COMMAND
paycheck_department = ACCOUNT_SCI
+ mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS)
liver_traits = list(TRAIT_ROYAL_METABOLISM, TRAIT_BALLMER_SCIENTIST)
display_order = JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR
@@ -41,7 +42,7 @@
family_heirlooms = list(/obj/item/toy/plush/slimeplushie)
rpg_title = "Archmagister"
- job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
+ job_flags = STATION_JOB_FLAGS | HEAD_OF_STAFF_JOB_FLAGS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/personal_ai.dm b/code/modules/jobs/job_types/spawner/personal_ai.dm
similarity index 100%
rename from code/modules/jobs/job_types/personal_ai.dm
rename to code/modules/jobs/job_types/spawner/personal_ai.dm
diff --git a/code/modules/jobs/job_types/positronic_brain.dm b/code/modules/jobs/job_types/spawner/positronic_brain.dm
similarity index 100%
rename from code/modules/jobs/job_types/positronic_brain.dm
rename to code/modules/jobs/job_types/spawner/positronic_brain.dm
diff --git a/code/modules/jobs/job_types/servant_golem.dm b/code/modules/jobs/job_types/spawner/servant_golem.dm
similarity index 100%
rename from code/modules/jobs/job_types/servant_golem.dm
rename to code/modules/jobs/job_types/spawner/servant_golem.dm
diff --git a/code/modules/library/skill_learning/skillchip.dm b/code/modules/library/skill_learning/skillchip.dm
index a58fbec7c75..2ef7a20e680 100644
--- a/code/modules/library/skill_learning/skillchip.dm
+++ b/code/modules/library/skill_learning/skillchip.dm
@@ -106,7 +106,7 @@
return "Skillchip is not active."
// Should not happen. Holding brain is destroyed and the chip hasn't had its state set appropriately.
- if(QDELETED(holding_brain))
+ if(!holding_brain)
stack_trace("Skillchip's owner is null or qdeleted brain.")
return "Skillchip cannot detect viable brain."
diff --git a/code/modules/mining/equipment/monster_organs/monster_organ.dm b/code/modules/mining/equipment/monster_organs/monster_organ.dm
index 61d795c764a..d8e4bfae986 100644
--- a/code/modules/mining/equipment/monster_organs/monster_organ.dm
+++ b/code/modules/mining/equipment/monster_organs/monster_organ.dm
@@ -69,7 +69,7 @@
deltimer(decay_timer)
return ..()
-/obj/item/organ/internal/monster_core/Insert(mob/living/carbon/target_carbon, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/monster_core/Insert(mob/living/carbon/target_carbon, special = FALSE, movement_flags)
. = ..()
if(!.)
return
@@ -83,7 +83,7 @@
target_carbon.visible_message(span_notice("[src] stabilizes as it's inserted."))
return TRUE
-/obj/item/organ/internal/monster_core/Remove(mob/living/carbon/target_carbon, special = 0)
+/obj/item/organ/internal/monster_core/Remove(mob/living/carbon/target_carbon, special, movement_flags)
if (!inert && !special)
owner.visible_message(span_notice("[src] rapidly decays as it's removed."))
go_inert()
diff --git a/code/modules/mining/equipment/monster_organs/rush_gland.dm b/code/modules/mining/equipment/monster_organs/rush_gland.dm
index 3554d67b2a6..b3932afdaab 100644
--- a/code/modules/mining/equipment/monster_organs/rush_gland.dm
+++ b/code/modules/mining/equipment/monster_organs/rush_gland.dm
@@ -21,11 +21,11 @@
if (owner.health <= HEALTH_DANGER_ZONE)
trigger_organ_action()
-/obj/item/organ/internal/monster_core/rush_gland/on_insert(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/monster_core/rush_gland/on_mob_insert(mob/living/carbon/organ_owner)
. = ..()
RegisterSignal(organ_owner, COMSIG_GOLIATH_TENTACLED_GRABBED, PROC_REF(trigger_organ_action))
-/obj/item/organ/internal/monster_core/rush_gland/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/monster_core/rush_gland/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_GOLIATH_TENTACLED_GRABBED)
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 139218e4572..219d8e357e7 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -485,6 +485,19 @@
DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(execute_quick_equip)))
+/// Safely drop everything, without deconstructing the mob
+/mob/proc/drop_everything(del_on_drop, force, del_if_nodrop)
+ . = list()
+ for(var/obj/item/item in src)
+ if(!dropItemToGround(item, force))
+ if(del_if_nodrop && !(item.item_flags & ABSTRACT))
+ qdel(item)
+ if(del_on_drop)
+ qdel(item)
+ //Anything thats not deleted and isn't in the mob, so everything that is succesfully dropped to the ground, is returned
+ if(!QDELETED(item) && !(item in src))
+ . += item
+
///proc extender of [/mob/verb/quick_equip] used to make the verb queuable if the server is overloaded
/mob/proc/execute_quick_equip()
var/obj/item/I = get_active_held_item()
@@ -505,8 +518,6 @@
/mob/proc/getBeltSlot()
return ITEM_SLOT_BELT
-
-
//Inventory.dm is -kind of- an ok place for this I guess
//This is NOT for dismemberment, as the user still technically has 2 "hands"
diff --git a/code/modules/mob/living/basic/lavaland/legion/legion.dm b/code/modules/mob/living/basic/lavaland/legion/legion.dm
index 080f397226e..3a18a0703e6 100644
--- a/code/modules/mob/living/basic/lavaland/legion/legion.dm
+++ b/code/modules/mob/living/basic/lavaland/legion/legion.dm
@@ -83,7 +83,7 @@
return
// Congratulations you have won a special prize: cancer
var/obj/item/organ/internal/legion_tumour/cancer = new()
- cancer.Insert(consumed, special = TRUE, drop_if_replaced = FALSE)
+ cancer.Insert(consumed, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/// A Legion which only drops skeletons instead of corpses which might have fun loot, so it cannot be farmed
diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm
index 078af57de2a..55c1e6426b3 100644
--- a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm
+++ b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm
@@ -50,7 +50,7 @@
animate(transform = matrix(), time = 0.5 SECONDS / speed_divider, easing = SINE_EASING | EASE_IN)
animate(transform = matrix(), time = 2 SECONDS / speed_divider)
-/obj/item/organ/internal/legion_tumour/Remove(mob/living/carbon/egg_owner, special)
+/obj/item/organ/internal/legion_tumour/Remove(mob/living/carbon/egg_owner, special, movement_flags)
. = ..()
stage = 0
elapsed_time = 0
diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_migration.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_migration.dm
index c7bee5a36e9..27fdb25ee22 100644
--- a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_migration.dm
+++ b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_migration.dm
@@ -10,33 +10,30 @@
/datum/ai_planning_subtree/carp_migration
/datum/ai_planning_subtree/carp_migration/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
- . = ..()
-
// If there's a rift nearby take a ride, then cancel everything else because it's not valid any more
- var/obj/effect/temp_visual/lesser_carp_rift/entrance/rift = locate(/obj/effect/temp_visual/lesser_carp_rift/entrance) in orange(controller.pawn, CARP_PORTAL_SEARCH_RANGE)
- if (rift)
+ for(var/obj/effect/temp_visual/lesser_carp_rift/entrance/rift in orange(controller.pawn, CARP_PORTAL_SEARCH_RANGE))
controller.queue_behavior(/datum/ai_behavior/travel_towards_atom, get_turf(rift))
return SUBTREE_RETURN_FINISH_PLANNING
- var/list/migration_points = controller.blackboard[BB_CARP_MIGRATION_PATH]
- if (!length(migration_points))
- return
-
+ // We have a destination, try to approach it
var/turf/moving_to = controller.blackboard[BB_CARP_MIGRATION_TARGET]
+ if(!isnull(moving_to))
+ var/turf/next_step = get_step_towards(controller.pawn, moving_to)
+ // Attempt to teleport around if we're blocked
+ if(next_step.is_blocked_turf(exclude_mobs = TRUE))
+ controller.queue_behavior(/datum/ai_behavior/make_carp_rift/towards/unvalidated, BB_CARP_RIFT, BB_CARP_MIGRATION_TARGET)
+ controller.queue_behavior(/datum/ai_behavior/attack_obstructions/carp, BB_CARP_MIGRATION_TARGET)
+ controller.queue_behavior(/datum/ai_behavior/step_towards_turf, BB_CARP_MIGRATION_TARGET)
+ // We've gotten close enough to it, clear it so we can select a new point (or do nothing)
+ if(get_dist(controller.pawn, moving_to) <= CARP_DESTINATION_SEARCH_RANGE)
+ controller.clear_blackboard_key(BB_CARP_MIGRATION_TARGET)
+ return SUBTREE_RETURN_FINISH_PLANNING
- // If we don't have a target or are close enough to it, pick a new one
- if (isnull(moving_to) || get_dist(controller.pawn, moving_to) <= CARP_DESTINATION_SEARCH_RANGE)
+ // We have a path to follow but no destination, select one
+ if(length(controller.blackboard[BB_CARP_MIGRATION_PATH]))
controller.queue_behavior(/datum/ai_behavior/find_next_carp_migration_step, BB_CARP_MIGRATION_PATH, BB_CARP_MIGRATION_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
- var/turf/next_step = get_step_towards(controller.pawn, moving_to)
- if (next_step.is_blocked_turf(exclude_mobs = TRUE))
- controller.queue_behavior(/datum/ai_behavior/make_carp_rift/towards/unvalidated, BB_CARP_RIFT, BB_CARP_MIGRATION_TARGET)
- controller.queue_behavior(/datum/ai_behavior/attack_obstructions/carp, BB_CARP_MIGRATION_TARGET)
- controller.queue_behavior(/datum/ai_behavior/step_towards_turf, BB_CARP_MIGRATION_TARGET)
-
- return SUBTREE_RETURN_FINISH_PLANNING
-
/**
* # Find next carp migration step
* Records the next turf we want to travel to into the blackboard for other actions
@@ -45,14 +42,13 @@
/datum/ai_behavior/find_next_carp_migration_step/perform(seconds_per_tick, datum/ai_controller/controller, path_key, target_key)
var/list/blackboard_points = controller.blackboard[path_key]
- var/list/potential_migration_points = blackboard_points.Copy()
- while (length(potential_migration_points))
- var/turf/potential_destination = popleft(potential_migration_points)
- if (!isnull(potential_destination) && get_dist(controller.pawn, potential_destination) > CARP_DESTINATION_SEARCH_RANGE)
- controller.set_blackboard_key(target_key, potential_destination)
+ for(var/turf/migration_point as anything in blackboard_points)
+ // By the end of this loop we will either have a valid migration point set, or an empty list in our blackboard
+ blackboard_points -= migration_point
+ if(get_dist(controller.pawn, migration_point) > CARP_DESTINATION_SEARCH_RANGE)
+ controller.set_blackboard_key(target_key, migration_point)
finish_action(controller, succeeded = TRUE)
return
- controller.set_blackboard_key(path_key, potential_migration_points.Copy())
finish_action(controller, succeeded = FALSE)
diff --git a/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm b/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm
index 8cdd7a6cf53..1cc7549f0ea 100644
--- a/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm
+++ b/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm
@@ -33,13 +33,13 @@
user.temporarilyRemoveItemFromInventory(src, TRUE)
src.Insert(user) //Consuming the heart literally replaces your heart with a demon heart. H A R D C O R E
-/obj/item/organ/internal/heart/demon/on_insert(mob/living/carbon/heart_owner)
+/obj/item/organ/internal/heart/demon/on_mob_insert(mob/living/carbon/heart_owner)
. = ..()
// Gives a non-eat-people crawl to the new owner
var/datum/action/cooldown/spell/jaunt/bloodcrawl/crawl = new(heart_owner)
crawl.Grant(heart_owner)
-/obj/item/organ/internal/heart/demon/on_remove(mob/living/carbon/heart_owner, special = FALSE)
+/obj/item/organ/internal/heart/demon/on_mob_remove(mob/living/carbon/heart_owner, special = FALSE)
. = ..()
var/datum/action/cooldown/spell/jaunt/bloodcrawl/crawl = locate() in heart_owner.actions
qdel(crawl)
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index bc0d2e3d8e8..0818532cc4d 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -286,7 +286,7 @@
return /datum/reagent/blood
/mob/living/carbon/human/get_blood_id()
- if(HAS_TRAIT(src, TRAIT_HUSK))
+ if(HAS_TRAIT(src, TRAIT_HUSK) || !dna)
return
if(check_holidays(APRIL_FOOLS) && is_clown_job(mind?.assigned_role))
return /datum/reagent/colorful_reagent
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index ab870461965..a613e2ad839 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -46,7 +46,14 @@
// Brain size logic
transform = transform.Scale(brain_size)
-/obj/item/organ/internal/brain/Insert(mob/living/carbon/brain_owner, special = FALSE, drop_if_replaced = TRUE, no_id_transfer = FALSE)
+/obj/item/organ/internal/brain/examine()
+ . = ..()
+ if(brain_size < 1)
+ . += span_notice("It is a bit on the smaller side...")
+ if(brain_size > 1)
+ . += span_notice("It is bigger than average...")
+
+/obj/item/organ/internal/brain/mob_insert(mob/living/carbon/brain_owner, special = FALSE, movement_flags)
. = ..()
if(!.)
return
@@ -54,7 +61,7 @@
name = initial(name)
// Special check for if you're trapped in a body you can't control because it's owned by a ling.
- if(brain_owner?.mind?.has_antag_datum(/datum/antagonist/changeling) && !no_id_transfer)
+ if(brain_owner?.mind?.has_antag_datum(/datum/antagonist/changeling) && !(movement_flags & NO_ID_TRANSFER))
if(brainmob && !(brain_owner.stat == DEAD || (HAS_TRAIT(brain_owner, TRAIT_DEATHCOMA))))
to_chat(brainmob, span_danger("You can't feel your body! You're still just a brain!"))
forceMove(brain_owner)
@@ -101,25 +108,11 @@
//Update the body's icon so it doesnt appear debrained anymore
brain_owner.update_body_parts()
-/obj/item/organ/internal/brain/on_insert(mob/living/carbon/organ_owner, special)
- // Are we inserting into a new mob from a head?
- // If yes, we want to quickly steal the brainmob from the head before we do anything else.
- // This is usually stuff like reattaching dismembered/amputated heads.
- if(istype(loc, /obj/item/bodypart/head))
- var/obj/item/bodypart/head/brain_holder = loc
- if(brain_holder.brainmob)
- brainmob = brain_holder.brainmob
- brain_holder.brainmob = null
- brainmob.container = null
- brainmob.forceMove(src)
-
- return ..()
-
-/obj/item/organ/internal/brain/Remove(mob/living/carbon/brain_owner, special = 0, no_id_transfer = FALSE)
+/obj/item/organ/internal/brain/mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
// Delete skillchips first as parent proc sets owner to null, and skillchips need to know the brain's owner.
- if(!QDELETED(brain_owner) && length(skillchips))
+ if(!QDELETED(organ_owner) && length(skillchips))
if(!special)
- to_chat(brain_owner, span_notice("You feel your skillchips enable emergency power saving mode, deactivating as your brain leaves your body..."))
+ to_chat(organ_owner, span_notice("You feel your skillchips enable emergency power saving mode, deactivating as your brain leaves your body..."))
for(var/chip in skillchips)
var/obj/item/skillchip/skillchip = chip
// Run the try_ proc with force = TRUE.
@@ -132,10 +125,11 @@
BT.on_lose(TRUE)
BT.owner = null
- if((!gc_destroyed || (owner && !owner.gc_destroyed)) && !no_id_transfer)
- transfer_identity(brain_owner)
- brain_owner.update_body_parts()
- brain_owner.clear_mood_event("brain_damage")
+ if((!gc_destroyed || (owner && !owner.gc_destroyed)) && !(movement_flags & NO_ID_TRANSFER))
+ transfer_identity(organ_owner)
+ if(!special)
+ organ_owner.update_body_parts()
+ organ_owner.clear_mood_event("brain_damage")
/obj/item/organ/internal/brain/proc/transfer_identity(mob/living/L)
name = "[L.name]'s [initial(name)]"
@@ -416,11 +410,11 @@
icon_state = "random_fly_4"
organ_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_LITERATE, TRAIT_CAN_STRIP)
-/obj/item/organ/internal/brain/lustrous/before_organ_replacement(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/brain/lustrous/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
organ_owner.cure_trauma_type(/datum/brain_trauma/special/bluespace_prophet, TRAUMA_RESILIENCE_ABSOLUTE)
-/obj/item/organ/internal/brain/lustrous/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/brain/lustrous/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
organ_owner.gain_trauma(/datum/brain_trauma/special/bluespace_prophet, TRAUMA_RESILIENCE_ABSOLUTE)
@@ -579,6 +573,6 @@
/// Brains REALLY like ghosting people. we need special tricks to avoid that, namely removing the old brain with no_id_transfer
/obj/item/organ/internal/brain/replace_into(mob/living/carbon/new_owner)
var/obj/item/organ/internal/brain/old_brain = new_owner.get_organ_slot(ORGAN_SLOT_BRAIN)
- old_brain.Remove(new_owner, special = TRUE, no_id_transfer = TRUE)
+ old_brain.Remove(new_owner, special = TRUE, movement_flags = NO_ID_TRANSFER)
qdel(old_brain)
- return Insert(new_owner, special = TRUE, drop_if_replaced = FALSE, no_id_transfer = TRUE)
+ return Insert(new_owner, special = TRUE, movement_flags = NO_ID_TRANSFER | DELETE_IF_REPLACED)
diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm
index a29b126dd8d..66f555b639a 100644
--- a/code/modules/mob/living/carbon/alien/organs.dm
+++ b/code/modules/mob/living/carbon/alien/organs.dm
@@ -69,14 +69,14 @@
else
owner.adjustPlasma(0.1 * plasma_rate * delta_time)
-/obj/item/organ/internal/alien/plasmavessel/on_insert(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/alien/plasmavessel/on_mob_insert(mob/living/carbon/organ_owner)
. = ..()
if(isalien(organ_owner))
var/mob/living/carbon/alien/target_alien = organ_owner
target_alien.updatePlasmaDisplay()
RegisterSignal(organ_owner, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(get_status_tab_item))
-/obj/item/organ/internal/alien/plasmavessel/on_remove(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/alien/plasmavessel/on_mob_remove(mob/living/carbon/organ_owner)
. = ..()
if(isalien(organ_owner))
var/mob/living/carbon/alien/organ_owner_alien = organ_owner
@@ -95,18 +95,18 @@
zone = BODY_ZONE_HEAD
slot = ORGAN_SLOT_XENO_HIVENODE
w_class = WEIGHT_CLASS_TINY
+ organ_traits = list(TRAIT_XENO_IMMUNE)
actions_types = list(/datum/action/cooldown/alien/whisper)
/// Indicates if the queen died recently, aliens are heavily weakened while this is active.
var/recent_queen_death = FALSE
-/obj/item/organ/internal/alien/hivenode/on_insert(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/alien/hivenode/on_mob_insert(mob/living/carbon/organ_owner)
. = ..()
organ_owner.faction |= ROLE_ALIEN
- ADD_TRAIT(organ_owner, TRAIT_XENO_IMMUNE, ORGAN_TRAIT)
-/obj/item/organ/internal/alien/hivenode/Remove(mob/living/carbon/organ_owner, special = FALSE)
- organ_owner.faction -= ROLE_ALIEN
- REMOVE_TRAIT(organ_owner, TRAIT_XENO_IMMUNE, ORGAN_TRAIT)
+/obj/item/organ/internal/alien/hivenode/on_mob_remove(mob/living/carbon/organ_owner, special = FALSE)
+ if(organ_owner)
+ organ_owner.faction -= ROLE_ALIEN
return ..()
//When the alien queen dies, all aliens suffer a penalty as punishment for failing to protect her.
@@ -232,11 +232,11 @@
stomach_contents -= source
UnregisterSignal(source, list(COMSIG_MOVABLE_MOVED, COMSIG_LIVING_DEATH, COMSIG_QDELETING))
-/obj/item/organ/internal/stomach/alien/Insert(mob/living/carbon/stomach_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/stomach/alien/Insert(mob/living/carbon/stomach_owner, special, movement_flags)
RegisterSignal(stomach_owner, COMSIG_ATOM_RELAYMOVE, PROC_REF(something_moved))
return ..()
-/obj/item/organ/internal/stomach/alien/Remove(mob/living/carbon/stomach_owner, special = FALSE)
+/obj/item/organ/internal/stomach/alien/Remove(mob/living/carbon/stomach_owner, special, movement_flags)
UnregisterSignal(stomach_owner, COMSIG_ATOM_RELAYMOVE)
return ..()
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index fb4828e457f..65a40a08805 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -1007,7 +1007,6 @@
for(var/obj/item/bodypart/bodypart_path as anything in bodyparts_paths)
var/real_body_part_path = overrides?[initial(bodypart_path.body_zone)] || bodypart_path
var/obj/item/bodypart/bodypart_instance = new real_body_part_path()
- bodypart_instance.set_owner(src)
add_bodypart(bodypart_instance)
/// Called when a new hand is added
@@ -1024,8 +1023,12 @@
/mob/living/carbon/proc/add_bodypart(obj/item/bodypart/new_bodypart)
SHOULD_NOT_OVERRIDE(TRUE)
+ new_bodypart.on_adding(src)
bodyparts += new_bodypart
- new_bodypart.set_owner(src)
+ new_bodypart.update_owner(src)
+
+ for(var/obj/item/organ/organ in new_bodypart)
+ organ.mob_insert(src)
switch(new_bodypart.body_part)
if(LEG_LEFT, LEG_RIGHT)
@@ -1040,10 +1043,17 @@
synchronize_bodytypes()
///Proc to hook behavior on bodypart removals. Do not directly call. You're looking for [/obj/item/bodypart/proc/drop_limb()].
-/mob/living/carbon/proc/remove_bodypart(obj/item/bodypart/old_bodypart)
+/mob/living/carbon/proc/remove_bodypart(obj/item/bodypart/old_bodypart, special)
SHOULD_NOT_OVERRIDE(TRUE)
- old_bodypart.on_removal()
+ if(special)
+ for(var/obj/item/organ/organ in old_bodypart)
+ organ.bodypart_remove(limb_owner = src, movement_flags = NO_ID_TRANSFER)
+ else
+ for(var/obj/item/organ/organ in old_bodypart)
+ organ.mob_remove(src, special)
+
+ old_bodypart.on_removal(src)
bodyparts -= old_bodypart
switch(old_bodypart.body_part)
@@ -1449,3 +1459,8 @@
our_splatter.blood_dna_info = get_blood_dna_list()
var/turf/targ = get_ranged_target_turf(src, splatter_direction, splatter_strength)
our_splatter.fly_towards(targ, splatter_strength)
+
+/mob/living/carbon/dropItemToGround(obj/item/item, force = FALSE, silent = FALSE, invdrop = TRUE)
+ if(item && ((item in organs) || (item in bodyparts))) //let's not do this, aight?
+ return FALSE
+ return ..()
diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm
index 78b8554361b..85a6b06a2d3 100644
--- a/code/modules/mob/living/carbon/death.dm
+++ b/code/modules/mob/living/carbon/death.dm
@@ -28,9 +28,9 @@
add_memory_in_range(src, 7, /datum/memory/witness_gib, protagonist = src)
if(drop_bitflags & DROP_ITEMS)
for(var/obj/item/W in src)
- dropItemToGround(W)
- if(prob(50))
- step(W, pick(GLOB.alldirs))
+ if(dropItemToGround(W))
+ if(prob(50))
+ step(W, pick(GLOB.alldirs))
var/atom/Tsec = drop_location()
for(var/mob/M in src)
M.forceMove(Tsec)
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index 7c41119b0c2..0de67ef055d 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -375,19 +375,17 @@ GLOBAL_LIST_EMPTY(features_by_species)
health_pct = (existing_organ.maxHealth - existing_organ.damage) / existing_organ.maxHealth
if(slot == ORGAN_SLOT_BRAIN)
var/obj/item/organ/internal/brain/existing_brain = existing_organ
- if(!existing_brain.decoy_override)
- existing_brain.before_organ_replacement(new_organ)
- existing_brain.Remove(organ_holder, special = TRUE, no_id_transfer = TRUE)
- QDEL_NULL(existing_organ)
+ existing_brain.before_organ_replacement(new_organ)
+ existing_brain.Remove(organ_holder, special = TRUE, movement_flags = NO_ID_TRANSFER)
else
existing_organ.before_organ_replacement(new_organ)
existing_organ.Remove(organ_holder, special = TRUE)
- QDEL_NULL(existing_organ)
- if(isnull(existing_organ) && should_have && !(new_organ.zone in excluded_zones))
+ QDEL_NULL(existing_organ)
+ if(isnull(existing_organ) && should_have && !(new_organ.zone in excluded_zones) && organ_holder.get_bodypart(deprecise_zone(new_organ.zone)))
used_neworgan = TRUE
new_organ.set_organ_damage(new_organ.maxHealth * (1 - health_pct))
- new_organ.Insert(organ_holder, special = TRUE, drop_if_replaced = FALSE)
+ new_organ.Insert(organ_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
if(!used_neworgan)
QDEL_NULL(new_organ)
@@ -430,7 +428,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(current_organ)
current_organ.before_organ_replacement(replacement)
// organ.Insert will qdel any current organs in that slot, so we don't need to.
- replacement.Insert(organ_holder, special=TRUE, drop_if_replaced=FALSE)
+ replacement.Insert(organ_holder, special=TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/species/proc/worn_items_fit_body_check(mob/living/carbon/wearer)
for(var/obj/item/equipped_item in wearer.get_all_worn_items())
@@ -497,7 +495,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
//Load a persons preferences from DNA
var/obj/item/organ/external/new_organ = SSwardrobe.provide_type(organ_path)
- new_organ.Insert(human, special=TRUE, drop_if_replaced=FALSE)
+ new_organ.Insert(human, special=TRUE, movement_flags = DELETE_IF_REPLACED)
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index d2f3576b6a9..5dffbcd26c0 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -13,6 +13,7 @@
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
can_be_shoved_into = TRUE
initial_language_holder = /datum/language_holder/empty // We get stuff from our species
+ flags_1 = PREVENT_CONTENTS_EXPLOSION_1
maxHealth = HUMAN_MAXHEALTH //NOVA EDIT ADDITION
health = HUMAN_MAXHEALTH //NOVA EDIT ADDITION
diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm
index 1db30b11dbe..2ec02dee2b4 100644
--- a/code/modules/mob/living/carbon/human/species_types/felinid.dm
+++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm
@@ -43,7 +43,7 @@
target_human.dna.features["ears"] = "Cat"
if(target_human.dna.features["ears"] == "Cat")
var/obj/item/organ/internal/ears/cat/ears = new
- ears.Insert(target_human, drop_if_replaced = FALSE)
+ ears.Insert(target_human, movement_flags = DELETE_IF_REPLACED)
else
mutantears = /obj/item/organ/internal/ears
return ..()
@@ -95,8 +95,8 @@
// Humans get converted directly to felinids, and the key is handled in on_species_gain.
// Now when we get mob.dna.features[feature_key], it returns None, which is why the tail is invisible.
// stored_feature_id is only set once (the first time an organ is inserted), so this should be safe.
- kitty_ears.Insert(soon_to_be_felinid, special = TRUE, drop_if_replaced = FALSE)
- kitty_tail.Insert(soon_to_be_felinid, special = TRUE, drop_if_replaced = FALSE)
+ kitty_ears.Insert(soon_to_be_felinid, special = TRUE, movement_flags = DELETE_IF_REPLACED)
+ kitty_tail.Insert(soon_to_be_felinid, special = TRUE, movement_flags = DELETE_IF_REPLACED)
if(!silent)
to_chat(soon_to_be_felinid, span_boldnotice("Something is nya~t right."))
playsound(get_turf(soon_to_be_felinid), 'sound/effects/meow1.ogg', 50, TRUE, -1)
@@ -119,16 +119,16 @@
for(var/external_organ in target_species.external_organs)
if(ispath(external_organ, /obj/item/organ/external/tail))
var/obj/item/organ/external/tail/new_tail = new external_organ()
- new_tail.Insert(purrbated_human, special = TRUE, drop_if_replaced = FALSE)
+ new_tail.Insert(purrbated_human, special = TRUE, movement_flags = DELETE_IF_REPLACED)
// Don't forget the spines we removed earlier
else if(ispath(external_organ, /obj/item/organ/external/spines))
var/obj/item/organ/external/spines/new_spines = new external_organ()
- new_spines.Insert(purrbated_human, special = TRUE, drop_if_replaced = FALSE)
+ new_spines.Insert(purrbated_human, special = TRUE, movement_flags = DELETE_IF_REPLACED)
var/obj/item/organ/internal/ears/old_ears = purrbated_human.get_organ_slot(ORGAN_SLOT_EARS)
if(istype(old_ears, /obj/item/organ/internal/ears/cat))
var/obj/item/organ/new_ears = new target_species.mutantears()
- new_ears.Insert(purrbated_human, special = TRUE, drop_if_replaced = FALSE)
+ new_ears.Insert(purrbated_human, special = TRUE, movement_flags = DELETE_IF_REPLACED)
if(!silent)
to_chat(purrbated_human, span_boldnotice("You are no longer a cat."))
diff --git a/code/modules/mob/living/carbon/human/species_types/monkeys.dm b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
index 00003a5240b..3c176ff4388 100644
--- a/code/modules/mob/living/carbon/human/species_types/monkeys.dm
+++ b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
@@ -167,11 +167,11 @@
build_all_button_icons()
-/obj/item/organ/internal/brain/primate/on_insert(mob/living/carbon/primate)
+/obj/item/organ/internal/brain/primate/on_mob_insert(mob/living/carbon/primate)
. = ..()
RegisterSignal(primate, COMSIG_MOVABLE_CROSS, PROC_REF(on_crossed), TRUE)
-/obj/item/organ/internal/brain/primate/on_remove(mob/living/carbon/primate)
+/obj/item/organ/internal/brain/primate/on_mob_remove(mob/living/carbon/primate)
. = ..()
UnregisterSignal(primate, COMSIG_MOVABLE_CROSS)
diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm
index 6e56b13055b..427f1f5f71b 100644
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm
@@ -203,11 +203,11 @@
name = "vampire heart"
color = "#1C1C1C"
-/obj/item/organ/internal/heart/vampire/on_insert(mob/living/carbon/receiver)
+/obj/item/organ/internal/heart/vampire/on_mob_insert(mob/living/carbon/receiver)
. = ..()
RegisterSignal(receiver, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(get_status_tab_item))
-/obj/item/organ/internal/heart/vampire/on_remove(mob/living/carbon/heartless)
+/obj/item/organ/internal/heart/vampire/on_mob_remove(mob/living/carbon/heartless)
. = ..()
UnregisterSignal(heartless, COMSIG_MOB_GET_STATUS_TAB_ITEMS)
diff --git a/code/modules/mob/mob_lists.dm b/code/modules/mob/mob_lists.dm
index 75904737c3f..9fd097a1fd6 100644
--- a/code/modules/mob/mob_lists.dm
+++ b/code/modules/mob/mob_lists.dm
@@ -53,8 +53,6 @@
GLOB.keyloop_list |= src
else if(stat != DEAD || !SSlag_switch?.measures[DISABLE_DEAD_KEYLOOP])
GLOB.keyloop_list |= src
- if(!SSticker.HasRoundStarted())
- return
if(stat == DEAD)
add_to_current_dead_players()
else
@@ -65,8 +63,6 @@
SHOULD_CALL_PARENT(TRUE)
GLOB.player_list -= src
GLOB.keyloop_list -= src
- if(!SSticker.HasRoundStarted())
- return
if(stat == DEAD)
remove_from_current_dead_players()
else
@@ -75,13 +71,9 @@
///Adds the cliented mob reference to either the list of dead player-mobs or to the list of observers, depending on how they joined the game.
/mob/proc/add_to_current_dead_players()
- if(!SSticker.HasRoundStarted())
- return
GLOB.dead_player_list |= src
/mob/dead/observer/add_to_current_dead_players()
- if(!SSticker.HasRoundStarted())
- return
if(started_as_observer)
GLOB.current_observers_list |= src
return
@@ -92,13 +84,9 @@
///Removes the mob reference from either the list of dead player-mobs or from the list of observers, depending on how they joined the game.
/mob/proc/remove_from_current_dead_players()
- if(!SSticker.HasRoundStarted())
- return
GLOB.dead_player_list -= src
/mob/dead/observer/remove_from_current_dead_players()
- if(!SSticker.HasRoundStarted())
- return
if(started_as_observer)
GLOB.current_observers_list -= src
return
@@ -107,16 +95,12 @@
///Adds the cliented mob reference to the list of living player-mobs. If the mob is an antag, it adds it to the list of living antag player-mobs.
/mob/proc/add_to_current_living_players()
- if(!SSticker.HasRoundStarted())
- return
GLOB.alive_player_list |= src
if(mind && (mind.special_role || length(mind.antag_datums)))
add_to_current_living_antags()
///Removes the mob reference from the list of living player-mobs. If the mob is an antag, it removes it from the list of living antag player-mobs.
/mob/proc/remove_from_current_living_players()
- if(!SSticker.HasRoundStarted())
- return
GLOB.alive_player_list -= src
if(LAZYLEN(mind?.antag_datums))
remove_from_current_living_antags()
@@ -124,9 +108,6 @@
///Adds the cliented mob reference to the list of living antag player-mobs.
/mob/proc/add_to_current_living_antags()
- if(!SSticker.HasRoundStarted())
- return
-
if (length(mind.antag_datums) == 0)
return
@@ -137,6 +118,4 @@
///Removes the mob reference from the list of living antag player-mobs.
/mob/proc/remove_from_current_living_antags()
- if(!SSticker.HasRoundStarted())
- return
GLOB.current_living_antags -= src
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 068c6c98366..ca82e13e803 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -127,8 +127,6 @@
/mob/living/carbon/human/AIize(client/preference_source, transfer_after = TRUE)
if(HAS_TRAIT(src, TRAIT_NO_TRANSFORM))
return
- for(var/t in bodyparts)
- qdel(t)
return ..()
@@ -173,11 +171,8 @@
ADD_TRAIT(src, TRAIT_NO_TRANSFORM, TEMPORARY_TRANSFORMATION_TRAIT)
Paralyze(1, ignore_canstun = TRUE)
- for(var/obj/item/W in src)
- if(delete_items)
- qdel(W)
- else
- dropItemToGround(W)
+ drop_everything(delete_items)
+
regenerate_icons()
icon = null
SetInvisibility(INVISIBILITY_MAXIMUM)
diff --git a/code/modules/mob_spawn/corpses/mining_corpses.dm b/code/modules/mob_spawn/corpses/mining_corpses.dm
index 8b7ad474b16..972bb5c3fa5 100644
--- a/code/modules/mob_spawn/corpses/mining_corpses.dm
+++ b/code/modules/mob_spawn/corpses/mining_corpses.dm
@@ -27,7 +27,7 @@
/obj/effect/mob_spawn/corpse/human/legioninfested/special(mob/living/carbon/human/spawned_human)
. = ..()
var/obj/item/organ/internal/legion_tumour/cancer = new()
- cancer.Insert(spawned_human, special = TRUE, drop_if_replaced = FALSE)
+ cancer.Insert(spawned_human, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/// Returns the outfit worn by our corpse
/obj/effect/mob_spawn/corpse/human/legioninfested/proc/select_outfit()
diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm
index d4b4239b9cb..d240dd64c22 100644
--- a/code/modules/projectiles/guns/ballistic.dm
+++ b/code/modules/projectiles/guns/ballistic.dm
@@ -429,10 +429,9 @@
return TRUE
/obj/item/gun/ballistic/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
- if(magazine && chambered.loaded_projectile && can_misfire && misfire_probability > 0)
- if(prob(misfire_probability))
- if(blow_up(user))
- to_chat(user, span_userdanger("[src] misfires!"))
+ if(target != user && chambered.loaded_projectile && can_misfire && prob(misfire_probability) && blow_up(user))
+ to_chat(user, span_userdanger("[src] misfires!"))
+ return
if (sawn_off)
bonus_spread += SAWN_OFF_ACC_PENALTY
@@ -700,11 +699,7 @@ GLOBAL_LIST_INIT(gun_saw_types, typecacheof(list(
///used for sawing guns, causes the gun to fire without the input of the user
/obj/item/gun/ballistic/proc/blow_up(mob/user)
- . = FALSE
- for(var/obj/item/ammo_casing/AC in magazine.stored_ammo)
- if(AC.loaded_projectile)
- process_fire(user, user, FALSE)
- . = TRUE
+ return chambered && process_fire(user, user, FALSE)
/obj/item/gun/ballistic/proc/instant_reload()
SIGNAL_HANDLER
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 4fd6e42d12f..ea069d51fc5 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -2849,7 +2849,7 @@
/datum/reagent/eldritch/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired)
. = ..()
var/need_mob_update = FALSE
- if(IS_HERETIC(drinker))
+ if(IS_HERETIC_OR_MONSTER(drinker))
drinker.adjust_drowsiness(-10 * REM * seconds_per_tick)
drinker.AdjustAllImmobility(-40 * REM * seconds_per_tick)
need_mob_update += drinker.adjustStaminaLoss(-10 * REM * seconds_per_tick, updating_stamina = FALSE)
diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
index 76cae5ce1ad..83754dd571f 100644
--- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm
+++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
@@ -601,9 +601,11 @@
if(!do_after(user, 2 SECONDS, src)) //takes longer because you are supposed to take the foil off the bottle first
return
- ///The bonus to success chance that the user gets for being a command role
- var/command_bonus = user.mind?.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND ? 20 : 0
- ///The bonus to success chance that the user gets for having a sabrage skillchip installed/otherwise having the trait through other means
+ //The bonus to success chance that the user gets for being a command role
+ var/obj/item/organ/internal/liver/liver = user.get_organ_slot(ORGAN_SLOT_LIVER)
+ var/command_bonus = (!isnull(liver) && HAS_TRAIT(liver, TRAIT_ROYAL_METABOLISM)) ? 20 : 0
+
+ //The bonus to success chance that the user gets for having a sabrage skillchip installed/otherwise having the trait through other means
var/skillchip_bonus = HAS_TRAIT(user, TRAIT_SABRAGE_PRO) ? 35 : 0
//calculate success chance. example: captain's sabre - 15 force = 75% chance
var/sabrage_chance = (attacking_item.force * sabrage_success_percentile) + command_bonus + skillchip_bonus
diff --git a/code/modules/religion/burdened/psyker.dm b/code/modules/religion/burdened/psyker.dm
index 5e9b0f0327b..f43e33aa25a 100644
--- a/code/modules/religion/burdened/psyker.dm
+++ b/code/modules/religion/burdened/psyker.dm
@@ -10,12 +10,12 @@
organ_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_LITERATE, TRAIT_CAN_STRIP, TRAIT_ANTIMAGIC_NO_SELFBLOCK)
w_class = WEIGHT_CLASS_NORMAL
-/obj/item/organ/internal/brain/psyker/on_insert(mob/living/carbon/inserted_into)
+/obj/item/organ/internal/brain/psyker/on_mob_insert(mob/living/carbon/inserted_into)
. = ..()
inserted_into.AddComponent(/datum/component/echolocation, blocking_trait = TRAIT_DUMB, echo_group = "psyker", echo_icon = "psyker", color_path = /datum/client_colour/psyker)
inserted_into.AddComponent(/datum/component/anti_magic, antimagic_flags = MAGIC_RESISTANCE_MIND)
-/obj/item/organ/internal/brain/psyker/on_remove(mob/living/carbon/removed_from)
+/obj/item/organ/internal/brain/psyker/on_mob_remove(mob/living/carbon/removed_from)
. = ..()
qdel(removed_from.GetComponent(/datum/component/echolocation))
qdel(removed_from.GetComponent(/datum/component/anti_magic))
@@ -82,9 +82,9 @@
qdel(old_head)
var/obj/item/organ/internal/brain/psyker/psyker_brain = new()
old_brain.before_organ_replacement(psyker_brain)
- old_brain.Remove(src, special = TRUE, no_id_transfer = TRUE)
+ old_brain.Remove(src, special = TRUE, movement_flags = NO_ID_TRANSFER)
qdel(old_brain)
- psyker_brain.Insert(src, special = TRUE, drop_if_replaced = FALSE)
+ psyker_brain.Insert(src, special = TRUE, movement_flags = DELETE_IF_REPLACED)
if(old_eyes)
qdel(old_eyes)
return TRUE
diff --git a/code/modules/spells/spell_types/self/summonitem.dm b/code/modules/spells/spell_types/self/summonitem.dm
index 62d6b078161..ab99f35271d 100644
--- a/code/modules/spells/spell_types/self/summonitem.dm
+++ b/code/modules/spells/spell_types/self/summonitem.dm
@@ -138,7 +138,6 @@
item_to_retrieve = null
break
- SEND_SIGNAL(holding_mark, COMSIG_MAGIC_RECALL, caster, item_to_retrieve)
holding_mark.dropItemToGround(item_to_retrieve)
else if(isobj(item_to_retrieve.loc))
@@ -157,15 +156,6 @@
infinite_recursion += 1
- else
- // Organs are usually stored in nullspace
- if(isorgan(item_to_retrieve))
- var/obj/item/organ/organ = item_to_retrieve
- if(organ.owner)
- // If this code ever runs I will be happy
- log_combat(caster, organ.owner, "magically removed [organ.name] from", addition = "COMBAT MODE: [uppertext(caster.combat_mode)]")
- organ.Remove(organ.owner)
-
if(!item_to_retrieve)
return
@@ -176,6 +166,8 @@
else
item_to_retrieve.forceMove(caster.drop_location())
item_to_retrieve.loc.visible_message(span_warning("[item_to_retrieve] suddenly appears!"))
+
+ SEND_SIGNAL(item_to_retrieve, COMSIG_MAGIC_RECALL, caster, item_to_retrieve)
playsound(get_turf(item_to_retrieve), 'sound/magic/summonitems_generic.ogg', 50, TRUE)
/datum/action/cooldown/spell/summonitem/abductor
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index fdeb50114a3..e51d083ddc4 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -6,6 +6,7 @@
w_class = WEIGHT_CLASS_SMALL
icon = 'icons/mob/human/bodyparts.dmi'
icon_state = "" //Leave this blank! Bodyparts are built using overlays
+ flags_1 = PREVENT_CONTENTS_EXPLOSION_1 //actually mindblowing
/// The icon for Organic limbs using greyscale
VAR_PROTECTED/icon_greyscale = DEFAULT_BODYPART_ICON_ORGANIC
///The icon for non-greyscale limbs
@@ -19,7 +20,7 @@
layer = BELOW_MOB_LAYER //so it isn't hidden behind objects when on the floor
grind_results = list(/datum/reagent/bone_dust = 10, /datum/reagent/consumable/liquidgibs = 5) // robotic bodyparts and chests/heads cannot be ground
/// The mob that "owns" this limb
- /// DO NOT MODIFY DIRECTLY. Use set_owner()
+ /// DO NOT MODIFY DIRECTLY. Use update_owner()
var/mob/living/carbon/owner
/// If this limb can be scarred.
@@ -155,9 +156,6 @@
/// If something is currently grasping this bodypart and trying to staunch bleeding (see [/obj/item/hand_item/self_grasp])
var/obj/item/hand_item/self_grasp/grasped_by
- ///A list of all the external organs we've got stored to draw horns, wings and stuff with (special because we are actually in the limbs unlike normal organs :/ )
- ///If someone ever comes around to making all organs exist in the bodyparts, you can just remove this and use a typed loop
- var/list/obj/item/organ/external/external_organs = list()
///A list of all bodypart overlays to draw
var/list/bodypart_overlays = list()
@@ -232,31 +230,40 @@
refresh_bleed_rate()
/obj/item/bodypart/Destroy()
- if(owner)
- owner.remove_bodypart(src)
- set_owner(null)
+ if(owner && !QDELETED(owner))
+ forced_removal(special = FALSE, dismembered = TRUE, move_to_floor = FALSE)
+ update_owner(null)
for(var/wound in wounds)
qdel(wound) // wounds is a lazylist, and each wound removes itself from it on deletion.
if(length(wounds))
stack_trace("[type] qdeleted with [length(wounds)] uncleared wounds")
wounds.Cut()
- if(length(external_organs))
- for(var/obj/item/organ/external/external_organ as anything in external_organs)
- external_organs -= external_organ
- qdel(external_organ) // It handles removing its references to this limb on its own.
+ owner = null
+
+ for(var/atom/movable/movable in contents)
+ qdel(movable)
- external_organs = list()
QDEL_LIST_ASSOC_VAL(feature_offsets)
return ..()
-/obj/item/bodypart/forceMove(atom/destination) //Please. Never forcemove a limb if its's actually in use. This is only for borgs.
- SHOULD_CALL_PARENT(TRUE)
+/obj/item/bodypart/ex_act(severity, target)
+ if(owner) //trust me bro you dont want this
+ return FALSE
+ return ..()
- . = ..()
- if(isturf(destination))
- update_icon_dropped()
+
+/obj/item/bodypart/proc/on_forced_removal(atom/old_loc, dir, forced, list/old_locs)
+ SIGNAL_HANDLER
+
+ forced_removal(special = FALSE, dismembered = TRUE, move_to_floor = FALSE)
+
+/// In-case someone, somehow only teleports someones limb
+/obj/item/bodypart/proc/forced_removal(special, dismembered, move_to_floor)
+ drop_limb(special, dismembered, move_to_floor)
+
+ update_icon_dropped()
/obj/item/bodypart/examine(mob/user)
SHOULD_CALL_PARENT(TRUE)
@@ -407,31 +414,24 @@
var/atom/drop_loc = drop_location()
if(IS_ORGANIC_LIMB(src))
playsound(drop_loc, 'sound/misc/splort.ogg', 50, TRUE, -1)
- QDEL_NULL(current_gauze)
- for(var/obj/item/organ/bodypart_organ as anything in get_organs())
- bodypart_organ.transfer_to_limb(src, owner)
- for(var/obj/item/organ/external/external as anything in external_organs)
- external.remove_from_limb()
- external.forceMove(drop_loc)
- for(var/obj/item/item_in_bodypart in src)
- item_in_bodypart.forceMove(drop_loc)
-
- update_icon_dropped()
-///since organs aren't actually stored in the bodypart themselves while attached to a person, we have to query the owner for what we should have
-/obj/item/bodypart/proc/get_organs()
- SHOULD_CALL_PARENT(TRUE)
- RETURN_TYPE(/list)
+ QDEL_NULL(current_gauze)
- if(!owner)
- return FALSE
+ for(var/obj/item/organ/bodypart_organ in contents)
+ if(bodypart_organ.organ_flags & ORGAN_UNREMOVABLE)
+ continue
+ if(owner)
+ bodypart_organ.Remove(bodypart_organ.owner)
+ else
+ if(bodypart_organ.bodypart_remove(src))
+ if(drop_loc) //can be null if being deleted
+ bodypart_organ.forceMove(get_turf(drop_loc))
- var/list/bodypart_organs
- for(var/obj/item/organ/organ_check as anything in owner.organs) //internal organs inside the dismembered limb are dropped.
- if(check_zone(organ_check.zone) == body_zone)
- LAZYADD(bodypart_organs, organ_check) // this way if we don't have any, it'll just return null
+ if(drop_loc) //can be null during deletion
+ for(var/atom/movable/movable as anything in src)
+ movable.forceMove(drop_loc)
- return bodypart_organs
+ update_icon_dropped()
//Return TRUE to get whatever mob this is in to update health.
/obj/item/bodypart/proc/on_life(seconds_per_tick, times_fired)
@@ -760,70 +760,91 @@
owner.update_health_hud() //update the healthdoll
owner.update_body()
-///Proc to change the value of the `owner` variable and react to the event of its change.
-/obj/item/bodypart/proc/set_owner(new_owner)
- SHOULD_CALL_PARENT(TRUE)
+/// Proc to change the value of the `owner` variable and react to the event of its change.
+/obj/item/bodypart/proc/update_owner(new_owner)
+ SHOULD_NOT_OVERRIDE(TRUE)
+
if(owner == new_owner)
return FALSE //`null` is a valid option, so we need to use a num var to make it clear no change was made.
- var/mob/living/carbon/old_owner = owner
- owner = new_owner
- SEND_SIGNAL(src, COMSIG_BODYPART_CHANGED_OWNER, new_owner, old_owner)
- var/needs_update_disabled = FALSE //Only really relevant if there's an owner
- if(old_owner)
- if(held_index)
- old_owner.on_lost_hand(src)
- if(old_owner.hud_used)
- var/atom/movable/screen/inventory/hand/hand = old_owner.hud_used.hand_slots["[held_index]"]
- if(hand)
- hand.update_appearance()
- old_owner.update_worn_gloves()
- if(speed_modifier)
- old_owner.update_bodypart_speed_modifier()
- if(length(bodypart_traits))
- old_owner.remove_traits(bodypart_traits, bodypart_trait_source)
- if(initial(can_be_disabled))
- if(HAS_TRAIT(old_owner, TRAIT_NOLIMBDISABLE))
- if(!owner || !HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE))
- set_can_be_disabled(initial(can_be_disabled))
- needs_update_disabled = TRUE
- UnregisterSignal(old_owner, list(
- SIGNAL_REMOVETRAIT(TRAIT_NOLIMBDISABLE),
- SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE),
- SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD),
- SIGNAL_ADDTRAIT(TRAIT_NOBLOOD),
- ))
- UnregisterSignal(old_owner, COMSIG_ATOM_RESTYLE)
- if(owner)
- if(held_index)
- owner.on_added_hand(src, held_index)
- if(owner.hud_used)
- var/atom/movable/screen/inventory/hand/hand = owner.hud_used.hand_slots["[held_index]"]
- if(hand)
- hand.update_appearance()
- owner.update_worn_gloves()
- if(speed_modifier)
- owner.update_bodypart_speed_modifier()
- if(length(bodypart_traits))
- owner.add_traits(bodypart_traits, bodypart_trait_source)
- if(initial(can_be_disabled))
- if(HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE))
- set_can_be_disabled(FALSE)
- needs_update_disabled = FALSE
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_NOLIMBDISABLE), PROC_REF(on_owner_nolimbdisable_trait_loss))
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE), PROC_REF(on_owner_nolimbdisable_trait_gain))
- // Bleeding stuff
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD), PROC_REF(on_owner_nobleed_loss))
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOBLOOD), PROC_REF(on_owner_nobleed_gain))
-
- if(needs_update_disabled)
- update_disabled()
- RegisterSignal(owner, COMSIG_ATOM_RESTYLE, PROC_REF(on_attempt_feature_restyle_mob))
+ SEND_SIGNAL(src, COMSIG_BODYPART_CHANGED_OWNER, new_owner, owner)
+
+ if(owner)
+ . = owner //return value is old owner
+ clear_ownership(owner)
+ if(new_owner)
+ apply_ownership(new_owner)
refresh_bleed_rate()
- return old_owner
+ return .
+
+/// Run all necessary procs to remove a limbs ownership and remove the appropriate signals and traits
+/obj/item/bodypart/proc/clear_ownership(mob/living/carbon/old_owner)
+ SHOULD_CALL_PARENT(TRUE)
+
+ owner = null
+
+ if(speed_modifier)
+ old_owner.update_bodypart_speed_modifier()
+ if(length(bodypart_traits))
+ old_owner.remove_traits(bodypart_traits, bodypart_trait_source)
+
+ UnregisterSignal(old_owner, list(
+ SIGNAL_REMOVETRAIT(TRAIT_NOLIMBDISABLE),
+ SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE),
+ SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD),
+ SIGNAL_ADDTRAIT(TRAIT_NOBLOOD),
+ ))
+
+ UnregisterSignal(old_owner, COMSIG_ATOM_RESTYLE)
+
+/// Apply ownership of a limb to someone, giving the appropriate traits, updates and signals
+/obj/item/bodypart/proc/apply_ownership(mob/living/carbon/new_owner)
+ SHOULD_CALL_PARENT(TRUE)
+
+ owner = new_owner
+
+ if(speed_modifier)
+ owner.update_bodypart_speed_modifier()
+ if(length(bodypart_traits))
+ owner.add_traits(bodypart_traits, bodypart_trait_source)
+
+ if(initial(can_be_disabled))
+ if(HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE))
+ set_can_be_disabled(FALSE)
+
+ // Listen to disable traits being added
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_NOLIMBDISABLE), PROC_REF(on_owner_nolimbdisable_trait_loss))
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE), PROC_REF(on_owner_nolimbdisable_trait_gain))
+
+ // Listen to no blood traits being added
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD), PROC_REF(on_owner_nobleed_loss))
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOBLOOD), PROC_REF(on_owner_nobleed_gain))
+
+ if(can_be_disabled)
+ update_disabled()
+
+ RegisterSignal(owner, COMSIG_ATOM_RESTYLE, PROC_REF(on_attempt_feature_restyle_mob))
+
+ forceMove(owner)
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_forced_removal)) //this must be set after we moved, or we insta gib
+
+/// Called on addition of a bodypart
+/obj/item/bodypart/proc/on_adding(mob/living/carbon/new_owner)
+ SHOULD_CALL_PARENT(TRUE)
+
+ item_flags |= ABSTRACT
+ ADD_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
+
+/// Called on removal of a bodypart.
+/obj/item/bodypart/proc/on_removal(mob/living/carbon/old_owner)
+ SHOULD_CALL_PARENT(TRUE)
+
+ UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
+
+ item_flags &= ~ABSTRACT
+ REMOVE_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
-/obj/item/bodypart/proc/on_removal()
if(!length(bodypart_traits))
return
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 34afbd625a6..293db57a74f 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -67,7 +67,7 @@
if(wounding_type != WOUND_BURN && isturf(chest_owner.loc) && can_bleed())
chest_owner.add_splatter_floor(chest_owner.loc)
playsound(get_turf(chest_owner), 'sound/misc/splort.ogg', 80, TRUE)
- for(var/obj/item/organ/organ as anything in chest_owner.organs)
+ for(var/obj/item/organ/organ in contents)
// NOVA EDIT START - Non-spillable organs
if(!organ.drop_when_organ_spilling)
continue
@@ -79,21 +79,13 @@
organ.forceMove(chest_owner.loc)
. += organ
- for(var/obj/item/organ/external/ext_organ as anything in src.external_organs)
- if(!(ext_organ.organ_flags & ORGAN_UNREMOVABLE))
- ext_organ.Remove(chest_owner)
- ext_organ.forceMove(chest_owner.loc)
- . += ext_organ
-
if(cavity_item)
cavity_item.forceMove(chest_owner.loc)
. += cavity_item
cavity_item = null
- return .
-
///limb removal. The "special" argument is used for swapping a limb with a new one without the effects of losing a limb kicking in.
-/obj/item/bodypart/proc/drop_limb(special, dismembered)
+/obj/item/bodypart/proc/drop_limb(special, dismembered, move_to_floor = TRUE)
if(!owner)
return
var/atom/drop_loc = owner.drop_location()
@@ -102,16 +94,13 @@
SEND_SIGNAL(src, COMSIG_BODYPART_REMOVED, owner, special, dismembered)
update_limb(dropping_limb = TRUE)
bodypart_flags &= ~BODYPART_IMPLANTED //limb is out and about, it can't really be considered an implant
- owner.remove_bodypart(src)
+ owner.remove_bodypart(src, special)
for(var/datum/scar/scar as anything in scars)
scar.victim = null
LAZYREMOVE(owner.all_scars, scar)
- for(var/obj/item/organ/external/ext_organ as anything in external_organs)
- ext_organ.transfer_to_limb(src, null) //Null is the second arg because the bodypart is being removed from it's owner.
-
- var/mob/living/carbon/phantom_owner = set_owner(null) // so we can still refer to the guy who lost their limb after said limb forgets 'em
+ var/mob/living/carbon/phantom_owner = update_owner(null) // so we can still refer to the guy who lost their limb after said limb forgets 'em
for(var/datum/wound/wound as anything in wounds)
wound.remove_wound(TRUE)
@@ -135,27 +124,22 @@
to_chat(phantom_owner, span_warning("You feel your [mutation] deactivating from the loss of your [body_zone]!"))
phantom_owner.dna.force_lose(mutation)
- for(var/obj/item/organ/organ as anything in phantom_owner.organs) //internal organs inside the dismembered limb are dropped.
- var/org_zone = check_zone(organ.zone)
- if(org_zone != body_zone)
- continue
- organ.transfer_to_limb(src, phantom_owner)
-
update_icon_dropped()
phantom_owner.update_health_hud() //update the healthdoll
phantom_owner.update_body()
phantom_owner.update_body_parts()
- if(!drop_loc) // drop_loc = null happens when a "dummy human" used for rendering icons on prefs screen gets its limbs replaced.
- qdel(src)
- return
-
if(bodypart_flags & BODYPART_PSEUDOPART)
drop_organs(phantom_owner) //Psuedoparts shouldn't have organs, but just in case
qdel(src)
return
- forceMove(drop_loc)
+ if(move_to_floor)
+ if(!drop_loc) // drop_loc = null happens when a "dummy human" used for rendering icons on prefs screen gets its limbs replaced.
+ qdel(src)
+ return
+ forceMove(drop_loc)
+
SEND_SIGNAL(phantom_owner, COMSIG_CARBON_POST_REMOVE_LIMB, src, special, dismembered)
/**
@@ -205,48 +189,53 @@
var/datum/wound/loss/dismembering = new
return dismembering.apply_dismember(src, wounding_type)
-///Transfers the organ to the limb, and to the limb's owner, if it has one. This is done on drop_limb().
-/obj/item/organ/proc/transfer_to_limb(obj/item/bodypart/bodypart, mob/living/carbon/bodypart_owner)
- Remove(bodypart_owner)
- add_to_limb(bodypart)
-
-///Adds the organ to a bodypart, used in transfer_to_limb()
-/obj/item/organ/proc/add_to_limb(obj/item/bodypart/bodypart)
- forceMove(bodypart)
-
-///Removes the organ from the limb, placing it into nullspace.
-/obj/item/organ/proc/remove_from_limb()
- moveToNullspace()
-
-/obj/item/organ/internal/brain/transfer_to_limb(obj/item/bodypart/head/head, mob/living/carbon/human/head_owner)
- Remove(head_owner) //Changeling brain concerns are now handled in Remove
- forceMove(head)
- head.brain = src
- if(brainmob)
- head.brainmob = brainmob
- brainmob = null
- head.brainmob.forceMove(head)
- head.brainmob.set_stat(DEAD)
-
-/obj/item/organ/internal/eyes/transfer_to_limb(obj/item/bodypart/head/head, mob/living/carbon/human/head_owner)
- head.eyes = src
- ..()
-
-/obj/item/organ/internal/ears/transfer_to_limb(obj/item/bodypart/head/head, mob/living/carbon/human/head_owner)
- head.ears = src
- ..()
-
-/obj/item/organ/internal/tongue/transfer_to_limb(obj/item/bodypart/head/head, mob/living/carbon/human/head_owner)
- head.tongue = src
- ..()
-
-/obj/item/bodypart/chest/drop_limb(special)
+/obj/item/organ/internal/eyes/on_bodypart_insert(obj/item/bodypart/head/head)
+ if(istype(head))
+ head.eyes = src
+ return ..()
+
+/obj/item/organ/internal/ears/on_bodypart_insert(obj/item/bodypart/head/head)
+ if(istype(head))
+ head.ears = src
+ return ..()
+
+/obj/item/organ/internal/tongue/on_bodypart_insert(obj/item/bodypart/head/head)
+ if(istype(head))
+ head.tongue = src
+ return ..()
+
+/obj/item/organ/internal/brain/on_bodypart_insert(obj/item/bodypart/head/head)
+ if(istype(head))
+ head.brain = src
+ return ..()
+
+/obj/item/organ/internal/eyes/on_bodypart_remove(obj/item/bodypart/head/head)
+ if(istype(head))
+ head.eyes = null
+ return ..()
+
+/obj/item/organ/internal/ears/on_bodypart_remove(obj/item/bodypart/head/head)
+ if(istype(head))
+ head.ears = null
+ return ..()
+
+/obj/item/organ/internal/tongue/on_bodypart_remove(obj/item/bodypart/head/head)
+ if(istype(head))
+ head.tongue = null
+ return ..()
+
+/obj/item/organ/internal/brain/on_bodypart_remove(obj/item/bodypart/head/head)
+ if(istype(head))
+ head.brain = null
+ return ..()
+
+/obj/item/bodypart/chest/drop_limb(special, dismembered, move_to_floor = TRUE)
if(special)
return ..()
//if this is not a special drop, this is a mistake
return FALSE
-/obj/item/bodypart/arm/drop_limb(special)
+/obj/item/bodypart/arm/drop_limb(special, dismembered, move_to_floor = TRUE)
var/mob/living/carbon/arm_owner = owner
if(special || !arm_owner)
@@ -269,7 +258,7 @@
arm_owner.update_worn_gloves() //to remove the bloody hands overlay
return ..()
-/obj/item/bodypart/leg/drop_limb(special)
+/obj/item/bodypart/leg/drop_limb(special, dismembered, move_to_floor = TRUE)
if(owner && !special)
if(owner.legcuffed)
owner.legcuffed.forceMove(owner.drop_location()) //At this point bodypart is still in nullspace
@@ -280,7 +269,7 @@
owner.dropItemToGround(owner.shoes, TRUE)
return ..()
-/obj/item/bodypart/head/drop_limb(special)
+/obj/item/bodypart/head/drop_limb(special, dismembered, move_to_floor = TRUE)
if(!special)
//Drop all worn head items
for(var/obj/item/head_item as anything in list(owner.glasses, owner.ears, owner.wear_mask, owner.head))
@@ -336,8 +325,6 @@
SEND_SIGNAL(new_limb_owner, COMSIG_CARBON_ATTACH_LIMB, src, special)
SEND_SIGNAL(src, COMSIG_BODYPART_ATTACHED, new_limb_owner, special)
- moveToNullspace()
- set_owner(new_limb_owner)
new_limb_owner.add_bodypart(src)
LAZYREMOVE(new_limb_owner.body_zone_dismembered_by, body_zone)
@@ -350,8 +337,10 @@
qdel(attach_surgery)
break
- for(var/obj/item/organ/limb_organ in contents)
- limb_organ.Insert(new_limb_owner, TRUE)
+ for(var/obj/item/organ/organ as anything in new_limb_owner.organs)
+ if(deprecise_zone(organ.zone) != body_zone)
+ continue
+ organ.bodypart_insert(src)
for(var/datum/wound/wound as anything in wounds)
// we have to remove the wound from the limb wound list first, so that we can reapply it fresh with the new person
@@ -391,15 +380,6 @@
if(!.)
return
- if(brain)
- brain = null
- if(tongue)
- tongue = null
- if(ears)
- ears = null
- if(eyes)
- eyes = null
-
if(old_real_name)
new_head_owner.real_name = old_real_name
real_name = new_head_owner.real_name
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 8d4d48bfe6c..e6e805664d3 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -26,7 +26,6 @@
unarmed_effectiveness = 0
bodypart_trait_source = HEAD_TRAIT
- var/mob/living/brain/brainmob //The current occupant.
var/obj/item/organ/internal/brain/brain //The brain organ
var/obj/item/organ/internal/eyes/eyes
var/obj/item/organ/internal/ears/ears
@@ -93,12 +92,6 @@
var/datum/worn_feature_offset/worn_face_offset
/obj/item/bodypart/head/Destroy()
- QDEL_NULL(brainmob) //order is sensitive, see warning in Exited() below
- QDEL_NULL(brain)
- QDEL_NULL(eyes)
- QDEL_NULL(ears)
- QDEL_NULL(tongue)
-
QDEL_NULL(worn_ears_offset)
QDEL_NULL(worn_glasses_offset)
QDEL_NULL(worn_mask_offset)
@@ -110,11 +103,6 @@
if(gone == brain)
brain = null
update_icon_dropped()
- if(!QDELETED(brainmob)) //this shouldn't happen without badminnery.
- message_admins("Brainmob: ([ADMIN_LOOKUPFLW(brainmob)]) was left stranded in [src] at [ADMIN_VERBOSEJMP(src)] without a brain!")
- brainmob.log_message(", brainmob, was left stranded in [src] without a brain", LOG_GAME)
- if(gone == brainmob)
- brainmob = null
if(gone == eyes)
eyes = null
update_icon_dropped()
@@ -129,12 +117,12 @@
if(show_organs_on_examine && IS_ORGANIC_LIMB(src))
if(!brain)
. += span_info("The brain has been removed from [src].")
- else if(brain.suicided || (brainmob && HAS_TRAIT(brainmob, TRAIT_SUICIDED)))
+ else if(brain.suicided || (brain.brainmob && HAS_TRAIT(brain.brainmob, TRAIT_SUICIDED)))
. += span_info("There's a miserable expression on [real_name]'s face; they must have really hated life. There's no hope of recovery.")
- else if(brainmob?.health <= HEALTH_THRESHOLD_DEAD)
- . += span_info("It's leaking some kind of... clear fluid? The brain inside must be in pretty bad shape.")
- else if(brainmob)
- if(brainmob.key || brainmob.get_ghost(FALSE, TRUE))
+ else if(brain.brainmob)
+ if(brain.brainmob?.health <= HEALTH_THRESHOLD_DEAD)
+ . += span_info("It's leaking some kind of... clear fluid? The brain inside must be in pretty bad shape.")
+ if(brain.brainmob.key || brain.brainmob.get_ghost(FALSE, TRUE))
. += span_info("Its muscles are twitching slightly... It seems to have some life still in it.")
else
. += span_info("It's completely lifeless. Perhaps there'll be a chance for them later.")
@@ -158,33 +146,12 @@
return ..()
/obj/item/bodypart/head/drop_organs(mob/user, violent_removal)
- var/atom/drop_loc = drop_location()
- for(var/obj/item/head_item in src)
- if(head_item == brain)
- if(user)
- user.visible_message(span_warning("[user] saws [src] open and pulls out a brain!"), span_notice("You saw [src] open and pull out a brain."))
- if(brainmob)
- brainmob.container = null
- brain.brainmob = brainmob
- brainmob = null
- if(violent_removal && prob(rand(80, 100))) //ghetto surgery can damage the brain.
- to_chat(user, span_warning("[brain] was damaged in the process!"))
- brain.set_organ_damage(brain.maxHealth)
- brain.forceMove(drop_loc)
- brain = null
- update_icon_dropped()
- else
- if(istype(head_item, /obj/item/reagent_containers/pill))
- for(var/datum/action/item_action/hands_free/activate_pill/pill_action in head_item.actions)
- qdel(pill_action)
- else if(isorgan(head_item))
- var/obj/item/organ/organ = head_item
- if(organ.organ_flags & ORGAN_UNREMOVABLE)
- continue
- head_item.forceMove(drop_loc)
- eyes = null
- ears = null
- tongue = null
+ if(user)
+ user.visible_message(span_warning("[user] saws [src] open and pulls out a brain!"), span_notice("You saw [src] open and pull out a brain."))
+ if(brain && violent_removal && prob(90)) //ghetto surgery can damage the brain.
+ to_chat(user, span_warning("[brain] was damaged in the process!"))
+ brain.set_organ_damage(brain.maxHealth)
+
update_limb()
return ..()
diff --git a/code/modules/surgery/bodyparts/head_hair_and_lips.dm b/code/modules/surgery/bodyparts/head_hair_and_lips.dm
index 15d4db296ee..4b23b9cc256 100644
--- a/code/modules/surgery/bodyparts/head_hair_and_lips.dm
+++ b/code/modules/surgery/bodyparts/head_hair_and_lips.dm
@@ -39,7 +39,7 @@
//HIDDEN CHECKS END
if(owner)
- if(!hair_hidden && !owner.get_organ_slot(ORGAN_SLOT_BRAIN) && !HAS_TRAIT(owner, TRAIT_NO_DEBRAIN_OVERLAY))
+ if(!hair_hidden && !owner.get_organ_slot(ORGAN_SLOT_BRAIN) && !HAS_TRAIT(owner, TRAIT_NO_DEBRAIN_OVERLAY) && !istype(src, /obj/item/bodypart/head/robot/synth)) // NOVA EDIT CHANGE - ORIGINAL: if(!hair_hidden && !owner.get_organ_slot(ORGAN_SLOT_BRAIN) && !HAS_TRAIT(owner, TRAIT_NO_DEBRAIN_OVERLAY))
show_debrained = TRUE
else
show_debrained = FALSE
@@ -49,7 +49,7 @@
else
show_eyeless = FALSE
else
- if(!hair_hidden && !brain)
+ if(!hair_hidden && !brain && !istype(src, /obj/item/bodypart/head/robot/synth)) // NOVA EDIT CHANGE - ORIGINAL: if(!hair_hidden && !brain)
show_debrained = TRUE
else
show_debrained = FALSE
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index c93fdf10b47..52671b5fbc1 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -15,6 +15,7 @@
/mob/living/carbon/proc/del_and_replace_bodypart(obj/item/bodypart/new_limb, special)
var/obj/item/bodypart/old_limb = get_bodypart(new_limb.body_zone)
if(old_limb)
+ old_limb.drop_limb(special = TRUE)
qdel(old_limb)
new_limb.try_attach_limb(src, special = special)
@@ -172,7 +173,7 @@
/mob/living/carbon/proc/synchronize_bodytypes()
var/all_limb_flags = NONE
for(var/obj/item/bodypart/limb as anything in bodyparts)
- for(var/obj/item/organ/external/ext_organ as anything in limb.external_organs)
+ for(var/obj/item/organ/external/ext_organ in limb)
all_limb_flags |= ext_organ.external_bodytypes
all_limb_flags |= limb.bodytype
diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
index 25f1ab985b6..c657e89cefd 100644
--- a/code/modules/surgery/bodyparts/parts.dm
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -35,8 +35,18 @@
/// Which functional (i.e. flightpotion) wing types (if any) does this bodypart support? If count is >1 a radial menu is used to choose between all icons in list
var/list/wing_types = list(/obj/item/organ/external/wings/functional/angel)
+/obj/item/bodypart/chest/forced_removal(dismembered, special, move_to_floor)
+ var/mob/living/carbon/old_owner = owner
+ ..(special = TRUE) //special because we're self destructing
+
+ //If someones chest is teleported away, they die pretty hard
+ if(!old_owner)
+ return
+ message_admins("[ADMIN_LOOKUPFLW(old_owner)] was gibbed after their chest teleported to [ADMIN_VERBOSEJMP(loc)].")
+ old_owner.gib(DROP_ALL_REMAINS)
+
/obj/item/bodypart/chest/can_dismember(obj/item/item)
- if(owner.stat < HARD_CRIT || !get_organs())
+ if(owner.stat < HARD_CRIT || !contents.len)
return FALSE
return ..()
@@ -127,6 +137,40 @@
QDEL_NULL(held_hand_offset)
return ..()
+/// We need to clear out hand hud items and appearance, so do that here
+/obj/item/bodypart/arm/clear_ownership(mob/living/carbon/old_owner)
+ ..()
+
+ old_owner.update_worn_gloves()
+
+ if(!held_index)
+ return
+
+ old_owner.on_lost_hand(src)
+
+ if(!old_owner.hud_used)
+ return
+
+ var/atom/movable/screen/inventory/hand/hand = old_owner.hud_used.hand_slots["[held_index]"]
+ hand?.update_appearance()
+
+/// We need to add hand hud items and appearance, so do that here
+/obj/item/bodypart/arm/apply_ownership(mob/living/carbon/new_owner)
+ ..()
+
+ new_owner.update_worn_gloves()
+
+ if(!held_index)
+ return
+
+ new_owner.on_added_hand(src, held_index)
+
+ if(!new_owner.hud_used)
+ return
+
+ var/atom/movable/screen/inventory/hand/hand = new_owner.hud_used.hand_slots["[held_index]"]
+ hand.update_appearance()
+
/obj/item/bodypart/arm/left
name = "left arm"
desc = "Did you know that the word 'sinister' stems originally from the \
@@ -143,27 +187,22 @@
px_y = 0
bodypart_trait_source = LEFT_ARM_TRAIT
-
-/obj/item/bodypart/arm/left/set_owner(new_owner)
- . = ..()
- if(. == FALSE)
- return
- if(owner)
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM))
- ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_loss))
- else
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_gain))
- if(.)
- var/mob/living/carbon/old_owner = .
- if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_L_ARM))
- UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM))
- if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM))
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
- else
- UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM))
-
+/obj/item/bodypart/arm/left/apply_ownership(mob/living/carbon/new_owner)
+ if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_L_ARM))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
+ RegisterSignal(new_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_loss))
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
+ RegisterSignal(new_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_gain))
+ ..()
+
+/obj/item/bodypart/arm/left/clear_ownership(mob/living/carbon/old_owner)
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_L_ARM))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM))
+ ..()
///Proc to react to the owner gaining the TRAIT_PARALYSIS_L_ARM trait.
/obj/item/bodypart/arm/left/proc/on_owner_paralysis_gain(mob/living/carbon/source)
@@ -172,7 +211,6 @@
UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM))
RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_loss))
-
///Proc to react to the owner losing the TRAIT_PARALYSIS_L_ARM trait.
/obj/item/bodypart/arm/left/proc/on_owner_paralysis_loss(mob/living/carbon/source)
SIGNAL_HANDLER
@@ -180,7 +218,6 @@
UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM))
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_gain))
-
/obj/item/bodypart/arm/left/set_disabled(new_disabled)
. = ..()
if(isnull(.) || !owner)
@@ -200,7 +237,6 @@
var/atom/movable/screen/inventory/hand/hand_screen_object = owner.hud_used.hand_slots["[held_index]"]
hand_screen_object?.update_appearance()
-
/obj/item/bodypart/arm/left/monkey
icon = 'icons/mob/human/species/monkey/bodyparts.dmi'
icon_static = 'icons/mob/human/species/monkey/bodyparts.dmi'
@@ -233,7 +269,6 @@
should_draw_greyscale = FALSE
appendage_noun = "scythe-like hand"
-
/obj/item/bodypart/arm/right
name = "right arm"
desc = "Over 87% of humans are right handed. That figure is much lower \
@@ -249,26 +284,22 @@
px_y = 0
bodypart_trait_source = RIGHT_ARM_TRAIT
-/obj/item/bodypart/arm/right/set_owner(new_owner)
- . = ..()
- if(. == FALSE)
- return
- if(owner)
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM))
- ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_loss))
- else
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_gain))
- if(.)
- var/mob/living/carbon/old_owner = .
- if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_R_ARM))
- UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM))
- if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM))
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
- else
- UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM))
-
+/obj/item/bodypart/arm/right/apply_ownership(mob/living/carbon/new_owner)
+ if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_R_ARM))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
+ RegisterSignal(new_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_loss))
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
+ RegisterSignal(new_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_gain))
+ ..()
+
+/obj/item/bodypart/arm/right/clear_ownership(mob/living/carbon/old_owner)
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_R_ARM))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM))
+ ..()
///Proc to react to the owner gaining the TRAIT_PARALYSIS_R_ARM trait.
/obj/item/bodypart/arm/right/proc/on_owner_paralysis_gain(mob/living/carbon/source)
@@ -277,7 +308,6 @@
UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM))
RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_loss))
-
///Proc to react to the owner losing the TRAIT_PARALYSIS_R_ARM trait.
/obj/item/bodypart/arm/right/proc/on_owner_paralysis_loss(mob/living/carbon/source)
SIGNAL_HANDLER
@@ -285,7 +315,6 @@
UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM))
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_gain))
-
/obj/item/bodypart/arm/right/set_disabled(new_disabled)
. = ..()
if(isnull(.) || !owner)
@@ -305,7 +334,6 @@
var/atom/movable/screen/inventory/hand/hand_screen_object = owner.hud_used.hand_slots["[held_index]"]
hand_screen_object?.update_appearance()
-
/obj/item/bodypart/arm/right/monkey
icon = 'icons/mob/human/species/monkey/bodyparts.dmi'
icon_static = 'icons/mob/human/species/monkey/bodyparts.dmi'
@@ -375,35 +403,30 @@
can_be_disabled = TRUE
bodypart_trait_source = LEFT_LEG_TRAIT
-/obj/item/bodypart/leg/left/set_owner(new_owner)
- . = ..()
- if(. == FALSE)
- return
- if(owner)
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG))
- ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_loss))
- else
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_gain))
- if(.)
- var/mob/living/carbon/old_owner = .
- if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_L_LEG))
- UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG))
- if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG))
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
- else
- UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG))
-
-
-///Proc to react to the owner gaining the TRAIT_PARALYSIS_L_LEG trait.
+/obj/item/bodypart/leg/left/apply_ownership(mob/living/carbon/new_owner)
+ if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_L_LEG))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
+ RegisterSignal(new_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_loss))
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
+ RegisterSignal(new_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_gain))
+ ..()
+
+/obj/item/bodypart/leg/left/clear_ownership(mob/living/carbon/old_owner)
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_L_LEG))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG))
+ ..()
+
+///Proc to react to the owner gaining the TRAIT_PARALYSIS_L_ARM trait.
/obj/item/bodypart/leg/left/proc/on_owner_paralysis_gain(mob/living/carbon/source)
SIGNAL_HANDLER
ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG))
RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_loss))
-
///Proc to react to the owner losing the TRAIT_PARALYSIS_L_LEG trait.
/obj/item/bodypart/leg/left/proc/on_owner_paralysis_loss(mob/living/carbon/source)
SIGNAL_HANDLER
@@ -411,7 +434,6 @@
UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG))
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_gain))
-
/obj/item/bodypart/leg/left/set_disabled(new_disabled)
. = ..()
if(isnull(.) || !owner)
@@ -469,26 +491,22 @@
px_y = 12
bodypart_trait_source = RIGHT_LEG_TRAIT
-/obj/item/bodypart/leg/right/set_owner(new_owner)
- . = ..()
- if(. == FALSE)
- return
- if(owner)
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG))
- ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
- RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG), PROC_REF(on_owner_paralysis_loss))
- else
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG), PROC_REF(on_owner_paralysis_gain))
- if(.)
- var/mob/living/carbon/old_owner = .
- if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_R_LEG))
- UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG))
- if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG))
- REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
- else
- UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG))
-
+/obj/item/bodypart/leg/right/apply_ownership(mob/living/carbon/new_owner)
+ if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_R_LEG))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
+ RegisterSignal(new_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG), PROC_REF(on_owner_paralysis_loss))
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
+ RegisterSignal(new_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG), PROC_REF(on_owner_paralysis_gain))
+ ..()
+
+/obj/item/bodypart/leg/right/clear_ownership(mob/living/carbon/old_owner)
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_R_LEG))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG))
+ ..()
///Proc to react to the owner gaining the TRAIT_PARALYSIS_R_LEG trait.
/obj/item/bodypart/leg/right/proc/on_owner_paralysis_gain(mob/living/carbon/source)
@@ -497,7 +515,6 @@
UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG))
RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG), PROC_REF(on_owner_paralysis_loss))
-
///Proc to react to the owner losing the TRAIT_PARALYSIS_R_LEG trait.
/obj/item/bodypart/leg/right/proc/on_owner_paralysis_loss(mob/living/carbon/source)
SIGNAL_HANDLER
diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
index 574ab49987b..7f514ecdaf4 100644
--- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
@@ -518,17 +518,16 @@
)
return ..()
-/obj/item/bodypart/arm/left/golem/set_owner(new_owner)
+/obj/item/bodypart/arm/left/golem/clear_ownership(mob/living/carbon/old_owner)
. = ..()
- if (. == FALSE)
- return
- if (owner)
- owner.AddComponentFrom(REF(src), /datum/component/shovel_hands)
- if (isnull(.))
- return
- var/mob/living/carbon/old_owner = .
+
old_owner.RemoveComponentSource(REF(src), /datum/component/shovel_hands)
+/obj/item/bodypart/arm/left/golem/apply_ownership(mob/living/carbon/new_owner)
+ . = ..()
+
+ new_owner.AddComponentFrom(REF(src), /datum/component/shovel_hands)
+
/obj/item/bodypart/arm/right/golem
icon = 'icons/mob/human/species/golems.dmi'
icon_static = 'icons/mob/human/species/golems.dmi'
@@ -552,17 +551,16 @@
)
return ..()
-/obj/item/bodypart/arm/right/golem/set_owner(new_owner)
+/obj/item/bodypart/arm/right/golem/clear_ownership(mob/living/carbon/old_owner)
. = ..()
- if (. == FALSE)
- return
- if (owner)
- owner.AddComponentFrom(REF(src), /datum/component/shovel_hands)
- if (isnull(.))
- return
- var/mob/living/carbon/old_owner = .
+
old_owner.RemoveComponentSource(REF(src), /datum/component/shovel_hands)
+/obj/item/bodypart/arm/right/golem/apply_ownership(mob/living/carbon/new_owner)
+ . = ..()
+
+ new_owner.AddComponentFrom(REF(src), /datum/component/shovel_hands)
+
/obj/item/bodypart/leg/left/golem
icon = 'icons/mob/human/species/golems.dmi'
icon_static = 'icons/mob/human/species/golems.dmi'
diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm
index a3feba76fec..d7f08a7be35 100644
--- a/code/modules/surgery/organs/_organ.dm
+++ b/code/modules/surgery/organs/_organ.dm
@@ -6,6 +6,8 @@
throwforce = 0
/// The mob that owns this organ.
var/mob/living/carbon/owner = null
+ /// Reference to the limb we're inside of
+ var/obj/item/bodypart/bodypart_owner
/// The cached info about the blood this organ belongs to
var/list/blood_dna_info = list("Synthetic DNA" = "O+") // not every organ spawns inside a person
/// The body zone this organ is supposed to inhabit.
@@ -77,134 +79,16 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
volume = reagent_vol,\
after_eat = CALLBACK(src, PROC_REF(OnEatFrom)))
- if(!IS_ROBOTIC_ORGAN(src))
- add_blood_DNA(blood_dna_info)
-
-/*
- * Insert the organ into the select mob.
- *
- * receiver - the mob who will get our organ
- * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
- * drop_if_replaced - if there's an organ in the slot already, whether we drop it afterwards
- */
-/obj/item/organ/proc/Insert(mob/living/carbon/receiver, special = FALSE, drop_if_replaced = TRUE)
- SHOULD_CALL_PARENT(TRUE)
-
- if(!iscarbon(receiver) || owner == receiver)
- return FALSE
-
- var/obj/item/organ/replaced = receiver.get_organ_slot(slot)
- if(replaced)
- replaced.Remove(receiver, special = TRUE)
- if(drop_if_replaced)
- replaced.forceMove(get_turf(receiver))
- else
- qdel(replaced)
-
- receiver.organs |= src
- receiver.organs_slot[slot] = src
- owner = receiver
-
- if(!IS_ROBOTIC_ORGAN(src) && (organ_flags & ORGAN_VIRGIN))
- blood_dna_info = receiver.get_blood_dna_list()
- // need to remove the synethic blood DNA that is initialized
- // wash also adds the blood dna again
- wash(CLEAN_TYPE_BLOOD)
- organ_flags &= ~ORGAN_VIRGIN
-
-
- // Apply unique side-effects. Return value does not matter.
- on_insert(receiver, special)
-
- return TRUE
-
-/// Called after the organ is inserted into a mob.
-/// Adds Traits, Actions, and Status Effects on the mob in which the organ is impanted.
-/// Override this proc to create unique side-effects for inserting your organ. Must be called by overrides.
-/obj/item/organ/proc/on_insert(mob/living/carbon/organ_owner, special)
- SHOULD_CALL_PARENT(TRUE)
-
- moveToNullspace()
-
- for(var/trait in organ_traits)
- ADD_TRAIT(organ_owner, trait, REF(src))
-
- for(var/datum/action/action as anything in actions)
- action.Grant(organ_owner)
-
- for(var/datum/status_effect/effect as anything in organ_effects)
- organ_owner.apply_status_effect(effect, type)
-
- RegisterSignal(owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_owner_examine))
- SEND_SIGNAL(src, COMSIG_ORGAN_IMPLANTED, organ_owner)
- SEND_SIGNAL(organ_owner, COMSIG_CARBON_GAIN_ORGAN, src, special)
-
-/*
- * Remove the organ from the select mob.
- *
- * * organ_owner - the mob who owns our organ, that we're removing the organ from.
- * * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
- */
-/obj/item/organ/proc/Remove(mob/living/carbon/organ_owner, special = FALSE)
- SHOULD_CALL_PARENT(TRUE)
-
- organ_owner.organs -= src
- if(organ_owner.organs_slot[slot] == src)
- organ_owner.organs_slot.Remove(slot)
-
- owner = null
-
- // Apply or reset unique side-effects. Return value does not matter.
- on_remove(organ_owner, special)
-
- return TRUE
-
-/// Called after the organ is removed from a mob.
-/// Removes Traits, Actions, and Status Effects on the mob in which the organ was impanted.
-/// Override this proc to create unique side-effects for removing your organ. Must be called by overrides.
-/obj/item/organ/proc/on_remove(mob/living/carbon/organ_owner, special)
- SHOULD_CALL_PARENT(TRUE)
-
- if(!iscarbon(organ_owner))
- stack_trace("Organ removal should not be happening on non carbon mobs: [organ_owner]")
-
- for(var/trait in organ_traits)
- REMOVE_TRAIT(organ_owner, trait, REF(src))
-
- for(var/datum/action/action as anything in actions)
- action.Remove(organ_owner)
-
- for(var/datum/status_effect/effect as anything in organ_effects)
- organ_owner.remove_status_effect(effect, type)
-
- UnregisterSignal(organ_owner, COMSIG_ATOM_EXAMINE)
- SEND_SIGNAL(src, COMSIG_ORGAN_REMOVED, organ_owner)
- SEND_SIGNAL(organ_owner, COMSIG_CARBON_LOSE_ORGAN, src, special)
-
- // We don't need to readd things to the organ if it's getting deleted
- if(QDELING(src))
- return
-
- if(!IS_ROBOTIC_ORGAN(src) && !(item_flags & NO_BLOOD_ON_ITEM))
- AddElement(/datum/element/decal/blood)
-
- var/list/diseases = organ_owner.get_static_viruses()
- if(!LAZYLEN(diseases))
- return
-
- var/list/datum/disease/diseases_to_add = list()
- for(var/datum/disease/disease as anything in diseases)
- // robotic organs are immune to disease unless 'inorganic biology' symptom is present
- if(IS_ROBOTIC_ORGAN(src) && !(disease.infectable_biotypes & MOB_ROBOTIC))
- continue
-
- // admin or special viruses that should not be reproduced
- if(disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS))
- continue
-
- diseases_to_add += disease
- if(LAZYLEN(diseases_to_add))
- AddComponent(/datum/component/infective, diseases_to_add)
+/obj/item/organ/Destroy()
+ if(bodypart_owner && !owner && !QDELETED(bodypart_owner))
+ bodypart_remove(bodypart_owner)
+ else if(owner)
+ // The special flag is important, because otherwise mobs can die
+ // while undergoing transformation into different mobs.
+ Remove(owner, special=TRUE)
+ else
+ STOP_PROCESSING(SSobj, src)
+ return ..()
/// Add a Trait to an organ that it will give its owner.
/obj/item/organ/proc/add_organ_trait(trait)
@@ -241,15 +125,6 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
/obj/item/organ/proc/on_find(mob/living/finder)
return
-/**
- * Proc that gets called when the organ is surgically removed by someone, can be used for special effects
- * Currently only used so surplus organs can explode when surgically removed.
- */
-/obj/item/organ/proc/on_surgical_removal(mob/living/user, mob/living/carbon/old_owner, target_zone, obj/item/tool)
- SHOULD_CALL_PARENT(TRUE)
- SEND_SIGNAL(src, COMSIG_ORGAN_SURGICALLY_REMOVED, user, old_owner, target_zone, tool)
- RemoveElement(/datum/element/decal/blood)
-
/obj/item/organ/wash(clean_types)
. = ..()
@@ -452,4 +327,4 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
/// Tries to replace the existing organ on the passed mob with this one, with special handling for replacing a brain without ghosting target
/obj/item/organ/proc/replace_into(mob/living/carbon/new_owner)
- return Insert(new_owner, special = TRUE, drop_if_replaced = FALSE)
+ return Insert(new_owner, special = TRUE, movement_flags = DELETE_IF_REPLACED)
diff --git a/code/modules/surgery/organs/external/_external_organ.dm b/code/modules/surgery/organs/external/_external_organ.dm
index dcc713a0991..5b9f9d67347 100644
--- a/code/modules/surgery/organs/external/_external_organ.dm
+++ b/code/modules/surgery/organs/external/_external_organ.dm
@@ -12,8 +12,6 @@
///The overlay datum that actually draws stuff on the limb
var/datum/bodypart_overlay/mutant/bodypart_overlay
- ///Reference to the limb we're inside of
- var/obj/item/bodypart/ownerlimb
///If not null, overrides the appearance with this sprite accessory datum
var/sprite_accessory_override
@@ -25,7 +23,7 @@
///Set to EXTERNAL_BEHIND, EXTERNAL_FRONT or EXTERNAL_ADJACENT if you want to draw one of those layers as the object sprite. FALSE to use your own
///This will not work if it doesn't have a limb to generate it's icon with
var/use_mob_sprite_as_obj_sprite = FALSE
- ///Does this organ have any bodytypes to pass to it's ownerlimb?
+ ///Does this organ have any bodytypes to pass to it's bodypart_owner?
var/external_bodytypes = NONE
///Which flags does a 'modification tool' need to have to restyle us, if it all possible (located in code/_DEFINES/mobs)
var/restyle_flags = NONE
@@ -59,23 +57,10 @@
if(restyle_flags)
RegisterSignal(src, COMSIG_ATOM_RESTYLE, PROC_REF(on_attempt_feature_restyle))
-/obj/item/organ/external/Destroy()
- if(owner)
- Remove(owner, special = TRUE)
- else if(ownerlimb)
- remove_from_limb()
-
- return ..()
-
-/obj/item/organ/external/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/external/mob_insert(mob/living/carbon/receiver, special, movement_flags)
if(!should_external_organ_apply_to(type, receiver))
stack_trace("adding a [type] to a [receiver.type] when it shouldn't be!")
- var/obj/item/bodypart/limb = receiver.get_bodypart(deprecise_zone(zone))
-
- if(!limb)
- return FALSE
-
. = ..()
if(!.)
@@ -91,54 +76,28 @@
// NOVA EDIT CHANGE END
bodypart_overlay.imprint_on_next_insertion = FALSE
- ownerlimb = limb
- add_to_limb(ownerlimb)
-
if(external_bodytypes)
receiver.synchronize_bodytypes()
receiver.update_body_parts()
-/obj/item/organ/external/Remove(mob/living/carbon/organ_owner, special, moving)
- . = ..()
-
- if(ownerlimb)
- remove_from_limb()
- if(!moving && use_mob_sprite_as_obj_sprite) //so we're being taken out and dropped
- update_appearance(UPDATE_OVERLAYS)
-
- if(organ_owner)
+/obj/item/organ/external/mob_remove(mob/living/carbon/organ_owner, special, moving)
+ if(!special)
+ organ_owner.synchronize_bodytypes()
organ_owner.update_body_parts()
+ return ..()
+/obj/item/organ/external/on_bodypart_insert(obj/item/bodypart/bodypart)
+ bodypart.add_bodypart_overlay(bodypart_overlay)
+ return ..()
-/obj/item/organ/external/on_remove(mob/living/carbon/organ_owner, special)
- . = ..()
- color = bodypart_overlay.draw_color // so a pink felinid doesn't drop a gray tail
-
-///Transfers the organ to the limb, and to the limb's owner, if it has one.
-/obj/item/organ/external/transfer_to_limb(obj/item/bodypart/bodypart, mob/living/carbon/bodypart_owner)
- if(owner)
- Remove(owner, moving = TRUE)
- else if(ownerlimb)
- remove_from_limb()
+/obj/item/organ/external/on_bodypart_remove(obj/item/bodypart/bodypart)
+ bodypart.remove_bodypart_overlay(bodypart_overlay)
- if(bodypart_owner)
- Insert(bodypart_owner, TRUE)
- else
- add_to_limb(bodypart)
-
-/obj/item/organ/external/add_to_limb(obj/item/bodypart/bodypart)
- bodypart.external_organs += src
- ownerlimb = bodypart
- ownerlimb.add_bodypart_overlay(bodypart_overlay)
- return ..()
+ if(use_mob_sprite_as_obj_sprite)
+ update_appearance(UPDATE_OVERLAYS)
-/obj/item/organ/external/remove_from_limb()
- ownerlimb.external_organs -= src
- ownerlimb.remove_bodypart_overlay(bodypart_overlay)
- if(ownerlimb.owner && external_bodytypes)
- ownerlimb.owner.synchronize_bodytypes()
- ownerlimb = null
+ color = bodypart_overlay.draw_color // so a pink felinid doesn't drop a gray tail
return ..()
/proc/should_external_organ_apply_to(obj/item/organ/external/organpath, mob/living/carbon/target)
@@ -172,8 +131,8 @@
if(owner) //are we in a person?
owner.update_body_parts()
- else if(ownerlimb) //are we in a limb?
- ownerlimb.update_icon_dropped()
+ else if(bodypart_owner) //are we in a limb?
+ bodypart_owner.update_icon_dropped()
//else if(use_mob_sprite_as_obj_sprite) //are we out in the world, unprotected by flesh?
/obj/item/organ/external/on_life(seconds_per_tick, times_fired)
@@ -188,7 +147,7 @@
//Build the mob sprite and use it as our overlay
for(var/external_layer in bodypart_overlay.all_layers)
if(bodypart_overlay.layers & external_layer)
- . += bodypart_overlay.get_overlay(external_layer, ownerlimb)
+ . += bodypart_overlay.get_overlay(external_layer, bodypart_owner)
///The horns of a lizard!
/obj/item/organ/external/horns
@@ -294,16 +253,17 @@
///Store our old datum here for if our antennae are healed
var/original_sprite_datum
-/obj/item/organ/external/antennae/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/external/antennae/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(!.)
return
RegisterSignal(receiver, COMSIG_HUMAN_BURNING, PROC_REF(try_burn_antennae))
RegisterSignal(receiver, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(heal_antennae))
-/obj/item/organ/external/antennae/Remove(mob/living/carbon/organ_owner, special, moving)
+/obj/item/organ/external/antennae/Remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
- UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL))
+ if(organ_owner)
+ UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL))
///check if our antennae can burn off ;_;
/obj/item/organ/external/antennae/proc/try_burn_antennae(mob/living/carbon/human/human)
diff --git a/code/modules/surgery/organs/external/restyling.dm b/code/modules/surgery/organs/external/restyling.dm
index 454e4395ae6..7d6be1b6d58 100644
--- a/code/modules/surgery/organs/external/restyling.dm
+++ b/code/modules/surgery/organs/external/restyling.dm
@@ -31,7 +31,7 @@
///Asks the external organs inside the limb if they can restyle
/obj/item/bodypart/proc/attempt_feature_restyle(atom/source, mob/living/trimmer, atom/movable/original_target, body_zone, restyle_type, style_speed)
var/list/valid_features = list()
- for(var/obj/item/organ/external/feature in external_organs)
+ for(var/obj/item/organ/external/feature in contents)
if(feature.restyle_flags & restyle_type)
valid_features.Add(feature)
diff --git a/code/modules/surgery/organs/external/spines.dm b/code/modules/surgery/organs/external/spines.dm
index 600ed619e68..86bc8c800a1 100644
--- a/code/modules/surgery/organs/external/spines.dm
+++ b/code/modules/surgery/organs/external/spines.dm
@@ -16,13 +16,13 @@
///A two-way reference between the tail and the spines because of wagging sprites. Bruh.
var/obj/item/organ/external/tail/lizard/paired_tail
-/obj/item/organ/external/spines/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/external/spines/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(.)
paired_tail = locate(/obj/item/organ/external/tail/lizard) in receiver.organs //We want specifically a lizard tail, so we don't use the slot.
paired_tail?.paired_spines = src
-/obj/item/organ/external/spines/Remove(mob/living/carbon/organ_owner, special, moving)
+/obj/item/organ/external/spines/Remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
if(paired_tail)
paired_tail.paired_spines = null
diff --git a/code/modules/surgery/organs/external/tails.dm b/code/modules/surgery/organs/external/tails.dm
index a3105b81b4a..5c620eea161 100644
--- a/code/modules/surgery/organs/external/tails.dm
+++ b/code/modules/surgery/organs/external/tails.dm
@@ -18,7 +18,7 @@
///The original owner of this tail
var/original_owner //Yay, snowflake code!
-/obj/item/organ/external/tail/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/external/tail/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(.)
RegisterSignal(receiver, COMSIG_ORGAN_WAG_TAIL, PROC_REF(wag))
@@ -32,15 +32,12 @@
else if(type in receiver.dna.species.external_organs)
receiver.add_mood_event("wrong_tail_regained", /datum/mood_event/tail_regained_wrong)
-/obj/item/organ/external/tail/Remove(mob/living/carbon/organ_owner, special, moving)
+/obj/item/organ/external/tail/on_mob_remove(mob/living/carbon/organ_owner, special)
+ . = ..()
+
if(wag_flags & WAG_WAGGING)
wag(organ_owner, start = FALSE)
- return ..()
-
-/obj/item/organ/external/tail/on_remove(mob/living/carbon/organ_owner, special)
- . = ..()
-
UnregisterSignal(organ_owner, COMSIG_ORGAN_WAG_TAIL)
if(type in organ_owner.dna.species.external_organs)
@@ -141,13 +138,13 @@
///A reference to the paired_spines, since for some fucking reason tail spines are tied to the spines themselves.
var/obj/item/organ/external/spines/paired_spines
-/obj/item/organ/external/tail/lizard/Insert(mob/living/carbon/reciever, special, drop_if_replaced)
+/obj/item/organ/external/tail/lizard/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(.)
- paired_spines = ownerlimb.owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_SPINES)
+ paired_spines = bodypart_owner.owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_SPINES)
paired_spines?.paired_tail = src
-/obj/item/organ/external/tail/lizard/Remove(mob/living/carbon/organ_owner, special, moving)
+/obj/item/organ/external/tail/lizard/Remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
if(paired_spines)
paired_spines.paired_tail = null
diff --git a/code/modules/surgery/organs/external/wings/functional_wings.dm b/code/modules/surgery/organs/external/wings/functional_wings.dm
index 58b9a709d87..d1f1eff6fc1 100644
--- a/code/modules/surgery/organs/external/wings/functional_wings.dm
+++ b/code/modules/surgery/organs/external/wings/functional_wings.dm
@@ -29,13 +29,13 @@
// grind_results = list(/datum/reagent/flightpotion = 5)
food_reagents = list(/datum/reagent/flightpotion = 5)
-/obj/item/organ/external/wings/functional/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/external/wings/functional/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(. && isnull(fly))
fly = new
fly.Grant(receiver)
-/obj/item/organ/external/wings/functional/Remove(mob/living/carbon/organ_owner, special, moving)
+/obj/item/organ/external/wings/functional/Remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
fly.Remove(organ_owner)
diff --git a/code/modules/surgery/organs/external/wings/moth_wings.dm b/code/modules/surgery/organs/external/wings/moth_wings.dm
index 88c1d5670fb..db1a14ca05d 100644
--- a/code/modules/surgery/organs/external/wings/moth_wings.dm
+++ b/code/modules/surgery/organs/external/wings/moth_wings.dm
@@ -14,13 +14,13 @@
///Store our old datum here for if our burned wings are healed
var/original_sprite_datum
-/obj/item/organ/external/wings/moth/on_insert(mob/living/carbon/receiver)
+/obj/item/organ/external/wings/moth/on_mob_insert(mob/living/carbon/receiver)
. = ..()
RegisterSignal(receiver, COMSIG_HUMAN_BURNING, PROC_REF(try_burn_wings))
RegisterSignal(receiver, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(heal_wings))
RegisterSignal(receiver, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(update_float_move))
-/obj/item/organ/external/wings/moth/on_remove(mob/living/carbon/organ_owner)
+/obj/item/organ/external/wings/moth/on_mob_remove(mob/living/carbon/organ_owner)
. = ..()
UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL, COMSIG_MOVABLE_PRE_MOVE))
REMOVE_TRAIT(organ_owner, TRAIT_FREE_FLOAT_MOVEMENT, REF(src))
diff --git a/code/modules/surgery/organs/internal/_internal_organ.dm b/code/modules/surgery/organs/internal/_internal_organ.dm
index ecab1c93da9..9f67fb3d899 100644
--- a/code/modules/surgery/organs/internal/_internal_organ.dm
+++ b/code/modules/surgery/organs/internal/_internal_organ.dm
@@ -5,19 +5,8 @@
. = ..()
START_PROCESSING(SSobj, src)
-/obj/item/organ/internal/Destroy()
- if(owner)
- // The special flag is important, because otherwise mobs can die
- // while undergoing transformation into different mobs.
- Remove(owner, special=TRUE)
- else
- STOP_PROCESSING(SSobj, src)
- return ..()
-
-/obj/item/organ/internal/Insert(mob/living/carbon/receiver, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
- if(!. || !owner)
- return
// organs_slot must ALWAYS be ordered in the same way as organ_process_order
// Otherwise life processing breaks down
@@ -25,18 +14,16 @@
STOP_PROCESSING(SSobj, src)
-/obj/item/organ/internal/Remove(mob/living/carbon/organ_owner, special = FALSE)
+/obj/item/organ/internal/on_mob_remove(mob/living/carbon/organ_owner, special = FALSE)
. = ..()
- if(organ_owner)
- if((organ_flags & ORGAN_VITAL) && !special && !(organ_owner.status_flags & GODMODE))
- if(organ_owner.stat != DEAD)
- organ_owner.investigate_log("has been killed by losing a vital organ ([src]).", INVESTIGATE_DEATHS)
- organ_owner.death()
+ if((organ_flags & ORGAN_VITAL) && !special && !(organ_owner.status_flags & GODMODE))
+ if(organ_owner.stat != DEAD)
+ organ_owner.investigate_log("has been killed by losing a vital organ ([src]).", INVESTIGATE_DEATHS)
+ organ_owner.death()
START_PROCESSING(SSobj, src)
-
/obj/item/organ/internal/process(seconds_per_tick, times_fired)
on_death(seconds_per_tick, times_fired) //Kinda hate doing it like this, but I really don't want to call process directly.
diff --git a/code/modules/surgery/organs/internal/appendix/_appendix.dm b/code/modules/surgery/organs/internal/appendix/_appendix.dm
index ee87d674dc4..e5190f1282e 100644
--- a/code/modules/surgery/organs/internal/appendix/_appendix.dm
+++ b/code/modules/surgery/organs/internal/appendix/_appendix.dm
@@ -76,12 +76,12 @@
/obj/item/organ/internal/appendix/get_availability(datum/species/owner_species, mob/living/owner_mob)
return owner_species.mutantappendix
-/obj/item/organ/internal/appendix/on_remove(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/appendix/on_mob_remove(mob/living/carbon/organ_owner)
. = ..()
REMOVE_TRAIT(organ_owner, TRAIT_DISEASELIKE_SEVERITY_MEDIUM, type)
organ_owner.med_hud_set_status()
-/obj/item/organ/internal/appendix/on_insert(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/appendix/on_mob_insert(mob/living/carbon/organ_owner)
. = ..()
if(inflamation_stage)
ADD_TRAIT(organ_owner, TRAIT_DISEASELIKE_SEVERITY_MEDIUM, type)
diff --git a/code/modules/surgery/organs/internal/appendix/appendix_golem.dm b/code/modules/surgery/organs/internal/appendix/appendix_golem.dm
index ede8dfa1e6b..5510b4bf967 100644
--- a/code/modules/surgery/organs/internal/appendix/appendix_golem.dm
+++ b/code/modules/surgery/organs/internal/appendix/appendix_golem.dm
@@ -12,7 +12,7 @@
. = ..()
smelter = new(src)
-/obj/item/organ/internal/appendix/golem/on_insert(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/appendix/golem/on_mob_insert(mob/living/carbon/organ_owner)
. = ..()
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(check_for_lava))
@@ -25,7 +25,7 @@
if (smelter.owner != owner)
smelter.Grant(owner)
-/obj/item/organ/internal/appendix/golem/on_remove(mob/living/carbon/organ_owner)
+/obj/item/organ/internal/appendix/golem/on_mob_remove(mob/living/carbon/organ_owner)
UnregisterSignal(organ_owner, COMSIG_MOVABLE_MOVED)
smelter?.Remove(organ_owner) // Might have been deleted by Destroy already
return ..()
diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm b/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm
index 94a4aba90b3..463e34977ee 100644
--- a/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm
+++ b/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm
@@ -76,7 +76,7 @@
to_chat(user, span_notice("You modify [src] to be installed on the [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm."))
update_appearance()
-/obj/item/organ/internal/cyberimp/arm/on_insert(mob/living/carbon/arm_owner)
+/obj/item/organ/internal/cyberimp/arm/on_mob_insert(mob/living/carbon/arm_owner)
. = ..()
var/side = zone == BODY_ZONE_R_ARM? RIGHT_HANDS : LEFT_HANDS
hand = arm_owner.hand_bodyparts[side]
@@ -84,7 +84,7 @@
RegisterSignal(hand, COMSIG_ITEM_ATTACK_SELF, PROC_REF(on_item_attack_self)) //If the limb gets an attack-self, open the menu. Only happens when hand is empty
RegisterSignal(arm_owner, COMSIG_KB_MOB_DROPITEM_DOWN, PROC_REF(dropkey)) //We're nodrop, but we'll watch for the drop hotkey anyway and then stow if possible.
-/obj/item/organ/internal/cyberimp/arm/on_remove(mob/living/carbon/arm_owner)
+/obj/item/organ/internal/cyberimp/arm/on_mob_remove(mob/living/carbon/arm_owner)
. = ..()
Retract()
if(hand)
@@ -379,14 +379,14 @@
///How long will the implant malfunction if it is EMP'd
var/emp_base_duration = 9 SECONDS
-/obj/item/organ/internal/cyberimp/arm/muscle/Insert(mob/living/carbon/receiver, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/cyberimp/arm/muscle/on_mob_insert(mob/living/carbon/arm_owner)
. = ..()
- if(ishuman(receiver)) //Sorry, only humans
- RegisterSignal(receiver, COMSIG_LIVING_EARLY_UNARMED_ATTACK, PROC_REF(on_attack_hand))
+ if(ishuman(arm_owner)) //Sorry, only humans
+ RegisterSignal(arm_owner, COMSIG_LIVING_EARLY_UNARMED_ATTACK, PROC_REF(on_attack_hand))
-/obj/item/organ/internal/cyberimp/arm/muscle/Remove(mob/living/carbon/implant_owner, special = 0)
+/obj/item/organ/internal/cyberimp/arm/muscle/on_mob_remove(mob/living/carbon/arm_owner)
. = ..()
- UnregisterSignal(implant_owner, COMSIG_LIVING_EARLY_UNARMED_ATTACK)
+ UnregisterSignal(arm_owner, COMSIG_LIVING_EARLY_UNARMED_ATTACK)
/obj/item/organ/internal/cyberimp/arm/muscle/emp_act(severity)
. = ..()
diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm b/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm
index d97df1d043b..513f0794c31 100644
--- a/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm
+++ b/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm
@@ -181,7 +181,7 @@
/datum/effect_system/trail_follow/ion \
)
-/obj/item/organ/internal/cyberimp/chest/thrusters/Remove(mob/living/carbon/thruster_owner, special = 0)
+/obj/item/organ/internal/cyberimp/chest/thrusters/Remove(mob/living/carbon/thruster_owner, special, movement_flags)
if(on)
deactivate(silent = TRUE)
..()
diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm b/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm
index 869d4cc1c9e..cec0241ece3 100644
--- a/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm
+++ b/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm
@@ -31,7 +31,7 @@
hud.show_to(eye_owner)
toggled_on = TRUE
-/obj/item/organ/internal/cyberimp/eyes/hud/Insert(mob/living/carbon/eye_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/cyberimp/eyes/hud/Insert(mob/living/carbon/eye_owner, special = FALSE, movement_flags)
. = ..()
if(!.)
return
@@ -42,7 +42,7 @@
ADD_TRAIT(eye_owner, HUD_trait, ORGAN_TRAIT)
toggled_on = TRUE
-/obj/item/organ/internal/cyberimp/eyes/hud/Remove(mob/living/carbon/eye_owner, special = FALSE)
+/obj/item/organ/internal/cyberimp/eyes/hud/Remove(mob/living/carbon/eye_owner, special, movement_flags)
. = ..()
if(HUD_type)
var/datum/atom_hud/hud = GLOB.huds[HUD_type]
diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm
index f0578832969..67a02e71d7e 100644
--- a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm
+++ b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm
@@ -87,7 +87,7 @@
stored_items = list()
-/obj/item/organ/internal/cyberimp/brain/anti_drop/Remove(mob/living/carbon/implant_owner, special = 0)
+/obj/item/organ/internal/cyberimp/brain/anti_drop/Remove(mob/living/carbon/implant_owner, special, movement_flags)
if(active)
ui_action_click()
..()
@@ -113,11 +113,11 @@
var/stun_cap_amount = 40
-/obj/item/organ/internal/cyberimp/brain/anti_stun/on_remove(mob/living/carbon/implant_owner)
+/obj/item/organ/internal/cyberimp/brain/anti_stun/on_mob_remove(mob/living/carbon/implant_owner)
. = ..()
UnregisterSignal(implant_owner, signalCache)
-/obj/item/organ/internal/cyberimp/brain/anti_stun/on_insert(mob/living/carbon/receiver)
+/obj/item/organ/internal/cyberimp/brain/anti_stun/on_mob_insert(mob/living/carbon/receiver)
. = ..()
RegisterSignals(receiver, signalCache, PROC_REF(on_signal))
diff --git a/code/modules/surgery/organs/internal/ears/_ears.dm b/code/modules/surgery/organs/internal/ears/_ears.dm
index b864e0f5b67..d0255d5a060 100644
--- a/code/modules/surgery/organs/internal/ears/_ears.dm
+++ b/code/modules/surgery/organs/internal/ears/_ears.dm
@@ -70,7 +70,7 @@
//NOVA EDIT REMOVAL BEGIN - CUSTOMIZATION
/*
-/obj/item/organ/internal/ears/cat/on_insert(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/cat/on_mob_insert(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner) && ear_owner.dna)
color = ear_owner.hair_color
@@ -78,7 +78,7 @@
ear_owner.dna.update_uf_block(DNA_EARS_BLOCK)
ear_owner.update_body()
-/obj/item/organ/internal/ears/cat/on_remove(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/cat/on_mob_remove(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner) && ear_owner.dna)
color = ear_owner.hair_color
@@ -91,13 +91,13 @@
name = "penguin ears"
desc = "The source of a penguin's happy feet."
-/obj/item/organ/internal/ears/penguin/on_insert(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/penguin/on_mob_insert(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner))
to_chat(ear_owner, span_notice("You suddenly feel like you've lost your balance."))
ear_owner.AddElement(/datum/element/waddling)
-/obj/item/organ/internal/ears/penguin/on_remove(mob/living/carbon/human/ear_owner)
+/obj/item/organ/internal/ears/penguin/on_mob_remove(mob/living/carbon/human/ear_owner)
. = ..()
if(istype(ear_owner))
to_chat(ear_owner, span_notice("Your sense of balance comes back to you."))
@@ -126,11 +126,11 @@
// The original idea was to use signals to do this not traits. Unfortunately, the star effect used for whispers applies before any relevant signals
// This seems like the least invasive solution
-/obj/item/organ/internal/ears/cybernetic/whisper/on_insert(mob/living/carbon/ear_owner)
+/obj/item/organ/internal/ears/cybernetic/whisper/on_mob_insert(mob/living/carbon/ear_owner)
. = ..()
ADD_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
-/obj/item/organ/internal/ears/cybernetic/whisper/on_remove(mob/living/carbon/ear_owner)
+/obj/item/organ/internal/ears/cybernetic/whisper/on_mob_remove(mob/living/carbon/ear_owner)
. = ..()
REMOVE_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
@@ -142,11 +142,11 @@
// Same sensitivity as felinid ears
damage_multiplier = 2
-/obj/item/organ/internal/ears/cybernetic/xray/on_insert(mob/living/carbon/ear_owner)
+/obj/item/organ/internal/ears/cybernetic/xray/on_mob_insert(mob/living/carbon/ear_owner)
. = ..()
ADD_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
-/obj/item/organ/internal/ears/cybernetic/xray/on_remove(mob/living/carbon/ear_owner)
+/obj/item/organ/internal/ears/cybernetic/xray/on_mob_remove(mob/living/carbon/ear_owner)
. = ..()
REMOVE_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm
index f8b3286ff22..e52b772479e 100644
--- a/code/modules/surgery/organs/internal/eyes/_eyes.dm
+++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm
@@ -52,7 +52,7 @@
/// Native FOV that will be applied if a config is enabled
var/native_fov = NONE //NOVA EDIT CHANGE - ORIGINAL: var/native_fov = FOV_90_DEGREES
-/obj/item/organ/internal/eyes/Insert(mob/living/carbon/eye_recipient, special = FALSE, drop_if_replaced = FALSE)
+/obj/item/organ/internal/eyes/Insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags = DELETE_IF_REPLACED)
// If we don't do this before everything else, heterochromia will be reset leading to eye_color_right no longer being accurate
if(ishuman(eye_recipient))
var/mob/living/carbon/human/human_recipient = eye_recipient
@@ -99,7 +99,7 @@
if(call_update)
affected_human.update_body()
-/obj/item/organ/internal/eyes/Remove(mob/living/carbon/eye_owner, special = FALSE)
+/obj/item/organ/internal/eyes/Remove(mob/living/carbon/eye_owner, special, movement_flags)
. = ..()
if(ishuman(eye_owner))
var/mob/living/carbon/human/human_owner = eye_owner
@@ -109,7 +109,8 @@
human_owner.eye_color_right = old_eye_color_right
if(native_fov)
eye_owner.remove_fov_trait(type)
- human_owner.update_body()
+ if(!special)
+ human_owner.update_body()
// Cure blindness from eye damage
eye_owner.cure_blind(EYE_DAMAGE)
@@ -353,11 +354,11 @@
eye_color_right = "000"
sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS
-/obj/item/organ/internal/eyes/robotic/xray/on_insert(mob/living/carbon/eye_owner)
+/obj/item/organ/internal/eyes/robotic/xray/on_mob_insert(mob/living/carbon/eye_owner)
. = ..()
ADD_TRAIT(eye_owner, TRAIT_XRAY_VISION, ORGAN_TRAIT)
-/obj/item/organ/internal/eyes/robotic/xray/on_remove(mob/living/carbon/eye_owner)
+/obj/item/organ/internal/eyes/robotic/xray/on_mob_remove(mob/living/carbon/eye_owner)
. = ..()
REMOVE_TRAIT(eye_owner, TRAIT_XRAY_VISION, ORGAN_TRAIT)
@@ -385,7 +386,7 @@
/obj/item/organ/internal/eyes/robotic/flashlight/emp_act(severity)
return
-/obj/item/organ/internal/eyes/robotic/flashlight/on_insert(mob/living/carbon/victim)
+/obj/item/organ/internal/eyes/robotic/flashlight/on_mob_insert(mob/living/carbon/victim)
. = ..()
if(!eye)
eye = new /obj/item/flashlight/eyelight()
@@ -394,7 +395,7 @@
eye.update_brightness(victim)
victim.become_blind(FLASHLIGHT_EYES)
-/obj/item/organ/internal/eyes/robotic/flashlight/on_remove(mob/living/carbon/victim)
+/obj/item/organ/internal/eyes/robotic/flashlight/on_mob_remove(mob/living/carbon/victim)
. = ..()
eye.set_light_on(FALSE)
eye.update_brightness(victim)
@@ -451,18 +452,18 @@
deactivate(close_ui = TRUE)
/// Set the initial color of the eyes on insert to be the mob's previous eye color.
-/obj/item/organ/internal/eyes/robotic/glow/Insert(mob/living/carbon/eye_recipient, special = FALSE, drop_if_replaced = FALSE)
+/obj/item/organ/internal/eyes/robotic/glow/Insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
left_eye_color_string = old_eye_color_left
right_eye_color_string = old_eye_color_right
update_mob_eye_color(eye_recipient)
-/obj/item/organ/internal/eyes/robotic/glow/on_insert(mob/living/carbon/eye_recipient)
+/obj/item/organ/internal/eyes/robotic/glow/on_mob_insert(mob/living/carbon/eye_recipient)
. = ..()
deactivate(close_ui = TRUE)
eye.forceMove(eye_recipient)
-/obj/item/organ/internal/eyes/robotic/glow/on_remove(mob/living/carbon/eye_owner)
+/obj/item/organ/internal/eyes/robotic/glow/on_mob_remove(mob/living/carbon/eye_owner)
deactivate(eye_owner, close_ui = TRUE)
if(!QDELETED(eye))
eye.forceMove(src)
@@ -750,7 +751,7 @@
high_light_cutoff = list(30, 35, 50)
var/obj/item/flashlight/eyelight/adapted/adapt_light
-/obj/item/organ/internal/eyes/night_vision/maintenance_adapted/on_insert(mob/living/carbon/eye_owner)
+/obj/item/organ/internal/eyes/night_vision/maintenance_adapted/on_mob_insert(mob/living/carbon/eye_owner)
. = ..()
//add lighting
if(!adapt_light)
@@ -769,7 +770,7 @@
apply_organ_damage(-10) //heal quickly
. = ..()
-/obj/item/organ/internal/eyes/night_vision/maintenance_adapted/Remove(mob/living/carbon/unadapted, special = FALSE)
+/obj/item/organ/internal/eyes/night_vision/maintenance_adapted/on_mob_remove(mob/living/carbon/unadapted, special = FALSE)
//remove lighting
adapt_light.set_light_on(FALSE)
adapt_light.update_brightness(unadapted)
diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm
index 1d3da14fa00..2773f588b24 100644
--- a/code/modules/surgery/organs/internal/heart/_heart.dm
+++ b/code/modules/surgery/organs/internal/heart/_heart.dm
@@ -33,7 +33,7 @@
. = ..()
icon_state = "[base_icon_state]-[beating ? "on" : "off"]"
-/obj/item/organ/internal/heart/Remove(mob/living/carbon/heartless, special = 0)
+/obj/item/organ/internal/heart/Remove(mob/living/carbon/heartless, special, movement_flags)
. = ..()
if(!special)
addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 12 SECONDS)
@@ -146,12 +146,14 @@
else
return ..()
-/obj/item/organ/internal/heart/cursed/on_insert(mob/living/carbon/accursed)
+/obj/item/organ/internal/heart/cursed/on_mob_insert(mob/living/carbon/accursed)
. = ..()
+
accursed.AddComponent(/datum/component/manual_heart, pump_delay = pump_delay, blood_loss = blood_loss, heal_brute = heal_brute, heal_burn = heal_burn, heal_oxy = heal_oxy)
-/obj/item/organ/internal/heart/cursed/Remove(mob/living/carbon/accursed, special = FALSE)
+/obj/item/organ/internal/heart/cursed/on_mob_remove(mob/living/carbon/accursed, special = FALSE)
. = ..()
+
qdel(accursed.GetComponent(/datum/component/manual_heart))
/obj/item/organ/internal/heart/cybernetic
diff --git a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
index 78ee55b0b28..50659115872 100644
--- a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
+++ b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
@@ -21,7 +21,7 @@
add_atom_colour(ethereal_color, FIXED_COLOUR_PRIORITY)
update_appearance()
-/obj/item/organ/internal/heart/ethereal/Insert(mob/living/carbon/heart_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/heart/ethereal/Insert(mob/living/carbon/heart_owner, special = FALSE, movement_flags)
. = ..()
if(!.)
return
@@ -29,7 +29,7 @@
RegisterSignal(heart_owner, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(on_owner_fully_heal))
RegisterSignal(heart_owner, COMSIG_QDELETING, PROC_REF(owner_deleted))
-/obj/item/organ/internal/heart/ethereal/Remove(mob/living/carbon/heart_owner, special = FALSE)
+/obj/item/organ/internal/heart/ethereal/Remove(mob/living/carbon/heart_owner, special, movement_flags)
UnregisterSignal(heart_owner, list(COMSIG_MOB_STATCHANGE, COMSIG_LIVING_POST_FULLY_HEAL, COMSIG_QDELETING))
REMOVE_TRAIT(heart_owner, TRAIT_CORPSELOCKED, SPECIES_TRAIT)
stop_crystalization_process(heart_owner)
diff --git a/code/modules/surgery/organs/internal/liver/_liver.dm b/code/modules/surgery/organs/internal/liver/_liver.dm
index c8515582ac1..d1d0df8f362 100644
--- a/code/modules/surgery/organs/internal/liver/_liver.dm
+++ b/code/modules/surgery/organs/internal/liver/_liver.dm
@@ -61,12 +61,12 @@
qdel(GetComponent(/datum/component/squeak))
/// Registers COMSIG_SPECIES_HANDLE_CHEMICAL from owner
-/obj/item/organ/internal/liver/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/liver/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
RegisterSignal(organ_owner, COMSIG_SPECIES_HANDLE_CHEMICAL, PROC_REF(handle_chemical))
/// Unregisters COMSIG_SPECIES_HANDLE_CHEMICAL from owner
-/obj/item/organ/internal/liver/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/liver/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_SPECIES_HANDLE_CHEMICAL)
@@ -134,7 +134,7 @@
return
var/obj/belly = owner.get_organ_slot(ORGAN_SLOT_STOMACH)
- var/list/cached_reagents = owner.reagents.reagent_list
+ var/list/cached_reagents = owner.reagents?.reagent_list
var/liver_damage = 0
var/provide_pain_message = HAS_NO_TOXIN
@@ -152,7 +152,7 @@
if(provide_pain_message != HAS_PAINFUL_TOXIN)
provide_pain_message = toxin.silent_toxin ? HAS_SILENT_TOXIN : HAS_PAINFUL_TOXIN
- owner.reagents.metabolize(owner, seconds_per_tick, times_fired, can_overdose = TRUE)
+ owner.reagents?.metabolize(owner, seconds_per_tick, times_fired, can_overdose = TRUE)
if(liver_damage)
apply_organ_damage(min(liver_damage * seconds_per_tick , MAX_TOXIN_LIVER_DAMAGE * seconds_per_tick))
diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm
index 2e99f70d989..874ec320648 100644
--- a/code/modules/surgery/organs/internal/lungs/_lungs.dm
+++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm
@@ -153,7 +153,7 @@
add_gas_reaction(/datum/gas/zauker, while_present = PROC_REF(too_much_zauker))
///Simply exists so that you don't keep any alerts from your previous lack of lungs.
-/obj/item/organ/internal/lungs/Insert(mob/living/carbon/receiver, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/lungs/Insert(mob/living/carbon/receiver, special = FALSE, movement_flags)
. = ..()
if(!.)
return .
@@ -163,7 +163,7 @@
receiver.clear_alert(ALERT_NOT_ENOUGH_PLASMA)
receiver.clear_alert(ALERT_NOT_ENOUGH_N2O)
-/obj/item/organ/internal/lungs/Remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/lungs/Remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
// This is very "manual" I realize, but it's useful to ensure cleanup for gases we're removing happens
// Avoids stuck alerts and such
diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm
index a1d615041e2..7916606f2df 100644
--- a/code/modules/surgery/organs/internal/stomach/_stomach.dm
+++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm
@@ -55,7 +55,7 @@
var/mob/living/carbon/body = owner
// digest food, sent all reagents that can metabolize to the body
- for(var/datum/reagent/bit as anything in reagents.reagent_list)
+ for(var/datum/reagent/bit as anything in reagents?.reagent_list)
// If the reagent does not metabolize then it will sit in the stomach
// This has an effect on items like plastic causing them to take up space in the stomach
@@ -93,7 +93,7 @@
return
//We are checking if we have nutriment in a damaged stomach.
- var/datum/reagent/nutri = locate(/datum/reagent/consumable/nutriment) in reagents.reagent_list
+ var/datum/reagent/nutri = locate(/datum/reagent/consumable/nutriment) in reagents?.reagent_list
//No nutriment found lets exit out
if(!nutri)
return
@@ -263,7 +263,7 @@
disgusted.throw_alert(ALERT_DISGUST, /atom/movable/screen/alert/disgusted)
disgusted.add_mood_event("disgust", /datum/mood_event/disgusted)
-/obj/item/organ/internal/stomach/Remove(mob/living/carbon/stomach_owner, special = 0)
+/obj/item/organ/internal/stomach/Remove(mob/living/carbon/stomach_owner, special, movement_flags)
if(ishuman(stomach_owner))
var/mob/living/carbon/human/human_owner = owner
human_owner.clear_alert(ALERT_DISGUST)
diff --git a/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm b/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm
index c1632f33329..68f9d9428a0 100644
--- a/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm
+++ b/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm
@@ -13,12 +13,12 @@
adjust_charge(-ETHEREAL_CHARGE_FACTOR * seconds_per_tick)
handle_charge(owner, seconds_per_tick, times_fired)
-/obj/item/organ/internal/stomach/ethereal/on_insert(mob/living/carbon/stomach_owner)
+/obj/item/organ/internal/stomach/ethereal/on_mob_insert(mob/living/carbon/stomach_owner)
. = ..()
RegisterSignal(stomach_owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(charge))
RegisterSignal(stomach_owner, COMSIG_LIVING_ELECTROCUTE_ACT, PROC_REF(on_electrocute))
-/obj/item/organ/internal/stomach/ethereal/on_remove(mob/living/carbon/stomach_owner)
+/obj/item/organ/internal/stomach/ethereal/on_mob_remove(mob/living/carbon/stomach_owner)
. = ..()
UnregisterSignal(stomach_owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT)
UnregisterSignal(stomach_owner, COMSIG_LIVING_ELECTROCUTE_ACT)
diff --git a/code/modules/surgery/organs/internal/stomach/stomach_golem.dm b/code/modules/surgery/organs/internal/stomach/stomach_golem.dm
index 5f721fa52b1..a1f5ce6c70e 100644
--- a/code/modules/surgery/organs/internal/stomach/stomach_golem.dm
+++ b/code/modules/surgery/organs/internal/stomach/stomach_golem.dm
@@ -11,11 +11,11 @@
/// How slow are you if you have absolutely nothing in the tank?
var/max_hunger_slowdown = 4
-/obj/item/organ/internal/stomach/golem/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/stomach/golem/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
RegisterSignal(owner, COMSIG_CARBON_ATTEMPT_EAT, PROC_REF(try_eating))
-/obj/item/organ/internal/stomach/golem/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/stomach/golem/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_CARBON_ATTEMPT_EAT)
organ_owner.remove_movespeed_modifier(/datum/movespeed_modifier/golem_hunger)
diff --git a/code/modules/surgery/organs/internal/tongue/_tongue.dm b/code/modules/surgery/organs/internal/tongue/_tongue.dm
index c0dcf47fbea..50e4e8fa478 100644
--- a/code/modules/surgery/organs/internal/tongue/_tongue.dm
+++ b/code/modules/surgery/organs/internal/tongue/_tongue.dm
@@ -125,7 +125,7 @@
food_taste_reaction = FOOD_LIKED
return food_taste_reaction
-/obj/item/organ/internal/tongue/Insert(mob/living/carbon/tongue_owner, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/tongue/Insert(mob/living/carbon/tongue_owner, special = FALSE, movement_flags)
. = ..()
if(!.)
return
@@ -144,7 +144,7 @@
ADD_TRAIT(tongue_owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
apply_tongue_effects()
-/obj/item/organ/internal/tongue/Remove(mob/living/carbon/tongue_owner, special = FALSE)
+/obj/item/organ/internal/tongue/Remove(mob/living/carbon/tongue_owner, special, movement_flags)
. = ..()
temp_say_mod = ""
UnregisterSignal(tongue_owner, COMSIG_MOB_SAY)
diff --git a/code/modules/surgery/organs/organ_movement.dm b/code/modules/surgery/organs/organ_movement.dm
new file mode 100644
index 00000000000..bee717fb764
--- /dev/null
+++ b/code/modules/surgery/organs/organ_movement.dm
@@ -0,0 +1,230 @@
+// There are two kinds of organ movement: mob movement and limb movement
+// If you pull someones brain out, you remove it from the mob and the limb
+// If you take someones head off, you remove it from the mob but not the limb
+// If you remove the brain from an already decapitated head, you remove it from the limb but not the mob
+
+// Keep the seperation of limb removal and mob removal absolute
+
+/*
+ * Insert the organ into the select mob.
+ *
+ * receiver - the mob who will get our organ
+ * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
+ * movement_flags - Flags for how we behave in movement. See DEFINES/organ_movement for flags
+ */
+/obj/item/organ/proc/Insert(mob/living/carbon/receiver, special = FALSE, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ mob_insert(receiver, special, movement_flags)
+ bodypart_insert(limb_owner = receiver, movement_flags = movement_flags)
+
+ return TRUE
+
+/*
+ * Remove the organ from the select mob.
+ *
+ * * organ_owner - the mob who owns our organ, that we're removing the organ from. Can be null
+ * * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
+ */
+/obj/item/organ/proc/Remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ mob_remove(organ_owner, special, movement_flags)
+ bodypart_remove(limb_owner = organ_owner, movement_flags = movement_flags)
+
+/*
+ * Insert the organ into the select mob.
+ *
+ * receiver - the mob who will get our organ
+ * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
+ * movement_flags - Flags for how we behave in movement. See DEFINES/organ_movement for flags
+ */
+/obj/item/organ/proc/mob_insert(mob/living/carbon/receiver, special, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(!iscarbon(receiver))
+ stack_trace("Tried to insert organ into non-carbon: [receiver.type]")
+ return
+
+ if(owner == receiver)
+ stack_trace("Organ receiver is already organ owner")
+ return
+
+ var/obj/item/organ/replaced = receiver.get_organ_slot(slot)
+ if(replaced)
+ replaced.Remove(receiver, special = TRUE)
+ if(movement_flags & DELETE_IF_REPLACED)
+ qdel(replaced)
+ else
+ replaced.forceMove(get_turf(receiver))
+
+ if(!IS_ROBOTIC_ORGAN(src) && (organ_flags & ORGAN_VIRGIN))
+ blood_dna_info = receiver.get_blood_dna_list()
+ // need to remove the synethic blood DNA that is initialized
+ // wash also adds the blood dna again
+ wash(CLEAN_TYPE_BLOOD)
+ organ_flags &= ~ORGAN_VIRGIN
+
+ receiver.organs |= src
+ receiver.organs_slot[slot] = src
+ owner = receiver
+
+ on_mob_insert(receiver, special)
+
+ return TRUE
+
+/// Called after the organ is inserted into a mob.
+/// Adds Traits, Actions, and Status Effects on the mob in which the organ is impanted.
+/// Override this proc to create unique side-effects for inserting your organ. Must be called by overrides.
+/obj/item/organ/proc/on_mob_insert(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ for(var/trait in organ_traits)
+ ADD_TRAIT(organ_owner, trait, REF(src))
+
+ for(var/datum/action/action as anything in actions)
+ action.Grant(organ_owner)
+
+ for(var/datum/status_effect/effect as anything in organ_effects)
+ organ_owner.apply_status_effect(effect, type)
+
+ RegisterSignal(owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_owner_examine))
+ SEND_SIGNAL(src, COMSIG_ORGAN_IMPLANTED, organ_owner)
+ SEND_SIGNAL(organ_owner, COMSIG_CARBON_GAIN_ORGAN, src, special)
+
+/// Insert an organ into a limb, assume the limb as always detached and include no owner operations here (except the get_bodypart helper here I guess)
+/// Give EITHER a limb OR a limb owner
+/obj/item/organ/proc/bodypart_insert(obj/item/bodypart/bodypart, mob/living/carbon/limb_owner, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(limb_owner)
+ bodypart = limb_owner.get_bodypart(deprecise_zone(zone))
+
+ // The true movement
+ forceMove(bodypart)
+ bodypart.contents |= src
+ bodypart_owner = bodypart
+
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(forced_removal))
+
+ // Apply unique side-effects. Return value does not matter.
+ on_bodypart_insert(bodypart)
+
+ return TRUE
+
+/// Add any limb specific effects you might want here
+/obj/item/organ/proc/on_bodypart_insert(obj/item/bodypart/limb, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ item_flags |= ABSTRACT
+ ADD_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
+
+/*
+ * Remove the organ from the select mob.
+ *
+ * * organ_owner - the mob who owns our organ, that we're removing the organ from. Can be null
+ * * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
+ */
+/obj/item/organ/proc/mob_remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(organ_owner)
+ if(organ_owner.organs_slot[slot] == src)
+ organ_owner.organs_slot.Remove(slot)
+ organ_owner.organs -= src
+
+ owner = null
+
+ on_mob_remove(organ_owner, special)
+
+ return TRUE
+
+/// Called after the organ is removed from a mob.
+/// Removes Traits, Actions, and Status Effects on the mob in which the organ was impanted.
+/// Override this proc to create unique side-effects for removing your organ. Must be called by overrides.
+/obj/item/organ/proc/on_mob_remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(!iscarbon(organ_owner))
+ stack_trace("Organ removal should not be happening on non carbon mobs: [organ_owner]")
+
+ for(var/trait in organ_traits)
+ REMOVE_TRAIT(organ_owner, trait, REF(src))
+
+ for(var/datum/action/action as anything in actions)
+ action.Remove(organ_owner)
+
+ for(var/datum/status_effect/effect as anything in organ_effects)
+ organ_owner.remove_status_effect(effect, type)
+
+ UnregisterSignal(organ_owner, COMSIG_ATOM_EXAMINE)
+ SEND_SIGNAL(src, COMSIG_ORGAN_REMOVED, organ_owner)
+ SEND_SIGNAL(organ_owner, COMSIG_CARBON_LOSE_ORGAN, src, special)
+
+ var/list/diseases = organ_owner.get_static_viruses()
+ if(!LAZYLEN(diseases))
+ return
+
+ var/list/datum/disease/diseases_to_add = list()
+ for(var/datum/disease/disease as anything in diseases)
+ // robotic organs are immune to disease unless 'inorganic biology' symptom is present
+ if(IS_ROBOTIC_ORGAN(src) && !(disease.infectable_biotypes & MOB_ROBOTIC))
+ continue
+
+ // admin or special viruses that should not be reproduced
+ if(disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS))
+ continue
+
+ diseases_to_add += disease
+
+ if(LAZYLEN(diseases_to_add))
+ AddComponent(/datum/component/infective, diseases_to_add)
+
+/// Called to remove an organ from a limb. Do not put any mob operations here (except the bodypart_getter at the start)
+/// Give EITHER a limb OR a limb_owner
+/obj/item/organ/proc/bodypart_remove(obj/item/bodypart/limb, mob/living/carbon/limb_owner, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(!isnull(limb_owner))
+ limb = limb_owner.get_bodypart(deprecise_zone(zone))
+
+ UnregisterSignal(src, COMSIG_MOVABLE_MOVED) //DONT MOVE THIS!!!! we moves the organ right after, so we unregister before we move them physically
+
+ // The true movement is here
+ moveToNullspace()
+ bodypart_owner.contents -= src
+ bodypart_owner = null
+
+ on_bodypart_remove(limb)
+
+ return TRUE
+
+/// Called on limb removal to remove limb specific limb effects or statusses
+/obj/item/organ/proc/on_bodypart_remove(obj/item/bodypart/limb, movement_flags)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(!IS_ROBOTIC_ORGAN(src) && !(item_flags & NO_BLOOD_ON_ITEM) && !QDELING(src))
+ AddElement(/datum/element/decal/blood)
+
+ item_flags &= ~ABSTRACT
+ REMOVE_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
+
+/// In space station videogame, nothing is sacred. If somehow an organ is removed unexpectedly, handle it properly
+/obj/item/organ/proc/forced_removal()
+ SIGNAL_HANDLER
+
+ if(owner)
+ Remove(owner)
+ else if(bodypart_owner)
+ bodypart_remove(bodypart_owner)
+ else
+ stack_trace("Force removed an already removed organ!")
+
+/**
+ * Proc that gets called when the organ is surgically removed by someone, can be used for special effects
+ * Currently only used so surplus organs can explode when surgically removed.
+ */
+/obj/item/organ/proc/on_surgical_removal(mob/living/user, mob/living/carbon/old_owner, target_zone, obj/item/tool)
+ SHOULD_CALL_PARENT(TRUE)
+ SEND_SIGNAL(src, COMSIG_ORGAN_SURGICALLY_REMOVED, user, old_owner, target_zone, tool)
+ RemoveElement(/datum/element/decal/blood)
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 84039c9bb66..a97246f908e 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -195,6 +195,7 @@
#include "objectives.dm"
#include "operating_table.dm"
#include "orderable_items.dm"
+#include "organ_bodypart_shuffle.dm"
#include "organ_set_bonus.dm"
#include "organs.dm"
#include "outfit_sanity.dm"
diff --git a/code/modules/unit_tests/hallucination_icons.dm b/code/modules/unit_tests/hallucination_icons.dm
index 6477e94714c..c9f9cf8237c 100644
--- a/code/modules/unit_tests/hallucination_icons.dm
+++ b/code/modules/unit_tests/hallucination_icons.dm
@@ -19,7 +19,7 @@
// Test preset delusion hallucinations for invalid image setups
for(var/datum/hallucination/delusion/preset/hallucination as anything in subtypesof(/datum/hallucination/delusion/preset))
- if(initial(hallucination.dynamic_icon))
+ if(initial(hallucination.dynamic_delusion))
continue
var/icon = initial(hallucination.delusion_icon_file)
var/icon_state = initial(hallucination.delusion_icon_state)
diff --git a/code/modules/unit_tests/heretic_rituals.dm b/code/modules/unit_tests/heretic_rituals.dm
index 4ac5bce8d3d..b55136cface 100644
--- a/code/modules/unit_tests/heretic_rituals.dm
+++ b/code/modules/unit_tests/heretic_rituals.dm
@@ -66,7 +66,10 @@
if(islist(ritual_item_path))
ritual_item_path = pick(ritual_item_path)
for(var/i in 1 to amount_to_create)
- created_atoms += new ritual_item_path(get_turf(our_heretic))
+ var/obj/item/item = new ritual_item_path(get_turf(our_heretic))
+ if(isitem(item))
+ item.item_flags &= ~ABSTRACT
+ created_atoms += item
// Now, we can ACTUALLY run the ritual. Let's do it.
// Attempt to run the knowledge via the sacrifice rune.
@@ -106,6 +109,10 @@
for(var/atom/thing as anything in nearby_atoms)
if(!ismovable(thing))
continue
+ if(isitem(thing))
+ var/obj/item/item = thing
+ if(item.item_flags & ABSTRACT) //bodyparts and stuff will get registered otherwise
+ continue
// There are atoms around the rune still, and there shouldn't be.
// All component atoms were consumed, and all resulting atoms were cleaned up.
diff --git a/code/modules/unit_tests/human_through_recycler.dm b/code/modules/unit_tests/human_through_recycler.dm
index 7d554d72690..c51b9a0e30b 100644
--- a/code/modules/unit_tests/human_through_recycler.dm
+++ b/code/modules/unit_tests/human_through_recycler.dm
@@ -17,8 +17,13 @@
TEST_ASSERT_EQUAL(damage_incurred, chewer.crush_damage, "Assistant did not take the expected amount of brute damage ([chewer.crush_damage]) from the emagged recycler! Took ([damage_incurred]) instead.")
TEST_ASSERT(chewer.bloody, "The emagged recycler did not become bloody after crushing the assistant!")
+ var/list/bad_contents = assistant.contents
+ for(var/obj/item/item in assistant.contents)
+ if(item.item_flags & ABSTRACT)
+ bad_contents -= item
+
// Now, let's test to see if all of their clothing got properly deleted.
- TEST_ASSERT_EQUAL(length(assistant.contents), 0, "Assistant still has items in its contents after being put through an emagged recycler!")
+ TEST_ASSERT_EQUAL(length(bad_contents), 0, "Assistant still has items in its contents after being put through an emagged recycler!")
// Consistent Assistants will always have the following: ID, PDA, backpack, a uniform, a headset, and a pair of shoes. If any of these are still present, then the recycler did not properly delete the assistant's clothing.
// However, let's check for EVERYTHING just in case, because we don't want to miss anything.
// This is just what we expect to be deleted.
diff --git a/code/modules/unit_tests/ling_decap.dm b/code/modules/unit_tests/ling_decap.dm
index 0c964f9a043..5d6f01caf9a 100644
--- a/code/modules/unit_tests/ling_decap.dm
+++ b/code/modules/unit_tests/ling_decap.dm
@@ -9,7 +9,7 @@
var/obj/item/bodypart/head/noggin = ling.get_bodypart(BODY_ZONE_HEAD)
noggin.dismember()
TEST_ASSERT_NULL(ling.get_bodypart(BODY_ZONE_HEAD), "Changeling failed to be decapitated.")
- TEST_ASSERT_NULL(noggin.brainmob.mind, "Changeling's mind was moved to their head after decapitation, but it should have remained in their body.")
+ TEST_ASSERT_NULL(noggin.brain.brainmob.mind, "Changeling's mind was moved to their brain after decapitation, but it should have remained in their body.")
var/obj/item/organ/internal/brain/oldbrain = noggin.brain
noggin.drop_organs()
@@ -33,7 +33,7 @@
var/obj/item/bodypart/head/noggin = normal_guy.get_bodypart(BODY_ZONE_HEAD)
noggin.dismember()
- TEST_ASSERT_EQUAL(noggin.brainmob.mind, my_guys_mind, "Dummy's mind was not moved to their head after decapitation.")
+ TEST_ASSERT_EQUAL(noggin.brain.brainmob.mind, my_guys_mind, "Dummy's mind was not moved to their brain after decapitation.")
var/obj/item/organ/internal/brain/oldbrain = noggin.brain
noggin.drop_organs()
diff --git a/code/modules/unit_tests/organ_bodypart_shuffle.dm b/code/modules/unit_tests/organ_bodypart_shuffle.dm
new file mode 100644
index 00000000000..842dd1c6c13
--- /dev/null
+++ b/code/modules/unit_tests/organ_bodypart_shuffle.dm
@@ -0,0 +1,34 @@
+/// Moves organs in and out of bodyparts, and moves the bodyparts around to see if someone didn't fuck up their movement
+/datum/unit_test/organ_bodypart_shuffle
+
+/datum/unit_test/organ_bodypart_shuffle/Run()
+ var/mob/living/carbon/human/hollow_boy = allocate(/mob/living/carbon/human/consistent)
+
+ // Test if organs are all properly updating when forcefully removed
+ var/list/removed_organs = list()
+
+ for(var/obj/item/organ/organ as anything in hollow_boy.organs)
+ organ.moveToNullspace()
+ removed_organs += organ
+
+ for(var/obj/item/organ/organ as anything in removed_organs)
+ TEST_ASSERT(!(organ in hollow_boy.organs), "Organ '[organ.name] remained inside human after forceMove into nullspace.")
+ TEST_ASSERT(organ.loc == null, "Organ '[organ.name] did not move to nullspace after being forced to.")
+ TEST_ASSERT(!(organ.owner), "Organ '[organ.name] kept reference to human after forceMove into nullspace.")
+ TEST_ASSERT(!(organ.bodypart_owner), "Organ '[organ.name] kept reference to bodypart after forceMove into nullspace.")
+
+ for(var/obj/item/bodypart/bodypart as anything in hollow_boy.bodyparts)
+ bodypart = new bodypart.type() //fresh, duplice bodypart with no insides
+ for(var/obj/item/organ/organ as anything in removed_organs)
+ if(bodypart.body_zone != deprecise_zone(organ.zone))
+ continue
+ organ.bodypart_insert(bodypart) // Put all the old organs back in
+ bodypart.replace_limb(hollow_boy) //so stick new bodyparts on them with their old organs
+ // Check if, after we put the old organs in a new limb, and after we put that new limb on the mob, if the organs came with
+ for(var/obj/item/organ/organ as anything in removed_organs) //technically readded organ now
+ if(bodypart.body_zone != deprecise_zone(organ.zone))
+ continue
+ TEST_ASSERT(organ in hollow_boy.organs, "Organ '[organ.name] was put in an empty bodypart that replaced a humans, but the organ did not come with.")
+
+ // Test if bodyparts are all properly updating when forcefully removed
+ hollow_boy = allocate(/mob/living/carbon/human/consistent) //freshly filled with wet insides
diff --git a/code/modules/unit_tests/organ_set_bonus.dm b/code/modules/unit_tests/organ_set_bonus.dm
index 06d9ba9f2e5..e89769fe7cf 100644
--- a/code/modules/unit_tests/organ_set_bonus.dm
+++ b/code/modules/unit_tests/organ_set_bonus.dm
@@ -31,7 +31,7 @@
// Attempt to insert entire list of mutant organs for the given infusion_entry.
for(var/obj/item/organ/organ as anything in output_organs)
organ = new organ()
- TEST_ASSERT(organ.Insert(lab_rat, special = TRUE, drop_if_replaced = FALSE), "The organ `[organ.type]` for `[infuser_entry.type]` was not inserted in the mob when expected, Insert() returned falsy when TRUE was expected.")
+ TEST_ASSERT(organ.Insert(lab_rat, special = TRUE, movement_flags = DELETE_IF_REPLACED), "The organ `[organ.type]` for `[infuser_entry.type]` was not inserted in the mob when expected, Insert() returned falsy when TRUE was expected.")
inserted_organs += organ
// Search for added Status Effect.
diff --git a/code/modules/unit_tests/organs.dm b/code/modules/unit_tests/organs.dm
index 4ba51e0870c..1da3808ba39 100644
--- a/code/modules/unit_tests/organs.dm
+++ b/code/modules/unit_tests/organs.dm
@@ -25,14 +25,10 @@
))
/datum/unit_test/organ_sanity/Run()
- for(var/obj/item/organ/organ_type as anything in subtypesof(/obj/item/organ))
+ for(var/obj/item/organ/organ_type as anything in subtypesof(/obj/item/organ) - test_organ_blacklist)
organ_test_insert(organ_type)
/datum/unit_test/organ_sanity/proc/organ_test_insert(obj/item/organ/organ_type)
- // Skip prototypes.
- if(test_organ_blacklist[organ_type])
- return
-
// Appropriate mob (Human) which will receive organ.
var/mob/living/carbon/human/lab_rat = allocate(/mob/living/carbon/human/consistent)
var/obj/item/organ/test_organ = new organ_type()
@@ -41,8 +37,8 @@
var/mob/living/basic/pet/dog/lab_dog = allocate(/mob/living/basic/pet/dog/corgi)
var/obj/item/organ/reject_organ = new organ_type()
- TEST_ASSERT(test_organ.Insert(lab_rat, special = TRUE, drop_if_replaced = FALSE), TEST_ORGAN_INSERT_MESSAGE(test_organ, "should return TRUE to indicate success."))
- TEST_ASSERT(!reject_organ.Insert(lab_dog, special = TRUE, drop_if_replaced = FALSE), TEST_ORGAN_INSERT_MESSAGE(test_organ, "shouldn't return TRUE when inserting into a basic mob (Corgi)."))
+ TEST_ASSERT(test_organ.Insert(lab_rat, special = TRUE, movement_flags = DELETE_IF_REPLACED), TEST_ORGAN_INSERT_MESSAGE(test_organ, "should return TRUE to indicate success."))
+ TEST_ASSERT(!reject_organ.Insert(lab_dog, special = TRUE, movement_flags = DELETE_IF_REPLACED), TEST_ORGAN_INSERT_MESSAGE(test_organ, "shouldn't return TRUE when inserting into a basic mob (Corgi)."))
// Species change swaps out all the organs, making test_organ un-usable by this point.
if(species_changing_organs[test_organ.type])
diff --git a/code/modules/unit_tests/species_change_organs.dm b/code/modules/unit_tests/species_change_organs.dm
index e0555aa89bc..41d55047f03 100644
--- a/code/modules/unit_tests/species_change_organs.dm
+++ b/code/modules/unit_tests/species_change_organs.dm
@@ -12,7 +12,7 @@
dummy.gain_trauma(/datum/brain_trauma/severe/blindness)
// Give a cyber heart
var/obj/item/organ/internal/heart/cybernetic/cyber_heart = allocate(/obj/item/organ/internal/heart/cybernetic)
- cyber_heart.Insert(dummy, special = TRUE, drop_if_replaced = FALSE)
+ cyber_heart.Insert(dummy, special = TRUE, movement_flags = DELETE_IF_REPLACED)
// Give one of their organs a bit of damage
var/obj/item/organ/internal/appendix/existing_appendix = dummy.get_organ_slot(ORGAN_SLOT_APPENDIX)
existing_appendix.set_organ_damage(25)
diff --git a/code/modules/unit_tests/tail_wag.dm b/code/modules/unit_tests/tail_wag.dm
index 0d828557953..ceb82e98c0d 100644
--- a/code/modules/unit_tests/tail_wag.dm
+++ b/code/modules/unit_tests/tail_wag.dm
@@ -6,7 +6,7 @@
/datum/unit_test/tail_wag/Run()
var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent)
var/obj/item/organ/external/tail/cat/dummy_tail = allocate(/obj/item/organ/external/tail/cat)
- dummy_tail.Insert(dummy, special = TRUE, drop_if_replaced = FALSE)
+ dummy_tail.Insert(dummy, special = TRUE, movement_flags = DELETE_IF_REPLACED)
// SANITY TEST
@@ -74,7 +74,7 @@
// TESTING MOB DEATH
// put it back and start wagging again
- dummy_tail.Insert(dummy, special = TRUE, drop_if_replaced = FALSE)
+ dummy_tail.Insert(dummy, special = TRUE, movement_flags = DELETE_IF_REPLACED)
SEND_SIGNAL(dummy, COMSIG_ORGAN_WAG_TAIL, TRUE)
if(!(dummy_tail.wag_flags & WAG_WAGGING))
TEST_FAIL("Tail did not start wagging when it should have!")
diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm
index d971b82a856..dc7304a1388 100644
--- a/code/modules/vehicles/mecha/_mecha.dm
+++ b/code/modules/vehicles/mecha/_mecha.dm
@@ -54,7 +54,7 @@
/// Keeps track of the mech's servo motor
var/obj/item/stock_parts/servo/servo
///Contains flags for the mecha
- var/mecha_flags = CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
+ var/mecha_flags = CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
///Spark effects are handled by this datum
var/datum/effect_system/spark_spread/spark_system
@@ -70,8 +70,6 @@
var/bumpsmash = FALSE
///////////ATMOS
- ///Whether the pilot is hidden from the outside viewers and whether the cabin can be sealed to be airtight
- var/enclosed = TRUE
///Whether the cabin exchanges gases with the environment
var/cabin_sealed = FALSE
///Internal air mix datum
@@ -397,7 +395,7 @@
/obj/vehicle/sealed/mecha/generate_actions()
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_eject)
- if(enclosed)
+ if(mecha_flags & IS_ENCLOSED)
initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/mech_toggle_cabin_seal, VEHICLE_CONTROL_SETTINGS)
if(can_use_overclock)
initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/mech_overclock)
@@ -468,7 +466,7 @@
. += span_warning("It's missing a capacitor.")
if(!scanmod)
. += span_warning("It's missing a scanning module.")
- if(enclosed)
+ if(mecha_flags & IS_ENCLOSED)
return
if(mecha_flags & SILICON_PILOT)
. += span_notice("[src] appears to be piloting itself...")
@@ -574,7 +572,7 @@
/obj/vehicle/sealed/mecha/proc/process_occupants(seconds_per_tick)
for(var/mob/living/occupant as anything in occupants)
- if(!enclosed && occupant?.incapacitated()) //no sides mean it's easy to just sorta fall out if you're incapacitated.
+ if(!(mecha_flags & IS_ENCLOSED) && occupant?.incapacitated()) //no sides mean it's easy to just sorta fall out if you're incapacitated.
mob_exit(occupant, randomstep = TRUE) //bye bye
continue
if(cell && cell.maxcharge)
@@ -725,12 +723,12 @@
/////////////////////////////////////
/obj/vehicle/sealed/mecha/remove_air(amount)
- if(enclosed && cabin_sealed)
+ if((mecha_flags & IS_ENCLOSED) && cabin_sealed)
return cabin_air.remove(amount)
return ..()
/obj/vehicle/sealed/mecha/return_air()
- if(enclosed && cabin_sealed)
+ if((mecha_flags & IS_ENCLOSED) && cabin_sealed)
return cabin_air
return ..()
@@ -749,7 +747,7 @@
///makes cabin unsealed, dumping cabin air outside or airtight filling the cabin with external air mix
/obj/vehicle/sealed/mecha/proc/set_cabin_seal(mob/user, cabin_sealed)
- if(!enclosed)
+ if(!(mecha_flags & IS_ENCLOSED))
balloon_alert(user, "cabin can't be sealed!")
log_message("Tried to seal cabin. This mech can't be airtight.", LOG_MECHA)
return
diff --git a/code/modules/vehicles/mecha/combat/gygax.dm b/code/modules/vehicles/mecha/combat/gygax.dm
index 223ab66ca31..82fd77f2289 100644
--- a/code/modules/vehicles/mecha/combat/gygax.dm
+++ b/code/modules/vehicles/mecha/combat/gygax.dm
@@ -47,7 +47,7 @@
force = 30
accesses = list(ACCESS_SYNDICATE)
wreckage = /obj/structure/mecha_wreckage/gygax/dark
- mecha_flags = ID_LOCK_ON | CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
+ mecha_flags = ID_LOCK_ON | CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
max_equip_by_category = list(
MECHA_L_ARM = 1,
MECHA_R_ARM = 1,
diff --git a/code/modules/vehicles/mecha/combat/honker.dm b/code/modules/vehicles/mecha/combat/honker.dm
index dddcb7e8225..a7c9f018d28 100644
--- a/code/modules/vehicles/mecha/combat/honker.dm
+++ b/code/modules/vehicles/mecha/combat/honker.dm
@@ -13,7 +13,7 @@
exit_delay = 40
accesses = list(ACCESS_MECH_SCIENCE, ACCESS_THEATRE)
wreckage = /obj/structure/mecha_wreckage/honker
- mecha_flags = CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
+ mecha_flags = CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
mech_type = EXOSUIT_MODULE_HONK
max_equip_by_category = list(
MECHA_L_ARM = 1,
@@ -47,7 +47,7 @@
max_temperature = 35000
accesses = list(ACCESS_SYNDICATE)
wreckage = /obj/structure/mecha_wreckage/honker/dark
- mecha_flags = ID_LOCK_ON | CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
+ mecha_flags = ID_LOCK_ON | CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
max_equip_by_category = list(
MECHA_L_ARM = 1,
MECHA_R_ARM = 1,
diff --git a/code/modules/vehicles/mecha/combat/marauder.dm b/code/modules/vehicles/mecha/combat/marauder.dm
index 2fe8da4bdc7..750223a85d7 100644
--- a/code/modules/vehicles/mecha/combat/marauder.dm
+++ b/code/modules/vehicles/mecha/combat/marauder.dm
@@ -12,7 +12,7 @@
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
accesses = list(ACCESS_CENT_SPECOPS)
wreckage = /obj/structure/mecha_wreckage/marauder
- mecha_flags = CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
+ mecha_flags = CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
mech_type = EXOSUIT_MODULE_MARAUDER
force = 45
max_equip_by_category = list(
@@ -117,7 +117,7 @@
base_icon_state = "mauler"
accesses = list(ACCESS_SYNDICATE)
wreckage = /obj/structure/mecha_wreckage/mauler
- mecha_flags = ID_LOCK_ON | CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
+ mecha_flags = ID_LOCK_ON | CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
max_equip_by_category = list(
MECHA_L_ARM = 1,
MECHA_R_ARM = 1,
diff --git a/code/modules/vehicles/mecha/combat/reticence.dm b/code/modules/vehicles/mecha/combat/reticence.dm
index bff5ecbd97b..ea8c72cae23 100644
--- a/code/modules/vehicles/mecha/combat/reticence.dm
+++ b/code/modules/vehicles/mecha/combat/reticence.dm
@@ -12,7 +12,7 @@
exit_delay = 40
wreckage = /obj/structure/mecha_wreckage/reticence
accesses = list(ACCESS_MECH_SCIENCE, ACCESS_THEATRE)
- mecha_flags = CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS | QUIET_STEPS | QUIET_TURNS | MMI_COMPATIBLE
+ mecha_flags = CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | QUIET_STEPS | QUIET_TURNS | MMI_COMPATIBLE
mech_type = EXOSUIT_MODULE_RETICENCE
max_equip_by_category = list(
MECHA_L_ARM = 1,
diff --git a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
index c784219e3c3..237a0d971b0 100644
--- a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
+++ b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
@@ -19,7 +19,7 @@
base_icon_state = "savannah_ivanov"
icon_state = "savannah_ivanov_0_0"
//does not include mmi compatibility
- mecha_flags = CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS
+ mecha_flags = CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS
mech_type = EXOSUIT_MODULE_SAVANNAH
movedelay = 3
max_integrity = 450 //really tanky, like damn
diff --git a/code/modules/vehicles/mecha/mecha_actions.dm b/code/modules/vehicles/mecha/mecha_actions.dm
index 43301c19605..2b410bd60c7 100644
--- a/code/modules/vehicles/mecha/mecha_actions.dm
+++ b/code/modules/vehicles/mecha/mecha_actions.dm
@@ -101,7 +101,7 @@
toggle_strafe()
/obj/vehicle/sealed/mecha/proc/toggle_strafe()
- if(!(mecha_flags & CANSTRAFE))
+ if(!(mecha_flags & CAN_STRAFE))
to_chat(occupants, "this mecha doesn't support strafing!")
return
diff --git a/code/modules/vehicles/mecha/mecha_control_console.dm b/code/modules/vehicles/mecha/mecha_control_console.dm
index 3dd69d4ba93..635ec9425b1 100644
--- a/code/modules/vehicles/mecha/mecha_control_console.dm
+++ b/code/modules/vehicles/mecha/mecha_control_console.dm
@@ -29,7 +29,7 @@
name = M.name,
integrity = round((M.get_integrity() / M.max_integrity) * 100),
charge = M.cell ? round(M.cell.percent()) : null,
- airtank = M.enclosed ? M.return_pressure() : null,
+ airtank = (M.mecha_flags & IS_ENCLOSED) ? M.return_pressure() : null,
pilot = M.return_drivers(),
location = get_area_name(M, TRUE),
emp_recharging = MT.recharging,
@@ -97,7 +97,7 @@
var/answer = {"Name: [chassis.name] Integrity: [round((chassis.get_integrity()/chassis.max_integrity * 100), 0.01)]% Cell Charge: [isnull(cell_charge) ? "Not Found":"[chassis.cell.percent()]%"]
- Cabin Pressure: [chassis.enclosed ? "[round(chassis.return_pressure(), 0.01)] kPa" : "Not Sealed"]
+ Cabin Pressure: [(chassis.mecha_flags & IS_ENCLOSED) ? "[round(chassis.return_pressure(), 0.01)] kPa" : "Not Sealed"] Pilot: [english_list(chassis.return_drivers(), nothing_text = "None")] Location: [get_area_name(chassis, TRUE) || "Unknown"]"}
if(istype(chassis, /obj/vehicle/sealed/mecha/ripley))
diff --git a/code/modules/vehicles/mecha/mecha_defense.dm b/code/modules/vehicles/mecha/mecha_defense.dm
index 746039e7552..e88ce2925ac 100644
--- a/code/modules/vehicles/mecha/mecha_defense.dm
+++ b/code/modules/vehicles/mecha/mecha_defense.dm
@@ -115,18 +115,15 @@
return ..()
/obj/vehicle/sealed/mecha/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit) //wrapper
- . = ..()
- if(. != BULLET_ACT_HIT)
- return .
-
//allows bullets to hit the pilot of open-canopy mechs
- if(!enclosed \
+ if(!(mecha_flags & IS_ENCLOSED) \
&& LAZYLEN(occupants) \
&& !(mecha_flags & SILICON_PILOT) \
&& (def_zone == BODY_ZONE_HEAD || def_zone == BODY_ZONE_CHEST))
- for(var/mob/living/hitmob as anything in occupants)
- hitmob.bullet_act(hitting_projectile, def_zone, piercing_hit) //If the sides are open, the occupant can be hit
- return BULLET_ACT_HIT
+ var/mob/living/hitmob = pick(occupants)
+ return hitmob.bullet_act(hitting_projectile, def_zone, piercing_hit) //If the sides are open, the occupant can be hit
+
+ . = ..()
log_message("Hit by projectile. Type: [hitting_projectile]([hitting_projectile.damage_type]).", LOG_MECHA, color="red")
// yes we *have* to run the armor calc proc here I love tg projectile code too
@@ -138,6 +135,7 @@
armour_penetration = hitting_projectile.armour_penetration,
), def_zone)
+
/obj/vehicle/sealed/mecha/ex_act(severity, target)
log_message("Affected by explosion of severity: [severity].", LOG_MECHA, color="red")
return ..()
@@ -199,7 +197,7 @@
/obj/vehicle/sealed/mecha/fire_act() //Check if we should ignite the pilot of an open-canopy mech
. = ..()
- if(enclosed || mecha_flags & SILICON_PILOT)
+ if(mecha_flags & IS_ENCLOSED || mecha_flags & SILICON_PILOT)
return
for(var/mob/living/cookedalive as anything in occupants)
if(cookedalive.fire_stacks < 5)
diff --git a/code/modules/vehicles/mecha/mecha_ui.dm b/code/modules/vehicles/mecha/mecha_ui.dm
index 76d8b4613fa..826a746c849 100644
--- a/code/modules/vehicles/mecha/mecha_ui.dm
+++ b/code/modules/vehicles/mecha/mecha_ui.dm
@@ -97,7 +97,7 @@
data["capacitor_rating"] = capacitor?.rating
data["weapons_safety"] = weapons_safety
- data["enclosed"] = enclosed
+ data["enclosed"] = mecha_flags & IS_ENCLOSED
data["cabin_sealed"] = cabin_sealed
data["cabin_temp"] = round(cabin_air.temperature - T0C)
data["cabin_pressure"] = round(cabin_air.return_pressure())
diff --git a/code/modules/vehicles/mecha/working/ripley.dm b/code/modules/vehicles/mecha/working/ripley.dm
index 9bc6ea75751..711da429cca 100644
--- a/code/modules/vehicles/mecha/working/ripley.dm
+++ b/code/modules/vehicles/mecha/working/ripley.dm
@@ -17,11 +17,11 @@
MECHA_POWER = 1,
MECHA_ARMOR = 1,
)
+ mecha_flags = CAN_STRAFE | HAS_LIGHTS | MMI_COMPATIBLE
wreckage = /obj/structure/mecha_wreckage/ripley
mech_type = EXOSUIT_MODULE_RIPLEY
possible_int_damage = MECHA_INT_FIRE|MECHA_INT_CONTROL_LOST|MECHA_INT_SHORT_CIRCUIT
accesses = list(ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_MINING)
- enclosed = FALSE //Normal ripley has an open cockpit design
enter_delay = 10 //can enter in a quarter of the time of other mechs
exit_delay = 10
/// Custom Ripley step and turning sounds (from TGMC)
@@ -75,10 +75,10 @@
movedelay = 4
max_temperature = 30000
max_integrity = 250
+ mecha_flags = CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
possible_int_damage = MECHA_INT_FIRE|MECHA_INT_TEMP_CONTROL|MECHA_CABIN_AIR_BREACH|MECHA_INT_CONTROL_LOST|MECHA_INT_SHORT_CIRCUIT
armor_type = /datum/armor/mecha_ripley_mk2
wreckage = /obj/structure/mecha_wreckage/ripley/mk2
- enclosed = TRUE
enter_delay = 40
silicon_icon_state = null
@@ -186,7 +186,7 @@
/obj/vehicle/sealed/mecha/ripley/paddy/preset
accesses = list(ACCESS_SECURITY)
- mecha_flags = CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE | ID_LOCK_ON
+ mecha_flags = CAN_STRAFE | HAS_LIGHTS | MMI_COMPATIBLE | ID_LOCK_ON
equip_by_category = list(
MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/energy/disabler,
MECHA_R_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/paddy_claw,
@@ -206,7 +206,7 @@
lights_power = 7
wreckage = /obj/structure/mecha_wreckage/ripley/deathripley
step_energy_drain = 0
- enclosed = TRUE
+ mecha_flags = CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE
enter_delay = 40
silicon_icon_state = null
equip_by_category = list(
diff --git a/code/modules/vending/cartridge.dm b/code/modules/vending/cartridge.dm
index dabf334c1f9..7c4a2c9578d 100644
--- a/code/modules/vending/cartridge.dm
+++ b/code/modules/vending/cartridge.dm
@@ -13,7 +13,7 @@
/obj/item/computer_disk/ordnance = 10,
/obj/item/computer_disk/quartermaster = 10,
/obj/item/computer_disk/command/captain = 3,
- /obj/item/modular_computer/pda/heads = 10,
+ /obj/item/modular_computer/pda = 10,
)
refill_canister = /obj/item/vending_refill/cart
default_price = PAYCHECK_COMMAND
diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm
index c30c7e629f2..f41d4fa8bf1 100644
--- a/code/modules/wiremod/shell/brain_computer_interface.dm
+++ b/code/modules/wiremod/shell/brain_computer_interface.dm
@@ -20,11 +20,6 @@
new /obj/item/circuit_component/bci_core,
), SHELL_CAPACITY_SMALL, starting_circuit = circuit)
-/obj/item/organ/internal/cyberimp/bci/on_insert(mob/living/carbon/receiver)
- . = ..()
- // Organs are put in nullspace, but this breaks circuit interactions
- forceMove(receiver)
-
/obj/item/organ/internal/cyberimp/bci/say(message, bubble_type, list/spans, sanitize, datum/language/language, ignore_spam, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null)
if (owner)
// Otherwise say_dead will be called.
diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm
index 7514b6cdc6b..2ed2bf541d8 100644
--- a/code/modules/zombie/organs.dm
+++ b/code/modules/zombie/organs.dm
@@ -23,24 +23,24 @@
GLOB.zombie_infection_list -= src
. = ..()
-/obj/item/organ/internal/zombie_infection/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/zombie_infection/on_mob_insert(mob/living/carbon/M, special = FALSE, movement_flags)
. = ..()
START_PROCESSING(SSobj, src)
-/obj/item/organ/internal/zombie_infection/Remove(mob/living/carbon/M, special = FALSE)
+/obj/item/organ/internal/zombie_infection/on_mob_remove(mob/living/carbon/M, special = FALSE)
. = ..()
STOP_PROCESSING(SSobj, src)
- if(iszombie(M) && old_species && !special && !QDELETED(src))
+ if(iszombie(M) && old_species && !special)
M.set_species(old_species)
if(timer_id)
deltimer(timer_id)
-/obj/item/organ/internal/zombie_infection/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/zombie_infection/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
RegisterSignal(organ_owner, COMSIG_LIVING_DEATH, PROC_REF(organ_owner_died))
-/obj/item/organ/internal/zombie_infection/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/zombie_infection/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_LIVING_DEATH)
diff --git a/html/changelogs/AutoChangeLog-pr-374.yml b/html/changelogs/AutoChangeLog-pr-374.yml
new file mode 100644
index 00000000000..401e41a87c1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-374.yml
@@ -0,0 +1,4 @@
+author: "vinylspiders"
+delete-after: True
+changes:
+ - image: "resprited CTF ID Cards"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-377.yml b/html/changelogs/AutoChangeLog-pr-377.yml
new file mode 100644
index 00000000000..2bad1a89660
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-377.yml
@@ -0,0 +1,4 @@
+author: "vinylspiders"
+delete-after: True
+changes:
+ - balance: "Last Resort can now be used while unconscious or dead. balance: Last Resort stuns bystanders for slightly longer. /:cl:"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-380.yml b/html/changelogs/AutoChangeLog-pr-380.yml
new file mode 100644
index 00000000000..b17766563f0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-380.yml
@@ -0,0 +1,4 @@
+author: "vinylspiders"
+delete-after: True
+changes:
+ - bugfix: "Fixes AIize and borgize gibbing you"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-387.yml b/html/changelogs/AutoChangeLog-pr-387.yml
new file mode 100644
index 00000000000..3700cae411e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-387.yml
@@ -0,0 +1,5 @@
+author: "vinylspiders"
+delete-after: True
+changes:
+ - bugfix: "bridge assistant no longer passes some head of staff checks"
+ - qol: "if you steal a command member's liver, you can now sabre better! /:cl:"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80583.yml b/html/changelogs/AutoChangeLog-pr-80583.yml
new file mode 100644
index 00000000000..579b1ef5f26
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80583.yml
@@ -0,0 +1,5 @@
+author: "Melbert"
+delete-after: True
+changes:
+ - balance: "Blood cultists can now convert a Null Rod into a cult weapon on an offer rune. The strength of the weapon it is converted into depends on how many cultists the Chaplain crit / killed with it. At five or more, it will turn into a Bastard Sword. Note, sacrificing someone holding a Null Rod will automatically convert it after they are gibbed."
+ - balance: "Heretics no longer produce a Bastard Sword upon cult conversion. They are still immune to cult stun and cannot be converted by blood cultists."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80698.yml b/html/changelogs/AutoChangeLog-pr-80698.yml
new file mode 100644
index 00000000000..0fc7f31ec1a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80698.yml
@@ -0,0 +1,4 @@
+author: "13spacemen"
+delete-after: True
+changes:
+ - qol: "If you can't heal a body part, you won't get a healing time delay. No more spending 5 seconds healing a body part only to get a \"can't heal that\" message."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80792.yml b/html/changelogs/AutoChangeLog-pr-80792.yml
new file mode 100644
index 00000000000..ace39411425
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80792.yml
@@ -0,0 +1,4 @@
+author: "OrionTheFox"
+delete-after: True
+changes:
+ - image: "Resprited a majority of undershirts"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80796.yml b/html/changelogs/AutoChangeLog-pr-80796.yml
new file mode 100644
index 00000000000..22e5f807d69
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80796.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed misfiring for firearms like tinkered detective revolvers."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80824.yml b/html/changelogs/AutoChangeLog-pr-80824.yml
new file mode 100644
index 00000000000..2d234eae5aa
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80824.yml
@@ -0,0 +1,4 @@
+author: "SyncIt21"
+delete-after: True
+changes:
+ - bugfix: "ejecting cells from microwaves via ctrl click now requires player proximity."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80825.yml b/html/changelogs/AutoChangeLog-pr-80825.yml
new file mode 100644
index 00000000000..5f6bfd9559d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80825.yml
@@ -0,0 +1,4 @@
+author: "SyncIt21"
+delete-after: True
+changes:
+ - code_imp: "smart fridge content overlay now uses `base_icon_state` instead of a separate var"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80828.yml b/html/changelogs/AutoChangeLog-pr-80828.yml
new file mode 100644
index 00000000000..ad2f0cb624e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80828.yml
@@ -0,0 +1,5 @@
+author: "Melbert"
+delete-after: True
+changes:
+ - bugfix: "Carps now migrate slightly better, probably."
+ - bugfix: "And Poly now talks better, probably."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80832.yml b/html/changelogs/AutoChangeLog-pr-80832.yml
new file mode 100644
index 00000000000..419a4da7b32
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80832.yml
@@ -0,0 +1,4 @@
+author: "Melbert"
+delete-after: True
+changes:
+ - admin: "Several types are now more accessible via the \"spawn\" verb (and friends), meaning you can type 'Spawn\" Modsuit' or 'Spawn \"Jumpsuit' in the command bar and it'll just give you a list of modsuits or jumpsuits to pick from."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80835.yml b/html/changelogs/AutoChangeLog-pr-80835.yml
new file mode 100644
index 00000000000..20f91b17106
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80835.yml
@@ -0,0 +1,4 @@
+author: "zxaber"
+delete-after: True
+changes:
+ - bugfix: "The various borg apparatuses can no longer pick up other internal borg tools."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80836.yml b/html/changelogs/AutoChangeLog-pr-80836.yml
new file mode 100644
index 00000000000..54945abcadf
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80836.yml
@@ -0,0 +1,4 @@
+author: "JohnFulpWillard"
+delete-after: True
+changes:
+ - bugfix: "The HoP's cartridge vending machine now sells regular PDAs instead of base command ones."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80837.yml b/html/changelogs/AutoChangeLog-pr-80837.yml
new file mode 100644
index 00000000000..da2da2857ed
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80837.yml
@@ -0,0 +1,4 @@
+author: "JohnFulpWillard"
+delete-after: True
+changes:
+ - bugfix: "Eldritch reagent (the one that heals heretics) now heal heretic monsters rather than kill them."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80840.yml b/html/changelogs/AutoChangeLog-pr-80840.yml
new file mode 100644
index 00000000000..01f8fd224d8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80840.yml
@@ -0,0 +1,4 @@
+author: "JohnFulpWillard"
+delete-after: True
+changes:
+ - bugfix: "Refried beans and Spanish rice now lets you take the bowl back after eating it."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80844.yml b/html/changelogs/AutoChangeLog-pr-80844.yml
new file mode 100644
index 00000000000..5d871a14596
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80844.yml
@@ -0,0 +1,4 @@
+author: "zxaber"
+delete-after: True
+changes:
+ - bugfix: "Ripley MK-Is and Paddys now correctly make incoming projectiles hit the pilot again."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80849.yml b/html/changelogs/AutoChangeLog-pr-80849.yml
new file mode 100644
index 00000000000..b5d830accb2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80849.yml
@@ -0,0 +1,4 @@
+author: "LT3"
+delete-after: True
+changes:
+ - bugfix: "Unencoded server admin announcements will now actually broadcast"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80855.yml b/html/changelogs/AutoChangeLog-pr-80855.yml
new file mode 100644
index 00000000000..152f5645488
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80855.yml
@@ -0,0 +1,5 @@
+author: "Melbert"
+delete-after: True
+changes:
+ - bugfix: "Lionhunter Rifle is now available at the same point as Mawed Crucible, making it available to all paths again."
+ - bugfix: "Lock knowledge now goes the correct order. Mark -> Ritual -> Unique item, rather than backwards."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80856.yml b/html/changelogs/AutoChangeLog-pr-80856.yml
new file mode 100644
index 00000000000..d7740a63e1a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80856.yml
@@ -0,0 +1,6 @@
+author: "IndieanaJones"
+delete-after: True
+changes:
+ - bugfix: "Dynamic midround rolls will properly consider roundstart players as part of the living population again, allowing antagonists with a minimum population value to spawn when they should be able to."
+ - bugfix: "Players who observe before roundstart can be considered for midround ghost roles again."
+ - bugfix: "Some antagonists which had elements that scale with living station population now function properly again."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80857.yml b/html/changelogs/AutoChangeLog-pr-80857.yml
new file mode 100644
index 00000000000..6a9a8a4ab53
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80857.yml
@@ -0,0 +1,4 @@
+author: "jlsnow301"
+delete-after: True
+changes:
+ - admin: "Fixed the bluescreen in the centcom pod launcher."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80858.yml b/html/changelogs/AutoChangeLog-pr-80858.yml
new file mode 100644
index 00000000000..f3d7581ba89
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80858.yml
@@ -0,0 +1,4 @@
+author: "jlsnow301"
+delete-after: True
+changes:
+ - bugfix: "Medical records console should be useable again"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80862.yml b/html/changelogs/AutoChangeLog-pr-80862.yml
new file mode 100644
index 00000000000..bb6e2eb327b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80862.yml
@@ -0,0 +1,4 @@
+author: "Absolucy"
+delete-after: True
+changes:
+ - bugfix: "Fixed the Codex Cicatrix always requiring a dead body, despite the description saying leather or a hide would work too."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80863.yml b/html/changelogs/AutoChangeLog-pr-80863.yml
new file mode 100644
index 00000000000..3fed04e52bc
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80863.yml
@@ -0,0 +1,4 @@
+author: "FlufflesTheDog"
+delete-after: True
+changes:
+ - bugfix: "Certain areas are now properly protected against grid check. Namely the supermatter should consistently be protected."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-98.yml b/html/changelogs/AutoChangeLog-pr-98.yml
new file mode 100644
index 00000000000..f59fc33dbb9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-98.yml
@@ -0,0 +1,10 @@
+author: "Paxilmaniac"
+delete-after: True
+changes:
+ - balance: "Kahraman Heavy Industries no longer sells base mining equipment that the station can just get. Instead their selection has been updated with a new selection of exploration and mining equipment fit for the frontier."
+ - rscadd: "Step aside colony core, there's a new sheriff in town. The organic materials printer, a green machine you make from a pile of flatpack parts from cargo. With a little input of plant matter, you can print yourself some grade A frontier equipment, or some organic materials, its up to you."
+ - rscadd: "There's a new piece of kit for marking your locations, the GPS beacon. Coming in a small size deployable item, it can be turned into an anchored, stationary GPS marker that you can use and rename to your liking. Don't like where it is? No worry, just pack it back up and move it elsewhere."
+ - rscadd: "Bluespace miners are so last codebase. Check out the hip new mining device from Kahraman: the Ore Thumper. Set one up outdoors with some wired power, and watch it slam away at the ground. After it works for long enough, the pressure built up by all that slamming will make deeply buried ore come to the surface."
+ - rscadd: "Looking for some frontier fashion? Some top notch kit? Kahraman has you covered with the new frontier clothing lineup. Now you too can buy equipment completely overrated for your cushy station adventures."
+ - sound: "New sounds added for the new ore thumper, as well as a neat new radio sound effect for the new frontier headset."
+ - image: "New sprites for the new machines, clothing, and whatever else in Kahraman are made by me, paxilmaniac, how dreamy."
\ No newline at end of file
diff --git a/html/changelogs/archive/2023-12.yml b/html/changelogs/archive/2023-12.yml
index 57265cc0bb5..37bdb29ffbd 100644
--- a/html/changelogs/archive/2023-12.yml
+++ b/html/changelogs/archive/2023-12.yml
@@ -926,6 +926,7 @@
2023-12-28:
KannaLisvern:
- rscadd: Added a new ERP emote. *lewdmoan
+ - qol: Removed Bluespace Crystals and Diamonds from medical emergency bed's recipe.
Nerev4r:
- rscadd: Due to increased availability of calcium supplements in Sector 13 due
to Deep Spacer lobbying, snailpeople have found their shells to be more suited
@@ -940,11 +941,21 @@
- balance: Shotgun shells, ESPECIALLY skyrat's shotgun shells, have been rebalanced
to not either be useless or insane in every situation, see pr for details.
- image: New sprites for some projectiles, like flechettes and whatnot
+ - qol: The names and descriptions of every paxilmaniac weapon have been improved
+ to be shorter (names) and mention at least what bullet the weapon fires (description)
+ - qol: Every weapon with an extended description has a hint in its normal examine
+ telling you that you can look further to get more info on the weapon
+ - code_imp: Two instances of now-deleted weapons paths (which i found out about
+ through someone getting one in a christmas tree) existing due to manufacturer
+ examines of all things have been cleaned up, players wont notice this one i
+ think
Rhials:
- bugfix: Icebox escape pods will now land randomly on the surface, instead of only
in certain ruins.
- qol: Wizard apprentices now spawn on the same tile as the contract that summoned
them.
+ - qol: Obsessed crewmembers are now displayed in the orbit panel.
+ - qol: The Paradox Clone orbit menu tab is now white. Neat!
zeroisthebiggay:
- bugfix: '''A whole bunch of spiders in a SWAT suit'' to ''A whole bunch of spiders
in a MODsuit'''
@@ -1047,8 +1058,6 @@
This doesn''t affect 4/5 base /tg/ station maps.'
- rscadd: 'New quirk for those who hate their appendix: Appendicitis Survivor! A
positive (-2) quirk.'
- KannaLisvern:
- - qol: Removed Bluespace Crystals and Diamonds from medical emergency bed's recipe.
LT3:
- qol: TGUI will now wait longer trying to reconnect to a new round
Lutowski:
@@ -1062,18 +1071,6 @@
NovaBot13:
- rscdel: Removes the wires from canisters. It's a cool idea, but cheap controlled
maxcaps are bad actually
- Paxilmaniac:
- - qol: The names and descriptions of every paxilmaniac weapon have been improved
- to be shorter (names) and mention at least what bullet the weapon fires (description)
- - qol: Every weapon with an extended description has a hint in its normal examine
- telling you that you can look further to get more info on the weapon
- - code_imp: Two instances of now-deleted weapons paths (which i found out about
- through someone getting one in a christmas tree) existing due to manufacturer
- examines of all things have been cleaned up, players wont notice this one i
- think
- Rhials:
- - qol: Obsessed crewmembers are now displayed in the orbit panel.
- - qol: The Paradox Clone orbit menu tab is now white. Neat!
Ryll/Shaps:
- balance: Yawns are less likely to propagate
goobliner:
diff --git a/icons/mob/clothing/underwear.dmi b/icons/mob/clothing/underwear.dmi
index d231b955bab..cd5564fe211 100644
Binary files a/icons/mob/clothing/underwear.dmi and b/icons/mob/clothing/underwear.dmi differ
diff --git a/modular_nova/master_files/code/datums/quirks/neutral_quirks/lungs.dm b/modular_nova/master_files/code/datums/quirks/neutral_quirks/lungs.dm
index 1e1fa2cc5f8..57247a3aa47 100644
--- a/modular_nova/master_files/code/datums/quirks/neutral_quirks/lungs.dm
+++ b/modular_nova/master_files/code/datums/quirks/neutral_quirks/lungs.dm
@@ -30,7 +30,7 @@
if (lungs != lungs_added && lungs != lungs_holding)
qdel(lungs_holding)
return
- lungs_holding.Insert(carbon_holder, special = TRUE, drop_if_replaced = FALSE)
+ lungs_holding.Insert(carbon_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
lungs_holding.organ_flags &= ~ORGAN_FROZEN
/datum/quirk/equipping/lungs/on_equip_item(obj/item/equipped, success)
diff --git a/modular_nova/master_files/code/datums/traits/neutral.dm b/modular_nova/master_files/code/datums/traits/neutral.dm
index ca1c43a5f39..52adfb141a5 100644
--- a/modular_nova/master_files/code/datums/traits/neutral.dm
+++ b/modular_nova/master_files/code/datums/traits/neutral.dm
@@ -248,7 +248,7 @@ GLOBAL_VAR_INIT(DNR_trait_overlay, generate_DNR_trait_overlay())
var/obj/item/organ/internal/tongue/dog/new_tongue = new(get_turf(human_holder))
new_tongue.copy_traits_from(human_holder.get_organ_slot(ORGAN_SLOT_TONGUE))
- new_tongue.Insert(human_holder, special = TRUE, drop_if_replaced = FALSE)
+ new_tongue.Insert(human_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/quirk/item_quirk/avian
name = "Avian Traits"
@@ -263,7 +263,7 @@ GLOBAL_VAR_INIT(DNR_trait_overlay, generate_DNR_trait_overlay())
var/obj/item/organ/internal/tongue/avian/new_tongue = new(get_turf(human_holder))
new_tongue.copy_traits_from(human_holder.get_organ_slot(ORGAN_SLOT_TONGUE))
- new_tongue.Insert(human_holder, special = TRUE, drop_if_replaced = FALSE)
+ new_tongue.Insert(human_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/quirk/sensitivesnout
name = "Sensitive Snout"
diff --git a/modular_nova/master_files/code/modules/client/preferences/brain.dm b/modular_nova/master_files/code/modules/client/preferences/brain.dm
index cdf60e3f59a..1f446e7d893 100644
--- a/modular_nova/master_files/code/modules/client/preferences/brain.dm
+++ b/modular_nova/master_files/code/modules/client/preferences/brain.dm
@@ -27,7 +27,7 @@
new_brain.modular_persistence = old_brain.modular_persistence
old_brain.modular_persistence = null
- new_brain.Insert(target, drop_if_replaced = FALSE)
+ new_brain.mob_insert(target, movement_flags = DELETE_IF_REPLACED)
// Prefs can be applied to mindless mobs, let's not try to move the non-existent mind back in!
if(!keep_me_safe)
diff --git a/modular_nova/master_files/icons/mob/clothing/modsuit/mod_clothing_mutant.dmi b/modular_nova/master_files/icons/mob/clothing/modsuit/mod_clothing_mutant.dmi
index 79f9697d849..368d63d14bc 100644
Binary files a/modular_nova/master_files/icons/mob/clothing/modsuit/mod_clothing_mutant.dmi and b/modular_nova/master_files/icons/mob/clothing/modsuit/mod_clothing_mutant.dmi differ
diff --git a/modular_nova/modules/company_imports/code/armament_datums/kahraman_industries.dm b/modular_nova/modules/company_imports/code/armament_datums/kahraman_industries.dm
index f15da1c1750..b392ab14c2c 100644
--- a/modular_nova/modules/company_imports/code/armament_datums/kahraman_industries.dm
+++ b/modular_nova/modules/company_imports/code/armament_datums/kahraman_industries.dm
@@ -2,110 +2,108 @@
category = KAHRAMAN_INDUSTRIES_NAME
company_bitflag = CARGO_COMPANY_KAHRAMAN
-// Mining PPE, SEVAs and hardhats, have you passed your OSHA inspection today?
-
-/datum/armament_entry/company_import/kahraman/ppe
- subcategory = "FOHSA Certified Protective Equipment"
-
-/datum/armament_entry/company_import/kahraman/ppe/weldhat
- item_type = /obj/item/clothing/head/utility/hardhat/welding/orange
- cost = PAYCHECK_CREW * 1.5
-
-/datum/armament_entry/company_import/kahraman/ppe/gasmask
- item_type = /obj/item/clothing/mask/gas/alt
+/// Kahraman-made machines
+/datum/armament_entry/company_import/kahraman/machinery
+ subcategory = "Machinery"
+
+/datum/armament_entry/company_import/kahraman/machinery/biogenerator
+ item_type = /obj/item/flatpacked_machine/organics_printer
+ description = "An advanced machine seen in frontier outposts and colonies capable of turning organic plant matter into \
+ reagents and items of use that a fabricator can't typically make."
+ cost = CARGO_CRATE_VALUE * 3
+
+/datum/armament_entry/company_import/kahraman/machinery/ore_thumper
+ item_type = /obj/item/flatpacked_machine/ore_thumper
+ description = "A frame with a heavy block of metal suspended atop a pipe. \
+ Must be deployed outdoors and given a wired power connection. \
+ Forces pressurized gas into the ground which brings up buried resources."
+ cost = CARGO_CRATE_VALUE * 5
+
+/datum/armament_entry/company_import/kahraman/machinery/gps_beacon
+ item_type = /obj/item/flatpacked_machine/gps_beacon
+ description = "A packed GPS beacon, can be deployed and anchored into the ground to \
+ provide and unobstructed homing beacon for wayward travelers across the galaxy."
cost = PAYCHECK_LOWER
-/datum/armament_entry/company_import/kahraman/ppe/hazard_vest
- item_type = /obj/item/clothing/suit/hazardvest
- cost = PAYCHECK_CREW
-
-/datum/armament_entry/company_import/kahraman/ppe/seva_mask
- item_type = /obj/item/clothing/mask/gas/seva
- cost = PAYCHECK_CREW * 1.5
+// Occupational health and safety? Never heard of her.
-/datum/armament_entry/company_import/kahraman/ppe/seva_suit
- item_type = /obj/item/clothing/suit/hooded/seva
- cost = PAYCHECK_COMMAND * 2
+/datum/armament_entry/company_import/kahraman/ppe
+ subcategory = "Protective Equipment"
-/datum/armament_entry/company_import/kahraman/ppe/sensors_cuffs
- item_type = /obj/item/kheiral_cuffs
- cost = PAYCHECK_COMMAND * 5
+/datum/armament_entry/company_import/kahraman/ppe/hazard_mod
+ item_type = /obj/item/mod/control/pre_equipped/frontier_colonist
+ cost = PAYCHECK_COMMAND * 6.5
-// Hand held mining equipment
+/datum/armament_entry/company_import/kahraman/ppe/gas_mask
+ item_type = /obj/item/clothing/mask/gas/atmos/frontier_colonist
+ cost = PAYCHECK_COMMAND
-/datum/armament_entry/company_import/kahraman/mining_tool
- subcategory = "Powered Mining Equipment"
+/datum/armament_entry/company_import/kahraman/ppe/headset
+ item_type = /obj/item/radio/headset/headset_frontier_colonist
+ cost = PAYCHECK_COMMAND * 1.5
-/datum/armament_entry/company_import/kahraman/mining_tool/drill
- item_type = /obj/item/pickaxe/drill
+/datum/armament_entry/company_import/kahraman/ppe/flak_vest
+ item_type = /obj/item/clothing/suit/frontier_colonist_flak
cost = PAYCHECK_COMMAND
-/datum/armament_entry/company_import/kahraman/mining_tool/resonator
- item_type = /obj/item/resonator
+/datum/armament_entry/company_import/kahraman/ppe/tanker_helmet
+ item_type = /obj/item/clothing/head/frontier_colonist_helmet
cost = PAYCHECK_COMMAND
-/datum/armament_entry/company_import/kahraman/mining_tool/pka
- item_type = /obj/item/gun/energy/recharge/kinetic_accelerator
- cost = PAYCHECK_COMMAND
+// Work clothing
-/datum/armament_entry/company_import/kahraman/mining_tool/cutter
- item_type = /obj/item/gun/energy/plasmacutter
- cost = PAYCHECK_COMMAND
+/datum/armament_entry/company_import/kahraman/work_clothing
+ subcategory = "Clothing"
-/datum/armament_entry/company_import/kahraman/mining_tool/diamond_drill
- item_type = /obj/item/pickaxe/drill/diamonddrill
- cost = PAYCHECK_COMMAND * 2
+/datum/armament_entry/company_import/kahraman/work_clothing/jumpsuit
+ item_type = /obj/item/clothing/under/frontier_colonist
+ cost = PAYCHECK_CREW
-/datum/armament_entry/company_import/kahraman/mining_tool/advanced_cutter
- item_type = /obj/item/gun/energy/plasmacutter/adv
- cost = PAYCHECK_COMMAND * 4
+/datum/armament_entry/company_import/kahraman/work_clothing/jacket
+ item_type = /obj/item/clothing/suit/jacket/frontier_colonist
+ cost = PAYCHECK_CREW
-/datum/armament_entry/company_import/kahraman/mining_tool/super_resonator
- item_type = /obj/item/resonator/upgraded
- cost = PAYCHECK_COMMAND * 4
+/datum/armament_entry/company_import/kahraman/work_clothing/jacket_short
+ item_type = /obj/item/clothing/suit/jacket/frontier_colonist/short
+ cost = PAYCHECK_CREW
-/datum/armament_entry/company_import/kahraman/mining_tool/jackhammer
- item_type = /obj/item/pickaxe/drill/jackhammer
- cost = PAYCHECK_COMMAND * 3
+/datum/armament_entry/company_import/kahraman/work_clothing/med_jacket
+ item_type = /obj/item/clothing/suit/jacket/frontier_colonist/medical
+ cost = PAYCHECK_CREW
-/datum/armament_entry/company_import/kahraman/sensing
- subcategory = "Sensing Equipment"
+/datum/armament_entry/company_import/kahraman/work_clothing/ballcap
+ item_type = /obj/item/clothing/head/soft/frontier_colonist
+ cost = PAYCHECK_CREW
-/datum/armament_entry/company_import/kahraman/sensing/mesons
- item_type = /obj/item/clothing/glasses/meson
+/datum/armament_entry/company_import/kahraman/work_clothing/med_ballcap
+ item_type = /obj/item/clothing/head/soft/frontier_colonist/medic
cost = PAYCHECK_CREW
-/datum/armament_entry/company_import/kahraman/sensing/autoscanner
- item_type = /obj/item/t_scanner/adv_mining_scanner/lesser
- cost = PAYCHECK_LOWER
+/datum/armament_entry/company_import/kahraman/work_clothing/booties
+ item_type = /obj/item/clothing/shoes/jackboots/frontier_colonist
+ cost = PAYCHECK_CREW
-/datum/armament_entry/company_import/kahraman/sensing/super_autoscanner
- item_type = /obj/item/t_scanner/adv_mining_scanner
- cost = PAYCHECK_COMMAND * 3
+/datum/armament_entry/company_import/kahraman/work_clothing/gloves
+ item_type = /obj/item/clothing/gloves/frontier_colonist
+ cost = PAYCHECK_CREW
-/datum/armament_entry/company_import/kahraman/sensing/nvg_mesons
- item_type = /obj/item/clothing/glasses/meson/night
- cost = PAYCHECK_COMMAND * 3
+// "Equipment", so storage items and whatnot
-/datum/armament_entry/company_import/kahraman/mecha_tools
- subcategory = "Heavy Powered Mining Equipment"
+/datum/armament_entry/company_import/kahraman/storage_equipment
+ subcategory = "Personal Equipment"
-/datum/armament_entry/company_import/kahraman/mecha_tools/scanner
- item_type = /obj/item/mecha_parts/mecha_equipment/mining_scanner
+/datum/armament_entry/company_import/kahraman/storage_equipment/backpack
+ item_type = /obj/item/storage/backpack/industrial/frontier_colonist
cost = PAYCHECK_CREW
-/datum/armament_entry/company_import/kahraman/mecha_tools/drill
- item_type = /obj/item/mecha_parts/mecha_equipment/drill
+/datum/armament_entry/company_import/kahraman/storage_equipment/satchel
+ item_type = /obj/item/storage/backpack/industrial/frontier_colonist/satchel
cost = PAYCHECK_CREW
-/datum/armament_entry/company_import/kahraman/mecha_tools/pka
- item_type = /obj/item/mecha_parts/mecha_equipment/weapon/energy/mecha_kineticgun
- cost = PAYCHECK_CREW * 3
-
-/datum/armament_entry/company_import/kahraman/mecha_tools/diamond_drill
- item_type = /obj/item/mecha_parts/mecha_equipment/drill/diamonddrill
- cost = PAYCHECK_CREW * 3
+/datum/armament_entry/company_import/kahraman/storage_equipment/messenger
+ item_type = /obj/item/storage/backpack/industrial/frontier_colonist/messenger
+ cost = PAYCHECK_CREW
-/datum/armament_entry/company_import/kahraman/mecha_tools/cutter
- item_type = /obj/item/mecha_parts/mecha_equipment/weapon/energy/plasma
- cost = PAYCHECK_CREW * 3
+/datum/armament_entry/company_import/kahraman/storage_equipment/belt
+ item_type = /obj/item/storage/belt/utility/frontier_colonist
+ cost = PAYCHECK_CREW
diff --git a/modular_nova/modules/cortical_borer/code/cortical_borer.dm b/modular_nova/modules/cortical_borer/code/cortical_borer.dm
index 9d5b83fc24a..d77025e6853 100644
--- a/modular_nova/modules/cortical_borer/code/cortical_borer.dm
+++ b/modular_nova/modules/cortical_borer/code/cortical_borer.dm
@@ -32,7 +32,7 @@ GLOBAL_LIST_EMPTY(cortical_borers)
return FALSE
//so if a person is debrained, the borer is removed
-/obj/item/organ/internal/brain/Remove(mob/living/carbon/target, special = 0, no_id_transfer = FALSE)
+/obj/item/organ/internal/brain/Remove(mob/living/carbon/target, special = FALSE, movement_flags)
. = ..()
var/mob/living/basic/cortical_borer/cb_inside = target.has_borer()
if(cb_inside)
@@ -50,7 +50,7 @@ GLOBAL_LIST_EMPTY(cortical_borers)
borer = null
return ..()
-/obj/item/organ/internal/borer_body/Insert(mob/living/carbon/carbon_target, special, drop_if_replaced)
+/obj/item/organ/internal/borer_body/Insert(mob/living/carbon/carbon_target, special, movement_flags)
. = ..()
for(var/datum/borer_focus/body_focus as anything in borer.body_focuses)
body_focus.on_add()
diff --git a/modular_nova/modules/cortical_borer/code/evolution/evolution_things/empowered_egg.dm b/modular_nova/modules/cortical_borer/code/evolution/evolution_things/empowered_egg.dm
index d04b31cce56..e1c12846835 100644
--- a/modular_nova/modules/cortical_borer/code/evolution/evolution_things/empowered_egg.dm
+++ b/modular_nova/modules/cortical_borer/code/evolution/evolution_things/empowered_egg.dm
@@ -19,7 +19,7 @@
if(iscarbon(loc))
Insert(loc)
-/obj/item/organ/internal/empowered_borer_egg/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/empowered_borer_egg/Insert(mob/living/carbon/M, special = FALSE, movement_flags = DELETE_IF_REPLACED)
..()
addtimer(CALLBACK(src, PROC_REF(try_burst)), burst_time)
diff --git a/modular_nova/modules/customization/modules/client/augment/limbs.dm b/modular_nova/modules/customization/modules/client/augment/limbs.dm
index 8ae5fed1f04..3d855176497 100644
--- a/modular_nova/modules/customization/modules/client/augment/limbs.dm
+++ b/modular_nova/modules/customization/modules/client/augment/limbs.dm
@@ -31,8 +31,6 @@
var/chosen_style = GLOB.robotic_styles_list[prefs.augment_limb_styles[slot]]
new_limb.set_icon_static(chosen_style)
new_limb.current_style = prefs.augment_limb_styles[slot]
- for (var/obj/item/organ/external/external_organ as anything in old_limb.external_organs)
- external_organ.transfer_to_limb(new_limb)
new_limb.replace_limb(augmented)
qdel(old_limb)
diff --git a/modular_nova/modules/customization/modules/client/augment/organs.dm b/modular_nova/modules/customization/modules/client/augment/organs.dm
index 091b0414456..0384c3668d9 100644
--- a/modular_nova/modules/customization/modules/client/augment/organs.dm
+++ b/modular_nova/modules/customization/modules/client/augment/organs.dm
@@ -8,7 +8,7 @@
var/obj/item/organ/organ_path = path // cast this to an organ so we can get the slot from it using initial()
var/obj/item/organ/new_organ = new path()
new_organ.copy_traits_from(human_holder.get_organ_slot(initial(organ_path.slot)))
- new_organ.Insert(human_holder, special = TRUE, drop_if_replaced = FALSE)
+ new_organ.Insert(human_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
//HEARTS
/datum/augment_item/organ/heart
diff --git a/modular_nova/modules/customization/modules/mob/dead/new_player/sprite_accessories/snout.dm b/modular_nova/modules/customization/modules/mob/dead/new_player/sprite_accessories/snout.dm
index 159d8c9a652..9eb7da1b4dd 100644
--- a/modular_nova/modules/customization/modules/mob/dead/new_player/sprite_accessories/snout.dm
+++ b/modular_nova/modules/customization/modules/mob/dead/new_player/sprite_accessories/snout.dm
@@ -30,7 +30,7 @@
return !sprite_datum.is_hidden(human)
-/obj/item/organ/external/snout/Insert(mob/living/carbon/reciever, special, drop_if_replaced)
+/obj/item/organ/external/snout/Insert(mob/living/carbon/reciever, special, movement_flags)
if(sprite_accessory_flags & SPRITE_ACCESSORY_USE_MUZZLED_SPRITE)
external_bodytypes |= BODYTYPE_SNOUTED
if(sprite_accessory_flags & SPRITE_ACCESSORY_USE_ALT_FACEWEAR_LAYER)
diff --git a/modular_nova/modules/customization/modules/mob/living/carbon/human/species.dm b/modular_nova/modules/customization/modules/mob/living/carbon/human/species.dm
index 9568aec0bd0..31b0dd14e20 100644
--- a/modular_nova/modules/customization/modules/mob/living/carbon/human/species.dm
+++ b/modular_nova/modules/customization/modules/mob/living/carbon/human/species.dm
@@ -277,7 +277,7 @@ GLOBAL_LIST_EMPTY(customizable_races)
replacement.build_from_dna(target.dna, key)
// organ.Insert will qdel any current organs in that slot, so we don't need to.
- replacement.Insert(target, special = TRUE, drop_if_replaced = FALSE)
+ replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED)
// var/obj/item/organ/path = new SA.organ_type
// var/obj/item/organ/oldorgan = C.get_organ_slot(path.slot)
diff --git a/modular_nova/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm b/modular_nova/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm
index a1574f895a4..e71c3ed9e50 100644
--- a/modular_nova/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm
+++ b/modular_nova/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm
@@ -53,39 +53,39 @@
/mob/living/carbon/proc/ReassignForeignBodyparts()
var/obj/item/bodypart/head = get_bodypart(BODY_ZONE_HEAD)
if (head?.type != part_default_head)
- qdel(head)
var/obj/item/bodypart/limb = new part_default_head
limb.replace_limb(src, TRUE)
+ qdel(head)
var/obj/item/bodypart/chest = get_bodypart(BODY_ZONE_CHEST)
if (chest?.type != part_default_chest)
- qdel(chest)
var/obj/item/bodypart/limb = new part_default_chest
limb.replace_limb(src, TRUE)
+ qdel(chest)
var/obj/item/bodypart/arm/left/left_arm = get_bodypart(BODY_ZONE_L_ARM)
if (left_arm?.type != part_default_l_arm)
- qdel(left_arm)
var/obj/item/bodypart/limb = new part_default_l_arm
limb.replace_limb(src, TRUE)
+ qdel(left_arm)
var/obj/item/bodypart/arm/right/right_arm = get_bodypart(BODY_ZONE_R_ARM)
if (right_arm?.type != part_default_r_arm)
- qdel(right_arm)
var/obj/item/bodypart/limb = new part_default_r_arm
limb.replace_limb(src, TRUE)
+ qdel(right_arm)
var/obj/item/bodypart/leg/left/left_leg = get_bodypart(BODY_ZONE_L_LEG)
if (left_leg?.type != part_default_l_leg)
- qdel(left_leg)
var/obj/item/bodypart/limb = new part_default_l_leg
limb.replace_limb(src, TRUE)
+ qdel(left_leg)
var/obj/item/bodypart/leg/right/right_leg = get_bodypart(BODY_ZONE_R_LEG)
if (right_leg?.type != part_default_r_leg)
- qdel(right_leg)
var/obj/item/bodypart/limb = new part_default_r_leg
limb.replace_limb(src, TRUE)
+ qdel(right_leg)
/datum/species/ghoul/on_species_gain(mob/living/carbon/new_ghoul, datum/species/old_species, pref_load)
// Missing Defaults in DNA? Randomize!
diff --git a/modular_nova/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_tumor.dm b/modular_nova/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_tumor.dm
index 2f4ee0c3c52..e19afb9734f 100644
--- a/modular_nova/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_tumor.dm
+++ b/modular_nova/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_tumor.dm
@@ -25,7 +25,7 @@
var/bloodloss_rate = NORMAL_BLOOD_DRAIN
-/obj/item/organ/internal/heart/hemophage/Insert(mob/living/carbon/tumorful, special, drop_if_replaced)
+/obj/item/organ/internal/heart/hemophage/Insert(mob/living/carbon/tumorful, special, movement_flags)
. = ..()
if(!. || !owner)
return
diff --git a/modular_nova/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm b/modular_nova/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm
index ad469037a85..33cd7ff1907 100644
--- a/modular_nova/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm
+++ b/modular_nova/modules/customization/modules/mob/living/carbon/human/species/roundstartslime.dm
@@ -389,7 +389,7 @@
if(robot_organs)
replacement_organ.organ_flags |= ORGAN_ROBOTIC
replacement_organ.build_from_dna(alterer.dna, chosen_key)
- replacement_organ.Insert(alterer, special = TRUE, drop_if_replaced = FALSE)
+ replacement_organ.Insert(alterer, special = TRUE, movement_flags = DELETE_IF_REPLACED)
else
var/list/new_acc_list = list()
new_acc_list[MUTANT_INDEX_NAME] = selected_sprite_accessory.name
diff --git a/modular_nova/modules/customization/modules/surgery/organs/genitals.dm b/modular_nova/modules/customization/modules/surgery/organs/genitals.dm
index 4abce60d6e3..17a22786e29 100644
--- a/modular_nova/modules/customization/modules/surgery/organs/genitals.dm
+++ b/modular_nova/modules/customization/modules/surgery/organs/genitals.dm
@@ -50,7 +50,7 @@
return INITIALIZE_HINT_QDEL
//Removes ERP organs depending on config
-/obj/item/organ/external/genital/Insert(mob/living/carbon/M, special, drop_if_replaced)
+/obj/item/organ/external/genital/Insert(mob/living/carbon/M, special, movement_flags)
if(CONFIG_GET(flag/disable_erp_preferences))
return
. = ..()
diff --git a/modular_nova/modules/customization/modules/surgery/organs/organ.dm b/modular_nova/modules/customization/modules/surgery/organs/organ.dm
index ac69342a036..e1892fc5543 100644
--- a/modular_nova/modules/customization/modules/surgery/organs/organ.dm
+++ b/modular_nova/modules/customization/modules/surgery/organs/organ.dm
@@ -14,21 +14,23 @@
if(mutantpart_key)
color = mutantpart_info[MUTANT_INDEX_COLOR_LIST][1]
-/obj/item/organ/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/Insert(mob/living/carbon/M, special = FALSE, movement_flags = DELETE_IF_REPLACED)
var/mob/living/carbon/human/H = M
if(mutantpart_key && istype(H))
H.dna.species.mutant_bodyparts[mutantpart_key] = mutantpart_info.Copy()
- H.update_body()
+ if(!special)
+ H.update_body()
. = ..()
-/obj/item/organ/Remove(mob/living/carbon/M, special = FALSE)
+/obj/item/organ/Remove(mob/living/carbon/M, special = FALSE, movement_flags)
var/mob/living/carbon/human/H = M
if(mutantpart_key && istype(H))
if(H.dna.species.mutant_bodyparts[mutantpart_key])
mutantpart_info = H.dna.species.mutant_bodyparts[mutantpart_key].Copy() //Update the info in case it was changed on the person
color = mutantpart_info[MUTANT_INDEX_COLOR_LIST][1]
H.dna.species.mutant_bodyparts -= mutantpart_key
- H.update_body()
+ if(!special)
+ H.update_body()
. = ..()
/obj/item/organ/proc/build_from_dna(datum/dna/DNA, associated_key)
diff --git a/modular_nova/modules/customization/modules/surgery/organs/tails.dm b/modular_nova/modules/customization/modules/surgery/organs/tails.dm
index 007fcb16dfd..3cafd6852a0 100644
--- a/modular_nova/modules/customization/modules/surgery/organs/tails.dm
+++ b/modular_nova/modules/customization/modules/surgery/organs/tails.dm
@@ -41,7 +41,7 @@
return TRUE
-/obj/item/organ/external/tail/Insert(mob/living/carbon/reciever, special, drop_if_replaced)
+/obj/item/organ/external/tail/Insert(mob/living/carbon/reciever, special, movement_flags)
if(sprite_accessory_flags & SPRITE_ACCESSORY_WAG_ABLE)
wag_flags |= WAG_ABLE
return ..()
diff --git a/modular_nova/modules/customization/modules/surgery/organs/taur_body.dm b/modular_nova/modules/customization/modules/surgery/organs/taur_body.dm
index 233b2290b7c..81b842a1af6 100644
--- a/modular_nova/modules/customization/modules/surgery/organs/taur_body.dm
+++ b/modular_nova/modules/customization/modules/surgery/organs/taur_body.dm
@@ -31,7 +31,7 @@
return GLOB.sprite_accessories["taur"]
-/obj/item/organ/external/taur_body/Insert(mob/living/carbon/reciever, special, drop_if_replaced)
+/obj/item/organ/external/taur_body/Insert(mob/living/carbon/reciever, special, movement_flags)
if(sprite_accessory_flags & SPRITE_ACCESSORY_HIDE_SHOES)
external_bodytypes |= BODYTYPE_HIDE_SHOES
diff --git a/modular_nova/modules/deforest_medical_items/code/healing_stack_items.dm b/modular_nova/modules/deforest_medical_items/code/healing_stack_items.dm
index 5c1563fd07b..e6bed70129f 100644
--- a/modular_nova/modules/deforest_medical_items/code/healing_stack_items.dm
+++ b/modular_nova/modules/deforest_medical_items/code/healing_stack_items.dm
@@ -30,7 +30,7 @@
var/treatment_sound = 'sound/items/duct_tape_rip.ogg'
// This is only relevant for the types of wounds defined, we can't work if there are none
-/obj/item/stack/medical/wound_recovery/try_heal(mob/living/patient, mob/user, silent)
+/obj/item/stack/medical/wound_recovery/try_heal(mob/living/patient, mob/user, silent, looping)
if(patient.has_status_effect(/datum/status_effect/vulnerable_to_damage))
patient.balloon_alert(user, "still recovering from last use!")
diff --git a/modular_nova/modules/kahraman_equipment/code/clothing/armor_datums.dm b/modular_nova/modules/kahraman_equipment/code/clothing/armor_datums.dm
new file mode 100644
index 00000000000..aa887a848c3
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/code/clothing/armor_datums.dm
@@ -0,0 +1,30 @@
+/datum/armor/colonist_clothing
+ laser = ARMOR_LEVEL_TINY
+ energy = ARMOR_LEVEL_TINY
+ bomb = ARMOR_LEVEL_TINY
+ bio = ARMOR_LEVEL_TINY
+ fire = ARMOR_LEVEL_WEAK
+ acid = ARMOR_LEVEL_TINY
+ wound = WOUND_ARMOR_WEAK
+
+/datum/armor/colonist_armor
+ melee = ARMOR_LEVEL_WEAK
+ bullet = ARMOR_LEVEL_WEAK
+ laser = ARMOR_LEVEL_TINY
+ energy = ARMOR_LEVEL_TINY
+ bomb = ARMOR_LEVEL_TINY
+ bio = ARMOR_LEVEL_TINY
+ fire = ARMOR_LEVEL_WEAK
+ acid = ARMOR_LEVEL_TINY
+ wound = WOUND_ARMOR_STANDARD
+
+/datum/armor/colonist_hazard
+ melee = ARMOR_LEVEL_TINY
+ bullet = ARMOR_LEVEL_TINY
+ laser = ARMOR_LEVEL_WEAK
+ energy = ARMOR_LEVEL_WEAK
+ bomb = ARMOR_LEVEL_MID
+ bio = 100
+ fire = 100
+ acid = ARMOR_LEVEL_MID
+ wound = WOUND_ARMOR_WEAK
diff --git a/modular_nova/modules/kahraman_equipment/code/clothing/clothing.dm b/modular_nova/modules/kahraman_equipment/code/clothing/clothing.dm
new file mode 100644
index 00000000000..45649885402
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/code/clothing/clothing.dm
@@ -0,0 +1,214 @@
+// Jumpsuit
+
+/obj/item/clothing/under/frontier_colonist
+ name = "frontier jumpsuit"
+ desc = "A heavy grey jumpsuit with extra padding around the joints. Two massive pockets included. \
+ No matter what you do to adjust it, its always just slightly too large."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi'
+ icon_state = "jumpsuit"
+ worn_icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi'
+ worn_icon_digi = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_digi.dmi'
+ worn_icon_teshari = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi'
+ worn_icon_state = "jumpsuit"
+ has_sensor = SENSOR_COORDS
+ random_sensor = FALSE
+
+/obj/item/clothing/under/frontier_colonist/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+// Boots
+
+/obj/item/clothing/shoes/jackboots/frontier_colonist
+ name = "heavy frontier boots"
+ desc = "A well built pair of tall boots usually seen on the feet of explorers, first wave colonists, \
+ and LARPers across the galaxy."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi'
+ icon_state = "boots"
+ worn_icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi'
+ worn_icon_digi = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_digi.dmi'
+ worn_icon_teshari = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi'
+ worn_icon_state = "boots"
+ armor_type = /datum/armor/colonist_clothing
+ resistance_flags = NONE
+
+/obj/item/clothing/shoes/jackboots/frontier_colonist/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+// Jackets
+
+/obj/item/clothing/suit/jacket/frontier_colonist
+ name = "frontier trenchcoat"
+ desc = "A knee length coat with a water-resistant exterior and relatively comfortable interior. \
+ In between? Just enough protective material to stop the odd sharp thing getting through, \
+ though don't expect miracles."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi'
+ icon_state = "jacket"
+ worn_icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi'
+ supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
+ worn_icon_teshari = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi'
+ worn_icon_state = "jacket"
+ slot_flags = ITEM_SLOT_OCLOTHING|ITEM_SLOT_NECK
+ armor_type = /datum/armor/colonist_clothing
+ resistance_flags = NONE
+ allowed = null
+
+/obj/item/clothing/suit/jacket/frontier_colonist/Initialize(mapload)
+ . = ..()
+ allowed += GLOB.colonist_suit_allowed
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+/obj/item/clothing/suit/jacket/frontier_colonist/short
+ name = "frontier jacket"
+ desc = "A short coat with a water-resistant exterior and relatively comfortable interior. \
+ In between? Just enough protective material to stop the odd sharp thing getting through, \
+ though don't expect miracles."
+ icon_state = "jacket_short"
+ worn_icon_state = "jacket_short"
+
+/obj/item/clothing/suit/jacket/frontier_colonist/medical
+ name = "frontier medical jacket"
+ desc = "A short coat with a water-resistant exterior and relatively comfortable interior. \
+ In between? Just enough protective material to stop the odd sharp thing getting through, \
+ though don't expect miracles. This one is colored a bright red and covered in white \
+ stripes to denote that someone wearing it might be able to provide medical assistance."
+ icon_state = "jacket_med"
+ worn_icon_state = "jacket_med"
+
+// Flak Jacket
+
+/obj/item/clothing/suit/frontier_colonist_flak
+ name = "frontier flak jacket"
+ desc = "A simple flak jacket with an exterior of water-resistant material. \
+ Jackets like these are often found on first wave colonists that want some armor \
+ due to the fact they can be made easily within a colony core type machine."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi'
+ icon_state = "flak"
+ worn_icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi'
+ supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
+ worn_icon_teshari = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi'
+ worn_icon_state = "flak"
+ body_parts_covered = CHEST
+ cold_protection = CHEST|GROIN
+ min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
+ heat_protection = CHEST|GROIN
+ max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT
+ armor_type = /datum/armor/colonist_armor
+ resistance_flags = NONE
+ allowed = null
+
+/obj/item/clothing/suit/frontier_colonist_flak/Initialize(mapload)
+ . = ..()
+ allowed += GLOB.colonist_suit_allowed
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+// Various softcaps
+
+/obj/item/clothing/head/soft/frontier_colonist
+ name = "frontier cap"
+ desc = "It's a robust baseball hat in a rugged green color."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi'
+ icon_state = "cap"
+ soft_type = "cap"
+ soft_suffix = null
+ worn_icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi'
+ supports_variations_flags = CLOTHING_SNOUTED_VARIATION_NO_NEW_ICON
+ worn_icon_teshari = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi'
+ worn_icon_state = "cap"
+
+/obj/item/clothing/head/soft/frontier_colonist/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+/obj/item/clothing/head/soft/frontier_colonist/medic
+ name = "frontier medical cap"
+ desc = "It's a robust baseball hat in a stylish red color. Has a white diamond to denote that its wearer might be able to provide medical assistance."
+ icon_state = "cap_medical"
+ soft_type = "cap_medical"
+ worn_icon_state = "cap_medical"
+
+// Helmet (Is it a helmet? Questionable? I'm not sure what to call this thing)
+
+/obj/item/clothing/head/frontier_colonist_helmet
+ name = "frontier soft helmet"
+ desc = "A unusual piece of headwear somewhere between a proper helmet and a normal cap."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi'
+ icon_state = "tanker"
+ worn_icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi'
+ supports_variations_flags = CLOTHING_SNOUTED_VARIATION_NO_NEW_ICON
+ worn_icon_teshari = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi'
+ worn_icon_state = "tanker"
+ armor_type = /datum/armor/colonist_armor
+ resistance_flags = NONE
+ flags_inv = 0
+ clothing_flags = SNUG_FIT | STACKABLE_HELMET_EXEMPT
+
+/obj/item/clothing/head/frontier_colonist_helmet/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+// Headset
+
+/obj/item/radio/headset/headset_frontier_colonist
+ name = "frontier radio headset"
+ desc = "A bulky headset that should hopefully survive exposure to the elements better than station headsets might. \
+ Has a built-in antenna allowing the headset to work independently of a communications network."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi'
+ icon_state = "radio"
+ worn_icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi'
+ supports_variations_flags = CLOTHING_SNOUTED_VARIATION_NO_NEW_ICON
+ worn_icon_teshari = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi'
+ worn_icon_state = "radio"
+ alternate_worn_layer = FACEMASK_LAYER + 0.5
+ subspace_transmission = FALSE
+ radiosound = 'modular_nova/modules/kahraman_equipment/sound/morse_signal.wav'
+
+/obj/item/radio/headset/headset_frontier_colonist/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+// Gloves
+
+/obj/item/clothing/gloves/frontier_colonist
+ name = "frontier gloves"
+ desc = "A sturdy pair of black gloves that'll keep your precious fingers protected from the outside world. \
+ They go a bit higher up the arm than most gloves should, and you aren't quite sure why."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi'
+ icon_state = "gloves"
+ worn_icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi'
+ supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
+ worn_icon_teshari = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi'
+ worn_icon_state = "gloves"
+ greyscale_colors = "#3a373e"
+ siemens_coefficient = 0.25 // Doesn't insulate you entirely, but makes you a little more resistant
+ cold_protection = HANDS
+ min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
+ heat_protection = HANDS
+ max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
+ resistance_flags = NONE
+ clothing_traits = list(TRAIT_QUICK_CARRY)
+
+/obj/item/clothing/gloves/frontier_colonist/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+// Special mask
+
+/obj/item/clothing/mask/gas/atmos/frontier_colonist
+ name = "frontier gas mask"
+ desc = "An improved gas mask commonly seen in places where the atmosphere is less than breathable, \
+ but otherwise more or less habitable. Its certified to protect against most biological hazards \
+ to boot."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi'
+ icon_state = "mask"
+ worn_icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi'
+ worn_icon_digi = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_digi.dmi'
+ worn_icon_teshari = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi'
+ worn_icon_state = "mask"
+ flags_inv = HIDEEYES|HIDEFACE|HIDEFACIALHAIR|HIDESNOUT
+ armor_type = /datum/armor/colonist_hazard
+
+/obj/item/clothing/mask/gas/atmos/frontier_colonist/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
diff --git a/modular_nova/modules/kahraman_equipment/code/clothing/mod.dm b/modular_nova/modules/kahraman_equipment/code/clothing/mod.dm
new file mode 100644
index 00000000000..f3ef68e8f66
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/code/clothing/mod.dm
@@ -0,0 +1,105 @@
+// Its modsuiting time
+
+/datum/mod_theme/frontier_colonist
+ name = "frontier hazard protective"
+ desc = "An unusual design of suit, in reality being no more than a slim underlayer with a built in coat and sealed helmet."
+ extended_desc = "The pinnacle of frontier cheap technology. Suits like this are usually not unified in design \
+ though are common in frontier settlements with less than optimal infrastructure. As with most unofficial \
+ designs, there are flaws and no single one is perfect, but they achieve a singular goal and that is the \
+ important part. Suits such as these are made specifically for the rare emergency that creates a hazard \
+ environment that other equipment just can't quite handle. Often, these suits are able to protect their users \
+ from not only electricity, but also radiation, biological hazards, other people, so on. This suit will not, \
+ however, protect you from yourself."
+
+ default_skin = "colonist"
+ armor_type = /datum/armor/colonist_hazard
+ resistance_flags = FIRE_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 7
+ charge_drain = DEFAULT_CHARGE_DRAIN * 2
+ slowdown_inactive = 1.5
+ slowdown_active = 1
+ inbuilt_modules = list(
+ /obj/item/mod/module/plate_compression/permanent,
+ /obj/item/mod/module/joint_torsion/permanent
+ )
+ allowed_suit_storage = list(
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/flashlight,
+ /obj/item/gun,
+ /obj/item/melee,
+ /obj/item/tank/internals,
+ /obj/item/storage/belt/holster,
+ /obj/item/construction,
+ /obj/item/fireaxe,
+ /obj/item/pipe_dispenser,
+ /obj/item/storage/bag,
+ /obj/item/pickaxe,
+ /obj/item/resonator,
+ /obj/item/t_scanner,
+ /obj/item/analyzer,
+ /obj/item/storage/medkit,
+ )
+ skins = list(
+ "colonist" = list(
+ MOD_ICON_OVERRIDE = 'modular_nova/modules/kahraman_equipment/icons/modsuits/mod.dmi',
+ MOD_WORN_ICON_OVERRIDE = 'modular_nova/modules/kahraman_equipment/icons/modsuits/mod_worn.dmi',
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/control/pre_equipped/frontier_colonist
+ theme = /datum/mod_theme/frontier_colonist
+ applied_cell = /obj/item/stock_parts/cell/high
+ applied_modules = list(
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/magboot,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/status_readout,
+ /obj/item/mod/module/thermal_regulator,
+ /obj/item/mod/module/rad_protection,
+ )
+ default_pins = list(
+ /obj/item/mod/module/magboot,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/thermal_regulator,
+ )
+
+/obj/item/mod/control/pre_equipped/frontier_colonist/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+// Plate compression module that cannot be removed
+
+/obj/item/mod/module/plate_compression/permanent
+ removable = FALSE
+ complexity = 0
+
+// Joint torsion module that can't be removed and has no complexity
+
+/obj/item/mod/module/joint_torsion/permanent
+ removable = FALSE
+ complexity = 0
diff --git a/modular_nova/modules/kahraman_equipment/code/clothing/storage.dm b/modular_nova/modules/kahraman_equipment/code/clothing/storage.dm
new file mode 100644
index 00000000000..3bd57a9fa94
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/code/clothing/storage.dm
@@ -0,0 +1,88 @@
+// Backpacks
+
+/obj/item/storage/backpack/industrial/frontier_colonist
+ name = "frontier backpack"
+ desc = "A rugged backpack often used by settlers and explorers. Holds all of your equipment and then some."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi'
+ icon_state = "backpack"
+ worn_icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi'
+ supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
+ worn_icon_teshari = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi'
+ worn_icon_state = "backpack"
+ inhand_icon_state = "backpack"
+
+/obj/item/storage/backpack/industrial/frontier_colonist/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+/obj/item/storage/backpack/industrial/frontier_colonist/satchel
+ name = "frontier satchel"
+ desc = "A rugged satchel often used by settlers and explorers. Holds less of your equipment than a backpack will."
+ icon_state = "satchel"
+ worn_icon_state = "satchel"
+
+/obj/item/storage/backpack/industrial/frontier_colonist/messenger
+ name = "frontier messenger bag"
+ desc = "A rugged messenger bag often used by settlers and explorers. Holds less of your equipment than a backpack will."
+ icon_state = "messenger"
+ worn_icon_state = "messenger"
+
+// Belts
+
+/obj/item/storage/belt/utility/frontier_colonist
+ name = "frontier chest rig"
+ desc = "A versatile chest rig with pockets to store really whatever you could think of within. \
+ That is, if whatever you could think of is within the realms of a utility belt. Fashion like this \
+ comes at a price you know!"
+ icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi'
+ icon_state = "harness"
+ worn_icon = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi'
+ supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
+ worn_icon_teshari = 'modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi'
+ worn_icon_state = "harness"
+ inhand_icon_state = null
+
+/obj/item/storage/belt/utility/frontier_colonist/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+ atom_storage.max_slots = 6
+ atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL
+ // Can hold whatever a toolbelt can + some mining equipment for convenience
+ atom_storage.set_holdable(list(
+ /obj/item/airlock_painter,
+ /obj/item/analyzer,
+ /obj/item/assembly/signaler,
+ /obj/item/clothing/gloves,
+ /obj/item/construction,
+ /obj/item/crowbar,
+ /obj/item/extinguisher/mini,
+ /obj/item/flashlight,
+ /obj/item/forcefield_projector,
+ /obj/item/geiger_counter,
+ /obj/item/holosign_creator,
+ /obj/item/inducer,
+ /obj/item/lightreplacer,
+ /obj/item/multitool,
+ /obj/item/pipe_dispenser,
+ /obj/item/pipe_painter,
+ /obj/item/plunger,
+ /obj/item/radio,
+ /obj/item/screwdriver,
+ /obj/item/stack/cable_coil,
+ /obj/item/t_scanner,
+ /obj/item/weldingtool,
+ /obj/item/wirecutters,
+ /obj/item/wrench,
+ /obj/item/spess_knife,
+ /obj/item/gps,
+ /obj/item/knife,
+ /obj/item/mining_scanner,
+ /obj/item/pickaxe,
+ /obj/item/reagent_containers/hypospray,
+ /obj/item/shovel,
+ /obj/item/survivalcapsule,
+ /obj/item/storage/bag/ore,
+ /obj/item/storage/fancy/cigarettes,
+ /obj/item/wormhole_jaunter,
+ /obj/item/resonator,
+ ))
diff --git a/modular_nova/modules/kahraman_equipment/code/gps_beacon.dm b/modular_nova/modules/kahraman_equipment/code/gps_beacon.dm
new file mode 100644
index 00000000000..dc7411a3e17
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/code/gps_beacon.dm
@@ -0,0 +1,23 @@
+/obj/item/gps/computer/beacon
+ name = "\improper GPS beacon"
+ desc = "A GPS beacon, anchored to the ground to prevent loss or accidental movement."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/gps_beacon.dmi'
+ icon_state = "gps_beacon"
+ pixel_y = 0
+ /// What this is undeployed back into
+ var/undeploy_type = /obj/item/flatpacked_machine/gps_beacon
+
+/obj/item/gps/computer/beacon/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/repackable, undeploy_type, 2 SECONDS)
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+/obj/item/flatpacked_machine/gps_beacon
+ name = "packed GPS beacon"
+ icon = 'modular_nova/modules/kahraman_equipment/icons/gps_beacon.dmi'
+ icon_state = "beacon_folded"
+ w_class = WEIGHT_CLASS_SMALL
+ type_to_deploy = /obj/item/gps/computer/beacon
+
+/obj/item/flatpacked_machine/gps_beacon/give_manufacturer_examine()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
diff --git a/modular_nova/modules/kahraman_equipment/code/looping_sounds.dm b/modular_nova/modules/kahraman_equipment/code/looping_sounds.dm
new file mode 100644
index 00000000000..b7edf20ccb5
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/code/looping_sounds.dm
@@ -0,0 +1,13 @@
+/datum/looping_sound/ore_thumper_fan
+ start_sound = 'modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_start.wav'
+ start_length = 3
+ mid_sounds = list(
+ 'modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_1.wav' = 1,
+ 'modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_2.wav' = 1,
+ 'modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_3.wav' = 1,
+ 'modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_4.wav' = 1,
+ )
+ mid_length = 3 SECONDS
+ end_sound = 'modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_end.wav'
+ volume = 100
+ falloff_exponent = 3
diff --git a/modular_nova/modules/kahraman_equipment/code/ore_thumper.dm b/modular_nova/modules/kahraman_equipment/code/ore_thumper.dm
new file mode 100644
index 00000000000..6f67a760deb
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/code/ore_thumper.dm
@@ -0,0 +1,271 @@
+#define SLAM_JAM_DELAY 15 SECONDS
+
+/obj/machinery/power/colony_ore_thumper
+ name = "ore thumper"
+ desc = "A frame with a heavy block of metal suspended atop a pipe. \
+ Must be deployed outdoors and given a wired power connection. \
+ Forces pressurized gas into the ground which brings up buried resources."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/ore_thumper.dmi'
+ icon_state = "thumper_idle"
+ density = TRUE
+ max_integrity = 250
+ idle_power_usage = 0
+ active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 25 // Should be 25 kw or half of a SOFIE generator
+ anchored = TRUE
+ can_change_cable_layer = FALSE
+ circuit = null
+ layer = ABOVE_MOB_LAYER
+ /// Are we currently working?
+ var/thumping = FALSE
+ /// Our looping fan sound that we play when turned on
+ var/datum/looping_sound/ore_thumper_fan/soundloop
+ /// How many times we've slammed, counts up until the number is high enough to make a box of materials
+ var/slam_jams = 0
+ /// How many times we need to slam in order to produce a box of materials
+ var/slam_jams_needed = 15
+ /// List of the thumping sounds we can choose from
+ var/static/list/list_of_thumper_sounds = list(
+ 'modular_nova/modules/kahraman_equipment/sound/thumper_thump/punch_press_1.wav',
+ 'modular_nova/modules/kahraman_equipment/sound/thumper_thump/punch_press_2.wav',
+ )
+ /// Keeps track of the callback timer to make sure we don't have more than one
+ var/callback_tracker
+ /// Weighted list of the ores we can spawn
+ var/static/list/ore_weight_list = list(
+ /obj/item/stack/ore/iron = 5,
+ /obj/item/stack/ore/glass/basalt = 5,
+ /obj/item/stack/ore/plasma = 4,
+ /obj/item/stack/ore/uranium = 3,
+ /obj/item/stack/ore/silver = 3,
+ /obj/item/stack/ore/gold = 3,
+ /obj/item/stack/ore/titanium = 3,
+ /obj/item/stack/ore/diamond = 2,
+ /obj/item/stack/ore/bluespace_crystal = 1,
+ )
+ /// How much of the listed types of ores should we spawn when spawning ore
+ var/static/list/ore_spawn_values = list(
+ /obj/item/stack/ore/iron = 25,
+ /obj/item/stack/ore/glass/basalt = 25,
+ /obj/item/stack/ore/plasma = 15,
+ /obj/item/stack/ore/uranium = 10,
+ /obj/item/stack/ore/silver = 10,
+ /obj/item/stack/ore/gold = 10,
+ /obj/item/stack/ore/titanium = 10,
+ /obj/item/stack/ore/diamond = 5,
+ /obj/item/stack/ore/bluespace_crystal = 1,
+ )
+ /// What's the limit for ore near us? Counts by stacks, not individual amounts of ore
+ var/nearby_ore_limit = 10
+ /// How far away does ore spawn?
+ var/ore_spawn_range = 2
+ /// What do we undeploy into
+ var/undeploy_type = /obj/item/flatpacked_machine/ore_thumper
+
+/obj/machinery/power/colony_ore_thumper/Initialize(mapload)
+ . = ..()
+ soundloop = new(src, FALSE)
+ AddElement(/datum/element/repackable, undeploy_type, 4 SECONDS)
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+/obj/machinery/power/colony_ore_thumper/add_context(
+ atom/source,
+ list/context,
+ obj/item/held_item,
+ mob/living/user,
+)
+
+ if(isnull(held_item))
+ if(panel_open)
+ context[SCREENTIP_CONTEXT_LMB] = "Activate Thumper"
+ return CONTEXTUAL_SCREENTIP_SET
+ return NONE
+
+
+/obj/machinery/power/colony_ore_thumper/examine(mob/user)
+ . = ..()
+ var/area/thumper_area = get_area(src)
+ if(!thumper_area.outdoors)
+ . += span_notice("Its must be constructed outdoors to function.")
+ if(!istype(get_turf(src), /turf/open/misc))
+ . += span_notice("It must be constructed on suitable terrain, like ash, snow, or sand.")
+ . += span_notice("It must have a powered, wired connection running beneath it with 25 kW of excess power to function.")
+ . += span_notice("It will produce a box of materials after it has slammed [slam_jams_needed] times.")
+ . += span_notice("Currently, it has slammed [slam_jams] / [slam_jams_needed] times needed.")
+ . += span_notice("It will stop producing resources if there are too many piles of ore near it.")
+
+
+/obj/machinery/power/colony_ore_thumper/process()
+ var/turf/our_turf = get_turf(src)
+ var/obj/structure/cable/cable_under_us = locate() in our_turf
+ if(!cable_under_us && powernet)
+ disconnect_from_network()
+ else if(cable_under_us && !powernet)
+ connect_to_network()
+ if(thumping)
+ if(!see_if_we_can_work(our_turf))
+ balloon_alert_to_viewers("invalid location!")
+ cut_that_out()
+ return
+ if(avail(active_power_usage))
+ add_load(active_power_usage)
+ else
+ balloon_alert_to_viewers("not enough power!")
+ cut_that_out()
+ else if(avail(idle_power_usage))
+ add_load(idle_power_usage)
+
+
+/// Checks the turf we are on to make sure we are outdoors and on a misc turf
+/obj/machinery/power/colony_ore_thumper/proc/see_if_we_can_work(turf/our_turf)
+ var/area/our_current_area = get_area(src)
+ if(!our_current_area.outdoors)
+ return FALSE
+ if(!istype(get_turf(src), /turf/open/misc))
+ return FALSE
+ return TRUE
+
+
+/obj/machinery/power/colony_ore_thumper/attack_hand(mob/user, list/modifiers)
+ . = ..()
+ if(.)
+ return
+
+ to_chat(user, span_notice("You toggle [src]'s power button."))
+
+ if(thumping)
+ cut_that_out(user)
+ return
+ start_her_up(user)
+
+
+/obj/machinery/power/colony_ore_thumper/attack_ai(mob/user)
+ return attack_hand(user)
+
+
+/obj/machinery/power/colony_ore_thumper/attack_robot(mob/user)
+ return attack_hand(user)
+
+
+/// Attempts turning the thumper on, failing if any of the conditions aren't met
+/obj/machinery/power/colony_ore_thumper/proc/start_her_up(mob/user)
+ var/turf/our_turf = get_turf(src)
+ var/obj/structure/cable/cable_under_us = locate() in our_turf
+ if(!cable_under_us && powernet)
+ balloon_alert(user, "not connected to wire")
+ return
+ if(!avail(active_power_usage))
+ balloon_alert(user, "not enough power")
+ return
+
+ thumping = TRUE
+ soundloop.start()
+
+ if(callback_tracker)
+ deltimer(callback_tracker)
+
+ balloon_alert(user, "thumper started")
+
+ callback_tracker = addtimer(CALLBACK(src, PROC_REF(slam_it_down)), SLAM_JAM_DELAY, TIMER_DELETE_ME | TIMER_STOPPABLE)
+
+
+/// Attempts to shut the thumper down
+/obj/machinery/power/colony_ore_thumper/proc/cut_that_out(mob/user)
+ thumping = FALSE
+ soundloop.stop()
+ if(user)
+ balloon_alert(user, "thumper stopped")
+
+
+/// Makes the machine slam down, producing a box of ore if it has been slamming long enough
+/obj/machinery/power/colony_ore_thumper/proc/slam_it_down()
+ if(!thumping)
+ return
+ var/turf/our_turf = get_turf(src)
+ if(!see_if_we_can_work(our_turf))
+ balloon_alert_to_viewers("invalid location!")
+ cut_that_out()
+ return
+ // Down we go
+ flick("thumper_slam", src)
+ playsound(src, pick(list_of_thumper_sounds), 80, TRUE)
+ if(slam_jams < (slam_jams_needed + 1))
+ slam_jams += 1
+
+ if(callback_tracker)
+ deltimer(callback_tracker)
+
+ callback_tracker = addtimer(CALLBACK(src, PROC_REF(slam_it_down)), SLAM_JAM_DELAY, TIMER_DELETE_ME | TIMER_STOPPABLE,)
+
+ // If the number of slams is less than that of what we need, then we can stop here
+ if(!(slam_jams >= slam_jams_needed))
+ return
+
+ var/nearby_ore = 0
+ var/is_there_a_thumper_too = FALSE
+ for(var/turf/nearby_turf in orange(ore_spawn_range, src))
+ for(var/ore as anything in nearby_turf.contents)
+ if(istype(ore, /obj/item/stack/ore))
+ nearby_ore += 1
+ continue
+ if(istype(ore, /obj/machinery/power/colony_ore_thumper))
+ if(ore == src)
+ continue
+ is_there_a_thumper_too = TRUE
+ break
+
+ if(nearby_ore > nearby_ore_limit)
+ balloon_alert_to_viewers("nearby ore too saturated")
+ return
+
+ if(is_there_a_thumper_too)
+ balloon_alert_to_viewers("too close to another thumper")
+ return
+
+ addtimer(CALLBACK(src, PROC_REF(make_some_ore)), 3 SECONDS, TIMER_DELETE_ME)
+
+
+/// Spawns an ore box on top of the thumper
+/obj/machinery/power/colony_ore_thumper/proc/make_some_ore()
+ var/list/nearby_valid_turfs = list()
+ for(var/turf/nearby_turf in orange(ore_spawn_range, src))
+ if(nearby_turf.is_blocked_turf(TRUE))
+ continue
+ if(!ismiscturf(nearby_turf))
+ continue
+ nearby_valid_turfs.Add(nearby_turf)
+ // Fallback in case somehow there are no valid nearby turfs
+ if(!length(nearby_valid_turfs))
+ nearby_valid_turfs.Add(get_turf(src))
+
+ for(var/iteration in 1 to rand(2, 4))
+ var/turf/target_turf = pick(nearby_valid_turfs)
+ var/obj/item/stack/new_ore = pick_weight(ore_weight_list)
+ var/obj/new_ore_pile = new new_ore(target_turf, ore_spawn_values[new_ore.type])
+ new /obj/effect/temp_visual/mook_dust/robot(target_turf)
+ playsound(new_ore_pile, 'modular_nova/master_files/sound/effects/robot_sit.ogg', 25, TRUE)
+
+ slam_jams -= slam_jams_needed
+
+
+// Item for deploying ore thumpers
+/obj/item/flatpacked_machine/ore_thumper
+ name = "flat-packed ore thumper"
+ icon = 'modular_nova/modules/kahraman_equipment/icons/ore_thumper_item.dmi'
+ icon_state = "thumper_packed"
+ type_to_deploy = /obj/machinery/power/colony_ore_thumper
+ custom_materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 15,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/titanium = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/silver = SHEET_MATERIAL_AMOUNT,
+ /datum/material/gold = HALF_SHEET_MATERIAL_AMOUNT,
+ )
+
+/obj/item/flatpacked_machine/ore_thumper/examine(mob/user)
+ . = ..()
+ . += span_notice("Its must be constructed outdoors to function.")
+ . += span_notice("It must be constructed on suitable terrain, like ash, snow, or sand.")
+ . += span_notice("It must have a powered, wired connection running beneath it to function.")
+
+/obj/item/flatpacked_machine/ore_thumper/give_manufacturer_examine()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
diff --git a/modular_nova/modules/kahraman_equipment/code/organic_printer.dm b/modular_nova/modules/kahraman_equipment/code/organic_printer.dm
new file mode 100644
index 00000000000..a548317c2e7
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/code/organic_printer.dm
@@ -0,0 +1,40 @@
+/obj/machinery/biogenerator/organic_printer
+ name = "organic materials printer"
+ desc = "An advanced machine seen in frontier outposts and colonies capable of turning organic plant matter into \
+ reagents and items of use that a fabricator can't typically make. While the exact designs these machines have differs from \
+ location to location, and upon who designed them, this one should be able to at the very least provide you with \
+ some clothing, basic food supplies, and whatever else you may require."
+ icon = 'modular_nova/modules/kahraman_equipment/icons/biogenerator.dmi'
+ circuit = null
+ anchored = FALSE
+ efficiency = 1
+ productivity = 2
+ max_items = 35
+ show_categories = list(
+ RND_CATEGORY_AKHTER_CLOTHING,
+ RND_CATEGORY_AKHTER_EQUIPMENT,
+ RND_CATEGORY_AKHTER_RESOURCES,
+ )
+
+/obj/machinery/biogenerator/organic_printer/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
+
+/obj/machinery/biogenerator/organic_printer/RefreshParts()
+ . = ..()
+ efficiency = 1
+ productivity = 2
+ max_items = 35
+
+/obj/machinery/biogenerator/organic_printer/default_deconstruction_crowbar()
+ return
+
+// Deployable item for cargo for the organics printer
+/obj/item/flatpacked_machine/organics_printer
+ name = "organic materials printer parts kit"
+ icon = 'modular_nova/modules/kahraman_equipment/icons/biogenerator.dmi'
+ icon_state = "biogenerator_parts"
+ type_to_deploy = /obj/machinery/biogenerator/organic_printer
+
+/obj/item/flatpacked_machine/organics_printer/give_manufacturer_examine()
+ AddElement(/datum/element/manufacturer_examine, COMPANY_KAHRAMAN)
diff --git a/modular_nova/modules/kahraman_equipment/code/organic_printer_designs/clothing.dm b/modular_nova/modules/kahraman_equipment/code/organic_printer_designs/clothing.dm
new file mode 100644
index 00000000000..478dd89beb5
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/code/organic_printer_designs/clothing.dm
@@ -0,0 +1,132 @@
+// Jumpsuit
+
+/datum/design/frontier_jumpsuit
+ name = "Frontier Jumpsuit"
+ id = "frontier_jumpsuit"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 75)
+ build_path = /obj/item/clothing/under/frontier_colonist
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_CLOTHING,
+ )
+
+// Boots
+
+/datum/design/frontier_boots
+ name = "Heavy Frontier Boots"
+ id = "frontier_boots"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 50)
+ build_path = /obj/item/clothing/shoes/jackboots/frontier_colonist
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_CLOTHING,
+ )
+
+// Gloves
+
+/datum/design/frontier_gloves
+ name = "Frontier Gloves"
+ id = "frontier_gloves"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 50)
+ build_path = /obj/item/clothing/gloves/frontier_colonist
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_CLOTHING,
+ )
+
+// Suit items
+
+/datum/design/frontier_trench
+ name = "Frontier Trenchcoat"
+ id = "frontier_trench"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 100)
+ build_path = /obj/item/clothing/suit/jacket/frontier_colonist
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_CLOTHING,
+ )
+
+/datum/design/frontier_jacket
+ name = "Frontier Jacket"
+ id = "frontier_jacket"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 100)
+ build_path = /obj/item/clothing/suit/jacket/frontier_colonist/short
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_CLOTHING,
+ )
+
+/datum/design/frontier_med_jacket
+ name = "Frontier Medical Jacket"
+ id = "frontier_med_jacket"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 125)
+ build_path = /obj/item/clothing/suit/jacket/frontier_colonist/medical
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_CLOTHING,
+ )
+
+/datum/design/frontier_flak
+ name = "Frontier Flak Jacket"
+ id = "frontier_flak_jacket"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 150)
+ build_path = /obj/item/clothing/suit/frontier_colonist_flak
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_CLOTHING,
+ )
+
+/datum/design/frontier_tanker_helmet
+ name = "Frontier Soft Helmet"
+ id = "frontier_tanker_helmet"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 150)
+ build_path = /obj/item/clothing/head/frontier_colonist_helmet
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_CLOTHING,
+ )
+
+// Hats
+
+/datum/design/frontier_cap
+ name = "Frontier Soft Cap"
+ id = "frontier_cap"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 75)
+ build_path = /obj/item/clothing/head/soft/frontier_colonist
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_CLOTHING,
+ )
+
+/datum/design/frontier_cap_med
+ name = "Frontier Medical Cap"
+ id = "frontier_cap_med"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 100)
+ build_path = /obj/item/clothing/head/soft/frontier_colonist/medic
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_CLOTHING,
+ )
+
+// That one gas mask
+
+/datum/design/frontier_mask
+ name = "Frontier Gas Mask"
+ id = "frontier_mask"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 100)
+ build_path = /obj/item/clothing/mask/gas/atmos/frontier_colonist
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_CLOTHING,
+ )
diff --git a/modular_nova/modules/kahraman_equipment/code/organic_printer_designs/equipment.dm b/modular_nova/modules/kahraman_equipment/code/organic_printer_designs/equipment.dm
new file mode 100644
index 00000000000..cc019598dc9
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/code/organic_printer_designs/equipment.dm
@@ -0,0 +1,69 @@
+// Belts
+
+/datum/design/frontier_chest_rig
+ name = "Frontier Chest Rig"
+ id = "frontier_chest_rig"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 150)
+ build_path = /obj/item/storage/belt/utility/frontier_colonist
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_EQUIPMENT,
+ )
+
+/datum/design/frontier_med_belt
+ name = "Satchel Medical Kit"
+ id = "frontier_med_belt"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 200)
+ build_path = /obj/item/storage/backpack/duffelbag/deforest_medkit
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_EQUIPMENT,
+ )
+
+/datum/design/frontier_medkit
+ name = "Frontier Medical Kit"
+ id = "frontier_medkit"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 100)
+ build_path = /obj/item/storage/medkit/frontier
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_EQUIPMENT,
+ )
+
+// Backpacks
+
+/datum/design/frontier_backpack
+ name = "Frontier Backpack"
+ id = "frontier_backpack"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 100)
+ build_path = /obj/item/storage/backpack/industrial/frontier_colonist
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_EQUIPMENT,
+ )
+
+/datum/design/frontier_satchel
+ name = "Frontier Satchel"
+ id = "frontier_satchel"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 100)
+ build_path = /obj/item/storage/backpack/industrial/frontier_colonist/satchel
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_EQUIPMENT,
+ )
+
+/datum/design/frontier_messenger
+ name = "Frontier Messenger Bag"
+ id = "frontier_messenger"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 100)
+ build_path = /obj/item/storage/backpack/industrial/frontier_colonist/messenger
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_EQUIPMENT,
+ )
diff --git a/modular_nova/modules/kahraman_equipment/code/organic_printer_designs/resources.dm b/modular_nova/modules/kahraman_equipment/code/organic_printer_designs/resources.dm
new file mode 100644
index 00000000000..9fb685140db
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/code/organic_printer_designs/resources.dm
@@ -0,0 +1,21 @@
+/datum/design/organic_plastic
+ name = "Plastic Sheet"
+ id = "oganic_plastic"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 25)
+ build_path = /obj/item/stack/sheet/plastic
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_RESOURCES,
+ )
+
+/datum/design/organic_cloth
+ name = "Cloth"
+ id = "oganic_cloth"
+ build_type = BIOGENERATOR
+ materials = list(/datum/material/biomass = 10)
+ build_path = /obj/item/stack/sheet/cloth
+ category = list(
+ RND_CATEGORY_INITIAL,
+ RND_CATEGORY_AKHTER_RESOURCES,
+ )
diff --git a/modular_nova/modules/kahraman_equipment/icons/biogenerator.dmi b/modular_nova/modules/kahraman_equipment/icons/biogenerator.dmi
new file mode 100644
index 00000000000..5fd87ee0ace
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/icons/biogenerator.dmi differ
diff --git a/modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi b/modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi
new file mode 100644
index 00000000000..bb4d9cec962
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/icons/clothes/clothing.dmi differ
diff --git a/modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi b/modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi
new file mode 100644
index 00000000000..fa232094446
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn.dmi differ
diff --git a/modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_digi.dmi b/modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_digi.dmi
new file mode 100644
index 00000000000..cb954472b24
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_digi.dmi differ
diff --git a/modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi b/modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi
new file mode 100644
index 00000000000..da167942a77
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/icons/clothes/clothing_worn_teshari.dmi differ
diff --git a/modular_nova/modules/kahraman_equipment/icons/gps_beacon.dmi b/modular_nova/modules/kahraman_equipment/icons/gps_beacon.dmi
new file mode 100644
index 00000000000..62c9cc11fa4
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/icons/gps_beacon.dmi differ
diff --git a/modular_nova/modules/kahraman_equipment/icons/modsuits/mod.dmi b/modular_nova/modules/kahraman_equipment/icons/modsuits/mod.dmi
new file mode 100644
index 00000000000..a2e6c642b18
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/icons/modsuits/mod.dmi differ
diff --git a/modular_nova/modules/kahraman_equipment/icons/modsuits/mod_worn.dmi b/modular_nova/modules/kahraman_equipment/icons/modsuits/mod_worn.dmi
new file mode 100644
index 00000000000..99254f654a4
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/icons/modsuits/mod_worn.dmi differ
diff --git a/modular_nova/modules/kahraman_equipment/icons/ore_thumper.dmi b/modular_nova/modules/kahraman_equipment/icons/ore_thumper.dmi
new file mode 100644
index 00000000000..a99b196ae67
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/icons/ore_thumper.dmi differ
diff --git a/modular_nova/modules/kahraman_equipment/icons/ore_thumper_item.dmi b/modular_nova/modules/kahraman_equipment/icons/ore_thumper_item.dmi
new file mode 100644
index 00000000000..7c53ff0f06a
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/icons/ore_thumper_item.dmi differ
diff --git a/modular_nova/modules/kahraman_equipment/sound/attributions.txt b/modular_nova/modules/kahraman_equipment/sound/attributions.txt
new file mode 100644
index 00000000000..19b7ff0e57e
--- /dev/null
+++ b/modular_nova/modules/kahraman_equipment/sound/attributions.txt
@@ -0,0 +1,7 @@
+The sounds at modular_skyrat/modules/colony_fabriactor_event_code/sound/thumper_fan are from https://pixabay.com/sound-effects/fan-1-6360/
+
+The sounds at modular_skyrat/modules/colony_fabriactor_event_code/sound/thumper_thump are from https://pixabay.com/sound-effects/punch-press-10-ton-clip1-104921/
+
+The following sounds are from https://pixabay.com/sound-effects/sos-signal-137144/
+
+modular_skyrat/modules/colony_fabricator/sound/morse_signal.wav
diff --git a/modular_nova/modules/kahraman_equipment/sound/morse_signal.wav b/modular_nova/modules/kahraman_equipment/sound/morse_signal.wav
new file mode 100644
index 00000000000..2f0dd568209
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/sound/morse_signal.wav differ
diff --git a/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_end.wav b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_end.wav
new file mode 100644
index 00000000000..f89e8d20ff9
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_end.wav differ
diff --git a/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_1.wav b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_1.wav
new file mode 100644
index 00000000000..96582d0ce32
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_1.wav differ
diff --git a/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_2.wav b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_2.wav
new file mode 100644
index 00000000000..e1656ec08ef
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_2.wav differ
diff --git a/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_3.wav b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_3.wav
new file mode 100644
index 00000000000..7d7275b5bea
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_3.wav differ
diff --git a/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_4.wav b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_4.wav
new file mode 100644
index 00000000000..7298e4eddca
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_mid_4.wav differ
diff --git a/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_start.wav b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_start.wav
new file mode 100644
index 00000000000..c3e95ad1e8b
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/sound/thumper_fan/fan_start.wav differ
diff --git a/modular_nova/modules/kahraman_equipment/sound/thumper_thump/punch_press_1.wav b/modular_nova/modules/kahraman_equipment/sound/thumper_thump/punch_press_1.wav
new file mode 100644
index 00000000000..513bb1f8f19
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/sound/thumper_thump/punch_press_1.wav differ
diff --git a/modular_nova/modules/kahraman_equipment/sound/thumper_thump/punch_press_2.wav b/modular_nova/modules/kahraman_equipment/sound/thumper_thump/punch_press_2.wav
new file mode 100644
index 00000000000..c4d259839b3
Binary files /dev/null and b/modular_nova/modules/kahraman_equipment/sound/thumper_thump/punch_press_2.wav differ
diff --git a/modular_nova/modules/modular_implants/code/nifs.dm b/modular_nova/modules/modular_implants/code/nifs.dm
index 5ec2b9b17fa..35177c8c0cf 100644
--- a/modular_nova/modules/modular_implants/code/nifs.dm
+++ b/modular_nova/modules/modular_implants/code/nifs.dm
@@ -126,7 +126,7 @@
QDEL_LIST(loaded_nifsofts)
return ..()
-/obj/item/organ/internal/cyberimp/brain/nif/Insert(mob/living/carbon/human/insertee, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/cyberimp/brain/nif/Insert(mob/living/carbon/human/insertee, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
if(linked_mob && stored_ckey != insertee.ckey && theft_protection)
diff --git a/modular_nova/modules/organs/code/heart.dm b/modular_nova/modules/organs/code/heart.dm
index 857e1f05749..bc2f8ebc59b 100644
--- a/modular_nova/modules/organs/code/heart.dm
+++ b/modular_nova/modules/organs/code/heart.dm
@@ -9,7 +9,7 @@
COOLDOWN_DECLARE(shell_effect_cd)
-/obj/item/organ/internal/heart/snail/on_insert(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/heart/snail/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner))
return
@@ -22,7 +22,7 @@
RegisterSignal(human_owner, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, PROC_REF(modify_damage))
RegisterSignal(human_owner, COMSIG_MOB_AFTER_APPLY_DAMAGE, PROC_REF(do_block_effect))
-/obj/item/organ/internal/heart/snail/on_remove(mob/living/carbon/organ_owner, special)
+/obj/item/organ/internal/heart/snail/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner) || QDELETED(organ_owner))
return
diff --git a/modular_nova/modules/organs/code/tongue.dm b/modular_nova/modules/organs/code/tongue.dm
index 957ee29fcda..c803bfc90c9 100644
--- a/modular_nova/modules/organs/code/tongue.dm
+++ b/modular_nova/modules/organs/code/tongue.dm
@@ -12,7 +12,7 @@
icon_state = "tongue"
modifies_speech = TRUE
-/obj/item/organ/internal/tongue/dog/Insert(mob/living/carbon/signer, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/tongue/dog/Insert(mob/living/carbon/signer, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
signer.verb_ask = "arfs"
signer.verb_exclaim = "wans"
@@ -34,7 +34,7 @@
icon_state = "tongue"
modifies_speech = TRUE
-/obj/item/organ/internal/tongue/avian/Insert(mob/living/carbon/signer, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/internal/tongue/avian/Insert(mob/living/carbon/signer, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
signer.verb_ask = "peeps"
signer.verb_exclaim = "squawks"
diff --git a/modular_nova/modules/primitive_structures/code/storage_structures.dm b/modular_nova/modules/primitive_structures/code/storage_structures.dm
index 1fa28126a47..ea4d9616108 100644
--- a/modular_nova/modules/primitive_structures/code/storage_structures.dm
+++ b/modular_nova/modules/primitive_structures/code/storage_structures.dm
@@ -58,7 +58,7 @@
icon = 'modular_nova/modules/primitive_structures/icons/storage.dmi'
resistance_flags = FLAMMABLE
base_build_path = /obj/machinery/smartfridge/producebin
- contents_icon_state = "produce"
+ base_icon_state = "produce"
use_power = NO_POWER_USE
light_power = 0
idle_power_usage = 0
@@ -89,7 +89,7 @@
icon = 'modular_nova/modules/primitive_structures/icons/storage.dmi'
resistance_flags = FLAMMABLE
base_build_path = /obj/machinery/smartfridge/seedshelf
- contents_icon_state = "seed"
+ base_icon_state = "seed"
use_power = NO_POWER_USE
light_power = 0
idle_power_usage = 0
@@ -119,7 +119,7 @@
icon = 'modular_nova/modules/primitive_structures/icons/storage.dmi'
resistance_flags = FLAMMABLE
base_build_path = /obj/machinery/smartfridge/rationshelf
- contents_icon_state = "ration"
+ base_icon_state = "ration"
use_power = NO_POWER_USE
light_power = 0
idle_power_usage = 0
@@ -149,7 +149,7 @@
icon = 'modular_nova/modules/primitive_structures/icons/storage.dmi'
resistance_flags = FLAMMABLE
base_build_path = /obj/machinery/smartfridge/producedisplay
- contents_icon_state = "nonfood"
+ base_icon_state = "nonfood"
use_power = NO_POWER_USE
light_power = 0
idle_power_usage = 0
diff --git a/modular_nova/modules/synths/code/bodyparts/brain.dm b/modular_nova/modules/synths/code/bodyparts/brain.dm
index 5638c39269f..bd4564c0886 100644
--- a/modular_nova/modules/synths/code/bodyparts/brain.dm
+++ b/modular_nova/modules/synths/code/bodyparts/brain.dm
@@ -10,15 +10,15 @@
/// The last time (in ticks) a message about brain damage was sent. Don't touch.
var/last_message_time = 0
-/obj/item/organ/internal/brain/synth/Insert(mob/living/carbon/user, special = FALSE, drop_if_replaced = TRUE, no_id_transfer = FALSE)
+/obj/item/organ/internal/brain/synth/mob_insert(mob/living/carbon/brain_owner, special = FALSE, movement_flags)
. = ..()
- if(user.stat != DEAD || !ishuman(user))
+ if(brain_owner.stat != DEAD || !ishuman(brain_owner))
return
- var/mob/living/carbon/human/user_human = user
- if(HAS_TRAIT(user_human, TRAIT_REVIVES_BY_HEALING) && user_human.health > SYNTH_BRAIN_WAKE_THRESHOLD)
- user_human.revive(FALSE)
+ var/mob/living/carbon/human/human_brain_owner = brain_owner
+ if(HAS_TRAIT(human_brain_owner, TRAIT_REVIVES_BY_HEALING) && human_brain_owner.health > SYNTH_BRAIN_WAKE_THRESHOLD)
+ human_brain_owner.revive(FALSE)
/obj/item/organ/internal/brain/synth/emp_act(severity) // EMP act against the posi, keep the cap far below the organ health
. = ..()
diff --git a/modular_nova/modules/synths/code/bodyparts/stomach.dm b/modular_nova/modules/synths/code/bodyparts/stomach.dm
index 437d9eacff2..686774162ec 100644
--- a/modular_nova/modules/synths/code/bodyparts/stomach.dm
+++ b/modular_nova/modules/synths/code/bodyparts/stomach.dm
@@ -47,7 +47,7 @@
)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE
-/obj/item/organ/internal/stomach/synth/Insert(mob/living/carbon/receiver, special, drop_if_replaced)
+/obj/item/organ/internal/stomach/synth/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
RegisterSignal(receiver, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(on_borg_charge))
diff --git a/modular_nova/tools/merge-upstream-pull-request.sh b/modular_nova/tools/merge-upstream-pull-request.sh
old mode 100644
new mode 100755
diff --git a/modular_nova/tools/nova_check_grep.sh b/modular_nova/tools/nova_check_grep.sh
old mode 100644
new mode 100755
diff --git a/tff_modular/modules/nabbers/code/nabber_organs.dm b/tff_modular/modules/nabbers/code/nabber_organs.dm
index f15456dbedf..7bb98527030 100644
--- a/tff_modular/modules/nabbers/code/nabber_organs.dm
+++ b/tff_modular/modules/nabbers/code/nabber_organs.dm
@@ -45,7 +45,7 @@
var/datum/action/toggle_welding/shield
var/active = FALSE
-/obj/item/organ/internal/eyes/robotic/nabber/Insert(mob/living/carbon/eye_recipient, special, drop_if_replaced)
+/obj/item/organ/internal/eyes/robotic/nabber/Insert(mob/living/carbon/eye_recipient, special, movement_flags)
. = ..()
shield = new(eye_recipient)
shield.button_icon = ORGGAN_ICON_NABBER
diff --git a/tgstation.dme b/tgstation.dme
index 2e12830c4c8..a06164488ef 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -159,6 +159,7 @@
#include "code\__DEFINES\nuclear_bomb.dm"
#include "code\__DEFINES\obj_flags.dm"
#include "code\__DEFINES\observers.dm"
+#include "code\__DEFINES\organ_movement.dm"
#include "code\__DEFINES\overlays.dm"
#include "code\__DEFINES\pai.dm"
#include "code\__DEFINES\paintings.dm"
@@ -4256,8 +4257,6 @@
#include "code\modules\jobs\job_types\medical_doctor.dm"
#include "code\modules\jobs\job_types\mime.dm"
#include "code\modules\jobs\job_types\paramedic.dm"
-#include "code\modules\jobs\job_types\personal_ai.dm"
-#include "code\modules\jobs\job_types\positronic_brain.dm"
#include "code\modules\jobs\job_types\prisoner.dm"
#include "code\modules\jobs\job_types\psychologist.dm"
#include "code\modules\jobs\job_types\quartermaster.dm"
@@ -4265,7 +4264,6 @@
#include "code\modules\jobs\job_types\roboticist.dm"
#include "code\modules\jobs\job_types\scientist.dm"
#include "code\modules\jobs\job_types\security_officer.dm"
-#include "code\modules\jobs\job_types\servant_golem.dm"
#include "code\modules\jobs\job_types\shaft_miner.dm"
#include "code\modules\jobs\job_types\station_engineer.dm"
#include "code\modules\jobs\job_types\unassigned.dm"
@@ -4311,6 +4309,9 @@
#include "code\modules\jobs\job_types\spawner\lavaland_syndicate.dm"
#include "code\modules\jobs\job_types\spawner\lifebringer.dm"
#include "code\modules\jobs\job_types\spawner\maintenance_drone.dm"
+#include "code\modules\jobs\job_types\spawner\personal_ai.dm"
+#include "code\modules\jobs\job_types\spawner\positronic_brain.dm"
+#include "code\modules\jobs\job_types\spawner\servant_golem.dm"
#include "code\modules\jobs\job_types\spawner\skeleton.dm"
#include "code\modules\jobs\job_types\spawner\space_bar_patron.dm"
#include "code\modules\jobs\job_types\spawner\space_bartender.dm"
@@ -5802,6 +5803,7 @@
#include "code\modules\surgery\organs\_organ.dm"
#include "code\modules\surgery\organs\autosurgeon.dm"
#include "code\modules\surgery\organs\helpers.dm"
+#include "code\modules\surgery\organs\organ_movement.dm"
#include "code\modules\surgery\organs\external\_external_organ.dm"
#include "code\modules\surgery\organs\external\restyling.dm"
#include "code\modules\surgery\organs\external\spines.dm"
@@ -7319,6 +7321,17 @@
#include "modular_nova\modules\jukebox\code\dance_machine.dm"
#include "modular_nova\modules\jukebox\code\jukebox_subsystem.dm"
#include "modular_nova\modules\jungle\code\flora.dm"
+#include "modular_nova\modules\kahraman_equipment\code\gps_beacon.dm"
+#include "modular_nova\modules\kahraman_equipment\code\looping_sounds.dm"
+#include "modular_nova\modules\kahraman_equipment\code\ore_thumper.dm"
+#include "modular_nova\modules\kahraman_equipment\code\organic_printer.dm"
+#include "modular_nova\modules\kahraman_equipment\code\clothing\armor_datums.dm"
+#include "modular_nova\modules\kahraman_equipment\code\clothing\clothing.dm"
+#include "modular_nova\modules\kahraman_equipment\code\clothing\mod.dm"
+#include "modular_nova\modules\kahraman_equipment\code\clothing\storage.dm"
+#include "modular_nova\modules\kahraman_equipment\code\organic_printer_designs\clothing.dm"
+#include "modular_nova\modules\kahraman_equipment\code\organic_printer_designs\equipment.dm"
+#include "modular_nova\modules\kahraman_equipment\code\organic_printer_designs\resources.dm"
#include "modular_nova\modules\knives\knives.dm"
#include "modular_nova\modules\layer_shift\code\mob_movement.dm"
#include "modular_nova\modules\liquids\code\drains.dm"
diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher.jsx b/tgui/packages/tgui/interfaces/CentcomPodLauncher.jsx
index 34dd4b36261..9b53499f046 100644
--- a/tgui/packages/tgui/interfaces/CentcomPodLauncher.jsx
+++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher.jsx
@@ -3,7 +3,7 @@ import { classes } from 'common/react';
import { storage } from 'common/storage';
import { multiline } from 'common/string';
import { createUuid } from 'common/uuid';
-import { Component, Fragment, useState } from 'react';
+import { Component, Fragment } from 'react';
import { useBackend, useLocalState } from '../backend';
import {
@@ -25,7 +25,7 @@ const pod_grey = {
};
const useCompact = () => {
- const [compact, setCompact] = useState(false);
+ const [compact, setCompact] = useLocalState('compact', false);
const toggleCompact = () => setCompact(!compact);
return [compact, toggleCompact];
};
@@ -830,10 +830,10 @@ class PresetsPage extends Component {
render() {
const { presets } = this.state;
const { act, data } = useBackend();
- const [presetIndex, setSelectedPreset] = useState(0);
- const [settingName, setEditingNameStatus] = useState(0);
- const [newNameText, setText] = useState('');
- const [hue, setHue] = useState(0);
+ const [presetIndex, setSelectedPreset] = useLocalState('presetIdx', 0);
+ const [settingName, setEditingNameStatus] = useLocalState('settingName', 0);
+ const [newNameText, setText] = useLocalState('nameText', '');
+ const [hue, setHue] = useLocalState('hue', 0);
return (
{
/** Individual crew tab */
const CrewTab = (props: { record: MedicalRecord }) => {
- const [selectedRecord, setSelectedRecord] = useState<
+ const [selectedRecord, setSelectedRecord] = useLocalState<
MedicalRecord | undefined
- >();
+ >('medicalRecord', undefined);
const { act, data } = useBackend();
const { assigned_view } = data;
diff --git a/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx b/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx
index 70e68963705..4670d9b06b1 100644
--- a/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx
+++ b/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx
@@ -1,5 +1,6 @@
import { filter, sortBy } from 'common/collections';
import { flow } from 'common/fp';
+import { useState } from 'react';
import { useBackend, useLocalState } from 'tgui/backend';
import {
Box,
@@ -26,7 +27,7 @@ export const SecurityRecordTabs = (props) => {
? 'No records found.'
: 'No match. Refine your search.';
- const [search, setSearch] = useLocalState('search', '');
+ const [search, setSearch] = useState('');
const sorted: SecurityRecord[] = flow([
filter((record: SecurityRecord) => isRecordMatch(record, search)),
diff --git a/tools/build/build.js b/tools/build/build.js
index 2eda2ebed01..386d648c029 100644
--- a/tools/build/build.js
+++ b/tools/build/build.js
@@ -188,6 +188,7 @@ export const DmTarget = new Juke.Target({
],
inputs: [
'_maps/map_files/generic/**',
+ 'maps/**/*.dm',
'code/**',
'html/**',
'icons/**',
diff --git a/tools/pull_request_hooks/rerunFlakyTests.js b/tools/pull_request_hooks/rerunFlakyTests.js
index a0d96ebcad5..6625d9caa8c 100644
--- a/tools/pull_request_hooks/rerunFlakyTests.js
+++ b/tools/pull_request_hooks/rerunFlakyTests.js
@@ -4,203 +4,221 @@ const TITLE_BOT_HEADER = "title: ";
// Only check jobs that start with these.
// Helps make sure we don't restart something like screenshot tests or linters, which are not known to be flaky.
const CONSIDERED_JOBS = [
- "Integration Tests",
+ "Integration Tests",
];
async function getFailedJobsForRun(github, context, workflowRunId, runAttempt) {
- const {
- data: { jobs },
- } = await github.rest.actions.listJobsForWorkflowRunAttempt({
- owner: context.repo.owner,
- repo: context.repo.repo,
- run_id: workflowRunId,
- attempt_number: runAttempt,
- });
-
- return jobs
- .filter((job) => job.conclusion === "failure")
- .filter((job) =>
- CONSIDERED_JOBS.some((title) => job.name.startsWith(title))
- );
+ const {
+ data: { jobs },
+ } = await github.rest.actions.listJobsForWorkflowRunAttempt({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ run_id: workflowRunId,
+ attempt_number: runAttempt,
+ });
+
+ return jobs
+ .filter((job) => job.conclusion === "failure")
+ .filter((job) =>
+ CONSIDERED_JOBS.some((title) => job.name.startsWith(title))
+ );
}
export async function rerunFlakyTests({ github, context }) {
- const failingJobs = await getFailedJobsForRun(
- github,
- context,
- context.payload.workflow_run.id,
- context.payload.workflow_run.run_attempt
- );
-
- if (failingJobs.length > 1) {
- console.log("Multiple jobs failing. PROBABLY not flaky, not rerunning.");
- return;
- }
-
- if (failingJobs.length === 0) {
- throw new Error(
- "rerunFlakyTests should not have run on a run with no failing jobs"
- );
- }
-
- github.rest.actions.reRunWorkflowFailedJobs({
- owner: context.repo.owner,
- repo: context.repo.repo,
- run_id: context.payload.workflow_run.id,
- });
+ const failingJobs = await getFailedJobsForRun(
+ github,
+ context,
+ context.payload.workflow_run.id,
+ context.payload.workflow_run.run_attempt
+ );
+
+ if (failingJobs.length > 1) {
+ console.log("Multiple jobs failing. PROBABLY not flaky, not rerunning.");
+ return;
+ }
+
+ if (failingJobs.length === 0) {
+ throw new Error(
+ "rerunFlakyTests should not have run on a run with no failing jobs"
+ );
+ }
+
+ github.rest.actions.reRunWorkflowFailedJobs({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ run_id: context.payload.workflow_run.id,
+ });
}
// Tries its best to extract a useful error title and message for the given log
export function extractDetails(log) {
- // Strip off timestamp
- const lines = log.split(/^[0-9.:T\-]*?Z /gm);
-
- const failureRegex = /^\t?FAILURE #(?[0-9]+): (?.+)/;
- const groupRegex = /^##\[group\](?.+)/;
-
- const failures = [];
- let lastGroup = "root";
- let loggingFailure;
-
- const newFailure = (failureMatch) => {
- const { headline } = failureMatch.groups;
-
- loggingFailure = {
- headline,
- group: lastGroup.replace("/datum/unit_test/", ""),
- details: [],
- };
- };
-
- for (const line of lines) {
- const groupMatch = line.match(groupRegex);
- if (groupMatch) {
- lastGroup = groupMatch.groups.group.trim();
- continue;
- }
-
- const failureMatch = line.match(failureRegex);
-
- if (loggingFailure === undefined) {
- if (!failureMatch) {
- continue;
- }
-
- newFailure(failureMatch);
- } else if (failureMatch || line.startsWith("##")) {
- failures.push(loggingFailure);
- loggingFailure = undefined;
-
- if (failureMatch) {
- newFailure(failureMatch);
- }
- } else {
- loggingFailure.details.push(line.trim());
- }
- }
-
- // We had no logged failures, there's not really anything we can do here
- if (failures.length === 0) {
- return {
- title: "Flaky test failure with no obvious source",
- failures,
- };
- }
-
- // We *could* create multiple failures for multiple groups.
- // This would be important if we had multiple flaky tests at the same time.
- // I'm choosing not to because it complicates this logic a bit, has the ability to go terribly wrong,
- // and also because there's something funny to me about that increasing the urgency of fixing
- // flaky tests. If it becomes a serious issue though, I would not mind this being fixed.
- const uniqueGroups = new Set(failures.map((failure) => failure.group));
-
- if (uniqueGroups.size > 1) {
- return {
- title: `Multiple flaky test failures in ${Array.from(uniqueGroups)
- .sort()
- .join(", ")}`,
- failures,
- };
- }
-
- const failGroup = failures[0].group;
-
- if (failures.length > 1) {
- return {
- title: `Multiple errors in flaky test ${failGroup}`,
- failures,
- };
- }
-
- const failure = failures[0];
-
- // Common patterns where we can always get a detailed title
- const runtimeMatch = failure.headline.match(/Runtime in .+?: (?.+)/);
- if (runtimeMatch) {
- return {
- title: `Flaky test ${failGroup}: ${runtimeMatch.groups.error.trim()}`,
- failures,
- };
- }
-
- // Try to normalize the title and remove anything that might be variable
- const normalizedError = failure.headline.replace(/\s*at .+?:[0-9]+.*/g, ""); // " at code.dm:123"
-
- return {
- title: `Flaky test ${failGroup}: ${normalizedError}`,
- failures,
- };
+ // Strip off timestamp
+ const lines = log.split(/^[0-9.:T\-]*?Z /gm);
+
+ const failureRegex = /^\t?FAILURE #(?[0-9]+): (?.+)/;
+ const groupRegex = /^##\[group\](?.+)/;
+
+ const failures = [];
+ let lastGroup = "root";
+ let loggingFailure;
+
+ const newFailure = (failureMatch) => {
+ const { headline } = failureMatch.groups;
+
+ loggingFailure = {
+ headline,
+ group: lastGroup.replace("/datum/unit_test/", ""),
+ details: [],
+ };
+ };
+
+ for (const line of lines) {
+ const groupMatch = line.match(groupRegex);
+ if (groupMatch) {
+ lastGroup = groupMatch.groups.group.trim();
+ continue;
+ }
+
+ const failureMatch = line.match(failureRegex);
+
+ if (loggingFailure === undefined) {
+ if (!failureMatch) {
+ continue;
+ }
+
+ newFailure(failureMatch);
+ } else if (failureMatch || line.startsWith("##")) {
+ failures.push(loggingFailure);
+ loggingFailure = undefined;
+
+ if (failureMatch) {
+ newFailure(failureMatch);
+ }
+ } else {
+ loggingFailure.details.push(line.trim());
+ }
+ }
+
+ // We had no logged failures, there's not really anything we can do here
+ if (failures.length === 0) {
+ return {
+ title: "Flaky test failure with no obvious source",
+ failures,
+ };
+ }
+
+ // We *could* create multiple failures for multiple groups.
+ // This would be important if we had multiple flaky tests at the same time.
+ // I'm choosing not to because it complicates this logic a bit, has the ability to go terribly wrong,
+ // and also because there's something funny to me about that increasing the urgency of fixing
+ // flaky tests. If it becomes a serious issue though, I would not mind this being fixed.
+ const uniqueGroups = new Set(failures.map((failure) => failure.group));
+
+ if (uniqueGroups.size > 1) {
+ return {
+ title: `Multiple flaky test failures in ${Array.from(uniqueGroups)
+ .sort()
+ .join(", ")}`,
+ failures,
+ };
+ }
+
+ const failGroup = failures[0].group;
+
+ if (failures.length > 1) {
+ return {
+ title: `Multiple errors in flaky test ${failGroup}`,
+ failures,
+ };
+ }
+
+ const failure = failures[0];
+
+ // Common patterns where we can always get a detailed title
+ const runtimeMatch = failure.headline.match(/Runtime in .+?: (?.+)/);
+ if (runtimeMatch) {
+ const runtime = runtimeMatch.groups.error.trim();
+
+ const invalidTimerMatch = runtime.match(/^Invalid timer:.+object:(?