From e1df008bce91b98c578a5baf22c525781272f5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= <6766154+Zumorica@users.noreply.github.com> Date: Fri, 5 Jun 2020 19:42:43 +0200 Subject: [PATCH] Add conditional spawning component (#1069) * Add conditional spawning component * Remove null checks * Remove leftover return * Properly spawn items when game rule gets added * Fix duplicate uids in saltern * GameRules returns IEnumerable using yield. --- Content.Client/EntryPoint.cs | 3 + Content.IntegrationTests/DummyGameTicker.cs | 11 ++ .../Markers/ConditionalSpawnerComponent.cs | 104 ++++++++++ Content.Server/GameTicking/GameTicker.cs | 27 +++ .../Interfaces/GameTicking/IGameTicker.cs | 2 + Resources/Maps/saltern.yml | 182 ++++++++++++++++++ .../Markers/gamemode_conditional_spawners.yml | 99 ++++++++++ .../Textures/Objects/markers.rsi/meta.json | 2 +- .../Objects/markers.rsi/spawner_melee.png | Bin 0 -> 3793 bytes .../Objects/markers.rsi/spawner_pistol.png | Bin 0 -> 3954 bytes .../Objects/markers.rsi/spawner_rifle.png | Bin 0 -> 4474 bytes 11 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 Content.Server/GameObjects/Components/Markers/ConditionalSpawnerComponent.cs create mode 100644 Resources/Prototypes/Entities/Markers/gamemode_conditional_spawners.yml create mode 100644 Resources/Textures/Objects/markers.rsi/spawner_melee.png create mode 100644 Resources/Textures/Objects/markers.rsi/spawner_pistol.png create mode 100644 Resources/Textures/Objects/markers.rsi/spawner_rifle.png 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 0000000000000000000000000000000000000000..bc3fd59227be303f3bdc5ba879378d298a19db0c GIT binary patch literal 3793 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 M07*qoM6N<$g3lv-X8-^I literal 0 HcmV?d00001