diff --git a/beestation.dme b/beestation.dme index c5a1ba64aa705..8315ca36b5c7b 100644 --- a/beestation.dme +++ b/beestation.dme @@ -718,12 +718,10 @@ #include "code\datums\components\plumbing\splitter.dm" #include "code\datums\components\slime\basic_scared_of_item.dm" #include "code\datums\components\slime\emotion_buffer.dm" -#include "code\datums\components\slime\friendship_tracker.dm" #include "code\datums\components\slime\generic_mob_hunger.dm" #include "code\datums\components\slime\latch_feeding.dm" #include "code\datums\components\slime\liquid_secretion.dm" #include "code\datums\components\slime\mob_stacker.dm" -#include "code\datums\components\slime\pollution_scrubber.dm" #include "code\datums\components\slime\vac_tagged.dm" #include "code\datums\components\storage\storage.dm" #include "code\datums\components\storage\concrete\_concrete.dm" @@ -2667,7 +2665,6 @@ #include "code\modules\food_and_drinks\kitchen_machinery\grill.dm" #include "code\modules\food_and_drinks\kitchen_machinery\icecream_vat.dm" #include "code\modules\food_and_drinks\kitchen_machinery\microwave.dm" -#include "code\modules\food_and_drinks\kitchen_machinery\monkeyrecycler.dm" #include "code\modules\food_and_drinks\kitchen_machinery\processor.dm" #include "code\modules\food_and_drinks\kitchen_machinery\smartfridge.dm" #include "code\modules\food_and_drinks\recipes\drinks_recipes.dm" @@ -3785,6 +3782,24 @@ #include "code\modules\research\techweb\_techweb_node.dm" #include "code\modules\research\techweb\all_nodes.dm" #include "code\modules\research\xenobiology\xenobiology.dm" +#include "code\modules\research\xenobiology\crossbreeding\__corecross.dm" +#include "code\modules\research\xenobiology\crossbreeding\_clothing.dm" +#include "code\modules\research\xenobiology\crossbreeding\_misc.dm" +#include "code\modules\research\xenobiology\crossbreeding\_mobs.dm" +#include "code\modules\research\xenobiology\crossbreeding\_potions.dm" +#include "code\modules\research\xenobiology\crossbreeding\_status_effects.dm" +#include "code\modules\research\xenobiology\crossbreeding\_weapons.dm" +#include "code\modules\research\xenobiology\crossbreeding\burning.dm" +#include "code\modules\research\xenobiology\crossbreeding\charged.dm" +#include "code\modules\research\xenobiology\crossbreeding\chilling.dm" +#include "code\modules\research\xenobiology\crossbreeding\consuming.dm" +#include "code\modules\research\xenobiology\crossbreeding\industrial.dm" +#include "code\modules\research\xenobiology\crossbreeding\prismatic.dm" +#include "code\modules\research\xenobiology\crossbreeding\recurring.dm" +#include "code\modules\research\xenobiology\crossbreeding\regenerative.dm" +#include "code\modules\research\xenobiology\crossbreeding\reproductive.dm" +#include "code\modules\research\xenobiology\crossbreeding\selfsustaining.dm" +#include "code\modules\research\xenobiology\crossbreeding\stabilized.dm" #include "code\modules\ruins\lavaland_ruin_code.dm" #include "code\modules\ruins\lavalandruin_code\biodome_clown_planet.dm" #include "code\modules\ruins\lavalandruin_code\pizzaparty.dm" diff --git a/code/__DEFINES/ai.dm b/code/__DEFINES/ai.dm index 7fe4abbd805a9..dc1effefca4e3 100644 --- a/code/__DEFINES/ai.dm +++ b/code/__DEFINES/ai.dm @@ -185,3 +185,6 @@ #define BB_BASIC_MOB_CURRENT_TARGET "BB_basic_current_target" #define BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION "BB_basic_current_target_hiding_location" #define BB_TARGETTING_DATUM "targetting_datum" + +/// Flag to set on if you want your mob to STOP running away +#define BB_BASIC_MOB_STOP_FLEEING "BB_basic_stop_fleeing" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm index 93721956b761e..66dd862db99d7 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm @@ -42,8 +42,6 @@ ///from base of mob/living/Stun() (amount, ignore_canstun) #define COMSIG_LIVING_STATUS_STUN "living_stun" -///from mob/living/carbon/enter_stamina_stun() -#define COMSIG_LIVING_STAMINA_STUN "carbon_stamina_stun" ///from base of mob/living/Knockdown() (amount, ignore_canstun) #define COMSIG_LIVING_STATUS_KNOCKDOWN "living_knockdown" ///from base of mob/living/Paralyze() (amount, ignore_canstun) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm index 72bcb7ebb2fbe..e12c7abbd9354 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm @@ -91,3 +91,9 @@ #define COMSIG_STACK_MOVE "stack_move" #define COMSIG_CHECK_CAN_ADD_NEW_STACK "check_stack_add" #define COMSIG_MOBSTACKER_DESTROY "mobstack_destroy_stack" + +/// Signal sent when a blackboard key is set to a new value +#define COMSIG_AI_BLACKBOARD_KEY_SET(blackboard_key) "ai_blackboard_key_set_[blackboard_key]" + +/// Signal sent when a blackboard key is cleared +#define COMSIG_AI_BLACKBOARD_KEY_CLEARED(blackboard_key) "ai_blackboard_key_clear_[blackboard_key]" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index be398a7d46e8e..c3ba6c272c0f6 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -603,3 +603,8 @@ GLOBAL_LIST_INIT(available_random_trauma_list, list( #define STANDING_UP 0 /// Mob is lying down, usually associated with lying_angle values of 90 or 270. #define LYING_DOWN 1 + +/// Default minimum body temperature mobs can exist in before taking damage +#define NPC_DEFAULT_MIN_TEMP 250 +/// Default maximum body temperature mobs can exist in before taking damage +#define NPC_DEFAULT_MAX_TEMP 350 diff --git a/code/__DEFINES/slimes.dm b/code/__DEFINES/slimes.dm index 1ae2801be222d..1e4972ea34f95 100644 --- a/code/__DEFINES/slimes.dm +++ b/code/__DEFINES/slimes.dm @@ -89,3 +89,9 @@ #define COMSIG_FRIENDSHIP_CHECK_LEVEL "friendship_check_level" #define COMSIG_FRIENDSHIP_CHANGE "friendship_change" +#define COMSIG_FRIENDSHIP_PASS_FRIENDSHIP "friendship_passfriends" + +#define TRAIT_RAINBOWED "rainbowed" + +#define COMSIG_ATOM_MOUSE_ENTERED "mouse_entered" +#define COMSIG_CLIENT_HOVER_NEW "client_new_hover" diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index 5b9849d7da401..323a681d1277e 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -270,3 +270,126 @@ multiple modular subtrees with behaviors if(iter_behavior.required_distance < minimum_distance) minimum_distance = iter_behavior.required_distance return minimum_distance + + +/// Used for above to track all the keys that have registered a signal +#define TRAIT_AI_TRACKING "tracked_by_ai" + + +/** + * Used to manage references to datum by AI controllers + * + * * tracked_datum - something being added to an ai blackboard + * * key - the associated key + */ +#define TRACK_AI_DATUM_TARGET(tracked_datum, key) do { \ + if(isweakref(tracked_datum)) { \ + var/datum/weakref/_bad_weakref = tracked_datum; \ + stack_trace("Weakref (Actual datum: [_bad_weakref.resolve()]) found in ai datum blackboard! \ + This is an outdated method of ai reference handling, please remove it."); \ + }; \ + else if(isdatum(tracked_datum)) { \ + var/datum/_tracked_datum = tracked_datum; \ + if(!HAS_TRAIT_FROM(_tracked_datum, TRAIT_AI_TRACKING, "[REF(src)]_[key]")) { \ + RegisterSignal(_tracked_datum, COMSIG_PARENT_QDELETING, PROC_REF(sig_remove_from_blackboard), override = TRUE); \ + ADD_TRAIT(_tracked_datum, TRAIT_AI_TRACKING, "[REF(src)]_[key]"); \ + }; \ + }; \ +} while(FALSE) + +/** + * Used to clear previously set reference handing by AI controllers + * + * * tracked_datum - something being removed from an ai blackboard + * * key - the associated key + */ +#define CLEAR_AI_DATUM_TARGET(tracked_datum, key) do { \ + if(isdatum(tracked_datum)) { \ + var/datum/_tracked_datum = tracked_datum; \ + REMOVE_TRAIT(_tracked_datum, TRAIT_AI_TRACKING, "[REF(src)]_[key]"); \ + if(!HAS_TRAIT(_tracked_datum, TRAIT_AI_TRACKING)) { \ + UnregisterSignal(_tracked_datum, COMSIG_PARENT_QDELETING); \ + }; \ + }; \ +} while(FALSE) + +/** + * Sets the key to the passed "thing". + * + * * key - A blackboard key + * * thing - a value to set the blackboard key to. + */ +/datum/ai_controller/proc/set_blackboard_key(key, thing) + // Assume it is an error when trying to set a value overtop a list + if(islist(blackboard[key])) + CRASH("set_blackboard_key attempting to set a blackboard value to key [key] when it's a list!") + // Don't do anything if it's already got this value + if (blackboard[key] == thing) + return + + // Clear existing values + if(!isnull(blackboard[key])) + clear_blackboard_key(key) + + TRACK_AI_DATUM_TARGET(thing, key) + blackboard[key] = thing + post_blackboard_key_set(key) + + +/** + * Clears the passed key, resetting it to null + * + * Not intended for use with list keys - use [proc/remove_thing_from_blackboard_key] if you are removing a value from a list at a key + * + * * key - A blackboard key + */ +/datum/ai_controller/proc/clear_blackboard_key(key) + if(isnull(blackboard[key])) + return + CLEAR_AI_DATUM_TARGET(blackboard[key], key) + blackboard[key] = null + if(isnull(pawn)) + return + SEND_SIGNAL(pawn, COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)) + +/** + * Called after we set a blackboard key, forwards signal information. + */ +/datum/ai_controller/proc/post_blackboard_key_set(key) + if (isnull(pawn)) + return + SEND_SIGNAL(pawn, COMSIG_AI_BLACKBOARD_KEY_SET(key)) + +/// Signal proc to go through every key and remove the datum from all keys it finds +/datum/ai_controller/proc/sig_remove_from_blackboard(datum/source) + SIGNAL_HANDLER + + var/list/list/remove_queue = list(blackboard) + var/index = 1 + while(index <= length(remove_queue)) + var/list/next_to_clear = remove_queue[index] + for(var/inner_value in next_to_clear) + var/associated_value = next_to_clear[inner_value] + // We are a lists of lists, add the next value to the queue so we can handle references in there + // (But we only need to bother checking the list if it's not empty.) + if(islist(inner_value) && length(inner_value)) + UNTYPED_LIST_ADD(remove_queue, inner_value) + + // We found the value that's been deleted. Clear it out from this list + else if(inner_value == source) + next_to_clear -= inner_value + + // We are an assoc lists of lists, the list at the next value so we can handle references in there + // (But again, we only need to bother checking the list if it's not empty.) + if(islist(associated_value) && length(associated_value)) + UNTYPED_LIST_ADD(remove_queue, associated_value) + + // We found the value that's been deleted, it was an assoc value. Clear it out entirely + else if(associated_value == source) + next_to_clear -= inner_value + SEND_SIGNAL(pawn, COMSIG_AI_BLACKBOARD_KEY_CLEARED(inner_value)) + + index += 1 + + +#undef TRAIT_AI_TRACKING diff --git a/code/datums/components/slime/friendship_tracker.dm b/code/datums/components/slime/friendship_tracker.dm deleted file mode 100644 index aa6f8bb35eadd..0000000000000 --- a/code/datums/components/slime/friendship_tracker.dm +++ /dev/null @@ -1,159 +0,0 @@ -/datum/component/friendship_container - ///our friendship thresholds from lowest to highest - var/list/friendship_levels = list() - ///our current friends stored as a weakref = amount - var/list/weakrefed_friends = list() - ///list of friendship levels that we send BEFRIEND signals on, if someone drops below these levels its over - var/befriend_level - ///list of all befriended refs - var/list/befriended_refs = list() - -/datum/component/friendship_container/Initialize(friendship_levels = list(), befriend_level) - . = ..() - if(!length(friendship_levels)) - return FALSE - - src.friendship_levels = friendship_levels - src.befriend_level = befriend_level - - -/datum/component/friendship_container/Destroy(force, silent) - . = ..() - befriended_refs = null - weakrefed_friends = null - friendship_levels = null - -/datum/component/friendship_container/RegisterWithParent() - RegisterSignal(parent, COMSIG_FRIENDSHIP_CHECK_LEVEL, PROC_REF(check_friendship_level)) - RegisterSignal(parent, COMSIG_FRIENDSHIP_CHANGE, PROC_REF(change_friendship)) - RegisterSignal(parent, COMSIG_FRIENDSHIP_PASS_FRIENDSHIP, PROC_REF(pass_friendship)) - RegisterSignal(parent, COMSIG_ATOM_MOUSE_ENTERED, PROC_REF(view_friendship)) - -/datum/component/friendship_container/UnregisterFromParent() - UnregisterSignal(parent, COMSIG_FRIENDSHIP_CHECK_LEVEL) - UnregisterSignal(parent, COMSIG_FRIENDSHIP_CHANGE) - UnregisterSignal(parent, COMSIG_FRIENDSHIP_PASS_FRIENDSHIP) - UnregisterSignal(parent, COMSIG_ATOM_MOUSE_ENTERED) - -/datum/component/friendship_container/proc/change_friendship(mob/living/source, atom/target, amount) - for(var/datum/weakref/ref as anything in weakrefed_friends) - if(ref.resolve() == target) - - ///handles registering pet commands and other things that use BEFRIEND - if(amount < 0) - if((friendship_levels[befriend_level] > weakrefed_friends[ref]) && (ref in befriended_refs)) - SEND_SIGNAL(parent, COMSIG_LIVING_UNFRIENDED, ref.resolve()) - befriended_refs -= ref - source.ai_controller?.remove_thing_from_blackboard_key(BB_FRIENDS_LIST, target) - - else if((friendship_levels[befriend_level] <= weakrefed_friends[ref]) && !(ref in befriended_refs)) - SEND_SIGNAL(parent, COMSIG_LIVING_BEFRIENDED, ref.resolve()) - befriended_refs += ref - source.ai_controller?.insert_blackboard_key_lazylist(BB_FRIENDS_LIST, target) - - weakrefed_friends[ref] += amount - return TRUE - weakrefed_friends += list(WEAKREF(target) = amount) - return TRUE - -///Returns {TRUE} if friendship is above a certain threshold else returns {FALSE} -/datum/component/friendship_container/proc/check_friendship_level(mob/living/source, atom/target, friendship_level) - for(var/datum/weakref/ref as anything in weakrefed_friends) - if(isnull(ref) || QDELETED(ref)) - weakrefed_friends -= ref - continue - if(ref.resolve() == target) - if(friendship_levels[friendship_level] <= weakrefed_friends[ref]) - return TRUE - return FALSE - return FALSE - - -/datum/component/friendship_container/proc/pass_friendship(datum/source, atom/target) - if(!target.GetComponent(/datum/component/friendship_container)) - target.AddComponent(/datum/component/friendship_container, friendship_levels, befriend_level) - - for(var/datum/weakref/ref as anything in weakrefed_friends) - if(isnull(ref) || QDELETED(ref)) - weakrefed_friends -= ref - continue - var/amount = weakrefed_friends[ref] - var/atom/resolved = ref.resolve() - SEND_SIGNAL(target, COMSIG_FRIENDSHIP_CHANGE, resolved, amount) - - -/datum/component/friendship_container/proc/view_friendship(mob/living/source, mob/living/clicker) - if(!istype(clicker) || !length(weakrefed_friends) || !clicker.client) - return - var/max_level = friendship_levels[length(friendship_levels)] - var/max_level_value = friendship_levels[max_level] - for(var/datum/weakref/ref as anything in weakrefed_friends) - if(isnull(ref) || QDELETED(ref)) - weakrefed_friends -= ref - continue - if(ref.resolve() != clicker) - continue - - var/list/offset_to_add = get_icon_dimensions(source.icon) - var/y_position = offset_to_add["height"] + 1 - var/obj/effect/overlay/happiness_overlay/hearts = new(null, clicker) - var/lowest_level = friendship_levels[1] - var/lowest_level_value = friendship_levels[lowest_level] - hearts.pixel_y = y_position - hearts.set_hearts((weakrefed_friends[ref] - (lowest_level_value)) / (max_level_value - (lowest_level_value))) - var/image/new_image = new(source) - new_image.appearance = hearts.appearance - if(!isturf(source.loc)) - new_image.loc = source.loc - else - new_image.loc = source - SET_PLANE(new_image, new_image.plane, source) - clicker.client.images += new_image - hearts.image = new_image - - -/obj/effect/overlay/happiness_overlay - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - anchored = TRUE - vis_flags = VIS_INHERIT_DIR | VIS_INHERIT_PLANE - layer = ABOVE_HUD_PLANE - plane = GAME_PLANE_UPPER - ///how many hearts should we display - VAR_PRIVATE/hearts_percentage - ///icon of our heart - var/heart_icon = 'icons/effects/effects.dmi' - var/client/stored_client - var/image/image - var/full_icon = "full_heart" - var/empty_icon = "empty_heart" - -/obj/effect/overlay/happiness_overlay/New(loc, mob/living/clicker) - . = ..() - if(!clicker) - return - - RegisterSignal(clicker.client, COMSIG_CLIENT_HOVER_NEW, PROC_REF(clear_view)) - stored_client = clicker.client - -/obj/effect/overlay/happiness_overlay/Destroy(force) - . = ..() - stored_client?.images -= image - QDEL_NULL(image) - stored_client = null - -/obj/effect/overlay/happiness_overlay/proc/set_hearts(happiness_percentage) - hearts_percentage = happiness_percentage - update_appearance(UPDATE_OVERLAYS) - -/obj/effect/overlay/happiness_overlay/update_overlays() - . = ..() - var/static/list/heart_positions = list(-24, -16, -8, 0, 8, 16, 24) - var/display_amount = round(length(heart_positions) * hearts_percentage, 1) - for(var/index in 1 to length(heart_positions)) - var/heart_icon_state = display_amount >= index ? full_icon : empty_icon - var/mutable_appearance/display_icon = mutable_appearance(icon = heart_icon, icon_state = heart_icon_state, layer = ABOVE_HUD_PLANE) - display_icon.pixel_x = heart_positions[index] - . += display_icon - -/obj/effect/overlay/happiness_overlay/proc/clear_view() - qdel(src) diff --git a/code/datums/components/slime/generic_mob_hunger.dm b/code/datums/components/slime/generic_mob_hunger.dm index c9e983768ad7b..c47a335d66572 100644 --- a/code/datums/components/slime/generic_mob_hunger.dm +++ b/code/datums/components/slime/generic_mob_hunger.dm @@ -93,7 +93,3 @@ /datum/component/generic_mob_hunger/proc/remove_hunger_trait(trait) REMOVE_TRAIT(parent, trait, "hunger_trait") - -/obj/effect/overlay/happiness_overlay/hunger - full_icon = "full_hunger" - empty_icon = "empty_hunger" diff --git a/code/datums/components/slime/latch_feeding.dm b/code/datums/components/slime/latch_feeding.dm index c08040a2f59cb..f7bdd090ce741 100644 --- a/code/datums/components/slime/latch_feeding.dm +++ b/code/datums/components/slime/latch_feeding.dm @@ -61,12 +61,12 @@ target.unbuckle_all_mobs(force = TRUE) if(target.buckle_mob(basic_mob, TRUE, loc_check)) basic_mob.layer = target.layer + 0.1 - target.visible_message(span_danger("[basic_mob] latches onto [target]!"), \ - span_userdanger("[basic_mob] latches onto [target]!")) + target.visible_message("[basic_mob] latches onto [target]!", \ + "[basic_mob] latches onto [target]!") ADD_TRAIT(target, TRAIT_LATCH_FEEDERED, "latch_feeding") return TRUE else - to_chat(basic_mob, span_notice("You failed to latch onto [target].")) + to_chat(basic_mob, ("You failed to latch onto [target].")) if(init) return FALSE else @@ -83,8 +83,8 @@ "I am not satisified", "I can not feed from this subject", \ "I do not feel nourished", "This subject is not food")]!") if(!silent) - basic_mob.visible_message(span_warning("[basic_mob] lets go of [basic_mob.buckled]!"), \ - span_notice("I stopped feeding.")) + basic_mob.visible_message("[basic_mob] lets go of [basic_mob.buckled]!", \ + "I stopped feeding.") REMOVE_TRAIT(target, TRAIT_LATCH_FEEDERED, "latch_feeding") basic_mob.layer = initial(basic_mob.layer) @@ -114,9 +114,9 @@ if(!check_and_replace || (check_and_replace && !check_and_replace.Invoke())) if(iscarbon(living_target)) - living_target.apply_damage(damage_amount, damage_type, spread_damage = TRUE) + living_target.apply_damage(damage_amount, damage_type) else - living_target.apply_damage(damage_amount, BRUTE, spread_damage = TRUE) + living_target.apply_damage(damage_amount, BRUTE) if(!QDELETED(parent)) // ??? I was getting runtimes for no parent but IDK how SEND_SIGNAL(parent, COMSIG_MOB_FEED, target, hunger_restore) diff --git a/code/datums/components/slime/liquid_secretion.dm b/code/datums/components/slime/liquid_secretion.dm index ad3464ba04c53..7401ce3f47fe9 100644 --- a/code/datums/components/slime/liquid_secretion.dm +++ b/code/datums/components/slime/liquid_secretion.dm @@ -50,4 +50,6 @@ var/list/reagent_list = list() reagent_list |= reagent_id reagent_list[reagent_id] = amount - parent_turf.add_liquid_list(reagent_list, FALSE, T20C) + for(reagent_id in reagent_list) + for(amount) + new reagent_id(parent_turf) diff --git a/code/datums/components/slime/pollution_scrubber.dm b/code/datums/components/slime/pollution_scrubber.dm deleted file mode 100644 index 108dfb2bd7a90..0000000000000 --- a/code/datums/components/slime/pollution_scrubber.dm +++ /dev/null @@ -1,28 +0,0 @@ -/datum/component/pollution_scrubber - ///the amount we try to scrub each process - var/scrubbing_amount - ///the lifetime if set it will delete itself after this point - var/lifetime - -/datum/component/pollution_scrubber/Initialize(scrubbing_amount, lifetime) - . = ..() - src.scrubbing_amount = scrubbing_amount - src.lifetime = lifetime - - if(lifetime) - addtimer(CALLBACK(src, PROC_REF(kill_component)), lifetime) - START_PROCESSING(SSobj, src) - - -/datum/component/pollution_scrubber/proc/kill_component() - qdel(src) - -/datum/component/pollution_scrubber/process(seconds_per_tick) - if(isliving(parent)) - var/mob/living/living = parent - if(living.stat == DEAD) - return - - var/turf/open/turf = get_turf(parent) - if(turf.pollution) - turf.pollution.scrub_amount(scrubbing_amount) diff --git a/code/datums/components/slime/vac_tagged.dm b/code/datums/components/slime/vac_tagged.dm index 44db508e3ea07..c214972f02fdd 100644 --- a/code/datums/components/slime/vac_tagged.dm +++ b/code/datums/components/slime/vac_tagged.dm @@ -9,13 +9,13 @@ creator = WEAKREF(creator_mob) /datum/component/vac_tagged/Destroy(force, silent) - UnregisterSignal(parent, list(COMSIG_MOB_FED_ON, COMSIG_QDELETING)) + UnregisterSignal(parent, list(COMSIG_MOB_FED_ON, COMSIG_PARENT_QDELETING)) . = ..() /datum/component/vac_tagged/RegisterWithParent() . = ..() RegisterSignal(parent, COMSIG_MOB_FED_ON, PROC_REF(on_fed_on)) - RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(on_deleting)) + RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(on_deleting)) /datum/component/vac_tagged/proc/on_deleting() qdel(src) diff --git a/code/game/machinery/corral_corner.dm b/code/game/machinery/corral_corner.dm index 47877c2e493fc..0310e9cf6787b 100644 --- a/code/game/machinery/corral_corner.dm +++ b/code/game/machinery/corral_corner.dm @@ -75,7 +75,7 @@ return var/obj/item/multitool/multitool = tool multitool.buffer = src - to_chat(user, span_notice("You save the data in the [multitool.name]'s buffer.")) + to_chat(user, "You save the data in the [multitool.name]'s buffer.") return TOOL_ACT_TOOLTYPE_SUCCESS /obj/machinery/corral_corner/proc/start_linking_procedure() diff --git a/code/game/machinery/xenobio/biomass_recycler.dm b/code/game/machinery/xenobio/biomass_recycler.dm index b735aed76d936..c5cbfdf32e957 100644 --- a/code/game/machinery/xenobio/biomass_recycler.dm +++ b/code/game/machinery/xenobio/biomass_recycler.dm @@ -26,7 +26,7 @@ GLOBAL_LIST_INIT(biomass_unlocks, list()) /obj/machinery/biomass_recycler/examine(mob/user) . = ..() if(in_range(user, src) || isobserver(user)) - . += span_notice("The status display reads: Recycles [cube_production] biomass units per unit inserted.") + . += "The status display reads: Recycles [cube_production] biomass units per unit inserted." /obj/machinery/biomass_recycler/wrench_act(mob/living/user, obj/item/tool) . = ..() @@ -45,14 +45,14 @@ GLOBAL_LIST_INIT(biomass_unlocks, list()) stored_matter += biomass.amount qdel(biomass) if(total_biomass > 0) - to_chat(user, span_notice("You dump [total_biomass] cube\s of biomass from [item] into [src].")) + to_chat(user, "You dump [total_biomass] cube\s of biomass from [item] into [src].") user.balloon_alert_to_viewers("inserted biomass") return else if(HAS_TRAIT(item, TRAIT_NODROP)) return else if(istype(item, /obj/item/stack/biomass)) var/obj/item/stack/biomass/biomass = item - to_chat(user, span_notice("You insert [biomass.amount] cube\s of biomass into [src].")) + to_chat(user, "You insert [biomass.amount] cube\s of biomass into [src].") user.balloon_alert_to_viewers("inserted biomass") stored_matter += biomass.amount qdel(biomass) @@ -84,24 +84,24 @@ GLOBAL_LIST_INIT(biomass_unlocks, list()) if(!istype(target)) return if(target.stat == CONSCIOUS) - to_chat(user, span_warning("[target] is struggling far too much to put it in the recycler.")) + to_chat(user, "[target] is struggling far too much to put it in the recycler.") return if(target.buckled || target.has_buckled_mobs()) - to_chat(user, span_warning("[target] is attached to something.")) + to_chat(user, "[target] is attached to something.") return recycle(target, user, recycable_type) /obj/machinery/biomass_recycler/proc/recycle(atom/movable/target, mob/living/user, recycable_type) qdel(target) - to_chat(user, span_notice("You stuff [target] into the machine.")) + to_chat(user, "You stuff [target] into the machine.") playsound(src.loc, 'sound/machines/juicer.ogg', 50, TRUE) var/offset = prob(50) ? -2 : 2 animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 200) //start shaking use_power(active_power_usage) stored_matter += cube_production * recyclable_types[recycable_type] addtimer(VARSET_CALLBACK(src, pixel_x, base_pixel_x)) - addtimer(CALLBACK(GLOBAL_PROC, /proc/to_chat, user, span_notice("The machine now has [stored_matter] unit\s of biomass stored."))) + addtimer(CALLBACK(GLOBAL_PROC, /proc/to_chat, user, "The machine now has [stored_matter] unit\s of biomass stored.")) /obj/machinery/biomass_recycler/interact(mob/user) var/list/items = list() @@ -125,15 +125,15 @@ GLOBAL_LIST_INIT(biomass_unlocks, list()) var/spawn_type = item_names[pick] if(stored_matter < printable_types[spawn_type]) - to_chat(user, span_warning("[src] does not have enough stored biomass for that! It currently has [stored_matter] out of [printable_types[spawn_type]] unit\s required.")) + to_chat(user, "[src] does not have enough stored biomass for that! It currently has [stored_matter] out of [printable_types[spawn_type]] unit\s required.") balloon_alert(user, "not enough biomass") return var/spawned = new spawn_type(user.loc) - to_chat(user, span_notice("The machine hisses loudly as it condenses the biomass. After a moment, it dispenses a brand new [spawned].")) + to_chat(user, "The machine hisses loudly as it condenses the biomass. After a moment, it dispenses a brand new [spawned].") playsound(src.loc, 'sound/machines/hiss.ogg', 50, TRUE) stored_matter -= printable_types[spawn_type] - to_chat(user, span_notice("The machine's display flashes that it has [stored_matter] unit\s of biomass left.")) + to_chat(user, "The machine's display flashes that it has [stored_matter] unit\s of biomass left.") /obj/item/stack/biomass name = "biomass cubes" @@ -161,7 +161,7 @@ GLOBAL_LIST_INIT(biomass_unlocks, list()) . = ..() if(istype(target, /obj/machinery/biomass_recycler)) var/obj/machinery/biomass_recycler/recycler = target - to_chat(user, span_notice("You install [src] into [recycler].")) + to_chat(user, "You install [src] into [recycler].") playsound(user, 'sound/machines/click.ogg', 30, TRUE) for(var/print_type in printable_types) diff --git a/code/game/machinery/xenobio/extract_requestor.dm b/code/game/machinery/xenobio/extract_requestor.dm index faed4fff0690a..62437b2ea0aad 100644 --- a/code/game/machinery/xenobio/extract_requestor.dm +++ b/code/game/machinery/xenobio/extract_requestor.dm @@ -32,11 +32,11 @@ /obj/machinery/slime_extract_requestor/attack_hand(mob/living/user, list/modifiers) . = ..() if(!console) - to_chat(user, span_warning("[src] does not have a console linked to it!")) + to_chat(user, "[src] does not have a console linked to it!") return var/obj/item/card/id/card = user.get_idcard(TRUE) if(!card) - to_chat(user, span_warning("Unable to locate an ID card!")) + to_chat(user, "Unable to locate an ID card!") return if(check_in_requests(card)) @@ -55,7 +55,7 @@ return var/obj/item/multitool/multitool = tool multitool.buffer = src - to_chat(user, span_notice("You save the data in the [multitool.name]'s buffer.")) + to_chat(user, "You save the data in the [multitool.name]'s buffer.") return TOOL_ACT_TOOLTYPE_SUCCESS /obj/machinery/slime_extract_requestor/proc/check_in_requests(obj/item/card/id/card) @@ -138,10 +138,10 @@ var/ready_for_pickup = FALSE /datum/extract_request_data/proc/on_creation() - RegisterSignal(host_card, COMSIG_QDELETING, PROC_REF(end_request_qdeleted)) + RegisterSignal(host_card, COMSIG_PARENT_QDELETING, PROC_REF(end_request_qdeleted)) /datum/extract_request_data/Destroy(force, ...) - UnregisterSignal(host_card, COMSIG_QDELETING) + UnregisterSignal(host_card, COMSIG_PARENT_QDELETING) host_card = null linked_console = null QDEL_LIST(radial_data) diff --git a/code/game/machinery/xenobio/ooze_compressor/ooze_compressor.dm b/code/game/machinery/xenobio/ooze_compressor/ooze_compressor.dm index 5ed9b18e56d8b..b0d149bc09a83 100644 --- a/code/game/machinery/xenobio/ooze_compressor/ooze_compressor.dm +++ b/code/game/machinery/xenobio/ooze_compressor/ooze_compressor.dm @@ -86,7 +86,7 @@ /obj/machinery/plumbing/ooze_compressor/create_reagents(max_vol, flags) . = ..() RegisterSignals(reagents, list(COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, COMSIG_REAGENTS_REACTED), PROC_REF(on_reagent_change)) - RegisterSignal(reagents, COMSIG_QDELETING, PROC_REF(on_reagents_del)) + RegisterSignal(reagents, COMSIG_PARENT_QDELETING, PROC_REF(on_reagents_del)) /obj/machinery/plumbing/ooze_compressor/update_icon_state() . = ..() @@ -102,7 +102,7 @@ if(listed_reagent.type != reagent) continue reagent_volume = listed_reagent.volume - . += span_notice("[reagent_volume] out of [current_recipe.required_oozes[reagent]] units of [initial(reagent.name)].") + . += "[reagent_volume] out of [current_recipe.required_oozes[reagent]] units of [initial(reagent.name)]." reagent_volume = 0 /obj/machinery/plumbing/ooze_compressor/update_overlays() @@ -144,7 +144,7 @@ /// Handles properly detaching signal hooks. /obj/machinery/plumbing/ooze_compressor/proc/on_reagents_del(datum/reagents/reagents) SIGNAL_HANDLER - UnregisterSignal(reagents, list(COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, COMSIG_REAGENTS_REACTED, COMSIG_QDELETING)) + UnregisterSignal(reagents, list(COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, COMSIG_REAGENTS_REACTED, COMSIG_PARENT_QDELETING)) return NONE /// Handles stopping the emptying process when the chamber empties. @@ -218,7 +218,7 @@ if(anchored && can_interact(user)) repeat_recipe = !repeat_recipe balloon_alert_to_viewers("[repeat_recipe ? "enabled" : "disabled"] repeating") - visible_message(span_notice("[user] presses a button turning the repeat recipe system [repeat_recipe ? span_green("on") : span_red("off")]")) + visible_message("[user] presses a button turning the repeat recipe system [repeat_recipe ? span_green("on") : span_red("off")]") return ..() /obj/machinery/plumbing/ooze_compressor/proc/change_recipe(mob/user, cross_breed = FALSE) diff --git a/code/game/machinery/xenobio/ooze_sucker.dm b/code/game/machinery/xenobio/ooze_sucker.dm index 204b2e61d097f..656e7ea3b23dc 100644 --- a/code/game/machinery/xenobio/ooze_sucker.dm +++ b/code/game/machinery/xenobio/ooze_sucker.dm @@ -71,9 +71,9 @@ GLOBAL_LIST_EMPTY_TYPED(ooze_suckers, /obj/machinery/plumbing/ooze_sucker) /obj/machinery/plumbing/ooze_sucker/examine(mob/user) . = ..() - . += span_notice("It's currently turned [turned_on ? "ON" : "OFF"]. Right-click to toggle.") + . += "It's currently turned [turned_on ? "ON" : "OFF"]. Right-click to toggle." if(length(upgrade_disks)) - . += span_notice("Ctrl-click to remove an installed upgrade.") + . += "Ctrl-click to remove an installed upgrade." for(var/obj/item/disk/sucker_upgrade/upgrade as anything in upgrade_disks) . += span_notice(upgrade.notice) @@ -109,7 +109,7 @@ GLOBAL_LIST_EMPTY_TYPED(ooze_suckers, /obj/machinery/plumbing/ooze_sucker) add_upgrade(upgrade) - to_chat(user, span_notice("You install a [upgrade.upgrade_type] upgrade into [src].")) + to_chat(user, "You install a [upgrade.upgrade_type] upgrade into [src].") playsound(user, 'sound/machines/click.ogg', 30, TRUE) /obj/machinery/plumbing/ooze_sucker/proc/add_upgrade(obj/item/disk/sucker_upgrade/upgrade) @@ -135,12 +135,12 @@ GLOBAL_LIST_EMPTY_TYPED(ooze_suckers, /obj/machinery/plumbing/ooze_sucker) /obj/machinery/plumbing/ooze_sucker/create_reagents(max_vol, flags) . = ..() RegisterSignals(reagents, list(COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, COMSIG_REAGENTS_REACTED), PROC_REF(on_reagent_change)) - RegisterSignal(reagents, COMSIG_QDELETING, PROC_REF(on_reagents_del)) + RegisterSignal(reagents, COMSIG_PARENT_QDELETING, PROC_REF(on_reagents_del)) /// Handles properly detaching signal hooks. /obj/machinery/plumbing/ooze_sucker/proc/on_reagents_del(datum/reagents/reagents) SIGNAL_HANDLER - UnregisterSignal(reagents, list(COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, COMSIG_REAGENTS_REACTED, COMSIG_QDELETING)) + UnregisterSignal(reagents, list(COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, COMSIG_REAGENTS_REACTED, COMSIG_PARENT_QDELETING)) return NONE /// Handles ensuring power usage becomes idle when reagents are full. @@ -250,7 +250,7 @@ GLOBAL_LIST_EMPTY_TYPED(ooze_suckers, /obj/machinery/plumbing/ooze_sucker) remove_upgrade(upgrade_disk) user.put_in_hands(upgrade_disk) - to_chat(user, span_notice("You remove \the [upgrade] upgrade from [src].")) + to_chat(user, "You remove \the [upgrade] upgrade from [src].") playsound(user, 'sound/machines/click.ogg', 30, TRUE) /obj/machinery/plumbing/ooze_sucker/attack_hand_secondary(mob/user, list/modifiers) diff --git a/code/game/machinery/xenobio/slime_market.dm b/code/game/machinery/xenobio/slime_market.dm index 57cd96c8397b4..65929ae915263 100644 --- a/code/game/machinery/xenobio/slime_market.dm +++ b/code/game/machinery/xenobio/slime_market.dm @@ -14,7 +14,7 @@ /obj/machinery/slime_market_pad/examine(mob/user) . = ..() if(!panel_open) - . += span_notice("The panel is screwed in.") + . += "The panel is screwed in." /obj/machinery/slime_market_pad/update_overlays() . = ..() @@ -46,18 +46,18 @@ if(. || !can_interact(user)) return if(default_deconstruction_screwdriver(user, icon_state, icon_state, item)) - user.visible_message(span_notice("\The [user] [panel_open ? "opens" : "closes"] the hatch on \the [src]."), span_notice("You [panel_open ? "open" : "close"] the hatch on \the [src].")) + user.visible_message("\The [user] [panel_open ? "opens" : "closes"] the hatch on \the [src]."), span_notice("You [panel_open ? "open" : "close"] the hatch on \the [src].") update_appearance() return TRUE if(default_unfasten_wrench(user, item) || default_deconstruction_crowbar(item)) return TRUE if(QDELETED(console)) - to_chat(user, span_warning("[src] does not have a console linked to it!")) + to_chat(user, "[src] does not have a console linked to it!") return if(istype(item, /obj/item/slime_extract)) var/obj/item/slime_extract/extract = item if(extract.tier == 0) - to_chat(user, span_warning("[src] doesn't seem to accept this extract!")) + to_chat(user, "[src] doesn't seem to accept this extract!") return flick("[base_icon_state]_vend", src) sell_extract(extract) @@ -99,7 +99,7 @@ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || !can_interact(user)) return if(QDELETED(console)) - to_chat(user, span_warning("[src] does not have a console linked to it!")) + to_chat(user, "[src] does not have a console linked to it!") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN if(istype(item, /obj/item/storage/bag/xeno)) @@ -115,11 +115,11 @@ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN if(QDELETED(console.request_pad)) - to_chat(user, span_warning("[console] does not have a request_pad linked to it!")) + to_chat(user, "[console] does not have a request_pad linked to it!") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN if(!length(console.request_pad.current_requests)) - to_chat(user, span_warning("There are no current extract requests!")) + to_chat(user, "There are no current extract requests!") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN if(istype(item, /obj/item/slime_extract)) diff --git a/code/game/machinery/xenobio/slime_market_computer.dm b/code/game/machinery/xenobio/slime_market_computer.dm index a3394c7a01054..3b2b9cc94229b 100644 --- a/code/game/machinery/xenobio/slime_market_computer.dm +++ b/code/game/machinery/xenobio/slime_market_computer.dm @@ -61,7 +61,7 @@ GLOBAL_DATUM(default_slime_market, /obj/machinery/computer/slime_market) return pad.console = src request_pad = pad - to_chat(user, span_notice("You link the [pad] to the [src].")) + to_chat(user, "You link the [pad] to the [src].") . = ..() /obj/machinery/computer/slime_market/ui_assets(mob/user) diff --git a/code/game/machinery/xenobio/slime_pen_controller.dm b/code/game/machinery/xenobio/slime_pen_controller.dm index 27b170683443b..38d30bc4c06e2 100644 --- a/code/game/machinery/xenobio/slime_pen_controller.dm +++ b/code/game/machinery/xenobio/slime_pen_controller.dm @@ -179,7 +179,7 @@ GLOBAL_LIST_EMPTY_TYPED(slime_pen_controllers, /obj/machinery/slime_pen_controll if(!QDELETED(linked_sucker)) linked_sucker.toggle_state() balloon_alert_to_viewers("[linked_sucker.turned_on ? "enabled" : "disabled"] ooze sucker") - visible_message(span_notice("[user] fiddles with the [src], [linked_sucker.turned_on ? "enabling" : "disabling"] the pens ooze sucker.")) + visible_message("[user] fiddles with the [src], [linked_sucker.turned_on ? "enabling" : "disabling"] the pens ooze sucker.") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN /obj/machinery/slime_pen_controller/multitool_act(mob/living/user, obj/item/multitool/multitool) @@ -189,14 +189,14 @@ GLOBAL_LIST_EMPTY_TYPED(slime_pen_controllers, /obj/machinery/slime_pen_controll if(!istype(pad) || !pad.connected_data) return if(linked_data) - UnregisterSignal(linked_data, COMSIG_QDELETING) + UnregisterSignal(linked_data, COMSIG_PARENT_QDELETING) linked_data = pad.connected_data - RegisterSignal(linked_data, COMSIG_QDELETING, PROC_REF(clear_data)) + RegisterSignal(linked_data, COMSIG_PARENT_QDELETING, PROC_REF(clear_data)) balloon_alert_to_viewers("linked pen") pad.balloon_alert_to_viewers("linked to controller") - to_chat(user, span_notice("You link the [pad] to the [src].")) + to_chat(user, "You link the [pad] to the [src].") return TRUE /obj/machinery/slime_pen_controller/proc/clear_data() - UnregisterSignal(linked_data, COMSIG_QDELETING) + UnregisterSignal(linked_data, COMSIG_PARENT_QDELETING) linked_data = null diff --git a/code/game/objects/effects/anomalies/anomaly_pyro.dm b/code/game/objects/effects/anomalies/anomaly_pyro.dm index cfba2889f241d..80cfacd4bd49c 100644 --- a/code/game/objects/effects/anomalies/anomaly_pyro.dm +++ b/code/game/objects/effects/anomalies/anomaly_pyro.dm @@ -32,6 +32,5 @@ ADD_TRAIT(pyro, TRAIT_SLIME_RABID, "pyro") pyro.maximum_survivable_temperature = INFINITY pyro.apply_temperature_requirements() - pyro.rabid = TRUE pyro.flavor_text = FLAVOR_TEXT_EVIL pyro.set_playable(ROLE_PYRO_SLIME) diff --git a/code/game/objects/items/mutation_syringe.dm b/code/game/objects/items/mutation_syringe.dm index 2138446e39a64..a452734f147c0 100644 --- a/code/game/objects/items/mutation_syringe.dm +++ b/code/game/objects/items/mutation_syringe.dm @@ -15,15 +15,15 @@ /obj/item/slime_mutation_syringe/examine(mob/user) . = ..() if(uses) - . += span_notice("It has [uses] uses left.") + . += "It has [uses] uses left." else - . += span_warning("It has been completely used up.") + . += "It has been completely used up." /obj/item/slime_mutation_syringe/afterattack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() if(!uses) user.balloon_alert(user, "used up") - to_chat(user, span_warning("[src] has been completely used up!")) + to_chat(user, "[src] has been completely used up!") return if(!istype(target, /mob/living/basic/slime)) return @@ -36,7 +36,7 @@ uses-- update_icon_state() user.balloon_alert_to_viewers("injected mutator") - to_chat(user, span_notice("You inject [target] with [src].")) + to_chat(user, "You inject [target] with [src].") on_inject(slime) if(uses <= 0) ADD_TRAIT(src, TRAIT_TRASH_ITEM, INNATE_TRAIT) diff --git a/code/game/objects/items/vacuum_pack.dm b/code/game/objects/items/vacuum_pack.dm index 7915406f27030..129336f91bd3d 100644 --- a/code/game/objects/items/vacuum_pack.dm +++ b/code/game/objects/items/vacuum_pack.dm @@ -69,7 +69,7 @@ /obj/item/vacuum_pack/multitool_act(mob/living/user, obj/item/tool) . = ..() modified = !modified - to_chat(user, span_notice("You turn the safety switch on [src] [modified ? "off" : "on"].")) + to_chat(user, "You turn the safety switch on [src] [modified ? "off" : "on"].") /obj/item/vacuum_pack/process(delta_time) if(!(VACUUM_PACK_UPGRADE_HEALING in upgrades)) @@ -81,10 +81,10 @@ /obj/item/vacuum_pack/examine(mob/user) . = ..() if(LAZYLEN(stored)) - . += span_notice("It has [LAZYLEN(stored)] creatures stored in it.") + . += "It has [LAZYLEN(stored)] creatures stored in it." if(LAZYLEN(upgrades)) for(var/upgrade in upgrades) - . += span_notice("It has \a [upgrade] upgrade installed.") + . += "It has \a [upgrade] upgrade installed." /obj/item/vacuum_pack/attackby(obj/item/item, mob/living/user, params) if(item == nozzle) @@ -98,16 +98,16 @@ var/obj/item/disk/vacuum_upgrade/upgrade = item if(illegal) - to_chat(user, span_warning("[src] has no slot to insert [upgrade] into!")) + to_chat(user, "[src] has no slot to insert [upgrade] into!") return if(upgrade.upgrade_type in upgrades) - to_chat(user, span_warning("[src] already has a [upgrade.upgrade_type] upgrade!")) + to_chat(user, "[src] already has a [upgrade.upgrade_type] upgrade!") return upgrades += upgrade.upgrade_type upgrade.on_upgrade(src) - to_chat(user, span_notice("You install a [upgrade.upgrade_type] upgrade into [src].")) + to_chat(user, "You install a [upgrade.upgrade_type] upgrade into [src].") playsound(user, 'sound/machines/click.ogg', 30, TRUE) qdel(upgrade) return @@ -122,7 +122,7 @@ return if(user.get_item_by_slot(user.getBackSlot()) != src && check_backpack) - to_chat(user, span_warning("[src] must be worn properly to use!")) + to_chat(user, "[src] must be worn properly to use!") return if(user.incapacitated()) @@ -133,7 +133,7 @@ if(nozzle in src) if(!user.put_in_hands(nozzle)) - to_chat(user, span_warning("You need a free hand to hold [nozzle]!")) + to_chat(user, "You need a free hand to hold [nozzle]!") return else playsound(user, 'sound/mecha/mechmove03.ogg', 75, TRUE) @@ -197,14 +197,14 @@ /obj/item/vacuum_nozzle/examine(mob/user) . = ..() if (!pack.illegal) - . += span_notice("Activate to change firing modes. Currently set to [pack.give_choice ? "selective" : "indiscriminate"].") + . += "Activate to change firing modes. Currently set to [pack.give_choice ? "selective" : "indiscriminate"]." else - . += span_notice("It's selection mechanism is hotwired to fire indiscriminately.") + . += "It's selection mechanism is hotwired to fire indiscriminately." /obj/item/vacuum_nozzle/doMove(atom/destination) if(destination && (destination != pack.loc || !ismob(destination))) if (loc != pack) - to_chat(pack.loc, span_notice("[src] snaps back onto [pack].")) + to_chat(pack.loc, "[src] snaps back onto [pack].") destination = pack . = ..() @@ -214,8 +214,8 @@ pack.give_choice = !pack.give_choice var/mode_desc = pack.give_choice ? "selectively" : "indiscriminately" visible_message( - span_notice("[user] switches the [pack] to fire [mode_desc]."), - span_notice("You switch the [pack] to fire [mode_desc]."), + "[user] switches the [pack] to fire [mode_desc].", + "You switch the [pack] to fire [mode_desc].", span_hear("You hear a click.") ) @@ -228,11 +228,11 @@ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN if(!(VACUUM_PACK_UPGRADE_BIOMASS in pack.upgrades)) - to_chat(user, span_warning("[pack] does not posess a required upgrade!")) + to_chat(user, "[pack] does not posess a required upgrade!") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN if(!pack.linked) - to_chat(user, span_warning("[pack] is not linked to a biomass recycler!")) + to_chat(user, "[pack] is not linked to a biomass recycler!") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN var/list/items = list() @@ -256,7 +256,7 @@ var/spawn_type = item_names[pick] if(pack.linked.stored_matter < pack.linked.vacuum_printable_types[spawn_type]) - to_chat(user, span_warning("[pack.linked] does not have enough stored biomass for that! It currently has [pack.linked.stored_matter] out of [pack.linked.vacuum_printable_types[spawn_type]] unit\s required.")) + to_chat(user, "[pack.linked] does not have enough stored biomass for that! It currently has [pack.linked.stored_matter] out of [pack.linked.vacuum_printable_types[spawn_type]] unit\s required.") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN var/atom/movable/spawned = new spawn_type(user.loc) @@ -273,7 +273,7 @@ spawned.pass_flags |= PASSMOB spawned.throw_at(target, min(get_dist(user, target), (pack.illegal ? 5 : 11)), 1, user, gentle = TRUE) //Gentle so eggs have 50% instead of 12.5% to spawn a chick - user.visible_message(span_warning("[user] shoots [spawned] out their [src]!"), span_notice("You fabricate and shoot [spawned] out of your [src].")) + user.visible_message("[user] shoots [spawned] out their [src]!"), "You fabricate and shoot [spawned] out of your [src]." return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN /obj/item/vacuum_nozzle/afterattack(atom/movable/target, mob/user, proximity, params) @@ -287,10 +287,10 @@ if(istype(target, /obj/machinery/biomass_recycler) && target.Adjacent(user)) if(!(VACUUM_PACK_UPGRADE_BIOMASS in pack.upgrades)) - to_chat(user, span_warning("[pack] does not posess a required upgrade!")) + to_chat(user, "[pack] does not posess a required upgrade!") return pack.linked = target - to_chat(user, span_notice("You link [pack] to [target].")) + to_chat(user, "You link [pack] to [target].") return if(pack.linked) @@ -307,15 +307,15 @@ if(can_recycle && (!is_type_in_typecache(target, pack.storable_objects) || target_stat != CONSCIOUS)) if(!(VACUUM_PACK_UPGRADE_BIOMASS in pack.upgrades)) - to_chat(user, span_warning("[pack] does not posess a required upgrade!")) + to_chat(user, "[pack] does not posess a required upgrade!") return if(!pack.linked) - to_chat(user, span_warning("[pack] is not linked to a biomass recycler!")) + to_chat(user, "[pack] is not linked to a biomass recycler!") return if(target_stat == CONSCIOUS) - to_chat(user, span_warning("[target] is struggling far too much for you to suck it in!")) + to_chat(user, "[target] is struggling far too much for you to suck it in!") return if(isliving(target)) @@ -333,7 +333,7 @@ animation_matrix.Translate((user.x - target.x) * 32, (user.y - target.y) * 32) animate(target, alpha = 0, time = 8, easing = QUAD_EASING|EASE_IN, transform = animation_matrix, flags = ANIMATION_PARALLEL) sleep(8) - user.visible_message(span_warning("[user] sucks [target] into their [pack]!"), span_notice("You successfully suck [target] into your [src] and recycle it.")) + user.visible_message("[user] sucks [target] into their [pack]!"), "You successfully suck [target] into your [src] and recycle it." qdel(target) playsound(user, 'sound/machines/juicer.ogg', 50, TRUE) pack.linked.use_power(500) @@ -342,25 +342,25 @@ if(is_type_in_typecache(target, pack.storable_objects)) if(get_dist(user, target) > pack.range) - to_chat(user, span_warning("[target] is too far away!")) + to_chat(user, "[target] is too far away!") return if(!(target in view(user, pack.range))) - to_chat(user, span_warning("You can't reach [target]!")) + to_chat(user, "You can't reach [target]!") return if(target.anchored || target.move_resist > MOVE_FORCE_STRONG) - to_chat(user, span_warning("You can't manage to suck [target] in!")) + to_chat(user, "You can't manage to suck [target] in!") return if(isslime(target)) var/mob/living/basic/slime/slime = target if(HAS_TRAIT(slime, TRAIT_SLIME_RABID) && !pack.illegal && !(VACUUM_PACK_UPGRADE_PACIFY in pack.upgrades)) - to_chat(user, span_warning("[slime] is wiggling far too much for you to suck it in!")) + to_chat(user, "[slime] is wiggling far too much for you to suck it in!") return if(LAZYLEN(pack.stored) >= pack.capacity) - to_chat(user, span_warning("[pack] is already filled to the brim!")) + to_chat(user, "[pack] is already filled to the brim!") return if(!do_after(user, pack.speed, target, timed_action_flags = IGNORE_TARGET_LOC_CHANGE|IGNORE_USER_LOC_CHANGE, extra_checks = CALLBACK(src, .proc/suck_checks, target, user))) @@ -373,7 +373,7 @@ return if(LAZYLEN(pack.stored) == 0) - to_chat(user, span_warning("[pack] is empty!")) + to_chat(user, "[pack] is empty!") return var/mob/living/spewed @@ -427,7 +427,7 @@ pack.stored -= spewed - user.visible_message(span_warning("[user] shoots [spewed] out their [src]!"), span_notice("You shoot [spewed] out of your [src].")) + user.visible_message("[user] shoots [spewed] out their [src]!"), "You shoot [spewed] out of your [src]." /obj/item/vacuum_nozzle/proc/suck_checks(atom/movable/target, mob/user) if(get_dist(user, target) > pack.range) @@ -468,7 +468,7 @@ ADD_TRAIT(slime, TRAIT_SLIME_STASIS, "vacuum_pack_stasis") SEND_SIGNAL(target, COMSIG_ATOM_SUCKED) if(!silent) - user.visible_message(span_warning("[user] sucks [target] into their [pack]!"), span_notice("You successfully suck [target] into your [src].")) + user.visible_message("[user] sucks [target] into their [pack]!"), "You successfully suck [target] into your [src]." var/mob/living/basic/slime/slime = target slime.slime_flags |= STORED_SLIME if(slime.ai_controller) @@ -476,7 +476,7 @@ slime.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, null) /obj/item/vacuum_nozzle/proc/start_busting(mob/living/basic/revenant/revenant, mob/living/user) - revenant.visible_message(span_warning("[user] starts sucking [revenant] into their [src]!"), span_userdanger("You are being sucked into [user]'s [src]!")) + revenant.visible_message("[user] starts sucking [revenant] into their [src]!"), span_userdanger("You are being sucked into [user]'s [src]!") pack.ghost_busting = revenant pack.ghost_buster = user pack.busting_beam = user.Beam(revenant, icon_state="drain_life") diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 9ca83316a5966..37ad39befa6f3 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -268,9 +268,10 @@ var/mobcolour = "grey" icon = 'icons/mob/slimes.dmi' icon_state = "grey baby slime" //sets the icon in the map editor + var/slime_species = /datum/slime_color/grey /obj/effect/mob_spawn/slime/equip(mob/living/basic/slime/S) - S.colour = mobcolour + S.change_color(slime_species) /obj/effect/mob_spawn/facehugger/create(ckey) //Creates a squashed facehugger var/obj/item/clothing/mask/facehugger/O = new(src.loc) //variable O is a new facehugger at the location of the landmark diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index 0760e9e5d0ba0..60a25d7016d9e 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -1803,7 +1803,7 @@ /obj/item/reagent_containers/medspray/sterilizine, /obj/item/rollerbed) crate_name = "surgical supplies crate" - + /datum/supply_pack/medical/implants name = "Surplus Implants Crate" desc = "Do you want implants, but those R&D folks hasn't learnt how to do their job? Just get started with this crate containing several of our dusty surplus implants. (Surgical tools not included)" @@ -2045,10 +2045,7 @@ access_budget = ACCESS_XENOBIOLOGY contains = list(/obj/item/slime_extract/grey, /obj/item/slime_extract/grey, - /obj/item/reagent_containers/syringe/plasma, - /obj/item/circuitboard/computer/xenobiology, - /obj/item/circuitboard/machine/monkey_recycler, - /obj/item/circuitboard/machine/processor/slime) + /obj/item/reagent_containers/syringe/plasma) crate_name = "xenobiology starter crate" crate_type = /obj/structure/closet/crate/secure/science diff --git a/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm b/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm deleted file mode 100644 index 79ef2e30f126c..0000000000000 --- a/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm +++ /dev/null @@ -1,103 +0,0 @@ -GLOBAL_LIST_EMPTY(monkey_recyclers) - -/obj/machinery/monkey_recycler - name = "monkey recycler" - desc = "A machine used for recycling dead monkeys into monkey cubes." - icon = 'icons/obj/kitchen.dmi' - icon_state = "grinder" - layer = BELOW_OBJ_LAYER - density = TRUE - use_power = IDLE_POWER_USE - idle_power_usage = 5 - active_power_usage = 50 - circuit = /obj/item/circuitboard/machine/monkey_recycler - var/stored_matter = 0 - var/cube_production = 0.2 - var/list/connected = list() //Keeps track of connected xenobio consoles, for deletion in /Destroy() - -/obj/machinery/monkey_recycler/Initialize(mapload) - . = ..() - if (mapload) - GLOB.monkey_recyclers += src - -/obj/machinery/monkey_recycler/Destroy() - if(src in GLOB.monkey_recyclers) - GLOB.monkey_recyclers -= src - for(var/obj/machinery/computer/camera_advanced/xenobio/console in connected) - console.connected_recycler = null - return ..() - -/obj/machinery/monkey_recycler/RefreshParts() //Ranges from 0.2 to 0.8 per monkey recycled - cube_production = 0 - for(var/obj/item/stock_parts/manipulator/B in component_parts) - cube_production += B.rating * 0.1 - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - cube_production += M.rating * 0.1 - -/obj/machinery/monkey_recycler/examine(mob/user) - . = ..() - if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Producing [cube_production] cubes for every monkey inserted." - -/obj/machinery/monkey_recycler/attackby(obj/item/O, mob/user, params) - if(default_deconstruction_screwdriver(user, "grinder_open", "grinder", O)) - return - - if(default_pry_open(O)) - return - - if(default_unfasten_wrench(user, O)) - power_change() - return - - if(default_deconstruction_crowbar(O)) - return - - if(machine_stat) //NOPOWER etc - return - else - return ..() - -/obj/machinery/monkey_recycler/MouseDrop_T(mob/living/target, mob/living/user) - if(!istype(target)) - return - if(ismonkey(target)) - stuff_monkey_in(target, user) - -/obj/machinery/monkey_recycler/proc/stuff_monkey_in(mob/living/carbon/monkey/target, mob/living/user) - if(!istype(target)) - return - if(target.stat == CONSCIOUS) - to_chat(user, "The monkey is struggling far too much to put it in the recycler.") - return - if(target.buckled || target.has_buckled_mobs()) - to_chat(user, "The monkey is attached to something.") - return - qdel(target) - to_chat(user, "You stuff the monkey into the machine.") - playsound(src.loc, 'sound/machines/juicer.ogg', 50, 1) - var/offset = prob(50) ? -2 : 2 - animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 200) //start shaking - use_power(500) - stored_matter += cube_production - addtimer(VARSET_CALLBACK(src, pixel_x, base_pixel_x)) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), user, "The machine now has [stored_matter] monkey\s worth of material stored.")) - -/obj/machinery/monkey_recycler/interact(mob/user) - if(stored_matter >= 1) - to_chat(user, "The machine hisses loudly as it condenses the ground monkey meat. After a moment, it dispenses a brand new monkey cube.") - playsound(src.loc, 'sound/machines/hiss.ogg', 50, 1) - for(var/i in 1 to FLOOR(stored_matter, 1)) - new /obj/item/food/monkeycube(src.loc) - stored_matter-- - to_chat(user, "The machine's display flashes that it has [stored_matter] monkeys worth of material left.") - else - to_chat(user, "The machine needs at least 1 monkey worth of material to produce a monkey cube. It currently has [stored_matter].") - -REGISTER_BUFFER_HANDLER(/obj/machinery/monkey_recycler) - -DEFINE_BUFFER_HANDLER(/obj/machinery/monkey_recycler) - if (TRY_STORE_IN_BUFFER(buffer_parent, src)) - to_chat(user, "You log [src] in the [buffer_parent]'s buffer.") - return COMPONENT_BUFFER_RECIEVED - return NONE diff --git a/code/modules/food_and_drinks/kitchen_machinery/processor.dm b/code/modules/food_and_drinks/kitchen_machinery/processor.dm index 16a5be432fe97..fdcd24c6dba06 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/processor.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/processor.dm @@ -148,56 +148,3 @@ /obj/machinery/processor/container_resist(mob/living/user) user.forceMove(drop_location()) user.visible_message("[user] crawls free of the processor!") - -/obj/machinery/processor/slime - name = "slime processor" - desc = "An industrial grinder with a sticker saying appropriated for science department. Keep hands clear of intake area while operating." - circuit = /obj/item/circuitboard/machine/processor/slime - var/sbacklogged = FALSE - -/obj/machinery/processor/slime/Initialize(mapload) - . = ..() - proximity_monitor = new(src, 1) - -/obj/machinery/processor/slime/adjust_item_drop_location(atom/movable/AM) - var/static/list/slimecores = subtypesof(/obj/item/slime_extract) - var/i = 0 - if(!(i = slimecores.Find(AM.type))) // If the item is not found - return - if (i <= 16) // If in the first 12 slots - AM.pixel_x = AM.base_pixel_x - 12 + ((i%4)*8) - AM.pixel_y = AM.base_pixel_y - 12 + (round(i/4)*8) - return i - var/ii = i - 16 - AM.pixel_x = AM.base_pixel_x - 8 + ((ii%3)*8) - AM.pixel_y = AM.base_pixel_y - 8 + (round(ii/3)*8) - return i - -/obj/machinery/processor/slime/interact(mob/user) - . = ..() - if(sbacklogged) - for(var/mob/living/basic/slime/AM in ohearers(1,src)) //fallback in case slimes got placed while processor was active triggers only after processing!!!! - if(AM.stat == DEAD) - visible_message("[AM] is sucked into [src].") - AM.forceMove(src) - sbacklogged = FALSE - -/obj/machinery/processor/slime/HasProximity(mob/AM) - if(!sbacklogged && istype(AM, /mob/living/basic/slime) && AM.stat == DEAD) - if(processing) - sbacklogged = TRUE - else - visible_message("[AM] is sucked into [src].") - AM.forceMove(src) - -/obj/machinery/processor/slime/process_food(datum/food_processor_process/recipe, atom/movable/what) - var/mob/living/basic/slime/S = what - if (istype(S)) - var/C = S.cores - for(var/i in 1 to (C+rating_amount-1)) - var/obj/item/slime_extract/item = new S.coretype(drop_location()) - if(S.transformeffects & SLIME_EFFECT_GOLD) - item.sparkly = TRUE - adjust_item_drop_location(item) - SSblackbox.record_feedback("tally", "slime_core_harvested", 1, S.colour) - ..() diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index 1b5a23f470210..186cf1d85be8f 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -422,12 +422,10 @@ /obj/machinery/smartfridge/extract/accept_check(obj/item/O) if(istype(O, /obj/item/slime_extract)) return TRUE - if(istype(O, /obj/item/slime_scanner)) - return TRUE return FALSE /obj/machinery/smartfridge/extract/preloaded - initial_contents = list(/obj/item/slime_scanner = 2) + initial_contents = list(/obj/item/slime_extract/grey = 2) // ------------------------- // Organ Surgery Smartfridge diff --git a/code/modules/food_and_drinks/recipes/processor_recipes.dm b/code/modules/food_and_drinks/recipes/processor_recipes.dm index a58e72a21f6b6..d65d2128679dd 100644 --- a/code/modules/food_and_drinks/recipes/processor_recipes.dm +++ b/code/modules/food_and_drinks/recipes/processor_recipes.dm @@ -93,11 +93,6 @@ input = /obj/item/food/grown/parsnip output = /obj/item/food/roastparsnip -/datum/food_processor_process/mob/slime - input = /mob/living/basic/slime - output = null - required_machine = /obj/machinery/processor/slime - /datum/food_processor_process/fish input = /obj/item/fish output = /obj/item/food/fishmeat diff --git a/code/modules/mob/living/basic/basic.dm b/code/modules/mob/living/basic/basic.dm index e853c16df621a..9ca3f4777af77 100644 --- a/code/modules/mob/living/basic/basic.dm +++ b/code/modules/mob/living/basic/basic.dm @@ -85,7 +85,19 @@ ///Sentience type, for slime potions. SHOULD BE AN ELEMENT BUT I DONT CARE ABOUT IT FOR NOW var/sentience_type = SENTIENCE_ORGANIC - + ///Leaving something at 0 means it's off - has no maximum. + var/list/habitable_atmos = list("min_oxy" = 5, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) + ///This damage is taken when atmos doesn't fit all the requirements above. Set to 0 to avoid adding the atmos_requirements element. + var/unsuitable_atmos_damage = 1 + + ///Minimal body temperature without receiving damage + var/minimum_survivable_temperature = NPC_DEFAULT_MIN_TEMP + ///Maximal body temperature without receiving damage + var/maximum_survivable_temperature = NPC_DEFAULT_MAX_TEMP + ///This damage is taken when the body temp is too cold. Set both this and unsuitable_heat_damage to 0 to avoid adding the basic_body_temp_sensitive element. + var/unsuitable_cold_damage = 1 + ///This damage is taken when the body temp is too hot. Set both this and unsuitable_cold_damage to 0 to avoid adding the basic_body_temp_sensitive element. + var/unsuitable_heat_damage = 1 /mob/living/basic/Initialize(mapload) . = ..() diff --git a/code/modules/mob/living/carbon/status_procs.dm b/code/modules/mob/living/carbon/status_procs.dm index d32db67cf7407..ae540b3b78506 100644 --- a/code/modules/mob/living/carbon/status_procs.dm +++ b/code/modules/mob/living/carbon/status_procs.dm @@ -16,7 +16,6 @@ ADD_TRAIT(src, TRAIT_INCAPACITATED, STAMINA) ADD_TRAIT(src, TRAIT_IMMOBILIZED, STAMINA) ADD_TRAIT(src, TRAIT_FLOORED, STAMINA) - SEND_SIGNAL(src, COMSIG_LIVING_STAMINA_STUN) /mob/living/carbon/adjust_drugginess(amount) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index b8c67e9328cfe..79d64000761d3 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -26,6 +26,12 @@ ///When the mob enters hard critical state and is fully incapacitated. var/hardcrit_threshold = HEALTH_THRESHOLD_FULLCRIT + //Damage dealing vars! These are meaningless outside of specific instances where it's checked and defined. + /// Lower bound of damage done by unarmed melee attacks. Mob code is a mess, only works where this is checked for. + var/melee_damage_lower = 0 + /// Upper bound of damage done by unarmed melee attacks. Please ensure you check the xyz_defenses.dm for the mobs in question to see if it uses this or hardcoded values. + var/melee_damage_upper = 0 + /// Generic bitflags for boolean conditions at the [/mob/living] level. Keep this for inherent traits of living types, instead of runtime-changeable ones. var/living_flags = NONE diff --git a/code/modules/mob/living/ventcrawling.dm b/code/modules/mob/living/ventcrawling.dm index 2a08405c20c21..c17fa5474b98a 100644 --- a/code/modules/mob/living/ventcrawling.dm +++ b/code/modules/mob/living/ventcrawling.dm @@ -74,13 +74,6 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, typecacheof(list( else to_chat(src, "This ventilation duct is not connected to anything!") -/mob/living/basic/slime/handle_ventcrawl(atom/A) - if(buckled) - to_chat(src, "I can't vent crawl while feeding...") - return - ..() - - /mob/living/proc/add_ventcrawl(obj/machinery/atmospherics/starting_machine) if(!istype(starting_machine) || !starting_machine.can_see_pipes()) return diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 09ced6b2d584a..c5a147df98983 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -921,7 +921,7 @@ * Turns you to face the other mob too */ /mob/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE) - if(M.buckled && !force) + if(M.buckled && (LAZYLEN(buckled_mobs) >= max_buckled_mobs) && !force) return FALSE var/turf/T = get_turf(src) if(M.loc != T) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index be7415f5cf929..8f529247e477c 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -593,7 +593,6 @@ var/list/babies = list() for(var/i in 1 to number) var/mob/living/basic/slime/M = new/mob/living/basic/slime(loc) - M.set_nutrition(round(nutrition/number)) step_away(M,src) babies += M new_slime = pick(babies) diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 06d87c838a6e4..47240db93061d 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -368,7 +368,7 @@ name = "charged slime core" desc = "A yellow slime core infused with plasma, it crackles with power." icon = 'icons/mob/slimes.dmi' - icon_state = "yellow slime extract" + icon_state = "yellow_slime_extract" custom_materials = null rating = 5 //self-recharge makes these desirable self_recharge = TRUE // Infused slime cores self-recharge, over time diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 8124ddab07f02..290302e24305d 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -2299,133 +2299,3 @@ Basically, we fill the time between now and 2s from now with hands based off the M.adjustOxyLoss(-5*REM, 0) . = 1 M.losebreath = 0 - -/datum/reagent/slime_ooze - name = "Generic Slime Ooze" - evaporation_rate = 0.01 - opacity = 225 - slippery = FALSE - var/obj/item/slime_extract/extract_path - -/datum/reagent/proc/add_to_member(obj/effect/abstract/liquid_turf/adder) - return - -/datum/reagent/proc/remove_from_member(obj/effect/abstract/liquid_turf/remover) - return - - -/datum/reagent/slime_ooze/grey - name = "Grey Slime Ooze" - color = COLOR_GRAY - extract_path = /obj/item/slime_extract/grey - -/datum/reagent/slime_ooze/blue - name = "Light Blue Slime Ooze" - color = COLOR_CARP_LIGHT_BLUE - extract_path = /obj/item/slime_extract/blue - -/datum/reagent/slime_ooze/darkblue - name = "Dark Blue Slime Ooze" - color = COLOR_BLUE - extract_path = /obj/item/slime_extract/darkblue - -/datum/reagent/slime_ooze/green - name = "Green Slime Ooze" - color = "#D6F264" - extract_path = /obj/item/slime_extract/green - -/datum/reagent/slime_ooze/metal - name = "Metal Slime Ooze" - color = "#6D758D" - extract_path = /obj/item/slime_extract/metal - -/datum/reagent/slime_ooze/purple - name = "Purple Slime Ooze" - color = "#BC4A9B" - extract_path = /obj/item/slime_extract/purple - -/datum/reagent/slime_ooze/orange - name = "Orange Slime Ooze" - color = "#FA6A0A" - extract_path = /obj/item/slime_extract/orange - -/datum/reagent/slime_ooze/pink - name = "Pink Slime Ooze" - color = "#F5A097" - extract_path = /obj/item/slime_extract/pink - -/datum/reagent/slime_ooze/darkpurple - name = "Dark Purple Slime Ooze" - color = "#793A80" - extract_path = /obj/item/slime_extract/darkpurple - -/datum/reagent/slime_ooze/red - name = "Red Slime Ooze" - color = "#B4202A" - extract_path = /obj/item/slime_extract/red - -/datum/reagent/slime_ooze/yellow - name = "Yellow Slime Ooze" - color = "#F9A31B" - extract_path = /obj/item/slime_extract/yellow - -/datum/reagent/slime_ooze/gold - name = "Gold Slime Ooze" - color = "#BB7547" - extract_path = /obj/item/slime_extract/gold - -/datum/reagent/slime_ooze/silver - name = "Silver Slime Ooze" - color = "#8B93AF" - extract_path = /obj/item/slime_extract/silver - -/datum/reagent/slime_ooze/lightpink - name = "Light Pink Slime Ooze" - color = "#E9B5A3" - extract_path = /obj/item/slime_extract/lightpink - -/datum/reagent/slime_ooze/black - name = "Black Slime Ooze" - color = "#333941" - extract_path = /obj/item/slime_extract/black - -/datum/reagent/slime_ooze/rainbow - name = "Rainbow Slime Ooze" - color = "#ffffff" - extract_path = /obj/item/slime_extract/rainbow - -/datum/reagent/slime_ooze/rainbow/add_to_member(obj/effect/abstract/liquid_turf/adder) - adder.rainbow_effect() - -/datum/reagent/slime_ooze/rainbow/remove_from_member(obj/effect/abstract/liquid_turf/remover) - remover.remove_rainbow_effect() - -/datum/reagent/slime_ooze/oil - name = "Oil Slime Ooze" - color = "#242234" - extract_path = /obj/item/slime_extract/oil - -/datum/reagent/slime_ooze/adamantine - name = "Adamantine Slime Ooze" - color = "#5DAF8D" - extract_path = /obj/item/slime_extract/adamantine - -/datum/reagent/slime_ooze/bluespace - name = "Bluespace Slime Ooze" - color = "#C0E4FD" - extract_path = /obj/item/slime_extract/bluespace - -/datum/reagent/slime_ooze/pyrite - name = "Pyrite Slime Ooze" - color = "#FFD541" - extract_path = /obj/item/slime_extract/pyrite - -/datum/reagent/slime_ooze/sepia - name = "Sepia Slime Ooze" - color = "#A08662" - extract_path = /obj/item/slime_extract/sepia - -/datum/reagent/slime_ooze/cerulean - name = "Cerulean Slime Ooze" - color = "#285CC4" - extract_path = /obj/item/slime_extract/cerulean diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm index f277d28c839e2..a3ad2ddaed3df 100644 --- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm +++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm @@ -24,7 +24,7 @@ required_other = TRUE /datum/chemical_reaction/slime/slimespawn/on_reaction(datum/reagents/holder) - var/mob/living/basic/slime/S = new(get_turf(holder.my_atom), "grey") + var/mob/living/basic/slime/S = new(get_turf(holder.my_atom), /datum/slime_color/grey) S.visible_message("Infused with plasma, the core begins to quiver and grow, and a new baby slime emerges from it!") ..() @@ -45,7 +45,7 @@ /datum/chemical_reaction/slime/slimemonkey/on_reaction(datum/reagents/holder) for(var/i in 1 to 3) - new /obj/item/food/monkeycube(get_turf(holder.my_atom)) + new /obj/item/stack/biomass(get_turf(holder.my_atom)) ..() //Green @@ -361,12 +361,12 @@ /datum/chemical_reaction/slime/slimebloodlust/on_reaction(datum/reagents/holder) for(var/mob/living/basic/slime/slime in viewers(get_turf(holder.my_atom))) - if(slime.docile) //Undoes docility, but doesn't make rabid. + if(slime.has_slime_trait(/datum/slime_trait/docility)) //Undoes docility, but doesn't make rabid. slime.visible_message("[slime] forgets its training, becoming wild once again!") - slime.docile = FALSE + slime.remove_trait(/datum/slime_trait/docility) slime.update_name() continue - slime.rabid = 1 + ADD_TRAIT(slime, TRAIT_SLIME_RABID, "bloodlust") slime.visible_message("The [slime] is driven into a frenzy!") ..() diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm index f05f5f0905c30..ae3377540931a 100644 --- a/code/modules/research/designs/autolathe_designs.dm +++ b/code/modules/research/designs/autolathe_designs.dm @@ -959,14 +959,6 @@ build_path = /obj/item/modular_computer/tablet category = list("initial","Misc") -/datum/design/slime_scanner - name = "Slime Scanner" - id = "slime_scanner" - build_type = AUTOLATHE - materials = list(/datum/material/iron = 300, /datum/material/glass = 200) - build_path = /obj/item/slime_scanner - category = list("initial", "Misc") - /datum/design/pet_carrier name = "Pet Carrier" id = "pet_carrier" diff --git a/code/modules/research/designs/comp_board_designs.dm b/code/modules/research/designs/comp_board_designs.dm index 06c9c53f85c4f..636e1a108aef1 100644 --- a/code/modules/research/designs/comp_board_designs.dm +++ b/code/modules/research/designs/comp_board_designs.dm @@ -46,14 +46,6 @@ category = list("Computer Boards") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE -/datum/design/board/xenobiocamera - name = "Computer Design (Xenobiology Console)" - desc = "Allows for the construction of circuit boards used to build xenobiology camera computers." - id = "xenobioconsole" - build_path = /obj/item/circuitboard/computer/xenobiology - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - /datum/design/board/aiupload name = "Computer Design (AI Upload)" desc = "Allows for the construction of circuit boards used to build an AI Upload Console." diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index b1bfcdfa293d8..9fc85ee15e9a2 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -386,14 +386,6 @@ category = list ("Misc. Machinery") departmental_flags = DEPARTMENTAL_FLAG_ALL -/datum/design/board/monkey_recycler - name = "Machine Design (Monkey Recycler Board)" - desc = "The circuit board for a monkey recycler." - id = "monkey_recycler" - build_path = /obj/item/circuitboard/machine/monkey_recycler - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_SERVICE - /datum/design/board/seed_extractor name = "Machine Design (Seed Extractor Board)" desc = "The circuit board for a seed extractor." diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index c09d4bc52451f..122fe0e4a62d7 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -289,10 +289,15 @@ "gibber", "griddle", "microwave", - "monkey_recycler", "processor", "reagentgrinder", "smartfridge", + "biomass_recycler", + "corral_corner", + "slime_extract_requestor", + "slime_market_pad", + "slime_market", + "slimevac", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -525,7 +530,6 @@ "bluespace_crystal", "dragnetbeacon", "telesci_gps", - "xenobioconsole", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -2265,7 +2269,7 @@ ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000, TECHWEB_POINT_TYPE_NANITES = 1000) export_price = 5000 - + /datum/techweb_node/nanite_cc id = "nanite_cc" tech_tier = 5 diff --git a/code/modules/research/xenobiology/crossbreeding/__corecross.dm b/code/modules/research/xenobiology/crossbreeding/__corecross.dm new file mode 100644 index 0000000000000..ab07cbb701015 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/__corecross.dm @@ -0,0 +1,195 @@ +////////////////////////////////////////////// +////////// SLIME CROSSBREEDS ////////// +////////////////////////////////////////////// +// A system of combining two extract types. // +// Performed by feeding a slime 10 of an // +// extract color. // +////////////////////////////////////////////// +/*==========================================*\ +To add a crossbreed: + The file name is automatically selected + by the crossbreeding effect, which uses + the format slimecross/[modifier]/[color]. + + If a crossbreed doesn't exist, don't + worry. If no file is found at that + location, it will simple display that + the crossbreed was too unstable. + + As a result, do not feel the need to + try to add all of the crossbred + effects at once, if you're here and + trying to make a new slime type. Just + get your slimetype in the codebase and + get around to the crossbreeds eventually! +\*==========================================*/ + +/obj/item/slimecross //The base type for crossbred extracts. Mostly here for posterity, and to set base case things. + name = "crossbred slime extract" + desc = "An extremely potent slime extract, formed through crossbreeding." + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + icon_state = "base" + var/colour = "null" + var/effect = "null" + var/effect_desc = "null" + force = 0 + w_class = WEIGHT_CLASS_TINY + throwforce = 0 + throw_speed = 3 + throw_range = 6 + +/obj/item/slimecross/examine(mob/user) + . = ..() + if(effect_desc) + . += "[effect_desc]" + +/obj/item/slimecross/Initialize(mapload) + . = ..() + name = effect + " " + colour + " extract" + var/itemcolor = "#FFFFFF" + switch(colour) + if("orange") + itemcolor = "#FFA500" + if("purple") + itemcolor = "#B19CD9" + if("blue") + itemcolor = "#ADD8E6" + if("metal") + itemcolor = "#7E7E7E" + if("yellow") + itemcolor = "#FFFF00" + if("dark purple") + itemcolor = "#551A8B" + if("dark blue") + itemcolor = "#0000FF" + if("silver") + itemcolor = "#D3D3D3" + if("bluespace") + itemcolor = "#32CD32" + if("sepia") + itemcolor = "#704214" + if("cerulean") + itemcolor = "#2956B2" + if("pyrite") + itemcolor = "#FAFAD2" + if("red") + itemcolor = "#FF0000" + if("green") + itemcolor = "#00FF00" + if("pink") + itemcolor = "#FF69B4" + if("gold") + itemcolor = "#FFD700" + if("oil") + itemcolor = "#505050" + if("black") + itemcolor = "#000000" + if("light pink") + itemcolor = "#FFB6C1" + if("adamantine") + itemcolor = "#008B8B" + add_atom_colour(itemcolor, FIXED_COLOUR_PRIORITY) + if(colour == "rainbow") + rainbow_effect() + +/obj/item/slimecrossbeaker //To be used as a result for extract reactions that make chemicals. + name = "result extract" + desc = "You shouldn't see this." + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + icon_state = "base" + var/del_on_empty = TRUE + var/list/list_reagents + +/obj/item/slimecrossbeaker/Initialize(mapload) + . = ..() + create_reagents(50, INJECTABLE | DRAWABLE) + if(list_reagents) + for(var/reagent in list_reagents) + reagents.add_reagent(reagent, list_reagents[reagent]) + if(del_on_empty) + START_PROCESSING(SSobj,src) + +/obj/item/slimecrossbeaker/Destroy() + STOP_PROCESSING(SSobj,src) + return ..() + +/obj/item/slimecrossbeaker/process() + if(!reagents.total_volume) + visible_message("[src] has been drained completely, and melts away.") + qdel(src) + +/obj/item/slimecrossbeaker/bloodpack //Pack of 50u blood. Deletes on empty. + name = "blood extract" + desc = "A sphere of liquid blood, somehow managing to stay together." + color = "#FF0000" + list_reagents = list(/datum/reagent/blood = 50) + +/obj/item/slimecrossbeaker/pax //5u synthpax. + name = "peace-inducing extract" + desc = "A small blob of synthetic pax." + color = "#FFCCCC" + list_reagents = list(/datum/reagent/pax/peaceborg = 5) + +/obj/item/slimecrossbeaker/omnizine //15u omnizine. + name = "healing extract" + desc = "A gelatinous extract of pure omnizine." + color = "#FF00FF" + list_reagents = list(/datum/reagent/medicine/omnizine = 15) + +/obj/item/slimecrossbeaker/autoinjector //As with the above, but automatically injects whomever it is used on with contents. + var/ignore_flags = FALSE + var/self_use_only = FALSE + +/obj/item/slimecrossbeaker/autoinjector/Initialize(mapload) + . = ..() + reagents.flags = DRAWABLE // Cannot be refilled, since it's basically an autoinjector! + +/obj/item/slimecrossbeaker/autoinjector/attack(mob/living/M, mob/user) + if(!reagents.total_volume) + to_chat(user, "[src] is empty!") + return + if(!iscarbon(M)) + return + if(self_use_only && M != user) + to_chat(user, "This can only be used on yourself.") + return + if(reagents.total_volume && (ignore_flags || M.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))) + reagents.trans_to(M, reagents.total_volume, transfered_by = user) + if(user != M) + to_chat(M, "[user] presses [src] against you!") + to_chat(user, "You press [src] against [M], injecting [M.p_them()].") + else + to_chat(user, "You press [src] against yourself, and it flattens against you!") + else + to_chat(user, "There's no place to stick [src]!") + +/obj/item/slimecrossbeaker/autoinjector/regenpack + ignore_flags = TRUE //It is, after all, intended to heal. + name = "mending solution" + desc = "A strange glob of sweet-smelling semifluid, which seems to stick to skin rather easily." + color = "#FF00FF" + list_reagents = list(/datum/reagent/medicine/regen_jelly = 20) + +/obj/item/slimecrossbeaker/autoinjector/slimejelly //Primarily for slimepeople, but you do you. + self_use_only = TRUE + ignore_flags = TRUE + name = "slime jelly bubble" + desc = "A sphere of slime jelly. It seems to stick to your skin, but avoids other surfaces." + color = "#00FF00" + list_reagents = list(/datum/reagent/toxin/slimejelly = 50) + +/obj/item/slimecrossbeaker/autoinjector/peaceandlove + name = "peaceful distillation" + desc = "A light pink gooey sphere. Simply touching it makes you a little dizzy." + color = "#DDAAAA" + list_reagents = list(/datum/reagent/pax/peaceborg = 10, /datum/reagent/drug/space_drugs = 15) //Peace, dudes + +/obj/item/slimecrossbeaker/autoinjector/peaceandlove/Initialize(mapload) + . = ..() + reagents.flags = NONE // It won't be *that* easy to get your hands on pax. + +/obj/item/slimecrossbeaker/autoinjector/slimestimulant + name = "invigorating gel" + desc = "A bubbling purple mixture, designed to heal and boost movement." + color = "#FF00FF" + list_reagents = list(/datum/reagent/medicine/regen_jelly = 30, /datum/reagent/drug/methamphetamine = 9) diff --git a/code/modules/research/xenobiology/crossbreeding/_clothing.dm b/code/modules/research/xenobiology/crossbreeding/_clothing.dm new file mode 100644 index 0000000000000..24837599c9831 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/_clothing.dm @@ -0,0 +1,151 @@ +/* +Slimecrossing Armor + Armor added by the slimecrossing system. + Collected here for clarity. +*/ + +//Rebreather mask - Chilling Blue +/obj/item/clothing/mask/nobreath + name = "rebreather mask" + desc = "A transparent mask, resembling a conventional breath mask, but made of bluish slime. Seems to lack any air supply tube, though." + icon_state = "slime" + inhand_icon_state = "b_mask" + body_parts_covered = NONE + w_class = WEIGHT_CLASS_SMALL + clothing_traits = list(TRAIT_NOBREATH) + armor_type = /datum/armor/mask_nobreath + flags_cover = MASKCOVERSMOUTH + resistance_flags = NONE + +/datum/armor/mask_nobreath + bio = 50 + +/obj/item/clothing/mask/nobreath/equipped(mob/living/carbon/human/user, slot) + . = ..() + if(slot & ITEM_SLOT_MASK) + user.failed_last_breath = FALSE + user.clear_alert(ALERT_NOT_ENOUGH_OXYGEN) + user.apply_status_effect(/datum/status_effect/rebreathing) + +/obj/item/clothing/mask/nobreath/dropped(mob/living/carbon/human/user) + ..() + user.remove_status_effect(/datum/status_effect/rebreathing) + +/obj/item/clothing/glasses/prism_glasses + name = "prism glasses" + desc = "The lenses seem to glow slightly, and reflect light into dazzling colors." + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + icon_state = "prismglasses" + actions_types = list(/datum/action/item_action/change_prism_colour, /datum/action/item_action/place_light_prism) + var/glasses_color = "#FFFFFF" + +/obj/item/clothing/glasses/prism_glasses/item_action_slot_check(slot) + if(slot & ITEM_SLOT_EYES) + return TRUE + +/obj/structure/light_prism + name = "light prism" + desc = "A shining crystal of semi-solid light. Looks fragile." + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + icon_state = "lightprism" + density = FALSE + anchored = TRUE + max_integrity = 10 + +/obj/structure/light_prism/Initialize(mapload, newcolor) + . = ..() + #if DM_VERSION < 515 + newcolor ||= COLOR_WHITE // If you're reading this and developing on 515 or later, you can remove this line + #endif + color = newcolor + set_light_color(newcolor) + set_light(5) + +/obj/structure/light_prism/attack_hand(mob/user, list/modifiers) + to_chat(user, "You dispel [src].") + qdel(src) + +/datum/action/item_action/change_prism_colour + name = "Adjust Prismatic Lens" + button_icon = 'icons/obj/xenobiology/slimecrossing.dmi' + button_icon_state = "prismcolor" + +/datum/action/item_action/change_prism_colour/Trigger(trigger_flags) + if(!IsAvailable(feedback = TRUE)) + return + var/obj/item/clothing/glasses/prism_glasses/glasses = target + var/new_color = tgui_color_picker(owner, "Choose the lens color:", "Color change", glasses.glasses_color) + if(!new_color) + return + glasses.glasses_color = new_color + +/datum/action/item_action/place_light_prism + name = "Fabricate Light Prism" + button_icon = 'icons/obj/xenobiology/slimecrossing.dmi' + button_icon_state = "lightprism" + +/datum/action/item_action/place_light_prism/Trigger(trigger_flags) + if(!IsAvailable(feedback = TRUE)) + return + var/obj/item/clothing/glasses/prism_glasses/glasses = target + if(locate(/obj/structure/light_prism) in get_turf(owner)) + to_chat(owner, "There isn't enough ambient energy to fabricate another light prism here.") + return + if(istype(glasses)) + if(!glasses.glasses_color) + to_chat(owner, "The lens is oddly opaque...") + return + to_chat(owner, "You channel nearby light into a glowing, ethereal prism.") + new /obj/structure/light_prism(get_turf(owner), glasses.glasses_color) + +/obj/item/clothing/head/peaceflower + name = "heroine bud" + desc = "An extremely addictive flower, full of peace magic." + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + worn_icon = 'icons/mob/clothing/head/costume.dmi' + icon_state = "peaceflower" + inhand_icon_state = null + slot_flags = ITEM_SLOT_HEAD + clothing_traits = list(TRAIT_PACIFISM) + body_parts_covered = NONE + force = 0 + throwforce = 0 + w_class = WEIGHT_CLASS_TINY + throw_speed = 1 + throw_range = 3 + +/obj/item/clothing/head/peaceflower/proc/at_peace_check(mob/user) + if(iscarbon(user)) + var/mob/living/carbon/carbon_user = user + if(src == carbon_user.head) + to_chat(user, "You feel at peace. Why would you want anything else?") + return TRUE + return FALSE + +/obj/item/clothing/head/peaceflower/attack_hand(mob/user, list/modifiers) + if(at_peace_check(user)) + return + return ..() + +/obj/item/clothing/head/peaceflower/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params) + if(at_peace_check(usr)) + return + return ..() + +/obj/item/clothing/suit/armor/heavy/adamantine + name = "adamantine armor" + desc = "A full suit of adamantine plate armor. Impressively resistant to damage, but weighs about as much as you do." + icon_state = "adamsuit" + icon = 'icons/obj/clothing/suits/armor.dmi' + worn_icon = 'icons/mob/clothing/suits/armor.dmi' + inhand_icon_state = null + flags_inv = NONE + item_flags = IMMUTABLE_SLOW + slowdown = 4 + var/hit_reflect_chance = 40 + +/obj/item/clothing/suit/armor/heavy/adamantine/IsReflect(def_zone) + if(def_zone in list(BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) && prob(hit_reflect_chance)) + return TRUE + else + return FALSE diff --git a/code/modules/research/xenobiology/crossbreeding/_misc.dm b/code/modules/research/xenobiology/crossbreeding/_misc.dm new file mode 100644 index 0000000000000..87f221bc41a42 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/_misc.dm @@ -0,0 +1,212 @@ +/* +Slimecrossing Items + General items added by the slimecrossing system. + Collected here for clarity. +*/ + +//Rewind camera - I'm already Burning Sepia +/obj/item/camera/rewind + name = "sepia-tinted camera" + desc = "They say a picture is like a moment stopped in time." + pictures_left = 1 + pictures_max = 1 + can_customise = FALSE + default_picture_name = "A nostalgic picture" + +/datum/saved_bodypart + var/obj/item/bodypart/old_part + var/bodypart_type + var/brute_dam + var/burn_dam + +/datum/saved_bodypart/New(obj/item/bodypart/part) + old_part = part + bodypart_type = part.type + brute_dam = part.brute_dam + burn_dam = part.burn_dam + +/mob/living/carbon/proc/apply_saved_bodyparts(list/datum/saved_bodypart/parts) + var/list/dont_chop = list() + for(var/zone in parts) + var/datum/saved_bodypart/saved_part = parts[zone] + var/obj/item/bodypart/already = get_bodypart(zone) + if(QDELETED(saved_part.old_part)) + saved_part.old_part = new saved_part.bodypart_type + if(!already || already != saved_part.old_part) + saved_part.old_part.replace_limb(src, TRUE) + saved_part.old_part.heal_damage(INFINITY, INFINITY, null, FALSE) + saved_part.old_part.receive_damage(saved_part.brute_dam, saved_part.burn_dam, wound_bonus=CANT_WOUND) + dont_chop[zone] = TRUE + for(var/_part in bodyparts) + var/obj/item/bodypart/part = _part + if(dont_chop[part.body_zone]) + continue + part.drop_limb(TRUE) + +/mob/living/carbon/proc/save_bodyparts() + var/list/datum/saved_bodypart/ret = list() + for(var/_part in bodyparts) + var/obj/item/bodypart/part = _part + var/datum/saved_bodypart/saved_part = new(part) + + ret[part.body_zone] = saved_part + return ret + +/obj/item/camera/rewind/afterattack(atom/target, mob/user, flag) + . |= AFTERATTACK_PROCESSED_ITEM + + if(!on || !pictures_left || !isturf(target.loc)) + return . + + if(user == target) + to_chat(user, "You take a selfie!") + else + to_chat(user, "You take a photo with [target]!") + to_chat(target, "[user] takes a photo with you!") + to_chat(target, span_boldnotice("You'll remember this moment forever!")) + + target.AddComponent(/datum/component/dejavu, 2) + return . | ..() + + + +//Timefreeze camera - Old Burning Sepia result. Kept in case admins want to spawn it +/obj/item/camera/timefreeze + name = "sepia-tinted camera" + desc = "They say a picture is like a moment stopped in time." + pictures_left = 1 + pictures_max = 1 + +/obj/item/camera/timefreeze/afterattack(atom/target, mob/user, flag) + . |= AFTERATTACK_PROCESSED_ITEM + + if(!on || !pictures_left || !isturf(target.loc)) + return . + new /obj/effect/timestop(get_turf(target), 2, 50, list(user)) + return . | ..() + +//Hypercharged slime cell - Charged Yellow +/obj/item/stock_parts/cell/emproof/slime/hypercharged // monke edit: make hypercharged slime cells EMP-proof, by changing their parent from cell/high to cell/emproof + name = "hypercharged slime core" + desc = "A charged yellow slime extract, infused with plasma. It almost hurts to touch. Its organic nature makes it immune to EMPs." + rating = 7 + maxcharge = 50000 + chargerate = 2500 + +//Barrier cube - Chilling Grey +/obj/item/barriercube + name = "barrier cube" + desc = "A compressed cube of slime. When squeezed, it grows to massive size!" + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + icon_state = "barriercube" + w_class = WEIGHT_CLASS_TINY + +/obj/item/barriercube/attack_self(mob/user) + if(locate(/obj/structure/barricade/slime) in get_turf(loc)) + to_chat(user, "You can't fit more than one barrier in the same space!") + return + to_chat(user, "You squeeze [src].") + var/obj/B = new /obj/structure/barricade/slime(get_turf(loc)) + B.visible_message("[src] suddenly grows into a large, gelatinous barrier!") + qdel(src) + +//Slime barricade - Chilling Grey +/obj/structure/barricade/slime + name = "gelatinous barrier" + desc = "A huge chunk of grey slime. Bullets might get stuck in it." + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + icon_state = "slimebarrier" + proj_pass_rate = 40 + max_integrity = 60 + +//Melting Gel Wall - Chilling Metal +/obj/effect/forcefield/slimewall + name = "solidified gel" + desc = "A mass of solidified slime gel - completely impenetrable, but it's melting away!" + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + icon_state = "slimebarrier_thick" + can_atmos_pass = ATMOS_PASS_NO + opacity = TRUE + initial_duration = 10 SECONDS + +//Rainbow barrier - Chilling Rainbow +/obj/effect/forcefield/slimewall/rainbow + name = "rainbow barrier" + desc = "Despite others' urgings, you probably shouldn't taste this." + icon_state = "rainbowbarrier" + +//Ice stasis block - Chilling Dark Blue +/obj/structure/ice_stasis + name = "ice block" + desc = "A massive block of ice. You can see something vaguely humanoid inside." + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + icon_state = "frozen" + density = TRUE + max_integrity = 100 + armor_type = /datum/armor/structure_ice_stasis + +/datum/armor/structure_ice_stasis + melee = 30 + bullet = 50 + laser = -50 + energy = -50 + fire = -80 + acid = 30 + +/obj/structure/ice_stasis/Initialize(mapload) + . = ..() + playsound(src, 'sound/magic/ethereal_exit.ogg', 50, TRUE) + +/obj/structure/ice_stasis/Destroy() + for(var/atom/movable/M in contents) + M.forceMove(loc) + playsound(src, 'sound/effects/glassbr3.ogg', 50, TRUE) + return ..() + +//Gold capture device - Chilling Gold +/obj/item/capturedevice + name = "gold capture device" + desc = "Bluespace technology packed into a roughly egg-shaped device, used to store nonhuman creatures. Can't catch them all, though - it only fits one." + w_class = WEIGHT_CLASS_SMALL + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + icon_state = "capturedevice" + +/obj/item/capturedevice/attack(mob/living/pokemon, mob/user) + if(length(contents)) + to_chat(user, "The device already has something inside.") + return + if(!isanimal_or_basicmob(pokemon)) + to_chat(user, "The capture device only works on simple creatures.") + return + if(pokemon.mind) + to_chat(user, "You offer the device to [pokemon].") + if(tgui_alert(pokemon, "Would you like to enter [user]'s capture device?", "Gold Capture Device", list("Yes", "No")) == "Yes") + if(user.can_perform_action(src) && user.can_perform_action(pokemon)) + to_chat(user, "You store [pokemon] in the capture device.") + to_chat(pokemon, "The world warps around you, and you're suddenly in an endless void, with a window to the outside floating in front of you.") + store(pokemon, user) + else + to_chat(user, "You were too far away from [pokemon].") + to_chat(pokemon, "You were too far away from [user].") + else + to_chat(user, "[pokemon] refused to enter the device.") + return + else if(!(FACTION_NEUTRAL in pokemon.faction)) + to_chat(user, "This creature is too aggressive to capture.") + return + to_chat(user, "You store [pokemon] in the capture device.") + store(pokemon) + +/obj/item/capturedevice/attack_self(mob/user) + if(contents.len) + to_chat(user, "You open the capture device!") + release() + else + to_chat(user, "The device is empty...") + +/obj/item/capturedevice/proc/store(mob/living/M) + M.forceMove(src) + +/obj/item/capturedevice/proc/release() + for(var/atom/movable/M in contents) + M.forceMove(get_turf(loc)) diff --git a/code/modules/research/xenobiology/crossbreeding/_mobs.dm b/code/modules/research/xenobiology/crossbreeding/_mobs.dm new file mode 100644 index 0000000000000..82b0f84bcd38c --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/_mobs.dm @@ -0,0 +1,47 @@ +/* +Slimecrossing Mobs + Mobs and effects added by the slimecrossing system. + Collected here for clarity. +*/ + +/// Slime transformation power - from Burning Black +/datum/action/cooldown/spell/shapeshift/slime_form + name = "Slime Transformation" + desc = "Transform from a human to a slime, or back again!" + button_icon_state = "transformslime" + cooldown_time = 0 SECONDS + + invocation_type = INVOCATION_NONE + spell_requirements = NONE + + convert_damage = TRUE + convert_damage_type = BRUTE // MONKESTATION EDIT: slimes take brute so we give the unshapeshift brute too + possible_shapes = list(/mob/living/basic/slime) + + /// If TRUE, we self-delete (remove ourselves) the next time we turn back into a human + var/remove_on_restore = FALSE + +/datum/action/cooldown/spell/shapeshift/slime_form/do_unshapeshift(mob/living/caster) + . = ..() + if(!.) + return + + if(remove_on_restore) + qdel(src) + +//Slime corgi - Chilling Pink +/mob/living/basic/pet/dog/corgi/puppy/slime + name = "\improper slime corgi puppy" + real_name = "slime corgi puppy" + desc = "An unbearably cute pink slime corgi puppy." + icon_state = "slime_puppy" + icon_living = "slime_puppy" + icon_dead = "slime_puppy_dead" + can_be_shaved = FALSE + gold_core_spawnable = NO_SPAWN + speak_emote = list("blorbles", "bubbles", "borks") + +/mob/living/basic/pet/dog/corgi/puppy/slime/update_dog_speech(datum/ai_planning_subtree/random_speech/speech) + speech.speak = string_list(list()) + speech.emote_hear = string_list(list("bubbles!", "splorts.", "splops!")) + speech.emote_see = string_list(list("gets goop everywhere.", "flops.", "jiggles!")) diff --git a/code/modules/research/xenobiology/crossbreeding/_potions.dm b/code/modules/research/xenobiology/crossbreeding/_potions.dm new file mode 100644 index 0000000000000..283ba165c10e1 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/_potions.dm @@ -0,0 +1,223 @@ +/* +Slimecrossing Potions + Potions added by the slimecrossing system. + Collected here for clarity. +*/ + +//Extract cloner - Charged Grey +/obj/item/slimepotion/extract_cloner + name = "extract cloning potion" + desc = "A more powerful version of the extract enhancer potion, capable of cloning regular slime extracts." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potpurple" + +/obj/item/slimepotion/extract_cloner/afterattack(obj/item/target, mob/user , proximity) + if(!proximity) + return + . |= AFTERATTACK_PROCESSED_ITEM + if(is_reagent_container(target)) + return ..(target, user, proximity) + if(istype(target, /obj/item/slimecross)) + to_chat(user, "[target] is too complex for the potion to clone!") + return + if(!istype(target, /obj/item/slime_extract)) + return + var/obj/item/slime_extract/S = target + if(S.recurring) + to_chat(user, "[target] is too complex for the potion to clone!") + return + var/path = S.type + var/obj/item/slime_extract/C = new path(get_turf(target)) + C.Uses = S.Uses + to_chat(user, "You pour the potion onto [target], and the fluid solidifies into a copy of it!") + qdel(src) + return + +//Peace potion - Charged Light Pink +/obj/item/slimepotion/peacepotion + name = "pacification potion" + desc = "A light pink solution of chemicals, smelling like liquid peace. And mercury salts." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potlightpink" + +/obj/item/slimepotion/peacepotion/attack(mob/living/peace_target, mob/user) + if(!isliving(peace_target) || peace_target.stat == DEAD) + to_chat(user, "[src] only works on the living.") + return ..() + if(ismegafauna(peace_target)) + to_chat(user, "[src] does not work on beings of pure evil!") + return ..() + if(peace_target != user) + peace_target.visible_message(span_danger("[user] starts to feed [peace_target] [src]!"), + span_userdanger("[user] starts to feed you [src]!")) + else + peace_target.visible_message(span_danger("[user] starts to drink [src]!"), + span_danger("You start to drink [src]!")) + + if(!do_after(user, 100, target = peace_target)) + return + if(peace_target != user) + to_chat(user, "You feed [peace_target] [src]!") + else + to_chat(user, "You drink [src]!") + if(isanimal_or_basicmob(peace_target)) + ADD_TRAIT(peace_target, TRAIT_PACIFISM, MAGIC_TRAIT) + else if(iscarbon(peace_target)) + var/mob/living/carbon/peaceful_carbon = peace_target + peaceful_carbon.gain_trauma(/datum/brain_trauma/severe/pacifism, TRAUMA_RESILIENCE_SURGERY) + qdel(src) + +//Love potion - Charged Pink +/obj/item/slimepotion/lovepotion + name = "love potion" + desc = "A pink chemical mix thought to inspire feelings of love." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potpink" + +/obj/item/slimepotion/lovepotion/attack(mob/living/love_target, mob/user) + if(!isliving(love_target) || love_target.stat == DEAD) + to_chat(user, "The love potion only works on living things, sicko!") + return ..() + if(ismegafauna(love_target)) + to_chat(user, "The love potion does not work on beings of pure evil!") + return ..() + if(user == love_target) + to_chat(user, "You can't drink the love potion. What are you, a narcissist?") + return ..() + if(love_target.has_status_effect(/datum/status_effect/in_love)) + to_chat(user, "[love_target] is already lovestruck!") + return ..() + + love_target.visible_message(span_danger("[user] starts to feed [love_target] a love potion!"), + span_userdanger("[user] starts to feed you a love potion!")) + + if(!do_after(user, 50, target = love_target)) + return + to_chat(user, "You feed [love_target] the love potion!") + to_chat(love_target, "You develop feelings for [user], and anyone [user.p_they()] like[user.p_s()].") + love_target.faction |= "[REF(user)]" + love_target.apply_status_effect(/datum/status_effect/in_love, user) + qdel(src) + +//Pressure potion - Charged Dark Blue +/obj/item/slimepotion/spaceproof + name = "slime pressurization potion" + desc = "A potent chemical sealant that will render any article of clothing airtight. Has two uses." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potblue" + var/uses = 2 + +/obj/item/slimepotion/spaceproof/afterattack(obj/item/clothing/C, mob/user, proximity) + . = ..() + if(!uses) + qdel(src) + return + if(!proximity) + return + if(!istype(C)) + to_chat(user, "The potion can only be used on clothing!") + return + . |= AFTERATTACK_PROCESSED_ITEM + if(istype(C, /obj/item/clothing/suit/space)) + to_chat(user, "The [C] is already pressure-resistant!") + return . | ..() + if(C.min_cold_protection_temperature == SPACE_SUIT_MIN_TEMP_PROTECT && C.clothing_flags & STOPSPRESSUREDAMAGE) + to_chat(user, "The [C] is already pressure-resistant!") + return . | ..() + to_chat(user, "You slather the blue gunk over the [C], making it airtight.") + C.name = "pressure-resistant [C.name]" + C.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + C.add_atom_colour("#000080", FIXED_COLOUR_PRIORITY) + C.min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + C.cold_protection = C.body_parts_covered + C.clothing_flags |= STOPSPRESSUREDAMAGE + uses-- + if(!uses) + qdel(src) + return . + +//Enhancer potion - Charged Cerulean +/obj/item/slimepotion/enhancer/max + name = "extract maximizer" + desc = "An extremely potent chemical mix that will maximize a slime extract's uses." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potpurple" + +//Lavaproofing potion - Charged Red +/obj/item/slimepotion/lavaproof + name = "slime lavaproofing potion" + desc = "A strange, reddish goo said to repel lava as if it were water, without reducing flammability. Has two uses." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potred" + resistance_flags = LAVA_PROOF | FIRE_PROOF + var/uses = 2 + +/obj/item/slimepotion/lavaproof/afterattack(obj/item/C, mob/user, proximity) + . = ..() + if(!uses) + qdel(src) + return ..() + if(!proximity) + return ..() + if(!istype(C)) + to_chat(user, "You can't coat this with lavaproofing fluid!") + return ..() + . |= AFTERATTACK_PROCESSED_ITEM + to_chat(user, "You slather the red gunk over the [C], making it lavaproof.") + C.name = "lavaproof [C.name]" + C.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + C.add_atom_colour("#800000", FIXED_COLOUR_PRIORITY) + C.resistance_flags |= LAVA_PROOF + if (isclothing(C)) + var/obj/item/clothing/CL = C + CL.clothing_flags |= LAVAPROTECT + uses-- + if(!uses) + qdel(src) + return . + +//Revival potion - Charged Grey +/obj/item/slimepotion/slime_reviver + name = "slime revival potion" + desc = "Infused with plasma and compressed gel, this brings dead slimes back to life." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potsilver" + +/obj/item/slimepotion/slime_reviver/attack(mob/living/basic/slime/revive_target, mob/user) + if(!isslime(revive_target)) + to_chat(user, "The potion only works on slimes!") + return ..() + if(revive_target.stat != DEAD) + to_chat(user, "The slime is still alive!") + return + if(revive_target.maxHealth <= 0) + to_chat(user, "The slime is too unstable to return!") + revive_target.revive(HEAL_ALL) + revive_target.set_stat(CONSCIOUS) + revive_target.visible_message("[revive_target] is filled with renewed vigor and blinks awake!") + revive_target.maxHealth -= 10 //Revival isn't healthy. + revive_target.health -= 10 + revive_target.regenerate_icons() + qdel(src) + +//Stabilizer potion - Charged Blue +/obj/item/slimepotion/slime/chargedstabilizer + name = "slime omnistabilizer" + desc = "An extremely potent chemical mix that will stop a slime from mutating completely." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potcyan" + +/obj/item/slimepotion/slime/chargedstabilizer/attack(mob/living/basic/slime/stabilize_target, mob/user) + if(!isslime(stabilize_target)) + to_chat(user, "The stabilizer only works on slimes!") + return ..() + if(stabilize_target.stat) + to_chat(user, "The slime is dead!") + return + if(stabilize_target.mutation_chance == 0) + to_chat(user, "The slime already has no chance of mutating!") + return + + to_chat(user, "You feed the slime the omnistabilizer. It will not mutate this cycle!") + stabilize_target.mutation_chance = 0 + qdel(src) diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm new file mode 100644 index 0000000000000..ad2be8ca8d307 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm @@ -0,0 +1,1093 @@ +/atom/movable/screen/alert/status_effect/rainbow_protection + name = "Rainbow Protection" + desc = "You are defended from harm, but so are those you might seek to injure!" + icon_state = "slime_rainbowshield" + +/datum/status_effect/rainbow_protection + id = "rainbow_protection" + duration = 100 + alert_type = /atom/movable/screen/alert/status_effect/rainbow_protection + show_duration = TRUE + var/originalcolor + +/datum/status_effect/rainbow_protection/on_apply() + owner.status_flags |= GODMODE + ADD_TRAIT(owner, TRAIT_PACIFISM, /datum/status_effect/rainbow_protection) + owner.visible_message("[owner] shines with a brilliant rainbow light.", + "You feel protected by an unknown force!") + originalcolor = owner.color + return ..() + +/datum/status_effect/rainbow_protection/tick() + owner.color = rgb(rand(0,255),rand(0,255),rand(0,255)) + return ..() + +/datum/status_effect/rainbow_protection/on_remove() + owner.status_flags &= ~GODMODE + owner.color = originalcolor + REMOVE_TRAIT(owner, TRAIT_PACIFISM, /datum/status_effect/rainbow_protection) + owner.visible_message("[owner] stops glowing, the rainbow light fading away.", + "You no longer feel protected...") + +/atom/movable/screen/alert/status_effect/slimeskin + name = "Adamantine Slimeskin" + desc = "You are covered in a thick, non-neutonian gel." + icon_state = "slime_stoneskin" + +/datum/status_effect/slimeskin + id = "slimeskin" + duration = 300 + alert_type = /atom/movable/screen/alert/status_effect/slimeskin + show_duration = TRUE + var/originalcolor + +/datum/status_effect/slimeskin/on_apply() + originalcolor = owner.color + owner.color = "#3070CC" + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.physiology.damage_resistance += 10 + owner.visible_message("[owner] is suddenly covered in a strange, blue-ish gel!", + "You are covered in a thick, rubbery gel.") + return ..() + +/datum/status_effect/slimeskin/on_remove() + owner.color = originalcolor + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.physiology.damage_resistance -= 10 + owner.visible_message("[owner]'s gel coating liquefies and dissolves away.", + "Your gel second-skin dissolves!") + +/datum/status_effect/slimerecall + id = "slime_recall" + duration = -1 //Will be removed by the extract. + tick_interval = -1 + alert_type = null + var/interrupted = FALSE + var/mob/target + var/icon/bluespace + +/datum/status_effect/slimerecall/on_apply() + RegisterSignal(owner, COMSIG_LIVING_RESIST, PROC_REF(resistField)) + to_chat(owner, span_danger("You feel a sudden tug from an unknown force, and feel a pull to bluespace!")) + to_chat(owner, "Resist if you wish avoid the force!") + bluespace = icon('icons/effects/effects.dmi',"chronofield") + owner.add_overlay(bluespace) + return ..() + +/datum/status_effect/slimerecall/proc/resistField() + SIGNAL_HANDLER + interrupted = TRUE + owner.remove_status_effect(src) + +/datum/status_effect/slimerecall/on_remove() + UnregisterSignal(owner, COMSIG_LIVING_RESIST) + owner.cut_overlay(bluespace) + if(interrupted || !ismob(target)) + to_chat(owner, "The bluespace tug fades away, and you feel that the force has passed you by.") + return + var/turf/old_location = get_turf(owner) + if(do_teleport(owner, target.loc, channel = TELEPORT_CHANNEL_QUANTUM)) //despite being named a bluespace teleportation method the quantum channel is used to preserve precision teleporting with a bag of holding + old_location.visible_message("[owner] disappears in a flurry of sparks!") + to_chat(owner, "The unknown force snatches briefly you from reality, and deposits you next to [target]!") + +/atom/movable/screen/alert/status_effect/freon/stasis + desc = "You're frozen inside of a protective ice cube! While inside, you can't do anything, but are immune to harm! Resist to get out." + +/datum/status_effect/frozenstasis + id = "slime_frozen" + status_type = STATUS_EFFECT_UNIQUE + duration = -1 //Will remove self when block breaks. + alert_type = /atom/movable/screen/alert/status_effect/freon/stasis + var/obj/structure/ice_stasis/cube + +/datum/status_effect/frozenstasis/on_apply() + RegisterSignal(owner, COMSIG_LIVING_RESIST, PROC_REF(breakCube)) + cube = new /obj/structure/ice_stasis(get_turf(owner)) + owner.forceMove(cube) + owner.status_flags |= GODMODE + return ..() + +/datum/status_effect/frozenstasis/tick() + if(!cube || owner.loc != cube) + owner.remove_status_effect(src) + +/datum/status_effect/frozenstasis/proc/breakCube() + SIGNAL_HANDLER + + owner.remove_status_effect(src) + +/datum/status_effect/frozenstasis/on_remove() + if(cube) + qdel(cube) + owner.status_flags &= ~GODMODE + UnregisterSignal(owner, COMSIG_LIVING_RESIST) + +/datum/status_effect/slime_clone + id = "slime_cloned" + status_type = STATUS_EFFECT_UNIQUE + duration = -1 + alert_type = null + var/mob/living/clone + var/datum/mind/originalmind //For when the clone gibs. + +/datum/status_effect/slime_clone/on_apply() + var/typepath = owner.type + clone = new typepath(owner.loc) + var/mob/living/carbon/O = owner + var/mob/living/carbon/C = clone + if(istype(C) && istype(O)) + C.real_name = O.real_name + C.update_name_tag(C.real_name) // monkestation edit: name tags + O.dna.transfer_identity(C) + C.updateappearance(mutcolor_update=1) + if(owner.mind) + originalmind = owner.mind + owner.mind.transfer_to(clone) + clone.apply_status_effect(/datum/status_effect/slime_clone_decay) + return ..() + +/datum/status_effect/slime_clone/tick() + if(!istype(clone) || clone.stat != CONSCIOUS) + owner.remove_status_effect(src) + +/datum/status_effect/slime_clone/on_remove() + if(clone?.mind && owner) + clone.mind.transfer_to(owner) + else + if(owner && originalmind) + originalmind.transfer_to(owner) + if(originalmind.key) + owner.ckey = originalmind.key + if(clone) + clone.unequip_everything() + qdel(clone) + +/atom/movable/screen/alert/status_effect/clone_decay + name = "Clone Decay" + desc = "You are simply a construct, and cannot maintain this form forever. You will be returned to your original body if you should fall." + icon_state = "slime_clonedecay" + +/datum/status_effect/slime_clone_decay + id = "slime_clonedecay" + status_type = STATUS_EFFECT_UNIQUE + duration = -1 + alert_type = /atom/movable/screen/alert/status_effect/clone_decay + +/datum/status_effect/slime_clone_decay/tick() + owner.adjustToxLoss(1, 0) + owner.adjustOxyLoss(1, 0) + owner.adjustBruteLoss(1, 0) + owner.adjustFireLoss(1, 0) + owner.color = "#007BA7" + +/atom/movable/screen/alert/status_effect/bloodchill + name = "Bloodchilled" + desc = "You feel a shiver down your spine after getting hit with a glob of cold blood. You'll move slower and get frostbite for a while!" + icon_state = "bloodchill" + +/datum/status_effect/bloodchill + id = "bloodchill" + duration = 100 + alert_type = /atom/movable/screen/alert/status_effect/bloodchill + +/datum/status_effect/bloodchill/on_apply() + owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/bloodchill) + return ..() + +/datum/status_effect/bloodchill/tick() + if(prob(50)) + owner.adjustFireLoss(2) + +/datum/status_effect/bloodchill/on_remove() + owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/bloodchill) + +/datum/status_effect/bonechill + id = "bonechill" + duration = 80 + alert_type = /atom/movable/screen/alert/status_effect/bonechill + +/datum/status_effect/bonechill/on_apply() + owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/bonechill) + return ..() + +/datum/status_effect/bonechill/tick() + if(prob(50)) + owner.adjustFireLoss(1) + owner.set_jitter_if_lower(6 SECONDS) + owner.adjust_bodytemperature(-10) + if(ishuman(owner)) + var/mob/living/carbon/human/humi = owner + humi.adjust_coretemperature(-10) + +/datum/status_effect/bonechill/on_remove() + owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/bonechill) +/atom/movable/screen/alert/status_effect/bonechill + name = "Bonechilled" + desc = "You feel a shiver down your spine after hearing the haunting noise of bone rattling. You'll move slower and get frostbite for a while!" + icon_state = "bloodchill" + +/datum/status_effect/rebreathing + id = "rebreathing" + duration = -1 + alert_type = null + +/datum/status_effect/rebreathing/tick() + owner.adjustOxyLoss(-6, 0) //Just a bit more than normal breathing. + +/////////////////////////////////////////////////////// +//////////////////CONSUMING EXTRACTS/////////////////// +/////////////////////////////////////////////////////// + +/datum/status_effect/firecookie + id = "firecookie" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 100 + +/datum/status_effect/firecookie/on_apply() + ADD_TRAIT(owner, TRAIT_RESISTCOLD,"firecookie") + owner.adjust_bodytemperature(110) + return ..() + +/datum/status_effect/firecookie/on_remove() + REMOVE_TRAIT(owner, TRAIT_RESISTCOLD,"firecookie") + +/datum/status_effect/watercookie + id = "watercookie" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 100 + +/datum/status_effect/watercookie/on_apply() + ADD_TRAIT(owner, TRAIT_NO_SLIP_WATER,"watercookie") + return ..() + +/datum/status_effect/watercookie/tick() + for(var/turf/open/T in range(get_turf(owner),1)) + T.MakeSlippery(TURF_WET_WATER, min_wet_time = 10, wet_time_to_add = 5) + +/datum/status_effect/watercookie/on_remove() + REMOVE_TRAIT(owner, TRAIT_NO_SLIP_WATER,"watercookie") + +/datum/status_effect/metalcookie + id = "metalcookie" + status_type = STATUS_EFFECT_REFRESH + alert_type = null + duration = 100 + +/datum/status_effect/metalcookie/on_apply() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.physiology.brute_mod *= 0.9 + return ..() + +/datum/status_effect/metalcookie/on_remove() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.physiology.brute_mod /= 0.9 + +/datum/status_effect/sparkcookie + id = "sparkcookie" + status_type = STATUS_EFFECT_REFRESH + alert_type = null + duration = 300 + var/original_coeff + +/datum/status_effect/sparkcookie/on_apply() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + original_coeff = H.physiology.siemens_coeff + H.physiology.siemens_coeff = 0 + return ..() + +/datum/status_effect/sparkcookie/on_remove() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.physiology.siemens_coeff = original_coeff + +/datum/status_effect/toxincookie + id = "toxincookie" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 600 + +/datum/status_effect/toxincookie/on_apply() + ADD_TRAIT(owner, TRAIT_TOXINLOVER,"toxincookie") + return ..() + +/datum/status_effect/toxincookie/on_remove() + REMOVE_TRAIT(owner, TRAIT_TOXINLOVER,"toxincookie") + +/datum/status_effect/timecookie + id = "timecookie" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 600 + +/datum/status_effect/timecookie/on_apply() + owner.add_actionspeed_modifier(/datum/actionspeed_modifier/timecookie) + return ..() + +/datum/status_effect/timecookie/on_remove() + owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/timecookie) + return ..() + +/datum/status_effect/lovecookie + id = "lovecookie" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 300 + +/datum/status_effect/lovecookie/tick() + if(owner.stat != CONSCIOUS) + return + if(iscarbon(owner)) + var/mob/living/carbon/C = owner + if(C.handcuffed) + return + var/list/huggables = list() + for(var/mob/living/carbon/L in range(get_turf(owner),1)) + if(L != owner) + huggables += L + if(length(huggables)) + var/mob/living/carbon/hugged = pick(huggables) + owner.visible_message("[owner] hugs [hugged]!"), span_notice("You hug [hugged]!") + +/datum/status_effect/tarcookie + id = "tarcookie" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 100 + +/datum/status_effect/tarcookie/tick() + for(var/mob/living/carbon/human/L in range(get_turf(owner),1)) + if(L != owner) + L.apply_status_effect(/datum/status_effect/tarfoot) + +/datum/status_effect/tarfoot + id = "tarfoot" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 30 + +/datum/status_effect/tarfoot/on_apply() + owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/tarfoot) + return ..() + +/datum/status_effect/tarfoot/on_remove() + owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/tarfoot) + +/datum/status_effect/spookcookie + id = "spookcookie" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 300 + +/datum/status_effect/spookcookie/on_apply() + var/image/I = image(icon = 'icons/mob/species/human/human.dmi', icon_state = "skeleton", layer = ABOVE_MOB_LAYER, loc = owner) + I.override = 1 + owner.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, "spookyscary", I) + return ..() + +/datum/status_effect/spookcookie/on_remove() + owner.remove_alt_appearance("spookyscary") + +/datum/status_effect/peacecookie + id = "peacecookie" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 100 + +/datum/status_effect/peacecookie/tick() + for(var/mob/living/L in range(get_turf(owner),1)) + L.apply_status_effect(/datum/status_effect/plur) + +/datum/status_effect/plur + id = "plur" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 30 + +/datum/status_effect/plur/on_apply() + ADD_TRAIT(owner, TRAIT_PACIFISM, "peacecookie") + return ..() + +/datum/status_effect/plur/on_remove() + REMOVE_TRAIT(owner, TRAIT_PACIFISM, "peacecookie") + +/datum/status_effect/adamantinecookie + id = "adamantinecookie" + status_type = STATUS_EFFECT_REFRESH + alert_type = null + duration = 100 + +/datum/status_effect/adamantinecookie/on_apply() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.physiology.burn_mod *= 0.9 + return ..() + +/datum/status_effect/adamantinecookie/on_remove() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.physiology.burn_mod /= 0.9 + +/////////////////////////////////////////////////////// +//////////////////STABILIZED EXTRACTS////////////////// +/////////////////////////////////////////////////////// + +/datum/status_effect/stabilized //The base stabilized extract effect, has no effect of its' own. + id = "stabilizedbase" + duration = -1 + alert_type = null + /// Item which provides this buff + var/obj/item/slimecross/stabilized/linked_extract + /// Colour of the extract providing the buff + var/colour = "null" + +/datum/status_effect/stabilized/Destroy() + if(linked_extract?.linked_effect == src) + linked_extract.linked_effect = null + linked_extract = null + return ..() + +/datum/status_effect/stabilized/on_creation(mob/living/new_owner, obj/item/slimecross/stabilized/linked_extract) + src.linked_extract = linked_extract + return ..() + +/datum/status_effect/stabilized/tick() + if(QDELETED(linked_extract)) + qdel(src) + return + if(linked_extract.get_held_mob() == owner) + return + owner.balloon_alert(owner, "[colour] extract faded!") + if(!QDELETED(linked_extract)) + linked_extract.linked_effect = null + START_PROCESSING(SSobj,linked_extract) + qdel(src) + +/datum/status_effect/stabilized/null //This shouldn't ever happen, but just in case. + id = "stabilizednull" + + +//Stabilized effects start below. +/datum/status_effect/stabilized/grey + id = "stabilizedgrey" + colour = "grey" + +/datum/status_effect/stabilized/grey/tick() + for(var/mob/living/basic/slime/new_friend in range(3, get_turf(owner))) + SEND_SIGNAL(new_friend, COMSIG_FRIENDSHIP_CHANGE, owner, 2) + return ..() + +/datum/status_effect/stabilized/orange + id = "stabilizedorange" + colour = "orange" + +/datum/status_effect/stabilized/orange/tick() + var/body_temp_target = owner.get_body_temp_normal(apply_change = FALSE) + + var/body_temp_actual = owner.bodytemperature + var/body_temp_offset = body_temp_target - body_temp_actual + body_temp_offset = clamp(body_temp_offset, -5, 5) + owner.adjust_bodytemperature(body_temp_offset) + + if(ishuman(owner)) + var/mob/living/carbon/human/human = owner + var/core_temp_actual = human.coretemperature + var/core_temp_offset = body_temp_target - core_temp_actual + core_temp_offset = clamp(core_temp_offset, -5, 5) + human.adjust_coretemperature(core_temp_offset) + + return ..() + +/datum/status_effect/stabilized/purple + id = "stabilizedpurple" + colour = "purple" + /// Whether we healed from our last tick + var/healed_last_tick = FALSE + +/datum/status_effect/stabilized/purple/tick() + healed_last_tick = FALSE + + if(owner.getBruteLoss() > 0) + owner.adjustBruteLoss(-0.2) + healed_last_tick = TRUE + + if(owner.getFireLoss() > 0) + owner.adjustFireLoss(-0.2) + healed_last_tick = TRUE + + if(owner.getToxLoss() > 0) + // Forced, so slimepeople are healed as well. + owner.adjustToxLoss(-0.2, forced = TRUE) + healed_last_tick = TRUE + + // Technically, "healed this tick" by now. + if(healed_last_tick) + new /obj/effect/temp_visual/heal(get_turf(owner), "#FF0000") + + return ..() + +/datum/status_effect/stabilized/purple/get_examine_text() + if(healed_last_tick) + return "[owner.p_they(TRUE)] [owner.p_are()] regenerating slowly, purplish goo filling in small injuries!" + + return null + +/datum/status_effect/stabilized/blue + id = "stabilizedblue" + colour = "blue" + +/datum/status_effect/stabilized/blue/on_apply() + ADD_TRAIT(owner, TRAIT_NO_SLIP_WATER, "slimestatus") + return ..() + +/datum/status_effect/stabilized/blue/on_remove() + REMOVE_TRAIT(owner, TRAIT_NO_SLIP_WATER, "slimestatus") + +/datum/status_effect/stabilized/metal + id = "stabilizedmetal" + colour = "metal" + var/cooldown = 30 + var/max_cooldown = 30 + +/datum/status_effect/stabilized/metal/tick() + if(cooldown > 0) + cooldown-- + else + cooldown = max_cooldown + var/list/sheets = list() + for(var/obj/item/stack/sheet/S in owner.get_all_contents()) + if(S.amount < S.max_amount) + sheets += S + + if(sheets.len > 0) + var/obj/item/stack/sheet/S = pick(sheets) + S.amount++ + S.update_custom_materials() + to_chat(owner, "[linked_extract] adds a layer of slime to [S], which metamorphosizes into another sheet of material!") + return ..() + + +/datum/status_effect/stabilized/yellow + id = "stabilizedyellow" + colour = "yellow" + var/cooldown = 10 + var/max_cooldown = 10 + +/datum/status_effect/stabilized/yellow/get_examine_text() + return "Nearby electronics seem just a little more charged wherever [owner.p_they()] go[owner.p_es()]." + +/datum/status_effect/stabilized/yellow/tick() + if(cooldown > 0) + cooldown-- + return ..() + cooldown = max_cooldown + var/list/batteries = list() + for(var/obj/item/stock_parts/cell/C in owner.get_all_contents()) + if(C.charge < C.maxcharge) + batteries += C + if(batteries.len) + var/obj/item/stock_parts/cell/ToCharge = pick(batteries) + ToCharge.charge += min(ToCharge.maxcharge - ToCharge.charge, ToCharge.maxcharge/10) //10% of the cell, or to maximum. + to_chat(owner, "[linked_extract] discharges some energy into a device you have.") + return ..() + +/obj/item/hothands + name = "burning fingertips" + desc = "You shouldn't see this." + +/obj/item/hothands/get_temperature() + return 290 //Below what's required to ignite plasma. + +/datum/status_effect/stabilized/darkpurple + id = "stabilizeddarkpurple" + colour = "dark purple" + var/obj/item/hothands/fire + +/datum/status_effect/stabilized/darkpurple/on_apply() + ADD_TRAIT(owner, TRAIT_RESISTHEATHANDS, "slimestatus") + fire = new(owner) + return ..() + +/datum/status_effect/stabilized/darkpurple/tick() + var/obj/item/item = owner.get_active_held_item() + if(item) + if(IS_EDIBLE(item) && (item.microwave_act(microwaver = owner) & COMPONENT_MICROWAVE_SUCCESS)) + to_chat(owner, "[linked_extract] flares up brightly, and your hands alone are enough to cook [item]!") + else + item.attackby(fire, owner) + return ..() + +/datum/status_effect/stabilized/darkpurple/on_remove() + REMOVE_TRAIT(owner, TRAIT_RESISTHEATHANDS, "slimestatus") + qdel(fire) + +/datum/status_effect/stabilized/darkpurple/get_examine_text() + return "[owner.p_their(TRUE)] fingertips burn brightly!" + +/datum/status_effect/stabilized/darkblue + id = "stabilizeddarkblue" + colour = "dark blue" + +/datum/status_effect/stabilized/darkblue/tick() + if(owner.fire_stacks > 0 && prob(80)) + owner.adjust_wet_stacks(1) + if(owner.fire_stacks <= 0) + to_chat(owner, "[linked_extract] coats you in a watery goo, extinguishing the flames.") + var/obj/O = owner.get_active_held_item() + if(O) + O.extinguish() //All shamelessly copied from water's expose_obj, since I didn't seem to be able to get it here for some reason. + O.wash(CLEAN_TYPE_ACID) + // Monkey cube + if(istype(O, /obj/item/food/monkeycube)) + to_chat(owner, "[linked_extract] kept your hands wet! It makes [O] expand!") + var/obj/item/food/monkeycube/cube = O + cube.Expand() + + // Dehydrated carp + else if(istype(O, /obj/item/toy/plush/carpplushie/dehy_carp)) + to_chat(owner, "[linked_extract] kept your hands wet! It makes [O] expand!") + var/obj/item/toy/plush/carpplushie/dehy_carp/dehy = O + dehy.Swell() // Makes a carp + + else if(istype(O, /obj/item/stack/sheet/hairlesshide)) + to_chat(owner, "[linked_extract] kept your hands wet! It wets [O]!") + var/obj/item/stack/sheet/hairlesshide/HH = O + new /obj/item/stack/sheet/wethide(get_turf(HH), HH.amount) + qdel(HH) + ..() + +/datum/status_effect/stabilized/silver + id = "stabilizedsilver" + colour = "silver" + +/datum/status_effect/stabilized/silver/on_apply() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.physiology.hunger_mod *= 0.8 //20% buff + return ..() + +/datum/status_effect/stabilized/silver/on_remove() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.physiology.hunger_mod /= 0.8 + +//Bluespace has an icon because it's kinda active. +/atom/movable/screen/alert/status_effect/bluespaceslime + name = "Stabilized Bluespace Extract" + desc = "You shouldn't see this, since we set it to change automatically!" + icon_state = "slime_bluespace_on" + +/datum/status_effect/bluespacestabilization + id = "stabilizedbluespacecooldown" + duration = 1200 + alert_type = null + +/datum/status_effect/stabilized/bluespace + id = "stabilizedbluespace" + colour = "bluespace" + alert_type = /atom/movable/screen/alert/status_effect/bluespaceslime + var/healthcheck + +/datum/status_effect/stabilized/bluespace/tick() + if(owner.has_status_effect(/datum/status_effect/bluespacestabilization)) + linked_alert.desc = "The stabilized bluespace extract is still aligning you with the bluespace axis." + linked_alert.icon_state = "slime_bluespace_off" + return ..() + else + linked_alert.desc = "The stabilized bluespace extract will try to redirect you from harm!" + linked_alert.icon_state = "slime_bluespace_on" + + if(healthcheck && (healthcheck - owner.health) > 5) + owner.visible_message("[linked_extract] notices the sudden change in [owner]'s physical health, and activates!") + do_sparks(5,FALSE,owner) + var/F = find_safe_turf(zlevels = owner.z, extended_safety_checks = TRUE) + var/range = 0 + if(!F) + F = get_turf(owner) + range = 50 + if(do_teleport(owner, F, range, channel = TELEPORT_CHANNEL_BLUESPACE)) + to_chat(owner, "[linked_extract] will take some time to re-align you on the bluespace axis.") + do_sparks(5,FALSE,owner) + owner.apply_status_effect(/datum/status_effect/bluespacestabilization) + healthcheck = owner.health + return ..() + +/datum/status_effect/stabilized/sepia + id = "stabilizedsepia" + colour = "sepia" + var/mod = 0 + +/datum/status_effect/stabilized/sepia/tick() + if(prob(50) && mod > -1) + mod-- + owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/status_effect/sepia, multiplicative_slowdown = -0.5) + else if(mod < 1) + mod++ + // yeah a value of 0 does nothing but replacing the trait in place is cheaper than removing and adding repeatedly + owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/status_effect/sepia, multiplicative_slowdown = 0) + return ..() + +/datum/status_effect/stabilized/sepia/on_remove() + owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/sepia) + +/datum/status_effect/stabilized/cerulean + id = "stabilizedcerulean" + colour = "cerulean" + var/mob/living/clone + +/datum/status_effect/stabilized/cerulean/on_apply() + var/typepath = owner.type + clone = new typepath(owner.loc) + var/mob/living/carbon/O = owner + var/mob/living/carbon/C = clone + if(istype(C) && istype(O)) + C.real_name = O.real_name + C.update_name_tag(C.real_name) // monkestation edit: name tags + O.dna.transfer_identity(C) + C.updateappearance(mutcolor_update=1) + return ..() + +/datum/status_effect/stabilized/cerulean/tick() + if(owner.stat == DEAD) + if(clone && clone.stat != DEAD) + owner.visible_message("[owner] blazes with brilliant light, [linked_extract] whisking [owner.p_their()] soul away.", + "You feel a warm glow from [linked_extract], and you open your eyes... elsewhere.") + if(owner.mind) + owner.mind.transfer_to(clone) + clone = null + qdel(linked_extract) + if(!clone || clone.stat == DEAD) + to_chat(owner, "[linked_extract] desperately tries to move your soul to a living body, but can't find one!") + qdel(linked_extract) + ..() + +/datum/status_effect/stabilized/cerulean/on_remove() + if(clone) + clone.visible_message("[clone] dissolves into a puddle of goo!") + clone.unequip_everything() + qdel(clone) + +/datum/status_effect/stabilized/pyrite + id = "stabilizedpyrite" + colour = "pyrite" + var/originalcolor + +/datum/status_effect/stabilized/pyrite/on_apply() + originalcolor = owner.color + return ..() + +/datum/status_effect/stabilized/pyrite/tick() + owner.color = rgb(rand(0,255),rand(0,255),rand(0,255)) + return ..() + +/datum/status_effect/stabilized/pyrite/on_remove() + owner.color = originalcolor + +/datum/status_effect/stabilized/red + id = "stabilizedred" + colour = "red" + +/datum/status_effect/stabilized/red/on_apply() + . = ..() + owner.add_movespeed_mod_immunities(type, /datum/movespeed_modifier/equipment_speedmod) + +/datum/status_effect/stabilized/red/on_remove() + owner.remove_movespeed_mod_immunities(type, /datum/movespeed_modifier/equipment_speedmod) + return ..() + +/datum/status_effect/stabilized/green + id = "stabilizedgreen" + colour = "green" + var/datum/dna/originalDNA + var/originalname + +/datum/status_effect/stabilized/green/on_apply() + to_chat(owner, "You feel different...") + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + originalDNA = new H.dna.type + originalname = H.real_name + H.dna.copy_dna(originalDNA) + randomize_human(H) + return ..() + +// Only occasionally give examiners a warning. +/datum/status_effect/stabilized/green/get_examine_text() + if(prob(50)) + return "[owner.p_they(TRUE)] look[owner.p_s()] a bit green and gooey..." + + return null + +/datum/status_effect/stabilized/green/on_remove() + to_chat(owner, "You feel more like yourself.") + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + originalDNA.transfer_identity(H) + H.real_name = originalname + H.update_name_tag(originalname) // monkestation edit: name tags + H.updateappearance(mutcolor_update=1) + +/datum/status_effect/brokenpeace + id = "brokenpeace" + duration = 1200 + alert_type = null + +/datum/status_effect/pinkdamagetracker + id = "pinkdamagetracker" + duration = -1 + alert_type = null + var/damage = 0 + var/lasthealth + +/datum/status_effect/pinkdamagetracker/tick() + if((lasthealth - owner.health) > 0) + damage += (lasthealth - owner.health) + lasthealth = owner.health + +/datum/status_effect/stabilized/pink + id = "stabilizedpink" + colour = "pink" + /// List of weakrefs to mobs we have pacified + var/list/mobs = list() + /// Name of our faction + var/faction_name = "" + +/datum/status_effect/stabilized/pink/on_apply() + faction_name = FACTION_PINK_EXTRACT(owner) + owner.faction |= faction_name + to_chat(owner, "[linked_extract] pulses, generating a fragile aura of peace.") + return ..() + +/datum/status_effect/stabilized/pink/tick() + update_nearby_mobs() + var/has_faction = FALSE + for (var/check_faction in owner.faction) + if(check_faction != faction_name) + continue + has_faction = TRUE + break + + if(has_faction) + if(owner.has_status_effect(/datum/status_effect/brokenpeace)) + owner.faction -= faction_name + to_chat(owner, span_userdanger("The peace has been broken! Hostile creatures will now react to you!")) + else if(!owner.has_status_effect(/datum/status_effect/brokenpeace)) + to_chat(owner, "[linked_extract] pulses, generating a fragile aura of peace.") + owner.faction |= faction_name + return ..() + +/// Pacifies mobs you can see and unpacifies mobs you no longer can +/datum/status_effect/stabilized/pink/proc/update_nearby_mobs() + var/list/visible_things = view(7, get_turf(owner)) + // Unpacify far away or offended mobs + for(var/datum/weakref/weak_mob as anything in mobs) + var/mob/living/beast = weak_mob.resolve() + if(isnull(beast)) + mobs -= weak_mob + continue + var/datum/status_effect/pinkdamagetracker/damage_tracker = beast.has_status_effect(/datum/status_effect/pinkdamagetracker) + if(istype(damage_tracker) && damage_tracker.damage > 0) + damage_tracker.damage = 0 + owner.apply_status_effect(/datum/status_effect/brokenpeace) + return // No point continuing from here if we're going to end the effect + if(beast in visible_things) + continue + beast.faction -= faction_name + beast.remove_status_effect(/datum/status_effect/pinkdamagetracker) + mobs -= weak_mob + + // Pacify nearby mobs + for(var/mob/living/beast in visible_things) + if(!isanimal_or_basicmob(beast)) + continue + var/datum/weakref/weak_mob = WEAKREF(beast) + if(weak_mob in mobs) + continue + mobs += weak_mob + beast.apply_status_effect(/datum/status_effect/pinkdamagetracker) + beast.faction |= faction_name + +/datum/status_effect/stabilized/pink/on_remove() + for(var/datum/weakref/weak_mob as anything in mobs) + var/mob/living/beast = weak_mob.resolve() + if(isnull(beast)) + continue + beast.faction -= faction_name + beast.remove_status_effect(/datum/status_effect/pinkdamagetracker) + owner.faction -= faction_name + +/datum/status_effect/stabilized/oil + id = "stabilizedoil" + colour = "oil" + +/datum/status_effect/stabilized/oil/tick() + if(owner.stat == DEAD) + explosion(owner, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 4, flame_range = 5, explosion_cause = src) + return ..() + +/datum/status_effect/stabilized/oil/get_examine_text() + return "[owner.p_they(TRUE)] smell[owner.p_s()] of sulfur and oil!" + +/// How much damage is dealt per healing done for the stabilized back. +/// This multiplier is applied to prevent two people from converting each other's damage away. +#define DRAIN_DAMAGE_MULTIPLIER 1.2 + +/datum/status_effect/stabilized/black + id = "stabilizedblack" + colour = "black" + /// How much we heal per tick (also how much we damage per tick times DRAIN_DAMAGE_MULTIPLIER). + var/heal_amount = 1 + /// Weakref to the mob we're currently draining every tick. + var/datum/weakref/draining_ref + +/datum/status_effect/stabilized/black/on_apply() + RegisterSignal(owner, COMSIG_MOVABLE_SET_GRAB_STATE, PROC_REF(on_grab)) + return ..() + +/datum/status_effect/stabilized/black/on_remove() + UnregisterSignal(owner, COMSIG_MOVABLE_SET_GRAB_STATE) + return ..() + +/// Whenever we grab someone by the neck, set "draining" to a weakref of them. +/datum/status_effect/stabilized/black/proc/on_grab(mob/living/source, new_state) + SIGNAL_HANDLER + + if(new_state < GRAB_KILL || !isliving(source.pulling)) + draining_ref = null + return + + var/mob/living/draining = source.pulling + if(draining.stat == DEAD) + return + + draining_ref = WEAKREF(draining) + to_chat(owner, span_boldnotice("You feel your hands melt around [draining]'s neck as you start to drain [draining.p_them()] of [draining.p_their()] life!")) + to_chat(draining, span_userdanger("[owner]'s hands melt around your neck as you can feel your life starting to drain away!")) + +/datum/status_effect/stabilized/black/get_examine_text() + var/mob/living/draining = draining_ref?.resolve() + if(!draining) + return null + + return "[owner.p_they(TRUE)] [owner.p_are()] draining health from [draining]!" + +/datum/status_effect/stabilized/black/tick() + if(owner.grab_state < GRAB_KILL || !IS_WEAKREF_OF(owner.pulling, draining_ref)) + return + + var/mob/living/drained = draining_ref.resolve() + if(drained.stat == DEAD) + to_chat(owner, "[drained] is dead, you cannot drain anymore life from them!") + draining_ref = null + return + + var/list/healing_types = list() + if(owner.getBruteLoss() > 0) + healing_types += BRUTE + if(owner.getFireLoss() > 0) + healing_types += BURN + if(owner.getToxLoss() > 0) + healing_types += TOX + if(owner.getCloneLoss() > 0) + healing_types += CLONE + + if(length(healing_types)) + owner.heal_damage_type(heal_amount, damagetype = pick(healing_types)) + + owner.adjust_nutrition(3) + drained.adjustCloneLoss(heal_amount * DRAIN_DAMAGE_MULTIPLIER) + return ..() + +#undef DRAIN_DAMAGE_MULTIPLIER + +/datum/status_effect/stabilized/lightpink + id = "stabilizedlightpink" + colour = "light pink" + +/datum/status_effect/stabilized/lightpink/on_apply() + owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/lightpink) + ADD_TRAIT(owner, TRAIT_PACIFISM, STABILIZED_LIGHT_PINK_TRAIT) + return ..() + +/datum/status_effect/stabilized/lightpink/tick() + for(var/mob/living/carbon/human/H in range(1, get_turf(owner))) + if(H != owner && H.stat != DEAD && H.health <= 0 && !H.reagents.has_reagent(/datum/reagent/medicine/epinephrine)) + to_chat(owner, "[linked_extract] pulses in sync with [H]'s heartbeat, trying to keep [H.p_them()] alive.") + H.reagents.add_reagent(/datum/reagent/medicine/epinephrine,5) + return ..() + +/datum/status_effect/stabilized/lightpink/on_remove() + owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/lightpink) + REMOVE_TRAIT(owner, TRAIT_PACIFISM, STABILIZED_LIGHT_PINK_TRAIT) + +/datum/status_effect/stabilized/adamantine + id = "stabilizedadamantine" + colour = "adamantine" + +/datum/status_effect/stabilized/adamantine/get_examine_text() + return "[owner.p_they(TRUE)] [owner.p_have()] strange metallic coating on [owner.p_their()] skin." + +/datum/status_effect/stabilized/gold + id = "stabilizedgold" + colour = "gold" + var/mob/living/simple_animal/familiar + +/datum/status_effect/stabilized/gold/tick() + var/obj/item/slimecross/stabilized/gold/linked = linked_extract + if(QDELETED(familiar)) + familiar = new linked.mob_type(get_turf(owner.loc)) + familiar.name = linked.mob_name + if(isanimal(familiar)) + familiar.del_on_death = TRUE + else //we are a basicmob otherwise + var/mob/living/basic/basic_familiar = familiar + basic_familiar.basic_mob_flags |= DEL_ON_DEATH + familiar.befriend(owner) + familiar.copy_languages(owner, LANGUAGE_MASTER) + if(linked.saved_mind) + linked.saved_mind.transfer_to(familiar) + familiar.update_atom_languages() + familiar.ckey = linked.saved_mind.key + else + if(familiar.mind) + linked.saved_mind = familiar.mind + return ..() + +/datum/status_effect/stabilized/gold/on_remove() + if(familiar) + qdel(familiar) + +/datum/status_effect/stabilized/adamantine/on_apply() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.physiology.damage_resistance += 5 + return ..() + +/datum/status_effect/stabilized/adamantine/on_remove() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.physiology.damage_resistance -= 5 + +/datum/status_effect/stabilized/rainbow + id = "stabilizedrainbow" + colour = "rainbow" + +/* monkestation edit: replaced in [monkestation\code\modules\slimecore\crossbreeding\stabilized.dm] +/datum/status_effect/stabilized/rainbow/tick() + if(owner.health <= 0) + var/obj/item/slimecross/stabilized/rainbow/X = linked_extract + if(istype(X)) + if(X.regencore) + X.regencore.afterattack(owner,owner,TRUE) + X.regencore = null + owner.visible_message("[owner] flashes a rainbow of colors, and [owner.p_their()] skin is coated in a milky regenerative goo!") + qdel(src) + qdel(linked_extract) + return ..() +*/ diff --git a/code/modules/research/xenobiology/crossbreeding/_weapons.dm b/code/modules/research/xenobiology/crossbreeding/_weapons.dm new file mode 100644 index 0000000000000..794d631c8ce54 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/_weapons.dm @@ -0,0 +1,134 @@ +/* +Slimecrossing Weapons + Weapons added by the slimecrossing system. + Collected here for clarity. +*/ + +//Boneblade - Burning Green +/obj/item/melee/arm_blade/slime + name = "slimy boneblade" + desc = "What remains of the bones in your arm. Incredibly sharp, and painful for both you and your opponents." + force = 15 + force_string = "painful" + +/obj/item/melee/arm_blade/slime/attack(mob/living/L, mob/user) + . = ..() + if(prob(20)) + user.emote("scream") + +//Rainbow knife - Burning Rainbow +/obj/item/knife/rainbowknife + name = "rainbow knife" + desc = "A strange, transparent knife which constantly shifts color. It hums slightly when moved." + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + icon_state = "rainbowknife" + inhand_icon_state = "rainbowknife" + force = 15 + throwforce = 15 + damtype = BRUTE + +/obj/item/knife/rainbowknife/afterattack(atom/O, mob/user, proximity) + if(proximity && isliving(O)) + damtype = pick(BRUTE, BURN, TOX, OXY, CLONE) + switch(damtype) + if(BRUTE) + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb_continuous = string_list(list("slashes", "slices", "cuts")) + attack_verb_simple = string_list(list("slash", "slice", "cut")) + if(BURN) + hitsound = 'sound/weapons/sear.ogg' + attack_verb_continuous = string_list(list("burns", "singes", "heats")) + attack_verb_simple = string_list(list("burn", "singe", "heat")) + if(TOX) + hitsound = 'sound/weapons/pierce.ogg' + attack_verb_continuous = string_list(list("poisons", "doses", "toxifies")) + attack_verb_simple = string_list(list("poison", "dose", "toxify")) + if(OXY) + hitsound = 'sound/effects/space_wind.ogg' + attack_verb_continuous = string_list(list("suffocates", "winds", "vacuums")) + attack_verb_simple = string_list(list("suffocate", "wind", "vacuum")) + if(CLONE) + hitsound = 'sound/items/geiger/ext1.ogg' + attack_verb_continuous = string_list(list("irradiates", "mutates", "maligns")) + attack_verb_simple = string_list(list("irradiate", "mutate", "malign")) + return ..() + +//Adamantine shield - Chilling Adamantine +/obj/item/shield/adamantineshield + name = "adamantine shield" + desc = "A gigantic shield made of solid adamantium." + icon = 'icons/obj/weapons/shields.dmi' + icon_state = "adamshield" + inhand_icon_state = "adamshield" + w_class = WEIGHT_CLASS_HUGE + armor_type = /datum/armor/shield_adamantineshield + slot_flags = ITEM_SLOT_BACK + block_chance = 75 + force = 0 + throw_range = 1 //How far do you think you're gonna throw a solid crystalline shield...? + throw_speed = 2 + attack_verb_continuous = list("bashes", "pounds", "slams") + attack_verb_simple = list("bash", "pound", "slam") + item_flags = SLOWS_WHILE_IN_HAND + breakable_by_damage = FALSE + +/datum/armor/shield_adamantineshield + melee = 50 + bullet = 50 + laser = 50 + bomb = 30 + fire = 80 + acid = 70 + +/obj/item/shield/adamantineshield/Initialize(mapload) + . = ..() + AddComponent(/datum/component/two_handed, require_twohands=TRUE, force_wielded=15) + +//Bloodchiller - Chilling Green +/obj/item/gun/magic/bloodchill + name = "blood chiller" + desc = "A horrifying weapon made of your own bone and blood vessels. It shoots slowing globules of your own blood. Ech." + icon = 'icons/obj/xenobiology/slimecrossing.dmi' + icon_state = "bloodgun" + inhand_icon_state = "bloodgun" + lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + item_flags = ABSTRACT | DROPDEL + w_class = WEIGHT_CLASS_HUGE + slot_flags = NONE + antimagic_flags = NONE + force = 5 + max_charges = 1 //Recharging costs blood. + recharge_rate = 1 + ammo_type = /obj/item/ammo_casing/magic/bloodchill + fire_sound = 'sound/effects/attackblob.ogg' + +/obj/item/gun/magic/bloodchill/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT) + +/obj/item/gun/magic/bloodchill/process(seconds_per_tick) + charge_timer += seconds_per_tick + if(charge_timer < recharge_rate || charges >= max_charges) + return FALSE + charge_timer = 0 + var/mob/living/M = loc + if(istype(M) && M.blood_volume >= 20) + charges++ + M.blood_volume -= 20 + if(charges == 1) + recharge_newshot() + return TRUE + +/obj/item/ammo_casing/magic/bloodchill + projectile_type = /obj/projectile/magic/bloodchill + +/obj/projectile/magic/bloodchill + name = "blood ball" + icon_state = "pulse0_bl" + hitsound = 'sound/effects/splat.ogg' + +/obj/projectile/magic/bloodchill/on_hit(mob/living/target, blocked = 0, pierce_hit) + . = ..() + if(isliving(target)) + target.apply_status_effect(/datum/status_effect/bloodchill) diff --git a/code/modules/research/xenobiology/crossbreeding/burning.dm b/code/modules/research/xenobiology/crossbreeding/burning.dm new file mode 100644 index 0000000000000..2aeb81c977e24 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/burning.dm @@ -0,0 +1,321 @@ +/* +Burning extracts: + Have a unique, primarily offensive effect when + filled with 10u plasma and activated in-hand. +*/ +/obj/item/slimecross/burning + name = "burning extract" + desc = "It's boiling over with barely-contained energy." + effect = "burning" + icon_state = "burning" + +/obj/item/slimecross/burning/Initialize(mapload) + . = ..() + create_reagents(10, INJECTABLE | DRAWABLE) + +/obj/item/slimecross/burning/attack_self(mob/user) + if(!reagents.has_reagent(/datum/reagent/toxin/plasma,10)) + to_chat(user, "This extract needs to be full of plasma to activate!") + return + reagents.remove_reagent(/datum/reagent/toxin/plasma,10) + to_chat(user, "You squeeze the extract, and it absorbs the plasma!") + playsound(src, 'sound/effects/bubbles.ogg', 50, TRUE) + playsound(src, 'sound/magic/fireball.ogg', 50, TRUE) + do_effect(user) + +/obj/item/slimecross/burning/proc/do_effect(mob/user) //If, for whatever reason, you don't want to delete the extract, don't do ..() + qdel(src) + return + +/obj/item/slimecross/burning/grey + colour = "grey" + effect_desc = "Creates a hungry and speedy slime that will love you forever." + +/obj/item/slimecross/burning/grey/do_effect(mob/user) + var/mob/living/basic/slime/S = new(get_turf(user)) + S.visible_message(span_danger("A baby slime emerges from [src], and it nuzzles [user] before burbling hungrily!")) + SEND_SIGNAL(S, COMSIG_FRIENDSHIP_CHANGE, user, 110) + S.bodytemperature = T0C + 400 //We gonna step on the gas. + ..() + +/obj/item/slimecross/burning/orange + colour = "orange" + effect_desc = "Expels pepperspray in a radius when activated." + +/obj/item/slimecross/burning/orange/do_effect(mob/user) + user.visible_message(span_danger("[src] boils over with a caustic gas!")) + var/datum/reagents/tmp_holder = new/datum/reagents(100) + tmp_holder.add_reagent(/datum/reagent/consumable/condensedcapsaicin, 100) + + var/datum/effect_system/fluid_spread/smoke/chem/smoke = new + smoke.set_up(7, holder = src, location = get_turf(user), carry = tmp_holder) + smoke.start(log = TRUE) + ..() + +/obj/item/slimecross/burning/purple + colour = "purple" + effect_desc = "Creates a clump of invigorating gel, it has healing properties and makes you feel good." + +/obj/item/slimecross/burning/purple/do_effect(mob/user) + user.visible_message("[src] fills with a bubbling liquid!") + new /obj/item/slimecrossbeaker/autoinjector/slimestimulant(get_turf(user)) + ..() + +/obj/item/slimecross/burning/blue + colour = "blue" + effect_desc = "Freezes the floor around you and chills nearby people." + +/obj/item/slimecross/burning/blue/do_effect(mob/user) + user.visible_message(span_danger("[src] flash-freezes the area!")) + for(var/turf/open/T in range(3, get_turf(user))) + T.MakeSlippery(TURF_WET_PERMAFROST, min_wet_time = 10, wet_time_to_add = 5) + for(var/mob/living/carbon/M in range(5, get_turf(user))) + if(M != user) + M.bodytemperature = BODYTEMP_COLD_DAMAGE_LIMIT + 10 //Not quite cold enough to hurt. + to_chat(M, span_danger("You feel a chill run down your spine, and the floor feels a bit slippery with frost...")) + ..() + +/obj/item/slimecross/burning/metal + colour = "metal" + effect_desc = "Instantly destroys walls around you." + +/obj/item/slimecross/burning/metal/do_effect(mob/user) +//monkestation edit start + var/turf/our_turf = get_turf(src) + if(GLOB.clock_ark && on_reebe(our_turf) && get_dist(our_turf, GLOB.clock_ark) <= ARK_TURF_DESTRUCTION_BLOCK_RANGE) + balloon_alert(user, "a near by energy source is stopping \the [src] from activating!") + return FALSE +//monkestation edit end + for(var/turf/closed/wall/W in range(1, our_turf)) //monkestation edit: replaces get_turf(src) with our_turf + W.dismantle_wall(1) + playsound(W, 'sound/effects/break_stone.ogg', 50, TRUE) + user.visible_message(span_danger("[src] pulses violently, and shatters the walls around it!")) + ..() + +/obj/item/slimecross/burning/yellow + colour = "yellow" + effect_desc = "Electrocutes people near you." + +/obj/item/slimecross/burning/yellow/do_effect(mob/user) + user.visible_message(span_danger("[src] explodes into an electrical field!")) + playsound(get_turf(src), 'sound/weapons/zapbang.ogg', 50, TRUE) + for(var/mob/living/M in range(4,get_turf(user))) + if(M != user) + var/mob/living/carbon/C = M + if(istype(C)) + C.electrocute_act(25,src) + else + M.adjustFireLoss(25) + to_chat(M, span_danger("You feel a sharp electrical pulse!")) + ..() + +/obj/item/slimecross/burning/darkpurple + colour = "dark purple" + effect_desc = "Creates a cloud of plasma." + +/obj/item/slimecross/burning/darkpurple/do_effect(mob/user) + user.visible_message(span_danger("[src] sublimates into a cloud of plasma!")) + var/turf/T = get_turf(user) + T.atmos_spawn_air("plasma=60") + ..() + +/obj/item/slimecross/burning/darkblue + colour = "dark blue" + effect_desc = "Expels a burst of chilling smoke while also filling you with regenerative jelly." + +/obj/item/slimecross/burning/darkblue/do_effect(mob/user) + user.visible_message(span_danger("[src] releases a burst of chilling smoke!")) + var/datum/reagents/tmp_holder = new/datum/reagents(100) + tmp_holder.add_reagent(/datum/reagent/consumable/frostoil, 40) + user.reagents.add_reagent(/datum/reagent/medicine/regen_jelly, 10) + var/datum/effect_system/fluid_spread/smoke/chem/smoke = new + smoke.set_up(7, holder = src, location = get_turf(user), carry = tmp_holder) + smoke.start(log = TRUE) + ..() + +/obj/item/slimecross/burning/silver + colour = "silver" + effect_desc = "Creates a few pieces of slime jelly laced food." + +/obj/item/slimecross/burning/silver/do_effect(mob/user) + var/amount = rand(3,6) + var/list/turfs = list() + for(var/turf/open/T in range(1,get_turf(user))) + turfs += T + for(var/i in 1 to amount) + var/path = get_random_food() + var/obj/item/food/food = new path(pick(turfs)) + food.reagents.add_reagent(/datum/reagent/toxin/slimejelly,5) //Oh god it burns + ADD_TRAIT(food, TRAIT_FOOD_SILVER, INNATE_TRAIT) + if(prob(50)) + food.desc += " It smells strange..." + user.visible_message(span_danger("[src] produces a few pieces of food!")) + ..() + +/obj/item/slimecross/burning/bluespace + colour = "bluespace" + effect_desc = "Teleports anyone directly next to you." + +/obj/item/slimecross/burning/bluespace/do_effect(mob/user) + user.visible_message(span_danger("[src] sparks, and lets off a shockwave of bluespace energy!")) + for(var/mob/living/L in range(1, get_turf(user))) + if(L != user) + do_teleport(L, get_turf(L), 6, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_BLUESPACE) //Somewhere between the effectiveness of fake and real BS crystal + new /obj/effect/particle_effect/sparks(get_turf(L)) + playsound(get_turf(L), SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + ..() + +/obj/item/slimecross/burning/sepia + colour = "sepia" + effect_desc = "Turns into a special camera that rewinds time when used." + +/obj/item/slimecross/burning/sepia/do_effect(mob/user) + user.visible_message("[src] shapes itself into a camera!") + new /obj/item/camera/rewind(get_turf(user)) + ..() + +/obj/item/slimecross/burning/cerulean + colour = "cerulean" + effect_desc = "Produces an extract cloning potion, which copies an extract, as well as its extra uses." + +/obj/item/slimecross/burning/cerulean/do_effect(mob/user) + user.visible_message("[src] produces a potion!") + new /obj/item/slimepotion/extract_cloner(get_turf(user)) + ..() + +/obj/item/slimecross/burning/pyrite + colour = "pyrite" + effect_desc = "Shatters all lights in the current room." + +/obj/item/slimecross/burning/pyrite/do_effect(mob/user) + user.visible_message(span_danger("[src] releases a colorful wave of energy, which shatters the lights!")) + var/area/A = get_area(user.loc) + for(var/obj/machinery/light/L in A) //Shamelessly copied from the APC effect. + L.on = TRUE + L.break_light_tube() + stoplag() + ..() + +/obj/item/slimecross/burning/red + colour = "red" + effect_desc = "Makes nearby slimes rabid, and they'll also attack their friends." + +/obj/item/slimecross/burning/red/do_effect(mob/user) + user.visible_message(span_danger("[src] pulses a hazy red aura for a moment, which wraps around [user]!")) + for(var/mob/living/basic/slime/S in view(7, get_turf(user))) + /* + if(user in S.Friends) + var/friendliness = S.Friends[user] + S.clear_friends() + S.set_friendship(user, friendliness) + else + S.clear_friends() + */ + ADD_TRAIT(S, TRAIT_SLIME_RABID, "burning-red") + S.visible_message(span_danger("The [S] is driven into a dangerous frenzy!")) + ..() + +/obj/item/slimecross/burning/green + colour = "green" + effect_desc = "The user gets a dull arm blade in the hand it is used in." + +/obj/item/slimecross/burning/green/do_effect(mob/user) + var/which_hand = "l_hand" + if(!(user.active_hand_index % 2)) + which_hand = "r_hand" + var/mob/living/L = user + if(!istype(user)) + return + var/obj/item/held = L.get_active_held_item() //This should be itself, but just in case... + L.dropItemToGround(held) + var/obj/item/melee/arm_blade/slime/blade = new(user) + if(!L.put_in_hands(blade)) + qdel(blade) + user.visible_message("[src] melts onto [user]'s arm, boiling the flesh horribly!") + else + user.visible_message(span_danger("[src] sublimates the flesh around [user]'s arm, transforming the bone into a gruesome blade!")) + user.emote("scream") + L.apply_damage(30,BURN,which_hand) + ..() + +/obj/item/slimecross/burning/pink + colour = "pink" + effect_desc = "Creates a beaker of synthpax." + +/obj/item/slimecross/burning/pink/do_effect(mob/user) + user.visible_message("[src] shrinks into a small, gel-filled pellet!") + new /obj/item/slimecrossbeaker/pax(get_turf(user)) + ..() + +/obj/item/slimecross/burning/gold + colour = "gold" + effect_desc = "Creates a gank squad of monsters that are friendly to the user." + +/obj/item/slimecross/burning/gold/do_effect(mob/user) + user.visible_message(span_danger("[src] shudders violently, and summons an army for [user]!")) + for(var/i in 1 to 3) //Less than gold normally does, since it's safer and faster. + var/mob/living/spawned_mob = create_random_mob(get_turf(user), HOSTILE_SPAWN) + spawned_mob.faction |= "[REF(user)]" + if(prob(50)) + for(var/j in 1 to rand(1, 3)) + step(spawned_mob, pick(NORTH,SOUTH,EAST,WEST)) + ..() + +/obj/item/slimecross/burning/oil + colour = "oil" + effect_desc = "Creates an explosion after a few seconds." + +/obj/item/slimecross/burning/oil/do_effect(mob/user) + user.visible_message("[user] activates [src]. It's going to explode!"), span_danger("You activate [src]. It crackles in anticipation") + addtimer(CALLBACK(src, PROC_REF(boom)), 50) + +/// Inflicts a blastwave upon every mob within a small radius. +/obj/item/slimecross/burning/oil/proc/boom() + var/turf/T = get_turf(src) + playsound(T, 'sound/effects/explosion2.ogg', 200, TRUE) + for(var/mob/living/target in range(2, T)) + new /obj/effect/temp_visual/explosion(get_turf(target)) + SSexplosions.med_mov_atom += target + qdel(src) + +/obj/item/slimecross/burning/black + colour = "black" + effect_desc = "Gives the user a one-time use slime transformation ability. They can transform back at will and do not lose any items." // monkestation edit: same here + +/obj/item/slimecross/burning/black/do_effect(mob/user) + if(!isliving(user)) + return + user.visible_message(span_danger("[user] absorbs \the [src]!")) // monkestation edit: slight change to reflect the cast removal + var/datum/action/cooldown/spell/shapeshift/slime_form/transform = new(user.mind || user) + transform.remove_on_restore = TRUE + transform.Grant(user) + //transform.cast(user) // monkestation removal: embrace the choice (it was broken anyway for whatever reason) + return ..() + +/obj/item/slimecross/burning/lightpink + colour = "light pink" + effect_desc = "Paxes everyone in sight." + +/obj/item/slimecross/burning/lightpink/do_effect(mob/user) + user.visible_message(span_danger("[src] lets off a hypnotizing pink glow!")) + for(var/mob/living/carbon/C in view(7, get_turf(user))) + C.reagents.add_reagent(/datum/reagent/pax,5) + ..() + +/obj/item/slimecross/burning/adamantine + colour = "adamantine" + effect_desc = "Creates a mighty adamantine shield." + +/obj/item/slimecross/burning/adamantine/do_effect(mob/user) + user.visible_message("[src] crystallizes into a large shield!") + new /obj/item/shield/adamantineshield(get_turf(user)) + ..() + +/obj/item/slimecross/burning/rainbow + colour = "rainbow" + effect_desc = "Creates the Rainbow Knife, a kitchen knife that deals random types of damage." + +/obj/item/slimecross/burning/rainbow/do_effect(mob/user) + user.visible_message("[src] flattens into a glowing rainbow blade.") + new /obj/item/knife/rainbowknife(get_turf(user)) + ..() diff --git a/code/modules/research/xenobiology/crossbreeding/charged.dm b/code/modules/research/xenobiology/crossbreeding/charged.dm new file mode 100644 index 0000000000000..178320703a845 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/charged.dm @@ -0,0 +1,281 @@ +/* +Charged extracts: + Have a unique, effect when filled with + 10u plasma and activated in-hand, related to their + normal extract effect. +*/ +/obj/item/slimecross/charged + name = "charged extract" + desc = "It sparks with electric power." + effect = "charged" + icon_state = "charged" + +/obj/item/slimecross/charged/Initialize(mapload) + . = ..() + create_reagents(10, INJECTABLE | DRAWABLE) + +/obj/item/slimecross/charged/attack_self(mob/user) + if(!reagents.has_reagent(/datum/reagent/toxin/plasma,10)) + to_chat(user, "This extract needs to be full of plasma to activate!") + return + reagents.remove_reagent(/datum/reagent/toxin/plasma,10) + to_chat(user, "You squeeze the extract, and it absorbs the plasma!") + playsound(src, 'sound/effects/bubbles.ogg', 50, TRUE) + playsound(src, 'sound/effects/light_flicker.ogg', 50, TRUE) + do_effect(user) + +/obj/item/slimecross/charged/proc/do_effect(mob/user) //If, for whatever reason, you don't want to delete the extract, don't do ..() + qdel(src) + return + +/obj/item/slimecross/charged/grey + colour = "grey" + effect_desc = "Produces a slime reviver potion, which revives dead slimes." + +/obj/item/slimecross/charged/grey/do_effect(mob/user) + new /obj/item/slimepotion/slime_reviver(get_turf(user)) + user.visible_message("[src] distills into a potion!") + ..() + +/obj/item/slimecross/charged/orange + colour = "orange" + effect_desc = "Instantly makes a large burst of flame for a moment." + +/obj/item/slimecross/charged/orange/do_effect(mob/user) + var/turf/targetturf = get_turf(user) + for(var/turf/turf as anything in RANGE_TURFS(5,targetturf)) + if(!locate(/obj/effect/hotspot) in turf) + new /obj/effect/hotspot(turf) + ..() + +/obj/item/slimecross/charged/purple + colour = "purple" + effect_desc = "Creates a packet of omnizine." + +/obj/item/slimecross/charged/purple/do_effect(mob/user) + new /obj/item/slimecrossbeaker/omnizine(get_turf(user)) + user.visible_message("[src] sparks, and floods with a regenerative solution!") + ..() + +/obj/item/slimecross/charged/blue + colour = "blue" + effect_desc = "Creates a potion that neuters the mutation chance of a slime, which passes on to new generations." + +/obj/item/slimecross/charged/blue/do_effect(mob/user) + new /obj/item/slimepotion/slime/chargedstabilizer(get_turf(user)) + user.visible_message("[src] distills into a potion!") + ..() + +/obj/item/slimecross/charged/metal + colour = "metal" + effect_desc = "Produces a bunch of metal and plasteel." + +/obj/item/slimecross/charged/metal/do_effect(mob/user) + new /obj/item/stack/sheet/iron(get_turf(user), 25) + new /obj/item/stack/sheet/plasteel(get_turf(user), 10) + user.visible_message("[src] grows into a plethora of metals!") + ..() + +/obj/item/slimecross/charged/yellow + colour = "yellow" + effect_desc = "Creates a hypercharged slime cell battery, which has high capacity but takes longer to recharge." + +/obj/item/slimecross/charged/yellow/do_effect(mob/user) + new /obj/item/stock_parts/cell/emproof/slime/hypercharged(user.drop_location()) // monke edit: make hypercharged slime cells EMP-proof, by changing their parent from cell/high to cell/emproof + user.visible_message("[src] sparks violently, and swells with electric power!") + ..() + +/obj/item/slimecross/charged/darkpurple + colour = "dark purple" + effect_desc = "Creates several sheets of plasma." + +/obj/item/slimecross/charged/darkpurple/do_effect(mob/user) + new /obj/item/stack/sheet/mineral/plasma(get_turf(user), 10) + user.visible_message("[src] produces a large amount of plasma!") + ..() + +/obj/item/slimecross/charged/darkblue + colour = "dark blue" + effect_desc = "Produces a pressure proofing potion." + +/obj/item/slimecross/charged/darkblue/do_effect(mob/user) + new /obj/item/slimepotion/spaceproof(get_turf(user)) + user.visible_message("[src] distills into a potion!") + ..() + +/obj/item/slimecross/charged/silver + colour = "silver" + effect_desc = "Creates a slime cake and some drinks." + +/obj/item/slimecross/charged/silver/do_effect(mob/user) + new /obj/item/food/cake/slimecake(get_turf(user)) + for(var/i in 1 to 10) + var/drink_type = get_random_drink() + new drink_type(get_turf(user)) + user.visible_message("[src] produces a party's worth of cake and drinks!") + ..() + +/obj/item/slimecross/charged/bluespace + colour = "bluespace" + effect_desc = "Makes a bluespace polycrystal." + +/obj/item/slimecross/charged/bluespace/do_effect(mob/user) + new /obj/item/stack/sheet/bluespace_crystal(get_turf(user), 10) + user.visible_message("[src] produces several sheets of polycrystal!") + ..() + +/obj/item/slimecross/charged/sepia + colour = "sepia" + effect_desc = "Creates a camera obscura." + +/obj/item/slimecross/charged/sepia/do_effect(mob/user) + new /obj/item/camera/spooky(get_turf(user)) + user.visible_message("[src] flickers in a strange, ethereal manner, and produces a camera!") + ..() + +/obj/item/slimecross/charged/cerulean + colour = "cerulean" + effect_desc = "Creates an extract enhancer, giving whatever it's used on five more uses." + +/obj/item/slimecross/charged/cerulean/do_effect(mob/user) + new /obj/item/slimepotion/enhancer/max(get_turf(user)) + user.visible_message("[src] distills into a potion!") + ..() + +/obj/item/slimecross/charged/pyrite + colour = "pyrite" + effect_desc = "Creates bananium. Oh no." + +/obj/item/slimecross/charged/pyrite/do_effect(mob/user) + new /obj/item/stack/sheet/mineral/bananium(get_turf(user), 10) + user.visible_message("[src] solidifies with a horrifying banana stench!") + ..() + +/obj/item/slimecross/charged/red + colour = "red" + effect_desc = "Produces a lavaproofing potion" + +/obj/item/slimecross/charged/red/do_effect(mob/user) + new /obj/item/slimepotion/lavaproof(get_turf(user)) + user.visible_message("[src] distills into a potion!") + ..() + +/obj/item/slimecross/charged/green + colour = "green" + effect_desc = "Lets you choose what slime species you want to be." + +/obj/item/slimecross/charged/green/do_effect(mob/user) + var/mob/living/carbon/human/human_user = user + if(!istype(human_user)) + to_chat(user, "You must be a humanoid to use this!") + return + var/list/choice_list = list() + for(var/datum/species/species_type as anything in subtypesof(/datum/species/jelly)) + choice_list[initial(species_type.name)] = species_type + var/racechoice = tgui_input_list(human_user, "Choose your slime subspecies", "Slime Selection", sort_list(choice_list)) + if(isnull(racechoice)) + to_chat(user, "You decide not to become a slime for now.") + return + if(!user.can_perform_action(src)) + return + human_user.set_species(choice_list[racechoice], icon_update=1) + human_user.visible_message("[human_user] suddenly shifts form as [src] dissolves into [human_user.p_their()] skin!") + ..() + +/obj/item/slimecross/charged/pink + colour = "pink" + effect_desc = "Produces a... lovepotion..." + +/obj/item/slimecross/charged/pink/do_effect(mob/user) + new /obj/item/slimepotion/lovepotion(get_turf(user)) + user.visible_message("[src] distills into a potion!") + ..() + +/obj/item/slimecross/charged/gold + colour = "gold" + effect_desc = "Slowly spawns 10 hostile monsters." + var/max_spawn = 10 + var/spawned = 0 + +/obj/item/slimecross/charged/gold/do_effect(mob/user) + user.visible_message("[src] starts shuddering violently!") + addtimer(CALLBACK(src, PROC_REF(startTimer)), 50) + +/obj/item/slimecross/charged/gold/proc/startTimer() + START_PROCESSING(SSobj, src) + +/obj/item/slimecross/charged/gold/process() + visible_message("[src] lets off a spark, and produces a living creature!") + new /obj/effect/particle_effect/sparks(get_turf(src)) + playsound(get_turf(src), SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + create_random_mob(get_turf(src), HOSTILE_SPAWN) + spawned++ + if(spawned >= max_spawn) + visible_message("[src] collapses into a puddle of goo.") + qdel(src) + +/obj/item/slimecross/charged/gold/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/slimecross/charged/oil + colour = "oil" + effect_desc = "Creates an explosion after a few seconds." + +/obj/item/slimecross/charged/oil/do_effect(mob/user) + user.visible_message(span_danger("[src] begins to shake with rapidly increasing force!")) + addtimer(CALLBACK(src, PROC_REF(boom)), 50) + +/obj/item/slimecross/charged/oil/proc/boom() + explosion(src, devastation_range = 2, heavy_impact_range = 3, light_impact_range = 4, explosion_cause = src) //Much smaller effect than normal oils, but devastatingly strong where it does hit. + qdel(src) + +/obj/item/slimecross/charged/black + colour = "black" + effect_desc = "Randomizes the user's species." + +/obj/item/slimecross/charged/black/do_effect(mob/user) + var/mob/living/carbon/human/H = user + if(!istype(H)) + to_chat(user, "You have to be able to have a species to get your species changed.") + return + var/list/allowed_species = list() + for(var/stype in subtypesof(/datum/species)) + var/datum/species/X = stype + if(initial(X.changesource_flags) & SLIME_EXTRACT) + allowed_species += stype + + var/datum/species/changed = pick(allowed_species) + if(changed) + H.set_species(changed, icon_update = 1) + to_chat(H, span_danger("You feel very different!")) + ..() + +/obj/item/slimecross/charged/lightpink + colour = "light pink" + effect_desc = "Produces a pacification potion, which works on monsters and humanoids." + +/obj/item/slimecross/charged/lightpink/do_effect(mob/user) + new /obj/item/slimepotion/peacepotion(get_turf(user)) + user.visible_message("[src] distills into a potion!") + ..() + +/obj/item/slimecross/charged/adamantine + colour = "adamantine" + effect_desc = "Creates a completed golem shell." + +/obj/item/slimecross/charged/adamantine/do_effect(mob/user) + user.visible_message("[src] produces a fully formed golem shell!") + new /obj/effect/mob_spawn/ghost_role/human/golem/servant(get_turf(src), /datum/species/golem/adamantine, user) + ..() + +/obj/item/slimecross/charged/rainbow + colour = "rainbow" + effect_desc = "Produces three living slimes of random colors." + +/obj/item/slimecross/charged/rainbow/do_effect(mob/user) + user.visible_message("[src] swells and splits into three new slimes!") + for(var/i in 1 to 3) + var/mob/living/basic/slime/S = new(get_turf(user)) + S.start_mutating(TRUE) + return ..() diff --git a/code/modules/research/xenobiology/crossbreeding/chilling.dm b/code/modules/research/xenobiology/crossbreeding/chilling.dm new file mode 100644 index 0000000000000..7b27efcd8859a --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/chilling.dm @@ -0,0 +1,344 @@ +/* +Chilling extracts: + Have a unique, primarily defensive effect when + filled with 10u plasma and activated in-hand. +*/ +/obj/item/slimecross/chilling + name = "chilling extract" + desc = "It's cold to the touch, as if frozen solid." + effect = "chilling" + icon_state = "chilling" + +/obj/item/slimecross/chilling/Initialize(mapload) + . = ..() + create_reagents(10, INJECTABLE | DRAWABLE) + +/obj/item/slimecross/chilling/attack_self(mob/user) + if(!reagents.has_reagent(/datum/reagent/toxin/plasma,10)) + to_chat(user, "This extract needs to be full of plasma to activate!") + return + reagents.remove_reagent(/datum/reagent/toxin/plasma,10) + to_chat(user, "You squeeze the extract, and it absorbs the plasma!") + playsound(src, 'sound/effects/bubbles.ogg', 50, TRUE) + playsound(src, 'sound/effects/glassbr1.ogg', 50, TRUE) + do_effect(user) + +/obj/item/slimecross/chilling/proc/do_effect(mob/user) //If, for whatever reason, you don't want to delete the extract, don't do ..() + qdel(src) + return + +/obj/item/slimecross/chilling/grey + colour = "grey" + effect_desc = "Creates some slime barrier cubes. When used they create slimy barricades." + +/obj/item/slimecross/chilling/grey/do_effect(mob/user) + user.visible_message("[src] produces a few small, grey cubes") + for(var/i in 1 to 3) + new /obj/item/barriercube(get_turf(user)) + ..() + +/obj/item/slimecross/chilling/orange + colour = "orange" + effect_desc = "Creates a ring of fire one tile away from the user." + +/obj/item/slimecross/chilling/orange/do_effect(mob/user) + user.visible_message(span_danger("[src] shatters, and lets out a jet of heat!")) + for(var/turf/T in orange(get_turf(user),2)) + if(get_dist(get_turf(user), T) > 1) + new /obj/effect/hotspot(T) + ..() + +/obj/item/slimecross/chilling/purple + colour = "purple" + effect_desc = "Injects everyone in the area with some regenerative jelly." + +/obj/item/slimecross/chilling/purple/do_effect(mob/user) + var/area/A = get_area(get_turf(user)) + if(A.outdoors) + to_chat(user, "[src] can't affect such a large area.") + return + user.visible_message("[src] shatters, and a healing aura fills the room briefly.") + for(var/mob/living/carbon/C in A) + C.reagents.add_reagent(/datum/reagent/medicine/regen_jelly,10) + ..() + +/obj/item/slimecross/chilling/blue + colour = "blue" + effect_desc = "Creates a rebreather, a tankless mask." + +/obj/item/slimecross/chilling/blue/do_effect(mob/user) + user.visible_message("[src] cracks, and spills out a liquid goo, which reforms into a mask!") + new /obj/item/clothing/mask/nobreath(get_turf(user)) + ..() + +/obj/item/slimecross/chilling/metal + colour = "metal" + effect_desc = "Temporarily surrounds the user with unbreakable walls." + +/obj/item/slimecross/chilling/metal/do_effect(mob/user) + user.visible_message(span_danger("[src] melts like quicksilver, and surrounds [user] in a wall!")) + for(var/turf/T in orange(get_turf(user),1)) + if(get_dist(get_turf(user), T) > 0) + new /obj/effect/forcefield/slimewall(T) + ..() + +/obj/item/slimecross/chilling/yellow + colour = "yellow" + effect_desc = "Recharges the room's APC by 50%." + +/obj/item/slimecross/chilling/yellow/do_effect(mob/user) + var/area/A = get_area(get_turf(user)) + user.visible_message("[src] shatters, and a the air suddenly feels charged for a moment.") + for(var/obj/machinery/power/apc/C in A) + if(C.cell) + C.cell.charge = min(C.cell.charge + C.cell.maxcharge/2, C.cell.maxcharge) + ..() + +/obj/item/slimecross/chilling/darkpurple + colour = "dark purple" + effect_desc = "Removes all plasma gas in the area." + +/obj/item/slimecross/chilling/darkpurple/do_effect(mob/user) + var/area/A = get_area(get_turf(user)) + if(A.outdoors) + to_chat(user, "[src] can't affect such a large area.") + return + var/filtered = FALSE + for(var/turf/open/T in A) + var/datum/gas_mixture/G = T.air + if(istype(G)) + G.assert_gas(/datum/gas/plasma) + G.gases[/datum/gas/plasma][MOLES] = 0 + filtered = TRUE + G.garbage_collect() + T.air_update_turf(FALSE, FALSE) + if(filtered) + user.visible_message("Cracks spread throughout [src], and some air is sucked in!") + else + user.visible_message("[src] cracks, but nothing happens.") + ..() + +/obj/item/slimecross/chilling/darkblue + colour = "dark blue" + effect_desc = "Seals the user in a protective block of ice." + +/obj/item/slimecross/chilling/darkblue/do_effect(mob/user) + if(isliving(user)) + user.visible_message("[src] freezes over [user]'s entire body!") + var/mob/living/M = user + M.apply_status_effect(/datum/status_effect/frozenstasis) + ..() + +/obj/item/slimecross/chilling/silver + colour = "silver" + effect_desc = "Creates several ration packs." + +/obj/item/slimecross/chilling/silver/do_effect(mob/user) + user.visible_message("[src] crumbles into icy powder, leaving behind several emergency food supplies!") + var/amount = rand(5, 10) + for(var/i in 1 to amount) + new /obj/item/food/rationpack(get_turf(user)) + ..() + +/obj/item/slimecross/chilling/bluespace + colour = "bluespace" + effect_desc = "Touching people with this extract adds them to a list, when it is activated it teleports everyone on that list to the user." + var/list/allies = list() + var/active = FALSE + +/obj/item/slimecross/chilling/bluespace/afterattack(atom/target, mob/user, proximity) + if(!proximity || !isliving(target) || active) + return + if(HAS_TRAIT(target, TRAIT_NO_TELEPORT)) + to_chat(user, "[target] resists being linked with [src]!") + return + if(target in allies) + allies -= target + to_chat(user, "You unlink [src] with [target].") + else + allies |= target + to_chat(user, "You link [src] with [target].") + return + +/obj/item/slimecross/chilling/bluespace/do_effect(mob/user) + if(allies.len <= 0) + to_chat(user, "[src] is not linked to anyone!") + return + to_chat(user, "You feel [src] pulse as it begins charging bluespace energies...") + active = TRUE + for(var/mob/living/M in allies) + var/datum/status_effect/slimerecall/S = M.apply_status_effect(/datum/status_effect/slimerecall) + S.target = user + if(do_after(user, 100, target=src)) + to_chat(user, "[src] shatters as it tears a hole in reality, snatching the linked individuals from the void!") + for(var/mob/living/M in allies) + var/datum/status_effect/slimerecall/S = M.has_status_effect(/datum/status_effect/slimerecall) + M.remove_status_effect(S) + else + to_chat(user, "[src] falls dark, dissolving into nothing as the energies fade away.") + for(var/mob/living/M in allies) + var/datum/status_effect/slimerecall/S = M.has_status_effect(/datum/status_effect/slimerecall) + if(istype(S)) + S.interrupted = TRUE + M.remove_status_effect(S) + ..() + +/obj/item/slimecross/chilling/sepia + colour = "sepia" + effect_desc = "Touching someone with it adds/removes them from a list. Activating the extract stops time for 30 seconds, and everyone on the list is immune, except the user." + var/list/allies = list() + +/obj/item/slimecross/chilling/sepia/afterattack(atom/target, mob/user, proximity) + if(!proximity || !isliving(target)) + return + if(target in allies) + allies -= target + to_chat(user, "You unlink [src] with [target].") + else + allies |= target + to_chat(user, "You link [src] with [target].") + return + +/obj/item/slimecross/chilling/sepia/do_effect(mob/user) + user.visible_message("[src] shatters, freezing time itself!") + allies -= user //support class + new /obj/effect/timestop(get_turf(user), 2, 300, allies) + ..() + +/obj/item/slimecross/chilling/cerulean + colour = "cerulean" + effect_desc = "Creates a flimsy copy of the user, that they control." + +/obj/item/slimecross/chilling/cerulean/do_effect(mob/user) + if(isliving(user)) + user.visible_message("[src] creaks and shifts into a clone of [user]!") + var/mob/living/M = user + M.apply_status_effect(/datum/status_effect/slime_clone) + ..() + +/obj/item/slimecross/chilling/pyrite + colour = "pyrite" + effect_desc = "Creates a pair of Prism Glasses, which allow the wearer to place colored light crystals." + +/obj/item/slimecross/chilling/pyrite/do_effect(mob/user) + user.visible_message("[src] crystallizes into a pair of spectacles!") + new /obj/item/clothing/glasses/prism_glasses(get_turf(user)) + ..() + +/obj/item/slimecross/chilling/red + colour = "red" + effect_desc = "Pacifies every slime in your vacinity." + +/obj/item/slimecross/chilling/red/do_effect(mob/user) + var/slimesfound = FALSE + for(var/mob/living/basic/slime/S in view(get_turf(user), 7)) + slimesfound = TRUE + S.add_trait(/datum/slime_trait/docility) + if(slimesfound) + user.visible_message("[src] lets out a peaceful ring as it shatters, and nearby slimes seem calm.") + else + user.visible_message("[src] lets out a peaceful ring as it shatters, but nothing happens...") + return ..() + +/obj/item/slimecross/chilling/green + colour = "green" + effect_desc = "Creates a bone gun in the hand it is used in, which uses blood as ammo." + +/obj/item/slimecross/chilling/green/do_effect(mob/user) + var/which_hand = "l_hand" + if(!(user.active_hand_index % 2)) + which_hand = "r_hand" + var/mob/living/L = user + if(!istype(user)) + return + var/obj/item/held = L.get_active_held_item() //This should be itself, but just in case... + L.dropItemToGround(held) + var/obj/item/gun/magic/bloodchill/gun = new(user) + if(!L.put_in_hands(gun)) + qdel(gun) + user.visible_message("[src] flash-freezes [user]'s arm, cracking the flesh horribly!") + else + user.visible_message(span_danger("[src] chills and snaps off the front of the bone on [user]'s arm, leaving behind a strange, gun-like structure!")) + user.emote("scream") + L.apply_damage(30,BURN,which_hand) + ..() + +/obj/item/slimecross/chilling/pink + colour = "pink" + effect_desc = "Creates a slime corgi puppy." + +/obj/item/slimecross/chilling/pink/do_effect(mob/user) + user.visible_message("[src] cracks like an egg, and an adorable puppy comes tumbling out!") + new /mob/living/basic/pet/dog/corgi/puppy/slime(get_turf(user)) + ..() + +/obj/item/slimecross/chilling/gold + colour = "gold" + effect_desc = "Produces a golden capture device" + +/obj/item/slimecross/chilling/gold/do_effect(mob/user) + user.visible_message("[src] lets off golden light as it melts and reforms into an egg-like device!") + new /obj/item/capturedevice(get_turf(user)) + ..() + +/obj/item/slimecross/chilling/oil + colour = "oil" + effect_desc = "It creates a weak, but wide-ranged explosion." + +/obj/item/slimecross/chilling/oil/do_effect(mob/user) + user.visible_message(span_danger("[src] begins to shake with muted intensity!")) + addtimer(CALLBACK(src, PROC_REF(boom)), 50) + +/obj/item/slimecross/chilling/oil/proc/boom() + explosion(src, devastation_range = -1, heavy_impact_range = -1, light_impact_range = 10, explosion_cause = src) //Large radius, but mostly light damage, and no flash. + qdel(src) + +/obj/item/slimecross/chilling/black + colour = "black" + effect_desc = "Transforms the user into a random type of golem." + +/obj/item/slimecross/chilling/black/do_effect(mob/user) + if(ishuman(user)) + user.visible_message("[src] crystallizes along [user]'s skin, turning into metallic scales!") + var/mob/living/carbon/human/H = user + + var/static/list/random_golem_types + random_golem_types = subtypesof(/datum/species/golem) - type + + for(var/datum/species/golem/golem as anything in random_golem_types) + if(!initial(golem.random_eligible)) + random_golem_types -= golem + H.set_species(pick(random_golem_types)) + ..() + +/obj/item/slimecross/chilling/lightpink + colour = "light pink" + effect_desc = "Creates a Heroine Bud, a special flower that pacifies whoever wears it on their head. They will not be able to take it off without help." + +/obj/item/slimecross/chilling/lightpink/do_effect(mob/user) + user.visible_message("[src] blooms into a beautiful flower!") + new /obj/item/clothing/head/peaceflower(get_turf(user)) + ..() + +/obj/item/slimecross/chilling/adamantine + colour = "adamantine" + effect_desc = "Solidifies into a set of adamantine armor." + +/obj/item/slimecross/chilling/adamantine/do_effect(mob/user) + user.visible_message("[src] creaks and breaks as it shifts into a heavy set of armor!") + new /obj/item/clothing/suit/armor/heavy/adamantine(get_turf(user)) + ..() + +/obj/item/slimecross/chilling/rainbow + colour = "rainbow" + effect_desc = "Makes an unpassable wall in every door in the area." + +/obj/item/slimecross/chilling/rainbow/do_effect(mob/user) + var/area/area = get_area(user) + if(area.outdoors) + to_chat(user, "[src] can't affect such a large area.") + return + user.visible_message("[src] reflects an array of dazzling colors and light, energy rushing to nearby doors!") + for(var/obj/machinery/door/airlock/door in area) + new /obj/effect/forcefield/slimewall/rainbow(door.loc) + return ..() diff --git a/code/modules/research/xenobiology/crossbreeding/consuming.dm b/code/modules/research/xenobiology/crossbreeding/consuming.dm new file mode 100644 index 0000000000000..7aafc6e2d91bb --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/consuming.dm @@ -0,0 +1,460 @@ +/* +Consuming extracts: + Can eat food items. + After consuming enough, produces special cookies. +*/ +/obj/item/slimecross/consuming + name = "consuming extract" + desc = "It hungers... for more." //My slimecross has finally decided to eat... my buffet! + icon_state = "consuming" + effect = "consuming" + var/nutriment_eaten = 0 + var/nutriment_required = 10 + var/cooldown = 600 //1 minute. + var/last_produced = 0 + var/cookies = 5 //Number of cookies to spawn + var/cookietype = /obj/item/slime_cookie + +/obj/item/slimecross/consuming/attackby(obj/item/O, mob/user) + if(IS_EDIBLE(O)) + if(last_produced + cooldown > world.time) + to_chat(user, "[src] is still digesting after its last meal!") + return + var/datum/reagent/N = O.reagents.has_reagent(/datum/reagent/consumable/nutriment) + if(N) + nutriment_eaten += N.volume + to_chat(user, "[src] opens up and swallows [O] whole!") + qdel(O) + playsound(src, 'sound/items/eatfood.ogg', 20, TRUE) + else + to_chat(user, "[src] burbles unhappily at the offering.") + if(nutriment_eaten >= nutriment_required) + nutriment_eaten = 0 + user.visible_message("[src] swells up and produces a small pile of cookies!") + playsound(src, 'sound/effects/splat.ogg', 40, TRUE) + last_produced = world.time + for(var/i in 1 to cookies) + var/obj/item/S = spawncookie() + S.pixel_x = base_pixel_x + rand(-5, 5) + S.pixel_y = base_pixel_y + rand(-5, 5) + return + ..() + +/obj/item/slimecross/consuming/proc/spawncookie() + return new cookietype(get_turf(src)) + +/obj/item/slime_cookie //While this technically acts like food, it's so removed from it that I made it its' own type. + name = "error cookie" + desc = "A weird slime cookie. You shouldn't see this." + icon = 'icons/obj/food/slimecookies.dmi' + var/taste = "error" + var/nutrition = 5 + icon_state = "base" + force = 0 + w_class = WEIGHT_CLASS_TINY + throwforce = 0 + throw_speed = 3 + throw_range = 6 + +/obj/item/slime_cookie/proc/do_effect(mob/living/M, mob/user) + return + +/obj/item/slime_cookie/attack(mob/living/M, mob/user) + var/fed = FALSE + if(M == user) + M.visible_message("[user] eats [src]!"), span_notice("You eat [src].") + fed = TRUE + else + M.visible_message(span_danger("[user] tries to force [M] to eat [src]!"), span_userdanger("[user] tries to force you to eat [src]!")) + if(do_after(user, 20, target = M)) + fed = TRUE + M.visible_message(span_danger("[user] forces [M] to eat [src]!"), "[user] forces you to eat [src].") + if(fed) + var/mob/living/carbon/human/H = M + + if(!istype(H) || !HAS_TRAIT(H, TRAIT_AGEUSIA)) + to_chat(M, "Tastes like [taste].") + playsound(get_turf(M), 'sound/items/eatfood.ogg', 20, TRUE) + if(nutrition) + M.reagents.add_reagent(/datum/reagent/consumable/nutriment,nutrition) + do_effect(M, user) + qdel(src) + return + ..() + +/obj/item/slimecross/consuming/grey + colour = "grey" + effect_desc = "Creates a slime cookie." + cookietype = /obj/item/slime_cookie/grey + +/obj/item/slime_cookie/grey + name = "slime cookie" + desc = "A grey-ish transparent cookie. Nutritious, probably." + icon_state = "grey" + taste = "goo" + nutrition = 15 + +/obj/item/slimecross/consuming/orange + colour = "orange" + effect_desc = "Creates a slime cookie that heats the target up and grants cold immunity for a short time." + cookietype = /obj/item/slime_cookie/orange + +/obj/item/slime_cookie/orange + name = "fiery cookie" + desc = "An orange cookie with a fiery pattern. Feels warm." + icon_state = "orange" + taste = "cinnamon and burning" + +/obj/item/slime_cookie/orange/do_effect(mob/living/M, mob/user) + M.apply_status_effect(/datum/status_effect/firecookie) + +/obj/item/slimecross/consuming/purple + colour = "purple" + effect_desc = "Creates a slime cookie that heals the target from every type of damage." + cookietype = /obj/item/slime_cookie/purple + +/obj/item/slime_cookie/purple + name = "health cookie" + desc = "A purple cookie with a cross pattern. Soothing." + icon_state = "purple" + taste = "fruit jam and cough medicine" + +/obj/item/slime_cookie/purple/do_effect(mob/living/M, mob/user) + M.adjustBruteLoss(-5) + M.adjustFireLoss(-5) + M.adjustToxLoss(-5, forced=1) //To heal slimepeople. + M.adjustOxyLoss(-5) + M.adjustCloneLoss(-5) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -5) + +/obj/item/slimecross/consuming/blue + colour = "blue" + effect_desc = "Creates a slime cookie that wets the floor around you and makes you immune to water based slipping for a short time." + cookietype = /obj/item/slime_cookie/blue + +/obj/item/slime_cookie/blue + name = "water cookie" + desc = "A transparent blue cookie. Constantly dripping wet." + icon_state = "blue" + taste = "water" + +/obj/item/slime_cookie/blue/do_effect(mob/living/M, mob/user) + M.apply_status_effect(/datum/status_effect/watercookie) + +/obj/item/slimecross/consuming/metal + colour = "metal" + effect_desc = "Creates a slime cookie that increases the target's resistance to brute damage." + cookietype = /obj/item/slime_cookie/metal + +/obj/item/slime_cookie/metal + name = "metallic cookie" + desc = "A shiny grey cookie. Hard to the touch." + icon_state = "metal" + taste = /datum/reagent/copper + +/obj/item/slime_cookie/metal/do_effect(mob/living/M, mob/user) + M.apply_status_effect(/datum/status_effect/metalcookie) + +/obj/item/slimecross/consuming/yellow + colour = "yellow" + effect_desc = "Creates a slime cookie that makes the target immune to electricity for a short time." + cookietype = /obj/item/slime_cookie/yellow + +/obj/item/slime_cookie/yellow + name = "sparking cookie" + desc = "A yellow cookie with a lightning pattern. Has a rubbery texture." + icon_state = "yellow" + taste = "lemon cake and rubber gloves" + +/obj/item/slime_cookie/yellow/do_effect(mob/living/M, mob/user) + M.apply_status_effect(/datum/status_effect/sparkcookie) + +/obj/item/slimecross/consuming/darkpurple + colour = "dark purple" + effect_desc = "Creates a slime cookie that reverses how the target's body treats toxins." + cookietype = /obj/item/slime_cookie/darkpurple + +/obj/item/slime_cookie/darkpurple + name = "toxic cookie" + desc = "A dark purple cookie, stinking of plasma." + icon_state = "darkpurple" + taste = "slime jelly and toxins" + +/obj/item/slime_cookie/darkpurple/do_effect(mob/living/M, mob/user) + M.apply_status_effect(/datum/status_effect/toxincookie) + +/obj/item/slimecross/consuming/darkblue + colour = "dark blue" + effect_desc = "Creates a slime cookie that chills the target and extinguishes them." + cookietype = /obj/item/slime_cookie/darkblue + +/obj/item/slime_cookie/darkblue + name = "frosty cookie" + desc = "A dark blue cookie with a snowflake pattern. Feels cold." + icon_state = "darkblue" + taste = "mint and bitter cold" + +/obj/item/slime_cookie/darkblue/do_effect(mob/living/M, mob/user) + M.adjust_bodytemperature(-110) + M.extinguish_mob() + +/obj/item/slimecross/consuming/silver + colour = "silver" + effect_desc = "Creates a slime cookie that never gets the target fat." + cookietype = /obj/item/slime_cookie/silver + +/obj/item/slime_cookie/silver + name = "waybread cookie" + desc = "A warm, crispy cookie, sparkling silver in the light. Smells wonderful." + icon_state = "silver" + taste = "masterful elven baking" + nutrition = 0 //We don't want normal nutriment + +/obj/item/slime_cookie/silver/do_effect(mob/living/M, mob/user) + M.reagents.add_reagent(/datum/reagent/consumable/nutriment/stabilized,10) + +/obj/item/slimecross/consuming/bluespace + colour = "bluespace" + effect_desc = "Creates a slime cookie that teleports the target to a random place in the area." + cookietype = /obj/item/slime_cookie/bluespace + +/obj/item/slime_cookie/bluespace + name = "space cookie" + desc = "A white cookie with green icing. Surprisingly hard to hold." + icon_state = "bluespace" + taste = "sugar and starlight" + +/obj/item/slime_cookie/bluespace/do_effect(mob/living/M, mob/user) + var/list/L = get_area_turfs(get_area(get_turf(M))) + var/turf/target + while (L.len && !target) + var/I = rand(1, L.len) + var/turf/T = L[I] + if (is_centcom_level(T.z)) + L.Cut(I,I+1) + continue + if(!T.density) + var/clear = TRUE + for(var/obj/O in T) + if(O.density) + clear = FALSE + break + if(clear) + target = T + if (!target) + L.Cut(I,I+1) + + if(target) + do_teleport(M, target, 0, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_BLUESPACE) + new /obj/effect/particle_effect/sparks(get_turf(M)) + playsound(get_turf(M), SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + +/obj/item/slimecross/consuming/sepia + colour = "sepia" + effect_desc = "Creates a slime cookie that makes the target do things slightly faster." + cookietype = /obj/item/slime_cookie/sepia + +/obj/item/slime_cookie/sepia + name = "time cookie" + desc = "A light brown cookie with a clock pattern. Takes some time to chew." + icon_state = "sepia" + taste = "brown sugar and a metronome" + +/obj/item/slime_cookie/sepia/do_effect(mob/living/M, mob/user) + M.apply_status_effect(/datum/status_effect/timecookie) + +/obj/item/slimecross/consuming/cerulean + colour = "cerulean" + effect_desc = "Creates a slime cookie that has a chance to make another once you eat it." + cookietype = /obj/item/slime_cookie/cerulean + cookies = 3 //You're gonna get more. + +/obj/item/slime_cookie/cerulean + name = "duplicookie" + desc = "A cerulean cookie with strange proportions. It feels like it could break apart easily." + icon_state = "cerulean" + taste = "a sugar cookie" + +/obj/item/slime_cookie/cerulean/do_effect(mob/living/M, mob/user) + if(prob(50)) + to_chat(M, "A piece of [src] breaks off while you chew, and falls to the ground.") + var/obj/item/slime_cookie/cerulean/C = new(get_turf(M)) + C.taste = taste + " and a sugar cookie" + +/obj/item/slimecross/consuming/pyrite + colour = "pyrite" + effect_desc = "Creates a slime cookie that randomly colors the target." + cookietype = /obj/item/slime_cookie/pyrite + +/obj/item/slime_cookie/pyrite + name = "color cookie" + desc = "A yellow cookie with rainbow-colored icing. Reflects the light strangely." + icon_state = "pyrite" + taste = "vanilla and " //Randomly selected color dye. + var/colour = "#FFFFFF" + +/obj/item/slime_cookie/pyrite/Initialize(mapload) + . = ..() + var/tastemessage = "paint remover" + switch(rand(1,7)) + if(1) + tastemessage = "red dye" + colour = "#FF0000" + if(2) + tastemessage = "orange dye" + colour = "#FFA500" + if(3) + tastemessage = "yellow dye" + colour = "#FFFF00" + if(4) + tastemessage = "green dye" + colour = "#00FF00" + if(5) + tastemessage = "blue dye" + colour = "#0000FF" + if(6) + tastemessage = "indigo dye" + colour = "#4B0082" + if(7) + tastemessage = "violet dye" + colour = "#FF00FF" + taste += tastemessage + +/obj/item/slime_cookie/pyrite/do_effect(mob/living/M, mob/user) + M.add_atom_colour(colour,WASHABLE_COLOUR_PRIORITY) + +/obj/item/slimecross/consuming/red + colour = "red" + effect_desc = "Creates a slime cookie that creates a spatter of blood on the floor, while also restoring some of the target's blood." + cookietype = /obj/item/slime_cookie/red + +/obj/item/slime_cookie/red + name = "blood cookie" + desc = "A red cookie, oozing a thick red fluid. Vampires might enjoy it." + icon_state = "red" + taste = "red velvet and iron" + +/obj/item/slime_cookie/red/do_effect(mob/living/M, mob/user) + new /obj/effect/decal/cleanable/blood(get_turf(M)) + playsound(get_turf(M), 'sound/effects/splat.ogg', 10, TRUE) + if(iscarbon(M)) + var/mob/living/carbon/C = M + C.blood_volume += 25 //Half a vampire drain. + +/obj/item/slimecross/consuming/green + colour = "green" + effect_desc = "Creates a slime cookie that is absolutely disgusting, makes the target vomit, however all reagent in their body are also removed." + cookietype = /obj/item/slime_cookie/green + +/obj/item/slime_cookie/green + name = "gross cookie" + desc = "A disgusting green cookie, seeping with pus. You kind of feel ill just looking at it." + icon_state = "green" + taste = "the contents of your stomach" + +/obj/item/slime_cookie/green/do_effect(mob/living/M, mob/user) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + H.vomit(25) + M.reagents.remove_all() + +/obj/item/slimecross/consuming/pink + colour = "pink" + effect_desc = "Creates a slime cookie that makes the target want to spread the love." + cookietype = /obj/item/slime_cookie/pink + +/obj/item/slime_cookie/pink + name = "love cookie" + desc = "A pink cookie with an icing heart. D'aww." + icon_state = "pink" + taste = "love and hugs" + +/obj/item/slime_cookie/pink/do_effect(mob/living/M, mob/user) + M.apply_status_effect(/datum/status_effect/lovecookie) + +/obj/item/slimecross/consuming/gold + colour = "gold" + effect_desc = "Creates a slime cookie that has a gold coin inside." + cookietype = /obj/item/slime_cookie/gold + +/obj/item/slime_cookie/gold + name = "gilded cookie" + desc = "A buttery golden cookie, closer to a bread than anything. May good fortune find you." + icon_state = "gold" + taste = "sweet cornbread and wealth" + +/obj/item/slime_cookie/gold/do_effect(mob/living/M, mob/user) + var/obj/item/held = M.get_active_held_item() //This should be itself, but just in case... + M.dropItemToGround(held) + var/newcoin = /obj/item/coin/gold + var/obj/item/coin/C = new newcoin(get_turf(M)) + playsound(get_turf(C), 'sound/items/coinflip.ogg', 50, TRUE) + M.put_in_hand(C) + +/obj/item/slimecross/consuming/oil + colour = "oil" + effect_desc = "Creates a slime cookie that slows anyone next to the user." + cookietype = /obj/item/slime_cookie/oil + +/obj/item/slime_cookie/oil + name = "tar cookie" + desc = "An oily black cookie, which sticks to your hands. Smells like chocolate." + icon_state = "oil" + taste = "rich molten chocolate and tar" + +/obj/item/slime_cookie/oil/do_effect(mob/living/M, mob/user) + M.apply_status_effect(/datum/status_effect/tarcookie) + +/obj/item/slimecross/consuming/black + colour = "black" + effect_desc = "Creates a slime cookie that makes the target look like a spooky skeleton for a little bit." + cookietype = /obj/item/slime_cookie/black + +/obj/item/slime_cookie/black + name = "spooky cookie" + desc = "A pitch black cookie with an icing ghost on the front. Spooky!" + icon_state = "black" + taste = "ghosts and stuff" + +/obj/item/slime_cookie/black/do_effect(mob/living/M, mob/user) + M.apply_status_effect(/datum/status_effect/spookcookie) + +/obj/item/slimecross/consuming/lightpink + colour = "light pink" + effect_desc = "Creates a slime cookie that makes the target, and anyone next to the target, pacifistic for a small amount of time." + cookietype = /obj/item/slime_cookie/lightpink + +/obj/item/slime_cookie/lightpink + name = "peace cookie" + desc = "A light pink cookie with a peace symbol in the icing. Lovely!" + icon_state = "lightpink" + taste = "strawberry icing and P.L.U.R" //Literal candy raver. + +/obj/item/slime_cookie/lightpink/do_effect(mob/living/M, mob/user) + M.apply_status_effect(/datum/status_effect/peacecookie) + +/obj/item/slimecross/consuming/adamantine + colour = "adamantine" + effect_desc = "Creates a slime cookie that increases the target's resistance to burn damage." + cookietype = /obj/item/slime_cookie/adamantine + +/obj/item/slime_cookie/adamantine + name = "crystal cookie" + desc = "A translucent rock candy in the shape of a cookie. Surprisingly chewy." + icon_state = "adamantine" + taste = "crystalline sugar and metal" + +/obj/item/slime_cookie/adamantine/do_effect(mob/living/M, mob/user) + M.apply_status_effect(/datum/status_effect/adamantinecookie) + +/obj/item/slimecross/consuming/rainbow + colour = "rainbow" + effect_desc = "Creates a slime cookie that has the effect of a random cookie." + +/obj/item/slimecross/consuming/rainbow/spawncookie() + var/cookie_type = pick(subtypesof(/obj/item/slime_cookie)) + var/obj/item/slime_cookie/S = new cookie_type(get_turf(src)) + S.name = "rainbow cookie" + S.desc = "A beautiful rainbow cookie, constantly shifting colors in the light." + S.icon_state = "rainbow" + return S diff --git a/code/modules/research/xenobiology/crossbreeding/industrial.dm b/code/modules/research/xenobiology/crossbreeding/industrial.dm index 8b1d02ad7cb4c..425e8026f8960 100644 --- a/code/modules/research/xenobiology/crossbreeding/industrial.dm +++ b/code/modules/research/xenobiology/crossbreeding/industrial.dm @@ -1,6 +1,208 @@ -// Ensure the output from an industrial extract is always layered below the extract -/obj/item/slimecross/industrial/do_after_spawn(obj/item/spawned) - spawned.layer = min(spawned.layer, layer - 0.1) +/* +Industrial extracts: + Slowly consume plasma, produce items with it. +*/ +/obj/item/slimecross/industrial + name = "industrial extract" + desc = "A gel-like, sturdy extract, fond of plasma and industry." + effect = "industrial" + icon_state = "industrial_still" + var/plasmarequired = 2 //Units of plasma required to be consumed to produce item. + var/itempath = /obj/item //The item produced by the extract. + var/plasmaabsorbed = 0 //Units of plasma aborbed by the extract already. Absorbs at a rate of 2u/obj tick. + var/itemamount = 1 //How many items to spawn + +/obj/item/slimecross/industrial/examine(mob/user) + . = ..() + . += "It currently has [plasmaabsorbed] units of plasma floating inside the outer shell, out of [plasmarequired] units." + +/obj/item/slimecross/industrial/proc/do_after_spawn(obj/item/spawned) + return + +/obj/item/slimecross/industrial/Initialize(mapload) + . = ..() + create_reagents(100, INJECTABLE | DRAWABLE) + START_PROCESSING(SSobj,src) + +/obj/item/slimecross/industrial/Destroy() + STOP_PROCESSING(SSobj,src) + return ..() + +/obj/item/slimecross/industrial/process() + var/IsWorking = FALSE + if(reagents.has_reagent(/datum/reagent/toxin/plasma,amount = 2) && plasmarequired > 1) //Can absorb as much as 2 + IsWorking = TRUE + reagents.remove_reagent(/datum/reagent/toxin/plasma,2) + plasmaabsorbed += 2 + else if(reagents.has_reagent(/datum/reagent/toxin/plasma,amount = 1)) //Can absorb as little as 1 + IsWorking = TRUE + reagents.remove_reagent(/datum/reagent/toxin/plasma,1) + plasmaabsorbed += 1 + + if(plasmaabsorbed >= plasmarequired) + playsound(src, 'sound/effects/attackblob.ogg', 50, TRUE) + plasmaabsorbed -= plasmarequired + for(var/i in 1 to itemamount) + do_after_spawn(new itempath(get_turf(src))) + else if(IsWorking) + playsound(src, 'sound/effects/bubbles.ogg', 5, TRUE) + if(IsWorking) + icon_state = "industrial" + else + icon_state = "industrial_still" /obj/item/slimecross/industrial/grey - effect_desc = "Produces biocubes." + colour = "grey" + effect_desc = "Produces monkey cubes." + itempath = /obj/item/stack/biomass + itemamount = 5 + +/obj/item/slimecross/industrial/orange + colour = "orange" + effect_desc = "Produces slime zippo lighters." + plasmarequired = 6 + itempath = /obj/item/lighter/slime + +/obj/item/slimecross/industrial/purple + colour = "purple" + effect_desc = "Produces autoinjectors with regen jelly inside." + plasmarequired = 5 + itempath = /obj/item/slimecrossbeaker/autoinjector/regenpack + +/obj/item/slimecross/industrial/blue + colour = "blue" + effect_desc = "Produces full fire extinguishers." + plasmarequired = 10 + itempath = /obj/item/extinguisher + +/obj/item/slimecross/industrial/metal + colour = "metal" + effect_desc = "Produces iron sheets." + plasmarequired = 3 + itempath = /obj/item/stack/sheet/iron/ten + +/obj/item/slimecross/industrial/yellow + colour = "yellow" + effect_desc = "Produces high capacity power cells, which are not fully charged on creation." + plasmarequired = 5 + itempath = /obj/item/stock_parts/cell/high + +/obj/item/slimecross/industrial/yellow/do_after_spawn(obj/item/spawned) + var/obj/item/stock_parts/cell/high/C = spawned + if(istype(C)) + C.charge = rand(0,C.maxcharge/2) + +/obj/item/slimecross/industrial/darkpurple + colour = "dark purple" + effect_desc = "Produces plasma... for plasma." + plasmarequired = 10 + itempath = /obj/item/stack/sheet/mineral/plasma + +/obj/item/slimecross/industrial/darkblue + colour = "dark blue" + effect_desc = "Produces one-use fireproofing potions." + plasmarequired = 6 + itempath = /obj/item/slimepotion/fireproof + +/obj/item/slimecross/industrial/darkblue/do_after_spawn(obj/item/spawned) + var/obj/item/slimepotion/fireproof/potion = spawned + if(istype(potion)) + potion.uses = 1 + +/obj/item/slimecross/industrial/silver + colour = "silver" + effect_desc = "Produces random food and drink items." + plasmarequired = 1 + //Item picked below. + +/obj/item/slimecross/industrial/silver/process() + itempath = pick(list(get_random_food(), get_random_drink())) + ..() + +/obj/item/slimecross/industrial/silver/do_after_spawn(obj/item/spawned) + ADD_TRAIT(spawned, TRAIT_FOOD_SILVER, INNATE_TRAIT) + +/obj/item/slimecross/industrial/bluespace + colour = "bluespace" + effect_desc = "Produces synthetic bluespace crystals." + plasmarequired = 7 + itempath = /obj/item/stack/ore/bluespace_crystal/artificial + +/obj/item/slimecross/industrial/sepia + colour = "sepia" + effect_desc = "Produces cameras." + plasmarequired = 2 + itempath = /obj/item/camera + +/obj/item/slimecross/industrial/cerulean + colour = "cerulean" + effect_desc = "Produces normal slime extract enhancers." + plasmarequired = 5 + itempath = /obj/item/slimepotion/enhancer + +/obj/item/slimecross/industrial/pyrite + colour = "pyrite" + effect_desc = "Produces cans of spraypaint." + plasmarequired = 2 + itempath = /obj/item/toy/crayon/spraycan + +/obj/item/slimecross/industrial/red + colour = "red" + effect_desc = "Produces blood orbs." + plasmarequired = 5 + itempath = /obj/item/slimecrossbeaker/bloodpack + +/obj/item/slimecross/industrial/green + colour = "green" + effect_desc = "Produces self-use-only slime jelly autoinjectors." + plasmarequired = 7 + itempath = /obj/item/slimecrossbeaker/autoinjector/slimejelly + +/obj/item/slimecross/industrial/pink + colour = "pink" + effect_desc = "Produces synthpax and space drug autoinjectors." + plasmarequired = 6 + itempath = /obj/item/slimecrossbeaker/autoinjector/peaceandlove + +/obj/item/slimecross/industrial/gold + colour = "gold" + effect_desc = "Produces random coins." + plasmarequired = 10 + +/obj/item/slimecross/industrial/gold/process() + itempath = pick(/obj/item/coin/silver, /obj/item/coin/iron, /obj/item/coin/gold, /obj/item/coin/diamond, /obj/item/coin/plasma, /obj/item/coin/uranium) + ..() + +/obj/item/slimecross/industrial/oil + colour = "oil" + effect_desc = "Produces IEDs." + plasmarequired = 4 + itempath = /obj/item/grenade/iedcasing/spawned + +/obj/item/slimecross/industrial/black //What does this have to do with black slimes? No clue! Fun, though + colour = "black" + effect_desc = "Produces slime brand regenerative cigarettes." + plasmarequired = 6 + itempath = /obj/item/storage/fancy/cigarettes/cigpack_xeno + +/obj/item/slimecross/industrial/lightpink + colour = "light pink" + effect_desc = "Produces heart shaped boxes that have candies in them." + plasmarequired = 3 + itempath = /obj/item/storage/fancy/heart_box + +/obj/item/slimecross/industrial/adamantine + colour = "adamantine" + effect_desc = "Produces sheets of adamantine." + plasmarequired = 10 + itempath = /obj/item/stack/sheet/mineral/adamantine + +/obj/item/slimecross/industrial/rainbow + colour = "rainbow" + effect_desc = "Produces random slime extracts." + plasmarequired = 5 + //Item picked below. + +/obj/item/slimecross/industrial/rainbow/process() + itempath = pick(subtypesof(/obj/item/slime_extract)) + ..() diff --git a/code/modules/research/xenobiology/crossbreeding/prismatic.dm b/code/modules/research/xenobiology/crossbreeding/prismatic.dm new file mode 100644 index 0000000000000..5d6567ed15a62 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/prismatic.dm @@ -0,0 +1,124 @@ +/* +Prismatic extracts: + Becomes an infinite-use paintbrush. +*/ +/obj/item/slimecross/prismatic + name = "prismatic extract" + desc = "It's constantly wet with a semi-transparent, colored goo." + effect = "prismatic" + effect_desc = "When used it paints whatever it hits." + icon_state = "prismatic" + var/paintcolor = "#FFFFFF" + +/obj/item/slimecross/prismatic/afterattack(turf/target, mob/user, proximity) + if(!proximity) + return + if(!istype(target) || isspaceturf(target)) + return + target.add_atom_colour(paintcolor, WASHABLE_COLOUR_PRIORITY) + playsound(target, 'sound/effects/slosh.ogg', 20, TRUE) + +/obj/item/slimecross/prismatic/grey/ + colour = "grey" + desc = "It's constantly wet with a pungent-smelling, clear chemical." + +/obj/item/slimecross/prismatic/grey/afterattack(turf/target, mob/user, proximity) + . = ..() + if(!proximity) + return + if(istype(target) && target.color != initial(target.color)) + target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + playsound(target, 'sound/effects/slosh.ogg', 20, TRUE) + +/obj/item/slimecross/prismatic/orange + paintcolor = "#FFA500" + colour = "orange" + +/obj/item/slimecross/prismatic/purple + paintcolor = "#B19CD9" + colour = "purple" + +/obj/item/slimecross/prismatic/blue + paintcolor = "#ADD8E6" + colour = "blue" + +/obj/item/slimecross/prismatic/metal + paintcolor = "#7E7E7E" + colour = "metal" + +/obj/item/slimecross/prismatic/yellow + paintcolor = "#FFFF00" + colour = "yellow" + +/obj/item/slimecross/prismatic/darkpurple + paintcolor = "#551A8B" + colour = "dark purple" + +/obj/item/slimecross/prismatic/darkblue + paintcolor = "#0000FF" + colour = "dark blue" + +/obj/item/slimecross/prismatic/silver + paintcolor = "#D3D3D3" + colour = "silver" + +/obj/item/slimecross/prismatic/bluespace + paintcolor = "#32CD32" + colour = "bluespace" + +/obj/item/slimecross/prismatic/sepia + paintcolor = "#704214" + colour = "sepia" + +/obj/item/slimecross/prismatic/cerulean + paintcolor = "#2956B2" + colour = "cerulean" + +/obj/item/slimecross/prismatic/pyrite + paintcolor = "#FAFAD2" + colour = "pyrite" + +/obj/item/slimecross/prismatic/red + paintcolor = "#FF0000" + colour = "red" + +/obj/item/slimecross/prismatic/green + paintcolor = "#00FF00" + colour = "green" + +/obj/item/slimecross/prismatic/pink + paintcolor = "#FF69B4" + colour = "pink" + +/obj/item/slimecross/prismatic/gold + paintcolor = "#FFD700" + colour = "gold" + +/obj/item/slimecross/prismatic/oil + paintcolor = "#505050" + colour = "oil" + +/obj/item/slimecross/prismatic/black + paintcolor = "#000000" + colour = "black" + +/obj/item/slimecross/prismatic/lightpink + paintcolor = "#FFB6C1" + colour = "light pink" + +/obj/item/slimecross/prismatic/adamantine + paintcolor = "#008B8B" + colour = "adamantine" + +/obj/item/slimecross/prismatic/rainbow + paintcolor = "#FFFFFF" + colour = "rainbow" + +/obj/item/slimecross/prismatic/rainbow/attack_self(mob/user) + var/newcolor = tgui_color_picker(user, "Choose the slime color:", "Color change", paintcolor) + if(user.get_active_held_item() != src || user.stat != CONSCIOUS || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) + return + if(!newcolor) + return + paintcolor = newcolor + return diff --git a/code/modules/research/xenobiology/crossbreeding/recurring.dm b/code/modules/research/xenobiology/crossbreeding/recurring.dm new file mode 100644 index 0000000000000..a9ec505f40a87 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/recurring.dm @@ -0,0 +1,138 @@ +/* +Recurring extracts: + Generates a new charge every few seconds. + If depleted of its' last charge, stops working. +*/ +/obj/item/slimecross/recurring + name = "recurring extract" + desc = "A tiny, glowing core, wrapped in several layers of goo." + effect = "recurring" + icon_state = "recurring" + var/extract_type + var/obj/item/slime_extract/extract + var/cooldown = 0 + var/max_cooldown = 10 // In seconds + +/obj/item/slimecross/recurring/Initialize(mapload) + . = ..() + extract = new extract_type(src.loc) + visible_message("[src] wraps a layer of goo around itself!") + extract.name = name + extract.desc = desc + extract.icon = icon + extract.icon_state = icon_state + extract.color = color + extract.recurring = TRUE + src.forceMove(extract) + START_PROCESSING(SSobj,src) + +/obj/item/slimecross/recurring/process(seconds_per_tick) + if(cooldown > 0) + cooldown -= seconds_per_tick + else if(extract.Uses < 10 && extract.Uses > 0) + extract.Uses++ + cooldown = max_cooldown + else if(extract.Uses <= 0) + extract.visible_message("The light inside [extract] flickers and dies out.") + extract.desc = "A tiny, inert core, bleeding dark, cerulean-colored goo." + extract.icon_state = "prismatic" + qdel(src) + +/obj/item/slimecross/recurring/Destroy() + . = ..() + STOP_PROCESSING(SSobj,src) + +/obj/item/slimecross/recurring/grey + extract_type = /obj/item/slime_extract/grey + colour = "grey" + +/obj/item/slimecross/recurring/orange + extract_type = /obj/item/slime_extract/orange + colour = "orange" + +/obj/item/slimecross/recurring/purple + extract_type = /obj/item/slime_extract/purple + colour = "purple" + +/obj/item/slimecross/recurring/blue + extract_type = /obj/item/slime_extract/blue + colour = "blue" + +/obj/item/slimecross/recurring/metal + extract_type = /obj/item/slime_extract/metal + colour = "metal" + max_cooldown = 20 + +/obj/item/slimecross/recurring/yellow + extract_type = /obj/item/slime_extract/yellow + colour = "yellow" + max_cooldown = 20 + +/obj/item/slimecross/recurring/darkpurple + extract_type = /obj/item/slime_extract/darkpurple + colour = "dark purple" + max_cooldown = 20 + +/obj/item/slimecross/recurring/darkblue + extract_type = /obj/item/slime_extract/darkblue + colour = "dark blue" + +/obj/item/slimecross/recurring/silver + extract_type = /obj/item/slime_extract/silver + colour = "silver" + +/obj/item/slimecross/recurring/bluespace + extract_type = /obj/item/slime_extract/bluespace + colour = "bluespace" + +/obj/item/slimecross/recurring/sepia + extract_type = /obj/item/slime_extract/sepia + colour = "sepia" + max_cooldown = 36 //No infinite timestop for you! + +/obj/item/slimecross/recurring/cerulean + extract_type = /obj/item/slime_extract/cerulean + colour = "cerulean" + +/obj/item/slimecross/recurring/pyrite + extract_type = /obj/item/slime_extract/pyrite + colour = "pyrite" + +/obj/item/slimecross/recurring/red + extract_type = /obj/item/slime_extract/red + colour = "red" + +/obj/item/slimecross/recurring/green + extract_type = /obj/item/slime_extract/green + colour = "green" + +/obj/item/slimecross/recurring/pink + extract_type = /obj/item/slime_extract/pink + colour = "pink" + +/obj/item/slimecross/recurring/gold + extract_type = /obj/item/slime_extract/gold + colour = "gold" + max_cooldown = 30 + +/obj/item/slimecross/recurring/oil + extract_type = /obj/item/slime_extract/oil + colour = "oil" //Why would you want this? + +/obj/item/slimecross/recurring/black + extract_type = /obj/item/slime_extract/black + colour = "black" + +/obj/item/slimecross/recurring/lightpink + extract_type = /obj/item/slime_extract/lightpink + colour = "light pink" + +/obj/item/slimecross/recurring/adamantine + extract_type = /obj/item/slime_extract/adamantine + colour = "adamantine" + max_cooldown = 20 + +/obj/item/slimecross/recurring/rainbow + extract_type = /obj/item/slime_extract/rainbow + colour = "rainbow" + max_cooldown = 40 //It's pretty powerful. diff --git a/code/modules/research/xenobiology/crossbreeding/regenerative.dm b/code/modules/research/xenobiology/crossbreeding/regenerative.dm new file mode 100644 index 0000000000000..19eea41a0d96e --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/regenerative.dm @@ -0,0 +1,289 @@ +/* +Regenerative extracts: + Work like a legion regenerative core. + Has a unique additional effect. + + HEAVILY REWORKED BY MONKESTATION, SEE [monkestation\code\modules\slimecore\crossbreeding\regenerative] +*/ +/obj/item/slimecross/regenerative + name = "regenerative extract" + desc = "It's filled with a milky substance, and pulses like a heartbeat." + effect = "regenerative" + icon_state = "regenerative" + +/obj/item/slimecross/regenerative/proc/core_effect(mob/living/carbon/human/target, mob/user) + return +/obj/item/slimecross/regenerative/proc/core_effect_before(mob/living/carbon/human/target, mob/user) + return + +/* monkestation edit: overriden in [monkestation\code\modules\slimecore\crossbreeding\regenerative\extract.dm] +/obj/item/slimecross/regenerative/afterattack(atom/target,mob/user,prox) + . = ..() + if(!prox || !isliving(target)) + return + var/mob/living/H = target + if(H.stat == DEAD) + to_chat(user, "[src] will not work on the dead!") + return + if(H != user) + user.visible_message("[user] crushes [src] over [H], the milky goo quickly regenerating all of [H.p_their()] injuries!", + "You squeeze [src], and it bursts over [H], the milky goo regenerating [H.p_their()] injuries.") + else + user.visible_message("[user] crushes [src] over [user.p_them()]self, the milky goo quickly regenerating all of [user.p_their()] injuries!", + "You squeeze [src], and it bursts in your hand, splashing you with milky goo which quickly regenerates your injuries!") + core_effect_before(H, user) + H.revive(HEAL_ALL) + core_effect(H, user) + playsound(target, 'sound/effects/splat.ogg', 40, TRUE) + qdel(src) +*/ + +/obj/item/slimecross/regenerative/grey + colour = "grey" //Has no bonus effect. + effect_desc = "Rapidly heals the target and does nothing else." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/orange + colour = "orange" + +/obj/item/slimecross/regenerative/orange/core_effect_before(mob/living/target, mob/user) + target.visible_message("\The [src] boils over!") + for(var/turf/targetturf in RANGE_TURFS(1,target)) + if(!locate(/obj/effect/hotspot) in targetturf) + new /obj/effect/hotspot(targetturf) + +/obj/item/slimecross/regenerative/purple + colour = "purple" + effect_desc = "Rapidly heals the target at an even greater rate, and injects them with some regenerative jelly afterwards. Prevents softcrit while active." // monkestation edit: updates effect_desc to reflect the rework + +/* monkestation edit: moved functionality to status effect +/obj/item/slimecross/regenerative/purple/core_effect(mob/living/target, mob/user) + target.reagents.add_reagent(/datum/reagent/medicine/regen_jelly,10) +*/ + +/obj/item/slimecross/regenerative/blue + colour = "blue" + effect_desc = "Rapidly heals the target and makes the floor wet." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/blue/core_effect(mob/living/target, mob/user) + if(isturf(target.loc)) + var/turf/open/T = get_turf(target) + T.MakeSlippery(TURF_WET_WATER, min_wet_time = 10, wet_time_to_add = 5) + target.visible_message("The milky goo in the extract gets all over the floor!") + +/obj/item/slimecross/regenerative/metal + colour = "metal" + effect_desc = "Rapidly heals the target and encases the target in a locker." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/metal/core_effect(mob/living/target, mob/user) + target.visible_message("The milky goo hardens and reshapes itself, encasing [target]!") + var/obj/structure/closet/C = new /obj/structure/closet(target.loc) + C.name = "slimy closet" + C.desc = "Looking closer, it seems to be made of a sort of solid, opaque, metal-like goo." + target.forceMove(C) + +/obj/item/slimecross/regenerative/yellow + colour = "yellow" + effect_desc = "Rapidly heals the target and fully recharges a single item on the target. Provides shock immunity while active." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/yellow/core_effect(mob/living/target, mob/user) + var/list/batteries = list() + for(var/obj/item/stock_parts/cell/C in target.get_all_contents()) + if(C.charge < C.maxcharge) + batteries += C + if(batteries.len) + var/obj/item/stock_parts/cell/ToCharge = pick(batteries) + ToCharge.charge = ToCharge.maxcharge + to_chat(target, "You feel a strange electrical pulse, and one of your electrical items was recharged.") + +/obj/item/slimecross/regenerative/darkpurple + colour = "dark purple" + effect_desc = "Rapidly heals the target and gives them purple clothing if they are naked." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/darkpurple/core_effect(mob/living/target, mob/user) + var/equipped = 0 + equipped += target.equip_to_slot_or_del(new /obj/item/clothing/shoes/sneakers/purple(null), ITEM_SLOT_FEET) + equipped += target.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(null), ITEM_SLOT_ICLOTHING) + equipped += target.equip_to_slot_or_del(new /obj/item/clothing/gloves/color/purple(null), ITEM_SLOT_GLOVES) + equipped += target.equip_to_slot_or_del(new /obj/item/clothing/head/soft/purple(null), ITEM_SLOT_HEAD) + if(equipped > 0) + target.visible_message("The milky goo congeals into clothing!") + +/obj/item/slimecross/regenerative/darkblue + colour = "dark blue" + effect_desc = "Rapidly heals the target and fireproofs their clothes." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/darkblue/core_effect(mob/living/target, mob/user) + if(!ishuman(target)) + return + var/mob/living/carbon/human/H = target + var/fireproofed = FALSE + if(H.get_item_by_slot(ITEM_SLOT_OCLOTHING)) + fireproofed = TRUE + var/obj/item/clothing/C = H.get_item_by_slot(ITEM_SLOT_OCLOTHING) + fireproof(C) + if(H.get_item_by_slot(ITEM_SLOT_HEAD)) + fireproofed = TRUE + var/obj/item/clothing/C = H.get_item_by_slot(ITEM_SLOT_HEAD) + fireproof(C) + if(fireproofed) + target.visible_message("Some of [target]'s clothing gets coated in the goo, and turns blue!") + +/obj/item/slimecross/regenerative/darkblue/proc/fireproof(obj/item/clothing/C) + C.name = "fireproofed [C.name]" + C.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + C.add_atom_colour("#000080", FIXED_COLOUR_PRIORITY) + C.max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + C.heat_protection = C.body_parts_covered + C.resistance_flags |= FIRE_PROOF + +/obj/item/slimecross/regenerative/silver + colour = "silver" + effect_desc = "Rapidly heals the target, regenerating their nutrition at a far greater rate than normal" // monkestation edit: updates effect_desc to reflect the rework + +/* monkestation edit: replaced in [monkestation\code\modules\slimecore\crossbreeding\regenerative\extract.dm] +/obj/item/slimecross/regenerative/silver/core_effect(mob/living/target, mob/user) + target.set_nutrition(NUTRITION_LEVEL_FULL - 1) + to_chat(target, "You feel satiated.") +*/ + +/obj/item/slimecross/regenerative/bluespace + colour = "bluespace" + effect_desc = "Rapidly heals the target and teleports them to where this core was created." // monkestation edit: updates effect_desc to reflect the rework + var/turf/open/T + +/obj/item/slimecross/regenerative/bluespace/core_effect(mob/living/target, mob/user) + var/turf/old_location = get_turf(target) + if(do_teleport(target, T, channel = TELEPORT_CHANNEL_QUANTUM)) //despite being named a bluespace teleportation method the quantum channel is used to preserve precision teleporting with a bag of holding + old_location.visible_message("[target] disappears in a shower of sparks!") + to_chat(target, span_danger("The milky goo teleports you somewhere it remembers!")) + + +/obj/item/slimecross/regenerative/bluespace/Initialize(mapload) + . = ..() + T = get_turf(src) + +/obj/item/slimecross/regenerative/sepia + colour = "sepia" + effect_desc = "Rapidly heals the target. After 10 seconds, relocate the target to the initial position the core was used with their previous health status." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/sepia/core_effect_before(mob/living/target, mob/user) + to_chat(target, "You try to forget how you feel.") + target.AddComponent(/datum/component/dejavu) + +/obj/item/slimecross/regenerative/cerulean + colour = "cerulean" + effect_desc = "Rapidly heals the target and makes a second regenerative core with no special effects." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/cerulean/core_effect(mob/living/target, mob/user) + src.forceMove(user.loc) + var/obj/item/slimecross/X = new /obj/item/slimecross/regenerative(user.drop_location()) + X.name = name + X.desc = desc + user.put_in_active_hand(X) + to_chat(user, "Some of the milky goo congeals in your hand!") + +/obj/item/slimecross/regenerative/pyrite + colour = "pyrite" + effect_desc = "Rapidly heals and randomly colors the target." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/pyrite/core_effect(mob/living/target, mob/user) + target.visible_message("The milky goo coating [target] leaves [target.p_them()] a different color!") + target.add_atom_colour(rgb(rand(0,255),rand(0,255),rand(0,255)),WASHABLE_COLOUR_PRIORITY) + +/obj/item/slimecross/regenerative/red + colour = "red" + effect_desc = "Rapidly heals the target and injects them with some ephedrine." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/red/core_effect(mob/living/target, mob/user) + to_chat(target, "You feel... faster.") + target.reagents.add_reagent(/datum/reagent/medicine/ephedrine,3) + +/obj/item/slimecross/regenerative/green + colour = "green" + effect_desc = "Rapidly heals the target and changes the species or color of a slime or jellyperson." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/green/core_effect(mob/living/target, mob/user) + if(isslime(target)) + target.visible_message("\The [target] suddenly changes color!") + var/mob/living/basic/slime/S = target + S.start_mutating(TRUE) + if(isjellyperson(target)) + target.reagents.add_reagent(/datum/reagent/mutationtoxin/jelly, 5) + + +/obj/item/slimecross/regenerative/pink + colour = "pink" + effect_desc = "Rapidly heals the target and injects them with some krokodil." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/pink/core_effect(mob/living/target, mob/user) + to_chat(target, "You feel more calm.") + target.reagents.add_reagent(/datum/reagent/drug/krokodil,4) + +/obj/item/slimecross/regenerative/gold + colour = "gold" + effect_desc = "Rapidly heals the target and produces a random coin." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/gold/core_effect(mob/living/target, mob/user) + var/newcoin = pick(/obj/item/coin/silver, /obj/item/coin/iron, /obj/item/coin/gold, /obj/item/coin/diamond, /obj/item/coin/plasma, /obj/item/coin/uranium) + var/obj/item/coin/C = new newcoin(target.loc) + playsound(C, 'sound/items/coinflip.ogg', 50, TRUE) + target.put_in_hand(C) + +/obj/item/slimecross/regenerative/oil + colour = "oil" + effect_desc = "Rapidly heals the target and flashes everyone in sight." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/oil/core_effect(mob/living/target, mob/user) + playsound(src, 'sound/weapons/flash.ogg', 100, TRUE) + for(var/mob/living/L in view(user,7)) + L.flash_act() + +/obj/item/slimecross/regenerative/black + colour = "black" + effect_desc = "Rapidly heals the target and creates an imperfect duplicate of them made of slime, that fakes their death." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/black/core_effect_before(mob/living/target, mob/user) + var/dummytype = target.type + if(ismegafauna(target)) //Prevents megafauna duping in a lame way + dummytype = /mob/living/basic/slime + to_chat(user, "The milky goo flows over [target], falling into a weak puddle.") + var/mob/living/dummy = new dummytype(target.loc) + to_chat(target, "The milky goo flows from your skin, forming an imperfect copy of you.") + if(iscarbon(target)) + var/mob/living/carbon/T = target + var/mob/living/carbon/D = dummy + T.dna.transfer_identity(D) + D.updateappearance(mutcolor_update=1) + D.real_name = T.real_name + D.update_name_tag(D.real_name) // monkestation edit: name tags + dummy.adjustBruteLoss(target.getBruteLoss()) + dummy.adjustFireLoss(target.getFireLoss()) + dummy.adjustToxLoss(target.getToxLoss()) + dummy.death() + +/obj/item/slimecross/regenerative/lightpink + colour = "light pink" + effect_desc = "Rapidly heals the target and also heals the user." // monkestation edit: updates effect_desc to reflect the rework + +/obj/item/slimecross/regenerative/lightpink/core_effect(mob/living/target, mob/user) + if(!isliving(user)) + return + if(target == user) + return + var/mob/living/U = user + U.revive(HEAL_ALL) + to_chat(U, "Some of the milky goo sprays onto you, as well!") + +/obj/item/slimecross/regenerative/adamantine + colour = "adamantine" + effect_desc = "Rapidly heals the target, while boosting their armor and general resilience." // monkestation edit: updates effect_desc to reflect the rework, also a typo fix + +/obj/item/slimecross/regenerative/adamantine/core_effect(mob/living/target, mob/user) //WIP - Find out why this doesn't work. + target.apply_status_effect(/datum/status_effect/slimeskin) + +/obj/item/slimecross/regenerative/rainbow + colour = "rainbow" + effect_desc = "Fully heals the target, including all forms of damage, wounds, brain traumas, sickness, blood loss, and unsafe body temperature. Does not regrow limbs (except for oozelings) or refresh organs. Temporarily makes them immortal, but pacifistic." // monkestation edit: updates effect_desc to reflect the rework, also a typo fix + +/obj/item/slimecross/regenerative/rainbow/core_effect(mob/living/target, mob/user) + target.apply_status_effect(/datum/status_effect/rainbow_protection) diff --git a/code/modules/research/xenobiology/crossbreeding/regenerative/colors.dm b/code/modules/research/xenobiology/crossbreeding/regenerative/colors.dm deleted file mode 100644 index 7c279e461d552..0000000000000 --- a/code/modules/research/xenobiology/crossbreeding/regenerative/colors.dm +++ /dev/null @@ -1,44 +0,0 @@ -/datum/status_effect/regenerative_extract/purple - base_healing_amt = 10 - diminishing_multiplier = 0.5 - diminish_time = 1.5 MINUTES - extra_traits = list(TRAIT_NOCRITOVERLAY, TRAIT_NOSOFTCRIT) - -/datum/status_effect/regenerative_extract/purple/on_remove() - . = ..() - if(owner.has_dna()?.species?.reagent_tag & PROCESS_ORGANIC) // won't work during cooldown, and won't waste effort injecting into IPCs - var/inject_amt = round(10 * multiplier) - if(inject_amt >= 1) - owner.reagents?.add_reagent(/datum/reagent/medicine/regen_jelly, inject_amt) - - -/datum/status_effect/regenerative_extract/silver - base_healing_amt = 4 - nutrition_heal_cap = NUTRITION_LEVEL_WELL_FED + 50 - diminishing_multiplier = 0.8 - diminish_time = 30 SECONDS - extra_traits = list(TRAIT_NOFAT) - -/datum/status_effect/regenerative_extract/yellow - extra_traits = list(TRAIT_SHOCKIMMUNE, TRAIT_TESLA_SHOCKIMMUNE, TRAIT_AIRLOCK_SHOCKIMMUNE) - -/datum/status_effect/regenerative_extract/adamantine - extra_traits = list(TRAIT_FEARLESS, TRAIT_HARDLY_WOUNDED) - -// rainbow extracts are similar to old regen extract effects, albeit it won't replace your organs, and won't heal limbs (unless you're an oozeling) -#define RAINBOW_HEAL_FLAGS ~(HEAL_ADMIN | HEAL_RESTRAINTS | HEAL_LIMBS | HEAL_REFRESH_ORGANS) - -/datum/status_effect/regenerative_extract/rainbow - alert_type = null - diminishing_multiplier = 0 // you can't use other extracts at all during this time - tick_interval = -1 - -/datum/status_effect/regenerative_extract/rainbow/on_apply() - var/heal_flags = RAINBOW_HEAL_FLAGS - if(isoozeling(owner)) // have some mercy on oozelings - heal_flags |= HEAL_LIMBS - owner.revive(heal_flags) - return FALSE // return false so we immediately clear the effect and start the cooldown - - -#undef RAINBOW_HEAL_FLAGS diff --git a/code/modules/research/xenobiology/crossbreeding/regenerative/cooldown.dm b/code/modules/research/xenobiology/crossbreeding/regenerative/cooldown.dm deleted file mode 100644 index 0614d914d1168..0000000000000 --- a/code/modules/research/xenobiology/crossbreeding/regenerative/cooldown.dm +++ /dev/null @@ -1,26 +0,0 @@ -/datum/status_effect/slime_regen_cooldown - id = "slime_regen_cooldown" - status_type = STATUS_EFFECT_MULTIPLE - tick_interval = -1 - alert_type = null - remove_on_fullheal = TRUE - heal_flag_necessary = HEAL_ADMIN - /// The multiplier applied to the effect of a regen extract while this cooldown is active. - /// As multiple cooldowns can be active at the same time, these multipliers stack, resulting in exponentially diminishing returns. - var/multiplier = 1 - -/datum/status_effect/slime_regen_cooldown/on_creation(mob/living/new_owner, multiplier = 1, duration = 45 SECONDS) - src.multiplier = multiplier - src.duration = duration - return ..() - -/datum/status_effect/slime_regen_cooldown/on_apply() - RegisterSignal(owner, COMSIG_SLIME_REGEN_CALC, PROC_REF(apply_multiplier)) - return TRUE - -/datum/status_effect/slime_regen_cooldown/on_remove() - UnregisterSignal(owner, COMSIG_SLIME_REGEN_CALC) - -/datum/status_effect/slime_regen_cooldown/proc/apply_multiplier(datum/source, multiplier_ptr) - SIGNAL_HANDLER - *multiplier_ptr *= multiplier diff --git a/code/modules/research/xenobiology/crossbreeding/regenerative/effect.dm b/code/modules/research/xenobiology/crossbreeding/regenerative/effect.dm deleted file mode 100644 index 763b98a02068e..0000000000000 --- a/code/modules/research/xenobiology/crossbreeding/regenerative/effect.dm +++ /dev/null @@ -1,97 +0,0 @@ -/datum/status_effect/regenerative_extract - id = "Slime Regeneration" - status_type = STATUS_EFFECT_UNIQUE - duration = 15 SECONDS - tick_interval = 0.2 SECONDS - alert_type = /atom/movable/screen/alert/status_effect/regen_extract - show_duration = TRUE - /// The damage healed (for each type) per tick. - /// This is multipled against the multiplier derived from cooldowns. - var/base_healing_amt = 5 - /// The number multiplied against the base healing amount, - /// used for the "diminishing returns" cooldown effect. - var/multiplier = 1 - /// The multiplier that the cooldown applied after the effect ends will use. - var/diminishing_multiplier = 0.75 - /// How long the subsequent cooldown effect will last. - var/diminish_time = 45 SECONDS - /// The maximum nutrition level this regenerative extract can heal up to. - var/nutrition_heal_cap = NUTRITION_LEVEL_FED - 50 - /// Base traits given to the owner. - var/static/list/given_traits = list(TRAIT_ANALGESIA, TRAIT_NOCRITDAMAGE) - /// Extra traits given to the owner, added to the base traits. - var/list/extra_traits - -/datum/status_effect/regenerative_extract/on_apply() - // So this seems weird, but this allows us to have multiple things affect the regen multiplier, - // without doing something like hardcoding a `for(var/datum/status_effect/slime_regen_cooldown/cooldown in owner.status_effects)` - // Instead, cooldown effects register the [COMSIG_SLIME_REGEN_CALC] signal, and can affect our multiplier via the pointer we pass. - SEND_SIGNAL(owner, COMSIG_SLIME_REGEN_CALC, &multiplier) - if(multiplier < 1) - to_chat(owner, span_warning("The previous regenerative goo hasn't fully evaporated yet, weakening the new regenerative effect!")) - owner.add_traits(islist(extra_traits) ? (given_traits + extra_traits) : given_traits, id) - return TRUE - -/datum/status_effect/regenerative_extract/on_remove() - owner.remove_traits(islist(extra_traits) ? (given_traits + extra_traits) : given_traits, id) - owner.apply_status_effect(/datum/status_effect/slime_regen_cooldown, diminishing_multiplier, diminish_time) - -/datum/status_effect/regenerative_extract/tick(seconds_per_tick, times_fired) - var/heal_amt = base_healing_amt * seconds_per_tick * multiplier - heal_act(heal_amt) - owner.updatehealth() - -/datum/status_effect/regenerative_extract/proc/heal_act(heal_amt) - if(!heal_amt) - return - heal_damage(heal_amt) - heal_misc(heal_amt) - if(iscarbon(owner)) - heal_organs(heal_amt) - heal_wounds() - -/datum/status_effect/regenerative_extract/proc/heal_damage(heal_amt) - owner.heal_overall_damage(brute = heal_amt, burn = heal_amt, updating_health = FALSE) - owner.stamina?.adjust(-heal_amt, forced = TRUE) - owner.adjustOxyLoss(-heal_amt, updating_health = FALSE) - owner.adjustToxLoss(-heal_amt, updating_health = FALSE, forced = TRUE) - owner.adjustCloneLoss(-heal_amt, updating_health = FALSE) - -/datum/status_effect/regenerative_extract/proc/heal_misc(heal_amt) - owner.adjust_disgust(-heal_amt) - if(owner.blood_volume < BLOOD_VOLUME_NORMAL) - owner.blood_volume = min(owner.blood_volume + heal_amt, BLOOD_VOLUME_NORMAL) - if((owner.nutrition < nutrition_heal_cap) && !HAS_TRAIT(owner, TRAIT_NOHUNGER)) - owner.nutrition = min(owner.nutrition + heal_amt, nutrition_heal_cap) - -/datum/status_effect/regenerative_extract/proc/heal_organs(heal_amt) - var/static/list/ignored_traumas - if(!ignored_traumas) - ignored_traumas = typecacheof(list( - /datum/brain_trauma/hypnosis, - /datum/brain_trauma/special/obsessed, - /datum/brain_trauma/severe/split_personality/brainwashing, - )) - var/mob/living/carbon/carbon_owner = owner - for(var/obj/item/organ/organ in carbon_owner.organs) - organ.apply_organ_damage(-heal_amt) - // stupid manual trauma curing code, so you can't just remove trauma-based antags with one click - var/obj/item/organ/internal/brain/brain = carbon_owner.get_organ_slot(ORGAN_SLOT_BRAIN) - for(var/datum/brain_trauma/trauma as anything in shuffle(brain?.traumas)) - if(!is_type_in_typecache(trauma, ignored_traumas) && trauma.resilience <= TRAUMA_RESILIENCE_MAGIC) - qdel(trauma) - return - -/datum/status_effect/regenerative_extract/proc/heal_wounds() - var/mob/living/carbon/carbon_owner = owner - if(length(carbon_owner.all_wounds)) - var/list/datum/wound/ordered_wounds = sort_list(carbon_owner.all_wounds, GLOBAL_PROC_REF(cmp_wound_severity_dsc)) - ordered_wounds[1]?.remove_wound() - -/datum/status_effect/regenerative_extract/get_examine_text() - return "[owner.p_They()] have a subtle, gentle glow to [owner.p_their()] skin, with slime soothing [owner.p_their()] wounds." - -/atom/movable/screen/alert/status_effect/regen_extract - name = "Slime Regeneration" - desc = "A milky slime covers your skin, soothing and regenerating your injuries!" - icon_state = "regenerative_core" diff --git a/code/modules/research/xenobiology/crossbreeding/regenerative/extract.dm b/code/modules/research/xenobiology/crossbreeding/regenerative/extract.dm deleted file mode 100644 index 96b292669ee13..0000000000000 --- a/code/modules/research/xenobiology/crossbreeding/regenerative/extract.dm +++ /dev/null @@ -1,54 +0,0 @@ -/// Returns the typepath to the status effect that should be applied to the target when this extract is used on them. -/obj/item/slimecross/regenerative/proc/get_status_path() - var/color_path = text2path("/datum/status_effect/regenerative_extract/[colour]") - if(ispath(color_path, /datum/status_effect/regenerative_extract)) - return color_path - return /datum/status_effect/regenerative_extract - -/obj/item/slimecross/regenerative/proc/can_use(mob/living/target, mob/living/user) - if(target.stat == DEAD) - to_chat(user, span_warning("[src] will not work on the dead!")) - return FALSE - if(target.has_status_effect(/datum/status_effect/regenerative_extract)) - to_chat(user, span_warning("[target == user ? "You are" : "[target] is"] already being healed by a regenerative extract!")) - return FALSE - return TRUE - -/obj/item/slimecross/regenerative/afterattack(mob/living/target, mob/user, prox) - . = ..() - if(!prox || !isliving(target) || !can_use(target, user)) - return - if(target != user) - user.visible_message(span_notice("[user] crushes [src] over [target], the milky goo coating [target.p_their()] injuries!"), - span_notice("You squeeze [src], and it bursts over [target], the milky goo beginning to regenerate [target.p_their()] injuries.")) - else - user.visible_message(span_notice("[user] crushes [src] over [user.p_them()]self, the milky goo quickly regenerating all of [user.p_their()] injuries!"), - span_notice("You squeeze [src], and it bursts in your hand, splashing you with milky goo which quickly regenerates your injuries!")) - core_effect_before(target, user) - apply_effect(target) - core_effect(target, user) - playsound(target, 'sound/effects/splat.ogg', vol = 40, vary = TRUE) - qdel(src) - -/obj/item/slimecross/regenerative/proc/apply_effect(mob/living/target) - target.apply_status_effect(get_status_path()) - -/obj/item/slimecross/regenerative/silver/core_effect(mob/living/target, mob/user) - return // handled by the status effect - -/obj/item/slimecross/regenerative/lightpink/core_effect(mob/living/target, mob/living/user) - if(!isliving(user)) - return - if(target == user) - return - if(!user.has_status_effect(/datum/status_effect/regenerative_extract)) - apply_effect(user) - to_chat(user, span_notice("Some of the milky goo sprays onto you, as well!")) - else - to_chat(user, span_warning("Some of the milky goo sprays onto you, but slides off due to the regenerative effect...")) - -/obj/item/slimecross/regenerative/rainbow/can_use(mob/living/target, mob/living/user) - if(target.has_status_effect(/datum/status_effect/slime_regen_cooldown)) - to_chat(user, span_warning("[target == user ? "You are" : "[target] is"] still recovering from the last regenerative extract!")) - return FALSE - return ..() diff --git a/code/modules/research/xenobiology/crossbreeding/reproductive.dm b/code/modules/research/xenobiology/crossbreeding/reproductive.dm new file mode 100644 index 0000000000000..a8c2a61f5e048 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/reproductive.dm @@ -0,0 +1,147 @@ +/* +Reproductive extracts: + When fed three monkey cubes, produces between + 1 and 4 normal slime extracts of the same colour. +*/ + + +/obj/item/slimecross/reproductive + name = "reproductive extract" + desc = "It pulses with a strange hunger." + icon_state = "reproductive" + effect = "reproductive" + effect_desc = "When fed monkey cubes it produces more extracts. Bio bag compatible as well." + var/extract_type = /obj/item/slime_extract/ + var/cooldown = 3 SECONDS + var/feedAmount = 3 + var/last_produce = 0 + +/obj/item/slimecross/reproductive/examine() + . = ..() + . += span_danger("It appears to have eaten [length(contents)] Monkey Cube[p_s()]") + +/obj/item/slimecross/reproductive/Initialize(mapload) + . = ..() + create_storage(storage_type = /datum/storage/extract_inventory) + +/obj/item/slimecross/reproductive/attackby(obj/item/O, mob/user) + var/datum/storage/extract_inventory/slime_storage = atom_storage + if(!istype(slime_storage)) + return + + if((last_produce + cooldown) > world.time) + to_chat(user, "[src] is still digesting!") + return + + if(length(contents) >= feedAmount) //if for some reason the contents are full, but it didnt digest, attempt to digest again + to_chat(user, "[src] appears to be full but is not digesting! Maybe poking it stimulated it to digest.") + slime_storage?.processCubes(user) + return + + if(istype(O, /obj/item/storage/bag/xeno)) + var/list/inserted = list() + O.atom_storage.remove_type(/obj/item/food/monkeycube, src, feedAmount - length(contents), TRUE, FALSE, user, inserted) + if(inserted.len) + to_chat(user, "You feed [length(inserted)] Monkey Cube[p_s()] to [src], and it pulses gently.") + playsound(src, 'sound/items/eatfood.ogg', 20, TRUE) + slime_storage?.processCubes(user) + else + to_chat(user, "There are no monkey cubes in the bio bag!") + return + + else if(istype(O, /obj/item/food/monkeycube)) + if(atom_storage?.attempt_insert(O, user, override = TRUE, force = TRUE)) + to_chat(user, "You feed 1 Monkey Cube to [src], and it pulses gently.") + slime_storage?.processCubes(user) + playsound(src, 'sound/items/eatfood.ogg', 20, TRUE) + return + else + to_chat(user, "The [src] rejects the Monkey Cube!") //in case it fails to insert for whatever reason you get feedback + +/obj/item/slimecross/reproductive/grey + extract_type = /obj/item/slime_extract/grey + colour = "grey" + +/obj/item/slimecross/reproductive/orange + extract_type = /obj/item/slime_extract/orange + colour = "orange" + +/obj/item/slimecross/reproductive/purple + extract_type = /obj/item/slime_extract/purple + colour = "purple" + +/obj/item/slimecross/reproductive/blue + extract_type = /obj/item/slime_extract/blue + colour = "blue" + +/obj/item/slimecross/reproductive/metal + extract_type = /obj/item/slime_extract/metal + colour = "metal" + +/obj/item/slimecross/reproductive/yellow + extract_type = /obj/item/slime_extract/yellow + colour = "yellow" + +/obj/item/slimecross/reproductive/darkpurple + extract_type = /obj/item/slime_extract/darkpurple + colour = "dark purple" + +/obj/item/slimecross/reproductive/darkblue + extract_type = /obj/item/slime_extract/darkblue + colour = "dark blue" + +/obj/item/slimecross/reproductive/silver + extract_type = /obj/item/slime_extract/silver + colour = "silver" + +/obj/item/slimecross/reproductive/bluespace + extract_type = /obj/item/slime_extract/bluespace + colour = "bluespace" + +/obj/item/slimecross/reproductive/sepia + extract_type = /obj/item/slime_extract/sepia + colour = "sepia" + +/obj/item/slimecross/reproductive/cerulean + extract_type = /obj/item/slime_extract/cerulean + colour = "cerulean" + +/obj/item/slimecross/reproductive/pyrite + extract_type = /obj/item/slime_extract/pyrite + colour = "pyrite" + +/obj/item/slimecross/reproductive/red + extract_type = /obj/item/slime_extract/red + colour = "red" + +/obj/item/slimecross/reproductive/green + extract_type = /obj/item/slime_extract/green + colour = "green" + +/obj/item/slimecross/reproductive/pink + extract_type = /obj/item/slime_extract/pink + colour = "pink" + +/obj/item/slimecross/reproductive/gold + extract_type = /obj/item/slime_extract/gold + colour = "gold" + +/obj/item/slimecross/reproductive/oil + extract_type = /obj/item/slime_extract/oil + colour = "oil" + +/obj/item/slimecross/reproductive/black + extract_type = /obj/item/slime_extract/black + colour = "black" + +/obj/item/slimecross/reproductive/lightpink + extract_type = /obj/item/slime_extract/lightpink + colour = "light pink" + +/obj/item/slimecross/reproductive/adamantine + extract_type = /obj/item/slime_extract/adamantine + colour = "adamantine" + +/obj/item/slimecross/reproductive/rainbow + extract_type = /obj/item/slime_extract/rainbow + colour = "rainbow" diff --git a/code/modules/research/xenobiology/crossbreeding/selfsustaining.dm b/code/modules/research/xenobiology/crossbreeding/selfsustaining.dm new file mode 100644 index 0000000000000..4ebaa0f9e09f9 --- /dev/null +++ b/code/modules/research/xenobiology/crossbreeding/selfsustaining.dm @@ -0,0 +1,151 @@ +/* +Self-sustaining extracts: + Produces 4 extracts that do not need reagents. +*/ +/obj/item/slimecross/selfsustaining + name = "self-sustaining extract" + effect = "self-sustaining" + icon_state = "selfsustaining" + var/extract_type = /obj/item/slime_extract + +/obj/item/autoslime + name = "autoslime" + desc = "It resembles a normal slime extract, but seems filled with a strange, multi-colored fluid." + var/obj/item/slime_extract/extract + var/effect_desc = "A self-sustaining slime extract. When used, lets you choose which reaction you want." + +//Just divides into the actual item. +/obj/item/slimecross/selfsustaining/Initialize(mapload) + ..() + visible_message("The [src] shudders, and splits into four smaller extracts.") + for(var/i in 1 to 4) + var/obj/item/autoslime/A = new /obj/item/autoslime(src.loc) + var/obj/item/slime_extract/X = new extract_type(A) + A.extract = X + A.icon = icon + A.icon_state = icon_state + A.color = color + A.name = "self-sustaining " + colour + " extract" + return INITIALIZE_HINT_QDEL + +/obj/item/autoslime/Initialize(mapload) + return ..() + +/obj/item/autoslime/attack_self(mob/user) + var/reagentselect = tgui_input_list(user, "Reagent the extract will produce.", "Self-sustaining Reaction", sort_list(extract.activate_reagents, GLOBAL_PROC_REF(cmp_typepaths_asc))) + if(isnull(reagentselect)) + return + var/amount = 5 + var/secondary + + if (user.get_active_held_item() != src || user.stat != CONSCIOUS || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) + return + if(!reagentselect) + return + if(reagentselect == "lesser plasma") + amount = 4 + reagentselect = /datum/reagent/toxin/plasma + if(reagentselect == "holy water and uranium") + reagentselect = /datum/reagent/water/holywater + secondary = /datum/reagent/uranium + extract.forceMove(user.drop_location()) + qdel(src) + user.put_in_active_hand(extract) + extract.reagents.add_reagent(reagentselect,amount) + if(secondary) + extract.reagents.add_reagent(secondary,amount) + +/obj/item/autoslime/examine(mob/user) + . = ..() + if(effect_desc) + . += "[effect_desc]" + +//Different types. + +/obj/item/slimecross/selfsustaining/grey + extract_type = /obj/item/slime_extract/grey + colour = "grey" + +/obj/item/slimecross/selfsustaining/orange + extract_type = /obj/item/slime_extract/orange + colour = "orange" + +/obj/item/slimecross/selfsustaining/purple + extract_type = /obj/item/slime_extract/purple + colour = "purple" + +/obj/item/slimecross/selfsustaining/blue + extract_type = /obj/item/slime_extract/blue + colour = "blue" + +/obj/item/slimecross/selfsustaining/metal + extract_type = /obj/item/slime_extract/metal + colour = "metal" + +/obj/item/slimecross/selfsustaining/yellow + extract_type = /obj/item/slime_extract/yellow + colour = "yellow" + +/obj/item/slimecross/selfsustaining/darkpurple + extract_type = /obj/item/slime_extract/darkpurple + colour = "dark purple" + +/obj/item/slimecross/selfsustaining/darkblue + extract_type = /obj/item/slime_extract/darkblue + colour = "dark blue" + +/obj/item/slimecross/selfsustaining/silver + extract_type = /obj/item/slime_extract/silver + colour = "silver" + +/obj/item/slimecross/selfsustaining/bluespace + extract_type = /obj/item/slime_extract/bluespace + colour = "bluespace" + +/obj/item/slimecross/selfsustaining/sepia + extract_type = /obj/item/slime_extract/sepia + colour = "sepia" + +/obj/item/slimecross/selfsustaining/cerulean + extract_type = /obj/item/slime_extract/cerulean + colour = "cerulean" + +/obj/item/slimecross/selfsustaining/pyrite + extract_type = /obj/item/slime_extract/pyrite + colour = "pyrite" + +/obj/item/slimecross/selfsustaining/red + extract_type = /obj/item/slime_extract/red + colour = "red" + +/obj/item/slimecross/selfsustaining/green + extract_type = /obj/item/slime_extract/green + colour = "green" + +/obj/item/slimecross/selfsustaining/pink + extract_type = /obj/item/slime_extract/pink + colour = "pink" + +/obj/item/slimecross/selfsustaining/gold + extract_type = /obj/item/slime_extract/gold + colour = "gold" + +/obj/item/slimecross/selfsustaining/oil + extract_type = /obj/item/slime_extract/oil + colour = "oil" + +/obj/item/slimecross/selfsustaining/black + extract_type = /obj/item/slime_extract/black + colour = "black" + +/obj/item/slimecross/selfsustaining/lightpink + extract_type = /obj/item/slime_extract/lightpink + colour = "light pink" + +/obj/item/slimecross/selfsustaining/adamantine + extract_type = /obj/item/slime_extract/adamantine + colour = "adamantine" + +/obj/item/slimecross/selfsustaining/rainbow + extract_type = /obj/item/slime_extract/rainbow + colour = "rainbow" diff --git a/code/modules/research/xenobiology/crossbreeding/stabilized.dm b/code/modules/research/xenobiology/crossbreeding/stabilized.dm index 71ac69cbcd858..8b3ac3abec356 100644 --- a/code/modules/research/xenobiology/crossbreeding/stabilized.dm +++ b/code/modules/research/xenobiology/crossbreeding/stabilized.dm @@ -1,29 +1,201 @@ -/obj/item/slimecross/stabilized/rainbow/Destroy() - if(!QDELETED(regencore)) - regencore.forceMove(drop_location()) - regencore = null +/* +Stabilized extracts: + Provides a passive buff to the holder. +*/ + +//To add: Create an effect in crossbreeding/_status_effects.dm with the name "/datum/status_effect/stabilized/[color]" +//Status effect will automatically be applied while held, and lost on drop. + +/obj/item/slimecross/stabilized + name = "stabilized extract" + desc = "It seems inert, but anything it touches glows softly..." + effect = "stabilized" + icon_state = "stabilized" + var/datum/status_effect/linked_effect + +/obj/item/slimecross/stabilized/Initialize(mapload) + . = ..() + START_PROCESSING(SSobj,src) + +/obj/item/slimecross/stabilized/Destroy() + STOP_PROCESSING(SSobj,src) + qdel(linked_effect) return ..() -/datum/status_effect/stabilized/rainbow/tick() - if(owner.health <= 0) - var/obj/item/slimecross/stabilized/rainbow/extract = linked_extract - if(!istype(extract) || QDELING(extract) || QDELETED(extract.regencore)) - return - // bypasses cooldowns, but also removes any existing regen effects - owner.remove_status_effect(/datum/status_effect/regenerative_extract) - owner.remove_status_effect(/datum/status_effect/slime_regen_cooldown) - owner.visible_message(span_hypnophrase("[owner] flashes a rainbow of colors, and [owner.p_their()] skin is coated in a milky regenerative goo!")) - playsound(owner, 'sound/effects/splat.ogg', vol = 40, vary = TRUE) - apply_regen(extract.regencore) - QDEL_NULL(linked_extract) - qdel(src) +/// Returns the mob that is currently holding us if we are either in their inventory or a backpack analogue. +/// Returns null if it's in an invalid location, so that we can check explicitly for null later. +/obj/item/slimecross/stabilized/proc/get_held_mob() + if(isnull(loc)) + return null + if(isliving(loc)) + return loc + // Snowflake check for modsuit backpacks, which should be valid but are 3 rather than 2 steps from the owner + if(istype(loc, /obj/item/mod/module/storage)) + var/obj/item/mod/module/storage/mod_backpack = loc + var/mob/living/modsuit_wearer = mod_backpack.mod?.wearer + return modsuit_wearer ? modsuit_wearer : null + var/nested_loc = loc.loc + if (isliving(nested_loc)) + return nested_loc + return null + +/obj/item/slimecross/stabilized/process() + var/mob/living/holder = get_held_mob() + if(isnull(holder)) return - return ..() + var/effectpath = /datum/status_effect/stabilized + var/static/list/effects = subtypesof(/datum/status_effect/stabilized) + for(var/datum/status_effect/stabilized/effect as anything in effects) + if(initial(effect.colour) != colour) + continue + effectpath = effect + break + if (holder.has_status_effect(effectpath)) + return + holder.apply_status_effect(effectpath, src) + STOP_PROCESSING(SSobj,src) + +//Colors and subtypes: +/obj/item/slimecross/stabilized/grey + colour = "grey" + effect_desc = "Makes slimes friendly to the owner" + +/obj/item/slimecross/stabilized/orange + colour = "orange" + effect_desc = "Passively tries to increase or decrease the owner's body temperature to normal" + +/obj/item/slimecross/stabilized/purple + colour = "purple" + effect_desc = "Provides a regeneration effect" + +/obj/item/slimecross/stabilized/blue + colour = "blue" + effect_desc = "Makes the owner immune to slipping on water, soap or foam. Space lube and ice are still too slippery." + +/obj/item/slimecross/stabilized/metal + colour = "metal" + effect_desc = "Every 30 seconds, adds a sheet of material to a random stack in the owner's backpack." + +/obj/item/slimecross/stabilized/yellow + colour = "yellow" + effect_desc = "Every ten seconds it recharges a device on the owner by 10%." + +/obj/item/slimecross/stabilized/darkpurple + colour = "dark purple" + effect_desc = "Gives you burning fingertips, automatically cooking any microwavable food you hold." + +/obj/item/slimecross/stabilized/darkblue + colour = "dark blue" + effect_desc = "Slowly extinguishes the owner if they are on fire, also wets items like monkey cubes, creating a monkey." + +/obj/item/slimecross/stabilized/silver + colour = "silver" + effect_desc = "Slows the rate at which the owner loses nutrition" + +/obj/item/slimecross/stabilized/bluespace + colour = "bluespace" + effect_desc = "On a two minute cooldown, when the owner has taken enough damage, they are teleported to a safe place." +/obj/item/slimecross/stabilized/sepia + colour = "sepia" + effect_desc = "Randomly adjusts the owner's speed." -/datum/status_effect/stabilized/rainbow/proc/apply_regen(obj/item/slimecross/regenerative/regen_core) - regen_core.core_effect_before(owner, owner) - regen_core.apply_effect(owner) - regen_core.core_effect(owner, owner) - qdel(regen_core) +/obj/item/slimecross/stabilized/cerulean + colour = "cerulean" + effect_desc = "Creates a duplicate of the owner. If the owner dies they will take control of the duplicate, unless the death was from beheading or gibbing." +/obj/item/slimecross/stabilized/pyrite + colour = "pyrite" + effect_desc = "Randomly colors the owner every few seconds." + +/obj/item/slimecross/stabilized/red + colour = "red" + effect_desc = "Nullifies all equipment based slowdowns." + +/obj/item/slimecross/stabilized/green + colour = "green" + effect_desc = "Changes the owner's name and appearance while holding this extract." + +/obj/item/slimecross/stabilized/pink + colour = "pink" + effect_desc = "As long as no creatures are harmed in the owner's presense, they will not attack you. If the peace is broken it takes two minutes to restore." + +/obj/item/slimecross/stabilized/gold + colour = "gold" + effect_desc = "Creates a pet when held." + var/mob_type + var/datum/mind/saved_mind + var/mob_name = "Familiar" + +/obj/item/slimecross/stabilized/gold/proc/generate_mobtype() + var/static/list/mob_spawn_pets = list() + if(!length(mob_spawn_pets)) + for(var/mob/living/simple_animal/animal as anything in subtypesof(/mob/living/simple_animal)) + if(initial(animal.gold_core_spawnable) == FRIENDLY_SPAWN) + mob_spawn_pets += animal + for(var/mob/living/basic/basicanimal as anything in subtypesof(/mob/living/basic)) + if(initial(basicanimal.gold_core_spawnable) == FRIENDLY_SPAWN) + mob_spawn_pets += basicanimal + mob_type = pick(mob_spawn_pets) + +/obj/item/slimecross/stabilized/gold/Initialize(mapload) + . = ..() + generate_mobtype() + +/obj/item/slimecross/stabilized/gold/attack_self(mob/user) + var/choice = tgui_input_list(user, "Which do you want to reset?", "Familiar Adjustment", sort_list(list("Familiar Location", "Familiar Species", "Familiar Sentience", "Familiar Name"))) + if(isnull(choice)) + return + if(!user.can_perform_action(src)) + return + if(isliving(user)) + var/mob/living/L = user + if(L.has_status_effect(/datum/status_effect/stabilized/gold)) + L.remove_status_effect(/datum/status_effect/stabilized/gold) + if(choice == "Familiar Location") + to_chat(user, "You prod [src], and it shudders slightly.") + START_PROCESSING(SSobj, src) + if(choice == "Familiar Species") + to_chat(user, "You squeeze [src], and a shape seems to shift around inside.") + generate_mobtype() + START_PROCESSING(SSobj, src) + if(choice == "Familiar Sentience") + to_chat(user, "You poke [src], and it lets out a glowing pulse.") + saved_mind = null + START_PROCESSING(SSobj, src) + if(choice == "Familiar Name") + var/newname = sanitize_name(tgui_input_text(user, "Would you like to change the name of [mob_name]", "Name change", mob_name, MAX_NAME_LEN)) + if(newname) + mob_name = newname + to_chat(user, "You speak softly into [src], and it shakes slightly in response.") + START_PROCESSING(SSobj, src) + +/obj/item/slimecross/stabilized/oil + colour = "oil" + effect_desc = "The owner will violently explode when they die while holding this extract." + +/obj/item/slimecross/stabilized/black + colour = "black" + effect_desc = "While strangling someone, the owner's hands melt around their neck, draining their life in exchange for food and healing." + +/obj/item/slimecross/stabilized/lightpink + colour = "light pink" + effect_desc = "The owner moves at high speeds while holding this extract, also stabilizes anyone in critical condition around you using Epinephrine." + +/obj/item/slimecross/stabilized/adamantine + colour = "adamantine" + effect_desc = "Owner gains a slight boost in damage resistance to all types." + +/obj/item/slimecross/stabilized/rainbow + colour = "rainbow" + effect_desc = "Accepts a regenerative extract and automatically uses it if the owner enters a critical condition." + var/obj/item/slimecross/regenerative/regencore + +/obj/item/slimecross/stabilized/rainbow/attackby(obj/item/O, mob/user) + var/obj/item/slimecross/regenerative/regen = O + if(istype(regen) && !regencore) + to_chat(user, "You place [O] in [src], prepping the extract for automatic application!") + regencore = regen + regen.forceMove(src) + return + return ..() diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index e2cebcfa8c8bc..06473cdd231e7 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -1,3 +1,1060 @@ +/// Slime Extracts /// + +/obj/item/slime_extract + name = "slime extract" + desc = "Goo extracted from a slime. Legends claim these to have \"magical powers\"." + icon = 'monkestation/code/modules/slimecore/icons/slimes.dmi' + icon_state = "grey_slime_extract" + force = 0 + w_class = WEIGHT_CLASS_TINY + throwforce = 0 + throw_speed = 3 + throw_range = 6 + grind_results = list() + var/Uses = 1 ///uses before it goes inert + var/qdel_timer = null ///deletion timer, for delayed reactions + var/effectmod ///Which type of crossbred + var/list/activate_reagents = list() ///Reagents required for activation + var/recurring = FALSE + + var/tier = 1 + +/obj/item/slime_extract/examine(mob/user) + . = ..() + if(Uses > 1) + . += "It has [Uses] uses remaining." + +/obj/item/slime_extract/attackby(obj/item/O, mob/user) + if(istype(O, /obj/item/slimepotion/enhancer)) + if(Uses >= 5 || recurring) + to_chat(user, "You cannot enhance this extract further!") + return ..() + if(O.type == /obj/item/slimepotion/enhancer) //Seriously, why is this defined here...? + to_chat(user, "You apply the enhancer to the slime extract. It may now be reused one more time.") + Uses++ + if(O.type == /obj/item/slimepotion/enhancer/max) + to_chat(user, "You dump the maximizer on the slime extract. It can now be used a total of 5 times!") + Uses = 5 + qdel(O) + ..() + +/obj/item/slime_extract/Initialize(mapload) + . = ..() + create_reagents(100, INJECTABLE | DRAWABLE) + +/obj/item/slime_extract/on_grind() + . = ..() + if(Uses) + grind_results[/datum/reagent/toxin/slimejelly] = 20 + +/** +* Effect when activated by a Luminescent. +* +* This proc is called whenever a Luminescent consumes a slime extract. Each one is separated into major and minor effects depending on the extract. Cooldown is measured in deciseconds. +* +* * arg1 - The mob absorbing the slime extract. +* * arg2 - The valid species for the absorbtion. Should always be a Luminescent unless something very major has changed. +* * arg3 - Whether or not the activation is major or minor. Major activations have large, complex effects, minor are simple. +*/ +/obj/item/slime_extract/proc/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + to_chat(user, "Nothing happened... This slime extract cannot be activated this way.") + return FALSE + +/obj/item/slime_extract/grey + name = "grey slime extract" + icon_state = "grey_slime_extract" + effectmod = "reproductive" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) + +/obj/item/slime_extract/grey/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + var/obj/item/stack/biomass/M = new + if(!user.put_in_active_hand(M)) + M.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + to_chat(user, "You spit out a monkey cube.") + return 120 + if(SLIME_ACTIVATE_MAJOR) + to_chat(user, "Your [name] starts pulsing...") + if(do_after(user, 40, target = user)) + var/mob/living/basic/slime/S = new(get_turf(user)) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + to_chat(user, "You spit out [S].") + return 350 + else + return 0 + +/obj/item/slime_extract/gold + name = "gold slime extract" + icon_state = "gold_slime_extract" + effectmod = "symbiont" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) + tier = 4 + + + +/obj/item/slime_extract/gold/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + user.visible_message("[user] starts shaking!"),"Your [name] starts pulsing gently..." + if(do_after(user, 40, target = user)) + var/mob/living/spawned_mob = create_random_mob(user.drop_location(), FRIENDLY_SPAWN) + spawned_mob.faction |= FACTION_NEUTRAL + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [spawned_mob]!"), "You spit out [spawned_mob]!" + return 300 + + if(SLIME_ACTIVATE_MAJOR) + user.visible_message("[user] starts shaking violently!"),span_warning("Your [name] starts pulsing violently...") + if(do_after(user, 50, target = user)) + var/mob/living/spawned_mob = create_random_mob(user.drop_location(), HOSTILE_SPAWN) + if(!(user.istate & ISTATE_HARM)) + spawned_mob.faction |= FACTION_NEUTRAL + else + spawned_mob.faction |= FACTION_SLIME + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [spawned_mob]!"), span_warning("You spit out [spawned_mob]!") + return 600 + +/obj/item/slime_extract/silver + name = "silver slime extract" + icon_state = "silver_slime_extract" + effectmod = "consuming" + activate_reagents = list(/datum/reagent/toxin/plasma,/datum/reagent/water) + tier = 2 + + + +/obj/item/slime_extract/silver/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + var/food_type = get_random_food() + var/obj/item/food/food_item = new food_type + ADD_TRAIT(food_item, TRAIT_FOOD_SILVER, INNATE_TRAIT) + if(!user.put_in_active_hand(food_item)) + food_item.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [food_item]!"), "You spit out [food_item]!" + return 200 + if(SLIME_ACTIVATE_MAJOR) + var/drink_type = get_random_drink() + var/obj/O = new drink_type + if(!user.put_in_active_hand(O)) + O.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [O]!"), "You spit out [O]!" + return 200 + +/obj/item/slime_extract/metal + name = "metal slime extract" + icon_state = "metal_slime_extract" + effectmod = "industrial" + activate_reagents = list(/datum/reagent/toxin/plasma,/datum/reagent/water) + +/obj/item/slime_extract/metal/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + var/obj/item/stack/sheet/glass/O = new(null, 5) + if(!user.put_in_active_hand(O)) + O.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [O]!"), "You spit out [O]!" + return 150 + + if(SLIME_ACTIVATE_MAJOR) + var/obj/item/stack/sheet/iron/O = new(null, 5) + if(!user.put_in_active_hand(O)) + O.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [O]!"), "You spit out [O]!" + return 200 + +/obj/item/slime_extract/purple + name = "purple slime extract" + icon_state = "purple_slime_extract" + effectmod = "regenerative" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) + +/obj/item/slime_extract/purple/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + user.adjust_nutrition(50) + user.blood_volume += 50 + to_chat(user, "You activate [src], and your body is refilled with fresh slime jelly!") + return 150 + + if(SLIME_ACTIVATE_MAJOR) + to_chat(user, "You activate [src], and it releases regenerative chemicals!") + user.reagents.add_reagent(/datum/reagent/medicine/regen_jelly,10) + return 600 + +/obj/item/slime_extract/darkpurple + name = "dark purple slime extract" + icon_state = "dark_purple_slime_extract" + effectmod = "self-sustaining" + activate_reagents = list(/datum/reagent/toxin/plasma) + tier = 2 + +/obj/item/slime_extract/darkpurple/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + var/obj/item/stack/sheet/mineral/plasma/O = new(null, 1) + if(!user.put_in_active_hand(O)) + O.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [O]!"), "You spit out [O]!" + return 150 + + if(SLIME_ACTIVATE_MAJOR) + var/turf/open/T = get_turf(user) + if(istype(T)) + T.atmos_spawn_air("plasma=20") + to_chat(user, "You activate [src], and a cloud of plasma bursts out of your skin!") + return 900 + +/obj/item/slime_extract/orange + name = "orange slime extract" + icon_state = "orange_slime_extract" + effectmod = "burning" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) + +/obj/item/slime_extract/orange/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + to_chat(user, "You activate [src]. You start feeling hot!") + user.reagents.add_reagent(/datum/reagent/consumable/capsaicin,10) + return 150 + + if(SLIME_ACTIVATE_MAJOR) + user.reagents.add_reagent(/datum/reagent/phosphorus,5)// + user.reagents.add_reagent(/datum/reagent/potassium,5) // = smoke, along with any reagents inside mr. slime + user.reagents.add_reagent(/datum/reagent/consumable/sugar,5) // + to_chat(user, "You activate [src], and a cloud of smoke bursts out of your skin!") + return 450 + +/obj/item/slime_extract/yellow + name = "yellow slime extract" + icon_state = "yellow_slime_extract" + effectmod = "charged" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) + tier = 2 + +/obj/item/slime_extract/yellow/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + if(species.glow_intensity != LUMINESCENT_DEFAULT_GLOW) + to_chat(user, "Your glow is already enhanced!") + return + species.update_glow(user, 5) + addtimer(CALLBACK(species, TYPE_PROC_REF(/datum/species/jelly/luminescent, update_glow), user, LUMINESCENT_DEFAULT_GLOW), 600) + to_chat(user, "You start glowing brighter.") + + if(SLIME_ACTIVATE_MAJOR) + user.visible_message("[user]'s skin starts flashing intermittently..."), span_warning("Your skin starts flashing intermittently...") + if(do_after(user, 25, target = user)) + empulse(user, 1, 2) + user.visible_message("[user]'s skin flashes!"), span_warning("Your skin flashes as you emit an electromagnetic pulse!") + return 600 + +/obj/item/slime_extract/red + name = "red slime extract" + icon_state = "red_slime_extract" + effectmod = "sanguine" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) + tier = 3 + +/obj/item/slime_extract/red/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + to_chat(user, "You activate [src]. You start feeling fast!") + user.reagents.add_reagent(/datum/reagent/medicine/ephedrine,5) + return 450 + + if(SLIME_ACTIVATE_MAJOR) + user.visible_message("[user]'s skin flashes red for a moment..."), span_warning("Your skin flashes red as you emit rage-inducing pheromones...") + for(var/mob/living/basic/slime/slime in viewers(get_turf(user), null)) + ADD_TRAIT(slime, TRAIT_SLIME_RABID, "red-extract") + slime.visible_message(span_danger("The [slime] is driven into a frenzy!")) + return 600 + +/obj/item/slime_extract/blue + name = "blue slime extract" + icon_state = "blue_slime_extract" + effectmod = "stabilized" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) + +/obj/item/slime_extract/blue/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + to_chat(user, "You activate [src]. Your genome feels more stable!") + user.adjustCloneLoss(-15) + user.reagents.add_reagent(/datum/reagent/medicine/mutadone, 10) + user.reagents.add_reagent(/datum/reagent/medicine/potass_iodide, 10) + return 250 + + if(SLIME_ACTIVATE_MAJOR) + user.reagents.create_foam(/datum/effect_system/fluid_spread/foam, 20, log = TRUE) + user.visible_message(span_danger("Foam spews out from [user]'s skin!"), "You activate [src], and foam bursts out of your skin!") + return 600 + +/obj/item/slime_extract/darkblue + name = "dark blue slime extract" + icon_state = "dark_blue_slime_extract" + effectmod = "chilling" + activate_reagents = list(/datum/reagent/toxin/plasma,/datum/reagent/water) + tier = 2 + +/obj/item/slime_extract/darkblue/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + to_chat(user, "You activate [src]. You start feeling colder!") + user.extinguish_mob() + user.adjust_wet_stacks(20) + user.reagents.add_reagent(/datum/reagent/consumable/frostoil,6) + user.reagents.add_reagent(/datum/reagent/medicine/regen_jelly,7) + return 100 + + if(SLIME_ACTIVATE_MAJOR) + var/turf/open/T = get_turf(user) + if(istype(T)) + T.atmos_spawn_air("nitrogen=40;TEMP=2.7") + to_chat(user, "You activate [src], and icy air bursts out of your skin!") + return 900 + +/obj/item/slime_extract/pink + name = "pink slime extract" + icon_state = "pink_slime_extract" + effectmod = "gentle" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) + tier = 4 + +/obj/item/slime_extract/pink/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + if(user.gender != MALE && user.gender != FEMALE) + to_chat(user, "You can't swap your gender!") + return + + if(user.gender == MALE) + user.gender = FEMALE + user.visible_message(span_boldnotice("[user] suddenly looks more feminine!"), span_boldwarning("You suddenly feel more feminine!")) + else + user.gender = MALE + user.visible_message(span_boldnotice("[user] suddenly looks more masculine!"), span_boldwarning("You suddenly feel more masculine!")) + return 100 + + if(SLIME_ACTIVATE_MAJOR) + user.visible_message("[user]'s skin starts flashing hypnotically..."), "Your skin starts forming odd patterns, pacifying creatures around you." + for(var/mob/living/carbon/C in viewers(user, null)) + if(C != user) + C.reagents.add_reagent(/datum/reagent/pax,2) + return 600 + +/obj/item/slime_extract/green + name = "green slime extract" + icon_state = "green_slime_extract" + effectmod = "mutative" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/uranium/radium) + tier = 4 + +/obj/item/slime_extract/green/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + to_chat(user, "You feel yourself reverting to human form...") + if(do_after(user, 120, target = user)) + to_chat(user, "You feel human again!") + user.set_species(/datum/species/human) + return + to_chat(user, "You stop the transformation.") + + if(SLIME_ACTIVATE_MAJOR) + to_chat(user, "You feel yourself radically changing your slime type...") + if(do_after(user, 120, target = user)) + to_chat(user, "You feel different!") + user.set_species(pick(/datum/species/jelly/slime, /datum/species/jelly/stargazer)) + return + to_chat(user, "You stop the transformation.") + +/obj/item/slime_extract/lightpink + name = "light pink slime extract" + icon_state = "light_pink_slime_extract" + effectmod = "loyal" + activate_reagents = list(/datum/reagent/toxin/plasma) + tier = 5 + +/obj/item/slime_extract/lightpink/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + var/obj/item/slimepotion/slime/renaming/O = new(null, 1) + if(!user.put_in_active_hand(O)) + O.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [O]!"), "You spit out [O]!" + return 150 + + if(SLIME_ACTIVATE_MAJOR) + var/obj/item/slimepotion/slime/sentience/O = new(null, 1) + if(!user.put_in_active_hand(O)) + O.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [O]!"), "You spit out [O]!" + return 450 + +/obj/item/slime_extract/black + name = "black slime extract" + icon_state = "black_slime_extract" + effectmod = "transformative" + activate_reagents = list(/datum/reagent/toxin/plasma) + tier = 5 + +/obj/item/slime_extract/black/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + user.infect_disease_predefined(DISEASE_SLIME, TRUE, "[ROUND_TIME()] Black slime extract Infection [key_name(user)]") + return 100 + + if(SLIME_ACTIVATE_MAJOR) + to_chat(user, "You feel your own light turning dark...") + if(do_after(user, 120, target = user)) + to_chat(user, "You feel a longing for darkness.") + user.set_species(pick(/datum/species/shadow)) + return + to_chat(user, "You stop feeding [src].") + +/obj/item/slime_extract/oil + name = "oil slime extract" + icon_state = "oil_slime_extract" + effectmod = "detonating" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) + tier = 5 + +/obj/item/slime_extract/oil/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + to_chat(user, "You vomit slippery oil.") + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + new /obj/effect/decal/cleanable/oil/slippery(get_turf(user)) + return 450 + + if(SLIME_ACTIVATE_MAJOR) + user.visible_message("[user]'s skin starts pulsing and glowing ominously..."), span_userdanger("You feel unstable...") + if(do_after(user, 60, target = user)) + to_chat(user, span_userdanger("You explode!")) + explosion(user, devastation_range = 1, heavy_impact_range = 3, light_impact_range = 6, explosion_cause = src) + user.investigate_log("has been gibbed by an oil slime extract explosion.", INVESTIGATE_DEATHS) + user.gib() + return + to_chat(user, "You stop feeding [src], and the feeling passes.") + +/obj/item/slime_extract/adamantine + name = "adamantine slime extract" + icon_state = "adamantine_slime_extract" + effectmod = "crystalline" + activate_reagents = list(/datum/reagent/toxin/plasma) + tier = 5 + +/obj/item/slime_extract/adamantine/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + if(species.armor > 0) + to_chat(user, "Your skin is already hardened!") + return + to_chat(user, "You feel your skin harden and become more resistant.") + species.armor += 25 + addtimer(CALLBACK(src, PROC_REF(reset_armor), species), 1200) + return 450 + + if(SLIME_ACTIVATE_MAJOR) + to_chat(user, "You feel your body rapidly crystallizing...") + if(do_after(user, 120, target = user)) + to_chat(user, "You feel solid.") + user.set_species(pick(/datum/species/golem/adamantine)) + return + to_chat(user, "You stop feeding [src], and your body returns to its slimelike state.") + +/obj/item/slime_extract/adamantine/proc/reset_armor(datum/species/jelly/luminescent/species) + if(istype(species)) + species.armor -= 25 + +/obj/item/slime_extract/bluespace + name = "bluespace slime extract" + icon_state = "bluespace_slime_extract" + effectmod = "warping" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) + var/teleport_ready = FALSE + var/teleport_x = 0 + var/teleport_y = 0 + var/teleport_z = 0 + tier = 6 + +/obj/item/slime_extract/bluespace/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + to_chat(user, "You feel your body vibrating...") + if(do_after(user, 25, target = user)) + to_chat(user, "You teleport!") + do_teleport(user, get_turf(user), 6, asoundin = 'sound/weapons/emitter2.ogg', channel = TELEPORT_CHANNEL_BLUESPACE) + return 300 + + if(SLIME_ACTIVATE_MAJOR) + if(!teleport_ready) + to_chat(user, "You feel yourself anchoring to this spot...") + var/turf/T = get_turf(user) + teleport_x = T.x + teleport_y = T.y + teleport_z = T.z + teleport_ready = TRUE + else + teleport_ready = FALSE + if(teleport_x && teleport_y && teleport_z) + var/turf/T = locate(teleport_x, teleport_y, teleport_z) + to_chat(user, "You snap back to your anchor point!") + do_teleport(user, T, asoundin = 'sound/weapons/emitter2.ogg', channel = TELEPORT_CHANNEL_BLUESPACE) + return 450 + + +/obj/item/slime_extract/pyrite + name = "pyrite slime extract" + icon_state = "pyrite_slime_extract" + effectmod = "prismatic" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) + tier = 3 + +/obj/item/slime_extract/pyrite/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + var/chosen = pick(difflist(subtypesof(/obj/item/toy/crayon),typesof(/obj/item/toy/crayon/spraycan))) + var/obj/item/O = new chosen(null) + if(!user.put_in_active_hand(O)) + O.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [O]!"), "You spit out [O]!" + return 150 + + if(SLIME_ACTIVATE_MAJOR) + var/blacklisted_cans = list(/obj/item/toy/crayon/spraycan/borg, /obj/item/toy/crayon/spraycan/infinite) + var/chosen = pick(subtypesof(/obj/item/toy/crayon/spraycan) - blacklisted_cans) + var/obj/item/O = new chosen(null) + if(!user.put_in_active_hand(O)) + O.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [O]!"), "You spit out [O]!" + return 250 + +/obj/item/slime_extract/cerulean + name = "cerulean slime extract" + icon_state = "cerulean_slime_extract" + effectmod = "recurring" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) + tier = 3 + +/obj/item/slime_extract/cerulean/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + user.reagents.add_reagent(/datum/reagent/medicine/salbutamol,15) + to_chat(user, "You feel like you don't need to breathe!") + return 150 + + if(SLIME_ACTIVATE_MAJOR) + var/turf/open/T = get_turf(user) + if(istype(T)) + T.atmos_spawn_air("o2=11;n2=41;TEMP=293.15") + to_chat(user, "You activate [src], and fresh air bursts out of your skin!") + return 600 + +/obj/item/slime_extract/sepia + name = "sepia slime extract" + icon_state = "sepia_slime_extract" + effectmod = "lengthened" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) + tier = 3 + +/obj/item/slime_extract/sepia/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + var/obj/item/camera/O = new(null, 1) + if(!user.put_in_active_hand(O)) + O.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [O]!"), "You spit out [O]!" + return 150 + + if(SLIME_ACTIVATE_MAJOR) + to_chat(user, "You feel time slow down...") + if(do_after(user, 30, target = user)) + new /obj/effect/timestop(get_turf(user), 2, 50, list(user)) + return 900 + +/obj/item/slime_extract/rainbow + name = "rainbow slime extract" + icon_state = "rainbow_slime_extract" + effectmod = "hyperchromatic" + activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,"lesser plasma",/datum/reagent/toxin/slimejelly,"holy water and uranium") //Curse this snowflake reagent list. + tier = 6 + +/obj/item/slime_extract/rainbow/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) + switch(activation_type) + if(SLIME_ACTIVATE_MINOR) + user.dna.features["mcolor"] = "#[pick("7F", "FF")][pick("7F", "FF")][pick("7F", "FF")]" + user.dna.update_uf_block(DNA_MUTANT_COLOR_BLOCK) + user.updateappearance(mutcolor_update=1) + species.update_glow(user) + to_chat(user, "You feel different...") + return 100 + + if(SLIME_ACTIVATE_MAJOR) + var/chosen = pick(subtypesof(/obj/item/slime_extract)) + var/obj/item/O = new chosen(null) + if(!user.put_in_active_hand(O)) + O.forceMove(user.drop_location()) + playsound(user, 'sound/effects/splat.ogg', 50, TRUE) + user.visible_message("[user] spits out [O]!"), "You spit out [O]!" + return 150 + +////Slime-derived potions/// + +/** +* #Slime potions +* +* Feed slimes potions either by hand or using the slime console. +* +* Slime potions either augment the slime's behavior, its extract output, or its intelligence. These all come either from extract effects or cross cores. +* A few of the more powerful ones can modify someone's equipment or gender. +* New ones should probably be accessible only through cross cores as all the normal core types already have uses. Rule of thumb is 'stronger effects go in cross cores'. +*/ + +/obj/item/slimepotion + name = "slime potion" + desc = "A hard yet gelatinous capsule excreted by a slime, containing mysterious substances." + w_class = WEIGHT_CLASS_TINY + +/obj/item/slimepotion/afterattack(obj/item/reagent_containers/target, mob/user , proximity) + . = ..() + if(!proximity) + return + if (istype(target)) + to_chat(user, "You cannot transfer [src] to [target]! It appears the potion must be given directly to a slime to absorb." ) + return + +/obj/item/slimepotion/slime/docility + name = "docility potion" + desc = "A potent chemical mix that nullifies a slime's hunger, causing it to become docile and tame." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potsilver" + +/obj/item/slimepotion/slime/docility/attack(mob/living/basic/slime/M, mob/user) + if(!isslime(M)) + to_chat(user, "The potion only works on slimes!") + return ..() + if(M.stat) + to_chat(user, "The slime is dead!") + return + if(HAS_TRAIT(M, TRAIT_SLIME_RABID)) //Stops being rabid, but doesn't become truly docile. + to_chat(M, "You absorb the potion, and your rabid hunger finally settles to a normal desire to feed.") + to_chat(user, "You feed the slime the potion, calming its rabid rage.") + REMOVE_TRAIT(M, TRAIT_SLIME_RABID, null) + qdel(src) + return + M.add_trait(/datum/slime_trait/docility) + to_chat(M, "You absorb the potion and feel your intense desire to feed melt away.") + to_chat(user, "You feed the slime the potion, removing its hunger and calming it.") + var/newname = sanitize_name(tgui_input_text(user, "Would you like to give the slime a name?", "Name your new pet", "Pet Slime", MAX_NAME_LEN)) + + if (!newname) + newname = "Pet Slime" + M.name = newname + M.real_name = newname + M.update_name_tag(newname) // monkestation edit: name tags + qdel(src) + +/obj/item/slimepotion/slime/sentience + name = "intelligence potion" + desc = "A miraculous chemical mix that grants human like intelligence to living beings." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potpink" + var/list/not_interested = list() + var/being_used = FALSE + var/sentience_type = SENTIENCE_ORGANIC + +/obj/item/slimepotion/slime/sentience/attack(mob/living/dumb_mob, mob/user) + if(being_used || !isliving(dumb_mob)) + return + if(dumb_mob.ckey) //only works on animals that aren't player controlled + balloon_alert(user, "already sentient!") + return + if(dumb_mob.stat) + balloon_alert(user, "it's dead!") + return + if(!dumb_mob.compare_sentience_type(sentience_type)) // Will also return false if not a basic or simple mob, which are the only two we want anyway + balloon_alert(user, "invalid creature!") + return + + balloon_alert(user, "offering...") + being_used = TRUE + + var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob( + "Do you want to play as [dumb_mob.name]", + role = ROLE_SENTIENCE, + poll_time = 5 SECONDS, + target_mob = dumb_mob, + ignore_category = POLL_IGNORE_SENTIENCE_POTION, + pic_source = dumb_mob, + role_name_text = "sentient mob" + ) + if(!LAZYLEN(candidates)) + balloon_alert(user, "try again later!") + being_used = FALSE + return ..() + + var/mob/dead/observer/C = pick(candidates) + dumb_mob.key = C.key + dumb_mob.mind.enslave_mind_to_creator(user) + SEND_SIGNAL(dumb_mob, COMSIG_SIMPLEMOB_SENTIENCEPOTION, user) + if(isanimal(dumb_mob)) + var/mob/living/simple_animal/smart_animal = dumb_mob + smart_animal.sentience_act() + dumb_mob.mind.add_antag_datum(/datum/antagonist/sentient_creature) + balloon_alert(user, "success") + after_success(user, dumb_mob) + qdel(src) + +/obj/item/slimepotion/slime/sentience/proc/after_success(mob/living/user, mob/living/smart_mob) + return + +/obj/item/slimepotion/slime/sentience/nuclear + name = "syndicate intelligence potion" + desc = "A miraculous chemical mix that grants human like intelligence to living beings. It has been modified with Syndicate technology to also grant an internal radio implant to the target and authenticate with identification systems." + +/obj/item/slimepotion/slime/sentience/nuclear/after_success(mob/living/user, mob/living/smart_mob) + var/obj/item/implant/radio/syndicate/imp = new(src) + imp.implant(smart_mob, user) + smart_mob.AddComponent(/datum/component/simple_access, list(ACCESS_SYNDICATE, ACCESS_MAINT_TUNNELS)) + +/obj/item/slimepotion/transference + name = "consciousness transference potion" + desc = "A strange slime-based chemical that, when used, allows the user to transfer their consciousness to a lesser being." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potorange" + var/prompted = 0 + var/animal_type = SENTIENCE_ORGANIC + +/obj/item/slimepotion/transference/afterattack(mob/living/switchy_mob, mob/living/user, proximity) + if(!proximity) + return + if(prompted || !isliving(switchy_mob)) + return + if(switchy_mob.ckey) //much like sentience, these will not work on something that is already player controlled + balloon_alert(user, "already sentient!") + return ..() + if(switchy_mob.stat) + balloon_alert(user, "it's dead!") + return ..() + if(!switchy_mob.compare_sentience_type(animal_type)) + balloon_alert(user, "invalid creature!") + return ..() + + var/job_banned = is_banned_from(user.ckey, ROLE_MIND_TRANSFER) + if(QDELETED(src) || QDELETED(switchy_mob) || QDELETED(user)) + return + + if(job_banned) + balloon_alert(user, "you're banned!") + return + + prompted = 1 + if(tgui_alert(usr,"This will permanently transfer your consciousness to [switchy_mob]. Are you sure you want to do this?",,list("Yes","No")) == "No") + prompted = 0 + return + + to_chat(user, "You drink the potion then place your hands on [switchy_mob]...") + + user.mind.transfer_to(switchy_mob) + SEND_SIGNAL(switchy_mob, COMSIG_SIMPLEMOB_TRANSFERPOTION, user) + switchy_mob.faction = user.faction.Copy() + switchy_mob.copy_languages(user, LANGUAGE_MIND) + switchy_mob.update_atom_languages() + user.death() + to_chat(switchy_mob, "In a quick flash, you feel your consciousness flow into [switchy_mob]!") + to_chat(switchy_mob, "You are now [switchy_mob]. Your allegiances, alliances, and role is still the same as it was prior to consciousness transfer!") + switchy_mob.name = "[user.real_name]" + qdel(src) + if(isanimal(switchy_mob)) + var/mob/living/simple_animal/switchy_animal= switchy_mob + switchy_animal.sentience_act() + +/obj/item/slimepotion/slime/steroid + name = "slime steroid" + desc = "A potent chemical mix that will cause slimes to make more ooze." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potred" + +/obj/item/slimepotion/slime/steroid/attack(mob/living/basic/slime/M, mob/user) + if(!isslime(M))//If target is not a slime. + to_chat(user, "The steroid only works on slimes!") // monkestation edit: not baby slimes only, no + return ..() + if(M.stat) + to_chat(user, "The slime is dead!") + return + // monkestation start: xenobio rework + if(M.ooze_production >= 50) + to_chat(user, "The slime is already producing too much ooze!") + return + to_chat(user, "You feed the slime the steroid. It will now produce more ooze.") + M.ooze_production = min(M.ooze_production + 20, 50) + // monkestation end + qdel(src) + +/obj/item/slimepotion/enhancer + name = "extract enhancer" + desc = "A potent chemical mix that will give a slime extract an additional use." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potpurple" + +/obj/item/slimepotion/slime/stabilizer + name = "slime stabilizer" + desc = "A potent chemical mix that will reduce the chance of a slime mutating." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potcyan" + +/obj/item/slimepotion/slime/stabilizer/attack(mob/living/basic/slime/M, mob/user) + if(!isslime(M)) + to_chat(user, "The stabilizer only works on slimes!") + return ..() + if(M.stat) + to_chat(user, "The slime is dead!") + return + if(M.mutation_chance == 0) + to_chat(user, "The slime already has no chance of mutating!") + return + + to_chat(user, "You feed the slime the stabilizer. It is now less likely to mutate.") + M.mutation_chance = clamp(M.mutation_chance-15,0,100) + qdel(src) + +/obj/item/slimepotion/slime/mutator + name = "slime mutator" + desc = "A potent chemical mix that will increase the chance of a slime mutating." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potgreen" + +/obj/item/slimepotion/slime/mutator/attack(mob/living/basic/slime/M, mob/user) + if(!isslime(M)) + to_chat(user, "The mutator only works on slimes!") + return ..() + if(M.stat) + to_chat(user, "The slime is dead!") + return + if(HAS_TRAIT(M, TRAIT_MUTATOR_USED)) + to_chat(user, "This slime has already consumed a mutator, any more would be far too unstable!") + return + if(M.mutation_chance == 100) + to_chat(user, "The slime is already guaranteed to mutate!") + return + + to_chat(user, "You feed the slime the mutator. It is now more likely to mutate.") + M.mutation_chance = clamp(M.mutation_chance+12,0,100) + ADD_TRAIT(M, TRAIT_MUTATOR_USED, "slime-mutator") + qdel(src) + +/obj/item/slimepotion/speed + name = "slime speed potion" + desc = "A potent chemical mix that will remove the slowdown from any item." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potyellow" + +/obj/item/slimepotion/speed/afterattack(obj/C, mob/user, proximity) + . = ..() + if(!proximity) + return + if(!istype(C)) + to_chat(user, "The potion can only be used on objects!") + return + . |= AFTERATTACK_PROCESSED_ITEM + if(SEND_SIGNAL(C, COMSIG_SPEED_POTION_APPLIED, src, user) & SPEED_POTION_STOP) + return + if(isitem(C)) + var/obj/item/I = C + if(I.slowdown <= 0 || (I.item_flags & IMMUTABLE_SLOW)) + to_chat(user, "The [C] can't be made any faster!") + return ..() + I.slowdown = 0 + + to_chat(user, "You slather the red gunk over the [C], making it faster.") + C.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + C.add_atom_colour("#FF0000", FIXED_COLOUR_PRIORITY) + qdel(src) + +/obj/item/slimepotion/speed/attackby_storage_insert(datum/storage, atom/storage_holder, mob/user) + if(!isitem(storage_holder)) + return TRUE + if(istype(storage_holder, /obj/item/mod/control)) + var/obj/item/mod/control/mod = storage_holder + return mod.slowdown_inactive <= 0 + var/obj/item/storage_item = storage_holder + return storage_item.slowdown <= 0 + +/obj/item/slimepotion/fireproof + name = "slime chill potion" + desc = "A potent chemical mix that will fireproof any article of clothing. Has three uses." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potblue" + resistance_flags = FIRE_PROOF + var/uses = 3 + +/obj/item/slimepotion/fireproof/afterattack(obj/item/clothing/clothing, mob/user, proximity) + . = ..() + if(!proximity) + return + if(!uses) + qdel(src) + return + . |= AFTERATTACK_PROCESSED_ITEM + if(!istype(clothing)) + to_chat(user, "The potion can only be used on clothing!") + return + if(clothing.max_heat_protection_temperature >= FIRE_IMMUNITY_MAX_TEMP_PROTECT) + to_chat(user, "The [clothing] is already fireproof!") + return + to_chat(user, "You slather the blue gunk over the [clothing], fireproofing it.") + clothing.name = "fireproofed [clothing.name]" + clothing.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + clothing.add_atom_colour("#000080", FIXED_COLOUR_PRIORITY) + clothing.max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + clothing.heat_protection = clothing.body_parts_covered + clothing.resistance_flags |= FIRE_PROOF + uses -- + if(!uses) + qdel(src) + +/obj/item/slimepotion/genderchange + name = "gender change potion" + desc = "An interesting chemical mix that changes the biological gender of what its applied to. Cannot be used on things that lack gender entirely." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potlightpink" + +/obj/item/slimepotion/genderchange/attack(mob/living/L, mob/user) + if(!istype(L) || L.stat == DEAD) + to_chat(user, "The potion can only be used on living things!") + return + + if(L.gender != MALE && L.gender != FEMALE) + to_chat(user, "The potion can only be used on gendered things!") + return + + if(L.gender == MALE) + L.gender = FEMALE + L.visible_message(span_boldnotice("[L] suddenly looks more feminine!"), span_boldwarning("You suddenly feel more feminine!")) + else + L.gender = MALE + L.visible_message(span_boldnotice("[L] suddenly looks more masculine!"), span_boldwarning("You suddenly feel more masculine!")) + L.regenerate_icons() + qdel(src) + +/obj/item/slimepotion/slime/renaming + name = "renaming potion" + desc = "A potion that allows a self-aware being to change what name it subconciously presents to the world." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potgreen" + + var/being_used = FALSE + +/obj/item/slimepotion/slime/renaming/attack(mob/living/M, mob/user) + if(being_used || !ismob(M)) + return + if(!M.ckey) //only works on animals that aren't player controlled + to_chat(user, "[M] is not self aware, and cannot pick its own name.") + return + + being_used = TRUE + + to_chat(user, "You offer [src] to [user]...") + + var/new_name = sanitize_name(tgui_input_text(M, "What would you like your name to be?", "Input a name", M.real_name, MAX_NAME_LEN)) + + if(!new_name || QDELETED(src) || QDELETED(M) || new_name == M.real_name || !M.Adjacent(user)) + being_used = FALSE + return + + M.visible_message("[span_name("[M]")] has a new name, [span_name("[new_name]")]."), span_notice("Your old name of [span_name("[M.real_name]")] fades away, and your new name [span_name("[new_name]")] anchors itself in your mind.") + message_admins("[ADMIN_LOOKUPFLW(user)] used [src] on [ADMIN_LOOKUPFLW(M)], letting them rename themselves into [new_name].") + user.log_message("used [src] on [key_name(M)], letting them rename themselves into [new_name].", LOG_GAME) + + // pass null as first arg to not update records or ID/PDA + M.fully_replace_character_name(null, new_name) + + qdel(src) + +/obj/item/slimepotion/slime/slimeradio + name = "bluespace radio potion" + desc = "A strange chemical that grants those who ingest it the ability to broadcast and receive subscape radio waves." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "potgrey" + +/obj/item/slimepotion/slime/slimeradio/attack(mob/living/radio_head, mob/user) + if(!isanimal_or_basicmob(radio_head)) + to_chat(user, "[radio_head] is too complex for the potion!") + return + if(radio_head.stat) + to_chat(user, "[radio_head] is dead!") + return + + to_chat(user, "You feed the potion to [radio_head].") + to_chat(radio_head, "Your mind tingles as you are fed the potion. You can hear radio waves now!") + var/obj/item/implant/radio/slime/imp = new(src) + imp.implant(radio_head, user) + qdel(src) + +///Definitions for slime products that don't have anywhere else to go (Floor tiles, blueprints). + +/obj/item/stack/tile/bluespace + name = "bluespace floor tile" + singular_name = "floor tile" + desc = "Through a series of micro-teleports these tiles let people move at incredible speeds." + icon_state = "tile_bluespace" + inhand_icon_state = "tile-bluespace" + w_class = WEIGHT_CLASS_NORMAL + force = 6 + mats_per_unit = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*5) + throwforce = 10 + throw_speed = 3 + throw_range = 7 + flags_1 = CONDUCT_1 + max_amount = 60 + turf_type = /turf/open/floor/bluespace + merge_type = /obj/item/stack/tile/bluespace + +/obj/item/stack/tile/sepia + name = "sepia floor tile" + singular_name = "floor tile" + desc = "Time seems to flow very slowly around these tiles." + icon_state = "tile_sepia" + inhand_icon_state = "tile-sepia" + w_class = WEIGHT_CLASS_NORMAL + force = 6 + mats_per_unit = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*5) + throwforce = 10 + throw_speed = 0.1 + throw_range = 28 + flags_1 = CONDUCT_1 + max_amount = 60 + turf_type = /turf/open/floor/sepia + merge_type = /obj/item/stack/tile/sepia + +/obj/item/areaeditor/blueprints/slime + name = "cerulean prints" + desc = "A one use yet of blueprints made of jelly like organic material. Extends the reach of the management console." + color = "#2956B2" + +/obj/item/areaeditor/blueprints/slime/edit_area() + ..() + var/area/A = get_area(src) + for(var/turf/T in A) + T.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + T.add_atom_colour("#2956B2", FIXED_COLOUR_PRIORITY) + A.area_flags |= XENOBIOLOGY_COMPATIBLE + qdel(src) + /datum/asset/spritesheet/xenobio_market name = "xenobio_market" diff --git a/code/modules/slimecore/corral/corral_data.dm b/code/modules/slimecore/corral/corral_data.dm index 0df5a6585b538..78fb40042f829 100644 --- a/code/modules/slimecore/corral/corral_data.dm +++ b/code/modules/slimecore/corral/corral_data.dm @@ -32,10 +32,10 @@ managed_slimes |= slime RegisterSignal(slime, COMSIG_ATOM_SUCKED, PROC_REF(remove_cause_sucked)) RegisterSignal(slime, COMSIG_LIVING_DEATH, PROC_REF(remove_cause_sucked)) - RegisterSignals(slime, list(COMSIG_PREQDELETED, COMSIG_QDELETING), PROC_REF(try_remove)) + RegisterSignals(slime, list(COMSIG_PARENT_PREQDELETED, COMSIG_PARENT_QDELETING), PROC_REF(try_remove)) for(var/obj/machinery/corral_corner/corner as anything in corral_corners) - RegisterSignal(corner, COMSIG_QDELETING, PROC_REF(start_break)) + RegisterSignal(corner, COMSIG_PARENT_QDELETING, PROC_REF(start_break)) /datum/corral_data/Destroy(force, ...) QDEL_LIST(corral_connectors) @@ -46,7 +46,7 @@ for(var/obj/machinery/corral_corner/corner as anything in corral_corners) corner.connected_data = null - UnregisterSignal(corner, COMSIG_QDELETING) + UnregisterSignal(corner, COMSIG_PARENT_QDELETING) corral_corners -= corner corral_corners = null @@ -77,7 +77,7 @@ RegisterSignal(arrived, COMSIG_ATOM_SUCKED, PROC_REF(remove_cause_sucked)) RegisterSignal(arrived, COMSIG_LIVING_DEATH, PROC_REF(remove_cause_sucked)) - RegisterSignals(arrived, list(COMSIG_PREQDELETED, COMSIG_QDELETING), PROC_REF(try_remove)) + RegisterSignals(arrived, list(COMSIG_PARENT_PREQDELETED, COMSIG_PARENT_QDELETING), PROC_REF(try_remove)) managed_slimes |= arrived for(var/datum/corral_upgrade/upgrade as anything in corral_upgrades) upgrade.on_slime_entered(arrived, src) @@ -93,7 +93,7 @@ UnregisterSignal(gone, COMSIG_ATOM_SUCKED) UnregisterSignal(gone, COMSIG_LIVING_DEATH) - UnregisterSignal(gone, list(COMSIG_PREQDELETED, COMSIG_QDELETING)) + UnregisterSignal(gone, list(COMSIG_PARENT_PREQDELETED, COMSIG_PARENT_QDELETING)) managed_slimes -= gone for(var/datum/corral_upgrade/upgrade as anything in corral_upgrades) upgrade.on_slime_exited(gone) @@ -103,7 +103,7 @@ UnregisterSignal(gone, COMSIG_ATOM_SUCKED) UnregisterSignal(gone, COMSIG_LIVING_DEATH) - UnregisterSignal(gone, list(COMSIG_PREQDELETED, COMSIG_QDELETING)) + UnregisterSignal(gone, list(COMSIG_PARENT_PREQDELETED, COMSIG_PARENT_QDELETING)) managed_slimes -= gone for(var/datum/corral_upgrade/upgrade as anything in corral_upgrades) upgrade.on_slime_exited(gone) @@ -121,7 +121,7 @@ continue UnregisterSignal(slime, COMSIG_ATOM_SUCKED) UnregisterSignal(slime, COMSIG_LIVING_DEATH) - UnregisterSignal(slime, list(COMSIG_PREQDELETED, COMSIG_QDELETING)) + UnregisterSignal(slime, list(COMSIG_PARENT_PREQDELETED, COMSIG_PARENT_QDELETING)) for(var/datum/corral_upgrade/upgrade as anything in corral_upgrades) upgrade.on_slime_exited(slime) diff --git a/code/modules/slimecore/mobs/_base_slime.dm b/code/modules/slimecore/mobs/_base_slime.dm index db24777e6b5ae..fafcd99a5d674 100644 --- a/code/modules/slimecore/mobs/_base_slime.dm +++ b/code/modules/slimecore/mobs/_base_slime.dm @@ -15,7 +15,7 @@ pass_flags = PASSTABLE | PASSGRILLE gender = NEUTER - faction = list(FACTION_SLIME) + faction = list("slime") melee_damage_lower = 5 melee_damage_upper = 15 @@ -165,7 +165,7 @@ /mob/living/basic/slime/mob_try_pickup(mob/living/user, instant) if(!SEND_SIGNAL(src, COMSIG_FRIENDSHIP_CHECK_LEVEL, user, FRIENDSHIP_FRIEND)) - to_chat(user, span_notice("[src] doesn't trust you enough to let you pick them up")) + to_chat(user, "[src] doesn't trust you enough to let you pick them up") balloon_alert(user, "not enough trust!") return FALSE . = ..() @@ -174,15 +174,15 @@ . = ..() if(SEND_SIGNAL(src, COMSIG_FRIENDSHIP_CHECK_LEVEL, user, FRIENDSHIP_FRIEND)) if(SEND_SIGNAL(src, COMSIG_FRIENDSHIP_CHECK_LEVEL, user, FRIENDSHIP_BESTFRIEND)) - . += span_notice("You are one of [src]'s best friends!") + . += "You are one of [src]'s best friends!" else - . += span_notice("You are one of [src]'s friends.") + . += "You are one of [src]'s friends." if(check_secretion()) switch(ooze_production) if(-INFINITY to 10) - . += span_notice("It's secreting some ooze.") + . += "It's secreting some ooze." if(10 to 40) - . += span_notice("It's secreting a lot of ooze.") + . += "It's secreting a lot of ooze." if(40 to INFINITY) . += span_boldnotice("It's overflowing with ooze!") @@ -352,7 +352,7 @@ ai_controller.set_ai_status(AI_STATUS_OFF) slime_flags |= SPLITTING_SLIME - visible_message(span_notice("[name] starts to flatten, it looks to be splitting.")) + visible_message("[name] starts to flatten, it looks to be splitting.") balloon_alert_to_viewers("splitting...") addtimer(CALLBACK(src, PROC_REF(finish_splitting)), 15 SECONDS) @@ -377,7 +377,7 @@ return FALSE ai_controller.set_ai_status(AI_STATUS_OFF) - visible_message(span_notice("[name] starts to undulate, it looks to be mutating.")) + visible_message("[name] starts to undulate, it looks to be mutating.") balloon_alert_to_viewers("mutating...") slime_flags |= MUTATING_SLIME diff --git a/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm b/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm index 98efcd5f8d308..4e6c38cd4abf5 100644 --- a/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm +++ b/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm @@ -18,7 +18,7 @@ return living_pawn.balloon_alert_to_viewers("cleaned") - living_pawn.visible_message(span_notice("[living_pawn] dissolves \the [target].")) + living_pawn.visible_message("[living_pawn] dissolves \the [target].") SEND_SIGNAL(living_pawn, COMSIG_MOB_FEED, target, 20) qdel(target) // Sent to the shadow realm to never be seen again finish_action(controller, TRUE, target_key)