From 1a9c71b7b7c7d70a741f8931410129a07e80e312 Mon Sep 17 00:00:00 2001 From: MrMelbert Date: Mon, 2 Mar 2026 16:32:11 -0600 Subject: [PATCH 1/2] Smell fixes and parfume --- code/datums/components/bakeable.dm | 4 +- code/datums/elements/decals/blood.dm | 3 +- code/datums/wounds/scars/_scars.dm | 2 +- .../effects/decals/cleanable/humans.dm | 28 +++--- .../objects/effects/decals/cleanable/misc.dm | 10 +- .../effects/decals/cleanable/robots.dm | 7 +- code/game/objects/items/cigs_lighters.dm | 4 +- code/game/objects/items/storage/backpack.dm | 8 +- code/game/objects/structures/bonfire.dm | 12 ++- code/game/objects/structures/headpike.dm | 2 +- code/modules/fishing/fish/_fish.dm | 2 +- .../food_and_drinks/machinery/deep_fryer.dm | 2 +- .../modules/food_and_drinks/machinery/oven.dm | 2 +- .../food_and_drinks/recipes/soup_mixtures.dm | 2 +- code/modules/forensics/_forensics.dm | 2 +- code/modules/forensics/forensics_helpers.dm | 21 +++-- code/modules/hydroponics/grown/flowers.dm | 13 ++- code/modules/hydroponics/hydroponics.dm | 2 +- code/modules/mob/living/carbon/examine.dm | 6 +- code/modules/mob/living/carbon/human/dummy.dm | 2 + code/modules/mob/mob.dm | 6 +- .../projectiles/ammunition/_ammunition.dm | 12 +-- code/modules/projectiles/guns/ballistic.dm | 12 +-- code/modules/projectiles/guns/energy.dm | 5 +- .../chemistry/reagents/other_reagents.dm | 21 +---- code/modules/reagents/reagent_dispenser.dm | 2 +- code/modules/surgery/bodyparts/_bodyparts.dm | 2 +- code/modules/surgery/bodyparts/head.dm | 2 +- code/modules/surgery/organs/_organ.dm | 27 ++++-- code/modules/surgery/organs/organ_movement.dm | 13 +-- maplestation.dme | 3 +- .../code/datums/elements/unique_examine.dm | 2 +- .../pain_reagents/painkiller_reactions.dm | 11 --- .../code/game/objects/items/ashtray.dm | 7 +- .../code/game/objects/items/perfume.dm | 38 ++++++++ .../loadout_items/loadout_datum_pocket.dm | 4 + .../code/modules/smells/_smell.dm | 20 ++++ .../code/modules/smells/smell_component.dm | 23 +++-- .../code/modules/smells/smell_element.dm | 14 +-- .../{living_procs.dm => smell_helpers.dm} | 55 ++++++++++- .../code/modules/smells/smell_holder.dm | 94 ++++++++++++++++--- 41 files changed, 350 insertions(+), 157 deletions(-) create mode 100644 maplestation_modules/code/game/objects/items/perfume.dm rename maplestation_modules/code/modules/smells/{living_procs.dm => smell_helpers.dm} (77%) 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..341bf058a68f 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,24 @@ 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) + 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 +108,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 +133,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 From bc575f4730a182558d39cd19f277db35f753dd4d Mon Sep 17 00:00:00 2001 From: MrMelbert Date: Mon, 2 Mar 2026 23:50:46 -0600 Subject: [PATCH 2/2] Runtimes --- maplestation_modules/code/modules/smells/smell_holder.dm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/maplestation_modules/code/modules/smells/smell_holder.dm b/maplestation_modules/code/modules/smells/smell_holder.dm index 341bf058a68f..9f0e8726bc0f 100644 --- a/maplestation_modules/code/modules/smells/smell_holder.dm +++ b/maplestation_modules/code/modules/smells/smell_holder.dm @@ -85,9 +85,10 @@ /// 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_intensity_scale, 0.1), 0.1, 1) - duration *= clamp(round(volume * volume_duration_scale, 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