diff --git a/Content.Client/GameObjects/Components/Trigger/TimerTriggerVisualizer2D.cs b/Content.Client/GameObjects/Components/Trigger/TimerTriggerVisualizer2D.cs new file mode 100644 index 0000000000..bc439ae632 --- /dev/null +++ b/Content.Client/GameObjects/Components/Trigger/TimerTriggerVisualizer2D.cs @@ -0,0 +1,76 @@ +using System; +using Content.Shared.GameObjects.Components.Triggers; +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Client.GameObjects.Components.Animations; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Utility; +using YamlDotNet.RepresentationModel; + +namespace Content.Client.GameObjects.Components.Doors +{ + public class TimerTriggerVisualizer2D : AppearanceVisualizer + { + private const string AnimationKey = "priming_animation"; + + private Animation PrimingAnimation; + + public override void LoadData(YamlMappingNode node) + { + base.LoadData(node); + + var countdownSound = node.GetNode("countdown_sound").AsString(); + + + PrimingAnimation = new Animation { Length = TimeSpan.MaxValue }; + { + var flick = new AnimationTrackSpriteFlick(); + PrimingAnimation.AnimationTracks.Add(flick); + flick.LayerKey = TriggerVisualLayers.Base; + flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("primed", 0f)); + + var sound = new AnimationTrackPlaySound(); + PrimingAnimation.AnimationTracks.Add(sound); + sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(countdownSound, 0)); + } + } + + public override void InitializeEntity(IEntity entity) + { + if (!entity.HasComponent()) + { + entity.AddComponent(); + } + } + + public override void OnChangeData(AppearanceComponent component) + { + var sprite = component.Owner.GetComponent(); + var animPlayer = component.Owner.GetComponent(); + if (!component.TryGetData(TriggerVisuals.VisualState, out TriggerVisualState state)) + { + state = TriggerVisualState.Unprimed; + } + + switch (state) + { + case TriggerVisualState.Primed: + if (!animPlayer.HasRunningAnimation(AnimationKey)) + { + animPlayer.Play(PrimingAnimation, AnimationKey); + } + break; + case TriggerVisualState.Unprimed: + sprite.LayerSetState(0, "icon"); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + public enum TriggerVisualLayers + { + Base + } +} diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index bef98d239a..25ef19f08a 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -1,4 +1,4 @@ - + net472 diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index 46cf2c90f3..4b8c401f2d 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -55,6 +55,8 @@ using Content.Server.Interfaces.GameObjects.Components.Movement; using Content.Server.GameObjects.Components.Research; using Content.Shared.GameObjects.Components.Research; using Robust.Shared.Interfaces.Log; +using Content.Server.GameObjects.Components.Explosive; +using Content.Server.GameObjects.Components.Triggers; namespace Content.Server { @@ -178,6 +180,9 @@ namespace Content.Server factory.Register(); + factory.Register(); + factory.Register(); + factory.Register(); factory.Register(); diff --git a/Content.Server/GameObjects/Components/Damage/DamageThreshold.cs b/Content.Server/GameObjects/Components/Damage/DamageThreshold.cs index 46e8a94a78..8e77872692 100644 --- a/Content.Server/GameObjects/Components/Damage/DamageThreshold.cs +++ b/Content.Server/GameObjects/Components/Damage/DamageThreshold.cs @@ -50,11 +50,13 @@ namespace Content.Server.GameObjects { public DamageThreshold DamageThreshold { get; } public bool Passed { get; } + public int ExcessDamage { get; } - public DamageThresholdPassedEventArgs(DamageThreshold threshold, bool passed) + public DamageThresholdPassedEventArgs(DamageThreshold threshold, bool passed, int excess) { DamageThreshold = threshold; Passed = passed; + ExcessDamage = excess; } } } diff --git a/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs b/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs index c7b997959c..52b3c663c2 100644 --- a/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs +++ b/Content.Server/GameObjects/Components/Damage/DamageableComponent.cs @@ -123,7 +123,13 @@ namespace Content.Server.GameObjects var value = threshold.Value; if (((value * changeSign) > (oldValue * changeSign)) && ((value * changeSign) <= (_currentDamage[damageType] * changeSign))) { - var args = new DamageThresholdPassedEventArgs(threshold, (changeSign > 0)); + var excessDamage = change - value; + var typeOfDamage = damageType; + if (change - value < 0) + { + excessDamage = 0; + } + var args = new DamageThresholdPassedEventArgs(threshold, (changeSign > 0), excessDamage); DamageThresholdPassed?.Invoke(this, args); } } diff --git a/Content.Server/GameObjects/Components/Damage/DestructibleComponent.cs b/Content.Server/GameObjects/Components/Damage/DestructibleComponent.cs index 0fd5cdd388..62c9eb8ea8 100644 --- a/Content.Server/GameObjects/Components/Damage/DestructibleComponent.cs +++ b/Content.Server/GameObjects/Components/Damage/DestructibleComponent.cs @@ -1,21 +1,27 @@ using System; using System.Collections.Generic; using Robust.Shared.GameObjects; -using Robust.Shared.Log; -using Robust.Shared.Utility; -using YamlDotNet.RepresentationModel; -using Content.Server.Interfaces; -using Content.Shared.GameObjects; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Maths; +using Content.Server.Interfaces; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects; namespace Content.Server.GameObjects.Components.Destructible { /// /// Deletes the entity once a certain damage threshold has been reached. /// - public class DestructibleComponent : Component, IOnDamageBehavior + public class DestructibleComponent : Component, IOnDamageBehavior, IDestroyAct, IExAct { + #pragma warning disable 649 + [Dependency] private readonly IEntitySystemManager _entitySystemManager; + #pragma warning restore 649 + /// public override string Name => "Destructible"; @@ -28,16 +34,24 @@ namespace Content.Server.GameObjects.Components.Destructible public DamageType damageType = DamageType.Total; public int damageValue = 0; - public string spawnOnDestroy = "SteelSheet"; + public string spawnOnDestroy = ""; public bool destroyed = false; + ActSystem _actSystem; + public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); serializer.DataField(ref damageValue, "thresholdvalue", 100); serializer.DataField(ref damageType, "thresholdtype", DamageType.Total); - serializer.DataField(ref spawnOnDestroy, "spawnondestroy", "SteelSheet"); + serializer.DataField(ref spawnOnDestroy, "spawnondestroy", ""); + } + + public override void Initialize() + { + base.Initialize(); + _actSystem = _entitySystemManager.GetEntitySystem(); } /// @@ -49,13 +63,40 @@ namespace Content.Server.GameObjects.Components.Destructible /// void IOnDamageBehavior.OnDamageThresholdPassed(object obj, DamageThresholdPassedEventArgs e) - { + { if (e.Passed && e.DamageThreshold == Threshold && destroyed == false) { destroyed = true; - var wreck = Owner.EntityManager.SpawnEntity(spawnOnDestroy); - wreck.Transform.GridPosition = Owner.Transform.GridPosition; - Owner.EntityManager.DeleteEntity(Owner); + _actSystem.HandleDestruction(Owner, true); + } + + } + + void IExAct.OnExplosion(ExplosionEventArgs eventArgs) + { + var prob = new Random(); + switch (eventArgs.Severity) + { + case ExplosionSeverity.Destruction: + _actSystem.HandleDestruction(Owner, false); + break; + case ExplosionSeverity.Heavy: + _actSystem.HandleDestruction(Owner, true); + break; + case ExplosionSeverity.Light: + if (RandomExtensions.Prob(prob, 40)) + _actSystem.HandleDestruction(Owner, true); + break; + } + + } + + + void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs) + { + if (!string.IsNullOrWhiteSpace(spawnOnDestroy) && eventArgs.IsSpawnWreck) + { + Owner.EntityManager.TrySpawnEntityAt(spawnOnDestroy, Owner.Transform.GridPosition, out var wreck); } } } diff --git a/Content.Server/GameObjects/Components/Explosion/ExplosiveComponent.cs b/Content.Server/GameObjects/Components/Explosion/ExplosiveComponent.cs new file mode 100644 index 0000000000..29dc5ded3a --- /dev/null +++ b/Content.Server/GameObjects/Components/Explosion/ExplosiveComponent.cs @@ -0,0 +1,147 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using Robust.Server.Interfaces.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.GameObjects.EntitySystemMessages; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.Timing; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects; +using Content.Shared.Maps; + +namespace Content.Server.GameObjects.Components.Explosive +{ + public class ExplosiveComponent : Component, ITimerTrigger, IDestroyAct + { +#pragma warning disable 649 + [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager; + [Dependency] private readonly IMapManager _mapManager; + [Dependency] private readonly IServerEntityManager _serverEntityManager; + [Dependency] private readonly IEntitySystemManager _entitySystemManager; +#pragma warning restore 649 + + public override string Name => "Explosive"; + + public int DevastationRange = 0; + public int HeavyImpactRange = 0; + public int LightImpactRange = 0; + public int FlashRange = 0; + + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref DevastationRange, "devastationRange", 0); + serializer.DataField(ref HeavyImpactRange, "heavyImpactRange", 0); + serializer.DataField(ref LightImpactRange, "lightImpactRange", 0); + serializer.DataField(ref FlashRange, "flashRange", 0); + } + + private bool Explosion() + { + var maxRange = MathHelper.Max(DevastationRange, HeavyImpactRange, LightImpactRange, 0f); + //Entity damage calculation + var entitiesAll = _serverEntityManager.GetEntitiesInRange(Owner.Transform.GridPosition, maxRange).ToList(); + + foreach (var entity in entitiesAll) + { + Owner.Delete(); + if (entity == Owner) + continue; + if (!entity.Transform.IsMapTransform) + continue; + var distanceFromEntity = (int)entity.Transform.GridPosition.Distance(_mapManager, Owner.Transform.GridPosition); + var exAct = _entitySystemManager.GetEntitySystem(); + var severity = ExplosionSeverity.Destruction; + if (distanceFromEntity < DevastationRange) + { + severity = ExplosionSeverity.Destruction; + } + else if (distanceFromEntity < HeavyImpactRange) + { + severity = ExplosionSeverity.Heavy; + } + else if (distanceFromEntity < LightImpactRange) + { + severity = ExplosionSeverity.Light; + } + else + { + continue; + } + exAct.HandleExplosion(Owner, entity, severity); + } + + //Tile damage calculation mockup + //TODO: make it into some sort of actual damage component or whatever the boys think is appropriate + var mapGrid = _mapManager.GetGrid(Owner.Transform.GridPosition.GridID); + var circle = new Circle(Owner.Transform.GridPosition.Position, maxRange); + var tiles = mapGrid.GetTilesIntersecting(circle); + foreach (var tile in tiles) + { + var tileLoc = mapGrid.GridTileToLocal(tile.GridIndices); + var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId]; + var distanceFromTile = (int)tileLoc.Distance(_mapManager, Owner.Transform.GridPosition); + if (!string.IsNullOrWhiteSpace(tileDef.SubFloor)) { + if (distanceFromTile < DevastationRange) + mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager["space"].TileId)); + if (distanceFromTile < HeavyImpactRange) + { + if (new Random().Prob(80)) + { + mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager[tileDef.SubFloor].TileId)); + } + else + { + mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager["space"].TileId)); + } + } + if (distanceFromTile < LightImpactRange) + { + if (new Random().Prob(50)) + { + mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager[tileDef.SubFloor].TileId)); + } + } + } + } + + //Effects and sounds + var time = IoCManager.Resolve().CurTime; + var message = new EffectSystemMessage + { + EffectSprite = "Effects/explosion.png", + Born = time, + DeathTime = time + TimeSpan.FromSeconds(5), + Size = new Vector2(FlashRange / 2, FlashRange / 2), + Coordinates = Owner.Transform.GridPosition, + //Rotated from east facing + Rotation = 0f, + ColorDelta = new Vector4(0, 0, 0, -1500f), + Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), 0.5f), + Shaded = false + }; + _entitySystemManager.GetEntitySystem().CreateParticle(message); + _entitySystemManager.GetEntitySystem().Play("/Audio/effects/explosion.ogg", Owner); + return true; + } + + bool ITimerTrigger.Trigger(TimerTriggerEventArgs eventArgs) + { + return Explosion(); + } + + void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs) + { + Explosion(); + } + } +} diff --git a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs index 33886c053d..f9a4b57b2c 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs @@ -1,4 +1,5 @@ -using Content.Server.GameObjects.EntitySystems; +using System.Linq; +using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects.Components.Storage; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.Container; @@ -26,7 +27,7 @@ namespace Content.Server.GameObjects /// /// Storage component for containing entities within this one, matches a UI on the client which shows stored entities /// - public class ServerStorageComponent : SharedStorageComponent, IAttackBy, IUse, IActivate, IStorageComponent + public class ServerStorageComponent : SharedStorageComponent, IAttackBy, IUse, IActivate, IStorageComponent, IDestroyAct { #pragma warning disable 649 [Dependency] private readonly IMapManager _mapManager; @@ -340,5 +341,14 @@ namespace Content.Server.GameObjects _storageInitialCalculated = true; } + + void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs) + { + var storedEntities = storage.ContainedEntities.ToList(); + foreach (var entity in storedEntities) + { + Remove(entity); + } + } } } diff --git a/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs b/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs index bb68d25f2f..63a7740c97 100644 --- a/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs @@ -12,7 +12,7 @@ using Robust.Shared.Serialization; namespace Content.Server.GameObjects { - public class SpeciesComponent : SharedSpeciesComponent, IActionBlocker, IOnDamageBehavior + public class SpeciesComponent : SharedSpeciesComponent, IActionBlocker, IOnDamageBehavior, IExAct { /// /// Damagestates are reached by reaching a certain damage threshold, they will block actions after being reached @@ -115,6 +115,28 @@ namespace Content.Server.GameObjects Owner.RaiseEvent(new MobDamageStateChangedMessage(this)); } + + void IExAct.OnExplosion(ExplosionEventArgs eventArgs) + { + var burnDamage = 0; + var bruteDamage = 0; + switch(eventArgs.Severity) + { + case ExplosionSeverity.Destruction: + bruteDamage += 250; + burnDamage += 250; + break; + case ExplosionSeverity.Heavy: + bruteDamage += 60; + burnDamage += 60; + break; + case ExplosionSeverity.Light: + bruteDamage += 30; + break; + } + Owner.GetComponent().TakeDamage(Shared.GameObjects.DamageType.Brute, bruteDamage); + Owner.GetComponent().TakeDamage(Shared.GameObjects.DamageType.Heat, burnDamage); + } } /// diff --git a/Content.Server/GameObjects/Components/Trigger/TimerTrigger/OnUseTimerTriggerComponent.cs b/Content.Server/GameObjects/Components/Trigger/TimerTrigger/OnUseTimerTriggerComponent.cs new file mode 100644 index 0000000000..b41bd35bbd --- /dev/null +++ b/Content.Server/GameObjects/Components/Trigger/TimerTrigger/OnUseTimerTriggerComponent.cs @@ -0,0 +1,44 @@ +using System; +using Robust.Server.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Serialization; +using Robust.Shared.GameObjects; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects.Components.Triggers; + +namespace Content.Server.GameObjects.Components.Triggers +{ + public class OnUseTimerTriggerComponent : Component, IUse + { + #pragma warning disable 649 + [Dependency] private readonly IEntitySystemManager _entitySystemManager; +#pragma warning restore 649 + + public override string Name => "OnUseTimerTrigger"; + + private float _delay = 0f; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _delay, "delay", 0f); + } + + public override void Initialize() + { + base.Initialize(); + } + + bool IUse.UseEntity(UseEntityEventArgs eventArgs) + { + var triggerSystem = _entitySystemManager.GetEntitySystem(); + if (Owner.TryGetComponent(out var appearance)) { + appearance.SetData(TriggerVisuals.VisualState, TriggerVisualState.Primed); + } + triggerSystem.HandleTimerTrigger(TimeSpan.FromSeconds(_delay), eventArgs.User, Owner); + return true; + } + } +} \ No newline at end of file diff --git a/Content.Server/GameObjects/EntitySystems/ActSystem.cs b/Content.Server/GameObjects/EntitySystems/ActSystem.cs new file mode 100644 index 0000000000..ae83a8788f --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/ActSystem.cs @@ -0,0 +1,83 @@ +using System; +using System.Linq; +using JetBrains.Annotations; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Content.Shared.GameObjects; + +namespace Content.Server.GameObjects.EntitySystems +{ + /// + /// This interface gives components behavior on getting destoyed. + /// + public interface IDestroyAct + { + /// + /// Called when object is destroyed + /// + void OnDestroy(DestructionEventArgs eventArgs); + } + + public class DestructionEventArgs : EventArgs + { + public IEntity Owner { get; set; } + public bool IsSpawnWreck { get; set; } + } + + public interface IExAct + { + /// + /// Called when explosion reaches the entity + /// + void OnExplosion(ExplosionEventArgs eventArgs); + } + + public class ExplosionEventArgs : EventArgs + { + public IEntity Source { get; set; } + public IEntity Target { get; set; } + public ExplosionSeverity Severity { get; set; } + } + + [UsedImplicitly] + public sealed class ActSystem : EntitySystem + { + public void HandleDestruction(IEntity owner, bool isWreck) + { + var eventArgs = new DestructionEventArgs + { + Owner = owner, + IsSpawnWreck = isWreck + }; + var destroyActs = owner.GetAllComponents().ToList(); + + foreach (var destroyAct in destroyActs) + { + destroyAct.OnDestroy(eventArgs); + } + owner.Delete(); + } + + public void HandleExplosion(IEntity source, IEntity target, ExplosionSeverity severity) + { + var eventArgs = new ExplosionEventArgs + { + Source = source, + Target = target, + Severity = severity + }; + var exActs = target.GetAllComponents().ToList(); + + foreach (var exAct in exActs) + { + exAct.OnExplosion(eventArgs); + } + } + } + public enum ExplosionSeverity + { + Destruction, + Heavy, + Light, + } +} \ No newline at end of file diff --git a/Content.Server/GameObjects/EntitySystems/TriggerSystem.cs b/Content.Server/GameObjects/EntitySystems/TriggerSystem.cs new file mode 100644 index 0000000000..79b306c7d1 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/TriggerSystem.cs @@ -0,0 +1,53 @@ +using System; +using System.Linq; +using JetBrains.Annotations; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Timers; + +namespace Content.Server.GameObjects.EntitySystems +{ + /// + /// This interface gives components behavior when being "triggered" by timer or other conditions + /// + public interface ITimerTrigger + { + /// + /// Called when one object is triggering some event + /// + bool Trigger(TimerTriggerEventArgs eventArgs); + } + + public class TimerTriggerEventArgs : EventArgs + { + public IEntity User { get; set; } + public IEntity Source { get; set; } + } + + [UsedImplicitly] + public sealed class TriggerSystem : EntitySystem + { + public void HandleTimerTrigger(TimeSpan delay, IEntity user, IEntity trigger) + { + + Timer.Spawn(delay, () => + { + var timerTriggerEventArgs = new TimerTriggerEventArgs + { + User = user, + Source = trigger + }; + var timerTriggers = trigger.GetAllComponents().ToList(); + + foreach (var timerTrigger in timerTriggers) + { + if (timerTrigger.Trigger(timerTriggerEventArgs)) + { + // If an IOnTimerTrigger returns a status completion we finish our trigger + return; + } + } + }); + } + } +} \ No newline at end of file diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index 2266a22812..2ee3a7b57a 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -18,4 +18,7 @@ + + + diff --git a/Content.Shared/GameObjects/Components/Trigger/TriggerVisuals.cs b/Content.Shared/GameObjects/Components/Trigger/TriggerVisuals.cs new file mode 100644 index 0000000000..a8d90904fe --- /dev/null +++ b/Content.Shared/GameObjects/Components/Trigger/TriggerVisuals.cs @@ -0,0 +1,20 @@ +using System; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Triggers +{ + [NetSerializable] + [Serializable] + public enum TriggerVisuals + { + VisualState, + } + + [NetSerializable] + [Serializable] + public enum TriggerVisualState + { + Primed, + Unprimed, + } +} diff --git a/Content.Shared/Maps/ContentTileDefinition.cs b/Content.Shared/Maps/ContentTileDefinition.cs index 3b939f0f9e..82115f4961 100644 --- a/Content.Shared/Maps/ContentTileDefinition.cs +++ b/Content.Shared/Maps/ContentTileDefinition.cs @@ -17,6 +17,7 @@ namespace Content.Shared.Maps public string DisplayName { get; private set; } public string SpriteName { get; private set; } public bool IsSubFloor { get; private set; } + public string SubFloor { get; private set; } public bool CanCrowbar { get; private set; } public string FootstepSounds { get; private set; } public float Friction { get; set; } @@ -36,6 +37,10 @@ namespace Content.Shared.Maps { IsSubFloor = node.AsBool(); } + if (mapping.TryGetNode("subfloor", out var another_node)) + { + SubFloor = another_node.AsString(); + } if (mapping.TryGetNode("can_crowbar", out node)) { diff --git a/Resources/Audio/effects/countdown.ogg b/Resources/Audio/effects/countdown.ogg new file mode 100644 index 0000000000..bb19300ba9 Binary files /dev/null and b/Resources/Audio/effects/countdown.ogg differ diff --git a/Resources/Audio/effects/explosion.ogg b/Resources/Audio/effects/explosion.ogg new file mode 100644 index 0000000000..14df2c88cc Binary files /dev/null and b/Resources/Audio/effects/explosion.ogg differ diff --git a/Resources/Prototypes/Entities/Container.yml b/Resources/Prototypes/Entities/Container.yml index b477d569fd..483b6f568a 100644 --- a/Resources/Prototypes/Entities/Container.yml +++ b/Resources/Prototypes/Entities/Container.yml @@ -18,6 +18,43 @@ - type: Collidable - type: Storage Capacity: 60 + + - type: Damageable + - type: Destructible + thresholdvalue: 100 + + placement: + snap: + - Wall + +- type: entity + id: weldtank + name: Fueltank + description: A storage tank containing welding fuel. + components: + - type: Sprite + texture: Buildings/weldtank.png + + - type: Icon + texture: Buildings/weldtank.png + + - type: Clickable + - type: BoundingBox + aabb: "-0.5,-0.25,0.5,0.25" + - type: Collidable + mask: 3 + IsScrapingFloor: true + - type: Physics + mass: 15 + Anchored: false + - type: Damageable + - type: Destructible + thresholdvalue: 10 + - type: Explosive + devastationRange: 1 + heavyImpactRange: 2 + lightImpactRange: 4 + flashRange: 5 placement: snap: diff --git a/Resources/Prototypes/Entities/Explosives.yml b/Resources/Prototypes/Entities/Explosives.yml new file mode 100644 index 0000000000..e7b379e5b9 --- /dev/null +++ b/Resources/Prototypes/Entities/Explosives.yml @@ -0,0 +1,74 @@ +- type: entity + name: Explosive Grenade + description: Grenade that creates small devastating explosion + parent: BaseItem + id: ExGrenade + components: + - type: Sprite + sprite: Objects/grenade.rsi + layers: + - state: icon + map: ["enum.TriggerVisualLayers.Base"] + + - type: Icon + sprite: Objects/grenade.rsi + state: icon + + - type: Item + Size: 5 + + - type: OnUseTimerTrigger + delay: 5 + + - type: Explosive + devastationRange: 1 + heavyImpactRange: 3 + lightImpactRange: 5 + flashRange: 7 + + - type: Damageable + - type: Destructible + thresholdvalue: 10 + + - type: Appearance + visuals: + - type: TimerTriggerVisualizer2D + countdown_sound: /Audio/effects/countdown.ogg + +- type: entity + name: Syndicate Minibomb + description: A syndicate manufactured explosive used to sow destruction and chaos. + parent: BaseItem + id: SyndieMiniBomb + components: + - type: Sprite + sprite: Objects/syndgrenade.rsi + layers: + - state: icon + map: ["enum.TriggerVisualLayers.Base"] + + - type: Icon + sprite: Objects/syndgrenade.rsi + state: icon + + - type: Item + Size: 5 + + - type: OnUseTimerTrigger + delay: 5 + + - type: Explosive + devastationRange: 3 + heavyImpactRange: 5 + lightImpactRange: 7 + flashRange: 10 + + - type: Damageable + - type: Destructible + thresholdvalue: 10 + + - type: Appearance + visuals: + - type: TimerTriggerVisualizer2D + countdown_sound: /Audio/effects/countdown.ogg + diff --git a/Resources/Prototypes/Entities/Items.yml b/Resources/Prototypes/Entities/Items.yml index 9a8e9a04a2..38386054b6 100644 --- a/Resources/Prototypes/Entities/Items.yml +++ b/Resources/Prototypes/Entities/Items.yml @@ -136,3 +136,21 @@ - type: Sound - type: EmitSoundOnUse sound: /Audio/items/bikehorn.ogg + +- type: entity + name: Table Parts + parent: BaseItem + id: TableParts + description: Parts of a table. + components: + - type: Sprite + sprite: Objects/table_parts.rsi + state: icon + + - type: Icon + sprite: Objects/table_parts.rsi + state: icon + + - type: Item + Size: 25 + sprite: Objects/table_parts.rsi diff --git a/Resources/Prototypes/Entities/closet.yml b/Resources/Prototypes/Entities/closet.yml index 4ef562074a..dd68a6f66a 100644 --- a/Resources/Prototypes/Entities/closet.yml +++ b/Resources/Prototypes/Entities/closet.yml @@ -25,6 +25,9 @@ Anchored: false - type: EntityStorage - type: PlaceableSurface + - type: Damageable + - type: Destructible + thresholdvalue: 100 - type: Appearance visuals: - type: StorageVisualizer2D diff --git a/Resources/Prototypes/Entities/table.yml b/Resources/Prototypes/Entities/table.yml index 9ffc7ce2d7..f39c868f29 100644 --- a/Resources/Prototypes/Entities/table.yml +++ b/Resources/Prototypes/Entities/table.yml @@ -21,3 +21,8 @@ - type: IconSmooth key: tables base: solid_ + + - type: Damageable + - type: Destructible + thresholdvalue: 50 + spawnondestroy: TableParts \ No newline at end of file diff --git a/Resources/Prototypes/Tiles/floors.yml b/Resources/Prototypes/Tiles/floors.yml index 8a608a8071..01c70a6317 100644 --- a/Resources/Prototypes/Tiles/floors.yml +++ b/Resources/Prototypes/Tiles/floors.yml @@ -6,6 +6,7 @@ can_crowbar: true footstep_sounds: footstep_floor friction: 0.35 + subfloor: plating - type: tile name: floor_white @@ -15,6 +16,7 @@ can_crowbar: true footstep_sounds: footstep_floor friction: 0.1 + subfloor: underplating - type: tile name: floor_techmaint @@ -24,3 +26,4 @@ can_crowbar: true footstep_sounds: footstep_floor friction: 0.5 + subfloor: underplating diff --git a/Resources/Prototypes/Tiles/plating.yml b/Resources/Prototypes/Tiles/plating.yml index e2083c8f20..d97886d00e 100644 --- a/Resources/Prototypes/Tiles/plating.yml +++ b/Resources/Prototypes/Tiles/plating.yml @@ -5,6 +5,7 @@ is_subfloor: true footstep_sounds: footstep_plating friction: 0.5 + subfloor: space - type: tile name: underplating diff --git a/Resources/Textures/Buildings/weldtank.png b/Resources/Textures/Buildings/weldtank.png new file mode 100644 index 0000000000..6daab863e3 Binary files /dev/null and b/Resources/Textures/Buildings/weldtank.png differ diff --git a/Resources/Textures/Effects/explosion.png b/Resources/Textures/Effects/explosion.png new file mode 100755 index 0000000000..1175b7f53b Binary files /dev/null and b/Resources/Textures/Effects/explosion.png differ diff --git a/Resources/Textures/Effects/explosion.rsi/explosion.png b/Resources/Textures/Effects/explosion.rsi/explosion.png new file mode 100644 index 0000000000..5dbdd0b854 Binary files /dev/null and b/Resources/Textures/Effects/explosion.rsi/explosion.png differ diff --git a/Resources/Textures/Effects/explosion.rsi/meta.json b/Resources/Textures/Effects/explosion.rsi/meta.json new file mode 100644 index 0000000000..8179c1e152 --- /dev/null +++ b/Resources/Textures/Effects/explosion.rsi/meta.json @@ -0,0 +1,28 @@ +{ + "version": 1, + "size": { + "x": 96, + "y": 96 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/icons/effects/96x96.dmi at commit 96d69b71a3a0549b4b668c0a095306580bd157c0", + "states": [ + { + "name": "explosion", + "select": [], + "flags": {}, + "directions": 1, + "delays": [ + [ + 0.2, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/Objects/grenade.rsi/icon.png b/Resources/Textures/Objects/grenade.rsi/icon.png new file mode 100644 index 0000000000..9c6bc8b164 Binary files /dev/null and b/Resources/Textures/Objects/grenade.rsi/icon.png differ diff --git a/Resources/Textures/Objects/grenade.rsi/meta.json b/Resources/Textures/Objects/grenade.rsi/meta.json new file mode 100644 index 0000000000..198dbe1347 --- /dev/null +++ b/Resources/Textures/Objects/grenade.rsi/meta.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/icons/obj/grenade.dmi at commit 8c96d52deed1eeea28a16334eea549369d7f9974", + "states": [ + { + "name": "icon", + "directions": 1, + "delays": [ + [1.0] + ] + }, + { + "name": "primed", + "directions": 1, + "delays": [ + [0.2, 0.2] + ] + } + ] +} diff --git a/Resources/Textures/Objects/grenade.rsi/primed.png b/Resources/Textures/Objects/grenade.rsi/primed.png new file mode 100644 index 0000000000..72df008d65 Binary files /dev/null and b/Resources/Textures/Objects/grenade.rsi/primed.png differ diff --git a/Resources/Textures/Objects/rack_parts.rsi/icon.png b/Resources/Textures/Objects/rack_parts.rsi/icon.png new file mode 100644 index 0000000000..74c36c2c24 Binary files /dev/null and b/Resources/Textures/Objects/rack_parts.rsi/icon.png differ diff --git a/Resources/Textures/Objects/rack_parts.rsi/inhand-left.png b/Resources/Textures/Objects/rack_parts.rsi/inhand-left.png new file mode 100644 index 0000000000..408bf24749 Binary files /dev/null and b/Resources/Textures/Objects/rack_parts.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/rack_parts.rsi/inhand-right.png b/Resources/Textures/Objects/rack_parts.rsi/inhand-right.png new file mode 100644 index 0000000000..874099622d Binary files /dev/null and b/Resources/Textures/Objects/rack_parts.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/rack_parts.rsi/meta.json b/Resources/Textures/Objects/rack_parts.rsi/meta.json new file mode 100644 index 0000000000..6bdaa41229 --- /dev/null +++ b/Resources/Textures/Objects/rack_parts.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13 at commit 125c975f1b3bf9826b37029e9ab5a5f89e975a7e", "states": [{"name": "icon", "directions": 1, "delays": [[1.0]]}, {"name": "inhand-left", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "inhand-right", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} \ No newline at end of file diff --git a/Resources/Textures/Objects/syndgrenade.rsi/icon.png b/Resources/Textures/Objects/syndgrenade.rsi/icon.png new file mode 100755 index 0000000000..38bac6d954 Binary files /dev/null and b/Resources/Textures/Objects/syndgrenade.rsi/icon.png differ diff --git a/Resources/Textures/Objects/syndgrenade.rsi/meta.json b/Resources/Textures/Objects/syndgrenade.rsi/meta.json new file mode 100644 index 0000000000..acb14eea1a --- /dev/null +++ b/Resources/Textures/Objects/syndgrenade.rsi/meta.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/icons/obj/grenade.dmi at commit 8c96d52deed1eeea28a16334eea549369d7f9974", + "states": [ + { + "name": "icon", + "directions": 1, + "delays": [ + [1.0] + ] + }, + { + "name": "primed", + "directions": 1, + "delays": [ + [0.2, 0.2, 0.2] + ] + } + ] +} diff --git a/Resources/Textures/Objects/syndgrenade.rsi/primed.png b/Resources/Textures/Objects/syndgrenade.rsi/primed.png new file mode 100644 index 0000000000..750a6b1c73 Binary files /dev/null and b/Resources/Textures/Objects/syndgrenade.rsi/primed.png differ diff --git a/Resources/Textures/Objects/table_parts.rsi/icon.png b/Resources/Textures/Objects/table_parts.rsi/icon.png new file mode 100644 index 0000000000..c83fdee7dd Binary files /dev/null and b/Resources/Textures/Objects/table_parts.rsi/icon.png differ diff --git a/Resources/Textures/Objects/table_parts.rsi/inhand-left.png b/Resources/Textures/Objects/table_parts.rsi/inhand-left.png new file mode 100644 index 0000000000..408bf24749 Binary files /dev/null and b/Resources/Textures/Objects/table_parts.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/table_parts.rsi/inhand-right.png b/Resources/Textures/Objects/table_parts.rsi/inhand-right.png new file mode 100644 index 0000000000..874099622d Binary files /dev/null and b/Resources/Textures/Objects/table_parts.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/table_parts.rsi/meta.json b/Resources/Textures/Objects/table_parts.rsi/meta.json new file mode 100644 index 0000000000..6bdaa41229 --- /dev/null +++ b/Resources/Textures/Objects/table_parts.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13 at commit 125c975f1b3bf9826b37029e9ab5a5f89e975a7e", "states": [{"name": "icon", "directions": 1, "delays": [[1.0]]}, {"name": "inhand-left", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "inhand-right", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} \ No newline at end of file