From 0c2f8cedb5cc7c6534b3f36469a508a80f5c679b Mon Sep 17 00:00:00 2001 From: Aronai Sieyes Date: Tue, 28 Apr 2020 23:18:08 -0400 Subject: [PATCH 1/3] Add UAV, UAV software Design placed in mechfab --- code/_helpers/game.dm | 6 +- code/game/objects/items/uav.dm | 347 ++++++++++++++++++ code/game/objects/objs.dm | 1 + code/modules/mob/living/carbon/human/human.dm | 2 +- code/modules/mob/living/carbon/human/life.dm | 24 +- code/modules/mob/living/life.dm | 2 - code/modules/mob/mob_movement.dm | 15 +- .../computers/modular_computer/core.dm | 12 + .../modular_computers/file_system/program.dm | 8 +- .../file_system/programs/generic/uav.dm | 266 ++++++++++++++ .../hardware/network_card.dm | 33 +- code/modules/nano/modules/nano_module.dm | 3 + code/modules/research/mechfab_designs.dm | 9 + icons/mob/radial.dmi | Bin 16285 -> 16705 bytes icons/obj/uav.dmi | Bin 0 -> 11615 bytes nano/templates/mod_uav.tmpl | 53 +++ vorestation.dme | 2 + 17 files changed, 754 insertions(+), 29 deletions(-) create mode 100644 code/game/objects/items/uav.dm create mode 100644 code/modules/modular_computers/file_system/programs/generic/uav.dm create mode 100644 icons/obj/uav.dmi create mode 100644 nano/templates/mod_uav.tmpl diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index 17a2544850..119f9d554f 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -272,17 +272,17 @@ var/list/hearturfs = list() for(var/thing in hear) - if(istype(thing,/obj)) + if(istype(thing, /obj)) //Can't use isobj() because /atom/movable returns true in that, and so lighting overlays would be included objs += thing hearturfs |= get_turf(thing) - else if(istype(thing,/mob)) + if(ismob(thing)) mobs += thing hearturfs |= get_turf(thing) //A list of every mob with a client for(var/mob in player_list) //VOREStation Edit - Trying to fix some vorestation bug. - if(!istype(mob, /mob)) + if(!ismob(mob)) player_list -= mob crash_with("There is a null or non-mob reference inside player_list ([mob]).") continue diff --git a/code/game/objects/items/uav.dm b/code/game/objects/items/uav.dm new file mode 100644 index 0000000000..d353dda849 --- /dev/null +++ b/code/game/objects/items/uav.dm @@ -0,0 +1,347 @@ +#define UAV_OFF 0 +#define UAV_ON 1 +#define UAV_PAIRING 2 +#define UAV_PACKED 3 + +/obj/item/device/uav + name = "recon skimmer" + desc = "A semi-portable reconisance drone that folds into a backpack-sized carrying case." + icon = 'icons/obj/uav.dmi' + icon_state = "uav" + + var/obj/item/weapon/cell/cell + var/cell_type = null //Can put a starting cell here + + density = 1 //Is dense, but not anchored, so you can swap with it + slowdown = 3 //Heevvee. + + health = 100 + var/power_per_process = 50 // About 6.5 minutes of use on a high-cell (10,000) + var/state = UAV_OFF + + var/datum/effect/effect/system/ion_trail_follow/ion_trail + + var/list/mob/living/masters + + // So you know which is which + var/nickname = "Generic Droan" + + // Radial menu + var/static/image/radial_pickup = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_pickup") + var/static/image/radial_wrench = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_wrench") + var/static/image/radial_power = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_power") + var/static/image/radial_pair = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_pair") + + // Movement cooldown + var/next_move = 0 + + // Idle shutdown time + var/no_masters_time = 0 + +/obj/item/device/uav/loaded + cell_type = /obj/item/weapon/cell/high + +/obj/item/device/uav/Initialize() + . = ..() + + if(!cell && cell_type) + cell = new cell_type + + ion_trail = new /datum/effect/effect/system/ion_trail_follow() + ion_trail.set_up(src) + ion_trail.stop() + +/obj/item/device/uav/Destroy() + qdel_null(cell) + qdel_null(ion_trail) + LAZYCLEARLIST(masters) + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/device/uav/attack_hand(var/mob/user) + //Has to be on the ground to work with it properly + if(!isturf(loc)) + return ..() + + var/list/options = list( + "Pick Up" = radial_pickup, + "(Dis)Assemble" = radial_wrench, + "Toggle Power" = radial_power, + "Pairing Mode" = radial_pair) + var/choice = show_radial_menu(user, src, options, require_near = !issilicon(user)) + + switch(choice) + // Can pick up when off or packed + if("Pick Up") + if(state == UAV_OFF || state == UAV_PACKED) + return ..() + else + to_chat(user,"Turn [nickname] off or pack it first!") + return + // Can disasemble or reassemble from packed or off (and this one takes time) + if("(Dis)Assemble") + if(can_transition_to(state == UAV_PACKED ? UAV_OFF : UAV_PACKED, user) && do_after(user, 10 SECONDS, src)) + return toggle_packed(user) + // Can toggle power from on and off + if("Toggle Power") + if(can_transition_to(state == UAV_ON ? UAV_OFF : UAV_ON, user)) + return toggle_power(user) + // Can pair when off + if("Pairing Mode") + if(can_transition_to(state == UAV_PAIRING ? UAV_OFF : UAV_PAIRING, user)) + return toggle_pairing(user) + +/obj/item/device/uav/attackby(var/obj/item/I, var/mob/user) + if(istype(I, /obj/item/modular_computer) && state == UAV_PAIRING) + var/obj/item/modular_computer/MC = I + LAZYDISTINCTADD(MC.paired_uavs, weakref(src)) + playsound(src, 'sound/machines/buttonbeep.ogg', 50, 1) + visible_message("[user] pairs [I] to [nickname]") + toggle_pairing() + + else if(I.is_screwdriver() && cell) + if(do_after(user, 3 SECONDS, src)) + to_chat(user, "You remove [cell] into [nickname].") + playsound(src, I.usesound, 50, 1) + power_down() + cell.forceMove(get_turf(src)) + cell = null + + else if(istype(I, /obj/item/weapon/cell) && !cell) + if(do_after(user, 3 SECONDS, src)) + to_chat(user, "You insert [I] into [nickname].") + playsound(src, 'sound/items/deconstruct.ogg', 50, 1) + power_down() + cell.forceMove(get_turf(src)) + cell = null + + else if(istype(I, /obj/item/weapon/pen) || istype(I, /obj/item/device/flashlight/pen)) + var/tmp_label = sanitizeSafe(input(user, "Enter a nickname for [src]", "Nickname", nickname), MAX_NAME_LEN) + if(length(tmp_label) > 50 || length(tmp_label) < 3) + to_chat(user, "The nickname must be between 3 and 50 characters.") + else + to_chat(user, "You scribble your new nickname on the side of [src].") + nickname = tmp_label + desc = initial(desc) + " This one has '[nickname]' scribbled on the side." + else + return ..() + +/obj/item/device/uav/proc/can_transition_to(var/new_state, var/mob/user) + switch(state) //Current one + if(UAV_ON) + if(new_state == UAV_OFF || new_state == UAV_PACKED) + . = TRUE + if(UAV_OFF) + if(new_state == UAV_ON || new_state == UAV_PACKED || new_state == UAV_PAIRING) + . = TRUE + if(UAV_PAIRING) + if(new_state == UAV_OFF) + . = TRUE + if(UAV_PACKED) + if(new_state == UAV_OFF) + . = TRUE + + if(!.) + if(user) + to_chat(user, "You can't do that while [nickname] is in this state.") + return FALSE + +/obj/item/device/uav/update_icon() + cut_overlays() + switch(state) + if(UAV_PAIRING) + add_overlay("[initial(icon_state)]_pairing") + icon_state = "[initial(icon_state)]" + if(UAV_ON) + icon_state = "[initial(icon_state)]_on" + if(UAV_OFF) + icon_state = "[initial(icon_state)]" + if(UAV_PACKED) + icon_state = "[initial(icon_state)]_packed" + +/obj/item/device/uav/process() + if(cell?.use(power_per_process) != power_per_process) + visible_message("[src] sputters and thuds to the ground, inert.") + playsound(src, 'sound/items/drop/metalboots.ogg', 75, 1) + power_down() + health -= initial(health)*0.25 //Lose 25% of your original health + + if(LAZYLEN(masters)) + no_masters_time = 0 + else if(no_masters_time++ > 50) + power_down() + +/obj/item/device/uav/proc/toggle_pairing() + switch(state) + if(UAV_PAIRING) + state = UAV_OFF + update_icon() + return TRUE + if(UAV_OFF) + state = UAV_PAIRING + update_icon() + return TRUE + return FALSE + +/obj/item/device/uav/proc/toggle_power() + switch(state) + if(UAV_OFF) + power_up() + return TRUE + if(UAV_ON) + power_down() + return TRUE + return FALSE + +/obj/item/device/uav/proc/toggle_packed() + if(UAV_ON) + power_down() + switch(state) + if(UAV_OFF) //Packing + state = UAV_PACKED + w_class = ITEMSIZE_LARGE + slowdown = 1 + density = FALSE + update_icon() + return TRUE + if(UAV_PACKED) //Unpacking + state = UAV_OFF + w_class = ITEMSIZE_HUGE + slowdown = 3 + density = TRUE + update_icon() + return TRUE + return FALSE + +/obj/item/device/uav/proc/power_up() + if(state != UAV_OFF || !isturf(loc)) + return + if(cell?.use(power_per_process) != power_per_process) + visible_message("[src] sputters and chugs as it tries, and fails, to power up.") + return + + state = UAV_ON + update_icon() + start_hover() + set_light(4, 4, "#FFFFFF") + START_PROCESSING(SSobj, src) + no_masters_time = 0 + visible_message("[nickname] buzzes and lifts into the air.") + +/obj/item/device/uav/proc/power_down() + if(state != UAV_ON) + return + + state = UAV_OFF + update_icon() + stop_hover() + set_light(0) + LAZYCLEARLIST(masters) + STOP_PROCESSING(SSobj, src) + visible_message("[nickname] gracefully settles onto the ground.") + +//////////////// Helpers +/obj/item/device/uav/get_cell() + return cell + +/obj/item/device/uav/relaymove(var/mob/user, direction, signal = 1) + if(signal && state == UAV_ON && (weakref(user) in masters)) + if(next_move <= world.time) + next_move = world.time + (1 SECOND/signal) + step(src, direction) + return TRUE // Even if we couldn't step, we're taking credit for absorbing the move + return FALSE + +/obj/item/device/uav/proc/get_status_string() + return "[nickname] - [get_x(src)],[get_y(src)],[get_z(src)] - I:[health]/[initial(health)] - C:[cell ? "[cell.charge]/[cell.maxcharge]" : "Not Installed"]" + +/obj/item/device/uav/proc/add_master(var/mob/living/M) + LAZYDISTINCTADD(masters, weakref(M)) + +/obj/item/device/uav/proc/remove_master(var/mob/living/M) + LAZYREMOVE(masters, weakref(M)) + +/obj/item/device/uav/check_eye() + if(state == UAV_ON) + return 0 + else + return -1 + +/obj/item/device/uav/proc/start_hover() + if(!ion_trail.on) //We'll just use this to store if we're floating or not + ion_trail.start() + var/amplitude = 2 //maximum displacement from original position + var/period = 36 //time taken for the mob to go up >> down >> original position, in deciseconds. Should be multiple of 4 + + var/top = old_y + amplitude + var/bottom = old_y - amplitude + var/half_period = period / 2 + var/quarter_period = period / 4 + + animate(src, pixel_y = top, time = quarter_period, easing = SINE_EASING | EASE_OUT, loop = -1) //up + animate(pixel_y = bottom, time = half_period, easing = SINE_EASING, loop = -1) //down + animate(pixel_y = old_y, time = quarter_period, easing = SINE_EASING | EASE_IN, loop = -1) //back + +/obj/item/device/uav/proc/stop_hover() + if(ion_trail.on) + ion_trail.stop() + animate(src, pixel_y = old_y, time = 5, easing = SINE_EASING | EASE_IN) //halt animation + +/obj/item/device/uav/hear_talk(var/mob/M, list/message_pieces, verb) + var/name_used = M.GetVoice() + for(var/wr_master in masters) + var/weakref/wr = wr_master + var/mob/master = wr.resolve() + var/message = master.combine_message(message_pieces, verb, M) + var/rendered = "UAV received: [name_used] [message]" + master.show_message(rendered, 2) + +/obj/item/device/uav/see_emote(var/mob/living/M, text) + for(var/wr_master in masters) + var/weakref/wr = wr_master + var/mob/master = wr.resolve() + var/rendered = "UAV received, [text]" + master.show_message(rendered, 2) + +/obj/item/device/uav/show_message(msg, type, alt, alt_type) + for(var/wr_master in masters) + var/weakref/wr = wr_master + var/mob/master = wr.resolve() + var/rendered = "UAV received, [msg]" + master.show_message(rendered, type) + +/obj/item/device/uav/take_damage(var/damage) + health -= damage + CheckHealth() + return + +/obj/item/device/uav/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + user.do_attack_animation(src) + health -= damage + CheckHealth() + return + +/obj/item/device/uav/ex_act(severity) + switch(severity) + if(1.0) + die() + if(2.0) + health -= 25 + CheckHealth() + +/obj/item/device/uav/proc/CheckHealth() + if(health <= 0) + die() + +/obj/item/device/uav/proc/die() + visible_message("[src] shorts out and explodes!") + power_down() + var/turf/T = get_turf(src) + qdel(src) + explosion(T, -1, 0, 1, 2) //Not very large + +#undef UAV_OFF +#undef UAV_ON +#undef UAV_PACKED \ No newline at end of file diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index a14a62eaa8..2a361f1d46 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -142,6 +142,7 @@ return /mob/proc/unset_machine() + machine?.remove_visual(src) src.machine = null /mob/proc/set_machine(var/obj/O) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 642eaa2c4f..7756829bf1 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -418,7 +418,7 @@ BITSET(hud_updateflag, WANTED_HUD) if(istype(usr,/mob/living/carbon/human)) var/mob/living/carbon/human/U = usr - U.handle_regular_hud_updates() + U.handle_hud_list() if(istype(usr,/mob/living/silicon/robot)) var/mob/living/silicon/robot/U = usr U.handle_regular_hud_updates() diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 4f5b754269..50156c808f 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -58,8 +58,8 @@ ..() - if(life_tick%30==15) - hud_updateflag = 1022 + if(life_tick % 30) + hud_updateflag = (1 << TOTAL_HUDS) - 1 voice = GetVoice() @@ -91,7 +91,7 @@ else if(stat == DEAD && !stasis) handle_defib_timer() - if(!handle_some_updates()) + if(skip_some_updates()) return //We go ahead and process them 5 times for HUD images and other stuff though. //Update our name based on whether our face is obscured/disfigured @@ -99,10 +99,10 @@ pulse = handle_pulse() -/mob/living/carbon/human/proc/handle_some_updates() +/mob/living/carbon/human/proc/skip_some_updates() if(life_tick > 5 && timeofdeath && (timeofdeath < 5 || world.time - timeofdeath > 6000)) //We are long dead, or we're junk mobs spawned like the clowns on the clown shuttle - return 0 - return 1 + return 1 + return 0 /mob/living/carbon/human/breathe() if(!inStasisNow()) @@ -951,7 +951,7 @@ //DO NOT CALL handle_statuses() from this proc, it's called from living/Life() as long as this returns a true value. /mob/living/carbon/human/handle_regular_status_updates() - if(!handle_some_updates()) + if(skip_some_updates()) return 0 if(status_flags & GODMODE) return 0 @@ -1292,8 +1292,11 @@ else bodytemp.icon_state = "temp0" - if(blinded) overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - else clear_fullscreens() + if(blinded) + overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + + else if(!machine) + clear_fullscreens() if(disabilities & NEARSIGHTED) //this looks meh but saves a lot of memory by not requiring to add var/prescription if(glasses) //to every /obj/item @@ -1395,11 +1398,12 @@ if(machine) var/viewflags = machine.check_eye(src) - machine.apply_visual(src) if(viewflags < 0) reset_view(null, 0) else if(viewflags && !looking_elsewhere) sight |= viewflags + else + machine.apply_visual(src) else if(eyeobj) if(eyeobj.owner != src) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 1e66f65478..b1c9018573 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -173,13 +173,11 @@ if(ear_damage < 100) adjustEarDamage(-0.05,-1) -//this handles hud updates. Calls update_vision() and handle_hud_icons() /mob/living/handle_regular_hud_updates() if(!client) return 0 ..() - handle_vision() handle_darksight() handle_hud_icons() diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 438d3e9071..d2fe2ecbd9 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -110,8 +110,8 @@ /client/Move(n, direct) - if(!mob) - return // Moved here to avoid nullrefs below + //if(!mob) // Clients cannot have a null mob, as enforced by byond + // return // Moved here to avoid nullrefs below if(mob.control_object) Move_object(direct) @@ -166,8 +166,11 @@ if(!mob.canmove) return - //if(istype(mob.loc, /turf/space) || (mob.flags & NOGRAV)) - // if(!mob.Process_Spacemove(0)) return 0 + //Relaymove could handle it + if(mob.machine) + var/result = mob.machine.relaymove(mob, direct) + if(result) + return result if(!mob.lastarea) mob.lastarea = get_area(mob.loc) @@ -218,10 +221,6 @@ return return mob.buckled.relaymove(mob,direct) - if(istype(mob.machine, /obj/machinery)) - if(mob.machine.relaymove(mob,direct)) - return - if(mob.pulledby || mob.buckled) // Wheelchair driving! if(istype(mob.loc, /turf/space)) return // No wheelchair driving in space diff --git a/code/modules/modular_computers/computers/modular_computer/core.dm b/code/modules/modular_computers/computers/modular_computer/core.dm index 2848468da8..db15e841ca 100644 --- a/code/modules/modular_computers/computers/modular_computer/core.dm +++ b/code/modules/modular_computers/computers/modular_computer/core.dm @@ -255,6 +255,18 @@ else return ..() +/obj/item/modular_computer/apply_visual(var/mob/user) + if(active_program) + return active_program.apply_visual(user) + +/obj/item/modular_computer/remove_visual(var/mob/user) + if(active_program) + return active_program.remove_visual(user) + +/obj/item/modular_computer/relaymove(var/mob/user, direction) + if(active_program) + return active_program.relaymove(user, direction) + /obj/item/modular_computer/proc/set_autorun(program) if(!hard_drive) return diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 895b73b5fa..ef7eece062 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -203,8 +203,12 @@ /datum/computer_file/program/apply_visual(mob/M) if(NM) - NM.apply_visual(M) + return NM.apply_visual(M) /datum/computer_file/program/remove_visual(mob/M) if(NM) - NM.remove_visual(M) + return NM.remove_visual(M) + +/datum/computer_file/program/proc/relaymove(var/mob/M, direction) + if(NM) + return NM.relaymove(M, direction) \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/generic/uav.dm b/code/modules/modular_computers/file_system/programs/generic/uav.dm new file mode 100644 index 0000000000..8e4909ab04 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/generic/uav.dm @@ -0,0 +1,266 @@ +/obj/item/modular_computer + var/list/paired_uavs //Weakrefs, don't worry about it! + +/datum/computer_file/program/uav + filename = "rigger" + filedesc = "UAV Control" + nanomodule_path = /datum/nano_module/uav + program_icon_state = "comm_monitor" + program_key_state = "generic_key" + program_menu_icon = "link" + extended_desc = "This program allows remote control of certain drones, but only when paired with this device." + size = 12 + available_on_ntnet = 1 + //requires_ntnet = 1 + +/datum/nano_module/uav + name = "UAV Control program" + var/obj/item/device/uav/current_uav = null //The UAV we're watching + var/signal_strength = 0 //Our last signal strength report (cached for a few seconds) + var/signal_test_counter = 0 //How long until next signal strength check + var/list/viewers //Who's viewing a UAV through us + var/adhoc_range = 30 //How far we can operate on a UAV without NTnet + +/datum/nano_module/uav/Destroy() + if(LAZYLEN(viewers)) + for(var/weakref/W in viewers) + var/M = W.resolve() + if(M) + unlook(M) + . = ..() + +/datum/nano_module/uav/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, state = default_state) + var/list/data = host.initial_data() + + if(current_uav) + if(QDELETED(current_uav)) + set_current(null) + else if(signal_test_counter-- <= 0) + signal_strength = get_signal_to(current_uav) + if(!signal_strength) + set_current(null) + else // Don't reset counter until we find a UAV that's actually in range we can stay connected to + signal_test_counter = 20 + + data["current_uav"] = null + if(current_uav) + data["current_uav"] = list("status" = current_uav.get_status_string(), "power" = current_uav.state == 1 ? 1 : null) + data["signal_strength"] = signal_strength ? signal_strength >= 2 ? "High" : "Low" : "None" + data["in_use"] = LAZYLEN(viewers) + + var/list/paired_map = list() + var/obj/item/modular_computer/mc_host = nano_host() + if(istype(mc_host)) + for(var/puav in mc_host.paired_uavs) + var/weakref/wr = puav + var/obj/item/device/uav/U = wr.resolve() + paired_map[++paired_map.len] = list("name" = "[U ? U.nickname : "!!Missing!!"]", "uavref" = "\ref[U]") + + data["paired_uavs"] = paired_map + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "mod_uav.tmpl", "UAV Control", 600, 500, state = state) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/datum/nano_module/uav/Topic(var/href, var/href_list = list(), var/datum/topic_state/state) + if((. = ..())) + return + state = state || DefaultTopicState() || global.default_state + if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE) + CouldUseTopic(usr) + return OnTopic(usr, href_list, state) + CouldNotUseTopic(usr) + return TRUE + +/datum/nano_module/uav/proc/OnTopic(var/mob/user, var/list/href_list) + if(href_list["switch_uav"]) + var/obj/item/device/uav/U = locate(href_list["switch_uav"]) //This is a \ref to the UAV itself + if(!istype(U)) + to_chat(usr,"Something is blocking the connection to that UAV. In-person investigation is required.") + return TOPIC_NOACTION + + if(!get_signal_to(U)) + to_chat(usr,"The screen freezes for a moment, before returning to the UAV selection menu. It's not able to connect to that UAV.") + return TOPIC_NOACTION + + set_current(U) + return TOPIC_REFRESH + + if(href_list["del_uav"]) + var/refstring = href_list["del_uav"] //This is a \ref to the UAV itself + var/obj/item/modular_computer/mc_host = nano_host() + //This is so we can really scrape up any weakrefs that can't resolve + for(var/weakref/wr in mc_host.paired_uavs) + if(wr.ref == refstring) + if(current_uav?.weakref == wr) + set_current(null) + LAZYREMOVE(mc_host.paired_uavs, wr) + + else if(href_list["view_uav"]) + if(!current_uav) + return TOPIC_NOACTION + + if(current_uav.check_eye(user) < 0) + to_chat(usr,"The screen freezes for a moment, before returning to the UAV selection menu. It's not able to connect to that UAV.") + else + viewing_uav(user) ? unlook(user) : look(user) + return TOPIC_NOACTION + + else if(href_list["power_uav"]) + if(!current_uav) + return TOPIC_NOACTION + else if(current_uav.toggle_power()) + //Clean up viewers faster + if(LAZYLEN(viewers)) + for(var/weakref/W in viewers) + var/M = W.resolve() + if(M) + unlook(M) + return TOPIC_REFRESH + +/datum/nano_module/uav/proc/DefaultTopicState() + return global.default_state + +/datum/nano_module/uav/proc/CouldNotUseTopic(mob/user) + . = ..() + unlook(user) + +/datum/nano_module/uav/proc/CouldUseTopic(mob/user) + . = ..() + if(viewing_uav(user)) + look(user) + +/datum/nano_module/uav/proc/set_current(var/obj/item/device/uav/U) + if(current_uav == U) + return + + signal_strength = 0 + current_uav = U + + if(LAZYLEN(viewers)) + for(var/weakref/W in viewers) + var/M = W.resolve() + if(M) + if(current_uav) + to_chat(M, "You're disconnected from the UAV's camera!") + unlook(M) + else + look(M) + +//// +//// Finding signal strength between us and the UAV +//// +/datum/nano_module/uav/proc/get_signal_to(var/atom/movable/AM) + // Following roughly the ntnet signal levels + // 0 is none + // 1 is weak + // 2 is strong + var/obj/item/modular_computer/host = nano_host() //Better not add this to anything other than modular computers. + if(!istype(host)) + return + var/our_signal = host.get_ntnet_status() //1 low, 2 good, 3 wired, 0 none + var/their_z = get_z(AM) + + //If we have no NTnet connection don't bother getting theirs + if(!our_signal) + if(get_z(host) == their_z && (get_dist(host, AM) < adhoc_range)) + return 1 //We can connect (with weak signal) in same z without ntnet, within 30 turfs + else + return 0 + + var/list/zlevels_in_range = using_map.get_map_levels(their_z, FALSE) + var/list/zlevels_in_long_range = using_map.get_map_levels(their_z, TRUE) - zlevels_in_range + var/their_signal = 0 + for(var/relay in ntnet_global.relays) + var/obj/machinery/ntnet_relay/R = relay + if(!R.operable()) + continue + if(R.z == their_z) + their_signal = 2 + break + if(R.z in zlevels_in_range) + their_signal = 2 + break + if(R.z in zlevels_in_long_range) + their_signal = 1 + break + + if(!their_signal) //They have no NTnet at all + if(get_z(host) == their_z && (get_dist(host, AM) < adhoc_range)) + return 1 //We can connect (with weak signal) in same z without ntnet, within 30 turfs + else + return 0 + else + return max(our_signal, their_signal) + +//// +//// UAV viewer handling +//// +/datum/nano_module/uav/proc/viewing_uav(mob/user) + return (weakref(user) in viewers) + +/datum/nano_module/uav/proc/look(var/mob/user) + if(issilicon(user)) //Too complicated for me to want to mess with at the moment + to_chat(user, "Regulations prevent you from controlling several corporeal forms at the same time!") + return + + if(!current_uav) + return + + user.set_machine(nano_host()) + user.reset_view(current_uav) + current_uav.add_master(user) + LAZYDISTINCTADD(viewers, weakref(user)) + +/datum/nano_module/uav/proc/unlook(var/mob/user) + user.unset_machine() + user.reset_view() + if(current_uav) + current_uav.remove_master(user) + LAZYREMOVE(viewers, weakref(user)) + +/datum/nano_module/uav/check_eye(var/mob/user) + if(get_dist(user, nano_host()) > 1 || user.blinded || !current_uav) + unlook(user) + return -1 + + var/viewflag = current_uav.check_eye(user) + if (viewflag < 0) //camera doesn't work + unlook(user) + return -1 + + return viewflag + +//// +//// Relaying movements to the UAV +//// +/datum/nano_module/uav/relaymove(var/mob/user, direction) + if(current_uav) + return current_uav.relaymove(user, direction, signal_strength) + +//// +//// The effects when looking through a UAV +//// +/datum/nano_module/uav/apply_visual(var/mob/M) + if(!M.client) + return + if(weakref(M) in viewers) + M.overlay_fullscreen("fishbed",/obj/screen/fullscreen/fishbed) + M.overlay_fullscreen("scanlines",/obj/screen/fullscreen/scanline) + + if(signal_strength <= 1) + M.overlay_fullscreen("whitenoise",/obj/screen/fullscreen/noise) + else + M.clear_fullscreen("whitenoise", 0) + else + remove_visual(M) + +/datum/nano_module/uav/remove_visual(mob/M) + if(!M.client) + return + M.clear_fullscreen("fishbed",0) + M.clear_fullscreen("scanlines",0) + M.clear_fullscreen("whitenoise",0) diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm index 2f382e0f4d..1447c5008e 100644 --- a/code/modules/modular_computers/hardware/network_card.dm +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -39,6 +39,27 @@ var/global/ntnet_card_uid = 1 icon_state = "netcard_advanced" hardware_size = 1 +/obj/item/weapon/computer_hardware/network_card/quantum + name = "quantum NTNet network card" + desc = "A network card that can connect to NTnet from anywhere, using quantum entanglement." + long_range = 1 + origin_tech = list(TECH_DATA = 6, TECH_ENGINEERING = 7) + power_usage = 200 // Infinite range but higher power usage. + icon_state = "netcard_advanced" + hardware_size = 1 + +/obj/item/weapon/computer_hardware/network_card/quantum/get_signal(var/specific_action = 0) + if(!holder2) + return 0 + + if(!enabled) + return 0 + + if(!check_functionality() || !ntnet_global || is_banned()) + return 0 + + return 2 + /obj/item/weapon/computer_hardware/network_card/wired name = "wired NTNet network card" desc = "An advanced network card for usage with standard NTNet frequencies. This one also supports wired connection." @@ -82,7 +103,8 @@ var/global/ntnet_card_uid = 1 var/holderz = get_z(holder2) if(!holderz) //no reception in nullspace return 0 - var/list/zlevels_in_range = using_map.get_map_levels(holderz, long_range) + var/list/zlevels_in_range = using_map.get_map_levels(holderz, FALSE) + var/list/zlevels_in_long_range = using_map.get_map_levels(holderz, TRUE) - zlevels_in_range var/best = 0 for(var/relay in ntnet_global.relays) var/obj/machinery/ntnet_relay/R = relay @@ -91,11 +113,16 @@ var/global/ntnet_card_uid = 1 continue //We're on the same z if(R.z == holderz) - best = 2 + best = 2 //Every network card gets high signal on the same z as the relay break // No point in going further //Not on the same z but within range anyway if(R.z in zlevels_in_range) - best = 1 + best = long_range ? 2 : 1 //High-power network cards get good signal further away + break + //Only in long range + if(long_range && (R.z in zlevels_in_long_range)) + best = 1 //High-power network cards can get low signal even at long range + break return best return 0 // No computer! diff --git a/code/modules/nano/modules/nano_module.dm b/code/modules/nano/modules/nano_module.dm index 90c213da1c..75c38c9ed3 100644 --- a/code/modules/nano/modules/nano_module.dm +++ b/code/modules/nano/modules/nano_module.dm @@ -62,3 +62,6 @@ /datum/proc/update_layout() return FALSE + +/datum/nano_module/proc/relaymove(var/mob/user, direction) + return FALSE diff --git a/code/modules/research/mechfab_designs.dm b/code/modules/research/mechfab_designs.dm index 125de3db6c..7316c42179 100644 --- a/code/modules/research/mechfab_designs.dm +++ b/code/modules/research/mechfab_designs.dm @@ -1030,3 +1030,12 @@ req_tech = list(TECH_MATERIAL = 7, TECH_ENGINEERING = 5, TECH_MAGNET = 5, TECH_POWER = 6, TECH_ILLEGAL = 3, TECH_BLUESPACE = 4, TECH_ARCANE = 2, TECH_PRECURSOR = 3) materials = list(MAT_DURASTEEL = 5000, MAT_GRAPHITE = 3000, MAT_MORPHIUM = 1500, MAT_OSMIUM = 1500, MAT_PHORON = 1750, MAT_VERDANTIUM = 3000, MAT_SUPERMATTER = 2000) build_path = /obj/item/rig_module/teleporter + +/datum/design/item/mechfab/uav/basic + name = "UAV - Recon Skimmer" + id = "recon_skimmer" + build_path = /obj/item/device/uav + time = 20 + req_tech = list(TECH_MATERIAL = 6, TECH_ENGINEERING = 5, TECH_PHORON = 3, TECH_MAGNET = 4, TECH_POWER = 6) + materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 6000, "silver" = 4000) + \ No newline at end of file diff --git a/icons/mob/radial.dmi b/icons/mob/radial.dmi index da1f69840c6843e9fdc1af99486fc56a8c1ee59d..883ea38caeb15ca55cdf4f4de936d645c4f93c1f 100644 GIT binary patch literal 16705 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 U4kaguWA^}M1r7O1S<}$}3+bS21^@s6 diff --git a/icons/obj/uav.dmi b/icons/obj/uav.dmi new file mode 100644 index 0000000000000000000000000000000000000000..daae9a03da8721510823274e2cb9834a4d50de41 GIT binary patch literal 11615 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 +
+
UAV:
+
+ {{if data.current_uav}} + {{:data.current_uav.status}} + {{else}} + [Not Connected] + {{/if}} +
+
+ +
+
Signal:
+
+ {{if data.current_uav}} + {{:data.signal_strength}} + {{else}} + [Not Connected] + {{/if}} +
+
+ +
+
Power:
+
+ {{if data.current_uav}} + {{:helper.link(data.current_uav.power ? 'Online' : 'Offline', data.current_uav.power ? 'check' : 'close', {'power_uav' : 1}, null, data.current_uav.power ? 'linkOn' : 'redButton')}} + {{else}} + [Not Connected] + {{/if}} +
+
+ +
+
Camera:
+
+ {{if data.current_uav}} + {{:helper.link(data.current_uav.power ? 'Available' : 'Unavailable', data.current_uav.power ? 'check' : 'close', {'view_uav' : 1}, null, data.in_use ? 'linkOn' : null)}} + {{else}} + [Not Connected] + {{/if}} +
+
+ +
+
Paired UAVs:
+
+{{for data.paired_uavs}} +
+ {{:helper.link(value.name, '', {'switch_uav' : value.uavref})}}{{:helper.link('', 'close', {'del_uav' : value.uavref}, null, 'redButton')}} +
+{{/for}} diff --git a/vorestation.dme b/vorestation.dme index ec81d482c9..c51c07fbab 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -1069,6 +1069,7 @@ #include "code\game\objects\items\trash.dm" #include "code\game\objects\items\trash_material.dm" #include "code\game\objects\items\trash_vr.dm" +#include "code\game\objects\items\uav.dm" #include "code\game\objects\items\devices\advnifrepair.dm" #include "code\game\objects\items\devices\ai_detector.dm" #include "code\game\objects\items\devices\aicard.dm" @@ -2803,6 +2804,7 @@ #include "code\modules\modular_computers\file_system\programs\generic\ntdownloader.dm" #include "code\modules\modular_computers\file_system\programs\generic\ntnrc_client.dm" #include "code\modules\modular_computers\file_system\programs\generic\nttransfer.dm" +#include "code\modules\modular_computers\file_system\programs\generic\uav.dm" #include "code\modules\modular_computers\file_system\programs\generic\wordprocessor.dm" #include "code\modules\modular_computers\file_system\programs\medical\suit_sensors.dm" #include "code\modules\modular_computers\file_system\programs\research\email_administration.dm" From fb87fbe2d7e6e1110324cb649a9a78938d1bcaa2 Mon Sep 17 00:00:00 2001 From: Aronai Sieyes Date: Tue, 28 Apr 2020 23:18:27 -0400 Subject: [PATCH 2/3] VS: Add UAV --- code/_helpers/unsorted.dm | 8 +------ .../ore_redemption_machine/survey_vendor.dm | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index a0a38bf9d1..2362ff1b3d 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -1273,14 +1273,8 @@ var/mob/dview/dview_mob = new if(!center) return - //VOREStation Add - Emergency Backup - if(!dview_mob) - dview_mob = new() - WARNING("dview mob was lost, and had to be recreated!") - //VOREStation Add End - dview_mob.loc = center - + dview_mob.see_invisible = invis_flags . = view(range, dview_mob) diff --git a/code/modules/mining/ore_redemption_machine/survey_vendor.dm b/code/modules/mining/ore_redemption_machine/survey_vendor.dm index a5afad7fab..fcde7790ea 100644 --- a/code/modules/mining/ore_redemption_machine/survey_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/survey_vendor.dm @@ -10,34 +10,35 @@ icon_vend = "exploration-vend" //VOREStation Add //VOREStation Edit Start - Heavily modified list prize_list = list( - new /datum/data/mining_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 1), + new /datum/data/mining_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 1), new /datum/data/mining_equipment("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 10), new /datum/data/mining_equipment("30 Marker Beacons", /obj/item/stack/marker_beacon/thirty, 30), new /datum/data/mining_equipment("GPS Device", /obj/item/device/gps/explorer, 10), - new /datum/data/mining_equipment("Whiskey", /obj/item/weapon/reagent_containers/food/drinks/bottle/whiskey, 10), + new /datum/data/mining_equipment("Whiskey", /obj/item/weapon/reagent_containers/food/drinks/bottle/whiskey, 10), new /datum/data/mining_equipment("Absinthe", /obj/item/weapon/reagent_containers/food/drinks/bottle/absinthe, 10), new /datum/data/mining_equipment("Cigar", /obj/item/clothing/mask/smokable/cigarette/cigar/havana, 15), new /datum/data/mining_equipment("Soap", /obj/item/weapon/soap/nanotrasen, 20), new /datum/data/mining_equipment("Laser Pointer", /obj/item/device/laser_pointer, 90), new /datum/data/mining_equipment("Geiger Counter", /obj/item/device/geiger, 75), new /datum/data/mining_equipment("Plush Toy", /obj/random/plushie, 30), - new /datum/data/mining_equipment("Extraction Equipment - Fulton Beacon", /obj/item/fulton_core, 300), - new /datum/data/mining_equipment("Extraction Equipment - Fulton Pack", /obj/item/extraction_pack, 125), + new /datum/data/mining_equipment("Extraction Equipment - Fulton Beacon",/obj/item/fulton_core, 300), + new /datum/data/mining_equipment("Extraction Equipment - Fulton Pack",/obj/item/extraction_pack, 125), new /datum/data/mining_equipment("Umbrella", /obj/item/weapon/melee/umbrella/random, 20), - new /datum/data/mining_equipment("Shelter Capsule", /obj/item/device/survivalcapsule, 50), + new /datum/data/mining_equipment("Shelter Capsule", /obj/item/device/survivalcapsule, 50), new /datum/data/mining_equipment("Point Transfer Card", /obj/item/weapon/card/mining_point_card/survey, 50), new /datum/data/mining_equipment("Survival Medipen", /obj/item/weapon/reagent_containers/hypospray/autoinjector/miner, 50), - new /datum/data/mining_equipment("Injector (L) - Glucose",/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose, 50), - new /datum/data/mining_equipment("Injector (L) - Panacea",/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity, 50), - new /datum/data/mining_equipment("Injector (L) - Trauma",/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/brute, 50), + new /datum/data/mining_equipment("Injector (L) - Glucose", /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose,50), + new /datum/data/mining_equipment("Injector (L) - Panacea", /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity,50), + new /datum/data/mining_equipment("Injector (L) - Trauma", /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/brute,50), new /datum/data/mining_equipment("Digital Tablet - Standard", /obj/item/modular_computer/tablet/preset/custom_loadout/standard, 50), new /datum/data/mining_equipment("Digital Tablet - Advanced", /obj/item/modular_computer/tablet/preset/custom_loadout/advanced, 100), new /datum/data/mining_equipment("Nanopaste Tube", /obj/item/stack/nanopaste, 100), new /datum/data/mining_equipment("Mini-Translocator", /obj/item/device/perfect_tele/one_beacon, 120), + new /datum/data/mining_equipment("UAV - Recon Skimmer", /obj/item/device/uav, 400), new /datum/data/mining_equipment("Space Cash", /obj/item/weapon/spacecash/c100, 100), new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 250), new /datum/data/mining_equipment("Luxury Shelter Capsule", /obj/item/device/survivalcapsule/luxury, 310), - new /datum/data/mining_equipment("Industrial Equipment - Phoron Bore", /obj/item/weapon/gun/magnetic/matfed, 300), + new /datum/data/mining_equipment("Industrial Equipment - Phoron Bore",/obj/item/weapon/gun/magnetic/matfed, 300), new /datum/data/mining_equipment("Survey Tools - Shovel", /obj/item/weapon/shovel, 40), new /datum/data/mining_equipment("Survey Tools - Mechanical Trap", /obj/item/weapon/beartrap, 50), new /datum/data/mining_equipment("Defense Equipment - Smoke Bomb",/obj/item/weapon/grenade/smokebomb, 10), @@ -47,7 +48,7 @@ new /datum/data/mining_equipment("Fishing Net", /obj/item/weapon/material/fishing_net, 50), new /datum/data/mining_equipment("Titanium Fishing Rod", /obj/item/weapon/material/fishing_rod/modern, 100), new /datum/data/mining_equipment("Durasteel Fishing Rod", /obj/item/weapon/material/fishing_rod/modern/strong, 750), - new /datum/data/mining_equipment("Bar Shelter Capsule", /obj/item/device/survivalcapsule/luxurybar, 1000) + new /datum/data/mining_equipment("Bar Shelter Capsule", /obj/item/device/survivalcapsule/luxurybar, 1000) ) //VOREStation Edit End From e8c068912c52172908388cf7dc699158f8335212 Mon Sep 17 00:00:00 2001 From: Aronai Sieyes Date: Tue, 28 Apr 2020 23:18:48 -0400 Subject: [PATCH 3/3] Fix MC nav icon --- .../modular_computers/file_system/programs/ships/navigation.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/modular_computers/file_system/programs/ships/navigation.dm b/code/modules/modular_computers/file_system/programs/ships/navigation.dm index dbe7aed5de..88db90598b 100644 --- a/code/modules/modular_computers/file_system/programs/ships/navigation.dm +++ b/code/modules/modular_computers/file_system/programs/ships/navigation.dm @@ -4,7 +4,7 @@ nanomodule_path = /datum/nano_module/program/ship/nav program_icon_state = "helm" program_key_state = "generic_key" - program_menu_icon = "search" + program_menu_icon = "pin-s" extended_desc = "Displays a ship's location in the sector." required_access = null requires_ntnet = 1