From 4c932542ef78c2126f7cdb2c9a63306e2652297c Mon Sep 17 00:00:00 2001 From: Fluffy <65877598+FluffyGhoster@users.noreply.github.com> Date: Wed, 17 Jul 2024 23:17:42 +0200 Subject: [PATCH] Telescience unapologetic buff (#18854) Refactored portals, improved handling. Telescience console can now target overmap zlevels, provided deep scan is not enabled and the contact is completely scanned. Telescience portals now properly delete when the timer expires. Greatly increased power usage for the telescience portals, especially if aiming outside targets. Refactored telescience console backend. Added various DMDocs. --- code/__DEFINES/_macros.dm | 1 + code/game/gamemodes/events/wormholes.dm | 2 +- code/game/objects/effects/portals.dm | 77 +++- code/modules/hydroponics/seed.dm | 2 +- code/modules/mining/mine_items.dm | 2 +- code/modules/shuttles/shuttle_specops.dm | 2 +- code/modules/telesci/telesci_computer.dm | 397 ++++++++++++++---- .../fluffyghost-teleporterscience.yml | 46 ++ 8 files changed, 437 insertions(+), 92 deletions(-) create mode 100644 html/changelogs/fluffyghost-teleporterscience.yml diff --git a/code/__DEFINES/_macros.dm b/code/__DEFINES/_macros.dm index 050551b3368..81192d6784a 100644 --- a/code/__DEFINES/_macros.dm +++ b/code/__DEFINES/_macros.dm @@ -21,6 +21,7 @@ #define SPAN_HEAR(X) ("" + X + "") #define SPAN_STYLE(style, X) "[X]" #define SPAN_COLOR(color, text) SPAN_STYLE("color: [color]", "[text]") +#define SPAN_CAUTION(X) ("" + X + "") #define SPAN_RED(x) "[x]" #define SPAN_YELLOW(x) "[x]" diff --git a/code/game/gamemodes/events/wormholes.dm b/code/game/gamemodes/events/wormholes.dm index 77ed3b137c8..8451d2d3021 100644 --- a/code/game/gamemodes/events/wormholes.dm +++ b/code/game/gamemodes/events/wormholes.dm @@ -49,7 +49,7 @@ //maybe this proc can even be used as an admin tool for teleporting players without ruining immulsions? /proc/create_wormhole(var/turf/enter as turf, var/turf/exit as turf) var/obj/effect/portal/P = new /obj/effect/portal( enter ) - P.target = exit + P.set_target(exit) P.creator = null P.icon = 'icons/obj/objects.dmi' P.failchance = 0 diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index 4728d4d680b..ce46e6f5ffb 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -8,28 +8,55 @@ mouse_opacity = MOUSE_OPACITY_ICON anchored = TRUE - var/does_teleport = TRUE // Some portals might just be visual + ///Boolean, if the portal can actually teleport, or not (aka it's just visual) + var/does_teleport = TRUE + var/has_lifespan = TRUE // Whether we want to directly control the lifespan or not + + ///Chance that the portal will fail var/failchance = 5 + + ///Boolean, if the portal has failed (aka it will teleport you somewhere else) var/has_failed = FALSE - var/obj/target + + /** + * The target the teleport aims to, an `/obj` or `/turf` + * + * *Not* to be set directly, use `set_target()` instead + */ + var/atom/target + + ///Who created the portal var/creator + + ///How precise (turf range) is the teleportation var/precision = 1 /obj/effect/portal/Initialize(mapload, turf/set_target, set_creator, lifespan = 300, precise = 1) . = ..() if(set_target) - target = set_target + set_target(set_target) + if(set_creator) creator = set_creator + if(istype(creator, /atom)) + RegisterSignal(creator, COMSIG_QDELETING, PROC_REF(handle_creator_qdel)) + if(has_lifespan && lifespan > 0) QDEL_IN(src, lifespan) + if(prob(failchance)) has_failed = TRUE precision = precise +/obj/effect/portal/Destroy() + if(istype(creator, /obj/item/hand_tele)) + var/obj/item/hand_tele/HT = creator + HT.remove_portal(src) + . = ..() + /obj/effect/portal/CollidedWith(atom/bumped_atom) . = ..() @@ -37,8 +64,6 @@ teleport(bumped_atom) /obj/effect/portal/Crossed(AM) - set waitfor = FALSE - if(does_teleport) teleport(AM) @@ -50,8 +75,6 @@ return ..() /obj/effect/portal/attack_hand(mob/user) - set waitfor = FALSE - if(does_teleport) teleport(user) @@ -86,11 +109,41 @@ visible_message(SPAN_WARNING("\The [src] oscillates violently as \the [movable] comes into contact with it, and collapses! Seems like the rift was unstable...")) qdel(src) -/obj/effect/portal/Destroy() - if(istype(creator, /obj/item/hand_tele)) - var/obj/item/hand_tele/HT = creator - HT.remove_portal(src) - return ..() +/** + * Sets the target of the teleporter + */ +/obj/effect/portal/proc/set_target(atom/new_target) + if(!is_type_in_list(new_target, list(/obj, /turf, /mob))) + stack_trace("Portal was tried to be targeted at something that it is not supposed to!") + return + + if(target) + UnregisterSignal(target, COMSIG_QDELETING) + + target = new_target + RegisterSignal(target, COMSIG_QDELETING, PROC_REF(handle_target_qdel)) + +/** + * Handles the teleporting target being deleted + */ +/obj/effect/portal/proc/handle_target_qdel() + SIGNAL_HANDLER + + if(!QDELETED(src)) + qdel(src) + +/** + * Handles the creator being deleted + */ +/obj/effect/portal/proc/handle_creator_qdel() + SIGNAL_HANDLER + + creator = null + + +/*############## + SUBTYPES +##############*/ /obj/effect/portal/spawner name = "portal" diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index a9f68e6661b..caef049a2be 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -277,7 +277,7 @@ if(turfs.len) var/turf/picked = get_turf(pick(turfs)) var/obj/effect/portal/P = new /obj/effect/portal(get_turf(target)) - P.target = picked + P.set_target(picked) P.creator = null /datum/seed/proc/handle_environment(var/turf/current_turf, var/datum/gas_mixture/environment, var/light_supplied, var/check_only) diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index 263a8eab90b..a80ae41fa1f 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -725,7 +725,7 @@ return var/chosen_beacon = pick(L) var/obj/effect/portal/wormhole/jaunt_tunnel/J = new /obj/effect/portal/wormhole/jaunt_tunnel(get_turf(src), chosen_beacon, null, 100) - J.target = chosen_beacon + J.set_target(chosen_beacon) playsound(src,'sound/effects/sparks4.ogg', 50, 1) qdel(src) diff --git a/code/modules/shuttles/shuttle_specops.dm b/code/modules/shuttles/shuttle_specops.dm index e37b4eb82f4..cdd5af58f89 100644 --- a/code/modules/shuttles/shuttle_specops.dm +++ b/code/modules/shuttles/shuttle_specops.dm @@ -158,7 +158,7 @@ var/obj/effect/portal/P = new(L.loc) P.set_invisibility(101)//So it is not seen by anyone. P.failchance = 0//So it has no fail chance when teleporting. - P.target = pick(spawn_marauder)//Where the marauder will arrive. + P.set_target(pick(spawn_marauder))//Where the marauder will arrive. spawn_marauder.Remove(P.target) sleep(10) diff --git a/code/modules/telesci/telesci_computer.dm b/code/modules/telesci/telesci_computer.dm index a09a97babe4..06fc1617a62 100644 --- a/code/modules/telesci/telesci_computer.dm +++ b/code/modules/telesci/telesci_computer.dm @@ -7,70 +7,178 @@ light_color = LIGHT_COLOR_BLUE circuit = /obj/item/circuitboard/telesci_console var/sending = 1 + + /** + * The telepad that this console controls + */ var/obj/machinery/telepad/telepad = null + var/temp_msg = "Telescience control console initialized.
Welcome." // VARIABLES // - var/teles_left // How many teleports left until it becomes uncalibrated + + ///How many teleports left until it becomes uncalibrated + var/teles_left + + ///Projectile data, info about the latest teleport/aim that was done var/datum/projectile_data/last_tele_data = null - var/z_co = 4 + + ///The zlevel aim offset, that was inputted by the user as a target + var/zlevel_offset = 0 + + ///The Zlevel that is being targeted, aka the real one + var/target_zlevel + + ///The rotation that was inputted by the user as a target + var/rotation = 0 + + ///The angle that was inputted by the user as a target + var/angle = 45 + + ///The power that was inputted by the user as a target + var/power = 5 + + ///The power offset var/power_off + + ///The rotation offset var/rotation_off //var/angle_off - var/last_target - var/rotation = 0 - var/angle = 45 - var/power = 5 + ///The target of the last teleportation, a `/turf` + var/turf/last_target // Based on the power used var/teleport_cooldown = 0 // every index requires a bluespace crystal + + /** + * A `/list` of power options that this console provides, for the projectile portal calculation + * + * Each added telecrystal (in `crystals`) unlocks an additional option, up to `max_crystals` + */ var/list/power_options = list(5, 10, 20, 25, 30, 40, 50, 80, 100) - var/teleporting = 0 + + ///Boolean, if we're teleporting (aka opening a portal) right now + var/teleporting = FALSE + var/starting_crystals = 0 //Edit this on the map, seriously. var/max_crystals = 5 - var/list/crystals = list() + + /** + * A `/list` of `/obj/item/bluespace_crystal` that this console contains + */ + var/list/obj/item/bluespace_crystal/crystals = list() + + ///A GPS that this console contains var/obj/item/device/gps/inserted_gps + ///A list of Zlevels that belong to the visitable (generally a ship) we are in + var/list/our_zlevels = list() + + /** + * A list of currently known Zlevels translations below our ship/visitable + * + * K -> String, the fake zlevel that can be aimed at, it's an absolute number (the console will show it as negative), + * starts at the offset of the lowest zlevel of the visitable the console is in + * (eg: console at the second zlevel of a ship, with one zlevel below it of the same ship, this will start with an index of 2, the console will show it as -2) + * + * V -> Number, the actual zlevel + */ + var/list/overmap_contacts_zlevels = list() + + /** + * The portal that is the origin, above the telepad + * + * Do *not* set this directly, use `set_origin_portal()` + */ + var/obj/effect/portal/origin_portal + + /** + * The portal that is the destination + * + * Do *not* set this directly, use `set_destination_portal()` + */ + var/obj/effect/portal/destination_portal + +/obj/machinery/computer/telescience/Initialize() + . = ..() + + recalibrate() + + for(var/i = 1; i <= starting_crystals; i++) + crystals += new /obj/item/bluespace_crystal/artificial(null) // starting crystals + + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/computer/telescience/LateInitialize() + . = ..() + + if(SSatlas.current_map.use_overmap && !linked) + var/my_sector = GLOB.map_sectors["[z]"] + if(istype(my_sector, /obj/effect/overmap/visitable)) + attempt_hook_up(my_sector) + + //If we got hooked up correctly, populate the list of our zlevels + if(linked) + for(var/zlevel in GLOB.map_sectors) + if(GLOB.map_sectors["[zlevel]"] == linked) + our_zlevels += text2num(zlevel) + + /obj/machinery/computer/telescience/Destroy() eject() if(inserted_gps) inserted_gps.forceMove(loc) inserted_gps = null + + if(!QDELETED(origin_portal)) + QDEL_NULL(origin_portal) + + if(!QDELETED(destination_portal)) + QDEL_NULL(destination_portal) + + QDEL_LIST(crystals) + + telepad = null + return ..() /obj/machinery/computer/telescience/get_examine_text(mob/user, distance, is_adjacent, infix, suffix) . = ..() - . += "There are [crystals.len ? crystals.len : "no"] bluespace crystal\s in the crystal slots." + . += "There are [length(crystals) ? length(crystals) : "no"] bluespace crystal\s in the crystal slots." -/obj/machinery/computer/telescience/Initialize() - . = ..() - recalibrate() - for(var/i = 1; i <= starting_crystals; i++) - crystals += new /obj/item/bluespace_crystal/artificial(null) // starting crystals /obj/machinery/computer/telescience/attackby(obj/item/attacking_item, mob/user, params) + if(istype(attacking_item, /obj/item/bluespace_crystal)) - if(crystals.len >= max_crystals) + if(length(crystals) >= max_crystals) to_chat(user, SPAN_WARNING("There are not enough crystal slots.")) return + user.drop_item(src) crystals += attacking_item attacking_item.forceMove(null) - user.visible_message("[user] inserts [attacking_item] into \the [src]'s crystal slot.", SPAN_NOTICE("You insert [attacking_item] into \the [src]'s crystal slot.")) + user.visible_message("[user] inserts [attacking_item] into \the [src]'s crystal slot.", + SPAN_NOTICE("You insert [attacking_item] into \the [src]'s crystal slot.")) updateDialog() + else if(istype(attacking_item, /obj/item/device/gps)) + if(!inserted_gps) inserted_gps = attacking_item user.unEquip(attacking_item) attacking_item.forceMove(src) - user.visible_message("[user] inserts [attacking_item] into \the [src]'s GPS device slot.", SPAN_NOTICE("You insert [attacking_item] into \the [src]'s GPS device slot.")) + user.visible_message("[user] inserts [attacking_item] into \the [src]'s GPS device slot.", + SPAN_NOTICE("You insert [attacking_item] into \the [src]'s GPS device slot.")) + else if(attacking_item.ismultitool()) + var/obj/item/device/multitool/M = attacking_item if(M.buffer && istype(M.buffer, /obj/machinery/telepad)) telepad = M.buffer M.buffer = null - to_chat(user, "You upload the data from the [attacking_item.name]'s buffer.") + to_chat(user, SPAN_CAUTION("You upload the data from the [attacking_item.name]'s buffer.")) + else ..() @@ -104,8 +212,8 @@ t += "Set Power" t += "
" - for(var/i = 1; i <= power_options.len; i++) - if(crystals.len + telepad.efficiency < i) + for(var/i = 1; i <= length(power_options); i++) + if(length(crystals) + telepad.efficiency < i) t += "[power_options[i]]" continue if(power == power_options[i]) @@ -114,8 +222,8 @@ t += "[power_options[i]]" t += "
" - t += "Set Sector" - t += "
[z_co ? z_co : "NULL"]
" + t += "Set Vertical Offset" + t += "
[!isnull(zlevel_offset) ? zlevel_offset : "No Data"]
" t += "
Open Portal" t += "
Recalibrate Crystals Eject Crystals" @@ -135,18 +243,29 @@ popup.open() return +/** + * Causes sparks to be emitted from the linked telepad + */ /obj/machinery/computer/telescience/proc/sparks() + SHOULD_NOT_SLEEP(TRUE) + if(telepad) spark(telepad, 5, GLOB.alldirs) else return +/** + * Called when the portal fails to be created + */ /obj/machinery/computer/telescience/proc/telefail() + SHOULD_NOT_SLEEP(TRUE) + sparks() visible_message(SPAN_WARNING("The telepad weakly fizzles.")) return /obj/machinery/computer/telescience/proc/doteleport(mob/user) + SHOULD_NOT_SLEEP(TRUE) if(teleport_cooldown > world.time) temp_msg = "Telepad is recharging power.
Please wait [round((teleport_cooldown - world.time) / 10)] seconds." @@ -167,76 +286,82 @@ var/trueX = Clamp(round(proj_data.dest_x, 1), 1, world.maxx) var/trueY = Clamp(round(proj_data.dest_y, 1), 1, world.maxy) - var/spawn_time = round(proj_data.time) * 10 + var/spawn_time = round(proj_data.time) - var/turf/target = locate(trueX, trueY, z_co) + var/turf/target = locate(trueX, trueY, target_zlevel) last_target = target - var/area/A = get_area(target) flick("pad-beam", telepad) - if(spawn_time > 15 ) // 1.5 seconds + if(spawn_time > 1.5 SECONDS) playsound(telepad.loc, 'sound/weapons/flash.ogg', 25, 1) // Wait depending on the time the projectile took to get there teleporting = 1 temp_msg = "Powering up bluespace crystals.
Please wait." + addtimer(CALLBACK(src, PROC_REF(complete_doteleport), user, target), spawn_time SECONDS) - spawn(round(proj_data.time) * 10) // in seconds - if(!telepad) - return - if(telepad.stat & NOPOWER) - return - teleporting = 0 - teleport_cooldown = world.time + (power * 2) - teles_left -= 1 +/** + * Completes the teleportation process, after the timer has expired, to be called only by + * the timer set by `doteleport()` + */ +/obj/machinery/computer/telescience/proc/complete_doteleport(mob/user, turf/target) + SHOULD_NOT_SLEEP(TRUE) - // use a lot of power - use_power_oneoff(power * 10) + if(!telepad) + return + if(telepad.stat & NOPOWER) + return + teleporting = 0 + teleport_cooldown = world.time + (power * 2) + teles_left -= 1 - spark(telepad, 5, GLOB.alldirs) + // use a lot of power + var/power_to_use = (power KILO WATTS) + if(overmap_contacts_zlevels["[abs(zlevel_offset)]"]) + power_to_use += (300 KILO WATTS * abs(zlevel_offset)) - temp_msg = "Bluespace portal creation successful.
" - if(teles_left < 10) - temp_msg += "
Calibration required soon." - else - temp_msg += "Data printed below." + use_power_oneoff(power_to_use) - spark(telepad, 5, GLOB.alldirs) + spark(telepad, 5, GLOB.alldirs) - var/turf/source = target - var/turf/dest = get_turf(telepad) - var/log_msg = "" - log_msg += ": [key_name(user)] has teleported " + temp_msg = "Bluespace portal creation successful.
" + if(teles_left < 10) + temp_msg += "
Calibration required soon." + else + temp_msg += "Data printed below." - if(sending) - source = dest - dest = target + spark(telepad, 5, GLOB.alldirs) - flick("pad-beam", telepad) - playsound(telepad.loc, 'sound/weapons/emitter2.ogg', 25, 1, extrarange = 3, falloff_distance = 5) + var/turf/source = target + var/turf/dest = get_turf(telepad) + var/log_msg = "" + log_msg += ": [key_name(user)] has teleported " - var/total_lifespawn = 25 * crystals.len + if(sending) + source = dest + dest = target - var/obj/effect/portal/origin = new /obj/effect/portal(dest, null, null, total_lifespawn, 0) - var/obj/effect/portal/destination = new /obj/effect/portal(source, null, null, total_lifespawn, 0) + flick("pad-beam", telepad) + playsound(telepad.loc, 'sound/weapons/emitter2.ogg', 25, 1, extrarange = 3, falloff_distance = 5) + + var/total_lifespawn = 25 * length(crystals) + + var/obj/effect/portal/origin = new /obj/effect/portal(dest, null, null, total_lifespawn, 0) + set_origin_portal(origin) + + var/obj/effect/portal/destination = new /obj/effect/portal(source, null, null, total_lifespawn, 0) + set_destination_portal(destination) - origin.target = destination - destination.target = origin - origin.has_failed = FALSE - destination.has_failed = FALSE + origin.set_target(destination) + destination.set_target(origin) + origin.has_failed = FALSE + destination.has_failed = FALSE - - if (dd_hassuffix(log_msg, ", ")) - log_msg = dd_limittext(log_msg, length(log_msg) - 2) - else - log_msg += "nothing" - log_msg += " [sending ? "to" : "from"] [trueX], [trueY], [z_co] ([A ? A.name : "null area"])" - investigate_log(log_msg, "telesci") - updateDialog() + updateDialog() /obj/machinery/computer/telescience/proc/teleport(mob/user) - if(rotation == null || angle == null || z_co == null) + if(rotation == null || angle == null || target_zlevel == null) temp_msg = "ERROR!
Set a angle, rotation and sector." return if(power <= 0) @@ -251,19 +376,19 @@ telefail() temp_msg = "ERROR!
Elevation is less than 1 or greater than 90." return - if(isNotStationLevel(z_co)) - telefail() - temp_msg = "ERROR! Sector is invalid! Valid sectors are [english_list(SSatlas.current_map.station_levels)]." - return if(teles_left > 0) doteleport(user) else telefail() temp_msg = "ERROR!
Calibration required." return - return +/** + * Ejects the telecrystals from the console + */ /obj/machinery/computer/telescience/proc/eject() + SHOULD_NOT_SLEEP(TRUE) + for(var/obj/item/I in crystals) I.forceMove(src.loc) crystals -= I @@ -295,14 +420,94 @@ var/index = href_list["setpower"] index = text2num(index) if(index != null && power_options[index]) - if(crystals.len + telepad.efficiency >= index) + if(length(crystals) + telepad.efficiency >= index) power = power_options[index] if(href_list["setz"]) - var/new_z = input("Please input desired sector.", name, z_co) as num + var/inputed_new_z = tgui_input_number(usr, "Please input desired height offset", "Section Selection", 0, max_value = world.maxz, min_value = world.maxz*-1, round_value = TRUE) + if(..()) return - z_co = Clamp(round(new_z), 1, 10) + + //Use a holder var to do the calculations, detached from the user input, as we vary it + var/new_z = inputed_new_z + //If we crossed the 0 mark, get a free additional zlevel (so we don't have to deal with a zlevel 0) + if(new_z < 0 && abs(new_z) >= src.z) + new_z-- + + //Prevent people from targeting admin levels + if(isAdminLevel(src.z + new_z)) + to_chat(usr, SPAN_WARNING("Bluespace forces prevent this offset from being used.")) + return + + //If we are going into the negatives, those are "fake" zlevels that we use to refer + //to outside areas (like other ships, planets, sites, you name it) + //Same goes for not connected zlevels, in case we ever add them above us + if((src.z + new_z) < 0 || !AreConnectedZLevels(src.z, (src.z + new_z))) + + overmap_contacts_zlevels = list() + + var/list/obj/effect/overmap/visitable/already_added_visitables = list() + + //Get the minimum zlevel that we need to offset to be extraneous of our ship + //eg. if we are on Zlevel 4, and our ship covers Z 1 to 5, we have 3 Zlevels + //that are ours, so we need to offset by 4 to start being outside our zlevels + //(of course those zlevels would be virtual ones, to place the translation to the real ones for overmap visitables) + var/our_zlevel = src.z + var/min_zlevel_below_us = 1 + while((our_zlevel-=1) in our_zlevels) + min_zlevel_below_us++ + + for(var/obj/machinery/computer/ship/sensors/S in SSmachinery.machinery) + //If we have ownership of this + if(linked.check_ownership(S)) + + //If the deep scan is toggled, we do not allow it, as the range is too large + //print a message to the user and break the loop + var/obj/machinery/shipsensors/ship_sensors = S.sensor_ref?.resolve() + if((ship_sensors?.deep_scan_toggled)) + to_chat(usr, SPAN_WARNING("The deep scan is currently enabled and interferes with the teleporter lock ability.")) + break + + for(var/obj/effect/overmap/visitable/known_visitable in S.objects_in_view) + //If not fully scanned and identified, skip + if(S.objects_in_view[known_visitable] < 100) + continue + + //If it's us or some BS, skip + if(known_visitable.map_z == our_zlevels) + continue + + //If we added this already, skip + if(known_visitable in already_added_visitables) + continue + + + //Ok, we found what would be the offset below us to start adding shit, let's add shit + for(var/contact_zlevel in known_visitable.map_z) + overmap_contacts_zlevels["[min_zlevel_below_us+length(overmap_contacts_zlevels)]"] = contact_zlevel + + //Mark it as added + already_added_visitables += known_visitable + + //One console gave us the zlevels, we can break the loop + if(length(overmap_contacts_zlevels)) + break + + if((src.z + new_z) < 0 && (num2text(abs(new_z)-1) in overmap_contacts_zlevels)) + target_zlevel = overmap_contacts_zlevels["[abs(new_z)-1]"] + zlevel_offset = inputed_new_z + else + to_chat(usr, SPAN_WARNING("There is nothing targetable at this height.")) + return + + //Same area as us + else + if((src.z+new_z) in our_zlevels) + target_zlevel = (src.z+new_z) + zlevel_offset = inputed_new_z + else + to_chat(usr, SPAN_WARNING("There is nothing targetable at this height.")) if(href_list["ejectGPS"]) if(inserted_gps) @@ -331,8 +536,48 @@ updateDialog() +/obj/machinery/computer/telescience/process(seconds_per_tick) + if((stat & NOPOWER) || (telepad?.stat & NOPOWER)) + QDEL_NULL(origin_portal) + +/** + * Recomputes the parameters used for aiming + */ /obj/machinery/computer/telescience/proc/recalibrate() + SHOULD_NOT_SLEEP(TRUE) + teles_left = rand(30, 40) //angle_off = rand(-25, 25) power_off = rand(-4, 0) rotation_off = rand(-10, 10) + +/** + * Sets the origin portal + * + * * orig_portal - An `/obj/effect/portal` that will act as the "origin" + */ +/obj/machinery/computer/telescience/proc/set_origin_portal(obj/effect/portal/orig_portal) + SHOULD_NOT_SLEEP(TRUE) + + origin_portal = orig_portal + RegisterSignal(origin_portal, COMSIG_QDELETING, PROC_REF(handle_portal_qdel)) + +/** + * Sets the origin portal + * + * * dest_portal - An `/obj/effect/portal` that will act as the "destination" + */ +/obj/machinery/computer/telescience/proc/set_destination_portal(obj/effect/portal/dest_portal) + SHOULD_NOT_SLEEP(TRUE) + + destination_portal = dest_portal + RegisterSignal(destination_portal, COMSIG_QDELETING, PROC_REF(handle_portal_qdel)) + +/** + * Handles a portal being deleted + */ +/obj/machinery/computer/telescience/proc/handle_portal_qdel() + SIGNAL_HANDLER + + destination_portal = null + origin_portal = null diff --git a/html/changelogs/fluffyghost-teleporterscience.yml b/html/changelogs/fluffyghost-teleporterscience.yml new file mode 100644 index 00000000000..bd39f882815 --- /dev/null +++ b/html/changelogs/fluffyghost-teleporterscience.yml @@ -0,0 +1,46 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +# balance +# admin +# backend +# security +# refactor +################################# + +# Your name. +author: FluffyGhost + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - refactor: "Refactored portals, improved handling." + - rscadd: "Telescience console can now target overmap zlevels, provided deep scan is not enabled and the contact is completely scanned." + - bugfix: "Telescience portals now properly delete when the timer expires." + - rscadd: "Greatly increased power usage for the telescience portals, especially if aiming outside targets." + - refactor: "Refactored telescience console backend." + - refactor: "Added various DMDocs."