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."