Merge remote-tracking branch 'refs/remotes/wizards/master'
# Conflicts: # Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml
This commit is contained in:
commit
fe8edf03d4
30 changed files with 565 additions and 443 deletions
|
|
@ -21,6 +21,7 @@ public sealed partial class AccessLevelControl : GridContainer
|
|||
public AccessLevelControl()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_sawmill = _logManager.GetSawmill("accesslevelcontrol");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Content.Shared.Audio;
|
|||
using Content.Shared.CCVar;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Random;
|
||||
using Content.Shared.Random.Rules;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.ResourceManagement;
|
||||
|
|
|
|||
|
|
@ -160,9 +160,9 @@ public sealed class HTNPlanJob : Job<HTNPlan>
|
|||
{
|
||||
var compound = _protoManager.Index<HTNCompoundPrototype>(compoundId.Task);
|
||||
|
||||
for (var i = mtrIndex; i < compound.Branches.Count; i++)
|
||||
for (; mtrIndex < compound.Branches.Count; mtrIndex++)
|
||||
{
|
||||
var branch = compound.Branches[i];
|
||||
var branch = compound.Branches[mtrIndex];
|
||||
var isValid = true;
|
||||
|
||||
foreach (var con in branch.Preconditions)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.Random;
|
||||
using Content.Shared.Random.Rules;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
|
|
|||
|
|
@ -62,21 +62,22 @@ public sealed class SmartEquipSystem : EntitySystem
|
|||
if (playerSession.AttachedEntity is not { Valid: true } uid || !Exists(uid))
|
||||
return;
|
||||
|
||||
if (!_actionBlocker.CanInteract(uid, null))
|
||||
return;
|
||||
|
||||
// early out if we don't have any hands or a valid inventory slot
|
||||
if (!TryComp<HandsComponent>(uid, out var hands) || hands.ActiveHand == null)
|
||||
return;
|
||||
|
||||
var handItem = hands.ActiveHand.HeldEntity;
|
||||
|
||||
// can the user interact, and is the item interactable? e.g. virtual items
|
||||
if (!_actionBlocker.CanInteract(uid, handItem))
|
||||
return;
|
||||
|
||||
if (!TryComp<InventoryComponent>(uid, out var inventory) || !_inventory.HasSlot(uid, equipmentSlot, inventory))
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("smart-equip-missing-equipment-slot", ("slotName", equipmentSlot)), uid, uid);
|
||||
return;
|
||||
}
|
||||
|
||||
var handItem = hands.ActiveHand.HeldEntity;
|
||||
|
||||
// early out if we have an item and cant drop it at all
|
||||
if (handItem != null && !_hands.CanDropHeld(uid, hands.ActiveHand))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Popups;
|
||||
|
|
@ -43,6 +44,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
|||
SubscribeLocalEvent<VirtualItemComponent, BeingUnequippedAttemptEvent>(OnBeingUnequippedAttempt);
|
||||
|
||||
SubscribeLocalEvent<VirtualItemComponent, BeforeRangedInteractEvent>(OnBeforeRangedInteract);
|
||||
SubscribeLocalEvent<VirtualItemComponent, GettingInteractedWithAttemptEvent>(OnGettingInteractedWithAttemptEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -72,6 +74,12 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
|||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnGettingInteractedWithAttemptEvent(Entity<VirtualItemComponent> ent, ref GettingInteractedWithAttemptEvent args)
|
||||
{
|
||||
// No interactions with a virtual item, please.
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
#region Hands
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
12
Content.Shared/Random/Rules/AlwaysTrue.cs
Normal file
12
Content.Shared/Random/Rules/AlwaysTrue.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
namespace Content.Shared.Random.Rules;
|
||||
|
||||
/// <summary>
|
||||
/// Always returns true. Used for fallbacks.
|
||||
/// </summary>
|
||||
public sealed partial class AlwaysTrueRule : RulesRule
|
||||
{
|
||||
public override bool Check(EntityManager entManager, EntityUid uid)
|
||||
{
|
||||
return !Inverted;
|
||||
}
|
||||
}
|
||||
39
Content.Shared/Random/Rules/GridInRange.cs
Normal file
39
Content.Shared/Random/Rules/GridInRange.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using System.Numerics;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Shared.Random.Rules;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if on a grid or in range of one.
|
||||
/// </summary>
|
||||
public sealed partial class GridInRangeRule : RulesRule
|
||||
{
|
||||
[DataField]
|
||||
public float Range = 10f;
|
||||
|
||||
public override bool Check(EntityManager entManager, EntityUid uid)
|
||||
{
|
||||
if (!entManager.TryGetComponent(uid, out TransformComponent? xform))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xform.GridUid != null)
|
||||
{
|
||||
return !Inverted;
|
||||
}
|
||||
|
||||
var transform = entManager.System<SharedTransformSystem>();
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
var worldPos = transform.GetWorldPosition(xform);
|
||||
var gridRange = new Vector2(Range, Range);
|
||||
|
||||
foreach (var _ in mapManager.FindGridsIntersecting(xform.MapID, new Box2(worldPos - gridRange, worldPos + gridRange)))
|
||||
{
|
||||
return !Inverted;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
18
Content.Shared/Random/Rules/InSpace.cs
Normal file
18
Content.Shared/Random/Rules/InSpace.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
namespace Content.Shared.Random.Rules;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the attached entity is in space.
|
||||
/// </summary>
|
||||
public sealed partial class InSpaceRule : RulesRule
|
||||
{
|
||||
public override bool Check(EntityManager entManager, EntityUid uid)
|
||||
{
|
||||
if (!entManager.TryGetComponent(uid, out TransformComponent? xform) ||
|
||||
xform.GridUid != null)
|
||||
{
|
||||
return Inverted;
|
||||
}
|
||||
|
||||
return !Inverted;
|
||||
}
|
||||
}
|
||||
77
Content.Shared/Random/Rules/NearbyAccess.cs
Normal file
77
Content.Shared/Random/Rules/NearbyAccess.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
using Content.Shared.Access;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Random.Rules;
|
||||
|
||||
/// <summary>
|
||||
/// Checks for an entity nearby with the specified access.
|
||||
/// </summary>
|
||||
public sealed partial class NearbyAccessRule : RulesRule
|
||||
{
|
||||
// This exists because of door electronics contained inside doors.
|
||||
/// <summary>
|
||||
/// Does the access entity need to be anchored.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Anchored = true;
|
||||
|
||||
/// <summary>
|
||||
/// Count of entities that need to be nearby.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int Count = 1;
|
||||
|
||||
[DataField(required: true)]
|
||||
public List<ProtoId<AccessLevelPrototype>> Access = new();
|
||||
|
||||
[DataField]
|
||||
public float Range = 10f;
|
||||
|
||||
public override bool Check(EntityManager entManager, EntityUid uid)
|
||||
{
|
||||
var xformQuery = entManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
if (!xformQuery.TryGetComponent(uid, out var xform) ||
|
||||
xform.MapUid == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var transform = entManager.System<SharedTransformSystem>();
|
||||
var lookup = entManager.System<EntityLookupSystem>();
|
||||
var reader = entManager.System<AccessReaderSystem>();
|
||||
|
||||
var found = false;
|
||||
var worldPos = transform.GetWorldPosition(xform, xformQuery);
|
||||
var count = 0;
|
||||
|
||||
// TODO: Update this when we get the callback version
|
||||
var entities = new HashSet<Entity<AccessReaderComponent>>();
|
||||
lookup.GetEntitiesInRange(xform.MapID, worldPos, Range, entities);
|
||||
foreach (var comp in entities)
|
||||
{
|
||||
if (!reader.AreAccessTagsAllowed(Access, comp) ||
|
||||
Anchored &&
|
||||
(!xformQuery.TryGetComponent(comp, out var compXform) ||
|
||||
!compXform.Anchored))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
if (count < Count)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return Inverted;
|
||||
|
||||
return !Inverted;
|
||||
}
|
||||
}
|
||||
71
Content.Shared/Random/Rules/NearbyComponents.cs
Normal file
71
Content.Shared/Random/Rules/NearbyComponents.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Random.Rules;
|
||||
|
||||
public sealed partial class NearbyComponentsRule : RulesRule
|
||||
{
|
||||
/// <summary>
|
||||
/// Does the entity need to be anchored.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Anchored;
|
||||
|
||||
[DataField]
|
||||
public int Count;
|
||||
|
||||
[DataField(required: true)]
|
||||
public ComponentRegistry Components = default!;
|
||||
|
||||
[DataField]
|
||||
public float Range = 10f;
|
||||
|
||||
public override bool Check(EntityManager entManager, EntityUid uid)
|
||||
{
|
||||
var inRange = new HashSet<Entity<IComponent>>();
|
||||
var xformQuery = entManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
if (!xformQuery.TryGetComponent(uid, out var xform) ||
|
||||
xform.MapUid == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var transform = entManager.System<SharedTransformSystem>();
|
||||
var lookup = entManager.System<EntityLookupSystem>();
|
||||
|
||||
var found = false;
|
||||
var worldPos = transform.GetWorldPosition(xform);
|
||||
var count = 0;
|
||||
|
||||
foreach (var compType in Components.Values)
|
||||
{
|
||||
inRange.Clear();
|
||||
lookup.GetEntitiesInRange(compType.Component.GetType(), xform.MapID, worldPos, Range, inRange);
|
||||
foreach (var comp in inRange)
|
||||
{
|
||||
if (Anchored &&
|
||||
(!xformQuery.TryGetComponent(comp, out var compXform) ||
|
||||
!compXform.Anchored))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
if (count < Count)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return Inverted;
|
||||
|
||||
return !Inverted;
|
||||
}
|
||||
}
|
||||
58
Content.Shared/Random/Rules/NearbyEntities.cs
Normal file
58
Content.Shared/Random/Rules/NearbyEntities.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
using Content.Shared.Whitelist;
|
||||
|
||||
namespace Content.Shared.Random.Rules;
|
||||
|
||||
/// <summary>
|
||||
/// Checks for entities matching the whitelist in range.
|
||||
/// This is more expensive than <see cref="NearbyComponentsRule"/> so prefer that!
|
||||
/// </summary>
|
||||
public sealed partial class NearbyEntitiesRule : RulesRule
|
||||
{
|
||||
/// <summary>
|
||||
/// How many of the entity need to be nearby.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int Count = 1;
|
||||
|
||||
[DataField(required: true)]
|
||||
public EntityWhitelist Whitelist = new();
|
||||
|
||||
[DataField]
|
||||
public float Range = 10f;
|
||||
|
||||
public override bool Check(EntityManager entManager, EntityUid uid)
|
||||
{
|
||||
if (!entManager.TryGetComponent(uid, out TransformComponent? xform) ||
|
||||
xform.MapUid == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var transform = entManager.System<SharedTransformSystem>();
|
||||
var lookup = entManager.System<EntityLookupSystem>();
|
||||
var whitelistSystem = entManager.System<EntityWhitelistSystem>();
|
||||
|
||||
var found = false;
|
||||
var worldPos = transform.GetWorldPosition(xform);
|
||||
var count = 0;
|
||||
|
||||
foreach (var ent in lookup.GetEntitiesInRange(xform.MapID, worldPos, Range))
|
||||
{
|
||||
if (whitelistSystem.IsWhitelistFail(Whitelist, ent))
|
||||
continue;
|
||||
|
||||
count++;
|
||||
|
||||
if (count < Count)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return Inverted;
|
||||
|
||||
return !Inverted;
|
||||
}
|
||||
}
|
||||
79
Content.Shared/Random/Rules/NearbyTilesPercent.cs
Normal file
79
Content.Shared/Random/Rules/NearbyTilesPercent.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Random.Rules;
|
||||
|
||||
public sealed partial class NearbyTilesPercentRule : RulesRule
|
||||
{
|
||||
/// <summary>
|
||||
/// If there are anchored entities on the tile do we ignore the tile.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool IgnoreAnchored;
|
||||
|
||||
[DataField(required: true)]
|
||||
public float Percent;
|
||||
|
||||
[DataField(required: true)]
|
||||
public List<ProtoId<ContentTileDefinition>> Tiles = new();
|
||||
|
||||
[DataField]
|
||||
public float Range = 10f;
|
||||
|
||||
public override bool Check(EntityManager entManager, EntityUid uid)
|
||||
{
|
||||
if (!entManager.TryGetComponent(uid, out TransformComponent? xform) ||
|
||||
!entManager.TryGetComponent<MapGridComponent>(xform.GridUid, out var grid))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var transform = entManager.System<SharedTransformSystem>();
|
||||
var tileDef = IoCManager.Resolve<ITileDefinitionManager>();
|
||||
|
||||
var physicsQuery = entManager.GetEntityQuery<PhysicsComponent>();
|
||||
var tileCount = 0;
|
||||
var matchingTileCount = 0;
|
||||
|
||||
foreach (var tile in grid.GetTilesIntersecting(new Circle(transform.GetWorldPosition(xform),
|
||||
Range)))
|
||||
{
|
||||
// Only consider collidable anchored (for reasons some subfloor stuff has physics but non-collidable)
|
||||
if (IgnoreAnchored)
|
||||
{
|
||||
var gridEnum = grid.GetAnchoredEntitiesEnumerator(tile.GridIndices);
|
||||
var found = false;
|
||||
|
||||
while (gridEnum.MoveNext(out var ancUid))
|
||||
{
|
||||
if (!physicsQuery.TryGetComponent(ancUid, out var physics) ||
|
||||
!physics.CanCollide)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
continue;
|
||||
}
|
||||
|
||||
tileCount++;
|
||||
|
||||
if (!Tiles.Contains(tileDef[tile.Tile.TypeId].ID))
|
||||
continue;
|
||||
|
||||
matchingTileCount++;
|
||||
}
|
||||
|
||||
if (tileCount == 0 || matchingTileCount / (float) tileCount < Percent)
|
||||
return Inverted;
|
||||
|
||||
return !Inverted;
|
||||
}
|
||||
}
|
||||
19
Content.Shared/Random/Rules/OnMapGrid.cs
Normal file
19
Content.Shared/Random/Rules/OnMapGrid.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
namespace Content.Shared.Random.Rules;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if griduid and mapuid match (AKA on 'planet').
|
||||
/// </summary>
|
||||
public sealed partial class OnMapGridRule : RulesRule
|
||||
{
|
||||
public override bool Check(EntityManager entManager, EntityUid uid)
|
||||
{
|
||||
if (!entManager.TryGetComponent(uid, out TransformComponent? xform) ||
|
||||
xform.GridUid != xform.MapUid ||
|
||||
xform.MapUid == null)
|
||||
{
|
||||
return Inverted;
|
||||
}
|
||||
|
||||
return !Inverted;
|
||||
}
|
||||
}
|
||||
39
Content.Shared/Random/Rules/RulesSystem.cs
Normal file
39
Content.Shared/Random/Rules/RulesSystem.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Random.Rules;
|
||||
|
||||
/// <summary>
|
||||
/// Rules-based item selection. Can be used for any sort of conditional selection
|
||||
/// Every single condition needs to be true for this to be selected.
|
||||
/// e.g. "choose maintenance audio if 90% of tiles nearby are maintenance tiles"
|
||||
/// </summary>
|
||||
[Prototype("rules")]
|
||||
public sealed partial class RulesPrototype : IPrototype
|
||||
{
|
||||
[IdDataField] public string ID { get; } = string.Empty;
|
||||
|
||||
[DataField("rules", required: true)]
|
||||
public List<RulesRule> Rules = new();
|
||||
}
|
||||
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract partial class RulesRule
|
||||
{
|
||||
[DataField]
|
||||
public bool Inverted;
|
||||
public abstract bool Check(EntityManager entManager, EntityUid uid);
|
||||
}
|
||||
|
||||
public sealed class RulesSystem : EntitySystem
|
||||
{
|
||||
public bool IsTrue(EntityUid uid, RulesPrototype rules)
|
||||
{
|
||||
foreach (var rule in rules.Rules)
|
||||
{
|
||||
if (!rule.Check(EntityManager, uid))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
using Content.Shared.Access;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Shared.Random;
|
||||
|
||||
/// <summary>
|
||||
/// Rules-based item selection. Can be used for any sort of conditional selection
|
||||
/// Every single condition needs to be true for this to be selected.
|
||||
/// e.g. "choose maintenance audio if 90% of tiles nearby are maintenance tiles"
|
||||
/// </summary>
|
||||
[Prototype("rules")]
|
||||
public sealed partial class RulesPrototype : IPrototype
|
||||
{
|
||||
[IdDataField] public string ID { get; } = string.Empty;
|
||||
|
||||
[DataField("rules", required: true)]
|
||||
public List<RulesRule> Rules = new();
|
||||
}
|
||||
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract partial class RulesRule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the attached entity is in space.
|
||||
/// </summary>
|
||||
public sealed partial class InSpaceRule : RulesRule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for entities matching the whitelist in range.
|
||||
/// This is more expensive than <see cref="NearbyComponentsRule"/> so prefer that!
|
||||
/// </summary>
|
||||
public sealed partial class NearbyEntitiesRule : RulesRule
|
||||
{
|
||||
/// <summary>
|
||||
/// How many of the entity need to be nearby.
|
||||
/// </summary>
|
||||
[DataField("count")]
|
||||
public int Count = 1;
|
||||
|
||||
[DataField("whitelist", required: true)]
|
||||
public EntityWhitelist Whitelist = new();
|
||||
|
||||
[DataField("range")]
|
||||
public float Range = 10f;
|
||||
}
|
||||
|
||||
public sealed partial class NearbyTilesPercentRule : RulesRule
|
||||
{
|
||||
/// <summary>
|
||||
/// If there are anchored entities on the tile do we ignore the tile.
|
||||
/// </summary>
|
||||
[DataField("ignoreAnchored")] public bool IgnoreAnchored;
|
||||
|
||||
[DataField("percent", required: true)]
|
||||
public float Percent;
|
||||
|
||||
[DataField("tiles", required: true, customTypeSerializer:typeof(PrototypeIdListSerializer<ContentTileDefinition>))]
|
||||
public List<string> Tiles = new();
|
||||
|
||||
[DataField("range")]
|
||||
public float Range = 10f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always returns true. Used for fallbacks.
|
||||
/// </summary>
|
||||
public sealed partial class AlwaysTrueRule : RulesRule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if on a grid or in range of one.
|
||||
/// </summary>
|
||||
public sealed partial class GridInRangeRule : RulesRule
|
||||
{
|
||||
[DataField("range")]
|
||||
public float Range = 10f;
|
||||
|
||||
[DataField("inverted")]
|
||||
public bool Inverted = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if griduid and mapuid match (AKA on 'planet').
|
||||
/// </summary>
|
||||
public sealed partial class OnMapGridRule : RulesRule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for an entity nearby with the specified access.
|
||||
/// </summary>
|
||||
public sealed partial class NearbyAccessRule : RulesRule
|
||||
{
|
||||
// This exists because of doorelectronics contained inside doors.
|
||||
/// <summary>
|
||||
/// Does the access entity need to be anchored.
|
||||
/// </summary>
|
||||
[DataField("anchored")]
|
||||
public bool Anchored = true;
|
||||
|
||||
/// <summary>
|
||||
/// Count of entities that need to be nearby.
|
||||
/// </summary>
|
||||
[DataField("count")]
|
||||
public int Count = 1;
|
||||
|
||||
[DataField("access", required: true)]
|
||||
public List<ProtoId<AccessLevelPrototype>> Access = new();
|
||||
|
||||
[DataField("range")]
|
||||
public float Range = 10f;
|
||||
}
|
||||
|
||||
public sealed partial class NearbyComponentsRule : RulesRule
|
||||
{
|
||||
/// <summary>
|
||||
/// Does the entity need to be anchored.
|
||||
/// </summary>
|
||||
[DataField("anchored")]
|
||||
public bool Anchored;
|
||||
|
||||
[DataField("count")] public int Count;
|
||||
|
||||
[DataField("components", required: true)]
|
||||
public ComponentRegistry Components = default!;
|
||||
|
||||
[DataField("range")]
|
||||
public float Range = 10f;
|
||||
}
|
||||
|
|
@ -1,247 +0,0 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Components;
|
||||
|
||||
namespace Content.Shared.Random;
|
||||
|
||||
public sealed class RulesSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDef = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _reader = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
public bool IsTrue(EntityUid uid, RulesPrototype rules)
|
||||
{
|
||||
var inRange = new HashSet<Entity<IComponent>>();
|
||||
foreach (var rule in rules.Rules)
|
||||
{
|
||||
switch (rule)
|
||||
{
|
||||
case AlwaysTrueRule:
|
||||
break;
|
||||
case GridInRangeRule griddy:
|
||||
{
|
||||
if (!TryComp(uid, out TransformComponent? xform))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xform.GridUid != null)
|
||||
{
|
||||
return !griddy.Inverted;
|
||||
}
|
||||
|
||||
var worldPos = _transform.GetWorldPosition(xform);
|
||||
var gridRange = new Vector2(griddy.Range, griddy.Range);
|
||||
|
||||
foreach (var _ in _mapManager.FindGridsIntersecting(
|
||||
xform.MapID,
|
||||
new Box2(worldPos - gridRange, worldPos + gridRange)))
|
||||
{
|
||||
return !griddy.Inverted;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case InSpaceRule:
|
||||
{
|
||||
if (!TryComp(uid, out TransformComponent? xform) ||
|
||||
xform.GridUid != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case NearbyAccessRule access:
|
||||
{
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
if (!xformQuery.TryGetComponent(uid, out var xform) ||
|
||||
xform.MapUid == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var found = false;
|
||||
var worldPos = _transform.GetWorldPosition(xform, xformQuery);
|
||||
var count = 0;
|
||||
|
||||
// TODO: Update this when we get the callback version
|
||||
var entities = new HashSet<Entity<AccessReaderComponent>>();
|
||||
_lookup.GetEntitiesInRange(xform.MapID, worldPos, access.Range, entities);
|
||||
foreach (var comp in entities)
|
||||
{
|
||||
if (!_reader.AreAccessTagsAllowed(access.Access, comp) ||
|
||||
access.Anchored &&
|
||||
(!xformQuery.TryGetComponent(comp, out var compXform) ||
|
||||
!compXform.Anchored))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
if (count < access.Count)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
case NearbyComponentsRule nearbyComps:
|
||||
{
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
if (!xformQuery.TryGetComponent(uid, out var xform) ||
|
||||
xform.MapUid == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var found = false;
|
||||
var worldPos = _transform.GetWorldPosition(xform);
|
||||
var count = 0;
|
||||
|
||||
foreach (var compType in nearbyComps.Components.Values)
|
||||
{
|
||||
inRange.Clear();
|
||||
_lookup.GetEntitiesInRange(compType.Component.GetType(), xform.MapID, worldPos, nearbyComps.Range, inRange);
|
||||
foreach (var comp in inRange)
|
||||
{
|
||||
if (nearbyComps.Anchored &&
|
||||
(!xformQuery.TryGetComponent(comp, out var compXform) ||
|
||||
!compXform.Anchored))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
if (count < nearbyComps.Count)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
case NearbyEntitiesRule entity:
|
||||
{
|
||||
if (!TryComp(uid, out TransformComponent? xform) ||
|
||||
xform.MapUid == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var found = false;
|
||||
var worldPos = _transform.GetWorldPosition(xform);
|
||||
var count = 0;
|
||||
|
||||
foreach (var ent in _lookup.GetEntitiesInRange(xform.MapID, worldPos, entity.Range))
|
||||
{
|
||||
if (_whitelistSystem.IsWhitelistFail(entity.Whitelist, ent))
|
||||
continue;
|
||||
|
||||
count++;
|
||||
|
||||
if (count < entity.Count)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
case NearbyTilesPercentRule tiles:
|
||||
{
|
||||
if (!TryComp(uid, out TransformComponent? xform) ||
|
||||
!TryComp<MapGridComponent>(xform.GridUid, out var grid))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
var tileCount = 0;
|
||||
var matchingTileCount = 0;
|
||||
|
||||
foreach (var tile in grid.GetTilesIntersecting(new Circle(_transform.GetWorldPosition(xform),
|
||||
tiles.Range)))
|
||||
{
|
||||
// Only consider collidable anchored (for reasons some subfloor stuff has physics but non-collidable)
|
||||
if (tiles.IgnoreAnchored)
|
||||
{
|
||||
var gridEnum = grid.GetAnchoredEntitiesEnumerator(tile.GridIndices);
|
||||
var found = false;
|
||||
|
||||
while (gridEnum.MoveNext(out var ancUid))
|
||||
{
|
||||
if (!physicsQuery.TryGetComponent(ancUid, out var physics) ||
|
||||
!physics.CanCollide)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
continue;
|
||||
}
|
||||
|
||||
tileCount++;
|
||||
|
||||
if (!tiles.Tiles.Contains(_tileDef[tile.Tile.TypeId].ID))
|
||||
continue;
|
||||
|
||||
matchingTileCount++;
|
||||
}
|
||||
|
||||
if (tileCount == 0 || matchingTileCount / (float) tileCount < tiles.Percent)
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
case OnMapGridRule:
|
||||
{
|
||||
if (!TryComp(uid, out TransformComponent? xform) ||
|
||||
xform.GridUid != xform.MapUid ||
|
||||
xform.MapUid == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +1,4 @@
|
|||
Entries:
|
||||
- author: Weax
|
||||
changes:
|
||||
- message: The CLF3 reaction now requires heating first before you can engulf chemistry
|
||||
in fiery death.
|
||||
type: Tweak
|
||||
id: 6417
|
||||
time: '2024-04-22T08:44:14.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/27187
|
||||
- author: Potato1234_x
|
||||
changes:
|
||||
- message: Added Psicodine, Mannitol, Lipolicide and Happiness.
|
||||
type: Add
|
||||
id: 6418
|
||||
time: '2024-04-22T08:45:39.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/27134
|
||||
- author: Terraspark4941
|
||||
changes:
|
||||
- message: Updated the engineering section of the guidebook!
|
||||
type: Tweak
|
||||
id: 6419
|
||||
time: '2024-04-22T08:58:54.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/26851
|
||||
- author: eclips_e
|
||||
changes:
|
||||
- message: Slimepeople can now morph into a "geras"--a smaller slime form that can
|
||||
pass under grilles, at the cost of dropping all of their inventory. They can
|
||||
also be picked up with two hands and placed into duffelbags.
|
||||
type: Add
|
||||
- message: Slimepeople now have an internal 2x2 storage that they (and anyone around
|
||||
them) can access. It is not dropped when morphing into a geras!
|
||||
type: Add
|
||||
- message: Slimepeople now have slightly increased regeneration and a slightly meatier
|
||||
punch, but slower attacks.
|
||||
type: Add
|
||||
id: 6420
|
||||
time: '2024-04-22T10:03:03.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/23425
|
||||
- author: FungiFellow
|
||||
changes:
|
||||
- message: Syndi-Cats are now 6TC, Insulated, Available to Syndies, Can Move in
|
||||
Space, Open Doors, and Hit Harder
|
||||
type: Tweak
|
||||
id: 6421
|
||||
time: '2024-04-22T12:18:28.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/27222
|
||||
- author: Tayrtahn
|
||||
changes:
|
||||
- message: Ghosts can no longer trigger artifacts by examining them.
|
||||
|
|
@ -3813,3 +3768,40 @@
|
|||
id: 6916
|
||||
time: '2024-07-14T02:59:45.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/29979
|
||||
- author: SlamBamActionman
|
||||
changes:
|
||||
- message: RGBee and Rainbow Carp plushies now cycle color when held/worn.
|
||||
type: Fix
|
||||
id: 6917
|
||||
time: '2024-07-14T10:26:34.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30023
|
||||
- author: Winkarst-cpu
|
||||
changes:
|
||||
- message: Now grappling gun is clumsy proof.
|
||||
type: Tweak
|
||||
id: 6918
|
||||
time: '2024-07-14T10:26:56.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/29904
|
||||
- author: HahayesSiH
|
||||
changes:
|
||||
- message: It is now possible to pet cyborgs.
|
||||
type: Add
|
||||
- message: Clicking on cyborgs and opening the strip menu no longer unlocks them.
|
||||
type: Tweak
|
||||
id: 6919
|
||||
time: '2024-07-14T14:09:41.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30037
|
||||
- author: deltanedas
|
||||
changes:
|
||||
- message: Fixed ninja shoes not working as magboots.
|
||||
type: Fix
|
||||
id: 6920
|
||||
time: '2024-07-14T15:11:40.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/28586
|
||||
- author: lzk228
|
||||
changes:
|
||||
- message: Scarves are eatable again.
|
||||
type: Fix
|
||||
id: 6921
|
||||
time: '2024-07-14T15:12:25.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/29959
|
||||
|
|
|
|||
|
|
@ -60,12 +60,26 @@ petting-success-honkbot = You pet {THE($target)} on {POSS-ADJ($target)} slippery
|
|||
petting-success-mimebot = You pet {THE($target)} on {POSS-ADJ($target)} cold metal head.
|
||||
petting-success-cleanbot = You pet {THE($target)} on {POSS-ADJ($target)} damp metal head.
|
||||
petting-success-medibot = You pet {THE($target)} on {POSS-ADJ($target)} sterile metal head.
|
||||
petting-success-generic-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} metal head.
|
||||
petting-success-salvage-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} dirty metal head.
|
||||
petting-success-engineer-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} reflective metal head.
|
||||
petting-success-janitor-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} damp metal head.
|
||||
petting-success-medical-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} sterile metal head.
|
||||
petting-success-service-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} dapper looking metal head.
|
||||
petting-success-syndicate-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} menacing metal head.
|
||||
petting-success-recycler = You pet {THE($target)} on {POSS-ADJ($target)} mildly threatening steel exterior.
|
||||
|
||||
petting-failure-honkbot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BASIC($target, "honk", "honks")} in refusal!
|
||||
petting-failure-cleanbot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy mopping!
|
||||
petting-failure-mimebot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy miming!
|
||||
petting-failure-medibot = You reach out to pet {THE($target)}, but {POSS-ADJ($target)} syringe nearly stabs your hand!
|
||||
petting-failure-generic-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy stating laws!
|
||||
petting-failure-salvage-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy drilling!
|
||||
petting-failure-engineer-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy repairing!
|
||||
petting-failure-janitor-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy cleaning!
|
||||
petting-failure-medical-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy saving lives!
|
||||
petting-failure-service-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy serving others!
|
||||
petting-failure-syndicate-cyborg = You reach out to pet {THE($target)}, but {POSS-ADJ($target)} treacherous affiliation makes you reconsider.
|
||||
|
||||
## Rattling fences
|
||||
|
||||
|
|
|
|||
|
|
@ -38,3 +38,4 @@
|
|||
- type: Tag
|
||||
tags:
|
||||
- Scarf
|
||||
- ClothMade
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@
|
|||
- type: Clothing
|
||||
sprite: Clothing/Shoes/Specific/spaceninja.rsi
|
||||
- type: NoSlip
|
||||
- type: Magboots # always have gravity because le suction cups
|
||||
- type: ClothingSpeedModifier
|
||||
# ninja are masters of sneaking around relatively quickly, won't break cloak
|
||||
walkModifier: 1.1
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@
|
|||
- type: Lock
|
||||
locked: true
|
||||
breakOnEmag: false
|
||||
unlockOnClick: false
|
||||
- type: ActivatableUIRequiresLock
|
||||
- type: LockedWiresPanel
|
||||
- type: Damageable
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@
|
|||
node: cyborg
|
||||
- type: Speech
|
||||
speechVerb: Robotic
|
||||
- type: InteractionPopup
|
||||
interactSuccessString: petting-success-generic-cyborg
|
||||
interactFailureString: petting-failure-generic-cyborg
|
||||
interactSuccessSound:
|
||||
path: /Audio/Ambience/Objects/periodic_beep.ogg
|
||||
|
||||
- type: entity
|
||||
id: BorgChassisMining
|
||||
|
|
@ -85,6 +90,11 @@
|
|||
access: [["Cargo"], ["Salvage"], ["Command"], ["Research"]]
|
||||
- type: Inventory
|
||||
templateId: borgTall
|
||||
- type: InteractionPopup
|
||||
interactSuccessString: petting-success-salvage-cyborg
|
||||
interactFailureString: petting-failure-salvage-cyborg
|
||||
interactSuccessSound:
|
||||
path: /Audio/Ambience/Objects/periodic_beep.ogg
|
||||
|
||||
- type: entity
|
||||
id: BorgChassisEngineer
|
||||
|
|
@ -133,6 +143,11 @@
|
|||
access: [["Engineering"], ["Command"], ["Research"]]
|
||||
- type: Inventory
|
||||
templateId: borgShort
|
||||
- type: InteractionPopup
|
||||
interactSuccessString: petting-success-engineer-cyborg
|
||||
interactFailureString: petting-failure-engineer-cyborg
|
||||
interactSuccessSound:
|
||||
path: /Audio/Ambience/Objects/periodic_beep.ogg
|
||||
|
||||
- type: entity
|
||||
id: BorgChassisJanitor
|
||||
|
|
@ -189,6 +204,11 @@
|
|||
access: [["Service"], ["Command"], ["Research"]]
|
||||
- type: Inventory
|
||||
templateId: borgShort
|
||||
- type: InteractionPopup
|
||||
interactSuccessString: petting-success-janitor-cyborg
|
||||
interactFailureString: petting-failure-janitor-cyborg
|
||||
interactSuccessSound:
|
||||
path: /Audio/Ambience/Objects/periodic_beep.ogg
|
||||
|
||||
- type: entity
|
||||
id: BorgChassisMedical
|
||||
|
|
@ -248,8 +268,13 @@
|
|||
- type: FootstepModifier
|
||||
footstepSoundCollection:
|
||||
collection: FootstepHoverBorg
|
||||
- type: TTS
|
||||
voice: FactCore
|
||||
- type: InteractionPopup
|
||||
interactSuccessString: petting-success-medical-cyborg
|
||||
interactFailureString: petting-failure-medical-cyborg
|
||||
interactSuccessSound:
|
||||
path: /Audio/Ambience/Objects/periodic_beep.ogg
|
||||
- type: TTS # Sunrise-Edit
|
||||
voice: FactCore # Sunrise-Edit
|
||||
|
||||
- type: entity
|
||||
id: BorgChassisService
|
||||
|
|
@ -298,6 +323,11 @@
|
|||
access: [["Service"], ["Command"], ["Research"]]
|
||||
- type: Inventory
|
||||
templateId: borgTall
|
||||
- type: InteractionPopup
|
||||
interactSuccessString: petting-success-service-cyborg
|
||||
interactFailureString: petting-failure-service-cyborg
|
||||
interactSuccessSound:
|
||||
path: /Audio/Ambience/Objects/periodic_beep.ogg
|
||||
|
||||
- type: entity
|
||||
id: BorgChassisSyndicateAssault
|
||||
|
|
@ -327,6 +357,11 @@
|
|||
noMindState: synd_sec
|
||||
- type: Construction
|
||||
node: syndicateassault
|
||||
- type: InteractionPopup
|
||||
interactSuccessString: petting-success-syndicate-cyborg
|
||||
interactFailureString: petting-failure-syndicate-cyborg
|
||||
interactSuccessSound:
|
||||
path: /Audio/Ambience/Objects/periodic_beep.ogg
|
||||
|
||||
- type: entity
|
||||
id: BorgChassisSyndicateMedical
|
||||
|
|
@ -359,6 +394,11 @@
|
|||
- type: ShowHealthBars
|
||||
damageContainers:
|
||||
- Biological
|
||||
- type: InteractionPopup
|
||||
interactSuccessString: petting-success-syndicate-cyborg
|
||||
interactFailureString: petting-failure-syndicate-cyborg
|
||||
interactSuccessSound:
|
||||
path: /Audio/Ambience/Objects/periodic_beep.ogg
|
||||
|
||||
- type: entity
|
||||
id: BorgChassisSyndicateSaboteur
|
||||
|
|
@ -392,3 +432,8 @@
|
|||
damageContainers:
|
||||
- Inorganic
|
||||
- Silicon
|
||||
- type: InteractionPopup
|
||||
interactSuccessString: petting-success-syndicate-cyborg
|
||||
interactFailureString: petting-failure-syndicate-cyborg
|
||||
interactSuccessSound:
|
||||
path: /Audio/Ambience/Objects/periodic_beep.ogg
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
id: Wristwatch
|
||||
parent: BaseItem
|
||||
name: wristwatch
|
||||
description: A cheap watch for telling time. How much did you waste playing Space Station 14?
|
||||
description: A cheap watch for telling time. How much did you waste working on this shift?
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Devices/wristwatch.rsi
|
||||
|
|
|
|||
|
|
@ -184,6 +184,19 @@
|
|||
energy: 2
|
||||
- type: RgbLightController
|
||||
layers: [ 0 ]
|
||||
- type: Item
|
||||
inhandVisuals:
|
||||
left:
|
||||
- state: bee-inhand-left
|
||||
shader: unshaded
|
||||
right:
|
||||
- state: bee-inhand-right
|
||||
shader: unshaded
|
||||
- type: Clothing
|
||||
clothingVisuals:
|
||||
head:
|
||||
- state: bee-equipped-HELMET
|
||||
shader: unshaded
|
||||
|
||||
- type: entity
|
||||
parent: BasePlushie
|
||||
|
|
@ -543,6 +556,15 @@
|
|||
energy: 2
|
||||
- type: RgbLightController
|
||||
layers: [ 0 ]
|
||||
- type: Item
|
||||
heldPrefix: rainbowcarpplush
|
||||
inhandVisuals:
|
||||
left:
|
||||
- state: rainbowcarpplush-inhand-left
|
||||
shader: unshaded
|
||||
right:
|
||||
- state: rainbowcarpplush-inhand-right
|
||||
shader: unshaded
|
||||
|
||||
- type: entity
|
||||
parent: PlushieCarp
|
||||
|
|
|
|||
|
|
@ -251,6 +251,7 @@
|
|||
- type: Gun
|
||||
soundGunshot: /Audio/Weapons/Guns/Gunshots/harpoon.ogg
|
||||
fireRate: 0.5
|
||||
clumsyProof: true
|
||||
- type: BasicEntityAmmoProvider
|
||||
proto: GrapplingHook
|
||||
capacity: 1
|
||||
|
|
|
|||
|
|
@ -5,4 +5,5 @@
|
|||
noSpawn: true
|
||||
components:
|
||||
- type: Item
|
||||
size: Ginormous # no storage insertion visuals
|
||||
- type: VirtualItem
|
||||
|
|
|
|||
|
|
@ -53,6 +53,14 @@
|
|||
{
|
||||
"name": "rainbowcarpplush"
|
||||
},
|
||||
{
|
||||
"name": "rainbowcarpplush-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "rainbowcarpplush-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "narplush"
|
||||
},
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Loading…
Add table
Reference in a new issue