From 91e628c73d549f83b1d69d1e48da938e57d676b5 Mon Sep 17 00:00:00 2001 From: Ghommie <42542238+Ghommie@users.noreply.github.com> Date: Fri, 6 Dec 2019 19:01:42 +0100 Subject: [PATCH] Ports timestop and sepia extracts improvements/tweaks. --- code/__DEFINES/components.dm | 1 + code/__DEFINES/traits.dm | 1 + code/datums/components/dejavu.dm | 107 +++++++++++++ code/game/atoms_movable.dm | 4 +- code/game/mecha/mecha.dm | 5 + .../antagonists/wizard/equipment/spellbook.dm | 2 +- code/modules/fields/timestop.dm | 146 +++++++++++------- code/modules/mob/living/living.dm | 20 ++- .../mob/living/simple_animal/simple_animal.dm | 7 + .../mob/living/simple_animal/slime/life.dm | 2 +- code/modules/photography/camera/camera.dm | 12 +- .../chemistry/recipes/slime_extracts.dm | 11 +- .../xenobiology/crossbreeding/burning.dm | 85 ++++++++-- .../xenobiology/crossbreeding/regenerative.dm | 5 +- code/modules/spells/spell_types/wizard.dm | 10 +- tgstation.dme | 1 + 16 files changed, 334 insertions(+), 85 deletions(-) create mode 100644 code/datums/components/dejavu.dm diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index a8c623a889..ff2d44257f 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -117,6 +117,7 @@ #define COMSIG_TURF_MULTIZ_NEW "turf_multiz_new" //from base of turf/New(): (turf/source, direction) // /atom/movable signals +#define COMSIG_MOVABLE_PRE_MOVE "movable_pre_move" //from base of atom/movable/Moved(): (/atom) #define COMSIG_MOVABLE_MOVED "movable_moved" //from base of atom/movable/Moved(): (/atom, dir) #define COMSIG_MOVABLE_CROSS "movable_cross" //from base of atom/movable/Cross(): (/atom/movable) #define COMSIG_MOVABLE_CROSSED "movable_crossed" //from base of atom/movable/Crossed(): (/atom/movable) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 14dbd97ffa..e677198348 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -219,3 +219,4 @@ #define ANTI_DROP_IMPLANT_TRAIT "anti-drop-implant" #define ABDUCTOR_ANTAGONIST "abductor-antagonist" #define MADE_UNCLONEABLE "made-uncloneable" +#define TIMESTOP_TRAIT "timestop" diff --git a/code/datums/components/dejavu.dm b/code/datums/components/dejavu.dm new file mode 100644 index 0000000000..1bad589c97 --- /dev/null +++ b/code/datums/components/dejavu.dm @@ -0,0 +1,107 @@ +/** + * A component to reset the parent to its previous state after some time passes + */ +/datum/component/dejavu + /// The turf the parent was on when this components was applied, they get moved back here after the duration + var/turf/starting_turf + /// Determined by the type of the parent so different behaviours can happen per type + var/rewind_type + /// How many rewinds will happen before the effect ends + var/rewinds_remaining + /// How long to wait between each rewind + var/rewind_interval + + /// The starting value of clone loss at the beginning of the effect + var/clone_loss = 0 + /// The starting value of toxin loss at the beginning of the effect + var/tox_loss = 0 + /// The starting value of oxygen loss at the beginning of the effect + var/oxy_loss = 0 + /// The starting value of brain loss at the beginning of the effect + var/brain_loss = 0 + /// The starting value of brute loss at the beginning of the effect + /// This only applies to simple animals + var/brute_loss + /// The starting value of integrity at the beginning of the effect + /// This only applies to objects + var/integrity + /// A list of body parts saved at the beginning of the effect + var/list/datum/saved_bodypart/saved_bodyparts + +/datum/component/dejavu/Initialize(rewinds = 1, interval = 10 SECONDS) + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + + starting_turf = get_turf(parent) + rewinds_remaining = rewinds + rewind_interval = interval + + if(isliving(parent)) + var/mob/living/L = parent + clone_loss = L.getCloneLoss() + tox_loss = L.getToxLoss() + oxy_loss = L.getOxyLoss() + brain_loss = L.getOrganLoss(ORGAN_SLOT_BRAIN) + rewind_type = .proc/rewind_living + + if(iscarbon(parent)) + var/mob/living/carbon/C = parent + saved_bodyparts = C.save_bodyparts() + rewind_type = .proc/rewind_carbon + + else if(isanimal(parent)) + var/mob/living/simple_animal/M = parent + brute_loss = M.bruteloss + rewind_type = .proc/rewind_animal + + else if(isobj(parent)) + var/obj/O = parent + integrity = O.obj_integrity + rewind_type = .proc/rewind_obj + + addtimer(CALLBACK(src, rewind_type), rewind_interval) + +/datum/component/dejavu/Destroy() + starting_turf = null + saved_bodyparts = null + return ..() + +/datum/component/dejavu/proc/rewind() + to_chat(parent, "You remember a time not so long ago...") + + //comes after healing so new limbs comically drop to the floor + if(starting_turf) + var/atom/movable/master = parent + master.forceMove(starting_turf) + + rewinds_remaining -- + if(rewinds_remaining) + addtimer(CALLBACK(src, rewind_type), rewind_interval) + else + to_chat(parent, "But the memory falls out of your reach.") + qdel(src) + +/datum/component/dejavu/proc/rewind_living() + var/mob/living/master = parent + master.setCloneLoss(clone_loss) + master.setToxLoss(tox_loss) + master.setOxyLoss(oxy_loss) + master.setOrganLoss(ORGAN_SLOT_BRAIN, brain_loss) + rewind() + +/datum/component/dejavu/proc/rewind_carbon() + if(saved_bodyparts) + var/mob/living/carbon/master = parent + master.apply_saved_bodyparts(saved_bodyparts) + rewind_living() + +/datum/component/dejavu/proc/rewind_animal() + var/mob/living/simple_animal/master = parent + master.bruteloss = brute_loss + master.updatehealth() + rewind_living() + +/datum/component/dejavu/proc/rewind_obj() + var/obj/master = parent + master.obj_integrity = integrity + rewind() diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index ef20b6e35e..ec997b62e7 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -159,7 +159,7 @@ /atom/movable/proc/Move_Pulled(atom/A) if(!pulling) return - if(pulling.anchored || !pulling.Adjacent(src)) + if(pulling.anchored || pulling.move_resist > move_force || !pulling.Adjacent(src)) stop_pulling() return if(isliving(pulling)) @@ -186,7 +186,7 @@ log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.") stop_pulling() return - if(pulling.anchored) + if(pulling.anchored || pulling.move_resist > move_force) stop_pulling() return diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index fe0ec33ed6..4dad4bfc09 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -49,6 +49,7 @@ var/lights = FALSE var/lights_power = 6 var/last_user_hud = 1 // used to show/hide the mecha hud while preserving previous preference + var/completely_disabled = FALSE //stops the mech from doing anything var/breach_time = 0 var/recharge_rate = 0 @@ -431,6 +432,8 @@ return if(!locate(/turf) in list(target,target.loc)) // Prevents inventory from being drilled return + if(completely_disabled) + return if(phasing) occupant_message("Unable to interact with objects while phasing") return @@ -508,6 +511,8 @@ return 1 /obj/mecha/relaymove(mob/user,direction) + if(completely_disabled) + return if(!direction) return if(user != occupant) //While not "realistic", this piece is player friendly. diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm index c5308cf0c0..d7d4c0d3e1 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook.dm @@ -159,7 +159,7 @@ /datum/spellbook_entry/timestop name = "Time Stop" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/timestop + spell_type = /obj/effect/proc_holder/spell/aoe_turf/timestop category = "Defensive" /datum/spellbook_entry/smoke diff --git a/code/modules/fields/timestop.dm b/code/modules/fields/timestop.dm index a063296edf..f0d86ac58a 100644 --- a/code/modules/fields/timestop.dm +++ b/code/modules/fields/timestop.dm @@ -9,7 +9,7 @@ pixel_x = -64 pixel_y = -64 mouse_opacity = MOUSE_OPACITY_TRANSPARENT - var/list/immune = list() // the one who creates the timestop is immune + var/list/immune = list() // the one who creates the timestop is immune, which includes wizards and the dead slime you murdered to make this chronofield var/turf/target var/freezerange = 2 var/duration = 140 @@ -27,10 +27,10 @@ for(var/A in immune_atoms) immune[A] = TRUE for(var/mob/living/L in GLOB.player_list) - if(locate(/obj/effect/proc_holder/spell/aoe_turf/conjure/timestop) in L.mind.spell_list) //People who can stop time are immune to its effects + if(locate(/obj/effect/proc_holder/spell/aoe_turf/timestop) in L.mind.spell_list) //People who can stop time are immune to its effects immune[L] = TRUE for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.parasites) - if(G.summoner && locate(/obj/effect/proc_holder/spell/aoe_turf/conjure/timestop) in G.summoner.mind.spell_list) //It would only make sense that a person's stand would also be immune. + if(G.summoner && locate(/obj/effect/proc_holder/spell/aoe_turf/timestop) in G.summoner.mind.spell_list) //It would only make sense that a person's stand would also be immune. immune[G] = TRUE if(start) timestop() @@ -46,9 +46,8 @@ chronofield = make_field(/datum/proximity_monitor/advanced/timestop, list("current_range" = freezerange, "host" = src, "immune" = immune, "check_anti_magic" = check_anti_magic, "check_holy" = check_holy)) QDEL_IN(src, duration) -/obj/effect/timestop/wizard +/obj/effect/timestop/magic check_anti_magic = TRUE - duration = 100 /datum/proximity_monitor/advanced/timestop name = "chronofield" @@ -56,9 +55,10 @@ field_shape = FIELD_SHAPE_RADIUS_SQUARE requires_processing = TRUE var/list/immune = list() - var/list/mob/living/frozen_mobs = list() - var/list/obj/item/projectile/frozen_projectiles = list() - var/list/atom/movable/frozen_throws = list() + var/list/frozen_things = list() + var/list/frozen_mobs = list() //cached separately for processing + var/list/frozen_structures = list() //Also machinery, and only frozen aestethically + var/list/frozen_turfs = list() //Only aesthetically var/check_anti_magic = FALSE var/check_holy = FALSE @@ -74,85 +74,125 @@ /datum/proximity_monitor/advanced/timestop/proc/freeze_atom(atom/movable/A) if(immune[A] || global_frozen_atoms[A] || !istype(A)) return FALSE - if(A.throwing) - freeze_throwing(A) + if(ismob(A)) + var/mob/M = A + if(M.anti_magic_check(check_anti_magic, check_holy)) + immune[A] = TRUE + return + var/frozen = TRUE if(isliving(A)) freeze_mob(A) else if(istype(A, /obj/item/projectile)) freeze_projectile(A) + else if(istype(A, /obj/mecha)) + freeze_mecha(A) + else if((ismachinery(A) && !istype(A, /obj/machinery/light)) || isstructure(A)) //Special exception for light fixtures since recoloring causes them to change light + freeze_structure(A) else - return FALSE + frozen = FALSE + if(A.throwing) + freeze_throwing(A) + frozen = TRUE + if(!frozen) + return + frozen_things[A] = A.move_resist + A.move_resist = INFINITY + global_frozen_atoms[A] = src into_the_negative_zone(A) + RegisterSignal(A, COMSIG_MOVABLE_PRE_MOVE, .proc/unfreeze_atom) + RegisterSignal(A, COMSIG_ITEM_PICKUP, .proc/unfreeze_atom) return TRUE /datum/proximity_monitor/advanced/timestop/proc/unfreeze_all() - for(var/i in frozen_projectiles) - unfreeze_projectile(i) - for(var/i in frozen_mobs) - unfreeze_mob(i) - for(var/i in frozen_throws) - unfreeze_throw(i) + for(var/i in frozen_things) + unfreeze_atom(i) + for(var/T in frozen_turfs) + unfreeze_turf(T) + +/datum/proximity_monitor/advanced/timestop/proc/unfreeze_atom(atom/movable/A) + if(A.throwing) + unfreeze_throwing(A) + if(isliving(A)) + unfreeze_mob(A) + else if(istype(A, /obj/item/projectile)) + unfreeze_projectile(A) + else if(istype(A, /obj/mecha)) + unfreeze_mecha(A) + + UnregisterSignal(A, COMSIG_MOVABLE_PRE_MOVE) + UnregisterSignal(A, COMSIG_ITEM_PICKUP) + escape_the_negative_zone(A) + A.move_resist = frozen_things[A] + frozen_things -= A + global_frozen_atoms -= A + +/datum/proximity_monitor/advanced/timestop/proc/freeze_mecha(obj/mecha/M) + M.completely_disabled = TRUE + +/datum/proximity_monitor/advanced/timestop/proc/unfreeze_mecha(obj/mecha/M) + M.completely_disabled = FALSE /datum/proximity_monitor/advanced/timestop/proc/freeze_throwing(atom/movable/AM) var/datum/thrownthing/T = AM.throwing T.paused = TRUE - frozen_throws[AM] = T - global_frozen_atoms[AM] = TRUE -/datum/proximity_monitor/advanced/timestop/proc/unfreeze_throw(atom/movable/AM) - var/datum/thrownthing/T = frozen_throws[AM] - T.paused = FALSE - frozen_throws -= AM - global_frozen_atoms -= AM +/datum/proximity_monitor/advanced/timestop/proc/unfreeze_throwing(atom/movable/AM) + var/datum/thrownthing/T = AM.throwing + if(T) + T.paused = FALSE + +/datum/proximity_monitor/advanced/timestop/proc/freeze_turf(turf/T) + into_the_negative_zone(T) + frozen_turfs += T + +/datum/proximity_monitor/advanced/timestop/proc/unfreeze_turf(turf/T) + escape_the_negative_zone(T) + +/datum/proximity_monitor/advanced/timestop/proc/freeze_structure(obj/O) + into_the_negative_zone(O) + frozen_structures += O + +/datum/proximity_monitor/advanced/timestop/proc/unfreeze_structure(obj/O) + escape_the_negative_zone(O) /datum/proximity_monitor/advanced/timestop/process() for(var/i in frozen_mobs) var/mob/living/m = i - if(get_dist(get_turf(m), get_turf(host)) > current_range) - unfreeze_mob(m) - else - m.Stun(20, 1, 1) + m.Stun(20, 1, 1) /datum/proximity_monitor/advanced/timestop/setup_field_turf(turf/T) for(var/i in T.contents) freeze_atom(i) + freeze_turf(T) return ..() -/datum/proximity_monitor/advanced/timestop/proc/unfreeze_projectile(obj/item/projectile/P) - escape_the_negative_zone(P) - frozen_projectiles -= P - P.paused = FALSE - global_frozen_atoms -= P - /datum/proximity_monitor/advanced/timestop/proc/freeze_projectile(obj/item/projectile/P) - frozen_projectiles[P] = TRUE P.paused = TRUE - global_frozen_atoms[P] = TRUE + +/datum/proximity_monitor/advanced/timestop/proc/unfreeze_projectile(obj/item/projectile/P) + P.paused = FALSE /datum/proximity_monitor/advanced/timestop/proc/freeze_mob(mob/living/L) - if(L.anti_magic_check(check_anti_magic, check_holy)) - immune += L - return + frozen_mobs += L L.Stun(20, 1, 1) - frozen_mobs[L] = L.anchored - L.anchored = TRUE - global_frozen_atoms[L] = TRUE - if(ishostile(L)) - var/mob/living/simple_animal/hostile/H = L - H.toggle_ai(AI_OFF) - H.LoseTarget() + ADD_TRAIT(L, TRAIT_MUTE, TIMESTOP_TRAIT) + walk(L, 0) //stops them mid pathing even if they're stunimmune + if(isanimal(L)) + var/mob/living/simple_animal/S = L + S.toggle_ai(AI_OFF) + if(ishostile(L)) + var/mob/living/simple_animal/hostile/H = L + H.LoseTarget() /datum/proximity_monitor/advanced/timestop/proc/unfreeze_mob(mob/living/L) - escape_the_negative_zone(L) L.AdjustStun(-20, 1, 1) - L.anchored = frozen_mobs[L] + REMOVE_TRAIT(L, TRAIT_MUTE, TIMESTOP_TRAIT) frozen_mobs -= L - global_frozen_atoms -= L - if(ishostile(L)) - var/mob/living/simple_animal/hostile/H = L - H.toggle_ai(initial(H.AIStatus)) + if(isanimal(L)) + var/mob/living/simple_animal/S = L + S.toggle_ai(initial(S.AIStatus)) //you don't look quite right, is something the matter? /datum/proximity_monitor/advanced/timestop/proc/into_the_negative_zone(atom/A) @@ -160,4 +200,4 @@ //let's put some colour back into your cheeks /datum/proximity_monitor/advanced/timestop/proc/escape_the_negative_zone(atom/A) - A.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY) \ No newline at end of file + A.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 6ced2a05a1..f02778bd91 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -84,8 +84,10 @@ if(now_pushing) return TRUE + var/they_can_move = TRUE if(isliving(M)) var/mob/living/L = M + they_can_move = L.canmove //L.mobility_flags & MOBILITY_MOVE //Also spread diseases for(var/thing in diseases) var/datum/disease/D = thing @@ -138,13 +140,17 @@ return 1 if(!M.buckled && !M.has_buckled_mobs()) - var/mob_swap - //the puller can always swap with its victim if on grab intent - if(M.pulledby == src && a_intent == INTENT_GRAB) - mob_swap = 1 - //restrained people act if they were on 'help' intent to prevent a person being pulled from being separated from their puller - else if((M.restrained() || M.a_intent == INTENT_HELP) && (restrained() || a_intent == INTENT_HELP)) - mob_swap = 1 + var/mob_swap = FALSE + var/too_strong = (M.move_resist > move_force) //can't swap with immovable objects unless they help us + if(!they_can_move) //we have to physically move them + if(!too_strong) + mob_swap = TRUE + else + if(M.pulledby == src && a_intent == INTENT_GRAB) + mob_swap = TRUE + //restrained people act if they were on 'help' intent to prevent a person being pulled from being separated from their puller + else if((M.restrained() || M.a_intent == INTENT_HELP) && (restrained() || a_intent == INTENT_HELP)) + mob_swap = TRUE if(mob_swap) //switch our position with M if(loc && !loc.Adjacent(M.loc)) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 52e58aea4e..855cd0085c 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -83,6 +83,7 @@ var/datum/personal_crafting/handcrafting var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever), AI_Z_OFF (Temporarily off due to nonpresence of players) + var/can_have_ai = TRUE //once we have become sentient, we can never go back var/shouldwakeup = FALSE //convenience var for forcibly waking up an idling AI on next check. @@ -424,6 +425,9 @@ canmove = FALSE else canmove = value_otherwise + if(!canmove) // !(mobility_flags & MOBILITY_MOVE) + walk(src, 0) //stop mid walk + update_transform() update_action_buttons_icon() return canmove @@ -442,6 +446,7 @@ /mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion toggle_ai(AI_OFF) // To prevent any weirdness. + can_have_ai = FALSE /mob/living/simple_animal/update_sight() if(!client) @@ -556,6 +561,8 @@ LoadComponent(/datum/component/riding) /mob/living/simple_animal/proc/toggle_ai(togglestatus) + if(!can_have_ai && (togglestatus != AI_OFF)) + return if (AIStatus != togglestatus) if (togglestatus > 0 && togglestatus < 5) if (togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF) diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm index 29b4689317..fd6cecf926 100644 --- a/code/modules/mob/living/simple_animal/slime/life.dm +++ b/code/modules/mob/living/simple_animal/slime/life.dm @@ -41,7 +41,7 @@ AIproc = 1 while(AIproc && stat != DEAD && (attacked || hungry || rabid || buckled)) - if(buckled) // can't eat AND have this little process at the same time + if(!canmove) // !(mobility_flags & MOBILITY_MOVE) //also covers buckling. Not sure why buckled is in the while condition if we're going to immediately break, honestly break if(!Target || client) diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm index 9d01eca0cf..f63a5f8ccc 100644 --- a/code/modules/photography/camera/camera.dm +++ b/code/modules/photography/camera/camera.dm @@ -33,6 +33,8 @@ var/picture_size_y_min = 1 var/picture_size_x_max = 4 var/picture_size_y_max = 4 + var/can_customise = TRUE + var/default_picture_name /obj/item/camera/attack_self(mob/user) if(!disk) @@ -198,8 +200,10 @@ user.put_in_hands(p) pictures_left-- to_chat(user, "[pictures_left] photos left.") - var/customize = alert(user, "Do you want to customize the photo?", "Customization", "Yes", "No") - if(customize == "Yes") + var/customise = "No" + if(can_customise) + customise = alert(user, "Do you want to customize the photo?", "Customization", "Yes", "No") + if(customise == "Yes") var/name1 = stripped_input(user, "Set a name for this photo, or leave blank. 32 characters max.", "Name", max_length = 32) var/desc1 = stripped_input(user, "Set a description to add to photo, or leave blank. 128 characters max.", "Caption", max_length = 128) var/caption = stripped_input(user, "Set a caption for this photo, or leave blank. 256 characters max.", "Caption", max_length = 256) @@ -209,6 +213,10 @@ picture.picture_desc = "[desc1] - [picture.picture_desc]" if(caption) picture.caption = caption + else + if(default_picture_name) + picture.picture_name = default_picture_name + p.set_picture(picture, TRUE, TRUE) if(CONFIG_GET(flag/picture_logging_camera)) picture.log_to_file() \ No newline at end of file diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm index 8edca91a91..b54fd51444 100644 --- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm +++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm @@ -565,9 +565,16 @@ required_other = TRUE /datum/chemical_reaction/slime/slimestop/on_reaction(datum/reagents/holder) + sleep(50) + var/obj/item/slime_extract/sepia/extract = holder.my_atom var/turf/T = get_turf(holder.my_atom) - var/list/M = list(get_mob_by_key(holder.my_atom.fingerprintslast)) - new /obj/effect/timestop(T, null, null, M) + new /obj/effect/timestop(T, null, null, null) + if(istype(extract)) + if(extract.Uses > 0) + var/mob/lastheld = get_mob_by_key(holder.my_atom.fingerprintslast) + if(lastheld && !lastheld.equip_to_slot_if_possible(extract, SLOT_HANDS, disable_warning = TRUE)) + extract.forceMove(get_turf(lastheld)) + ..() /datum/chemical_reaction/slime/slimecamera diff --git a/code/modules/research/xenobiology/crossbreeding/burning.dm b/code/modules/research/xenobiology/crossbreeding/burning.dm index 363331e12d..375adf8e88 100644 --- a/code/modules/research/xenobiology/crossbreeding/burning.dm +++ b/code/modules/research/xenobiology/crossbreeding/burning.dm @@ -154,7 +154,7 @@ Burning extracts: /obj/item/slimecross/burning/sepia/do_effect(mob/user) user.visible_message("[src] shapes itself into a camera!") - new /obj/item/camera/timefreeze(get_turf(user)) + new /obj/item/camera/rewind(get_turf(user)) ..() /obj/item/slimecross/burning/cerulean @@ -289,25 +289,88 @@ Burning extracts: //Misc. things added +//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" + var/used = FALSE + +/datum/saved_bodypart + var/obj/item/bodypart/old_part + var/bodypart_type + var/brute_dam + var/burn_dam + var/stamina_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 + stamina_dam = part.stamina_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, INFINITY, null, FALSE) + saved_part.old_part.receive_damage(saved_part.brute_dam, saved_part.burn_dam, saved_part.stamina_dam) + 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) + if(!on || !pictures_left || !isturf(target.loc)) + return + if(!used)//selfie time + 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, "You'll remember this moment forever!") + + used = TRUE + target.AddComponent(/datum/component/dejavu, 2) + .=..() + +//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 + var/used = FALSE /obj/item/camera/timefreeze/afterattack(atom/target, mob/user, flag) if(!on || !pictures_left || !isturf(target.loc)) return - new /obj/effect/timestop(get_turf(target), 2, 50, list(user)) - . = ..() - var/text = "The camera fades away" - if(disk) - text += ", leaving the disk behind!" - user.put_in_hands(disk) - else - text += "!" - to_chat(user,"[text]") - qdel(src) + if(!used) //refilling the film does not refill the timestop + new /obj/effect/timestop(get_turf(target), 2, 50, list(user)) + used = TRUE + desc = "This camera has seen better days." + return ..() /obj/item/slimepotion/extract_cloner name = "extract cloning potion" diff --git a/code/modules/research/xenobiology/crossbreeding/regenerative.dm b/code/modules/research/xenobiology/crossbreeding/regenerative.dm index 6c7ea102bc..b1164beff4 100644 --- a/code/modules/research/xenobiology/crossbreeding/regenerative.dm +++ b/code/modules/research/xenobiology/crossbreeding/regenerative.dm @@ -148,8 +148,9 @@ Regenerative extracts: /obj/item/slimecross/regenerative/sepia colour = "sepia" -/obj/item/slimecross/regenerative/sepia/core_effect(mob/living/target, mob/user) - new /obj/effect/timestop(get_turf(target), 2, 50, list(user,target)) +/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" diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm index c4d2c34a71..233fa6fc5a 100644 --- a/code/modules/spells/spell_types/wizard.dm +++ b/code/modules/spells/spell_types/wizard.dm @@ -152,19 +152,21 @@ sound1 = 'sound/magic/teleport_diss.ogg' sound2 = 'sound/magic/teleport_app.ogg' -/obj/effect/proc_holder/spell/aoe_turf/conjure/timestop +/obj/effect/proc_holder/spell/aoe_turf/timestop name = "Stop Time" desc = "This spell stops time for everyone except for you, allowing you to move freely while your enemies and even projectiles are frozen." charge_max = 500 clothes_req = 1 - invocation = "TOKI WO TOMARE" + invocation = "TOKI YO TOMARE" invocation_type = "shout" range = 0 cooldown_min = 100 - summon_amt = 1 action_icon_state = "time" + var/timestop_range = 2 + var/timestop_duration = 100 - summon_type = list(/obj/effect/timestop/wizard) +/obj/effect/proc_holder/spell/aoe_turf/timestop/cast(list/targets, mob/user = usr) + new /obj/effect/timestop/magic(get_turf(user), timestop_range, timestop_duration, list(user)) /obj/effect/proc_holder/spell/aoe_turf/conjure/carp name = "Summon Carp" diff --git a/tgstation.dme b/tgstation.dme index e85c2c1111..d16e8d7242 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -356,6 +356,7 @@ #include "code\datums\components\chasm.dm" #include "code\datums\components\construction.dm" #include "code\datums\components\decal.dm" +#include "code\datums\components\dejavu.dm" #include "code\datums\components\earprotection.dm" #include "code\datums\components\edit_complainer.dm" #include "code\datums\components\empprotection.dm"