Cluster grenades for uplink and security (#22029)
* clustergrenades go boom * Small tweaks * Some tweaks and soaplet * clustergrenadesystem changes and launcher types * small tweaks * typo * whitespace * rsi edit * another typo * add containers * Some changes related to merge * Forgot to change name * Made changes based on review * Removed new china lake ammo based on feedback in other PR * Unneeded nested loop moment * Nested loop needed after all moment
@@ -1,7 +1,6 @@
|
||||
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
|
||||
{
|
||||
@@ -13,8 +12,8 @@ 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.
|
||||
@@ -28,20 +27,91 @@ namespace Content.Server.Explosion.Components
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@ using Content.Shared.Interaction.Events;
|
||||
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;
|
||||
|
||||
@@ -13,9 +17,11 @@ public sealed class ClusterGrenadeSystem : EntitySystem
|
||||
{
|
||||
[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()
|
||||
{
|
||||
@@ -23,12 +29,12 @@ public sealed class ClusterGrenadeSystem : EntitySystem
|
||||
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)
|
||||
@@ -53,64 +59,97 @@ public sealed class ClusterGrenadeSystem : EntitySystem
|
||||
!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;
|
||||
|
||||
// TODO: Should be an Update loop
|
||||
uid.SpawnTimer((int) (component.Delay * 1000), () =>
|
||||
{
|
||||
if (Deleted(uid))
|
||||
return;
|
||||
|
||||
var component = clug.Comp;
|
||||
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))
|
||||
{
|
||||
var angleMin = segmentAngle * thrownCount;
|
||||
var angleMax = segmentAngle * (thrownCount + 1);
|
||||
var angle = Angle.FromDegrees(_random.Next(angleMin, angleMax));
|
||||
// var distance = random.NextFloat() * _throwDistance;
|
||||
|
||||
delay += _random.Next(550, 900);
|
||||
thrownCount++;
|
||||
|
||||
// TODO: Suss out throw strength
|
||||
_throwingSystem.TryThrow(grenade, angle.ToVec().Normalized() * component.ThrowDistance);
|
||||
|
||||
grenade.SpawnTimer(delay, () =>
|
||||
{
|
||||
if ((!EntityManager.EntityExists(grenade) ? EntityLifeStage.Deleted : MetaData(grenade).EntityLifeStage) >= EntityLifeStage.Deleted)
|
||||
return;
|
||||
|
||||
_trigger.Trigger(grenade, args.User);
|
||||
});
|
||||
}
|
||||
|
||||
EntityManager.DeleteEntity(uid);
|
||||
});
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private bool TryGetGrenade(Entity<ClusterGrenadeComponent> ent, out EntityUid grenade)
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
var query = EntityQueryEnumerator<ClusterGrenadeComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var clug))
|
||||
{
|
||||
if (clug.CountDown && clug.UnspawnedCount > 0)
|
||||
{
|
||||
var grenadesInserted = clug.GrenadesContainer.ContainedEntities.Count + clug.UnspawnedCount;
|
||||
var thrownCount = 0;
|
||||
var segmentAngle = 360 / grenadesInserted;
|
||||
var grenadeDelay = 0f;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShootProjectile(EntityUid grenade, Angle angle, ClusterGrenadeComponent clug, EntityUid clugUid)
|
||||
{
|
||||
var direction = angle.ToVec().Normalized();
|
||||
|
||||
if (clug.RandomSpread)
|
||||
direction = _random.NextVector2().Normalized();
|
||||
|
||||
_gun.ShootProjectile(grenade, direction, Vector2.One.Normalized(), clugUid);
|
||||
|
||||
}
|
||||
|
||||
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(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;
|
||||
}
|
||||
|
||||
@@ -128,12 +167,12 @@ public sealed class ClusterGrenadeSystem : EntitySystem
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Shared.Database;
|
||||
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;
|
||||
@@ -70,7 +71,18 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@ public sealed partial class ProjectileComponent : Component
|
||||
[DataField("deleteOnCollide")]
|
||||
public bool DeleteOnCollide = true;
|
||||
|
||||
[DataField("canPenetrate")]
|
||||
public bool CanPenetrate = false;
|
||||
|
||||
[DataField("ignoreResistances")]
|
||||
public bool IgnoreResistances = false;
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ uplink-gloves-north-star-desc = A pair of gloves that reduce your punching coold
|
||||
|
||||
# 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.
|
||||
@@ -63,6 +63,15 @@ uplink-exploding-pen-desc = A class IV explosive device contained within a stand
|
||||
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.
|
||||
@@ -240,6 +249,9 @@ uplink-proximity-mine-desc = A mine disguised as a wet floor sign.
|
||||
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!
|
||||
@@ -308,6 +320,9 @@ uplink-syndicate-segway-crate-desc = Be an enemy of the corporation, in style!
|
||||
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.
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
startingInventory:
|
||||
Handcuffs: 8
|
||||
GrenadeFlashBang: 4
|
||||
ClusterBangFull: 2
|
||||
GrenadeStinger: 4
|
||||
Flash: 5
|
||||
FlashlightSeclite: 5
|
||||
ClothingEyesGlassesSunglasses: 2
|
||||
|
||||
@@ -250,6 +250,36 @@
|
||||
- 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
|
||||
@@ -956,6 +986,20 @@
|
||||
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
|
||||
@@ -1186,6 +1230,16 @@
|
||||
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
|
||||
|
||||
@@ -128,6 +128,46 @@
|
||||
- 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
|
||||
|
||||
@@ -83,11 +83,23 @@
|
||||
- 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
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
- 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
|
||||
@@ -641,6 +641,11 @@
|
||||
- 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
|
||||
|
||||
@@ -11,16 +11,233 @@
|
||||
- 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
|
||||
|
||||
@@ -29,7 +29,12 @@
|
||||
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
|
||||
|
||||
@@ -79,6 +79,9 @@
|
||||
{
|
||||
"name": "syndie-4"
|
||||
},
|
||||
{
|
||||
"name": "syndie-soaplet"
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
|
||||
|
After Width: | Height: | Size: 157 B |
|
After Width: | Height: | Size: 459 B |
@@ -22,6 +22,19 @@
|
||||
{
|
||||
"name": "base-3",
|
||||
"directions": 1
|
||||
},
|
||||
{
|
||||
"name": "icon",
|
||||
"directions": 1
|
||||
},
|
||||
{
|
||||
"name": "primed",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 680 B |
|
After Width: | Height: | Size: 467 B |
|
After Width: | Height: | Size: 502 B |
|
After Width: | Height: | Size: 476 B |
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 667 B |
|
After Width: | Height: | Size: 234 B |
|
After Width: | Height: | Size: 290 B |
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 349 B |
|
After Width: | Height: | Size: 233 B |
|
After Width: | Height: | Size: 293 B |
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 351 B |
|
After Width: | Height: | Size: 254 B |
|
After Width: | Height: | Size: 312 B |
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 377 B |