From f7b898a2a96dd16e27b581b8cf1f4d709ebcda3f Mon Sep 17 00:00:00 2001
From: LetterN <24603524+LetterN@users.noreply.github.com>
Date: Thu, 28 Oct 2021 17:34:52 +0800
Subject: [PATCH] frontend
---
code/__DEFINES/dcs/signals.dm | 6 +
code/__DEFINES/machines.dm | 29 +-
code/__DEFINES/misc.dm | 1 +
code/controllers/subsystem/sound_loops.dm | 3 +
code/datums/chatmessage.dm | 2 +-
code/datums/looping_sounds/_looping_sound.dm | 81 +-
code/game/atoms_movement.dm | 13 +-
code/game/communications.dm | 4 +-
.../game/machinery/computer/communications.dm | 109 +-
code/game/machinery/cryopod.dm | 549 +-
code/game/machinery/hologram.dm | 152 +-
code/game/objects/structures/artstuff.dm | 4 +-
code/game/objects/structures/noticeboard.dm | 132 +-
.../NTNet/NTNRC/conversation.dm | 74 +-
.../computers/_modular_computer_shared.dm | 6 +-
.../computers/item/computer_components.dm | 14 +-
.../computers/item/computer_damage.dm | 2 +-
.../computers/item/computer_power.dm | 2 +-
.../computers/item/computer_ui.dm | 22 +-
.../computers/item/laptop.dm | 24 +-
.../computers/item/processor.dm | 6 +-
.../computers/item/tablet.dm | 24 +-
.../computers/item/tablet_presets.dm | 32 +
.../computers/machinery/console_presets.dm | 91 +-
.../computers/machinery/modular_computer.dm | 115 +-
.../computers/machinery/modular_console.dm | 5 +-
.../file_system/computer_file.dm | 14 +-
.../modular_computers/file_system/data.dm | 4 +-
.../modular_computers/file_system/program.dm | 37 +-
.../file_system/program_events.dm | 4 +-
.../file_system/programs/airestorer.dm | 7 +-
.../programs/antagonist/contract_uplink.dm | 10 +-
.../file_system/programs/antagonist/dos.dm | 1 +
.../programs/antagonist/revelation.dm | 11 +-
.../file_system/programs/arcade.dm | 16 +-
.../file_system/programs/atmosscan.dm | 3 +-
.../file_system/programs/borg_monitor.dm | 86 +-
.../file_system/programs/bounty_board.dm | 1 +
.../file_system/programs/budgetordering.dm | 51 +-
.../file_system/programs/card.dm | 1 +
.../file_system/programs/cargoship.dm | 25 +-
.../file_system/programs/crewmanifest.dm | 7 +-
.../file_system/programs/file_browser.dm | 19 +-
.../file_system/programs/jobmanagement.dm | 41 +-
.../file_system/programs/ntdownloader.dm | 59 +-
.../file_system/programs/ntmonitor.dm | 3 +-
.../file_system/programs/ntnrc_client.dm | 85 +-
.../file_system/programs/portrait_printer.dm | 83 +
.../file_system/programs/powermonitor.dm | 1 +
.../file_system/programs/radar.dm | 21 +-
.../file_system/programs/robocontrol.dm | 1 +
.../file_system/programs/robotact.dm | 9 +-
.../file_system/programs/secureye.dm | 1 +
.../file_system/programs/signaler.dm | 83 +
.../file_system/programs/sm_monitor.dm | 66 +-
.../modular_computers/hardware/_hardware.dm | 51 +-
.../modular_computers/hardware/ai_slot.dm | 26 +-
.../hardware/battery_module.dm | 47 +-
.../modular_computers/hardware/card_slot.dm | 69 +-
.../modular_computers/hardware/hard_drive.dm | 20 +-
.../hardware/network_card.dm | 11 +-
.../modular_computers/hardware/printer.dm | 10 +-
.../modular_computers/hardware/recharger.dm | 3 +-
.../hardware/sensor_package.dm | 9 +
.../modular_computers/laptop_vendor.dm | 28 +-
code/modules/paperwork/clipboard.dm | 254 +-
code/modules/power/supermatter/supermatter.dm | 4 +
.../wiremod/shell/brain_computer_interface.dm | 8 +-
....custom-core-widgit-mouse-sortable.min.js} | 0
html/{ => jquery}/jquery.min.js | 0
icons/UI_Icons/adventure/default.png | Bin 0 -> 244 bytes
icons/UI_Icons/adventure/grue.png | Bin 0 -> 167 bytes
icons/UI_Icons/adventure/signal_lost.png | Bin 0 -> 252 bytes
icons/UI_Icons/adventure/trade.png | Bin 0 -> 428 bytes
icons/UI_Icons/inventory/back.png | Bin 0 -> 298 bytes
icons/UI_Icons/inventory/belt.png | Bin 0 -> 205 bytes
icons/UI_Icons/inventory/collar.png | Bin 0 -> 215 bytes
icons/UI_Icons/inventory/ears.png | Bin 0 -> 276 bytes
icons/UI_Icons/inventory/glasses.png | Bin 0 -> 180 bytes
icons/UI_Icons/inventory/gloves.png | Bin 0 -> 249 bytes
icons/UI_Icons/inventory/hand_l.png | Bin 0 -> 203 bytes
icons/UI_Icons/inventory/hand_r.png | Bin 0 -> 203 bytes
icons/UI_Icons/inventory/head.png | Bin 0 -> 219 bytes
icons/UI_Icons/inventory/id.png | Bin 0 -> 186 bytes
icons/UI_Icons/inventory/mask.png | Bin 0 -> 252 bytes
icons/UI_Icons/inventory/neck.png | Bin 0 -> 229 bytes
icons/UI_Icons/inventory/pocket.png | Bin 0 -> 237 bytes
icons/UI_Icons/inventory/shoes.png | Bin 0 -> 196 bytes
icons/UI_Icons/inventory/suit.png | Bin 0 -> 213 bytes
icons/UI_Icons/inventory/suit_storage.png | Bin 0 -> 305 bytes
icons/UI_Icons/inventory/uniform.png | Bin 0 -> 250 bytes
icons/UI_Icons/tgui/grid_background.png | Bin 0 -> 21650 bytes
icons/UI_Icons/tgui/ntosradar_background.png | Bin 21490 -> 7367 bytes
icons/UI_Icons/tgui/ntosradar_pointer.png | Bin 1467 -> 284 bytes
icons/UI_Icons/tgui/ntosradar_pointer_S.png | Bin 1456 -> 291 bytes
tgstation.dme | 2 +
.../@yarnpkg/plugin-interactive-tools.cjs | 356 +-
tgui/.yarn/releases/yarn-2.4.1.cjs | 55 -
tgui/.yarn/releases/yarn-3.0.1.cjs | 631 ++
tgui/.yarn/sdks/eslint/bin/eslint.js | 2 +-
tgui/.yarn/sdks/eslint/lib/api.js | 2 +-
tgui/.yarn/sdks/eslint/package.json | 2 +-
tgui/.yarn/sdks/integrations.yml | 4 +-
tgui/.yarn/sdks/typescript/bin/tsc | 2 +-
tgui/.yarn/sdks/typescript/bin/tsserver | 2 +-
tgui/.yarn/sdks/typescript/lib/tsc.js | 2 +-
tgui/.yarn/sdks/typescript/lib/tsserver.js | 84 +-
.../sdks/typescript/lib/tsserverlibrary.js | 157 +
tgui/.yarn/sdks/typescript/lib/typescript.js | 2 +-
tgui/.yarn/sdks/typescript/package.json | 2 +-
tgui/.yarnrc.yml | 2 +-
tgui/babel.config.js | 2 +-
tgui/bin/tgui | 4 +-
tgui/bin/tgui_.ps1 | 4 +-
tgui/docs/component-reference.md | 32 +-
tgui/package.json | 55 +-
tgui/packages/tgfont/icons/air-tank-slash.svg | 30 +-
tgui/packages/tgfont/icons/air-tank.svg | 27 +-
tgui/packages/tgfont/icons/image-minus.svg | 1 +
tgui/packages/tgfont/icons/image-plus.svg | 1 +
.../packages/tgfont/icons/nanotrasen-logo.svg | 7 +-
tgui/packages/tgfont/icons/sound-minus.svg | 1 +
tgui/packages/tgfont/icons/sound-plus.svg | 1 +
tgui/packages/tgfont/icons/syndicate-logo.svg | 5 +-
tgui/packages/tgfont/package.json | 2 +-
tgui/packages/tgui-dev-server/dreamseeker.js | 5 +-
tgui/packages/tgui-dev-server/index.esm.js | 5 -
.../link/{client.js => client.cjs} | 15 +-
tgui/packages/tgui-dev-server/link/retrace.js | 8 +-
tgui/packages/tgui-dev-server/link/server.js | 8 +-
.../{common => tgui-dev-server}/logging.js | 0
tgui/packages/tgui-dev-server/package.json | 7 +-
tgui/packages/tgui-dev-server/reloader.js | 4 +-
tgui/packages/tgui-dev-server/require.js | 9 +
tgui/packages/tgui-dev-server/util.js | 22 +-
tgui/packages/tgui-dev-server/webpack.js | 2 +-
tgui/packages/tgui-dev-server/winreg.js | 2 +-
tgui/packages/tgui-panel/Panel.js | 4 +-
tgui/packages/tgui-panel/chat/constants.js | 10 +-
tgui/packages/tgui-panel/chat/middleware.js | 4 +-
tgui/packages/tgui-panel/chat/renderer.js | 14 +-
tgui/packages/tgui-panel/index.js | 19 +-
tgui/packages/tgui-panel/package.json | 2 +-
.../tgui-panel/settings/SettingsPanel.js | 20 +-
tgui/packages/tgui-panel/settings/reducer.js | 2 +
.../tgui-panel/styles/components/Chat.scss | 5 +-
.../tgui-panel/styles/components/Ping.scss | 4 +-
.../tgui-panel/styles/goon/chat-dark.scss | 27 +-
.../tgui-panel/styles/goon/chat-light.scss | 14 +-
tgui/packages/tgui-polyfill/index.js | 6 +-
tgui/packages/tgui-polyfill/package.json | 6 +-
.../tgui/components/AnimatedNumber.js | 2 +-
tgui/packages/tgui/components/Box.tsx | 2 +
tgui/packages/tgui/components/Button.js | 42 +-
.../packages/tgui/components/InfinitePlane.js | 208 +
tgui/packages/tgui/components/Popper.tsx | 72 +
tgui/packages/tgui/components/RoundGauge.js | 19 +-
tgui/packages/tgui/components/Tooltip.js | 28 -
tgui/packages/tgui/components/Tooltip.tsx | 73 +
tgui/packages/tgui/components/index.js | 2 +
tgui/packages/tgui/constants.js | 15 +-
tgui/packages/tgui/debug/middleware.js | 2 +-
tgui/packages/tgui/format.js | 2 +-
tgui/packages/tgui/index.js | 2 +-
.../tgui/interfaces/AbductorConsole.js | 12 +-
.../tgui/interfaces/AdventureBrowser.tsx | 126 +
tgui/packages/tgui/interfaces/AiAirlock.js | 5 +-
tgui/packages/tgui/interfaces/AiRestorer.js | 8 +-
tgui/packages/tgui/interfaces/AirAlarm.js | 11 +-
tgui/packages/tgui/interfaces/AlertModal.js | 177 +-
.../tgui/interfaces/AntagInfoBrainwashed.tsx | 86 +
.../tgui/interfaces/AntagInfoMorph.tsx | 54 +
.../tgui/interfaces/AntagInfoNightmare.tsx | 88 +
.../tgui/interfaces/AntagInfoTraitor.tsx | 246 +
.../tgui/interfaces/AntagInfoWizard.tsx | 152 +
tgui/packages/tgui/interfaces/Apc.js | 18 +-
tgui/packages/tgui/interfaces/ApcControl.js | 16 +-
.../tgui/interfaces/AtmosAlertConsole.js | 3 +-
.../tgui/interfaces/AtmosControlConsole.js | 3 +-
.../tgui/interfaces/AtmosControlPanel.js | 3 +-
.../packages/tgui/interfaces/AtmosTempPump.js | 2 +-
tgui/packages/tgui/interfaces/Bepis.js | 2 +-
tgui/packages/tgui/interfaces/Biogenerator.js | 8 +-
.../tgui/interfaces/BlackMarketUplink.js | 27 +-
.../tgui/interfaces/BluespaceArtillery.js | 9 +-
.../tgui/interfaces/BluespaceLocator.js | 3 +-
.../tgui/interfaces/BluespaceSender.js | 6 +-
.../tgui/interfaces/BluespaceVendor.js | 2 +-
tgui/packages/tgui/interfaces/BorgPanel.js | 5 +-
tgui/packages/tgui/interfaces/BrigTimer.js | 8 +-
.../packages/tgui/interfaces/CameraConsole.js | 3 +-
tgui/packages/tgui/interfaces/Canister.js | 29 +-
tgui/packages/tgui/interfaces/Canvas.js | 3 +-
tgui/packages/tgui/interfaces/CargoExpress.js | 8 +-
.../tgui/interfaces/CargoHoldTerminal.js | 8 +-
.../tgui/interfaces/CellularEmporium.js | 8 +-
.../tgui/interfaces/CentcomPodLauncher.js | 302 +-
tgui/packages/tgui/interfaces/Changelog.js | 348 +
.../tgui/interfaces/CircuitAdminPanel.tsx | 85 +
.../packages/tgui/interfaces/CircuitModule.js | 145 +
tgui/packages/tgui/interfaces/Clipboard.js | 15 +-
.../tgui/interfaces/CommunicationsConsole.js | 127 +-
.../tgui/interfaces/ComponentPrinter.tsx | 171 +
.../tgui/interfaces/ComputerFabricator.js | 43 +-
tgui/packages/tgui/interfaces/Crayon.js | 3 +-
tgui/packages/tgui/interfaces/CrewConsole.js | 3 +-
tgui/packages/tgui/interfaces/CrewManifest.js | 55 +-
tgui/packages/tgui/interfaces/Cryo.js | 12 +-
.../tgui/interfaces/CryopodConsole.js | 98 +-
tgui/packages/tgui/interfaces/Crystallizer.js | 4 +-
.../tgui/interfaces/CyborgBootDebug.js | 2 +-
tgui/packages/tgui/interfaces/DecalPainter.js | 4 +-
tgui/packages/tgui/interfaces/Electrolyzer.js | 5 +-
.../tgui/interfaces/EngravedMessage.js | 22 +-
.../tgui/interfaces/ExodroneConsole.tsx | 24 +-
.../tgui/interfaces/ExosuitControlConsole.js | 8 +-
.../tgui/interfaces/ExosuitFabricator.js | 718 +-
tgui/packages/tgui/interfaces/Filteriffic.js | 34 +-
.../packages/tgui/interfaces/ForbiddenLore.js | 5 +-
tgui/packages/tgui/interfaces/Gateway.js | 11 +-
.../tgui/interfaces/GhostPoolProtection.js | 5 +-
tgui/packages/tgui/interfaces/Gps.js | 8 +-
.../tgui/interfaces/GreyscaleModifyMenu.tsx | 245 +-
.../tgui/interfaces/GulagItemReclaimer.js | 3 +-
.../tgui/interfaces/GulagTeleporterConsole.js | 5 +-
tgui/packages/tgui/interfaces/Holodeck.js | 3 +-
tgui/packages/tgui/interfaces/Holopad.js | 17 +-
.../IntegratedCircuit/BasicInput.js | 27 +
.../IntegratedCircuit/CircuitInfo.js | 33 +
.../IntegratedCircuit/Connections.js | 57 +
.../IntegratedCircuit/DisplayName.js | 63 +
.../IntegratedCircuit/FundamentalTypes.js | 107 +
.../IntegratedCircuit/ObjectComponent.js | 204 +
.../tgui/interfaces/IntegratedCircuit/Port.js | 111 +
.../IntegratedCircuit/VariableMenu.js | 115 +
.../interfaces/IntegratedCircuit/constants.js | 5 +
.../interfaces/IntegratedCircuit/index.js | 388 +
tgui/packages/tgui/interfaces/Intellicard.js | 3 +-
tgui/packages/tgui/interfaces/Interview.js | 119 +-
tgui/packages/tgui/interfaces/KeycardAuth.js | 9 +-
tgui/packages/tgui/interfaces/LanguageMenu.js | 12 +-
.../tgui/interfaces/LaunchpadConsole.js | 3 +-
tgui/packages/tgui/interfaces/ListInput.js | 13 +-
tgui/packages/tgui/interfaces/MafiaPanel.js | 823 +--
.../interfaces/MalfunctionModulePicker.js | 3 +-
.../tgui/interfaces/MassDriverControl.js | 153 +-
.../tgui/interfaces/MechpadConsole.js | 3 +-
tgui/packages/tgui/interfaces/MedicalKiosk.js | 17 +-
tgui/packages/tgui/interfaces/Microscope.js | 2 +-
tgui/packages/tgui/interfaces/MiningVendor.js | 3 +-
tgui/packages/tgui/interfaces/Mule.js | 5 +-
tgui/packages/tgui/interfaces/NoticeBoard.js | 52 +
.../interfaces/NotificationPreferences.js | 3 +-
.../tgui/interfaces/NtosAiRestorer.js | 3 +-
tgui/packages/tgui/interfaces/NtosAtmos.js | 3 +-
tgui/packages/tgui/interfaces/NtosCargo.js | 3 +-
.../tgui/interfaces/NtosConfiguration.js | 8 +-
.../tgui/interfaces/NtosCrewManifest.js | 3 +-
.../interfaces/NtosCyborgRemoteMonitor.js | 214 +-
.../tgui/interfaces/NtosFileManager.js | 13 +-
.../tgui/interfaces/NtosJobManager.js | 3 +-
tgui/packages/tgui/interfaces/NtosMain.js | 3 +-
tgui/packages/tgui/interfaces/NtosNetChat.js | 320 +-
tgui/packages/tgui/interfaces/NtosNetDos.js | 5 +-
.../tgui/interfaces/NtosNetDownloader.js | 208 +-
.../tgui/interfaces/NtosNetMonitor.js | 17 +-
.../tgui/interfaces/NtosPortraitPrinter.js | 140 +
.../tgui/interfaces/NtosPowerMonitor.js | 3 +-
.../tgui/interfaces/NtosRequestKiosk.js | 3 +-
.../tgui/interfaces/NtosRoboControl.js | 36 +-
tgui/packages/tgui/interfaces/NtosRobotact.js | 33 +-
tgui/packages/tgui/interfaces/NtosSecurEye.js | 10 +-
tgui/packages/tgui/interfaces/NtosShipping.js | 3 +-
tgui/packages/tgui/interfaces/NtosSignaler.js | 14 +
.../interfaces/NtosStationAlertConsole.js | 3 +-
.../tgui/interfaces/NtosSupermatterMonitor.js | 164 +-
tgui/packages/tgui/interfaces/Orbit.js | 6 +-
tgui/packages/tgui/interfaces/OreBox.js | 3 +-
.../tgui/interfaces/OreRedemptionMachine.js | 8 +-
tgui/packages/tgui/interfaces/OrionGame.js | 8 +-
tgui/packages/tgui/interfaces/OutfitEditor.js | 6 +-
tgui/packages/tgui/interfaces/Pandemic.js | 16 +-
tgui/packages/tgui/interfaces/PaperSheet.js | 8 +-
.../tgui/interfaces/PersonalCrafting.js | 8 +-
.../tgui/interfaces/PortableChemMixer.js | 18 +-
.../tgui/interfaces/PortableGenerator.js | 3 +-
.../tgui/interfaces/PortableScrubber.js | 2 +-
.../tgui/interfaces/PortableTurret.js | 5 +-
.../tgui/interfaces/PortraitPicker.js | 10 +-
tgui/packages/tgui/interfaces/PowerMonitor.js | 12 +-
.../tgui/interfaces/ProduceConsole.js | 2 +-
.../tgui/interfaces/RemoteRobotControl.js | 8 +-
tgui/packages/tgui/interfaces/RequestKiosk.js | 26 +-
.../tgui/interfaces/RoboticsControlConsole.js | 26 +-
tgui/packages/tgui/interfaces/Roulette.js | 381 +-
.../packages/tgui/interfaces/SDQLSpellMenu.js | 777 ++
.../tgui/interfaces/SatelliteControl.js | 5 +-
tgui/packages/tgui/interfaces/Secrets.js | 910 +--
.../packages/tgui/interfaces/SeedExtractor.js | 3 +-
.../tgui/interfaces/SelectEquipment.js | 7 +-
.../tgui/interfaces/SentienceFunBalloon.js | 52 +
.../tgui/interfaces/ShuttleConsole.js | 27 +-
.../tgui/interfaces/ShuttleManipulator.js | 16 +-
tgui/packages/tgui/interfaces/Signaler.js | 159 +-
tgui/packages/tgui/interfaces/SkillStation.js | 32 +-
tgui/packages/tgui/interfaces/Sleeper.js | 9 +-
tgui/packages/tgui/interfaces/SmartVend.js | 3 +-
tgui/packages/tgui/interfaces/SpawnersMenu.js | 8 +-
tgui/packages/tgui/interfaces/Stack.js | 8 +-
.../tgui/interfaces/StackingConsole.js | 8 +-
.../tgui/interfaces/StationAlertConsole.js | 8 +-
.../tgui/interfaces/SupermatterMonitor.js | 192 +
.../tgui/interfaces/SyndContractor.js | 31 +-
tgui/packages/tgui/interfaces/TachyonArray.js | 41 +-
tgui/packages/tgui/interfaces/Tank.js | 1 -
tgui/packages/tgui/interfaces/Techweb.js | 7 +-
tgui/packages/tgui/interfaces/Telecomms.js | 53 +-
.../tgui/interfaces/TelecommsInteraction.js | 29 +-
tgui/packages/tgui/interfaces/Timer.js | 5 +-
.../tgui/interfaces/TrackedPlaytime.js | 42 +-
tgui/packages/tgui/interfaces/TramControl.js | 34 +-
.../tgui/interfaces/TurbineComputer.js | 5 +-
tgui/packages/tgui/interfaces/Uplink.js | 8 +-
tgui/packages/tgui/interfaces/Vendatray.js | 9 +-
.../interfaces/{Vending.js => Vending.tsx} | 104 +-
tgui/packages/tgui/interfaces/Wires.js | 5 +-
tgui/packages/tgui/layouts/Window.js | 32 +-
tgui/packages/tgui/logging.js | 2 +-
tgui/packages/tgui/package.json | 7 +-
tgui/packages/tgui/stories/Popper.stories.js | 47 +
tgui/packages/tgui/stories/Tooltip.stories.js | 17 +-
tgui/packages/tgui/styles/atomic/links.scss | 10 +
tgui/packages/tgui/styles/base.scss | 5 +-
.../tgui/styles/components/Button.scss | 2 +-
.../packages/tgui/styles/components/Tabs.scss | 15 +-
.../tgui/styles/components/Tooltip.scss | 133 +-
tgui/packages/tgui/styles/functions.scss | 10 +-
.../tgui/styles/interfaces/Changelog.scss | 13 +
.../styles/interfaces/IntegratedCircuit.scss | 54 +
.../tgui/styles/interfaces/Roulette.scss | 66 +-
.../tgui/styles/layouts/TitleBar.scss | 9 +
tgui/packages/tgui/styles/main.scss | 3 +
tgui/public/tgui.html | 10 +-
tgui/tsconfig.json | 27 +-
tgui/webpack.config.js | 4 -
tgui/yarn.lock | 6371 ++++++++---------
346 files changed, 13695 insertions(+), 8474 deletions(-)
create mode 100644 code/controllers/subsystem/sound_loops.dm
create mode 100644 code/modules/modular_computers/file_system/programs/portrait_printer.dm
create mode 100644 code/modules/modular_computers/file_system/programs/signaler.dm
rename html/{IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js => jquery/jquery-ui.custom-core-widgit-mouse-sortable.min.js} (100%)
rename html/{ => jquery}/jquery.min.js (100%)
create mode 100644 icons/UI_Icons/adventure/default.png
create mode 100644 icons/UI_Icons/adventure/grue.png
create mode 100644 icons/UI_Icons/adventure/signal_lost.png
create mode 100644 icons/UI_Icons/adventure/trade.png
create mode 100644 icons/UI_Icons/inventory/back.png
create mode 100644 icons/UI_Icons/inventory/belt.png
create mode 100644 icons/UI_Icons/inventory/collar.png
create mode 100644 icons/UI_Icons/inventory/ears.png
create mode 100644 icons/UI_Icons/inventory/glasses.png
create mode 100644 icons/UI_Icons/inventory/gloves.png
create mode 100644 icons/UI_Icons/inventory/hand_l.png
create mode 100644 icons/UI_Icons/inventory/hand_r.png
create mode 100644 icons/UI_Icons/inventory/head.png
create mode 100644 icons/UI_Icons/inventory/id.png
create mode 100644 icons/UI_Icons/inventory/mask.png
create mode 100644 icons/UI_Icons/inventory/neck.png
create mode 100644 icons/UI_Icons/inventory/pocket.png
create mode 100644 icons/UI_Icons/inventory/shoes.png
create mode 100644 icons/UI_Icons/inventory/suit.png
create mode 100644 icons/UI_Icons/inventory/suit_storage.png
create mode 100644 icons/UI_Icons/inventory/uniform.png
create mode 100644 icons/UI_Icons/tgui/grid_background.png
delete mode 100644 tgui/.yarn/releases/yarn-2.4.1.cjs
create mode 100644 tgui/.yarn/releases/yarn-3.0.1.cjs
create mode 100644 tgui/.yarn/sdks/typescript/lib/tsserverlibrary.js
create mode 100644 tgui/packages/tgfont/icons/image-minus.svg
create mode 100644 tgui/packages/tgfont/icons/image-plus.svg
create mode 100644 tgui/packages/tgfont/icons/sound-minus.svg
create mode 100644 tgui/packages/tgfont/icons/sound-plus.svg
delete mode 100644 tgui/packages/tgui-dev-server/index.esm.js
rename tgui/packages/tgui-dev-server/link/{client.js => client.cjs} (94%)
rename tgui/packages/{common => tgui-dev-server}/logging.js (100%)
create mode 100644 tgui/packages/tgui-dev-server/require.js
create mode 100644 tgui/packages/tgui/components/InfinitePlane.js
create mode 100644 tgui/packages/tgui/components/Popper.tsx
delete mode 100644 tgui/packages/tgui/components/Tooltip.js
create mode 100644 tgui/packages/tgui/components/Tooltip.tsx
create mode 100644 tgui/packages/tgui/interfaces/AdventureBrowser.tsx
create mode 100644 tgui/packages/tgui/interfaces/AntagInfoBrainwashed.tsx
create mode 100644 tgui/packages/tgui/interfaces/AntagInfoMorph.tsx
create mode 100644 tgui/packages/tgui/interfaces/AntagInfoNightmare.tsx
create mode 100644 tgui/packages/tgui/interfaces/AntagInfoTraitor.tsx
create mode 100644 tgui/packages/tgui/interfaces/AntagInfoWizard.tsx
create mode 100644 tgui/packages/tgui/interfaces/Changelog.js
create mode 100644 tgui/packages/tgui/interfaces/CircuitAdminPanel.tsx
create mode 100644 tgui/packages/tgui/interfaces/CircuitModule.js
create mode 100644 tgui/packages/tgui/interfaces/ComponentPrinter.tsx
create mode 100644 tgui/packages/tgui/interfaces/IntegratedCircuit/BasicInput.js
create mode 100644 tgui/packages/tgui/interfaces/IntegratedCircuit/CircuitInfo.js
create mode 100644 tgui/packages/tgui/interfaces/IntegratedCircuit/Connections.js
create mode 100644 tgui/packages/tgui/interfaces/IntegratedCircuit/DisplayName.js
create mode 100644 tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.js
create mode 100644 tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.js
create mode 100644 tgui/packages/tgui/interfaces/IntegratedCircuit/Port.js
create mode 100644 tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.js
create mode 100644 tgui/packages/tgui/interfaces/IntegratedCircuit/constants.js
create mode 100644 tgui/packages/tgui/interfaces/IntegratedCircuit/index.js
create mode 100644 tgui/packages/tgui/interfaces/NoticeBoard.js
create mode 100644 tgui/packages/tgui/interfaces/NtosPortraitPrinter.js
create mode 100644 tgui/packages/tgui/interfaces/NtosSignaler.js
create mode 100644 tgui/packages/tgui/interfaces/SDQLSpellMenu.js
create mode 100644 tgui/packages/tgui/interfaces/SentienceFunBalloon.js
create mode 100644 tgui/packages/tgui/interfaces/SupermatterMonitor.js
rename tgui/packages/tgui/interfaces/{Vending.js => Vending.tsx} (64%)
create mode 100644 tgui/packages/tgui/stories/Popper.stories.js
create mode 100644 tgui/packages/tgui/styles/atomic/links.scss
create mode 100644 tgui/packages/tgui/styles/interfaces/Changelog.scss
create mode 100644 tgui/packages/tgui/styles/interfaces/IntegratedCircuit.scss
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 637c7f1b178b..0853e650da19 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -417,6 +417,12 @@
///from /obj/machinery/obj_break(damage_flag): (damage_flag)
#define COMSIG_MACHINERY_BROKEN "machinery_broken"
+// /obj/machinery/power/supermatter_crystal signals
+/// from /obj/machinery/power/supermatter_crystal/process_atmos(); when the SM delam reaches the point of sounding alarms
+#define COMSIG_SUPERMATTER_DELAM_START_ALARM "sm_delam_start_alarm"
+/// from /obj/machinery/power/supermatter_crystal/process_atmos(); when the SM sounds an audible alarm
+#define COMSIG_SUPERMATTER_DELAM_ALARM "sm_delam_alarm"
+
// /obj/item signals
#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user)
#define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob)
diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm
index 762df4247246..b25ef763cf41 100644
--- a/code/__DEFINES/machines.dm
+++ b/code/__DEFINES/machines.dm
@@ -48,32 +48,39 @@
#define MC_CHARGE "CHARGE"
#define MC_AI "AI"
#define MC_SENSORS "SENSORS"
+#define MC_SIGNALER "SIGNALER"
//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)
-#define NTNET_SOFTWAREDOWNLOAD 1 // Downloads of software from NTNet
-#define NTNET_PEERTOPEER 2 // P2P transfers of files between devices
-#define NTNET_COMMUNICATION 3 // Communication (messaging)
-#define NTNET_SYSTEMCONTROL 4 // Control of various systems, RCon, air alarm control, etc.
+#define NTNET_SOFTWAREDOWNLOAD 1 // Downloads of software from NTNet
+#define NTNET_PEERTOPEER 2 // P2P transfers of files between devices
+#define NTNET_COMMUNICATION 3 // Communication (messaging)
+#define NTNET_SYSTEMCONTROL 4 // Control of various systems, RCon, air alarm control, etc.
//NTNet transfer speeds, used when downloading/uploading a file/program.
-#define NTNETSPEED_LOWSIGNAL 0.5 // GQ/s transfer speed when the device is wirelessly connected and on Low signal
-#define NTNETSPEED_HIGHSIGNAL 1 // GQ/s transfer speed when the device is wirelessly connected and on High signal
-#define NTNETSPEED_ETHERNET 2 // GQ/s transfer speed when the device is using wired connection
+#define NTNETSPEED_LOWSIGNAL 0.5 // GQ/s transfer speed when the device is wirelessly connected and on Low signal
+#define NTNETSPEED_HIGHSIGNAL 1 // GQ/s transfer speed when the device is wirelessly connected and on High signal
+#define NTNETSPEED_ETHERNET 2 // GQ/s transfer speed when the device is using wired connection
//Caps for NTNet logging. Less than 10 would make logging useless anyway, more than 500 may make the log browser too laggy. Defaults to 100 unless user changes it.
#define MAX_NTNET_LOGS 300
#define MIN_NTNET_LOGS 10
//Program bitflags
-#define PROGRAM_ALL (~0)
-#define PROGRAM_CONSOLE (1<<0)
-#define PROGRAM_LAPTOP (1<<1)
-#define PROGRAM_TABLET (1<<2)
+#define PROGRAM_ALL (~0)
+#define PROGRAM_CONSOLE (1<<0)
+#define PROGRAM_LAPTOP (1<<1)
+#define PROGRAM_TABLET (1<<2)
//Program states
#define PROGRAM_STATE_KILLED 0
#define PROGRAM_STATE_BACKGROUND 1
#define PROGRAM_STATE_ACTIVE 2
+//Program categories
+#define PROGRAM_CATEGORY_CREW "Crew"
+#define PROGRAM_CATEGORY_ENGI "Engineering"
+#define PROGRAM_CATEGORY_ROBO "Robotics"
+#define PROGRAM_CATEGORY_SUPL "Supply"
+#define PROGRAM_CATEGORY_MISC "Other"
#define FIREDOOR_OPEN 1
#define FIREDOOR_CLOSED 2
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 14382eb09c6c..1410a5d99a96 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -323,6 +323,7 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
#define FIRST_DIAG_STEP 1
#define SECOND_DIAG_STEP 2
+#define DEADCHAT_ANNOUNCEMENT "announcement"
#define DEADCHAT_ARRIVALRATTLE "arrivalrattle"
#define DEADCHAT_DEATHRATTLE "deathrattle"
#define DEADCHAT_REGULAR "regular-deadchat"
diff --git a/code/controllers/subsystem/sound_loops.dm b/code/controllers/subsystem/sound_loops.dm
new file mode 100644
index 000000000000..c1e6d097d623
--- /dev/null
+++ b/code/controllers/subsystem/sound_loops.dm
@@ -0,0 +1,3 @@
+PROCESSING_SUBSYSTEM_DEF(sound_loops)
+ name = "Sound Loops"
+ priority = FIRE_PRIORITY_SOUND_LOOPS
diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm
index 03c23e11f6a8..14b63065428f 100644
--- a/code/datums/chatmessage.dm
+++ b/code/datums/chatmessage.dm
@@ -125,7 +125,7 @@
// Append radio icon if from a virtual speaker
if (extra_classes.Find("virtual-speaker"))
- var/image/r_icon = image('icons/UI_Icons/chat/chat_icons.dmi', icon_state = "radio")
+ var/image/r_icon = image('icons/ui_icons/chat/chat_icons.dmi', icon_state = "radio")
text = "\icon[r_icon] " + text
// We dim italicized text to make it more distinguishable from regular text
diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm
index 6af3b3c993b9..e8c6bc4d22f5 100644
--- a/code/datums/looping_sounds/_looping_sound.dm
+++ b/code/datums/looping_sounds/_looping_sound.dm
@@ -1,21 +1,21 @@
/*
- output_atoms (list of atoms) The destination(s) for the sounds
+ output_atoms (list of atoms) The destination(s) for the sounds
- mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end.
- mid_length (num) The length to wait between playing mid_sounds
+ mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end.
+ mid_length (num) The length to wait between playing mid_sounds
- start_sound (soundfile) Played before starting the mid_sounds loop
- start_length (num) How long to wait before starting the main loop after playing start_sound
+ start_sound (soundfile) Played before starting the mid_sounds loop
+ start_length (num) How long to wait before starting the main loop after playing start_sound
- end_sound (soundfile) The sound played after the main loop has concluded
+ end_sound (soundfile) The sound played after the main loop has concluded
- chance (num) Chance per loop to play a mid_sound
- volume (num) Sound output volume
- max_loops (num) The max amount of loops to run for.
- direct (bool) If true plays directly to provided atoms instead of from them
+ chance (num) Chance per loop to play a mid_sound
+ volume (num) Sound output volume
+ max_loops (num) The max amount of loops to run for.
+ direct (bool) If true plays directly to provided atoms instead of from them
*/
/datum/looping_sound
- var/list/atom/output_atoms
+ var/atom/parent
var/mid_sounds
var/mid_length
///Override for volume of start sound
@@ -34,38 +34,46 @@
var/falloff_exponent
var/timerid
var/falloff_distance
+ var/skip_starting_sounds = FALSE
+ var/loop_started = FALSE
-/datum/looping_sound/New(list/_output_atoms=list(), start_immediately=FALSE, _direct=FALSE)
+/datum/looping_sound/New(_parent, start_immediately=FALSE, _direct=FALSE, _skip_starting_sounds = FALSE)
if(!mid_sounds)
WARNING("A looping sound datum was created without sounds to play.")
return
- output_atoms = _output_atoms
+ set_parent(_parent)
direct = _direct
+ skip_starting_sounds = _skip_starting_sounds
if(start_immediately)
start()
/datum/looping_sound/Destroy()
- stop()
- output_atoms = null
+ stop(TRUE)
return ..()
-/datum/looping_sound/proc/start(atom/add_thing)
- if(add_thing)
- output_atoms |= add_thing
+/datum/looping_sound/proc/start(on_behalf_of)
+ if(on_behalf_of)
+ set_parent(on_behalf_of)
if(timerid)
return
on_start()
-/datum/looping_sound/proc/stop(atom/remove_thing)
- if(remove_thing)
- output_atoms -= remove_thing
+/datum/looping_sound/proc/stop(null_parent)
+ if(null_parent)
+ set_parent(null)
if(!timerid)
return
on_stop()
- deltimer(timerid)
+ deltimer(timerid, SSsound_loops)
timerid = null
+ loop_started = FALSE
+
+/datum/looping_sound/proc/start_sound_loop()
+ loop_started = TRUE
+ sound_loop()
+ timerid = addtimer(CALLBACK(src, .proc/sound_loop, world.time), mid_length, TIMER_CLIENT_TIME | TIMER_STOPPABLE | TIMER_LOOP | TIMER_DELETE_ME, SSsound_loops)
/datum/looping_sound/proc/sound_loop(starttime)
if(max_loops && world.time >= starttime + mid_length * max_loops)
@@ -73,21 +81,15 @@
return
if(!chance || prob(chance))
play(get_sound(starttime))
- if(!timerid)
- timerid = addtimer(CALLBACK(src, .proc/sound_loop, world.time), mid_length, TIMER_CLIENT_TIME | TIMER_STOPPABLE | TIMER_LOOP)
/datum/looping_sound/proc/play(soundfile, volume_override)
- var/list/atoms_cache = output_atoms
var/sound/S = sound(soundfile)
if(direct)
S.channel = SSsounds.random_available_channel()
S.volume = volume_override || volume //Use volume as fallback if theres no override
- for(var/i in 1 to atoms_cache.len)
- var/atom/thing = atoms_cache[i]
- if(direct)
- SEND_SOUND(thing, S)
- else
- playsound(thing, S, volume, vary, extra_range, falloff_exponent = falloff_exponent, falloff_distance = falloff_distance)
+ SEND_SOUND(parent, S)
+ else
+ playsound(parent, S, volume, vary, extra_range, falloff_exponent = falloff_exponent, falloff_distance = falloff_distance)
/datum/looping_sound/proc/get_sound(starttime, _mid_sounds)
. = _mid_sounds || mid_sounds
@@ -96,11 +98,22 @@
/datum/looping_sound/proc/on_start()
var/start_wait = 0
- if(start_sound)
+ if(start_sound && !skip_starting_sounds)
play(start_sound, start_volume)
start_wait = start_length
- addtimer(CALLBACK(src, .proc/sound_loop), start_wait, TIMER_CLIENT_TIME)
+ timerid = addtimer(CALLBACK(src, .proc/start_sound_loop), start_wait, TIMER_CLIENT_TIME | TIMER_DELETE_ME | TIMER_STOPPABLE, SSsound_loops)
/datum/looping_sound/proc/on_stop()
- if(end_sound)
+ if(end_sound && loop_started)
play(end_sound, end_volume)
+
+/datum/looping_sound/proc/set_parent(new_parent)
+ if(parent)
+ UnregisterSignal(parent, COMSIG_PARENT_QDELETING)
+ parent = new_parent
+ if(parent)
+ RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/handle_parent_del)
+
+/datum/looping_sound/proc/handle_parent_del(datum/source)
+ SIGNAL_HANDLER
+ set_parent(null)
diff --git a/code/game/atoms_movement.dm b/code/game/atoms_movement.dm
index 4f07ff6f954e..43a22c9df000 100644
--- a/code/game/atoms_movement.dm
+++ b/code/game/atoms_movement.dm
@@ -48,8 +48,17 @@
continue
var/atom/movable/thing = i
thing.Crossed(src)
-//
-////////////////////////////////////////
+
+/**
+ * meant for movement with zero side effects. only use for objects that are supposed to move "invisibly" (like camera mobs or ghosts)
+ * if you want something to move onto a tile with a beartrap or recycler or tripmine or mouse without that object knowing about it at all, use this
+ * most of the time you want forceMove()
+ */
+/atom/movable/proc/abstract_move(atom/new_loc)
+ var/atom/old_loc = loc
+ // move_stacks++
+ loc = new_loc
+ Moved(old_loc)
/atom/movable/Move(atom/newloc, direct, glide_size_override = 0)
var/atom/movable/pullee = pulling
diff --git a/code/game/communications.dm b/code/game/communications.dm
index 696b942434f7..2b45fc9469c4 100644
--- a/code/game/communications.dm
+++ b/code/game/communications.dm
@@ -193,7 +193,9 @@ GLOBAL_LIST_INIT(reverseradiochannels, list(
var/frequency = 0
var/transmission_method
var/list/data
+ var/logging_data
-/datum/signal/New(data, transmission_method = TRANSMISSION_RADIO)
+/datum/signal/New(data, transmission_method = TRANSMISSION_RADIO, logging_data = null)
src.data = data || list()
src.transmission_method = transmission_method
+ src.logging_data = logging_data
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 822302789fde..5dcbd9430b2c 100755
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -44,6 +44,7 @@
/obj/machinery/computer/communications/Initialize()
. = ..()
GLOB.shuttle_caller_list += src
+ AddComponent(/datum/component/gps, "Secured Communications Signal")
/// Are we NOT a silicon, AND we're logged in as the captain?
/obj/machinery/computer/communications/proc/authenticated_as_non_silicon_captain(mob/user)
@@ -79,13 +80,11 @@
if (obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
- SSshuttle.shuttle_purchase_requirements_met |= "emagged"
if (authenticated)
authorize_access = get_all_accesses()
- to_chat(user, "You scramble the communication routing circuits!")
+ to_chat(user, span_danger("You scramble the communication routing circuits!"))
playsound(src, 'sound/machines/terminal_alert.ogg', 50, FALSE)
SSshuttle.shuttle_purchase_requirements_met["emagged"] = TRUE
- return
/obj/machinery/computer/communications/ui_act(action, list/params)
var/static/list/approved_states = list(STATE_BUYING_SHUTTLE, STATE_CHANGING_STATUS, STATE_MAIN, STATE_MESSAGES)
@@ -104,6 +103,7 @@
if ("answerMessage")
if (!authenticated(usr))
return
+
var/answer_index = params["answer"]
var/message_index = params["message"]
@@ -136,11 +136,11 @@
var/obj/item/held_item = usr.get_active_held_item()
var/obj/item/card/id/id_card = held_item?.GetID()
if (!istype(id_card))
- to_chat(usr, "You need to swipe your ID!")
+ to_chat(usr, span_warning("You need to swipe your ID!"))
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return
if (!(ACCESS_CAPTAIN in id_card.access))
- to_chat(usr, "You are not authorized to do this!")
+ to_chat(usr, span_warning("You are not authorized to do this!"))
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return
@@ -152,26 +152,28 @@
set_security_level(new_sec_level)
- to_chat(usr, "Authorization confirmed. Modifying security level.")
+ to_chat(usr, span_notice("Authorization confirmed. Modifying security level."))
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
// Only notify people if an actual change happened
log_game("[key_name(usr)] has changed the security level to [params["newSecurityLevel"]] with [src] at [AREACOORD(usr)].")
message_admins("[ADMIN_LOOKUPFLW(usr)] has changed the security level to [params["newSecurityLevel"]] with [src] at [AREACOORD(usr)].")
- deadchat_broadcast(" has changed the security level to [params["newSecurityLevel"]] with [src] at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr)
+ deadchat_broadcast(" has changed the security level to [params["newSecurityLevel"]] with [src] at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type=DEADCHAT_ANNOUNCEMENT)
alert_level_tick += 1
if ("deleteMessage")
if (!authenticated(usr))
return
- var/message_index = params["message"]
-
- if(!isnum(message_index))
- message_admins("[ADMIN_LOOKUPFLW(usr)] provided an invalid index type when deleting a message on [src] [ADMIN_JMP(src)]. This should not happen. Please check with a maintainer and/or consult tgui logs.")
- CRASH("Non-numeric index provided when deleting comms console message.")
+ var/message_index = text2num(params["message"])
if (!message_index)
return
LAZYREMOVE(messages, LAZYACCESS(messages, message_index))
+ if ("emergency_meeting")
+ if(!(SSevents.holidays && SSevents.holidays[APRIL_FOOLS]))
+ return
+ if (!authenticated_as_silicon_or_captain(usr))
+ return
+ // emergency_meeting(usr)
if ("makePriorityAnnouncement")
if (!authenticated_as_silicon_or_captain(usr))
return
@@ -188,32 +190,36 @@
var/emagged = obj_flags & EMAGGED
if (emagged)
message_syndicate(message, usr)
- to_chat(usr, "SYSERR @l(19833)of(transmit.dm): !@$ MESSAGE TRANSMITTED TO SYNDICATE COMMAND.")
+ to_chat(usr, span_danger("SYSERR @l(19833)of(transmit.dm): !@$ MESSAGE TRANSMITTED TO SYNDICATE COMMAND."))
else
message_centcom(message, usr)
- to_chat(usr, "Message transmitted to Central Command.")
+ to_chat(usr, span_notice("Message transmitted to Central Command."))
var/associates = emagged ? "the Syndicate": "CentCom"
usr.log_talk(message, LOG_SAY, tag = "message to [associates]")
- deadchat_broadcast(" has messaged [associates], \"[message]\" at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr)
+ deadchat_broadcast(" has messaged [associates], \"[message]\" at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
COOLDOWN_START(src, important_action_cooldown, IMPORTANT_ACTION_COOLDOWN)
if ("purchaseShuttle")
var/can_buy_shuttles_or_fail_reason = can_buy_shuttles(usr)
if (can_buy_shuttles_or_fail_reason != TRUE)
if (can_buy_shuttles_or_fail_reason != FALSE)
- to_chat(usr, "[can_buy_shuttles_or_fail_reason]")
+ to_chat(usr, span_alert("[can_buy_shuttles_or_fail_reason]"))
return
var/list/shuttles = flatten_list(SSmapping.shuttle_templates)
var/datum/map_template/shuttle/shuttle = locate(params["shuttle"]) in shuttles
if (!istype(shuttle))
return
+ // if (!can_purchase_this_shuttle(shuttle))
+ // return
if (!shuttle.prerequisites_met())
- to_chat(usr, "You have not met the requirements for purchasing this shuttle.")
+ to_chat(usr, span_alert("You have not met the requirements for purchasing this shuttle."))
return
var/datum/bank_account/bank_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
if (bank_account.account_balance < shuttle.credit_cost)
return
SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_PURCHASED
+ // for(var/datum/round_event_control/shuttle_insurance/insurance_event in SSevents.control)
+ // insurance_event.weight *= 20
SSshuttle.unload_preview()
SSshuttle.existing_shuttle = SSshuttle.emergency
SSshuttle.action_load(shuttle, replace = TRUE)
@@ -235,7 +241,7 @@
return
var/reason = trim(html_encode(params["reason"]), MAX_MESSAGE_LEN)
nuke_request(reason, usr)
- to_chat(usr, "Request sent.")
+ to_chat(usr, span_notice("Request sent."))
usr.log_message("has requested the nuclear codes from CentCom with reason \"[reason]\"", LOG_SAY)
priority_announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self-Destruct Codes Requested", "commandreport")
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
@@ -245,7 +251,7 @@
return
if (!(obj_flags & EMAGGED))
return
- to_chat(usr, "Backup routing data restored.")
+ to_chat(usr, span_notice("Backup routing data restored."))
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
obj_flags &= ~EMAGGED
if ("sendToOtherSector")
@@ -256,7 +262,7 @@
if (!COOLDOWN_FINISHED(src, important_action_cooldown))
return
- var/message = trim(html_encode(params["message"]), MAX_MESSAGE_LEN)
+ var/message = trim(params["message"], MAX_MESSAGE_LEN)
if (!message)
return
@@ -268,12 +274,13 @@
var/network_name = CONFIG_GET(string/cross_comms_network)
if (network_name)
payload["network"] = network_name
+ payload["sender_ckey"] = usr.ckey
- send2otherserver(station_name(), message, "Comms_Console", destination == "all" ? null : list(destination), additional_data = payload)
+ send2otherserver(html_decode(station_name()), message, "Comms_Console", destination == "all" ? null : list(destination), additional_data = payload)
minor_announce(message, title = "Outgoing message to allied station")
usr.log_talk(message, LOG_SAY, tag = "message to the other server")
message_admins("[ADMIN_LOOKUPFLW(usr)] has sent a message to the other server\[s].")
- deadchat_broadcast(" has sent an outgoing message to the other station(s).", "[usr.real_name]", usr)
+ deadchat_broadcast(" has sent an outgoing message to the other station(s).", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
COOLDOWN_START(src, important_action_cooldown, IMPORTANT_ACTION_COOLDOWN)
if ("setState")
@@ -315,7 +322,7 @@
authenticated = TRUE
authorize_access = get_all_accesses()
authorize_name = "Unknown"
- to_chat(usr, "[src] lets out a quiet alarm as its login is overridden.")
+ to_chat(usr, span_warning("[src] lets out a quiet alarm as its login is overridden."))
playsound(src, 'sound/machines/terminal_alert.ogg', 25, FALSE)
else if(isliving(usr))
var/mob/living/L = usr
@@ -334,22 +341,36 @@
revoke_maint_all_access()
log_game("[key_name(usr)] disabled emergency maintenance access.")
message_admins("[ADMIN_LOOKUPFLW(usr)] disabled emergency maintenance access.")
- deadchat_broadcast(" disabled emergency maintenance access at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr)
+ deadchat_broadcast(" disabled emergency maintenance access at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
else
make_maint_all_access()
log_game("[key_name(usr)] enabled emergency maintenance access.")
message_admins("[ADMIN_LOOKUPFLW(usr)] enabled emergency maintenance access.")
- deadchat_broadcast(" enabled emergency maintenance access at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr)
+ deadchat_broadcast(" enabled emergency maintenance access at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
/obj/machinery/computer/communications/ui_data(mob/user)
var/list/data = list(
"authenticated" = FALSE,
"emagged" = FALSE,
- "hasConnection" = has_communication(),
)
var/ui_state = issilicon(user) ? cyborg_state : state
+ var/has_connection = has_communication()
+ data["hasConnection"] = has_connection
+
+ // if(!SSjob.assigned_captain && !SSjob.safe_code_requested && SSid_access.spare_id_safe_code && has_connection)
+ // data["canRequestSafeCode"] = TRUE
+ // data["safeCodeDeliveryWait"] = 0
+ // else
+ // data["canRequestSafeCode"] = FALSE
+ // if(SSjob.safe_code_timer_id && has_connection)
+ // data["safeCodeDeliveryWait"] = timeleft(SSjob.safe_code_timer_id)
+ // data["safeCodeDeliveryArea"] = get_area(SSjob.safe_code_request_loc)
+ // else
+ // data["safeCodeDeliveryWait"] = 0
+ // data["safeCodeDeliveryArea"] = null
+
if (authenticated || issilicon(user))
data["authenticated"] = TRUE
data["canLogOut"] = !issilicon(user)
@@ -371,16 +392,17 @@
data["importantActionReady"] = COOLDOWN_FINISHED(src, important_action_cooldown)
data["shuttleCalled"] = FALSE
data["shuttleLastCalled"] = FALSE
-
+ data["aprilFools"] = SSevents.holidays && SSevents.holidays[APRIL_FOOLS]
data["alertLevel"] = NUM2SECLEVEL(GLOB.security_level)
data["authorizeName"] = authorize_name
data["canLogOut"] = !issilicon(user)
- data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac(user, TRUE)
+ data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac(user)
if (authenticated_as_non_silicon_captain(user))
data["canRequestNuke"] = TRUE
if (authenticated_as_non_silicon_command(user))
data["canMessageAssociates"] = TRUE
+
if (can_send_messages_to_other_sectors(user))
data["canSendToSectors"] = TRUE
@@ -428,8 +450,13 @@
for (var/shuttle_id in SSmapping.shuttle_templates)
var/datum/map_template/shuttle/shuttle_template = SSmapping.shuttle_templates[shuttle_id]
- if (!shuttle_template.can_be_bought || shuttle_template.credit_cost == INFINITY)
+
+ if (shuttle_template.credit_cost == INFINITY)
+ continue
+
+ if (!shuttle_template.can_be_bought)
continue
+
shuttles += list(list(
"name" = shuttle_template.name,
"description" = shuttle_template.description,
@@ -478,6 +505,7 @@
return FALSE
if (!authenticated_as_non_silicon_captain(user))
return FALSE
+
if (SSshuttle.emergency.mode != SHUTTLE_RECALL && SSshuttle.emergency.mode != SHUTTLE_IDLE)
return "The shuttle is already in transit."
if (SSshuttle.shuttle_purchased == SHUTTLEPURCHASE_PURCHASED)
@@ -492,21 +520,38 @@
return length(CONFIG_GET(keyed_list/cross_server)) > 0
+/**
+ * Call an emergency meeting
+ *
+ * Comm Console wrapper for the Communications subsystem wrapper for the call_emergency_meeting world proc.
+ * Checks to make sure the proc can be called, and handles relevant feedback, logging and timing.
+ * See the SScommunications proc definition for more detail, in short, teleports the entire crew to
+ * the bridge for a meetup. Should only really happen during april fools.
+ * Arguments:
+ * * user - Mob who called the meeting
+ */
+/obj/machinery/computer/communications/proc/emergency_meeting(mob/living/user)
+ // if(!SScommunications.can_make_emergency_meeting(user))
+ // to_chat(user, span_alert("The emergency meeting button doesn't seem to work right now. Please stand by."))
+ // return
+ // SScommunications.emergency_meeting(user)
+ // deadchat_broadcast(" called an emergency meeting from [span_name("[get_area_name(usr, TRUE)]")].", span_name("[user.real_name]"), user, message_type=DEADCHAT_ANNOUNCEMENT)
+
/obj/machinery/computer/communications/proc/make_announcement(mob/living/user)
var/is_ai = issilicon(user)
if(!SScommunications.can_announce(user, is_ai))
- to_chat(user, "Intercomms recharging. Please stand by.")
+ to_chat(user, span_alert("Intercomms recharging. Please stand by."))
return
var/input = stripped_input(user, "Please choose a message to announce to the station crew.", "What?")
if(!input || !user.canUseTopic(src, !issilicon(usr)))
return
if(!(user.can_speak())) //No more cheating, mime/random mute guy!
input = "..."
- to_chat(user, "You find yourself unable to speak.")
+ to_chat(user, span_warning("You find yourself unable to speak."))
else
input = user.treat_message(input) //Adds slurs and so on. Someone should make this use languages too.
SScommunications.make_announcement(user, is_ai, input)
- deadchat_broadcast(" made a priority announcement from [get_area_name(usr, TRUE)].", "[user.real_name]", user)
+ deadchat_broadcast(" made a priority announcement from [span_name("[get_area_name(usr, TRUE)]")].", user, src.loc, message_type=DEADCHAT_ANNOUNCEMENT)
/obj/machinery/computer/communications/proc/post_status(command, data1, data2)
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 9474320894d4..461a2888ab39 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -1,3 +1,5 @@
+#define AHELP_FIRST_MESSAGE "Please adminhelp before leaving the round, even if there are no administrators online!"
+
/*
* Cryogenic refrigeration unit. Basically a despawner.
* Stealing a lot of concepts/code from sleepers due to massive laziness.
@@ -5,170 +7,71 @@
* since time_entered, which is world.time when the occupant moves in.
* ~ Zuhayr
*/
+GLOBAL_LIST_EMPTY(cryopod_computers)
//Main cryopod console.
/obj/machinery/computer/cryopod
name = "cryogenic oversight console"
desc = "An interface between crew and the cryogenic storage oversight systems."
- icon = 'icons/obj/Cryogenic2.dmi'
+ icon = 'icons/obj/machines/cryopod.dmi'
icon_state = "cellconsole_1"
- circuit = /obj/item/circuitboard/cryopodcontrol
+ icon_keyboard = null
+ // circuit = /obj/item/circuitboard/cryopodcontrol
density = FALSE
interaction_flags_machine = INTERACT_MACHINE_OFFLINE
- req_one_access = list(ACCESS_HEADS, ACCESS_ARMORY) //Heads of staff or the warden can go here to claim recover items from their department that people went were cryodormed with.
-
- var/menu = 1 //Which menu screen to display
+ req_one_access = list(ACCESS_HEADS, ACCESS_ARMORY) // Heads of staff or the warden can go here to claim recover items from their department that people went were cryodormed with.
+ var/mode = null
- //Used for logging people entering cryosleep and important items they are carrying.
+ // Used for logging people entering cryosleep and important items they are carrying.
var/list/frozen_crew = list()
- var/list/obj/stored_packages = list()
- var/allow_items = TRUE
+ var/storage_type = "crewmembers"
+ var/storage_name = "Cryogenic Oversight Control"
-/obj/machinery/computer/cryopod/deconstruct()
+/obj/machinery/computer/cryopod/Initialize()
. = ..()
- for(var/i in stored_packages)
- var/obj/O = i
- O.forceMove(drop_location())
-
-/obj/machinery/computer/cryopod/attack_ai()
- attack_hand()
-
-/obj/machinery/computer/cryopod/ui_interact(mob/user = usr)
- if(!is_operational())
- return
-
- user.set_machine(src)
- add_fingerprint(user)
-
- var/dat = ""
-
- dat += "
Welcome, [user.real_name].
"
- dat += "
"
-
- switch(src.menu)
- if(1)
- dat += "View crew storage log
"
- if(allow_items)
- dat += "View objects storage log
"
- dat += "Recover object
"
- dat += "Recover all objects
"
- if(2)
- dat += "<< Back
"
- dat += "Recently stored Crew
"
- if(!frozen_crew.len)
- dat += "There has been no storage usage at this terminal.
"
- else
- for(var/person in frozen_crew)
- dat += "[person]
"
- dat += "
"
- if(3)
- dat += "<< Back
"
- dat += "Recently stored objects
"
- if(!stored_packages.len)
- dat += "There has been no storage usage at this terminal.
"
- else
- for(var/obj/O in stored_packages)
- dat += "[O.name]
"
- dat += "
"
+ GLOB.cryopod_computers += src
- var/datum/browser/popup = new(user, "cryopod_console", "Cryogenic System Control")
- popup.set_content(dat)
- popup.open()
+/obj/machinery/computer/cryopod/Destroy()
+ GLOB.cryopod_computers -= src
+ return ..()
-/obj/machinery/computer/cryopod/Topic(href, href_list)
- if(..())
- return TRUE
+/obj/machinery/computer/cryopod/update_icon_state()
+ if(stat & (NOPOWER|BROKEN))
+ icon_state = "cellconsole"
+ return ..()
+ icon_state = "cellconsole_1"
+ return ..()
- var/mob/user = usr
+/obj/machinery/computer/cryopod/ui_interact(mob/user, datum/tgui/ui)
+ if(stat & (NOPOWER|BROKEN))
+ return
add_fingerprint(user)
- if(href_list["item"])
- if(!allowed(user) && !(obj_flags & EMAGGED))
- to_chat(user, "Access Denied.")
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- updateUsrDialog()
- return
-
- if(!allow_items)
- return
-
- if(stored_packages.len == 0)
- to_chat(user, "There is nothing to recover from storage.")
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- updateUsrDialog()
- return
-
- var/obj/I = tgui_input_list(user, "Please choose which object to retrieve.","Object recovery", stored_packages)
- playsound(src, "terminal_type", 25, 0)
- if(!I)
- return
-
- if(!(I in stored_packages))
- to_chat(user, "\The [I] is no longer in storage.")
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- updateUsrDialog()
- return
-
- visible_message("The console beeps happily as it disgorges \the [I].")
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
-
- I.forceMove(drop_location())
- if(user && Adjacent(user) && user.can_hold_items())
- user.put_in_hands(I)
- stored_packages -= I
- updateUsrDialog()
-
- else if(href_list["allitems"])
- playsound(src, "terminal_type", 25, 0)
- if(!allowed(user) && !(obj_flags & EMAGGED))
- to_chat(user, "Access Denied.")
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- updateUsrDialog()
- return
-
- if(!allow_items)
- return
-
- if(stored_packages.len == 0)
- to_chat(user, "There is nothing to recover from storage.")
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- return
-
- visible_message("The console beeps happily as it disgorges the desired objects.")
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
-
- for(var/obj/O in stored_packages)
- O.forceMove(get_turf(src))
- stored_packages.Cut()
- updateUsrDialog()
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "CryopodConsole", name)
+ ui.open()
- else if (href_list["menu"])
- src.menu = text2num(href_list["menu"])
- playsound(src, "terminal_type", 25, 0)
- updateUsrDialog()
+/obj/machinery/computer/cryopod/ui_data(mob/user)
+ var/list/data = list()
+ data["frozen_crew"] = frozen_crew
- ui_interact(usr)
- updateUsrDialog()
- return
+ var/obj/item/card/id/id_card
+ var/datum/bank_account/current_user
+ if(isliving(user))
+ var/mob/living/person = user
+ id_card = person.get_idcard()
+ if(id_card?.registered_account)
+ current_user = id_card.registered_account
+ if(current_user)
+ data["account_name"] = current_user.account_holder
-/obj/item/circuitboard/cryopodcontrol
- name = "Circuit board (Cryogenic Oversight Console)"
- build_path = "/obj/machinery/computer/cryopod"
+ return data
-/obj/machinery/computer/cryopod/contents_explosion()
- return
-
-/obj/machinery/computer/cryopod/contents_explosion()
- return //don't blow everyone's shit up.
-
-/// The box
-/obj/item/storage/box/blue/cryostorage_items
- w_class = WEIGHT_CLASS_HUGE
-
-//Cryopods themselves.
+// Cryopods themselves.
/obj/machinery/cryopod
name = "cryogenic freezer"
desc = "Suited for Cyborgs and Humanoids, the pod is a safe place for personnel affected by the Space Sleep Disorder to get some rest."
@@ -181,47 +84,55 @@
var/on_store_message = "has entered long-term storage."
var/on_store_name = "Cryogenic Oversight"
- // 15 minutes-ish safe period before being despawned.
- var/time_till_despawn = 15 * 600 // This is reduced by 90% if a player manually enters cryo
- var/despawn_world_time = null // Used to keep track of the safe period.
+ /// Time until despawn when a mob enters a cryopod. You cannot other people in pods unless they're catatonic.
+ var/time_till_despawn = 30 SECONDS
+ /// Cooldown for when it's now safe to try an despawn the player.
+ COOLDOWN_DECLARE(despawn_world_time)
- var/obj/machinery/computer/cryopod/control_computer
- var/item_storage_type = /obj/item/storage/box/blue/cryostorage_items //with how storage components work this can be anything the player can open or anything with a storage component.
- var/last_no_computer_message = 0
+ ///Weakref to our controller
+ var/datum/weakref/control_computer_weakref
+ COOLDOWN_DECLARE(last_no_computer_message)
-/obj/machinery/cryopod/Initialize(mapload)
- . = ..()
+/obj/machinery/cryopod/Initialize()
+ ..()
+ return INITIALIZE_HINT_LATELOAD //Gotta populate the cryopod computer GLOB first
+
+/obj/machinery/cryopod/LateInitialize()
update_icon()
- find_control_computer(mapload)
+ find_control_computer()
+
+// This is not a good situation
+/obj/machinery/cryopod/Destroy()
+ control_computer_weakref = null
+ return ..()
/obj/machinery/cryopod/proc/find_control_computer(urgent = FALSE)
- for(var/obj/machinery/computer/cryopod/C in get_area(src))
- control_computer = C
- if(C)
- return C
- break
+ for(var/cryo_console as anything in GLOB.cryopod_computers)
+ var/obj/machinery/computer/cryopod/console = cryo_console
+ if(get_area(console) == get_area(src))
+ control_computer_weakref = WEAKREF(console)
+ break
// Don't send messages unless we *need* the computer, and less than five minutes have passed since last time we messaged
- if(!control_computer && urgent && last_no_computer_message + 5*60*10 < world.time)
+ if(!control_computer_weakref && urgent && COOLDOWN_FINISHED(src, last_no_computer_message))
+ COOLDOWN_START(src, last_no_computer_message, 5 MINUTES)
log_admin("Cryopod in [get_area(src)] could not find control computer!")
message_admins("Cryopod in [get_area(src)] could not find control computer!")
last_no_computer_message = world.time
- return control_computer != null
+ return control_computer_weakref != null
-/obj/machinery/cryopod/close_machine(mob/user)
- if(!control_computer)
+/obj/machinery/cryopod/close_machine(atom/movable/target)
+ if(!control_computer_weakref)
find_control_computer(TRUE)
- if((isnull(user) || istype(user)) && state_open && !panel_open)
- ..(user)
+ if((isnull(target) || isliving(target)) && state_open && !panel_open)
+ ..(target)
var/mob/living/mob_occupant = occupant
- investigate_log("Cryogenics machine closed with occupant [key_name(occupant)] by user [key_name(user)].", INVESTIGATE_CRYOGENICS)
+ investigate_log("Cryogenics machine closed with occupant [key_name(occupant)] by user [key_name(target)].", INVESTIGATE_CRYOGENICS)
if(mob_occupant && mob_occupant.stat != DEAD)
- to_chat(occupant, "You feel cool air surround you. You go numb as your senses turn inward.")
- if(mob_occupant.client)//if they're logged in
- despawn_world_time = world.time + (time_till_despawn * 0.1)
- else
- despawn_world_time = world.time + time_till_despawn
+ to_chat(occupant, span_notice("You feel cool air surround you. You go numb as your senses turn inward."))
+
+ COOLDOWN_START(src, despawn_world_time, time_till_despawn)
icon_state = "cryopod"
/obj/machinery/cryopod/open_machine()
@@ -234,8 +145,8 @@
/obj/machinery/cryopod/container_resist(mob/living/user)
investigate_log("Cryogenics machine container resisted by [key_name(user)] with occupant [key_name(occupant)].", INVESTIGATE_CRYOGENICS)
- visible_message("[occupant] emerges from [src]!",
- "You climb out of [src]!")
+ visible_message(span_notice("[occupant] emerges from [src]!"),
+ span_notice("You climb out of [src]!"))
open_machine()
/obj/machinery/cryopod/relaymove(mob/user)
@@ -246,205 +157,151 @@
return
var/mob/living/mob_occupant = occupant
- if(mob_occupant)
- // Eject dead people
- if(mob_occupant.stat == DEAD)
- open_machine()
+ if(mob_occupant.stat == DEAD)
+ open_machine()
- if(!(world.time > despawn_world_time + 100))//+ 10 seconds
- return
+ if(!mob_occupant.client && COOLDOWN_FINISHED(src, despawn_world_time))
+ if(!control_computer_weakref)
+ find_control_computer(urgent = TRUE)
- if(!mob_occupant.client && mob_occupant.stat < 2) //Occupant is living and has no client.
- if(!control_computer)
- find_control_computer(urgent = TRUE)//better hope you found it this time
+ despawn_occupant()
- despawn_occupant()
+/obj/machinery/cryopod/proc/handle_objectives()
+ var/mob/living/mob_occupant = occupant
+ // Update any existing objectives involving this mob.
+ for(var/datum/objective/objective in GLOB.objectives)
+ // We don't want revs to get objectives that aren't for heads of staff. Letting
+ // them win or lose based on cryo is silly so we remove the objective.
+ if(istype(objective,/datum/objective/mutiny) && objective.target == mob_occupant.mind)
+ objective.team.objectives -= objective
+ qdel(objective)
+ for(var/datum/mind/mind in objective.team.members)
+ to_chat(mind.current, "
[span_userdanger("Your target is no longer within reach. Objective removed!")]")
+ mind.announce_objectives()
+ else if(istype(objective.target) && objective.target == mob_occupant.mind)
+ if(istype(objective, /datum/objective/contract))
+ var/datum/antagonist/traitor/affected_traitor = objective.owner.has_antag_datum(/datum/antagonist/traitor)
+ var/datum/contractor_hub/affected_contractor_hub = affected_traitor.contractor_hub
+ for(var/datum/syndicate_contract/affected_contract as anything in affected_contractor_hub.assigned_contracts)
+ if(affected_contract.contract == objective)
+ affected_contract.generate(affected_contractor_hub.assigned_targets)
+ affected_contractor_hub.assigned_targets.Add(affected_contract.contract.target)
+ to_chat(objective.owner.current, "
[span_userdanger("Contract target out of reach. Contract rerolled.")]")
+ break
+ else
+ var/old_target = objective.target
+ objective.target = null
+ if(!objective)
+ return
+ objective.find_target()
+ if(!objective.target && objective.owner)
+ to_chat(objective.owner.current, "
[span_userdanger("Your target is no longer within reach. Objective removed!")]")
+ for(var/datum/antagonist/antag in objective.owner.antag_datums)
+ antag.objectives -= objective
+ if (!objective.team)
+ objective.update_explanation_text()
+ objective.owner.announce_objectives()
+ to_chat(objective.owner.current, "
[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
+ else
+ var/list/objectivestoupdate
+ for(var/datum/mind/objective_owner in objective.get_owners())
+ to_chat(objective_owner.current, "
[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
+ for(var/datum/objective/update_target_objective in objective_owner.get_all_objectives())
+ LAZYADD(objectivestoupdate, update_target_objective)
+ objectivestoupdate += objective.team.objectives
+ for(var/datum/objective/update_objective in objectivestoupdate)
+ if(update_objective.target != old_target || !istype(update_objective,objective.type))
+ continue
+ update_objective.target = objective.target
+ update_objective.update_explanation_text()
+ to_chat(objective.owner.current, "
[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
+ update_objective.owner.announce_objectives()
+ qdel(objective)
+
+/obj/machinery/cryopod/proc/should_preserve_item(obj/item/item)
+ for(var/datum/objective_item/steal/possible_item in GLOB.possible_items)
+ if(istype(item, possible_item.targetitem))
+ return TRUE
+ return FALSE
// This function can not be undone; do not call this unless you are sure
/obj/machinery/cryopod/proc/despawn_occupant()
- if(!control_computer)
- find_control_computer()
-
var/mob/living/mob_occupant = occupant
+ var/list/crew_member = list()
- var/list/obj/item/storing = list()
- var/list/obj/item/destroying = list()
- var/list/obj/item/destroy_later = list()
-
- investigate_log("Despawning [key_name(mob_occupant)].", INVESTIGATE_CRYOGENICS)
-
- var/atom/target_store = (control_computer?.allow_items && control_computer) || src //the double control computer check makes it return the control computer.
- var/drop_to_ground = !istype(target_store, /obj/machinery/computer/cryopod)
-
- var/mind_identity = mob_occupant.mind?.name
- var/occupant_identity = mob_occupant.real_name
-
- if(iscyborg(mob_occupant))
- var/mob/living/silicon/robot/R = mob_occupant
- if(R.mmi?.brain)
- destroy_later += R.mmi
- destroy_later += R.mmi.brain
- for(var/i in R.module)
- if(!isitem(i))
- destroying += i
- continue
- var/obj/item/I = i
- // let's be honest we only care about the trash bag don't beat around the bush
- if(SEND_SIGNAL(I, COMSIG_CONTAINS_STORAGE))
- storing += I.contents
- for(var/atom/movable/AM in I.contents)
- AM.forceMove(src)
- R.module.remove_module(I, TRUE)
- else
-
- if(ishuman(mob_occupant))
- var/mob/living/carbon/human/H = mob_occupant
- if(H.mind && H.client && H.client.prefs && H == H.mind.original_character)
- H.SaveTCGCards()
-
- var/list/gear = list()
- if(iscarbon(mob_occupant)) // sorry simp-le-mobs deserve no mercy
- var/mob/living/carbon/C = mob_occupant
- gear = C.get_all_gear()
- for(var/i in gear)
- var/obj/item/I = i
- I.forceMove(src)
- if(!istype(I))
- destroying += I
- continue
- if(I.item_flags & (DROPDEL | ABSTRACT))
- destroying += I
- continue
- if(HAS_TRAIT(I, TRAIT_NODROP))
- destroying += I
- continue
- // WEE WOO SNOWFLAKE TIME
- if(istype(I, /obj/item/pda))
- var/obj/item/pda/P = I
- if((P.owner == mind_identity) || (P.owner == occupant_identity))
- destroying += P
- else
- storing += P
- else if(istype(I, /obj/item/card/id))
- var/obj/item/card/id/idcard = I
- if((idcard.registered_name == mind_identity) || (idcard.registered_name == occupant_identity))
- destroying += idcard
- else
- storing += idcard
- else
- storing += I
-
- // get rid of mobs
- for(var/mob/living/L in mob_occupant.GetAllContents() - mob_occupant)
- L.forceMove(drop_location())
-
- if(storing.len)
- var/obj/O = new item_storage_type
- O.name = "cryogenic retrieval package: [mob_occupant.real_name]"
- for(var/i in storing)
- var/obj/item/I = i
- I.forceMove(O)
- O.forceMove(drop_to_ground? target_store.drop_location() : target_store)
- if((target_store == control_computer) && !drop_to_ground)
- control_computer.stored_packages += O
-
- QDEL_LIST(destroying)
-
- //Update any existing objectives involving this mob.
- for(var/i in GLOB.objectives)
- var/datum/objective/O = i
- // We don't want revs to get objectives that aren't for heads of staff. Letting
- // them win or lose based on cryo is silly so we remove the objective.
- if(istype(O,/datum/objective/mutiny) && O.target == mob_occupant.mind)
- qdel(O)
- else if(O.target && istype(O.target, /datum/mind))
- if(O.target != mob_occupant.mind)
- continue
- if(O.check_midround_completion())
- continue
- if(O.owner && O.owner.current)
- to_chat(O.owner.current, "
You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")
- O.target = null
- spawn(10) //This should ideally fire after the occupant is deleted.
- if(!O)
- return
- O.find_target()
- O.update_explanation_text()
- if(!(O.target))
- qdel(O)
+ crew_member["name"] = mob_occupant.real_name
if(mob_occupant.mind)
- //Handle job slot/tater cleanup.
- if(mob_occupant.mind.assigned_role)
- var/job = mob_occupant.mind.assigned_role
- SSjob.FreeRole(job)
+ // Handle job slot/tater cleanup.
+ var/job = mob_occupant.mind.assigned_role
+ crew_member["job"] = job
+ SSjob.FreeRole(job)
+ // if(LAZYLEN(mob_occupant.mind.objectives))
+ // mob_occupant.mind.objectives.Cut()
mob_occupant.mind.special_role = null
+ else
+ crew_member["job"] = "N/A"
// Delete them from datacore.
-
var/announce_rank = null
- for(var/datum/data/record/R in GLOB.data_core.medical)
- if((R.fields["name"] == mob_occupant.real_name))
- qdel(R)
- for(var/datum/data/record/T in GLOB.data_core.security)
- if((T.fields["name"] == mob_occupant.real_name))
- qdel(T)
- for(var/datum/data/record/G in GLOB.data_core.general)
- if((G.fields["name"] == mob_occupant.real_name))
- announce_rank = G.fields["rank"]
- qdel(G)
-
- for(var/obj/machinery/computer/cloning/cloner in world)
- for(var/datum/data/record/R in cloner.records)
- if(R.fields["name"] == mob_occupant.real_name)
- cloner.records.Remove(R)
-
- //Make an announcement and log the person entering storage.
- if(control_computer)
- control_computer.frozen_crew += "[mob_occupant.real_name]"
+ for(var/datum/data/record/medical_record as anything in GLOB.data_core.medical)
+ if(medical_record.fields["name"] == mob_occupant.real_name)
+ qdel(medical_record)
+ for(var/datum/data/record/security_record as anything in GLOB.data_core.security)
+ if(security_record.fields["name"] == mob_occupant.real_name)
+ qdel(security_record)
+ for(var/datum/data/record/general_record as anything in GLOB.data_core.general)
+ if(general_record.fields["name"] == mob_occupant.real_name)
+ announce_rank = general_record.fields["rank"]
+ qdel(general_record)
+
+ var/obj/machinery/computer/cryopod/control_computer = control_computer_weakref?.resolve()
+ if(!control_computer)
+ control_computer_weakref = null
+ else
+ control_computer.frozen_crew += list(crew_member)
+ // Make an announcement and log the person entering storage.
if(GLOB.announcement_systems.len)
var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
announcer.announce("CRYOSTORAGE", mob_occupant.real_name, announce_rank, list())
- visible_message("\The [src] hums and hisses as it moves [mob_occupant.real_name] into storage.")
- // Ghost and delete the mob.
- var/mob/dead/observer/G = mob_occupant.get_ghost(TRUE)
- if(G)
- G.voluntary_ghosted = TRUE
- else
- mob_occupant.ghostize(FALSE, penalize = TRUE, voluntary = TRUE, cryo = TRUE)
+ visible_message(span_notice("[src] hums and hisses as it moves [mob_occupant.real_name] into storage."))
+
+ for(var/obj/item/item_content as anything in mob_occupant)
+ if(!istype(item_content) || HAS_TRAIT(item_content, TRAIT_NODROP))
+ continue
+ mob_occupant.transferItemToLoc(item_content, drop_location(), force = TRUE, silent = TRUE)
+
+ handle_objectives()
QDEL_NULL(occupant)
- QDEL_LIST(destroy_later)
open_machine()
name = initial(name)
/obj/machinery/cryopod/MouseDrop_T(mob/living/target, mob/user)
- if(!istype(target) || user.incapacitated() || !target.Adjacent(user) || !Adjacent(user) || !ismob(target) || (!ishuman(user) && !iscyborg(user)) || !istype(user.loc, /turf) || target.buckled)
+ if(!istype(target) || !can_interact(user) || !target.Adjacent(user) || !ismob(target) || isanimal(target) || !istype(user.loc, /turf) || target.buckled)
return
if(occupant)
- to_chat(user, "The cryo pod is already occupied!")
+ to_chat(user, span_notice("[src] is already occupied!"))
return
if(target.stat == DEAD)
- to_chat(user, "Dead people can not be put into cryo.")
+ to_chat(user, span_notice("Dead people can not be put into cryo."))
return
- if(target.client && user != target)
+ if(target.key && user != target)
if(iscyborg(target))
- to_chat(user, "You can't put [target] into [src]. They're online.")
+ to_chat(user, span_danger("You can't put [target] into [src]. [target.p_theyre(capitalized = TRUE)] online."))
else
- to_chat(user, "You can't put [target] into [src]. They're conscious.")
+ to_chat(user, span_danger("You can't put [target] into [src]. [target.p_theyre(capitalized = TRUE)] conscious."))
return
- else if(target.client)
- if(tgui_alert(target,"Would you like to enter cryosleep?",,list("Yes","No")) == "No")
- return
- var/generic_plsnoleave_message = " Please adminhelp before leaving the round, even if there are no administrators online!"
+ if(target == user && (tgalert(target, "Would you like to enter cryosleep?", "Enter Cryopod?", "Yes", "No") != "Yes"))
+ return
- if(target == user && world.time - target.client.cryo_warned > 5 MINUTES)//if we haven't warned them in the last 5 minutes
+ if(target == user)
var/list/caught_string
var/addendum = ""
if(target.mind.assigned_role in GLOB.command_positions)
@@ -462,30 +319,34 @@
LAZYADD(caught_string, "Revolutionary")
if(caught_string)
- tgui_alert(target, "You're a [english_list(caught_string)]![generic_plsnoleave_message][addendum]")
+ tgui_alert(target, "You're a [english_list(caught_string)]! [AHELP_FIRST_MESSAGE][addendum]")
target.client.cryo_warned = world.time
return
- if(!target || user.incapacitated() || !target.Adjacent(user) || !Adjacent(user) || (!ishuman(user) && !iscyborg(user)) || !istype(user.loc, /turf) || target.buckled)
+ if(!istype(target) || !can_interact(user) || !target.Adjacent(user) || !ismob(target) || isanimal(target) || !istype(user.loc, /turf) || target.buckled)
+ return
+ // rerun the checks in case of shenanigans
+
+ if(occupant)
+ to_chat(user, span_notice("[src] is already occupied!"))
return
- //rerun the checks in case of shenanigans
if(target == user)
- visible_message("[user] starts climbing into the cryo pod.")
+ visible_message("[user] starts climbing into the cryo pod.")
else
- visible_message("[user] starts putting [target] into the cryo pod.")
+ visible_message("[user] starts putting [target] into the cryo pod.")
- if(occupant)
- to_chat(user, "\The [src] is in use.")
- return
- close_machine(target)
+ to_chat(target, span_warning("If you ghost, log out or close your client now, your character will shortly be permanently removed from the round."))
- to_chat(target, "If you ghost, log out or close your client now, your character will shortly be permanently removed from the round.")
- name = "[name] ([occupant.name])"
- log_admin("[key_name(target)] entered a stasis pod.")
- message_admins("[key_name_admin(target)] entered a stasis pod. (JMP)")
+ log_admin("[key_name(target)] entered a stasis pod.")
+ message_admins("[key_name_admin(target)] entered a stasis pod. [ADMIN_JMP(src)]")
add_fingerprint(target)
-//Attacks/effects.
+ close_machine(target)
+ name = "[name] ([target.name])"
+
+// Attacks/effects.
/obj/machinery/cryopod/blob_act()
- return //Sorta gamey, but we don't really want these to be destroyed.
+ return // Sorta gamey, but we don't really want these to be destroyed.
+
+#undef AHELP_FIRST_MESSAGE
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 5b2551fa8baf..91d354e5e6ae 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -1,8 +1,8 @@
/* Holograms!
* Contains:
- * Holopad
- * Hologram
- * Other stuff
+ * Holopad
+ * Hologram
+ * Other stuff
*/
/*
@@ -24,7 +24,6 @@ Possible to do for anyone motivated enough:
* Holopad
*/
-GLOBAL_LIST_EMPTY(network_holopads)
#define HOLOPAD_PASSIVE_POWER_USAGE 1
#define HOLOGRAM_POWER_USAGE 2
@@ -32,6 +31,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
name = "holopad"
desc = "It's a floor-mounted device for projecting holographic images."
icon_state = "holopad0"
+ base_icon_state = "holopad"
layer = LOW_OBJ_LAYER
plane = FLOOR_PLANE
flags_1 = HEAR_1
@@ -70,7 +70,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
var/obj/effect/overlay/holo_pad_hologram/replay_holo
/// Calls will be automatically answered after a couple rings, here for debugging
var/static/force_answer_call = FALSE
- // var/static/list/holopads = list()
+ var/static/list/holopads = list()
var/obj/effect/overlay/holoray/ray
var/ringing = FALSE
var/offset = FALSE
@@ -107,26 +107,47 @@ GLOBAL_LIST_EMPTY(network_holopads)
new_disk.forceMove(src)
disk = new_disk
-/obj/machinery/holopad/tutorial/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
+/obj/machinery/holopad/Moved(atom/OldLoc, Dir)
+ . = ..()
+ if(!loc)
+ return
+ // move any relevant holograms, basically non-AI, and rays with the pad
+ if(replay_holo)
+ replay_holo.abstract_move(loc)
+ for(var/i in holorays)
+ var/obj/effect/overlay/holoray/ray = holorays[i]
+ ray.abstract_move(loc)
+ var/list/non_call_masters = masters?.Copy()
+ for(var/datum/holocall/holocall as anything in holo_calls)
+ if(!holocall.user || !LAZYACCESS(masters, holocall.user))
+ continue
+ non_call_masters -= holocall.user
+ // moving the eye moves the holo which updates the ray too
+ holocall.eye.setLoc(locate(clamp(x + (holocall.hologram.x - OldLoc.x), 1, world.maxx), clamp(y + (holocall.hologram.y - OldLoc.y), 1, world.maxy), z))
+ for(var/mob/living/holo_master as anything in non_call_masters)
+ var/obj/effect/holo = masters[holo_master]
+ update_holoray(holo_master, holo.loc)
+
+/obj/machinery/holopad/tutorial/attack_hand(mob/user, list/modifiers)
if(!istype(user))
return
if(user.incapacitated() || !is_operational())
return
if(replay_mode)
replay_stop()
- else if(disk && disk.record)
+ else if(disk?.record)
replay_start()
/obj/machinery/holopad/tutorial/HasProximity(atom/movable/AM)
if (!isliving(AM))
return
- if(!replay_mode && (disk && disk.record))
+ if(!replay_mode && (disk?.record))
replay_start()
/obj/machinery/holopad/Initialize()
. = ..()
if(on_network)
- GLOB.network_holopads += src
+ holopads += src
/obj/machinery/holopad/Destroy()
if(outgoing_call)
@@ -146,7 +167,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
QDEL_NULL(disk)
- GLOB.network_holopads -= src
+ holopads -= src
return ..()
/obj/machinery/holopad/power_change()
@@ -172,8 +193,10 @@ GLOBAL_LIST_EMPTY(network_holopads)
/obj/machinery/holopad/examine(mob/user)
. = ..()
- if(in_range(user, src) || isobserver(user))
- . += "The status display reads: Current projection range: [holo_range] units."
+ if(isAI(user))
+ . += span_notice("The status display reads: Current projection range: [holo_range] units. Use :h to speak through the projection. Right-click to project or cancel a projection. Alt-click to hangup all active and incomming calls. Ctrl-click to end projection without jumping to your last location.")
+ else if(in_range(user, src) || isobserver(user))
+ . += span_notice("The status display reads: Current projection range: [holo_range] units.")
/obj/machinery/holopad/attackby(obj/item/P, mob/user, params)
if(default_deconstruction_screwdriver(user, "holopad_open", "holopad0", P))
@@ -190,11 +213,11 @@ GLOBAL_LIST_EMPTY(network_holopads)
if(istype(P,/obj/item/disk/holodisk))
if(disk)
- to_chat(user,"There's already a disk inside [src]!")
+ to_chat(user,span_warning("There's already a disk inside [src]!"))
return
if (!user.transferItemToLoc(P,src))
return
- to_chat(user,"You insert [P] into [src].")
+ to_chat(user,span_notice("You insert [P] into [src]."))
disk = P
return
@@ -242,24 +265,29 @@ GLOBAL_LIST_EMPTY(network_holopads)
switch(action)
if("AIrequest")
+ if(isAI(usr))
+ var/mob/living/silicon/ai/ai_user = usr
+ ai_user.eyeobj.setLoc(get_turf(src))
+ to_chat(usr, span_info("AIs can not request AI presence. Jumping instead."))
+ return
if(last_request + 200 < world.time)
last_request = world.time
- to_chat(usr, "You requested an AI's presence.")
+ to_chat(usr, span_info("You requested an AI's presence."))
var/area/area = get_area(src)
for(var/mob/living/silicon/ai/AI in GLOB.silicon_mobs)
if(!AI.client)
continue
- to_chat(AI, "Your presence is requested at \the [area].")
+ to_chat(AI, span_info("Your presence is requested at \the [area].")) // Project Hologram?"))
return TRUE
else
- to_chat(usr, "A request for AI presence was already sent recently.")
+ to_chat(usr, span_info("A request for AI presence was already sent recently."))
return
if("holocall")
if(outgoing_call)
return
if(usr.loc == loc)
var/list/callnames = list()
- for(var/I in GLOB.network_holopads)
+ for(var/I in holopads)
var/area/A = get_area(I)
if(A)
LAZYADD(callnames[A], I)
@@ -274,7 +302,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
calling = TRUE
return TRUE
else
- to_chat(usr, "You must stand on the holopad to make a call!")
+ to_chat(usr, span_warning("You must stand on the holopad to make a call!"))
if("connectcall")
var/datum/holocall/call_to_connect = locate(params["holopad"]) in holo_calls
if(!QDELETED(call_to_connect))
@@ -285,6 +313,12 @@ GLOBAL_LIST_EMPTY(network_holopads)
if(!QDELETED(call_to_disconnect))
call_to_disconnect.Disconnect(src)
return TRUE
+ if("rejectall")
+ for(var/datum/holocall/call_to_reject as anything in holo_calls)
+ if(call_to_reject.connected_holopad == src) // do not kill the current connection
+ continue
+ call_to_reject.Disconnect(src)
+ return TRUE
if("disk_eject")
if(disk && !replay_mode)
disk.forceMove(drop_location())
@@ -327,14 +361,13 @@ GLOBAL_LIST_EMPTY(network_holopads)
return TRUE
/**
- * hangup_all_calls: Disconnects all current holocalls from the holopad
- */
+ * hangup_all_calls: Disconnects all current holocalls from the holopad
+ */
/obj/machinery/holopad/proc/hangup_all_calls()
for(var/I in holo_calls)
var/datum/holocall/HC = I
HC.Disconnect(src)
-//do not allow AIs to answer calls or people will use it to meta the AI sattelite
/obj/machinery/holopad/attack_ai(mob/living/silicon/ai/user)
if (!istype(user))
return
@@ -343,12 +376,25 @@ GLOBAL_LIST_EMPTY(network_holopads)
/*There are pretty much only three ways to interact here.
I don't need to check for client since they're clicking on an object.
This may change in the future but for now will suffice.*/
- if(user.eyeobj.loc != src.loc)//Set client eye on the object if it's not already.
- user.eyeobj.setLoc(get_turf(src))
- else if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, possibly make one.
+ if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, possibly make one.
activate_holo(user)
- else//If there is a hologram, remove it.
+ else//If there is a hologram, remove it, and jump to your last location.
clear_holo(user)
+ // if(user.lastloc)//only jump to your last location if your lastloc is set, which only sets if you projected from a request message.
+ // user.eyeobj.setLoc(user.lastloc)
+ // user.lastloc = null
+
+/obj/machinery/holopad/AICtrlClick(mob/living/silicon/ai/user)
+ if (!istype(user))
+ return
+ if (!on_network)
+ return
+ if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, then this button does nothing.
+ return
+ else//If there is a hologram, remove it, but dont jump to your last location.
+ // user.lastloc = null
+ clear_holo(user)
+ return
/obj/machinery/holopad/process()
if(LAZYLEN(masters))
@@ -378,25 +424,26 @@ GLOBAL_LIST_EMPTY(network_holopads)
if(outgoing_call)
HC.Disconnect(src)//can't answer calls while calling
else
- playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring!
+ playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring!
ringing = TRUE
- update_icon()
+ update_appearance()
/obj/machinery/holopad/proc/activate_holo(mob/living/user)
var/mob/living/silicon/ai/AI = user
if(!istype(AI))
AI = null
- if(is_operational() && (!AI || AI.eyeobj.loc == loc))//If the projector has power and client eye is on it
- if (AI && istype(AI.current, /obj/machinery/holopad))
- to_chat(user, "ERROR: \black Image feed in progress.")
+ if(is_operational())//If the projector has power
+ if(AI && istype(AI.current, /obj/machinery/holopad))
+ to_chat(user, "[span_danger("ERROR:")] \black Image feed in progress.")
return
var/obj/effect/overlay/holo_pad_hologram/Hologram = new(loc)//Spawn a blank effect at the location.
if(AI)
Hologram.icon = AI.holo_icon
- else //make it like real life
+ AI.eyeobj.setLoc(get_turf(src)) //ensure the AI camera moves to the holopad
+ else //make it like real life
Hologram.icon = user.icon
Hologram.icon_state = user.icon_state
Hologram.copy_overlays(user, TRUE)
@@ -407,17 +454,17 @@ GLOBAL_LIST_EMPTY(network_holopads)
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
- Hologram.setAnchored(TRUE)//So space wind cannot drag it.
+ Hologram.set_anchored(TRUE)//So space wind cannot drag it.
Hologram.name = "[user.name] (Hologram)"//If someone decides to right click.
- Hologram.set_light(2) //hologram lighting
+ Hologram.set_light(2) //hologram lighting
move_hologram()
set_holo(user, Hologram)
- visible_message("A holographic image of [user] flickers to life before your eyes!")
+ visible_message(span_notice("A holographic image of [user] flickers to life before your eyes!"))
return Hologram
else
- to_chat(user, "ERROR: Unable to project hologram.")
+ to_chat(user, "[span_danger("ERROR:")] Unable to project hologram.")
/*This is the proc for special two-way communication between AI and holopad/people talking near holopad.
For the other part of the code, check silicon say.dm. Particularly robot talk.*/
@@ -430,10 +477,13 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
for(var/I in holo_calls)
var/datum/holocall/HC = I
- if(HC.connected_holopad == src && speaker != HC.hologram)
- HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)
+ if(HC.connected_holopad == src)
+ if(speaker == HC.hologram && HC.user.client?.prefs.chat_on_map)
+ HC.user.create_chat_message(speaker, message_language, raw_message, spans)
+ else
+ HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)
- if(outgoing_call && speaker == outgoing_call.user)
+ if(outgoing_call?.hologram && speaker == outgoing_call.user)
outgoing_call.hologram.say(raw_message)
if(record_mode && speaker == record_user)
@@ -447,16 +497,15 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
set_light(2)
else
set_light(0)
- update_icon()
+ update_appearance()
/obj/machinery/holopad/update_icon_state()
var/total_users = LAZYLEN(masters) + LAZYLEN(holo_calls)
if(ringing)
- icon_state = "holopad_ringing"
- else if(total_users || replay_mode)
- icon_state = "holopad1"
- else
- icon_state = "holopad0"
+ icon_state = "[base_icon_state]_ringing"
+ return ..()
+ icon_state = "[base_icon_state][(total_users || replay_mode) ? 1 : 0]"
+ return ..()
/obj/machinery/holopad/proc/set_holo(mob/living/user, obj/effect/overlay/holo_pad_hologram/h)
LAZYSET(masters, user, h)
@@ -488,7 +537,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
var/obj/effect/overlay/holo_pad_hologram/h = masters[holo_owner]
if(!h || h.HC) //Holocalls can't change source.
return FALSE
- for(var/pad in GLOB.network_holopads)
+ for(var/pad in holopads)
var/obj/machinery/holopad/another = pad
if(another == src)
continue
@@ -524,7 +573,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
else
transfered = TRUE
//All is good.
- holo.forceMove(new_turf)
+ holo.abstract_move(new_turf)
if(!transfered)
update_holoray(user,new_turf)
return TRUE
@@ -565,10 +614,10 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
holder.selected_language = record.language
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
- Hologram.setAnchored(TRUE)//So space wind cannot drag it.
+ Hologram.set_anchored(TRUE)//So space wind cannot drag it.
Hologram.name = "[record.caller_name] (Hologram)"//If someone decides to right click.
- Hologram.set_light(2) //hologram lighting
- visible_message("A holographic image of [record.caller_name] flickers to life before your eyes!")
+ Hologram.set_light(2) //hologram lighting
+ visible_message(span_notice("A holographic image of [record.caller_name] flickers to life before your eyes!"))
return Hologram
/obj/machinery/holopad/proc/replay_start()
@@ -661,7 +710,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
record_user = null
/obj/machinery/holopad/proc/record_clear()
- if(disk && disk.record)
+ if(disk?.record)
QDEL_NULL(disk.record)
/obj/effect/overlay/holo_pad_hologram
@@ -673,6 +722,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
Impersonation = null
if(!QDELETED(HC))
HC.Disconnect(HC.calling_holopad)
+ HC = null
return ..()
/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0)
diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm
index 9b2f305b421a..b751724313fe 100644
--- a/code/game/objects/structures/artstuff.dm
+++ b/code/game/objects/structures/artstuff.dm
@@ -52,6 +52,8 @@
var/author_ckey
var/icon_generated = FALSE
var/icon/generated_icon
+ ///boolean that blocks persistence from saving it. enabled from printing copies, because we do not want to save copies.
+ var/no_save = FALSE
// Painting overlay offset when framed
var/framed_offset_x = 11
@@ -370,7 +372,7 @@
update_icon()
/obj/structure/sign/painting/proc/save_persistent()
- if(!persistence_id || !current_canvas)
+ if(!persistence_id || !current_canvas || current_canvas.no_save)
return
if(sanitize_filename(persistence_id) != persistence_id)
stack_trace("Invalid persistence_id - [persistence_id]")
diff --git a/code/game/objects/structures/noticeboard.dm b/code/game/objects/structures/noticeboard.dm
index 68da53aa6ca2..cf608ce39e5a 100644
--- a/code/game/objects/structures/noticeboard.dm
+++ b/code/game/objects/structures/noticeboard.dm
@@ -1,3 +1,5 @@
+#define MAX_NOTICES 5
+
/obj/structure/noticeboard
name = "notice board"
desc = "A board for pinning important notices upon."
@@ -7,8 +9,25 @@
density = FALSE
anchored = TRUE
max_integrity = 150
+ /// Current number of a pinned notices
var/notices = 0
+/obj/structure/noticeboard/directional/north
+ dir = SOUTH
+ pixel_y = 32
+
+/obj/structure/noticeboard/directional/south
+ dir = NORTH
+ pixel_y = -32
+
+/obj/structure/noticeboard/directional/east
+ dir = WEST
+ pixel_x = 32
+
+/obj/structure/noticeboard/directional/west
+ dir = EAST
+ pixel_x = -32
+
/obj/structure/noticeboard/Initialize(mapload)
. = ..()
@@ -16,7 +35,7 @@
return
for(var/obj/item/I in loc)
- if(notices > 4)
+ if(notices >= MAX_NOTICES)
break
if(istype(I, /obj/item/paper))
I.forceMove(src)
@@ -27,67 +46,84 @@
/obj/structure/noticeboard/attackby(obj/item/O, mob/user, params)
if(istype(O, /obj/item/paper) || istype(O, /obj/item/photo))
if(!allowed(user))
- to_chat(user, "You are not authorized to add notices")
+ to_chat(user, span_warning("You are not authorized to add notices!"))
return
- if(notices < 5)
+ if(notices < MAX_NOTICES)
if(!user.transferItemToLoc(O, src))
return
notices++
icon_state = "nboard0[notices]"
- to_chat(user, "You pin the [O] to the noticeboard.")
+ to_chat(user, span_notice("You pin the [O] to the noticeboard."))
else
- to_chat(user, "The notice board is full")
+ to_chat(user, span_warning("The notice board is full!"))
else
return ..()
-/obj/structure/noticeboard/interact(mob/user)
- ui_interact(user)
-
-/obj/structure/noticeboard/ui_interact(mob/user)
+/obj/structure/noticeboard/ui_state(mob/user)
+ return GLOB.physical_state
+
+/obj/structure/noticeboard/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "NoticeBoard", name)
+ ui.open()
+
+/obj/structure/noticeboard/ui_data(mob/user)
+ var/list/data = list()
+ data["allowed"] = allowed(user)
+ data["items"] = list()
+ for(var/obj/item/content in contents)
+ var/list/content_data = list(
+ name = content.name,
+ ref = REF(content)
+ )
+ data["items"] += list(content_data)
+ return data
+
+/obj/structure/noticeboard/ui_act(action, params)
. = ..()
- var/auth = allowed(user)
- var/dat = "[name]
"
- for(var/obj/item/P in src)
- if(istype(P, /obj/item/paper))
- dat += "[P.name] [auth ? "Write Remove" : ""]
"
- else
- dat += "[P.name] [auth ? "Remove" : ""]
"
- user << browse("Notices[dat]","window=noticeboard")
- onclose(user, "noticeboard")
-
-/obj/structure/noticeboard/Topic(href, href_list)
- ..()
- usr.set_machine(src)
- if(href_list["remove"])
- if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open
- return
- var/obj/item/I = locate(href_list["remove"]) in contents
- if(istype(I) && I.loc == src)
- I.forceMove(usr.loc)
- usr.put_in_hands(I)
- notices--
- icon_state = "nboard0[notices]"
+ if(.)
+ return
- if(href_list["write"])
- if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open
- return
- var/obj/item/P = locate(href_list["write"]) in contents
- if(istype(P) && P.loc == src)
- var/obj/item/I = usr.is_holding_item_of_type(/obj/item/pen)
- if(I)
- add_fingerprint(usr)
- P.attackby(I, usr)
- else
- to_chat(usr, "You'll need something to write with!")
+ var/obj/item/item = locate(params["ref"]) in contents
+ if(!istype(item) || item.loc != src)
+ return
- if(href_list["read"])
- var/obj/item/I = locate(href_list["read"]) in contents
- if(istype(I) && I.loc == src)
- usr.examinate(I)
+ var/mob/user = usr
+
+ switch(action)
+ if("examine")
+ if(istype(item, /obj/item/paper))
+ item.ui_interact(user)
+ else
+ user.examinate(item)
+ return TRUE
+ if("remove")
+ if(!allowed(user))
+ return
+ remove_item(item, user)
+ return TRUE
+
+/**
+ * Removes an item from the notice board
+ *
+ * Arguments:
+ * * item - The item that is to be removed
+ * * user - The mob that is trying to get the item removed, if there is one
+ */
+/obj/structure/noticeboard/proc/remove_item(obj/item/item, mob/user)
+ item.forceMove(drop_location())
+ if(user)
+ user.put_in_hands(item)
+ balloon_alert(user, "removed from board")
+ notices--
+ icon_state = "nboard0[notices]"
/obj/structure/noticeboard/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
- new /obj/item/stack/sheet/metal (loc, 1)
+ new /obj/item/stack/sheet/iron (loc, 1)
+ for(var/obj/item/content in contents)
+ remove_item(content)
qdel(src)
// Notice boards for the heads of staff (plus the qm)
@@ -131,3 +167,5 @@
name = "Staff Notice Board"
desc = "Important notices from the heads of staff."
req_access = list(ACCESS_HEADS)
+
+#undef MAX_NOTICES
diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
index b5f3bae53d24..fada2229e5ac 100644
--- a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
+++ b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
@@ -5,7 +5,14 @@
var/title = "Untitled Conversation"
var/datum/computer_file/program/chatclient/operator // "Administrator" of this channel. Creator starts as channel's operator,
var/list/messages = list()
- var/list/clients = list()
+ ///chat clients who are active or minimized
+ var/list/active_clients = list()
+ ///chat clients who have exited out of the program.
+ var/list/offline_clients = list()
+ ///clients muted by operator
+ var/list/muted_clients = list()
+ //if a channel is strong, it cannot be renamed or deleted.
+ var/strong = FALSE
var/password
var/static/ntnrc_uid = 0
@@ -22,6 +29,8 @@
/datum/ntnet_conversation/Destroy()
if(SSnetworks.station_network)
SSnetworks.station_network.chat_channels.Remove(src)
+ for(var/datum/computer_file/program/chatclient/chatterbox in (active_clients | offline_clients | muted_clients))
+ purge_client(chatterbox)
return ..()
/datum/ntnet_conversation/proc/add_message(message, username)
@@ -38,39 +47,70 @@
return
messages = messages.Copy(messages.len-50 ,0)
-/datum/ntnet_conversation/proc/add_client(datum/computer_file/program/chatclient/C)
- if(!istype(C))
+/datum/ntnet_conversation/proc/add_client(datum/computer_file/program/chatclient/new_user, silent = FALSE)
+ if(!istype(new_user))
return
- clients.Add(C)
- add_status_message("[C.username] has joined the channel.")
+ new_user.conversations |= src
+ active_clients.Add(new_user)
+ if(!silent)
+ add_status_message("[new_user.username] has joined the channel.")
// No operator, so we assume the channel was empty. Assign this user as operator.
if(!operator)
- changeop(C)
+ changeop(new_user)
-/datum/ntnet_conversation/proc/remove_client(datum/computer_file/program/chatclient/C)
- if(!istype(C) || !(C in clients))
+//Clear all of our references to a client, used for client deletion
+/datum/ntnet_conversation/proc/purge_client(datum/computer_file/program/chatclient/forget)
+ remove_client(forget)
+ muted_clients -= forget
+ offline_clients -= forget
+ forget.conversations -= src
+
+/datum/ntnet_conversation/proc/remove_client(datum/computer_file/program/chatclient/leaving)
+ if(!istype(leaving))
return
- clients.Remove(C)
- add_status_message("[C.username] has left the channel.")
+ if(leaving in active_clients)
+ active_clients.Remove(leaving)
+ add_status_message("[leaving.username] has left the channel.")
// Channel operator left, pick new operator
- if(C == operator)
+ if(leaving == operator)
operator = null
- if(clients.len)
- var/datum/computer_file/program/chatclient/newop = pick(clients)
+ if(active_clients.len)
+ var/datum/computer_file/program/chatclient/newop = pick(active_clients)
changeop(newop)
+/datum/ntnet_conversation/proc/go_offline(datum/computer_file/program/chatclient/offline)
+ if(!istype(offline) || !(offline in active_clients))
+ return
+ active_clients.Remove(offline)
+ offline_clients.Add(offline)
+
+/datum/ntnet_conversation/proc/mute_user(datum/computer_file/program/chatclient/op, datum/computer_file/program/chatclient/muted)
+ if(operator != op) //sanity even if the person shouldn't be able to see the mute button
+ return
+ if(muted in muted_clients)
+ muted_clients.Remove(muted)
+ muted.computer.alert_call(muted, "You have been unmuted from [title]!", 'sound/machines/ping.ogg')
+ else
+ muted_clients.Add(muted)
+ muted.computer.alert_call(muted, "You have been muted from [title]!")
+
+/datum/ntnet_conversation/proc/ping_user(datum/computer_file/program/chatclient/pinger, datum/computer_file/program/chatclient/pinged)
+ if(pinger in muted_clients) //oh my god fuck off
+ return
+ add_status_message("[pinger.username] pinged [pinged.username].")
+ pinged.computer.alert_call(pinged, "You have been pinged in [title] by [pinger.username]!", 'sound/machines/ping.ogg')
/datum/ntnet_conversation/proc/changeop(datum/computer_file/program/chatclient/newop)
if(istype(newop))
operator = newop
add_status_message("Channel operator status transferred to [newop.username].")
-/datum/ntnet_conversation/proc/change_title(newtitle, datum/computer_file/program/chatclient/client)
- if(operator != client)
- return FALSE // Not Authorised
+/datum/ntnet_conversation/proc/change_title(newtitle, datum/computer_file/program/chatclient/renamer)
+ if(operator != renamer || strong)
+ return FALSE // Not Authorised or channel cannot be editted
- add_status_message("[client.username] has changed channel title from [title] to [newtitle]")
+ add_status_message("[renamer.username] has changed channel title from [title] to [newtitle]")
title = newtitle
#undef MAX_CHANNELS
diff --git a/code/modules/modular_computers/computers/_modular_computer_shared.dm b/code/modules/modular_computers/computers/_modular_computer_shared.dm
index 0aca92f9d8ca..1a2ba822d1b7 100644
--- a/code/modules/modular_computers/computers/_modular_computer_shared.dm
+++ b/code/modules/modular_computers/computers/_modular_computer_shared.dm
@@ -39,7 +39,7 @@
. += "It has a slot installed for an intelliCard which contains: [ai_slot.stored_card.name]"
else
. += "It has a slot installed for an intelliCard, which appears to be occupied."
- . += "Alt-click to eject the intelliCard."
+ . += span_info("Alt-click to eject the intelliCard.")
else
. += "It has a slot installed for an intelliCard."
@@ -55,7 +55,7 @@
. += "It has [multiple_slots ? "two slots" : "a slot"] for identification cards installed[multiple_cards ? " which contain [first_ID] and [second_ID]" : ", one of which contains [first_ID ? first_ID : second_ID]"]."
else
. += "It has [multiple_slots ? "two slots" : "a slot"] for identification cards installed, [multiple_cards ? "both of which appear" : "and one of them appears"] to be occupied."
- . += "Alt-click [src] to eject the identification card[multiple_cards ? "s":""]."
+ . += span_info("Alt-click [src] to eject the identification card[multiple_cards ? "s":""].")
else
. += "It has [multiple_slots ? "two slots" : "a slot"] installed for identification cards."
@@ -63,4 +63,4 @@
if(printer_slot)
. += "It has a printer installed."
if(user_is_adjacent)
- . += "The printer's paper levels are at: [printer_slot.stored_paper]/[printer_slot.max_paper]."
+ . += "The printer's paper levels are at: [printer_slot.stored_paper]/[printer_slot.max_paper].]"
diff --git a/code/modules/modular_computers/computers/item/computer_components.dm b/code/modules/modular_computers/computers/item/computer_components.dm
index 8668b279cf4b..91c369581fbd 100644
--- a/code/modules/modular_computers/computers/item/computer_components.dm
+++ b/code/modules/modular_computers/computers/item/computer_components.dm
@@ -3,19 +3,19 @@
return FALSE
if(H.w_class > max_hardware_size)
- to_chat(user, "This component is too large for \the [src]!")
+ to_chat(user, span_warning("This component is too large for \the [src]!"))
return FALSE
if(H.expansion_hw)
if(LAZYLEN(expansion_bays) >= max_bays)
- to_chat(user, "All of the computer's expansion bays are filled.")
+ to_chat(user, span_warning("All of the computer's expansion bays are filled."))
return FALSE
if(LAZYACCESS(expansion_bays, H.device_type))
- to_chat(user, "The computer immediately ejects /the [H] and flashes an error: \"Hardware Address Conflict\".")
+ to_chat(user, span_warning("The computer immediately ejects /the [H] and flashes an error: \"Hardware Address Conflict\"."))
return FALSE
if(all_components[H.device_type])
- to_chat(user, "This computer's hardware slot is already occupied by \the [all_components[H.device_type]].")
+ to_chat(user, span_warning("This computer's hardware slot is already occupied by \the [all_components[H.device_type]]."))
return FALSE
return TRUE
@@ -32,7 +32,7 @@
LAZYSET(expansion_bays, H.device_type, H)
all_components[H.device_type] = H
- to_chat(user, "You install \the [H] into \the [src].")
+ to_chat(user, span_notice("You install \the [H] into \the [src]."))
H.holder = src
H.forceMove(src)
H.on_install(src, user)
@@ -47,14 +47,14 @@
LAZYREMOVE(expansion_bays, H.device_type)
all_components.Remove(H.device_type)
- to_chat(user, "You remove \the [H] from \the [src].")
+ to_chat(user, span_notice("You remove \the [H] from \the [src]."))
H.forceMove(get_turf(src))
H.holder = null
H.on_remove(src, user)
if(enabled && !use_power())
shutdown_computer()
- update_icon()
+ update_appearance()
return TRUE
diff --git a/code/modules/modular_computers/computers/item/computer_damage.dm b/code/modules/modular_computers/computers/item/computer_damage.dm
index b510f8adedb8..9053aebcd5e8 100644
--- a/code/modules/modular_computers/computers/item/computer_damage.dm
+++ b/code/modules/modular_computers/computers/item/computer_damage.dm
@@ -18,7 +18,7 @@
/obj/item/modular_computer/proc/break_apart()
if(!(flags_1 & NODECONSTRUCT_1))
- physical.visible_message("\The [src] breaks apart!")
+ physical.visible_message(span_notice("\The [src] breaks apart!"))
var/turf/newloc = get_turf(src)
new /obj/item/stack/sheet/metal(newloc, round(steel_sheet_cost/2))
for(var/C in all_components)
diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm
index 92d4a812a273..f31c3c82cce2 100644
--- a/code/modules/modular_computers/computers/item/computer_power.dm
+++ b/code/modules/modular_computers/computers/item/computer_power.dm
@@ -16,7 +16,7 @@
if(cell.use(amount * GLOB.CELLRATE))
return TRUE
else // Discharge the cell anyway.
- cell.use(min(amount*GLOB.CELLRATE, cell.charge))
+ cell.use(min(amount * GLOB.CELLRATE, cell.charge))
return FALSE
return FALSE
diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm
index d9d9e5c87685..9695c0b0a57e 100644
--- a/code/modules/modular_computers/computers/item/computer_ui.dm
+++ b/code/modules/modular_computers/computers/item/computer_ui.dm
@@ -13,6 +13,10 @@
ui.close()
return
+ // if(HAS_TRAIT(user, TRAIT_CHUNKYFINGERS))
+ // to_chat(user, span_warning("Your fingers are too big to use this right now!"))
+ // return
+
// 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)
@@ -30,7 +34,7 @@
// This screen simply lists available programs and user may select them.
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.")
+ to_chat(user, span_danger("\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.
ui = SStgui.try_update_ui(user, src, ui)
@@ -111,7 +115,7 @@
active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs
active_program = null
- update_icon()
+ update_appearance()
if(user && istype(user))
ui_interact(user) // Re-open the UI on this computer. It should show the main screen now.
if("eject_pen")
@@ -132,7 +136,7 @@
return
P.kill_program(forced = TRUE)
- to_chat(user, "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed.")
+ to_chat(user, span_notice("Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed."))
if("PC_runprogram")
var/prog = params["name"]
@@ -142,7 +146,7 @@
P = hard_drive.find_file_by_name(prog)
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.")
+ to_chat(user, span_danger("\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning."))
return
P.computer = src
@@ -156,22 +160,22 @@
active_program = P
P.alert_pending = FALSE
idle_threads.Remove(P)
- update_icon()
+ update_appearance()
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.")
+ to_chat(user, span_danger("\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.")
+ to_chat(user, span_danger("\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()
+ update_appearance()
return 1
if("PC_toggle_light")
@@ -185,7 +189,7 @@
if(!new_color)
return
if(color_hex2num(new_color) < 200) //Colors too dark are rejected
- to_chat(user, "That color is too dark! Choose a lighter one.")
+ to_chat(user, span_warning("That color is too dark! Choose a lighter one."))
new_color = null
return set_flashlight_color(new_color)
diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm
index 5686dec8d09c..b4669c72f800 100644
--- a/code/modules/modular_computers/computers/item/laptop.dm
+++ b/code/modules/modular_computers/computers/item/laptop.dm
@@ -17,8 +17,8 @@
// No running around with open laptops in hands.
item_flags = SLOWS_WHILE_IN_HAND
- screen_on = FALSE // Starts closed
- var/start_open = TRUE // unless this var is set to 1
+ screen_on = FALSE // Starts closed
+ var/start_open = TRUE // unless this var is set to 1
var/icon_state_closed = "laptop-closed"
var/w_class_open = WEIGHT_CLASS_BULKY
var/slowdown_open = TRUE
@@ -44,15 +44,14 @@
/obj/item/modular_computer/laptop/update_icon_state()
if(!screen_on)
icon_state = icon_state_closed
- else
- . = ..()
+ return
+ return ..()
/obj/item/modular_computer/laptop/update_overlays()
- if(screen_on)
- return ..()
- else
+ if(!screen_on)
cut_overlays()
- icon_state = icon_state_closed
+ return
+ return ..()
/obj/item/modular_computer/laptop/attack_self(mob/user)
if(!screen_on)
@@ -68,7 +67,8 @@
try_toggle_open(usr)
/obj/item/modular_computer/laptop/MouseDrop(obj/over_object, src_location, over_location)
- if(istype(over_object, /atom/movable/screen/inventory/hand) || over_object == usr)
+ . = ..()
+ if(istype(over_object, /atom/movable/screen/inventory/hand))
var/atom/movable/screen/inventory/hand/H = over_object
var/mob/M = usr
@@ -103,17 +103,17 @@
/obj/item/modular_computer/laptop/proc/toggle_open(mob/living/user=null)
if(screen_on)
- to_chat(user, "You close \the [src].")
+ to_chat(user, span_notice("You close \the [src]."))
slowdown = initial(slowdown)
w_class = initial(w_class)
else
- to_chat(user, "You open \the [src].")
+ to_chat(user, span_notice("You open \the [src]."))
slowdown = slowdown_open
w_class = w_class_open
screen_on = !screen_on
display_overlays = screen_on
- update_icon()
+ update_appearance()
diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm
index 970dc8bd1dfd..d569ad24fdab 100644
--- a/code/modules/modular_computers/computers/item/processor.dm
+++ b/code/modules/modular_computers/computers/item/processor.dm
@@ -38,7 +38,7 @@
integrity_failure = machinery_computer.integrity_failure
base_active_power_usage = machinery_computer.base_active_power_usage
base_idle_power_usage = machinery_computer.base_idle_power_usage
- machinery_computer.RegisterSignal(src, COMSIG_ATOM_UPDATED_ICON, /atom/proc/update_icon) //when we update_icon, also update the computer
+ machinery_computer.RegisterSignal(src, COMSIG_ATOM_UPDATED_ICON, /obj/machinery/modular_computer/proc/relay_icon_update) //when we update_icon, also update the computer
/obj/item/modular_computer/processor/relay_qdel()
qdel(machinery_computer)
@@ -47,7 +47,7 @@
if(!machinery_computer)
return
..()
- machinery_computer.update_icon()
+ machinery_computer.update_appearance()
return
/obj/item/modular_computer/processor/attack_ghost(mob/user)
@@ -57,4 +57,4 @@
if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext)
return
playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE)
- machinery_computer.visible_message("The [src] displays a [caller.filedesc] notification: [alerttext]")
+ machinery_computer.visible_message(span_notice("The [src] displays a [caller.filedesc] notification: [alerttext]"))
diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm
index d4d3ef52c26b..8741c468d205 100644
--- a/code/modules/modular_computers/computers/item/tablet.dm
+++ b/code/modules/modular_computers/computers/item/tablet.dm
@@ -2,9 +2,10 @@
name = "tablet computer"
icon = 'icons/obj/modular_tablet.dmi'
icon_state = "tablet-red"
- icon_state_unpowered = "tablet"
- icon_state_powered = "tablet"
+ icon_state_unpowered = "tablet-red"
+ icon_state_powered = "tablet-red"
icon_state_menu = "menu"
+ base_icon_state = "tablet"
// worn_icon_state = "tablet"
hardware_flag = PROGRAM_TABLET
max_hardware_size = 1
@@ -80,12 +81,12 @@
/obj/item/modular_computer/tablet/ui_data(mob/user)
. = ..()
.["PC_showpeneject"] = inserted_item ? 1 : 0
-
/obj/item/modular_computer/tablet/update_icon_state()
if(has_variants)
if(!finish_color)
- finish_color = pick("red","blue","brown","green","black")
- icon_state = icon_state_powered = icon_state_unpowered = "tablet-[finish_color]"
+ finish_color = pick("red", "blue", "brown", "green", "black")
+ icon_state = icon_state_powered = icon_state_unpowered = "[base_icon_state]-[finish_color]"
+ return ..()
/obj/item/modular_computer/tablet/syndicate_contract_uplink
name = "contractor tablet"
@@ -102,6 +103,8 @@
/// Given to Nuke Ops members.
/obj/item/modular_computer/tablet/nukeops
icon_state = "tablet-syndicate"
+ icon_state_powered = "tablet-syndicate"
+ icon_state_unpowered = "tablet-syndicate"
comp_light_luminosity = 6.3
has_variants = FALSE
device_theme = "syndicate"
@@ -109,15 +112,18 @@
/obj/item/modular_computer/tablet/nukeops/emag_act(mob/user)
if(!enabled)
- to_chat(user, "You'd need to turn the [src] on first.")
+ to_chat(user, span_warning("You'd need to turn the [src] on first."))
return FALSE
- to_chat(user, "You swipe \the [src]. It's screen briefly shows a message reading \"MEMORY CODE INJECTION DETECTED AND SUCCESSFULLY QUARANTINED\".")
+ to_chat(user, span_notice("You swipe \the [src]. It's screen briefly shows a message reading \"MEMORY CODE INJECTION DETECTED AND SUCCESSFULLY QUARANTINED\"."))
return FALSE
/// Borg Built-in tablet interface
/obj/item/modular_computer/tablet/integrated
name = "modular interface"
icon_state = "tablet-silicon"
+ icon_state_powered = "tablet-silicon"
+ icon_state_unpowered = "tablet-silicon"
+ base_icon_state = "tablet-silicon"
has_light = FALSE //tablet light button actually enables/disables the borg lamp
comp_light_luminosity = 0
has_variants = FALSE
@@ -198,11 +204,13 @@
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
borgo.playsound_local(src, sound, 50, TRUE)
- to_chat(borgo, "The [src] displays a [caller.filedesc] notification: [alerttext]")
+ to_chat(borgo, span_notice("The [src] displays a [caller.filedesc] notification: [alerttext]"))
/obj/item/modular_computer/tablet/integrated/syndicate
icon_state = "tablet-silicon-syndicate"
+ icon_state_powered = "tablet-silicon-syndicate"
+ icon_state_unpowered = "tablet-silicon-syndicate"
device_theme = "syndicate"
diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm
index 90dd1498253e..2c760a4b6c23 100644
--- a/code/modules/modular_computers/computers/item/tablet_presets.dm
+++ b/code/modules/modular_computers/computers/item/tablet_presets.dm
@@ -20,6 +20,17 @@
install_component(new /obj/item/computer_hardware/card_slot)
install_component(new /obj/item/computer_hardware/printer/mini)
+/obj/item/modular_computer/tablet/preset/science/Initialize()
+ . = ..()
+ var/obj/item/computer_hardware/hard_drive/small/hard_drive = new
+ 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(hard_drive)
+ install_component(new /obj/item/computer_hardware/card_slot)
+ install_component(new /obj/item/computer_hardware/network_card)
+ install_component(new /obj/item/computer_hardware/radio_card)
+ hard_drive.store_file(new /datum/computer_file/program/signaler)
+
/obj/item/modular_computer/tablet/preset/cargo/Initialize()
. = ..()
var/obj/item/computer_hardware/hard_drive/small/hard_drive = new
@@ -30,17 +41,38 @@
install_component(new /obj/item/computer_hardware/network_card)
install_component(new /obj/item/computer_hardware/printer/mini)
// hard_drive.store_file(new /datum/computer_file/program/shipping)
+ var/datum/computer_file/program/chatclient/chatprogram
+ chatprogram = new
+ hard_drive.store_file(chatprogram)
+ chatprogram.username = get_cargochat_username()
+
+/obj/item/modular_computer/tablet/preset/cargo/proc/get_cargochat_username()
+ return "cargonian_[rand(1,999)]"
+
+/obj/item/modular_computer/tablet/preset/cargo/quartermaster/get_cargochat_username()
+ return "quartermaster"
/obj/item/modular_computer/tablet/preset/advanced/atmos/Initialize() //This will be defunct and will be replaced when NtOS PDAs are done
. = ..()
install_component(new /obj/item/computer_hardware/sensorpackage)
+/obj/item/modular_computer/tablet/preset/advanced/engineering/Initialize()
+ . = ..()
+ 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/supermatter_monitor)
+
/obj/item/modular_computer/tablet/preset/advanced/command/Initialize()
. = ..()
var/obj/item/computer_hardware/hard_drive/small/hard_drive = find_hardware_by_name("solid state drive")
install_component(new /obj/item/computer_hardware/sensorpackage)
install_component(new /obj/item/computer_hardware/card_slot/secondary)
hard_drive.store_file(new /datum/computer_file/program/budgetorders)
+ // hard_drive.store_file(new /datum/computer_file/program/science)
+
+/obj/item/modular_computer/tablet/preset/advanced/command/engineering/Initialize()
+ . = ..()
+ 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/supermatter_monitor)
/// 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()
diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm
index 12b2f6d25a58..9da6b35ff9a1 100644
--- a/code/modules/modular_computers/computers/machinery/console_presets.dm
+++ b/code/modules/modular_computers/computers/machinery/console_presets.dm
@@ -26,8 +26,6 @@
/obj/machinery/modular_computer/console/preset/proc/install_programs()
return
-
-
// ===== ENGINEERING CONSOLE =====
/obj/machinery/modular_computer/console/preset/engineering
console_department = "Engineering"
@@ -45,6 +43,7 @@
console_department = "Research"
name = "research director's console"
desc = "A stationary computer. This one comes preloaded with research programs."
+ _has_second_id_slot = TRUE
_has_ai = TRUE
/obj/machinery/modular_computer/console/preset/research/install_programs()
@@ -84,6 +83,18 @@
hard_drive.store_file(new/datum/computer_file/program/job_management())
hard_drive.store_file(new/datum/computer_file/program/crew_manifest())
+/obj/machinery/modular_computer/console/preset/id/centcom
+ desc = "A stationary computer. This one comes preloaded with CentCom identification modification programs."
+
+/obj/machinery/modular_computer/console/preset/id/centcom/install_programs()
+ var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
+ var/datum/computer_file/program/card_mod/card_mod_centcom = new /datum/computer_file/program/card_mod()
+ card_mod_centcom.is_centcom = TRUE
+ hard_drive.store_file(new /datum/computer_file/program/chatclient())
+ hard_drive.store_file(card_mod_centcom)
+ hard_drive.store_file(new /datum/computer_file/program/job_management())
+ hard_drive.store_file(new /datum/computer_file/program/crew_manifest())
+
// ===== CIVILIAN CONSOLE =====
/obj/machinery/modular_computer/console/preset/civilian
console_department = "Civilian"
@@ -94,3 +105,79 @@
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
hard_drive.store_file(new/datum/computer_file/program/chatclient())
hard_drive.store_file(new/datum/computer_file/program/arcade())
+
+// curator
+/obj/machinery/modular_computer/console/preset/curator
+ console_department = "Civilian"
+ name = "curator console"
+ desc = "A stationary computer. This one comes preloaded with art programs."
+ _has_printer = TRUE
+
+/obj/machinery/modular_computer/console/preset/curator/install_programs()
+ var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
+ hard_drive.store_file(new/datum/computer_file/program/portrait_printer())
+
+// ===== CARGO CHAT CONSOLES =====
+/obj/machinery/modular_computer/console/preset/cargochat
+ name = "cargo chatroom console"
+ desc = "A stationary computer. This one comes preloaded with a chatroom for your cargo requests."
+ ///chat client installed on this computer, just helpful for linking all the computers
+ var/datum/computer_file/program/chatclient/chatprogram
+
+/obj/machinery/modular_computer/console/preset/cargochat/install_programs()
+ var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
+ chatprogram = new
+ chatprogram.computer = cpu
+ hard_drive.store_file(chatprogram)
+ chatprogram.username = "[lowertext(console_department)]_department"
+ chatprogram.program_state = PROGRAM_STATE_ACTIVE
+ cpu.active_program = chatprogram
+
+//ONE PER MAP PLEASE, IT MAKES A CARGOBUS FOR EACH ONE OF THESE
+/obj/machinery/modular_computer/console/preset/cargochat/cargo
+ console_department = "Cargo"
+ name = "department chatroom console"
+ desc = "A stationary computer. This one comes preloaded with a chatroom for incoming cargo requests. You may moderate it from this computer."
+
+/obj/machinery/modular_computer/console/preset/cargochat/cargo/install_programs()
+ var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
+
+ //adding chat, setting it as the active window immediately
+ chatprogram = new
+ chatprogram.computer = cpu
+ hard_drive.store_file(chatprogram)
+ chatprogram.program_state = PROGRAM_STATE_ACTIVE
+ cpu.active_program = chatprogram
+
+ //setting up chat
+ chatprogram.username = "cargo_requests_operator"
+ var/datum/ntnet_conversation/cargochat = new
+ cargochat.operator = chatprogram //adding operator before joining the chat prevents an unnecessary message about switching op from showing
+ cargochat.add_client(chatprogram)
+ cargochat.title = "#cargobus"
+ cargochat.strong = TRUE
+ chatprogram.active_channel = cargochat.id
+
+/obj/machinery/modular_computer/console/preset/cargochat/cargo/LateInitialize()
+ . = ..()
+ var/datum/ntnet_conversation/cargochat = SSnetworks.station_network.get_chat_channel_by_id(chatprogram.active_channel)
+ for(var/obj/machinery/modular_computer/console/preset/cargochat/cargochat_console in GLOB.machines)
+ if(cargochat_console == src)
+ continue
+ cargochat_console.chatprogram.active_channel = chatprogram.active_channel
+ cargochat.add_client(cargochat_console.chatprogram, silent = TRUE)
+
+/obj/machinery/modular_computer/console/preset/cargochat/service
+ console_department = "Service"
+
+/obj/machinery/modular_computer/console/preset/cargochat/engineering
+ console_department = "Engineering"
+
+/obj/machinery/modular_computer/console/preset/cargochat/science
+ console_department = "Science"
+
+/obj/machinery/modular_computer/console/preset/cargochat/security
+ console_department = "Security"
+
+/obj/machinery/modular_computer/console/preset/cargochat/medical
+ console_department = "Medical"
diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm
index 090bf1c7fc82..6ac143a4a1f4 100644
--- a/code/modules/modular_computers/computers/machinery/modular_computer.dm
+++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm
@@ -4,28 +4,41 @@
name = "modular computer"
desc = "An advanced computer."
- use_power = IDLE_POWER_USE
- idle_power_usage = 5
- var/hardware_flag = 0 // A flag that describes this device type
- var/last_power_usage = 0 // Power usage during last tick
-
// Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..)
// must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently
// If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example.
-
icon = null
icon_state = null
- var/icon_state_unpowered = null // Icon state when the computer is turned off.
- var/icon_state_powered = null // Icon state when the computer is turned on.
- var/screen_icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
- var/screen_icon_screensaver = "standby" // Icon state overlay when the computer is powered, but not 'switched on'.
- var/max_hardware_size = 0 // Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed.
- var/steel_sheet_cost = 10 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
- var/light_strength = 0 // Light luminosity when turned on
- var/base_active_power_usage = 100 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
- var/base_idle_power_usage = 10 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
-
- var/obj/item/modular_computer/processor/cpu = null // CPU that handles most logic while this type only handles power and other specific things.
+
+ use_power = IDLE_POWER_USE
+ idle_power_usage = 5
+ ///A flag that describes this device type
+ var/hardware_flag = 0
+ ///Power usage during last tick
+ var/last_power_usage = 0
+
+
+ ///Icon state when the computer is turned off.
+ var/icon_state_unpowered = null
+ ///Icon state when the computer is turned on.
+ var/icon_state_powered = null
+ ///Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
+ var/screen_icon_state_menu = "menu"
+ ///Icon state overlay when the computer is powered, but not 'switched on'.
+ var/screen_icon_screensaver = "standby"
+ ///Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed.
+ var/max_hardware_size = 0
+ ///Amount of steel sheets refunded when disassembling an empty frame of this computer.
+ var/steel_sheet_cost = 10
+ ///Light luminosity when turned on
+ var/light_strength = 0
+ ///Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
+ var/base_active_power_usage = 100
+ ///Power usage when the computer is idle and screen is off (currently only applies to laptops)
+ var/base_idle_power_usage = 10
+
+ ///CPU that handles most logic while this type only handles power and other specific things.
+ var/obj/item/modular_computer/processor/cpu = null
/obj/machinery/modular_computer/Initialize()
. = ..()
@@ -48,32 +61,35 @@
cpu.attack_ghost(user)
/obj/machinery/modular_computer/emag_act(mob/user)
- . = ..()
if(!cpu)
- to_chat(user, "You'd need to turn the [src] on first.")
+ to_chat(user, span_warning("You'd need to turn the [src] on first."))
return FALSE
return (cpu.emag_act(user))
-/obj/machinery/modular_computer/update_icon()
- cut_overlays()
- icon_state = icon_state_powered
+/obj/machinery/modular_computer/update_appearance(updates)
+ . = ..()
+ set_light(cpu?.enabled ? light_strength : 0)
+
+/obj/machinery/modular_computer/update_icon_state()
+ icon_state = (cpu?.enabled || (!(stat & NOPOWER) && cpu?.use_power())) ? icon_state_powered : icon_state_unpowered
+ return ..()
- if(!cpu || !cpu.enabled)
+/obj/machinery/modular_computer/update_overlays()
+ . = ..()
+ if(!cpu?.enabled)
if (!(stat & NOPOWER) && (cpu?.use_power()))
- add_overlay(screen_icon_screensaver)
- else
- icon_state = icon_state_unpowered
- set_light(0)
+ . += screen_icon_screensaver
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
- add_overlay(screen_icon_state_menu)
+ . += cpu.active_program?.program_icon_state || screen_icon_state_menu
if(cpu && cpu.obj_integrity <= cpu.integrity_failure * cpu.max_integrity)
- add_overlay("bsod")
- add_overlay("broken")
+ . += "bsod"
+ . += "broken"
+
+/// Eats the "source" arg because update_icon actually expects args now.
+/obj/machinery/modular_computer/proc/relay_icon_update(datum/source, updates, updated)
+ SIGNAL_HANDLER
+ return update_icon(updates)
/obj/machinery/modular_computer/AltClick(mob/user)
if(cpu)
@@ -98,17 +114,17 @@
/obj/machinery/modular_computer/proc/power_failure(malfunction = 0)
var/obj/item/computer_hardware/battery/battery_module = cpu.all_components[MC_CELL]
if(cpu?.enabled) // Shut down the computer
- visible_message("\The [src]'s screen flickers [battery_module ? "\"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly.")
+ visible_message(span_danger("\The [src]'s screen flickers [battery_module ? "\"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly."))
if(cpu)
cpu.shutdown_computer(0)
- stat |= NOPOWER
- update_icon()
+ set_machine_stat(stat | NOPOWER)
+ update_appearance()
// Modular computers can have battery in them, we handle power in previous proc, so prevent this from messing it up for us.
/obj/machinery/modular_computer/power_change()
if(cpu?.use_power()) // If MC_CPU still has a power source, PC wouldn't go offline.
- stat &= ~NOPOWER
- update_icon()
+ set_machine_stat(stat & ~NOPOWER)
+ update_appearance()
return
. = ..()
@@ -116,7 +132,7 @@
if(cpu)
return cpu.screwdriver_act(user, tool)
-/obj/machinery/modular_computer/attackby(obj/item/W as obj, mob/user)
+/obj/machinery/modular_computer/attackby(obj/item/W as obj, mob/living/user)
if (user.a_intent == INTENT_HELP && cpu && !(flags_1 & NODECONSTRUCT_1))
return cpu.attackby(W, user)
return ..()
@@ -126,15 +142,16 @@
// Minor explosions are mostly mitigitated by casing.
/obj/machinery/modular_computer/ex_act(severity)
if(cpu)
- cpu.ex_act(severity)
- // switch(severity)
- // if(EXPLODE_DEVASTATE)
- // SSexplosions.high_mov_atom += cpu
- // if(EXPLODE_HEAVY)
- // SSexplosions.med_mov_atom += cpu
- // if(EXPLODE_LIGHT)
- // SSexplosions.low_mov_atom += cpu
- ..()
+ return cpu.ex_act(severity)
+
+ // switch(severity)
+ // if(EXPLODE_DEVASTATE)
+ // SSexplosions.high_mov_atom += cpu
+ // if(EXPLODE_HEAVY)
+ // SSexplosions.med_mov_atom += cpu
+ // if(EXPLODE_LIGHT)
+ // SSexplosions.low_mov_atom += cpu
+ return ..()
// EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components
/obj/machinery/modular_computer/emp_act(severity)
diff --git a/code/modules/modular_computers/computers/machinery/modular_console.dm b/code/modules/modular_computers/computers/machinery/modular_console.dm
index 0e27d8130536..aa6108fcbde5 100644
--- a/code/modules/modular_computers/computers/machinery/modular_console.dm
+++ b/code/modules/modular_computers/computers/machinery/modular_console.dm
@@ -16,7 +16,8 @@
light_strength = 2
max_integrity = 300
integrity_failure = 0.5
- var/console_department = "" // Used in New() to set network tag according to our area.
+ ///Used in New() to set network tag according to our area.
+ var/console_department = ""
/obj/machinery/modular_computer/console/buildable/Initialize()
. = ..()
@@ -52,4 +53,4 @@
network_card.identification_string = "Unknown Console"
if(cpu)
cpu.screen_on = 1
- update_icon()
+ update_appearance()
diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm
index 4e862c4ae35e..08e77b8a9bbe 100644
--- a/code/modules/modular_computers/file_system/computer_file.dm
+++ b/code/modules/modular_computers/file_system/computer_file.dm
@@ -1,11 +1,11 @@
/datum/computer_file
- var/filename = "NewFile" // Placeholder. No spacebars
- var/filetype = "XXX" // File full names are [filename].[filetype] so like NewFile.XXX in this case
- var/size = 1 // File size in GQ. Integers only!
- var/obj/item/computer_hardware/hard_drive/holder // Holder that contains this file.
- var/unsendable = FALSE // Whether the file may be sent to someone via NTNet transfer or other means.
- var/undeletable = FALSE // Whether the file may be deleted. Setting to TRUE prevents deletion/renaming/etc.
- var/uid // UID of this file
+ var/filename = "NewFile" // Placeholder. No spacebars
+ var/filetype = "XXX" // File full names are [filename].[filetype] so like NewFile.XXX in this case
+ var/size = 1 // File size in GQ. Integers only!
+ var/obj/item/computer_hardware/hard_drive/holder // Holder that contains this file.
+ var/unsendable = FALSE // Whether the file may be sent to someone via NTNet transfer or other means.
+ var/undeletable = FALSE // Whether the file may be deleted. Setting to TRUE prevents deletion/renaming/etc.
+ var/uid // UID of this file
var/static/file_uid = 0
/datum/computer_file/New()
diff --git a/code/modules/modular_computers/file_system/data.dm b/code/modules/modular_computers/file_system/data.dm
index 32ef6f53dd1f..e0404e787fb8 100644
--- a/code/modules/modular_computers/file_system/data.dm
+++ b/code/modules/modular_computers/file_system/data.dm
@@ -1,10 +1,10 @@
// /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.
+ var/stored_data = "" // Stored data in string format.
filetype = "DAT"
var/block_size = 250
- var/do_not_edit = 0 // Whether the user will be reminded that the file probably shouldn't be edited.
+ var/do_not_edit = 0 // Whether the user will be reminded that the file probably shouldn't be edited.
/datum/computer_file/data/clone()
var/datum/computer_file/data/temp = ..()
diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm
index a86405f88277..dc0cdda5d136 100644
--- a/code/modules/modular_computers/file_system/program.dm
+++ b/code/modules/modular_computers/file_system/program.dm
@@ -15,6 +15,8 @@
var/filedesc = "Unknown Program"
/// Short description of this program's function.
var/extended_desc = "N/A"
+ /// Category in the NTDownloader.
+ var/category = PROGRAM_CATEGORY_MISC
/// Program-specific screen icon state
var/program_icon_state = null
/// Set to 1 for program to require nonstop NTNet connection to run. If NTNet connection is lost program crashes.
@@ -25,10 +27,10 @@
var/ntnet_status = 1
/// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL
var/usage_flags = PROGRAM_ALL
- /// Whether the program can be downloaded from NTNet. Set to 0 to disable.
- var/available_on_ntnet = 1
- /// Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable.
- var/available_on_syndinet = 0
+ /// Whether the program can be downloaded from NTNet. Set to FALSE to disable.
+ var/available_on_ntnet = TRUE
+ /// Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to TRUE to enable.
+ var/available_on_syndinet = FALSE
/// Name of the tgui interface
var/tgui_id
/// Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /icons/program_icons. Be careful not to use too large images!
@@ -64,7 +66,7 @@
// Relays icon update to the computer.
/datum/computer_file/program/proc/update_computer_icon()
if(computer)
- computer.update_icon()
+ computer.update_appearance()
// Attempts to create a log in global ntnet datum. Returns 1 on success, 0 on fail.
/datum/computer_file/program/proc/generate_network_log(text)
@@ -72,10 +74,25 @@
return computer.add_log(text)
return 0
+/**
+ *Runs when the device is used to attack an atom in non-combat mode.
+ *
+ *Simulates using the device to read or scan something. Tap is called by the computer during pre_attack
+ *and sends us all of the related info. If we return TRUE, the computer will stop the attack process
+ *there. What we do with the info is up to us, but we should only return TRUE if we actually perform
+ *an action of some sort.
+ *Arguments:
+ *A is the atom being tapped
+ *user is the person making the attack action
+ *params is anything the pre_attack() proc had in the same-named variable.
+*/
+/datum/computer_file/program/proc/tap(atom/A, mob/living/user, params)
+ return FALSE
+
/datum/computer_file/program/proc/is_supported_by_hardware(hardware_flag = 0, loud = 0, mob/user = null)
if(!(hardware_flag & usage_flags))
if(loud && computer && user)
- to_chat(user, "\The [computer] flashes a \"Hardware Error - Incompatible software\" warning.")
+ to_chat(user, span_danger("\The [computer] flashes a \"Hardware Error - Incompatible software\" warning."))
return FALSE
return TRUE
@@ -109,7 +126,7 @@
if(!access_to_check) // No required_access, allow it.
return TRUE
- if(!transfer && computer && (computer.obj_flags & EMAGGED)) //emags can bypass the execution locks but not the download ones.
+ if(!transfer && computer && (computer.obj_flags & EMAGGED)) //emags can bypass the execution locks but not the download ones.
return TRUE
if(IsAdminGhost(user))
@@ -127,14 +144,14 @@
if(!D)
if(loud)
- to_chat(user, "\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning.")
+ to_chat(user, span_danger("\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning."))
return FALSE
access = D.GetAccess()
if(access_to_check in access)
return TRUE
if(loud)
- to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.")
+ to_chat(user, span_danger("\The [computer] flashes an \"Access Denied\" warning."))
return FALSE
// This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones
@@ -219,7 +236,7 @@
program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs
computer.active_program = null
- computer.update_icon()
+ computer.update_appearance()
ui.close()
if(user && istype(user))
diff --git a/code/modules/modular_computers/file_system/program_events.dm b/code/modules/modular_computers/file_system/program_events.dm
index 1cb74a227b4f..c11b1066bd22 100644
--- a/code/modules/modular_computers/file_system/program_events.dm
+++ b/code/modules/modular_computers/file_system/program_events.dm
@@ -13,6 +13,6 @@
/datum/computer_file/program/proc/event_networkfailure(background)
kill_program(forced = TRUE)
if(background)
- computer.visible_message("\The [computer]'s screen displays a \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error")
+ computer.visible_message(span_danger("\The [computer]'s screen displays a \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error"))
else
- computer.visible_message("\The [computer]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error.")
+ computer.visible_message(span_danger("\The [computer]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error."))
diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm
index faf2831ca14a..4f181c0e3424 100644
--- a/code/modules/modular_computers/file_system/programs/airestorer.dm
+++ b/code/modules/modular_computers/file_system/programs/airestorer.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/aidiag
filename = "aidiag"
filedesc = "NT FRK"
+ category = PROGRAM_CATEGORY_ROBO
program_icon_state = "generic"
extended_desc = "Firmware Restoration Kit, capable of reconstructing damaged AI systems. Requires direct AI connection via intellicard slot."
size = 12
@@ -55,7 +56,7 @@
/datum/computer_file/program/aidiag/process_tick()
. = ..()
- if(!restoring) //Put the check here so we don't check for an ai all the time
+ if(!restoring) //Put the check here so we don't check for an ai all the time
return
var/obj/item/aicard/cardhold = get_ai(2)
@@ -64,7 +65,7 @@
var/mob/living/silicon/ai/A = get_ai()
if(!A || !cardhold)
- restoring = FALSE // If the AI was removed, stop the restoration sequence.
+ restoring = FALSE // If the AI was removed, stop the restoration sequence.
if(ai_slot)
ai_slot.locked = FALSE
return
@@ -84,7 +85,7 @@
if(A.health >= 0 && A.stat == DEAD)
A.revive(full_heal = FALSE, admin_revive = FALSE)
- cardhold.update_icon()
+ cardhold.update_appearance()
// Finished restoring
if(A.health >= 100)
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
index 8709526de683..1cad0e46f830 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/contract_uplink
filename = "contractor uplink"
filedesc = "Syndicate Contractor Uplink"
+ category = PROGRAM_CATEGORY_MISC
program_icon_state = "assign"
extended_desc = "A standard, Syndicate issued system for handling important contracts while on the field."
size = 10
@@ -91,9 +92,9 @@
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(H.put_in_hands(crystals))
- to_chat(H, "Your payment materializes into your hands!")
+ to_chat(H, span_notice("Your payment materializes into your hands!"))
else
- to_chat(user, "Your payment materializes onto the floor.")
+ to_chat(user, span_notice("Your payment materializes onto the floor."))
hard_drive.traitor_data.contractor_hub.contract_TC_payed_out += hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem
hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem = 0
@@ -164,6 +165,11 @@
))
for (var/datum/syndicate_contract/contract in traitor_data.contractor_hub.assigned_contracts)
+ if(!contract.contract)
+ stack_trace("Syndiate contract with null contract objective found in [traitor_data.owner]'s contractor hub!")
+ contract.status = CONTRACT_STATUS_ABORTED
+ continue
+
data["contracts"] += list(list(
"target" = contract.contract.target,
"target_rank" = contract.target_rank,
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
index bb3c62cac2a1..90510bacdb8e 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/ntnet_dos
filename = "ntn_dos"
filedesc = "DoS Traffic Generator"
+ category = PROGRAM_CATEGORY_MISC
program_icon_state = "hostile"
extended_desc = "This advanced script can perform denial of service attacks against NTNet quantum relays. The system administrator will probably notice this. Multiple devices can run this program together against same relay for increased effect"
size = 20
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 ba24a5ab3e0c..4e5afa32a726 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/revelation
filename = "revelation"
filedesc = "Revelation"
+ category = PROGRAM_CATEGORY_MISC
program_icon_state = "hostile"
extended_desc = "This virus can destroy hard drive of system it is executed on. It may be obfuscated to look like another non-malicious program. Once armed, it will destroy the system upon next execution."
size = 13
@@ -20,13 +21,13 @@
if(computer)
if(istype(computer, /obj/item/modular_computer/tablet/integrated)) //If this is a borg's integrated tablet
var/obj/item/modular_computer/tablet/integrated/modularInterface = computer
- to_chat(modularInterface.borgo,"SYSTEM PURGE DETECTED/")
+ to_chat(modularInterface.borgo,span_userdanger("SYSTEM PURGE DETECTED/"))
addtimer(CALLBACK(modularInterface.borgo, /mob/living/silicon/robot/.proc/death), 2 SECONDS, TIMER_UNIQUE)
return
- computer.visible_message("\The [computer]'s screen brightly flashes and loud electrical buzzing is heard.")
+ computer.visible_message(span_notice("\The [computer]'s screen brightly flashes and loud electrical buzzing is heard."))
computer.enabled = FALSE
- computer.update_icon()
+ computer.update_appearance()
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]
var/obj/item/computer_hardware/recharger/recharger = computer.all_components[MC_CHARGE]
@@ -34,13 +35,13 @@
computer.take_damage(25, BRUTE, 0, 0)
if(battery_module && prob(25))
qdel(battery_module)
- computer.visible_message("\The [computer]'s battery explodes in rain of sparks.")
+ computer.visible_message(span_notice("\The [computer]'s battery explodes in rain of sparks."))
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread
spark_system.start()
if(recharger && prob(50))
qdel(recharger)
- computer.visible_message("\The [computer]'s recharger explodes in rain of sparks.")
+ computer.visible_message(span_notice("\The [computer]'s recharger explodes in rain of sparks."))
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread
spark_system.start()
diff --git a/code/modules/modular_computers/file_system/programs/arcade.dm b/code/modules/modular_computers/file_system/programs/arcade.dm
index c330cdcbe889..3fe38cdf6ffd 100644
--- a/code/modules/modular_computers/file_system/programs/arcade.dm
+++ b/code/modules/modular_computers/file_system/programs/arcade.dm
@@ -32,7 +32,7 @@
game_active = FALSE
program_icon_state = "arcade_off"
if(istype(computer))
- computer.update_icon()
+ computer.update_appearance()
ticket_count += 1
// user?.mind?.adjust_experience(/datum/skill/gaming, 50)
sleep(10)
@@ -42,7 +42,7 @@
game_active = FALSE
program_icon_state = "arcade_off"
if(istype(computer))
- computer.update_icon()
+ computer.update_appearance()
// user?.mind?.adjust_experience(/datum/skill/gaming, 10)
sleep(10)
@@ -150,20 +150,20 @@
return TRUE
if("Dispense_Tickets")
if(!printer)
- to_chat(usr, "Hardware error: A printer is required to redeem tickets.")
+ to_chat(usr, span_notice("Hardware error: A printer is required to redeem tickets."))
return
if(printer.stored_paper <= 0)
- to_chat(usr, "Hardware error: Printer is out of paper.")
+ to_chat(usr, span_notice("Hardware error: Printer is out of paper."))
return
else
- computer.visible_message("\The [computer] prints out paper.")
+ computer.visible_message(span_notice("\The [computer] prints out paper."))
if(ticket_count >= 1)
new /obj/item/stack/arcadeticket((get_turf(computer)), 1)
- to_chat(usr, "[src] dispenses a ticket!")
+ to_chat(usr, span_notice("[computer] dispenses a ticket!"))
ticket_count -= 1
printer.stored_paper -= 1
else
- to_chat(usr, "You don't have any stored tickets!")
+ to_chat(usr, span_notice("You don't have any stored tickets!"))
return TRUE
if("Start_Game")
game_active = TRUE
@@ -175,4 +175,4 @@
boss_id = rand(1,6)
pause_state = FALSE
if(istype(computer))
- computer.update_icon()
+ computer.update_appearance()
diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm
index 1576a5b4b7fd..db9f0d85b4a0 100644
--- a/code/modules/modular_computers/file_system/programs/atmosscan.dm
+++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/atmosscan
filename = "atmosscan"
filedesc = "AtmoZphere"
+ category = PROGRAM_CATEGORY_ENGI
program_icon_state = "air"
extended_desc = "A small built-in sensor reads out the atmospheric conditions around the device."
size = 4
@@ -12,7 +13,7 @@
if (!.)
return
if(!computer?.get_modular_computer_part(MC_SENSORS)) //Giving a clue to users why the program is spitting out zeros.
- to_chat(user, "\The [computer] flashes an error: \"hardware\\sensorpackage\\startup.bin -- file not found\".")
+ to_chat(user, span_warning("\The [computer] flashes an error: \"hardware\\sensorpackage\\startup.bin -- file not found\"."))
/datum/computer_file/program/atmosscan/ui_data(mob/user)
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 46e1f89ee45f..10f65610875d 100644
--- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/borg_monitor
filename = "siliconnect"
filedesc = "SiliConnect"
+ category = PROGRAM_CATEGORY_ROBO
ui_header = "borg_mon.gif"
program_icon_state = "generic"
extended_desc = "This program allows for remote monitoring of station cyborgs."
@@ -9,6 +10,70 @@
size = 5
tgui_id = "NtosCyborgRemoteMonitor"
program_icon = "project-diagram"
+ var/emagged = FALSE ///Bool of if this app has already been emagged
+ var/list/loglist = list() ///A list to copy a borg's IC log list into
+ var/mob/living/silicon/robot/DL_source ///reference of a borg if we're downloading a log, or null if not.
+ var/DL_progress = -1 ///Progress of current download, 0 to 100, -1 for no current download
+
+/datum/computer_file/program/borg_monitor/Destroy()
+ loglist = null
+ DL_source = null
+ return ..()
+
+/datum/computer_file/program/borg_monitor/kill_program(forced = FALSE)
+ loglist = null //Not everything is saved if you close an app
+ DL_source = null
+ DL_progress = 0
+ return ..()
+
+/datum/computer_file/program/borg_monitor/run_emag()
+ if(emagged)
+ return FALSE
+ emagged = TRUE
+ return TRUE
+
+/datum/computer_file/program/borg_monitor/tap(atom/A, mob/living/user, params)
+ var/mob/living/silicon/robot/borgo = A
+ if(!istype(borgo) || !borgo.modularInterface)
+ return FALSE
+ DL_source = borgo
+ DL_progress = 0
+
+ var/username = "unknown user"
+ var/obj/item/card/id/stored_card = computer.GetID()
+ if(istype(stored_card) && stored_card.registered_name)
+ username = "user [stored_card.registered_name]"
+ to_chat(borgo, span_userdanger("Request received from [username] for the system log file. Upload in progress."))//Damning evidence may be contained, so warn the borg
+ borgo.logevent("File request by [username]: /var/logs/syslog")
+ return TRUE
+
+/datum/computer_file/program/borg_monitor/process_tick()
+ if(!DL_source)
+ DL_progress = -1
+ return
+
+ var/turf/here = get_turf(computer)
+ var/turf/there = get_turf(DL_source)
+ if(!here.Adjacent(there))//If someone walked away, cancel the download
+ to_chat(DL_source, span_danger("Log upload failed: general connection error."))//Let the borg know the upload stopped
+ DL_source = null
+ DL_progress = -1
+ return
+
+ if(DL_progress == 100)
+ if(!DL_source || !DL_source.modularInterface) //sanity check, in case the borg or their modular tablet poofs somehow
+ loglist = list("System log of unit [DL_source.name]")
+ loglist += "Error -- Download corrupted."
+ else
+ loglist = DL_source.modularInterface.borglog.Copy()
+ loglist.Insert(1,"System log of unit [DL_source.name]")
+ DL_progress = -1
+ DL_source = null
+ for(var/datum/tgui/window in SStgui.open_uis_by_src[REF(src)])
+ window.send_full_update()
+ return
+
+ DL_progress += 25
/datum/computer_file/program/borg_monitor/ui_data(mob/user)
var/list/data = get_header_data()
@@ -32,15 +97,22 @@
var/list/cyborg_data = list(
name = R.name,
+ integ = round((R.health + 100) / 2), //mob heath is -100 to 100, we want to scale that to 0 - 100
locked_down = R.locked_down,
status = R.stat,
shell_discon = shell,
charge = R.cell ? round(R.cell.percent()) : null,
- module = R.module ? "[R.module.name] Module" : "No Module Detected",
+ module = R.module ? "[R.module.name] Model" : "No Model Detected",
upgrades = upgrade,
ref = REF(R)
)
data["cyborgs"] += list(cyborg_data)
+ data["DL_progress"] = DL_progress
+ return data
+
+/datum/computer_file/program/borg_monitor/ui_static_data(mob/user)
+ var/list/data = list()
+ data["borglog"] = loglist
return data
/datum/computer_file/program/borg_monitor/ui_act(action, params)
@@ -57,16 +129,16 @@
if(!ID)
return
if(R.stat == DEAD) //Dead borgs will listen to you no longer
- to_chat(usr, "Error -- Could not open a connection to unit:[R]")
+ to_chat(usr, span_warning("Error -- Could not open a connection to unit:[R]"))
var/message = stripped_input(usr, message = "Enter message to be sent to remote cyborg.", title = "Send Message")
if(!message)
return
- to_chat(R, "
Message from [ID] -- \"[message]\"
")
+ to_chat(R, "
[span_notice("Message from [ID] -- \"[message]\"")]
")
to_chat(usr, "Message sent to [R]: [message]")
R.logevent("Message from [ID] -- \"[message]\"")
SEND_SOUND(R, 'sound/machines/twobeep_high.ogg')
if(R.connected_ai)
- to_chat(R.connected_ai, "
Message from [ID] to [R] -- \"[message]\"
")
+ to_chat(R.connected_ai, "
[span_notice("Message from [ID] to [R] -- \"[message]\"")]
")
SEND_SOUND(R.connected_ai, 'sound/machines/twobeep_high.ogg')
usr.log_talk(message, LOG_PDA, tag="Cyborg Monitor Program: ID name \"[ID]\" to [R]")
@@ -82,12 +154,15 @@
/datum/computer_file/program/borg_monitor/proc/checkID()
var/obj/item/card/id/ID = computer.GetID()
if(!ID)
+ if(emagged)
+ return "STDERR:UNDF"
return FALSE
return ID.registered_name
/datum/computer_file/program/borg_monitor/syndicate
filename = "roboverlord"
filedesc = "Roboverlord"
+ category = PROGRAM_CATEGORY_ROBO
ui_header = "borg_mon.gif"
program_icon_state = "generic"
extended_desc = "This program allows for remote monitoring of mission-assigned cyborgs."
@@ -97,6 +172,9 @@
transfer_access = null
tgui_id = "NtosCyborgRemoteMonitorSyndicate"
+/datum/computer_file/program/borg_monitor/syndicate/run_emag()
+ return FALSE
+
/datum/computer_file/program/borg_monitor/syndicate/evaluate_borg(mob/living/silicon/robot/R)
if((get_turf(computer)).z != (get_turf(R)).z)
return FALSE
diff --git a/code/modules/modular_computers/file_system/programs/bounty_board.dm b/code/modules/modular_computers/file_system/programs/bounty_board.dm
index 9c42a28a9bc9..d008d5e72be3 100644
--- a/code/modules/modular_computers/file_system/programs/bounty_board.dm
+++ b/code/modules/modular_computers/file_system/programs/bounty_board.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/bounty_board
filename = "bountyboard"
filedesc = "Bounty Board Request Network"
+ category = PROGRAM_CATEGORY_SUPL
program_icon_state = "bountyboard"
extended_desc = "A multi-platform network for placing requests across the station, with payment across the network being possible.."
requires_ntnet = TRUE
diff --git a/code/modules/modular_computers/file_system/programs/budgetordering.dm b/code/modules/modular_computers/file_system/programs/budgetordering.dm
index 4fda99864c5e..1f8fd6f7a60d 100644
--- a/code/modules/modular_computers/file_system/programs/budgetordering.dm
+++ b/code/modules/modular_computers/file_system/programs/budgetordering.dm
@@ -1,41 +1,50 @@
/datum/computer_file/program/budgetorders
filename = "orderapp"
filedesc = "NT IRN"
- // category = PROGRAM_CATEGORY_SUPL
+ category = PROGRAM_CATEGORY_SUPL
program_icon_state = "request"
extended_desc = "Nanotrasen Internal Requisition Network interface for supply purchasing using a department budget account."
requires_ntnet = TRUE
- transfer_access = ACCESS_HEADS
usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET
size = 20
tgui_id = "NtosCargo"
///Are you actually placing orders with it?
var/requestonly = TRUE
///Can the tablet see or buy illegal stuff?
- var/contraband_view = FALSE
+ var/contraband = FALSE
///Is it being bought from a personal account, or is it being done via a budget/cargo?
var/self_paid = FALSE
///Can this console approve purchase requests?
var/can_approve_requests = FALSE
///What do we say when the shuttle moves with living beings on it.
- var/safety_warning = "For safety reasons, the automated supply shuttle \
- cannot transport live organisms, human remains, classified nuclear weaponry, \
- homing beacons or machinery housing any form of artificial intelligence."
+ var/safety_warning = "For safety and ethical reasons, the automated supply shuttle \
+ cannot transport live organisms, human remains, classified nuclear weaponry, mail, \
+ homing beacons, unstable eigenstates or machinery housing any form of artificial intelligence."
///If you're being raided by pirates, what do you tell the crew?
var/blockade_warning = "Bluespace instability detected. Shuttle movement impossible."
+ ///The name of the shuttle template being used as the cargo shuttle. 'supply' is default and contains critical code. Don't change this unless you know what you're doing.
+ var/cargo_shuttle = "supply"
+ ///The docking port called when returning to the station.
+ var/docking_home = "supply_home"
+ ///The docking port called when leaving the station.
+ var/docking_away = "supply_away"
+ ///If this console can loan the cargo shuttle. Set to false to disable.
+ var/stationcargo = TRUE
+ ///The account this console processes and displays. Independent from the account the shuttle processes.
+ var/cargo_account = ACCOUNT_CAR
/datum/computer_file/program/budgetorders/proc/get_export_categories()
. = EXPORT_CARGO
/datum/computer_file/program/budgetorders/run_emag()
- if(!contraband_view)
- contraband_view = TRUE
+ if(!contraband)
+ contraband = TRUE
return TRUE
/datum/computer_file/program/budgetorders/proc/is_visible_pack(mob/user, paccess_to_check, list/access, contraband)
if(issilicon(user)) //Borgs can't buy things.
return FALSE
- if((computer.obj_flags & EMAGGED) || contraband_view)
+ if(computer.obj_flags & EMAGGED)
return TRUE
else if(contraband) //Hide contrband when non-emagged.
return FALSE
@@ -64,11 +73,11 @@
. = ..()
var/list/data = get_header_data()
data["location"] = SSshuttle.supply.getStatusText()
- var/datum/bank_account/buyer = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ var/datum/bank_account/buyer = SSeconomy.get_dep_account(cargo_account)
var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD]
var/obj/item/card/id/id_card = card_slot?.GetID()
if(id_card?.registered_account)
- if(ACCESS_HEADS in id_card.access)
+ if((ACCESS_HEADS in id_card.access) || (ACCESS_QM in id_card.access))
requestonly = FALSE
buyer = SSeconomy.get_dep_account(id_card.registered_account.account_job.paycheck_department)
can_approve_requests = TRUE
@@ -85,14 +94,14 @@
data["supplies"] = list()
for(var/pack in SSshuttle.supply_packs)
var/datum/supply_pack/P = SSshuttle.supply_packs[pack]
- if(!is_visible_pack(usr, P.access , null, P.contraband))
+ if(!is_visible_pack(usr, P.access , null, P.contraband) || P.hidden)
continue
if(!data["supplies"][P.group])
data["supplies"][P.group] = list(
"name" = P.group,
"packs" = list()
)
- if(((P.hidden || P.contraband) && !contraband_view) || (P.special && !P.special_enabled) || P.DropPodOnly)
+ if((P.hidden && (P.contraband && !contraband) || (P.special && !P.special_enabled) || P.DropPodOnly))
continue
data["supplies"][P.group]["packs"] += list(list(
"name" = P.name,
@@ -105,7 +114,7 @@
//Data regarding the User's capability to buy things.
data["has_id"] = id_card
- data["away"] = SSshuttle.supply.getDockedId() == "supply_away"
+ data["away"] = SSshuttle.supply.getDockedId() == docking_away
data["self_paid"] = self_paid
data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE
data["loan"] = !!SSshuttle.shuttle_loan
@@ -153,15 +162,15 @@
if(SSshuttle.supplyBlocked)
computer.say(blockade_warning)
return
- if(SSshuttle.supply.getDockedId() == "supply_home")
+ if(SSshuttle.supply.getDockedId() == docking_home)
SSshuttle.supply.export_categories = get_export_categories()
- SSshuttle.moveShuttle("supply", "supply_away", TRUE)
+ SSshuttle.moveShuttle(cargo_shuttle, docking_away, TRUE)
computer.say("The supply shuttle is departing.")
computer.investigate_log("[key_name(usr)] sent the supply shuttle away.", INVESTIGATE_CARGO)
else
computer.investigate_log("[key_name(usr)] called the supply shuttle.", INVESTIGATE_CARGO)
computer.say("The supply shuttle has been called and will arrive in [SSshuttle.supply.timeLeft(600)] minutes.")
- SSshuttle.moveShuttle("supply", "supply_home", TRUE)
+ SSshuttle.moveShuttle(cargo_shuttle, docking_home, TRUE)
. = TRUE
if("loan")
if(!SSshuttle.shuttle_loan)
@@ -171,7 +180,9 @@
return
else if(SSshuttle.supply.mode != SHUTTLE_IDLE)
return
- else if(SSshuttle.supply.getDockedId() != "supply_away")
+ else if(SSshuttle.supply.getDockedId() != docking_away)
+ return
+ else if(stationcargo != TRUE)
return
else
SSshuttle.shuttle_loan.loan_shuttle()
@@ -184,7 +195,7 @@
var/datum/supply_pack/pack = SSshuttle.supply_packs[id]
if(!istype(pack))
return
- if(((pack.hidden || pack.contraband) && !contraband_view) || pack.DropPodOnly)
+ if((pack.hidden && (pack.contraband && !contraband) || pack.DropPodOnly))
return
var/name = "*None Provided*"
@@ -273,7 +284,7 @@
self_paid = !self_paid
. = TRUE
if(.)
- post_signal("supply")
+ post_signal(cargo_shuttle)
/datum/computer_file/program/budgetorders/proc/post_signal(command)
diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm
index 65bb5f2343b0..18e717191e03 100644
--- a/code/modules/modular_computers/file_system/programs/card.dm
+++ b/code/modules/modular_computers/file_system/programs/card.dm
@@ -9,6 +9,7 @@
/datum/computer_file/program/card_mod
filename = "plexagonidwriter"
filedesc = "Plexagon Access Management"
+ 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
diff --git a/code/modules/modular_computers/file_system/programs/cargoship.dm b/code/modules/modular_computers/file_system/programs/cargoship.dm
index a023f928311b..672c0de4700c 100644
--- a/code/modules/modular_computers/file_system/programs/cargoship.dm
+++ b/code/modules/modular_computers/file_system/programs/cargoship.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/shipping
filename = "shipping"
filedesc = "GrandArk Exporter"
+ category = PROGRAM_CATEGORY_SUPL
program_icon_state = "shipping"
extended_desc = "A combination printer/scanner app that enables modular computers to print barcodes for easy scanning and shipping."
size = 6
@@ -8,8 +9,12 @@
program_icon = "tags"
///Account used for creating barcodes.
var/datum/bank_account/payments_acc
- ///The amount which the tagger will receive for the sale.
- var/percent_cut = 20
+ ///The person who tagged this will receive the sale value multiplied by this number.
+ var/cut_multiplier = 0.5
+ ///Maximum value for cut_multiplier.
+ var/cut_max = 0.5
+ ///Minimum value for cut_multiplier.
+ var/cut_min = 0.01
/datum/computer_file/program/shipping/ui_data(mob/user)
var/list/data = get_header_data()
@@ -22,7 +27,7 @@
data["paperamt"] = printer ? "[printer.stored_paper] / [printer.max_paper]" : null
data["card_owner"] = card_slot?.stored_card ? id_card.registered_name : "No Card Inserted."
data["current_user"] = payments_acc ? payments_acc.account_holder : null
- data["barcode_split"] = percent_cut
+ data["barcode_split"] = cut_multiplier * 100
return data
/datum/computer_file/program/shipping/ui_act(action, list/params)
@@ -54,20 +59,20 @@
if("resetid")
payments_acc = null
if("setsplit")
- var/potential_cut = tgui_input_num(usr, "How much would you like to payout to the registered card?","Percentage Profit")
- percent_cut = potential_cut ? clamp(round(potential_cut, 1), 1, 50) : 20
+ var/potential_cut = input("How much would you like to pay out to the registered card?","Percentage Profit ([round(cut_min*100)]% - [round(cut_max*100)]%)") as num|null
+ cut_multiplier = potential_cut ? clamp(round(potential_cut/100, cut_min), cut_min, cut_max) : initial(cut_multiplier)
if("print")
if(!printer)
- to_chat(usr, "Hardware error: A printer is required to print barcodes.")
+ to_chat(usr, span_notice("Hardware error: A printer is required to print barcodes."))
return
if(printer.stored_paper <= 0)
- to_chat(usr, "Hardware error: Printer is out of paper.")
+ to_chat(usr, span_notice("Hardware error: Printer is out of paper."))
return
if(!payments_acc)
- to_chat(usr, "Software error: Please set a current user first.")
+ to_chat(usr, span_notice("Software error: Please set a current user first."))
return
var/obj/item/barcode/barcode = new /obj/item/barcode(get_turf(ui_host()))
barcode.payments_acc = payments_acc
- barcode.percent_cut = percent_cut
+ barcode.cut_multiplier = cut_multiplier
printer.stored_paper--
- to_chat(usr, "The computer prints out a barcode.")
+ to_chat(usr, span_notice("The computer prints out a barcode."))
diff --git a/code/modules/modular_computers/file_system/programs/crewmanifest.dm b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
index debe87259def..bc6923773ea2 100644
--- a/code/modules/modular_computers/file_system/programs/crewmanifest.dm
+++ b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/crew_manifest
filename = "plexagoncrew"
filedesc = "Plexagon Crew List"
+ category = PROGRAM_CATEGORY_CREW
program_icon_state = "id"
extended_desc = "Program for viewing and printing the current crew manifest"
transfer_access = ACCESS_HEADS
@@ -11,7 +12,7 @@
/datum/computer_file/program/crew_manifest/ui_static_data(mob/user)
var/list/data = list()
- data["manifest"] = GLOB.data_core.get_manifest_tg()
+ data["manifest"] = GLOB.data_core.get_manifest()
return data
/datum/computer_file/program/crew_manifest/ui_data(mob/user)
@@ -44,7 +45,7 @@
[GLOB.data_core ? GLOB.data_core.get_manifest() : ""]
"}
if(!printer.print_text(contents,text("crew manifest ([])", STATION_TIME_TIMESTAMP("hh:mm:ss", world.time))))
- to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.")
+ to_chat(usr, span_notice("Hardware error: Printer was unable to print the file. It may be out of paper."))
return
else
- computer.visible_message("\The [computer] prints out a paper.")
+ computer.visible_message(span_notice("\The [computer] prints out a paper."))
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 97a71496eaba..60a9536543b2 100644
--- a/code/modules/modular_computers/file_system/programs/file_browser.dm
+++ b/code/modules/modular_computers/file_system/programs/file_browser.dm
@@ -38,14 +38,27 @@
return
RHDD.remove_file(file)
return TRUE
- if("PRG_rename")
+ if("PRG_renamefile")
if(!HDD)
return
var/datum/computer_file/file = HDD.find_file_by_name(params["name"])
if(!file)
return
- var/newname = params["new_name"]
- if(!newname)
+ var/newname = reject_bad_name(params["new_name"])
+ if(!newname || newname != params["new_name"])
+ playsound(computer, 'sound/machines/terminal_error.ogg', 25, FALSE)
+ return
+ file.filename = newname
+ return TRUE
+ if("PRG_usbrenamefile")
+ if(!RHDD)
+ return
+ var/datum/computer_file/file = RHDD.find_file_by_name(params["name"])
+ if(!file)
+ return
+ var/newname = reject_bad_name(params["new_name"])
+ if(!newname || newname != params["new_name"])
+ playsound(computer, 'sound/machines/terminal_error.ogg', 25, FALSE)
return
file.filename = newname
return TRUE
diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
index 0babfcdddd4a..a15016ac0eda 100644
--- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm
+++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
@@ -1,6 +1,10 @@
+/// The time since the last job opening was created
+// GLOBAL_VAR_INIT(time_last_changed_position, 0)
+
/datum/computer_file/program/job_management
filename = "plexagoncore"
filedesc = "Plexagon HR Core"
+ category = PROGRAM_CATEGORY_CREW
program_icon_state = "id"
extended_desc = "Program for viewing and changing job slot avalibility."
transfer_access = ACCESS_HEADS
@@ -35,21 +39,25 @@
change_position_cooldown = CONFIG_GET(number/id_console_jobslot_delay)
/datum/computer_file/program/job_management/proc/can_open_job(datum/job/job)
- if(!(job?.title in blacklisted))
- if((job.total_positions <= length(GLOB.player_list) * (max_relative_positions / 100)))
- var/delta = (world.time / 10) - GLOB.time_last_changed_position
- if((change_position_cooldown < delta) || (opened_positions[job.title] < 0))
- return TRUE
+ if(job?.title in blacklisted)
+ return FALSE
+ if((job.total_positions <= length(GLOB.player_list) * (max_relative_positions / 100)))
+ var/delta = (world.time / 10) - GLOB.time_last_changed_position
+ if((change_position_cooldown < delta) || (opened_positions[job.title] < 0))
+ return TRUE
return FALSE
+
/datum/computer_file/program/job_management/proc/can_close_job(datum/job/job)
- if(!(job?.title in blacklisted))
- if(job.total_positions > job.current_positions)
- var/delta = (world.time / 10) - GLOB.time_last_changed_position
- if((change_position_cooldown < delta) || (opened_positions[job.title] > 0))
- return TRUE
+ if(job?.title in blacklisted)
+ return FALSE
+ if(job.total_positions > job.current_positions)
+ var/delta = (world.time / 10) - GLOB.time_last_changed_position
+ if((change_position_cooldown < delta) || (opened_positions[job.title] > 0))
+ return TRUE
return FALSE
+
/datum/computer_file/program/job_management/ui_act(action, params, datum/tgui/ui)
. = ..()
if(.)
@@ -68,9 +76,10 @@
if(!j || !can_open_job(j))
return
if(opened_positions[edit_job_target] >= 0)
- GLOB.time_last_changed_position = world.time / 10 // global cd
+ GLOB.time_last_changed_position = world.time / 10
j.total_positions++
opened_positions[edit_job_target]++
+ log_game("[key_name(usr)] opened a [j.title] job position, for a total of [j.total_positions] open job slots.")
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
if("PRG_close_job")
@@ -83,21 +92,23 @@
GLOB.time_last_changed_position = world.time / 10
j.total_positions--
opened_positions[edit_job_target]--
+ log_game("[key_name(usr)] closed a [j.title] job position, leaving [j.total_positions] open job slots.")
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
if("PRG_priority")
- if(length(SSjob.prioritized_jobs) >= 5)
- return
var/priority_target = params["target"]
var/datum/job/j = SSjob.GetJob(priority_target)
- if(!j)
+ if(!j || (job?.title in blacklisted))
return
if(j.total_positions <= j.current_positions)
return
if(j in SSjob.prioritized_jobs)
SSjob.prioritized_jobs -= j
else
- SSjob.prioritized_jobs += j
+ if(length(SSjob.prioritized_jobs) < 5)
+ SSjob.prioritized_jobs += j
+ else
+ computer.say("Error: CentCom employment protocols restrict prioritising more than 5 jobs.")
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
index f3fa6df2b3a6..56283e3c8be7 100644
--- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm
+++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
@@ -22,6 +22,13 @@
var/emagged = FALSE
var/list/main_repo
var/list/antag_repo
+ var/list/show_categories = list(
+ PROGRAM_CATEGORY_CREW,
+ PROGRAM_CATEGORY_ENGI,
+ PROGRAM_CATEGORY_ROBO,
+ PROGRAM_CATEGORY_SUPL,
+ PROGRAM_CATEGORY_MISC,
+ )
/datum/computer_file/program/ntnetdownload/run_program()
. = ..()
@@ -146,39 +153,29 @@
var/obj/item/computer_hardware/hard_drive/hard_drive = my_computer.all_components[MC_HDD]
data["disk_size"] = hard_drive.max_capacity
data["disk_used"] = hard_drive.used_capacity
- var/list/all_entries[0]
- for(var/A in main_repo)
- var/datum/computer_file/program/P = A
- // Only those programs our user can run will show in the list
- if(hard_drive.find_file_by_name(P.filename))
- continue
- all_entries.Add(list(list(
+ data["emagged"] = emagged
+
+ var/list/repo = antag_repo | main_repo
+ var/list/program_categories = list()
+
+ for(var/I in repo)
+ var/datum/computer_file/program/P = I
+ if(!(P.category in program_categories))
+ program_categories.Add(P.category)
+ data["programs"] += list(list(
+ "icon" = P.program_icon,
"filename" = P.filename,
"filedesc" = P.filedesc,
"fileinfo" = P.extended_desc,
- "compatibility" = check_compatibility(P),
+ "category" = P.category,
+ "installed" = !!hard_drive.find_file_by_name(P.filename),
+ "compatible" = check_compatibility(P),
"size" = P.size,
- "access" = P.can_run(user,transfer = 1, access = access)
- )))
- data["hackedavailable"] = FALSE
- if(emagged) // If we are running on emagged computer we have access to some "bonus" software
- var/list/hacked_programs[0]
- for(var/S in antag_repo)
- var/datum/computer_file/program/P = S
- if(hard_drive.find_file_by_name(P.filename))
- continue
- data["hackedavailable"] = TRUE
- hacked_programs.Add(list(list(
- "filename" = P.filename,
- "filedesc" = P.filedesc,
- "fileinfo" = P.extended_desc,
- "compatibility" = check_compatibility(P),
- "size" = P.size,
- "access" = TRUE,
- )))
- data["hacked_programs"] = hacked_programs
-
- data["downloadable_programs"] = all_entries
+ "access" = emagged && P.available_on_syndinet ? TRUE : P.can_run(user,transfer = 1, access = access),
+ "verifiedsource" = P.available_on_ntnet,
+ ))
+
+ data["categories"] = show_categories & program_categories
return data
@@ -186,8 +183,8 @@
var/hardflag = computer.hardware_flag
if(P?.is_supported_by_hardware(hardflag,0))
- return "Compatible"
- return "Incompatible!"
+ return TRUE
+ return FALSE
/datum/computer_file/program/ntnetdownload/kill_program(forced)
abort_file_download()
diff --git a/code/modules/modular_computers/file_system/programs/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/ntmonitor.dm
index 63f0b18a7477..aeb81060404d 100644
--- a/code/modules/modular_computers/file_system/programs/ntmonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/ntmonitor.dm
@@ -1,11 +1,12 @@
/datum/computer_file/program/ntnetmonitor
filename = "wirecarp"
filedesc = "WireCarp"
+ category = PROGRAM_CATEGORY_MISC
program_icon_state = "comm_monitor"
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 = 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 19172f130ab3..29a4418d2ca5 100644
--- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
+++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
@@ -1,25 +1,36 @@
+
/datum/computer_file/program/chatclient
filename = "ntnrc_client"
filedesc = "Chat Client"
+ category = PROGRAM_CATEGORY_MISC
program_icon_state = "command"
extended_desc = "This program allows communication over NTNRC network"
size = 8
- requires_ntnet = 1
+ requires_ntnet = TRUE
requires_ntnet_feature = NTNET_COMMUNICATION
ui_header = "ntnrc_idle.gif"
- available_on_ntnet = 1
+ available_on_ntnet = TRUE
tgui_id = "NtosNetChat"
program_icon = "comment-alt"
- var/last_message // Used to generate the toolbar icon
+ alert_able = TRUE
+ var/last_message // Used to generate the toolbar icon
var/username
var/active_channel
var/list/channel_history = list()
- var/operator_mode = FALSE // Channel operator mode
- var/netadmin_mode = FALSE // Administrator mode (invisible to other users + bypasses passwords)
+ var/operator_mode = FALSE // Channel operator mode
+ var/netadmin_mode = FALSE // Administrator mode (invisible to other users + bypasses passwords)
+ //A list of all the converstations we're a part of
+ var/list/datum/ntnet_conversation/conversations = list()
/datum/computer_file/program/chatclient/New()
username = "DefaultUser[rand(100, 999)]"
+/datum/computer_file/program/chatclient/Destroy()
+ for(var/datum/ntnet_conversation/discussion as anything in conversations)
+ discussion.purge_client(src)
+ conversations.Cut()
+ return ..()
+
/datum/computer_file/program/chatclient/ui_act(action, params)
. = ..()
if(.)
@@ -36,7 +47,7 @@
var/message = reject_bad_text(params["message"])
if(!message)
return
- if(channel.password && !(src in channel.clients))
+ if(channel.password && (!(src in channel.active_clients) && !(src in channel.offline_clients)))
if(channel.password == message)
channel.add_client(src)
return TRUE
@@ -56,7 +67,7 @@
active_channel = new_target
channel = SSnetworks.station_network.get_chat_channel_by_id(new_target)
- if(!(src in channel.clients) && !channel.password)
+ if((!(src in channel.active_clients) && !(src in channel.offline_clients)) && !channel.password)
channel.add_client(src)
return TRUE
if("PRG_leavechannel")
@@ -89,12 +100,12 @@
return TRUE
if("PRG_changename")
var/newname = sanitize(params["new_name"])
- if(!newname)
+ newname = replacetext(newname, " ", "_")
+ if(!newname || newname == username)
return
- for(var/C in SSnetworks.station_network.chat_channels)
- var/datum/ntnet_conversation/chan = C
- if(src in chan.clients)
- chan.add_status_message("[username] is now known as [newname].")
+ for(var/datum/ntnet_conversation/anychannel as anything in SSnetworks.station_network.chat_channels)
+ if(src in anychannel.active_clients)
+ anychannel.add_status_message("[username] is now known as [newname].")
username = newname
return TRUE
if("PRG_savelog")
@@ -117,9 +128,9 @@
// 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.")
+ computer.visible_message(span_warning("\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(span_warning("\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."))
return TRUE
if("PRG_renamechannel")
if(!authed)
@@ -145,6 +156,18 @@
channel.password = new_password
return TRUE
+ if("PRG_mute_user")
+ if(!authed)
+ return
+ var/datum/computer_file/program/chatclient/muted = locate(params["ref"]) in channel.active_clients + channel.offline_clients
+ channel.mute_user(src, muted)
+ return TRUE
+ if("PRG_ping_user")
+ if(!authed)
+ return
+ var/datum/computer_file/program/chatclient/pinged = locate(params["ref"]) in channel.active_clients + channel.offline_clients
+ channel.ping_user(src, pinged)
+ return TRUE
/datum/computer_file/program/chatclient/process_tick()
. = ..()
@@ -162,10 +185,19 @@
else
ui_header = "ntnrc_idle.gif"
+/datum/computer_file/program/chatclient/run_program(mob/living/user)
+ . = ..()
+ if(!.)
+ return
+ for(var/datum/ntnet_conversation/channel as anything in SSnetworks.station_network.chat_channels)
+ if(src in channel.offline_clients)
+ channel.offline_clients.Remove(src)
+ channel.active_clients.Add(src)
+
/datum/computer_file/program/chatclient/kill_program(forced = FALSE)
- for(var/C in SSnetworks.station_network.chat_channels)
- var/datum/ntnet_conversation/channel = C
- channel.remove_client(src)
+ for(var/datum/ntnet_conversation/channel as anything in SSnetworks.station_network.chat_channels)
+ channel.go_offline(src)
+ active_channel = null
..()
/datum/computer_file/program/chatclient/ui_static_data(mob/user)
@@ -192,6 +224,7 @@
data["all_channels"] = all_channels
data["active_channel"] = active_channel
+ data["selfref"] = REF(src) //used to verify who is you, as usernames can be copied.
data["username"] = username
data["adminmode"] = netadmin_mode
var/datum/ntnet_conversation/channel = SSnetworks.station_network.get_chat_channel_by_id(active_channel)
@@ -203,21 +236,25 @@
if(netadmin_mode)
authed = TRUE
var/list/clients = list()
- for(var/C in channel.clients)
- if(C == src)
+ for(var/datum/computer_file/program/chatclient/channel_client as anything in channel.active_clients + channel.offline_clients)
+ if(channel_client == src)
authed = TRUE
- var/datum/computer_file/program/chatclient/cl = C
clients.Add(list(list(
- "name" = cl.username
+ "name" = channel_client.username,
+ "status" = channel_client.program_state,
+ "muted" = (channel_client in channel.muted_clients),
+ "operator" = channel.operator == channel_client,
+ "ref" = REF(channel_client)
)))
data["authed"] = authed
//no fishing for ui data allowed
if(authed)
+ data["strong"] = channel.strong
data["clients"] = clients
var/list/messages = list()
- for(var/M in channel.messages)
+ for(var/message in channel.messages)
messages.Add(list(list(
- "msg" = M
+ "msg" = message
)))
data["messages"] = messages
data["is_operator"] = (channel.operator == src) || netadmin_mode
diff --git a/code/modules/modular_computers/file_system/programs/portrait_printer.dm b/code/modules/modular_computers/file_system/programs/portrait_printer.dm
new file mode 100644
index 000000000000..12a2187550ef
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/portrait_printer.dm
@@ -0,0 +1,83 @@
+
+///how much paper it takes from the printer to create a canvas.
+#define CANVAS_PAPER_COST 10
+
+/**
+ * ## portrait printer!
+ *
+ * Program that lets the curator browse all of the portraits in the database
+ * They are free to print them out as they please.
+ */
+/datum/computer_file/program/portrait_printer
+ filename = "PortraitPrinter"
+ filedesc = "Marlowe Treeby's Art Galaxy"
+ category = PROGRAM_CATEGORY_CREW
+ 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
+ usage_flags = PROGRAM_CONSOLE
+ requires_ntnet = TRUE
+ size = 9
+ tgui_id = "NtosPortraitPrinter"
+ program_icon = "paint-brush"
+
+/datum/computer_file/program/portrait_printer/ui_data(mob/user)
+ var/list/data = list()
+ data["library"] = SSpersistence.paintings["library"] ? SSpersistence.paintings["library"] : 0
+ data["library_secure"] = SSpersistence.paintings["library_secure"] ? SSpersistence.paintings["library_secure"] : 0
+ data["library_private"] = SSpersistence.paintings["library_private"] ? SSpersistence.paintings["library_private"] : 0 //i'm gonna regret this, won't i?
+ return data
+
+/datum/computer_file/program/portrait_printer/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/simple/portraits/library),
+ get_asset_datum(/datum/asset/simple/portraits/library_secure),
+ get_asset_datum(/datum/asset/simple/portraits/library_private)
+ )
+
+/datum/computer_file/program/portrait_printer/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+
+ //printer check!
+ var/obj/item/computer_hardware/printer/printer
+ if(computer)
+ printer = computer.all_components[MC_PRINT]
+ if(!printer)
+ to_chat(usr, span_notice("Hardware error: A printer is required to print a canvas."))
+ return
+ if(printer.stored_paper < CANVAS_PAPER_COST)
+ to_chat(usr, span_notice("Printing error: Your printer needs at least [CANVAS_PAPER_COST] paper to print a canvas."))
+ return
+ printer.stored_paper -= CANVAS_PAPER_COST
+
+ //canvas printing!
+ var/list/tab2key = list(TAB_LIBRARY = "library", TAB_SECURE = "library_secure", TAB_PRIVATE = "library_private")
+ var/folder = tab2key[params["tab"]]
+ var/list/current_list = SSpersistence.paintings[folder]
+ var/list/chosen_portrait = current_list[params["selected"]]
+ var/author = chosen_portrait["author"]
+ var/title = chosen_portrait["title"]
+ var/png = "data/paintings/[folder]/[chosen_portrait["md5"]].png"
+ var/icon/art_icon = new(png)
+ var/obj/item/canvas/printed_canvas
+ var/art_width = art_icon.Width()
+ var/art_height = art_icon.Height()
+ for(var/canvas_type in typesof(/obj/item/canvas))
+ printed_canvas = canvas_type
+ if(initial(printed_canvas.width) == art_width && initial(printed_canvas.height) == art_height)
+ printed_canvas = new canvas_type(get_turf(computer.physical))
+ break
+ printed_canvas.fill_grid_from_icon(art_icon)
+ printed_canvas.generated_icon = art_icon
+ printed_canvas.icon_generated = TRUE
+ printed_canvas.finalized = TRUE
+ printed_canvas.painting_name = title
+ printed_canvas.author_ckey = author
+ printed_canvas.name = "painting - [title]"
+ ///this is a copy of something that is already in the database- it should not be able to be saved.
+ printed_canvas.no_save = TRUE
+ printed_canvas.update_icon()
+ to_chat(usr, span_notice("You have printed [title] onto a new canvas."))
+ playsound(computer.physical, 'sound/items/poster_being_created.ogg', 100, TRUE)
diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm
index 78a14ff1ad8c..933c9410a27d 100644
--- a/code/modules/modular_computers/file_system/programs/powermonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm
@@ -3,6 +3,7 @@
/datum/computer_file/program/power_monitor
filename = "ampcheck"
filedesc = "AmpCheck"
+ category = PROGRAM_CATEGORY_ENGI
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"
diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm
index 0bf5eb211845..4b14a611527a 100644
--- a/code/modules/modular_computers/file_system/programs/radar.dm
+++ b/code/modules/modular_computers/file_system/programs/radar.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/radar //generic parent that handles most of the process
filename = "genericfinder"
filedesc = "debug_finder"
+ category = PROGRAM_CATEGORY_CREW
ui_header = "borg_mon.gif" //DEBUG -- new icon before PR
program_icon_state = "radarntos"
requires_ntnet = TRUE
@@ -15,7 +16,7 @@
var/atom/selected
///Used to store when the next scan is available. Updated by the scan() proc.
var/next_scan = 0
- ///Used to keep track of the last value program_icon_state was set to, to prevent constant unnecessary update_icon() calls
+ ///Used to keep track of the last value program_icon_state was set to, to prevent constant unnecessary update_appearance() calls
var/last_icon_state = ""
///Used by the tgui interface, themed NT or Syndicate.
var/arrowstyle = "ntosradarpointer.png"
@@ -174,7 +175,7 @@
if(!trackable(signal))
program_icon_state = "[initial(program_icon_state)]lost"
if(last_icon_state != program_icon_state)
- computer.update_icon()
+ computer.update_appearance()
last_icon_state = program_icon_state
return
@@ -192,7 +193,7 @@
program_icon_state = "[initial(program_icon_state)]far"
if(last_icon_state != program_icon_state)
- computer.update_icon()
+ computer.update_appearance()
last_icon_state = program_icon_state
computer.setDir(get_dir(here_turf, target_turf))
@@ -241,13 +242,12 @@
/datum/computer_file/program/radar/lifeline/trackable(mob/living/carbon/human/humanoid)
if(!humanoid || !istype(humanoid))
return FALSE
- if(..() && istype(humanoid.w_uniform, /obj/item/clothing/under))
-
- var/obj/item/clothing/under/uniform = humanoid.w_uniform
- if(!uniform.has_sensor || (uniform.sensor_mode < SENSOR_COORDS)) // Suit sensors must be on maximum.
- return FALSE
-
- return TRUE
+ if(..())
+ if (istype(humanoid.w_uniform, /obj/item/clothing/under))
+ var/obj/item/clothing/under/uniform = humanoid.w_uniform
+ if(uniform.has_sensor && uniform.sensor_mode >= SENSOR_COORDS) // Suit sensors must be on maximum
+ return TRUE
+ return FALSE
////////////////////////
//Nuke Disk Finder App//
@@ -257,6 +257,7 @@
/datum/computer_file/program/radar/fission360
filename = "fission360"
filedesc = "Fission360"
+ category = PROGRAM_CATEGORY_MISC
program_icon_state = "radarsyndicate"
extended_desc = "This program allows for tracking of nuclear authorization disks and warheads."
requires_ntnet = FALSE
diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm
index 8c41ea6c3803..7fe5a09ab2a8 100644
--- a/code/modules/modular_computers/file_system/programs/robocontrol.dm
+++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm
@@ -2,6 +2,7 @@
/datum/computer_file/program/robocontrol
filename = "botkeeper"
filedesc = "BotKeeper"
+ category = PROGRAM_CATEGORY_ROBO
program_icon_state = "robot"
extended_desc = "A remote controller used for giving basic commands to non-sentient robots."
transfer_access = null
diff --git a/code/modules/modular_computers/file_system/programs/robotact.dm b/code/modules/modular_computers/file_system/programs/robotact.dm
index b25332d02778..d4dce4220404 100644
--- a/code/modules/modular_computers/file_system/programs/robotact.dm
+++ b/code/modules/modular_computers/file_system/programs/robotact.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/robotact
filename = "robotact"
filedesc = "RoboTact"
+ category = PROGRAM_CATEGORY_ROBO
extended_desc = "A built-in app for cyborg self-management and diagnostics."
ui_header = "robotact.gif" //DEBUG -- new icon before PR
program_icon_state = "command"
@@ -22,7 +23,7 @@
/datum/computer_file/program/robotact/run_program(mob/living/user)
if(!istype(computer, /obj/item/modular_computer/tablet/integrated))
- to_chat(user, "A warning flashes across \the [computer]: Device Incompatible.")
+ to_chat(user, span_warning("A warning flashes across \the [computer]: Device Incompatible."))
return FALSE
. = ..()
if(.)
@@ -39,7 +40,7 @@
var/mob/living/silicon/robot/borgo = tablet.borgo
data["name"] = borgo.name
- data["designation"] = borgo.designation //Borgo module type
+ data["designation"] = borgo.designation //Borgo model type
data["masterAI"] = borgo.connected_ai //Master AI
var/charge = 0
@@ -62,7 +63,7 @@
data["cover"] = "[borgo.locked? "LOCKED":"UNLOCKED"]"
//Ability to move. FAULT if lockdown wire is cut, DISABLED if borg locked, ENABLED otherwise
data["locomotion"] = "[borgo.wires.is_cut(WIRE_LOCKDOWN)?"FAULT":"[borgo.locked_down?"DISABLED":"ENABLED"]"]"
- //Module wire. FAULT if cut, NOMINAL otherwise
+ //Model wire. FAULT if cut, NOMINAL otherwise
data["wireModule"] = "[borgo.wires.is_cut(WIRE_RESET_MODULE)?"FAULT":"NOMINAL"]"
//DEBUG -- Camera(net) wire. FAULT if cut (or no cameranet camera), DISABLED if pulse-disabled, NOMINAL otherwise
data["wireCamera"] = "[!borgo.builtInCamera || borgo.wires.is_cut(WIRE_CAMERA)?"FAULT":"[borgo.builtInCamera.can_use()?"NOMINAL":"DISABLED"]"]"
@@ -110,7 +111,7 @@
if("alertPower")
if(borgo.stat == CONSCIOUS)
if(!borgo.cell || !borgo.cell.charge)
- borgo.visible_message("The power warning light on [borgo] flashes urgently.", \
+ borgo.visible_message(span_notice("The power warning light on [span_name("[borgo]")] flashes urgently."), \
"You announce you are operating in low power mode.")
playsound(borgo, 'sound/machines/buzz-two.ogg', 50, FALSE)
diff --git a/code/modules/modular_computers/file_system/programs/secureye.dm b/code/modules/modular_computers/file_system/programs/secureye.dm
index 92275b1e8b61..d2d7590cb18a 100644
--- a/code/modules/modular_computers/file_system/programs/secureye.dm
+++ b/code/modules/modular_computers/file_system/programs/secureye.dm
@@ -3,6 +3,7 @@
/datum/computer_file/program/secureye
filename = "secureye"
filedesc = "SecurEye"
+ category = PROGRAM_CATEGORY_MISC
ui_header = "borg_mon.gif"
program_icon_state = "generic"
extended_desc = "This program allows access to standard security camera networks."
diff --git a/code/modules/modular_computers/file_system/programs/signaler.dm b/code/modules/modular_computers/file_system/programs/signaler.dm
new file mode 100644
index 000000000000..b7bbcacaa02d
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/signaler.dm
@@ -0,0 +1,83 @@
+/datum/computer_file/program/signaler
+ filename = "signaler"
+ filedesc = "SignalCommander"
+ category = PROGRAM_CATEGORY_MISC
+ program_icon_state = "signal"
+ extended_desc = "A small built-in frequency app that sends out signaller signals with the appropriate hardware."
+ size = 2
+ tgui_id = "NtosSignaler"
+ program_icon = "satellite-dish"
+ usage_flags = PROGRAM_TABLET | PROGRAM_LAPTOP
+ ///What is the saved signal frequency?
+ var/signal_frequency = FREQ_SIGNALER
+ /// What is the saved signal code?
+ var/signal_code = DEFAULT_SIGNALER_CODE
+ /// Radio connection datum used by signalers.
+ var/datum/radio_frequency/radio_connection
+
+/datum/computer_file/program/signaler/run_program(mob/living/user)
+ . = ..()
+ if (!.)
+ return
+ if(!computer?.get_modular_computer_part(MC_SIGNALER)) //Giving a clue to users why the program is spitting out zeros.
+ to_chat(user, span_warning("\The [computer] flashes an error: \"hardware\\signal_hardware\\startup.bin -- file not found\"."))
+
+
+/datum/computer_file/program/signaler/ui_data(mob/user)
+ var/list/data = get_header_data()
+ var/obj/item/computer_hardware/radio_card/sensor = computer?.get_modular_computer_part(MC_SIGNALER)
+ if(sensor?.check_functionality())
+ data["frequency"] = signal_frequency
+ data["code"] = signal_code
+ data["minFrequency"] = MIN_FREE_FREQ
+ data["maxFrequency"] = MAX_FREE_FREQ
+ return data
+
+/datum/computer_file/program/signaler/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+ var/obj/item/computer_hardware/radio_card/sensor = computer?.get_modular_computer_part(MC_SIGNALER)
+ if(!(sensor?.check_functionality()))
+ playsound(src, 'sound/machines/scanbuzz.ogg', 100, FALSE)
+ return
+ switch(action)
+ if("signal")
+ INVOKE_ASYNC(src, .proc/signal)
+ . = TRUE
+ if("freq")
+ signal_frequency = unformat_frequency(params["freq"])
+ signal_frequency = sanitize_frequency(signal_frequency, TRUE)
+ set_frequency(signal_frequency)
+ . = TRUE
+ if("code")
+ signal_code = text2num(params["code"])
+ signal_code = round(signal_code)
+ . = TRUE
+ if("reset")
+ if(params["reset"] == "freq")
+ signal_frequency = initial(signal_frequency)
+ else
+ signal_code = initial(signal_code)
+ . = TRUE
+
+/datum/computer_file/program/signaler/proc/signal()
+ if(!radio_connection)
+ return
+
+ var/time = time2text(world.realtime,"hh:mm:ss")
+ var/turf/T = get_turf(src)
+
+ var/logging_data
+ if(usr)
+ logging_data = "[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(signal_frequency)]/[signal_code]"
+ GLOB.lastsignalers.Add(logging_data)
+
+ var/datum/signal/signal = new(list("code" = signal_code), logging_data = logging_data)
+ radio_connection.post_signal(src, signal)
+
+/datum/computer_file/program/signaler/proc/set_frequency(new_frequency)
+ SSradio.remove_object(src, signal_frequency)
+ signal_frequency = new_frequency
+ radio_connection = SSradio.add_object(src, signal_frequency, RADIO_SIGNALER)
+ 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 6c9ce59c72f5..9038c71a8877 100644
--- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/supermatter_monitor
filename = "ntcims"
filedesc = "NT CIMS"
+ category = PROGRAM_CATEGORY_ENGI
ui_header = "smmon_0.gif"
program_icon_state = "smmon_0"
extended_desc = "Crystal Integrity Monitoring System, connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines."
@@ -12,7 +13,7 @@
alert_able = TRUE
var/last_status = SUPERMATTER_INACTIVE
var/list/supermatters
- var/obj/machinery/power/supermatter_crystal/active // Currently selected supermatter crystal.
+ var/obj/machinery/power/supermatter_crystal/active // Currently selected supermatter crystal.
/datum/computer_file/program/supermatter_monitor/Destroy()
clear_signals()
@@ -27,7 +28,7 @@
ui_header = "smmon_[last_status].gif"
program_icon_state = "smmon_[last_status]"
if(istype(computer))
- computer.update_icon()
+ computer.update_appearance()
/datum/computer_file/program/supermatter_monitor/run_program(mob/living/user)
. = ..(user)
@@ -36,11 +37,15 @@
refresh()
/datum/computer_file/program/supermatter_monitor/kill_program(forced = FALSE)
+ for(var/supermatter in supermatters)
+ clear_supermatter(supermatter)
supermatters = null
..()
// Refreshes list of active supermatter crystals
/datum/computer_file/program/supermatter_monitor/proc/refresh()
+ for(var/supermatter in supermatters)
+ clear_supermatter(supermatter)
supermatters = list()
var/turf/T = get_turf(ui_host())
if(!T)
@@ -50,9 +55,7 @@
if (!isturf(S.loc) || !(is_station_level(S.z) || is_mining_level(S.z) || S.z == T.z))
continue
supermatters.Add(S)
-
- if(!(active in supermatters))
- active = null
+ RegisterSignal(S, COMSIG_PARENT_QDELETING, .proc/react_to_del)
/datum/computer_file/program/supermatter_monitor/proc/get_status()
. = SUPERMATTER_INACTIVE
@@ -67,9 +70,9 @@
* the signal and exit.
*/
/datum/computer_file/program/supermatter_monitor/proc/set_signals()
- // if(active)
- // RegisterSignal(active, COMSIG_SUPERMATTER_DELAM_ALARM, .proc/send_alert, override = TRUE)
- // RegisterSignal(active, COMSIG_SUPERMATTER_DELAM_START_ALARM, .proc/send_start_alert, override = TRUE)
+ if(active)
+ RegisterSignal(active, COMSIG_SUPERMATTER_DELAM_ALARM, .proc/send_alert, override = TRUE)
+ RegisterSignal(active, COMSIG_SUPERMATTER_DELAM_START_ALARM, .proc/send_start_alert, override = TRUE)
/**
* Removes the signal listener for Supermatter delaminations from the selected supermatter.
@@ -77,9 +80,9 @@
* Pretty much does what it says.
*/
/datum/computer_file/program/supermatter_monitor/proc/clear_signals()
- // if(active)
- // UnregisterSignal(active, COMSIG_SUPERMATTER_DELAM_ALARM)
- // UnregisterSignal(active, COMSIG_SUPERMATTER_DELAM_START_ALARM)
+ if(active)
+ UnregisterSignal(active, COMSIG_SUPERMATTER_DELAM_ALARM)
+ UnregisterSignal(active, COMSIG_SUPERMATTER_DELAM_START_ALARM)
/**
* Sends an SM delam alert to the computer.
@@ -90,6 +93,7 @@
* the supermatter probably don't need constant beeping to distract them.
*/
/datum/computer_file/program/supermatter_monitor/proc/send_alert()
+ SIGNAL_HANDLER
if(!computer.get_ntnet_status())
return
if(computer.active_program != src)
@@ -106,12 +110,13 @@
* minimized or closed to avoid double-notifications.
*/
/datum/computer_file/program/supermatter_monitor/proc/send_start_alert()
+ SIGNAL_HANDLER
if(!computer.get_ntnet_status())
return
if(computer.active_program == src)
computer.alert_call(src, "Crystal delamination in progress!")
-/datum/computer_file/program/supermatter_monitor/ui_data()
+/datum/computer_file/program/supermatter_monitor/ui_data(mob/user)
var/list/data = get_header_data()
if(istype(active))
@@ -125,30 +130,9 @@
active = null
return
- data["active"] = TRUE
- data["SM_integrity"] = active.get_integrity()
- data["SM_power"] = active.power
- data["SM_ambienttemp"] = air.return_temperature()
- data["SM_ambientpressure"] = air.return_pressure()
- //data["SM_EPR"] = round((air.total_moles / air.group_multiplier) / 23.1, 0.01)
- var/list/gasdata = list()
-
-
- if(air.total_moles())
- for(var/gasid in air.get_gases())
- var/amount = air.get_moles(gasid)
- if(amount)
- gasdata.Add(list(list(
- "name"= GLOB.gas_data.names[gasid],
- "amount" = round(100*amount/air.total_moles(),0.01))))
-
- else
- for(var/gasid in air.get_gases())
- gasdata.Add(list(list(
- "name"= GLOB.gas_data.names[gasid],
- "amount" = 0)))
-
- data["gases"] = gasdata
+ data += active.ui_data()
+ data["singlecrystal"] = FALSE
+
else
var/list/SMS = list()
for(var/obj/machinery/power/supermatter_crystal/S in supermatters)
@@ -185,3 +169,13 @@
active = S
set_signals()
return TRUE
+
+/datum/computer_file/program/supermatter_monitor/proc/react_to_del(datum/source)
+ SIGNAL_HANDLER
+ clear_supermatter(source)
+
+/datum/computer_file/program/supermatter_monitor/proc/clear_supermatter(matter)
+ supermatters -= matter
+ if(matter == active)
+ active = null
+ UnregisterSignal(matter, COMSIG_PARENT_QDELETING)
diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm
index 0ccb9f6b967f..b1a44c4cc55d 100644
--- a/code/modules/modular_computers/hardware/_hardware.dm
+++ b/code/modules/modular_computers/hardware/_hardware.dm
@@ -4,22 +4,34 @@
icon = 'icons/obj/module.dmi'
icon_state = "std_mod"
- w_class = WEIGHT_CLASS_TINY // w_class limits which devices can contain this component.
+ w_class = WEIGHT_CLASS_TINY // w_class limits which devices can contain this component.
// 1: PDAs/Tablets, 2: Laptops, 3-4: Consoles only
var/obj/item/modular_computer/holder = null
// Computer that holds this hardware, if any.
- var/power_usage = 0 // If the hardware uses extra power, change this.
- var/enabled = TRUE // If the hardware is turned off set this to 0.
- var/critical = FALSE // Prevent disabling for important component, like the CPU.
- var/can_install = TRUE // Prevents direct installation of removable media.
- var/expansion_hw = FALSE // Hardware that fits into expansion bays.
- var/removable = TRUE // Whether the hardware is removable or not.
- var/damage = 0 // Current damage level
- var/max_damage = 100 // Maximal damage level.
- var/damage_malfunction = 20 // "Malfunction" threshold. When damage exceeds this value the hardware piece will semi-randomly fail and do !!FUN!! things
- var/damage_failure = 50 // "Failure" threshold. When damage exceeds this value the hardware piece will not work at all.
- var/malfunction_probability = 10// Chance of malfunction when the component is damaged
+ // If the hardware uses extra power, change this.
+ var/power_usage = 0
+ // If the hardware is turned off set this to 0.
+ var/enabled = TRUE
+ // Prevent disabling for important component, like the CPU.
+ var/critical = FALSE
+ // Prevents direct installation of removable media.
+ var/can_install = TRUE
+ // Hardware that fits into expansion bays.
+ var/expansion_hw = FALSE
+ // Whether the hardware is removable or not.
+ var/removable = TRUE
+ // Current damage level
+ var/damage = 0
+// Maximal damage level.
+ var/max_damage = 100
+ // "Malfunction" threshold. When damage exceeds this value the hardware piece will semi-randomly fail and do !!FUN!! things
+ var/damage_malfunction = 20
+ // "Failure" threshold. When damage exceeds this value the hardware piece will not work at all.
+ var/damage_failure = 50
+ // Chance of malfunction when the component is damaged
+ 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
/obj/item/computer_hardware/New(obj/L)
@@ -38,10 +50,10 @@
if(istype(I, /obj/item/stack/cable_coil))
var/obj/item/stack/S = I
if(obj_integrity == max_integrity)
- to_chat(user, "\The [src] doesn't seem to require repairs.")
+ to_chat(user, span_warning("\The [src] doesn't seem to require repairs."))
return 1
if(S.use(1))
- to_chat(user, "You patch up \the [src] with a bit of \the [I].")
+ to_chat(user, span_notice("You patch up \the [src] with a bit of \the [I]."))
obj_integrity = min(obj_integrity + 10, max_integrity)
return 1
@@ -78,11 +90,11 @@
/obj/item/computer_hardware/examine(mob/user)
. = ..()
if(damage > damage_failure)
- . += "It seems to be severely damaged!"
+ . += span_danger("It seems to be severely damaged!")
else if(damage > damage_malfunction)
- . += "It seems to be damaged!"
+ . += span_warning("It seems to be damaged!")
else if(damage)
- . += "It seems to be slightly damaged."
+ . += span_notice("It seems to be slightly damaged.")
// Component-side compatibility check.
/obj/item/computer_hardware/proc/can_install(obj/item/modular_computer/M, mob/living/user = null)
@@ -93,8 +105,9 @@
return
// Called when component is removed from PC.
-/obj/item/computer_hardware/proc/on_remove(obj/item/modular_computer/M, mob/living/user = null)
- try_eject(forced = TRUE)
+/obj/item/computer_hardware/proc/on_remove(obj/item/modular_computer/M, mob/living/user)
+ if(M.physical || !QDELETED(M))
+ try_eject(forced = TRUE)
// Called when someone tries to insert something in it - paper in printer, card in card reader, etc.
/obj/item/computer_hardware/proc/try_insert(obj/item/I, mob/living/user = null)
diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm
index 5d42747308e7..8740b59b3523 100644
--- a/code/modules/modular_computers/hardware/ai_slot.dm
+++ b/code/modules/modular_computers/hardware/ai_slot.dm
@@ -7,13 +7,14 @@
device_type = MC_AI
expansion_hw = TRUE
- var/obj/item/aicard/stored_card = null
+ var/obj/item/aicard/stored_card
var/locked = FALSE
-/obj/item/computer_hardware/ai_slot/handle_atom_del(atom/A)
- if(A == stored_card)
- try_eject(forced = TRUE)
- . = ..()
+///What happens when the intellicard is removed (or deleted) from the module, through try_eject() or not.
+/obj/item/computer_hardware/ai_slot/Exited(atom/movable/gone, direction)
+ if(stored_card == gone)
+ stored_card = null
+ return ..()
/obj/item/computer_hardware/ai_slot/examine(mob/user)
. = ..()
@@ -28,34 +29,33 @@
return FALSE
if(stored_card)
- to_chat(user, "You try to insert \the [I] into \the [src], but the slot is occupied.")
+ to_chat(user, span_warning("You try to insert \the [I] into \the [src], but the slot is occupied."))
return FALSE
if(user && !user.transferItemToLoc(I, src))
return FALSE
stored_card = I
- to_chat(user, "You insert \the [I] into \the [src].")
+ to_chat(user, span_notice("You insert \the [I] into \the [src]."))
return TRUE
/obj/item/computer_hardware/ai_slot/try_eject(mob/living/user = null, forced = FALSE)
if(!stored_card)
- to_chat(user, "There is no card in \the [src].")
+ to_chat(user, span_warning("There is no card in \the [src]."))
return FALSE
if(locked && !forced)
- to_chat(user, "Safeties prevent you from removing the card until reconstruction is complete...")
+ to_chat(user, span_warning("Safeties prevent you from removing the card until reconstruction is complete..."))
return FALSE
if(stored_card)
- to_chat(user, "You remove [stored_card] from [src].")
+ to_chat(user, span_notice("You remove [stored_card] from [src]."))
locked = FALSE
- if(user)
+ if(Adjacent(user))
user.put_in_hands(stored_card)
else
stored_card.forceMove(drop_location())
- stored_card = null
return TRUE
return FALSE
@@ -64,6 +64,6 @@
if(..())
return
if(I.tool_behaviour == TOOL_SCREWDRIVER)
- to_chat(user, "You press down on the manual eject button with \the [I].")
+ to_chat(user, span_notice("You press down on the manual eject button with \the [I]."))
try_eject(user, TRUE)
return
diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm
index 0668248315b1..27d3546ca24f 100644
--- a/code/modules/modular_computers/hardware/battery_module.dm
+++ b/code/modules/modular_computers/hardware/battery_module.dm
@@ -4,25 +4,28 @@
icon_state = "cell_con"
critical = 1
malfunction_probability = 1
- var/obj/item/stock_parts/cell/battery = null
+ var/obj/item/stock_parts/cell/battery
device_type = MC_CELL
/obj/item/computer_hardware/battery/get_cell()
return battery
-/obj/item/computer_hardware/battery/New(loc, battery_type = null)
+/obj/item/computer_hardware/battery/Initialize(mapload, battery_type)
+ . = ..()
if(battery_type)
battery = new battery_type(src)
- ..()
/obj/item/computer_hardware/battery/Destroy()
- . = ..()
- QDEL_NULL(battery)
+ battery = null
+ return ..()
-/obj/item/computer_hardware/battery/handle_atom_del(atom/A)
- if(A == battery)
- try_eject(forced = TRUE)
- . = ..()
+///What happens when the battery is removed (or deleted) from the module, through try_eject() or not.
+/obj/item/computer_hardware/battery/Exited(atom/movable/gone, direction)
+ if(battery == gone)
+ battery = null
+ if(holder?.enabled && !holder.use_power())
+ holder.shutdown_computer()
+ return ..()
/obj/item/computer_hardware/battery/try_insert(obj/item/I, mob/living/user = null)
if(!holder)
@@ -32,46 +35,33 @@
return FALSE
if(battery)
- to_chat(user, "You try to connect \the [I] to \the [src], but its connectors are occupied.")
+ to_chat(user, span_warning("You try to connect \the [I] to \the [src], but its connectors are occupied."))
return FALSE
if(I.w_class > holder.max_hardware_size)
- to_chat(user, "This power cell is too large for \the [holder]!")
+ to_chat(user, span_warning("This power cell is too large for \the [holder]!"))
return FALSE
if(user && !user.transferItemToLoc(I, src))
return FALSE
battery = I
- to_chat(user, "You connect \the [I] to \the [src].")
+ to_chat(user, span_notice("You connect \the [I] to \the [src]."))
return TRUE
-
-/obj/item/computer_hardware/battery/try_eject(mob/living/user = null, forced = FALSE)
+/obj/item/computer_hardware/battery/try_eject(mob/living/user, forced = FALSE)
if(!battery)
- to_chat(user, "There is no power cell connected to \the [src].")
+ to_chat(user, span_warning("There is no power cell connected to \the [src]."))
return FALSE
else
if(user)
user.put_in_hands(battery)
+ to_chat(user, span_notice("You detach \the [battery] from \the [src]."))
else
battery.forceMove(drop_location())
- to_chat(user, "You detach \the [battery] from \the [src].")
- battery = null
-
- if(holder)
- if(holder.enabled && !holder.use_power())
- holder.shutdown_computer()
-
return TRUE
-
-
-
-
-
-
/obj/item/stock_parts/cell/computer
name = "standard battery"
desc = "A standard power cell, commonly seen in high-end portable microcomputers or low-end laptops."
@@ -80,7 +70,6 @@
w_class = WEIGHT_CLASS_TINY
maxcharge = 750
-
/obj/item/stock_parts/cell/computer/advanced
name = "advanced battery"
desc = "An advanced power cell, often used in most laptops. It is too large to be fitted into smaller devices."
diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm
index 9139eee0b03f..13f1b3bbc9b3 100644
--- a/code/modules/modular_computers/hardware/card_slot.dm
+++ b/code/modules/modular_computers/hardware/card_slot.dm
@@ -1,17 +1,31 @@
/obj/item/computer_hardware/card_slot
- name = "primary RFID card module" // \improper breaks the find_hardware_by_name proc
+ name = "primary RFID card module" // \improper breaks the find_hardware_by_name proc
desc = "A module allowing this computer to read or write data on ID cards. Necessary for some programs to run properly."
power_usage = 10 //W
icon_state = "card_mini"
w_class = WEIGHT_CLASS_TINY
device_type = MC_CARD
- var/obj/item/card/id/stored_card = null
-
-/obj/item/computer_hardware/card_slot/handle_atom_del(atom/A)
- if(A == stored_card)
- try_eject(null, TRUE)
- . = ..()
+ var/obj/item/card/id/stored_card
+
+///What happens when the ID card is removed (or deleted) from the module, through try_eject() or not.
+/obj/item/computer_hardware/card_slot/Exited(atom/movable/gone, direction)
+ if(stored_card == gone)
+ stored_card = null
+ if(holder)
+ if(holder.active_program)
+ holder.active_program.event_idremoved(0)
+ for(var/p in holder.idle_threads)
+ var/datum/computer_file/program/computer_program = p
+ computer_program.event_idremoved(1)
+
+ holder.update_slot_icon()
+
+ if(ishuman(holder.loc))
+ var/mob/living/carbon/human/human_wearer = holder.loc
+ if(human_wearer.wear_id == holder)
+ human_wearer.sec_hud_set_ID()
+ return ..()
/obj/item/computer_hardware/card_slot/Destroy()
try_eject(forced = TRUE)
@@ -47,6 +61,11 @@
if(stored_card)
return FALSE
+
+ // item instead of player is checked so telekinesis will still work if the item itself is close
+ if(!in_range(src, I))
+ return FALSE
+
if(user)
if(!user.transferItemToLoc(I, src))
return FALSE
@@ -54,38 +73,32 @@
I.forceMove(src)
stored_card = I
- to_chat(user, "You insert \the [I] into \the [expansion_hw ? "secondary":"primary"] [src].")
+ to_chat(user, span_notice("You insert \the [I] into \the [expansion_hw ? "secondary":"primary"] [src]."))
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- H.sec_hud_set_ID()
+
+ var/holder_loc = holder.loc
+ if(ishuman(holder_loc))
+ var/mob/living/carbon/human/human_wearer = holder_loc
+ if(human_wearer.wear_id == holder)
+ human_wearer.sec_hud_set_ID()
+ holder.update_slot_icon()
return TRUE
/obj/item/computer_hardware/card_slot/try_eject(mob/living/user = null, forced = FALSE)
if(!stored_card)
- to_chat(user, "There are no cards in \the [src].")
+ to_chat(user, span_warning("There are no cards in \the [src]."))
return FALSE
- if(user)
+ if(user && !issilicon(user) && in_range(src, user))
user.put_in_hands(stored_card)
else
stored_card.forceMove(drop_location())
- stored_card = null
-
- if(holder)
- if(holder.active_program)
- holder.active_program.event_idremoved(0)
-
- for(var/p in holder.idle_threads)
- 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()
- to_chat(user, "You remove the card from \the [src].")
+
+ to_chat(user, span_notice("You remove the card from \the [src]."))
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+
return TRUE
/obj/item/computer_hardware/card_slot/attackby(obj/item/I, mob/living/user)
@@ -93,11 +106,11 @@
return
if(I.tool_behaviour == TOOL_SCREWDRIVER)
if(stored_card)
- to_chat(user, "You press down on the manual eject button with \the [I].")
+ to_chat(user, span_notice("You press down on the manual eject button with \the [I]."))
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, span_notice("You adjust the connecter 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 8debb00c19d8..29614cc7b096 100644
--- a/code/modules/modular_computers/hardware/hard_drive.dm
+++ b/code/modules/modular_computers/hardware/hard_drive.dm
@@ -8,19 +8,19 @@
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!
+ var/list/stored_files = list() // List of stored files on this drive. DO NOT MODIFY DIRECTLY!
/obj/item/computer_hardware/hard_drive/on_remove(obj/item/modular_computer/MC, mob/user)
MC.shutdown_computer()
/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/filemanager(src)) // File manager, allows text editor functions and basic file manipulation.
+ 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/filemanager(src)) // File manager, allows text editor functions and basic file manipulation.
/obj/item/computer_hardware/hard_drive/examine(user)
. = ..()
- . += "It has [max_capacity] GQ of storage capacity."
+ . += span_notice("It has [max_capacity] GQ of storage capacity.")
/obj/item/computer_hardware/hard_drive/diagnostics(mob/user)
..()
@@ -117,7 +117,7 @@
return null
/obj/item/computer_hardware/hard_drive/Destroy()
- stored_files = null
+ QDEL_LIST(stored_files)
return ..()
/obj/item/computer_hardware/hard_drive/Initialize()
@@ -129,7 +129,7 @@
name = "advanced hard disk drive"
desc = "A hybrid HDD, for use in higher grade computers where balance between power efficiency and capacity is desired."
max_capacity = 256
- power_usage = 50 // Hybrid, medium capacity and medium power storage
+ power_usage = 50 // Hybrid, medium capacity and medium power storage
icon_state = "harddisk_mini"
w_class = WEIGHT_CLASS_SMALL
@@ -137,7 +137,7 @@
name = "super hard disk drive"
desc = "A high capacity HDD, for use in cluster storage solutions where capacity is more important than power efficiency."
max_capacity = 512
- power_usage = 100 // High-capacity but uses lots of power, shortening battery life. Best used with APC link.
+ power_usage = 100 // High-capacity but uses lots of power, shortening battery life. Best used with APC link.
icon_state = "harddisk_mini"
w_class = WEIGHT_CLASS_SMALL
@@ -161,8 +161,8 @@
// 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/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/robotact(src))
diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm
index 625ead6ed7eb..074e158dbfa3 100644
--- a/code/modules/modular_computers/hardware/network_card.dm
+++ b/code/modules/modular_computers/hardware/network_card.dm
@@ -3,8 +3,9 @@
desc = "A basic wireless network card for usage with standard NTNet frequencies."
power_usage = 50
icon_state = "radio_mini"
- var/identification_id = null // Identification ID. Technically MAC address of this device. Can't be changed by user.
- var/identification_string = "" // Identification string, technically nickname seen in the network. Can be set by user.
+ // network_id = NETWORK_CARDS // Network we are on
+ var/hardware_id = null // Identification ID. Technically MAC address of this device. Can't be changed by user.
+ var/identification_string = "" // Identification string, technically nickname seen in the network. Can be set by user.
var/long_range = 0
var/ethernet = 0 // Hard-wired, therefore always on, ignores NTNet wireless checks.
malfunction_probability = 1
@@ -13,7 +14,7 @@
/obj/item/computer_hardware/network_card/diagnostics(mob/user)
..()
- to_chat(user, "NIX Unique ID: [identification_id]")
+ to_chat(user, "NIX Unique ID: [hardware_id]")
to_chat(user, "NIX User Tag: [identification_string]")
to_chat(user, "Supported protocols:")
to_chat(user, "511.m SFS (Subspace) - Standard Frequency Spread")
@@ -24,11 +25,11 @@
/obj/item/computer_hardware/network_card/New(l)
..()
- identification_id = ntnet_card_uid++
+ hardware_id = ntnet_card_uid++
// Returns a string identifier of this network card
/obj/item/computer_hardware/network_card/proc/get_network_tag()
- return "[identification_string] (NID [identification_id])"
+ return "[identification_string] (NID [hardware_id])"
// 0 - No signal, 1 - Low signal, 2 - High signal. 3 - Wired Connection
/obj/item/computer_hardware/network_card/proc/get_signal(specific_action = 0)
diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm
index 3bd5946435b2..99756e3dd8d2 100644
--- a/code/modules/modular_computers/hardware/printer.dm
+++ b/code/modules/modular_computers/hardware/printer.dm
@@ -11,11 +11,11 @@
/obj/item/computer_hardware/printer/diagnostics(mob/living/user)
..()
- to_chat(user, "Paper level: [stored_paper]/[max_paper].")
+ to_chat(user, span_notice("Paper level: [stored_paper]/[max_paper]."))
/obj/item/computer_hardware/printer/examine(mob/user)
. = ..()
- . += "Paper level: [stored_paper]/[max_paper]."
+ . += span_notice("Paper level: [stored_paper]/[max_paper].")
/obj/item/computer_hardware/printer/proc/print_text(text_to_print, paper_title = "")
@@ -33,7 +33,7 @@
P.info = text_to_print
if(paper_title)
P.name = paper_title
- P.update_icon()
+ P.update_appearance()
stored_paper--
P = null
return TRUE
@@ -41,12 +41,12 @@
/obj/item/computer_hardware/printer/try_insert(obj/item/I, mob/living/user = null)
if(istype(I, /obj/item/paper))
if(stored_paper >= max_paper)
- to_chat(user, "You try to add \the [I] into [src], but its paper bin is full!")
+ to_chat(user, span_warning("You try to add \the [I] into [src], but its paper bin is full!"))
return FALSE
if(user && !user.temporarilyRemoveItemFromInventory(I))
return FALSE
- to_chat(user, "You insert \the [I] into [src]'s paper recycler.")
+ to_chat(user, span_notice("You insert \the [I] into [src]'s paper recycler."))
qdel(I)
stored_paper++
return TRUE
diff --git a/code/modules/modular_computers/hardware/recharger.dm b/code/modules/modular_computers/hardware/recharger.dm
index ecfbf4c6b242..e8c479db479c 100644
--- a/code/modules/modular_computers/hardware/recharger.dm
+++ b/code/modules/modular_computers/hardware/recharger.dm
@@ -55,7 +55,7 @@
/obj/item/computer_hardware/recharger/wired/can_install(obj/item/modular_computer/M, mob/living/user = null)
if(ismachinery(M.physical) && M.physical.anchored)
return ..()
- to_chat(user, "\The [src] is incompatible with portable computers!")
+ to_chat(user, span_warning("\The [src] is incompatible with portable computers!"))
return FALSE
/obj/item/computer_hardware/recharger/wired/use_power(amount, charging=0)
@@ -96,3 +96,4 @@
/obj/item/computer_hardware/recharger/lambda/use_power(amount, charging=0)
return 1
+
diff --git a/code/modules/modular_computers/hardware/sensor_package.dm b/code/modules/modular_computers/hardware/sensor_package.dm
index c0363bc809bc..0579b752fb03 100644
--- a/code/modules/modular_computers/hardware/sensor_package.dm
+++ b/code/modules/modular_computers/hardware/sensor_package.dm
@@ -6,3 +6,12 @@
w_class = WEIGHT_CLASS_TINY
device_type = MC_SENSORS
expansion_hw = TRUE
+
+/obj/item/computer_hardware/radio_card
+ name = "integrated radio card"
+ desc = "An integrated signaling assembly for computers to send an outgoing frequency signal. Required by certain programs."
+ icon_state = "signal_card"
+ w_class = WEIGHT_CLASS_TINY
+ device_type = MC_SIGNALER
+ expansion_hw = TRUE
+ power_usage = 10
diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm
index 08119830881b..4a268c291182 100644
--- a/code/modules/modular_computers/laptop_vendor.dm
+++ b/code/modules/modular_computers/laptop_vendor.dm
@@ -13,19 +13,19 @@
var/obj/item/modular_computer/tablet/fabricated_tablet = null
// Utility vars
- var/state = 0 // 0: Select device type, 1: Select loadout, 2: Payment, 3: Thankyou screen
- var/devtype = 0 // 0: None(unselected), 1: Laptop, 2: Tablet
- var/total_price = 0 // Price of currently vended device.
+ var/state = 0 // 0: Select device type, 1: Select loadout, 2: Payment, 3: Thankyou screen
+ var/devtype = 0 // 0: None(unselected), 1: Laptop, 2: Tablet
+ var/total_price = 0 // Price of currently vended device.
var/credits = 0
// Device loadout
- var/dev_cpu = 1 // 1: Default, 2: Upgraded
- var/dev_battery = 1 // 1: Default, 2: Upgraded, 3: Advanced
- var/dev_disk = 1 // 1: Default, 2: Upgraded, 3: Advanced
- var/dev_netcard = 0 // 0: None, 1: Basic, 2: Long-Range
- var/dev_apc_recharger = 0 // 0: None, 1: Standard (LAPTOP ONLY)
- var/dev_printer = 0 // 0: None, 1: Standard
- var/dev_card = 0 // 0: None, 1: Standard
+ var/dev_cpu = 1 // 1: Default, 2: Upgraded
+ var/dev_battery = 1 // 1: Default, 2: Upgraded, 3: Advanced
+ var/dev_disk = 1 // 1: Default, 2: Upgraded, 3: Advanced
+ var/dev_netcard = 0 // 0: None, 1: Basic, 2: Long-Range
+ var/dev_apc_recharger = 0 // 0: None, 1: Standard (LAPTOP ONLY)
+ var/dev_printer = 0 // 0: None, 1: Standard
+ var/dev_card = 0 // 0: None, 1: Standard
// Removes all traces of old order and allows you to begin configuration from scratch.
/obj/machinery/lapvend/proc/reset_order()
@@ -48,7 +48,7 @@
// Recalculates the price and optionally even fabricates the device.
/obj/machinery/lapvend/proc/fabricate_and_recalc_price(fabricate = FALSE)
total_price = 0
- if(devtype == 1) // Laptop, generally cheaper to make it accessible for most station roles
+ if(devtype == 1) // Laptop, generally cheaper to make it accessible for most station roles
var/obj/item/computer_hardware/battery/battery_module = null
if(fabricate)
fabricated_laptop = new /obj/item/modular_computer/laptop/buildable(src)
@@ -111,7 +111,7 @@
fabricated_laptop.install_component(new /obj/item/computer_hardware/card_slot/secondary)
return total_price
- else if(devtype == 2) // Tablet, more expensive, not everyone could probably afford this.
+ else if(devtype == 2) // Tablet, more expensive, not everyone could probably afford this.
var/obj/item/computer_hardware/battery/battery_module = null
if(fabricate)
fabricated_tablet = new(src)
@@ -241,13 +241,13 @@
if(!user.temporarilyRemoveItemFromInventory(c))
return
credits += c.value
- visible_message("[user] inserts [c.value] cr into [src].")
+ visible_message(span_info("[span_name("[user]")] inserts [c.value] cr into [src]."))
qdel(c)
return
else if(istype(I, /obj/item/holochip))
var/obj/item/holochip/HC = I
credits += HC.credits
- visible_message("[user] inserts a [HC.credits] cr holocredit chip into [src].")
+ visible_message(span_info("[user] inserts a [HC.credits] cr holocredit chip into [src]."))
qdel(HC)
return
else if(istype(I, /obj/item/card/id))
diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm
index 13de7898c193..c957a927ec5f 100644
--- a/code/modules/paperwork/clipboard.dm
+++ b/code/modules/paperwork/clipboard.dm
@@ -1,127 +1,201 @@
+/**
+ * Clipboard
+ */
/obj/item/clipboard
name = "clipboard"
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "clipboard"
item_state = "clipboard"
- // inhand_icon_state = "clipboard"
- // worn_icon_state = "clipboard"
throwforce = 0
w_class = WEIGHT_CLASS_SMALL
throw_speed = 3
throw_range = 7
- var/obj/item/pen/haspen //The stored pen.
- var/obj/item/paper/toppaper //The topmost piece of paper.
slot_flags = ITEM_SLOT_BELT
resistance_flags = FLAMMABLE
+ /// The stored pen
+ var/obj/item/pen/pen
+ /// Is the pen integrated?
+ var/integrated_pen = FALSE
+ /**
+ * Weakref of the topmost piece of paper
+ *
+ * This is used for the paper displayed on the clipboard's icon
+ * and it is the one attacked, when attacking the clipboard.
+ * (As you can't organise contents directly in BYOND)
+ */
+ var/datum/weakref/toppaper_ref
/obj/item/clipboard/suicide_act(mob/living/carbon/user)
- user.visible_message("[user] begins putting [user.p_their()] head into the clip of \the [src]! It looks like [user.p_theyre()] trying to commit suicide!")
- return BRUTELOSS//the clipboard's clip is very strong. industrial duty. can kill a man easily.
+ user.visible_message(span_suicide("[user] begins putting [user.p_their()] head into the clip of \the [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
+ return BRUTELOSS //The clipboard's clip is very strong. Industrial duty. Can kill a man easily.
/obj/item/clipboard/Initialize()
- update_icon()
+ update_appearance()
. = ..()
/obj/item/clipboard/Destroy()
- QDEL_NULL(haspen)
- QDEL_NULL(toppaper) //let movable/Destroy handle the rest
+ QDEL_NULL(pen)
return ..()
+/obj/item/clipboard/examine()
+ . = ..()
+ if(!integrated_pen && pen)
+ . += span_notice("Alt-click to remove [pen].")
+ var/obj/item/paper/toppaper = toppaper_ref?.resolve()
+ if(toppaper)
+ . += span_notice("Right-click to remove [toppaper].")
+
+/// Take out the topmost paper
+/obj/item/clipboard/proc/remove_paper(obj/item/paper/paper, mob/user)
+ if(!istype(paper))
+ return
+ paper.forceMove(user.loc)
+ user.put_in_hands(paper)
+ to_chat(user, span_notice("You remove [paper] from [src]."))
+ var/obj/item/paper/toppaper = toppaper_ref?.resolve()
+ if(paper == toppaper)
+ UnregisterSignal(toppaper, COMSIG_ATOM_UPDATED_ICON)
+ toppaper_ref = null
+ var/obj/item/paper/newtop = locate(/obj/item/paper) in src
+ if(newtop && (newtop != paper))
+ toppaper_ref = WEAKREF(newtop)
+ else
+ toppaper_ref = null
+ update_icon()
+
+/obj/item/clipboard/proc/remove_pen(mob/user)
+ pen.forceMove(user.loc)
+ user.put_in_hands(pen)
+ to_chat(user, span_notice("You remove [pen] from [src]."))
+ pen = null
+ update_icon()
+
+/obj/item/clipboard/AltClick(mob/user)
+ ..()
+ if(pen)
+ if(integrated_pen)
+ to_chat(user, span_warning("You can't seem to find a way to remove [src]'s [pen]."))
+ else
+ remove_pen(user)
+
/obj/item/clipboard/update_overlays()
. = ..()
+ var/obj/item/paper/toppaper = toppaper_ref?.resolve()
if(toppaper)
. += toppaper.icon_state
. += toppaper.overlays
- if(haspen)
+ if(pen)
. += "clipboard_pen"
. += "clipboard_over"
-/obj/item/clipboard/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/paper))
- if(!user.transferItemToLoc(W, src))
+/obj/item/clipboard/attack_hand(mob/user, act_intent)
+ if(act_intent != INTENT_HELP)
+ var/obj/item/paper/toppaper = toppaper_ref?.resolve()
+ remove_paper(toppaper, user)
+ return TRUE
+ . = ..()
+
+/obj/item/clipboard/attackby(obj/item/weapon, mob/user, params)
+ var/obj/item/paper/toppaper = toppaper_ref?.resolve()
+ if(istype(weapon, /obj/item/paper))
+ //Add paper into the clipboard
+ if(!user.transferItemToLoc(weapon, src))
+ return
+ if(toppaper)
+ UnregisterSignal(toppaper, COMSIG_ATOM_UPDATED_ICON)
+ RegisterSignal(weapon, COMSIG_ATOM_UPDATED_ICON, .proc/on_top_paper_change)
+ toppaper_ref = WEAKREF(weapon)
+ to_chat(user, span_notice("You clip [weapon] onto [src]."))
+ else if(istype(weapon, /obj/item/pen) && !pen)
+ //Add a pen into the clipboard, attack (write) if there is already one
+ if(!usr.transferItemToLoc(weapon, src))
return
- toppaper = W
- to_chat(user, "You clip the paper onto \the [src].")
- update_icon()
+ pen = weapon
+ to_chat(usr, span_notice("You slot [weapon] into [src]."))
else if(toppaper)
toppaper.attackby(user.get_active_held_item(), user)
- update_icon()
-
+ update_appearance()
/obj/item/clipboard/attack_self(mob/user)
- var/dat = "Clipboard"
- if(haspen)
- dat += "Remove Pen
"
- else
- dat += "Add Pen
"
-
- //The topmost paper. You can't organise contents directly in byond, so this is what we're stuck with. -Pete
- if(toppaper)
- var/obj/item/paper/P = toppaper
- dat += "Write Remove - [P.name]
"
-
- for(P in src)
- if(P == toppaper)
- continue
- dat += "Write Remove Move to top - [P.name]
"
- user << browse(dat, "window=clipboard")
- onclose(user, "clipboard")
add_fingerprint(usr)
+ ui_interact(user)
+ return
+
+/obj/item/clipboard/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Clipboard")
+ ui.open()
+
+/obj/item/clipboard/ui_data(mob/user)
+ // prepare data for TGUI
+ var/list/data = list()
+ data["pen"] = "[pen]"
+ data["integrated_pen"] = integrated_pen
+
+ var/obj/item/paper/toppaper = toppaper_ref?.resolve()
+ data["top_paper"] = "[toppaper]"
+ data["top_paper_ref"] = "[REF(toppaper)]"
+
+ data["paper"] = list()
+ data["paper_ref"] = list()
+ for(var/obj/item/paper/paper in src)
+ if(paper == toppaper)
+ continue
+ data["paper"] += "[paper]"
+ data["paper_ref"] += "[REF(paper)]"
+
+ return data
+
+/obj/item/clipboard/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
-
-/obj/item/clipboard/Topic(href, href_list)
- ..()
- if(usr.stat != CONSCIOUS || usr.restrained()) //HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED))
+ if(usr.stat != CONSCIOUS || usr.restrained())
return
- if(usr.contents.Find(src))
-
- if(href_list["pen"])
- if(haspen)
- haspen.forceMove(usr.loc)
- usr.put_in_hands(haspen)
- haspen = null
-
- if(href_list["addpen"])
- if(!haspen)
- var/obj/item/held = usr.get_active_held_item()
- if(istype(held, /obj/item/pen))
- var/obj/item/pen/W = held
- if(!usr.transferItemToLoc(W, src))
- return
- haspen = W
- to_chat(usr, "You slot [W] into [src].")
-
- if(href_list["write"])
- var/obj/item/P = locate(href_list["write"]) in src
- if(istype(P))
- if(usr.get_active_held_item())
- P.attackby(usr.get_active_held_item(), usr)
-
- if(href_list["remove"])
- var/obj/item/P = locate(href_list["remove"]) in src
- if(istype(P))
- P.forceMove(usr.loc)
- usr.put_in_hands(P)
- if(P == toppaper)
- toppaper = null
- var/obj/item/paper/newtop = locate(/obj/item/paper) in src
- if(newtop && (newtop != P))
- toppaper = newtop
- else
- toppaper = null
-
- if(href_list["read"])
- var/obj/item/paper/P = locate(href_list["read"]) in src
- if(istype(P))
- usr.examinate(P)
-
- if(href_list["top"])
- var/obj/item/P = locate(href_list["top"]) in src
- if(istype(P))
- toppaper = P
- to_chat(usr, "You move [P.name] to the top.")
-
- //Update everything
- attack_self(usr)
- update_icon()
+ switch(action)
+ // Take the pen out
+ if("remove_pen")
+ if(pen)
+ if(!integrated_pen)
+ remove_pen(usr)
+ else
+ to_chat(usr, span_warning("You can't seem to find a way to remove [src]'s [pen]."))
+ . = TRUE
+ // Take paper out
+ if("remove_paper")
+ var/obj/item/paper/paper = locate(params["ref"]) in src
+ if(istype(paper))
+ remove_paper(paper, usr)
+ . = TRUE
+ // Look at (or edit) the paper
+ if("edit_paper")
+ var/obj/item/paper/paper = locate(params["ref"]) in src
+ if(istype(paper))
+ paper.ui_interact(usr)
+ update_icon()
+ . = TRUE
+ // Move paper to the top
+ if("move_top_paper")
+ var/obj/item/paper/paper = locate(params["ref"]) in src
+ if(istype(paper))
+ toppaper_ref = WEAKREF(paper)
+ to_chat(usr, span_notice("You move [paper] to the top."))
+ update_icon()
+ . = TRUE
+ // Rename the paper (it's a verb)
+ if("rename_paper")
+ var/obj/item/paper/paper = locate(params["ref"]) in src
+ if(istype(paper))
+ paper.rename()
+ update_icon()
+ . = TRUE
+
+/**
+ * This is a simple proc to handle calling update_icon() upon receiving the top paper's `COMSIG_ATOM_UPDATE_APPEARANCE`.
+ */
+/obj/item/clipboard/proc/on_top_paper_change()
+ SIGNAL_HANDLER
+ update_appearance()
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index b802cc7b884a..7f1d23e43cd7 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -628,6 +628,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
//Tells the engi team to get their butt in gear
if(damage > warning_point) // while the core is still damaged and it's still worth noting its status
+ if(damage_archived < warning_point) //If damage_archive is under the warning point, this is the very first cycle that we've reached said point.
+ SEND_SIGNAL(src, COMSIG_SUPERMATTER_DELAM_START_ALARM)
if((REALTIMEOFDAY - lastwarning) / 10 >= WARNING_DELAY)
alarm()
@@ -635,6 +637,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
if(damage > emergency_point)
// it's bad, LETS YELL
radio.talk_into(src, "[emergency_alert] Integrity: [get_integrity()]%", common_channel, list(SPAN_YELL))
+ SEND_SIGNAL(src, COMSIG_SUPERMATTER_DELAM_ALARM)
lastwarning = REALTIMEOFDAY
if(!has_reached_emergency)
investigate_log("has reached the emergency point for the first time.", INVESTIGATE_SUPERMATTER)
@@ -642,6 +645,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
has_reached_emergency = TRUE
else if(damage >= damage_archived) // The damage is still going up
radio.talk_into(src, "[warning_alert] Integrity: [get_integrity()]%", engineering_channel)
+ SEND_SIGNAL(src, COMSIG_SUPERMATTER_DELAM_ALARM)
lastwarning = REALTIMEOFDAY - (WARNING_DELAY * 5)
else // Phew, we're safe
diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm
index d00166fb0ccd..d288edbc2d08 100644
--- a/code/modules/wiremod/shell/brain_computer_interface.dm
+++ b/code/modules/wiremod/shell/brain_computer_interface.dm
@@ -372,11 +372,11 @@
/obj/machinery/bci_implanter/update_overlays()
var/list/overlays = ..()
- if ((machine_stat & MAINT) || panel_open)
+ if ((stat & MAINT) || panel_open)
overlays += "maint"
return overlays
- if (machine_stat & (NOPOWER|BROKEN))
+ if (stat & (NOPOWER|BROKEN))
return overlays
if (busy || locked)
@@ -448,9 +448,9 @@
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/machinery/bci_implanter/proc/start_process()
- if (machine_stat & (NOPOWER|BROKEN))
+ if (stat & (NOPOWER|BROKEN))
return
- if ((machine_stat & MAINT) || panel_open)
+ if ((stat & MAINT) || panel_open)
return
if (!occupant || busy)
return
diff --git a/html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js b/html/jquery/jquery-ui.custom-core-widgit-mouse-sortable.min.js
similarity index 100%
rename from html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js
rename to html/jquery/jquery-ui.custom-core-widgit-mouse-sortable.min.js
diff --git a/html/jquery.min.js b/html/jquery/jquery.min.js
similarity index 100%
rename from html/jquery.min.js
rename to html/jquery/jquery.min.js
diff --git a/icons/UI_Icons/adventure/default.png b/icons/UI_Icons/adventure/default.png
new file mode 100644
index 0000000000000000000000000000000000000000..953dd8f2b00f4cef0724786298e5213180f15fc5
GIT binary patch
literal 244
zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGkr_z-^myw5q}T#{LR^9Le+CBW|Gi2;)=Ezo
z#}JR>Z!c}+J**(mnyACIYlDMIn8(b%P$SO-6Su^cDIy$)7``h@Ms~^k+!|l_-EaB2
zIcmGy1+LpGEDV?2mL%14QkuuelQl9Y#O|cgw$qDt%#7GMA-(gIm=Ng@lms&PtssZb6ptN#LHE-_P`h$~vBc%SZ5IL}%sqqV;IY0pFV
qH7dI%&iI#ovG=!6UcuA#pINV$+xs}Im?jQ%G=rzBpUXO@geCyU7g%rr
literal 0
HcmV?d00001
diff --git a/icons/UI_Icons/adventure/grue.png b/icons/UI_Icons/adventure/grue.png
new file mode 100644
index 0000000000000000000000000000000000000000..fe60784b61026274696e864f3608e765f736995b
GIT binary patch
literal 167
zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGi5W=ldog_mkm3yR32_C||A9!6A#_196Oa?>
z>Eaj?aro_pg}et81X>dX?k;#M;B!$h>jhuVMa8J*<9DV=6!o<{|L<@6S$#S;PzOW9
z%D0myKb|)E^YqDCcHfU4c_XX+?5IlRq%Xqr*VyS#Q&N8mRCb-Qs)S3~>xxJWNQ$r7xK6GVELq?o>)t^aW@{NIN3gdd0Re0ckxv4!XVbNg=rlK_-YDkLNf0aYQ{!=8pDD$q&X9;(
zCcY&87!q6ES&3c|#-%v+CccI^B*JUrt)w59_9ilolE^w~W+Ihjuo732<@WoMcr$4;
z=}4{-=u86OIi}YIq9yraoK=u-#;gzzbho5mIxjbb09Q!7SFT9QX}<+B{ed78Jlq7r
zn0J6Pi5g3JWz0t+>Eml8*hyPY@{5g;CH!OJwaBJwPWPi*C&hYQj6JfaiT?p}b!O1Zg4TcSEq^%7=iHk1^4*;m
zA$=u%VGB5nTg({0y^Kxd)$nBX3RrYJq<1!lYx}-$G4aVc$`%#*mhTf?G}x*Nt{n|5GO<%a8koG4`T|7z9E5@y
wGG=R>ndYFtp0d{PjI6@*B#s5YQUw?pzG%qod=>Zo63{LNPgg&ebxsLQ0OSls`~Uy|
literal 0
HcmV?d00001
diff --git a/icons/UI_Icons/inventory/collar.png b/icons/UI_Icons/inventory/collar.png
new file mode 100644
index 0000000000000000000000000000000000000000..a7d1b5278497e3b32905747bed21972530393802
GIT binary patch
literal 215
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@o&cW^S0J6*Iz6FkLR#z0=}gz=
z0{KiOL4LsuCm!px?*j5FJY5_^BrYc_a22d&YGQR@OV=iKMB@Me
literal 0
HcmV?d00001
diff --git a/icons/UI_Icons/inventory/ears.png b/icons/UI_Icons/inventory/ears.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c3ba34929495e7089da07bb66835d052ea48cbc
GIT binary patch
literal 276
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv=>VS)S0J6*Iz6drQd~uEd}V)9
z(CD+e`6ZkLtW|3JlI{^6}X75^JJl$&Qx(Apw-_>_{PJMR{r
z!@o4`kFk}_e66c=;GE!(BJqV^T7U18@KRT0=FWL*bSGg7Pi(@@-^@=O)~BEQDf5rr
X&~PfF-l{~PQyDy6{an^LB{Ts53=wSA
literal 0
HcmV?d00001
diff --git a/icons/UI_Icons/inventory/glasses.png b/icons/UI_Icons/inventory/glasses.png
new file mode 100644
index 0000000000000000000000000000000000000000..3dff0cd5992d934251fda248f98285bc42f30107
GIT binary patch
literal 180
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv(Ey(iS0J6*Iz7I!FQ%+Js-!)#
zs5Pl+vf0*&8-Nn*B|(0{3@84Az~<7sM}b1_o-U3d8t0QG!W=w3Iejz^{%C4yI(Dqd
zVS!7;fg?v%8QL2djUyU#8yiJ67HMRhIlvI2bmYK+V+Rg&xi%bSP1&}&llgD}J41XP
W_ruAu=Y9i?WbkzLb6Mw<&;$Vg4M39s
literal 0
HcmV?d00001
diff --git a/icons/UI_Icons/inventory/gloves.png b/icons/UI_Icons/inventory/gloves.png
new file mode 100644
index 0000000000000000000000000000000000000000..87c829eb626ea814e79a05e2fa8c32e9ddf8abff
GIT binary patch
literal 249
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4S0J6*Iz6#*VnWq~_{#pJ
zM|L~_3bB?1`2{na`1|+ox(S9|K!GWqE{-7@=UdNTz7F_pBB`5=&wtqe7g10Ei$HGB^NU6{IAVWb^icUf5w5H{A=zDOs$mG3nt}oE3<9v
zXWf|bkNuEdn6Saj4UAuex@?6N?roKpnf+Fq$zSeZaoHaS$CUkTTu$A~*p7S%@=|tb
uXHbz9aCj|y=Jw7>zkXhfzAdO+!||Ajafu30~e@gHMWcxR*Yvl+oRl#cf>&U=<%bYA>`>ny);gNA!8P77wF
xm_Lx+^!BO
zX;*AnPeRqi8o!$iWS0J6#G%32YGqrVka?_Nw
z)|qkTJ-IzgBa7QI+U6v;Om#oQ>IYQBT@vIM%y8oGiN6rg+?rJn6t4GlaSYKo-+Mlg
zx50p?B~j;!lkOQX0x=GS6zvw5i<%V6@#Mb{N44Jam{$E(=C1{p3~V;F+Q>>QWxC%X
z5z*4|?SM*YgBer$1iohy!iWFz^E~aFy>&yZ!@gz8N6T5~-JAYc=wB7%y!i9(Q)3$@
lE#hALhG|Zg+U5gi>1OVdMY+
literal 0
HcmV?d00001
diff --git a/icons/UI_Icons/inventory/neck.png b/icons/UI_Icons/inventory/neck.png
new file mode 100644
index 0000000000000000000000000000000000000000..74cf1e33eafaece35d2279fa4c25fd8ebbd8702b
GIT binary patch
literal 229
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4S0J6yIw!ueKe1tAYRe24
zKl2$tA*PZbzhH(Fk9FF20eQ`yE{-7@=cit{$jfBN!+b%@=fN{Jk6^X~y21><&mTz-
zS|Vpvzi(CNoFcJ}_6lon@h{N3P$0^3;pMc355J#pWzT#cs8aiKO^q_R4{Ye*ptJuaY~R}pq0YZLl37h9`^1#UVI>P
ZU!|YWKl>0b37~TrJYD@<);T3K0RU)(RLlSX
literal 0
HcmV?d00001
diff --git a/icons/UI_Icons/inventory/pocket.png b/icons/UI_Icons/inventory/pocket.png
new file mode 100644
index 0000000000000000000000000000000000000000..2e08ed41b05a239304c2ceada8fb8ebc96742de8
GIT binary patch
literal 237
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv=>VS)*VNYOKxR_o#N_(vsjV}j
zirb@0I-*NEB8yscK0l2B$}yJ&`30X~_`B>_Z#$4%=jq}YqH%uh`Hfr+3IYueo4cMZ
zZhJOQVW+sku5f`Wrs&zXq^sWdO%Vv3@M3-+KfkEKD{g_AUrRn0NG@VwaHx(*ezWcL
zj7BxK6&|w`8eA{&O;GGGWZV>N+^@X(O@QK_REDQ3)~ES2Rd4=%Fu!5%x9MkQH0FJh
hUM1r4!ZVR8?}e3q@FU&h$v~Gdc)I$ztaD0e0sy&sTNMBR
literal 0
HcmV?d00001
diff --git a/icons/UI_Icons/inventory/shoes.png b/icons/UI_Icons/inventory/shoes.png
new file mode 100644
index 0000000000000000000000000000000000000000..b1b19c633972d74ee230b2e61925cc5eda7cd261
GIT binary patch
literal 196
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvi2$DvS0J6*Iz7I!FD$PzH+*BJGxxT-o4+~kGz%~Z-V81J;+4$7o-sFqTY!U&@qsyuQAogyi1u#}!fm>J
zn7>A?-@qt+-P2o^6_wY@j(|SF?e8+Xn{qcXA$kpWO%s+RNbS>gTe~
HDWM4f8_`O|
literal 0
HcmV?d00001
diff --git a/icons/UI_Icons/inventory/suit_storage.png b/icons/UI_Icons/inventory/suit_storage.png
new file mode 100644
index 0000000000000000000000000000000000000000..0dd12ed4f8f169448969e7dfa6dfa335ae05531b
GIT binary patch
literal 305
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
RA4fqTEj4}BVilW^{{x4e&vyU-
literal 0
HcmV?d00001
diff --git a/icons/UI_Icons/tgui/ntosradar_background.png b/icons/UI_Icons/tgui/ntosradar_background.png
index bac7647e3ad365cad61312ecfaa35ab8a767247e..94b9c8ea69f69a7637d17a6f4c820e17b5621339 100644
GIT binary patch
literal 7367
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
bUrC2Ge{a50%K8cXvxlXjs;$zfWF7f`3G|qB
literal 21490
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%0DqBR
zIe(^dBE(mdB(lV?KA*Xk>~r~lal-HyBt`R2e<%!me##jL
z4iEI#_h0v%ak@E+w|z1>R8h&|8*o=cq7aS#E=PSPAw4m_1U%Is6@Oz}9mIOB>q!H2
zP0zPsm&hxFccZLTx&-}9{ip!!cbp30!p2-i)^J)0pW_&>`^|5WXVHeq^70;|+&KxQ
z%KSX&KFDw?^}6Ad@5T~4!XKwxXQP=Fib^1sT6Dlgt@s!UK0cP3T@Q1ehxaz<7h#IL
zGyULJFei)B71zJFPYq9z15}Lcs|+j6Og!qJt%}@E9NdtW-Pqx7>sZ8ZWpn8JwpQsJ
zB795;fj(h5xtiX9wVf!(Mw4ML`PRS=mur3;Rq$)EuM~f9=UW(zqh5Mcn0#ECbUmtd
z^g~=-xgY1XkUykSuS8MDuODQ=guDs5s(9#)*Iy%Iz+tMlTfD*U!L)#+S#W`x!@w%|
zbHW6N*qLdN8k>avlf391oVM!%Rhf^`7UVlL2lhj~`5UKY!P_N_nE-mz^M2-FdSS45
znd;(uFZX(K=vAY+#QeOSh7e~}M#82Ni|T#mjD+?X{pqXErl6F8uanF4Lt_k;>q#g4
zO$WP{5_wzH(fAitR6A5BtSy#j_6^Veih$#00
z#G~bLb_Daj{AcAl<4~K{MXOB@>9tK)i|-VY^?6rO*Y?B~m(U8Hu^ED4Zb>A?uX0Gj
zTaDqFUMj^a5}g)%7nl`eR;x9F?4V#OP2YV;h^Vkt@9^?RcazkE(K0+qgalPT~kn?^1pKbI{fV%ALe+Q
zW;sa9ddg54S2}X({s9YMEIqeMkwSA%M418!_^EZ9
zJge~puLB8C4TPXmECV6BF=}9^rP-C)m|aFz$->{}=f!LBvd3Cjx?6V6D~Er3gQ<42
zt|wdV29!`h8#oFf=g6~pebX1sD>@K~lO)KRv{X5>g-tD|*3@B>96G@;AF+0g_t?kV
zQM8196nj_X1%DkU=h8B$$HB#hHH4kC=UKe05sh9s8ei!?)016P+-lBw%gRRfl{y}i
zWi9F#l`t3z|77nIsPpYJ?_MNQ)@@tCD0bt~7VDL?t}3D!c)&@F@n;zoL95=I%BYwk
z1wn)vj+1@wYS~v5;}#!9F-{1pUTv6TAXRt5@|9?xU9vyP33wb%`YZ*wLsdmgpQkw5
zha9OL{1C0*DSmS?GfUr(#Y9qHhVG1uC>1BTudg#g8WyL&%G(Pe#%jDMr16U>zL4ED
zVkCYuV)2u#o9OfF2+WTf>>=Uu@WMO;2ys&dbo=D*mC!+qgBFhQG3_Ysip)fVLmGc@
z>>_<)Bs>h-4+{KQu&LH0x{2Ro*`bJk4SyP5{qp8kwV2@$`EFj{;*h5i=r
z&RZvhG^{jL+lO6O0HR=`!s@Pi*$FY(?TC-1W|oNhV_1CzOz
z^7mL!9)L%LEHEXQBa9Y`a+`JpN=(`US&7MvdSw;R3Ook;oq;MJs71$pO0U~>pmkFX
zZ4I~kFsE(Hstgzku7gKkhKLv1fhU?;kBq8V)KGxw0
z&$9bIC-Ih$-w<9K%B;E^0@dnLrRkLU2F|Qx3XS~MVLF6x6&uK
zG6I6%`6(noq?3|ng|mD8a5sI26!S(@q?@XO`mrWu7XB1#srvwe+zYo>o|
z%B!sCgyj{_HQ}4ZNI7gBO=xLl%ZMi{w+PEZFwj)`YjQ+;K>L;U;P%jVOn>Ku3$-F1{1n3Le9W*(FE-
z1MCcAiE!Pn?X=Uo#SZ(V-v|%0;eq3s4Q6J9mUP}qbA@AHz6Q
TmIU7CTyfCAc>lA#Hdp=+FtC5w
diff --git a/icons/UI_Icons/tgui/ntosradar_pointer.png b/icons/UI_Icons/tgui/ntosradar_pointer.png
index e71823f391314f5d7356f1be6e9367735d61b526..efa2026ef7d7885cb61a4c5c6b232c5db48a6c30 100644
GIT binary patch
delta 269
zcmdnZJ%?$6L_G^L0|P_v_2l_Lia)?7#1%*@?Q@T~QO1*@^#A|=-T+$zpb%q8kY6x^
z!?PP{K+bMY7srr{dvDJkX>T!bK4YWzKKTfcU|iH
zu)eJH&ZS$D+f24D*IjC2s{C@pb|wF8<)W^YMix&y-qx?}2=1E^+*i>hxIF2IrhC$<
z!>L6o%Yyh%uh&hEvpT(I;ocO_om+dXbP6TYW!tW+xcm8?j6eG5X#Bs=MyK1VPOeJT
z?+eikotW*_w`Xa{zDM&ogP4sX(yrxgLZ8Bx@cpCJWn2E-sC!0f~9K
MviZ5Jb4q9e00?Vtg8%>k
literal 1467
zcmeAS@N?(olHy`uVBq!ia0vp^Mhpy$UpUx+toomIYk(AEage(c!@6@aFM%AEbVpxD
z28NCO+M1MG8<*vcxr_Bsf2G+x7Y9H-j0xP4HP#BT{1IMQFTevl<8K>q*_r?|Z_Y
zqhicq=X(aZm{l|?#ssNSy!jmwDjTtr_L}&t%Yz0&wMXAUu|pqWFq5g-D5VUDcypPZyNIRTH2SK7EFG(I=$}MJH=|A2wS6P&lEpz
zyVm|Lb>8yLCw!gjo;O@wn`|dzl%Kly=XCwy-2
zvq?H*sek@ownyTDS4C&mm%eV-diG~e#F0@*pAguw@#n>@XAQ&*N8v~cfp2r})iJf+
Xj9va#V#Qiu*~;MQ>gTe~DWM4f{aqyy
diff --git a/icons/UI_Icons/tgui/ntosradar_pointer_S.png b/icons/UI_Icons/tgui/ntosradar_pointer_S.png
index 51a0dd49d9688024572ab9dbe8449744d64f354b..7d394826d2a662e4ff41fe42407b5948cb1ff6e6 100644
GIT binary patch
delta 276
zcmdnMy_ji&L_HHT0|Nuo^lA1$iYLG)#1%;YGB7x#q*N&&@asaYC6Lcp666=m;PC85
z8jy3;)5S3);_%zE8+n@?B#wR*Ut?nEm-vuj+Pi|Y?8jzgWS(RY(Q))oeki7MP|oC=
z+@5A@@eKy=um7Dq-R8u0#onu?YlAN;zFOO}(RlY!tNK|tjbgvUxVhoog0wYz*kq4cDW9k*Jh76weU4SZiIvXXD_T1c;Libe7MrM1MG8<*vcxr_Bsf2G+x7W}13Oh=;UgT_)=xF%j(ZAt_gyc)-d6z!+)bCoT
z|LKv=j%~YNUtLpSJ=gC)>oFxFpleOqgo_tfUW^X>Wj-`ci)jQkuvLvVWX
zH>o2(cz7e;8n%5DEWT~EVfSu%h#XZAFSxsWm_{(
z^6-y|J8flCxIh2-x_5r!p0x9ftE-P2ta~=?%OTsd)z?3BJYHIFm{59J;@IY$ytY5$
zXMA4sT&BleYQrwGXLs$>w#pT#6rVf2dB5~RdD)v!XPk+DILq??Y&)GQUdvh6CoHbS
zLPh`i@$0K>ZMybuUQ79!f6`XpWH$etu=8)}ks0T|-cHZ4da{^t_1fb(Mk#rfwr|p(
zl~+C6J5TP@iH56U$@8Q(9qst|W=4D4ocLW&I}FcXwYD$*KEal4jhtlh=LtL4eqCBU
z^ZQGkqRSKZTPBpgp26RKX6+jO|F@sXSUuih^@M{tOuqNw1m!-7>&Ny;7yY|5(Y?p=
z`O};Kc{F(&ifm5bk2NvF~dvmY
diff --git a/tgstation.dme b/tgstation.dme
index b9ca2387876e..4cb12d4aee93 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2946,11 +2946,13 @@
#include "code\modules\modular_computers\file_system\programs\ntdownloader.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\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\robocontrol.dm"
#include "code\modules\modular_computers\file_system\programs\robotact.dm"
#include "code\modules\modular_computers\file_system\programs\secureye.dm"
+#include "code\modules\modular_computers\file_system\programs\signaler.dm"
#include "code\modules\modular_computers\file_system\programs\sm_monitor.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\contract_uplink.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm"
diff --git a/tgui/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs b/tgui/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
index e64e6dda163b..527659ff97f5 100644
--- a/tgui/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
+++ b/tgui/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
@@ -1,17 +1,307 @@
/* eslint-disable */
+//prettier-ignore
module.exports = {
name: "@yarnpkg/plugin-interactive-tools",
factory: function (require) {
-var plugin;plugin=(()=>{var __webpack_modules__={7560:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>K});function r(e,t,n,r){var i,o=arguments.length,u=o<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)u=Reflect.decorate(e,t,n,r);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(u=(o<3?i(u):o>3?i(t,n,u):i(t,n))||u);return o>3&&u&&Object.defineProperty(t,n,u),u}const i=require("@yarnpkg/cli"),o=require("@yarnpkg/core");var u=n(9245),a=n(7382);const l=(0,a.memo)(({active:e})=>{const t=(0,a.useMemo)(()=>e?"◉":"◯",[e]),n=(0,a.useMemo)(()=>e?"green":"yellow",[e]);return a.createElement(u.Text,{color:n},t)});function s({active:e},t,n){const{stdin:r}=(0,u.useStdin)(),i=(0,a.useCallback)((e,n)=>t(e,n),n);(0,a.useEffect)(()=>{if(e&&r)return r.on("keypress",i),()=>{r.off("keypress",i)}},[e,i,r])}var c;!function(e){e.BEFORE="before",e.AFTER="after"}(c||(c={}));const f=function(e,t,{active:n,minus:r,plus:i,set:o,loop:u=!0}){s({active:n},(n,a)=>{const l=t.indexOf(e);switch(a.name){case r:{const e=l-1;if(u)return void o(t[(t.length+e)%t.length]);if(e<0)return;o(t[e])}break;case i:{const e=l+1;if(u)return void o(t[e%t.length]);if(e>=t.length)return;o(t[e])}}},[t,e,i,o,u])},d=({active:e=!0,children:t=[],radius:n=10,size:r=1,loop:i=!0,onFocusRequest:o,willReachEnd:l})=>{const d=a.Children.map(t,e=>(e=>{if(null===e.key)throw new Error("Expected all children to have a key");return e.key})(e)),p=d[0],[h,v]=(0,a.useState)(p),m=d.indexOf(h);(0,a.useEffect)(()=>{d.includes(h)||v(p)},[t]),(0,a.useEffect)(()=>{l&&m>=d.length-2&&l()},[m]),function({active:e},t,n){s({active:e},(e,n)=>{"tab"===n.name&&(n.shift?t(c.BEFORE):t(c.AFTER))},n)}({active:e&&!!o},e=>{null==o||o(e)},[o]),f(h,d,{active:e,minus:"up",plus:"down",set:v,loop:i});let g=m-n,y=m+n;y>d.length&&(g-=y-d.length,y=d.length),g<0&&(y+=-g,g=0),y>=d.length&&(y=d.length-1);const _=[];for(let n=g;n<=y;++n){const i=d[n],o=e&&i===h;_.push(a.createElement(u.Box,{key:i,height:r},a.createElement(u.Box,{marginLeft:1,marginRight:1},a.createElement(u.Text,null,o?a.createElement(u.Text,{color:"cyan",bold:!0},">"):" ")),a.createElement(u.Box,null,a.cloneElement(t[n],{active:o}))))}return a.createElement(u.Box,{flexDirection:"column",width:"100%"},_)},p=require("readline"),h=a.createContext(null),v=({children:e})=>{const{stdin:t,setRawMode:n}=(0,u.useStdin)();(0,a.useEffect)(()=>{n&&n(!0),t&&(0,p.emitKeypressEvents)(t)},[t,n]);const[r,i]=(0,a.useState)(new Map),o=(0,a.useMemo)(()=>({getAll:()=>r,get:e=>r.get(e),set:(e,t)=>i(new Map([...r,[e,t]]))}),[r,i]);return a.createElement(h.Provider,{value:o,children:e})};function m(e,t){const n=(0,a.useContext)(h);if(null===n)throw new Error("Expected this hook to run with a ministore context attached");if(void 0===e)return n.getAll();const r=(0,a.useCallback)(t=>{n.set(e,t)},[e,n.set]);let i=n.get(e);return void 0===i&&(i=t),[i,r]}async function g(e,t){let n;const{waitUntilExit:r}=(0,u.render)(a.createElement(v,null,a.createElement(e,Object.assign({},t,{useSubmit:e=>{const{exit:t}=(0,u.useApp)();s({active:!0},(r,i)=>{"return"===i.name&&(n=e,t())},[t,e])}}))));return await r(),n}const y=require("clipanion");var _=n(7840),b=n(4410);const w={appId:"OFCNCOG2CU",apiKey:"6fe4476ee5a1832882e326b506d14126",indexName:"npm-search"},E=n.n(b)()(w.appId,w.apiKey).initIndex(w.indexName),D=async(e,t=0)=>await E.search(e,{analyticsTags:["yarn-plugin-interactive-tools"],attributesToRetrieve:["name","version","owner","repository","humanDownloadsLast30Days"],page:t,hitsPerPage:10}),S=["regular","dev","peer"];class C extends i.BaseCommand{async execute(){const e=await o.Configuration.find(this.context.cwd,this.context.plugins),t=()=>a.createElement(u.Box,{flexDirection:"row"},a.createElement(u.Box,{flexDirection:"column",width:48},a.createElement(u.Box,null,a.createElement(u.Text,null,"Press ",a.createElement(u.Text,{bold:!0,color:"cyanBright"},""),"/",a.createElement(u.Text,{bold:!0,color:"cyanBright"},"")," to move between packages.")),a.createElement(u.Box,null,a.createElement(u.Text,null,"Press ",a.createElement(u.Text,{bold:!0,color:"cyanBright"},"")," to select a package.")),a.createElement(u.Box,null,a.createElement(u.Text,null,"Press ",a.createElement(u.Text,{bold:!0,color:"cyanBright"},"")," again to change the target."))),a.createElement(u.Box,{flexDirection:"column"},a.createElement(u.Box,{marginLeft:1},a.createElement(u.Text,null,"Press ",a.createElement(u.Text,{bold:!0,color:"cyanBright"},"")," to install the selected packages.")),a.createElement(u.Box,{marginLeft:1},a.createElement(u.Text,null,"Press ",a.createElement(u.Text,{bold:!0,color:"cyanBright"},"")," to abort.")))),n=()=>a.createElement(a.Fragment,null,a.createElement(u.Box,{width:15},a.createElement(u.Text,{bold:!0,underline:!0,color:"gray"},"Owner")),a.createElement(u.Box,{width:11},a.createElement(u.Text,{bold:!0,underline:!0,color:"gray"},"Version")),a.createElement(u.Box,{width:10},a.createElement(u.Text,{bold:!0,underline:!0,color:"gray"},"Downloads"))),r=()=>a.createElement(u.Box,{width:17},a.createElement(u.Text,{bold:!0,underline:!0,color:"gray"},"Target")),i=({hit:t,active:n})=>{const[r,i]=m(t.name,null);s({active:n},(e,t)=>{if("space"!==t.name)return;if(!r)return void i(S[0]);const n=S.indexOf(r)+1;n===S.length?i(null):i(S[n])},[r,i]);const l=o.structUtils.parseIdent(t.name),c=o.structUtils.prettyIdent(e,l);return a.createElement(u.Box,null,a.createElement(u.Box,{width:45},a.createElement(u.Text,{bold:!0,wrap:"wrap"},c)),a.createElement(u.Box,{width:14,marginLeft:1},a.createElement(u.Text,{bold:!0,wrap:"truncate"},t.owner.name)),a.createElement(u.Box,{width:10,marginLeft:1},a.createElement(u.Text,{italic:!0,wrap:"truncate"},t.version)),a.createElement(u.Box,{width:16,marginLeft:1},a.createElement(u.Text,null,t.humanDownloadsLast30Days)))},c=({name:t,active:n})=>{const[r]=m(t,null),i=o.structUtils.parseIdent(t);return a.createElement(u.Box,null,a.createElement(u.Box,{width:47},a.createElement(u.Text,{bold:!0}," - ",o.structUtils.prettyIdent(e,i))),S.map(e=>a.createElement(u.Box,{key:e,width:14,marginLeft:1},a.createElement(u.Text,null," ",a.createElement(l,{active:r===e})," ",a.createElement(u.Text,{bold:!0},e)))))},f=()=>a.createElement(u.Box,{marginTop:1},a.createElement(u.Text,null,"Powered by Algolia.")),p=await g(({useSubmit:e})=>{const o=m();e(o);const l=Array.from(o.keys()).filter(e=>null!==o.get(e)),[s,p]=(0,a.useState)(""),[h,v]=(0,a.useState)(0),[g,y]=(0,a.useState)([]);return(0,a.useEffect)(()=>{s?(async()=>{v(0);const e=await D(s);e.query===s&&y(e.hits)})():y([])},[s]),a.createElement(u.Box,{flexDirection:"column"},a.createElement(t,null),a.createElement(u.Box,{flexDirection:"row",marginTop:1},a.createElement(u.Text,{bold:!0},"Search: "),a.createElement(u.Box,{width:41},a.createElement(_.ZP,{value:s,onChange:e=>{e.match(/\t| /)||p(e)},placeholder:"i.e. babel, webpack, react...",showCursor:!1})),a.createElement(n,null)),g.length?a.createElement(d,{radius:2,loop:!1,children:g.map(e=>a.createElement(i,{key:e.name,hit:e,active:!1})),willReachEnd:async()=>{const e=await D(s,h+1);e.query===s&&e.page-1===h&&(v(e.page),y([...g,...e.hits]))}}):a.createElement(u.Text,{color:"gray"},"Start typing..."),a.createElement(u.Box,{flexDirection:"row",marginTop:1},a.createElement(u.Box,{width:49},a.createElement(u.Text,{bold:!0},"Selected:")),a.createElement(r,null)),l.length?l.map(e=>a.createElement(c,{key:e,name:e,active:!1})):a.createElement(u.Text,{color:"gray"},"No selected packages..."),a.createElement(f,null))},{});if(void 0===p)return 1;const h=Array.from(p.keys()).filter(e=>"regular"===p.get(e)),v=Array.from(p.keys()).filter(e=>"dev"===p.get(e)),y=Array.from(p.keys()).filter(e=>"peer"===p.get(e));return h.length&&await this.cli.run(["add",...h]),v.length&&await this.cli.run(["add","--dev",...v]),y&&await this.cli.run(["add","--peer",...y]),0}}C.usage=y.Command.Usage({category:"Interactive commands",description:"open the search interface",details:"\n This command opens a fullscreen terminal interface where you can search for and install packages from the npm registry.\n ",examples:[["Open the search window","yarn search"]]}),r([y.Command.Path("search")],C.prototype,"execute",null);var k=n(5882),T=n.n(k);const x=({length:e,active:t})=>{if(0===e)return null;const n=e>1?" "+T().underline(" ".repeat(e-1)):" ";return a.createElement(u.Text,{dimColor:!t},n)},A=function({active:e,skewer:t,options:n,value:r,onChange:i,sizes:o=[]}){const s=n.map(({value:e})=>e),c=s.indexOf(r);return f(r,s,{active:e,minus:"left",plus:"right",set:i}),a.createElement(a.Fragment,null,n.map(({label:n},r)=>{const i=r===c,s=o[r]-1||0,f=n.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,""),d=Math.max(0,s-f.length-2);return a.createElement(u.Box,{key:n,width:s,marginLeft:1},a.createElement(u.Text,{wrap:"truncate"},a.createElement(l,{active:i})," ",n),t?a.createElement(x,{active:e,length:d}):null)}))},O=require("@yarnpkg/plugin-essentials");function P(){}function I(e,t,n,r,i){for(var o=0,u=t.length,a=0,l=0;oe.length?n:e})),s.value=e.join(f)}else s.value=e.join(n.slice(a,a+s.count));a+=s.count,s.added||(l+=s.count)}}var d=t[u-1];return u>1&&"string"==typeof d.value&&(d.added||d.removed)&&e.equals("",d.value)&&(t[u-2].value+=d.value,t.pop()),t}function N(e){return{newPos:e.newPos,components:e.components.slice(0)}}P.prototype={diff:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.callback;"function"==typeof n&&(r=n,n={}),this.options=n;var i=this;function o(e){return r?(setTimeout((function(){r(void 0,e)}),0),!0):e}e=this.castInput(e),t=this.castInput(t),e=this.removeEmpty(this.tokenize(e));var u=(t=this.removeEmpty(this.tokenize(t))).length,a=e.length,l=1,s=u+a,c=[{newPos:-1,components:[]}],f=this.extractCommon(c[0],t,e,0);if(c[0].newPos+1>=u&&f+1>=a)return o([{value:this.join(t),count:t.length}]);function d(){for(var n=-1*l;n<=l;n+=2){var r=void 0,s=c[n-1],f=c[n+1],d=(f?f.newPos:0)-n;s&&(c[n-1]=void 0);var p=s&&s.newPos+1=u&&d+1>=a)return o(I(i,r.components,t,e,i.useLongestToken));c[n]=r}else c[n]=void 0}l++}if(r)!function e(){setTimeout((function(){if(l>s)return r();d()||e()}),0)}();else for(;l<=s;){var p=d();if(p)return p}},pushComponent:function(e,t,n){var r=e[e.length-1];r&&r.added===t&&r.removed===n?e[e.length-1]={count:r.count+1,added:t,removed:n}:e.push({count:1,added:t,removed:n})},extractCommon:function(e,t,n,r){for(var i=t.length,o=n.length,u=e.newPos,a=u-r,l=0;u+1=?)?)([0-9]+)(\.[0-9]+)(\.[0-9]+)((?:-\S+)?)$/;class Y extends i.BaseCommand{async execute(){const e=await o.Configuration.find(this.context.cwd,this.context.plugins),{project:t,workspace:n}=await o.Project.find(e,this.context.cwd),r=await o.Cache.find(e);if(!n)throw new i.WorkspaceRequiredError(t.cwd,this.context.cwd);const l=(t,n)=>{const r=(i=t,u=n,a=M(a,{ignoreWhitespace:!0}),L.diff(i,u,a));var i,u,a;let l="";for(const t of r)t.added?l+=o.formatUtils.pretty(e,t.value,"green"):t.removed||(l+=t.value);return l},s=(t,n)=>{if(t===n)return n;const r=o.structUtils.parseRange(t),i=o.structUtils.parseRange(n),u=r.selector.match($),a=i.selector.match($);if(!u||!a)return l(t,n);const s=["gray","red","yellow","green","magenta"];let c=null,f="";for(let t=1;t{const u=await O.suggestUtils.fetchDescriptorFrom(e,o,{project:t,cache:r,preserveModifier:i,workspace:n});return null!==u?u.range:e.range},f=()=>a.createElement(u.Box,{flexDirection:"row"},a.createElement(u.Box,{flexDirection:"column",width:49},a.createElement(u.Box,{marginLeft:1},a.createElement(u.Text,null,"Press ",a.createElement(u.Text,{bold:!0,color:"cyanBright"},""),"/",a.createElement(u.Text,{bold:!0,color:"cyanBright"},"")," to select packages.")),a.createElement(u.Box,{marginLeft:1},a.createElement(u.Text,null,"Press ",a.createElement(u.Text,{bold:!0,color:"cyanBright"},""),"/",a.createElement(u.Text,{bold:!0,color:"cyanBright"},"")," to select versions."))),a.createElement(u.Box,{flexDirection:"column"},a.createElement(u.Box,{marginLeft:1},a.createElement(u.Text,null,"Press ",a.createElement(u.Text,{bold:!0,color:"cyanBright"},"")," to install.")),a.createElement(u.Box,{marginLeft:1},a.createElement(u.Text,null,"Press ",a.createElement(u.Text,{bold:!0,color:"cyanBright"},"")," to abort.")))),p=()=>a.createElement(u.Box,{flexDirection:"row",paddingTop:1,paddingBottom:1},a.createElement(u.Box,{width:50},a.createElement(u.Text,{bold:!0},a.createElement(u.Text,{color:"greenBright"},"?")," Pick the packages you want to upgrade.")),a.createElement(u.Box,{width:17},a.createElement(u.Text,{bold:!0,underline:!0,color:"gray"},"Current")),a.createElement(u.Box,{width:17},a.createElement(u.Text,{bold:!0,underline:!0,color:"gray"},"Range")),a.createElement(u.Box,{width:17},a.createElement(u.Text,{bold:!0,underline:!0,color:"gray"},"Latest"))),h=({active:t,descriptor:n,suggestions:r})=>{const[i,l]=m(n.descriptorHash,null),s=o.structUtils.stringifyIdent(n),c=Math.max(0,45-s.length);return a.createElement(a.Fragment,null,a.createElement(u.Box,null,a.createElement(u.Box,{width:45},a.createElement(u.Text,{bold:!0},o.structUtils.prettyIdent(e,n)),a.createElement(x,{active:t,length:c})),null!==r?a.createElement(A,{active:t,options:r,value:i,skewer:!0,onChange:l,sizes:[17,17,17]}):a.createElement(u.Box,{marginLeft:2},a.createElement(u.Text,{color:"gray"},"Fetching suggestions..."))))},v=({dependencies:e})=>{const[t,n]=(0,a.useState)(null),r=(0,a.useRef)(!0);return(0,a.useEffect)(()=>()=>{r.current=!1}),(0,a.useEffect)(()=>{Promise.all(e.map(e=>(async e=>{const t=G().valid(e.range)?"^"+e.range:e.range,[n,r]=await Promise.all([c(e,e.range,t).catch(()=>null),c(e,e.range,"latest").catch(()=>null)]),i=[{value:null,label:e.range}];return n&&n!==e.range&&i.push({value:n,label:s(e.range,n)}),r&&r!==n&&r!==e.range&&i.push({value:r,label:s(e.range,r)}),i})(e))).then(t=>{const i=e.map((e,n)=>[e,t[n]]).filter(([e,t])=>t.length>1);r.current&&n(i)})},[]),t?t.length?a.createElement(d,{radius:10,children:t.map(([e,t])=>a.createElement(h,{key:e.descriptorHash,active:!1,descriptor:e,suggestions:t}))}):a.createElement(u.Text,null,"No upgrades found"):a.createElement(u.Text,null,"Fetching suggestions...")},y=await g(({useSubmit:e})=>{e(m());const n=new Map;for(const e of t.workspaces)for(const r of["dependencies","devDependencies"])for(const i of e.manifest[r].values())null===t.tryWorkspaceByDescriptor(i)&&n.set(i.descriptorHash,i);const r=o.miscUtils.sortMap(n.values(),e=>o.structUtils.stringifyDescriptor(e));return a.createElement(u.Box,{flexDirection:"column"},a.createElement(f,null),a.createElement(p,null),a.createElement(v,{dependencies:r}))},{});if(void 0===y)return 1;let _=!1;for(const e of t.workspaces)for(const t of["dependencies","devDependencies"]){const n=e.manifest[t];for(const e of n.values()){const t=y.get(e.descriptorHash);null!=t&&(n.set(e.identHash,o.structUtils.makeDescriptor(e,t)),_=!0)}}if(!_)return 0;return(await o.StreamReport.start({configuration:e,stdout:this.context.stdout,includeLogs:!this.context.quiet},async e=>{await t.install({cache:r,report:e})})).exitCode()}}Y.usage=y.Command.Usage({category:"Interactive commands",description:"open the upgrade interface",details:"\n This command opens a fullscreen terminal interface where you can see any out of date packages used by your application, their status compared to the latest versions available on the remote registry, and select packages to upgrade.\n ",examples:[["Open the upgrade window","yarn upgrade-interactive"]]}),r([y.Command.Path("upgrade-interactive")],Y.prototype,"execute",null);const K={commands:[C,Y]}},7840:(e,t,n)=>{"use strict";const r=n(7382),i=n(7382),o=n(9245),u=n(1525),a=({value:e,placeholder:t="",focus:n=!0,mask:a,highlightPastedText:l=!1,showCursor:s=!0,onChange:c,onSubmit:f})=>{const[{cursorOffset:d,cursorWidth:p},h]=i.useState({cursorOffset:(e||"").length,cursorWidth:0});i.useEffect(()=>{h(t=>{if(!n||!s)return t;const r=e||"";return t.cursorOffset>r.length-1?{cursorOffset:r.length,cursorWidth:0}:t})},[e,n,s]);const v=l?p:0,m=a?a.repeat(e.length):e;let g=m,y=t?u.grey(t):void 0;if(s&&n){y=t.length>0?u.inverse(t[0])+u.grey(t.slice(1)):u.inverse(" "),g=m.length>0?"":u.inverse(" ");let e=0;for(const t of m)g+=e>=d-v&&e<=d?u.inverse(t):t,e++;m.length>0&&d===m.length&&(g+=u.inverse(" "))}return o.useInput((t,n)=>{if(n.upArrow||n.downArrow||n.ctrl&&"c"===t||n.tab||n.shift&&n.tab)return;if(n.return)return void(f&&f(e));let r=d,i=e,o=0;n.leftArrow?s&&r--:n.rightArrow?s&&r++:n.backspace||n.delete?d>0&&(i=e.slice(0,d-1)+e.slice(d,e.length),r--):(i=e.slice(0,d)+t+e.slice(d,e.length),r+=t.length,t.length>1&&(o=t.length)),d<0&&(r=0),d>e.length&&(r=e.length),h({cursorOffset:r,cursorWidth:o}),i!==e&&c(i)},{isActive:n}),r.createElement(o.Text,null,t?m.length>0?g:y:g)};t.ZP=a},9902:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(1525)),o=/^(rgb|hsl|hsv|hwb)\(\s?(\d+),\s?(\d+),\s?(\d+)\s?\)$/,u=/^(ansi|ansi256)\(\s?(\d+)\s?\)$/,a=(e,t)=>"foreground"===t?e:"bg"+e[0].toUpperCase()+e.slice(1);t.default=(e,t,n)=>{if(!t)return e;if(t in i.default){const r=a(t,n);return i.default[r](e)}if(t.startsWith("#")){const r=a("hex",n);return i.default[r](t)(e)}if(t.startsWith("ansi")){const r=u.exec(t);if(!r)return e;const o=a(r[1],n),l=Number(r[2]);return i.default[o](l)(e)}if(t.startsWith("rgb")||t.startsWith("hsl")||t.startsWith("hsv")||t.startsWith("hwb")){const r=o.exec(t);if(!r)return e;const u=a(r[1],n),l=Number(r[2]),s=Number(r[3]),c=Number(r[4]);return i.default[u](l,s,c)(e)}return e}},2773:function(e,t,n){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n),Object.defineProperty(e,r,{enumerable:!0,get:function(){return t[n]}})}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.hasOwnProperty.call(e,n)&&r(t,e,n);return i(t,e),t},u=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const a=o(n(7382)),l=u(n(1696)),s=u(n(5512)),c=u(n(1489)),f=u(n(6834)),d=u(n(5001)),p=u(n(2560)),h=u(n(9052));class v extends a.PureComponent{constructor(){super(...arguments),this.state={isFocusEnabled:!0,activeFocusId:void 0,focusables:[],error:void 0},this.rawModeEnabledCount=0,this.handleSetRawMode=e=>{const{stdin:t}=this.props;if(!this.isRawModeSupported())throw t===process.stdin?new Error("Raw mode is not supported on the current process.stdin, which Ink uses as input stream by default.\nRead about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported"):new Error("Raw mode is not supported on the stdin provided to Ink.\nRead about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported");if(t.setEncoding("utf8"),e)return 0===this.rawModeEnabledCount&&(t.addListener("data",this.handleInput),t.resume(),t.setRawMode(!0)),void this.rawModeEnabledCount++;0==--this.rawModeEnabledCount&&(t.setRawMode(!1),t.removeListener("data",this.handleInput),t.pause())},this.handleInput=e=>{""===e&&this.props.exitOnCtrlC&&this.handleExit(),""===e&&this.state.activeFocusId&&this.setState({activeFocusId:void 0}),this.state.isFocusEnabled&&this.state.focusables.length>0&&("\t"===e&&this.focusNext(),"[Z"===e&&this.focusPrevious())},this.handleExit=e=>{this.isRawModeSupported()&&this.handleSetRawMode(!1),this.props.onExit(e)},this.enableFocus=()=>{this.setState({isFocusEnabled:!0})},this.disableFocus=()=>{this.setState({isFocusEnabled:!1})},this.focusNext=()=>{this.setState(e=>{const t=e.focusables[0].id;return{activeFocusId:this.findNextFocusable(e)||t}})},this.focusPrevious=()=>{this.setState(e=>{const t=e.focusables[e.focusables.length-1].id;return{activeFocusId:this.findPreviousFocusable(e)||t}})},this.addFocusable=(e,{autoFocus:t})=>{this.setState(n=>{let r=n.activeFocusId;return!r&&t&&(r=e),{activeFocusId:r,focusables:[...n.focusables,{id:e,isActive:!0}]}})},this.removeFocusable=e=>{this.setState(t=>({activeFocusId:t.activeFocusId===e?void 0:t.activeFocusId,focusables:t.focusables.filter(t=>t.id!==e)}))},this.activateFocusable=e=>{this.setState(t=>({focusables:t.focusables.map(t=>t.id!==e?t:{id:e,isActive:!0})}))},this.deactivateFocusable=e=>{this.setState(t=>({activeFocusId:t.activeFocusId===e?void 0:t.activeFocusId,focusables:t.focusables.map(t=>t.id!==e?t:{id:e,isActive:!1})}))},this.findNextFocusable=e=>{for(let t=e.focusables.findIndex(t=>t.id===e.activeFocusId)+1;t{for(let t=e.focusables.findIndex(t=>t.id===e.activeFocusId)-1;t>=0;t--)if(e.focusables[t].isActive)return e.focusables[t].id}}static getDerivedStateFromError(e){return{error:e}}isRawModeSupported(){return this.props.stdin.isTTY}render(){return a.default.createElement(s.default.Provider,{value:{exit:this.handleExit}},a.default.createElement(c.default.Provider,{value:{stdin:this.props.stdin,setRawMode:this.handleSetRawMode,isRawModeSupported:this.isRawModeSupported(),internal_exitOnCtrlC:this.props.exitOnCtrlC}},a.default.createElement(f.default.Provider,{value:{stdout:this.props.stdout,write:this.props.writeToStdout}},a.default.createElement(d.default.Provider,{value:{stderr:this.props.stderr,write:this.props.writeToStderr}},a.default.createElement(p.default.Provider,{value:{activeId:this.state.activeFocusId,add:this.addFocusable,remove:this.removeFocusable,activate:this.activateFocusable,deactivate:this.deactivateFocusable,enableFocus:this.enableFocus,disableFocus:this.disableFocus,focusNext:this.focusNext,focusPrevious:this.focusPrevious}},this.state.error?a.default.createElement(h.default,{error:this.state.error}):this.props.children)))))}componentDidMount(){l.default.hide(this.props.stdout)}componentWillUnmount(){l.default.show(this.props.stdout),this.isRawModeSupported()&&this.handleSetRawMode(!1)}componentDidCatch(e){this.handleExit(e)}}t.default=v,v.displayName="InternalApp"},5512:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(7382).createContext({exit:()=>{}});r.displayName="InternalAppContext",t.default=r},5277:function(e,t,n){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n),Object.defineProperty(e,r,{enumerable:!0,get:function(){return t[n]}})}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.hasOwnProperty.call(e,n)&&r(t,e,n);return i(t,e),t},u=this&&this.__rest||function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i{var{children:n}=e,r=u(e,["children"]);const i=Object.assign(Object.assign({},r),{marginLeft:r.marginLeft||r.marginX||r.margin||0,marginRight:r.marginRight||r.marginX||r.margin||0,marginTop:r.marginTop||r.marginY||r.margin||0,marginBottom:r.marginBottom||r.marginY||r.margin||0,paddingLeft:r.paddingLeft||r.paddingX||r.padding||0,paddingRight:r.paddingRight||r.paddingX||r.padding||0,paddingTop:r.paddingTop||r.paddingY||r.padding||0,paddingBottom:r.paddingBottom||r.paddingY||r.padding||0});return a.default.createElement("ink-box",{ref:t,style:i},n)});l.displayName="Box",l.defaultProps={flexDirection:"row",flexGrow:0,flexShrink:1},t.default=l},9052:function(e,t,n){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n),Object.defineProperty(e,r,{enumerable:!0,get:function(){return t[n]}})}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.hasOwnProperty.call(e,n)&&r(t,e,n);return i(t,e),t},u=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const a=o(n(5747)),l=u(n(7382)),s=u(n(9796)),c=u(n(9908)),f=u(n(5277)),d=u(n(9146)),p=new s.default({cwd:process.cwd(),internals:s.default.nodeInternals()});t.default=({error:e})=>{const t=e.stack?e.stack.split("\n").slice(1):void 0,n=t?p.parseLine(t[0]):void 0;let r,i=0;if((null==n?void 0:n.file)&&(null==n?void 0:n.line)&&a.existsSync(n.file)){const e=a.readFileSync(n.file,"utf8");if(r=c.default(e,n.line),r)for(const{line:e}of r)i=Math.max(i,String(e).length)}return l.default.createElement(f.default,{flexDirection:"column",padding:1},l.default.createElement(f.default,null,l.default.createElement(d.default,{backgroundColor:"red",color:"white"}," ","ERROR"," "),l.default.createElement(d.default,null," ",e.message)),n&&l.default.createElement(f.default,{marginTop:1},l.default.createElement(d.default,{dimColor:!0},n.file,":",n.line,":",n.column)),n&&r&&l.default.createElement(f.default,{marginTop:1,flexDirection:"column"},r.map(({line:e,value:t})=>l.default.createElement(f.default,{key:e},l.default.createElement(f.default,{width:i+1},l.default.createElement(d.default,{dimColor:e!==n.line,backgroundColor:e===n.line?"red":void 0,color:e===n.line?"white":void 0},String(e).padStart(i," "),":")),l.default.createElement(d.default,{key:e,backgroundColor:e===n.line?"red":void 0,color:e===n.line?"white":void 0}," "+t)))),e.stack&&l.default.createElement(f.default,{marginTop:1,flexDirection:"column"},e.stack.split("\n").slice(1).map(e=>{const t=p.parseLine(e);return t?l.default.createElement(f.default,{key:e},l.default.createElement(d.default,{dimColor:!0},"- "),l.default.createElement(d.default,{dimColor:!0,bold:!0},t.function),l.default.createElement(d.default,{dimColor:!0,color:"gray"}," ","(",t.file,":",t.line,":",t.column,")")):l.default.createElement(f.default,{key:e},l.default.createElement(d.default,{dimColor:!0},"- "),l.default.createElement(d.default,{dimColor:!0,bold:!0},e))})))}},2560:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(7382).createContext({activeId:void 0,add:()=>{},remove:()=>{},activate:()=>{},deactivate:()=>{},enableFocus:()=>{},disableFocus:()=>{},focusNext:()=>{},focusPrevious:()=>{}});r.displayName="InternalFocusContext",t.default=r},8200:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(7382)),o=({count:e=1})=>i.default.createElement("ink-text",null,"\n".repeat(e));o.displayName="Newline",t.default=o},2198:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(7382)),o=r(n(5277)),u=()=>i.default.createElement(o.default,{flexGrow:1});u.displayName="Spacer",t.default=u},8915:function(e,t,n){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n),Object.defineProperty(e,r,{enumerable:!0,get:function(){return t[n]}})}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.hasOwnProperty.call(e,n)&&r(t,e,n);return i(t,e),t};Object.defineProperty(t,"__esModule",{value:!0});const u=o(n(7382)),a=e=>{const{items:t,children:n,style:r}=e,[i,o]=u.useState(0),a=u.useMemo(()=>t.slice(i),[t,i]);u.useLayoutEffect(()=>{o(t.length)},[t.length]);const l=a.map((e,t)=>n(e,i+t)),s=u.useMemo(()=>Object.assign({position:"absolute",flexDirection:"column"},r),[r]);return u.default.createElement("ink-box",{internal_static:!0,style:s},l)};a.displayName="Static",t.default=a},5001:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(7382).createContext({stderr:void 0,write:()=>{}});r.displayName="InternalStderrContext",t.default=r},1489:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(7382).createContext({stdin:void 0,setRawMode:()=>{},isRawModeSupported:!1,internal_exitOnCtrlC:!0});r.displayName="InternalStdinContext",t.default=r},6834:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(7382).createContext({stdout:void 0,write:()=>{}});r.displayName="InternalStdoutContext",t.default=r},9146:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(7382)),o=r(n(1525)),u=r(n(9902)),a=({color:e,backgroundColor:t,dimColor:n,bold:r,italic:a,underline:l,strikethrough:s,inverse:c,wrap:f,children:d})=>{if(null==d)return null;return i.default.createElement("ink-text",{style:{flexGrow:0,flexShrink:1,flexDirection:"row",textWrap:f},internal_transform:i=>(n&&(i=o.default.dim(i)),e&&(i=u.default(i,e,"foreground")),t&&(i=u.default(i,t,"background")),r&&(i=o.default.bold(i)),a&&(i=o.default.italic(i)),l&&(i=o.default.underline(i)),s&&(i=o.default.strikethrough(i)),c&&(i=o.default.inverse(i)),i)},d)};a.displayName="Text",a.defaultProps={dimColor:!1,bold:!1,italic:!1,underline:!1,strikethrough:!1,wrap:"wrap"},t.default=a},4592:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(7382)),o=({children:e,transform:t})=>null==e?null:i.default.createElement("ink-text",{style:{flexGrow:0,flexShrink:1,flexDirection:"row"},internal_transform:t},e);o.displayName="Transform",t.default=o},146:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(3296)),o=n(5187),u=global;u.WebSocket||(u.WebSocket=i.default),u.window||(u.window=global),u.window.__REACT_DEVTOOLS_COMPONENT_FILTERS__=[{type:1,value:7,isEnabled:!0},{type:2,value:"InternalApp",isEnabled:!0,isValid:!0},{type:2,value:"InternalAppContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalStdoutContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalStderrContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalStdinContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalFocusContext",isEnabled:!0,isValid:!0}],o.connectToDevTools()},9864:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.setTextNodeValue=t.createTextNode=t.setStyle=t.setAttribute=t.removeChildNode=t.insertBeforeNode=t.appendChildNode=t.createNode=t.TEXT_NAME=void 0;const i=r(n(6401)),o=r(n(8113)),u=r(n(5809)),a=r(n(2030)),l=r(n(9099));t.TEXT_NAME="#text",t.createNode=e=>{var t;const n={nodeName:e,style:{},attributes:{},childNodes:[],parentNode:null,yogaNode:"ink-virtual-text"===e?void 0:i.default.Node.create()};return"ink-text"===e&&(null===(t=n.yogaNode)||void 0===t||t.setMeasureFunc(s.bind(null,n))),n},t.appendChildNode=(e,n)=>{var r;n.parentNode&&t.removeChildNode(n.parentNode,n),n.parentNode=e,e.childNodes.push(n),n.yogaNode&&(null===(r=e.yogaNode)||void 0===r||r.insertChild(n.yogaNode,e.yogaNode.getChildCount())),"ink-text"!==e.nodeName&&"ink-virtual-text"!==e.nodeName||f(e)},t.insertBeforeNode=(e,n,r)=>{var i,o;n.parentNode&&t.removeChildNode(n.parentNode,n),n.parentNode=e;const u=e.childNodes.indexOf(r);if(u>=0)return e.childNodes.splice(u,0,n),void(n.yogaNode&&(null===(i=e.yogaNode)||void 0===i||i.insertChild(n.yogaNode,u)));e.childNodes.push(n),n.yogaNode&&(null===(o=e.yogaNode)||void 0===o||o.insertChild(n.yogaNode,e.yogaNode.getChildCount())),"ink-text"!==e.nodeName&&"ink-virtual-text"!==e.nodeName||f(e)},t.removeChildNode=(e,t)=>{var n,r;t.yogaNode&&(null===(r=null===(n=t.parentNode)||void 0===n?void 0:n.yogaNode)||void 0===r||r.removeChild(t.yogaNode)),t.parentNode=null;const i=e.childNodes.indexOf(t);i>=0&&e.childNodes.splice(i,1),"ink-text"!==e.nodeName&&"ink-virtual-text"!==e.nodeName||f(e)},t.setAttribute=(e,t,n)=>{e.attributes[t]=n},t.setStyle=(e,t)=>{e.style=t,e.yogaNode&&u.default(e.yogaNode,t)},t.createTextNode=e=>{const n={nodeName:"#text",nodeValue:e,yogaNode:void 0,parentNode:null,style:{}};return t.setTextNodeValue(n,e),n};const s=function(e,t){var n,r;const i="#text"===e.nodeName?e.nodeValue:l.default(e),u=o.default(i);if(u.width<=t)return u;if(u.width>=1&&t>0&&t<1)return u;const s=null!==(r=null===(n=e.style)||void 0===n?void 0:n.textWrap)&&void 0!==r?r:"wrap",c=a.default(i,t,s);return o.default(c)},c=e=>{var t;if(e&&e.parentNode)return null!==(t=e.yogaNode)&&void 0!==t?t:c(e.parentNode)},f=e=>{const t=c(e);null==t||t.markDirty()};t.setTextNodeValue=(e,t)=>{"string"!=typeof t&&(t=String(t)),e.nodeValue=t,f(e)}},317:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(6401));t.default=e=>e.getComputedWidth()-e.getComputedPadding(i.default.EDGE_LEFT)-e.getComputedPadding(i.default.EDGE_RIGHT)-e.getComputedBorder(i.default.EDGE_LEFT)-e.getComputedBorder(i.default.EDGE_RIGHT)},4699:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=n(7382),o=r(n(5512));t.default=()=>i.useContext(o.default)},5442:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=n(7382),o=r(n(2560));t.default=()=>{const e=i.useContext(o.default);return{enableFocus:e.enableFocus,disableFocus:e.disableFocus,focusNext:e.focusNext,focusPrevious:e.focusPrevious}}},8230:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=n(7382),o=r(n(2560)),u=r(n(1541));t.default=({isActive:e=!0,autoFocus:t=!1}={})=>{const{isRawModeSupported:n,setRawMode:r}=u.default(),{activeId:a,add:l,remove:s,activate:c,deactivate:f}=i.useContext(o.default),d=i.useMemo(()=>Math.random().toString().slice(2,7),[]);return i.useEffect(()=>(l(d,{autoFocus:t}),()=>{s(d)}),[d,t]),i.useEffect(()=>{e?c(d):f(d)},[e,d]),i.useEffect(()=>{if(n&&e)return r(!0),()=>{r(!1)}},[e]),{isFocused:Boolean(d)&&a===d}}},4495:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=n(7382),o=r(n(1541));t.default=(e,t={})=>{const{stdin:n,setRawMode:r,internal_exitOnCtrlC:u}=o.default();i.useEffect(()=>{if(!1!==t.isActive)return r(!0),()=>{r(!1)}},[t.isActive,r]),i.useEffect(()=>{if(!1===t.isActive)return;const r=t=>{let n=String(t);const r={upArrow:"[A"===n,downArrow:"[B"===n,leftArrow:"[D"===n,rightArrow:"[C"===n,pageDown:"[6~"===n,pageUp:"[5~"===n,return:"\r"===n,escape:""===n,ctrl:!1,shift:!1,tab:"\t"===n||"[Z"===n,backspace:"\b"===n,delete:""===n||"[3~"===n,meta:!1};n<=""&&!r.return&&(n=String.fromCharCode(n.charCodeAt(0)+"a".charCodeAt(0)-1),r.ctrl=!0),n.startsWith("")&&(n=n.slice(1),r.meta=!0);const i=n>="A"&&n<="Z",o=n>="А"&&n<="Я";1===n.length&&(i||o)&&(r.shift=!0),r.tab&&"[Z"===n&&(r.shift=!0),(r.tab||r.backspace||r.delete)&&(n=""),"c"===n&&r.ctrl&&u||e(n,r)};return null==n||n.on("data",r),()=>{null==n||n.off("data",r)}},[t.isActive,n,u,e])}},1686:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=n(7382),o=r(n(5001));t.default=()=>i.useContext(o.default)},1541:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=n(7382),o=r(n(1489));t.default=()=>i.useContext(o.default)},9890:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=n(7382),o=r(n(6834));t.default=()=>i.useContext(o.default)},9245:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(9417);Object.defineProperty(t,"render",{enumerable:!0,get:function(){return r.default}});var i=n(5277);Object.defineProperty(t,"Box",{enumerable:!0,get:function(){return i.default}});var o=n(9146);Object.defineProperty(t,"Text",{enumerable:!0,get:function(){return o.default}});var u=n(8915);Object.defineProperty(t,"Static",{enumerable:!0,get:function(){return u.default}});var a=n(4592);Object.defineProperty(t,"Transform",{enumerable:!0,get:function(){return a.default}});var l=n(8200);Object.defineProperty(t,"Newline",{enumerable:!0,get:function(){return l.default}});var s=n(2198);Object.defineProperty(t,"Spacer",{enumerable:!0,get:function(){return s.default}});var c=n(4495);Object.defineProperty(t,"useInput",{enumerable:!0,get:function(){return c.default}});var f=n(4699);Object.defineProperty(t,"useApp",{enumerable:!0,get:function(){return f.default}});var d=n(1541);Object.defineProperty(t,"useStdin",{enumerable:!0,get:function(){return d.default}});var p=n(9890);Object.defineProperty(t,"useStdout",{enumerable:!0,get:function(){return p.default}});var h=n(1686);Object.defineProperty(t,"useStderr",{enumerable:!0,get:function(){return h.default}});var v=n(8230);Object.defineProperty(t,"useFocus",{enumerable:!0,get:function(){return v.default}});var m=n(5442);Object.defineProperty(t,"useFocusManager",{enumerable:!0,get:function(){return m.default}});var g=n(3887);Object.defineProperty(t,"measureElement",{enumerable:!0,get:function(){return g.default}})},3206:function(e,t,n){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n),Object.defineProperty(e,r,{enumerable:!0,get:function(){return t[n]}})}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.hasOwnProperty.call(e,n)&&r(t,e,n);return i(t,e),t},u=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const a=u(n(7382)),l=n(464),s=u(n(503)),c=u(n(7589)),f=u(n(2738)),d=u(n(2633)),p=u(n(5117)),h=u(n(5691)),v=u(n(6458)),m=u(n(8070)),g=o(n(9864)),y=u(n(9679)),_=u(n(2773)),b="false"!==process.env.CI&&f.default,w=()=>{};t.default=class{constructor(e){this.resolveExitPromise=()=>{},this.rejectExitPromise=()=>{},this.unsubscribeExit=()=>{},this.onRender=()=>{if(this.isUnmounted)return;const{output:e,outputHeight:t,staticOutput:n}=h.default(this.rootNode,this.options.stdout.columns||80),r=n&&"\n"!==n;return this.options.debug?(r&&(this.fullStaticOutput+=n),void this.options.stdout.write(this.fullStaticOutput+e)):b?(r&&this.options.stdout.write(n),void(this.lastOutput=e)):(r&&(this.fullStaticOutput+=n),t>=this.options.stdout.rows?(this.options.stdout.write(c.default.clearTerminal+this.fullStaticOutput+e),void(this.lastOutput=e)):(r&&(this.log.clear(),this.options.stdout.write(n),this.log(e)),r||e===this.lastOutput||this.throttledLog(e),void(this.lastOutput=e)))},d.default(this),this.options=e,this.rootNode=g.createNode("ink-root"),this.rootNode.onRender=e.debug?this.onRender:l.throttle(this.onRender,32,{leading:!0,trailing:!0}),this.rootNode.onImmediateRender=this.onRender,this.log=s.default.create(e.stdout),this.throttledLog=e.debug?this.log:l.throttle(this.log,void 0,{leading:!0,trailing:!0}),this.isUnmounted=!1,this.lastOutput="",this.fullStaticOutput="",this.container=p.default.createContainer(this.rootNode,!1,!1),this.unsubscribeExit=v.default(this.unmount,{alwaysLast:!1}),"true"===process.env.DEV&&p.default.injectIntoDevTools({bundleType:0,version:"16.13.1",rendererPackageName:"ink"}),e.patchConsole&&this.patchConsole(),b||(e.stdout.on("resize",this.onRender),this.unsubscribeResize=()=>{e.stdout.off("resize",this.onRender)})}render(e){const t=a.default.createElement(_.default,{stdin:this.options.stdin,stdout:this.options.stdout,stderr:this.options.stderr,writeToStdout:this.writeToStdout,writeToStderr:this.writeToStderr,exitOnCtrlC:this.options.exitOnCtrlC,onExit:this.unmount},e);p.default.updateContainer(t,this.container,null,w)}writeToStdout(e){this.isUnmounted||(this.options.debug?this.options.stdout.write(e+this.fullStaticOutput+this.lastOutput):b?this.options.stdout.write(e):(this.log.clear(),this.options.stdout.write(e),this.log(this.lastOutput)))}writeToStderr(e){if(!this.isUnmounted)return this.options.debug?(this.options.stderr.write(e),void this.options.stdout.write(this.fullStaticOutput+this.lastOutput)):void(b?this.options.stderr.write(e):(this.log.clear(),this.options.stderr.write(e),this.log(this.lastOutput)))}unmount(e){this.isUnmounted||(this.onRender(),this.unsubscribeExit(),"function"==typeof this.restoreConsole&&this.restoreConsole(),"function"==typeof this.unsubscribeResize&&this.unsubscribeResize(),b?this.options.stdout.write(this.lastOutput+"\n"):this.options.debug||this.log.done(),this.isUnmounted=!0,p.default.updateContainer(null,this.container,null,w),y.default.delete(this.options.stdout),e instanceof Error?this.rejectExitPromise(e):this.resolveExitPromise())}waitUntilExit(){return this.exitPromise||(this.exitPromise=new Promise((e,t)=>{this.resolveExitPromise=e,this.rejectExitPromise=t})),this.exitPromise}clear(){b||this.options.debug||this.log.clear()}patchConsole(){this.options.debug||(this.restoreConsole=m.default((e,t)=>{if("stdout"===e&&this.writeToStdout(t),"stderr"===e){t.startsWith("The above error occurred")||this.writeToStderr(t)}}))}}},9679:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=new WeakMap},503:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(7589)),o=r(n(1696));t.default={create:(e,{showCursor:t=!1}={})=>{let n=0,r="",u=!1;const a=a=>{t||u||(o.default.hide(),u=!0);const l=a+"\n";l!==r&&(r=l,e.write(i.default.eraseLines(n)+l),n=l.split("\n").length)};return a.clear=()=>{e.write(i.default.eraseLines(n)),r="",n=0},a.done=()=>{r="",n=0,t||(o.default.show(),u=!1)},a}}},3887:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=e=>{var t,n,r,i;return{width:null!==(n=null===(t=e.yogaNode)||void 0===t?void 0:t.getComputedWidth())&&void 0!==n?n:0,height:null!==(i=null===(r=e.yogaNode)||void 0===r?void 0:r.getComputedHeight())&&void 0!==i?i:0}}},8113:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(8949)),o={};t.default=e=>{if(0===e.length)return{width:0,height:0};if(o[e])return o[e];const t=i.default(e),n=e.split("\n").length;return o[e]={width:t,height:n},{width:t,height:n}}},4110:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(1566)),o=r(n(3262));t.default=class{constructor(e){this.writes=[];const{width:t,height:n}=e;this.width=t,this.height=n}write(e,t,n,r){const{transformers:i}=r;n&&this.writes.push({x:e,y:t,text:n,transformers:i})}get(){const e=[];for(let t=0;te.trimRight()).join("\n"),height:e.length}}}},5117:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=n(7181),o=r(n(7714)),u=r(n(6401)),a=n(9864);"true"===process.env.DEV&&n(146);const l=e=>{null==e||e.unsetMeasureFunc(),null==e||e.freeRecursive()};t.default=o.default({schedulePassiveEffects:i.unstable_scheduleCallback,cancelPassiveEffects:i.unstable_cancelCallback,now:Date.now,getRootHostContext:()=>({isInsideText:!1}),prepareForCommit:()=>{},resetAfterCommit:e=>{if(e.isStaticDirty)return e.isStaticDirty=!1,void("function"==typeof e.onImmediateRender&&e.onImmediateRender());"function"==typeof e.onRender&&e.onRender()},getChildHostContext:(e,t)=>{const n="ink-text"===t||"ink-virtual-text"===t;return e.isInsideText===n?e:{isInsideText:n}},shouldSetTextContent:()=>!1,createInstance:(e,t,n,r)=>{if(r.isInsideText&&"ink-box"===e)throw new Error(" can’t be nested inside component");const i="ink-text"===e&&r.isInsideText?"ink-virtual-text":e,o=a.createNode(i);for(const[e,n]of Object.entries(t))"children"!==e&&("style"===e?a.setStyle(o,n):"internal_transform"===e?o.internal_transform=n:"internal_static"===e?o.internal_static=!0:a.setAttribute(o,e,n));return o},createTextInstance:(e,t,n)=>{if(!n.isInsideText)throw new Error(`Text string "${e}" must be rendered inside component`);return a.createTextNode(e)},resetTextContent:()=>{},hideTextInstance:e=>{a.setTextNodeValue(e,"")},unhideTextInstance:(e,t)=>{a.setTextNodeValue(e,t)},getPublicInstance:e=>e,hideInstance:e=>{var t;null===(t=e.yogaNode)||void 0===t||t.setDisplay(u.default.DISPLAY_NONE)},unhideInstance:e=>{var t;null===(t=e.yogaNode)||void 0===t||t.setDisplay(u.default.DISPLAY_FLEX)},appendInitialChild:a.appendChildNode,appendChild:a.appendChildNode,insertBefore:a.insertBeforeNode,finalizeInitialChildren:(e,t,n,r)=>(e.internal_static&&(r.isStaticDirty=!0,r.staticNode=e),!1),supportsMutation:!0,appendChildToContainer:a.appendChildNode,insertInContainerBefore:a.insertBeforeNode,removeChildFromContainer:(e,t)=>{a.removeChildNode(e,t),l(t.yogaNode)},prepareUpdate:(e,t,n,r,i)=>{e.internal_static&&(i.isStaticDirty=!0);const o={},u=Object.keys(r);for(const e of u)if(r[e]!==n[e]){if("style"===e&&"object"==typeof r.style&&"object"==typeof n.style){const e=r.style,t=n.style,i=Object.keys(e);for(const n of i){if("borderStyle"===n||"borderColor"===n){if("object"!=typeof o.style){const e={};o.style=e}o.style.borderStyle=e.borderStyle,o.style.borderColor=e.borderColor}if(e[n]!==t[n]){if("object"!=typeof o.style){const e={};o.style=e}o.style[n]=e[n]}}continue}o[e]=r[e]}return o},commitUpdate:(e,t)=>{for(const[n,r]of Object.entries(t))"children"!==n&&("style"===n?a.setStyle(e,r):"internal_transform"===n?e.internal_transform=r:"internal_static"===n?e.internal_static=!0:a.setAttribute(e,n,r))},commitTextUpdate:(e,t,n)=>{a.setTextNodeValue(e,n)},removeChild:(e,t)=>{a.removeChildNode(e,t),l(t.yogaNode)}})},4907:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(4097)),o=r(n(9902));t.default=(e,t,n,r)=>{if("string"==typeof n.style.borderStyle){const u=n.yogaNode.getComputedWidth(),a=n.yogaNode.getComputedHeight(),l=n.style.borderColor,s=i.default[n.style.borderStyle],c=o.default(s.topLeft+s.horizontal.repeat(u-2)+s.topRight,l,"foreground"),f=(o.default(s.vertical,l,"foreground")+"\n").repeat(a-2),d=o.default(s.bottomLeft+s.horizontal.repeat(u-2)+s.bottomRight,l,"foreground");r.write(e,t,c,{transformers:[]}),r.write(e,t+1,f,{transformers:[]}),r.write(e+u-1,t+1,f,{transformers:[]}),r.write(e,t+a-1,d,{transformers:[]})}}},3782:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(6401)),o=r(n(8949)),u=r(n(9646)),a=r(n(2030)),l=r(n(317)),s=r(n(9099)),c=r(n(4907)),f=(e,t,n)=>{var r;const{offsetX:d=0,offsetY:p=0,transformers:h=[],skipStaticElements:v}=n;if(v&&e.internal_static)return;const{yogaNode:m}=e;if(m){if(m.getDisplay()===i.default.DISPLAY_NONE)return;const n=d+m.getComputedLeft(),g=p+m.getComputedTop();let y=h;if("function"==typeof e.internal_transform&&(y=[e.internal_transform,...h]),"ink-text"===e.nodeName){let i=s.default(e);if(i.length>0){const s=o.default(i),c=l.default(m);if(s>c){const t=null!==(r=e.style.textWrap)&&void 0!==r?r:"wrap";i=a.default(i,c,t)}i=((e,t)=>{var n;const r=null===(n=e.childNodes[0])||void 0===n?void 0:n.yogaNode;if(r){const e=r.getComputedLeft(),n=r.getComputedTop();t="\n".repeat(n)+u.default(t,e)}return t})(e,i),t.write(n,g,i,{transformers:y})}return}if("ink-box"===e.nodeName&&c.default(n,g,e,t),"ink-root"===e.nodeName||"ink-box"===e.nodeName)for(const r of e.childNodes)f(r,t,{offsetX:n,offsetY:g,transformers:y,skipStaticElements:v})}};t.default=f},9417:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(3206)),o=r(n(9679)),u=n(2413);t.default=(e,t)=>{const n=Object.assign({stdout:process.stdout,stdin:process.stdin,stderr:process.stderr,debug:!1,exitOnCtrlC:!0,patchConsole:!0},a(t)),r=l(n.stdout,()=>new i.default(n));return r.render(e),{rerender:r.render,unmount:()=>r.unmount(),waitUntilExit:r.waitUntilExit,cleanup:()=>o.default.delete(n.stdout),clear:r.clear}};const a=(e={})=>e instanceof u.Stream?{stdout:e,stdin:process.stdin}:e,l=(e,t)=>{let n;return o.default.has(e)?n=o.default.get(e):(n=t(),o.default.set(e,n)),n}},5691:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(6401)),o=r(n(3782)),u=r(n(4110));t.default=(e,t)=>{var n;if(e.yogaNode.setWidth(t),e.yogaNode){e.yogaNode.calculateLayout(void 0,void 0,i.default.DIRECTION_LTR);const t=new u.default({width:e.yogaNode.getComputedWidth(),height:e.yogaNode.getComputedHeight()});let r;o.default(e,t,{skipStaticElements:!0}),(null===(n=e.staticNode)||void 0===n?void 0:n.yogaNode)&&(r=new u.default({width:e.staticNode.yogaNode.getComputedWidth(),height:e.staticNode.yogaNode.getComputedHeight()}),o.default(e.staticNode,r,{skipStaticElements:!1}));const{output:a,height:l}=t.get();return{output:a,outputHeight:l,staticOutput:r?r.get().output+"\n":""}}return{output:"",outputHeight:0,staticOutput:""}}},9099:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=e=>{let t="";if(e.childNodes.length>0)for(const r of e.childNodes){let e="";"#text"===r.nodeName?e=r.nodeValue:("ink-text"!==r.nodeName&&"ink-virtual-text"!==r.nodeName||(e=n(r)),e.length>0&&"function"==typeof r.internal_transform&&(e=r.internal_transform(e))),t+=e}return t};t.default=n},5809:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(6401));t.default=(e,t={})=>{((e,t)=>{"position"in t&&e.setPositionType("absolute"===t.position?i.default.POSITION_TYPE_ABSOLUTE:i.default.POSITION_TYPE_RELATIVE)})(e,t),((e,t)=>{"marginLeft"in t&&e.setMargin(i.default.EDGE_START,t.marginLeft||0),"marginRight"in t&&e.setMargin(i.default.EDGE_END,t.marginRight||0),"marginTop"in t&&e.setMargin(i.default.EDGE_TOP,t.marginTop||0),"marginBottom"in t&&e.setMargin(i.default.EDGE_BOTTOM,t.marginBottom||0)})(e,t),((e,t)=>{"paddingLeft"in t&&e.setPadding(i.default.EDGE_LEFT,t.paddingLeft||0),"paddingRight"in t&&e.setPadding(i.default.EDGE_RIGHT,t.paddingRight||0),"paddingTop"in t&&e.setPadding(i.default.EDGE_TOP,t.paddingTop||0),"paddingBottom"in t&&e.setPadding(i.default.EDGE_BOTTOM,t.paddingBottom||0)})(e,t),((e,t)=>{var n;"flexGrow"in t&&e.setFlexGrow(null!==(n=t.flexGrow)&&void 0!==n?n:0),"flexShrink"in t&&e.setFlexShrink("number"==typeof t.flexShrink?t.flexShrink:1),"flexDirection"in t&&("row"===t.flexDirection&&e.setFlexDirection(i.default.FLEX_DIRECTION_ROW),"row-reverse"===t.flexDirection&&e.setFlexDirection(i.default.FLEX_DIRECTION_ROW_REVERSE),"column"===t.flexDirection&&e.setFlexDirection(i.default.FLEX_DIRECTION_COLUMN),"column-reverse"===t.flexDirection&&e.setFlexDirection(i.default.FLEX_DIRECTION_COLUMN_REVERSE)),"flexBasis"in t&&("number"==typeof t.flexBasis?e.setFlexBasis(t.flexBasis):"string"==typeof t.flexBasis?e.setFlexBasisPercent(Number.parseInt(t.flexBasis,10)):e.setFlexBasis(NaN)),"alignItems"in t&&("stretch"!==t.alignItems&&t.alignItems||e.setAlignItems(i.default.ALIGN_STRETCH),"flex-start"===t.alignItems&&e.setAlignItems(i.default.ALIGN_FLEX_START),"center"===t.alignItems&&e.setAlignItems(i.default.ALIGN_CENTER),"flex-end"===t.alignItems&&e.setAlignItems(i.default.ALIGN_FLEX_END)),"alignSelf"in t&&("auto"!==t.alignSelf&&t.alignSelf||e.setAlignSelf(i.default.ALIGN_AUTO),"flex-start"===t.alignSelf&&e.setAlignSelf(i.default.ALIGN_FLEX_START),"center"===t.alignSelf&&e.setAlignSelf(i.default.ALIGN_CENTER),"flex-end"===t.alignSelf&&e.setAlignSelf(i.default.ALIGN_FLEX_END)),"justifyContent"in t&&("flex-start"!==t.justifyContent&&t.justifyContent||e.setJustifyContent(i.default.JUSTIFY_FLEX_START),"center"===t.justifyContent&&e.setJustifyContent(i.default.JUSTIFY_CENTER),"flex-end"===t.justifyContent&&e.setJustifyContent(i.default.JUSTIFY_FLEX_END),"space-between"===t.justifyContent&&e.setJustifyContent(i.default.JUSTIFY_SPACE_BETWEEN),"space-around"===t.justifyContent&&e.setJustifyContent(i.default.JUSTIFY_SPACE_AROUND))})(e,t),((e,t)=>{var n,r;"width"in t&&("number"==typeof t.width?e.setWidth(t.width):"string"==typeof t.width?e.setWidthPercent(Number.parseInt(t.width,10)):e.setWidthAuto()),"height"in t&&("number"==typeof t.height?e.setHeight(t.height):"string"==typeof t.height?e.setHeightPercent(Number.parseInt(t.height,10)):e.setHeightAuto()),"minWidth"in t&&("string"==typeof t.minWidth?e.setMinWidthPercent(Number.parseInt(t.minWidth,10)):e.setMinWidth(null!==(n=t.minWidth)&&void 0!==n?n:0)),"minHeight"in t&&("string"==typeof t.minHeight?e.setMinHeightPercent(Number.parseInt(t.minHeight,10)):e.setMinHeight(null!==(r=t.minHeight)&&void 0!==r?r:0))})(e,t),((e,t)=>{"display"in t&&e.setDisplay("flex"===t.display?i.default.DISPLAY_FLEX:i.default.DISPLAY_NONE)})(e,t),((e,t)=>{if("borderStyle"in t){const n="string"==typeof t.borderStyle?1:0;e.setBorder(i.default.EDGE_TOP,n),e.setBorder(i.default.EDGE_BOTTOM,n),e.setBorder(i.default.EDGE_LEFT,n),e.setBorder(i.default.EDGE_RIGHT,n)}})(e,t)}},2030:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(n(4332)),o=r(n(5301)),u={};t.default=(e,t,n)=>{const r=e+String(t)+String(n);if(u[r])return u[r];let a=e;if("wrap"===n&&(a=i.default(e,t,{trim:!1,hard:!0})),n.startsWith("truncate")){let r="end";"truncate-middle"===n&&(r="middle"),"truncate-start"===n&&(r="start"),a=o.default(e,t,{position:r})}return u[r]=a,a}},5767:(e,t,n)=>{
-/** @license React v0.24.0
- * react-reconciler.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-e.exports=function t(r){"use strict";var i=n(9381),o=n(7382),u=n(7181);function a(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;nOe||(e.current=Ae[Oe],Ae[Oe]=null,Oe--)}function Ie(e,t){Oe++,Ae[Oe]=e.current,e.current=t}var Ne={},Me={current:Ne},Re={current:!1},Fe=Ne;function Le(e,t){var n=e.type.contextTypes;if(!n)return Ne;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var i,o={};for(i in n)o[i]=t[i];return r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function Be(e){return null!=(e=e.childContextTypes)}function je(e){Pe(Re),Pe(Me)}function Ue(e){Pe(Re),Pe(Me)}function ze(e,t,n){if(Me.current!==Ne)throw Error(a(168));Ie(Me,t),Ie(Re,n)}function We(e,t,n){var r=e.stateNode;if(e=t.childContextTypes,"function"!=typeof r.getChildContext)return n;for(var o in r=r.getChildContext())if(!(o in e))throw Error(a(108,C(t)||"Unknown",o));return i({},n,{},r)}function He(e){var t=e.stateNode;return t=t&&t.__reactInternalMemoizedMergedChildContext||Ne,Fe=Me.current,Ie(Me,t),Ie(Re,Re.current),!0}function Ve(e,t,n){var r=e.stateNode;if(!r)throw Error(a(169));n?(t=We(e,t,Fe),r.__reactInternalMemoizedMergedChildContext=t,Pe(Re),Pe(Me),Ie(Me,t)):Pe(Re),Ie(Re,n)}var qe=u.unstable_runWithPriority,Ge=u.unstable_scheduleCallback,$e=u.unstable_cancelCallback,Ye=u.unstable_shouldYield,Ke=u.unstable_requestPaint,Xe=u.unstable_now,Qe=u.unstable_getCurrentPriorityLevel,Je=u.unstable_ImmediatePriority,Ze=u.unstable_UserBlockingPriority,et=u.unstable_NormalPriority,tt=u.unstable_LowPriority,nt=u.unstable_IdlePriority,rt={},it=void 0!==Ke?Ke:function(){},ot=null,ut=null,at=!1,lt=Xe(),st=1e4>lt?Xe:function(){return Xe()-lt};function ct(){switch(Qe()){case Je:return 99;case Ze:return 98;case et:return 97;case tt:return 96;case nt:return 95;default:throw Error(a(332))}}function ft(e){switch(e){case 99:return Je;case 98:return Ze;case 97:return et;case 96:return tt;case 95:return nt;default:throw Error(a(332))}}function dt(e,t){return e=ft(e),qe(e,t)}function pt(e,t,n){return e=ft(e),Ge(e,t,n)}function ht(e){return null===ot?(ot=[e],ut=Ge(Je,mt)):ot.push(e),rt}function vt(){if(null!==ut){var e=ut;ut=null,$e(e)}mt()}function mt(){if(!at&&null!==ot){at=!0;var e=0;try{var t=ot;dt(99,(function(){for(;e=t&&(dr=!0),e.firstContext=null)}function It(e,t){if(kt!==e&&!1!==t&&0!==t)if("number"==typeof t&&1073741823!==t||(kt=e,t=1073741823),t={context:e,observedBits:t,next:null},null===Ct){if(null===St)throw Error(a(308));Ct=t,St.dependencies={expirationTime:0,firstContext:t,responders:null}}else Ct=Ct.next=t;return q?e._currentValue:e._currentValue2}var Nt=!1;function Mt(e){return{baseState:e,firstUpdate:null,lastUpdate:null,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function Rt(e){return{baseState:e.baseState,firstUpdate:e.firstUpdate,lastUpdate:e.lastUpdate,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function Ft(e,t){return{expirationTime:e,suspenseConfig:t,tag:0,payload:null,callback:null,next:null,nextEffect:null}}function Lt(e,t){null===e.lastUpdate?e.firstUpdate=e.lastUpdate=t:(e.lastUpdate.next=t,e.lastUpdate=t)}function Bt(e,t){var n=e.alternate;if(null===n){var r=e.updateQueue,i=null;null===r&&(r=e.updateQueue=Mt(e.memoizedState))}else r=e.updateQueue,i=n.updateQueue,null===r?null===i?(r=e.updateQueue=Mt(e.memoizedState),i=n.updateQueue=Mt(n.memoizedState)):r=e.updateQueue=Rt(i):null===i&&(i=n.updateQueue=Rt(r));null===i||r===i?Lt(r,t):null===r.lastUpdate||null===i.lastUpdate?(Lt(r,t),Lt(i,t)):(Lt(r,t),i.lastUpdate=t)}function jt(e,t){var n=e.updateQueue;null===(n=null===n?e.updateQueue=Mt(e.memoizedState):Ut(e,n)).lastCapturedUpdate?n.firstCapturedUpdate=n.lastCapturedUpdate=t:(n.lastCapturedUpdate.next=t,n.lastCapturedUpdate=t)}function Ut(e,t){var n=e.alternate;return null!==n&&t===n.updateQueue&&(t=e.updateQueue=Rt(t)),t}function zt(e,t,n,r,o,u){switch(n.tag){case 1:return"function"==typeof(e=n.payload)?e.call(u,r,o):e;case 3:e.effectTag=-4097&e.effectTag|64;case 0:if(null==(o="function"==typeof(e=n.payload)?e.call(u,r,o):e))break;return i({},r,o);case 2:Nt=!0}return r}function Wt(e,t,n,r,i){Nt=!1;for(var o=(t=Ut(e,t)).baseState,u=null,a=0,l=t.firstUpdate,s=o;null!==l;){var c=l.expirationTime;cd?(p=f,f=null):p=f.sibling;var h=m(i,f,a[d],l);if(null===h){null===f&&(f=p);break}e&&f&&null===h.alternate&&t(i,f),u=o(h,u,d),null===c?s=h:c.sibling=h,c=h,f=p}if(d===a.length)return n(i,f),s;if(null===f){for(;dp?(h=d,d=null):h=d.sibling;var _=m(i,d,y.value,s);if(null===_){null===d&&(d=h);break}e&&d&&null===_.alternate&&t(i,d),u=o(_,u,p),null===f?c=_:f.sibling=_,f=_,d=h}if(y.done)return n(i,d),c;if(null===d){for(;!y.done;p++,y=l.next())null!==(y=v(i,y.value,s))&&(u=o(y,u,p),null===f?c=y:f.sibling=y,f=y);return c}for(d=r(i,d);!y.done;p++,y=l.next())null!==(y=g(d,i,p,y.value,s))&&(e&&null!==y.alternate&&d.delete(null===y.key?p:y.key),u=o(y,u,p),null===f?c=y:f.sibling=y,f=y);return e&&d.forEach((function(e){return t(i,e)})),c}return function(e,r,o,l){var s="object"==typeof o&&null!==o&&o.type===d&&null===o.key;s&&(o=o.props.children);var p="object"==typeof o&&null!==o;if(p)switch(o.$$typeof){case c:e:{for(p=o.key,s=r;null!==s;){if(s.key===p){if(7===s.tag?o.type===d:s.elementType===o.type){n(e,s.sibling),(r=i(s,o.type===d?o.props.children:o.props)).ref=en(e,s,o),r.return=e,e=r;break e}n(e,s);break}t(e,s),s=s.sibling}o.type===d?((r=so(o.props.children,e.mode,l,o.key)).return=e,e=r):((l=lo(o.type,o.key,o.props,null,e.mode,l)).ref=en(e,r,o),l.return=e,e=l)}return u(e);case f:e:{for(s=o.key;null!==r;){if(r.key===s){if(4===r.tag&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),(r=i(r,o.children||[])).return=e,e=r;break e}n(e,r);break}t(e,r),r=r.sibling}(r=fo(o,e.mode,l)).return=e,e=r}return u(e)}if("string"==typeof o||"number"==typeof o)return o=""+o,null!==r&&6===r.tag?(n(e,r.sibling),(r=i(r,o)).return=e,e=r):(n(e,r),(r=co(o,e.mode,l)).return=e,e=r),u(e);if(Zt(o))return y(e,r,o,l);if(S(o))return _(e,r,o,l);if(p&&tn(e,o),void 0===o&&!s)switch(e.tag){case 1:case 0:throw e=e.type,Error(a(152,e.displayName||e.name||"Component"))}return n(e,r)}}var rn=nn(!0),on=nn(!1),un={},an={current:un},ln={current:un},sn={current:un};function cn(e){if(e===un)throw Error(a(174));return e}function fn(e,t){Ie(sn,t),Ie(ln,e),Ie(an,un),t=P(t),Pe(an),Ie(an,t)}function dn(e){Pe(an),Pe(ln),Pe(sn)}function pn(e){var t=cn(sn.current),n=cn(an.current);n!==(t=I(n,e.type,t))&&(Ie(ln,e),Ie(an,t))}function hn(e){ln.current===e&&(Pe(an),Pe(ln))}var vn={current:0};function mn(e){for(var t=e;null!==t;){if(13===t.tag){var n=t.memoizedState;if(null!==n&&(null===(n=n.dehydrated)||ye(n)||_e(n)))return t}else if(19===t.tag&&void 0!==t.memoizedProps.revealOrder){if(0!=(64&t.effectTag))return t}else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function gn(e,t){return{responder:e,props:t}}var yn=l.ReactCurrentDispatcher,_n=l.ReactCurrentBatchConfig,bn=0,wn=null,En=null,Dn=null,Sn=null,Cn=null,kn=null,Tn=0,xn=null,An=0,On=!1,Pn=null,In=0;function Nn(){throw Error(a(321))}function Mn(e,t){if(null===t)return!1;for(var n=0;nTn&&zi(Tn=f)):(Ui(f,s.suspenseConfig),o=s.eagerReducer===e?s.eagerState:e(o,s.action)),u=s,s=s.next}while(null!==s&&s!==r);c||(l=u,i=o),_t(o,t.memoizedState)||(dr=!0),t.memoizedState=o,t.baseUpdate=l,t.baseState=i,n.lastRenderedState=o}return[t.memoizedState,n.dispatch]}function zn(e){var t=Ln();return"function"==typeof e&&(e=e()),t.memoizedState=t.baseState=e,e=(e=t.queue={last:null,dispatch:null,lastRenderedReducer:jn,lastRenderedState:e}).dispatch=Jn.bind(null,wn,e),[t.memoizedState,e]}function Wn(e){return Un(jn)}function Hn(e,t,n,r){return e={tag:e,create:t,destroy:n,deps:r,next:null},null===xn?(xn={lastEffect:null}).lastEffect=e.next=e:null===(t=xn.lastEffect)?xn.lastEffect=e.next=e:(n=t.next,t.next=e,e.next=n,xn.lastEffect=e),e}function Vn(e,t,n,r){var i=Ln();An|=e,i.memoizedState=Hn(t,n,void 0,void 0===r?null:r)}function qn(e,t,n,r){var i=Bn();r=void 0===r?null:r;var o=void 0;if(null!==En){var u=En.memoizedState;if(o=u.destroy,null!==r&&Mn(r,u.deps))return void Hn(0,n,o,r)}An|=e,i.memoizedState=Hn(t,n,o,r)}function Gn(e,t){return Vn(516,192,e,t)}function $n(e,t){return qn(516,192,e,t)}function Yn(e,t){return"function"==typeof t?(e=e(),t(e),function(){t(null)}):null!=t?(e=e(),t.current=e,function(){t.current=null}):void 0}function Kn(){}function Xn(e,t){return Ln().memoizedState=[e,void 0===t?null:t],e}function Qn(e,t){var n=Bn();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&Mn(t,r[1])?r[0]:(n.memoizedState=[e,t],e)}function Jn(e,t,n){if(!(25>In))throw Error(a(301));var r=e.alternate;if(e===wn||null!==r&&r===wn)if(On=!0,e={expirationTime:bn,suspenseConfig:null,action:n,eagerReducer:null,eagerState:null,next:null},null===Pn&&(Pn=new Map),void 0===(n=Pn.get(t)))Pn.set(t,e);else{for(t=n;null!==t.next;)t=t.next;t.next=e}else{var i=xi(),o=qt.suspense;o={expirationTime:i=Ai(i,e,o),suspenseConfig:o,action:n,eagerReducer:null,eagerState:null,next:null};var u=t.last;if(null===u)o.next=o;else{var l=u.next;null!==l&&(o.next=l),u.next=o}if(t.last=o,0===e.expirationTime&&(null===r||0===r.expirationTime)&&null!==(r=t.lastRenderedReducer))try{var s=t.lastRenderedState,c=r(s,n);if(o.eagerReducer=r,o.eagerState=c,_t(c,s))return}catch(e){}Oi(e,i)}}var Zn={readContext:It,useCallback:Nn,useContext:Nn,useEffect:Nn,useImperativeHandle:Nn,useLayoutEffect:Nn,useMemo:Nn,useReducer:Nn,useRef:Nn,useState:Nn,useDebugValue:Nn,useResponder:Nn,useDeferredValue:Nn,useTransition:Nn},er={readContext:It,useCallback:Xn,useContext:It,useEffect:Gn,useImperativeHandle:function(e,t,n){return n=null!=n?n.concat([e]):null,Vn(4,36,Yn.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Vn(4,36,e,t)},useMemo:function(e,t){var n=Ln();return t=void 0===t?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Ln();return t=void 0!==n?n(t):t,r.memoizedState=r.baseState=t,e=(e=r.queue={last:null,dispatch:null,lastRenderedReducer:e,lastRenderedState:t}).dispatch=Jn.bind(null,wn,e),[r.memoizedState,e]},useRef:function(e){return e={current:e},Ln().memoizedState=e},useState:zn,useDebugValue:Kn,useResponder:gn,useDeferredValue:function(e,t){var n=zn(e),r=n[0],i=n[1];return Gn((function(){u.unstable_next((function(){var n=_n.suspense;_n.suspense=void 0===t?null:t;try{i(e)}finally{_n.suspense=n}}))}),[e,t]),r},useTransition:function(e){var t=zn(!1),n=t[0],r=t[1];return[Xn((function(t){r(!0),u.unstable_next((function(){var n=_n.suspense;_n.suspense=void 0===e?null:e;try{r(!1),t()}finally{_n.suspense=n}}))}),[e,n]),n]}},tr={readContext:It,useCallback:Qn,useContext:It,useEffect:$n,useImperativeHandle:function(e,t,n){return n=null!=n?n.concat([e]):null,qn(4,36,Yn.bind(null,t,e),n)},useLayoutEffect:function(e,t){return qn(4,36,e,t)},useMemo:function(e,t){var n=Bn();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&Mn(t,r[1])?r[0]:(e=e(),n.memoizedState=[e,t],e)},useReducer:Un,useRef:function(){return Bn().memoizedState},useState:Wn,useDebugValue:Kn,useResponder:gn,useDeferredValue:function(e,t){var n=Wn(),r=n[0],i=n[1];return $n((function(){u.unstable_next((function(){var n=_n.suspense;_n.suspense=void 0===t?null:t;try{i(e)}finally{_n.suspense=n}}))}),[e,t]),r},useTransition:function(e){var t=Wn(),n=t[0],r=t[1];return[Qn((function(t){r(!0),u.unstable_next((function(){var n=_n.suspense;_n.suspense=void 0===e?null:e;try{r(!1),t()}finally{_n.suspense=n}}))}),[e,n]),n]}},nr=null,rr=null,ir=!1;function or(e,t){var n=oo(5,null,null,0);n.elementType="DELETED",n.type="DELETED",n.stateNode=t,n.return=e,n.effectTag=8,null!==e.lastEffect?(e.lastEffect.nextEffect=n,e.lastEffect=n):e.firstEffect=e.lastEffect=n}function ur(e,t){switch(e.tag){case 5:return null!==(t=me(t,e.type,e.pendingProps))&&(e.stateNode=t,!0);case 6:return null!==(t=ge(t,e.pendingProps))&&(e.stateNode=t,!0);case 13:default:return!1}}function ar(e){if(ir){var t=rr;if(t){var n=t;if(!ur(e,t)){if(!(t=be(n))||!ur(e,t))return e.effectTag=-1025&e.effectTag|2,ir=!1,void(nr=e);or(nr,n)}nr=e,rr=we(t)}else e.effectTag=-1025&e.effectTag|2,ir=!1,nr=e}}function lr(e){for(e=e.return;null!==e&&5!==e.tag&&3!==e.tag&&13!==e.tag;)e=e.return;nr=e}function sr(e){if(!Y||e!==nr)return!1;if(!ir)return lr(e),ir=!0,!1;var t=e.type;if(5!==e.tag||"head"!==t&&"body"!==t&&!j(t,e.memoizedProps))for(t=rr;t;)or(e,t),t=be(t);if(lr(e),13===e.tag){if(!Y)throw Error(a(316));if(!(e=null!==(e=e.memoizedState)?e.dehydrated:null))throw Error(a(317));rr=Se(e)}else rr=nr?be(e.stateNode):null;return!0}function cr(){Y&&(rr=nr=null,ir=!1)}var fr=l.ReactCurrentOwner,dr=!1;function pr(e,t,n,r){t.child=null===e?on(t,null,n,r):rn(t,e.child,n,r)}function hr(e,t,n,r,i){n=n.render;var o=t.ref;return Pt(t,i),r=Rn(e,t,n,r,o,i),null===e||dr?(t.effectTag|=1,pr(e,t,r,i),t.child):(t.updateQueue=e.updateQueue,t.effectTag&=-517,e.expirationTime<=i&&(e.expirationTime=0),Pr(e,t,i))}function vr(e,t,n,r,i,o){if(null===e){var u=n.type;return"function"!=typeof u||uo(u)||void 0!==u.defaultProps||null!==n.compare||void 0!==n.defaultProps?((e=lo(n.type,null,r,null,t.mode,o)).ref=t.ref,e.return=t,t.child=e):(t.tag=15,t.type=u,mr(e,t,u,r,i,o))}return u=e.child,it)&&Si.set(e,t))}}function Pi(e,t){e.expirationTime(e=e.nextKnownPendingLevel)?t:e:t}function Ni(e){if(0!==e.lastExpiredTime)e.callbackExpirationTime=1073741823,e.callbackPriority=99,e.callbackNode=ht(Ri.bind(null,e));else{var t=Ii(e),n=e.callbackNode;if(0===t)null!==n&&(e.callbackNode=null,e.callbackExpirationTime=0,e.callbackPriority=90);else{var r=xi();if(1073741823===t?r=99:1===t||2===t?r=95:r=0>=(r=10*(1073741821-t)-10*(1073741821-r))?99:250>=r?98:5250>=r?97:95,null!==n){var i=e.callbackPriority;if(e.callbackExpirationTime===t&&i>=r)return;n!==rt&&$e(n)}e.callbackExpirationTime=t,e.callbackPriority=r,t=1073741823===t?ht(Ri.bind(null,e)):pt(r,Mi.bind(null,e),{timeout:10*(1073741821-t)-st()}),e.callbackNode=t}}}function Mi(e,t){if(Ti=0,t)return go(e,t=xi()),Ni(e),null;var n=Ii(e);if(0!==n){if(t=e.callbackNode,0!=(48&oi))throw Error(a(327));if(Xi(),e===ui&&n===li||Li(e,n),null!==ai){var r=oi;oi|=ii;for(var i=ji();;)try{Hi();break}catch(t){Bi(e,t)}if(Tt(),oi=r,ni.current=i,1===si)throw t=ci,Li(e,n),vo(e,n),Ni(e),t;if(null===ai)switch(i=e.finishedWork=e.current.alternate,e.finishedExpirationTime=n,r=si,ui=null,r){case 0:case 1:throw Error(a(345));case 2:go(e,2=n){e.lastPingedTime=n,Li(e,n);break}}if(0!==(o=Ii(e))&&o!==n)break;if(0!==r&&r!==n){e.lastPingedTime=r;break}e.timeoutHandle=W($i.bind(null,e),i);break}$i(e);break;case 4:if(vo(e,n),n===(r=e.lastSuspendedTime)&&(e.nextKnownPendingLevel=Gi(i)),vi&&(0===(i=e.lastPingedTime)||i>=n)){e.lastPingedTime=n,Li(e,n);break}if(0!==(i=Ii(e))&&i!==n)break;if(0!==r&&r!==n){e.lastPingedTime=r;break}if(1073741823!==di?r=10*(1073741821-di)-st():1073741823===fi?r=0:(r=10*(1073741821-fi)-5e3,0>(r=(i=st())-r)&&(r=0),(n=10*(1073741821-n)-i)<(r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*ti(r/1960))-r)&&(r=n)),10=(r=0|u.busyMinDurationMs)?r=0:(i=0|u.busyDelayMs,r=(o=st()-(10*(1073741821-o)-(0|u.timeoutMs||5e3)))<=i?0:i+r-o),10 component higher in the tree to provide a loading indicator or placeholder to display."+xe(i))}5!==si&&(si=2),o=Fr(o,i),l=r;do{switch(l.tag){case 3:u=o,l.effectTag|=4096,l.expirationTime=t,jt(l,Jr(l,u,t));break e;case 1:u=o;var g=l.type,y=l.stateNode;if(0==(64&l.effectTag)&&("function"==typeof g.getDerivedStateFromError||null!==y&&"function"==typeof y.componentDidCatch&&(null===bi||!bi.has(y)))){l.effectTag|=4096,l.expirationTime=t,jt(l,Zr(l,u,t));break e}}l=l.return}while(null!==l)}ai=qi(ai)}catch(e){t=e;continue}break}}function ji(){var e=ni.current;return ni.current=Zn,null===e?Zn:e}function Ui(e,t){ehi&&(hi=e)}function Wi(){for(;null!==ai;)ai=Vi(ai)}function Hi(){for(;null!==ai&&!Ye();)ai=Vi(ai)}function Vi(e){var t=ei(e.alternate,e,li);return e.memoizedProps=e.pendingProps,null===t&&(t=qi(e)),ri.current=null,t}function qi(e){ai=e;do{var t=ai.alternate;if(e=ai.return,0==(2048&ai.effectTag)){e:{var n=t,r=li,i=(t=ai).pendingProps;switch(t.tag){case 2:case 16:break;case 15:case 0:break;case 1:Be(t.type)&&je();break;case 3:dn(),Ue(),(i=t.stateNode).pendingContext&&(i.context=i.pendingContext,i.pendingContext=null),(null===n||null===n.child)&&sr(t)&&Ir(t),Dr(t);break;case 5:hn(t);var o=cn(sn.current);if(r=t.type,null!==n&&null!=t.stateNode)Sr(n,t,r,i,o),n.ref!==t.ref&&(t.effectTag|=128);else if(i){if(n=cn(an.current),sr(t)){if(i=t,!Y)throw Error(a(175));n=Ee(i.stateNode,i.type,i.memoizedProps,o,n,i),i.updateQueue=n,(n=null!==n)&&Ir(t)}else{var u=R(r,i,o,n,t);Er(u,t,!1,!1),t.stateNode=u,L(u,r,i,o,n)&&Ir(t)}null!==t.ref&&(t.effectTag|=128)}else if(null===t.stateNode)throw Error(a(166));break;case 6:if(n&&null!=t.stateNode)Cr(n,t,n.memoizedProps,i);else{if("string"!=typeof i&&null===t.stateNode)throw Error(a(166));if(n=cn(sn.current),o=cn(an.current),sr(t)){if(n=t,!Y)throw Error(a(176));(n=De(n.stateNode,n.memoizedProps,n))&&Ir(t)}else t.stateNode=z(i,n,o,t)}break;case 11:break;case 13:if(Pe(vn),i=t.memoizedState,0!=(64&t.effectTag)){t.expirationTime=r;break e}i=null!==i,o=!1,null===n?void 0!==t.memoizedProps.fallback&&sr(t):(o=null!==(r=n.memoizedState),i||null===r||null!==(r=n.child.sibling)&&(null!==(u=t.firstEffect)?(t.firstEffect=r,r.nextEffect=u):(t.firstEffect=t.lastEffect=r,r.nextEffect=null),r.effectTag=8)),i&&!o&&0!=(2&t.mode)&&(null===n&&!0!==t.memoizedProps.unstable_avoidThisFallback||0!=(1&vn.current)?0===si&&(si=3):(0!==si&&3!==si||(si=4),0!==hi&&null!==ui&&(vo(ui,li),mo(ui,hi)))),$&&i&&(t.effectTag|=4),G&&(i||o)&&(t.effectTag|=4);break;case 7:case 8:case 12:break;case 4:dn(),Dr(t);break;case 10:At(t);break;case 9:case 14:break;case 17:Be(t.type)&&je();break;case 19:if(Pe(vn),null===(i=t.memoizedState))break;if(o=0!=(64&t.effectTag),null===(u=i.rendering)){if(o)Mr(i,!1);else if(0!==si||null!==n&&0!=(64&n.effectTag))for(n=t.child;null!==n;){if(null!==(u=mn(n))){for(t.effectTag|=64,Mr(i,!1),null!==(n=u.updateQueue)&&(t.updateQueue=n,t.effectTag|=4),null===i.lastEffect&&(t.firstEffect=null),t.lastEffect=i.lastEffect,n=r,i=t.child;null!==i;)r=n,(o=i).effectTag&=2,o.nextEffect=null,o.firstEffect=null,o.lastEffect=null,null===(u=o.alternate)?(o.childExpirationTime=0,o.expirationTime=r,o.child=null,o.memoizedProps=null,o.memoizedState=null,o.updateQueue=null,o.dependencies=null):(o.childExpirationTime=u.childExpirationTime,o.expirationTime=u.expirationTime,o.child=u.child,o.memoizedProps=u.memoizedProps,o.memoizedState=u.memoizedState,o.updateQueue=u.updateQueue,r=u.dependencies,o.dependencies=null===r?null:{expirationTime:r.expirationTime,firstContext:r.firstContext,responders:r.responders}),i=i.sibling;Ie(vn,1&vn.current|2),t=t.child;break e}n=n.sibling}}else{if(!o)if(null!==(n=mn(u))){if(t.effectTag|=64,o=!0,null!==(n=n.updateQueue)&&(t.updateQueue=n,t.effectTag|=4),Mr(i,!0),null===i.tail&&"hidden"===i.tailMode&&!u.alternate){null!==(t=t.lastEffect=i.lastEffect)&&(t.nextEffect=null);break}}else st()>i.tailExpiration&&1i&&(i=r),(u=o.childExpirationTime)>i&&(i=u),o=o.sibling;n.childExpirationTime=i}if(null!==t)return t;null!==e&&0==(2048&e.effectTag)&&(null===e.firstEffect&&(e.firstEffect=ai.firstEffect),null!==ai.lastEffect&&(null!==e.lastEffect&&(e.lastEffect.nextEffect=ai.firstEffect),e.lastEffect=ai.lastEffect),1(e=e.childExpirationTime)?t:e}function $i(e){var t=ct();return dt(99,Yi.bind(null,e,t)),null}function Yi(e,t){do{Xi()}while(null!==Ei);if(0!=(48&oi))throw Error(a(327));var n=e.finishedWork,r=e.finishedExpirationTime;if(null===n)return null;if(e.finishedWork=null,e.finishedExpirationTime=0,n===e.current)throw Error(a(177));e.callbackNode=null,e.callbackExpirationTime=0,e.callbackPriority=90,e.nextKnownPendingLevel=0;var i=Gi(n);if(e.firstPendingTime=i,r<=e.lastSuspendedTime?e.firstSuspendedTime=e.lastSuspendedTime=e.nextKnownPendingLevel=0:r<=e.firstSuspendedTime&&(e.firstSuspendedTime=r-1),r<=e.lastPingedTime&&(e.lastPingedTime=0),r<=e.lastExpiredTime&&(e.lastExpiredTime=0),e===ui&&(ai=ui=null,li=0),1=n?Tr(e,t,n):(Ie(vn,1&vn.current),null!==(t=Pr(e,t,n))?t.sibling:null);Ie(vn,1&vn.current);break;case 19:if(r=t.childExpirationTime>=n,0!=(64&e.effectTag)){if(r)return Or(e,t,n);t.effectTag|=64}if(null!==(i=t.memoizedState)&&(i.rendering=null,i.tail=null),Ie(vn,vn.current),!r)return null}return Pr(e,t,n)}dr=!1}}else dr=!1;switch(t.expirationTime=0,t.tag){case 2:if(r=t.type,null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),e=t.pendingProps,i=Le(t,Me.current),Pt(t,n),i=Rn(null,t,r,e,i,n),t.effectTag|=1,"object"==typeof i&&null!==i&&"function"==typeof i.render&&void 0===i.$$typeof){if(t.tag=1,Fn(),Be(r)){var o=!0;He(t)}else o=!1;t.memoizedState=null!==i.state&&void 0!==i.state?i.state:null;var u=r.getDerivedStateFromProps;"function"==typeof u&&$t(t,r,u,e),i.updater=Yt,t.stateNode=i,i._reactInternalFiber=t,Jt(t,r,e,n),t=br(null,t,r,!0,o,n)}else t.tag=0,pr(null,t,i,n),t=t.child;return t;case 16:if(i=t.elementType,null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),e=t.pendingProps,function(e){if(-1===e._status){e._status=0;var t=e._ctor;t=t(),e._result=t,t.then((function(t){0===e._status&&(t=t.default,e._status=1,e._result=t)}),(function(t){0===e._status&&(e._status=2,e._result=t)}))}}(i),1!==i._status)throw i._result;switch(i=i._result,t.type=i,o=t.tag=function(e){if("function"==typeof e)return uo(e)?1:0;if(null!=e){if((e=e.$$typeof)===y)return 11;if(e===w)return 14}return 2}(i),e=Et(i,e),o){case 0:t=yr(null,t,i,e,n);break;case 1:t=_r(null,t,i,e,n);break;case 11:t=hr(null,t,i,e,n);break;case 14:t=vr(null,t,i,Et(i.type,e),r,n);break;default:throw Error(a(306,i,""))}return t;case 0:return r=t.type,i=t.pendingProps,yr(e,t,r,i=t.elementType===r?i:Et(r,i),n);case 1:return r=t.type,i=t.pendingProps,_r(e,t,r,i=t.elementType===r?i:Et(r,i),n);case 3:if(wr(t),null===(r=t.updateQueue))throw Error(a(282));if(i=null!==(i=t.memoizedState)?i.element:null,Wt(t,r,t.pendingProps,null,n),(r=t.memoizedState.element)===i)cr(),t=Pr(e,t,n);else{if((i=t.stateNode.hydrate)&&(Y?(rr=we(t.stateNode.containerInfo),nr=t,i=ir=!0):i=!1),i)for(n=on(t,null,r,n),t.child=n;n;)n.effectTag=-3&n.effectTag|1024,n=n.sibling;else pr(e,t,r,n),cr();t=t.child}return t;case 5:return pn(t),null===e&&ar(t),r=t.type,i=t.pendingProps,o=null!==e?e.memoizedProps:null,u=i.children,j(r,i)?u=null:null!==o&&j(r,o)&&(t.effectTag|=16),gr(e,t),4&t.mode&&1!==n&&U(r,i)?(t.expirationTime=t.childExpirationTime=1,t=null):(pr(e,t,u,n),t=t.child),t;case 6:return null===e&&ar(t),null;case 13:return Tr(e,t,n);case 4:return fn(t,t.stateNode.containerInfo),r=t.pendingProps,null===e?t.child=rn(t,null,r,n):pr(e,t,r,n),t.child;case 11:return r=t.type,i=t.pendingProps,hr(e,t,r,i=t.elementType===r?i:Et(r,i),n);case 7:return pr(e,t,t.pendingProps,n),t.child;case 8:case 12:return pr(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,i=t.pendingProps,u=t.memoizedProps,xt(t,o=i.value),null!==u){var l=u.value;if(0===(o=_t(l,o)?0:0|("function"==typeof r._calculateChangedBits?r._calculateChangedBits(l,o):1073741823))){if(u.children===i.children&&!Re.current){t=Pr(e,t,n);break e}}else for(null!==(l=t.child)&&(l.return=t);null!==l;){var s=l.dependencies;if(null!==s){u=l.child;for(var c=s.firstContext;null!==c;){if(c.context===r&&0!=(c.observedBits&o)){1===l.tag&&((c=Ft(n,null)).tag=2,Bt(l,c)),l.expirationTime=t&&e<=t}function vo(e,t){var n=e.firstSuspendedTime,r=e.lastSuspendedTime;nt||0===n)&&(e.lastSuspendedTime=t),t<=e.lastPingedTime&&(e.lastPingedTime=0),t<=e.lastExpiredTime&&(e.lastExpiredTime=0)}function mo(e,t){t>e.firstPendingTime&&(e.firstPendingTime=t);var n=e.firstSuspendedTime;0!==n&&(t>=n?e.firstSuspendedTime=e.lastSuspendedTime=e.nextKnownPendingLevel=0:t>=e.lastSuspendedTime&&(e.lastSuspendedTime=t+1),t>e.nextKnownPendingLevel&&(e.nextKnownPendingLevel=t))}function go(e,t){var n=e.lastExpiredTime;(0===n||n>t)&&(e.lastExpiredTime=t)}function yo(e){var t=e._reactInternalFiber;if(void 0===t){if("function"==typeof e.render)throw Error(a(188));throw Error(a(268,Object.keys(e)))}return null===(e=A(t))?null:e.stateNode}function _o(e,t){null!==(e=e.memoizedState)&&null!==e.dehydrated&&e.retryTime{"use strict";e.exports=n(5767)},3296:(e,t,n)=>{"use strict";const r=n(5760);r.createWebSocketStream=n(6387),r.Server=n(43),r.Receiver=n(1762),r.Sender=n(9576),e.exports=r},8716:(e,t,n)=>{"use strict";const{EMPTY_BUFFER:r}=n(5739);function i(e,t){if(0===e.length)return r;if(1===e.length)return e[0];const n=Buffer.allocUnsafe(t);let i=0;for(let t=0;t{"use strict";e.exports={BINARY_TYPES:["nodebuffer","arraybuffer","fragments"],GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),EMPTY_BUFFER:Buffer.alloc(0),NOOP:()=>{}}},7002:e=>{"use strict";class t{constructor(e,t){this.target=t,this.type=e}}class n extends t{constructor(e,t){super("message",t),this.data=e}}class r extends t{constructor(e,t,n){super("close",n),this.wasClean=n._closeFrameReceived&&n._closeFrameSent,this.reason=t,this.code=e}}class i extends t{constructor(e){super("open",e)}}class o extends t{constructor(e,t){super("error",t),this.message=e.message,this.error=e}}const u={addEventListener(e,t,u){if("function"!=typeof t)return;function a(e){t.call(this,new n(e,this))}function l(e,n){t.call(this,new r(e,n,this))}function s(e){t.call(this,new o(e,this))}function c(){t.call(this,new i(this))}const f=u&&u.once?"once":"on";"message"===e?(a._listener=t,this[f](e,a)):"close"===e?(l._listener=t,this[f](e,l)):"error"===e?(s._listener=t,this[f](e,s)):"open"===e?(c._listener=t,this[f](e,c)):this[f](e,t)},removeEventListener(e,t){const n=this.listeners(e);for(let r=0;r{"use strict";const t=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0];function n(e,t,n){void 0===e[t]?e[t]=[n]:e[t].push(n)}e.exports={format:function(e){return Object.keys(e).map(t=>{let n=e[t];return Array.isArray(n)||(n=[n]),n.map(e=>[t].concat(Object.keys(e).map(t=>{let n=e[t];return Array.isArray(n)||(n=[n]),n.map(e=>!0===e?t:`${t}=${e}`).join("; ")})).join("; ")).join(", ")}).join(", ")},parse:function(e){const r=Object.create(null);if(void 0===e||""===e)return r;let i,o,u=Object.create(null),a=!1,l=!1,s=!1,c=-1,f=-1,d=0;for(;d{"use strict";const t=Symbol("kDone"),n=Symbol("kRun");e.exports=class{constructor(e){this[t]=()=>{this.pending--,this[n]()},this.concurrency=e||1/0,this.jobs=[],this.pending=0}add(e){this.jobs.push(e),this[n]()}[n](){if(this.pending!==this.concurrency&&this.jobs.length){const e=this.jobs.shift();this.pending++,e(this[t])}}}},2309:(e,t,n)=>{"use strict";const r=n(8761),i=n(8716),o=n(1390),{kStatusCode:u,NOOP:a}=n(5739),l=Buffer.from([0,0,255,255]),s=Symbol("permessage-deflate"),c=Symbol("total-length"),f=Symbol("callback"),d=Symbol("buffers"),p=Symbol("error");let h;function v(e){this[d].push(e),this[c]+=e.length}function m(e){this[c]+=e.length,this[s]._maxPayload<1||this[c]<=this[s]._maxPayload?this[d].push(e):(this[p]=new RangeError("Max payload size exceeded"),this[p][u]=1009,this.removeListener("data",m),this.reset())}function g(e){this[s]._inflate=null,e[u]=1007,this[f](e)}e.exports=class{constructor(e,t,n){if(this._maxPayload=0|n,this._options=e||{},this._threshold=void 0!==this._options.threshold?this._options.threshold:1024,this._isServer=!!t,this._deflate=null,this._inflate=null,this.params=null,!h){const e=void 0!==this._options.concurrencyLimit?this._options.concurrencyLimit:10;h=new o(e)}}static get extensionName(){return"permessage-deflate"}offer(){const e={};return this._options.serverNoContextTakeover&&(e.server_no_context_takeover=!0),this._options.clientNoContextTakeover&&(e.client_no_context_takeover=!0),this._options.serverMaxWindowBits&&(e.server_max_window_bits=this._options.serverMaxWindowBits),this._options.clientMaxWindowBits?e.client_max_window_bits=this._options.clientMaxWindowBits:null==this._options.clientMaxWindowBits&&(e.client_max_window_bits=!0),e}accept(e){return e=this.normalizeParams(e),this.params=this._isServer?this.acceptAsServer(e):this.acceptAsClient(e),this.params}cleanup(){if(this._inflate&&(this._inflate.close(),this._inflate=null),this._deflate){const e=this._deflate[f];this._deflate.close(),this._deflate=null,e&&e(new Error("The deflate stream was closed while data was being processed"))}}acceptAsServer(e){const t=this._options,n=e.find(e=>!(!1===t.serverNoContextTakeover&&e.server_no_context_takeover||e.server_max_window_bits&&(!1===t.serverMaxWindowBits||"number"==typeof t.serverMaxWindowBits&&t.serverMaxWindowBits>e.server_max_window_bits)||"number"==typeof t.clientMaxWindowBits&&!e.client_max_window_bits));if(!n)throw new Error("None of the extension offers can be accepted");return t.serverNoContextTakeover&&(n.server_no_context_takeover=!0),t.clientNoContextTakeover&&(n.client_no_context_takeover=!0),"number"==typeof t.serverMaxWindowBits&&(n.server_max_window_bits=t.serverMaxWindowBits),"number"==typeof t.clientMaxWindowBits?n.client_max_window_bits=t.clientMaxWindowBits:!0!==n.client_max_window_bits&&!1!==t.clientMaxWindowBits||delete n.client_max_window_bits,n}acceptAsClient(e){const t=e[0];if(!1===this._options.clientNoContextTakeover&&t.client_no_context_takeover)throw new Error('Unexpected parameter "client_no_context_takeover"');if(t.client_max_window_bits){if(!1===this._options.clientMaxWindowBits||"number"==typeof this._options.clientMaxWindowBits&&t.client_max_window_bits>this._options.clientMaxWindowBits)throw new Error('Unexpected or invalid parameter "client_max_window_bits"')}else"number"==typeof this._options.clientMaxWindowBits&&(t.client_max_window_bits=this._options.clientMaxWindowBits);return t}normalizeParams(e){return e.forEach(e=>{Object.keys(e).forEach(t=>{let n=e[t];if(n.length>1)throw new Error(`Parameter "${t}" must have only a single value`);if(n=n[0],"client_max_window_bits"===t){if(!0!==n){const e=+n;if(!Number.isInteger(e)||e<8||e>15)throw new TypeError(`Invalid value for parameter "${t}": ${n}`);n=e}else if(!this._isServer)throw new TypeError(`Invalid value for parameter "${t}": ${n}`)}else if("server_max_window_bits"===t){const e=+n;if(!Number.isInteger(e)||e<8||e>15)throw new TypeError(`Invalid value for parameter "${t}": ${n}`);n=e}else{if("client_no_context_takeover"!==t&&"server_no_context_takeover"!==t)throw new Error(`Unknown parameter "${t}"`);if(!0!==n)throw new TypeError(`Invalid value for parameter "${t}": ${n}`)}e[t]=n})}),e}decompress(e,t,n){h.add(r=>{this._decompress(e,t,(e,t)=>{r(),n(e,t)})})}compress(e,t,n){h.add(r=>{this._compress(e,t,(e,t)=>{r(),n(e,t)})})}_decompress(e,t,n){const o=this._isServer?"client":"server";if(!this._inflate){const e=o+"_max_window_bits",t="number"!=typeof this.params[e]?r.Z_DEFAULT_WINDOWBITS:this.params[e];this._inflate=r.createInflateRaw({...this._options.zlibInflateOptions,windowBits:t}),this._inflate[s]=this,this._inflate[c]=0,this._inflate[d]=[],this._inflate.on("error",g),this._inflate.on("data",m)}this._inflate[f]=n,this._inflate.write(e),t&&this._inflate.write(l),this._inflate.flush(()=>{const e=this._inflate[p];if(e)return this._inflate.close(),this._inflate=null,void n(e);const r=i.concat(this._inflate[d],this._inflate[c]);t&&this.params[o+"_no_context_takeover"]?(this._inflate.close(),this._inflate=null):(this._inflate[c]=0,this._inflate[d]=[]),n(null,r)})}_compress(e,t,n){const o=this._isServer?"server":"client";if(!this._deflate){const e=o+"_max_window_bits",t="number"!=typeof this.params[e]?r.Z_DEFAULT_WINDOWBITS:this.params[e];this._deflate=r.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:t}),this._deflate[c]=0,this._deflate[d]=[],this._deflate.on("error",a),this._deflate.on("data",v)}this._deflate[f]=n,this._deflate.write(e),this._deflate.flush(r.Z_SYNC_FLUSH,()=>{if(!this._deflate)return;let e=i.concat(this._deflate[d],this._deflate[c]);t&&(e=e.slice(0,e.length-4)),this._deflate[f]=null,t&&this.params[o+"_no_context_takeover"]?(this._deflate.close(),this._deflate=null):(this._deflate[c]=0,this._deflate[d]=[]),n(null,e)})}}},1762:(e,t,n)=>{"use strict";const{Writable:r}=n(2413),i=n(2309),{BINARY_TYPES:o,EMPTY_BUFFER:u,kStatusCode:a,kWebSocket:l}=n(5739),{concat:s,toArrayBuffer:c,unmask:f}=n(8716),{isValidStatusCode:d,isValidUTF8:p}=n(9498);function h(e,t,n,r){const i=new e(n?"Invalid WebSocket frame: "+t:t);return Error.captureStackTrace(i,h),i[a]=r,i}e.exports=class extends r{constructor(e,t,n,r){super(),this._binaryType=e||o[0],this[l]=void 0,this._extensions=t||{},this._isServer=!!n,this._maxPayload=0|r,this._bufferedBytes=0,this._buffers=[],this._compressed=!1,this._payloadLength=0,this._mask=void 0,this._fragmented=0,this._masked=!1,this._fin=!1,this._opcode=0,this._totalPayloadLength=0,this._messageLength=0,this._fragments=[],this._state=0,this._loop=!1}_write(e,t,n){if(8===this._opcode&&0==this._state)return n();this._bufferedBytes+=e.length,this._buffers.push(e),this.startLoop(n)}consume(e){if(this._bufferedBytes-=e,e===this._buffers[0].length)return this._buffers.shift();if(e=n.length?t.set(this._buffers.shift(),r):(t.set(new Uint8Array(n.buffer,n.byteOffset,e),r),this._buffers[0]=n.slice(e)),e-=n.length}while(e>0);return t}startLoop(e){let t;this._loop=!0;do{switch(this._state){case 0:t=this.getInfo();break;case 1:t=this.getPayloadLength16();break;case 2:t=this.getPayloadLength64();break;case 3:this.getMask();break;case 4:t=this.getData(e);break;default:return void(this._loop=!1)}}while(this._loop);e(t)}getInfo(){if(this._bufferedBytes<2)return void(this._loop=!1);const e=this.consume(2);if(0!=(48&e[0]))return this._loop=!1,h(RangeError,"RSV2 and RSV3 must be clear",!0,1002);const t=64==(64&e[0]);if(t&&!this._extensions[i.extensionName])return this._loop=!1,h(RangeError,"RSV1 must be clear",!0,1002);if(this._fin=128==(128&e[0]),this._opcode=15&e[0],this._payloadLength=127&e[1],0===this._opcode){if(t)return this._loop=!1,h(RangeError,"RSV1 must be clear",!0,1002);if(!this._fragmented)return this._loop=!1,h(RangeError,"invalid opcode 0",!0,1002);this._opcode=this._fragmented}else if(1===this._opcode||2===this._opcode){if(this._fragmented)return this._loop=!1,h(RangeError,"invalid opcode "+this._opcode,!0,1002);this._compressed=t}else{if(!(this._opcode>7&&this._opcode<11))return this._loop=!1,h(RangeError,"invalid opcode "+this._opcode,!0,1002);if(!this._fin)return this._loop=!1,h(RangeError,"FIN must be set",!0,1002);if(t)return this._loop=!1,h(RangeError,"RSV1 must be clear",!0,1002);if(this._payloadLength>125)return this._loop=!1,h(RangeError,"invalid payload length "+this._payloadLength,!0,1002)}if(this._fin||this._fragmented||(this._fragmented=this._opcode),this._masked=128==(128&e[1]),this._isServer){if(!this._masked)return this._loop=!1,h(RangeError,"MASK must be set",!0,1002)}else if(this._masked)return this._loop=!1,h(RangeError,"MASK must be clear",!0,1002);if(126===this._payloadLength)this._state=1;else{if(127!==this._payloadLength)return this.haveLength();this._state=2}}getPayloadLength16(){if(!(this._bufferedBytes<2))return this._payloadLength=this.consume(2).readUInt16BE(0),this.haveLength();this._loop=!1}getPayloadLength64(){if(this._bufferedBytes<8)return void(this._loop=!1);const e=this.consume(8),t=e.readUInt32BE(0);return t>Math.pow(2,21)-1?(this._loop=!1,h(RangeError,"Unsupported WebSocket frame: payload length > 2^53 - 1",!1,1009)):(this._payloadLength=t*Math.pow(2,32)+e.readUInt32BE(4),this.haveLength())}haveLength(){if(this._payloadLength&&this._opcode<8&&(this._totalPayloadLength+=this._payloadLength,this._totalPayloadLength>this._maxPayload&&this._maxPayload>0))return this._loop=!1,h(RangeError,"Max payload size exceeded",!1,1009);this._masked?this._state=3:this._state=4}getMask(){this._bufferedBytes<4?this._loop=!1:(this._mask=this.consume(4),this._state=4)}getData(e){let t=u;if(this._payloadLength){if(this._bufferedBytes7?this.controlMessage(t):this._compressed?(this._state=5,void this.decompress(t,e)):(t.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(t)),this.dataMessage())}decompress(e,t){this._extensions[i.extensionName].decompress(e,this._fin,(e,n)=>{if(e)return t(e);if(n.length){if(this._messageLength+=n.length,this._messageLength>this._maxPayload&&this._maxPayload>0)return t(h(RangeError,"Max payload size exceeded",!1,1009));this._fragments.push(n)}const r=this.dataMessage();if(r)return t(r);this.startLoop(t)})}dataMessage(){if(this._fin){const e=this._messageLength,t=this._fragments;if(this._totalPayloadLength=0,this._messageLength=0,this._fragmented=0,this._fragments=[],2===this._opcode){let n;n="nodebuffer"===this._binaryType?s(t,e):"arraybuffer"===this._binaryType?c(s(t,e)):t,this.emit("message",n)}else{const n=s(t,e);if(!p(n))return this._loop=!1,h(Error,"invalid UTF-8 sequence",!0,1007);this.emit("message",n.toString())}}this._state=0}controlMessage(e){if(8===this._opcode)if(this._loop=!1,0===e.length)this.emit("conclude",1005,""),this.end();else{if(1===e.length)return h(RangeError,"invalid payload length 1",!0,1002);{const t=e.readUInt16BE(0);if(!d(t))return h(RangeError,"invalid status code "+t,!0,1002);const n=e.slice(2);if(!p(n))return h(Error,"invalid UTF-8 sequence",!0,1007);this.emit("conclude",t,n.toString()),this.end()}}else 9===this._opcode?this.emit("ping",e):this.emit("pong",e);this._state=0}}},9576:(e,t,n)=>{"use strict";const{randomFillSync:r}=n(6417),i=n(2309),{EMPTY_BUFFER:o}=n(5739),{isValidStatusCode:u}=n(9498),{mask:a,toBuffer:l}=n(8716),s=Buffer.alloc(4);class c{constructor(e,t){this._extensions=t||{},this._socket=e,this._firstFragment=!0,this._compress=!1,this._bufferedBytes=0,this._deflating=!1,this._queue=[]}static frame(e,t){const n=t.mask&&t.readOnly;let i=t.mask?6:2,o=e.length;e.length>=65536?(i+=8,o=127):e.length>125&&(i+=2,o=126);const u=Buffer.allocUnsafe(n?e.length+i:i);return u[0]=t.fin?128|t.opcode:t.opcode,t.rsv1&&(u[0]|=64),u[1]=o,126===o?u.writeUInt16BE(e.length,2):127===o&&(u.writeUInt32BE(0,2),u.writeUInt32BE(e.length,6)),t.mask?(r(s,0,4),u[1]|=128,u[i-4]=s[0],u[i-3]=s[1],u[i-2]=s[2],u[i-1]=s[3],n?(a(e,s,u,i,e.length),[u]):(a(e,s,e,0,e.length),[u,e])):[u,e]}close(e,t,n,r){let i;if(void 0===e)i=o;else{if("number"!=typeof e||!u(e))throw new TypeError("First argument must be a valid error code number");if(void 0===t||""===t)i=Buffer.allocUnsafe(2),i.writeUInt16BE(e,0);else{const n=Buffer.byteLength(t);if(n>123)throw new RangeError("The message must not be greater than 123 bytes");i=Buffer.allocUnsafe(2+n),i.writeUInt16BE(e,0),i.write(t,2)}}this._deflating?this.enqueue([this.doClose,i,n,r]):this.doClose(i,n,r)}doClose(e,t,n){this.sendFrame(c.frame(e,{fin:!0,rsv1:!1,opcode:8,mask:t,readOnly:!1}),n)}ping(e,t,n){const r=l(e);if(r.length>125)throw new RangeError("The data size must not be greater than 125 bytes");this._deflating?this.enqueue([this.doPing,r,t,l.readOnly,n]):this.doPing(r,t,l.readOnly,n)}doPing(e,t,n,r){this.sendFrame(c.frame(e,{fin:!0,rsv1:!1,opcode:9,mask:t,readOnly:n}),r)}pong(e,t,n){const r=l(e);if(r.length>125)throw new RangeError("The data size must not be greater than 125 bytes");this._deflating?this.enqueue([this.doPong,r,t,l.readOnly,n]):this.doPong(r,t,l.readOnly,n)}doPong(e,t,n,r){this.sendFrame(c.frame(e,{fin:!0,rsv1:!1,opcode:10,mask:t,readOnly:n}),r)}send(e,t,n){const r=l(e),o=this._extensions[i.extensionName];let u=t.binary?2:1,a=t.compress;if(this._firstFragment?(this._firstFragment=!1,a&&o&&(a=r.length>=o._threshold),this._compress=a):(a=!1,u=0),t.fin&&(this._firstFragment=!0),o){const e={fin:t.fin,rsv1:a,opcode:u,mask:t.mask,readOnly:l.readOnly};this._deflating?this.enqueue([this.dispatch,r,this._compress,e,n]):this.dispatch(r,this._compress,e,n)}else this.sendFrame(c.frame(r,{fin:t.fin,rsv1:!1,opcode:u,mask:t.mask,readOnly:l.readOnly}),n)}dispatch(e,t,n,r){if(!t)return void this.sendFrame(c.frame(e,n),r);const o=this._extensions[i.extensionName];this._bufferedBytes+=e.length,this._deflating=!0,o.compress(e,n.fin,(t,i)=>{if(this._socket.destroyed){const e=new Error("The socket was closed while data was being compressed");"function"==typeof r&&r(e);for(let t=0;t{"use strict";const{Duplex:r}=n(2413);function i(e){e.emit("close")}function o(){!this.destroyed&&this._writableState.finished&&this.destroy()}function u(e){this.removeListener("error",u),this.destroy(),0===this.listenerCount("error")&&this.emit("error",e)}e.exports=function(e,t){let n=!0;function a(){n&&e._socket.resume()}e.readyState===e.CONNECTING?e.once("open",(function(){e._receiver.removeAllListeners("drain"),e._receiver.on("drain",a)})):(e._receiver.removeAllListeners("drain"),e._receiver.on("drain",a));const l=new r({...t,autoDestroy:!1,emitClose:!1,objectMode:!1,writableObjectMode:!1});return e.on("message",(function(t){l.push(t)||(n=!1,e._socket.pause())})),e.once("error",(function(e){l.destroyed||l.destroy(e)})),e.once("close",(function(){l.destroyed||l.push(null)})),l._destroy=function(t,n){if(e.readyState===e.CLOSED)return n(t),void process.nextTick(i,l);let r=!1;e.once("error",(function(e){r=!0,n(e)})),e.once("close",(function(){r||n(t),process.nextTick(i,l)})),e.terminate()},l._final=function(t){e.readyState!==e.CONNECTING?null!==e._socket&&(e._socket._writableState.finished?(t(),l._readableState.endEmitted&&l.destroy()):(e._socket.once("finish",(function(){t()})),e.close())):e.once("open",(function(){l._final(t)}))},l._read=function(){e.readyState!==e.OPEN||n||(n=!0,e._receiver._writableState.needDrain||e._socket.resume())},l._write=function(t,n,r){e.readyState!==e.CONNECTING?e.send(t,r):e.once("open",(function(){l._write(t,n,r)}))},l.on("end",o),l.on("error",u),l}},9498:(e,t,n)=>{"use strict";try{const e=n(Object(function(){var e=new Error("Cannot find module 'utf-8-validate'");throw e.code="MODULE_NOT_FOUND",e}()));t.isValidUTF8="object"==typeof e?e.Validation.isValidUTF8:e}catch(e){t.isValidUTF8=()=>!0}t.isValidStatusCode=e=>e>=1e3&&e<=1014&&1004!==e&&1005!==e&&1006!==e||e>=3e3&&e<=4999},43:(e,t,n)=>{"use strict";const r=n(8614),{createHash:i}=n(6417),{createServer:o,STATUS_CODES:u}=n(8605),a=n(2309),l=n(5760),{format:s,parse:c}=n(8162),{GUID:f,kWebSocket:d}=n(5739),p=/^[+/0-9A-Za-z]{22}==$/;function h(e){e.emit("close")}function v(){this.destroy()}function m(e,t,n,r){e.writable&&(n=n||u[t],r={Connection:"close","Content-Type":"text/html","Content-Length":Buffer.byteLength(n),...r},e.write(`HTTP/1.1 ${t} ${u[t]}\r\n`+Object.keys(r).map(e=>`${e}: ${r[e]}`).join("\r\n")+"\r\n\r\n"+n)),e.removeListener("error",v),e.destroy()}e.exports=class extends r{constructor(e,t){if(super(),null==(e={maxPayload:104857600,perMessageDeflate:!1,handleProtocols:null,clientTracking:!0,verifyClient:null,noServer:!1,backlog:null,server:null,host:null,path:null,port:null,...e}).port&&!e.server&&!e.noServer)throw new TypeError('One of the "port", "server", or "noServer" options must be specified');null!=e.port?(this._server=o((e,t)=>{const n=u[426];t.writeHead(426,{"Content-Length":n.length,"Content-Type":"text/plain"}),t.end(n)}),this._server.listen(e.port,e.host,e.backlog,t)):e.server&&(this._server=e.server),this._server&&(this._removeListeners=function(e,t){for(const n of Object.keys(t))e.on(n,t[n]);return function(){for(const n of Object.keys(t))e.removeListener(n,t[n])}}(this._server,{listening:this.emit.bind(this,"listening"),error:this.emit.bind(this,"error"),upgrade:(e,t,n)=>{this.handleUpgrade(e,t,n,t=>{this.emit("connection",t,e)})}})),!0===e.perMessageDeflate&&(e.perMessageDeflate={}),e.clientTracking&&(this.clients=new Set),this.options=e}address(){if(this.options.noServer)throw new Error('The server is operating in "noServer" mode');return this._server?this._server.address():null}close(e){if(e&&this.once("close",e),this.clients)for(const e of this.clients)e.terminate();const t=this._server;t&&(this._removeListeners(),this._removeListeners=this._server=null,null!=this.options.port)?t.close(()=>this.emit("close")):process.nextTick(h,this)}shouldHandle(e){if(this.options.path){const t=e.url.indexOf("?");if((-1!==t?e.url.slice(0,t):e.url)!==this.options.path)return!1}return!0}handleUpgrade(e,t,n,r){t.on("error",v);const i=void 0!==e.headers["sec-websocket-key"]&&e.headers["sec-websocket-key"].trim(),o=+e.headers["sec-websocket-version"],u={};if("GET"!==e.method||"websocket"!==e.headers.upgrade.toLowerCase()||!i||!p.test(i)||8!==o&&13!==o||!this.shouldHandle(e))return m(t,400);if(this.options.perMessageDeflate){const n=new a(this.options.perMessageDeflate,!0,this.options.maxPayload);try{const t=c(e.headers["sec-websocket-extensions"]);t[a.extensionName]&&(n.accept(t[a.extensionName]),u[a.extensionName]=n)}catch(e){return m(t,400)}}if(this.options.verifyClient){const a={origin:e.headers[""+(8===o?"sec-websocket-origin":"origin")],secure:!(!e.connection.authorized&&!e.connection.encrypted),req:e};if(2===this.options.verifyClient.length)return void this.options.verifyClient(a,(o,a,l,s)=>{if(!o)return m(t,a||401,l,s);this.completeUpgrade(i,u,e,t,n,r)});if(!this.options.verifyClient(a))return m(t,401)}this.completeUpgrade(i,u,e,t,n,r)}completeUpgrade(e,t,n,r,o,u){if(!r.readable||!r.writable)return r.destroy();if(r[d])throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");const c=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade","Sec-WebSocket-Accept: "+i("sha1").update(e+f).digest("base64")],p=new l(null);let h=n.headers["sec-websocket-protocol"];if(h&&(h=h.trim().split(/ *, */),h=this.options.handleProtocols?this.options.handleProtocols(h,n):h[0],h&&(c.push("Sec-WebSocket-Protocol: "+h),p.protocol=h)),t[a.extensionName]){const e=t[a.extensionName].params,n=s({[a.extensionName]:[e]});c.push("Sec-WebSocket-Extensions: "+n),p._extensions=t}this.emit("headers",c,n),r.write(c.concat("\r\n").join("\r\n")),r.removeListener("error",v),p.setSocket(r,o,this.options.maxPayload),this.clients&&(this.clients.add(p),p.on("close",()=>this.clients.delete(p))),u(p)}}},5760:(e,t,n)=>{"use strict";const r=n(8614),i=n(7211),o=n(8605),u=n(1631),a=n(4016),{randomBytes:l,createHash:s}=n(6417),{URL:c}=n(8835),f=n(2309),d=n(1762),p=n(9576),{BINARY_TYPES:h,EMPTY_BUFFER:v,GUID:m,kStatusCode:g,kWebSocket:y,NOOP:_}=n(5739),{addEventListener:b,removeEventListener:w}=n(7002),{format:E,parse:D}=n(8162),{toBuffer:S}=n(8716),C=["CONNECTING","OPEN","CLOSING","CLOSED"],k=[8,13];class T extends r{constructor(e,t,n){super(),this.readyState=T.CONNECTING,this.protocol="",this._binaryType=h[0],this._closeFrameReceived=!1,this._closeFrameSent=!1,this._closeMessage="",this._closeTimer=null,this._closeCode=1006,this._extensions={},this._receiver=null,this._sender=null,this._socket=null,null!==e?(this._bufferedAmount=0,this._isServer=!1,this._redirects=0,Array.isArray(t)?t=t.join(", "):"object"==typeof t&&null!==t&&(n=t,t=void 0),function e(t,n,r,u){const a={protocolVersion:k[1],maxPayload:104857600,perMessageDeflate:!0,followRedirects:!1,maxRedirects:10,...u,createConnection:void 0,socketPath:void 0,hostname:void 0,protocol:void 0,timeout:void 0,method:void 0,host:void 0,path:void 0,port:void 0};if(!k.includes(a.protocolVersion))throw new RangeError(`Unsupported protocol version: ${a.protocolVersion} (supported versions: ${k.join(", ")})`);let d;n instanceof c?(d=n,t.url=n.href):(d=new c(n),t.url=n);const p="ws+unix:"===d.protocol;if(!(d.host||p&&d.pathname))throw new Error("Invalid URL: "+t.url);const h="wss:"===d.protocol||"https:"===d.protocol,v=h?443:80,g=l(16).toString("base64"),y=h?i.get:o.get;let _;a.createConnection=h?A:x,a.defaultPort=a.defaultPort||v,a.port=d.port||v,a.host=d.hostname.startsWith("[")?d.hostname.slice(1,-1):d.hostname,a.headers={"Sec-WebSocket-Version":a.protocolVersion,"Sec-WebSocket-Key":g,Connection:"Upgrade",Upgrade:"websocket",...a.headers},a.path=d.pathname+d.search,a.timeout=a.handshakeTimeout,a.perMessageDeflate&&(_=new f(!0!==a.perMessageDeflate?a.perMessageDeflate:{},!1,a.maxPayload),a.headers["Sec-WebSocket-Extensions"]=E({[f.extensionName]:_.offer()}));r&&(a.headers["Sec-WebSocket-Protocol"]=r);a.origin&&(a.protocolVersion<13?a.headers["Sec-WebSocket-Origin"]=a.origin:a.headers.Origin=a.origin);(d.username||d.password)&&(a.auth=`${d.username}:${d.password}`);if(p){const e=a.path.split(":");a.socketPath=e[0],a.path=e[1]}let b=t._req=y(a);a.timeout&&b.on("timeout",()=>{O(t,b,"Opening handshake has timed out")});b.on("error",e=>{t._req.aborted||(b=t._req=null,t.readyState=T.CLOSING,t.emit("error",e),t.emitClose())}),b.on("response",i=>{const o=i.headers.location,l=i.statusCode;if(o&&a.followRedirects&&l>=300&&l<400){if(++t._redirects>a.maxRedirects)return void O(t,b,"Maximum redirects exceeded");b.abort();const i=new c(o,n);e(t,i,r,u)}else t.emit("unexpected-response",b,i)||O(t,b,"Unexpected server response: "+i.statusCode)}),b.on("upgrade",(e,n,i)=>{if(t.emit("upgrade",e),t.readyState!==T.CONNECTING)return;b=t._req=null;const o=s("sha1").update(g+m).digest("base64");if(e.headers["sec-websocket-accept"]!==o)return void O(t,n,"Invalid Sec-WebSocket-Accept header");const u=e.headers["sec-websocket-protocol"],l=(r||"").split(/, */);let c;if(!r&&u?c="Server sent a subprotocol but none was requested":r&&!u?c="Server sent no subprotocol":u&&!l.includes(u)&&(c="Server sent an invalid subprotocol"),c)O(t,n,c);else{if(u&&(t.protocol=u),_)try{const n=D(e.headers["sec-websocket-extensions"]);n[f.extensionName]&&(_.accept(n[f.extensionName]),t._extensions[f.extensionName]=_)}catch(e){return void O(t,n,"Invalid Sec-WebSocket-Extensions header")}t.setSocket(n,i,a.maxPayload)}})}(this,e,t,n)):this._isServer=!0}get CONNECTING(){return T.CONNECTING}get CLOSING(){return T.CLOSING}get CLOSED(){return T.CLOSED}get OPEN(){return T.OPEN}get binaryType(){return this._binaryType}set binaryType(e){h.includes(e)&&(this._binaryType=e,this._receiver&&(this._receiver._binaryType=e))}get bufferedAmount(){return this._socket?this._socket._writableState.length+this._sender._bufferedBytes:this._bufferedAmount}get extensions(){return Object.keys(this._extensions).join()}setSocket(e,t,n){const r=new d(this._binaryType,this._extensions,this._isServer,n);this._sender=new p(e,this._extensions),this._receiver=r,this._socket=e,r[y]=this,e[y]=this,r.on("conclude",I),r.on("drain",N),r.on("error",M),r.on("message",F),r.on("ping",L),r.on("pong",B),e.setTimeout(0),e.setNoDelay(),t.length>0&&e.unshift(t),e.on("close",j),e.on("data",U),e.on("end",z),e.on("error",W),this.readyState=T.OPEN,this.emit("open")}emitClose(){if(!this._socket)return this.readyState=T.CLOSED,void this.emit("close",this._closeCode,this._closeMessage);this._extensions[f.extensionName]&&this._extensions[f.extensionName].cleanup(),this._receiver.removeAllListeners(),this.readyState=T.CLOSED,this.emit("close",this._closeCode,this._closeMessage)}close(e,t){if(this.readyState!==T.CLOSED){if(this.readyState===T.CONNECTING){const e="WebSocket was closed before the connection was established";return O(this,this._req,e)}this.readyState!==T.CLOSING?(this.readyState=T.CLOSING,this._sender.close(e,t,!this._isServer,e=>{e||(this._closeFrameSent=!0,this._closeFrameReceived&&this._socket.end())}),this._closeTimer=setTimeout(this._socket.destroy.bind(this._socket),3e4)):this._closeFrameSent&&this._closeFrameReceived&&this._socket.end()}}ping(e,t,n){if(this.readyState===T.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");"function"==typeof e?(n=e,e=t=void 0):"function"==typeof t&&(n=t,t=void 0),"number"==typeof e&&(e=e.toString()),this.readyState===T.OPEN?(void 0===t&&(t=!this._isServer),this._sender.ping(e||v,t,n)):P(this,e,n)}pong(e,t,n){if(this.readyState===T.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");"function"==typeof e?(n=e,e=t=void 0):"function"==typeof t&&(n=t,t=void 0),"number"==typeof e&&(e=e.toString()),this.readyState===T.OPEN?(void 0===t&&(t=!this._isServer),this._sender.pong(e||v,t,n)):P(this,e,n)}send(e,t,n){if(this.readyState===T.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if("function"==typeof t&&(n=t,t={}),"number"==typeof e&&(e=e.toString()),this.readyState!==T.OPEN)return void P(this,e,n);const r={binary:"string"!=typeof e,mask:!this._isServer,compress:!0,fin:!0,...t};this._extensions[f.extensionName]||(r.compress=!1),this._sender.send(e||v,r,n)}terminate(){if(this.readyState!==T.CLOSED){if(this.readyState===T.CONNECTING){const e="WebSocket was closed before the connection was established";return O(this,this._req,e)}this._socket&&(this.readyState=T.CLOSING,this._socket.destroy())}}}function x(e){return e.path=e.socketPath,u.connect(e)}function A(e){return e.path=void 0,e.servername||""===e.servername||(e.servername=e.host),a.connect(e)}function O(e,t,n){e.readyState=T.CLOSING;const r=new Error(n);Error.captureStackTrace(r,O),t.setHeader?(t.abort(),t.once("abort",e.emitClose.bind(e)),e.emit("error",r)):(t.destroy(r),t.once("error",e.emit.bind(e,"error")),t.once("close",e.emitClose.bind(e)))}function P(e,t,n){if(t){const n=S(t).length;e._socket?e._sender._bufferedBytes+=n:e._bufferedAmount+=n}if(n){n(new Error(`WebSocket is not open: readyState ${e.readyState} (${C[e.readyState]})`))}}function I(e,t){const n=this[y];n._socket.removeListener("data",U),n._socket.resume(),n._closeFrameReceived=!0,n._closeMessage=t,n._closeCode=e,1005===e?n.close():n.close(e,t)}function N(){this[y]._socket.resume()}function M(e){const t=this[y];t._socket.removeListener("data",U),t.readyState=T.CLOSING,t._closeCode=e[g],t.emit("error",e),t._socket.destroy()}function R(){this[y].emitClose()}function F(e){this[y].emit("message",e)}function L(e){const t=this[y];t.pong(e,!t._isServer,_),t.emit("ping",e)}function B(e){this[y].emit("pong",e)}function j(){const e=this[y];this.removeListener("close",j),this.removeListener("end",z),e.readyState=T.CLOSING,e._socket.read(),e._receiver.end(),this.removeListener("data",U),this[y]=void 0,clearTimeout(e._closeTimer),e._receiver._writableState.finished||e._receiver._writableState.errorEmitted?e.emitClose():(e._receiver.on("error",R),e._receiver.on("finish",R))}function U(e){this[y]._receiver.write(e)||this.pause()}function z(){const e=this[y];e.readyState=T.CLOSING,e._receiver.end(),this.end()}function W(){const e=this[y];this.removeListener("error",W),this.on("error",_),e&&(e.readyState=T.CLOSING,this.destroy())}C.forEach((e,t)=>{T[e]=t}),["open","error","close","message"].forEach(e=>{Object.defineProperty(T.prototype,"on"+e,{get(){const t=this.listeners(e);for(let e=0;e{"use strict";function r(e){const t=[...e.caches],n=t.shift();return void 0===n?i():{get:(e,i,o={miss:()=>Promise.resolve()})=>n.get(e,i,o).catch(()=>r({caches:t}).get(e,i,o)),set:(e,i)=>n.set(e,i).catch(()=>r({caches:t}).set(e,i)),delete:e=>n.delete(e).catch(()=>r({caches:t}).delete(e)),clear:()=>n.clear().catch(()=>r({caches:t}).clear())}}function i(){return{get:(e,t,n={miss:()=>Promise.resolve()})=>t().then(e=>Promise.all([e,n.miss(e)])).then(([e])=>e),set:(e,t)=>Promise.resolve(t),delete:e=>Promise.resolve(),clear:()=>Promise.resolve()}}n.r(t),n.d(t,{createFallbackableCache:()=>r,createNullCache:()=>i})},6712:(e,t,n)=>{"use strict";function r(e={serializable:!0}){let t={};return{get(n,r,i={miss:()=>Promise.resolve()}){const o=JSON.stringify(n);if(o in t)return Promise.resolve(e.serializable?JSON.parse(t[o]):t[o]);const u=r(),a=i&&i.miss||(()=>Promise.resolve());return u.then(e=>a(e)).then(()=>u)},set:(n,r)=>(t[JSON.stringify(n)]=e.serializable?JSON.stringify(r):r,Promise.resolve(r)),delete:e=>(delete t[JSON.stringify(e)],Promise.resolve()),clear:()=>(t={},Promise.resolve())}}n.r(t),n.d(t,{createInMemoryCache:()=>r})},2223:(e,t,n)=>{"use strict";n.r(t),n.d(t,{addABTest:()=>a,createAnalyticsClient:()=>u,deleteABTest:()=>l,getABTest:()=>s,getABTests:()=>c,stopABTest:()=>f});var r=n(1757),i=n(7858),o=n(5541);const u=e=>{const t=e.region||"us",n=(0,r.createAuth)(r.AuthMode.WithinHeaders,e.appId,e.apiKey),o=(0,i.createTransporter)({hosts:[{url:`analytics.${t}.algolia.com`}],...e,headers:{...n.headers(),"content-type":"application/json",...e.headers},queryParameters:{...n.queryParameters(),...e.queryParameters}}),u=e.appId;return(0,r.addMethods)({appId:u,transporter:o},e.methods)},a=e=>(t,n)=>e.transporter.write({method:o.N.Post,path:"2/abtests",data:t},n),l=e=>(t,n)=>e.transporter.write({method:o.N.Delete,path:(0,r.encode)("2/abtests/%s",t)},n),s=e=>(t,n)=>e.transporter.read({method:o.N.Get,path:(0,r.encode)("2/abtests/%s",t)},n),c=e=>t=>e.transporter.read({method:o.N.Get,path:"2/abtests"},t),f=e=>(t,n)=>e.transporter.write({method:o.N.Post,path:(0,r.encode)("2/abtests/%s/stop",t)},n)},1757:(e,t,n)=>{"use strict";function r(e,t,n){const r={"x-algolia-api-key":n,"x-algolia-application-id":t};return{headers:()=>e===f.WithinHeaders?r:{},queryParameters:()=>e===f.WithinQueryParameters?r:{}}}function i(e){let t=0;const n=()=>(t++,new Promise(r=>{setTimeout(()=>{r(e(n))},Math.min(100*t,1e3))}));return e(n)}function o(e,t=((e,t)=>Promise.resolve())){return Object.assign(e,{wait:n=>o(e.then(e=>Promise.all([t(e,n),e])).then(e=>e[1]))})}function u(e){let t=e.length-1;for(;t>0;t--){const n=Math.floor(Math.random()*(t+1)),r=e[t];e[t]=e[n],e[n]=r}return e}function a(e,t){return Object.keys(void 0!==t?t:{}).forEach(n=>{e[n]=t[n](e)}),e}function l(e,...t){let n=0;return e.replace(/%s/g,()=>encodeURIComponent(t[n++]))}n.r(t),n.d(t,{AuthMode:()=>f,addMethods:()=>a,createAuth:()=>r,createRetryablePromise:()=>i,createWaitablePromise:()=>o,destroy:()=>c,encode:()=>l,shuffle:()=>u,version:()=>s});const s="4.2.0",c=e=>()=>e.transporter.requester.destroy(),f={WithinQueryParameters:0,WithinHeaders:1}},103:(e,t,n)=>{"use strict";n.r(t),n.d(t,{createRecommendationClient:()=>u,getPersonalizationStrategy:()=>a,setPersonalizationStrategy:()=>l});var r=n(1757),i=n(7858),o=n(5541);const u=e=>{const t=e.region||"us",n=(0,r.createAuth)(r.AuthMode.WithinHeaders,e.appId,e.apiKey),o=(0,i.createTransporter)({hosts:[{url:`recommendation.${t}.algolia.com`}],...e,headers:{...n.headers(),"content-type":"application/json",...e.headers},queryParameters:{...n.queryParameters(),...e.queryParameters}});return(0,r.addMethods)({appId:e.appId,transporter:o},e.methods)},a=e=>t=>e.transporter.read({method:o.N.Get,path:"1/strategies/personalization"},t),l=e=>(t,n)=>e.transporter.write({method:o.N.Post,path:"1/strategies/personalization",data:t},n)},6586:(e,t,n)=>{"use strict";n.r(t),n.d(t,{ApiKeyACLEnum:()=>Te,BatchActionEnum:()=>xe,ScopeEnum:()=>Ae,StrategyEnum:()=>Oe,SynonymEnum:()=>Pe,addApiKey:()=>d,assignUserID:()=>p,assignUserIDs:()=>h,batch:()=>z,browseObjects:()=>W,browseRules:()=>H,browseSynonyms:()=>V,chunkedBatch:()=>q,clearObjects:()=>G,clearRules:()=>$,clearSynonyms:()=>Y,copyIndex:()=>v,copyRules:()=>m,copySettings:()=>g,copySynonyms:()=>y,createBrowsablePromise:()=>a,createMissingObjectIDError:()=>s,createObjectNotFoundError:()=>c,createSearchClient:()=>l,createValidUntilNotFoundError:()=>f,deleteApiKey:()=>_,deleteBy:()=>K,deleteIndex:()=>X,deleteObject:()=>Q,deleteObjects:()=>J,deleteRule:()=>Z,deleteSynonym:()=>ee,exists:()=>te,findObject:()=>ne,generateSecuredApiKey:()=>b,getApiKey:()=>w,getLogs:()=>E,getObject:()=>re,getObjectPosition:()=>ie,getObjects:()=>oe,getRule:()=>ue,getSecuredApiKeyRemainingValidity:()=>D,getSettings:()=>ae,getSynonym:()=>le,getTask:()=>se,getTopUserIDs:()=>S,getUserID:()=>C,hasPendingMappings:()=>k,initIndex:()=>T,listApiKeys:()=>x,listClusters:()=>A,listIndices:()=>O,listUserIDs:()=>P,moveIndex:()=>I,multipleBatch:()=>N,multipleGetObjects:()=>M,multipleQueries:()=>R,multipleSearchForFacetValues:()=>F,partialUpdateObject:()=>ce,partialUpdateObjects:()=>fe,removeUserID:()=>L,replaceAllObjects:()=>de,replaceAllRules:()=>pe,replaceAllSynonyms:()=>he,restoreApiKey:()=>B,saveObject:()=>ve,saveObjects:()=>me,saveRule:()=>ge,saveRules:()=>ye,saveSynonym:()=>_e,saveSynonyms:()=>be,search:()=>we,searchForFacetValues:()=>Ee,searchRules:()=>De,searchSynonyms:()=>Se,searchUserIDs:()=>j,setSettings:()=>Ce,updateApiKey:()=>U,waitTask:()=>ke});var r=n(1757),i=n(7858),o=n(5541),u=n(6417);function a(e){const t=n=>e.request(n).then(r=>{if(void 0!==e.batch&&e.batch(r.hits),!e.shouldStop(r))return r.cursor?t({cursor:r.cursor}):t({page:(n.page||0)+1})});return t({})}const l=e=>{const t=e.appId,n=(0,r.createAuth)(void 0!==e.authMode?e.authMode:r.AuthMode.WithinHeaders,t,e.apiKey),o=(0,i.createTransporter)({hosts:[{url:t+"-dsn.algolia.net",accept:i.CallEnum.Read},{url:t+".algolia.net",accept:i.CallEnum.Write}].concat((0,r.shuffle)([{url:t+"-1.algolianet.com"},{url:t+"-2.algolianet.com"},{url:t+"-3.algolianet.com"}])),...e,headers:{...n.headers(),"content-type":"application/x-www-form-urlencoded",...e.headers},queryParameters:{...n.queryParameters(),...e.queryParameters}}),u={transporter:o,appId:t,addAlgoliaAgent(e,t){o.userAgent.add({segment:e,version:t})},clearCache:()=>Promise.all([o.requestsCache.clear(),o.responsesCache.clear()]).then(()=>{})};return(0,r.addMethods)(u,e.methods)};function s(){return{name:"MissingObjectIDError",message:"All objects must have an unique objectID (like a primary key) to be valid. Algolia is also able to generate objectIDs automatically but *it's not recommended*. To do it, use the `{'autoGenerateObjectIDIfNotExist': true}` option."}}function c(){return{name:"ObjectNotFoundError",message:"Object not found."}}function f(){return{name:"ValidUntilNotFoundError",message:"ValidUntil not found in given secured api key."}}const d=e=>(t,n)=>{const{queryParameters:i,...u}=n||{},a={acl:t,...void 0!==i?{queryParameters:i}:{}};return(0,r.createWaitablePromise)(e.transporter.write({method:o.N.Post,path:"1/keys",data:a},u),(t,n)=>(0,r.createRetryablePromise)(r=>w(e)(t.key,n).catch(e=>{if(404!==e.status)throw e;return r()})))},p=e=>(t,n,r)=>{const u=(0,i.createMappedRequestOptions)(r);return u.queryParameters["X-Algolia-User-ID"]=t,e.transporter.write({method:o.N.Post,path:"1/clusters/mapping",data:{cluster:n}},u)},h=e=>(t,n,r)=>e.transporter.write({method:o.N.Post,path:"1/clusters/mapping/batch",data:{users:t,cluster:n}},r),v=e=>(t,n,i)=>(0,r.createWaitablePromise)(e.transporter.write({method:o.N.Post,path:(0,r.encode)("1/indexes/%s/operation",t),data:{operation:"copy",destination:n}},i),(n,r)=>T(e)(t,{methods:{waitTask:ke}}).waitTask(n.taskID,r)),m=e=>(t,n,r)=>v(e)(t,n,{...r,scope:[Ae.Rules]}),g=e=>(t,n,r)=>v(e)(t,n,{...r,scope:[Ae.Settings]}),y=e=>(t,n,r)=>v(e)(t,n,{...r,scope:[Ae.Synonyms]}),_=e=>(t,n)=>(0,r.createWaitablePromise)(e.transporter.write({method:o.N.Delete,path:(0,r.encode)("1/keys/%s",t)},n),(n,i)=>(0,r.createRetryablePromise)(n=>w(e)(t,i).then(n).catch(e=>{if(404!==e.status)throw e}))),b=()=>(e,t)=>{const n=(0,i.serializeQueryParameters)(t),r=(0,u.createHmac)("sha256",e).update(n).digest("hex");return Buffer.from(r+n).toString("base64")},w=e=>(t,n)=>e.transporter.read({method:o.N.Get,path:(0,r.encode)("1/keys/%s",t)},n),E=e=>t=>e.transporter.read({method:o.N.Get,path:"1/logs"},t),D=()=>e=>{const t=Buffer.from(e,"base64").toString("ascii").match(/validUntil=(\d+)/);if(null===t)throw{name:"ValidUntilNotFoundError",message:"ValidUntil not found in given secured api key."};return parseInt(t[1],10)-Math.round((new Date).getTime()/1e3)},S=e=>t=>e.transporter.read({method:o.N.Get,path:"1/clusters/mapping/top"},t),C=e=>(t,n)=>e.transporter.read({method:o.N.Get,path:(0,r.encode)("1/clusters/mapping/%s",t)},n),k=e=>t=>{const{retrieveMappings:n,...r}=t||{};return!0===n&&(r.getClusters=!0),e.transporter.read({method:o.N.Get,path:"1/clusters/mapping/pending"},r)},T=e=>(t,n={})=>{const i={transporter:e.transporter,appId:e.appId,indexName:t};return(0,r.addMethods)(i,n.methods)},x=e=>t=>e.transporter.read({method:o.N.Get,path:"1/keys"},t),A=e=>t=>e.transporter.read({method:o.N.Get,path:"1/clusters"},t),O=e=>t=>e.transporter.read({method:o.N.Get,path:"1/indexes"},t),P=e=>t=>e.transporter.read({method:o.N.Get,path:"1/clusters/mapping"},t),I=e=>(t,n,i)=>(0,r.createWaitablePromise)(e.transporter.write({method:o.N.Post,path:(0,r.encode)("1/indexes/%s/operation",t),data:{operation:"move",destination:n}},i),(n,r)=>T(e)(t,{methods:{waitTask:ke}}).waitTask(n.taskID,r)),N=e=>(t,n)=>(0,r.createWaitablePromise)(e.transporter.write({method:o.N.Post,path:"1/indexes/*/batch",data:{requests:t}},n),(t,n)=>Promise.all(Object.keys(t.taskID).map(r=>T(e)(r,{methods:{waitTask:ke}}).waitTask(t.taskID[r],n)))),M=e=>(t,n)=>e.transporter.read({method:o.N.Post,path:"1/indexes/*/objects",data:{requests:t}},n),R=e=>(t,n)=>{const r=t.map(e=>({...e,params:(0,i.serializeQueryParameters)(e.params||{})}));return e.transporter.read({method:o.N.Post,path:"1/indexes/*/queries",data:{requests:r},cacheable:!0},n)},F=e=>(t,n)=>Promise.all(t.map(t=>{const{facetName:r,facetQuery:i,...o}=t.params;return T(e)(t.indexName,{methods:{searchForFacetValues:Ee}}).searchForFacetValues(r,i,{...n,...o})})),L=e=>(t,n)=>{const r=(0,i.createMappedRequestOptions)(n);return r.queryParameters["X-Algolia-User-ID"]=t,e.transporter.write({method:o.N.Delete,path:"1/clusters/mapping"},r)},B=e=>(t,n)=>(0,r.createWaitablePromise)(e.transporter.write({method:o.N.Post,path:(0,r.encode)("1/keys/%s/restore",t)},n),(n,i)=>(0,r.createRetryablePromise)(n=>w(e)(t,i).catch(e=>{if(404!==e.status)throw e;return n()}))),j=e=>(t,n)=>e.transporter.read({method:o.N.Post,path:"1/clusters/mapping/search",data:{query:t}},n),U=e=>(t,n)=>{const i=Object.assign({},n),{queryParameters:u,...a}=n||{},l=u?{queryParameters:u}:{},s=["acl","indexes","referers","restrictSources","queryParameters","description","maxQueriesPerIPPerHour","maxHitsPerQuery"];return(0,r.createWaitablePromise)(e.transporter.write({method:o.N.Put,path:(0,r.encode)("1/keys/%s",t),data:l},a),(n,o)=>(0,r.createRetryablePromise)(n=>w(e)(t,o).then(e=>(e=>Object.keys(i).filter(e=>-1!==s.indexOf(e)).every(t=>e[t]===i[t]))(e)?Promise.resolve():n())))},z=e=>(t,n)=>(0,r.createWaitablePromise)(e.transporter.write({method:o.N.Post,path:(0,r.encode)("1/indexes/%s/batch",e.indexName),data:{requests:t}},n),(t,n)=>ke(e)(t.taskID,n)),W=e=>t=>a({...t,shouldStop:e=>void 0===e.cursor,request:n=>e.transporter.read({method:o.N.Post,path:(0,r.encode)("1/indexes/%s/browse",e.indexName),data:n},t)}),H=e=>t=>{const n={hitsPerPage:1e3,...t};return a({...n,shouldStop:e=>e.hits.lengthDe(e)("",{...n,...t}).then(e=>({...e,hits:e.hits.map(e=>(delete e._highlightResult,e))}))})},V=e=>t=>{const n={hitsPerPage:1e3,...t};return a({...n,shouldStop:e=>e.hits.lengthSe(e)("",{...n,...t}).then(e=>({...e,hits:e.hits.map(e=>(delete e._highlightResult,e))}))})},q=e=>(t,n,i)=>{const{batchSize:o,...u}=i||{},a={taskIDs:[],objectIDs:[]},l=(r=0)=>{const i=[];let s;for(s=r;s