diff --git a/code/datums/components/bakeable.dm b/code/datums/components/bakeable.dm
index 8967493b53c7..5fae78006755 100644
--- a/code/datums/components/bakeable.dm
+++ b/code/datums/components/bakeable.dm
@@ -92,10 +92,10 @@
used_tray.AddToPlate(baked_result)
if(positive_result)
- new /obj/effect/abstract/smell/oven/good(used_oven.loc)
+ new /obj/effect/abstract/smell/oven/good(used_oven)
BLACKBOX_LOG_FOOD_MADE(baked_result.type)
else
- new /obj/effect/abstract/smell/oven/bad(used_oven.loc)
+ new /obj/effect/abstract/smell/oven/bad(used_oven)
SEND_SIGNAL(parent, COMSIG_ITEM_BAKED, baked_result)
qdel(parent)
diff --git a/code/datums/elements/decals/blood.dm b/code/datums/elements/decals/blood.dm
index c0e6e394ec67..e43e8df8a715 100644
--- a/code/datums/elements/decals/blood.dm
+++ b/code/datums/elements/decals/blood.dm
@@ -15,9 +15,10 @@
. = ..()
RegisterSignal(as_item, COMSIG_ATOM_GET_EXAMINE_NAME, PROC_REF(get_examine_name), TRUE)
RegisterSignal(as_item, COMSIG_ATOM_COLOR_UPDATED, PROC_REF(on_color_update), TRUE)
+ RegisterSignal(as_item, COMSIG_ORGAN_SURGICALLY_REMOVED, PROC_REF(Detach), TRUE)
/datum/element/decal/blood/Detach(atom/source)
- UnregisterSignal(source, list(COMSIG_ATOM_GET_EXAMINE_NAME, COMSIG_ATOM_COLOR_UPDATED))
+ UnregisterSignal(source, list(COMSIG_ATOM_GET_EXAMINE_NAME, COMSIG_ATOM_COLOR_UPDATED, COMSIG_ORGAN_SURGICALLY_REMOVED))
if (isitem(source))
var/obj/item/source_item = source
REMOVE_KEEP_TOGETHER(source_item, type)
diff --git a/code/datums/wounds/scars/_scars.dm b/code/datums/wounds/scars/_scars.dm
index 050f5db42082..d7aaa27fd069 100644
--- a/code/datums/wounds/scars/_scars.dm
+++ b/code/datums/wounds/scars/_scars.dm
@@ -164,7 +164,7 @@
if(WOUND_SEVERITY_LOSS)
msg = "[victim.p_Their()] [limb.plaintext_zone] [description]." // different format
msg = span_notice("[msg]")
- return "\t[msg]"
+ return msg
/// Whether a scar can currently be seen by the viewer
/datum/scar/proc/is_visible(mob/viewer)
diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm
index 2d07e3ab81de..ff6e6629bcd9 100644
--- a/code/game/objects/effects/decals/cleanable/humans.dm
+++ b/code/game/objects/effects/decals/cleanable/humans.dm
@@ -170,16 +170,16 @@
/// Add the passed smell with the passed category to the blood
/// Can optionally pass a multiplier which affects both intensity and radius
-/obj/effect/decal/cleanable/blood/proc/add_smell(smell, category, multiplier = 1)
- var/intensity = max(floor(bloodiness * 0.2 * multiplier), SMELL_INTENSITY_FAINT)
- var/radius = round(bloodiness * 0.04 * multiplier)
+/obj/effect/decal/cleanable/blood/proc/add_tracked_smell(smell, category, multiplier = 1)
+ var/effective_bloodiness = min(bloodiness, BLOOD_AMOUNT_PER_DECAL * 2)
+ var/intensity = max(floor(effective_bloodiness * 0.1 * multiplier), SMELL_INTENSITY_FAINT)
+ var/radius = round(effective_bloodiness * 0.02 * multiplier)
AddElement(/datum/element/simple_smell, \
- smell = smell, \
+ smell_basetype = /datum/smell/blood, \
category = category, \
+ smell = smell, \
intensity = intensity, \
radius = radius, \
- id = "dna", \
- smell_basetype = /datum/smell/blood, \
)
LAZYADD(smell_elements_present, list(list(
"smell" = smell,
@@ -198,18 +198,17 @@
unique_smells["[blood.scent_text]-[blood.scent_category]"] += 1
for(var/blood_smell, count in unique_smells)
var/resplit_smell = splittext(blood_smell, "-")
- add_smell(text2path(resplit_smell[1]) || resplit_smell[1], resplit_smell[2], count / GET_ATOM_BLOOD_DNA_LENGTH(src))
+ add_tracked_smell(text2path(resplit_smell[1]) || resplit_smell[1], resplit_smell[2], count / GET_ATOM_BLOOD_DNA_LENGTH(src))
last_bloodiness_refresh = bloodiness
/obj/effect/decal/cleanable/blood/proc/clear_smells()
for(var/list/smell_element as anything in smell_elements_present)
RemoveElement(/datum/element/simple_smell, \
- smell = smell_element["smell"], \
+ smell_basetype = /datum/smell/blood, \
category = smell_element["category"], \
+ smell = smell_element["smell"], \
intensity = smell_element["intensity"], \
radius = smell_element["radius"], \
- id = "dna", \
- smell_basetype = /datum/smell/blood, \
)
LAZYREMOVE(smell_elements_present, list(smell_element))
@@ -374,7 +373,7 @@
very_bloody = TRUE
icon_state = pick("trails_1", "trails_2")
-/obj/effect/decal/cleanable/blood/trail/add_smell(smell, category, multiplier)
+/obj/effect/decal/cleanable/blood/trail/add_tracked_smell(smell, category, multiplier)
if(!isturf(loc))
return // fake
return ..()
@@ -499,7 +498,7 @@
. = ..()
setDir(pick(GLOB.cardinals))
AddElement(/datum/element/swabable, CELL_LINE_TABLE_SLUDGE, CELL_VIRUS_TABLE_GENERIC, rand(2,4), 10)
- AddElement(/datum/element/simple_smell, /datum/smell/decay, SMELL_INTENSITY_STRONG, 1)
+ add_smell(smell = /datum/smell/decay, intensity = SMELL_INTENSITY_STRONG, radius = 1)
/obj/effect/decal/cleanable/blood/drip
name = "drop of blood"
@@ -511,7 +510,7 @@
dry_desc = "A dried spattering."
drying_time = 1 MINUTES
-/obj/effect/decal/cleanable/blood/drip/add_smell(smell, category, multiplier)
+/obj/effect/decal/cleanable/blood/drip/add_tracked_smell(smell, category, multiplier)
if(!isturf(loc))
return // fake
return ..()
@@ -658,6 +657,9 @@
if(splatter_strength)
src.splatter_strength = splatter_strength
+/obj/effect/decal/cleanable/blood/hitsplatter/add_tracked_smell(smell, category, multiplier)
+ return // ephemeral
+
/obj/effect/decal/cleanable/blood/hitsplatter/proc/expire()
if(isturf(loc) && !skip)
playsound(src, 'sound/effects/wounds/splatter.ogg', 60, TRUE, -1)
diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm
index b30e5dd51c39..5781ffde3fa6 100644
--- a/code/game/objects/effects/decals/cleanable/misc.dm
+++ b/code/game/objects/effects/decals/cleanable/misc.dm
@@ -167,7 +167,7 @@
leave_smell()
/obj/effect/decal/cleanable/vomit/proc/leave_smell()
- AddElement(/datum/element/simple_smell, "vomit", SMELL_INTENSITY_STRONG, 1)
+ add_smell(smell = "vomit", intensity = SMELL_INTENSITY_STRONG, radius = 1)
/obj/effect/decal/cleanable/vomit/attack_hand(mob/user, list/modifiers)
. = ..()
@@ -336,7 +336,7 @@
/obj/effect/decal/cleanable/garbage/Initialize(mapload)
. = ..()
AddElement(/datum/element/swabable, CELL_LINE_TABLE_SLUDGE, CELL_VIRUS_TABLE_GENERIC, rand(2,4), 15)
- AddElement(/datum/element/simple_smell, "rotting garbage", SMELL_INTENSITY_MODERATE, 2, "stench")
+ add_smell(category = "stench", smell = "rotting garbage", intensity = SMELL_INTENSITY_MODERATE, radius = 2)
/obj/effect/decal/cleanable/ants
name = "space ants"
@@ -454,9 +454,9 @@
return INITIALIZE_HINT_QDEL
if(burn_stacks)
- burn_amount = max(min(burn_stacks, 10), 1)
-
- AddElement(/datum/element/simple_smell, "fuel", floor(SMELL_INTENSITY_MODERATE * burn_amount * 0.1), 1, "stench")
+ burn_amount = clamp(burn_stacks, 1, 10)
+ if(burn_amount > 1)
+ add_smell(category = "stench", smell = "fuel", intensity = floor(SMELL_INTENSITY_MODERATE * burn_amount * 0.1), radius = 1)
/obj/effect/decal/cleanable/fuel_pool/fire_act(exposed_temperature, exposed_volume)
. = ..()
diff --git a/code/game/objects/effects/decals/cleanable/robots.dm b/code/game/objects/effects/decals/cleanable/robots.dm
index 0d4b333aa5f0..4d9553af41eb 100644
--- a/code/game/objects/effects/decals/cleanable/robots.dm
+++ b/code/game/objects/effects/decals/cleanable/robots.dm
@@ -86,9 +86,10 @@
/obj/effect/decal/cleanable/oil/Initialize(mapload, list/datum/disease/diseases)
. = ..()
- AddElement(/datum/element/easy_ignite) // NON-MODULE CHANGE
- AddElement(/datum/element/simple_smell, /datum/smell/oil, SMELL_INTENSITY_STRONG, 2) // NON-MODULE CHANGE
- add_blood_DNA(list("CRUDE OIL" = /datum/blood_type/oil)) // NON-MODULE CHANGE : For bloody shoes // Yeah don't think about it too much
+ // NON-MODULE CHANGE
+ AddElement(/datum/element/easy_ignite)
+ add_smell(smell = /datum/smell/oil, intensity = SMELL_INTENSITY_STRONG, radius = 2)
+ add_blood_DNA(list("CRUDE OIL" = /datum/blood_type/oil))
/obj/effect/decal/cleanable/oil/streak
icon_state = "streak1"
diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm
index 461e3321068f..b19258a64da3 100644
--- a/code/game/objects/items/cigs_lighters.dm
+++ b/code/game/objects/items/cigs_lighters.dm
@@ -467,7 +467,9 @@ CIGARETTE PACKETS ARE IN FANCY.DM
/obj/item/cigarette/proc/handle_reagents(mob/living/carbon/smoker, seconds_per_tick)
reagents.expose_temperature(heat, 0.05)
if(reagents.has_reagent(/datum/reagent/drug/nicotine, 1, check_subtypes = TRUE))
- new /obj/effect/abstract/smell/cigarette_smoke(get_turf(smoker || src))
+ new /obj/effect/abstract/smell/cigarette_smoke/lingering(get_turf(src))
+ new /obj/effect/abstract/smell/cigarette_smoke(src)
+ new /obj/effect/abstract/smell/cigarette_smoke(smoker)
if(reagents.total_volume <= 0) //may have reacted and gone to 0 after expose_temperature
return
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index a9e7e5cd76eb..a044cea87796 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -286,13 +286,7 @@
/datum/component/bloody_spreader,\
blood_dna = list("UNKNOWN DNA" = /datum/blood_type/animal),\
)
- AddComponent(
- /datum/component/complex_smell, \
- duration = INFINITY, \
- smell = "meat", \
- intensity = SMELL_INTENSITY_STRONG, \
- radius = 2, \
- )
+ add_smell(smell = "meat", intensity = SMELL_INTENSITY_STRONG, radius = 2)
atom_storage.storage_sound = 'sound/effects/blobattack.ogg'
/*
diff --git a/code/game/objects/structures/bonfire.dm b/code/game/objects/structures/bonfire.dm
index e9a5a82dbb06..cdaa5f2986d3 100644
--- a/code/game/objects/structures/bonfire.dm
+++ b/code/game/objects/structures/bonfire.dm
@@ -115,7 +115,11 @@
bonfire_burn()
particles = new /particles/bonfire()
START_PROCESSING(SSobj, src)
- AddElement(/datum/element/simple_smell, "smoke", SMELL_INTENSITY_STRONG, 5)
+ AddElement(/datum/element/simple_smell, \
+ smell = "smoke", \
+ intensity = SMELL_INTENSITY_STRONG, \
+ radius = 5, \
+ )
/obj/structure/bonfire/fire_act(exposed_temperature, exposed_volume)
start_burning()
@@ -174,7 +178,11 @@
set_light(0)
QDEL_NULL(particles)
STOP_PROCESSING(SSobj, src)
- RemoveElement(/datum/element/simple_smell, "smoke", SMELL_INTENSITY_STRONG, 5)
+ RemoveElement(/datum/element/simple_smell, \
+ smell = "smoke", \
+ intensity = SMELL_INTENSITY_STRONG, \
+ radius = 5, \
+ )
/obj/structure/bonfire/buckle_mob(mob/living/buckled_mob, force = FALSE, check_loc = TRUE)
if(..())
diff --git a/code/game/objects/structures/headpike.dm b/code/game/objects/structures/headpike.dm
index eab0b7fd6277..d7f488bb8cf3 100644
--- a/code/game/objects/structures/headpike.dm
+++ b/code/game/objects/structures/headpike.dm
@@ -22,7 +22,7 @@
if(mapload)
CheckParts()
pixel_x = rand(-8, 8)
- AddElement(/datum/element/simple_smell, /datum/smell/decay, SMELL_INTENSITY_MODERATE, 2)
+ add_smell(smell = /datum/smell/decay, intensity = SMELL_INTENSITY_MODERATE, radius = 2)
/obj/structure/headpike/Destroy()
QDEL_NULL(victim)
diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm
index 8571774d5bbb..c489cf8384f0 100644
--- a/code/modules/fishing/fish/_fish.dm
+++ b/code/modules/fishing/fish/_fish.dm
@@ -184,7 +184,7 @@
register_context()
register_item_context()
- AddElement(/datum/element/simple_smell, "fish", SMELL_INTENSITY_WEAK, 2)
+ add_smell(smell = "fish", intensity = SMELL_INTENSITY_WEAK, radius = 2)
/obj/item/fish/add_item_context(atom/source, list/context, obj/item/held_item, mob/user)
if(HAS_TRAIT(source, TRAIT_CATCH_AND_RELEASE))
diff --git a/code/modules/food_and_drinks/machinery/deep_fryer.dm b/code/modules/food_and_drinks/machinery/deep_fryer.dm
index a359185ff195..eca04daaf012 100644
--- a/code/modules/food_and_drinks/machinery/deep_fryer.dm
+++ b/code/modules/food_and_drinks/machinery/deep_fryer.dm
@@ -148,7 +148,7 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
audible_message(span_notice("[src] dings!"))
else if (cook_time >= DEEPFRYER_BURNTIME && !frying_burnt)
frying_burnt = TRUE
- new /obj/effect/abstract/smell/oven/bad/fryer(loc)
+ new /obj/effect/abstract/smell/oven/bad/fryer(src)
use_energy(active_power_usage)
diff --git a/code/modules/food_and_drinks/machinery/oven.dm b/code/modules/food_and_drinks/machinery/oven.dm
index d2df58a0921c..8b11d79c8610 100644
--- a/code/modules/food_and_drinks/machinery/oven.dm
+++ b/code/modules/food_and_drinks/machinery/oven.dm
@@ -90,7 +90,7 @@
baked_item.fire_act(1000) //Hot hot hot!
if(SPT_PROB(10, seconds_per_tick))
- new /obj/effect/abstract/smell/oven/bad(loc)
+ new /obj/effect/abstract/smell/oven/bad(src)
set_smoke_state(worst_cooked_food_state)
update_appearance()
diff --git a/code/modules/food_and_drinks/recipes/soup_mixtures.dm b/code/modules/food_and_drinks/recipes/soup_mixtures.dm
index 1bbf7a98b41d..b0d1de09398f 100644
--- a/code/modules/food_and_drinks/recipes/soup_mixtures.dm
+++ b/code/modules/food_and_drinks/recipes/soup_mixtures.dm
@@ -222,7 +222,7 @@
testing("Soup reaction finished with a total react volume of [react_vol] and [length(pot.added_ingredients)] ingredients. Cleaning up.")
clean_up(holder, reaction, react_vol)
- new /obj/effect/abstract/smell/oven/good(get_turf(pot))
+ new /obj/effect/abstract/smell/oven/good(pot)
/**
* Cleans up the ingredients and adds whatever leftover reagents to the mixture
diff --git a/code/modules/forensics/_forensics.dm b/code/modules/forensics/_forensics.dm
index 77ae7c2b3861..f51f07e4dcfe 100644
--- a/code/modules/forensics/_forensics.dm
+++ b/code/modules/forensics/_forensics.dm
@@ -231,7 +231,7 @@
/// Updates the blood displayed on parent
/datum/forensics/proc/check_blood()
- if(!isitem(parent) || isorgan(parent)) // organs don't spawn with blood decals by default
+ if(!isitem(parent))
return
if(!length(blood_DNA))
return
diff --git a/code/modules/forensics/forensics_helpers.dm b/code/modules/forensics/forensics_helpers.dm
index 03d607ac732f..8e691d9be770 100644
--- a/code/modules/forensics/forensics_helpers.dm
+++ b/code/modules/forensics/forensics_helpers.dm
@@ -101,6 +101,12 @@
var/blood_type_to_use = all_dna[all_dna[1]]
return find_blood_type(blood_type_to_use).color
+/obj/item/organ/get_blood_dna_color()
+ if(isnull(blood_dna_info))
+ return COLOR_BLOOD
+ var/blood_type_to_use = blood_dna_info[blood_dna_info[1]]
+ return find_blood_type(blood_type_to_use).color
+
/// Adds blood dna to the atom
/atom/proc/add_blood_DNA(list/blood_DNA_to_add) //ASSOC LIST DNA = BLOODTYPE
return FALSE
@@ -144,14 +150,13 @@
for(var/some_dna, blood_type in blood_DNA_to_add)
var/datum/blood_type/blood = find_blood_type(blood_type)
if(blood.scent_text)
- AddComponent( \
- /datum/component/complex_smell, \
- duration = duration, \
- smell = blood.scent_text, \
- category = blood.scent_category, \
- smell_basetype = /datum/smell/blood, \
- intensity = intensity, \
- radius = radius, \
+ add_smell(
+ duration = duration,
+ base_type = /datum/smell/blood,
+ smell = blood.scent_text,
+ category = blood.scent_category,
+ intensity = intensity,
+ radius = radius,
)
/obj/item/add_blood_DNA(list/blood_DNA_to_add)
diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm
index e9fa214aaf85..0ac176247e49 100644
--- a/code/modules/hydroponics/grown/flowers.dm
+++ b/code/modules/hydroponics/grown/flowers.dm
@@ -31,10 +31,17 @@
distill_reagent = /datum/reagent/consumable/ethanol/vermouth
drop_sound = 'maplestation_modules/sound/items/drop/herb.ogg'
pickup_sound = 'maplestation_modules/sound/items/pickup/herb.ogg'
+ juice_typepath = /datum/reagent/medicine/painkiller/oxycodone
/obj/item/food/grown/poppy/Initialize(mapload, obj/item/seeds/new_seed)
. = ..()
- AddElement(/datum/element/simple_smell, "flowers", SMELL_INTENSITY_WEAK, 1, "fragrance")
+ add_smell(category = "fragrance", smell = "flowers", intensity = SMELL_INTENSITY_WEAK, radius = 1)
+
+/obj/item/food/grown/poppy/grind(datum/reagents/target_holder, mob/user)
+ . = ..()
+ if(!.)
+ return
+ target_holder.add_reagent(/datum/reagent/perfume, round(seed.potency * pick(0.01, 0.02, 0.03), CHEMICAL_VOLUME_ROUNDING))
// Lily
/obj/item/seeds/poppy/lily
@@ -55,6 +62,7 @@
name = "lily"
desc = "A beautiful white flower."
icon_state = "lily"
+ juice_typepath = null
//Spacemans's Trumpet
/obj/item/seeds/poppy/lily/trumpet
@@ -111,6 +119,7 @@
name = "geranium"
desc = "A beautiful blue flower."
icon_state = "geranium"
+ juice_typepath = null
///Fraxinella seeds.
/obj/item/seeds/poppy/geranium/fraxinella
@@ -315,7 +324,7 @@
/obj/item/food/grown/rose/Initialize(mapload, obj/item/seeds/new_seed)
. = ..()
- AddElement(/datum/element/simple_smell, "roses", SMELL_INTENSITY_WEAK, 1, "fragrance")
+ add_smell(category = "fragrance", smell = "roses", intensity = SMELL_INTENSITY_WEAK, radius = 1)
/obj/item/food/grown/rose/equipped(mob/user, slot, initial)
. = ..()
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index c5434d69a50a..8325a0fe389d 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -1148,7 +1148,7 @@
/obj/machinery/hydroponics/soil/Initialize(mapload)
. = ..()
- AddElement(/datum/element/simple_smell, "fresh soil", SMELL_INTENSITY_WEAK, 1)
+ add_smell(smell = "fresh soil", intensity = SMELL_INTENSITY_WEAK, radius = 1)
/obj/machinery/hydroponics/soil/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/screwdriver)
return NONE
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index 60258d1d18fb..ec222d53cdd0 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -624,7 +624,7 @@
age_text = "very old"
if(101 to INFINITY)
age_text = "withering away"
- . += list(span_notice("[p_They()] appear[p_s()] to be [age_text]."))
+ . += list(span_info("[p_They()] appear[p_s()] to be [age_text]."))
/// Reports the height difference between src and user
/mob/living/carbon/proc/get_height_difference(mob/user)
@@ -693,7 +693,7 @@
if(6 to INFINITY)
. += " [p_Theyre()] also significantly taller than a typical [dna.species]."
- return span_notice(.)
+ return span_info(.)
/// Returns the mob height modified by traits purely
/mob/living/carbon/human/proc/get_visual_height()
@@ -735,7 +735,7 @@
if(!.)
return
- return span_notice(.)
+ return span_info(.)
/mob/living/carbon/human/proc/get_visual_strength()
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index d849ad4064c3..4837fe123fd7 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -28,6 +28,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
ORGAN_SLOT_EYES, ORGAN_SLOT_EARS, ORGAN_SLOT_TONGUE, ORGAN_SLOT_LIVER, ORGAN_SLOT_STOMACH))
var/obj/item/organ/current_organ = get_organ_slot(slot) //Time to cache it lads
if(current_organ)
+ current_organ.blood_dna_info = null // ensure no DNA exists
current_organ.Remove(src, special = TRUE) //Please don't somehow kill our dummy
SSwardrobe.stash_object(current_organ)
@@ -35,6 +36,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
for(var/organ_path in current_species.mutant_organs)
var/obj/item/organ/current_organ = get_organ_by_type(organ_path)
if(current_organ)
+ current_organ.blood_dna_info = null // ensure no DNA exists
current_organ.Remove(src, special = TRUE) //Please don't somehow kill our dummy
SSwardrobe.stash_object(current_organ)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index f8dd9b3e6e7a..5cffb13fb200 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -530,8 +530,10 @@
var/examine_time = client.recent_examines[ref_to_atom]
if(examine_time && (world.time - examine_time < EXAMINE_MORE_WINDOW))
var/list/result = examinify.examine_more(src)
- if(!length(result))
- result += span_notice("You examine [examinify] closer, but find nothing of interest...")
+ if(length(result))
+ result.Insert(1, span_info("You examine [examinify] closer."))
+ else
+ result += span_info("You examine [examinify] closer, but find nothing of note.")
result_combined = examine_block(jointext(result, "
"))
closer_look = TRUE
diff --git a/code/modules/projectiles/ammunition/_ammunition.dm b/code/modules/projectiles/ammunition/_ammunition.dm
index 97fa0864631b..49bbcd140756 100644
--- a/code/modules/projectiles/ammunition/_ammunition.dm
+++ b/code/modules/projectiles/ammunition/_ammunition.dm
@@ -149,12 +149,12 @@
/obj/item/ammo_casing/proc/is_spent(mapload = FALSE)
if(!mapload)
- AddComponent(/datum/component/complex_smell, \
- duration = 4 MINUTES, \
- smell = "gunpowder", \
- intensity = SMELL_INTENSITY_FAINT, \
- radius = 1, \
- wash_types = CLEAN_TYPE_FINGERPRINTS, \
+ add_smell(
+ duration = 4 MINUTES,
+ smell = "gunpowder",
+ intensity = SMELL_INTENSITY_FAINT,
+ radius = 1,
+ wash_type = CLEAN_TYPE_FINGERPRINTS,
)
name = "spent [name]"
diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm
index f823a21ade92..826b88010ddb 100644
--- a/code/modules/projectiles/guns/ballistic.dm
+++ b/code/modules/projectiles/guns/ballistic.dm
@@ -256,12 +256,12 @@
/obj/item/gun/ballistic/handle_chamber(empty_chamber = TRUE, from_firing = TRUE, chamber_next_round = TRUE)
if(!semi_auto && from_firing)
return
- AddComponent(/datum/component/complex_smell, \
- duration = 30 SECONDS, \
- smell = "gunpowder", \
- intensity = SMELL_INTENSITY_WEAK, \
- radius = 2, \
- wash_types = CLEAN_TYPE_FINGERPRINTS, \
+ add_smell(
+ duration = 30 SECONDS,
+ smell = "gunpowder",
+ intensity = SMELL_INTENSITY_WEAK,
+ radius = 2,
+ wash_type = CLEAN_TYPE_FINGERPRINTS,
)
var/obj/item/ammo_casing/casing = chambered //Find chambered round
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index c2d6ddedbb87..23d42d99af38 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -210,9 +210,12 @@
if(chambered && !chambered.loaded_projectile) //if loaded_projectile is null, i.e the shot has been fired...
var/obj/item/ammo_casing/energy/shot = chambered
cell.use(shot.e_cost)//... drain the cell cell
+ if(chambered.projectile_type::damage > 0 || chambered.projectile_type::stamina > 0 || chambered.projectile_type::pain > 0)
+ new /obj/effect/abstract/smell/ozone(src)
+ new /obj/effect/abstract/smell/ozone/lingering(get_turf(src))
+
chambered = null //either way, released the prepared shot
recharge_newshot() //try to charge a new shot
- new /obj/effect/abstract/smell/ozone(get_turf(src))
/obj/item/gun/energy/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
if(!chambered && can_shoot())
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 921c2348fab1..dda4b65aae2d 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -1364,17 +1364,6 @@
/datum/reagent/space_cleaner/expose_obj(obj/exposed_obj, reac_volume)
. = ..()
exposed_obj.wash(clean_types)
- if(QDELETED(exposed_obj))
- return
- exposed_obj.AddComponent( \
- /datum/component/complex_smell, \
- duration = smell_type::duration, \
- smell = smell_type::smell, \
- intensity = smell_type::intensity * (reac_volume / 5), \
- radius = 1, \
- category = smell_type::category, \
- fade_intensity_over_time = TRUE, \
- )
/datum/reagent/space_cleaner/expose_turf(turf/exposed_turf, reac_volume)
. = ..()
@@ -1399,15 +1388,7 @@
exposed_mob.wash(clean_types)
if(QDELETED(exposed_mob))
return
- exposed_mob.AddComponent( \
- /datum/component/complex_smell, \
- duration = smell_type::duration, \
- smell = smell_type::smell, \
- intensity = smell_type::intensity * (reac_volume / 5), \
- radius = 1, \
- category = smell_type::category, \
- fade_intensity_over_time = TRUE, \
- )
+ new smell_type(exposed_mob, reac_volume)
/datum/reagent/space_cleaner/on_burn_wound_processing(datum/wound/flesh/burn_wound)
burn_wound.sanitization += 0.3
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index 50af7f9a891e..3ad6133da28d 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -413,7 +413,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/reagent_dispensers/wall/virusfood, 30
/obj/structure/reagent_dispensers/servingdish/Initialize(mapload)
. = ..()
- AddElement(/datum/element/simple_smell, "slop", SMELL_INTENSITY_MODERATE, 1, "stench")
+ add_smell(category = "stench", smell = "slop", intensity = SMELL_INTENSITY_MODERATE, radius = 1)
/obj/structure/reagent_dispensers/plumbed
name = "stationary water tank"
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 6b4350c452f2..d1ac1895c8c1 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -662,7 +662,7 @@
check_wounding(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus, attack_direction, damage_source, sharpness)
for(var/datum/wound/iter_wound as anything in wounds)
- iter_wound.receive_damage(wounding_type, wounding_dmg, wound_bonus, damage_source)
+ iter_wound.receive_damage(wounding_type, wounding_dmg, wound_bonus, attack_direction, damage_source)
// END WOUND HANDLING
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 40e4c41e5566..9201f8f4cdba 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -204,7 +204,7 @@
if(!IS_ORGANIC_LIMB(src))
head_flags &= ~HEAD_SHOW_ORGANS_ON_EXAMINE
// specifically to facilitate finding decapitated heads
- AddElement(/datum/element/simple_smell, /datum/smell/decay, SMELL_INTENSITY_MODERATE, 2)
+ add_smell(smell = /datum/smell/decay, intensity = SMELL_INTENSITY_MODERATE, radius = 2)
/obj/item/bodypart/head/get_voice(add_id_name)
return "The head of [real_name]"
diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm
index 5122f5156521..ec00e4ba7b6a 100644
--- a/code/modules/surgery/organs/_organ.dm
+++ b/code/modules/surgery/organs/_organ.dm
@@ -8,7 +8,7 @@
/// 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" = /datum/blood_type/crew/human/o_minus) // not every organ spawns inside a person
+ var/list/blood_dna_info
/// The body zone this organ is supposed to inhabit.
var/zone = BODY_ZONE_CHEST
/**
@@ -89,6 +89,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
if(bodypart_overlay)
setup_bodypart_overlay()
+ RegisterSignal(src, COMSIG_DETECTIVE_SCANNED, PROC_REF(pass_blood_dna_info))
/obj/item/organ/Destroy()
if(isnull(owner) && !isnull(bodypart_owner))
@@ -135,13 +136,6 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
/obj/item/organ/proc/on_find(mob/living/finder)
return
-/obj/item/organ/wash(clean_types)
- . = ..()
-
- // always add the original dna to the organ after it's washed
- if(!IS_ROBOTIC_ORGAN(src) && (clean_types & CLEAN_TYPE_BLOOD))
- add_blood_DNA(blood_dna_info)
-
/obj/item/organ/process(seconds_per_tick, times_fired)
return
@@ -425,6 +419,23 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
to_chat(feeder, span_warning("The only thing you could think of doing with [source] right now is feeding it to [eater], but that doesn't seem right."))
return BLOCK_EAT_ATTEMPT
+/// Signal proc for [COMSIG_DETECTIVE_SCANNED], show innate blood info in the data
+/obj/item/organ/proc/pass_blood_dna_info(datum/source, mob/user, list/det_data)
+ SIGNAL_HANDLER
+ if(isnull(blood_dna_info))
+ if(IS_ORGANIC_ORGAN(src))
+ // if there is no dna, but it is organic, it must be mapspawned or something, so show dummy data
+ LAZYADD(det_data[DETSCAN_CATEGORY_BLOOD], \
+ "Type: [find_blood_type(/datum/blood_type/crew/human/o_minus)])] DNA (UE): Synthetic DNA")
+ else
+ // a robotic organ not owned by a robotic mob should show no DNA
+ LAZYADD(det_data[DETSCAN_CATEGORY_BLOOD], \
+ "Type: N/A DNA (UE): N/A")
+ else
+ // show the dna of the mob that owned the organ in the past to the scanner
+ LAZYADD(det_data[DETSCAN_CATEGORY_BLOOD], \
+ "Type: [find_blood_type(blood_dna_info[blood_dna_info[1]])] DNA (UE): [blood_dna_info[1]]")
+
/// Get all possible organ slots by checking every organ, and then store it and give it whenever needed
/proc/get_all_slots()
var/static/list/all_organ_slots = list()
diff --git a/code/modules/surgery/organs/organ_movement.dm b/code/modules/surgery/organs/organ_movement.dm
index 62fbac434973..697448445fce 100644
--- a/code/modules/surgery/organs/organ_movement.dm
+++ b/code/modules/surgery/organs/organ_movement.dm
@@ -78,11 +78,9 @@
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)
+ if(organ_flags & ORGAN_VIRGIN)
+ if(IS_ORGANIC_ORGAN(src) || isandroid(receiver))
+ blood_dna_info = receiver.get_blood_dna_list()
organ_flags &= ~ORGAN_VIRGIN
if(external_bodytypes)
@@ -242,9 +240,9 @@
/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))
- add_blood_DNA(blood_dna_info)
+ if(!QDELING(src) && !isnull(blood_dna_info))
AddElement(/datum/element/decal/blood)
+ add_blood_scent(blood_dna_info, 5 MINUTES, SMELL_INTENSITY_MODERATE)
item_flags &= ~ABSTRACT
REMOVE_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
@@ -288,7 +286,6 @@
/obj/item/organ/proc/on_surgical_removal(mob/living/user, obj/item/bodypart/limb, obj/item/tool)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ORGAN_SURGICALLY_REMOVED, user, limb.owner, limb.body_zone, tool)
- RemoveElement(/datum/element/decal/blood)
/**
* Proc that gets called when the organ is surgically inserted by someone. Seem familiar?
diff --git a/maplestation.dme b/maplestation.dme
index b1cd772c7226..bbe6a3c62752 100644
--- a/maplestation.dme
+++ b/maplestation.dme
@@ -6361,6 +6361,7 @@
#include "maplestation_modules\code\game\objects\items\cybernetics_paintkit.dm"
#include "maplestation_modules\code\game\objects\items\holy_weapons.dm"
#include "maplestation_modules\code\game\objects\items\locker_spawners.dm"
+#include "maplestation_modules\code\game\objects\items\perfume.dm"
#include "maplestation_modules\code\game\objects\items\plushes.dm"
#include "maplestation_modules\code\game\objects\items\robot_upgrades.dm"
#include "maplestation_modules\code\game\objects\items\trash.dm"
@@ -6730,9 +6731,9 @@
#include "maplestation_modules\code\modules\research\xenobiology\potions.dm"
#include "maplestation_modules\code\modules\robotic_limb_detach\robot_limb_detach_quirk.dm"
#include "maplestation_modules\code\modules\smells\_smell.dm"
-#include "maplestation_modules\code\modules\smells\living_procs.dm"
#include "maplestation_modules\code\modules\smells\smell_component.dm"
#include "maplestation_modules\code\modules\smells\smell_element.dm"
+#include "maplestation_modules\code\modules\smells\smell_helpers.dm"
#include "maplestation_modules\code\modules\smells\smell_holder.dm"
#include "maplestation_modules\code\modules\smells\smell_mood.dm"
#include "maplestation_modules\code\modules\surgery\bodyparts\cyber_arms.dm"
diff --git a/maplestation_modules/code/datums/elements/unique_examine.dm b/maplestation_modules/code/datums/elements/unique_examine.dm
index 615543e7a206..9d9a156d7f71 100644
--- a/maplestation_modules/code/datums/elements/unique_examine.dm
+++ b/maplestation_modules/code/datums/elements/unique_examine.dm
@@ -248,7 +248,7 @@
break
//Department checks
- if(examiner_mind.assigned_role.departments_bitflags & special_desc_req[1])
+ if(isnum(special_desc_req[1]) && (examiner_mind.assigned_role.departments_bitflags & special_desc_req[1]))
fulfilled_requirements[EXAMINE_CHECK_DEPARTMENT] = TRUE
//Standard faction checks
diff --git a/maplestation_modules/code/datums/pain/pain_reagents/painkiller_reactions.dm b/maplestation_modules/code/datums/pain/pain_reagents/painkiller_reactions.dm
index 6a9dc0c5eb61..af2f2e676295 100644
--- a/maplestation_modules/code/datums/pain/pain_reagents/painkiller_reactions.dm
+++ b/maplestation_modules/code/datums/pain/pain_reagents/painkiller_reactions.dm
@@ -52,17 +52,6 @@
required_catalysts = list(/datum/reagent/medicine/c2/aiuri = 1)
reaction_tags = REACTION_TAG_MODERATE | REACTION_TAG_HEALING | REACTION_TAG_OTHER | REACTION_TAG_DRUG | REACTION_TAG_PAIN
-// Not really reactions, but I'm leaving these here
-// Gain oxycodone from juicing poppies
-/obj/item/food/grown/poppy
- juice_typepath = /datum/reagent/medicine/painkiller/oxycodone
-
-/obj/item/food/grown/poppy/geranium
- juice_typepath = null
-
-/obj/item/food/grown/poppy/lily
- juice_typepath = null
-
/datum/chemical_reaction/medicine/dimenhydrinate
results = list(/datum/reagent/medicine/dimenhydrinate = 3)
required_reagents = list(/datum/reagent/medicine/diphenhydramine = 1, /datum/reagent/nitrogen = 1, /datum/reagent/chlorine = 1)
diff --git a/maplestation_modules/code/game/objects/items/ashtray.dm b/maplestation_modules/code/game/objects/items/ashtray.dm
index 36c5ea30c946..52ab1e3bce88 100644
--- a/maplestation_modules/code/game/objects/items/ashtray.dm
+++ b/maplestation_modules/code/game/objects/items/ashtray.dm
@@ -81,16 +81,12 @@
return .
/obj/item/ashtray/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking)
- . = ..()
- if(. & ITEM_INTERACT_BLOCKING)
- return .
-
if(!istype(tool, /obj/item/cigarette) \
&& !istype(tool, /obj/item/cigbutt) \
&& !istype(tool, /obj/item/food/candy_trash) \
&& !istype(tool, /obj/item/match) \
)
- return .
+ return NONE
if(length(contents) > 24)
balloon_alert(user, "it's full!")
@@ -116,6 +112,7 @@
cig.transfer_fingerprints_to(butt)
cig.transfer_fibers_to(butt)
qdel(cig)
+ new /obj/effect/abstract/smell/cigarette_smoke/lingering/longer(src)
else
user.visible_message(
diff --git a/maplestation_modules/code/game/objects/items/perfume.dm b/maplestation_modules/code/game/objects/items/perfume.dm
new file mode 100644
index 000000000000..09cba866911b
--- /dev/null
+++ b/maplestation_modules/code/game/objects/items/perfume.dm
@@ -0,0 +1,38 @@
+/obj/item/reagent_containers/spray/perfume
+ name = "perfume bottle"
+ slot_flags = NONE
+ w_class = WEIGHT_CLASS_TINY
+
+ can_toggle_range = FALSE
+ current_range = 1
+ spray_range = 1
+ volume = 10
+ amount_per_transfer_from_this = 2
+ possible_transfer_amounts = list(1, 2)
+ can_fill_from_container = FALSE
+
+/obj/item/reagent_containers/spray/perfume/Initialize(mapload, vol)
+ . = ..()
+ reagents.add_reagent(/datum/reagent/perfume, volume)
+ transform = transform.Scale(0.5, 0.5)
+
+/datum/reagent/perfume
+ name = "Perfume"
+ description = "A sweet-smelling liquid that is used to make things smell nicer."
+ reagent_state = LIQUID
+ color = "#bb8bbb"
+ taste_description = "perfume"
+ ph = 6.5
+ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
+ /// What type of smell to produce when exposed
+ var/obj/effect/abstract/smell/smell_type = /obj/effect/abstract/smell/reagent/perfume
+
+/datum/reagent/perfume/expose_turf(turf/exposed_turf, reac_volume)
+ . = ..()
+ new smell_type(exposed_turf, reac_volume)
+
+/datum/reagent/perfume/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message=TRUE, touch_protection=0)
+ . = ..()
+ if(!(methods & (TOUCH|VAPOR)) || QDELETED(exposed_mob))
+ return
+ new smell_type(exposed_mob, reac_volume)
diff --git a/maplestation_modules/code/modules/loadouts/loadout_items/loadout_datum_pocket.dm b/maplestation_modules/code/modules/loadouts/loadout_items/loadout_datum_pocket.dm
index 4ddc75bae3a2..1f5b146ab10e 100644
--- a/maplestation_modules/code/modules/loadouts/loadout_items/loadout_datum_pocket.dm
+++ b/maplestation_modules/code/modules/loadouts/loadout_items/loadout_datum_pocket.dm
@@ -346,3 +346,7 @@
/datum/loadout_item/pocket_items/black_parasol
name = "Umbrella (Black Parasol)"
item_path = /obj/item/umbrella/parasol
+
+/datum/loadout_item/pocket_items/perfume
+ name = "Perfume"
+ item_path = /obj/item/reagent_containers/spray/perfume
diff --git a/maplestation_modules/code/modules/smells/_smell.dm b/maplestation_modules/code/modules/smells/_smell.dm
index a8711f06eccc..e645a49dcad6 100644
--- a/maplestation_modules/code/modules/smells/_smell.dm
+++ b/maplestation_modules/code/modules/smells/_smell.dm
@@ -19,6 +19,14 @@
/datum/smell/proc/on_smell(mob/living/whom, intensity)
return
+/// Returns the text shown when double-examining an atom with this smell
+/datum/smell/proc/get_examine_text(mob/living/for_whom, atom/movable/source, intensity)
+ var/adj = get_adjective(for_whom, intensity)
+ var/cat = get_category(for_whom, intensity)
+ if(adj)
+ return "[source.p_They()] [source.p_are()] emitting \a [adj] [cat] of [text]."
+ return "[source.p_They()] [source.p_are()] emitting \a [cat] of [text]."
+
/// Returns what adjective to use for the smell for output
/datum/smell/proc/get_adjective(mob/living/for_whom, intensity)
switch(intensity)
@@ -126,3 +134,15 @@
/datum/smell/burnt_food/fryer
text = "something acrid"
+
+/datum/smell/perfume
+ text = "perfume"
+ category = "fragrance"
+
+/datum/smell/perfume/on_smell(mob/living/whom, intensity)
+ whom.add_mood_event("perfume-smell", /datum/mood_event/perfume_smell)
+
+/datum/mood_event/perfume_smell
+ description = "What a nice smell."
+ mood_change = 1
+ timeout = 40 SECONDS
diff --git a/maplestation_modules/code/modules/smells/smell_component.dm b/maplestation_modules/code/modules/smells/smell_component.dm
index 6875a83c1c21..95da88392ad8 100644
--- a/maplestation_modules/code/modules/smells/smell_component.dm
+++ b/maplestation_modules/code/modules/smells/smell_component.dm
@@ -83,7 +83,7 @@
return ..()
/datum/component/complex_smell/proc/add_element()
- parent.AddElement(/datum/element/simple_smell, smell, intensity, radius, category, REF(src), smell_basetype)
+ parent.AddElement(/datum/element/simple_smell, smell, ceil(intensity), radius, category, smell_basetype)
// propagate to wearer if applicable
if(isitem(parent))
var/obj/item/item_parent = parent
@@ -91,7 +91,7 @@
on_worn(item_parent, item_parent.loc)
/datum/component/complex_smell/proc/remove_element()
- parent.RemoveElement(/datum/element/simple_smell, smell, intensity, radius, category, REF(src), smell_basetype)
+ parent.RemoveElement(/datum/element/simple_smell, smell, ceil(intensity), radius, category, smell_basetype)
// clear from wearer if applicable
if(isitem(parent))
var/obj/item/item_parent = parent
@@ -113,12 +113,12 @@
/datum/component/complex_smell/proc/on_worn(datum/source, mob/wearer)
SIGNAL_HANDLER
- wearer.RemoveElement(/datum/element/simple_smell, smell, intensity, radius, category, REF(src), smell_basetype)
- wearer.AddElement(/datum/element/simple_smell, smell, intensity, radius, category, REF(src), smell_basetype)
+ wearer.RemoveElement(/datum/element/simple_smell, smell, ceil(intensity), radius, category, smell_basetype)
+ wearer.AddElement(/datum/element/simple_smell, smell, ceil(intensity), radius, category, smell_basetype)
/datum/component/complex_smell/proc/on_unworn(datum/source, mob/wearer)
SIGNAL_HANDLER
- wearer.RemoveElement(/datum/element/simple_smell, smell, intensity, radius, category, REF(src), smell_basetype)
+ wearer.RemoveElement(/datum/element/simple_smell, smell, ceil(intensity), radius, category, smell_basetype)
/datum/component/complex_smell/proc/clean_up(...)
SIGNAL_HANDLER
@@ -142,14 +142,19 @@
fade_intensity_over_time,
list/clear_signals,
)
- if(smell != src.smell || category != src.category || wash_types != src.wash_types)
+ if(smell != src.smell || category != src.category || src.smell_basetype != smell_basetype)
return FALSE
+ // only replace the smell if the new one is stronger
if(intensity > src.intensity || radius > src.radius)
- parent.RemoveElement(/datum/element/simple_smell, smell, src.intensity, src.radius, category, REF(src), smell_basetype)
- parent.AddElement(/datum/element/simple_smell, smell, intensity, radius, category, REF(src), smell_basetype)
+ parent.RemoveElement(/datum/element/simple_smell, smell, ceil(src.intensity), src.radius, category, smell_basetype)
+ parent.AddElement(/datum/element/simple_smell, smell, ceil(intensity), radius, category, smell_basetype)
src.intensity = intensity
src.radius = radius
- addtimer(CALLBACK(src, PROC_REF(clean_up)), duration, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_DELETE_ME|TIMER_NO_HASH_WAIT)
+ // easier to just combine the wash types than try to determine which is stronger
+ src.wash_types |= wash_types
+ // otherwise just refresh duration
+ if(duration != INFINITY)
+ addtimer(CALLBACK(src, PROC_REF(clean_up)), duration, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_DELETE_ME|TIMER_NO_HASH_WAIT)
return TRUE
diff --git a/maplestation_modules/code/modules/smells/smell_element.dm b/maplestation_modules/code/modules/smells/smell_element.dm
index 8488cdeaca44..140d87aa43aa 100644
--- a/maplestation_modules/code/modules/smells/smell_element.dm
+++ b/maplestation_modules/code/modules/smells/smell_element.dm
@@ -44,9 +44,6 @@
var/intensity
/// How big the smell radius is
var/radius
- /// For smells which may be identical to another smell in all respects but need to be tracked separately
- /// Primarily used for complex smells which may stack multiple times on the same atom, like bloody clothing
- var/id
/**
* Arguments:
@@ -59,17 +56,15 @@
* Smell intensity will fall off linearly over distance.
* * category - Optional: the smell category, used for categorizing smells and determining how they interact with each other.
* Only used if smell is passed as a string.
- * * id - Optional: identifier for this smell instance, used to differentiate between multiple instances of the same smell on the same atom.
* * smell_basetype - Optional: the basetype to use when generating a smell singleton from a string. Defaults to /datum/smell.
*/
-/datum/element/simple_smell/Attach(datum/target, smell = "stink", intensity = SMELL_INTENSITY_FAINT, radius = 2, category, id = "innate", smell_basetype = /datum/smell)
+/datum/element/simple_smell/Attach(datum/target, smell = "stink", intensity = SMELL_INTENSITY_FAINT, radius = 2, category, smell_basetype = /datum/smell)
. = ..()
if(!isatom(target))
return ELEMENT_INCOMPATIBLE
if(intensity < SMELL_INTENSITY_FAINT)
stack_trace("A simple smell was created with invalid intensity - we will coerce it, but it should be fixed: [intensity]")
- src.id = id
if(isnull(src.smell))
src.smell = get_smell(smell, category, smell_basetype)
src.intensity = max(intensity, SMELL_INTENSITY_FAINT)
@@ -79,14 +74,21 @@
if(isturf(atom_target.loc))
mark_turfs(target, atom_target.loc)
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(update_turfs))
+ RegisterSignal(target, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(double_examine))
/datum/element/simple_smell/Detach(datum/target)
. = ..()
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(target, COMSIG_ATOM_EXAMINE_MORE)
var/atom/atom_target = target
if(isturf(atom_target.loc))
unmark_turfs(target, atom_target.loc)
+/datum/element/simple_smell/proc/double_examine(atom/movable/source, mob/examiner, list/examine_list)
+ SIGNAL_HANDLER
+ if(examiner.can_smell())
+ examine_list += span_info(smell.get_examine_text(examiner, source, intensity))
+
/datum/element/simple_smell/proc/update_turfs(atom/movable/source, atom/old_loc)
SIGNAL_HANDLER
diff --git a/maplestation_modules/code/modules/smells/living_procs.dm b/maplestation_modules/code/modules/smells/smell_helpers.dm
similarity index 77%
rename from maplestation_modules/code/modules/smells/living_procs.dm
rename to maplestation_modules/code/modules/smells/smell_helpers.dm
index 59444420620c..3376b07804b7 100644
--- a/maplestation_modules/code/modules/smells/living_procs.dm
+++ b/maplestation_modules/code/modules/smells/smell_helpers.dm
@@ -87,7 +87,7 @@
var/original_index = all_smells.Find(smell_effect)
var/correct_index = original_index
while(correct_index > 1)
- if(all_smells[all_smells[correct_index - 1]] > all_smells[all_smells[correct_index]])
+ if(all_smells[all_smells[correct_index - 1]] > effective_intensity)
break
correct_index -= 1
if(correct_index != original_index)
@@ -177,3 +177,56 @@
SIGNAL_ADDTRAIT(TRAIT_NO_ORGAN_DECAY), \
), \
)
+
+/// A helper proc to add smells that abstracts away the component vs element details,
+/// since most callers won't care about the implementation details
+/atom/proc/add_smell(
+ smell,
+ intensity,
+ radius,
+ category,
+ duration,
+ base_type,
+ wash_type,
+ is_fading,
+ clear_signals,
+)
+ ASSERT(istext(smell) || ispath(smell), "Smell must be a string or a path to a /datum/smell")
+ ASSERT(intensity > 0, "Smell intensity must be greater than 0")
+ ASSERT(radius >= 0, "Smell radius must be 0 or greater")
+ ASSERT(isnull(duration) || isnum(duration), "Smell duration must be a number or null")
+ ASSERT(isnull(category) || istext(category), "Smell category must be a string or null")
+ ASSERT(isnull(base_type) || ispath(base_type), "Smell base type must be a path to a /datum/smell or null")
+ ASSERT(isnull(wash_type) || isnum(wash_type), "Smell wash type must be a wash type flag or null")
+ ASSERT(isnull(is_fading) || isnum(is_fading), "Smell fading must be a boolean or null")
+ ASSERT(isnull(clear_signals) || islist(clear_signals), "Smell clear signals must be a list or null")
+
+ var/use_complex = duration || is_fading || wash_type || length(clear_signals)
+ if(isitem(src))
+ var/obj/item/wearable = src
+ if(wearable.slot_flags)
+ use_complex = TRUE
+
+ if(use_complex)
+ AddComponent(/datum/component/complex_smell, \
+ duration = duration, \
+ smell = smell, \
+ intensity = intensity, \
+ radius = radius, \
+ category = category, \
+ smell_basetype = base_type, \
+ wash_types = wash_type, \
+ fade_intensity_over_time = is_fading, \
+ clear_signals = clear_signals, \
+ )
+ else
+ AddElement(/datum/element/simple_smell, \
+ smell = smell, \
+ intensity = intensity, \
+ radius = radius, \
+ category = category, \
+ smell_basetype = base_type, \
+ )
+
+/area/add_smell(...)
+ CRASH("Areas do not support smells")
diff --git a/maplestation_modules/code/modules/smells/smell_holder.dm b/maplestation_modules/code/modules/smells/smell_holder.dm
index f8d167835467..9f0e8726bc0f 100644
--- a/maplestation_modules/code/modules/smells/smell_holder.dm
+++ b/maplestation_modules/code/modules/smells/smell_holder.dm
@@ -11,12 +11,56 @@
var/radius = 2
/// Optional, duration of the smell effect
var/duration
+ /// If TRUE, the smell's intensity will fade over its duration, otherwise it will disappear after the duration ends
+ var/fade_intensity_over_time = FALSE
+
+ /// If TRUE, immediately has nearby mobs take a "smell"
+ /// Primarily for scents that serve a gameplay purpose rather than just ambiance
+ var/force_smell = FALSE
/obj/effect/abstract/smell/Initialize(mapload)
. = ..()
- if(duration)
- QDEL_IN(src, duration)
- AddElement(/datum/element/simple_smell, smell, intensity, radius, category)
+ if(ismovable(loc))
+ apply_to_loc(arglist(length(args) >= 2 ? args.Copy(2) : list()))
+ . = INITIALIZE_HINT_QDEL
+ else if(isnull(loc))
+ . = INITIALIZE_HINT_QDEL
+ else
+ if(duration && duration != INFINITY)
+ QDEL_IN(src, duration)
+
+ if(fade_intensity_over_time)
+ AddComponent(/datum/component/complex_smell, \
+ duration = duration, \
+ smell = smell, \
+ intensity = intensity, \
+ radius = radius, \
+ category = category, \
+ fade_intensity_over_time = TRUE, \
+ )
+ else
+ AddElement(/datum/element/simple_smell, \
+ smell = smell, \
+ intensity = intensity, \
+ radius = radius, \
+ category = category, \
+ )
+
+ if(force_smell)
+ for(var/mob/living/smeller in get_hearers_in_view(DEFAULT_MESSAGE_RANGE, get_turf(src)))
+ smeller.smell_something()
+
+
+/obj/effect/abstract/smell/proc/apply_to_loc(...)
+ loc.AddComponent( \
+ /datum/component/complex_smell, \
+ duration = src.duration, \
+ smell = src.smell, \
+ intensity = src.intensity, \
+ radius = src.radius, \
+ category = src.category, \
+ fade_intensity_over_time = src.fade_intensity_over_time, \
+ )
// /obj/effect/abstract/smell/gunpowder
// smell = "gunpowder"
@@ -27,15 +71,25 @@
smell = "ozone"
category = "fragrance"
intensity = SMELL_INTENSITY_FAINT
+ duration = 20 SECONDS
+
+/obj/effect/abstract/smell/ozone/lingering
duration = 10 MINUTES
+ fade_intensity_over_time = TRUE
/obj/effect/abstract/smell/reagent
- /// Intensity scales based on volume used and this factor
- var/volume_scale_factor = 5
+ /// Intensity scales based on volume used and this factor. Cannot exceed base intensity
+ /// Think of this in terms of "You need at least vol/x units to get the full effect"
+ var/volume_intensity_scale = 0.2
+ /// Duration scales based on volume used and this factor. Cannot exceed base duration
+ /// Think of this in terms of "You need at least vol/x units to get the full duration"
+ var/volume_duration_scale = 1
-/obj/effect/abstract/smell/reagent/Initialize(mapload, volume = 1)
- . = ..()
- intensity *= clamp(round(volume / volume_scale_factor, 0.1), 0.1, 1)
+/obj/effect/abstract/smell/reagent/Initialize(mapload, volume)
+ if(isnum(volume))
+ intensity *= clamp(round(volume * volume_intensity_scale, 0.1), 0.1, 1)
+ duration *= clamp(round(volume * volume_duration_scale, 0.1), 0.1, 1)
+ return ..()
/obj/effect/abstract/smell/reagent/cleaning_chemicals
smell = "cleaning chemicals"
@@ -55,16 +109,19 @@
intensity = SMELL_INTENSITY_MODERATE
duration = 2 MINUTES
+/obj/effect/abstract/smell/reagent/perfume
+ smell = "perfume"
+ category = "fragrance"
+ intensity = SMELL_INTENSITY_WEAK
+ duration = 30 MINUTES
+ volume_intensity_scale = 0.5
+ volume_duration_scale = 0.5
+
/obj/effect/abstract/smell/oven
intensity = SMELL_INTENSITY_MODERATE
duration = 5 MINUTES
radius = 6
-
-/obj/effect/abstract/smell/oven/Initialize(mapload)
- . = ..()
- // immediately forces nearby mobs to take a sniff
- for(var/mob/living/smeller in get_hearers_in_view(DEFAULT_MESSAGE_RANGE, src))
- smeller.smell_something()
+ force_smell = TRUE
/obj/effect/abstract/smell/oven/good
smell = /datum/smell/good_food
@@ -77,5 +134,15 @@
/obj/effect/abstract/smell/cigarette_smoke
smell = /datum/smell/cigarette_smoke
- intensity = SMELL_INTENSITY_MODERATE
+ intensity = SMELL_INTENSITY_MODERATE * 0.5
duration = 20 SECONDS
+
+/obj/effect/abstract/smell/cigarette_smoke/lingering
+ intensity = SMELL_INTENSITY_WEAK
+ duration = 2 MINUTES
+ fade_intensity_over_time = TRUE
+
+/obj/effect/abstract/smell/cigarette_smoke/lingering/longer
+ intensity = SMELL_INTENSITY_WEAK
+ duration = 8 MINUTES
+ fade_intensity_over_time = TRUE