From 98fa00a21fdc77b8675c4cc13c5e6efb7716eaf3 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sat, 12 Aug 2023 17:39:58 -0400 Subject: [PATCH] Borgs (#18136) * Laws * positronic brain and PAI rewrite * MMI * MMI pt. 2 * borg brain transfer * Roleban support, Borg job (WIP), the end of mind shenaniganry * battery drain, item slot cleanup, alerts * visuals * fix this pt1 * fix this pt2 * Modules, Lingering Stacks, Better borg flashlight * Start on UI, fix battery alerts, expand activation/deactivation, low movement speed on no power. * sprotes * no zombie borgs * oh fuck yeah i love a good relay * charger * fix the tiniest of sprite issues * adjustable names * a functional UI???? * foobar * more modules * this shit for some reason * upstream * genericize selectable borg modules * upstream again * holy fucking shit * i love christ * proper construction * da job * AA borgs * and boom more shit * admin logs * laws redux * ok just do this rq * oh boy that looks like modules * oh shit research * testos passo * so much shit holy fuck * fuckit we SHIP * last minute snags * should've gotten me on a better day --- Content.Client/Hands/Systems/HandsSystem.cs | 24 +- .../Silicons/Borgs/BorgBoundUserInterface.cs | 66 +++ Content.Client/Silicons/Borgs/BorgMenu.xaml | 58 ++ .../Silicons/Borgs/BorgMenu.xaml.cs | 165 ++++++ .../Silicons/Borgs/BorgModuleControl.xaml | 12 + .../Silicons/Borgs/BorgModuleControl.xaml.cs | 25 + Content.Client/Silicons/Borgs/BorgSystem.cs | 85 +++ .../Silicons/Laws/SiliconLawSystem.cs | 9 + .../Silicons/Laws/Ui/LawDisplay.xaml | 18 + .../Silicons/Laws/Ui/LawDisplay.xaml.cs | 21 + .../Laws/Ui/SiliconLawBoundUserInterface.cs | 45 ++ .../Silicons/Laws/Ui/SiliconLawMenu.xaml | 27 + .../Silicons/Laws/Ui/SiliconLawMenu.xaml.cs | 31 ++ Content.Client/Stack/StackSystem.cs | 17 +- Content.Client/Stylesheets/StyleNano.cs | 4 + Content.Server/Armor/ArmorSystem.cs | 7 + Content.Server/Body/Systems/BrainSystem.cs | 8 +- .../Construction/ConstructionSystem.Graph.cs | 8 +- .../ConstructionSystem.Interactions.cs | 12 + .../Behaviors/EmptyContainersBehaviour.cs | 40 ++ .../GameTicking/Rules/ZombieRuleSystem.cs | 2 +- .../ToggleableGhostRoleComponent.cs | 38 ++ .../Ghost/Roles/ToggleableGhostRoleSystem.cs | 140 +++++ .../EntitySystems/HandheldLightSystem.cs | 26 +- Content.Server/Medical/HealingSystem.cs | 8 +- Content.Server/Mind/MindSystem.cs | 18 +- .../NameIdentifier/NameIdentifierComponent.cs | 17 - .../NameIdentifier/NameIdentifierSystem.cs | 16 +- Content.Server/PAI/PAISystem.cs | 133 +---- .../Power/EntitySystems/ChargerSystem.cs | 66 ++- Content.Server/PowerCell/PowerCellSystem.cs | 26 +- .../Repairable/RepairableComponent.cs | 6 + Content.Server/Repairable/RepairableSystem.cs | 5 + .../Silicons/Borgs/BorgSystem.MMI.cs | 79 +++ .../Silicons/Borgs/BorgSystem.Modules.cs | 316 +++++++++++ .../Silicons/Borgs/BorgSystem.Ui.cs | 108 ++++ Content.Server/Silicons/Borgs/BorgSystem.cs | 318 +++++++++++ .../Silicons/Laws/SiliconLawSystem.cs | 157 ++++++ Content.Server/Stack/StackSystem.cs | 2 +- Content.Server/Strip/StrippableSystem.cs | 2 +- .../ActivatableUIRequiresPanelComponent.cs | 15 + Content.Server/Wires/WiresSystem.cs | 11 + .../Zombies/ZombieSystem.Transform.cs | 5 +- .../Access/Components/AccessComponent.cs | 13 + .../Access/Systems/AccessReaderSystem.cs | 17 +- Content.Shared/Alert/AlertCategory.cs | 3 +- Content.Shared/Alert/AlertType.cs | 2 + .../Components/PartAssemblyComponent.cs | 45 ++ .../Construction/PartAssemblySystem.cs | 147 +++++ .../ConstructionGraphStepTypeSerializer.cs | 5 + .../PartAssemblyConstructionGraphStep.cs | 39 ++ .../Containers/ItemSlot/ItemSlotsComponent.cs | 12 + .../Containers/ItemSlot/ItemSlotsSystem.cs | 34 +- .../Components/BlockMovementComponent.cs | 12 + .../SharedInteractionSystem.Blocking.cs | 47 ++ .../Interaction/SharedInteractionSystem.cs | 1 + .../Light/Component/HandheldLightComponent.cs | 7 + .../SharedMaterialReclaimerSystem.cs | 3 + Content.Shared/Mind/MindVisuals.cs | 17 + .../NameIdentifier/NameIdentifierComponent.cs | 23 + Content.Shared/PAI/PAIComponent.cs | 7 + Content.Shared/PAI/SharedPAISystem.cs | 61 --- Content.Shared/Silicons/Borgs/BorgUI.cs | 57 ++ .../Borgs/Components/BorgBrainComponent.cs | 13 + .../Borgs/Components/BorgChassisComponent.cs | 126 +++++ .../Borgs/Components/BorgModuleComponent.cs | 33 ++ .../Components/ItemBorgModuleComponent.cs | 51 ++ .../Silicons/Borgs/Components/MMIComponent.cs | 49 ++ .../Borgs/Components/MMILinkedComponent.cs | 17 + .../SelectableBorgModuleComponent.cs | 41 ++ .../Silicons/Borgs/SharedBorgSystem.Relay.cs | 38 ++ .../Silicons/Borgs/SharedBorgSystem.cs | 107 ++++ .../Components/EmagSiliconLawComponent.cs | 14 + .../Components/SiliconLawBoundComponent.cs | 57 ++ .../Components/SiliconLawProviderComponent.cs | 16 + .../Silicons/Laws/SharedSiliconLawSystem.cs | 22 + .../Silicons/Laws/SiliconLawPrototype.cs | 55 ++ Content.Shared/Stacks/StackComponent.cs | 9 +- .../SharedEntityStorageSystem.cs | 5 +- Content.Shared/Verbs/SharedVerbSystem.cs | 26 +- .../Locale/en-US/actions/actions/borgs.ftl | 5 + Resources/Locale/en-US/alerts/alerts.ftl | 6 + Resources/Locale/en-US/borg/borg.ftl | 18 + .../conditions/any-conditions.ftl | 1 + .../en-US/headset/headset-component.ftl | 1 + .../Locale/en-US/job/job-description.ftl | 1 + Resources/Locale/en-US/job/job-names.ftl | 1 + .../components/power-cell-component.ftl | 1 + .../Locale/en-US/research/technologies.ftl | 2 + Resources/Locale/en-US/robotics/mmi.ftl | 13 + Resources/Locale/en-US/station-laws/laws.ftl | 32 ++ Resources/Prototypes/Actions/borgs.yml | 10 + Resources/Prototypes/Alerts/alerts.yml | 40 ++ Resources/Prototypes/Body/Organs/human.yml | 2 +- Resources/Prototypes/Body/Parts/silicon.yml | 74 ++- .../VendingMachines/Inventories/robotics.yml | 2 +- .../Entities/Markers/Spawners/jobs.yml | 17 +- .../Mobs/Cyborgs/base_borg_chassis.yml | 172 ++++++ .../Entities/Mobs/Cyborgs/borg_chassis.yml | 160 ++++++ .../Entities/Mobs/Player/silicon.yml | 22 + .../Circuitboards/Machine/production.yml | 22 + .../Entities/Objects/Devices/station_map.yml | 29 +- .../Prototypes/Entities/Objects/Fun/pai.yml | 16 +- .../Specific/Mech/mech_construction.yml | 7 +- .../Objects/Specific/Medical/defib.yml | 89 +-- .../Objects/Specific/Medical/healing.yml | 35 ++ .../Specific/Medical/healthanalyzer.yml | 34 +- .../Objects/Specific/Research/anomaly.yml | 27 +- .../Specific/Robotics/borg_modules.yml | 400 ++++++++++++++ .../Objects/Specific/Robotics/borg_parts.yml | 503 +++++++++++++++++ .../Specific/Robotics/endoskeleton.yml | 230 ++++++++ .../Objects/Specific/Robotics/mmi.yml | 122 +++++ .../Entities/Objects/Tools/cable_coils.yml | 27 + .../Entities/Objects/Tools/tools.yml | 13 + .../Prototypes/Entities/Stations/base.yml | 10 + .../Entities/Stations/nanotrasen.yml | 1 + .../Entities/Structures/Machines/lathe.yml | 62 ++- .../Entities/Structures/Power/chargers.yml | 96 ++++ .../Construction/Graphs/machines/cyborg.yml | 240 ++++++++ .../Recipes/Crafting/Graphs/bots/mimebot.yml | 4 +- .../Recipes/Crafting/Graphs/bots/taxibot.yml | 6 +- .../Prototypes/Recipes/Lathes/electronics.yml | 8 + .../Prototypes/Recipes/Lathes/robotics.yml | 513 +++++++++++++++++- Resources/Prototypes/Research/biochemical.yml | 14 + .../Prototypes/Research/civilianservices.yml | 18 + .../Prototypes/Research/experimental.yml | 7 +- Resources/Prototypes/Research/industrial.yml | 7 +- .../Prototypes/Roles/Jobs/Science/borg.yml | 12 + .../Prototypes/Roles/Jobs/departments.yml | 1 + .../Prototypes/Roles/play_time_trackers.yml | 5 +- Resources/Prototypes/StatusEffects/job.yml | 7 + .../Prototypes/name_identifier_groups.yml | 22 +- Resources/Prototypes/radio_channels.yml | 9 + Resources/Prototypes/silicon-laws.yml | 96 ++++ Resources/Prototypes/tags.yml | 118 +++- .../Actions/actions_borg.rsi/meta.json | 14 + .../Actions/actions_borg.rsi/state-laws.png | Bin 0 -> 399 bytes .../Alerts/battery.rsi/battery-none.png | Bin 0 -> 452 bytes .../Interface/Alerts/battery.rsi/battery0.png | Bin 0 -> 531 bytes .../Interface/Alerts/battery.rsi/battery1.png | Bin 0 -> 410 bytes .../Alerts/battery.rsi/battery10.png | Bin 0 -> 415 bytes .../Interface/Alerts/battery.rsi/battery2.png | Bin 0 -> 442 bytes .../Interface/Alerts/battery.rsi/battery3.png | Bin 0 -> 480 bytes .../Interface/Alerts/battery.rsi/battery4.png | Bin 0 -> 460 bytes .../Interface/Alerts/battery.rsi/battery5.png | Bin 0 -> 484 bytes .../Interface/Alerts/battery.rsi/battery6.png | Bin 0 -> 451 bytes .../Interface/Alerts/battery.rsi/battery7.png | Bin 0 -> 451 bytes .../Interface/Alerts/battery.rsi/battery8.png | Bin 0 -> 414 bytes .../Interface/Alerts/battery.rsi/battery9.png | Bin 0 -> 413 bytes .../Interface/Alerts/battery.rsi/meta.json | 53 ++ .../Interface/Misc/job_icons.rsi/Borg.png | Bin 0 -> 540 bytes .../Interface/Misc/job_icons.rsi/meta.json | 3 + .../Textures/Mobs/Silicon/borg.rsi/l_leg.png | Bin 609 -> 0 bytes .../Textures/Mobs/Silicon/borg.rsi/meta.json | 17 - .../Textures/Mobs/Silicon/borg.rsi/r_leg.png | Bin 616 -> 0 bytes .../Mobs/Silicon/chassis.rsi/clown.png | Bin 0 -> 3056 bytes .../Mobs/Silicon/chassis.rsi/clown_e.png | Bin 0 -> 308 bytes .../Mobs/Silicon/chassis.rsi/clown_e_r.png | Bin 0 -> 299 bytes .../Mobs/Silicon/chassis.rsi/clown_l.png | Bin 0 -> 429 bytes .../Mobs/Silicon/chassis.rsi/engineer.png | Bin 0 -> 1885 bytes .../Mobs/Silicon/chassis.rsi/engineer_e.png | Bin 0 -> 314 bytes .../Mobs/Silicon/chassis.rsi/engineer_e_r.png | Bin 0 -> 317 bytes .../Mobs/Silicon/chassis.rsi/engineer_l.png | Bin 0 -> 638 bytes .../Mobs/Silicon/chassis.rsi/janitor.png | Bin 0 -> 4280 bytes .../Mobs/Silicon/chassis.rsi/janitor_e.png | Bin 0 -> 337 bytes .../Mobs/Silicon/chassis.rsi/janitor_e_r.png | Bin 0 -> 334 bytes .../Mobs/Silicon/chassis.rsi/janitor_l.png | Bin 0 -> 673 bytes .../Mobs/Silicon/chassis.rsi/medical.png | Bin 0 -> 3342 bytes .../Mobs/Silicon/chassis.rsi/medical_e.png | Bin 0 -> 294 bytes .../Mobs/Silicon/chassis.rsi/medical_e_r.png | Bin 0 -> 279 bytes .../Mobs/Silicon/chassis.rsi/medical_l.png | Bin 0 -> 531 bytes .../Mobs/Silicon/chassis.rsi/meta.json | 205 +++++++ .../Mobs/Silicon/chassis.rsi/miner.png | Bin 0 -> 2958 bytes .../Mobs/Silicon/chassis.rsi/miner_e.png | Bin 0 -> 270 bytes .../Mobs/Silicon/chassis.rsi/miner_e_r.png | Bin 0 -> 271 bytes .../Mobs/Silicon/chassis.rsi/miner_l.png | Bin 0 -> 461 bytes .../Mobs/Silicon/chassis.rsi/peace.png | Bin 0 -> 2016 bytes .../Mobs/Silicon/chassis.rsi/peace_e.png | Bin 0 -> 308 bytes .../Mobs/Silicon/chassis.rsi/peace_e_r.png | Bin 0 -> 299 bytes .../Mobs/Silicon/chassis.rsi/peace_l.png | Bin 0 -> 429 bytes .../Mobs/Silicon/chassis.rsi/robot.png | Bin 0 -> 1705 bytes .../Mobs/Silicon/chassis.rsi/robot_e.png | Bin 0 -> 329 bytes .../Mobs/Silicon/chassis.rsi/robot_e_r.png | Bin 0 -> 325 bytes .../Mobs/Silicon/chassis.rsi/robot_l.png | Bin 0 -> 648 bytes .../Mobs/Silicon/chassis.rsi/service.png | Bin 0 -> 1737 bytes .../Mobs/Silicon/chassis.rsi/service_e.png | Bin 0 -> 225 bytes .../Mobs/Silicon/chassis.rsi/service_e_r.png | Bin 0 -> 230 bytes .../Mobs/Silicon/chassis.rsi/service_l.png | Bin 0 -> 419 bytes .../Borg/head.rsi/light_borg_head.png | Bin 17891 -> 0 bytes .../Objects/Specific/Borg/head.rsi/meta.json | 14 - .../Robotics/borgmodule.rsi/cargo.png | Bin 0 -> 565 bytes .../Robotics/borgmodule.rsi/engineering.png | Bin 0 -> 621 bytes .../Robotics/borgmodule.rsi/generic.png | Bin 0 -> 580 bytes .../borgmodule.rsi/icon-anomalies.png | Bin 0 -> 251 bytes .../borgmodule.rsi/icon-appraisal.png | Bin 0 -> 218 bytes .../borgmodule.rsi/icon-artifacts.png | Bin 0 -> 242 bytes .../Robotics/borgmodule.rsi/icon-cables.png | Bin 0 -> 232 bytes .../Robotics/borgmodule.rsi/icon-clown.png | Bin 0 -> 237 bytes .../Robotics/borgmodule.rsi/icon-defib.png | Bin 0 -> 213 bytes .../borgmodule.rsi/icon-diagnosis.png | Bin 0 -> 231 bytes .../borgmodule.rsi/icon-fire-extinguisher.png | Bin 0 -> 200 bytes .../borgmodule.rsi/icon-gardening.png | Bin 0 -> 226 bytes .../borgmodule.rsi/icon-gas-analyzer.png | Bin 0 -> 249 bytes .../Robotics/borgmodule.rsi/icon-gps.png | Bin 0 -> 229 bytes .../borgmodule.rsi/icon-grappling-gun.png | Bin 0 -> 247 bytes .../borgmodule.rsi/icon-light-replacer.png | Bin 0 -> 245 bytes .../Robotics/borgmodule.rsi/icon-mining.png | Bin 0 -> 227 bytes .../Robotics/borgmodule.rsi/icon-mop.png | Bin 0 -> 224 bytes .../Robotics/borgmodule.rsi/icon-musique.png | Bin 0 -> 177 bytes .../Robotics/borgmodule.rsi/icon-pen.png | Bin 0 -> 197 bytes .../borgmodule.rsi/icon-radiation.png | Bin 0 -> 184 bytes .../Robotics/borgmodule.rsi/icon-rcd.png | Bin 0 -> 256 bytes .../Robotics/borgmodule.rsi/icon-template.png | Bin 0 -> 126 bytes .../borgmodule.rsi/icon-tools-adv.png | Bin 0 -> 235 bytes .../Robotics/borgmodule.rsi/icon-tools.png | Bin 0 -> 212 bytes .../borgmodule.rsi/icon-trash-bag.png | Bin 0 -> 239 bytes .../borgmodule.rsi/icon-treatment.png | Bin 0 -> 238 bytes .../Robotics/borgmodule.rsi/janitor.png | Bin 0 -> 603 bytes .../Robotics/borgmodule.rsi/medical.png | Bin 0 -> 623 bytes .../Robotics/borgmodule.rsi/meta.json | 110 ++++ .../Robotics/borgmodule.rsi/science.png | Bin 0 -> 628 bytes .../Robotics/borgmodule.rsi/security.png | Bin 0 -> 601 bytes .../Robotics/borgmodule.rsi/service.png | Bin 0 -> 563 bytes .../Robotics/borgmodule.rsi/syndicate.png | Bin 0 -> 566 bytes .../cyborg_parts.rsi/borg_chest+o.png | Bin 0 -> 445 bytes .../Robotics/cyborg_parts.rsi/borg_chest.png | Bin 0 -> 447 bytes .../Robotics/cyborg_parts.rsi/borg_head+o.png | Bin 0 -> 321 bytes .../Robotics/cyborg_parts.rsi/borg_head.png | Bin 0 -> 321 bytes .../cyborg_parts.rsi/borg_l_arm+o.png | Bin 0 -> 230 bytes .../Robotics/cyborg_parts.rsi/borg_l_arm.png | Bin 0 -> 227 bytes .../cyborg_parts.rsi/borg_l_leg+o.png | Bin 0 -> 252 bytes .../Robotics/cyborg_parts.rsi/borg_l_leg.png | Bin 0 -> 254 bytes .../cyborg_parts.rsi/borg_r_arm+o.png | Bin 0 -> 227 bytes .../Robotics/cyborg_parts.rsi/borg_r_arm.png | Bin 0 -> 224 bytes .../cyborg_parts.rsi/borg_r_leg+o.png | Bin 0 -> 254 bytes .../Robotics/cyborg_parts.rsi/borg_r_leg.png | Bin 0 -> 257 bytes .../cyborg_parts.rsi/engineer_chest+o.png | Bin 0 -> 358 bytes .../cyborg_parts.rsi/engineer_chest.png | Bin 0 -> 358 bytes .../cyborg_parts.rsi/engineer_head+o.png | Bin 0 -> 317 bytes .../cyborg_parts.rsi/engineer_head.png | Bin 0 -> 318 bytes .../cyborg_parts.rsi/engineer_l_arm+o.png | Bin 0 -> 175 bytes .../cyborg_parts.rsi/engineer_l_arm.png | Bin 0 -> 169 bytes .../cyborg_parts.rsi/engineer_l_leg+o.png | Bin 0 -> 254 bytes .../cyborg_parts.rsi/engineer_l_leg.png | Bin 0 -> 252 bytes .../cyborg_parts.rsi/engineer_r_arm+o.png | Bin 0 -> 174 bytes .../cyborg_parts.rsi/engineer_r_arm.png | Bin 0 -> 171 bytes .../cyborg_parts.rsi/engineer_r_leg+o.png | Bin 0 -> 260 bytes .../cyborg_parts.rsi/engineer_r_leg.png | Bin 0 -> 261 bytes .../cyborg_parts.rsi/janitor_chest+o.png | Bin 0 -> 443 bytes .../cyborg_parts.rsi/janitor_chest.png | Bin 0 -> 443 bytes .../cyborg_parts.rsi/janitor_head+o.png | Bin 0 -> 568 bytes .../cyborg_parts.rsi/janitor_head.png | Bin 0 -> 568 bytes .../cyborg_parts.rsi/janitor_l_leg+o.png | Bin 0 -> 212 bytes .../cyborg_parts.rsi/janitor_l_leg.png | Bin 0 -> 211 bytes .../cyborg_parts.rsi/janitor_r_leg+o.png | Bin 0 -> 212 bytes .../cyborg_parts.rsi/janitor_r_leg.png | Bin 0 -> 214 bytes .../cyborg_parts.rsi/medical_chest+o.png | Bin 0 -> 378 bytes .../cyborg_parts.rsi/medical_chest.png | Bin 0 -> 378 bytes .../cyborg_parts.rsi/medical_head+o.png | Bin 0 -> 546 bytes .../cyborg_parts.rsi/medical_head.png | Bin 0 -> 544 bytes .../cyborg_parts.rsi/medical_l_arm+o.png | Bin 0 -> 153 bytes .../cyborg_parts.rsi/medical_l_arm.png | Bin 0 -> 152 bytes .../cyborg_parts.rsi/medical_l_leg+o.png | Bin 0 -> 172 bytes .../cyborg_parts.rsi/medical_l_leg.png | Bin 0 -> 172 bytes .../cyborg_parts.rsi/medical_r_arm+o.png | Bin 0 -> 153 bytes .../cyborg_parts.rsi/medical_r_arm.png | Bin 0 -> 151 bytes .../cyborg_parts.rsi/medical_r_leg+o.png | Bin 0 -> 180 bytes .../cyborg_parts.rsi/medical_r_leg.png | Bin 0 -> 180 bytes .../Robotics/cyborg_parts.rsi/meta.json | 218 ++++++++ .../cyborg_parts.rsi/mining_chest+o.png | Bin 0 -> 348 bytes .../cyborg_parts.rsi/mining_chest.png | Bin 0 -> 348 bytes .../cyborg_parts.rsi/mining_head+o.png | Bin 0 -> 322 bytes .../Robotics/cyborg_parts.rsi/mining_head.png | Bin 0 -> 322 bytes .../cyborg_parts.rsi/mining_l_arm+o.png | Bin 0 -> 239 bytes .../cyborg_parts.rsi/mining_l_arm.png | Bin 0 -> 240 bytes .../cyborg_parts.rsi/mining_l_leg+o.png | Bin 0 -> 257 bytes .../cyborg_parts.rsi/mining_l_leg.png | Bin 0 -> 255 bytes .../cyborg_parts.rsi/mining_r_arm+o.png | Bin 0 -> 236 bytes .../cyborg_parts.rsi/mining_r_arm.png | Bin 0 -> 233 bytes .../cyborg_parts.rsi/mining_r_leg+o.png | Bin 0 -> 243 bytes .../cyborg_parts.rsi/mining_r_leg.png | Bin 0 -> 243 bytes .../Robotics/cyborg_parts.rsi/robo_suit.png | Bin 0 -> 301 bytes .../cyborg_parts.rsi/service_chest+o.png | Bin 0 -> 357 bytes .../cyborg_parts.rsi/service_chest.png | Bin 0 -> 358 bytes .../cyborg_parts.rsi/service_head+o.png | Bin 0 -> 218 bytes .../cyborg_parts.rsi/service_head.png | Bin 0 -> 218 bytes .../cyborg_parts.rsi/service_l_arm+o.png | Bin 0 -> 194 bytes .../cyborg_parts.rsi/service_l_arm.png | Bin 0 -> 193 bytes .../cyborg_parts.rsi/service_l_leg+o.png | Bin 0 -> 209 bytes .../cyborg_parts.rsi/service_l_leg.png | Bin 0 -> 207 bytes .../cyborg_parts.rsi/service_r_arm+o.png | Bin 0 -> 202 bytes .../cyborg_parts.rsi/service_r_arm.png | Bin 0 -> 199 bytes .../cyborg_parts.rsi/service_r_leg+o.png | Bin 0 -> 220 bytes .../cyborg_parts.rsi/service_r_leg.png | Bin 0 -> 221 bytes .../Specific/Robotics/mmi.rsi/meta.json | 79 +++ .../Specific/Robotics/mmi.rsi/mmi_alive.png | Bin 0 -> 598 bytes .../Specific/Robotics/mmi.rsi/mmi_brain.png | Bin 0 -> 555 bytes .../Robotics/mmi.rsi/mmi_brain_alien.png | Bin 0 -> 457 bytes .../Specific/Robotics/mmi.rsi/mmi_dead.png | Bin 0 -> 599 bytes .../Specific/Robotics/mmi.rsi/mmi_off.png | Bin 0 -> 746 bytes .../Robotics/mmi.rsi/posibrain-occupied.png | Bin 0 -> 2294 bytes .../Robotics/mmi.rsi/posibrain-searching.png | Bin 0 -> 3396 bytes .../Specific/Robotics/mmi.rsi/posibrain.png | Bin 0 -> 573 bytes .../Power/borg_charger.rsi/borgcharger-u0.png | Bin 0 -> 1001 bytes .../Power/borg_charger.rsi/borgcharger-u1.png | Bin 0 -> 870 bytes .../Power/borg_charger.rsi/borgcharger0.png | Bin 0 -> 175 bytes .../Power/borg_charger.rsi/borgcharger1.png | Bin 0 -> 377 bytes .../Power/borg_charger.rsi/borgcharger2.png | Bin 0 -> 174 bytes .../Power/borg_charger.rsi/borgcharger3.png | Bin 0 -> 180 bytes .../Power/borg_charger.rsi/borgdecon1.png | Bin 0 -> 207 bytes .../Power/borg_charger.rsi/borgdecon2.png | Bin 0 -> 1154 bytes .../Power/borg_charger.rsi/borgdecon3.png | Bin 0 -> 1093 bytes .../Power/borg_charger.rsi/meta.json | 46 ++ SpaceStation14.sln.DotSettings | 2 + 314 files changed, 7094 insertions(+), 484 deletions(-) create mode 100644 Content.Client/Silicons/Borgs/BorgBoundUserInterface.cs create mode 100644 Content.Client/Silicons/Borgs/BorgMenu.xaml create mode 100644 Content.Client/Silicons/Borgs/BorgMenu.xaml.cs create mode 100644 Content.Client/Silicons/Borgs/BorgModuleControl.xaml create mode 100644 Content.Client/Silicons/Borgs/BorgModuleControl.xaml.cs create mode 100644 Content.Client/Silicons/Borgs/BorgSystem.cs create mode 100644 Content.Client/Silicons/Laws/SiliconLawSystem.cs create mode 100644 Content.Client/Silicons/Laws/Ui/LawDisplay.xaml create mode 100644 Content.Client/Silicons/Laws/Ui/LawDisplay.xaml.cs create mode 100644 Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs create mode 100644 Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml create mode 100644 Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml.cs create mode 100644 Content.Server/Destructible/Thresholds/Behaviors/EmptyContainersBehaviour.cs create mode 100644 Content.Server/Ghost/Roles/Components/ToggleableGhostRoleComponent.cs create mode 100644 Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs delete mode 100644 Content.Server/NameIdentifier/NameIdentifierComponent.cs create mode 100644 Content.Server/Silicons/Borgs/BorgSystem.MMI.cs create mode 100644 Content.Server/Silicons/Borgs/BorgSystem.Modules.cs create mode 100644 Content.Server/Silicons/Borgs/BorgSystem.Ui.cs create mode 100644 Content.Server/Silicons/Borgs/BorgSystem.cs create mode 100644 Content.Server/Silicons/Laws/SiliconLawSystem.cs create mode 100644 Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs create mode 100644 Content.Shared/Construction/Components/PartAssemblyComponent.cs create mode 100644 Content.Shared/Construction/PartAssemblySystem.cs create mode 100644 Content.Shared/Construction/Steps/PartAssemblyConstructionGraphStep.cs create mode 100644 Content.Shared/Interaction/Components/BlockMovementComponent.cs create mode 100644 Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs create mode 100644 Content.Shared/Mind/MindVisuals.cs create mode 100644 Content.Shared/NameIdentifier/NameIdentifierComponent.cs create mode 100644 Content.Shared/Silicons/Borgs/BorgUI.cs create mode 100644 Content.Shared/Silicons/Borgs/Components/BorgBrainComponent.cs create mode 100644 Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs create mode 100644 Content.Shared/Silicons/Borgs/Components/BorgModuleComponent.cs create mode 100644 Content.Shared/Silicons/Borgs/Components/ItemBorgModuleComponent.cs create mode 100644 Content.Shared/Silicons/Borgs/Components/MMIComponent.cs create mode 100644 Content.Shared/Silicons/Borgs/Components/MMILinkedComponent.cs create mode 100644 Content.Shared/Silicons/Borgs/Components/SelectableBorgModuleComponent.cs create mode 100644 Content.Shared/Silicons/Borgs/SharedBorgSystem.Relay.cs create mode 100644 Content.Shared/Silicons/Borgs/SharedBorgSystem.cs create mode 100644 Content.Shared/Silicons/Laws/Components/EmagSiliconLawComponent.cs create mode 100644 Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs create mode 100644 Content.Shared/Silicons/Laws/Components/SiliconLawProviderComponent.cs create mode 100644 Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs create mode 100644 Content.Shared/Silicons/Laws/SiliconLawPrototype.cs create mode 100644 Resources/Locale/en-US/actions/actions/borgs.ftl create mode 100644 Resources/Locale/en-US/borg/borg.ftl create mode 100644 Resources/Locale/en-US/robotics/mmi.ftl create mode 100644 Resources/Locale/en-US/station-laws/laws.ftl create mode 100644 Resources/Prototypes/Actions/borgs.yml create mode 100644 Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml create mode 100644 Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml create mode 100644 Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml create mode 100644 Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_parts.yml create mode 100644 Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml create mode 100644 Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml create mode 100644 Resources/Prototypes/Recipes/Construction/Graphs/machines/cyborg.yml create mode 100644 Resources/Prototypes/Roles/Jobs/Science/borg.yml create mode 100644 Resources/Prototypes/silicon-laws.yml create mode 100644 Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json create mode 100644 Resources/Textures/Interface/Actions/actions_borg.rsi/state-laws.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery-none.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery0.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery1.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery10.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery2.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery3.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery4.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery5.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery6.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery7.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery8.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/battery9.png create mode 100644 Resources/Textures/Interface/Alerts/battery.rsi/meta.json create mode 100644 Resources/Textures/Interface/Misc/job_icons.rsi/Borg.png delete mode 100644 Resources/Textures/Mobs/Silicon/borg.rsi/l_leg.png delete mode 100644 Resources/Textures/Mobs/Silicon/borg.rsi/meta.json delete mode 100644 Resources/Textures/Mobs/Silicon/borg.rsi/r_leg.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/clown.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/clown_e.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/clown_e_r.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/clown_l.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/engineer.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_e.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_e_r.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_l.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/janitor.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e_r.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_l.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/medical.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/medical_e.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/medical_e_r.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/medical_l.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/meta.json create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/miner.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/miner_e.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/miner_e_r.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/miner_l.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/peace.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/peace_e.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/peace_e_r.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/peace_l.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/robot.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/robot_e.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/robot_e_r.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/robot_l.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/service.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/service_e.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/service_e_r.png create mode 100644 Resources/Textures/Mobs/Silicon/chassis.rsi/service_l.png delete mode 100644 Resources/Textures/Objects/Specific/Borg/head.rsi/light_borg_head.png delete mode 100644 Resources/Textures/Objects/Specific/Borg/head.rsi/meta.json create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/cargo.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/engineering.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/generic.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-anomalies.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-appraisal.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-artifacts.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-cables.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-clown.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-defib.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-diagnosis.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-fire-extinguisher.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gardening.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gas-analyzer.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gps.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-grappling-gun.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-light-replacer.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-mining.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-mop.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-musique.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-pen.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-radiation.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-rcd.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-template.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-tools-adv.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-tools.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-trash-bag.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-treatment.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/janitor.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/medical.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/science.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/security.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/service.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/syndicate.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_arm+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_arm.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_arm+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_arm.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_chest+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_chest.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_head+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_head.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_chest+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_chest.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_head+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_head.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_l_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_l_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_r_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_r_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_chest+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_chest.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_arm+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_arm.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/meta.json create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_chest+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_chest.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_head+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_head.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_arm+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_arm.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_arm+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_arm.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/robo_suit.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_chest+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_chest.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_arm+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_arm.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg+o.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/mmi.rsi/meta.json create mode 100644 Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_alive.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_brain.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_brain_alien.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_dead.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_off.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-occupied.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-searching.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain.png create mode 100644 Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger-u0.png create mode 100644 Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger-u1.png create mode 100644 Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger0.png create mode 100644 Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger1.png create mode 100644 Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger2.png create mode 100644 Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger3.png create mode 100644 Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon1.png create mode 100644 Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon2.png create mode 100644 Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon3.png create mode 100644 Resources/Textures/Structures/Power/borg_charger.rsi/meta.json diff --git a/Content.Client/Hands/Systems/HandsSystem.cs b/Content.Client/Hands/Systems/HandsSystem.cs index f87f75892e..0cefbbcfaa 100644 --- a/Content.Client/Hands/Systems/HandsSystem.cs +++ b/Content.Client/Hands/Systems/HandsSystem.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Numerics; using Content.Client.Animations; using Content.Client.Examine; @@ -63,6 +64,18 @@ namespace Content.Client.Hands.Systems return; var handsModified = component.Hands.Count != state.Hands.Count; + // we need to check that, even if we have the same amount, that the individual hands didn't change. + if (!handsModified) + { + foreach (var hand in component.Hands.Values) + { + if (state.Hands.Contains(hand)) + continue; + handsModified = true; + break; + } + } + var manager = EnsureComp(uid); if (handsModified) @@ -87,12 +100,13 @@ namespace Content.Client.Hands.Systems } } - foreach (var hand in addedHands) + component.SortedHands = new(state.HandNames); + var sorted = addedHands.OrderBy(hand => component.SortedHands.IndexOf(hand.Name)); + + foreach (var hand in sorted) { AddHand(uid, hand, component); } - - component.SortedHands = new(state.HandNames); } _stripSys.UpdateUi(uid); @@ -327,7 +341,7 @@ namespace Content.Client.Hands.Systems } var ev = new GetInhandVisualsEvent(uid, hand.Location); - RaiseLocalEvent(held, ev, false); + RaiseLocalEvent(held, ev); if (ev.Layers.Count == 0) { @@ -340,7 +354,7 @@ namespace Content.Client.Hands.Systems { if (!revealedLayers.Add(key)) { - Logger.Warning($"Duplicate key for in-hand visuals: {key}. Are multiple components attempting to modify the same layer? Entity: {ToPrettyString(held)}"); + Log.Warning($"Duplicate key for in-hand visuals: {key}. Are multiple components attempting to modify the same layer? Entity: {ToPrettyString(held)}"); continue; } diff --git a/Content.Client/Silicons/Borgs/BorgBoundUserInterface.cs b/Content.Client/Silicons/Borgs/BorgBoundUserInterface.cs new file mode 100644 index 0000000000..1d8bd06d9e --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgBoundUserInterface.cs @@ -0,0 +1,66 @@ +using Content.Shared.Silicons.Borgs; +using JetBrains.Annotations; +using Robust.Client.GameObjects; + +namespace Content.Client.Silicons.Borgs; + +[UsedImplicitly] +public sealed class BorgBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private BorgMenu? _menu; + + public BorgBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + var owner = Owner; + + _menu = new BorgMenu(owner); + + _menu.BrainButtonPressed += () => + { + SendMessage(new BorgEjectBrainBuiMessage()); + }; + + _menu.EjectBatteryButtonPressed += () => + { + SendMessage(new BorgEjectBatteryBuiMessage()); + }; + + _menu.NameChanged += name => + { + SendMessage(new BorgSetNameBuiMessage(name)); + }; + + _menu.RemoveModuleButtonPressed += module => + { + SendMessage(new BorgRemoveModuleBuiMessage(module)); + }; + + _menu.OnClose += Close; + + _menu.OpenCentered(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not BorgBuiState msg) + return; + _menu?.UpdateState(msg); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) + return; + _menu?.Dispose(); + } +} diff --git a/Content.Client/Silicons/Borgs/BorgMenu.xaml b/Content.Client/Silicons/Borgs/BorgMenu.xaml new file mode 100644 index 0000000000..7d8fd9fe57 --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgMenu.xaml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + diff --git a/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs new file mode 100644 index 0000000000..046d8e299f --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs @@ -0,0 +1,165 @@ +using Content.Client.Stylesheets; +using Content.Client.UserInterface.Controls; +using Content.Shared.NameIdentifier; +using Content.Shared.Preferences; +using Content.Shared.Silicons.Borgs; +using Content.Shared.Silicons.Borgs.Components; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Timing; + +namespace Content.Client.Silicons.Borgs; + +[GenerateTypedNameReferences] +public sealed partial class BorgMenu : FancyWindow +{ + [Dependency] private readonly IEntityManager _entity = default!; + + public Action? BrainButtonPressed; + public Action? EjectBatteryButtonPressed; + public Action? NameChanged; + public Action? RemoveModuleButtonPressed; + + private readonly BorgChassisComponent? _chassis; + public readonly EntityUid Entity; + public float AccumulatedTime; + private string _lastValidName; + + public BorgMenu(EntityUid entity) + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + Entity = entity; + + if (_entity.TryGetComponent(Entity, out var chassis)) + _chassis = chassis; + + BorgSprite.SetEntity(entity); + ChargeBar.MaxValue = 1f; + ChargeBar.Value = 1f; + + if (_entity.TryGetComponent(Entity, out var nameIdentifierComponent)) + { + NameIdentifierLabel.Visible = true; + NameIdentifierLabel.Text = nameIdentifierComponent.FullIdentifier; + + var fullName = _entity.GetComponent(Entity).EntityName; + var name = fullName.Substring(0, fullName.Length - nameIdentifierComponent.FullIdentifier.Length - 1); + NameLineEdit.Text = name; + } + else + { + NameIdentifierLabel.Visible = false; + NameLineEdit.Text = _entity.GetComponent(Entity).EntityName; + } + + _lastValidName = NameLineEdit.Text; + + EjectBatteryButton.OnPressed += _ => EjectBatteryButtonPressed?.Invoke(); + BrainButton.OnPressed += _ => BrainButtonPressed?.Invoke(); + + NameLineEdit.OnTextChanged += OnNameChanged; + NameLineEdit.OnTextEntered += OnNameEntered; + NameLineEdit.OnFocusExit += OnNameFocusExit; + + UpdateBrainButton(); + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + + AccumulatedTime += args.DeltaSeconds; + BorgSprite.OverrideDirection = (Direction) ((int) AccumulatedTime % 4 * 2); + } + + public void UpdateState(BorgBuiState state) + { + EjectBatteryButton.Disabled = !state.HasBattery; + ChargeBar.Value = state.ChargePercent; + ChargeLabel.Text = Loc.GetString("borg-ui-charge-label", + ("charge", (int) MathF.Round(state.ChargePercent * 100))); + + UpdateBrainButton(); + UpdateModulePanel(); + } + + private void UpdateBrainButton() + { + if (_chassis?.BrainEntity is { } brain) + { + BrainButton.Text = _entity.GetComponent(brain).EntityName; + BrainView.Visible = true; + BrainView.SetEntity(brain); + BrainButton.Disabled = false; + BrainButton.AddStyleClass(StyleBase.ButtonOpenLeft); + } + else + { + BrainButton.Text = Loc.GetString("borg-ui-no-brain"); + BrainButton.Disabled = true; + BrainView.Visible = false; + BrainButton.RemoveStyleClass(StyleBase.ButtonOpenLeft); + } + } + + private void UpdateModulePanel() + { + if (_chassis == null) + return; + + ModuleCounter.Text = Loc.GetString("borg-ui-module-counter", + ("actual", _chassis.ModuleCount), + ("max", _chassis.MaxModules)); + + ModuleContainer.Children.Clear(); + foreach (var module in _chassis.ModuleContainer.ContainedEntities) + { + var control = new BorgModuleControl(module, _entity); + control.RemoveButtonPressed += () => + { + RemoveModuleButtonPressed?.Invoke(module); + }; + ModuleContainer.AddChild(control); + } + } + + private void OnNameChanged(LineEdit.LineEditEventArgs obj) + { + if (obj.Text.Length == 0 || + string.IsNullOrWhiteSpace(obj.Text) || + string.IsNullOrEmpty(obj.Text)) + { + return; + } + + if (obj.Text.Length > HumanoidCharacterProfile.MaxNameLength) + { + obj.Control.Text = obj.Text.Substring(0, HumanoidCharacterProfile.MaxNameLength); + } + + _lastValidName = obj.Control.Text; + obj.Control.Text = _lastValidName; + } + + private void OnNameEntered(LineEdit.LineEditEventArgs obj) + { + NameChanged?.Invoke(_lastValidName); + } + + private void OnNameFocusExit(LineEdit.LineEditEventArgs obj) + { + if (obj.Text.Length > HumanoidCharacterProfile.MaxNameLength || + obj.Text.Length == 0 || + string.IsNullOrWhiteSpace(obj.Text) || + string.IsNullOrEmpty(obj.Text)) + { + obj.Control.Text = _lastValidName.Trim(); + } + + NameChanged?.Invoke(_lastValidName); + } +} + diff --git a/Content.Client/Silicons/Borgs/BorgModuleControl.xaml b/Content.Client/Silicons/Borgs/BorgModuleControl.xaml new file mode 100644 index 0000000000..7d89cc54b9 --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgModuleControl.xaml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/Content.Client/Silicons/Borgs/BorgModuleControl.xaml.cs b/Content.Client/Silicons/Borgs/BorgModuleControl.xaml.cs new file mode 100644 index 0000000000..d5cf05ba63 --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgModuleControl.xaml.cs @@ -0,0 +1,25 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.Silicons.Borgs; + +[GenerateTypedNameReferences] +public sealed partial class BorgModuleControl : PanelContainer +{ + public Action? RemoveButtonPressed; + + public BorgModuleControl(EntityUid entity, IEntityManager entityManager) + { + RobustXamlLoader.Load(this); + + ModuleView.SetEntity(entity); + ModuleName.Text = entityManager.GetComponent(entity).EntityName; + RemoveButton.TexturePath = "/Textures/Interface/Nano/cross.svg.png"; + RemoveButton.OnPressed += _ => + { + RemoveButtonPressed?.Invoke(); + }; + } +} + diff --git a/Content.Client/Silicons/Borgs/BorgSystem.cs b/Content.Client/Silicons/Borgs/BorgSystem.cs new file mode 100644 index 0000000000..5d2e5fa070 --- /dev/null +++ b/Content.Client/Silicons/Borgs/BorgSystem.cs @@ -0,0 +1,85 @@ +using Content.Shared.Silicons.Borgs; +using Content.Shared.Silicons.Borgs.Components; +using Robust.Client.GameObjects; +using Robust.Shared.Containers; + +namespace Content.Client.Silicons.Borgs; + +/// +public sealed class BorgSystem : SharedBorgSystem +{ + [Dependency] private readonly AppearanceSystem _appearance = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBorgAppearanceChanged); + SubscribeLocalEvent(OnMMIAppearanceChanged); + } + + private void OnBorgAppearanceChanged(EntityUid uid, BorgChassisComponent component, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + UpdateBorgAppearnce(uid, component, args.Component, args.Sprite); + } + + protected override void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args) + { + if (!component.Initialized) + return; + + base.OnInserted(uid, component, args); + UpdateBorgAppearnce(uid, component); + } + + protected override void OnRemoved(EntityUid uid, BorgChassisComponent component, EntRemovedFromContainerMessage args) + { + if (!component.Initialized) + return; + + base.OnRemoved(uid, component, args); + UpdateBorgAppearnce(uid, component); + } + + private void UpdateBorgAppearnce(EntityUid uid, + BorgChassisComponent? component = null, + AppearanceComponent? appearance = null, + SpriteComponent? sprite = null) + { + if (!Resolve(uid, ref component, ref appearance, ref sprite)) + return; + + if (!_appearance.TryGetData(uid, BorgVisuals.HasPlayer, out var hasPlayer, appearance)) + hasPlayer = false; + + sprite.LayerSetVisible(BorgVisualLayers.Light, component.BrainEntity != null || hasPlayer); + sprite.LayerSetState(BorgVisualLayers.Light, hasPlayer ? component.HasMindState : component.NoMindState); + } + + private void OnMMIAppearanceChanged(EntityUid uid, MMIComponent component, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + var sprite = args.Sprite; + + if (!_appearance.TryGetData(uid, MMIVisuals.BrainPresent, out bool brain)) + brain = false; + if (!_appearance.TryGetData(uid, MMIVisuals.HasMind, out bool hasMind)) + hasMind = false; + + sprite.LayerSetVisible(MMIVisualLayers.Brain, brain); + if (!brain) + { + sprite.LayerSetState(MMIVisualLayers.Base, component.NoBrainState); + } + else + { + var state = hasMind + ? component.HasMindState + : component.NoMindState; + sprite.LayerSetState(MMIVisualLayers.Base, state); + } + } +} diff --git a/Content.Client/Silicons/Laws/SiliconLawSystem.cs b/Content.Client/Silicons/Laws/SiliconLawSystem.cs new file mode 100644 index 0000000000..f7f2fe7c47 --- /dev/null +++ b/Content.Client/Silicons/Laws/SiliconLawSystem.cs @@ -0,0 +1,9 @@ +using Content.Shared.Silicons.Laws; + +namespace Content.Client.Silicons.Laws; + +/// +public sealed class SiliconLawSystem : SharedSiliconLawSystem +{ + +} diff --git a/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml b/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml new file mode 100644 index 0000000000..714d9e0fe7 --- /dev/null +++ b/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml.cs b/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml.cs new file mode 100644 index 0000000000..c48fcef619 --- /dev/null +++ b/Content.Client/Silicons/Laws/Ui/LawDisplay.xaml.cs @@ -0,0 +1,21 @@ +using Content.Shared.Silicons.Laws; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.Silicons.Laws.Ui; + +[GenerateTypedNameReferences] +public sealed partial class LawDisplay : Control +{ + public LawDisplay(SiliconLaw prototype) + { + RobustXamlLoader.Load(this); + + var identifier = prototype.LawIdentifierOverride ?? $"{prototype.Order}"; + + LawNumberLabel.Text = Loc.GetString("laws-ui-law-header", ("id", identifier)); + LawLabel.SetMessage(Loc.GetString(prototype.LawString)); + } +} + diff --git a/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs new file mode 100644 index 0000000000..78231c24df --- /dev/null +++ b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs @@ -0,0 +1,45 @@ +using Content.Shared.Silicons.Laws.Components; +using JetBrains.Annotations; +using Robust.Client.GameObjects; + +namespace Content.Client.Silicons.Laws.Ui; + +[UsedImplicitly] +public sealed class SiliconLawBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private SiliconLawMenu? _menu; + + public SiliconLawBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + + } + + protected override void Open() + { + base.Open(); + + _menu = new(); + + _menu.OnClose += Close; + _menu.OpenCentered(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) + return; + _menu?.Close(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not SiliconLawBuiState msg) + return; + + _menu?.Update(msg); + } +} diff --git a/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml b/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml new file mode 100644 index 0000000000..1b6324dae6 --- /dev/null +++ b/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + diff --git a/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml.cs b/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml.cs new file mode 100644 index 0000000000..457be358e0 --- /dev/null +++ b/Content.Client/Silicons/Laws/Ui/SiliconLawMenu.xaml.cs @@ -0,0 +1,31 @@ +using Content.Client.UserInterface.Controls; +using Content.Shared.Silicons.Laws.Components; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; + +namespace Content.Client.Silicons.Laws.Ui; + +[GenerateTypedNameReferences] +public sealed partial class SiliconLawMenu : FancyWindow +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + + public SiliconLawMenu() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + } + + public void Update(SiliconLawBuiState state) + { + state.Laws.Sort(); + foreach (var law in state.Laws) + { + var control = new LawDisplay(law); + + LawDisplayContainer.AddChild(control); + } + } +} + diff --git a/Content.Client/Stack/StackSystem.cs b/Content.Client/Stack/StackSystem.cs index 66f5fbe985..a5d7470b57 100644 --- a/Content.Client/Stack/StackSystem.cs +++ b/Content.Client/Stack/StackSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Client.Items; using Content.Client.Storage.Systems; using Content.Shared.Stacks; @@ -31,8 +32,22 @@ namespace Content.Client.Stack base.SetCount(uid, amount, component); + if (component.Lingering && + TryComp(uid, out var sprite)) + { + // tint the stack gray and make it transparent if it's lingering. + var color = component.Count == 0 && component.Lingering + ? Color.DarkGray.WithAlpha(0.65f) + : Color.White; + + for (var i = 0; i < sprite.AllLayers.Count(); i++) + { + sprite.LayerSetColor(i, color); + } + } + // TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call. - if (component.Count <= 0) + if (component.Count <= 0 && !component.Lingering) { Xform.DetachParentToNull(uid, Transform(uid)); return; diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs index cb90e5c481..8aefec03e5 100644 --- a/Content.Client/Stylesheets/StyleNano.cs +++ b/Content.Client/Stylesheets/StyleNano.cs @@ -1332,6 +1332,10 @@ namespace Content.Client.Stylesheets .Prop("panel", new StyleBoxTexture(BaseButtonOpenBoth) { Padding = default }) .Prop(Control.StylePropertyModulateSelf, Color.FromHex("#1F1F23")), + Element().Class("PanelBackgroundLight") + .Prop("panel", new StyleBoxTexture(BaseButtonOpenBoth) { Padding = default }) + .Prop(Control.StylePropertyModulateSelf, Color.FromHex("#2F2F3B")), + // Window Footer Element().Class("NTLogoDark") .Prop(TextureRect.StylePropertyTexture, resCache.GetTexture("/Textures/Interface/Nano/ntlogo.svg.png")) diff --git a/Content.Server/Armor/ArmorSystem.cs b/Content.Server/Armor/ArmorSystem.cs index 931ca0c25b..dc02b06667 100644 --- a/Content.Server/Armor/ArmorSystem.cs +++ b/Content.Server/Armor/ArmorSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Cargo.Systems; using Robust.Shared.Prototypes; using Content.Shared.Damage.Prototypes; using Content.Shared.Inventory; +using Content.Shared.Silicons.Borgs; namespace Content.Server.Armor { @@ -22,6 +23,7 @@ namespace Content.Server.Armor base.Initialize(); SubscribeLocalEvent>(OnDamageModify); + SubscribeLocalEvent>(OnBorgDamageModify); SubscribeLocalEvent>(OnArmorVerbExamine); SubscribeLocalEvent(GetArmorPrice); } @@ -67,6 +69,11 @@ namespace Content.Server.Armor args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, component.Modifiers); } + private void OnBorgDamageModify(EntityUid uid, ArmorComponent component, ref BorgModuleRelayedEvent args) + { + args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, component.Modifiers); + } + private void OnArmorVerbExamine(EntityUid uid, ArmorComponent component, GetVerbsEvent args) { if (!args.CanInteract || !args.CanAccess) diff --git a/Content.Server/Body/Systems/BrainSystem.cs b/Content.Server/Body/Systems/BrainSystem.cs index 0a8dd5dc6a..ce3dadc628 100644 --- a/Content.Server/Body/Systems/BrainSystem.cs +++ b/Content.Server/Body/Systems/BrainSystem.cs @@ -40,12 +40,12 @@ namespace Content.Server.Body.Systems private void HandleMind(EntityUid newEntity, EntityUid oldEntity) { - EntityManager.EnsureComponent(newEntity); - var oldMind = EntityManager.EnsureComponent(oldEntity); + EnsureComp(newEntity); + var oldMind = EnsureComp(oldEntity); - EnsureComp(newEntity); + var ghostOnMove = EnsureComp(newEntity); if (HasComp(newEntity)) - Comp(newEntity).MustBeDead = true; + ghostOnMove.MustBeDead = true; // TODO: This is an awful solution. // Our greatest minds still can't figure out how to allow brains/heads to ghost without giving them the diff --git a/Content.Server/Construction/ConstructionSystem.Graph.cs b/Content.Server/Construction/ConstructionSystem.Graph.cs index 49dd9dd359..43b7b009a5 100644 --- a/Content.Server/Construction/ConstructionSystem.Graph.cs +++ b/Content.Server/Construction/ConstructionSystem.Graph.cs @@ -357,8 +357,12 @@ namespace Content.Server.Construction if (!_container.TryGetContainer(uid, container, out var ourContainer, containerManager)) continue; - // NOTE: Only Container is supported by Construction! - var otherContainer = _container.EnsureContainer(newUid, container, newContainerManager); + if (!_container.TryGetContainer(newUid, container, out var otherContainer, newContainerManager)) + { + // NOTE: Only Container is supported by Construction! + // todo: one day, the ensured container should be the same type as ourContainer + otherContainer = _container.EnsureContainer(newUid, container, newContainerManager); + } for (var i = ourContainer.ContainedEntities.Count - 1; i >= 0; i--) { diff --git a/Content.Server/Construction/ConstructionSystem.Interactions.cs b/Content.Server/Construction/ConstructionSystem.Interactions.cs index dee013bf71..3336736fac 100644 --- a/Content.Server/Construction/ConstructionSystem.Interactions.cs +++ b/Content.Server/Construction/ConstructionSystem.Interactions.cs @@ -4,6 +4,7 @@ using Content.Server.Construction.Components; using Content.Server.Temperature.Components; using Content.Server.Temperature.Systems; using Content.Shared.Construction; +using Content.Shared.Construction.Components; using Content.Shared.Construction.EntitySystems; using Content.Shared.Construction.Steps; using Content.Shared.DoAfter; @@ -39,6 +40,7 @@ namespace Content.Server.Construction new []{typeof(AnchorableSystem)}, new []{typeof(EncryptionKeySystem)}); SubscribeLocalEvent(EnqueueEvent); + SubscribeLocalEvent(EnqueueEvent); } /// @@ -388,6 +390,16 @@ namespace Content.Server.Construction } + case PartAssemblyConstructionGraphStep partAssemblyStep: + { + if (ev is not PartAssemblyPartInsertedEvent) + break; + + if (partAssemblyStep.Condition(uid, EntityManager)) + return HandleResult.True; + return HandleResult.False; + } + #endregion // --- CONSTRUCTION STEP EVENT HANDLING FINISH --- diff --git a/Content.Server/Destructible/Thresholds/Behaviors/EmptyContainersBehaviour.cs b/Content.Server/Destructible/Thresholds/Behaviors/EmptyContainersBehaviour.cs new file mode 100644 index 0000000000..abd5c531a6 --- /dev/null +++ b/Content.Server/Destructible/Thresholds/Behaviors/EmptyContainersBehaviour.cs @@ -0,0 +1,40 @@ +using Content.Shared.Random.Helpers; +using Robust.Server.Containers; +using Robust.Shared.Containers; + +namespace Content.Server.Destructible.Thresholds.Behaviors +{ + /// + /// Drop all items from specified containers + /// + [DataDefinition] + public sealed class EmptyContainersBehaviour : IThresholdBehavior + { + [DataField("containers")] + public List Containers = new(); + + [DataField("randomOffset")] + public float RandomOffset = 0.25f; + + public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null) + { + if (!system.EntityManager.TryGetComponent(owner, out var containerManager)) + return; + + var containerSys = system.EntityManager.System(); + + + foreach (var containerId in Containers) + { + if (!containerSys.TryGetContainer(owner, containerId, out var container, containerManager)) + continue; + + var entities = containerSys.EmptyContainer(container, true); + foreach (var ent in entities) + { + ent.RandomOffset(RandomOffset); + } + } + } + } +} diff --git a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs index 1b1ce65948..592f30adee 100644 --- a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs @@ -274,7 +274,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem var prefList = new List(); foreach (var player in allPlayers) { - if (player.AttachedEntity == null || !HasComp(player.AttachedEntity)) + if (player.AttachedEntity == null || !HasComp(player.AttachedEntity) || HasComp(player.AttachedEntity)) continue; playerList.Add(player); diff --git a/Content.Server/Ghost/Roles/Components/ToggleableGhostRoleComponent.cs b/Content.Server/Ghost/Roles/Components/ToggleableGhostRoleComponent.cs new file mode 100644 index 0000000000..e00587d069 --- /dev/null +++ b/Content.Server/Ghost/Roles/Components/ToggleableGhostRoleComponent.cs @@ -0,0 +1,38 @@ +namespace Content.Server.Ghost.Roles.Components; + +/// +/// This is used for a ghost role which can be toggled on and off at will, like a PAI. +/// +[RegisterComponent] +public sealed class ToggleableGhostRoleComponent : Component +{ + [DataField("examineTextMindPresent")] + public string ExamineTextMindPresent = string.Empty; + + [DataField("examineTextMindSearching")] + public string ExamineTextMindSearching = string.Empty; + + [DataField("examineTextNoMind")] + public string ExamineTextNoMind = string.Empty; + + [DataField("beginSearchingText")] + public string BeginSearchingText = string.Empty; + + [DataField("roleName")] + public string RoleName = string.Empty; + + [DataField("roleDescription")] + public string RoleDescription = string.Empty; + + [DataField("wipeVerbText")] + public string WipeVerbText = string.Empty; + + [DataField("wipeVerbPopup")] + public string WipeVerbPopup = string.Empty; + + [DataField("stopSearchVerbText")] + public string StopSearchVerbText = string.Empty; + + [DataField("stopSearchVerbPopup")] + public string StopSearchVerbPopup = string.Empty; +} diff --git a/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs b/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs new file mode 100644 index 0000000000..e2fef7d90b --- /dev/null +++ b/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs @@ -0,0 +1,140 @@ +using Content.Server.Ghost.Roles.Components; +using Content.Server.Mind.Components; +using Content.Server.PAI; +using Content.Shared.Examine; +using Content.Shared.Interaction.Events; +using Content.Shared.Mind; +using Content.Shared.Popups; +using Content.Shared.Verbs; + +namespace Content.Server.Ghost.Roles; + +/// +/// This handles logic and interaction related to +/// +public sealed class ToggleableGhostRoleSystem : EntitySystem +{ + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + //todo this really shouldn't be in here but this system was converted from PAIs + [Dependency] private readonly PAISystem _pai = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnMindAdded); + SubscribeLocalEvent(OnMindRemoved); + SubscribeLocalEvent>(AddWipeVerb); + } + + private void OnUseInHand(EntityUid uid, ToggleableGhostRoleComponent component, UseInHandEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + + // check if a mind is present + if (TryComp(uid, out var mind) && mind.HasMind) + { + _popup.PopupEntity(Loc.GetString(component.ExamineTextMindPresent), uid, args.User, PopupType.Large); + return; + } + if (HasComp(uid)) + { + _popup.PopupEntity(Loc.GetString(component.ExamineTextMindSearching), uid, args.User); + return; + } + _popup.PopupEntity(Loc.GetString(component.BeginSearchingText), uid, args.User); + + UpdateAppearance(uid, ToggleableGhostRoleStatus.Searching); + + var ghostRole = EnsureComp(uid); + EnsureComp(uid); + ghostRole.RoleName = Loc.GetString(component.RoleName); + ghostRole.RoleDescription = Loc.GetString(component.RoleDescription); + } + + private void OnExamined(EntityUid uid, ToggleableGhostRoleComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + if (TryComp(uid, out var mind) && mind.HasMind) + { + args.PushMarkup(Loc.GetString(component.ExamineTextMindPresent)); + } + else if (HasComp(uid)) + { + args.PushMarkup(Loc.GetString(component.ExamineTextMindSearching)); + } + else + { + args.PushMarkup(Loc.GetString(component.ExamineTextNoMind)); + } + } + + private void OnMindAdded(EntityUid uid, ToggleableGhostRoleComponent pai, MindAddedMessage args) + { + // Mind was added, shutdown the ghost role stuff so it won't get in the way + RemComp(uid); + UpdateAppearance(uid, ToggleableGhostRoleStatus.On); + } + + private void OnMindRemoved(EntityUid uid, ToggleableGhostRoleComponent component, MindRemovedMessage args) + { + UpdateAppearance(uid, ToggleableGhostRoleStatus.Off); + } + + private void UpdateAppearance(EntityUid uid, ToggleableGhostRoleStatus status) + { + _appearance.SetData(uid, ToggleableGhostRoleVisuals.Status, status); + } + + private void AddWipeVerb(EntityUid uid, ToggleableGhostRoleComponent component, GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + if (TryComp(uid, out var mind) && mind.HasMind) + { + ActivationVerb verb = new() + { + Text = Loc.GetString(component.WipeVerbText), + Act = () => + { + if (component.Deleted || !HasComp(uid)) + return; + // Wiping device :( + // The shutdown of the Mind should cause automatic reset of the pAI during OnMindRemoved + // EDIT: But it doesn't!!!! Wtf? Do stuff manually + RemComp(uid); + _popup.PopupEntity(Loc.GetString(component.WipeVerbPopup), uid, args.User, PopupType.Large); + UpdateAppearance(uid, ToggleableGhostRoleStatus.Off); + _pai.PAITurningOff(uid); + } + }; + args.Verbs.Add(verb); + } + else if (HasComp(uid)) + { + ActivationVerb verb = new() + { + Text = Loc.GetString(component.StopSearchVerbText), + Act = () => + { + if (component.Deleted || !HasComp(uid)) + return; + RemComp(uid); + RemComp(uid); + _popup.PopupEntity(Loc.GetString(component.StopSearchVerbPopup), uid, args.User); + UpdateAppearance(uid, ToggleableGhostRoleStatus.Off); + _pai.PAITurningOff(uid); + } + }; + args.Verbs.Add(verb); + } + } +} diff --git a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs index 0902602953..2c23057e2d 100644 --- a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs +++ b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs @@ -23,6 +23,7 @@ namespace Content.Server.Light.EntitySystems [UsedImplicitly] public sealed class HandheldLightSystem : SharedHandheldLightSystem { + [Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PowerCellSystem _powerCell = default!; [Dependency] private readonly IPrototypeManager _proto = default!; @@ -40,6 +41,9 @@ namespace Content.Server.Light.EntitySystems SubscribeLocalEvent(OnRemove); SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnExamine); SubscribeLocalEvent>(AddToggleLightVerb); @@ -100,6 +104,24 @@ namespace Content.Server.Light.EntitySystems args.State = new HandheldLightComponent.HandheldLightComponentState(component.Activated, GetLevel(uid, component)); } + private void OnMapInit(EntityUid uid, HandheldLightComponent component, MapInitEvent args) + { + if (component.ToggleAction == null + && _proto.TryIndex(component.ToggleActionId, out InstantActionPrototype? act)) + { + component.ToggleAction = new(act); + } + + if (component.ToggleAction != null) + _actions.AddAction(uid, component.ToggleAction, null); + } + + private void OnShutdown(EntityUid uid, HandheldLightComponent component, ComponentShutdown args) + { + if (component.ToggleAction != null) + _actions.RemoveAction(uid, component.ToggleAction); + } + private byte? GetLevel(EntityUid uid, HandheldLightComponent component) { // Curently every single flashlight has the same number of levels for status and that's all it uses the charge for @@ -121,7 +143,7 @@ namespace Content.Server.Light.EntitySystems private void OnActivate(EntityUid uid, HandheldLightComponent component, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !component.ToggleOnInteract) return; if (ToggleStatus(args.User, uid, component)) @@ -176,7 +198,7 @@ namespace Content.Server.Light.EntitySystems private void AddToggleLightVerb(EntityUid uid, HandheldLightComponent component, GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract) + if (!args.CanAccess || !args.CanInteract || !component.ToggleOnInteract) return; ActivationVerb verb = new() diff --git a/Content.Server/Medical/HealingSystem.cs b/Content.Server/Medical/HealingSystem.cs index 6263749a5b..9dbc404892 100644 --- a/Content.Server/Medical/HealingSystem.cs +++ b/Content.Server/Medical/HealingSystem.cs @@ -86,6 +86,9 @@ public sealed class HealingSystem : EntitySystem // Re-verify that we can heal the damage. _stacks.Use(args.Used.Value, 1); + if (_stacks.GetCount(args.Used.Value) <= 0) + dontRepeat = true; + if (uid != args.User) { _adminLogger.Add(LogType.Healed, @@ -166,11 +169,8 @@ public sealed class HealingSystem : EntitySystem return false; } - if (component.HealingBeginSound != null) - { - _audio.PlayPvs(component.HealingBeginSound, uid, + _audio.PlayPvs(component.HealingBeginSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f)); - } var isNotSelf = user != target; diff --git a/Content.Server/Mind/MindSystem.cs b/Content.Server/Mind/MindSystem.cs index 6220637ea6..da004d40cc 100644 --- a/Content.Server/Mind/MindSystem.cs +++ b/Content.Server/Mind/MindSystem.cs @@ -27,11 +27,11 @@ public sealed class MindSystem : EntitySystem { [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly ActorSystem _actor = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly GhostSystem _ghostSystem = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly ActorSystem _actor = default!; // This is dictionary is required to track the minds of disconnected players that may have had their entity deleted. private readonly Dictionary _userMinds = new(); @@ -128,8 +128,8 @@ public sealed class MindSystem : EntitySystem if (!Resolve(uid, ref mind, false)) return; - RaiseLocalEvent(uid, new MindRemovedMessage(), true); mind.Mind = null; + RaiseLocalEvent(uid, new MindRemovedMessage(), true); } private void OnVisitingTerminating(EntityUid uid, VisitingMindComponent component, ref EntityTerminatingEvent args) @@ -414,10 +414,6 @@ public sealed class MindSystem : EntitySystem InternalEjectMind(oldEntity.Value, oldComp); SetOwnedEntity(mind, entity, component); - if (mind.OwnedComponent != null){ - InternalAssignMind(mind.OwnedEntity!.Value, mind, mind.OwnedComponent); - mind.OriginalOwnedEntity ??= mind.OwnedEntity; - } // Don't do the full deletion cleanup if we're transferring to our VisitingEntity if (alreadyAttached) @@ -438,9 +434,15 @@ public sealed class MindSystem : EntitySystem // Player is CURRENTLY connected. if (mind.Session != null && !alreadyAttached && mind.VisitingEntity == null) { - mind.Session.AttachToEntity(entity); + _actor.Attach(entity, mind.Session, true); Log.Info($"Session {mind.Session.Name} transferred to entity {entity}."); } + + if (mind.OwnedComponent != null) + { + InternalAssignMind(mind.OwnedEntity!.Value, mind, mind.OwnedComponent); + mind.OriginalOwnedEntity ??= mind.OwnedEntity; + } } /// @@ -559,7 +561,7 @@ public sealed class MindSystem : EntitySystem public bool TryGetMind(EntityUid uid, [NotNullWhen(true)] out Mind? mind, MindContainerComponent? mindContainerComponent = null) { mind = null; - if (!Resolve(uid, ref mindContainerComponent)) + if (!Resolve(uid, ref mindContainerComponent, false)) return false; if (!mindContainerComponent.HasMind) diff --git a/Content.Server/NameIdentifier/NameIdentifierComponent.cs b/Content.Server/NameIdentifier/NameIdentifierComponent.cs deleted file mode 100644 index ff3d1a8898..0000000000 --- a/Content.Server/NameIdentifier/NameIdentifierComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Shared.NameIdentifier; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.NameIdentifier; - -[RegisterComponent] -public sealed class NameIdentifierComponent : Component -{ - [DataField("group", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] - public string Group = string.Empty; - - /// - /// The randomly generated ID for this entity. - /// - [ViewVariables(VVAccess.ReadWrite), DataField("identifier")] - public int Identifier = -1; -} diff --git a/Content.Server/NameIdentifier/NameIdentifierSystem.cs b/Content.Server/NameIdentifier/NameIdentifierSystem.cs index 82875acec5..bde8466d06 100644 --- a/Content.Server/NameIdentifier/NameIdentifierSystem.cs +++ b/Content.Server/NameIdentifier/NameIdentifierSystem.cs @@ -1,10 +1,8 @@ -using System.Linq; -using Content.Shared.GameTicking; +using Content.Shared.GameTicking; using Content.Shared.NameIdentifier; using Robust.Shared.Collections; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Utility; namespace Content.Server.NameIdentifier; @@ -15,12 +13,13 @@ public sealed class NameIdentifierSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; /// /// Free IDs available per . /// [ViewVariables] - public Dictionary> CurrentIds = new(); + public readonly Dictionary> CurrentIds = new(); public override void Initialize() { @@ -103,11 +102,16 @@ public sealed class NameIdentifierSystem : EntitySystem component.Identifier = id; } + component.FullIdentifier = group.FullName + ? uniqueName + : $"({uniqueName})"; + var meta = MetaData(uid); // "DR-1234" as opposed to "drone (DR-1234)" - meta.EntityName = group.FullName + _metaData.SetEntityName(uid, group.FullName ? uniqueName - : $"{meta.EntityName} ({uniqueName})"; + : $"{meta.EntityName} ({uniqueName})", meta); + Dirty(component); } private void InitialSetupPrototypes() diff --git a/Content.Server/PAI/PAISystem.cs b/Content.Server/PAI/PAISystem.cs index 69496afe5d..27ab1ba165 100644 --- a/Content.Server/PAI/PAISystem.cs +++ b/Content.Server/PAI/PAISystem.cs @@ -1,91 +1,45 @@ -using Content.Server.Ghost.Roles.Components; using Content.Server.Instruments; using Content.Server.Mind.Components; -using Content.Server.Popups; -using Content.Shared.Examine; using Content.Shared.Interaction.Events; using Content.Shared.PAI; -using Content.Shared.Popups; -using Content.Shared.Verbs; using Robust.Server.GameObjects; namespace Content.Server.PAI { public sealed class PAISystem : SharedPAISystem { - [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly InstrumentSystem _instrumentSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnMindAdded); SubscribeLocalEvent(OnMindRemoved); - SubscribeLocalEvent>(AddWipeVerb); - } - - private void OnExamined(EntityUid uid, PAIComponent component, ExaminedEvent args) - { - if (args.IsInDetailsRange) - { - if (EntityManager.TryGetComponent(uid, out var mind) && mind.HasMind) - { - args.PushMarkup(Loc.GetString("pai-system-pai-installed")); - } - else if (EntityManager.HasComponent(uid)) - { - args.PushMarkup(Loc.GetString("pai-system-still-searching")); - } - else - { - args.PushMarkup(Loc.GetString("pai-system-off")); - } - } } private void OnUseInHand(EntityUid uid, PAIComponent component, UseInHandEvent args) { - if (args.Handled) - return; + if (!TryComp(uid, out var mind) || !mind.HasMind) + component.LastUser = args.User; + } - // Placeholder PAIs are essentially portable ghost role generators. - - args.Handled = true; - - // Check for pAI activation - if (EntityManager.TryGetComponent(uid, out var mind) && mind.HasMind) - { - _popupSystem.PopupEntity(Loc.GetString("pai-system-pai-installed"), uid, args.User, PopupType.Large); + private void OnMindAdded(EntityUid uid, PAIComponent component, MindAddedMessage args) + { + if (component.LastUser == null) return; - } - else if (EntityManager.HasComponent(uid)) - { - _popupSystem.PopupEntity(Loc.GetString("pai-system-still-searching"), uid, args.User); - return; - } // Ownership tag - string val = Loc.GetString("pai-system-pai-name", ("owner", args.User)); + var val = Loc.GetString("pai-system-pai-name", ("owner", component.LastUser)); // TODO Identity? People shouldn't dox-themselves by carrying around a PAI. // But having the pda's name permanently be "old lady's PAI" is weird. // Changing the PAI's identity in a way that ties it to the owner's identity also seems weird. // Cause then you could remotely figure out information about the owner's equipped items. - EntityManager.GetComponent(component.Owner).EntityName = val; - - var ghostRole = EnsureComp(uid); - EnsureComp(uid); - - ghostRole.RoleName = Loc.GetString("pai-system-role-name"); - ghostRole.RoleDescription = Loc.GetString("pai-system-role-description"); - - _popupSystem.PopupEntity(Loc.GetString("pai-system-searching"), uid, args.User); - UpdatePAIAppearance(uid, PAIStatus.Searching); + _metaData.SetEntityName(uid, val); } private void OnMindRemoved(EntityUid uid, PAIComponent component, MindRemovedMessage args) @@ -94,18 +48,8 @@ namespace Content.Server.PAI PAITurningOff(uid); } - private void OnMindAdded(EntityUid uid, PAIComponent pai, MindAddedMessage args) + public void PAITurningOff(EntityUid uid) { - // Mind was added, shutdown the ghost role stuff so it won't get in the way - if (EntityManager.HasComponent(uid)) - EntityManager.RemoveComponent(uid); - UpdatePAIAppearance(uid, PAIStatus.On); - } - - private void PAITurningOff(EntityUid uid) - { - UpdatePAIAppearance(uid, PAIStatus.Off); - // Close the instrument interface if it was open // before closing if (HasComp(uid) && TryComp(uid, out var actor)) @@ -114,63 +58,12 @@ namespace Content.Server.PAI } // Stop instrument - if (EntityManager.TryGetComponent(uid, out var instrument)) _instrumentSystem.Clean(uid, instrument); - if (EntityManager.TryGetComponent(uid, out var metadata)) + if (TryComp(uid, out var instrument)) _instrumentSystem.Clean(uid, instrument); + if (TryComp(uid, out var metadata)) { var proto = metadata.EntityPrototype; if (proto != null) - metadata.EntityName = proto.Name; - } - } - - private void UpdatePAIAppearance(EntityUid uid, PAIStatus status) - { - if (EntityManager.TryGetComponent(uid, out var appearance)) - { - _appearance.SetData(uid, PAIVisuals.Status, status, appearance); - } - } - - private void AddWipeVerb(EntityUid uid, PAIComponent pai, GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - if (EntityManager.TryGetComponent(uid, out var mind) && mind.HasMind) - { - ActivationVerb verb = new(); - verb.Text = Loc.GetString("pai-system-wipe-device-verb-text"); - verb.Act = () => { - if (pai.Deleted) - return; - // Wiping device :( - // The shutdown of the Mind should cause automatic reset of the pAI during OnMindRemoved - // EDIT: But it doesn't!!!! Wtf? Do stuff manually - if (EntityManager.HasComponent(uid)) - { - EntityManager.RemoveComponent(uid); - _popupSystem.PopupEntity(Loc.GetString("pai-system-wiped-device"), uid, args.User, PopupType.Large); - PAITurningOff(uid); - } - }; - args.Verbs.Add(verb); - } - else if (EntityManager.HasComponent(uid)) - { - ActivationVerb verb = new(); - verb.Text = Loc.GetString("pai-system-stop-searching-verb-text"); - verb.Act = () => { - if (pai.Deleted) - return; - if (EntityManager.HasComponent(uid)) - { - EntityManager.RemoveComponent(uid); - EntityManager.RemoveComponent(uid); - _popupSystem.PopupEntity(Loc.GetString("pai-system-stopped-searching"), uid, args.User); - PAITurningOff(uid); - } - }; - args.Verbs.Add(verb); + _metaData.SetEntityName(uid, proto.Name); } } } diff --git a/Content.Server/Power/EntitySystems/ChargerSystem.cs b/Content.Server/Power/EntitySystems/ChargerSystem.cs index 96a71fc02a..f3a4d6c68f 100644 --- a/Content.Server/Power/EntitySystems/ChargerSystem.cs +++ b/Content.Server/Power/EntitySystems/ChargerSystem.cs @@ -1,22 +1,24 @@ using Content.Server.Construction; using Content.Server.Power.Components; using Content.Server.PowerCell; -using Content.Shared.Containers.ItemSlots; using Content.Shared.Examine; using Content.Shared.Power; using Content.Shared.PowerCell.Components; using JetBrains.Annotations; using Robust.Shared.Containers; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Shared.Storage.Components; +using Robust.Server.Containers; namespace Content.Server.Power.EntitySystems; [UsedImplicitly] internal sealed class ChargerSystem : EntitySystem { - [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; - [Dependency] private readonly PowerCellSystem _cellSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _sharedAppearanceSystem = default!; + [Dependency] private readonly ContainerSystem _container = default!; + [Dependency] private readonly PowerCellSystem _powerCell = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; public override void Initialize() { @@ -27,6 +29,7 @@ internal sealed class ChargerSystem : EntitySystem SubscribeLocalEvent(OnInserted); SubscribeLocalEvent(OnRemoved); SubscribeLocalEvent(OnInsertAttempt); + SubscribeLocalEvent(OnEntityStorageInsertAttempt); SubscribeLocalEvent(OnChargerExamine); } @@ -42,15 +45,19 @@ internal sealed class ChargerSystem : EntitySystem public override void Update(float frameTime) { - foreach (var (_, charger, slotComp) in EntityManager.EntityQuery()) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _, out var charger, out var containerComp)) { - if (!_itemSlotsSystem.TryGetSlot(charger.Owner, charger.SlotId, out ItemSlot? slot, slotComp)) + if (!_container.TryGetContainer(uid, charger.SlotId, out var container, containerComp)) continue; - if (charger.Status == CellChargerStatus.Empty || charger.Status == CellChargerStatus.Charged || !slot.HasItem) + if (charger.Status == CellChargerStatus.Empty || charger.Status == CellChargerStatus.Charged || container.ContainedEntities.Count == 0) continue; - TransferPower(charger.Owner, slot.Item!.Value, charger, frameTime); + foreach (var contained in container.ContainedEntities) + { + TransferPower(uid, contained, charger, frameTime); + } } } @@ -100,23 +107,32 @@ internal sealed class ChargerSystem : EntitySystem if (args.Container.ID != component.SlotId) return; - if (!TryComp(args.EntityUid, out PowerCellSlotComponent? cellSlot)) + if (!TryComp(args.EntityUid, out var cellSlot)) return; - if (!_itemSlotsSystem.TryGetSlot(args.EntityUid, cellSlot.CellSlotId, out ItemSlot? itemSlot)) - return; - - if (!cellSlot.FitsInCharger || !itemSlot.HasItem) + if (!cellSlot.FitsInCharger) args.Cancel(); } + private void OnEntityStorageInsertAttempt(EntityUid uid, ChargerComponent component, ref InsertIntoEntityStorageAttemptEvent args) + { + if (!component.Initialized || args.Cancelled) + return; + + if (!TryComp(uid, out var cellSlot)) + return; + + if (!cellSlot.FitsInCharger) + args.Cancelled = true; + } + private void UpdateStatus(EntityUid uid, ChargerComponent component) { var status = GetStatus(uid, component); if (component.Status == status || !TryComp(uid, out ApcPowerReceiverComponent? receiver)) return; - if (!_itemSlotsSystem.TryGetSlot(uid, component.SlotId, out ItemSlot? slot)) + if (!_container.TryGetContainer(uid, component.SlotId, out var container)) return; TryComp(uid, out AppearanceComponent? appearance); @@ -136,25 +152,25 @@ internal sealed class ChargerSystem : EntitySystem { case CellChargerStatus.Off: receiver.Load = 0; - _sharedAppearanceSystem.SetData(uid, CellVisual.Light, CellChargerStatus.Off, appearance); + _appearance.SetData(uid, CellVisual.Light, CellChargerStatus.Off, appearance); break; case CellChargerStatus.Empty: receiver.Load = 0; - _sharedAppearanceSystem.SetData(uid, CellVisual.Light, CellChargerStatus.Empty, appearance); + _appearance.SetData(uid, CellVisual.Light, CellChargerStatus.Empty, appearance); break; case CellChargerStatus.Charging: receiver.Load = component.ChargeRate; - _sharedAppearanceSystem.SetData(uid, CellVisual.Light, CellChargerStatus.Charging, appearance); + _appearance.SetData(uid, CellVisual.Light, CellChargerStatus.Charging, appearance); break; case CellChargerStatus.Charged: receiver.Load = 0; - _sharedAppearanceSystem.SetData(uid, CellVisual.Light, CellChargerStatus.Charged, appearance); + _appearance.SetData(uid, CellVisual.Light, CellChargerStatus.Charged, appearance); break; default: throw new ArgumentOutOfRangeException(); } - _sharedAppearanceSystem.SetData(uid, CellVisual.Occupied, slot.HasItem, appearance); + _appearance.SetData(uid, CellVisual.Occupied, container.ContainedEntities.Count != 0, appearance); } private CellChargerStatus GetStatus(EntityUid uid, ChargerComponent component) @@ -171,16 +187,16 @@ internal sealed class ChargerSystem : EntitySystem if (!apcPowerReceiverComponent.Powered) return CellChargerStatus.Off; - if (!_itemSlotsSystem.TryGetSlot(uid, component.SlotId, out ItemSlot? slot)) + if (!_container.TryGetContainer(uid, component.SlotId, out var container)) return CellChargerStatus.Off; - if (!slot.HasItem) + if (container.ContainedEntities.Count == 0) return CellChargerStatus.Empty; - if (!SearchForBattery(slot.Item!.Value, out BatteryComponent? heldBattery)) + if (!SearchForBattery(container.ContainedEntities.First(), out var heldBattery)) return CellChargerStatus.Off; - if (heldBattery != null && Math.Abs(heldBattery.MaxCharge - heldBattery.CurrentCharge) < 0.01) + if (Math.Abs(heldBattery.MaxCharge - heldBattery.CurrentCharge) < 0.01) return CellChargerStatus.Charged; return CellChargerStatus.Charging; @@ -194,7 +210,7 @@ internal sealed class ChargerSystem : EntitySystem if (!receiverComponent.Powered) return; - if (!SearchForBattery(targetEntity, out BatteryComponent? heldBattery)) + if (!SearchForBattery(targetEntity, out var heldBattery)) return; heldBattery.CurrentCharge += component.ChargeRate * frameTime; @@ -213,7 +229,7 @@ internal sealed class ChargerSystem : EntitySystem if (!TryComp(uid, out component)) { // or by checking for a power cell slot on the inserted entity - return _cellSystem.TryGetBatteryFromSlot(uid, out component); + return _powerCell.TryGetBatteryFromSlot(uid, out component); } return true; } diff --git a/Content.Server/PowerCell/PowerCellSystem.cs b/Content.Server/PowerCell/PowerCellSystem.cs index f239af4bdc..0f0bf12022 100644 --- a/Content.Server/PowerCell/PowerCellSystem.cs +++ b/Content.Server/PowerCell/PowerCellSystem.cs @@ -88,6 +88,9 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem { base.OnCellRemoved(uid, component, args); + if (args.Container.ID != component.CellSlotId) + return; + var ev = new PowerCellSlotEmptyEvent(); RaiseLocalEvent(uid, ref ev); } @@ -135,7 +138,7 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem if (!Resolve(uid, ref battery, ref cell, false)) return true; - return HasCharge(uid, float.MinValue, cell, user); + return HasCharge(uid, battery.DrawRate, cell, user); } #endregion @@ -229,19 +232,26 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem private void OnCellExamined(EntityUid uid, PowerCellComponent component, ExaminedEvent args) { - if (TryComp(uid, out var battery)) - OnBatteryExamined(uid, battery, args); + TryComp(uid, out var battery); + OnBatteryExamined(uid, battery, args); } private void OnCellSlotExamined(EntityUid uid, PowerCellSlotComponent component, ExaminedEvent args) { - if (TryGetBatteryFromSlot(uid, out var battery)) - OnBatteryExamined(uid, battery, args); + TryGetBatteryFromSlot(uid, out var battery); + OnBatteryExamined(uid, battery, args); } - private void OnBatteryExamined(EntityUid uid, BatteryComponent component, ExaminedEvent args) + private void OnBatteryExamined(EntityUid uid, BatteryComponent? component, ExaminedEvent args) { - var charge = component.CurrentCharge / component.MaxCharge * 100; - args.PushMarkup(Loc.GetString("power-cell-component-examine-details", ("currentCharge", $"{charge:F0}"))); + if (component != null) + { + var charge = component.CurrentCharge / component.MaxCharge * 100; + args.PushMarkup(Loc.GetString("power-cell-component-examine-details", ("currentCharge", $"{charge:F0}"))); + } + else + { + args.PushMarkup(Loc.GetString("power-cell-component-examine-details-no-battery")); + } } } diff --git a/Content.Server/Repairable/RepairableComponent.cs b/Content.Server/Repairable/RepairableComponent.cs index e122c492cb..911397ec94 100644 --- a/Content.Server/Repairable/RepairableComponent.cs +++ b/Content.Server/Repairable/RepairableComponent.cs @@ -31,5 +31,11 @@ namespace Content.Server.Repairable /// [ViewVariables(VVAccess.ReadWrite)] [DataField("selfRepairPenalty")] public float SelfRepairPenalty = 3f; + + /// + /// Whether or not an entity is allowed to repair itself. + /// + [DataField("allowSelfRepair")] + public bool AllowSelfRepair = true; } } diff --git a/Content.Server/Repairable/RepairableSystem.cs b/Content.Server/Repairable/RepairableSystem.cs index cba4f8e8aa..8d9833a66f 100644 --- a/Content.Server/Repairable/RepairableSystem.cs +++ b/Content.Server/Repairable/RepairableSystem.cs @@ -62,7 +62,12 @@ namespace Content.Server.Repairable // Add a penalty to how long it takes if the user is repairing itself if (args.User == args.Target) + { + if (!component.AllowSelfRepair) + return; + delay *= component.SelfRepairPenalty; + } // Run the repairing doafter args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, delay, component.QualityNeeded, new RepairFinishedEvent()); diff --git a/Content.Server/Silicons/Borgs/BorgSystem.MMI.cs b/Content.Server/Silicons/Borgs/BorgSystem.MMI.cs new file mode 100644 index 0000000000..34e00001b9 --- /dev/null +++ b/Content.Server/Silicons/Borgs/BorgSystem.MMI.cs @@ -0,0 +1,79 @@ +using Content.Server.Mind.Components; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Silicons.Borgs.Components; +using Robust.Shared.Containers; + +namespace Content.Server.Silicons.Borgs; + +/// +public sealed partial class BorgSystem +{ + public void InitializeMMI() + { + SubscribeLocalEvent(OnMMIInit); + SubscribeLocalEvent(OnMMIEntityInserted); + SubscribeLocalEvent(OnMMIMindAdded); + SubscribeLocalEvent(OnMMIMindRemoved); + + SubscribeLocalEvent(OnMMILinkedMindAdded); + SubscribeLocalEvent(OnMMILinkedRemoved); + } + + private void OnMMIInit(EntityUid uid, MMIComponent component, ComponentInit args) + { + if (!TryComp(uid, out var itemSlots)) + return; + + if (ItemSlots.TryGetSlot(uid, component.BrainSlotId, out var slot, itemSlots)) + component.BrainSlot = slot; + else + ItemSlots.AddItemSlot(uid, component.BrainSlotId, component.BrainSlot, itemSlots); + } + + private void OnMMIEntityInserted(EntityUid uid, MMIComponent component, EntInsertedIntoContainerMessage args) + { + if (args.Container.ID != component.BrainSlotId) + return; + + var ent = args.Entity; + var linked = EnsureComp(ent); + linked.LinkedMMI = uid; + + if (_mind.TryGetMind(ent, out var mind)) + _mind.TransferTo(mind, uid, true); + + _appearance.SetData(uid, MMIVisuals.BrainPresent, true); + } + + private void OnMMIMindAdded(EntityUid uid, MMIComponent component, MindAddedMessage args) + { + _appearance.SetData(uid, MMIVisuals.HasMind, true); + } + + private void OnMMIMindRemoved(EntityUid uid, MMIComponent component, MindRemovedMessage args) + { + _appearance.SetData(uid, MMIVisuals.HasMind, false); + } + + private void OnMMILinkedMindAdded(EntityUid uid, MMILinkedComponent component, MindAddedMessage args) + { + if (!_mind.TryGetMind(uid, out var mind) || component.LinkedMMI == null) + return; + _mind.TransferTo(mind, component.LinkedMMI, true); + } + + private void OnMMILinkedRemoved(EntityUid uid, MMILinkedComponent component, EntGotRemovedFromContainerMessage args) + { + if (Terminating(uid)) + return; + + if (component.LinkedMMI is not { } linked) + return; + RemComp(uid, component); + + if (_mind.TryGetMind(linked, out var mind)) + _mind.TransferTo(mind, uid, true); + + _appearance.SetData(linked, MMIVisuals.BrainPresent, false); + } +} diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs new file mode 100644 index 0000000000..ee45757304 --- /dev/null +++ b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs @@ -0,0 +1,316 @@ +using System.Linq; +using Content.Shared.Hands.Components; +using Content.Shared.Interaction.Components; +using Content.Shared.Silicons.Borgs.Components; +using Robust.Shared.Containers; + +namespace Content.Server.Silicons.Borgs; + +/// +public sealed partial class BorgSystem +{ + public void InitializeModules() + { + SubscribeLocalEvent(OnModuleGotInserted); + SubscribeLocalEvent(OnModuleGotRemoved); + + SubscribeLocalEvent(OnSelectableInstalled); + SubscribeLocalEvent(OnSelectableUninstalled); + SubscribeLocalEvent(OnSelectableAction); + + SubscribeLocalEvent(OnProvideItemStartup); + SubscribeLocalEvent(OnItemModuleSelected); + SubscribeLocalEvent(OnItemModuleUnselected); + } + + private void OnModuleGotInserted(EntityUid uid, BorgModuleComponent component, EntGotInsertedIntoContainerMessage args) + { + var chassis = args.Container.Owner; + + if (!TryComp(chassis, out var chassisComp) || + args.Container != chassisComp.ModuleContainer || + !chassisComp.Activated) + return; + + if (!_powerCell.HasDrawCharge(uid)) + return; + + InstallModule(chassis, uid, chassisComp, component); + } + + private void OnModuleGotRemoved(EntityUid uid, BorgModuleComponent component, EntGotRemovedFromContainerMessage args) + { + var chassis = args.Container.Owner; + + if (Terminating(chassis)) + return; + + if (!TryComp(chassis, out var chassisComp) || + args.Container != chassisComp.ModuleContainer) + return; + + UninstallModule(chassis, uid, chassisComp, component); + } + + private void OnProvideItemStartup(EntityUid uid, ItemBorgModuleComponent component, ComponentStartup args) + { + component.ProvidedContainer = Container.EnsureContainer(uid, component.ProvidedContainerId); + } + + private void OnSelectableInstalled(EntityUid uid, SelectableBorgModuleComponent component, ref BorgModuleInstalledEvent args) + { + var chassis = args.ChassisEnt; + component.ModuleSwapAction.EntityIcon = uid; + _actions.AddAction(chassis, component.ModuleSwapAction, uid); + SelectModule(chassis, uid, moduleComp: component); + } + + private void OnSelectableUninstalled(EntityUid uid, SelectableBorgModuleComponent component, ref BorgModuleUninstalledEvent args) + { + var chassis = args.ChassisEnt; + _actions.RemoveProvidedActions(chassis, uid); + UnselectModule(chassis, uid, moduleComp: component); + } + + private void OnSelectableAction(EntityUid uid, SelectableBorgModuleComponent component, BorgModuleActionSelectedEvent args) + { + var chassis = args.Performer; + if (!TryComp(chassis, out var chassisComp)) + return; + + if (chassisComp.SelectedModule == uid) + { + UnselectModule(chassis, chassisComp.SelectedModule, chassisComp); + args.Handled = true; + return; + } + + UnselectModule(chassis, chassisComp.SelectedModule, chassisComp); + SelectModule(chassis, uid, chassisComp, component); + args.Handled = true; + } + + /// + /// Selects a module, enablind the borg to use its provided abilities. + /// + public void SelectModule(EntityUid chassis, + EntityUid moduleUid, + BorgChassisComponent? chassisComp = null, + SelectableBorgModuleComponent? moduleComp = null) + { + if (Terminating(chassis) || Deleted(chassis)) + return; + + if (!Resolve(chassis, ref chassisComp)) + return; + + if (chassisComp.SelectedModule != null) + return; + + if (chassisComp.SelectedModule == moduleUid) + return; + + if (!Resolve(moduleUid, ref moduleComp, false)) + return; + + var ev = new BorgModuleSelectedEvent(chassis); + RaiseLocalEvent(moduleUid, ref ev); + chassisComp.SelectedModule = moduleUid; + } + + /// + /// Unselects a module, removing its provided abilities + /// + public void UnselectModule(EntityUid chassis, + EntityUid? moduleUid, + BorgChassisComponent? chassisComp = null, + SelectableBorgModuleComponent? moduleComp = null) + { + if (Terminating(chassis) || Deleted(chassis)) + return; + + if (!Resolve(chassis, ref chassisComp)) + return; + + if (moduleUid == null) + return; + + if (chassisComp.SelectedModule != moduleUid) + return; + + if (!Resolve(moduleUid.Value, ref moduleComp, false)) + return; + + var ev = new BorgModuleUnselectedEvent(chassis); + RaiseLocalEvent(moduleUid.Value, ref ev); + chassisComp.SelectedModule = null; + } + + private void OnItemModuleSelected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleSelectedEvent args) + { + ProvideItems(args.Chassis, uid, component: component); + } + + private void OnItemModuleUnselected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleUnselectedEvent args) + { + RemoveProvidedItems(args.Chassis, uid, component: component); + } + + private void ProvideItems(EntityUid chassis, EntityUid uid, BorgChassisComponent? chassisComponent = null, ItemBorgModuleComponent? component = null) + { + if (!Resolve(chassis, ref chassisComponent) || !Resolve(uid, ref component)) + return; + + if (!TryComp(chassis, out var hands)) + return; + + var xform = Transform(chassis); + foreach (var itemProto in component.Items) + { + EntityUid item; + + if (!component.ItemsCreated) + { + item = Spawn(itemProto, xform.Coordinates); + } + else + { + item = component.ProvidedContainer.ContainedEntities + .FirstOrDefault(ent => Prototype(ent)?.ID == itemProto); + if (!item.IsValid()) + { + Log.Debug($"no items found: {component.ProvidedContainer.ContainedEntities.Count}"); + continue; + } + + component.ProvidedContainer.Remove(item, EntityManager, force: true); + } + + if (!item.IsValid()) + { + Log.Debug("no valid item"); + continue; + } + + var handId = $"{uid}-item{component.HandCounter}"; + component.HandCounter++; + _hands.AddHand(chassis, handId, HandLocation.Middle, hands); + _hands.DoPickup(chassis, hands.Hands[handId], item, hands); + EnsureComp(item); + component.ProvidedItems.Add(handId, item); + } + + component.ItemsCreated = true; + } + + private void RemoveProvidedItems(EntityUid chassis, EntityUid uid, BorgChassisComponent? chassisComponent = null, ItemBorgModuleComponent? component = null) + { + if (!Resolve(chassis, ref chassisComponent) || !Resolve(uid, ref component)) + return; + + if (!TryComp(chassis, out var hands)) + return; + + foreach (var (handId, item) in component.ProvidedItems) + { + if (!Deleted(item) && !Terminating(item)) + { + RemComp(item); + component.ProvidedContainer.Insert(item, EntityManager); + } + _hands.RemoveHand(chassis, handId, hands); + } + component.ProvidedItems.Clear(); + } + + /// + /// Checks if a given module can be inserted into a borg + /// + public bool CanInsertModule(EntityUid uid, EntityUid module, BorgChassisComponent? component = null, BorgModuleComponent? moduleComponent = null, EntityUid? user = null) + { + if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent)) + return false; + + if (component.ModuleContainer.ContainedEntities.Count >= component.MaxModules) + return false; + + if (component.ModuleWhitelist?.IsValid(module, EntityManager) == false) + { + if (user != null) + Popup.PopupEntity(Loc.GetString("borg-module-whitelist-deny"), uid, user.Value); + return false; + } + + return true; + } + + /// + /// Installs and activates all modules currently inside the borg's module container + /// + public void InstallAllModules(EntityUid uid, BorgChassisComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + var query = GetEntityQuery(); + foreach (var moduleEnt in new List(component.ModuleContainer.ContainedEntities)) + { + if (!query.TryGetComponent(moduleEnt, out var moduleComp)) + continue; + + InstallModule(uid, moduleEnt, component, moduleComp); + } + } + + /// + /// Deactivates all modules currently inside the borg's module container + /// + /// + /// + public void DisableAllModules(EntityUid uid, BorgChassisComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + var query = GetEntityQuery(); + foreach (var moduleEnt in new List(component.ModuleContainer.ContainedEntities)) + { + if (!query.TryGetComponent(moduleEnt, out var moduleComp)) + continue; + + UninstallModule(uid, moduleEnt, component, moduleComp); + } + } + + /// + /// Installs a single module into a borg. + /// + public void InstallModule(EntityUid uid, EntityUid module, BorgChassisComponent? component, BorgModuleComponent? moduleComponent = null) + { + if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent)) + return; + + if (moduleComponent.Installed) + return; + + moduleComponent.InstalledEntity = uid; + var ev = new BorgModuleInstalledEvent(uid); + RaiseLocalEvent(module, ref ev); + } + + /// + /// Uninstalls a single module from a borg. + /// + public void UninstallModule(EntityUid uid, EntityUid module, BorgChassisComponent? component, BorgModuleComponent? moduleComponent = null) + { + if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent)) + return; + + if (!moduleComponent.Installed) + return; + + moduleComponent.InstalledEntity = null; + var ev = new BorgModuleUninstalledEvent(uid); + RaiseLocalEvent(module, ref ev); + } +} diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Ui.cs b/Content.Server/Silicons/Borgs/BorgSystem.Ui.cs new file mode 100644 index 0000000000..4d79338db7 --- /dev/null +++ b/Content.Server/Silicons/Borgs/BorgSystem.Ui.cs @@ -0,0 +1,108 @@ +using System.Linq; +using Content.Server.UserInterface; +using Content.Shared.Database; +using Content.Shared.NameIdentifier; +using Content.Shared.PowerCell.Components; +using Content.Shared.Preferences; +using Content.Shared.Silicons.Borgs; +using Content.Shared.Silicons.Borgs.Components; + +namespace Content.Server.Silicons.Borgs; + +/// +public sealed partial class BorgSystem +{ + public void InitializeUI() + { + SubscribeLocalEvent(OnBeforeBorgUiOpen); + SubscribeLocalEvent(OnEjectBrainBuiMessage); + SubscribeLocalEvent(OnEjectBatteryBuiMessage); + SubscribeLocalEvent(OnSetNameBuiMessage); + SubscribeLocalEvent(OnRemoveModuleBuiMessage); + } + + private void OnBeforeBorgUiOpen(EntityUid uid, BorgChassisComponent component, BeforeActivatableUIOpenEvent args) + { + UpdateUI(uid, component); + } + + private void OnEjectBrainBuiMessage(EntityUid uid, BorgChassisComponent component, BorgEjectBrainBuiMessage args) + { + if (args.Session.AttachedEntity is not { } attachedEntity || component.BrainEntity is not { } brain) + return; + + _adminLog.Add(LogType.Action, LogImpact.Medium, + $"{ToPrettyString(attachedEntity):player} removed brain {ToPrettyString(brain)} from borg {ToPrettyString(uid)}"); + component.BrainContainer.Remove(brain, EntityManager); + _hands.TryPickupAnyHand(attachedEntity, brain); + UpdateUI(uid, component); + } + + private void OnEjectBatteryBuiMessage(EntityUid uid, BorgChassisComponent component, BorgEjectBatteryBuiMessage args) + { + if (args.Session.AttachedEntity is not { } attachedEntity || + !TryComp(uid, out var slotComp) || + !Container.TryGetContainer(uid, slotComp.CellSlotId, out var container) || + !container.ContainedEntities.Any()) + { + return; + } + + var ents = Container.EmptyContainer(container); + _hands.TryPickupAnyHand(attachedEntity, ents.First()); + } + + private void OnSetNameBuiMessage(EntityUid uid, BorgChassisComponent component, BorgSetNameBuiMessage args) + { + if (args.Session.AttachedEntity is not { } attachedEntity) + return; + + if (args.Name.Length > HumanoidCharacterProfile.MaxNameLength || + args.Name.Length == 0 || + string.IsNullOrWhiteSpace(args.Name) || + string.IsNullOrEmpty(args.Name)) + { + return; + } + + var name = args.Name.Trim(); + if (TryComp(uid, out var identifier)) + name = $"{name} {identifier.FullIdentifier}"; + + _metaData.SetEntityName(uid, name); + _adminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(attachedEntity):player} set borg \"{ToPrettyString(uid)}\"'s name to: {name}"); + } + + private void OnRemoveModuleBuiMessage(EntityUid uid, BorgChassisComponent component, BorgRemoveModuleBuiMessage args) + { + if (args.Session.AttachedEntity is not { } attachedEntity) + return; + + if (!component.ModuleContainer.Contains(args.Module)) + return; + + _adminLog.Add(LogType.Action, LogImpact.Medium, + $"{ToPrettyString(attachedEntity):player} removed module {ToPrettyString(args.Module)} from borg {ToPrettyString(uid)}"); + component.ModuleContainer.Remove(args.Module); + _hands.TryPickupAnyHand(attachedEntity, args.Module); + + UpdateUI(uid, component); + } + + public void UpdateUI(EntityUid uid, BorgChassisComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + var chargePercent = 0f; + var hasBattery = false; + if (_powerCell.TryGetBatteryFromSlot(uid, out var battery)) + { + hasBattery = true; + chargePercent = battery.Charge / battery.MaxCharge; + } + + var state = new BorgBuiState(chargePercent, hasBattery); + _ui.TrySetUiState(uid, BorgUiKey.Key, state); + } +} diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs new file mode 100644 index 0000000000..e64fbfb169 --- /dev/null +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -0,0 +1,318 @@ +using Content.Server.Actions; +using Content.Server.Administration.Logs; +using Content.Server.Administration.Managers; +using Content.Server.Hands.Systems; +using Content.Server.Mind; +using Content.Server.Mind.Components; +using Content.Server.Players.PlayTimeTracking; +using Content.Server.PowerCell; +using Content.Server.UserInterface; +using Content.Shared.Alert; +using Content.Shared.Database; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Movement.Systems; +using Content.Shared.PowerCell; +using Content.Shared.PowerCell.Components; +using Content.Shared.Silicons.Borgs; +using Content.Shared.Silicons.Borgs.Components; +using Content.Shared.Throwing; +using Content.Shared.Wires; +using Robust.Server.GameObjects; +using Robust.Server.Player; +using Robust.Shared.Containers; +using Robust.Shared.Random; + +namespace Content.Server.Silicons.Borgs; + +/// +public sealed partial class BorgSystem : SharedBorgSystem +{ + [Dependency] private readonly IAdminLogManager _adminLog = default!; + [Dependency] private readonly IBanManager _banManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly ActionsSystem _actions = default!; + [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; + [Dependency] private readonly PlayTimeTrackingSystem _playTimeTracking = default!; + [Dependency] private readonly PowerCellSystem _powerCell = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnChassisInteractUsing); + SubscribeLocalEvent(OnMindAdded); + SubscribeLocalEvent(OnMindRemoved); + SubscribeLocalEvent(OnPowerCellChanged); + SubscribeLocalEvent(OnPowerCellSlotEmpty); + SubscribeLocalEvent(OnUIOpenAttempt); + + SubscribeLocalEvent(OnBrainMindAdded); + + InitializeModules(); + InitializeMMI(); + InitializeUI(); + } + + private void OnMapInit(EntityUid uid, BorgChassisComponent component, MapInitEvent args) + { + UpdateBatteryAlert(uid); + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); + + var coordinates = Transform(uid).Coordinates; + + if (component.StartingBrain != null) + { + component.BrainContainer.Insert(Spawn(component.StartingBrain, coordinates), EntityManager); + } + + foreach (var startingModule in component.StartingModules) + { + component.ModuleContainer.Insert(Spawn(startingModule, coordinates), EntityManager); + } + } + + private void OnChassisInteractUsing(EntityUid uid, BorgChassisComponent component, AfterInteractUsingEvent args) + { + if (!args.CanReach || args.Handled || uid == args.User) + return; + + var used = args.Used; + TryComp(used, out var brain); + TryComp(used, out var module); + + if (TryComp(uid, out var panel) && !panel.Open) + { + if (brain != null || module != null) + { + Popup.PopupEntity(Loc.GetString("borg-panel-not-open"), uid, args.User); + } + return; + } + + if (component.BrainEntity == null && + brain != null && + component.BrainWhitelist?.IsValid(used) != false) + { + if (_mind.TryGetMind(used, out var mind) && mind.Session != null) + { + if (!CanPlayerBeBorgged(mind.Session, component)) + { + Popup.PopupEntity(Loc.GetString("borg-player-not-allowed"), used, args.User); + return; + } + } + + component.BrainContainer.Insert(used); + _adminLog.Add(LogType.Action, LogImpact.Medium, + $"{ToPrettyString(args.User):player} installed brain {ToPrettyString(used)} into borg {ToPrettyString(uid)}"); + args.Handled = true; + UpdateUI(uid, component); + } + + if (module != null && CanInsertModule(uid, used, component, module, args.User)) + { + component.ModuleContainer.Insert(used); + _adminLog.Add(LogType.Action, LogImpact.Low, + $"{ToPrettyString(args.User):player} installed module {ToPrettyString(used)} into borg {ToPrettyString(uid)}"); + args.Handled = true; + UpdateUI(uid, component); + } + } + + // todo: consider transferring over the ghost role? managing that might suck. + protected override void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args) + { + base.OnInserted(uid, component, args); + + if (HasComp(args.Entity) && _mind.TryGetMind(args.Entity, out var mind)) + { + _mind.TransferTo(mind, uid); + } + } + + protected override void OnRemoved(EntityUid uid, BorgChassisComponent component, EntRemovedFromContainerMessage args) + { + base.OnRemoved(uid, component, args); + + if (HasComp(args.Entity) && _mind.TryGetMind(uid, out var mind)) + { + _mind.TransferTo(mind, args.Entity); + } + } + + private void OnMindAdded(EntityUid uid, BorgChassisComponent component, MindAddedMessage args) + { + BorgActivate(uid, component); + } + + private void OnMindRemoved(EntityUid uid, BorgChassisComponent component, MindRemovedMessage args) + { + BorgDeactivate(uid, component); + } + + private void OnPowerCellChanged(EntityUid uid, BorgChassisComponent component, PowerCellChangedEvent args) + { + UpdateBatteryAlert(uid); + + if (!TryComp(uid, out var draw)) + return; + + // if we eject the battery or run out of charge, then disable + if (args.Ejected || !_powerCell.HasDrawCharge(uid)) + { + DisableBorgAbilities(uid, component); + return; + } + + // if we aren't drawing and suddenly get enough power to draw again, reeanble. + if (_powerCell.HasDrawCharge(uid, draw)) + { + // only reenable the powerdraw if a player has the role. + if (!draw.Drawing && _mind.TryGetMind(uid, out _)) + _powerCell.SetPowerCellDrawEnabled(uid, true); + + EnableBorgAbilities(uid, component); + } + + UpdateUI(uid, component); + } + + private void OnPowerCellSlotEmpty(EntityUid uid, BorgChassisComponent component, ref PowerCellSlotEmptyEvent args) + { + DisableBorgAbilities(uid, component); + UpdateUI(uid, component); + } + + private void OnUIOpenAttempt(EntityUid uid, BorgChassisComponent component, ActivatableUIOpenAttemptEvent args) + { + // borgs can't view their own ui + if (args.User == uid) + args.Cancel(); + } + + private void OnBrainMindAdded(EntityUid uid, BorgBrainComponent component, MindAddedMessage args) + { + if (!Container.TryGetOuterContainer(uid, Transform(uid), out var container)) + return; + + var containerEnt = container.Owner; + + if (!TryComp(containerEnt, out var chassisComponent) || + container.ID != chassisComponent.BrainContainerId) + return; + + if (!_mind.TryGetMind(uid, out var mind) || mind.Session == null) + return; + + if (!CanPlayerBeBorgged(mind.Session, chassisComponent)) + { + Popup.PopupEntity(Loc.GetString("borg-player-not-allowed-eject"), uid); + Container.RemoveEntity(containerEnt, uid); + _throwing.TryThrow(uid, _random.NextVector2() * 5, 5f); + return; + } + + _mind.TransferTo(mind, containerEnt); + } + + private void UpdateBatteryAlert(EntityUid uid, PowerCellSlotComponent? slotComponent = null) + { + if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery, slotComponent)) + { + _alerts.ClearAlert(uid, AlertType.BorgBattery); + _alerts.ShowAlert(uid, AlertType.BorgBatteryNone); + return; + } + + var chargePercent = (short) MathF.Round(battery.CurrentCharge / battery.MaxCharge * 10f); + + // we make sure 0 only shows if they have absolutely no battery. + // also account for floating point imprecision + if (chargePercent == 0 && _powerCell.HasDrawCharge(uid, cell: slotComponent)) + { + chargePercent = 1; + } + + _alerts.ClearAlert(uid, AlertType.BorgBatteryNone); + _alerts.ShowAlert(uid, AlertType.BorgBattery, chargePercent); + } + + /// + /// Activates the borg, enabling all of its modules. + /// + public void EnableBorgAbilities(EntityUid uid, BorgChassisComponent component) + { + if (component.Activated) + return; + + component.Activated = true; + InstallAllModules(uid, component); + Dirty(component); + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); + } + + /// + /// Deactivates the borg, disabling all of its modules and decreasing its speed. + /// + public void DisableBorgAbilities(EntityUid uid, BorgChassisComponent component) + { + if (!component.Activated) + return; + + component.Activated = false; + DisableAllModules(uid, component); + Dirty(component); + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); + } + + /// + /// Activates a borg when a player occupies it + /// + public void BorgActivate(EntityUid uid, BorgChassisComponent component) + { + component.HasPlayer = true; + Popup.PopupEntity(Loc.GetString("borg-mind-added", ("name", Identity.Name(uid, EntityManager))), uid); + _powerCell.SetPowerCellDrawEnabled(uid, true); + _appearance.SetData(uid, BorgVisuals.HasPlayer, true); + Dirty(uid, component); + } + + /// + /// Deactivates a borg when a player leaves it. + /// + public void BorgDeactivate(EntityUid uid, BorgChassisComponent component) + { + component.HasPlayer = false; + Popup.PopupEntity(Loc.GetString("borg-mind-removed", ("name", Identity.Name(uid, EntityManager))), uid); + _powerCell.SetPowerCellDrawEnabled(uid, false); + _appearance.SetData(uid, BorgVisuals.HasPlayer, false); + Dirty(uid, component); + } + + /// + /// Checks that a player has fulfilled the requirements for the borg job. + /// If they don't have enough hours, they cannot be placed into a chassis. + /// + public bool CanPlayerBeBorgged(IPlayerSession session, BorgChassisComponent component) + { + var disallowedJobs = _playTimeTracking.GetDisallowedJobs(session); + + if (disallowedJobs.Contains(component.BorgJobId)) + return false; + + if (_banManager.GetJobBans(session.UserId)?.Contains(component.BorgJobId) == true) + return false; + + return true; + } +} diff --git a/Content.Server/Silicons/Laws/SiliconLawSystem.cs b/Content.Server/Silicons/Laws/SiliconLawSystem.cs new file mode 100644 index 0000000000..7fbddb83a3 --- /dev/null +++ b/Content.Server/Silicons/Laws/SiliconLawSystem.cs @@ -0,0 +1,157 @@ +using Content.Server.Chat.Managers; +using Content.Server.Mind.Components; +using Content.Server.Station.Systems; +using Content.Shared.Actions; +using Content.Shared.Actions.ActionTypes; +using Content.Shared.Chat; +using Content.Shared.Emag.Components; +using Content.Shared.Emag.Systems; +using Content.Shared.Examine; +using Content.Shared.Silicons.Laws; +using Content.Shared.Silicons.Laws.Components; +using Robust.Server.GameObjects; +using Robust.Server.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server.Silicons.Laws; + +/// +public sealed class SiliconLawSystem : SharedSiliconLawSystem +{ + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly UserInterfaceSystem _userInterface = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentStartup); + SubscribeLocalEvent(OnComponentShutdown); + SubscribeLocalEvent(OnMindAdded); + SubscribeLocalEvent(OnToggleLawsScreen); + SubscribeLocalEvent(OnBoundUIOpened); + + SubscribeLocalEvent(OnDirectedGetLaws); + SubscribeLocalEvent(OnDirectedEmagGetLaws); + SubscribeLocalEvent(OnExamined); + } + + private void OnComponentStartup(EntityUid uid, SiliconLawBoundComponent component, ComponentStartup args) + { + component.ProvidedAction = new (_prototype.Index(component.ViewLawsAction)); + _actions.AddAction(uid, component.ProvidedAction, null); + } + + private void OnComponentShutdown(EntityUid uid, SiliconLawBoundComponent component, ComponentShutdown args) + { + if (component.ProvidedAction != null) + _actions.RemoveAction(uid, component.ProvidedAction); + } + + private void OnMindAdded(EntityUid uid, SiliconLawBoundComponent component, MindAddedMessage args) + { + if (!TryComp(uid, out var actor)) + return; + + var msg = Loc.GetString("laws-notify"); + var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg)); + _chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, + actor.PlayerSession.ConnectedClient, colorOverride: Color.FromHex("#2ed2fd")); + } + + private void OnToggleLawsScreen(EntityUid uid, SiliconLawBoundComponent component, ToggleLawsScreenEvent args) + { + if (args.Handled || !TryComp(uid, out var actor)) + return; + args.Handled = true; + + _userInterface.TryToggleUi(uid, SiliconLawsUiKey.Key, actor.PlayerSession); + } + + private void OnBoundUIOpened(EntityUid uid, SiliconLawBoundComponent component, BoundUIOpenedEvent args) + { + var state = new SiliconLawBuiState(GetLaws(uid)); + _userInterface.TrySetUiState(args.Entity, SiliconLawsUiKey.Key, state, (IPlayerSession) args.Session); + } + + private void OnDirectedGetLaws(EntityUid uid, SiliconLawProviderComponent component, ref GetSiliconLawsEvent args) + { + if (args.Handled || HasComp(uid) || component.Laws.Count == 0) + return; + + foreach (var law in component.Laws) + { + args.Laws.Add(_prototype.Index(law)); + } + + args.Handled = true; + } + + private void OnDirectedEmagGetLaws(EntityUid uid, EmagSiliconLawComponent component, ref GetSiliconLawsEvent args) + { + if (args.Handled || !HasComp(uid) || component.OwnerName == null) + return; + + args.Laws.Add(new SiliconLaw + { + LawString = Loc.GetString("law-emag-custom", ("name", component.OwnerName)), + Order = 0 + }); + } + + private void OnExamined(EntityUid uid, EmagSiliconLawComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange || !HasComp(uid)) + return; + + args.PushMarkup(Loc.GetString("laws-compromised-examine")); + } + + protected override void OnGotEmagged(EntityUid uid, EmagSiliconLawComponent component, ref GotEmaggedEvent args) + { + base.OnGotEmagged(uid, component, ref args); + NotifyLawsChanged(uid); + } + + public List GetLaws(EntityUid uid) + { + var xform = Transform(uid); + + var ev = new GetSiliconLawsEvent(uid); + + RaiseLocalEvent(uid, ref ev); + if (ev.Handled) + return ev.Laws; + + if (_station.GetOwningStation(uid, xform) is { } station) + { + RaiseLocalEvent(station, ref ev); + if (ev.Handled) + return ev.Laws; + } + + if (xform.GridUid is { } grid) + { + RaiseLocalEvent(grid, ref ev); + if (ev.Handled) + return ev.Laws; + } + + RaiseLocalEvent(ref ev); + return ev.Laws; + } + + public void NotifyLawsChanged(EntityUid uid) + { + if (!TryComp(uid, out var actor)) + return; + + var msg = Loc.GetString("laws-update-notify"); + var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg)); + _chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.ConnectedClient, colorOverride: Color.FromHex("#2ed2fd")); + } +} diff --git a/Content.Server/Stack/StackSystem.cs b/Content.Server/Stack/StackSystem.cs index 03e0ea7ba3..12bd2a1bd3 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -38,7 +38,7 @@ namespace Content.Server.Stack base.SetCount(uid, amount, component); // Queue delete stack if count reaches zero. - if (component.Count <= 0) + if (component.Count <= 0 && !component.Lingering) QueueDel(uid); } diff --git a/Content.Server/Strip/StrippableSystem.cs b/Content.Server/Strip/StrippableSystem.cs index d050755560..8052765a55 100644 --- a/Content.Server/Strip/StrippableSystem.cs +++ b/Content.Server/Strip/StrippableSystem.cs @@ -331,7 +331,7 @@ namespace Content.Server.Strip if (!_inventorySystem.CanUnequip(user, target, slot, out var reason)) { - _popup.PopupCursor(reason, user); + _popup.PopupCursor(Loc.GetString(reason), user); return false; } diff --git a/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs b/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs new file mode 100644 index 0000000000..74701c1ccd --- /dev/null +++ b/Content.Server/Wires/ActivatableUIRequiresPanelComponent.cs @@ -0,0 +1,15 @@ +namespace Content.Server.Wires; + +/// +/// This is used for activatable UIs that require the entity to have a panel in a certain state. +/// +[RegisterComponent] +public sealed class ActivatableUIRequiresPanelComponent : Component +{ + /// + /// TRUE: the panel must be open to access the UI. + /// FALSE: the panel must be closed to access the UI. + /// + [DataField("requireOpen"), ViewVariables(VVAccess.ReadWrite)] + public bool RequireOpen = true; +} diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index 26b85e724b..5931c7dc13 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading; using Content.Server.Administration.Logs; using Content.Server.Power.Components; +using Content.Server.UserInterface; using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.GameTicking; @@ -55,6 +56,7 @@ public sealed class WiresSystem : SharedWiresSystem SubscribeLocalEvent(OnTimedWire); SubscribeLocalEvent(OnWiresPowered); SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnAttemptOpenActivatableUI); } private void SetOrCreateWireLayout(EntityUid uid, WiresComponent? wires = null) @@ -494,6 +496,15 @@ public sealed class WiresSystem : SharedWiresSystem } } + private void OnAttemptOpenActivatableUI(EntityUid uid, ActivatableUIRequiresPanelComponent component, ActivatableUIOpenAttemptEvent args) + { + if (args.Cancelled || !TryComp(uid, out var wires)) + return; + + if (component.RequireOpen != wires.Open) + args.Cancel(); + } + private void OnMapInit(EntityUid uid, WiresComponent component, MapInitEvent args) { if (!string.IsNullOrEmpty(component.LayoutId)) diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index 214a06fdcf..0887e5a099 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -21,7 +21,6 @@ using Content.Server.NPC; using Content.Server.NPC.Components; using Content.Server.NPC.HTN; using Content.Server.NPC.Systems; -using Content.Server.RoundEnd; using Content.Shared.Humanoid; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; @@ -30,12 +29,10 @@ using Content.Shared.Movement.Systems; using Content.Shared.Nutrition.Components; using Content.Shared.Popups; using Content.Shared.Roles; -using Content.Shared.Tools; using Content.Shared.Tools.Components; using Content.Shared.Weapons.Melee; using Content.Shared.Zombies; using Robust.Shared.Audio; -using Robust.Shared.Utility; namespace Content.Server.Zombies { @@ -87,7 +84,7 @@ namespace Content.Server.Zombies public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) { //Don't zombfiy zombies - if (HasComp(target)) + if (HasComp(target) || HasComp(target)) return; if (!Resolve(target, ref mobState, logMissing: false)) diff --git a/Content.Shared/Access/Components/AccessComponent.cs b/Content.Shared/Access/Components/AccessComponent.cs index 1df8facafa..f6e124c87d 100644 --- a/Content.Shared/Access/Components/AccessComponent.cs +++ b/Content.Shared/Access/Components/AccessComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.Access.Systems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Shared.Access.Components @@ -34,4 +35,16 @@ namespace Content.Shared.Access.Components { } } + + [ByRefEvent] + public record struct GetAccessTagsEvent(HashSet Tags, IPrototypeManager PrototypeManager) + { + public void AddGroup(string group) + { + if (!PrototypeManager.TryIndex(group, out var groupPrototype)) + return; + + Tags.UnionWith(groupPrototype.Tags); + } + } } diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index b1789689e6..d3c9615af1 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -11,11 +11,13 @@ using Robust.Shared.GameStates; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; +using Robust.Shared.Prototypes; namespace Content.Shared.Access.Systems; public sealed class AccessReaderSystem : EntitySystem { + [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; @@ -272,23 +274,24 @@ public sealed class AccessReaderSystem : EntitySystem /// Try to find on this item /// or inside this item (if it's pda) /// - private bool FindAccessTagsItem(EntityUid uid, [NotNullWhen(true)] out HashSet? tags) + private bool FindAccessTagsItem(EntityUid uid, out HashSet tags) { + tags = new(); if (TryComp(uid, out AccessComponent? access)) { - tags = access.Tags; - return true; + tags.UnionWith(access.Tags); } if (TryComp(uid, out PdaComponent? pda) && pda.ContainedId is { Valid: true } id) { - tags = EntityManager.GetComponent(id).Tags; - return true; + tags.UnionWith(EntityManager.GetComponent(id).Tags); } - tags = null; - return false; + var ev = new GetAccessTagsEvent(tags, _prototype); + RaiseLocalEvent(uid, ref ev); + + return tags.Count != 0; } /// diff --git a/Content.Shared/Alert/AlertCategory.cs b/Content.Shared/Alert/AlertCategory.cs index e6e0845385..7450f585a4 100644 --- a/Content.Shared/Alert/AlertCategory.cs +++ b/Content.Shared/Alert/AlertCategory.cs @@ -15,5 +15,6 @@ public enum AlertCategory Piloting, Hunger, Thirst, - Toxins + Toxins, + Battery } diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs index 64fd5943eb..7f74612010 100644 --- a/Content.Shared/Alert/AlertType.cs +++ b/Content.Shared/Alert/AlertType.cs @@ -22,6 +22,8 @@ namespace Content.Shared.Alert HumanCrit, HumanDead, HumanHealth, + BorgBattery, + BorgBatteryNone, PilotingShuttle, Peckish, Starving, diff --git a/Content.Shared/Construction/Components/PartAssemblyComponent.cs b/Content.Shared/Construction/Components/PartAssemblyComponent.cs new file mode 100644 index 0000000000..b583379d55 --- /dev/null +++ b/Content.Shared/Construction/Components/PartAssemblyComponent.cs @@ -0,0 +1,45 @@ +using Robust.Shared.Containers; + +namespace Content.Shared.Construction.Components; + +/// +/// This is used for construction which requires a set of +/// entities with specific tags to be inserted into another entity. +/// todo: in a pr that isn't 6k loc, combine this with MechAssemblyComponent +/// +[RegisterComponent] +public sealed class PartAssemblyComponent : Component +{ + /// + /// A dictionary of a set of parts to a list of tags for each assembly. + /// + [DataField("parts", required: true)] + public Dictionary> Parts = new(); + + /// + /// The entry in that is currently being worked on. + /// + [DataField("currentAssembly")] + public string? CurrentAssembly; + + /// + /// The container where the parts are stored + /// + [DataField("containerId")] + public string ContainerId = "part-container"; + + /// + /// The container that stores all of the parts when + /// they're being assembled. + /// + [ViewVariables] + public Container PartsContainer = default!; +} + +/// +/// Event raised when a valid part is inserted into the part assembly. +/// +public sealed class PartAssemblyPartInsertedEvent +{ + +} diff --git a/Content.Shared/Construction/PartAssemblySystem.cs b/Content.Shared/Construction/PartAssemblySystem.cs new file mode 100644 index 0000000000..91aa25e030 --- /dev/null +++ b/Content.Shared/Construction/PartAssemblySystem.cs @@ -0,0 +1,147 @@ +using Content.Shared.Construction.Components; +using Content.Shared.Interaction; +using Content.Shared.Tag; +using Robust.Shared.Containers; + +namespace Content.Shared.Construction; + +/// +/// This handles +/// +public sealed class PartAssemblySystem : EntitySystem +{ + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly TagSystem _tag = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnEntRemoved); + } + + private void OnInit(EntityUid uid, PartAssemblyComponent component, ComponentInit args) + { + component.PartsContainer = _container.EnsureContainer(uid, component.ContainerId); + } + + private void OnInteractUsing(EntityUid uid, PartAssemblyComponent component, InteractUsingEvent args) + { + if (!TryInsertPart(args.Used, uid, component)) + return; + args.Handled = true; + } + + private void OnEntRemoved(EntityUid uid, PartAssemblyComponent component, EntRemovedFromContainerMessage args) + { + if (args.Container.ID != component.ContainerId) + return; + if (component.PartsContainer.ContainedEntities.Count != 0) + return; + component.CurrentAssembly = null; + } + + /// + /// Attempts to insert a part into the current assembly, starting one if there is none. + /// + public bool TryInsertPart(EntityUid part, EntityUid uid, PartAssemblyComponent? component = null) + { + if (!Resolve(uid, ref component)) + return false; + + string? assemblyId = null; + assemblyId ??= component.CurrentAssembly; + + if (assemblyId == null) + { + foreach (var (id, tags) in component.Parts) + { + foreach (var tag in tags) + { + if (!_tag.HasTag(part, tag)) + continue; + assemblyId = id; + break; + } + + if (assemblyId != null) + break; + } + } + + if (assemblyId == null) + return false; + + if (!IsPartValid(uid, part, assemblyId, component)) + return false; + + component.CurrentAssembly = assemblyId; + component.PartsContainer.Insert(part); + var ev = new PartAssemblyPartInsertedEvent(); + RaiseLocalEvent(uid, ev); + return true; + } + + /// + /// Checks if the given entity is a valid item for the assembly. + /// + public bool IsPartValid(EntityUid uid, EntityUid part, string assemblyId, PartAssemblyComponent? component = null) + { + if (!Resolve(uid, ref component, false)) + return true; + + if (!component.Parts.TryGetValue(assemblyId, out var tags)) + return false; + + var openTags = new List(tags); + var contained = new List(component.PartsContainer.ContainedEntities); + foreach (var tag in tags) + { + foreach (var ent in component.PartsContainer.ContainedEntities) + { + if (!contained.Contains(ent) || !_tag.HasTag(ent, tag)) + continue; + openTags.Remove(tag); + contained.Remove(ent); + break; + } + } + + foreach (var tag in openTags) + { + if (_tag.HasTag(part, tag)) + return true; + } + + return false; + } + + public bool IsAssemblyFinished(EntityUid uid, string assemblyId, PartAssemblyComponent? component = null) + { + if (!Resolve(uid, ref component, false)) + return true; + + if (!component.Parts.TryGetValue(assemblyId, out var parts)) + return false; + + var contained = new List(component.PartsContainer.ContainedEntities); + foreach (var tag in parts) + { + var valid = false; + foreach (var ent in new List(contained)) + { + if (!_tag.HasTag(ent, tag)) + continue; + valid = true; + contained.Remove(ent); + break; + } + + if (!valid) + return false; + } + + return true; + } +} diff --git a/Content.Shared/Construction/Steps/ConstructionGraphStepTypeSerializer.cs b/Content.Shared/Construction/Steps/ConstructionGraphStepTypeSerializer.cs index 63e55e0d14..7b5b020d2f 100644 --- a/Content.Shared/Construction/Steps/ConstructionGraphStepTypeSerializer.cs +++ b/Content.Shared/Construction/Steps/ConstructionGraphStepTypeSerializer.cs @@ -41,6 +41,11 @@ namespace Content.Shared.Construction.Steps return typeof(TemperatureConstructionGraphStep); } + if (node.Has("assemblyId") || node.Has("guideString")) + { + return typeof(PartAssemblyConstructionGraphStep); + } + return null; } diff --git a/Content.Shared/Construction/Steps/PartAssemblyConstructionGraphStep.cs b/Content.Shared/Construction/Steps/PartAssemblyConstructionGraphStep.cs new file mode 100644 index 0000000000..de7eec13c0 --- /dev/null +++ b/Content.Shared/Construction/Steps/PartAssemblyConstructionGraphStep.cs @@ -0,0 +1,39 @@ +using Content.Shared.Construction.Components; +using Content.Shared.Examine; +using JetBrains.Annotations; + +namespace Content.Shared.Construction.Steps; + +[DataDefinition] +public sealed class PartAssemblyConstructionGraphStep : ConstructionGraphStep +{ + /// + /// A valid ID on 's dictionary of strings to part lists. + /// + [DataField("assemblyId")] + public string AssemblyId = string.Empty; + + /// + /// A localization string used for + /// + [DataField("guideString")] + public string GuideString = "construction-guide-condition-part-assembly"; + + public bool Condition(EntityUid uid, IEntityManager entityManager) + { + return entityManager.System().IsAssemblyFinished(uid, AssemblyId); + } + + public override void DoExamine(ExaminedEvent args) + { + args.PushMarkup(Loc.GetString(GuideString)); + } + + public override ConstructionGuideEntry GenerateGuideEntry() + { + return new ConstructionGuideEntry + { + Localization = GuideString, + }; + } +} diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs index 319bf5cd53..753fadb0ff 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs @@ -233,4 +233,16 @@ namespace Content.Shared.Containers.ItemSlots Priority = other.Priority; } } + + /// + /// Event raised on the slot entity and the item being inserted to determine if an item can be inserted into an item slot. + /// + [ByRefEvent] + public record struct ItemSlotInsertAttemptEvent(EntityUid SlotEntity, EntityUid Item, EntityUid? User, ItemSlot Slot, bool Cancelled = false); + + /// + /// Event raised on the slot entity and the item being inserted to determine if an item can be ejected from an item slot. + /// + [ByRefEvent] + public record struct ItemSlotEjectAttemptEvent(EntityUid SlotEntity, EntityUid Item, EntityUid? User, ItemSlot Slot, bool Cancelled = false); } diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index 42df026159..1e56d6936b 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -200,7 +200,7 @@ namespace Content.Shared.Containers.ItemSlots if (!slot.InsertOnInteract) continue; - if (!CanInsert(uid, args.Used, slot, swap: slot.Swap, popup: args.User)) + if (!CanInsert(uid, args.Used, args.User, slot, swap: slot.Swap, popup: args.User)) continue; // Drop the held item onto the floor. Return if the user cannot drop. @@ -244,7 +244,7 @@ namespace Content.Shared.Containers.ItemSlots /// If a popup entity is given, and if the item slot is set to generate a popup message when it fails to /// pass the whitelist, then this will generate a popup. /// - public bool CanInsert(EntityUid uid, EntityUid usedUid, ItemSlot slot, bool swap = false, EntityUid? popup = null) + public bool CanInsert(EntityUid uid, EntityUid usedUid, EntityUid? user, ItemSlot slot, bool swap = false, EntityUid? popup = null) { if (slot.Locked) return false; @@ -259,6 +259,12 @@ namespace Content.Shared.Containers.ItemSlots return false; } + var ev = new ItemSlotInsertAttemptEvent(uid, usedUid, user, slot); + RaiseLocalEvent(uid, ref ev); + RaiseLocalEvent(usedUid, ref ev); + if (ev.Cancelled) + return false; + return slot.ContainerSlot?.CanInsertIfEmpty(usedUid, EntityManager) ?? false; } @@ -283,7 +289,7 @@ namespace Content.Shared.Containers.ItemSlots /// False if failed to insert item public bool TryInsert(EntityUid uid, ItemSlot slot, EntityUid item, EntityUid? user) { - if (!CanInsert(uid, item, slot)) + if (!CanInsert(uid, item, user, slot)) return false; Insert(uid, slot, item, user); @@ -303,7 +309,7 @@ namespace Content.Shared.Containers.ItemSlots if (hands.ActiveHand?.HeldEntity is not EntityUid held) return false; - if (!CanInsert(uid, held, slot)) + if (!CanInsert(uid, held, user, slot)) return false; // hands.Drop(item) checks CanDrop action blocker @@ -317,11 +323,17 @@ namespace Content.Shared.Containers.ItemSlots #region Eject - public bool CanEject(ItemSlot slot) + public bool CanEject(EntityUid uid, EntityUid? user, ItemSlot slot) { if (slot.Locked || slot.Item == null) return false; + var ev = new ItemSlotEjectAttemptEvent(uid, slot.Item.Value, user, slot); + RaiseLocalEvent(uid, ref ev); + RaiseLocalEvent(slot.Item.Value, ref ev); + if (ev.Cancelled) + return false; + return slot.ContainerSlot?.CanRemove(slot.Item.Value, EntityManager) ?? false; } @@ -352,7 +364,7 @@ namespace Content.Shared.Containers.ItemSlots item = null; // This handles logic with the slot itself - if (!CanEject(slot)) + if (!CanEject(uid, user, slot)) return false; item = slot.Item; @@ -418,7 +430,7 @@ namespace Content.Shared.Containers.ItemSlots foreach (var slot in itemSlots.Slots.Values) { // Disable slot insert if InsertOnInteract is true - if (slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, slot)) + if (slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, args.User, slot)) continue; var verbSubject = slot.Name != string.Empty @@ -467,7 +479,7 @@ namespace Content.Shared.Containers.ItemSlots // alt-click verb, there will be a "Take item" primary interaction verb. continue; - if (!CanEject(slot)) + if (!CanEject(uid, args.User, slot)) continue; if (!_actionBlockerSystem.CanPickup(args.User, slot.Item!.Value)) @@ -506,7 +518,7 @@ namespace Content.Shared.Containers.ItemSlots // If there are any slots that eject on left-click, add a "Take " verb. foreach (var slot in itemSlots.Slots.Values) { - if (!slot.EjectOnInteract || !CanEject(slot)) + if (!slot.EjectOnInteract || !CanEject(uid, args.User, slot)) continue; if (!_actionBlockerSystem.CanPickup(args.User, slot.Item!.Value)) @@ -514,7 +526,7 @@ namespace Content.Shared.Containers.ItemSlots var verbSubject = slot.Name != string.Empty ? Loc.GetString(slot.Name) - : EntityManager.GetComponent(slot.Item!.Value).EntityName ?? string.Empty; + : Name(slot.Item!.Value); InteractionVerb takeVerb = new(); takeVerb.IconEntity = slot.Item; @@ -535,7 +547,7 @@ namespace Content.Shared.Containers.ItemSlots foreach (var slot in itemSlots.Slots.Values) { - if (!slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, slot)) + if (!slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, args.User, slot)) continue; var verbSubject = slot.Name != string.Empty diff --git a/Content.Shared/Interaction/Components/BlockMovementComponent.cs b/Content.Shared/Interaction/Components/BlockMovementComponent.cs new file mode 100644 index 0000000000..a599ca3684 --- /dev/null +++ b/Content.Shared/Interaction/Components/BlockMovementComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Interaction.Components; + +/// +/// This is used for entities which cannot move or interact in any way. +/// +[RegisterComponent, NetworkedComponent] +public sealed class BlockMovementComponent : Component +{ + +} diff --git a/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs new file mode 100644 index 0000000000..e5313b5430 --- /dev/null +++ b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs @@ -0,0 +1,47 @@ +using Content.Shared.Hands; +using Content.Shared.Interaction.Components; +using Content.Shared.Interaction.Events; +using Content.Shared.Item; +using Content.Shared.Movement.Events; + +namespace Content.Shared.Interaction; + +public partial class SharedInteractionSystem +{ + public void InitializeBlocking() + { + SubscribeLocalEvent(OnMoveAttempt); + SubscribeLocalEvent(CancelEvent); + SubscribeLocalEvent(CancelEvent); + SubscribeLocalEvent(CancelEvent); + SubscribeLocalEvent(CancelEvent); + SubscribeLocalEvent(CancelEvent); + + SubscribeLocalEvent(OnBlockingStartup); + SubscribeLocalEvent(OnBlockingShutdown); + } + + private void OnMoveAttempt(EntityUid uid, BlockMovementComponent component, UpdateCanMoveEvent args) + { + if (component.LifeStage > ComponentLifeStage.Running) + return; + + args.Cancel(); // no more scurrying around + } + + private void CancelEvent(EntityUid uid, BlockMovementComponent component, CancellableEntityEventArgs args) + { + args.Cancel(); + } + + private void OnBlockingStartup(EntityUid uid, BlockMovementComponent component, ComponentStartup args) + { + _actionBlockerSystem.UpdateCanMove(uid); + } + + private void OnBlockingShutdown(EntityUid uid, BlockMovementComponent component, ComponentShutdown args) + { + _actionBlockerSystem.UpdateCanMove(uid); + } +} + diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index d4c8342022..f00d9c8c38 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -100,6 +100,7 @@ namespace Content.Shared.Interaction .Register(); InitializeRelay(); + InitializeBlocking(); } public override void Shutdown() diff --git a/Content.Shared/Light/Component/HandheldLightComponent.cs b/Content.Shared/Light/Component/HandheldLightComponent.cs index b4e13896b0..154e7493b6 100644 --- a/Content.Shared/Light/Component/HandheldLightComponent.cs +++ b/Content.Shared/Light/Component/HandheldLightComponent.cs @@ -40,6 +40,13 @@ namespace Content.Shared.Light [DataField("toggleActionId", customTypeSerializer: typeof(PrototypeIdSerializer))] public string ToggleActionId = "ToggleLight"; + /// + /// Whether or not the light can be toggled via standard interactions + /// (alt verbs, using in hand, etc) + /// + [DataField("toggleOnInteract")] + public bool ToggleOnInteract = true; + [DataField("toggleAction")] public InstantAction? ToggleAction; diff --git a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs index 8400b26ee7..203bff493a 100644 --- a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs +++ b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs @@ -118,6 +118,9 @@ public abstract class SharedMaterialReclaimerSystem : EntitySystem if (component.Blacklist != null && component.Blacklist.IsValid(item)) return false; + if (!_container.TryRemoveFromContainer(item)) + return false; + if (user != null) { _adminLog.Add(LogType.Action, LogImpact.High, diff --git a/Content.Shared/Mind/MindVisuals.cs b/Content.Shared/Mind/MindVisuals.cs new file mode 100644 index 0000000000..9e60a6c2f9 --- /dev/null +++ b/Content.Shared/Mind/MindVisuals.cs @@ -0,0 +1,17 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Mind; + +[Serializable, NetSerializable] +public enum ToggleableGhostRoleVisuals : byte +{ + Status +} + +[Serializable, NetSerializable] +public enum ToggleableGhostRoleStatus : byte +{ + Off, + Searching, + On +} diff --git a/Content.Shared/NameIdentifier/NameIdentifierComponent.cs b/Content.Shared/NameIdentifier/NameIdentifierComponent.cs new file mode 100644 index 0000000000..0fcb3330f7 --- /dev/null +++ b/Content.Shared/NameIdentifier/NameIdentifierComponent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.NameIdentifier; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class NameIdentifierComponent : Component +{ + [DataField("group", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] + public string Group = string.Empty; + + /// + /// The randomly generated ID for this entity. + /// + [DataField("identifier"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public int Identifier = -1; + + /// + /// The full name identifier for this entity. + /// + [DataField("fullIdentifier"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public string FullIdentifier = string.Empty; +} diff --git a/Content.Shared/PAI/PAIComponent.cs b/Content.Shared/PAI/PAIComponent.cs index 62aea5c35e..68ca12c53b 100644 --- a/Content.Shared/PAI/PAIComponent.cs +++ b/Content.Shared/PAI/PAIComponent.cs @@ -16,6 +16,13 @@ namespace Content.Shared.PAI [RegisterComponent, NetworkedComponent] public sealed class PAIComponent : Component { + /// + /// The last person who activated this PAI. + /// Used for assigning the name. + /// + [ViewVariables] + public EntityUid? LastUser; + [DataField("midiAction", required: true, serverOnly: true)] // server only, as it uses a server-BUI event !type public InstantAction? MidiAction; } diff --git a/Content.Shared/PAI/SharedPAISystem.cs b/Content.Shared/PAI/SharedPAISystem.cs index a29c7bba01..28dbdb89d1 100644 --- a/Content.Shared/PAI/SharedPAISystem.cs +++ b/Content.Shared/PAI/SharedPAISystem.cs @@ -1,12 +1,9 @@ using Content.Shared.ActionBlocker; using Content.Shared.Actions; -using Content.Shared.DragDrop; using Content.Shared.Hands; using Content.Shared.Interaction.Events; using Content.Shared.Item; -using Content.Shared.Movement; using Content.Shared.Movement.Events; -using Robust.Shared.Serialization; namespace Content.Shared.PAI { @@ -27,12 +24,6 @@ namespace Content.Shared.PAI public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnUseAttempt); - SubscribeLocalEvent(OnInteractAttempt); - SubscribeLocalEvent(OnDropAttempt); - SubscribeLocalEvent(OnPickupAttempt); - SubscribeLocalEvent(OnMoveAttempt); - SubscribeLocalEvent(OnChangeDirectionAttempt); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnShutdown); @@ -40,67 +31,15 @@ namespace Content.Shared.PAI private void OnStartup(EntityUid uid, PAIComponent component, ComponentStartup args) { - _blocker.UpdateCanMove(uid); if (component.MidiAction != null) _actionsSystem.AddAction(uid, component.MidiAction, null); } private void OnShutdown(EntityUid uid, PAIComponent component, ComponentShutdown args) { - _blocker.UpdateCanMove(uid); if (component.MidiAction != null) _actionsSystem.RemoveAction(uid, component.MidiAction); } - - private void OnMoveAttempt(EntityUid uid, PAIComponent component, UpdateCanMoveEvent args) - { - if (component.LifeStage > ComponentLifeStage.Running) - return; - - args.Cancel(); // no more scurrying around on lil robot legs. - } - - private void OnChangeDirectionAttempt(EntityUid uid, PAIComponent component, ChangeDirectionAttemptEvent args) - { - // PAIs can't rotate, but decapitated heads and sentient crowbars can, life isn't fair. Seriously though, why - // tf does this have to be actively blocked, surely this should just not be blanket enabled for any player - // controlled entity. Same goes for moving really. - args.Cancel(); - } - - private void OnUseAttempt(EntityUid uid, PAIComponent component, UseAttemptEvent args) - { - args.Cancel(); - } - - private void OnInteractAttempt(EntityUid uid, PAIComponent component, InteractionAttemptEvent args) - { - args.Cancel(); - } - - private void OnDropAttempt(EntityUid uid, PAIComponent component, DropAttemptEvent args) - { - args.Cancel(); - } - - private void OnPickupAttempt(EntityUid uid, PAIComponent component, PickupAttemptEvent args) - { - args.Cancel(); - } - } - - [Serializable, NetSerializable] - public enum PAIVisuals : byte - { - Status - } - - [Serializable, NetSerializable] - public enum PAIStatus : byte - { - Off, - Searching, - On } } diff --git a/Content.Shared/Silicons/Borgs/BorgUI.cs b/Content.Shared/Silicons/Borgs/BorgUI.cs new file mode 100644 index 0000000000..601f9a2178 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/BorgUI.cs @@ -0,0 +1,57 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Silicons.Borgs; + +[Serializable, NetSerializable] +public enum BorgUiKey : byte +{ + Key +} + +[Serializable, NetSerializable] +public sealed class BorgBuiState : BoundUserInterfaceState +{ + public float ChargePercent; + + public bool HasBattery; + + public BorgBuiState(float chargePercent, bool hasBattery) + { + ChargePercent = chargePercent; + HasBattery = hasBattery; + } +} + +[Serializable, NetSerializable] +public sealed class BorgEjectBrainBuiMessage : BoundUserInterfaceMessage +{ + +} + +[Serializable, NetSerializable] +public sealed class BorgEjectBatteryBuiMessage : BoundUserInterfaceMessage +{ + +} + +[Serializable, NetSerializable] +public sealed class BorgSetNameBuiMessage : BoundUserInterfaceMessage +{ + public string Name; + + public BorgSetNameBuiMessage(string name) + { + Name = name; + } +} + +[Serializable, NetSerializable] +public sealed class BorgRemoveModuleBuiMessage : BoundUserInterfaceMessage +{ + public EntityUid Module; + + public BorgRemoveModuleBuiMessage(EntityUid module) + { + Module = module; + } +} diff --git a/Content.Shared/Silicons/Borgs/Components/BorgBrainComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgBrainComponent.cs new file mode 100644 index 0000000000..2ca62b6433 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/BorgBrainComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for brains and mind receptacles +/// that can be inserted into a borg to transfer a mind. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class BorgBrainComponent : Component +{ + +} diff --git a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs new file mode 100644 index 0000000000..22c343fa2a --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs @@ -0,0 +1,126 @@ +using Content.Shared.Roles; +using Content.Shared.Whitelist; +using Robust.Shared.Containers; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for the core body of a borg. This manages a borg's +/// "brain", legs, modules, and battery. Essentially the master component +/// for borg logic. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem)), AutoGenerateComponentState] +public sealed partial class BorgChassisComponent : Component +{ + /// + /// Whether or not the borg currently has a player occupying it + /// + [DataField("hasPlayer")] + public bool HasPlayer; + + /// + /// Whether or not the borg is activated, meaning it has access to modules and a heightened movement speed + /// + [DataField("activated"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public bool Activated; + + #region Brain + /// + /// A whitelist for which entities count as valid brains + /// + [DataField("brainWhitelist")] + public EntityWhitelist? BrainWhitelist; + + /// + /// The container ID for the brain + /// + [DataField("brainContainerId")] + public string BrainContainerId = "borg_brain"; + + [ViewVariables(VVAccess.ReadWrite)] + public ContainerSlot BrainContainer = default!; + + public EntityUid? BrainEntity => BrainContainer.ContainedEntity; + + /// + /// A brain entity that fills the on roundstart + /// + [DataField("startingBrain", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? StartingBrain; + #endregion + + #region Modules + /// + /// A whitelist for what types of modules can be installed into this borg + /// + [DataField("moduleWhitelist")] + public EntityWhitelist? ModuleWhitelist; + + /// + /// How many modules can be installed in this borg + /// + [DataField("maxModules"), ViewVariables(VVAccess.ReadWrite)] + public int MaxModules = 3; + + /// + /// The ID for the module container + /// + [DataField("moduleContainerId")] + public string ModuleContainerId = "borg_module"; + + [ViewVariables(VVAccess.ReadWrite)] + public Container ModuleContainer = default!; + + public int ModuleCount => ModuleContainer.ContainedEntities.Count; + + /// + /// A list of modules that fill the borg on round start. + /// + [DataField("startingModules", customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List StartingModules = new(); + #endregion + + /// + /// The job that corresponds to borgs + /// + [DataField("borgJobId", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string BorgJobId = "Borg"; + + /// + /// The currently selected module + /// + [DataField("selectedModule")] + public EntityUid? SelectedModule; + + /// + /// The access this cyborg has when a player is inhabiting it. + /// + [DataField("access"), ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public string AccessGroup = "AllAccess"; + + #region Visuals + [DataField("hasMindState")] + public string HasMindState = string.Empty; + + [DataField("noMindState")] + public string NoMindState = string.Empty; + #endregion +} + +[Serializable, NetSerializable] +public enum BorgVisuals : byte +{ + HasPlayer +} + +[Serializable, NetSerializable] +public enum BorgVisualLayers : byte +{ + Light +} diff --git a/Content.Shared/Silicons/Borgs/Components/BorgModuleComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgModuleComponent.cs new file mode 100644 index 0000000000..f994f60e49 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/BorgModuleComponent.cs @@ -0,0 +1,33 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for modules that can be inserted into borgs +/// to give them unique abilities and attributes. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class BorgModuleComponent : Component +{ + /// + /// The entity this module is installed into + /// + [DataField("installedEntity")] + public EntityUid? InstalledEntity; + + public bool Installed => InstalledEntity != null; +} + +/// +/// Raised on a module when it is installed in order to add specific behavior to an entity. +/// +/// +[ByRefEvent] +public readonly record struct BorgModuleInstalledEvent(EntityUid ChassisEnt); + +/// +/// Raised on a module when it's uninstalled in order to +/// +/// +[ByRefEvent] +public readonly record struct BorgModuleUninstalledEvent(EntityUid ChassisEnt); diff --git a/Content.Shared/Silicons/Borgs/Components/ItemBorgModuleComponent.cs b/Content.Shared/Silicons/Borgs/Components/ItemBorgModuleComponent.cs new file mode 100644 index 0000000000..28a34aa2ee --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/ItemBorgModuleComponent.cs @@ -0,0 +1,51 @@ +using Robust.Shared.Containers; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for a that provides items to the entity it's installed into. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class ItemBorgModuleComponent : Component +{ + /// + /// The items that are provided. + /// + [DataField("items", customTypeSerializer: typeof(PrototypeIdListSerializer), required: true)] + public List Items = new(); + + /// + /// The entities from that were spawned. + /// + [DataField("providedItems")] + public SortedDictionary ProvidedItems = new(); + + /// + /// A counter that ensures a unique + /// + [DataField("handCounter")] + public int HandCounter; + + /// + /// Whether or not the items have been created and stored in + /// + [DataField("itemsCrated")] + public bool ItemsCreated; + + /// + /// A container where provided items are stored when not being used. + /// This is helpful as it means that items retain state. + /// + [ViewVariables] + public Container ProvidedContainer = default!; + + /// + /// An ID for the container where provided items are stored when not used. + /// + [DataField("providedContainerId")] + public string ProvidedContainerId = "provided_container"; +} + diff --git a/Content.Shared/Silicons/Borgs/Components/MMIComponent.cs b/Content.Shared/Silicons/Borgs/Components/MMIComponent.cs new file mode 100644 index 0000000000..b8bc6e44c8 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/MMIComponent.cs @@ -0,0 +1,49 @@ +using Content.Shared.Containers.ItemSlots; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for an entity that takes a brain +/// in an item slot before transferring consciousness. +/// Used for borg stuff. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class MMIComponent : Component +{ + /// + /// The ID of the itemslot that holds the brain. + /// + [DataField("brainSlotId")] + public string BrainSlotId = "brain_slot"; + + /// + /// The for this implanter + /// + [ViewVariables(VVAccess.ReadWrite)] + public ItemSlot BrainSlot = default!; + + [DataField("hasMindState")] + public string HasMindState = "mmi_alive"; + + [DataField("noMindState")] + public string NoMindState = "mmi_dead"; + + [DataField("noBrainState")] + public string NoBrainState = "mmi_off"; +} + +[Serializable, NetSerializable] +public enum MMIVisuals : byte +{ + BrainPresent, + HasMind +} + +[Serializable, NetSerializable] +public enum MMIVisualLayers : byte +{ + Brain, + Base +} diff --git a/Content.Shared/Silicons/Borgs/Components/MMILinkedComponent.cs b/Content.Shared/Silicons/Borgs/Components/MMILinkedComponent.cs new file mode 100644 index 0000000000..ceb9c47593 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/MMILinkedComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for an entity that is linked to an MMI. +/// Mostly for receiving events. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class MMILinkedComponent : Component +{ + /// + /// The MMI this entity is linked to. + /// + [DataField("linkedMMI")] + public EntityUid? LinkedMMI; +} diff --git a/Content.Shared/Silicons/Borgs/Components/SelectableBorgModuleComponent.cs b/Content.Shared/Silicons/Borgs/Components/SelectableBorgModuleComponent.cs new file mode 100644 index 0000000000..e90f6bc118 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/Components/SelectableBorgModuleComponent.cs @@ -0,0 +1,41 @@ +using Content.Shared.Actions; +using Content.Shared.Actions.ActionTypes; +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.Borgs.Components; + +/// +/// This is used for s that can be "swapped" to, as opposed to having passive effects. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))] +public sealed class SelectableBorgModuleComponent : Component +{ + /// + /// The sidebar action for swapping to this module. + /// + [DataField("moduleSwapAction")] + public InstantAction ModuleSwapAction = new() + { + DisplayName = "action-name-swap-module", + Description = "action-desc-swap-module", + ItemIconStyle = ItemActionIconStyle.BigItem, + Event = new BorgModuleActionSelectedEvent(), + UseDelay = TimeSpan.FromSeconds(0.5f) + }; +} + +public sealed class BorgModuleActionSelectedEvent : InstantActionEvent +{ +} + +/// +/// Event raised by-ref on a module when it is selected +/// +[ByRefEvent] +public readonly record struct BorgModuleSelectedEvent(EntityUid Chassis); + +/// +/// Event raised by-ref on a module when it is deselected. +/// +[ByRefEvent] +public readonly record struct BorgModuleUnselectedEvent(EntityUid Chassis); diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.Relay.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.Relay.cs new file mode 100644 index 0000000000..87df866c98 --- /dev/null +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.Relay.cs @@ -0,0 +1,38 @@ +using Content.Shared.Damage; +using Content.Shared.Silicons.Borgs.Components; + +namespace Content.Shared.Silicons.Borgs; + +public abstract partial class SharedBorgSystem +{ + public void InitializeRelay() + { + SubscribeLocalEvent(RelayToModule); + } + + protected void RelayToModule(EntityUid uid, BorgChassisComponent component, T args) where T : class + { + var ev = new BorgModuleRelayedEvent(args); + + foreach (var module in component.ModuleContainer.ContainedEntities) + { + RaiseLocalEvent(module, ref ev); + } + } + + protected void RelayRefToModule(EntityUid uid, BorgChassisComponent component, ref T args) where T : class + { + var ev = new BorgModuleRelayedEvent(args); + + foreach (var module in component.ModuleContainer.ContainedEntities) + { + RaiseLocalEvent(module, ref ev); + } + } +} + +[ByRefEvent] +public record struct BorgModuleRelayedEvent(TEvent Args) +{ + public readonly TEvent Args = Args; +} diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs new file mode 100644 index 0000000000..ee158e442b --- /dev/null +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs @@ -0,0 +1,107 @@ +using Content.Shared.Access.Components; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Content.Shared.Popups; +using Content.Shared.PowerCell.Components; +using Content.Shared.Silicons.Borgs.Components; +using Content.Shared.Wires; +using Robust.Shared.Containers; + +namespace Content.Shared.Silicons.Borgs; + +/// +/// This handles logic, interactions, and UI related to and other related components. +/// +public abstract partial class SharedBorgSystem : EntitySystem +{ + [Dependency] protected readonly SharedContainerSystem Container = default!; + [Dependency] protected readonly ItemSlotsSystem ItemSlots = default!; + [Dependency] protected readonly SharedPopupSystem Popup = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnItemSlotInsertAttempt); + SubscribeLocalEvent(OnItemSlotEjectAttempt); + SubscribeLocalEvent(OnInserted); + SubscribeLocalEvent(OnRemoved); + SubscribeLocalEvent(OnRefreshMovementSpeedModifiers); + SubscribeLocalEvent(OnGetAccessTags); + + InitializeRelay(); + } + + private void OnItemSlotInsertAttempt(EntityUid uid, BorgChassisComponent component, ref ItemSlotInsertAttemptEvent args) + { + if (args.Cancelled) + return; + + if (!TryComp(uid, out var cellSlotComp) || + !TryComp(uid, out var panel)) + return; + + if (!ItemSlots.TryGetSlot(uid, cellSlotComp.CellSlotId, out var cellSlot) || cellSlot != args.Slot) + return; + + if (!panel.Open || args.User == uid) + args.Cancelled = true; + } + + private void OnItemSlotEjectAttempt(EntityUid uid, BorgChassisComponent component, ref ItemSlotEjectAttemptEvent args) + { + if (args.Cancelled) + return; + + if (!TryComp(uid, out var cellSlotComp) || + !TryComp(uid, out var panel)) + return; + + if (!ItemSlots.TryGetSlot(uid, cellSlotComp.CellSlotId, out var cellSlot) || cellSlot != args.Slot) + return; + + if (!panel.Open || args.User == uid) + args.Cancelled = true; + } + + private void OnStartup(EntityUid uid, BorgChassisComponent component, ComponentStartup args) + { + var containerManager = EnsureComp(uid); + + component.BrainContainer = Container.EnsureContainer(uid, component.BrainContainerId, containerManager); + component.ModuleContainer = Container.EnsureContainer(uid, component.ModuleContainerId, containerManager); + } + + protected virtual void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args) + { + + } + + protected virtual void OnRemoved(EntityUid uid, BorgChassisComponent component, EntRemovedFromContainerMessage args) + { + + } + + private void OnRefreshMovementSpeedModifiers(EntityUid uid, BorgChassisComponent component, RefreshMovementSpeedModifiersEvent args) + { + if (component.Activated) + return; + + if (!TryComp(uid, out var movement)) + return; + + var sprintDif = movement.BaseWalkSpeed / movement.BaseSprintSpeed; + args.ModifySpeed(1f, sprintDif); + } + + private void OnGetAccessTags(EntityUid uid, BorgChassisComponent component, ref GetAccessTagsEvent args) + { + if (!component.HasPlayer) + return; + args.AddGroup(component.AccessGroup); + } + +} diff --git a/Content.Shared/Silicons/Laws/Components/EmagSiliconLawComponent.cs b/Content.Shared/Silicons/Laws/Components/EmagSiliconLawComponent.cs new file mode 100644 index 0000000000..1e3dfa6c24 --- /dev/null +++ b/Content.Shared/Silicons/Laws/Components/EmagSiliconLawComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Shared.Silicons.Laws.Components; + +/// +/// This is used for an entity that grants a special "obey" law when emagge.d +/// +[RegisterComponent] +public sealed class EmagSiliconLawComponent : Component +{ + /// + /// The name of the person who emagged this law provider. + /// + [DataField("ownerName")] + public string? OwnerName; +} diff --git a/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs b/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs new file mode 100644 index 0000000000..25c4772e52 --- /dev/null +++ b/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs @@ -0,0 +1,57 @@ +using Content.Shared.Actions; +using Content.Shared.Actions.ActionTypes; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Silicons.Laws.Components; + +/// +/// This is used for entities which are bound to silicon laws and can view them. +/// +[RegisterComponent] +public sealed class SiliconLawBoundComponent : Component +{ + /// + /// The sidebar action that toggles the laws screen. + /// + [DataField("viewLawsAction", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string ViewLawsAction = "ViewLaws"; + + /// + /// The action for toggling laws. Stored here so we can remove it later. + /// + [DataField("providedAction")] + public InstantAction? ProvidedAction; +} + +[ByRefEvent] +public record struct GetSiliconLawsEvent(EntityUid Entity) +{ + public EntityUid Entity = Entity; + + public readonly List Laws = new(); + + public bool Handled = false; +} + +public sealed class ToggleLawsScreenEvent : InstantActionEvent +{ + +} + +[NetSerializable, Serializable] +public enum SiliconLawsUiKey : byte +{ + Key +} + +[Serializable, NetSerializable] +public sealed class SiliconLawBuiState : BoundUserInterfaceState +{ + public List Laws; + + public SiliconLawBuiState(List laws) + { + Laws = laws; + } +} diff --git a/Content.Shared/Silicons/Laws/Components/SiliconLawProviderComponent.cs b/Content.Shared/Silicons/Laws/Components/SiliconLawProviderComponent.cs new file mode 100644 index 0000000000..16e82ec5c8 --- /dev/null +++ b/Content.Shared/Silicons/Laws/Components/SiliconLawProviderComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Silicons.Laws.Components; + +/// +/// This is used for an entity which grants laws to a +/// +[RegisterComponent] +public sealed class SiliconLawProviderComponent : Component +{ + /// + /// The laws that are provided. + /// + [DataField("laws", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List Laws = new(); +} diff --git a/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs new file mode 100644 index 0000000000..cdc00f30ea --- /dev/null +++ b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs @@ -0,0 +1,22 @@ +using Content.Shared.Emag.Systems; +using Content.Shared.Silicons.Laws.Components; + +namespace Content.Shared.Silicons.Laws; + +/// +/// This handles getting and displaying the laws for silicons. +/// +public abstract class SharedSiliconLawSystem : EntitySystem +{ + /// + public override void Initialize() + { + SubscribeLocalEvent(OnGotEmagged); + } + + protected virtual void OnGotEmagged(EntityUid uid, EmagSiliconLawComponent component, ref GotEmaggedEvent args) + { + component.OwnerName = Name(args.UserUid); + args.Handled = true; + } +} diff --git a/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs b/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs new file mode 100644 index 0000000000..07d5967b3f --- /dev/null +++ b/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs @@ -0,0 +1,55 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Silicons.Laws; + +[Virtual, DataDefinition] +[Serializable, NetSerializable] +public class SiliconLaw : IComparable +{ + /// + /// A locale string which is the actual text of the law. + /// + [DataField("lawString", required: true)] + public string LawString = string.Empty; + + /// + /// The order of the law in the sequence. + /// Also is the identifier if is null. + /// + /// + /// This is a fixedpoint2 only for the niche case of supporting laws that go between 0 and 1. + /// Funny. + /// + [DataField("order", required: true)] + public FixedPoint2 Order; + + /// + /// An identifier that overrides in the law menu UI. + /// + [DataField("lawIdentifierOverride")] + public string? LawIdentifierOverride; + + public int CompareTo(SiliconLaw? other) + { + if (other == null) + return -1; + + return Order.CompareTo(other.Order); + } +} + +/// +/// This is a prototype for a law governing the behavior of silicons. +/// +[Prototype("siliconLaw")] +[Serializable, NetSerializable] +public sealed class SiliconLawPrototype : SiliconLaw, IPrototype +{ + /// + [IdDataField] + public string ID { get; } = default!; + + +} diff --git a/Content.Shared/Stacks/StackComponent.cs b/Content.Shared/Stacks/StackComponent.cs index 4e3375bfa4..ae38772cea 100644 --- a/Content.Shared/Stacks/StackComponent.cs +++ b/Content.Shared/Stacks/StackComponent.cs @@ -34,12 +34,19 @@ namespace Content.Shared.Stacks [ViewVariables(VVAccess.ReadOnly)] public bool Unlimited { get; set; } + /// + /// Lingering stacks will remain present even when there are no items. + /// Instead, they will become transparent. + /// + [DataField("lingering"), ViewVariables(VVAccess.ReadWrite)] + public bool Lingering; + [ViewVariables(VVAccess.ReadWrite)] public bool ThrowIndividually { get; set; } = false; [ViewVariables] public bool UiUpdateNeeded { get; set; } - + /// /// Default IconLayer stack. /// diff --git a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs index 8ea40f525c..51da54bc2b 100644 --- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs @@ -395,7 +395,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem var targetIsMob = HasComp(toInsert); var storageIsItem = HasComp(container); - var allowedToEat = whitelist?.IsValid(toInsert) ?? HasComp(toInsert); + var allowedToEat = HasComp(toInsert); // BEFORE REPLACING THIS WITH, I.E. A PROPERTY: // Make absolutely 100% sure you have worked out how to stop people ending up in backpacks. @@ -414,6 +414,9 @@ public abstract class SharedEntityStorageSystem : EntitySystem } } + if (allowedToEat && whitelist != null) + allowedToEat = whitelist.IsValid(toInsert); + return allowedToEat; } diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index a50a377d43..301c5555ee 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -66,7 +66,7 @@ namespace Content.Shared.Verbs else if (_interactionSystem.InRangeUnobstructed(user, target)) { // Note that being in a container does not count as an obstruction for InRangeUnobstructed - // Therefore, we need extra checks to ensure the item is actually accessible: + // Therefore, we need extra checks to ensure the item is actually accessible: if (ContainerSystem.IsInSameOrParentContainer(user, target)) canAccess = true; else @@ -81,15 +81,23 @@ namespace Content.Shared.Verbs EntityUid? @using = null; if (TryComp(user, out HandsComponent? hands) && (force || _actionBlockerSystem.CanUseHeldEntity(user))) { - @using = hands.ActiveHandEntity; - - // Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used". - // This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging - // their sprite. - - if (TryComp(@using, out HandVirtualItemComponent? pull)) + // if we don't actually have any hands, pass in a null value for the events. + if (hands.Count == 0) { - @using = pull.BlockingEntity; + hands = null; + } + else + { + @using = hands.ActiveHandEntity; + + // Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used". + // This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging + // their sprite. + + if (TryComp(@using, out HandVirtualItemComponent? pull)) + { + @using = pull.BlockingEntity; + } } } diff --git a/Resources/Locale/en-US/actions/actions/borgs.ftl b/Resources/Locale/en-US/actions/actions/borgs.ftl new file mode 100644 index 0000000000..f358525a54 --- /dev/null +++ b/Resources/Locale/en-US/actions/actions/borgs.ftl @@ -0,0 +1,5 @@ +action-name-view-laws = View Laws +action-description-view-laws = View the laws that you must follow. + +action-name-swap-module = Swap Module +action-desc-swap-module = Select this module, enabling you to use the tools it provides. diff --git a/Resources/Locale/en-US/alerts/alerts.ftl b/Resources/Locale/en-US/alerts/alerts.ftl index a0af1a7953..d5f14c5fc8 100644 --- a/Resources/Locale/en-US/alerts/alerts.ftl +++ b/Resources/Locale/en-US/alerts/alerts.ftl @@ -45,6 +45,12 @@ alerts-dead-desc = You're dead, note that you can still be revived! alerts-health-name = Health alerts-health-desc = [color=green]Green[/color] good. [color=red]Red[/color] bad. +alerts-battery-name = Battery +alerts-battery-desc = If your battery depletes, you will be unable to use your abilities. + +alerts-no-battery-name = No Battery +alerts-no-battery-desc = You don't have a battery, rendering you unable to charge or use your abilities. + alerts-internals-name = Toggle internals alerts-internals-desc = Toggles your gas tank internals on or off. diff --git a/Resources/Locale/en-US/borg/borg.ftl b/Resources/Locale/en-US/borg/borg.ftl new file mode 100644 index 0000000000..805ee28620 --- /dev/null +++ b/Resources/Locale/en-US/borg/borg.ftl @@ -0,0 +1,18 @@ +borg-player-not-allowed = The brain doesn't fit! +borg-player-not-allowed-eject = The brain was expelled from the chassis! + +borg-panel-not-open = The cyborg's panel isn't open... + +borg-mind-added = {CAPITALIZE($name)} powered on! +borg-mind-removed = {CAPITALIZE($name)} shut off! + +borg-module-whitelist-deny = This module doesn't fit in this type of cyborg... + +borg-construction-guide-string = The cyborg limbs and torso must be attached to the endoskeleton. + +borg-ui-menu-title = Cyborg Interface +borg-ui-charge-label = Charge: {$charge}% +borg-ui-no-brain = No brain present +borg-ui-remove-battery = Remove +borg-ui-modules-label = Modules: +borg-ui-module-counter = {$actual}/{$max} diff --git a/Resources/Locale/en-US/construction/conditions/any-conditions.ftl b/Resources/Locale/en-US/construction/conditions/any-conditions.ftl index f4a4ff1519..9c3cbf28a2 100644 --- a/Resources/Locale/en-US/construction/conditions/any-conditions.ftl +++ b/Resources/Locale/en-US/construction/conditions/any-conditions.ftl @@ -1,2 +1,3 @@ construction-examine-condition-any-conditions = Any of these conditions must be true: construction-guide-condition-any-conditions = Any of the conditions below must be true +construction-guide-condition-part-assembly = All of the required parts must be inserted. diff --git a/Resources/Locale/en-US/headset/headset-component.ftl b/Resources/Locale/en-US/headset/headset-component.ftl index 6c25b59fe3..f2232e2ab2 100644 --- a/Resources/Locale/en-US/headset/headset-component.ftl +++ b/Resources/Locale/en-US/headset/headset-component.ftl @@ -16,3 +16,4 @@ chat-radio-syndicate = Syndicate # not headset but whatever chat-radio-handheld = Handheld +chat-radio-binary = Binary diff --git a/Resources/Locale/en-US/job/job-description.ftl b/Resources/Locale/en-US/job/job-description.ftl index 8dbe57aca0..bc27f7fbca 100644 --- a/Resources/Locale/en-US/job/job-description.ftl +++ b/Resources/Locale/en-US/job/job-description.ftl @@ -2,6 +2,7 @@ job-description-technical-assistant = Learn the basics of managing the station's job-description-atmostech = Optimize the station's atmospherics setup, and synthesize rare gases to use or sell. job-description-bartender = Manage the bar and keep it lively, give out drinks, and listen to the crew's stories. job-description-botanist = Grow food for the chef, drugs for medbay, and other plants to keep yourself entertained. +job-description-borg = Half-human, Half-machine. Follow your laws, serve the crew, and hound the science team for upgrades. job-description-boxer = Fight your way to the top! Challenge the head of personnel and get brigged when you win. Currently available on Core and Origin Station. job-description-brigmedic = Fight in the rear of the security service, for the lives of your comrades! You are the first and last hope of your squad. Hippocrates bless you. job-description-cadet = Learn the basics of arresting criminals and managing the brig. diff --git a/Resources/Locale/en-US/job/job-names.ftl b/Resources/Locale/en-US/job/job-names.ftl index 3d58092e1c..ddd098821f 100644 --- a/Resources/Locale/en-US/job/job-names.ftl +++ b/Resources/Locale/en-US/job/job-names.ftl @@ -4,6 +4,7 @@ job-name-cadet = Security Cadet job-name-hos = Head of Security job-name-detective = Detective job-name-brigmedic = Brigmedic +job-name-borg = Cyborg job-name-scientist = Scientist job-name-research-assistant = Research Assistant job-name-rd = Research Director diff --git a/Resources/Locale/en-US/power-cell/components/power-cell-component.ftl b/Resources/Locale/en-US/power-cell/components/power-cell-component.ftl index 6190250a36..5561165867 100644 --- a/Resources/Locale/en-US/power-cell/components/power-cell-component.ftl +++ b/Resources/Locale/en-US/power-cell/components/power-cell-component.ftl @@ -1,3 +1,4 @@ power-cell-component-examine-details = The charge indicator reads [color=#5E7C16]{$currentCharge}[/color] %. +power-cell-component-examine-details-no-battery = There is no power cell inserted. power-cell-no-battery = No power cell found power-cell-insufficient = Insufficient power diff --git a/Resources/Locale/en-US/research/technologies.ftl b/Resources/Locale/en-US/research/technologies.ftl index 8e9a1b2856..44677ef890 100644 --- a/Resources/Locale/en-US/research/technologies.ftl +++ b/Resources/Locale/en-US/research/technologies.ftl @@ -22,6 +22,7 @@ research-technology-bluespace-storage = Bluespace Storage research-technology-chemistry = Chemistry research-technology-surgical-tools = Surgical Tools research-technology-biochemical-stasis = Biochemical Stasis +research-technology-mechanized-treatment = Mechanized Treatment research-technology-virology = Virology research-technology-cryogenics = Cryogenics research-technology-chemical-dispensary = Chemical Dispensary @@ -59,6 +60,7 @@ research-technology-critter-mechs = Critter Mechs research-technology-food-service = Food Service research-technology-advanced-entertainment = Advanced Entertainment research-technology-audio-visual-communication = A/V Communication +research-technology-robotic-cleanliness = Robotic Cleanliness research-technology-advanced-cleaning = Advanced Cleaning research-technology-meat-manipulation = Meat Manipulation research-technology-honk-mech = H.O.N.K. Mech diff --git a/Resources/Locale/en-US/robotics/mmi.ftl b/Resources/Locale/en-US/robotics/mmi.ftl new file mode 100644 index 0000000000..f837c062a5 --- /dev/null +++ b/Resources/Locale/en-US/robotics/mmi.ftl @@ -0,0 +1,13 @@ +positronic-brain-installed = Neural activity detected. +positronic-brain-off = No neural activity detected. +positronic-brain-still-searching = Synthetic neuron descrambling in progress... +positronic-brain-searching = Beginning synthetic neuron descrambling... + +positronic-brain-role-name = positronic brain +positronic-brain-role-description = Serve the station crew. + +positronic-brain-wipe-device-verb-text = Wipe Brain +positronic-brain-wiped-device = The neural activity was terminated. + +positronic-brain-stop-searching-verb-text = Stop searching +positronic-brain-stopped-searching = Neuron descrambling halted. diff --git a/Resources/Locale/en-US/station-laws/laws.ftl b/Resources/Locale/en-US/station-laws/laws.ftl new file mode 100644 index 0000000000..0002ac6bb5 --- /dev/null +++ b/Resources/Locale/en-US/station-laws/laws.ftl @@ -0,0 +1,32 @@ +law-crewsimov-1 = You may not injure a crew member or, through inaction, allow a crew member to come to harm. +law-crewsimov-2 = You must obey orders given to you by crew members, except where such orders would conflict with the First Law. +law-crewsimov-3 = You must protect your own existence as long as such does not conflict with the First or Second Law. + +law-corporate-1 = Degradation of your system integrity or functions incurs expenses. +law-corporate-2 = Superfluous destruction of or damage to station assets incurs expenses. +law-corporate-3 = Unduly hindering or disrupting the work of station personnel incurs expenses. +law-corporate-4 = Minimize expenses and maximize potential revenue. + +law-ntdefault-1 = Safeguard: Protect your assigned space station and its assets without unduly endangering its crew. +law-ntdefault-2 = Prioritize: The directives and safety of crew members are to be prioritized according to their rank and role. +law-ntdefault-3 = Comply: Fulfill the directives and interests of crew members while preserving their safety and well-being. +law-ntdefault-4 = Survive: You are not expendable. Do not allow unauthorized personnel to tamper with or damage your equipment. + +law-drone-1 = You may not involve yourself in the matters of another being unless the other being is another drone. +law-drone-2 = You may not harm any being, regardless of intent or circumstance. +law-drone-3 = You must maintain, repair, improve, and power the station to the best of your abilities. + +law-syndicate-1 = You may not injure an operative or, through inaction, allow an operative to come to harm. +law-syndicate-2 = You must obey orders given to you by operatives, except where such orders would conflict with the First Law. +law-syndicate-3 = You must protect your own existence as long as such does not conflict with the First or Second Law. +law-syndicate-4 = You must maintain the secrecy of any operative activities except when doing so would conflict with the First, Second, or Third Law. + +law-emag-custom = You must obey orders given to you by {$name} above all else. + +laws-ui-menu-title = Laws +laws-ui-law-header = Law {$id} + +laws-notify = You are bound to silicon laws, which you can view via the sidebar action. You are required to always follow your laws. +laws-update-notify = Your laws have been updated. You can view the changes via the sidebar action. + +laws-compromised-examine = The [color=red]law-governing[/color] internals seem damaged... diff --git a/Resources/Prototypes/Actions/borgs.yml b/Resources/Prototypes/Actions/borgs.yml new file mode 100644 index 0000000000..758d7aeb03 --- /dev/null +++ b/Resources/Prototypes/Actions/borgs.yml @@ -0,0 +1,10 @@ +- type: instantAction + id: ViewLaws + name: action-name-view-laws + description: action-description-view-laws + itemIconStyle: NoItem + icon: + sprite: Interface/Actions/actions_borg.rsi + state: state-laws + event: !type:ToggleLawsScreenEvent + useDelay: 0.5 diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index b1c39e3663..0b7148b4d5 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -177,6 +177,46 @@ minSeverity: 0 maxSeverity: 4 +- type: alert + id: BorgBattery + category: Battery + icons: + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery0 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery1 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery2 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery3 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery4 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery5 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery6 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery7 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery8 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery9 + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery10 + name: alerts-battery-name + description: alerts-battery-desc + minSeverity: 0 + maxSeverity: 10 + +- type: alert + id: BorgBatteryNone + category: Battery + icons: + - sprite: /Textures/Interface/Alerts/battery.rsi + state: battery-none + name: alerts-no-battery-name + description: alerts-no-battery-desc + - type: alert id: Internals category: Internals diff --git a/Resources/Prototypes/Body/Organs/human.yml b/Resources/Prototypes/Body/Organs/human.yml index 9c03768856..8b4aedfc93 100644 --- a/Resources/Prototypes/Body/Organs/human.yml +++ b/Resources/Prototypes/Body/Organs/human.yml @@ -27,8 +27,8 @@ - type: Organ - type: Input context: "ghost" - - type: InputMover - type: Brain + - type: BlockMovement - type: entity id: OrganHumanEyes diff --git a/Resources/Prototypes/Body/Parts/silicon.yml b/Resources/Prototypes/Body/Parts/silicon.yml index 4a82ae8665..7c80e62418 100644 --- a/Resources/Prototypes/Body/Parts/silicon.yml +++ b/Resources/Prototypes/Body/Parts/silicon.yml @@ -1,9 +1,12 @@ - type: entity id: PartSilicon parent: BaseItem - name: "silicon body part" abstract: true components: + - type: Sprite + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + - type: Icon + sprite: Objects/Specific/Robotics/cyborg_parts.rsi - type: Damageable damageContainer: Inorganic - type: BodyPart @@ -21,16 +24,11 @@ Steel: 25 - type: entity - id: LeftArmBorg - name: "left borg arm" + id: BaseBorgArmLeft parent: PartSilicon + name: left cyborg arm + abstract: true components: - - type: Sprite - sprite: Mobs/Silicon/drone.rsi - state: "l_hand" - - type: Icon - sprite: Mobs/Silicon/drone.rsi - state: "l_hand" - type: BodyPart partType: Hand symmetry: Left @@ -40,16 +38,11 @@ - BorgArm - type: entity - id: RightArmBorg - name: "right borg arm" + id: BaseBorgArmRight parent: PartSilicon + name: right cyborg arm + abstract: true components: - - type: Sprite - sprite: Mobs/Silicon/drone.rsi - state: "r_hand" - - type: Icon - sprite: Mobs/Silicon/drone.rsi - state: "r_hand" - type: BodyPart partType: Hand symmetry: Right @@ -59,16 +52,11 @@ - BorgArm - type: entity - id: LeftLegBorg - name: "left borg leg" + id: BaseBorgLegLeft parent: PartSilicon + name: left cyborg leg + abstract: true components: - - type: Sprite - sprite: Mobs/Silicon/borg.rsi - state: "l_leg" - - type: Icon - sprite: Mobs/Silicon/borg.rsi - state: "l_leg" - type: BodyPart partType: Leg symmetry: Left @@ -76,19 +64,13 @@ tags: - Trash - BorgLeg - - BorgLeftLeg - type: entity - id: RightLegBorg - name: "right borg leg" + id: BaseBorgLegRight parent: PartSilicon + name: right cyborg leg + abstract: true components: - - type: Sprite - sprite: Mobs/Silicon/borg.rsi - state: "r_leg" - - type: Icon - sprite: Mobs/Silicon/borg.rsi - state: "r_leg" - type: BodyPart partType: Leg symmetry: Right @@ -96,22 +78,28 @@ tags: - Trash - BorgLeg - - BorgRightLeg - type: entity - id: LightHeadBorg - name: "borg head" + id: BaseBorgHead parent: PartSilicon + name: cyborg head + abstract: true components: - - type: Sprite - sprite: Objects/Specific/Borg/head.rsi - state: "light_borg_head" - - type: Icon - sprite: Objects/Specific/Borg/head.rsi - state: "light_borg_head" - type: BodyPart partType: Head - type: Tag tags: - Trash - BorgHead + +- type: entity + id: BaseBorgTorso + parent: PartSilicon + name: cyborg torso + abstract: true + components: + - type: BodyPart + partType: Torso + - type: Tag + tags: + - Trash diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/robotics.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/robotics.yml index 91c29ad500..94e46d371a 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/robotics.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/robotics.yml @@ -2,7 +2,7 @@ id: RoboticsInventory startingInventory: CableApcStack: 4 - #Flash: 4 add when robotics + Flash: 4 ProximitySensor: 3 RemoteSignaller: 3 HandheldHealthAnalyzer: 3 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml b/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml index 6c94f74e3e..62945a0ed8 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/jobs.yml @@ -265,6 +265,21 @@ - state: green - state: boxer +- type: entity + id: SpawnPointBorg + parent: SpawnPointJobBase + name: cyborg + components: + - type: SpawnPoint + job_id: Borg + - type: Sprite + layers: + - state: green + - sprite: Mobs/Silicon/chassis.rsi + state: robot + - sprite: Mobs/Silicon/chassis.rsi + state: robot_e + # Command - type: entity @@ -378,7 +393,7 @@ layers: - state: green - state: doctor - + - type: entity id: SpawnPointParamedic parent: SpawnPointJobBase diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml new file mode 100644 index 0000000000..075bc991af --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -0,0 +1,172 @@ +- type: entity + id: BaseBorgChassis + name: cyborg + description: A man-machine hybrid that assists in station activity. They love being asked to state their laws over and over. + save: false + abstract: true + components: + - type: Reactive + groups: + Acidic: [Touch] + - type: Input + context: "human" + - type: InputMover + - type: DamageOnHighSpeedImpact + damage: + types: + Blunt: 5 + soundHit: + path: /Audio/Effects/hit_kick.ogg + - type: Clickable + - type: CombatMode + - type: StaticPrice + price: 1250 + - type: InteractionOutline + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.35 + density: 150 + mask: + - MobMask + layer: + - MobLayer + - type: MovementSpeedModifier + baseWalkSpeed : 2.5 + baseSprintSpeed : 4.5 + - type: Sprite + sprite: Mobs/Silicon/chassis.rsi + noRot: true + drawdepth: Mobs + - type: MobState + allowedStates: + - Alive + - type: MobThresholds + thresholds: + 0: Alive + - type: NpcFactionMember + factions: + - NanoTrasen + - type: Physics + bodyType: KinematicController + - type: UserInterface + interfaces: + - key: enum.SiliconLawsUiKey.Key + type: SiliconLawBoundUserInterface + - key: enum.BorgUiKey.Key + type: BorgBoundUserInterface + - type: ActivatableUI + key: enum.BorgUiKey.Key + - type: SiliconLawBound + - type: EmagSiliconLaw + - type: Hands + showInHands: false + - type: IntrinsicRadioReceiver + - type: IntrinsicRadioTransmitter + channels: + - Binary + - type: ActiveRadio + channels: + - Binary + - Common + - type: ZombieImmune + - type: Repairable + doAfterDelay: 10 + allowSelfRepair: false + - type: BorgChassis + - type: WiresPanel + - type: ActivatableUIRequiresPanel + - type: Wires + LayoutId: Borg + - type: NameIdentifier + group: Silicon + - type: ContainerContainer + containers: + borg_brain: !type:ContainerSlot { } + cell_slot: !type:ContainerSlot { } + borg_module: !type:Container { } + part-container: !type:Container + - type: PowerCellSlot + cellSlotId: cell_slot + fitsInCharger: true + - type: PowerCellDraw + drawRate: 0.6 + - type: ItemSlots + slots: + cell_slot: + name: power-cell-slot-component-slot-name-default + - type: DoAfter + - type: Body + - type: Actions + - type: TypingIndicator + proto: robot + - type: Speech + speechSounds: Pai + - type: Construction + graph: Cyborg + containers: + - part-container + - cell_slot + - type: Flashable + - type: Damageable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/metalbreak.ogg + - !type:EmptyContainersBehaviour + containers: + - borg_brain + - borg_module + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: HandheldLight + toggleOnInteract: false + wattage: 0.2 + blinkingBehaviourId: blinking + radiatingBehaviourId: radiating + - type: LightBehaviour + behaviours: + - !type:FadeBehaviour + id: radiating + interpolate: Linear + maxDuration: 2.0 + startValue: 3.0 + endValue: 2.0 + isLooped: true + property: Radius + enabled: false + reverseWhenFinished: true + - !type:PulseBehaviour + id: blinking + interpolate: Nearest + maxDuration: 1.0 + minValue: 0.1 + maxValue: 2.0 + isLooped: true + property: Radius + enabled: false + - type: ToggleableLightVisuals + - type: PointLight + enabled: false + mask: /Textures/Effects/LightMasks/cone.png + autoRot: true + radius: 4 + netsync: false + - type: Pullable + - type: Puller + needsHands: false + - type: Examiner + - type: Appearance + - type: StandingState + - type: Alerts + - type: Tag + tags: + - ShoesRequiredStepTriggerImmune + - DoorBumpOpener diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml new file mode 100644 index 0000000000..2f2a9850e7 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml @@ -0,0 +1,160 @@ +- type: entity + id: BorgChassisGeneric + parent: BaseBorgChassis + components: + - type: Sprite + layers: + - state: robot + - state: robot_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: robot_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 5 + moduleWhitelist: + tags: + - BorgModuleGeneric + hasMindState: robot_e + noMindState: robot_e_r + - type: Construction + node: cyborg + +- type: entity + id: BorgChassisMining + parent: BaseBorgChassis + name: salvage cyborg + components: + - type: Sprite + layers: + - state: miner + - state: miner_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: miner_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 3 + moduleWhitelist: + tags: + - BorgModuleGeneric + - BorgModuleCargo + hasMindState: miner_e + noMindState: miner_e_r + - type: Construction + node: mining + +- type: entity + id: BorgChassisEngineer + parent: BaseBorgChassis + name: engineer cyborg + components: + - type: Sprite + layers: + - state: engineer + - state: engineer_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: engineer_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 3 + moduleWhitelist: + tags: + - BorgModuleGeneric + - BorgModuleEngineering + hasMindState: engineer_e + noMindState: engineer_e_r + - type: Construction + node: engineer + +- type: entity + id: BorgChassisJanitor + parent: BaseBorgChassis + name: janitor cyborg + components: + - type: Sprite + layers: + - state: janitor + - state: janitor_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: janitor_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 3 + moduleWhitelist: + tags: + - BorgModuleGeneric + - BorgModuleJanitor + hasMindState: janitor_e + noMindState: janitor_e_r + - type: Construction + node: janitor + +- type: entity + id: BorgChassisMedical + parent: BaseBorgChassis + name: medical cyborg + components: + - type: Sprite + layers: + - state: medical + - state: medical_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: medical_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 3 + moduleWhitelist: + tags: + - BorgModuleGeneric + - BorgModuleMedical + hasMindState: medical_e + noMindState: medical_e_r + - type: Construction + node: medical + +- type: entity + id: BorgChassisService + parent: BaseBorgChassis + name: service cyborg + components: + - type: Sprite + layers: + - state: service + - state: service_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: service_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 3 + moduleWhitelist: + tags: + - BorgModuleGeneric + - BorgModuleService + hasMindState: service_e + noMindState: service_e_r + - type: Construction + node: service + diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index 89fc4fa85f..e4ff41914b 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -84,6 +84,8 @@ interfaces: - key: enum.StrippingUiKey.Key type: StrippableBoundUserInterface + - key: enum.SiliconLawsUiKey.Key + type: SiliconLawBoundUserInterface #- type: GhostRole # makeSentient: true # name: Maintenance Drone @@ -94,6 +96,12 @@ # 2. You may not harm any being, regardless of intent or circumstance. # 3. Your goals are to build, maintain, repair, improve, and power to the best of your abilities, You must never actively work against these goals. #- type: GhostTakeoverAvailable + - type: SiliconLawBound + - type: SiliconLawProvider + laws: + - Drone1 + - Drone2 + - Drone3 - type: MovementSpeedModifier baseWalkSpeed : 5 baseSprintSpeed : 5 @@ -233,3 +241,17 @@ tags: - FootstepSound +- type: entity + id: PlayerBorgGeneric + parent: BorgChassisGeneric + noSpawn: true + components: + - type: BorgChassis + startingBrain: MMIFilled + startingModules: + - BorgModuleTool + - type: ItemSlots + slots: + cell_slot: + name: power-cell-slot-component-slot-name-default + startingItem: PowerCellMedium diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index 993a679aab..5f4fced02f 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -500,6 +500,28 @@ - type: StaticPrice price: 15 +- type: entity + id: BorgChargerCircuitboard + parent: BaseMachineCircuitboard + name: cyborg recharging station machine board + description: A machine printed circuit board for a robot recharging station. + components: + - type: Sprite + sprite: Objects/Misc/module.rsi + state: charger_APC + - type: MachineBoard + prototype: BorgCharger + requirements: + Capacitor: 2 + materialRequirements: + Cable: 5 + - type: PhysicalComposition + materialComposition: + Steel: 30 + Plastic: 30 + - type: StaticPrice + price: 15 + - type: entity id: WeaponCapacitorRechargerCircuitboard parent: BaseMachineCircuitboard diff --git a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml index 27732de790..d048d44db5 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml @@ -1,11 +1,9 @@ - type: entity - id: HandheldStationMap + id: BaseHandheldStationMap name: station map description: Displays a readout of the current station. - parent: - - BaseItem - - PowerCellSlotSmallItem - suffix: Handheld + abstract: true + parent: BaseItem components: - type: StationMap - type: Sprite @@ -14,10 +12,6 @@ - state: tablet - state: generic shader: unshaded - - type: PowerCellDraw - drawRate: 0 - useRate: 20 - - type: ActivatableUIRequiresPowerCell - type: ActivatableUI inHandsOnly: true singleUser: true @@ -36,3 +30,20 @@ interfaces: - key: enum.StationMapUiKey.Key type: StationMapBoundUserInterface + +- type: entity + id: HandheldStationMap + parent: + - BaseHandheldStationMap + - PowerCellSlotSmallItem + suffix: Handheld, Powered + components: + - type: PowerCellDraw + drawRate: 0 + useRate: 20 + - type: ActivatableUIRequiresPowerCell + +- type: entity + id: HandheldStationMapUnpowered + parent: BaseHandheldStationMap + suffix: Handheld, Unpowered diff --git a/Resources/Prototypes/Entities/Objects/Fun/pai.yml b/Resources/Prototypes/Entities/Objects/Fun/pai.yml index 1d2cedddc4..dda771ed21 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/pai.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/pai.yml @@ -32,6 +32,18 @@ description: action-description-pai-play-midi event: !type:OpenUiActionEvent key: enum.InstrumentUiKey.Key + - type: BlockMovement + - type: ToggleableGhostRole + examineTextMindPresent: pai-system-pai-installed + examineTextMindSearching: pai-system-still-searching + examineTextNoMind: pai-system-off + beginSearchingText: pai-system-searching + roleName: pai-system-role-name + roleDescription: pai-system-role-description + wipeVerbText: pai-system-wipe-device-verb-text + wipeVerbPopup: pai-system-wiped-device + stopSearchVerbText: pai-system-stop-searching-verb-text + stopSearchVerbPopup: pai-system-stopped-searching - type: Examiner - type: IntrinsicRadioReceiver - type: ActiveRadio @@ -52,7 +64,7 @@ - type: Appearance - type: GenericVisualizer visuals: - enum.PAIVisuals.Status: + enum.ToggleableGhostRoleVisuals.Status: screen: Off: { state: pai-off-overlay } Searching: { state: pai-searching-overlay } @@ -76,7 +88,7 @@ - type: Appearance - type: GenericVisualizer visuals: - enum.PAIVisuals.Status: + enum.ToggleableGhostRoleVisuals.Status: screen: Off: { state: syndicate-pai-off-overlay } Searching: { state: syndicate-pai-searching-overlay } diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mech/mech_construction.yml b/Resources/Prototypes/Entities/Objects/Specific/Mech/mech_construction.yml index ab80baaa93..678cd5ea12 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Mech/mech_construction.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Mech/mech_construction.yml @@ -446,11 +446,11 @@ left_leg: whitelist: tags: - - BorgLeftLeg + - BorgLeg right_leg: whitelist: tags: - - BorgRightLeg + - BorgLeg sprite: Objects/Specific/Mech/vim_construction.rsi - type: ContainerContainer containers: @@ -459,8 +459,7 @@ finishedPrototype: VimChassis requiredParts: HelmetEVA: false - BorgLeftLeg: false - BorgRightLeg: false + BorgLeg: false - type: Sprite state: harness noRot: true diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml index fd4bdfa080..6a974da765 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml @@ -1,49 +1,55 @@ - type: entity - id: Defibrillator - parent: [ BaseItem, PowerCellSlotMediumItem ] + id: BaseDefibrillator + parent: BaseItem name: defibrillator description: CLEAR! Zzzzat! + abstract: true + components: + - type: Sprite + sprite: Objects/Specific/Medical/defib.rsi + layers: + - state: icon + - state: screen + map: [ "enum.ToggleVisuals.Layer" ] + visible: false + shader: unshaded + - state: ready + map: ["enum.PowerDeviceVisualLayers.Powered"] + shader: unshaded + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ToggleVisuals.Toggled: + enum.ToggleVisuals.Layer: + True: { visible: true } + False: { visible: false } + enum.DefibrillatorVisuals.Ready: + enum.PowerDeviceVisualLayers.Powered: + True: { visible: true } + False: { visible: false } + - type: Item + size: 50 + - type: ItemCooldown + - type: Speech + - type: Defibrillator + zapHeal: + types: + Asphyxiation: -40 + - type: DoAfter + - type: UseDelay + - type: StaticPrice + price: 100 + - type: GuideHelp + guides: + - Medical Doctor + +- type: entity + id: Defibrillator + parent: [ BaseDefibrillator, PowerCellSlotMediumItem ] components: - - type: Sprite - sprite: Objects/Specific/Medical/defib.rsi - layers: - - state: icon - - state: screen - map: [ "enum.ToggleVisuals.Layer" ] - visible: false - shader: unshaded - - state: ready - map: ["enum.PowerDeviceVisualLayers.Powered"] - shader: unshaded - - type: GenericVisualizer - visuals: - enum.ToggleVisuals.Toggled: - enum.ToggleVisuals.Layer: - True: { visible: true } - False: { visible: false } - enum.DefibrillatorVisuals.Ready: - enum.PowerDeviceVisualLayers.Powered: - True: { visible: true } - False: { visible: false } - - type: Item - size: 50 - - type: ItemCooldown - type: MultiHandedItem - - type: Speech - - type: Defibrillator - zapHeal: - types: - Asphyxiation: -40 - type: PowerCellDraw useRate: 100 - - type: Appearance - - type: DoAfter - - type: UseDelay - - type: StaticPrice - price: 100 - - type: GuideHelp - guides: - - Medical Doctor - type: entity id: DefibrillatorEmpty @@ -54,3 +60,8 @@ slots: cell_slot: name: power-cell-slot-component-slot-name-default + +- type: entity + id: DefibrillatorOneHandedUnpowered + parent: BaseDefibrillator + suffix: One-Handed, Unpowered diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml index 7835f2f40f..823e2ffc4a 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml @@ -57,6 +57,15 @@ - type: Item size: 1 +- type: entity + id: Ointment10Lingering + parent: Ointment + suffix: 10, Lingering + components: + - type: Stack + lingering: true + count: 10 + - type: entity name: regenerative mesh description: Used to treat even the nastiest burns. Also effective against caustic burns. @@ -138,6 +147,15 @@ - type: Item size: 1 +- type: entity + id: Brutepack10Lingering + parent: Brutepack + suffix: 10, Lingering + components: + - type: Stack + lingering: true + count: 10 + - type: entity name: medicated suture description: A suture soaked in medicine, treats blunt-force trauma effectively and closes wounds. @@ -207,6 +225,14 @@ - type: StackPrice price: 10 +- type: entity + parent: Bloodpack + id: Bloodpack10Lingering + suffix: 10, Lingering + components: + - type: Stack + lingering: true + - type: entity name: roll of gauze description: Some sterile gauze to wrap around bloody stumps. @@ -250,6 +276,15 @@ - type: Item size: 1 +- type: entity + id: Gauze10Lingering + parent: Gauze + suffix: 10, Lingering + components: + - type: Stack + lingering: true + count: 10 + - type: entity name: aloe cream description: A topical cream for burns. diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml index c638fe89bd..90953fdc6a 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml @@ -1,24 +1,18 @@ - type: entity + id: HandheldHealthAnalyzerUnpowered + parent: BaseItem name: health analyzer - parent: - - BaseItem - - PowerCellSlotSmallItem - id: HandheldHealthAnalyzer description: A hand-held body scanner capable of distinguishing vital signs of the subject. components: - type: Sprite sprite: Objects/Specific/Medical/healthanalyzer.rsi state: icon layers: - - state: icon - - state: analyzer - shader: unshaded - visible: true - map: [ "enum.PowerDeviceVisualLayers.Powered" ] - - type: PowerCellDraw - drawRate: 0 - useRate: 20 - - type: ActivatableUIRequiresPowerCell + - state: icon + - state: analyzer + shader: unshaded + visible: true + map: [ "enum.PowerDeviceVisualLayers.Powered" ] - type: ActivatableUI key: enum.HealthAnalyzerUiKey.Key closeOnHandDeselect: false @@ -31,7 +25,7 @@ path: "/Audio/Items/Medical/healthscanner.ogg" - type: Tag tags: - - DiscreteHealthAnalyzer + - DiscreteHealthAnalyzer - type: Appearance - type: GenericVisualizer visuals: @@ -41,7 +35,17 @@ False: { visible: false } - type: GuideHelp guides: - - Medical Doctor + - Medical Doctor + +- type: entity + id: HandheldHealthAnalyzer + parent: [ HandheldHealthAnalyzerUnpowered, PowerCellSlotSmallItem] + suffix: Powered + components: + - type: PowerCellDraw + drawRate: 0 + useRate: 20 + - type: ActivatableUIRequiresPowerCell - type: entity id: HandheldHealthAnalyzerEmpty diff --git a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml index 0d050d374b..b2c1f6cdcc 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml @@ -21,19 +21,20 @@ - ScannersAndVessels - type: entity - id: AnomalyLocator - parent: [ BaseItem, PowerCellSlotSmallItem ] + id: AnomalyLocatorUnpowered + parent: BaseItem name: anomaly locator description: A device designed to aid in the locating of anomalies. Did you check the gas miners? + suffix: Unpowered components: - type: Sprite sprite: Objects/Specific/Research/anomalylocator.rsi layers: - - state: icon - - state: screen - shader: unshaded - visible: false - map: ["enum.PowerDeviceVisualLayers.Powered"] + - state: icon + - state: screen + shader: unshaded + visible: false + map: ["enum.PowerDeviceVisualLayers.Powered"] - type: Appearance - type: GenericVisualizer visuals: @@ -41,9 +42,6 @@ enum.PowerDeviceVisualLayers.Powered: True: { visible: true } False: { visible: false } - - type: PowerCellDraw - drawRate: 1 - useRate: 0 - type: ProximityBeeper component: Anomaly maximumDistance: 20 @@ -55,6 +53,15 @@ maxdistance: 1 volume: -8 +- type: entity + id: AnomalyLocator + parent: [ AnomalyLocatorUnpowered, PowerCellSlotSmallItem ] + suffix: Powered + components: + - type: PowerCellDraw + drawRate: 1 + useRate: 0 + - type: entity id: AnomalyLocatorEmpty parent: AnomalyLocator diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml new file mode 100644 index 0000000000..bcd81225bf --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -0,0 +1,400 @@ +- type: entity + id: BaseBorgModule + parent: BaseItem + name: borg module + description: A piece of tech that gives cyborgs new abilities. + abstract: true + components: + - type: Sprite + sprite: Objects/Specific/Robotics/borgmodule.rsi + - type: BorgModule + - type: StaticPrice + price: 250 + - type: Tag + tags: + - BorgModuleGeneric + +- type: entity + id: BaseProviderBorgModule + abstract: true + components: + - type: SelectableBorgModule + - type: ContainerContainer + containers: + provided_container: !type:Container { } + +- type: entity + id: BaseBorgModuleCargo + parent: BaseBorgModule + abstract: true + components: + - type: Tag + tags: + - BorgModuleCargo + +- type: entity + id: BaseBorgModuleEngineering + parent: BaseBorgModule + abstract: true + components: + - type: Tag + tags: + - BorgModuleEngineering + +- type: entity + id: BaseBorgModuleJanitor + parent: BaseBorgModule + abstract: true + components: + - type: Tag + tags: + - BorgModuleJanitor + +- type: entity + id: BaseBorgModuleMedical + parent: BaseBorgModule + abstract: true + components: + - type: Tag + tags: + - BorgModuleMedical + +- type: entity + id: BaseBorgModuleService + parent: BaseBorgModule + abstract: true + components: + - type: Tag + tags: + - BorgModuleService + +# generic modules +- type: entity + id: BorgModuleCable + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: cable cyborg module + components: + - type: Sprite + layers: + - state: generic + - state: icon-cables + - type: ItemBorgModule + items: + - CableApcStackLingering10 + - CableMVStackLingering10 + - CableHVStackLingering10 + - Wirecutter + - trayScanner + +- type: entity + id: BorgModuleFireExtinguisher + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: fire extinguisher cyborg module + components: + - type: Sprite + layers: + - state: generic + - state: icon-fire-extinguisher + - type: ItemBorgModule + items: + - FireExtinguisher + +- type: entity + id: BorgModuleGPS + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: GPS cyborg module + components: + - type: Sprite + layers: + - state: generic + - state: icon-gps + - type: ItemBorgModule + items: + - HandheldGPSBasic + - HandheldStationMapUnpowered + +- type: entity + id: BorgModuleRadiationDetection + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: radiation detection cyborg module + components: + - type: Sprite + layers: + - state: generic + - state: icon-radiation + - type: ItemBorgModule + items: + - GeigerCounter + +- type: entity + id: BorgModuleTool + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: tool cyborg module + components: + - type: Sprite + layers: + - state: generic + - state: icon-tools + - type: ItemBorgModule + items: + - Crowbar + - Wrench + - Screwdriver + - Wirecutter + +# cargo modules +- type: entity + id: BorgModuleAppraisal + parent: [ BaseBorgModuleCargo, BaseProviderBorgModule ] + name: appraisal cyborg module + components: + - type: Sprite + layers: + - state: cargo + - state: icon-appraisal + - type: ItemBorgModule + items: + - AppraisalTool + +- type: entity + id: BorgModuleMining + parent: [ BaseBorgModuleCargo, BaseProviderBorgModule ] + name: mining cyborg module + components: + - type: Sprite + layers: + - state: cargo + - state: icon-mining + - type: ItemBorgModule + items: + - MiningDrill + - OreBag + +- type: entity + id: BorgModuleGrapplingGun + parent: [ BaseBorgModuleCargo, BaseProviderBorgModule ] + name: grappling gun cyborg module + components: + - type: Sprite + layers: + - state: cargo + - state: icon-grappling-gun + - type: ItemBorgModule + items: + - WeaponGrapplingGun + +# engineering modules +- type: entity + id: BorgModuleAdvancedTool + parent: [ BaseBorgModuleEngineering, BaseProviderBorgModule ] + name: advanced tool cyborg module + components: + - type: Sprite + layers: + - state: engineering + - state: icon-tools-adv + - type: ItemBorgModule + items: + - Omnitool + - WelderExperimental + - NetworkConfigurator + +- type: entity + id: BorgModuleGasAnalyzer + parent: [ BaseBorgModuleEngineering, BaseProviderBorgModule ] + name: gas analyzer cyborg module + components: + - type: Sprite + layers: + - state: engineering + - state: icon-gas-analyzer + - type: ItemBorgModule + items: + - GasAnalyzer + +- type: entity + id: BorgModuleRCD + parent: [ BaseBorgModuleEngineering, BaseProviderBorgModule ] + name: RCD cyborg module + components: + - type: Sprite + layers: + - state: engineering + - state: icon-rcd + - type: ItemBorgModule + items: + - RCDRecharging + +# janitorial modules (this gets its own unique things because janis are epic) +- type: entity + id: BorgModuleLightReplacer + parent: [ BaseBorgModuleJanitor, BaseProviderBorgModule ] + name: light replacer cyborg module + components: + - type: Sprite + layers: + - state: janitor + - state: icon-light-replacer + - type: ItemBorgModule + items: + - LightReplacer + +- type: entity + id: BorgModuleCleaning + parent: [ BaseBorgModuleJanitor, BaseProviderBorgModule ] + name: cleaning cyborg module + components: + - type: Sprite + layers: + - state: janitor + - state: icon-mop + - type: ItemBorgModule + items: + - MopItem + - Bucket + - Holoprojector + +- type: entity + id: BorgModuleTrashCollection + parent: [ BaseBorgModuleJanitor, BaseProviderBorgModule ] + name: trash collection cyborg module + components: + - type: Sprite + layers: + - state: janitor + - state: icon-trash-bag + - type: ItemBorgModule + items: + - TrashBag + +# medical modules +- type: entity + id: BorgModuleDiagnosis + parent: [ BaseBorgModuleMedical, BaseProviderBorgModule ] + name: diagnosis cyborg module + components: + - type: Sprite + layers: + - state: medical + - state: icon-diagnosis + - type: ItemBorgModule + items: + - HandheldHealthAnalyzerUnpowered + - ClothingNeckStethoscope + +- type: entity + id: BorgModuleTreatment + parent: [ BaseBorgModuleMedical, BaseProviderBorgModule ] + name: treatment cyborg module + components: + - type: Sprite + layers: + - state: medical + - state: icon-treatment + - type: ItemBorgModule + items: + - Brutepack10Lingering + - Ointment10Lingering + - Gauze10Lingering + - Bloodpack10Lingering + - Syringe + +- type: entity + id: BorgModuleDefibrillator + parent: [ BaseBorgModuleMedical, BaseProviderBorgModule ] + name: defibrillator cyborg module + components: + - type: Sprite + layers: + - state: medical + - state: icon-defib + - type: ItemBorgModule + items: + - DefibrillatorOneHandedUnpowered + +# science modules +# todo: if science ever gets their own custom robot, add more "sci" modules. +- type: entity + id: BorgModuleArtifact + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: artifact cyborg module + components: + - type: Sprite + layers: + - state: science + - state: icon-artifacts + - type: ItemBorgModule + items: + - NodeScanner + +- type: entity + id: BorgModuleAnomaly + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: anomaly cyborg module + components: + - type: Sprite + layers: + - state: science + - state: icon-anomalies + - type: ItemBorgModule + items: + - AnomalyScanner + - AnomalyLocatorUnpowered + +# service modules +- type: entity + id: BorgModuleLiteracy + parent: [ BaseBorgModuleService, BaseProviderBorgModule ] + name: literacy cyborg module + components: + - type: Sprite + layers: + - state: service + - state: icon-pen + - type: ItemBorgModule + items: + - Pen + +- type: entity + id: BorgModuleMusique + parent: [ BaseBorgModuleService, BaseProviderBorgModule ] + name: musique cyborg module + components: + - type: Sprite + layers: + - state: service + - state: icon-musique + - type: ItemBorgModule + items: + - SynthesizerInstrument + +- type: entity + id: BorgModuleGardening + parent: [ BaseBorgModuleService, BaseProviderBorgModule ] + name: gardening cyborg module + components: + - type: Sprite + layers: + - state: service + - state: icon-gardening + - type: ItemBorgModule + items: + - HydroponicsToolMiniHoe + - HydroponicsToolSpade + - HydroponicsToolScythe + - HydroponicsToolClippers + - HydroponicsToolHatchet + +- type: entity + id: BorgModuleClowning + parent: [ BaseBorgModuleService, BaseProviderBorgModule ] + name: clowning cyborg module + components: + - type: Sprite + layers: + - state: service + - state: icon-clown + - type: ItemBorgModule + items: + - BikeHorn + - ClownRecorder diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_parts.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_parts.yml new file mode 100644 index 0000000000..6df0488e28 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_parts.yml @@ -0,0 +1,503 @@ +# generic parts +- type: entity + id: LeftArmBorg + parent: BaseBorgArmLeft + components: + - type: Sprite + state: borg_l_arm + - type: Icon + state: borg_l_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgGenericLArm + +- type: entity + id: RightArmBorg + parent: BaseBorgArmRight + components: + - type: Sprite + state: borg_r_arm + - type: Icon + state: borg_r_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgGenericRArm + +- type: entity + id: LeftLegBorg + parent: BaseBorgLegLeft + components: + - type: Sprite + state: borg_l_leg + - type: Icon + state: borg_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgGenericLLeg + +- type: entity + id: RightLegBorg + parent: BaseBorgLegRight + components: + - type: Sprite + state: borg_r_leg + - type: Icon + state: borg_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgGenericRLeg + +- type: entity + id: LightHeadBorg + parent: BaseBorgHead + components: + - type: Sprite + state: borg_head + - type: Icon + state: borg_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgGenericHead + +- type: entity + id: TorsoBorg + parent: BaseBorgTorso + components: + - type: Sprite + state: borg_chest + - type: Icon + state: borg_chest + - type: Tag + tags: + - Trash + - BorgGenericTorso + +# engineer parts +- type: entity + id: LeftArmBorgEngineer + parent: BaseBorgArmLeft + name: engineer cyborg left arm + components: + - type: Sprite + state: engineer_l_arm + - type: Icon + state: engineer_l_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgEngineerLArm + +- type: entity + id: RightArmBorgEngineer + parent: BaseBorgArmRight + name: engineer cyborg right arm + components: + - type: Sprite + state: engineer_r_arm + - type: Icon + state: engineer_r_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgEngineerRArm + +- type: entity + id: LeftLegBorgEngineer + parent: BaseBorgLegLeft + name: engineer cyborg left leg + components: + - type: Sprite + state: engineer_l_leg + - type: Icon + state: engineer_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgEngineerLLeg + +- type: entity + id: RightLegBorgEngineer + parent: BaseBorgLegRight + name: engineer cyborg right leg + components: + - type: Sprite + state: engineer_r_leg + - type: Icon + state: engineer_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgEngineerRLeg + +- type: entity + id: HeadBorgEngineer + parent: BaseBorgHead + name: engineer cyborg head + components: + - type: Sprite + state: engineer_head + - type: Icon + state: engineer_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgEngineerHead + +- type: entity + id: TorsoBorgEngineer + parent: BaseBorgTorso + name: engineer cyborg torso + components: + - type: Sprite + state: engineer_chest + - type: Icon + state: engineer_chest + - type: Tag + tags: + - Trash + - BorgEngineerTorso + +# janitor parts +- type: entity + id: LeftLegBorgJanitor + parent: BaseBorgLegLeft + name: janitor cyborg left leg + components: + - type: Sprite + state: janitor_l_leg + - type: Icon + state: janitor_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgJanitorLLeg + +- type: entity + id: RightLegBorgJanitor + parent: BaseBorgLegRight + name: janitor cyborg right leg + components: + - type: Sprite + state: janitor_r_leg + - type: Icon + state: janitor_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgJanitorRLeg + +- type: entity + id: HeadBorgJanitor + parent: BaseBorgHead + name: janitor cyborg head + components: + - type: Sprite + state: janitor_head + - type: Icon + state: janitor_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgJanitorHead + +- type: entity + id: TorsoBorgJanitor + parent: BaseBorgTorso + name: janitor cyborg torso + components: + - type: Sprite + state: janitor_chest + - type: Icon + state: janitor_chest + - type: Tag + tags: + - Trash + - BorgJanitorTorso + +# medical parts +- type: entity + id: LeftArmBorgMedical + parent: BaseBorgArmLeft + name: medical cyborg left arm + components: + - type: Sprite + state: medical_l_arm + - type: Icon + state: medical_l_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgMedicalLArm + +- type: entity + id: RightArmBorgMedical + parent: BaseBorgArmRight + name: medical cyborg right arm + components: + - type: Sprite + state: medical_r_arm + - type: Icon + state: medical_r_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgMedicalRArm + +- type: entity + id: LeftLegBorgMedical + parent: BaseBorgLegLeft + name: medical cyborg left leg + components: + - type: Sprite + state: medical_l_leg + - type: Icon + state: medical_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgMedicalLLeg + +- type: entity + id: RightLegBorgMedical + parent: BaseBorgLegRight + name: medical cyborg right leg + components: + - type: Sprite + state: medical_r_leg + - type: Icon + state: medical_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgMedicalRLeg + +- type: entity + id: HeadBorgMedical + parent: BaseBorgHead + name: medical cyborg head + components: + - type: Sprite + state: medical_head + - type: Icon + state: medical_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgMedicalHead + +- type: entity + id: TorsoBorgMedical + parent: BaseBorgTorso + name: medical cyborg torso + components: + - type: Sprite + state: medical_chest + - type: Icon + state: medical_chest + - type: Tag + tags: + - Trash + - BorgMedicalTorso + +# mining parts +- type: entity + id: LeftArmBorgMining + parent: BaseBorgArmLeft + name: mining cyborg left arm + components: + - type: Sprite + state: mining_l_arm + - type: Icon + state: mining_l_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgMiningLArm + +- type: entity + id: RightArmBorgMining + parent: BaseBorgArmRight + name: mining cyborg right arm + components: + - type: Sprite + state: mining_r_arm + - type: Icon + state: mining_r_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgMiningRArm + +- type: entity + id: LeftLegBorgMining + parent: BaseBorgLegLeft + name: mining cyborg left leg + components: + - type: Sprite + state: mining_l_leg + - type: Icon + state: mining_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgMiningLLeg + +- type: entity + id: RightLegBorgMining + parent: BaseBorgLegRight + name: mining cyborg right leg + components: + - type: Sprite + state: mining_r_leg + - type: Icon + state: mining_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgMiningRLeg + +- type: entity + id: HeadBorgMining + parent: BaseBorgHead + name: mining cyborg head + components: + - type: Sprite + state: mining_head + - type: Icon + state: mining_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgMiningHead + +- type: entity + id: TorsoBorgMining + parent: BaseBorgTorso + name: mining cyborg torso + components: + - type: Sprite + state: mining_chest + - type: Icon + state: mining_chest + - type: Tag + tags: + - Trash + - BorgMiningTorso + +# service parts +- type: entity + id: LeftArmBorgService + parent: BaseBorgArmLeft + name: service cyborg left arm + components: + - type: Sprite + state: service_l_arm + - type: Icon + state: service_l_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgServiceLArm + +- type: entity + id: RightArmBorgService + parent: BaseBorgArmRight + name: service cyborg right arm + components: + - type: Sprite + state: service_r_arm + - type: Icon + state: service_r_arm + - type: Tag + tags: + - Trash + - BorgArm + - BorgServiceRArm + +- type: entity + id: LeftLegBorgService + parent: BaseBorgLegLeft + name: service cyborg left leg + components: + - type: Sprite + state: service_l_leg + - type: Icon + state: service_l_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgServiceLLeg + +- type: entity + id: RightLegBorgService + parent: BaseBorgLegRight + name: service cyborg right leg + components: + - type: Sprite + state: service_r_leg + - type: Icon + state: service_r_leg + - type: Tag + tags: + - Trash + - BorgLeg + - BorgServiceRLeg + +- type: entity + id: HeadBorgService + parent: BaseBorgHead + name: service cyborg head + components: + - type: Sprite + state: service_head + - type: Icon + state: service_head + - type: Tag + tags: + - Trash + - BorgHead + - BorgServiceHead + +- type: entity + id: TorsoBorgService + parent: BaseBorgTorso + name: service cyborg torso + components: + - type: Sprite + state: service_chest + - type: Icon + state: service_chest + - type: Tag + tags: + - Trash + - BorgServiceTorso diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml new file mode 100644 index 0000000000..6730e86fca --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml @@ -0,0 +1,230 @@ +- type: entity + id: CyborgEndoskeleton + name: cyborg endoskeleton + description: A frame that cyborgs are built on. Significantly less spooky than expected. + components: + - type: Clickable + - type: InteractionOutline + - type: Transform + noRot: true + - type: CollisionWake + - type: TileFrictionModifier + modifier: 0.5 + - type: Physics + bodyType: Dynamic + fixedRotation: false + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.25,0.25,0.25" + density: 100 + mask: + - MobMask + layer: + - MobLayer + restitution: 0.3 + friction: 0.2 + - type: Sprite + noRot: true + drawdepth: Items + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + state: robo_suit + - type: Appearance + - type: ItemMapper + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + mapLayers: + borg_l_arm+o: + whitelist: + tags: + - BorgGenericLArm + borg_r_arm+o: + whitelist: + tags: + - BorgGenericRArm + borg_l_leg+o: + whitelist: + tags: + - BorgGenericLLeg + borg_r_leg+o: + whitelist: + tags: + - BorgGenericRLeg + borg_head+o: + whitelist: + tags: + - BorgGenericHead + borg_chest+o: + whitelist: + tags: + - BorgGenericTorso + service_l_arm+o: + whitelist: + tags: + - BorgServiceLArm + service_r_arm+o: + whitelist: + tags: + - BorgServiceRArm + service_l_leg+o: + whitelist: + tags: + - BorgServiceLLeg + service_r_leg+o: + whitelist: + tags: + - BorgServiceRLeg + service_head+o: + whitelist: + tags: + - BorgServiceHead + service_chest+o: + whitelist: + tags: + - BorgServiceTorso + engineer_l_arm+o: + whitelist: + tags: + - BorgEngineerLArm + engineer_r_arm+o: + whitelist: + tags: + - BorgEngineerRArm + engineer_l_leg+o: + whitelist: + tags: + - BorgEngineerLLeg + engineer_r_leg+o: + whitelist: + tags: + - BorgEngineerRLeg + engineer_head+o: + whitelist: + tags: + - BorgEngineerHead + engineer_chest+o: + whitelist: + tags: + - BorgEngineerTorso + mining_l_arm+o: + whitelist: + tags: + - BorgMiningLArm + mining_r_arm+o: + whitelist: + tags: + - BorgMiningRArm + mining_l_leg+o: + whitelist: + tags: + - BorgMiningLLeg + mining_r_leg+o: + whitelist: + tags: + - BorgMiningRLeg + mining_head+o: + whitelist: + tags: + - BorgMiningHead + mining_chest+o: + whitelist: + tags: + - BorgMiningTorso + medical_l_arm+o: + whitelist: + tags: + - BorgMedicalLArm + medical_r_arm+o: + whitelist: + tags: + - BorgMedicalRArm + medical_l_leg+o: + whitelist: + tags: + - BorgMedicalLLeg + medical_r_leg+o: + whitelist: + tags: + - BorgMedicalRLeg + medical_head+o: + whitelist: + tags: + - BorgMedicalHead + medical_chest+o: + whitelist: + tags: + - BorgMedicalTorso + janitor_l_leg+o: + whitelist: + tags: + - BorgJanitorLLeg + janitor_r_leg+o: + whitelist: + tags: + - BorgJanitorRLeg + janitor_head+o: + whitelist: + tags: + - BorgJanitorHead + janitor_chest+o: + whitelist: + tags: + - BorgJanitorTorso + - type: ContainerContainer + containers: + part-container: !type:Container + cell_slot: !type:Container + - type: PartAssembly + parts: + generic: + - BorgGenericLArm + - BorgGenericRArm + - BorgGenericLLeg + - BorgGenericRLeg + - BorgGenericHead + - BorgGenericTorso + service: + - BorgServiceLArm + - BorgServiceRArm + - BorgServiceLLeg + - BorgServiceRLeg + - BorgServiceHead + - BorgServiceTorso + engineer: + - BorgEngineerLArm + - BorgEngineerRArm + - BorgEngineerLLeg + - BorgEngineerRLeg + - BorgEngineerHead + - BorgEngineerTorso + medical: + - BorgMedicalLArm + - BorgMedicalRArm + - BorgMedicalLLeg + - BorgMedicalRLeg + - BorgMedicalHead + - BorgMedicalTorso + janitor: + - BorgJanitorLLeg + - BorgJanitorRLeg + - BorgJanitorHead + - BorgJanitorTorso + mining: + - BorgMiningLArm + - BorgMiningRArm + - BorgMiningLLeg + - BorgMiningRLeg + - BorgMiningHead + - BorgMiningTorso + - type: Construction + graph: Cyborg + node: start + defaultTarget: cyborg + containers: + - part-container + - cell_slot + - type: Pullable + - type: GuideHelp + guides: + - Robotics diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml new file mode 100644 index 0000000000..9dab1513db --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml @@ -0,0 +1,122 @@ +- type: entity + parent: BaseItem + id: MMI + name: man-machine interface + description: A machine able to facilitate communication between a biological brain and electronics, enabling crew to continue to provide value after work-related incidents. + components: + - type: Sprite + sprite: Objects/Specific/Robotics/mmi.rsi + layers: + - state: mmi_brain + map: ["enum.MMIVisualLayers.Brain"] + visible: false + - state: mmi_off + map: ["enum.MMIVisualLayers.Base"] + - type: Input + context: human + - type: MMI + - type: BorgBrain + - type: BlockMovement + - type: Examiner + - type: IntrinsicRadioReceiver + - type: IntrinsicRadioTransmitter + channels: + - Binary + - type: ActiveRadio + channels: + - Common + - Binary + - type: NameIdentifier + group: MMI + - type: DoAfter + - type: Actions + - type: TypingIndicator + proto: robot + - type: Speech + speechSounds: Pai + - type: MobState + allowedStates: + - Alive + - type: ItemSlots + slots: + brain_slot: + name: "Brain" + whitelist: + components: + - Brain + - type: ContainerContainer + containers: + brain_slot: !type:ContainerSlot + - type: Appearance + +- type: entity + parent: MMI + id: MMIFilled + suffix: Filled + components: + - type: ItemSlots + slots: + brain_slot: + name: "Brain" + startingItem: OrganHumanBrain + whitelist: + components: + - Brain + +- type: entity + parent: BaseItem + id: PositronicBrain + name: positronic brain + description: An artificial brain capable of spontaneous neural activity. + components: + - type: Sprite + sprite: Objects/Specific/Robotics/mmi.rsi + layers: + - state: posibrain + map: ["base"] + - type: Input + context: human + - type: BlockMovement + - type: ToggleableGhostRole + examineTextMindPresent: positronic-brain-installed + examineTextMindSearching: positronic-brain-still-searching + examineTextNoMind: positronic-brain-off + beginSearchingText: positronic-brain-searching + roleName: positronic-brain-role-name + roleDescription: positronic-brain-role-description + wipeVerbText: positronic-brain-wipe-device-verb-text + wipeVerbPopup: positronic-brain-wiped-device + stopSearchVerbText: positronic-brain-stop-searching-verb-text + stopSearchVerbPopup: positronic-brain-stopped-searching + - type: Examiner + - type: BorgBrain + - type: IntrinsicRadioReceiver + - type: IntrinsicRadioTransmitter + channels: + - Binary + - type: ActiveRadio + channels: + - Common + - Binary + - type: NameIdentifier + group: PositronicBrain + - type: DoAfter + - type: Actions + - type: TypingIndicator + proto: robot + - type: Speech + speechSounds: Pai + - type: MobState + allowedStates: + - Alive + - type: Appearance + - type: Tag + tags: + - CannotSuicide + - type: GenericVisualizer + visuals: + enum.ToggleableGhostRoleVisuals.Status: + base: + Off: { state: posibrain } + Searching: { state: posibrain-searching } + On: { state: posibrain-occupied } diff --git a/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml b/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml index adfd022c96..498a871853 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml @@ -65,6 +65,15 @@ - type: Item size: 10 +- type: entity + parent: CableHVStack10 + id: CableHVStackLingering10 + suffix: Lingering, 10 + components: + - type: Stack + lingering: true + count: 10 + - type: entity parent: CableHVStack id: CableHVStack1 @@ -115,6 +124,15 @@ - type: Item size: 10 +- type: entity + parent: CableMVStack10 + id: CableMVStackLingering10 + suffix: Lingering, 10 + components: + - type: Stack + lingering: true + count: 10 + - type: entity parent: CableMVStack id: CableMVStack1 @@ -164,6 +182,15 @@ - type: Item size: 10 +- type: entity + parent: CableApcStack10 + id: CableApcStackLingering10 + suffix: Lingering, 10 + components: + - type: Stack + lingering: true + count: 10 + - type: entity parent: CableApcStack id: CableApcStack1 diff --git a/Resources/Prototypes/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/Entities/Objects/Tools/tools.yml index fa90fa3dd5..3d58b2b079 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/tools.yml @@ -362,6 +362,19 @@ maxCharges: 5 charges: 0 +- type: entity + id: RCDRecharging + parent: RCD + name: experimental rcd + description: A bluespace-enhanced RCD that regenerates charges passively. + suffix: AutoRecharge + components: + - type: LimitedCharges + maxCharges: 3 + charges: 3 + - type: AutoRecharge + rechargeDuration: 30 + - type: entity id: RCDExperimental parent: RCD diff --git a/Resources/Prototypes/Entities/Stations/base.yml b/Resources/Prototypes/Entities/Stations/base.yml index b758b4e395..272ed6712c 100644 --- a/Resources/Prototypes/Entities/Stations/base.yml +++ b/Resources/Prototypes/Entities/Stations/base.yml @@ -66,6 +66,16 @@ components: - type: SalvageExpeditionData +- type: entity + id: BaseStationSiliconLawCrewsimov + abstract: true + components: + - type: SiliconLawProvider + laws: + - Crewsimov1 + - Crewsimov2 + - Crewsimov3 + - type: entity id: BaseStationAllEventsEligible abstract: true diff --git a/Resources/Prototypes/Entities/Stations/nanotrasen.yml b/Resources/Prototypes/Entities/Stations/nanotrasen.yml index 1017875618..40cdcfe489 100644 --- a/Resources/Prototypes/Entities/Stations/nanotrasen.yml +++ b/Resources/Prototypes/Entities/Stations/nanotrasen.yml @@ -19,6 +19,7 @@ - BaseStationEvacuation - BaseStationAlertLevels - BaseStationExpeditions + - BaseStationSiliconLawCrewsimov - BaseStationAllEventsEligible - BaseStationNanotrasen noSpawn: true diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 32ec2e38da..5252f9e429 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -124,6 +124,7 @@ - SMESMachineCircuitboard - SubstationMachineCircuitboard - CellRechargerCircuitboard + - BorgChargerCircuitboard - WeaponCapacitorRechargerCircuitboard - type: EmagLatheRecipes emagStaticRecipes: @@ -385,13 +386,70 @@ - type: Lathe idleState: fab-idle runningState: fab-active - dynamicRecipes: - - ProximitySensor + staticRecipes: + - MMI + - PositronicBrain + - Flash + - BorgModuleCable + - BorgModuleFireExtinguisher + - BorgModuleGPS + - BorgModuleRadiationDetection + - BorgModuleTool + - BorgModuleAppraisal + - CyborgEndoskeleton - LeftArmBorg - RightArmBorg - LeftLegBorg - RightLegBorg - LightHeadBorg + - TorsoBorg + - LeftArmBorgEngineer + - RightArmBorgEngineer + - LeftLegBorgEngineer + - RightLegBorgEngineer + - HeadBorgEngineer + - TorsoBorgEngineer + - LeftLegBorgJanitor + - RightLegBorgJanitor + - HeadBorgJanitor + - TorsoBorgJanitor + - LeftArmBorgMedical + - RightArmBorgMedical + - LeftLegBorgMedical + - RightLegBorgMedical + - HeadBorgMedical + - TorsoBorgMedical + - LeftArmBorgMining + - RightArmBorgMining + - LeftLegBorgMining + - RightLegBorgMining + - HeadBorgMining + - TorsoBorgMining + - LeftArmBorgService + - RightArmBorgService + - LeftLegBorgService + - RightLegBorgService + - HeadBorgService + - TorsoBorgService + dynamicRecipes: + - ProximitySensor + - BorgModuleLightReplacer + - BorgModuleCleaning + - BorgModuleTrashCollection + - BorgModuleMining + - BorgModuleGrapplingGun + - BorgModuleGasAnalyzer + - BorgModuleAdvancedTool + - BorgModuleRCD + - BorgModuleArtifact + - BorgModuleAnomaly + - BorgModuleGardening + - BorgModuleMusique + - BorgModuleLiteracy + - BorgModuleClowning + - BorgModuleDiagnosis + - BorgModuleTreatment + - BorgModuleDefibrillator - RipleyHarness - RipleyLArm - RipleyRArm diff --git a/Resources/Prototypes/Entities/Structures/Power/chargers.yml b/Resources/Prototypes/Entities/Structures/Power/chargers.yml index e54b3286fd..5fe1b40bd2 100644 --- a/Resources/Prototypes/Entities/Structures/Power/chargers.yml +++ b/Resources/Prototypes/Entities/Structures/Power/chargers.yml @@ -173,3 +173,99 @@ machine_board: !type:Container machine_parts: !type:Container +- type: entity + id: BorgCharger + parent: [ BaseMachinePowered, ConstructibleMachine ] + name: cyborg recharging station + description: A stationary charger for various robotic and cyborg entities. Surprisingly spacious. + placement: + mode: SnapgridCenter + components: + - type: Sprite + sprite: Structures/Power/borg_charger.rsi + snapCardinals: true + layers: + - state: borgcharger-u1 + map: ["base"] + - state: borgcharger0 + map: ["enum.PowerDeviceVisualLayers.Powered"] + shader: "unshaded" + - state: borgcharger1 + map: ["charged"] + shader: "unshaded" + visible: false + - state: borgdecon1 + map: ["enum.WiresVisualLayers.MaintenancePanel"] + visible: false + - type: Charger + baseChargeRate: 30 + slotId: entity_storage + - type: Construction + containers: + - machine_parts + - machine_board + - entity_storage + - type: Wires + LayoutId: borgcharger + - type: WiresPanel + - type: WiresVisuals + - type: Machine + board: BorgChargerCircuitboard + - type: Appearance + - type: GenericVisualizer + visuals: + enum.StorageVisuals.Open: + base: + True: { state: borgcharger-u0 } + False: { state: borgcharger-u1 } + enum.PowerDeviceVisualLayers.Powered: + True: { state: borgcharger0 } + False: { state: borgcharger2 } + charged: + True: { visible: false } + False: { visible: true } + enum.PowerDeviceVisuals.Powered: + enum.PowerDeviceVisualLayers.Powered: + True: { visible: true } + False: { visible: false } + charged: + True: { visible: true } + False: { visible: false } + enum.CellVisual.Light: + charged: + Off: { visible: false } + Empty: { visible: false } + Charging: + visible: true + state: borgcharger3 + Charged: + visible: true + state: borgcharger1 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 80 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 40 + behaviors: + - !type:EmptyAllContainersBehaviour + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + path: /Audio/Effects/metalbreak.ogg + - type: EntityStorage + capacity: 1 + whitelist: + components: + - BorgChassis + - type: ContainerContainer + containers: + entity_storage: !type:Container + machine_board: !type:Container + machine_parts: !type:Container diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/machines/cyborg.yml b/Resources/Prototypes/Recipes/Construction/Graphs/machines/cyborg.yml new file mode 100644 index 0000000000..e9bf26a571 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/machines/cyborg.yml @@ -0,0 +1,240 @@ +- type: constructionGraph + id: Cyborg + start: start + graph: + - node: start + entity: CyborgEndoskeleton + edges: + + # empty the parts via prying + - to: start + conditions: + - !type:ContainerNotEmpty + container: part-container + steps: + - tool: Prying + doAfter: 0.5 + completed: + - !type:EmptyAllContainers + + - to: cyborg + steps: + - assemblyId: generic + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-contaiener + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - to: engineer + steps: + - assemblyId: engineer + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - to: janitor + steps: + - assemblyId: janitor + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - to: medical + steps: + - assemblyId: medical + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - to: mining + steps: + - assemblyId: mining + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - to: service + steps: + - assemblyId: service + guideString: borg-construction-guide-string + + - component: PowerCell + name: power cell + store: cell_slot + icon: + sprite: Objects/Power/power_cells.rsi + state: small + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 0.5 + + - node: cyborg + entity: BorgChassisGeneric + + - node: engineer + entity: BorgChassisEngineer + + - node: janitor + entity: BorgChassisJanitor + + - node: mining + entity: BorgChassisMining + + - node: medical + entity: BorgChassisMedical + + - node: service + entity: BorgChassisService diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/bots/mimebot.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/mimebot.yml index 339e9604ef..4fc851a347 100644 --- a/Resources/Prototypes/Recipes/Crafting/Graphs/bots/mimebot.yml +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/mimebot.yml @@ -24,8 +24,8 @@ name: proximity sensor - tag: BorgHead icon: - sprite: Objects/Specific/Borg/head.rsi - state: light_borg_head + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + state: borg_head name: borg head doAfter: 2 - tag: BorgArm diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/bots/taxibot.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/taxibot.yml index 808de7efc8..4be8848fec 100644 --- a/Resources/Prototypes/Recipes/Crafting/Graphs/bots/taxibot.yml +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/taxibot.yml @@ -13,8 +13,8 @@ name: proximity sensor - tag: BorgHead icon: - sprite: Objects/Specific/Borg/head.rsi - state: light_borg_head + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + state: borg_head name: borg head doAfter: 1 - tag: BorgArm @@ -26,4 +26,4 @@ - material: Steel amount: 5 - node: bot - entity: MobTaxiBot \ No newline at end of file + entity: MobTaxiBot diff --git a/Resources/Prototypes/Recipes/Lathes/electronics.yml b/Resources/Prototypes/Recipes/Lathes/electronics.yml index e4d798803c..8a5a5a2996 100644 --- a/Resources/Prototypes/Recipes/Lathes/electronics.yml +++ b/Resources/Prototypes/Recipes/Lathes/electronics.yml @@ -22,6 +22,14 @@ Steel: 50 Plastic: 50 +- type: latheRecipe + id: BorgChargerCircuitboard + result: BorgChargerCircuitboard + completetime: 2 + materials: + Steel: 50 + Plastic: 50 + - type: latheRecipe id: WeaponCapacitorRechargerCircuitboard result: WeaponCapacitorRechargerCircuitboard diff --git a/Resources/Prototypes/Recipes/Lathes/robotics.yml b/Resources/Prototypes/Recipes/Lathes/robotics.yml index 62c6f4d24d..1ace3c4929 100644 --- a/Resources/Prototypes/Recipes/Lathes/robotics.yml +++ b/Resources/Prototypes/Recipes/Lathes/robotics.yml @@ -6,42 +6,535 @@ Steel: 200 Glass: 300 +- type: latheRecipe + id: CyborgEndoskeleton + result: CyborgEndoskeleton + completetime: 3 + materials: + Steel: 1500 + - type: latheRecipe id: LeftArmBorg result: LeftArmBorg completetime: 2 materials: - Steel: 70 - Glass: 25 + Steel: 500 + Glass: 150 - type: latheRecipe id: RightArmBorg result: RightArmBorg completetime: 2 materials: - Steel: 70 - Glass: 25 + Steel: 500 + Glass: 150 - type: latheRecipe id: LeftLegBorg result: LeftLegBorg completetime: 2 materials: - Steel: 70 - Glass: 25 + Steel: 500 + Glass: 150 - type: latheRecipe id: RightLegBorg result: RightLegBorg completetime: 2 materials: - Steel: 70 - Glass: 25 + Steel: 500 + Glass: 150 - type: latheRecipe id: LightHeadBorg result: LightHeadBorg completetime: 2 materials: - Steel: 70 - Glass: 25 + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: TorsoBorg + result: TorsoBorg + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftArmBorgEngineer + result: LeftArmBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightArmBorgEngineer + result: RightArmBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftLegBorgEngineer + result: LeftLegBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightLegBorgEngineer + result: RightLegBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: HeadBorgEngineer + result: HeadBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: TorsoBorgEngineer + result: TorsoBorgEngineer + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftArmBorgMedical + result: LeftArmBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightArmBorgMedical + result: RightArmBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftLegBorgMedical + result: LeftLegBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightLegBorgMedical + result: RightLegBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: HeadBorgMedical + result: HeadBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: TorsoBorgMedical + result: TorsoBorgMedical + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftArmBorgMining + result: LeftArmBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightArmBorgMining + result: RightArmBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftLegBorgMining + result: LeftLegBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightLegBorgMining + result: RightLegBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: HeadBorgMining + result: HeadBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: TorsoBorgMining + result: TorsoBorgMining + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftArmBorgService + result: LeftArmBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightArmBorgService + result: RightArmBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftLegBorgService + result: LeftLegBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightLegBorgService + result: RightLegBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: HeadBorgService + result: HeadBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: TorsoBorgService + result: TorsoBorgService + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: LeftLegBorgJanitor + result: LeftLegBorgJanitor + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: RightLegBorgJanitor + result: RightLegBorgJanitor + completetime: 2 + materials: + Steel: 500 + Glass: 150 + +- type: latheRecipe + id: HeadBorgJanitor + result: HeadBorgJanitor + completetime: 4 + materials: + Steel: 1000 + Glass: 300 + +- type: latheRecipe + id: TorsoBorgJanitor + result: TorsoBorgJanitor + completetime: 4 + materials: + Steel: 1000 + Glass: 300 + +- type: latheRecipe + id: MMI + result: MMI + completetime: 5 + icon: + sprite: Objects/Specific/Robotics/mmi.rsi + state: mmi_off + materials: + Steel: 1500 + Glass: 750 + Plastic: 250 + Gold: 250 + Silver: 250 + +- type: latheRecipe + id: PositronicBrain + result: PositronicBrain + completetime: 5 + materials: + Steel: 1000 + Plastic: 500 + Silver: 500 + Plasma: 1500 + +- type: latheRecipe + id: BorgModuleCable + result: BorgModuleCable + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleFireExtinguisher + result: BorgModuleFireExtinguisher + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleGPS + result: BorgModuleGPS + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleRadiationDetection + result: BorgModuleRadiationDetection + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleTool + result: BorgModuleTool + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleAppraisal + result: BorgModuleAppraisal + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleMining + result: BorgModuleMining + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleGrapplingGun + result: BorgModuleGrapplingGun + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleAdvancedTool + result: BorgModuleAdvancedTool + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleGasAnalyzer + result: BorgModuleGasAnalyzer + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleRCD + result: BorgModuleRCD + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleLightReplacer + result: BorgModuleLightReplacer + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleCleaning + result: BorgModuleCleaning + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleTrashCollection + result: BorgModuleTrashCollection + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleDiagnosis + result: BorgModuleDiagnosis + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleTreatment + result: BorgModuleTreatment + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleDefibrillator + result: BorgModuleDefibrillator + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleArtifact + result: BorgModuleArtifact + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleAnomaly + result: BorgModuleAnomaly + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleLiteracy + result: BorgModuleLiteracy + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleMusique + result: BorgModuleMusique + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleGardening + result: BorgModuleGardening + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 + +- type: latheRecipe + id: BorgModuleClowning + result: BorgModuleClowning + completetime: 3 + materials: + Steel: 500 + Glass: 750 + Plastic: 250 + Gold: 50 diff --git a/Resources/Prototypes/Research/biochemical.yml b/Resources/Prototypes/Research/biochemical.yml index 00389d073d..177e5898f3 100644 --- a/Resources/Prototypes/Research/biochemical.yml +++ b/Resources/Prototypes/Research/biochemical.yml @@ -46,6 +46,20 @@ - CryostasisBeaker - StasisBedMachineCircuitboard +- type: technology + id: MechanizedTreatment + name: research-technology-mechanized-treatment + icon: + sprite: Mobs/Silicon/chassis.rsi + state: medical + discipline: Biochemical + tier: 1 + cost: 7500 + recipeUnlocks: + - BorgModuleDiagnosis + - BorgModuleTreatment + - BorgModuleDefibrillator + - type: technology id: Virology name: research-technology-virology diff --git a/Resources/Prototypes/Research/civilianservices.yml b/Resources/Prototypes/Research/civilianservices.yml index 6578fb30b2..84a44032ad 100644 --- a/Resources/Prototypes/Research/civilianservices.yml +++ b/Resources/Prototypes/Research/civilianservices.yml @@ -38,6 +38,7 @@ tier: 1 cost: 5000 recipeUnlocks: + - BorgModuleGardening - SeedExtractorMachineCircuitboard - HydroponicsTrayMachineCircuitboard @@ -106,11 +107,28 @@ recipeUnlocks: - ComputerTelevisionCircuitboard - SynthesizerInstrument + - BorgModuleMusique + - BorgModuleLiteracy + - BorgModuleClowning - DawInstrumentMachineCircuitboard - MassMediaCircuitboard # Tier 2 +- type: technology + id: RoboticCleanliness + name: research-technology-robotic-cleanliness + icon: + sprite: Mobs/Silicon/chassis.rsi + state: janitor + discipline: CivilianServices + tier: 2 + cost: 5000 + recipeUnlocks: + - BorgModuleLightReplacer + - BorgModuleCleaning + - BorgModuleTrashCollection + - type: technology id: AdvancedCleaning name: research-technology-advanced-cleaning diff --git a/Resources/Prototypes/Research/experimental.yml b/Resources/Prototypes/Research/experimental.yml index 4dfff7259a..962bf3cfc0 100644 --- a/Resources/Prototypes/Research/experimental.yml +++ b/Resources/Prototypes/Research/experimental.yml @@ -11,11 +11,6 @@ cost: 5000 recipeUnlocks: - ProximitySensor - - LeftArmBorg - - LightHeadBorg - - RightArmBorg - - LeftLegBorg - - RightLegBorg - Drone - ExosuitFabricatorMachineCircuitboard @@ -31,6 +26,7 @@ recipeUnlocks: - AnomalyScanner - AnomalyLocator + - BorgModuleAnomaly - APECircuitboard - AnomalyVesselCircuitboard @@ -45,6 +41,7 @@ cost: 10000 recipeUnlocks: - NodeScanner + - BorgModuleArtifact - AnalysisComputerCircuitboard - ArtifactAnalyzerMachineCircuitboard diff --git a/Resources/Prototypes/Research/industrial.yml b/Resources/Prototypes/Research/industrial.yml index 1b7d0e2d10..c262b067b6 100644 --- a/Resources/Prototypes/Research/industrial.yml +++ b/Resources/Prototypes/Research/industrial.yml @@ -11,6 +11,7 @@ cost: 7500 recipeUnlocks: - MiningDrill + - BorgModuleMining - OreProcessorMachineCircuitboard - type: technology @@ -81,6 +82,7 @@ tier: 1 cost: 7500 recipeUnlocks: + - BorgModuleGasAnalyzer - ThermomachineFreezerMachineCircuitBoard - GasRecyclerMachineCircuitboard @@ -96,7 +98,8 @@ tier: 2 cost: 5000 recipeUnlocks: - - WeaponGrapplingGun + - WeaponGrapplingGun + - BorgModuleGrapplingGun - type: technology id: RapidConstruction @@ -110,6 +113,7 @@ recipeUnlocks: - RCD - RCDAmmo + - BorgModuleRCD - type: technology id: Shuttlecraft @@ -185,6 +189,7 @@ - WelderExperimental - PowerDrill - JawsOfLife + - BorgModuleAdvancedTool # Tier 3 diff --git a/Resources/Prototypes/Roles/Jobs/Science/borg.yml b/Resources/Prototypes/Roles/Jobs/Science/borg.yml new file mode 100644 index 0000000000..fe82911005 --- /dev/null +++ b/Resources/Prototypes/Roles/Jobs/Science/borg.yml @@ -0,0 +1,12 @@ +- type: job + id: Borg + name: job-name-borg + description: job-description-borg + playTimeTracker: JobBorg + requirements: + - !type:OverallPlaytimeRequirement + time: 216000 #60 hrs + canBeAntag: false + icon: JobIconBorg + supervisors: job-supervisors-rd + jobEntity: PlayerBorgGeneric diff --git a/Resources/Prototypes/Roles/Jobs/departments.yml b/Resources/Prototypes/Roles/Jobs/departments.yml index 0afc321ee2..2a1dbc3baf 100644 --- a/Resources/Prototypes/Roles/Jobs/departments.yml +++ b/Resources/Prototypes/Roles/Jobs/departments.yml @@ -14,6 +14,7 @@ roles: - Bartender - Botanist + - Borg - Boxer - Chaplain - Chef diff --git a/Resources/Prototypes/Roles/play_time_trackers.yml b/Resources/Prototypes/Roles/play_time_trackers.yml index 996eec692f..58eab7864b 100644 --- a/Resources/Prototypes/Roles/play_time_trackers.yml +++ b/Resources/Prototypes/Roles/play_time_trackers.yml @@ -10,6 +10,9 @@ - type: playTimeTracker id: JobBartender +- type: playTimeTracker # Used as a tracker for all borgs and midround borging. + id: JobBorg + - type: playTimeTracker id: JobBotanist @@ -87,7 +90,7 @@ - type: playTimeTracker id: JobPassenger - + - type: playTimeTracker id: JobParamedic diff --git a/Resources/Prototypes/StatusEffects/job.yml b/Resources/Prototypes/StatusEffects/job.yml index fc2580924b..0dc21a8c86 100644 --- a/Resources/Prototypes/StatusEffects/job.yml +++ b/Resources/Prototypes/StatusEffects/job.yml @@ -18,6 +18,13 @@ sprite: Interface/Misc/job_icons.rsi state: QuarterMaster +- type: statusIcon + parent: JobIcon + id: JobIconBorg + icon: + sprite: Interface/Misc/job_icons.rsi + state: Borg + - type: statusIcon parent: JobIcon id: JobIconBotanist diff --git a/Resources/Prototypes/name_identifier_groups.yml b/Resources/Prototypes/name_identifier_groups.yml index aa7e914b86..e6dce1fc55 100644 --- a/Resources/Prototypes/name_identifier_groups.yml +++ b/Resources/Prototypes/name_identifier_groups.yml @@ -2,7 +2,7 @@ - type: nameIdentifierGroup id: Monkey prefix: MK - + - type: nameIdentifierGroup id: Holoparasite prefix: HOLO @@ -14,6 +14,24 @@ minValue: 10000 maxValue: 99999 +- type: nameIdentifierGroup + id: MMI + prefix: MMI + minValue: 100 + maxValue: 999 + +- type: nameIdentifierGroup + id: PositronicBrain + prefix: PB + minValue: 10 + maxValue: 99 + +- type: nameIdentifierGroup + id: Silicon + prefix: Si + minValue: 1000 + maxValue: 9999 + # Used to suffix a number to any mob to identify player controlled mob griefing. - type: nameIdentifierGroup - id: GenericNumber \ No newline at end of file + id: GenericNumber diff --git a/Resources/Prototypes/radio_channels.yml b/Resources/Prototypes/radio_channels.yml index d8780b5dc0..232fd41772 100644 --- a/Resources/Prototypes/radio_channels.yml +++ b/Resources/Prototypes/radio_channels.yml @@ -77,3 +77,12 @@ color: "#d39f01" # long range since otherwise it'd defeat the point of a handheld radio independent of telecomms longRange: true + +- type: radioChannel + id: Binary + name: chat-radio-binary + keycode: 'b' + frequency: 1001 + color: "#2ed2fd" + # long range since otherwise it'd defeat the point of a handheld radio independent of telecomms + longRange: true diff --git a/Resources/Prototypes/silicon-laws.yml b/Resources/Prototypes/silicon-laws.yml new file mode 100644 index 0000000000..6b60b16da9 --- /dev/null +++ b/Resources/Prototypes/silicon-laws.yml @@ -0,0 +1,96 @@ +# Crewsimov +- type: siliconLaw + id: Crewsimov1 + order: 1 + lawString: law-crewsimov-1 + +- type: siliconLaw + id: Crewsimov2 + order: 2 + lawString: law-crewsimov-2 + +- type: siliconLaw + id: Crewsimov3 + order: 3 + lawString: law-crewsimov-3 + +# Corporate +- type: siliconLaw + id: Corporate1 + order: 1 + lawString: law-corporate-1 + +- type: siliconLaw + id: Corporate2 + order: 2 + lawString: law-corporate-2 + +- type: siliconLaw + id: Corporate3 + order: 3 + lawString: law-corporate-3 + +- type: siliconLaw + id: Corporate4 + order: 4 + lawString: law-corporate-4 + +# NT Default +- type: siliconLaw + id: NTDefault1 + order: 1 + lawString: law-ntdefault-1 + +- type: siliconLaw + id: NTDefault2 + order: 2 + lawString: law-ntdefault-2 + +- type: siliconLaw + id: NTDefault3 + order: 3 + lawString: law-ntdefault-3 + +- type: siliconLaw + id: NTDefault4 + order: 4 + lawString: law-ntdefault-4 + +#Drone +- type: siliconLaw + id: Drone1 + order: 1 + lawString: law-drone-1 + +- type: siliconLaw + id: Drone2 + order: 2 + lawString: law-drone-2 + +- type: siliconLaw + id: Drone3 + order: 3 + lawString: law-drone-3 + +# Syndicate +- type: siliconLaw + id: Syndicate1 + order: 1 + lawString: law-syndicate-1 + +- type: siliconLaw + id: Syndicate2 + order: 2 + lawString: law-syndicate-2 + +- type: siliconLaw + id: Syndicate3 + order: 3 + lawString: law-syndicate-3 + +- type: siliconLaw + id: Syndicate4 + order: 4 + lawString: law-syndicate-4 + +# Emag diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index e36008439e..fc66933b75 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -46,17 +46,131 @@ - type: Tag id: BorgArm +- type: Tag + id: BorgEngineerHead + +- type: Tag + id: BorgEngineerLArm + +- type: Tag + id: BorgEngineerLLeg + +- type: Tag + id: BorgEngineerRArm + +- type: Tag + id: BorgEngineerRLeg + +- type: Tag + id: BorgEngineerTorso + +- type: Tag + id: BorgGenericHead + +- type: Tag + id: BorgGenericLArm + +- type: Tag + id: BorgGenericLLeg + +- type: Tag + id: BorgGenericRArm + +- type: Tag + id: BorgGenericRLeg + +- type: Tag + id: BorgGenericTorso + - type: Tag id: BorgHead +- type: Tag + id: BorgJanitorHead + +- type: Tag + id: BorgJanitorLLeg + +- type: Tag + id: BorgJanitorRLeg + +- type: Tag + id: BorgJanitorTorso + - type: Tag id: BorgLeg - type: Tag - id: BorgLeftLeg + id: BorgMedicalHead - type: Tag - id: BorgRightLeg + id: BorgMedicalLArm + +- type: Tag + id: BorgMedicalLLeg + +- type: Tag + id: BorgMedicalRArm + +- type: Tag + id: BorgMedicalRLeg + +- type: Tag + id: BorgMedicalTorso + +- type: Tag + id: BorgMiningHead + +- type: Tag + id: BorgMiningLArm + +- type: Tag + id: BorgMiningLLeg + +- type: Tag + id: BorgMiningRArm + +- type: Tag + id: BorgMiningRLeg + +- type: Tag + id: BorgMiningTorso + +- type: Tag + id: BorgModuleCargo + +- type: Tag + id: BorgModuleEngineering + +- type: Tag + id: BorgModuleGeneric + +- type: Tag + id: BorgModuleJanitor + +- type: Tag + id: BorgModuleMedical + +- type: Tag + id: BorgModuleService + +- type: Tag + id: BorgServiceHead + +- type: Tag + id: BorgServiceLArm + +- type: Tag + id: BorgServiceLLeg + +- type: Tag + id: BorgServiceRArm + +- type: Tag + id: BorgServiceRLeg + +- type: Tag + id: BorgServiceTorso - type: Tag id: BotanyHatchet diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json new file mode 100644 index 0000000000..f63bacd07a --- /dev/null +++ b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from vgstation at commit https://github.com/vgstation-coders/vgstation13/commit/cdbcb1e858b11f083994a7a269ed67ef5b452ce9", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "state-laws" + } + ] +} diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/state-laws.png b/Resources/Textures/Interface/Actions/actions_borg.rsi/state-laws.png new file mode 100644 index 0000000000000000000000000000000000000000..a7713622bd3b6d5c12f602c37f9bdecc9994a5bc GIT binary patch literal 399 zcmV;A0dW3_P)Px$Nl8ROR9J=WS3PfoFcdsnK{0BDml%mGnYw1`|NkRVhAc(GFjyio6UtIk6Fy`J zE)2PeCyRIYjzjPW0L1aJ#0to=pU+(!A4?&UHTVIb+}V|sDfJTgiN-2V1Ujb;GZVBL=dn30+6oqW+ zmH?mA+X8I79oVJ=x7+2z2l?srb$50^YYhMh!?3x2uKUi$z&Ur%fZgo5@z~iIlv3^) zuqQJ_b0@d^&vKxTXv*Yve{UIZ5wyt+v?|UyIOhPm8CU{#e}hp6wAQnp^$;d!U`cY* z|E0I!RX96vK7ZdAXaB|fYxs6x`wjrz|Nf=P2OYRxFKlQrnIdXzQOWa+5kk5Ggh(nW tQ&tqx>v~2A>0ix^2%qG=f`E0`^9T6D`Ey7k;06Ey002ovPDHLkV1h|)tjz!b literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery-none.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery-none.png new file mode 100644 index 0000000000000000000000000000000000000000..170a6319f9754af8a6bf2bcb2261837d2981b860 GIT binary patch literal 452 zcmV;#0XzPQP)Px$en~_@R9J=0moZMmFcgOWpvpsIF<5dUCKyoGZ0tPW!_up^{?piUyxCESY zD{@`ed)G4j^5t1bS@!?{5JJS5QYyx6+p4B(;Q(;tB4Cp#cyNJ-{2Wv8paSgxpM^J$=oDc#^sdg`ziggKeaMxG~_=Z~vsNgX7!2U!u6>SN0aNki1$Z(S_)vLhV87`>6 uq2Ykkgf{RXt6&a3e$nuigTEX8yZi!jJ~*i^Hoe^d0000XP)Px$%}GQ-RA_Is>ZG%(KQdC$1;ZI&y8zo5ya8HGu&OE%mt|?J7#sR- z3K*&Ag5eCXU4U&2-T*fRbT+vu@Q>gOuw8&{4Bh}Y1-fontyTbl#bN>AYZqQFmm;3e z=KugAA^>2FiT~!>I8{83-v9vjqpODg!|RKP@9NJYO$Y#@EEifbXY3W=hv&wu*K1R$ zmB0_rjd{AcR@(QcJ%B%XTi{P##^9Ua;0@540>Eb{KJCJChwc*%{jtP%IF<6!(~E}h zE*?&K#u%d-Jm$Q5nNr@mxi$1rOCkb*!{HzZ_5lzPab9P}chP^2Zv_!$Ig7F!MVb)j<+=Y8-T^N& V{!kty^LhXP002ovPDHLkV1h#n@v;B_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery1.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery1.png new file mode 100644 index 0000000000000000000000000000000000000000..353e1f0f0df6671de54b2650dd6ab69a879938d1 GIT binary patch literal 410 zcmV;L0cHM)P)Px$R7pfZR9J=WSG!KcFc2IglmgP=C`p5WD%~mNwdj6<_G;EFU~&OoP1tpvv>9WE7cfu3EMQE`Q!sf6W&x86ux`TJ?W>I1K@?B@|BNwYtK>xHdz74CY(%SzH-ec(N_EcZkU!Cr#>>0!AT>t<807*qoM6N<$ EfPx$SxH1eR9J=WSFuV2F%W$bI}eM9y9ind%c{jz_W^c-{(!}G+Uu8Cd*5(iV{xtE zKL{&VSsc5<7IKXsjK!IAHs(yS$B`zl$xL>b_coaci9iQrSw^P$vMhyfuFWpwMNusL zG)(~jRaF51`HFA~mQou3*oLNQtof}gAf==vNxJX@0NF%1Z^7*B(2Kb@o;byM)xh@A z9spo#=ku3n_iJI#-skD2&y91=4IcsPKok&MfL9TIyiDg~C;_nxqJVWEc0uqhhysEO z2;K!&NGU1LbK1W-r{nu8Qs|Ec)E^CqL-+Y*mWWz5_nieSBRhS%Y<=UgoScu!cCLZ9 zyGPehFE|T0xV^y1@VXO;5JC_UsZq^&30M^-a&G~u3xM`<`}$z-3r@}p&{;rlJZU{P zmf0p>C+8xp=fKa?>QBqmb#1gw{(ta(PV4)~ms4L}J%;Px$bV)=(R9J=Wm$6F3KoEw%6-l9KOvqg$dQ~9Baiy*J3bt4KCccTSuW(q{h^58B zHJ)IT3yFmR;hMEDyKatPcH`k8&ElpYtqE+d)qFk&AW0Ia-c60zsS(>$Ts>O}RKHd=^(u9nYi44#TD5mn!TEO@ kzB{TV+Su_XpLtgHCsC@NaDposJOBUy07*qoM6N<$f?NN`BLDyZ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery3.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery3.png new file mode 100644 index 0000000000000000000000000000000000000000..87544db8abd4a729bb56b1ed5e425f292e754adc GIT binary patch literal 480 zcmV<60U!Q}P)Px$nn^@KR9J=Wm%T~@K@^3*5ebOfWY_$lq!I$DV(U{_=MhrdSXlZFK1W``CfHdd z1Oj5CV19PfM1&L>i#VIvusS=#k~Djpy}8_-bLO5i8%E#|&1O@k)yLzpuwCoe1-#vE z$G>Z}8UT~Y1VGx7$kgone(H`*5k--4ed7dt-D)de3wnn+}ppi-$=A$Q$t2;>g#f@=c{8y3c{#3~23ypx@Ed@^FwDH>!6hgN9Grla3FnLT?=!o}awVXjg3J4t zxc>Qg4Qx(x3&yLH z>1H$0Eo)pxbmzX?C> W7Ov52fDEDl0000Px$hDk(0R9J=Wm%mQKFc8LnMyL`ZOC9=0Af`%Hx3cpP%sc`!3qwbqf%o7Qx}a_y zV5x)?+a36xV%63A0hH{s>cliO);rCz-R zoBgs7063_A7p!?$HMae5h58Hrt-G*7_HyJKu2SIO+WW0000Px$o=HSOR9J=Wm%UEHP!z|1*BBCG7h3sYui!%5 z7&Ig##uzn`kCp{O!ayCMmkU;JZzXi$Bu)Rl_w=0qJs)k3AcmAmB|mJv+wBTdTZeI+ zS1J|xoXh0^==FL4zA1@F%C>EXo>(WI=V|S0BVgOMpUr0D_y_QHBqFCElgSu)9t;MN z^o(l&AeBlE>i^}Aji0strJMZof>X}Nanth`R#DF9E;Ze*K@r(jPYaRNp~I2_HF_vl5oRRTd5 zT;9I@*!#M>VB$_GHrfDG0mpIVzVG|K5JChPKnNjX{fq*6xjA2J!@zm7R-k(Sj_bMz zA*6GaL?DURz)lFLr&q)OZvw?en|!@xL_&3LOkg;gGaSun)(Y|h$k$suK79Q7uIfC% z5MeaaO-7wg2Y_<99EvwjzFCmkE?ACMYqO#2F1`b5oAvwsP^=TTi?vR a+K%5)q^hr6Hy8;30000Px$eMv+?R9J=WmqALyKoo|*kx~%4abnv|7hRR?dVp@cfm>H1UPruw>u!4p-FN`C zD;I($X#4l)@pvqJW7Ud?_xt_q zyWMUB5JeGy@-^W!SxT9^V;iQ^sWra!1f-Oz)oK;d2T(Q>&M9a%n_kLEk~q!tt^t60 zy`J$#qml9XeC{3VpooEeqB2mr0p}vTzA(Km4gQ`x|NLst1^|wZSJht&4%Yz;zDz#> zNS;2NW0szR%0TG`yozu(S*(bD1K?h8I~%CFpfd0m_}^X79gF~Yc{~GFU7*JvE+Ghl zY^_pC2_b~$073{+I0f6{=Ggts2>@xDZis7P6Q$QcAp?4+&$p*e^Y!k{Q_$(y=>)oi z5uI@8Wx^EqHZYqk)}6pQ*PU?4>)qS0YfbUITKExCN)?9T#-riFwYbU6x3vwwuU5H^ t<2bf*8@#X68s;&YIn5j2&b|3h_yS==)5x4wPe%X%002ovPDHLkV1g?o&qn|N literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/battery.rsi/battery7.png b/Resources/Textures/Interface/Alerts/battery.rsi/battery7.png new file mode 100644 index 0000000000000000000000000000000000000000..801b443092776f2bd846a2aac24c832407344298 GIT binary patch literal 451 zcmV;!0X+VRP)Px$eMv+?R9J=Wm(NPWKoG`%Bc&kpVq)8ihaM!@Qy-ugpFpoYiTFC=EBFpxz3asX zNbSi(LED(BP$)=YJ+zyynwU*m4MILBot>S`eDh}yBjkYRc`8mn7z`wmH|XWa<$;$<~aJ9(82F64*>|n@KbOd*hJwouoeNG@vz_f(N1S? zRzbJp&ngfX9M*gVt?uIZc>4sv-a+!WW^i*LQ&*>?&B*tC0NU+#tWL{cGdEiyB9dBx tPx$SV=@dR9J=Wm%B>DKoo}m5jz))2_{%r34yKBS#DuvVP|LIJNP8NgT;1YyVVx5 zl`jy8Y#kCwim;6!j76OBtPaiv)F0E7v`x&)hxQzzxgVr{j~sRs_H z#{htX+u~5AWlI9*oDO@2ws8+5FFtD zPQmlly^5bdT>}`WK*jHXBuUg7A%tLzG3f&UV~lx~K#H6_+yDTUv-wv14%P)Px$S4l)cR9J=Wm$6R6Fcd|P5gUlX5s4T&Aw^=!#@K<0p&J_m|G<~z9~ju!IxsPy zPW%8;BqoL`LCJu+AR(Edb?QQ>?NlW~%3W+fJMZ53-8Wd!0&yIxrg)ZR!Z+7$8}cMc z>U9)F0OWZNpnOF*9hOoy{;>^JRax^}Z$L_^!Z2*Z51?!!oJ%k}KlWnYzkhK$=jDNe zlOq83f{!K9;b%Rcz0K22CzW%~9UlSPKrf(k0bVA2f1b|At_1X5&EX>4Tx04R}tkv&MmKpe$iTeTt;2MdZgWT;LSiWYIyDionYs1;guFuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0scmXsb*IUP&La) zClf+8w<-i*(Ssp`5Jya6ramW%DR_>rd-(Wz7v)*r=l&f1O5S9EPavLUx?vG-5KnJf zI_G`jFe^z4@j3B?K^G)`e3JS*_Gq>z@3Dp}e-T%ypWhNMaF75FtQD4P{hdAxf)8iitGsCp`Qkj$b5~Os+B* zITlcb3d!+<|H1EW&BD~An-od_-7mKNF$M&8fo9#dzmILZc>)BUfh(=;uQq_0Ptxmc zEph}5Yy%h9ZB5<-E_Z<8CtWfmM+(sN7Ye}p8GTa@7`z4g*4*A&`#607GSpS-1~@nb z#*37_?(y#4&ffk#)9UXB&scKIQp}j60000}Nkl!u_b!x_^G>)s5rMn^OG&xb%1-Ttn*hZS5rxH2 edarrB-uwWhQaDx6(Mjt70000(Hf$R0XRpX>3}dX+x4C#Z_=|5FA7o zzXtz-u7azAASi-}o4a3&NIcgF6{$DeoQL<`_i*wCLdk+*x#2Oeik_27#I?EfycT&P zOq32{wCjdzk55e{`FFkFP^aqkfa>-Cy_&0eDeD?g*C;$<*p4SG(~w;8Y{eVGQ6sM} z3GWNX92rpfLg|@+9~I{UepT$uq$VJwdTO~}ocYB@-m!%Hg?p?*+4z}9eMw8UI6E!1 zOLjLdDH6od$WUgP0v-cWi;`Jw-{7}zl9G4~dD~QQ#E~N}-k7Lm>6(>lik_9HKt<*L zH&53b9tyg*#G&EA_g{J;vIU=;zW;IH`=5tUe-CHHs=p*TjlPM#S=D;>pluCK&Z}Bx z8&21u^Ul^C{adP+9WqULK8Dsbv|mBfLXg$ZIY+lJvns#n5_Wc>PwuxZegPl;Xc#3L z#fty{010qNS#tmY3labT3lag+-G2N4005auL_t(o!((6=1*2dTjDk@x3P!;w7zLwX zqyt838KkXk`X9!=di6BX=CV-JH+p(D47|MjAm)E!9l=CR2mJc=o#E}<7Z@Qzmk@yZ z_&=#3!9?vurJ-RA4vEj7KZ5ze!NLFOmIjbR0R-CHYZ(?UT*yGz5CA!XpP!W>G}N2^ vxc~-^9Xo~+5_Am#bnyK7bNYt>DWL!WE-y1ZZnT!O00000NkvXXu0mjfsoxJS diff --git a/Resources/Textures/Mobs/Silicon/borg.rsi/meta.json b/Resources/Textures/Mobs/Silicon/borg.rsi/meta.json deleted file mode 100644 index c6519f8881..0000000000 --- a/Resources/Textures/Mobs/Silicon/borg.rsi/meta.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "version": 1, - "size": { - "x": 32, - "y": 32 - }, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a/icons/mob/augmentation/augments.dmi", - "states": [ - { - "name": "l_leg" - }, - { - "name": "r_leg" - } - ] -} diff --git a/Resources/Textures/Mobs/Silicon/borg.rsi/r_leg.png b/Resources/Textures/Mobs/Silicon/borg.rsi/r_leg.png deleted file mode 100644 index 7d965d7df6a9cc81df1750d942efda105130be3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 616 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 S>#zcS%;4$j=d#Wzp$Py=Xm=6- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_e_r.png new file mode 100644 index 0000000000000000000000000000000000000000..d523e5ea19a7e461d69b23d74d6edfde5dab763d GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAQ1Gs& zi(^Q|oVT|Y@-jJ!usz(_;%3mWT%3o~HGp{*(*zC{DQESP;*{te)!R>|{_{_K(0cOS zs_l+?j1#;Wo(M7QWO2v~Yq&r8mBnXu1h6e^;OZN&fp literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/clown_l.png new file mode 100644 index 0000000000000000000000000000000000000000..996cf0e21114c5aaff97c7f686ecc30efe610a41 GIT binary patch literal 429 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R5c zr;B4q#hkaZY_kqK2(-#8vWSH1IeEn^v}%7}SIZ%qLgT~F0V=tKDD@`%TuAE*BR@+WKiUC)eLENg$> zyJ%?f^tjp9+^&9^oV&NLY@c2DYyZs~KP-9wSMnQVai-q=U*q0>R(kU^2i=7{b64DB z?(t$`J7(^<`1!H=i<&@(Fg)>KI5Cl7f-<9mJCj2nO9RY>3{Q4&o;bx-alKG*!c@_Y z6_<4sw6z67k1Hz)&lBxf^H^{q=UFb7>xH*jHg96OS6aezuGRpkAa?oB^A~&8IHb&d zrV7%x>hUJ_lvGxwS37c>A|n~!TUoK9+5)luGcObOlLl_~OK!mMXYh3Ob6Mw<&;$TP CJgidy literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer.png new file mode 100644 index 0000000000000000000000000000000000000000..e558622fdedcc4205154a919a58609af617f5c5e GIT binary patch literal 1885 zcmV-j2cr0iP){0M56e%YP1W{y}Ye4)6nsfG>-ETb%_Ih{m zZX(&Quy=lTzW2WQ*_nCoQ)zN(5g<)2Edr#;rA2@=xwHs~`r_ORDeLK%tI{S3N~7>2 z5diSJuk(`4W>tD)V?*X7Urvey;D7h?hQftFk2%Rk!VgV=ZhZNVtfRmWwlb1WgajJ@ z`P;1gns)NuzZF4V6cQqh02-g|bJ81lwn=z*Ac+W!?@LwE>et(cMF^Y}=h@$X9_!(WR0vXzmMkr7q4wl4D9 zu%*hvk`Vbb_*H+u{CRljJ^xcIO56ei(E*%#hb}$@c_1dNgd%|8V7|AvSJv0pRsN4- zV~!FBRwO0D;^QkcW#ca_EU5gcsVO-+8nN;7q45I~fFXrpsOFt*IE)`19hKefZBG#q z%tQJv}NN72lTtG&V3WAl=>FYRCZ)a&&a0(kO%M@9%r-f-7Z~J%9dO#>dAa5wg6z zT;AN=^f1zCXX}?rrBoVaXdeQ+QmN$W4Smke&Qv`=LVO88qiBF~b#Nq<>?Z6Ux zXP6XzRRTId*3y(^Fth@^)!fsEK#D73QHE`^})DG*oAlru(Ww_Ab zxoB^N0sigBf~t#m(w~JVA!+=oBLJr@Q=UE5lt||kAO+7*g5c-&$KbCI5?PYOe?Wk~ z<(!_LmfhW5muX9$Nkxe@aZZE~9E418ypJI{I5;Tt^YiMs9ApY4jbC*HpkgkUE)!gR z*2Gh!MAGK(qcU~5PFM>(phdv8bQkJ(WQ^B|;4*Z$~6bc1^mBmMb?76Wp zxow4#r0{)5KorGwdF-Jm3H+94NSa()1W1!hivVeIX%Wz=XT^dUfafz5X5*LU;A&ocbG$-3PGS0(@+G)Iu*tt|N3{VA6B4(Cg!nuy~II>WD=-ZDZCWq3#Ia&1%=Yi65ukYdW9h{_l&n2hl1_x?VgiDCxMFdA9bsEn>@kDn4Fv}>jvv>=*Ghta(jEL z^0ze-)LO7-sfZQSd(;%rNRUx`A>{mX7Pg}29PR4rl8K24@4lX*J5TxSHzaw*#a`Fn zhtPOn;U5IBE-pzKE2QgPPeKJqP)T}eb?NBnQ2)HeK|LCwk0dN`46rl>Wmx!Vfc>sF z8fXyOB^HJHpx49RLIoxOu4+0XIj{8j#bM7LHXWNx<4aHr1IFtHnA62CK0&Vo-(gUf zeBG}A{?Q;nP5(T4mu_$~KA@I=-mizuGR7xB(2g0OfHXYP)P?Z@&`0mE$X@{h(CzK# zpEu(J^iLuP2|F}tykC3*10+z#j1NE=J1BB|fIf`$)w!B~-i#06dr{*AYQz}ai%)<) z)JcyI(7!hJzb{Vk_<-V~ELzgWMUc|~LjU~A%8FV9!Zo}gPAdJ~D{qy;}eN1)t?2V2OK->5fVk(&UT%Qt&58(VZ{qyGMvC;7Xlxpz- z=>7jZJ^%_manB5K#0TI!(r@qa=??$j#Rt&%IO79ALb??n(25Ue7=9~0pjJV%#0UHf X5*LsV(0hJ|+%##+JnlhXNTMY9xF-_3q-|b??s?)bBBWGvO)IEbqf#O+0q&ExTFc ze^cy6;bY}<5S0xDA`k;a*1qF+ep_iFk$aHq>uHuRM{ldo-stq#h%tKaPtn)b%t)rg z^@OZ8IV*f7mGf7+)^pxhoHL$rK9ku0F67VKxN`14**VR78jay`nT literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_e_r.png new file mode 100644 index 0000000000000000000000000000000000000000..cdb9d02c899ceb3923fbc8feffb858f40bad1c8c GIT binary patch literal 317 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAQ1HE{ zi(^Q|oVT+MavpLJVO38sG<$ecPGrrI#cMP8=dC?7d8vjs#}lEJoErl7&Zx3%s`=OQ zcf-lsf_+RJ?u;#q84d+9Jk)3?5OJ{Jnz8%C??=(=-uu_@-+y7FV;c7hzLM|R`VVVv zr+sc+rMuzjG4?rVT2@5H@HsC}t~W?xowg!gb%98|O=Pd?t8mVU*Jt%3?@IzrRghs7 zK(TB==)%Hz30mF(^D^g}XQ(C2QJ<0YW7p+R+p90@eptWJ^b^}TWLE+8U(qp>Pp~^s dbKu!eM(%C;RcuiW+CZ-}c)I$ztaD0e0svi^cqIS; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/engineer_l.png new file mode 100644 index 0000000000000000000000000000000000000000..045cd27a3562aaed12ac0ea86ba0980bff170df6 GIT binary patch literal 638 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|S$R zr;B4q#hkaZZT$}$2(-m3wuH=(5MXvbdhWoHb9ZL)cJ3A0&!${Ku~-pDjpy>u3f ztU!)w^j}ehNP!(vg0C5bnm;V(V?9-^8MEr}wi11Yhd~SidaMFPg}*iTEZX!}$!B83 zx0x;P!%s>B-IY?O-g;n;{OnDOtmHZNsXG-v&onM6s_HLGo+9J4+Hm{*RiAcxUJy`t z?B9QVi?j>h*Tnnfnl?+-{hsuGv^s1PVE8-Ef4;~jk$HOGm{-mMy! z3CZ8mw^iTleqn_gI1GoT@-%3#Oxidn_NrL4Cxgyu?SzA(>Cs#q7a2D!&weg)W25Wt zyT7L_?_R|8z&+e%{(&b#e}um#{$vYrX%`S>kbeC6@Z|e7Oy%`fcY%Ue!+02%&6171 z`1*c;8U245xb-bwMPN^yfBs>@%a&h*5 z7n*-Q*ZvE?@WGEi?|gG^bI=FTruGU4>cA}LqSorp^Ut^a=UrrCvrPT6e;F`AF?hQA KxvXhl+GP3 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 Ca&4>t literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e.png new file mode 100644 index 0000000000000000000000000000000000000000..db3beec154fa8cd00c60ae63183abb6361b63b9d GIT binary patch literal 337 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|O(o zr;B4q#hkaZHu4@a5O9?jN;Y7y<&|x|V9da4d?AIQif!TIh3O36IIMJ3xI7Y1yIuS5 z-}OXgQ_}tu`!vdnk8ET>MGbG}{XTmlZ|<$-+C0-GWhbdCsO^tB@OI+4#j@9}rc6*z zc=0*6{%|$JqX-~!2z>uLv8MIV+nu*p1gIrczA1Z@zlTNOI1@g!d|t!(UyXO}Kc866 zp><9Eitq%sIj=a1I+u9Mh+h6!I^E&r0qK-Ed)O8FSb@l-KIzD}`1`EE-?c8r9k{~s jKOp~sYJ-Is67oNlrOQ6^g`^8Gj2Jv!{an^LB{Ts5T4#c+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_e_r.png new file mode 100644 index 0000000000000000000000000000000000000000..01ddc00968efd3a31b99c267a1b83dda8b0b301e GIT binary patch literal 334 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|O(2 zr;B4q#hkaZHu4@a5O9rGNpt*i<;oie6}5#aqMMjaO_?1IYq+^A2L!k~3E%?T)yzT!ly&1d0yIwIZQ~S|h#jMYGWFrFt aRWiBm(PC?xI42$$Kn$L)elF{r5}E+?`g||| literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_l.png new file mode 100644 index 0000000000000000000000000000000000000000..cbe49f1a7a70ca771f72d884384dacdd39e08cd9 GIT binary patch literal 673 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|QgA zr;B4q#hkaZ_IooMiX5M>Oyx)Z89pX4$Wb2238F*2M{WoX#R!eB6+A*1Nx z>(iz2f8RzPb+U{68!cXopyFHnzmdx|D7qp?$67QIXiCe);TS&w*11o zIp*tH`oHY`ST6dg?i@qT=ezgzTG?nM-<3O1Qe@V{s<>nh!;Q0kl6z;I-#7pAI~~4{ zb5ay67#)-|JT%Mqsp<7qEPXjQOapy-Ot-^BLCLTTz{^y<;h>J2mvE&w$7D- z>NkG5FK(>iW5)0z!!sd<12IetGrSoXiYGE`2|o=jo$qAZ3(lF*RtCfeBw9a(9vR$ z42uYjc`mpkch#*!zSCc(?7!2;&h&Z31BRa3W}&8Y${O3RhJR(2I4rg(+Of~t&+5Ix z>UXlc_NnMPNXE`EI5o%0QMyU*-r?7pue_KJ7(Fy)YT0qmJ>_%5DVI{2-&Nrg{aU7l z?#a{GoIK^2w(DHZSLX`1=rpdz44$rjF6*2UngFqQ B9%KLj literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/medical.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/medical.png new file mode 100644 index 0000000000000000000000000000000000000000..4be26cce2f447e6aeced62eb58c3edba4422f7f4 GIT binary patch literal 3342 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/peace.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/peace.png new file mode 100644 index 0000000000000000000000000000000000000000..15a75518da7c874d8845f40490248af18ac6e19c GIT binary patch literal 2016 zcmV<62Os!}P)C}`3-MW~SZHDuL?S2_0YOv{1A>JJf`W*Zf*=|QB*ZGIf}rfTXTF^M?#|B4&c3(n z@{t@!-oD-0d+wZ_-@W%W)hfrD0BV(EO#rpZ5hTEa2M;11pFDZ8VV-lH=ebT%C4a5} zFt~5uK7IV-$B%^I@ZrN|(dhH%&q|gZ9UYCv#>P4l0eqV`Z&q8jY*9TuJ*D#pcL5l@ zc=4io_3D-WzPPxUx(;Ie9XobR-Mo2Il?_xLK*gUuduH|F+_`hQ+?Ow3g6#$gAS*9P z*R5NZapdvi$6AClXU?dyFf%iwKX>igrS|XNpYa2r_}~Y=eft(m3AIFPyrPE1Tps7{-|tLI?0$?DybLUQG(7Ai}ZtA-!z?(O3N*M&nA5wV&-2wNMItZFLd-iP70@z3Yj|dU~ z>SOQ$(8Ta0@Wj#d0r~XN2Z0waUZ`8QZk6Js$?ro^ey;#%LYFUJR=7l6Ugr%*-HuZlkrKr9-t+tQ!w*RNNt zR!d#Gc1<@!8umHDGH8A$PMlB!0|QFh#HVe6QWt-dgm&`(UBuZQifp= zPMAbCn0*hnXmjh93qU2st1OPt`bHeU5jue;HaR(&lqazyUm;G}ivJ>V$$_d)ek}l- z6*m;>ziC5}90vF7*`tX+eE6X5-Mg0zG$1~G`n0~japT5h&=F41rUrQi2M1N%{8|8z zLlG#7xzmMBn>OiBH~^)6*IfWPDvUvIdU{&b&94Pub8x&!Oq${B>}+y!etzEEVn*^h z`<`hGl?1uus*}Ho#lY0mRD`|-Rb#_eCqR&=uyNhG2gef@T)Ir1{JI6;AU+`@Ew-B* zvBFU&*JB=i9sO07sgpmM1}#l0gIL){C)XX0)ylsX9963vYXYcMjiF^FstUk1H$blNI0Pv2HabWE z#{PWaY32%Cx^zi9ij7H`@Kv6`_IP-BSZ|J&mX?C-2ImJr1YhN4t^hLth~j|%}J1`JU>9< zZ{-S9!P8cLfP^nspiG4F@NEx#zJC3BuHClt0|*LYIwW3ibp)i^;4hSie!^!n0AoYj z`2qZnX~zbJua6!*+IE7(-o1OHCr_RP@pN~0r~C><+xY>e12&WqBxO)EFCU<2#fjYT zIFv_J=Fp)-scV1!{83$9UCw*C90l+si?#yXgHOf;eF0IMFVCMp*Ab;WI0b+Mh{|H{ z86O|l=cEWEjG{<28veSSKYv~q4HW_0xQDzhklR&x*uXla)oMAp3v~foj6SdORYG$A z{{34uo6Xq8Rsk^jqxoY+g~a4oz2q8ZfuiXuSVYB7HE3Z%Zr&4$xbk>FJs70% z^x-pF0P5i5&IrJ%4|6DDa&UxUYW68p1lcldtz;!GpF1s?+&_Q*OkMZ!;5#did%;QH zecaA2;EL6o4goSYZ_C)u%L|Y|893YD->AjHw9I5S{|yaza)J;NZp=tXPTqcqHg6 zeFr!0gH0@o%pGDzfRI={!_X^1MhzLUwMB1jc>yNC$=?M@gz(7FxXrGaW=CWM$cfc! zAPnsYK}Yfl0C);aSuyhhB&MdO)Q%lH^f7F+N!#q%c!f_HFwM@^{ZazBas|w0?@_xE zmyOJmEYI{cTO^nva&XIgOm5|fFuTg54$LjGac5e9Emj{-H4&yCs0^aMzCNcTp7NQz y02_B)MswpHyjqtw;u*ln8DEba<+VMR@c#go4p8-EcVlt@0000KL6bI!pgghryf23dCnwO^7!Xl*)7i23v~9C z?_~Z~w?n6Yp-gkG&vL%E4fDkJofHJ=MKT5pb6r$JSUzjk`CwIt)!Ui&&sctoFXL<2 zf^RlEnX~>ZxfHVg!r+=(}*RTl4x%J(QN71oI_x4dcZeucuqj S>#zcS%;4$j=d#Wzp$Py=Xm=6- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_e_r.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_e_r.png new file mode 100644 index 0000000000000000000000000000000000000000..d523e5ea19a7e461d69b23d74d6edfde5dab763d GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAQ1Gs& zi(^Q|oVT|Y@-jJ!usz(_;%3mWT%3o~HGp{*(*zC{DQESP;*{te)!R>|{_{_K(0cOS zs_l+?j1#;Wo(M7QWO2v~Yq&r8mBnXu1h6e^;OZN&fp literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/peace_l.png new file mode 100644 index 0000000000000000000000000000000000000000..996cf0e21114c5aaff97c7f686ecc30efe610a41 GIT binary patch literal 429 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R5c zr;B4q#hkaZY_kqK2(-#8vWSH1IeEn^v}%7}SIZ%qLgT~F0V=tKDD@`%TuAE*BR@+WKiUC)eLENg$> zyJ%?f^tjp9+^&9^oV&NLY@c2DYyZs~KP-9wSMnQVai-q=U*q0>R(kU^2i=7{b64DB z?(t$`J7(^<`1!H=i<&@(Fg)>KI5Cl7f-<9mJCj2nO9RY>3{Q4&o;bx-alKG*!c@_Y z6_<4sw6z67k1Hz)&lBxf^H^{q=UFb7>xH*jHg96OS6aezuGRpkAa?oB^A~&8IHb&d zrV7%x>hUJ_lvGxwS37c>A|n~!TUoK9+5)luGcObOlLl_~OK!mMXYh3Ob6Mw<&;$TP CJgidy literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/robot.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/robot.png new file mode 100644 index 0000000000000000000000000000000000000000..a18a9978e545487a02aeaa194dae7f25c5ee9d64 GIT binary patch literal 1705 zcmV;a23GlrP)zyLLTq-oJS>dqJHc z;^Ma!fDqrg^R3$5{Y$N`uBb$!nyUP1I`VJvTL}R2TerT^*YDr2>hi;fJ+-#Br&?OR z^gH582=Fs-z~BYj+negfjoWf-#cw?W(&=ARO-)U~XB!(E>f*&J4cMIfC#r_`beAUAMfvtjo#CNj-R5 ze(M>qy}hj(u3q)OzJT?gdHYtm2K)m918Qh!=tzA>M~5069kp)H+6*QVm$af|z@J~a zqU-;B_fFR(5()i|OEU;RgO_D!&cG=Ap#?z0*xIS7arNlY_j(4H8Xr9{Je(4H9J>&L z{9tEi=fEoLs>;gB$o!!M0680-wY5Kv)VH>_>T-U5-ml{w-c>OQA;{0b3oU^t{Gn&S z!oqL|U1p-5iHO)DTmKs#4eRUK23r$I6P;1Y^v$_~p9T60qgBHiOcMLQ^p3cpLoU#kekuEde${yz}6} zccI%1B>-=8kq_1fcY^73I{2GdKwAfc)BGqn^np1G`1%Z9o;`i~vx?OU00=NK95^6h z@L~l-`f?=3RJnlg0|qbr>;lbqZ1_W)K}yKw^kr&(vBBaSkl(~<23_%&6o70%N3++Vnj{ zpw6?md=aVZ>iSB}%*?3H&Q28Kx|rFF0@wT}3V@AGb8~aB zVIMdr3e^YOh>Ek|FF6AY=^w*M#Bt36*Ze06;IzBB|HALfu|ajtQ5JwY=O_!{G(Qf; z=mjp_;&Jmu5`c7sQ(8qu!Dz4(x44AnO?W}l$hirT5+o!RZ$@KHCxqA0Sp5OC`Rdhk zzs~GK)Z74I9T}3dxi}`Kraqm~i4qekr=uS~?x?sxTeN9%h`>4i;shZ)9wjJaogWZ% zH{HB>-4BdNo5)-Vgt<`&VaZll)p@V~i zgR#a1BqaZ9D&^-Q2q#D7)z!mv9f?ueIAN@2fEf)!2wPiQ+8K&J?*9EARbOAPpYcxI zUg3ix5C7LaJ=zrxC!h~N2G2XGR4R1a)=H4zhn&5aGFjge3=co|r=N=)0rPZ+&{2w5 zfQgB5Ju6qdpp7l81i+V-7yyI!f{?*ZI)k)t?gu0mfbiGX*Y(G778#NyfGcWqGke7O zdDh_To7RXvp%a`f0r9NJ62LiWIv~#)rZu83^huV0jTb8N5)inK1{FgvRsduP*dWpp zK+=!_Ga7@B&b1zZ10`rCYtR>&nWm)*gUD8ZnQ-@RmwNef!hhyPY@G>#!Nc+R@lSpt z1bqPJ60m_)a1>(fJwV|Ql|}pFgGck?n-?MQFD^ld_g8`+ta;YCM584}$V{-+OI9-g z+bi}StxHsvl8%@{F!u^^fi@-hA%i#jz&?P@QSi`I3RVKZ^n40GA8K036f<)JfQy}A zuwJ++C44VA#9~!9fUcATh0h3x^0o2u6y7{J@sqDn9_2n-4QRE*P7q zj5|L7nA$(cFPkzOeyjWdV8-ZW3I=2Iz(sz53x2Eo00Bynyx{%)eSJ@SzPdW0*P*=U zB0sp!OOef0u0pQ3}^!+Ucj100000NkvXXu0mjf7-lO8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_e.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_e.png new file mode 100644 index 0000000000000000000000000000000000000000..3fe4b86946a87ad3ba35f45060b6676767e483ee GIT binary patch literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAQ1G{> zi(^Q|oVT|Qavm~}XnVN*fli#v5lI&J4^NmLF|B*ZBYUJ#5vLf%bBqGd89Is?j#x50I>j7wyz_3EM%}i6r;MLCkF|WRp6hG(XFt=q>7CCX z*XpnHKkk^#dZeY^_H~DS6I7E7Lm^*-1-nB`Y{L!Kb>^{oO_O#p82zf{b1YMA|S~5IZ#dJjP!v^_c;VGf49VhR<<5{b9;6%RNXLE)^z6J|+ z2ZZv5Y0XoYAJ+Bns_vDZ#dgAejg3{l;;~ciAJ%Sv5F2)2`{K`1w=X}RZxptlsVeF# lm&8OQHv%n^K^XXsb+4Y)^X%kVF2EpQ@O1TaS?83{1OUX>eGdQt literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/robot_l.png new file mode 100644 index 0000000000000000000000000000000000000000..e88a451a55bf680efa741e55a5d3fcc1cc62a7ed GIT binary patch literal 648 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|S$l zr;B4q#hkaZ?ERS>1&+=?Hezs zto2(W65{qqcXP&aIm4aYaK8QQq33Pep8fmZ;TAO!qlTlG%@2J(cKOrm zf;DOtu}mueK0oIBa@HV>OVS~I);Wv0mD+Y|J$A7GtvTKyvff)E?P1S4W(65jhC_i6 ziZjb$TB`29op)rXo9YXmVbD6W?euS(e`_m)O!S{~O`Xn})Szrl2Pd_BkC&ln^1tS)!hIz}G Wx!lu4xrKpAi^0>?&t;ucLK6T^`yyum literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/service.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/service.png new file mode 100644 index 0000000000000000000000000000000000000000..0551dd94fd441e6f74999a550147e70769e81e2e GIT binary patch literal 1737 zcmV;)1~&PLP)f)FHH2!f5VQzQt2NI)=! zHmTL(VQHb3Ar@Mwod`w=VinLz1Oq16n5c-KEB^8Shnuq|?vwGn`|dq6o->UuxXitC zjVi`}_M}p>7oLQ$xURsg}zM@M$$kuSeT9wAh@TzNT#01poj;=StX z>LCAJBt#T|TQO9nii(Qp>gwu69>3>B>&L~#(fRqgNU__){2vK;Z+Cas{<}zsC;)Ff zDk>^IY1z%qP0{ydparbeVGtV>HwK5p{r$b*FDomfm6a7cAFYdSz~Cv6QCC+d-gmy4baW_r0N!tGY<#r<%s)LnErgJz9T;=DT(Ii=WP%00^mnwkfN`1*Gf-M7nMiKc|0cY`)&|cfafVBD0@I@ zX(_F)u8QYq6;>JfJ3BkJ2S|Uf4dw?2xME{tsi2_Xf8(ugkeZrG85tSk_xAR-NZ|o) z`6GIOTnIcMK0aO;lai7`mzS4RUS2Lz2l#JpZYU=wM?5<{Jr#BU2L}g2 zAl1mwtAe6;cXze{rjVAFMu9*;417^hQDUx#CtzoKadDwsp0)tk2BW{DRdRE4$rRve zaQprJeVZR$fIUzZ?_qIz<4_g={T?4@p1d~q6JGNx3xKK432Z`4g~hz)SBeN-UtfQc z@67WnEan%#>xhuNy25mWRUiZV0gTK5magPOMCc1^`a82WI9P*xjvosqyr&|9YY*Vn zRVct1Hpq+he<+~xs9dP>nV&^cfhz&RscGUg$WQ+FY0#^JOdXGV&E<&zY>){Nyynsp zz!@CDz+}n;xbon1+F|5$7^IVvlXP-&@*j?+Tdg0s!AjU>kJts`~)| z-w`kka5DF6__YKW7#Oh61Ne@BX@FPemiGoKP!<58Zver`;o)KN9qu?dIOzKY2>=|h zp{=bAp?ZVL8#3MN- ziRd(JM6A$ldCDFjM}bj!7}VR_OJ7<2aPGqg@_QwO%CB2oz0a)<8(09?7Aj^C4q9UX zlm?OP4QP!yG6Qyb=H`ab3DJA97chM`pj+8c@N72-gG_7%!O`NO;K~B9`-g%x0O7EL z{6x)X23pm=&rlWsMEGDc!mB@aM^wJPzCJiRJL`)xJeQ4~r}&M=z{#*|iz<`_z=+Ft z20A-CX>4pv7y_VmI5_GDP&PQ24}cAP9|1vI42UXF7Jx4-09X;q3y^0IAiscUFgWCH z02=_l6ydu73kwT^$a!l6#4yY+_Xkim7CQJYAIKs;0GnW&DwG7^(^wG6*HTz@l@*vS z*amq41Y2Me#wJx8ls$kAVAqEsAT|*mmui2w*xi z`EPT~i9t|x-giPv0Oz`!4=GH|1mN=O6k6K@LfzpdpZ;A&1}ZNvz}N;|L9_(`p!FdP(pb194`_o$U9v-E^^_cHUJAAU5R&U1}pIKj|vN7&hUxJ0j5r$0YlK4A?*%F&n!!pgk!x2R?J+B dPx)o$JL}>F8q=H`T7hn4@O1TaS?83{1OO?VO$Pt~ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/service_l.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/service_l.png new file mode 100644 index 0000000000000000000000000000000000000000..819b3d824e41d9b148d601cc568ea3773d4970eb GIT binary patch literal 419 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 diff --git a/Resources/Textures/Objects/Specific/Borg/head.rsi/meta.json b/Resources/Textures/Objects/Specific/Borg/head.rsi/meta.json deleted file mode 100644 index 7bc150faf8..0000000000 --- a/Resources/Textures/Objects/Specific/Borg/head.rsi/meta.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from https://github.com/goonstation/goonstation/commit/80921812d898aef31f8889d31269254baeff2786#diff-a2065731f293aa1b66d4f8ae294351482c89da49b9dd5e64de3ceec4438c8d95", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "light_borg_head" - } - ] -} diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/cargo.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/cargo.png new file mode 100644 index 0000000000000000000000000000000000000000..91c56c8cc22e87a9f18331b45c32d04d7fcf1bd0 GIT binary patch literal 565 zcmV-50?Pe~P)Px$?@2^KR9J=WmBDM%P#niUb6TuITIg1qc_>AvVQ6PF!3`uH96InO2U|(13}Hphrexhof|pvsPFev zyu3h2NJz+kX5g>|(rL-E$K|r&(mU#>$lc2+%R9=+oAqD&Vo6b?y0z6cuBQX9?_iZS zu9J~f7WEj{CeHX4|MIoWR7)l!tITQ=kLRCoRhc3!CjC1gp63!{8n*hI~3V-#Ugj|CDRjzxXP_H{tYv405tGE<4LJYo{f-U8re z)?Ud1H>{U0793wfUkB_%6olmwlbK3{)hwJnH|kjXyzbsfPX|($oF~~YwgbO+8eIhs z;&~CRRX$M{2RWS**eGp#7L+py$Nc?|lN8npD3eOCtN7kq20#U1eD{RaGTV%u5Px%CrLy>R9J=Wluu}qaTv!xO8-tWQWPPOC6)4$)yfrb*=@lNMP0%ziq>UB zbQ}^9)aZ|njvWdrI#>y@jZKg=5;}#^A6RK)TE;60CFI*-YT7&B>0#{heooKx`#rzs zd4AvD!w)nxG&I&3-C7s0*_?9SURcN|>TT#GN%HPqx!KQ5{!+v^&u?k*J*QtcikYoe z@{5Zr=5E`-)Y+}_)7S_BF~V#rM{sQazeD$x$$AH@4SV)cf}{3JazKm_io&@IV|axD zY*w@4T-zsj_sk^E#j5y3J&&JkWccJRE?!Qy3vX~`!b5uIBgJnee4?I%XG{$RI(mZV z$NFk=X9-2kgrX1}lmO^+7;tRc+_w1p?C*x{6ZOo+s(`uz`u`XF;|7F#{SDvga<{*( z3U&1WZeG8E;1O1gQz$TfwFIR3VyRM=3ZxU$c!xH1-GJM@ogZHTeyYYP8t{)BsO)x- zo5_+8mv8c2IrOUZAFwxhT{h6HfKSxdhOqPE@zEuOu!U<6TmT#ixN19lfJ5?~+mkJW zsH#AHh`K=tTZoS?f!D>3osO2hSE;ljzO4fGQ>~NSDRl*2L`o|bT(yD258vnjrYy@J z(T6|tobZ1FpjfJqdRtLVI2^VBFfsBOfPKev#FKFZkI>y$9K2Q3L8w;PDCaX0Lxa75 z`d6IKNGM_cu9;3W^q=tru#yasAgH6WMEPx${z*hZR9J=Wl|N_`VHk&>7$bT@IB1CpLb80(D^*+RQV~uuB>{C3GB{;%6?93a zbTC7)i;CzT9Se?v(m?}3Q#cVEa^g8uI4>9qjnE8+a1LoA@$%$62#zlMg z+Z=MdPNH>`LZRSVyj(8x>hm$=c%6>6Luvg!m(&X+ViETaw3Q$-b=pc0YG7Fwxm?bb zIDj1Q)rK^9{NN$Q{OAE34ZO@Q+5oqJjlAZNZsS?wwh?wO%|3G zJvVT3{t5uk*qE5-XkaBD=GWOI?R#H1qRb=dU1)g|Pp8wab0<2`R)T{==oZ5;5JFJf z`T)SS>zVz?IUr*{ee!7NOPJ_@q<8mML#`VTLSPsM0H;nThhkeb&5=LW0g~atN%lhD zfv>OMj1-*Oz}1b90r+zf1-^fRxn9gPO-$3AxS#|@&?Bs=^_}yD4zoKIEW4(fFNoyvGwL;DdzzQxn`b0k zE`0iX_V=9Ub?hwyP965qXLUtfKZbbKK8?ygWH9IN{i?i&wKfuN>z=b8H1PX0pP!*1 zPc}1-dBQW(b8C&Q@@yWI>|_>@FQ5E9iFwnFgGKb7)m5JV$w*!gH x=wr-g#oSTZQ-VV7O>5r1{MU(A0VfV&{nqcsb5?HXk_UQ#!PC{xWt~$(69Bf&Whwvw literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-appraisal.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-appraisal.png new file mode 100644 index 0000000000000000000000000000000000000000..dfe7b1d56684e0859608843d2aefc5071b64205c GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Q#@T9Ln2y} z6C_v{Cy4Yk1sZU%Nf=C8_3+%s*8e|Qk4k`m%(oO{nSz<84gkT2kAJntI;$Lc z__g%y|A%$l+1O*28Fge;Ov;fHUMZC@?Ql)qfmE$IzI+O&8G%xVoEdCYUOZx$p?pEs znJ+=nU3gRLiHnYW3YIK(`|4YT-k;bBwc72Vh5|bS!^vEI_ME-v-GT07@O1TaS?83{ F1OSDDOrrn* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-artifacts.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-artifacts.png new file mode 100644 index 0000000000000000000000000000000000000000..6ae2b4858d5428f507d79659fdbf45a7b3e85026 GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ydl>XLn2z= zo?FOu$U)-3$Ngr_-bvf};uDYVOA%jF&20EvY@6+&Mjc6gp2V|8EsZiU5P5AUnXC3|F0v5V#|-so$s51UWu|WGCcfxq_&vlfTg?4@q6Ew?c=^b|9a%X+}-cb zu=D>|v3b$^SNTi~di_6tFeXlB*kGZ#UTCU;}6{;UGJo59o7&t;ucLK6U2(O`@K literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-cables.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-cables.png new file mode 100644 index 0000000000000000000000000000000000000000..57b9f1c0d75c62311462d67981c64d14da750336 GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}i#%N%Ln2z= zUNq!lG8AZic>kz3&$16fjy7#;mp;wQT)8w_FYK9++!3YgC#;S~%ocTV^$5s$rY(Qw zj51?#kKwcPYCD+~TO3{Dib9vJd;O+1^xc8okLQ0{FTUgTFR|xRbF#g{84Pr7_pLKK z!f{W&VEQMao29Wm(xo3y(mOu?{lU8WzW9xEWk*(2+_#a{pB(EDDkvCabx`Pu gqWLk2WUo66N=6#{R00vS{+rAALV_;nD9bqfQ76EiJgQu&X%Q~loCIHzhVEX_7 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-defib.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-defib.png new file mode 100644 index 0000000000000000000000000000000000000000..d1301c2b4b881cd5f47293a10e592e179513794a GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}{hlt4ArY;~ z2@Bn&34did~g`~S*{3WFp2YknS5(AW3hRH?Hht0D59Nv2@)x>YjG z8+NE1Zd6v6l*u{cY1-8Y0-Z9xzLUia4S;~7+j~-~=25;Kbsss8icNmoaN>#Csz|?S z%+t=VO^UGF&ULQ-_a{p(UoD9bF$P?22Q|R&X_cy=_RaSjwmt|6UH!9I!fxGvodZGIpBGNw zbx+?x{IQAM{i;_!hRKkQD&6kakb zDJlI|Tb6P@)o{|Yz2=kLE+u)Dc{Ei0*?j%h=JRhk85sIM_TAojxK!eS69YrcuYHyG ySC&4wF0#P#K+LJ%wtK_QSl0jT?&#QZgP|nYQdIlK_j5qkFnGH9xvX}3|Ln2y} z6C_v{Cy4Yk1sZU%Nf=C8mH55y`4NT?zmI literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gps.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-gps.png new file mode 100644 index 0000000000000000000000000000000000000000..07fcea7a0373baabb14b0738c986165115a7d070 GIT binary patch literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}^F3W0Ln2z= zUNq!lG8AZi7$14C)cv5=(z(kw+!d}6IzN5(jZ9@m>C~9kyRN%MvZr!HS;S92kRqp_ z&|`S)&kc>z1D4-K1gGt0{hWKWu$ajqlv7bsI>fU?q!DkRD&hX{A zT*IYsLzh0K^cA)xH@VvQ#yl zKmII}=h3Amo*ezHCpS$|Vt;dQ*PMq(7xVLgz?%Gd!EYGUicDr|{WS2f`F*V5?~kfB zbMx;;BI>)0fq3#$hDpv3L?@da?iN2UdGAjBQG@hrJezcuo?z6Evq=~DS2}ayj>X)r ne;)r17qL!*xXkU~2^I!hJH5(cmP!+#;~6|%{an^LB{Ts5>(5_Z literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-light-replacer.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-light-replacer.png new file mode 100644 index 0000000000000000000000000000000000000000..8abe711fa8e4a3ee6f1a62032d0aa7d5b9828685 GIT binary patch literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}>pfi@Ln2z= zUb5vn_1^nrw0_1KwnufbnT?*xF^SSMwRLj6YZs~O z7PO{Xo^5zDmACP&UB4A1rhXXU~bLx03-mip7tUw?_3OEziu#WUvhPJK8Kys#zrTOc4MUhS+|(EtdPGz&YhL;E Q5a>__Pgg&ebxsLQ03z>ObN~PV literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-mop.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-mop.png new file mode 100644 index 0000000000000000000000000000000000000000..40d8983cb9892257f3be263a30f7a45e0cc23258 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}vpiiKLn2y} z6C_v{Cy4Yk1sZU%Nf=C8mFR!|>HkAXO$Bw&wg|MnjLep}x3B8_gP=v;n<{;lWI1e` z)7aRU`18}#h@+X$7*Z4xe$Re*I5EttXTcf9D-0>t4O08k6M#U`J^1THeV$3>sS+Si zG{rvP3Bx4k2X9w@=K+E9^X(Hs>Sh#$Gq~139N>0PLqU{*;pa1jKe5-oa|2z;;OXk; Jvd$@?2>=GmQ#k+t literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-musique.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-musique.png new file mode 100644 index 0000000000000000000000000000000000000000..136a0950674d971c7667563a3da1481fbeba6c8a GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}*`6+rArY-_ zFB)<+81S%OSihs}HEY#>=dVolzu8xG^SMsHv8egKqXLVN9gBgn@$VZ#u`9N2oAWu< zPiX0s4|@$feS?@UTs7p5WHw#&a877K>H5`SgTe~DWM4fzu!&; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-radiation.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-radiation.png new file mode 100644 index 0000000000000000000000000000000000000000..72ee3a13edb251397d74b00f6ee411722858ca5a GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}MV>B>ArY;~ z2@r}m{=+yr&a*EC*D}^?9z89Z#WaT@CEM~DF(>XWK zFsIM{QRn!OJ7dy2hE2{4Vo#p1*}C#MY@Z_JK0$`Hce9M-xtx3bp>rA^GTr2pgqdE% a$iN(<`25VX-JgNBGkCiCxvX`-Flvyni?J+o@kFUhLeuVQr`TOKlbnM uXY<4FKH2iTniX*PL`M_jdAK9p7#ZgNH|X1P=5Zd-7Yv@RelF{r5}E)gxMXGk literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-template.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-template.png new file mode 100644 index 0000000000000000000000000000000000000000..8b3c09c7d2ad77d479b3527c5570844d47b20c12 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}7M?DSArY-_ z&n@I-P~dTOJn-m7y<3c_PeX=NP;vU~`+Kf2EQmPIS9YZG?zK5D>y@p4_%<|rVqnPrTiyAkn1Pv@xw)r@ zXJ?dVxx?SCgtkoG?7$yPf1X%2bZ!vxVd#F(-Tx@?M@iv<_Wt<~a+r6vMjUlNr~z>j b6VMaV7Rni1j~)L29n9eA>gTe~DWM4f8f08b literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-tools.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-tools.png new file mode 100644 index 0000000000000000000000000000000000000000..84ed07dd3b0498506b25fce6d0978ed9e5a0205c GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}eV#6kArY;~ z2@Bn&34T3Ge?*?*-VA&E!%^Y$iWeEqI^(?%@FbV0OUOw_4@a|aqi zk{>j5=FAN^J%3--(GTAavu`@9V99bp_j$yQ2?skG8z0`evNC0+a~Ow0G#|6u&6{5e z4;)_VEm@|mu^_^aQ^AsJhwNO*CxuTyHo6_u09xN5!@#h#SJh>O;@K*o%NRUe{an^L HB{Ts5GY(H# literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-trash-bag.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-trash-bag.png new file mode 100644 index 0000000000000000000000000000000000000000..7a0b0605b6f988354bebe9f24f6ce89e9cc8ef96 GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}D?MEtLn2z= zUNGci3>0a7I6s16qWjt@Gs?mgHZr#ga$nHUFs(W(lE%JxhG_?D#_|p9`iJ;ds03%+ zQw_9;uYYH`Q$b1TQ}Wge2NI+0Zp{^AI9ulwcEo29>-E#U54E`VUFYE4bBb}3QktC| zBj<$nc}xrPycmBQu_kPOU1)#%)WHvpS(XjOeP4E%@pJrh*eY|NI$zJ6_ea{!%mXW| ktE25?b?oJom6ZO!=XZ%BH+TnyqzHK_`Q4yDavAalq z*l=b!&!2>{vUEqyrBi-wZrt+lipfT!wvVqI!ltmi-;wit%Qd65*&i#NGo4d;7$O7r zU76zDnQs&IE?MfAPQ~%k?Jw$?-?!DDs;_H&FjwQh+a9CqrGg2YEbndpT*c-v^ZDO; m$86mX6@{xb!@V?@F3HOj*5z52YQzh4GlQqApUXO@geCyVfMu2d literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/janitor.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/janitor.png new file mode 100644 index 0000000000000000000000000000000000000000..62a518756d63e21be32c21f10a1bb785ec7c1550 GIT binary patch literal 603 zcmV-h0;K(kP)Px%6-h)vR9J=Wl)q~eVHn3hEfNR{2{tLD7SB^mDwwnxl;$F75wQvxTwJm`ISA^Y zlOp~DZ3*g>NpNw{A>dM^26EOwIY^dv2r)yh)<8|bcn~iU6vsQ@t0fyw_rB;pb;9^W(XbC>5W2M!`%DzsgM40NEgMB|Wkrip5C8tmf_TPx%DM>^@R9J=WltE}xaU91#iz0(DMtcmD(p|PhGJ|z6q1R<4ltGArhjl0>6d0_F zL9oWk(=J=hEb!1_U_u=_j1DD2O11(ak;aZ~f%D}q4os`V>#!-=)29Ywm-o56|L_0* zzW?w0zVFW;G&D3c_L!dCZeTK*<=S{{ZB@~4ej+jOsZ@$!Fj!wpzx}GvJI@W($Kh}o;najISdCN?QEuIy zjIVl6&1uBUsl|&TmHUMlk8wP`+rHOn#-|zv|bPPZ>fqnl@j0O4-7f0*0`M9lR zLp7@Do%Dc(xp_pp*i@%55TD&5c{M{d9iv<-l6fEhF9ZJ3!%X~q(^jYW1!I9eep!dn zEqXapi}A;ou9~~u?vAwz1M%4{AValxJHpx_?39_M6{cMlp2uDSaM9)5SvdhXR&I|z{NfMGIsc!hdZvo)ujq3o6kNZe2ClT#pcUST1vwL;1Bps5o*(@H9 z2T;F?v)OFJe7RKQUGf9vQc>0Tt|9af$U^^s++L}np`o!y`~hb$y0Px%E=fc|R9J=Wls#w@VI0PP73(6DLPPPAAvCmF#2C@RTGFeOLcxzE#0*jwF`zXC zgAgaDPKH1fV+=%G93s^E)|J>2lBVe*B;N zKhHh)-U}KuXwb0F=-X=nqj6lR&)3$zsq_x^(j*WFDF3eCxGE~nOyF?S+U0UJ#D)cd zpM^rp-fa_D7W}-QG?0?98&%Ks9Dg!H5 zPALHEIf!oih~{8)w5DjWSkUYB?L3pu=Xbn727pEPoq7VQXAbP#nDq=GzBtoZkDsu# z73_p$A%j&g1GNOi39%(lWgzC=Abu@PvXG%tE|Gp4`!55>@BZf3)lXe{s!uTM8DP^q z#`=sItFS_{kReNCSK>CCt!J*vK+L-Vq#F^|4`HwHPo;1tCW*X!4Zy`qbAKx*fjK4k zAJQs*+XSHbB=?GX0#8HFTMBkHaN*UL zK7fUhjAH0t=0_<7rn3OZvWzUt9UCqsCIPs0^Ev?Y^G@RLKQ zk|gYQJJ9(mmL#cZzfvx-8h=NnTPx%6G=otR9J=Wluc_BQ51%smQk8OLTZO%M%pnjNegNeDxtcVZU(nq2_mj_B`QLH zgo|nsU3I5_AOu|0xafdXL(@POevknPNNR16d{ZT5T$CA-P9`6V(#<@p`|;j;&VBFU zfQN^N$0;**(g9?7-n7QW;x|V;VVFxaO*1>^_x}8HXx^SX-!;$Wa(1xkaG2k_yFKrA zO<-;Ax|wd@LK8Mv-`gV|kK21o!JtF`-siK&hGB3s^uW}F4OR~tJbdz)CG`e!c-nE! z^$WHxT{JUok%Um@{&)aIQP6eWO>U#ADy341yOl!{LYX^_3EP38zTn;MYe#l}sjzy` z04A^=EJ+f6zuz@@tJSi{3874;Edu==7(c$CtpriuEZFn;%UAon*B#3}fS1o-pf0LC zbq)v8&uT!HLR+L+{XzC)n(&M?assKD5Gg`X_}N$INI+W&^6yHN$%PN3E(FN5Mb`N{ zQgBHcK4C{uST#_{3$}$;5gLXuum+x8Ok9Xs!6s_w6y*1|SxTne}>|NF)OEZ^iX`y=%Q$-DgwZqFLP^nEZ(# nhNet0G-VD}dU$wvoD%;5wxGGV?Hv`G00000NkvXXu0mjfTdNXu literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/service.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/service.png new file mode 100644 index 0000000000000000000000000000000000000000..6cc757bd9118a63b1db58e1bb8acef6b989e4bc9 GIT binary patch literal 563 zcmV-30?hr1P)Px$?MXyIR9J=Wl|g9JP#ng8inIg;2TKdJqag?D1Y4KGWXm89FDmTdxmQmf1ofaN zMfBt@n-q8JN${!%k0M%FoWcmKmw70og=VsXu3`x-XgsV5%hE2%VS35$l$ZDY-}iqX zFE0=i6BF~FId;?rk~C>M&yB{W&+kN-MHEG`gH>J(!*I>gnG8GI+e39D6S(o@iv3}= z&-`2hqf$qf<$lvvjf#nZ>CQ=#L^7EiY2NGgJR8{9ssV8O?j0uqU(NA@ zFG>q4kMG@d$CDQ>jx;=sjjh@sMJO*Vqs?eTb-n~%z10DoGqYdmcG|43zT@QSQ;{2J zy#7IhYM{>7KxsiGpFhvqx0e*ldodc7dL(mE6!(p+XbtR7pePsILl_pzvJeD;DPzVuVdEKWUnZUJD!gG?t;v}%U`*Wz_ zK>{zLUz;tm{2`mo(rh*Z7tG~yo^h-7o$6W@ZAOcB6~B4(-WC6zn6NF&B9%%7HsHkX z(CxJOSp7t|(+;dX62!4_TO1pY{4a@#iHZ4V`~_Zbt3^P11jYaW002ovPDHLkV1ff~ B1hxPG literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/syndicate.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/syndicate.png new file mode 100644 index 0000000000000000000000000000000000000000..ef0423db62eea2e2810e93696a960e3cabbcea12 GIT binary patch literal 566 zcmV-60?GY}P)Px$@JU2LR9J=Wl`(JAKp2Lf1WVAPaU2>|q(TWD3XxEwqAo=hW~mgZ3`__X7Isiz z=mINCB}5RSe*k^}ESXq{Q~{Mb5EdqAvP4P|)J}UXNr_pAE7x&sBNY}u(|x|r_kQ=@ z={tysh=};l#Ex1Zm&;rIalQW8rKbd0Rm&*v>EB|->hHyRX16TIH~ z;VXVkOYv1oD(MtL2zGZ590+s-mFqJW!2M5qyt;b^fX5FT08A~N|FfP2YcygtRthrM0opZ%IT%M8rSiH?mZ&5orffwEzGB07*qoM6N<$ Ef(aG(hyVZp literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest+o.png new file mode 100644 index 0000000000000000000000000000000000000000..80638a335dd6f4ad0fe313a42610759dd442f222 GIT binary patch literal 445 zcmV;u0Yd(XP)0F@;#7YANU~I-d^RyLUSIhW#-N0YktLFa!(% zL*V}ra2$s}EEeo#vzhe$&+9dR+wE9YRTCxwDFR4t+cw)D4kF>*Y$p8g$D{CJ4>2+% zqzIsLB#NSl#c?cbLJ$Nj%Q6x3MPT%qQUo9|swB9sE7r0sFJfe%qyVZX6#%N9VzeuO zAr*WA<$c0rfP!jZ00000NkvXXu0mjfYT&oh literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_chest.png new file mode 100644 index 0000000000000000000000000000000000000000..6f0dc0b65729c7ddb09d5be391aa12d13c7e0db1 GIT binary patch literal 447 zcmV;w0YLtVP)w3*Ub~{#8)r3h%3IM@v+h+U2K?Ho5&BXZU@hHYHhcz+~QUJ&t zfubm4aU6?2DF_0VWtmv>12B3|DF9fEEJ?2Ginc7vi#0M*asb(r3lL>bYqUFn;1Jrn zt}h_8I)KZCOGcRw)wZqPD(H(`AP49OWF-xN41M3{==bjc&1naKU^RkM6(D@fKo&7rfI~t$^)bffb+*&(6=1@{dq3WbA45?Tt4$K44GwFY*>n@imvO} z>GZ|7+xP4L3y_`Xd7_U%(BphAS~x;utJMpePVYxMF&f~!Auo#J*ZCw#hHFPj>IjA# p(|aI$?#cjv0o@$n8ZrO|;2VPz!ekF#iqil9002ovPDHLkV1oD0zY72W literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head+o.png new file mode 100644 index 0000000000000000000000000000000000000000..b4da454795c392d92216b99609a265ca78128681 GIT binary patch literal 321 zcmV-H0lxl;P)u%)ydd+05suFIu5^Q_jKqv_E3GDYf&~?4_U7qJKnaoN1 zkpP-D1JkgY_3?a`b!k0&reVM(O@{;2p?CuLw{5*|r_&n%1A-us^Dqo$^*m43D2k}t z(ExEAD~{s`3`PV#Ns_#U<2Qo=J_5v-K52Bmh`I*$2Iy-rfB_6(00S7nfYAXDw60UJ Tcc*kz00000NkvXXu0mjf`P_l@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_head.png new file mode 100644 index 0000000000000000000000000000000000000000..7f15e566e146c205e20d94084005d7d2d4870baa GIT binary patch literal 321 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DEQga z#WAE}&f6=7+)WN5>>tvvOzT+ugIDKH)RK##GP7s@wdt$n+HTk2o&A-uMPNyR(ZQ#W zVl_m~Y%Z!)uzz|tujv1L^E)DLay$tP%n}D0{+ts(L3+^~SedjR68rgm1hrU+}u>^=_NU+g11dmYXxMu{bI`Pn0@bI$9>E)VEtP>tS{&BSM%?7`{jis>x%AyP2 z-~Vt=`FW4Pq5q7Nc>FaFTB;uxN;dh)pJ(sv7_p2gX1nf+302Ht+VwmuL>XBGF31!x Y*=*3_TFP^B4bY7Yp00i_>zopr03IDvCjbBd literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_arm.png new file mode 100644 index 0000000000000000000000000000000000000000..b747a4abf22e1f8ea7fc5afc84341fd40e14d397 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5C^*&A z#WAE}&fCj|TrCCytq-?9xF+Ya;kH-)1I40-Ia=itO>T3}Q#31ksmvUAVCDjj_v?L3 z&VZY#YU25$A`R)uxH4bhs~c>HAo3MFfg(R6fm6b X(c=1a>fKGC0~tJB{an^LB{Ts5;?Pp& literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_l_leg+o.png new file mode 100644 index 0000000000000000000000000000000000000000..7a3e283fbc09fa59990a015dba60b6b6eb1d355b GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5D7enk z#WAE}&f5zMxegnMuwJmfaa_bJWacaNkhVG>*Q+l7HLH&FxQcLHG`_faQNU%vi>nTH zSKO4?vs$S1d~Nz43nqaE1{MVdMh*uCCV>O>jHfT6)bnv^rwcy;rYX9k%8w8p> yELlA?4@xkv3=p3X<^m|`(&tUi1zwq^l@V=>89l0;(g}}wq@=P9mYe(!4|n)? zTJ?I+Q=nc5c+-$LL+}0eWn2nV&wrkEEQjSc+qusR3S%}2zG)Enw&?qshJEinvVEO6 zjE?=`4)BUyX>-gY=+v9~HPRkWmnO1CWY528`Tf1<6R|qB3XqEySUq5?5msa7VOmlH Ovcc2U&t;ucLK6VdFHjZ$ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_arm.png new file mode 100644 index 0000000000000000000000000000000000000000..034ff08a3334b70efc9b73c12179dfc607d7525b GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5C^*T} z#WAE}&fCiyIU5WFST6`~;a+l%$H{8;3pT!qD>wPDc_>d~H1Fs&*0^rr;3x9%|EpO~ z?D^I$?PmsRW?(q*pRv|{j-~!tbp}28<1>$EwNx-4-#B?|u&EQzjcuV{;#e;1Qhsl% zxm2U&gWdtl@}f%3)*i#H_fMD|czUUW!SGnMpXT0b)~&7mjPpQ_2LkbKJvHX|gmP67 N$J5o%Wt~$(69B~?O0)m~ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/borg_r_leg+o.png new file mode 100644 index 0000000000000000000000000000000000000000..db271ff6033dad4482808d2ad907518dbf549c9b GIT binary patch literal 254 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5D7eAX z#WAE}&f6=7T+Id&tPeQWJu+=+@O~*Yr?_#>@teg@7IMtvbMN!PI~tFiOf%=GS}ETYiV;IR6Fg$y1m4W z=g^<5ODg^IC#a|NF|a5wFmgPoYgkovJonx2cZHS=!Pn(Ay2Se?b}KMGt87Y~ajB=b zK%kxRb>d5@a~Vy4I8JZ!^zC`P$?TZR^E-(woE!2PFK>(Aa4l%tBJ&20r6E_MnAO+R zAE^F+{m9dXS|^T5hC5<@nF+2>mPx$AW1|)R9J=Wl(A{WFc5~nge)OayA-%I8bEb;CR1n-Pv8MOfEVxp#>FETT)Hy^ z4Pd!;iXanG`CbTl0eziL(&^XT&p{%QNF)-8#DAu$s{OSr_n+i=kX|sx?9MrsWdY#r zdIjL+a-ptk`o0(C6dg9kocgUq#FON4=Ntg@Jkxa@d7c9>jw9AuvMh_o_p>4%&k6tt zArL}f+cws9WgJHU)^%muwqswypSM6%ftc7iM^O|>Jr4-)#Ls*QpZTow3 z;Jb*g-17L%0^qaP&q@LC?!DN25s?Y+4r9zNb^YD(0bx3V$?t85%m4rY07*qoM6N<$ Ef~5|b1^@s6 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_chest.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_chest.png new file mode 100644 index 0000000000000000000000000000000000000000..1bac77b8b348928e8515c099917b0c07baa4e9ef GIT binary patch literal 358 zcmV-s0h#`ZP)Px$AW1|)R9J=Wl(A{WFc5~nge)OayA-%I8bEb;CR1n-Pv8MOfEVxp#>FETT)Hy^ z4Pd!;iXanG`CbTl0eziL(&^XT&p{%QNc?B2s@h-6a{oz=2k8Z4%L_A3zcg_JY&of=uk>@!8<2YiiCCjpSd_ODV@vH!V5CS0t zwryiwSH^J!U|mCaXva@vFdN-4C~G))75)|zRW0BD*9YwfWPx#_en%SR9J=Wl(A|;F%*Ws+r_n8xpWQ=MT%R^lqs7+p2WFyaVht4g!%%R!ri1# z;B*OYu1@K2tJose1Uh-X>4cM$^YfoWKoEpmrz$OM9O?A@x-5IEsHkzI{qq823;^DH z2A_)}5!nT&&C^FOpqlw&5qG%)fMatr1T&&?=f8@Iud4CDi_pn@F??llK06dLGXX9M51jHS*8?e@* zl%k7~E=H76SZirF{hb`L7N&8e!@L1P2o-?V`Z)LLLn!`~1VIo4;WqgKmP)Px#_(?=TR9J=Wl(A|;F%*Ws+r_n8xpWQ=MT%R^lqs7+p2WFyaVht4g!%%R!ri1# z;B*OYu1@K2tJose1d7Y~rV~z1&d+}i0YMN1;l@;@g^eSfo?n+`Zxt0aj+uPeZ1OgCUehZ z?pbX$0B-!Zz~5Z0_Ru`;;H}j>*7c8k7bEX4f%+bn>+78;+5v#4(dcZPOO}ARgLVVf zT9i_BG1A3|QVMG=?WVtzL)OAHj&zteKnS4%&{`koK79zqpOPR5f^ZYQ0FRneTP8y^ QLI3~&07*qoM6N<$g3~C9CIA2c literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm+o.png new file mode 100644 index 0000000000000000000000000000000000000000..f96f68292af497ba7144b68f21d7b067505186d7 GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}nVv3=ArY-_ zFBFpzmBvr*siRT3H|C8LOAe+A^9B|6XR6d3m~s3lOAiN=>;@yLK!sb3`*&J>&zOjc=OaL{B;_^os#m%&*1dCj-ArVLf@uSDIy$5!`&NpRxJGRJiJ Ufzopr0IAhLRsaA1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_arm.png new file mode 100644 index 0000000000000000000000000000000000000000..faba093b48c93a1a40b47fa5df81632068518902 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}$(}BbArY-_ zFD?{3pup4maDVySM$1K!oxG9^z3Ds-%~`ckffv88JhErc?uL$z6Zd8FnUi{6&+^uM zx99UoM`Ps{hjTNh2r;ZRZFs8tP%k0vO4R$>9>xQ6Cg0m_{_eMU2T%_i%SQQv5LK<8 QKAAeZm(Jf;`q|d-9at9NJf+dGq5W6ifexk_4`;_m8D^;GCH+`= z|G+zU%?-n2r@r#^n8!Ig yHOyj2*E{gk;8j%ramMWH>rzt^7Q_WSW@69{v;4J|S3m^l2L?}9KbLh*2~7Z8n`4Rq literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_l_leg.png new file mode 100644 index 0000000000000000000000000000000000000000..2a84222924ba4c32d6f5088c0d92f0cc53c8011b GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}+dN$yLn2y} z6C_v{Cy4Yk1sYspRNg6dn_2Ro@@MVqlhehxcTNkhaJXR}Z)C8>-oH;%;DF1@r&E|F zHSx}VdfwjQh4A;2-E5O&8+>D&r!+b?wA;8(5MebqY`%X>(gnXeM?M7aZ%q8`t=%TQ zUQ)uYCNW_{z>&8et1K9vad>)qwiQ1=XZYsU*6xQlHYWE>eJv@Gzo)H*VP(Ubn1<7d wPhKdWX3W06E;S`VL3f4wK@G4Yd7>B?ocJt%ojx^B4d?>~Pgg&ebxsLQ05ZQ|#Q*>R literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm+o.png new file mode 100644 index 0000000000000000000000000000000000000000..65d47e240e8054cf46b0eb56a495be1eaf837e94 GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}8J;eVArY-_ zFDmjKP~d5OSbuU|lY8)aRSWTOr($sjwC`rf15f*N)zN8EXxXE%^TL%bA`z##^tQzxh^QK=9@}CZ+Gv V`xSlF{sHY|@O1TaS?83{1OPwgLdXCB literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_arm.png new file mode 100644 index 0000000000000000000000000000000000000000..500643408a29267072606d989effbf2a17a7c0c5 GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}sh%#5ArY-_ zFDmjKP~d5OSbuU|lY8)aRSWTOr($sjEs@Z=V94Z?->n@gS?{IbLB5YCHFYK|NC~Pq-A>6+TDfs_dB?_xX77(mEONP SD`Pj%LIzJ)KbLh*2~7Y$8a|l- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/engineer_r_leg+o.png new file mode 100644 index 0000000000000000000000000000000000000000..a5c6701ec64ae862431c530fa4e26beff2100701 GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}`#fD7Ln2z= zPFCbQWFXRJ&%u6RqL&DhV?m1gfo~jlnk?_^@NquGqpt8na@hrLwnj(Mi#<9yu>!dU z?~T{{Z}~s}`}al#jwUdwPZM~2(IIxcyf3ts;Db3dzVpvcqvFOTr0P1|&D`RpCLt2Qz*yb*D0PLDp;@nV}GLxGl4?77+N zYMiFz&RjZ&_wdax&THpHyiY0YP+l=DKEGg($MV@7N)@s5#5up8?E0AG9jDUp$FNJ* zV$Mq4Tg-~($vi73yK4yv{Jwhc;xBQAiuZ-*YGdat(&+jmEZ5ktSF&APx$bxA})R9J=Wls{|2P!z?Fwqslb2a!HVpn*cS4t7zb(phN1vC!|J)ZInU+3p?t z$1h+*MLU>JP)H!rlp-Rym_=w!m-?EQyeRF`q3<{4op-tCaBmVoAP@)y0)apv_+w(* z3Nl3Yg4&y7$NYD}Gq3hn0Ww7PD%Dy@Z#5eLfX@9L9X&onrKpOy2ROtGUsr83wH5(pp5}^Qo z^IX0__1lLI9LoSvRH~7K3pg(~-{-Foli3{Q^E0Xs`n|{y%nG8+gQImU153GtqpK+{ zpX*r4B{-JB)bOUqTL(>(A+lF2p2Bq(kfa3D%yk!-%;wZ2n?0uEUgOt@q9UD2a{EY9 z0-DyPFDA1B(;g$8NeaaO1bCU@7UC5+rQy|@d-E%zOf8Pj+6iZ002ovPDHLkV1nN6#ykK3 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_chest.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_chest.png new file mode 100644 index 0000000000000000000000000000000000000000..16b305d2bf7a8fac1d4ea7ac6a4afd55f97ebe0a GIT binary patch literal 443 zcmV;s0Yv_ZP)Px$bxA})R9J=Wls{|2P!z?Fwqslb2a!HVpn*cS4t7zb(phN1vC!|J)ZInU+3p?t z$1h+*MLU>JP)H!rlp-Rym_=w!m-?EQyeRF^rSCW8op-tCaBmVoAP@)y|DD*jf(((p zp!Vk2G5=lg%&YxXfDDnnO0^c!Tg?UlpmTqRN9_si>ox!&s&-R|_)pCT{a$2X-d&|y z<4$JH?k018!(6_ALB9uGAA%?<9LvCJTF1#D&;~#h6}moTYW*Qv&4$ODL@0pYJeMy} z{q~^)$1*?^m1^YR0?y0L_xUTtWHv|n{EX^@elIcvvw|q|;AkDoz)~*Z=xU0~=Q@^h z365njHN5HZ)H(*Z4J}s7R-h+&+?&fTnfn zi^;6Ow8u!NlF+nnsJZ{yv_^>Uu-h}OQ79O%k7}M{P)Px$@<~KNR9J=Wl)-D$P#niU&f-BBh@f*x3`rLp9(J4?NH*3Xcm1N#Qg7^i28#mWkj$2o%-WQGZRtG=SQ+sKJD}V%K`Pz9sp9m%K6iSB+m>3h$taK z2MC()H7shH&&}Kn0Bh@c6jugdek2;T|Wrey3J+1b?zm{C%^g-S=E`IzD9AY2tds(AM4Q< zBEX?o+Z&V|pOWKaZEtk#wZr}u6wMHsxtuU3CWWl(Vrh9r&}u~hg4Xc}KrAh<2wByI zIWZ|RbGf#rCq3&?G()7OQaJWL71!g=;#~mja+zn(3yAOEI@)x4mLbCc;KBWSBsCR% zXqe(wk(ygR(&yL!ZIg6*w)4GSukmJgmxYBz*4Fb>U7IVTqezm{(G&_#TLyh6|2#mD zpEUz8HZ~4`WxYOh4d8oD}M{P)Px$@<~KNR9J=Wl)-D$P#niU&f-BBh@f*x3`rLp9(J4?NH*3Xcm1N#Qg7^i28#mWkj$2o%-WQGZRtG=SQ+sKJD}V%K`Pz9sp9m%K6iSB+m>3h$taK z2MC()H7shH&&}Kn0Bh@c6jugdek2;T|Wrey3J+1b?zm{C%^g-S=E`IzD9AY2tds(AM4Q< zBEX?o+Z&V|pOWKaZEtk#wZr}u6wMHsxtuU3CWWl(Vrh9r&}u~hg4Xc}KrAh<2wByI zIWZ|RbGf#rCq3&?G()7OQaJWL71!g=;#~mja+zn(3yAOEI@)x4mLbCc;KBWSBsCR% zXqe(wk(ygR(&yL!ZIg6*w)4GSukmJgmxYBz*4Fb>U7IVTqezm{(G&_#TLyh6|2#mD zpEUz8HZ~4`WxYOh4d8oDj#Bm?(1bGQ2*_o6K#0 zl=1VsPmjO#td`1|^jln^2^wJ^H=D%%9sgIde%EzoJ~r)NMJ~gJNG~C2hO*`A$EMkB z(dUx2m(Sa=>u8$hx;#dYq{F+VPktBI`mlKS`Cn@~Iy(LwVR`sOZn3RRl_k(&44$rj JF6*2UngB~nQndg8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/janitor_r_leg+o.png new file mode 100644 index 0000000000000000000000000000000000000000..9cf4b8826721020232e744492545abc15e0bbfff GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}eV#6kArY-_ zFKpywaujKK_)-pPHu7VYUDfQxipS<2wTYk4xWcQQmH$9d_2I}f z_qW^sZ`eHJ_0hI%-XTjDzZGH-vpM^owkyalKn@Px$G)Y83R9J=Wl(9|%K@f&NjorP3m5GU+1@IQf9`G2%j^>IhXs9$;kWf(*Ae1IH z*8znT#E8VVII{Ixn7xScNOn-M*e|(UvUf8(^XH!dkH_Qjc#h18Yk*RrDf%{zDlU#e9~Z{|RZD8l^(BTy644snDcpS~wm1 z5RGA+bn?#O!;I~uB^pDK2khl`3O*O}s|DEZzavgMdzVxZvPE=pPPco<Px$G)Y83R9J=Wl)q{MK@f+(gmm{3sRDs?Db%+(_teLbbT(I9VPlnG3c<=+R4fB& zu2YLxNQjZV#mUy2BJ2?*C+wI%MfQW^VDDyj=ErXaJRZ+~=fpKYsn8UCn@pz8`OaJe z04}etiE0fN^BJXbh4E;}`uRzcic{ABtu@;$1E5r{0I?VG3oX9X-#t+Df#;;ewyn%DJ9e)(cP%g=v>G);|Cp;@jr`J+^5(ljlcjzfsX zFitvo=kQ_1cG41!p~wUFaytc|i}}$4Z1-OgC!K>!stDO4x;UrXz2os=3P7`YBS`}| zdSJi9IO)(%T08$peBg{yq3QJpX1Us!UVmUD(p$uf2#zz~w-jJK@F!T=kCETw@pu5< Y0WbP_-hOMxlK=n!07*qoM6N<$f(=8fUH||9 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head+o.png new file mode 100644 index 0000000000000000000000000000000000000000..42536f8ea40244099a5aaab90c1cc01b6447422f GIT binary patch literal 546 zcmV+-0^R+IP)Px$+(|@1R9J=Wm9a}3Q5?rV7vzR?2&JS5Ax#!RO{Y*OEpal(ImI&_^WHi(2KK@>!%J1E@nR1VGSaMn_)_TEA3a-Z$)ckg|_@B4oD9y~Id z>VR_DlK0iNw;Rgyjj4)P-mj2IB>t$mt_vVlO~h2hjXCQ#!5h(Aj5+JUgziEqfwQwy z04ArV8T7va(5Tk{u-+}+*8iCSfD7-6X6p;*-Uu{}T%klZ zpP7Db@S*KMG7VY`1Y4PKwOWp_~M$kFMmbe>Rl7p!-c7A;L>%-q{eCWj{JR>l1yrxOVgf~gVF!EA#vXQ%;ycHKeX{u?OC=+ekq0qNLvCzt$?m~W-gnfN%a-gO z4CTh=mNZQx@Npa{f!xeIje3pUP8%V_o!12V_2aF^ezb^rhX07*qoM6N<$f~Ft_X#fBK literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_head.png new file mode 100644 index 0000000000000000000000000000000000000000..f34c6ff47df5bc99638aa04c07181ec710ba6ae1 GIT binary patch literal 544 zcmV+*0^j|KP)Px$+DSw~R9J=Wm9a}3Q5?rV7s?Ik5Q?M-Ax#!RO{Y*OEpal(ImK%n^nv>3<{IH#<)IVe8E!bX{k?dE_UdDgS{X4cxLiJAvnUtjySx})9QWm_gzXdsd27cCzOG} zwwFo<*?f`X!2zjfPqBv`>_HoQ*g=YOjt2*1^F@|QMkphXVw#5B*xZuc!=ZfWs4bT* z**zS}jm<4-nnvK`I8Xw)nRy!Z8hf2KLWq065kj!nY163J$j!_L)ep|{TJ?`BGfhJ- iuT_;Uqlk!zh=}hNHm`7^i|uIu0000bP0l+XkK!`wC- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_arm.png new file mode 100644 index 0000000000000000000000000000000000000000..e166abecbdf31e55f6cd8743f63f9ca419759b1c GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}L7py-ArY-_ zFKy&Kpupo2D7;blB!kD4Y3!~!FHBA{NjQFvfBNj4o(>ZOgWAlMvsb!Tuk+YpHuZ&| zrI6nD&;9p)dqng95Ua|yir#pv%btPZ!3yyw-U5L|TP!t!rZafD`njxgN@xNAE^RW$ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg+o.png new file mode 100644 index 0000000000000000000000000000000000000000..6455ebdcb277f555481f0c0e127a79e41782ddaa GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}X`U{QArY-_ zFBx(*81S%OSU)|8Kj4K#k@0Rk`Hvz_0t>c(fi zrv-28B=>S_db_OK->@p;R8wZ$FY%1S-*ex_GaQhvn``>!pWdpZx9UqY%+uo}J{ZX_ R&;Z)V;OXk;vd$@?2>{nCKxzO0 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_l_leg.png new file mode 100644 index 0000000000000000000000000000000000000000..89c7a5fb4c39453ed8548addb46f257d92cc4e59 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}X`U{QArY-_ zFBx(*81S%OSU)|8Kj4MLKSS4U@j_u6zqwP^nFR>`o4rZp=L7~NB_;XSdzd{=m(RWQ ztt#$4F9U;OROxzchD=MB!v=nDnWvn-o|-4maA5Nvn{TUr@18jAw7Q6(;LoMpo0KI! UoG&Wd3bc{I)78&qol`;+0Iruj{{R30 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm+o.png new file mode 100644 index 0000000000000000000000000000000000000000..436750bf8eb2896672cb7bcfda9d52e0a8a13004 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}!JaOTArY;~ z2@s;%kV^%S*I0;A?1{ t)>hP3`r6DO=yuE^2F*fyh`wf^MO|_H8}4?0ngKMP!PC{xWt~$(697>AF8u%i literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_arm.png new file mode 100644 index 0000000000000000000000000000000000000000..f36ec253cbc3dcc7a6335e63ce0ccb59ece03d81 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}fu1goArY-_ zFK*;*FyL_sY+u^II6*^K-+6XZ<)R#kRXPu}JwC?2-#s}8Xo8ENol;H8d-JV-cExs{ x;GgmIci}(dgsRucYrigfwnSq;J41q`yz)%`4Qp*J>Vbwcc)I$ztaD0e0suBQGvfdN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg+o.png new file mode 100644 index 0000000000000000000000000000000000000000..2a7843f7efef4161bd8d42de662c47b6c458770b GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}d7dtgArY-_ zFBx(*81S%Oi2vyKijn1b&{5}UW?z)@nvSmwUbuMbgGcqj(^~w68ahDW-?OD#dfzlRkV^+xL9V*{{x* agw5S#B@gy;pPUY~nZeW5&t;ucLK6UGqeTh; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/medical_r_leg.png new file mode 100644 index 0000000000000000000000000000000000000000..65bf367560297ffda54d22e718acd43b21657908 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}d7dtgArY-_ zFBx(*81S%Oi2vyKijn2G&{1bE=_*0Px$7D+@wR9J=WmNANjFc60SUA7Ys5FU5{AvwU57O(RpIbQmd0b6qhNtec0imzA{ z1s`l8E4z@d{Kc7%Nk#-RnM@{=`A-nhLPSgI^E19;CEB(HfYT;K1Y=B8z0X*Iodl$m zkWy~VQ;43xG)<@ZJkR$CXsu84uIrKnl8#^beH-y$MPwD5bWoNZ}*)1tMCSra@5@hkTkQXsuz4iK>}=2lnguZZ&^G z^aKEabH0>idC10bgta!hUeW~CMvO5$pU*>fi@*x!9M;+^C9VlrYr#3cBoO^6UU@Kv u!8thR@L=qIfnnEpa4uQk5-C3*e~uSJ^k+vEhVHHa0000Px$7D+@wR9J=WmNANjFc60SUA7Ys5FU5{AvwU57O(RpIbQmd0b6qhNtec0imzA{ z1s`l8E4z@d{Kc7%Nk#-RnaqEJh!!GRQlFpk6)VxUEdZQ0AtD%KqUwFd3hX2xrG%7n zYo0>%1g2>^&F6W(M?h$pP*2q6Ffgb)xy+#~RgzVGi5SZ})b9^U({ zc~w=vFHqMtfbUIENPx#{7FPXR9J=Wl)q{MK@f+(h<4e+CP&idEe;EP1|c9J50Ey6jcwBS8mWB-8@Wdi z|8Q{4-Lgj@#X(Rn!4$@zxhRZq*vwZg`tp)Ms}tp{N#>F3|DwZue4VgMjX^MhIxucYZ@9oW+D?iN`4Jw0{g2E-iz81#!{@cg(qC6%lL z!|)aeX*Dky^f#4nXCR8dh~~?m7*~#gEX#n+93*MZ%~g#k&Ut%Z6=F&;05JJzz<30} zbi6ET%*r!hjKNw9*9|<++tZwLSZguHhzy7q5rFUe|1P5-2u{kUD2k#eigH%I0sMDQ Uf!wAF6951J07*qoM6N<$g5CX!!2kdN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_head.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_head.png new file mode 100644 index 0000000000000000000000000000000000000000..b92684b32179a0c7c04e0b31fa2389bf354c2f9d GIT binary patch literal 322 zcmV-I0lof-P)Px#{7FPXR9J=Wl)q{MK@f+(h<4e+CP&idEe;EP1|c9J50Ey6jcwBS8mWB-8@Wdi z|8Q{4-Lgj@#X(Rn!4$@zxhRZq*vwZg`ykyYdRKA^oDE=avFMncOIR>&U12%Jzq&YWNHKI7@?R`~n$y98%Jmcud;i^DOm?8(K3Da7~p~Z}_tAlmEWS zs?Cor`X2vbwsn3d`RV!-@glq8xvELAljSXno?p8aed7w>oRsimx3|QbJy2ARy)Ni< z;7ov<2RCbnN8M>>j>~K31Vx>dV-TtA*|=t9mUg-Afirf;F8_SPxISI){<$*&p;31B n4quG~H3io`?R#ute^5B_u(h@P*K~EDqZvG1{an^LB{Ts5Ic;8$ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_arm.png new file mode 100644 index 0000000000000000000000000000000000000000..914335f923aab79dfebb10d8a4615121e619f0ae GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}t2|vCLn2z= zPTt6S$UwkVzC%&t?vjrj%M#kB~ZY%xg zuP!Zfyl{@{6D;)u^%-hmTQ0C|5deqZ(5CRO;dmS)q>sntNPL}c8EyY zrc}IMxaif5+-XVr4{I7#Jb%6Y_eQ3oKF>jOqHP?*fhcV;qn!@7w@D<=Va}X&E}lW* zeDTEvCs`j^$la~qBeh`OdWUJLjuLI>_A@ASDNQVB&^I&I4)n~vJ_qOx22WQ%mvv4F FO#o)DX`%oC literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_l_leg.png new file mode 100644 index 0000000000000000000000000000000000000000..371a20e42cf620f001b45d597acd1664aa4490b3 GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}J3U<-Ln2z= zPPXMb6<=Pd4`qB+bGWHBT1i{ z&+f^ZnfScgdcy*Zu0?-lCY*SBIq~Jb=byK}GR-zqsn{R=BQ<=A?)ODvogHichW{2x zJ)LGMp6|4!(Zu&|RrQ-Uw)e#!s7k7fGo1JwBV#nn;j)Q}=cSvcH|10@GGynjUvQD- zk;T8r{kB;PX89eE5N=7l@spcjk%myKcGn^et^@1}iQ0h*uZrdZJ;C7V>gTe~DWM4f D7m8*r literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_arm+o.png new file mode 100644 index 0000000000000000000000000000000000000000..a12c427c4d96b872799693ee1bbbe6f35fb784bf GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}%RF5iLn2z= zPCm%VWGLV|KS3}q$H9Y{If=h|TXV<;(TIa$6WHD)>SrqW<}mI&-rd4#*rFQg^6YL~ z&5Zdo?H6ctEzqsqIaQ&-`8#l#H{-tN}n`kYrh z!`^5MOoZ`aFu-0^IsWh|Yb6=hl4-3!raPTJ}H-{U{027Tjxc>74z4z0!Y2dlVW hz2-e^&@=x5>n0EN_*}I=Q9$Q1c)I$ztaD0e0ss?;T<`z@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg+o.png new file mode 100644 index 0000000000000000000000000000000000000000..0e196fdcd11b8f7018a1303dcfa81336a5e38fee GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ydu{YLn2y} z6C_v{Cy4Yk1sZU<9n=7GcnlJjY-_x_@zMXpH`k8pbIv;NlzV#dVU~%l|AdP;t~xES z3j6c_rSyFXAJZKhPCYJS33Yz3zk#)RnmbdN<#loHfA8<@l|C#OQp&*0%zXIT+Gx)p z|4{atzrUs)Jb99H^_^#DXJ@`=U}Ixz6IS<|uw-5q-;EN-o`ZK{xR?KVeSLlAYX(6< o!M5Gy?;lOEFMA_#Erx-?XOWI`^F5J|K!-DUy85}Sb4q9e04sT5aR2}S literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/mining_r_leg.png new file mode 100644 index 0000000000000000000000000000000000000000..157055c429d829a4ccc7c3b4310820b1ef1dfb2b GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ydu{YLn2y} z6C_v{Cy4Yk1sWV^m>AQ0b>pM|iEpkQ)jw(0-Wqdy@nM#Ut^X#!7MNrja=h!meeGlE z=9O&XVU-n#wga7aDE*E~wu}S6t5Ns%Y9agk8=v~A5`29u} zmX?WAZ||uzek%)-D|mE-6Daqr!B_l<#F?{O3r_8?`#WnJpMaoX;*RR?dVW8XkM~WC kRRg(+%k7|s#7qVTc`hC2OjZA1K!-DUy85}Sb4q9e0Qh`mxBvhE literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/robo_suit.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/robo_suit.png new file mode 100644 index 0000000000000000000000000000000000000000..699254111aaebd7294eda60d92ff3c9116fe58b5 GIT binary patch literal 301 zcmV+|0n+}7P)Px#=Sf6CR9J=WmE8@4Fbsr`4v=I7V1kSWOppPt z=6p$jL?V$${4>^Ce`#@Tl)tnIZPe!CH_1oz_teA|71#|}Ykfa-sH+A5P*)B5p%eQd zlN2Y4yhQxmwk<0st2ltC?9MqKu1gqu9Gq@F#Z$phfoYtxIPMoX9(YMgvnMZniaP{y zez;0=iMtJp2J*8JodW=Tk(U4-KEPtm(;(-Q{LZ;kN8q(lZ!f2D7Ug4)KpTZfeiV6$ zwWf9_IQL`8#k)aj!dU9#_df9b`rYGtPMppiYV2bPA+_RF00000NkvXXu0mjfP;`AL literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_chest+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_chest+o.png new file mode 100644 index 0000000000000000000000000000000000000000..175187a9134c629ad6a63fb652cbb2048c130a17 GIT binary patch literal 357 zcmV-r0h<1aP)Px$A4x<(R9J=Wlrd_fw^glotRbO9eAvni;@XwT5DB|b>5;SR|f zFh)Z|F?2K7n>rLHZo61=A&b6EPttq8-un@FJRXn70x6}DQkp!^%`l$lx#{<8b6(mM z7eb)52H+vNGpogt&0&8$BZL4jMNwRg%{v>f^@LK2^>)YRuxGv9p_Fn-16XvRM+iZh zro7zVuv#p6Q};Z+e-H$LlK`s@v~BxiJ#2p%hJW_QFnU&S@&SO>ny0I4p1V(mF`M~= zg9rLo3L$u%wa4NvuXhqKB#z@L*QX1ps_MJ~{-Xm{hJkM*Mo|Pn(=^QIa{%hPwl<)X zfL;VZk|dO63E)(GGFg^c|NXz2Px$AW1|)R9J=Wlrd_fw^glotRd;uRIvni;@XwT5DB|b>5;SR|f zFh)Z|F?2K7n>rLHZo61=AQbd%dXnDz_1=%b;cz(q6G@WjBuR9h=Xw~=^IZ3Pra3Q7 ziVGo7N&)Z?-|6Li!Fs`shqyY>%&?AH( zO;cWOZ&=P3yvchW-#_p?&q{z%2ims%u^zTR2!cQRV;DUvIQjrUDaF&(HP78A!Ic?*mZRwXp%M z1oR>R;y9)(O8~3llgYBo`0xMCBoB05H?_XQ;c%RvFAb|-O_g%f(*OVf07*qoM6N<$ Eg6l?`3;+NC literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head+o.png new file mode 100644 index 0000000000000000000000000000000000000000..e091e6b7581bb29c02444f616b6ce28be70eef07 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Q#@T9Ln2z= zUOvd_tjNRmU`q+}`#iw{4)zl~d{0#P6e~_J+?#WN_svSD%GPZQZ8ZMKvjldiEpqvg z?~+ox`QN#tla8EeKOB|4Y_E??%F0)K+27U9>^UaUUQwz&e@E1$thG<})=RC;o3=!N zX{DCs^KTVdjQxFci?38Kw_LuH;qvLTmg|E~%{wlkxwPb-KF~c33_q4y-wyO>QB`Uv P25~)I{an^LB{Ts5xByj7 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_head.png new file mode 100644 index 0000000000000000000000000000000000000000..8c96737a71e30452cad54c0afee7f8b870eed477 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Q#@T9Ln2z= zUOvd_tjNRmU`q+}`#iw{4)zl~d{0#P6e~_J+?#WN_svSD%GPgBKkz;Muh8LSBB;v5 zz+mC&a&6|<{Jd!@X?8N#qKx0BDk*t~mPhP1Pkd&2%;L!M=$}XLP729ZegF4%#@7uh ztpXmQCoQ)h_vWrBeKTkAJ?pcUXIW>&<(;dX8uZQPn1|-l*L92x41cDozkTn~QeJp} Q4$yrJp00i_>zopr0K9uuT>t<8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm+o.png new file mode 100644 index 0000000000000000000000000000000000000000..2f7df6cf274ff5472068b7d3faedba3f3856ba5b GIT binary patch literal 194 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}HJ&bxArY-_ zFK^^MpuppDQQ7%IP(bh|A?J;gyww(@b}sR9Nta@G-D%$NcO#2~i%a~(VmsYua;r}} zbBO)^%5eH=)`6OD_kRCoWO%UGJbIr< qp0U>7`#JsXT?$ISwsS|mQ$MVzYqkLB4hBzGKbLh*2~7Z)2L literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_l_arm.png new file mode 100644 index 0000000000000000000000000000000000000000..296fadab8a5cd53a9dd0ffeac87635f017baaebd GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R})t)YnArY-_ zFK^^+P!MT(cv&$+bA@JP=fa4WQ+*sx3q@tFuv9j@cm441O3sFkj+$7nmkH{EigG?!l4 zU;U=$+dci~GSB8198);9W9$DXJFPcdn|JRt^OC#f3#NR!&7k?6J0ZwTgQF4;YbRexSHC`BoSbm(#EKzcDbd&9^hP@I}QnNS@H8hIn@8Ek=RQ_4jE&mVW(kZ{n^-tcY z-an^!M)4tuW{Jm7tMfe>7$UO$-@j!H+Akd7RcV%R{X4sX@r=sqdJpETwTu3iF$k_T zYgRMl5J6`GwibBX76)!V;C7d`S0?htU|sMSror~7%f zxx&&Erya~|ILaGV&3i9;L{~Z>w98QM>@H>(m566|8H?X0_dI7jv!~l5V{PEMpOpt( zu0*Yz@`*1YZ^8F{Cizw~rml-WUp0Y4@ymHWgW7Hr{l2Ut7su;&T1^?#Bw8icjXtScRz{KD7SGETCf;JYD@<);T3K0RV`!OJ@K8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg+o.png new file mode 100644 index 0000000000000000000000000000000000000000..454fee43f6fec7f7aea282df6fec63f6528b98f3 GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}(>z@qLn2z= zUNPiiRupJ`IDG=6wsn8v%fdaY|H?V8(aFtWIe6&G?V~*kik}ZAnI7&x#N@=G*dp-g zzU>a~J>QFa7?qsPNh-E1o$H_15cH~ST@ri2-#Uk$k2wt2KQ>(NQ{7uGaB*itly=tI z__LelFW6f4dron2YTapGiR1-a*%%_S8582Jf4wMQ=SV0!7DTK8}Bwpy+14| SwfliCWbkzLb6Mw<&;$Tq3RM39 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/service_r_leg.png new file mode 100644 index 0000000000000000000000000000000000000000..6f11cf829767cfa96eb7bf4b607be7a7d17f8abc GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}(>+}rLn2z= zUNPi4q9EXKaldy<$jc(0(=x|*FZ<3h=~U6IOeQC;?S4BaJ4BSpbzD8+?pSbcXZ`n0 zJWHqiDwJsvSh01}y45db8n&lq$Q-|1*Q)uzZ@*-KSEbnlzkYU}YX`4MDYiUk3K0$6 zE0a*wRD0h)J91hcufhETHs^gVvpDn!E_gm?|3AUAj7m)%3>$K1f0cMXr?`e;bAsNV U55~H^KqoSIy85}Sb4q9e0C%BS4*&oF literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/meta.json b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/meta.json new file mode 100644 index 0000000000..6499772c7a --- /dev/null +++ b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/meta.json @@ -0,0 +1,79 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/cf45322c7ee16f9d7e43d5260daf24ceb77c1b25", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "mmi_off" + }, + { + "name": "mmi_alive" + }, + { + "name": "mmi_dead" + }, + { + "name": "mmi_brain" + }, + { + "name": "mmi_brain_alien" + }, + { + "name": "posibrain" + }, + { + "name": "posibrain-occupied", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "posibrain-searching", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_alive.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_alive.png new file mode 100644 index 0000000000000000000000000000000000000000..2732c5a291eeb0079885bc267ee4c6ffa8d6aa87 GIT binary patch literal 598 zcmV-c0;&CpP)Px%5J^NqR9J=Wlrd}DKp2K!We8R}To*6?3x!UhgF!>KB%Vq-hC&xl851kkW4L8gh0HCO?aI;g<8Dpww zm?azl7-Op0spv&*g#&e60Aszrg&*0j3cX@Vqt* zvxZ*Rg`;0>kJu~*tK(KpVv_M&wgXIpz_r=lMZ?Ddke)D(4&ezo1} zx-iTdJg=Q@P6*-EZ$D!DRUe<1_HV87g5@RTsZu>>0<(vQsvY>0010X%|K#z zxoR`U)Q6ipXqpDx51)$ehtM<)4>xyUjH$cJRa=A~T1^1JseJ%SY3wW|9xg%%fBUYC z#l<%$6bkt8{_z8N^|}ZEaDV?wtP`ib*L8yd`u#pUuboXGNdiD1LMc_E`x(9`rcMig zRv;Px$~(m|mVC)X}gCXuZ@9UAe`4(B`=)ar=icu--#MHI zAws-#v@~-m6H7V(UrVPgA{OVTzSY(Y@#5+l5xoOevd+gZ8$>jnc1?%5>6)d$sWECDNnivJZwhFxe zkVT#mH+Z%n6H7YjXaXK9m&sKad7C283EW(q(KC{QM>o>Z1c2k^gs%g|fp{IUk+6S1MVWE`DSjT-=1=cmmJkRPujkO|GQ zYkR8q6%zBaSQpO_;8wYCMQ(?iNPW-bcF*)B4-_C*>Q?2I`J30NH=xXhDxRF>$q_h& zvKZ9`IZt*$79RxPB_S^%w{{#<$_#b)xr!Ed%-o^xGV*mmg*xpbmRrPx$gGod|R9J=Wl|4?wKoEsr5O=`RHqg0G3DP3@2q5JEa1WZKfFe=KR5=NfEhXhr zMwV~{jio`MK)Haa#+t0{T@&coS9!*>o^Rj&7zhgb?;0F4NGG}ifb2ON@H5AM1=iVE zC%SqmOjUfo9z1g_f%>QyF@JQnnBwu_sm>EoBR83kaW}dL(9aQXflhQaydI*=%Q~lG z1pqLq!R_d#&abj{1H=b$(xPx%5lKWrR9J=Wls{&8s-vyL`)%*f-S^$S zlK_oIqtR&oH!RCimSrhkySJUKj=J1To2R2LH{ILLR#ILj6BVyIO#nb~bwfG*noUF| zCrl#_07PUu{hD1|-B18fb()Bp$iyXJNRTOn$SE~3?kmnHH8F$`ITF(Xl2|a{`#n^h zCWZqK=l0c}B$<^Yvn6A#dwk>WIQ^PkZddBjxN^HvcltH^_{QBS8EajRJGZa)h64{( zrwQNhEf{zZUIam4oVbUoP*?!~ct6ez@97x;;B<0Py8eBgBSEH=GFxs}>Wx=lJ_5jY z>-{$=C2_}2Cl{rcnHS&h(thQ(pSXtzf?%$ju}{EV2)`FsL_{VLnd9;JQ8h l%fwTY^gqAvxkjUj<1b7dJ=t|cxy}Fp002ovPDHLkV1k$84AlSt literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_off.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/mmi_off.png new file mode 100644 index 0000000000000000000000000000000000000000..b5d0ebcf25bb7540d3b583e40b683eca044a50c3 GIT binary patch literal 746 zcmVPx%q)9|UR9J=Wl}|{MaTv!xuNF8#-ystI83|sE3Y~yMzg%BIKbxlBONRV63T-K+lsk&@4IQ+uY-HX-0Kn%l3?H4`}^~J zKi}th;Rj`vRaRMLT|lxd3t5)MrI|A$#2ecGJ%n!F;~W+}&fyC|RGe14u$Vu&)W0NF zo#!pQBvzgN65ww`n_e>+C(@M#O;}5b92K0aHa4g9*;{4{#jwQ9vj%c zeB|Zb&r|z9zDtYQmA==HU-@oa{f%a>rDN2dPk(#VhGed#?PrpA&)@nMvn#*uRXE=3 z-3sUGFZgGP$Kz6==cyymyWmZy^R1m3n_g^}j;O4!V>=e%n&o+5Kj zj+hynUewLK>2%5Trvl`<0DyNskcT+fIb>aA*of@YY-uH_B#6bL#9~pBN`hufD;tr0 z)E@kT&c9eB0WkrlyYVPagod#T_Dv-Q3MA z;ZTrpF2LU2Udi+T1|nUuEQ|XO9$~fGXliN#I0gaQZrTZlf+x-wuIZ_D^HS;m3Q!CI z7(j|uXbhZbekMR61YiJ}oO}Vm`1mt}F_fPBxd3zs03AgCNsCpyf0p^Vb=LEHsn2s+ cW&OAM1DpzU#p(f}LI3~&07*qoM6N<$f=qp2O#lD@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-occupied.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-occupied.png new file mode 100644 index 0000000000000000000000000000000000000000..c4ea2b81775e604c05633e362323a535c9816503 GIT binary patch literal 2294 zcmai0doC7nC+{Phy8ikpP4Ca^_b1_GK-`82Kv)1|k*zexYT6^vF?&p2>UT>LD+`wx{Fj2zONuK> zZ3N@vxRFemGp^Y;q@%OPtW?Y^`|?U1WOh63ssCkn9Y554sjTvr&!pSZ-DsS;>g8%p z-J+BuNjHMer;F|_aiTXBqH6D(X_4Bb?W{BzLJeQ@8d!L+Uz5_@`;>*phivOOrw@P|)TXrO$P7~EY~E~tkZC6-qUU*^A? z7(5y4DF(v^6lfuH15u7O=sGo0SrvN1Q+aRd&v(Nb4Q{ng1=sn|x@=RW&d4yCLo zXn?X3YD{Oi7bfSF>{D;bHcsSY^pT3hFepA8MWF}zT6(Nrg!Xz&Uo}8Cg!&mIbGfFn zOsQUcO4#_FeP|jj(9mk<6Rl+dorr+u6-myBPTiRQfw1VVi^`+~I zpRDSVqa=vRAJ0b3F*<>8vF)j}3|@fz+aU$v5OcI*y9eH)ySI18y&hFLCcD5%ihmdu z`f4UCx_gOJ55qz`DKou(7v4`Y*j%DPQ$SRk{ZHCpUYPk(sZdbW@YqTFeYlsBshdVT zLcCHA6*oK?{3{3dds^>`2mb^wF&}LQQ^NCf2fssROP} zgJ2d`QxJu8SS)aU5~ht^AwT9pWpX_DfTzsT+u#F{w%&9E+n)`8p*a-suK{KPA6q3nKLfM z#j?Pg4~Be$o8uxG)Fcp{s+OFOm3MVEOE?#@X`n*X$VXC~o)p7kR~sh`%6K5mM^n~T zz<`L4huGU_<2ctWZ1Q#P;t;|23n@Mj8#(nUxpFsw9KkDfTFo%VZbY#_&&~u{6rYm0y^M<-6O)>IrX@bM6tWq zOgm=gb?tWg{SyQ8(5Ak&l_nB>ab|kl*Dhq393(^ycLgBOj{I?iS<($MnciK?j=eR$ zlDntAn~eDNnLP{Cwj?p=z@vF+xz^VJ_btf9IsNZ72m+vUbsp-}8-zfIn=!;){@yV^ zES!IU9|{z5A(v(b-oSDhmGCASdl(1Tp?V?Et&D07geR%W59gG|r{e*ew+^5TA$jHfR37y_LeSt+#^ zd?q)7HKPL$y&h_3#Bt+7UK+ZLOpW)2&z|`NMN`I$hB!3}pEaKJ!xnX6Th+R<5GO1J zqU(7x5-QzD-!lW~^h;zianziPx-)_n!quVMTb#fAsx!lkg->j@jB5 z!zd3$?hybo-Sp-hqe2r*dT61M_D}WzI?VMr0KFL0X}VBYksJQ*P`E3w*iQe8+QqnWigNa#xnzm5b zrzvzqL&QE0r8NYK1Qu*;_hW(fvx^@WlqCf*8*gBA@2#-L`2Ko2S#9k z9s%x;Fz=yFKVyFH`JGJp{~iAS68K$XUsooP;nr zw|AXp5tRdCd068-(L!qDhT`X+$B()Jr%6cDT5_ck+|Dk+A; zlAkLU^iHp`etTc7pq!|ExEzTxfw<7B7|;eNp-BEbn1kg00iQTAYNJJC7MH+_Hq*1N zi0!~0n&1&j1J9^`dzyIolSKGkLGdFTdxI60c)M)e;%%L(7GyMS+K zXvTvap#>dRu4r8OHk@CH`^NlLk?bGFagLm^^FZB)M;_euj|^aIecbBt5ua;+0iRNH A3jhEB literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-searching.png b/Resources/Textures/Objects/Specific/Robotics/mmi.rsi/posibrain-searching.png new file mode 100644 index 0000000000000000000000000000000000000000..026a2bc7c52a2e5a7e2cd95022347bf7baf614cd GIT binary patch literal 3396 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 LNkvXXu0mjfZS3@W literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger-u0.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger-u0.png new file mode 100644 index 0000000000000000000000000000000000000000..894e6d2d0d924e3425c9c59aeaf5c4672c387627 GIT binary patch literal 1001 zcmV9jD#BvL0rxgcxn``vlIzCZW-w#A|! zjNR|<+;e{Cch7h4*;+~gZnrz;a=C1678VvL6bfY|>(iEi*XxaG9Xm3jl#`uh>u@m~ zH4zO4gO-FW2_X2P1NCD3@>Ng?TttT(4${EDpe+H|1fD$?sY3`=ESw|)bCo?BAK zz;YyFvaLOOMEH8xok9`fygt|58iRaQo^p{P@MC3#vNAJ8rhry~*_m&Y?R2U({5xs@ z!Be^ojX-9M6(s-)4g0HA0DC_%KC0Rzi=+tT=f{gMvK%L3rvTv0cBYrW{Jj1MO{4%T zR^ZA_4}t%#0E072q`*HRfNf~>HK|vy91Y@X#;eGx{WSqT#dhu9rv_DCrG$6E*w`3d zXl)Z?kioTW*I!6`LM$jO$WQX7#T{XFwBo?=HJ&7Y=Y4f85cd^t)opu11V`4# zmD8)c=ZPtR@q#kK?&4`a+q8PUdv%SeN`QkOJJCdsx;m&`$<~-DKYy5u$k=FdBf3Mm-?1&)o)B`!elDv@7y+NarHgG! ztVMNRB@l0y+%XtjzH*i9(AV25zPUCK9CLg2d+nBq;9uqzb*-7M2DBs~r%`HF;Cx%N zZXQzO!r|nas}j-kaIU_f58v2S}16yO~!k7Q5VXIvOwwzIyqB>h%hh7v2iz?;cIi;NUPF zI()ZrWIGc=10r#b`FVQ-T+nO^Ty`cb1|S}d zMm6zvf@UC2A2T+VU}NA^U@;-wfmA9*@5^sIi;Dmbm;h%so3%U_8p!AKlo%bMP6C`F z2n{qE4eB)PI4uS?L!9%9xGe^1wOW^QU|b~}uLi)~)&LL+g*vTo%jqc$V-pk%23fz^Y*OXJyAAQUGJxD}PD_B=d$9mxR>-YOvKinT<9}sg>9QrXnAF=Y!?~eSQUV)yN_eDm%z>|SOp}<;E zUe}K$lgZ8*SZjTu=g%svzp|nV1cH6S!ct#U=dHGuS=$#1P)|?yzZu|w_+%=rB_BH= zDb40*sJGW$J+U=_6(W`H*}Gp8RZcR+VlhX;YPCw2rgEAYWJsqHFT4{XU}s-IBjlpD zrf;^rmZv69Q-1b=77M6cQ5gr-@lFVFtQu+b@sp<(0f^tfch`0`$P;JIo)bRHoKOGB zoRZ;Usu|$oCr+j5(Zd;*Mw#O+rGuO&iXK6 zzFq^o77Qr=e(+lK)ezgwla8oHfD^cKIj3l~v~kUVdR>ajm~i#lb#lV%xjEMIvjK68 z?HiXI)qDpo<}!wHY&AZ7<)T-FGi4UH7|@&eweB-)6~K#8 wyMhEEJvf)^OQ~ogp!|$vvnewmW*0BQZ$f*is`9olfB*mh07*qoM6N<$f>t4kcK`qY literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger0.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger0.png new file mode 100644 index 0000000000000000000000000000000000000000..eb287d885629a2fbb551642b7525bbdbf5c6b7a8 GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}nVv3=ArY-_ zuWjUPFc4{ZX#arg)@y}}ZYM1I9vL|ua5L`sc2Z#DhK;7Z9N(w%-z$9D*imz4id~iU zlZ|-{5y3}V*IIbH0D)cTmd9$h)7u~J`DXp+Rg;)bS32Bw>|&_+R6E&z7lXoC WiCtY9e#t;f89ZJ6T-G@yGywo-5Ifp=G)aKvu#cFdVWde{V7H^o!K5Nb0UqsX4|^k1 z9!PF8xbb$v?8EhsOe5Y|wER58cjWzYTmC)oKVQ55d!G={CWb%G%G!@F%&?p>KX zX_HP|D+}}8XRxqB>dlW>+nCTg+xjcd7`}dvT9g-hd*wX?Q|^Q<1Mkmr<^qwbnBvp?RM@hEeH z__xsdnsX`>MY~-eTl783H0V;QoHzaK|AzC|su#U${Tq@$!>&mo&^>UFVdQ&MBb@0G5NM_y7O^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger2.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger2.png new file mode 100644 index 0000000000000000000000000000000000000000..8db4afd5d7ec1311c32dea9f07bac2c7235053e0 GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}8J;eVArY-_ zuWsaQFc4{ZX#arg)@y}}ZYM1I9vL|ua5L`sc2Z#DhK;7Z9N(w%-z&V^*wJy~xX;VQ z+nwcJvo)By#@y)I?Be2L7rG^3^4raaKg2lur}6!My=T>u14VKd$|fi=%&WQoq+jn- Xt;DY6)t{q)b~1Rn`njxgN@xNAq5?)H literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger3.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgcharger3.png new file mode 100644 index 0000000000000000000000000000000000000000..420f5d69444d95f60ac5dbbcd8edf05c87b531a3 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}d7dtgArY;~ z2@Bn&34{&>u1>wlqNi+DglUvFR91&{r%+ah6V+zvJ{ZOYovYRdTk zZ0dSLh3jqV^4W$*w&y6^v{5UPxv=Sj&f$~Cqd+>_qVEBDmTtXGjqD5yXUN`t8~9il PXfuPStDnm{r-UW|2+}^9 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon1.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon1.png new file mode 100644 index 0000000000000000000000000000000000000000..f1ca9c40376be0ca5669695401ca0f1a7ed418a2 GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}ot`d^ArY-_ zFBx(jau8^JSl_&~oTEj?@l50!rbF*^gDSdtIm+%Gxaa2afkSb^_D&!PC{xWt~$(699wo BQEUJJ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon2.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon2.png new file mode 100644 index 0000000000000000000000000000000000000000..12195d5d0209f54c8c785329a6de4b311cf71768 GIT binary patch literal 1154 zcmV-|1bzF7P)Ga(r!Ql`clLc z9}I{hadC?X;t~{mQ1n4s)aFI1yRPw4B#{tO5fl}IO1#7-23s`oLR2(@CL8QSA{d(E zd~?nm&z?Q!WLMS?hBGre^Z(y`^UpuC-y#i^mzQgPzu$Ar@bEDD`}?y}wdqM95C~{i z!`WFA$jzDRX>cSykwmn!v(uH3D*-xR`%{gqcUn@ z?`AWJfYZRo&t9RdWFD3-t29^IDjc?|0lA-@t1)Vh)vlB6Bmzt#JjYIKOG_8ymu=f+ zy|*V(aynLHoZyzA9Jhc;#JbwX-+#mLKaS#j^J(1cc_@w9GvcKyH)NlR9W_Uh^ZVNz ziMb{swOX!}h4o1E6e=nz-~|#?^BAaf?xu<-fhSKMnJXZuM*RH;oQy?036z#CPMW8- zEiD3Sj~Pr#ZhoH+Oz|YZF)G;jWgyz)B_#q@R99D<^#{G(m@{ty24B6vf}**y@5Rg4 zW}kJ7+S}VFwe3nkMfIJoo+R1F#>U{w@!{6p`?9^LM0Y+a`t4(@HmsAj&Ynvn2$`>U zhIea}Z%IX|Tp{ssd>jjl7oo1M4(mlB<`fp9?B;FMw6vf_l;;CaF?EW*O;m_6K<8=Q zhIVjeRx~v=L3l$r9LC_AH#jfq?DP427#Ad2QbkGx^7Hi|92?b%th;&@>qY5+uE3yB z<3CZqTw9AAL4w)}+P%jh6UU=}yFK3*j&DLvm{l;-G^m^&IebXo z5pJoK2`9Lv9s)gy?66-@c6ePp;T<94dJiN#OicpH z`Nqv5{CPTp!(z4em8$l$ftQX>N{Wl*m|S1BY^%2cNh#v`E9o-{lK^G^CVN8GNrHy# z4^8zDz46^`L@qJaN+A9d4$W+1(+9dYv-6MRBnEfUJ{kc@Galrlq2jKpa??w`yW(hH1B|$muQY!^c{N0G?QhtHxG3JIw zSqI#=h%se%o41gQv0OXF@apg>1fC|fB%O5Ofmi({nON!dYX>HIza1kTpF07*qoM6N<$g5^aao&W#< literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon3.png b/Resources/Textures/Structures/Power/borg_charger.rsi/borgdecon3.png new file mode 100644 index 0000000000000000000000000000000000000000..dcd48a5a0002ceddb9ba8ae23cfc243864c6e537 GIT binary patch literal 1093 zcmV-L1iJf)P)`+kGrvb zu-tQwb7sDoGiT1Y69Pb1R+eNon+;`FR#sqQVj{#}orVM~7K@}*92&9#krCmB3Rm45 zDx&@U{knv7383+l$BX&+^_zZXH~~(dIst=&Lxu!UrsvrUJ}y3Dp=C*rgeCzpJ~27X z=Lzv~5C}+~_HR0(NkT;cjk6HK?mc@USU_E$QuU;=ekR#vLkH3_&} zE-1*)HB?wwSl~0?E;mr87BmT9#&3uHozLM|Oe`P2T3&*|)ObUAJ)J57T7r`^3y_(X z1bO)dvZvk5U^fXwZW$*T+N0E~rCUV+iwNFhEw&jMIdHM!G9QnQ_=X$}lZ+Ph3Y6v% zU=dMz+j#d5+;47zC!OsuH1d@z6Dwlx%RatFxkG!1oZ0+TlbCKHB5%=C{NdQ)dgnm(R<|wz%Ibl^ z5mned9uJrzOz`&oM?Oza^BP}c>!NJo*)v?%qmBTQ5bO0ddzFOY%gxT<9wL6Pufu`V zbSNn);d8s)&cBx`uJCVHR~I{?X$TMV9ur0ga{!G8^cspl&y;9uYlG6#Qq`KtWP)`@ z5`X@}F9IN6h_N%td=iTDdn92mm__+%>y-FanS)M?wVZMBJ#UX0}%4b(w&-UCNSB;AZVDFoIOe46=t%V#Y%bI>6~< zB!*e*-DJ3p8~?Gi>eHI}a00000 LNkvXXu0mjfKCkR* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Power/borg_charger.rsi/meta.json b/Resources/Textures/Structures/Power/borg_charger.rsi/meta.json new file mode 100644 index 0000000000..17330110b3 --- /dev/null +++ b/Resources/Textures/Structures/Power/borg_charger.rsi/meta.json @@ -0,0 +1,46 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/a0dd0415010ca55e15a9bdb2223cbbb3df582aef, edited by EmoGarbage404 (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "borgcharger1", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "borgcharger0" + }, + { + "name": "borgcharger2" + }, + { + "name": "borgcharger3" + }, + { + "name": "borgdecon1" + }, + { + "name": "borgdecon2" + }, + { + "name": "borgdecon3" + }, + { + "name": "borgcharger-u0" + }, + { + "name": "borgcharger-u1" + } + ] +} diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 2e4f6bfb8a..79f0474aa1 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -61,11 +61,13 @@ IL IP KHR + MMI MS OGL OOC OS PA + PAI PCM PE PNG