From 0fa23c964dad20afc01c74baea8f5cb7b4fdc680 Mon Sep 17 00:00:00 2001 From: covertcorvid <17987483+covertcorvid@users.noreply.github.com> Date: Wed, 19 Apr 2023 19:28:23 -0400 Subject: [PATCH 001/549] Enables Byond 515 --- code/__byond_version_compat.dm | 2 ++ code/_compile_options.dm | 6 +++--- dependencies.sh | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/code/__byond_version_compat.dm b/code/__byond_version_compat.dm index 4b79bdb8e68..9caf336854a 100644 --- a/code/__byond_version_compat.dm +++ b/code/__byond_version_compat.dm @@ -20,6 +20,8 @@ /savefile/byond_version = MIN_COMPILER_VERSION #endif +#define YES_I_WANT_515 + // Temporary 515 block until it is completely compatible. // AnturK says there are issues with savefiles that would make it dangerous to test merge, // and so this check is in place to stop serious damage. diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 885a8ed47d4..b8060da6f98 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -72,10 +72,10 @@ //End NSV //Update this whenever the byond version is stable so people stop updating to hilariously broken versions -#define MAX_COMPILER_VERSION 514 -#define MAX_COMPILER_BUILD 1589 +#define MAX_COMPILER_VERSION 515 +#define MAX_COMPILER_BUILD 1604 #if DM_VERSION > MAX_COMPILER_VERSION || DM_BUILD > MAX_COMPILER_BUILD -#warn WARNING: Your BYOND version is over the recommended version (514.1589)! Stability is not guaranteed. +#warn WARNING: Your BYOND version is over the recommended version (515.1604)! Stability is not guaranteed. #endif //Log the full sendmaps profile on 514.1556+, any earlier and we get bugs or it not existing #if DM_VERSION >= 514 && DM_BUILD >= 1556 diff --git a/dependencies.sh b/dependencies.sh index b4f1d0f2771..f2a3771cca7 100755 --- a/dependencies.sh +++ b/dependencies.sh @@ -4,8 +4,8 @@ #Final authority on what's required to fully build the project # byond version -export BYOND_MAJOR=514 -export BYOND_MINOR=1589 +export BYOND_MAJOR=515 +export BYOND_MINOR=1604 #rust version export RUST_VERSION=1.54.0 From bc7856ae2784d525b5d31a260464af7ee0017ec6 Mon Sep 17 00:00:00 2001 From: covertcorvid <17987483+covertcorvid@users.noreply.github.com> Date: Thu, 20 Apr 2023 20:07:53 -0400 Subject: [PATCH 002/549] Some things that stopped it from compiling in 515.1603 --- code/controllers/configuration/config_entry.dm | 2 +- code/game/objects/items/extinguisher.dm | 4 ++-- nsv13/code/modules/overmap/ai-skynet.dm | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/controllers/configuration/config_entry.dm b/code/controllers/configuration/config_entry.dm index a4a78852480..b8a4782be97 100644 --- a/code/controllers/configuration/config_entry.dm +++ b/code/controllers/configuration/config_entry.dm @@ -38,7 +38,7 @@ /datum/config_entry/can_vv_get(var_name) . = ..() - if(var_name == NAMEOF_STATIC(src, config_entry_value) || var_name == NAMEOF_STATIC(src, default)) + if(var_name == NAMEOF(src, config_entry_value) || var_name == NAMEOF(src, default)) . &= !(protection & CONFIG_ENTRY_HIDDEN) /datum/config_entry/vv_edit_var(var_name, var_value) diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm index e0ff8f815ae..dcfbfc98696 100644 --- a/code/game/objects/items/extinguisher.dm +++ b/code/game/objects/items/extinguisher.dm @@ -196,9 +196,9 @@ /obj/item/extinguisher/proc/manage_chair_speed(datum/move_loop/move/source) SIGNAL_HANDLER switch(source.lifetime) - if(5 to 4) + if(4 to 5) source.delay = 2 - if(3 to 1) + if(1 to 3) source.delay = 3 /obj/item/extinguisher/AltClick(mob/user) diff --git a/nsv13/code/modules/overmap/ai-skynet.dm b/nsv13/code/modules/overmap/ai-skynet.dm index 55c1f2d770e..45662029495 100644 --- a/nsv13/code/modules/overmap/ai-skynet.dm +++ b/nsv13/code/modules/overmap/ai-skynet.dm @@ -1978,9 +1978,9 @@ Seek a ship thich we'll station ourselves around switch(angular_difference) if(-15 to 15) boost(NORTH) //ZOOOM - if(-45 to -180) + if(-180 to -45) boost(WEST) - if(-180 to -INFINITY) + if(-INFINITY to -180) boost(EAST) if(45 to 180) boost(EAST) From be1affeb5133992aa033ede058b027be73c10380 Mon Sep 17 00:00:00 2001 From: Bobbanz1 <59128051+Bobbanz1@users.noreply.github.com> Date: Sat, 22 Apr 2023 23:52:09 +0200 Subject: [PATCH 003/549] [Upstream] Modular PDA Attempt Two - Electric Boogaloo! (#2250) Co-authored-by: covertcorvid <17987483+covertcorvid@users.noreply.github.com> --- _maps/RandomRuins/SpaceRuins/bigderelict1.dmm | 2 +- _maps/RandomZLevels/SnowCabin.dmm | 18 +- _maps/RandomZLevels/moonoutpost19.dmm | 4 +- _maps/RandomZLevels/undergroundoutpost45.dmm | 4 +- _maps/RuinGeneration/13x9_cratestorage.dmm | 2 +- _maps/map_files/Aetherwhisp/Aetherwhisp1.dmm | 30 +- _maps/map_files/Aetherwhisp/Aetherwhisp2.dmm | 38 +- _maps/map_files/Atlas/atlas.dmm | 6 +- _maps/map_files/Atlas/atlas2.dmm | 2 +- _maps/map_files/Eclipse/Eclipse1.dmm | 2 +- _maps/map_files/Galactica/Galactica1.dmm | 8 +- _maps/map_files/Galactica/Galactica2.dmm | 6 +- .../Galactica/old/Galactica2_old.dmm | 18 +- .../map_files/Galactica/old/Galactica_old.dmm | 8 +- _maps/map_files/Gladius/Gladius1.dmm | 18 +- _maps/map_files/Gladius/Gladius2.dmm | 20 +- _maps/map_files/Hammerhead/Hammerhead.dmm | 4 +- .../Instanced/map_files/Babylon2.dmm | 8 +- .../Instanced/map_files/SpaceSHIP.dmm | 2 +- .../map_files/Mining/nsv13/ruins/mining4.dmm | 4 +- _maps/map_files/Serendipity/Serendipity1.dmm | 2 +- _maps/map_files/Shrike/Shrike1.dmm | 2 +- _maps/map_files/Snake/snake_lower.dmm | 2 +- _maps/map_files/Snake/snake_upper.dmm | 6 +- _maps/map_files/Tycoon/Tycoon1.dmm | 2 +- _maps/map_files/Tycoon/Tycoon2.dmm | 272 ++-- _maps/map_files/Vago/vagodeck1.dmm | 6 +- _maps/map_files/Vago/vagodeck2.dmm | 8 +- _maps/map_files/generic/CentCom.dmm | 6 +- .../obj_signals/item_signals/item_signals.dm | 7 +- code/__DEFINES/devices.dm | 72 + code/__DEFINES/interaction_flags.dm | 1 + code/__DEFINES/machines.dm | 18 + code/__DEFINES/misc.dm | 3 - code/__HELPERS/cmp.dm | 8 +- code/__HELPERS/text.dm | 44 +- code/_globalvars/bitfields.dm | 5 +- code/_globalvars/lists/objects.dm | 1 + code/_onclick/hud/_defines.dm | 20 +- code/_onclick/hud/ai.dm | 51 +- code/_onclick/hud/pai.dm | 44 +- code/_onclick/hud/robot.dm | 8 +- code/_onclick/other_mobs.dm | 3 +- code/controllers/subsystem/blackbox.dm | 4 +- code/controllers/subsystem/traumas.dm | 2 +- code/datums/action.dm | 9 +- code/datums/components/uplink.dm | 13 +- code/datums/mind.dm | 4 +- code/game/gamemodes/sandbox/h_sandbox.dm | 2 +- code/game/machinery/PDApainter.dm | 22 +- code/game/machinery/airlock_cycle_control.dm | 2 +- code/game/machinery/camera/camera.dm | 19 +- code/game/machinery/computer/cloning.dm | 2 +- .../game/machinery/computer/communications.dm | 5 +- code/game/machinery/computer/security.dm | 12 +- code/game/machinery/doors/firedoor.dm | 2 +- code/game/machinery/navbeacon.dm | 2 +- code/game/machinery/newscaster.dm | 9 +- .../machinery/telecomms/computers/message.dm | 650 ++++----- .../telecomms/machines/message_server.dm | 66 +- code/game/mecha/mecha_defense.dm | 8 +- .../objects/effects/spawners/mailspawner.dm | 4 +- code/game/objects/items/cards_ids.dm | 12 +- code/game/objects/items/cigs_lighters.dm | 4 +- .../circuitboards/machine_circuitboards.dm | 2 +- code/game/objects/items/colorizers/items.dm | 4 +- code/game/objects/items/crayons.dm | 1 + code/game/objects/items/devices/PDA/PDA.dm | 1248 ----------------- .../objects/items/devices/PDA/PDA_types.dm | 273 ---- code/game/objects/items/devices/PDA/cart.dm | 700 --------- .../objects/items/devices/PDA/virus_cart.dm | 107 -- code/game/objects/items/devices/paicard.dm | 2 + code/game/objects/items/mop.dm | 5 + code/game/objects/items/stacks/telecrystal.dm | 4 +- code/game/objects/items/storage/backpack.dm | 2 +- code/game/objects/items/storage/boxes.dm | 26 +- .../game/objects/items/storage/uplink_kits.dm | 10 +- .../crates_lockers/closets/job_closets.dm | 2 +- .../closets/secure/engineering.dm | 2 +- .../crates_lockers/closets/secure/medical.dm | 2 +- .../closets/secure/scientist.dm | 2 +- .../crates_lockers/closets/secure/security.dm | 6 +- .../crates_lockers/closets/syndicate.dm | 2 +- code/game/objects/structures/displaycase.dm | 2 +- code/game/objects/structures/janicart.dm | 5 + code/modules/admin/admin_verbs.dm | 1 + code/modules/admin/verbs/debug.dm | 10 +- code/modules/admin/verbs/randomverbs.dm | 12 + .../traitor/equipment/contractor.dm | 2 +- code/modules/asset_cache/asset_list_items.dm | 9 + .../atmospherics/machinery/airalarm.dm | 2 +- code/modules/awaymissions/corpse.dm | 8 +- code/modules/cargo/exports/gear.dm | 2 +- code/modules/cargo/expressconsole.dm | 2 +- code/modules/cargo/packs.dm | 4 +- code/modules/clothing/chameleon.dm | 21 +- code/modules/clothing/outfits/ert.dm | 11 +- code/modules/clothing/outfits/standard.dm | 9 +- .../crew_objectives/civilian_objectives.dm | 4 +- code/modules/emoji/emoji_parse.dm | 1 - code/modules/jobs/job_types/_job.dm | 12 +- code/modules/jobs/job_types/assistant.dm | 2 +- .../jobs/job_types/atmospheric_technician.dm | 3 +- code/modules/jobs/job_types/bartender.dm | 2 +- code/modules/jobs/job_types/botanist.dm | 2 +- code/modules/jobs/job_types/brig_physician.dm | 2 +- code/modules/jobs/job_types/captain.dm | 4 +- .../jobs/job_types/cargo_technician.dm | 2 +- code/modules/jobs/job_types/chaplain.dm | 2 +- code/modules/jobs/job_types/chemist.dm | 2 +- code/modules/jobs/job_types/chief_engineer.dm | 5 +- .../jobs/job_types/chief_medical_officer.dm | 5 +- code/modules/jobs/job_types/clown.dm | 2 +- code/modules/jobs/job_types/cook.dm | 2 +- code/modules/jobs/job_types/curator.dm | 2 +- code/modules/jobs/job_types/deputy.dm | 2 +- code/modules/jobs/job_types/detective.dm | 2 +- .../jobs/job_types/exploration_team.dm | 4 +- code/modules/jobs/job_types/geneticist.dm | 2 +- code/modules/jobs/job_types/gimmick.dm | 8 +- .../jobs/job_types/head_of_personnel.dm | 6 +- .../jobs/job_types/head_of_security.dm | 8 +- code/modules/jobs/job_types/janitor.dm | 2 +- code/modules/jobs/job_types/lawyer.dm | 2 +- code/modules/jobs/job_types/medical_doctor.dm | 2 +- code/modules/jobs/job_types/mime.dm | 2 +- code/modules/jobs/job_types/paramedic.dm | 2 +- code/modules/jobs/job_types/quartermaster.dm | 2 +- .../jobs/job_types/research_director.dm | 5 +- code/modules/jobs/job_types/roboticist.dm | 3 +- code/modules/jobs/job_types/scientist.dm | 3 +- .../jobs/job_types/security_officer.dm | 4 +- code/modules/jobs/job_types/shaft_miner.dm | 2 +- .../jobs/job_types/station_engineer.dm | 3 +- code/modules/jobs/job_types/virologist.dm | 2 +- code/modules/jobs/job_types/warden.dm | 2 +- code/modules/mining/abandoned_crates.dm | 4 +- code/modules/mob/living/carbon/human/human.dm | 12 +- .../mob/living/carbon/human/human_helpers.dm | 13 +- .../mob/living/carbon/human/species.dm | 2 +- .../human/species_types/shadowpeople.dm | 13 +- code/modules/mob/living/living.dm | 7 +- code/modules/mob/living/silicon/ai/ai.dm | 15 +- code/modules/mob/living/silicon/pai/pai.dm | 21 +- .../mob/living/silicon/pai/pai_shell.dm | 4 - .../mob/living/silicon/pai/signaler.dm} | 1 + .../mob/living/silicon/pai/software.dm | 39 - .../modules/mob/living/silicon/robot/robot.dm | 52 +- code/modules/mob/living/silicon/silicon.dm | 62 +- .../mob/living/simple_animal/bot/bot.dm | 2 +- .../mob/living/simple_animal/bot/cleanbot.dm | 7 +- code/modules/mob/mob.dm | 25 +- .../computers/item/computer.dm | 364 ++++- .../computers/item/computer_components.dm | 22 +- .../computers/item/computer_ui.dm | 163 ++- .../computers/item/processor.dm | 5 +- .../computers/item/role_tablet_presets.dm | 286 ++++ .../computers/item/tablet.dm | 316 ++++- .../computers/item/tablet_presets.dm | 6 +- .../computers/machinery/modular_computer.dm | 2 - .../modular_computers/file_system/data.dm | 19 +- .../modular_computers/file_system/program.dm | 83 +- .../file_system/programs/airestorer.dm | 2 +- .../file_system/programs/alarm.dm | 2 +- .../file_system/programs/antagonist/emag.dm | 22 + .../programs/antagonist/revelation.dm | 2 +- .../file_system/programs/atmosscan.dm | 4 +- .../file_system/programs/borg_monitor.dm | 4 +- .../file_system/programs/borg_self_monitor.dm | 7 +- .../file_system/programs/card.dm | 42 +- .../file_system/programs/cargobounty.dm | 2 +- .../file_system/programs/configurator.dm | 42 +- .../file_system/programs/crewmanifest.dm | 2 +- .../file_system/programs/file_browser.dm | 13 +- .../file_system/programs/jobmanagement.dm | 2 +- .../file_system/programs/log_viewer.dm | 96 ++ .../file_system/programs/notepad.dm | 42 + .../file_system/programs/ntdownloader.dm | 4 +- .../file_system/programs/ntmessenger.dm | 424 ++++++ .../file_system/programs/ntmonitor.dm | 2 +- .../file_system/programs/ntnrc_client.dm | 27 +- .../file_system/programs/phys_scanner.dm | 116 ++ .../file_system/programs/portrait_printer.dm | 2 +- .../file_system/programs/powermonitor.dm | 4 +- .../file_system/programs/radar.dm | 56 +- .../file_system/programs/records.dm | 87 ++ .../file_system/programs/remote_airlock.dm | 52 + .../file_system/programs/robocontrol.dm | 33 +- .../file_system/programs/secureye.dm | 2 +- .../file_system/programs/signaller.dm | 2 +- .../file_system/programs/sm_monitor.dm | 4 +- .../file_system/programs/statusdisplay.dm | 69 + .../modular_computers/hardware/_hardware.dm | 5 + .../modular_computers/hardware/card_slot.dm | 20 +- .../modular_computers/hardware/hard_drive.dm | 70 +- .../modular_computers/hardware/identifier.dm | 13 + .../modular_computers/hardware/job_disk.dm | 234 ++++ .../hardware/network_card.dm | 10 +- .../modular_computers/hardware/virus_disk.dm | 105 ++ code/modules/paperwork/contract.dm | 10 +- code/modules/paperwork/paper.dm | 18 +- code/modules/photography/camera/camera.dm | 11 +- .../ruins/spaceruin_code/TheDerelict.dm | 2 +- .../objective_types/assassination.dm | 2 +- .../objective_types/vip_extraction.dm | 2 +- .../ruin_generator/mapping.dm | 6 +- code/modules/tgui/states/reverse_contained.dm | 18 + code/modules/tgui/tgui_input_emoji.dm | 147 ++ code/modules/tgui/tgui_input_pda_message.dm | 196 +++ code/modules/tgui/tgui_select_picture.dm | 88 ++ code/modules/uplink/uplink_items.dm | 12 +- code/modules/vehicles/pimpin_ride.dm | 3 +- code/modules/vending/assist.dm | 2 +- code/modules/vending/cartridge.dm | 26 - code/modules/vending/job_disk.dm | 36 + code/modules/vending/wardrobes.dm | 2 +- icons/obj/pda.dmi | Bin 20619 -> 21701 bytes nsv13.dme | 25 +- nsv13/code/modules/cargo/objective_cargo.dm | 2 +- .../modules/coffee/machine/coffeemaker.dm | 2 +- .../jobs/job_types/fighter_technician.dm | 2 +- .../jobs/job_types/marine/military_police.dm | 4 +- .../modules/jobs/job_types/master_at_arms.dm | 2 +- .../jobs/job_types/munitions_technician.dm | 2 +- .../modules/overmap/fighters/_fighters.dm | 2 +- .../overmap/fighters/modules/cargo_hold.dm | 2 +- nsv13/code/modules/power/reactor/rbmk.dm | 2 +- nsv13/code/modules/power/stormdrive.dm | 2 +- nsv13/code/modules/recycling/replicatorDS.dm | 4 +- .../modules/ship_missions/hail_computer.dm | 2 +- nsv13/icons/obj/pda.dmi | Bin 11050 -> 24269 bytes tgui/packages/tgui/components/TextArea.js | 8 + tgui/packages/tgui/index.js | 21 + tgui/packages/tgui/interfaces/EmagConsole.js | 17 + .../tgui/interfaces/EmojiInputModal.js | 143 ++ .../tgui/interfaces/MessageMonitor.js | 325 +++++ .../tgui/interfaces/NtosAirlockControl.js | 38 + .../tgui/interfaces/NtosConfiguration.js | 19 +- .../tgui/interfaces/NtosEmagConsole.js | 141 ++ .../packages/tgui/interfaces/NtosLogViewer.js | 90 ++ tgui/packages/tgui/interfaces/NtosMain.js | 120 +- .../packages/tgui/interfaces/NtosMessenger.js | 293 ++++ tgui/packages/tgui/interfaces/NtosNetChat.js | 3 +- tgui/packages/tgui/interfaces/NtosNotepad.js | 32 + .../tgui/interfaces/NtosPhysScanner.js | 40 + tgui/packages/tgui/interfaces/NtosRadar.js | 87 +- tgui/packages/tgui/interfaces/NtosRecords.js | 97 ++ .../tgui/interfaces/NtosRoboControl.js | 12 +- tgui/packages/tgui/interfaces/NtosStatus.js | 88 ++ .../packages/tgui/interfaces/PDAInputModal.js | 55 + .../tgui/interfaces/PictureSelectModal.js | 44 + tgui/packages/tgui/layouts/NtosWindow.js | 6 +- tgui/packages/tgui/layouts/Window.js | 4 +- .../tgui/styles/components/Section.scss | 6 + .../tgui/styles/themes-ntos/blue.scss | 7 + .../tgui/styles/themes-ntos/brown.scss | 7 + .../tgui/styles/themes-ntos/clown-pink.scss | 11 + .../tgui/styles/themes-ntos/clown-yellow.scss | 11 + .../tgui/styles/themes-ntos/dark.scss | 7 + .../tgui/styles/themes-ntos/default.scss | 5 + .../tgui/styles/themes-ntos/green.scss | 7 + .../tgui/styles/themes-ntos/grey.scss | 7 + .../tgui/styles/themes-ntos/hackerman.scss | 12 + .../tgui/styles/themes-ntos/light.scss | 12 + .../tgui/styles/themes-ntos/ntos-colors.scss | 47 + .../tgui/styles/themes-ntos/olive.scss | 7 + .../tgui/styles/themes-ntos/orange.scss | 7 + .../tgui/styles/themes-ntos/pink.scss | 7 + .../tgui/styles/themes-ntos/purple.scss | 7 + .../packages/tgui/styles/themes-ntos/red.scss | 7 + .../tgui/styles/themes-ntos/teal.scss | 7 + .../tgui/styles/themes-ntos/violet.scss | 7 + .../tgui/styles/themes-ntos/yellow.scss | 7 + .../tgui/styles/themes/hackerman.scss | 4 + .../packages/tgui/styles/themes/hackeros.scss | 62 + tgui/packages/tgui/styles/themes/retro.scss | 20 +- .../styles/themes/thinktronic-classic.scss | 198 +++ tools/UpdatePaths/pdas_to_tabs.txt | 65 + 278 files changed, 6420 insertions(+), 3881 deletions(-) create mode 100644 code/__DEFINES/devices.dm delete mode 100644 code/game/objects/items/devices/PDA/PDA.dm delete mode 100644 code/game/objects/items/devices/PDA/PDA_types.dm delete mode 100644 code/game/objects/items/devices/PDA/cart.dm delete mode 100644 code/game/objects/items/devices/PDA/virus_cart.dm rename code/{game/objects/items/devices/PDA/radio.dm => modules/mob/living/silicon/pai/signaler.dm} (96%) create mode 100644 code/modules/modular_computers/computers/item/role_tablet_presets.dm create mode 100644 code/modules/modular_computers/file_system/programs/antagonist/emag.dm create mode 100644 code/modules/modular_computers/file_system/programs/log_viewer.dm create mode 100644 code/modules/modular_computers/file_system/programs/notepad.dm create mode 100644 code/modules/modular_computers/file_system/programs/ntmessenger.dm create mode 100644 code/modules/modular_computers/file_system/programs/phys_scanner.dm create mode 100644 code/modules/modular_computers/file_system/programs/records.dm create mode 100644 code/modules/modular_computers/file_system/programs/remote_airlock.dm create mode 100644 code/modules/modular_computers/file_system/programs/statusdisplay.dm create mode 100644 code/modules/modular_computers/hardware/identifier.dm create mode 100644 code/modules/modular_computers/hardware/job_disk.dm create mode 100644 code/modules/modular_computers/hardware/virus_disk.dm create mode 100644 code/modules/tgui/states/reverse_contained.dm create mode 100644 code/modules/tgui/tgui_input_emoji.dm create mode 100644 code/modules/tgui/tgui_input_pda_message.dm create mode 100644 code/modules/tgui/tgui_select_picture.dm delete mode 100644 code/modules/vending/cartridge.dm create mode 100644 code/modules/vending/job_disk.dm create mode 100644 tgui/packages/tgui/interfaces/EmagConsole.js create mode 100644 tgui/packages/tgui/interfaces/EmojiInputModal.js create mode 100644 tgui/packages/tgui/interfaces/MessageMonitor.js create mode 100644 tgui/packages/tgui/interfaces/NtosAirlockControl.js create mode 100644 tgui/packages/tgui/interfaces/NtosEmagConsole.js create mode 100644 tgui/packages/tgui/interfaces/NtosLogViewer.js create mode 100644 tgui/packages/tgui/interfaces/NtosMessenger.js create mode 100644 tgui/packages/tgui/interfaces/NtosNotepad.js create mode 100644 tgui/packages/tgui/interfaces/NtosPhysScanner.js create mode 100644 tgui/packages/tgui/interfaces/NtosRecords.js create mode 100644 tgui/packages/tgui/interfaces/NtosStatus.js create mode 100644 tgui/packages/tgui/interfaces/PDAInputModal.js create mode 100644 tgui/packages/tgui/interfaces/PictureSelectModal.js create mode 100644 tgui/packages/tgui/styles/themes-ntos/blue.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/brown.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/clown-pink.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/clown-yellow.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/dark.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/default.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/green.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/grey.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/hackerman.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/light.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/ntos-colors.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/olive.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/orange.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/pink.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/purple.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/red.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/teal.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/violet.scss create mode 100644 tgui/packages/tgui/styles/themes-ntos/yellow.scss create mode 100644 tgui/packages/tgui/styles/themes/hackeros.scss create mode 100644 tgui/packages/tgui/styles/themes/thinktronic-classic.scss create mode 100644 tools/UpdatePaths/pdas_to_tabs.txt diff --git a/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm b/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm index 920252a99cb..c649b0fa1ca 100644 --- a/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm +++ b/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm @@ -2146,7 +2146,7 @@ /area/ruin/space/has_grav/derelictoutpost/cargostorage) "eC" = ( /obj/structure/closet/crate, -/obj/item/pda/clear, +/obj/item/modular_computer/tablet/pda/clear, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, /area/ruin/space/has_grav/derelictoutpost/cargostorage) diff --git a/_maps/RandomZLevels/SnowCabin.dmm b/_maps/RandomZLevels/SnowCabin.dmm index 2e828062320..e61092ce025 100644 --- a/_maps/RandomZLevels/SnowCabin.dmm +++ b/_maps/RandomZLevels/SnowCabin.dmm @@ -1128,12 +1128,11 @@ /area/awaymission/cabin/snowforest/sovietsurface) "dm" = ( /obj/structure/table/reinforced, -/obj/item/pda/syndicate{ - background_color = "#0039A6"; - default_cartridge = /obj/item/cartridge/virus/mime; +/obj/item/modular_computer/tablet/pda/syndicate{ + default_disk = /obj/item/computer_hardware/hard_drive/role/virus/mime; desc = "A portable microcomputer by Thinktronic Systems, LTD. Seems like it may have useful information on it."; - name = "soviet PDA"; - note = "TRANSLATED TO GALACTIC COMMON:
My partner has left to help those Nanotrasen fucks three days ago. They said that a distress signal came from down south and they had to check it out. How fucking long does it take to investigate a mining outpost? Either those Nanotrasen fuckers betrayed us or something really did go wrong. Either way, I'm leaving before this becomes an issue for me and anyone else here. That dumb idiot." + name = "soviet tablet"; + note = "TRANSLATED TO GALACTIC COMMON: My partner has left to help those Nanotrasen fucks three days ago. They said that a distress signal came from down south and they had to check it out. How fucking long does it take to investigate a mining outpost? Either those Nanotrasen fuckers betrayed us or something really did go wrong. Either way, I'm leaving before this becomes an issue for me and anyone else here. That dumb idiot." }, /turf/open/floor/plating/snowed/temperatre, /area/awaymission/cabin/snowforest/sovietsurface) @@ -5220,12 +5219,11 @@ /area/awaymission/cabin/caves) "nb" = ( /obj/effect/turf_decal/weather/snow, -/obj/item/pda/syndicate{ - background_color = "#0039A6"; - default_cartridge = /obj/item/cartridge/virus/clown; +/obj/item/modular_computer/tablet/pda/syndicate{ + default_disk = /obj/item/computer_hardware/hard_drive/role/virus/clown; desc = "A portable microcomputer by Thinktronic Systems, LTD. Seems like it may have useful information on it."; - name = "soviet PDA"; - note = "TRANSLATED TO GALACTIC COMMON:
DO NOT GO SOUTH." + name = "soviet tablet"; + note = "TRANSLATED TO GALACTIC COMMON: DO NOT GO SOUTH." }, /obj/effect/decal/remains/human{ color = "#72e4fa" diff --git a/_maps/RandomZLevels/moonoutpost19.dmm b/_maps/RandomZLevels/moonoutpost19.dmm index b0ad0d9c033..5eb28e2c9cd 100644 --- a/_maps/RandomZLevels/moonoutpost19.dmm +++ b/_maps/RandomZLevels/moonoutpost19.dmm @@ -3760,8 +3760,8 @@ /area/awaymission/moonoutpost19/research) "hr" = ( /obj/structure/table, -/obj/item/cartridge/signal/toxins, -/obj/item/cartridge/signal/toxins{ +/obj/item/computer_hardware/hard_drive/role/signal/toxins, +/obj/item/computer_hardware/hard_drive/role/signal/toxins{ pixel_x = -4; pixel_y = 2 }, diff --git a/_maps/RandomZLevels/undergroundoutpost45.dmm b/_maps/RandomZLevels/undergroundoutpost45.dmm index 5946555c8a7..cbd71caaad7 100644 --- a/_maps/RandomZLevels/undergroundoutpost45.dmm +++ b/_maps/RandomZLevels/undergroundoutpost45.dmm @@ -7002,8 +7002,8 @@ /area/awaymission/undergroundoutpost45/research) "nk" = ( /obj/structure/table, -/obj/item/cartridge/signal/toxins, -/obj/item/cartridge/signal/toxins{ +/obj/item/computer_hardware/hard_drive/role/signal/toxins, +/obj/item/computer_hardware/hard_drive/role/signal/toxins{ pixel_x = -4; pixel_y = 2 }, diff --git a/_maps/RuinGeneration/13x9_cratestorage.dmm b/_maps/RuinGeneration/13x9_cratestorage.dmm index 4ed21920631..2ad4746e664 100644 --- a/_maps/RuinGeneration/13x9_cratestorage.dmm +++ b/_maps/RuinGeneration/13x9_cratestorage.dmm @@ -35,7 +35,7 @@ "o" = ( /obj/effect/turf_decal/delivery, /obj/structure/closet/crate, -/obj/item/pda/clear, +/obj/item/modular_computer/tablet/pda/clear, /obj/effect/spawner/lootdrop/ruinloot/important, /turf/open/floor/plasteel, /area/ruin/unpowered) diff --git a/_maps/map_files/Aetherwhisp/Aetherwhisp1.dmm b/_maps/map_files/Aetherwhisp/Aetherwhisp1.dmm index b10d61a86dd..349f69990f7 100644 --- a/_maps/map_files/Aetherwhisp/Aetherwhisp1.dmm +++ b/_maps/map_files/Aetherwhisp/Aetherwhisp1.dmm @@ -6744,7 +6744,7 @@ dir = 8 }, /obj/item/clothing/head/beret/black, -/obj/item/cartridge/security, +/obj/item/computer_hardware/hard_drive/role/security, /obj/effect/spawner/lootdrop/gloves, /turf/open/floor/carpet/ship/red_carpet, /area/security/main/warroom) @@ -11280,7 +11280,7 @@ pixel_y = -1 }, /obj/item/stamp/law, -/obj/item/cartridge/lawyer{ +/obj/item/computer_hardware/hard_drive/role/lawyer{ pixel_x = 8; pixel_y = 1 }, @@ -12427,7 +12427,7 @@ pixel_x = -4 }, /obj/item/stack/package_wrap, -/obj/item/cartridge/security, +/obj/item/computer_hardware/hard_drive/role/security, /obj/item/dest_tagger{ pixel_x = 4; pixel_y = 3 @@ -13920,7 +13920,7 @@ }, /obj/item/reagent_containers/glass/beaker, /obj/machinery/airalarm/directional/north, -/obj/item/cartridge/chemistry, +/obj/item/computer_hardware/hard_drive/role/chemistry, /turf/open/floor/carpet/ship/blue, /area/medical/chemistry) "jcC" = ( @@ -14163,7 +14163,7 @@ dir = 8 }, /obj/item/clothing/head/beret/black, -/obj/item/cartridge/security, +/obj/item/computer_hardware/hard_drive/role/security, /obj/effect/spawner/lootdrop/gloves, /obj/structure/disposalpipe/segment{ dir = 4 @@ -19834,14 +19834,14 @@ pixel_x = -5; pixel_y = 4 }, -/obj/item/cartridge/medical, +/obj/item/computer_hardware/hard_drive/role/medical, /turf/open/floor/carpet/ship/blue, /area/medical/storage) "mZO" = ( /obj/structure/table, -/obj/item/cartridge/engineering, +/obj/item/computer_hardware/hard_drive/role/engineering, /obj/item/airlock_painter, -/obj/item/cartridge/atmos, +/obj/item/computer_hardware/hard_drive/role/atmos, /obj/item/holosign_creator/engineering, /obj/structure/cable/white{ icon_state = "1-2" @@ -26048,7 +26048,7 @@ pixel_y = 5 }, /obj/item/clothing/glasses/sunglasses/advanced, -/obj/item/cartridge/detective, +/obj/item/computer_hardware/hard_drive/role/detective, /obj/structure/disposalpipe/segment{ dir = 4 }, @@ -31635,17 +31635,17 @@ /area/maintenance/department/engine) "uPC" = ( /obj/structure/table/glass, -/obj/item/cartridge/chemistry{ +/obj/item/computer_hardware/hard_drive/role/chemistry{ pixel_x = -4; pixel_y = 4 }, /obj/item/storage/firstaid/regular, -/obj/item/cartridge/chemistry{ +/obj/item/computer_hardware/hard_drive/role/chemistry{ pixel_x = -4; pixel_y = 4 }, -/obj/item/cartridge/medical, -/obj/item/cartridge/medical, +/obj/item/computer_hardware/hard_drive/role/medical, +/obj/item/computer_hardware/hard_drive/role/medical, /obj/item/screwdriver, /obj/machinery/airalarm/directional/west, /obj/item/computer_hardware/card_slot, @@ -34657,7 +34657,7 @@ dir = 4 }, /obj/item/clothing/head/beret/black, -/obj/item/cartridge/security, +/obj/item/computer_hardware/hard_drive/role/security, /obj/effect/spawner/lootdrop/gloves, /turf/open/floor/carpet/ship/red_carpet, /area/security/main/warroom) @@ -34939,7 +34939,7 @@ }, /obj/structure/table, /obj/item/stack/sheet/mineral/copper, -/obj/item/cartridge/medical, +/obj/item/computer_hardware/hard_drive/role/medical, /turf/open/floor/carpet/ship/blue, /area/medical/storage) "xpy" = ( diff --git a/_maps/map_files/Aetherwhisp/Aetherwhisp2.dmm b/_maps/map_files/Aetherwhisp/Aetherwhisp2.dmm index 9e8a6e6d428..3c012b694e0 100644 --- a/_maps/map_files/Aetherwhisp/Aetherwhisp2.dmm +++ b/_maps/map_files/Aetherwhisp/Aetherwhisp2.dmm @@ -1675,7 +1675,7 @@ /obj/item/stack/cable_coil, /obj/item/stock_parts/cell/empty, /obj/item/stock_parts/cell/potato, -/obj/item/cartridge/signal, +/obj/item/computer_hardware/hard_drive/role/signal, /turf/open/floor/holofloor/wood, /area/science/computer_lab) "bfR" = ( @@ -2994,7 +2994,7 @@ /turf/open/floor/engine, /area/nsv/hanger/storage) "bXL" = ( -/obj/machinery/vending/cart, +/obj/machinery/vending/job_disk, /obj/item/storage/secure/safe{ pixel_x = 32 }, @@ -4140,7 +4140,7 @@ /obj/item/camera_film, /obj/item/camera, /obj/item/taperecorder, -/obj/item/cartridge/curator, +/obj/item/computer_hardware/hard_drive/role/curator, /turf/open/floor/carpet, /area/library) "cKo" = ( @@ -5044,18 +5044,18 @@ /area/engine/engineering/reactor_control) "dzp" = ( /obj/structure/table/wood, -/obj/item/pda/curator{ +/obj/item/modular_computer/tablet/pda/curator{ pixel_x = 3; pixel_y = 3 }, /obj/item/gun/ballistic/revolver/russian{ pixel_x = -12 }, -/obj/item/pda/curator{ +/obj/item/modular_computer/tablet/pda/curator{ pixel_x = 13; pixel_y = 5 }, -/obj/item/pda/curator{ +/obj/item/modular_computer/tablet/pda/curator{ pixel_x = 9; pixel_y = -6 }, @@ -5984,7 +5984,7 @@ pixel_x = -4 }, /obj/machinery/airalarm/directional/east, -/obj/item/cartridge/janitor, +/obj/item/computer_hardware/hard_drive/role/janitor, /obj/item/pushbroom, /turf/open/floor/carpet/ship, /area/janitor) @@ -6335,7 +6335,7 @@ pixel_x = -6; pixel_y = 4 }, -/obj/item/cartridge/roboticist, +/obj/item/computer_hardware/hard_drive/role/roboticist, /obj/structure/window/reinforced{ dir = 1 }, @@ -21079,13 +21079,13 @@ /area/nsv/weapons/starboard) "oBd" = ( /obj/structure/table, -/obj/item/cartridge/atmos, +/obj/item/computer_hardware/hard_drive/role/atmos, /obj/item/multitool{ pixel_x = -4; pixel_y = 2 }, /obj/item/clothing/gloves/color/black, -/obj/item/cartridge/engineering, +/obj/item/computer_hardware/hard_drive/role/engineering, /obj/item/holosign_creator/atmos, /obj/machinery/computer/security/telescreen/ce{ dir = null; @@ -23660,7 +23660,7 @@ /obj/item/camera, /obj/item/hand_labeler_refill, /obj/item/computer_hardware/card_slot, -/obj/item/cartridge/quartermaster, +/obj/item/computer_hardware/hard_drive/role/quartermaster, /obj/structure/table, /obj/machinery/camera/autoname, /turf/open/floor/carpet/orange, @@ -25122,16 +25122,16 @@ "rhn" = ( /obj/structure/table, /obj/item/storage/toolbox/mechanical, -/obj/item/cartridge/signal/toxins{ +/obj/item/computer_hardware/hard_drive/role/signal/toxins{ pixel_x = -4; pixel_y = 2 }, -/obj/item/cartridge/signal/toxins, -/obj/item/cartridge/signal/toxins{ +/obj/item/computer_hardware/hard_drive/role/signal/toxins, +/obj/item/computer_hardware/hard_drive/role/signal/toxins{ pixel_x = 4; pixel_y = 6 }, -/obj/item/cartridge/signal, +/obj/item/computer_hardware/hard_drive/role/signal, /turf/open/floor/carpet/ship/purple_carpet, /area/science/mixing) "rhw" = ( @@ -27728,7 +27728,7 @@ pixel_x = -32; pixel_y = -32 }, -/obj/item/cartridge/captain, +/obj/item/computer_hardware/hard_drive/role/captain, /obj/item/computer_hardware/card_slot, /turf/open/floor/carpet, /area/crew_quarters/heads/captain/private) @@ -30921,7 +30921,7 @@ /obj/item/tank/internals/plasmaman/belt/full{ pixel_x = 7 }, -/obj/item/pda/curator{ +/obj/item/modular_computer/tablet/pda/curator{ pixel_x = -10 }, /obj/machinery/airalarm/directional/north, @@ -33179,8 +33179,8 @@ /obj/item/pen, /obj/item/storage/toolbox/mechanical, /obj/item/folder/yellow, -/obj/item/cartridge/engineering, -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering, +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = 3 }, /obj/item/reagent_containers/pill/patch/silver_sulf, diff --git a/_maps/map_files/Atlas/atlas.dmm b/_maps/map_files/Atlas/atlas.dmm index 03b83e70323..2ee01a32076 100644 --- a/_maps/map_files/Atlas/atlas.dmm +++ b/_maps/map_files/Atlas/atlas.dmm @@ -9175,15 +9175,15 @@ /area/maintenance/nsv/deck2/port/fore) "EG" = ( /obj/structure/table/reinforced, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = 6; pixel_y = 5 }, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = -4; pixel_y = 7 }, -/obj/item/cartridge/quartermaster, +/obj/item/computer_hardware/hard_drive/role/quartermaster, /obj/item/paper_bin, /obj/item/stamp/quartermaster, /obj/item/clothing/mask/cigarette/cigar, diff --git a/_maps/map_files/Atlas/atlas2.dmm b/_maps/map_files/Atlas/atlas2.dmm index cd2a1c0086e..41a2508df4d 100644 --- a/_maps/map_files/Atlas/atlas2.dmm +++ b/_maps/map_files/Atlas/atlas2.dmm @@ -4927,7 +4927,7 @@ name = "Executive requests console"; pixel_y = -32 }, -/obj/machinery/vending/cart{ +/obj/machinery/vending/job_disk{ density = 0; pixel_x = 23 }, diff --git a/_maps/map_files/Eclipse/Eclipse1.dmm b/_maps/map_files/Eclipse/Eclipse1.dmm index d1fab581abc..1801800b82d 100644 --- a/_maps/map_files/Eclipse/Eclipse1.dmm +++ b/_maps/map_files/Eclipse/Eclipse1.dmm @@ -10606,7 +10606,7 @@ /turf/open/floor/monotile/dark, /area/hallway/primary/fore) "RW" = ( -/obj/machinery/vending/cart, +/obj/machinery/vending/job_disk, /turf/open/floor/plasteel/dark/side{ dir = 4 }, diff --git a/_maps/map_files/Galactica/Galactica1.dmm b/_maps/map_files/Galactica/Galactica1.dmm index d642660b97d..df3a2240b10 100644 --- a/_maps/map_files/Galactica/Galactica1.dmm +++ b/_maps/map_files/Galactica/Galactica1.dmm @@ -1032,7 +1032,7 @@ "cT" = ( /obj/structure/table/glass, /obj/item/paper_bin, -/obj/item/cartridge/chemistry{ +/obj/item/computer_hardware/hard_drive/role/chemistry{ pixel_y = 2 }, /obj/item/clothing/glasses/hud/health, @@ -18101,9 +18101,9 @@ pixel_y = -8 }, /obj/structure/closet/secure_closet/quartermaster, -/obj/item/cartridge/quartermaster, -/obj/item/cartridge/quartermaster, -/obj/item/cartridge/quartermaster, +/obj/item/computer_hardware/hard_drive/role/quartermaster, +/obj/item/computer_hardware/hard_drive/role/quartermaster, +/obj/item/computer_hardware/hard_drive/role/quartermaster, /obj/structure/disposalpipe/segment, /obj/item/clothing/head/beret/supply, /turf/open/floor/carpet/ship, diff --git a/_maps/map_files/Galactica/Galactica2.dmm b/_maps/map_files/Galactica/Galactica2.dmm index baa625599b4..3ec1f6ebf09 100644 --- a/_maps/map_files/Galactica/Galactica2.dmm +++ b/_maps/map_files/Galactica/Galactica2.dmm @@ -28479,7 +28479,7 @@ /obj/item/paper/monitorkey, /obj/item/clothing/glasses/meson, /obj/item/stamp/chief_engineer, -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = 3 }, /obj/item/reagent_containers/pill/patch/silver_sulf, @@ -33437,7 +33437,7 @@ /turf/open/floor/monotile/steel, /area/ai_monitored/security/armory/lockup) "rIP" = ( -/obj/machinery/vending/cart, +/obj/machinery/vending/job_disk, /obj/machinery/light{ dir = 8 }, @@ -37236,7 +37236,7 @@ "tIA" = ( /obj/structure/table/reinforced, /obj/item/rcl/pre_loaded, -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = 4; pixel_y = 5 }, diff --git a/_maps/map_files/Galactica/old/Galactica2_old.dmm b/_maps/map_files/Galactica/old/Galactica2_old.dmm index cbc5a790c9f..3653958d04d 100644 --- a/_maps/map_files/Galactica/old/Galactica2_old.dmm +++ b/_maps/map_files/Galactica/old/Galactica2_old.dmm @@ -14761,7 +14761,7 @@ name = "Executive officer's Office" }) "aHJ" = ( -/obj/machinery/vending/cart, +/obj/machinery/vending/job_disk, /turf/open/floor/monotile, /area/crew_quarters/heads/hop{ name = "Executive officer's Office" @@ -14807,7 +14807,7 @@ /obj/structure/sign/nanotrasen{ pixel_y = 26 }, -/obj/item/cartridge/lawyer, +/obj/item/computer_hardware/hard_drive/role/lawyer, /obj/item/taperecorder, /turf/open/floor/monotile, /area/lawoffice) @@ -19068,18 +19068,18 @@ /area/crew_quarters/heads/chief) "aRr" = ( /obj/structure/table/reinforced, -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = 4; pixel_y = 5 }, -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = -3; pixel_y = 2 }, -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = 3 }, -/obj/item/cartridge/atmos, +/obj/item/computer_hardware/hard_drive/role/atmos, /obj/machinery/camera/autoname{ dir = 1; icon_state = "camera" @@ -54495,12 +54495,12 @@ /turf/open/floor/monotile, /area/security/checkpoint/engineering) "sVz" = ( -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = -4; pixel_y = 7 }, -/obj/item/cartridge/quartermaster, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster, +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = 6; pixel_y = 5 }, diff --git a/_maps/map_files/Galactica/old/Galactica_old.dmm b/_maps/map_files/Galactica/old/Galactica_old.dmm index 5d0c034cf69..448590381cf 100644 --- a/_maps/map_files/Galactica/old/Galactica_old.dmm +++ b/_maps/map_files/Galactica/old/Galactica_old.dmm @@ -6917,10 +6917,10 @@ /area/crew_quarters/heads/cmo) "rx" = ( /obj/structure/table/glass, -/obj/item/cartridge/medical, -/obj/item/cartridge/medical, -/obj/item/cartridge/medical, -/obj/item/cartridge/chemistry{ +/obj/item/computer_hardware/hard_drive/role/medical, +/obj/item/computer_hardware/hard_drive/role/medical, +/obj/item/computer_hardware/hard_drive/role/medical, +/obj/item/computer_hardware/hard_drive/role/chemistry{ pixel_y = 2 }, /obj/item/clothing/glasses/hud/health, diff --git a/_maps/map_files/Gladius/Gladius1.dmm b/_maps/map_files/Gladius/Gladius1.dmm index f0051faf01f..ea9fed5fb21 100644 --- a/_maps/map_files/Gladius/Gladius1.dmm +++ b/_maps/map_files/Gladius/Gladius1.dmm @@ -22438,7 +22438,7 @@ pixel_x = -2 }, /obj/item/book/manual/wiki/security_space_law, -/obj/item/cartridge/detective, +/obj/item/computer_hardware/hard_drive/role/detective, /obj/machinery/light_switch/north, /turf/open/floor/monotile/steel, /area/crew_quarters/heads/hos) @@ -23358,16 +23358,16 @@ /turf/open/floor/monotile/steel, /area/crew_quarters/dorms/nsv/state_room) "kXg" = ( -/obj/item/cartridge/medical{ +/obj/item/computer_hardware/hard_drive/role/medical{ pixel_x = -2; pixel_y = 6 }, -/obj/item/cartridge/medical{ +/obj/item/computer_hardware/hard_drive/role/medical{ pixel_x = 6; pixel_y = 3 }, -/obj/item/cartridge/medical, -/obj/item/cartridge/chemistry{ +/obj/item/computer_hardware/hard_drive/role/medical, +/obj/item/computer_hardware/hard_drive/role/chemistry{ pixel_y = 2 }, /obj/structure/table/glass, @@ -30180,12 +30180,12 @@ /area/crew_quarters/heads/hor) "oga" = ( /obj/structure/table, -/obj/item/cartridge/signal/toxins, -/obj/item/cartridge/signal/toxins{ +/obj/item/computer_hardware/hard_drive/role/signal/toxins, +/obj/item/computer_hardware/hard_drive/role/signal/toxins{ pixel_x = -4; pixel_y = 2 }, -/obj/item/cartridge/signal/toxins{ +/obj/item/computer_hardware/hard_drive/role/signal/toxins{ pixel_x = 4; pixel_y = 6 }, @@ -42075,7 +42075,7 @@ /turf/open/floor/monotile/steel, /area/nsv/briefingroom) "tPt" = ( -/obj/machinery/vending/cart{ +/obj/machinery/vending/job_disk{ req_access_txt = "57" }, /turf/open/floor/wood, diff --git a/_maps/map_files/Gladius/Gladius2.dmm b/_maps/map_files/Gladius/Gladius2.dmm index 4d319adca87..7ee7e11b309 100644 --- a/_maps/map_files/Gladius/Gladius2.dmm +++ b/_maps/map_files/Gladius/Gladius2.dmm @@ -19201,18 +19201,18 @@ /turf/open/floor/monotile/dark, /area/quartermaster/sorting) "lqn" = ( -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = 4; pixel_y = 5 }, -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = -3; pixel_y = 2 }, -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = 3 }, -/obj/item/cartridge/atmos, +/obj/item/computer_hardware/hard_drive/role/atmos, /obj/structure/disposalpipe/segment, /obj/structure/rack, /obj/effect/turf_decal/ship/techfloor{ @@ -20218,7 +20218,7 @@ pixel_x = 4; pixel_y = -2 }, -/obj/item/cartridge/janitor, +/obj/item/computer_hardware/hard_drive/role/janitor, /obj/machinery/light{ dir = 4 }, @@ -23048,7 +23048,7 @@ /turf/open/floor/carpet/green, /area/engine/engineering/reactor_core) "nsL" = ( -/obj/item/cartridge/lawyer, +/obj/item/computer_hardware/hard_drive/role/lawyer, /obj/item/clothing/accessory/lawyers_badge, /obj/item/clothing/accessory/lawyers_badge, /obj/item/clothing/suit/toggle/lawyer, @@ -25000,7 +25000,7 @@ pixel_y = 7 }, /obj/item/pen, -/obj/item/cartridge/lawyer{ +/obj/item/computer_hardware/hard_drive/role{ pixel_x = 8 }, /turf/open/floor/carpet/blue, @@ -30473,15 +30473,15 @@ /area/quartermaster/qm) "rvu" = ( /obj/structure/table, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = -4; pixel_y = 7 }, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = 6; pixel_y = 5 }, -/obj/item/cartridge/quartermaster, +/obj/item/computer_hardware/hard_drive/role/quartermaster, /obj/item/gps{ gpstag = "QM0" }, diff --git a/_maps/map_files/Hammerhead/Hammerhead.dmm b/_maps/map_files/Hammerhead/Hammerhead.dmm index b2491444a2d..b77c08476da 100644 --- a/_maps/map_files/Hammerhead/Hammerhead.dmm +++ b/_maps/map_files/Hammerhead/Hammerhead.dmm @@ -20097,7 +20097,7 @@ /turf/open/floor/plasteel/dark, /area/crew_quarters/heads/chief) "byf" = ( -/obj/machinery/vending/cart, +/obj/machinery/vending/job_disk, /turf/open/floor/wood, /area/crew_quarters/heads/hop) "byj" = ( @@ -50633,7 +50633,7 @@ /turf/open/floor/plating, /area/maintenance/department/electrical) "uFa" = ( -/obj/item/pda/clear, +/obj/item/modular_computer/tablet/pda/clear, /obj/structure/grille{ layer = 3.001 }, diff --git a/_maps/map_files/Instanced/map_files/Babylon2.dmm b/_maps/map_files/Instanced/map_files/Babylon2.dmm index 12ffed62a38..17033165d0d 100644 --- a/_maps/map_files/Instanced/map_files/Babylon2.dmm +++ b/_maps/map_files/Instanced/map_files/Babylon2.dmm @@ -2719,15 +2719,15 @@ "cGz" = ( /obj/structure/table, /obj/item/clipboard, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = 6; pixel_y = 5 }, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = -4; pixel_y = 7 }, -/obj/item/cartridge/quartermaster, +/obj/item/computer_hardware/hard_drive/role/quartermaster, /obj/item/coin/silver, /turf/open/floor/plasteel/tech/grid, /area/quartermaster/pvp) @@ -20411,7 +20411,7 @@ /turf/open/floor/plasteel, /area/quartermaster/pvp) "sxo" = ( -/obj/machinery/vending/cart, +/obj/machinery/vending/job_disk, /obj/structure/disposalpipe/segment{ dir = 4 }, diff --git a/_maps/map_files/Instanced/map_files/SpaceSHIP.dmm b/_maps/map_files/Instanced/map_files/SpaceSHIP.dmm index 90f6114c8f8..b761a7dedca 100644 --- a/_maps/map_files/Instanced/map_files/SpaceSHIP.dmm +++ b/_maps/map_files/Instanced/map_files/SpaceSHIP.dmm @@ -20674,7 +20674,7 @@ /turf/open/floor/plasteel/tech/grid, /area/hallway/pvp) "WU" = ( -/obj/machinery/vending/cart, +/obj/machinery/vending/job_disk, /turf/open/floor/carpet/red, /area/crew_quarters/heads/captain/pvp) "WV" = ( diff --git a/_maps/map_files/Mining/nsv13/ruins/mining4.dmm b/_maps/map_files/Mining/nsv13/ruins/mining4.dmm index a85a6c7acdf..947342fd7b9 100644 --- a/_maps/map_files/Mining/nsv13/ruins/mining4.dmm +++ b/_maps/map_files/Mining/nsv13/ruins/mining4.dmm @@ -25,7 +25,7 @@ /area/ruin/powered) "g" = ( /obj/item/shovel, -/obj/item/pda/engineering, +/obj/item/modular_computer/tablet/pda/engineering, /turf/open/floor/plating, /area/ruin/powered) "h" = ( @@ -34,7 +34,7 @@ /turf/open/floor/plating, /area/ruin/powered) "i" = ( -/obj/item/pda/janitor, +/obj/item/modular_computer/tablet/pda/janitor, /turf/open/floor/plating, /area/ruin/powered) "j" = ( diff --git a/_maps/map_files/Serendipity/Serendipity1.dmm b/_maps/map_files/Serendipity/Serendipity1.dmm index e183ec9ffca..a698320a7ae 100644 --- a/_maps/map_files/Serendipity/Serendipity1.dmm +++ b/_maps/map_files/Serendipity/Serendipity1.dmm @@ -13910,7 +13910,7 @@ /turf/open/floor/grass, /area/chapel) "TI" = ( -/obj/machinery/vending/cart, +/obj/machinery/vending/job_disk, /turf/open/floor/carpet, /area/crew_quarters/heads/xo) "TJ" = ( diff --git a/_maps/map_files/Shrike/Shrike1.dmm b/_maps/map_files/Shrike/Shrike1.dmm index 1a7c4f609de..ea38f8a3934 100644 --- a/_maps/map_files/Shrike/Shrike1.dmm +++ b/_maps/map_files/Shrike/Shrike1.dmm @@ -8596,7 +8596,7 @@ /turf/closed/wall/ship, /area/hallway/nsv/deck1/frame1/starboard) "NR" = ( -/obj/machinery/vending/cart, +/obj/machinery/vending/job_disk, /turf/open/floor/plasteel/ridged/steel, /area/bridge{ lighting_colour_bulb = "#c1e1ff"; diff --git a/_maps/map_files/Snake/snake_lower.dmm b/_maps/map_files/Snake/snake_lower.dmm index afbf2490835..2333ad47572 100644 --- a/_maps/map_files/Snake/snake_lower.dmm +++ b/_maps/map_files/Snake/snake_lower.dmm @@ -13155,7 +13155,7 @@ /obj/structure/cable{ icon_state = "1-8" }, -/obj/machinery/vending/cart, +/obj/machinery/vending/job_disk, /obj/machinery/light_switch/south, /turf/open/floor/wood, /area/crew_quarters/heads/hop{ diff --git a/_maps/map_files/Snake/snake_upper.dmm b/_maps/map_files/Snake/snake_upper.dmm index c7f7cfd9972..5af0e2ac938 100644 --- a/_maps/map_files/Snake/snake_upper.dmm +++ b/_maps/map_files/Snake/snake_upper.dmm @@ -7571,12 +7571,12 @@ /area/maintenance/central) "RR" = ( /obj/structure/table/reinforced, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = -4; pixel_y = 7 }, -/obj/item/cartridge/quartermaster, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster, +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = 6; pixel_y = 5 }, diff --git a/_maps/map_files/Tycoon/Tycoon1.dmm b/_maps/map_files/Tycoon/Tycoon1.dmm index 8d8be5b396c..77d9d6b540b 100644 --- a/_maps/map_files/Tycoon/Tycoon1.dmm +++ b/_maps/map_files/Tycoon/Tycoon1.dmm @@ -1971,7 +1971,7 @@ name = "Executive officer's Office" }) "fi" = ( -/obj/machinery/vending/cart, +/obj/machinery/vending/job_disk, /obj/machinery/keycard_auth{ pixel_x = -26 }, diff --git a/_maps/map_files/Tycoon/Tycoon2.dmm b/_maps/map_files/Tycoon/Tycoon2.dmm index 615cdf85cf1..81623bce9bc 100644 --- a/_maps/map_files/Tycoon/Tycoon2.dmm +++ b/_maps/map_files/Tycoon/Tycoon2.dmm @@ -5278,22 +5278,6 @@ }, /turf/open/floor/monotile/steel, /area/medical/virology) -"ani" = ( -/obj/item/cartridge/engineering{ - pixel_x = 4; - pixel_y = 5 - }, -/obj/item/cartridge/engineering{ - pixel_x = -3; - pixel_y = 2 - }, -/obj/item/cartridge/engineering{ - pixel_x = 3 - }, -/obj/structure/table/reinforced, -/obj/item/cartridge/atmos, -/turf/open/floor/carpet/blue, -/area/crew_quarters/heads/chief) "anj" = ( /turf/closed/wall/r_wall/ship, /area/engine/engineering/hangar) @@ -5435,13 +5419,6 @@ }, /turf/open/floor/monotile/light, /area/medical/virology) -"anF" = ( -/obj/structure/table/glass, -/obj/item/cartridge/lawyer, -/obj/item/taperecorder, -/obj/item/reagent_containers/food/drinks/solgovcup, -/turf/open/floor/carpet/blue, -/area/lawoffice) "anG" = ( /obj/machinery/computer/station_alert{ dir = 4 @@ -5578,16 +5555,6 @@ }, /turf/open/floor/monotile/dark, /area/tcommsat/computer) -"anY" = ( -/obj/structure/cable/yellow{ - icon_state = "1-2" - }, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/layer_manifold, -/turf/open/floor/monotile/steel, -/area/engine/engineering) "aoa" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer2{ dir = 4 @@ -8378,22 +8345,6 @@ }, /turf/open/floor/plating, /area/engine/storage) -"ayv" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, -/obj/machinery/door/airlock/ship/command{ - name = "Gravity Generator"; - req_one_access_txt = "56" - }, -/obj/machinery/door/firedoor/border_only, -/obj/machinery/door/firedoor/border_only{ - dir = 1 - }, -/turf/open/floor/monotile/steel, -/area/engine/gravity_generator) "ayE" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer2, /obj/structure/cable{ @@ -15067,21 +15018,6 @@ }, /turf/open/space/basic, /area/space/nearstation) -"aOj" = ( -/obj/structure/table/reinforced, -/obj/item/cartridge/quartermaster{ - pixel_x = -4; - pixel_y = 7 - }, -/obj/item/cartridge/quartermaster, -/obj/item/cartridge/quartermaster{ - pixel_x = 6; - pixel_y = 5 - }, -/obj/item/reagent_containers/food/drinks/solgovcup, -/obj/item/clothing/ears/earmuffs, -/turf/open/floor/carpet/ship, -/area/quartermaster/qm) "aOk" = ( /obj/structure/cable{ icon_state = "1-2" @@ -31264,30 +31200,6 @@ }, /turf/open/floor/monotile/light, /area/crew_quarters/heads/cmo) -"bwd" = ( -/obj/structure/table/glass, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer2{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/item/cartridge/medical, -/obj/item/cartridge/medical{ - pixel_x = 6; - pixel_y = 3 - }, -/obj/item/cartridge/medical{ - pixel_x = -2; - pixel_y = 6 - }, -/obj/item/cartridge/chemistry{ - pixel_y = 2 - }, -/obj/item/clothing/glasses/hud/health, -/turf/open/floor/carpet/blue, -/area/crew_quarters/heads/cmo) "bwe" = ( /obj/machinery/atmospherics/pipe/simple/general/visible{ dir = 4 @@ -39186,6 +39098,21 @@ /obj/effect/turf_decal/tile/orange, /turf/open/floor/monotile/steel, /area/nsv/weapons/fore) +"bYJ" = ( +/obj/structure/table/reinforced, +/obj/item/computer_hardware/hard_drive/role/quartermaster{ + pixel_x = -4; + pixel_y = 7 + }, +/obj/item/computer_hardware/hard_drive/role/quartermaster, +/obj/item/computer_hardware/hard_drive/role/quartermaster{ + pixel_x = 6; + pixel_y = 5 + }, +/obj/item/reagent_containers/food/drinks/solgovcup, +/obj/item/clothing/ears/earmuffs, +/turf/open/floor/carpet/ship, +/area/quartermaster/qm) "bYK" = ( /obj/structure/sign/ship/securearea{ dir = 8 @@ -40707,6 +40634,22 @@ }, /turf/closed/wall/r_wall/ship, /area/engine/atmospherics_engine) +"dwf" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, +/obj/machinery/door/airlock/ship/command{ + name = "Gravity Generator"; + req_one_access_txt = "56" + }, +/obj/machinery/door/firedoor/border_only, +/obj/machinery/door/firedoor/border_only{ + dir = 1 + }, +/turf/open/floor/monotile/steel, +/area/engine/gravity_generator) "dyj" = ( /obj/structure/bookcase/manuals/medical, /obj/machinery/camera/autoname{ @@ -43334,6 +43277,22 @@ /obj/machinery/newscaster/security_unit, /turf/closed/wall/ship, /area/security/brig) +"hrK" = ( +/obj/item/computer_hardware/hard_drive/role/engineering{ + pixel_x = 4; + pixel_y = 5 + }, +/obj/item/computer_hardware/hard_drive/role/engineering{ + pixel_x = -3; + pixel_y = 2 + }, +/obj/item/computer_hardware/hard_drive/role/engineering{ + pixel_x = 3 + }, +/obj/structure/table/reinforced, +/obj/item/computer_hardware/hard_drive/role/atmos, +/turf/open/floor/carpet/blue, +/area/crew_quarters/heads/chief) "hsX" = ( /obj/structure/disposalpipe/segment{ dir = 9 @@ -44109,6 +44068,30 @@ /obj/machinery/disposal/bin, /turf/open/floor/plasteel/grid/steel, /area/quartermaster/storage) +"iBz" = ( +/obj/structure/table/glass, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer2{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/item/computer_hardware/hard_drive/role/medical, +/obj/item/computer_hardware/hard_drive/role/medical{ + pixel_x = 6; + pixel_y = 3 + }, +/obj/item/computer_hardware/hard_drive/role/medical{ + pixel_x = -2; + pixel_y = 6 + }, +/obj/item/computer_hardware/hard_drive/role/chemistry{ + pixel_y = 2 + }, +/obj/item/clothing/glasses/hud/health, +/turf/open/floor/carpet/blue, +/area/crew_quarters/heads/cmo) "iCC" = ( /obj/effect/decal/cleanable/oil, /turf/open/floor/plating, @@ -48079,6 +48062,16 @@ }, /turf/open/floor/monotile/steel, /area/nsv/weapons/fore) +"ogB" = ( +/obj/effect/turf_decal/tile/yellow{ + dir = 8 + }, +/obj/effect/turf_decal/tile/yellow, +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, +/turf/open/floor/monotile/dark, +/area/engine/engineering) "ohf" = ( /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -48779,16 +48772,6 @@ /obj/effect/landmark/zebra_interlock_point, /turf/open/floor/monotile/steel, /area/nsv/weapons/fore) -"prR" = ( -/obj/effect/turf_decal/tile/yellow{ - dir = 8 - }, -/obj/effect/turf_decal/tile/yellow, -/obj/structure/cable/yellow{ - icon_state = "1-2" - }, -/turf/open/floor/monotile/dark, -/area/engine/engineering) "psx" = ( /obj/structure/disposalpipe/segment{ dir = 5 @@ -49273,6 +49256,21 @@ }, /turf/open/floor/plasteel/grid/steel, /area/quartermaster/storage) +"qiA" = ( +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4{ + dir = 8 + }, +/obj/machinery/door/airlock/ship/maintenance{ + req_one_access_txt = "10" + }, +/turf/open/floor/plating, +/area/maintenance/department/engine) "qjd" = ( /obj/machinery/atmospherics/pipe/simple/dark/visible, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, @@ -52014,20 +52012,6 @@ /obj/machinery/light, /turf/open/floor/engine, /area/engine/engineering/reactor_core) -"urL" = ( -/obj/effect/turf_decal/stripes/line, -/obj/structure/cable{ - icon_state = "4-8" - }, -/obj/structure/cable/yellow{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden/layer4, -/turf/open/floor/monotile/steel, -/area/engine/engineering) "uti" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{ dir = 4 @@ -52041,6 +52025,20 @@ }, /turf/open/floor/monotile/dark, /area/hallway/secondary/exit) +"utB" = ( +/obj/effect/turf_decal/stripes/line, +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden/layer4, +/turf/open/floor/monotile/steel, +/area/engine/engineering) "uyI" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -52211,6 +52209,13 @@ /obj/effect/landmark/zebra_interlock_point, /turf/open/floor/monotile/steel, /area/medical/medbay/lobby) +"uOT" = ( +/obj/structure/table/glass, +/obj/item/computer_hardware/hard_drive/role/lawyer, +/obj/item/taperecorder, +/obj/item/reagent_containers/food/drinks/solgovcup, +/turf/open/floor/carpet/blue, +/area/lawoffice) "uOY" = ( /obj/structure/closet/crate{ opened = 1 @@ -52218,6 +52223,16 @@ /obj/effect/spawner/lootdrop/maintenance, /turf/open/floor/plating, /area/maintenance/starboard/fore) +"uPh" = ( +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/layer_manifold, +/turf/open/floor/monotile/steel, +/area/engine/engineering) "uRG" = ( /obj/machinery/atmospherics/components/unary/portables_connector/layer2{ dir = 8 @@ -52908,21 +52923,6 @@ }, /turf/open/floor/plating, /area/maintenance/starboard/fore) -"vZO" = ( -/obj/structure/cable{ - icon_state = "4-8" - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4{ - dir = 8 - }, -/obj/machinery/door/airlock/ship/maintenance{ - req_one_access_txt = "10" - }, -/turf/open/floor/plating, -/area/maintenance/department/engine) "wat" = ( /obj/machinery/atmospherics/pipe/manifold/scrubbers/visible{ dir = 4 @@ -79079,7 +79079,7 @@ aMs blb bwa bwc -bwd +iBz byY bCq acF @@ -93232,7 +93232,7 @@ ajp ajs aka akd -aOj +bYJ aOr phM aEf @@ -93438,7 +93438,7 @@ aRA aRq amq aui -anF +uOT asc ate avn @@ -100117,7 +100117,7 @@ llg asL aqj aqh -ani +hrK aSm btq anp @@ -101140,7 +101140,7 @@ amx aey aey aey -vZO +qiA aey aey aey @@ -101907,7 +101907,7 @@ anJ amx aHM aIy -ayv +dwf ieG wpa wpa @@ -102429,9 +102429,9 @@ iVI jrd tFb vYQ -anY -urL -prR +uPh +utB +ogB iVY gvQ kjd diff --git a/_maps/map_files/Vago/vagodeck1.dmm b/_maps/map_files/Vago/vagodeck1.dmm index 32fbfa20dd2..20b831eaec9 100644 --- a/_maps/map_files/Vago/vagodeck1.dmm +++ b/_maps/map_files/Vago/vagodeck1.dmm @@ -2525,12 +2525,12 @@ /obj/item/gps{ gpstag = "QM0" }, -/obj/item/cartridge/quartermaster, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster, +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = 6; pixel_y = 5 }, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = -4; pixel_y = 7 }, diff --git a/_maps/map_files/Vago/vagodeck2.dmm b/_maps/map_files/Vago/vagodeck2.dmm index 8da8b72f431..612f1560ff4 100644 --- a/_maps/map_files/Vago/vagodeck2.dmm +++ b/_maps/map_files/Vago/vagodeck2.dmm @@ -4646,7 +4646,7 @@ }, /obj/item/pen, /obj/item/taperecorder, -/obj/item/cartridge/lawyer, +/obj/item/computer_hardware/hard_drive/role/lawyer, /obj/item/radio/intercom{ pixel_y = 23 }, @@ -11059,15 +11059,15 @@ /area/hallway/nsv/deck2/frame2/port) "Jo" = ( /obj/structure/table/wood, -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = -3; pixel_y = 2 }, -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = -3; pixel_y = 2 }, -/obj/item/cartridge/engineering{ +/obj/item/computer_hardware/hard_drive/role/engineering{ pixel_x = -3; pixel_y = 2 }, diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 803d795c613..a2e3013a91e 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -4795,13 +4795,13 @@ /area/centcom/ferry) "pP" = ( /obj/structure/table/reinforced, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = -6 }, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_x = 6 }, -/obj/item/cartridge/quartermaster{ +/obj/item/computer_hardware/hard_drive/role/quartermaster{ pixel_y = 6 }, /obj/item/gps/mining, diff --git a/code/__DEFINES/dcs/signals/obj_signals/item_signals/item_signals.dm b/code/__DEFINES/dcs/signals/obj_signals/item_signals/item_signals.dm index 0d397364baf..ff100275b75 100644 --- a/code/__DEFINES/dcs/signals/obj_signals/item_signals/item_signals.dm +++ b/code/__DEFINES/dcs/signals/obj_signals/item_signals/item_signals.dm @@ -50,9 +50,10 @@ // /obj/effect/mine signals #define COMSIG_MINE_TRIGGERED "minegoboom" ///from [/obj/effect/mine/proc/triggermine]: -// /obj/item/pda signals -#define COMSIG_PDA_CHANGE_RINGTONE "pda_change_ringtone" //! called on pda when the user changes the ringtone: (mob/living/user, new_ringtone) - #define COMPONENT_STOP_RINGTONE_CHANGE 1 +// /obj/item/modular_computer/tablet/pda signals +/// Called on tablet (PDA) when the user changes the ringtone: (mob/living/user, new_ringtone) +#define COMSIG_TABLET_CHANGE_RINGTONE "comsig_tablet_change_ringtone" + #define COMPONENT_STOP_RINGTONE_CHANGE (1<<0) // /obj/item/radio signals diff --git a/code/__DEFINES/devices.dm b/code/__DEFINES/devices.dm new file mode 100644 index 00000000000..179b9e93d78 --- /dev/null +++ b/code/__DEFINES/devices.dm @@ -0,0 +1,72 @@ +// Role disk defines + +#define DISK_POWER (1<<0) +#define DISK_ATMOS (1<<1) +#define DISK_MED (1<<2) +#define DISK_CHEM (1<<3) +#define DISK_MANIFEST (1<<4) +#define DISK_NEWS (1<<5) +#define DISK_SIGNAL (1<<6) +#define DISK_STATUS (1<<7) +#define DISK_CARGO (1<<8) +#define DISK_ROBOS (1<<9) +#define DISK_JANI (1<<10) +#define DISK_SEC (1<<11) +#define DISK_BUDGET (1<<12) +#define DISK_REMOTE_AIRLOCK (1<<13) +#define DISK_SILO_LOG (1<<14) +#define DISK_HOP (1<<15) + +// Theme defines + +#define THEME_NTOS "ntos-default" +#define THEME_THINKTRONIC "thinktronic-classic" +#define THEME_NTOS_LIGHT "ntos-light" +#define THEME_NTOS_DARK "ntos-dark" +#define THEME_NTOS_RED "ntos-red" +#define THEME_NTOS_ORANGE "ntos-orange" +#define THEME_NTOS_YELLOW "ntos-yellow" +#define THEME_NTOS_OLIVE "ntos-olive" +#define THEME_NTOS_GREEN "ntos-green" +#define THEME_NTOS_TEAL "ntos-teal" +#define THEME_NTOS_BLUE "ntos-blue" +#define THEME_NTOS_VIOLET "ntos-violet" +#define THEME_NTOS_PURPLE "ntos-purple" +#define THEME_NTOS_PINK "ntos-pink" +#define THEME_NTOS_BROWN "ntos-brown" +#define THEME_NTOS_GREY "ntos-grey" +#define THEME_NTOS_CLOWN_PINK "ntos-clown-pink" +#define THEME_NTOS_CLOWN_YELLOW "ntos-clown-yellow" +#define THEME_NTOS_HACKERMAN "ntos-hackerman" +#define THEME_HACKERMAN "hackeros" +#define THEME_RETRO "retro" + +#define THEME_SYNDICATE "syndicate" + +GLOBAL_LIST_INIT(ntos_device_themes_default, list( + "NtOS Default" = THEME_NTOS, + "Thinktronic Classic" = THEME_THINKTRONIC, + "NtOS Light" = THEME_NTOS_LIGHT, + "NtOS Dark" = THEME_NTOS_DARK, + "NtOS Red" = THEME_NTOS_RED, + "NtOS Orange" = THEME_NTOS_ORANGE, + "NtOS Yellow" = THEME_NTOS_YELLOW, + "NtOS Olive" = THEME_NTOS_OLIVE, + "NtOS Green" = THEME_NTOS_GREEN, + "NtOS Teal" = THEME_NTOS_TEAL, + "NtOS Blue" = THEME_NTOS_BLUE, + "NtOS Violet" = THEME_NTOS_VIOLET, + "NtOS Purple" = THEME_NTOS_PURPLE, + "NtOS Pink" = THEME_NTOS_PINK, + "NtOS Brown" = THEME_NTOS_BROWN, + "NtOS Grey" = THEME_NTOS_GREY, + "NtOS Clown Pink" = THEME_NTOS_CLOWN_PINK, + "NtOS Clown Yellow" = THEME_NTOS_CLOWN_YELLOW, + "NtOS Hackerman" = THEME_NTOS_HACKERMAN, + "Hackerman" = THEME_HACKERMAN, + "Retro" = THEME_RETRO +)) + +GLOBAL_LIST_INIT(ntos_device_themes_emagged, list( + "Syndix" = THEME_SYNDICATE +) + GLOB.ntos_device_themes_default) diff --git a/code/__DEFINES/interaction_flags.dm b/code/__DEFINES/interaction_flags.dm index 9535a99fc2d..93b8d3695f0 100644 --- a/code/__DEFINES/interaction_flags.dm +++ b/code/__DEFINES/interaction_flags.dm @@ -8,6 +8,7 @@ #define INTERACT_ATOM_CHECK_GRAB (1<<6) //! incapacitated check checks grab #define INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND (1<<7) //! prevents leaving fingerprints automatically on attack_hand #define INTERACT_ATOM_NO_FINGERPRINT_INTERACT (1<<8) //! adds hiddenprints instead of fingerprints on interact +#define INTERACT_ATOM_ALLOW_USER_LOCATION (1<<9) //! allows this atom to skip the adjacency check #define INTERACT_ITEM_ATTACK_HAND_PICKUP (1<<0) //! attempt pickup on attack_hand for items diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 95c4e209ae5..4c28da23921 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -53,9 +53,11 @@ //Modular computer part defines #define MC_CPU "CPU" #define MC_HDD "HDD" +#define MC_HDD_JOB "HDD_JOB" #define MC_SDD "SDD" #define MC_CARD "CARD" #define MC_CARD2 "CARD2" +#define MC_CART "CART" #define MC_NET "NET" #define MC_PRINT "PRINT" #define MC_CELL "CELL" @@ -63,6 +65,7 @@ #define MC_AI "AI" #define MC_SENSORS "SENSORS" #define MC_SIGNALLER "SIGNALER" +#define MC_IDENTIFY "IDENTIFY" //! ## NTNet stuff, for modular computers //! **NTNet module-configuration values. Do not change these. If you need to add another use larger number (5..6..7 etc)** @@ -159,3 +162,18 @@ #define PLANT_GENE_EXTRACTABLE (1<<1) #define CLICKSOUND_INTERVAL (0.1 SECONDS) //clicky noises, how much time needed in between clicks on the machine for the sound to play on click again. + +// From code/game/machinery/computer/communications.dm +// --------------------------------------------------- + +// for setting status display. Used in modpc status app as well. +#define MAX_STATUS_LINE_LENGTH 40 +// approvied pictures, also used in modpc app +GLOBAL_LIST_INIT(approved_status_pictures, list( + "biohazard", + "blank", + "default", + "lockdown", + "redalert", + "shuttle", +)) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 412b1f80dec..cb7c4fcf79d 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -401,9 +401,6 @@ GLOBAL_LIST_INIT(pda_styles, sortList(list(MONO, VT, ORBITRON, SHARE))) #define EGG_LAYING_MESSAGES list("lays an egg.","squats down and croons.","begins making a huge racket.","begins clucking raucously.") -// Used by PDA and cartridge code to reduce repetitiveness of spritesheets -#define PDAIMG(what) {""} - //Filters #define AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, color="#04080FAA") #define GAUSSIAN_BLUR(filter_size) filter(type="blur", size=filter_size) diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 0f86fed86e7..19040a150a1 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -125,11 +125,11 @@ GLOBAL_VAR_INIT(cmp_field, "name") /proc/cmp_typepaths_asc(A, B) return sorttext("[B]","[A]") -/proc/cmp_pdaname_asc(obj/item/pda/A, obj/item/pda/B) - return sorttext(B.owner, A.owner) +/proc/cmp_pdaname_asc(obj/item/modular_computer/A, obj/item/modular_computer/B) + return sorttext(B?.saved_identification, A?.saved_identification) -/proc/cmp_pdajob_asc(obj/item/pda/A, obj/item/pda/B) - return sorttext(B.ownjob, A.ownjob) +/proc/cmp_pdajob_asc(obj/item/modular_computer/A, obj/item/modular_computer/B) + return sorttext(B?.saved_job, A?.saved_job) /proc/cmp_num_string_asc(A, B) return text2num(A) - text2num(B) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index fe03bc7c972..24819c2d030 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -64,7 +64,7 @@ //Returns null if there is any bad text in the string -/proc/reject_bad_text(text, max_length = 512, ascii_only = TRUE) +/proc/reject_bad_text(text, max_length = 512, ascii_only = TRUE, alphanumeric_only = FALSE, underscore_allowed = TRUE) var/char_count = 0 var/non_whitespace = FALSE var/lenbytes = length(text) @@ -79,13 +79,51 @@ return if(0 to 31) return - if(32) - continue + if(32 to 47) + if(alphanumeric_only) + return + else + non_whitespace = TRUE + continue + if(58 to 64) + if(alphanumeric_only) + return + else + non_whitespace = TRUE + continue + if(91 to 94) + if(alphanumeric_only) + return + else + non_whitespace = TRUE + continue + if(95) + if(underscore_allowed) + non_whitespace = TRUE + continue + else if(alphanumeric_only) + return + else + non_whitespace = TRUE + continue + if(96) + if(alphanumeric_only) + return + else + non_whitespace = TRUE + continue + if(123 to 126) + if(alphanumeric_only) + return + else + non_whitespace = TRUE + continue if(127 to INFINITY) if(ascii_only) return else non_whitespace = TRUE + if(non_whitespace) return text //only accepts the text if it has some non-spaces diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 2c09abc557c..ccab73e84aa 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -114,7 +114,8 @@ DEFINE_BITFIELD(interaction_flags_atom, list( "INTERACT_ATOM_IGNORE_RESTRAINED" = INTERACT_ATOM_IGNORE_RESTRAINED, "INTERACT_ATOM_CHECK_GRAB" = INTERACT_ATOM_CHECK_GRAB, "INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND" = INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND, - "INTERACT_ATOM_NO_FINGERPRINT_INTERACT" = INTERACT_ATOM_NO_FINGERPRINT_INTERACT + "INTERACT_ATOM_NO_FINGERPRINT_INTERACT" = INTERACT_ATOM_NO_FINGERPRINT_INTERACT, + "INTERACT_ATOM_ALLOW_USER_LOCATION" = INTERACT_ATOM_ALLOW_USER_LOCATION, )) DEFINE_BITFIELD(interaction_flags_machine, list( @@ -308,7 +309,7 @@ DEFINE_BITFIELD(overmap_deletion_traits, list( "NEVER_DELETE_OCCUPIED" = NEVER_DELETE_OCCUPIED, "DELETE_UNOCCUPIED_ON_DEPARTURE" = DELETE_UNOCCUPIED_ON_DEPARTURE, "FIGHTERS_ARE_OCCUPANTS" = FIGHTERS_ARE_OCCUPANTS, -)) +)) DEFINE_BITFIELD(overmap_user_roles, list( "OVERMAP_USER_ROLE_PILOT" = OVERMAP_USER_ROLE_PILOT, diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 9d688b2b570..ac47123b886 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -31,6 +31,7 @@ GLOBAL_LIST_EMPTY(meteor_list) // List of all meteors. GLOBAL_LIST_EMPTY(active_jammers) // List of active radio jammers GLOBAL_LIST_EMPTY(ladders) GLOBAL_LIST_EMPTY(bot_elevator) +GLOBAL_LIST_EMPTY(janitor_devices) GLOBAL_LIST_EMPTY(trophy_cases) GLOBAL_LIST_EMPTY(wire_color_directory) diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index f5feb50450f..5845568efcd 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -134,16 +134,15 @@ #define ui_ai_announcement "SOUTH:6,WEST+7" #define ui_ai_shuttle "SOUTH:6,WEST+8" #define ui_ai_state_laws "SOUTH:6,WEST+9" -#define ui_ai_pda_send "SOUTH:6,WEST+10" -#define ui_ai_pda_log "SOUTH:6,WEST+11" -#define ui_ai_take_picture "SOUTH:6,WEST+12" -#define ui_ai_view_images "SOUTH:6,WEST+13" -#define ui_ai_sensor "SOUTH:6,WEST+14" -#define ui_ai_multicam "SOUTH:6,WEST+15" -#define ui_ai_add_multicam "SOUTH:6,WEST+16" +#define ui_ai_mod_int "SOUTH:6,WEST+10" +#define ui_ai_take_picture "SOUTH:6,WEST+11" +#define ui_ai_view_images "SOUTH:6,WEST+12" +#define ui_ai_sensor "SOUTH:6,WEST+13" +#define ui_ai_multicam "SOUTH:6,WEST+12" +#define ui_ai_add_multicam "SOUTH:6,WEST+13" +#define ui_ai_move_up "SOUTH:6,WEST+14" +#define ui_ai_move_down "SOUTH:6,WEST+15" #define ui_ai_language_menu "CENTER+7:32,SOUTH+1:5" -#define ui_ai_move_up "SOUTH:5+1,WEST+14" -#define ui_ai_move_down "SOUTH:5+1,WEST+15" // pAI @@ -156,8 +155,7 @@ #define ui_pai_host_monitor "SOUTH:6,WEST+6" #define ui_pai_crew_manifest "SOUTH:6,WEST+7" #define ui_pai_state_laws "SOUTH:6,WEST+8" -#define ui_pai_pda_send "SOUTH:6,WEST+9" -#define ui_pai_pda_log "SOUTH:6,WEST+10" +#define ui_pai_mod_int "SOUTH:6,WEST+9" #define ui_pai_take_picture "SOUTH:6,WEST+12" #define ui_pai_view_images "SOUTH:6,WEST+13" diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index adee4485fa2..8cccc65b566 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -46,6 +46,21 @@ var/mob/living/silicon/ai/AI = usr AI.toggle_camera_light() +/atom/movable/screen/ai/modpc + name = "Messenger" + icon_state = "pda_send" + var/mob/living/silicon/ai/robot + +/atom/movable/screen/ai/modpc/Click() + . = ..() + if(. || !robot.modularInterface?.turn_on(robot, open_ui = FALSE)) + return + var/obj/item/computer_hardware/hard_drive/drive = robot.modularInterface.all_components[MC_HDD] + for(var/datum/computer_file/program/messenger/app in drive?.stored_files) + robot.modularInterface.open_program(robot, app) + robot.modularInterface.interact(robot) + break + /atom/movable/screen/ai/crew_monitor name = "Crew Monitoring Console" icon_state = "crew_monitor" @@ -106,26 +121,6 @@ var/mob/living/silicon/ai/AI = usr AI.checklaws() -/atom/movable/screen/ai/pda_msg_send - name = "PDA - Send Message" - icon_state = "pda_send" - -/atom/movable/screen/ai/pda_msg_send/Click() - if(..()) - return - var/mob/living/silicon/ai/AI = usr - AI.cmd_send_pdamesg(usr) - -/atom/movable/screen/ai/pda_msg_show - name = "PDA - Show Message Log" - icon_state = "pda_receive" - -/atom/movable/screen/ai/pda_msg_show/Click() - if(..()) - return - var/mob/living/silicon/ai/AI = usr - AI.cmd_show_message_log(usr) - /atom/movable/screen/ai/image_take name = "Take Image" icon_state = "take_picture" @@ -210,6 +205,7 @@ /datum/hud/ai/New(mob/owner) ..() var/atom/movable/screen/using + var/mob/living/silicon/ai/myai = mymob // Language menu using = new /atom/movable/screen/language_menu @@ -277,17 +273,14 @@ using.hud = src static_inventory += using -//PDA message - using = new /atom/movable/screen/ai/pda_msg_send() - using.screen_loc = ui_ai_pda_send - using.hud = src - static_inventory += using - -//PDA log - using = new /atom/movable/screen/ai/pda_msg_show() - using.screen_loc = ui_ai_pda_log +// Modular Interface + using = new /atom/movable/screen/ai/modpc() + using.screen_loc = ui_ai_mod_int using.hud = src static_inventory += using + myai.interfaceButton = using + var/atom/movable/screen/ai/modpc/tabletbutton = using + tabletbutton.robot = myai //Take image using = new /atom/movable/screen/ai/image_take() diff --git a/code/_onclick/hud/pai.dm b/code/_onclick/hud/pai.dm index e97e88c12da..9bee046d244 100644 --- a/code/_onclick/hud/pai.dm +++ b/code/_onclick/hud/pai.dm @@ -113,27 +113,20 @@ var/mob/living/silicon/pai/pAI = usr pAI.checklaws() -/atom/movable/screen/pai/pda_msg_send - name = "PDA - Send Message" +/atom/movable/screen/pai/modpc + name = "Messenger" icon_state = "pda_send" - required_software = "digital messenger" + var/mob/living/silicon/pai/pAI -/atom/movable/screen/pai/pda_msg_send/Click() - if(!..()) +/atom/movable/screen/pai/modpc/Click() + . = ..() + if(!. || !pAI.modularInterface || !pAI.modularInterface.turn_on(pAI, open_ui = FALSE)) return - var/mob/living/silicon/pai/pAI = usr - pAI.cmd_send_pdamesg(usr) - -/atom/movable/screen/pai/pda_msg_show - name = "PDA - Show Message Log" - icon_state = "pda_receive" - required_software = "digital messenger" - -/atom/movable/screen/pai/pda_msg_show/Click() - if(!..()) - return - var/mob/living/silicon/pai/pAI = usr - pAI.cmd_show_message_log(usr) + var/obj/item/computer_hardware/hard_drive/drive = pAI.modularInterface.all_components[MC_HDD] + for(var/datum/computer_file/program/messenger/app in drive?.stored_files) + pAI.modularInterface.open_program(pAI, app) + pAI.modularInterface.interact(pAI) + break /atom/movable/screen/pai/image_take name = "Take Image" @@ -171,6 +164,7 @@ /datum/hud/pai/New(mob/living/silicon/pai/owner) ..() var/atom/movable/screen/using + var/mob/living/silicon/pai/mypai = mymob // Software menu using = new /atom/movable/screen/pai/software @@ -222,15 +216,13 @@ using.screen_loc = ui_pai_state_laws static_inventory += using -// PDA message - using = new /atom/movable/screen/pai/pda_msg_send() - using.screen_loc = ui_pai_pda_send - static_inventory += using - -// PDA log - using = new /atom/movable/screen/pai/pda_msg_show() - using.screen_loc = ui_pai_pda_log +// Modular Interface + using = new /atom/movable/screen/pai/modpc() + using.screen_loc = ui_pai_mod_int static_inventory += using + mypai.interface_button = using + var/atom/movable/screen/pai/modpc/tablet_button = using + tablet_button.pAI = mypai // Take image using = new /atom/movable/screen/pai/image_take() diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index b13db01141d..6b3076380ba 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -125,14 +125,14 @@ static_inventory += using //Borg Integrated Tablet - using = new /atom/movable/screen/robot/modPC() + using = new /atom/movable/screen/robot/modpc() using.screen_loc = ui_borg_tablet using.hud = src static_inventory += using mymobR.interfaceButton = using if(mymobR.modularInterface) using.vis_contents += mymobR.modularInterface - var/atom/movable/screen/robot/modPC/tabletbutton = using + var/atom/movable/screen/robot/modpc/tabletbutton = using tabletbutton.robot = mymobR //Alerts @@ -293,12 +293,12 @@ else icon_state = "lamp_off" -/atom/movable/screen/robot/modPC +/atom/movable/screen/robot/modpc name = "Modular Interface" icon_state = "template" var/mob/living/silicon/robot/robot -/atom/movable/screen/robot/modPC/Click() +/atom/movable/screen/robot/modpc/Click() . = ..() if(.) return diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index 9d57f171af0..1f2d0c12884 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -47,7 +47,7 @@ return FALSE /atom/proc/can_interact(mob/user) - if(!user.can_interact_with(src)) + if(!user.can_interact_with(src, interaction_flags_atom & INTERACT_ATOM_ALLOW_USER_LOCATION)) return FALSE if((interaction_flags_atom & INTERACT_ATOM_REQUIRES_DEXTERITY) && !user.IsAdvancedToolUser()) to_chat(user, "You don't have the dexterity to do this!") @@ -58,6 +58,7 @@ /atom/ui_status(mob/user) . = ..() + //Check if both user and atom are at the same location if(!can_interact(user)) . = min(., UI_UPDATE) diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm index e4cd90a5ba3..9c224aebfcb 100644 --- a/code/controllers/subsystem/blackbox.dm +++ b/code/controllers/subsystem/blackbox.dm @@ -89,8 +89,8 @@ SUBSYSTEM_DEF(blackbox) /datum/controller/subsystem/blackbox/proc/FinalFeedback() record_feedback("tally", "ahelp_stats", GLOB.ahelp_tickets.active_tickets.len, "unresolved") for (var/obj/machinery/telecomms/message_server/MS in GLOB.telecomms_list) - if (MS.pda_msgs.len) - record_feedback("tally", "radio_usage", MS.pda_msgs.len, "PDA") + if (MS.modular_msgs.len) + record_feedback("tally", "radio_usage", MS.modular_msgs.len, "PDA") if (MS.rc_msgs.len) record_feedback("tally", "radio_usage", MS.rc_msgs.len, "request console") diff --git a/code/controllers/subsystem/traumas.dm b/code/controllers/subsystem/traumas.dm index fdc3601b87e..d38033d4847 100644 --- a/code/controllers/subsystem/traumas.dm +++ b/code/controllers/subsystem/traumas.dm @@ -64,7 +64,7 @@ SUBSYSTEM_DEF(traumas) "clowns" = typecacheof(list(/obj/item/clothing/under/rank/civilian/clown, /obj/item/clothing/shoes/clown_shoes, /obj/item/clothing/mask/gas/clown_hat, /obj/item/instrument/bikehorn, - /obj/item/pda/clown, /obj/item/grown/bananapeel)), + /obj/item/modular_computer/tablet/pda/clown, /obj/item/grown/bananapeel)), "greytide" = typecacheof(list(/obj/item/clothing/under/color/grey, /obj/item/melee/baton/cattleprod, /obj/item/spear, /obj/item/clothing/mask/gas/old)), diff --git a/code/datums/action.dm b/code/datums/action.dm index 67bed4f474d..10da7130767 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -203,9 +203,9 @@ name = "Toggle Light" /datum/action/item_action/toggle_light/Trigger() - if(istype(target, /obj/item/pda)) - var/obj/item/pda/P = target - P.toggle_light(owner) + if(istype(target, /obj/item/modular_computer)) + var/obj/item/modular_computer/mc = target + mc.toggle_flashlight() return ..() @@ -228,6 +228,9 @@ /datum/action/item_action/startchainsaw name = "Pull The Starting Cord" +/datum/action/item_action/toggle_computer_light + name = "Toggle Flashlight" + /datum/action/item_action/toggle_gunlight name = "Toggle Gunlight" diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm index f3ea6c1749b..cfcb7a133b8 100644 --- a/code/datums/components/uplink.dm +++ b/code/datums/components/uplink.dm @@ -42,8 +42,8 @@ RegisterSignal(parent, COMSIG_IMPLANT_IMPLANTING, PROC_REF(implanting)) RegisterSignal(parent, COMSIG_IMPLANT_OTHER, PROC_REF(old_implant)) RegisterSignal(parent, COMSIG_IMPLANT_EXISTING_UPLINK, PROC_REF(new_implant)) - else if(istype(parent, /obj/item/pda)) - RegisterSignal(parent, COMSIG_PDA_CHANGE_RINGTONE, PROC_REF(new_ringtone)) + else if(istype(parent, /obj/item/modular_computer/tablet)) + RegisterSignal(parent, COMSIG_TABLET_CHANGE_RINGTONE, PROC_REF(new_ringtone)) else if(istype(parent, /obj/item/radio)) RegisterSignal(parent, COMSIG_RADIO_MESSAGE, PROC_REF(radio_message)) else if(istype(parent, /obj/item/pen)) @@ -287,7 +287,6 @@ /datum/component/uplink/proc/new_ringtone(datum/source, mob/living/user, new_ring_text) SIGNAL_HANDLER - var/obj/item/pda/master = parent if(trim(lowertext(new_ring_text)) != trim(lowertext(unlock_code))) if(failsafe_code && trim(lowertext(new_ring_text)) == trim(lowertext(failsafe_code))) failsafe() @@ -295,9 +294,7 @@ return locked = FALSE interact(null, user) - to_chat(user, "The PDA softly beeps.") - user << browse(null, "window=pda") - master.mode = 0 + to_chat(user, "The computer softly beeps.") return COMPONENT_STOP_RINGTONE_CHANGE // Radio signal responses @@ -353,7 +350,7 @@ /datum/component/uplink/proc/setup_unlock_code() unlock_code = generate_code() var/obj/item/P = parent - if(istype(parent,/obj/item/pda)) + if(istype(parent,/obj/item/modular_computer/tablet)) unlock_note = "Uplink Passcode: [unlock_code] ([P.name])." else if(istype(parent,/obj/item/radio)) unlock_note = "Radio Passcode: [unlock_code] ([P.name] on the :d channel)." @@ -361,7 +358,7 @@ unlock_note = "Uplink Degrees: [english_list(unlock_code)] ([P.name])." /datum/component/uplink/proc/generate_code() - if(istype(parent,/obj/item/pda)) + if(istype(parent,/obj/item/modular_computer/tablet)) return "[random_code(3)] [pick(GLOB.phonetic_alphabet)]" else if(istype(parent,/obj/item/radio)) return "[pick(GLOB.phonetic_alphabet)]" diff --git a/code/datums/mind.dm b/code/datums/mind.dm index cbdd4bed3ad..5804ebd9197 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -284,7 +284,7 @@ return var/list/all_contents = traitor_mob.GetAllContents() - var/obj/item/pda/PDA = locate() in all_contents + var/obj/item/modular_computer/tablet/pda/PDA = locate() in all_contents var/obj/item/radio/R = locate() in all_contents var/obj/item/pen/P @@ -346,7 +346,7 @@ if(uplink_loc == R) to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply speak [U.unlock_code] into the :d channel to unlock its hidden features.") else if(uplink_loc == PDA) - to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[U.unlock_code]\" into the ringtone select to unlock its hidden features.") + to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[U.unlock_code]\" into the ring tone selection to unlock its hidden features.") else if(uplink_loc == P) to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [english_list(U.unlock_code)] from its starting position to unlock its hidden features.") diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index 41caf951879..93160201f68 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -29,7 +29,7 @@ GLOBAL_VAR_INIT(hsboxspawn, TRUE) //items that shouldn't spawn on the floor because they would bug or act weird var/static/list/spawn_forbidden = list( /obj/item/tk_grab, /obj/item/implant, // not implanter, the actual thing that is inside you - /obj/item/assembly, /obj/item/onetankbomb, /obj/item/pda/ai, + /obj/item/assembly, /obj/item/onetankbomb, /obj/item/small_delivery, /obj/item/projectile, /obj/item/borg/sight, /obj/item/borg/stun, /obj/item/robot_module) diff --git a/code/game/machinery/PDApainter.dm b/code/game/machinery/PDApainter.dm index 8c904c3d994..b325b4cd347 100644 --- a/code/game/machinery/PDApainter.dm +++ b/code/game/machinery/PDApainter.dm @@ -1,12 +1,12 @@ /obj/machinery/pdapainter name = "\improper color manipulator" desc = "A machine able to color PDAs and IDs with ease. Insert an ID card or PDA and pick a color scheme." - icon = 'icons/obj/pda.dmi' + icon = 'nsv13/icons/obj/pda.dmi' //NSV13 - old sprites icon_state = "coloriser" max_integrity = 200 density = TRUE anchored = TRUE - var/obj/item/pda/storedpda = null + var/obj/item/modular_computer/tablet/pda/storedpda = null var/obj/item/card/id/storedid = null //Nsv13 - Crayon eaters & MPs var/pda_icons = list( @@ -117,16 +117,14 @@ /obj/machinery/pdapainter/Initialize(mapload) . = ..() var/list/blocked = list( - /obj/item/pda/ai/pai, - /obj/item/pda/ai, - /obj/item/pda/heads, - /obj/item/pda/clear, - /obj/item/pda/syndicate, - /obj/item/pda/chameleon, - /obj/item/pda/chameleon/broken) + /obj/item/modular_computer/tablet/pda/heads, + /obj/item/modular_computer/tablet/pda/clear, + /obj/item/modular_computer/tablet/pda/syndicate, + /obj/item/modular_computer/tablet/pda/chameleon, + /obj/item/modular_computer/tablet/pda/chameleon/broken) - for(var/P in typesof(/obj/item/pda) - blocked) - var/obj/item/pda/D = new P + for(var/P in typesof(/obj/item/modular_computer/tablet/pda) - blocked) + var/obj/item/modular_computer/tablet/pda/D = new P //D.name = "PDA Style [colorlist.len+1]" //Gotta set the name, otherwise it all comes up as "PDA" D.name = D.icon_state //PDAs don't have unique names, but using the sprite names works. @@ -165,7 +163,7 @@ power_change() return - else if(istype(O, /obj/item/pda)) + else if(istype(O, /obj/item/modular_computer/tablet/pda)) if(storedpda) to_chat(user, "There is already a PDA inside!") return diff --git a/code/game/machinery/airlock_cycle_control.dm b/code/game/machinery/airlock_cycle_control.dm index e4563cc07bf..7be45657e23 100644 --- a/code/game/machinery/airlock_cycle_control.dm +++ b/code/game/machinery/airlock_cycle_control.dm @@ -452,7 +452,7 @@ to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"].") update_icon() return - else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda))// trying to unlock the interface with an ID card + else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/modular_computer/tablet/pda))// trying to unlock the interface with an ID card togglelock(user) return else if(panel_open && is_wire_tool(W)) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 9411299fa89..3e1d4b30acf 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -251,21 +251,20 @@ return // OTHER - if((istype(I, /obj/item/paper) || istype(I, /obj/item/pda)) && isliving(user)) + if((istype(I, /obj/item/paper) || istype(I, /obj/item/modular_computer/tablet)) && isliving(user)) var/mob/living/U = user - var/obj/item/paper/X = null - var/obj/item/pda/P = null var/itemname = "" var/info = "" if(istype(I, /obj/item/paper)) - X = I - itemname = X.name - info = X.info - else - P = I - itemname = P.name - info = P.notehtml + var/obj/item/paper/pressed_paper = I + itemname = pressed_paper.name + info = pressed_paper.info + if(istype(I, /obj/item/modular_computer/tablet)) + var/obj/item/modular_computer/tablet/computer = I + itemname = computer.name + info = computer.note + itemname = sanitize(itemname) to_chat(U, "You hold \the [itemname] up to the camera...") U.changeNext_move(CLICK_CD_MELEE) diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 9003b69dd89..cf6bb68113f 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -234,7 +234,7 @@ scantemp = "Cannot delete: Data Corrupted." return FALSE var/obj/item/card/id/C = usr.get_idcard(hand_first = TRUE) - if(istype(C) || istype(C, /obj/item/pda) || istype(C, /obj/item/modular_computer/tablet)) + if(istype(C) || istype(C, /obj/item/modular_computer/tablet)) if(check_access(C)) scantemp = "[GRAB.fields["name"]] => Record deleted." records.Remove(GRAB) diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index eb5152b44d5..60468d9de16 100755 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -1,5 +1,4 @@ #define IMPORTANT_ACTION_COOLDOWN (60 SECONDS) -#define MAX_STATUS_LINE_LENGTH 40 #define STATE_BUYING_SHUTTLE "buying_shuttle" #define STATE_CHANGING_STATUS "changing_status" @@ -80,7 +79,6 @@ /obj/machinery/computer/communications/ui_act(action, list/params) var/static/list/approved_states = list(STATE_BUYING_SHUTTLE, STATE_CHANGING_STATUS, STATE_MESSAGES, STATE_OBJECTIVES) //NSV13 - added objectives - var/static/list/approved_status_pictures = list("biohazard", "blank", "default", "lockdown", "redalert", "shuttle") . = ..() if (.) @@ -296,7 +294,7 @@ if (!authenticated(usr)) return var/picture = params["picture"] - if (!(picture in approved_status_pictures)) + if (!(picture in GLOB.approved_status_pictures)) return post_status("alert", picture) playsound(src, "terminal_type", 50, FALSE) @@ -593,7 +591,6 @@ possible_answers = new_possible_answers #undef IMPORTANT_ACTION_COOLDOWN -#undef MAX_STATUS_LINE_LENGTH #undef STATE_BUYING_SHUTTLE #undef STATE_CHANGING_STATUS #undef STATE_MESSAGES diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index aa116eb0771..b2a27e8edf1 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -870,16 +870,16 @@ What a mess.*/ if(!canUseSecurityRecordsConsole(usr, t1, null, a2)) return - var/crime = GLOB.data_core.createCrimeEntry(t1, "", authenticated, station_time_timestamp(), fine) - for (var/obj/item/pda/P in GLOB.PDAs) - if(P.owner == active1.fields["name"]) + var/datum/data/crime/crime = GLOB.data_core.createCrimeEntry(t1, "", authenticated, station_time_timestamp(), fine) + for (var/obj/item/modular_computer/tablet in GLOB.TabletMessengers) + if(tablet.saved_identification == active1.fields["name"]) var/message = "You have been fined [fine] credits for '[t1]'. Fines may be paid at security." - var/datum/signal/subspace/messaging/pda/signal = new(src, list( + var/datum/signal/subspace/messaging/tablet_msg/signal = new(src, list( "name" = "Security Citation", "job" = "Citation Server", "message" = message, - "targets" = list("[P.owner] ([P.ownjob])"), - "automated" = 1 + "targets" = list(tablet), + "automated" = TRUE )) signal.send_to_receivers() usr.log_message("(PDA: Citation Server) sent \"[message]\" to [signal.format_target()]", LOG_PDA) diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 98d0fb4e213..660d16aa0dd 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -163,7 +163,7 @@ if(operating) return - if(istype(C, /obj/item/pda)) + if(istype(C, /obj/item/modular_computer/tablet/pda)) var/attack_verb = pick("smushes","rubs","smashes","presses","taps") visible_message("[user] [attack_verb] \the [C] against [src]\s card reader.", "You [attack_verb] \the [C] against [src]\s card reader. It doesn't do anything.", "You hear plastic click against metal.") return diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index f626a45a8f9..05204158fe6 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -114,7 +114,7 @@ update_icon() - else if (istype(I, /obj/item/card/id)||istype(I, /obj/item/pda)) + else if (istype(I, /obj/item/card/id) || istype(I, /obj/item/modular_computer/tablet)) if(open) if (src.allowed(user)) src.locked = !src.locked diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 740e5af1c3e..94b1350c49e 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -813,10 +813,11 @@ GLOBAL_LIST_EMPTY(allCasters) if(ishuman(user)) var/mob/living/carbon/human/human_user = user if(human_user.wear_id) - if(istype(human_user.wear_id, /obj/item/pda)) - var/obj/item/pda/P = human_user.wear_id - if(P.id) - scanned_user = "[P.id.registered_name] ([P.id.assignment])" + if(istype(human_user.wear_id, /obj/item/modular_computer/tablet/pda)) + var/obj/item/modular_computer/tablet/pda/P = human_user.wear_id + var/obj/item/card/id/ID = P.GetID() + if(istype(ID)) + scanned_user = "[ID.registered_name] ([ID.assignment])" else scanned_user = "Unknown" else if(istype(human_user.wear_id, /obj/item/card/id) ) diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm index 212729e00c4..ca9f1806ccd 100644 --- a/code/game/machinery/telecomms/computers/message.dm +++ b/code/game/machinery/telecomms/computers/message.dm @@ -2,43 +2,16 @@ The monitoring computer for the messaging server. Lets you read PDA and request console messages. */ - -#define LINKED_SERVER_NONRESPONSIVE (!linkedServer || (linkedServer.machine_stat & (NOPOWER|BROKEN))) - -#define MSG_MON_SCREEN_MAIN 0 -#define MSG_MON_SCREEN_LOGS 1 -#define MSG_MON_SCREEN_HACKED 2 -#define MSG_MON_SCREEN_CUSTOM_MSG 3 -#define MSG_MON_SCREEN_REQUEST_LOGS 4 - -// The monitor itself. /obj/machinery/computer/message_monitor name = "message monitor console" desc = "Used to monitor the crew's PDA messages, as well as request console messages." icon_screen = "comm_logs" circuit = /obj/item/circuitboard/computer/message_monitor - //Server linked to. - var/obj/machinery/telecomms/message_server/linkedServer = null - //Sparks effect - For emag - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread - //Messages - Saves me time if I want to change something. - var/noserver = "ALERT: No server detected." - var/incorrectkey = "ALERT: Incorrect decryption key!" - var/defaultmsg = "Welcome. Please select an option." - var/rebootmsg = "%$&(£: Critical %$$@ Error // !RestArting! - ?pLeaSe wAit!" - //Computer properties - var/screen = MSG_MON_SCREEN_MAIN // 0 = Main menu, 1 = Message Logs, 2 = Hacked screen, 3 = Custom Message - var/hacking = FALSE // Is it being hacked into by the AI/Cyborg - var/message = "System bootup complete. Please select an option." // The message that shows on the main menu. - var/auth = FALSE // Are they authenticated? - var/optioncount = 7 - // Custom Message Properties - var/customsender = "System Administrator" - var/obj/item/pda/customrecepient = null - var/customjob = "Admin" - var/custommessage = "This is a test, please ignore." - light_color = LIGHT_COLOR_GREEN + /// Message server selected to receive data from + var/obj/machinery/telecomms/message_server/linked_server + /// If the console is currently being hacked by a silicon + var/hacking = FALSE /obj/machinery/computer/message_monitor/attackby(obj/item/O, mob/living/user, params) if(O.tool_behaviour == TOOL_SCREWDRIVER && (obj_flags & EMAGGED)) @@ -50,19 +23,32 @@ /obj/machinery/computer/message_monitor/emag_act(mob/user) if(obj_flags & EMAGGED) return - if(!isnull(linkedServer)) + if(!linked_server) obj_flags |= EMAGGED - screen = MSG_MON_SCREEN_HACKED - spark_system.set_up(5, 0, src) - spark_system.start() - var/obj/item/paper/monitorkey/MK = new(loc, linkedServer) - // Will help make emagging the console not so easy to get away with. + to_chat(user, "A 'no server detected' error appears on the screen.") + return + else + ui_update() + do_sparks(5, FALSE, src) + addtimer(CALLBACK(src, PROC_REF(after_emag)), 10 * length(linked_server.decryptkey) SECONDS) + +/obj/machinery/computer/message_monitor/proc/after_emag() + // Print an "error" decryption key, leaving physical evidence of the hack. + if(linked_server) + var/obj/item/paper/monitorkey/MK = new(loc, linked_server) MK.info += "

£%@%(*$%&(£&?*(%&£/{}" - var/time = 100 * length(linkedServer.decryptkey) - addtimer(CALLBACK(src, PROC_REF(UnmagConsole)), time) - message = rebootmsg else - to_chat(user, "A no server error appears on the screen.") + say("Error: Server link lost!") + obj_flags &= ~EMAGGED + ui_update() + +/obj/machinery/computer/message_monitor/proc/finish_hack(mob/living/silicon/user) + hacking = FALSE + ui_update() + if(!linked_server) + to_chat(user, "Could not complete brute-force: Linked Server Disconnected!") + return + to_chat(user, "Brute-force completed! The decryption key is '[linked_server.decryptkey]'.") /obj/machinery/computer/message_monitor/New() ..() @@ -73,384 +59,222 @@ return INITIALIZE_HINT_LATELOAD /obj/machinery/computer/message_monitor/LateInitialize() - //Is the server isn't linked to a server, and there's a server available, default it to the first one in the list. - if(!linkedServer) + //If the server isn't linked to a server, and there's a server available, default it to the first one in the list. + if(!linked_server) for(var/obj/machinery/telecomms/message_server/S in GLOB.telecomms_list) - linkedServer = S + set_linked_server(S) break +/obj/machinery/computer/message_monitor/proc/set_linked_server(var/obj/machinery/telecomms/message_server/server) + if(linked_server) + UnregisterSignal(linked_server, COMSIG_PARENT_QDELETING) + if(server != linked_server) + authenticated = FALSE + linked_server = server + if(server) + RegisterSignal(server, COMSIG_PARENT_QDELETING, PROC_REF(server_deleting)) + ui_update() + +/obj/machinery/computer/message_monitor/proc/server_deleting() + set_linked_server(null) + /obj/machinery/computer/message_monitor/Destroy() GLOB.telecomms_list -= src + set_linked_server(null) return ..() -/obj/machinery/computer/message_monitor/ui_interact(mob/living/user) +/obj/machinery/computer/message_monitor/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/chat), + ) + +/obj/machinery/computer/message_monitor/ui_static_data(mob/user) + var/list/data = list() + data["emoji_names"] = icon_states('icons/emoji.dmi') + return data + +/obj/machinery/computer/message_monitor/ui_data(mob/user) + var/list/data = ..() + data["server_on"] = linked_server?.on + data["authenticated"] = authenticated + data["hacking"] = hacking || (obj_flags & EMAGGED) + var/mob/living/silicon/S = user + data["can_hack"] = istype(S) && S.hack_software + var/no_server = !linked_server || (linked_server.machine_stat & (NOPOWER|BROKEN)) + data["no_server"] = no_server + if(no_server || !authenticated) + return data + var/list/pda_messages = list() + for(var/datum/data_tablet_msg/message in linked_server.modular_msgs) + var/list/message_data = list() + var/datum/picture/pic = message.picture + if(istype(pic)) + message_data["photo"] = pda_rsc_image(pic, "[REF(message)]", user) + message_data["photo_width"] = pic.psize_x + message_data["photo_height"] = pic.psize_y + message_data["sender"] = message.sender + message_data["recipient"] = message.recipient + message_data["contents"] = message.message + message_data["emojis"] = message.emojis + message_data["ref"] = REF(message) + pda_messages += list(message_data) + data["pda_messages"] = pda_messages + var/list/request_messages = list() + for(var/datum/data_rc_msg/req in linked_server.rc_msgs) + request_messages += list(list( + "sending_department" = req.send_dpt, + "receiving_department" = req.rec_dpt, + "stamp" = req.stamp, + "id_auth" = req.id_auth, + "priority" = req.priority, + "message" = req.message, + "ref" = REF(req), + )) + data["request_messages"] = request_messages + return data + +/obj/machinery/computer/message_monitor/ui_act(action, params) . = ..() - //If the computer is being hacked or is emagged, display the reboot message. - if(hacking || (obj_flags & EMAGGED)) - message = rebootmsg - var/dat = "
" - - if(auth) - dat += "

\[Authenticated\] /" - dat += " Server Power: [linkedServer && linkedServer.on ? "\[On\]":"\[Off\]"]

" - else - dat += "

\[Unauthenticated\] /" - dat += " Server Power: [linkedServer && linkedServer.on ? "\[On\]":"\[Off\]"]

" - - if(hacking || (obj_flags & EMAGGED)) - screen = MSG_MON_SCREEN_HACKED - else if(!auth || LINKED_SERVER_NONRESPONSIVE) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - screen = MSG_MON_SCREEN_MAIN - - switch(screen) - //Main menu - if(MSG_MON_SCREEN_MAIN) - // = TAB - var/i = 0 - dat += "
[++i]. Link To A Server
" - if(auth) - if(LINKED_SERVER_NONRESPONSIVE) - dat += "
ERROR: Server not found!
" - else - dat += "
[++i]. View Message Logs
" - dat += "
[++i]. View Request Console Logs
" - dat += "
[++i]. Clear Message Logs
" - dat += "
[++i]. Clear Request Console Logs
" - dat += "
[++i]. Set Custom Key
" - dat += "
[++i]. Send Admin Message
" + if(.) + return TRUE + switch(action) + if("login") + if(!usr || authenticated) + return TRUE + if(!linked_server) + to_chat(usr, "The console flashes a message: 'ERROR: Server connection lost.'") + return TRUE + var/dkey = capped_input(usr, "Please enter the decryption key.") + if(dkey && linked_server.decryptkey == dkey) + authenticated = TRUE else - for(var/n = ++i; n <= optioncount; n++) - dat += "
[n]. ---------------
" + to_chat(usr, "The console flashes a message: 'ALERT: Incorrect decryption key!'") + return TRUE + if("logout") + authenticated = FALSE + return TRUE + if("hack") var/mob/living/silicon/S = usr - if(istype(S) && S.hack_software) - //Malf/Traitor AIs can bruteforce into the system to gain the Key. - dat += "
*&@#. Bruteforce Key
" - else - dat += "
" - - //Bottom message - if(!auth) - dat += "

Please authenticate with the server in order to show additional options." - else - dat += "

Reg, #514 forbids sending messages to a Head of Staff containing Erotic Rendering Properties." - - //Message Logs - if(MSG_MON_SCREEN_LOGS) - var/index = 0 - dat += "
Back - Refresh

" - dat += "" - for(var/datum/data_pda_msg/pda in linkedServer.pda_msgs) - index++ - if(index > 3000) - break - // Del - Sender - Recepient - Message - // X - Al Green - Your Mom - WHAT UP!? - dat += "" - dat += "
XSenderRecipientMessage
X
[pda.sender][pda.recipient][pda.message][pda.picture ? " (Photo)":""]
" - //Hacking screen. - if(MSG_MON_SCREEN_HACKED) - if(isAI(user) || iscyborg(user)) - dat += "Brute-forcing for server key.
It will take 20 seconds for every character that the password has." - dat += "In the meantime, this console can reveal your true intentions if you let someone access it. Make sure no humans enter the room during that time." - else - //It's the same message as the one above but in binary. Because robots understand binary and humans don't... well I thought it was clever. - dat += {"01000010011100100111010101110100011001010010110
- 10110011001101111011100100110001101101001011011100110011
- 10010000001100110011011110111001000100000011100110110010
- 10111001001110110011001010111001000100000011010110110010
- 10111100100101110001000000100100101110100001000000111011
- 10110100101101100011011000010000001110100011000010110101
- 10110010100100000001100100011000000100000011100110110010
- 10110001101101111011011100110010001110011001000000110011
- 00110111101110010001000000110010101110110011001010111001
- 00111100100100000011000110110100001100001011100100110000
- 10110001101110100011001010111001000100000011101000110100
- 00110000101110100001000000111010001101000011001010010000
- 00111000001100001011100110111001101110111011011110111001
- 00110010000100000011010000110000101110011001011100010000
- 00100100101101110001000000111010001101000011001010010000
- 00110110101100101011000010110111001110100011010010110110
- 10110010100101100001000000111010001101000011010010111001
- 10010000001100011011011110110111001110011011011110110110
- 00110010100100000011000110110000101101110001000000111001
- 00110010101110110011001010110000101101100001000000111100
- 10110111101110101011100100010000001110100011100100111010
- 10110010100100000011010010110111001110100011001010110111
- 00111010001101001011011110110111001110011001000000110100
- 10110011000100000011110010110111101110101001000000110110
- 00110010101110100001000000111001101101111011011010110010
- 10110111101101110011001010010000001100001011000110110001
- 10110010101110011011100110010000001101001011101000010111
- 00010000001001101011000010110101101100101001000000111001
- 10111010101110010011001010010000001101110011011110010000
- 00110100001110101011011010110000101101110011100110010000
- 00110010101101110011101000110010101110010001000000111010
- 00110100001100101001000000111001001101111011011110110110
- 10010000001100100011101010111001001101001011011100110011
- 10010000001110100011010000110000101110100001000000111010
- 001101001011011010110010100101110"} - - //Fake messages - if(MSG_MON_SCREEN_CUSTOM_MSG) - dat += "
Back - Reset

" - - dat += {" - - - - "} - //Sender - Sender's Job - Recepient - Message - //Al Green- Your Dad - Your Mom - WHAT UP!? - - dat += {" - - - "} - dat += "
SenderSender's JobRecipientMessage
[customsender][customjob][customrecepient ? customrecepient.owner : "NONE"][custommessage]

Send" - - //Request Console Logs - if(MSG_MON_SCREEN_REQUEST_LOGS) - - var/index = 0 - /* data_rc_msg - X - 5% - var/rec_dpt = "Unspecified" //name of the person - 15% - var/send_dpt = "Unspecified" //name of the sender- 15% - var/message = "Blank" //transferred message - 300px - var/stamp = "Unstamped" - 15% - var/id_auth = "Unauthenticated" - 15% - var/priority = "Normal" - 10% - */ - dat += "
Back - Refresh

" - dat += {" - "} - for(var/datum/data_rc_msg/rc in linkedServer.rc_msgs) - index++ - if(index > 3000) - break - // Del - Sender - Recepient - Message - // X - Al Green - Your Mom - WHAT UP!? - dat += {" - "} - dat += "
XSending Dep.Receiving Dep.MessageStampID Auth.Priority.
X
[rc.send_dpt][rc.rec_dpt][rc.message][rc.stamp][rc.id_auth][rc.priority]
" - - message = defaultmsg - var/datum/browser/popup = new(user, "hologram_console", name, 700, 700) - popup.set_content(dat) - popup.open() - -/obj/machinery/computer/message_monitor/proc/BruteForce(mob/user) - if(isnull(linkedServer)) - to_chat(user, "Could not complete brute-force: Linked Server Disconnected!") - else - var/currentKey = linkedServer.decryptkey - to_chat(user, "Brute-force completed! The key is '[currentKey]'.") - hacking = FALSE - screen = MSG_MON_SCREEN_MAIN // Return the screen back to normal - -/obj/machinery/computer/message_monitor/proc/UnmagConsole() - obj_flags &= ~EMAGGED - -/obj/machinery/computer/message_monitor/proc/ResetMessage() - customsender = "System Administrator" - customrecepient = null - custommessage = "This is a test, please ignore." - customjob = "Admin" - -/obj/machinery/computer/message_monitor/Topic(href, href_list) - if(..()) - return - - if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr)) - //Authenticate - if (href_list["auth"]) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - else if(auth) - auth = FALSE - screen = MSG_MON_SCREEN_MAIN - else - var/dkey = capped_input(usr, "Please enter the decryption key.") - if(dkey && dkey != "") - if(linkedServer.decryptkey == dkey) - auth = TRUE - else - message = incorrectkey - - //Turn the server on/off. - if (href_list["active"]) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - else if(auth) - linkedServer.toggled = !linkedServer.toggled - //Find a server - if (href_list["find"]) + if(!istype(S) || !S.hack_software) + return TRUE + if(!linked_server) + to_chat(S, "The console flashes a message: 'ERROR: Server connection lost.'") + return TRUE + hacking = TRUE + var/duration = 10 * length(linked_server.decryptkey) SECONDS + var/approx_duration = max(duration + rand(-20, 20), 1) + to_chat(S, "Brute-force decryption started. This will take approximately [DisplayTimeText(approx_duration, round_seconds_to = 10)].") + addtimer(CALLBACK(src, PROC_REF(finish_hack), S), duration) + return TRUE + if("link") var/list/message_servers = list() + var/obj/machinery/telecomms/message_server/last for (var/obj/machinery/telecomms/message_server/M in GLOB.telecomms_list) - message_servers += M - - if(message_servers.len > 1) - linkedServer = input(usr, "Please select a server.", "Select a server.", null) as null|anything in message_servers - message = "NOTICE: Server selected." - else if(message_servers.len > 0) - linkedServer = message_servers[1] - message = "NOTICE: Only Single Server Detected - Server selected." + var/key_base = "[M.network] - [M.name]" + var/key = key_base + var/number = 1 + while(key in message_servers) + key = key_base + " ([number])" + number++ + message_servers[key] = M + last = M + + if(length(message_servers) > 1) + var/choice = input(usr, "Please select a server.", "Select a server.", null) as null|anything in message_servers + if(choice in message_servers) + set_linked_server(message_servers[choice]) + else + set_linked_server(null) + else if(length(message_servers) == 1) + set_linked_server(last) else - message = noserver - - //View the logs - KEY REQUIRED - if (href_list["view_logs"]) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - else if(auth) - screen = MSG_MON_SCREEN_LOGS - - //Clears the logs - KEY REQUIRED - if (href_list["clear_logs"]) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - else if(auth) - linkedServer.pda_msgs = list() - message = "NOTICE: Logs cleared." - //Clears the request console logs - KEY REQUIRED - if (href_list["clear_requests"]) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - else if(auth) - linkedServer.rc_msgs = list() - message = "NOTICE: Logs cleared." - //Change the password - KEY REQUIRED - if (href_list["pass"]) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - else if(auth) - var/dkey = stripped_input(usr, "Please enter the decryption key.") - if(dkey && dkey != "") - if(linkedServer.decryptkey == dkey) - var/newkey = trim(input(usr,"Please enter the new key (3 - 16 characters max):")) - if(length(newkey) <= 3) - message = "NOTICE: Decryption key too short!" - else if(length(newkey) > 16) - message = "NOTICE: Decryption key too long!" - else if(newkey && newkey != "") - linkedServer.decryptkey = newkey - message = "NOTICE: Decryption key set." - else - message = incorrectkey - - //Hack the Console to get the password - if (href_list["hack"]) - var/mob/living/silicon/S = usr - if(istype(S) && S.hack_software) - hacking = TRUE - screen = MSG_MON_SCREEN_HACKED - //Time it takes to bruteforce is dependant on the password length. - spawn(100*length(linkedServer.decryptkey)) - if(src && linkedServer && usr) - BruteForce(usr) - //Delete the log. - if (href_list["delete_logs"]) - //Are they on the view logs screen? - if(screen == MSG_MON_SCREEN_LOGS) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - else //if(istype(href_list["delete_logs"], /datum/data_pda_msg)) - linkedServer.pda_msgs -= locate(href_list["delete_logs"]) in linkedServer.pda_msgs - message = "NOTICE: Log Deleted!" - //Delete the request console log. - if (href_list["delete_requests"]) - //Are they on the view logs screen? - if(screen == MSG_MON_SCREEN_REQUEST_LOGS) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - else //if(istype(href_list["delete_logs"], /datum/data_pda_msg)) - linkedServer.rc_msgs -= locate(href_list["delete_requests"]) in linkedServer.rc_msgs - message = "NOTICE: Log Deleted!" - //Create a custom message - if (href_list["msg"]) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - else if(auth) - screen = MSG_MON_SCREEN_CUSTOM_MSG - //Fake messaging selection - KEY REQUIRED - if (href_list["select"]) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - screen = MSG_MON_SCREEN_MAIN + set_linked_server(null) + return TRUE + if("power") + if(!authenticated) + return TRUE + if(!linked_server) + to_chat(usr, "The console flashes a message: 'ERROR: Server connection lost.'") + return TRUE + linked_server.toggled = !linked_server.toggled + // Trigger this immediately or hte UI will not update properly... wow this is a dumb proc + linked_server.update_power() + return TRUE + if("reset_key") + if(!usr || !authenticated) + return TRUE + if(!linked_server) + to_chat(usr, "The console flashes a message: 'ERROR: Server connection lost.'") + return TRUE + var/dkey = capped_input(usr, "Please enter the decryption key.") + if(!dkey) + return + if(linked_server.decryptkey == dkey) + var/newkey = capped_input(usr, "Please enter the new key (4-16 characters):") + if(length(newkey) < 4) + to_chat(usr, "The console flashes a message: 'NOTICE: Decryption key too short!'") + else if(length(newkey) > 16) + to_chat(usr, "The console flashes a message: 'NOTICE: Decryption key too long!'") + else if(newkey && newkey != "") + linked_server.decryptkey = newkey + to_chat(usr, "The console flashes a message: 'NOTICE: Decryption key set.'") else - switch(href_list["select"]) - - //Reset - if("Reset") - ResetMessage() - - //Select Your Name - if("Sender") - customsender = stripped_input(usr, "Please enter the sender's name.") || customsender - - //Select Receiver - if("Recepient") - //Get out list of viable PDAs - var/list/obj/item/pda/sendPDAs = get_viewable_pdas() - if(GLOB.PDAs && GLOB.PDAs.len > 0) - customrecepient = input(usr, "Select a PDA from the list.") as null|anything in sendPDAs - else - customrecepient = null - - //Enter custom job - if("RecJob") - customjob = stripped_input(usr, "Please enter the sender's job.") || customjob - - //Enter message - if("Message") - custommessage = stripped_input(usr, "Please enter your message.") || custommessage - - //Send message - if("Send") - if(isnull(customsender) || customsender == "") - customsender = "UNKNOWN" - - if(isnull(customrecepient)) - message = "NOTICE: No recepient selected!" - return attack_hand(usr) - - if(isnull(custommessage) || custommessage == "") - message = "NOTICE: No message entered!" - return attack_hand(usr) - - var/datum/signal/subspace/messaging/pda/signal = new(src, list( - "name" = "[customsender]", - "job" = "[customjob]", - "message" = custommessage, - "targets" = list("[customrecepient.owner] ([customrecepient.ownjob])") - )) - // this will log the signal and transmit it to the target - linkedServer.receive_information(signal, null) - usr.log_message("(PDA: [name] | [usr.real_name]) sent \"[custommessage]\" to [signal.format_target()]", LOG_PDA) - - - //Request Console Logs - KEY REQUIRED - if(href_list["view_requests"]) - if(LINKED_SERVER_NONRESPONSIVE) - message = noserver - else if(auth) - screen = MSG_MON_SCREEN_REQUEST_LOGS - - if (href_list["back"]) - screen = MSG_MON_SCREEN_MAIN - - return attack_hand(usr) - -#undef MSG_MON_SCREEN_MAIN -#undef MSG_MON_SCREEN_LOGS -#undef MSG_MON_SCREEN_HACKED -#undef MSG_MON_SCREEN_CUSTOM_MSG -#undef MSG_MON_SCREEN_REQUEST_LOGS - -#undef LINKED_SERVER_NONRESPONSIVE - + to_chat(usr,"The console flashes a message: 'ALERT: Incorrect decryption key!'") + if("clear_logs") + var/type = params["type"] + if(!usr || !authenticated || (type != "pda" && type != "request")) + return TRUE + if(!linked_server) + to_chat(usr, "The console flashes a message: 'ERROR: Server connection lost.'") + return TRUE + if(type == "request") + linked_server.rc_msgs.Cut() + else + linked_server.modular_msgs.Cut() + to_chat(usr, "The console flashes a message: 'NOTICE: Logs cleared.'") + var/turf/the_turf = get_turf(src) + usr.log_message("cleared [type] logs using [src] at [AREACOORD(the_turf)]", LOG_GAME) + message_admins("[ADMIN_FLW(usr)] cleared [type] logs using [src] at [ADMIN_VERBOSEJMP(the_turf)]") + return TRUE + if("delete_log") + var/ref = params["ref"] + var/type = params["type"] + if(!usr || !authenticated || (type != "pda" && type != "request") || !ref) + return TRUE + if(!linked_server) + to_chat(usr, "The console flashes a message: 'ERROR: Server connection lost.'") + return TRUE + var/list/target = type == "request" ? linked_server.rc_msgs : linked_server.modular_msgs + var/datum/entry = locate(ref) in target + if(!entry) + return + target -= entry + var/msg = "" + if(istype(entry, /datum/data_tablet_msg)) + var/datum/data_tablet_msg/pda_entry = entry + msg = "[pda_entry.sender] to [pda_entry.recipient]: [pda_entry.message]" + else if(istype(entry, /datum/data_rc_msg)) + var/datum/data_rc_msg/rc_entry = entry + msg = "[rc_entry.send_dpt] to [rc_entry.rec_dpt] PRIORITY [rc_entry.priority] AUTH [rc_entry.id_auth] STAMP [rc_entry.stamp]: [rc_entry.message]" + to_chat(usr, "The console flashes a message: 'NOTICE: Log entry deleted.'") + var/turf/the_turf = get_turf(src) + usr.log_message("cleared [type] log entry \"[msg]\" using [src] at [AREACOORD(the_turf)]", LOG_GAME) + message_admins("[key_name_admin(usr)][ADMIN_FLW(usr)] deleted [type] log entry \"[msg]\" using [src] at [ADMIN_VERBOSEJMP(the_turf)]") + return TRUE + if("admin_message") + if(!usr || !authenticated) + return TRUE + if(!linked_server) + to_chat(usr, "The console flashes a message: 'ERROR: Server connection lost.'") + return TRUE + tgui_send_admin_pda(usr, src, linked_server) + +/obj/machinery/computer/message_monitor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "MessageMonitor") + ui.open() + ui.set_autoupdate(TRUE) /obj/item/paper/monitorkey name = "monitor decryption key" @@ -463,7 +287,21 @@ return INITIALIZE_HINT_LATELOAD /obj/item/paper/monitorkey/proc/print(obj/machinery/telecomms/message_server/server) - info = "

Daily Key Reset


The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from the clown.
If necessary, change the password to a more secure one." + info = "

Telecommunications Security Notice


\ +
INCOMING TRANSMISSION - KEY RESET REPORT

\ +

\ +

\
+	REPORT: PREVIOUS SHIFT DATA WIPED.
\ + KEY UPDATED.
\ +
\ + Monitor Decryption Key: [server.decryptkey]\ +

\ +

\
+	PLEASE MAXIMIZE KEY SECURITY.
\ + UPDATE KEY IF NECESSARY.
\ + TRANSMISSION END.
\ + SENDER: CentCom Telecommunications Data Retention\ +

" add_overlay("paper_words") /obj/item/paper/monitorkey/LateInitialize() diff --git a/code/game/machinery/telecomms/machines/message_server.dm b/code/game/machinery/telecomms/machines/message_server.dm index 5eccac38333..4cc0a278b90 100644 --- a/code/game/machinery/telecomms/machines/message_server.dm +++ b/code/game/machinery/telecomms/machines/message_server.dm @@ -80,7 +80,7 @@ active_power_usage = 100 circuit = /obj/item/circuitboard/machine/telecomms/message_server - var/list/datum/data_pda_msg/pda_msgs = list() + var/list/datum/data_tablet_msg/modular_msgs = list() var/list/datum/data_rc_msg/rc_msgs = list() var/decryptkey = "password" var/calibrating = 15 MINUTES //Init reads this and adds world.time, then becomes 0 when that time has passed and the machine works @@ -93,15 +93,9 @@ if (calibrating) calibrating += world.time say("Calibrating... Estimated wait time: [rand(3, 9)] minutes.") - pda_msgs += new /datum/data_pda_msg("System Administrator", "system", "This is an automated message. System calibration started at [station_time_timestamp()]") + modular_msgs += new /datum/data_tablet_msg("System Administrator", "system", "This is an automated message. System calibration started at [station_time_timestamp()].") else - pda_msgs += new /datum/data_pda_msg("System Administrator", "system", MESSAGE_SERVER_FUNCTIONING_MESSAGE) - -/obj/machinery/telecomms/message_server/Destroy() - for(var/obj/machinery/computer/message_monitor/monitor in GLOB.telecomms_list) - if(monitor.linkedServer && monitor.linkedServer == src) - monitor.linkedServer = null - . = ..() + modular_msgs += new /datum/data_tablet_msg("System Administrator", "system", MESSAGE_SERVER_FUNCTIONING_MESSAGE) /obj/machinery/telecomms/message_server/examine(mob/user) . = ..() @@ -119,7 +113,7 @@ . = ..() if(calibrating && calibrating <= world.time) calibrating = 0 - pda_msgs += new /datum/data_pda_msg("System Administrator", "system", MESSAGE_SERVER_FUNCTIONING_MESSAGE) + modular_msgs += new /datum/data_tablet_msg("System Administrator", "system", MESSAGE_SERVER_FUNCTIONING_MESSAGE) /obj/machinery/telecomms/message_server/receive_information(datum/signal/subspace/messaging/signal, obj/machinery/telecomms/machine_from) // can't log non-message signals @@ -127,16 +121,16 @@ return // log the signal - if(istype(signal, /datum/signal/subspace/messaging/pda)) - var/datum/signal/subspace/messaging/pda/PDAsignal = signal - var/datum/data_pda_msg/M = new(PDAsignal.format_target(), "[PDAsignal.data["name"]] ([PDAsignal.data["job"]])", PDAsignal.data["message"], PDAsignal.data["photo"]) - pda_msgs += M - signal.logged = M + if(istype(signal, /datum/signal/subspace/messaging/tablet_msg)) + var/datum/signal/subspace/messaging/tablet_msg/PDAsignal = signal + var/datum/data_tablet_msg/msg = new(PDAsignal.format_target(), "[PDAsignal.data["name"]] ([PDAsignal.data["job"]])", PDAsignal.data["message"], PDAsignal.data["photo"], PDAsignal.data["emojis"]) + modular_msgs += msg + signal.logged = msg else if(istype(signal, /datum/signal/subspace/messaging/rc)) - var/datum/data_rc_msg/M = new(signal.data["rec_dpt"], signal.data["send_dpt"], signal.data["message"], signal.data["stamped"], signal.data["verified"], signal.data["priority"]) - signal.logged = M + var/datum/data_rc_msg/msg = new(signal.data["rec_dpt"], signal.data["send_dpt"], signal.data["message"], signal.data["stamped"], signal.data["verified"], signal.data["priority"]) + signal.logged = msg if(signal.data["send_dpt"]) // don't log messages not from a department but allow them to work - rc_msgs += M + rc_msgs += msg signal.data["reject"] = FALSE // pass it along to either the hub or the broadcaster @@ -170,23 +164,25 @@ copy.levels = levels return copy -// PDA signal datum -/datum/signal/subspace/messaging/pda/proc/format_target() +// Tablet message signal datum +/datum/signal/subspace/messaging/tablet_msg/proc/format_target() if (length(data["targets"]) > 1) return "Everyone" - return data["targets"][1] + var/obj/item/modular_computer/target = data["targets"][1] + return "[target.saved_identification] ([target.saved_job])" -/datum/signal/subspace/messaging/pda/proc/format_message() - if (logged && data["photo"]) +/datum/signal/subspace/messaging/tablet_msg/proc/format_message(include_photo = FALSE) + if (include_photo && logged && data["photo"]) return "\"[data["message"]]\" (Photo)" return "\"[data["message"]]\"" -/datum/signal/subspace/messaging/pda/broadcast() +/datum/signal/subspace/messaging/tablet_msg/broadcast() if (!logged) // Can only go through if a message server logs it return - for (var/obj/item/pda/P in GLOB.PDAs) - if ("[P.owner] ([P.ownjob])" in data["targets"]) - P.receive_message(src) + for (var/obj/item/modular_computer/comp in data["targets"]) + var/obj/item/computer_hardware/hard_drive/drive = comp.all_components[MC_HDD] + for(var/datum/computer_file/program/messenger/app in drive.stored_files) + app.receive_message(src) // Request Console signal datum /datum/signal/subspace/messaging/rc/broadcast() @@ -198,14 +194,16 @@ Console.createmessage(data["sender"], data["send_dpt"], data["message"], data["verified"], data["stamped"], data["priority"], data["notify_freq"]) // Log datums stored by the message server. -/datum/data_pda_msg +/datum/data_tablet_msg var/sender = "Unspecified" var/recipient = "Unspecified" var/message = "Blank" // transferred message var/datum/picture/picture // attached photo - var/automated = 0 //automated message + var/automated = FALSE //automated message + /// If this message is allowed to render emojis + var/emojis = FALSE -/datum/data_pda_msg/New(param_rec, param_sender, param_message, param_photo) +/datum/data_tablet_msg/New(param_rec, param_sender, param_message, param_photo, param_emojis) if(param_rec) recipient = param_rec if(param_sender) @@ -214,16 +212,18 @@ message = param_message if(param_photo) picture = param_photo + if(param_emojis) + emojis = param_emojis -/datum/data_pda_msg/Topic(href,href_list) +/datum/data_tablet_msg/Topic(href,href_list) ..() if(href_list["photo"]) var/mob/M = usr M << browse_rsc(picture.picture_image, "pda_photo.png") M << browse("PDA Photo" \ + "" \ - + "" \ - + "", "window=pdaphoto;size=[picture.psize_x]x[picture.psize_y];can-close=true") + + "" \ + + "", "window=photo_showing;size=480x608") onclose(M, "pdaphoto") /datum/data_rc_msg diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm index 857f211be6b..8e3522ee82f 100644 --- a/code/game/mecha/mecha_defense.dm +++ b/code/game/mecha/mecha_defense.dm @@ -188,13 +188,7 @@ if(W.GetID()) if(add_req_access || maint_access) if(internals_access_allowed(user)) - var/obj/item/card/id/id_card - if(istype(W, /obj/item/card/id)) - id_card = W - else - var/obj/item/pda/pda = W - id_card = pda.id - output_maintenance_dialog(id_card, user) + output_maintenance_dialog(W.GetID(), user) return to_chat(user, "Invalid ID: Access denied.") return diff --git a/code/game/objects/effects/spawners/mailspawner.dm b/code/game/objects/effects/spawners/mailspawner.dm index 18390702eaf..75997c67f01 100644 --- a/code/game/objects/effects/spawners/mailspawner.dm +++ b/code/game/objects/effects/spawners/mailspawner.dm @@ -159,8 +159,8 @@ /obj/item/gps/science, /obj/item/inducer/sci, /obj/item/megaphone, - /obj/item/pda/roboticist, - /obj/item/pda/toxins, + /obj/item/modular_computer/tablet/pda/roboticist, + /obj/item/modular_computer/tablet/pda/science, /obj/item/anomaly_neutralizer, /obj/item/shuttle_creator, /obj/item/soap, diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index b6979fbd49c..7d38969757d 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -163,15 +163,9 @@ registered_account.payday(1) /obj/item/card/id/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/holochip)) + if(iscash(W)) insert_money(W, user) return - else if(istype(W, /obj/item/stack/spacecash)) - insert_money(W, user, TRUE) - return - else if(istype(W, /obj/item/coin)) - insert_money(W, user, TRUE) - return else if(istype(W, /obj/item/storage/bag/money)) var/obj/item/storage/bag/money/money_bag = W var/list/money_contained = money_bag.contents @@ -184,7 +178,7 @@ else return ..() -/obj/item/card/id/proc/insert_money(obj/item/I, mob/user, physical_currency) +/obj/item/card/id/proc/insert_money(obj/item/I, mob/user) if(!registered_account) to_chat(user, "[src] doesn't have a linked account to deposit [I] into!") return @@ -194,7 +188,7 @@ return registered_account.adjust_money(cash_money) - if(physical_currency) + if(istype(I, /obj/item/stack/spacecash) || istype(I, /obj/item/coin)) to_chat(user, "You stuff [I] into [src]. It disappears in a small puff of bluespace smoke, adding [cash_money] credits to the linked account.") else to_chat(user, "You insert [I] into [src], adding [cash_money] credits to the linked account.") diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index a36fa4e4fc1..d3b73ee56af 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -493,7 +493,9 @@ CIGARETTE PACKETS ARE IN FANCY.DM smoketime = 0 chem_volume = 100 list_reagents = null - var/packeditem = 0 + w_class = WEIGHT_CLASS_SMALL + /// Name of the stuff packed inside this pipe + var/packeditem /obj/item/clothing/mask/cigarette/pipe/Initialize(mapload) . = ..() diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index 70975034d86..ddd40cb655e 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -558,7 +558,7 @@ /obj/machinery/vending/hydroseeds = "MegaSeed Servitor", /obj/machinery/vending/sustenance = "Sustenance Vendor", /obj/machinery/vending/dinnerware = "Plasteel Chef's Dinnerware Vendor", - /obj/machinery/vending/cart = "PTech", + /obj/machinery/vending/job_disk = "PTech", /obj/machinery/vending/robotics = "Robotech Deluxe", /obj/machinery/vending/engineering = "Robco Tool Maker", /obj/machinery/vending/sovietsoda = "BODA", diff --git a/code/game/objects/items/colorizers/items.dm b/code/game/objects/items/colorizers/items.dm index 727611cab07..60a40dfdd16 100644 --- a/code/game/objects/items/colorizers/items.dm +++ b/code/game/objects/items/colorizers/items.dm @@ -1,5 +1,5 @@ /obj/item/colorizer/pdatransparent name = "Transparent PDA Colorizer" - allowed_targets = list(/obj/item/pda) - forbidden_targets = list(/obj/item/pda/syndicate, /obj/item/pda/clown) + allowed_targets = list(/obj/item/modular_computer/tablet/pda) + forbidden_targets = list(/obj/item/modular_computer/tablet/pda/syndicate, /obj/item/modular_computer/tablet/pda/clown) apply_icon_state = "pda-clear" diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 37451c3d5b3..70c7bfc29ac 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -580,6 +580,7 @@ lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi' desc = "A metallic container containing tasty paint." + w_class = WEIGHT_CLASS_SMALL instant = TRUE edible = FALSE diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm deleted file mode 100644 index 7b10db3e921..00000000000 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ /dev/null @@ -1,1248 +0,0 @@ - -//The advanced pea-green monochrome lcd of tomorrow. - -GLOBAL_LIST_EMPTY(PDAs) - -#define PDA_SCANNER_NONE 0 -#define PDA_SCANNER_MEDICAL 1 -#define PDA_SCANNER_FORENSICS 2 //unused -#define PDA_SCANNER_REAGENT 3 -#define PDA_SCANNER_HALOGEN 4 -#define PDA_SCANNER_GAS 5 -#define PDA_SPAM_DELAY 1 MINUTES -#define PDA_TOGGLE_ON "On" -#define PDA_TOGGLE_OFF "Off" - -/obj/item/pda - name = "\improper PDA" - desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." - icon = 'nsv13/icons/obj/pda.dmi' //NSV13 - old sprites - icon_state = "pda" - item_state = "electronic" - worn_icon_state = "electronic" - lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' - item_flags = NOBLUDGEON - w_class = WEIGHT_CLASS_TINY - slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT - actions_types = list(/datum/action/item_action/toggle_light) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100, "stamina" = 0) - resistance_flags = FIRE_PROOF | ACID_PROOF - light_system = MOVABLE_LIGHT - light_range = 2.3 - light_power = 0.6 - light_color = "#FFCC66" - light_on = FALSE - - //Main variables - var/owner = null // String name of owner - var/default_cartridge = 0 // Access level defined by cartridge - var/obj/item/cartridge/cartridge = null //current cartridge - var/mode = 0 //Controls what menu the PDA will display. 0 is hub; the rest are either built in or based on cartridge. - var/icon_alert = "pda-r" //Icon to be overlayed for message alerts. Taken from the pda icon file. - var/icon_pai = "pai-overlay" // Icon to be overlayed when an active pAI is slotted in. - var/icon_inactive_pai = "pai-off-overlay" // Same as above but for an inactive pAI. - var/font_index = 0 //This int tells DM which font is currently selected and lets DM know when the last font has been selected so that it can cycle back to the first font when "toggle font" is pressed again. - var/font_mode = "font-family:monospace;" //The currently selected font. - var/background_color = "#808000" //The currently selected background color. - - #define FONT_MONO "font-family:monospace;" - #define FONT_SHARE "font-family:\"Share Tech Mono\", monospace;letter-spacing:0px;" - #define FONT_ORBITRON "font-family:\"Orbitron\", monospace;letter-spacing:0px; font-size:15px" - #define FONT_VT "font-family:\"VT323\", monospace;letter-spacing:1px;" - #define MODE_MONO 0 - #define MODE_SHARE 1 - #define MODE_ORBITRON 2 - #define MODE_VT 3 - - //Secondary variables - var/scanmode = PDA_SCANNER_NONE - var/shorted = FALSE //Is the flashlight shorted out? - var/silent = FALSE //To beep or not to beep, that is the question - var/toff = FALSE //If TRUE, messenger disabled - var/tnote = null //Current Texts - var/last_text //No text spamming - var/last_everyone //No text for everyone spamming - var/last_noise //Also no honk spamming that's bad too - var/ttone = "beep" //The ringtone! - var/honkamt = 0 //How many honks left when infected with honk.exe - var/mimeamt = 0 //How many silence left when infected with mime.exe - var/note = "Congratulations, your station has chosen the Thinktronic 5230 Personal Data Assistant!" //Current note in the notepad function - var/notehtml = "" - var/notescanned = FALSE // True if what is in the notekeeper was from a paper. - var/detonatable = TRUE // Can the PDA be blown up? - var/hidden = FALSE // Is the PDA hidden from the PDA list? - var/emped = FALSE - var/equipped = FALSE //used here to determine if this is the first time its been picked up - var/allow_emojis = FALSE //if the pda can send emojis and actually have them parsed as such - var/sort_by_job = FALSE // If this is TRUE, will sort PDA list by job. - var/toggle_auto_update = PDA_TOGGLE_ON // If this is "On", automatically update PDA when taken a card, if no, it doesn't. - - var/obj/item/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both. - var/ownjob = null //related to above - - var/obj/item/paicard/pai = null // A slot for a personal AI device - - var/datum/picture/picture //Scanned photo - - var/list/contained_item = list(/obj/item/pen, /obj/item/toy/crayon, /obj/item/lipstick, /obj/item/flashlight/pen, /obj/item/clothing/mask/cigarette) - var/obj/item/inserted_item //Used for pen, crayon, and lipstick insertion or removal. Same as above. - var/overlays_x_offset = 0 //x offset to use for certain overlays - - var/underline_flag = TRUE //flag for underline - - var/datum/component/holomap/holo_base = null //holomap component - var/datum/action/toggle_holomap/useless //Useless thingy meant for to be used for nothing more than deleting the action if it already exists - -/obj/item/pda/suicide_act(mob/living/carbon/user) - var/deathMessage = msg_input(user) - if (!deathMessage) - deathMessage = "i ded" - user.visible_message("[user] is sending a message to the Grim Reaper! It looks like [user.p_theyre()] trying to commit suicide!") - tnote += "→ To The Grim Reaper:
[deathMessage]
"//records a message in their PDA as being sent to the grim reaper - return BRUTELOSS - -/obj/item/pda/examine(mob/user) - . = ..() - if(!id && !inserted_item) - return - - if(id) - . += "Alt-click to remove the ID." //won't name ID on examine in case it's stolen - - if(inserted_item && (!isturf(loc))) - . += "Ctrl-click to remove [inserted_item]." //traitor pens are disguised so we're fine naming them on examine - - if((!isnull(cartridge))) - . += "Ctrl+Shift-click to remove the cartridge." //won't name cart on examine in case it's Detomatix - -/obj/item/pda/Initialize(mapload) - . = ..() - - GLOB.PDAs += src - if(default_cartridge) - cartridge = new default_cartridge(src) - if(inserted_item) - inserted_item = new inserted_item(src) - else - inserted_item = new /obj/item/pen(src) - update_icon() - return INITIALIZE_HINT_LATELOAD - -/obj/item/pda/LateInitialize() - . = ..() - apply_holomap() - -/obj/item/pda/proc/apply_holomap() - if(holo_base) - holo_base.RemoveComponent() - QDEL_NULL(holo_base) - AddComponent(/datum/component/holomap) //NSV13 - -/obj/item/pda/equipped(mob/user, slot) - . = ..() - if(!equipped) - if(user.client) - background_color = user.client.prefs.pda_color - switch(user.client.prefs.pda_style) - if(MONO) - font_index = MODE_MONO - font_mode = FONT_MONO - if(SHARE) - font_index = MODE_SHARE - font_mode = FONT_SHARE - if(ORBITRON) - font_index = MODE_ORBITRON - font_mode = FONT_ORBITRON - if(VT) - font_index = MODE_VT - font_mode = FONT_VT - else - font_index = MODE_MONO - font_mode = FONT_MONO - equipped = TRUE - -/obj/item/pda/proc/update_label() - name = "PDA-[owner] ([ownjob])" //Name generalisation - -/obj/item/pda/GetAccess() - if(id) - return id.GetAccess() - else - return ..() - -/obj/item/pda/GetID() - return id - -/obj/item/pda/RemoveID() - return do_remove_id() - -/obj/item/pda/InsertID(obj/item/inserting_item) - var/obj/item/card/inserting_id = inserting_item.RemoveID() - if(!inserting_id) - return - insert_id(inserting_id) - if(id == inserting_id) - return TRUE - return FALSE - -/obj/item/pda/update_icon() - cut_overlays() - var/mutable_appearance/overlay = new() - overlay.pixel_x = overlays_x_offset - if(id) - overlay.icon_state = "id_overlay" - add_overlay(new /mutable_appearance(overlay)) - if(inserted_item) - overlay.icon_state = "insert_overlay" - add_overlay(new /mutable_appearance(overlay)) - if(light_on) - overlay.icon_state = "light_overlay" - add_overlay(new /mutable_appearance(overlay)) - if(pai) - if(pai.pai) - overlay.icon_state = icon_pai - add_overlay(new /mutable_appearance(overlay)) - else - overlay.icon_state = icon_inactive_pai - add_overlay(new /mutable_appearance(overlay)) - -/obj/item/pda/MouseDrop(mob/over, src_location, over_location) - var/mob/M = usr - if((M == over) && usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) - return attack_self(M) - return ..() - -/obj/item/pda/attack_self_tk(mob/user) - to_chat(user, "The PDA's capacitive touch screen doesn't seem to respond!") - return - -/obj/item/pda/interact(mob/user) - if(!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return - - ..() - - var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/pda) - assets.send(user) - - var/datum/asset/spritesheet/emoji_s = get_asset_datum(/datum/asset/spritesheet/chat) - emoji_s.send(user) //Already sent by chat but no harm doing this - - user.set_machine(src) - - var/dat = "Personal Data Assistant" - dat += assets.css_tag() - dat += emoji_s.css_tag() - - dat += "[PDAIMG(refresh)]Refresh" - - if ((!isnull(cartridge)) && (mode == 0)) - dat += " | [PDAIMG(eject)]Eject [cartridge]" - if (mode) - dat += " | [PDAIMG(menu)]Return" - - if (mode == 0) - dat += "
" - dat += "
Toggle Font" - dat += " | Change Color" - dat += " | Toggle Underline" //underline button - - dat += "
" - - dat += "
" - - if (!owner) - dat += "Warning: No owner information entered. Please swipe card.

" - dat += "[PDAIMG(refresh)]Retry" - else - switch (mode) - if (0) - dat += "

PERSONAL DATA ASSISTANT v.1.2

" - dat += "Owner: [owner], [ownjob]
" - dat += text("ID: [id ? "[id.registered_name], [id.assignment]" : "----------"]") - dat += text("
[id ? "Update PDA Info" : ""]
") - dat += text("
Toggle auto-updating: \[[toggle_auto_update]\]

") - - dat += "[station_time_timestamp()]
" //:[world.time / 100 % 6][world.time / 100 % 10]" - dat += "[time2text(world.realtime, "MMM DD")] [GLOB.year_integer+YEAR_OFFSET]" //NSV13 edit: year offset change - - dat += "

" - - dat += "

General Functions

" - dat += "" - if (cartridge.access & CART_ENGINE) - dat += "

Engineering Functions

" - dat += "" - if (cartridge.access & CART_MEDICAL) - dat += "

Medical Functions

" - dat += "" - if (cartridge.access & CART_SECURITY) - dat += "

Security Functions

" - dat += "" - if(cartridge.access & CART_QUARTERMASTER) - dat += "

Quartermaster Functions:

" - dat += "" - dat += "" - - dat += "

Utilities

" - dat += "" - - if (1) - dat += "

[PDAIMG(notes)] Notekeeper V2.2

" - dat += "Edit
" - if(notescanned) - dat += "(This is a scanned image, editing it may cause some text formatting to change.)
" - dat += "
[(!notehtml ? note : notehtml)]" - - if (2) - dat += "

[PDAIMG(mail)] SpaceMessenger V3.9.6

" - dat += "[PDAIMG(bell)]Ringer: [silent == 1 ? "Off" : "On"] | " - dat += "[PDAIMG(mail)]Send / Receive: [toff == 1 ? "Off" : "On"] | " - dat += "[PDAIMG(bell)]Set Ringtone | " - dat += "[PDAIMG(mail)]Messages
" - dat += "Sorted by: [sort_by_job ? "Job" : "Name"]" - - if(cartridge) - dat += cartridge.message_header() - - dat += "

[PDAIMG(menu)] Detected PDAs

" - - dat += "" - if (count == 0) - dat += "None detected.
" - else if(cartridge && cartridge.spam_delay) - dat += "Send To All" - - //NSV13 - DATABASE AKA WIKI ACCESS - START - if("Database") - var/wikiurl = CONFIG_GET(string/wikiurl) - dat += "
" - //NSV13 - DATABASE AKA WIKI ACCESS - END - - if(21) - dat += "

[PDAIMG(mail)] SpaceMessenger V3.9.6

" - dat += "[PDAIMG(blank)]Clear Messages" - - dat += "

[PDAIMG(mail)] Messages

" - - dat += tnote - dat += "
" - - if (3) - dat += "

[PDAIMG(atmos)] Atmospheric Readings

" - - var/turf/T = user.loc - if (isnull(T)) - dat += "Unable to obtain a reading.
" - else - var/datum/gas_mixture/environment = T.return_air() - - var/pressure = environment.return_pressure() - var/total_moles = environment.total_moles() - - dat += "Air Pressure: [round(pressure,0.1)] kPa
" - - if (total_moles) - for(var/id in environment.get_gases()) - var/gas_level = environment.get_moles(id)/total_moles - if(gas_level > 0) - dat += "[GLOB.gas_data.names[id]]: [round(gas_level*100, 0.01)]%
" - - dat += "Temperature: [round(environment.return_temperature()-T0C)]°C
" - dat += "
" - else//Else it links to the cart menu proc. Although, it really uses menu hub 4--menu 4 doesn't really exist as it simply redirects to hub. - dat += cartridge.generate_menu() - - dat += "" - - if (underline_flag) - dat = replacetext(dat, "text-decoration:none", "text-decoration:underline") - if (!underline_flag) - dat = replacetext(dat, "text-decoration:underline", "text-decoration:none") - - user << browse(dat, "window=pda;size=400x450;border=1;can_resize=1;can_minimize=0") - onclose(user, "pda", src) - -/obj/item/pda/Topic(href, href_list) - ..() - var/mob/living/U = usr - //Looking for master was kind of pointless since PDAs don't appear to have one. - - if(usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && !href_list["close"]) - add_fingerprint(U) - U.set_machine(src) - - switch(href_list["choice"]) - -//BASIC FUNCTIONS=================================== - - if("Refresh")//Refresh, goes to the end of the proc. - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if ("Toggle_Font") - //CODE REVISION 2 - font_index = (font_index + 1) % 4 - - switch(font_index) - if (MODE_MONO) - font_mode = FONT_MONO - if (MODE_SHARE) - font_mode = FONT_SHARE - if (MODE_ORBITRON) - font_mode = FONT_ORBITRON - if (MODE_VT) - font_mode = FONT_VT - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if ("Change_Color") - var/new_color = input("Please enter a color name or hex value (Default is \'#808000\').",background_color)as color - background_color = new_color - - if ("Toggle_Underline") - underline_flag = !underline_flag - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("Return")//Return - if(mode=="Database" || mode<=9) //NSV13 - mode = 0 - else - mode = round(mode/10) - if(mode==4 || mode == 5)//Fix for cartridges. Redirects to hub. - mode = 0 - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if ("Authenticate")//Checks for ID - id_check(U) - if("UpdateInfo") - update_pda() - if("ToggleAutoUpdate") - switch(toggle_auto_update) - if(PDA_TOGGLE_ON) - toggle_auto_update = PDA_TOGGLE_OFF - if(PDA_TOGGLE_OFF) - toggle_auto_update = PDA_TOGGLE_ON - update_pda() - if("Eject")//Ejects the cart, only done from hub. - eject_cart(U) - if(!silent) - playsound(src, 'sound/machines/terminal_eject.ogg', 50, TRUE) - -//MENU FUNCTIONS=================================== - - if("0")//Hub - mode = 0 - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("1")//Notes - mode = 1 - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("2")//Messenger - mode = 2 - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("Database")//Database //NSV13 - mode = "Database" //NSV13 - if(!silent) //NSV13 - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) //NSV13 - if("21")//Read messeges - mode = 21 - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("3")//Atmos scan - mode = 3 - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("4")//Redirects to hub - mode = 0 - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - - - - -//MAIN FUNCTIONS=================================== - - if("Light") - toggle_light(U) - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("Medical Scan") - if(scanmode == PDA_SCANNER_MEDICAL) - scanmode = PDA_SCANNER_NONE - else if((!isnull(cartridge)) && (cartridge.access & CART_MEDICAL)) - scanmode = PDA_SCANNER_MEDICAL - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("Reagent Scan") - if(scanmode == PDA_SCANNER_REAGENT) - scanmode = PDA_SCANNER_NONE - else if((!isnull(cartridge)) && (cartridge.access & CART_REAGENT_SCANNER)) - scanmode = PDA_SCANNER_REAGENT - if("Halogen Counter") - if(scanmode == PDA_SCANNER_HALOGEN) - scanmode = PDA_SCANNER_NONE - else if((!isnull(cartridge)) && (cartridge.access & CART_ENGINE)) - scanmode = PDA_SCANNER_HALOGEN - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("Honk") - if ( !(last_noise && world.time < last_noise + 20) ) - playsound(src, 'sound/items/bikehorn.ogg', 50, 1) - last_noise = world.time - if("Trombone") - if ( !(last_noise && world.time < last_noise + 20) ) - playsound(src, 'sound/misc/sadtrombone.ogg', 50, 1) - last_noise = world.time - if("Gas Scan") - if(scanmode == PDA_SCANNER_GAS) - scanmode = PDA_SCANNER_NONE - else if((!isnull(cartridge)) && (cartridge.access & CART_ATMOS)) - scanmode = PDA_SCANNER_GAS - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("Drone Phone") - var/alert_s = input(U,"Alert severity level","Ping Drones",null) as null|anything in list("Low","Medium","High","Critical") - var/area/A = get_area(U) - if(A && alert_s && !QDELETED(U)) - var/msg = "NON-DRONE PING: [U.name]: [alert_s] priority alert in [A.name]!" - _alert_drones(msg, TRUE, U) - to_chat(U, msg) - if(!silent) - playsound(src, 'sound/machines/terminal_success.ogg', 15, TRUE) - - -//NOTEKEEPER FUNCTIONS=================================== - - if ("Edit") - var/n = stripped_multiline_input(U, "Please enter message", name, note) - if (in_range(src, U) && loc == U) - if (mode == 1 && n) - note = n - notehtml = parsemarkdown(n, U) - notescanned = FALSE - else - U << browse(null, "window=pda") - return - -//MESSENGER FUNCTIONS=================================== - - if("Toggle Messenger") - toff = !toff - if("Toggle Ringer")//If viewing texts then erase them, if not then toggle silent status - silent = !silent - if("Clear")//Clears messages - tnote = null - if("Ringtone") - var/t = stripped_input(U, "Please enter new ringtone", name, ttone, max_length=20) - if(in_range(src, U) && loc == U && t) - if(SEND_SIGNAL(src, COMSIG_PDA_CHANGE_RINGTONE, U, t) & COMPONENT_STOP_RINGTONE_CHANGE) - U << browse(null, "window=pda") - return - else - ttone = t - else - U << browse(null, "window=pda") - return - if("Message") - create_message(U, locate(href_list["target"]) in GLOB.PDAs) - - if("Sorting Mode") - sort_by_job = !sort_by_job - - if("MessageAll") - if(cartridge?.spam_delay) - send_to_all(U, cartridge?.spam_delay) - - if("cart") - if(cartridge) - cartridge.special(U, href_list) - else - U << browse(null, "window=pda") - return - -//SYNDICATE FUNCTIONS=================================== - - if("Toggle Door") - if(cartridge && cartridge.access & CART_REMOTE_DOOR) - for(var/obj/machinery/door/poddoor/M in GLOB.machines) - if(M.id == cartridge.remote_door_id) - if(M.density) - M.open() - else - M.close() - -//pAI FUNCTIONS=================================== - if("pai") - switch(href_list["option"]) - if("1") // Configure pAI device - pai.attack_self(U) - if("2") // Eject pAI device - usr.put_in_hands(pai) - to_chat(usr, "You remove the pAI from the [name].") - -//LINK FUNCTIONS=================================== - - else//Cartridge menu linking - mode = max(text2num(href_list["choice"]), 0) - - else//If not in range, can't interact or not using the pda. - U.unset_machine() - U << browse(null, "window=pda") - return - -//EXTRA FUNCTIONS=================================== - - if (mode == 2 || mode == 21)//To clear message overlays. - update_icon() - - if ((honkamt > 0) && (prob(60)))//For clown virus. - honkamt-- - playsound(src, 'sound/items/bikehorn.ogg', 30, 1) - - if(U.machine == src && href_list["skiprefresh"]!="1")//Final safety. - attack_self(U)//It auto-closes the menu prior if the user is not in range and so on. - else - U.unset_machine() - U << browse(null, "window=pda") - return - -/obj/item/pda/proc/remove_id() - if(issilicon(usr) || !usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) - return - do_remove_id(usr) - -/obj/item/pda/proc/update_pda() - ownjob = id.assignment - if(istype(id, /obj/item/card/id/syndicate)) - owner = id.registered_name - update_label() - if(!silent) - playsound(src, 'sound/machines/terminal_processing.ogg', 15, TRUE) - addtimer(CALLBACK(GLOBAL_PROC, PROC_REF(playsound), src, 'sound/machines/terminal_success.ogg', 15, TRUE), 1.3 SECONDS) - -/obj/item/pda/proc/do_remove_id(mob/user) - if(!id) - return - if(user) - user.put_in_hands(id) - to_chat(user, "You remove the ID from the [name].") - else - id.forceMove(get_turf(src)) - - . = id - id = null - updateSelfDialog() - update_icon() - - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - if(H.wear_id == src) - H.sec_hud_set_ID() - -/obj/item/pda/proc/msg_input(mob/living/U = usr) - var/t = stripped_input(U, "Please enter message", name) - if (!t || toff) - return - if(!U.canUseTopic(src, BE_CLOSE)) - return - if(emped) - t = Gibberish(t, TRUE) - return t - -/obj/item/pda/proc/send_pda_message(mob/living/user, list/obj/item/pda/targets, everyone, multi_delay=0) - var/message = msg_input(user) - if(!message || !targets.len) - return - if((last_text && world.time < last_text + 10) || (everyone && last_everyone && world.time < (last_everyone + PDA_SPAM_DELAY*multi_delay))) - return - if(prob(1)) - message += "\nSent from my PDA" - // Send the signal - var/list/string_targets = list() - for (var/obj/item/pda/P in targets) - if (P.owner && P.ownjob) // != src is checked by the UI - string_targets += "[P.owner] ([P.ownjob])" - for (var/obj/machinery/computer/message_monitor/M in targets) - // In case of "Reply" to a message from a console, this will make the - // message be logged successfully. If the console is impersonating - // someone by matching their name and job, the reply will reach the - // impersonated PDA. - string_targets += "[M.customsender] ([M.customjob])" - if (!string_targets.len) - return - - if(CHAT_FILTER_CHECK(message)) - to_chat(user, "ERROR: Prohibited word(s) detected in message.") - return - - var/datum/signal/subspace/messaging/pda/signal = new(src, list( - "name" = "[owner]", - "job" = "[ownjob]", - "message" = message, - "targets" = string_targets, - "emojis" = allow_emojis, - )) - if (picture) - signal.data["photo"] = picture - signal.send_to_receivers() - - // If it didn't reach, note that fact - if (!signal.data["done"]) - to_chat(user, "ERROR: Server isn't responding.") - if(!silent) - playsound(src, 'sound/machines/terminal_error.ogg', 15, TRUE) - return - - var/target_text = signal.format_target() - if(allow_emojis) - message = emoji_parse(message)//already sent- this just shows the sent emoji as one to the sender in the to_chat - signal.data["message"] = emoji_parse(signal.data["message"]) - - // Log it in our logs - tnote += "→ To [target_text]:
[signal.format_message()]
" - // Show it to ghosts - var/ghost_message = "[owner] PDA Message --> [target_text]: [signal.format_message()]" - for(var/mob/M in GLOB.player_list) - if(isobserver(M) && M.client && (M.client.prefs.chat_toggles & CHAT_GHOSTPDA)) - to_chat(M, "[FOLLOW_LINK(M, user)] [ghost_message]") - // Log in the talk log - user.log_talk(message, LOG_PDA, tag="PDA: [initial(name)] to [target_text]") - to_chat(user, "PDA message sent to [target_text]: \"[message]\"") - if(!silent) - if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) - playsound(src, pick('sound/machines/twobeep_voice1.ogg', 'sound/machines/twobeep_voice2.ogg'), 50, TRUE) - else - playsound(src, 'sound/machines/terminal_success.ogg', 15, TRUE) - // Reset the photo - picture = null - last_text = world.time - if (everyone) - last_everyone = world.time - -/obj/item/pda/proc/receive_message(datum/signal/subspace/messaging/pda/signal) - tnote += "← From [signal.data["name"]] ([signal.data["job"]]):
[signal.format_message()]
" - - if (!silent) - if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) - playsound(src, pick('sound/machines/twobeep_voice1.ogg', 'sound/machines/twobeep_voice2.ogg'), 50, TRUE) - else - playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE) - audible_message("[icon2html(src, hearers(src))] *[ttone]*", null, 3) - //Search for holder of the PDA. - var/mob/living/L = null - if(loc && isliving(loc)) - L = loc - //Maybe they are a pAI! - else - L = get(src, /mob/living/silicon) - - if(L && L.stat != UNCONSCIOUS) - var/reply = "(Reply)" - var/hrefstart - var/hrefend - if (isAI(L)) - hrefstart = "" - hrefend = "" - - if(signal.data["automated"]) - reply = "\[Automated Message\]" - - var/inbound_message = signal.format_message() - if(signal.data["emojis"] == TRUE)//so will not parse emojis as such from pdas that don't send emojis - inbound_message = emoji_parse(inbound_message) - - to_chat(L, "[icon2html(src)] PDA message from [hrefstart][signal.data["name"]] ([signal.data["job"]])[hrefend], [inbound_message] [reply]") - - update_icon() - add_overlay(icon_alert) - -/obj/item/pda/proc/send_to_all(mob/living/U, multi_delay) - if (last_everyone && world.time < (last_everyone + PDA_SPAM_DELAY*multi_delay)) - to_chat(U,"Send To All function is still on cooldown. Enabled in [(last_everyone + PDA_SPAM_DELAY*multi_delay - world.time)/10] seconds.") - return - if(multi_delay) - send_pda_message(U,get_viewable_pdas(), TRUE, multi_delay) - -/obj/item/pda/proc/create_message(mob/living/U, obj/item/pda/P) - send_pda_message(U,list(P)) - -/obj/item/pda/AltClick(mob/user) - if(id) - remove_id(user) - else - remove_pen(user) - -/obj/item/pda/CtrlClick(mob/user) - ..() - - if(isturf(loc)) //stops the user from dragging the PDA by ctrl-clicking it. - return - - remove_pen(user) - -/obj/item/pda/CtrlShiftClick(mob/user) - ..() - eject_cart(user) - -/obj/item/pda/verb/verb_toggle_light() - set name = "Toggle light" - set category = "Object" - set src in oview(1) - - toggle_light(usr) - -/obj/item/pda/verb/verb_remove_id() - set category = "Object" - set name = "Eject ID" - set src in usr - - if(id) - remove_id(usr) - else - to_chat(usr, "This PDA does not have an ID in it!") - -/obj/item/pda/verb/verb_remove_pen() - set category = "Object" - set name = "Remove Pen" - set src in usr - - remove_pen(usr) - -/obj/item/pda/verb/verb_eject_cart() - set category = "Object" - set name = "Eject Cartridge" - set src in usr - - eject_cart(usr) - -/obj/item/pda/proc/toggle_light(mob/user) - if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE)) - return - if(shorted) - to_chat(user, "[src]'s light is not turning on!") - return - if(light_on) - set_light_on(FALSE) - else if(light_range) - set_light_on(TRUE) - update_icon() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/pda/proc/remove_pen(mob/user) - - if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) //TK doesn't work even with this removed but here for readability - return - - if(inserted_item) - user.put_in_hands(inserted_item) - to_chat(user, "You remove [inserted_item] from [src].") - inserted_item = null - update_icon() - playsound(src, 'sound/machines/pda_button2.ogg', 50, TRUE) - else - to_chat(user, "This PDA does not have a pen in it!") - -/obj/item/pda/proc/eject_cart(mob/user) - if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) //TK disabled to stop cartridge teleporting into hand - return - if (!isnull(cartridge)) - user.put_in_hands(cartridge) - to_chat(user, "You eject [cartridge] from [src].") - scanmode = PDA_SCANNER_NONE - cartridge.host_pda = null - cartridge = null - updateSelfDialog() - update_icon() - -//trying to insert or remove an id -/obj/item/pda/proc/id_check(mob/user, obj/item/card/id/I) - if(!I) - if(id && (src in user.contents)) - remove_id(user) - return TRUE - else - var/obj/item/card/id/C = user.get_active_held_item() - if(istype(C)) - I = C - - if(I?.registered_name) - if(!user.transferItemToLoc(I, src)) - return FALSE - insert_id(I, user) - update_icon() - playsound(src, 'sound/machines/pda_button1.ogg', 50, TRUE) - return TRUE - -/obj/item/pda/pre_attack(obj/target, mob/living/user, params) - if(!ismachinery(target)) - return ..() - var/obj/machinery/target_machine = target - if(!target_machine.panel_open && !istype(target, /obj/machinery/computer)) - return ..() - if(!istype(cartridge, /obj/item/cartridge/virus/clown)) - return ..() - var/obj/item/cartridge/virus/installed_cartridge = cartridge - - if(installed_cartridge.charges <=0) - balloon_alert(user, "The PDA beeps: 'Out of charge. Please insert a new cartridge.'") - return ..() - - if(target.GetComponent(/datum/component/sound_player)) - balloon_alert(user, "The PDA beeps: 'Virus already present on client, aborting.'") - return - - balloon_alert(user, "You upload the virus.") - var/list/sig_list = list() - if(istype(target, /obj/machinery/door/airlock)) - sig_list += list(COMSIG_AIRLOCK_OPEN, COMSIG_AIRLOCK_CLOSE) - else - sig_list += list(COMSIG_ATOM_ATTACK_HAND) - installed_cartridge.charges-- - target.AddComponent(/datum/component/sound_player, amount = (rand(30,50)), signal_or_sig_list = sig_list) - return TRUE - -/obj/item/pda/proc/insert_id(obj/item/card/id/inserting_id, mob/user) - var/obj/old_id = id - id = inserting_id - if(ishuman(loc)) - var/mob/living/carbon/human/human_wearer = loc - if(human_wearer.wear_id == src) - human_wearer.sec_hud_set_ID() - if(old_id) - if(user) - user.put_in_hands(old_id) - else - old_id.forceMove(get_turf(src)) - -// access to status display signals -/obj/item/pda/attackby(obj/item/C, mob/user, params) - if(istype(C, /obj/item/cartridge)) - if(!user.transferItemToLoc(C, src)) - return - eject_cart(user) - cartridge = C - cartridge.host_pda = src - to_chat(user, "You insert [cartridge] into [src].") - updateSelfDialog() - update_icon() - if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) - playsound(src, pick('sound/machines/twobeep_voice1.ogg', 'sound/machines/twobeep_voice2.ogg'), 50, TRUE) - else - playsound(src, 'sound/machines/pda_button1.ogg', 50, TRUE) - - else if(istype(C, /obj/item/card/id)) - var/obj/item/card/id/idcard = C - if(!idcard.registered_name) - to_chat(user, "\The [src] rejects the ID!") - if(!silent) - playsound(src, 'sound/machines/terminal_error.ogg', 50, TRUE) - return - if(!owner) - owner = idcard.registered_name - ownjob = idcard.assignment - update_label() - to_chat(user, "Card scanned.") - if(!silent) - playsound(src, 'sound/machines/terminal_success.ogg', 50, TRUE) - else - if(!id_check(user, idcard)) - return - to_chat(user, "You put the ID into \the [src]'s slot.") - if(((owner != id.registered_name) || (ownjob != id.assignment)) && (toggle_auto_update == PDA_TOGGLE_ON)) // auto-update by inserting your card - update_pda() - updateSelfDialog()//Update self dialog on success. - - return //Return in case of failed check or when successful. - - updateSelfDialog()//For the non-input related code. - else if(istype(C, /obj/item/paicard) && !pai) - if(!user.transferItemToLoc(C, src)) - return - pai = C - to_chat(user, "You slot \the [C] into [src].") - update_icon() - updateUsrDialog() - else if(is_type_in_list(C, contained_item)) //Checks if there is a pen - if(inserted_item) - to_chat(user, "There is already \a [inserted_item] in \the [src]!") - else - if(C.w_class > WEIGHT_CLASS_TINY) - to_chat(user, "The [C] doesnt fit!") - return - if(!user.transferItemToLoc(C, src)) - return - to_chat(user, "You slide \the [C] into \the [src].") - inserted_item = C - update_icon() - playsound(src, 'sound/machines/pda_button1.ogg', 50, TRUE) - else if(istype(C, /obj/item/photo)) - var/obj/item/photo/P = C - picture = P.picture - to_chat(user, "You scan \the [C].") - else - return ..() - -/obj/item/pda/attack(mob/living/carbon/C, mob/living/user) - if(istype(C)) - switch(scanmode) - - if(PDA_SCANNER_MEDICAL) - C.visible_message("[user] has analyzed [C]'s vitals!") - healthscan(user, C, 1) - add_fingerprint(user) - - if(PDA_SCANNER_HALOGEN) - C.visible_message("[user] has analyzed [C]'s radiation levels!") - - user.show_message("Analyzing Results for [C]:") - if(C.radiation) - user.show_message("\green Radiation Level: \black [C.radiation]") - else - user.show_message("No radiation detected.") - -/obj/item/pda/afterattack(atom/A as mob|obj|turf|area, mob/user, proximity) - . = ..() - if(!proximity) - return - switch(scanmode) - if(PDA_SCANNER_REAGENT) - if(!istype(A, /obj/item/reagent_containers/pill/floorpill) && !istype(A, /obj/item/reagent_containers/glass/chem_heirloom)) - if(!isnull(A.reagents)) - if(A.reagents.reagent_list.len > 0) - var/reagents_length = A.reagents.reagent_list.len - to_chat(user, "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.") - for (var/re in A.reagents.reagent_list) - to_chat(user, "\t [re]") - else - to_chat(user, "No active chemical agents found in [A].") - else - to_chat(user, "No significant chemical agents found in [A].") - else - to_chat(user, "You can't scan [A].") - - if(PDA_SCANNER_GAS) - A.analyzer_act(user, src) - - if (!scanmode && istype(A, /obj/item/paper) && owner) - var/obj/item/paper/PP = A - if (!PP.info) - to_chat(user, "Unable to scan! Paper is blank.") - return - notehtml = PP.info - note = replacetext(notehtml, "
", "\[br\]") - note = replacetext(note, "
  • ", "\[*\]") - note = replacetext(note, "
      ", "\[list\]") - note = replacetext(note, "
    ", "\[/list\]") - note = html_encode(note) - notescanned = TRUE - to_chat(user, "Paper scanned. Saved to PDA's notekeeper." ) - - -/obj/item/pda/proc/explode() //This needs tuning. - if(!detonatable) - return - var/turf/T = get_turf(src) - - if (ismob(loc)) - var/mob/M = loc - M.show_message("Your [src] explodes!", MSG_VISUAL, "You hear a loud *pop*!", MSG_AUDIBLE) - else - visible_message("[src] explodes!", "You hear a loud *pop*!") - - if(T) - T.hotspot_expose(700,125) - if(istype(cartridge, /obj/item/cartridge/virus/syndicate)) - explosion(T, -1, 1, 3, 4) - else - explosion(T, -1, -1, 2, 3) - qdel(src) - return - -/obj/item/pda/Destroy() - GLOB.PDAs -= src - if(istype(id)) - QDEL_NULL(id) - if(istype(cartridge)) - QDEL_NULL(cartridge) - if(istype(pai)) - QDEL_NULL(pai) - if(istype(inserted_item)) - QDEL_NULL(inserted_item) - return ..() - -//AI verb and proc for sending PDA messages. - -/mob/living/silicon/proc/cmd_send_pdamesg(mob/user) - var/list/plist = list() - var/list/namecounts = list() - - if(aiPDA.toff) - to_chat(user, "Turn on your receiver in order to send messages.") - return - - for (var/obj/item/pda/P in get_viewable_pdas()) - if (P == src) - continue - else if (P == aiPDA) - continue - - plist[avoid_assoc_duplicate_keys(P.owner, namecounts)] = P - - var/c = input(user, "Please select a PDA") as null|anything in sortList(plist) - - if (!c) - return - - var/selected = plist[c] - - if(aicamera.stored.len) - var/add_photo = input(user,"Do you want to attach a photo?","Photo","No") as null|anything in list("Yes","No") - if(add_photo=="Yes") - var/datum/picture/Pic = aicamera.selectpicture(user) - aiPDA.picture = Pic - - if(incapacitated()) - return - - aiPDA.create_message(src, selected) - - -/mob/living/silicon/verb/cmd_toggle_pda_receiver() - set category = "AI Commands" - set name = "PDA - Toggle Sender/Receiver" - if(usr.stat == DEAD) - return //won't work if dead - if(!isnull(aiPDA)) - aiPDA.toff = !aiPDA.toff - to_chat(usr, "PDA sender/receiver toggled [(aiPDA.toff ? "Off" : "On")]!") - else - to_chat(usr, "You do not have a PDA. You should make an issue report about this.") - -/mob/living/silicon/verb/cmd_toggle_pda_silent() - set category = "AI Commands" - set name = "PDA - Toggle Ringer" - if(usr.stat == DEAD) - return //won't work if dead - if(!isnull(aiPDA)) - //0 - aiPDA.silent = !aiPDA.silent - to_chat(usr, "PDA ringer toggled [(aiPDA.silent ? "Off" : "On")]!") - else - to_chat(usr, "You do not have a PDA. You should make an issue report about this.") - -/mob/living/silicon/proc/cmd_show_message_log(mob/user) - if(incapacitated()) - return - if(!isnull(aiPDA)) - var/HTML = "AI PDA Message Log[aiPDA.tnote]" - user << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0") - else - to_chat(user, "You do not have a PDA. You should make an issue report about this.") - - -// Pass along the pulse to atoms in contents, largely added so pAIs are vulnerable to EMP -/obj/item/pda/emp_act(severity) - . = ..() - if(!(. & EMP_PROTECT_CONTENTS)) - for(var/atom/A in src) - A.emp_act(severity) - if(!(. & EMP_PROTECT_SELF)) - emped += 1 - var/emptime = 200 * severity - addtimer(CALLBACK(src, PROC_REF(decrease_emp_level)), emptime) - -/obj/item/pda/proc/decrease_emp_level() - emped -= 1 - -/proc/get_viewable_pdas(sort_by_job = FALSE) - . = list() - // Returns a list of PDAs which can be viewed from another PDA/message monitor., - var/sortmode - if(sort_by_job) - sortmode = GLOBAL_PROC_REF(cmp_pdajob_asc) - else - sortmode = GLOBAL_PROC_REF(cmp_pdaname_asc) - - for(var/obj/item/pda/P in sortList(GLOB.PDAs, sortmode)) - if(!P.owner || P.toff || P.hidden) - continue - . += P - -#undef PDA_SCANNER_NONE -#undef PDA_SCANNER_MEDICAL -#undef PDA_SCANNER_FORENSICS -#undef PDA_SCANNER_REAGENT -#undef PDA_SCANNER_HALOGEN -#undef PDA_SCANNER_GAS -#undef PDA_SPAM_DELAY -#undef PDA_TOGGLE_ON -#undef PDA_TOGGLE_OFF diff --git a/code/game/objects/items/devices/PDA/PDA_types.dm b/code/game/objects/items/devices/PDA/PDA_types.dm deleted file mode 100644 index b61de358864..00000000000 --- a/code/game/objects/items/devices/PDA/PDA_types.dm +++ /dev/null @@ -1,273 +0,0 @@ -//Clown PDA is slippery. -/obj/item/pda/clown - name = "clown PDA" - default_cartridge = /obj/item/cartridge/virus/clown - inserted_item = /obj/item/toy/crayon/rainbow - icon_state = "pda-clown" - desc = "A portable microcomputer by Thinktronic Systems, LTD. The surface is coated with polytetrafluoroethylene and banana drippings." - ttone = "honk" - var/slipvictims = list() //Track slipped people - -/obj/item/pda/clown/ComponentInitialize() - . = ..() - AddComponent(/datum/component/slippery, 7 SECONDS, NO_SLIP_WHEN_WALKING, CALLBACK(src, PROC_REF(AfterSlip)), 5 SECONDS) - -/obj/item/pda/clown/proc/AfterSlip(mob/living/carbon/human/M) - if (istype(M) && (M.real_name != owner)) - slipvictims |= M - var/obj/item/cartridge/virus/clown/cart = cartridge - if(istype(cart) && cart.charges < 5) - cart.charges++ - playsound(src,'sound/machines/ping.ogg', 30, TRUE) - -//Mime PDA sends "silent" messages. -/obj/item/pda/mime - name = "mime PDA" - default_cartridge = /obj/item/cartridge/virus/mime - inserted_item = /obj/item/toy/crayon/mime - icon_state = "pda-mime" - desc = "A portable microcomputer by Thinktronic Systems, LTD. The hardware has been modified for compliance with the vows of silence." - allow_emojis = TRUE - silent = TRUE - ttone = "silence" - -/obj/item/pda/mime/msg_input(mob/living/U = usr) - if(emped || toff) - return - var/emojis = emoji_sanitize(stripped_input(U, "Please enter emojis", name)) - if(!emojis) - return - if(!U.canUseTopic(src, BE_CLOSE)) - return - return emojis - -// Special AI/pAI PDAs that cannot explode. -/obj/item/pda/ai - icon = null - ttone = "data" - detonatable = FALSE - -/obj/item/pda/ai/attack_self(mob/user) - if ((honkamt > 0) && (prob(60)))//For clown virus. - honkamt-- - playsound(loc, 'sound/items/bikehorn.ogg', 30, 1) - return - -/obj/item/pda/ai/pai - ttone = "assist" - -//Various types of PDAs - -/obj/item/pda/assistant - name = "assistant PDA" - icon_state = "pda-assistant" - -/obj/item/pda/medical - name = "medical PDA" - default_cartridge = /obj/item/cartridge/medical - icon_state = "pda-medical" - -/obj/item/pda/paramedic - name = "paramedic PDA" - default_cartridge = /obj/item/cartridge/medical - icon_state = "pda-paramedical" - -/obj/item/pda/virologist - name = "virology PDA" - default_cartridge = /obj/item/cartridge/medical - icon_state = "pda-virology" - -/obj/item/pda/station_engineer - name = "engineering PDA" - default_cartridge = /obj/item/cartridge/engineering - icon_state = "pda-engineer" - -/obj/item/pda/security - name = "security PDA" - default_cartridge = /obj/item/cartridge/security - icon_state = "pda-security" - -/obj/item/pda/deputy - name = "deputy PDA" - default_cartridge = /obj/item/cartridge/security - icon_state = "pda-deputy" - -/obj/item/pda/brig_physician - name = "brig Physician PDA" - //no cartridge? - icon_state = "pda-brigphys" - -/obj/item/pda/detective - name = "detective PDA" - default_cartridge = /obj/item/cartridge/detective - icon_state = "pda-detective" - -/obj/item/pda/warden - name = "warden PDA" - default_cartridge = /obj/item/cartridge/security - icon_state = "pda-warden" - -/obj/item/pda/janitor - name = "janitor PDA" - default_cartridge = /obj/item/cartridge/janitor - icon_state = "pda-janitor" - ttone = "slip" - -/obj/item/pda/toxins - name = "scientist PDA" - default_cartridge = /obj/item/cartridge/signal/toxins - icon_state = "pda-science" - ttone = "boom" - -/obj/item/pda/service - name = "service PDA" - icon_state = "pda-service" - -/obj/item/pda/heads - default_cartridge = /obj/item/cartridge/head - icon_state = "pda-heads" - -/obj/item/pda/heads/head_of_personnel - name = "head of personnel PDA" - default_cartridge = /obj/item/cartridge/hop - icon_state = "pda-hop" - -/obj/item/pda/heads/head_of_security - name = "head of security PDA" - default_cartridge = /obj/item/cartridge/hos - icon_state = "pda-hos" - -/obj/item/pda/heads/chief_engineer - name = "chief engineer PDA" - default_cartridge = /obj/item/cartridge/ce - icon_state = "pda-ce" - -/obj/item/pda/heads/chief_medical_officer - name = "chief medical officer PDA" - default_cartridge = /obj/item/cartridge/cmo - icon_state = "pda-cmo" - -/obj/item/pda/heads/research_director - name = "research director PDA" - default_cartridge = /obj/item/cartridge/rd - inserted_item = /obj/item/pen/fountain - icon_state = "pda-rd" - -/obj/item/pda/captain - name = "captain PDA" - default_cartridge = /obj/item/cartridge/captain - inserted_item = /obj/item/pen/fountain/captain - desc = "A portable microcomputer by Thinktronic Systems, LTD. The internals are modified to be more tough than the usual." - icon_state = "pda-captain" - detonatable = FALSE - -/obj/item/pda/cargo_technician - name = "cargo technician PDA" - default_cartridge = /obj/item/cartridge/quartermaster - icon_state = "pda-cargo" - -/obj/item/pda/quartermaster - name = "quartermaster PDA" - default_cartridge = /obj/item/cartridge/quartermaster - inserted_item = /obj/item/pen/fountain - icon_state = "pda-qm" - -/obj/item/pda/shaft_miner - name = "shaft miner PDA" - icon_state = "pda-miner" - -/obj/item/pda/exploration_crew - name = "exploration PDA" - icon_state = "pda-exploration" - -/obj/item/pda/syndicate - default_cartridge = /obj/item/cartridge/virus/syndicate - desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a WGW-XL series." - note = "Congratulations, your -corrupted- has chosen the Thinktronic 5290 WGW-XL Series Personal Data Assistant!" - icon_state = "pda-syndi" - icon_alert = "pda-r-wide" - icon_pai = "pai-overlay-wide" - icon_inactive_pai = "pai-off-overlay-wide" - name = "military PDA" - owner = "John Doe" - detonatable = FALSE - hidden = 1 - -/obj/item/pda/chaplain - name = "chaplain PDA" - icon_state = "pda-chaplain" - ttone = "holy" - -/obj/item/pda/lawyer - name = "lawyer PDA" - default_cartridge = /obj/item/cartridge/lawyer - inserted_item = /obj/item/pen/fountain - icon_state = "pda-lawyer" - ttone = "objection" - -/obj/item/pda/roboticist - name = "roboticist PDA" - icon_state = "pda-roboticist" - default_cartridge = /obj/item/cartridge/roboticist - -/obj/item/pda/curator - name = "curator PDA" - icon_state = "pda-library" - icon_alert = "pda-r-wide" - icon_pai = "pai-overlay-wide" - icon_inactive_pai = "pai-off-overlay-wide" - default_cartridge = /obj/item/cartridge/curator - inserted_item = /obj/item/pen/fountain - desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a WGW-11 series e-reader." - note = "Congratulations, your station has chosen the Thinktronic 5290 WGW-11 Series E-reader and Personal Data Assistant!" - silent = TRUE //Quiet in the library! - -/obj/item/pda/clear - name = "clear PDA" - icon_state = "pda-clear" - desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a special edition with a transparent case." - note = "Congratulations, you have chosen the Thinktronic 5230 Personal Data Assistant Deluxe Special Max Turbo Limited Edition!" - -/obj/item/pda/cook - name = "cook PDA" - icon_state = "pda-cook" - -/obj/item/pda/bartender - name = "bartender PDA" - icon_state = "pda-bartender" - inserted_item = /obj/item/pen/fountain - -/obj/item/pda/atmospheric_technician - name = "atmospherics PDA" - default_cartridge = /obj/item/cartridge/atmos - icon_state = "pda-atmos" - -/obj/item/pda/chemist - name = "chemist PDA" - default_cartridge = /obj/item/cartridge/chemistry - icon_state = "pda-chemistry" - -/obj/item/pda/geneticist - name = "geneticist PDA" - default_cartridge = /obj/item/cartridge/medical - icon_state = "pda-genetics" - -/obj/item/pda/vip - name = "fancy PDA" - default_cartridge = /obj/item/cartridge/annoyance //so they can send messages to everyone and be generally obnoxious - inserted_item = /obj/item/pen/fountain - desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a gold-plated LRP Series, and probably quite expensive." - note = "Congratulations, you have chosen the Thinktronic 5230 LRP Series Personal Data Assistant Golden Edition!" - icon_state = "pda-gold" - ttone = "ch-CHING" - -/obj/item/pda/unlicensed - name = "unlicensed PDA" - default_cartridge = /obj/item/cartridge/annoyance/lesser - desc = "A shitty knockoff of a portable microcomputer by Thinktronic Systems, LTD. Complete with a cracked operating system." - note = "Error: Unlicensed OS. Please contact your supervisor." - icon_state = "pda-knockoff" - icon_alert = "pda-r-wide" - icon_pai = "pai-overlay-wide" - icon_inactive_pai = "pai-off-overlay-wide" - inserted_item = /obj/item/pen/charcoal diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm deleted file mode 100644 index 8f2ccadf85a..00000000000 --- a/code/game/objects/items/devices/PDA/cart.dm +++ /dev/null @@ -1,700 +0,0 @@ - -#define CART_SECURITY (1<<0) -#define CART_ENGINE (1<<1) -#define CART_ATMOS (1<<2) -#define CART_MEDICAL (1<<3) -#define CART_MANIFEST (1<<4) -#define CART_CLOWN (1<<5) -#define CART_MIME (1<<6) -#define CART_REAGENT_SCANNER (1<<7) -#define CART_NEWSCASTER (1<<8) -#define CART_REMOTE_DOOR (1<<9) -#define CART_STATUS_DISPLAY (1<<10) -#define CART_QUARTERMASTER (1<<11) -#define CART_HYDROPONICS (1<<12) -#define CART_DRONEPHONE (1<<13) - - -/obj/item/cartridge - name = "generic cartridge" - desc = "A data cartridge for portable microcomputers." - icon = 'icons/obj/pda.dmi' - icon_state = "cart" - item_state = "electronic" - lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' - w_class = WEIGHT_CLASS_TINY - - var/obj/item/integrated_signaler/radio = null - - var/access = 0 //Bit flags for cartridge access - - var/remote_door_id = "" - - var/bot_access_flags = 0 //Bit flags. Selection: SEC_BOT | MULE_BOT | FLOOR_BOT | CLEAN_BOT | MED_BOT | FIRE_BOT - var/spam_delay = 0 //Enables "Send to All" Option. 1=1 min, 2=2mins, 2.5=2 min 30 seconds - - var/obj/item/pda/host_pda = null - var/menu - var/datum/data/record/active1 = null //General - var/datum/data/record/active2 = null //Medical - var/datum/data/record/active3 = null //Security - var/obj/machinery/computer/monitor/powmonitor = null // Power Monitor - var/list/powermonitors = list() - var/message1 // used for status_displays - var/message2 - var/list/stored_data = list() - var/current_channel - - var/mob/living/simple_animal/bot/active_bot - var/list/botlist = list() - -/obj/item/cartridge/Initialize(mapload) - . = ..() - var/obj/item/pda/pda = loc - if(istype(pda)) - host_pda = pda - -/obj/item/cartridge/engineering - name = "\improper Power-ON cartridge" - icon_state = "cart-engie" - access = CART_ENGINE | CART_DRONEPHONE - bot_access_flags = FLOOR_BOT - -/obj/item/cartridge/atmos - name = "\improper BreatheDeep cartridge" - icon_state = "cart-atmos" - access = CART_ATMOS | CART_DRONEPHONE - bot_access_flags = FLOOR_BOT | FIRE_BOT - -/obj/item/cartridge/medical - name = "\improper Med-U cartridge" - icon_state = "cart-med" - access = CART_MEDICAL - bot_access_flags = MED_BOT - -/obj/item/cartridge/chemistry - name = "\improper ChemWhiz cartridge" - icon_state = "cart-chem" - access = CART_REAGENT_SCANNER - bot_access_flags = MED_BOT - -/obj/item/cartridge/security - name = "\improper R.O.B.U.S.T. cartridge" - icon_state = "cart-sec" - access = CART_SECURITY | CART_MANIFEST - bot_access_flags = SEC_BOT - -/obj/item/cartridge/detective - name = "\improper D.E.T.E.C.T. cartridge" - icon_state = "cart-det" - access = CART_SECURITY | CART_MEDICAL | CART_MANIFEST - bot_access_flags = SEC_BOT - -/obj/item/cartridge/janitor - name = "\improper CustodiPRO cartridge" - desc = "The ultimate in clean-room design." - icon_state = "cart-jan" - access = CART_DRONEPHONE - bot_access_flags = CLEAN_BOT - -/obj/item/cartridge/lawyer - name = "\improper P.R.O.V.E. cartridge" - icon_state = "cart-prove" - access = CART_SECURITY - spam_delay = 2.5 - -/obj/item/cartridge/curator - name = "\improper Lib-Tweet cartridge" - icon_state = "cart-cur" - access = CART_NEWSCASTER - spam_delay = 3.5 - -/obj/item/cartridge/roboticist - name = "\improper B.O.O.P. Remote Control cartridge" - desc = "Packed with heavy duty quad-bot interlink!" - icon_state = "cart-robo" - bot_access_flags = FLOOR_BOT | CLEAN_BOT | MED_BOT | FIRE_BOT - access = CART_DRONEPHONE - -/obj/item/cartridge/signal - name = "generic signaler cartridge" - icon_state = "cart-signal" - desc = "A data cartridge with an integrated radio signaler module." - -/obj/item/cartridge/signal/toxins - name = "\improper Signal Ace 2 cartridge" - desc = "Complete with integrated radio signaler!" - icon_state = "cart-tox" - access = CART_REAGENT_SCANNER | CART_ATMOS - -/obj/item/cartridge/signal/Initialize(mapload) - . = ..() - radio = new(src) - -/obj/item/cartridge/quartermaster - name = "space parts & space vendors cartridge" - desc = "Perfect for the Quartermaster on the go!" - icon_state = "cart-qm" - access = CART_QUARTERMASTER - bot_access_flags = MULE_BOT - -/obj/item/cartridge/head - name = "\improper Easy-Record DELUXE cartridge" - icon_state = "cart-val" - access = CART_MANIFEST | CART_STATUS_DISPLAY - -/obj/item/cartridge/hop - name = "\improper HumanResources9001 cartridge" - icon_state = "cart-hop" - access = CART_MANIFEST | CART_STATUS_DISPLAY | CART_SECURITY | CART_NEWSCASTER | CART_QUARTERMASTER | CART_DRONEPHONE - bot_access_flags = MULE_BOT | CLEAN_BOT - -/obj/item/cartridge/hos - name = "\improper R.O.B.U.S.T. DELUXE cartridge" - icon_state = "cart-hos" - access = CART_MANIFEST | CART_STATUS_DISPLAY | CART_SECURITY - bot_access_flags = SEC_BOT - -/obj/item/cartridge/ce - name = "\improper Power-On DELUXE cartridge" - icon_state = "cart-ce" - access = CART_MANIFEST | CART_STATUS_DISPLAY | CART_ENGINE | CART_ATMOS | CART_DRONEPHONE - bot_access_flags = FLOOR_BOT | FIRE_BOT - -/obj/item/cartridge/cmo - name = "\improper Med-U DELUXE cartridge" - icon_state = "cart-cmo" - access = CART_MANIFEST | CART_STATUS_DISPLAY | CART_REAGENT_SCANNER | CART_MEDICAL - bot_access_flags = MED_BOT - -/obj/item/cartridge/rd - name = "\improper Signal Ace DELUXE cartridge" - icon_state = "cart-rd" - access = CART_MANIFEST | CART_STATUS_DISPLAY | CART_REAGENT_SCANNER | CART_ATMOS | CART_DRONEPHONE - bot_access_flags = FLOOR_BOT | CLEAN_BOT | MED_BOT | FIRE_BOT - -/obj/item/cartridge/rd/Initialize(mapload) - . = ..() - radio = new(src) - -/obj/item/cartridge/captain - name = "\improper Value-PAK cartridge" - desc = "Now with 350% more value!" //Give the Captain...EVERYTHING! (Except Mime, Clown, and Syndie) - icon_state = "cart-cap" - access = ~(CART_CLOWN | CART_MIME | CART_REMOTE_DOOR) - bot_access_flags = SEC_BOT | MULE_BOT | FLOOR_BOT | CLEAN_BOT | MED_BOT | FIRE_BOT - spam_delay = 2 - -/obj/item/cartridge/captain/Initialize(mapload) - . = ..() - radio = new(src) - -/obj/item/cartridge/annoyance //the only purpose of this cartridge is to allow the VIP to be annoying - name = "\improper TWIT cartridge" - icon_state = "cart-twit" - spam_delay = 1.5 - -/obj/item/cartridge/annoyance/lesser //HoP can give you this - name = "\improper FACEBUCKS cartridge" - icon_state = "cart-signal" // might need a new sprite - spam_delay = 5 - -/obj/item/cartridge/proc/post_status(command, data1, data2) - - var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS) - - if(!frequency) - return - - var/datum/signal/status_signal = new(list("command" = command)) - switch(command) - if("message") - status_signal.data["msg1"] = data1 - status_signal.data["msg2"] = data2 - message_admins("[ADMIN_LOOKUPFLW(usr)] changed the Status Message to - [data1], [data2] - From a PDA.") - log_game("[key_name(usr)] changed the Status Message to - [data1], [data2] - From a PDA.") - if("alert") - status_signal.data["picture_state"] = data1 - - frequency.post_signal(src, status_signal) - -/obj/item/cartridge/proc/generate_menu(mob/user) - if(!host_pda) - return - switch(host_pda.mode) - if(40) //signaller - menu = "

    [PDAIMG(signaler)] Remote Signaling System

    " - - menu += {" -Send Signal
    -Frequency: -- -- -[format_frequency(radio.frequency)] -+ -+
    -
    -Code: -- -- -[radio.code] -+ -+
    "} - if (41) //crew manifest - menu = "

    [PDAIMG(notes)] Crew Manifest

    " - menu += "
    [GLOB.data_core.get_manifest_html(monochrome=TRUE)]
    " - - if (42) //status displays - menu = "

    [PDAIMG(status)] Station Status Display Interlink

    " - - menu += "\[ Clear \]
    " - menu += "\[ Shuttle ETA \]
    " - menu += "\[ Message \]" - menu += "
    " - menu += "\[ Alert: None |" - menu += " Red Alert |" - menu += " Lockdown |" - menu += " Biohazard \]
    " - - if (43) - menu = "

    [PDAIMG(power)] Power Monitors - Please select one


    " - powmonitor = null - powermonitors = list() - var/powercount = 0 - - - - var/turf/pda_turf = get_turf(src) - for(var/obj/machinery/computer/monitor/pMon in GLOB.machines) - if(pMon.machine_stat & (NOPOWER | BROKEN)) //check to make sure the computer is functional - continue - if(pda_turf.get_virtual_z_level() != pMon.get_virtual_z_level()) //and that we're on the same zlevel as the computer (lore: limited signal strength) - continue - if(pMon.is_secret_monitor) //make sure it isn't a secret one (ie located on a ruin), allowing people to metagame that the location exists - continue - powercount++ - powermonitors += pMon - - - if(!powercount) - menu += "No connection
    " - else - - menu += "" - var/count = 0 - for(var/obj/machinery/computer/monitor/pMon in powermonitors) - count++ - menu += "[pMon] - [get_area_name(pMon, TRUE)]
    " - - menu += "
    " - - if (433) - menu = "

    [PDAIMG(power)] Power Monitor


    " - if(!powmonitor || !powmonitor.get_powernet()) - menu += "No connection
    " - else - var/list/L = list() - var/datum/powernet/connected_powernet = powmonitor.get_powernet() - for(var/obj/machinery/power/terminal/term in connected_powernet.nodes) - if(istype(term.master, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = term.master - L += A - - menu += "
    Location: [get_area_name(powmonitor, TRUE)]
    Total power: [display_power(connected_powernet.viewavail)]
    Total load: [display_power(connected_powernet.viewload)]
    " - - menu += "" - - if(L.len > 0) - menu += "Area Eqp./Lgt./Env. Load Cell
    " - - var/list/S = list(" Off","AOff"," On", " AOn") - var/list/chg = list("N","C","F") -//Neither copytext nor copytext_char is appropriate here; neither 30 UTF-8 code units nor 30 code points equates to 30 columns of output. -//Some glyphs are very tall or very wide while others are small or even take up no space at all. -//Emojis can take modifiers which are many characters but render as only one glyph. -//A proper solution here (as far as Unicode goes, maybe not ideal as far as markup goes, a table would be better) -//would be to use [A.area.name] - for(var/obj/machinery/power/apc/A in L) - menu += copytext_char(add_trailing(A.area.name, 30, " "), 1, 30) - if(A.integration_cog) - menu += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_leading(display_power(A.lastused_total), 6, " ")] 100% F
    " - else - menu += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_leading(display_power(A.lastused_total), 6, " ")] [A.cell ? "[add_leading("[round(A.cell.percent())]", 3, " ")]% [chg[A.charging+1]]" : " N/C"]
    " - - menu += "
    " - - if (44) //medical records //This thing only displays a single screen so it's hard to really get the sub-menu stuff working. - menu = "

    [PDAIMG(medical)] Medical Record List

    " - if(GLOB.data_core.general) - for(var/datum/data/record/R in sortRecord(GLOB.data_core.general)) - menu += "[R.fields["id"]]: [R.fields["name"]]
    " - menu += "
    " - if(441) - menu = "

    [PDAIMG(medical)] Medical Record

    " - - if(active1 in GLOB.data_core.general) - menu += "Name: [active1.fields["name"]] ID: [active1.fields["id"]]
    " - menu += "Gender: [active1.fields["gender"]]
    " //NSV13 - Gender Neutrality - menu += "Age: [active1.fields["age"]]
    " - menu += "Rank: [active1.fields["rank"]]
    " - menu += "Fingerprint: [active1.fields["fingerprint"]]
    " - menu += "Physical Status: [active1.fields["p_stat"]]
    " - menu += "Mental Status: [active1.fields["m_stat"]]
    " - else - menu += "Record Lost!
    " - - menu += "
    " - - menu += "

    [PDAIMG(medical)] Medical Data

    " - if(active2 in GLOB.data_core.medical) - menu += "Blood Type: [active2.fields["blood_type"]]

    " - - menu += "Minor Disabilities: [active2.fields["mi_dis"]]
    " - menu += "Details: [active2.fields["mi_dis_d"]]

    " - - menu += "Major Disabilities: [active2.fields["ma_dis"]]
    " - menu += "Details: [active2.fields["ma_dis_d"]]

    " - - menu += "Allergies: [active2.fields["alg"]]
    " - menu += "Details: [active2.fields["alg_d"]]

    " - - menu += "Current Diseases: [active2.fields["cdi"]]
    " - menu += "Details: [active2.fields["cdi_d"]]

    " - - menu += "Important Notes: [active2.fields["notes"]]
    " - else - menu += "Record Lost!
    " - - menu += "
    " - if (45) //security records - menu = "

    [PDAIMG(cuffs)] Security Record List

    " - if(GLOB.data_core.general) - for (var/datum/data/record/R in sortRecord(GLOB.data_core.general)) - menu += "
    [R.fields["id"]]: [R.fields["name"]]
    " - - menu += "
    " - if(451) - menu = "

    [PDAIMG(cuffs)] Security Record

    " - - if(active1 in GLOB.data_core.general) - menu += "Name: [active1.fields["name"]] ID: [active1.fields["id"]]
    " - menu += "Gender: [active1.fields["gender"]]
    " //NSV13 - Gender Neutrality - menu += "Age: [active1.fields["age"]]
    " - menu += "Rank: [active1.fields["rank"]]
    " - menu += "Fingerprint: [active1.fields["fingerprint"]]
    " - menu += "Physical Status: [active1.fields["p_stat"]]
    " - menu += "Mental Status: [active1.fields["m_stat"]]
    " - else - menu += "Record Lost!
    " - - menu += "
    " - - menu += "

    [PDAIMG(cuffs)] Security Data

    " - if(active3 in GLOB.data_core.security) - menu += "Criminal Status: [active3.fields["criminal"]]
    " - - menu += text("
    \nCrimes:") - - menu +={" - - - - - -"} - for(var/datum/data/crime/c in active3.fields["crim"]) - menu += "" - menu += "" - menu += "" - menu += "" - menu += "" - menu += "
    CrimeDetailsAuthorTime Added
    [c.crimeName][c.crimeDetails][c.author][c.time]
    " - - menu += "
    \nImportant Notes:
    " - menu += "[active3.fields["notes"]]" - else - menu += "Record Lost!
    " - - menu += "
    " - - if (47) //quartermaster order records - menu = "

    [PDAIMG(crate)] Supply Record Interlink

    " - - menu += "
    Supply shuttle
    " - menu += "Location: " - switch(SSshuttle.supply.mode) - if(SHUTTLE_CALL) - menu += "Moving to " - if(!is_station_level(SSshuttle.supply.z)) - menu += "station" - else - menu += "CentCom" - menu += " ([SSshuttle.supply.timeLeft(600)] Mins)" - else - menu += "At " - if(!is_station_level(SSshuttle.supply.z)) - menu += "CentCom" - else - menu += "station" - menu += "
    Current approved orders:
      " - for(var/S in SSshuttle.shoppinglist) - var/datum/supply_order/SO = S - menu += "
    1. #[SO.id] - [SO.pack.name] approved by [SO.orderer] [SO.reason ? "([SO.reason])":""]
    2. " - menu += "
    " - - menu += "Current requests:
      " - for(var/S in SSshuttle.requestlist) - var/datum/supply_order/SO = S - menu += "
    1. #[SO.id] - [SO.pack.name] requested by [SO.orderer]
    2. " - menu += "
    Upgrade NOW to Space Parts & Space Vendors PLUS for full remote order control and inventory management." - - if (48) // quartermaster ore logs - menu = list("

    [PDAIMG(crate)] Ore Silo Logs

    ") - if (GLOB.ore_silo_default) - var/list/logs = GLOB.silo_access_logs[REF(GLOB.ore_silo_default)] - var/len = LAZYLEN(logs) - var/i = 0 - for(var/M in logs) - if (++i > 30) - menu += "(... older logs not shown ...)" - break - var/datum/ore_silo_log/entry = M - menu += "[len - i]. [entry.formatted]

    " - if(i == 0) - menu += "Nothing!" - else - menu += "No ore silo detected!" - menu = jointext(menu, "") - - if (53) // Newscaster - menu = "

    [PDAIMG(notes)] Newscaster Access

    " - menu += "
    Current Newsfeed:
    [current_channel ? current_channel : "None"]
    " - var/datum/newscaster/feed_channel/current - for(var/datum/newscaster/feed_channel/chan in GLOB.news_network.network_channels) - if (chan.channel_name == current_channel) - current = chan - if(!current) - menu += "
    ERROR : NO CHANNEL FOUND
    " - return menu - var/i = 1 - for(var/datum/newscaster/feed_message/msg in current.messages) - menu +="-[msg.returnBody(-1)]
    \[Story by [msg.returnAuthor(-1)]\]
    " - menu +="[msg.comments.len] comment[msg.comments.len > 1 ? "s" : ""]
    " - if(msg.img) - user << browse_rsc(msg.img, "tmp_photo[i].png") - menu +="
    " - i++ - for(var/datum/newscaster/feed_comment/comment in msg.comments) - menu +="[comment.body]
    [comment.author] [comment.time_stamp]
    " - menu += "
    Post Message" - - if (54) // Beepsky, Medibot, Floorbot, and Cleanbot access - menu = "

    [PDAIMG(medbot)] Bots Interlink

    " - bot_control() - if (55) // Emoji Guidebook for mimes - menu = "

    [PDAIMG(emoji)] Emoji Guidebook

    " - var/static/list/emoji_icon_states - var/static/emoji_table - if(!emoji_table) - var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat) - var/list/collate = list("
    ") - for(var/emoji in sortList(icon_states(icon('icons/emoji.dmi')))) - var/tag = sheet.icon_tag("emoji-[emoji]") - collate += "" - collate += "
    [emoji][tag]

    " - emoji_table = collate.Join() - - menu += "
    To use an emoji in a pda message, refer to the guide and add \":\" around the emoji. Your PDA supports the following emoji:
    " - menu += emoji_table - - if (99) //Newscaster message permission error - menu = "
    ERROR : NOT AUTHORIZED [host_pda.id ? "" : "- ID SLOT EMPTY"]
    " - - return menu - -/obj/item/cartridge/Topic(href, href_list) - ..() - - if(!usr.canUseTopic(src, !issilicon(usr))) - usr.unset_machine() - usr << browse(null, "window=pda") - return - - switch(href_list["choice"]) - if("Medical Records") - active1 = find_record("id", href_list["target"], GLOB.data_core.general) - if(active1) - active2 = find_record("id", href_list["target"], GLOB.data_core.medical) - host_pda.mode = 441 - if(!active2) - active1 = null - - if("Security Records") - active1 = find_record("id", href_list["target"], GLOB.data_core.general) - if(active1) - active3 = find_record("id", href_list["target"], GLOB.data_core.security) - host_pda.mode = 451 - if(!active3) - active1 = null - - if("Send Signal") - INVOKE_ASYNC(radio, TYPE_PROC_REF(/obj/item/integrated_signaler, send_activation)) - - if("Signal Frequency") - var/new_frequency = sanitize_frequency(radio.frequency + text2num(href_list["sfreq"])) - radio.set_frequency(new_frequency) - - if("Signal Code") - radio.code += text2num(href_list["scode"]) - radio.code = round(radio.code) - radio.code = min(100, radio.code) - radio.code = max(1, radio.code) - - if("Status") - switch(href_list["statdisp"]) - if("message") - post_status("message", message1, message2) - if("alert") - post_status("alert", href_list["alert"]) - if("setmsg1") - message1 = reject_bad_text(capped_input(usr, "Line 1", "Enter Message Text", message1), 40) - updateSelfDialog() - if("setmsg2") - message2 = reject_bad_text(capped_input(usr, "Line 2", "Enter Message Text", message2), 40) - updateSelfDialog() - else - post_status(href_list["statdisp"]) - if("Power Select") - var/pnum = text2num(href_list["target"]) - powmonitor = powermonitors[pnum] - host_pda.mode = 433 - - if("Supply Orders") - host_pda.mode =47 - - if("Newscaster Access") - host_pda.mode = 53 - - if("Newscaster Message") - var/host_pda_owner_name = host_pda.id ? "[host_pda.id.registered_name] ([host_pda.id.assignment])" : "Unknown" - var/message = host_pda.msg_input() - var/datum/newscaster/feed_channel/current - for(var/datum/newscaster/feed_channel/chan in GLOB.news_network.network_channels) - if (chan.channel_name == current_channel) - current = chan - if(current.locked && current.author != host_pda_owner_name) - host_pda.mode = 99 - host_pda.Topic(null,list("choice"="Refresh")) - return - GLOB.news_network.SubmitArticle(message,host_pda.owner,current_channel) - host_pda.Topic(null,list("choice"=num2text(host_pda.mode))) - return - - if("Newscaster Switch Channel") - current_channel = host_pda.msg_input() - host_pda.Topic(null,list("choice"=num2text(host_pda.mode))) - return - - //emoji previews - if(href_list["emoji"]) - var/parse = emoji_parse(":[href_list["emoji"]]:") - to_chat(usr, parse) - - //Bot control section! Viciously ripped from radios for being laggy and terrible. - if(href_list["op"]) - switch(href_list["op"]) - - if("control") - active_bot = locate(href_list["bot"]) in GLOB.bots_list - - if("botlist") - active_bot = null - if("summon") //Args are in the correct order, they are stated here just as an easy reminder. - active_bot.bot_control("summon", usr, host_pda.GetAccess()) - else //Forward all other bot commands to the bot itself! - active_bot.bot_control(href_list["op"], usr) - - if(href_list["mule"]) //MULEbots are special snowflakes, and need different args due to how they work. - var/mob/living/simple_animal/bot/mulebot/mule = active_bot - if (istype(mule)) - mule.bot_control(href_list["mule"], usr, pda=TRUE) - - if(!host_pda) - return - host_pda.attack_self(usr) - - -/obj/item/cartridge/proc/bot_control() - if(active_bot) - menu += "[active_bot]
    Status: ([PDAIMG(refresh)]refresh)
    " - menu += "Model: [active_bot.model]
    " - menu += "Location: [get_area(active_bot)]
    " - menu += "Mode: [active_bot.get_mode()]" - if(active_bot.allow_pai) - menu += "
    pAI: " - if(active_bot.paicard?.pai) - menu += "[active_bot.paicard.pai.name]" - if(active_bot.bot_core.allowed(usr)) - menu += " (eject)" - else - menu += "none" - - //MULEs! - if(active_bot.bot_type == MULE_BOT) - var/mob/living/simple_animal/bot/mulebot/MULE = active_bot - var/atom/Load = MULE.load - menu += "
    Current Load: [ !Load ? "none" : "[Load.name] (unload)" ]
    " - menu += "Destination: [MULE.destination ? MULE.destination : "None"] (set)
    " - menu += "Set ID: [MULE.suffix] Modify
    " - menu += "Power: [MULE.cell ? MULE.cell.percent() : 0]%
    " - menu += "Home: [!MULE.home_destination ? "none" : MULE.home_destination ]
    " - menu += "Delivery Reporting: [MULE.report_delivery ? "(On)": "(Off)"]
    " - menu += "Auto Return Home: [MULE.auto_return ? "(On)": "(Off)"]
    " - menu += "Auto Pickup Crate: [MULE.auto_pickup ? "(On)": "(Off)"]

    " //Hue. - - menu += "\[Stop\] " - menu += "\[Proceed\] " - menu += "\[Return Home\]
    " - - else - menu += "
    \[Stop Patrol\] " //patrolon - menu += "\[Start Patrol\] " //patroloff - menu += "\[Summon Bot\]
    " //summon - menu += "Keep an ID inserted to upload access codes upon summoning." - - menu += "
    [PDAIMG(back)]Return to bot list" - else - menu += "
    [PDAIMG(refresh)]Scan for active bots

    " - var/turf/current_turf = get_turf(src) - var/zlevel = current_turf.get_virtual_z_level() - var/botcount = 0 - for(var/B in GLOB.bots_list) //Git da botz - var/mob/living/simple_animal/bot/Bot = B - if(!Bot.on || Bot.remote_disabled || !(bot_access_flags & Bot.bot_type)) //Only non-emagged bots are detected! - continue //Also, the PDA must have access to the bot type. - if(Bot.get_virtual_z_level() in SSmapping.levels_by_trait(ZTRAIT_STATION)) - if(zlevel in SSmapping.levels_by_trait(ZTRAIT_STATION)) - menu += "[Bot.name] ([Bot.get_mode()])
    " - botcount++ - else if(Bot.get_virtual_z_level() == zlevel) - if(!(zlevel in SSmapping.levels_by_trait(ZTRAIT_STATION))) - menu += "
    [Bot.name] ([Bot.get_mode()])
    " - botcount++ - if(!botcount) //No bots at all? Lame. - menu += "No bots found.
    " - return - - return menu - -//If the cartridge adds a special line to the top of the messaging app -/obj/item/cartridge/proc/message_header() - return "" - -//If the cartridge adds something to each potetial messaging target -/obj/item/cartridge/proc/message_special(obj/item/pda/target) - return "" - -//This is called for special abilities of cartridges -/obj/item/cartridge/proc/special(mob/living/user, list/params) diff --git a/code/game/objects/items/devices/PDA/virus_cart.dm b/code/game/objects/items/devices/PDA/virus_cart.dm deleted file mode 100644 index 474ebebe595..00000000000 --- a/code/game/objects/items/devices/PDA/virus_cart.dm +++ /dev/null @@ -1,107 +0,0 @@ -/obj/item/cartridge/virus - name = "Generic Virus PDA cart" - var/charges = 5 - -/obj/item/cartridge/virus/proc/send_virus(obj/item/pda/target, mob/living/U) - return - -/obj/item/cartridge/virus/message_header() - return "[charges] viral files left.
    " - -/obj/item/cartridge/virus/message_special(obj/item/pda/target) - if (!istype(loc, /obj/item/pda)) - return "" //Sanity check, this shouldn't be possible. - return " (
    *Send Virus*)" - -/obj/item/cartridge/virus/special(mob/living/user, list/params) - var/obj/item/pda/P = locate(params["target"]) in GLOB.PDAs //Leaving it alone in case it may do something useful, I guess. - send_virus(P,user) - -/obj/item/cartridge/virus/clown - name = "\improper Honkworks 5.0 cartridge" - icon_state = "cart-clown" - desc = "A data cartridge for portable microcomputers. It smells vaguely of bananas." - access = CART_CLOWN - -/obj/item/cartridge/virus/clown/send_virus(obj/item/pda/target, mob/living/U) - if(charges <= 0) - to_chat(U, "Out of charges.") - return - if(!isnull(target) && !target.toff) - charges-- - to_chat(U, "Virus Sent!") - target.honkamt = (rand(15,20)) - else - to_chat(U, "PDA not found.") - -/obj/item/cartridge/virus/mime - name = "\improper Gestur-O 1000 cartridge" - icon_state = "cart-mi" - access = CART_MIME - -/obj/item/cartridge/virus/mime/send_virus(obj/item/pda/target, mob/living/U) - if(charges <= 0) - to_chat(U, "Out of charges.") - return - if(!isnull(target) && !target.toff) - charges-- - to_chat(U, "Virus Sent!") - target.silent = TRUE - target.ttone = "silence" - else - to_chat(U, "PDA not found.") - -/obj/item/cartridge/virus/syndicate - name = "\improper Detomatix cartridge" - icon_state = "cart-detomatrix" - access = CART_REMOTE_DOOR - remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!! //don't ask about the name, testing. - charges = 4 - -/obj/item/cartridge/virus/syndicate/send_virus(obj/item/pda/target, mob/living/U) - if(charges <= 0) - to_chat(U, "Out of charges.") - return - if(!isnull(target) && !target.toff) - charges-- - var/difficulty = 0 - if(target.cartridge) - difficulty += bit_count(target.cartridge.access&(CART_MEDICAL | CART_SECURITY | CART_ENGINE | CART_CLOWN | CART_MANIFEST)) - if(target.cartridge.access & CART_MANIFEST) - difficulty++ //if cartridge has manifest access it has extra snowflake difficulty - else - difficulty += 2 - var/datum/component/uplink/hidden_uplink = target.GetComponent(/datum/component/uplink) - if(!target.detonatable || prob(difficulty * 15) || (hidden_uplink)) - U.show_message("An error flashes on your [src].", MSG_VISUAL) - else - log_bomber(U, "triggered a PDA explosion on", target, "[!is_special_character(U) ? "(TRIGGED BY NON-ANTAG)" : ""]") - U.show_message("Success!", MSG_VISUAL) - target.explode() - else - to_chat(U, "PDA not found.") - -/obj/item/cartridge/virus/frame - name = "\improper F.R.A.M.E. cartridge" - icon_state = "cart-prove" - var/telecrystals = 0 - -/obj/item/cartridge/virus/frame/send_virus(obj/item/pda/target, mob/living/U) - if(charges <= 0) - to_chat(U, "Out of charges.") - return - if(!isnull(target) && !target.toff) - charges-- - var/lock_code = "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]" - to_chat(U, "Virus Sent! The unlock code to the target is: [lock_code]") - var/datum/component/uplink/hidden_uplink = target.GetComponent(/datum/component/uplink) - if(!hidden_uplink) - hidden_uplink = target.AddComponent(/datum/component/uplink) - hidden_uplink.unlock_code = lock_code - else - hidden_uplink.hidden_crystals += hidden_uplink.telecrystals //Temporarially hide the PDA's crystals, so you can't steal telecrystals. - hidden_uplink.telecrystals = telecrystals - telecrystals = 0 - hidden_uplink.active = TRUE - else - to_chat(U, "PDA not found.") diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index 6162b067f30..3d12b10ddb4 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -137,6 +137,8 @@ src.pai = personality src.add_overlay("pai-null") + pai.modularInterface?.saved_identification = pai.name + playsound(loc, 'sound/effects/pai_boot.ogg', 50, 1, -1) audible_message("\The [src] plays a cheerful startup noise!") diff --git a/code/game/objects/items/mop.dm b/code/game/objects/items/mop.dm index 10192c36555..b4122f97843 100644 --- a/code/game/objects/items/mop.dm +++ b/code/game/objects/items/mop.dm @@ -23,6 +23,11 @@ /obj/item/mop/Initialize(mapload) . = ..() create_reagents(mopcap) + GLOB.janitor_devices += src + +/obj/item/mop/Destroy() + GLOB.janitor_devices -= src + return ..() /obj/item/mop/proc/clean(turf/A) if(reagents.has_reagent(/datum/reagent/water, 1) || reagents.has_reagent(/datum/reagent/water/holywater, 1) || reagents.has_reagent(/datum/reagent/consumable/ethanol/vodka, 1) || reagents.has_reagent(/datum/reagent/space_cleaner, 1)) diff --git a/code/game/objects/items/stacks/telecrystal.dm b/code/game/objects/items/stacks/telecrystal.dm index 1d45f8b640b..e45971db4c9 100644 --- a/code/game/objects/items/stacks/telecrystal.dm +++ b/code/game/objects/items/stacks/telecrystal.dm @@ -23,8 +23,8 @@ /obj/item/stack/telecrystal/afterattack(obj/item/I, mob/user, proximity) . = ..() - if(istype(I, /obj/item/cartridge/virus/frame)) - var/obj/item/cartridge/virus/frame/cart = I + if(istype(I, /obj/item/computer_hardware/hard_drive/role/virus/frame)) + var/obj/item/computer_hardware/hard_drive/role/virus/frame/cart = I if(!cart.charges) to_chat(user, "[cart] is out of charges, it's refusing to accept [src].") return diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm index 4336923df53..8e50e27f01d 100644 --- a/code/game/objects/items/storage/backpack.dm +++ b/code/game/objects/items/storage/backpack.dm @@ -659,7 +659,7 @@ STR.silent = TRUE /obj/item/storage/backpack/duffelbag/clown/syndie/PopulateContents() - new /obj/item/pda/clown(src) + new /obj/item/modular_computer/tablet/pda/clown(src) new /obj/item/clothing/under/rank/civilian/clown(src) new /obj/item/clothing/shoes/clown_shoes(src) new /obj/item/clothing/mask/gas/clown_hat(src) diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index 771e5de0fb2..da250477491 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -615,14 +615,14 @@ /obj/item/storage/box/PDAs/PopulateContents() for(var/i in 1 to 4) - new /obj/item/pda(src) - new /obj/item/cartridge/head(src) - - var/newcart = pick( /obj/item/cartridge/engineering, - /obj/item/cartridge/security, - /obj/item/cartridge/medical, - /obj/item/cartridge/signal/toxins, - /obj/item/cartridge/quartermaster) + new /obj/item/modular_computer/tablet/pda(src) + new /obj/item/computer_hardware/hard_drive/role/head(src) + + var/newcart = pick( /obj/item/computer_hardware/hard_drive/role/engineering, + /obj/item/computer_hardware/hard_drive/role/security, + /obj/item/computer_hardware/hard_drive/role/medical, + /obj/item/computer_hardware/hard_drive/role/signal/toxins, + /obj/item/computer_hardware/hard_drive/role/cargo_technician) new newcart(src) /obj/item/storage/box/silver_ids @@ -651,15 +651,15 @@ new /obj/item/card/id/prisoner/seven(src) /obj/item/storage/box/seccarts - name = "box of PDA security cartridges" - desc = "A box full of PDA cartridges used by Security." + name = "box of PDA security job disks" + desc = "A box full of PDA job disks used by Security." icon_state = "secbox" illustration = "pda" /obj/item/storage/box/seccarts/PopulateContents() - new /obj/item/cartridge/detective(src) + new /obj/item/computer_hardware/hard_drive/role/detective(src) for(var/i in 1 to 6) - new /obj/item/cartridge/security(src) + new /obj/item/computer_hardware/hard_drive/role/security(src) /obj/item/storage/box/firingpins name = "box of standard firing pins" @@ -1314,7 +1314,7 @@ /obj/item/storage/box/debugtools/PopulateContents() var/static/items_inside = list( /obj/item/flashlight/emp/debug=1,\ - /obj/item/pda=1,\ + /obj/item/modular_computer/tablet/pda=1,\ /obj/item/modular_computer/tablet/preset/advanced=1,\ /obj/item/storage/belt/military/abductor/full=1,\ /obj/item/geiger_counter=1,\ diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index d0433c5a75a..f42020ac02b 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -103,7 +103,7 @@ new /obj/item/doorCharge(src) new /obj/item/camera_bug(src) new /obj/item/sbeacondrop/powersink(src) - new /obj/item/cartridge/virus/syndicate(src) + new /obj/item/computer_hardware/hard_drive/role/virus/syndicate(src) new /obj/item/storage/toolbox/syndicate(src) //To actually get to those places new /obj/item/pizzabox/bomb(src) new /obj/item/storage/box/syndie_kit/emp(src) @@ -464,7 +464,7 @@ new /obj/item/storage/backpack/chameleon(src) new /obj/item/radio/headset/chameleon(src) new /obj/item/stamp/chameleon(src) - new /obj/item/pda/chameleon(src) + new /obj/item/modular_computer/tablet/pda/chameleon(src) new /obj/item/razor(src) new /obj/item/handmirror(src) new /obj/item/clothing/head/wig(src) @@ -484,7 +484,7 @@ new /obj/item/storage/backpack/chameleon(src) new /obj/item/radio/headset/chameleon(src) new /obj/item/stamp/chameleon(src) - new /obj/item/pda/chameleon(src) + new /obj/item/modular_computer/tablet/pda/chameleon(src) //5*(2*4) = 5*8 = 45, 45 damage if you hit one person with all 5 stars. //Not counting the damage it will do while embedded (2*4 = 8, at 15% chance) @@ -524,7 +524,7 @@ new /obj/item/radio/headset/headset_cent/empty(src) new /obj/item/clothing/glasses/sunglasses/advanced(src) new /obj/item/storage/backpack/satchel(src) - new /obj/item/pda/heads(src) + new /obj/item/modular_computer/tablet/pda/heads(src) new /obj/item/clipboard(src) /obj/item/storage/box/syndie_kit/chameleon/broken/PopulateContents() @@ -538,7 +538,7 @@ new /obj/item/storage/backpack/chameleon/broken(src) new /obj/item/radio/headset/chameleon/broken(src) new /obj/item/stamp/chameleon/broken(src) - new /obj/item/pda/chameleon/broken(src) + new /obj/item/modular_computer/tablet/pda/chameleon/broken(src) new /obj/item/card/id/syndicate/broken(src) // No chameleon laser, they can't randomise for //REASONS// 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 ff270ca6e76..b8fff05478c 100644 --- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm @@ -53,7 +53,7 @@ /obj/structure/closet/jcloset/PopulateContents() ..() new /obj/item/clothing/under/rank/civilian/janitor(src) - new /obj/item/cartridge/janitor(src) + new /obj/item/computer_hardware/hard_drive/role/janitor(src) new /obj/item/clothing/gloves/color/black(src) new /obj/item/clothing/head/soft/purple(src) new /obj/item/paint/paint_remover(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm index f829cf34b17..3f5e4f623f2 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm @@ -19,7 +19,7 @@ new /obj/item/storage/box/radiokey/eng(src) new /obj/item/storage/box/command_keys(src) new /obj/item/megaphone/command(src) - new /obj/item/cartridge/ce(src) + new /obj/item/computer_hardware/hard_drive/role/ce(src) new /obj/item/storage/bag/construction(src) new /obj/item/circuitboard/machine/techfab/department/engineering(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm index b5a4b29a33c..f2105e1a464 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm @@ -96,7 +96,7 @@ new /obj/item/storage/box/radiokey/med(src) new /obj/item/storage/box/command_keys(src) new /obj/item/megaphone/command(src) - new /obj/item/cartridge/cmo(src) + new /obj/item/computer_hardware/hard_drive/role/cmo(src) new /obj/item/storage/lockbox/medal/med(src) new /obj/item/circuitboard/machine/techfab/department/medical(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm index 7bb7db28b0e..c7ef92fb412 100755 --- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm @@ -22,7 +22,7 @@ new /obj/item/storage/box/radiokey/sci(src) new /obj/item/storage/box/command_keys(src) new /obj/item/megaphone/command(src) - new /obj/item/cartridge/rd(src) + new /obj/item/computer_hardware/hard_drive/role/rd(src) new /obj/item/storage/lockbox/medal/sci(src) new /obj/item/laser_pointer(src) new /obj/item/circuitboard/machine/techfab/department/science(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index a3418571de3..48c37cf8fe6 100755 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -26,7 +26,7 @@ new /obj/item/storage/box/radiokey/com(src) new /obj/item/storage/box/command_keys(src) new /obj/item/megaphone/command(src) - new /obj/item/cartridge/captain(src) + new /obj/item/computer_hardware/hard_drive/role/captain(src) new /obj/item/storage/box/silver_ids(src) new /obj/item/restraints/handcuffs/cable/zipties(src) @@ -67,7 +67,7 @@ new /obj/item/storage/box/radiokey/srv(src) new /obj/item/storage/box/command_keys(src) - new /obj/item/cartridge/hop(src) + new /obj/item/computer_hardware/hard_drive/role/hop(src) new /obj/item/storage/box/ids(src) new /obj/item/storage/box/ids(src) new /obj/item/restraints/handcuffs/cable/zipties(src) @@ -138,7 +138,7 @@ new /obj/item/storage/box/radiokey/sec(src) new /obj/item/storage/box/command_keys(src) new /obj/item/megaphone/sec(src) - new /obj/item/cartridge/hos(src) + new /obj/item/computer_hardware/hard_drive/role/hos(src) new /obj/item/storage/box/flashbangs(src) new /obj/item/storage/box/deputy(src) new /obj/item/storage/lockbox/medal/sec(src) diff --git a/code/game/objects/structures/crates_lockers/closets/syndicate.dm b/code/game/objects/structures/crates_lockers/closets/syndicate.dm index 9f9ab50f107..8fd78d0a3c3 100644 --- a/code/game/objects/structures/crates_lockers/closets/syndicate.dm +++ b/code/game/objects/structures/crates_lockers/closets/syndicate.dm @@ -25,7 +25,7 @@ new /obj/item/storage/box/flashbangs(src) new /obj/item/storage/box/teargas(src) new /obj/item/storage/backpack/duffelbag/syndie/med(src) - new /obj/item/pda/syndicate(src) + new /obj/item/modular_computer/tablet/pda/syndicate(src) /obj/structure/closet/syndicate/resources desc = "An old, dusty locker." diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index a91bb28a328..28fc643f1cd 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -544,7 +544,7 @@ playsound(src, 'sound/machines/click.ogg', 20, TRUE) toggle_lock() return - if(istype(I, /obj/item/pda)) + if(istype(I, /obj/item/modular_computer)) return TRUE ui_update() . = ..() diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index 60d2024f672..da5fd9fa6f9 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -19,6 +19,11 @@ /obj/structure/janitorialcart/Initialize(mapload) . = ..() create_reagents(100, OPENCONTAINER) + GLOB.janitor_devices += src + +/obj/structure/janitorialcart/Destroy() + GLOB.janitor_devices -= src + return ..() /obj/structure/janitorialcart/proc/wet_mop(obj/item/mop, mob/user) if(reagents.total_volume < 1) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 174defe9c39..3e48900aaed 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -81,6 +81,7 @@ GLOBAL_PROTECT(admin_verbs_admin) /client/proc/openTicketManager, /client/proc/battle_royale, /client/proc/delete_book, + /client/proc/cmd_admin_send_pda_msg, /client/proc/changeranks, //NSV13 - verb to change rank structure /client/proc/system_manager, //Nsv13 - Fleet + starsystem management /client/proc/instance_overmap_menu, //Nsv13 - Midround ship creation. diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 4cbaeed7bf6..4b269ea4ac8 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -204,10 +204,12 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that id.update_label() if(worn) - if(istype(worn, /obj/item/pda)) - var/obj/item/pda/PDA = worn - PDA.id = id - id.forceMove(PDA) + if(istype(worn, /obj/item/modular_computer/tablet/pda)) + var/obj/item/modular_computer/tablet/pda/PDA = worn + var/obj/item/computer_hardware/card_slot/card = PDA.all_components[MC_CARD] + qdel(card.stored_card) + if(card) + card.try_insert(id, H) else if(istype(worn, /obj/item/storage/wallet)) var/obj/item/storage/wallet/W = worn W.front_id = id diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index af840c75bb2..b3fae2315a9 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1349,3 +1349,15 @@ Traitors and the like can also be revived with the previous role mostly intact. var/turf/T = get_turf(usr) new /mob/living/carbon/human(T) log_admin("[key_name(usr)] spawned a mindless human.") + +/client/proc/cmd_admin_send_pda_msg() + set name = "Send PDA Message" + set category = "Adminbus" + + if(!check_rights(R_ADMIN)) + return + var/obj/machinery/telecomms/message_server/server + for(var/obj/machinery/telecomms/message_server/S in GLOB.telecomms_list) + server = S + break + tgui_send_admin_pda(usr, null, server, theme = "admin", allow_send_all = TRUE) diff --git a/code/modules/antagonists/traitor/equipment/contractor.dm b/code/modules/antagonists/traitor/equipment/contractor.dm index 16b1a0aac8c..7827bf9f394 100644 --- a/code/modules/antagonists/traitor/equipment/contractor.dm +++ b/code/modules/antagonists/traitor/equipment/contractor.dm @@ -184,7 +184,7 @@ uniform = /obj/item/clothing/under/chameleon suit = /obj/item/clothing/suit/chameleon back = /obj/item/storage/backpack - belt = /obj/item/pda/chameleon + belt = /obj/item/modular_computer/tablet/pda/chameleon mask = /obj/item/clothing/mask/cigarette/syndicate shoes = /obj/item/clothing/shoes/chameleon/noslip ears = /obj/item/radio/headset/chameleon diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index e1a3c8b54f7..009ca048067 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -194,6 +194,15 @@ Insert("language-[icon_state]", icon, icon_state=icon_state) ..() +/datum/asset/spritesheet/emoji + name = "emoji" + +/datum/asset/spritesheet/emoji/register() + var/icon/I = icon('icons/emoji.dmi') + I.Scale(48, 48) + InsertAll("", I) + ..() + /datum/asset/simple/lobby assets = list( "playeroptions.css" = 'html/browser/playeroptions.css' diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm index 869edff37d3..2637c2d9825 100644 --- a/code/modules/atmospherics/machinery/airalarm.dm +++ b/code/modules/atmospherics/machinery/airalarm.dm @@ -741,7 +741,7 @@ to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"].") update_icon() return - else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda))// trying to unlock the interface with an ID card + else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/modular_computer/tablet/pda))// trying to unlock the interface with an ID card togglelock(user) return else if(panel_open && is_wire_tool(W)) diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 81b45895be8..45d5cc86506 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -213,9 +213,9 @@ H.equipOutfit(outfit) if(disable_pda) // We don't want corpse PDAs to show up in the messenger list. - var/obj/item/pda/PDA = locate(/obj/item/pda) in H + var/obj/item/modular_computer/tablet/pda/PDA = locate(/obj/item/modular_computer/tablet/pda) in H if(PDA) - PDA.toff = TRUE + PDA.messenger_invisible = TRUE if(disable_sensors) // Using crew monitors to find corpses while creative makes finding certain ruins too easy. var/obj/item/clothing/under/C = H.w_uniform @@ -266,7 +266,7 @@ var/mob/living/silicon/ai/spawned/M = new(loc) //spawn new AI at landmark as var M M.name = src.name M.real_name = src.name - M.aiPDA.toff = TRUE //turns the AI's PDA messenger off, stopping it showing up on player PDAs + M.modularInterface.messenger_invisible = TRUE //turns the AI's PDA messenger off, stopping it showing up on player PDAs M.death() //call the AI's death proc qdel(src) @@ -347,7 +347,7 @@ /obj/effect/mob_spawn/human/doctor/alive/equip(mob/living/carbon/human/H) ..() // Remove radio and PDA so they wouldn't annoy station crew. - var/list/del_types = list(/obj/item/pda, /obj/item/radio/headset) + var/list/del_types = list(/obj/item/modular_computer/tablet/pda, /obj/item/radio/headset) for(var/del_type in del_types) var/obj/item/I = locate(del_type) in H qdel(I) diff --git a/code/modules/cargo/exports/gear.dm b/code/modules/cargo/exports/gear.dm index 72126ff5d87..519986bbb2c 100644 --- a/code/modules/cargo/exports/gear.dm +++ b/code/modules/cargo/exports/gear.dm @@ -85,7 +85,7 @@ /datum/export/gear/goldpda cost = 500 unit_name = "gilded PDA" - export_types = list(/obj/item/pda/vip) + export_types = list(/obj/item/modular_computer/tablet/pda/vip) /datum/export/gear/envirosuitvip cost = 4500 diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm index 123da270bf9..088169735f5 100644 --- a/code/modules/cargo/expressconsole.dm +++ b/code/modules/cargo/expressconsole.dm @@ -37,7 +37,7 @@ to_chat(user, "You set [src] to use the [DB.department_name].") account_type = DB.department_ID return - if((istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) && allowed(user)) + if((istype(W, /obj/item/card/id) || istype(W, /obj/item/modular_computer/tablet/pda)) && allowed(user)) locked = !locked to_chat(user, "You [locked ? "lock" : "unlock"] the interface.") return diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index 1f085827d93..22f52079573 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -2045,10 +2045,10 @@ /datum/supply_pack/service/vending/ptech name = "PTech Supply Crate" - desc = "Not enough cartridges after half the crew lost their PDA to explosions? This may fix it." + desc = "Not enough job disks after half the crew lost their PDA to explosions? This may fix it." cost = 800 access_budget = ACCESS_HOP - contains = list(/obj/item/vending_refill/cart) + contains = list(/obj/item/vending_refill/job_disk) crate_name = "ptech supply crate" /datum/supply_pack/service/vending/snack diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index c64b828b87f..08cb9cf5ef0 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -215,6 +215,11 @@ if(ispath(picked_item, /obj/item/card/id)) var/mob/living/carbon/human/H = user H?.sec_hud_set_ID() + if(istype(target, /obj/item/modular_computer/tablet/pda)) + var/mob/living/carbon/human/H = user + H?.sec_hud_set_ID() + var/obj/item/modular_computer/tablet/pda/PDA = target + PDA.update_id_display() var/obj/item/thing = target thing.update_slot_icon() @@ -700,25 +705,25 @@ item_state = "syndie_headset" bang_protect = 3 -/obj/item/pda/chameleon - name = "PDA" +/obj/item/modular_computer/tablet/pda/chameleon + name = "tablet" var/datum/action/item_action/chameleon/change/chameleon_action -/obj/item/pda/chameleon/Initialize(mapload) +/obj/item/modular_computer/tablet/pda/chameleon/Initialize(mapload) . = ..() chameleon_action = new(src) - chameleon_action.chameleon_type = /obj/item/pda - chameleon_action.chameleon_name = "PDA" - chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/pda/heads, /obj/item/pda/ai, /obj/item/pda/ai/pai), only_root_path = TRUE) + chameleon_action.chameleon_type = /obj/item/modular_computer/tablet/pda + chameleon_action.chameleon_name = "tablet" + chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/modular_computer/tablet/pda/heads), only_root_path = TRUE) chameleon_action.initialize_disguises() -/obj/item/pda/chameleon/emp_act(severity) +/obj/item/modular_computer/tablet/pda/chameleon/emp_act(severity) . = ..() if(. & EMP_PROTECT_SELF) return chameleon_action.emp_randomise() -/obj/item/pda/chameleon/broken/Initialize(mapload) +/obj/item/modular_computer/tablet/pda/chameleon/broken/Initialize(mapload) . = ..() chameleon_action.emp_randomise(INFINITY) diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm index 9d0751c5ae2..630e2a33f4d 100644 --- a/code/modules/clothing/outfits/ert.dm +++ b/code/modules/clothing/outfits/ert.dm @@ -172,7 +172,7 @@ belt = /obj/item/gun/energy/e_gun l_pocket = /obj/item/pen back = /obj/item/storage/backpack/satchel - r_pocket = /obj/item/pda/heads + r_pocket = /obj/item/modular_computer/tablet/pda/heads l_hand = /obj/item/clipboard id = /obj/item/card/id/centcom @@ -180,10 +180,9 @@ if(visualsOnly) return - var/obj/item/pda/heads/pda = H.r_store - pda.owner = H.real_name - pda.ownjob = JOB_CENTCOM_OFFICIAL - pda.update_label() + var/obj/item/modular_computer/tablet/pda/heads/pda = H.r_store + pda.saved_identification = H.real_name + pda.saved_job = JOB_CENTCOM_OFFICIAL var/obj/item/card/id/W = H.wear_id W.icon_state = "centcom" @@ -362,7 +361,7 @@ /datum/outfit/centcom_clown name = "Code Banana ERT" id = /obj/item/card/id/centcom - belt = /obj/item/pda/clown + belt = /obj/item/modular_computer/tablet/pda/clown ears = /obj/item/radio/headset/headset_cent uniform = /obj/item/clothing/under/rank/civilian/clown back = /obj/item/storage/backpack/clown diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm index 1cbf8c1bfe0..85ad5994a3e 100644 --- a/code/modules/clothing/outfits/standard.dm +++ b/code/modules/clothing/outfits/standard.dm @@ -195,7 +195,7 @@ l_pocket = /obj/item/melee/transforming/energy/sword/saber l_hand = /obj/item/storage/secure/briefcase id = /obj/item/card/id/syndicate - belt = /obj/item/pda/heads + belt = /obj/item/modular_computer/tablet/pda/heads /datum/outfit/assassin/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) var/obj/item/clothing/under/U = H.w_uniform @@ -215,10 +215,9 @@ SEND_SIGNAL(sec_briefcase, COMSIG_TRY_STORAGE_INSERT, new /obj/item/ammo_box/a357, null, TRUE, TRUE) SEND_SIGNAL(sec_briefcase, COMSIG_TRY_STORAGE_INSERT, new /obj/item/grenade/plastic/x4, null, TRUE, TRUE) - var/obj/item/pda/heads/pda = H.belt - pda.owner = H.real_name - pda.ownjob = "Reaper" - pda.update_label() + var/obj/item/modular_computer/tablet/pda/heads/pda = H.belt + pda.saved_identification = H.real_name + pda.saved_job = "Reaper" var/obj/item/card/id/syndicate/W = H.wear_id W.access = get_all_accesses() diff --git a/code/modules/crew_objectives/civilian_objectives.dm b/code/modules/crew_objectives/civilian_objectives.dm index 43888838141..f300b9e4929 100644 --- a/code/modules/crew_objectives/civilian_objectives.dm +++ b/code/modules/crew_objectives/civilian_objectives.dm @@ -178,8 +178,8 @@ /datum/objective/crew/slipster/check_completion() var/list/uniqueslips = list() if(owner?.current) - for(var/obj/item/pda/clown/PDA in owner.current.get_contents()) - for(var/mob/living/carbon/human/H in PDA.slipvictims) + for(var/obj/item/modular_computer/tablet/pda/clown/PDA in owner.current.get_contents()) + for(var/H in PDA.slip_victims) uniqueslips |= H if(uniqueslips.len >= target_amount) return TRUE diff --git a/code/modules/emoji/emoji_parse.dm b/code/modules/emoji/emoji_parse.dm index 185341d294c..a7f6c06613f 100644 --- a/code/modules/emoji/emoji_parse.dm +++ b/code/modules/emoji/emoji_parse.dm @@ -2,7 +2,6 @@ . = text if(!CONFIG_GET(flag/emojis)) return - var/static/list/emojis = icon_states(icon('icons/emoji.dmi')) var/parsed = "" var/pos = 1 var/search = 0 diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index 52582b7f224..f5e2676e2da 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -299,7 +299,7 @@ uniform = /obj/item/clothing/under/color/grey id = /obj/item/card/id ears = /obj/item/radio/headset - belt = /obj/item/pda + belt = /obj/item/modular_computer/tablet/pda back = /obj/item/storage/backpack shoes = /obj/item/clothing/shoes/sneakers/black box = /obj/item/storage/box/survival @@ -364,11 +364,11 @@ break H.sec_hud_set_ID() - var/obj/item/pda/PDA = H.get_item_by_slot(pda_slot) + var/obj/item/modular_computer/tablet/pda/PDA = H.get_item_by_slot(pda_slot) if(istype(PDA)) - PDA.owner = H.real_name - PDA.ownjob = J.title - PDA.update_label() + PDA.saved_identification = C.registered_name + PDA.saved_job = C.assignment + PDA.update_id_display() /datum/outfit/job/get_chameleon_disguise_info() var/list/types = ..() @@ -387,7 +387,7 @@ //NSV13 /datum/job/proc/get_rank() return display_rank - + //why is this as part of a job? because it's something every human recieves at roundstart after all other initializations and factors job in. it fits best with the equipment proc //this gives a dormant disease for the virologist to check for. if this disease actually does something to the mob... call me, or your local coder /datum/job/proc/dormant_disease_check(mob/living/carbon/human/H) diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm index 7604d8e0332..c0636ae3bb4 100644 --- a/code/modules/jobs/job_types/assistant.dm +++ b/code/modules/jobs/job_types/assistant.dm @@ -33,7 +33,7 @@ Assistant /datum/outfit/job/assistant name = JOB_NAME_ASSISTANT jobtype = /datum/job/assistant - belt = /obj/item/pda/assistant + belt = /obj/item/modular_computer/tablet/pda/assistant id = /obj/item/card/id/job/assistant /datum/outfit/job/assistant/pre_equip(mob/living/carbon/human/H) diff --git a/code/modules/jobs/job_types/atmospheric_technician.dm b/code/modules/jobs/job_types/atmospheric_technician.dm index 34336017254..0d4fa293d78 100644 --- a/code/modules/jobs/job_types/atmospheric_technician.dm +++ b/code/modules/jobs/job_types/atmospheric_technician.dm @@ -32,7 +32,7 @@ id = /obj/item/card/id/job/atmospheric_technician belt = /obj/item/storage/belt/utility/atmostech - l_pocket = /obj/item/pda/atmospheric_technician + l_pocket = /obj/item/modular_computer/tablet/pda/atmospheric_technician ears = /obj/item/radio/headset/headset_eng uniform = /obj/item/clothing/under/rank/engineering/atmospheric_technician r_pocket = /obj/item/analyzer @@ -42,7 +42,6 @@ duffelbag = /obj/item/storage/backpack/duffelbag/engineering box = /obj/item/storage/box/engineer pda_slot = ITEM_SLOT_LPOCKET - backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced/atmos=1) /datum/outfit/job/atmospheric_technician/rig name = "Atmospheric Technician (Hardsuit)" diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm index 2e3a5e387a9..c73340a2b30 100644 --- a/code/modules/jobs/job_types/bartender.dm +++ b/code/modules/jobs/job_types/bartender.dm @@ -29,7 +29,7 @@ id = /obj/item/card/id/job/bartender glasses = /obj/item/clothing/glasses/sunglasses/advanced/reagent - belt = /obj/item/pda/bartender + belt = /obj/item/modular_computer/tablet/pda/bartender ears = /obj/item/radio/headset/headset_srv uniform = /obj/item/clothing/under/rank/civilian/bartender suit = /obj/item/clothing/suit/armor/vest diff --git a/code/modules/jobs/job_types/botanist.dm b/code/modules/jobs/job_types/botanist.dm index 9db932adffc..ca02d36530a 100644 --- a/code/modules/jobs/job_types/botanist.dm +++ b/code/modules/jobs/job_types/botanist.dm @@ -28,7 +28,7 @@ jobtype = /datum/job/botanist id = /obj/item/card/id/job/botanist - belt = /obj/item/pda/service + belt = /obj/item/modular_computer/tablet/pda/botanist //NSV13 ears = /obj/item/radio/headset/headset_srv uniform = /obj/item/clothing/under/rank/civilian/hydroponics suit = /obj/item/clothing/suit/apron diff --git a/code/modules/jobs/job_types/brig_physician.dm b/code/modules/jobs/job_types/brig_physician.dm index 09446f9114e..83c3afa9744 100644 --- a/code/modules/jobs/job_types/brig_physician.dm +++ b/code/modules/jobs/job_types/brig_physician.dm @@ -34,7 +34,7 @@ jobtype = /datum/job/brig_physician id = /obj/item/card/id/job/brig_physician - belt = /obj/item/pda/brig_physician + belt = /obj/item/modular_computer/tablet/pda/brig_physician ears = /obj/item/radio/headset/headset_medsec uniform = /obj/item/clothing/under/rank/brig_physician shoes = /obj/item/clothing/shoes/sneakers/white diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm index a6142c84d98..5b4455796be 100755 --- a/code/modules/jobs/job_types/captain.dm +++ b/code/modules/jobs/job_types/captain.dm @@ -44,7 +44,7 @@ id = /obj/item/card/id/job/captain belt = /obj/item/melee/classic_baton/telescopic/stunsword //NSV13 - stunsword - l_pocket = /obj/item/pda/captain //NSV13 - moved PDA to pocket + l_pocket = /obj/item/modular_computer/tablet/pda/heads/captain //NSV13 - moved PDA to pocket glasses = /obj/item/clothing/glasses/sunglasses/advanced ears = /obj/item/radio/headset/heads/captain/alt gloves = /obj/item/clothing/gloves/color/captain @@ -52,7 +52,7 @@ suit = /obj/item/clothing/suit/ship/officer //NSV13 - officer's uniform shoes = /obj/item/clothing/shoes/jackboots //NSV13 - boots head = /obj/item/clothing/head/beret/ship/captain //NSV13 - captain's beret - backpack_contents = list(/obj/item/melee/classic_baton/police/telescopic=1, /obj/item/station_charter=1, /obj/item/modular_computer/tablet/preset/advanced/command=1, /obj/item/squad_pager/all_channels=1) //NSV13 - added squad pager + backpack_contents = list(/obj/item/melee/classic_baton/police/telescopic=1, /obj/item/station_charter=1, /obj/item/squad_pager/all_channels=1) //NSV13 - added squad pager backpack = /obj/item/storage/backpack/captain satchel = /obj/item/storage/backpack/satchel/cap diff --git a/code/modules/jobs/job_types/cargo_technician.dm b/code/modules/jobs/job_types/cargo_technician.dm index 39fb3792933..7cbc4cf31b4 100644 --- a/code/modules/jobs/job_types/cargo_technician.dm +++ b/code/modules/jobs/job_types/cargo_technician.dm @@ -30,7 +30,7 @@ jobtype = /datum/job/cargo_technician id = /obj/item/card/id/job/cargo_technician - belt = /obj/item/pda/cargo_technician + belt = /obj/item/modular_computer/tablet/pda/cargo_technician ears = /obj/item/radio/headset/headset_cargo uniform = /obj/item/clothing/under/rank/cargo/tech l_hand = /obj/item/export_scanner diff --git a/code/modules/jobs/job_types/chaplain.dm b/code/modules/jobs/job_types/chaplain.dm index 817627572c1..9f26a683f59 100644 --- a/code/modules/jobs/job_types/chaplain.dm +++ b/code/modules/jobs/job_types/chaplain.dm @@ -115,7 +115,7 @@ jobtype = /datum/job/chaplain id = /obj/item/card/id/job/chaplain - belt = /obj/item/pda/chaplain + belt = /obj/item/modular_computer/tablet/pda/chaplain ears = /obj/item/radio/headset/headset_srv uniform = /obj/item/clothing/under/rank/civilian/chaplain backpack_contents = list( diff --git a/code/modules/jobs/job_types/chemist.dm b/code/modules/jobs/job_types/chemist.dm index daa46e20be3..371a49d7dc7 100644 --- a/code/modules/jobs/job_types/chemist.dm +++ b/code/modules/jobs/job_types/chemist.dm @@ -34,7 +34,7 @@ jobtype = /datum/job/chemist id = /obj/item/card/id/job/chemist glasses = /obj/item/clothing/glasses/science - belt = /obj/item/pda/chemist + belt = /obj/item/modular_computer/tablet/pda/chemist ears = /obj/item/radio/headset/headset_med uniform = /obj/item/clothing/under/rank/medical/chemist shoes = /obj/item/clothing/shoes/sneakers/white diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm index b2fa2550e31..36c26f07608 100644 --- a/code/modules/jobs/job_types/chief_engineer.dm +++ b/code/modules/jobs/job_types/chief_engineer.dm @@ -43,15 +43,14 @@ id = /obj/item/card/id/job/chief_engineer belt = /obj/item/storage/belt/utility/chief/full - l_pocket = /obj/item/pda/heads/chief_engineer + l_pocket = /obj/item/modular_computer/tablet/pda/heads/chief_engineer ears = /obj/item/radio/headset/heads/chief_engineer uniform = /obj/item/clothing/under/rank/engineering/chief_engineer suit = /obj/item/clothing/suit/ship/engineer shoes = /obj/item/clothing/shoes/workboots //NSV13 Give us boots that match head = /obj/item/clothing/head/beret/ce //NSV13 Give us our cool beret back gloves = /obj/item/clothing/gloves/color/black - backpack_contents = list(/obj/item/melee/classic_baton/police/telescopic=1, - /obj/item/modular_computer/tablet/preset/advanced/command=1) + backpack_contents = list(/obj/item/melee/classic_baton/police/telescopic=1) backpack = /obj/item/storage/backpack/industrial satchel = /obj/item/storage/backpack/satchel/eng diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm index c9acfa1a485..51db39d1035 100644 --- a/code/modules/jobs/job_types/chief_medical_officer.dm +++ b/code/modules/jobs/job_types/chief_medical_officer.dm @@ -42,7 +42,7 @@ jobtype = /datum/job/chief_medical_officer id = /obj/item/card/id/job/chief_medical_officer - belt = /obj/item/pda/heads/chief_medical_officer + belt = /obj/item/modular_computer/tablet/pda/heads/chief_medical_officer l_pocket = /obj/item/pinpointer/crew ears = /obj/item/radio/headset/heads/cmo head = /obj/item/clothing/head/beret/durathread @@ -51,8 +51,7 @@ suit = /obj/item/clothing/suit/toggle/labcoat/cmo l_hand = /obj/item/storage/firstaid/medical suit_store = /obj/item/flashlight/pen - backpack_contents = list(/obj/item/melee/classic_baton/police/telescopic=1, - /obj/item/modular_computer/tablet/preset/advanced/command=1) + backpack_contents = list(/obj/item/melee/classic_baton/police/telescopic=1) backpack = /obj/item/storage/backpack/medic satchel = /obj/item/storage/backpack/satchel/med diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm index 6d98cbd9fdc..e0ee39ae5c0 100644 --- a/code/modules/jobs/job_types/clown.dm +++ b/code/modules/jobs/job_types/clown.dm @@ -32,7 +32,7 @@ name = JOB_NAME_CLOWN jobtype = /datum/job/clown - belt = /obj/item/pda/clown + belt = /obj/item/modular_computer/tablet/pda/clown ears = /obj/item/radio/headset/headset_srv uniform = /obj/item/clothing/under/rank/civilian/clown shoes = /obj/item/clothing/shoes/clown_shoes diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm index b73c78bf6c3..ec80fd24c33 100644 --- a/code/modules/jobs/job_types/cook.dm +++ b/code/modules/jobs/job_types/cook.dm @@ -30,7 +30,7 @@ jobtype = /datum/job/cook id = /obj/item/card/id/job/cook - belt = /obj/item/pda/cook + belt = /obj/item/modular_computer/tablet/pda/cook ears = /obj/item/radio/headset/headset_srv uniform = /obj/item/clothing/under/rank/civilian/chef suit = /obj/item/clothing/suit/toggle/chef diff --git a/code/modules/jobs/job_types/curator.dm b/code/modules/jobs/job_types/curator.dm index 0b189b9ef73..c12315e4af9 100644 --- a/code/modules/jobs/job_types/curator.dm +++ b/code/modules/jobs/job_types/curator.dm @@ -32,7 +32,7 @@ id = /obj/item/card/id/job/curator shoes = /obj/item/clothing/shoes/laceup - belt = /obj/item/pda/curator + belt = /obj/item/modular_computer/tablet/pda/curator ears = /obj/item/radio/headset/headset_curator uniform = /obj/item/clothing/under/rank/civilian/curator l_hand = /obj/item/storage/bag/books diff --git a/code/modules/jobs/job_types/deputy.dm b/code/modules/jobs/job_types/deputy.dm index 3a32fb5d4fe..b2b4ee10814 100644 --- a/code/modules/jobs/job_types/deputy.dm +++ b/code/modules/jobs/job_types/deputy.dm @@ -35,7 +35,7 @@ shoes = /obj/item/clothing/shoes/sneakers/black glasses = /obj/item/clothing/glasses/hud/security/deputy head = /obj/item/clothing/head/soft/sec - l_pocket = /obj/item/pda/deputy + l_pocket = /obj/item/modular_computer/tablet/pda/deputy backpack = /obj/item/storage/backpack/security satchel = /obj/item/storage/backpack/satchel/sec diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm index f9255f1fc33..129ad736b2f 100644 --- a/code/modules/jobs/job_types/detective.dm +++ b/code/modules/jobs/job_types/detective.dm @@ -35,7 +35,7 @@ jobtype = /datum/job/detective id = /obj/item/card/id/job/detective - belt = /obj/item/pda/detective + belt = /obj/item/modular_computer/tablet/pda/detective ears = /obj/item/radio/headset/headset_sec/alt uniform = /obj/item/clothing/under/rank/security/detective neck = /obj/item/clothing/neck/tie/detective diff --git a/code/modules/jobs/job_types/exploration_team.dm b/code/modules/jobs/job_types/exploration_team.dm index f1eb7f8c540..86858412d27 100644 --- a/code/modules/jobs/job_types/exploration_team.dm +++ b/code/modules/jobs/job_types/exploration_team.dm @@ -56,7 +56,7 @@ jobtype = /datum/job/exploration_crew id = /obj/item/card/id/job/exploration_crew - belt = /obj/item/pda/exploration_crew + belt = /obj/item/modular_computer/tablet/pda/exploration_crew ears = /obj/item/radio/headset/headset_exploration shoes = /obj/item/clothing/shoes/jackboots gloves = /obj/item/clothing/gloves/color/black @@ -77,7 +77,7 @@ name = "Exploration Crew (Engineer)" belt = /obj/item/storage/belt/utility/full - r_pocket = /obj/item/pda/exploration_crew + r_pocket = /obj/item/modular_computer/tablet/pda/exploration_crew backpack_contents = list( /obj/item/kitchen/knife/combat/survival=1, diff --git a/code/modules/jobs/job_types/geneticist.dm b/code/modules/jobs/job_types/geneticist.dm index 8cbb5d2c76f..d19362d5fac 100644 --- a/code/modules/jobs/job_types/geneticist.dm +++ b/code/modules/jobs/job_types/geneticist.dm @@ -33,7 +33,7 @@ name = JOB_NAME_GENETICIST jobtype = /datum/job/geneticist id = /obj/item/card/id/job/geneticist - belt = /obj/item/pda/geneticist + belt = /obj/item/modular_computer/tablet/pda/geneticist ears = /obj/item/radio/headset/headset_medsci uniform = /obj/item/clothing/under/rank/medical/geneticist shoes = /obj/item/clothing/shoes/sneakers/white diff --git a/code/modules/jobs/job_types/gimmick.dm b/code/modules/jobs/job_types/gimmick.dm index 1fffacd5d56..7a41fc370c4 100644 --- a/code/modules/jobs/job_types/gimmick.dm +++ b/code/modules/jobs/job_types/gimmick.dm @@ -52,7 +52,7 @@ jobtype = /datum/job/gimmick/barber id = /obj/item/card/id/job/barber - belt = /obj/item/pda/unlicensed + belt = /obj/item/modular_computer/tablet/pda/unlicensed ears = /obj/item/radio/headset uniform = /obj/item/clothing/under/suit/sl shoes = /obj/item/clothing/shoes/laceup @@ -87,7 +87,7 @@ jobtype = /datum/job/gimmick/stage_magician id = /obj/item/card/id/job/stage_magician - belt = /obj/item/pda/unlicensed + belt = /obj/item/modular_computer/tablet/pda/unlicensed head = /obj/item/clothing/head/that ears = /obj/item/radio/headset neck = /obj/item/bedsheet/magician @@ -128,7 +128,7 @@ jobtype = /datum/job/gimmick/psychiatrist id = /obj/item/card/id/job/psychiatrist - belt = /obj/item/pda/medical + belt = /obj/item/modular_computer/tablet/pda/medical ears = /obj/item/radio/headset/headset_med uniform = /obj/item/clothing/under/suit/black shoes = /obj/item/clothing/shoes/laceup @@ -160,7 +160,7 @@ jobtype = /datum/job/gimmick/vip id = /obj/item/card/id/gold/vip - belt = /obj/item/pda/vip + belt = /obj/item/modular_computer/tablet/pda/vip glasses = /obj/item/clothing/glasses/sunglasses/advanced ears = /obj/item/radio/headset/heads //VIP can talk loud for no reason uniform = /obj/item/clothing/under/suit/black_really diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm index 26c4c7ceb09..2bd69fd0c94 100644 --- a/code/modules/jobs/job_types/head_of_personnel.dm +++ b/code/modules/jobs/job_types/head_of_personnel.dm @@ -48,7 +48,7 @@ jobtype = /datum/job/head_of_personnel id = /obj/item/card/id/job/head_of_personnel - r_pocket = /obj/item/pda/heads/head_of_personnel + r_pocket = /obj/item/modular_computer/tablet/pda/heads/head_of_personnel belt = /obj/item/melee/classic_baton/telescopic/stunsword ears = /obj/item/radio/headset/heads/xo //NSV13 - update to XO uniform = /obj/item/clothing/under/ship/officer @@ -57,7 +57,7 @@ head = /obj/item/clothing/head/beret/ship/xo //NSV13 - update to XO backpack_contents = list(/obj/item/storage/box/ids=1, /obj/item/melee/classic_baton/police/telescopic=1, - /obj/item/modular_computer/tablet/preset/advanced/command=1, - /obj/item/squad_pager/all_channels=1, /obj/item/storage/box/squad_lanyards=1) // NSV13 - added squad lanyards and pager + /obj/item/squad_pager/all_channels=1, + /obj/item/storage/box/squad_lanyards=1) // NSV13 - added squad lanyards and pager chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/head_of_personnel) diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm index fd51c8bf456..279bfdddb6f 100644 --- a/code/modules/jobs/job_types/head_of_security.dm +++ b/code/modules/jobs/job_types/head_of_security.dm @@ -43,7 +43,7 @@ jobtype = /datum/job/head_of_security id = /obj/item/card/id/job/head_of_security - belt = /obj/item/pda/heads/head_of_security + belt = /obj/item/modular_computer/tablet/pda/heads/head_of_security ears = /obj/item/radio/headset/heads/hos/alt uniform = /obj/item/clothing/under/ship/peacekeeper //NSV13 shoes = /obj/item/clothing/shoes/jackboots @@ -55,8 +55,10 @@ r_pocket = /obj/item/assembly/flash/handheld l_pocket = /obj/item/restraints/handcuffs backpack_contents = list(/obj/item/melee/baton/loaded=1, - /obj/item/modular_computer/tablet/preset/advanced/command=1, - /obj/item/gun/ballistic/tazer, /obj/item/ammo_box/magazine/tazer_cartridge_storage=1, /obj/item/book/granter/martial/jujitsu, /obj/item/club=1) //NSV13 this line) + /obj/item/gun/ballistic/tazer, + /obj/item/ammo_box/magazine/tazer_cartridge_storage=1, + /obj/item/book/granter/martial/jujitsu, + /obj/item/club=1) //NSV13 this line) backpack = /obj/item/storage/backpack/security satchel = /obj/item/storage/backpack/satchel/sec diff --git a/code/modules/jobs/job_types/janitor.dm b/code/modules/jobs/job_types/janitor.dm index 35aa3b7a466..113ffea346b 100644 --- a/code/modules/jobs/job_types/janitor.dm +++ b/code/modules/jobs/job_types/janitor.dm @@ -30,7 +30,7 @@ jobtype = /datum/job/janitor id = /obj/item/card/id/job/janitor - belt = /obj/item/pda/janitor + belt = /obj/item/modular_computer/tablet/pda/janitor ears = /obj/item/radio/headset/headset_srv uniform = /obj/item/clothing/under/rank/civilian/janitor backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) diff --git a/code/modules/jobs/job_types/lawyer.dm b/code/modules/jobs/job_types/lawyer.dm index fae3a19924a..f7dc1925989 100644 --- a/code/modules/jobs/job_types/lawyer.dm +++ b/code/modules/jobs/job_types/lawyer.dm @@ -31,7 +31,7 @@ jobtype = /datum/job/lawyer id = /obj/item/card/id/job/lawyer - belt = /obj/item/pda/lawyer + belt = /obj/item/modular_computer/tablet/pda/lawyer ears = /obj/item/radio/headset/headset_srvsec uniform = /obj/item/clothing/under/rank/civilian/lawyer/bluesuit suit = /obj/item/clothing/suit/toggle/lawyer diff --git a/code/modules/jobs/job_types/medical_doctor.dm b/code/modules/jobs/job_types/medical_doctor.dm index 4428060504e..b5e61ea929c 100644 --- a/code/modules/jobs/job_types/medical_doctor.dm +++ b/code/modules/jobs/job_types/medical_doctor.dm @@ -33,7 +33,7 @@ jobtype = /datum/job/medical_doctor id = /obj/item/card/id/job/medical_doctor - belt = /obj/item/pda/medical + belt = /obj/item/modular_computer/tablet/pda/medical ears = /obj/item/radio/headset/headset_med uniform = /obj/item/clothing/under/ship/medical //NSV13 - ship jumpsuit shoes = /obj/item/clothing/shoes/sneakers/white diff --git a/code/modules/jobs/job_types/mime.dm b/code/modules/jobs/job_types/mime.dm index 8c7261b56a5..95b5d5d5812 100644 --- a/code/modules/jobs/job_types/mime.dm +++ b/code/modules/jobs/job_types/mime.dm @@ -33,7 +33,7 @@ jobtype = /datum/job/mime id = /obj/item/card/id/job/mime - belt = /obj/item/pda/mime + belt = /obj/item/modular_computer/tablet/pda/mime ears = /obj/item/radio/headset/headset_srv uniform = /obj/item/clothing/under/rank/civilian/mime mask = /obj/item/clothing/mask/gas/mime diff --git a/code/modules/jobs/job_types/paramedic.dm b/code/modules/jobs/job_types/paramedic.dm index ab95ae244a3..49f1abc8523 100644 --- a/code/modules/jobs/job_types/paramedic.dm +++ b/code/modules/jobs/job_types/paramedic.dm @@ -41,7 +41,7 @@ jobtype = /datum/job/paramedic id = /obj/item/card/id/job/paramedic - belt = /obj/item/pda/paramedic + belt = /obj/item/modular_computer/tablet/pda/paramedic ears = /obj/item/radio/headset/headset_med gloves = /obj/item/clothing/gloves/color/latex/nitrile uniform = /obj/item/clothing/under/ship/medical //NSV13 - ship jumpsuit diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm index d6a38da5759..cfceac95594 100644 --- a/code/modules/jobs/job_types/quartermaster.dm +++ b/code/modules/jobs/job_types/quartermaster.dm @@ -32,7 +32,7 @@ jobtype = /datum/job/quartermaster id = /obj/item/card/id/job/quartermaster - belt = /obj/item/pda/quartermaster + belt = /obj/item/modular_computer/tablet/pda/quartermaster ears = /obj/item/radio/headset/headset_quartermaster uniform = /obj/item/clothing/under/rank/cargo/quartermaster shoes = /obj/item/clothing/shoes/sneakers/brown diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm index c1d1e97a10b..938f07b3711 100644 --- a/code/modules/jobs/job_types/research_director.dm +++ b/code/modules/jobs/job_types/research_director.dm @@ -45,15 +45,14 @@ jobtype = /datum/job/research_director id = /obj/item/card/id/job/research_director - belt = /obj/item/pda/heads/research_director + belt = /obj/item/modular_computer/tablet/pda/heads/research_director ears = /obj/item/radio/headset/heads/research_director uniform = /obj/item/clothing/under/rank/rnd/research_director shoes = /obj/item/clothing/shoes/sneakers/brown suit = /obj/item/clothing/suit/toggle/labcoat/research_director l_hand = /obj/item/clipboard l_pocket = /obj/item/laser_pointer - backpack_contents = list(/obj/item/melee/classic_baton/police/telescopic=1, - /obj/item/modular_computer/tablet/preset/advanced/command=1) + backpack_contents = list(/obj/item/melee/classic_baton/police/telescopic=1) backpack = /obj/item/storage/backpack/science satchel = /obj/item/storage/backpack/satchel/tox diff --git a/code/modules/jobs/job_types/roboticist.dm b/code/modules/jobs/job_types/roboticist.dm index f308d920923..5a67b98b411 100644 --- a/code/modules/jobs/job_types/roboticist.dm +++ b/code/modules/jobs/job_types/roboticist.dm @@ -34,13 +34,12 @@ id = /obj/item/card/id/job/roboticist belt = /obj/item/storage/belt/utility/full - l_pocket = /obj/item/pda/roboticist + l_pocket = /obj/item/modular_computer/tablet/pda/roboticist ears = /obj/item/radio/headset/headset_sci uniform = /obj/item/clothing/under/rank/rnd/roboticist suit = /obj/item/clothing/suit/toggle/labcoat backpack = /obj/item/storage/backpack/science satchel = /obj/item/storage/backpack/satchel/tox - backpack_contents = list(/obj/item/modular_computer/tablet/preset/science=1) pda_slot = ITEM_SLOT_LPOCKET diff --git a/code/modules/jobs/job_types/scientist.dm b/code/modules/jobs/job_types/scientist.dm index 1ee8dda24c1..c798bbb2d26 100644 --- a/code/modules/jobs/job_types/scientist.dm +++ b/code/modules/jobs/job_types/scientist.dm @@ -34,7 +34,7 @@ jobtype = /datum/job/scientist id = /obj/item/card/id/job/scientist - belt = /obj/item/pda/toxins + belt = /obj/item/modular_computer/tablet/pda/science ears = /obj/item/radio/headset/headset_sci uniform = /obj/item/clothing/under/rank/rnd/scientist shoes = /obj/item/clothing/shoes/sneakers/white @@ -44,7 +44,6 @@ backpack = /obj/item/storage/backpack/science satchel = /obj/item/storage/backpack/satchel/tox - backpack_contents = list(/obj/item/modular_computer/tablet/preset/science=1) /datum/outfit/job/scientist/pre_equip(mob/living/carbon/human/H) ..() diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index d98c9564984..d4da7941ab6 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -124,7 +124,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S jobtype = /datum/job/security_officer id = /obj/item/card/id/job/security_officer - belt = /obj/item/pda/security + belt = /obj/item/modular_computer/tablet/pda/security ears = /obj/item/radio/headset/headset_sec/alt glasses = /obj/item/clothing/glasses/hud/security/sunglasses uniform = /obj/item/clothing/under/ship/peacekeeper @@ -137,7 +137,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S l_pocket = /obj/item/restraints/handcuffs r_pocket = /obj/item/assembly/flash/handheld suit_store = /obj/item/gun/ballistic/tazer //NSV13 - backpack_contents = list(/obj/item/gun/ballistic/tazer=1,/obj/item/ammo_box/magazine/tazer_cartridge_storage=1,/obj/item/pda/security, /obj/item/book/granter/martial/jujitsu, /obj/item/club=1) //NSV13) + backpack_contents = list(/obj/item/gun/ballistic/tazer=1,/obj/item/ammo_box/magazine/tazer_cartridge_storage=1,/obj/item/modular_computer/tablet/pda/security, /obj/item/book/granter/martial/jujitsu, /obj/item/club=1) //NSV13) backpack = /obj/item/storage/backpack/security satchel = /obj/item/storage/backpack/satchel/sec diff --git a/code/modules/jobs/job_types/shaft_miner.dm b/code/modules/jobs/job_types/shaft_miner.dm index 62dd48786b1..8f359365156 100644 --- a/code/modules/jobs/job_types/shaft_miner.dm +++ b/code/modules/jobs/job_types/shaft_miner.dm @@ -33,7 +33,7 @@ jobtype = /datum/job/shaft_miner id = /obj/item/card/id/job/shaft_miner - belt = /obj/item/pda/shaft_miner + belt = /obj/item/modular_computer/tablet/pda/shaft_miner ears = /obj/item/radio/headset/headset_cargo/shaft_miner shoes = /obj/item/clothing/shoes/workboots/mining gloves = /obj/item/clothing/gloves/color/black diff --git a/code/modules/jobs/job_types/station_engineer.dm b/code/modules/jobs/job_types/station_engineer.dm index ead9e2ed2ac..f4fc0cd2c58 100644 --- a/code/modules/jobs/job_types/station_engineer.dm +++ b/code/modules/jobs/job_types/station_engineer.dm @@ -35,7 +35,7 @@ id = /obj/item/card/id/job/station_engineer belt = /obj/item/storage/belt/utility/full/engi - l_pocket = /obj/item/pda/station_engineer + l_pocket = /obj/item/modular_computer/tablet/pda/station_engineer ears = /obj/item/radio/headset/headset_eng uniform = /obj/item/clothing/under/ship/engineer //NSV13 uniforms shoes = /obj/item/clothing/shoes/workboots @@ -48,7 +48,6 @@ duffelbag = /obj/item/storage/backpack/duffelbag/engineering box = /obj/item/storage/box/engineer pda_slot = ITEM_SLOT_LPOCKET - backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) /datum/outfit/job/engineer/gloved name = "Station Engineer (Gloves)" diff --git a/code/modules/jobs/job_types/virologist.dm b/code/modules/jobs/job_types/virologist.dm index 2db3502edac..bdcfc9aba69 100644 --- a/code/modules/jobs/job_types/virologist.dm +++ b/code/modules/jobs/job_types/virologist.dm @@ -41,7 +41,7 @@ shoes = /obj/item/clothing/shoes/sneakers/white suit = /obj/item/clothing/suit/toggle/labcoat/virologist suit_store = /obj/item/flashlight/pen - r_pocket = /obj/item/pda/virologist + r_pocket = /obj/item/modular_computer/tablet/pda/virologist backpack = /obj/item/storage/backpack/virology satchel = /obj/item/storage/backpack/satchel/vir diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm index f12e6605482..0b2b4ab4983 100644 --- a/code/modules/jobs/job_types/warden.dm +++ b/code/modules/jobs/job_types/warden.dm @@ -40,7 +40,7 @@ jobtype = /datum/job/warden id = /obj/item/card/id/job/warden - belt = /obj/item/pda/warden + belt = /obj/item/modular_computer/tablet/pda/warden ears = /obj/item/radio/headset/headset_sec/alt uniform = /obj/item/clothing/under/rank/security/warden shoes = /obj/item/clothing/shoes/jackboots diff --git a/code/modules/mining/abandoned_crates.dm b/code/modules/mining/abandoned_crates.dm index 2d82d0ddcb7..352b3288a10 100644 --- a/code/modules/mining/abandoned_crates.dm +++ b/code/modules/mining/abandoned_crates.dm @@ -218,7 +218,7 @@ new /obj/item/storage/backpack/clown(src) new /obj/item/clothing/under/rank/civilian/clown(src) new /obj/item/clothing/shoes/clown_shoes(src) - new /obj/item/pda/clown(src) + new /obj/item/modular_computer/tablet/pda/clown(src) new /obj/item/clothing/mask/gas/clown_hat(src) new /obj/item/bikehorn(src) new /obj/item/toy/crayon/rainbow(src) @@ -226,7 +226,7 @@ if(95) new /obj/item/clothing/under/rank/civilian/mime(src) new /obj/item/clothing/shoes/sneakers/black(src) - new /obj/item/pda/mime(src) + new /obj/item/modular_computer/tablet/pda/mime(src) new /obj/item/clothing/gloves/color/white(src) new /obj/item/clothing/mask/gas/mime(src) new /obj/item/clothing/head/beret(src) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 63a7486b32b..b868cb5f3a9 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -332,16 +332,16 @@ return fine = min(fine, maxFine) - var/crime = GLOB.data_core.createCrimeEntry(t1, "", allowed_access, station_time_timestamp(), fine) - for (var/obj/item/pda/P in GLOB.PDAs) - if(P.owner == R.fields["name"]) + var/datum/data/crime/crime = GLOB.data_core.createCrimeEntry(t1, "", allowed_access, station_time_timestamp(), fine) + for (var/obj/item/modular_computer/tablet in GLOB.TabletMessengers) + if(tablet.saved_identification == R.fields["name"]) var/message = "You have been fined [fine] credits for '[t1]'. Fines may be paid at security." - var/datum/signal/subspace/messaging/pda/signal = new(src, list( + var/datum/signal/subspace/messaging/tablet_msg/signal = new(src, list( "name" = "Security Citation", "job" = "Citation Server", "message" = message, - "targets" = list("[P.owner] ([P.ownjob])"), - "automated" = 1 + "targets" = list(tablet), + "automated" = TRUE )) signal.send_to_receivers() usr.log_message("(PDA: Citation Server) sent \"[message]\" to [signal.format_target()]", LOG_PDA) diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index a1f6f5b3d17..bef46105f65 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -16,9 +16,9 @@ if(id) . = id.assignment else - var/obj/item/pda/pda = wear_id + var/obj/item/modular_computer/pda = wear_id if(istype(pda)) - . = pda.ownjob + . = pda.saved_job else return if_no_id if(!.) @@ -30,9 +30,9 @@ var/obj/item/card/id/id = get_idcard(FALSE) if(id) return id.registered_name - var/obj/item/pda/pda = wear_id + var/obj/item/modular_computer/pda = wear_id if(istype(pda)) - return pda.owner + return pda.saved_identification return if_no_id //repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a separate proc as it'll be useful elsewhere @@ -64,15 +64,12 @@ //Useful when player is being seen by other mobs /mob/living/carbon/human/proc/get_id_name(if_no_id = "Unknown") var/obj/item/storage/wallet/wallet = wear_id - var/obj/item/pda/pda = wear_id - var/obj/item/card/id/id = wear_id var/obj/item/modular_computer/tablet/tablet = wear_id + var/obj/item/card/id/id = wear_id if(istype(wallet)) id = wallet.front_id if(istype(id)) . = id.registered_name - else if(istype(pda)) - . = pda.owner else if(istype(tablet)) var/obj/item/computer_hardware/card_slot/card_slot = tablet.all_components[MC_CARD] if(card_slot?.stored_card) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 08b26ce83d5..69aacdc66c0 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1130,7 +1130,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(!disable_warning) to_chat(H, "The [I.name] is too big to attach.") //should be src? return FALSE - if( istype(I, /obj/item/pda) || istype(I, /obj/item/pen) || is_type_in_list(I, H.wear_suit.allowed) ) + if(istype(I, /obj/item/modular_computer/tablet) || istype(I, /obj/item/pen) || is_type_in_list(I, H.wear_suit.allowed)) return TRUE return FALSE if(ITEM_SLOT_HANDCUFFED) diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index 25373b3ab22..4e98524fa88 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -233,14 +233,13 @@ playsound(src, 'sound/items/welder.ogg', 50, 1) -/obj/item/pda/lighteater_act(obj/item/light_eater/light_eater) +/obj/item/modular_computer/tablet/lighteater_act(obj/item/light_eater/light_eater) if(light_range && light_power > 0 && light_on) - //Eject the ID card - if(id) - id.forceMove(get_turf(src)) - id = null - update_icon() - playsound(src, 'sound/machines/terminal_eject.ogg', 50, TRUE) + // Only the queen of Beetania can save our IDs from this infernal nightmare + var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2] + var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] + card_slot2.try_eject() + card_slot.try_eject() ..() /obj/item/clothing/head/helmet/space/plasmaman/lighteater_act(obj/item/light_eater/light_eater, atom/parent) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index a0d2fa33500..d041306d6a5 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -200,20 +200,17 @@ return /mob/living/get_photo_description(obj/item/camera/camera) - var/list/mob_details = list() var/list/holding = list() var/len = length(held_items) if(len) for(var/obj/item/I in held_items) - if(!holding.len) + if(!length(holding)) holding += "[p_they(TRUE)] [p_are()] holding \a [I]" else if(held_items.Find(I) == len) holding += ", and \a [I]." else holding += ", \a [I]" - holding += "." - mob_details += "You can also see [src] on the photo[health < (maxHealth * 0.75) ? ", looking a bit hurt":""][holding ? ". [holding.Join("")]":"."]." - return mob_details.Join("") + return "You can also see [src] on the photo[health < (maxHealth * 0.75) ? ", looking a bit hurt":""].[length(holding) ? " [holding.Join("")].":""]" //Called when we bump onto an obj /mob/living/proc/ObjBump(obj/O) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index f71664c79fd..1e5881a721d 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -98,6 +98,8 @@ var/list/cam_hotkeys = new/list(9) var/cam_prev + var/atom/movable/screen/ai/modpc/interfaceButton + var/datum/robot_control/robot_control //NSV13 /mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai) @@ -132,6 +134,7 @@ job = JOB_NAME_AI + create_modularInterface() create_eye() if(client) apply_pref_name("ai",client) @@ -147,11 +150,6 @@ add_verb(/mob/living/silicon/ai/proc/show_laws_verb) - aiPDA = new/obj/item/pda/ai(src) - aiPDA.owner = real_name - aiPDA.ownjob = JOB_NAME_AI - aiPDA.name = real_name + " (" + aiPDA.ownjob + ")" - aiMulti = new(src) radio = new /obj/item/radio/headset/silicon/ai(src) aicamera = new/obj/item/camera/siliconcam/ai_camera(src) @@ -987,17 +985,12 @@ if(oldname != real_name) if(eyeobj) eyeobj.name = "[newname] (AI Eye)" + modularInterface.saved_identification = real_name // Notify Cyborgs for(var/mob/living/silicon/robot/Slave in connected_robots) Slave.show_laws() -/mob/living/silicon/ai/replace_identification_name(oldname,newname) - if(aiPDA) - aiPDA.owner = newname - aiPDA.name = newname + " (" + aiPDA.ownjob + ")" - - /mob/living/silicon/ai/proc/add_malf_picker() to_chat(src, "In the top right corner of the screen you will find the Malfunctions tab, where you can purchase various abilities, from upgraded surveillance to station ending doomsday devices.") to_chat(src, "You are also capable of hacking APCs, which grants you more points to spend on your Malfunction powers. The drawback is that a hacked APC will give you away if spotted by the crew. Hacking an APC takes 60 seconds.") diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 43ac4b15fca..fdb6787f62c 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -87,6 +87,7 @@ var/overload_bulletblock = 0 //Why is this a good idea? var/overload_maxhealth = 0 var/silent = FALSE + var/atom/movable/screen/ai/modpc/interface_button /mob/living/silicon/pai/can_unbuckle() @@ -126,16 +127,13 @@ aicamera = new /obj/item/camera/siliconcam/ai_camera(src) aicamera.flash_enabled = TRUE - //PDA - aiPDA = new/obj/item/pda/ai(src) - aiPDA.owner = real_name - aiPDA.ownjob = "pAI Messenger" - aiPDA.name = real_name + " (" + aiPDA.ownjob + ")" - . = ..() + create_modularInterface() emittersemicd = TRUE addtimer(CALLBACK(src, PROC_REF(emittercool)), 600) + return INITIALIZE_HINT_LATELOAD + /mob/living/silicon/pai/Life() if(hacking) @@ -160,6 +158,11 @@ D.open() hacking = FALSE + +/mob/living/silicon/pai/LateInitialize() + . = ..() + modularInterface.saved_identification = name + /mob/living/silicon/pai/make_laws() laws = new /datum/ai_laws/pai() return TRUE @@ -296,6 +299,7 @@ /mob/living/silicon/pai/process(delta_time) emitterhealth = CLAMP((emitterhealth + (emitterregen * delta_time)), -50, emittermaxhealth) + /obj/item/paicard/attackby(obj/item/W, mob/user, params) ..() user.set_machine(src) @@ -307,6 +311,11 @@ else to_chat(user, "Encryption Key ports not configured.") +/mob/living/silicon/pai/can_interact_with(atom/A) + if(A == modularInterface) + return TRUE + return ..() + /obj/item/paicard/emag_act(mob/user) // Emag to wipe the master DNA and supplemental directive if(!pai) return diff --git a/code/modules/mob/living/silicon/pai/pai_shell.dm b/code/modules/mob/living/silicon/pai/pai_shell.dm index 5d85e6cd489..cdb00414f8f 100644 --- a/code/modules/mob/living/silicon/pai/pai_shell.dm +++ b/code/modules/mob/living/silicon/pai/pai_shell.dm @@ -20,10 +20,6 @@ addtimer(CALLBACK(src, PROC_REF(emittercool)), emittercd) mobility_flags = MOBILITY_FLAGS_DEFAULT density = TRUE - if(istype(card.loc, /obj/item/pda)) - var/obj/item/pda/P = card.loc - P.pai = null - P.visible_message("[src] ejects itself from [P]!") if(isliving(card.loc)) var/mob/living/L = card.loc if(!L.temporarilyRemoveItemFromInventory(card)) diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/modules/mob/living/silicon/pai/signaler.dm similarity index 96% rename from code/game/objects/items/devices/PDA/radio.dm rename to code/modules/mob/living/silicon/pai/signaler.dm index 5eb75cea263..791f9f01561 100644 --- a/code/game/objects/items/devices/PDA/radio.dm +++ b/code/modules/mob/living/silicon/pai/signaler.dm @@ -1,3 +1,4 @@ +// Formerly [code/game/objects/items/devices/PDA/radio.dm] // Radio Cartridge, essentially a remote signaler with limited spectrum. /obj/item/integrated_signaler name = "\improper PDA radio module" diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index a7cd7dd6ba3..dcadb125dca 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -50,8 +50,6 @@ left_part = "" if("directives") left_part = directives() - if("pdamessage") - left_part = pdamessage() if("buy") left_part = downloadSoftware() if("manifest") @@ -217,21 +215,6 @@ to_chat(src, "You are not being carried by anyone!") return 0 // FALSE ? If you return here you won't call paiinterface() below - if("pdamessage") - if(!isnull(aiPDA)) - if(!aiPDA.owner) - aiPDA.owner = src.real_name - aiPDA.ownjob = "pAI" - if(href_list["toggler"]) - aiPDA.toff = !aiPDA.toff - else if(href_list["ringer"]) - aiPDA.silent = !aiPDA.silent - else if(href_list["target"]) - if(silent) - return alert("Communications circuits remain uninitialized.") - var/target = locate(href_list["target"]) in GLOB.PDAs - aiPDA.create_message(src, target) - if("medicalrecord") // Accessing medical records if(subscreen == 1) medicalActive1 = find_record("id", href_list["med_rec"], GLOB.data_core.general) @@ -318,8 +301,6 @@ // Basic dat += "Basic
    " for(var/s in software) - if(s == "digital messenger") - dat += "Digital Messenger
    " if(s == "crew manifest") dat += "Crew Manifest
    " if(s == "host scan") @@ -628,26 +609,6 @@ to_chat(AI, "Network Alert: Brute-force encryption crack in progress. Unable to pinpoint location.") hacking = TRUE -// Digital Messenger -/mob/living/silicon/pai/proc/pdamessage() - - var/dat = "

    Digital Messenger

    " - dat += {"Signal/Receiver Status: - [(aiPDA.toff) ? "\[Off\]" : "\[On\]"]
    - Ringer Status: - [(aiPDA.silent) ? "\[Off\]" : "\[On\]"]

    "} - dat += "
      " - if(!aiPDA.toff) - for (var/obj/item/pda/P in get_viewable_pdas()) - if (P == aiPDA) - continue - dat += "
    • [P]" - dat += "
    • " - dat += "
    " - dat += "

    " - dat += "Messages:
    [aiPDA.tnote]" - return dat - // Loudness Booster /mob/living/silicon/pai/proc/softwareLoudness() if(!internal_instrument) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index a8d343170e4..f02f1879c7f 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -86,9 +86,7 @@ var/sight_mode = 0 hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD, DIAG_TRACK_HUD) - ///The reference to the built-in tablet that borgs carry. - var/obj/item/modular_computer/tablet/integrated/modularInterface - var/atom/movable/screen/robot/modPC/interfaceButton + var/atom/movable/screen/robot/modpc/interfaceButton var/list/upgrades = list() @@ -179,17 +177,6 @@ diag_hud_set_borgcell() logevent("System brought online.") -/mob/living/silicon/robot/proc/create_modularInterface() - if(!modularInterface) - modularInterface = new /obj/item/modular_computer/tablet/integrated(src) - modularInterface.layer = ABOVE_HUD_PLANE - modularInterface.plane = ABOVE_HUD_PLANE - -/mob/living/silicon/robot/modules/syndicate/create_modularInterface() - if(!modularInterface) - modularInterface = new /obj/item/modular_computer/tablet/integrated/syndicate(src) - return ..() - /** * Sets the tablet theme and icon * @@ -198,12 +185,12 @@ */ /mob/living/silicon/robot/proc/set_modularInterface_theme() if(istype(module, /obj/item/robot_module/syndicate) || emagged) - modularInterface.device_theme = "syndicate" + modularInterface.device_theme = THEME_SYNDICATE modularInterface.icon_state = "tablet-silicon-syndicate" modularInterface.icon_state_powered = "tablet-silicon-syndicate" modularInterface.icon_state_unpowered = "tablet-silicon-syndicate" else - modularInterface.device_theme = "ntos" + modularInterface.device_theme = THEME_NTOS modularInterface.icon_state = "tablet-silicon" modularInterface.icon_state_powered = "tablet-silicon" modularInterface.icon_state_unpowered = "tablet-silicon" @@ -561,7 +548,7 @@ else to_chat(user, "Unable to locate a radio!") - else if (istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card + else if (istype(W, /obj/item/card/id)||istype(W, /obj/item/modular_computer/tablet/pda)) // trying to unlock the interface with an ID card togglelock(user) else if(istype(W, /obj/item/borg/upgrade/)) @@ -932,6 +919,8 @@ /mob/living/silicon/robot/modules/syndicate/create_modularInterface() if(!modularInterface) modularInterface = new /obj/item/modular_computer/tablet/integrated/syndicate(src) + modularInterface.saved_identification = real_name + modularInterface.saved_job = "Cyborg" return ..() /mob/living/silicon/robot/modules/syndicate/proc/show_playstyle() @@ -941,6 +930,11 @@ /mob/living/silicon/robot/modules/syndicate/ResetModule() return +/mob/living/silicon/robot/modules/syndicate/create_modularInterface() + if(!modularInterface) + modularInterface = new /obj/item/modular_computer/tablet/integrated/syndicate(src) + return ..() + /mob/living/silicon/robot/modules/syndicate/medical icon_state = "synd_medical" playstyle_string = "You are a Syndicate medical cyborg!
    \ @@ -1087,6 +1081,7 @@ notify_ai(RENAME, oldname, newname) if(!QDELETED(builtInCamera)) builtInCamera.c_tag = real_name + modularInterface.saved_identification = real_name custom_name = newname @@ -1314,26 +1309,3 @@ cell.charge = min(cell.charge + amount, cell.maxcharge) if(repairs) heal_bodypart_damage(repairs, repairs - 1) - -/** - * Records an IC event log entry in the cyborg's internal tablet. - * - * Creates an entry in the borglog list of the cyborg's internal tablet, listing the current - * in-game time followed by the message given. These logs can be seen by the cyborg in their - * BorgUI tablet app. By design, logging fails if the cyborg is dead. - * - * Arguments: - * arg1: a string containing the message to log. - */ -/mob/living/silicon/robot/proc/logevent(var/string = "") - if(!string) - return - if(stat == DEAD) //Dead borgs log no longer - return - if(!modularInterface) - stack_trace("Cyborg [src] ( [type] ) was somehow missing their integrated tablet. Please make a bug report.") - create_modularInterface() - modularInterface.borglog += "[station_time_timestamp()] - [string]" - var/datum/computer_file/program/borg_self_monitor/program = modularInterface.get_self_monitoring() - if(program) - program.force_full_update() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 8c2cec804d5..3781e60c925 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -46,7 +46,8 @@ var/hack_software = FALSE //Will be able to use hacking actions var/interaction_range = 7 //wireless control range - var/obj/item/pda/aiPDA + ///The reference to the built-in tablet that borgs carry. + var/obj/item/modular_computer/tablet/integrated/modularInterface //The internal ID card inside the AI. var/list/default_access_list = list() @@ -73,6 +74,30 @@ internal_id_card.name = "[src] internal access" internal_id_card.access |= access_list +/mob/living/silicon/proc/create_modularInterface() + if(!modularInterface) + modularInterface = new /obj/item/modular_computer/tablet/integrated(src) + modularInterface.layer = ABOVE_HUD_PLANE + modularInterface.plane = ABOVE_HUD_PLANE + modularInterface.saved_identification = real_name || name + if(iscyborg(src)) + modularInterface.saved_job = JOB_NAME_CYBORG + modularInterface.install_component(new /obj/item/computer_hardware/hard_drive/small/pda/robot) + if(isAI(src)) + modularInterface.saved_job = JOB_NAME_AI + modularInterface.install_component(new /obj/item/computer_hardware/hard_drive/small/pda/ai) + if(ispAI(src)) + modularInterface.saved_job = JOB_NAME_PAI + modularInterface.install_component(new /obj/item/computer_hardware/hard_drive/small/pda/ai) + +/mob/living/silicon/robot/model/syndicate/create_modularInterface() + if(!modularInterface) + modularInterface = new /obj/item/modular_computer/tablet/integrated/syndicate(src) + modularInterface.saved_identification = real_name + modularInterface.saved_job = JOB_NAME_CYBORG + return ..() + + /mob/living/silicon/med_hud_set_health() return //we use a different hud @@ -468,3 +493,38 @@ /mob/living/silicon/hears_radio() return FALSE + +/** + * Records an IC event log entry in the cyborg's internal tablet. + * + * Creates an entry in the borglog list of the cyborg's internal tablet (if it's a borg), listing the current + * in-game time followed by the message given. These logs can be seen by the cyborg in their + * BorgUI tablet app. By design, logging fails if the cyborg is dead. + * + * (This used to be in robot.dm. It's in here now.) + * + * Arguments: + * string: a string containing the message to log. + */ +/mob/living/silicon/proc/logevent(string = "") + if(!string) + return + if(stat == DEAD) //Dead silicons log no longer + return + if(!modularInterface) + stack_trace("Silicon [src] ( [type] ) was somehow missing their integrated tablet. Please make a bug report.") + create_modularInterface() + var/mob/living/silicon/robot/robo = modularInterface.borgo + if(istype(robo)) + modularInterface.borglog += "[station_time_timestamp()] - [string]" + var/datum/computer_file/program/borg_self_monitor/program = modularInterface.get_self_monitoring() + if(program) + program.force_full_update() + +/// Same as the normal character name replacement, but updates the contents of the modular interface. +/mob/living/silicon/fully_replace_character_name(oldname, newname) + . = ..() + if(!modularInterface) + stack_trace("Silicon [src] ( [type] ) was somehow missing their integrated tablet. Please make a bug report.") + create_modularInterface() + modularInterface.saved_identification = newname diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index f8d516e1dae..cfe451c1b4f 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -307,7 +307,7 @@ to_chat(user, "The maintenance panel is now [open ? "opened" : "closed"].") else to_chat(user, "The maintenance panel is locked.") - else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) + else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/modular_computer/tablet/pda)) togglelock(user) else if(istype(W, /obj/item/paicard)) insertpai(user, W) diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index 4dabbac52ac..9e61c933938 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -41,6 +41,11 @@ var/datum/job/janitor/J = new/datum/job/janitor access_card.access += J.get_access() prev_access = access_card.access + GLOB.janitor_devices += src + +/mob/living/simple_animal/bot/cleanbot/Destroy() + GLOB.janitor_devices -= src + return ..() /mob/living/simple_animal/bot/cleanbot/turn_on() ..() @@ -64,7 +69,7 @@ text_dehack_fail = "[name] does not seem to respond to your repair code!" /mob/living/simple_animal/bot/cleanbot/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) + if(istype(W, /obj/item/card/id)||istype(W, /obj/item/modular_computer/tablet/pda)) if(bot_core.allowed(user) && !open && !emagged) locked = !locked to_chat(user, "You [ locked ? "lock" : "unlock"] \the [src] behaviour controls.") diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 306884c0a65..6e06354683b 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -150,7 +150,7 @@ * Return the desc of this mob for a photo */ /mob/proc/get_photo_description(obj/item/camera/camera) - return "a ... thing?" + return "You can also see a ... thing?" /** * Show a message to this mob (visual or audible) @@ -186,7 +186,7 @@ to_chat(src, msg, avoid_highlighting = avoid_highlighting) -/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, list/visible_message_flags, separation = " ") //NSV13 +/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, list/visible_message_flags, allow_inside_usr = FALSE, separation = " ") //NSV13 var/turf/T = get_turf(src) if(!T) return @@ -227,7 +227,8 @@ if(M.see_invisible < invisibility)//if src is invisible to M msg = blind_message else if(T != loc && T != src) //if src is inside something and not a turf. - msg = blind_message + if(!allow_inside_usr || loc != usr) + msg = blind_message else if(T.lighting_object && T.lighting_object.invisibility <= M.see_invisible && T.is_softly_lit() && !in_range(T,M)) //if it is too dark. msg = blind_message if(!msg) @@ -998,8 +999,12 @@ return 1 ///Can the mob interact() with an atom? -/mob/proc/can_interact_with(atom/A) - return IsAdminGhost(src) || Adjacent(A) +/mob/proc/can_interact_with(atom/A, treat_mob_as_adjacent) + if(IsAdminGhost(src)) + return TRUE + if(treat_mob_as_adjacent && src == A.loc) + return TRUE + return Adjacent(A) ///Can the mob use Topic to interact with machines /mob/proc/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE) @@ -1098,11 +1103,11 @@ break search_id = 0 - else if( search_pda && istype(A, /obj/item/pda) ) - var/obj/item/pda/PDA = A - if(PDA.owner == oldname) - PDA.owner = newname - PDA.update_label() + else if(search_pda && istype(A, /obj/item/modular_computer/tablet/pda)) + var/obj/item/modular_computer/tablet/pda/PDA = A + if(PDA.saved_identification == oldname) + PDA.saved_identification = newname + PDA.update_id_display() if(!search_id) break search_pda = 0 diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 28d5d549bd2..3796b34677d 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -1,13 +1,33 @@ +GLOBAL_LIST_EMPTY(TabletMessengers) // a list of all active messengers, similar to GLOB.PDAs (used primarily with ntmessenger.dm) + // This is the base type that does all the hardware stuff. // Other types expand it - tablets use a direct subtypes, and // consoles and laptops use "procssor" item that is held inside machinery piece /obj/item/modular_computer name = "modular microcomputer" desc = "A small portable microcomputer." + light_system = MOVABLE_LIGHT + light_range = 3 + light_power = 0.6 + light_color = "#FFFFFF" + light_on = FALSE var/enabled = 0 // Whether the computer is turned on. var/screen_on = 1 // Whether the computer is active/opened/it's screen is on. - var/device_theme = "ntos" // Sets the theme for the main menu, hardware config, and file browser apps. Overridden by certain non-NT devices. + /// If it's bypassing the set icon state + var/bypass_state = FALSE + /// Whether or not the computer can be upgraded + var/upgradable = TRUE + /// Whether or not the computer can be deconstructed + var/deconstructable = TRUE + /// Sets the theme for the main menu, hardware config, and file browser apps. + var/device_theme = THEME_NTOS + /// Whether this device is allowed to change themes or not. + var/theme_locked = FALSE + /// List of themes for this device to allow. + var/list/allowed_themes + /// Color used for the Thinktronic Classic theme. + var/classic_color = "#808000" var/datum/computer_file/program/active_program = null // A currently active program running on the computer. var/hardware_flag = 0 // A flag that describes this device type var/last_power_usage = 0 @@ -41,21 +61,73 @@ /// Number of total expansion bays this computer has available. var/max_bays = 0 - var/list/idle_threads // Idle programs on background. They still receive process calls but can't be interacted with. - var/obj/physical = null // Object that represents our computer. It's used for Adjacent() and UI visibility checks. - var/has_light = FALSE //If the computer has a flashlight/LED light/what-have-you installed - var/comp_light_luminosity = 3 //The brightness of that light - var/comp_light_color //The color of that light - light_on = FALSE // override behavior from atom so flashlight button is not marked as ON + /// The currently imprinted ID. + var/saved_identification = null + /// The currently imprinted job. + var/saved_job = null + /// If the saved info should auto-update + var/saved_auto_imprint = TRUE + /// The amount of honks. honk honk honk honk honk honkh onk honkhnoohnk + var/honk_amount = 0 + /// Idle programs on background. They still receive process calls but can't be interacted with. + var/list/idle_threads + /// Object that represents our computer. It's used for Adjacent() and UI visibility checks. + var/obj/physical = null + /// If the computer has a flashlight/LED light/what-have-you installed + var/has_light = FALSE + /// How far the computer's light can reach, is not editable by players. + var/comp_light_luminosity = 3 + /// The built-in light's color, editable by players. + var/comp_light_color = "#FFFFFF" + /// Whether or not the tablet is invisible in messenger and other apps + var/messenger_invisible = FALSE + /// The saved image used for messaging purposes + var/datum/picture/saved_image + /// The ringtone that will be set on initialize + var/init_ringtone = "beep" + /// If the device starts with its ringer on + var/init_ringer_on = TRUE + /// The action for enabling/disabling the flashlight + var/datum/action/item_action/toggle_computer_light/light_action + /// Stored pAI card + var/obj/item/paicard/stored_pai_card + /// If the device is capable of storing a pAI + var/can_store_pai = FALSE /obj/item/modular_computer/Initialize(mapload) . = ..() + allowed_themes = GLOB.ntos_device_themes_default START_PROCESSING(SSobj, src) if(!physical) physical = src - comp_light_color = "#FFFFFF" + set_light_color(comp_light_color) + set_light_range(comp_light_luminosity) idle_threads = list() + update_id_display() + if(has_light) + light_action = new(src) update_icon() + add_messenger() + +/obj/item/modular_computer/proc/update_id_display() + var/obj/item/computer_hardware/identifier/id = all_components[MC_IDENTIFY] + if(id) + id.UpdateDisplay() + +/obj/item/modular_computer/proc/on_id_insert() + var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD] + // We shouldn't auto-imprint if ID modification is open. + if(!saved_auto_imprint || !cardholder || istype(active_program, /datum/computer_file/program/card_mod)) + return + if(cardholder.current_identification == saved_identification && cardholder.current_job == saved_job) + return + if(!cardholder.current_identification || !cardholder.current_job) + return + saved_identification = cardholder.current_identification + saved_job = cardholder.current_job + update_id_display() + playsound(src, 'sound/machines/terminal_processing.ogg', 15, TRUE) + addtimer(CALLBACK(GLOBAL_PROC, PROC_REF(playsound), src, 'sound/machines/terminal_success.ogg', 15, TRUE), 1.3 SECONDS) /obj/item/modular_computer/Destroy() kill_program(forced = TRUE) @@ -63,20 +135,50 @@ for(var/port in all_components) var/obj/item/computer_hardware/component = all_components[port] qdel(component) - all_components.Cut() //Die demon die + all_components?.Cut() + if(istype(stored_pai_card)) + qdel(stored_pai_card) + remove_pai() + if(istype(light_action)) + QDEL_NULL(light_action) physical = null + remove_messenger() return ..() -/obj/item/modular_computer/AltClick(mob/user) - if(issilicon(user)) - return +/obj/item/modular_computer/ui_action_click(mob/user, actiontype) + if(istype(actiontype, light_action)) + toggle_flashlight() + else + ..() + +/// From [/datum/newscaster/feed_network/proc/save_photo] +/obj/item/modular_computer/proc/save_photo(icon/photo) + var/photo_file = copytext_char(md5("/icon[photo]"), 1, 6) + if(!fexists("[GLOB.log_directory]/photos/[photo_file].png")) + //Clean up repeated frames + var/icon/clean = new /icon() + clean.Insert(photo, "", SOUTH, 1, 0) + fcopy(clean, "[GLOB.log_directory]/photos/[photo_file].png") + return photo_file + +/** + * Plays a ping sound. + * + * Timers runtime if you try to make them call playsound. Yep. + */ +/obj/item/modular_computer/proc/play_ping() + playsound(loc, 'sound/machines/ping.ogg', get_clamped_volume(), FALSE, -1) - if(user.canUseTopic(src, BE_CLOSE)) - var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2] - var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - return (card_slot2?.try_eject(user) || card_slot?.try_eject(user)) //Try the secondary one first. +/obj/item/modular_computer/AltClick(mob/user) + if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE)) + return FALSE + var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2] + var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] + if(!card_slot2?.try_eject(user)) + return card_slot?.try_eject(user) + return TRUE -// Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs. +// Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs. (They are now!! you are welcome - itsmeow) /obj/item/modular_computer/GetAccess() var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] if(card_slot) @@ -92,19 +194,31 @@ /obj/item/modular_computer/RemoveID() var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2] var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - return (card_slot2?.try_eject() || card_slot?.try_eject()) //Try the secondary one first. + var/removed_id = (card_slot2?.try_eject() || card_slot?.try_eject()) + if(removed_id) + if(ishuman(loc)) + var/mob/living/carbon/human/human_wearer = loc + if(human_wearer.wear_id == src) + human_wearer.sec_hud_set_ID() + return removed_id + return ..() /obj/item/modular_computer/InsertID(obj/item/inserting_item) var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2] + if(!(card_slot || card_slot2)) return FALSE - var/obj/item/card/inserting_id = inserting_item.RemoveID() + var/obj/item/card/inserting_id = inserting_item.GetID() if(!inserting_id) return FALSE if((card_slot?.try_insert(inserting_id)) || (card_slot2?.try_insert(inserting_id))) + if(ishuman(loc)) + var/mob/living/carbon/human/human_wearer = loc + if(human_wearer.wear_id == src) + human_wearer.sec_hud_set_ID() return TRUE return FALSE @@ -142,6 +256,10 @@ newemag = TRUE if(newemag) to_chat(user, "You swipe \the [src]. A console window momentarily fills the screen, with white text rapidly scrolling past.") + var/datum/computer_file/program/emag_console/emag_console = new(src) + emag_console.computer = src + emag_console.program_state = PROGRAM_STATE_ACTIVE + emag_console.ui_interact(user) return TRUE to_chat(user, "You swipe \the [src]. A console window fills the screen, but it quickly closes itself after only a few lines are written to it.") return FALSE @@ -157,18 +275,22 @@ /obj/item/modular_computer/update_icon() cut_overlays() - if(!enabled) - icon_state = icon_state_unpowered - else - icon_state = icon_state_powered - if(active_program) - add_overlay(active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu) - else - add_overlay(icon_state_menu) + if(!bypass_state) + icon_state = enabled ? icon_state_powered : icon_state_unpowered + + var/init_icon = initial(icon) + if(!init_icon) + return + + if(enabled) + add_overlay(active_program ? mutable_appearance(init_icon, active_program.program_icon_state) : mutable_appearance(init_icon, icon_state_menu)) + + if(can_store_pai && stored_pai_card) + add_overlay(stored_pai_card.pai ? mutable_appearance(init_icon, "pai-overlay") : mutable_appearance(init_icon, "pai-off-overlay")) if(obj_integrity <= integrity_failure) - add_overlay("bsod") - add_overlay("broken") + add_overlay(mutable_appearance(init_icon, "bsod")) + add_overlay(mutable_appearance(init_icon, "broken")) // On-click handling. Turns on the computer if it's off and opens the GUI. @@ -178,14 +300,18 @@ else turn_on(user) -/obj/item/modular_computer/proc/turn_on(mob/user) +/obj/item/modular_computer/proc/turn_on(mob/user, open_ui = TRUE) + if(enabled) + if(open_ui) + ui_interact(user) + return TRUE var/issynth = issilicon(user) // Robots and AIs get different activation messages. if(obj_integrity <= integrity_failure) if(issynth) to_chat(user, "You send an activation signal to \the [src], but it responds with an error code. It must be damaged.") else to_chat(user, "You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again.") - return + return FALSE // If we have a recharger, enable it automatically. Lets computer without a battery work. var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE] @@ -199,12 +325,15 @@ to_chat(user, "You press the power button and start up \the [src].") enabled = 1 update_icon() - ui_interact(user) + if(open_ui) + ui_interact(user) + return TRUE else // Unpowered if(issynth) to_chat(user, "You send an activation signal to \the [src] but it does not respond.") else to_chat(user, "You press the power button but \the [src] does not respond.") + return FALSE // Process currently calls handle_power(), may be expanded in future if more things are added. /obj/item/modular_computer/process(delta_time) @@ -264,11 +393,26 @@ if(istype(holder)) to_chat(holder, "[icon2html(src)] The [src] displays a [caller.filedesc] notification: [alerttext]") +/obj/item/modular_computer/proc/ring(ringtone) // bring bring + if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) + playsound(src, pick('sound/machines/twobeep_voice1.ogg', 'sound/machines/twobeep_voice2.ogg'), 50, TRUE) + else + playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE) + visible_message("*[ringtone]*") + +/obj/item/modular_computer/proc/send_sound() + playsound(src, 'sound/machines/terminal_success.ogg', 15, TRUE) + +/obj/item/modular_computer/proc/send_select_sound() + playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) + // Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_" /obj/item/modular_computer/proc/get_header_data() var/list/data = list() data["PC_device_theme"] = device_theme + data["PC_classic_color"] = classic_color + data["PC_theme_locked"] = theme_locked var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL] var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE] @@ -334,6 +478,51 @@ ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. update_icon() +/obj/item/modular_computer/proc/open_program(mob/user, datum/computer_file/program/program, in_background = FALSE) + if(program.computer != src) + CRASH("tried to open program that does not belong to this computer") + + if(!program || !istype(program)) // Program not found or it's not executable program. + to_chat(user, "\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning.") + return FALSE + + if(!program.is_supported_by_hardware(hardware_flag, 1, user)) + return FALSE + + // The program is already running. Resume it. + if(!in_background) + if(program in idle_threads) + program.program_state = PROGRAM_STATE_ACTIVE + active_program = program + program.alert_pending = FALSE + idle_threads.Remove(program) + update_icon() + updateUsrDialog() + return TRUE + else if(program in idle_threads) + return TRUE + var/obj/item/computer_hardware/processor_unit/PU = all_components[MC_CPU] + if(idle_threads.len > PU.max_idle_programs) + to_chat(user, "\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error.") + return FALSE + + if(program.requires_ntnet && !get_ntnet_status(program.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet. + to_chat(user, "\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning.") + return FALSE + + if(!program.on_start(user)) + return FALSE + + if(!in_background) + active_program = program + program.alert_pending = FALSE + updateUsrDialog() + else + program.program_state = PROGRAM_STATE_BACKGROUND + idle_threads.Add(program) + update_icon() + return TRUE + // Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on) /obj/item/modular_computer/proc/get_ntnet_status(specific_action = 0) var/obj/item/computer_hardware/network_card/network_card = all_components[MC_NET] @@ -358,7 +547,40 @@ enabled = 0 update_icon() +/** + * Toggles the computer's flashlight, if it has one. + * + * Called from ui_act(), does as the name implies. + * It is seperated from ui_act() to be overwritten as needed. +*/ +/obj/item/modular_computer/proc/toggle_flashlight() + if(!has_light) + return FALSE + set_light_on(!light_on) + update_icon() + // Show the light_on overlay on top of the action button icon + if(light_action?.owner) + light_action.UpdateButtonIcon(force = TRUE) + return TRUE + +/** + * Sets the computer's light color, if it has a light. + * + * Called from ui_act(), this proc takes a color string and applies it. + * It is seperated from ui_act() to be overwritten as needed. + * Arguments: + ** color is the string that holds the color value that we should use. Proc auto-fails if this is null. +*/ +/obj/item/modular_computer/proc/set_flashlight_color(color) + if(!has_light || !color) + return FALSE + comp_light_color = color + set_light_color(color) + return TRUE + /obj/item/modular_computer/screwdriver_act(mob/user, obj/item/tool) + if(!deconstructable) + return if(!length(all_components)) balloon_alert(user, "no components installed!") return @@ -381,53 +603,99 @@ return tool.play_tool_sound(user, volume=20) - uninstall_component(H, user) + uninstall_component(H, user, TRUE) return -/obj/item/modular_computer/attackby(obj/item/W as obj, mob/user as mob) +/obj/item/modular_computer/attackby(obj/item/attacking_item, mob/user, params) // Check for ID first - if(istype(W, /obj/item/card/id) && InsertID(W)) + if(istype(attacking_item, /obj/item/card/id) && InsertID(attacking_item)) return + // Scan a photo. + if(istype(attacking_item, /obj/item/photo)) + var/obj/item/computer_hardware/hard_drive/hdd = all_components[MC_HDD] + var/obj/item/photo/pic = attacking_item + if(hdd) + for(var/datum/computer_file/program/messenger/messenger in hdd.stored_files) + saved_image = pic.picture + messenger.ProcessPhoto() + to_chat(user, "You scan \the [pic] into \the [src]'s messenger.") + return + // Insert items into the components for(var/h in all_components) var/obj/item/computer_hardware/H = all_components[h] - if(H.try_insert(W, user)) + if(H.try_insert(attacking_item, user)) return + // Insert a pAI card + if(can_store_pai && !stored_pai_card && istype(attacking_item, /obj/item/paicard)) + if(!user.transferItemToLoc(attacking_item, src)) + return + stored_pai_card = attacking_item + // If the pAI moves out of the PDA, remove the reference. + RegisterSignal(stored_pai_card, COMSIG_MOVABLE_MOVED, PROC_REF(stored_pai_moved)) + RegisterSignal(stored_pai_card, COMSIG_PARENT_QDELETING, PROC_REF(remove_pai)) + to_chat(user, "You slot \the [attacking_item] into [src].") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50) + update_icon() + // Insert new hardware - if(istype(W, /obj/item/computer_hardware)) - if(install_component(W, user)) + var/obj/item/computer_hardware/inserted_hardware = attacking_item + if(istype(inserted_hardware) && upgradable) + if(install_component(inserted_hardware, user)) + inserted_hardware.on_inserted(user) return - if(W.tool_behaviour == TOOL_WRENCH) + if(attacking_item.tool_behaviour == TOOL_WRENCH) if(length(all_components)) balloon_alert(user, "remove the other components!") return - W.play_tool_sound(src, user, 20, volume=20) + attacking_item.play_tool_sound(src, user, 20, volume=20) new /obj/item/stack/sheet/iron( get_turf(src.loc), steel_sheet_cost ) user.balloon_alert(user, "disassembled") relay_qdel() qdel(src) return - if(W.tool_behaviour == TOOL_WELDER) + if(attacking_item.tool_behaviour == TOOL_WELDER) if(obj_integrity == max_integrity) to_chat(user, "\The [src] does not require repairs.") return - if(!W.tool_start_check(user, amount=1)) + if(!attacking_item.tool_start_check(user, amount=1)) return to_chat(user, "You begin repairing damage to \the [src]...") - if(W.use_tool(src, user, 20, volume=50, amount=1)) + if(attacking_item.use_tool(src, user, 20, volume=50, amount=1)) obj_integrity = max_integrity to_chat(user, "You repair \the [src].") update_icon() return + var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] + // Check to see if we have an ID inside, and a valid input for money + if(card_slot?.GetID() && iscash(attacking_item)) + var/obj/item/card/id/id = card_slot.GetID() + id.attackby(attacking_item, user) // If we do, try and put that attacking object in + return ..() +/// Handle when the pAI moves to exit the PDA +/obj/item/modular_computer/proc/stored_pai_moved() + if(istype(stored_pai_card) && stored_pai_card.loc != src) + visible_message("[stored_pai_card] ejects itself from [src]!") + remove_pai() + +/// Set the internal pAI card to null - this is NOT "Ejecting" it. +/obj/item/modular_computer/proc/remove_pai() + if(!istype(stored_pai_card)) + return + UnregisterSignal(stored_pai_card, COMSIG_MOVABLE_MOVED) + UnregisterSignal(stored_pai_card, COMSIG_PARENT_QDELETING) + stored_pai_card = null + update_icon() + // Used by processor to relay qdel() to machinery type. /obj/item/modular_computer/proc/relay_qdel() return @@ -437,3 +705,13 @@ if(physical && physical != src) return physical.Adjacent(neighbor) return ..() + +/obj/item/modular_computer/proc/add_messenger() + GLOB.TabletMessengers += src + +/obj/item/modular_computer/proc/remove_messenger() + GLOB.TabletMessengers -= src + +// Make messages visible via allow_inside_usr +/obj/item/modular_computer/visible_message(message, self_message, blind_message, vision_distance, list/ignored_mobs, list/visible_message_flags, allow_inside_usr = TRUE) + return ..() diff --git a/code/modules/modular_computers/computers/item/computer_components.dm b/code/modules/modular_computers/computers/item/computer_components.dm index 92a7ffaf474..6da96a9f09d 100644 --- a/code/modules/modular_computers/computers/item/computer_components.dm +++ b/code/modules/modular_computers/computers/item/computer_components.dm @@ -13,9 +13,9 @@ if(LAZYACCESS(expansion_bays, try_install.device_type)) to_chat(user, "The computer immediately ejects /the [try_install] and flashes an error: \"Hardware Address Conflict\".") return FALSE - - if(all_components[try_install.device_type]) - to_chat(user, "This computer's hardware slot is already occupied by \the [all_components[try_install.device_type]].") + var/obj/item/computer_hardware/existing = all_components[try_install.device_type] + if(existing && (!istype(existing) || !existing.hotswap)) + to_chat(user, "This computer's hardware slot is already occupied by \the [existing].") return FALSE return TRUE @@ -28,6 +28,13 @@ if(user && !user.transferItemToLoc(install, src)) return FALSE + var/obj/item/computer_hardware/existing = all_components[install.device_type] + if(istype(existing) && existing.hotswap) + if(!uninstall_component(existing, user, TRUE)) + // ABORT!! + install.forceMove(get_turf(user)) + return FALSE + if(install.expansion_hw) LAZYSET(expansion_bays, install.device_type, install) all_components[install.device_type] = install @@ -36,16 +43,19 @@ install.holder = src install.forceMove(src) install.on_install(src, user) - + return TRUE /// Uninstalls component. -/obj/item/modular_computer/proc/uninstall_component(obj/item/computer_hardware/yeet, mob/living/user = null) +/obj/item/modular_computer/proc/uninstall_component(obj/item/computer_hardware/yeet, mob/living/user = null, put_in_hands) if(yeet.holder != src) // Not our component at all. return FALSE to_chat(user, "You remove \the [yeet] from \the [src].") - yeet.forceMove(get_turf(src)) + if(put_in_hands) + user.put_in_hands(yeet) + else + yeet.forceMove(get_turf(src)) forget_component(yeet) yeet.on_remove(src, user) if(enabled && !use_power()) diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index 82b0ce03a6d..686f14ebca3 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -1,7 +1,3 @@ -/obj/item/modular_computer/attack_self(mob/user) - . = ..() - ui_interact(user) - // Operates TGUI /obj/item/modular_computer/ui_state(mob/user) @@ -17,17 +13,17 @@ if(!enabled) if(ui) ui.close() - return 0 + return FALSE if(!use_power()) if(ui) ui.close() - return 0 + return FALSE // Robots don't really need to see the screen, their wireless connection works as long as computer is on. if(!screen_on && !issilicon(user)) if(ui) ui.close() - return 0 + return FALSE // If we have an active program switch to it now. if(active_program) @@ -41,7 +37,11 @@ var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] if(!hard_drive || !hard_drive.stored_files || !hard_drive.stored_files.len) to_chat(user, "\The [src] beeps three times, it's screen displaying a \"DISK ERROR\" warning.") - return // No HDD, No HDD files list or no stored files. Something is very broken. + return FALSE // No HDD, No HDD files list or no stored files. Something is very broken. + + if(honk_amount > 0) // EXTRA annoying, huh! + honk_amount-- + playsound(src, 'sound/items/bikehorn.ogg', 30, TRUE) ui = SStgui.try_update_ui(user, src, ui) if (!ui) @@ -53,24 +53,46 @@ /obj/item/modular_computer/ui_data(mob/user) var/list/data = get_header_data() data["device_theme"] = device_theme - data["login"] = list() + + data["disk"] = null + + data["stored_pai"] = istype(stored_pai_card) + data["stored_pai_name"] = stored_pai_card?.pai?.name + var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD] + var/obj/item/computer_hardware/hard_drive/role/ssd = all_components[MC_HDD_JOB] data["cardholder"] = FALSE + if(cardholder) data["cardholder"] = TRUE - var/obj/item/card/id/stored_card = cardholder.GetID() - if(stored_card) - var/stored_name = stored_card.registered_name - var/stored_title = stored_card.assignment - if(!stored_name) - stored_name = "Unknown" - if(!stored_title) - stored_title = "Unknown" - data["login"] = list( - IDName = stored_name, - IDJob = stored_title, - ) + data["auto_imprint"] = saved_auto_imprint + + var/stored_name = saved_identification + var/stored_title = saved_job + if(!stored_name) + stored_name = "Unknown" + if(!stored_title) + stored_title = "Unknown" + data["login"] = list( + IDName = saved_identification, + IDJob = saved_job, + ) + data["proposed_login"] = list( + IDName = cardholder.current_identification, + IDJob = cardholder.current_job, + ) + + if(ssd) + data["disk"] = ssd + data["disk_name"] = ssd.name + + for(var/datum/computer_file/program/prog in ssd.stored_files) + var/running = FALSE + if(prog in idle_threads) + running = TRUE + + data["disk_programs"] += list(list("name" = prog.filename, "desc" = prog.filedesc, "running" = running, "icon" = prog.program_icon, "alert" = prog.alert_pending)) data["removable_media"] = list() if(all_components[MC_SDD]) @@ -101,6 +123,8 @@ /obj/item/modular_computer/ui_act(action, params) if(..()) return + if(device_theme == THEME_THINKTRONIC) + send_select_sound() var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] switch(action) if("PC_exit") @@ -137,52 +161,23 @@ return TRUE if("PC_runprogram") - var/prog = params["name"] - var/datum/computer_file/program/P = null - var/mob/user = usr - if(hard_drive) - P = hard_drive.find_file_by_name(prog) + var/is_disk = params["is_disk"] + var/datum/computer_file/program/program + var/obj/item/computer_hardware/hard_drive/role/ssd = all_components[MC_HDD_JOB] - if(!P || !istype(P)) // Program not found or it's not executable program. - to_chat(user, "\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning.") - return - - P.computer = src - - if(!P.is_supported_by_hardware(hardware_flag, 1, user)) - return + if(hard_drive && !is_disk) + program = hard_drive.find_file_by_name(params["name"]) + if(ssd && is_disk) + program = ssd.find_file_by_name(params["name"]) - // The program is already running. Resume it. - if(P in idle_threads) - P.program_state = PROGRAM_STATE_ACTIVE - active_program = P - P.alert_pending = FALSE - idle_threads.Remove(P) - update_icon() + if(!program || !istype(program)) // Program not found or it's not executable program. + to_chat(usr, "\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning.") return - - var/obj/item/computer_hardware/processor_unit/PU = all_components[MC_CPU] - - if(idle_threads.len > PU.max_idle_programs) - to_chat(user, "\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error.") - return - - if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet. - to_chat(user, "\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning.") - return - if(P.run_program(user)) - active_program = P - P.alert_pending = FALSE - update_icon() - return TRUE + program.computer = src + open_program(usr, program) if("PC_toggle_light") - light_on = !light_on - if(light_on) - set_light(comp_light_luminosity, 1, comp_light_color) - else - set_light(0) - return TRUE + return toggle_flashlight() if("PC_light_color") var/mob/user = usr @@ -194,10 +189,7 @@ if(color_hex2num(new_color) < 200) //Colors too dark are rejected to_chat(user, "That color is too dark! Choose a lighter one.") new_color = null - comp_light_color = new_color - light_color = new_color - update_light() - return TRUE + return set_flashlight_color(new_color) if("PC_Eject_Disk") var/param = params["name"] @@ -207,8 +199,13 @@ var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD] if(!portable_drive) return - if(uninstall_component(portable_drive, usr)) - user.put_in_hands(portable_drive) + if(uninstall_component(portable_drive, usr, TRUE)) + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50) + if("job disk") + var/obj/item/computer_hardware/hard_drive/role/ssd = all_components[MC_HDD_JOB] + if(!ssd) + return + if(uninstall_component(ssd, usr, TRUE)) playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50) if("intelliCard") var/obj/item/computer_hardware/ai_slot/intelliholder = all_components[MC_AI] @@ -220,14 +217,40 @@ var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD] if(!cardholder) return - cardholder.try_eject(user) + if(cardholder.try_eject(user)) + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50) if("secondary RFID card") var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD2] if(!cardholder) return - cardholder.try_eject(user) + if(cardholder.try_eject(user)) + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50) + if("PC_Imprint_ID") + var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD] + if(!cardholder) + return + + saved_identification = cardholder.current_identification + saved_job = cardholder.current_job + update_id_display() + playsound(src, 'sound/machines/terminal_processing.ogg', 15, TRUE) + addtimer(CALLBACK(GLOBAL_PROC, PROC_REF(playsound), src, 'sound/machines/terminal_success.ogg', 15, TRUE), 1.3 SECONDS) + if("PC_Toggle_Auto_Imprint") + saved_auto_imprint = !saved_auto_imprint + if(saved_auto_imprint) + on_id_insert() + if("PC_Pai_Interact") + if(!can_store_pai || !istype(stored_pai_card)) + return + if(params["option"] == "interact") + stored_pai_card.attack_self(usr) + else if(params["option"] == "eject") + usr.put_in_hands(stored_pai_card) + remove_pai() + to_chat(usr, "You remove the pAI from [src].") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50) else return diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm index 76eb48053f7..1c754740dc0 100644 --- a/code/modules/modular_computers/computers/item/processor.dm +++ b/code/modules/modular_computers/computers/item/processor.dm @@ -65,5 +65,8 @@ /obj/item/modular_computer/processor/alert_call(datum/computer_file/program/caller, alerttext) if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext) return - playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE) + var/sound = 'sound/machines/twobeep_high.ogg' + if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) + sound = pick('sound/machines/twobeep_voice1.ogg', 'sound/machines/twobeep_voice2.ogg') + playsound(src, sound, 50, TRUE) machinery_computer.visible_message("The [src] displays a [caller.filedesc] notification: [alerttext]") diff --git a/code/modules/modular_computers/computers/item/role_tablet_presets.dm b/code/modules/modular_computers/computers/item/role_tablet_presets.dm new file mode 100644 index 00000000000..08bf10fde80 --- /dev/null +++ b/code/modules/modular_computers/computers/item/role_tablet_presets.dm @@ -0,0 +1,286 @@ +/obj/item/modular_computer/tablet/pda/clown + name = "clown PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. The surface is coated with polytetrafluoroethylene and banana drippings." + note = "Honk!" + default_disk = /obj/item/computer_hardware/hard_drive/role/virus/clown + icon_state = "pda-clown" + insert_type = /obj/item/toy/crayon/rainbow + /// List of victims (of a very funny joke, that everyone loves!). Stores references to mobs. + var/list/slip_victims = list() + init_ringtone = "honk" + device_theme = THEME_NTOS_CLOWN_PINK // Give the clown the best theme + +/obj/item/modular_computer/tablet/pda/clown/ComponentInitialize() + . = ..() + AddComponent(/datum/component/slippery, 7 SECONDS, NO_SLIP_WHEN_WALKING, CALLBACK(src, PROC_REF(AfterSlip)), 5 SECONDS) + +/obj/item/modular_computer/tablet/pda/clown/proc/AfterSlip(mob/living/carbon/human/M) + if (istype(M) && (M.real_name != saved_identification)) + slip_victims |= REF(M) + var/obj/item/computer_hardware/hard_drive/role/virus/clown/cart = all_components[MC_HDD_JOB] + if(istype(cart) && cart.charges < 5) + cart.charges++ + playsound(src,'sound/machines/ping.ogg',30,TRUE) + +/obj/item/modular_computer/tablet/pda/mime + name = "mime PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. The hardware has been modified for compliance with the vows of silence." + default_disk = /obj/item/computer_hardware/hard_drive/role/virus/mime + insert_type = /obj/item/toy/crayon/mime + init_ringer_on = FALSE + init_ringtone = "silence" + +/obj/item/modular_computer/tablet/pda/mime/Initialize(mapload) + . = ..() + var/obj/item/computer_hardware/hard_drive/hdd = all_components[MC_HDD] + + if(hdd) + for(var/datum/computer_file/program/messenger/msg in hdd.stored_files) + msg.mime_mode = TRUE + msg.allow_emojis = TRUE + +/obj/item/modular_computer/tablet/pda/assistant + name = "assistant PDA" + icon_state = "pda-assistant" + +/obj/item/modular_computer/tablet/pda/medical + name = "medical PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/medical + icon_state = "pda-medical" + +/obj/item/modular_computer/tablet/pda/paramedic + name = "paramedic PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/medical + icon_state = "pda-paramedical" + +/obj/item/modular_computer/tablet/pda/virologist + name = "virology PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/medical + icon_state = "pda-virology" + +/obj/item/modular_computer/tablet/pda/station_engineer + name = "engineering PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/engineering + icon_state = "pda-engineer" + +/obj/item/modular_computer/tablet/pda/security + name = "security PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/security + icon_state = "pda-security" + +/obj/item/modular_computer/tablet/pda/deputy + name = "deputy PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/security + icon_state = "pda-deputy" + +/obj/item/modular_computer/tablet/pda/brig_physician + name = "brig physician PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/brig_physician + icon_state = "pda-brigphys" + + +/obj/item/modular_computer/tablet/pda/detective + name = "detective PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/detective + icon_state = "pda-detective" + +/obj/item/modular_computer/tablet/pda/warden + name = "warden PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/security + icon_state = "pda-warden" + +/obj/item/modular_computer/tablet/pda/janitor + name = "janitor PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/janitor + icon_state = "pda-janitor" + init_ringtone = "slip" + +/obj/item/modular_computer/tablet/pda/science + name = "scientist PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/signal/toxins + icon_state = "pda-science" + init_ringtone = "boom" + +/obj/item/modular_computer/tablet/pda/science/Initialize(mapload) + . = ..() + install_component(new /obj/item/computer_hardware/radio_card) + +/obj/item/modular_computer/tablet/pda/service + name = "service PDA" + icon_state = "pda-service" + +/obj/item/modular_computer/tablet/pda/heads + default_disk = /obj/item/computer_hardware/hard_drive/role/head + icon_state = "pda-heads" + +/obj/item/modular_computer/tablet/pda/heads/Initialize(mapload) + . = ..() + install_component(new /obj/item/computer_hardware/card_slot/secondary) + +/obj/item/modular_computer/tablet/pda/heads/head_of_personnel + name = "head of personnel PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/hop + icon_state = "pda-hop" + +/obj/item/modular_computer/tablet/pda/heads/head_of_personnel/Initialize(mapload) + . = ..() + install_component(new /obj/item/computer_hardware/printer/mini) + +/obj/item/modular_computer/tablet/pda/heads/head_of_security + name = "head of security PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/hos + icon_state = "pda-hos" + +/obj/item/modular_computer/tablet/pda/heads/chief_engineer + name = "chief engineer PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/ce + icon_state = "pda-ce" + +/obj/item/modular_computer/tablet/pda/heads/chief_medical_officer + name = "chief medical officer PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/cmo + icon_state = "pda-cmo" + +/obj/item/modular_computer/tablet/pda/heads/research_director + name = "research director PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/rd + insert_type = /obj/item/pen/fountain + icon_state = "pda-rd" + +/obj/item/modular_computer/tablet/pda/heads/research_director/Initialize(mapload) + . = ..() + install_component(new /obj/item/computer_hardware/radio_card) + +/obj/item/modular_computer/tablet/pda/heads/captain + name = "captain PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. The internals are modified to be more tough than the usual." + default_disk = /obj/item/computer_hardware/hard_drive/role/captain + insert_type = /obj/item/pen/fountain/captain + icon_state = "pda-captain" + detonatable = FALSE + +/obj/item/modular_computer/tablet/pda/cargo_technician + name = "cargo technician PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/cargo_technician + icon_state = "pda-cargo" + +/obj/item/modular_computer/tablet/pda/cargo_technician/Initialize(mapload) + . = ..() + install_component(new /obj/item/computer_hardware/printer/mini) + +/obj/item/modular_computer/tablet/pda/quartermaster + name = "quartermaster PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/quartermaster + insert_type = /obj/item/pen/fountain + icon_state = "pda-qm" + +/obj/item/modular_computer/tablet/pda/quartermaster/Initialize(mapload) + . = ..() + install_component(new /obj/item/computer_hardware/printer/mini) + +/obj/item/modular_computer/tablet/pda/shaft_miner + name = "shaft miner PDA" + icon_state = "pda-miner" + +/obj/item/modular_computer/tablet/pda/exploration_crew + name = "exploration crew PDA" + icon_state = "pda-exploration" + +/obj/item/modular_computer/tablet/pda/syndicate + name = "military PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a WGW-XL-NTOS series." + note = "Congratulations, your -corrupted- has chosen the Thinktronic 5290 WGW-XL-NTOS Series Personal Data Assistant!" + default_disk = /obj/item/computer_hardware/hard_drive/role/virus/syndicate/military + saved_identification = "John Doe" + saved_job = "Citizen" + icon_state = "pda-syndi" + messenger_invisible = TRUE + detonatable = FALSE + device_theme = THEME_SYNDICATE + theme_locked = TRUE + +/obj/item/modular_computer/tablet/pda/chaplain + name = "chaplain PDA" + icon_state = "pda-chaplain" + init_ringtone = "holy" + +/obj/item/modular_computer/tablet/pda/lawyer + name = "lawyer PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/lawyer + insert_type = /obj/item/pen/fountain + icon_state = "pda-lawyer" + init_ringtone = "objection" + +/obj/item/modular_computer/tablet/pda/roboticist + name = "roboticist PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/roboticist + icon_state = "pda-roboticist" + +/obj/item/modular_computer/tablet/pda/curator + name = "curator PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a WGW-11-NTOS series e-reader." + note = "Congratulations, your station has chosen the Thinktronic 5290 WGW-11-NTOS Series E-reader and Personal Data Assistant!" + default_disk = /obj/item/computer_hardware/hard_drive/role/curator + icon_state = "pda-library" + insert_type = /obj/item/pen/fountain + init_ringtone = "silence" + init_ringer_on = FALSE + +/obj/item/modular_computer/tablet/pda/clear + name = "clear PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a special edition with a transparent case." + note = "Congratulations, you have chosen the Thinktronic 5230-NTOS Personal Data Assistant Deluxe Special Max Turbo Limited Edition!" + icon_state = "pda-clear" + +/obj/item/modular_computer/tablet/pda/cook + name = "cook PDA" + icon_state = "pda-cook" + +/obj/item/modular_computer/tablet/pda/bartender + name = "bartender PDA" + insert_type = /obj/item/pen/fountain + icon_state = "pda-bartender" + +/obj/item/modular_computer/tablet/pda/atmospheric_technician + name = "atmospherics PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/atmos + icon_state = "pda-atmos" + +/obj/item/modular_computer/tablet/pda/chemist + name = "chemist PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/chemistry + icon_state = "pda-chemistry" + +/obj/item/modular_computer/tablet/pda/geneticist + name = "geneticist PDA" + default_disk = /obj/item/computer_hardware/hard_drive/role/medical + icon_state = "pda-genetics" + +/obj/item/modular_computer/tablet/pda/vip + name = "fancy PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a gold-plated 5230-NTOS LRP Series, and probably quite expensive." + note = "Congratulations, you have chosen the Thinktronic 5230-NTOS LRP Series Personal Data Assistant Golden Edition!" + default_disk = /obj/item/computer_hardware/hard_drive/role/vip + insert_type = /obj/item/pen/fountain + icon_state = "pda-gold" + init_ringtone = "ch-CHING" + +/obj/item/modular_computer/tablet/pda/unlicensed + name = "unlicensed PDA" + desc = "A shitty knockoff of a portable microcomputer by Thinktronic Systems, LTD. Complete with a cracked operating system." + note = "Error: Unlicensed software detected. Please contact your supervisor." + default_disk = /obj/item/computer_hardware/hard_drive/role/unlicensed + icon_state = "pda-knockoff" + +// NSV13 - PDA Variants +/obj/item/modular_computer/tablet/pda/botanist + name = "botanist PDA" + icon_state = "pda-hydro" + +/obj/item/modular_computer/tablet/pda/munition + name = "munitions PDA" + icon_state = "pda-munition" + +/obj/item/modular_computer/tablet/pda/heads/maa + name = "master at arms PDA" + icon_state = "pda-maa" diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm index 372d3935126..67c72fd1a48 100644 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -13,18 +13,166 @@ slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT has_light = TRUE //LED flashlight! comp_light_luminosity = 2.3 //Same as the PDA + interaction_flags_atom = INTERACT_ATOM_ALLOW_USER_LOCATION + var/has_variants = TRUE var/finish_color = null + var/list/contained_item = list(/obj/item/pen, /obj/item/toy/crayon, /obj/item/lipstick, /obj/item/flashlight/pen, /obj/item/clothing/mask/cigarette) + var/obj/item/insert_type = /obj/item/pen + var/obj/item/inserted_item + + /// If this tablet can be detonated with detomatix (needs to be refactored into a signal) + var/detonatable = TRUE + + /// The note used by the notekeeping app, stored here for convenience. + var/note = "Congratulations on your station upgrading to the new NtOS and Thinktronic based collaboration effort, bringing you the best in electronics and software since 2467!" + /// Scanned paper + var/obj/item/paper/stored_paper + +/obj/item/modular_computer/tablet/Destroy() + QDEL_NULL(stored_paper) + return ..() + +/obj/item/modular_computer/tablet/ui_static_data(mob/user) + var/list/data = ..() + data["show_imprint"] = TRUE + return data + /obj/item/modular_computer/tablet/update_icon() ..() - if (has_variants) + if (has_variants && !bypass_state) if(!finish_color) finish_color = pick("red","blue","brown","green","black") icon_state = "tablet-[finish_color]" icon_state_unpowered = "tablet-[finish_color]" icon_state_powered = "tablet-[finish_color]" +/obj/item/modular_computer/tablet/proc/try_scan_paper(obj/target, mob/user) + if(!istype(target, /obj/item/paper)) + return FALSE + var/obj/item/paper/paper = target + if (!paper.info) + to_chat(user, "Unable to scan! Paper is blank.") + else + // clean up after ourselves + if(stored_paper) + qdel(stored_paper) + stored_paper = paper.copy(src) + to_chat(user, "Paper scanned. Saved to PDA's notekeeper.") + return TRUE + +/obj/item/modular_computer/tablet/attackby(obj/item/attacking_item, mob/user) + . = ..() + + if(is_type_in_list(attacking_item, contained_item)) + if(attacking_item.w_class >= WEIGHT_CLASS_SMALL) // Prevent putting spray cans, pipes, etc (subtypes of pens/crayons) + return + if(inserted_item) + to_chat(user, "There is already \a [inserted_item] in \the [src]!") + else + if(!user.transferItemToLoc(attacking_item, src)) + return + to_chat(user, "You insert \the [attacking_item] into \the [src].") + inserted_item = attacking_item + playsound(src, 'sound/machines/pda_button1.ogg', 50, TRUE) + update_icon() + if(!try_scan_paper(attacking_item, user)) + return + +/obj/item/modular_computer/tablet/pre_attack(atom/target, mob/living/user, params) + if(try_scan_paper(target, user)) + return FALSE + return ..() + + +/obj/item/modular_computer/tablet/attack(atom/target, mob/living/user, params) + // Send to programs for processing - this should go LAST + // Used to implement the physical scanner. + for(var/datum/computer_file/program/thread in (idle_threads + active_program)) + if(thread.use_attack && !thread.attack(target, user, params)) + return + ..() + +/obj/item/modular_computer/tablet/attack_obj(obj/target, mob/living/user) + // Send to programs for processing - this should go LAST + // Used to implement the gas scanner. + for(var/datum/computer_file/program/thread in (idle_threads + active_program)) + if(thread.use_attack_obj && !thread.attack_obj(target, user)) + return + ..() + +// Eject the pen if the ID was not ejected +/obj/item/modular_computer/tablet/AltClick(mob/user) + if(..() || issilicon(user) || !user.canUseTopic(src, BE_CLOSE)) + return + remove_pen(user) + +// Always eject pen with Ctrl+Click +/obj/item/modular_computer/tablet/CtrlClick(mob/user) + ..() + // We want to allow the user to drag the tablet still + if(isturf(loc) || issilicon(user) || !user.canUseTopic(src, BE_CLOSE)) + return + remove_pen(user) + +// Eject Job Disk +/obj/item/modular_computer/tablet/CtrlShiftClick(mob/user) + ..() + // We want to allow the user to drag the tablet still + if(isturf(loc) || issilicon(user) || !user.canUseTopic(src, BE_CLOSE)) + return + var/obj/item/computer_hardware/hard_drive/role/disk = all_components[MC_HDD_JOB] + if(istype(disk)) + uninstall_component(disk, user, TRUE) + +/obj/item/modular_computer/tablet/verb/verb_toggle_light() + set name = "Toggle Light" + set category = "Object" + set src in oview(1) + toggle_flashlight() + +/obj/item/modular_computer/tablet/verb/verb_remove_pen() + set name = "Eject Pen" + set category = "Object" + set src in usr + remove_pen(usr) + +/obj/item/modular_computer/tablet/proc/remove_pen(mob/user) + if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) //TK doesn't work even with this removed but here for readability + return + if(inserted_item) + to_chat(user, "You remove [inserted_item] from [src].") + user.put_in_hands(inserted_item) + inserted_item = null + playsound(src, 'sound/machines/pda_button2.ogg', 50, TRUE) + update_icon() + else + to_chat(user, "This tablet does not have a pen in it!") + +// Tablet 'splosion.. + +/obj/item/modular_computer/tablet/proc/explode(mob/target, mob/bomber) + var/turf/current_turf = get_turf(src) + + log_bomber(bomber, "tablet-bombed", target, "[bomber && !is_special_character(bomber) ? "(SENT BY NON-ANTAG)" : ""]") + + if (ismob(loc)) + var/mob/victim = loc + victim.show_message("Your [src] explodes!", MSG_VISUAL, "You hear a loud *pop*!", MSG_AUDIBLE) + else + visible_message("[src] explodes!", "You hear a loud *pop*!") + + if(current_turf) + current_turf.hotspot_expose(700,125) + if(istype(all_components[MC_HDD_JOB], /obj/item/computer_hardware/hard_drive/role/virus/syndicate)) + explosion(current_turf, devastation_range = -1, heavy_impact_range = 1, light_impact_range = 3, flash_range = 4) + else + explosion(current_turf, devastation_range = -1, heavy_impact_range = -1, light_impact_range = 2, flash_range = 3) + qdel(src) + +// SUBTYPES + /obj/item/modular_computer/tablet/syndicate_contract_uplink name = "contractor tablet" icon = 'icons/obj/contractor_tablet.dmi' @@ -36,6 +184,8 @@ slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT comp_light_luminosity = 6.3 has_variants = FALSE + device_theme = THEME_SYNDICATE + theme_locked = TRUE /// Given to Nuke Ops members. /obj/item/modular_computer/tablet/nukeops @@ -44,7 +194,8 @@ icon_state_unpowered = "tablet-syndicate" comp_light_luminosity = 6.3 has_variants = FALSE - device_theme = "syndicate" + device_theme = THEME_SYNDICATE + theme_locked = TRUE light_color = COLOR_RED /obj/item/modular_computer/tablet/nukeops/emag_act(mob/user) @@ -64,8 +215,8 @@ has_light = FALSE //tablet light button actually enables/disables the borg lamp comp_light_luminosity = 0 has_variants = FALSE - ///Ref to the borg we're installed in. Set by the borg during our creation. - var/mob/living/silicon/robot/borgo + ///Ref to the silicon we're installed in. Set by the borg during our creation. + var/mob/living/silicon/borgo ///Ref to the Cyborg Self-Monitoring app. Important enough to borgs to deserve a ref. var/datum/computer_file/program/borg_self_monitor/self_monitoring ///IC log that borgs can view in their personal management app @@ -84,7 +235,7 @@ borgo = null return ..() -/obj/item/modular_computer/tablet/integrated/turn_on(mob/user) +/obj/item/modular_computer/tablet/integrated/turn_on(mob/user, open_ui = FALSE) if(borgo?.stat != DEAD) return ..() return FALSE @@ -117,50 +268,143 @@ //Makes the light settings reflect the borg's headlamp settings /obj/item/modular_computer/tablet/integrated/ui_data(mob/user) . = ..() - .["has_light"] = TRUE - .["light_on"] = borgo?.lamp_enabled - .["comp_light_color"] = borgo?.lamp_color - -//Overrides the ui_act to make the flashlight controls link to the borg instead -/obj/item/modular_computer/tablet/integrated/ui_act(action, params) - switch(action) - if("PC_toggle_light") - if(!borgo) - return FALSE - borgo.toggle_headlamp() - return TRUE - - if("PC_light_color") - if(!borgo) - return FALSE - var/mob/user = usr - var/new_color - while(!new_color) - new_color = input(user, "Choose a new color for [src]'s flashlight.", "Light Color",light_color) as color|null - if(!new_color || QDELETED(borgo)) - return - if(color_hex2num(new_color) < 200) //Colors too dark are rejected - to_chat(user, "That color is too dark! Choose a lighter one.") - new_color = null - borgo.lamp_color = new_color - borgo.toggle_headlamp(FALSE, TRUE) - return TRUE - return ..() + if(iscyborg(borgo)) + var/mob/living/silicon/robot/robo = borgo + .["light_on"] = robo.lamp_enabled + .["comp_light_color"] = robo.lamp_color + .["has_light"] = TRUE + +//Makes the flashlight button affect the borg rather than the tablet +/obj/item/modular_computer/tablet/integrated/toggle_flashlight() + if(!borgo || QDELETED(borgo) || !iscyborg(borgo)) + return FALSE + var/mob/living/silicon/robot/robo = borgo + robo.toggle_headlamp() + return TRUE + +//Makes the flashlight color setting affect the borg rather than the tablet +/obj/item/modular_computer/tablet/integrated/set_flashlight_color(color) + if(!borgo || QDELETED(borgo) || !color || !iscyborg(borgo)) + return FALSE + var/mob/living/silicon/robot/robo = borgo + robo.lamp_color = color + robo.toggle_headlamp(FALSE, TRUE) + return TRUE /obj/item/modular_computer/tablet/integrated/alert_call(datum/computer_file/program/caller, alerttext, sound = 'sound/machines/twobeep_high.ogg') if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext) //Yeah, we're checking alert_able. No, you don't get to make alerts that the user can't silence. return + if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) + sound = pick('sound/machines/twobeep_voice1.ogg', 'sound/machines/twobeep_voice2.ogg') borgo.playsound_local(src, sound, 50, TRUE) to_chat(borgo, "The [src] displays a [caller.filedesc] notification: [alerttext]") +/obj/item/modular_computer/tablet/integrated/ui_state(mob/user) + return GLOB.reverse_contained_state + /obj/item/modular_computer/tablet/integrated/syndicate icon_state = "tablet-silicon-syndicate" icon_state_unpowered = "tablet-silicon-syndicate" icon_state_powered = "tablet-silicon-syndicate" icon_state_menu = "command-syndicate" - device_theme = "syndicate" + device_theme = THEME_SYNDICATE + theme_locked = TRUE /obj/item/modular_computer/tablet/integrated/syndicate/Initialize() . = ..() - borgo.lamp_color = COLOR_RED //Syndicate likes it red + if(iscyborg(borgo)) + var/mob/living/silicon/robot/robo = borgo + robo.lamp_color = COLOR_RED //Syndicate likes it red + +// Round start tablets + +/obj/item/modular_computer/tablet/pda + icon = 'nsv13/icons/obj/pda.dmi' //NSV13 - old sprites + icon_state = "pda" + worn_icon_state = "electronic" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + + bypass_state = TRUE + can_store_pai = TRUE + + var/default_disk = 0 + /// If the PDA has been picked up / equipped before. This is used to set the user's preference background color / theme. + var/equipped = FALSE + + ///NSV13 - Holomap - Start + var/datum/component/holomap/holo_base = null //holomap component + var/datum/action/toggle_holomap/useless //Useless thingy meant for to be used for nothing more than deleting the action if it already exists + ///NSV13 - Holomap - Stop + +/obj/item/modular_computer/tablet/pda/send_sound() + if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) + playsound(src, pick('sound/machines/twobeep_voice1.ogg', 'sound/machines/twobeep_voice2.ogg'), 15, TRUE) + else + ..() + +/obj/item/modular_computer/tablet/pda/send_select_sound() + if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) + playsound(src, pick('sound/machines/twobeep_voice1.ogg', 'sound/machines/twobeep_voice2.ogg'), 15, TRUE) + else + ..() + +/obj/item/modular_computer/tablet/pda/equipped(mob/user, slot) + . = ..() + if(equipped || !user.client) + return + classic_color = user.client.prefs.pda_color + equipped = TRUE + +/obj/item/modular_computer/tablet/pda/update_icon() + ..() + var/init_icon = initial(icon) + if(!init_icon) + return + var/obj/item/computer_hardware/card_slot/card = all_components[MC_CARD] + if(card) + if(card.stored_card) + add_overlay(mutable_appearance(init_icon, "id_overlay")) + if(inserted_item) + add_overlay(mutable_appearance(init_icon, "insert_overlay")) + if(light_on) + add_overlay(mutable_appearance(init_icon, "light_overlay")) + + +/obj/item/modular_computer/tablet/pda/attack_ai(mob/user) + to_chat(user, "It doesn't feel right to snoop around like that...") + return // we don't want ais or cyborgs using a private role tablet + +/obj/item/modular_computer/tablet/pda/Initialize(mapload) + . = ..() + install_component(new /obj/item/computer_hardware/hard_drive/small/pda) + install_component(new /obj/item/computer_hardware/processor_unit/small) + install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer)) + install_component(new /obj/item/computer_hardware/network_card) + install_component(new /obj/item/computer_hardware/card_slot) + install_component(new /obj/item/computer_hardware/identifier) + install_component(new /obj/item/computer_hardware/sensorpackage) + + if(default_disk) + var/obj/item/computer_hardware/hard_drive/portable/disk = new default_disk(src) + install_component(disk) + + if(insert_type) + inserted_item = new insert_type(src) + // show the inserted item + update_icon() + + ///NSV13 - Holomap - Start + return INITIALIZE_HINT_LATELOAD + +/obj/item/modular_computer/tablet/pda/LateInitialize() + . = ..() + apply_holomap() + +/obj/item/modular_computer/tablet/pda/proc/apply_holomap() + if(holo_base) + holo_base.RemoveComponent() + QDEL_NULL(holo_base) + AddComponent(/datum/component/holomap) + ///NSV13 - Holomap - Stop diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm index e6e91088fa0..516da5e850c 100644 --- a/code/modules/modular_computers/computers/item/tablet_presets.dm +++ b/code/modules/modular_computers/computers/item/tablet_presets.dm @@ -53,6 +53,11 @@ . = ..() install_component(new /obj/item/computer_hardware/sensorpackage) +/obj/item/modular_computer/tablet/preset/advanced/custodial/Initialize(mapload) + . = ..() + var/obj/item/computer_hardware/hard_drive/small/hard_drive = find_hardware_by_name("solid state drive") + hard_drive.store_file(new /datum/computer_file/program/radar/custodial_locator) + /// Given by the syndicate as part of the contract uplink bundle - loads in the Contractor Uplink. /obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink/Initialize(mapload) . = ..() @@ -84,6 +89,5 @@ /obj/item/modular_computer/tablet/integrated/Initialize() . = ..() install_component(new /obj/item/computer_hardware/processor_unit/small) - install_component(new /obj/item/computer_hardware/hard_drive/small/integrated) install_component(new /obj/item/computer_hardware/recharger/cyborg) install_component(new /obj/item/computer_hardware/network_card/integrated) diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm index c81650e160a..f40a8d484ee 100644 --- a/code/modules/modular_computers/computers/machinery/modular_computer.dm +++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm @@ -62,9 +62,7 @@ add_overlay(screen_icon_screensaver) else icon_state = icon_state_unpowered - set_light(0) else - set_light(light_strength) if(cpu.active_program) add_overlay(cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu) else diff --git a/code/modules/modular_computers/file_system/data.dm b/code/modules/modular_computers/file_system/data.dm index 32ef6f53dd1..9cf2b4ef6f4 100644 --- a/code/modules/modular_computers/file_system/data.dm +++ b/code/modules/modular_computers/file_system/data.dm @@ -1,20 +1,25 @@ -// /data/ files store data in string format. -// They don't contain other logic for now. /datum/computer_file/data - var/stored_data = "" // Stored data in string format. filetype = "DAT" + /// Amount of characters to count as "1 GQ" var/block_size = 250 - var/do_not_edit = 0 // Whether the user will be reminded that the file probably shouldn't be edited. + /// Stored data in string format. Use set_stored_data instead of direct assignment. + var/stored_data = "" + /// Whether the user will be reminded that the file probably shouldn't be edited. + var/do_not_edit = FALSE /datum/computer_file/data/clone() var/datum/computer_file/data/temp = ..() - temp.stored_data = stored_data + temp.set_stored_data(stored_data) return temp -// Calculates file size from amount of characters in saved string +/// Calculates file size from amount of characters in saved string /datum/computer_file/data/proc/calculate_size() size = max(1, round(length(stored_data) / block_size)) -/datum/computer_file/data/logfile +/datum/computer_file/data/proc/set_stored_data(data) + stored_data = data + calculate_size() + +/datum/computer_file/data/log_file filetype = "LOG" diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 5f5fa083608..ad4f9feb13b 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -2,8 +2,10 @@ /datum/computer_file/program filetype = "PRG" filename = "UnknownProgram" // File name. FILE NAME MUST BE UNIQUE IF YOU WANT THE PROGRAM TO BE DOWNLOADABLE FROM NTNET! - var/required_access = null // List of required accesses to *run* the program. - var/transfer_access = null // List of required access to download or file host the program + /// List of required accesses to *run* the program. + var/list/required_access = list() + /// List of required access to download or file host the program + var/list/transfer_access = list() var/program_state = PROGRAM_STATE_KILLED// PROGRAM_STATE_KILLED or PROGRAM_STATE_BACKGROUND or PROGRAM_STATE_ACTIVE - specifies whether this program is running. var/obj/item/modular_computer/computer // Device that runs this program. var/filedesc = "Unknown Program" // User-friendly name of this program. @@ -29,11 +31,17 @@ var/alert_silenced = FALSE /// Whether to highlight our program in the main screen. Intended for alerts, but loosely available for any need to notify of changed conditions. Think Windows task bar highlighting. Available even if alerts are muted. var/alert_pending = FALSE + /// If this program should process attack calls + var/use_attack = FALSE + /// If this program should process attack_obj calls + var/use_attack_obj = FALSE /datum/computer_file/program/New(obj/item/modular_computer/comp = null) ..() - if(comp && istype(comp)) + if(istype(comp)) computer = comp + else if(istype(holder?.holder, /obj/item/modular_computer)) + computer = holder.holder /datum/computer_file/program/Destroy() computer = null @@ -77,7 +85,7 @@ return 1 /** - *Check if the user can run program. Only humans can operate computer. Automatically called in run_program() + *Check if the user can run program. Only humans and silicons can operate computer. Automatically called in on_start() *ID must be inserted into a card slot to be read. If the program is not currently installed (as is the case when *NT Software Hub is checking available software), a list can be given to be used instead. *Arguments: @@ -88,39 +96,40 @@ *access can contain a list of access numbers to check against. If access is not empty, it will be used istead of checking any inserted ID. */ /datum/computer_file/program/proc/can_run(mob/user, loud = FALSE, access_to_check, transfer = FALSE, var/list/access) + if(issilicon(user)) + return TRUE + + if(IsAdminGhost(user)) + return TRUE + + if(!transfer && computer && (computer.obj_flags & EMAGGED)) //emags can bypass the execution locks but not the download ones. + return TRUE + // Defaults to required_access if(!access_to_check) if(transfer && transfer_access) access_to_check = transfer_access else access_to_check = required_access - if(!access_to_check) // No required_access, allow it. - return 1 - - if(!transfer && computer && (computer.obj_flags & EMAGGED)) //emags can bypass the execution locks but not the download ones. - return 1 - - if(IsAdminGhost(user)) - return 1 - - if(issilicon(user)) - return 1 + if(!length(access_to_check)) // No required_access, allow it. + return TRUE if(!length(access)) - var/obj/item/card/id/D + var/obj/item/card/id/access_card var/obj/item/computer_hardware/card_slot/card_slot if(computer) card_slot = computer.all_components[MC_CARD] - D = card_slot?.GetID() + access_card = card_slot?.GetID() - if(!D) + if(!access_card) if(loud) to_chat(user, "\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning.") return FALSE - access = D.GetAccess() + access = access_card.GetAccess() - if(access_to_check in access) - return TRUE + for(var/singular_access in access_to_check) + if(singular_access in access) //For loop checks every individual access entry in the access list. If the user's ID has access to any entry, then we're good. + return TRUE if(loud) to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.") return FALSE @@ -144,14 +153,14 @@ * Arguments: * * user - The mob that started the program **/ -/datum/computer_file/program/proc/run_program(mob/living/user) +/datum/computer_file/program/proc/on_start(mob/living/user) SHOULD_CALL_PARENT(TRUE) if(can_run(user, 1)) if(requires_ntnet && network_destination) generate_network_log("Connection opened to [network_destination].") program_state = PROGRAM_STATE_ACTIVE - return 1 - return 0 + return TRUE + return FALSE /** * @@ -204,17 +213,19 @@ // ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE. /datum/computer_file/program/ui_act(action,params,datum/tgui/ui) if(..()) - return 1 + return TRUE if(computer) + if(computer.device_theme == THEME_THINKTRONIC) + computer.send_select_sound() switch(action) if("PC_exit") computer.kill_program() ui.close() - return 1 + return TRUE if("PC_shutdown") computer.shutdown_computer() ui.close() - return 1 + return TRUE if("PC_minimize") var/mob/user = usr if(!computer.active_program || !computer.all_components[MC_CPU]) @@ -229,15 +240,27 @@ if(user && istype(user)) computer.ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. + return TRUE /datum/computer_file/program/ui_host() - if(computer.physical) - return computer.physical - else + if(computer) + if(computer.physical) + return computer.physical return computer + return ..() /datum/computer_file/program/ui_status(mob/user) if(program_state != PROGRAM_STATE_ACTIVE) // Our program was closed. Close the ui if it exists. return UI_CLOSE return ..() + +/// Return TRUE if nothing was processed. Return FALSE to prevent further actions running. +/// Set use_attack = TRUE to receive proccalls from the parent computer. +/datum/computer_file/program/proc/attack(atom/target, mob/living/user, params) + return TRUE + +/// Return TRUE if nothing was processed. Return FALSE to prevent further actions running. +/// Set use_attack_obj = TRUE to receive proccalls from the parent computer. +/datum/computer_file/program/proc/attack_obj(obj/target, mob/living/user) + return TRUE diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm index 5be92dd0925..a13a4ff9147 100644 --- a/code/modules/modular_computers/file_system/programs/airestorer.dm +++ b/code/modules/modular_computers/file_system/programs/airestorer.dm @@ -7,7 +7,7 @@ size = 12 requires_ntnet = FALSE usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP - transfer_access = ACCESS_HEADS + transfer_access = list(ACCESS_HEADS) available_on_ntnet = TRUE tgui_id = "NtosAiRestorer" program_icon = "laptop-code" diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm index c3d6d9c458e..59eefd18565 100644 --- a/code/modules/modular_computers/file_system/programs/alarm.dm +++ b/code/modules/modular_computers/file_system/programs/alarm.dm @@ -112,7 +112,7 @@ if(L.len) has_alert = TRUE -/datum/computer_file/program/alarm_monitor/run_program(mob/user) +/datum/computer_file/program/alarm_monitor/on_start(mob/user) . = ..(user) if(!.) return diff --git a/code/modules/modular_computers/file_system/programs/antagonist/emag.dm b/code/modules/modular_computers/file_system/programs/antagonist/emag.dm new file mode 100644 index 00000000000..7dbab341d2f --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/antagonist/emag.dm @@ -0,0 +1,22 @@ +/datum/computer_file/program/emag_console + filename = "emag_console" + filedesc = "Crypto-breaker" + category = PROGRAM_CATEGORY_MISC + program_icon_state = "hostile" + extended_desc = "The console output from an emag. You shouldn't be seeing this." + size = 0 + available_on_ntnet = FALSE + tgui_id = "NtosEmagConsole" + +/datum/computer_file/program/emag_console/ui_data(mob/user) + return get_header_data() + +/datum/computer_file/program/emag_console/ui_act(action,params,datum/tgui/ui) + if(!ui || ui.status != UI_INTERACTIVE) + return TRUE + if(computer) + computer.device_theme = THEME_SYNDICATE + computer.allowed_themes = GLOB.ntos_device_themes_emagged + // bye bye UI + qdel(src) + return TRUE diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm index e33f6ef0d77..5dcf3e19a17 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -12,7 +12,7 @@ program_icon = "magnet" var/armed = 0 -/datum/computer_file/program/revelation/run_program(mob/living/user) +/datum/computer_file/program/revelation/on_start(mob/living/user) . = ..() if(!.) return diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm index 683f5ede460..c6ad5c039a0 100644 --- a/code/modules/modular_computers/file_system/programs/atmosscan.dm +++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm @@ -2,14 +2,14 @@ filename = "atmosscan" filedesc = "Atmospheric Scanner" category = PROGRAM_CATEGORY_ENGI - program_icon_state = "air" + program_icon_state = "atmos_control" extended_desc = "A small built-in sensor reads out the atmospheric conditions around the device." network_destination = "atmos scan" size = 4 tgui_id = "NtosAtmos" program_icon = "thermometer-half" -/datum/computer_file/program/atmosscan/run_program(mob/living/user) +/datum/computer_file/program/atmosscan/on_start(mob/living/user) . = ..() if (!.) return diff --git a/code/modules/modular_computers/file_system/programs/borg_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_monitor.dm index 5abde291948..c6dc672ab1e 100644 --- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm @@ -6,7 +6,7 @@ program_icon_state = "generic" extended_desc = "This program allows for remote monitoring of station cyborgs." requires_ntnet = TRUE - transfer_access = ACCESS_ROBOTICS + transfer_access = list(ACCESS_ROBOTICS) network_destination = "cyborg remote monitoring" size = 5 tgui_id = "NtosCyborgRemoteMonitor" @@ -77,7 +77,7 @@ to_chat(usr, "ERROR: Prohibited word(s) detected in message.") return to_chat(usr, "

    Message to [R] (as [sender_name]) -- \"[message]\"
    ") - playsound(usr, 'sound/machines/terminal_success.ogg', 15, TRUE) + computer.send_sound() to_chat(R, "

    Message from [sender_name] -- \"[message]\"
    ") SEND_SOUND(R, 'sound/machines/twobeep_high.ogg') if(R.connected_ai) diff --git a/code/modules/modular_computers/file_system/programs/borg_self_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_self_monitor.dm index 4d72556f429..1fd66dd63d4 100644 --- a/code/modules/modular_computers/file_system/programs/borg_self_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/borg_self_monitor.dm @@ -5,7 +5,6 @@ ui_header = "borg_self_monitor.gif" //DEBUG -- new icon before PR program_icon_state = "command" requires_ntnet = FALSE - transfer_access = null available_on_ntnet = FALSE unsendable = TRUE undeletable = TRUE @@ -19,14 +18,14 @@ tablet = null return ..() -/datum/computer_file/program/borg_self_monitor/run_program(mob/living/user) +/datum/computer_file/program/borg_self_monitor/on_start(mob/living/user) if(!istype(computer, /obj/item/modular_computer/tablet/integrated)) to_chat(user, "A warning flashes across \the [computer]: Device Incompatible.") return FALSE . = ..() if(.) tablet = computer - if(tablet.device_theme == "syndicate") + if(tablet.device_theme == THEME_SYNDICATE) program_icon_state = "command-syndicate" return TRUE return FALSE @@ -51,7 +50,7 @@ data["integrity"] = ((borgo.health + 100) / 2) //Borgo health, as percentage data["lampIntensity"] = borgo.lamp_intensity //Borgo lamp power setting data["sensors"] = "[borgo.sensors_on?"ACTIVE":"DISABLED"]" - data["printerPictures"] = borgo.connected_ai? borgo.connected_ai.aicamera.stored.len : borgo.aicamera.stored.len //Number of pictures taken, synced to AI if available + data["printerPictures"] = borgo.connected_ai ? length(borgo.connected_ai.aicamera?.stored) : length(borgo.aicamera?.stored) //Number of pictures taken, synced to AI if available data["printerToner"] = borgo.toner //amount of toner data["printerTonerMax"] = borgo.tonermax //It's a variable, might as well use it data["thrustersInstalled"] = borgo.ionpulse //If we have a thruster uprade diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm index 16c8fa07326..ab1646a33a5 100644 --- a/code/modules/modular_computers/file_system/programs/card.dm +++ b/code/modules/modular_computers/file_system/programs/card.dm @@ -13,7 +13,7 @@ category = PROGRAM_CATEGORY_CREW program_icon_state = "id" extended_desc = "Program for programming employee ID cards to access parts of the station." - transfer_access = ACCESS_HEADS + transfer_access = list(ACCESS_HEADS) requires_ntnet = 0 size = 8 tgui_id = "NtosCard" @@ -107,24 +107,22 @@ if(..()) return TRUE - var/obj/item/computer_hardware/card_slot/card_slot - var/obj/item/computer_hardware/card_slot/card_slot2 - var/obj/item/computer_hardware/printer/printer - if(computer) - card_slot = computer.all_components[MC_CARD] - card_slot2 = computer.all_components[MC_CARD2] - printer = computer.all_components[MC_PRINT] - if(!card_slot || !card_slot2) - return + if(!computer) + return + + var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + var/obj/item/computer_hardware/card_slot/card_slot2 = computer.all_components[MC_CARD2] + var/obj/item/computer_hardware/printer/printer = computer.all_components[MC_PRINT] + if(!card_slot || !card_slot2) + return var/mob/user = usr var/obj/item/card/id/user_id_card = card_slot.stored_card - var/obj/item/card/id/target_id_card = card_slot2.stored_card switch(action) if("PRG_authenticate") - if(!computer || !user_id_card) + if(!user_id_card) playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) return if(authenticate(user, user_id_card)) @@ -135,7 +133,7 @@ playsound(computer, 'sound/machines/terminal_off.ogg', 50, FALSE) return TRUE if("PRG_print") - if(!computer || !printer) + if(!printer) return if(!authenticated) return @@ -160,7 +158,7 @@ computer.visible_message("\The [computer] prints out a paper.") return TRUE if("PRG_eject") - if(!computer || !card_slot2) + if(!card_slot2) return if(target_id_card) GLOB.data_core.manifest_modify(target_id_card.registered_name, target_id_card.assignment, target_id_card.hud_state) @@ -171,7 +169,7 @@ return card_slot2.try_insert(I, user) return FALSE if("PRG_terminate") - if(!computer || !authenticated) + if(!authenticated) return if(minor) if(!(target_id_card.assignment in head_subordinates) && target_id_card.assignment != JOB_NAME_ASSISTANT) @@ -184,7 +182,7 @@ playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) return TRUE if("PRG_edit") - if(!computer || !authenticated || !target_id_card) + if(!authenticated || !target_id_card) return // Sanitize the name first. We're not using the full sanitize_name proc as ID cards can have a wider variety of things on them that @@ -202,7 +200,7 @@ playsound(computer, "terminal_type", 50, FALSE) return TRUE if("PRG_assign") - if(!computer || !authenticated || !target_id_card) + if(!authenticated || !target_id_card) return var/target = params["assign_target"] if(!target) @@ -245,7 +243,7 @@ playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) return TRUE if("PRG_access") - if(!computer || !authenticated) + if(!authenticated) return var/access_type = text2num(params["access_target"]) if(access_type in (is_centcom ? get_all_centcom_access() : get_all_accesses())) @@ -258,21 +256,21 @@ playsound(computer, "terminal_type", 50, FALSE) return TRUE if("PRG_grantall") - if(!computer || !authenticated || minor) + if(!authenticated || minor) return target_id_card.access |= (is_centcom ? get_all_centcom_access() : get_all_accesses()) log_id("[key_name(usr)] granted All Access to [target_id_card] using [user_id_card] via a portable ID console at [AREACOORD(usr)].") playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) return TRUE if("PRG_denyall") - if(!computer || !authenticated || minor) + if(!authenticated || minor) return target_id_card.access.Cut() log_id("[key_name(usr)] removed All Access from [target_id_card] using [user_id_card] via a portable ID console at [AREACOORD(usr)].") playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) return TRUE if("PRG_grantregion") - if(!computer || !authenticated) + if(!authenticated) return var/region = text2num(params["region"]) if(isnull(region)) @@ -282,7 +280,7 @@ playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) return TRUE if("PRG_denyregion") - if(!computer || !authenticated) + if(!authenticated) return var/region = text2num(params["region"]) if(isnull(region)) diff --git a/code/modules/modular_computers/file_system/programs/cargobounty.dm b/code/modules/modular_computers/file_system/programs/cargobounty.dm index ca6bbdc99b4..4d6fb60dd1c 100644 --- a/code/modules/modular_computers/file_system/programs/cargobounty.dm +++ b/code/modules/modular_computers/file_system/programs/cargobounty.dm @@ -5,7 +5,7 @@ program_icon_state = "bounty" extended_desc = "A basic interface for supply personnel to check and claim bounties." requires_ntnet = TRUE - transfer_access = ACCESS_CARGO + transfer_access = list(ACCESS_CARGO) network_destination = "cargo claims interface" size = 10 tgui_id = "NtosBountyConsole" diff --git a/code/modules/modular_computers/file_system/programs/configurator.dm b/code/modules/modular_computers/file_system/programs/configurator.dm index cf21d853d16..6e4186df8de 100644 --- a/code/modules/modular_computers/file_system/programs/configurator.dm +++ b/code/modules/modular_computers/file_system/programs/configurator.dm @@ -4,8 +4,8 @@ /datum/computer_file/program/computerconfig filename = "compconfig" - filedesc = "Hardware Configuration Tool" - extended_desc = "This program allows configuration of computer's hardware" + filedesc = "Settings" + extended_desc = "This program allows configuration of computer's hardware and operating system" program_icon_state = "generic" unsendable = 1 undeletable = 1 @@ -15,25 +15,24 @@ tgui_id = "NtosConfiguration" program_icon = "cog" - var/obj/item/modular_computer/movable = null - +/datum/computer_file/program/computerconfig/ui_static_data(mob/user) + var/list/data = ..() + data["themes"] = computer.allowed_themes + return data /datum/computer_file/program/computerconfig/ui_data(mob/user) - movable = computer - var/obj/item/computer_hardware/hard_drive/hard_drive = movable.all_components[MC_HDD] - var/obj/item/computer_hardware/battery/battery_module = movable.all_components[MC_CELL] - if(!istype(movable)) - movable = null + var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] + var/obj/item/computer_hardware/battery/battery_module = computer.all_components[MC_CELL] // No computer connection, we can't get data from that. - if(!movable) - return 0 + if(!computer) + return FALSE var/list/data = get_header_data() data["disk_size"] = hard_drive.max_capacity data["disk_used"] = hard_drive.used_capacity - data["power_usage"] = movable.last_power_usage + data["power_usage"] = computer.last_power_usage data["battery_exists"] = battery_module ? 1 : 0 if(battery_module && battery_module.battery) data["battery_rating"] = battery_module.battery.maxcharge @@ -43,8 +42,8 @@ data["battery"] = list("max" = battery_module.battery.maxcharge, "charge" = round(battery_module.battery.charge)) var/list/all_entries[0] - for(var/I in movable.all_components) - var/obj/item/computer_hardware/H = movable.all_components[I] + for(var/I in computer.all_components) + var/obj/item/computer_hardware/H = computer.all_components[I] all_entries.Add(list(list( "name" = H.name, "desc" = H.desc, @@ -62,7 +61,20 @@ return switch(action) if("PC_toggle_component") - var/obj/item/computer_hardware/H = movable.find_hardware_by_name(params["name"]) + var/obj/item/computer_hardware/H = computer.find_hardware_by_name(params["name"]) if(H && istype(H)) H.enabled = !H.enabled . = TRUE + if("PC_select_theme") + if(computer.theme_locked || !(params["theme"] in computer.allowed_themes)) + return + computer.device_theme = computer.allowed_themes[params["theme"]] + . = TRUE + if("PC_set_classic_color") + if(computer.device_theme != THEME_THINKTRONIC) + return + var/new_color = input(usr, "Choose a new color for the device's system theme.", "System Color",computer.classic_color) as color|null + if(!new_color) + return + computer.classic_color = new_color + . = TRUE diff --git a/code/modules/modular_computers/file_system/programs/crewmanifest.dm b/code/modules/modular_computers/file_system/programs/crewmanifest.dm index 4dc038b2523..6fdfbea9782 100644 --- a/code/modules/modular_computers/file_system/programs/crewmanifest.dm +++ b/code/modules/modular_computers/file_system/programs/crewmanifest.dm @@ -4,7 +4,7 @@ category = PROGRAM_CATEGORY_CREW program_icon_state = "id" extended_desc = "Program for viewing and printing the current crew manifest" - transfer_access = ACCESS_HEADS + transfer_access = list(ACCESS_HEADS) requires_ntnet = FALSE size = 4 tgui_id = "NtosCrewManifest" diff --git a/code/modules/modular_computers/file_system/programs/file_browser.dm b/code/modules/modular_computers/file_system/programs/file_browser.dm index 462bcc0b796..92fbc1d7a3e 100644 --- a/code/modules/modular_computers/file_system/programs/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/file_browser.dm @@ -43,7 +43,7 @@ var/datum/computer_file/file = HDD.find_file_by_name(params["name"]) if(!file) return - var/newname = reject_bad_name(params["new_name"]) + var/newname = check_filename(params["new_name"]) if(!newname || newname != params["new_name"]) playsound(computer, 'sound/machines/terminal_error.ogg', 25, FALSE) return @@ -55,7 +55,7 @@ var/datum/computer_file/file = RHDD.find_file_by_name(params["name"]) if(!file) return - var/newname = reject_bad_name(params["new_name"]) + var/newname = check_filename(params["new_name"]) if(!newname || newname != params["new_name"]) playsound(computer, 'sound/machines/terminal_error.ogg', 25, FALSE) return @@ -127,3 +127,12 @@ data["usbfiles"] = usbfiles return data + +/datum/computer_file/program/proc/check_filename(name) + if(CHAT_FILTER_CHECK(name)) + alert(usr, "Filename contains prohibited words.") + return + if(!reject_bad_text(name, 32, ascii_only = TRUE, alphanumeric_only = TRUE, underscore_allowed = TRUE) || lowertext(name) != name) + alert(usr, "All filenames must be 32 characters or less, lowercase, and cannot contain: < > / and \\") + return + return name diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm index 9f9a7cb494f..3c533344894 100644 --- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm +++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm @@ -4,7 +4,7 @@ category = PROGRAM_CATEGORY_CREW program_icon_state = "id" extended_desc = "Program for viewing and changing job slot avalibility." - transfer_access = ACCESS_HEADS + transfer_access = list(ACCESS_HEADS) requires_ntnet = 0 size = 4 tgui_id = "NtosJobManager" diff --git a/code/modules/modular_computers/file_system/programs/log_viewer.dm b/code/modules/modular_computers/file_system/programs/log_viewer.dm new file mode 100644 index 00000000000..8e533d1394b --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/log_viewer.dm @@ -0,0 +1,96 @@ +/datum/computer_file/program/log_viewer + filename = "log_viewer" + filedesc = "Log Viewer" + category = PROGRAM_CATEGORY_MISC + program_icon_state = "generic" + extended_desc = "View logs via NTNet or saved to your system." + size = 4 + tgui_id = "NtosLogViewer" + program_icon = "database" + +/datum/computer_file/program/log_viewer/ui_act(action, list/params, datum/tgui/ui) + . = ..() + if(.) + return + + switch(action) + if("DownloadRemote") + if(!istype(computer)) + return + if(!check_remote()) + computer.visible_message("\The [computer] shows an \"Connection Error - Remote log server connection timeout\" warning.") + return + var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] + if(!hard_drive) + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.") + return + var/datum/computer_file/data/log_file/log + switch(params["name"]) + if("ore_silo") + var/obj/item/computer_hardware/hard_drive/role/job_disk = computer.all_components[MC_HDD_JOB] + if(!istype(job_disk) || !(job_disk.disk_flags & DISK_SILO_LOG) || !GLOB.ore_silo_default) + computer.visible_message("\The [computer] shows an \"Access Error - Remote log server refused connection\" warning.") + return + log = new() + log.set_stored_data(get_silo_log()) + if(!log) + return + var/filename = check_filename(stripped_input(usr, "Enter a name for the file", "File Name Entry", "", 16)) + if(!filename) + return + log.filename = filename + if(!hard_drive.store_file(log)) + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [log.size]GQ\" warning.") + return + return TRUE + +/datum/computer_file/program/log_viewer/ui_data(mob/user) + var/list/data = get_header_data() + if(!istype(computer)) + return data + var/list/datum/computer_file/data/log_file/data_files = list() + var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] + var/obj/item/computer_hardware/hard_drive/ssd = computer.all_components[MC_SDD] + if(hard_drive) + for(var/datum/computer_file/data/log_file/file in hard_drive.stored_files) + data_files += file + if(ssd) + for(var/datum/computer_file/data/log_file/file in ssd.stored_files) + data_files += file + var/files = list() + for(var/datum/computer_file/data/log_file/file in data_files) + files += list(list( + name = file.filename, + size = file.size, + data = file.stored_data, + )) + var/online = check_remote() + var/obj/item/computer_hardware/hard_drive/role/job_disk = computer.all_components[MC_HDD_JOB] + if(GLOB.ore_silo_default && istype(job_disk) && (job_disk.disk_flags & DISK_SILO_LOG)) + var/silo_log + if(online) + silo_log = get_silo_log() + files += list( + list( + name = "ore_silo", + remote = TRUE, + data = silo_log, + online = online, + ) + ) + data["files"] = files + return data + +/datum/computer_file/program/log_viewer/proc/get_silo_log() + if(!GLOB.ore_silo_default) + return "" + var/list/silo_logs = GLOB.silo_access_logs[REF(GLOB.ore_silo_default)] + var/silo_log = "" + for(var/i in length(silo_logs) to 1 step -1) + var/datum/ore_silo_log/entry = silo_logs[i] + // strip_html_simple would be great, if it actually removed the stuff between <>. smh + silo_log += replacetext(replacetext(replacetext("[entry.formatted]\n", "
    ", "\n"), "", ""), "", "") + return silo_log + +/datum/computer_file/program/log_viewer/proc/check_remote() + return computer.get_ntnet_status(NTNET_COMMUNICATION) diff --git a/code/modules/modular_computers/file_system/programs/notepad.dm b/code/modules/modular_computers/file_system/programs/notepad.dm new file mode 100644 index 00000000000..6268744200a --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/notepad.dm @@ -0,0 +1,42 @@ +/datum/computer_file/program/notepad + filename = "notepad" + filedesc = "Notepad" + category = PROGRAM_CATEGORY_MISC + program_icon_state = "generic" + extended_desc = "Jot down your work-safe thoughts and what not." + size = 0 + undeletable = TRUE // It comes by default in PDAs, can't be downloaded, takes no space and should obviously not be able to be deleted. + available_on_ntnet = FALSE + tgui_id = "NtosNotepad" + program_icon = "book" + usage_flags = PROGRAM_TABLET + +/datum/computer_file/program/notepad/ui_act(action, list/params, datum/tgui/ui) + . = ..() + if(.) + return + + switch(action) + if("UpdateNote") + var/obj/item/modular_computer/tablet/tablet = computer + if(!istype(tablet)) + return + tablet.note = params["newnote"] + return TRUE + if("ShowPaper") + var/obj/item/modular_computer/tablet/tablet = computer + if(!istype(tablet) || QDELETED(tablet.stored_paper)) + return + tablet.stored_paper.ui_interact(usr) + return TRUE + + +/datum/computer_file/program/notepad/ui_data(mob/user) + var/list/data = get_header_data() + var/obj/item/modular_computer/tablet/tablet = computer + if(!istype(tablet)) + return data + data["note"] = tablet.note + data["has_paper"] = !QDELETED(tablet.stored_paper) + + return data diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm index bd293606094..a2a7d4e3750 100644 --- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm @@ -32,7 +32,7 @@ PROGRAM_CATEGORY_MISC, ) -/datum/computer_file/program/ntnetdownload/run_program() +/datum/computer_file/program/ntnetdownload/on_start() . = ..() if(!.) return @@ -210,7 +210,7 @@ tgui_id = "NtosNetDownloader" emagged = TRUE -/datum/computer_file/program/ntnetdownload/syndicate/run_program() +/datum/computer_file/program/ntnetdownload/syndicate/on_start() . = ..() if(!.) return diff --git a/code/modules/modular_computers/file_system/programs/ntmessenger.dm b/code/modules/modular_computers/file_system/programs/ntmessenger.dm new file mode 100644 index 00000000000..c5d870f82e4 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/ntmessenger.dm @@ -0,0 +1,424 @@ +#define PDA_SPAM_DELAY 1 MINUTES +/datum/computer_file/program/messenger + filename = "nt_messenger" + filedesc = "Direct Messenger" + category = PROGRAM_CATEGORY_MISC + program_icon_state = "command" + // This should be running when the tablet is created, so it's minimized by default + program_state = PROGRAM_STATE_BACKGROUND + extended_desc = "This program allows old-school communication with other modular devices." + size = 0 + undeletable = TRUE // It comes by default in tablets, can't be downloaded, takes no space and should obviously not be able to be deleted. + available_on_ntnet = FALSE + usage_flags = PROGRAM_TABLET + ui_header = "ntnrc_idle.gif" + tgui_id = "NtosMessenger" + program_icon = "comment-alt" + alert_able = TRUE + + /// The current ringtone (displayed in the chat when a message is received). + var/ringtone = "beep" + /// Whether or not the ringtone is currently on. + var/ringer_status = TRUE + /// Whether or not we're sending and receiving messages. + var/sending_and_receiving = TRUE + /// The messages currently saved in the app. + var/messages = list() + /// great wisdom from PDA.dm - "no spamming" (prevents people from spamming the same message over and over) + var/last_text + /// even more wisdom from PDA.dm - "no everyone spamming" (prevents people from spamming the same message over and over) + var/last_text_everyone + /// Whether or not we allow emojis to be sent by the user. + var/allow_emojis = FALSE + /// Whether or not we're currently looking at the message list. + var/viewing_messages = FALSE + // Whether or not this device is currently hidden from the message monitor. + var/monitor_hidden = FALSE + // Whether or not we're sorting by job. + var/sort_by_job = TRUE + // Whether or not we're sending (or trying to send) a virus. + var/sending_virus = FALSE + + /// The path for the current loaded image in rsc - used only for the "saved image" preview in the Messenger before sending + var/photo_path + + /// Whether or not this app is loaded on a silicon's tablet. + var/is_silicon = FALSE + /// Whether or not we're in a mime PDA. + var/mime_mode = FALSE + +/datum/computer_file/program/messenger/proc/ScrubMessengerList() + var/list/dictionary = list() + + for(var/obj/item/modular_computer/messenger in GetViewableDevices(sort_by_job)) + if(messenger.saved_identification && messenger.saved_job && !(messenger == computer)) + var/list/data = list() + data["name"] = messenger.saved_identification + data["job"] = messenger.saved_job + data["ref"] = REF(messenger) + + //if(data["ref"] != REF(computer)) // you cannot message yourself (despite all my rage) + dictionary += list(data) + + return dictionary + +/proc/GetViewableDevices(sort_by_job = FALSE) + var/list/dictionary = list() + + var/sortmode + if(sort_by_job) + sortmode = GLOBAL_PROC_REF(cmp_pdajob_asc) + else + sortmode = GLOBAL_PROC_REF(cmp_pdaname_asc) + + for(var/obj/item/modular_computer/P in sortList(GLOB.TabletMessengers, sortmode)) + var/obj/item/computer_hardware/hard_drive/drive = P.all_components[MC_HDD] + if(!drive) + continue + for(var/datum/computer_file/program/messenger/app in drive.stored_files) + if(!P.saved_identification || !P.saved_job || P.messenger_invisible || app.monitor_hidden) + continue + dictionary += P + + return dictionary + +/datum/computer_file/program/messenger/proc/StringifyMessengerTarget(obj/item/modular_computer/messenger) + return "[messenger.saved_identification] ([messenger.saved_job])" + +/datum/computer_file/program/messenger/proc/ProcessPhoto() + if(computer.saved_image) + var/icon/img = computer.saved_image.picture_image + var/deter_path = "tmp_msg_photo[rand(0, 99999)].png" + usr << browse_rsc(img, deter_path) // funny random assignment for now, i'll make an actual key later + photo_path = deter_path + +/datum/computer_file/program/messenger/ui_state(mob/user) + if(istype(user, /mob/living/silicon)) + return GLOB.reverse_contained_state + return GLOB.default_state + +/datum/computer_file/program/messenger/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/chat), + ) + +/datum/computer_file/program/messenger/ui_static_data(mob/user) + var/list/data = list() + data["emoji_names"] = icon_states('icons/emoji.dmi') + return data + +/datum/computer_file/program/messenger/ui_act(action, list/params, datum/tgui/ui) + . = ..() + if(.) + return + + switch(action) + if("PDA_ringSet") + var/mob/living/usr_mob = usr + if(!in_range(computer, usr_mob) || computer.loc != usr_mob) + return + var/new_ringtone = stripped_input(usr, "Enter a new ringtone", "Ringtone", ringtone, 20) + if(!new_ringtone) + return + if(SEND_SIGNAL(computer, COMSIG_TABLET_CHANGE_RINGTONE, usr_mob, new_ringtone) & COMPONENT_STOP_RINGTONE_CHANGE) + ui.close(can_be_suspended = FALSE) + return + ringtone = new_ringtone + return TRUE + if("PDA_ringer_status") + ringer_status = !ringer_status + return TRUE + if("PDA_sAndR") + sending_and_receiving = !sending_and_receiving + return TRUE + if("PDA_viewMessages") + viewing_messages = !viewing_messages + return TRUE + if("PDA_clearMessages") + messages = list() + return TRUE + if("PDA_changeSortStyle") + sort_by_job = !sort_by_job + return TRUE + if("PDA_sendEveryone") + if(!sending_and_receiving) + to_chat(usr, "ERROR: Device has sending disabled.") + return + var/obj/item/computer_hardware/hard_drive/role/disk = computer.all_components[MC_HDD_JOB] + if(!disk?.spam_delay) + if(!disk) + return + log_href_exploit(usr) + return + + var/list/targets = list() + + for(var/obj/item/modular_computer/mc in GetViewableDevices()) + targets += mc + + if(targets.len > 0) + if(last_text_everyone && world.time < (last_text_everyone + PDA_SPAM_DELAY * disk.spam_delay)) + to_chat(usr, "Send To All function is still on cooldown. Enabled in [(last_text_everyone + PDA_SPAM_DELAY * disk.spam_delay - world.time)/10] seconds.") + return + send_message(usr, targets, TRUE, multi_delay = disk.spam_delay) + + return TRUE + if("PDA_sendMessage") + if(!sending_and_receiving) + to_chat(usr, "ERROR: Device has sending disabled.") + return + var/obj/item/modular_computer/target = locate(params["ref"]) + if(!istype(target)) + return // we don't want tommy sending his messages to nullspace + if(!(target.saved_identification == params["name"] && target.saved_job == params["job"])) + to_chat(usr, "ERROR: User no longer exists.") + return + + var/obj/item/computer_hardware/hard_drive/drive = target.all_components[MC_HDD] + + for(var/datum/computer_file/program/messenger/app in drive.stored_files) + if(!app.sending_and_receiving && !sending_virus) + to_chat(usr, "ERROR: Device has receiving disabled.") + return + if(sending_virus) + var/obj/item/computer_hardware/hard_drive/role/virus/disk = computer.all_components[MC_HDD_JOB] + if(istype(disk)) + disk.send_virus(target, usr) + return TRUE + send_message(usr, list(target)) + return TRUE + if("PDA_clearPhoto") + computer.saved_image = null + photo_path = null + return TRUE + if("PDA_selectPhoto") + if(!issilicon(usr)) + return + var/mob/living/silicon/user = usr + if(!user.aicamera) + return + if(!length(user.aicamera.stored)) + to_chat(user, "ERROR: No stored photos located.") + if(ringer_status) + playsound(computer, 'sound/machines/terminal_error.ogg', 15, TRUE) + return + var/datum/picture/selected_photo = tgui_select_picture(user, user.aicamera.stored, "Select Message Attachment") + if(!istype(selected_photo, /datum/picture)) + return + computer.saved_image = selected_photo + ProcessPhoto() + return TRUE + if("PDA_toggleVirus") + sending_virus = !sending_virus + return TRUE + +/datum/computer_file/program/messenger/ui_data(mob/user) + var/list/data = get_header_data() + + var/obj/item/computer_hardware/hard_drive/role/disk = computer.all_components[MC_HDD_JOB] + + data["owner"] = computer.saved_identification + // Convert the photo object into a file so it can be rendered properly in Show Messages + for(var/list/message as() in messages) + var/datum/picture/pic = message["photo_obj"] + if(!message["photo"] && istype(pic)) + message["photo"] = pda_rsc_image(pic, message["ref"], user) + message["photo_width"] = pic.psize_x + message["photo_height"] = pic.psize_y + data["messages"] = messages + data["ringer_status"] = ringer_status + data["sending_and_receiving"] = sending_and_receiving + data["messengers"] = ScrubMessengerList() + data["viewing_messages"] = viewing_messages + data["sortByJob"] = sort_by_job + data["isSilicon"] = is_silicon + data["photo"] = photo_path + + if(disk) + data["canSpam"] = disk.spam_delay > 0 + data["virus_attach"] = istype(disk, /obj/item/computer_hardware/hard_drive/role/virus) + data["sending_virus"] = sending_virus + + return data + +/proc/pda_rsc_image(datum/picture/photo, ref, user) + if(!istype(photo) || !photo.picture_image) + return + var/path = "pda_img[ref].png" + user << browse_rsc(photo.picture_image, path) + return path + +//////////////////////// +// MESSAGE HANDLING +//////////////////////// + +// How I Learned To Stop Being A PDA Bloat Chump And Learn To Embrace The Lightweight + +// Gets the input for a message being sent. + +/datum/computer_file/program/messenger/proc/msg_input(mob/living/user = usr, target_name = null) + var/message = null + + if(mime_mode) + message = emoji_sanitize(tgui_input_emoji(user, "NT Messaging")) + else + message = tgui_input_text(user, "Enter a message", "NT Messaging[target_name ? " ([target_name])" : ""]") + + if (!message || !sending_and_receiving) + return + if(!user.canUseTopic(computer, BE_CLOSE)) + return + return sanitize(message) + +/datum/computer_file/program/messenger/proc/send_message(mob/living/user, list/obj/item/modular_computer/targets, everyone = FALSE, fake_name = null, fake_job = null, multi_delay = 0) + if(!targets.len) + return FALSE + var/target_name = length(targets) == 1 ? targets[1].saved_identification : "Everyone" + var/message = msg_input(user, target_name) + if(!message) + return FALSE + // notifying is done somewhere else, this is just a sanity check + if((last_text && world.time < last_text + 10) || (everyone && last_text_everyone && world.time < (last_text_everyone + PDA_SPAM_DELAY * multi_delay))) + return FALSE + if(prob(1)) + message += "\nSent from my PDA" + + // Filter + if(CHAT_FILTER_CHECK(message)) + to_chat(user, "ERROR: Prohibited word(s) detected in message.") + return + + // Check for jammers + var/turf/position = get_turf(computer) + for(var/obj/item/jammer/jammer as anything in GLOB.active_jammers) + var/turf/jammer_turf = get_turf(jammer) + if(position?.z == jammer_turf.z && (get_dist(position, jammer_turf) <= jammer.range)) + return FALSE + + // Send the signal + var/list/string_targets = list() + for (var/obj/item/modular_computer/comp in targets) + if (comp.saved_identification && comp.saved_job) // != src is checked by the UI + string_targets += "[comp.saved_identification] ([comp.saved_job])" + + if (!string_targets.len) + return FALSE + + var/datum/signal/subspace/messaging/tablet_msg/signal = new(computer, list( + "name" = fake_name || computer.saved_identification, + "job" = fake_job || computer.saved_job, + "message" = html_decode(message), + "ref" = REF(computer), + "targets" = targets, + "emojis" = allow_emojis, + "photo" = computer.saved_image, + "automated" = FALSE, + )) + + signal.send_to_receivers() + + // If it didn't reach, note that fact + if (!signal.data["done"]) + to_chat(user, "ERROR: Server isn't responding.") + if(ringer_status) + playsound(computer, 'sound/machines/terminal_error.ogg', 15, TRUE) + return FALSE + + var/target_text = signal.format_target() + + // Create log entry + var/list/message_data = list() + message_data["name"] = signal.data["name"] + message_data["job"] = signal.data["job"] + message_data["target"] = target_text + message_data["contents"] = html_decode(signal.data["message"]) + message_data["outgoing"] = TRUE + message_data["ref"] = signal.data["ref"] + message_data["photo_obj"] = signal.data["photo"] + message_data["emojis"] = signal.data["emojis"] + + // Parse emojis before to_chat + if(allow_emojis) + message = emoji_parse(message)//already sent- this just shows the sent emoji as one to the sender in the to_chat + signal.data["message"] = emoji_parse(signal.data["message"]) + + // Show it to ghosts + var/ghost_message = "[message_data["name"]] PDA Message --> [target_text]: [signal.format_message(include_photo = TRUE)]" + for(var/mob/M in GLOB.player_list) + if(isobserver(M) && (M.client?.prefs.chat_toggles & CHAT_GHOSTPDA)) + to_chat(M, "[FOLLOW_LINK(M, user)] [ghost_message]") + + // Log in the talk log + user.log_talk(message, LOG_PDA, tag="PDA: [initial(message_data["name"])] to [target_text]") + to_chat(user, "PDA message sent to [target_text]: [signal.format_message()]") + + if (ringer_status) + computer.send_sound() + + last_text = world.time + if (everyone) + message_data["name"] = "Everyone" + message_data["job"] = "" + last_text_everyone = world.time + + // Log it in the local PDA's logs + messages += list(message_data) + return TRUE + +/datum/computer_file/program/messenger/proc/receive_message(datum/signal/subspace/messaging/tablet_msg/signal) + var/list/message_data = list() + message_data["name"] = signal.data["name"] + message_data["job"] = signal.data["job"] + message_data["contents"] = html_decode(signal.data["message"]) + message_data["outgoing"] = FALSE + message_data["ref"] = signal.data["ref"] + message_data["automated"] = signal.data["automated"] + message_data["photo_obj"] = signal.data["photo"] + message_data["emojis"] = signal.data["emojis"] + messages += list(message_data) + + var/mob/living/L = null + if(isliving(computer.loc)) + L = computer.loc + //Maybe they are a pAI! + else if(computer) + L = get(computer, /mob/living/silicon) + + if(L && (L.stat == CONSCIOUS || L.stat == SOFT_CRIT)) + var/reply = "(Reply)" + var/hrefstart + var/hrefend + if (isAI(L)) + hrefstart = "" + hrefend = "" + + if(signal.data["automated"]) + reply = "\[Automated Message\]" + + var/inbound_message = signal.format_message(include_photo = TRUE) + if(signal.data["emojis"] == TRUE)//so will not parse emojis as such from pdas that don't send emojis + inbound_message = emoji_parse(inbound_message) + + to_chat(L, "[icon2html(src)] PDA message from [hrefstart][signal.data["name"]] ([signal.data["job"]])[hrefend], [inbound_message] [reply]") + + + if (ringer_status) + computer.ring(ringtone) + +/// topic call that answers to people pressing "(Reply)" in chat +/datum/computer_file/program/messenger/Topic(href, href_list) + ..() + if(QDELETED(src)) + return + // Open messenger in the background + if(!computer.enabled) + if(!computer.turn_on(usr, open_ui = FALSE)) + return + if(computer.active_program != src) + if(!computer.open_program(usr, src, in_background = TRUE)) + return + if(!href_list["close"] && usr.canUseTopic(computer, BE_CLOSE, FALSE, NO_TK)) + switch(href_list["choice"]) + if("Message") + send_message(usr, list(locate(href_list["target"]))) +#undef PDA_SPAM_DELAY diff --git a/code/modules/modular_computers/file_system/programs/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/ntmonitor.dm index 0ff1061f345..69a9e61ed3a 100644 --- a/code/modules/modular_computers/file_system/programs/ntmonitor.dm +++ b/code/modules/modular_computers/file_system/programs/ntmonitor.dm @@ -6,7 +6,7 @@ extended_desc = "This program monitors stationwide NTNet network, provides access to logging systems, and allows for configuration changes" size = 12 requires_ntnet = TRUE - required_access = ACCESS_NETWORK //NETWORK CONTROL IS A MORE SECURE PROGRAM. + required_access = list(ACCESS_NETWORK) //NETWORK CONTROL IS A MORE SECURE PROGRAM. available_on_ntnet = TRUE tgui_id = "NtosNetMonitor" program_icon = "network-wired" diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm index 0fa5a09d8ee..00a1882ed39 100644 --- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm @@ -121,26 +121,23 @@ if("PRG_savelog") if(!channel) return - var/logname = stripped_input(params["log_name"]) + var/logname = check_filename(params["log_name"]) if(!logname) return - var/datum/computer_file/data/logfile = new /datum/computer_file/data/logfile() + var/datum/computer_file/data/log_file/logfile = new() // Now we will generate HTML-compliant file that can actually be viewed/printed. logfile.filename = logname - logfile.stored_data = "\[b\]Logfile dump from NTNRC channel [channel.title]\[/b\]\[BR\]" + var/log_data = "Logfile dump from NTNRC channel [channel.title]\n" for(var/logstring in channel.messages) - logfile.stored_data = "[logfile.stored_data][logstring]\[BR\]" - logfile.stored_data = "[logfile.stored_data]\[b\]Logfile dump completed.\[/b\]" - logfile.calculate_size() + log_data += "[logstring]\n" + log_data += "\nLogfile dump completed.\n" + logfile.set_stored_data(log_data) var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - if(!computer || !hard_drive || !hard_drive.store_file(logfile)) - if(!computer) - // This program shouldn't even be runnable without computer. - CRASH("Var computer is null!") - if(!hard_drive) - computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.") - else // In 99.9% cases this will mean our HDD is full - computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [logfile.size]GQ\" warning.") + if(!hard_drive) + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.") + else if(!hard_drive.store_file(logfile)) + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [logfile.size]GQ\" warning.") + computer.send_sound() return TRUE if("PRG_renamechannel") if(!authed) @@ -195,7 +192,7 @@ else ui_header = "ntnrc_idle.gif" -/datum/computer_file/program/chatclient/run_program(mob/living/user) +/datum/computer_file/program/chatclient/on_start(mob/living/user) . = ..() if(!.) return diff --git a/code/modules/modular_computers/file_system/programs/phys_scanner.dm b/code/modules/modular_computers/file_system/programs/phys_scanner.dm new file mode 100644 index 00000000000..e51b15137cd --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/phys_scanner.dm @@ -0,0 +1,116 @@ +/datum/computer_file/program/phys_scanner + filename = "phys_scanner" + filedesc = "Physical Scanner" + program_icon_state = "generic" + category = PROGRAM_CATEGORY_MISC + extended_desc = "This program allows the tablet to scan physical objects and display a data output." + size = 8 + usage_flags = PROGRAM_TABLET + available_on_ntnet = FALSE + tgui_id = "NtosPhysScanner" + program_icon = "barcode" + // Process attack calls from the computer + use_attack = TRUE + use_attack_obj = TRUE + + var/current_mode = 0 + var/available_modes = 0 + + var/last_record = "" + +/datum/computer_file/program/phys_scanner/proc/mode_to_names(mode_holder, use_list = FALSE) + var/reads = list() + if(mode_holder & DISK_CHEM) + reads += "Reagent" + if(mode_holder & DISK_MED) + reads += "Health" + if(mode_holder & DISK_POWER) + reads += "Radiation" + if(mode_holder & DISK_ATMOS) + reads += "Gas" + if(!length(reads)) + return + return use_list ? reads : reads[1] + +/datum/computer_file/program/phys_scanner/proc/ReadModes() + return mode_to_names(available_modes, use_list = TRUE) + +/datum/computer_file/program/phys_scanner/proc/ReadCurrent() + return mode_to_names(current_mode) + +/datum/computer_file/program/phys_scanner/attack(atom/target, mob/living/user, params) + switch(current_mode) + if(DISK_CHEM) + var/mob/living/carbon/carbon = target + if(istype(carbon)) + user.visible_message("[user] analyzes [carbon]'s vitals.", "You analyze [carbon]'s vitals.") + last_record = chemscan(user, carbon) + return FALSE + else if(!istype(target, /obj/item/reagent_containers/pill/floorpill) && !istype(target, /obj/item/reagent_containers/glass/chem_heirloom)) + if(!isnull(target.reagents)) + if(target.reagents.reagent_list.len > 0) + var/reagents_length = target.reagents.reagent_list.len + last_record = "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.\n" + for (var/re in target.reagents.reagent_list) + last_record += "\t [re]\n" + else + last_record = "No active chemical agents found in [target]." + else + last_record = "No significant chemical agents found in [target]." + return FALSE + if(DISK_MED) + var/mob/living/carbon/carbon = target + if(istype(carbon)) + user.visible_message("[user] analyzes [carbon]'s vitals.", "You analyze [carbon]'s vitals.") + last_record = healthscan(user, carbon, 1) + return FALSE + if(DISK_POWER) + var/mob/living/carbon/carbon = target + if(istype(carbon)) + user.visible_message("[user] analyzes [carbon]'s radiation levels.", "You analyze [carbon]'s radiation levels.") + last_record = "Analyzing Results for [carbon]:\n" + if(carbon.radiation) + last_record += "Radiation Level: [carbon.radiation]%" + else + last_record += "No radiation detected." + return FALSE + return ..() + +/datum/computer_file/program/phys_scanner/attack_obj(obj/target, mob/living/user) + switch(current_mode) + if(DISK_ATMOS) + var/scan_result = atmosanalyzer_scan(user, target, silent = TRUE) + if(scan_result) + user.visible_message("[user] analyzes [icon2html(target, viewers(user))] [target]'s gas contents.", "You analyze [icon2html(target, user)] [target]'s gas contents.") + last_record = scan_result + return FALSE + return ..() + +/datum/computer_file/program/phys_scanner/ui_act(action, list/params, datum/tgui/ui) + . = ..() + if(.) + return + + switch(action) + if("selectMode") + switch(params["newMode"]) + if("Reagent") + current_mode = DISK_CHEM + if("Health") + current_mode = DISK_MED + if("Radiation") + current_mode = DISK_POWER + if("Gas") + current_mode = DISK_ATMOS + + return UI_UPDATE + + +/datum/computer_file/program/phys_scanner/ui_data(mob/user) + var/list/data = get_header_data() + + data["set_mode"] = ReadCurrent() + data["last_record"] = last_record + data["available_modes"] = ReadModes() + + return data diff --git a/code/modules/modular_computers/file_system/programs/portrait_printer.dm b/code/modules/modular_computers/file_system/programs/portrait_printer.dm index f0f16c90d09..d55ea45b59e 100644 --- a/code/modules/modular_computers/file_system/programs/portrait_printer.dm +++ b/code/modules/modular_computers/file_system/programs/portrait_printer.dm @@ -14,7 +14,7 @@ category = PROGRAM_CATEGORY_MISC program_icon_state = "dummy" extended_desc = "This program connects to a Spinward Sector community art site for viewing and printing art." - transfer_access = ACCESS_LIBRARY + transfer_access = list(ACCESS_LIBRARY) usage_flags = PROGRAM_CONSOLE requires_ntnet = TRUE size = 9 diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm index ee45ebdd5ef..b3cc10ae4d9 100644 --- a/code/modules/modular_computers/file_system/programs/powermonitor.dm +++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm @@ -7,7 +7,7 @@ program_icon_state = "power_monitor" extended_desc = "This program connects to sensors around the station to provide information about electrical systems" ui_header = "power_norm.gif" - transfer_access = ACCESS_ENGINE + transfer_access = list(ACCESS_ENGINE) usage_flags = PROGRAM_CONSOLE requires_ntnet = 0 network_destination = "power monitoring system" @@ -26,7 +26,7 @@ var/next_record = 0 -/datum/computer_file/program/power_monitor/run_program(mob/living/user) +/datum/computer_file/program/power_monitor/on_start(mob/living/user) . = ..(user) if(!.) return diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm index 4ad0491586f..7246c881334 100644 --- a/code/modules/modular_computers/file_system/programs/radar.dm +++ b/code/modules/modular_computers/file_system/programs/radar.dm @@ -7,7 +7,6 @@ ui_header = "borg_mon.gif" //DEBUG -- new icon before PR program_icon_state = "radarntos" requires_ntnet = TRUE - transfer_access = null available_on_ntnet = FALSE usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET network_destination = "tracking program" @@ -27,7 +26,7 @@ var/pointercolor = "green" COOLDOWN_DECLARE(last_scan) -/datum/computer_file/program/radar/run_program(mob/living/user) +/datum/computer_file/program/radar/on_start(mob/living/user) . = ..() if(.) START_PROCESSING(SSfastprocess, src) @@ -51,6 +50,8 @@ /datum/computer_file/program/radar/ui_data(mob/user) var/list/data = get_header_data() + // PDAs should not have full radar capabilities + data["full_capability"] = !istype(computer, /obj/item/modular_computer/tablet/pda) data["selected"] = selected data["objects"] = list() data["scanning"] = (world.time < next_scan) @@ -98,8 +99,9 @@ var/pointer = "crosshairs" var/locx = (target_turf.x - here_turf.x) + 24 var/locy = (here_turf.y - target_turf.y) + 24 + var/dist = get_dist_euclidian(here_turf, target_turf) - if(get_dist_euclidian(here_turf, target_turf) > 24) + if(dist > 24 || istype(computer, /obj/item/modular_computer/tablet/pda)) userot = TRUE rot = round(get_angle(here_turf, target_turf)) else @@ -116,6 +118,9 @@ "arrowstyle" = arrowstyle, "color" = pointercolor, "pointer" = pointer, + "gpsx" = target_turf.x, + "gpsy" = target_turf.y, + "dist" = round(dist), ) return trackinfo @@ -215,7 +220,7 @@ filedesc = "Lifeline" extended_desc = "This program allows for tracking of crew members via their suit sensors." requires_ntnet = TRUE - transfer_access = ACCESS_MEDICAL + transfer_access = list(ACCESS_MEDICAL) available_on_ntnet = TRUE program_icon = "heartbeat" @@ -258,6 +263,48 @@ return TRUE return FALSE +///Tracks all janitor equipment +/datum/computer_file/program/radar/custodial_locator + filename = "custodiallocator" + filedesc = "Custodial Locator" + extended_desc = "This program allows for tracking of custodial equipment." + requires_ntnet = TRUE + transfer_access = list(ACCESS_JANITOR) + available_on_ntnet = TRUE + program_icon = "broom" + size = 2 + +/datum/computer_file/program/radar/custodial_locator/find_atom() + return locate(selected) in GLOB.janitor_devices + +/datum/computer_file/program/radar/custodial_locator/scan() + if(world.time < next_scan) + return + next_scan = world.time + (2 SECONDS) + objects = list() + for(var/obj/custodial_tools as anything in GLOB.janitor_devices) + if(!trackable(custodial_tools)) + continue + var/tool_name = custodial_tools.name + + if(istype(custodial_tools, /obj/item/mop)) + var/obj/item/mop/wet_mop = custodial_tools + tool_name = "[wet_mop.reagents.total_volume ? "Wet" : "Dry"] [wet_mop.name]" + + if(istype(custodial_tools, /obj/structure/janitorialcart)) + var/obj/structure/janitorialcart/janicart = custodial_tools + tool_name = "[janicart.name] - Water level: [janicart.reagents.total_volume] / [janicart.reagents.maximum_volume]" + + if(istype(custodial_tools, /mob/living/simple_animal/bot/cleanbot)) + var/mob/living/simple_animal/bot/cleanbot/cleanbots = custodial_tools + tool_name = "[cleanbots.name] - [cleanbots.on ? "Online" : "Offline"]" + + var/list/tool_information = list( + ref = REF(custodial_tools), + name = tool_name, + ) + objects += list(tool_information) + //////////////////////// //Nuke Disk Finder App// //////////////////////// @@ -270,7 +317,6 @@ program_icon_state = "radarsyndicate" extended_desc = "This program allows for tracking of nuclear authorization disks and warheads." requires_ntnet = FALSE - transfer_access = null available_on_ntnet = FALSE available_on_syndinet = TRUE tgui_id = "NtosRadarSyndicate" diff --git a/code/modules/modular_computers/file_system/programs/records.dm b/code/modules/modular_computers/file_system/programs/records.dm new file mode 100644 index 00000000000..97e0b95b0d4 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/records.dm @@ -0,0 +1,87 @@ +/datum/computer_file/program/records + filename = "ntrecords" + filedesc = "Records" + extended_desc = "Allows the user to view several basic records from the crew." + category = PROGRAM_CATEGORY_MISC + program_icon = "clipboard" + program_icon_state = "crew" + tgui_id = "NtosRecords" + size = 4 + usage_flags = PROGRAM_TABLET | PROGRAM_LAPTOP + available_on_ntnet = FALSE + + var/mode + +/datum/computer_file/program/records/medical + filedesc = "Medical Records" + filename = "medrecords" + program_icon_state = "med-records" + program_icon = "book-medical" + extended_desc = "Allows the user to view several basic medical records from the crew." + transfer_access = list(ACCESS_MEDICAL, ACCESS_HEADS) + available_on_ntnet = TRUE + mode = "medical" + +/datum/computer_file/program/records/security + filedesc = "Security Records" + filename = "secrecords" + program_icon_state = "sec-records" + extended_desc = "Allows the user to view several basic security records from the crew." + transfer_access = list(ACCESS_SECURITY, ACCESS_HEADS) + available_on_ntnet = TRUE + mode = "security" + +/datum/computer_file/program/records/proc/GetRecordsReadable() + var/list/all_records = list() + + + switch(mode) + if("security") + for(var/datum/data/record/person in GLOB.data_core.general) + var/datum/data/record/security_person = find_record("id", person.fields["id"], GLOB.data_core.security) + var/list/current_record = list() + + if(security_person) + current_record["wanted"] = security_person.fields["criminal"] + + current_record["id"] = person.fields["id"] + current_record["name"] = person.fields["name"] + current_record["rank"] = person.fields["rank"] + current_record["sex"] = person.fields["sex"] + current_record["age"] = person.fields["age"] + current_record["species"] = person.fields["species"] + current_record["fingerprint"] = person.fields["fingerprint"] + + all_records += list(current_record) + if("medical") + for(var/datum/data/record/person in GLOB.data_core.general) + var/list/current_record = list() + + current_record["id"] = person.fields["id"] + current_record["name"] = person.fields["name"] + current_record["rank"] = person.fields["rank"] + current_record["sex"] = person.fields["sex"] + current_record["age"] = person.fields["age"] + current_record["species"] = person.fields["species"] + + var/datum/data/record/medical_person = find_record("id", person.fields["id"], GLOB.data_core.medical) + + if(medical_person) + current_record["b_dna"] = medical_person.fields["b_dna"] + current_record["bloodtype"] = medical_person.fields["blood_type"] + current_record["mi_dis"] = medical_person.fields["mi_dis"] + current_record["ma_dis"] = medical_person.fields["ma_dis"] + current_record["notes"] = medical_person.fields["notes"] + current_record["cnotes"] = medical_person.fields["notes_d"] + + all_records += list(current_record) + + return all_records + + + +/datum/computer_file/program/records/ui_data(mob/user) + var/list/data = get_header_data() + data["records"] = GetRecordsReadable() + data["mode"] = mode + return data diff --git a/code/modules/modular_computers/file_system/programs/remote_airlock.dm b/code/modules/modular_computers/file_system/programs/remote_airlock.dm new file mode 100644 index 00000000000..66e0709b952 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/remote_airlock.dm @@ -0,0 +1,52 @@ +/datum/computer_file/program/remote_airlock + filename = "remote_airlock" + filedesc = "Remote Airlock Control" + extended_desc = "Allows remote control of select airlocks via an integrated local bluespace relay." + category = PROGRAM_CATEGORY_MISC + program_icon = "lock-open" + tgui_id = "NtosAirlockControl" + size = 1 + available_on_ntnet = FALSE + undeletable = TRUE + unsendable = TRUE + +/datum/computer_file/program/remote_airlock/ui_data(mob/user) + var/list/data = get_header_data() + var/list/airlocks = list() + var/list/all_controllable = list() + var/obj/item/computer_hardware/hard_drive/drive = computer.all_components[MC_HDD] + if(istype(drive) && length(drive.controllable_airlocks)) + all_controllable += drive.controllable_airlocks + drive = computer.all_components[MC_HDD_JOB] + if(istype(drive) && length(drive.controllable_airlocks)) + all_controllable += drive.controllable_airlocks + for(var/obj/machinery/door/poddoor/airlock in GLOB.airlocks) + if((airlock.id in all_controllable) && airlock.get_virtual_z_level() == computer.get_virtual_z_level() && !QDELETED(airlock)) + var/turf/L = get_turf(airlock) + airlocks += list(list("id" = airlock.id, + "name" = airlock.name, + "open" = !airlock.density, + "locx" = "[L.x]", + "locy" = "[L.y]", + )) + data["airlocks"] = airlocks + return data + +/datum/computer_file/program/remote_airlock/ui_act(action, params, datum/tgui/ui) + . = ..() + if(.) + return + switch(action) + if("airlock_control") + if(!params["id"]) + return + for(var/obj/machinery/door/poddoor/airlock in GLOB.airlocks) + if(airlock.id == params["id"]) + // Fail, but reload data + if(airlock.get_virtual_z_level() != computer.get_virtual_z_level()) + return TRUE + if(airlock.density) + airlock.open() + else + airlock.close() + return TRUE diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm index 320b2ccb018..bf73121c0be 100644 --- a/code/modules/modular_computers/file_system/programs/robocontrol.dm +++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm @@ -27,10 +27,8 @@ var/obj/item/computer_hardware/card_slot/card_slot = computer ? computer.all_components[MC_CARD] : null data["have_id_slot"] = !!card_slot if(computer) - var/obj/item/card/id/id_card = card_slot ? card_slot.stored_card : null - data["has_id"] = !!id_card - data["id_owner"] = id_card ? id_card.registered_name : "No Card Inserted." - data["access_on_card"] = id_card ? id_card.access : null + var/obj/item/card/id/id_card = card_slot ? card_slot.stored_card : "" + data["id_owner"] = id_card botcount = 0 current_user = user @@ -84,19 +82,12 @@ var/list/standard_actions = list("patroloff", "patrolon", "ejectpai") var/list/MULE_actions = list("stop", "go", "home", "destination", "setid", "sethome", "unload", "autoret", "autopick", "report", "ejectpai") - var/mob/living/simple_animal/bot/Bot = locate(params["robot"]) in GLOB.bots_list - var access_okay = TRUE - if(!id_card && !Bot.bot_core.allowed(current_user)) - access_okay = FALSE - else if(id_card && !Bot.bot_core.check_access(id_card)) - access_okay = FALSE - if (access_okay && (action in standard_actions)) - Bot.bot_control(action, current_user, id_card ? id_card.access : current_access) - if (access_okay && (action in MULE_actions)) - Bot.bot_control(action, current_user, id_card ? id_card.access : current_access, TRUE) + var/mob/living/simple_animal/bot/selected_bot = locate(params["robot"]) in GLOB.bots_list switch(action) if("summon") - Bot.bot_control(action, current_user, id_card ? id_card.access : current_access) + if(!selected_bot) + return + selected_bot.bot_control(action, current_user, id_card ? id_card.access : current_access) if("ejectcard") if(!computer || !card_slot) return @@ -105,4 +96,14 @@ card_slot.try_eject(current_user) else playsound(get_turf(ui_host()) , 'sound/machines/buzz-sigh.ogg', 25, FALSE) - return + if(!selected_bot) + return + var access_okay = TRUE + if(!id_card && !selected_bot.bot_core.allowed(current_user)) + access_okay = FALSE + else if(id_card && !selected_bot.bot_core.check_access(id_card)) + access_okay = FALSE + if (access_okay && (action in standard_actions)) + selected_bot.bot_control(action, current_user, id_card ? id_card.access : current_access) + if (access_okay && (action in MULE_actions)) + selected_bot.bot_control(action, current_user, id_card ? id_card.access : current_access, TRUE) diff --git a/code/modules/modular_computers/file_system/programs/secureye.dm b/code/modules/modular_computers/file_system/programs/secureye.dm index be142cc7e57..f5872c131aa 100644 --- a/code/modules/modular_computers/file_system/programs/secureye.dm +++ b/code/modules/modular_computers/file_system/programs/secureye.dm @@ -7,7 +7,7 @@ program_icon_state = "generic" extended_desc = "This program allows access to standard security camera networks." requires_ntnet = TRUE - transfer_access = ACCESS_SECURITY + transfer_access = list(ACCESS_SECURITY) usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP size = 5 tgui_id = "NtosSecurEye" diff --git a/code/modules/modular_computers/file_system/programs/signaller.dm b/code/modules/modular_computers/file_system/programs/signaller.dm index ee9df374044..0e82a6bd4fc 100644 --- a/code/modules/modular_computers/file_system/programs/signaller.dm +++ b/code/modules/modular_computers/file_system/programs/signaller.dm @@ -15,7 +15,7 @@ /// Radio connection datum used by signallers. var/datum/radio_frequency/radio_connection -/datum/computer_file/program/signaller/run_program(mob/living/user) +/datum/computer_file/program/signaller/on_start(mob/living/user) . = ..() if (!.) return diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm index 8e251cca960..822b93e775a 100644 --- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm @@ -6,7 +6,7 @@ program_icon_state = "smmon_0" extended_desc = "This program connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines." requires_ntnet = TRUE - transfer_access = ACCESS_CONSTRUCTION + transfer_access = list(ACCESS_CONSTRUCTION) network_destination = "supermatter monitoring system" size = 5 tgui_id = "NtosSupermatterMonitor" @@ -31,7 +31,7 @@ if(istype(computer)) computer.update_icon() -/datum/computer_file/program/supermatter_monitor/run_program(mob/living/user) +/datum/computer_file/program/supermatter_monitor/on_start(mob/living/user) . = ..(user) if(!.) return diff --git a/code/modules/modular_computers/file_system/programs/statusdisplay.dm b/code/modules/modular_computers/file_system/programs/statusdisplay.dm new file mode 100644 index 00000000000..f283ecf66e4 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/statusdisplay.dm @@ -0,0 +1,69 @@ +/datum/computer_file/program/status + filename = "statusdisplay" + filedesc = "Status Display" + program_icon = "signal" + program_icon_state = "generic" + requires_ntnet = TRUE + size = 4 + + extended_desc = "An app used to change the message on the station status displays." + tgui_id = "NtosStatus" + + usage_flags = PROGRAM_ALL + available_on_ntnet = FALSE + + var/upper_text + var/lower_text + var/picture + +/datum/computer_file/program/status/proc/SendSignal(type) + var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS) + + if(!frequency) + return + + var/datum/signal/status_signal = new(list("command" = type)) + switch(type) + if("message") + var/data1 = reject_bad_text(upper_text || "", MAX_STATUS_LINE_LENGTH) + var/data2 = reject_bad_text(lower_text || "", MAX_STATUS_LINE_LENGTH) + status_signal.data["msg1"] = data1 + status_signal.data["msg2"] = data2 + message_admins("[ADMIN_LOOKUPFLW(usr)] changed the Status Message to - [data1], [data2] - From the Status Display app.") + log_game("[key_name(usr)] changed the Status Message to - [data1], [data2] - From the Status Display app.") + if("alert") + status_signal.data["picture_state"] = picture + + frequency.post_signal(computer, status_signal) + +/datum/computer_file/program/status/proc/SetText(position, text) + switch(position) + if("upper") + upper_text = text + if("lower") + lower_text = text + +/datum/computer_file/program/status/ui_act(action, list/params, datum/tgui/ui) + . = ..() + if(.) + return + + switch(action) + if("stat_send") + SendSignal("message") + if("stat_update") + SetText(params["position"], params["text"]) + if("stat_pic") + var/chosen_picture = params["picture"] + if (!(chosen_picture in GLOB.approved_status_pictures)) + return + picture = chosen_picture + SendSignal("alert") + +/datum/computer_file/program/status/ui_data(mob/user) + var/list/data = get_header_data() + + data["upper"] = upper_text + data["lower"] = lower_text + + return data diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm index c2387e12824..b2398841278 100644 --- a/code/modules/modular_computers/hardware/_hardware.dm +++ b/code/modules/modular_computers/hardware/_hardware.dm @@ -33,6 +33,8 @@ var/malfunction_probability = 10 /// What define is used to qualify this piece of hardware? Important for upgraded versions of the same hardware. var/device_type + /// If the hardware can be "hotswapped" (ejected when another is installed) + var/hotswap = FALSE /obj/item/computer_hardware/New(var/obj/L) ..() @@ -44,6 +46,9 @@ holder.forget_component(src) return ..() +/// Called when the hardware is inserted BY HAND. Use on_install for cases where it's installed by code. +/obj/item/computer_hardware/proc/on_inserted() + return /obj/item/computer_hardware/attackby(obj/item/I, mob/living/user) // Cable coil. Works as repair method, but will probably require multiple applications and more cable. diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm index 4e8bae4f719..0904e7b8a3c 100644 --- a/code/modules/modular_computers/hardware/card_slot.dm +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -6,7 +6,9 @@ w_class = WEIGHT_CLASS_TINY device_type = MC_CARD - var/obj/item/card/id/stored_card = null + var/obj/item/card/id/stored_card + var/current_identification + var/current_job /obj/item/computer_hardware/card_slot/handle_atom_del(atom/A) if(A == stored_card) @@ -65,7 +67,10 @@ if(ishuman(user)) var/mob/living/carbon/human/H = user H.sec_hud_set_ID() - + current_identification = stored_card.registered_name + current_job = stored_card.assignment + holder?.on_id_insert() + holder?.update_icon() return TRUE @@ -88,10 +93,15 @@ var/datum/computer_file/program/computer_program = p computer_program.event_idremoved(1) if(ishuman(user)) - var/mob/living/carbon/human/human_user = user - human_user.sec_hud_set_ID() + var/mob/living/carbon/human/human_wearer = user + if(human_wearer.wear_id == holder) + human_wearer.sec_hud_set_ID() to_chat(user, "You remove the card from \the [src].") playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + stored_card = null + current_identification = null + current_job = null + holder?.update_icon() return TRUE /obj/item/computer_hardware/card_slot/attackby(obj/item/I, mob/living/user) @@ -103,7 +113,7 @@ try_eject(user) return swap_slot() - to_chat(user, "You adjust the connecter to fit into [expansion_hw ? "an expansion bay" : "the primary ID bay"].") + to_chat(user, "You adjust the connector to fit into [expansion_hw ? "an expansion bay" : "the primary ID bay"].") /** *Swaps the card_slot hardware between using the dedicated card slot bay on a computer, and using an expansion bay. diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index fdc1da0ac54..cb1a777250d 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -8,16 +8,31 @@ device_type = MC_HDD var/max_capacity = 128 var/used_capacity = 0 - var/list/stored_files = list() // List of stored files on this drive. DO NOT MODIFY DIRECTLY! + /// List of stored files on this drive. DO NOT MODIFY DIRECTLY! + var/list/stored_files = list() + /// If we should install the default programs + var/default_installs = TRUE + /// If the drive has been installed before (used to prevent re-setting initial ringtone) + var/has_been_installed = FALSE + /// List of airlocks this disk can control with program/remote_airlock + var/list/controllable_airlocks /obj/item/computer_hardware/hard_drive/on_remove(obj/item/modular_computer/remove_from, mob/user) remove_from.shutdown_computer() +/obj/item/computer_hardware/hard_drive/on_install(obj/item/modular_computer/install_into, mob/living/user) + // We don't want to install again if they remove the drive + if(has_been_installed) + return + has_been_installed = TRUE + // Add default programs now, instead of Initialize (this is important so they have a reference to "holder" and thus "computer") + if(default_installs) + install_default_programs() + /obj/item/computer_hardware/hard_drive/proc/install_default_programs() - store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar store_file(new/datum/computer_file/program/ntnetdownload(src)) // NTNet Downloader Utility, allows users to download more software from NTNet repository + store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar store_file(new/datum/computer_file/program/filemanager(src)) // File manager, allows text editor functions and basic file manipulation. - store_file(new/datum/computer_file/program/databank_uplink(src)) // NSV13 - Wiki Uplink in Modular Computer form, allows the computer to access the NSV13 Wiki! /obj/item/computer_hardware/hard_drive/examine(user) . = ..() @@ -48,6 +63,9 @@ return 0 F.holder = src + if(holder && istype(F, /datum/computer_file/program)) + var/datum/computer_file/program/P = F + P.computer = holder stored_files.Add(F) recalculate_size() return 1 @@ -121,11 +139,6 @@ QDEL_LIST(stored_files) return ..() -/obj/item/computer_hardware/hard_drive/Initialize(mapload) - . = ..() - install_default_programs() - - /obj/item/computer_hardware/hard_drive/advanced name = "advanced hard disk drive" desc = "A hybrid HDD, for use in higher grade computers where balance between power efficiency and capacity is desired." @@ -160,12 +173,32 @@ w_class = WEIGHT_CLASS_TINY custom_price = 15 +// PDA Version of the SSD, contains all the programs that PDAs have by default, however with the variables of the SSD. +/obj/item/computer_hardware/hard_drive/small/pda/install_default_programs() + store_file(new /datum/computer_file/program/messenger(src)) + store_file(new /datum/computer_file/program/notepad(src)) + store_file(new/datum/computer_file/program/databank_uplink(src)) // Wiki Uplink, allows the user to access the Wiki from in-game! + ..() + +/obj/item/computer_hardware/hard_drive/small/pda/on_install(obj/item/modular_computer/install_into, mob/living/user = null) + . = ..() + if(!.) + return + // Set the default ringtone + for(var/datum/computer_file/program/messenger/messenger in stored_files) + messenger.ringer_status = install_into.init_ringer_on + messenger.ringtone = install_into.init_ringtone + // For borg integrated tablets. No downloader. -/obj/item/computer_hardware/hard_drive/small/integrated/install_default_programs() - store_file(new /datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar - store_file(new /datum/computer_file/program/filemanager(src)) // File manager, allows text editor functions and basic file manipulation. - store_file(new /datum/computer_file/program/borg_self_monitor(src)) +/obj/item/computer_hardware/hard_drive/small/pda/ai/install_default_programs() + var/datum/computer_file/program/messenger/messenger = new(src) + messenger.is_silicon = TRUE + store_file(messenger) +/obj/item/computer_hardware/hard_drive/small/pda/robot/install_default_programs() + store_file(new /datum/computer_file/program/borg_self_monitor(src)) + store_file(new /datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar + store_file(new /datum/computer_file/program/filemanager(src)) // File manager, allows text editor functions and basic file manipulation. // Syndicate variant - very slight better /obj/item/computer_hardware/hard_drive/small/syndicate @@ -178,13 +211,16 @@ /obj/item/computer_hardware/hard_drive/small/nukeops power_usage = 8 max_capacity = 70 + // Make sure this matches the syndicate shuttle's shield/door id in _maps/shuttles/infiltrator/infiltrator_basic.dmm + controllable_airlocks = list("smindicate") /obj/item/computer_hardware/hard_drive/small/nukeops/install_default_programs() - store_file(new/datum/computer_file/program/computerconfig(src)) - store_file(new/datum/computer_file/program/ntnetdownload/syndicate(src)) // Syndicate version; automatic access to syndicate apps and no NT apps - store_file(new/datum/computer_file/program/filemanager(src)) - store_file(new/datum/computer_file/program/radar/fission360(src)) //I am legitimately afraid if I don't do this, Ops players will think they just don't get a pinpointer anymore. - store_file(new/datum/computer_file/program/borg_monitor/syndicate(src)) + store_file(new /datum/computer_file/program/computerconfig(src)) + store_file(new /datum/computer_file/program/ntnetdownload/syndicate(src)) // Syndicate version; automatic access to syndicate apps and no NT apps + store_file(new /datum/computer_file/program/filemanager(src)) + store_file(new /datum/computer_file/program/radar/fission360(src)) //I am legitimately afraid if I don't do this, Ops players will think they just don't get a pinpointer anymore. + store_file(new /datum/computer_file/program/remote_airlock(src)) // Remote control for the shuttle door + store_file(new /datum/computer_file/program/borg_monitor/syndicate(src)) /obj/item/computer_hardware/hard_drive/micro name = "micro solid state drive" diff --git a/code/modules/modular_computers/hardware/identifier.dm b/code/modules/modular_computers/hardware/identifier.dm new file mode 100644 index 00000000000..bf7e1a045c7 --- /dev/null +++ b/code/modules/modular_computers/hardware/identifier.dm @@ -0,0 +1,13 @@ +/obj/item/computer_hardware/identifier + name = "identifier" + desc = "Used to automatically update the names of modular devices." + power_usage = 0 + w_class = WEIGHT_CLASS_TINY + device_type = MC_IDENTIFY + expansion_hw = FALSE + +/obj/item/computer_hardware/identifier/proc/UpdateDisplay() + var/name = holder.saved_identification + var/job = holder.saved_job + + holder.name = "PDA-[name] ([job])" diff --git a/code/modules/modular_computers/hardware/job_disk.dm b/code/modules/modular_computers/hardware/job_disk.dm new file mode 100644 index 00000000000..1a5e05392b6 --- /dev/null +++ b/code/modules/modular_computers/hardware/job_disk.dm @@ -0,0 +1,234 @@ +/obj/item/computer_hardware/hard_drive/role + name = "job data disk" + desc = "A disk meant to give a worker the needed programs to work." + power_usage = 0 + icon = 'icons/obj/pda.dmi' + icon_state = "cart" + w_class = WEIGHT_CLASS_TINY + critical = FALSE + max_capacity = 500 + device_type = MC_HDD_JOB + default_installs = FALSE + hotswap = TRUE + + var/disk_flags = 0 // bit flag for the programs + /// Enables "Send to All" Option. 1=1 min, 2=2mins, 2.5=2 min 30 seconds + var/spam_delay = 0 + +/obj/item/computer_hardware/hard_drive/role/on_inserted(mob/user) + ..() + if(holder) + playsound(holder, 'sound/machines/pda_button1.ogg', 50, TRUE) + +/obj/item/computer_hardware/hard_drive/role/on_remove(obj/item/modular_computer/remove_from, mob/user) + return + +/obj/item/computer_hardware/hard_drive/role/Initialize(mapload) + . = ..() + var/list/progs_to_store = list() + + if(disk_flags & DISK_POWER) + progs_to_store += new /datum/computer_file/program/power_monitor(src) + progs_to_store += new /datum/computer_file/program/supermatter_monitor(src) + + if(disk_flags & DISK_ATMOS) + progs_to_store += new /datum/computer_file/program/atmosscan(src) + + if(disk_flags & DISK_MANIFEST) + progs_to_store += new /datum/computer_file/program/crew_manifest(src) + + if(disk_flags & DISK_SEC) + progs_to_store += new /datum/computer_file/program/records/security(src) + + if(disk_flags & DISK_JANI) + progs_to_store += new /datum/computer_file/program/radar/custodial_locator(src) + + if((disk_flags & DISK_CHEM) || (disk_flags & DISK_MED) || (disk_flags & DISK_POWER) || (disk_flags & DISK_ATMOS)) + var/datum/computer_file/program/phys_scanner/scanner = new(src) + + if(disk_flags & DISK_CHEM) + scanner.available_modes += DISK_CHEM + + if(disk_flags & DISK_MED) + progs_to_store += new /datum/computer_file/program/records/medical(src) + scanner.available_modes += DISK_MED + + if(disk_flags & DISK_POWER) + scanner.available_modes += DISK_POWER + + if(disk_flags & DISK_ATMOS) + scanner.available_modes += DISK_ATMOS + + progs_to_store += scanner + + if(disk_flags & DISK_ROBOS) + var/datum/computer_file/program/robocontrol/robo = new(src) + progs_to_store += robo + + if(disk_flags & DISK_CARGO) + progs_to_store += new /datum/computer_file/program/bounty(src) + + if(disk_flags & DISK_SILO_LOG) + progs_to_store += new /datum/computer_file/program/log_viewer(src) + + if(disk_flags & DISK_SIGNAL) + progs_to_store += new /datum/computer_file/program/signaller(src) + + // TODO tablet-pda - the newscaster needs to be updated to TGUI for this to exist. + // Port: tg's #65038 - plus #65774 and #65799 possibly + // Then port #66035 for the program + //if(disk_flags & DISK_NEWS) + // progs_to_store += new /datum/computer_file/program/newscaster(src) + + if(disk_flags & DISK_BUDGET) + progs_to_store += new /datum/computer_file/program/budgetorders(src) + + if(disk_flags & DISK_STATUS) + progs_to_store += new /datum/computer_file/program/status(src) + + if(disk_flags & DISK_REMOTE_AIRLOCK) + progs_to_store += new /datum/computer_file/program/remote_airlock(src) + + if(disk_flags & DISK_HOP) + progs_to_store += new /datum/computer_file/program/card_mod(src) + progs_to_store += new /datum/computer_file/program/job_management(src) + + for (var/datum/computer_file/program/prog in progs_to_store) + prog.usage_flags = PROGRAM_ALL + prog.required_access = list() + prog.transfer_access = list() + store_file(prog) + +// Disk Definitions + +/obj/item/computer_hardware/hard_drive/role/engineering + name = "\improper Power-ON disk" + desc = "Engineers ignoring station power-draw since 2400." + icon_state = "cart-engie" + disk_flags = DISK_POWER + +/obj/item/computer_hardware/hard_drive/role/atmos + name = "\improper BreatheDeep disk" + icon_state = "cart-atmos" + disk_flags = DISK_ATMOS | DISK_ROBOS + +/obj/item/computer_hardware/hard_drive/role/medical + name = "\improper Med-U disk" + icon_state = "cart-med" + disk_flags = DISK_MED | DISK_ROBOS + +/obj/item/computer_hardware/hard_drive/role/chemistry + name = "\improper ChemWhiz disk" + icon_state = "cart-chem" + disk_flags = DISK_CHEM + +/obj/item/computer_hardware/hard_drive/role/brig_physician + name = "\improper R.O.B.U.S.T. MED-U disk" + icon_state = "cart-brigphys" + disk_flags = DISK_MANIFEST | DISK_MED | DISK_ROBOS + +/obj/item/computer_hardware/hard_drive/role/security + name = "\improper R.O.B.U.S.T. disk" + icon_state = "cart-sec" + disk_flags = DISK_SEC | DISK_MANIFEST | DISK_ROBOS + +/obj/item/computer_hardware/hard_drive/role/detective + name = "\improper D.E.T.E.C.T. disk" + icon_state = "cart-det" + disk_flags = DISK_MED | DISK_SEC | DISK_MANIFEST | DISK_ROBOS + +/obj/item/computer_hardware/hard_drive/role/janitor + name = "\improper CustodiPRO disk" + icon_state = "cart-jan" + desc = "The ultimate in clean-room design." + disk_flags = DISK_JANI | DISK_ROBOS + +/obj/item/computer_hardware/hard_drive/role/lawyer + name = "\improper P.R.O.V.E. disk" + icon_state = "cart-prove" + disk_flags = DISK_SEC + spam_delay = 2.5 + +/obj/item/computer_hardware/hard_drive/role/curator + name = "\improper Lib-Tweet disk" + icon_state = "cart-cur" + disk_flags = DISK_NEWS + spam_delay = 3.5 + +/obj/item/computer_hardware/hard_drive/role/roboticist + name = "\improper B.O.O.P. Remote Control disk" + icon_state = "cart-robo" + desc = "Packed with heavy duty quad-bot interlink!" + disk_flags = DISK_ROBOS + +/obj/item/computer_hardware/hard_drive/role/signal + name = "generic signaler disk" + icon_state = "cart-signal" + desc = "A data disk with an integrated radio signaler module." + disk_flags = DISK_SIGNAL + +/obj/item/computer_hardware/hard_drive/role/signal/toxins + name = "\improper Signal Ace 2 disk" + icon_state = "cart-tox" + desc = "Complete with integrated radio signaler!" + disk_flags = DISK_ATMOS | DISK_SIGNAL | DISK_CHEM + +/obj/item/computer_hardware/hard_drive/role/quartermaster + name = "space parts DELUXE disk" + icon_state = "cart-qm" + desc = "Perfect for the Quartermaster on the go!" + disk_flags = DISK_CARGO | DISK_SILO_LOG | DISK_ROBOS | DISK_BUDGET + +/obj/item/computer_hardware/hard_drive/role/cargo_technician + name = "space parts disk" + icon_state = "cart-cargo" + desc = "Perfect for the Cargo Tech on the go!" + disk_flags = DISK_CARGO | DISK_ROBOS | DISK_BUDGET + +/obj/item/computer_hardware/hard_drive/role/head + name = "\improper Easy-Record DELUXE disk" + icon_state = "cart-val" + disk_flags = DISK_MANIFEST | DISK_STATUS | DISK_BUDGET + +/obj/item/computer_hardware/hard_drive/role/hop + name = "\improper HumanResources9001 disk" + icon_state = "cart-hop" + disk_flags = DISK_MANIFEST | DISK_STATUS | DISK_JANI | DISK_SEC | DISK_NEWS | DISK_CARGO | DISK_SILO_LOG | DISK_ROBOS | DISK_BUDGET | DISK_HOP + +/obj/item/computer_hardware/hard_drive/role/hos + name = "\improper R.O.B.U.S.T. DELUXE disk" + icon_state = "cart-hos" + disk_flags = DISK_MANIFEST | DISK_STATUS | DISK_SEC | DISK_ROBOS | DISK_BUDGET + +/obj/item/computer_hardware/hard_drive/role/ce + name = "\improper Power-On DELUXE disk" + icon_state = "cart-ce" + disk_flags = DISK_POWER | DISK_ATMOS | DISK_MANIFEST | DISK_STATUS | DISK_ROBOS | DISK_BUDGET + +/obj/item/computer_hardware/hard_drive/role/cmo + name = "\improper Med-U DELUXE disk" + icon_state = "cart-cmo" + disk_flags = DISK_MANIFEST | DISK_STATUS | DISK_MED | DISK_CHEM | DISK_ROBOS | DISK_BUDGET + +/obj/item/computer_hardware/hard_drive/role/rd + name = "\improper Signal Ace DELUXE disk" + icon_state = "cart-rd" + disk_flags = DISK_ATMOS | DISK_MANIFEST | DISK_STATUS | DISK_CHEM | DISK_ROBOS | DISK_BUDGET | DISK_SIGNAL + +/obj/item/computer_hardware/hard_drive/role/captain + name = "\improper Value-PAK disk" + icon_state = "cart-cap" + desc = "Now with 350% more value!" + //Give the Captain...EVERYTHING! (except the remote airlock control) + disk_flags = ~(DISK_REMOTE_AIRLOCK) + spam_delay = 2 + +/obj/item/computer_hardware/hard_drive/role/vip //the only purpose of this disk is to allow the VIP to be annoying + name = "\improper TWIT disk" + icon_state = "cart-twit" + spam_delay = 1.5 + +/obj/item/computer_hardware/hard_drive/role/unlicensed //HoP can give you this + name = "\improper FACEBUCKS disk" + icon_state = "cart-signal" // might need a new sprite + spam_delay = 5 diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm index e2ee4cea31d..92af91f0e26 100644 --- a/code/modules/modular_computers/hardware/network_card.dm +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -92,10 +92,12 @@ if(!modularInterface.borgo) return FALSE //No borg found - if(modularInterface.borgo.lockcharge) - return FALSE //lockdown restricts borg networking + var/mob/living/silicon/robot/robo = modularInterface.borgo + if(istype(robo)) + if(robo.lockcharge) + return FALSE //lockdown restricts borg networking - if(!modularInterface.borgo.cell || modularInterface.borgo.cell.charge == 0) - return FALSE //borg cell dying restricts borg networking + if(!robo.cell || robo.cell.charge == 0) + return FALSE //borg cell dying restricts borg networking return ..() diff --git a/code/modules/modular_computers/hardware/virus_disk.dm b/code/modules/modular_computers/hardware/virus_disk.dm new file mode 100644 index 00000000000..e7f19f3c2fb --- /dev/null +++ b/code/modules/modular_computers/hardware/virus_disk.dm @@ -0,0 +1,105 @@ +/obj/item/computer_hardware/hard_drive/role/virus + name = "\improper generic virus disk" + icon_state = "cart-detomatrix" + var/charges = 5 + +/obj/item/computer_hardware/hard_drive/role/virus/proc/send_virus(obj/item/modular_computer/tablet/target, mob/living/user) + return + +/obj/item/computer_hardware/hard_drive/role/virus/clown + name = "\improper H.O.N.K. disk" + desc = "A data disk for portable microcomputers. It smells vaguely of bananas." + icon_state = "cart-clown" + +/obj/item/computer_hardware/hard_drive/role/virus/clown/send_virus(obj/item/modular_computer/tablet/target, mob/living/user) + if(charges <= 0) + to_chat(user, "ERROR: Out of charges.") + return + + if(target) + to_chat(user, "Success!") + charges-- + target.honk_amount = rand(15, 25) + else + to_chat(user, "ERROR: Could not find device.") + +/obj/item/computer_hardware/hard_drive/role/virus/mime + name = "\improper sound of silence disk" + +/obj/item/computer_hardware/hard_drive/role/virus/mime/send_virus(obj/item/modular_computer/tablet/target, mob/living/user) + if(charges <= 0) + to_chat(user, "ERROR: Out of charges.") + return + + if(target) + to_chat(user, "Success!") + charges-- + var/obj/item/computer_hardware/hard_drive/drive = target.all_components[MC_HDD] + for(var/datum/computer_file/program/messenger/app in drive.stored_files) + app.ringer_status = FALSE + app.ringtone = "" + else + to_chat(user, "ERROR: Could not find device.") + +/obj/item/computer_hardware/hard_drive/role/virus/syndicate + name = "\improper D.E.T.O.M.A.T.I.X. disk" + icon_state = "cart-detomatrix" + charges = 4 + +/obj/item/computer_hardware/hard_drive/role/virus/syndicate/send_virus(obj/item/modular_computer/tablet/target, mob/living/user) + if(charges <= 0) + to_chat(user, "ERROR: Out of charges.") + return + if(!target) + to_chat(user, "ERROR: Could not find device.") + return + charges-- + + var/difficulty = 0 + var/obj/item/computer_hardware/hard_drive/role/disk = target.all_components[MC_HDD_JOB] + + if(disk) + difficulty += bit_count(disk.disk_flags & (DISK_MED | DISK_SEC | DISK_POWER | DISK_MANIFEST)) + if(disk.disk_flags & DISK_MANIFEST) + difficulty++ //if disk has manifest access it has extra snowflake difficulty + else + difficulty += 2 + var/datum/component/uplink/hidden_uplink = target.GetComponent(/datum/component/uplink) + if(!target.detonatable || prob(difficulty * 15) || (hidden_uplink)) + to_chat(user, "An error flashes on your [src].") + else + log_bomber(user, "triggered a PDA explosion on", target, "[!is_special_character(user) ? "(TRIGGED BY NON-ANTAG)" : ""]") + to_chat(user, "Success!") + target.explode(target, user) + +/obj/item/computer_hardware/hard_drive/role/virus/syndicate/military + name = "\improper D.E.T.O.M.A.T.I.X. Deluxe disk" + disk_flags = DISK_REMOTE_AIRLOCK + // Make sure this matches the syndicate shuttle's shield/door id in _maps/shuttles/infiltrator/infiltrator_basic.dmm + controllable_airlocks = list("smindicate") + +/obj/item/computer_hardware/hard_drive/role/virus/frame + name = "\improper F.R.A.M.E. disk" + icon_state = "cart-prove" + var/telecrystals = 0 + +/obj/item/computer_hardware/hard_drive/role/virus/frame/send_virus(obj/item/modular_computer/tablet/target, mob/living/user) + if(charges <= 0) + to_chat(user, "ERROR: Out of charges.") + return + if(!target) + to_chat(user, "ERROR: Could not find device.") + return + charges-- + var/lock_code = "[random_code(3)] [pick(GLOB.phonetic_alphabet)]" + to_chat(user, "Success! The unlock code to the target is: [lock_code]") + var/datum/component/uplink/hidden_uplink = target.GetComponent(/datum/component/uplink) + if(!hidden_uplink) + hidden_uplink = target.AddComponent(/datum/component/uplink) + hidden_uplink.unlock_code = lock_code + else + hidden_uplink.hidden_crystals += hidden_uplink.telecrystals //Temporarially hide the PDA's crystals, so you can't steal telecrystals. + hidden_uplink.telecrystals = telecrystals + telecrystals = 0 + hidden_uplink.active = TRUE + diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm index 55a0a05fc83..daf35c4df4d 100644 --- a/code/modules/paperwork/contract.dm +++ b/code/modules/paperwork/contract.dm @@ -301,10 +301,12 @@ id.assignment = JOB_NAME_CAPTAIN id.update_label() if(worn) - if(istype(worn, /obj/item/pda)) - var/obj/item/pda/PDA = worn - PDA.id = id - id.forceMove(worn) + if(istype(worn, /obj/item/modular_computer/tablet/pda)) + var/obj/item/modular_computer/tablet/pda/PDA = worn + var/obj/item/computer_hardware/card_slot/card = PDA.all_components[MC_CARD] + card.try_eject(user, TRUE) // return their ID because we are nice + if(card) + card.try_insert(id, user) else if(istype(worn, /obj/item/storage/wallet)) var/obj/item/storage/wallet/W = worn W.front_id = id diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 00f416c9151..6ab88db9d56 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -65,14 +65,17 @@ * sheet, Makes it nice and easy for carbon and * the copyer machine */ -/obj/item/paper/proc/copy() - var/obj/item/paper/N = new(arglist(args)) +/obj/item/paper/proc/copy(loc) + var/obj/item/paper/N = new(loc) + N.forceMove(loc) N.info = info N.color = color N.update_icon_state() N.stamps = stamps - N.stamped = stamped.Copy() - N.form_fields = form_fields.Copy() + if(N.stamped) + N.stamped = stamped.Copy() + if(N.form_fields) + N.form_fields = form_fields.Copy() N.field_counter = field_counter copy_overlays(N, TRUE) return N @@ -168,7 +171,10 @@ return UI_INTERACTIVE return ..() - +/obj/item/paper/ui_state(mob/user) + if(istype(loc, /obj/item/modular_computer)) + return GLOB.reverse_contained_state + return ..() /obj/item/paper/can_interact(mob/user) if(in_contents_of(/obj/machinery/door/airlock)) @@ -363,7 +369,7 @@ update_icon() /obj/item/paper/ui_host(mob/user) - if(istype(loc, /obj/structure/noticeboard)) + if(istype(loc, /obj/structure/noticeboard) || istype(loc, /obj/item/modular_computer)) return loc return ..() diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm index f922afdc462..22030255ab7 100644 --- a/code/modules/photography/camera/camera.dm +++ b/code/modules/photography/camera/camera.dm @@ -163,7 +163,7 @@ return FALSE size_x = CLAMP(size_x, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT) size_y = CLAMP(size_y, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT) - var/list/desc = list("This is a photo of an area of [size_x+1] meters by [size_y+1] meters.") + var/list/desc = list("This is a photo of an area of [(2*size_x)+1] meters by [(2*size_y)+1] meters.") var/list/mobs_spotted = list() var/list/dead_spotted = list() var/ai_user = isAI(user) @@ -189,12 +189,15 @@ mobs += M if(locate(/obj/item/areaeditor/blueprints) in T) blueprints = TRUE - for(var/i in mobs) - var/mob/M = i + for(var/mob/M in mobs) + // No describing invisible stuff (except ghosts)! + if((M.invisibility >= SEE_INVISIBLE_LIVING || M.alpha <= 50) && !isobserver(M)) + continue mobs_spotted += M if(M.stat == DEAD) dead_spotted += M - desc += M.get_photo_description(src) + // |=, let's not spam "You can also see a ... thing? 8 times" + desc |= M.get_photo_description(src) var/psize_x = (size_x * 2 + 1) * world.icon_size var/psize_y = (size_y * 2 + 1) * world.icon_size diff --git a/code/modules/ruins/spaceruin_code/TheDerelict.dm b/code/modules/ruins/spaceruin_code/TheDerelict.dm index 58e257d5877..742613b9f4f 100644 --- a/code/modules/ruins/spaceruin_code/TheDerelict.dm +++ b/code/modules/ruins/spaceruin_code/TheDerelict.dm @@ -6,7 +6,7 @@ /obj/item/paper/fluff/ruins/thederelict/syndie_mission name = "Mission Objectives" - info = "The Syndicate have cunningly disguised a Syndicate Uplink as your PDA. Simply enter the code \"678 Bravo\" into the ringtone select to unlock its hidden features.

    Objective #1. Kill the God damn AI in a fire blast that it rocks the station. Success!
    Objective #2. Escape alive. Failed." + info = "The Syndicate have cunningly disguised a Syndicate Uplink as your PDA. Simply enter the code \"678 Bravo\" into the ring tone selection to unlock its hidden features.

    Objective #1. Kill the God damn AI in a fire blast that it rocks the station. Success!
    Objective #2. Escape alive. Failed." /obj/item/paper/fluff/ruins/thederelict/nukie_objectives name = "Objectives of a Nuclear Operative" diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm index 06fed366a7d..c49e6cc64ce 100644 --- a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm +++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm @@ -131,7 +131,7 @@ glasses = /obj/item/clothing/glasses/chameleon belt = /obj/item/storage/belt/chameleon l_pocket = /obj/item/stamp/chameleon - r_pocket = /obj/item/pda/chameleon + r_pocket = /obj/item/modular_computer/tablet/pda/chameleon id = /obj/item/card/id/syndicate/anyone neck = /obj/item/clothing/neck/chameleon head = /obj/item/clothing/head/chameleon diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm index a9204ff81f0..de86fe6ff67 100644 --- a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm +++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm @@ -112,7 +112,7 @@ belt = /obj/item/gun/energy/e_gun l_pocket = /obj/item/pen back = /obj/item/storage/backpack/satchel - r_pocket = /obj/item/pda/heads + r_pocket = /obj/item/modular_computer/tablet/pda/heads l_hand = /obj/item/clothing/head/helmet/space/fragile id = /obj/item/card/id/away/old diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/ruin_generator/mapping.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/ruin_generator/mapping.dm index 135cb00bc99..5edca9608a6 100644 --- a/code/modules/shuttle/super_cruise/orbital_poi_generator/ruin_generator/mapping.dm +++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/ruin_generator/mapping.dm @@ -96,7 +96,7 @@ /obj/item/wrench = 6, /obj/item/assembly/signaler = 5, /obj/item/transfer_valve = 6, - /obj/item/cartridge/rd = 3, + /obj/item/computer_hardware/hard_drive/role/rd = 3, /obj/item/radio = 5, /obj/item/camera = 4, /obj/item/encryptionkey/headset_sci = 3, @@ -107,8 +107,8 @@ /obj/item/hand_tele = 1, /obj/item/inducer/sci = 3, /obj/item/megaphone = 1, - /obj/item/pda/roboticist = 3, - /obj/item/pda/toxins = 3, + /obj/item/modular_computer/tablet/pda/roboticist = 3, + /obj/item/modular_computer/tablet/pda/science = 3, /obj/item/pinpointer/crew = 4, /obj/item/reactive_armour_shell = 1, /obj/item/anomaly_neutralizer = 1, diff --git a/code/modules/tgui/states/reverse_contained.dm b/code/modules/tgui/states/reverse_contained.dm new file mode 100644 index 00000000000..a6d1034a789 --- /dev/null +++ b/code/modules/tgui/states/reverse_contained.dm @@ -0,0 +1,18 @@ +/*! + * Not copyrighted, but magatsuchi made it. + * + */ + +/** + * tgui state: reverse_contained_state + * + * + * Checks if src_object is inside of user. + */ + +GLOBAL_DATUM_INIT(reverse_contained_state, /datum/ui_state/reverse_contained_state, new) + +/datum/ui_state/reverse_contained_state/can_use_topic(atom/src_object, mob/user) + if(!user.contains(src_object)) + return UI_CLOSE + return user.shared_ui_interaction(src_object) diff --git a/code/modules/tgui/tgui_input_emoji.dm b/code/modules/tgui/tgui_input_emoji.dm new file mode 100644 index 00000000000..9f18d310594 --- /dev/null +++ b/code/modules/tgui/tgui_input_emoji.dm @@ -0,0 +1,147 @@ +/** + * Creates a TGUI window with a emoji input. Returns the user's response. + * + * This proc should be used to create windows for emoji entry that the caller will wait for a response from. + * If tgui fancy chat is turned off: Will return a normal input. If max_length is specified, will return + * stripped_multiline_input. + * + * Arguments: + * * user - The user to show the textbox to. + * * title - The title of the textbox modal, shown on the top of the TGUI window. + */ +/proc/tgui_input_emoji(mob/user, title = "Emoji Input") + if (!user) + user = usr + if (!istype(user)) + if (istype(user, /client)) + var/client/client = user + user = client.mob + else + return + var/datum/tgui_input_emoji/textbox = new(user, title) + textbox.ui_interact(user) + textbox.wait() + if (textbox) + . = textbox.entry + qdel(textbox) + +/** + * Creates an asynchronous TGUI emoji input window with an associated callback. + * + * This proc should be used to create textboxes that invoke a callback with the user's entry. + * Arguments: + * * user - The user to show the textbox to. + * * title - The title of the textbox modal, shown on the top of the TGUI window. + * * callback - The callback to be invoked when a choice is made. + */ +/proc/tgui_input_emoji_async(mob/user, title = "Emoji Input", datum/callback/callback) + if (!user) + user = usr + if (!istype(user)) + if (istype(user, /client)) + var/client/client = user + user = client.mob + else + return + var/datum/tgui_input_emoji/async/textbox = new(user, title, callback) + textbox.ui_interact(user) + +/** + * # tgui_input_emoji + * + * Datum used for instantiating and using a TGUI-controlled textbox that prompts the user with + * a message and has an input for emoji entry. + */ +/datum/tgui_input_emoji + /// Boolean field describing if the tgui_input_emoji was closed by the user. + var/closed + /// The entry that the user has return_typed in. + var/entry + /// The title of the TGUI window + var/title + + +/datum/tgui_input_emoji/New(mob/user, title) + src.title = title + +/datum/tgui_input_emoji/Destroy(force, ...) + SStgui.close_uis(src) + . = ..() + +/** + * Waits for a user's response to the tgui_input_emoji's prompt before returning. Returns early if + * the window was closed by the user. + */ +/datum/tgui_input_emoji/proc/wait() + while (!entry && !closed && !QDELETED(src)) + stoplag(1) + +/datum/tgui_input_emoji/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "EmojiInputModal") + ui.open() + +/datum/tgui_input_emoji/ui_close(mob/user) + . = ..() + closed = TRUE + +/datum/tgui_input_emoji/ui_state(mob/user) + return GLOB.always_state + +/datum/tgui_input_emoji/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/emoji) + ) + +/datum/tgui_input_emoji/ui_static_data(mob/user) + return list( + "all_emojis" = icon_states(icon('icons/emoji.dmi')) + ) + +/datum/tgui_input_emoji/ui_data(mob/user) + return list( + "title" = title, + ) + +/datum/tgui_input_emoji/ui_act(action, list/params) + . = ..() + if (.) + return + switch(action) + if("submit") + set_entry(params["entry"]) + SStgui.close_uis(src) + return TRUE + if("cancel") + set_entry(null) + SStgui.close_uis(src) + return TRUE + +/datum/tgui_input_emoji/proc/set_entry(entry) + src.entry = entry + +/** + * # async tgui_input_emoji + * + * An asynchronous version of tgui_input_emoji to be used with callbacks instead of waiting on user responses. + */ +/datum/tgui_input_emoji/async + /// The callback to be invoked by the tgui_input_emoji upon having a choice made. + var/datum/callback/callback + +/datum/tgui_input_emoji/async/New(mob/user, title, callback) + ..(user, title) + src.callback = callback + +/datum/tgui_input_emoji/async/Destroy(force, ...) + QDEL_NULL(callback) + . = ..() + +/datum/tgui_input_emoji/async/set_entry(entry) + . = ..() + if(!isnull(src.entry)) + callback?.InvokeAsync(src.entry) + +/datum/tgui_input_emoji/async/wait() + return diff --git a/code/modules/tgui/tgui_input_pda_message.dm b/code/modules/tgui/tgui_input_pda_message.dm new file mode 100644 index 00000000000..a0eee44dd0e --- /dev/null +++ b/code/modules/tgui/tgui_input_pda_message.dm @@ -0,0 +1,196 @@ +/proc/tgui_send_admin_pda(mob/user, obj/signal_source, obj/machinery/telecomms/message_server/server, theme, allow_send_all = FALSE) + if (!user) + user = usr + if (!istype(user)) + if (istype(user, /client)) + var/client/client = user + user = client.mob + else + return + var/datum/tgui_input_pda_message/pda_input = new(user) + pda_input.src_console = signal_source + pda_input.can_send_all = allow_send_all + pda_input.theme = theme + pda_input.ui_interact(user) + pda_input.wait() + if (!pda_input) + return + if(!pda_input.submit || (!pda_input.send_all && (!istype(pda_input.target) || QDELETED(pda_input.target)))) + qdel(pda_input) + return + if(istype(signal_source) && usr.default_can_use_topic(signal_source) != UI_INTERACTIVE) + to_chat(usr, "Out of range! Message not sent!") + qdel(pda_input) + return + // If we are impersonating someone, we should match their computer in the (Reply) href + var/ref + for(var/obj/item/modular_computer/messenger in GetViewableDevices()) + if(messenger.saved_identification == pda_input.name && messenger.saved_job == pda_input.job && (pda_input.send_all || messenger != pda_input.target)) + ref = REF(messenger) + break + var/datum/signal/subspace/messaging/tablet_msg/signal = new(signal_source ? signal_source : server, list( + "name" = "[pda_input.name]", + "job" = "[pda_input.job]", + "message" = pda_input.text, + "emojis" = TRUE, + "photo" = pda_input.current_image, + "ref" = ref, + "targets" = pda_input.send_all ? GetViewableDevices() : list(pda_input.target), + )) + if(istype(server) && !QDELETED(server)) + server.receive_information(signal, null) + else + signal.send_to_receivers() + var/turf/source_turf = signal_source ? get_turf(signal_source) : null + usr.log_message("(PDA: [pda_input.name] | [usr.real_name]) sent \"[pda_input.text]\"[signal["photo"] ? " (Photo Attached)" : ""] to [signal.format_target()] via [signal_source ? "[signal_source] at [AREACOORD(source_turf)]" : "Admin UI"]", LOG_PDA) + message_admins("[key_name_admin(usr)][ADMIN_FLW(usr)] sent PDA message: \"[pda_input.text]\"[signal["photo"] ? " (Photo Attached)" : ""] to [signal.format_target()] via [signal_source ? "[signal_source] at [ADMIN_VERBOSEJMP(source_turf)]" : "Admin UI"]") + qdel(pda_input) + +/datum/tgui_input_pda_message + var/closed + var/submit + var/name = "System Administrator" + var/job = "Admin" + var/text = "" + var/theme + var/datum/picture/current_image + var/obj/item/modular_computer/target + var/can_send_all = FALSE + var/send_all = FALSE + var/obj/src_console + var/static/datum/ui_state/tgui_input_pda_state/tgui_input_pda_state + +/datum/tgui_input_pda_message/New() + +/datum/tgui_input_pda_message/Destroy(force, ...) + SStgui.close_uis(src) + . = ..() + +/datum/tgui_input_pda_message/proc/wait() + UNTIL(submit || closed || QDELETED(src)) + +/datum/tgui_input_pda_message/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PDAInputModal") + ui.open() + +/datum/tgui_input_pda_message/ui_close(mob/user) + . = ..() + closed = TRUE + +/datum/tgui_input_pda_message/ui_state(mob/user) + if(!tgui_input_pda_state) + tgui_input_pda_state = new() + return tgui_input_pda_state + +/datum/ui_state/tgui_input_pda_state/can_use_topic(src_object, mob/user) + var/datum/tgui_input_pda_message/src_object_pda = src_object + if(!istype(src_object_pda)) + return UI_CLOSE + return src_object_pda.src_console ? user.default_can_use_topic(src_object_pda.src_console) : UI_INTERACTIVE + +/datum/tgui_input_pda_message/ui_data(mob/user) + . = list() + .["name"] = name + .["job"] = job + .["text"] = text + .["image"] = istype(current_image) + if(theme) + .["theme"] = theme + if(istype(target)) + .["target"] = "[target.saved_identification] ([target.saved_job])" + .["everyone"] = send_all + +/datum/tgui_input_pda_message/ui_act(action, list/params) + . = ..() + if (.) + return + switch(action) + if("submit") + if(!send_all && !istype(target)) + alert(usr, "Please select a recipient!", "Send Failure", "OK") + return + if(!length(name) || !length(job) || !length(text)) + alert(usr, "Please enter text into all fields!", "Send Failure", "OK") + return + submit = TRUE + closed = TRUE + SStgui.close_uis(src) + return TRUE + if("cancel") + // don't send the message + target = null + send_all = FALSE + closed = TRUE + SStgui.close_uis(src) + return TRUE + if("select") + var/list/devices = list() + for(var/obj/item/modular_computer/messenger in GetViewableDevices(TRUE)) + if(!messenger.saved_identification || !messenger.saved_job) + continue + var/key_base = "[messenger.saved_identification] ([messenger.saved_job])" + var/key = key_base + var/number = 1 + while(key in devices) + key = key_base + " ([number])" + number++ + devices[key] = messenger + if(can_send_all) + devices["Everyone"] = "Everyone" + var/choice = input(usr, "Select PDA to send message to.", "Select PDA.", null) as null|anything in devices + if(istype(target)) + UnregisterSignal(target, COMSIG_PARENT_QDELETING) + target = null + if(can_send_all && choice == "Everyone") + send_all = TRUE + else if(choice in devices) + send_all = FALSE + target = devices[choice] + RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(target_deleting)) + else + target = null + send_all = FALSE + return TRUE + if("set_message") + text = trim(params["value"], MAX_MESSAGE_LEN) + return TRUE + if("set_name") + name = trim(params["value"], MAX_NAME_LEN) + return TRUE + if("set_job") + job = trim(params["value"], MAX_NAME_LEN) + return TRUE + if("photo") + if(current_image) + current_image = null + return TRUE + if(issilicon(usr)) + var/mob/living/silicon/S = usr + var/datum/picture/selection = S.aicamera?.selectpicture(usr) + current_image = istype(selection) ? selection : null + else + var/obj/item/photo/photo = usr?.is_holding_item_of_type(/obj/item/photo) + current_image = istype(photo) ? photo.picture : null + if(current_image) + if(src_console) + src_console.balloon_alert(usr, "photo selected.") + playsound(src_console, 'sound/machines/terminal_success.ogg', 15, TRUE) + else + usr.balloon_alert(usr, "photo selected.") + SEND_SOUND(usr, 'sound/machines/terminal_success.ogg') + else + if(src_console) + src_console.balloon_alert(usr, "no photo identified.") + else + usr.balloon_alert(usr, "no photo identified.") + return TRUE + if("send_all") + if(!check_rights(R_ADMIN)) + return TRUE + send_all = TRUE + +/datum/tgui_input_pda_message/proc/target_deleting() + target = null + ui_update() diff --git a/code/modules/tgui/tgui_select_picture.dm b/code/modules/tgui/tgui_select_picture.dm new file mode 100644 index 00000000000..325d673817e --- /dev/null +++ b/code/modules/tgui/tgui_select_picture.dm @@ -0,0 +1,88 @@ +/proc/tgui_select_picture(mob/user, list/datum/picture/choices, title = "Select Photo") + if (!user) + user = usr + if (!istype(user)) + if (istype(user, /client)) + var/client/client = user + user = client.mob + else + return + if(!choices) + return + var/datum/tgui_select_picture/textbox = new(user, choices, title) + textbox.ui_interact(user) + textbox.wait() + if (textbox) + . = textbox.entry + qdel(textbox) + +/datum/tgui_select_picture + /// Boolean field describing if the tgui_select_picture was closed by the user. + var/closed + /// The entry that the user has selected + var/datum/picture/entry + /// The title of the TGUI window + var/title + /// The list of picture datums to select from + var/list/datum/picture/choices = list() + +/datum/tgui_select_picture/New(mob/user, choices, title) + src.title = title + src.choices = choices + +/datum/tgui_select_picture/Destroy(force, ...) + SStgui.close_uis(src) + . = ..() + +/** + * Waits for a user's response to the tgui_select_picture's prompt before returning. Returns early if + * the window was closed by the user. + */ +/datum/tgui_select_picture/proc/wait() + while (!entry && !closed && !QDELETED(src)) + stoplag(1) + +/datum/tgui_select_picture/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PictureSelectModal") + ui.open() + +/datum/tgui_select_picture/ui_close(mob/user) + . = ..() + closed = TRUE + +/datum/tgui_select_picture/ui_state(mob/user) + return GLOB.always_state + +/datum/tgui_select_picture/ui_data(mob/user) + var/list/pictures = list() + for(var/datum/picture/picture in choices) + var/icon/img = picture.picture_image + var/picture_path = "tgui_select_picture_[picture.id]_[rand(0, 99999)].png" + usr << browse_rsc(img, picture_path) + // DM is a great language, am I right? + // Adding a list to a list un-lists it, so we need a double list to make an "object" list. + pictures += list(list( + "ref" = "[REF(picture)]", + "path" = picture_path, + "name" = picture.picture_name, + "desc" = picture.picture_desc, + )) + return list( + "title" = title, + "pictures" = pictures + ) + +/datum/tgui_select_picture/ui_act(action, list/params) + . = ..() + if (.) + return + switch(action) + if("submit") + var/datum/picture/choice = locate(params["entry"]) in choices + if(!istype(choice)) + return + entry = choice + SStgui.close_uis(src) + return TRUE diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index d1d25a43340..edbfbe4165b 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1126,11 +1126,11 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) purchasable_from = UPLINK_CLOWN_OPS /datum/uplink_item/explosives/detomatix - name = "Detomatix PDA Cartridge" - desc = "When inserted into a personal digital assistant, this cartridge gives you four opportunities to \ + name = "Detomatix PDA Disk" + desc = "When inserted into a personal digital assistant, this disk gives you four opportunities to \ detonate PDAs of crewmembers who have their message feature enabled. \ The concussive effect from the explosion will knock the recipient out for a short period, and deafen them for longer." - item = /obj/item/cartridge/virus/syndicate + item = /obj/item/computer_hardware/hard_drive/role/virus/syndicate cost = 6 restricted = TRUE @@ -1653,12 +1653,12 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 8 /datum/uplink_item/device_tools/frame - name = "F.R.A.M.E. PDA Cartridge" - desc = "When inserted into a personal digital assistant, this cartridge gives you five PDA viruses which \ + name = "F.R.A.M.E. PDA Disk" + desc = "When inserted into a personal digital assistant, this disk gives you five PDA viruses which \ when used cause the targeted PDA to become a new uplink with zero TCs, and immediately become unlocked. \ You will receive the unlock code upon activating the virus, and the new uplink may be charged with \ telecrystals normally." - item = /obj/item/cartridge/virus/frame + item = /obj/item/computer_hardware/hard_drive/role/virus/frame cost = 4 restricted = TRUE diff --git a/code/modules/vehicles/pimpin_ride.dm b/code/modules/vehicles/pimpin_ride.dm index 331fe1dfaec..4ee6b5bb753 100644 --- a/code/modules/vehicles/pimpin_ride.dm +++ b/code/modules/vehicles/pimpin_ride.dm @@ -12,11 +12,12 @@ update_icon() var/datum/component/riding/D = LoadComponent(/datum/component/riding) D.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 7), TEXT_EAST = list(-12, 7), TEXT_WEST = list( 12, 7))) - + GLOB.janitor_devices += src if(floorbuffer) AddElement(/datum/element/cleaning) /obj/vehicle/ridden/janicart/Destroy() + GLOB.janitor_devices -= src if(mybag) qdel(mybag) mybag = null diff --git a/code/modules/vending/assist.dm b/code/modules/vending/assist.dm index 5b78274f253..08d5eba1ce9 100644 --- a/code/modules/vending/assist.dm +++ b/code/modules/vending/assist.dm @@ -3,7 +3,7 @@ /obj/item/assembly/igniter = 3, /obj/item/assembly/signaler = 4, /obj/item/wirecutters = 1, - /obj/item/cartridge/signal = 4) + /obj/item/computer_hardware/hard_drive/role/signal = 4) contraband = list(/obj/item/assembly/timer = 2, /obj/item/assembly/voice = 2, /obj/item/assembly/health = 2) diff --git a/code/modules/vending/cartridge.dm b/code/modules/vending/cartridge.dm deleted file mode 100644 index 44790b54b2c..00000000000 --- a/code/modules/vending/cartridge.dm +++ /dev/null @@ -1,26 +0,0 @@ -//This one's from bay12 -/obj/machinery/vending/cart - name = "\improper PTech" - desc = "Cartridges for PDAs." - product_slogans = "Carts to go!" - icon_state = "cart" - icon_deny = "cart-deny" - light_color = LIGHT_COLOR_WHITE - products = list(/obj/item/cartridge/medical = 10, - /obj/item/cartridge/engineering = 10, - /obj/item/cartridge/security = 10, - /obj/item/cartridge/janitor = 10, - /obj/item/cartridge/signal/toxins = 10, - /obj/item/pda/heads = 10, - /obj/item/cartridge/captain = 3, - /obj/item/cartridge/quartermaster = 10) - premium = list(/obj/item/cartridge/annoyance/lesser = 3) - refill_canister = /obj/item/vending_refill/cart - default_price = 50 - extra_price = 100 - payment_department = ACCOUNT_SRV - -/obj/item/vending_refill/cart - machine_name = "PTech" - icon_state = "refill_smoke" - diff --git a/code/modules/vending/job_disk.dm b/code/modules/vending/job_disk.dm new file mode 100644 index 00000000000..881897498e0 --- /dev/null +++ b/code/modules/vending/job_disk.dm @@ -0,0 +1,36 @@ +//This one's from bay12 +/obj/machinery/vending/job_disk + name = "\improper PTech" + desc = "Job disks for PDAs." + product_slogans = "Disks to go!" + icon_state = "cart" + icon_deny = "cart-deny" + light_color = LIGHT_COLOR_WHITE + products = list(/obj/item/modular_computer/tablet/pda = 15, + /obj/item/computer_hardware/hard_drive/role/medical = 5, + /obj/item/computer_hardware/hard_drive/role/chemistry = 5, + /obj/item/computer_hardware/hard_drive/role/brig_physician = 3, + /obj/item/computer_hardware/hard_drive/role/security = 3, + /obj/item/computer_hardware/hard_drive/role/detective = 3, + /obj/item/computer_hardware/hard_drive/role/engineering = 5, + /obj/item/computer_hardware/hard_drive/role/atmos = 5, + /obj/item/computer_hardware/hard_drive/role/signal/toxins = 5, + /obj/item/computer_hardware/hard_drive/role/roboticist = 3, + /obj/item/computer_hardware/hard_drive/role/lawyer = 3, + /obj/item/computer_hardware/hard_drive/role/curator = 3, + /obj/item/computer_hardware/hard_drive/role/janitor = 5, + /obj/item/computer_hardware/hard_drive/role/quartermaster = 3, + /obj/item/computer_hardware/hard_drive/role/cargo_technician = 5, + /obj/item/computer_hardware/hard_drive/role/unlicensed = 5, + /obj/item/computer_hardware/hard_drive/role/head = 5) + premium = list(/obj/item/computer_hardware/hard_drive/role/captain = 3) + contraband = list(/obj/item/computer_hardware/hard_drive/role/virus/clown = 2, + /obj/item/computer_hardware/hard_drive/role/virus/mime = 2) + refill_canister = /obj/item/vending_refill/job_disk + default_price = 100 + extra_price = 300 + payment_department = ACCOUNT_SRV + +/obj/item/vending_refill/job_disk + machine_name = "PTech" + icon_state = "refill_smoke" diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm index 6a7b2e32700..bc54ad05955 100644 --- a/code/modules/vending/wardrobes.dm +++ b/code/modules/vending/wardrobes.dm @@ -341,7 +341,7 @@ //obj/item/clothing/under/rank/civilian/janitor/skirt = 2, //NSV13 no skirts /obj/item/clothing/under/plasmaman/janitor = 2, /obj/item/clothing/head/helmet/space/plasmaman/janitor = 2, - /obj/item/cartridge/janitor = 2, + /obj/item/computer_hardware/hard_drive/role/janitor = 2, /obj/item/clothing/gloves/color/black = 2, /obj/item/clothing/head/soft/purple = 2, /obj/item/pushbroom = 2, diff --git a/icons/obj/pda.dmi b/icons/obj/pda.dmi index 4f44e64fe32da05fc554e0ed2f6c2d2bc7ec18ef..4efa6f002330ed8edfabe8642ce0244962012fe7 100644 GIT binary patch literal 21701 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 MQ`T0(C|Cvk2LXV=J^%m! literal 20619 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 ZkO0Wx>%>+*0Z?QR(9<@$S$xAT{GY>)rK11< diff --git a/nsv13.dme b/nsv13.dme index 719942812f8..0208c2824c2 100644 --- a/nsv13.dme +++ b/nsv13.dme @@ -56,6 +56,7 @@ #include "code\__DEFINES\cooldowns.dm" #include "code\__DEFINES\cult.dm" #include "code\__DEFINES\departments.dm" +#include "code\__DEFINES\devices.dm" #include "code\__DEFINES\directional.dm" #include "code\__DEFINES\diseases.dm" #include "code\__DEFINES\DNA.dm" @@ -1223,11 +1224,6 @@ #include "code\game\objects\items\devices\taperecorder.dm" #include "code\game\objects\items\devices\traitordevices.dm" #include "code\game\objects\items\devices\transfer_valve.dm" -#include "code\game\objects\items\devices\PDA\cart.dm" -#include "code\game\objects\items\devices\PDA\PDA.dm" -#include "code\game\objects\items\devices\PDA\PDA_types.dm" -#include "code\game\objects\items\devices\PDA\radio.dm" -#include "code\game\objects\items\devices\PDA\virus_cart.dm" #include "code\game\objects\items\devices\radio\electropack.dm" #include "code\game\objects\items\devices\radio\encryptionkey.dm" #include "code\game\objects\items\devices\radio\headset.dm" @@ -2665,6 +2661,7 @@ #include "code\modules\mob\living\silicon\pai\pai_shell.dm" #include "code\modules\mob\living\silicon\pai\personality.dm" #include "code\modules\mob\living\silicon\pai\say.dm" +#include "code\modules\mob\living\silicon\pai\signaler.dm" #include "code\modules\mob\living\silicon\pai\software.dm" #include "code\modules\mob\living\silicon\robot\death.dm" #include "code\modules\mob\living\silicon\robot\emote.dm" @@ -2815,6 +2812,7 @@ #include "code\modules\modular_computers\computers\item\laptop.dm" #include "code\modules\modular_computers\computers\item\laptop_presets.dm" #include "code\modules\modular_computers\computers\item\processor.dm" +#include "code\modules\modular_computers\computers\item\role_tablet_presets.dm" #include "code\modules\modular_computers\computers\item\tablet.dm" #include "code\modules\modular_computers\computers\item\tablet_presets.dm" #include "code\modules\modular_computers\computers\machinery\console_presets.dm" @@ -2837,18 +2835,26 @@ #include "code\modules\modular_computers\file_system\programs\crewmanifest.dm" #include "code\modules\modular_computers\file_system\programs\file_browser.dm" #include "code\modules\modular_computers\file_system\programs\jobmanagement.dm" +#include "code\modules\modular_computers\file_system\programs\log_viewer.dm" +#include "code\modules\modular_computers\file_system\programs\notepad.dm" #include "code\modules\modular_computers\file_system\programs\ntdownloader.dm" +#include "code\modules\modular_computers\file_system\programs\ntmessenger.dm" #include "code\modules\modular_computers\file_system\programs\ntmonitor.dm" #include "code\modules\modular_computers\file_system\programs\ntnrc_client.dm" +#include "code\modules\modular_computers\file_system\programs\phys_scanner.dm" #include "code\modules\modular_computers\file_system\programs\portrait_printer.dm" #include "code\modules\modular_computers\file_system\programs\powermonitor.dm" #include "code\modules\modular_computers\file_system\programs\radar.dm" +#include "code\modules\modular_computers\file_system\programs\records.dm" +#include "code\modules\modular_computers\file_system\programs\remote_airlock.dm" #include "code\modules\modular_computers\file_system\programs\robocontrol.dm" #include "code\modules\modular_computers\file_system\programs\secureye.dm" #include "code\modules\modular_computers\file_system\programs\signaller.dm" #include "code\modules\modular_computers\file_system\programs\sm_monitor.dm" +#include "code\modules\modular_computers\file_system\programs\statusdisplay.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\contract_uplink.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm" +#include "code\modules\modular_computers\file_system\programs\antagonist\emag.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\revelation.dm" #include "code\modules\modular_computers\hardware\_hardware.dm" #include "code\modules\modular_computers\hardware\ai_slot.dm" @@ -2856,11 +2862,14 @@ #include "code\modules\modular_computers\hardware\card_slot.dm" #include "code\modules\modular_computers\hardware\CPU.dm" #include "code\modules\modular_computers\hardware\hard_drive.dm" +#include "code\modules\modular_computers\hardware\identifier.dm" +#include "code\modules\modular_computers\hardware\job_disk.dm" #include "code\modules\modular_computers\hardware\modules.dm" #include "code\modules\modular_computers\hardware\network_card.dm" #include "code\modules\modular_computers\hardware\portable_disk.dm" #include "code\modules\modular_computers\hardware\printer.dm" #include "code\modules\modular_computers\hardware\recharger.dm" +#include "code\modules\modular_computers\hardware\virus_disk.dm" #include "code\modules\modular_computers\NTNet\NTNRC\conversation.dm" #include "code\modules\ninja\__ninjaDefines.dm" #include "code\modules\ninja\energy_katana.dm" @@ -3480,6 +3489,9 @@ #include "code\modules\tgui\tgui_input_list.dm" #include "code\modules\tgui\tgui_input_number.dm" #include "code\modules\tgui\tgui_input_text.dm" +#include "code\modules\tgui\tgui_input_emoji.dm" +#include "code\modules\tgui\tgui_input_pda_message.dm" +#include "code\modules\tgui\tgui_select_picture.dm" #include "code\modules\tgui\tgui_window.dm" #include "code\modules\tgui\states\admin.dm" #include "code\modules\tgui\states\always.dm" @@ -3499,6 +3511,7 @@ #include "code\modules\tgui\states\notcontained.dm" #include "code\modules\tgui\states\observer.dm" #include "code\modules\tgui\states\physical.dm" +#include "code\modules\tgui\states\reverse_contained.dm" #include "code\modules\tgui\states\self.dm" #include "code\modules\tgui\states\zlevel.dm" #include "code\modules\tgui_panel\audio.dm" @@ -3534,7 +3547,6 @@ #include "code\modules\vending\assist.dm" #include "code\modules\vending\autodrobe.dm" #include "code\modules\vending\boozeomat.dm" -#include "code\modules\vending\cartridge.dm" #include "code\modules\vending\cigarette.dm" #include "code\modules\vending\clothesmate.dm" #include "code\modules\vending\coffee.dm" @@ -3543,6 +3555,7 @@ #include "code\modules\vending\engineering.dm" #include "code\modules\vending\engivend.dm" #include "code\modules\vending\games.dm" +#include "code\modules\vending\job_disk.dm" #include "code\modules\vending\liberation.dm" #include "code\modules\vending\liberation_toy.dm" #include "code\modules\vending\magivend.dm" diff --git a/nsv13/code/modules/cargo/objective_cargo.dm b/nsv13/code/modules/cargo/objective_cargo.dm index 2106893bad4..231b2242a96 100644 --- a/nsv13/code/modules/cargo/objective_cargo.dm +++ b/nsv13/code/modules/cargo/objective_cargo.dm @@ -15,7 +15,7 @@ /obj/structure/closet/crate/large/freight_objective/New() . = ..() - RegisterSignal( src, COMSIG_FREIGHT_TAMPERED, .proc/poll_for_ghost_sentience ) + RegisterSignal( src, COMSIG_FREIGHT_TAMPERED, PROC_REF(poll_for_ghost_sentience) ) /obj/structure/closet/crate/large/freight_objective/attackby(obj/item/W, mob/user, params) if(W.tool_behaviour == TOOL_CROWBAR) diff --git a/nsv13/code/modules/coffee/machine/coffeemaker.dm b/nsv13/code/modules/coffee/machine/coffeemaker.dm index 091c00b6b0e..bf8d4b65009 100644 --- a/nsv13/code/modules/coffee/machine/coffeemaker.dm +++ b/nsv13/code/modules/coffee/machine/coffeemaker.dm @@ -410,7 +410,7 @@ playsound(src, 'nsv13/sound/machines/coffeemaker_brew.ogg', 20, vary = TRUE) toggle_steam() use_power(active_power_usage * time * 0.1) // .1 needed here to convert time (in deciseconds) to seconds such that watts * seconds = joules - addtimer(CALLBACK(src, .proc/stop_operating), time / speed) + addtimer(CALLBACK(src, PROC_REF(stop_operating)), time / speed) /obj/machinery/coffeemaker/proc/stop_operating() brewing = FALSE diff --git a/nsv13/code/modules/jobs/job_types/fighter_technician.dm b/nsv13/code/modules/jobs/job_types/fighter_technician.dm index 419a0c9267e..6e7a7d56eb8 100644 --- a/nsv13/code/modules/jobs/job_types/fighter_technician.dm +++ b/nsv13/code/modules/jobs/job_types/fighter_technician.dm @@ -33,7 +33,7 @@ head = /obj/item/clothing/head/helmet/decktech gloves = /obj/item/clothing/gloves/color/brown id = /obj/item/card/id/deck_technician - l_pocket = /obj/item/pda + l_pocket = /obj/item/modular_computer/tablet/pda/munition backpack = /obj/item/storage/backpack/munitions satchel = /obj/item/storage/backpack/satchel/munitions diff --git a/nsv13/code/modules/jobs/job_types/marine/military_police.dm b/nsv13/code/modules/jobs/job_types/marine/military_police.dm index 0d1da369fcb..519ecae2da4 100644 --- a/nsv13/code/modules/jobs/job_types/marine/military_police.dm +++ b/nsv13/code/modules/jobs/job_types/marine/military_police.dm @@ -83,7 +83,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S destination = /area/security/checkpoint/science spawn_point = locate(/obj/effect/landmark/start/depsec/science) in GLOB.department_security_spawns accessory = /obj/item/clothing/accessory/armband/science - if(SEC_DEPT_MUNITIONS) + if(SEC_DEPT_MUNITIONS) ears = /obj/item/radio/headset/munitions/munitions_security_alt dep_access = list(ACCESS_MUNITIONS, ACCESS_MUNITIONS_STORAGE) accessory = /obj/item/clothing/accessory/armband/munitions @@ -139,7 +139,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S id = /obj/item/card/id/job/security_officer l_pocket = /obj/item/restraints/handcuffs r_pocket = /obj/item/assembly/flash/handheld - backpack_contents = list(/obj/item/ammo_box/magazine/glock, /obj/item/gun/ballistic/tazer, /obj/item/ammo_box/magazine/tazer_cartridge_storage=1,/obj/item/pda/security, /obj/item/book/granter/martial/jujitsu, /obj/item/squad_pager/all_channels, /obj/item/club=1) //NSV13 + backpack_contents = list(/obj/item/ammo_box/magazine/glock, /obj/item/gun/ballistic/tazer, /obj/item/ammo_box/magazine/tazer_cartridge_storage=1,/obj/item/modular_computer/tablet/pda/security, /obj/item/book/granter/martial/jujitsu, /obj/item/squad_pager/all_channels, /obj/item/club=1) //NSV13 backpack = /obj/item/storage/backpack/security satchel = /obj/item/storage/backpack/satchel/sec diff --git a/nsv13/code/modules/jobs/job_types/master_at_arms.dm b/nsv13/code/modules/jobs/job_types/master_at_arms.dm index 120c46fc791..0b6eb4e9e61 100644 --- a/nsv13/code/modules/jobs/job_types/master_at_arms.dm +++ b/nsv13/code/modules/jobs/job_types/master_at_arms.dm @@ -50,7 +50,7 @@ head = /obj/item/clothing/head/ship/maa_hat glasses = /obj/item/clothing/glasses/sunglasses/advanced id = /obj/item/card/id/job/master_at_arms - l_pocket = /obj/item/pda + l_pocket = /obj/item/modular_computer/tablet/pda/heads/maa backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1) backpack = /obj/item/storage/backpack/munitions diff --git a/nsv13/code/modules/jobs/job_types/munitions_technician.dm b/nsv13/code/modules/jobs/job_types/munitions_technician.dm index 96a9c748bd1..c0694020da2 100644 --- a/nsv13/code/modules/jobs/job_types/munitions_technician.dm +++ b/nsv13/code/modules/jobs/job_types/munitions_technician.dm @@ -37,7 +37,7 @@ head = /obj/item/clothing/head/helmet/decktech gloves = /obj/item/clothing/gloves/color/brown id = /obj/item/card/id/job/munitions_technician - l_pocket = /obj/item/pda + l_pocket = /obj/item/modular_computer/tablet/pda/munition backpack = /obj/item/storage/backpack/munitions satchel = /obj/item/storage/backpack/satchel/munitions diff --git a/nsv13/code/modules/overmap/fighters/_fighters.dm b/nsv13/code/modules/overmap/fighters/_fighters.dm index e7f365b37a3..71c3638c2a3 100644 --- a/nsv13/code/modules/overmap/fighters/_fighters.dm +++ b/nsv13/code/modules/overmap/fighters/_fighters.dm @@ -688,7 +688,7 @@ Been a mess since 2018, we'll fix it someday (probably) /obj/structure/overmap/small_craft/attackby(obj/item/W, mob/user, params) //fueling and changing equipment add_fingerprint(user) - if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda) && length(operators)) + if(istype(W, /obj/item/card/id) || istype(W, /obj/item/modular_computer/tablet/pda) && length(operators)) if(!allowed(user)) var/ersound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg') playsound(src, ersound, 100, 1) diff --git a/nsv13/code/modules/overmap/fighters/modules/cargo_hold.dm b/nsv13/code/modules/overmap/fighters/modules/cargo_hold.dm index 89fd890b616..7258fc9393f 100644 --- a/nsv13/code/modules/overmap/fighters/modules/cargo_hold.dm +++ b/nsv13/code/modules/overmap/fighters/modules/cargo_hold.dm @@ -19,7 +19,7 @@ max_freight = 20 /obj/item/fighter_component/secondary/utility/hold/load(obj/structure/overmap/target, atom/movable/AM) - if(length(contents) >= max_freight || isliving(AM) || istype(AM, /obj/item/fighter_component) || istype(AM, /obj/item/card/id) || istype(AM, /obj/item/pda) || istype(AM, /obj/structure/overmap)) //This just causess issues, trust me on this) + if(length(contents) >= max_freight || isliving(AM) || istype(AM, /obj/item/fighter_component) || istype(AM, /obj/item/card/id) || istype(AM, /obj/item/modular_computer/tablet/pda) || istype(AM, /obj/structure/overmap)) //This just causess issues, trust me on this) return FALSE if((AM.move_resist > MOVE_FORCE_DEFAULT) || !AM.doMove(src)) return //Can't put ultra heavy stuff in diff --git a/nsv13/code/modules/power/reactor/rbmk.dm b/nsv13/code/modules/power/reactor/rbmk.dm index 1f17e90509c..35eddff15c0 100644 --- a/nsv13/code/modules/power/reactor/rbmk.dm +++ b/nsv13/code/modules/power/reactor/rbmk.dm @@ -851,7 +851,7 @@ The reactor CHEWS through moderator. It does not do this slowly. Be very careful if(tempOutputdata.len > 100) //Only lets you track over a certain timeframe. tempOutputdata.Cut(1, 2) -/datum/computer_file/program/nuclear_monitor/run_program(mob/living/user) +/datum/computer_file/program/nuclear_monitor/on_start(mob/living/user) . = ..(user) //No reactor? Go find one then. if(!reactor) diff --git a/nsv13/code/modules/power/stormdrive.dm b/nsv13/code/modules/power/stormdrive.dm index 92bb1d2664b..87e6c0cf15c 100644 --- a/nsv13/code/modules/power/stormdrive.dm +++ b/nsv13/code/modules/power/stormdrive.dm @@ -1894,7 +1894,7 @@ Control Rods if(istype(computer)) computer.update_icon() -/datum/computer_file/program/stormdrive_monitor/run_program(mob/living/user) +/datum/computer_file/program/stormdrive_monitor/on_start(mob/living/user) . = ..(user) //No reactor? Go find one then. if(!reactor) diff --git a/nsv13/code/modules/recycling/replicatorDS.dm b/nsv13/code/modules/recycling/replicatorDS.dm index 9b138f8db0f..e8749e170a7 100644 --- a/nsv13/code/modules/recycling/replicatorDS.dm +++ b/nsv13/code/modules/recycling/replicatorDS.dm @@ -341,8 +341,8 @@ menu = design menu = lowertext(menu) - addtimer(CALLBACK(src, .proc/replicate, menu, temperature, user), speed_mult) - addtimer(CALLBACK(src, .proc/set_ready, TRUE), speed_mult) + addtimer(CALLBACK(src, PROC_REF(replicate), menu, temperature, user), speed_mult) + addtimer(CALLBACK(src, PROC_REF(set_ready), TRUE), speed_mult) /obj/machinery/replicator/proc/set_ready() icon_state = "replicator-on" diff --git a/nsv13/code/modules/ship_missions/hail_computer.dm b/nsv13/code/modules/ship_missions/hail_computer.dm index bc63176dbe4..7f51d7f6080 100644 --- a/nsv13/code/modules/ship_missions/hail_computer.dm +++ b/nsv13/code/modules/ship_missions/hail_computer.dm @@ -14,7 +14,7 @@ tgui_id = "NtosHailLogs" var/obj/var/obj/structure/overmap/ship //Our ship -/datum/computer_file/program/ship_hail_logger/run_program(mob/living/user) +/datum/computer_file/program/ship_hail_logger/on_start(mob/living/user) . = ..(user) ship = user.get_overmap() diff --git a/nsv13/icons/obj/pda.dmi b/nsv13/icons/obj/pda.dmi index dbcf1b128844a04a16150c62fdcf9cd86a0df8a8..f2736834ad8664ad4364b2ba657b63cb13d9a15f 100644 GIT binary patch literal 24269 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

    Z#>vUGBII&d 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 literal 11050 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 diff --git a/tgui/packages/tgui/components/TextArea.js b/tgui/packages/tgui/components/TextArea.js index 00e1605a3b3..d9bcac3f877 100644 --- a/tgui/packages/tgui/components/TextArea.js +++ b/tgui/packages/tgui/components/TextArea.js @@ -104,6 +104,14 @@ export class TextArea extends Component { if (input) { input.value = toInputValue(nextValue); } + if (this.props.autoFocus || this.props.autoSelect) { + setTimeout(() => { + input.focus(); + if (this.props.autoSelect) { + input.select(); + } + }, 1); + } } componentDidUpdate(prevProps, prevState) { diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js index 3b8048de470..456a2b1993d 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.js @@ -11,14 +11,35 @@ import './styles/themes/admin.scss'; import './styles/themes/cardtable.scss'; import './styles/themes/clockwork.scss'; import './styles/themes/hackerman.scss'; +import './styles/themes/hackeros.scss'; // NSV13 - PDA Hackerman Theme, different from regular one. import './styles/themes/malfunction.scss'; import './styles/themes/neutral.scss'; import './styles/themes/ntos.scss'; +import './styles/themes-ntos/ntos-colors.scss'; +import './styles/themes-ntos/default.scss'; +import './styles/themes-ntos/dark.scss'; +import './styles/themes-ntos/light.scss'; +import './styles/themes-ntos/red.scss'; +import './styles/themes-ntos/orange.scss'; +import './styles/themes-ntos/yellow.scss'; +import './styles/themes-ntos/olive.scss'; +import './styles/themes-ntos/green.scss'; +import './styles/themes-ntos/teal.scss'; +import './styles/themes-ntos/blue.scss'; +import './styles/themes-ntos/violet.scss'; +import './styles/themes-ntos/purple.scss'; +import './styles/themes-ntos/pink.scss'; +import './styles/themes-ntos/brown.scss'; +import './styles/themes-ntos/grey.scss'; +import './styles/themes-ntos/clown-pink.scss'; +import './styles/themes-ntos/clown-yellow.scss'; +import './styles/themes-ntos/hackerman.scss'; import './styles/themes/generic.scss'; import './styles/themes/paper.scss'; import './styles/themes/retro.scss'; import './styles/themes/syndicate.scss'; import './styles/themes/clockwork.scss'; +import './styles/themes/thinktronic-classic.scss'; import './styles/themes/dominion.scss'; import { perf } from 'common/perf'; diff --git a/tgui/packages/tgui/interfaces/EmagConsole.js b/tgui/packages/tgui/interfaces/EmagConsole.js new file mode 100644 index 00000000000..718e77575ba --- /dev/null +++ b/tgui/packages/tgui/interfaces/EmagConsole.js @@ -0,0 +1,17 @@ +import { Window } from '../layouts'; +import { EmagConsoleText } from "./NtosEmagConsole"; +import { useBackend } from '../backend'; + +export const EmagConsole = (props, context) => { + const { data } = useBackend(context); + return ( + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/EmojiInputModal.js b/tgui/packages/tgui/interfaces/EmojiInputModal.js new file mode 100644 index 00000000000..e54efda16eb --- /dev/null +++ b/tgui/packages/tgui/interfaces/EmojiInputModal.js @@ -0,0 +1,143 @@ + +import { KEY_BACKSPACE, KEY_ENTER, KEY_LEFT, KEY_RIGHT } from 'common/keycodes'; +import { useBackend, useLocalState } from '../backend'; +import { Box, Section, Button } from '../components'; +import { Window } from '../layouts'; + +const clamp = (num, min, max) => Math.min(Math.max(num, min), max); + +export const EmojiInputModal = (_, context) => { + const { data, act } = useBackend(context); + const { + title, + all_emojis = [], + } = data; + const [emojis, setEmojis] = useLocalState(context, 'emoji_input_text', []); + const [cursor_pos, setCursorPos] = useLocalState(context, 'emoji_input_cursor', 0); + + const insert = (arr, index, newItem) => [ + ...arr.slice(0, index), + newItem, + ...arr.slice(index), + ]; + const remove = (arr, index) => [ + ...arr.slice(0, index - 1), + ...arr.slice(index), + ]; + const submit = () => act('submit', { entry: emojis.map(emoji => `:${emoji}:`).join(" ") }); + const backspace = () => { + if (cursor_pos > 0 && cursor_pos <= emojis.length) { + setEmojis(remove(emojis, cursor_pos)); + setCursorPos(cursor_pos - 1); + } + }; + const left = () => setCursorPos(clamp(cursor_pos - 1, 0, emojis.length)); + const right = () => setCursorPos(clamp(cursor_pos + 1, 0, emojis.length)); + return ( + + + { + const keyCode = window.event ? event.which : event.code; + const e = window.event ? window.event : event; + switch (keyCode) { + case KEY_ENTER: + submit(); + e.preventDefault(); + break; + case KEY_BACKSPACE: + backspace(); + e.preventDefault(); + break; + case KEY_LEFT: + left(); + e.preventDefault(); + break; + case KEY_RIGHT: + right(); + e.preventDefault(); + break; + } + }}> +

    +
    +
    +
    + {Object.keys(all_emojis).slice(1).map(emoji => ( { + setEmojis(insert(emojis, cursor_pos, + event.target.value)); + setCursorPos(cursor_pos + 1); + }} + />))} +
    +
    + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/MessageMonitor.js b/tgui/packages/tgui/interfaces/MessageMonitor.js new file mode 100644 index 00000000000..4112b8e7080 --- /dev/null +++ b/tgui/packages/tgui/interfaces/MessageMonitor.js @@ -0,0 +1,325 @@ +import { Component, createRef } from "inferno"; +import { useBackend, useSharedState } from "../backend"; +import { Tabs, Section, Icon, Button, Box, Flex, Dimmer, Table, BlockQuote } from "../components"; +import { ButtonConfirm } from "../components/Button"; +import { Window } from "../layouts"; +import { MessageContent } from "./NtosMessenger"; +import { sanitizeText } from "../sanitize"; + +const processedText = value => { + return { + __html: sanitizeText(value), + }; +}; + + +export const MessageMonitor = (_, context) => { + const { data } = useBackend(context); + const { + authenticated, + } = data; + return ( + + + + + + ); +}; + +export const MessageMonitorContent = (_, context) => { + const { act, data } = useBackend(context); + const { + server_on, + authenticated, + no_server, + hacking, + can_hack, + pda_messages = [], + request_messages = [], + emoji_names = [], + } = data; + const [selectedTab, setSelectedTab] = useSharedState(context, "selected_tab", "pda"); + if (hacking) { + return ( + + +
    + {`------------------- +Crypto-Breaker 5000 +------------------- +Brute Force In Progress +Please Wait...`} +
    +
    + + + +
    + ); + } + return ( + + +
    + SERVER CONNECTION + + } + fontSize={1.25} + color={!no_server && server_on ? "good" : "bad"} + buttons={ + <> + {!no_server && authenticated ? ( +
    +
    + +
    + {!authenticated ? ( + + + + Awaiting Decryption Key... + + + +
    +
    + {authenticated ? ( + +
    + + + + + setSelectedTab("pda")}> + PDA Logs + + setSelectedTab("request")}> + Request Logs + + + + + + {selectedTab === "pda" ? ( +
    +
    + {selectedTab === "pda" ? ( + pda_messages.map(message => ( +
    act("delete_log", { type: "pda", ref: message.ref })} /> + } + mb={2}> + +
    + )) + ) : ( + request_messages.map(request => ( +
    act("delete_log", { type: "request", ref: request.ref })} /> + }> + + {request.priority} Priority + +
    + {request.stamp && request.stamp !== "Unstamped" ? ( + <> + +
    + + ) : null} + {request.id_auth && request.id_auth !== "Unauthenticated" ? ( + <> + +
    + + ) : null} +
    + {request.message} +
    +
    + )) + )} + {(selectedTab === "pda" && !pda_messages?.length) || (selectedTab === "request" && !request_messages?.length) ? ( +
    + + No Data + +
    + ) : null} +
    + ) : null} +
    + ); +}; + +const L1 = ["the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le"]; +const L2 = ["diamond", "beer", "mushroom", "assistant", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai"]; +const L3 = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]; + +class PasswordScroller extends Component { + + constructor(props) { + super(props); + this.timer = null; + this.reset(); + this.state = { + text: "---DECRYPTION KEY BRUTE-FORCE BEGIN---\n", + }; + this.endRef = createRef(); + } + + reset() { + this.index1 = 0; + this.index2 = 0; + this.index3 = 0; + } + + tick() { + this.setState(oldState => { + return { text: oldState.text + L1[this.index1] + L2[this.index2] + L3[this.index3] + "\n" }; + }); + if (this.index3 < L3.length - 1) { + this.index3++; + } else { + if (this.index2 < L2.length - 1) { + this.index2++; + } else { + if (this.index1 < L1.length - 1) { + this.index1++; + } else { + this.index1 = 0; + this.setState({ + text: "---DECRYPTION KEY BRUTE-FORCE BEGIN---\n", + }); + } + this.index2 = 0; + } + this.index3 = 0; + } + this.endRef.current?.scrollIntoView({ behavior: 'smooth' }); + } + + componentDidMount() { + this.timer = setInterval(() => this.tick(), this.props.tickInterval); + } + + componentWillUnmount() { + clearTimeout(this.timer); + } + + render() { + return ( +
    + {this.state.text} +
    +
    + ); + } +} diff --git a/tgui/packages/tgui/interfaces/NtosAirlockControl.js b/tgui/packages/tgui/interfaces/NtosAirlockControl.js new file mode 100644 index 00000000000..903ccf1e099 --- /dev/null +++ b/tgui/packages/tgui/interfaces/NtosAirlockControl.js @@ -0,0 +1,38 @@ +import { useBackend } from '../backend'; +import { Section, LabeledList, Button, Icon } from '../components'; +import { NtosWindow } from '../layouts'; + +export const NtosAirlockControl = (_, context) => { + const { act, data } = useBackend(context); + const { + airlocks = [], + } = data; + return ( + + +
    + + {airlocks.map(airlock => ( + + {` ${airlock.name} (${airlock.locx}, ${airlock.locy})`} + + )} + key={airlock.id} + buttons={ +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/NtosConfiguration.js b/tgui/packages/tgui/interfaces/NtosConfiguration.js index b6ac5dff36c..f266b769c55 100644 --- a/tgui/packages/tgui/interfaces/NtosConfiguration.js +++ b/tgui/packages/tgui/interfaces/NtosConfiguration.js @@ -1,5 +1,5 @@ import { useBackend } from '../backend'; -import { Box, Button, LabeledList, ProgressBar, Section } from '../components'; +import { Box, Button, LabeledList, ProgressBar, Section, ColorBox, Dropdown } from '../components'; import { NtosWindow } from '../layouts'; export const NtosConfiguration = (props, context) => { @@ -12,11 +12,26 @@ export const NtosConfiguration = (props, context) => { disk_size, disk_used, hardware = [], + PC_device_theme, + themes = {}, + PC_theme_locked, } = data; - return ( + {!PC_theme_locked ? ( +
    + themes[key] === PC_device_theme) || "NtOS Default"} + onSelected={value => act('PC_select_theme', { + theme: value, + })} /> + {PC_device_theme === "thinktronic-classic" ?
    + ) : null}
    { + return ( + + + + + + ); +}; + +export class EmagConsoleText extends Component { + + constructor(props) { + super(props); + this.timer = null; + this.state = { + index: 0, + }; + this.text = logTextPrependAlways + this.props.log_text; + } + + tick() { + const { props, state } = this; + if (state.index < this.text.length) { + this.setState({ index: state.index + (1 * (props?.frame_skip || 1)) }); + } else { + clearTimeout(this.timer); + this.timer = setTimeout(() => { + const { act } = useBackend(this.context); + // All UI actions close the program + act("PC_exit"); + }, this.props.end_pause || 500); + } + } + + componentDidMount() { + this.timer = setInterval(() => this.tick(), tickInterval); + } + + componentWillUnmount() { + clearTimeout(this.timer); + } + + render() { + const { state } = this; + const toShow = this.text.substring(0, + Math.min(state.index, this.text.length)); + return ( +
    + {(logTextAlways + toShow).split("\n").map((log) => ( + log !== "\n" ? ( + + {log} + + ) : null + ))} +
    + ); + } +} diff --git a/tgui/packages/tgui/interfaces/NtosLogViewer.js b/tgui/packages/tgui/interfaces/NtosLogViewer.js new file mode 100644 index 00000000000..5e8b46ad83c --- /dev/null +++ b/tgui/packages/tgui/interfaces/NtosLogViewer.js @@ -0,0 +1,90 @@ +import { NtosWindow } from '../layouts'; +import { useBackend, useLocalState } from '../backend'; +import { Section, Table, Button } from '../components'; +import { Component, createRef } from 'inferno'; + +export const NtosLogViewer = (props, context) => { + const { act, data } = useBackend(context); + const [openFile, setOpenFile] = useLocalState(context, "log_viewer_open", null); + const { + files = [], + } = data; + const openFileResult = files.find(file => + file.name === openFile + && (!file.remote || file.online)); + return ( + + + + {!openFileResult ? ( +
    + + + + Filename + + + Size + + + {files.map(file => ( + + + {file.name}.log + + + {!file.remote ? `${file.size}GQ` : "Remote"} + + + {!!file.remote &&
    +
    + ) : ( + + )} +
    +
    + ); +}; + +class Log extends Component { + + constructor(props) { + super(props); + } + + messagesEndRef = createRef() + + componentDidMount() { + this.scrollToBottom(); + } + componentDidUpdate(prevProps) { + if (prevProps.file.data !== this.props.file.data) { + this.scrollToBottom(); + } + } + scrollToBottom = () => { + this.messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + } + render() { + return ( +
    this.props.setOpenFile(null)} />}> + {this.props.file.data} +
    +
    + ); + } +} diff --git a/tgui/packages/tgui/interfaces/NtosMain.js b/tgui/packages/tgui/interfaces/NtosMain.js index f3a81d7421e..d8874ea2b3e 100644 --- a/tgui/packages/tgui/interfaces/NtosMain.js +++ b/tgui/packages/tgui/interfaces/NtosMain.js @@ -1,17 +1,26 @@ import { useBackend } from '../backend'; import { Button, ColorBox, Section, Table } from '../components'; +import { ButtonCheckbox } from '../components/Button'; import { NtosWindow } from '../layouts'; export const NtosMain = (props, context) => { const { act, data } = useBackend(context); const { + show_imprint, programs = [], has_light, light_on, comp_light_color, removable_media = [], cardholder, + auto_imprint, login = [], + proposed_login = [], + disk, + disk_name, + disk_programs = [], + stored_pai, + stored_pai_name, } = data; return ( {
    )} - {!!cardholder && ( + {!!(cardholder) && (
    act('PC_Eject_Disk', { name: "ID" })} - /> + <> +
    @@ -75,6 +104,38 @@ export const NtosMain = (props, context) => { )} + {!!stored_pai && ( +
    + + + +
    +
    + )}
    {programs.map(program => ( @@ -88,6 +149,7 @@ export const NtosMain = (props, context) => { content={program.desc} onClick={() => act('PC_runprogram', { name: program.name, + is_disk: false, })} /> @@ -107,6 +169,50 @@ export const NtosMain = (props, context) => { ))}
    + {!!disk && ( +
    act('PC_Eject_Disk', { name: "job disk" })} /> + )}> + + {disk_programs.map(program => ( + + +
    +
    + )}
    ); diff --git a/tgui/packages/tgui/interfaces/NtosMessenger.js b/tgui/packages/tgui/interfaces/NtosMessenger.js new file mode 100644 index 00000000000..d6757e26af8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/NtosMessenger.js @@ -0,0 +1,293 @@ +import { useBackend, useLocalState } from '../backend'; +import { createSearch } from 'common/string'; +import { Box, Button, Dimmer, Icon, Section, Stack, Input } from '../components'; +import { NtosWindow } from '../layouts'; + +export const NtosMessenger = (_, context) => { + const { data } = useBackend(context); + const { viewing_messages } = data; + return ( + + + {viewing_messages ? ( + + ) : ( + + )} + + + ); +}; + +const NoIDDimmer = (_, context) => { + const { data } = useBackend(context); + return ( + + + + + + + + + + + + + Please imprint an ID to continue. + + + + + + ); +}; + +const MessageListScreen = (props, context) => { + const { act, data } = useBackend(context); + const { + messages = [], + emoji_names = [], + } = data; + return ( + +
    +
    + {messages.map(message => ( + <> +
    + + {message.outgoing ? ( + "(OUTGOING)" + ) : ( + "(INCOMING)" + )} + + {message.outgoing ? ( + + {message.target} + + ) : ( +
    +
    + +
    + + ))} +
    + ); +}; + +const ContactsScreen = (props, context) => { + const { act, data } = useBackend(context); + const { + owner, + ringer_status, + sending_and_receiving, + messengers = [], + sortByJob, + canSpam, + isSilicon, + photo, + virus_attach, + sending_virus, + } = data; + const [searchUser, setSearchUser] = useLocalState(context, 'searchUser', ''); + const search = createSearch( + searchUser, + (messengers) => messengers.name + messengers.job + ); + let users + = searchUser.length > 0 ? data.messengers.filter(search) : messengers; + return ( + <> + +
    + + + SpaceMessenger V8.5.3 + + + Bringing you spy-proof communications since 2467. + +
    +
    + +
    + +
    +
    + {!!photo && ( + +
    + + Current Photo +
    +
    + +
    +
    + )} + +
    + + Detected Messengers
    + setSearchUser(value)} + /> +
    +
    + +
    + + {users.length === 0 && 'No users found'} + {users.map(messenger => ( + + ))} + + {!!canSpam && ( +
    +
    + {(!owner && !isSilicon) && ( + + )} + + ); +}; + +export const MessageContent = (props) => { + const { + contents, + photo, + photo_width, + photo_height, + emojis, + emoji_names, + } = props; + return ( + <> + { + contents.split(":").map((part, index, arr) => { + if (emojis + && Object.keys(emoji_names).includes(part)) { + return (); + } else { + // re-add colons from split() + // if the next element in the array is not valid emoji + return {part}{arr.length - 1 !== index && (index + 1 >= arr.length || !emojis || !Object.keys(emoji_names).includes(arr[index + 1])) ? ":" : ""}; + } + }) + } + {!!photo && ( + <> +
    + + + )} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/NtosNetChat.js b/tgui/packages/tgui/interfaces/NtosNetChat.js index 5c765f2049e..a74e17d2ed8 100644 --- a/tgui/packages/tgui/interfaces/NtosNetChat.js +++ b/tgui/packages/tgui/interfaces/NtosNetChat.js @@ -70,6 +70,7 @@ export const NtosNetChat = (props, context) => { all_channels = [], clients = [], messages = [], + PC_device_theme, } = data; const in_channel = (active_channel !== null); @@ -185,7 +186,7 @@ export const NtosNetChat = (props, context) => { ) ) || ( - + PC_device_theme !== "thinktronic-classic" && )} diff --git a/tgui/packages/tgui/interfaces/NtosNotepad.js b/tgui/packages/tgui/interfaces/NtosNotepad.js new file mode 100644 index 00000000000..b6caf35f1f4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/NtosNotepad.js @@ -0,0 +1,32 @@ +import { NtosWindow } from '../layouts'; +import { useBackend } from '../backend'; +import { Section, TextArea, Button } from '../components'; + +export const NtosNotepad = (props, context) => { + const { act, data } = useBackend(context); + const { + note, + has_paper, + } = data; + return ( + + +
    act('ShowPaper')} />} + fill fitted> +