Merge branch 'master' into pr/39875
@@ -5,6 +5,8 @@ using Content.Client.Items;
|
|||||||
using Content.Client.Weapons.Ranged.Components;
|
using Content.Client.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Camera;
|
using Content.Shared.Camera;
|
||||||
using Content.Shared.CombatMode;
|
using Content.Shared.CombatMode;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Components;
|
||||||
using Content.Shared.Weapons.Ranged;
|
using Content.Shared.Weapons.Ranged;
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
using Content.Shared.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Weapons.Ranged.Events;
|
using Content.Shared.Weapons.Ranged.Events;
|
||||||
@@ -16,6 +18,7 @@ using Robust.Client.Input;
|
|||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Client.State;
|
using Robust.Client.State;
|
||||||
using Robust.Shared.Animations;
|
using Robust.Shared.Animations;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
@@ -234,6 +237,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Clean this up in a gun refactor at some point - too much copy pasting
|
||||||
switch (shootable)
|
switch (shootable)
|
||||||
{
|
{
|
||||||
case CartridgeAmmoComponent cartridge:
|
case CartridgeAmmoComponent cartridge:
|
||||||
@@ -266,7 +270,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
else
|
else
|
||||||
RemoveShootable(ent.Value);
|
RemoveShootable(ent.Value);
|
||||||
break;
|
break;
|
||||||
case HitscanPrototype:
|
case HitscanAmmoComponent:
|
||||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
||||||
Recoil(user, direction, gun.CameraRecoilScalarModified);
|
Recoil(user, direction, gun.CameraRecoilScalarModified);
|
||||||
break;
|
break;
|
||||||
@@ -404,4 +408,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
_animPlayer.Stop(gunUid, uidPlayer, "muzzle-flash-light");
|
_animPlayer.Stop(gunUid, uidPlayer, "muzzle-flash-light");
|
||||||
_animPlayer.Play((gunUid, uidPlayer), animTwo, "muzzle-flash-light");
|
_animPlayer.Play((gunUid, uidPlayer), animTwo, "muzzle-flash-light");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move RangedDamageSoundComponent to shared so this can be predicted.
|
||||||
|
public override void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ public sealed partial class SpaceVillainGame
|
|||||||
UpdateUi(
|
UpdateUi(
|
||||||
uid,
|
uid,
|
||||||
Loc.GetString("space-villain-game-player-loses-message"),
|
Loc.GetString("space-villain-game-player-loses-message"),
|
||||||
Loc.GetString("space-villain-game-enemy-dies-with-player-message ", ("enemyName", _villainName)),
|
Loc.GetString("space-villain-game-enemy-dies-with-player-message", ("enemyName", _villainName)),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
_audioSystem.PlayPvs(arcade.GameOverSound, uid, AudioParams.Default.WithVolume(-4f));
|
_audioSystem.PlayPvs(arcade.GameOverSound, uid, AudioParams.Default.WithVolume(-4f));
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public sealed class SprayPainterSystem : SharedSprayPainterSystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
if (TryComp(ent, out LimitedChargesComponent? charges) && charges.LastCharges < ent.Comp.DecalChargeCost)
|
if (TryComp(ent, out LimitedChargesComponent? charges) && _charges.GetCurrentCharges((ent, charges)) < ent.Comp.DecalChargeCost)
|
||||||
{
|
{
|
||||||
_popup.PopupEntity(Loc.GetString("spray-painter-interact-no-charges"), args.User, args.User);
|
_popup.PopupEntity(Loc.GetString("spray-painter-interact-no-charges"), args.User, args.User);
|
||||||
return;
|
return;
|
||||||
@@ -165,7 +165,7 @@ public sealed class SprayPainterSystem : SharedSprayPainterSystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (TryComp<LimitedChargesComponent>(args.Used, out var charges)
|
if (TryComp<LimitedChargesComponent>(args.Used, out var charges)
|
||||||
&& charges.LastCharges < painter.PipeChargeCost)
|
&& _charges.GetCurrentCharges((args.Used, charges)) < painter.PipeChargeCost)
|
||||||
{
|
{
|
||||||
var msg = Loc.GetString("spray-painter-interact-no-charges");
|
var msg = Loc.GetString("spray-painter-interact-no-charges");
|
||||||
_popup.PopupEntity(msg, args.User, args.User);
|
_popup.PopupEntity(msg, args.User, args.User);
|
||||||
|
|||||||
@@ -1,27 +1,22 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Server.Cargo.Systems;
|
using Content.Server.Cargo.Systems;
|
||||||
using Content.Server.Weapons.Ranged.Components;
|
using Content.Server.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Cargo;
|
using Content.Shared.Cargo;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Systems;
|
using Content.Shared.Damage.Systems;
|
||||||
using Content.Shared.Database;
|
|
||||||
using Content.Shared.Effects;
|
|
||||||
using Content.Shared.Projectiles;
|
using Content.Shared.Projectiles;
|
||||||
using Content.Shared.Weapons.Melee;
|
using Content.Shared.Weapons.Melee;
|
||||||
using Content.Shared.Weapons.Ranged;
|
using Content.Shared.Weapons.Ranged;
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
using Content.Shared.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Weapons.Ranged.Events;
|
using Content.Shared.Weapons.Ranged.Events;
|
||||||
using Content.Shared.Weapons.Ranged.Systems;
|
using Content.Shared.Weapons.Ranged.Systems;
|
||||||
using Content.Shared.Weapons.Reflect;
|
using Content.Shared.Weapons.Hitscan.Components;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Weapons.Hitscan.Events;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using Robust.Shared.Containers;
|
|
||||||
|
|
||||||
namespace Content.Server.Weapons.Ranged.Systems;
|
namespace Content.Server.Weapons.Ranged.Systems;
|
||||||
|
|
||||||
@@ -29,9 +24,6 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
||||||
[Dependency] private readonly PricingSystem _pricing = default!;
|
[Dependency] private readonly PricingSystem _pricing = default!;
|
||||||
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
|
|
||||||
[Dependency] private readonly SharedStaminaSystem _stamina = default!;
|
|
||||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
|
||||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||||
|
|
||||||
private const float DamagePitchVariation = 0.05f;
|
private const float DamagePitchVariation = 0.05f;
|
||||||
@@ -103,6 +95,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Clean this up in a gun refactor at some point - too much copy pasting
|
||||||
switch (shootable)
|
switch (shootable)
|
||||||
{
|
{
|
||||||
// Cartridge shoots something else
|
// Cartridge shoots something else
|
||||||
@@ -141,107 +134,21 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
CreateAndFireProjectiles(ent.Value, newAmmo);
|
CreateAndFireProjectiles(ent.Value, newAmmo);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case HitscanPrototype hitscan:
|
case HitscanAmmoComponent:
|
||||||
|
if (ent == null)
|
||||||
|
break;
|
||||||
|
|
||||||
EntityUid? lastHit = null;
|
var hitscanEv = new HitscanTraceEvent
|
||||||
|
|
||||||
var from = fromMap;
|
|
||||||
// can't use map coords above because funny FireEffects
|
|
||||||
var fromEffect = fromCoordinates;
|
|
||||||
var dir = mapDirection.Normalized();
|
|
||||||
|
|
||||||
//in the situation when user == null, means that the cannon fires on its own (via signals). And we need the gun to not fire by itself in this case
|
|
||||||
var lastUser = user ?? gunUid;
|
|
||||||
|
|
||||||
if (hitscan.Reflective != ReflectType.None)
|
|
||||||
{
|
{
|
||||||
for (var reflectAttempt = 0; reflectAttempt < 3; reflectAttempt++)
|
FromCoordinates = fromCoordinates,
|
||||||
{
|
ShotDirection = mapDirection.Normalized(),
|
||||||
var ray = new CollisionRay(from.Position, dir, hitscan.CollisionMask);
|
Gun = gunUid,
|
||||||
var rayCastResults =
|
Shooter = user,
|
||||||
Physics.IntersectRay(from.MapId, ray, hitscan.MaxLength, lastUser, false).ToList();
|
Target = gun.Target,
|
||||||
if (!rayCastResults.Any())
|
};
|
||||||
break;
|
RaiseLocalEvent(ent.Value, ref hitscanEv);
|
||||||
|
|
||||||
var result = rayCastResults[0];
|
Del(ent);
|
||||||
|
|
||||||
// Check if laser is shot from in a container
|
|
||||||
if (!_container.IsEntityOrParentInContainer(lastUser))
|
|
||||||
{
|
|
||||||
// Checks if the laser should pass over unless targeted by its user
|
|
||||||
foreach (var collide in rayCastResults)
|
|
||||||
{
|
|
||||||
if (collide.HitEntity != gun.Target &&
|
|
||||||
CompOrNull<RequireProjectileTargetComponent>(collide.HitEntity)?.Active == true)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = collide;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var hit = result.HitEntity;
|
|
||||||
lastHit = hit;
|
|
||||||
|
|
||||||
FireEffects(fromEffect, result.Distance, dir.Normalized().ToAngle(), hitscan, hit);
|
|
||||||
|
|
||||||
var ev = new HitScanReflectAttemptEvent(user, gunUid, hitscan.Reflective, dir, false);
|
|
||||||
RaiseLocalEvent(hit, ref ev);
|
|
||||||
|
|
||||||
if (!ev.Reflected)
|
|
||||||
break;
|
|
||||||
|
|
||||||
fromEffect = Transform(hit).Coordinates;
|
|
||||||
from = TransformSystem.ToMapCoordinates(fromEffect);
|
|
||||||
dir = ev.Direction;
|
|
||||||
lastUser = hit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastHit != null)
|
|
||||||
{
|
|
||||||
var hitEntity = lastHit.Value;
|
|
||||||
if (hitscan.StaminaDamage > 0f)
|
|
||||||
_stamina.TakeStaminaDamage(hitEntity, hitscan.StaminaDamage, source: user);
|
|
||||||
|
|
||||||
var dmg = hitscan.Damage;
|
|
||||||
|
|
||||||
var hitName = ToPrettyString(hitEntity);
|
|
||||||
if (dmg != null)
|
|
||||||
dmg = Damageable.TryChangeDamage(hitEntity, dmg * Damageable.UniversalHitscanDamageModifier, origin: user);
|
|
||||||
|
|
||||||
// check null again, as TryChangeDamage returns modified damage values
|
|
||||||
if (dmg != null)
|
|
||||||
{
|
|
||||||
if (!Deleted(hitEntity))
|
|
||||||
{
|
|
||||||
if (dmg.AnyPositive())
|
|
||||||
{
|
|
||||||
_color.RaiseEffect(Color.Red, new List<EntityUid>() { hitEntity }, Filter.Pvs(hitEntity, entityManager: EntityManager));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO get fallback position for playing hit sound.
|
|
||||||
PlayImpactSound(hitEntity, dmg, hitscan.Sound, hitscan.ForceSound);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
Logs.Add(LogType.HitScanHit,
|
|
||||||
$"{ToPrettyString(user.Value):user} hit {hitName:target} using hitscan and dealt {dmg.GetTotal():damage} damage");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logs.Add(LogType.HitScanHit,
|
|
||||||
$"{hitName:target} hit by hitscan dealing {dmg.GetTotal():damage} damage");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FireEffects(fromEffect, hitscan.MaxLength, dir.ToAngle(), hitscan);
|
|
||||||
}
|
|
||||||
|
|
||||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
||||||
break;
|
break;
|
||||||
@@ -353,7 +260,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
RaiseNetworkEvent(message, filter);
|
RaiseNetworkEvent(message, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound)
|
public override void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound)
|
||||||
{
|
{
|
||||||
DebugTools.Assert(!Deleted(otherEntity), "Impact sound entity was deleted");
|
DebugTools.Assert(!Deleted(otherEntity), "Impact sound entity was deleted");
|
||||||
|
|
||||||
@@ -384,69 +291,4 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
Audio.PlayPvs(weaponSound, otherEntity);
|
Audio.PlayPvs(weaponSound, otherEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Pseudo RNG so the client can predict these.
|
|
||||||
#region Hitscan effects
|
|
||||||
|
|
||||||
private void FireEffects(EntityCoordinates fromCoordinates, float distance, Angle angle, HitscanPrototype hitscan, EntityUid? hitEntity = null)
|
|
||||||
{
|
|
||||||
// Lord
|
|
||||||
// Forgive me for the shitcode I am about to do
|
|
||||||
// Effects tempt me not
|
|
||||||
var sprites = new List<(NetCoordinates coordinates, Angle angle, SpriteSpecifier sprite, float scale)>();
|
|
||||||
var fromXform = Transform(fromCoordinates.EntityId);
|
|
||||||
|
|
||||||
// We'll get the effects relative to the grid / map of the firer
|
|
||||||
// Look you could probably optimise this a bit with redundant transforms at this point.
|
|
||||||
|
|
||||||
var gridUid = fromXform.GridUid;
|
|
||||||
if (gridUid != fromCoordinates.EntityId && TryComp(gridUid, out TransformComponent? gridXform))
|
|
||||||
{
|
|
||||||
var (_, gridRot, gridInvMatrix) = TransformSystem.GetWorldPositionRotationInvMatrix(gridXform);
|
|
||||||
var map = TransformSystem.ToMapCoordinates(fromCoordinates);
|
|
||||||
fromCoordinates = new EntityCoordinates(gridUid.Value, Vector2.Transform(map.Position, gridInvMatrix));
|
|
||||||
angle -= gridRot;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
angle -= TransformSystem.GetWorldRotation(fromXform);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (distance >= 1f)
|
|
||||||
{
|
|
||||||
if (hitscan.MuzzleFlash != null)
|
|
||||||
{
|
|
||||||
var coords = fromCoordinates.Offset(angle.ToVec().Normalized() / 2);
|
|
||||||
var netCoords = GetNetCoordinates(coords);
|
|
||||||
|
|
||||||
sprites.Add((netCoords, angle, hitscan.MuzzleFlash, 1f));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hitscan.TravelFlash != null)
|
|
||||||
{
|
|
||||||
var coords = fromCoordinates.Offset(angle.ToVec() * (distance + 0.5f) / 2);
|
|
||||||
var netCoords = GetNetCoordinates(coords);
|
|
||||||
|
|
||||||
sprites.Add((netCoords, angle, hitscan.TravelFlash, distance - 1.5f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hitscan.ImpactFlash != null)
|
|
||||||
{
|
|
||||||
var coords = fromCoordinates.Offset(angle.ToVec() * distance);
|
|
||||||
var netCoords = GetNetCoordinates(coords);
|
|
||||||
|
|
||||||
sprites.Add((netCoords, angle.FlipPositive(), hitscan.ImpactFlash, 1f));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sprites.Count > 0)
|
|
||||||
{
|
|
||||||
RaiseNetworkEvent(new HitscanEvent
|
|
||||||
{
|
|
||||||
Sprites = sprites,
|
|
||||||
}, Filter.Pvs(fromCoordinates, entityMan: EntityManager));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
/// systems use this.
|
/// systems use this.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DataField("maxVol")]
|
[DataField("maxVol")]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public FixedPoint2 MaxVolume { get; set; } = FixedPoint2.Zero;
|
public FixedPoint2 MaxVolume { get; set; } = FixedPoint2.Zero;
|
||||||
|
|
||||||
public float FillFraction => MaxVolume == 0 ? 1 : Volume.Float() / MaxVolume.Float();
|
public float FillFraction => MaxVolume == 0 ? 1 : Volume.Float() / MaxVolume.Float();
|
||||||
@@ -45,8 +44,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// If reactions will be checked for when adding reagents to the container.
|
/// If reactions will be checked for when adding reagents to the container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
[DataField("canReact")]
|
|
||||||
public bool CanReact { get; set; } = true;
|
public bool CanReact { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -58,8 +56,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The temperature of the reagents in the solution.
|
/// The temperature of the reagents in the solution.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
[DataField("temperature")]
|
|
||||||
public float Temperature { get; set; } = 293.15f;
|
public float Temperature { get; set; } = 293.15f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -100,7 +97,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
_heatCapacity = 0;
|
_heatCapacity = 0;
|
||||||
foreach (var (reagent, quantity) in Contents)
|
foreach (var (reagent, quantity) in Contents)
|
||||||
{
|
{
|
||||||
_heatCapacity += (float) quantity *
|
_heatCapacity += (float)quantity *
|
||||||
protoMan.Index<ReagentPrototype>(reagent.Prototype).SpecificHeat;
|
protoMan.Index<ReagentPrototype>(reagent.Prototype).SpecificHeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +145,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="prototype">The prototype ID of the reagent to add.</param>
|
/// <param name="prototype">The prototype ID of the reagent to add.</param>
|
||||||
/// <param name="quantity">The quantity in milli-units.</param>
|
/// <param name="quantity">The quantity in milli-units.</param>
|
||||||
public Solution(string prototype, FixedPoint2 quantity, List<ReagentData>? data = null) : this()
|
public Solution([ForbidLiteral] string prototype, FixedPoint2 quantity, List<ReagentData>? data = null) : this()
|
||||||
{
|
{
|
||||||
AddReagent(new ReagentId(prototype, data), quantity);
|
AddReagent(new ReagentId(prototype, data), quantity);
|
||||||
}
|
}
|
||||||
@@ -190,7 +187,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
public void ValidateSolution()
|
public void ValidateSolution()
|
||||||
{
|
{
|
||||||
// sandbox forbids: [Conditional("DEBUG")]
|
// sandbox forbids: [Conditional("DEBUG")]
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// Correct volume
|
// Correct volume
|
||||||
DebugTools.Assert(Contents.Select(x => x.Quantity).Sum() == Volume);
|
DebugTools.Assert(Contents.Select(x => x.Quantity).Sum() == Volume);
|
||||||
|
|
||||||
@@ -208,7 +205,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
UpdateHeatCapacity(null);
|
UpdateHeatCapacity(null);
|
||||||
DebugTools.Assert(MathHelper.CloseTo(_heatCapacity, cur, tolerance: 0.01));
|
DebugTools.Assert(MathHelper.CloseTo(_heatCapacity, cur, tolerance: 0.01));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
void ISerializationHooks.AfterDeserialization()
|
||||||
@@ -223,7 +220,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
MaxVolume = Volume;
|
MaxVolume = Volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ContainsPrototype(string prototype)
|
public bool ContainsPrototype([ForbidLiteral] string prototype)
|
||||||
{
|
{
|
||||||
foreach (var (reagent, _) in Contents)
|
foreach (var (reagent, _) in Contents)
|
||||||
{
|
{
|
||||||
@@ -245,7 +242,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ContainsReagent(string reagentId, List<ReagentData>? data)
|
public bool ContainsReagent([ForbidLiteral] string reagentId, List<ReagentData>? data)
|
||||||
=> ContainsReagent(new(reagentId, data));
|
=> ContainsReagent(new(reagentId, data));
|
||||||
|
|
||||||
public bool TryGetReagent(ReagentId id, out ReagentQuantity quantity)
|
public bool TryGetReagent(ReagentId id, out ReagentQuantity quantity)
|
||||||
@@ -352,7 +349,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="prototype">The prototype ID of the reagent to add.</param>
|
/// <param name="prototype">The prototype ID of the reagent to add.</param>
|
||||||
/// <param name="quantity">The quantity in milli-units.</param>
|
/// <param name="quantity">The quantity in milli-units.</param>
|
||||||
public void AddReagent(string prototype, FixedPoint2 quantity, bool dirtyHeatCap = true)
|
public void AddReagent([ForbidLiteral] string prototype, FixedPoint2 quantity, bool dirtyHeatCap = true)
|
||||||
=> AddReagent(new ReagentId(prototype, null), quantity, dirtyHeatCap);
|
=> AddReagent(new ReagentId(prototype, null), quantity, dirtyHeatCap);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -673,6 +670,12 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
return sol;
|
return sol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Splits a solution into two by moving reagents from the given solution into a new one.
|
||||||
|
/// This modifies the original solution.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toTake">The quantity of this solution to remove.</param>
|
||||||
|
/// <returns>A new solution containing the removed reagents.</returns>
|
||||||
public Solution SplitSolution(FixedPoint2 toTake)
|
public Solution SplitSolution(FixedPoint2 toTake)
|
||||||
{
|
{
|
||||||
if (toTake <= FixedPoint2.Zero)
|
if (toTake <= FixedPoint2.Zero)
|
||||||
@@ -690,7 +693,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
var origVol = Volume;
|
var origVol = Volume;
|
||||||
var effVol = Volume.Value;
|
var effVol = Volume.Value;
|
||||||
newSolution = new Solution(Contents.Count) { Temperature = Temperature };
|
newSolution = new Solution(Contents.Count) { Temperature = Temperature };
|
||||||
var remaining = (long) toTake.Value;
|
var remaining = (long)toTake.Value;
|
||||||
|
|
||||||
for (var i = Contents.Count - 1; i >= 0; i--) // iterate backwards because of remove swap.
|
for (var i = Contents.Count - 1; i >= 0; i--) // iterate backwards because of remove swap.
|
||||||
{
|
{
|
||||||
@@ -706,7 +709,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var splitQuantity = FixedPoint2.FromCents((int) split);
|
var splitQuantity = FixedPoint2.FromCents((int)split);
|
||||||
var newQuantity = quantity - splitQuantity;
|
var newQuantity = quantity - splitQuantity;
|
||||||
|
|
||||||
DebugTools.Assert(newQuantity >= 0);
|
DebugTools.Assert(newQuantity >= 0);
|
||||||
@@ -753,7 +756,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
|
|
||||||
var effVol = Volume.Value;
|
var effVol = Volume.Value;
|
||||||
Volume -= toTake;
|
Volume -= toTake;
|
||||||
var remaining = (long) toTake.Value;
|
var remaining = (long)toTake.Value;
|
||||||
for (var i = Contents.Count - 1; i >= 0; i--)// iterate backwards because of remove swap.
|
for (var i = Contents.Count - 1; i >= 0; i--)// iterate backwards because of remove swap.
|
||||||
{
|
{
|
||||||
var (reagent, quantity) = Contents[i];
|
var (reagent, quantity) = Contents[i];
|
||||||
@@ -768,7 +771,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var splitQuantity = FixedPoint2.FromCents((int) split);
|
var splitQuantity = FixedPoint2.FromCents((int)split);
|
||||||
var newQuantity = quantity - splitQuantity;
|
var newQuantity = quantity - splitQuantity;
|
||||||
|
|
||||||
if (newQuantity > FixedPoint2.Zero)
|
if (newQuantity > FixedPoint2.Zero)
|
||||||
|
|||||||
@@ -588,7 +588,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
|||||||
/// Adds a solution to the container, if it can fully fit.
|
/// Adds a solution to the container, if it can fully fit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="targetUid">entity holding targetSolution</param>
|
/// <param name="targetUid">entity holding targetSolution</param>
|
||||||
/// <param name="targetSolution">entity holding targetSolution</param>
|
/// <param name="targetSolution">entity holding targetSolution</param>
|
||||||
/// <param name="toAdd">solution being added</param>
|
/// <param name="toAdd">solution being added</param>
|
||||||
/// <returns>If the solution could be added.</returns>
|
/// <returns>If the solution could be added.</returns>
|
||||||
public bool TryAddSolution(Entity<SolutionComponent> soln, Solution toAdd)
|
public bool TryAddSolution(Entity<SolutionComponent> soln, Solution toAdd)
|
||||||
@@ -606,40 +606,44 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds as much of a solution to a container as can fit.
|
/// Adds as much of a solution to a container as can fit and updates the container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="targetUid">The entity containing <paramref cref="targetSolution"/></param>
|
/// <param name="targetUid">The entity containing <paramref cref="targetSolution"/></param>
|
||||||
/// <param name="targetSolution">The solution being added to.</param>
|
/// <param name="targetSolution">The solution being added to.</param>
|
||||||
/// <param name="toAdd">The solution being added to <paramref cref="targetSolution"/></param>
|
/// <param name="toAdd">The solution being added to <paramref cref="targetSolution"/>. This solution is not modified.</param>
|
||||||
/// <returns>The quantity of the solution actually added.</returns>
|
/// <returns>The quantity of the solution actually added.</returns>
|
||||||
public FixedPoint2 AddSolution(Entity<SolutionComponent> soln, Solution toAdd)
|
public FixedPoint2 AddSolution(Entity<SolutionComponent> soln, Solution toAdd)
|
||||||
{
|
{
|
||||||
var (uid, comp) = soln;
|
var solution = soln.Comp.Solution;
|
||||||
var solution = comp.Solution;
|
|
||||||
|
|
||||||
if (toAdd.Volume == FixedPoint2.Zero)
|
if (toAdd.Volume == FixedPoint2.Zero)
|
||||||
return FixedPoint2.Zero;
|
return FixedPoint2.Zero;
|
||||||
|
|
||||||
var quantity = FixedPoint2.Max(FixedPoint2.Zero, FixedPoint2.Min(toAdd.Volume, solution.AvailableVolume));
|
var quantity = FixedPoint2.Max(FixedPoint2.Zero, FixedPoint2.Min(toAdd.Volume, solution.AvailableVolume));
|
||||||
if (quantity < toAdd.Volume)
|
if (quantity < toAdd.Volume)
|
||||||
TryTransferSolution(soln, toAdd, quantity);
|
{
|
||||||
|
// TODO: This should be made into a function that directly transfers reagents.
|
||||||
|
// Currently this is quite inefficient.
|
||||||
|
solution.AddSolution(toAdd.Clone().SplitSolution(quantity), PrototypeManager);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ForceAddSolution(soln, toAdd);
|
solution.AddSolution(toAdd, PrototypeManager);
|
||||||
|
|
||||||
|
UpdateChemicals(soln);
|
||||||
return quantity;
|
return quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a solution to a container and updates the container.
|
/// Adds a solution to a container and updates the container.
|
||||||
|
/// This can exceed the maximum volume of the solution added to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="targetUid">The entity containing <paramref cref="targetSolution"/></param>
|
/// <param name="targetUid">The entity containing <paramref cref="targetSolution"/></param>
|
||||||
/// <param name="targetSolution">The solution being added to.</param>
|
/// <param name="targetSolution">The solution being added to.</param>
|
||||||
/// <param name="toAdd">The solution being added to <paramref cref="targetSolution"/></param>
|
/// <param name="toAdd">The solution being added to <paramref cref="targetSolution"/>. This solution is not modified.</param>
|
||||||
/// <returns>Whether any reagents were added to the solution.</returns>
|
/// <returns>Whether any reagents were added to the solution.</returns>
|
||||||
public bool ForceAddSolution(Entity<SolutionComponent> soln, Solution toAdd)
|
public bool ForceAddSolution(Entity<SolutionComponent> soln, Solution toAdd)
|
||||||
{
|
{
|
||||||
var (uid, comp) = soln;
|
var solution = soln.Comp.Solution;
|
||||||
var solution = comp.Solution;
|
|
||||||
|
|
||||||
if (toAdd.Volume == FixedPoint2.Zero)
|
if (toAdd.Volume == FixedPoint2.Zero)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Content.Shared.Weapons.Ranged;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This component is used to indicate an entity is shootable from a hitscan weapon.
|
||||||
|
/// This is placed on the laser entity being shot, not the gun itself.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class HitscanAmmoComponent : Component, IShootable;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Content.Shared.Damage;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hitscan entities that have this component will do the damage specified to hit targets (Who didn't reflect it).
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class HitscanBasicDamageComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// How much damage the hitscan weapon will do when hitting a target.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public DamageSpecifier Damage;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// System or basic "effects" like sounds and hit markers for hitscans.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class HitscanBasicEffectsComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This will turn hit entities this color briefly.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public Color? HitColor = Color.Red;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sound that plays upon the thing being hit.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public SoundSpecifier? Sound;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Force the hitscan sound to play rather than playing the entity's override sound (if it exists).
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool ForceSound;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using Content.Shared.Physics;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A basic raycast system that will shoot in a straight line when triggered.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class HitscanBasicRaycastComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum distance the raycast will travel before giving up. Reflections will reset the distance traveled
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float MaxDistance = 20.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The collision mask the hitscan ray uses to collide with other objects. See the enum for more information
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public CollisionGroup CollisionMask = CollisionGroup.Opaque;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides basic visuals for hitscan weapons - works with <see cref="HitscanBasicRaycastComponent"/>
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class HitscanBasicVisualsComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The muzzle flash from the hitscan weapon.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public SpriteSpecifier? MuzzleFlash;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The "travel" sprite, this gets repeated until it hits the target.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public SpriteSpecifier? TravelFlash;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sprite that gets shown on the impact of the laser.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public SpriteSpecifier? ImpactFlash;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Content.Shared.Weapons.Reflect;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hitscan entities with this component will get reflected by certain things (E.G energy swords).
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class HitscanReflectComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The reflective type, will only reflect from entities that have a matching reflection type.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public ReflectType ReflectiveType = ReflectType.Energy;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum number of reflections the laser will make. <see cref="CurrentReflections"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int MaxReflections = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current number of times this hitscan entity was reflected. Will not be more than <see cref="MaxReflections"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int CurrentReflections;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hitscan entities that have this component will deal stamina damage to the target.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class HitscanStaminaDamageComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// How much stamania damage the hitscan weapon will do when hitting a target.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float StaminaDamage = 10.0f;
|
||||||
|
}
|
||||||
110
Content.Shared/Weapons/Hitscan/Events/HitscanEvents.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the hitscan entity when "fired". This could be from reflections or from the gun. This is the catalyst that
|
||||||
|
/// other systems will listen for to actually shoot the gun.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct HitscanTraceEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Location the hitscan was fired from.
|
||||||
|
/// </summary>
|
||||||
|
public EntityCoordinates FromCoordinates;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Direction that the ray was fired towards.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 ShotDirection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gun that was fired - this will always be the original weapon even if reflected.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid Gun;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Player who shot the gun, if null the gun was fired by itself.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid? Shooter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target that was being aimed at (Not necessarily hit).
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid? Target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All data known data for when a hitscan is actually fired.
|
||||||
|
/// </summary>
|
||||||
|
public record struct HitscanRaycastFiredData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Direction that the ray was fired towards.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 ShotDirection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The entity that got hit, if null the raycast didn't hit anyone.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid? HitEntity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gun that fired the raycast.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid Gun;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Player who shot the gun, if null the gun was fired by itself.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid? Shooter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to hit the targeted entity with a hitscan laser. Stuff like the reflection system should listen for this and
|
||||||
|
/// cancel the event if the laser was reflected.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public struct AttemptHitscanRaycastFiredEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Data for the hitscan that was fired.
|
||||||
|
/// </summary>
|
||||||
|
public HitscanRaycastFiredData Data;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set to true the hitscan is cancelled (e.g. due to reflection).
|
||||||
|
/// Cancelled hitscans should not apply damage or trigger follow-up effects.
|
||||||
|
/// </summary>
|
||||||
|
public bool Cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Results of a hitscan raycast and will be raised on the raycast entity on itself. Stuff like the damage system should
|
||||||
|
/// listen for this. At this point we KNOW the laser hit the entity.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public struct HitscanRaycastFiredEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Data for the hitscan that was fired.
|
||||||
|
/// </summary>
|
||||||
|
public HitscanRaycastFiredData Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct HitscanDamageDealtEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Target that was dealt damage.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid Target;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of damage that the target was dealt.
|
||||||
|
/// </summary>
|
||||||
|
public DamageSpecifier DamageDealt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Events;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Systems;
|
||||||
|
|
||||||
|
public sealed class HitscanBasicDamageSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly DamageableSystem _damage = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HitscanBasicDamageComponent, HitscanRaycastFiredEvent>(OnHitscanHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHitscanHit(Entity<HitscanBasicDamageComponent> ent, ref HitscanRaycastFiredEvent args)
|
||||||
|
{
|
||||||
|
if (args.Data.HitEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var dmg = ent.Comp.Damage * _damage.UniversalHitscanDamageModifier;
|
||||||
|
|
||||||
|
var damageDealt = _damage.TryChangeDamage(args.Data.HitEntity, dmg, origin: args.Data.Gun);
|
||||||
|
|
||||||
|
if (damageDealt == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var damageEvent = new HitscanDamageDealtEvent
|
||||||
|
{
|
||||||
|
Target = args.Data.HitEntity.Value,
|
||||||
|
DamageDealt = damageDealt,
|
||||||
|
};
|
||||||
|
|
||||||
|
RaiseLocalEvent(ent, ref damageEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Effects;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Events;
|
||||||
|
using Content.Shared.Weapons.Ranged.Systems;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Systems;
|
||||||
|
|
||||||
|
public sealed class HitscanBasicEffectsSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
|
||||||
|
[Dependency] private readonly SharedGunSystem _gun = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HitscanBasicEffectsComponent, HitscanDamageDealtEvent>(OnHitscanDamageDealt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHitscanDamageDealt(Entity<HitscanBasicEffectsComponent> ent, ref HitscanDamageDealtEvent args)
|
||||||
|
{
|
||||||
|
if (Deleted(args.Target))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ent.Comp.HitColor != null && args.DamageDealt.GetTotal() != 0)
|
||||||
|
{
|
||||||
|
_color.RaiseEffect(ent.Comp.HitColor.Value,
|
||||||
|
new List<EntityUid> { args.Target },
|
||||||
|
Filter.Pvs(args.Target, entityManager: EntityManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
_gun.PlayImpactSound(args.Target, args.DamageDealt, ent.Comp.Sound, ent.Comp.ForceSound);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.Administration.Logs;
|
||||||
|
using Content.Shared.Damage.Components;
|
||||||
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Events;
|
||||||
|
using Content.Shared.Weapons.Ranged.Systems;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
|
using Robust.Shared.Physics.Systems;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Systems;
|
||||||
|
|
||||||
|
public sealed class HitscanBasicRaycastSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||||
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||||
|
[Dependency] private readonly ISharedAdminLogManager _log = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
|
||||||
|
private EntityQuery<HitscanBasicVisualsComponent> _visualsQuery;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_visualsQuery = GetEntityQuery<HitscanBasicVisualsComponent>();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HitscanBasicRaycastComponent, HitscanTraceEvent>(OnHitscanFired);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHitscanFired(Entity<HitscanBasicRaycastComponent> ent, ref HitscanTraceEvent args)
|
||||||
|
{
|
||||||
|
var shooter = args.Shooter ?? args.Gun;
|
||||||
|
var mapCords = _transform.ToMapCoordinates(args.FromCoordinates);
|
||||||
|
var ray = new CollisionRay(mapCords.Position, args.ShotDirection, (int) ent.Comp.CollisionMask);
|
||||||
|
var rayCastResults = _physics.IntersectRay(mapCords.MapId, ray, ent.Comp.MaxDistance, shooter, false);
|
||||||
|
|
||||||
|
var target = args.Target;
|
||||||
|
// If you are in a container, use the raycast result
|
||||||
|
// Otherwise:
|
||||||
|
// 1.) Hit the first entity that you targeted.
|
||||||
|
// 2.) Hit the first entity that doesn't require you to aim at it specifically to be hit.
|
||||||
|
var result = _container.IsEntityOrParentInContainer(shooter)
|
||||||
|
? rayCastResults.FirstOrNull()
|
||||||
|
: rayCastResults.FirstOrNull(hit => hit.HitEntity == target
|
||||||
|
|| CompOrNull<RequireProjectileTargetComponent>(hit.HitEntity)?.Active != true);
|
||||||
|
|
||||||
|
var distanceTried = result?.Distance ?? ent.Comp.MaxDistance;
|
||||||
|
|
||||||
|
// Do visuals without an event. They should always happen and putting it on the attempt event is weird!
|
||||||
|
// If more stuff gets added here, it should probably be turned into an event.
|
||||||
|
FireEffects(args.FromCoordinates, distanceTried, args.ShotDirection.ToAngle(), ent.Owner);
|
||||||
|
|
||||||
|
// Admin logging
|
||||||
|
if (result?.HitEntity != null)
|
||||||
|
{
|
||||||
|
_log.Add(LogType.HitScanHit,
|
||||||
|
$"{ToPrettyString(shooter):user} hit {ToPrettyString(result.Value.HitEntity):target}"
|
||||||
|
+ $" using {ToPrettyString(args.Gun):entity}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = new HitscanRaycastFiredData
|
||||||
|
{
|
||||||
|
ShotDirection = args.ShotDirection,
|
||||||
|
Gun = args.Gun,
|
||||||
|
Shooter = args.Shooter,
|
||||||
|
HitEntity = result?.HitEntity,
|
||||||
|
};
|
||||||
|
|
||||||
|
var attemptEvent = new AttemptHitscanRaycastFiredEvent { Data = data };
|
||||||
|
RaiseLocalEvent(ent, ref attemptEvent);
|
||||||
|
|
||||||
|
if (attemptEvent.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var hitEvent = new HitscanRaycastFiredEvent { Data = data };
|
||||||
|
RaiseLocalEvent(ent, ref hitEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create visual effects for the fired hitscan weapon.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fromCoordinates">Location to start the effect.</param>
|
||||||
|
/// <param name="distance">Distance of the hitscan shot.</param>
|
||||||
|
/// <param name="shotAngle">Angle of the shot.</param>
|
||||||
|
/// <param name="hitscanUid">The hitscan entity itself.</param>
|
||||||
|
private void FireEffects(EntityCoordinates fromCoordinates, float distance, Angle shotAngle, EntityUid hitscanUid)
|
||||||
|
{
|
||||||
|
if (distance == 0 || !_visualsQuery.TryComp(hitscanUid, out var vizComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sprites = new List<(NetCoordinates coordinates, Angle angle, SpriteSpecifier sprite, float scale)>();
|
||||||
|
var fromXform = Transform(fromCoordinates.EntityId);
|
||||||
|
|
||||||
|
// We'll get the effects relative to the grid / map of the firer
|
||||||
|
// Look you could probably optimise this a bit with redundant transforms at this point.
|
||||||
|
|
||||||
|
var gridUid = fromXform.GridUid;
|
||||||
|
if (gridUid != fromCoordinates.EntityId && TryComp(gridUid, out TransformComponent? gridXform))
|
||||||
|
{
|
||||||
|
var (_, gridRot, gridInvMatrix) = _transform.GetWorldPositionRotationInvMatrix(gridXform);
|
||||||
|
var map = _transform.ToMapCoordinates(fromCoordinates);
|
||||||
|
fromCoordinates = new EntityCoordinates(gridUid.Value, Vector2.Transform(map.Position, gridInvMatrix));
|
||||||
|
shotAngle -= gridRot;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shotAngle -= _transform.GetWorldRotation(fromXform);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distance >= 1f)
|
||||||
|
{
|
||||||
|
if (vizComp.MuzzleFlash != null)
|
||||||
|
{
|
||||||
|
var coords = fromCoordinates.Offset(shotAngle.ToVec().Normalized() / 2);
|
||||||
|
var netCoords = GetNetCoordinates(coords);
|
||||||
|
|
||||||
|
sprites.Add((netCoords, shotAngle, vizComp.MuzzleFlash, 1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vizComp.TravelFlash != null)
|
||||||
|
{
|
||||||
|
var coords = fromCoordinates.Offset(shotAngle.ToVec() * (distance + 0.5f) / 2);
|
||||||
|
var netCoords = GetNetCoordinates(coords);
|
||||||
|
|
||||||
|
sprites.Add((netCoords, shotAngle, vizComp.TravelFlash, distance - 1.5f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vizComp.ImpactFlash != null)
|
||||||
|
{
|
||||||
|
var coords = fromCoordinates.Offset(shotAngle.ToVec() * distance);
|
||||||
|
var netCoords = GetNetCoordinates(coords);
|
||||||
|
|
||||||
|
sprites.Add((netCoords, shotAngle.FlipPositive(), vizComp.ImpactFlash, 1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sprites.Count > 0)
|
||||||
|
{
|
||||||
|
RaiseNetworkEvent(new SharedGunSystem.HitscanEvent
|
||||||
|
{
|
||||||
|
Sprites = sprites,
|
||||||
|
}, Filter.Pvs(fromCoordinates, entityMan: EntityManager));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Events;
|
||||||
|
using Content.Shared.Weapons.Ranged.Events;
|
||||||
|
using Content.Shared.Weapons.Reflect;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Systems;
|
||||||
|
|
||||||
|
public sealed class HitscanReflectSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HitscanReflectComponent, AttemptHitscanRaycastFiredEvent>(OnHitscanHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHitscanHit(Entity<HitscanReflectComponent> hitscan, ref AttemptHitscanRaycastFiredEvent args)
|
||||||
|
{
|
||||||
|
var data = args.Data;
|
||||||
|
|
||||||
|
if (hitscan.Comp.ReflectiveType == ReflectType.None || data.HitEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (hitscan.Comp.CurrentReflections >= hitscan.Comp.MaxReflections)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ev = new HitScanReflectAttemptEvent(data.Shooter ?? data.Gun, data.Gun, hitscan.Comp.ReflectiveType, data.ShotDirection, false);
|
||||||
|
RaiseLocalEvent(data.HitEntity.Value, ref ev);
|
||||||
|
|
||||||
|
if (!ev.Reflected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hitscan.Comp.CurrentReflections++;
|
||||||
|
|
||||||
|
args.Cancelled = true;
|
||||||
|
|
||||||
|
var fromEffect = Transform(data.HitEntity.Value).Coordinates;
|
||||||
|
|
||||||
|
var hitFiredEvent = new HitscanTraceEvent
|
||||||
|
{
|
||||||
|
FromCoordinates = fromEffect,
|
||||||
|
ShotDirection = ev.Direction,
|
||||||
|
Gun = data.Gun,
|
||||||
|
Shooter = data.HitEntity.Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
RaiseLocalEvent(hitscan, ref hitFiredEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Content.Shared/Weapons/Hitscan/Systems/HitscanStunSystem.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Content.Shared.Damage.Systems;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Components;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Events;
|
||||||
|
|
||||||
|
namespace Content.Shared.Weapons.Hitscan.Systems;
|
||||||
|
|
||||||
|
public sealed class HitscanStunSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedStaminaSystem _stamina = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HitscanStaminaDamageComponent, HitscanRaycastFiredEvent>(OnHitscanHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHitscanHit(Entity<HitscanStaminaDamageComponent> hitscan, ref HitscanRaycastFiredEvent args)
|
||||||
|
{
|
||||||
|
if (args.Data.HitEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_stamina.TakeStaminaDamage(args.Data.HitEntity.Value, hitscan.Comp.StaminaDamage, source: args.Data.Shooter ?? args.Data.Gun);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Shared.Weapons.Ranged.Components;
|
namespace Content.Shared.Weapons.Ranged.Components;
|
||||||
|
|
||||||
[RegisterComponent, NetworkedComponent]
|
[RegisterComponent, NetworkedComponent]
|
||||||
public sealed partial class HitscanBatteryAmmoProviderComponent : BatteryAmmoProviderComponent
|
public sealed partial class HitscanBatteryAmmoProviderComponent : BatteryAmmoProviderComponent
|
||||||
{
|
{
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<HitscanPrototype>))]
|
[DataField("proto", required: true)]
|
||||||
public string Prototype = default!;
|
public EntProtoId HitscanEntityProto;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Physics;
|
|
||||||
using Content.Shared.Weapons.Reflect;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Shared.Weapons.Ranged;
|
|
||||||
|
|
||||||
[Prototype]
|
|
||||||
public sealed partial class HitscanPrototype : IPrototype, IShootable
|
|
||||||
{
|
|
||||||
[ViewVariables]
|
|
||||||
[IdDataField]
|
|
||||||
public string ID { get; private set; } = default!;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("staminaDamage")]
|
|
||||||
public float StaminaDamage;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("damage")]
|
|
||||||
public DamageSpecifier? Damage;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly), DataField("muzzleFlash")]
|
|
||||||
public SpriteSpecifier? MuzzleFlash;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly), DataField("travelFlash")]
|
|
||||||
public SpriteSpecifier? TravelFlash;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly), DataField("impactFlash")]
|
|
||||||
public SpriteSpecifier? ImpactFlash;
|
|
||||||
|
|
||||||
[DataField("collisionMask")]
|
|
||||||
public int CollisionMask = (int) CollisionGroup.Opaque;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// What we count as for reflection.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("reflective")] public ReflectType Reflective = ReflectType.Energy;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sound that plays upon the thing being hit.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("sound")]
|
|
||||||
public SoundSpecifier? Sound;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Force the hitscan sound to play rather than potentially playing the entity's sound.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("forceSound")]
|
|
||||||
public bool ForceSound;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Try not to set this too high.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("maxLength")]
|
|
||||||
public float MaxLength = 20f;
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ using Content.Shared.Damage;
|
|||||||
using Content.Shared.Damage.Events;
|
using Content.Shared.Damage.Events;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Projectiles;
|
using Content.Shared.Projectiles;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Components;
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
using Content.Shared.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Weapons.Ranged.Events;
|
using Content.Shared.Weapons.Ranged.Events;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
@@ -80,11 +81,10 @@ public abstract partial class SharedGunSystem
|
|||||||
{
|
{
|
||||||
if (component is ProjectileBatteryAmmoProviderComponent battery)
|
if (component is ProjectileBatteryAmmoProviderComponent battery)
|
||||||
{
|
{
|
||||||
if (ProtoManager.Index<EntityPrototype>(battery.Prototype)
|
if (ProtoManager.Index<EntityPrototype>(battery.Prototype).Components
|
||||||
.Components
|
|
||||||
.TryGetValue(Factory.GetComponentName<ProjectileComponent>(), out var projectile))
|
.TryGetValue(Factory.GetComponentName<ProjectileComponent>(), out var projectile))
|
||||||
{
|
{
|
||||||
var p = (ProjectileComponent)projectile.Component;
|
var p = (ProjectileComponent) projectile.Component;
|
||||||
|
|
||||||
if (!p.Damage.Empty)
|
if (!p.Damage.Empty)
|
||||||
{
|
{
|
||||||
@@ -97,8 +97,11 @@ public abstract partial class SharedGunSystem
|
|||||||
|
|
||||||
if (component is HitscanBatteryAmmoProviderComponent hitscan)
|
if (component is HitscanBatteryAmmoProviderComponent hitscan)
|
||||||
{
|
{
|
||||||
var dmg = ProtoManager.Index<HitscanPrototype>(hitscan.Prototype).Damage;
|
var dmg = ProtoManager.Index(hitscan.HitscanEntityProto);
|
||||||
return dmg == null ? dmg : dmg * Damageable.UniversalHitscanDamageModifier;
|
if (!dmg.TryGetComponent<HitscanBasicDamageComponent>(out var basicDamageComp, Factory))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return basicDamageComp.Damage * Damageable.UniversalHitscanDamageModifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -155,7 +158,8 @@ public abstract partial class SharedGunSystem
|
|||||||
var ent = Spawn(proj.Prototype, coordinates);
|
var ent = Spawn(proj.Prototype, coordinates);
|
||||||
return (ent, EnsureShootable(ent));
|
return (ent, EnsureShootable(ent));
|
||||||
case HitscanBatteryAmmoProviderComponent hitscan:
|
case HitscanBatteryAmmoProviderComponent hitscan:
|
||||||
return (null, ProtoManager.Index<HitscanPrototype>(hitscan.Prototype));
|
var hitscanEnt = Spawn(hitscan.HitscanEntityProto);
|
||||||
|
return (hitscanEnt, EnsureShootable(hitscanEnt));
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ using Content.Shared.Tag;
|
|||||||
using Content.Shared.Throwing;
|
using Content.Shared.Throwing;
|
||||||
using Content.Shared.Timing;
|
using Content.Shared.Timing;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
|
using Content.Shared.Weapons.Hitscan.Components;
|
||||||
using Content.Shared.Weapons.Melee;
|
using Content.Shared.Weapons.Melee;
|
||||||
using Content.Shared.Weapons.Melee.Events;
|
using Content.Shared.Weapons.Melee.Events;
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
using Content.Shared.Weapons.Ranged.Components;
|
||||||
@@ -500,6 +501,9 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
if (TryComp<CartridgeAmmoComponent>(uid, out var cartridge))
|
if (TryComp<CartridgeAmmoComponent>(uid, out var cartridge))
|
||||||
return cartridge;
|
return cartridge;
|
||||||
|
|
||||||
|
if (TryComp<HitscanAmmoComponent>(uid, out var hitscanAmmo))
|
||||||
|
return hitscanAmmo;
|
||||||
|
|
||||||
return EnsureComp<AmmoComponent>(uid);
|
return EnsureComp<AmmoComponent>(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,6 +618,8 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
|
|
||||||
protected abstract void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null);
|
protected abstract void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null);
|
||||||
|
|
||||||
|
public abstract void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used for animated effects on the client.
|
/// Used for animated effects on the client.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ public sealed partial class ReflectComponent : Component
|
|||||||
public SoundSpecifier? SoundOnReflect = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg", AudioParams.Default.WithVariation(0.05f));
|
public SoundSpecifier? SoundOnReflect = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg", AudioParams.Default.WithVariation(0.05f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for both the projectiles being reflected and the entities reflecting. If there is ever overlap between the
|
||||||
|
/// reflection types, the projectile will be reflected.
|
||||||
|
/// </summary>
|
||||||
[Flags, Serializable, NetSerializable]
|
[Flags, Serializable, NetSerializable]
|
||||||
public enum ReflectType : byte
|
public enum ReflectType : byte
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,3 +28,14 @@
|
|||||||
copyright: "raptorgoesextinct.wav by Nerdwizard78"
|
copyright: "raptorgoesextinct.wav by Nerdwizard78"
|
||||||
source: "https://freesound.org/people/Nerdwizard78/sounds/643928/"
|
source: "https://freesound.org/people/Nerdwizard78/sounds/643928/"
|
||||||
|
|
||||||
|
- files:
|
||||||
|
- vox_chitter.ogg
|
||||||
|
license: "CC0-1.0"
|
||||||
|
copyright: "dinosaur.wav by JhennaSide"
|
||||||
|
source: "https://freesound.org/people/JhennaSide/sounds/455906/"
|
||||||
|
|
||||||
|
- files:
|
||||||
|
- vox_click.ogg
|
||||||
|
license: "CC0-1.0"
|
||||||
|
copyright: "Storks bill-clapping by Breviceps"
|
||||||
|
source: "https://freesound.org/people/Breviceps/sounds/705861/"
|
||||||
|
|||||||
BIN
Resources/Audio/Voice/Vox/vox_chitter.ogg
Normal file
BIN
Resources/Audio/Voice/Vox/vox_click.ogg
Normal file
@@ -1,63 +1,4 @@
|
|||||||
Entries:
|
Entries:
|
||||||
- author: ScarKy0
|
|
||||||
changes:
|
|
||||||
- message: Increased the health of uranium windows, making them a reasonable upgrade
|
|
||||||
over regular plasma.
|
|
||||||
type: Tweak
|
|
||||||
id: 8611
|
|
||||||
time: '2025-06-03T18:20:09.0000000+00:00'
|
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/38040
|
|
||||||
- author: Moomoobeef
|
|
||||||
changes:
|
|
||||||
- message: Nanotask printouts now look much nicer!
|
|
||||||
type: Tweak
|
|
||||||
id: 8612
|
|
||||||
time: '2025-06-04T08:16:15.0000000+00:00'
|
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/37805
|
|
||||||
- author: TheCactus, ilovehans10
|
|
||||||
changes:
|
|
||||||
- message: Added text highlighting.
|
|
||||||
type: Add
|
|
||||||
id: 8613
|
|
||||||
time: '2025-06-04T10:12:37.0000000+00:00'
|
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/31442
|
|
||||||
- author: SlamBamActionman
|
|
||||||
changes:
|
|
||||||
- message: Diona can now root, keeping them from slipping but absorbing fluids from
|
|
||||||
the ground.
|
|
||||||
type: Add
|
|
||||||
id: 8614
|
|
||||||
time: '2025-06-04T10:53:00.0000000+00:00'
|
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/32782
|
|
||||||
- author: spanky-spanky
|
|
||||||
changes:
|
|
||||||
- message: Evac repair lockers, which contain equipment to repair shuttle bombing.
|
|
||||||
type: Add
|
|
||||||
id: 8615
|
|
||||||
time: '2025-06-05T06:08:38.0000000+00:00'
|
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/38075
|
|
||||||
- author: slarticodefast
|
|
||||||
changes:
|
|
||||||
- message: Fixed the game not launching in compability mode.
|
|
||||||
type: Fix
|
|
||||||
id: 8616
|
|
||||||
time: '2025-06-05T12:52:39.0000000+00:00'
|
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/38080
|
|
||||||
- author: Orsoniks
|
|
||||||
changes:
|
|
||||||
- message: Added more in-hand sprites for food items
|
|
||||||
type: Add
|
|
||||||
id: 8617
|
|
||||||
time: '2025-06-05T21:06:38.0000000+00:00'
|
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/38024
|
|
||||||
- author: chromiumboy
|
|
||||||
changes:
|
|
||||||
- message: Holopads now provide less visual coverage to station AIs (1 tile radius,
|
|
||||||
down from 7.5). Holopads must also be anchored to the floor to provide vision.
|
|
||||||
type: Tweak
|
|
||||||
id: 8618
|
|
||||||
time: '2025-06-05T23:15:55.0000000+00:00'
|
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/38059
|
|
||||||
- author: Simyon
|
- author: Simyon
|
||||||
changes:
|
changes:
|
||||||
- message: Sleeper agents no longer have different codewords than round-start traitors.
|
- message: Sleeper agents no longer have different codewords than round-start traitors.
|
||||||
@@ -3933,3 +3874,66 @@
|
|||||||
id: 9112
|
id: 9112
|
||||||
time: '2025-10-16T18:47:31.0000000+00:00'
|
time: '2025-10-16T18:47:31.0000000+00:00'
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/35636
|
url: https://github.com/space-wizards/space-station-14/pull/35636
|
||||||
|
- author: Wolfkey-SomeoneElseTookMyUsername
|
||||||
|
changes:
|
||||||
|
- message: The recipes for grilled cheese, cotton buns, cotton cakes, and cotton
|
||||||
|
grilled cheese are now in the correct category in the guidebook
|
||||||
|
type: Fix
|
||||||
|
id: 9113
|
||||||
|
time: '2025-10-17T18:56:01.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40949
|
||||||
|
- author: TrixxedHeart
|
||||||
|
changes:
|
||||||
|
- message: Added Enter/Leave Genpop access to Security by default, allowing them
|
||||||
|
to be able to fix Genpop turnstiles with the access configurator if they are
|
||||||
|
destroyed.
|
||||||
|
type: Tweak
|
||||||
|
id: 9114
|
||||||
|
time: '2025-10-18T00:28:44.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/39515
|
||||||
|
- author: TrixxedHeart
|
||||||
|
changes:
|
||||||
|
- message: Added Vox Chitter and Clicking emotes
|
||||||
|
type: Add
|
||||||
|
id: 9115
|
||||||
|
time: '2025-10-18T00:28:44.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40878
|
||||||
|
- author: Kittygyat
|
||||||
|
changes:
|
||||||
|
- message: Added a new generic Artistry borg module!
|
||||||
|
type: Add
|
||||||
|
id: 9116
|
||||||
|
time: '2025-10-18T07:13:42.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/39679
|
||||||
|
- author: Hitlinemoss
|
||||||
|
changes:
|
||||||
|
- message: Folders and clipboards now recycle into sensible material components,
|
||||||
|
rather than only cardboard.
|
||||||
|
type: Fix
|
||||||
|
- message: Clipboards and plastic clipboards require slightly more steel to produce
|
||||||
|
in autolathes.
|
||||||
|
type: Tweak
|
||||||
|
id: 9117
|
||||||
|
time: '2025-10-18T09:25:00.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40954
|
||||||
|
- author: PicklOH
|
||||||
|
changes:
|
||||||
|
- message: Rags can no longer be used to remove evidence.
|
||||||
|
type: Remove
|
||||||
|
id: 9118
|
||||||
|
time: '2025-10-18T13:49:54.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40818
|
||||||
|
- author: MissKay1994
|
||||||
|
changes:
|
||||||
|
- message: Vox organs now have unique sprites.
|
||||||
|
type: Add
|
||||||
|
id: 9119
|
||||||
|
time: '2025-10-18T17:07:52.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40555
|
||||||
|
- author: Hitlinemoss
|
||||||
|
changes:
|
||||||
|
- message: Cargo orders containing beverages now ship in freezers.
|
||||||
|
type: Tweak
|
||||||
|
id: 9120
|
||||||
|
time: '2025-10-18T17:20:44.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40955
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
meta:
|
meta:
|
||||||
format: 7
|
format: 7
|
||||||
category: Map
|
category: Map
|
||||||
engineVersion: 267.2.0
|
engineVersion: 267.3.0
|
||||||
forkId: ""
|
forkId: ""
|
||||||
forkVersion: ""
|
forkVersion: ""
|
||||||
time: 10/08/2025 23:22:51
|
time: 10/16/2025 13:06:44
|
||||||
entityCount: 36080
|
entityCount: 36082
|
||||||
maps:
|
maps:
|
||||||
- 1
|
- 1
|
||||||
grids:
|
grids:
|
||||||
@@ -186578,6 +186578,29 @@ entities:
|
|||||||
parent: 13329
|
parent: 13329
|
||||||
- type: Fixtures
|
- type: Fixtures
|
||||||
fixtures: {}
|
fixtures: {}
|
||||||
|
- proto: SignArrivals
|
||||||
|
entities:
|
||||||
|
- uid: 9339
|
||||||
|
components:
|
||||||
|
- type: Transform
|
||||||
|
pos: -37.5,-27.5
|
||||||
|
parent: 13329
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures: {}
|
||||||
|
- uid: 9340
|
||||||
|
components:
|
||||||
|
- type: Transform
|
||||||
|
pos: -24.5,-7.5
|
||||||
|
parent: 13329
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures: {}
|
||||||
|
- uid: 10642
|
||||||
|
components:
|
||||||
|
- type: Transform
|
||||||
|
pos: -24.5,-23.5
|
||||||
|
parent: 13329
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures: {}
|
||||||
- proto: SignAtmos
|
- proto: SignAtmos
|
||||||
entities:
|
entities:
|
||||||
- uid: 26468
|
- uid: 26468
|
||||||
@@ -188492,14 +188515,6 @@ entities:
|
|||||||
fixtures: {}
|
fixtures: {}
|
||||||
- proto: SignShipDock
|
- proto: SignShipDock
|
||||||
entities:
|
entities:
|
||||||
- uid: 13601
|
|
||||||
components:
|
|
||||||
- type: Transform
|
|
||||||
rot: -1.5707963267948966 rad
|
|
||||||
pos: -37.5,-27.5
|
|
||||||
parent: 13329
|
|
||||||
- type: Fixtures
|
|
||||||
fixtures: {}
|
|
||||||
- uid: 19947
|
- uid: 19947
|
||||||
components:
|
components:
|
||||||
- type: Transform
|
- type: Transform
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
tags:
|
tags:
|
||||||
- Trash
|
- Trash
|
||||||
isSpecialDigestibleExclusive: false
|
isSpecialDigestibleExclusive: false
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Mobs/Species/Vox/organs.rsi
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: OrganHumanLiver
|
parent: OrganHumanLiver
|
||||||
@@ -37,6 +39,8 @@
|
|||||||
components:
|
components:
|
||||||
- type: Metabolizer
|
- type: Metabolizer
|
||||||
metabolizerTypes: [Vox]
|
metabolizerTypes: [Vox]
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Mobs/Species/Vox/organs.rsi
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: OrganHumanHeart
|
parent: OrganHumanHeart
|
||||||
@@ -46,3 +50,49 @@
|
|||||||
components:
|
components:
|
||||||
- type: Metabolizer
|
- type: Metabolizer
|
||||||
metabolizerTypes: [Vox]
|
metabolizerTypes: [Vox]
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Mobs/Species/Vox/organs.rsi
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: OrganHumanKidneys
|
||||||
|
id: OrganVoxKidneys
|
||||||
|
name: kidney
|
||||||
|
description: "Smells flammable."
|
||||||
|
components:
|
||||||
|
- type: Metabolizer
|
||||||
|
metabolizerTypes: [Vox]
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Mobs/Species/Vox/organs.rsi
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: OrganVoxEyes
|
||||||
|
parent: OrganHumanEyes
|
||||||
|
name: eyes
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Mobs/Species/Vox/organs.rsi
|
||||||
|
- type: Item
|
||||||
|
size: Small
|
||||||
|
heldPrefix: eyeballs
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: OrganVoxTongueA
|
||||||
|
parent: OrganHumanTongue
|
||||||
|
name: tongue
|
||||||
|
description: "A fleshy muscle mostly used for screaming."
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Mobs/Species/Vox/organs.rsi
|
||||||
|
- type: Item
|
||||||
|
size: Small
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: OrganVoxTongueB
|
||||||
|
parent: OrganHumanTongue
|
||||||
|
name: tongue
|
||||||
|
description: "A fleshy muscle mostly used for screaming."
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Mobs/Species/Vox/organs.rsi
|
||||||
|
- type: Item
|
||||||
|
size: Small
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
- torso
|
- torso
|
||||||
organs:
|
organs:
|
||||||
brain: OrganHumanBrain
|
brain: OrganHumanBrain
|
||||||
eyes: OrganHumanEyes
|
eyes: OrganVoxEyes
|
||||||
torso:
|
torso:
|
||||||
part: TorsoVox
|
part: TorsoVox
|
||||||
connections:
|
connections:
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
lungs: OrganVoxLungs
|
lungs: OrganVoxLungs
|
||||||
stomach: OrganVoxStomach
|
stomach: OrganVoxStomach
|
||||||
liver: OrganVoxLiver
|
liver: OrganVoxLiver
|
||||||
kidneys: OrganHumanKidneys
|
kidneys: OrganVoxKidneys
|
||||||
right arm:
|
right arm:
|
||||||
part: RightArmVox
|
part: RightArmVox
|
||||||
connections:
|
connections:
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: CrateFoodCooking
|
id: CrateFoodCooking
|
||||||
parent: CratePlastic
|
parent: CrateFreezer
|
||||||
name: kitchen supplies crate
|
name: kitchen supplies crate
|
||||||
description: Extra kitchen supplies, in case the botanists are absent.
|
description: Extra kitchen supplies, in case the botanists are absent.
|
||||||
components:
|
components:
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: CrateFoodBarSupply
|
id: CrateFoodBarSupply
|
||||||
parent: CratePlastic
|
parent: CrateFreezer
|
||||||
name: bartending supplies crate
|
name: bartending supplies crate
|
||||||
description: Extra Bar supplies, in case the clown was allowed in the bar unsupervised.
|
description: Extra Bar supplies, in case the clown was allowed in the bar unsupervised.
|
||||||
components:
|
components:
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: CrateFoodSoftdrinks
|
id: CrateFoodSoftdrinks
|
||||||
parent: CratePlastic
|
parent: CrateFreezer
|
||||||
name: softdrinks crate
|
name: softdrinks crate
|
||||||
description: A variety of sodas to complement a small party, without having to empty the soda machines. Includes 14 sodas.
|
description: A variety of sodas to complement a small party, without having to empty the soda machines. Includes 14 sodas.
|
||||||
components:
|
components:
|
||||||
@@ -129,7 +129,7 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: CrateFoodGetMore
|
id: CrateFoodGetMore
|
||||||
parent: CratePlastic
|
parent: CrateFreezer
|
||||||
name: Getmore Bakemore crate
|
name: Getmore Bakemore crate
|
||||||
description: Getmore branded snacks and baking supplies for the creative chef, all without the need of emptying your station's Getmore machines!
|
description: Getmore branded snacks and baking supplies for the creative chef, all without the need of emptying your station's Getmore machines!
|
||||||
components:
|
components:
|
||||||
|
|||||||
@@ -217,7 +217,7 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: CrateServiceSodaDispenser
|
id: CrateServiceSodaDispenser
|
||||||
parent: CrateGenericSteel
|
parent: CrateFreezer
|
||||||
name: soda dispenser refill crate
|
name: soda dispenser refill crate
|
||||||
description: Contains refills for soda dispensers.
|
description: Contains refills for soda dispensers.
|
||||||
components:
|
components:
|
||||||
@@ -244,7 +244,7 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: CrateServiceBoozeDispenser
|
id: CrateServiceBoozeDispenser
|
||||||
parent: CrateGenericSteel
|
parent: CrateFreezer
|
||||||
name: booze dispenser refill crate
|
name: booze dispenser refill crate
|
||||||
description: Contains refills for booze dispensers.
|
description: Contains refills for booze dispensers.
|
||||||
components:
|
components:
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
- type: Speech
|
- type: Speech
|
||||||
speechVerb: Vox
|
speechVerb: Vox
|
||||||
speechSounds: Vox
|
speechSounds: Vox
|
||||||
|
allowedEmotes: ['Click', 'Chitter']
|
||||||
- type: TypingIndicator
|
- type: TypingIndicator
|
||||||
proto: vox
|
proto: vox
|
||||||
- type: Vocal
|
- type: Vocal
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
- type: entity
|
- type: entity
|
||||||
abstract: true
|
abstract: true
|
||||||
parent: BaseItem
|
parent: BaseItem
|
||||||
id: Crayon
|
id: CrayonInedible
|
||||||
name: crayon
|
name: crayon
|
||||||
description: A colourful crayon. Looks tasty. Mmmm...
|
description: A colourful crayon. Looks tasty. Mmmm...
|
||||||
components:
|
components:
|
||||||
@@ -24,7 +24,25 @@
|
|||||||
selectedState: like
|
selectedState: like
|
||||||
- type: LimitedCharges
|
- type: LimitedCharges
|
||||||
maxCharges: 25
|
maxCharges: 25
|
||||||
- type: Food
|
- type: StaticPrice
|
||||||
|
price: 5
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
abstract: true
|
||||||
|
parent: CrayonInedible
|
||||||
|
id: Crayon
|
||||||
|
name: crayon
|
||||||
|
description: A colourful crayon. Looks tasty. Mmmm...
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Fun/crayons.rsi
|
||||||
|
- type: Item
|
||||||
|
sprite: Objects/Fun/crayons.rsi
|
||||||
|
size: Tiny
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- Recyclable
|
||||||
|
- type: Edible
|
||||||
- type: FlavorProfile
|
- type: FlavorProfile
|
||||||
flavors:
|
flavors:
|
||||||
- chewy
|
- chewy
|
||||||
@@ -37,8 +55,6 @@
|
|||||||
Quantity: 3
|
Quantity: 3
|
||||||
- ReagentId: MindbreakerToxin
|
- ReagentId: MindbreakerToxin
|
||||||
Quantity: 2
|
Quantity: 2
|
||||||
- type: StaticPrice
|
|
||||||
price: 5
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: Crayon
|
parent: Crayon
|
||||||
@@ -109,6 +125,30 @@
|
|||||||
- type: AutoRecharge
|
- type: AutoRecharge
|
||||||
rechargeDuration: 5
|
rechargeDuration: 5
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: CrayonInedible
|
||||||
|
id: CrayonBorg
|
||||||
|
name: electric crayon
|
||||||
|
description: Supposedly the most delicious crayon type in all the universes; unfortunately, you cannot eat.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: electric
|
||||||
|
- type: Item
|
||||||
|
heldPrefix: electric
|
||||||
|
- type: Crayon
|
||||||
|
deleteEmpty: false
|
||||||
|
color: Red
|
||||||
|
selectableColor: true
|
||||||
|
- type: LimitedCharges
|
||||||
|
maxCharges: 30
|
||||||
|
- type: AutoRecharge
|
||||||
|
rechargeDuration: 5
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- Write
|
||||||
|
- Crayon
|
||||||
|
- Trash
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: Crayon
|
parent: Crayon
|
||||||
id: CrayonBlack
|
id: CrayonBlack
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
- state: folder-base
|
- state: folder-base
|
||||||
- state: folder-stamp-inverse
|
- state: folder-stamp-inverse
|
||||||
color: "#1dff00"
|
color: "#1dff00"
|
||||||
|
- type: PhysicalComposition
|
||||||
|
materialComposition:
|
||||||
|
Paper: 50
|
||||||
- type: SpawnItemsOnUse
|
- type: SpawnItemsOnUse
|
||||||
items:
|
items:
|
||||||
- id: NukeCodePaper
|
- id: NukeCodePaper
|
||||||
@@ -58,6 +61,11 @@
|
|||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- Document
|
- Document
|
||||||
|
- type: PhysicalComposition
|
||||||
|
materialComposition:
|
||||||
|
Paper: 50
|
||||||
|
- type: StaticPrice
|
||||||
|
price: 15 # Weirdly this is more expensive than cardboard boxes. But if I have this any lower then I get an arbitrage testfail because the raw materials are worth ~$14.
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
@@ -304,6 +312,10 @@
|
|||||||
slots: [belt]
|
slots: [belt]
|
||||||
quickEquip: false
|
quickEquip: false
|
||||||
sprite: Objects/Misc/clipboard.rsi
|
sprite: Objects/Misc/clipboard.rsi
|
||||||
|
- type: PhysicalComposition
|
||||||
|
materialComposition: # half of autolathe printing cost
|
||||||
|
Wood: 50
|
||||||
|
Steel: 25
|
||||||
- type: Storage
|
- type: Storage
|
||||||
grid:
|
grid:
|
||||||
- 0,0,5,3
|
- 0,0,5,3
|
||||||
@@ -355,6 +367,10 @@
|
|||||||
sprite: Objects/Misc/plastic_clipboard.rsi
|
sprite: Objects/Misc/plastic_clipboard.rsi
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Objects/Misc/plastic_clipboard.rsi
|
sprite: Objects/Misc/plastic_clipboard.rsi
|
||||||
|
- type: PhysicalComposition
|
||||||
|
materialComposition: # half of autolathe printing cost
|
||||||
|
Plastic: 50
|
||||||
|
Steel: 25
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: [BoxFolderPlasticClipboardEmpty, BoxFolderFill]
|
parent: [BoxFolderPlasticClipboardEmpty, BoxFolderFill]
|
||||||
@@ -385,6 +401,11 @@
|
|||||||
sprite: Objects/Misc/cc-clipboard.rsi
|
sprite: Objects/Misc/cc-clipboard.rsi
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Objects/Misc/cc-clipboard.rsi
|
sprite: Objects/Misc/cc-clipboard.rsi
|
||||||
|
- type: PhysicalComposition
|
||||||
|
materialComposition: # same composition as regular clipboard + bit of cloth because it's "upholstered with green velvet"
|
||||||
|
Wood: 50
|
||||||
|
Steel: 25
|
||||||
|
Cloth: 25
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: [BoxFolderCentComClipboardEmpty, BoxFolderFill]
|
parent: [BoxFolderCentComClipboardEmpty, BoxFolderFill]
|
||||||
|
|||||||
@@ -362,7 +362,6 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- Mop
|
- Mop
|
||||||
- type: CleansForensics
|
|
||||||
- type: Fiber
|
- type: Fiber
|
||||||
fiberColor: fibers-white
|
fiberColor: fibers-white
|
||||||
- type: DnaSubstanceTrace
|
- type: DnaSubstanceTrace
|
||||||
|
|||||||
@@ -450,6 +450,23 @@
|
|||||||
- type: BorgModuleIcon
|
- type: BorgModuleIcon
|
||||||
icon: { sprite: Interface/Actions/actions_borg.rsi, state: wire-module }
|
icon: { sprite: Interface/Actions/actions_borg.rsi, state: wire-module }
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: BorgModuleArtistry
|
||||||
|
parent: [ BaseBorgModule, BaseProviderBorgModule ]
|
||||||
|
name: artistry cyborg module
|
||||||
|
description: A module for arts & crafts whilst the station burns!
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
layers:
|
||||||
|
- state: generic
|
||||||
|
- state: icon-artistry
|
||||||
|
- type: ItemBorgModule
|
||||||
|
hands:
|
||||||
|
- item: SprayPainterBorg
|
||||||
|
- item: CrayonBorg
|
||||||
|
- type: BorgModuleIcon
|
||||||
|
icon: { sprite: Interface/Actions/actions_borg.rsi, state: artistry-module }
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: BorgModuleFireExtinguisher
|
id: BorgModuleFireExtinguisher
|
||||||
parent: [ BaseBorgModule, BaseProviderBorgModule ]
|
parent: [ BaseBorgModule, BaseProviderBorgModule ]
|
||||||
|
|||||||
@@ -47,6 +47,16 @@
|
|||||||
- type: AutoRecharge
|
- type: AutoRecharge
|
||||||
rechargeDuration: 1
|
rechargeDuration: 1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: SprayPainter
|
||||||
|
name: experimental spray painter
|
||||||
|
description: An experimental recharging spray painter that can infinitely replicate compressed paint.
|
||||||
|
id: SprayPainterBorg
|
||||||
|
suffix: Borg
|
||||||
|
components:
|
||||||
|
- type: AutoRecharge
|
||||||
|
rechargeDuration: 5
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: SprayPainter
|
parent: SprayPainter
|
||||||
id: SprayPainterEmpty
|
id: SprayPainterEmpty
|
||||||
|
|||||||
@@ -16,125 +16,145 @@
|
|||||||
- HideContextMenu
|
- HideContextMenu
|
||||||
- type: AnimationPlayer
|
- type: AnimationPlayer
|
||||||
|
|
||||||
- type: hitscan
|
- type: entity
|
||||||
id: RedLaser
|
id: BasicHitscan
|
||||||
damage:
|
categories: [ HideSpawnMenu ]
|
||||||
types:
|
components:
|
||||||
Heat: 14
|
- type: HitscanAmmo
|
||||||
muzzleFlash:
|
- type: HitscanBasicRaycast
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
- type: HitscanBasicVisuals
|
||||||
state: muzzle_laser
|
muzzleFlash:
|
||||||
travelFlash:
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
state: muzzle_laser
|
||||||
state: beam
|
travelFlash:
|
||||||
impactFlash:
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
state: beam
|
||||||
state: impact_laser
|
impactFlash:
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
|
state: impact_laser
|
||||||
|
- type: HitscanReflect
|
||||||
|
- type: HitscanBasicEffects
|
||||||
|
|
||||||
- type: hitscan
|
- type: entity
|
||||||
id: RedLaserPractice
|
parent: BasicHitscan
|
||||||
damage:
|
|
||||||
types:
|
|
||||||
Heat: 1
|
|
||||||
muzzleFlash:
|
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
|
||||||
state: muzzle_laser
|
|
||||||
travelFlash:
|
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
|
||||||
state: beam
|
|
||||||
impactFlash:
|
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
|
||||||
state: impact_laser
|
|
||||||
|
|
||||||
- type: hitscan
|
|
||||||
id: RedMediumLaser
|
|
||||||
damage:
|
|
||||||
types:
|
|
||||||
Heat: 17
|
|
||||||
muzzleFlash:
|
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
|
||||||
state: muzzle_laser
|
|
||||||
travelFlash:
|
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
|
||||||
state: beam
|
|
||||||
impactFlash:
|
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
|
||||||
state: impact_laser
|
|
||||||
|
|
||||||
- type: hitscan
|
|
||||||
id: RedLightLaser
|
id: RedLightLaser
|
||||||
damage:
|
categories: [ HideSpawnMenu ]
|
||||||
types:
|
components:
|
||||||
Heat: 7
|
- type: HitscanBasicDamage
|
||||||
muzzleFlash:
|
damage:
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
types:
|
||||||
state: muzzle_laser
|
Heat: 7
|
||||||
travelFlash:
|
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
|
||||||
state: beam
|
|
||||||
impactFlash:
|
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
|
||||||
state: impact_laser
|
|
||||||
|
|
||||||
- type: hitscan
|
- type: entity
|
||||||
id: XrayLaser
|
parent: BasicHitscan
|
||||||
damage:
|
id: RedLaser
|
||||||
types:
|
categories: [ HideSpawnMenu ]
|
||||||
Heat: 10
|
components:
|
||||||
Radiation: 10
|
- type: HitscanBasicDamage
|
||||||
muzzleFlash:
|
damage:
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
types:
|
||||||
state: muzzle_xray
|
Heat: 14
|
||||||
travelFlash:
|
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
|
||||||
state: xray
|
|
||||||
impactFlash:
|
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
|
||||||
state: impact_xray
|
|
||||||
|
|
||||||
- type: hitscan
|
- type: entity
|
||||||
|
parent: BasicHitscan
|
||||||
|
id: RedMediumLaser
|
||||||
|
categories: [ HideSpawnMenu ]
|
||||||
|
components:
|
||||||
|
- type: HitscanBasicDamage
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Heat: 17
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BasicHitscan
|
||||||
id: RedHeavyLaser
|
id: RedHeavyLaser
|
||||||
damage:
|
categories: [ HideSpawnMenu ]
|
||||||
types:
|
components:
|
||||||
Heat: 28
|
- type: HitscanBasicDamage
|
||||||
muzzleFlash:
|
damage:
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
types:
|
||||||
state: muzzle_beam_heavy
|
Heat: 28
|
||||||
travelFlash:
|
- type: HitscanBasicVisuals
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
muzzleFlash:
|
||||||
state: beam_heavy
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
impactFlash:
|
state: muzzle_beam_heavy
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
travelFlash:
|
||||||
state: impact_beam_heavy
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
|
state: beam_heavy
|
||||||
|
impactFlash:
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
|
state: impact_beam_heavy
|
||||||
|
|
||||||
- type: hitscan
|
- type: entity
|
||||||
|
parent: BasicHitscan
|
||||||
|
id: RedLaserPractice
|
||||||
|
categories: [ HideSpawnMenu ]
|
||||||
|
components:
|
||||||
|
- type: HitscanBasicDamage
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Heat: 1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BasicHitscan
|
||||||
|
id: XrayLaser
|
||||||
|
categories: [ HideSpawnMenu ]
|
||||||
|
components:
|
||||||
|
- type: HitscanBasicDamage
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Heat: 10
|
||||||
|
Radiation: 10
|
||||||
|
- type: HitscanBasicVisuals
|
||||||
|
muzzleFlash:
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
|
state: muzzle_xray
|
||||||
|
travelFlash:
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
|
state: xray
|
||||||
|
impactFlash:
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
|
state: impact_xray
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BasicHitscan
|
||||||
id: Pulse
|
id: Pulse
|
||||||
damage:
|
categories: [ HideSpawnMenu ]
|
||||||
types:
|
components:
|
||||||
Heat: 35
|
- type: HitscanBasicDamage
|
||||||
muzzleFlash:
|
damage:
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
types:
|
||||||
state: muzzle_blue
|
Heat: 35
|
||||||
travelFlash:
|
- type: HitscanBasicVisuals
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
muzzleFlash:
|
||||||
state: beam_blue
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
impactFlash:
|
state: muzzle_blue
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
travelFlash:
|
||||||
state: impact_blue
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
|
state: beam_blue
|
||||||
|
impactFlash:
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
|
state: impact_blue
|
||||||
|
|
||||||
- type: hitscan
|
- type: entity
|
||||||
|
parent: BasicHitscan
|
||||||
id: RedShuttleLaser
|
id: RedShuttleLaser
|
||||||
maxLength: 60
|
categories: [ HideSpawnMenu ]
|
||||||
damage:
|
components:
|
||||||
types:
|
- type: HitscanBasicRaycast
|
||||||
Heat: 45
|
maxDistance: 60.0
|
||||||
Structural: 10
|
- type: HitscanBasicDamage
|
||||||
muzzleFlash:
|
damage:
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
types:
|
||||||
state: muzzle_beam_heavy2
|
Heat: 45
|
||||||
travelFlash:
|
Structural: 10
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
- type: HitscanBasicVisuals
|
||||||
state: beam_heavy2
|
muzzleFlash:
|
||||||
impactFlash:
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
state: muzzle_beam_heavy2
|
||||||
state: impact_beam_heavy2
|
travelFlash:
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
|
state: beam_heavy2
|
||||||
|
impactFlash:
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
|
||||||
|
state: impact_beam_heavy2
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
name: cotton bun recipe
|
name: cotton bun recipe
|
||||||
result: FoodCottonBun
|
result: FoodCottonBun
|
||||||
time: 5
|
time: 5
|
||||||
group: Breads
|
group: Moth
|
||||||
solids:
|
solids:
|
||||||
FoodDoughCottonSlice: 1
|
FoodDoughCottonSlice: 1
|
||||||
|
|
||||||
@@ -2393,6 +2393,7 @@
|
|||||||
name: grilled cheese sandwich recipe
|
name: grilled cheese sandwich recipe
|
||||||
result: FoodBakedGrilledCheeseSandwich
|
result: FoodBakedGrilledCheeseSandwich
|
||||||
time: 10
|
time: 10
|
||||||
|
group: Savory
|
||||||
solids:
|
solids:
|
||||||
FoodBreadPlainSlice: 2
|
FoodBreadPlainSlice: 2
|
||||||
FoodCheeseSlice: 1
|
FoodCheeseSlice: 1
|
||||||
@@ -2403,6 +2404,7 @@
|
|||||||
name: cotton grilled cheese sandwich recipe
|
name: cotton grilled cheese sandwich recipe
|
||||||
result: FoodBakedGrilledCheeseSandwichCotton
|
result: FoodBakedGrilledCheeseSandwichCotton
|
||||||
time: 10
|
time: 10
|
||||||
|
group: Moth
|
||||||
solids:
|
solids:
|
||||||
FoodBreadCottonSlice: 2
|
FoodBreadCottonSlice: 2
|
||||||
FoodCheeseSlice: 1
|
FoodCheeseSlice: 1
|
||||||
@@ -2453,9 +2455,8 @@
|
|||||||
name: cotton cake recipe
|
name: cotton cake recipe
|
||||||
result: FoodCakeCotton
|
result: FoodCakeCotton
|
||||||
time: 5
|
time: 5
|
||||||
group: Cake
|
group: Moth
|
||||||
reagents:
|
reagents:
|
||||||
Fiber: 10
|
Fiber: 10
|
||||||
solids:
|
solids:
|
||||||
FoodCakePlain: 1
|
FoodCakePlain: 1
|
||||||
|
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
- BorgModuleCable
|
- BorgModuleCable
|
||||||
- BorgModuleFireExtinguisher
|
- BorgModuleFireExtinguisher
|
||||||
- BorgModuleInflatable
|
- BorgModuleInflatable
|
||||||
|
- BorgModuleArtistry
|
||||||
|
|
||||||
- type: latheRecipePack
|
- type: latheRecipePack
|
||||||
id: BorgLimbsStatic
|
id: BorgLimbsStatic
|
||||||
|
|||||||
@@ -222,7 +222,7 @@
|
|||||||
completetime: 2
|
completetime: 2
|
||||||
materials:
|
materials:
|
||||||
Wood: 100
|
Wood: 100
|
||||||
Steel: 25
|
Steel: 50
|
||||||
|
|
||||||
- type: latheRecipe
|
- type: latheRecipe
|
||||||
id: BoxFolderPlasticClipboardEmpty
|
id: BoxFolderPlasticClipboardEmpty
|
||||||
@@ -230,7 +230,7 @@
|
|||||||
completetime: 2
|
completetime: 2
|
||||||
materials:
|
materials:
|
||||||
Plastic: 100
|
Plastic: 100
|
||||||
Steel: 25
|
Steel: 50
|
||||||
|
|
||||||
- type: latheRecipe
|
- type: latheRecipe
|
||||||
id: TowelColorWhite
|
id: TowelColorWhite
|
||||||
|
|||||||
@@ -42,6 +42,11 @@
|
|||||||
id: BorgModuleInflatable
|
id: BorgModuleInflatable
|
||||||
result: BorgModuleInflatable
|
result: BorgModuleInflatable
|
||||||
|
|
||||||
|
- type: latheRecipe
|
||||||
|
parent: BaseBorgModuleRecipe
|
||||||
|
id: BorgModuleArtistry
|
||||||
|
result: BorgModuleArtistry
|
||||||
|
|
||||||
# Cargo Modules
|
# Cargo Modules
|
||||||
|
|
||||||
- type: latheRecipe
|
- type: latheRecipe
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
- Detective
|
- Detective
|
||||||
- Cryogenics
|
- Cryogenics
|
||||||
- External
|
- External
|
||||||
|
- GenpopEnter
|
||||||
|
- GenpopLeave
|
||||||
special:
|
special:
|
||||||
- !type:AddImplantSpecial
|
- !type:AddImplantSpecial
|
||||||
implants: [ MindShieldImplant ]
|
implants: [ MindShieldImplant ]
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
- Service
|
- Service
|
||||||
- External
|
- External
|
||||||
- Cryogenics
|
- Cryogenics
|
||||||
|
- GenpopEnter
|
||||||
|
- GenpopLeave
|
||||||
special:
|
special:
|
||||||
- !type:AddImplantSpecial
|
- !type:AddImplantSpecial
|
||||||
implants: [ MindShieldImplant ]
|
implants: [ MindShieldImplant ]
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
- Service
|
- Service
|
||||||
- External
|
- External
|
||||||
- Cryogenics
|
- Cryogenics
|
||||||
|
- GenpopEnter
|
||||||
|
- GenpopLeave
|
||||||
special:
|
special:
|
||||||
- !type:AddImplantSpecial
|
- !type:AddImplantSpecial
|
||||||
implants: [ MindShieldImplant ]
|
implants: [ MindShieldImplant ]
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
- External
|
- External
|
||||||
- Detective
|
- Detective
|
||||||
- Cryogenics
|
- Cryogenics
|
||||||
|
- GenpopEnter
|
||||||
|
- GenpopLeave
|
||||||
special:
|
special:
|
||||||
- !type:AddImplantSpecial
|
- !type:AddImplantSpecial
|
||||||
implants: [ MindShieldImplant ]
|
implants: [ MindShieldImplant ]
|
||||||
|
|||||||
@@ -230,6 +230,10 @@
|
|||||||
path: /Audio/Voice/Vox/vox_cough.ogg
|
path: /Audio/Voice/Vox/vox_cough.ogg
|
||||||
Sigh:
|
Sigh:
|
||||||
path: /Audio/Voice/Vox/vox_sigh.ogg
|
path: /Audio/Voice/Vox/vox_sigh.ogg
|
||||||
|
Click:
|
||||||
|
path: /Audio/Voice/Vox/vox_click.ogg
|
||||||
|
Chitter:
|
||||||
|
path: /Audio/Voice/Vox/vox_chitter.ogg
|
||||||
Honk:
|
Honk:
|
||||||
collection: BikeHorn
|
collection: BikeHorn
|
||||||
Crying:
|
Crying:
|
||||||
|
|||||||
|
After Width: | Height: | Size: 857 B |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"license": "CC-BY-SA-3.0",
|
"license": "CC-BY-SA-3.0",
|
||||||
"copyright": "Taken from vgstation at commit https://github.com/vgstation-coders/vgstation13/commit/cdbcb1e858b11f083994a7a269ed67ef5b452ce9, inflatable module by FungiFellow (GitHub), Module actions by Scarky0. chem, adv-chem, and adv-mining by mubururu_, xenoborg actions by Samuka-C (github), advclown by ThatGuyUSA. c20r and esword by RedBookcase on Github.",
|
"copyright": "Taken from vgstation at commit https://github.com/vgstation-coders/vgstation13/commit/cdbcb1e858b11f083994a7a269ed67ef5b452ce9, inflatable module by FungiFellow (GitHub), Module actions by Scarky0. chem, adv-chem, and adv-mining by mubururu_, xenoborg actions by Samuka-C (github), advclown by ThatGuyUSA. c20r and esword by RedBookcase on Github, artistry-module by Kittygyat, with help from TiniestShark",
|
||||||
"size": {
|
"size": {
|
||||||
"x": 32,
|
"x": 32,
|
||||||
"y": 32
|
"y": 32
|
||||||
@@ -25,6 +25,9 @@
|
|||||||
{
|
{
|
||||||
"name":"extinguisher-module"
|
"name":"extinguisher-module"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name":"artistry-module"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name":"geiger-module"
|
"name":"geiger-module"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"y": 32
|
"y": 32
|
||||||
},
|
},
|
||||||
"license": "CC-BY-SA-3.0",
|
"license": "CC-BY-SA-3.0",
|
||||||
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/faf6db214927874c19b8fa8585d26b5d40de1acc, derelict generic sprites modified by GoldenCan(GitHub), xenoborg sprites, created and modified by Samuka-C (github). Derelict Engineer, Janitor, Miner, Medical, and Assault Borg sprites by _miket on Discord.",
|
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/faf6db214927874c19b8fa8585d26b5d40de1acc, derelict generic sprites modified by GoldenCan(GitHub), xenoborg sprites, created and modified by Samuka-C (github), Derelict Engineer, Janitor, Miner, Medical, and Assault Borg sprites by _miket on Discord, minor resprite of the service borg by Kittygyat (github).",
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "clown",
|
"name": "clown",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
BIN
Resources/Textures/Mobs/Species/Vox/organs.rsi/eyeball-l.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
Resources/Textures/Mobs/Species/Vox/organs.rsi/eyeball-r.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 175 B |
|
After Width: | Height: | Size: 174 B |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
BIN
Resources/Textures/Mobs/Species/Vox/organs.rsi/heart-on.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
Resources/Textures/Mobs/Species/Vox/organs.rsi/kidney-l.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
Resources/Textures/Mobs/Species/Vox/organs.rsi/kidney-r.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 311 B |
|
After Width: | Height: | Size: 309 B |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
BIN
Resources/Textures/Mobs/Species/Vox/organs.rsi/liver.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 408 B After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 404 B After Width: | Height: | Size: 4.9 KiB |
@@ -1,12 +1,69 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"license": "CC-BY-SA-3.0",
|
"license": "CC-BY-SA-3.0",
|
||||||
"copyright": "made by mubururu_ (github)",
|
"copyright": "Originally from Paradise station, updated and edited for SS14 by MissKay1994 (github)",
|
||||||
"size": {
|
"size": {
|
||||||
"x": 32,
|
"x": 32,
|
||||||
"y": 32
|
"y": 32
|
||||||
},
|
},
|
||||||
"states": [
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "eyeballs-inhand-left",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eyeballs-inhand-right",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eyeball-l"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eyeball-r"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "heart-inhand-left",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "heart-inhand-right",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "heart-on",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.6,
|
||||||
|
0.1,
|
||||||
|
0.1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kidneys-inhand-left",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kidneys-inhand-right",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kidney-l"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kidney-r"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liver"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liver-inhand-left",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liver-inhand-right",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "lungs-inhand-left",
|
"name": "lungs-inhand-left",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
@@ -20,6 +77,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "lung-r"
|
"name": "lung-r"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stomach"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stomach-inhand-left",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stomach-inhand-right",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tongue"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
BIN
Resources/Textures/Mobs/Species/Vox/organs.rsi/stomach.png
Normal file
|
After Width: | Height: | Size: 725 B |
BIN
Resources/Textures/Mobs/Species/Vox/organs.rsi/tongue.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
Resources/Textures/Objects/Fun/crayons.rsi/electric.png
Normal file
|
After Width: | Height: | Size: 235 B |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"license": "CC-BY-SA-3.0",
|
"license": "CC-BY-SA-3.0",
|
||||||
"copyright": "Taken from tgstation and modified by Swept at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24, tweaked by Ubaser",
|
"copyright": "Taken from tgstation and modified by Swept at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24, tweaked by Ubaser, electric crayon by Kittygyat",
|
||||||
"size": {
|
"size": {
|
||||||
"x": 32,
|
"x": 32,
|
||||||
"y": 32
|
"y": 32
|
||||||
@@ -40,6 +40,9 @@
|
|||||||
"name": "box-inhand-right",
|
"name": "box-inhand-right",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "electric"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "green"
|
"name": "green"
|
||||||
},
|
},
|
||||||
|
|||||||
|
After Width: | Height: | Size: 237 B |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"copyright": "Created by EmoGarbage404 (github) for Space Station 14. icon-construction.png created by deltanedas (github). syndicateborgbomb.png created by Mangohydra (github). icon-chem.png & icon-mining-adv.png created by mubururu_ (github) icon-inflatable.png made by FungiFellow (GitHub), Xenoborg modules sprites by Samuka-C (github)",
|
"copyright": "Created by EmoGarbage404 (github) for Space Station 14. icon-construction.png created by deltanedas (github). syndicateborgbomb.png created by Mangohydra (github). icon-chem.png & icon-mining-adv.png created by mubururu_ (github) icon-inflatable.png made by FungiFellow (GitHub), Xenoborg modules sprites by Samuka-C (github), icon-artistry by Kittygyat",
|
||||||
"size": {
|
"size": {
|
||||||
"x": 32,
|
"x": 32,
|
||||||
"y": 32
|
"y": 32
|
||||||
@@ -55,6 +55,9 @@
|
|||||||
{
|
{
|
||||||
"name": "icon-fire-extinguisher"
|
"name": "icon-fire-extinguisher"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "icon-artistry"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "icon-gardening"
|
"name": "icon-gardening"
|
||||||
},
|
},
|
||||||
|
|||||||