diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 0b535a44c3..8da220bf67 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -327,3 +327,41 @@ proc/isInSight(var/atom/A, var/atom/B) spawn(delay) for(var/client/C in group) C.screen -= O + +datum/projectile_data + var/src_x + var/src_y + var/time + var/distance + var/power_x + var/power_y + var/dest_x + var/dest_y + +/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ + var/power_x, var/power_y, var/dest_x, var/dest_y) + src.src_x = src_x + src.src_y = src_y + src.time = time + src.distance = distance + src.power_x = power_x + src.power_y = power_y + src.dest_x = dest_x + src.dest_y = dest_y + +/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power) + + // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], + // rotated at [rotation] and with the power of [power] + // Thanks to VistaPOWA for this function + + var/power_x = power * cos(angle) + var/power_y = power * sin(angle) + var/time = 2* power_y / 10 //10 = g + + var/distance = time * power_x + + var/dest_x = src_x + distance*sin(rotation); + var/dest_y = src_y + distance*cos(rotation); + + return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index e861826e0f..f2bd04a785 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -1575,6 +1575,16 @@ datum/design/bag_holding reliability_base = 80 build_path = "/obj/item/weapon/storage/backpack/holding" +datum/design/bluespace_crystal + name = "Artificial Bluespace Crystal" + desc = "A small blue crystal with mystical properties." + id = "bluespace_crystal" + req_tech = list("bluespace" = 5, "materials" = 7) + build_type = PROTOLATHE + materials = list("$gold" = 1500, "$diamond" = 3000, "$plasma" = 1500) + reliability_base = 100 + build_path = "/obj/item/bluespace_crystal/artificial" + ///////////////////////////////////////// /////////////////HUDs//////////////////// ///////////////////////////////////////// diff --git a/code/modules/telesci/bscrystal.dm b/code/modules/telesci/bscrystal.dm new file mode 100644 index 0000000000..d9e968f4da --- /dev/null +++ b/code/modules/telesci/bscrystal.dm @@ -0,0 +1,38 @@ +// Bluespace crystals, used in telescience and when crushed it will blink you to a random turf. + +/obj/item/bluespace_crystal + name = "bluespace crystal" + desc = "A glowing bluespace crystal, not much is known about how they work. It looks very delicate." + icon = 'icons/obj/telescience.dmi' + icon_state = "bluespace_crystal" + w_class = 1 + origin_tech = "bluespace=4;materials=3" + var/blink_range = 8 // The teleport range when crushed/thrown at someone. + +/obj/item/bluespace_crystal/New() + ..() + pixel_x = rand(-5, 5) + pixel_y = rand(-5, 5) + +/obj/item/bluespace_crystal/attack_self(var/mob/user) + blink_mob(user) + user.drop_item() + user.visible_message("[user] crushes the [src]!") + del(src) + +/obj/item/bluespace_crystal/proc/blink_mob(var/mob/living/L) + do_teleport(L, get_turf(L), blink_range, asoundin = 'sound/effects/phasein.ogg') + +/obj/item/bluespace_crystal/throw_impact(atom/hit_atom) + ..() + if(isliving(hit_atom)) + blink_mob(hit_atom) + del(src) + +// Artifical bluespace crystal, doesn't give you much research. + +/obj/item/bluespace_crystal/artificial + name = "artificial bluespace crystal" + desc = "An artificially made bluespace crystal, it looks delicate." + origin_tech = "bluespace=2" + blink_range = 4 // Not as good as the organic stuff! \ No newline at end of file diff --git a/code/modules/telesci/gps.dm b/code/modules/telesci/gps.dm index 19b107a503..28555a2dce 100644 --- a/code/modules/telesci/gps.dm +++ b/code/modules/telesci/gps.dm @@ -19,6 +19,10 @@ var/list/GPS_list = list() /obj/item/device/gps/Del() GPS_list.Remove(src) ..() +/obj/item/device/gps/Del() + GPS_list.Remove(src) + ..() + /obj/item/device/gps/emp_act(severity) emped = 1 overlays -= "working" @@ -29,6 +33,7 @@ var/list/GPS_list = list() overlays += "working" /obj/item/device/gps/attack_self(mob/user as mob) + var/obj/item/device/gps/t = "" if(emped) t += "ERROR" @@ -54,14 +59,11 @@ var/list/GPS_list = list() ..() if(href_list["tag"] ) var/a = input("Please enter desired tag.", name, gpstag) as text - a = copytext(sanitize(a), 1, 20) - if(length(a) != 4) - usr << " The tag must be four letters long!" - return - else + a = uppertext(copytext(sanitize(a), 1, 5)) + if(src.loc == usr) gpstag = a name = "global positioning system ([gpstag])" - return + attack_self(usr) /obj/item/device/gps/science icon_state = "gps-s" diff --git a/code/modules/telesci/telesci_computer.dm b/code/modules/telesci/telesci_computer.dm index a42e617f1c..85232a4106 100644 --- a/code/modules/telesci/telesci_computer.dm +++ b/code/modules/telesci/telesci_computer.dm @@ -1,27 +1,51 @@ /obj/machinery/computer/telescience - name = "telepad control console" + name = "\improper Telepad Control Console" desc = "Used to teleport objects to and from the telescience telepad." icon_state = "teleport" var/sending = 1 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 - var/x_off // X offset - var/y_off // Y offset - var/x_co = 1 // X coordinate - var/y_co = 1 // Y coordinate - var/z_co = 1 // Z coordinate + var/datum/projectile_data/last_tele_data = null + var/z_co = 1 + var/power_off + var/rotation_off + var/angle_off + + var/rotation = 0 + var/angle = 45 + var/power + + // Based on the power used + var/teleport_cooldown = 0 + var/list/power_options = list(5, 10, 20, 25, 30, 40, 50, 80, 100) // every index requires a bluespace crystal + var/teleporting = 0 + var/starting_crystals = 3 + var/list/crystals = list() /obj/machinery/computer/telescience/New() ..() - teles_left = rand(8,12) - x_off = rand(-10,10) - y_off = rand(-10,10) - initialize() + link_telepad() + recalibrate() + +/obj/machinery/computer/telescience/Del() + eject() + ..() + +/obj/machinery/computer/telescience/examine() + ..() + usr << "There are [crystals.len] bluespace crystals in the crystal ports." /obj/machinery/computer/telescience/initialize() ..() + link_telepad() + for(var/i = 1; i <= starting_crystals; i++) + crystals += new /obj/item/bluespace_crystal/artificial(null) // starting crystals + power = power_options[1] + +/obj/machinery/computer/telescience/proc/link_telepad() telepad = locate() in range(src, 7) /obj/machinery/computer/telescience/update_icon() @@ -35,220 +59,251 @@ icon_state = initial(icon_state) stat &= ~NOPOWER - /** - * The ui_interact proc is used to open and update Nano UIs - * If ui_interact is not used then the UI will not update correctly - * ui_interact is currently defined for /atom/movable - * - * @param user /mob The mob who is interacting with this ui - * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") - * - * @return nothing - */ -/obj/machinery/computer/telescience/ui_interact(mob/user, ui_key = "main") - if(stat & (BROKEN|NOPOWER)) return - if(user.stat || user.restrained()) return - - // this is the data which will be sent to the ui - var/data[0] - data["coordx"] = x_co - data["coordy"] = y_co - data["coordz"] = z_co - - var/datum/nanoui/ui = nanomanager.get_open_ui(user, src, ui_key) - if (!ui) - // the ui does not exist, so we'll create a new one - ui = new(user, src, ui_key, "telescience_console.tmpl", name, 380, 110) - // When the UI is first opened this is the data it will use - ui.set_initial_data(data) - ui.open() - else - // The UI is already open so push the new data to it - ui.push_data(data) - return - /obj/machinery/computer/telescience/attack_paw(mob/user) user << "You are too primitive to use this computer." return +/obj/machinery/computer/telescience/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/bluespace_crystal)) + if(crystals.len >= power_options.len) + user << "There are not enough crystal ports." + return + user.drop_item() + crystals += W + W.loc = null + user.visible_message("[user] inserts a [W] into the [src]'s crystal port.") + else + ..() + /obj/machinery/computer/telescience/attack_ai(mob/user) - return src.attack_hand(user) + src.attack_hand(user) -/obj/machinery/computer/telescience/attack_hand(mob/user as mob) - if(stat & BROKEN) +/obj/machinery/computer/telescience/attack_hand(mob/user) + if(..()) return + interact(user) - ui_interact(user) +/obj/machinery/computer/telescience/interact(mob/user) + + var/t = "
[temp_msg]

