From cb49ec21b5327fb722c1b309de26bfcc8df1bec7 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 18 Aug 2024 15:24:19 -0400 Subject: [PATCH 1/2] Revert "Merge pull request #65 from VMSolidus/Fix-Fallback-maybe" This reverts commit 7b316b855158d30b268607a8ba8af369bca2a6fe, reversing changes made to b9796a7deb8dc58c759b5e89cdb7f8eb4c7756f5. --- Content.Shared/CCVar/CCVars.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 10957b4181..3a5c8b2b3f 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -166,7 +166,7 @@ public static readonly CVarDef /// The preset for the game to fall back to if the selected preset could not be used, and fallback is enabled. /// public static readonly CVarDef - GameLobbyFallbackPreset = CVarDef.Create("game.fallbackpreset", "Extended", CVar.ARCHIVE); + GameLobbyFallbackPreset = CVarDef.Create("game.fallbackpreset", "Traitor,Extended", CVar.ARCHIVE); /// /// Controls if people can win the game in Suspicion or Deathmatch. From 5d27ffcd38a2435707059589cf75a0e0843d252b Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 18 Aug 2024 15:24:22 -0400 Subject: [PATCH 2/2] Revert "Merge pull request #64 from VMSolidus/Changeling-Playtest" This reverts commit b9796a7deb8dc58c759b5e89cdb7f8eb4c7756f5, reversing changes made to c93299b28a148fd32211229393dead02e3afc6ed. --- .../SpawnEquipDeleteBenchmark.cs | 2 +- Content.Client/Actions/ActionsSystem.cs | 3 - .../Changeling/ChangelingSystem.cs | 47 - .../Humanoid/HumanoidAppearanceSystem.cs | 5 +- .../Systems/Actions/ActionUIController.cs | 2 - .../Systems/Actions/Controls/ActionButton.cs | 13 +- .../Tests/GameRules/NukeOpsTest.cs | 208 - .../Tests/GameRules/RuleMaxTimeRestartTest.cs | 1 - .../Tests/GameRules/SecretStartsTest.cs | 6 +- .../Actions/ActionOnInteractSystem.cs | 5 - Content.Server/Administration/ServerApi.cs | 1 - .../Systems/AdminVerbSystem.Antags.cs | 57 +- .../Antag/AntagSelectionPlayerPool.cs | 27 - .../Antag/AntagSelectionSystem.API.cs | 303 -- Content.Server/Antag/AntagSelectionSystem.cs | 598 +-- .../Components/AntagSelectionComponent.cs | 190 - .../GhostRoleAntagSpawnerComponent.cs | 14 - .../Antag/MobReplacementRuleSystem.cs | 153 +- .../Systems/ParadoxAnomalySystem.cs | 2 +- .../Events/GlimmerMobSpawnRule.cs | 1 - .../Events/PirateRadioSpawnRule.cs | 1 - .../Destructible/Thresholds/MinMax.cs | 15 +- Content.Server/Entry/EntryPoint.cs | 2 +- .../Components/DelayedStartRuleComponent.cs | 16 - .../GameTicking/GameTicker.GameRule.cs | 65 +- Content.Server/GameTicking/GameTicker.cs | 1 - .../Components/ActiveGameRuleComponent.cs | 2 +- .../Components/EndedGameRuleComponent.cs | 2 +- .../Components/GameRuleComponent.cs | 9 +- .../Rules/Components/LoadMapRuleComponent.cs | 29 - .../Rules/Components/NinjaRuleComponent.cs | 2 +- .../NukeOperativeSpawnerComponent.cs | 11 +- .../Components/NukeOpsShuttleComponent.cs | 2 - .../Rules/Components/NukeopsRuleComponent.cs | 60 +- .../Rules/Components/PiratesRuleComponent.cs | 24 + .../Components/RevolutionaryRuleComponent.cs | 37 + .../Rules/Components/ThiefRuleComponent.cs | 36 +- .../Rules/Components/TraitorRuleComponent.cs | 15 - .../Rules/Components/ZombieRuleComponent.cs | 58 + .../GameTicking/Rules/DeathMatchRuleSystem.cs | 24 +- .../Rules/GameRuleSystem.Utility.cs | 28 +- .../GameTicking/Rules/GameRuleSystem.cs | 38 +- .../Rules/InactivityTimeRestartRuleSystem.cs | 1 - .../Rules/KillCalloutRuleSystem.cs | 1 - .../GameTicking/Rules/LoadMapRuleSystem.cs | 80 - .../Rules/MaxTimeRestartRuleSystem.cs | 1 - .../GameTicking/Rules/NukeopsRuleSystem.cs | 757 ++- .../GameTicking/Rules/PiratesRuleSystem.cs | 321 ++ .../GameTicking/Rules/RespawnRuleSystem.cs | 1 - .../Rules/RevolutionaryRuleSystem.cs | 135 +- .../RoundstartStationVariationRuleSystem.cs | 1 - .../GameTicking/Rules/SandboxRuleSystem.cs | 1 - .../GameTicking/Rules/SecretRuleSystem.cs | 1 - .../GameTicking/Rules/SubGamemodesSystem.cs | 1 - .../GameTicking/Rules/ThiefRuleSystem.cs | 99 +- .../GameTicking/Rules/TraitorRuleSystem.cs | 212 +- .../GameTicking/Rules/ZombieRuleSystem.cs | 213 +- .../Changeling/ChangelingSystem.Abilities.cs | 618 --- .../Changeling/ChangelingSystem.cs | 634 --- .../GameTicking/Rules/ChangelingRuleSystem.cs | 122 - .../Components/ChangelingRuleComponent.cs | 24 - .../Components/AbsorbConditionComponent.cs | 11 - .../ImpersonateConditionComponent.cs | 29 - .../Components/StealDNAConditionComponent.cs | 11 - .../Systems/ChangelingObjectiveSystem.cs | 32 - .../Systems/ImpersonateConditionSystem.cs | 76 - .../Roles/ChangelingRoleComponent.cs | 8 - Content.Server/IoC/ServerContentIoC.cs | 2 +- .../StationEvents/Events/FreeProberRule.cs | 1 - .../Events/GlimmerEventSystem.cs | 1 - .../Events/GlimmerRandomSentienceRule.cs | 1 - .../Events/GlimmerRevenantSpawnRule.cs | 1 - .../Events/GlimmerWispSpawnRule.cs | 1 - .../StationEvents/Events/MassMindSwapRule.cs | 1 - .../StationEvents/Events/MidRoundAntagRule.cs | 1 - .../StationEvents/Events/NoosphericFryRule.cs | 1 - .../Events/NoosphericStormRule.cs | 1 - .../StationEvents/Events/NoosphericZapRule.cs | 1 - .../Events/PsionicCatGotYourTongueRule.cs | 1 - Content.Server/Objectives/ObjectivesSystem.cs | 19 +- .../PowerMonitoringConsoleSystem.cs | 5 +- .../Managers/IServerPreferencesManager.cs | 1 - .../Managers/ServerPreferencesManager.cs | 14 - .../RandomMetadata/RandomMetadataSystem.cs | 11 +- Content.Server/Roles/RoleSystem.cs | 1 - .../EntitySystems/ConditionalSpawnerSystem.cs | 1 - .../Station/Systems/StationSpawningSystem.cs | 2 +- .../BasicStationEventSchedulerSystem.cs | 1 - .../Components/LoneOpsSpawnRuleComponent.cs | 18 + .../StationEvents/Events/AnomalySpawnRule.cs | 1 - .../Events/BluespaceArtifactRule.cs | 3 +- .../Events/BluespaceLockerRule.cs | 3 +- .../StationEvents/Events/BreakerFlipRule.cs | 3 +- .../Events/BureaucraticErrorRule.cs | 1 - .../StationEvents/Events/CargoGiftsRule.cs | 1 - .../StationEvents/Events/ClericalErrorRule.cs | 3 +- .../StationEvents/Events/FalseAlarmRule.cs | 1 - .../StationEvents/Events/GasLeakRule.cs | 1 - .../StationEvents/Events/ImmovableRodRule.cs | 1 - .../StationEvents/Events/IonStormRule.cs | 2 +- .../StationEvents/Events/KudzuGrowthRule.cs | 1 - .../StationEvents/Events/LoneOpsSpawnRule.cs | 47 + .../Events/MassHallucinationsRule.cs | 1 - .../StationEvents/Events/MeteorSwarmRule.cs | 1 - .../StationEvents/Events/NinjaSpawnRule.cs | 1 - .../Events/PowerGridCheckRule.cs | 1 - .../Events/RandomEntityStorageSpawnRule.cs | 1 - .../Events/RandomSentienceRule.cs | 1 - .../StationEvents/Events/RandomSpawnRule.cs | 1 - .../StationEvents/Events/SolarFlareRule.cs | 1 - .../Events/StationEventSystem.cs | 1 - .../StationEvents/Events/VentClogRule.cs | 1 - .../StationEvents/Events/VentCrittersRule.cs | 1 - .../RampingStationEventSchedulerSystem.cs | 1 - .../Components/AutoTraitorComponent.cs | 4 +- .../Traitor/Systems/AutoTraitorSystem.cs | 48 +- .../Uplink/Commands/AddUplinkCommand.cs | 5 +- .../Zombies/PendingZombieComponent.cs | 16 - Content.Server/Zombies/ZombieSystem.cs | 4 - .../Actions/ActionContainerSystem.cs | 28 - Content.Shared/Actions/ActionEvents.cs | 5 - Content.Shared/Actions/BaseActionComponent.cs | 8 +- .../Actions/Events/ActionPerformedEvent.cs | 8 - Content.Shared/Actions/SharedActionsSystem.cs | 33 +- Content.Shared/Alert/AlertType.cs | 2 - Content.Shared/Antag/AntagAcceptability.cs | 5 - Content.Shared/CCVar/CCVars.cs | 85 + .../Loadouts/Systems/LoadoutSystem.cs | 2 +- Content.Shared/Content.Shared.csproj | 3 - .../Changeling/AbsorbableComponent.cs | 12 - .../Changeling/AbsorbedComponent.cs | 13 - .../Goobstation/Changeling/AbsorbedSystem.cs | 27 - .../Changeling/Changeling.Actions.cs | 69 - .../Changeling/Changeling.DoAfter.cs | 7 - .../Changeling/ChangelingComponent.cs | 161 - .../Changeling/HivemindComponent.cs | 12 - .../SharedHumanoidAppearanceSystem.cs | 5 +- .../Inventory/InventorySystem.Helpers.cs | 4 +- .../NukeOps/NukeOperativeComponent.cs | 7 +- Content.Shared/Roles/SharedRoleSystem.cs | 64 - .../Station/SharedStationSpawningSystem.cs | 22 +- .../Ambience/Antag/changeling_start.ogg | Bin 120933 -> 0 bytes .../Changeling/Effects/changeling_shriek.ogg | Bin 28562 -> 0 bytes .../Goobstation/Changeling/alerts/alerts.ftl | 6 - .../Changeling/guidebook/guides.ftl | 1 - .../Changeling/objectives/changeling.ftl | 7 - .../Changeling/popup/changeling.ftl | 3 - .../Goobstation/Changeling/radio_channels.ftl | 1 - .../Changeling/store/categories.ftl | 4 - .../changeling/abilities/changeling.ftl | 64 - .../changeling/administration/antag.ftl | 3 - .../game-presets/preset-changeling.ftl | 20 - .../changeling/prototypes/roles/antags.ftl | 2 - .../changeling/store/changeling-catalog.ftl | 134 - .../Goobstation/changeling/store/currency.ftl | 1 - .../game-presets/presets-dualantag.ftl | 8 - .../game-presets/preset-pirates.ftl | 10 + Resources/Maps/Shuttles/striker.yml | 4778 ++++++++--------- Resources/Prototypes/Alerts/alerts.yml | 2 - .../Entities/Mobs/Species/vulpkanin.yml | 1 - .../Entities/Markers/Spawners/ghost_roles.yml | 30 +- .../Entities/Mobs/Species/arachnid.yml | 1 - .../Prototypes/Entities/Mobs/Species/base.yml | 3 - .../Entities/Mobs/Species/diona.yml | 1 - .../Entities/Mobs/Species/dwarf.yml | 1 - .../Entities/Mobs/Species/gingerbread.yml | 1 - .../Entities/Mobs/Species/harpy.yml | 1 - .../Entities/Mobs/Species/human.yml | 1 - .../Prototypes/Entities/Mobs/Species/moth.yml | 1 - .../Entities/Mobs/Species/reptilian.yml | 1 - .../Entities/Mobs/Species/slime.yml | 1 - .../Prototypes/Entities/Mobs/Species/vox.yml | 1 - Resources/Prototypes/GameRules/events.yml | 51 +- Resources/Prototypes/GameRules/midround.yml | 17 - Resources/Prototypes/GameRules/roundstart.yml | 131 +- .../Prototypes/Goobstation/Alerts/alerts.yml | 0 .../Changeling/Actions/changeling.yml | 537 -- .../Changeling/Alerts/changeling.yml | 35 - .../Changeling/Catalog/changeling_catalog.yml | 341 -- .../Clothing/Head/hardsuit-helmets.yml | 16 - .../Entities/Clothing/Head/helmets.yml | 19 - .../Entities/Clothing/OuterClothing/armor.yml | 28 - .../Clothing/OuterClothing/hardsuits.yml | 30 - .../Entities/Guidebook/changeling.yml | 9 - .../Entities/Objects/Shields/shields.yml | 31 - .../Weapons/Melee/changeling_armblade.yml | 48 - .../Weapons/Throwable/throwing_stars.yml | 12 - .../Changeling/Guidebook/antagonist.yml | 4 - .../Changeling/Objectives/changeling.yml | 67 - .../Changeling/Reagents/biological.yml | 27 - .../Changeling/Roles/Antags/changeling.yml | 7 - .../Changeling/StatusIcon/antag.yml | 6 - .../Changeling/Store/categories.yml | 16 - .../Goobstation/Changeling/Store/currency.yml | 4 - .../Goobstation/Changeling/ai_factions.yml | 7 - .../Goobstation/Changeling/radio_channels.yml | 7 - .../Goobstation/GameRules/roundstart.yml | 149 - .../Prototypes/Goobstation/game_presets.yml | 86 - Resources/Prototypes/Guidebook/antagonist.yml | 1 - Resources/Prototypes/Roles/Antags/nukeops.yml | 30 +- .../Roles/Jobs/Fun/misc_startinggear.yml | 14 +- Resources/Prototypes/game_presets.yml | 13 +- Resources/Prototypes/secret_weights.yml | 19 +- .../Guidebook/Antagonist/Changelings.xml | 61 - .../guidebook_changeling.rsi/icon.png | Bin 4746 -> 0 bytes .../guidebook_changeling.rsi/meta.json | 15 - .../Changeling/arm_blade.rsi/icon.png | Bin 876 -> 0 bytes .../Changeling/arm_blade.rsi/inhand-left.png | Bin 1425 -> 0 bytes .../Changeling/arm_blade.rsi/inhand-right.png | Bin 1443 -> 0 bytes .../Changeling/arm_blade.rsi/meta.json | 34 - .../Changeling/bone_shard.rsi/icon.png | Bin 4334 -> 0 bytes .../Changeling/bone_shard.rsi/meta.json | 14 - .../changeling_abilities.rsi/absorb_dna.png | Bin 607 -> 0 bytes .../anatomic_panacea.png | Bin 768 -> 0 bytes .../changeling_abilities.rsi/armblade.png | Bin 707 -> 0 bytes .../augmented_eyesight.png | Bin 717 -> 0 bytes .../changeling_abilities.rsi/biodegrade.png | Bin 563 -> 0 bytes .../changeling_abilities.rsi/bone_shard.png | Bin 712 -> 0 bytes .../chameleon_skin.png | Bin 1554 -> 0 bytes .../chitinous_armor.png | Bin 675 -> 0 bytes .../epinephrine_overdose.png | Bin 639 -> 0 bytes .../evolution_menu.png | Bin 673 -> 0 bytes .../changeling_abilities.rsi/fleshmend.png | Bin 675 -> 0 bytes .../hivemind_access.png | Bin 634 -> 0 bytes .../changeling_abilities.rsi/lesser_form.png | Bin 686 -> 0 bytes .../changeling_abilities.rsi/meta.json | 103 - .../organic_shield.png | Bin 594 -> 0 bytes .../shriek_dissonant.png | Bin 626 -> 0 bytes .../shriek_resonant.png | Bin 605 -> 0 bytes .../space_adaptation.png | Bin 662 -> 0 bytes .../changeling_abilities.rsi/stasis_enter.png | Bin 408 -> 0 bytes .../changeling_abilities.rsi/stasis_exit.png | Bin 558 -> 0 bytes .../sting_armblade.png | Bin 672 -> 0 bytes .../changeling_abilities.rsi/sting_blind.png | Bin 606 -> 0 bytes .../changeling_abilities.rsi/sting_cryo.png | Bin 654 -> 0 bytes .../sting_extractdna.png | Bin 555 -> 0 bytes .../sting_lethargic.png | Bin 647 -> 0 bytes .../changeling_abilities.rsi/sting_mute.png | Bin 609 -> 0 bytes .../sting_transform.png | Bin 711 -> 0 bytes .../strained_muscles.png | Bin 676 -> 0 bytes .../changeling_abilities.rsi/tentacle.png | Bin 691 -> 0 bytes .../changeling_abilities.rsi/transform.png | Bin 654 -> 0 bytes .../transform_cycle.png | Bin 768 -> 0 bytes .../Changeling/changeling_biomass.rsi/0.png | Bin 1356 -> 0 bytes .../Changeling/changeling_biomass.rsi/1.png | Bin 1084 -> 0 bytes .../Changeling/changeling_biomass.rsi/10.png | Bin 1389 -> 0 bytes .../Changeling/changeling_biomass.rsi/11.png | Bin 1387 -> 0 bytes .../Changeling/changeling_biomass.rsi/12.png | Bin 1397 -> 0 bytes .../Changeling/changeling_biomass.rsi/13.png | Bin 1401 -> 0 bytes .../Changeling/changeling_biomass.rsi/14.png | Bin 1393 -> 0 bytes .../Changeling/changeling_biomass.rsi/15.png | Bin 1396 -> 0 bytes .../Changeling/changeling_biomass.rsi/16.png | Bin 1363 -> 0 bytes .../Changeling/changeling_biomass.rsi/2.png | Bin 1106 -> 0 bytes .../Changeling/changeling_biomass.rsi/3.png | Bin 1131 -> 0 bytes .../Changeling/changeling_biomass.rsi/4.png | Bin 1140 -> 0 bytes .../Changeling/changeling_biomass.rsi/5.png | Bin 1191 -> 0 bytes .../Changeling/changeling_biomass.rsi/6.png | Bin 1225 -> 0 bytes .../Changeling/changeling_biomass.rsi/7.png | Bin 1252 -> 0 bytes .../Changeling/changeling_biomass.rsi/8.png | Bin 1282 -> 0 bytes .../Changeling/changeling_biomass.rsi/9.png | Bin 1318 -> 0 bytes .../changeling_biomass.rsi/meta.json | 63 - .../Changeling/changeling_chemicals.rsi/0.png | Bin 861 -> 0 bytes .../Changeling/changeling_chemicals.rsi/1.png | Bin 958 -> 0 bytes .../changeling_chemicals.rsi/10.png | Bin 1009 -> 0 bytes .../changeling_chemicals.rsi/11.png | Bin 1010 -> 0 bytes .../changeling_chemicals.rsi/12.png | Bin 1024 -> 0 bytes .../changeling_chemicals.rsi/13.png | Bin 1024 -> 0 bytes .../changeling_chemicals.rsi/14.png | Bin 1021 -> 0 bytes .../changeling_chemicals.rsi/15.png | Bin 1029 -> 0 bytes .../changeling_chemicals.rsi/16.png | Bin 1033 -> 0 bytes .../changeling_chemicals.rsi/17.png | Bin 1031 -> 0 bytes .../changeling_chemicals.rsi/18.png | Bin 1029 -> 0 bytes .../Changeling/changeling_chemicals.rsi/2.png | Bin 968 -> 0 bytes .../Changeling/changeling_chemicals.rsi/3.png | Bin 980 -> 0 bytes .../Changeling/changeling_chemicals.rsi/4.png | Bin 991 -> 0 bytes .../Changeling/changeling_chemicals.rsi/5.png | Bin 998 -> 0 bytes .../Changeling/changeling_chemicals.rsi/6.png | Bin 992 -> 0 bytes .../Changeling/changeling_chemicals.rsi/7.png | Bin 1000 -> 0 bytes .../Changeling/changeling_chemicals.rsi/8.png | Bin 1009 -> 0 bytes .../Changeling/changeling_chemicals.rsi/9.png | Bin 1007 -> 0 bytes .../changeling_chemicals.rsi/meta.json | 86 - .../ling_armor.rsi/equipped-OUTERCLOTHING.png | Bin 1103 -> 0 bytes .../Changeling/ling_armor.rsi/icon.png | Bin 494 -> 0 bytes .../Changeling/ling_armor.rsi/meta.json | 18 - .../ling_armor_helmet.rsi/equipped-HELMET.png | Bin 837 -> 0 bytes .../Changeling/ling_armor_helmet.rsi/icon.png | Bin 309 -> 0 bytes .../ling_armor_helmet.rsi/meta.json | 18 - .../equipped-OUTERCLOTHING.png | Bin 1590 -> 0 bytes .../Changeling/ling_spacesuit.rsi/icon.png | Bin 687 -> 0 bytes .../Changeling/ling_spacesuit.rsi/meta.json | 18 - .../equipped-HELMET.png | Bin 864 -> 0 bytes .../ling_spacesuit_helmet.rsi/icon.png | Bin 309 -> 0 bytes .../ling_spacesuit_helmet.rsi/meta.json | 18 - .../Changeling/shields.rsi/ling-icon.png | Bin 4706 -> 0 bytes .../shields.rsi/ling-inhand-left.png | Bin 5060 -> 0 bytes .../shields.rsi/ling-inhand-right.png | Bin 5044 -> 0 bytes .../Changeling/shields.rsi/meta.json | 22 - .../Misc/job_icons.rsi/Changeling.png | Bin 177 -> 0 bytes .../Interface/Misc/job_icons.rsi/meta.json | 3 - 299 files changed, 4800 insertions(+), 8853 deletions(-) delete mode 100644 Content.Client/Goobstation/Changeling/ChangelingSystem.cs delete mode 100644 Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs delete mode 100644 Content.Server/Antag/AntagSelectionPlayerPool.cs delete mode 100644 Content.Server/Antag/AntagSelectionSystem.API.cs delete mode 100644 Content.Server/Antag/Components/AntagSelectionComponent.cs delete mode 100644 Content.Server/Antag/Components/GhostRoleAntagSpawnerComponent.cs delete mode 100644 Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs rename Content.Server/GameTicking/{ => Rules}/Components/ActiveGameRuleComponent.cs (84%) rename Content.Server/GameTicking/{ => Rules}/Components/EndedGameRuleComponent.cs (81%) rename Content.Server/GameTicking/{ => Rules}/Components/GameRuleComponent.cs (83%) delete mode 100644 Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs create mode 100644 Content.Server/GameTicking/Rules/Components/PiratesRuleComponent.cs delete mode 100644 Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs delete mode 100644 Content.Server/Goobstation/Changeling/ChangelingSystem.Abilities.cs delete mode 100644 Content.Server/Goobstation/Changeling/ChangelingSystem.cs delete mode 100644 Content.Server/Goobstation/GameTicking/Rules/ChangelingRuleSystem.cs delete mode 100644 Content.Server/Goobstation/GameTicking/Rules/Components/ChangelingRuleComponent.cs delete mode 100644 Content.Server/Goobstation/Objectives/Components/AbsorbConditionComponent.cs delete mode 100644 Content.Server/Goobstation/Objectives/Components/ImpersonateConditionComponent.cs delete mode 100644 Content.Server/Goobstation/Objectives/Components/StealDNAConditionComponent.cs delete mode 100644 Content.Server/Goobstation/Objectives/Systems/ChangelingObjectiveSystem.cs delete mode 100644 Content.Server/Goobstation/Objectives/Systems/ImpersonateConditionSystem.cs delete mode 100644 Content.Server/Goobstation/Roles/ChangelingRoleComponent.cs create mode 100644 Content.Server/StationEvents/Components/LoneOpsSpawnRuleComponent.cs create mode 100644 Content.Server/StationEvents/Events/LoneOpsSpawnRule.cs delete mode 100644 Content.Shared/Actions/Events/ActionPerformedEvent.cs delete mode 100644 Content.Shared/Goobstation/Changeling/AbsorbableComponent.cs delete mode 100644 Content.Shared/Goobstation/Changeling/AbsorbedComponent.cs delete mode 100644 Content.Shared/Goobstation/Changeling/AbsorbedSystem.cs delete mode 100644 Content.Shared/Goobstation/Changeling/Changeling.Actions.cs delete mode 100644 Content.Shared/Goobstation/Changeling/Changeling.DoAfter.cs delete mode 100644 Content.Shared/Goobstation/Changeling/ChangelingComponent.cs delete mode 100644 Content.Shared/Goobstation/Changeling/HivemindComponent.cs delete mode 100644 Resources/Audio/Goobstation/Ambience/Antag/changeling_start.ogg delete mode 100644 Resources/Audio/Goobstation/Changeling/Effects/changeling_shriek.ogg delete mode 100644 Resources/Locale/en-US/Goobstation/Changeling/alerts/alerts.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/Changeling/guidebook/guides.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/Changeling/objectives/changeling.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/Changeling/popup/changeling.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/Changeling/radio_channels.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/Changeling/store/categories.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/changeling/abilities/changeling.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/changeling/administration/antag.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/changeling/game-ticking/game-presets/preset-changeling.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/changeling/prototypes/roles/antags.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/changeling/store/changeling-catalog.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/changeling/store/currency.ftl delete mode 100644 Resources/Locale/en-US/Goobstation/game-ticking/game-presets/presets-dualantag.ftl create mode 100644 Resources/Locale/en-US/game-ticking/game-presets/preset-pirates.ftl delete mode 100644 Resources/Prototypes/Goobstation/Alerts/alerts.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Actions/changeling.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Alerts/changeling.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Catalog/changeling_catalog.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/hardsuit-helmets.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/helmets.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/armor.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/hardsuits.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Entities/Guidebook/changeling.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Shields/shields.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Melee/changeling_armblade.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Throwable/throwing_stars.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Guidebook/antagonist.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Objectives/changeling.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Reagents/biological.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Roles/Antags/changeling.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/StatusIcon/antag.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Store/categories.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/Store/currency.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/ai_factions.yml delete mode 100644 Resources/Prototypes/Goobstation/Changeling/radio_channels.yml delete mode 100644 Resources/Prototypes/Goobstation/GameRules/roundstart.yml delete mode 100644 Resources/Prototypes/Goobstation/game_presets.yml delete mode 100644 Resources/ServerInfo/Goobstation/Changeling/Guidebook/Antagonist/Changelings.xml delete mode 100644 Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/icon.png delete mode 100644 Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/meta.json delete mode 100644 Resources/Textures/Goobstation/Changeling/arm_blade.rsi/icon.png delete mode 100644 Resources/Textures/Goobstation/Changeling/arm_blade.rsi/inhand-left.png delete mode 100644 Resources/Textures/Goobstation/Changeling/arm_blade.rsi/inhand-right.png delete mode 100644 Resources/Textures/Goobstation/Changeling/arm_blade.rsi/meta.json delete mode 100644 Resources/Textures/Goobstation/Changeling/bone_shard.rsi/icon.png delete mode 100644 Resources/Textures/Goobstation/Changeling/bone_shard.rsi/meta.json delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/absorb_dna.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/anatomic_panacea.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/armblade.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/augmented_eyesight.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/biodegrade.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/bone_shard.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/chameleon_skin.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/chitinous_armor.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/epinephrine_overdose.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/evolution_menu.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/fleshmend.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/hivemind_access.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/lesser_form.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/meta.json delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/organic_shield.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_dissonant.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_resonant.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/space_adaptation.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/stasis_enter.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/stasis_exit.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_armblade.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_blind.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_cryo.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_extractdna.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_lethargic.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_mute.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_transform.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/strained_muscles.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/tentacle.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/transform.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/transform_cycle.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/0.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/1.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/10.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/11.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/12.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/13.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/14.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/15.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/16.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/2.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/3.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/4.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/5.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/6.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/7.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/8.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/9.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/meta.json delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/0.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/1.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/10.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/11.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/12.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/13.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/14.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/15.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/16.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/17.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/18.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/2.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/3.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/4.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/5.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/6.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/7.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/8.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/9.png delete mode 100644 Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/meta.json delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_armor.rsi/equipped-OUTERCLOTHING.png delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_armor.rsi/icon.png delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_armor.rsi/meta.json delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/equipped-HELMET.png delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/icon.png delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/meta.json delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/equipped-OUTERCLOTHING.png delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/icon.png delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/meta.json delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/equipped-HELMET.png delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/icon.png delete mode 100644 Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/meta.json delete mode 100644 Resources/Textures/Goobstation/Changeling/shields.rsi/ling-icon.png delete mode 100644 Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-left.png delete mode 100644 Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-right.png delete mode 100644 Resources/Textures/Goobstation/Changeling/shields.rsi/meta.json delete mode 100644 Resources/Textures/Interface/Misc/job_icons.rsi/Changeling.png diff --git a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs index 8512107b69..de51b2fb19 100644 --- a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs +++ b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs @@ -58,7 +58,7 @@ await _pair.Server.WaitPost(() => for (var i = 0; i < N; i++) { _entity = server.EntMan.SpawnAttachedTo(Mob, _coords); - _spawnSys.EquipStartingGear(_entity, _gear); + _spawnSys.EquipStartingGear(_entity, _gear, null); server.EntMan.DeleteEntity(_entity); } }); diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs index b9d554607c..b992e77256 100644 --- a/Content.Client/Actions/ActionsSystem.cs +++ b/Content.Client/Actions/ActionsSystem.cs @@ -248,10 +248,7 @@ public void TriggerAction(EntityUid actionId, BaseActionComponent action) if (action.ClientExclusive) { if (instantAction.Event != null) - { instantAction.Event.Performer = user; - instantAction.Event.Action = actionId; - } PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime); } diff --git a/Content.Client/Goobstation/Changeling/ChangelingSystem.cs b/Content.Client/Goobstation/Changeling/ChangelingSystem.cs deleted file mode 100644 index 58d949a634..0000000000 --- a/Content.Client/Goobstation/Changeling/ChangelingSystem.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Content.Client.Alerts; -using Content.Client.UserInterface.Systems.Alerts.Controls; -using Content.Shared.Changeling; -using Content.Shared.StatusIcon.Components; -using Robust.Shared.Prototypes; - -namespace Content.Client.Changeling; - -public sealed partial class ChangelingSystem : EntitySystem -{ - - [Dependency] private readonly IPrototypeManager _prototype = default!; - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnUpdateAlert); - SubscribeLocalEvent(GetChanglingIcon); - } - - private void OnUpdateAlert(EntityUid uid, ChangelingComponent comp, ref UpdateAlertSpriteEvent args) - { - var stateNormalized = 0f; - - // hardcoded because uhh umm i don't know. send help. - switch (args.Alert.AlertViewEntity) - { - case "ChangelingChemicals": - stateNormalized = (int) (comp.Chemicals / comp.MaxChemicals * 18); - break; - - case "ChangelingBiomass": - stateNormalized = (int) (comp.Biomass / comp.MaxBiomass * 16); - break; - default: - return; - } - var sprite = args.SpriteViewEnt.Comp; - sprite.LayerSetState(AlertVisualLayers.Base, $"{stateNormalized}"); - } - - private void GetChanglingIcon(Entity ent, ref GetStatusIconsEvent args) - { - if (HasComp(ent) && _prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype)) - args.StatusIcons.Add(iconPrototype); - } -} diff --git a/Content.Client/Humanoid/HumanoidAppearanceSystem.cs b/Content.Client/Humanoid/HumanoidAppearanceSystem.cs index 867dcbc269..8087d1833e 100644 --- a/Content.Client/Humanoid/HumanoidAppearanceSystem.cs +++ b/Content.Client/Humanoid/HumanoidAppearanceSystem.cs @@ -118,11 +118,8 @@ private void SetLayerData( /// This should not be used if the entity is owned by the server. The server will otherwise /// override this with the appearance data it sends over. /// - public override void LoadProfile(EntityUid uid, HumanoidCharacterProfile? profile, HumanoidAppearanceComponent? humanoid = null) + public override void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, HumanoidAppearanceComponent? humanoid = null) { - if (profile == null) - return; - if (!Resolve(uid, ref humanoid)) { return; diff --git a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs index 7b67d23cec..09663ba82c 100644 --- a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs +++ b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs @@ -215,7 +215,6 @@ private bool TryTargetWorld(in PointerInputCmdArgs args, EntityUid actionId, Wor { action.Event.Target = coords; action.Event.Performer = user; - action.Event.Action = actionId; } _actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime); @@ -250,7 +249,6 @@ private bool TryTargetEntity(in PointerInputCmdArgs args, EntityUid actionId, En { action.Event.Target = entity; action.Event.Performer = user; - action.Event.Action = actionId; } _actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime); diff --git a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs index 19699696c6..2372d98f8d 100644 --- a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs +++ b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs @@ -281,19 +281,10 @@ public void UpdateIcons() _controller ??= UserInterfaceManager.GetUIController(); _spriteSys ??= _entities.System(); - if ((_controller.SelectingTargetFor == ActionId || _action.Toggled)) - { - if (_action.IconOn != null) - SetActionIcon(_spriteSys.Frame0(_action.IconOn)); - - if (_action.BackgroundOn != null) - _buttonBackgroundTexture = _spriteSys.Frame0(_action.BackgroundOn); - } + if ((_controller.SelectingTargetFor == ActionId || _action.Toggled) && _action.IconOn != null) + SetActionIcon(_spriteSys.Frame0(_action.IconOn)); else - { SetActionIcon(_action.Icon != null ? _spriteSys.Frame0(_action.Icon) : null); - _buttonBackgroundTexture = Theme.ResolveTexture("SlotBackground"); - } } public void UpdateBackground() diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs deleted file mode 100644 index f539daee36..0000000000 --- a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs +++ /dev/null @@ -1,208 +0,0 @@ -/* WD edit - -#nullable enable -using System.Linq; -using Content.Server.Body.Components; -using Content.Server.GameTicking; -using Content.Server.GameTicking.Presets; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.Mind; -using Content.Server.NPC.Systems; -using Content.Server.Pinpointer; -using Content.Server.Roles; -using Content.Server.Shuttles.Components; -using Content.Server.Station.Components; -using Content.Shared.CCVar; -using Content.Shared.Damage; -using Content.Shared.FixedPoint; -using Content.Shared.GameTicking; -using Content.Shared.Hands.Components; -using Content.Shared.Inventory; -using Content.Shared.NukeOps; -using Robust.Server.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.Map.Components; - -namespace Content.IntegrationTests.Tests.GameRules; - -[TestFixture] -public sealed class NukeOpsTest -{ - /// - /// Check that a nuke ops game mode can start without issue. I.e., that the nuke station and such all get loaded. - /// - [Test] - public async Task TryStopNukeOpsFromConstantlyFailing() - { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - DummyTicker = false, - Connected = true, - InLobby = true - }); - - var server = pair.Server; - var client = pair.Client; - var entMan = server.EntMan; - var mapSys = server.System(); - var ticker = server.System(); - var mindSys = server.System(); - var roleSys = server.System(); - var invSys = server.System(); - var factionSys = server.System(); - - Assert.That(server.CfgMan.GetCVar(CCVars.GridFill), Is.False); - server.CfgMan.SetCVar(CCVars.GridFill, true); - - // Initially in the lobby - Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); - Assert.That(client.AttachedEntity, Is.Null); - Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay)); - - // There are no grids or maps - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - - // And no nukie related components - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - - // Ready up and start nukeops - await pair.WaitClientCommand("toggleready True"); - Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.ReadyToPlay)); - await pair.WaitCommand("forcepreset Nukeops"); - await pair.RunTicksSync(10); - - // Game should have started - Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.InRound)); - Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.JoinedGame)); - Assert.That(client.EntMan.EntityExists(client.AttachedEntity)); - var player = pair.Player!.AttachedEntity!.Value; - Assert.That(entMan.EntityExists(player)); - - // Maps now exist - Assert.That(entMan.Count(), Is.GreaterThan(0)); - Assert.That(entMan.Count(), Is.GreaterThan(0)); - Assert.That(entMan.Count(), Is.EqualTo(2)); // The main station & nukie station - Assert.That(entMan.Count(), Is.GreaterThan(3)); // Each station has at least 1 grid, plus some shuttles - Assert.That(entMan.Count(), Is.EqualTo(1)); - - // And we now have nukie related components - Assert.That(entMan.Count(), Is.EqualTo(1)); - Assert.That(entMan.Count(), Is.EqualTo(1)); - Assert.That(entMan.Count(), Is.EqualTo(1)); - Assert.That(entMan.Count(), Is.EqualTo(1)); - - // The player entity should be the nukie commander - var mind = mindSys.GetMind(player)!.Value; - Assert.That(entMan.HasComponent(player)); - Assert.That(roleSys.MindIsAntagonist(mind)); - Assert.That(roleSys.MindHasRole(mind)); - - Assert.That(factionSys.IsMember(player, "Syndicate"), Is.True); - Assert.That(factionSys.IsMember(player, "NanoTrasen"), Is.False); - - var roles = roleSys.MindGetAllRoles(mind); - var cmdRoles = roles.Where(x => x.Prototype == "NukeopsCommander" && x.Component is NukeopsRoleComponent); - Assert.That(cmdRoles.Count(), Is.EqualTo(1)); - - // The game rule exists, and all the stations/shuttles/maps are properly initialized - var rule = entMan.AllComponents().Single().Component; - var mapRule = entMan.AllComponents().Single().Component; - foreach (var grid in mapRule.MapGrids) - { - Assert.That(entMan.EntityExists(grid)); - Assert.That(entMan.HasComponent(grid)); - Assert.That(entMan.HasComponent(grid)); - } - Assert.That(entMan.EntityExists(rule.TargetStation)); - - Assert.That(entMan.HasComponent(rule.TargetStation)); - - var nukieShuttlEnt = entMan.AllComponents().FirstOrDefault().Uid; - Assert.That(entMan.EntityExists(nukieShuttlEnt)); - - EntityUid? nukieStationEnt = null; - foreach (var grid in mapRule.MapGrids) - { - if (entMan.HasComponent(grid)) - { - nukieStationEnt = grid; - break; - } - } - - Assert.That(entMan.EntityExists(nukieStationEnt)); - var nukieStation = entMan.GetComponent(nukieStationEnt!.Value); - - Assert.That(entMan.EntityExists(nukieStation.Station)); - Assert.That(nukieStation.Station, Is.Not.EqualTo(rule.TargetStation)); - - Assert.That(server.MapMan.MapExists(mapRule.Map)); - var nukieMap = mapSys.GetMap(mapRule.Map!.Value); - - var targetStation = entMan.GetComponent(rule.TargetStation!.Value); - var targetGrid = targetStation.Grids.First(); - var targetMap = entMan.GetComponent(targetGrid).MapUid!.Value; - Assert.That(targetMap, Is.Not.EqualTo(nukieMap)); - - Assert.That(entMan.GetComponent(player).MapUid, Is.EqualTo(nukieMap)); - Assert.That(entMan.GetComponent(nukieStationEnt.Value).MapUid, Is.EqualTo(nukieMap)); - Assert.That(entMan.GetComponent(nukieShuttlEnt).MapUid, Is.EqualTo(nukieMap)); - - // The maps are all map-initialized, including the player - // Yes, this is necessary as this has repeatedly been broken somehow. - Assert.That(mapSys.IsInitialized(nukieMap)); - Assert.That(mapSys.IsInitialized(targetMap)); - Assert.That(mapSys.IsPaused(nukieMap), Is.False); - Assert.That(mapSys.IsPaused(targetMap), Is.False); - - EntityLifeStage LifeStage(EntityUid? uid) => entMan.GetComponent(uid!.Value).EntityLifeStage; - Assert.That(LifeStage(player), Is.GreaterThan(EntityLifeStage.Initialized)); - Assert.That(LifeStage(nukieMap), Is.GreaterThan(EntityLifeStage.Initialized)); - Assert.That(LifeStage(targetMap), Is.GreaterThan(EntityLifeStage.Initialized)); - Assert.That(LifeStage(nukieStationEnt.Value), Is.GreaterThan(EntityLifeStage.Initialized)); - Assert.That(LifeStage(nukieShuttlEnt), Is.GreaterThan(EntityLifeStage.Initialized)); - Assert.That(LifeStage(rule.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized)); - - // Make sure the player has hands. We've had fucking disarmed nukies before. - Assert.That(entMan.HasComponent(player)); - Assert.That(entMan.GetComponent(player).Hands.Count, Is.GreaterThan(0)); - - // While we're at it, lets make sure they aren't naked. I don't know how many inventory slots all mobs will be - // likely to have in the future. But nukies should probably have at least 3 slots with something in them. - var enumerator = invSys.GetSlotEnumerator(player); - int total = 0; - while (enumerator.NextItem(out _)) - { - total++; - } - Assert.That(total, Is.GreaterThan(3)); - - // Finally lets check the nukie commander passed basic training and figured out how to breathe. - var totalSeconds = 30; - var totalTicks = (int) Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds); - int increment = 5; - var resp = entMan.GetComponent(player); - var damage = entMan.GetComponent(player); - for (var tick = 0; tick < totalTicks; tick += increment) - { - await pair.RunTicksSync(increment); - Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold)); - Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero)); - } - - //ticker.SetGamePreset((GamePresetPrototype?)null); WD edit - server.CfgMan.SetCVar(CCVars.GridFill, false); - await pair.CleanReturnAsync(); - } -} - -*/ diff --git a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs index ffaff3b8de..1e3f9c9854 100644 --- a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs @@ -1,6 +1,5 @@ using Content.Server.GameTicking; using Content.Server.GameTicking.Commands; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Shared.CCVar; diff --git a/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs b/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs index 5d7ae8efbf..0f665a63de 100644 --- a/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs @@ -17,7 +17,6 @@ public async Task TestSecretStarts() var server = pair.Server; await server.WaitIdleAsync(); - var entMan = server.ResolveDependency(); var gameTicker = server.ResolveDependency().GetEntitySystem(); await server.WaitAssertion(() => @@ -33,7 +32,10 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { - Assert.That(gameTicker.GetAddedGameRules().Count(), Is.GreaterThan(1), $"No additional rules started by secret rule."); + foreach (var rule in gameTicker.GetAddedGameRules()) + { + Assert.That(gameTicker.GetActiveGameRules(), Does.Contain(rule)); + } // End all rules gameTicker.ClearGameRules(); diff --git a/Content.Server/Actions/ActionOnInteractSystem.cs b/Content.Server/Actions/ActionOnInteractSystem.cs index e226b7803b..c9a5f4b5d0 100644 --- a/Content.Server/Actions/ActionOnInteractSystem.cs +++ b/Content.Server/Actions/ActionOnInteractSystem.cs @@ -47,10 +47,7 @@ private void OnActivate(EntityUid uid, ActionOnInteractComponent component, Acti var (actId, act) = _random.Pick(options); if (act.Event != null) - { act.Event.Performer = args.User; - act.Event.Action = actId; - } _actions.PerformAction(args.User, null, actId, act, act.Event, _timing.CurTime, false); args.Handled = true; @@ -78,7 +75,6 @@ private void OnAfterInteract(EntityUid uid, ActionOnInteractComponent component, if (entAct.Event != null) { entAct.Event.Performer = args.User; - entAct.Event.Action = entActId; entAct.Event.Target = args.Target.Value; } @@ -104,7 +100,6 @@ private void OnAfterInteract(EntityUid uid, ActionOnInteractComponent component, if (act.Event != null) { act.Event.Performer = args.User; - act.Event.Action = actId; act.Event.Target = args.ClickLocation; } diff --git a/Content.Server/Administration/ServerApi.cs b/Content.Server/Administration/ServerApi.cs index 04fd38598f..6f10ef9b47 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Content.Server.Administration.Systems; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Rules.Components; using Content.Server.Maps; diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs index 108b4db0fb..9849d2df79 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs @@ -1,37 +1,23 @@ -using Content.Server.Administration.Commands; -using Content.Server.Antag; -using Content.Server.GameTicking.Rules.Components; +using Content.Server.GameTicking.Rules; using Content.Server.Zombies; using Content.Shared.Administration; using Content.Shared.Database; +using Content.Shared.Humanoid; using Content.Shared.Mind.Components; -using Content.Shared.Roles; using Content.Shared.Verbs; using Robust.Shared.Player; -using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Server.Administration.Systems; public sealed partial class AdminVerbSystem { - [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly ZombieSystem _zombie = default!; - - [ValidatePrototypeId] - private const string DefaultTraitorRule = "Traitor"; - - [ValidatePrototypeId] - private const string DefaultNukeOpRule = "LoneOpsSpawn"; - - [ValidatePrototypeId] - private const string DefaultRevsRule = "Revolutionary"; - - [ValidatePrototypeId] - private const string DefaultThiefRule = "Thief"; - - [ValidatePrototypeId] - private const string PirateGearId = "PirateGear"; + [Dependency] private readonly ThiefRuleSystem _thief = default!; + [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; + [Dependency] private readonly NukeopsRuleSystem _nukeopsRule = default!; + [Dependency] private readonly PiratesRuleSystem _piratesRule = default!; + [Dependency] private readonly RevolutionaryRuleSystem _revolutionaryRule = default!; // All antag verbs have names so invokeverb works. private void AddAntagVerbs(GetVerbsEvent args) @@ -54,7 +40,9 @@ private void AddAntagVerbs(GetVerbsEvent args) Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Structures/Wallmounts/posters.rsi"), "poster5_contraband"), Act = () => { - _antag.ForceMakeAntag(player, DefaultTraitorRule); + // if its a monkey or mouse or something dont give uplink or objectives + var isHuman = HasComp(args.Target); + _traitorRule.MakeTraitorAdmin(args.Target, giveUplink: isHuman, giveObjectives: isHuman); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-traitor"), @@ -83,7 +71,7 @@ private void AddAntagVerbs(GetVerbsEvent args) Icon = new SpriteSpecifier.Rsi(new("/Textures/Structures/Wallmounts/signs.rsi"), "radiation"), Act = () => { - _antag.ForceMakeAntag(player, DefaultNukeOpRule); + _nukeopsRule.MakeLoneNukie(args.Target); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-nuclear-operative"), @@ -97,14 +85,14 @@ private void AddAntagVerbs(GetVerbsEvent args) Icon = new SpriteSpecifier.Rsi(new("/Textures/Clothing/Head/Hats/pirate.rsi"), "icon"), Act = () => { - // pirates just get an outfit because they don't really have logic associated with them - SetOutfitCommand.SetOutfit(args.Target, PirateGearId, EntityManager); + _piratesRule.MakePirate(args.Target); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-pirate"), }; args.Verbs.Add(pirate); + //todo come here at some point dear lort. Verb headRev = new() { Text = Loc.GetString("admin-verb-text-make-head-rev"), @@ -112,7 +100,7 @@ private void AddAntagVerbs(GetVerbsEvent args) Icon = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/job_icons.rsi"), "HeadRevolutionary"), Act = () => { - _antag.ForceMakeAntag(player, DefaultRevsRule); + _revolutionaryRule.OnHeadRevAdmin(args.Target); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-head-rev"), @@ -126,26 +114,11 @@ private void AddAntagVerbs(GetVerbsEvent args) Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Clothing/Hands/Gloves/ihscombat.rsi"), "icon"), Act = () => { - _antag.ForceMakeAntag(player, DefaultThiefRule); + _thief.AdminMakeThief(args.Target, false); //Midround add pacified is bad }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-thief"), }; args.Verbs.Add(thief); - - // Goobstation - changelings - Verb ling = new() - { - Text = Loc.GetString("admin-verb-text-make-changeling"), - Category = VerbCategory.Antag, - Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Goobstation/Changeling/changeling_abilities.rsi"), "transform"), - Act = () => - { - _antag.ForceMakeAntag(player, "Changeling"); - }, - Impact = LogImpact.High, - Message = Loc.GetString("admin-verb-make-changeling"), - }; - args.Verbs.Add(ling); } } diff --git a/Content.Server/Antag/AntagSelectionPlayerPool.cs b/Content.Server/Antag/AntagSelectionPlayerPool.cs deleted file mode 100644 index 87873e96d1..0000000000 --- a/Content.Server/Antag/AntagSelectionPlayerPool.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Robust.Shared.Player; -using Robust.Shared.Random; - -namespace Content.Server.Antag; - -public sealed class AntagSelectionPlayerPool (List> orderedPools) -{ - public bool TryPickAndTake(IRobustRandom random, [NotNullWhen(true)] out ICommonSession? session) - { - session = null; - - foreach (var pool in orderedPools) - { - if (pool.Count == 0) - continue; - - session = random.PickAndTake(pool); - break; - } - - return session != null; - } - - public int Count => orderedPools.Sum(p => p.Count); -} diff --git a/Content.Server/Antag/AntagSelectionSystem.API.cs b/Content.Server/Antag/AntagSelectionSystem.API.cs deleted file mode 100644 index 470f98fca1..0000000000 --- a/Content.Server/Antag/AntagSelectionSystem.API.cs +++ /dev/null @@ -1,303 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Content.Server.Antag.Components; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.Objectives; -using Content.Shared.Chat; -using Content.Shared.Mind; -using JetBrains.Annotations; -using Robust.Shared.Audio; -using Robust.Shared.Player; - -namespace Content.Server.Antag; - -public sealed partial class AntagSelectionSystem -{ - /// - /// Tries to get the next non-filled definition based on the current amount of selected minds and other factors. - /// - public bool TryGetNextAvailableDefinition(Entity ent, - [NotNullWhen(true)] out AntagSelectionDefinition? definition) - { - definition = null; - - var totalTargetCount = GetTargetAntagCount(ent); - var mindCount = ent.Comp.SelectedMinds.Count; - if (mindCount >= totalTargetCount) - return false; - - foreach (var def in ent.Comp.Definitions) - { - var target = GetTargetAntagCount(ent, null, def); - - if (mindCount < target) - { - definition = def; - return true; - } - - mindCount -= target; - } - - return false; - } - - /// - /// Gets the number of antagonists that should be present for a given rule based on the provided pool. - /// A null pool will simply use the player count. - /// - public int GetTargetAntagCount(Entity ent, AntagSelectionPlayerPool? pool = null) - { - var count = 0; - foreach (var def in ent.Comp.Definitions) - { - count += GetTargetAntagCount(ent, pool, def); - } - - return count; - } - - /// - /// Gets the number of antagonists that should be present for a given antag definition based on the provided pool. - /// A null pool will simply use the player count. - /// - public int GetTargetAntagCount(Entity ent, AntagSelectionPlayerPool? pool, AntagSelectionDefinition def) - { - var poolSize = pool?.Count ?? _playerManager.Sessions.Length; - // factor in other definitions' affect on the count. - var countOffset = 0; - foreach (var otherDef in ent.Comp.Definitions) - { - countOffset += Math.Clamp(poolSize / otherDef.PlayerRatio, otherDef.Min, otherDef.Max) * otherDef.PlayerRatio; - } - // make sure we don't double-count the current selection - countOffset -= Math.Clamp((poolSize + countOffset) / def.PlayerRatio, def.Min, def.Max) * def.PlayerRatio; - - return Math.Clamp((poolSize - countOffset) / def.PlayerRatio, def.Min, def.Max); - } - - /// - /// Returns identifiable information for all antagonists to be used in a round end summary. - /// - /// - /// A list containing, in order, the antag's mind, the session data, and the original name stored as a string. - /// - public List<(EntityUid, SessionData, string)> GetAntagIdentifiers(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return new List<(EntityUid, SessionData, string)>(); - - var output = new List<(EntityUid, SessionData, string)>(); - foreach (var (mind, name) in ent.Comp.SelectedMinds) - { - if (!TryComp(mind, out var mindComp) || mindComp.OriginalOwnerUserId == null) - continue; - - if (!_playerManager.TryGetPlayerData(mindComp.OriginalOwnerUserId.Value, out var data)) - continue; - - output.Add((mind, data, name)); - } - return output; - } - - /// - /// Returns all the minds of antagonists. - /// - public List> GetAntagMinds(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return new(); - - var output = new List>(); - foreach (var (mind, _) in ent.Comp.SelectedMinds) - { - if (!TryComp(mind, out var mindComp) || mindComp.OriginalOwnerUserId == null) - continue; - - output.Add((mind, mindComp)); - } - return output; - } - - /// - /// Helper specifically for - /// - public List GetAntagMindEntityUids(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return new(); - - return ent.Comp.SelectedMinds.Select(p => p.Item1).ToList(); - } - - /// - /// Returns all the antagonists for this rule who are currently alive - /// - public IEnumerable GetAliveAntags(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - yield break; - - var minds = GetAntagMinds(ent); - foreach (var mind in minds) - { - if (_mind.IsCharacterDeadIc(mind)) - continue; - - if (mind.Comp.OriginalOwnedEntity != null) - yield return GetEntity(mind.Comp.OriginalOwnedEntity.Value); - } - } - - /// - /// Returns the number of alive antagonists for this rule. - /// - public int GetAliveAntagCount(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return 0; - - var numbah = 0; - var minds = GetAntagMinds(ent); - foreach (var mind in minds) - { - if (_mind.IsCharacterDeadIc(mind)) - continue; - - numbah++; - } - - return numbah; - } - - /// - /// Returns if there are any remaining antagonists alive for this rule. - /// - public bool AnyAliveAntags(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return false; - - return GetAliveAntags(ent).Any(); - } - - /// - /// Checks if all the antagonists for this rule are alive. - /// - public bool AllAntagsAlive(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return false; - - return GetAliveAntagCount(ent) == ent.Comp.SelectedMinds.Count; - } - - /// - /// Helper method to send the briefing text and sound to a player entity - /// - /// The entity chosen to be antag - /// The briefing text to send - /// The color the briefing should be, null for default - /// The sound to briefing/greeting sound to play - public void SendBriefing(EntityUid entity, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) - { - if (!_mind.TryGetMind(entity, out _, out var mindComponent)) - return; - - if (mindComponent.Session == null) - return; - - SendBriefing(mindComponent.Session, briefing, briefingColor, briefingSound); - } - - /// - /// Helper method to send the briefing text and sound to a list of sessions - /// - /// The sessions that will be sent the briefing - /// The briefing text to send - /// The color the briefing should be, null for default - /// The sound to briefing/greeting sound to play - [PublicAPI] - public void SendBriefing(List sessions, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) - { - foreach (var session in sessions) - { - SendBriefing(session, briefing, briefingColor, briefingSound); - } - } - - /// - /// Helper method to send the briefing text and sound to a session - /// - /// The player chosen to be an antag - /// The briefing data - public void SendBriefing( - ICommonSession? session, - BriefingData? data) - { - if (session == null || data == null) - return; - - var text = data.Value.Text == null ? string.Empty : Loc.GetString(data.Value.Text); - SendBriefing(session, text, data.Value.Color, data.Value.Sound); - } - - /// - /// Helper method to send the briefing text and sound to a session - /// - /// The player chosen to be an antag - /// The briefing text to send - /// The color the briefing should be, null for default - /// The sound to briefing/greeting sound to play - public void SendBriefing( - ICommonSession? session, - string briefing, - Color? briefingColor, - SoundSpecifier? briefingSound) - { - if (session == null) - return; - - _audio.PlayGlobal(briefingSound, session); - if (!string.IsNullOrEmpty(briefing)) - { - var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", briefing)); - _chat.ChatMessageToOne(ChatChannel.Server, briefing, wrappedMessage, default, false, session.Channel, - briefingColor); - } - } - - /// - /// This technically is a gamerule-ent-less way to make an entity an antag. - /// You should almost never be using this. - /// - public void ForceMakeAntag(ICommonSession? player, string defaultRule) where T : Component - { - var rule = ForceGetGameRuleEnt(defaultRule); - - if (!TryGetNextAvailableDefinition(rule, out var def)) - def = rule.Comp.Definitions.Last(); - - MakeAntag(rule, player, def.Value); - } - - /// - /// Tries to grab one of the weird specific antag gamerule ents or starts a new one. - /// This is gross code but also most of this is pretty gross to begin with. - /// - public Entity ForceGetGameRuleEnt(string id) where T : Component - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var comp)) - { - return (uid, comp); - } - var ruleEnt = GameTicker.AddGameRule(id); - RemComp(ruleEnt); - var antag = Comp(ruleEnt); - antag.SelectionsComplete = true; // don't do normal selection. - GameTicker.StartGameRule(ruleEnt); - return (ruleEnt, antag); - } -} diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index 7f61585add..b11c562df5 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -1,469 +1,347 @@ -using System.Linq; -using Content.Server.Antag.Components; -using Content.Server.Chat.Managers; -using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; -using Content.Server.Ghost.Roles; -using Content.Server.Ghost.Roles.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Server.Mind; using Content.Server.Preferences.Managers; -using Content.Server.Roles; using Content.Server.Roles.Jobs; using Content.Server.Shuttles.Components; -using Content.Server.Station.Systems; using Content.Shared.Antag; -using Content.Shared.Ghost; using Content.Shared.Humanoid; using Content.Shared.Players; using Content.Shared.Preferences; +using Content.Shared.Roles; using Robust.Server.Audio; -using Robust.Server.GameObjects; -using Robust.Server.Player; -using Robust.Shared.Enums; -using Robust.Shared.Map; +using Robust.Shared.Audio; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Random; +using System.Linq; +using Content.Shared.Chat; +using Robust.Shared.Enums; namespace Content.Server.Antag; -public sealed partial class AntagSelectionSystem : GameRuleSystem +public sealed class AntagSelectionSystem : GameRuleSystem { - [Dependency] private readonly IChatManager _chat = default!; - [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IServerPreferencesManager _pref = default!; - [Dependency] private readonly AudioSystem _audio = default!; - [Dependency] private readonly GhostRoleSystem _ghostRole = default!; + [Dependency] private readonly IServerPreferencesManager _prefs = default!; + [Dependency] private readonly AudioSystem _audioSystem = default!; [Dependency] private readonly JobSystem _jobs = default!; - [Dependency] private readonly MindSystem _mind = default!; - [Dependency] private readonly RoleSystem _role = default!; - [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; - [Dependency] private readonly TransformSystem _transform = default!; - - // arbitrary random number to give late joining some mild interest. - public const float LateJoinRandomChance = 0.5f; - - /// - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnTakeGhostRole); - - SubscribeLocalEvent(OnPlayerSpawning); - SubscribeLocalEvent(OnJobsAssigned); - SubscribeLocalEvent(OnSpawnComplete); - } + [Dependency] private readonly MindSystem _mindSystem = default!; + [Dependency] private readonly SharedRoleSystem _roleSystem = default!; - private void OnTakeGhostRole(Entity ent, ref TakeGhostRoleEvent args) + #region Eligible Player Selection + /// + /// Get all players that are eligible for an antag role + /// + /// All sessions from which to select eligible players + /// The prototype to get eligible players for + /// Should jobs that prohibit antag roles (ie Heads, Sec, Interns) be included + /// Should players already selected as antags be eligible + /// Should we ignore if the player has enabled this specific role + /// A custom condition that each player is tested against, if it returns true the player is excluded from eligibility + /// List of all player entities that match the requirements + public List GetEligiblePlayers(IEnumerable playerSessions, + ProtoId antagPrototype, + bool includeAllJobs = false, + AntagAcceptability acceptableAntags = AntagAcceptability.NotExclusive, + bool ignorePreferences = false, + bool allowNonHumanoids = false, + Func? customExcludeCondition = null) { - if (args.TookRole) - return; - - if (ent.Comp.Rule is not { } rule || ent.Comp.Definition is not { } def) - return; + var eligiblePlayers = new List(); - if (!Exists(rule) || !TryComp(rule, out var select)) - return; + foreach (var player in playerSessions) + { + if (IsPlayerEligible(player, antagPrototype, includeAllJobs, acceptableAntags, ignorePreferences, allowNonHumanoids, customExcludeCondition)) + eligiblePlayers.Add(player.AttachedEntity!.Value); + } - MakeAntag((rule, select), args.Player, def, ignoreSpawner: true); - args.TookRole = true; - _ghostRole.UnregisterGhostRole((ent, Comp(ent))); + return eligiblePlayers; } - private void OnPlayerSpawning(RulePlayerSpawningEvent args) + /// + /// Get all sessions that are eligible for an antag role, can be run prior to sessions being attached to an entity + /// This does not exclude sessions that have already been chosen as antags - that must be handled manually + /// + /// All sessions from which to select eligible players + /// The prototype to get eligible players for + /// Should we ignore if the player has enabled this specific role + /// List of all player sessions that match the requirements + public List GetEligibleSessions(IEnumerable playerSessions, ProtoId antagPrototype, bool ignorePreferences = false) { - var pool = args.PlayerPool; + var eligibleSessions = new List(); - var query = QueryActiveRules(); - while (query.MoveNext(out var uid, out _, out var comp, out _)) + foreach (var session in playerSessions) { - if (comp.SelectionTime != AntagSelectionTime.PrePlayerSpawn) - continue; - - if (comp.SelectionsComplete) - return; - - ChooseAntags((uid, comp), pool); - comp.SelectionsComplete = true; - - foreach (var session in comp.SelectedSessions) - { - args.PlayerPool.Remove(session); - GameTicker.PlayerJoinGame(session); - } + if (IsSessionEligible(session, antagPrototype, ignorePreferences)) + eligibleSessions.Add(session); } - } - - private void OnJobsAssigned(RulePlayerJobsAssignedEvent args) - { - var query = QueryActiveRules(); - while (query.MoveNext(out var uid, out _, out var comp, out _)) - { - if (comp.SelectionTime != AntagSelectionTime.PostPlayerSpawn) - continue; - - if (comp.SelectionsComplete) - continue; - ChooseAntags((uid, comp)); - comp.SelectionsComplete = true; - } + return eligibleSessions; } - private void OnSpawnComplete(PlayerSpawnCompleteEvent args) + /// + /// Test eligibility of the player for a specific antag role + /// + /// The player session to test + /// The prototype to get eligible players for + /// Should jobs that prohibit antag roles (ie Heads, Sec, Interns) be included + /// Should players already selected as antags be eligible + /// Should we ignore if the player has enabled this specific role + /// A function, accepting an EntityUid and returning bool. Each player is tested against this, returning truw will exclude the player from eligibility + /// True if the player session matches the requirements, false otherwise + public bool IsPlayerEligible(ICommonSession session, + ProtoId antagPrototype, + bool includeAllJobs = false, + AntagAcceptability acceptableAntags = AntagAcceptability.NotExclusive, + bool ignorePreferences = false, + bool allowNonHumanoids = false, + Func? customExcludeCondition = null) { - if (!args.LateJoin) - return; - - // TODO: this really doesn't handle multiple latejoin definitions well - // eventually this should probably store the players per definition with some kind of unique identifier. - // something to figure out later. - - var query = QueryActiveRules(); - while (query.MoveNext(out var uid, out _, out var antag, out _)) - { - if (!RobustRandom.Prob(LateJoinRandomChance)) - continue; + if (!IsSessionEligible(session, antagPrototype, ignorePreferences)) + return false; - if (!antag.Definitions.Any(p => p.LateJoinAdditional)) - continue; + //Ensure the player has a mind + if (session.GetMind() is not { } playerMind) + return false; - if (!TryGetNextAvailableDefinition((uid, antag), out var def)) - continue; + //Ensure the player has an attached entity + if (session.AttachedEntity is not { } playerEntity) + return false; - if (TryMakeAntag((uid, antag), args.Player, def.Value)) - break; - } - } + //Ignore latejoined players, ie those on the arrivals station + if (HasComp(playerEntity)) + return false; - protected override void Added(EntityUid uid, AntagSelectionComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) - { - base.Added(uid, component, gameRule, args); + //Exclude jobs that cannot be antag, unless explicitly allowed + if (!includeAllJobs && !_jobs.CanBeAntag(session)) + return false; - for (var i = 0; i < component.Definitions.Count; i++) + //Check if the entity is already an antag + switch (acceptableAntags) { - var def = component.Definitions[i]; - - if (def.MinRange != null) - { - def.Min = def.MinRange.Value.Next(RobustRandom); - } - - if (def.MaxRange != null) - { - def.Max = def.MaxRange.Value.Next(RobustRandom); - } + //If we dont want to select any antag roles + case AntagAcceptability.None: + { + if (_roleSystem.MindIsAntagonist(playerMind)) + return false; + break; + } + //If we dont want to select exclusive antag roles + case AntagAcceptability.NotExclusive: + { + if (_roleSystem.MindIsExclusiveAntagonist(playerMind)) + return false; + break; + } } - } - - protected override void Started(EntityUid uid, AntagSelectionComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) - { - base.Started(uid, component, gameRule, args); - if (component.SelectionsComplete) - return; + //Unless explictly allowed, ignore non humanoids (eg pets) + if (!allowNonHumanoids && !HasComp(playerEntity)) + return false; - if (GameTicker.RunLevel != GameRunLevel.InRound) - return; + //If a custom condition was provided, test it and exclude the player if it returns true + if (customExcludeCondition != null && customExcludeCondition(playerEntity)) + return false; - if (GameTicker.RunLevel == GameRunLevel.InRound && component.SelectionTime == AntagSelectionTime.PrePlayerSpawn) - return; - ChooseAntags((uid, component)); - component.SelectionsComplete = true; + return true; } /// - /// Chooses antagonists from the current selection of players + /// Check if the session is eligible for a role, can be run prior to the session being attached to an entity /// - public void ChooseAntags(Entity ent) + /// Player session to check + /// Which antag prototype to check for + /// Ignore if the player has enabled this antag + /// True if the session matches the requirements, false otherwise + public bool IsSessionEligible(ICommonSession session, ProtoId antagPrototype, bool ignorePreferences = false) { - var sessions = _playerManager.Sessions.ToList(); - ChooseAntags(ent, sessions); + //Exclude disconnected or zombie sessions + //No point giving antag roles to them + if (session.Status == SessionStatus.Disconnected || + session.Status == SessionStatus.Zombie) + return false; + + //Check the player has this antag preference selected + //Unless we are ignoring preferences, in which case add them anyway + var pref = (HumanoidCharacterProfile) _prefs.GetPreferences(session.UserId).SelectedCharacter; + if (!pref.AntagPreferences.Contains(antagPrototype.Id) && !ignorePreferences) + return false; + + return true; } + #endregion /// - /// Chooses antagonists from the given selection of players + /// Helper method to calculate the number of antags to select based upon the number of players /// - public void ChooseAntags(Entity ent, List pool) + /// How many players there are on the server + /// How many players should there be for an additional antag + /// Maximum number of antags allowed + /// The number of antags that should be chosen + public int CalculateAntagCount(int playerCount, int playersPerAntag, int maxAntags) { - foreach (var def in ent.Comp.Definitions) - { - ChooseAntags(ent, pool, def); - } + return Math.Clamp(playerCount / playersPerAntag, 1, maxAntags); } + #region Antag Selection /// - /// Chooses antagonists from the given selection of players for the given antag definition. + /// Selects a set number of entities from several lists, prioritising the first list till its empty, then second list etc /// - public void ChooseAntags(Entity ent, List pool, AntagSelectionDefinition def) + /// Array of lists, which are chosen from in order until the correct number of items are selected + /// How many items to select + /// Up to the specified count of elements from all provided lists + public List ChooseAntags(int count, params List[] eligiblePlayerLists) { - var playerPool = GetPlayerPool(ent, pool, def); - var count = GetTargetAntagCount(ent, playerPool, def); - - // if there is both a spawner and players getting picked, let it fall back to a spawner. - var noSpawner = def.SpawnerPrototype == null; - for (var i = 0; i < count; i++) + var chosenPlayers = new List(); + foreach (var playerList in eligiblePlayerLists) { - var session = (ICommonSession?) null; - if (def.PickPlayer) + //Remove all chosen players from this list, to prevent duplicates + foreach (var chosenPlayer in chosenPlayers) { - if (!playerPool.TryPickAndTake(RobustRandom, out session) && noSpawner) - { - Log.Warning($"Couldn't pick a player for {ToPrettyString(ent):rule}, no longer choosing antags for this definition"); - break; - } - - if (session != null && ent.Comp.SelectedSessions.Contains(session)) - { - Log.Warning($"Somehow picked {session} for an antag when this rule already selected them previously"); - continue; - } + playerList.Remove(chosenPlayer); } - MakeAntag(ent, session, def); + //If we have reached the desired number of players, skip + if (chosenPlayers.Count >= count) + continue; + + //Pick and choose a random number of players from this list + chosenPlayers.AddRange(ChooseAntags(count - chosenPlayers.Count, playerList)); } + return chosenPlayers; } - /// - /// Tries to makes a given player into the specified antagonist. + /// Helper method to choose antags from a list /// - public bool TryMakeAntag(Entity ent, ICommonSession? session, AntagSelectionDefinition def, bool ignoreSpawner = false) + /// List of eligible players + /// How many to choose + /// Up to the specified count of elements from the provided list + public List ChooseAntags(int count, List eligiblePlayers) { - if (!IsSessionValid(ent, session, def) || - !IsEntityValid(session?.AttachedEntity, def)) + var chosenPlayers = new List(); + + for (var i = 0; i < count; i++) { - return false; + if (eligiblePlayers.Count == 0) + break; + + chosenPlayers.Add(RobustRandom.PickAndTake(eligiblePlayers)); } - MakeAntag(ent, session, def, ignoreSpawner); - return true; + return chosenPlayers; } /// - /// Makes a given player into the specified antagonist. + /// Selects a set number of sessions from several lists, prioritising the first list till its empty, then second list etc /// - public void MakeAntag(Entity ent, ICommonSession? session, AntagSelectionDefinition def, bool ignoreSpawner = false) + /// Array of lists, which are chosen from in order until the correct number of items are selected + /// How many items to select + /// Up to the specified count of elements from all provided lists + public List ChooseAntags(int count, params List[] eligiblePlayerLists) { - var antagEnt = (EntityUid?) null; - var isSpawner = false; - - if (session != null) - { - ent.Comp.SelectedSessions.Add(session); - - // we shouldn't be blocking the entity if they're just a ghost or smth. - if (!HasComp(session.AttachedEntity)) - antagEnt = session.AttachedEntity; - } - else if (!ignoreSpawner && def.SpawnerPrototype != null) // don't add spawners if we have a player, dummy. - { - antagEnt = Spawn(def.SpawnerPrototype); - isSpawner = true; - } - - if (!antagEnt.HasValue) - { - var getEntEv = new AntagSelectEntityEvent(session, ent); - RaiseLocalEvent(ent, ref getEntEv, true); - - if (!getEntEv.Handled) - { - throw new InvalidOperationException($"Attempted to make {session} antagonist in gamerule {ToPrettyString(ent)} but there was no valid entity for player."); - } - - antagEnt = getEntEv.Entity; - } - - if (antagEnt is not { } player) - return; - - var getPosEv = new AntagSelectLocationEvent(session, ent); - RaiseLocalEvent(ent, ref getPosEv, true); - if (getPosEv.Handled) - { - var playerXform = Transform(player); - var pos = RobustRandom.Pick(getPosEv.Coordinates); - _transform.SetMapCoordinates((player, playerXform), pos); - } - - if (isSpawner) - { - if (!TryComp(player, out var spawnerComp)) - { - Log.Error("Antag spawner with GhostRoleAntagSpawnerComponent."); - return; - } - - spawnerComp.Rule = ent; - spawnerComp.Definition = def; - return; - } - - EntityManager.AddComponents(player, def.Components); - _stationSpawning.EquipStartingGear(player, def.StartingGear); - - if (session != null) + var chosenPlayers = new List(); + foreach (var playerList in eligiblePlayerLists) { - var curMind = session.GetMind(); - if (curMind == null) + //Remove all chosen players from this list, to prevent duplicates + foreach (var chosenPlayer in chosenPlayers) { - curMind = _mind.CreateMind(session.UserId, Name(antagEnt.Value)); - _mind.SetUserId(curMind.Value, session.UserId); + playerList.Remove(chosenPlayer); } - _mind.TransferTo(curMind.Value, antagEnt, ghostCheckOverride: true); - _role.MindAddRoles(curMind.Value, def.MindComponents); - ent.Comp.SelectedMinds.Add((curMind.Value, Name(player))); - } + //If we have reached the desired number of players, skip + if (chosenPlayers.Count >= count) + continue; - if (def.Briefing is { } briefing) - { - SendBriefing(session, briefing); + //Pick and choose a random number of players from this list + chosenPlayers.AddRange(ChooseAntags(count - chosenPlayers.Count, playerList)); } - - var afterEv = new AfterAntagEntitySelectedEvent(session, player, ent, def); - RaiseLocalEvent(ent, ref afterEv, true); + return chosenPlayers; } - /// - /// Gets an ordered player pool based on player preferences and the antagonist definition. + /// Helper method to choose sessions from a list /// - public AntagSelectionPlayerPool GetPlayerPool(Entity ent, List sessions, AntagSelectionDefinition def) + /// List of eligible sessions + /// How many to choose + /// Up to the specified count of elements from the provided list + public List ChooseAntags(int count, List eligiblePlayers) { - var preferredList = new List(); - var secondBestList = new List(); - var unwantedList = new List(); - var invalidList = new List(); - foreach (var session in sessions) + var chosenPlayers = new List(); + + for (int i = 0; i < count; i++) { - if (!IsSessionValid(ent, session, def) || - !IsEntityValid(session.AttachedEntity, def)) - { - invalidList.Add(session); - continue; - } + if (eligiblePlayers.Count == 0) + break; - var pref = (HumanoidCharacterProfile) _pref.GetPreferences(session.UserId).SelectedCharacter; - if (def.PrefRoles.Count != 0 && pref.AntagPreferences.Any(p => def.PrefRoles.Contains(p))) - { - preferredList.Add(session); - } - else if (def.FallbackRoles.Count != 0 && pref.AntagPreferences.Any(p => def.FallbackRoles.Contains(p))) - { - secondBestList.Add(session); - } - else - { - unwantedList.Add(session); - } + chosenPlayers.Add(RobustRandom.PickAndTake(eligiblePlayers)); } - return new AntagSelectionPlayerPool(new() { preferredList, secondBestList, unwantedList, invalidList }); + return chosenPlayers; } + #endregion + #region Briefings /// - /// Checks if a given session is valid for an antagonist. + /// Helper method to send the briefing text and sound to a list of entities /// - public bool IsSessionValid(Entity ent, ICommonSession? session, AntagSelectionDefinition def, EntityUid? mind = null) + /// The players chosen to be antags + /// The briefing text to send + /// The color the briefing should be, null for default + /// The sound to briefing/greeting sound to play + public void SendBriefing(List entities, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) { - if (session == null) - return true; - - mind ??= session.GetMind(); - - if (session.Status is SessionStatus.Disconnected or SessionStatus.Zombie) - return false; - - if (ent.Comp.SelectedSessions.Contains(session)) - return false; - - //todo: we need some way to check that we're not getting the same role twice. (double picking thieves or zombies through midrounds) - - switch (def.MultiAntagSetting) + foreach (var entity in entities) { - case AntagAcceptability.None: - { - if (_role.MindIsAntagonist(mind)) - return false; - break; - } - case AntagAcceptability.NotExclusive: - { - if (_role.MindIsExclusiveAntagonist(mind)) - return false; - break; - } + SendBriefing(entity, briefing, briefingColor, briefingSound); } - - // todo: expand this to allow for more fine antag-selection logic for game rules. - if (!_jobs.CanBeAntag(session)) - return false; - - return true; } /// - /// Checks if a given entity (mind/session not included) is valid for a given antagonist. + /// Helper method to send the briefing text and sound to a player entity /// - private bool IsEntityValid(EntityUid? entity, AntagSelectionDefinition def) + /// The entity chosen to be antag + /// The briefing text to send + /// The color the briefing should be, null for default + /// The sound to briefing/greeting sound to play + public void SendBriefing(EntityUid entity, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) { - if (entity == null) - return false; + if (!_mindSystem.TryGetMind(entity, out _, out var mindComponent)) + return; - if (HasComp(entity)) - return false; + if (mindComponent.Session == null) + return; - if (!def.AllowNonHumans && !HasComp(entity)) - return false; + SendBriefing(mindComponent.Session, briefing, briefingColor, briefingSound); + } - if (def.Whitelist != null) - { - if (!def.Whitelist.IsValid(entity.Value, EntityManager)) - return false; - } + /// + /// Helper method to send the briefing text and sound to a list of sessions + /// + /// + /// + /// + /// - if (def.Blacklist != null) + public void SendBriefing(List sessions, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) + { + foreach (var session in sessions) { - if (def.Blacklist.IsValid(entity.Value, EntityManager)) - return false; + SendBriefing(session, briefing, briefingColor, briefingSound); } - - return true; } -} - -/// -/// Event raised on a game rule entity in order to determine what the antagonist entity will be. -/// Only raised if the selected player's current entity is invalid. -/// -[ByRefEvent] -public record struct AntagSelectEntityEvent(ICommonSession? Session, Entity GameRule) -{ - public readonly ICommonSession? Session = Session; - - public bool Handled => Entity != null; - - public EntityUid? Entity; -} - -/// -/// Event raised on a game rule entity to determine the location for the antagonist. -/// -[ByRefEvent] -public record struct AntagSelectLocationEvent(ICommonSession? Session, Entity GameRule) -{ - public readonly ICommonSession? Session = Session; - - public bool Handled => Coordinates.Any(); + /// + /// Helper method to send the briefing text and sound to a session + /// + /// The player chosen to be an antag + /// The briefing text to send + /// The color the briefing should be, null for default + /// The sound to briefing/greeting sound to play - public List Coordinates = new(); + public void SendBriefing(ICommonSession session, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) + { + _audioSystem.PlayGlobal(briefingSound, session); + var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", briefing)); + ChatManager.ChatMessageToOne(ChatChannel.Server, briefing, wrappedMessage, default, false, session.Channel, briefingColor); + } + #endregion } - -/// -/// Event raised on a game rule entity after the setup logic for an antag is complete. -/// Used for applying additional more complex setup logic. -/// -[ByRefEvent] -public readonly record struct AfterAntagEntitySelectedEvent(ICommonSession? Session, EntityUid EntityUid, Entity GameRule, AntagSelectionDefinition Def); diff --git a/Content.Server/Antag/Components/AntagSelectionComponent.cs b/Content.Server/Antag/Components/AntagSelectionComponent.cs deleted file mode 100644 index 4c32d9c2ca..0000000000 --- a/Content.Server/Antag/Components/AntagSelectionComponent.cs +++ /dev/null @@ -1,190 +0,0 @@ -using Content.Server.Administration.Systems; -using Content.Server.Destructible.Thresholds; -using Content.Shared.Antag; -using Content.Shared.Roles; -using Content.Shared.Storage; -using Content.Shared.Whitelist; -using Robust.Shared.Audio; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; - -namespace Content.Server.Antag.Components; - -[RegisterComponent, Access(typeof(AntagSelectionSystem), typeof(AdminVerbSystem))] -public sealed partial class AntagSelectionComponent : Component -{ - /// - /// Has the primary selection of antagonists finished yet? - /// - [DataField] - public bool SelectionsComplete; - - /// - /// The definitions for the antagonists - /// - [DataField] - public List Definitions = new(); - - /// - /// The minds and original names of the players selected to be antagonists. - /// - [DataField] - public List<(EntityUid, string)> SelectedMinds = new(); - - /// - /// When the antag selection will occur. - /// - [DataField] - public AntagSelectionTime SelectionTime = AntagSelectionTime.PostPlayerSpawn; - - /// - /// Cached sessions of players who are chosen. Used so we don't have to rebuild the pool multiple times in a tick. - /// Is not serialized. - /// - public HashSet SelectedSessions = new(); -} - -[DataDefinition] -public partial struct AntagSelectionDefinition() -{ - /// - /// A list of antagonist roles that are used for selecting which players will be antagonists. - /// - [DataField] - public List> PrefRoles = new(); - - /// - /// Fallback for . Useful if you need multiple role preferences for a team antagonist. - /// - [DataField] - public List> FallbackRoles = new(); - - /// - /// Should we allow people who already have an antagonist role? - /// - [DataField] - public AntagAcceptability MultiAntagSetting = AntagAcceptability.None; - - /// - /// The minimum number of this antag. - /// - [DataField] - public int Min = 1; - - /// - /// The maximum number of this antag. - /// - [DataField] - public int Max = 1; - - /// - /// A range used to randomly select - /// - [DataField] - public MinMax? MinRange; - - /// - /// A range used to randomly select - /// - [DataField] - public MinMax? MaxRange; - - /// - /// a player to antag ratio: used to determine the amount of antags that will be present. - /// - [DataField] - public int PlayerRatio = 10; - - /// - /// Whether or not players should be picked to inhabit this antag or not. - /// If no players are left and is set, it will make a ghost role. - /// - [DataField] - public bool PickPlayer = true; - - /// - /// If true, players that latejoin into a round have a chance of being converted into antagonists. - /// - [DataField] - public bool LateJoinAdditional = false; - - //todo: find out how to do this with minimal boilerplate: filler department, maybe? - //public HashSet> JobBlacklist = new() - - /// - /// Mostly just here for legacy compatibility and reducing boilerplate - /// - [DataField] - public bool AllowNonHumans = false; - - /// - /// A whitelist for selecting which players can become this antag. - /// - [DataField] - public EntityWhitelist? Whitelist; - - /// - /// A blacklist for selecting which players can become this antag. - /// - [DataField] - public EntityWhitelist? Blacklist; - - /// - /// Components added to the player. - /// - [DataField] - public ComponentRegistry Components = new(); - - /// - /// Components added to the player's mind. - /// - [DataField] - public ComponentRegistry MindComponents = new(); - - /// - /// A set of starting gear that's equipped to the player. - /// - [DataField] - public ProtoId? StartingGear; - - /// - /// A briefing shown to the player. - /// - [DataField] - public BriefingData? Briefing; - - /// - /// A spawner used to defer the selection of this particular definition. - /// - /// - /// Not the cleanest way of doing this code but it's just an odd specific behavior. - /// Sue me. - /// - [DataField] - public EntProtoId? SpawnerPrototype; -} - -/// -/// Contains data used to generate a briefing. -/// -[DataDefinition] -public partial struct BriefingData -{ - /// - /// The text shown - /// - [DataField] - public LocId? Text; - - /// - /// The color of the text. - /// - [DataField] - public Color? Color; - - /// - /// The sound played. - /// - [DataField] - public SoundSpecifier? Sound; -} diff --git a/Content.Server/Antag/Components/GhostRoleAntagSpawnerComponent.cs b/Content.Server/Antag/Components/GhostRoleAntagSpawnerComponent.cs deleted file mode 100644 index fcaa4d4267..0000000000 --- a/Content.Server/Antag/Components/GhostRoleAntagSpawnerComponent.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Content.Server.Antag.Components; - -/// -/// Ghost role spawner that creates an antag for the associated gamerule. -/// -[RegisterComponent, Access(typeof(AntagSelectionSystem))] -public sealed partial class GhostRoleAntagSpawnerComponent : Component -{ - [DataField] - public EntityUid? Rule; - - [DataField] - public AntagSelectionDefinition? Definition; -} diff --git a/Content.Server/Antag/MobReplacementRuleSystem.cs b/Content.Server/Antag/MobReplacementRuleSystem.cs index 18837b5a7c..ba09c84bce 100644 --- a/Content.Server/Antag/MobReplacementRuleSystem.cs +++ b/Content.Server/Antag/MobReplacementRuleSystem.cs @@ -1,16 +1,45 @@ +using System.Numerics; +using Content.Server.Advertise.Components; +using Content.Server.Advertise.EntitySystems; using Content.Server.Antag.Mimic; -using Content.Server.GameTicking.Components; +using Content.Server.Chat.Systems; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; +using Content.Server.NPC.Systems; +using Content.Server.Station.Systems; +using Content.Server.GameTicking; using Content.Shared.VendingMachines; using Robust.Shared.Map; +using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Server.GameObjects; +using Robust.Shared.Physics.Systems; +using System.Linq; +using Robust.Shared.Physics; +using Content.Shared.Movement.Components; +using Content.Shared.Damage; +using Content.Server.NPC.HTN; +using Content.Server.NPC; +using Content.Shared.Weapons.Melee; +using Content.Server.Power.Components; +using Content.Shared.CombatMode; namespace Content.Server.Antag; public sealed class MobReplacementRuleSystem : GameRuleSystem { [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IComponentFactory _componentFactory = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly NpcFactionSystem _npcFaction = default!; + [Dependency] private readonly NPCSystem _npc = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly AdvertiseSystem _advertise = default!; + protected override void Started(EntityUid uid, MobReplacementRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { @@ -18,21 +47,133 @@ protected override void Started(EntityUid uid, MobReplacementRuleComponent compo var query = AllEntityQuery(); var spawns = new List<(EntityUid Entity, EntityCoordinates Coordinates)>(); + var stations = _gameTicker.GetSpawnableStations(); while (query.MoveNext(out var vendingUid, out _, out var xform)) { - if (!_random.Prob(component.Chance)) + var ownerStation = _station.GetOwningStation(vendingUid); + + if (ownerStation == null + || ownerStation != stations[0]) + continue; + + // Make sure that we aren't running this on something that is already a mimic + if (HasComp(vendingUid)) continue; spawns.Add((vendingUid, xform.Coordinates)); } - foreach (var entity in spawns) + if (spawns == null) { - var coordinates = entity.Coordinates; - Del(entity.Entity); + //WTF THE STATION DOESN'T EXIST! WE MUST BE IN A TEST! QUICK, PUT A MIMIC AT 0,0!!! + Spawn(component.Proto, new EntityCoordinates(uid, new Vector2(0, 0))); + } + else + { + // This is intentionally not clamped. If a server host wants to replace every vending machine in the entire station with a mimic, who am I to stop them? + var k = MathF.MaxMagnitude(component.NumberToReplace, 1); + while (k > 0 && spawns != null && spawns.Count > 0) + { + if (k > 1) + { + var spawnLocation = _random.PickAndTake(spawns); + BuildAMimicWorkshop(spawnLocation.Entity, component); + } + else + { + BuildAMimicWorkshop(spawns[0].Entity, component); + } + + if (k == MathF.MaxMagnitude(component.NumberToReplace, 1) + && component.DoAnnouncement) + _chat.DispatchStationAnnouncement(stations[0], Loc.GetString("station-event-rampant-intelligence-announcement"), playDefaultSound: true, + colorOverride: Color.Red, sender: "Central Command"); + + k--; + } + } + } + + /// + /// It's like Build a Bear, but MURDER + /// + /// + public void BuildAMimicWorkshop(EntityUid uid, MobReplacementRuleComponent component) + { + var metaData = MetaData(uid); + var vendorPrototype = metaData.EntityPrototype; + var mimicProto = _prototype.Index(component.Proto); + + var vendorComponents = vendorPrototype?.Components.Keys + .Where(n => n != "Transform" && n != "MetaData") + .Select(name => (name, _componentFactory.GetRegistration(name).Type)) + .ToList() ?? new List<(string name, Type type)>(); + + var mimicComponents = mimicProto?.Components.Keys + .Where(n => n != "Transform" && n != "MetaData") + .Select(name => (name, _componentFactory.GetRegistration(name).Type)) + .ToList() ?? new List<(string name, Type type)>(); - Spawn(component.Proto, coordinates); + foreach (var name in mimicComponents.Except(vendorComponents)) + { + var newComponent = _componentFactory.GetComponent(name.name); + EntityManager.AddComponent(uid, newComponent); } + + var xform = Transform(uid); + if (xform.Anchored) + _transform.Unanchor(uid, xform); + + SetupMimicNPC(uid, component); + + if (TryComp(uid, out var vendor) + && component.VendorModify) + SetupMimicVendor(uid, component, vendor); + } + /// + /// This handles getting the entity ready to be a hostile NPC + /// + /// + /// + private void SetupMimicNPC(EntityUid uid, MobReplacementRuleComponent component) + { + _physics.SetBodyType(uid, BodyType.KinematicController); + _npcFaction.AddFaction(uid, "SimpleHostile"); + + var melee = EnsureComp(uid); + melee.Angle = 0; + DamageSpecifier dspec = new() + { + DamageDict = new() + { + { "Blunt", component.MimicMeleeDamage } + } + }; + melee.Damage = dspec; + + var movementSpeed = EnsureComp(uid); + (movementSpeed.BaseSprintSpeed, movementSpeed.BaseWalkSpeed) = (component.MimicMoveSpeed, component.MimicMoveSpeed); + + var htn = EnsureComp(uid); + htn.RootTask = new HTNCompoundTask() { Task = component.MimicAIType }; + htn.Blackboard.SetValue(NPCBlackboard.NavSmash, component.MimicSmashGlass); + _npc.WakeNPC(uid, htn); + } + + /// + /// Handling specific interactions with vending machines + /// + /// + /// + /// + private void SetupMimicVendor(EntityUid uid, MobReplacementRuleComponent mimicComponent, AdvertiseComponent vendorComponent) + { + vendorComponent.MinimumWait = 5; + vendorComponent.MaximumWait = 15; + _advertise.SayAdvertisement(uid, vendorComponent); + + if (TryComp(uid, out var aPC)) + aPC.NeedsPower = false; } } diff --git a/Content.Server/DeltaV/ParadoxAnomaly/Systems/ParadoxAnomalySystem.cs b/Content.Server/DeltaV/ParadoxAnomaly/Systems/ParadoxAnomalySystem.cs index abdc950020..62d994dac3 100644 --- a/Content.Server/DeltaV/ParadoxAnomaly/Systems/ParadoxAnomalySystem.cs +++ b/Content.Server/DeltaV/ParadoxAnomaly/Systems/ParadoxAnomalySystem.cs @@ -144,7 +144,7 @@ private bool TrySpawnParadoxAnomaly(string rule, [NotNullWhen(true)] out EntityU if (job.StartingGear != null && _proto.TryIndex(job.StartingGear, out var gear)) { - _stationSpawning.EquipStartingGear(spawned, gear); + _stationSpawning.EquipStartingGear(spawned, gear, profile); _stationSpawning.EquipIdCard(spawned, profile.Name, job, diff --git a/Content.Server/DeltaV/StationEvents/Events/GlimmerMobSpawnRule.cs b/Content.Server/DeltaV/StationEvents/Events/GlimmerMobSpawnRule.cs index 1cb3809e21..22d96a5414 100644 --- a/Content.Server/DeltaV/StationEvents/Events/GlimmerMobSpawnRule.cs +++ b/Content.Server/DeltaV/StationEvents/Events/GlimmerMobSpawnRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Robust.Shared.Random; using Content.Server.GameTicking.Rules.Components; using Content.Server.NPC.Components; diff --git a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs b/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs index c5d199164b..ba042d8966 100644 --- a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs +++ b/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs @@ -19,7 +19,6 @@ using Content.Shared.Salvage; using Content.Shared.Random.Helpers; using System.Linq; -using Content.Server.GameTicking.Components; using Content.Shared.CCVar; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/Destructible/Thresholds/MinMax.cs b/Content.Server/Destructible/Thresholds/MinMax.cs index c44864183a..b438e7c0e8 100644 --- a/Content.Server/Destructible/Thresholds/MinMax.cs +++ b/Content.Server/Destructible/Thresholds/MinMax.cs @@ -1,6 +1,4 @@ -using Robust.Shared.Random; - -namespace Content.Server.Destructible.Thresholds +namespace Content.Server.Destructible.Thresholds { [Serializable] [DataDefinition] @@ -11,16 +9,5 @@ public partial struct MinMax [DataField("max")] public int Max; - - public MinMax(int min, int max) - { - Min = min; - Max = max; - } - - public int Next(IRobustRandom random) - { - return random.Next(Min, Max + 1); - } } } diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs index 48a6597349..a04f274491 100644 --- a/Content.Server/Entry/EntryPoint.cs +++ b/Content.Server/Entry/EntryPoint.cs @@ -5,9 +5,9 @@ using Content.Server.Afk; using Content.Server.Chat.Managers; using Content.Server.Connection; +using Content.Server.DiscordAuth; using Content.Server.JoinQueue; using Content.Server.Database; -using Content.Server.DiscordAuth; using Content.Server.EUI; using Content.Server.GameTicking; using Content.Server.GhostKick; diff --git a/Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs b/Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs deleted file mode 100644 index de4be83627..0000000000 --- a/Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; - -namespace Content.Server.GameTicking.Components; - -/// -/// Generic component used to track a gamerule that's start has been delayed. -/// -[RegisterComponent, AutoGenerateComponentPause] -public sealed partial class DelayedStartRuleComponent : Component -{ - /// - /// The time at which the rule will start properly. - /// - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField] - public TimeSpan RuleStartTime; -} diff --git a/Content.Server/GameTicking/GameTicker.GameRule.cs b/Content.Server/GameTicking/GameTicker.GameRule.cs index f52a3cb296..4ebe946af4 100644 --- a/Content.Server/GameTicking/GameTicker.GameRule.cs +++ b/Content.Server/GameTicking/GameTicker.GameRule.cs @@ -1,6 +1,6 @@ using System.Linq; using Content.Server.Administration; -using Content.Server.GameTicking.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Shared.Administration; using Content.Shared.Database; using Content.Shared.Prototypes; @@ -102,22 +102,6 @@ public bool StartGameRule(EntityUid ruleEntity, GameRuleComponent? ruleData = nu if (MetaData(ruleEntity).EntityPrototype?.ID is not { } id) // you really fucked up return false; - // If we already have it, then we just skip the delay as it has already happened. - if (!RemComp(ruleEntity) && ruleData.Delay != null) - { - var delayTime = TimeSpan.FromSeconds(ruleData.Delay.Value.Next(_robustRandom)); - - if (delayTime > TimeSpan.Zero) - { - _sawmill.Info($"Queued start for game rule {ToPrettyString(ruleEntity)} with delay {delayTime}"); - _adminLogger.Add(LogType.EventStarted, $"Queued start for game rule {ToPrettyString(ruleEntity)} with delay {delayTime}"); - - var delayed = EnsureComp(ruleEntity); - delayed.RuleStartTime = _gameTiming.CurTime + (delayTime); - return true; - } - } - _allPreviousGameRules.Add((RoundDuration(), id)); _sawmill.Info($"Started game rule {ToPrettyString(ruleEntity)}"); _adminLogger.Add(LogType.EventStarted, $"Started game rule {ToPrettyString(ruleEntity)}"); @@ -271,18 +255,6 @@ public IEnumerable GetAllGameRulePrototypes() } } - private void UpdateGameRules() - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var delay, out var rule)) - { - if (_gameTiming.CurTime < delay.RuleStartTime) - continue; - - StartGameRule(uid, rule); - } - } - #region Command Implementations [AdminCommand(AdminFlags.Fun)] @@ -351,3 +323,38 @@ private void ClearGameRulesCommand(IConsoleShell shell, string argstr, string[] #endregion } + +/* +/// +/// Raised broadcast when a game rule is selected, but not started yet. +/// +public sealed class GameRuleAddedEvent +{ + public GameRulePrototype Rule { get; } + + public GameRuleAddedEvent(GameRulePrototype rule) + { + Rule = rule; + } +} + +public sealed class GameRuleStartedEvent +{ + public GameRulePrototype Rule { get; } + + public GameRuleStartedEvent(GameRulePrototype rule) + { + Rule = rule; + } +} + +public sealed class GameRuleEndedEvent +{ + public GameRulePrototype Rule { get; } + + public GameRuleEndedEvent(GameRulePrototype rule) + { + Rule = rule; + } +} +*/ diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index fa23312268..efda3df0ca 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -133,7 +133,6 @@ public override void Update(float frameTime) return; base.Update(frameTime); UpdateRoundFlow(frameTime); - UpdateGameRules(); } } } diff --git a/Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ActiveGameRuleComponent.cs similarity index 84% rename from Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs rename to Content.Server/GameTicking/Rules/Components/ActiveGameRuleComponent.cs index b9e6fa5d4b..956768bdd9 100644 --- a/Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/ActiveGameRuleComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.GameTicking.Components; +namespace Content.Server.GameTicking.Rules.Components; /// /// Added to game rules before and removed before . diff --git a/Content.Server/GameTicking/Components/EndedGameRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/EndedGameRuleComponent.cs similarity index 81% rename from Content.Server/GameTicking/Components/EndedGameRuleComponent.cs rename to Content.Server/GameTicking/Rules/Components/EndedGameRuleComponent.cs index 3234bfff3a..4484abd4d0 100644 --- a/Content.Server/GameTicking/Components/EndedGameRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/EndedGameRuleComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.GameTicking.Components; +namespace Content.Server.GameTicking.Rules.Components; /// /// Added to game rules before . diff --git a/Content.Server/GameTicking/Components/GameRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/GameRuleComponent.cs similarity index 83% rename from Content.Server/GameTicking/Components/GameRuleComponent.cs rename to Content.Server/GameTicking/Rules/Components/GameRuleComponent.cs index 1e6c3f0ab1..6309b97402 100644 --- a/Content.Server/GameTicking/Components/GameRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/GameRuleComponent.cs @@ -1,7 +1,6 @@ -using Content.Server.Destructible.Thresholds; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Server.GameTicking.Components; +namespace Content.Server.GameTicking.Rules.Components; /// /// Component attached to all gamerule entities. @@ -21,12 +20,6 @@ public sealed partial class GameRuleComponent : Component /// [DataField] public int MinPlayers; - - /// - /// A delay for when the rule the is started and when the starting logic actually runs. - /// - [DataField] - public MinMax? Delay; } /// diff --git a/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs deleted file mode 100644 index 463aecbff5..0000000000 --- a/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Content.Server.Maps; -using Content.Shared.Whitelist; -using Robust.Shared.Map; -using Robust.Shared.Prototypes; -using Robust.Shared.Utility; - -namespace Content.Server.GameTicking.Rules.Components; - -/// -/// This is used for a game rule that loads a map when activated. -/// -[RegisterComponent] -public sealed partial class LoadMapRuleComponent : Component -{ - [DataField] - public MapId? Map; - - [DataField] - public ProtoId? GameMap ; - - [DataField] - public ResPath? MapPath; - - [DataField] - public List MapGrids = new(); - - [DataField] - public EntityWhitelist? SpawnerWhitelist; -} diff --git a/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs index fa352eb320..e6966c1e37 100644 --- a/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs @@ -8,7 +8,7 @@ namespace Content.Server.GameTicking.Rules.Components; /// /// Stores some configuration used by the ninja system. -/// Objectives and roundend summary are handled by . +/// Objectives and roundend summary are handled by . /// [RegisterComponent, Access(typeof(SpaceNinjaSystem))] public sealed partial class NinjaRuleComponent : Component diff --git a/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs b/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs index bb1b7c8746..e02d90c18b 100644 --- a/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs @@ -1,3 +1,6 @@ +using Content.Shared.Roles; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + namespace Content.Server.GameTicking.Rules.Components; /// @@ -6,5 +9,11 @@ namespace Content.Server.GameTicking.Rules.Components; /// TODO: Remove once systems can request spawns from the ghost role system directly. /// [RegisterComponent] -public sealed partial class NukeOperativeSpawnerComponent : Component; +public sealed partial class NukeOperativeSpawnerComponent : Component +{ + [DataField("name", required:true)] + public string OperativeName = default!; + [DataField] + public NukeopSpawnPreset SpawnDetails = default!; +} diff --git a/Content.Server/GameTicking/Rules/Components/NukeOpsShuttleComponent.cs b/Content.Server/GameTicking/Rules/Components/NukeOpsShuttleComponent.cs index 3d097cd7c7..358b157cdf 100644 --- a/Content.Server/GameTicking/Rules/Components/NukeOpsShuttleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NukeOpsShuttleComponent.cs @@ -6,6 +6,4 @@ [RegisterComponent] public sealed partial class NukeOpsShuttleComponent : Component { - [DataField] - public EntityUid AssociatedRule; } diff --git a/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs index f64947e286..8efd61b469 100644 --- a/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs @@ -1,9 +1,10 @@ using Content.Server.Maps; using Content.Server.NPC.Components; using Content.Server.RoundEnd; +using Content.Server.StationEvents.Events; using Content.Shared.Dataset; using Content.Shared.Roles; -using Robust.Shared.Audio; +using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; @@ -13,9 +14,18 @@ namespace Content.Server.GameTicking.Rules.Components; -[RegisterComponent, Access(typeof(NukeopsRuleSystem))] +[RegisterComponent, Access(typeof(NukeopsRuleSystem), typeof(LoneOpsSpawnRule))] public sealed partial class NukeopsRuleComponent : Component { + /// + /// This INCLUDES the operatives. So a value of 3 is satisfied by 2 players & 1 operative + /// + [DataField] + public int PlayersPerOperative = 10; + + [DataField] + public int MaxOps = 5; + /// /// What will happen if all of the nuclear operatives will die. Used by LoneOpsSpawn event. /// @@ -46,6 +56,12 @@ public sealed partial class NukeopsRuleComponent : Component [DataField] public TimeSpan EvacShuttleTime = TimeSpan.FromMinutes(3); + /// + /// Whether or not to spawn the nuclear operative outpost. Used by LoneOpsSpawn event. + /// + [DataField] + public bool SpawnOutpost = true; + /// /// Whether or not nukie left their outpost /// @@ -68,7 +84,7 @@ public sealed partial class NukeopsRuleComponent : Component /// This amount of TC will be given to each nukie /// [DataField] - public int WarTcAmountPerNukie = 40; + public int WarTCAmountPerNukie = 40; /// /// Delay between war declaration and nuke ops arrival on station map. Gives crew time to prepare @@ -83,22 +99,48 @@ public sealed partial class NukeopsRuleComponent : Component public int WarDeclarationMinOps = 4; [DataField] - public WinType WinType = WinType.Neutral; + public EntProtoId SpawnPointProto = "SpawnPointNukies"; [DataField] - public List WinConditions = new (); + public EntProtoId GhostSpawnPointProto = "SpawnPointGhostNukeOperative"; [DataField] - public EntityUid? TargetStation; + public string OperationName = "Test Operation"; [DataField] - public ProtoId Faction = "Syndicate"; + public ProtoId OutpostMapPrototype = "NukieOutpost"; + + [DataField] + public WinType WinType = WinType.Neutral; + + [DataField] + public List WinConditions = new (); + + public MapId? NukiePlanet; + + // TODO: use components, don't just cache entity UIDs + // There have been (and probably still are) bugs where these refer to deleted entities from old rounds. + public EntityUid? NukieOutpost; + public EntityUid? NukieShuttle; + public EntityUid? TargetStation; /// - /// Path to antagonist alert sound. + /// Data to be used in for an operative once the Mind has been added. /// [DataField] - public SoundSpecifier GreetSoundNotification = new SoundPathSpecifier("/Audio/Ambience/Antag/nukeops_start.ogg"); + public Dictionary OperativeMindPendingData = new(); + + [DataField(required: true)] + public ProtoId Faction = default!; + + [DataField] + public NukeopSpawnPreset CommanderSpawnDetails = new() { AntagRoleProto = "NukeopsCommander", GearProto = "SyndicateCommanderGearFull", NamePrefix = "nukeops-role-commander", NameList = "SyndicateNamesElite" }; + + [DataField] + public NukeopSpawnPreset AgentSpawnDetails = new() { AntagRoleProto = "NukeopsMedic", GearProto = "SyndicateOperativeMedicFull", NamePrefix = "nukeops-role-agent", NameList = "SyndicateNamesNormal" }; + + [DataField] + public NukeopSpawnPreset OperativeSpawnDetails = new(); } /// diff --git a/Content.Server/GameTicking/Rules/Components/PiratesRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/PiratesRuleComponent.cs new file mode 100644 index 0000000000..1d03b41d77 --- /dev/null +++ b/Content.Server/GameTicking/Rules/Components/PiratesRuleComponent.cs @@ -0,0 +1,24 @@ +using Robust.Shared.Audio; + +namespace Content.Server.GameTicking.Rules.Components; + +[RegisterComponent, Access(typeof(PiratesRuleSystem))] +public sealed partial class PiratesRuleComponent : Component +{ + [ViewVariables] + public List Pirates = new(); + [ViewVariables] + public EntityUid PirateShip = EntityUid.Invalid; + [ViewVariables] + public HashSet InitialItems = new(); + [ViewVariables] + public double InitialShipValue; + + /// + /// Path to antagonist alert sound. + /// + [DataField("pirateAlertSound")] + public SoundSpecifier PirateAlertSound = new SoundPathSpecifier( + "/Audio/Ambience/Antag/pirate_start.ogg", + AudioParams.Default.WithVolume(4)); +} diff --git a/Content.Server/GameTicking/Rules/Components/RevolutionaryRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/RevolutionaryRuleComponent.cs index 3b19bbffb6..2ce3f1f9a6 100644 --- a/Content.Server/GameTicking/Rules/Components/RevolutionaryRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/RevolutionaryRuleComponent.cs @@ -22,6 +22,43 @@ public sealed partial class RevolutionaryRuleComponent : Component [DataField] public TimeSpan TimerWait = TimeSpan.FromSeconds(20); + /// + /// Stores players minds + /// + [DataField] + public Dictionary HeadRevs = new(); + + [DataField] + public ProtoId HeadRevPrototypeId = "HeadRev"; + + /// + /// Min players needed for Revolutionary gamemode to start. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int MinPlayers = 15; + + /// + /// Max Head Revs allowed during selection. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int MaxHeadRevs = 3; + + /// + /// The amount of Head Revs that will spawn per this amount of players. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int PlayersPerHeadRev = 15; + + /// + /// The gear head revolutionaries are given on spawn. + /// + [DataField] + public List StartingGear = new() + { + "Flash", + "ClothingEyesGlassesSunglasses" + }; + /// /// The time it takes after the last head is killed for the shuttle to arrive. /// diff --git a/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs index 01a078625a..9dfd6e6627 100644 --- a/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs @@ -1,11 +1,12 @@ using Content.Shared.Random; +using Content.Shared.Roles; using Robust.Shared.Audio; using Robust.Shared.Prototypes; namespace Content.Server.GameTicking.Rules.Components; /// -/// Stores data for . +/// Stores data for . /// [RegisterComponent, Access(typeof(ThiefRuleSystem))] public sealed partial class ThiefRuleComponent : Component @@ -22,9 +23,42 @@ public sealed partial class ThiefRuleComponent : Component [DataField] public float BigObjectiveChance = 0.7f; + /// + /// Add a Pacified comp to thieves + /// + [DataField] + public bool PacifistThieves = true; + + [DataField] + public ProtoId ThiefPrototypeId = "Thief"; + [DataField] public float MaxObjectiveDifficulty = 2.5f; [DataField] public int MaxStealObjectives = 10; + + /// + /// Things that will be given to thieves + /// + [DataField] + public List StarterItems = new() { "ToolboxThief", "ClothingHandsChameleonThief" }; + + /// + /// All Thieves created by this rule + /// + [DataField] + public List ThievesMinds = new(); + + /// + /// Max Thiefs created by rule on roundstart + /// + [DataField] + public int MaxAllowThief = 3; + + /// + /// Sound played when making the player a thief via antag control or ghost role + /// + [DataField] + public SoundSpecifier? GreetingSound = new SoundPathSpecifier("/Audio/Misc/thief_greeting.ogg"); } diff --git a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs index dd359969b6..62619db76a 100644 --- a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs @@ -57,19 +57,4 @@ public enum SelectionState /// [DataField] public SoundSpecifier GreetSoundNotification = new SoundPathSpecifier("/Audio/Ambience/Antag/traitor_start.ogg"); - - /// - /// The amount of codewords that are selected. - /// - [DataField] - public int CodewordCount = 4; - - /// - /// The amount of TC traitors start with. - /// - [DataField] - public int StartingBalance = 20; - - [DataField] - public int MaxDifficulty = 5; } diff --git a/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs index 59d1940eaf..4fe91e3a5f 100644 --- a/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs @@ -8,6 +8,12 @@ namespace Content.Server.GameTicking.Rules.Components; [RegisterComponent, Access(typeof(ZombieRuleSystem))] public sealed partial class ZombieRuleComponent : Component { + [DataField] + public Dictionary InitialInfectedNames = new(); + + [DataField] + public ProtoId PatientZeroPrototypeId = "InitialInfected"; + /// /// When the round will next check for round end. /// @@ -20,9 +26,61 @@ public sealed partial class ZombieRuleComponent : Component [DataField] public TimeSpan EndCheckDelay = TimeSpan.FromSeconds(30); + /// + /// The time at which the initial infected will be chosen. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan? StartTime; + + /// + /// The minimum amount of time after the round starts that the initial infected will be chosen. + /// + [DataField] + public TimeSpan MinStartDelay = TimeSpan.FromMinutes(10); + + /// + /// The maximum amount of time after the round starts that the initial infected will be chosen. + /// + [DataField] + public TimeSpan MaxStartDelay = TimeSpan.FromMinutes(15); + + /// + /// The sound that plays when someone becomes an initial infected. + /// todo: this should have a unique sound instead of reusing the zombie one. + /// + [DataField] + public SoundSpecifier InitialInfectedSound = new SoundPathSpecifier("/Audio/Ambience/Antag/zombie_start.ogg"); + + /// + /// The minimum amount of time initial infected have before they start taking infection damage. + /// + [DataField] + public TimeSpan MinInitialInfectedGrace = TimeSpan.FromMinutes(12.5f); + + /// + /// The maximum amount of time initial infected have before they start taking damage. + /// + [DataField] + public TimeSpan MaxInitialInfectedGrace = TimeSpan.FromMinutes(15f); + + /// + /// How many players for each initial infected. + /// + [DataField] + public int PlayersPerInfected = 10; + + /// + /// The maximum number of initial infected. + /// + [DataField] + public int MaxInitialInfected = 6; + /// /// After this amount of the crew become zombies, the shuttle will be automatically called. /// [DataField] public float ZombieShuttleCallPercentage = 0.7f; + + [DataField] + public EntProtoId ZombifySelfActionPrototype = "ActionTurnUndead"; } diff --git a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs index 78b8a8a85c..82ac755592 100644 --- a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs @@ -1,6 +1,5 @@ using System.Linq; using Content.Server.Administration.Commands; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.KillTracking; using Content.Server.Mind; @@ -34,6 +33,7 @@ public override void Initialize() SubscribeLocalEvent(OnSpawnComplete); SubscribeLocalEvent(OnKillReported); SubscribeLocalEvent(OnPointChanged); + SubscribeLocalEvent(OnRoundEndTextAppend); } private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev) @@ -113,17 +113,21 @@ private void OnPointChanged(EntityUid uid, DeathMatchRuleComponent component, re _roundEnd.EndRound(component.RestartDelay); } - protected override void AppendRoundEndText(EntityUid uid, DeathMatchRuleComponent component, GameRuleComponent gameRule, ref RoundEndTextAppendEvent args) + private void OnRoundEndTextAppend(RoundEndTextAppendEvent ev) { - if (!TryComp(uid, out var point)) - return; - - if (component.Victor != null && _player.TryGetPlayerData(component.Victor.Value, out var data)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var dm, out var point, out var rule)) { - args.AddLine(Loc.GetString("point-scoreboard-winner", ("player", data.UserName))); - args.AddLine(""); + if (!GameTicker.IsGameRuleAdded(uid, rule)) + continue; + + if (dm.Victor != null && _player.TryGetPlayerData(dm.Victor.Value, out var data)) + { + ev.AddLine(Loc.GetString("point-scoreboard-winner", ("player", data.UserName))); + ev.AddLine(""); + } + ev.AddLine(Loc.GetString("point-scoreboard-header")); + ev.AddLine(new FormattedMessage(point.Scoreboard).ToMarkup()); } - args.AddLine(Loc.GetString("point-scoreboard-header")); - args.AddLine(new FormattedMessage(point.Scoreboard).ToMarkup()); } } diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs index 27a9edbad7..a60a2bfe22 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Robust.Shared.Collections; @@ -16,12 +15,29 @@ protected EntityQueryEnumerator Q return EntityQueryEnumerator(); } - /// - /// Queries all gamerules, regardless of if they're active or not. - /// - protected EntityQueryEnumerator QueryAllRules() + protected bool TryRoundStartAttempt(RoundStartAttemptEvent ev, string localizedPresetName) { - return EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out _, out _, out var gameRule)) + { + var minPlayers = gameRule.MinPlayers; + if (!ev.Forced && ev.Players.Length < minPlayers) + { + ChatManager.SendAdminAnnouncement(Loc.GetString("preset-not-enough-ready-players", + ("readyPlayersCount", ev.Players.Length), ("minimumPlayers", minPlayers), + ("presetName", localizedPresetName))); + ev.Cancel(); + continue; + } + + if (ev.Players.Length == 0) + { + ChatManager.DispatchServerAnnouncement(Loc.GetString("preset-no-one-ready")); + ev.Cancel(); + } + } + + return !ev.Cancelled; } /// diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.cs index c167ae7b6c..363c2ad7f7 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.cs @@ -1,6 +1,6 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; +using Content.Server.GameTicking.Rules.Components; using Robust.Server.GameObjects; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -22,31 +22,9 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnStartAttempt); SubscribeLocalEvent(OnGameRuleAdded); SubscribeLocalEvent(OnGameRuleStarted); SubscribeLocalEvent(OnGameRuleEnded); - SubscribeLocalEvent(OnRoundEndTextAppend); - } - - private void OnStartAttempt(RoundStartAttemptEvent args) - { - if (args.Forced || args.Cancelled) - return; - - var query = QueryAllRules(); - while (query.MoveNext(out var uid, out _, out var gameRule)) - { - var minPlayers = gameRule.MinPlayers; - if (args.Players.Length >= minPlayers) - continue; - - ChatManager.SendAdminAnnouncement(Loc.GetString("preset-not-enough-ready-players", - ("readyPlayersCount", args.Players.Length), - ("minimumPlayers", minPlayers), - ("presetName", ToPrettyString(uid)))); - args.Cancel(); - } } private void OnGameRuleAdded(EntityUid uid, T component, ref GameRuleAddedEvent args) @@ -70,12 +48,6 @@ private void OnGameRuleEnded(EntityUid uid, T component, ref GameRuleEndedEvent Ended(uid, component, ruleData, args); } - private void OnRoundEndTextAppend(Entity ent, ref RoundEndTextAppendEvent args) - { - if (!TryComp(ent, out var ruleData)) - return; - AppendRoundEndText(ent, ent, ruleData, ref args); - } /// /// Called when the gamerule is added @@ -101,14 +73,6 @@ protected virtual void Ended(EntityUid uid, T component, GameRuleComponent gameR } - /// - /// Called at the end of a round when text needs to be added for a game rule. - /// - protected virtual void AppendRoundEndText(EntityUid uid, T component, GameRuleComponent gameRule, ref RoundEndTextAppendEvent args) - { - - } - /// /// Called on an active gamerule entity in the Update function /// diff --git a/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs b/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs index 01fa387595..b775b7af56 100644 --- a/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs @@ -1,6 +1,5 @@ using System.Threading; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Robust.Server.Player; using Robust.Shared.Player; diff --git a/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs b/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs index 3da55e30c9..01fd97d9a7 100644 --- a/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.KillTracking; using Content.Shared.Chat; diff --git a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs deleted file mode 100644 index aba9ed9e58..0000000000 --- a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Content.Server.Antag; -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.Spawners.Components; -using Robust.Server.GameObjects; -using Robust.Server.Maps; -using Robust.Shared.Prototypes; - -namespace Content.Server.GameTicking.Rules; - -public sealed class LoadMapRuleSystem : GameRuleSystem -{ - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly MapSystem _map = default!; - [Dependency] private readonly MapLoaderSystem _mapLoader = default!; - [Dependency] private readonly TransformSystem _transform = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnSelectLocation); - SubscribeLocalEvent(OnGridSplit); - } - - private void OnGridSplit(ref GridSplitEvent args) - { - var rule = QueryActiveRules(); - while (rule.MoveNext(out _, out var mapComp, out _)) - { - if (!mapComp.MapGrids.Contains(args.Grid)) - continue; - - mapComp.MapGrids.AddRange(args.NewGrids); - break; - } - } - - protected override void Added(EntityUid uid, LoadMapRuleComponent comp, GameRuleComponent rule, GameRuleAddedEvent args) - { - if (comp.Map != null) - return; - - _map.CreateMap(out var mapId); - comp.Map = mapId; - - if (comp.GameMap != null) - { - var gameMap = _prototypeManager.Index(comp.GameMap.Value); - comp.MapGrids.AddRange(GameTicker.LoadGameMap(gameMap, comp.Map.Value, new MapLoadOptions())); - } - else if (comp.MapPath != null) - { - if (_mapLoader.TryLoad(comp.Map.Value, comp.MapPath.Value.ToString(), out var roots, new MapLoadOptions { LoadMap = true })) - comp.MapGrids.AddRange(roots); - } - else - { - Log.Error($"No valid map prototype or map path associated with the rule {ToPrettyString(uid)}"); - } - } - - private void OnSelectLocation(Entity ent, ref AntagSelectLocationEvent args) - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var xform)) - { - if (xform.MapID != ent.Comp.Map) - continue; - - if (xform.GridUid == null || !ent.Comp.MapGrids.Contains(xform.GridUid.Value)) - continue; - - if (ent.Comp.SpawnerWhitelist != null && !ent.Comp.SpawnerWhitelist.IsValid(uid, EntityManager)) - continue; - - args.Coordinates.Add(_transform.GetMapCoordinates(xform)); - } - } -} diff --git a/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs b/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs index ee3a025533..e792a004df 100644 --- a/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs @@ -1,6 +1,5 @@ using System.Threading; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Timer = Robust.Shared.Timing.Timer; diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index d06b9fb899..46040e2945 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -1,51 +1,77 @@ +using Content.Server.Administration.Commands; +using Content.Server.Administration.Managers; using Content.Server.Antag; using Content.Server.Communications; using Content.Server.GameTicking.Rules.Components; +using Content.Server.Ghost.Roles.Components; +using Content.Server.Ghost.Roles.Events; using Content.Server.Humanoid; +using Content.Server.Mind; +using Content.Server.NPC.Components; +using Content.Server.NPC.Systems; using Content.Server.Nuke; using Content.Server.NukeOps; using Content.Server.Popups; using Content.Server.Preferences.Managers; +using Content.Server.RandomMetadata; using Content.Server.Roles; using Content.Server.RoundEnd; using Content.Server.Shuttles.Events; using Content.Server.Shuttles.Systems; +using Content.Server.Spawners.Components; using Content.Server.Station.Components; +using Content.Server.Station.Systems; using Content.Server.Store.Components; using Content.Server.Store.Systems; +using Content.Shared.CCVar; +using Content.Shared.Dataset; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Mind.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Nuke; using Content.Shared.NukeOps; using Content.Shared.Preferences; +using Content.Shared.Roles; using Content.Shared.Store; using Content.Shared.Tag; using Content.Shared.Zombies; +using Robust.Server.Player; +using Robust.Shared.Configuration; using Robust.Shared.Map; +using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; using System.Linq; -using Content.Server.GameTicking.Components; -using Content.Server.NPC.Components; -using Content.Server.NPC.Systems; namespace Content.Server.GameTicking.Rules; public sealed class NukeopsRuleSystem : GameRuleSystem { + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IServerPreferencesManager _prefs = default!; + [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly EmergencyShuttleSystem _emergency = default!; [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly RandomMetadataSystem _randomMetadata = default!; + [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; - [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; + [Dependency] private readonly SharedRoleSystem _roles = default!; + [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; [Dependency] private readonly StoreSystem _store = default!; [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; + + private ISawmill _sawmill = default!; [ValidatePrototypeId] private const string TelecrystalCurrencyPrototype = "Telecrystal"; @@ -53,67 +79,141 @@ public sealed class NukeopsRuleSystem : GameRuleSystem [ValidatePrototypeId] private const string NukeOpsUplinkTagPrototype = "NukeOpsUplink"; + [ValidatePrototypeId] + public const string NukeopsId = "Nukeops"; + + [ValidatePrototypeId] + private const string OperationPrefixDataset = "operationPrefix"; + + [ValidatePrototypeId] + private const string OperationSuffixDataset = "operationSuffix"; + public override void Initialize() { base.Initialize(); + _sawmill = _logManager.GetSawmill("NukeOps"); + + SubscribeLocalEvent(OnStartAttempt); + SubscribeLocalEvent(OnPlayersSpawning); + SubscribeLocalEvent(OnRoundEndText); SubscribeLocalEvent(OnNukeExploded); SubscribeLocalEvent(OnRunLevelChanged); SubscribeLocalEvent(OnNukeDisarm); SubscribeLocalEvent(OnComponentRemove); SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnPlayersGhostSpawning); + SubscribeLocalEvent(OnMindAdded); SubscribeLocalEvent(OnOperativeZombified); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnShuttleFTLAttempt); SubscribeLocalEvent(OnWarDeclared); SubscribeLocalEvent(OnShuttleCallAttempt); - - SubscribeLocalEvent(OnAntagSelectEntity); - SubscribeLocalEvent(OnAfterAntagEntSelected); } protected override void Started(EntityUid uid, NukeopsRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { - var eligible = new List>(); - var eligibleQuery = EntityQueryEnumerator(); - while (eligibleQuery.MoveNext(out var eligibleUid, out var eligibleComp, out var member)) + base.Started(uid, component, gameRule, args); + + if (GameTicker.RunLevel == GameRunLevel.InRound) + SpawnOperativesForGhostRoles(uid, component); + } + + #region Event Handlers + + private void OnStartAttempt(RoundStartAttemptEvent ev) + { + TryRoundStartAttempt(ev, Loc.GetString("nukeops-title")); + } + + private void OnPlayersSpawning(RulePlayerSpawningEvent ev) + { + var query = QueryActiveRules(); + while (query.MoveNext(out var uid, out _, out var nukeops, out _)) { - if (!_npcFaction.IsFactionHostile(component.Faction, eligibleUid, member)) + if (!SpawnMap((uid, nukeops))) + { + _sawmill.Info("Failed to load map for nukeops"); continue; + } - eligible.Add((eligibleUid, eligibleComp, member)); - } + //Handle there being nobody readied up + if (ev.PlayerPool.Count == 0) + continue; - if (eligible.Count == 0) - return; + var commanderEligible = _antagSelection.GetEligibleSessions(ev.PlayerPool, nukeops.CommanderSpawnDetails.AntagRoleProto); + var agentEligible = _antagSelection.GetEligibleSessions(ev.PlayerPool, nukeops.AgentSpawnDetails.AntagRoleProto); + var operativeEligible = _antagSelection.GetEligibleSessions(ev.PlayerPool, nukeops.OperativeSpawnDetails.AntagRoleProto); + //Calculate how large the nukeops team needs to be + var nukiesToSelect = _antagSelection.CalculateAntagCount(_playerManager.PlayerCount, nukeops.PlayersPerOperative, nukeops.MaxOps); + + //Select Nukies + //Select Commander, priority : commanderEligible, agentEligible, operativeEligible, all players + var selectedCommander = _antagSelection.ChooseAntags(1, commanderEligible, agentEligible, operativeEligible, ev.PlayerPool).FirstOrDefault(); + //Select Agent, priority : agentEligible, operativeEligible, all players + var selectedAgent = _antagSelection.ChooseAntags(1, agentEligible, operativeEligible, ev.PlayerPool).FirstOrDefault(); + //Select Operatives, priority : operativeEligible, all players + var selectedOperatives = _antagSelection.ChooseAntags(nukiesToSelect - 2, operativeEligible, ev.PlayerPool); + + //Create the team! + //If the session is null, they will be spawned as ghost roles (provided the cvar is set) + var operatives = new List { new NukieSpawn(selectedCommander, nukeops.CommanderSpawnDetails) }; + if (nukiesToSelect > 1) + operatives.Add(new NukieSpawn(selectedAgent, nukeops.AgentSpawnDetails)); + + for (var i = 0; i < nukiesToSelect - 2; i++) + { + //Use up all available sessions first, then spawn the rest as ghost roles (if enabled) + if (selectedOperatives.Count > i) + { + operatives.Add(new NukieSpawn(selectedOperatives[i], nukeops.OperativeSpawnDetails)); + } + else + { + operatives.Add(new NukieSpawn(null, nukeops.OperativeSpawnDetails)); + } + } - component.TargetStation = RobustRandom.Pick(eligible); + SpawnOperatives(operatives, _cfg.GetCVar(CCVars.NukeopsSpawnGhostRoles), nukeops); + + foreach (var nukieSpawn in operatives) + { + if (nukieSpawn.Session == null) + continue; + + GameTicker.PlayerJoinGame(nukieSpawn.Session); + } + } } - #region Event Handlers - protected override void AppendRoundEndText(EntityUid uid, NukeopsRuleComponent component, GameRuleComponent gameRule, - ref RoundEndTextAppendEvent args) + private void OnRoundEndText(RoundEndTextAppendEvent ev) { - var winText = Loc.GetString($"nukeops-{component.WinType.ToString().ToLower()}"); - args.AddLine(winText); - - foreach (var cond in component.WinConditions) + var ruleQuery = QueryActiveRules(); + while (ruleQuery.MoveNext(out _, out _, out var nukeops, out _)) { - var text = Loc.GetString($"nukeops-cond-{cond.ToString().ToLower()}"); - args.AddLine(text); - } + var winText = Loc.GetString($"nukeops-{nukeops.WinType.ToString().ToLower()}"); + ev.AddLine(winText); - args.AddLine(Loc.GetString("nukeops-list-start")); + foreach (var cond in nukeops.WinConditions) + { + var text = Loc.GetString($"nukeops-cond-{cond.ToString().ToLower()}"); + ev.AddLine(text); + } + } - var antags =_antag.GetAntagIdentifiers(uid); + ev.AddLine(Loc.GetString("nukeops-list-start")); - foreach (var (_, sessionData, name) in antags) + var nukiesQuery = EntityQueryEnumerator(); + while (nukiesQuery.MoveNext(out var nukeopsUid, out _, out var mindContainer)) { - args.AddLine(Loc.GetString("nukeops-list-name-user", ("name", name), ("user", sessionData.UserName))); + if (!_mind.TryGetMind(nukeopsUid, out _, out var mind, mindContainer)) + continue; + + ev.AddLine(mind.Session != null + ? Loc.GetString("nukeops-list-name-user", ("name", Name(nukeopsUid)), ("user", mind.Session.Name)) + : Loc.GetString("nukeops-list-name", ("name", Name(nukeopsUid)))); } } @@ -124,10 +224,10 @@ private void OnNukeExploded(NukeExplodedEvent ev) { if (ev.OwningStation != null) { - if (ev.OwningStation == GetOutpost(uid)) + if (ev.OwningStation == nukeops.NukieOutpost) { nukeops.WinConditions.Add(WinCondition.NukeExplodedOnNukieOutpost); - SetWinType((uid, nukeops), WinType.CrewMajor); + SetWinType(uid, WinType.CrewMajor, nukeops); continue; } @@ -142,7 +242,7 @@ private void OnNukeExploded(NukeExplodedEvent ev) } nukeops.WinConditions.Add(WinCondition.NukeExplodedOnCorrectStation); - SetWinType((uid, nukeops), WinType.OpsMajor); + SetWinType(uid, WinType.OpsMajor, nukeops); correctStation = true; } @@ -163,85 +263,19 @@ private void OnNukeExploded(NukeExplodedEvent ev) private void OnRunLevelChanged(GameRunLevelChangedEvent ev) { - if (ev.New is not GameRunLevel.PostRound) - return; - var query = QueryActiveRules(); while (query.MoveNext(out var uid, out _, out var nukeops, out _)) { - OnRoundEnd((uid, nukeops)); - } - } - - private void OnRoundEnd(Entity ent) - { - // If the win condition was set to operative/crew major win, ignore. - if (ent.Comp.WinType == WinType.OpsMajor || ent.Comp.WinType == WinType.CrewMajor) - return; - - var nukeQuery = AllEntityQuery(); - var centcomms = _emergency.GetCentcommMaps(); - - while (nukeQuery.MoveNext(out var nuke, out var nukeTransform)) - { - if (nuke.Status != NukeStatus.ARMED) - continue; - - // UH OH - if (nukeTransform.MapUid != null && centcomms.Contains(nukeTransform.MapUid.Value)) + switch (ev.New) { - ent.Comp.WinConditions.Add(WinCondition.NukeActiveAtCentCom); - SetWinType((ent, ent), WinType.OpsMajor); - return; + case GameRunLevel.InRound: + OnRoundStart(uid, nukeops); + break; + case GameRunLevel.PostRound: + OnRoundEnd(uid, nukeops); + break; } - - if (nukeTransform.GridUid == null || ent.Comp.TargetStation == null) - continue; - - if (!TryComp(ent.Comp.TargetStation.Value, out StationDataComponent? data)) - continue; - - foreach (var grid in data.Grids) - { - if (grid != nukeTransform.GridUid) - continue; - - ent.Comp.WinConditions.Add(WinCondition.NukeActiveInStation); - SetWinType(ent, WinType.OpsMajor); - return; - } - } - - if (_antag.AllAntagsAlive(ent.Owner)) - { - SetWinType(ent, WinType.OpsMinor); - ent.Comp.WinConditions.Add(WinCondition.AllNukiesAlive); - return; } - - ent.Comp.WinConditions.Add(_antag.AnyAliveAntags(ent.Owner) - ? WinCondition.SomeNukiesAlive - : WinCondition.AllNukiesDead); - - var diskAtCentCom = false; - var diskQuery = AllEntityQuery(); - while (diskQuery.MoveNext(out _, out var transform)) - { - diskAtCentCom = transform.MapUid != null && centcomms.Contains(transform.MapUid.Value); - - // TODO: The target station should be stored, and the nuke disk should store its original station. - // This is fine for now, because we can assume a single station in base SS14. - break; - } - - // If the disk is currently at Central Command, the crew wins - just slightly. - // This also implies that some nuclear operatives have died. - SetWinType(ent, diskAtCentCom - ? WinType.CrewMinor - : WinType.OpsMinor); - ent.Comp.WinConditions.Add(diskAtCentCom - ? WinCondition.NukeDiskOnCentCom - : WinCondition.NukeDiskNotOnCentCom); } private void OnNukeDisarm(NukeDisarmSuccessEvent ev) @@ -260,31 +294,66 @@ private void OnMobStateChanged(EntityUid uid, NukeOperativeComponent component, CheckRoundShouldEnd(); } - private void OnOperativeZombified(EntityUid uid, NukeOperativeComponent component, ref EntityZombifiedEvent args) + private void OnPlayersGhostSpawning(EntityUid uid, NukeOperativeComponent component, GhostRoleSpawnerUsedEvent args) { - RemCompDeferred(uid, component); + var spawner = args.Spawner; + + if (!TryComp(spawner, out var nukeOpSpawner)) + return; + + HumanoidCharacterProfile? profile = null; + if (TryComp(args.Spawned, out ActorComponent? actor)) + profile = _prefs.GetPreferences(actor.PlayerSession.UserId).SelectedCharacter as HumanoidCharacterProfile; + + // TODO: this is kinda awful for multi-nukies + foreach (var nukeops in EntityQuery()) + { + SetupOperativeEntity(uid, nukeOpSpawner.OperativeName, nukeOpSpawner.SpawnDetails, profile); + + nukeops.OperativeMindPendingData.Add(uid, nukeOpSpawner.SpawnDetails.AntagRoleProto); + } } - private void OnMapInit(Entity ent, ref MapInitEvent args) + private void OnMindAdded(EntityUid uid, NukeOperativeComponent component, MindAddedMessage args) { - var map = Transform(ent).MapID; + if (!_mind.TryGetMind(uid, out var mindId, out var mind)) + return; - var rules = EntityQueryEnumerator(); - while (rules.MoveNext(out var uid, out _, out var mapRule)) + var query = QueryActiveRules(); + while (query.MoveNext(out _, out _, out var nukeops, out _)) { - if (map != mapRule.Map) - continue; - ent.Comp.AssociatedRule = uid; - break; + if (nukeops.OperativeMindPendingData.TryGetValue(uid, out var role) || !nukeops.SpawnOutpost || + nukeops.RoundEndBehavior == RoundEndBehavior.Nothing) + { + role ??= nukeops.OperativeSpawnDetails.AntagRoleProto; + _roles.MindAddRole(mindId, new NukeopsRoleComponent { PrototypeId = role }); + nukeops.OperativeMindPendingData.Remove(uid); + } + + if (mind.Session is not { } playerSession) + return; + + if (GameTicker.RunLevel != GameRunLevel.InRound) + return; + + if (nukeops.TargetStation != null && !string.IsNullOrEmpty(Name(nukeops.TargetStation.Value))) + { + NotifyNukie(playerSession, component, nukeops); + } } } + private void OnOperativeZombified(EntityUid uid, NukeOperativeComponent component, ref EntityZombifiedEvent args) + { + RemCompDeferred(uid, component); + } + private void OnShuttleFTLAttempt(ref ConsoleFTLAttemptEvent ev) { var query = QueryActiveRules(); - while (query.MoveNext(out var uid, out _, out var nukeops, out _)) + while (query.MoveNext(out _, out _, out var nukeops, out _)) { - if (ev.Uid != GetShuttle((uid, nukeops))) + if (ev.Uid != nukeops.NukieShuttle) continue; if (nukeops.WarDeclaredTime != null) @@ -328,12 +397,12 @@ private void OnWarDeclared(ref WarDeclaredEvent ev) { // TODO: this is VERY awful for multi-nukies var query = QueryActiveRules(); - while (query.MoveNext(out var uid, out _, out var nukeops, out _)) + while (query.MoveNext(out _, out _, out var nukeops, out _)) { if (nukeops.WarDeclaredTime != null) continue; - if (TryComp(uid, out var mapComp) && Transform(ev.DeclaratorEntity).MapID != mapComp.Map) + if (Transform(ev.DeclaratorEntity).MapID != nukeops.NukiePlanet) continue; var newStatus = GetWarCondition(nukeops, ev.Status); @@ -344,7 +413,7 @@ private void OnWarDeclared(ref WarDeclaredEvent ev) var timeRemain = nukeops.WarNukieArriveDelay + Timing.CurTime; ev.DeclaratorEntity.Comp.ShuttleDisabledTime = timeRemain; - DistributeExtraTc((uid, nukeops)); + DistributeExtraTc(nukeops); } } } @@ -371,7 +440,7 @@ public WarConditionStatus GetWarCondition(NukeopsRuleComponent nukieRule, WarCon return WarConditionStatus.YesWar; } - private void DistributeExtraTc(Entity nukieRule) + private void DistributeExtraTc(NukeopsRuleComponent nukieRule) { var enumerator = EntityQueryEnumerator(); while (enumerator.MoveNext(out var uid, out var component)) @@ -379,22 +448,161 @@ private void DistributeExtraTc(Entity nukieRule) if (!_tag.HasTag(uid, NukeOpsUplinkTagPrototype)) continue; - if (GetOutpost(nukieRule.Owner) is not { } outpost) + if (!nukieRule.NukieOutpost.HasValue) continue; - if (Transform(uid).MapID != Transform(outpost).MapID) // Will receive bonus TC only on their start outpost + if (Transform(uid).MapID != Transform(nukieRule.NukieOutpost.Value).MapID) // Will receive bonus TC only on their start outpost continue; - _store.TryAddCurrency(new () { { TelecrystalCurrencyPrototype, nukieRule.Comp.WarTcAmountPerNukie } }, uid, component); + _store.TryAddCurrency(new () { { TelecrystalCurrencyPrototype, nukieRule.WarTCAmountPerNukie } }, uid, component); var msg = Loc.GetString("store-currency-war-boost-given", ("target", uid)); _popupSystem.PopupEntity(msg, uid); } } - private void SetWinType(Entity ent, WinType type, bool endRound = true) + private void OnRoundStart(EntityUid uid, NukeopsRuleComponent? component = null) { - ent.Comp.WinType = type; + if (!Resolve(uid, ref component)) + return; + + // TODO: This needs to try and target a Nanotrasen station. At the very least, + // we can only currently guarantee that NT stations are the only station to + // exist in the base game. + + var eligible = new List>(); + var eligibleQuery = EntityQueryEnumerator(); + while (eligibleQuery.MoveNext(out var eligibleUid, out var eligibleComp, out var member)) + { + if (!_npcFaction.IsFactionHostile(component.Faction, eligibleUid, member)) + continue; + + eligible.Add((eligibleUid, eligibleComp, member)); + } + + if (eligible.Count == 0) + return; + + component.TargetStation = RobustRandom.Pick(eligible); + component.OperationName = _randomMetadata.GetRandomFromSegments([OperationPrefixDataset, OperationSuffixDataset], " "); + + var filter = Filter.Empty(); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out var nukeops, out var actor)) + { + NotifyNukie(actor.PlayerSession, nukeops, component); + filter.AddPlayer(actor.PlayerSession); + } + } + + private void OnRoundEnd(EntityUid uid, NukeopsRuleComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + // If the win condition was set to operative/crew major win, ignore. + if (component.WinType == WinType.OpsMajor || component.WinType == WinType.CrewMajor) + return; + + var nukeQuery = AllEntityQuery(); + var centcomms = _emergency.GetCentcommMaps(); + + while (nukeQuery.MoveNext(out var nuke, out var nukeTransform)) + { + if (nuke.Status != NukeStatus.ARMED) + continue; + + // UH OH + if (nukeTransform.MapUid != null && centcomms.Contains(nukeTransform.MapUid.Value)) + { + component.WinConditions.Add(WinCondition.NukeActiveAtCentCom); + SetWinType(uid, WinType.OpsMajor, component); + return; + } + + if (nukeTransform.GridUid == null || component.TargetStation == null) + continue; + + if (!TryComp(component.TargetStation.Value, out StationDataComponent? data)) + continue; + + foreach (var grid in data.Grids) + { + if (grid != nukeTransform.GridUid) + continue; + + component.WinConditions.Add(WinCondition.NukeActiveInStation); + SetWinType(uid, WinType.OpsMajor, component); + return; + } + } + + var allAlive = true; + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var nukeopsUid, out _, out var mindContainer, out var mobState)) + { + // mind got deleted somehow so ignore it + if (!_mind.TryGetMind(nukeopsUid, out _, out var mind, mindContainer)) + continue; + + // check if player got gibbed or ghosted or something - count as dead + if (mind.OwnedEntity != null && + // if the player somehow isn't a mob anymore that also counts as dead + // have to be alive, not crit or dead + mobState.CurrentState is MobState.Alive) + { + continue; + } + + allAlive = false; + break; + } + + // If all nuke ops were alive at the end of the round, + // the nuke ops win. This is to prevent people from + // running away the moment nuke ops appear. + if (allAlive) + { + SetWinType(uid, WinType.OpsMinor, component); + component.WinConditions.Add(WinCondition.AllNukiesAlive); + return; + } + + component.WinConditions.Add(WinCondition.SomeNukiesAlive); + + var diskAtCentCom = false; + var diskQuery = AllEntityQuery(); + + while (diskQuery.MoveNext(out _, out var transform)) + { + diskAtCentCom = transform.MapUid != null && centcomms.Contains(transform.MapUid.Value); + + // TODO: The target station should be stored, and the nuke disk should store its original station. + // This is fine for now, because we can assume a single station in base SS14. + break; + } + + // If the disk is currently at Central Command, the crew wins - just slightly. + // This also implies that some nuclear operatives have died. + if (diskAtCentCom) + { + SetWinType(uid, WinType.CrewMinor, component); + component.WinConditions.Add(WinCondition.NukeDiskOnCentCom); + } + // Otherwise, the nuke ops win. + else + { + SetWinType(uid, WinType.OpsMinor, component); + component.WinConditions.Add(WinCondition.NukeDiskNotOnCentCom); + } + } + + private void SetWinType(EntityUid uid, WinType type, NukeopsRuleComponent? component = null, bool endRound = true) + { + if (!Resolve(uid, ref component)) + return; + + component.WinType = type; if (endRound && (type == WinType.CrewMajor || type == WinType.OpsMajor)) _roundEndSystem.EndRound(); @@ -405,130 +613,243 @@ private void CheckRoundShouldEnd() var query = QueryActiveRules(); while (query.MoveNext(out var uid, out _, out var nukeops, out _)) { - CheckRoundShouldEnd((uid, nukeops)); + if (nukeops.RoundEndBehavior == RoundEndBehavior.Nothing || nukeops.WinType == WinType.CrewMajor || nukeops.WinType == WinType.OpsMajor) + continue; + + // If there are any nuclear bombs that are active, immediately return. We're not over yet. + var armed = false; + foreach (var nuke in EntityQuery()) + { + if (nuke.Status == NukeStatus.ARMED) + { + armed = true; + break; + } + } + if (armed) + continue; + + MapId? shuttleMapId = Exists(nukeops.NukieShuttle) + ? Transform(nukeops.NukieShuttle.Value).MapID + : null; + + MapId? targetStationMap = null; + if (nukeops.TargetStation != null && TryComp(nukeops.TargetStation, out StationDataComponent? data)) + { + var grid = data.Grids.FirstOrNull(); + targetStationMap = grid != null + ? Transform(grid.Value).MapID + : null; + } + + // Check if there are nuke operatives still alive on the same map as the shuttle, + // or on the same map as the station. + // If there are, the round can continue. + var operatives = EntityQuery(true); + var operativesAlive = operatives + .Where(ent => + ent.Item3.MapID == shuttleMapId + || ent.Item3.MapID == targetStationMap) + .Any(ent => ent.Item2.CurrentState == MobState.Alive && ent.Item1.Running); + + if (operativesAlive) + continue; // There are living operatives than can access the shuttle, or are still on the station's map. + + // Check that there are spawns available and that they can access the shuttle. + var spawnsAvailable = EntityQuery(true).Any(); + if (spawnsAvailable && shuttleMapId == nukeops.NukiePlanet) + continue; // Ghost spawns can still access the shuttle. Continue the round. + + // The shuttle is inaccessible to both living nuke operatives and yet to spawn nuke operatives, + // and there are no nuclear operatives on the target station's map. + nukeops.WinConditions.Add(spawnsAvailable + ? WinCondition.NukiesAbandoned + : WinCondition.AllNukiesDead); + + SetWinType(uid, WinType.CrewMajor, nukeops, false); + _roundEndSystem.DoRoundEndBehavior( + nukeops.RoundEndBehavior, nukeops.EvacShuttleTime, nukeops.RoundEndTextSender, nukeops.RoundEndTextShuttleCall, nukeops.RoundEndTextAnnouncement); + + // prevent it called multiple times + nukeops.RoundEndBehavior = RoundEndBehavior.Nothing; + } + } + + private bool SpawnMap(Entity ent) + { + if (!ent.Comp.SpawnOutpost + || ent.Comp.NukiePlanet != null) + return true; + + ent.Comp.NukiePlanet = _mapManager.CreateMap(); + var gameMap = _prototypeManager.Index(ent.Comp.OutpostMapPrototype); + ent.Comp.NukieOutpost = GameTicker.LoadGameMap(gameMap, ent.Comp.NukiePlanet.Value, null)[0]; + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var grid, out _, out var shuttleTransform)) + { + if (shuttleTransform.MapID != ent.Comp.NukiePlanet) + continue; + + ent.Comp.NukieShuttle = grid; + break; } + + return true; } - private void CheckRoundShouldEnd(Entity ent) + /// + /// Adds missing nuke operative components, equips starting gear and renames the entity. + /// + private void SetupOperativeEntity(EntityUid mob, string name, NukeopSpawnPreset spawnDetails, HumanoidCharacterProfile? profile) { - var nukeops = ent.Comp; + _metaData.SetEntityName(mob, name); + EnsureComp(mob); + + if (profile != null) + _humanoid.LoadProfile(mob, profile); + + var gear = _prototypeManager.Index(spawnDetails.GearProto); + _stationSpawning.EquipStartingGear(mob, gear, profile); - if (nukeops.RoundEndBehavior == RoundEndBehavior.Nothing || nukeops.WinType == WinType.CrewMajor || nukeops.WinType == WinType.OpsMajor) + _npcFaction.RemoveFaction(mob, "NanoTrasen", false); + _npcFaction.AddFaction(mob, "Syndicate"); + } + + private void SpawnOperatives(List sessions, bool spawnGhostRoles, NukeopsRuleComponent component) + { + if (component.NukieOutpost is not { Valid: true } outpostUid) return; + var spawns = new List(); + foreach (var (_, meta, xform) in EntityQuery(true)) + { + if (meta.EntityPrototype?.ID != component.SpawnPointProto.Id) + continue; + + if (xform.ParentUid != component.NukieOutpost) + continue; + + spawns.Add(xform.Coordinates); + break; + } - // If there are any nuclear bombs that are active, immediately return. We're not over yet. - foreach (var nuke in EntityQuery()) + //Fallback, spawn at the centre of the map + if (spawns.Count == 0) { - if (nuke.Status == NukeStatus.ARMED) - return; + spawns.Add(Transform(outpostUid).Coordinates); + _sawmill.Warning($"Fell back to default spawn for nukies!"); } - var shuttle = GetShuttle((ent, ent)); + //Spawn the team + foreach (var nukieSession in sessions) + { + var name = $"{Loc.GetString(nukieSession.Type.NamePrefix)} {RobustRandom.PickAndTake(_prototypeManager.Index(nukieSession.Type.NameList).Values.ToList())}"; - MapId? shuttleMapId = Exists(shuttle) - ? Transform(shuttle.Value).MapID - : null; + var nukeOpsAntag = _prototypeManager.Index(nukieSession.Type.AntagRoleProto); - MapId? targetStationMap = null; - if (nukeops.TargetStation != null && TryComp(nukeops.TargetStation, out StationDataComponent? data)) - { - var grid = data.Grids.FirstOrNull(); - targetStationMap = grid != null - ? Transform(grid.Value).MapID - : null; + //If a session is available, spawn mob and transfer mind into it + if (nukieSession.Session != null) + { + var profile = _prefs.GetPreferences(nukieSession.Session.UserId).SelectedCharacter as HumanoidCharacterProfile; + if (!_prototypeManager.TryIndex(profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies, out SpeciesPrototype? species)) + { + species = _prototypeManager.Index(SharedHumanoidAppearanceSystem.DefaultSpecies); + } + + var mob = Spawn(species.Prototype, RobustRandom.Pick(spawns)); + SetupOperativeEntity(mob, name, nukieSession.Type, profile); + + var newMind = _mind.CreateMind(nukieSession.Session.UserId, name); + _mind.SetUserId(newMind, nukieSession.Session.UserId); + _roles.MindAddRole(newMind, new NukeopsRoleComponent { PrototypeId = nukieSession.Type.AntagRoleProto }); + + _mind.TransferTo(newMind, mob); + } + //Otherwise, spawn as a ghost role + else if (spawnGhostRoles) + { + var spawnPoint = Spawn(component.GhostSpawnPointProto, RobustRandom.Pick(spawns)); + var ghostRole = EnsureComp(spawnPoint); + EnsureComp(spawnPoint); + ghostRole.RoleName = Loc.GetString(nukeOpsAntag.Name); + ghostRole.RoleDescription = Loc.GetString(nukeOpsAntag.Objective); + + var nukeOpSpawner = EnsureComp(spawnPoint); + nukeOpSpawner.OperativeName = name; + nukeOpSpawner.SpawnDetails = nukieSession.Type; + } } + } - // Check if there are nuke operatives still alive on the same map as the shuttle, - // or on the same map as the station. - // If there are, the round can continue. - var operatives = EntityQuery(true); - var operativesAlive = operatives - .Where(op => - op.Item3.MapID == shuttleMapId - || op.Item3.MapID == targetStationMap) - .Any(op => op.Item2.CurrentState == MobState.Alive && op.Item1.Running); - - if (operativesAlive) - return; // There are living operatives than can access the shuttle, or are still on the station's map. - - // Check that there are spawns available and that they can access the shuttle. - var spawnsAvailable = EntityQuery(true).Any(); - if (spawnsAvailable && CompOrNull(ent)?.Map == shuttleMapId) - return; // Ghost spawns can still access the shuttle. Continue the round. - - // The shuttle is inaccessible to both living nuke operatives and yet to spawn nuke operatives, - // and there are no nuclear operatives on the target station's map. - nukeops.WinConditions.Add(spawnsAvailable - ? WinCondition.NukiesAbandoned - : WinCondition.AllNukiesDead); - - SetWinType(ent, WinType.CrewMajor, false); - _roundEndSystem.DoRoundEndBehavior( - nukeops.RoundEndBehavior, nukeops.EvacShuttleTime, nukeops.RoundEndTextSender, nukeops.RoundEndTextShuttleCall, nukeops.RoundEndTextAnnouncement); - - // prevent it called multiple times - nukeops.RoundEndBehavior = RoundEndBehavior.Nothing; + /// + /// Display a greeting message and play a sound for a nukie + /// + private void NotifyNukie(ICommonSession session, NukeOperativeComponent nukeop, NukeopsRuleComponent nukeopsRule) + { + if (nukeopsRule.TargetStation is not { } station) + return; + + _antagSelection.SendBriefing(session, Loc.GetString("nukeops-welcome", ("station", station), ("name", nukeopsRule.OperationName)), Color.Red, nukeop.GreetSoundNotification); } - // this should really go anywhere else but im tired. - private void OnAntagSelectEntity(Entity ent, ref AntagSelectEntityEvent args) + /// + /// Spawn nukie ghost roles if this gamerule was started mid round + /// + private void SpawnOperativesForGhostRoles(EntityUid uid, NukeopsRuleComponent? component = null) { - if (args.Handled) + if (!Resolve(uid, ref component)) return; - var profile = args.Session != null - ? _prefs.GetPreferences(args.Session.UserId).SelectedCharacter as HumanoidCharacterProfile - : HumanoidCharacterProfile.RandomWithSpecies(); - if (!_prototypeManager.TryIndex(profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies, out SpeciesPrototype? species)) + if (!SpawnMap((uid, component))) { - species = _prototypeManager.Index(SharedHumanoidAppearanceSystem.DefaultSpecies); + _sawmill.Info("Failed to load map for nukeops"); + return; } - args.Entity = Spawn(species.Prototype); - _humanoid.LoadProfile(args.Entity.Value, profile); - } + var numNukies = _antagSelection.CalculateAntagCount(_playerManager.PlayerCount, component.PlayersPerOperative, component.MaxOps); - private void OnAfterAntagEntSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) - { - if (ent.Comp.TargetStation is not { } station) + //Dont continue if we have no nukies to spawn + if (numNukies == 0) return; - _antag.SendBriefing(args.Session, Loc.GetString("nukeops-welcome", - ("station", station), - ("name", Name(ent))), - Color.Red, - ent.Comp.GreetSoundNotification); + //Fill the ranks, commander first, then agent, then operatives + //TODO: Possible alternative team compositions? Like multiple commanders or agents + var operatives = new List(); + if (numNukies >= 1) + operatives.Add(new NukieSpawn(null, component.CommanderSpawnDetails)); + if (numNukies >= 2) + operatives.Add(new NukieSpawn(null, component.AgentSpawnDetails)); + if (numNukies >= 3) + { + for (var i = 2; i < numNukies; i++) + { + operatives.Add(new NukieSpawn(null, component.OperativeSpawnDetails)); + } + } + + SpawnOperatives(operatives, true, component); } - /// - /// Is this method the shitty glue holding together the last of my sanity? yes. - /// Do i have a better solution? not presently. - /// - private EntityUid? GetOutpost(Entity ent) + //For admins forcing someone to nukeOps. + public void MakeLoneNukie(EntityUid entity) { - if (!Resolve(ent, ref ent.Comp, false)) - return null; + if (!_mind.TryGetMind(entity, out var mindId, out var mindComponent)) + return; - return ent.Comp.MapGrids.Where(e => HasComp(e) && !HasComp(e)).FirstOrNull(); + //ok hardcoded value bad but so is everything else here + _roles.MindAddRole(mindId, new NukeopsRoleComponent { PrototypeId = NukeopsId }, mindComponent); + SetOutfitCommand.SetOutfit(entity, "SyndicateOperativeGearFull", EntityManager); } - /// - /// Is this method the shitty glue holding together the last of my sanity? yes. - /// Do i have a better solution? not presently. - /// - private EntityUid? GetShuttle(Entity ent) + private sealed class NukieSpawn { - if (!Resolve(ent, ref ent.Comp, false)) - return null; + public ICommonSession? Session { get; private set; } + public NukeopSpawnPreset Type { get; private set; } - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) + public NukieSpawn(ICommonSession? session, NukeopSpawnPreset type) { - if (comp.AssociatedRule == ent.Owner) - return uid; + Session = session; + Type = type; } - - return null; } } diff --git a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs index e69de29bb2..128f112304 100644 --- a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs @@ -0,0 +1,321 @@ +using System.Linq; +using System.Numerics; +using Content.Server.Administration.Commands; +using Content.Server.Cargo.Systems; +using Content.Server.Chat.Managers; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.NPC.Components; +using Content.Server.NPC.Systems; +using Content.Server.Preferences.Managers; +using Content.Server.Spawners.Components; +using Content.Server.Station.Components; +using Content.Server.Station.Systems; +using Content.Shared.CCVar; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Mind; +using Content.Shared.Preferences; +using Content.Shared.Roles; +using Robust.Server.GameObjects; +using Robust.Server.Maps; +using Robust.Server.Player; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; +using Robust.Shared.Enums; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Utility; + +namespace Content.Server.GameTicking.Rules; + +/// +/// This handles the Pirates minor antag, which is designed to coincide with other modes on occasion. +/// +public sealed class PiratesRuleSystem : GameRuleSystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IServerPreferencesManager _prefs = default!; + [Dependency] private readonly StationSpawningSystem _stationSpawningSystem = default!; + [Dependency] private readonly PricingSystem _pricingSystem = default!; + [Dependency] private readonly MapLoaderSystem _map = default!; + [Dependency] private readonly NamingSystem _namingSystem = default!; + [Dependency] private readonly NpcFactionSystem _npcFaction = default!; + [Dependency] private readonly SharedMindSystem _mindSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + + [ValidatePrototypeId] + private const string GameRuleId = "Pirates"; + + [ValidatePrototypeId] + private const string MobId = "MobHuman"; + + [ValidatePrototypeId] + private const string SpeciesId = "Human"; + + [ValidatePrototypeId] + private const string PirateFactionId = "Syndicate"; + + [ValidatePrototypeId] + private const string EnemyFactionId = "NanoTrasen"; + + [ValidatePrototypeId] + private const string GearId = "PirateGear"; + + [ValidatePrototypeId] + private const string SpawnPointId = "SpawnPointPirates"; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPlayerSpawningEvent); + SubscribeLocalEvent(OnRoundEndTextEvent); + SubscribeLocalEvent(OnStartAttempt); + } + + private void OnRoundEndTextEvent(RoundEndTextAppendEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var pirates, out var gameRule)) + { + if (Deleted(pirates.PirateShip)) + { + // Major loss, the ship somehow got annihilated. + ev.AddLine(Loc.GetString("pirates-no-ship")); + } + else + { + List<(double, EntityUid)> mostValuableThefts = new(); + + var comp1 = pirates; + var finalValue = _pricingSystem.AppraiseGrid(pirates.PirateShip, uid => + { + foreach (var mindId in comp1.Pirates) + { + if (TryComp(mindId, out MindComponent? mind) && mind.CurrentEntity == uid) + return false; // Don't appraise the pirates twice, we count them in separately. + } + + return true; + }, (uid, price) => + { + if (comp1.InitialItems.Contains(uid)) + return; + + mostValuableThefts.Add((price, uid)); + mostValuableThefts.Sort((i1, i2) => i2.Item1.CompareTo(i1.Item1)); + if (mostValuableThefts.Count > 5) + mostValuableThefts.Pop(); + }); + + foreach (var mindId in pirates.Pirates) + { + if (TryComp(mindId, out MindComponent? mind) && mind.CurrentEntity is not null) + finalValue += _pricingSystem.GetPrice(mind.CurrentEntity.Value); + } + + var score = finalValue - pirates.InitialShipValue; + + ev.AddLine(Loc.GetString("pirates-final-score", ("score", $"{score:F2}"))); + ev.AddLine(Loc.GetString("pirates-final-score-2", ("finalPrice", $"{finalValue:F2}"))); + + ev.AddLine(""); + ev.AddLine(Loc.GetString("pirates-most-valuable")); + + foreach (var (price, obj) in mostValuableThefts) + { + ev.AddLine(Loc.GetString("pirates-stolen-item-entry", ("entity", obj), ("credits", $"{price:F2}"))); + } + + if (mostValuableThefts.Count == 0) + ev.AddLine(Loc.GetString("pirates-stole-nothing")); + } + + ev.AddLine(""); + ev.AddLine(Loc.GetString("pirates-list-start")); + foreach (var pirate in pirates.Pirates) + { + if (TryComp(pirate, out MindComponent? mind)) + { + ev.AddLine($"- {mind.CharacterName} ({mind.Session?.Name})"); + } + } + } + } + + private void OnPlayerSpawningEvent(RulePlayerSpawningEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var pirates, out var gameRule)) + { + // Forgive me for copy-pasting nukies. + if (!GameTicker.IsGameRuleAdded(uid, gameRule)) + return; + + pirates.Pirates.Clear(); + pirates.InitialItems.Clear(); + + // Between 1 and : needs at least n players per op. + var numOps = Math.Max(1, + (int) Math.Min( + Math.Floor((double) ev.PlayerPool.Count / _cfg.GetCVar(CCVars.PiratesPlayersPerOp)), + _cfg.GetCVar(CCVars.PiratesMaxOps))); + var ops = new ICommonSession[numOps]; + for (var i = 0; i < numOps; i++) + { + ops[i] = _random.PickAndTake(ev.PlayerPool); + } + + var map = "/Maps/Shuttles/pirate.yml"; + var xformQuery = GetEntityQuery(); + + var aabbs = EntityQuery().SelectMany(x => + x.Grids.Select(x => + xformQuery.GetComponent(x).WorldMatrix.TransformBox(Comp(x).LocalAABB))) + .ToArray(); + + var aabb = aabbs[0]; + + for (var i = 1; i < aabbs.Length; i++) + { + aabb.Union(aabbs[i]); + } + + // (Not commented?) + var a = MathF.Max(aabb.Height / 2f, aabb.Width / 2f) * 2.5f; + + var gridId = _map.LoadGrid(GameTicker.DefaultMap, map, new MapLoadOptions + { + Offset = aabb.Center + new Vector2(a, a), + LoadMap = false, + }); + + if (!gridId.HasValue) + { + Log.Error($"Gridid was null when loading \"{map}\", aborting."); + foreach (var session in ops) + { + ev.PlayerPool.Add(session); + } + + return; + } + + pirates.PirateShip = gridId.Value; + + // TODO: Loot table or something + var pirateGear = _prototypeManager.Index(GearId); // YARRR + + var spawns = new List(); + + // Forgive me for hardcoding prototypes + foreach (var (_, meta, xform) in + EntityQuery(true)) + { + if (meta.EntityPrototype?.ID != SpawnPointId || xform.ParentUid != pirates.PirateShip) + continue; + + spawns.Add(xform.Coordinates); + } + + if (spawns.Count == 0) + { + spawns.Add(Transform(pirates.PirateShip).Coordinates); + Log.Warning($"Fell back to default spawn for pirates!"); + } + + for (var i = 0; i < ops.Length; i++) + { + var sex = _random.Prob(0.5f) ? Sex.Male : Sex.Female; + var gender = sex == Sex.Male ? Gender.Male : Gender.Female; + + var name = _namingSystem.GetName(SpeciesId, gender); + + var session = ops[i]; + var newMind = _mindSystem.CreateMind(session.UserId, name); + _mindSystem.SetUserId(newMind, session.UserId); + + var mob = Spawn(MobId, _random.Pick(spawns)); + _metaData.SetEntityName(mob, name); + + _mindSystem.TransferTo(newMind, mob); + var profile = _prefs.GetPreferences(session.UserId).SelectedCharacter as HumanoidCharacterProfile; + _stationSpawningSystem.EquipStartingGear(mob, pirateGear, profile); + + _npcFaction.RemoveFaction(mob, EnemyFactionId, false); + _npcFaction.AddFaction(mob, PirateFactionId); + + pirates.Pirates.Add(newMind); + + // Notificate every player about a pirate antagonist role with sound + _audioSystem.PlayGlobal(pirates.PirateAlertSound, session); + + GameTicker.PlayerJoinGame(session); + } + + pirates.InitialShipValue = _pricingSystem.AppraiseGrid(pirates.PirateShip, uid => + { + pirates.InitialItems.Add(uid); + return true; + }); // Include the players in the appraisal. + } + } + + //Forcing one player to be a pirate. + public void MakePirate(EntityUid entity) + { + if (!_mindSystem.TryGetMind(entity, out var mindId, out var mind)) + return; + + SetOutfitCommand.SetOutfit(entity, GearId, EntityManager); + + var pirateRule = EntityQuery().FirstOrDefault(); + if (pirateRule == null) + { + //todo fuck me this shit is awful + GameTicker.StartGameRule(GameRuleId, out var ruleEntity); + pirateRule = Comp(ruleEntity); + } + + // Notificate every player about a pirate antagonist role with sound + if (mind.Session != null) + { + _audioSystem.PlayGlobal(pirateRule.PirateAlertSound, mind.Session); + } + } + + private void OnStartAttempt(RoundStartAttemptEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var pirates, out var gameRule)) + { + if (!GameTicker.IsGameRuleActive(uid, gameRule)) + return; + + var minPlayers = _cfg.GetCVar(CCVars.PiratesMinPlayers); + if (!ev.Forced && ev.Players.Length < minPlayers) + { + _chatManager.SendAdminAnnouncement(Loc.GetString("nukeops-not-enough-ready-players", + ("readyPlayersCount", ev.Players.Length), ("minimumPlayers", minPlayers))); + ev.Cancel(); + return; + } + + if (ev.Players.Length == 0) + { + _chatManager.DispatchServerAnnouncement(Loc.GetString("nukeops-no-one-ready")); + ev.Cancel(); + } + } + } +} diff --git a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs index 5215da96aa..b11c28fb2b 100644 --- a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Systems; using Content.Shared.Chat; diff --git a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs index 7e6901e6c4..d20775c734 100644 --- a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs @@ -16,6 +16,7 @@ using Content.Shared.Database; using Content.Shared.Humanoid; using Content.Shared.IdentityManagement; +using Content.Shared.Inventory; using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Mindshield.Components; @@ -23,11 +24,12 @@ using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Revolutionary.Components; +using Content.Shared.Roles; using Content.Shared.Stunnable; using Content.Shared.Zombies; using Robust.Shared.Prototypes; using Robust.Shared.Timing; -using Content.Server.GameTicking.Components; +using System.Linq; namespace Content.Server.GameTicking.Rules; @@ -38,7 +40,7 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem RevolutionaryNpcFaction = "Revolutionary"; @@ -57,12 +60,23 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem(OnStartAttempt); + SubscribeLocalEvent(OnPlayerJobAssigned); SubscribeLocalEvent(OnCommandMobStateChanged); SubscribeLocalEvent(OnHeadRevMobStateChanged); + SubscribeLocalEvent(OnRoundEndText); SubscribeLocalEvent(OnGetBriefing); SubscribeLocalEvent(OnPostFlash); } + //Set miniumum players + protected override void Added(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) + { + base.Added(uid, component, gameRule, args); + + gameRule.MinPlayers = component.MinPlayers; + } + protected override void Started(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { base.Started(uid, component, gameRule, args); @@ -84,29 +98,40 @@ protected override void ActiveTick(EntityUid uid, RevolutionaryRuleComponent com } } - protected override void AppendRoundEndText(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, - ref RoundEndTextAppendEvent args) + private void OnRoundEndText(RoundEndTextAppendEvent ev) { - base.AppendRoundEndText(uid, component, gameRule, ref args); - var revsLost = CheckRevsLose(); var commandLost = CheckCommandLose(); - // This is (revsLost, commandsLost) concatted together - // (moony wrote this comment idk what it means) - var index = (commandLost ? 1 : 0) | (revsLost ? 2 : 0); - args.AddLine(Loc.GetString(Outcomes[index])); - - var sessionData = _antag.GetAntagIdentifiers(uid); - args.AddLine(Loc.GetString("rev-headrev-count", ("initialCount", sessionData.Count))); - foreach (var (mind, data, name) in sessionData) + var query = AllEntityQuery(); + while (query.MoveNext(out var headrev)) { - var count = CompOrNull(mind)?.ConvertedCount ?? 0; - args.AddLine(Loc.GetString("rev-headrev-name-user", - ("name", name), - ("username", data.UserName), - ("count", count))); + // This is (revsLost, commandsLost) concatted together + // (moony wrote this comment idk what it means) + var index = (commandLost ? 1 : 0) | (revsLost ? 2 : 0); + ev.AddLine(Loc.GetString(Outcomes[index])); + + ev.AddLine(Loc.GetString("rev-headrev-count", ("initialCount", headrev.HeadRevs.Count))); + foreach (var player in headrev.HeadRevs) + { + // TODO: when role entities are a thing this has to change + var count = CompOrNull(player.Value)?.ConvertedCount ?? 0; + + _mind.TryGetSession(player.Value, out var session); + var username = session?.Name; + if (username != null) + { + ev.AddLine(Loc.GetString("rev-headrev-name-user", + ("name", player.Key), + ("username", username), ("count", count))); + } + else + { + ev.AddLine(Loc.GetString("rev-headrev-name", + ("name", player.Key), ("count", count))); + } - // TODO: someone suggested listing all alive? revs maybe implement at some point + // TODO: someone suggested listing all alive? revs maybe implement at some point + } } } @@ -119,6 +144,57 @@ private void OnGetBriefing(EntityUid uid, RevolutionaryRoleComponent comp, ref G args.Append(Loc.GetString(head ? "head-rev-briefing" : "rev-briefing")); } + //Check for enough players to start rule + private void OnStartAttempt(RoundStartAttemptEvent ev) + { + TryRoundStartAttempt(ev, Loc.GetString("roles-antag-rev-name")); + } + + private void OnPlayerJobAssigned(RulePlayerJobsAssignedEvent ev) + { + var query = QueryActiveRules(); + while (query.MoveNext(out var uid, out var activeGameRule, out var comp, out var gameRule)) + { + var eligiblePlayers = _antagSelection.GetEligiblePlayers(ev.Players, comp.HeadRevPrototypeId); + + if (eligiblePlayers.Count == 0) + continue; + + var headRevCount = _antagSelection.CalculateAntagCount(ev.Players.Length, comp.PlayersPerHeadRev, comp.MaxHeadRevs); + + var headRevs = _antagSelection.ChooseAntags(headRevCount, eligiblePlayers); + + GiveHeadRev(headRevs, comp.HeadRevPrototypeId, comp); + } + } + + private void GiveHeadRev(IEnumerable chosen, ProtoId antagProto, RevolutionaryRuleComponent comp) + { + foreach (var headRev in chosen) + GiveHeadRev(headRev, antagProto, comp); + } + private void GiveHeadRev(EntityUid chosen, ProtoId antagProto, RevolutionaryRuleComponent comp) + { + RemComp(chosen); + + var inCharacterName = MetaData(chosen).EntityName; + + if (!_mind.TryGetMind(chosen, out var mind, out _)) + return; + + if (!_role.MindHasRole(mind)) + { + _role.MindAddRole(mind, new RevolutionaryRoleComponent { PrototypeId = antagProto }, silent: true); + } + + comp.HeadRevs.Add(inCharacterName, mind); + _inventory.SpawnItemsOnEntity(chosen, comp.StartingGear); + var revComp = EnsureComp(chosen); + EnsureComp(chosen); + + _antagSelection.SendBriefing(chosen, Loc.GetString("head-rev-role-greeting"), Color.CornflowerBlue, revComp.RevStartSound); + } + /// /// Called when a Head Rev uses a flash in melee to convert somebody else. /// @@ -157,7 +233,22 @@ private void OnPostFlash(EntityUid uid, HeadRevolutionaryComponent comp, ref Aft } if (mind?.Session != null) - _antag.SendBriefing(mind.Session, Loc.GetString("rev-role-greeting"), Color.Red, revComp.RevStartSound); + _antagSelection.SendBriefing(mind.Session, Loc.GetString("rev-role-greeting"), Color.Red, revComp.RevStartSound); + } + + public void OnHeadRevAdmin(EntityUid entity) + { + if (HasComp(entity)) + return; + + var revRule = EntityQuery().FirstOrDefault(); + if (revRule == null) + { + GameTicker.StartGameRule("Revolutionary", out var ruleEnt); + revRule = Comp(ruleEnt); + } + + GiveHeadRev(entity, revRule.HeadRevPrototypeId, revRule); } //TODO: Enemies of the revolution @@ -218,7 +309,7 @@ private bool CheckRevsLose() _popup.PopupEntity(Loc.GetString("rev-break-control", ("name", Identity.Entity(uid, EntityManager))), uid); _adminLogManager.Add(LogType.Mind, LogImpact.Medium, $"{ToPrettyString(uid)} was deconverted due to all Head Revolutionaries dying."); - if (!_mind.TryGetMind(uid, out var mindId, out _, mc)) + if (!_mind.TryGetMind(uid, out var mindId, out var mind, mc)) continue; // remove their antag role diff --git a/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs b/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs index f09ed3ebc3..7755f684be 100644 --- a/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Shuttles.Systems; using Content.Server.Station.Components; diff --git a/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs b/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs index c60670a3ad..a26a2d783c 100644 --- a/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Sandbox; diff --git a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs index d5adb8fdb7..fa5f17b4f3 100644 --- a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Administration.Logs; -using Content.Server.GameTicking.Components; using Content.Server.Chat.Managers; using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Rules.Components; diff --git a/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs b/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs index 4486ee40fb..42e7e82335 100644 --- a/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs +++ b/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Shared.Storage; diff --git a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs index 083085fa0d..32f9040f89 100644 --- a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs @@ -3,38 +3,118 @@ using Content.Server.Mind; using Content.Server.Objectives; using Content.Server.Roles; +using Content.Shared.Antag; +using Content.Shared.CombatMode.Pacification; using Content.Shared.Humanoid; +using Content.Shared.Inventory; using Content.Shared.Mind; using Content.Shared.Objectives.Components; +using Content.Shared.Roles; using Robust.Shared.Random; +using System.Linq; namespace Content.Server.GameTicking.Rules; public sealed class ThiefRuleSystem : GameRuleSystem { [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; [Dependency] private readonly MindSystem _mindSystem = default!; - [Dependency] private readonly AntagSelectionSystem _antag = default!; + [Dependency] private readonly SharedRoleSystem _roleSystem = default!; [Dependency] private readonly ObjectivesSystem _objectives = default!; + [Dependency] private readonly InventorySystem _inventory = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(AfterAntagSelected); + SubscribeLocalEvent(OnPlayersSpawned); SubscribeLocalEvent(OnGetBriefing); SubscribeLocalEvent(OnObjectivesTextGetInfo); } - private void AfterAntagSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) + private void OnPlayersSpawned(RulePlayerJobsAssignedEvent ev) { - if (!_mindSystem.TryGetMind(args.EntityUid, out var mindId, out var mind)) + var query = QueryActiveRules(); + while (query.MoveNext(out var uid, out _, out var comp, out var gameRule)) + { + //Get all players eligible for this role, allow selecting existing antags + //TO DO: When voxes specifies are added, increase their chance of becoming a thief by 4 times >:) + var eligiblePlayers = _antagSelection.GetEligiblePlayers(ev.Players, comp.ThiefPrototypeId, acceptableAntags: AntagAcceptability.All, allowNonHumanoids: true); + + //Abort if there are none + if (eligiblePlayers.Count == 0) + { + Log.Warning($"No eligible thieves found, ending game rule {ToPrettyString(uid):rule}"); + GameTicker.EndGameRule(uid, gameRule); + continue; + } + + //Calculate number of thieves to choose + var thiefCount = _random.Next(1, comp.MaxAllowThief + 1); + + //Select our theives + var thieves = _antagSelection.ChooseAntags(thiefCount, eligiblePlayers); + + MakeThief(thieves, comp, comp.PacifistThieves); + } + } + + public void MakeThief(List players, ThiefRuleComponent thiefRule, bool addPacified) + { + foreach (var thief in players) + { + MakeThief(thief, thiefRule, addPacified); + } + } + + public void MakeThief(EntityUid thief, ThiefRuleComponent thiefRule, bool addPacified) + { + if (!_mindSystem.TryGetMind(thief, out var mindId, out var mind)) return; + if (HasComp(mindId)) + return; + + // Assign thief roles + _roleSystem.MindAddRole(mindId, new ThiefRoleComponent + { + PrototypeId = thiefRule.ThiefPrototypeId, + }, silent: true); + + //Add Pacified + //To Do: Long-term this should just be using the antag code to add components. + if (addPacified) //This check is important because some servers may want to disable the thief's pacifism. Do not remove. + { + EnsureComp(thief); + } + //Generate objectives - GenerateObjectives(mindId, mind, ent); - _antag.SendBriefing(args.EntityUid, MakeBriefing(args.EntityUid), null, null); + GenerateObjectives(mindId, mind, thiefRule); + + //Send briefing here to account for humanoid/animal + _antagSelection.SendBriefing(thief, MakeBriefing(thief), null, thiefRule.GreetingSound); + + // Give starting items + _inventory.SpawnItemsOnEntity(thief, thiefRule.StarterItems); + + thiefRule.ThievesMinds.Add(mindId); + } + + public void AdminMakeThief(EntityUid entity, bool addPacified) + { + var thiefRule = EntityQuery().FirstOrDefault(); + if (thiefRule == null) + { + GameTicker.StartGameRule("Thief", out var ruleEntity); + thiefRule = Comp(ruleEntity); + } + + if (HasComp(entity)) + return; + + MakeThief(entity, thiefRule, addPacified); } private void GenerateObjectives(EntityUid mindId, MindComponent mind, ThiefRuleComponent thiefRule) @@ -80,7 +160,8 @@ private void OnGetBriefing(Entity thief, ref GetBriefingEven private string MakeBriefing(EntityUid thief) { var isHuman = HasComp(thief); - var briefing = isHuman + var briefing = "\n"; + briefing = isHuman ? Loc.GetString("thief-role-greeting-human") : Loc.GetString("thief-role-greeting-animal"); @@ -88,9 +169,9 @@ private string MakeBriefing(EntityUid thief) return briefing; } - private void OnObjectivesTextGetInfo(Entity ent, ref ObjectivesTextGetInfoEvent args) + private void OnObjectivesTextGetInfo(Entity thiefs, ref ObjectivesTextGetInfoEvent args) { - args.Minds = _antag.GetAntagMindEntityUids(ent.Owner); + args.Minds = thiefs.Comp.ThievesMinds; args.AgentName = Loc.GetString("thief-round-end-agent-name"); } } diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 1cc5e57704..fc9f0a9a9f 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -6,61 +6,96 @@ using Content.Server.PDA.Ringer; using Content.Server.Roles; using Content.Server.Traitor.Uplink; +using Content.Shared.CCVar; +using Content.Shared.Dataset; using Content.Shared.Mind; +using Content.Shared.Mobs.Systems; using Content.Shared.Objectives.Components; using Content.Shared.PDA; using Content.Shared.Roles; using Content.Shared.Roles.Jobs; +using Robust.Server.Player; +using Robust.Shared.Configuration; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Timing; using System.Linq; using System.Text; -using Content.Server.GameTicking.Components; -using Content.Server.Traitor.Components; namespace Content.Server.GameTicking.Rules; public sealed class TraitorRuleSystem : GameRuleSystem { + [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; - [Dependency] private readonly AntagSelectionSystem _antag = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly UplinkSystem _uplink = default!; [Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly SharedRoleSystem _roleSystem = default!; [Dependency] private readonly SharedJobSystem _jobs = default!; [Dependency] private readonly ObjectivesSystem _objectives = default!; + [Dependency] private readonly IGameTiming _timing = default!; - public const int MaxPicks = 20; + private int PlayersPerTraitor => _cfg.GetCVar(CCVars.TraitorPlayersPerTraitor); + private int MaxTraitors => _cfg.GetCVar(CCVars.TraitorMaxTraitors); public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(AfterEntitySelected); + SubscribeLocalEvent(OnStartAttempt); + SubscribeLocalEvent(OnPlayersSpawned); + SubscribeLocalEvent(HandleLatejoin); SubscribeLocalEvent(OnObjectivesTextGetInfo); SubscribeLocalEvent(OnObjectivesTextPrepend); } + //Set min players on game rule protected override void Added(EntityUid uid, TraitorRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) { base.Added(uid, component, gameRule, args); + + gameRule.MinPlayers = _cfg.GetCVar(CCVars.TraitorMinPlayers); + } + + protected override void Started(EntityUid uid, TraitorRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); MakeCodewords(component); } - private void AfterEntitySelected(Entity ent, ref AfterAntagEntitySelectedEvent args) + protected override void ActiveTick(EntityUid uid, TraitorRuleComponent component, GameRuleComponent gameRule, float frameTime) + { + base.ActiveTick(uid, component, gameRule, frameTime); + + if (component.SelectionStatus < TraitorRuleComponent.SelectionState.Started && component.AnnounceAt < _timing.CurTime) + { + DoTraitorStart(component); + component.SelectionStatus = TraitorRuleComponent.SelectionState.Started; + } + } + + /// + /// Check for enough players + /// + /// + private void OnStartAttempt(RoundStartAttemptEvent ev) { - MakeTraitor(args.EntityUid, ent); + TryRoundStartAttempt(ev, Loc.GetString("traitor-title")); } private void MakeCodewords(TraitorRuleComponent component) { - var adjectives = _prototypeManager.Index(component.CodewordAdjectives).Values; - var verbs = _prototypeManager.Index(component.CodewordVerbs).Values; + var codewordCount = _cfg.GetCVar(CCVars.TraitorCodewordCount); + var adjectives = _prototypeManager.Index(component.CodewordAdjectives).Values; + var verbs = _prototypeManager.Index(component.CodewordVerbs).Values; var codewordPool = adjectives.Concat(verbs).ToList(); - var finalCodewordCount = Math.Min(component.CodewordCount, codewordPool.Count); + var finalCodewordCount = Math.Min(codewordCount, codewordPool.Count); component.Codewords = new string[finalCodewordCount]; for (var i = 0; i < finalCodewordCount; i++) { @@ -68,25 +103,66 @@ private void MakeCodewords(TraitorRuleComponent component) } } + private void DoTraitorStart(TraitorRuleComponent component) + { + var eligiblePlayers = _antagSelection.GetEligiblePlayers(_playerManager.Sessions, component.TraitorPrototypeId); + + if (eligiblePlayers.Count == 0) + return; + + var traitorsToSelect = _antagSelection.CalculateAntagCount(_playerManager.PlayerCount, PlayersPerTraitor, MaxTraitors); + + var selectedTraitors = _antagSelection.ChooseAntags(traitorsToSelect, eligiblePlayers); + + MakeTraitor(selectedTraitors, component); + } + + private void OnPlayersSpawned(RulePlayerJobsAssignedEvent ev) + { + //Start the timer + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var comp, out var gameRuleComponent)) + { + var delay = TimeSpan.FromSeconds( + _cfg.GetCVar(CCVars.TraitorStartDelay) + + _random.NextFloat(0f, _cfg.GetCVar(CCVars.TraitorStartDelayVariance))); + + //Set the delay for choosing traitors + comp.AnnounceAt = _timing.CurTime + delay; + + comp.SelectionStatus = TraitorRuleComponent.SelectionState.ReadyToStart; + } + } + + public bool MakeTraitor(List traitors, TraitorRuleComponent component, bool giveUplink = true, bool giveObjectives = true) + { + foreach (var traitor in traitors) + { + MakeTraitor(traitor, component, giveUplink, giveObjectives); + } + + return true; + } + public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true, bool giveObjectives = true) { //Grab the mind if it wasnt provided if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind)) return false; - var briefing = Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", component.Codewords))); - - if (TryComp(traitor, out var autoTraitorComponent)) + if (HasComp(mindId)) { - giveUplink = autoTraitorComponent.GiveUplink; - giveObjectives = autoTraitorComponent.GiveObjectives; + Log.Error($"Player {mind.CharacterName} is already a traitor."); + return false; } + var briefing = Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", component.Codewords))); + Note[]? code = null; if (giveUplink) { // Calculate the amount of currency on the uplink. - var startingBalance = component.StartingBalance; + var startingBalance = _cfg.GetCVar(CCVars.TraitorStartingBalance); if (_jobs.MindTryGetJob(mindId, out _, out var prototype)) startingBalance = Math.Max(startingBalance - prototype.AntagAdvantage, 0); @@ -104,14 +180,19 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp", "#")))); } - _antag.SendBriefing(traitor, GenerateBriefing(component.Codewords, code), null, component.GreetSoundNotification); + _antagSelection.SendBriefing(traitor, GenerateBriefing(component.Codewords, code), null, component.GreetSoundNotification); component.TraitorMinds.Add(mindId); + // Assign traitor roles + _roleSystem.MindAddRole(mindId, new TraitorRoleComponent + { + PrototypeId = component.TraitorPrototypeId + }, mind, true); // Assign briefing _roleSystem.MindAddRole(mindId, new RoleBriefingComponent { - Briefing = briefing + Briefing = briefing.ToString() }, mind, true); // Change the faction @@ -121,8 +202,11 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool // Give traitors their objectives if (giveObjectives) { + var maxDifficulty = _cfg.GetCVar(CCVars.TraitorMaxDifficulty); + var maxPicks = _cfg.GetCVar(CCVars.TraitorMaxPicks); var difficulty = 0f; - for (var pick = 0; pick < MaxPicks && component.MaxDifficulty > difficulty; pick++) + Log.Debug($"Attempting {maxPicks} objective picks with {maxDifficulty} difficulty"); + for (var pick = 0; pick < maxPicks && maxDifficulty > difficulty; pick++) { var objective = _objectives.GetRandomObjective(mindId, mind, component.ObjectiveGroup); if (objective == null) @@ -138,9 +222,53 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool return true; } + private void HandleLatejoin(PlayerSpawnCompleteEvent ev) + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var comp, out _)) + { + if (comp.TotalTraitors >= MaxTraitors) + continue; + + if (!ev.LateJoin) + continue; + + if (!_antagSelection.IsPlayerEligible(ev.Player, comp.TraitorPrototypeId)) + continue; + + //If its before we have selected traitors, continue + if (comp.SelectionStatus < TraitorRuleComponent.SelectionState.Started) + continue; + + // the nth player we adjust our probabilities around + var target = PlayersPerTraitor * comp.TotalTraitors + 1; + var chance = 1f / PlayersPerTraitor; + + // If we have too many traitors, divide by how many players below target for next traitor we are. + if (ev.JoinOrder < target) + { + chance /= (target - ev.JoinOrder); + } + else // Tick up towards 100% chance. + { + chance *= ((ev.JoinOrder + 1) - target); + } + + if (chance > 1) + chance = 1; + + // Now that we've calculated our chance, roll and make them a traitor if we roll under. + // You get one shot. + if (_random.Prob(chance)) + { + MakeTraitor(ev.Mob, comp); + } + } + } + private void OnObjectivesTextGetInfo(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextGetInfoEvent args) { - args.Minds = _antag.GetAntagMindEntityUids(uid); + args.Minds = comp.TraitorMinds; args.AgentName = Loc.GetString("traitor-round-end-agent-name"); } @@ -149,6 +277,27 @@ private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, r args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords))); } + /// + /// Start this game rule manually + /// + public TraitorRuleComponent StartGameRule() + { + var comp = EntityQuery().FirstOrDefault(); + if (comp == null) + { + GameTicker.StartGameRule("Traitor", out var ruleEntity); + comp = Comp(ruleEntity); + } + + return comp; + } + + public void MakeTraitorAdmin(EntityUid entity, bool giveUplink, bool giveObjectives) + { + var traitorRule = StartGameRule(); + MakeTraitor(entity, traitorRule, giveUplink, giveObjectives); + } + private string GenerateBriefing(string[] codewords, Note[]? uplinkCode) { var sb = new StringBuilder(); @@ -163,11 +312,9 @@ private string GenerateBriefing(string[] codewords, Note[]? uplinkCode) public List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind) { List<(EntityUid Id, MindComponent Mind)> allTraitors = new(); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var traitor)) + foreach (var traitor in EntityQuery()) { - foreach (var role in GetOtherTraitorMindsAliveAndConnected(ourMind, (uid, traitor))) + foreach (var role in GetOtherTraitorMindsAliveAndConnected(ourMind, traitor)) { if (!allTraitors.Contains(role)) allTraitors.Add(role); @@ -177,15 +324,20 @@ private string GenerateBriefing(string[] codewords, Note[]? uplinkCode) return allTraitors; } - private List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind, Entity rule) + private List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind, TraitorRuleComponent component) { var traitors = new List<(EntityUid Id, MindComponent Mind)>(); - foreach (var mind in _antag.GetAntagMinds(rule.Owner)) + foreach (var traitor in component.TraitorMinds) { - if (mind.Comp == ourMind) - continue; - - traitors.Add((mind, mind)); + if (TryComp(traitor, out MindComponent? mind) && + mind.OwnedEntity != null && + mind.Session != null && + mind != ourMind && + _mobStateSystem.IsAlive(mind.OwnedEntity.Value) && + mind.CurrentEntity == mind.OwnedEntity) + { + traitors.Add((traitor, mind)); + } } return traitors; diff --git a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs index f62d0b79ff..5714337d4d 100644 --- a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs @@ -1,35 +1,46 @@ +using Content.Server.Actions; using Content.Server.Antag; using Content.Server.Chat.Systems; using Content.Server.GameTicking.Rules.Components; using Content.Server.Popups; +using Content.Server.Roles; using Content.Server.RoundEnd; using Content.Server.Station.Components; using Content.Server.Station.Systems; using Content.Server.Zombies; +using Content.Shared.CCVar; using Content.Shared.Humanoid; using Content.Shared.Mind; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.Roles; using Content.Shared.Zombies; +using Robust.Server.Player; +using Robust.Shared.Configuration; using Robust.Shared.Player; +using Robust.Shared.Random; using Robust.Shared.Timing; using System.Globalization; using Content.Server.Announcements.Systems; -using Content.Server.GameTicking.Components; namespace Content.Server.GameTicking.Rules; public sealed class ZombieRuleSystem : GameRuleSystem { + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly RoundEndSystem _roundEnd = default!; [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly ActionsSystem _action = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly ZombieSystem _zombie = default!; [Dependency] private readonly SharedMindSystem _mindSystem = default!; + [Dependency] private readonly SharedRoleSystem _roles = default!; [Dependency] private readonly StationSystem _station = default!; - [Dependency] private readonly AntagSelectionSystem _antag = default!; + [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly AnnouncerSystem _announcer = default!; [Dependency] private readonly GameTicker _gameTicker = default!; @@ -38,56 +49,67 @@ public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnStartAttempt); + SubscribeLocalEvent(OnRoundEndText); SubscribeLocalEvent(OnZombifySelf); } - protected override void AppendRoundEndText(EntityUid uid, ZombieRuleComponent component, GameRuleComponent gameRule, - ref RoundEndTextAppendEvent args) + /// + /// Set the required minimum players for this gamemode to start + /// + protected override void Added(EntityUid uid, ZombieRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) { - base.AppendRoundEndText(uid, component, gameRule, ref args); - - // This is just the general condition thing used for determining the win/lose text - var fraction = GetInfectedFraction(true, true); - - if (fraction <= 0) - args.AddLine(Loc.GetString("zombie-round-end-amount-none")); - else if (fraction <= 0.25) - args.AddLine(Loc.GetString("zombie-round-end-amount-low")); - else if (fraction <= 0.5) - args.AddLine(Loc.GetString("zombie-round-end-amount-medium", ("percent", Math.Round((fraction * 100), 2).ToString(CultureInfo.InvariantCulture)))); - else if (fraction < 1) - args.AddLine(Loc.GetString("zombie-round-end-amount-high", ("percent", Math.Round((fraction * 100), 2).ToString(CultureInfo.InvariantCulture)))); - else - args.AddLine(Loc.GetString("zombie-round-end-amount-all")); - - var antags = _antag.GetAntagIdentifiers(uid); - args.AddLine(Loc.GetString("zombie-round-end-initial-count", ("initialCount", antags.Count))); - foreach (var (_, data, entName) in antags) - { - args.AddLine(Loc.GetString("zombie-round-end-user-was-initial", - ("name", entName), - ("username", data.UserName))); - } + base.Added(uid, component, gameRule, args); - var healthy = GetHealthyHumans(); - // Gets a bunch of the living players and displays them if they're under a threshold. - // InitialInfected is used for the threshold because it scales with the player count well. - if (healthy.Count <= 0 || healthy.Count > 2 * antags.Count) - return; - args.AddLine(""); - args.AddLine(Loc.GetString("zombie-round-end-survivor-count", ("count", healthy.Count))); - foreach (var survivor in healthy) + gameRule.MinPlayers = _cfg.GetCVar(CCVars.ZombieMinPlayers); + } + + private void OnRoundEndText(RoundEndTextAppendEvent ev) + { + foreach (var zombie in EntityQuery()) { - var meta = MetaData(survivor); - var username = string.Empty; - if (_mindSystem.TryGetMind(survivor, out _, out var mind) && mind.Session != null) + // This is just the general condition thing used for determining the win/lose text + var fraction = GetInfectedFraction(true, true); + + if (fraction <= 0) + ev.AddLine(Loc.GetString("zombie-round-end-amount-none")); + else if (fraction <= 0.25) + ev.AddLine(Loc.GetString("zombie-round-end-amount-low")); + else if (fraction <= 0.5) + ev.AddLine(Loc.GetString("zombie-round-end-amount-medium", ("percent", Math.Round((fraction * 100), 2).ToString(CultureInfo.InvariantCulture)))); + else if (fraction < 1) + ev.AddLine(Loc.GetString("zombie-round-end-amount-high", ("percent", Math.Round((fraction * 100), 2).ToString(CultureInfo.InvariantCulture)))); + else + ev.AddLine(Loc.GetString("zombie-round-end-amount-all")); + + ev.AddLine(Loc.GetString("zombie-round-end-initial-count", ("initialCount", zombie.InitialInfectedNames.Count))); + foreach (var player in zombie.InitialInfectedNames) { - username = mind.Session.Name; + ev.AddLine(Loc.GetString("zombie-round-end-user-was-initial", + ("name", player.Key), + ("username", player.Value))); } - args.AddLine(Loc.GetString("zombie-round-end-user-was-survivor", - ("name", meta.EntityName), - ("username", username))); + var healthy = GetHealthyHumans(true); + // Gets a bunch of the living players and displays them if they're under a threshold. + // InitialInfected is used for the threshold because it scales with the player count well. + if (healthy.Count <= 0 || healthy.Count > 2 * zombie.InitialInfectedNames.Count) + continue; + ev.AddLine(""); + ev.AddLine(Loc.GetString("zombie-round-end-survivor-count", ("count", healthy.Count))); + foreach (var survivor in healthy) + { + var meta = MetaData(survivor); + var username = string.Empty; + if (_mindSystem.TryGetMind(survivor, out _, out var mind) && mind.Session != null) + { + username = mind.Session.Name; + } + + ev.AddLine(Loc.GetString("zombie-round-end-user-was-survivor", + ("name", meta.EntityName), + ("username", username))); + } } } @@ -117,20 +139,38 @@ private void CheckRoundEnd(ZombieRuleComponent zombieRuleComponent) _roundEnd.EndRound(); } + /// + /// Check we have enough players to start this game mode, if not - cancel and announce + /// + private void OnStartAttempt(RoundStartAttemptEvent ev) + { + TryRoundStartAttempt(ev, Loc.GetString("zombie-title")); + } + protected override void Started(EntityUid uid, ZombieRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { base.Started(uid, component, gameRule, args); - component.NextRoundEndCheck = _timing.CurTime + component.EndCheckDelay; + var delay = _random.Next(component.MinStartDelay, component.MaxStartDelay); + component.StartTime = _timing.CurTime + delay; } protected override void ActiveTick(EntityUid uid, ZombieRuleComponent component, GameRuleComponent gameRule, float frameTime) { base.ActiveTick(uid, component, gameRule, frameTime); - if (!component.NextRoundEndCheck.HasValue || component.NextRoundEndCheck > _timing.CurTime) - return; - CheckRoundEnd(component); - component.NextRoundEndCheck = _timing.CurTime + component.EndCheckDelay; + + if (component.StartTime.HasValue && component.StartTime < _timing.CurTime) + { + InfectInitialPlayers(component); + component.StartTime = null; + component.NextRoundEndCheck = _timing.CurTime + component.EndCheckDelay; + } + + if (component.NextRoundEndCheck.HasValue && component.NextRoundEndCheck < _timing.CurTime) + { + CheckRoundEnd(component); + component.NextRoundEndCheck = _timing.CurTime + component.EndCheckDelay; + } } private void OnZombifySelf(EntityUid uid, PendingZombieComponent component, ZombifySelfActionEvent args) @@ -195,4 +235,81 @@ private List GetHealthyHumans(bool includeOffStation = false) } return healthy; } + + /// + /// Infects the first players with the passive zombie virus. + /// Also records their names for the end of round screen. + /// + /// + /// The reason this code is written separately is to facilitate + /// allowing this gamemode to be started midround. As such, it doesn't need + /// any information besides just running. + /// + private void InfectInitialPlayers(ZombieRuleComponent component) + { + //Get all players with initial infected enabled, and exclude those with the ZombieImmuneComponent and roles with CanBeAntag = False + var eligiblePlayers = _antagSelection.GetEligiblePlayers( + _playerManager.Sessions, + component.PatientZeroPrototypeId, + includeAllJobs: false, + customExcludeCondition: player => HasComp(player) || HasComp(player) + ); + + //And get all players, excluding ZombieImmune and roles with CanBeAntag = False - to fill any leftover initial infected slots + var allPlayers = _antagSelection.GetEligiblePlayers( + _playerManager.Sessions, + component.PatientZeroPrototypeId, + acceptableAntags: Shared.Antag.AntagAcceptability.All, + includeAllJobs: false , + ignorePreferences: true, + customExcludeCondition: HasComp + ); + + //If there are no players to choose, abort + if (allPlayers.Count == 0) + return; + + //How many initial infected should we select + var initialInfectedCount = _antagSelection.CalculateAntagCount(_playerManager.PlayerCount, component.PlayersPerInfected, component.MaxInitialInfected); + + //Choose the required number of initial infected from the eligible players, making up any shortfall by choosing from all players + var initialInfected = _antagSelection.ChooseAntags(initialInfectedCount, eligiblePlayers, allPlayers); + + //Make brain craving + MakeZombie(initialInfected, component); + + //Send the briefing, play greeting sound + _antagSelection.SendBriefing(initialInfected, Loc.GetString("zombie-patientzero-role-greeting"), Color.Plum, component.InitialInfectedSound); + } + + private void MakeZombie(List entities, ZombieRuleComponent component) + { + foreach (var entity in entities) + { + MakeZombie(entity, component); + } + } + private void MakeZombie(EntityUid entity, ZombieRuleComponent component) + { + if (!_mindSystem.TryGetMind(entity, out var mind, out var mindComponent)) + return; + + //Add the role to the mind silently (to avoid repeating job assignment) + _roles.MindAddRole(mind, new InitialInfectedRoleComponent { PrototypeId = component.PatientZeroPrototypeId }, silent: true); + EnsureComp(entity); + + //Add the zombie components and grace period + var pending = EnsureComp(entity); + pending.GracePeriod = _random.Next(component.MinInitialInfectedGrace, component.MaxInitialInfectedGrace); + EnsureComp(entity); + EnsureComp(entity); + + //Add the zombify action + _action.AddAction(entity, ref pending.Action, component.ZombifySelfActionPrototype, entity); + + //Get names for the round end screen, incase they leave mid-round + var inCharacterName = MetaData(entity).EntityName; + var accountName = mindComponent.Session == null ? string.Empty : mindComponent.Session.Name; + component.InitialInfectedNames.Add(inCharacterName, accountName); + } } diff --git a/Content.Server/Goobstation/Changeling/ChangelingSystem.Abilities.cs b/Content.Server/Goobstation/Changeling/ChangelingSystem.Abilities.cs deleted file mode 100644 index cd9ffaee6b..0000000000 --- a/Content.Server/Goobstation/Changeling/ChangelingSystem.Abilities.cs +++ /dev/null @@ -1,618 +0,0 @@ -using Content.Shared.Changeling; -using Content.Shared.Chemistry.Components; -using Content.Shared.Cuffs.Components; -using Content.Shared.DoAfter; -using Content.Shared.FixedPoint; -using Content.Shared.IdentityManagement; -using Content.Shared.Mobs; -using Content.Server.Store.Components; -using Content.Shared.Popups; -using Content.Shared.Damage; -using Robust.Shared.Prototypes; -using Content.Shared.Damage.Prototypes; -using Content.Server.Objectives.Components; -using Content.Server.Light.Components; -using Content.Shared.Eye.Blinding.Systems; -using Content.Shared.Eye.Blinding.Components; -using Content.Server.Flash.Components; -using Content.Shared.Movement.Pulling.Components; -using Content.Shared.Stealth.Components; -using Content.Shared.Damage.Components; -using Content.Server.Radio.Components; - -namespace Content.Server.Changeling; - -public sealed partial class ChangelingSystem : EntitySystem -{ - public void SubscribeAbilities() - { - SubscribeLocalEvent(OnOpenEvolutionMenu); - SubscribeLocalEvent(OnAbsorb); - SubscribeLocalEvent(OnAbsorbDoAfter); - SubscribeLocalEvent(OnStingExtractDNA); - SubscribeLocalEvent(OnTransformCycle); - SubscribeLocalEvent(OnTransform); - SubscribeLocalEvent(OnEnterStasis); - SubscribeLocalEvent(OnExitStasis); - - SubscribeLocalEvent(OnToggleArmblade); - SubscribeLocalEvent(OnCreateBoneShard); - SubscribeLocalEvent(OnToggleArmor); - SubscribeLocalEvent(OnToggleShield); - SubscribeLocalEvent(OnShriekDissonant); - SubscribeLocalEvent(OnShriekResonant); - SubscribeLocalEvent(OnToggleStrainedMuscles); - - SubscribeLocalEvent(OnStingBlind); - SubscribeLocalEvent(OnStingCryo); - SubscribeLocalEvent(OnStingLethargic); - SubscribeLocalEvent(OnStingMute); - SubscribeLocalEvent(OnStingTransform); - SubscribeLocalEvent(OnStingFakeArmblade); - - SubscribeLocalEvent(OnAnatomicPanacea); - SubscribeLocalEvent(OnAugmentedEyesight); - SubscribeLocalEvent(OnBiodegrade); - SubscribeLocalEvent(OnChameleonSkin); - SubscribeLocalEvent(OnEphedrineOverdose); - SubscribeLocalEvent(OnHealUltraSwag); - SubscribeLocalEvent(OnLastResort); - SubscribeLocalEvent(OnLesserForm); - SubscribeLocalEvent(OnSpacesuit); - SubscribeLocalEvent(OnHivemindAccess); - } - - #region Basic Abilities - - private void OnOpenEvolutionMenu(EntityUid uid, ChangelingComponent comp, ref OpenEvolutionMenuEvent args) - { - if (!TryComp(uid, out var store)) - return; - - _store.ToggleUi(uid, uid, store); - } - - private void OnAbsorb(EntityUid uid, ChangelingComponent comp, ref AbsorbDNAEvent args) - { - var target = args.Target; - - if (!IsIncapacitated(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-absorb-fail-incapacitated"), uid, uid); - return; - } - if (HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-absorb-fail-absorbed"), uid, uid); - return; - } - if (!HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-absorb-fail-unabsorbable"), uid, uid); - return; - } - - if (!TryUseAbility(uid, comp, args)) - return; - - var popupOthers = Loc.GetString("changeling-absorb-start", ("user", Identity.Entity(uid, EntityManager)), ("target", Identity.Entity(target, EntityManager))); - _popup.PopupEntity(popupOthers, uid, PopupType.LargeCaution); - PlayMeatySound(uid, comp); - var dargs = new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(15), new AbsorbDNADoAfterEvent(), uid, target) - { - DistanceThreshold = 1.5f, - BreakOnDamage = true, - BreakOnHandChange = false, - BreakOnUserMove = true, - BreakOnTargetMove = true, - BreakOnWeightlessMove = true, - AttemptFrequency = AttemptFrequency.StartAndEnd - }; - _doAfter.TryStartDoAfter(dargs); - } - public ProtoId AbsorbedDamageGroup = "Genetic"; - private void OnAbsorbDoAfter(EntityUid uid, ChangelingComponent comp, ref AbsorbDNADoAfterEvent args) - { - if (args.Args.Target == null) - return; - - var target = args.Args.Target.Value; - - if (args.Cancelled || !IsIncapacitated(target) || HasComp(target)) - return; - - PlayMeatySound(args.User, comp); - - UpdateBiomass(uid, comp, comp.MaxBiomass - comp.TotalAbsorbedEntities); - - var dmg = new DamageSpecifier(_proto.Index(AbsorbedDamageGroup), 200); - _damage.TryChangeDamage(target, dmg, false, false); - _blood.ChangeBloodReagent(target, "FerrochromicAcid"); - _blood.SpillAllSolutions(target); - - EnsureComp(target); - - var popup = Loc.GetString("changeling-absorb-end-self-ling"); - var bonusChemicals = 0f; - var bonusEvolutionPoints = 0f; - if (TryComp(target, out var targetComp)) - { - bonusChemicals += targetComp.MaxChemicals / 2; - bonusEvolutionPoints += 10; - comp.MaxBiomass += targetComp.MaxBiomass / 2; - } - else - { - popup = Loc.GetString("changeling-absorb-end-self"); - bonusChemicals += 10; - bonusEvolutionPoints += 2; - } - TryStealDNA(uid, target, comp, true); - comp.TotalAbsorbedEntities++; - - _popup.PopupEntity(popup, args.User, args.User); - comp.MaxChemicals += bonusChemicals; - - if (TryComp(args.User, out var store)) - { - _store.TryAddCurrency(new Dictionary { { "EvolutionPoint", bonusEvolutionPoints } }, args.User, store); - _store.UpdateUserInterface(args.User, args.User, store); - } - - if (_mind.TryGetMind(uid, out var mindId, out var mind)) - if (_mind.TryGetObjectiveComp(mindId, out var objective, mind)) - objective.Absorbed += 1; - } - - private void OnStingExtractDNA(EntityUid uid, ChangelingComponent comp, ref StingExtractDNAEvent args) - { - if (!TrySting(uid, comp, args, true)) - return; - - var target = args.Target; - if (!TryStealDNA(uid, target, comp, true)) - { - _popup.PopupEntity(Loc.GetString("changeling-sting-extract-fail"), uid, uid); - // royal cashback - comp.Chemicals += Comp(args.Action).ChemicalCost; - } - else _popup.PopupEntity(Loc.GetString("changeling-sting", ("target", Identity.Entity(target, EntityManager))), uid, uid); - } - - private void OnTransformCycle(EntityUid uid, ChangelingComponent comp, ref ChangelingTransformCycleEvent args) - { - comp.AbsorbedDNAIndex += 1; - if (comp.AbsorbedDNAIndex >= comp.MaxAbsorbedDNA || comp.AbsorbedDNAIndex >= comp.AbsorbedDNA.Count) - comp.AbsorbedDNAIndex = 0; - - if (comp.AbsorbedDNA.Count == 0) - { - _popup.PopupEntity(Loc.GetString("changeling-transform-cycle-empty"), uid, uid); - return; - } - - var selected = comp.AbsorbedDNA.ToArray()[comp.AbsorbedDNAIndex]; - comp.SelectedForm = selected; - _popup.PopupEntity(Loc.GetString("changeling-transform-cycle", ("target", selected.Name)), uid, uid); - } - private void OnTransform(EntityUid uid, ChangelingComponent comp, ref ChangelingTransformEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryTransform(uid, comp)) - comp.Chemicals += Comp(args.Action).ChemicalCost; - } - - private void OnEnterStasis(EntityUid uid, ChangelingComponent comp, ref EnterStasisEvent args) - { - if (comp.IsInStasis || HasComp(uid)) - { - _popup.PopupEntity(Loc.GetString("changeling-stasis-enter-fail"), uid, uid); - return; - } - - if (!TryUseAbility(uid, comp, args)) - return; - - comp.Chemicals = 0f; - - if (_mobState.IsAlive(uid)) - { - // fake our death - var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", uid)); - _popup.PopupEntity(othersMessage, uid, Robust.Shared.Player.Filter.PvsExcept(uid), true); - - var selfMessage = Loc.GetString("changeling-stasis-enter"); - _popup.PopupEntity(selfMessage, uid, uid); - } - - if (!_mobState.IsDead(uid)) - _mobState.ChangeMobState(uid, MobState.Dead); - - comp.IsInStasis = true; - } - private void OnExitStasis(EntityUid uid, ChangelingComponent comp, ref ExitStasisEvent args) - { - if (!comp.IsInStasis) - { - _popup.PopupEntity(Loc.GetString("changeling-stasis-exit-fail"), uid, uid); - return; - } - if (HasComp(uid)) - { - _popup.PopupEntity(Loc.GetString("changeling-stasis-exit-fail-dead"), uid, uid); - return; - } - - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryComp(uid, out var damageable)) - return; - - // heal of everything - _damage.SetAllDamage(uid, damageable, 0); - _mobState.ChangeMobState(uid, MobState.Alive); - _blood.TryModifyBloodLevel(uid, 1000); - _blood.TryModifyBleedAmount(uid, -1000); - - _popup.PopupEntity(Loc.GetString("changeling-stasis-exit"), uid, uid); - - comp.IsInStasis = false; - } - - #endregion - - #region Combat Abilities - - private void OnToggleArmblade(EntityUid uid, ChangelingComponent comp, ref ToggleArmbladeEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryToggleItem(uid, ArmbladePrototype, comp)) - return; - - PlayMeatySound(uid, comp); - } - private void OnCreateBoneShard(EntityUid uid, ChangelingComponent comp, ref CreateBoneShardEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - var star = Spawn(BoneShardPrototype, Transform(uid).Coordinates); - _hands.TryPickupAnyHand(uid, star); - - PlayMeatySound(uid, comp); - } - private void OnToggleArmor(EntityUid uid, ChangelingComponent comp, ref ToggleChitinousArmorEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryToggleItem(uid, ArmorPrototype, comp, "outerClothing") - || !TryToggleItem(uid, ArmorHelmetPrototype, comp, "head")) - { - _popup.PopupEntity(Loc.GetString("changeling-equip-armor-fail"), uid, uid); - comp.Chemicals += Comp(args.Action).ChemicalCost; - return; - } - - PlayMeatySound(uid, comp); - } - private void OnToggleShield(EntityUid uid, ChangelingComponent comp, ref ToggleOrganicShieldEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryToggleItem(uid, ShieldPrototype, comp)) - return; - - PlayMeatySound(uid, comp); - } - private void OnShriekDissonant(EntityUid uid, ChangelingComponent comp, ref ShriekDissonantEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - DoScreech(uid, comp); - - var pos = _transform.GetMapCoordinates(uid); - var power = comp.ShriekPower; - _emp.EmpPulse(pos, power, 5000f, power * 2); - } - private void OnShriekResonant(EntityUid uid, ChangelingComponent comp, ref ShriekResonantEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - DoScreech(uid, comp); - - var power = comp.ShriekPower; - _flash.FlashArea(uid, uid, power, power * 2f * 1000f); - - var lookup = _lookup.GetEntitiesInRange(uid, power); - var lights = GetEntityQuery(); - - foreach (var ent in lookup) - if (lights.HasComponent(ent)) - _light.TryDestroyBulb(ent); - } - private void OnToggleStrainedMuscles(EntityUid uid, ChangelingComponent comp, ref ToggleStrainedMusclesEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - ToggleStrainedMuscles(uid, comp); - } - private void ToggleStrainedMuscles(EntityUid uid, ChangelingComponent comp) - { - if (!comp.StrainedMusclesActive) - { - _popup.PopupEntity(Loc.GetString("changeling-muscles-start"), uid, uid); - comp.StrainedMusclesActive = true; - } - else - { - _popup.PopupEntity(Loc.GetString("changeling-muscles-end"), uid, uid); - comp.StrainedMusclesActive = false; - } - - PlayMeatySound(uid, comp); - _speed.RefreshMovementSpeedModifiers(uid); - } - - #endregion - - #region Stings - - private void OnStingBlind(EntityUid uid, ChangelingComponent comp, ref StingBlindEvent args) - { - if (!TrySting(uid, comp, args)) - return; - - var target = args.Target; - if (!TryComp(target, out var blindable) || blindable.IsBlind) - return; - - _blindable.AdjustEyeDamage((target, blindable), 2); - var timeSpan = TimeSpan.FromSeconds(5f); - _statusEffect.TryAddStatusEffect(target, TemporaryBlindnessSystem.BlindingStatusEffect, timeSpan, false, TemporaryBlindnessSystem.BlindingStatusEffect); - } - private void OnStingCryo(EntityUid uid, ChangelingComponent comp, ref StingCryoEvent args) - { - var reagents = new List<(string, FixedPoint2)>() - { - ("Fresium", 20f), - ("ChloralHydrate", 10f) - }; - - if (!TryReagentSting(uid, comp, args, reagents)) - return; - } - private void OnStingLethargic(EntityUid uid, ChangelingComponent comp, ref StingLethargicEvent args) - { - var reagents = new List<(string, FixedPoint2)>() - { - ("Impedrezene", 10f), - ("MuteToxin", 5f) - }; - - if (!TryReagentSting(uid, comp, args, reagents)) - return; - } - private void OnStingMute(EntityUid uid, ChangelingComponent comp, ref StingMuteEvent args) - { - var reagents = new List<(string, FixedPoint2)>() - { - ("MuteToxin", 15f) - }; - - if (!TryReagentSting(uid, comp, args, reagents)) - return; - } - private void OnStingTransform(EntityUid uid, ChangelingComponent comp, ref StingTransformEvent args) - { - if (!TrySting(uid, comp, args, true)) - return; - - var target = args.Target; - if (!TryTransform(target, comp, true, true)) - comp.Chemicals += Comp(args.Action).ChemicalCost; - } - private void OnStingFakeArmblade(EntityUid uid, ChangelingComponent comp, ref StingFakeArmbladeEvent args) - { - if (!TrySting(uid, comp, args)) - return; - - var target = args.Target; - var fakeArmblade = EntityManager.SpawnEntity(FakeArmbladePrototype, Transform(target).Coordinates); - if (!_hands.TryPickupAnyHand(target, fakeArmblade)) - { - QueueDel(fakeArmblade); - comp.Chemicals += Comp(args.Action).ChemicalCost; - _popup.PopupEntity(Loc.GetString("changeling-sting-fail-simplemob"), uid, uid); - return; - } - - PlayMeatySound(target, comp); - } - - #endregion - - #region Utilities - - public void OnAnatomicPanacea(EntityUid uid, ChangelingComponent comp, ref ActionAnatomicPanaceaEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - var reagents = new List<(string, FixedPoint2)>() - { - ("Diphenhydramine", 5f), - ("Arithrazine", 5f), - ("Ethylredoxrazine", 5f) - }; - if (TryInjectReagents(uid, reagents)) - _popup.PopupEntity(Loc.GetString("changeling-panacea"), uid, uid); - else return; - PlayMeatySound(uid, comp); - } - public void OnAugmentedEyesight(EntityUid uid, ChangelingComponent comp, ref ActionAugmentedEyesightEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (HasComp(uid)) - { - _popup.PopupEntity(Loc.GetString("changeling-passive-active"), uid, uid); - return; - } - - EnsureComp(uid); - _popup.PopupEntity(Loc.GetString("changeling-passive-activate"), uid, uid); - } - public void OnBiodegrade(EntityUid uid, ChangelingComponent comp, ref ActionBiodegradeEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (TryComp(uid, out var cuffs) && cuffs.Container.ContainedEntities.Count > 0) - { - var cuff = cuffs.LastAddedCuffs; - - _cuffs.Uncuff(uid, cuffs.LastAddedCuffs, cuff); - QueueDel(cuff); - } - - var soln = new Solution(); - soln.AddReagent("PolytrinicAcid", 10f); - - if (_pull.IsPulled(uid)) - { - var puller = Comp(uid).Puller; - if (puller != null) - { - _puddle.TrySplashSpillAt((EntityUid) puller, Transform((EntityUid) puller).Coordinates, soln, out _); - return; - } - } - _puddle.TrySplashSpillAt(uid, Transform(uid).Coordinates, soln, out _); - } - public void OnChameleonSkin(EntityUid uid, ChangelingComponent comp, ref ActionChameleonSkinEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (HasComp(uid) && HasComp(uid)) - { - RemComp(uid); - RemComp(uid); - _popup.PopupEntity(Loc.GetString("changeling-chameleon-end"), uid, uid); - return; - } - - EnsureComp(uid); - EnsureComp(uid); - _popup.PopupEntity(Loc.GetString("changeling-chameleon-start"), uid, uid); - } - public void OnEphedrineOverdose(EntityUid uid, ChangelingComponent comp, ref ActionEphedrineOverdoseEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - var stam = EnsureComp(uid); - stam.StaminaDamage = 0; - - var reagents = new List<(string, FixedPoint2)>() - { - ("Desoxyephedrine", 5f) - }; - if (TryInjectReagents(uid, reagents)) - _popup.PopupEntity(Loc.GetString("changeling-inject"), uid, uid); - else - { - _popup.PopupEntity(Loc.GetString("changeling-inject-fail"), uid, uid); - return; - } - } - // john space made me do this - public void OnHealUltraSwag(EntityUid uid, ChangelingComponent comp, ref ActionFleshmendEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - var reagents = new List<(string, FixedPoint2)>() - { - ("Ichor", 10f), - ("TranexamicAcid", 5f) - }; - if (TryInjectReagents(uid, reagents)) - _popup.PopupEntity(Loc.GetString("changeling-fleshmend"), uid, uid); - else return; - PlayMeatySound(uid, comp); - } - public void OnLastResort(EntityUid uid, ChangelingComponent comp, ref ActionLastResortEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - // todo: implement - } - public void OnLesserForm(EntityUid uid, ChangelingComponent comp, ref ActionLesserFormEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - var newUid = TransformEntity(uid, protoId: "MobMonkey", comp: comp); - if (newUid == null) - { - comp.Chemicals += Comp(args.Action).ChemicalCost; - return; - } - - PlayMeatySound((EntityUid) newUid, comp); - var loc = Loc.GetString("changeling-transform-others", ("user", Identity.Entity((EntityUid) newUid, EntityManager))); - _popup.PopupEntity(loc, (EntityUid) newUid, PopupType.LargeCaution); - - comp.IsInLesserForm = true; - } - public void OnSpacesuit(EntityUid uid, ChangelingComponent comp, ref ActionSpacesuitEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryToggleItem(uid, SpacesuitPrototype, comp, "outerClothing") - || !TryToggleItem(uid, SpacesuitHelmetPrototype, comp, "head")) - { - _popup.PopupEntity(Loc.GetString("changeling-equip-armor-fail"), uid, uid); - comp.Chemicals += Comp(args.Action).ChemicalCost; - return; - } - - PlayMeatySound(uid, comp); - } - public void OnHivemindAccess(EntityUid uid, ChangelingComponent comp, ref ActionHivemindAccessEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (HasComp(uid)) - { - _popup.PopupEntity(Loc.GetString("changeling-passive-active"), uid, uid); - return; - } - - EnsureComp(uid); - var reciever = EnsureComp(uid); - var transmitter = EnsureComp(uid); - var radio = EnsureComp(uid); - radio.Channels = new() { "Hivemind" }; - transmitter.Channels = new() { "Hivemind" }; - - _popup.PopupEntity(Loc.GetString("changeling-hivemind-start"), uid, uid); - } - - #endregion -} diff --git a/Content.Server/Goobstation/Changeling/ChangelingSystem.cs b/Content.Server/Goobstation/Changeling/ChangelingSystem.cs deleted file mode 100644 index 8b912004ff..0000000000 --- a/Content.Server/Goobstation/Changeling/ChangelingSystem.cs +++ /dev/null @@ -1,634 +0,0 @@ -using Content.Server.DoAfter; -using Content.Server.Forensics; -using Content.Server.Polymorph.Systems; -using Content.Server.Popups; -using Content.Server.Store.Systems; -using Content.Server.Zombies; -using Content.Shared.Alert; -using Content.Shared.Changeling; -using Content.Shared.Chemistry.Components; -using Content.Shared.Cuffs.Components; -using Content.Shared.FixedPoint; -using Content.Shared.Humanoid; -using Content.Shared.IdentityManagement; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Systems; -using Content.Shared.Nutrition.Components; -using Content.Server.Store.Components; -using Robust.Server.Audio; -using Robust.Shared.Audio; -using Robust.Shared.Random; -using Content.Shared.Popups; -using Content.Shared.Damage; -using Robust.Shared.Prototypes; -using Content.Server.Body.Systems; -using Content.Shared.Actions; -using Content.Shared.Polymorph; -using Robust.Shared.Serialization.Manager; -using Content.Server.Actions; -using Content.Server.Humanoid; -using Content.Server.Polymorph.Components; -using Content.Shared.Chemistry.EntitySystems; -using Content.Server.Flash; -using Content.Server.Emp; -using Robust.Server.GameObjects; -using Content.Shared.Hands.EntitySystems; -using Content.Shared.Inventory; -using Content.Shared.Movement.Systems; -using Content.Shared.Damage.Systems; -using Content.Shared.Mind; -using Content.Server.Objectives.Components; -using Content.Server.Light.EntitySystems; -using Content.Shared.Eye.Blinding.Systems; -using Content.Shared.StatusEffect; -using Content.Shared.Movement.Pulling.Systems; -using Content.Shared.Cuffs; -using Content.Shared.Fluids; -using Content.Shared.Revolutionary.Components; -using Robust.Shared.Player; -using System.Numerics; -using Content.Shared.Camera; -using Robust.Shared.Timing; -using Content.Shared.Damage.Components; -using Content.Server.Gravity; -using Content.Shared.Mobs.Components; -using Content.Server.Stunnable; -using Content.Shared.Jittering; -using System.Linq; - -namespace Content.Server.Changeling; - -public sealed partial class ChangelingSystem : EntitySystem -{ - // this is one hell of a star wars intro text - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly IRobustRandom _rand = default!; - [Dependency] private readonly ActionsSystem _actions = default!; - [Dependency] private readonly StoreSystem _store = default!; - [Dependency] private readonly AudioSystem _audio = default!; - [Dependency] private readonly PolymorphSystem _polymorph = default!; - [Dependency] private readonly AlertsSystem _alerts = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly DoAfterSystem _doAfter = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly DamageableSystem _damage = default!; - [Dependency] private readonly BloodstreamSystem _blood = default!; - [Dependency] private readonly ISerializationManager _serialization = default!; - [Dependency] private readonly MetaDataSystem _metaData = default!; - [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; - [Dependency] private readonly TransformSystem _transform = default!; - [Dependency] private readonly FlashSystem _flash = default!; - [Dependency] private readonly EmpSystem _emp = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly PoweredLightSystem _light = default!; - [Dependency] private readonly ISharedPlayerManager _player = default!; - [Dependency] private readonly SharedCameraRecoilSystem _recoil = default!; - [Dependency] private readonly SharedHandsSystem _hands = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly MovementSpeedModifierSystem _speed = default!; - [Dependency] private readonly StaminaSystem _stamina = default!; - [Dependency] private readonly GravitySystem _gravity = default!; - [Dependency] private readonly BlindableSystem _blindable = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffect = default!; - [Dependency] private readonly PullingSystem _pull = default!; - [Dependency] private readonly SharedCuffableSystem _cuffs = default!; - [Dependency] private readonly SharedPuddleSystem _puddle = default!; - [Dependency] private readonly StunSystem _stun = default!; - [Dependency] private readonly SharedJitteringSystem _jitter = default!; - - public EntProtoId ArmbladePrototype = "ArmBladeChangeling"; - public EntProtoId FakeArmbladePrototype = "FakeArmBladeChangeling"; - - public EntProtoId ShieldPrototype = "ChangelingShield"; - public EntProtoId BoneShardPrototype = "ThrowingStarChangeling"; - - public EntProtoId ArmorPrototype = "ChangelingClothingOuterArmor"; - public EntProtoId ArmorHelmetPrototype = "ChangelingClothingHeadHelmet"; - - public EntProtoId SpacesuitPrototype = "ChangelingClothingOuterHardsuit"; - public EntProtoId SpacesuitHelmetPrototype = "ChangelingClothingHeadHelmetHardsuit"; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(OnMobStateChange); - SubscribeLocalEvent(OnDamageChange); - SubscribeLocalEvent(OnComponentRemove); - - SubscribeLocalEvent(OnRefreshSpeed); - - SubscribeAbilities(); - } - - private void OnRefreshSpeed(Entity ent, ref RefreshMovementSpeedModifiersEvent args) - { - if (ent.Comp.StrainedMusclesActive) - args.ModifySpeed(1.25f, 1.5f); - else - args.ModifySpeed(1f, 1f); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - if (!_timing.IsFirstTimePredicted) - return; - - foreach (var comp in EntityManager.EntityQuery()) - { - var uid = comp.Owner; - - if (_timing.CurTime < comp.UpdateTimer) - continue; - - comp.UpdateTimer = _timing.CurTime + TimeSpan.FromSeconds(comp.UpdateCooldown); - - Cycle(uid, comp); - } - } - public void Cycle(EntityUid uid, ChangelingComponent comp) - { - UpdateChemicals(uid, comp); - - comp.BiomassUpdateTimer += 1; - if (comp.BiomassUpdateTimer >= comp.BiomassUpdateCooldown) - { - comp.BiomassUpdateTimer = 0; - UpdateBiomass(uid, comp); - } - - UpdateAbilities(uid, comp); - } - - private void UpdateChemicals(EntityUid uid, ChangelingComponent comp, float? amount = null) - { - var chemicals = comp.Chemicals; - // either amount or regen - chemicals += amount ?? 1 + comp.BonusChemicalRegen; - comp.Chemicals = Math.Clamp(chemicals, 0, comp.MaxChemicals); - Dirty(uid, comp); - _alerts.ShowAlert(uid, AlertType.ChangelingChemicals); - } - private void UpdateBiomass(EntityUid uid, ChangelingComponent comp, float? amount = null) - { - comp.Biomass += amount ?? -1; - comp.Biomass = Math.Clamp(comp.Biomass, 0, comp.MaxBiomass); - Dirty(uid, comp); - _alerts.ShowAlert(uid, AlertType.ChangelingBiomass); - - var random = (int) _rand.Next(1, 3); - - if (comp.Biomass <= 0) - // game over, man - _damage.TryChangeDamage(uid, new DamageSpecifier(_proto.Index(AbsorbedDamageGroup), 50), true); - - if (comp.Biomass <= comp.MaxBiomass / 10) - { - // THE FUNNY ITCH IS REAL!! - comp.BonusChemicalRegen = 3f; - _popup.PopupEntity(Loc.GetString("popup-changeling-biomass-deficit-high"), uid, uid, PopupType.LargeCaution); - _jitter.DoJitter(uid, TimeSpan.FromSeconds(comp.BiomassUpdateCooldown), true, amplitude: 5, frequency: 10); - } - else if (comp.Biomass <= comp.MaxBiomass / 3) - { - // vomit blood - if (random == 1) - { - if (TryComp(uid, out var status)) - _stun.TrySlowdown(uid, TimeSpan.FromSeconds(1.5f), true, 0.5f, 0.5f, status); - - var solution = new Solution(); - - var vomitAmount = 15f; - _blood.TryModifyBloodLevel(uid, -vomitAmount); - solution.AddReagent("Blood", vomitAmount); - - _puddle.TrySplashSpillAt(uid, Transform(uid).Coordinates, solution, out _); - - _popup.PopupEntity(Loc.GetString("disease-vomit", ("person", Identity.Entity(uid, EntityManager))), uid); - } - - // the funny itch is not real - if (random == 3) - { - _popup.PopupEntity(Loc.GetString("popup-changeling-biomass-deficit-medium"), uid, uid, PopupType.MediumCaution); - _jitter.DoJitter(uid, TimeSpan.FromSeconds(.5f), true, amplitude: 5, frequency: 10); - } - } - else if (comp.Biomass <= comp.MaxBiomass / 2 && random == 3) - { - if (random == 1) - _popup.PopupEntity(Loc.GetString("popup-changeling-biomass-deficit-low"), uid, uid, PopupType.SmallCaution); - } - else comp.BonusChemicalRegen = 0f; - } - private void UpdateAbilities(EntityUid uid, ChangelingComponent comp) - { - _speed.RefreshMovementSpeedModifiers(uid); - if (comp.StrainedMusclesActive) - { - var stamina = EnsureComp(uid); - _stamina.TakeStaminaDamage(uid, 7.5f, visual: false); - if (stamina.StaminaDamage >= stamina.CritThreshold || _gravity.IsWeightless(uid)) - ToggleStrainedMuscles(uid, comp); - } - } - - #region Helper Methods - - public void PlayMeatySound(EntityUid uid, ChangelingComponent comp) - { - var rand = _rand.Next(0, comp.SoundPool.Count - 1); - var sound = comp.SoundPool.ToArray()[rand]; - _audio.PlayPvs(sound, uid, AudioParams.Default.WithVolume(-3f)); - } - public void DoScreech(EntityUid uid, ChangelingComponent comp) - { - _audio.PlayPvs(comp.ShriekSound, uid); - - var center = Transform(uid).MapPosition; - var gamers = Filter.Empty(); - gamers.AddInRange(center, comp.ShriekPower, _player, EntityManager); - - foreach (var gamer in gamers.Recipients) - { - if (gamer.AttachedEntity == null) - continue; - - var pos = Transform(gamer.AttachedEntity!.Value).WorldPosition; - var delta = center.Position - pos; - - if (delta.EqualsApprox(Vector2.Zero)) - delta = new(.01f, 0); - - _recoil.KickCamera(uid, -delta.Normalized()); - } - } - - /// - /// Check if a target is crit/dead or cuffed. For absorbing. - /// - public bool IsIncapacitated(EntityUid uid) - { - if (_mobState.IsIncapacitated(uid) - || (TryComp(uid, out var cuffs) && cuffs.CuffedHandCount > 0)) - return true; - - return false; - } - - public bool TryUseAbility(EntityUid uid, ChangelingComponent comp, BaseActionEvent action) - { - if (action.Handled) - return false; - - if (!TryComp(action.Action, out var lingAction)) - return false; - - if (comp.Biomass < 1 && lingAction.RequireBiomass) - { - _popup.PopupEntity(Loc.GetString("changeling-biomass-deficit"), uid, uid); - return false; - } - - if (!lingAction.UseInLesserForm && comp.IsInLesserForm) - { - _popup.PopupEntity(Loc.GetString("changeling-action-fail-lesserform"), uid, uid); - return false; - } - - if (comp.Chemicals < lingAction.ChemicalCost) - { - _popup.PopupEntity(Loc.GetString("changeling-chemicals-deficit"), uid, uid); - return false; - } - - if (lingAction.RequireAbsorbed > comp.TotalAbsorbedEntities) - { - var delta = lingAction.RequireAbsorbed - comp.TotalAbsorbedEntities; - _popup.PopupEntity(Loc.GetString("changeling-action-fail-absorbed", ("number", delta)), uid, uid); - return false; - } - - UpdateChemicals(uid, comp, -lingAction.ChemicalCost); - UpdateBiomass(uid, comp, -lingAction.BiomassCost); - - action.Handled = true; - - return true; - } - public bool TrySting(EntityUid uid, ChangelingComponent comp, EntityTargetActionEvent action, bool overrideMessage = false) - { - if (!TryUseAbility(uid, comp, action)) - return false; - - var target = action.Target; - - // can't get his dna if he doesn't have it! - if (!HasComp(target) || HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-sting-extract-fail"), uid, uid); - return false; - } - - if (HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-sting-fail-self", ("target", Identity.Entity(target, EntityManager))), uid, uid); - _popup.PopupEntity(Loc.GetString("changeling-sting-fail-ling"), target, target); - return false; - } - if (!overrideMessage) - _popup.PopupEntity(Loc.GetString("changeling-sting", ("target", Identity.Entity(target, EntityManager))), uid, uid); - return true; - } - public bool TryInjectReagents(EntityUid uid, List<(string, FixedPoint2)> reagents) - { - var solution = new Solution(); - foreach (var reagent in reagents) - solution.AddReagent(reagent.Item1, reagent.Item2); - - if (!_solution.TryGetInjectableSolution(uid, out var targetSolution, out var _)) - return false; - - if (!_solution.TryAddSolution(targetSolution.Value, solution)) - return false; - - return true; - } - public bool TryReagentSting(EntityUid uid, ChangelingComponent comp, EntityTargetActionEvent action, List<(string, FixedPoint2)> reagents) - { - var target = action.Target; - if (!TrySting(uid, comp, action)) - return false; - - if (!TryInjectReagents(target, reagents)) - return false; - - return true; - } - public bool TryToggleItem(EntityUid uid, EntProtoId proto, ChangelingComponent comp, string? clothingSlot = null) - { - if (!comp.Equipment.TryGetValue(proto.Id, out var item) && item == null) - { - item = Spawn(proto, Transform(uid).Coordinates); - if (clothingSlot != null && !_inventory.TryEquip(uid, (EntityUid) item, clothingSlot, force: true)) - { - QueueDel(item); - return false; - } - else if (!_hands.TryForcePickupAnyHand(uid, (EntityUid) item)) - { - _popup.PopupEntity(Loc.GetString("changeling-fail-hands"), uid, uid); - QueueDel(item); - return false; - } - comp.Equipment.Add(proto.Id, item); - return true; - } - - QueueDel(item); - // assuming that it exists - comp.Equipment.Remove(proto.Id); - - return true; - } - - public bool TryStealDNA(EntityUid uid, EntityUid target, ChangelingComponent comp, bool countObjective = false) - { - if (!TryComp(target, out var appearance) - || !TryComp(target, out var metadata) - || !TryComp(target, out var dna) - || !TryComp(target, out var fingerprint)) - return false; - - foreach (var storedDNA in comp.AbsorbedDNA) - { - if (storedDNA.DNA != null && storedDNA.DNA == dna.DNA) - return false; - } - - var data = new TransformData - { - Name = metadata.EntityName, - DNA = dna.DNA, - Appearance = appearance - }; - - if (fingerprint.Fingerprint != null) - data.Fingerprint = fingerprint.Fingerprint; - - if (comp.AbsorbedDNA.Count >= comp.MaxAbsorbedDNA) - _popup.PopupEntity(Loc.GetString("changeling-sting-extract-max"), uid, uid); - else comp.AbsorbedDNA.Add(data); - - if (countObjective - && _mind.TryGetMind(uid, out var mindId, out var mind) - && _mind.TryGetObjectiveComp(mindId, out var objective, mind)) - { - objective.DNAStolen += 1; - } - - comp.TotalStolenDNA++; - - return true; - } - - private ChangelingComponent? CopyChangelingComponent(EntityUid target, ChangelingComponent comp) - { - var newComp = EnsureComp(target); - newComp.AbsorbedDNA = comp.AbsorbedDNA; - newComp.AbsorbedDNAIndex = comp.AbsorbedDNAIndex; - - newComp.Chemicals = comp.Chemicals; - newComp.MaxChemicals = comp.MaxChemicals; - - newComp.Biomass = comp.Biomass; - newComp.MaxBiomass = comp.MaxBiomass; - - newComp.IsInLesserForm = comp.IsInLesserForm; - newComp.CurrentForm = comp.CurrentForm; - - newComp.TotalAbsorbedEntities = comp.TotalAbsorbedEntities; - newComp.TotalStolenDNA = comp.TotalStolenDNA; - - return comp; - } - private EntityUid? TransformEntity(EntityUid uid, TransformData? data = null, EntProtoId? protoId = null, ChangelingComponent? comp = null, bool persistentDna = false) - { - EntProtoId? pid = null; - - if (data != null) - { - if (!_proto.TryIndex(data.Appearance.Species, out var species)) - return null; - pid = species.Prototype; - } - else if (protoId != null) - pid = protoId; - else return null; - - var config = new PolymorphConfiguration() - { - Entity = (EntProtoId) pid, - TransferDamage = true, - Forced = true, - Inventory = PolymorphInventoryChange.Transfer, - RevertOnCrit = false, - RevertOnDeath = false - }; - var newUid = _polymorph.PolymorphEntity(uid, config); - - if (newUid == null) - return null; - - var newEnt = newUid.Value; - - if (data != null) - { - Comp(newEnt).Fingerprint = data.Fingerprint; - Comp(newEnt).DNA = data.DNA; - _humanoid.CloneAppearance(data.Appearance.Owner, newEnt); - _metaData.SetEntityName(newEnt, data.Name); - var message = Loc.GetString("changeling-transform-finish", ("target", data.Name)); - _popup.PopupEntity(message, newEnt, newEnt); - } - - RemCompDeferred(newEnt); - - if (comp != null) - { - // copy our stuff - var newLingComp = CopyChangelingComponent(newEnt, comp); - if (!persistentDna && data != null) - newLingComp?.AbsorbedDNA.Remove(data); - RemCompDeferred(uid); - - if (TryComp(uid, out var storeComp)) - { - var storeCompCopy = _serialization.CreateCopy(storeComp, notNullableOverride: true); - RemComp(newUid.Value); - EntityManager.AddComponent(newUid.Value, storeCompCopy); - } - } - - // exceptional comps check - // there's no foreach for types i believe so i gotta thug it out yandev style. - if (HasComp(uid)) - EnsureComp(newEnt); - if (HasComp(uid)) - EnsureComp(newEnt); - - QueueDel(uid); - - return newUid; - } - public bool TryTransform(EntityUid target, ChangelingComponent comp, bool sting = false, bool persistentDna = false) - { - if (HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-transform-fail-absorbed"), target, target); - return false; - } - - var data = comp.SelectedForm; - - if (data == null) - { - _popup.PopupEntity(Loc.GetString("changeling-transform-fail-self"), target, target); - return false; - } - if (data == comp.CurrentForm) - { - _popup.PopupEntity(Loc.GetString("changeling-transform-fail-choose"), target, target); - return false; - } - - var locName = Identity.Entity(target, EntityManager); - EntityUid? newUid = null; - if (sting) - newUid = TransformEntity(target, data: data, persistentDna: persistentDna); - else newUid = TransformEntity(target, data: data, comp: comp, persistentDna: persistentDna); - - if (newUid != null) - { - PlayMeatySound((EntityUid) newUid, comp); - var loc = Loc.GetString("changeling-transform-others", ("user", locName)); - _popup.PopupEntity(loc, (EntityUid) newUid, PopupType.LargeCaution); - } - - return true; - } - - public void RemoveAllChangelingEquipment(EntityUid target, ChangelingComponent comp) - { - // check if there's no entities or all entities are null - if (comp.Equipment.Values.Count == 0 - || comp.Equipment.Values.All(ent => ent == null ? true : false)) - return; - - foreach (var equip in comp.Equipment.Values) - QueueDel(equip); - - PlayMeatySound(target, comp); - } - - #endregion - - #region Event Handlers - - private void OnStartup(EntityUid uid, ChangelingComponent comp, ref ComponentStartup args) - { - RemComp(uid); - RemComp(uid); - EnsureComp(uid); - - // add actions - foreach (var actionId in comp.BaseChangelingActions) - _actions.AddAction(uid, actionId); - - // making sure things are right in this world - comp.Chemicals = comp.MaxChemicals; - comp.Biomass = comp.MaxBiomass; - - // show alerts - UpdateChemicals(uid, comp, 0); - UpdateBiomass(uid, comp, 0); - // make their blood unreal - _blood.ChangeBloodReagent(uid, "BloodChangeling"); - } - - private void OnMobStateChange(EntityUid uid, ChangelingComponent comp, ref MobStateChangedEvent args) - { - if (args.NewMobState == MobState.Dead) - RemoveAllChangelingEquipment(uid, comp); - } - - private void OnDamageChange(Entity ent, ref DamageChangedEvent args) - { - var target = args.Damageable; - - if (!TryComp(ent, out var mobState)) - return; - - if (mobState.CurrentState != MobState.Dead) - return; - - if (!args.DamageIncreased) - return; - - target.Damage.ClampMax(200); // we never die. UNLESS?? - } - - private void OnComponentRemove(Entity ent, ref ComponentRemove args) - { - RemoveAllChangelingEquipment(ent, ent.Comp); - } - - #endregion -} \ No newline at end of file diff --git a/Content.Server/Goobstation/GameTicking/Rules/ChangelingRuleSystem.cs b/Content.Server/Goobstation/GameTicking/Rules/ChangelingRuleSystem.cs deleted file mode 100644 index f70aebbe4d..0000000000 --- a/Content.Server/Goobstation/GameTicking/Rules/ChangelingRuleSystem.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Content.Server.Antag; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.Mind; -using Content.Server.Objectives; -using Content.Server.Roles; -using Content.Shared.Changeling; -using Content.Server.NPC.Components; -using Content.Server.NPC.Systems; -using Content.Shared.Roles; -using Content.Shared.Store; -using Content.Server.Store.Components; -using Robust.Shared.Audio; -using Robust.Shared.Prototypes; -using System.Text; - -namespace Content.Server.GameTicking.Rules; - -public sealed partial class ChangelingRuleSystem : GameRuleSystem -{ - [Dependency] private readonly MindSystem _mind = default!; - [Dependency] private readonly AntagSelectionSystem _antag = default!; - [Dependency] private readonly SharedRoleSystem _role = default!; - [Dependency] private readonly NpcFactionSystem _npcFaction = default!; - [Dependency] private readonly ObjectivesSystem _objective = default!; - - public readonly SoundSpecifier BriefingSound = new SoundPathSpecifier("/Audio/Goobstation/Ambience/Antag/changeling_start.ogg"); - - public readonly ProtoId ChangelingPrototypeId = "Changeling"; - - public readonly ProtoId ChangelingFactionId = "Changeling"; - - public readonly ProtoId NanotrasenFactionId = "NanoTrasen"; - - public readonly ProtoId Currency = "EvolutionPoint"; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnSelectAntag); - SubscribeLocalEvent(OnTextPrepend); - } - - private void OnSelectAntag(EntityUid uid, ChangelingRuleComponent comp, ref AfterAntagEntitySelectedEvent args) - { - MakeChangeling(args.EntityUid, comp); - } - public bool MakeChangeling(EntityUid target, ChangelingRuleComponent rule) - { - if (!_mind.TryGetMind(target, out var mindId, out var mind)) - return false; - - // briefing - if (TryComp(target, out var metaData)) - { - var briefing = Loc.GetString("changeling-role-greeting", ("name", metaData?.EntityName ?? "Unknown")); - var briefingShort = Loc.GetString("changeling-role-greeting-short", ("name", metaData?.EntityName ?? "Unknown")); - - _antag.SendBriefing(target, briefing, Color.Yellow, BriefingSound); - _role.MindAddRole(mindId, new RoleBriefingComponent { Briefing = briefingShort }, mind, true); - } - // hivemind stuff - _npcFaction.RemoveFaction(target, NanotrasenFactionId, false); - _npcFaction.AddFaction(target, ChangelingFactionId); - - // make sure it's initial chems are set to max - EnsureComp(target); - - // add store - var store = EnsureComp(target); - foreach (var category in rule.StoreCategories) - store.Categories.Add(category); - store.CurrencyWhitelist.Add(Currency); - store.Balance.Add(Currency, 16); - - rule.ChangelingMinds.Add(mindId); - - foreach (var objective in rule.Objectives) - _mind.TryAddObjective(mindId, mind, objective); - - return true; - } - - private void OnTextPrepend(EntityUid uid, ChangelingRuleComponent comp, ref ObjectivesTextPrependEvent args) - { - var mostAbsorbedName = string.Empty; - var mostStolenName = string.Empty; - var mostAbsorbed = 0f; - var mostStolen = 0f; - - foreach (var ling in EntityQuery()) - { - if (!_mind.TryGetMind(ling.Owner, out var mindId, out var mind)) - continue; - - if (!TryComp(ling.Owner, out var metaData)) - continue; - - if (ling.TotalAbsorbedEntities > mostAbsorbed) - { - mostAbsorbed = ling.TotalAbsorbedEntities; - mostAbsorbedName = _objective.GetTitle(mindId); - if (mostAbsorbedName is null) - mostAbsorbedName = String.Empty; - } - if (ling.TotalStolenDNA > mostStolen) - { - mostStolen = ling.TotalStolenDNA; - mostStolenName = _objective.GetTitle(mindId); - if (mostStolenName is null) - mostStolenName = String.Empty; - - } - } - - var sb = new StringBuilder(); - sb.AppendLine(Loc.GetString($"roundend-prepend-changeling-absorbed{(!string.IsNullOrWhiteSpace(mostAbsorbedName) ? "-named" : "")}", ("name", mostAbsorbedName), ("number", mostAbsorbed))); - sb.AppendLine(Loc.GetString($"roundend-prepend-changeling-stolen{(!string.IsNullOrWhiteSpace(mostStolenName) ? "-named" : "")}", ("name", mostStolenName), ("number", mostStolen))); - - args.Text = sb.ToString(); - } -} diff --git a/Content.Server/Goobstation/GameTicking/Rules/Components/ChangelingRuleComponent.cs b/Content.Server/Goobstation/GameTicking/Rules/Components/ChangelingRuleComponent.cs deleted file mode 100644 index 2a23375ad5..0000000000 --- a/Content.Server/Goobstation/GameTicking/Rules/Components/ChangelingRuleComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Content.Shared.Store; -using Robust.Shared.Prototypes; - -namespace Content.Server.GameTicking.Rules.Components; - -[RegisterComponent, Access(typeof(ChangelingRuleSystem))] -public sealed partial class ChangelingRuleComponent : Component -{ - public readonly List ChangelingMinds = new(); - - public readonly List> StoreCategories = new() - { - "ChangelingAbilityCombat", - "ChangelingAbilitySting", - "ChangelingAbilityUtility" - }; - - public readonly List> Objectives = new() - { - "ChangelingSurviveObjective", - "ChangelingStealDNAObjective", - "EscapeIdentityObjective" - }; -} diff --git a/Content.Server/Goobstation/Objectives/Components/AbsorbConditionComponent.cs b/Content.Server/Goobstation/Objectives/Components/AbsorbConditionComponent.cs deleted file mode 100644 index 764e90d763..0000000000 --- a/Content.Server/Goobstation/Objectives/Components/AbsorbConditionComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Content.Server.Changeling; -using Content.Server.Objectives.Systems; - -namespace Content.Server.Objectives.Components; - -[RegisterComponent, Access(typeof(ChangelingObjectiveSystem), typeof(ChangelingSystem))] -public sealed partial class AbsorbConditionComponent : Component -{ - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float Absorbed = 0f; -} diff --git a/Content.Server/Goobstation/Objectives/Components/ImpersonateConditionComponent.cs b/Content.Server/Goobstation/Objectives/Components/ImpersonateConditionComponent.cs deleted file mode 100644 index f94d5db6f9..0000000000 --- a/Content.Server/Goobstation/Objectives/Components/ImpersonateConditionComponent.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Content.Server.Objectives.Systems; - -namespace Content.Server.Objectives.Components; - -/// -/// Requires that you have the same identity a target for a certain length of time before the round ends. -/// Obviously the agent id will work for this, but it's assumed that you will kill the target to prevent suspicion. -/// Depends on to function. -/// -[RegisterComponent, Access(typeof(ImpersonateConditionSystem))] -public sealed partial class ImpersonateConditionComponent : Component -{ - /// - /// Name that must match your identity for greentext. - /// This is stored once after the objective is assigned: - /// 1. to be a tiny bit more efficient - /// 2. to prevent the name possibly changing when borging or anything else and messing you up - /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public string? Name; - - /// - /// Mind this objective got assigned to, used to continiously checkd impersonation. - /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public EntityUid? MindId; - - public bool Completed = false; -} diff --git a/Content.Server/Goobstation/Objectives/Components/StealDNAConditionComponent.cs b/Content.Server/Goobstation/Objectives/Components/StealDNAConditionComponent.cs deleted file mode 100644 index 409ff8a30a..0000000000 --- a/Content.Server/Goobstation/Objectives/Components/StealDNAConditionComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Content.Server.Changeling; -using Content.Server.Objectives.Systems; - -namespace Content.Server.Objectives.Components; - -[RegisterComponent, Access(typeof(ChangelingObjectiveSystem), typeof(ChangelingSystem))] -public sealed partial class StealDNAConditionComponent : Component -{ - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float DNAStolen = 0f; -} diff --git a/Content.Server/Goobstation/Objectives/Systems/ChangelingObjectiveSystem.cs b/Content.Server/Goobstation/Objectives/Systems/ChangelingObjectiveSystem.cs deleted file mode 100644 index 6633bb07de..0000000000 --- a/Content.Server/Goobstation/Objectives/Systems/ChangelingObjectiveSystem.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Content.Server.Objectives.Components; -using Content.Shared.Objectives.Components; - -namespace Content.Server.Objectives.Systems; - -public sealed partial class ChangelingObjectiveSystem : EntitySystem -{ - [Dependency] private readonly NumberObjectiveSystem _number = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAbsorbGetProgress); - SubscribeLocalEvent(OnStealDNAGetProgress); - } - - private void OnAbsorbGetProgress(EntityUid uid, AbsorbConditionComponent comp, ref ObjectiveGetProgressEvent args) - { - var target = _number.GetTarget(uid); - if (target != 0) - args.Progress = MathF.Min(comp.Absorbed / target, 1f); - else args.Progress = 1f; - } - private void OnStealDNAGetProgress(EntityUid uid, StealDNAConditionComponent comp, ref ObjectiveGetProgressEvent args) - { - var target = _number.GetTarget(uid); - if (target != 0) - args.Progress = MathF.Min(comp.DNAStolen / target, 1f); - else args.Progress = 1f; - } -} diff --git a/Content.Server/Goobstation/Objectives/Systems/ImpersonateConditionSystem.cs b/Content.Server/Goobstation/Objectives/Systems/ImpersonateConditionSystem.cs deleted file mode 100644 index 086f462fb6..0000000000 --- a/Content.Server/Goobstation/Objectives/Systems/ImpersonateConditionSystem.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Content.Server.Objectives.Components; -using Content.Server.Shuttles.Systems; -using Content.Shared.Cuffs.Components; -using Content.Shared.Mind; -using Content.Shared.Objectives.Components; - -namespace Content.Server.Objectives.Systems; - -/// -/// Handles escaping on the shuttle while being another person detection. -/// -public sealed class ImpersonateConditionSystem : EntitySystem -{ - [Dependency] private readonly TargetObjectiveSystem _target = default!; - [Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAfterAssign); - SubscribeLocalEvent(OnGetProgress); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) - { - if (comp.Name == null || comp.MindId == null) - continue; - - if (!TryComp(comp.MindId, out var mind) || mind.OwnedEntity == null) - continue; - if (!TryComp(mind.CurrentEntity, out var metaData)) - continue; - - if (metaData.EntityName == comp.Name) - comp.Completed = true; - else comp.Completed = false; - } - } - - private void OnAfterAssign(EntityUid uid, ImpersonateConditionComponent comp, ref ObjectiveAfterAssignEvent args) - { - if (!_target.GetTarget(uid, out var target)) - return; - - if (!TryComp(target, out var targetMind) || targetMind.CharacterName == null) - return; - - comp.Name = targetMind.CharacterName; - comp.MindId = args.MindId; - } - - // copypasta from escape shittle objective. eh. - private void OnGetProgress(EntityUid uid, ImpersonateConditionComponent comp, ref ObjectiveGetProgressEvent args) - { - args.Progress = GetProgress(args.Mind, comp); - } - - public float GetProgress(MindComponent mind, ImpersonateConditionComponent comp) - { - // not escaping alive if you're deleted/dead - if (mind.OwnedEntity == null || _mind.IsCharacterDeadIc(mind)) - return 0f; - // You're not escaping if you're restrained! - if (TryComp(mind.OwnedEntity, out var cuffed) && cuffed.CuffedHandCount > 0) - return 0f; - - return (_emergencyShuttle.IsTargetEscaping(mind.OwnedEntity.Value) ? .5f : 0f) + (comp.Completed ? .5f : 0f); - } -} diff --git a/Content.Server/Goobstation/Roles/ChangelingRoleComponent.cs b/Content.Server/Goobstation/Roles/ChangelingRoleComponent.cs deleted file mode 100644 index a1184c6795..0000000000 --- a/Content.Server/Goobstation/Roles/ChangelingRoleComponent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Shared.Roles; - -namespace Content.Server.Roles; - -[RegisterComponent, ExclusiveAntagonist] -public sealed partial class ChangelingRoleComponent : AntagonistRoleComponent -{ -} diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs index bc39997735..fa263e059d 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -5,10 +5,10 @@ using Content.Server.Afk; using Content.Server.Chat.Managers; using Content.Server.Connection; +using Content.Server.DiscordAuth; using Content.Server.JoinQueue; using Content.Server.Database; using Content.Server.Discord; -using Content.Server.DiscordAuth; using Content.Server.EUI; using Content.Server.GhostKick; using Content.Server.Info; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/FreeProberRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/FreeProberRule.cs index 35bcb4c28b..46dff726e5 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/FreeProberRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/FreeProberRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Robust.Shared.Map; using Robust.Shared.Random; using Content.Server.GameTicking.Rules.Components; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs index bf32769c8f..078826604e 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Psionics.Glimmer; using Content.Shared.Psionics.Glimmer; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRandomSentienceRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRandomSentienceRule.cs index 578f8bf48b..c086462b40 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRandomSentienceRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRandomSentienceRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Ghost.Roles.Components; using Content.Server.Psionics; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRevenantSpawnRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRevenantSpawnRule.cs index 152d6d9fe5..8bab321db7 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRevenantSpawnRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRevenantSpawnRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Robust.Shared.Random; using Content.Server.GameTicking.Rules.Components; using Content.Server.Psionics.Glimmer; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerWispSpawnRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerWispSpawnRule.cs index 90061c501a..89b5a176f2 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerWispSpawnRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerWispSpawnRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Robust.Shared.Random; using Content.Server.GameTicking.Rules.Components; using Content.Server.NPC.Components; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs index 871705b5e8..3be2eed638 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs @@ -1,6 +1,5 @@ using Robust.Shared.Random; using Content.Server.Psionics.Abilities; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Psionics; using Content.Server.StationEvents.Components; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs index 94a488bd84..7abbdcdab3 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Random; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericFryRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericFryRule.cs index b1ec62d190..d7880af903 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericFryRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericFryRule.cs @@ -3,7 +3,6 @@ using Robust.Shared.Player; using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; -using Content.Server.GameTicking.Components; using Content.Shared.Construction.EntitySystems; using Content.Server.GameTicking.Rules.Components; using Content.Server.Popups; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericStormRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericStormRule.cs index 84407954df..021c959102 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericStormRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericStormRule.cs @@ -1,6 +1,5 @@ using Robust.Shared.Random; using Content.Server.Psionics.Abilities; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.Psionics; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs index ae85e16e9e..a11faa0693 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Popups; using Content.Server.Psionics; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs index 4b9fb6ae53..753b2e2572 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Robust.Shared.Random; using Robust.Shared.Player; using Content.Server.Psionics; diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index 47fe4eb5f8..20205b8b72 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -1,7 +1,10 @@ using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Mind; using Content.Server.Shuttles.Systems; using Content.Shared.Cuffs.Components; using Content.Shared.Mind; +using Content.Shared.Mobs.Systems; using Content.Shared.Objectives.Components; using Content.Shared.Objectives.Systems; using Content.Shared.Random; @@ -9,9 +12,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using System.Linq; -using Content.Server.GameTicking.Components; using System.Text; -using Robust.Server.Player; namespace Content.Server.Objectives; @@ -19,8 +20,8 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem { [Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!; public override void Initialize() @@ -178,9 +179,7 @@ private void AddSummary(StringBuilder result, string agent, List mind .ThenByDescending(x => x.completedObjectives); foreach (var (summary, _, _) in sortedAgents) - { result.AppendLine(summary); - } } public EntityUid? GetRandomObjective(EntityUid mindId, MindComponent mind, string objectiveGroupProto) @@ -245,14 +244,8 @@ private bool IsInCustody(EntityUid mindId, MindComponent? mind = null) return null; var name = mind.CharacterName; - var username = (string?) null; - - if (mind.OriginalOwnerUserId != null && - _player.TryGetPlayerData(mind.OriginalOwnerUserId.Value, out var sessionData)) - { - username = sessionData.UserName; - } - + _mind.TryGetSession(mindId, out var session); + var username = session?.Name; if (username != null) { diff --git a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs index 0e20f007d7..107d09c898 100644 --- a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs @@ -17,7 +17,6 @@ using Robust.Shared.Utility; using System.Linq; using System.Diagnostics.CodeAnalysis; -using Content.Server.GameTicking.Components; namespace Content.Server.Power.EntitySystems; @@ -724,8 +723,8 @@ private void GetLoadsForNode(EntityUid uid, Node node, out List> GetSelectedProfilesForPlayers(List userIds); bool HavePreferencesLoaded(ICommonSession session); } diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index c3efe14be9..e262fde64d 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -256,20 +256,6 @@ public PlayerPreferences GetPreferences(NetUserId userId) return prefs; } - /// - /// Retrieves preferences for the given username from storage or returns null. - /// Creates and saves default preferences if they are not found, then returns them. - /// - public PlayerPreferences? GetPreferencesOrNull(NetUserId? userId) - { - if (userId == null) - return null; - - if (_cachedPlayerPrefs.TryGetValue(userId.Value, out var pref)) - return pref.Prefs; - return null; - } - private async Task GetOrCreatePreferencesAsync(NetUserId userId) { var prefs = await _db.GetPlayerPreferencesAsync(userId); diff --git a/Content.Server/RandomMetadata/RandomMetadataSystem.cs b/Content.Server/RandomMetadata/RandomMetadataSystem.cs index 0c254c52ac..c088d57fd9 100644 --- a/Content.Server/RandomMetadata/RandomMetadataSystem.cs +++ b/Content.Server/RandomMetadata/RandomMetadataSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.Dataset; +using Content.Shared.Dataset; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -47,12 +47,9 @@ public string GetRandomFromSegments(List segments, string? separator) var outputSegments = new List(); foreach (var segment in segments) { - if (_prototype.TryIndex(segment, out var proto)) - outputSegments.Add(_random.Pick(proto.Values)); - else if (Loc.TryGetString(segment, out var localizedSegment)) - outputSegments.Add(localizedSegment); - else - outputSegments.Add(segment); + outputSegments.Add(_prototype.TryIndex(segment, out var proto) + ? Loc.GetString(_random.Pick(proto.Values)) + : Loc.GetString(segment)); } return string.Join(separator, outputSegments); } diff --git a/Content.Server/Roles/RoleSystem.cs b/Content.Server/Roles/RoleSystem.cs index 7b18485787..f7a5177357 100644 --- a/Content.Server/Roles/RoleSystem.cs +++ b/Content.Server/Roles/RoleSystem.cs @@ -19,7 +19,6 @@ public override void Initialize() SubscribeAntagEvents(); SubscribeAntagEvents(); SubscribeAntagEvents(); - SubscribeAntagEvents(); } public string? MindGetBriefing(EntityUid? mindId) diff --git a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs index 75f8618798..506fd61d55 100644 --- a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs +++ b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs @@ -1,6 +1,5 @@ using System.Numerics; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Spawners.Components; using JetBrains.Annotations; diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 2fdfb0fa86..07837361d6 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -174,7 +174,7 @@ public EntityUid SpawnPlayerMob( if (prototype?.StartingGear != null) { var startingGear = _prototypeManager.Index(prototype.StartingGear); - EquipStartingGear(entity.Value, startingGear); + EquipStartingGear(entity.Value, startingGear, profile); if (profile != null) EquipIdCard(entity.Value, profile.Name, prototype, station); InternalEncryptionKeySpawner.TryInsertEncryptionKey(entity.Value, startingGear, EntityManager, profile); // Parkstation - IPC diff --git a/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs b/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs index b9eb3b7b09..0243a00c9a 100644 --- a/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs +++ b/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs @@ -1,6 +1,5 @@ using System.Linq; using Content.Server.Administration; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Components/LoneOpsSpawnRuleComponent.cs b/Content.Server/StationEvents/Components/LoneOpsSpawnRuleComponent.cs new file mode 100644 index 0000000000..92911e0858 --- /dev/null +++ b/Content.Server/StationEvents/Components/LoneOpsSpawnRuleComponent.cs @@ -0,0 +1,18 @@ +using Content.Server.StationEvents.Events; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.StationEvents.Components; + +[RegisterComponent, Access(typeof(LoneOpsSpawnRule))] +public sealed partial class LoneOpsSpawnRuleComponent : Component +{ + [DataField("loneOpsShuttlePath")] + public string LoneOpsShuttlePath = "Maps/Shuttles/striker.yml"; + + [DataField("gameRuleProto", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string GameRuleProto = "Nukeops"; + + [DataField("additionalRule")] + public EntityUid? AdditionalRule; +} diff --git a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs index 98d5aa76a6..4cd94d3e71 100644 --- a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs +++ b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs @@ -1,5 +1,4 @@ using Content.Server.Anomaly; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs index 29c1897657..b25c1d6561 100644 --- a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs +++ b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs @@ -1,5 +1,4 @@ -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Random; using Content.Server.Announcements.Systems; diff --git a/Content.Server/StationEvents/Events/BluespaceLockerRule.cs b/Content.Server/StationEvents/Events/BluespaceLockerRule.cs index eef9850e73..709b750334 100644 --- a/Content.Server/StationEvents/Events/BluespaceLockerRule.cs +++ b/Content.Server/StationEvents/Events/BluespaceLockerRule.cs @@ -1,5 +1,4 @@ -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Server.Resist; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/BreakerFlipRule.cs b/Content.Server/StationEvents/Events/BreakerFlipRule.cs index 3b2368556b..e7574f27ad 100644 --- a/Content.Server/StationEvents/Events/BreakerFlipRule.cs +++ b/Content.Server/StationEvents/Events/BreakerFlipRule.cs @@ -1,5 +1,4 @@ -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Station.Components; diff --git a/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs b/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs index 282e28e499..feb88d9b84 100644 --- a/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs +++ b/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.Station.Systems; diff --git a/Content.Server/StationEvents/Events/CargoGiftsRule.cs b/Content.Server/StationEvents/Events/CargoGiftsRule.cs index 62f01f58fe..80af23c6fa 100644 --- a/Content.Server/StationEvents/Events/CargoGiftsRule.cs +++ b/Content.Server/StationEvents/Events/CargoGiftsRule.cs @@ -2,7 +2,6 @@ using Content.Server.Cargo.Components; using Content.Server.Cargo.Systems; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/ClericalErrorRule.cs b/Content.Server/StationEvents/Events/ClericalErrorRule.cs index 854ee685b3..dd4473952c 100644 --- a/Content.Server/StationEvents/Events/ClericalErrorRule.cs +++ b/Content.Server/StationEvents/Events/ClericalErrorRule.cs @@ -1,5 +1,4 @@ -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.StationRecords; using Content.Server.StationRecords.Systems; diff --git a/Content.Server/StationEvents/Events/FalseAlarmRule.cs b/Content.Server/StationEvents/Events/FalseAlarmRule.cs index 2d129b3558..cd434a721b 100644 --- a/Content.Server/StationEvents/Events/FalseAlarmRule.cs +++ b/Content.Server/StationEvents/Events/FalseAlarmRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using JetBrains.Annotations; diff --git a/Content.Server/StationEvents/Events/GasLeakRule.cs b/Content.Server/StationEvents/Events/GasLeakRule.cs index 1221612171..68544e416c 100644 --- a/Content.Server/StationEvents/Events/GasLeakRule.cs +++ b/Content.Server/StationEvents/Events/GasLeakRule.cs @@ -1,5 +1,4 @@ using Content.Server.Atmos.EntitySystems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Audio; diff --git a/Content.Server/StationEvents/Events/ImmovableRodRule.cs b/Content.Server/StationEvents/Events/ImmovableRodRule.cs index 781d0368f4..a61c6b69e1 100644 --- a/Content.Server/StationEvents/Events/ImmovableRodRule.cs +++ b/Content.Server/StationEvents/Events/ImmovableRodRule.cs @@ -1,5 +1,4 @@ using System.Numerics; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.ImmovableRod; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/IonStormRule.cs b/Content.Server/StationEvents/Events/IonStormRule.cs index 8361cc6048..cd3cd63ae8 100644 --- a/Content.Server/StationEvents/Events/IonStormRule.cs +++ b/Content.Server/StationEvents/Events/IonStormRule.cs @@ -1,5 +1,5 @@ -using Content.Server.GameTicking.Components; using System.Linq; +using Content.Server.GameTicking.Rules.Components; using Content.Server.Silicons.Laws; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/KudzuGrowthRule.cs b/Content.Server/StationEvents/Events/KudzuGrowthRule.cs index 5b56e03846..3fa12cd4e9 100644 --- a/Content.Server/StationEvents/Events/KudzuGrowthRule.cs +++ b/Content.Server/StationEvents/Events/KudzuGrowthRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/LoneOpsSpawnRule.cs b/Content.Server/StationEvents/Events/LoneOpsSpawnRule.cs new file mode 100644 index 0000000000..4b15e59099 --- /dev/null +++ b/Content.Server/StationEvents/Events/LoneOpsSpawnRule.cs @@ -0,0 +1,47 @@ +using Robust.Server.GameObjects; +using Robust.Server.Maps; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.StationEvents.Components; +using Content.Server.RoundEnd; + +namespace Content.Server.StationEvents.Events; + +public sealed class LoneOpsSpawnRule : StationEventSystem +{ + [Dependency] private readonly MapLoaderSystem _map = default!; + + protected override void Started(EntityUid uid, LoneOpsSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + // Loneops can only spawn if there is no nukeops active + if (GameTicker.IsGameRuleAdded()) + { + ForceEndSelf(uid, gameRule); + return; + } + + var shuttleMap = MapManager.CreateMap(); + var options = new MapLoadOptions + { + LoadMap = true, + }; + + _map.TryLoad(shuttleMap, component.LoneOpsShuttlePath, out _, options); + + var nukeopsEntity = GameTicker.AddGameRule(component.GameRuleProto); + component.AdditionalRule = nukeopsEntity; + var nukeopsComp = Comp(nukeopsEntity); + nukeopsComp.SpawnOutpost = false; + nukeopsComp.RoundEndBehavior = RoundEndBehavior.Nothing; + GameTicker.StartGameRule(nukeopsEntity); + } + + protected override void Ended(EntityUid uid, LoneOpsSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args) + { + base.Ended(uid, component, gameRule, args); + + if (component.AdditionalRule != null) + GameTicker.EndGameRule(component.AdditionalRule.Value); + } +} diff --git a/Content.Server/StationEvents/Events/MassHallucinationsRule.cs b/Content.Server/StationEvents/Events/MassHallucinationsRule.cs index 2239db7f70..4fc158f864 100644 --- a/Content.Server/StationEvents/Events/MassHallucinationsRule.cs +++ b/Content.Server/StationEvents/Events/MassHallucinationsRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.Traits.Assorted; diff --git a/Content.Server/StationEvents/Events/MeteorSwarmRule.cs b/Content.Server/StationEvents/Events/MeteorSwarmRule.cs index 455011259d..ad56479b37 100644 --- a/Content.Server/StationEvents/Events/MeteorSwarmRule.cs +++ b/Content.Server/StationEvents/Events/MeteorSwarmRule.cs @@ -1,5 +1,4 @@ using System.Numerics; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Map; diff --git a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs index d9d68a386c..8ad5c8602e 100644 --- a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs +++ b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Ninja.Systems; using Content.Server.Station.Components; diff --git a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs index b0a0bbc9fe..97e8948461 100644 --- a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs +++ b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs @@ -1,5 +1,4 @@ using System.Threading; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; diff --git a/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs b/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs index 87d50fc8b2..c3cd719cc4 100644 --- a/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs +++ b/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.Storage.Components; diff --git a/Content.Server/StationEvents/Events/RandomSentienceRule.cs b/Content.Server/StationEvents/Events/RandomSentienceRule.cs index 7b9173241f..f667ad7975 100644 --- a/Content.Server/StationEvents/Events/RandomSentienceRule.cs +++ b/Content.Server/StationEvents/Events/RandomSentienceRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Ghost.Roles.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/RandomSpawnRule.cs b/Content.Server/StationEvents/Events/RandomSpawnRule.cs index 77744d44e4..c514acc623 100644 --- a/Content.Server/StationEvents/Events/RandomSpawnRule.cs +++ b/Content.Server/StationEvents/Events/RandomSpawnRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/SolarFlareRule.cs b/Content.Server/StationEvents/Events/SolarFlareRule.cs index 0370b4ee61..a4ec74b43b 100644 --- a/Content.Server/StationEvents/Events/SolarFlareRule.cs +++ b/Content.Server/StationEvents/Events/SolarFlareRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Radio; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/Events/StationEventSystem.cs b/Content.Server/StationEvents/Events/StationEventSystem.cs index 257babd0d2..6de8024bd0 100644 --- a/Content.Server/StationEvents/Events/StationEventSystem.cs +++ b/Content.Server/StationEvents/Events/StationEventSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Administration.Logs; using Content.Server.Chat.Systems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Systems; diff --git a/Content.Server/StationEvents/Events/VentClogRule.cs b/Content.Server/StationEvents/Events/VentClogRule.cs index 867f41dccc..e263a5f4f6 100644 --- a/Content.Server/StationEvents/Events/VentClogRule.cs +++ b/Content.Server/StationEvents/Events/VentClogRule.cs @@ -6,7 +6,6 @@ using Robust.Shared.Random; using System.Linq; using Content.Server.Fluids.EntitySystems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/VentCrittersRule.cs b/Content.Server/StationEvents/Events/VentCrittersRule.cs index c2605039bc..cdcf2bf6ff 100644 --- a/Content.Server/StationEvents/Events/VentCrittersRule.cs +++ b/Content.Server/StationEvents/Events/VentCrittersRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.StationEvents.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; diff --git a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs index a6c38ef765..aa0c9b214b 100644 --- a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs +++ b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs @@ -1,5 +1,4 @@ using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/Traitor/Components/AutoTraitorComponent.cs b/Content.Server/Traitor/Components/AutoTraitorComponent.cs index 473441ccec..ab4bee2f26 100644 --- a/Content.Server/Traitor/Components/AutoTraitorComponent.cs +++ b/Content.Server/Traitor/Components/AutoTraitorComponent.cs @@ -11,12 +11,12 @@ public sealed partial class AutoTraitorComponent : Component /// /// Whether to give the traitor an uplink or not. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField("giveUplink"), ViewVariables(VVAccess.ReadWrite)] public bool GiveUplink = true; /// /// Whether to give the traitor objectives or not. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField("giveObjectives"), ViewVariables(VVAccess.ReadWrite)] public bool GiveObjectives = true; } diff --git a/Content.Server/Traitor/Systems/AutoTraitorSystem.cs b/Content.Server/Traitor/Systems/AutoTraitorSystem.cs index e9307effbc..15deae2552 100644 --- a/Content.Server/Traitor/Systems/AutoTraitorSystem.cs +++ b/Content.Server/Traitor/Systems/AutoTraitorSystem.cs @@ -1,7 +1,6 @@ -using Content.Server.Antag; +using Content.Server.GameTicking.Rules; using Content.Server.Traitor.Components; using Content.Shared.Mind.Components; -using Robust.Shared.Prototypes; namespace Content.Server.Traitor.Systems; @@ -10,10 +9,7 @@ namespace Content.Server.Traitor.Systems; /// public sealed class AutoTraitorSystem : EntitySystem { - [Dependency] private readonly AntagSelectionSystem _antag = default!; - - [ValidatePrototypeId] - private const string DefaultTraitorRule = "Traitor"; + [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; public override void Initialize() { @@ -24,6 +20,44 @@ public override void Initialize() private void OnMindAdded(EntityUid uid, AutoTraitorComponent comp, MindAddedMessage args) { - _antag.ForceMakeAntag(args.Mind.Comp.Session, DefaultTraitorRule); + TryMakeTraitor(uid, comp); + } + + /// + /// Sets the GiveUplink field. + /// + public void SetGiveUplink(EntityUid uid, bool giveUplink, AutoTraitorComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + + comp.GiveUplink = giveUplink; + } + + /// + /// Sets the GiveObjectives field. + /// + public void SetGiveObjectives(EntityUid uid, bool giveObjectives, AutoTraitorComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + + comp.GiveObjectives = giveObjectives; + } + + /// + /// Checks if there is a mind, then makes it a traitor using the options. + /// + public bool TryMakeTraitor(EntityUid uid, AutoTraitorComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return false; + + //Start the rule if it has not already been started + var traitorRuleComponent = _traitorRule.StartGameRule(); + _traitorRule.MakeTraitor(uid, traitorRuleComponent, giveUplink: comp.GiveUplink, giveObjectives: comp.GiveObjectives); + // prevent spamming anything if it fails + RemComp(uid); + return true; } } diff --git a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs index 79192f6b49..cdaed3f928 100644 --- a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs +++ b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs @@ -83,9 +83,12 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) uplinkEntity = eUid; } + // Get TC count + var tcCount = _cfgManager.GetCVar(CCVars.TraitorStartingBalance); + Logger.Debug(_entManager.ToPrettyString(user)); // Finally add uplink var uplinkSys = _entManager.System(); - if (!uplinkSys.AddUplink(user, 20, uplinkEntity: uplinkEntity)) + if (!uplinkSys.AddUplink(user, FixedPoint2.New(tcCount), uplinkEntity: uplinkEntity)) { shell.WriteLine(Loc.GetString("add-uplink-command-error-2")); } diff --git a/Content.Server/Zombies/PendingZombieComponent.cs b/Content.Server/Zombies/PendingZombieComponent.cs index 1bb0ef2872..a49b424c53 100644 --- a/Content.Server/Zombies/PendingZombieComponent.cs +++ b/Content.Server/Zombies/PendingZombieComponent.cs @@ -1,5 +1,4 @@ using Content.Shared.Damage; -using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Zombies; @@ -36,21 +35,6 @@ public sealed partial class PendingZombieComponent : Component [DataField("gracePeriod"), ViewVariables(VVAccess.ReadWrite)] public TimeSpan GracePeriod = TimeSpan.Zero; - /// - /// The minimum amount of time initial infected have before they start taking infection damage. - /// - [DataField] - public TimeSpan MinInitialInfectedGrace = TimeSpan.FromMinutes(12.5f); - - /// - /// The maximum amount of time initial infected have before they start taking damage. - /// - [DataField] - public TimeSpan MaxInitialInfectedGrace = TimeSpan.FromMinutes(15f); - - [DataField] - public EntProtoId ZombifySelfActionPrototype = "ActionTurnUndead"; - /// /// The chance each second that a warning will be shown. /// diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs index 09c8fa26db..080bef44e7 100644 --- a/Content.Server/Zombies/ZombieSystem.cs +++ b/Content.Server/Zombies/ZombieSystem.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.Actions; using Content.Server.Body.Systems; using Content.Server.Chat; using Content.Server.Chat.Systems; @@ -31,7 +30,6 @@ public sealed partial class ZombieSystem : SharedZombieSystem [Dependency] private readonly BloodstreamSystem _bloodstream = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly AutoEmoteSystem _autoEmote = default!; [Dependency] private readonly EmoteOnDamageSystem _emoteOnDamage = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; @@ -76,8 +74,6 @@ private void OnPendingMapInit(EntityUid uid, PendingZombieComponent component, M } component.NextTick = _timing.CurTime + TimeSpan.FromSeconds(1f); - component.GracePeriod = _random.Next(component.MinInitialInfectedGrace, component.MaxInitialInfectedGrace); - _actions.AddAction(uid, ref component.Action, component.ZombifySelfActionPrototype); } public override void Update(float frameTime) diff --git a/Content.Shared/Actions/ActionContainerSystem.cs b/Content.Shared/Actions/ActionContainerSystem.cs index dec57bdcc7..1c5a3ba0d9 100644 --- a/Content.Shared/Actions/ActionContainerSystem.cs +++ b/Content.Shared/Actions/ActionContainerSystem.cs @@ -237,19 +237,6 @@ public bool AddAction(EntityUid uid, EntityUid actionId, BaseActionComponent? ac DebugTools.AssertOwner(uid, comp); comp ??= EnsureComp(uid); - - // goobstation - changelings - if (!TryComp(actionId, out var actionData)) - return false; - if (!TryPrototype(actionId, out var actionProto, actionData)) - return false; - - if (HasAction(uid, actionProto.ID)) - { - Log.Debug($"Tried to insert action {ToPrettyString(actionId)} into {ToPrettyString(uid)}. Failed due to duplicate actions."); - return false; - } - if (!_container.Insert(actionId, comp.Container)) { Log.Error($"Failed to insert action {ToPrettyString(actionId)} into {ToPrettyString(uid)}"); @@ -263,21 +250,6 @@ public bool AddAction(EntityUid uid, EntityUid actionId, BaseActionComponent? ac return true; } - // goobstation - changelings - public bool HasAction(EntityUid uid, string pId, ActionsContainerComponent? actions = null) - { - if (!Resolve(uid, ref actions, false)) - return false; - - foreach (var act in actions.Container.ContainedEntities) - if (TryComp(act, out var metaData)) - if (TryPrototype(act, out var actProto, metaData)) - if (pId == actProto.ID) - return true; - - return false; - } - /// /// Removes an action from its container and any action-performer and moves the action to null-space /// diff --git a/Content.Shared/Actions/ActionEvents.cs b/Content.Shared/Actions/ActionEvents.cs index 1f1e07f3aa..72a566b8c8 100644 --- a/Content.Shared/Actions/ActionEvents.cs +++ b/Content.Shared/Actions/ActionEvents.cs @@ -154,9 +154,4 @@ public abstract partial class BaseActionEvent : HandledEntityEventArgs /// The user performing the action. /// public EntityUid Performer; - - /// - /// The action that was performed. - /// - public EntityUid Action; } diff --git a/Content.Shared/Actions/BaseActionComponent.cs b/Content.Shared/Actions/BaseActionComponent.cs index 57c145a0ec..6d9242acc1 100644 --- a/Content.Shared/Actions/BaseActionComponent.cs +++ b/Content.Shared/Actions/BaseActionComponent.cs @@ -1,4 +1,5 @@ -using Robust.Shared.Audio; +using Content.Shared.Mobs; +using Robust.Shared.Audio; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -24,11 +25,6 @@ public abstract partial class BaseActionComponent : Component /// [DataField("iconOn")] public SpriteSpecifier? IconOn; - /// - /// For toggle actions only, background to show when toggled on. - /// - [DataField] public SpriteSpecifier? BackgroundOn; - /// /// If not null, this color will modulate the action icon color. /// diff --git a/Content.Shared/Actions/Events/ActionPerformedEvent.cs b/Content.Shared/Actions/Events/ActionPerformedEvent.cs deleted file mode 100644 index 530d7c9335..0000000000 --- a/Content.Shared/Actions/Events/ActionPerformedEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Content.Shared.Actions.Events; - -/// -/// Raised on the action entity when it is used and . -/// -/// The entity that performed this action. -[ByRefEvent] -public readonly record struct ActionPerformedEvent(EntityUid Performer); diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 538726c89d..9f3fb96410 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -145,6 +145,9 @@ public bool ResolveActionData( public void SetCooldown(EntityUid? actionId, TimeSpan start, TimeSpan end) { + if (actionId == null) + return; + if (!TryGetActionData(actionId, out var action)) return; @@ -160,6 +163,9 @@ public void SetCooldown(EntityUid? actionId, TimeSpan cooldown) public void ClearCooldown(EntityUid? actionId) { + if (actionId == null) + return; + if (!TryGetActionData(actionId, out var action)) return; @@ -170,27 +176,6 @@ public void ClearCooldown(EntityUid? actionId) Dirty(actionId.Value, action); } - /// - /// Sets the cooldown for this action only if it is bigger than the one it already has. - /// - public void SetIfBiggerCooldown(EntityUid? actionId, TimeSpan? cooldown) - { - if (cooldown == null || - cooldown.Value <= TimeSpan.Zero || - !TryGetActionData(actionId, out var action)) - { - return; - } - - var start = GameTiming.CurTime; - var end = start + cooldown; - if (action.Cooldown?.End > end) - return; - - action.Cooldown = (start, end.Value); - Dirty(actionId.Value, action); - } - public void StartUseDelay(EntityUid? actionId) { if (actionId == null) @@ -454,10 +439,7 @@ private void OnActionRequest(RequestPerformActionEvent ev, EntitySessionEventArg } if (performEvent != null) - { performEvent.Performer = user; - performEvent.Action = actionEnt; - } // All checks passed. Perform the action! PerformAction(user, component, actionEnt, action, performEvent, curTime); @@ -579,9 +561,6 @@ public void PerformAction(EntityUid performer, ActionsComponent? component, Enti if (dirty && component != null) Dirty(performer, component); - - var ev = new ActionPerformedEvent(performer); - RaiseLocalEvent(actionId, ref ev); } #endregion diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs index 30887d2070..113c96db04 100644 --- a/Content.Shared/Alert/AlertType.cs +++ b/Content.Shared/Alert/AlertType.cs @@ -56,8 +56,6 @@ public enum AlertType : byte BorgDead, Charge, Offer, - ChangelingChemicals, - ChangelingBiomass, } } diff --git a/Content.Shared/Antag/AntagAcceptability.cs b/Content.Shared/Antag/AntagAcceptability.cs index 02d0b5f58f..98abe713eb 100644 --- a/Content.Shared/Antag/AntagAcceptability.cs +++ b/Content.Shared/Antag/AntagAcceptability.cs @@ -20,8 +20,3 @@ public enum AntagAcceptability All } -public enum AntagSelectionTime : byte -{ - PrePlayerSpawn, - PostPlayerSpawn -} diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 3a5c8b2b3f..70e3f27d76 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -537,6 +537,91 @@ public static readonly CVarDef public static readonly CVarDef DiscordAuthApiKey = CVarDef.Create("discord.auth_api_key", "", CVar.SERVERONLY | CVar.CONFIDENTIAL); + + /* + * Suspicion + */ + + public static readonly CVarDef SuspicionMinPlayers = + CVarDef.Create("suspicion.min_players", 5); + + public static readonly CVarDef SuspicionMinTraitors = + CVarDef.Create("suspicion.min_traitors", 2); + + public static readonly CVarDef SuspicionPlayersPerTraitor = + CVarDef.Create("suspicion.players_per_traitor", 6); + + public static readonly CVarDef SuspicionStartingBalance = + CVarDef.Create("suspicion.starting_balance", 20); + + public static readonly CVarDef SuspicionMaxTimeSeconds = + CVarDef.Create("suspicion.max_time_seconds", 300); + + /* + * Traitor + */ + + public static readonly CVarDef TraitorMinPlayers = + CVarDef.Create("traitor.min_players", 5); + + public static readonly CVarDef TraitorMaxTraitors = + CVarDef.Create("traitor.max_traitors", 12); // Assuming average server maxes somewhere from like 50-80 people + + public static readonly CVarDef TraitorPlayersPerTraitor = + CVarDef.Create("traitor.players_per_traitor", 10); + + public static readonly CVarDef TraitorCodewordCount = + CVarDef.Create("traitor.codeword_count", 4); + + public static readonly CVarDef TraitorStartingBalance = + CVarDef.Create("traitor.starting_balance", 20); + + public static readonly CVarDef TraitorMaxDifficulty = + CVarDef.Create("traitor.max_difficulty", 5); + + public static readonly CVarDef TraitorMaxPicks = + CVarDef.Create("traitor.max_picks", 20); + + public static readonly CVarDef TraitorStartDelay = + CVarDef.Create("traitor.start_delay", 4f * 60f); + + public static readonly CVarDef TraitorStartDelayVariance = + CVarDef.Create("traitor.start_delay_variance", 3f * 60f); + + /* + * TraitorDeathMatch + */ + + public static readonly CVarDef TraitorDeathMatchStartingBalance = + CVarDef.Create("traitordm.starting_balance", 20); + + /* + * Zombie + */ + + public static readonly CVarDef ZombieMinPlayers = + CVarDef.Create("zombie.min_players", 20); + + /* + * Pirates + */ + + public static readonly CVarDef PiratesMinPlayers = + CVarDef.Create("pirates.min_players", 25); + + public static readonly CVarDef PiratesMaxOps = + CVarDef.Create("pirates.max_pirates", 6); + + public static readonly CVarDef PiratesPlayersPerOp = + CVarDef.Create("pirates.players_per_pirate", 5); + + /* + * Nukeops + */ + + public static readonly CVarDef NukeopsSpawnGhostRoles = + CVarDef.Create("nukeops.spawn_ghost_roles", false); + /* * Tips */ diff --git a/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs b/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs index 08ca204372..e7a0eef80e 100644 --- a/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs +++ b/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs @@ -34,7 +34,7 @@ private void OnMapInit(EntityUid uid, LoadoutComponent component, MapInitEvent a return; var proto = _prototype.Index(_random.Pick(component.Prototypes)); - _station.EquipStartingGear(uid, proto); + _station.EquipStartingGear(uid, proto, null); } diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index bd82b9fcd4..9752bfcfe2 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -26,9 +26,6 @@ - - - diff --git a/Content.Shared/Goobstation/Changeling/AbsorbableComponent.cs b/Content.Shared/Goobstation/Changeling/AbsorbableComponent.cs deleted file mode 100644 index 57fa59d1b1..0000000000 --- a/Content.Shared/Goobstation/Changeling/AbsorbableComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Changeling; - -/// -/// Component that indicates that a person can be absorbed by a changeling. -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class AbsorbableComponent : Component -{ - -} diff --git a/Content.Shared/Goobstation/Changeling/AbsorbedComponent.cs b/Content.Shared/Goobstation/Changeling/AbsorbedComponent.cs deleted file mode 100644 index 36bed5222a..0000000000 --- a/Content.Shared/Goobstation/Changeling/AbsorbedComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Changeling; - - -/// -/// Component that indicates that a person's DNA has been absorbed by a changeling. -/// -[RegisterComponent, NetworkedComponent, Access(typeof(AbsorbedSystem))] -public sealed partial class AbsorbedComponent : Component -{ - -} diff --git a/Content.Shared/Goobstation/Changeling/AbsorbedSystem.cs b/Content.Shared/Goobstation/Changeling/AbsorbedSystem.cs deleted file mode 100644 index 0496dc63be..0000000000 --- a/Content.Shared/Goobstation/Changeling/AbsorbedSystem.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Content.Shared.Examine; -using Content.Shared.Mobs; - -namespace Content.Shared.Changeling; - -public sealed partial class AbsorbedSystem : EntitySystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnExamine); - SubscribeLocalEvent(OnMobStateChange); - } - - private void OnExamine(Entity ent, ref ExaminedEvent args) - { - args.PushMarkup(Loc.GetString("changeling-absorb-onexamine")); - } - - private void OnMobStateChange(Entity ent, ref MobStateChangedEvent args) - { - // in case one somehow manages to dehusk someone - if (args.NewMobState != MobState.Dead) - RemComp(ent); - } -} diff --git a/Content.Shared/Goobstation/Changeling/Changeling.Actions.cs b/Content.Shared/Goobstation/Changeling/Changeling.Actions.cs deleted file mode 100644 index ef2406b405..0000000000 --- a/Content.Shared/Goobstation/Changeling/Changeling.Actions.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Content.Shared.Actions; -using Robust.Shared.GameStates; - -namespace Content.Shared.Changeling; - -[RegisterComponent, NetworkedComponent] -public sealed partial class ChangelingActionComponent : Component -{ - [DataField] public bool RequireBiomass = true; - - [DataField] public float ChemicalCost = 0; - - [DataField] public float BiomassCost = 0; - - [DataField] public bool UseInLesserForm = false; - - [DataField] public float RequireAbsorbed = 0; -} - -#region Events - Basic - -public sealed partial class OpenEvolutionMenuEvent : InstantActionEvent { } -public sealed partial class AbsorbDNAEvent : EntityTargetActionEvent { } -public sealed partial class StingExtractDNAEvent : EntityTargetActionEvent { } -public sealed partial class ChangelingTransformCycleEvent : InstantActionEvent { } -public sealed partial class ChangelingTransformEvent : InstantActionEvent { } -public sealed partial class EnterStasisEvent : InstantActionEvent { } -public sealed partial class ExitStasisEvent : InstantActionEvent { } - -#endregion - -#region Events - Combat - -public sealed partial class ToggleArmbladeEvent : InstantActionEvent { } -public sealed partial class CreateBoneShardEvent : InstantActionEvent { } -public sealed partial class ToggleChitinousArmorEvent : InstantActionEvent { } -public sealed partial class ToggleOrganicShieldEvent : InstantActionEvent { } -public sealed partial class ShriekDissonantEvent : InstantActionEvent { } -public sealed partial class ShriekResonantEvent : InstantActionEvent { } -public sealed partial class ToggleStrainedMusclesEvent : InstantActionEvent { } - -#endregion - -#region Events - Sting - -public sealed partial class StingBlindEvent : EntityTargetActionEvent { } -public sealed partial class StingCryoEvent : EntityTargetActionEvent { } -public sealed partial class StingLethargicEvent : EntityTargetActionEvent { } -public sealed partial class StingMuteEvent : EntityTargetActionEvent { } -public sealed partial class StingFakeArmbladeEvent : EntityTargetActionEvent { } -public sealed partial class StingTransformEvent : EntityTargetActionEvent { } - -#endregion - -#region Events - Utility - -public sealed partial class ActionAnatomicPanaceaEvent : InstantActionEvent { } -public sealed partial class ActionAugmentedEyesightEvent : InstantActionEvent { } -public sealed partial class ActionBiodegradeEvent : InstantActionEvent { } -public sealed partial class ActionChameleonSkinEvent : InstantActionEvent { } -public sealed partial class ActionEphedrineOverdoseEvent : InstantActionEvent { } -public sealed partial class ActionFleshmendEvent : InstantActionEvent { } -public sealed partial class ActionLastResortEvent : InstantActionEvent { } -public sealed partial class ActionLesserFormEvent : InstantActionEvent { } -public sealed partial class ActionSpacesuitEvent : InstantActionEvent { } -public sealed partial class ActionHivemindAccessEvent : InstantActionEvent { } -public sealed partial class ActionContortBodyEvent : InstantActionEvent { } - -#endregion diff --git a/Content.Shared/Goobstation/Changeling/Changeling.DoAfter.cs b/Content.Shared/Goobstation/Changeling/Changeling.DoAfter.cs deleted file mode 100644 index 4138052954..0000000000 --- a/Content.Shared/Goobstation/Changeling/Changeling.DoAfter.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Content.Shared.DoAfter; -using Robust.Shared.Serialization; - -namespace Content.Shared.Changeling; - -[Serializable, NetSerializable] -public sealed partial class AbsorbDNADoAfterEvent : SimpleDoAfterEvent { } diff --git a/Content.Shared/Goobstation/Changeling/ChangelingComponent.cs b/Content.Shared/Goobstation/Changeling/ChangelingComponent.cs deleted file mode 100644 index 5eaa37b5e7..0000000000 --- a/Content.Shared/Goobstation/Changeling/ChangelingComponent.cs +++ /dev/null @@ -1,161 +0,0 @@ -using Content.Shared.Humanoid; -using Content.Shared.StatusIcon; -using Robust.Shared.Audio; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Changeling; - -[RegisterComponent, NetworkedComponent] -[AutoGenerateComponentState] -public sealed partial class ChangelingComponent : Component -{ - #region Prototypes - - [DataField("soundMeatPool")] - public List SoundPool = new() - { - new SoundPathSpecifier("/Audio/Effects/gib1.ogg"), - new SoundPathSpecifier("/Audio/Effects/gib2.ogg"), - new SoundPathSpecifier("/Audio/Effects/gib3.ogg"), - }; - - [DataField("soundShriek")] - public SoundSpecifier ShriekSound = new SoundPathSpecifier("/Audio/Goobstation/Changeling/Effects/changeling_shriek.ogg"); - - [DataField("shriekPower")] - public float ShriekPower = 2.5f; - - public readonly List> BaseChangelingActions = new() - { - "ActionEvolutionMenu", - "ActionAbsorbDNA", - "ActionStingExtractDNA", - "ActionChangelingTransformCycle", - "ActionChangelingTransform", - "ActionEnterStasis", - "ActionExitStasis" - }; - - /// - /// The status icon corresponding to the Changlings. - /// - - [DataField, ViewVariables(VVAccess.ReadOnly)] - public ProtoId StatusIcon { get; set; } = "HivemindFaction"; - - #endregion - - public bool IsInStasis = false; - - public bool StrainedMusclesActive = false; - - public bool IsInLesserForm = false; - - - public Dictionary Equipment = new(); - - /// - /// Amount of biomass changeling currently has. - /// - [DataField, AutoNetworkedField] - public float Biomass = 30f; - - /// - /// Maximum amount of biomass a changeling can have. - /// - [DataField, AutoNetworkedField] - public float MaxBiomass = 30f; - - /// - /// How much biomass should be removed per cycle. - /// - [DataField, AutoNetworkedField] - public float BiomassDrain = 1f; - - /// - /// Current amount of chemicals changeling currently has. - /// - [DataField, AutoNetworkedField] - public float Chemicals = 100f; - - /// - /// Maximum amount of chemicals changeling can have. - /// - [DataField, AutoNetworkedField] - public float MaxChemicals = 100f; - - /// - /// Bonus chemicals regeneration. In case - /// - [DataField, AutoNetworkedField] - public float BonusChemicalRegen = 0f; - - /// - /// Cooldown between chem regen events. - /// - public TimeSpan UpdateTimer = TimeSpan.Zero; - public float UpdateCooldown = 1f; - - public float BiomassUpdateTimer = 0f; - public float BiomassUpdateCooldown = 60f; - - [ViewVariables(VVAccess.ReadOnly)] - public List AbsorbedDNA = new(); - /// - /// Index of . Used for switching forms. - /// - [ViewVariables(VVAccess.ReadOnly)] - public int AbsorbedDNAIndex = 0; - - /// - /// Maximum amount of DNA a changeling can absorb. - /// - public int MaxAbsorbedDNA = 5; - - /// - /// Total absorbed DNA. Counts towards objectives. - /// - [ViewVariables(VVAccess.ReadWrite)] - public int TotalAbsorbedEntities = 0; - - /// - /// Total stolen DNA. Counts towards objectives. - /// - [ViewVariables(VVAccess.ReadWrite)] - public int TotalStolenDNA = 0; - - [ViewVariables(VVAccess.ReadOnly)] - public TransformData? CurrentForm; - - [ViewVariables(VVAccess.ReadOnly)] - public TransformData? SelectedForm; -} - -[DataDefinition] -public sealed partial class TransformData -{ - /// - /// Entity's name. - /// - [DataField] - public string Name; - - /// - /// Entity's fingerprint, if it exists. - /// - [DataField] - public string? Fingerprint; - - /// - /// Entity's DNA. - /// - [DataField("dna")] - public string DNA; - - /// - /// Entity's humanoid appearance component. - /// - [ViewVariables(VVAccess.ReadOnly), NonSerialized] - public HumanoidAppearanceComponent Appearance; -} diff --git a/Content.Shared/Goobstation/Changeling/HivemindComponent.cs b/Content.Shared/Goobstation/Changeling/HivemindComponent.cs deleted file mode 100644 index bc4e821726..0000000000 --- a/Content.Shared/Goobstation/Changeling/HivemindComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Changeling; - -/// -/// Used for identifying other changelings. -/// Indicates that a changeling has bought the hivemind access ability. -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class HivemindComponent : Component -{ -} diff --git a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs index ce49f80af3..ece4b59e91 100644 --- a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs +++ b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs @@ -328,11 +328,8 @@ public void SetScale(EntityUid uid, Vector2 scale, bool sync = true, HumanoidApp /// The mob's entity UID. /// The character profile to load. /// Humanoid component of the entity - public virtual void LoadProfile(EntityUid uid, HumanoidCharacterProfile? profile, HumanoidAppearanceComponent? humanoid = null) + public virtual void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, HumanoidAppearanceComponent? humanoid = null) { - if (profile == null) - return; - if (!Resolve(uid, ref humanoid)) { return; diff --git a/Content.Shared/Inventory/InventorySystem.Helpers.cs b/Content.Shared/Inventory/InventorySystem.Helpers.cs index 7e325abe21..811387d375 100644 --- a/Content.Shared/Inventory/InventorySystem.Helpers.cs +++ b/Content.Shared/Inventory/InventorySystem.Helpers.cs @@ -1,6 +1,8 @@ using System.Diagnostics.CodeAnalysis; +using System.Linq; using Content.Shared.Hands.Components; using Content.Shared.Storage.EntitySystems; +using Robust.Shared.Containers; using Robust.Shared.Prototypes; namespace Content.Shared.Inventory; @@ -94,7 +96,7 @@ bool DeleteItem() /// /// The entity that you want to spawn an item on /// A list of prototype IDs that you want to spawn in the bag. - public void SpawnItemsOnEntity(EntityUid entity, List items) + public void SpawnItemsOnEntity(EntityUid entity, List items) { foreach (var item in items) { diff --git a/Content.Shared/NukeOps/NukeOperativeComponent.cs b/Content.Shared/NukeOps/NukeOperativeComponent.cs index d19f0ae3e9..cdbefece9d 100644 --- a/Content.Shared/NukeOps/NukeOperativeComponent.cs +++ b/Content.Shared/NukeOps/NukeOperativeComponent.cs @@ -13,9 +13,14 @@ namespace Content.Shared.NukeOps; [RegisterComponent, NetworkedComponent] public sealed partial class NukeOperativeComponent : Component { + /// + /// Path to antagonist alert sound. + /// + [DataField("greetSoundNotification")] + public SoundSpecifier GreetSoundNotification = new SoundPathSpecifier("/Audio/Ambience/Antag/nukeops_start.ogg"); /// - /// + /// /// [DataField("syndStatusIcon", customTypeSerializer: typeof(PrototypeIdSerializer))] public string SyndStatusIcon = "SyndicateFaction"; diff --git a/Content.Shared/Roles/SharedRoleSystem.cs b/Content.Shared/Roles/SharedRoleSystem.cs index 94ad32164b..e8053e4c67 100644 --- a/Content.Shared/Roles/SharedRoleSystem.cs +++ b/Content.Shared/Roles/SharedRoleSystem.cs @@ -1,4 +1,3 @@ -using System.Linq; using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Mind; @@ -63,64 +62,6 @@ protected void SubscribeAntagEvents() where T : AntagonistRoleComponent _antagTypes.Add(typeof(T)); } - public void MindAddRoles(EntityUid mindId, ComponentRegistry components, MindComponent? mind = null, bool silent = false) - { - if (!Resolve(mindId, ref mind)) - return; - - EntityManager.AddComponents(mindId, components); - var antagonist = false; - foreach (var compReg in components.Values) - { - var compType = compReg.Component.GetType(); - - var comp = EntityManager.ComponentFactory.GetComponent(compType); - if (IsAntagonistRole(comp.GetType())) - { - antagonist = true; - break; - } - } - - var mindEv = new MindRoleAddedEvent(silent); - RaiseLocalEvent(mindId, ref mindEv); - - var message = new RoleAddedEvent(mindId, mind, antagonist, silent); - if (mind.OwnedEntity != null) - { - RaiseLocalEvent(mind.OwnedEntity.Value, message, true); - } - - _adminLogger.Add(LogType.Mind, LogImpact.Low, - $"Role components {string.Join(components.Keys.ToString(), ", ")} added to mind of {_minds.MindOwnerLoggingString(mind)}"); - } - - public void MindAddRole(EntityUid mindId, Component component, MindComponent? mind = null, bool silent = false) - { - if (!Resolve(mindId, ref mind)) - return; - - if (HasComp(mindId, component.GetType())) - { - throw new ArgumentException($"We already have this role: {component}"); - } - - EntityManager.AddComponent(mindId, component); - var antagonist = IsAntagonistRole(component.GetType()); - - var mindEv = new MindRoleAddedEvent(silent); - RaiseLocalEvent(mindId, ref mindEv); - - var message = new RoleAddedEvent(mindId, mind, antagonist, silent); - if (mind.OwnedEntity != null) - { - RaiseLocalEvent(mind.OwnedEntity.Value, message, true); - } - - _adminLogger.Add(LogType.Mind, LogImpact.Low, - $"'Role {component}' added to mind of {_minds.MindOwnerLoggingString(mind)}"); - } - /// /// Gives this mind a new role. /// @@ -236,11 +177,6 @@ public bool IsAntagonistRole() return _antagTypes.Contains(typeof(T)); } - public bool IsAntagonistRole(Type component) - { - return _antagTypes.Contains(component); - } - /// /// Play a sound for the mind, if it has a session attached. /// Use this for role greeting sounds. diff --git a/Content.Shared/Station/SharedStationSpawningSystem.cs b/Content.Shared/Station/SharedStationSpawningSystem.cs index 59c875f30e..715ee2a149 100644 --- a/Content.Shared/Station/SharedStationSpawningSystem.cs +++ b/Content.Shared/Station/SharedStationSpawningSystem.cs @@ -1,17 +1,16 @@ using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory; +using Content.Shared.Preferences; using Content.Shared.Roles; using Content.Shared.Storage; using Content.Shared.Storage.EntitySystems; using Robust.Shared.Collections; -using Robust.Shared.Prototypes; namespace Content.Shared.Station; public abstract class SharedStationSpawningSystem : EntitySystem { - [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] protected readonly InventorySystem InventorySystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedStorageSystem _storage = default!; @@ -22,27 +21,14 @@ public abstract class SharedStationSpawningSystem : EntitySystem /// /// Entity to load out. /// Starting gear to use. - public void EquipStartingGear(EntityUid entity, ProtoId? startingGear) + /// Character profile to use, if any. + public void EquipStartingGear(EntityUid entity, StartingGearPrototype startingGear, HumanoidCharacterProfile? profile) { - PrototypeManager.TryIndex(startingGear, out var gearProto); - EquipStartingGear(entity, gearProto); - } - - /// - /// Equips starting gear onto the given entity. - /// - /// Entity to load out. - /// Starting gear to use. - public void EquipStartingGear(EntityUid entity, StartingGearPrototype? startingGear) - { - if (startingGear == null) - return; - if (InventorySystem.TryGetSlots(entity, out var slotDefinitions)) { foreach (var slot in slotDefinitions) { - var equipmentStr = startingGear.GetGear(slot.Name, null); + var equipmentStr = startingGear.GetGear(slot.Name, profile); if (string.IsNullOrEmpty(equipmentStr)) continue; diff --git a/Resources/Audio/Goobstation/Ambience/Antag/changeling_start.ogg b/Resources/Audio/Goobstation/Ambience/Antag/changeling_start.ogg deleted file mode 100644 index 1132ccca29c4be205bbc8c09113f9012b5fc10fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 120933 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 z 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 byu~4phd6S`;~|b*b!Xmnac5Q~)=Vt`EPz-u diff --git a/Resources/Locale/en-US/Goobstation/Changeling/alerts/alerts.ftl b/Resources/Locale/en-US/Goobstation/Changeling/alerts/alerts.ftl deleted file mode 100644 index 9429bc7f53..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/alerts/alerts.ftl +++ /dev/null @@ -1,6 +0,0 @@ -alerts-changeling-chemicals-name = Chemicals -alerts-changeling-chemicals-desc = Spend chemicals to use your abilities. Slowly regenerates. - -alerts-changeling-biomass-name = Biomass -alerts-changeling-biomass-desc = - This is your health. If it reaches 0 - it's [color=red]game over[/color]. Absorb humanoids to recover some of it. \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/Changeling/guidebook/guides.ftl b/Resources/Locale/en-US/Goobstation/Changeling/guidebook/guides.ftl deleted file mode 100644 index 75a120878b..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/guidebook/guides.ftl +++ /dev/null @@ -1 +0,0 @@ -guide-entry-changelings = Changelings \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/Changeling/objectives/changeling.ftl b/Resources/Locale/en-US/Goobstation/Changeling/objectives/changeling.ftl deleted file mode 100644 index 47c7a326ae..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/objectives/changeling.ftl +++ /dev/null @@ -1,7 +0,0 @@ -objective-condition-absorb-title = Absorb {$count} humanoids. -objective-condition-absorb-description = I must absorb {$count} humanoids. It is necessary for my survival and further evolution. - -objective-condition-stealdna-title = Extract {$count} compatible genomes. -objective-condition-stealdna-description = I must extract {$count} unique genomes. - -objective-condition-escape-identity-title = Escape on the evacuation shuttle alive and unrestrained while being {$targetName}, {CAPITALIZE($job)}. \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/Changeling/popup/changeling.ftl b/Resources/Locale/en-US/Goobstation/Changeling/popup/changeling.ftl deleted file mode 100644 index 9ad650ccec..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/popup/changeling.ftl +++ /dev/null @@ -1,3 +0,0 @@ -popup-changeling-biomass-deficit-low = Your skin itches. -popup-changeling-biomass-deficit-medium = Must find a food source... -popup-changeling-biomass-deficit-high = Must eat... NOW!! \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/Changeling/radio_channels.ftl b/Resources/Locale/en-US/Goobstation/Changeling/radio_channels.ftl deleted file mode 100644 index a2eeebd9cb..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/radio_channels.ftl +++ /dev/null @@ -1 +0,0 @@ -chat-radio-hivemind = Hivemind \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/Changeling/store/categories.ftl b/Resources/Locale/en-US/Goobstation/Changeling/store/categories.ftl deleted file mode 100644 index c0b4796701..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/store/categories.ftl +++ /dev/null @@ -1,4 +0,0 @@ -# Changeling -store-ling-category-combat = Combat -store-ling-category-sting = Stings -store-ling-category-utility = Utility diff --git a/Resources/Locale/en-US/Goobstation/changeling/abilities/changeling.ftl b/Resources/Locale/en-US/Goobstation/changeling/abilities/changeling.ftl deleted file mode 100644 index 01343e20b5..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/abilities/changeling.ftl +++ /dev/null @@ -1,64 +0,0 @@ -# Abilities -changeling-biomass-deficit = Not enough biomass! -changeling-chemicals-deficit = Not enough chemicals! -changeling-action-fail-lesserform = Can't use it while in lesser form! -changeling-action-fail-absorbed = Need to absorb {$number} more organics to use it! - -changeling-absorb-start = {CAPITALIZE(THE($user))} starts absorbing {CAPITALIZE(THE($target))}'s! -changeling-absorb-fail-incapacitated = You can't absorb it until it's not incapacitated. -changeling-absorb-fail-absorbed = You've already absorbed it. -changeling-absorb-fail-unabsorbable = The target is not absorbable. -changeling-absorb-end-self = Another organic absorbed. You are evolving. -changeling-absorb-end-self-ling = Another changeling absorbed. You are evolving more rapidly. -changeling-absorb-onexamine = [color=red]The body feels hollow.[/color] - -changeling-transform-cycle = Switched to {$target}'s DNA. -changeling-transform-cycle-empty = You don't have any DNA strains! -changeling-transform-others = {CAPITALIZE(THE($user))}'s body twists and takes shape of another being! -changeling-transform-fail-self = You can't transform into your current form! -changeling-transform-fail-choose = You did not choose a form to transform into! -changeling-transform-fail-absorbed = You can't transform a husk! -changeling-transform-finish = You are now {$target}. - -changeling-sting-fail-self = You tried to sting {CAPITALIZE(THE($target))}, but something stopped you from doing it! -changeling-sting-fail-ling = Someone just tried to silently sting you! - -changeling-sting = You silently sting {CAPITALIZE(THE($target))} -changeling-sting-fail-simplemob = You can't sting a lesser creature! -changeling-sting-extract-fail = Unable to extract DNA -changeling-sting-extract-max = Need to get rid of the stored DNA beforehand - -changeling-stasis-enter = You enter regenerative stasis -changeling-stasis-enter-fail = Can't enter stasis! -changeling-stasis-exit = You exit regenerative stasis -changeling-stasis-exit-fail = We're not in a stasis! -changeling-stasis-exit-fail-dead = Can't exit stasis! - -changeling-hand-transform-end = Your arm takes back it's initial form -changeling-fail-hands = Need to drop something beforehand - -changeling-armblade-start = Your arm reforms into a grotesque blade -changeling-shield-start = Your arm reforms into a meat shield - -changeling-muscles-start = Your body feels a lot lighter -changeling-muscles-end = Your legs feel heavier - -changeling-equip-armor-fail = Need to get rid of existing outer clothing beforehand -changeling-equip-armor-start = Your body gets wrapped in a sturdy chitinous shell -changeling-equip-spacesuit-start = Your body transforms into a spaceproof abomination -changeling-equip-end = Your body takes back it's original shape - -changeling-inject = You inject yourself -changeling-inject-fail = Failed to inject yourself! - -changeling-passive-activate = Activated ability -changeling-passive-activate-fail = Failed to activate the ability -changeling-passive-active = Already active! - -changeling-fleshmend = Your body twists, sealing wounds and regenerating dead cells -changeling-panacea = You mutate and alter your DNA for better cell regeneration - -changeling-chameleon-start = You adapt your skin to the environment -changeling-chameleon-end = Your skin is losing it's translucency - -changeling-hivemind-start = We tune our brainwaves to match the hivemind frequency diff --git a/Resources/Locale/en-US/Goobstation/changeling/administration/antag.ftl b/Resources/Locale/en-US/Goobstation/changeling/administration/antag.ftl deleted file mode 100644 index 3ba13f47e5..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/administration/antag.ftl +++ /dev/null @@ -1,3 +0,0 @@ -admin-verb-make-changeling = Make the target into a changeling. - -admin-verb-text-make-changeling = Make Changeling \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/changeling/game-ticking/game-presets/preset-changeling.ftl b/Resources/Locale/en-US/Goobstation/changeling/game-ticking/game-presets/preset-changeling.ftl deleted file mode 100644 index 1e550cb4f1..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/game-ticking/game-presets/preset-changeling.ftl +++ /dev/null @@ -1,20 +0,0 @@ -changeling-roundend-name = changeling - -objective-issuer-hivemind = [color=orange]Hivemind[/color] - -roundend-prepend-changeling-absorbed-named = [color=white]{$name}[/color] has absorbed a total of [color=red]{$number}[/color] organics. -roundend-prepend-changeling-stolen-named = [color=white]{$name}[/color] has extracted a total of [color=orange]{$number}[/color] DNA samples. -roundend-prepend-changeling-absorbed = Someone has absorbed a total of [color=red]{$number}[/color] organics. -roundend-prepend-changeling-stolen = Someone had extracted a total of [color=orange]{$number}[/color] DNA samples. - -changeling-gamemode-title = Changelings -changeling-gamemode-description = - The changeling hive has boarded the station, ready to take anything it desires - be it your equipment, your faces, or your lives! - -changeling-role-greeting = - You are a changeling who has absorbed and taken the form of {$name}! - Your objectives are listed in the character menu. - Absorb, shapeshift and evolve to complete them! - -changeling-role-greeting-short = - You are a changeling who has absorbed and taken the initial form of {$name}. diff --git a/Resources/Locale/en-US/Goobstation/changeling/prototypes/roles/antags.ftl b/Resources/Locale/en-US/Goobstation/changeling/prototypes/roles/antags.ftl deleted file mode 100644 index 5d5e578d60..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/prototypes/roles/antags.ftl +++ /dev/null @@ -1,2 +0,0 @@ -roles-antag-changeling-name = Changeling -roles-antag-changeling-description = Use your shapeshifting abilities to complete your objectives. \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/changeling/store/changeling-catalog.ftl b/Resources/Locale/en-US/Goobstation/changeling/store/changeling-catalog.ftl deleted file mode 100644 index f1897c18de..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/store/changeling-catalog.ftl +++ /dev/null @@ -1,134 +0,0 @@ - -# combat - -evolutionmenu-combat-armblade-name = Arm Blade -evolutionmenu-combat-armblade-desc = - Reform one of your arms into a grotesque blade, composed of bone and flesh, able to pry open airlocks and cut through your foes like butter. - Costs 15 chemicals. - -evolutionmenu-combat-boneshard-name = Bone Shard -evolutionmenu-combat-boneshard-desc = - Break off shards of your bone and shape them into a throwing star which embeds into your foes. But a one timer opportinuty. - Costs 15 chemicals. - -evolutionmenu-combat-armor-name = Chitinous Armor -evolutionmenu-combat-armor-desc = - Inflate your body into an all-consuming chitinous mass of armor. - Provides extensive protection against physical damage, but less against other types. - It massively slows your movement, and maintaining its shape slows chemical generation. - WARNING: Requires you to absorb at least 2 organics to use the ability. - Costs 25 chemicals. - -evolutionmenu-combat-shield-name = Organic Shield -evolutionmenu-combat-shield-desc = - Reforms one of your arms into a large, fleshy shield. - Blocks attacks automatically, but very brittle. - WARNING: Requires you to absorb at least 1 organic to use the ability. - Costs 20 chemicals. - -evolutionmenu-combat-shriek-dissonant-name = Dissonant Shriek -evolutionmenu-combat-shriek-dissonant-desc = - You emit an EMP blast, which disables technology in the surrounding area, including radio headsets. - Good for escaping cyborgs and security. - WARNING: Requires you to absorb at least 1 organic to use the ability. - Costs 30 chemicals. - -evolutionmenu-combat-shriek-resonant-name = Resonant Shriek -evolutionmenu-combat-shriek-resonant-desc = - You emit a tone beyond the range of human hearing, - bursting lights and causing disorientation in an area around yourself. - Good for escaping groups, or hindering people from fleeing. - WARNING: Requires you to absorb at least 1 organic to use the ability. - Costs 30 chemicals. - -evolutionmenu-combat-strainedmuscles-name = Strained Muscles -evolutionmenu-combat-strainedmuscles-desc = - You reduce lactic acid buildup in your leg muscles, allowing you to move at extremely fast speeds. - While active, you will take steadily increments of stamina damage and eventually pass out. - Cost-free. - -# sting - -evolutionmenu-sting-blind-name = Blind Sting -evolutionmenu-sting-blind-desc = - Silently sting an organic target, completely blinding them for a short time, and rendering them near-sighted until oculine is applied. - May be used while under the effects of Lesser Form. - Costs 35 chemicals. - -evolutionmenu-sting-cryo-name = Cryogenic Sting -evolutionmenu-sting-cryo-desc = - Inject an organic target with a cocktail of chemicals that chills the blood. - May be used while under the effects of Lesser Form. - Costs 35 chemicals. - -evolutionmenu-sting-lethargic-name = Lethargic Sting -evolutionmenu-sting-lethargic-desc = - Inject an organic target with a cocktail of anesthetics, slowing the victim down for a decent amount of time. - May be used while under the effects of Lesser Form. - Costs 35 chemicals. - -evolutionmenu-sting-mute-name = Mute Sting -evolutionmenu-sting-mute-desc = - Inject mute toxin into an organic target, completely silencing them for a while. - May be used while under the effects of Lesser Form. - Costs 35 chemicals. - -evolutionmenu-sting-transform-name = Transformation Sting -evolutionmenu-sting-transform-desc = - Inject some of your genome into an organic target, forcing their body to shapeshift into whoever you've chosen using the Cycle DNA ability. - May be used while under the effects of Lesser Form. - Costs 75 chemicals. - -evolutionmenu-sting-armblade-name = Fake Arm Blade Sting -evolutionmenu-sting-armblade-desc = - Inject some of your genome into an organic target, forcing their arm to shapeshift into a dull armblade. - May be used while under the effects of Lesser Form. - Costs 50 chemicals. - -# utility -evolutionmenu-utility-panacea-name = Anatomic Panacea -evolutionmenu-utility-panacea-desc = - Cure yourself of diseases, disabilities, radiation, toxins, drunkenness, and brain damage. Generally covers the things that fleshmend doesn't. - Costs 30 chemicals. - -evolutionmenu-utility-eyesight-name = Augmented Eyesight -evolutionmenu-utility-eyesight-desc = - Evolve additional features in your eyes, such as flash protection. - Cost-free. - -evolutionmenu-utility-biodegrade-name = Biodegrade -evolutionmenu-utility-biodegrade-desc = - Vomit a caustic substance onto any restraints you may be wearing, allowing yourself to break free. - Using this ability while being grabbed will spit acid in your attackers face. - Costs 30 chemicals. - -evolutionmenu-utility-chameleon-name = Chameleon Skin -evolutionmenu-utility-chameleon-desc = - Alter the pigment in your skin to match your surroundings, rendering you invisible.p - Costs 20 chemicals. - -evolutionmenu-utility-stims-name = Ephedrine Overdose -evolutionmenu-utility-stims-desc = - Inject a cocktail of stimulants into yourself, quickly removing any stuns and giving yourself a speed boost. - Continuous injection is poisonous. - Costs 30 chemicals. - -evolutionmenu-utility-fleshmend-name = Fleshmend -evolutionmenu-utility-fleshmend-desc = - Rapidly heal yourself of all bruises and burns. - Costs 35 chemicals. - -evolutionmenu-utility-lesserform-name = Lesser Form -evolutionmenu-utility-lesserform-desc = - Abandon your current form and turn into a sentient monkey. - Costs 20 chemicals. - -evolutionmenu-utility-spacesuit-name = Space Adaptation -evolutionmenu-utility-spacesuit-desc = - Get rid of useless tissue in order to facilitate space travel. A source of oxygen is still required for space walking. - Costs 20 chemicals. - -evolutionmenu-utility-hivemindaccess-name = Hivemind Access -evolutionmenu-utility-hivemindaccess-desc = - Tunes our chemical receptors for hivemind communication, allowing us to recognize and communicate with other changelings who have also evolved this ability. - Default radio key is :g diff --git a/Resources/Locale/en-US/Goobstation/changeling/store/currency.ftl b/Resources/Locale/en-US/Goobstation/changeling/store/currency.ftl deleted file mode 100644 index 0398a8cc4e..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/store/currency.ftl +++ /dev/null @@ -1 +0,0 @@ -store-currency-display-evolutionpoints = Evolution Points \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/game-ticking/game-presets/presets-dualantag.ftl b/Resources/Locale/en-US/Goobstation/game-ticking/game-presets/presets-dualantag.ftl deleted file mode 100644 index dcb51e9198..0000000000 --- a/Resources/Locale/en-US/Goobstation/game-ticking/game-presets/presets-dualantag.ftl +++ /dev/null @@ -1,8 +0,0 @@ -traitorling-title = Traitorling -traitorling-description = Attention. Known enemy signals and strange biosigns detected. Confirmed Syndicate Agents and Changelings on board. - -revtraitor-title = Revolutionary Traitors -revtraitor-description = A revolution has been provoked by a member of the Syndicate, but not every agent got the hint... - -revling-title = Revolutionary Changelings -revling-description = A revolution has been provoked by the Syndicate, and opportunistic changelings have come to feast on the carnage. diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-pirates.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-pirates.ftl new file mode 100644 index 0000000000..941643dd9a --- /dev/null +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-pirates.ftl @@ -0,0 +1,10 @@ +pirates-title = Privateers +pirates-description = A group of privateers has approached your lowly station. Hostile or not, their sole goal is to end the round with as many knicknacks on their ship as they can get. + +pirates-no-ship = Through unknown circumstances, the privateer's ship was completely and utterly destroyed. No score. +pirates-final-score = The privateers successfully obtained {$score} spesos worth +pirates-final-score-2 = of knicknacks, with a total of {$finalPrice} spesos. +pirates-list-start = The privateers were: +pirates-most-valuable = The most valuable stolen items were: +pirates-stolen-item-entry = {$entity} ({$credits} spesos) +pirates-stole-nothing = - The pirates stole absolutely nothing at all. Point and laugh. diff --git a/Resources/Maps/Shuttles/striker.yml b/Resources/Maps/Shuttles/striker.yml index 88b113d7fd..35b6178bd4 100644 --- a/Resources/Maps/Shuttles/striker.yml +++ b/Resources/Maps/Shuttles/striker.yml @@ -1,2389 +1,2389 @@ -meta: - format: 6 - postmapinit: false -tilemap: - 0: Space - 29: FloorDark - 84: FloorShuttleRed - 104: FloorTechMaint - 105: FloorTechMaint2 - 118: FloorWood - 120: Lattice - 121: Plating -entities: -- proto: "" - entities: - - uid: 325 - components: - - type: MetaData - - type: Transform - pos: 0.5638949,0.47865233 - parent: invalid - - type: MapGrid - chunks: - -1,-1: - ind: -1,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAdgAAAAAAdgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAdgAAAAADdgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAADHQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaQAAAAAAaQAAAAAAHQAAAAABHQAAAAABHQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAHQAAAAABHQAAAAACHQAAAAAB - version: 6 - 0,-1: - ind: 0,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAACeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAABeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABHQAAAAABHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADHQAAAAACeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 - -1,0: - ind: -1,0 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAVAAAAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 - 0,0: - ind: 0,0 - tiles: VAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 - - type: Broadphase - - type: Physics - bodyStatus: InAir - angularDamping: 0.05 - linearDamping: 0.05 - fixedRotation: False - bodyType: Dynamic - - type: Fixtures - fixtures: {} - - type: OccluderTree - - type: Shuttle - - type: Gravity - gravityShakeSound: !type:SoundPathSpecifier - path: /Audio/Effects/alert.ogg - - type: DecalGrid - chunkCollection: - version: 2 - nodes: - - node: - color: '#FFFFFFFF' - id: BrickTileDarkCornerNe - decals: - 11: 1,-1 - - node: - color: '#FFFFFFFF' - id: BrickTileDarkCornerNw - decals: - 5: -3,-1 - - node: - color: '#FFFFFFFF' - id: BrickTileDarkCornerSe - decals: - 4: 1,-3 - - node: - color: '#FFFFFFFF' - id: BrickTileDarkCornerSw - decals: - 3: -3,-3 - - node: - color: '#FFFFFFFF' - id: BrickTileDarkLineS - decals: - 0: -1,-3 - 1: -2,-3 - 2: 0,-3 - - node: - color: '#7F1C1FFF' - id: BrickTileWhiteCornerNe - decals: - 13: 1,-1 - - node: - color: '#7F1C1FFF' - id: BrickTileWhiteCornerNw - decals: - 12: -3,-1 - - node: - color: '#7F1C1FFF' - id: BrickTileWhiteCornerSe - decals: - 9: 1,-3 - - node: - color: '#7F1C1FFF' - id: BrickTileWhiteCornerSw - decals: - 10: -3,-3 - - node: - color: '#7F1C1FFF' - id: BrickTileWhiteLineS - decals: - 6: -2,-3 - 7: -1,-3 - 8: 0,-3 - - node: - color: '#FFFFFFFF' - id: Delivery - decals: - 23: 2,-2 - 24: -4,-2 - - node: - color: '#FFFFFFFF' - id: WarnLineE - decals: - 14: 1,-2 - - node: - color: '#FFFFFFFF' - id: WarnLineS - decals: - 16: -3,-2 - - node: - color: '#FFFFFFFF' - id: WarnLineW - decals: - 15: -1,-1 - - node: - color: '#FFFFFFFF' - id: WoodTrimThinLineN - decals: - 17: -1,-5 - 18: 0,-5 - 19: -2,-5 - - node: - color: '#FFFFFFFF' - id: WoodTrimThinLineS - decals: - 20: -2,-6 - 21: -1,-6 - 22: 0,-6 - - type: GridAtmosphere - version: 2 - data: - tiles: - -1,-1: - 0: 65535 - 0,-1: - 0: 65535 - -2,-1: - 0: 52424 - -1,-3: - 0: 65280 - -1,-2: - 0: 65535 - 0,-3: - 0: 30464 - 0,-2: - 0: 30583 - -2,0: - 0: 8 - -1,0: - 0: 3839 - 0,0: - 0: 895 - uniqueMixes: - - volume: 2500 - temperature: 293.15 - moles: - - 21.824879 - - 82.10312 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - chunkSize: 4 - - type: GasTileOverlay - - type: RadiationGridResistance - - type: GravityShake - shakeTimes: 10 - - type: SpreaderGrid - - type: GridPathfinding -- proto: AirCanister - entities: - - uid: 91 - components: - - type: Transform - pos: -0.5,-8.5 - parent: 325 - - type: AtmosDevice - joinedGrid: 325 -- proto: AirlockExternalShuttleSyndicateLocked - entities: - - uid: 142 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.5,-1.5 - parent: 325 -- proto: AirlockSyndicateLocked - entities: - - uid: 20 - components: - - type: Transform - pos: -0.5,-3.5 - parent: 325 - - uid: 88 - components: - - type: Transform - pos: -0.5,-6.5 - parent: 325 -- proto: APCBasic - entities: - - uid: 107 - components: - - type: Transform - pos: 0.5,-6.5 - parent: 325 - - type: PowerNetworkBattery - loadingNetworkDemand: 15107 - currentReceiving: 15106.935 - currentSupply: 15107 - supplyRampPosition: 0.064453125 -- proto: AtmosDeviceFanTiny - entities: - - uid: 6 - components: - - type: Transform - pos: -3.5,-1.5 - parent: 325 -- proto: Bed - entities: - - uid: 76 - components: - - type: Transform - pos: 0.5,-5.5 - parent: 325 -- proto: BedsheetSyndie - entities: - - uid: 164 - components: - - type: Transform - pos: 0.5,-5.5 - parent: 325 -- proto: BlastDoorOpen - entities: - - uid: 190 - components: - - type: Transform - pos: 1.5,-5.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 331 - - type: DeviceLinkSink - links: - - 205 - - uid: 191 - components: - - type: Transform - pos: 1.5,-4.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 332 - - type: DeviceLinkSink - links: - - 205 - - uid: 192 - components: - - type: Transform - pos: -2.5,-5.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 333 - - type: DeviceLinkSink - links: - - 205 - - uid: 193 - components: - - type: Transform - pos: -2.5,-4.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 334 - - type: DeviceLinkSink - links: - - 205 - - uid: 196 - components: - - type: Transform - pos: 3.5,-1.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 337 - - type: DeviceLinkSink - links: - - 205 - - uid: 198 - components: - - type: Transform - pos: -1.5,1.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 339 - - type: DeviceLinkSink - links: - - 205 - - uid: 199 - components: - - type: Transform - pos: -1.5,2.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 340 - - type: DeviceLinkSink - links: - - 205 - - uid: 200 - components: - - type: Transform - pos: -0.5,2.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 341 - - type: DeviceLinkSink - links: - - 205 - - uid: 201 - components: - - type: Transform - pos: 0.5,2.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 342 - - type: DeviceLinkSink - links: - - 205 - - uid: 202 - components: - - type: Transform - pos: 0.5,1.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 343 - - type: DeviceLinkSink - links: - - 205 -- proto: BoxMRE - entities: - - uid: 320 - components: - - type: Transform - pos: 0.70504504,-7.29326 - parent: 325 -- proto: CableApcExtension - entities: - - uid: 120 - components: - - type: Transform - pos: 0.5,-6.5 - parent: 325 - - uid: 121 - components: - - type: Transform - pos: -0.5,-6.5 - parent: 325 - - uid: 122 - components: - - type: Transform - pos: -0.5,-7.5 - parent: 325 - - uid: 123 - components: - - type: Transform - pos: -0.5,-8.5 - parent: 325 - - uid: 124 - components: - - type: Transform - pos: -1.5,-8.5 - parent: 325 - - uid: 125 - components: - - type: Transform - pos: 0.5,-8.5 - parent: 325 - - uid: 126 - components: - - type: Transform - pos: 1.5,-8.5 - parent: 325 - - uid: 127 - components: - - type: Transform - pos: -2.5,-8.5 - parent: 325 - - uid: 128 - components: - - type: Transform - pos: -3.5,-8.5 - parent: 325 - - uid: 129 - components: - - type: Transform - pos: -3.5,-7.5 - parent: 325 - - uid: 130 - components: - - type: Transform - pos: 2.5,-8.5 - parent: 325 - - uid: 131 - components: - - type: Transform - pos: 2.5,-7.5 - parent: 325 - - uid: 132 - components: - - type: Transform - pos: -0.5,-5.5 - parent: 325 - - uid: 133 - components: - - type: Transform - pos: -0.5,-4.5 - parent: 325 - - uid: 134 - components: - - type: Transform - pos: -0.5,-3.5 - parent: 325 - - uid: 135 - components: - - type: Transform - pos: -0.5,-2.5 - parent: 325 - - uid: 136 - components: - - type: Transform - pos: -0.5,-1.5 - parent: 325 - - uid: 137 - components: - - type: Transform - pos: -0.5,-0.5 - parent: 325 - - uid: 138 - components: - - type: Transform - pos: -0.5,0.5 - parent: 325 - - uid: 139 - components: - - type: Transform - pos: -0.5,1.5 - parent: 325 - - uid: 140 - components: - - type: Transform - pos: -0.5,2.5 - parent: 325 - - uid: 141 - components: - - type: Transform - pos: -1.5,1.5 - parent: 325 - - uid: 143 - components: - - type: Transform - pos: 0.5,1.5 - parent: 325 - - uid: 145 - components: - - type: Transform - pos: -1.5,-1.5 - parent: 325 - - uid: 146 - components: - - type: Transform - pos: -2.5,-1.5 - parent: 325 - - uid: 147 - components: - - type: Transform - pos: -3.5,-1.5 - parent: 325 - - uid: 148 - components: - - type: Transform - pos: -4.5,-1.5 - parent: 325 - - uid: 149 - components: - - type: Transform - pos: 0.5,-1.5 - parent: 325 - - uid: 150 - components: - - type: Transform - pos: 1.5,-1.5 - parent: 325 - - uid: 151 - components: - - type: Transform - pos: 2.5,-1.5 - parent: 325 - - uid: 152 - components: - - type: Transform - pos: 3.5,-1.5 - parent: 325 - - uid: 153 - components: - - type: Transform - pos: 0.5,-4.5 - parent: 325 - - uid: 154 - components: - - type: Transform - pos: 1.5,-4.5 - parent: 325 - - uid: 155 - components: - - type: Transform - pos: 1.5,-5.5 - parent: 325 - - uid: 156 - components: - - type: Transform - pos: -1.5,-4.5 - parent: 325 - - uid: 157 - components: - - type: Transform - pos: -2.5,-4.5 - parent: 325 - - uid: 158 - components: - - type: Transform - pos: -2.5,-5.5 - parent: 325 -- proto: CableHV - entities: - - uid: 111 - components: - - type: Transform - pos: 1.5,-7.5 - parent: 325 - - uid: 112 - components: - - type: Transform - pos: 0.5,-7.5 - parent: 325 - - uid: 113 - components: - - type: Transform - pos: -0.5,-7.5 - parent: 325 - - uid: 114 - components: - - type: Transform - pos: -1.5,-7.5 - parent: 325 - - uid: 115 - components: - - type: Transform - pos: -2.5,-7.5 - parent: 325 - - uid: 116 - components: - - type: Transform - pos: -1.5,-6.5 - parent: 325 -- proto: CableHVStack1 - entities: - - uid: 235 - components: - - type: Transform - parent: 41 - - type: Stack - count: 10 - - type: Physics - canCollide: False - - uid: 239 - components: - - type: Transform - parent: 56 - - type: Stack - count: 10 - - type: Physics - canCollide: False -- proto: CableMV - entities: - - uid: 117 - components: - - type: Transform - pos: -1.5,-6.5 - parent: 325 - - uid: 118 - components: - - type: Transform - pos: -0.5,-6.5 - parent: 325 - - uid: 119 - components: - - type: Transform - pos: 0.5,-6.5 - parent: 325 -- proto: CapacitorStockPart - entities: - - uid: 233 - components: - - type: Transform - parent: 41 - - type: Physics - canCollide: False - - uid: 234 - components: - - type: Transform - parent: 41 - - type: Physics - canCollide: False - - uid: 237 - components: - - type: Transform - parent: 56 - - type: Physics - canCollide: False - - uid: 238 - components: - - type: Transform - parent: 56 - - type: Physics - canCollide: False - - uid: 241 - components: - - type: Transform - parent: 58 - - type: Physics - canCollide: False - - uid: 242 - components: - - type: Transform - parent: 58 - - type: Physics - canCollide: False - - uid: 243 - components: - - type: Transform - parent: 58 - - type: Physics - canCollide: False - - uid: 254 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 261 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 268 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 275 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 282 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 289 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 296 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 303 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False -- proto: Carpet - entities: - - uid: 74 - components: - - type: Transform - pos: -0.5,-4.5 - parent: 325 - - uid: 89 - components: - - type: Transform - pos: -0.5,-5.5 - parent: 325 -- proto: Catwalk - entities: - - uid: 159 - components: - - type: Transform - pos: -1.5,-7.5 - parent: 325 - - uid: 160 - components: - - type: Transform - pos: -0.5,-7.5 - parent: 325 - - uid: 161 - components: - - type: Transform - pos: 0.5,-7.5 - parent: 325 -- proto: ChairOfficeDark - entities: - - uid: 93 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,-2.5 - parent: 325 -- proto: ChairPilotSeat - entities: - - uid: 78 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,0.5 - parent: 325 -- proto: ComputerIFFSyndicate - entities: - - uid: 40 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,0.5 - parent: 325 -- proto: ComputerShuttleSyndie - entities: - - uid: 64 - components: - - type: Transform - pos: -0.5,1.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 245 -- proto: CyberPen - entities: - - uid: 77 - components: - - type: Transform - pos: -1.1813428,-5.15565 - parent: 325 -- proto: DoorElectronics - entities: - - uid: 331 - components: - - type: Transform - parent: 190 - - type: Physics - canCollide: False - - uid: 332 - components: - - type: Transform - parent: 191 - - type: Physics - canCollide: False - - uid: 333 - components: - - type: Transform - parent: 192 - - type: Physics - canCollide: False - - uid: 334 - components: - - type: Transform - parent: 193 - - type: Physics - canCollide: False - - uid: 337 - components: - - type: Transform - parent: 196 - - type: Physics - canCollide: False - - uid: 339 - components: - - type: Transform - parent: 198 - - type: Physics - canCollide: False - - uid: 340 - components: - - type: Transform - parent: 199 - - type: Physics - canCollide: False - - uid: 341 - components: - - type: Transform - parent: 200 - - type: Physics - canCollide: False - - uid: 342 - components: - - type: Transform - parent: 201 - - type: Physics - canCollide: False - - uid: 343 - components: - - type: Transform - parent: 202 - - type: Physics - canCollide: False - - uid: 346 - components: - - type: Transform - parent: 206 - - type: Physics - canCollide: False -- proto: DresserFilled - entities: - - uid: 85 - components: - - type: Transform - pos: 0.5,-4.5 - parent: 325 -- proto: DrinkNukieCan - entities: - - uid: 144 - components: - - type: Transform - pos: -2.6964839,-2.109029 - parent: 325 -- proto: FaxMachineSyndie - entities: - - uid: 46 - components: - - type: Transform - pos: -1.5,-5.5 - parent: 325 - - type: FaxMachine - name: Striker -- proto: filingCabinetRandom - entities: - - uid: 75 - components: - - type: Transform - pos: -1.5,-4.5 - parent: 325 -- proto: Firelock - entities: - - uid: 224 - components: - - type: Transform - pos: -0.5,-3.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 350 - - type: DeviceNetwork - address: 44a24659 - receiveFrequency: 1621 - - uid: 225 - components: - - type: Transform - pos: -0.5,-6.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 351 - - type: DeviceNetwork - address: 6fdb75cf - receiveFrequency: 1621 -- proto: FirelockElectronics - entities: - - uid: 350 - components: - - type: Transform - parent: 224 - - type: Physics - canCollide: False - - uid: 351 - components: - - type: Transform - parent: 225 - - type: Physics - canCollide: False -- proto: FoodBoxDonut - entities: - - uid: 87 - components: - - type: Transform - pos: -2.470145,-2.3953476 - parent: 325 -- proto: GasPipeFourway - entities: - - uid: 216 - components: - - type: Transform - pos: -0.5,-1.5 - parent: 325 -- proto: GasPipeStraight - entities: - - uid: 211 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-6.5 - parent: 325 - - uid: 213 - components: - - type: Transform - pos: -0.5,-4.5 - parent: 325 - - uid: 214 - components: - - type: Transform - pos: -0.5,-3.5 - parent: 325 - - uid: 215 - components: - - type: Transform - pos: -0.5,-2.5 - parent: 325 - - uid: 217 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-0.5 - parent: 325 -- proto: GasPipeTJunction - entities: - - uid: 210 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,-7.5 - parent: 325 - - uid: 212 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -0.5,-5.5 - parent: 325 -- proto: GasPort - entities: - - uid: 59 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-8.5 - parent: 325 - - type: AtmosDevice - joinedGrid: 325 -- proto: GasVentPump - entities: - - uid: 218 - components: - - type: Transform - pos: -0.5,0.5 - parent: 325 - - type: DeviceNetwork - address: Vnt-5f41a0ae - transmitFrequency: 1621 - receiveFrequency: 1621 - - type: AtmosDevice - joinedGrid: 325 - - uid: 219 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -1.5,-1.5 - parent: 325 - - type: DeviceNetwork - address: Vnt-129c27d2 - transmitFrequency: 1621 - receiveFrequency: 1621 - - type: AtmosDevice - joinedGrid: 325 - - uid: 220 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-1.5 - parent: 325 - - type: DeviceNetwork - address: Vnt-11c4609d - transmitFrequency: 1621 - receiveFrequency: 1621 - - type: AtmosDevice - joinedGrid: 325 - - uid: 221 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-5.5 - parent: 325 - - type: DeviceNetwork - address: Vnt-6859729f - transmitFrequency: 1621 - receiveFrequency: 1621 - - type: AtmosDevice - joinedGrid: 325 - - uid: 222 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -1.5,-7.5 - parent: 325 - - type: DeviceNetwork - address: Vnt-19d24c7f - transmitFrequency: 1621 - receiveFrequency: 1621 - - type: AtmosDevice - joinedGrid: 325 -- proto: GeneratorBasic15kW - entities: - - uid: 41 - components: - - type: Transform - pos: -2.5,-7.5 - parent: 325 - - type: PowerSupplier - supplyRampPosition: 7552.5303 - - type: ContainerContainer - containers: - machine_board: !type:Container - ents: - - 232 - machine_parts: !type:Container - ents: - - 233 - - 234 - - 235 - - uid: 56 - components: - - type: Transform - pos: 1.5,-7.5 - parent: 325 - - type: PowerSupplier - supplyRampPosition: 7552.5303 - - type: ContainerContainer - containers: - machine_board: !type:Container - ents: - - 236 - machine_parts: !type:Container - ents: - - 237 - - 238 - - 239 -- proto: GravityGeneratorMini - entities: - - uid: 57 - components: - - type: Transform - pos: -1.5,-8.5 - parent: 325 -- proto: Grille - entities: - - uid: 1 - components: - - type: Transform - pos: -0.5,2.5 - parent: 325 - - uid: 2 - components: - - type: Transform - pos: -1.5,2.5 - parent: 325 - - uid: 3 - components: - - type: Transform - pos: -1.5,1.5 - parent: 325 - - uid: 4 - components: - - type: Transform - pos: 0.5,2.5 - parent: 325 - - uid: 5 - components: - - type: Transform - pos: 0.5,1.5 - parent: 325 - - uid: 21 - components: - - type: Transform - pos: 3.5,-1.5 - parent: 325 - - uid: 50 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-5.5 - parent: 325 - - uid: 51 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-4.5 - parent: 325 - - uid: 52 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-5.5 - parent: 325 - - uid: 53 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-4.5 - parent: 325 -- proto: Gyroscope - entities: - - uid: 58 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-8.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 240 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 241 - - 242 - - 243 - - 244 -- proto: GyroscopeMachineCircuitboard - entities: - - uid: 240 - components: - - type: Transform - parent: 58 - - type: Physics - canCollide: False -- proto: MedkitCombatFilled - entities: - - uid: 19 - components: - - type: Transform - pos: 1.48298,-0.3211529 - parent: 325 -- proto: MicroManipulatorStockPart - entities: - - uid: 250 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 251 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 252 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 253 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 257 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 258 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 259 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 260 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 264 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 265 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 266 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 267 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 271 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 272 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 273 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 274 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 278 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 279 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 280 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 281 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 285 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 286 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 287 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 288 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 292 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 293 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 294 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 295 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 299 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False - - uid: 300 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False - - uid: 301 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False - - uid: 302 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False -- proto: Mirror - entities: - - uid: 321 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-3.5 - parent: 325 -- proto: NitrogenTankFilled - entities: - - uid: 105 - components: - - type: Transform - pos: 1.373605,-0.2749618 - parent: 325 -- proto: NukeCodePaper - entities: - - uid: 323 - components: - - type: Transform - pos: 1.561105,-2.5567772 - parent: 325 -- proto: PinpointerNuclear - entities: - - uid: 162 - components: - - type: Transform - pos: 1.3790641,-2.3161128 - parent: 325 - - type: Physics - canCollide: False -- proto: PlasmaReinforcedWindowDirectional - entities: - - uid: 104 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-0.5 - parent: 325 - - uid: 109 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-0.5 - parent: 325 -- proto: PlushieNuke - entities: - - uid: 47 - components: - - type: Transform - pos: 0.5061571,-5.233775 - parent: 325 -- proto: PortableGeneratorSuperPacmanMachineCircuitboard - entities: - - uid: 232 - components: - - type: Transform - parent: 41 - - type: Physics - canCollide: False - - uid: 236 - components: - - type: Transform - parent: 56 - - type: Physics - canCollide: False -- proto: PosterContrabandC20r - entities: - - uid: 24 - components: - - type: Transform - pos: 2.5,-2.5 - parent: 325 -- proto: PosterContrabandEnergySwords - entities: - - uid: 227 - components: - - type: Transform - pos: -2.5,-6.5 - parent: 325 -- proto: PosterContrabandNuclearDeviceInformational - entities: - - uid: 228 - components: - - type: Transform - pos: -2.5,0.5 - parent: 325 -- proto: PosterContrabandSyndicateRecruitment - entities: - - uid: 229 - components: - - type: Transform - pos: 0.5,-3.5 - parent: 325 -- proto: Poweredlight - entities: - - uid: 94 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,0.5 - parent: 325 - - uid: 110 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-2.5 - parent: 325 -- proto: PoweredlightLED - entities: - - uid: 182 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 2.5,-5.5 - parent: 325 - - type: ApcPowerReceiver - powerLoad: 0 - - uid: 183 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -3.5,-5.5 - parent: 325 - - type: ApcPowerReceiver - powerLoad: 0 - - uid: 184 - components: - - type: Transform - pos: -1.5,-7.5 - parent: 325 - - type: ApcPowerReceiver - powerLoad: 0 -- proto: PoweredSmallLight - entities: - - uid: 204 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-5.5 - parent: 325 - - type: ApcPowerReceiver - powerLoad: 0 -- proto: Rack - entities: - - uid: 83 - components: - - type: Transform - pos: 1.5,-0.5 - parent: 325 - - uid: 84 - components: - - type: Transform - pos: 1.5,-2.5 - parent: 325 -- proto: ReinforcedPlasmaWindow - entities: - - uid: 14 - components: - - type: Transform - pos: -1.5,1.5 - parent: 325 - - uid: 15 - components: - - type: Transform - pos: -1.5,2.5 - parent: 325 - - uid: 16 - components: - - type: Transform - pos: -0.5,2.5 - parent: 325 - - uid: 17 - components: - - type: Transform - pos: 0.5,2.5 - parent: 325 - - uid: 18 - components: - - type: Transform - pos: 0.5,1.5 - parent: 325 - - uid: 26 - components: - - type: Transform - pos: 3.5,-1.5 - parent: 325 - - uid: 42 - components: - - type: Transform - pos: 1.5,-4.5 - parent: 325 - - uid: 70 - components: - - type: Transform - pos: 1.5,-5.5 - parent: 325 - - uid: 71 - components: - - type: Transform - pos: -2.5,-4.5 - parent: 325 - - uid: 72 - components: - - type: Transform - pos: -2.5,-5.5 - parent: 325 -- proto: RemoteSignaller - entities: - - uid: 176 - components: - - type: Transform - pos: 1.3427892,-2.379079 - parent: 325 - - type: Physics - canCollide: False -- proto: SheetGlass1 - entities: - - uid: 244 - components: - - type: Transform - parent: 58 - - type: Stack - count: 2 - - type: Physics - canCollide: False -- proto: SheetSteel1 - entities: - - uid: 255 - components: - - type: Transform - parent: 95 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 262 - components: - - type: Transform - parent: 96 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 269 - components: - - type: Transform - parent: 97 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 276 - components: - - type: Transform - parent: 98 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 283 - components: - - type: Transform - parent: 99 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 290 - components: - - type: Transform - parent: 100 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 297 - components: - - type: Transform - parent: 101 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 304 - components: - - type: Transform - parent: 102 - - type: Stack - count: 5 - - type: Physics - canCollide: False -- proto: SignalButton - entities: - - uid: 205 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-3.5 - parent: 325 - - type: DeviceLinkSource - linkedPorts: - 193: - - Pressed: Toggle - 192: - - Pressed: Toggle - 190: - - Pressed: Toggle - 191: - - Pressed: Toggle - 196: - - Pressed: Toggle - 202: - - Pressed: Toggle - 201: - - Pressed: Toggle - 200: - - Pressed: Toggle - 199: - - Pressed: Toggle - 198: - - Pressed: Toggle -- proto: SignSpace - entities: - - uid: 230 - components: - - type: Transform - pos: -3.5,-0.5 - parent: 325 -- proto: SoapSyndie - entities: - - uid: 90 - components: - - type: Transform - pos: 0.5436061,-7.5129323 - parent: 325 -- proto: SpawnPointNukies - entities: - - uid: 322 - components: - - type: Transform - pos: -0.5,-4.5 - parent: 325 -- proto: StealthBox - entities: - - uid: 106 - components: - - type: Transform - pos: 0.49860507,-2.4513345 - parent: 325 - - type: Stealth - enabled: False - - type: EntityStorage - open: True -- proto: SubstationWallBasic - entities: - - uid: 103 - components: - - type: Transform - pos: -1.5,-6.5 - parent: 325 - - type: PowerNetworkBattery - loadingNetworkDemand: 15106.935 - currentReceiving: 15105.06 - currentSupply: 15106.935 - supplyRampPosition: 1.875 -- proto: SuitStorageSyndie - entities: - - uid: 67 - components: - - type: Transform - pos: 2.5,-1.5 - parent: 325 -- proto: SyndicateCommsComputerCircuitboard - entities: - - uid: 246 - components: - - type: Transform - parent: 65 - - type: Physics - canCollide: False -- proto: SyndicateComputerComms - entities: - - uid: 65 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -1.5,0.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 246 -- proto: SyndicateIDCard - entities: - - uid: 324 - components: - - type: Transform - pos: 1.57673,-2.3849022 - parent: 325 -- proto: SyndicateShuttleConsoleCircuitboard - entities: - - uid: 245 - components: - - type: Transform - parent: 64 - - type: Physics - canCollide: False -- proto: Table - entities: - - uid: 165 - components: - - type: Transform - pos: -2.5,-2.5 - parent: 325 -- proto: TableWood - entities: - - uid: 45 - components: - - type: Transform - pos: -1.5,-5.5 - parent: 325 -- proto: Thruster - entities: - - uid: 95 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -3.5,-9.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 249 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 250 - - 251 - - 252 - - 253 - - 254 - - 255 - - uid: 96 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 2.5,-9.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 256 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 257 - - 258 - - 259 - - 260 - - 261 - - 262 - - uid: 97 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-4.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 263 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 264 - - 265 - - 266 - - 267 - - 268 - - 269 - - uid: 98 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-5.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 270 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 271 - - 272 - - 273 - - 274 - - 275 - - 276 - - uid: 99 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-4.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 277 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 278 - - 279 - - 280 - - 281 - - 282 - - 283 - - uid: 100 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-5.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 284 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 285 - - 286 - - 287 - - 288 - - 289 - - 290 - - uid: 101 - components: - - type: Transform - pos: -3.5,1.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 291 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 292 - - 293 - - 294 - - 295 - - 296 - - 297 - - uid: 102 - components: - - type: Transform - pos: 2.5,1.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 298 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 299 - - 300 - - 301 - - 302 - - 303 - - 304 -- proto: ThrusterMachineCircuitboard - entities: - - uid: 249 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 256 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 263 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 270 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 277 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 284 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 291 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 298 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False -- proto: ToolboxSyndicateFilled - entities: - - uid: 177 - components: - - type: Transform - pos: 1.5699697,-0.44908836 - parent: 325 - - type: Physics - canCollide: False -- proto: ToyFigurineNukie - entities: - - uid: 10 - components: - - type: Transform - pos: -2.3371089,-2.140279 - parent: 325 -- proto: VendingMachineSyndieDrobe - entities: - - uid: 163 - components: - - type: Transform - pos: -2.5,-0.5 - parent: 325 -- proto: WallPlastitanium - entities: - - uid: 7 - components: - - type: Transform - pos: -2.5,0.5 - parent: 325 - - uid: 8 - components: - - type: Transform - pos: -3.5,0.5 - parent: 325 - - uid: 9 - components: - - type: Transform - pos: -3.5,-0.5 - parent: 325 - - uid: 11 - components: - - type: Transform - pos: 1.5,0.5 - parent: 325 - - uid: 12 - components: - - type: Transform - pos: 2.5,0.5 - parent: 325 - - uid: 13 - components: - - type: Transform - pos: -4.5,-0.5 - parent: 325 - - uid: 22 - components: - - type: Transform - pos: 3.5,-0.5 - parent: 325 - - uid: 25 - components: - - type: Transform - pos: 3.5,-2.5 - parent: 325 - - uid: 27 - components: - - type: Transform - pos: -3.5,-2.5 - parent: 325 - - uid: 28 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -3.5,-6.5 - parent: 325 - - uid: 29 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-6.5 - parent: 325 - - uid: 30 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-6.5 - parent: 325 - - uid: 31 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -3.5,-8.5 - parent: 325 - - uid: 32 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-8.5 - parent: 325 - - uid: 33 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-6.5 - parent: 325 - - uid: 34 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-3.5 - parent: 325 - - uid: 35 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-9.5 - parent: 325 - - uid: 36 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-8.5 - parent: 325 - - uid: 37 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-9.5 - parent: 325 - - uid: 38 - components: - - type: Transform - pos: -4.5,-2.5 - parent: 325 - - uid: 39 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-3.5 - parent: 325 - - uid: 44 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-8.5 - parent: 325 - - uid: 48 - components: - - type: Transform - pos: 2.5,-7.5 - parent: 325 - - uid: 49 - components: - - type: Transform - pos: -3.5,-7.5 - parent: 325 - - uid: 54 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-3.5 - parent: 325 - - uid: 55 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -3.5,-3.5 - parent: 325 - - uid: 60 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-6.5 - parent: 325 - - uid: 61 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-6.5 - parent: 325 - - uid: 62 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-3.5 - parent: 325 - - uid: 63 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-3.5 - parent: 325 - - uid: 66 - components: - - type: Transform - pos: 0.5,-9.5 - parent: 325 - - uid: 69 - components: - - type: Transform - pos: -2.5,1.5 - parent: 325 - - uid: 73 - components: - - type: Transform - pos: 1.5,1.5 - parent: 325 - - uid: 80 - components: - - type: Transform - pos: 2.5,-2.5 - parent: 325 - - uid: 81 - components: - - type: Transform - pos: 2.5,-0.5 - parent: 325 - - uid: 92 - components: - - type: Transform - pos: -1.5,-9.5 - parent: 325 - - uid: 108 - components: - - type: Transform - pos: -0.5,-9.5 - parent: 325 -- proto: WallPlastitaniumDiagonal - entities: - - uid: 23 - components: - - type: Transform - pos: -4.5,0.5 - parent: 325 - - uid: 43 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 3.5,0.5 - parent: 325 - - uid: 68 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,2.5 - parent: 325 - - uid: 79 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -4.5,-3.5 - parent: 325 - - uid: 82 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 3.5,-3.5 - parent: 325 - - uid: 86 - components: - - type: Transform - pos: -2.5,2.5 - parent: 325 -- proto: WindoorSecure - entities: - - uid: 166 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-1.5 - parent: 325 - - uid: 206 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-0.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 346 -- proto: YellowOxygenTankFilled - entities: - - uid: 167 - components: - - type: Transform - pos: 1.60798,-0.3062118 - parent: 325 -... +meta: + format: 6 + postmapinit: false +tilemap: + 0: Space + 29: FloorDark + 84: FloorShuttleRed + 104: FloorTechMaint + 105: FloorTechMaint2 + 118: FloorWood + 120: Lattice + 121: Plating +entities: +- proto: "" + entities: + - uid: 325 + components: + - type: MetaData + - type: Transform + pos: 0.5638949,0.47865233 + parent: invalid + - type: MapGrid + chunks: + -1,-1: + ind: -1,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAdgAAAAAAdgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAdgAAAAADdgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAADHQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaQAAAAAAaQAAAAAAHQAAAAABHQAAAAABHQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAHQAAAAABHQAAAAACHQAAAAAB + version: 6 + 0,-1: + ind: 0,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAACeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAABeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABHQAAAAABHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADHQAAAAACeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + -1,0: + ind: -1,0 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAVAAAAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + 0,0: + ind: 0,0 + tiles: VAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + - type: Broadphase + - type: Physics + bodyStatus: InAir + angularDamping: 0.05 + linearDamping: 0.05 + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: Shuttle + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: + - node: + color: '#FFFFFFFF' + id: BrickTileDarkCornerNe + decals: + 11: 1,-1 + - node: + color: '#FFFFFFFF' + id: BrickTileDarkCornerNw + decals: + 5: -3,-1 + - node: + color: '#FFFFFFFF' + id: BrickTileDarkCornerSe + decals: + 4: 1,-3 + - node: + color: '#FFFFFFFF' + id: BrickTileDarkCornerSw + decals: + 3: -3,-3 + - node: + color: '#FFFFFFFF' + id: BrickTileDarkLineS + decals: + 0: -1,-3 + 1: -2,-3 + 2: 0,-3 + - node: + color: '#7F1C1FFF' + id: BrickTileWhiteCornerNe + decals: + 13: 1,-1 + - node: + color: '#7F1C1FFF' + id: BrickTileWhiteCornerNw + decals: + 12: -3,-1 + - node: + color: '#7F1C1FFF' + id: BrickTileWhiteCornerSe + decals: + 9: 1,-3 + - node: + color: '#7F1C1FFF' + id: BrickTileWhiteCornerSw + decals: + 10: -3,-3 + - node: + color: '#7F1C1FFF' + id: BrickTileWhiteLineS + decals: + 6: -2,-3 + 7: -1,-3 + 8: 0,-3 + - node: + color: '#FFFFFFFF' + id: Delivery + decals: + 23: 2,-2 + 24: -4,-2 + - node: + color: '#FFFFFFFF' + id: WarnLineE + decals: + 14: 1,-2 + - node: + color: '#FFFFFFFF' + id: WarnLineS + decals: + 16: -3,-2 + - node: + color: '#FFFFFFFF' + id: WarnLineW + decals: + 15: -1,-1 + - node: + color: '#FFFFFFFF' + id: WoodTrimThinLineN + decals: + 17: -1,-5 + 18: 0,-5 + 19: -2,-5 + - node: + color: '#FFFFFFFF' + id: WoodTrimThinLineS + decals: + 20: -2,-6 + 21: -1,-6 + 22: 0,-6 + - type: GridAtmosphere + version: 2 + data: + tiles: + -1,-1: + 0: 65535 + 0,-1: + 0: 65535 + -2,-1: + 0: 52424 + -1,-3: + 0: 65280 + -1,-2: + 0: 65535 + 0,-3: + 0: 30464 + 0,-2: + 0: 30583 + -2,0: + 0: 8 + -1,0: + 0: 3839 + 0,0: + 0: 895 + uniqueMixes: + - volume: 2500 + temperature: 293.15 + moles: + - 21.824879 + - 82.10312 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + chunkSize: 4 + - type: GasTileOverlay + - type: RadiationGridResistance + - type: GravityShake + shakeTimes: 10 + - type: SpreaderGrid + - type: GridPathfinding +- proto: AirCanister + entities: + - uid: 91 + components: + - type: Transform + pos: -0.5,-8.5 + parent: 325 + - type: AtmosDevice + joinedGrid: 325 +- proto: AirlockExternalShuttleSyndicateLocked + entities: + - uid: 142 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-1.5 + parent: 325 +- proto: AirlockSyndicateLocked + entities: + - uid: 20 + components: + - type: Transform + pos: -0.5,-3.5 + parent: 325 + - uid: 88 + components: + - type: Transform + pos: -0.5,-6.5 + parent: 325 +- proto: APCBasic + entities: + - uid: 107 + components: + - type: Transform + pos: 0.5,-6.5 + parent: 325 + - type: PowerNetworkBattery + loadingNetworkDemand: 15107 + currentReceiving: 15106.935 + currentSupply: 15107 + supplyRampPosition: 0.064453125 +- proto: AtmosDeviceFanTiny + entities: + - uid: 6 + components: + - type: Transform + pos: -3.5,-1.5 + parent: 325 +- proto: Bed + entities: + - uid: 76 + components: + - type: Transform + pos: 0.5,-5.5 + parent: 325 +- proto: BedsheetSyndie + entities: + - uid: 164 + components: + - type: Transform + pos: 0.5,-5.5 + parent: 325 +- proto: BlastDoorOpen + entities: + - uid: 190 + components: + - type: Transform + pos: 1.5,-5.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 331 + - type: DeviceLinkSink + links: + - 205 + - uid: 191 + components: + - type: Transform + pos: 1.5,-4.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 332 + - type: DeviceLinkSink + links: + - 205 + - uid: 192 + components: + - type: Transform + pos: -2.5,-5.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 333 + - type: DeviceLinkSink + links: + - 205 + - uid: 193 + components: + - type: Transform + pos: -2.5,-4.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 334 + - type: DeviceLinkSink + links: + - 205 + - uid: 196 + components: + - type: Transform + pos: 3.5,-1.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 337 + - type: DeviceLinkSink + links: + - 205 + - uid: 198 + components: + - type: Transform + pos: -1.5,1.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 339 + - type: DeviceLinkSink + links: + - 205 + - uid: 199 + components: + - type: Transform + pos: -1.5,2.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 340 + - type: DeviceLinkSink + links: + - 205 + - uid: 200 + components: + - type: Transform + pos: -0.5,2.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 341 + - type: DeviceLinkSink + links: + - 205 + - uid: 201 + components: + - type: Transform + pos: 0.5,2.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 342 + - type: DeviceLinkSink + links: + - 205 + - uid: 202 + components: + - type: Transform + pos: 0.5,1.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 343 + - type: DeviceLinkSink + links: + - 205 +- proto: BoxMRE + entities: + - uid: 320 + components: + - type: Transform + pos: 0.70504504,-7.29326 + parent: 325 +- proto: CableApcExtension + entities: + - uid: 120 + components: + - type: Transform + pos: 0.5,-6.5 + parent: 325 + - uid: 121 + components: + - type: Transform + pos: -0.5,-6.5 + parent: 325 + - uid: 122 + components: + - type: Transform + pos: -0.5,-7.5 + parent: 325 + - uid: 123 + components: + - type: Transform + pos: -0.5,-8.5 + parent: 325 + - uid: 124 + components: + - type: Transform + pos: -1.5,-8.5 + parent: 325 + - uid: 125 + components: + - type: Transform + pos: 0.5,-8.5 + parent: 325 + - uid: 126 + components: + - type: Transform + pos: 1.5,-8.5 + parent: 325 + - uid: 127 + components: + - type: Transform + pos: -2.5,-8.5 + parent: 325 + - uid: 128 + components: + - type: Transform + pos: -3.5,-8.5 + parent: 325 + - uid: 129 + components: + - type: Transform + pos: -3.5,-7.5 + parent: 325 + - uid: 130 + components: + - type: Transform + pos: 2.5,-8.5 + parent: 325 + - uid: 131 + components: + - type: Transform + pos: 2.5,-7.5 + parent: 325 + - uid: 132 + components: + - type: Transform + pos: -0.5,-5.5 + parent: 325 + - uid: 133 + components: + - type: Transform + pos: -0.5,-4.5 + parent: 325 + - uid: 134 + components: + - type: Transform + pos: -0.5,-3.5 + parent: 325 + - uid: 135 + components: + - type: Transform + pos: -0.5,-2.5 + parent: 325 + - uid: 136 + components: + - type: Transform + pos: -0.5,-1.5 + parent: 325 + - uid: 137 + components: + - type: Transform + pos: -0.5,-0.5 + parent: 325 + - uid: 138 + components: + - type: Transform + pos: -0.5,0.5 + parent: 325 + - uid: 139 + components: + - type: Transform + pos: -0.5,1.5 + parent: 325 + - uid: 140 + components: + - type: Transform + pos: -0.5,2.5 + parent: 325 + - uid: 141 + components: + - type: Transform + pos: -1.5,1.5 + parent: 325 + - uid: 143 + components: + - type: Transform + pos: 0.5,1.5 + parent: 325 + - uid: 145 + components: + - type: Transform + pos: -1.5,-1.5 + parent: 325 + - uid: 146 + components: + - type: Transform + pos: -2.5,-1.5 + parent: 325 + - uid: 147 + components: + - type: Transform + pos: -3.5,-1.5 + parent: 325 + - uid: 148 + components: + - type: Transform + pos: -4.5,-1.5 + parent: 325 + - uid: 149 + components: + - type: Transform + pos: 0.5,-1.5 + parent: 325 + - uid: 150 + components: + - type: Transform + pos: 1.5,-1.5 + parent: 325 + - uid: 151 + components: + - type: Transform + pos: 2.5,-1.5 + parent: 325 + - uid: 152 + components: + - type: Transform + pos: 3.5,-1.5 + parent: 325 + - uid: 153 + components: + - type: Transform + pos: 0.5,-4.5 + parent: 325 + - uid: 154 + components: + - type: Transform + pos: 1.5,-4.5 + parent: 325 + - uid: 155 + components: + - type: Transform + pos: 1.5,-5.5 + parent: 325 + - uid: 156 + components: + - type: Transform + pos: -1.5,-4.5 + parent: 325 + - uid: 157 + components: + - type: Transform + pos: -2.5,-4.5 + parent: 325 + - uid: 158 + components: + - type: Transform + pos: -2.5,-5.5 + parent: 325 +- proto: CableHV + entities: + - uid: 111 + components: + - type: Transform + pos: 1.5,-7.5 + parent: 325 + - uid: 112 + components: + - type: Transform + pos: 0.5,-7.5 + parent: 325 + - uid: 113 + components: + - type: Transform + pos: -0.5,-7.5 + parent: 325 + - uid: 114 + components: + - type: Transform + pos: -1.5,-7.5 + parent: 325 + - uid: 115 + components: + - type: Transform + pos: -2.5,-7.5 + parent: 325 + - uid: 116 + components: + - type: Transform + pos: -1.5,-6.5 + parent: 325 +- proto: CableHVStack1 + entities: + - uid: 235 + components: + - type: Transform + parent: 41 + - type: Stack + count: 10 + - type: Physics + canCollide: False + - uid: 239 + components: + - type: Transform + parent: 56 + - type: Stack + count: 10 + - type: Physics + canCollide: False +- proto: CableMV + entities: + - uid: 117 + components: + - type: Transform + pos: -1.5,-6.5 + parent: 325 + - uid: 118 + components: + - type: Transform + pos: -0.5,-6.5 + parent: 325 + - uid: 119 + components: + - type: Transform + pos: 0.5,-6.5 + parent: 325 +- proto: CapacitorStockPart + entities: + - uid: 233 + components: + - type: Transform + parent: 41 + - type: Physics + canCollide: False + - uid: 234 + components: + - type: Transform + parent: 41 + - type: Physics + canCollide: False + - uid: 237 + components: + - type: Transform + parent: 56 + - type: Physics + canCollide: False + - uid: 238 + components: + - type: Transform + parent: 56 + - type: Physics + canCollide: False + - uid: 241 + components: + - type: Transform + parent: 58 + - type: Physics + canCollide: False + - uid: 242 + components: + - type: Transform + parent: 58 + - type: Physics + canCollide: False + - uid: 243 + components: + - type: Transform + parent: 58 + - type: Physics + canCollide: False + - uid: 254 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 261 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 268 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 275 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 282 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 289 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 296 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 303 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False +- proto: Carpet + entities: + - uid: 74 + components: + - type: Transform + pos: -0.5,-4.5 + parent: 325 + - uid: 89 + components: + - type: Transform + pos: -0.5,-5.5 + parent: 325 +- proto: Catwalk + entities: + - uid: 159 + components: + - type: Transform + pos: -1.5,-7.5 + parent: 325 + - uid: 160 + components: + - type: Transform + pos: -0.5,-7.5 + parent: 325 + - uid: 161 + components: + - type: Transform + pos: 0.5,-7.5 + parent: 325 +- proto: ChairOfficeDark + entities: + - uid: 93 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-2.5 + parent: 325 +- proto: ChairPilotSeat + entities: + - uid: 78 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,0.5 + parent: 325 +- proto: ComputerIFFSyndicate + entities: + - uid: 40 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,0.5 + parent: 325 +- proto: ComputerShuttleSyndie + entities: + - uid: 64 + components: + - type: Transform + pos: -0.5,1.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 245 +- proto: CyberPen + entities: + - uid: 77 + components: + - type: Transform + pos: -1.1813428,-5.15565 + parent: 325 +- proto: DoorElectronics + entities: + - uid: 331 + components: + - type: Transform + parent: 190 + - type: Physics + canCollide: False + - uid: 332 + components: + - type: Transform + parent: 191 + - type: Physics + canCollide: False + - uid: 333 + components: + - type: Transform + parent: 192 + - type: Physics + canCollide: False + - uid: 334 + components: + - type: Transform + parent: 193 + - type: Physics + canCollide: False + - uid: 337 + components: + - type: Transform + parent: 196 + - type: Physics + canCollide: False + - uid: 339 + components: + - type: Transform + parent: 198 + - type: Physics + canCollide: False + - uid: 340 + components: + - type: Transform + parent: 199 + - type: Physics + canCollide: False + - uid: 341 + components: + - type: Transform + parent: 200 + - type: Physics + canCollide: False + - uid: 342 + components: + - type: Transform + parent: 201 + - type: Physics + canCollide: False + - uid: 343 + components: + - type: Transform + parent: 202 + - type: Physics + canCollide: False + - uid: 346 + components: + - type: Transform + parent: 206 + - type: Physics + canCollide: False +- proto: DresserFilled + entities: + - uid: 85 + components: + - type: Transform + pos: 0.5,-4.5 + parent: 325 +- proto: DrinkNukieCan + entities: + - uid: 144 + components: + - type: Transform + pos: -2.6964839,-2.109029 + parent: 325 +- proto: FaxMachineSyndie + entities: + - uid: 46 + components: + - type: Transform + pos: -1.5,-5.5 + parent: 325 + - type: FaxMachine + name: Striker +- proto: filingCabinetRandom + entities: + - uid: 75 + components: + - type: Transform + pos: -1.5,-4.5 + parent: 325 +- proto: Firelock + entities: + - uid: 224 + components: + - type: Transform + pos: -0.5,-3.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 350 + - type: DeviceNetwork + address: 44a24659 + receiveFrequency: 1621 + - uid: 225 + components: + - type: Transform + pos: -0.5,-6.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 351 + - type: DeviceNetwork + address: 6fdb75cf + receiveFrequency: 1621 +- proto: FirelockElectronics + entities: + - uid: 350 + components: + - type: Transform + parent: 224 + - type: Physics + canCollide: False + - uid: 351 + components: + - type: Transform + parent: 225 + - type: Physics + canCollide: False +- proto: FoodBoxDonut + entities: + - uid: 87 + components: + - type: Transform + pos: -2.470145,-2.3953476 + parent: 325 +- proto: GasPipeFourway + entities: + - uid: 216 + components: + - type: Transform + pos: -0.5,-1.5 + parent: 325 +- proto: GasPipeStraight + entities: + - uid: 211 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-6.5 + parent: 325 + - uid: 213 + components: + - type: Transform + pos: -0.5,-4.5 + parent: 325 + - uid: 214 + components: + - type: Transform + pos: -0.5,-3.5 + parent: 325 + - uid: 215 + components: + - type: Transform + pos: -0.5,-2.5 + parent: 325 + - uid: 217 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-0.5 + parent: 325 +- proto: GasPipeTJunction + entities: + - uid: 210 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -0.5,-7.5 + parent: 325 + - uid: 212 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -0.5,-5.5 + parent: 325 +- proto: GasPort + entities: + - uid: 59 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-8.5 + parent: 325 + - type: AtmosDevice + joinedGrid: 325 +- proto: GasVentPump + entities: + - uid: 218 + components: + - type: Transform + pos: -0.5,0.5 + parent: 325 + - type: DeviceNetwork + address: Vnt-5f41a0ae + transmitFrequency: 1621 + receiveFrequency: 1621 + - type: AtmosDevice + joinedGrid: 325 + - uid: 219 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-1.5 + parent: 325 + - type: DeviceNetwork + address: Vnt-129c27d2 + transmitFrequency: 1621 + receiveFrequency: 1621 + - type: AtmosDevice + joinedGrid: 325 + - uid: 220 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,-1.5 + parent: 325 + - type: DeviceNetwork + address: Vnt-11c4609d + transmitFrequency: 1621 + receiveFrequency: 1621 + - type: AtmosDevice + joinedGrid: 325 + - uid: 221 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,-5.5 + parent: 325 + - type: DeviceNetwork + address: Vnt-6859729f + transmitFrequency: 1621 + receiveFrequency: 1621 + - type: AtmosDevice + joinedGrid: 325 + - uid: 222 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-7.5 + parent: 325 + - type: DeviceNetwork + address: Vnt-19d24c7f + transmitFrequency: 1621 + receiveFrequency: 1621 + - type: AtmosDevice + joinedGrid: 325 +- proto: GeneratorBasic15kW + entities: + - uid: 41 + components: + - type: Transform + pos: -2.5,-7.5 + parent: 325 + - type: PowerSupplier + supplyRampPosition: 7552.5303 + - type: ContainerContainer + containers: + machine_board: !type:Container + ents: + - 232 + machine_parts: !type:Container + ents: + - 233 + - 234 + - 235 + - uid: 56 + components: + - type: Transform + pos: 1.5,-7.5 + parent: 325 + - type: PowerSupplier + supplyRampPosition: 7552.5303 + - type: ContainerContainer + containers: + machine_board: !type:Container + ents: + - 236 + machine_parts: !type:Container + ents: + - 237 + - 238 + - 239 +- proto: GravityGeneratorMini + entities: + - uid: 57 + components: + - type: Transform + pos: -1.5,-8.5 + parent: 325 +- proto: Grille + entities: + - uid: 1 + components: + - type: Transform + pos: -0.5,2.5 + parent: 325 + - uid: 2 + components: + - type: Transform + pos: -1.5,2.5 + parent: 325 + - uid: 3 + components: + - type: Transform + pos: -1.5,1.5 + parent: 325 + - uid: 4 + components: + - type: Transform + pos: 0.5,2.5 + parent: 325 + - uid: 5 + components: + - type: Transform + pos: 0.5,1.5 + parent: 325 + - uid: 21 + components: + - type: Transform + pos: 3.5,-1.5 + parent: 325 + - uid: 50 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-5.5 + parent: 325 + - uid: 51 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-4.5 + parent: 325 + - uid: 52 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-5.5 + parent: 325 + - uid: 53 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-4.5 + parent: 325 +- proto: Gyroscope + entities: + - uid: 58 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,-8.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 240 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 241 + - 242 + - 243 + - 244 +- proto: GyroscopeMachineCircuitboard + entities: + - uid: 240 + components: + - type: Transform + parent: 58 + - type: Physics + canCollide: False +- proto: MedkitCombatFilled + entities: + - uid: 19 + components: + - type: Transform + pos: 1.48298,-0.3211529 + parent: 325 +- proto: MicroManipulatorStockPart + entities: + - uid: 250 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 251 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 252 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 253 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 257 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 258 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 259 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 260 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 264 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 265 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 266 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 267 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 271 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 272 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 273 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 274 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 278 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 279 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 280 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 281 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 285 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 286 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 287 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 288 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 292 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 293 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 294 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 295 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 299 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False + - uid: 300 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False + - uid: 301 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False + - uid: 302 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False +- proto: Mirror + entities: + - uid: 321 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -2.5,-3.5 + parent: 325 +- proto: NitrogenTankFilled + entities: + - uid: 105 + components: + - type: Transform + pos: 1.373605,-0.2749618 + parent: 325 +- proto: NukeCodePaper + entities: + - uid: 323 + components: + - type: Transform + pos: 1.561105,-2.5567772 + parent: 325 +- proto: PinpointerNuclear + entities: + - uid: 162 + components: + - type: Transform + pos: 1.3790641,-2.3161128 + parent: 325 + - type: Physics + canCollide: False +- proto: PlasmaReinforcedWindowDirectional + entities: + - uid: 104 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-0.5 + parent: 325 + - uid: 109 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-0.5 + parent: 325 +- proto: PlushieNuke + entities: + - uid: 47 + components: + - type: Transform + pos: 0.5061571,-5.233775 + parent: 325 +- proto: PortableGeneratorSuperPacmanMachineCircuitboard + entities: + - uid: 232 + components: + - type: Transform + parent: 41 + - type: Physics + canCollide: False + - uid: 236 + components: + - type: Transform + parent: 56 + - type: Physics + canCollide: False +- proto: PosterContrabandC20r + entities: + - uid: 24 + components: + - type: Transform + pos: 2.5,-2.5 + parent: 325 +- proto: PosterContrabandEnergySwords + entities: + - uid: 227 + components: + - type: Transform + pos: -2.5,-6.5 + parent: 325 +- proto: PosterContrabandNuclearDeviceInformational + entities: + - uid: 228 + components: + - type: Transform + pos: -2.5,0.5 + parent: 325 +- proto: PosterContrabandSyndicateRecruitment + entities: + - uid: 229 + components: + - type: Transform + pos: 0.5,-3.5 + parent: 325 +- proto: Poweredlight + entities: + - uid: 94 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,0.5 + parent: 325 + - uid: 110 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -2.5,-2.5 + parent: 325 +- proto: PoweredlightLED + entities: + - uid: 182 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 2.5,-5.5 + parent: 325 + - type: ApcPowerReceiver + powerLoad: 0 + - uid: 183 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-5.5 + parent: 325 + - type: ApcPowerReceiver + powerLoad: 0 + - uid: 184 + components: + - type: Transform + pos: -1.5,-7.5 + parent: 325 + - type: ApcPowerReceiver + powerLoad: 0 +- proto: PoweredSmallLight + entities: + - uid: 204 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-5.5 + parent: 325 + - type: ApcPowerReceiver + powerLoad: 0 +- proto: Rack + entities: + - uid: 83 + components: + - type: Transform + pos: 1.5,-0.5 + parent: 325 + - uid: 84 + components: + - type: Transform + pos: 1.5,-2.5 + parent: 325 +- proto: ReinforcedPlasmaWindow + entities: + - uid: 14 + components: + - type: Transform + pos: -1.5,1.5 + parent: 325 + - uid: 15 + components: + - type: Transform + pos: -1.5,2.5 + parent: 325 + - uid: 16 + components: + - type: Transform + pos: -0.5,2.5 + parent: 325 + - uid: 17 + components: + - type: Transform + pos: 0.5,2.5 + parent: 325 + - uid: 18 + components: + - type: Transform + pos: 0.5,1.5 + parent: 325 + - uid: 26 + components: + - type: Transform + pos: 3.5,-1.5 + parent: 325 + - uid: 42 + components: + - type: Transform + pos: 1.5,-4.5 + parent: 325 + - uid: 70 + components: + - type: Transform + pos: 1.5,-5.5 + parent: 325 + - uid: 71 + components: + - type: Transform + pos: -2.5,-4.5 + parent: 325 + - uid: 72 + components: + - type: Transform + pos: -2.5,-5.5 + parent: 325 +- proto: RemoteSignaller + entities: + - uid: 176 + components: + - type: Transform + pos: 1.3427892,-2.379079 + parent: 325 + - type: Physics + canCollide: False +- proto: SheetGlass1 + entities: + - uid: 244 + components: + - type: Transform + parent: 58 + - type: Stack + count: 2 + - type: Physics + canCollide: False +- proto: SheetSteel1 + entities: + - uid: 255 + components: + - type: Transform + parent: 95 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 262 + components: + - type: Transform + parent: 96 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 269 + components: + - type: Transform + parent: 97 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 276 + components: + - type: Transform + parent: 98 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 283 + components: + - type: Transform + parent: 99 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 290 + components: + - type: Transform + parent: 100 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 297 + components: + - type: Transform + parent: 101 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 304 + components: + - type: Transform + parent: 102 + - type: Stack + count: 5 + - type: Physics + canCollide: False +- proto: SignalButton + entities: + - uid: 205 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-3.5 + parent: 325 + - type: DeviceLinkSource + linkedPorts: + 193: + - Pressed: Toggle + 192: + - Pressed: Toggle + 190: + - Pressed: Toggle + 191: + - Pressed: Toggle + 196: + - Pressed: Toggle + 202: + - Pressed: Toggle + 201: + - Pressed: Toggle + 200: + - Pressed: Toggle + 199: + - Pressed: Toggle + 198: + - Pressed: Toggle +- proto: SignSpace + entities: + - uid: 230 + components: + - type: Transform + pos: -3.5,-0.5 + parent: 325 +- proto: SoapSyndie + entities: + - uid: 90 + components: + - type: Transform + pos: 0.5436061,-7.5129323 + parent: 325 +- proto: SpawnPointLoneNukeOperative + entities: + - uid: 322 + components: + - type: Transform + pos: -0.5,-4.5 + parent: 325 +- proto: StealthBox + entities: + - uid: 106 + components: + - type: Transform + pos: 0.49860507,-2.4513345 + parent: 325 + - type: Stealth + enabled: False + - type: EntityStorage + open: True +- proto: SubstationWallBasic + entities: + - uid: 103 + components: + - type: Transform + pos: -1.5,-6.5 + parent: 325 + - type: PowerNetworkBattery + loadingNetworkDemand: 15106.935 + currentReceiving: 15105.06 + currentSupply: 15106.935 + supplyRampPosition: 1.875 +- proto: SuitStorageSyndie + entities: + - uid: 67 + components: + - type: Transform + pos: 2.5,-1.5 + parent: 325 +- proto: SyndicateCommsComputerCircuitboard + entities: + - uid: 246 + components: + - type: Transform + parent: 65 + - type: Physics + canCollide: False +- proto: SyndicateComputerComms + entities: + - uid: 65 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,0.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 246 +- proto: SyndicateIDCard + entities: + - uid: 324 + components: + - type: Transform + pos: 1.57673,-2.3849022 + parent: 325 +- proto: SyndicateShuttleConsoleCircuitboard + entities: + - uid: 245 + components: + - type: Transform + parent: 64 + - type: Physics + canCollide: False +- proto: Table + entities: + - uid: 165 + components: + - type: Transform + pos: -2.5,-2.5 + parent: 325 +- proto: TableWood + entities: + - uid: 45 + components: + - type: Transform + pos: -1.5,-5.5 + parent: 325 +- proto: Thruster + entities: + - uid: 95 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-9.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 249 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 250 + - 251 + - 252 + - 253 + - 254 + - 255 + - uid: 96 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 2.5,-9.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 256 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 257 + - 258 + - 259 + - 260 + - 261 + - 262 + - uid: 97 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-4.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 263 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 264 + - 265 + - 266 + - 267 + - 268 + - 269 + - uid: 98 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-5.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 270 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 271 + - 272 + - 273 + - 274 + - 275 + - 276 + - uid: 99 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-4.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 277 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 278 + - 279 + - 280 + - 281 + - 282 + - 283 + - uid: 100 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-5.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 284 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 285 + - 286 + - 287 + - 288 + - 289 + - 290 + - uid: 101 + components: + - type: Transform + pos: -3.5,1.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 291 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 292 + - 293 + - 294 + - 295 + - 296 + - 297 + - uid: 102 + components: + - type: Transform + pos: 2.5,1.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 298 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 299 + - 300 + - 301 + - 302 + - 303 + - 304 +- proto: ThrusterMachineCircuitboard + entities: + - uid: 249 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 256 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 263 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 270 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 277 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 284 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 291 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 298 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False +- proto: ToolboxSyndicateFilled + entities: + - uid: 177 + components: + - type: Transform + pos: 1.5699697,-0.44908836 + parent: 325 + - type: Physics + canCollide: False +- proto: ToyFigurineNukie + entities: + - uid: 10 + components: + - type: Transform + pos: -2.3371089,-2.140279 + parent: 325 +- proto: VendingMachineSyndieDrobe + entities: + - uid: 163 + components: + - type: Transform + pos: -2.5,-0.5 + parent: 325 +- proto: WallPlastitanium + entities: + - uid: 7 + components: + - type: Transform + pos: -2.5,0.5 + parent: 325 + - uid: 8 + components: + - type: Transform + pos: -3.5,0.5 + parent: 325 + - uid: 9 + components: + - type: Transform + pos: -3.5,-0.5 + parent: 325 + - uid: 11 + components: + - type: Transform + pos: 1.5,0.5 + parent: 325 + - uid: 12 + components: + - type: Transform + pos: 2.5,0.5 + parent: 325 + - uid: 13 + components: + - type: Transform + pos: -4.5,-0.5 + parent: 325 + - uid: 22 + components: + - type: Transform + pos: 3.5,-0.5 + parent: 325 + - uid: 25 + components: + - type: Transform + pos: 3.5,-2.5 + parent: 325 + - uid: 27 + components: + - type: Transform + pos: -3.5,-2.5 + parent: 325 + - uid: 28 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-6.5 + parent: 325 + - uid: 29 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-6.5 + parent: 325 + - uid: 30 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-6.5 + parent: 325 + - uid: 31 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-8.5 + parent: 325 + - uid: 32 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-8.5 + parent: 325 + - uid: 33 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-6.5 + parent: 325 + - uid: 34 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-3.5 + parent: 325 + - uid: 35 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-9.5 + parent: 325 + - uid: 36 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-8.5 + parent: 325 + - uid: 37 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-9.5 + parent: 325 + - uid: 38 + components: + - type: Transform + pos: -4.5,-2.5 + parent: 325 + - uid: 39 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-3.5 + parent: 325 + - uid: 44 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-8.5 + parent: 325 + - uid: 48 + components: + - type: Transform + pos: 2.5,-7.5 + parent: 325 + - uid: 49 + components: + - type: Transform + pos: -3.5,-7.5 + parent: 325 + - uid: 54 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-3.5 + parent: 325 + - uid: 55 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-3.5 + parent: 325 + - uid: 60 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-6.5 + parent: 325 + - uid: 61 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-6.5 + parent: 325 + - uid: 62 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-3.5 + parent: 325 + - uid: 63 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-3.5 + parent: 325 + - uid: 66 + components: + - type: Transform + pos: 0.5,-9.5 + parent: 325 + - uid: 69 + components: + - type: Transform + pos: -2.5,1.5 + parent: 325 + - uid: 73 + components: + - type: Transform + pos: 1.5,1.5 + parent: 325 + - uid: 80 + components: + - type: Transform + pos: 2.5,-2.5 + parent: 325 + - uid: 81 + components: + - type: Transform + pos: 2.5,-0.5 + parent: 325 + - uid: 92 + components: + - type: Transform + pos: -1.5,-9.5 + parent: 325 + - uid: 108 + components: + - type: Transform + pos: -0.5,-9.5 + parent: 325 +- proto: WallPlastitaniumDiagonal + entities: + - uid: 23 + components: + - type: Transform + pos: -4.5,0.5 + parent: 325 + - uid: 43 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 3.5,0.5 + parent: 325 + - uid: 68 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,2.5 + parent: 325 + - uid: 79 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -4.5,-3.5 + parent: 325 + - uid: 82 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 3.5,-3.5 + parent: 325 + - uid: 86 + components: + - type: Transform + pos: -2.5,2.5 + parent: 325 +- proto: WindoorSecure + entities: + - uid: 166 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-1.5 + parent: 325 + - uid: 206 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-0.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 346 +- proto: YellowOxygenTankFilled + entities: + - uid: 167 + components: + - type: Transform + pos: 1.60798,-0.3062118 + parent: 325 +... diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index d699229b02..00b799670f 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -4,10 +4,8 @@ # If item is not in list it will go at the bottom (ties broken by alert type enum value) id: BaseAlertOrder order: - - alertType: ChangelingBiomass # goobstation - changelings - category: Health - category: Stamina - - alertType: ChangelingChemicals # goobstation - changelings - alertType: SuitPower - category: Internals - alertType: Fire diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml index 474dd8200a..0bcd71fbad 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml @@ -5,7 +5,6 @@ id: BaseMobVulpkanin abstract: true components: - - type: Absorbable # Goobstation - changelings - type: HumanoidAppearance species: Vulpkanin - type: Hunger diff --git a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml index f39c6a0f69..712dfcf3a0 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml @@ -90,7 +90,8 @@ - !type:DepartmentTimeRequirement # DeltaV - Security dept time requirement department: Security time: 36000 # DeltaV - 10 hours - - type: GhostRoleAntagSpawner + - type: GhostRoleMobSpawner + prototype: MobHumanLoneNuclearOperative - type: Sprite sprite: Markers/jobs.rsi layers: @@ -98,33 +99,6 @@ - sprite: Structures/Wallmounts/signs.rsi state: radiation -- type: entity - noSpawn: true - parent: SpawnPointLoneNukeOperative - id: SpawnPointNukeopsCommander - components: - - type: GhostRole - name: roles-antag-nuclear-operative-commander-name - description: roles-antag-nuclear-operative-commander-objective - -- type: entity - noSpawn: true - parent: SpawnPointLoneNukeOperative - id: SpawnPointNukeopsMedic - components: - - type: GhostRole - name: roles-antag-nuclear-operative-agent-name - description: roles-antag-nuclear-operative-agent-objective - -- type: entity - noSpawn: true - parent: SpawnPointLoneNukeOperative - id: SpawnPointNukeopsOperative - components: - - type: GhostRole - name: roles-antag-nuclear-operative-name - description: roles-antag-nuclear-operative-objective - - type: entity parent: MarkerBase id: SpawnPointGhostDragon diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml index 38da683652..c135ac2b82 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml @@ -5,7 +5,6 @@ id: BaseMobArachnid abstract: true components: - - type: Absorbable # Goobstation - changelings - type: Body prototype: Arachnid requiredLegs: 2 # It would be funny if arachnids could use their little back limbs to move around once they lose their legs, but just something to consider post-woundmed diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index a807119df0..9fb241c40d 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -195,8 +195,6 @@ type: HumanoidMarkingModifierBoundUserInterface - key: enum.StrippingUiKey.Key type: StrippableBoundUserInterface - - key: enum.StoreUiKey.Key - type: StoreBoundUserInterface - type: Puller - type: Speech speechSounds: Alto @@ -376,7 +374,6 @@ - type: Body prototype: Human requiredLegs: 2 - - type: Absorbable # Goobstation - changelings - type: UserInterface interfaces: - key: enum.HumanoidMarkingModifierKey.Key # sure, this can go here too diff --git a/Resources/Prototypes/Entities/Mobs/Species/diona.yml b/Resources/Prototypes/Entities/Mobs/Species/diona.yml index 7a8dc8160c..42383d9a42 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/diona.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/diona.yml @@ -5,7 +5,6 @@ id: BaseMobDiona abstract: true components: - - type: Absorbable # Goobstation - changelings - type: HumanoidAppearance species: Diona - type: Hunger diff --git a/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml b/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml index 045eca86bc..055c6522dd 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml @@ -5,7 +5,6 @@ id: BaseMobDwarf abstract: true components: - - type: Absorbable # Goobstation - changelings - type: Hunger - type: Thirst - type: Carriable # Carrying system from nyanotrasen. diff --git a/Resources/Prototypes/Entities/Mobs/Species/gingerbread.yml b/Resources/Prototypes/Entities/Mobs/Species/gingerbread.yml index 0660ea53d0..c514a6f1a0 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/gingerbread.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/gingerbread.yml @@ -5,7 +5,6 @@ id: BaseMobGingerbread abstract: true components: - - type: Absorbable # Goobstation - changelings - type: HumanoidAppearance species: Gingerbread - type: Icon diff --git a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml index 5295e8c4fe..60ccc31d79 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml @@ -31,7 +31,6 @@ type: HumanoidMarkingModifierBoundUserInterface - key: enum.StrippingUiKey.Key type: StrippableBoundUserInterface - - type: Absorbable - type: Sprite scale: 0.9, 0.9 layers: diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 6b187c52d3..ac373725ce 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -5,7 +5,6 @@ name: Urist McHands abstract: true components: - - type: Absorbable # Goobstation - changelings - type: Hunger - type: Icon # It will not have an icon in the adminspawn menu without this. Body parts seem fine for whatever reason. sprite: Mobs/Species/Human/parts.rsi diff --git a/Resources/Prototypes/Entities/Mobs/Species/moth.yml b/Resources/Prototypes/Entities/Mobs/Species/moth.yml index c99b4b3a1a..1c55dcf0df 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/moth.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/moth.yml @@ -5,7 +5,6 @@ id: BaseMobMoth abstract: true components: - - type: Absorbable # Goobstation - changelings - type: HumanoidAppearance species: Moth - type: Hunger diff --git a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml index ff91d4c788..35f9e9fa39 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml @@ -5,7 +5,6 @@ id: BaseMobReptilian abstract: true components: - - type: Absorbable # Goobstation - changelings - type: HumanoidAppearance species: Reptilian - type: Hunger diff --git a/Resources/Prototypes/Entities/Mobs/Species/slime.yml b/Resources/Prototypes/Entities/Mobs/Species/slime.yml index af575159cd..0e05b0c827 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/slime.yml @@ -4,7 +4,6 @@ id: BaseMobSlimePerson abstract: true components: - - type: Absorbable # Goobstation - changelings - type: Hunger - type: Thirst - type: Carriable # Carrying system from nyanotrasen. diff --git a/Resources/Prototypes/Entities/Mobs/Species/vox.yml b/Resources/Prototypes/Entities/Mobs/Species/vox.yml index 71f484c132..a271e9d084 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/vox.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/vox.yml @@ -3,7 +3,6 @@ id: BaseMobVox abstract: true components: - - type: Absorbable # Goobstation - changelings - type: Hunger - type: Thirst - type: Icon diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 284ed00652..ca88511744 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -441,28 +441,11 @@ weight: 2 duration: 1 - type: ZombieRule - - type: AntagSelection - definitions: - - prefRoles: [ InitialInfected ] - max: 3 - playerRatio: 10 - blacklist: - components: - - ZombieImmune - - InitialInfectedExempt - briefing: - text: zombie-patientzero-role-greeting - color: Plum - sound: "/Audio/Ambience/Antag/zombie_start.ogg" - components: - - type: PendingZombie #less time to prepare than normal - minInitialInfectedGrace: 300 - maxInitialInfectedGrace: 450 - - type: ZombifyOnDeath - - type: IncurableZombie - mindComponents: - - type: InitialInfectedRole - prototype: InitialInfected + minStartDelay: 0 #let them know immediately + maxStartDelay: 10 + maxInitialInfected: 2 + minInitialInfectedGrace: 300 + maxInitialInfectedGrace: 450 - type: entity id: LoneOpsSpawn @@ -475,29 +458,7 @@ minimumPlayers: 15 reoccurrenceDelay: 45 duration: 1 - - type: LoadMapRule - mapPath: /Maps/Shuttles/striker.yml - - type: NukeopsRule - roundEndBehavior: Nothing - - type: AntagSelection - definitions: - - spawnerPrototype: SpawnPointLoneNukeOperative - min: 1 - max: 1 - pickPlayer: false - startingGear: SyndicateLoneOperativeGearFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - SyndicateNamesPrefix - - SyndicateNamesNormal - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: Nukeops + - type: LoneOpsSpawnRule - type: entity id: MassHallucinations diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index bb870f6007..37fc4b44cd 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -34,23 +34,6 @@ id: Thief components: - type: ThiefRule - - type: AntagSelection - definitions: - - prefRoles: [ Thief ] - maxRange: - min: 1 - max: 3 - playerRatio: 1 - allowNonHumans: true - multiAntagSetting: All - startingGear: ThiefGear - components: - - type: Pacified - mindComponents: - - type: ThiefRole - prototype: Thief - briefing: - sound: "/Audio/Misc/thief_greeting.ogg" - type: entity noSpawn: true diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index f3d0704ca5..0af55a7f9d 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -70,116 +70,31 @@ components: - type: GameRule minPlayers: 35 - - type: RandomMetadata #this generates the random operation name cuz it's cool. - nameSegments: - - operationPrefix - - operationSuffix - type: NukeopsRule - - type: LoadMapRule - gameMap: NukieOutpost - - type: AntagSelection - selectionTime: PrePlayerSpawn - definitions: - - prefRoles: [ NukeopsCommander ] - fallbackRoles: [ Nukeops, NukeopsMedic ] - spawnerPrototype: SpawnPointNukeopsCommander - startingGear: SyndicateCommanderGearFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-commander - - SyndicateNamesElite - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: NukeopsCommander - - prefRoles: [ NukeopsMedic ] - fallbackRoles: [ Nukeops, NukeopsCommander ] - spawnerPrototype: SpawnPointNukeopsMedic - startingGear: SyndicateOperativeMedicFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-agent - - SyndicateNamesNormal - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: NukeopsMedic - - prefRoles: [ Nukeops ] - fallbackRoles: [ NukeopsCommander, NukeopsMedic ] - spawnerPrototype: SpawnPointNukeopsOperative - max: 3 - playerRatio: 10 - startingGear: SyndicateOperativeGearFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-operator - - SyndicateNamesNormal - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: Nukeops + faction: Syndicate + +- type: entity + id: Pirates + parent: BaseGameRule + noSpawn: true + components: + - type: PiratesRule - type: entity id: Traitor parent: BaseGameRule noSpawn: true components: - - type: GameRule - minPlayers: 5 - delay: - min: 240 - max: 420 - type: TraitorRule - - type: AntagSelection - definitions: - - prefRoles: [ Traitor ] - max: 12 - playerRatio: 10 - blacklist: - components: - - AntagImmune - - Changeling # Goobstation - lateJoinAdditional: true - mindComponents: - - type: TraitorRole - prototype: Traitor - type: entity id: Revolutionary parent: BaseGameRule noSpawn: true components: - - type: GameRule - minPlayers: 15 - type: RevolutionaryRule - - type: AntagSelection - definitions: - - prefRoles: [ HeadRev ] - max: 2 - playerRatio: 20 # WD - briefing: - text: head-rev-role-greeting - color: CornflowerBlue - sound: "/Audio/Ambience/Antag/headrev_start.ogg" - startingGear: HeadRevGear - components: - - type: Revolutionary - - type: HeadRevolutionary - mindComponents: - - type: RevolutionaryRole - prototype: HeadRev + maxHeadRevs: 2 # DeltaV + playersPerHeadRev: 30 # DeltaV - need highpop readied up for multiple headrevs - type: entity id: Sandbox @@ -200,32 +115,7 @@ parent: BaseGameRule noSpawn: true components: - - type: GameRule - minPlayers: 20 - delay: - min: 600 - max: 900 - type: ZombieRule - - type: AntagSelection - definitions: - - prefRoles: [ InitialInfected ] - max: 6 - playerRatio: 10 - blacklist: - components: - - ZombieImmune - - InitialInfectedExempt - briefing: - text: zombie-patientzero-role-greeting - color: Plum - sound: "/Audio/Ambience/Antag/zombie_start.ogg" - components: - - type: PendingZombie - - type: ZombifyOnDeath - - type: IncurableZombie - mindComponents: - - type: InitialInfectedRole - prototype: InitialInfected # event schedulers - type: entity @@ -264,6 +154,7 @@ - id: BasicTrashVariationPass - id: SolidWallRustingVariationPass - id: ReinforcedWallRustingVariationPass + - id: CutWireVariationPass - id: BasicPuddleMessVariationPass prob: 0.99 orGroup: puddleMess diff --git a/Resources/Prototypes/Goobstation/Alerts/alerts.yml b/Resources/Prototypes/Goobstation/Alerts/alerts.yml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Resources/Prototypes/Goobstation/Changeling/Actions/changeling.yml b/Resources/Prototypes/Goobstation/Changeling/Actions/changeling.yml deleted file mode 100644 index 3ae08abd7d..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Actions/changeling.yml +++ /dev/null @@ -1,537 +0,0 @@ -# abilities - -# starting -- type: entity - id: ActionEvolutionMenu - name: Open evolution menu - description: Opens the evolution menu. - noSpawn: true - components: - - type: InstantAction - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: evolution_menu - event: !type:OpenEvolutionMenuEvent {} - - type: ChangelingAction - requireBiomass: false - useInLesserForm: true - -- type: entity - id: ActionAbsorbDNA - name: Absorb DNA - description: Absorb the target's DNA, husking them in the process. Costs 25 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - useDelay: 5 - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: absorb_dna - event: !type:AbsorbDNAEvent {} - - type: ChangelingAction - chemicalCost: 25 - requireBiomass: false - useInLesserForm: true - -- type: entity - id: ActionStingExtractDNA - name: Extract DNA sting - description: Steal your target's genetic information. Costs 25 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_extractdna - event: !type:StingExtractDNAEvent {} - - type: ChangelingAction - chemicalCost: 25 - useInLesserForm: true - -- type: entity - id: ActionChangelingTransformCycle - name: Cycle DNA - description: Cycle your available DNA. - noSpawn: true - components: - - type: InstantAction - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: transform_cycle - event: !type:ChangelingTransformCycleEvent {} - - type: ChangelingAction - requireBiomass: false - useInLesserForm: true - -- type: entity - id: ActionChangelingTransform - name: Transform - description: Transform into another humanoid. Doesn't come with clothes. Costs 5 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 5 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: transform - event: !type:ChangelingTransformEvent {} - - type: ChangelingAction - chemicalCost: 5 - useInLesserForm: true - -- type: entity - id: ActionEnterStasis - name: Enter regenerative stasis - description: Fake your death and start regenerating. Drains all your chemicals. Consumes biomass. - noSpawn: true - components: - - type: InstantAction - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: stasis_enter - event: !type:EnterStasisEvent {} - - type: ChangelingAction - biomassCost: 1 - useInLesserForm: true - -- type: entity - id: ActionExitStasis - name: Exit stasis - description: Rise from the dead with full health. Costs 60 chemicals. - noSpawn: true - components: - - type: InstantAction - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: stasis_exit - event: !type:ExitStasisEvent {} - - type: ChangelingAction - chemicalCost: 60 - useInLesserForm: true - -# combat -- type: entity - id: ActionToggleArmblade - name: Toggle Arm Blade - description: Reform one of your arms into a strong blade, composed of bone and flesh. Retract on secondary use. Costs 15 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 2 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: armblade - event: !type:ToggleArmbladeEvent {} - - type: ChangelingAction - chemicalCost: 15 - -- type: entity - id: ActionCreateBoneShard - name: Form Bone Shard - description: Break off shards of your bone and shape them into a throwing star. Costs 15 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 1 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: bone_shard - event: !type:CreateBoneShardEvent {} - - type: ChangelingAction - chemicalCost: 15 - -- type: entity - id: ActionToggleChitinousArmor - name: Toggle Armor - description: Inflate your body into an all-consuming chitinous mass of armor. Costs 25 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 2 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: chitinous_armor - event: !type:ToggleChitinousArmorEvent {} - - type: ChangelingAction - chemicalCost: 25 - requireAbsorbed: 2 - -- type: entity - id: ActionToggleOrganicShield - name: Form Shield - description: Reform one of your arms into a large, fleshy shield. Costs 20 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 2 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: organic_shield - event: !type:ToggleOrganicShieldEvent {} - - type: ChangelingAction - chemicalCost: 20 - requireAbsorbed: 1 - -- type: entity - id: ActionShriekDissonant - name: Dissonant Shriek - description: Emit an EMP blast, with just your voice. Costs 30 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 10 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: shriek_dissonant - event: !type:ShriekDissonantEvent {} - - type: ChangelingAction - chemicalCost: 30 - useInLesserForm: true - requireAbsorbed: 1 - -- type: entity - id: ActionShriekResonant - name: Resonant Shriek - description: Disorient people and break lights, with just your voice. Costs 30 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 10 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: shriek_resonant - event: !type:ShriekResonantEvent {} - - type: ChangelingAction - chemicalCost: 30 - useInLesserForm: true - requireAbsorbed: 1 - -- type: entity - id: ActionToggleStrainedMuscles - name: Strain Muscles - description: Move at extremely fast speeds. Deals stamina damage. - noSpawn: true - components: - - type: InstantAction - useDelay: 1 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: strained_muscles - event: !type:ToggleStrainedMusclesEvent {} - - type: ChangelingAction - chemicalCost: 0 - useInLesserForm: true - -# stings -- type: entity - id: ActionStingBlind - name: Blind Sting - description: Silently sting your target, blinding them for a short time and rendering them near sighted. Costs 35 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_blind - event: !type:StingBlindEvent {} - - type: ChangelingAction - chemicalCost: 35 - useInLesserForm: true - -- type: entity - id: ActionStingCryo - name: Cryogenic Sting - description: Silently sting your target, constantly slowing and freezing them. Costs 35 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_cryo - event: !type:StingCryoEvent {} - - type: ChangelingAction - chemicalCost: 35 - useInLesserForm: true - -- type: entity - id: ActionStingLethargic - name: Lethargic Sting - description: Silently inject a cocktail of anesthetics into the target. Costs 35 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_lethargic - event: !type:StingLethargicEvent {} - - type: ChangelingAction - chemicalCost: 35 - useInLesserForm: true - -- type: entity - id: ActionStingMute - name: Mute Sting - description: Silently sting your target, completely silencing them for a short time. Costs 35 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_mute - event: !type:StingMuteEvent {} - - type: ChangelingAction - chemicalCost: 35 - useInLesserForm: true - -- type: entity - id: ActionStingFakeArmblade - name: Fake Armblade Sting - description: Silently sting your target, making them grow a dull armblade for a short time. Costs 50 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_armblade - event: !type:StingFakeArmbladeEvent {} - - type: ChangelingAction - chemicalCost: 50 - useInLesserForm: true - -- type: entity - id: ActionStingTransform - name: Transformation Sting - description: Silently sting your target, transforming them into a person of your choosing. Costs 75 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_transform - event: !type:StingTransformEvent {} - - type: ChangelingAction - chemicalCost: 75 - useInLesserForm: true - -# utility -- type: entity - id: ActionAnatomicPanacea - name: Anatomic Panacea - description: Cure yourself of diseases, disabilities, radiation, toxins, drunkedness and brain damage. Costs 30 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 30 - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: anatomic_panacea - event: !type:ActionAnatomicPanaceaEvent {} - - type: ChangelingAction - chemicalCost: 30 - useInLesserForm: true - -- type: entity - id: ActionAugmentedEyesight - name: Augmented Eyesight - description: Toggle flash protection. - noSpawn: true - components: - - type: InstantAction - checkCanInteract: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: augmented_eyesight - event: !type:ActionAugmentedEyesightEvent {} - - type: ChangelingAction - chemicalCost: 0 - -- type: entity - id: ActionBiodegrade - name: Biodegrade - description: Vomit a caustic substance onto any restraints, or someone's face. Costs 30 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 5 - checkCanInteract: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: biodegrade - event: !type:ActionBiodegradeEvent {} - - type: ChangelingAction - chemicalCost: 30 - -- type: entity - id: ActionChameleonSkin - name: Chameleon Skin - description: Slowly blend in with the environment. Costs 25 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 1 - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: chameleon_skin - event: !type:ActionChameleonSkinEvent {} - - type: ChangelingAction - chemicalCost: 20 - -- type: entity - id: ActionEphedrineOverdose - name: Ephedrine Overdose - description: Inject some stimulants into yourself. Costs 30 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 10 - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: epinephrine_overdose - event: !type:ActionEphedrineOverdoseEvent {} - - type: ChangelingAction - chemicalCost: 30 - useInLesserForm: true - -- type: entity - id: ActionFleshmend - name: Fleshmend - description: Rapidly heal yourself. Costs 35 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 30 - checkCanInteract: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: fleshmend - event: !type:ActionFleshmendEvent {} - - type: ChangelingAction - chemicalCost: 35 - useInLesserForm: true - -- type: entity - id: ActionToggleLesserForm - name: Lesser Form - description: Abandon your current form and transform into a monkey. Costs 20 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 5 - checkCanInteract: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: lesser_form - event: !type:ActionLesserFormEvent {} - - type: ChangelingAction - chemicalCost: 20 - -- type: entity - id: ActionToggleSpacesuit - name: Toggle Space Suit - description: Make your body space proof. Costs 20 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 2 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: space_adaptation - event: !type:ActionSpacesuitEvent {} - - type: ChangelingAction - chemicalCost: 20 - -- type: entity - id: ActionHivemindAccess - name: Hivemind Access - description: Tune your chemical receptors for hivemind communication. - noSpawn: true - components: - - type: InstantAction - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: hivemind_access - event: !type:ActionHivemindAccessEvent {} - - type: ChangelingAction - chemicalCost: 0 - useInLesserForm: true diff --git a/Resources/Prototypes/Goobstation/Changeling/Alerts/changeling.yml b/Resources/Prototypes/Goobstation/Changeling/Alerts/changeling.yml deleted file mode 100644 index 6d14d49c06..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Alerts/changeling.yml +++ /dev/null @@ -1,35 +0,0 @@ -- type: alert - id: ChangelingChemicals - icons: - - sprite: /Textures/Goobstation/Changeling/changeling_chemicals.rsi - state: 0 - alertViewEntity: AlertChangelingChemicalsSpriteView - name: alerts-changeling-chemicals-name - description: alerts-changeling-chemicals-desc - -- type: alert - id: ChangelingBiomass - icons: - - sprite: /Textures/Goobstation/Changeling/changeling_biomass.rsi - state: 0 - alertViewEntity: AlertChangelingBiomassSpriteView - name: alerts-changeling-biomass-name - description: alerts-changeling-biomass-desc - -- type: entity - id: AlertChangelingChemicalsSpriteView - categories: [ HideSpawnMenu ] - components: - - type: Sprite - sprite: /Textures/Goobstation/Changeling/changeling_chemicals.rsi - layers: - - map: [ "enum.AlertVisualLayers.Base" ] - -- type: entity - id: AlertChangelingBiomassSpriteView - categories: [ HideSpawnMenu ] - components: - - type: Sprite - sprite: /Textures/Goobstation/Changeling/changeling_biomass.rsi - layers: - - map: [ "enum.AlertVisualLayers.Base" ] diff --git a/Resources/Prototypes/Goobstation/Changeling/Catalog/changeling_catalog.yml b/Resources/Prototypes/Goobstation/Changeling/Catalog/changeling_catalog.yml deleted file mode 100644 index 00eba5e962..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Catalog/changeling_catalog.yml +++ /dev/null @@ -1,341 +0,0 @@ -# combat - -- type: listing - id: EvolutionMenuCombatArmblade - name: evolutionmenu-combat-armblade-name - description: evolutionmenu-combat-armblade-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: armblade } - productAction: ActionToggleArmblade - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatBoneShard - name: evolutionmenu-combat-boneshard-name - description: evolutionmenu-combat-boneshard-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: bone_shard } - productAction: ActionCreateBoneShard - cost: - EvolutionPoint: 3 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatChitinousArmor - name: evolutionmenu-combat-armor-name - description: evolutionmenu-combat-armor-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: chitinous_armor } - productAction: ActionToggleChitinousArmor - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatOrganicShield - name: evolutionmenu-combat-shield-name - description: evolutionmenu-combat-shield-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: organic_shield } - productAction: ActionToggleOrganicShield - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatShriekDissonant - name: evolutionmenu-combat-shriek-dissonant-name - description: evolutionmenu-combat-shriek-dissonant-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: shriek_dissonant } - productAction: ActionShriekDissonant - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatShriekResonant - name: evolutionmenu-combat-shriek-resonant-name - description: evolutionmenu-combat-shriek-resonant-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: shriek_resonant } - productAction: ActionShriekResonant - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatMuscles - name: evolutionmenu-combat-strainedmuscles-name - description: evolutionmenu-combat-strainedmuscles-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: strained_muscles } - productAction: ActionToggleStrainedMuscles - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -#- type: listing -# id: EvolutionMenuCombatSpiders -# name: evolutionmenu-combat-spiders-name -# description: evolutionmenu-combat-spiders-desc -# icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: spiders_spawn } -# productAction: ActionSpawnChangelingSpider -# cost: -# EvolutionPoint: 6 -# categories: -# - ChangelingAbilityCombat -# consitions: -# - !type:ListingLimitedStockCondition -# stock: 1 - -# sting - -- type: listing - id: EvolutionMenuStingBlind - name: evolutionmenu-sting-blind-name - description: evolutionmenu-sting-blind-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_blind } - productAction: ActionStingBlind - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuStingCryo - name: evolutionmenu-sting-cryo-name - description: evolutionmenu-sting-cryo-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_cryo } - productAction: ActionStingCryo - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuStingLethargic - name: evolutionmenu-sting-lethargic-name - description: evolutionmenu-sting-lethargic-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_lethargic } - productAction: ActionStingLethargic - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuStingMute - name: evolutionmenu-sting-mute-name - description: evolutionmenu-sting-mute-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_mute } - productAction: ActionStingMute - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuStingFakeArmblade - name: evolutionmenu-sting-armblade-name - description: evolutionmenu-sting-armblade-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_armblade } - productAction: ActionStingFakeArmblade - cost: - EvolutionPoint: 5 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuStingTransform - name: evolutionmenu-sting-transform-name - description: evolutionmenu-sting-transform-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_transform } - productAction: ActionStingTransform - cost: - EvolutionPoint: 6 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -# utility - -- type: listing - id: EvolutionMenuUtilityPanacea - name: evolutionmenu-utility-panacea-name - description: evolutionmenu-utility-panacea-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: anatomic_panacea } - productAction: ActionAnatomicPanacea - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityEyesight - name: evolutionmenu-utility-eyesight-name - description: evolutionmenu-utility-eyesight-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: augmented_eyesight } - productAction: ActionAugmentedEyesight - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityBiodegrade - name: evolutionmenu-utility-biodegrade-name - description: evolutionmenu-utility-biodegrade-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: biodegrade } - productAction: ActionBiodegrade - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityChameleon - name: evolutionmenu-utility-chameleon-name - description: evolutionmenu-utility-chameleon-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: chameleon_skin } - productAction: ActionChameleonSkin - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityOverdose - name: evolutionmenu-utility-stims-name - description: evolutionmenu-utility-stims-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: epinephrine_overdose } - productAction: ActionEphedrineOverdose - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityFleshmend - name: evolutionmenu-utility-fleshmend-name - description: evolutionmenu-utility-fleshmend-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: fleshmend } - productAction: ActionFleshmend - cost: - EvolutionPoint: 5 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -#- type: listing -# id: EvolutionMenuUtilityLastResort -# name: evolutionmenu-utility-lastresort-name -# description: evolutionmenu-utility-lastresort-desc -# icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: last_resort } -# productAction: ActionLastResort -# cost: -# EvolutionPoint: 2 -# categories: -# - ChangelingAbilityUtility -# conditions: -# - !type:ListingLimitedStockCondition -# stock: 1 - -- type: listing - id: EvolutionMenuUtilityLesserForm - name: evolutionmenu-utility-lesserform-name - description: evolutionmenu-utility-lesserform-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: lesser_form } - productAction: ActionToggleLesserForm - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilitySpacesuit - name: evolutionmenu-utility-spacesuit-name - description: evolutionmenu-utility-spacesuit-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: space_adaptation } - productAction: ActionToggleSpacesuit - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityHivemindAccess - name: evolutionmenu-utility-hivemindaccess-name - description: evolutionmenu-utility-hivemindaccess-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: hivemind_access } - productAction: ActionHivemindAccess - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/hardsuit-helmets.yml deleted file mode 100644 index de00f10e38..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/hardsuit-helmets.yml +++ /dev/null @@ -1,16 +0,0 @@ -- type: entity - parent: ClothingHeadHardsuitBase - id: ChangelingClothingHeadHelmetHardsuit - name: organic space helmet - description: A spaceworthy biomass of pressure and temperature resistant tissue. - suffix: Unremoveable - components: - - type: Item - - type: Sprite - sprite: Goobstation/Changeling/ling_spacesuit_helmet.rsi - - type: Clothing - sprite: Goobstation/Changeling/ling_spacesuit_helmet.rsi - - type: PressureProtection - highPressureMultiplier: 0.225 - lowPressureMultiplier: 1000 - - type: Unremoveable \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/helmets.yml deleted file mode 100644 index 11d3e23b9f..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/helmets.yml +++ /dev/null @@ -1,19 +0,0 @@ -- type: entity - parent: ClothingHeadBase - id: ChangelingClothingHeadHelmet - name: chitinous helmet - description: An all-consuming chitinous mass of armor. - suffix: Unremoveable - components: - - type: Sprite - sprite: Goobstation/Changeling/ling_armor_helmet.rsi - - type: Clothing - sprite: Goobstation/Changeling/ling_armor_helmet.rsi - - type: Armor - modifiers: - coefficients: - Blunt: 0.9 - Slash: 0.9 - Piercing: 0.9 - Heat: 0.9 - - type: Unremoveable \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/armor.yml deleted file mode 100644 index 9627c226e1..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/armor.yml +++ /dev/null @@ -1,28 +0,0 @@ -- type: entity - parent: ClothingOuterBaseLarge - id: ChangelingClothingOuterArmor - name: chitinous armor - description: An all-consuming chitinous mass of armor. - suffix: Unremoveable - components: - - type: Sprite - sprite: Goobstation/Changeling/ling_armor.rsi - - type: Clothing - sprite: Goobstation/Changeling/ling_armor.rsi - - type: Armor - modifiers: - coefficients: - Blunt: 0.5 - Slash: 0.5 - Piercing: 0.5 - Heat: 0.5 - Radiation: 0.5 - Caustic: 0.5 - - type: ClothingSpeedModifier - walkModifier: 0.7 - sprintModifier: 0.65 - - type: HeldSpeedModifier - - type: ExplosionResistance - damageCoefficient: 0.5 - - type: GroupExamine - - type: Unremoveable \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/hardsuits.yml deleted file mode 100644 index dda0aef11c..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/hardsuits.yml +++ /dev/null @@ -1,30 +0,0 @@ -- type: entity - parent: ClothingOuterBaseLarge - id: ChangelingClothingOuterHardsuit - name: organic space suit - description: A spaceworthy biomass of pressure and temperature resistant tissue. - suffix: Unremoveable - components: - - type: Sprite - sprite: Goobstation/Changeling/ling_spacesuit.rsi - - type: Clothing - sprite: Goobstation/Changeling/ling_spacesuit.rsi - - type: PressureProtection - highPressureMultiplier: 0.225 - lowPressureMultiplier: 1000 - - type: TemperatureProtection - coefficient: 0.01 - - type: ExplosionResistance - damageCoefficient: 0.2 - - type: Armor - modifiers: - coefficients: - Blunt: 0.95 - Slash: 0.95 - Piercing: 1 - Heat: 0.5 - - type: ClothingSpeedModifier - walkModifier: 0.9 - sprintModifier: 0.9 - - type: HeldSpeedModifier - - type: Unremoveable \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Guidebook/changeling.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Guidebook/changeling.yml deleted file mode 100644 index b1c64f0bcb..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Guidebook/changeling.yml +++ /dev/null @@ -1,9 +0,0 @@ -- type: entity - id: GuidebookChangelingFluff - name: guidebook changeling - description: you shouldn't be seeing this normally. - noSpawn: true - components: - - type: Sprite - sprite: Goobstation/Changeling/Guidebook/guidebook_changeling.rsi - state: icon \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Shields/shields.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Shields/shields.yml deleted file mode 100644 index b6f409f432..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Shields/shields.yml +++ /dev/null @@ -1,31 +0,0 @@ -- type: entity - parent: BaseShield - id: ChangelingShield - name: oraganic shield - description: A large, fleshy shield. - suffix: Unremoveable - components: - - type: Unremoveable - - type: Sprite - sprite: Goobstation/Changeling/shields.rsi - state: ling-icon - - type: Item - sprite: Goobstation/Changeling/shields.rsi - heldPrefix: ling - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 60 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Melee/changeling_armblade.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Melee/changeling_armblade.yml deleted file mode 100644 index 6caa150e3f..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Melee/changeling_armblade.yml +++ /dev/null @@ -1,48 +0,0 @@ -- type: entity - parent: ArmBlade - id: ArmBladeChangeling - suffix: Unremoveable - components: - - type: Sharp - - type: Sprite - sprite: Goobstation/Changeling/arm_blade.rsi - state: icon - - type: MeleeWeapon - wideAnimationRotation: 90 - attackRate: 0.75 - damage: - types: - Blunt: 5 - Slash: 12.5 - Piercing: 10 - Structural: 10 - soundHit: - path: /Audio/Weapons/bladeslice.ogg - - type: Item - size: Ginormous - sprite: Goobstation/Changeling/arm_blade.rsi - - type: Prying - pryPowered: true - - type: Unremoveable - - type: Tool - qualities: - - Slicing - - Prying - - type: DisarmMalus - malus: 0 - -- type: entity - parent: ArmBladeChangeling - id: FakeArmBladeChangeling - components: - - type: MeleeWeapon - wideAnimationRotation: 90 - attackRate: 0.75 - damage: - types: - Blunt: 1 - Slash: 1 - Piercing: 1 - Structural: 1 - - type: TimedDespawn - lifetime: 60 \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Throwable/throwing_stars.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Throwable/throwing_stars.yml deleted file mode 100644 index 20d076903d..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Throwable/throwing_stars.yml +++ /dev/null @@ -1,12 +0,0 @@ -- type: entity - parent: ThrowingStar - id: ThrowingStarChangeling - name: bone shard - components: - - type: Sprite - sprite: Goobstation/Changeling/bone_shard.rsi - layers: - - state: icon - map: ["base"] - - type: TimedDespawn - lifetime: 30 \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Guidebook/antagonist.yml b/Resources/Prototypes/Goobstation/Changeling/Guidebook/antagonist.yml deleted file mode 100644 index e437e355fb..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Guidebook/antagonist.yml +++ /dev/null @@ -1,4 +0,0 @@ -- type: guideEntry - id: Changelings - name: guide-entry-changelings - text: "/ServerInfo/Goobstation/Changeling/Guidebook/Antagonist/Changelings.xml" \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Objectives/changeling.yml b/Resources/Prototypes/Goobstation/Changeling/Objectives/changeling.yml deleted file mode 100644 index fbbc93c84b..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Objectives/changeling.yml +++ /dev/null @@ -1,67 +0,0 @@ -- type: entity - abstract: true - parent: BaseObjective - id: BaseChangelingObjective - components: - - type: Objective - difficulty: 1.5 # unused but necessary i guess - issuer: hivemind - - type: RoleRequirement - roles: - components: - - ChangelingRole - -- type: entity - parent: [BaseChangelingObjective, BaseSurviveObjective] - id: ChangelingSurviveObjective - name: Survive - description: I must survive no matter what. - components: - - type: Objective - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: stasis_exit - -- type: entity - parent: BaseChangelingObjective - id: ChangelingAbsorbObjective - components: - - type: Objective - icon: - sprite: Mobs/Demons/abomination.rsi - state: dead - - type: NumberObjective - min: 2 - max: 4 - title: objective-condition-absorb-title - description: objective-condition-absorb-description - - type: AbsorbCondition - -- type: entity - parent: BaseChangelingObjective - id: ChangelingStealDNAObjective - components: - - type: Objective - icon: - sprite: Mobs/Species/Human/organs.rsi - state: brain - - type: NumberObjective - min: 6 - max: 9 - title: objective-condition-stealdna-title - description: objective-condition-stealdna-description - - type: StealDNACondition - -- type: entity - parent: BaseChangelingObjective - id: EscapeIdentityObjective - description: I need to escape on the evacuation shuttle. Undercover. - components: - - type: Objective - icon: - sprite: Objects/Magic/magicactions.rsi - state: blink - - type: ImpersonateCondition - - type: TargetObjective - title: objective-condition-escape-identity-title - - type: PickRandomPerson \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Reagents/biological.yml b/Resources/Prototypes/Goobstation/Changeling/Reagents/biological.yml deleted file mode 100644 index fa8bbb9644..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Reagents/biological.yml +++ /dev/null @@ -1,27 +0,0 @@ -- type: reagent - parent: Blood - id: BloodChangeling - -- type: reaction - id: ChangelingBloodBreakdown - source: true - requiredMixerCategories: - - Centrifuge - reactants: - BloodChangeling: - amount: 5 - products: - Water: 11 - Iron: 0.5 - Sugar: 2 - CarbonDioxide: 3 - Protein: 4 - effects: - - !type:CreateEntityReactionEffect - entity: FleshKudzu - - !type:ExplosionReactionEffect - explosionType: Default - maxIntensity: 2 - intensityPerUnit: 0.5 - intensitySlope: 4 - maxTotalIntensity: 1 diff --git a/Resources/Prototypes/Goobstation/Changeling/Roles/Antags/changeling.yml b/Resources/Prototypes/Goobstation/Changeling/Roles/Antags/changeling.yml deleted file mode 100644 index 0bfe8dc560..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Roles/Antags/changeling.yml +++ /dev/null @@ -1,7 +0,0 @@ -- type: antag - id: Changeling - name: roles-antag-changeling-name - antagonist: true - setPreference: true - objective: roles-antag-changeling-description - # guides: [ Changelings ] #Temporarily commented \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/StatusIcon/antag.yml b/Resources/Prototypes/Goobstation/Changeling/StatusIcon/antag.yml deleted file mode 100644 index db243ce62d..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/StatusIcon/antag.yml +++ /dev/null @@ -1,6 +0,0 @@ -- type: statusIcon - id: HivemindFaction - priority: 11 - icon: - sprite: /Textures/Interface/Misc/job_icons.rsi - state: Changeling diff --git a/Resources/Prototypes/Goobstation/Changeling/Store/categories.yml b/Resources/Prototypes/Goobstation/Changeling/Store/categories.yml deleted file mode 100644 index 8444dea1bf..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Store/categories.yml +++ /dev/null @@ -1,16 +0,0 @@ -# changeling - -- type: storeCategory - id: ChangelingAbilityCombat - name: store-ling-category-combat - priority: 0 - -- type: storeCategory - id: ChangelingAbilitySting - name: store-ling-category-sting - priority: 1 - -- type: storeCategory - id: ChangelingAbilityUtility - name: store-ling-category-utility - priority: 2 diff --git a/Resources/Prototypes/Goobstation/Changeling/Store/currency.yml b/Resources/Prototypes/Goobstation/Changeling/Store/currency.yml deleted file mode 100644 index 3ad315d05d..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Store/currency.yml +++ /dev/null @@ -1,4 +0,0 @@ -- type: currency - id: EvolutionPoint - displayName: store-currency-display-evolutionpoints - canWithdraw: false diff --git a/Resources/Prototypes/Goobstation/Changeling/ai_factions.yml b/Resources/Prototypes/Goobstation/Changeling/ai_factions.yml deleted file mode 100644 index 9a6e1745af..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/ai_factions.yml +++ /dev/null @@ -1,7 +0,0 @@ -- type: npcFaction - id: Changeling - hostile: - - NanoTrasen - - Syndicate - - Zombie - - Revolutionary \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/radio_channels.yml b/Resources/Prototypes/Goobstation/Changeling/radio_channels.yml deleted file mode 100644 index efb3b8da0f..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/radio_channels.yml +++ /dev/null @@ -1,7 +0,0 @@ -- type: radioChannel - id: Hivemind - name: chat-radio-hivemind - keycode: 'g' - frequency: 19840 - color: "#e36b00" - longRange: true \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/GameRules/roundstart.yml b/Resources/Prototypes/Goobstation/GameRules/roundstart.yml deleted file mode 100644 index 791b2dd41b..0000000000 --- a/Resources/Prototypes/Goobstation/GameRules/roundstart.yml +++ /dev/null @@ -1,149 +0,0 @@ -- type: entity - parent: BaseGameRule - id: Changeling - components: - - type: ChangelingRule - - type: GameRule - minPlayers: 15 - delay: - min: 240 - max: 420 - - type: AntagSelection - agentName: changeling-roundend-name - definitions: - - prefRoles: [ Changeling ] - max: 5 - playerRatio: 15 - lateJoinAdditional: true - mindComponents: - - type: ChangelingRole - prototype: Changeling - -- type: entity - parent: Traitor - id: CalmTraitor # For Dual Antag Gamemodes - components: - - type: GameRule - minPlayers: 30 - delay: - min: 240 - max: 420 - - type: AntagSelection - definitions: - - prefRoles: [ Traitor ] - max: 5 - playerRatio: 15 - blacklist: - components: - - AntagImmune - - Changeling - lateJoinAdditional: true - mindComponents: - - type: TraitorRole - prototype: Traitor - -- type: entity - parent: Changeling - id: CalmLing # For Dual Antag Gamemodes - components: - - type: GameRule - minPlayers: 30 - delay: - min: 240 - max: 420 - - type: AntagSelection - agentName: changeling-roundend-name - definitions: - - prefRoles: [ Changeling ] - max: 2 - playerRatio: 20 - lateJoinAdditional: true - mindComponents: - - type: ChangelingRole - prototype: Changeling - -- type: entity - parent: Nukeops - id: Calmops # For Dual Antag Gamemodes - components: - - type: GameRule - minPlayers: 30 - - type: LoadMapRule - gameMap: NukieOutpost - - type: AntagSelection - selectionTime: PrePlayerSpawn - definitions: - - prefRoles: [ NukeopsCommander ] - fallbackRoles: [ Nukeops, NukeopsMedic ] - spawnerPrototype: SpawnPointNukeopsCommander - startingGear: SyndicateCommanderGearFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-commander - - SyndicateNamesElite - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: NukeopsCommander - - prefRoles: [ NukeopsMedic ] - fallbackRoles: [ Nukeops, NukeopsCommander ] - spawnerPrototype: SpawnPointNukeopsMedic - startingGear: SyndicateOperativeMedicFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-agent - - SyndicateNamesNormal - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: NukeopsMedic - - prefRoles: [ Nukeops ] - fallbackRoles: [ NukeopsCommander, NukeopsMedic ] - spawnerPrototype: SpawnPointNukeopsOperative - max: 1 - playerRatio: 15 - startingGear: SyndicateOperativeGearFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-operator - - SyndicateNamesNormal - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: Nukeops - -- type: entity - id: CalmRevs # For Dual Antag Gamemodes - parent: BaseGameRule - components: - - type: GameRule - minPlayers: 30 - - type: RevolutionaryRule - - type: AntagSelection - definitions: - - prefRoles: [ HeadRev ] - max: 1 - playerRatio: 25 - briefing: - text: head-rev-role-greeting - color: CornflowerBlue - sound: "/Audio/Ambience/Antag/headrev_start.ogg" - startingGear: HeadRevGear - components: - - type: Revolutionary - - type: HeadRevolutionary - mindComponents: - - type: RevolutionaryRole - prototype: HeadRev diff --git a/Resources/Prototypes/Goobstation/game_presets.yml b/Resources/Prototypes/Goobstation/game_presets.yml deleted file mode 100644 index 36edaa285b..0000000000 --- a/Resources/Prototypes/Goobstation/game_presets.yml +++ /dev/null @@ -1,86 +0,0 @@ -- type: gamePreset - id: Changeling - alias: - - ling - - lings - - changeling - name: changeling-gamemode-title - description: changeling-gamemode-description - showInVote: false - rules: - - Changeling - - SubGamemodesRule - - BasicStationEventScheduler - - BasicRoundstartVariation - -- type: gamePreset - id: Traitorling - alias: - - lingtraitor - - traitorling - name: traitorling-title - description: traitorling-description - showInVote: false - rules: - - CalmLing - - CalmTraitor - - BasicStationEventScheduler - - BasicRoundstartVariation - -- type: gamePreset - id: NukeTraitor - alias: - - nuketot - - optraitor - - optot - name: nukeops-title - description: nukeops-description - showInVote: false - rules: - - Calmops - - CalmTraitor - - BasicStationEventScheduler - - BasicRoundstartVariation - -- type: gamePreset - id: NukeLing - alias: - - nukeling - - opling - name: nukeops-title - description: nukeops-description - showInVote: false - rules: - - Calmops - - CalmLing - - BasicStationEventScheduler - - BasicRoundstartVariation - -- type: gamePreset - id: RevTraitor - alias: - - revtraitor - - revtot - - totrevs - name: revtraitor-title - description: revtraitor-description - showInVote: false - rules: - - CalmRevs - - CalmTraitor - - BasicStationEventScheduler - -- type: gamePreset - id: RevLing - alias: - - revling - - lingrevs - name: revling-title - description: revling-description - showInVote: false - rules: - - CalmRevs - - CalmLing - - BasicStationEventScheduler - - BasicRoundstartVariation - - BasicRoundstartVariation diff --git a/Resources/Prototypes/Guidebook/antagonist.yml b/Resources/Prototypes/Guidebook/antagonist.yml index 985e590c4d..081ff7ef0a 100644 --- a/Resources/Prototypes/Guidebook/antagonist.yml +++ b/Resources/Prototypes/Guidebook/antagonist.yml @@ -4,7 +4,6 @@ text: "/ServerInfo/Guidebook/Antagonist/Antagonists.xml" children: - Traitors - - Changelings # goobstation - changelings - NuclearOperatives - Zombies - Revolutionaries diff --git a/Resources/Prototypes/Roles/Antags/nukeops.yml b/Resources/Prototypes/Roles/Antags/nukeops.yml index 1f803dd681..6e58b1c9ba 100644 --- a/Resources/Prototypes/Roles/Antags/nukeops.yml +++ b/Resources/Prototypes/Roles/Antags/nukeops.yml @@ -30,23 +30,13 @@ antagonist: true setPreference: true objective: roles-antag-nuclear-operative-commander-objective - -#Lone Operative Gear -- type: startingGear - id: SyndicateLoneOperativeGearFull - equipment: - jumpsuit: ClothingUniformJumpsuitOperative - back: ClothingBackpackDuffelSyndicateOperative - mask: ClothingMaskGasSyndicate - eyes: ClothingEyesHudSyndicate - ears: ClothingHeadsetAltSyndicate - gloves: ClothingHandsGlovesCombat - outerClothing: ClothingOuterHardsuitSyndie - shoes: ClothingShoesBootsCombatFilled - id: SyndiPDA - pocket1: DoubleEmergencyOxygenTankFilled - pocket2: BaseUplinkRadio40TC - belt: ClothingBeltMilitaryWebbing - innerClothingSkirt: ClothingUniformJumpskirtOperative - satchel: ClothingBackpackDuffelSyndicateOperative - duffelbag: ClothingBackpackDuffelSyndicateOperative + # requirements: + # - !type:OverallPlaytimeRequirement + # time: 216000 # DeltaV - 60 hours + # - !type:DepartmentTimeRequirement # DeltaV - Security dept time requirement + # department: Security + # time: 36000 # DeltaV - 10 hours + # - !type:DepartmentTimeRequirement # DeltaV - Command dept time requirement + # department: Command + # time: 36000 # DeltaV - 10 hours + # - !type:WhitelistRequirement # DeltaV - Whitelist requirement diff --git a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml index b2bcd8bcb4..434a7c1083 100644 --- a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml +++ b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml @@ -293,18 +293,8 @@ #Head Rev Gear - type: startingGear id: HeadRevGear - storage: - back: - - Flash - - ClothingEyesGlassesSunglasses - -#Thief Gear -- type: startingGear - id: ThiefGear - storage: - back: - - ToolboxThief - - ClothingHandsChameleonThief + equipment: + pocket2: Flash #Gladiator with spear - type: startingGear diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index e71dee0433..92d879b066 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -31,7 +31,6 @@ - Revolutionary - Zombie - RampingStationEventScheduler - - Changeling - type: gamePreset id: Extended @@ -165,3 +164,15 @@ - Zombie - BasicStationEventScheduler - BasicRoundstartVariation + +- type: gamePreset + id: Pirates + alias: + - pirates + name: pirates-title + description: pirates-description + showInVote: false + rules: + - Pirates + - BasicStationEventScheduler + - BasicRoundstartVariation diff --git a/Resources/Prototypes/secret_weights.yml b/Resources/Prototypes/secret_weights.yml index 8a9f9a766a..4ad31cd194 100644 --- a/Resources/Prototypes/secret_weights.yml +++ b/Resources/Prototypes/secret_weights.yml @@ -1,16 +1,9 @@ - type: weightedRandom id: Secret weights: - Traitor: 0.40 - Changeling: 0.10 - Traitorling: 0.05 - Nukeops: 0.20 - NukeTraitor: 0.01 - NukeLing: 0.01 - Revolutionary: 0.10 - RevTraitor: 0.02 - RevLing: 0.01 - Zombie: 0.05 - Survival: 0.05 - # Wizard: 0.05 - # Cult: 0.05 + Survival: 0.44 + Nukeops: 0.14 + Zombie: 0.03 + Traitor: 0.39 + #Pirates: 0.15 #ahoy me bucko + diff --git a/Resources/ServerInfo/Goobstation/Changeling/Guidebook/Antagonist/Changelings.xml b/Resources/ServerInfo/Goobstation/Changeling/Guidebook/Antagonist/Changelings.xml deleted file mode 100644 index 789982ad9e..0000000000 --- a/Resources/ServerInfo/Goobstation/Changeling/Guidebook/Antagonist/Changelings.xml +++ /dev/null @@ -1,61 +0,0 @@ - - # Changelings - - - - A Changeling is an alien creature that is intelligent and able to morph into humans. Changelings are aliens in a form of a headslug, but before a shift starts, they either transform as someone while they're off-station or rather they somehow killed someone and made into a Changeling through unknown ways. - The main weapons of the Changeling are its ability to internally synthesize dangerous chemicals, morph into other creatures that it has absorbed, and blend in with humans. - - The changeling can be anyone it's absorbed, it can switch identities instantaneously, only absorbing takes time and peace. Unlike the Traitor, the Changeling's only objective is to survive until the shuttle arrives and escape on it while transformed into somebody else. - - Remember that Changelings are not obliged to work eachother as a team, and some may go solo/rogue depending on how they like it. - - ## I've turned into myself, what do? - - ### Chemicals - Chemicals are your source of abilities. Without them you won't be able to use your powers. - They regenerate slowly over time, and absorption will increase their max capacity. - - ### Biomass - Your biomass is your health. In the beginning you have 30 biomass to begin. You spend 1 biomass each minute, and absorption fully recovers it. - Once your biomass levels get low enough, the effects of your decay will be seen by crew, such as: - - Vomiting blood - - Violently shaking - - Death. - You cannot die normally, as in being gibbed by blunt trauma, but your Biomass is slowly draining away, and if you don't get to absorb someone before it runs out, your game will be over. - - ### DNA Absorption - Your main weapon is deception. Transform into other humanoid creatures to confuse the crew. - To do this, it must take ANY human, living or dead (even thrown away bodies from cloning), and absorb them using either the Absorb abliity, or the DNA Extraction Sting. - You can only have a maximum of 5 DNA strands at a time, and must transform to obtain more. - - Acquiring DNA via absorption requires the victim's incapacitation, critical condition or death. Simply, handcuff, put him into crit or kill him if you need to absorb him. - - - Absorbing someone takes a lot of time, so prepare a safe spot or do it somewhere with the least amount of ears possible. - - Absorbing a victim will recover all of your biomass, increase your maximum chemical capacity and give you bonus evolution points to buy new abilities. - - Absorbing another changeling will, on top of that, increase your maximum biomass capacity, allowing you to stay alive for more time and give you even more chemicals and evolution points. - - [color=red]Absorbed victims cannot be cloned.[/color] On the other hand, they can still be turned into cyborgs. - - ### You exclaim, "I am the only one here!" - Changelings are limited, however, to how much DNA they can absorb at once! If a changeling has 5 DNAs stored and attempts to gain another, they must purge the older DNA by transforming. Eventually, any changeling will have to be a twin of someone else on the station, living or dead. - The changeling can shift its appearance, making them look and sound exactly like a victim of which they have absorbed. This can be massive compromise in security, especially if command staff are absorbed and the changeling is able to imitate them. - Changelings can also, via their lesser form ability, transform into monkeys and do monkey things. - - - ### Regeneration - Also known as Regenerative Stasis, changelings have the ability to 'kill' themselves, and appear dead. After an uncertain amount of time, the changeling can revive at will, fully healed of all injuries and illness. - Entering stasis drains all of the changeling's chemicals, and leaving costs 60. Chemicals will still regenerate while a changeling is dead, meaning it can always enter stasis unless it's biomass levels are critical. - - Changelings can also choose to revive by defibrillator, making it even harder to tell if they are one or not. - - This makes them nigh-unkillable, as they can fully regenerate themselves even from death if they have enough chemicals. Spaced changelings may also be able to make it back on station given enough time. The best way to permanently deal with a changeling is to starve it to death, putting them on mining colonies or solitary confinement. - - ### Going Solo or Teaming Up - Like traitors, changelings operate individually and are in no way obligated to assist each other. It is not required for changelings to even reveal their identity to each other, as it's not uncommon for changelings to backstab each other to remove competition. - Even so, [colopr=red]a coordinated group of changelings is truly a terror to behold[/color]. - - ## Identifying a changeling - Changelings have a different blood type. Even if a changeling is pretending to be a diona, vox or a moth person, they have one blood type. - Also, when put in a centrifuge, the changeling's blood reacts violently. - You cannot identify changelings in any other possible way, unless they're dead obvious and start going loud. - diff --git a/Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/icon.png b/Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/icon.png deleted file mode 100644 index b0a671bf4a99e987809a9645cdef208561101ead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4746 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 diff --git a/Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/meta.json deleted file mode 100644 index 23850a83e4..0000000000 --- a/Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/meta.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "https://github.com/tgstation/tgstation/blob/8024397cc81c5f47f74cf4279e35728487d0a1a7/icons/mob/human_parts_greyscale.dmi , modified by DrSmugleaf and whateverusername0", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon", - "delays": [ [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 ] ] - } - ] -} diff --git a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/icon.png b/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/icon.png deleted file mode 100644 index 94bfbcc36539a639b57bd9bcc6a81a7bc4596b27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 876 zcmV-y1C#uTP)Px&CP_p=R9HvtmtANSR~Uw$nVmDUyWQPx*6xonf`ZUUgcQ7pBB?E6Xwg!or8ZD+ zA_#2>lw4b|1bWen-Ux+?Qjm%*rBE+~5-3P+EP>D_s9>Q~)6KFNt<7X-f6na8DKj-o zQ=`Ps#s)b^JbE+%XqoWcu;0SaAJ0rUxtVI>H75$ z{j-}kh0(Vf(WiU&s_$>ySoLzWnz4UI>{%Ay-MYome4ZuCLeq81i3De_UghKde(JW( zJG*vKx_OiCEnBdJ;H`7#TEVs+J{TNSc05jfYKp25C{5!TDY;`=552WDOp{OM=J+O) zL5oC~jm1bb8q~8{lx5MJpXczwgV@)u@q8x3d^*kh=g+So{Q&lnkr5@Oq%tvq4&n3- zgJ`pfGz?tB2ur6S1ct7Ys@L&!ou==jc^(VNB+*(8U&@wvs;`fP<8buCg$MLj&HLEc zn5tANlrCPx^nJW-+wdF*-}4Ys5_KFrrOZM+gp!)Z zyJyZk^n5+?;C+7Vn3@xU$h~{~I5`QLhA>TBDM6{f=f6gPJhik$Jsw9!B99bb)7S8e z~oWx=J;UHnbD`991m_)lwzsrA8cS zb;`yxLQ$P@)qS*0x~NF1?u`wR&HlGDyMKJ%n>TOX=Y9VA-0}2qR*=<{1pp|xx;T1^ z87Ce{T2hQ1nZ#Hz!IHh59YFJt)-nLdbFPkf-_+0*%F8%aHr0;_$wOXz=OR~v!!nSif2(hShtb|#n1o04(vJhE zh+ze)%@~ZGy^2N*n~Ft2QwFCpMM0C9v6O@fv=qc&COms)4~^Do!)~2uNmM-`N0=LL&V%oa`fon{p;7 zfLFx$jAj|YZV(@Cdl*ei&--5P#ymsNf)s^x)@mi0Z&q^DfHmMgYfy$=SiWHE5223=ib4~$I-BKd#m z)Q=Fch%CqcreSJ=Wc^-?9JxlAZ+T{2x)F$gT^M*Z zFb+G9j^R#uaQ#y7MqsFCbW}M$KHl1H)tf`E#w)+xqUh&n8}fH463?Em$}{yZh1|4& zalGG{g}b9hYG;gWyhBH z{fhdZST6<&!8}H}18qWDBAE8dMfjS%T0C`}bpI<*EstI=*vxK5JZU3d_wT-J@)tKB z4hc!CEX7gRsNG$$0%>0+l)~-t2h+SjOxQ*I6*~=KGm>GK@SlEvU+TBWQwY&yh>IS6T>ziUBcHEF9nye8$^sc!6wg^?Y4@3qu-M zu@?e#ufyEZYql$^%*Hf9%?2d1o(3;-qBRWjWp+6EfPLqwZbVv@1)(7;U*!>}O_JGp zV?BDmi7)wjf~Wv`i?H6%b}`aT+MiD@aX2Wv0O;3Bx&xfrR6$L?{4we;MS71}nh?)P zG7!viEhU*tJGbdB_;Z~~;1ipvp^#XZ`z&1-Xp&IoQSbwI$%ga~u$K`>eYNaiL&kZ{ U238VE;ui~Coje?y9m2A}-*9%A?EnA( diff --git a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/inhand-right.png b/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/inhand-right.png deleted file mode 100644 index 10e69a7ef1b4e8863b14252a91d5a6bad23d9b5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmV;U1zh@xP)Px)TuDShRCr$PoUv;hM-;~Ansf$Z@Kr`|hzmwCq>%%*Fe0t4P+@~P<0@_Z2N)GD zQi!Ct+f>18gHxzDwLt{Au+_K`!iP8zsaB1_otrV=!ZVAIcIKVld$Vhe8VTL*d~bg5 zo7t7-&Gg(Q|LR@x{<{nyJK&NN$V1?g8z4`Dc?jeo5J}*EzwiGk%Vdtx(r0yOG8%dC z8cimw`yv?!FVoNub_C>qIXQ7pyboR*?7Urn zaQ`dnH#%TpVZjXs12=p9x_TT+O>m)h$N?7mjSlGd`)+M*t%`SRYs(A(Ilw}{u~TG2 zxWF1feHdu!2LT`puwqdZrVgmPKvO^1sqftdK>(DArKKgeyu6%jC}`3t(mckD?7l7bX#?@1KOGytGKcQtVp+2 zCp)06nX!s1J3yMW);fM{oLqe|2hhs#)~8QyZ*Nbm-Pn%Lt1lX8eJP!riP(-dT~}X9 zkJT3eK=cvZ`eaho5=e9?wvD>}jme~{C5XK@`od6yw0c~3!FX}efBF2`WJRg2PX!e* z`eFbG5C9C;*B$19&8t_<>xsDf;sEMJ4SydVIx7cI0=W9(01yaOl;GzSwFMk~aR8Wo z-<0LIx`H2vLpPmH#dXe`lc4{sEWgziJP7EE2ZjKCUR-pyu3dAeD=O~Ztzxx$H{j~4 z0QmLx?doy(XgqfN`}^h&fIc3Fe?;hu13sRd zySwYkvNUx7bUFHJ0FZra`_se-0I~Wi0LJ67hxJorzZwDzeQ^LB`k~kBc|Pariv!r$ z*zgAj2j=6RU~%=u0bm{9^2O)0OSt;t0Oql6%6l?^l=R!6ogL7IEToJfJ0K^*AW5^CjNxu!+*#T|HLdqDj132`tnaS$k+h@h4q0e>z_wQ{E;FxQr_1OThlRVW{ zdH}*sa>=dq(Evam0pPzq)*GpztEC?e071~wGnTxt1a7^hUPGS+fFS&P{ko&W6=KG% zq2ZD$8`AxoMuW(R1}?Lynx0bR(FW{lYZnsmF+c6LA)vZNVfc7P_`F0`E; z(1k2%#>jF2RcL4i)YP_yJ_~?kY05khHA-_>08-Qg)In+JvjAXI^6INsNzMfU2o3#c z0JJF?yTVh{3v1~|0iY8hQXPn(sUHP^yCr>|39jiLP5mH%y7f{Vh@h?C2mq4~WhTXO x7#8|L2T0qwZS7?MZOe#tJlO%(#M`F#6@wLet~F3r#Q*>R07*qoM6N<$f&ic5sTKeL diff --git a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/meta.json deleted file mode 100644 index 9ef5a5f7d0..0000000000 --- a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/meta.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "taken from tg at https://github.com/tgstation/tgstation/blob/master/icons/obj/changeling_items.dmi and edited by https://github.com/RealFakeSoof", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - }, - { - "name": "inhand-left", - "directions" : 4, - "delays": [ - [ 0.5, 0.5 ], - [ 0.5, 0.5 ], - [ 0.5, 0.5 ], - [ 0.5, 0.5 ] - ] - }, - { - "name": "inhand-right", - "directions" : 4, - "delays": [ - [ 0.5, 0.5 ], - [ 0.5, 0.5 ], - [ 0.5, 0.5 ], - [ 0.5, 0.5 ] - ] - } - ] -} diff --git a/Resources/Textures/Goobstation/Changeling/bone_shard.rsi/icon.png b/Resources/Textures/Goobstation/Changeling/bone_shard.rsi/icon.png deleted file mode 100644 index 7661444b00f3d3096f6b3f8a2f0f453e7db67b1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4334 zcmeHLeNYo;8s8k{ID>F%MLFSQP3;vao82TElI#!xlK>eBA0cDG&KzuZ--NA6HtsGY zaHsaLHzz|Y)H3~`_Byq-qE5k4#+Eux@enQ3`Z0bD)=Jf})y^S$Ugu;^hTgjgpL3nL zVdnZDnc3{V&-;6R@ALeg=Y1!erqbeq1my}Pf*=X@LaP(rQTciz27dp^+3zb zvt#$mx9|N;NNT%Y70BPZ_?4zpOAn^(Pbte<)sy$JxBtL}vn3Tr!+#!BZnxga?EA9& z`LhY1eZ1^zi-~VbZ)_TDsL4#cw77e~7`NeMP3#+7+py)Y<6OaL%pJNiz2}^*4M7wy zaTZId-C~){2NV<}jhjxKXz4$&z4=IIVrL^ds<<_LUy#4yw`ge_AX6#z`$Kq^oKDH^XM}2JURL}6CUR__lT33?#yJcUPGanHLlQTBI zY~0ha;@kN8H#)zja{w7%c@I{)H!OZDNRtAAg4f92hK_rB4+GrkpR`A|5#aiH%N*m-pv?63+) z5ykL+HO=yFpbqr*Vxa4Z#hWPqOV0HX%;=vg;LY6&Ao8{8T!i_jZr0}V76O-2K4(hw0UmZ9=_!B4|-a(>zi@L<3jnUDpi@=EPyl~x^v zMl7X1TJk^#xCgiZ%ZJ2iu#5A9a*3Ap)EKk`q0t-ldc8(R>WtH%4L}fKFUqVMLaies zGjd@lm=2VdmOB*^MC>pd$|3+-;sqDa`^+ji1XT8%ns&eq#nKXOr6mAK2`xzx28z(R z2ot3tD7_XV^b|1z&$FDT{(qt6?Sp1Vi(bfyaQ*s-DY~c1!RF{FI`nao&4i+nZ9&mY zGzF2a18gKt$Q7MpYUqF$!1H6GVJGd}Pc(ypb-Rs($AGzMpv6d7P0XYv^cbx(05_54 zF?c-DW|(3Zd5;vL1(4^3Izp{rgGRJM(441^tNqhT3Rl)x8LvYe|YQGvxn?Qp_f8(7MCNr-wncl0uY6JeSsC( z5&PRhI9L!YtP~L>eu;c3E?nO_290r&-C>Kn8yAzPGz_()YoV#qZq0M0pJS?%hi@RC z_Wqt+D?W;KDc;Xmsu;TFcq^qfHqe#1fX~16&*Aj=rDfXOEayNOc|7H+r`h?aGJTm? wa&fTvg);(hc^`}rx#IVJsq4YQf1i&-;!Y-|T`F6Y1Q$WPx%8A(JzR9HvNmp@AbK@i0!g%A`KEVU3rs$e0hf?61m6p~o_F~m}^7OX`47?v6= zf^ZfwqzV?2Dnuk`DX1udMdU3EO!oHA-6bKp=E7ZW-*0B#% zhiO9{I2M3O96kV0Qa^YBOeU0~nC}v-dYGm1WhZiFya0L|Vr3SU;(N6hdT)JEId@Nu zbcJq9tC1kQ0Gt6fAq5sCm3joB_^~k0KU;>74+a8q7e~r*pyt)~4lU*96+j$u?z${n!nm2L&VBD^z(C~Cg-d#&n^QLBryC>xi7_-x96 tb6d^}PV~dXH%3Q&e>%SnM;)bWy?@yX!Z7qC^Nauh002ovPDHLkV1ic(21Eb= diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/anatomic_panacea.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/anatomic_panacea.png deleted file mode 100644 index 348e6bc318d3befa08a723c7211bfbcc8c221f3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmV+b1ONPqP)Px%x=BPqR9HvNm$7RVK@i3_V48GlS{oq>Vk-nu5DADNf`5RGO)4uJ4O%JDLaS6Z zHrgv9h!9jzLcmr86R=5}#6n2(1m*W0-}&9#-fj>cFVoz6J9|6d%zQIDHMPE)YCF-My8jj~z|(i%Sgv8ODGl0+I3WZ|)7Z zpmz`*0C+1Lw;ysHvL}R9(s>)Kq321fk?9-r8o-^GHmUXpsYIK8k>HAC05$^IU*q$*Or(`+!2vK^}hhK4*M&=~5bO?G9P* zO-ttyk$sUcM?OWM6!w|3a}5BGf_G8YtBuHeK};OO%1?P^J^?#*+!*ZoY*rPZA zm7z+pngS=G6Y=u%FWFLKk31KU2*69RT813rw;SaMdvi@j1_c0qI1ikN+FOl)0O%^_ ze>6_mF%|*j^3R&5p-Ms@Msmj0+jAX}(2qd;yP#Ccy0`#Stpb6_T`W$x+@0WhH6p_9 zrC0@^H2AeD&*fc*`nC`!C$)8LsRHmZDjHTk)geczXwL;Ixj0p+s;YN^#;8{-i|N;k z$DP)ygJKkXWa}Hzs%1#txyuO~0CpeVX*~3)2fa(*Px%eMv+?R9HvNS3OHZK@hwYHWn!??35&^1hEnk3>XZ8RyJxQg5W<8EVU7mQt&TC zuu~f=ZA66F*(efFlAyIsK${eiIhe57-1|5s#BE;Q-Q{L>_T%m)%z!O1V0`mG2}}cc>5CmnuOKw-}DL)k0JIwiqKsJ`3 z7$eqkz~Q}j2-vY?iOK9HxE#~cyS$Gb0=7*&rKe#T&pE`M$#+?8nyax^R|2})aZe+{ z1{da`)4#d7CIbZ|Fn_c5|4iU4yK1(p*Bub{atSQ!9r1>ZZ_e1}^J@awgergp44Xj} zkZ_gSNwH#UUV>vCLaPx%heuN-2L(6x}P>Ji3|btoEsDv zKxhigKIe2^iO?bZDv3D)%9QF8L8jOLL$%~je8WwyPDgc>mykk=9 zm<3j{m_s;Xc@PZ&2|()phquAj{n?F=zYT#Mi|r{*fJtY;$pR$unu6;ds!$jLC7_A) z`Rjg0i5Lk5|9)~kcVu}g3X>4=|Irz^X0z#y)M~Y0Y&06VyDuM|RRZ8|zWsFVb~~({ zPN)7mO;h)2xD)C_A%UmvRt8kPRil^vU7r&mtH3dJu&?+0=%-2(fVXTGK#6dUW3$=J zO(v7f-pT|Z8(O2$2!`tQdd4{dYKSnV;Mf2{2xlb)NC2A^0EE~Fm<7jrZ6s5ffHed{ zN%FsJz?cPQvT==)SXu!XvtH5|2|y8eiFHa+_bo*L08By}fL8#c0?ZF3VDkoZNYctl zXwSYq4KUbhwfyjSJPsjQbaF});Dzzf>4rk2@uV^+EUb>GdRn+*>t7F_t6xUesDcAT z382q{q{kDAXca6hL{tFfST7`&-q`(M>pnnuFYpGfbQ>xOy@X&`4N~e~+lS$rwznb3 zDR^lGXdFr?iFYTbu~P5HK*ZjN_FY&=01NDESP}JYr{4=%z1ZW+I+p+@^;vF>#-8i3 zr#JV-$mTxFxpA$3O!A~$#oT88H(Z6Mi){V_ARjP4W%HKw00000NkvXXu0mjfKJ-LD diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/biodegrade.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/biodegrade.png deleted file mode 100644 index 902205d2035557d55706063d70938482c0a55bd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 563 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 Z`~mmYb3YLXD*FHc002ovPDHLkV1foIC941c diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/evolution_menu.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/evolution_menu.png deleted file mode 100644 index a68fa0544d05aa6abe6d4d99daf1f021de4877b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 673 zcmV;S0$%-zP)Px%TS-JgR9HvNSFuV1K@{98Y!pTO0WqWsR;fg6L`Wg|5({g=S`h3~*!Tz5!9tKU zB35Y>D~b367K%0&kvW-j?%m$*Dwt%OOODOXy!YnKzGQC7E1NRDn@s|(`ML3bWai=H zvl4LS1;Iz#P1hJa7Ci7>*V^Wi%Z`R__wu>){-U$$#)s|v-P6r${=_*v&ypw+u;Ba= zo&dywcw!O!W%yPiu+m<1N&@@z9FS2G6#^Qmf`-6v57rA38^`xfN#J*yNojxMk)AA{l?+OS%o0Wk(ChKELZh$idnJIbphK#D zR71&D%v4(vKJ|x6V7n-4L{zkzhl1MHBRI1YGP0-$?G?;&Xua9@b+bTdPKdT4%|!2g zs2vNyjGncwvumFbv82fwV6PJ#T>vsvdd#fsJdt!8;!w3s>vr}khDEDs* z6On?m#`FN6$Os-AMi5w126P_ayl3x#6t|;cDKPaCH6lB3`ewY8nSr9S6Lu{DFvMhR zry`wlu}9}Zd*kT@t>bvoSkA+y`o}~gn#AYX{BJmk$cgaPx%T}ebiR9HvNm%lCqQ543{MkrQRrO*<^Mj;WEL?aR!4MMUM>j}Jo+6yT33M2%L zhDIY%i9{h=2(3b8V+$hWyUE9yGjs3WiQVkpW@hd^bH3j>bN`$vNQYaY!}`*l0At18 z?{;M7q4AjtIMqS%>R^xTy?zO@7qBz$e~6J(2);zkYJh zbPiG6Z|u$n?Eq^bQUHjU4UQo`vwE3ue04M@gy29kyM>)cmrX$EEMx*OjdZ#!Qwe|* zxe*N!V}}eyW#D{s)QM@QOXYzCz-jGG2!R1)2#_H=WQ-8O3;@82mnM>nqJCEpLI8l` zAv)Amt&+p~Z?=$yrT`$-DP>gJIUEzh z!5MLmyQ|6;eFrmO0Vu%u3E~4lKO4l+^qfI(xdAK$Bhxc8>-%%OB>;(Kkn7=`z%( zpM}6qrz2!GbRQtmU~9#r0bpp)1I`B>A+mx7QQQLv<390d03NSd5D@S+o&{NvvsQ@u z1X{oc0Wk~Xi=1@>{6-ze1PEn*03#M9`bFQIAgoQhF3&<`S!O}PI**gl z)_Qt%z$R8asv=@UwkY*Mz+P8JzR2kh_QAtQPe2W!=LPRstm-1wes6wGmSaHx4BCGJ zLg0+hRj^%(jNF#%TG)!?IrGn`qdz9zU8YR~-T80WreHJO{s3OrX+JT8lnVd=002ov JPDHLkV1nMCBp(0( diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/hivemind_access.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/hivemind_access.png deleted file mode 100644 index a0405d5f6cb288b9a26003bec99e890a0525a976..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 634 zcmV-=0)_pFP)Px%G)Y83R9HvNS1n6KQ53ujT5N*BAXo%JjRu27kPW)U;(xG+(PFX~#o~XkSXRR# zST-1pDhL+AU=VDU6_j}}%k0^6KUP=PTi(}w=ggTqXWo6aFyc`gasKc>0W3_6fBgkB z4n+M%%J;wxGWAI%ZIV^}C0Hw)G*ijI6zJllj zz-nWaBiPvbl#Ai>>aFK~3`7zDA`k>=SBjCmy*wSR&+1`qyHzFJXg!C^`fOOeeGLS` zcRUbT23S4T+)K_fo4a$>Y2)OnTX_kHff*p}sGMWv=q}vsFIAVy2Cgj#zC$q<15nkI zdIZzt46Yy)0J|dA2~iPh+iCI=05KMd7|`A)0~P>9r;>*rQd&??7Y)WJ zLy`e1o5G2?AuSk3ew1wc(FK)FbW9e2m)vSWMMq6o>a}u$$O3>9+76%wa6~(%oDd)| zbZ?-a=AtFApI|0tMWt4(Kp3-_0jqp9ArFAp8CDYmiO}Px%Xh}ptR9HvNmoZBNK@f#EN}32(mNtq-u+dhq34#_{DOzY@eu1F1zrn)7%ErP* z&`JwMNF&&4BPk@6rIlcsppm!mj+e=1?=BLOYc999cki2-w{x3<8}d;Yvc4Nlfca9f zJ&4S_HCtT)cMU*%%PqNU_w0BE%wlOQ$n?_u_478%lkTSe<>%OE^+5~K0ifcSbHj0QJ2+nDZ~$IDJn5IL2{6K$vG{fnKn_hyu`X6rWh0^}^pz4G#fv zj_Eiv5h?>piUa-tR9^rTQ*ow*a5e+DPe0M5C`~Q^idbQY;=s5Cpcu|-W_l+IP`g@n zht)^N60r7~dI~}S^A(_aw&KAmI}iSh{-G-g5V-&lVe_cw)<0SuhP<0Dy3$D{5u*pT zcV~Q7LI9aEfJl$e8=e{uSkom-L5!(O5dd5rq3Cfhmj^Pr!o9=6A)l_7Hm=m9H^`sopBwOO(dErIp{_ITD4;vv@) zDy*BQd31+lcSD#L+-nc@k4dcQ4C3Bs{x=*%q`ltx2c~*v UKl|sTlK=n!07*qoM6N<$f*f%p^8f$< diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/meta.json deleted file mode 100644 index e85f2a54fa..0000000000 --- a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/meta.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken and edited from Paradise Station wiki https://paradisestation.org/wiki/index.php/Changeling , sting_transform and sting_armblade retextures made by whateverusername0", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "absorb_dna" - }, - { - "name": "anatomic_panacea" - }, - { - "name": "armblade" - }, - { - "name": "augmented_eyesight" - }, - { - "name": "biodegrade" - }, - { - "name": "bone_shard" - }, - { - "name": "chameleon_skin", - "delays": [ [ 0.1, 0.1, 0.1, 0.1 ] ] - }, - { - "name": "chitinous_armor" - }, - { - "name": "epinephrine_overdose" - }, - { - "name": "evolution_menu" - }, - { - "name": "fleshmend" - }, - { - "name": "hivemind_access" - }, - { - "name": "lesser_form" - }, - { - "name": "organic_shield" - }, - { - "name": "shriek_dissonant" - }, - { - "name": "shriek_resonant" - }, - { - "name": "space_adaptation" - }, - { - "name": "stasis_enter" - }, - { - "name": "stasis_exit", - "delays": [ [ 0.5, 0.5 ] ] - }, - { - "name": "sting_armblade" - }, - { - "name": "sting_blind" - }, - { - "name": "sting_cryo" - }, - { - "name": "sting_extractdna" - }, - { - "name": "sting_lethargic" - }, - { - "name": "sting_mute" - }, - { - "name": "sting_transform" - }, - { - "name": "strained_muscles" - }, - { - "name": "tentacle" - }, - { - "name": "transform" - }, - { - "name": "transform_cycle" - } - ] -} diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/organic_shield.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/organic_shield.png deleted file mode 100644 index 1fc5524e2d65bba2f5f8b95f387117018ded0ea8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 594 zcmV-Y0Px%3`s;mR9HvNSD{V=K@{8*7(_pT1cGP?3P&9Z4>*1T4-z~-fdf1WR~(9v3IYkA zP=P^|%q7!I=XUq@XfExVz3y#y=Dm6E?Yk)_Z*ybThiBe@ej5fi78LyR zXgBOUe+0}J+oM4dudnfRzK{RMjNv2}2&BLXT;04D5tv;b)cZ&zMJepu`%zfVgV)Kq)wb4l@ETM`9@g9@#EK zDkIbz#ROPDH2)cJF)P&zN=&n*a}ZL3yWw$NR5azflIR?S8h|9=R|XF{=%v`D zau4uIZY2=w1>gY;zYF{lQZIP$E#(0ca1yB)h^3zHZIyucfyY723p_Yic+{)K*ReOF zY|FejxeVTbICyIe(4tgF+}44!L;wH) diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_dissonant.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_dissonant.png deleted file mode 100644 index 88e691862460da4889d88cab23aadd6f4f6cdad8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 626 zcmV-&0*(ENP)Px%EJ;K`R9HvNSD{V=K@i;vLI8!Uz(7#NB5N=hFeHTV31CPqUw|bT@EAf!FvVaH zRFI&lB48>IBvn921>`k(mv?i!-R(isj=AgJ?Y%eiW@n}qH)gAh8Q)DNf!gfU&nP0( z{_GVTE-VmyzP{zUt!sy?ns06I>LT~fQFYoi50(o)Y@R$jOCm>L5V!?a(ECm|18c2T zGw`|kQh~(U&TWoFkN|gvfEDzqv7Z6qeR%H!$4gNHHstC8ye6=MDp`ge1ZEa%+4is3 zcK<$!KuBkR`+zA2wI}%dc{3*g)}sWl!$yIgTW|t1D%2l9AFUUMq=VgEu4hD|1Y+Qf zj!zCe1DJvnnIw`3SP*(1S_cRSK>`2)1a`A9oXy_gvXC1&j0ddd5G&BL8{}ciW&N=`LQ283EPz+t5bq=IUW zV2_=Qv=ZV}ph`x2K}sR=5#$=Kb}qnL@<39y7uZ>W$;ZUxh!%~&U%H{~0nneu<2aB2 zUe?Lj*8Gq>8h#~lw!NE;_B^Ho$8v5S>mL&v#!>&C%>Ra?_FBLH3*=|sKWYNl+W-In M07*qoM6N<$f@sYKt^fc4 diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_resonant.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_resonant.png deleted file mode 100644 index 1f75903bb4057aa1ba7e8008d1c413d63b54c418..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 605 zcmV-j0;2tiP)Px%7fD1xR9HvNm#t3&K@i2Kq6R@iKw@bcBnlD?7z7wV;_&BE|XRj@3c(fYUm)QhZookQkaOU-Q7z7+l z#KaF}LoVe>@TrHLJLt~KQU6XBHur-Vz8|l}LR0{(xE8fS9*h-V>TCxP@4b@>L=?b_ z*b~(X8o(3B+9CjLGevXud(kg;w#Iy4gNOg`MF3bB%Q<&Ut`>btKH_GsnN|S=mQ=A1unWf0fZ;+Q9``v6j%TX za8nI2srbYWSaA!H>ig^ke1<*2-UmSn7@-$AE}0mFwjD>#c0ojskp;jaK$M7SSUiVk zc-|?9dAKnVfR4r%p684eXCJ^ku4P_KMKxe6I92&{ivT)3{LXg+x{ytQw%=AfHf1l+ zPGFrLc8zwSs1suIlvoTx&wCC`)d{_$=s81RyPidfQ-Lm-bpRW&8O8cQF84a`JdVXL zBq?!QAKp;~oY$au60!~eAa>M{<6KI3h1+#3#3TUdpV#(AvB-3E-rWFk4hEu&5#N)q ri&B+e;d{vwch^^v@rEBPx%P)S5VR9HvNm(44LQ543n#cb?+EG#G~l1r`?Rp}fr*y1oveRW0-Xukh`|6++| zQZ*HYl>kkM6*>VG46^{`L-5j$dXv3BT0 zdjU9za1}wX5Za7MARI!AZ|gZ904V5b2msU#k6=PT1?+QaRtf}wIQhK_s3R8wzd;o) zl~%z}HUJ<2tv%0qZvhOo6jTDu#FrA-5I{k>&+@7C1p(05b*>QXZUXWQZQSMy28haD zOM?Kqpi11Hmf+h>h+wY#aeJ-`?hFWO!X()l*i}IH*(a(>&>(^nycsKiI!TfmXWhVR wPk_YNa%NT3KPI80_NVOr=YPYW$}YsuCoo_7Fl?&RApigX07*qoM6N<$f;V_6vj6}9 diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/stasis_enter.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/stasis_enter.png deleted file mode 100644 index 95b6448748748473b0cb4b23162841f8f9638c52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 408 zcmV;J0cZY+P)Px$Qb|NXR9Hu2WEfz;IKcKZ3{D4Vak2iVMaW$H_8Y^)7y`7i9OfZ(N1!->)|O*) z#Ha&?UI=u}n~67rlqIDT%s|Kqk)#j+SuQOnhu4Cqj~+1)asWEM8-Z0S2 z0Wja=vK;0jVjMwM2n@IbmhRck5bfg00HX0l0Za{u#s$arg3Sau0t8?V z0R=kWI0G5*--%V4J_!dUpNOA(lju_hzfkA6-5q!B`}yHU>1RVPAKug)KcUC zST;ltYFO0Z%YIaG04$}Wmj$52NsQ%GNdquPAg6v}EvK3T@Fg-raulTjLWUC~KX@Gg zay_kE#(10Mu;8PWBZ%sl5EF;gR5LjHhSc;SA+rEd4dx+}d(~_J0000Px$=1D|BRA@u(nY~H_K@i6`g^h)U*acDwT1n6s2sR3Kc7lbCZ{S1t1~wK#Iy*rd z@dcD%rC=Gb6AKF)3uO*QHtXK*%eSVREH);QmAT8qvGzOt*`!fKvjmIFgVZQ*dYrTyKaP6O-p9R~~$yh4FM?tmX=q%5I)}DOd|j!1yBJX{5spX4h2F|251Q= z04xHbCOp2qF)#avzoS<#FAQb_MBV-DV0)*wS0rVCmH=u7TutuH!)SNLsm;6V>fZN! zZ@483KuZ7>0E}~k-~#}e5sE&nlav8k0yqF52-xIc9&U-y`SBjgA{6~B02%@~0AMU? zfNP92WCk1*=d%E42;c%h4RC&boK(}-Hip$X7J0OlTmrZNL_tbG3IH1d&Zjyrk^x{0 zA=m=o8AV9lD{&j|BJ6hos5f7&VQt1bup0qx281A|1b__ycLVn;DRgIw^SJ<|1CRs& zA=ujw)%jci(g8>YV5ctkTA~L)CP)O}-vZ!5x5sC1gE__}NMaD3{0fsaH0000Px%T1iAfR9HvNSFtWcK@^>hC@2(IYKh3(t+X1gMnx1T{K5W#Li~Y0P#_{y8m&gF z+H6FkmZecph>$bM*_?NG-pr7d-D&o{c{6j*z4zR6XKQZAqc&vwZZrWjC&$14BQovx z&pwCyCSZJTcFY~Wd^!Ab;GJXBt@y`g{lm4p6YkR8x$D-VTR(bq{D$wC$H2FX(x!viG~k&)Rb*_^7`?cBrxPy<+c8UofkjABq5R5t(ugPLGV zHwnNRrkGek!MlRB&y0rUQZ@iqKuAc`ScxR&#gY@8NCL2>WOHxBPn%1t+y3$4_N)g( zdBJm&B&=dTHmatFJWqk(o@f=MBEa4d0&r5Sxr4dsQY6^Wl$=ojBq7<06wrGZ=YiTC z6%0ss-e~w#RCU!P03Jwfzv{gC)@qHzV+;^W_Yi;r)4fR{0@1XLz+Qr|-m2PBZWTc2 zPLb6Osc<$5mWZr#00S6sfR&&r;04ECN}~4xDjJN0^dP1I{Pw2^aI>bt0#GTgQblW= z5y?)0b~xJy2-4mi2~0_6l`~ldR>5+N$AH+9O-2A4sN>p%H0PmCsB;=2%auw1Sg1E+ zfWV;shr+Jxu426ch+fONaHxMwa!PBEYew_G;UGOso%I8X$!I^#3_tJy0000 diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_blind.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_blind.png deleted file mode 100644 index 4d3e48c7bca814c34f65e33619e1314ea7708396..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 606 zcmV-k0-^nhP)Px%7)eAyR9HvNmpw`ZK@f#|0uuv4@B)e&7%rHIiRK1g!Q2xVdjfN>U|_C^m5`#}I~92)nUajhV)OhW+r^zsrf0^lX8KHRDyPyl_VYvLjHx6`LQbu!t{ zSH1TD=zX&aN&vPq4h6vfIR+~LkG(gcQk<;<_N^)epk+n*v_qDR>;LqrIw%KtxHcU< zEK-I%!@gOC%22(t?*>?l0(i!oNoe1M^&W;XYzOq35$2$vjeivYi{3XuH1?&gSgHW* scfX7l`e9Pb?lIPu^V@I?PfKV10h$zPKZuyNAOHXW07*qoM6N<$g7=39IsgCw diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_cryo.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_cryo.png deleted file mode 100644 index b935074af093df7f0c17fe5076954f1184b175a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 654 zcmV;90&)F`P)Px%MoC0LR9HvNS1n6KK@^=uEmjw_3ThCe!C(=L)-4vnC}{R4m=%nI#j@2XSPTZE z7*wz-tkt5ToP{IjoxShPY*^M?-q*~%=iWK@%)C|_v$e(?KmAVt%hOXoqo~Zo$LB!8 z11}JKr#+F*d*2CH8lGv3yC?nInWc^N{&=09&i2#g!9vqu=TVA>~{o(Q5#e@00Kr$@Yc-&@W2!k z7Zf}zSifgAoJ++3*a4A{XfOa-zk5w^%&YRlE0G1@t>kO3<=24Gz4C&7vn0g8i2azT zn$9ded#{3%Gb=>73UF`mj0p%O)@9*_rsQlCB9f5o#S$=k1gArE06=J9f3DkAvjFIn z+WvOh?Qg9fT(hmK0N?A}tQ3Q2+D1?`f~ZJi&;bz#IET7Xv>Q_4yb4Ohk}lxEMz2`_ zK&+$zh)56WIzg)xovMle5Cn)90Z=KfQ$-I>Qf2`ld*uOI)As>gR}Dm9N;+50Y!|qK zl?3-gtOC&kZB~egmWK@8gmi7AolxI7^L1`*1u#fY#@zR*|54c^ma70YWeN!3D0000Px$mkFtbDtSbROk@xUUWyGH~{8zm;>hDm0^f7mt)`%6K3EP69QZE{na~$AFtjr zEIav#;d}lBBjAvtdp=8@#*#IRFGeagPo_(EtLlL`k#*2o*5M zq0n)C8rTw$Wyp>|=2vKDfz=T008&EWf3*?V@NK!x7!L57Tnmgu7Jfo zOpGRJ0A@HWEg(Ap7WYIu0Hl!AasX~2G%cXS12pbQasaX;h%E`h7C_SiiUXi=PqG7G zjzCXUq&7m(^%G13Fb%MR2Hk*ZK6}Y(h>#Qlgqmf9(g;RgL=qqv0;5f`0Sf_8G!1C8 t9F|T7v}1xV_0mFaaP|#p;Zs~@0swts0YBj+rqloc002ovPDHLkV1ls8+R^|3 diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_lethargic.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_lethargic.png deleted file mode 100644 index 53283dcac913fda6ef17dec55fd06e02076e57fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 647 zcmV;20(kw2P)Px%K}keGR9HvNSD{YCKor~yV9s(0SP~Rq`2YkCdkMiHFc=Vg2m0+}X5rgN{|MO^OIwB5El^WM&z*LKp3M`yS$RY9g@<7bi54CE%sxd++IIgb)|c=?kh0uA3EMllt+Z zdb$P+VBF`>YZqtMh;kWVZ%{BsjH8obLsN0KmLWw*^1k6{&aCECF2* z!g+ijR;@JxZ_|KolfIiZA_z^(NJ&Jh_q0dA>KjGfkPgQV$1J!IbKv~2(PNeX*s5p% z68Ql|ry}$WMPF4#1V{qHiwI~+n_SVsX<%6bh+cJo()2#SNd4Q9#8hZ7Yk82as{f#=I&S{2R7mWn)pne&H1P1*-N_(VoZO?7dW4-XtXr@0VkPx%8%ab#R9HvNSF270K@gl17z7Ck8V9)$et-lF2F=6a@cac9g~#D|Xbg&kA0Xrc z4oyOWz#t&CNnMKCo}0PAW^c^y&CX6$S6BDV4pPr!&@+CTO#*B4bKhMk^Yr=E3b-{v z@WsvDhQs^)O~O5|B(}7=-Q2#MACI~2r;W5XTp0hp9(|;%^pIAL&eGfSU9*Ri_(ed$ zu^59yR1ts^_+q%z5^MQjy_tKxd`wD$?#Yl zTtOkT4o}5Y1fqOnbAn@Y34#~3kX0bcIb=sP7zEXI$gvV|6VlWZJ>aHO*(oJ9 zf~#2x8a?je0j9{S0MDxlzIG{3)WUC;I*%^vno6VUjOO?HEWT8HX=ks?{A%))+ta!fc-$tsY-C(tK(l4aAlM@+qSML zdI0$^MXxEqed(;AL^>f5kzlFfs)rW*E~cPH0armgUG#s#=fy@;Q-a=<+y|Z#eO_$E zlL)Z>9WWQY64A3|RU?Yhpl1O6Z*UJduy)ozDG+tTocui0Mrvghz+#+?OQqPx%fk{L`R9HvNS0PUWF&OPaAYsYOLV_eqhFPXia|FZ`j)1`6C(wjnfJWdk2*?~k zARs8FPz#17Xc98BWCB7)UU#p2_g#BE#4>8`uGi~(ec$`O_DW{Xtu$wR^FIkR%8Qd( zDDyJ@$}Bk30r10>1#|K7ZE$77Guy*e+ulxA>!$QRFkAa~=H{qwcFqQ-b=r61ddE$B zk9XIXZ+1>iB1J%h?;X_aq-X#IV7a`M5@$UAw9ng{=Tq=?rQNGBAQB?sC4eaiPGIzD zaP*}Jbo-`SsW=A1(P(-vNMJ!y1uucWf;$Bi0rYDU=*uO*SQcG5J%EWB0;z(JL?HtA zj~%OCDCPi0%NQ{6K!`+dw^>|)T??Td@7AAV4 zH~|(EPa%8zV0rOsNLVD$L6Vf5j&X?Z?31E;s8G{5Gw_iD_Phwekr0YZA t==c5^&GpB`S7>J0_dov)XSrfM_YbKSaX)h{=WqZ3002ovPDHLkV1jhlHIM)R diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/strained_muscles.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/strained_muscles.png deleted file mode 100644 index 0bc4ca3177f618072ae2b3107ea5890b51b27d8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 676 zcmV;V0$crwP)Px%UP(kjR9HvNmp?B=K@`RBMl^)V7a$7JDJ2?_(1?PNprBUxFbXvV5<(#wn}|lD z)QLj)0xCiyvT~lBm$P^0&CJHin&!Qoow?_C|ID9Pvo5zC-w=l#g+PBY0$vxT!F#lEJRS?k#cmVZOA*#f&Vo-!C?$Zzj zz%tUp^8hrAXfQHd{}hiwB#Tf1DDHC%2<{6MM;|w<(tWe0XrO<7=P|tsH20w*QvrR~ zU6TC(L}OaA0n~dK&_eS7v0V~dG#Vq-ae%6N@sM1i7@ypTSYXIK8zO)PL)v`0_#f+r zPo4W~fP=BQ%1wzi<0HTnqCXqJ8`CHN0*$+)-x)&yj6xOw2J1ojm>NxiKf?7tJqx@z#s$BOy82<#PL+3J?Em~W?1X2zHogH{91lN*xze5h0000< KMNUMnLSTZ~dL`Qc diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/tentacle.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/tentacle.png deleted file mode 100644 index f09b036552c5d38feecb0844692810256363f7b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 691 zcmV;k0!;mhP)Px%ZAnByR9HvNm$6C%K@f&Fg^fiXK~OA$SlJn{u(DS>UqY}nWh@0>!cOh2EQHuu ziKGw(AHl-LBJv-6@y}#)n+PG<=8n6)oo{CTo!M)o1&_vp?Wg4g*jil~%p)?d-#=px zk3GQn`~GIycz8>L^PP&Naq(PaV(v%pYdBYHu$CT1h!KFCaSR`&-Ql8#t!BFG{~6)M zZF;J11tN&`cC)$)LIV(o7z5BG4Sg)*7?x4S+VOV!ym?6{{rlmw>(;>#N+3YQ2*fC4 z0o=F@M?+df5PRLru@nJP2T>aVk>feW3EgAotnygiYX}TaW7Gi9^O%mQ0OW}bxbdrQ zZ$t+~fxIJDkAlBL548aLKzTpyw5t$n#08n72nj-ix&BV~603_-(ft29T)$@J^m-F&CBR8;=3wJL!bo!aJfdl?L#@)h@5S*l4$qcZ|5_?3O6pnE-st z5s4@)K!Azim!5!7DvO0E)&YfXQa)q(jo%_fAe7RX0JH$5SBp>sqOf9WqDXyyQH0w2 zc;X^+fe{s7I0q58WibW%007@|Ikm^soPMG=5z#8#t>=G0Dlyg9{A=vx*M7d1c! z%R$7bs7kU^RH|dWV7{lc2%wJ~f_wUVPU*4Oc#~p18r@VPtb_1w=_S~ANNmSwOU@Jl zn2c9`tvf5IT?>MeJLynT1fT``HHwL|0$UG}3L>WkKN%GPJX{We_nZ|SMy7NitPx%NJ&INR9HvNSFuV%F%(S)2Ni{iKhPow9XpA1DXv{y+#KCSa1tF9(M@r7>o0V2 zaS`3RCOLEUWIrk(js0p{igyXB}1W=iq8Tu3kWW=w=2OZLI#&*NAW!z@9G7c z)kn3w-O%qZ&6d#``dk2VZvjC_>JfakaiW8v{66n``hC7uHUQA)0+DMALgIUEf0$sG z8`t_B8MDonDyC2R!k;i;*ED|HW$4oZD$g2rN*HA^MXvAh)4d+tj6K zA4mv}5^P*`&56i)Qh-qa>=V&Gke*wILwaEv7YTY%bOZL|`E>)SYe oQGZOv36L^(I{zD{WRg1P7ylMEKYErIlmGw#07*qoM6N<$g8o4kX8-^I diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/transform_cycle.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/transform_cycle.png deleted file mode 100644 index fab4a3dcb09817e32896f05fbfedb214fc59b723..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmV+b1ONPqP)Px%xk*GpR9HvNmp^C}K^Vp73JaA4BdKg8M?eUIaFvD>20@!Ew6PFOrG;G(1Vyya zh=QPEW06z|SXf!4QIJ?jVigc2AcPC{LByaI7Lnh;8@`*J-JJ`O%QQPPJ2UTn@B1^m z4cF(^=rg|Cn*_!V?A!hy%B=nVTQImVA@I)RsB12*JDwxsgPzaE@=xDyJXlTg+rHfj zYqVZx@TA2gx(MWepBk926lY~?swWZ!0@iXNVRapF%t}P!d6cj!Bo$y2L})t!7pNs+ zU6D33HOYhS)OALdeVP#FVt;GAp;F#fZ#=5BSKbP<6> zwgQs=(z(}_mZ!kAAw}Zj&Ew&`N*AOEuo*Rwe&yb@KWL%#*$rQA`~K#_VmO>R2uxml z+vPr~5T|0&1!d9$eyn}pgFS3scfUS#eE;A?GZ0{I=15UJEx?#s1+4tmdJg%%x)s2l zotg7}ELPh;GUmIffRr0*vp@=vMpQk?UkHjs5SGP3b_{R;fJz^g-k1bRC>?M7xawrN z6d?vE%@i=5m7RvNV(Haccly(&AEuOm1hG&CuDgThw`)xwm{~!{BEZ!kks=V+fY^G* z0;iu%z=Df}O)I3A^)~nvjFR7iXA?+)TM|PP7knzp|55>PQn3Ae-TOWU&nCb^iV;4u zF0gU%b@5JDJ>VVS)B{=1-1`&5;S9;YqZnY5jRVGzpivQNDn=q;!Fhl5MaSOsc@GJsy12rJFgm;e*%?4@JAr?~LC`UZddQ!lC`&9*Ifr*)r z-xZW1QF|BI>c=Kzr`q+>C{C%gtoPx)1xZ9fRA@uRS>J0ER}?-wQZ&iplR_U%;t!CHmb{1*B`Qg+DWuX0g6~q=r@Z%1 z@TI=wp-_A;OCKb(K@uggt!$A9meORcwC-kC#JZsYyO~WZWXpGF&fRnG-r3D=bmqRy z%)NK!oAaGt_nw;}B06w*sNucz=)!v!>Bj7I27O7kH1UuR4@rh314d#W|)1omx6jt6QGFL42T*^Hr>#FW@KDFv%n7@-;WI& z2i`xb#`3v&bv^&l1U*`562NYl2ELPk{=Te11V8hYGMN@q(AU3bfP&2pZAs;|63r~! zqEc~{?ylUYzn(s$d~S}ePJK@Ev$wPb*aRer-fPM@ zOjiUXf!~&Zq5X%n(Dw?91_56T4$`$sh4_lWQGd*sgLi)ZRe0v7FHm)+jk0;XGk9FxN~^HHBjW)` z_-5{=$_Q8>N$qb3|F?2zQQY1g>K$JZ(9Z7xVr$c70n%FRc){;;MhFAqbxlCG_$~|R z6<-t3F5*KVsTr3Ae4X8*&uX4hikxf50uG3$Mo#Fs00O$fcUeHM_$?M-YcDRsvlZNq zGJ~BV_&v@V{4DnbydIDiuw8t%g2jVjX6hAR5pd$|Q{-3jH12sSigQGlzHeuM52ADk z|Cj8$vIh%~I52o*m@0pUsdRjDf*uzN)C>MtDf|Zfe<%2w0PNV%e(-(zHwcV;Z!?TJ zXA1>29|2ghkFj&n7z-}Pu@gi?07QdDfPilCPp^dd$N)RRR|I_Me@s99x}c+~WR&l1 zmg6iy&cg@!8QOQHK@iXjew+n#i?7ole&WXLv?f7nlJA*m*BDz5YoF)Hxts-DX)rB( zy%4gsVEZzdMMI>=J-fh%JgNgS-&x7N1=pe{5&?#!^^n!Op8v!!ibd z09)&EksSSY8k9@nZtx`mJHc<+j7UwmB6^&UNq@&ZzYEQ~0$)@fOC8_29YsMjDux;n z*7ox_zn2!^st9%34kwAwCk=jB6%!<%< z3nEG28w3pem!)7cpn<3+C`(eKBVo12RrO^uVD5HF%vBMJs|luoA6pd;>KjfXWYxIE z6rv7QUf&D|HNpJDC91Bkk=y`vDvF}&oD_cNvUq)iuTteUtdklNo&;0Erq6vV}$Wy~3iw$*4bO?~*kiD6zRVL2)NTN#eH> z5FLbA71`vD#uCPFIUHrdZ4Jly`|IeJhoed38w3D6Nx_*sCLj&`{{gin2`P*K{__9; O002ovPDHLkV1fY7R)b;y diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/1.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/1.png deleted file mode 100644 index 626313e67417d97c6af2ad780b9283c2c8257a25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1084 zcmV-C1jGA@P)Px&_DMuRR9Hv7SItXQQ4~Kj2B|Z;swhzV5CREAun5B>Eiy8xMc_i(MBvIze?^zo!5&kkr-&4M)QN zla;JvG_9{De0cynWmHjKsQ^eICKz^&3F<*tzd9Iz%{CkfgJmdGOP6{&DKQt#17J4< ze^pa@e^&!gKr(5ftU6ma%wKNoP-9lOurlizHZ@nzsA^<8Yf-WFD z=AJ;D0Sa@KU8`@oaAoFfJ|Y0S6)aQ-|w` zt(DecZJvnEJNxnd-eG~yF!%F3SRlWTdhXn%#A-}tL_Q5!Ia?h{E>W+r80&J*T*nxV zWh66T!ZDKSE;JBPR%e9%55V$!(Mjc3Zqj0#b>^HL+x8seBpavl=;csTaw#Mu+GrULc z%`sR?Ci*4mh>c5GEI zV*uS|NoK2v4y*}L^``gttUzSd?8SsC?=xvaO&D32qtwsvGVl~?DhRn>JGw1uxxidQlFF20YhIG%i2j|-^;nu)646hJgb@0nrKR&t)J~JjFzuP;AP)#)+&L|nk1yx9>tj6dB%W~Rp z#x{gtM*sq8ynpf(dRO`~)*L9Hl*mlN(b-|gTuV1FdBbiAXBs}R1@e|MP+nq;BTGxx z+UQc`U;&bNBW71d9>_rgfyqXCTVe*KLNSQD*pj#Ora_!4u%$X6-wK^lF=!|~Y zns4b2TNyIOynSS519o5H+gsg;MwT%YwoimtMlaNJ#pF~fzjN6TF*ylGv=S{XVJqUy zv6?MIjNFhP^1q&y+x91*=&kSWQtpmS#aYF+Yr>f!+9Nj~*?R*w>~&SOHCEsH1jjpS z(e~LpXuH;o>(`8TMTN!os_V##_g-Z06PmWRMLYKW6m~iuI5s4u-ywkDFYNa{&E(>JkJxPlGs ziSR3N^~x3N2!8a*1w+6aua{#T%XnGL>wj=benj*I{8KRH(o5^E3}`!h(9>yXh>J)i zWb_0XZ56A-f<9x7-FCE|{0M*Cuf(y|dhth}U}#{_5KzW5FuP+I#GmcLTj0fWwuK-& z%-!b+cPbHqQm+b9}fxw&Ix3FY7B}g$(`6eTj@Qtd@6aGv-#%gpGi}#|!pBq_^p_PUH0W@D~ z6pIO~{HY98S_@)%+c?Zhc-%=bew>aVma<8Rm>|ig`okjqgq+7vW zsKloJ1c|&DQhP?y_Ph!Bk^!u(WfBvJ-1z;6ASDrWCzxQ4QxM!Q7L^W_=TvG<^^c49 zBj4Q@S()O$a#B$7ks#9i9K|-`R+=TLgxq-Uim3i%L-y-fVsv^~lwKr+po%#N9Yq%{ zdm#Xe=1gWqB#Tss09{WjC4()5YQc^sr}Kk^%i58Uts=@<6EbETFkL)O(5I1(MYXKO zkqZ=v-8VcL6;UQ~)`U_BAkkXl)k(m)sX}3Z=X_kz5|&VAYxa9ZnA9nI~%#{IrQIt v9JB;EFC&HcWk~-em@3F2fXM$x;;i@!8tJ{joTrXZ00000NkvXXu0mjfX^)s| diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/11.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/11.png deleted file mode 100644 index d5060df98bbac9f9fa1b14a12cd8617565f5aa0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1387 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 DIi8Q& diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/13.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/13.png deleted file mode 100644 index 2df6df47dd92c6e461dbc2efa66c53048b423dde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1401 zcmV-<1%~>GP)LP;95G_|C`m)O>% z5@VBw_|=&9lmEbv{sZHOn);a#KQsi1;tN$m+7i$Z;zET4mJOu}n_^*KSXg$PGk53i z+`GG2jrJj%nYnXjp68r9XYRp-zzgjmZfqO4c(xxmM?YnFiZJ!Tw^Jkd@bX8T`cF%U zLxA63B}EXFkjwZI!Z#)yD5hl_1sP9w$af$a1nVP*#?q@IkVIP22BX(2>63Yr4x2rN)$yf z;HyGvHwnKlfOI;ESUig1@vjkGS;dd@Q_}N0Lt_ZFwc`G~7J+img@o$ctWL1LtbIJO zDFr(mkjUZnlV>orHe9skPyuyBmM5HCxb2ubeFOOUdZ8tp?|h>mP_z_*hFWVJU0Jc$ z##W+-3sA(HiNb**qQoa%npFfRn!B_X(mwcc#ARopEgVKZ0VLFUa0p0LY@;<8w!PK8 zgJ`eKMDTf!HN19O%n34UKH{>E*n6Jq?!n#J8Ngszn_iO7)cdyuPzF%j>J3XT$fMV8 zMex&mU81i`EO<+O@>I=}y>uc!uSbGJdp zgENdxq3h^fDvb61dTia=$6nKqWnT>28#~}5X)I1G%h8n5IQW&3;ffe_q;$)S6;tvZlzir?VcE!j79 z@6W#f3SR1K$1YEy$qb>MHNwXNf+QzMjr;X+O>uCL!CywmVteiV8gy;v7!e1o7U+o1m z4ERyyAT?1TLcec&*}+a>ng51%b^`bLoEDu5>>*=|p?wlYLhh?@^~x1H1>bt-k|p4o zV-48ADxMUJ{8!+0W(lYl5m?x<43dBD!t|MV%(f6@N5u9Xxt;qQQ0R3*ij=Z~ zUc+O)h4ev6?nj_QNFS!6G^ELI7KZV5=ew8~9ECSfDbw)LK^&FLD{s%k(vZd~Gf2#8 zJ`HI-WzfvT9Hs_;kBA*?J*mpqL zG*nU0MW|@d&8X1W*n~L$11V1e2N^4988ZcMHh_l@i-`$DZr%Toq$GmA33)I_76kWe zWTivpIh9(oBh&Kv#25EuRA$8oNC~QWHi(?aqr_&?4znUvPzTRl5p|sGEd4oFn4G&U zOD_^a(8U~vzUs@iy%Yd-{T5e5sz`MR;5-NY%cO)*E!cO;x%pwjRqaS9RS}h~2^q5z zjLSbK=+&r=Wwo46q8!MPLf_iSs)&kFwkA|U2uVRoW91c*+eK;~RR1ZYGilj|>RK^y z;VOo{`x4A!*eAcJCRG0$a=+%cxU;GJCbYQ72%>Crl)j=M z5|f%rd@?bnKKUQ`=zm~*sHx9{_+SVUMGZBCv?ZV+#DxkGmX%V4O|h^)SXgGf=gyv) zJF~l3jrJpO=gypazwey6=gh$mfoD1*roOG?;<Iuc%TsiY*h$dGEj_B&HYS zwG==~=p1PKZbwN-b~ob^!}q;}UKETjzBR|1RCqCd3VQOsj!p*v_y$AG7y7f4T>^XJ-Ii!`k$cyvOg~7C;$5V_P7KjfPdc z{a!z68*9*UK7zLv9f8e}X7uhl^WNvgpLeb6^~Aay*?#$U0^1uqm>r2p5d|K35GfLc z2$*&RnEZ5qw@prjPx+s=30w52Q0yxTZypEut>agF{(JtOErRwVEKV%r(;L^3h_7Ni z=46DQy=M^lg@G64HIujN$6aK)a>@2! z{keBuz_WcF*fld~_D9g5HK9ri2@*q)>V*aF25w`kw2Q3&yEWr>Vc&27YPW~1fc0U zf~a>6E|p3dTprECCH70t-8qLHf^K_^bSQsBIyv9TD66lmj{9Tw6O?9Z@mL_Jk~ikQSsgXRnCdZc+=N{*RH(W#t&DZ^OWAS26V67tnmV zee#QXLjK>-3@Uz$noaFDq18i15N(^Q^%V`Ni8APY{;YcPT_}zK5qmuJl9j^W+3=|s zFu(t5%ogPS7%9Y$Li#VEse)Vri2Q#fE{VSYo`0yNqb+b800000NkvXXu0mjf6;qad diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/15.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/15.png deleted file mode 100644 index 36ca36b9fa49dc99137d2b7692c8425529bfe26a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1396 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 Vnnf6pQCa{1002ovPDHLkV1g)BgOvaP diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/2.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/2.png deleted file mode 100644 index a7f6210c9ec8e060b7d28b79b9ac2ad8dd02ddc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1106 zcmV-Y1g-mtP)Px(3`s;mR9Hv7SIM=Nq9!5^a(IPu+i=gxh1-W!X;v$^m7Jm2}wIq#ku z0D$tU%1m%Ah%4th(9_o);MiV?QYHZSO%Rm50^$NtTwIE+txW|$fTUKJ(Ho8YPgXLH z(O8;K266z_i%@)^L;)~?*kHspHmG~;57fZ~Y)fx6LYApeO`X5phU8={2SD5q`c;PX z)`kY4fNZCMj5=Ej$1l{is=g?EKR4kUHszO2tGQ@oP_>t@x8dV#0RSb%Ar}zoOA8Wb zf#O)nfl{QmH#L*Vl>~Z+`jLn)U}Saz-z%Y z0hj$b0CBZ9GT8ff04fik(n`l-!!7_%0o(^5Ehn35T3l`#N!&kt}YP8 zsf}DfJzShYS4FK>Abr1aF8s7OqX7hhK|Ff$Sj7PWym>XiCD|;HAY^-Te>KEf(>l&g z0XXBV=l45@1v$fBFXLnZ!2oXGyoKa^+{}nv8j5nUI=nc8PQzl(n`8Do`xMz@GD9XD zBT;#_20%tZefE^D6@oeCQ$_NFZkl(r4&$o6=NPO7wBKsvHvH{uU22d#Cd7u|Fw05Mq9v4Ta zb!)G+V?hG4v93W8na6;Q-Y>$%db^eVvd?5i0#WV+rlSJTaI6v6q7e`it&)f~eP1X8 zgatcOjCfX`%!Z2U6IlGbp!%)t1_0hzly9A#_!17g@|{+g{^pwF;jm}6*mE|cya_1` zK<$F7H8=3~!;n7Ew8+cHcJzC#m&0U^))!1eg6Qe%)*wtv*OLe`sS) zeLsoR>I%#Y=#K!H?*rb~_RGYq7YT&QT;f#C+#7lQ9D$-BPx(B}qgE!VuxuBI1E8WJi1qa~1t1{lr3LiFqW{w> zSxafmPNsZ$fRz$dlm`@m4r0Q{(U?#-+q%@o1U5@gEJ`a=pc*>fejcf@L>@qnA@pMh zGV7~0fCAab09m!SUD$u3>9lH#z_$~lo^Dfn=AasjMf+8K_R@KLcwYn{P!V!~NLy;q za0V3n0_8zuHr8xQrWU8rGw=*k$!WZNKZ-B2bBIOzaHFde{k_k;Eno^{duOO>%fMJ% zO)GF&p9e^)wbA~bzXJ$Y9k7KCCk7n=_W^VQkeY)n4W}G)xMpPhH5R_6F_lc(09zh` z2u`o&9qQIh9Nl{xZ2{8u>gU9dGjSWh=lA3Gy*tVe2=L-*AE)G^K%9{E{<3O_eUDW{D1c(;&c$A5Yx;!vhv_e}ULqprkljvzVjj;8-7G_GU$!2? z$)Rz(Hs_kYAP(8wbXXa(zyc<{mxqh?W-aSwoo*FzM7a|fjtb!Lo@QK(ML{NNB_6Gx zeX$4-E0~d@`Lp_DH0-V4hnY{)s(rdm1K^FtGT+gG&yk2D-l>It-keh%iMU3KIc8dv zHz5lh&_03-4Oj5$-GIHJu94dro8Ip>UpA95YF{)A3Gtx!t_`7UBHv;Kw=uDvSVrdP zb0rEKVQ4u<*0h3~mYi#Pd@=3yJqqsyD+~pIH6eypdU5D-BX;bnL|t>(^dr=SYYO`2 zQga1;lOriCFRWnvNgNYHllFK)7|MwJ5e0(%CP;O44Zf9T5GV=a*SB1j#CF-w0Osd& z5V~NtDcp2CEj@GIhLVyoA!VwSC>^uO3By9msCst@o10mzrq`6FEYwyn6z-N0d0xi? zAQ_FG8S5|w_O?1&E8EnUa0K!+lLv|Y9^j7IEKFhS9!?B8m|7Z6IUf%i4O2Y*VNx8xdWaYq x@isiomi>xZ1e+Rqo%g@?J07PLzFmR^{5M*oQtd=bFT(%;002ovPDHLkV1iyY38Mf2 diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/4.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/4.png deleted file mode 100644 index 36d867d3b85128fbbedf9973129f57e6df73fad7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1140 zcmV-)1dIELP)Px(E=fc|R9Hv7m&;33Q5?s=GX~|%Xb`;=n_dt|Ab~{~7G+V9MJ<9B(k22|ZTc(P z;qQ%C{mASR5s#)P`rd`o?pz-H@6#A#&;R7=OMokM0gRRj<>M1E8v zzm#(T6v%QOu++Dm#`)uQO=>IxUylv>hE2`s18OZ1?@|4ki|6p}Z3%!-dBg=GW9dP| z8Bm-HZ3-j5yy#dmGdF>b-Y!g}C-LI#5I%pLMk3yg8@F1~)7j;30aGByTO-@nOpLi) zR)Nd;B0yT5jrVl?9YA#3UPtIas?P=R06;4M>ABcgd&;$kX9h<{F#9EoiFDclSkna} zIGZa5)a|Jx+IAgu1jyKLo*Um!B^`i3Fo-+%@2WT;!1Jfw+)~H_2}1UJHtvULt82%# z5deqn{rvsPwt{@a+%NOA0)hctyK)to@wA>1#WWP*qIGO4i55*`uIqE=I>&5sMz;(t zVJnH~ky-$jlM&AU05!j!>QjD|rfck_ojIqEz4kLBG82>NO%^jEz`tQ#d()@XjHcH8 zFMwWF@Unmz4}^Hw{a9s$0x0Eo9`^IC850s8X1vIG$%sNgwz&aHMLa*~Wf^Mw;>01G z>>qV{bFUc-5|9mb^~#W|EMU_6MYw2h_Htg%=~j_Ilskdxr~vABHsC@c4l+?IiD=#T zr7}RQU}lOI&*~?$Vb}iMnEEiO#!bx{0BZ8QG)n zl`L?jp&b|&0s0p@ap=-PRBo@pzJ{oRsg72gu3U3Gotaj9#s@Q)pIyM{lO)Fa$Blcz zPD24;|0fWUvI=YtY{3eCDj_**9=VkT2g*;lY*>|svLOG60>OFH8dX)*__iUBP+1r& z-wIU{#~Xi!Ff+4SYpzpsGw`(X%zYb5NydVdrB-Ugu%j7a*ytYB?}*^nFAKTsqB1iZ zO|3Ty_o|3|uVVp_ibk)Dbr`=OI8o88mZVN(Y8L?96_Ia268;`RemRe@T@&i1Sz}$H z+VQG-1&FC;mSkQP(V8_OtKR(5q9=%6HTzjYmG{eeuqI5r9!B=-9P|t5j{x-d0mIFF zndtQ*K`6}C?5de(|(XwC)QI&}dlV z=?|0A0QN(~sEF6$X*M5J%p%y-(9hrhI`4X%Quula7VzJ)c~tH9vdD%20000Px(VM#5CTn zR0YBRK@bbo2NCRJkVR2~hLR{riDBE2N`tVC2AWN_ZDO;bg=Diy+ay=c-2G-}CiiYE zE%{#N&d=|BzwVsz0ATO_lpkA&;jOk4_^$VBk7HXUN|^xQH$hOg3Wx=ut}cP)3p0UYE3hNMBmeaSkYs#kVI=T3OPb%+F7uXW$a1a?`kcV+40^&mfcT!-wb2qQCc2 zvpi!?wHvRThEHf zPfcSWTd4?-zhTpQH>R|Vt~TQ@t_|}-m=CtY`>ofrH#-hyr zbP9KFc3bsME6jLvO~VJ>`uu8hu19$jQkZ}`1aCLJhd=%r&6On9R}o zs%1zJLyNsQ^3h>*bRD+5x2N`b*PD_3e^>8VbHnMY7`k{B6GM~ww``SRIWc?zaPUMk zc05~;{p~30Q@>rN##Mm!c3xHLI0)TB8 z2#ynoNV29L&v-krhE+9G^cPTETh#MZOmUUVf(>OT6Y@tC5F9tX!G1$SBOcV25U)vK z?Lokj*x$Aq$L#Dz#hj<>-NZBKnSC1yB_kmv)f%rI)s_(qh2c@{b4jeP`zRKcl$$AZ zwJB5{#)u-XV*wzH#$?7O7)y)MIa1%LdcqS~+5rHY5k(S`#KR;?D? z@p)X{X*emodxNb$^v zNp%3_hls<7x8P~E5L1{%v1?&eyq|R3dYn@ERw)+X{{e0na_x?D(oO&X002ovPDHLk FV1nqKGwJ{U diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/6.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/6.png deleted file mode 100644 index 994c2fb5bd5ad8734683db6369efdbe54e1206de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1225 zcmV;)1UCDLP)Px(gGod|R9Hv7SKn(~MHK$--9Ve&O$(*fB-YioAcjaNMoA;F*ebRdiz0;}#g{4+ zAF3euKL}z$eGoyPB*f`*zXO$lM!kV^C5HZ{;}vTc(#8(K^@n^cor`R4AKojbXA zQ&DnW=FXWP=R0T4x$}(!0K505Ja^v3%WcQ-Rqy8xb(Z30V6 ziwb}MDcqYyPbU3;M#U>TjhhpBrvhLhhT5%h1;7MigOQ`LLA}#?T5U|gw)AAuj4}H-!sVhjHiTG&1QvoIHIB z{k`X+BVZe3e`j*nhJ`U#EGXczz5*bp)~5S={vCkSu07i5(AYo#z?a8?Q)dBbIoQ#3 zC@_a>E{V4K4{D0%y6?dApIykC;o9Gs{uH!i;uqeMCAbi z{Q6TLr}$MMNyzr`E&Cwen%;5j^2fk4ANuq8{oAk~1rd9`=otmL4!T}Hj{HQ;guA%mx4i%1{8X4Ff?vfrunyb=dCgz$#Xhs_4z5 zxVoUnX_#UgmkI02PzL0WC?KdeqrrYdLnH3jln{?4uzKHTN$hV~iDPDFy&t{O)vo~ zM(1E%r|Jn$WNG^Vf{ZAVkR<*}qO@E>qRa_RYSz)>vz;*3D;W5ru_;*?BifJ?lJ%CB z7DIuUtT`$Yirz1mKu(ysGJ?XrIhdLMKmg450n^QXnV5W$Kxix=W!B8K>0iEs6LTRK z(7Gd#r7U2H?xcX}C8s2vM00000NkvXXu0mjf)&N1a diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/7.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/7.png deleted file mode 100644 index deede1c64cd501f15c05a051e04c71d286d1b240..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1252 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 diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/meta.json deleted file mode 100644 index 12f5ad4cdb..0000000000 --- a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/meta.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from cmss13 at https://github.com/cmss13-devs/cmss13/blob/09a5191fb11aab8ddffe3f9be94292b53e4d96f6/icons/mob/hud/alien_standard.dmi", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "16" - }, - { - "name": "15" - }, - { - "name": "14" - }, - { - "name": "13" - }, - { - "name": "12" - }, - { - "name": "11" - }, - { - "name": "10" - }, - { - "name": "9" - }, - { - "name": "8" - }, - { - "name": "7" - }, - { - "name": "6" - }, - { - "name": "5" - }, - { - "name": "4" - }, - { - "name": "3" - }, - { - "name": "2" - }, - { - "name": "1" - }, - { - "name": "0", - "delays": [ [ 0.1, 0.1 ] ] - } - ] -} diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/0.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/0.png deleted file mode 100644 index bb1a8a818ad7c3b59c5fd0cbf92bf400af3cb67b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 861 zcmV-j1ETziP)TR}xA zl>7r;{cFTSp?3)$Bxup7q=KbV>OqQaDuPM3IT=AHNw!kRxNml6c4mL1K{xBSgq=-h z_I=))nVoN=VCei=GD-OHHTlQ~JuK@%k5K18)OF&z1imMkCRxmy=2<^ib#0KJ^>n+fJ z)}UB^1-0rPyx-YinXea0kjkWCcSov#pJb3!GGjIp$!ZLvtVriCcYjNfab{q zNGGzeR#}ChboJ6SeEj?gAVQ$N`Hg+Y=U)Ke4!~>rRF-9MVdNqNDTJ`%u!V?~uqr@B z1u%G!nS1HU@)BsZJt%M6J0i-nM+Kd^tgrjqVnNOtpQrWbtj#JAM)tBs$1x9-iG0O!sOhf)Ddl1PVMhyI5Oj7*QS4^BaOrDOsmL#O~I zhzFBv8{&_zD@C|7^#EN^Um-lVA@>4!)9?2Hm*+MNqYA#;kXHdA45tGhw#tD8#mvq3 zKd_((foFmxqMU|_qtndkpgp(D=syE^Iim5&3Gd^WYi(VHwR0V+2j$|2Z5cGo1hj#<#-Vx>Hh0DEm`|Q^Oz3+_K&RKVc$v z+kBGgh}`n^gd7CbVn!20Rv27(KTGKUxb)vTyQYTHkiWeMg}2XuoQQ~(@A8EFe?uCR z@;2VtXunaTj$kO`_*Sg%wTYV)e3ZL9Bd>f#?h0UFM~2Q|UOKK1U-gFQyO$*aqyWf) ndkOlNA~KKyfWiA1*i(N1R*Ibjt1`GI00000NkvXXu0mjfLXL`% diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/1.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/1.png deleted file mode 100644 index ab9936e2a668dd6dd8b7ffc41794a48698ec88b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 958 zcmV;v13~vQ3R95oU9;}BwHzT-8Z{4JF`36#+s~` z`IfM|$?U$*dpooH?F)mclj(#J+6uwg`5Q1de@}w|73L3KEkA>q>$eQt4+=y8h(tOG z1d4)e{`vvArEDu<$aT%CL-kula{x3a1`pzfp>jjX#&Bb4Kyo}XJr0$XBH1SZNP_b~ z+Yu8*5PiELz|imA0o`MGjZBY$9fxeAb?3LR>;w%s5W|3IBnAz=#sF;940z|uT zabOs-)tqPio&a4K%nrC%T!3aY+JA|GVgQv9^rU3YJ zc%?EKuonOkoeu&)?eG>PW0|J2ZAKT)4Z(*`9|1xdR9C)|@96z!0Js8hn>>;s88n;i z-$2-L$V9|S*cC8X1u%FkGneW9+ykhTH=wZQtcWnp9@Xm1d42i5#wO%t^Yf_slift01{<8X&Bl}9ECH`0q9#=UFpOQ!GB_aU1xH;Mmx7z+=Dj0I=7@Yfp{9k1 zn={CtD7-cF!o)4>b?_4=;_O>jvK*0DzMhbRpz6eE0?P`6Gm~ly{U4S7TPNE@R}!YL zPC@qd6VMKY*v$8MLi)d<5#jPSpV?@?QN6BUDD(9#SYOkO8sMXm!C`*p3-VR~1G_SG z2a{>PF?80OGTuDTGavzg58O=914`q81ON=)#=ueu0Db8*fm$;Ufa(v(K-@4?sz0Fm z1AbEd0k>3tfS2kIP^tca>JO;?fa(vZ{=k9y1G(8d`v#l2`;d(%!CToMa9>;suKEME gRDVDw)gSQFA3EeY_^h5jN&o-=07*qoM6N<$f*B&WP5=M^ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/10.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/10.png deleted file mode 100644 index 9ef25d0043f7cc4eb7b494c8c68c07df035c0e70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1009 zcmVWHI2f7_{jMmKG!lDZ#U_cZFqLCQX)$T>i? z2UmLfpinK^<~IarLu0zZ`L#K4qk-I`2|WUe5dk#bOwUDnH1QZJg-AxW8)Ec={*&jkQjarAXfS6IRNgK!$U*H61<)t)Bw($YKJQLBZ+zG?J4{0 zgkGp4n;{>31hdmK8bI8K2rR3f6OdhBj;{mdYSnI!1%>p5z!)*15lJbV6%~eU@bUcxw#Nrec`aRBT7^eIo5=d6e87} zLjHK+qoFq@)v{azC!r(eSi6%}5!vOq6H*XVjTD+-c!kD|B2^dGe^l~cnH&>sNf@~? z423t(K{^&PGT&Ac(*F&W2;;Y!Kv;gGIxWUf>g$^;ebr*raWCob?PD{alRW}x*y5qp zm`MB0p;@m=c{?>@0C51UVK+wqQxXfr0ibaY4GYBqWV$Z+TAJAag8qOA#0o)apkOGmdq;JMWBH|!6Lj;U49gFBuw9R2=) zXHm#%Td}MDK+_>q;|J~!AOtB!$L}B7_@Vj(qhs3UuQ;eMFn00000NkvXXu0mjf5U$6X diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/11.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/11.png deleted file mode 100644 index af2190ad8ed4122e3bffaf1cca71bc400863e337..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1010 zcmV#>Nf~^dND>Kscz;VdA`y z5RP8B>diCpU%+@U;o?ab4`vafr~wa%A&h8DU>XxQ=1yW_LKwx6Q2TrBYu{^MXOXpX z=yzG#Zm<1)e!u=+-><(gs5;#pS3;X1$X>h&Q#1D^@KIs*;MLM|7{75_!F|6#IDkl` zl|Y~<$i}Z9P*}`26NX$@jXKo6RV5QZGHtLRRv0QPlxPeymKr3-gJZ)`T`rM*0)Ql# z2huL@D1yj4+Z+u2-D=P}hSlI$7IZsg8?BrFg+(Vwz^ZEGsTAlFfQaS;2e5T?6B4nMYj53X;Cu$&e=GuoB&aQaCBM=8Zvfy7z-{7Kig@7G z+xOHF2p0oP$ebq?FE9&=g?HSo8 z5%9x%R$bjmW8b(17Ze4&)`0r3q6vxN=K%cD=*WmM1+V6YHGoqmI-uqgfW$0%d&+-^ zKxZaRzIY4fW@a^jb{`_JqI!-ClWiK}pD#-VxSj*}Fg@uhgm4Lg1R9OX6lhZ1*^+bh0_E)T+IEa$w`4iYVRNBVLZF7Cz0B z(kq+0Lyq0T^fBWJfboyLBBDhq2cRf>z-A)_!CG+0a&aNJy5O+2BT7^eIo5=d6e87} zLO#6k*3b)+YFVy>lh6@!U%Qf45!vPVB%~myIw>^4@CuC^MXD~W|ET2OGTFx45-@gc z6!Nd1f^;NgWWKE?r0<4Ggz;NVAS}O8T^3^~b@}E>U$q!@e3lIM^|P7J$sPeTZ1K=) zOr*b!p;@m=c{4q00C51UVK+wqQ4$Np0ibaY4GYBqq`S}gTAJAag8qOA#0opGx_8S{hQr?< z@GJ@$?NRKiKhSgt)%d>q0|-Hi(aF09Hh!S~!1%-?a_km{9gi~WpC@I z!qE#?y?G{{yV~t zs{-Mp5m40%lztT8})ZyGC?r{EhqHzi-m#(St!;9V20N=tb{t3eZFHH7salln)c z+gMW^hOZ7m_RTX;4u#Cjuc`^ze?u+I`ArfC%WqVh%@~RlFGA@{ic!aBNndvlpZS8S zBY=i&9@>q`^shNA>xs0t6H_LT0Kgk|WAs0z@IV3p8u!q!R02Rp`&rKm)0zN5e?SIe zhhYl(13`blNkMtOc>W**-IPnAlU!+q%wz`}y_9}_?2*dOpq2e&`qxkwV$ z><^5Lk}Bx#O-~sPet*ETC>YNGSN(yyLlEP;?hhaYEk?)g?AiF9`U4}QkLbQz7=Av= us( diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/13.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/13.png deleted file mode 100644 index 26c1870e72247ce1f3e219446f494b5d0cf53ce4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1024 zcmV+b1poVqP)$k zhyItP?e^O5_kDY>{qGBdsxSuz16GX@J{g`u)SiN-KvX+Uz^H#`94`4zHH0FVUd zfwavhiXigVCI>@5w+6J1VbwRB0X+`cMyuv`VbKW^Fd&Ko(MSw7&VM5YhR-0c;#!heRypI$JmDy_km2Ul#yE5>)1Ykk9DvZvfy5z-{70ie%t6 z+xByBUbPBzLSp%S1)v%l95j~T_3VHKaOPACRD1%En1|ku zatIY@O?Qwt{se`|DGi|6hYH{-zz0*!GQ=<6uH@ln7U0Xon5PiJWk|gM_$c8r^s)+z z`ov?O3*0iKRqz4=gKPJHAVCoVp9#7oZWY9~PZFn-$!b=s^6ABagNqzdM^~r#aZEMy zZJJoGO!gi*b_>(Zj3)rbH?@w4np6%zQFefxjT8jEV87+!LhxTpoOeWt9FeUjl%x=G za|(I!!k>m-n7C!R3UO8GD`WdLyiY+yG*|5FkR!~tM%4+9Iu0d%xq@Z6X>0tEd55r`FrD(DXc z{Q)}#{Q;|j{s5~z`vb7B;9c}|+POb4^Xwt`r9Vh8J@s#1{_Xj+yLRz~OyXp_r9YPJh@BRQn5HmV<|G>cy)E^ibeM*kq!m#5} urvIr!f53WiA$ZUq&=vFtL<;%?cG?BEYa3Efd@!8=0000>ZZAsL16{M4UMq1 zU4~FY7*w?k#h(Sp=3YV}zXtDC=4s}0nQ4f|WAJ5#tALYukZ5yU--$F=I|YgaP)LAk z7cO@7LZ+0h7+)2j34<8{XIE#yjRra|nV?vKmXrGVg?!$E%onNyu)yn^)`U9Kde#*H ze-5vBA_2w%pknhu0N6ab0kKHJb+&HQcRmRpJ}m+S6eulxrQfmpZvx;7z-{bUf@a`0 z+r2;-ap*+EN*EQ;%nD#|btMN8sJ(i~XNjzm2NMsWSXhJHvb7?DEPFr1Cgf%Ec~W{# z_h|)K_I|0YJ#Ow-U52$(4|$;Vk=wu~B$nS-0H)!gA#({{Ne}7(r%tp$$tM7Xd+F_J z2T+05WC#7=Eyztx>j2F@Q~;MDzL+YOA^!MkH4E3$03RpEJ%x}iL*@m*M@g5VmsMCY zCLaG@5SAgUf)@}NTz~!p4T=!>PB5eht01y-oI0IUTDMxI_fPj7T;_;6x;o{rW2V{9 zGt_#e(zofcTbOQcJOwa+sdPllqzV9yG24aU{s`m%# z{Q)P{`vZ3UU;TmE$M?W9C6kA|Kj3NhHXTTR02UXW|1t4Xj{O0@ba?v%o{KEQs{Mh{ zG2(*m-t?5=@b?Eit3pzL6uasV)Et5Y-*bNeA*dN0zq9Y)`|1yjjyqJA23w!56D#S4>;)$Ts#+%a%O~700000NkvXXu0mjf$!p9X diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/15.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/15.png deleted file mode 100644 index 61536f9e5cc2d550e20b4715107cceec79808185..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1029 zcmV+g1p51lP)TG*l32lTRbLkdLOg)sqM}^sgxAU)H^yVD}_x%Fl03wk_ z0)e6+>wkVhelF)G47skFb*TI(OD2G1#$Z9LFjQ73(HLed4M>jrM+Tw1uuAp`0FvN3 zkaqZqB8a@b#lg_;tpTlLSoM!&K#xPV(W?1dSagB}42YsYG!la?dDGa6pfH2chDO-h zu0o_C3bI^<^6wJl3$LM6T!RlQ^JM08xoLT25={mx@IbvRJAQKm)IDnhWY&>v=~2 z>^ZEGsTAl1Kt$IA2e5g30}`>6_uv8Pa6Am%g{~XZNRla7bK?NR{*Nvp&_FLuVn`{fHS9BpyCsN#Ju!&l|#rt zYr2Dc@D>!NrZs?OA2NVth%cs^GQ=O>tmffH7U1*bxTg@pGNfJre3Y;Zz0AUrzVO)h z0#}AK3tm8AaP9dIBq&1QJ3*JkRY7d$Byl>KtY)<;ADwdQvyb=4@2(V z3y_Y5jFn&G3F-faN`#rWx`5DrquMONP^xX?tgmWDZJ#Cm-M#F}=Tw^k3~ZU9HJC{M z8bepTD&_s;v;o8cuz{Tf{ZC0O5C?$4Jq#=q2hh=e!BtDXZ-AgbAOf+%PzC*gpg&-z zpg&;MzWW2SPalBWLI^!h{r-R#!HE{t45ZUXyg%UC>;*cM{s7(v+kZ^_lx=^&FCE?f zfag-nux@`~bWC+Y_iua3aP<2Fo@F7eJ&GOm2V93xgYUaPfDputj^8_Q@B{S+M#r9z zW2Z3ec$DdXT<8y2FD?WR`UARx{(wk9f51+Aigy_K4@D0U00000NkvXXu0mjf32)a2 diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/16.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/16.png deleted file mode 100644 index 9c054691a6849ccadc637a0232f3de29abbe5929..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1033 zcmV+k1or!hP)@I z!qE#?y?G{{yZga3Gon;ZOv2)NOMwgF+96k2b;1 zZV5t1!=S1qDE!DnCi?>Nxea)`wn$gLke-ETJO-cFI1f}34-##O>w`#3wM(Et0EGom z?Zbthen=NHW%H{7G@~)Y;Qaa=IMGn|MH3V~(0W3@KA+23khy$y0A_e|+X|@DZD$+- z@YnE)cXoo208}g<1c2?sTM&zMg1VN~7Xh`Yk8A@ZHr{_+1_&rnT>3)4XTQG*fEj@M z*b&RPTk0c0JYb|arXmI+dcyF4W_rL2<2=L)fw({Q01EjH$gWx$5oD|PLo6UKk?nEu z868s(c)5C++B@RrxM~`@DHaPXAkqE40x*q^jF>5SIW?>UoIKtNMV|l^?xnY{9e@Yg z5?yq|pCCIks{^$7-~miSd@+@!A)b7>o`I_=fDhA?ooW$$4v8|=4k1a zOx>c#PGNev`4qrBQ(h6VB2@s;w0+>Ck%3?>IApuI6#Uz&P&=Yz6;Z{SP*DQ3n$ze< z6#g{q#WX1b&~+X@*CA|Gln8Xs!;lpVzlD3WU#lNk9WQ>B)3YX!0Kgk|V)P%S@IV3p8u!q!R02R($63dJKs*6x^ao@hb{M8cf1uGH zsHFd+KQRC3F1RJW4s_HXaML)|@-l@m>{Ra$xNe+-fJCCs`vab>UZMl(53pFM-~NDK zs^9*A=OSrXwLdUEL8_oTH#}vi|NekyUP$PVVn_V}*CB}UzwQqp1T97nX*&JprH6+Kw6QfXE-XLQFfFzg) z${xQ^1W|W)I2ii5XeiEMH9V07(}%o=%l3Cc*$)(8Kr{`)p$P1#+t#}b3L_Y9X@#BL zGK5;fpsHmk{w_c!`w9yAO?bb)L{`3-o`+~W24B~i2vidb5^awggGhU=pFmLuAY`7@+LeEhru5Ky4B{Ed80ufGL=I{@EfC)_g^ zAxs~Vh!_bo0-6;8FU(npRUP8t%p)ikHX*xa?}#8>y&s|pc}0IcExjOP5&h% z+#1*1hHgqsNDRNP094~+W7ZbDni@3#&YbFil1~5%b8pz!jvxZ(5{jSO5*0eqUB_7p<84QVX^K1#X`y^O-DnRr7N67p?Gqu>Punj80j zAf^a`&jeEnw+kYBCrPD~Oc|wC>BF^xNm491B>PUr`g% z{|&Vevu-_su>3}K3T7z1Naadjy%?=}FB$G1WHX;rWdzVzSfOYp)Bonstk8tmAf5m;`vWo%5r(SSA87Ums>#*$XIH+>$ zK_X!d57ZwxjImTJD_kAus6XJExd37M)b0D#boe_(P-uY&H~@|2JPXMp_?DLKY$RV7@fXbw|QOtfyt@I z B#UTIy diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/18.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/18.png deleted file mode 100644 index 6a7b5a75d821ed3674ccff8e3c563f3961e88fc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1029 zcmV+g1p51lP)z0Jbf zvcvvl>Fd_^|9fA**M59K&~>&Wss%O!ki2vYre+>0;G=^4&fCRTFn051INY6qv9)oXdTm&kK2Z^@EjX|Wf+9^=b0fh+= z?ZM@qK1dfcWye z!CU}DG#>c>zi*z2!Sf-15{`Bh*pS~;u1QaMPeJ9`3`_BMy1>iPz!aZ|4gy};P z5hGznKw}Z`!kmX#(IFm9J%&Pl1G1~uiU`u#`yrZ;n5NH)FUgohz{}ao)ZP(i6X8asph}Vk-69D#0c|}Bv zQ~^NK_JGeu3WBxZpzY#P@Sm$f?TC_9L=|g7MG25;4FoJRmZ{Tuy(Mt`8uA8_6un1A{JoM`a&I0LP@Kj74qmvBxZ z!G;Iw4;;o=s^w*&4s_HXaLwEXVfs|>54dj5LE!DE@BV;ivzO>d`UCcatiS$%U#j2! zfalVeVb%V?*o0mM-M`~0L;d#$Jd1+)QgYNEa2-N7KX88lAxJSgIbO4QP5ptfi6>;= zDGYx+%Bp`{=nvR0E(LG&2TV2k12Q%G1C_K7W_KMnA)T-d00000NkvXXu0mjf398RS diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/2.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/2.png deleted file mode 100644 index f6b5880efec011c4a930de4e5228a93886290771..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 968 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 O0000>P)#>Nf;dXP9`BplS}FmYZ; z2uCkm_2!xQFJL^FaPg#z2eSxK)PM)X5Fi>8Kx5*@+(}GK2%{Jhp8dV{wePjBv&dRK z^h=hu+iQQH->>hrUw%>0bh0x^BAXG&oxcGSQ}+}IP*MKi<-#)@?ZTjo{D4gBwN$^PNKEmDNxY>g#@Vf z;9^f76skqX___cs7|aMbw>Ar2G|+v?1jP!p9oNqV@QCe*3+)1CnM zb9kjQ888+A6`KzNz}DeSNF_3!v-P42XS49`!xBJ5f$HKH`W?IfCIGGg+@_9XXa-)h z-4BEjhfYMSgi!%vRse%HGIN>kPdtE1c^zg}tQ8Su*`perIj_&(R?G=`S^GSyKBfD# z0zqWYtD`e*?why3&+M7_35n$w0I)m);)v2Nh_~ zcF_;sf|;pl9iUaKz&S#|Pc3ViW zG4WiNA*+HP5Exv4{sRq)5cp0oqzG@r#O_h*baHv!YE|DpK5%fEBkJnumcNdf=046+ z>y^vjrpI1kdb#lw!2HGOh?q$g00`Lwel{`?^nwGni%Y?^X9@2eQ8Gv5>IoGkLfssp zf1>c#unSYStk%Fyn25EnUrBdFPPuwQ27;F@31XTBh31u(EJLwhip z{xpWodQIf@!Yl>pGyc_vV6<^Tx$12Pah3{%)22>SzW z3i|_gh5Z3uVSj)r><@(f0cn2#mX`dDoXytn4~&j!F6iz}Um5=Y{(x^)$m)+`PyK<0 zLukSG-5)>*YDUNJ960!a`U9h559zU27=Ap;@;?pe57;j*1rPfJhQj`UOksb(O}_#5 W#>Nf;dXQwqNI0m`VdA`y z5RP8B>diCpU%+@U;o?ab4`dOdr~wa%AxJbvKw{#?+(}GK2%{Jh>ifO+wePjBv&h>Tb~7LXqI4G%zNah2{<03^Y2 zpzMl?B8a-PEx@qv?E&rQ@ah}Rff0vp<2CEIsO$m-I1o*Pcq{?i>Xx~cL16{s$D3e# zrv|a(aZuG7RDPAAIP(I^rFD3_vOqJRFHA!+or13`Tm{_3gCtwh`c9&y-X%~WfIIYMO-Lm&p0o9$OBb^6{?iA5hyvBcZ}dC%{7nFy0k};a%g_wG zX1gBqT3(lYa0K%uG$|04<~fRi9%ok>I#6Ii?~0_;R%fH}U`ojCl@B;#a>+gS{K@kGi1Vf6j8YXs6P^Xj2>sG7!?&*Pp%N$W>cb9xQ zW}5pvORZNff0rJ6h3VnWrvTXy|SxCs-n_VqjIj>sujPsl(}HEJ}$%nE~>Ce;x7KPL5$O1H`8 z6bxS?P=bO5uS701WP9V5tOv&W`heS~CYg*dLIA*kPE$ z{y^9ta8uYHuq*5j@Cy3_OkscEaP|k*CLa3^0NJd2e;|n9b^8M&qr?T>zwIl-;qMRl zR)wtoD)!VLXgCB3zVH43LQpe0cJIK!57ZwR8P%tzR~Y_$l;wXK&>yhhTnZlc2MmS% f0hz-7fSdjTlu{^@A8qWK00000NkvXXu0mjf;8oP; diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/9.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/9.png deleted file mode 100644 index 8ac5c2493414a3ac7136ef6e12dd1226511e0401..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1007 zcmV0) zgrgU(dh<;D7cd@7xOmdWgK2~)YQO_x2oQ}4pfRzf_9P}Igt8ctF7sx0c4u~H%Oc%| z!+xaM*>-l{@4bC9`*>j>I@=l7LYpDTT)qPnQ;!sApu+sg+l5y!di$P+<3@o90Fg*D zg+MWojlaJkKcA~7481RjI+TBw6bnGHZ15m<7$!TEYz{Y<8Kmd^BZE*{T%%(OfFzg) z%C6`rf~Y&&0u1}!ZqPo5SN})`3_J7~tysT>Wfv&GfoK{;BQe-kx6Gpq3L_Y8YKHBd z3PhTspsE!p{V76z<~0-x>+oT9fqFiln}&EY0pC`+2)Ky{iMJ;8L9DgbB~T)Of&)}l zxZ2YPxpLkyza~Hn8Z!*et<8cL4Rl{KK`{btr}gWLg@OfHDAopGhBvmXggV!L-V*?S z4X

1;zrPV)-BdY#rZ(L@edmTQ9nDF%6%-ECGZRC@=n?-?8U!0^kb3W8y@Ldf?UD zgFqN|Xd+@Hj0k9E1kiXrJ(ub6#1kkL*I{PGS`lI9J*wWG^ZNd4#Z1WSug9=gDzjA*9QY)dJ8!NtdCY zQCKz-&vhBHDEI+^#`XOls8NK#cY+~BSOu}&leE&wWc5<3{PFprjms*cuAXlB>zHZo z>ntt3GTHm|+$&5kH=hESzc?!*R-_65npOoq8yN`Jf&;djOTo!*%GZu4Sw-Yp6DmrG zR&yHt6NQh4-I!L(Y6aYcj#y*;PI^V;ls00W@s$ z&~8ko{pPT&C(_@N47aCfcIRJwGfDFVA!xZ!f zg8qP;g8qPAL4SZ(&>vt5`UBGb04y!}Z#|tp!uPx#1ZP1_K>z@;j|==^1poj5El^BUMUx2$vQAF_002@N8Y~bHkvKSeH#aU25KbN* zBMl8V7Z*7e7BCJDED{m`002JY&AI>p01tFhPE-H?|NsC0|NsC0|NsC00My)=p#T5? z32;bRa{vGi!vFvd!vV){sAK>D1CU8XK~z{r)mH72+aL^-_!i{+{*T+0jctTO(rN#+ zJD179ij9x}iMGGQ|0nRUpC-6}ukhq&MgHOvZ_Zknk^AZbPs=9(4Dh$sAbxsi$5_js zeV}Q@F!W&yx|s3pk0}H2r}@ZV0wJaxFk%WhMK!kA`2Zh-#|UP^kpu=9~a zh?YMELLw50fkG7Kl4cjcJHbkVjxgQ)CBRh}28oN1Pfc$|DZ=?EDqQQ^2KEvYKR# zWlFOEDNju_hswKIfLcMI2>9fJh)5B5VLBtGiaD8IIsrm4?Z1a=Ib`8op% z69e-21+Ou9N?(! z8_wx1@Y7nxZ{yS2&l>^dcLB=}G59n5a{|Ai_jSeR+6Y(!E3g;u4+_UAHU>rj1GETh zg@ds5dlt|SnaLYEkDKsZb3-jYd2npugMdDD%jF7?({pJAd@P|_NeiYLN;I{9G)E7C zy4O}AfUy>aV%v|=*yB3ZgzDAD58$>YZ5~D!24g(Bfg66{O@Fe(b4(~eWZDlSP~m^Y z)4zzL_6{(y?Z@aY9wa;$+9dRe3EDeUSd3-C3ZQDC_=(fw_y*4HG_2kimQAojZC@4+ zz*zg7R$N;NXzzm-D!hMG5hZ<55vJD1ictX0N8MssWnpj7B68g)6r(^8O7LP;SWtQ3 zA}SZE!;Mh@InrIs9ohwS2%1OO^a+KD0b*2~2C7d?P<2ft%FB&W01C(gLfwZCphc^M zEcW3JBY;gbiero?G_-Hko>MCT?`-LufRHaaEdbc z)n3(8`@VQAys>B3mRl|7d8@n=S57Hd&m_Nf+9!rBtlXjR%H|o~l6{f(SRv?hx%G}& zznD5wT9lkw=iKgMX7IVGqLy%e`7sNIbHO}wZ)8pjSEx7?=RPr5vXH4tsEm8oxm#Bq z|6YyP+i!Ve*-XC5D+LPd34f*vF`5}`W_?^Ur}Wa7g9q=ewfz1m@9kdZz4eSP WZ*}VKZT<*~BL+`bKbLh*2~7Z;&c$i~ diff --git a/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/meta.json deleted file mode 100644 index b8872a3c05..0000000000 --- a/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/meta.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/master/icons/mob/clothing/suit.dmi", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - }, - { - "name": "equipped-OUTERCLOTHING", - "directions": 4 - } - ] -} \ No newline at end of file diff --git a/Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/equipped-HELMET.png b/Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/equipped-HELMET.png deleted file mode 100644 index cb4bdb9ebcfdda6c247d4264aced1db955f727c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 837 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 oCIaI6^>}Fr0^$hs!TuMhwN8u$nFY510000t<8 diff --git a/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/icon.png b/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/icon.png deleted file mode 100644 index 27bcc7fe797ad0a8e1836d25472817a9b48951db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 687 zcmV;g0#N;lP)Px%Xh}ptR9Hvtmd{HYK@`W|8V~~ki4x74qz8pu)QHf72hj#86xtq2MX(Cq^(c7g zAK*ps)?*Js(UW*kp-@VQUUDc+lUk5KduY^X1Cd1`R*YM|122qg+8uFSDP%7DV|Mp_ z-gn;j?Pzq*L%ZkrR0pUlP*>ppQeeKh+3bx(==sjhHSqPG9`kEHPp=K*MoB3@jNnmS zr(hr;de`9gcJ-bgOaRLDH zV>r45>}?rD>Gec9xP2c~5{Bc^hU0;JqTA*~(^>_5yfT2>_LDZ6LU28~$ys)_v=}KhIOB VFj;3>fB*mh07*qoM6N<$f&j(eEtLQO diff --git a/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/meta.json deleted file mode 100644 index ffa6adf456..0000000000 --- a/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/meta.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "whateverusername0", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - }, - { - "name": "equipped-OUTERCLOTHING", - "directions": 4 - } - ] -} \ No newline at end of file diff --git a/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/equipped-HELMET.png b/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/equipped-HELMET.png deleted file mode 100644 index 27a959da55eeec133fec773894c0b0cbc3a909b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 864 zcmV-m1E2hfP)Px&7)eAyRCr$Pn!ipPK@i3V8Veiyl#4jWkt;yKh|UE`lz>E$Dx$(~Ku3`_5=t5I z0(pTvfSm@VN`VAKNk~I!6#hs-gybL>9p)~wkho}8+S4hOckNw`aQiZR;wjJ5Tx2&*R|rlY?y`6&+u4J3jiKO5zVP7 zYtE}CrPueh0rc8{`g;t3xDK;0qJ`Zxim2~)4n383zfX?>gk(}bzl z=LAr9$h1CAfN8?i>vICAJ7iiPC%`me>h(DR)EzRdj}xFvID0>jHEi4rXSEKhzg7a| zen3{xZ5UeH5g25DWdQPi9tXxp-W20^8xZ~KhDfiI1Q0P?8S zQm(1;GyJ@=BDO;wHk%wifE8ycJwfyaNm6s%x#p12-gGvQ9@_C5k7+wPi5a<9xu#o^cJPwWz z?kc<~mEh~af%1-kG5~BSA-ue~flj9b?`CG;MHB%pPMH30kX_Kg9fe}C2&_< zz5(>&8s&Ew0A+V0X8U8?!gJ5?2Zj> qoB$h?Dfi$6Pt=`Ln7SY zPBY|dau9H}FJlu@;x*j3InvoZeTwphO0Ir~HMZ^S7fwkxbn2>hN@?jBx-5v@ta9W1 z5f9_b=l_`N&%SGFP$0?tyqWO^gLDFO3bVUnN_}VUrXHL2D;1nfmYZ^COuK5hr<>`u zisIQzc|o)Nmj(Cue&)V##dO|xYYnw6j90#`wm4?WW6dDC&t`sMl+Hi)1z%QInewKs z>yDn4#IB$`hc%aZ-}}=ma(5qSeqO3@+vfPQj}9TBw?1!?4p|`JG%fXK?Md0=in;6A zCNP9NzPIgZp`CjEy~~ps>%LA*WH39x26kDq^bdwjO74oH3#KFkea_(N>gTe~DWQo0 E0HeTn7ytkO diff --git a/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/meta.json deleted file mode 100644 index a40d761820..0000000000 --- a/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/meta.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "whateverusername0", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - }, - { - "name": "equipped-HELMET", - "directions": 4 - } - ] -} \ No newline at end of file diff --git a/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-icon.png b/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-icon.png deleted file mode 100644 index 81da793fff738d75134f749841fe195548e1c65f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4706 zcmeHLeNYo;8eh}{M5NX8#8VuXq<00%CcB%>*Ou^^AkhXQM2bhfve}QYnlF-tgq#Jf z+T-x!L~pcbEr)k%dmcJ)+9|YnN`V%8MO&+_XVof{vGof_ZFSUX@7;vYxz5}$Q~x6~ zo85ih=lQ+Q@Ao|KJ9+a5bMC_FQFEgp2%2up(dUCZEMAe};M*Gr+y%F;fTc*tr+l#6 zw zsapSmK6mCG;=qy5a&mUmM@-)IO0&22pLaH2pLwTPw=0vI+D84ou)6y6r+mrhB^Fus z1A%Itw*35y)f*p4KibU2{$pObI#U(j+TPS`y>afKG`YAf>&7}`spjwX+pfHb_hP@?ux!CMsha@PuyuS^mf4t< zHAD!|*!5DKE~mphYkgk9Uc)mn6;GCRo9B1Rl6s5w7fqSA_rS;d*B)Q|LxVAEYj1Bt z%j~#E%NofR&9$qSk|JMDgHOcd*2?mGUo!4CY{_EkDdtS}?wDNpMTJQaW zueKgv8YcZ<(h76Dw(3^eeO>V(jjsRK>wD7&HaMy`hOJrNaHi?T0R4sYb?AHBrA627 zuQZk1+;%s6;fA=dwO<_TDqpzm-s*qPFS&2r5q-SwktMS^vh6RQsQcIKE^cu3yk7h; z?u{qXw{vf;nj9B5sqa@G{`Q{V*YrE*4;T590}*AbJp1gMR~&?(n58^uup-lQB<*s@ zD8^-FWqyYnbRGnyr1{+xy^DNhuxFmQM&2kAGbP-nSBt<4OoaOScFd-vkC}QyQ z6}Tid3QqAb9GS1r9)tjII!UP@xJkL(=kv*Y3Yp7elVe(~R*vFw97li$;;nQFlpk?= zr6NQKL(h6?5APOumlGB-DXXho&`Bg<93INg;Wn9u;ho+=6@VUcKjoHVGF0wx$VW$b z1w#b@8BFL$BfJ){PUZQm*H!MJSwjWu6r`ge7IJi6W2Rha zG?|AZL z7^_4GoW&4IO%Na(T7}_Ol~SwF2B8?5%yxMk6euU}plqz%?X(34M8U}nvr#9(W$1{- zY^MYc1b{WbI~kYHJ2GV99c+O>iF#scT%k~?luAN{VM?WD#Ho<=ctJ0Us2D0!gd`CQ zLjpQLT1xCx01%7-Y{)DRO9?KI#pSZ=Bw|vq7&$y_0tbW=Yxw0hJ(w3r05U@ zFIB;Y$_21OLv$(Sw6S3S7;M-fJO3liz*w~yOJFL5!3od|1i>L%meU|gOrus1Y7Ar4 zAy&iaUKc0$C=Z)q13Ch&K!XOgf)fTyl^9fQ*xy&migf^zAt>m%pkydPD%BGCS?G3-wSF^eptOyV;xyEK}V%%e)XY?dReZ2JPH6 z2N)v+qsb8Qb>uWis%qFh-v|CGK#ckfOHs#-!s6NvG1Whg4|`+D|r?LFSy;oZrvu z$4>0Pw@7=!CzrheHD%0xWll+7w($1hm_Egv+%-$?UJV~et?ufMbPmYesRQ3Mo@&z; z?ELO1*N`vkNtC5NuU>KU7|))KKawAXOV^*mrN`pKXPsI+lm7ZxcU{G$OZbdMN$qD3 z?FmSi$jXl;1meTquZoZV9>*`%IHeNH)qux*=e8j+2K%@>U3d0S?B3SHGuG_PTYMp} z;BUXVnw~;_+i>Q?g05X~=2}Ofp~{)6+|Sniop#UZnsdLL+dX61{+hh#>brB3 p3=66aXO0%VJ@tY7g_c`S--7xNP>G5Q)*hfGWHjXJ4`!CU{9gq`r!W8j diff --git a/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-left.png b/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-left.png deleted file mode 100644 index 373bbc8d43198dadc5eae920ba2f664ef0ff62f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5060 zcmeHLc~leU7N6ik1jQm&t!PhmdY_(Oj=4$a_BTO$sui& z7J_V-4eEH8N>1;#JN-vH9AO%JN_ zyx-+ArB0Kyp(TIA!-~*pnmNsjqCeSl@SDj~{%8viT-Q@XYhP%H@dNY9&%NwXI@Dtx z8;7xZ$deb_;?~wR56naAvBz1##pSKkqxi(8XBSR;Dx|{{LiKHEP&xo!heD#Msw`%Fx`#Mz6gC++R*ELHEjCHO z&ky9Baf!_4Mm5e+{t&jLFSq4pMXF2dHAi;!$mr^VL4^rdjwAMK5OzdXEgvu)Z+UE1|FYag#3D_8+JvZHh*T+b=ERz{y`JAzHh>)&W?R4y1i zZ0@a}A3PdzC%4k!Z0{kLp7}pHk2h6t&aFL?P_IUhqb;m8*K6$axtPQp&DaU*p@9`9pl6sd`;}oBQUzL3!HU>D}Dj9c~UkIla}E zy7Oz}>w4;Ji(^u{Wt0B9gzXUIGLZrW7N;7A;wA$ZBTO2SYcrTZH8LY`2F01d>NW~4D2VzhcN5bYQW(u!LsGfkO{90n89 zn38FK7zX>CH~tySD%D$fqqTztzz5HUnRx;(pJy=eI$K!juoM8&;m}`NSYtq+@}fwq zDcORPVJW1M_UH^j;BW2C$rimm90KQ&deQ(?t>9Eamm|ZKD)n0nh5{XBFx#yF*B%XOHh6% zeG@@x)Bc+_(>@%(_M}HpR&airUDV!F(PUElQ~RTyvNsclV{Z!-!`nl!VksnH_Y+{X zZ{f2rqmBggqoZKo$f>_5224v5LNOsiWTYHqP#^+jfnfw9!+o_213Ut*K!Mu1;&^qW%Dbz6>MWAU1E37y%MgY#87lEb#Zs8};lmT+GL5eUClNx* z=ObcFC_v;wtp*WmB_fSPM*5O6tgE~Kdw3lc@g_V!9y1W%B-4-gAL(va=rlnAE$uQe znZcmvy`A(Od@(ci1AaQn>;s+vpg$h+Ui^Nf>myz7#lU+hf2^*LbiEe?@1^{)y8dr; zIlX&VB#qz)UMhGKY+3rnBJdX3Q!_SP0qulFL!-^5>Hr|=WsaC^g&=1)=3+r>^9KQ; zBdt`0Io@SELmuMD9qI89 z=T^g-1RdJPlRb9IDcGmMEOU1`w!TV(Wd%91F6QJ}Dw?dlAl605=SBLC>htC~NI*%x zV+I>#)11-OuU9=D{I_K%o39^*)=>A%!7t7@HB20ksP7r-nV!JC?WWTE>{MFs?3Qkx z>X5!cF#6T;^*aXD;MT0iBYXb-^yTy5ni0?P4|yGp*K-%-#|kobRIc>?<@Q;@=yeuI zEDc9@On11VJi4H8E^$*L4%j6pA^2`wRng+gZ)DXjf)QR^lA g)*Mh)*AE7sUEoq>d6WtMHGv>ySfpZm$jteF0)snPSpWb4 diff --git a/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-right.png b/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-right.png deleted file mode 100644 index 9f879f0840bd52e39c1ddd7f7264ec0e0a296f35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5044 zcmeHKc~leU79VgSMFcnc6!E+HAST&PwFD%O3$r-Dn=+M`%`R-YabeP6;Np7xw~Jn#9RIVYLiS9Qv<-gcN((^@>I(0x?cG5vx1S2NZf`u6N*on&DEO39A=yEX4fdO=yw z`bqJl1KrPWoETev?rhMO;3Yx!z&p!x#XTbzX71dSQj`DvmfD|od=!-KRHVOAyor(K6IUF*+8{57ef;Zkc~qIh5xnMD(TeP!R6Xzr3v=Bsd@o z6=);Xo7bNFRC8_e9ya5-e7RSB=}T2)HLGUbxsbba*Qb^nPOV98_21$Xu}rgjb`O7l_uGLd zra!Y;t7hq6UuVud^2nTR+MMx~b_WD`PhvoWB`d~DNP~_;PzE*4vFVJU^B^cR+-4-m zblL)|=`=IkMfhvqq^h_o$*F-1fCaB2VbP}i7;bESkHVFXG(G~)> z=`!?YiA~CO;Yz@}Q_N+5B$s8OSVWcygJP?es693?xHIT(3eN z&I{pFVni%Jd5BO<2oXGl5+Na|Ml8aG1VIRe-Js-pvxU%;v=a(|a~OaVq7f4UjKmO< zM~e|5j;j%Zr^XNzqeT>oX)s))=>{>u#DK0OGP+0QgrWeHkP^@WF-0H(991Ji3P9l^ zHHCzLwFpBsIA4UjpeRxjV=(CmP)zbVp(-9O5nvKb z1lW_H-SiCWU-+PBD=gg%W1=SN4wcFLJQYX*{1Yj`3QA=P{o5vtJ;M2v~W2%i@4G&qLR1lmqg z7rNP?u~-QcJvI&S2)F_b>f#Cx?kLqKZ%1RDNjvKRC__*&;-pM0!9pZLjLm)j>4`Li z5GP3?~#|_YdW6SLk+v z0$SQ-;A92|J-6$m@8AoR;(dNP+U$M0fZ-2L-ihB2bbX-fofvp0<@cIRj72G0wtK(y3P!%)=8e^4jJ+b_>f4PuSoW zGS^sOkG&kQ{;~GHSKcPC8R%fI-nIE99P8o3I`0CHtbf@&FS81Ipl-h&$x`f9r{@cE zpyZn(kADpJvWMT!LI%G6va)H&EYspIY-d;ngKURKjGV50u=v2r!EOT!R`xyZaj|f4 z#puTDx(15XQqLu-u;zQo4Tb$LPOIJ>J2IxBs?P0~s9audUt1a3;AhmU>_X?}Mxrrg}$%!G2vz}I0el{HSWIJGcWbID!6s)vr(qB*O z{VWh&8&E2jGv5CkuzT$|b3{$f0P1A(%iyw~E4KD8TVLXy>anutUmBF+m?Ob)=Tbc9 zuunHF?bp&AZ+7^vsjBqFQy07}RIh8X^?DvZFg(8D$EhC1Jx6Rid>BP{N%QG-Qa?xQI*G235>nlssx5Xq@EIsi3FE7R%g`TPc@JlDA zc^zuopek)KznWhke=FvGpq}=5UL&~qdg#IRex*x-LZ5AEM%=X(F{X;8n&EKMnFT>l zpZq=GyOjF2yeyxsoBZ&kN?}0xX6>E0-RBn-q*Rm+xHN9(h~#+8v@wP8bKk8Bhy|9D z3JyigNx~90q%aOQY?4#9nLfL4-jh8R&XyD=GUCMD8_F-IPOMZtgSd^a$UEja(A@oj zrETl92VVp={_weW1W>!`(ehBSZ_$(B0hh`r`flB3KmOfhkm4=xS8hIKtW~(LN({Oa Nl1C@Vc0|r7{0|<1MUnsj diff --git a/Resources/Textures/Goobstation/Changeling/shields.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/shields.rsi/meta.json deleted file mode 100644 index a7ffc69df7..0000000000 --- a/Resources/Textures/Goobstation/Changeling/shields.rsi/meta.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from https://github.com/ParadiseSS13/Paradise/blob/master/icons/obj/weapons/shield.dmi", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "ling-icon" - }, - { - "name": "ling-inhand-left", - "directions": 4 - }, - { - "name": "ling-inhand-right", - "directions": 4 - } - ] -} diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/Changeling.png b/Resources/Textures/Interface/Misc/job_icons.rsi/Changeling.png deleted file mode 100644 index 95be43946010d365dcb8820ee628f21dde3327f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|vOHZJLo|Yu zQxXyqCQO|8lYi00gOk<$B_tgu{rU5UXH(*83Ay!L%&RRgs2zC_@U`#Zjni(>)dj Z#&YN1*B(#N1KP^q>FVdQ&MBdZ0RT)%J!t>{ diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json b/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json index e845b98728..745cc43b84 100644 --- a/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json +++ b/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json @@ -185,9 +185,6 @@ }, { "name": "InitialInfected" - }, - { - "name": "Changeling" } ] }