diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index 8713510fca6..2b395552680 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -66,7 +66,8 @@ var/global/list/socks_t = list( "White normal" = "white_norm", "White short" = "white_short", "White knee" = "white_knee", "White thigh" = "white_thigh", "Black normal" = "black_norm", "Black short" = "black_short", "Black knee" = "black_knee", "Black thigh" = "black_thigh", "Thin knee" = "thin_knee", - "Thin thigh" = "thin_thigh", "Pantyhose" = "pantyhose", "None") + "Thin thigh" = "thin_thigh", "Pantyhose" = "pantyhose", "Striped thigh" = "striped_thigh", + "Striped knee" = "striped_knee", "Rainbow knee" = "rainbow_knee", "Rainbow thigh" = "rainbow_thigh", "None") //Backpacks var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt") diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index ea4a7c5b73e..bd6f014e0b5 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -962,7 +962,7 @@ proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0) var/old_underlays = T.underlays.Copy() if(platingRequired) - if(istype(B, get_base_turf(B.z))) + if(istype(B, get_base_turf_by_area(B))) continue moving var/turf/X = B diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index aba57b47a27..0c88772aebd 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -100,6 +100,7 @@ var/list/gamemode_cache = list() var/forumurl var/githuburl var/rulesurl + var/mapurl //Alert level description var/alert_desc_green = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced." @@ -418,6 +419,12 @@ var/list/gamemode_cache = list() if ("forumurl") config.forumurl = value + if ("rulesurl") + config.rulesurl = value + + if ("mapurl") + config.mapurl = value + if ("githuburl") config.githuburl = value if ("guest_jobban") diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index ac5ebee68c3..5c86505da7e 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -166,7 +166,7 @@ if(H.mind && !player_is_antag(H.mind, only_offstation_roles = 1)) var/assignment = GetAssignment(H) - var/id = add_zero(num2hex(rand(1, 1.6777215E7)), 6) //this was the best they could come up with? A large random number? *sigh* + var/id = add_zero(num2hex(rand(1, 65535)), 4) //no point generating higher numbers because of the limitations of num2hex //General Record var/datum/data/record/G = CreateGeneralRecord(H, id) G.fields["name"] = H.real_name @@ -395,7 +395,7 @@ proc/get_id_photo(var/mob/living/carbon/human/H, var/assigned_role) side = new(get_id_photo(dummy), dir = WEST) qdel(dummy) - if(!id) id = text("[]", add_zero(num2hex(rand(1, 1.6777215E7)), 6)) + if(!id) id = text("[]", add_zero(num2hex(rand(1, 65536)), 4)) var/datum/data/record/G = new /datum/data/record() G.name = "Employee Record #[id]" G.fields["name"] = "New Record" diff --git a/code/datums/wires/radio.dm b/code/datums/wires/radio.dm index 9da8890ac32..ab6053da832 100644 --- a/code/datums/wires/radio.dm +++ b/code/datums/wires/radio.dm @@ -12,11 +12,6 @@ var/const/WIRE_TRANSMIT = 4 return 1 return 0 -/datum/wires/radio/Interact(var/mob/living/user) - if(CanUse(user)) - var/obj/item/device/radio/R = holder - R.interact(user) - /datum/wires/radio/UpdatePulsed(var/index) var/obj/item/device/radio/R = holder switch(index) @@ -29,6 +24,7 @@ var/const/WIRE_TRANSMIT = 4 if(WIRE_TRANSMIT) R.broadcasting = !R.broadcasting && !IsIndexCut(WIRE_SIGNAL) + nanomanager.update_uis(holder) /datum/wires/radio/UpdateCut(var/index, var/mended) var/obj/item/device/radio/R = holder @@ -42,3 +38,4 @@ var/const/WIRE_TRANSMIT = 4 if(WIRE_TRANSMIT) R.broadcasting = mended && !IsIndexCut(WIRE_SIGNAL) + nanomanager.update_uis(holder) diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index cd288a31287..60dc87b2eed 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -147,6 +147,7 @@ area/space/atmosalert() requires_power = 0 flags = RAD_SHIELDED sound_env = SMALL_ENCLOSED + base_turf = /turf/space /area/shuttle/arrival name = "\improper Arrival Shuttle" @@ -1025,14 +1026,17 @@ area/space/atmosalert() /area/hallway/secondary/entry/D1 name = "\improper Shuttle Dock Hallway - Dock One" icon_state = "entry_D1" + base_turf = /turf/space /area/hallway/secondary/entry/D2 name = "\improper Shuttle Dock Hallway - Dock Two" icon_state = "entry_D2" + base_turf = /turf/space /area/hallway/secondary/entry/D3 name = "\improper Shuttle Dock Hallway - Dock Three" icon_state = "entry_D3" + base_turf = /turf/space /area/hallway/secondary/entry/D4 name = "\improper Shuttle Dock Hallway - Dock Four" @@ -1603,6 +1607,7 @@ area/space/atmosalert() requires_power = 1 always_unpowered = 1 lighting_use_dynamic = 0 + base_turf = /turf/space auxport name = "\improper Fore Port Solar Array" @@ -2285,6 +2290,7 @@ area/space/atmosalert() /area/shuttle/constructionsite name = "\improper Construction Site Shuttle" icon_state = "yellow" + lighting_use_dynamic = 0 /area/shuttle/constructionsite/station name = "\improper Construction Site Shuttle" diff --git a/code/game/gamemodes/cult/narsie.dm b/code/game/gamemodes/cult/narsie.dm index 87d921a3082..a3248a07e5b 100644 --- a/code/game/gamemodes/cult/narsie.dm +++ b/code/game/gamemodes/cult/narsie.dm @@ -223,9 +223,9 @@ var/global/list/narsie_list = list() consume(AM2) continue - if (dist <= consume_range && !istype(A, get_base_turf(A.z))) + if (dist <= consume_range && !istype(A, get_base_turf_by_area(A))) var/turf/T2 = A - T2.ChangeTurf(get_base_turf(A.z)) + T2.ChangeTurf(get_base_turf_by_area(A)) /obj/singularity/narsie/consume(const/atom/A) //This one is for the small ones. if(!(A.singuloCanEat())) @@ -265,9 +265,9 @@ var/global/list/narsie_list = list() spawn (0) AM2.singularity_pull(src, src.current_size) - if (dist <= consume_range && !istype(A, get_base_turf(A.z))) + if (dist <= consume_range && !istype(A, get_base_turf_by_area(A))) var/turf/T2 = A - T2.ChangeTurf(get_base_turf(A.z)) + T2.ChangeTurf(get_base_turf_by_area(A)) /obj/singularity/narsie/ex_act(severity) //No throwing bombs at it either. --NEO return diff --git a/code/game/gamemodes/events/black_hole.dm b/code/game/gamemodes/events/black_hole.dm index 9c212ae0c51..a8b0071f657 100644 --- a/code/game/gamemodes/events/black_hole.dm +++ b/code/game/gamemodes/events/black_hole.dm @@ -24,7 +24,7 @@ qdel(M) for(var/obj/O in orange(1,src)) qdel(O) - var/base_turf = get_base_turf(src.z) + var/base_turf = get_base_turf_by_area(src) for(var/turf/simulated/ST in orange(1,src)) if(ST.type == base_turf) continue @@ -87,6 +87,6 @@ //Destroying the turf if( T && istype(T,/turf/simulated) && prob(turf_removal_chance) ) var/turf/simulated/ST = T - var/base_turf = get_base_turf(src.z) + var/base_turf = get_base_turf_by_area(src) if(ST.type != base_turf) ST.ChangeTurf(base_turf) diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index 0f1b42b73c4..0b3121779f3 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -100,6 +100,9 @@ var/global/list/engineering_networks = list( /obj/machinery/camera/xray icon_state = "xraycam" // Thanks to Krutchen for the icons. +/obj/machinery/camera/xray/command + network = list(NETWORK_COMMAND) + /obj/machinery/camera/xray/security network = list(NETWORK_SECURITY) diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index 35cd31399ef..d9d5f4707be 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -96,15 +96,6 @@ text = replacetext(text, "\n", "
") return text - -/obj/machinery/computer/attack_ghost(user as mob) - return src.attack_hand(user) - -/obj/machinery/computer/attack_hand(user as mob) - /* Observers can view computers, but not actually use them via Topic*/ - if(istype(user, /mob/dead/observer)) return 0 - return ..() - /obj/machinery/computer/attackby(I as obj, user as mob) if(istype(I, /obj/item/weapon/screwdriver) && circuit) playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index cbb57283540..9eeec021bf6 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -40,14 +40,14 @@ allow_items = 0 /obj/machinery/computer/cryopod/dorms - name = "residental oversight console" - desc = "An interface between visitors and the residental oversight systems tasked with keeping track of all visitors in the deeper section of the colony." + name = "residential oversight console" + desc = "An interface between visitors and the residential oversight systems tasked with keeping track of all visitors in the deeper section of the colony." icon = 'icons/obj/robot_storage.dmi' //placeholder icon_state = "console" //placeholder circuit = "/obj/item/weapon/circuitboard/robotstoragecontrol" storage_type = "visitors" - storage_name = "Residental Oversight Control" + storage_name = "Residential Oversight Control" allow_items = 1 /obj/machinery/computer/cryopod/travel @@ -173,7 +173,7 @@ origin_tech = list(TECH_DATA = 3) /obj/item/weapon/circuitboard/dormscontrol - name = "Circuit board (Residental Oversight Console)" + name = "Circuit board (Residential Oversight Console)" build_path = "/obj/machinery/computer/cryopod/door/dorms" origin_tech = list(TECH_DATA = 3) @@ -271,13 +271,13 @@ disallow_occupant_types = list(/mob/living/silicon/robot/drone) /obj/machinery/cryopod/robot/door/dorms - name = "Residental District Elevator" + name = "Residential District Elevator" desc = "A small elevator that goes down to the deeper section of the colony." - on_store_message = "has departed for the residental district." - on_store_name = "Residental Oversight" - on_enter_occupant_message = "The elevator door closes slowly, ready to bring you down to the residental district." + on_store_message = "has departed for the residential district." + on_store_name = "Residential Oversight" + on_enter_occupant_message = "The elevator door closes slowly, ready to bring you down to the residential district." on_store_visible_message_1 = "makes a ding as it moves" - on_store_visible_message_2 = "to the residental district." + on_store_visible_message_2 = "to the residential district." /obj/machinery/cryopod/robot/door/travel name = "Passenger Elevator" diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index 63336d5dfd9..d5d03749bee 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -10,10 +10,6 @@ var/one_time_use = 0 //Used for one-time-use teleport cards (such as clown planet coordinates.) //Setting this to 1 will set src.locked to null after a player enters the portal and will not allow hand-teles to open portals to that location. -/* Ghosts can't use this */ -/obj/machinery/computer/teleporter/attack_ghost(user as mob) - return 1 - /obj/machinery/computer/teleporter/New() src.id = "[rand(1000, 9999)]" ..() diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm index 7139a52cb39..8dbefe9a55e 100644 --- a/code/game/mecha/equipment/tools/tools.dm +++ b/code/game/mecha/equipment/tools/tools.dm @@ -284,7 +284,7 @@ if(do_after_cooldown(target)) if(disabled) return chassis.spark_system.start() - target:ChangeTurf(get_base_turf(target.z)) + target:ChangeTurf(get_base_turf_by_area(target)) playsound(target, 'sound/items/Deconstruct.ogg', 50, 1) chassis.use_power(energy_drain) else if (istype(target, /obj/machinery/door/airlock)) @@ -297,7 +297,7 @@ playsound(target, 'sound/items/Deconstruct.ogg', 50, 1) chassis.use_power(energy_drain) if(1) - if(istype(target, /turf/space) || istype(target,get_base_turf(target.z))) + if(istype(target, /turf/space) || istype(target,get_base_turf_by_area(target))) occupant_message("Building Floor...") set_ready_state(0) if(do_after_cooldown(target)) diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm new file mode 100644 index 00000000000..23cc51b518f --- /dev/null +++ b/code/game/objects/items/devices/communicator/communicator.dm @@ -0,0 +1,351 @@ +var/global/list/obj/item/device/communicator/all_communicators = list() + +/obj/item/device/communicator + name = "communicator" + desc = "A personal device used to enable long range dialog between two people, utilizing existing telecommunications infrastructure to allow \ + communications across different stations, planets, or even star systems." + icon = 'icons/obj/device.dmi' + icon_state = "communicator" + w_class = 2.0 + slot_flags = SLOT_ID | SLOT_BELT + + origin_tech = list(TECH_ENGINEERING = 2, TECH_MAGNET = 2, TECH_BLUESPACE = 2, TECH_DATA = 2) + matter = list(DEFAULT_WALL_MATERIAL = 30,"glass" = 10) + + var/mob/living/voice/voice_mob = null + var/list/voice_requests = list() + var/owner = null + var/owner_job = null + var/owner_rank = null //Is this different from job? + var/alert_called = 0 + var/obj/machinery/message_server/server = null //Reference to the PDA server, to avoid having to look it up so often. + +/obj/item/device/communicator/New() + ..() + all_communicators += src + all_communicators = sortAtom(all_communicators) + server = get_message_server() + //This is a pretty terrible way of doing this. + spawn(20) //Wait for our mob to finish spawning. + if(ismob(loc)) + register_device(loc) + else if(istype(loc, /obj/item/weapon/storage)) + var/obj/item/weapon/storage/S = loc + if(ismob(S.loc)) + register_device(S.loc) + +/obj/item/device/communicator/examine(mob/user) + if(!..(user)) + return + + var/msg = "" + if(voice_mob) + msg += "On the screen, you can see a image feed of [voice_mob].\n" + msg += "" + + if(src.voice_mob && src.voice_mob.key) + switch(src.voice_mob.stat) + if(CONSCIOUS) + if(!src.voice_mob.client) + msg += "[voice_mob] appears to be asleep.\n" //afk + if(UNCONSCIOUS) + msg += "[voice_mob] doesn't appear to be conscious.\n" + if(DEAD) + msg += "[voice_mob] appears to have died...\n" //Hopefully this never has to be used. + else + msg += "The device doesn't appear to be transmitting any data.\n" + msg += "" + user << msg + return + +//This is pretty lengthy +/obj/item/device/communicator/attack_self(mob/user) + if(!owner) + var/choice = alert(user,"Would you like to register [src]?","Communicator","Yes","No") + if(choice == "Yes") + register_device(user) + return + + if(voice_mob) + var/choice = alert(user,"Would you like to hang up?","Communicator","No","Yes") + if(choice == "Yes") + close_connection(user, "[user] hung up") + return + + if(!server || !server.active) + user << "\icon[src] Error: Cannot establish connection to telecommunications." + sleep(30) + user << "\icon[src] Attempting to connect to telecommunications." + sleep(30) + if(!server) + server = get_message_server() + if(!server) + user << "\icon[src] Query to telecommunications timed out." + else if(server.active) + user << "\icon[src] Connection re-established." + else + if(server.active) + user << "\icon[src] Connection re-established." + else + user << "\icon[src] Query to telecommunications timed out." + + return + + var/mode = alert(user,"Would you like to check for communications requests or attempt to send a communication request to an external device?", + "Communicator","Check Requests","Send Request", "Cancel") + switch(mode) + if("Check Requests") + if(!voice_requests.len) + user << "\icon[src] No incoming communications requests." + return + + else + alert_called = 0 + update_icon() + var/choice = input(user,"Would would you like to talk to?","Communicator") as null|anything in voice_requests + if(choice) + open_connection(user, choice) + return + + if("Send Request") + var/list/potential_candidates = list() + for(var/mob/dead/observer/O in dead_mob_list) //To my knowledge, there isn't a list of all ghosts. + if(O.client) //We only want logged in ghosts. + potential_candidates.Add(O.client.prefs.real_name) //This is needed so you don't see ghosted maint drones on the list. + + if(!potential_candidates.len) + user << "\icon[src] Unfortunately, no other communicators outside your local area could be found. Please try again later." + return + + var/choice = input(src,"Please select a person to call. Due to the vast distances involved \ + as well as time-zones, please note that your request may take a long time to be noticed, and \ + that the available people to call may change as time passes.") as null|anything in potential_candidates + if(choice) + var/mob/dead/observer/boo = null + for(var/mob/dead/observer/O in dead_mob_list) + if(O.client && O.client.prefs.real_name == choice) + boo = O + break + boo << "\icon[src] A communications request has been received from [src]. If you would like to answer, please use the Call Communicator verb." + user << "\icon[src] A communications request has been sent to [choice]. Now you need to wait until they answer, if they do." + return + +/obj/item/device/communicator/proc/register_device(mob/user, var/job = null) + if(!user) + return + owner = user.name + if(job) + owner_job = job + owner_rank = job + else + if(ishuman(user)) + var/mob/living/carbon/human/H = user + var/obj/item/weapon/card/id/idcard = H.wear_id + if(idcard) + owner_job = idcard.assignment + owner_rank = idcard.rank + else + user << "\icon[src] ID not found. Please swipe your ID on the device." + return + else //They're probably a synth. + owner_job = user.job + owner_rank = user.job + + name = "\improper [initial(name)]-[owner] ([owner_job])" + +/* + owner = idcard.registered_name + owner_job = idcard.assignment + owner_rank = idcard.rank + name = "\improper communicator-[owner] ([ownjob])" +*/ + +/obj/item/device/communicator/proc/open_connection(mob/user, var/mob/candidate) + if(!isobserver(candidate)) + return + + //Handle moving the ghost into the new shell. + announce_ghost_joinleave(candidate, 0, "They are occupying a personal communications device now.") + voice_mob = new /mob/living/voice(src) //Make the voice mob the ghost is going to be. + voice_mob.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences. + //Do some simple logging since this is a tad risky as a concept. + var/msg = "[candidate.client ? "[candidate.client.key]" : "*no key*"] ([candidate]) has entered [src], triggered by \ + [user.client ? "[user.client.key]" : "*no key*"] ([user]) at [x],[y],[z]. They have joined as [voice_mob.name]." + message_admins(msg) + log_game(msg) + voice_mob.mind = candidate.mind //Transfer the mind, if any. + voice_mob.ckey = candidate.ckey //Finally, bring the client over. + + var/obj/screen/blackness = new() //Makes a black screen, so the candidate can't see what's going on before actually 'connecting' to the communicator. + blackness.screen_loc = ui_entire_screen + blackness.icon = 'icons/effects/effects.dmi' + blackness.icon_state = "1" + blackness.mouse_opacity = 2 //Can't see anything! + voice_mob.client.screen.Add(blackness) + + update_icon() + + //Now for some connection fluff. + user << "\icon[src] Connecting to [candidate]." + src.voice_mob << "\icon[src] Attempting to call [src]." + sleep(10) + src.voice_mob << "\icon[src] Dialing to [station_name()], Kara Subsystem, [system_name()]." + sleep(20) + src.voice_mob << "\icon[src] Connecting to [station_name()] telecommunications array." + sleep(40) + src.voice_mob << "\icon[src] Connection to [station_name()] telecommunications array established. Redirecting signal to [src]." + sleep(20) + + //We're connected, no need to hide everything. + voice_mob.client.screen.Remove(blackness) + qdel(blackness) + + voice_mob.stable_connection = 1 + voice_mob << "\icon[src] Connection to [src] established." + voice_mob << "To talk to the person on the other end of the call, just talk normally." + voice_mob << "If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time." + voice_mob << "Remember, your character does not know anything you've learned from observing!" + if(voice_mob.mind) + voice_mob.mind.assigned_role = "Disembodied Voice" + user << "\icon[src] Your communicator is now connected to [candidate]'s communicator." + +/obj/item/device/communicator/proc/close_connection(mob/user, var/reason) + if(!voice_mob) + return + if(user != voice_mob) + user << "\icon[src] [reason]." + voice_mob << "\icon[src] [reason]." + else + if(ismob(loc)) + user << "\icon[src] [reason]." + loc << "\icon[src] [reason]." + qdel(voice_mob) + + update_icon() + +/obj/item/device/communicator/proc/request(var/mob/candidate) + if(!isobserver(candidate)) + return + if(candidate in voice_requests) + return + voice_requests.Add(candidate) + + playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) + for (var/mob/O in hearers(2, loc)) + O.show_message(text("\icon[src] *beep*")) + + alert_called = 1 + update_icon() + + //Search for holder of the device. + var/mob/living/L = null + if(loc && isliving(loc)) + L = loc + + if(L) + L << "\icon[src] Communications request from [candidate]." + +/obj/item/device/communicator/Destroy() + if(voice_mob) + voice_mob << "\icon[src] Connection timed out with remote host." + qdel(voice_mob) + all_communicators -= src + ..() + +/obj/item/device/communicator/update_icon() + if(voice_mob) + icon_state = "communicator-active" + return + + if(alert_called) + icon_state = "communicator-called" + return + + icon_state = initial(icon_state) + +/obj/item/device/communicator/see_emote(mob/living/M, text) + if(voice_mob && voice_mob.client) + if(!voice_mob.stable_connection) + return 0 + var/rendered = "[text]" + voice_mob.show_message(rendered, 2) + ..() + +/obj/item/device/communicator/show_message(msg, type, alt, alt_type) + if(voice_mob && voice_mob.client) + if(!voice_mob.stable_connection) + return 0 + var/rendered = "[msg]" + voice_mob.show_message(rendered, type) + ..() + +/obj/item/device/communicator/attackby(obj/item/C as obj, mob/user as mob) + if(istype(C, /obj/item/weapon/card/id)) + var/obj/item/weapon/card/id/idcard = C + if(!idcard.registered_name) + user << "\icon[src] \The [src] rejects the ID." + return + if(!owner) + owner = idcard.registered_name + owner_job = idcard.assignment + owner_rank = idcard.rank + name = "\improper communicator-[owner] ([owner_job])" + user << "\icon[src] Card scanned." + else + ..() + +/mob/dead/verb/join_as_voice() + set category = "Ghost" + set name = "Call Communicator" + set desc = "If there is a communicator available, send a request to speak through it. This will reset your respawn timer, if someone picks up." + + if(ticker.current_state < GAME_STATE_PLAYING) + src << "The game hasn't started yet!" + return + + if (!src.stat) + return + + if (usr != src) + return //something is terribly wrong + + var/confirm = alert(src, "Would you like to talk as [src.client.prefs.real_name], over a communicator? \ + This will reset your respawn timer, if someone answers.", "Join as Voice?", "Yes","No") + if(confirm == "No") + return + + for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. + if(src.client.prefs.real_name == L.real_name) + src << "Your identity is already present in the game world. Please load in a different character first." + return + + if(!all_communicators.len) + src << "There are no available communicators, sorry." + return + + if(!get_message_server()) + src << "The messaging server or telecommunications is down at the moment, so your call can't go through." + return + + var/choice = input(src,"Send a voice request to whom?") as null|anything in all_communicators + if(choice) + var/obj/item/device/communicator/chosen_communicator = choice + chosen_communicator.request(src) + + src << "A communications request has been sent to [chosen_communicator]. Now you need to wait until someone answers." + +/obj/item/device/communicator/integrated //For synths who have no hands. + name = "integrated communicator" + desc = "A circuit used for long-range communications, able to be integrated into a system." + +/obj/item/device/communicator/integrated/verb/activate() + set category = "AI IM" + set name = "Use Communicator" + set desc = "Utilizes your built-in communicator." + set src in usr + + if(usr.stat == 2) + usr << "You can't do that because you are dead!" + return + + src.attack_self(usr) \ No newline at end of file diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 9d7bcf88127..ed8cf4ce0bc 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -33,10 +33,12 @@ /obj/item/device/radio/intercom/department/medbay name = "station intercom (Medbay)" + icon_state = "secintercom" frequency = MED_I_FREQ /obj/item/device/radio/intercom/department/security name = "station intercom (Security)" + icon_state = "medintercom" frequency = SEC_I_FREQ /obj/item/device/radio/intercom/New() @@ -110,7 +112,7 @@ if(!on) icon_state = "intercom-p" else - icon_state = "intercom" + icon_state = initial(icon_state) /obj/item/device/radio/intercom/locked var/locked_frequency diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 0c5b1c4231b..affde77733b 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -82,9 +82,6 @@ var/global/list/default_medbay_channels = list( for (var/ch_name in channels) secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) -/obj/item/device/radio/attack_ghost(mob/user) - return ui_interact(user) - /obj/item/device/radio/attack_self(mob/user as mob) user.set_machine(src) interact(user) diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index ed0f88ad8de..6ca739046f0 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -100,7 +100,7 @@ build_delay = 50 build_type = "airlock" build_other = /obj/machinery/door/airlock - else if(!deconstruct && (istype(T,/turf/space) || istype(T,get_base_turf(T.z)))) + else if(!deconstruct && (istype(T,/turf/space) || istype(T,get_base_turf_by_area(T)))) build_cost = 1 build_type = "floor" build_turf = /turf/simulated/floor/airless diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 5f3487a667c..e7c6508bb31 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -148,7 +148,7 @@ icon_state = "duffle" item_state = "duffle" slowdown = 1 - max_storage_space = 56 + max_storage_space = 38 storage_slots = 20 /obj/item/weapon/storage/backpack/dufflebag/syndie diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 800659307e8..3b73764c37b 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -39,7 +39,8 @@ return STATUS_CLOSE /mob/living/silicon/CanUseObjTopic(var/obj/O) - return O.allowed(src) + var/id = src.GetIdCard() + return O.check_access(id) /mob/proc/CanUseObjTopic() return 1 @@ -113,6 +114,9 @@ if(!ai_in_use && !is_in_use) in_use = 0 +/obj/attack_ghost(mob/user) + ui_interact(user) + /obj/proc/interact(mob/user) return diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm index 8e18901410d..588bded4d42 100644 --- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm @@ -20,10 +20,10 @@ new /obj/item/clothing/head/that(src) new /obj/item/device/radio/headset/headset_service(src) new /obj/item/device/radio/headset/headset_service(src) - new /obj/item/clothing/head/hairflower - new /obj/item/clothing/head/hairflower/pink - new /obj/item/clothing/head/hairflower/yellow - new /obj/item/clothing/head/hairflower/blue + new /obj/item/clothing/head/hairflower(src) + new /obj/item/clothing/head/hairflower/pink(src) + new /obj/item/clothing/head/hairflower/yellow(src) + new /obj/item/clothing/head/hairflower/blue(src) new /obj/item/clothing/under/sl_suit(src) new /obj/item/clothing/under/sl_suit(src) new /obj/item/clothing/under/rank/bartender(src) diff --git a/code/game/supplyshuttle.dm b/code/game/supplyshuttle.dm index 5825c32d484..e98d59528ff 100644 --- a/code/game/supplyshuttle.dm +++ b/code/game/supplyshuttle.dm @@ -31,11 +31,13 @@ var/list/mechtoys = list( name = "Supply Shuttle" icon_state = "shuttle3" requires_power = 0 + base_turf = /turf/space /area/supply/dock name = "Supply Shuttle" icon_state = "shuttle3" requires_power = 0 + base_turf = /turf/space /obj/structure/plasticflaps //HOW DO YOU CALL THOSE THINGS ANYWAY name = "\improper plastic flaps" diff --git a/code/game/turfs/simulated/floor_acts.dm b/code/game/turfs/simulated/floor_acts.dm index 21573970503..ee42c625fe5 100644 --- a/code/game/turfs/simulated/floor_acts.dm +++ b/code/game/turfs/simulated/floor_acts.dm @@ -2,14 +2,14 @@ //set src in oview(1) switch(severity) if(1.0) - src.ChangeTurf(get_base_turf(src.z)) + src.ChangeTurf(get_base_turf_by_area(src)) if(2.0) switch(pick(40;1,40;2,3)) if (1) if(prob(33)) new /obj/item/stack/material/steel(src) src.ReplaceWithLattice() if(2) - src.ChangeTurf(get_base_turf(src.z)) + src.ChangeTurf(get_base_turf_by_area(src)) if(3) if(prob(33)) new /obj/item/stack/material/steel(src) if(prob(80)) diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index f6aabc5ce60..10ffb3a8cc4 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -1,5 +1,5 @@ /turf/proc/ReplaceWithLattice() - src.ChangeTurf(get_base_turf(src.z)) + src.ChangeTurf(get_base_turf_by_area(src)) spawn() new /obj/structure/lattice( locate(src.x, src.y, src.z) ) diff --git a/code/modules/client/movement.dm b/code/modules/client/movement.dm new file mode 100644 index 00000000000..efcf51c02ee --- /dev/null +++ b/code/modules/client/movement.dm @@ -0,0 +1,15 @@ + +/client/New() + ..() + dir = NORTH + +/client/verb/spinleft() + set name = "Spin View CCW" + set category = "OOC" + dir = turn(dir, 90) + +/client/verb/spinright() + set name = "Spin View CW" + set category = "OOC" + dir = turn(dir, -90) + diff --git a/code/modules/client/preferences_gear.dm b/code/modules/client/preferences_gear.dm index c2d970b8cf3..3a2c8e3fa62 100644 --- a/code/modules/client/preferences_gear.dm +++ b/code/modules/client/preferences_gear.dm @@ -773,6 +773,13 @@ var/global/list/gear_datums = list() cost = 1 allowed_roles = list("Captain") +/datum/gear/corpdetsuit + display_name = "uniform, corporate (Detective)" + path = /obj/item/clothing/under/det/corporate + cost = 1 + slot = slot_w_uniform + allowed_roles = list("Detective","Head of Security") + /datum/gear/corpsecsuit display_name = "uniform, corporate (Security)" path = /obj/item/clothing/under/rank/security/corp @@ -1008,7 +1015,7 @@ var/global/list/gear_datums = list() /datum/gear/leather_coat display_name = "leather coat" - path = /obj/item/clothing/suit/leathercoat/alt + path = /obj/item/clothing/suit/leathercoat cost = 2 slot = slot_wear_suit @@ -1504,6 +1511,12 @@ var/global/list/gear_datums = list() sort_category = "utility" cost = 1 +/datum/gear/communicator + display_name = "personal communicator" + path = /obj/item/device/communicator + sort_category = "utility" + cost = 1 + /datum/gear/folder_blue display_name = "folder, blue" path = /obj/item/weapon/folder/blue diff --git a/code/modules/clothing/spacesuits/spacesuits.dm b/code/modules/clothing/spacesuits/spacesuits.dm index a5348767b8d..0e4d122bd22 100644 --- a/code/modules/clothing/spacesuits/spacesuits.dm +++ b/code/modules/clothing/spacesuits/spacesuits.dm @@ -7,14 +7,13 @@ icon_state = "space" desc = "A special helmet designed for work in a hazardous, low-pressure environment." item_flags = STOPPRESSUREDAMAGE | THICKMATERIAL | AIRTIGHT - flags_inv = BLOCKHAIR item_state_slots = list( slot_l_hand_str = "s_helmet", slot_r_hand_str = "s_helmet", ) permeability_coefficient = 0.01 armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 50) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR body_parts_covered = HEAD|FACE|EYES cold_protection = HEAD min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE diff --git a/code/modules/clothing/spacesuits/syndi.dm b/code/modules/clothing/spacesuits/syndi.dm index dd6af1597ee..9922483f2cf 100644 --- a/code/modules/clothing/spacesuits/syndi.dm +++ b/code/modules/clothing/spacesuits/syndi.dm @@ -122,7 +122,7 @@ name = "black medical space helmet" desc = "A black helmet sporting a medical cross and durable plating. Hopefully the wearer abides by space geneva." icon_state = "syndicate-helm-black-med" - item_state_slots = list(slot_head_str = "syndicate-black-med") + item_state_slots = list(slot_head_str = "syndicate-helm-black-med") /obj/item/clothing/suit/space/syndicate/black/med name = "black medical space suit" diff --git a/code/modules/clothing/spacesuits/void/void.dm b/code/modules/clothing/spacesuits/void/void.dm index 313f206948f..a6a20de09d4 100644 --- a/code/modules/clothing/spacesuits/void/void.dm +++ b/code/modules/clothing/spacesuits/void/void.dm @@ -8,7 +8,7 @@ armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20) max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE - flags_inv = HIDEEARS | BLOCKHAIR +// flags_inv = HIDEEARS|BLOCKHAIR //Species-specific stuff. species_restricted = list("Human") diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 4d1948baddd..f8373370734 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -212,18 +212,12 @@ //coats /obj/item/clothing/suit/leathercoat - name = "leather coat" - desc = "A long, thick black leather coat." - icon_state = "leathercoat" - item_state = "leathercoat" - -/obj/item/clothing/suit/leathercoat/alt name = "leather coat" desc = "A long, thick black leather coat." icon_state = "leathercoat_alt" item_state = "leathercoat_alt" -/obj/item/clothing/suit/leathercoat/alt/sec +/obj/item/clothing/suit/leathercoat/sec name = "leather coat" desc = "A long, thick black leather coat." icon_state = "leathercoat_sec" @@ -397,11 +391,8 @@ body_parts_covered = UPPER_TORSO|ARMS /obj/item/clothing/suit/storage/leather_jacket/alt - name = "leather jacket" - desc = "A black leather coat." - icon_state = "leather_jacket_alt" - item_state = "leather_jacket_alt" - body_parts_covered = UPPER_TORSO|ARMS + icon_state = "leatherjacket_alt" + item_state = "leatherjacket_alt" /obj/item/clothing/suit/storage/leather_jacket/nanotrasen desc = "A black leather coat. A corporate logo is proudly displayed on the back." @@ -409,7 +400,7 @@ //This one has buttons for some reason /obj/item/clothing/suit/storage/toggle/brown_jacket - name = "leather jacket" + name = "brown jacket" desc = "A brown leather coat." icon_state = "brown_jacket" item_state = "brown_jacket" diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index a44f1ef3fed..64d5c12048c 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -53,6 +53,7 @@ var/list/ai_verbs_default = list( var/viewalerts = 0 var/icon/holo_icon//Default is assigned when AI is created. var/obj/item/device/pda/ai/aiPDA = null + var/obj/item/device/communicator/aiCommunicator = null var/obj/item/device/multitool/aiMulti = null var/obj/item/device/radio/headset/heads/ai_integrated/aiRadio = null var/camera_light_on = 0 //Defines if the AI toggled the light on the camera it's looking through. @@ -114,6 +115,9 @@ var/list/ai_verbs_default = list( density = 1 loc = loc + aiCommunicator = new /obj/item/device/communicator/integrated(src) + + holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo1")) proc_holder_list = new() @@ -243,6 +247,9 @@ var/list/ai_verbs_default = list( aiPDA.owner = pickedName aiPDA.name = pickedName + " (" + aiPDA.ownjob + ")" + if(aiCommunicator) + aiCommunicator.register_device(src) + /* The AI Power supply is a dummy object used for powering the AI since only machinery should be using power. The alternative was to rewrite a bunch of AI code instead here we are. diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 0c53adc467c..7c588d0f786 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -15,6 +15,7 @@ var/userDNA // The DNA string of our assigned user var/obj/item/device/paicard/card // The card we inhabit var/obj/item/device/radio/radio // Our primary radio + var/obj/item/device/communicator/integrated/communicator // Our integrated communicator. var/chassis = "repairbot" // A record of your chosen chassis. var/global/list/possible_chassis = list( @@ -75,6 +76,7 @@ src.loc = paicard card = paicard sradio = new(src) + communicator = new(src) if(card) if(!card.radio) card.radio = new /obj/item/device/radio(src.card) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index f361e8f911c..93f9e409224 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -47,6 +47,7 @@ var/module_state_3 = null var/obj/item/device/radio/borg/radio = null + var/obj/item/device/communicator/integrated/communicator = null var/mob/living/silicon/ai/connected_ai = null var/obj/item/weapon/cell/cell = null var/obj/machinery/camera/camera = null @@ -110,6 +111,8 @@ updateicon() radio = new /obj/item/device/radio/borg(src) +// communicator = new /obj/item/device/communicator/integrated(src) +// communicator.register_device(src) common_radio = radio if(!scrambledcodes && !camera) @@ -199,6 +202,11 @@ rbPDA = new/obj/item/device/pda/ai(src) rbPDA.set_name_and_job(custom_name,"[modtype] [braintype]") +/mob/living/silicon/robot/proc/setup_communicator() + if (!communicator) + communicator = new/obj/item/device/communicator/integrated(src) + communicator.register_device(src, "[modtype] [braintype]") + //If there's an MMI in the robot, have it ejected when the mob goes away. --NEO //Improved /N /mob/living/silicon/robot/Destroy() @@ -279,6 +287,9 @@ // if we've changed our name, we also need to update the display name for our PDA setup_PDA() + // as well as our communicator registration + setup_communicator() + //We also need to update name of internal camera. if (camera) camera.c_tag = changed_name diff --git a/code/modules/mob/living/voice/voice.dm b/code/modules/mob/living/voice/voice.dm new file mode 100644 index 00000000000..e5ae40c5480 --- /dev/null +++ b/code/modules/mob/living/voice/voice.dm @@ -0,0 +1,94 @@ +//A very, very simple mob that exists inside other objects to talk and has zero influence on the world. +/mob/living/voice + name = "unknown" + desc = "How are you examining me?" +// default_language = "Galactic Common" + var/obj/item/device/communicator/comm = null + + emote_type = 2 //This lets them emote through containers. The communicator has a image feed of the person calling them so... + var/stable_connection = 0 //If set to zero, makes the mob effectively deaf, mute, and blind, due to interferance. + + +/mob/living/voice/New(loc) + add_language("Galactic Common") + for(var/datum/language/L in languages) //This is needed to get around some saycode problems. + if(L.name == "Galactic Common") + set_default_language(L) + break + + if(istype(loc, /obj/item/device/communicator)) + comm = loc + ..() + +/mob/living/voice/proc/transfer_identity(mob/speaker) + if(ismob(speaker)) + icon = speaker.icon + icon_state = speaker.icon_state + overlays = speaker.overlays + + alpha = 127 //Maybe we'll have hologram calls later. + if(speaker.client && speaker.client.prefs) + var/datum/preferences/p = speaker.client.prefs + name = p.real_name + real_name = name + gender = p.gender + + for(var/language in p.alternate_languages) + add_language(language) + +/mob/living/voice/Login() + var/obj/screen/static_effect = new() //Since what the player sees is essentially a video feed, from a vast distance away, the view isn't going to be perfect. + static_effect.screen_loc = ui_entire_screen + static_effect.icon = 'icons/effects/static.dmi' + static_effect.icon_state = "1 light" + static_effect.mouse_opacity = 0 //So the static doesn't get in the way of clicking. + client.screen.Add(static_effect) + +/mob/living/voice/Destroy() + comm = null + ..() + +/mob/living/voice/verb/hang_up() + set name = "Hang Up" + set category = "Communicator" + set desc = "Disconnects you from whoever you're talking to." + set src = usr + + if(comm) + comm.close_connection(src, "[src] hung up") + else + src << "You appear to not be inside a communicator. This is a bug and you should report it." + +/mob/living/voice/Life() + if(comm && comm.server && !comm.server.active) + comm.close_connection(src,"Connection to telecommunications array timed out") + ..() + +/mob/living/voice/say(var/message, var/datum/language/speaking = null, var/verb="says", var/alt_name="") +// world << "say() was called. Arguments: ([message], [speaking], [verb], [alt_name])." + if(!stable_connection) + return 0 + + //Speech bubbles. + if(comm) + var/speech_bubble_test = say_test(message) + var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]") + spawn(30) + qdel(speech_bubble) + + for(var/mob/M in hearers(world.view,comm)) //simplifed since it's just a speech bubble + M << speech_bubble + src << speech_bubble + + ..(message, speaking, verb, alt_name) //mob/living/say() can do the actual talking. +/* +/mob/verb/test_voice_mob() + set name = "Make Voice Mob" + set category = "Debug" + set desc = "For testing only!" + set src = usr + + var/turf/T = get_turf(src) + var/obj/item/device/communicator/comm = new(T) + comm.open_connection(src, src) +*/ diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index b9b2433b647..d26f5242624 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -733,8 +733,8 @@ if(istype(buckled, /obj/vehicle)) var/obj/vehicle/V = buckled if(cannot_stand()) - lying = 1 - canmove = 0 + lying = 0 + canmove = 1 pixel_y = V.mob_offset_y - 5 else if(buckled.buckle_lying != -1) lying = buckled.buckle_lying diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm index b4fcec3e98c..1d550014abe 100644 --- a/code/modules/mob/new_player/sprite_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories.dm @@ -206,6 +206,14 @@ name = "Bun" icon_state = "hair_bun" + bun2 + name = "Bun 2" + icon_state = "hair_bun2" + + bun3 + name = "Bun 3" + icon_state = "hair_bun3" + bowl name = "Bowl" icon_state = "hair_bowlcut" @@ -267,6 +275,14 @@ name = "Long Emo" icon_state = "hair_emolong" + fringeemo + name = "Emo Fringe" + icon_state = "hair_emofringe" + + veryshortovereyealternate + name = "Overeye Very Short, Alternate" + icon_state = "hair_veryshortovereyealternate" + veryshortovereye name = "Overeye Very Short" icon_state = "hair_veryshortovereye" diff --git a/code/modules/nano/interaction/base.dm b/code/modules/nano/interaction/base.dm index ccd9f3740b3..82fd32ea3c5 100644 --- a/code/modules/nano/interaction/base.dm +++ b/code/modules/nano/interaction/base.dm @@ -1,6 +1,9 @@ /datum/proc/nano_host() return src +/datum/proc/nano_container() + return src + /datum/proc/CanUseTopic(var/mob/user, var/datum/topic_state/state) var/src_object = nano_host() return state.can_use_topic(src_object, user) diff --git a/code/modules/nano/interaction/default.dm b/code/modules/nano/interaction/default.dm index ea6fc21e6bf..e682ea55288 100644 --- a/code/modules/nano/interaction/default.dm +++ b/code/modules/nano/interaction/default.dm @@ -9,10 +9,12 @@ /mob/proc/default_can_use_topic(var/src_object) return STATUS_CLOSE // By default no mob can do anything with NanoUI -/mob/dead/observer/default_can_use_topic() +/mob/dead/observer/default_can_use_topic(var/src_object) if(can_admin_interact()) - return STATUS_INTERACTIVE // Admins are more equal - return STATUS_UPDATE // Ghosts can view updates + return STATUS_INTERACTIVE // Admins are more equal + if(!client || get_dist(src_object, src) > client.view) // Preventing ghosts from having a million windows open by limiting to objects in range + return STATUS_CLOSE + return STATUS_UPDATE // Ghosts can view updates /mob/living/silicon/pai/default_can_use_topic(var/src_object) if((src_object == src || src_object == radio) && !stat) diff --git a/code/modules/nano/interaction/interactive.dm b/code/modules/nano/interaction/interactive.dm new file mode 100644 index 00000000000..ddd8f8f9e11 --- /dev/null +++ b/code/modules/nano/interaction/interactive.dm @@ -0,0 +1,7 @@ +/* + This state always returns STATUS_INTERACTIVE +*/ +/var/global/datum/topic_state/interactive/interactive_state = new() + +/datum/topic_state/interactive/can_use_topic(var/src_object, var/mob/user) + return STATUS_INTERACTIVE diff --git a/code/modules/nano/interaction/remote.dm b/code/modules/nano/interaction/remote.dm new file mode 100644 index 00000000000..ef2cb6926df --- /dev/null +++ b/code/modules/nano/interaction/remote.dm @@ -0,0 +1,39 @@ +/* + This state checks that user is capable, within range of the remoter, etc. and that src_object meets the basic requirements for interaction (being powered, non-broken, etc. + Whoever initializes this state is also responsible for deleting it properly. +*/ +/datum/topic_state/remote + var/datum/remoter + var/datum/remote_target + var/datum/topic_state/remoter_state + +/datum/topic_state/remote/New(var/remoter, var/remote_target, var/datum/topic_state/remoter_state = default_state) + src.remoter = remoter + src.remote_target = remote_target + src.remoter_state = remoter_state + ..() + +/datum/topic_state/remote/Destroy() + src.remoter = null + src.remoter_state = null + + // Force an UI update before we go, ensuring that any windows we may have opened for the remote target closes. + nanomanager.update_uis(remote_target.nano_container()) + remote_target = null + return ..() + +/datum/topic_state/remote/can_use_topic(var/datum/src_object, var/mob/user) + if(!(remoter && remoter_state)) // The remoter is gone, let us leave + return STATUS_CLOSE + + if(src_object != remote_target) + error("remote - Unexpected src_object: Expected '[remote_target]'/[remote_target.type], was '[src_object]'/[src_object.type]") + + // This checks if src_object is powered, etc. + // The interactive state is otherwise simplistic and only returns STATUS_INTERACTIVE and never checks distances, etc. + . = src_object.CanUseTopic(user, interactive_state) + if(. == STATUS_CLOSE) + return + + // This is the (generally) heavy checking, making sure the user is capable, within range of the remoter source, etc. + return min(., remoter.CanUseTopic(user, remoter_state)) diff --git a/code/modules/nano/modules/power_monitor.dm b/code/modules/nano/modules/power_monitor.dm index 9d386e8c932..94223fc4eff 100644 --- a/code/modules/nano/modules/power_monitor.dm +++ b/code/modules/nano/modules/power_monitor.dm @@ -47,7 +47,7 @@ // Allows us to process UI clicks, which are relayed in form of hrefs. /datum/nano_module/power_monitor/Topic(href, href_list) if(..()) - return + return 1 if( href_list["clear"] ) active_sensor = null if( href_list["refresh"] ) diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index e90a562fefd..c1e367fc98e 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -140,7 +140,8 @@ nanoui is used to open and update nano browser uis * @return nothing */ /datum/nanoui/proc/update_status(var/push_update = 0) - var/new_status = src_object.CanUseTopic(user, state) + var/obj/host = src_object.nano_host() + var/new_status = host.CanUseTopic(user, state) if(master_ui) new_status = min(new_status, master_ui.status) @@ -428,6 +429,9 @@ nanoui is used to open and update nano browser uis user << browse(null, "window=[window_id]") for(var/datum/nanoui/child in children) child.close() + children.Cut() + state = null + master_ui = null /** * Set the UI window to call the nanoclose verb when the window is closed diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 6d25a0f25e1..a062fd1910e 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -720,11 +720,6 @@ // do APC interaction src.interact(user) -/obj/machinery/power/apc/attack_ghost(user as mob) - if(stat & (BROKEN|MAINT)) - return - return ui_interact(user) - /obj/machinery/power/apc/interact(mob/user) if(!user) return @@ -749,7 +744,7 @@ "totalLoad" = round(lastused_total), "totalCharging" = round(lastused_charging), "coverLocked" = coverlocked, - "siliconUser" = istype(user, /mob/living/silicon), + "siliconUser" = issilicon(user) || isobserver(user), //I add observer here so admins can have more control, even if it makes 'siliconUser' seem inaccurate. "powerChannels" = list( list( @@ -822,11 +817,13 @@ /obj/machinery/power/apc/proc/can_use(mob/user as mob, var/loud = 0) //used by attack_hand() and Topic() + if(!user.client) + return 0 + if(isobserver(user) && is_admin(user) ) //This is to allow nanoUI interaction by ghost admins. + return 1 if (user.stat) user << "You must be conscious to use [src]!" return 0 - if(!user.client) - return 0 if(inoperable()) return 0 if(!user.IsAdvancedToolUser()) @@ -872,10 +869,15 @@ if(!can_use(usr, 1)) return 1 - if(!istype(usr, /mob/living/silicon) && locked) - // Shouldn't happen, this is here to prevent href exploits - usr << "You must unlock the panel to use this!" - return 1 + if(locked && !issilicon(usr) ) + if(isobserver(usr) ) + var/mob/dead/observer/O = usr //Added to allow admin nanoUI interactions. + if(!O.can_admin_interact() ) //NanoUI /should/ make this not needed, but better safe than sorry. + usr << "Try as you might, your ghostly fingers can't press the buttons." + return 1 + else + usr << "You must unlock the panel to use this!" + return 1 if (href_list["lock"]) coverlocked = !coverlocked diff --git a/code/modules/power/singularity/act.dm b/code/modules/power/singularity/act.dm index 157f0c5b480..23774d526ed 100644 --- a/code/modules/power/singularity/act.dm +++ b/code/modules/power/singularity/act.dm @@ -107,7 +107,7 @@ continue if(O.invisibility == 101) O.singularity_act(src, current_size) - ChangeTurf(get_base_turf(src.z)) + ChangeTurf(get_base_turf_by_area(src)) return 2 /turf/simulated/wall/singularity_pull(S, current_size) diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index d89ec2ae254..05dae4fdbfd 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -223,10 +223,7 @@ /obj/machinery/power/smes/attack_ai(mob/user) - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/power/smes/attack_ghost(mob/user) + add_hiddenprint(user) ui_interact(user) /obj/machinery/power/smes/attack_hand(mob/user) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 75c3666c845..733b086669b 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -309,6 +309,14 @@ return !P.launch_from_gun(target, user, src, target_zone, x_offset, y_offset) +//apart of reskins that have two sprites, touching may result in frustration and breaks +/obj/item/weapon/gun/projectile/colt/detective/attack_hand(var/mob/living/user) + if(!unique_reskin && loc == user) + reskin_gun(user) + return + ..() + + //Suicide handling. /obj/item/weapon/gun/var/mouthshoot = 0 //To stop people from suiciding twice... >.> /obj/item/weapon/gun/proc/handle_suicide(mob/living/user) @@ -389,5 +397,4 @@ return new_mode /obj/item/weapon/gun/attack_self(mob/user) - switch_firemodes(user) - + switch_firemodes(user) \ No newline at end of file diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index c3b92134b0d..38d3a1fcfd5 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -30,7 +30,7 @@ /obj/item/weapon/gun/projectile/automatic/c20r name = "submachine gun" - desc = "The C-20r is a lightweight and rapid firing SMG, for when you REALLY need someone dead. Uses 12mm rounds. Has a 'Scarborough Arms - Per falcis, per pravitas' buttstamp." + desc = "The C-20r is a lightweight and rapid firing SMG, for when you REALLY need someone dead. Uses 10mm rounds. Has a 'Scarborough Arms - Per falcis, per pravitas' buttstamp." icon_state = "c20r" item_state = "c20r" w_class = 3 diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index 09ef988dfd4..dcd5b450e4a 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -1,4 +1,5 @@ /obj/item/weapon/gun/projectile/colt + var/unique_reskin name = "vintage .45 pistol" desc = "A cheap Martian knock-off of a Colt M1911. Uses .45 rounds." magazine_type = /obj/item/ammo_magazine/c45m @@ -12,6 +13,18 @@ desc = "A Martian recreation of an old Terran pistol. Uses .45 rounds." magazine_type = /obj/item/ammo_magazine/c45m/rubber +/obj/item/weapon/gun/projectile/colt/detective/update_icon() + if(ammo_magazine) + if(unique_reskin) + icon_state = unique_reskin + else + icon_state = initial(icon_state) + else + if(unique_reskin) + icon_state = "[unique_reskin]-e" + else + icon_state = "[initial(icon_state)]-e" + /obj/item/weapon/gun/projectile/colt/detective/verb/rename_gun() set name = "Name Gun" set category = "Object" @@ -40,9 +53,14 @@ options["NT Mk. 58"] = "secguncomp" options["NT Mk. 58 Custom"] = "secgundark" options["Colt M1911"] = "colt" + options["USP"] = "usp" + options["H&K VP"] = "VP78" + options["P08 Luger"] = "p08" + options["P08 Luger, Brown"] = "p08b" var/choice = input(M,"What do you want to skin the gun to?","Reskin Gun") in options if(src && choice && !M.stat && in_range(M,src)) icon_state = options[choice] + unique_reskin = options[choice] M << "Your gun is now skinned as [choice]. Say hello to your new friend." return 1 @@ -56,6 +74,13 @@ fire_sound = 'sound/weapons/Gunshot_light.ogg' load_method = MAGAZINE +/obj/item/weapon/gun/projectile/sec/update_icon() + ..() + if(ammo_magazine) + icon_state = "secguncomp" + else + icon_state = "secguncomp-e" + /obj/item/weapon/gun/projectile/sec/flash name = ".45 signal pistol" magazine_type = /obj/item/ammo_magazine/c45m/flash @@ -65,6 +90,13 @@ name = "custom .45 Pistol" icon_state = "secgundark" +/obj/item/weapon/gun/projectile/sec/wood/update_icon() + ..() + if(ammo_magazine) + icon_state = "secgundark" + else + icon_state = "secgundark-e" + /obj/item/weapon/gun/projectile/silenced name = "silenced pistol" desc = "A small, quiet, easily concealable gun. Uses .45 rounds." diff --git a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm index 3d832bf6a28..d6ad4a750a5 100644 --- a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm @@ -73,6 +73,26 @@ qdel(src) return B +/obj/item/weapon/reagent_containers/food/drinks/bottle/verb/smash_bottle() + set name = "Smash Bottle" + set category = "Object" + + var/list/things_to_smash_on = list() + for(var/atom/A in range (1, usr)) + if(A.density && usr.Adjacent(A) && !istype(A, /mob)) + things_to_smash_on += A + + var/atom/choice = input("Select what you want to smash the bottle on.") as null|anything in things_to_smash_on + if(!choice) + return + if(!(choice.density && usr.Adjacent(choice))) + usr << "You must stay close to your target! You moved away from \the [choice]" + return + + usr.put_in_hands(src.smash(usr.loc, choice)) + usr.visible_message("\The [usr] smashed \the [src] on \the [choice]!") + usr << "You smash \the [src] on \the [choice]!" + /obj/item/weapon/reagent_containers/food/drinks/bottle/attackby(obj/item/W, mob/user) if(!rag && istype(W, /obj/item/weapon/reagent_containers/glass/rag)) insert_rag(W, user) @@ -452,6 +472,4 @@ center_of_mass = list("x"=16, "y"=10) New() ..() - reagents.add_reagent("ale", 30) - - + reagents.add_reagent("ale", 30) \ No newline at end of file diff --git a/html/changelog.html b/html/changelog.html index 897f98072d3..1c5a0a5c8e2 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,43 @@ -->
+

06 December 2015

+

Datraen updated:

+ +

HarpyEagle updated:

+ +

Hubblenaut updated:

+ +

MagmaRam updated:

+ +

Neerti updated:

+ +

PsiOmegaDelta updated:

+ +

26 September 2015

PsiOmegaDelta updated: