diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 4468574b25..c7ee8d6eca 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -162,6 +162,9 @@ namespace Content.Client "Tool", "TilePrying", "RandomToolColor", + "ConditionalSpawner", + "PottedPlantHide", + "SecureEntityStorage", }; foreach (var ignoreName in registerIgnore) diff --git a/Content.IntegrationTests/DummyGameTicker.cs b/Content.IntegrationTests/DummyGameTicker.cs index 33bc84ac24..1f944841ab 100644 --- a/Content.IntegrationTests/DummyGameTicker.cs +++ b/Content.IntegrationTests/DummyGameTicker.cs @@ -19,6 +19,12 @@ namespace Content.IntegrationTests remove { } } + public event Action OnRuleAdded + { + add{ } + remove { } + } + public void Initialize() { } @@ -64,6 +70,11 @@ namespace Content.IntegrationTests return new T(); } + public bool HasGameRule(Type type) + { + return false; + } + public void RemoveGameRule(GameRule rule) { } diff --git a/Content.Server/GameObjects/Components/Markers/ConditionalSpawnerComponent.cs b/Content.Server/GameObjects/Components/Markers/ConditionalSpawnerComponent.cs new file mode 100644 index 0000000000..a97f899776 --- /dev/null +++ b/Content.Server/GameObjects/Components/Markers/ConditionalSpawnerComponent.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using Content.Server.GameTicking; +using Content.Server.Interfaces.GameTicking; +using NFluidsynth; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.Interfaces.Reflection; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using SQLitePCL; +using Logger = Robust.Shared.Log.Logger; + +namespace Content.Server.GameObjects.Components.Markers +{ + [RegisterComponent] + public class ConditionalSpawnerComponent : Component, IMapInit + { + public override string Name => "ConditionalSpawner"; + +#pragma warning disable 649 + [Dependency] private IGameTicker _gameTicker; + [Dependency] private IReflectionManager _reflectionManager; + [Dependency] private IEntityManager _entityManager; + [Dependency] private IRobustRandom _robustRandom; +#pragma warning restore 649 + + [ViewVariables(VVAccess.ReadWrite)] + public List Prototypes { get; set; } = new List(); + + [ViewVariables(VVAccess.ReadWrite)] + private List _gameRules = new List(); + + [ViewVariables(VVAccess.ReadWrite)] + public float Chance { get; set; } = 1.0f; + + public IEnumerable GameRules + { + get + { + foreach (var rule in _gameRules) + { + yield return _reflectionManager.GetType(rule); + } + } + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + serializer.DataField(this, x => Prototypes, "prototypes", new List()); + serializer.DataField(this, x => Chance, "chance", 1.0f); + serializer.DataField(this, x => _gameRules, "gameRules", new List()); + } + + private void RuleAdded(GameRuleAddedEventArgs obj) + { + if(_gameRules.Contains(obj.GameRule.GetType().Name)) + Spawn(); + } + + private void TrySpawn() + { + if (_gameRules.Count == 0) + { + Spawn(); + return; + } + + foreach (var rule in GameRules) + { + if (!_gameTicker.HasGameRule(rule)) continue; + Spawn(); + return; + } + } + + private void Spawn() + { + if (Chance != 1.0f && !_robustRandom.Prob(Chance)) + return; + + if (Prototypes.Count == 0) + { + Logger.Warning($"Prototype list in ConditionalSpawnComponent is empty! Entity: {Owner}"); + return; + } + + _entityManager.SpawnEntity(_robustRandom.Pick(Prototypes), Owner.Transform.GridPosition); + } + + public void MapInit() + { + _gameTicker.OnRuleAdded += RuleAdded; + + TrySpawn(); + } + } +} diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index de97df2a5d..c65ce92ad4 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -98,6 +98,7 @@ namespace Content.Server.GameTicking } public event Action OnRunLevelChanged; + public event Action OnRuleAdded; private TimeSpan LobbyDuration => TimeSpan.FromSeconds(_configurationManager.GetCVar("game.lobbyduration")); @@ -327,9 +328,25 @@ namespace Content.Server.GameTicking _gameRules.Add(instance); instance.Added(); + OnRuleAdded?.Invoke(new GameRuleAddedEventArgs(instance)); + return instance; } + public bool HasGameRule(Type t) + { + if (t == null || !t.IsAssignableFrom(typeof(GameRule))) + return false; + + foreach (var rule in _gameRules) + { + if (rule.GetType().Equals(t)) + return true; + } + + return false; + } + public void RemoveGameRule(GameRule rule) { if (_gameRules.Contains(rule)) return; @@ -786,4 +803,14 @@ The current game mode is: [color=white]{0}[/color]. public GameRunLevel OldRunLevel { get; } public GameRunLevel NewRunLevel { get; } } + + public class GameRuleAddedEventArgs : EventArgs + { + public GameRule GameRule { get; } + + public GameRuleAddedEventArgs(GameRule rule) + { + GameRule = rule; + } + } } diff --git a/Content.Server/Interfaces/GameTicking/IGameTicker.cs b/Content.Server/Interfaces/GameTicking/IGameTicker.cs index 67bf2857d2..56e04a84ef 100644 --- a/Content.Server/Interfaces/GameTicking/IGameTicker.cs +++ b/Content.Server/Interfaces/GameTicking/IGameTicker.cs @@ -15,6 +15,7 @@ namespace Content.Server.Interfaces.GameTicking GameRunLevel RunLevel { get; } event Action OnRunLevelChanged; + event Action OnRuleAdded; void Initialize(); void Update(FrameEventArgs frameEventArgs); @@ -34,6 +35,7 @@ namespace Content.Server.Interfaces.GameTicking // GameRule system. T AddGameRule() where T : GameRule, new(); + bool HasGameRule(Type type); void RemoveGameRule(GameRule rule); IEnumerable ActiveGameRules { get; } diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index a25dcdb111..5bf91a5506 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -23219,4 +23219,186 @@ entities: pos: 0.5,9.5 rot: -1.5707963267948966 rad type: Transform +- uid: 2987 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: -20.5,1.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2988 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: -28.5,13.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2989 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: -29.5,10.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2990 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: -0.5,0.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2991 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: 6.5,8.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2992 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: 10.5,8.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2993 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: 15.5,8.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2994 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: 10.5,-13.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2995 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: 18.5,-24.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2996 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: 17.5,-20.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2997 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: 22.5,-4.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2998 + type: SuspicionRifleSpawner + components: + - parent: 0 + pos: 23.5,-0.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2999 + type: SuspicionRifleSpawner + components: + - parent: 0 + pos: -2.5,-4.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3000 + type: SuspicionRifleSpawner + components: + - parent: 0 + pos: -14.5,-0.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3001 + type: SuspicionRifleSpawner + components: + - parent: 0 + pos: -32.5,-8.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3002 + type: SuspicionPistolSpawner + components: + - parent: 0 + pos: -18.5,-11.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3003 + type: SuspicionPistolSpawner + components: + - parent: 0 + pos: -31.5,8.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3004 + type: SuspicionPistolSpawner + components: + - parent: 0 + pos: -20.5,14.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3005 + type: SuspicionRifleSpawner + components: + - parent: 0 + pos: -7.5,20.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3006 + type: SuspicionRifleSpawner + components: + - parent: 0 + pos: -7.5,21.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3007 + type: SuspicionPistolSpawner + components: + - parent: 0 + pos: 6.5,20.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3008 + type: SuspicionRifleSpawner + components: + - parent: 0 + pos: 0.5,28.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3009 + type: SuspicionRifleSpawner + components: + - parent: 0 + pos: 6.5,28.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3010 + type: SuspicionPistolSpawner + components: + - parent: 0 + pos: -13.5,24.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3011 + type: SuspicionMeleeSpawner + components: + - parent: 0 + pos: -4.5,20.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3012 + type: SuspicionRifleSpawner + components: + - parent: 0 + pos: 5.5,16.5 + rot: -1.5707963267948966 rad + type: Transform ... diff --git a/Resources/Prototypes/Entities/Markers/gamemode_conditional_spawners.yml b/Resources/Prototypes/Entities/Markers/gamemode_conditional_spawners.yml new file mode 100644 index 0000000000..9b5f6757b8 --- /dev/null +++ b/Resources/Prototypes/Entities/Markers/gamemode_conditional_spawners.yml @@ -0,0 +1,99 @@ +- type: entity + name: base conditional spawner + id: BaseConditionalSpawner + abstract: true + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: cross_blue + - type: Icon + sprite: Objects/markers.rsi + state: cross_blue + - type: Marker + - type: Clickable + - type: InteractionOutline + - type: Collidable + - type: ConditionalSpawner + placement: + mode: AlignTileAny + +- type: entity + name: Suspicion Rifle Spawner + id: SuspicionRifleSpawner + parent: BaseConditionalSpawner + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: spawner_rifle + - type: Icon + sprite: Objects/markers.rsi + state: spawner_rifle + - type: ConditionalSpawner + prototypes: + - RifleAk + - RifleBlackAk + - RifleCarbine + - RifleDallas + - RifleIhHeavy + - RifleSolEot + - RifleSolPara + - RifleSts + chance: 0.75 + gameRules: + - RuleSuspicion + +- type: entity + name: Suspicion Pistol Spawner + id: SuspicionPistolSpawner + parent: BaseConditionalSpawner + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: spawner_pistol + - type: Icon + sprite: Objects/markers.rsi + state: spawner_pistol + - type: ConditionalSpawner + prototypes: + - PistolClarissa + - PistolDeagle + - PistolDeckard + - PistolGiskard + - PistolGyro + - PistolLamia + - PistolMakarov + - PistolMk58 + - PistolOlivawCivil + chance: 0.75 + gameRules: + - RuleSuspicion + +- type: entity + name: Suspicion Melee Spawner + id: SuspicionMeleeSpawner + parent: BaseConditionalSpawner + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: spawner_melee + - type: Icon + sprite: Objects/markers.rsi + state: spawner_melee + - type: ConditionalSpawner + prototypes: + - ButchCleaver + - Pickaxe + - Spear + - ToolboxEmergency + - CrowbarRed + chance: 0.75 + gameRules: + - RuleSuspicion diff --git a/Resources/Textures/Objects/markers.rsi/meta.json b/Resources/Textures/Objects/markers.rsi/meta.json index 1a47da626f..02df455363 100644 --- a/Resources/Textures/Objects/markers.rsi/meta.json +++ b/Resources/Textures/Objects/markers.rsi/meta.json @@ -1 +1 @@ -{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi", "states": [{"name": "cross_blue", "directions": 1, "delays": [[1.0]]}, {"name": "cross_green", "directions": 1, "delays": [[1.0]]}, {"name": "cross_red", "directions": 1, "delays": [[1.0]]}, {"name": "AI", "directions": 1, "delays": [[1.0]]}, {"name": "Assistant", "directions": 1, "delays": [[1.0]]}, {"name": "Atmospheric Technician", "directions": 1, "delays": [[1.0]]}, {"name": "Bartender", "directions": 1, "delays": [[1.0]]}, {"name": "Botanist", "directions": 1, "delays": [[1.0]]}, {"name": "Captain", "directions": 1, "delays": [[1.0]]}, {"name": "Cargo Technician", "directions": 1, "delays": [[1.0]]}, {"name": "Chaplain", "directions": 1, "delays": [[1.0]]}, {"name": "Chemist", "directions": 1, "delays": [[1.0]]}, {"name": "Chief Engineer", "directions": 1, "delays": [[1.0]]}, {"name": "Chief Medical Officer", "directions": 1, "delays": [[1.0]]}, {"name": "Clown", "directions": 1, "delays": [[1.0]]}, {"name": "Cook", "directions": 1, "delays": [[1.0]]}, {"name": "Curator", "directions": 1, "delays": [[1.0]]}, {"name": "Cyborg", "directions": 1, "delays": [[1.0]]}, {"name": "Detective", "directions": 1, "delays": [[1.0]]}, {"name": "Geneticist", "directions": 1, "delays": [[1.0]]}, {"name": "Head of Personnel", "directions": 1, "delays": [[1.0]]}, {"name": "Head of Security", "directions": 1, "delays": [[1.0]]}, {"name": "Janitor", "directions": 1, "delays": [[1.0]]}, {"name": "Lawyer", "directions": 1, "delays": [[1.0]]}, {"name": "Medical Doctor", "directions": 1, "delays": [[1.0]]}, {"name": "Mime", "directions": 1, "delays": [[1.0]]}, {"name": "Paramedic", "directions": 1, "delays": [[1.0]]}, {"name": "Prisoner", "directions": 1, "delays": [[1.0]]}, {"name": "Psychologist", "directions": 1, "delays": [[1.0]]}, {"name": "Quartermaster", "directions": 1, "delays": [[1.0]]}, {"name": "Research Director", "directions": 1, "delays": [[1.0]]}, {"name": "Roboticist", "directions": 1, "delays": [[1.0]]}, {"name": "Scientist", "directions": 1, "delays": [[1.0]]}, {"name": "Security Officer", "directions": 1, "delays": [[1.0]]}, {"name": "Shaft Miner", "directions": 1, "delays": [[1.0]]}, {"name": "Station Engineer", "directions": 1, "delays": [[1.0]]}, {"name": "Virologist", "directions": 1, "delays": [[1.0]]}, {"name": "Warden", "directions": 1, "delays": [[1.0]]}, {"name": "cross_pink", "directions": 1, "delays": [[1.0]]}, {"name": "observer_start", "directions": 1, "delays": [[1.0]]}]} \ No newline at end of file +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi", "states": [{"name": "cross_blue", "directions": 1, "delays": [[1.0]]}, {"name": "cross_green", "directions": 1, "delays": [[1.0]]}, {"name": "cross_red", "directions": 1, "delays": [[1.0]]}, {"name": "AI", "directions": 1, "delays": [[1.0]]}, {"name": "Assistant", "directions": 1, "delays": [[1.0]]}, {"name": "Atmospheric Technician", "directions": 1, "delays": [[1.0]]}, {"name": "Bartender", "directions": 1, "delays": [[1.0]]}, {"name": "Botanist", "directions": 1, "delays": [[1.0]]}, {"name": "Captain", "directions": 1, "delays": [[1.0]]}, {"name": "Cargo Technician", "directions": 1, "delays": [[1.0]]}, {"name": "Chaplain", "directions": 1, "delays": [[1.0]]}, {"name": "Chemist", "directions": 1, "delays": [[1.0]]}, {"name": "Chief Engineer", "directions": 1, "delays": [[1.0]]}, {"name": "Chief Medical Officer", "directions": 1, "delays": [[1.0]]}, {"name": "Clown", "directions": 1, "delays": [[1.0]]}, {"name": "Cook", "directions": 1, "delays": [[1.0]]}, {"name": "Curator", "directions": 1, "delays": [[1.0]]}, {"name": "Cyborg", "directions": 1, "delays": [[1.0]]}, {"name": "Detective", "directions": 1, "delays": [[1.0]]}, {"name": "Geneticist", "directions": 1, "delays": [[1.0]]}, {"name": "Head of Personnel", "directions": 1, "delays": [[1.0]]}, {"name": "Head of Security", "directions": 1, "delays": [[1.0]]}, {"name": "Janitor", "directions": 1, "delays": [[1.0]]}, {"name": "Lawyer", "directions": 1, "delays": [[1.0]]}, {"name": "Medical Doctor", "directions": 1, "delays": [[1.0]]}, {"name": "Mime", "directions": 1, "delays": [[1.0]]}, {"name": "Paramedic", "directions": 1, "delays": [[1.0]]}, {"name": "Prisoner", "directions": 1, "delays": [[1.0]]}, {"name": "Psychologist", "directions": 1, "delays": [[1.0]]}, {"name": "Quartermaster", "directions": 1, "delays": [[1.0]]}, {"name": "Research Director", "directions": 1, "delays": [[1.0]]}, {"name": "Roboticist", "directions": 1, "delays": [[1.0]]}, {"name": "Scientist", "directions": 1, "delays": [[1.0]]}, {"name": "Security Officer", "directions": 1, "delays": [[1.0]]}, {"name": "Shaft Miner", "directions": 1, "delays": [[1.0]]}, {"name": "Station Engineer", "directions": 1, "delays": [[1.0]]}, {"name": "Virologist", "directions": 1, "delays": [[1.0]]}, {"name": "Warden", "directions": 1, "delays": [[1.0]]}, {"name": "cross_pink", "directions": 1, "delays": [[1.0]]}, {"name": "observer_start", "directions": 1, "delays": [[1.0]]}, {"name": "spawner_melee", "directions": 1, "delays": [[1.0]]}, {"name": "spawner_rifle", "directions": 1, "delays": [[1.0]]}, {"name": "spawner_pistol", "directions": 1, "delays": [[1.0]]}]} diff --git a/Resources/Textures/Objects/markers.rsi/spawner_melee.png b/Resources/Textures/Objects/markers.rsi/spawner_melee.png new file mode 100644 index 0000000000..bc3fd59227 Binary files /dev/null and b/Resources/Textures/Objects/markers.rsi/spawner_melee.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_pistol.png b/Resources/Textures/Objects/markers.rsi/spawner_pistol.png new file mode 100644 index 0000000000..6f9b871e53 Binary files /dev/null and b/Resources/Textures/Objects/markers.rsi/spawner_pistol.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_rifle.png b/Resources/Textures/Objects/markers.rsi/spawner_rifle.png new file mode 100644 index 0000000000..0b1c339595 Binary files /dev/null and b/Resources/Textures/Objects/markers.rsi/spawner_rifle.png differ