" + t += "Set Bearing" + t += "
[rotation]°
" + t += "Set Elevation" + t += "
[angle]°
" + t += "Set Power" + t += "
" + + for(var/i = 1; i <= power_options.len; i++) + if(crystals.len < i) + t += "[power_options[i]]" + continue + if(power == power_options[i]) + t += "[power_options[i]]" + continue + t += "[power_options[i]]" + + t += "
" + t += "Set Sector" + t += "
[z_co ? z_co : "NULL"]
" + + t += "
Send" + t += " Receive" + t += "
Recalibrate Crystals Eject Crystals" + + // Information about the last teleport + t += "
" + if(!last_tele_data) + t += "No teleport data found." + else + t += "Source Location: ([last_tele_data.src_x], [last_tele_data.src_y])
" + //t += "Distance: [round(last_tele_data.distance, 0.1)]m
" + t += "Time: [round(last_tele_data.time, 0.1)] secs
" + t += "
" + + var/datum/browser/popup = new(user, "telesci", name, 300, 500) + popup.set_content(t) + popup.open() + return /obj/machinery/computer/telescience/proc/sparks() if(telepad) - var/L = get_turf(E) var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, L) + s.set_up(5, 1, get_turf(telepad)) s.start() else return /obj/machinery/computer/telescience/proc/telefail() - if(prob(50)) - sparks() - for(var/mob/O in hearers(src, null)) - O.show_message("\red The telepad weakly fizzles.", 2) - return - if(prob(5)) - // Irradiate everyone in telescience! - for(var/obj/machinery/telepad/E in machines) - var/L = get_turf(E) - sparks() - for(var/mob/living/carbon/human/M in viewers(L, null)) - M.apply_effect((rand(10, 20)), IRRADIATE, 0) - M << "\red You feel strange." - return - if(prob(1)) - // AI CALL SHUTTLE I SAW RUNE, SUPER LOW CHANCE, CAN HARDLY HAPPEN - for(var/mob/living/carbon/O in viewers(src, null)) - var/datum/game_mode/cult/temp = new - O.show_message("\red The telepad flashes with a strange light, and you have a sudden surge of allegiance toward the true dark one!", 2) - O.mind.make_Cultist() - temp.grant_runeword(O) - sparks() - return - if(prob(1)) - // VIVA LA FUCKING REVOLUTION BITCHES, SUPER LOW CHANCE, CAN HARDLY HAPPEN - for(var/mob/living/carbon/O in viewers(src, null)) - O.show_message("\red The telepad flashes with a strange light, and you see all kind of images flash through your mind, of murderous things Nanotrasen has done, and you decide to rebel!", 2) - O.mind.make_Rev() - sparks() - return - if(prob(1)) - // The OH SHIT FUCK GOD DAMN IT LYNCH THE SCIENTISTS event. - for(var/mob/living/carbon/O in viewers(src, null)) - O.show_message("\red The telepad changes colors rapidly, and opens a portal, and you see what your mind seems to think is the very threads that hold the pattern of the universe together, and a eerie sense of paranoia creeps into you.", 2) - spacevine_infestation() - sparks() - return - if(prob(5)) - // HOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONK - for(var/mob/living/carbon/M in hearers(src, null)) - M << sound('sound/items/AirHorn.ogg') - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(istype(H.l_ear, /obj/item/clothing/ears/earmuffs) || istype(H.r_ear, /obj/item/clothing/ears/earmuffs)) - continue - M << "HONK" - M.sleeping = 0 - M.stuttering += 20 - M.ear_deaf += 30 - M.Weaken(3) - if(prob(30)) - M.Stun(10) - M.Paralyse(4) - else - M.make_jittery(500) - sparks() - return - if(prob(1)) - // They did the mash! (They did the monster mash!) The monster mash! (It was a graveyard smash!) - sparks() - for(var/obj/machinery/telepad/E in machines) - var/L = get_turf(E) - var/blocked = list(/mob/living/simple_animal/hostile, - /mob/living/simple_animal/hostile/alien/queen/large, - /mob/living/simple_animal/hostile/retaliate, - /mob/living/simple_animal/hostile/retaliate/clown, - /mob/living/simple_animal/hostile/giant_spider/nurse) - var/list/hostiles = typesof(/mob/living/simple_animal/hostile) - blocked - playsound(L, 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/carbon/human/M in viewers(L, null)) - flick("e_flash", M.flash) - var/chosen = pick(hostiles) - var/mob/living/simple_animal/hostile/H = new chosen - H.loc = L - return - return + sparks() + visible_message("The telepad weakly fizzles.") return /obj/machinery/computer/telescience/proc/doteleport(mob/user) - var/trueX = (x_co + x_off) - var/trueY = (y_co + y_off) - trueX = Clamp(trueX, 1, world.maxx) - trueY = Clamp(trueY, 1, world.maxy) - if(telepad) - var/turf/target = locate(trueX, trueY, z_co) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, telepad) - s.start() - flick("pad-beam", telepad) - user << "Teleport successful." - var/sparks = get_turf(target) - var/datum/effect/effect/system/spark_spread/y = new /datum/effect/effect/system/spark_spread - y.set_up(5, 1, sparks) - y.start() - var/turf/source = target - var/turf/dest = get_turf(telepad) - if(sending) - source = dest - dest = target - for(var/atom/movable/ROI in source) - if(ROI.anchored) continue - do_teleport(ROI, dest, 0) + + if(teleport_cooldown > world.time) + temp_msg = "Telepad is recharging power.
Please wait [round((teleport_cooldown - world.time) / 10)] seconds." return - return + + if(teleporting) + temp_msg = "Telepad is in use.
Please wait." + return + + if(telepad) + + var/truePower = Clamp(power + power_off, 1, 1000) + var/trueRotation = rotation + rotation_off + var/trueAngle = Clamp(angle + angle_off, 1, 90) + + var/datum/projectile_data/proj_data = projectile_trajectory(telepad.x, telepad.y, trueRotation, trueAngle, truePower) + last_tele_data = proj_data + + 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/turf/target = locate(trueX, trueY, z_co) + var/area/A = get_area(target) + flick("pad-beam", telepad) + + if(spawn_time > 15) // 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." + + + 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 + + // use a lot of power + use_power(power * 10) + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, get_turf(telepad)) + s.start() + + temp_msg = "Teleport successful.
" + if(teles_left < 10) + temp_msg += "
Calibration required soon." + else + temp_msg += "Data printed below." + investigate_log("[key_name(usr)]/[user] has teleported with Telescience at [trueX],[trueY],[z_co], in [A ? A.name : "null area"].","telesci") + + var/sparks = get_turf(target) + var/datum/effect/effect/system/spark_spread/y = new /datum/effect/effect/system/spark_spread + y.set_up(5, 1, sparks) + y.start() + + var/turf/source = target + var/turf/dest = get_turf(telepad) + if(sending) + source = dest + dest = target + + flick("pad-beam", telepad) + playsound(telepad.loc, 'sound/weapons/emitter2.ogg', 25, 1) + for(var/atom/movable/ROI in source) + // if is anchored, don't let through + if(ROI.anchored) + if(isliving(ROI)) + var/mob/living/L = ROI + if(L.buckled) + // TP people on office chairs + if(L.buckled.anchored) + continue + else + continue + else if(!isobserver(ROI)) + continue + do_teleport(ROI, dest) + updateDialog() /obj/machinery/computer/telescience/proc/teleport(mob/user) - if(x_co == null || y_co == null || z_co == null) - user << "Error: coordinates not set." + if(rotation == null || angle == null || z_co == null) + temp_msg = "ERROR!
Set a angle, rotation and sector." + return + if(power <= 0) + telefail() + temp_msg = "ERROR!
No power selected!" + return + if(angle < 1 || angle > 90) + telefail() + temp_msg = "ERROR!
Elevation is less than 1 or greater than 90." + return + if(z_co == 2 || z_co < 1 || z_co > 6) + telefail() + temp_msg = "ERROR! Sector is less than 1,
greater than 6, or equal to 2." + return if(teles_left > 0) - teles_left -= 1 doteleport(user) else telefail() + temp_msg = "ERROR!
Calibration required." return return +/obj/machinery/computer/telescience/proc/eject() + for(var/obj/item/I in crystals) + I.loc = src.loc + crystals -= I + power = 0 + /obj/machinery/computer/telescience/Topic(href, href_list) - if(stat & (NOPOWER|BROKEN)) - return 0 + if(..()) + return + if(href_list["setrotation"]) + var/new_rot = input("Please input desired bearing in degrees.", name, rotation) as num + if(..()) // Check after we input a value, as they could've moved after they entered something + return + rotation = Clamp(new_rot, -900, 900) + rotation = round(rotation, 0.01) - if(href_list["setx"]) - var/new_x = input("Please input desired X coordinate.", name, x_co) as num - if(new_x < 1 || new_x > 255) - usr << "Error: Invalid X coordinate." - else - x_co = new_x - return 1 + if(href_list["setangle"]) + var/new_angle = input("Please input desired elevation in degrees.", name, angle) as num + if(..()) + return + angle = Clamp(round(new_angle, 0.1), 1, 9999) - if(href_list["sety"]) - var/new_y = input("Please input desired Y coordinate.", name, y_co) as num - if(new_y < 1 || new_y > 255) - usr << "Error: Invalid Y coordinate." - else - y_co = new_y - return 1 + if(href_list["setpower"]) + var/index = href_list["setpower"] + index = text2num(index) + if(index != null && power_options[index]) + if(crystals.len >= index) + power = power_options[index] if(href_list["setz"]) - var/new_z = input("Please input desired Z coordinate.", name, z_co) as num - if(new_z == 2 || new_z < 1 || new_z > 7) - usr << "Error: Invalid Z coordinate." - else - z_co = new_z - return 1 + var/new_z = input("Please input desired sector.", name, z_co) as num + if(..()) + return + z_co = Clamp(round(new_z), 1, 10) if(href_list["send"]) sending = 1 teleport(usr) - return 1 if(href_list["receive"]) sending = 0 teleport(usr) - return 1 if(href_list["recal"]) - teles_left = rand(9,12) - x_off = rand(-10,10) - y_off = rand(-10,10) + recalibrate() sparks() - usr << "Calibration successful." - return 1 - return 0 \ No newline at end of file + temp_msg = "NOTICE:
Calibration successful." + + if(href_list["eject"]) + eject() + temp_msg = "NOTICE:
Bluespace crystals ejected." + + updateDialog() + +/obj/machinery/computer/telescience/proc/recalibrate() + teles_left = rand(30, 40) + angle_off = rand(-25, 25) + power_off = rand(-4, 0) + rotation_off = rand(-10, 10) \ No newline at end of file diff --git a/icons/obj/telescience.dmi b/icons/obj/telescience.dmi index 1f3344f2e4..4e8251beac 100644 Binary files a/icons/obj/telescience.dmi and b/icons/obj/telescience.dmi differ