using Content.Server.Explosion.EntitySystems;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Explosion.Components
{
/// <summary>
/// What we fill our prototype with if we want to pre-spawn with grenades.
/// </summary>
- [DataField("fillPrototype", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
- public string? FillPrototype;
+ [DataField("fillPrototype")]
+ public EntProtoId? FillPrototype;
/// <summary>
/// If we have a pre-fill how many more can we spawn.
public int MaxGrenades = 3;
/// <summary>
- /// How long until our grenades are shot out and armed.
+ /// Maximum delay in seconds between individual grenade triggers
/// </summary>
- [ViewVariables(VVAccess.ReadWrite)] [DataField("delay")]
- public float Delay = 1;
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("grenadeTriggerIntervalMax")]
+ public float GrenadeTriggerIntervalMax = 0f;
/// <summary>
- /// Max distance grenades can be thrown.
+ /// Minimum delay in seconds between individual grenade triggers
/// </summary>
- [ViewVariables(VVAccess.ReadWrite)] [DataField("distance")]
- public float ThrowDistance = 50;
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("grenadeTriggerIntervalMin")]
+ public float GrenadeTriggerIntervalMin = 0f;
+
+ /// <summary>
+ /// Minimum delay in seconds before any grenades start to be triggered.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("baseTriggerDelay")]
+ public float BaseTriggerDelay = 1.0f;
+
+ /// <summary>
+ /// Decides if grenades trigger after getting launched
+ /// </summary>
+ [DataField("triggerGrenades")]
+ public bool TriggerGrenades = true;
+
+ /// <summary>
+ /// Does the cluster grenade shoot or throw
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("grenadeType")]
+ public Enum GrenadeType = Components.GrenadeType.Throw;
+
+ /// <summary>
+ /// The speed at which grenades get thrown
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("velocity")]
+ public float Velocity = 5;
+
+ /// <summary>
+ /// Should the spread be random
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("randomSpread")]
+ public bool RandomSpread = false;
+
+ /// <summary>
+ /// Should the angle be random
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("randomAngle")]
+ public bool RandomAngle = false;
+
+ /// <summary>
+ /// Static distance grenades will be thrown to.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("distance")]
+ public float Distance = 1f;
+
+ /// <summary>
+ /// Max distance grenades should randomly be thrown to.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("maxSpreadDistance")]
+ public float MaxSpreadDistance = 2.5f;
+
+ /// <summary>
+ /// Minimal distance grenades should randomly be thrown to.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("minSpreadDistance")]
+ public float MinSpreadDistance = 0f;
/// <summary>
/// This is the end.
/// </summary>
public bool CountDown;
}
+
+ public enum GrenadeType
+ {
+ Throw,
+ Shoot
+ }
+
}
using Content.Shared.Throwing;
using Robust.Shared.Containers;
using Robust.Shared.Random;
+using Content.Server.Weapons.Ranged.Systems;
+using System.Numerics;
+using Robust.Server.Containers;
+using Robust.Server.GameObjects;
namespace Content.Server.Explosion.EntitySystems;
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
- [Dependency] private readonly TriggerSystem _trigger = default!;
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly GunSystem _gun = default!;
+ [Dependency] private readonly TransformSystem _transformSystem = default!;
+ [Dependency] private readonly ContainerSystem _containerSystem = default!;
public override void Initialize()
{
SubscribeLocalEvent<ClusterGrenadeComponent, ComponentInit>(OnClugInit);
SubscribeLocalEvent<ClusterGrenadeComponent, ComponentStartup>(OnClugStartup);
SubscribeLocalEvent<ClusterGrenadeComponent, InteractUsingEvent>(OnClugUsing);
- SubscribeLocalEvent<ClusterGrenadeComponent, UseInHandEvent>(OnClugUse);
+ SubscribeLocalEvent<ClusterGrenadeComponent, TriggerEvent>(OnClugTrigger);
}
private void OnClugInit(EntityUid uid, ClusterGrenadeComponent component, ComponentInit args)
{
- component.GrenadesContainer = _container.EnsureContainer<Container>(uid, "cluster-flash");
+ component.GrenadesContainer = _container.EnsureContainer<Container>(uid, "cluster-payload");
}
private void OnClugStartup(Entity<ClusterGrenadeComponent> clug, ref ComponentStartup args)
!HasComp<FlashOnTriggerComponent>(args.Used))
return;
- component.GrenadesContainer.Insert(args.Used);
+ _containerSystem.Insert(args.Used, component.GrenadesContainer);
UpdateAppearance(clug);
args.Handled = true;
}
- private void OnClugUse(EntityUid uid, ClusterGrenadeComponent component, UseInHandEvent args)
+ private void OnClugTrigger(Entity<ClusterGrenadeComponent> clug, ref TriggerEvent args)
{
- if (component.CountDown || (component.GrenadesContainer.ContainedEntities.Count + component.UnspawnedCount) <= 0)
- return;
+ var component = clug.Comp;
+ component.CountDown = true;
+ args.Handled = true;
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+ var query = EntityQueryEnumerator<ClusterGrenadeComponent>();
- // TODO: Should be an Update loop
- uid.SpawnTimer((int) (component.Delay * 1000), () =>
+ while (query.MoveNext(out var uid, out var clug))
{
- if (Deleted(uid))
- return;
-
- component.CountDown = true;
- var delay = 20;
- var grenadesInserted = component.GrenadesContainer.ContainedEntities.Count + component.UnspawnedCount;
- var thrownCount = 0;
- var segmentAngle = 360 / grenadesInserted;
- while (TryGetGrenade((uid, component), out var grenade))
+ if (clug.CountDown && clug.UnspawnedCount > 0)
{
- var angleMin = segmentAngle * thrownCount;
- var angleMax = segmentAngle * (thrownCount + 1);
- var angle = Angle.FromDegrees(_random.Next(angleMin, angleMax));
- // var distance = random.NextFloat() * _throwDistance;
+ var grenadesInserted = clug.GrenadesContainer.ContainedEntities.Count + clug.UnspawnedCount;
+ var thrownCount = 0;
+ var segmentAngle = 360 / grenadesInserted;
+ var grenadeDelay = 0f;
- delay += _random.Next(550, 900);
- thrownCount++;
+ while (TryGetGrenade(uid, clug, out var grenade))
+ {
+ // var distance = random.NextFloat() * _throwDistance;
+ var angleMin = segmentAngle * thrownCount;
+ var angleMax = segmentAngle * (thrownCount + 1);
+ var angle = Angle.FromDegrees(_random.Next(angleMin, angleMax));
+ if (clug.RandomAngle)
+ angle = _random.NextAngle();
+ thrownCount++;
+
+ switch (clug.GrenadeType)
+ {
+ case GrenadeType.Shoot:
+ ShootProjectile(grenade, angle, clug, uid);
+ break;
+ case GrenadeType.Throw:
+ ThrowGrenade(grenade, angle, clug);
+ break;
+ }
+
+ // give an active timer trigger to the contained grenades when they get launched
+ if (clug.TriggerGrenades)
+ {
+ grenadeDelay += _random.NextFloat(clug.GrenadeTriggerIntervalMin, clug.GrenadeTriggerIntervalMax);
+ var grenadeTimer = EnsureComp<ActiveTimerTriggerComponent>(grenade);
+ grenadeTimer.TimeRemaining = (clug.BaseTriggerDelay + grenadeDelay);
+ var ev = new ActiveTimerTriggerEvent(grenade, uid);
+ RaiseLocalEvent(uid, ref ev);
+ }
+ }
+ // delete the empty shell of the clusterbomb
+ Del(uid);
+ }
+ }
+ }
- // TODO: Suss out throw strength
- _throwingSystem.TryThrow(grenade, angle.ToVec().Normalized() * component.ThrowDistance);
+ private void ShootProjectile(EntityUid grenade, Angle angle, ClusterGrenadeComponent clug, EntityUid clugUid)
+ {
+ var direction = angle.ToVec().Normalized();
- grenade.SpawnTimer(delay, () =>
- {
- if ((!EntityManager.EntityExists(grenade) ? EntityLifeStage.Deleted : MetaData(grenade).EntityLifeStage) >= EntityLifeStage.Deleted)
- return;
+ if (clug.RandomSpread)
+ direction = _random.NextVector2().Normalized();
- _trigger.Trigger(grenade, args.User);
- });
- }
+ _gun.ShootProjectile(grenade, direction, Vector2.One.Normalized(), clugUid);
- EntityManager.DeleteEntity(uid);
- });
+ }
- args.Handled = true;
+ private void ThrowGrenade(EntityUid grenade, Angle angle, ClusterGrenadeComponent clug)
+ {
+ var direction = angle.ToVec().Normalized() * clug.Distance;
+
+ if (clug.RandomSpread)
+ direction = angle.ToVec().Normalized() * _random.NextFloat(clug.MinSpreadDistance, clug.MaxSpreadDistance);
+
+ _throwingSystem.TryThrow(grenade, direction, clug.Velocity);
}
- private bool TryGetGrenade(Entity<ClusterGrenadeComponent> ent, out EntityUid grenade)
+ private bool TryGetGrenade(EntityUid clugUid, ClusterGrenadeComponent component, out EntityUid grenade)
{
grenade = default;
- var component = ent.Comp;
if (component.UnspawnedCount > 0)
{
component.UnspawnedCount--;
- grenade = EntityManager.SpawnEntity(component.FillPrototype, Transform(ent).MapPosition);
+ grenade = Spawn(component.FillPrototype, _transformSystem.GetMapCoordinates(clugUid));
return true;
}
return false;
}
- private void UpdateAppearance(Entity<ClusterGrenadeComponent> ent)
+ private void UpdateAppearance(Entity<ClusterGrenadeComponent> clug)
{
- var component = ent.Comp;
- if (!TryComp<AppearanceComponent>(ent, out var appearance))
+ var component = clug.Comp;
+ if (!TryComp<AppearanceComponent>(clug, out var appearance))
return;
- _appearance.SetData(ent, ClusterGrenadeVisuals.GrenadesCounter, component.GrenadesContainer.ContainedEntities.Count + component.UnspawnedCount, appearance);
+ _appearance.SetData(clug, ClusterGrenadeVisuals.GrenadesCounter, component.GrenadesContainer.ContainedEntities.Count + component.UnspawnedCount, appearance);
}
}
using Content.Shared.Projectiles;
using Robust.Server.GameObjects;
using Robust.Shared.Physics.Events;
+using Content.Shared.Mobs.Components;
using Robust.Shared.Player;
namespace Content.Server.Projectiles;
component.DamagedEntity = true;
- if (component.DeleteOnCollide)
+ if (component.DeleteOnCollide )
+ {
+ QueueDel(uid);
+ }
+ if (component.CanPenetrate)
+ {
+ component.DamagedEntity = false;
+
+ if (!TryComp<MobStateComponent>(target, out var mobState))
+ QueueDel(uid);
+ }
+ else if (component.DeleteOnCollide && !component.CanPenetrate)
{
QueueDel(uid);
}
[DataField("deleteOnCollide")]
public bool DeleteOnCollide = true;
+ [DataField("canPenetrate")]
+ public bool CanPenetrate = false;
+
[DataField("ignoreResistances")]
public bool IgnoreResistances = false;
# Explosives
uplink-explosive-grenade-name = Explosive Grenade
-uplink-explosive-grenade-desc = A simplistic grenade with a ten-second fuse that is geared towards injuring personnel. Causes minimal hull damage.
+uplink-explosive-grenade-desc = A simplistic grenade with a three-and-a-half-second long fuse that is geared towards injuring personnel. Causes minimal hull damage.
uplink-flash-grenade-name = Flashbang
uplink-flash-grenade-desc = A standard-issue flashbang, capable of blinding and slowing down anyone without proper protection. This, of course, includes you; make sure you're properly equipped before using it.
uplink-exploding-syndicate-bomb-name = Syndicate Bomb
uplink-exploding-syndicate-bomb-desc = A big, anchored bomb that can create a huge explosion if not defused in time. Useful as a distraction. Has an adjustable timer with a minimum setting of 120 seconds.
+uplink-cluster-grenade-name = Cluster Grenade
+uplink-cluster-grenade-desc = Three explosive grenades bundled together, the grenades get launched after the 3.5 second timer runs out.
+
+uplink-incendiary-grenade-name = Incendiary Grenade
+uplink-incendiary-grenade-desc = Releases a spray of incendiary fragments, igniting anyone near the detonation area.
+
+uplink-shrapnel-grenade-name = Shrapnel Grenade
+uplink-shrapnel-grenade-desc = Launches a spray of sharp fragments dealing great damage against unarmored targets.
+
# Ammo
uplink-pistol-magazine-name = Pistol Magazine (.35 auto)
uplink-pistol-magazine-desc = Pistol magazine with 10 catridges. Compatible with the Viper.
uplink-disposable-turret-name = Disposable Ballistic Turret
uplink-disposable-turret-desc = Looks and functions like a normal electrical toolbox. Upon hitting the toolbox it will transform into a ballistic turret, theoretically shooting at anyone except members of the syndicate. Can be turned back into a toolbox using a screwdriver and repaired using a wrench.
+uplink-cluster-banana-peel-name = Cluster Banana
+uplink-cluster-banana-peel-desc = Splits into 6 explosive banana peels after being thrown, the peels detonate automatically after 20 seconds if nobody slips on them.
+
# Armor
uplink-chameleon-name = Chameleon Kit
uplink-chameleon-desc = A backpack full of items that contain chameleon technology allowing you to disguise as pretty much anything on the station, and more!
uplink-syndicate-sponge-box-name = Syndicate Sponge Box
uplink-syndicate-sponge-box-desc = A box containing 6 syndicate sponges disguised as monkey cubes, these cubes turn into a variety of angry wildlife after coming into contact with water.
+uplink-slipocalypse-clustersoap-name = Slipocalypse Clustersoap
+uplink-slipocalypse-clustersoap-desc = Scatters arounds small pieces of syndicate-brand soap after being thrown, these pieces of soap evaporate after 60 seconds.
+
# Pointless
uplink-revolver-cap-gun-name = Cap Gun
uplink-revolver-cap-gun-desc = Looks almost like the real thing! Ages 8 and up.
startingInventory:
Handcuffs: 8
GrenadeFlashBang: 4
+ ClusterBangFull: 2
+ GrenadeStinger: 4
Flash: 5
FlashlightSeclite: 5
ClothingEyesGlassesSunglasses: 2
- UplinkExplosives
restockTime: 30
+- type: listing
+ id: UplinkClusterGrenade
+ name: uplink-cluster-grenade-name
+ description: uplink-cluster-grenade-desc
+ productEntity: ClusterGrenade
+ cost:
+ Telecrystal: 8
+ categories:
+ - UplinkExplosives
+
+- type: listing
+ id: UplinkGrenadeShrapnel
+ name: uplink-shrapnel-grenade-name
+ description: uplink-shrapnel-grenade-desc
+ productEntity: GrenadeShrapnel
+ cost:
+ Telecrystal: 4
+ categories:
+ - UplinkExplosives
+
+- type: listing
+ id: UplinkGrenadeIncendiary
+ name: uplink-incendiary-grenade-name
+ description: uplink-incendiary-grenade-desc
+ productEntity: GrenadeIncendiary
+ cost:
+ Telecrystal: 4
+ categories:
+ - UplinkExplosives
+
# Ammo
- type: listing
whitelist:
- Clown
+- type: listing
+ id: UplinkClusterBananaPeel
+ name: uplink-cluster-banana-peel-name
+ description: uplink-cluster-banana-peel-desc
+ productEntity: ClusterBananaPeel
+ cost:
+ Telecrystal: 6
+ categories:
+ - UplinkJob
+ conditions:
+ - !type:BuyerJobCondition
+ whitelist:
+ - Clown
+
- type: listing
id: UplinkHoloclownKit
name: uplink-holoclown-kit-name
categories:
- UplinkMisc
+- type: listing
+ id: UplinkSlipocalypseClusterSoap
+ name: uplink-slipocalypse-clustersoap-name
+ description: uplink-slipocalypse-clustersoap-desc
+ productEntity: SlipocalypseClusterSoap
+ cost:
+ Telecrystal: 3
+ categories:
+ - UplinkMisc
+
- type: listing
id: UplinkUltrabrightLantern
name: uplink-ultrabright-lantern-name
- clean
- punishment
+- type: entity
+ name: soaplet
+ id: SoapletSyndie
+ noSpawn: true
+ parent: Soap
+ description: A tiny piece of syndicate soap.
+ components:
+ - type: Sprite
+ layers:
+ - state: syndie-soaplet
+ - type: Slippery
+ paralyzeTime: 5
+ launchForwardsMultiplier: 2.5
+ - type: StepTrigger
+ intersectRatio: 0.04
+ - type: Item
+ heldPrefix: syndie
+ - type: Fixtures
+ fixtures:
+ slips:
+ shape:
+ !type:PhysShapeAabb
+ bounds: "-0.08,-0.06,0.08,0.06"
+ layer:
+ - SlipLayer
+ hard: false
+ fix1:
+ shape:
+ !type:PhysShapeAabb
+ bounds: "-0.08,-0.06,0.08,0.06"
+ density: 1
+ mask:
+ - ItemMask
+ - type: DeleteOnTrigger
+ - type: EmitSoundOnTrigger
+ sound:
+ path: "/Audio/Effects/Fluids/splat.ogg"
+ params:
+ volume: -20
+
- type: entity
name: soap
id: SoapHomemade
- type: ExplodeOnTrigger
- type: Explosive
explosionType: Default
- maxIntensity: 2
- totalIntensity: 10
+ maxIntensity: 3.4
+ intensitySlope: 3
+ totalIntensity: 20
canCreateVacuum: false
- type: DeleteOnTrigger
- type: AnimationPlayer
+ - type: Damageable
+ damageContainer: Inorganic
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 10
+ behaviors:
+ - !type:TriggerBehavior
+ - !type:DoActsBehavior
+ acts: ["Destruction"]
- type: entity
parent: BaseItem
--- /dev/null
+- type: entity
+ id: PelletClusterRubber
+ name: pellet (ball, Rubber)
+ noSpawn: true
+ parent: BaseBullet
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles2.rsi
+ state: buckshot
+ - type: Projectile
+ deleteOnCollide: false
+ canPenetrate: true
+ damage:
+ types:
+ Blunt: 4
+ - type: StaminaDamageOnCollide
+ damage: 55
+ - type: TimedDespawn
+ lifetime: 0.25
+
+- type: entity
+ id: PelletClusterLethal
+ name: pellet (ball, Lethal)
+ noSpawn: true
+ parent: BaseBullet
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles2.rsi
+ state: buckshot
+ - type: Projectile
+ deleteOnCollide: false
+ canPenetrate: true
+ damage:
+ types:
+ Piercing: 45
+ - type: TimedDespawn
+ lifetime: 0.25
+
+- type: entity
+ id: PelletClusterIncendiary
+ name: pellet (ball, incendiary)
+ noSpawn: true
+ parent: BaseBulletIncendiary
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles2.rsi
+ state: buckshot-flare
+ - type: Projectile
+ deleteOnCollide: false
+ canPenetrate: true
+ damage:
+ groups:
+ Burn: 4
+ - type: IgniteOnCollide
+ fireStacks: 3
+ count: 10
+ - type: TimedDespawn
+ lifetime: 0.25
- state: grenade
- type: FlashOnTrigger
range: 7
+ - type: SpawnOnTrigger
+ proto: GrenadeFlashEffect
+ - type: ActiveTimerTrigger
+ timeRemaining: 0.3
+ - type: DeleteOnTrigger
# This is supposed to spawn shrapnel and stuff so uhh... TODO?
- type: entity
- type: ClusterGrenadeVisuals
state: base
- type: ClusterGrenade
+ - type: OnUseTimerTrigger
+ delay: 3.5
- type: ContainerContainer
containers:
- cluster-flash: !type:Container
+ cluster-payload: !type:Container
- type: entity
- parent: ClusterBang
+ parent: GrenadeBase
id: ClusterBangFull
+ name: ClusterBang
+ description: Launches three flashbangs after the timer runs out.
suffix: Full
components:
- type: Sprite
- state: base-3
+ sprite: Objects/Weapons/Grenades/clusterbang.rsi
+ layers:
+ - state: icon
+ map: ["enum.TriggerVisualLayers.Base"]
- type: ClusterGrenade
fillPrototype: GrenadeFlashBang
+ distance: 7
+ velocity: 7
+ - type: TimerTriggerVisuals
+ primingSound:
+ path: /Audio/Effects/countdown.ogg
+ - type: GenericVisualizer
+ visuals:
+ enum.Trigger.TriggerVisuals.VisualState:
+ enum.ConstructionVisuals.Layer:
+ Primed: { state: primed }
+ Unprimed: { state: icon }
+ - type: EmitSoundOnTrigger
+ sound:
+ path: "/Audio/Machines/door_lock_off.ogg"
+ - type: ContainerContainer
+ containers:
+ cluster-payload: !type:Container
+
+- type: entity
+ parent: GrenadeBase
+ id: ClusterGrenade
+ name: clustergrenade
+ description: Why use one grenade when you can use three at once!
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Grenades/clusterbomb.rsi
+ layers:
+ - state: icon
+ map: ["enum.TriggerVisualLayers.Base"]
+ - type: ClusterGrenade
+ fillPrototype: ExGrenade
+ velocity: 3.5
+ distance: 5
+ - type: OnUseTimerTrigger
+ beepSound:
+ path: "/Audio/Effects/beep1.ogg"
+ params:
+ volume: 5
+ initialBeepDelay: 0
+ beepInterval: 0.5
+ - type: EmitSoundOnTrigger
+ sound:
+ path: "/Audio/Machines/door_lock_off.ogg"
+ - type: ContainerContainer
+ containers:
+ cluster-payload: !type:Container
+
+- type: entity
+ parent: BaseItem
+ id: ClusterBananaPeel
+ name: cluster banana peel
+ description: Splits into 6 explosive banana peels after throwing, guaranteed fun!
+ components:
+ - type: Sprite
+ sprite: Objects/Specific/Hydroponics/banana.rsi
+ state: produce
+ - type: Appearance
+ - type: ClusterGrenade
+ fillPrototype: TrashBananaPeelExplosive
+ maxGrenadesCount: 6
+ baseTriggerDelay: 20
+ - type: DamageOnLand
+ damage:
+ types:
+ Blunt: 10
+ - type: Damageable
+ damageContainer: Inorganic
+ - type: EmitSoundOnTrigger
+ sound:
+ path: "/Audio/Items/bikehorn.ogg"
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 10
+ behaviors:
+ - !type:TriggerBehavior
+ - !type:DoActsBehavior
+ acts: ["Destruction"]
+ - type: ContainerContainer
+ containers:
+ cluster-payload: !type:Container
+
+- type: entity
+ parent: GrenadeBase
+ id: GrenadeStinger
+ name: stinger grenade
+ description: Nothing to see here, please disperse.
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Grenades/stingergrenade.rsi
+ layers:
+ - state: icon
+ map: ["enum.TriggerVisualLayers.Base"]
+ - type: ClusterGrenade
+ fillPrototype: PelletClusterRubber
+ maxGrenadesCount: 30
+ grenadeType: enum.GrenadeType.Shoot
+ - type: FlashOnTrigger
+ range: 7
+ - type: EmitSoundOnTrigger
+ sound:
+ path: "/Audio/Effects/flash_bang.ogg"
+ - type: SpawnOnTrigger
+ proto: GrenadeFlashEffect
+ - type: TimerTriggerVisuals
+ primingSound:
+ path: /Audio/Effects/countdown.ogg
+ - type: ContainerContainer
+ containers:
+ cluster-payload: !type:Container
+
+- type: entity
+ parent: GrenadeBase
+ id: GrenadeIncendiary
+ name: incendiary grenade
+ description: Guaranteed to light up the mood.
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Grenades/pyrogrenade.rsi
+ layers:
+ - state: icon
+ map: ["enum.TriggerVisualLayers.Base"]
+ - type: ClusterGrenade
+ fillPrototype: PelletClusterIncendiary
+ maxGrenadesCount: 15
+ grenadeType: enum.GrenadeType.Shoot
+ - type: OnUseTimerTrigger
+ beepSound:
+ path: "/Audio/Effects/beep1.ogg"
+ params:
+ volume: 5
+ initialBeepDelay: 0
+ beepInterval: 2
+ - type: EmitSoundOnTrigger
+ sound:
+ path: "/Audio/Weapons/Guns/Gunshots/batrifle.ogg"
+ - type: ContainerContainer
+ containers:
+ cluster-payload: !type:Container
+
+- type: entity
+ parent: GrenadeBase
+ id: GrenadeShrapnel
+ name: shrapnel grenade
+ description: Releases a deadly spray of shrapnel that causes severe bleeding.
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Grenades/shrapnelgrenade.rsi
+ layers:
+ - state: icon
+ map: ["enum.TriggerVisualLayers.Base"]
+ - type: ClusterGrenade
+ fillPrototype: PelletClusterLethal
+ maxGrenadesCount: 30
+ grenadeType: enum.GrenadeType.Shoot
+ - type: OnUseTimerTrigger
+ beepSound:
+ path: "/Audio/Effects/beep1.ogg"
+ params:
+ volume: 5
+ initialBeepDelay: 0
+ beepInterval: 2
+ - type: EmitSoundOnTrigger
+ sound:
+ path: "/Audio/Weapons/Guns/Gunshots/batrifle.ogg"
+ - type: ContainerContainer
+ containers:
+ cluster-payload: !type:Container
+
+- type: entity
+ parent: SoapSyndie
+ id: SlipocalypseClusterSoap
+ name: slipocalypse clustersoap
+ description: Spreads small pieces of syndicate soap over an area upon landing on the floor.
+ components:
+ - type: Sprite
+ sprite: Objects/Specific/Janitorial/soap.rsi
+ layers:
+ - state: syndie-4
+ - type: Appearance
+ - type: ClusterGrenade
+ fillPrototype: SoapletSyndie
+ maxGrenadesCount: 30
+ grenadeTriggerIntervalMax: 0
+ grenadeTriggerIntervalMin: 0
+ baseTriggerDelay: 60
+ randomSpread: true
+ velocity: 3
+ - type: DamageOnLand
+ damage:
+ types:
+ Blunt: 10
+ - type: EmitSoundOnTrigger
+ sound:
+ path: "/Audio/Effects/flash_bang.ogg"
+ - type: Damageable
+ damageContainer: Inorganic
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 10
+ behaviors:
+ - !type:TriggerBehavior
+ - !type:DoActsBehavior
+ acts: ["Destruction"]
+ - type: ContainerContainer
+ containers:
+ cluster-payload: !type:Container
acts: ["Destruction"]
- type: Appearance
- type: AnimationPlayer
- - type: TimerTriggerVisuals
+ - type: GenericVisualizer
+ visuals:
+ enum.Trigger.TriggerVisuals.VisualState:
+ enum.ConstructionVisuals.Layer:
+ Primed: { state: primed }
+ Unprimed: { state: icon }
- type: entity
name: explosive grenade
{
"name": "syndie-4"
},
+ {
+ "name": "syndie-soaplet"
+ },
{
"name": "inhand-left",
"directions": 4
{
"name": "base-3",
"directions": 1
+ },
+ {
+ "name": "icon",
+ "directions": 1
+ },
+ {
+ "name": "primed",
+ "delays": [
+ [
+ 0.1,
+ 0.1
+ ]
+ ]
}
]
}
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from tgstation and modified by Swept at https://github.com/tgstation/tgstation/commit/29c0ed1b000619cb5398ef921000a8d4502ba0b6 and modified by Swept",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "icon"
+ },
+ {
+ "name": "base-1"
+ },
+ {
+ "name": "base-2"
+ },
+ {
+ "name": "primed",
+ "delays": [
+ [
+ 0.1,
+ 0.1
+ ]
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/b13d244d761a07e200a9a41730bd446e776020d5",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "icon"
+ },
+ {
+ "name": "primed",
+ "delays": [
+ [
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "equipped-BELT",
+ "directions": 4
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/b13d244d761a07e200a9a41730bd446e776020d5",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "icon"
+ },
+ {
+ "name": "primed",
+ "delays": [
+ [
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "equipped-BELT",
+ "directions": 4
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/b13d244d761a07e200a9a41730bd446e776020d5",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "icon"
+ },
+ {
+ "name": "primed",
+ "delays": [
+ [
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "equipped-BELT",
+ "directions": 4
+ }
+ ]
+}
"name": "spent"
}
]
-}
\ No newline at end of file
+}