Merge branch 'master' into airalarm

This commit is contained in:
BriBrooo 2026-07-26 14:12:51 +02:00 committed by GitHub
commit 94157358a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
290 changed files with 6223 additions and 985 deletions

View File

@ -2,6 +2,7 @@ using Content.Client.Cargo.UI;
using Content.Shared.Cargo.Components;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
using Content.Shared._DV.Cargo.Components; // DeltaV: Bounty claim messages
namespace Content.Client.Cargo.BUI;
@ -30,6 +31,18 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
{
SendMessage(new BountySkipMessage(id));
};
// DeltaV: bounty claim stuff begins
_menu.OnClaimButtonPressed += id =>
{
SendMessage(new BountyClaimedMessage(id));
};
_menu.OnStatusOptionSelected += (id, status) =>
{
SendMessage(new BountySetStatusMessage(id, status));
};
// DeltaV: bounty claim stuff ends
}
protected override void UpdateState(BoundUserInterfaceState message)

View File

@ -10,6 +10,8 @@
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
<RichTextLabel Name="RewardLabel"/>
<RichTextLabel Name="ManifestLabel"/>
<RichTextLabel Name="ClaimedBylabel"/> <!-- DeltaV bounty claim -->
<RichTextLabel Name="StatusLabel"/> <!-- DeltaV bounty claim -->
</BoxContainer>
<Control MinWidth="10"/>
<BoxContainer Orientation="Vertical" MinWidth="120">
@ -17,14 +19,22 @@
<Button Name="PrintButton"
Text="{Loc 'bounty-console-label-button-text'}"
HorizontalExpand="False"
HorizontalAlignment="Right"
StyleClasses="OpenRight"/>
StyleClasses="OpenBoth"/>
<Button Name="SkipButton"
Text="{Loc 'bounty-console-skip-button-text'}"
HorizontalExpand="False"
HorizontalAlignment="Right"
HorizontalExpand="True"
StyleClasses="OpenLeft"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" MinWidth="120"> <!-- Begin DeltaV bounty claim content -->
<Button Name="ClaimButton"
Text= "{Loc 'bounty-console-claim-button-text'}"
HorizontalExpand="False"
StyleClasses="OpenRight"/>
<OptionButton Name="BountyStatusSelector"
Access="Public"
StyleClasses="OpenBoth"
HorizontalExpand="True"/>
</BoxContainer> <!-- End DeltaV content-->
<RichTextLabel Name="IdLabel" HorizontalAlignment="Right" Margin="0 0 5 0"/>
</BoxContainer>
</BoxContainer>

View File

@ -17,6 +17,8 @@ public sealed partial class BountyEntry : BoxContainer
public Action? OnLabelButtonPressed;
public Action? OnSkipButtonPressed;
public Action? OnClaimButtonPressed; // DeltaV
public Action? OnStatusOptionSelected; // DeltaV
public TimeSpan EndTime;
public TimeSpan UntilNextSkip;
@ -45,6 +47,20 @@ public sealed partial class BountyEntry : BoxContainer
PrintButton.OnPressed += _ => OnLabelButtonPressed?.Invoke();
SkipButton.OnPressed += _ => OnSkipButtonPressed?.Invoke();
// Begin DeltaV bounty claiming
ClaimButton.OnPressed += _ => OnClaimButtonPressed?.Invoke();
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.Undelivered)}"), 0);
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.Waiting)}"), 1);
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.OnShuttle)}"), 2);
BountyStatusSelector.Select((int) bounty.Status);
BountyStatusSelector.ToolTip = Loc.GetString($"bounty-console-status-tooltip-{bounty.Status.ToString()}");
var claimedByText = string.IsNullOrEmpty(bounty.ClaimedBy) ? Loc.GetString("bounty-console-claimed-by-none") : bounty.ClaimedBy;
ClaimedBylabel.SetMarkup(Loc.GetString("bounty-console-claimed-by", ("claimant", claimedByText)));
StatusLabel.SetMarkup(Loc.GetString($"bounty-console-formatted-status-{bounty.Status.ToString()}"));
// End DeltaV bounty claiming
}
private void UpdateSkipButton(float deltaSeconds)

View File

@ -1,8 +1,9 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
<!--DeltaV - SetSize was 550, now 750 to accomodate bounty claim UI -->
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'bounty-console-menu-title'}"
SetSize="550 420"
SetSize="750 420"
MinSize="400 350">
<BoxContainer Orientation="Vertical"
VerticalExpand="True"

View File

@ -11,6 +11,8 @@ public sealed partial class CargoBountyMenu : FancyWindow
{
public Action<string>? OnLabelButtonPressed;
public Action<string>? OnSkipButtonPressed;
public Action<string>? OnClaimButtonPressed; // DeltaV
public Action<string, int>? OnStatusOptionSelected; // DeltaV
public CargoBountyMenu()
{
@ -31,6 +33,8 @@ public sealed partial class CargoBountyMenu : FancyWindow
var entry = new BountyEntry(b, untilNextSkip);
entry.OnLabelButtonPressed += () => OnLabelButtonPressed?.Invoke(b.Id);
entry.OnSkipButtonPressed += () => OnSkipButtonPressed?.Invoke(b.Id);
entry.OnClaimButtonPressed += () => OnClaimButtonPressed?.Invoke(b.Id); // DeltaV
entry.BountyStatusSelector.OnItemSelected += args => OnStatusOptionSelected?.Invoke(b.Id, args.Id); // DeltaV
BountyEntriesContainer.AddChild(entry);
}

View File

@ -34,6 +34,9 @@ namespace Content.Client.Input
common.AddFunction(ContentKeyFunctions.RotateStoredItem);
common.AddFunction(ContentKeyFunctions.SaveItemLocation);
common.AddFunction(ContentKeyFunctions.Point);
// Floofstation section
common.AddFunction(ContentKeyFunctions.OfferItem);
// Floofstation section end
common.AddFunction(ContentKeyFunctions.ZoomOut);
common.AddFunction(ContentKeyFunctions.ZoomIn);
common.AddFunction(ContentKeyFunctions.ResetZoom);

View File

@ -75,9 +75,11 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
if (!LocalizedNames.TryGetValue(netEntity, out var name))
name = Loc.GetString("navmap-unknown-entity");
var pos = _xform.ToMapCoordinates(blip.Coordinates); // DeltaV - map-coordinates
var message = name + "\n" + Loc.GetString("navmap-location",
("x", MathF.Round(blip.Coordinates.X)),
("y", MathF.Round(blip.Coordinates.Y)));
("x", MathF.Round(pos.X)), // DeltaV - map-coordinates
("y", MathF.Round(pos.Y))); // DeltaV - map-coordinates
_trackedEntityLabel.Text = message;
_trackedEntityPanel.Visible = true;

View File

@ -208,6 +208,7 @@ namespace Content.Client.Options.UI.Tabs
AddButton(ContentKeyFunctions.MovePulledObject);
AddButton(ContentKeyFunctions.ReleasePulledObject);
AddButton(ContentKeyFunctions.Point);
AddButton(ContentKeyFunctions.OfferItem); // Floofstation
AddButton(ContentKeyFunctions.RotateObjectClockwise);
AddButton(ContentKeyFunctions.RotateObjectCounterclockwise);
AddButton(ContentKeyFunctions.FlipObject);

View File

@ -0,0 +1,73 @@
using System.Linq;
using Content.Shared._DV.Forensics;
using Robust.Client.GameObjects;
using Robust.Client.ResourceManagement;
using Robust.Shared.Containers;
using Robust.Shared.Utility;
namespace Content.Client._DV.Forensics;
public sealed class DVItemSlotVisualsSystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
private static readonly ResPath TextureRoot = new("/Textures");
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DVItemSlotVisualsComponent, EntInsertedIntoContainerMessage>(OnInsertedIntoContainer);
SubscribeLocalEvent<DVItemSlotVisualsComponent, EntRemovedFromContainerMessage>(OnRemovedFromContainer);
}
private void RefreshVisuals(Entity<DVItemSlotVisualsComponent> ent)
{
var slot = _container.EnsureContainer<ContainerSlot>(ent, ent.Comp.ItemSlot);
if (slot.ContainedEntity is { } contained)
{
_sprite.CopySprite(contained, ent.Owner);
_sprite.AddLayer(ent.Owner, ent.Comp.FilledSprite);
}
else
{
var sprite = Comp<SpriteComponent>(ent);
var count = sprite.AllLayers.Count();
for (var i = count - 1; i >= 0; i--)
{
_sprite.RemoveLayer((ent, sprite), i);
}
if (ent.Comp.UnfilledSprite is not SpriteSpecifier.Rsi rsi)
{
throw new InvalidOperationException($"{ToPrettyString(ent)} has an unfilled sprite that's not an RSI");
}
if (!_resourceCache.TryGetResource<RSIResource>(TextureRoot / rsi.RsiPath, out var res))
{
throw new InvalidOperationException($"{ToPrettyString(ent)} has invalid RSI: {TextureRoot / rsi.RsiPath}");
}
_sprite.SetBaseRsi(ent.Owner, res.RSI);
_sprite.AddLayer(ent.Owner, ent.Comp.UnfilledSprite);
}
}
private void OnInsertedIntoContainer(Entity<DVItemSlotVisualsComponent> ent, ref EntInsertedIntoContainerMessage args)
{
if (args.Container.ID != ent.Comp.ItemSlot)
return;
RefreshVisuals(ent);
}
private void OnRemovedFromContainer(Entity<DVItemSlotVisualsComponent> ent, ref EntRemovedFromContainerMessage args)
{
if (args.Container.ID != ent.Comp.ItemSlot)
return;
RefreshVisuals(ent);
}
}

View File

@ -0,0 +1,71 @@
using System.Numerics;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.UserInterface;
using Robust.Shared.Enums;
using Robust.Shared.Utility;
namespace Content.Client._Floof.OfferItem;
public sealed class OfferItemIndicatorsOverlay : Overlay
{
private readonly IInputManager _inputManager;
private readonly IEntityManager _entMan;
private readonly IEyeManager _eye;
private readonly OfferItemSystem _offer;
private readonly Texture _sight;
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
private readonly Color _mainColor = Color.White.WithAlpha(0.3f);
private readonly Color _strokeColor = Color.Black.WithAlpha(0.5f);
private readonly float _scale = 0.6f; // 1 is a little big
public OfferItemIndicatorsOverlay(IInputManager input, IEntityManager entMan,
IEyeManager eye, OfferItemSystem offerSys)
{
_inputManager = input;
_entMan = entMan;
_eye = eye;
_offer = offerSys;
var spriteSys = _entMan.EntitySysManager.GetEntitySystem<SpriteSystem>();
_sight = spriteSys.Frame0(new SpriteSpecifier.Rsi(new("/Textures/_Floof/Interface/Misc/give_item.rsi"), "give_item"));
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (!_offer.IsInOfferMode())
return false;
return base.BeforeDraw(in args);
}
protected override void Draw(in OverlayDrawArgs args)
{
var mouseScreenPosition = _inputManager.MouseScreenPosition;
var mousePosMap = _eye.PixelToMap(mouseScreenPosition);
if (mousePosMap.MapId != args.MapId)
return;
var mousePos = mouseScreenPosition.Position;
var uiScale = (args.ViewportControl as Control)?.UIScale ?? 1f;
var limitedScale = uiScale > 1.25f ? 1.25f : uiScale;
DrawSight(_sight, args.ScreenHandle, mousePos, limitedScale * _scale);
}
private void DrawSight(Texture sight, DrawingHandleScreen screen, Vector2 centerPos, float scale)
{
var sightSize = sight.Size * scale;
var expandedSize = sightSize + new Vector2(7f, 7f);
screen.DrawTextureRect(sight,
UIBox2.FromDimensions(centerPos - sightSize * 0.5f, sightSize), _strokeColor);
screen.DrawTextureRect(sight,
UIBox2.FromDimensions(centerPos - expandedSize * 0.5f, expandedSize), _mainColor);
}
}

View File

@ -0,0 +1,39 @@
using Content.Shared._Floof.OfferItem;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
namespace Content.Client._Floof.OfferItem;
public sealed class OfferItemSystem : SharedOfferItemSystem
{
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IEyeManager _eye = default!;
public override void Initialize()
{
base.Initialize();
_overlayManager.AddOverlay(new OfferItemIndicatorsOverlay(
_inputManager,
EntityManager,
_eye,
this));
}
public override void Shutdown()
{
_overlayManager.RemoveOverlay<OfferItemIndicatorsOverlay>();
base.Shutdown();
}
public bool IsInOfferMode()
{
var entity = _playerManager.LocalEntity;
if (entity == null)
return false;
return IsInOfferMode(entity.Value);
}
}

View File

@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Access.Systems; // DeltaV
using Content.Server.Cargo.Components;
using Content.Server.NameIdentifier;
using Content.Shared.Access.Components;
@ -20,6 +21,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Content.Shared._DV.Cargo.Components; // DeltaV: Bounty claim messages
namespace Content.Server.Cargo.Systems;
@ -28,6 +30,7 @@ public sealed partial class CargoSystem
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly NameIdentifierSystem _nameIdentifier = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSys = default!;
[Dependency] private readonly IdCardSystem _idCard = default!; // DeltaV
private static readonly ProtoId<NameIdentifierGroupPrototype> BountyNameIdentifierGroup = "Bounty";
@ -40,6 +43,8 @@ public sealed partial class CargoSystem
SubscribeLocalEvent<CargoBountyConsoleComponent, BoundUIOpenedEvent>(OnBountyConsoleOpened);
SubscribeLocalEvent<CargoBountyConsoleComponent, BountyPrintLabelMessage>(OnPrintLabelMessage);
SubscribeLocalEvent<CargoBountyConsoleComponent, BountySkipMessage>(OnSkipBountyMessage);
SubscribeLocalEvent<CargoBountyConsoleComponent, BountyClaimedMessage>(OnBountyClaimedMessage); // DeltaV
SubscribeLocalEvent<CargoBountyConsoleComponent, BountySetStatusMessage>(OnSetBountyStatusMessage); // DeltaV
SubscribeLocalEvent<CargoBountyLabelComponent, PriceCalculationEvent>(OnGetBountyPrice);
SubscribeLocalEvent<EntitySoldEvent>(OnSold);
SubscribeLocalEvent<StationCargoBountyDatabaseComponent, MapInitEvent>(OnMapInit);
@ -111,6 +116,72 @@ public sealed partial class CargoSystem
_audio.PlayPvs(component.SkipSound, uid);
}
// Begin DeltaV bounty claim system
private void OnSetBountyStatusMessage(Entity<CargoBountyConsoleComponent> ent, ref BountySetStatusMessage args)
{
if (_station.GetOwningStation(ent.Owner) is not { } station || !TryComp<StationCargoBountyDatabaseComponent>(station, out var bountyDbComp))
return;
for (var i = 0; i < bountyDbComp.Bounties.Count; i++)
{
var bounty = bountyDbComp.Bounties[i];
if (bounty.Id != args.BountyId)
continue;
var newData = new CargoBountyData
{
Id = bounty.Id,
Bounty = bounty.Bounty,
ClaimedBy = bounty.ClaimedBy,
Status = (CargoBountyStatus)args.Status,
};
bountyDbComp.Bounties[i] = newData;
}
var untilNextSkip = bountyDbComp.NextSkipTime - Timing.CurTime;
_uiSystem.SetUiState(ent.Owner, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(bountyDbComp.Bounties, bountyDbComp.History, untilNextSkip));
}
private void OnBountyClaimedMessage(Entity<CargoBountyConsoleComponent> ent, ref BountyClaimedMessage args)
{
if (_station.GetOwningStation(ent.Owner) is not { } station || !TryComp<StationCargoBountyDatabaseComponent>(station, out var bountyDbComp))
return;
for (var i = 0; i < bountyDbComp.Bounties.Count; i++)
{
var bounty = bountyDbComp.Bounties[i];
if (bounty.Id != args.BountyId)
continue;
string name;
if (_idCard.TryFindIdCard(args.Actor, out var idCard) && idCard.Comp.FullName != null)
{
name = idCard.Comp.FullName;
}
else
{
name = Loc.GetString("bounty-console-claimed-by-unknown");
}
var newData = new CargoBountyData
{
Id = bounty.Id,
Bounty = bounty.Bounty,
ClaimedBy = name.Equals(bounty.ClaimedBy) ? string.Empty : name,
Status = bounty.Status,
};
bountyDbComp.Bounties[i] = newData;
}
var untilNextSkip = bountyDbComp.NextSkipTime - Timing.CurTime;
_uiSystem.SetUiState(ent.Owner, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(bountyDbComp.Bounties, bountyDbComp.History, untilNextSkip));
}
// End DeltaV bounty claim system
public void SetupBountyLabel(EntityUid uid, EntityUid stationId, CargoBountyData bounty, PaperComponent? paper = null, CargoBountyLabelComponent? label = null)
{
if (!Resolve(uid, ref paper, ref label) || !_protoMan.Resolve<CargoBountyPrototype>(bounty.Bounty, out var prototype))

View File

@ -23,6 +23,7 @@ using Robust.Shared.Timing;
using System.Linq;
using Content.Shared.EntityEffects.Effects.Solution;
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
using Content.Server.Body.Components; // Delta V
namespace Content.Server.Fluids.EntitySystems;
@ -267,7 +268,8 @@ public sealed class SmokeSystem : EntitySystem
if (!_solutionContainerSystem.ResolveSolution(entity, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution) || bloodSolution.AvailableVolume <= 0)
return;
var blockIngestion = _internals.AreInternalsWorking(entity);
var blockIngestion = _internals.AreInternalsWorking(entity)
|| !HasComp<RespiratorComponent>(entity); // Starlight - Shadekin does not breathe and "AreInternalsWorking" does not check for that
var cloneSolution = solution.Clone();
var availableTransfer = FixedPoint2.Min(cloneSolution.Volume, component.TransferRate);

View File

@ -41,6 +41,7 @@ using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Content.Server.Mobs; // Starlight
namespace Content.Server.Ghost
{
@ -619,7 +620,15 @@ namespace Content.Server.Ghost
_damageable.GetTotalDamage((playerEntity.Value, damageable));
}
DamageSpecifier damage = new(_prototypeManager.Index(AsphyxiationDamageType), dealtDamage);
// Starlight - Start
//DamageSpecifier damage = new(_prototypeManager.Index(AsphyxiationDamageType), dealtDamage);
var damageType = _prototypeManager.Index(AsphyxiationDamageType);
if (TryComp<DeathgaspComponent>(playerEntity, out var deathgasp))
damageType = _prototypeManager.Index(deathgasp.DamageType);
DamageSpecifier damage = new(damageType, dealtDamage);
// Starlight - End
_damageable.ChangeDamage(playerEntity.Value, damage, true);
}

View File

@ -1,4 +1,6 @@
using Content.Shared.Chat.Prototypes;
using Content.Shared.Chat.Prototypes; // Starlight
using Content.Shared.Damage.Prototypes; // Starlight
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Mobs;
@ -21,4 +23,10 @@ public sealed partial class DeathgaspComponent : Component
/// </summary>
[DataField]
public bool NeedsCritical = true;
/// <summary>
/// Starlight - The damage that is taken when succumbing
/// </summary>
[DataField]
public ProtoId<DamageTypePrototype> DamageType = "Asphyxiation";
}

View File

@ -55,9 +55,13 @@ public sealed class PseudoItemSystem : SharedPseudoItemSystem
protected override void OnGettingPickedUpAttempt(EntityUid uid, PseudoItemComponent component, GettingPickedUpAttemptEvent args)
{
// Floof - changed this a bit to actually start a do-after
// Try to pick the entity up instead first
if (args.User != args.Item && _carrying.TryCarry(args.User, uid))
if (args.User != args.Item
&& TryComp<CarriableComponent>(uid, out var carriable)
&& _carrying.CanCarry(args.User, (uid, carriable)))
{
_carrying.StartCarryDoAfter(args.User, (uid, carriable));
args.Cancel();
return;
}

View File

@ -209,20 +209,24 @@ namespace Content.Server.Psionics.Glimmer
private void OnDestroyed(EntityUid uid, SharedGlimmerReactiveComponent component, DestructionEventArgs args)
{
Spawn("MaterialBluespace1", Transform(uid).Coordinates);
var proberCoords = Transform(uid).Coordinates;
var tier = _glimmerSystem.GetGlimmerTier();
if (tier < GlimmerTier.High)
return;
var totalIntensity = (float) (_glimmerSystem.Glimmer * 2);
var slope = (float) (11 - _glimmerSystem.Glimmer / 100);
var maxIntensity = 20;
var explosionMultiplier = 2;
if (_glimmerSystem.GetGlimmerTier() == GlimmerTier.Critical) // YOU DONE FUCKED UP
explosionMultiplier = 3;
var removed = (float) _glimmerSystem.Glimmer * _random.NextFloat(0.06f, 0.08f);
_glimmerSystem.Glimmer -= (int) removed;
var totalIntensity = (float)(_glimmerSystem.Glimmer * explosionMultiplier);
var slope = (float)(11 - _glimmerSystem.Glimmer / 100);
var maxIntensity = 75; // Same as syndicate bomb
var removed = _glimmerSystem.Glimmer * _random.NextFloat(0.06f, 0.08f);
_glimmerSystem.Glimmer -= (int)removed;
BeamRandomNearProber(uid, _glimmerSystem.Glimmer / 350, _glimmerSystem.Glimmer / 50);
_explosionSystem.QueueExplosion(uid, "Default", totalIntensity, slope, maxIntensity);
_explosionSystem.QueueExplosion(uid, "Default", totalIntensity, slope, maxIntensity, addLog: true);
Spawn("MaterialBluespace1", proberCoords); // Congrats on your bluespace!
}
private void OnUnanchorAttempt(EntityUid uid, SharedGlimmerReactiveComponent component, UnanchorAttemptEvent args)
@ -230,7 +234,7 @@ namespace Content.Server.Psionics.Glimmer
if (component.Locked)
{
_sharedAudioSystem.PlayPvs(component.ShockNoises, args.User);
_electrocutionSystem.TryDoElectrocution(args.User, null, _glimmerSystem.Glimmer / 200, TimeSpan.FromSeconds((float) _glimmerSystem.Glimmer / 100), false);
_electrocutionSystem.TryDoElectrocution(args.User, uid, _glimmerSystem.Glimmer / 200, TimeSpan.FromSeconds((float) _glimmerSystem.Glimmer / 100), false);
args.Cancel();
}
}

View File

@ -184,7 +184,8 @@ public sealed class OracleSystem : EntitySystem
while (i != 0)
{
Spawn("MaterialBluespace1", Transform(user).Coordinates);
var entityToSpawn = _random.Next(0, 2) == 0 ? "MaterialBluespace1" : "CrystalNormality";
Spawn(entityToSpawn, Transform(user).Coordinates);
i--;
}

View File

@ -125,12 +125,12 @@ public sealed class ProjectileSystem : SharedProjectileSystem
RaiseLocalEvent(projectile, ref pierceEv);
// If the object won't be destroyed, it "tanks" the penetration hit.
if (damage.GetTotal() < damageRequired)
if (damage.GetTotal() < damageRequired && !pierceEv.Pierced) // DeltaV - Addition of the NT-3
{
return false;
}
if (!projectile.Comp.ProjectileSpent)
if (!projectile.Comp.ProjectileSpent && !pierceEv.Pierced) // DeltaV - Addition of the NT-3
{
projectile.Comp.PenetrationAmount += damageRequired;
// The projectile has dealt enough damage to be spent.
@ -139,7 +139,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem
return false;
}
if (projectile.Comp.ProjectileSpent && pierceEv.Pierced) // DeltaV - Addition of the NT-3
if (projectile.Comp.ProjectileSpent)
{
return true;
}

View File

@ -1,4 +1,5 @@
using Content.Shared.Tag;
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes;
namespace Content.Server._DV.Projectiles.Components;
@ -21,10 +22,14 @@ public sealed partial class PiercingProjectileComponent : Component
public float PierceCounter;
/// <summary>
/// The tag that will cause the piercing bullet to increment it's <see cref="PierceCounter"/>.
/// The whitelist for checking what increments the <see cref="PierceCounter"/>.
/// </summary>
/// <example>
/// If this has the tag "Wall" in it, any entity with the tag "Wall" will increment <see cref="PierceCounter"/>
/// upon being hit.
/// </example>
[DataField]
public List<ProtoId<TagPrototype>> PierceBlockTag = ["Wall", "Window"];
public EntityWhitelist PierceCounterWhitelist;
/// <summary>
/// The number of entities it is allowed to pierce before being deleted.

View File

@ -3,7 +3,7 @@ using Content.Shared.FixedPoint;
namespace Content.Server._DV.Projectiles.Events;
/// <summary>
/// Raised when a piercing projectile hits an entity that doesn't follow upstream piercing rules.
/// Raised when a piercing projectile that doesn't follow upstream piercing rules hits an entity.
/// </summary>
[ByRefEvent]
public record struct ProjectilePierceEvent(EntityUid Target, FixedPoint2 RequiredDamage, bool Pierced = false);

View File

@ -1,29 +1,33 @@
using Content.Server._DV.Projectiles.Components;
using Content.Server._DV.Projectiles.Events;
using Content.Shared.Tag;
using Content.Shared.Whitelist;
namespace Content.Server._DV.Projectiles.Systems;
public sealed class PiercingProjectileSystem : EntitySystem
{
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
// Mobs return a required Damage amount of Float.MaxValue. Therefore, we need to check for absurdly high values.
private readonly int _indestructibleNumber = 20000000;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PiercingProjectileComponent, ProjectilePierceEvent>(OnPierce);
}
private void OnPierce(Entity<PiercingProjectileComponent> bullet, ref ProjectilePierceEvent args)
{
// If the target doesn't have any tags to stop the bullet from piercing, it's automatically true.
if (!_tagSystem.HasAnyTag(args.Target, bullet.Comp.PierceBlockTag))
if (_whitelist.IsWhitelistFail(bullet.Comp.PierceCounterWhitelist, args.Target))
{
args.Pierced = true;
return;
}
// If it does have the tag to stop it and enough health to count as "strongly armored", it'll block the bullet.
if (bullet.Comp.HealthThreshold < args.RequiredDamage)
if (bullet.Comp.HealthThreshold < args.RequiredDamage && args.RequiredDamage < _indestructibleNumber)
return;
if (bullet.Comp.Direction == null) // Get the direction of the bullet to determine which walls count.

View File

@ -0,0 +1,163 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
using System.Text.RegularExpressions;
using Content.Server.Administration;
using Content.Shared.Administration;
using Robust.Shared.Console;
using Robust.Shared.Maths;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Errors;
using Robust.Shared.Utility;
namespace Content.Server._DV.Toolshed;
/// <summary>
/// An extended version of the engine's <c>do</c> command. Runs a console command once per piped value,
/// substituting <c>$NAME</c> tokens in the command string first.
/// </summary>
/// <remarks>
/// On top of the built-in tokens that <c>do</c> supports (<c>$ID</c>, <c>$PID</c>, <c>$WX</c>, <c>$WY</c>,
/// <c>$LX</c>, <c>$LY</c>, <c>$SELF</c>), any other <c>$name</c> is resolved as a toolshed variable and
/// converted to a string. Tokens that don't resolve to anything are left untouched.
///
/// Where the command actually runs is explicit:
/// <list type="bullet">
/// <item><c>doext:client</c> sends it back down to the calling user's own client to run locally. This is what
/// you want for client-side commands like <c>exec</c>, which resolve paths against the client's user data.</item>
/// <item><c>doext:server</c> runs it server-side as the calling session, like <c>do</c> does.</item>
/// </list>
/// </remarks>
/// <example><code>
/// self doext:client "exec /Script_$var.txt"
/// i 5 => $count; self doext:server "somecommand $count"
/// </code></example>
[ToolshedCommand(Name = "doext"), AdminCommand(AdminFlags.Debug)]
public sealed class DoExtCommand : ToolshedCommand
{
/// <summary>
/// Matches a <c>$name</c> token. Names use the same character set as toolshed variable names, so this
/// matches whole identifiers - <c>$IDLE</c> resolves as "IDLE" rather than being mangled into <c>$ID</c>.
/// </summary>
private static readonly Regex TokenRegex = new(@"\$(\w+)", RegexOptions.Compiled);
[Dependency] private readonly IConsoleHost _console = default!;
private SharedTransformSystem? _xform;
/// <summary>
/// Sends the command back down to the calling user's client, which runs it locally.
/// </summary>
[CommandImplementation("client"), TakesPipedTypeAsGeneric]
public IEnumerable<T> Client<T>(IInvocationContext ctx, [PipedArgument] IEnumerable<T> input, string command)
{
// No session means there's no client to hand this back to - e.g. the server console.
if (ctx.Session is not { } session)
{
ctx.ReportError(new NoClientSessionError());
yield break;
}
foreach (var value in input)
{
_console.RemoteExecuteCommand(session, Substitute(command, value, ctx));
yield return value;
}
}
/// <summary>
/// Runs the command on the server as the calling session.
/// </summary>
[CommandImplementation("server"), TakesPipedTypeAsGeneric]
public IEnumerable<T> Server<T>(IInvocationContext ctx, [PipedArgument] IEnumerable<T> input, string command)
{
foreach (var value in input)
{
_console.ExecuteCommand(ctx.Session, Substitute(command, value, ctx));
yield return value;
}
}
private string Substitute<T>(string command, T value, IInvocationContext ctx)
{
return TokenRegex.Replace(command, match =>
{
var name = match.Groups[1].Value;
if (TryGetBuiltin(name, value, ctx, out var builtin))
return builtin;
// Anything else is looked up as a toolshed variable and implicitly stringified.
if (ctx.ReadVar(name) is { } variable)
return Stringify(variable);
// Unresolved, so leave the token alone rather than silently blanking it.
return match.Value;
});
}
private bool TryGetBuiltin<T>(string name, T value, IInvocationContext ctx, [NotNullWhen(true)] out string? result)
{
switch (name)
{
case "SELF":
result = Stringify(value);
return true;
case "PID":
result = (ctx.Session?.AttachedEntity ?? EntityUid.Invalid).ToString();
return true;
}
// The remaining tokens are all positional, so they only apply when piping entities.
if (value is not EntityUid uid)
{
result = null;
return false;
}
switch (name)
{
case "ID":
result = uid.ToString();
return true;
case "WX":
result = Number(WorldPosition(uid).X);
return true;
case "WY":
result = Number(WorldPosition(uid).Y);
return true;
case "LX":
result = Number(Transform(uid).Coordinates.X);
return true;
case "LY":
result = Number(Transform(uid).Coordinates.Y);
return true;
}
result = null;
return false;
}
private Vector2 WorldPosition(EntityUid uid)
{
_xform ??= GetSys<SharedTransformSystem>();
return _xform.GetWorldPosition(uid);
}
private static string Number(float value) => value.ToString(CultureInfo.InvariantCulture);
private static string Stringify(object? value) => value?.ToString() ?? string.Empty;
}
public sealed class NoClientSessionError : IConError
{
public FormattedMessage DescribeInner()
{
return FormattedMessage.FromUnformatted("There is no client to run this on. doext:client must be run by a player, not the server console.");
}
public string? Expression { get; set; }
public Vector2i? IssueSpan { get; set; }
public StackTrace? Trace { get; set; }
}

View File

@ -0,0 +1,136 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Content.Server.Administration;
using Content.Shared.Administration;
using Robust.Shared.Console;
using Robust.Shared.Maths;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Errors;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Server._DV.Toolshed;
/// <summary>
/// Reads the value of a single field or property off of the piped input, by name.
/// Member names are resolved the same way <c>vvread</c> does: the member must be exposed
/// via <c>[ViewVariables]</c>, <c>[DataField]</c>, or <c>[IncludeDataField]</c>.
/// </summary>
/// <example><code>ent 12345 . Name</code></example>
[ToolshedCommand(Name = "."), AdminCommand(AdminFlags.Debug)]
public sealed class FieldCommand : ToolshedCommand
{
[CommandImplementation]
public object? Field([PipedArgument] object? value, [CommandArgument(typeof(FieldNameParser))] string field, IInvocationContext ctx)
{
if (value == null)
{
ctx.ReportError(new NullInputFieldError());
return null;
}
var type = value.GetType();
var member = GetSingleMember(type, field);
// Restrict to members that vvread would let you read, so this can't be used to peek at arbitrary internals.
if (member == null || !ViewVariablesUtility.TryGetViewVariablesAccess(member, out _))
{
ctx.ReportError(new NoSuchFieldError(type, field));
return null;
}
return member switch
{
FieldInfo f => f.GetValue(value),
PropertyInfo p => p.GetValue(value),
_ => null,
};
}
/// <summary>
/// Finds the field or property with the given name, mirroring the resolution used by <c>vvread</c>.
/// </summary>
private static MemberInfo? GetSingleMember(Type type, string member)
{
var members = type
.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(m => m.Name == member && m is FieldInfo or PropertyInfo)
.ToArray();
if (members.Length == 0)
return null;
// In case there's member hiding going on, grab the one declared by the type of the object by default.
return members.Length > 1
? members.FirstOrDefault(m => m.DeclaringType == type) ?? members[0]
: members[0];
}
}
/// <summary>
/// Parses a field name as a bare identifier (letters, digits, underscore) so that the <c>.</c> command doesn't
/// require the name to be wrapped in quotes like the default <see cref="string"/> parser does.
/// </summary>
public sealed class FieldNameParser : CustomTypeParser<string>
{
public override bool TryParse(ParserContext ctx, [NotNullWhen(true)] out string? result)
{
ctx.ConsumeWhitespace();
result = ctx.GetWord(ParserContext.IsToken);
if (result != null)
return true;
if (ctx.PeekRune() is null)
ctx.Error = new OutOfInputError();
else
ctx.Error = new InvalidFieldNameError();
ctx.Error.Contextualize(ctx.Input, (ctx.Index, ctx.Index + 1));
return false;
}
public override CompletionResult? TryAutocomplete(ParserContext ctx, CommandArgument? arg)
{
return CompletionResult.FromHint(GetArgHint(arg));
}
}
public sealed class InvalidFieldNameError : IConError
{
public FormattedMessage DescribeInner()
{
return FormattedMessage.FromUnformatted("Expected a field name (letters, digits, or underscores).");
}
public string? Expression { get; set; }
public Vector2i? IssueSpan { get; set; }
public StackTrace? Trace { get; set; }
}
public sealed class NullInputFieldError : IConError
{
public FormattedMessage DescribeInner()
{
return FormattedMessage.FromUnformatted("Cannot read a field off of a null input.");
}
public string? Expression { get; set; }
public Vector2i? IssueSpan { get; set; }
public StackTrace? Trace { get; set; }
}
public sealed class NoSuchFieldError(Type type, string field) : IConError
{
public FormattedMessage DescribeInner()
{
return FormattedMessage.FromUnformatted($"Type {type.Name} has no readable field or property named '{field}'. It must be exposed via [ViewVariables], [DataField], or [IncludeDataField].");
}
public string? Expression { get; set; }
public Vector2i? IssueSpan { get; set; }
public StackTrace? Trace { get; set; }
}

View File

@ -0,0 +1,5 @@
using Content.Shared._Floof.OfferItem;
namespace Content.Server._Floof.OfferItem;
public sealed partial class OfferItemSystem : SharedOfferItemSystem;

View File

@ -0,0 +1,290 @@
using Content.Shared.Humanoid;
using Content.Shared.Alert;
using Robust.Server.GameObjects;
using Content.Shared.Examine;
using Robust.Server.Containers;
using Content.Shared._Starlight;
using Content.Shared.Damage.Components;
using Content.Shared.Mobs;
using Content.Shared.Movement.Systems;
using Content.Shared.Movement.Components;
using Content.Shared.Damage;
using Content.Server.Chat.Managers;
using Content.Shared.Damage.Systems;
using Content.Shared._Goobstation.Overlays;
using Robust.Shared.Timing;
using Content.Server.Body.Components;
using System.Linq;
using Content.Shared._Goobstation.Flashbang;
using Content.Shared._Starlight.Flash.Components;
using Content.Shared.Body.Components;
using Content.Shared.Flash; // Delta V - Flash Work
namespace Content.Server._Starlight;
public sealed class ShadekinSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly MovementSpeedModifierSystem _speed = default!;
[Dependency] private readonly SharedFlashSystem _flashSystem = default!;
private sealed class LightCone
{
public float Direction { get; set; }
public float InnerWidth { get; set; }
public float OuterWidth { get; set; }
}
private readonly Dictionary<string, List<LightCone>> lightMasks = new()
{
["/Textures/Effects/LightMasks/cone.png"] = new List<LightCone>
{
new LightCone { Direction = 0, InnerWidth = 30, OuterWidth = 60 }
},
["/Textures/Effects/LightMasks/double_cone.png"] = new List<LightCone>
{
new LightCone { Direction = 0, InnerWidth = 30, OuterWidth = 60 },
new LightCone { Direction = 180, InnerWidth = 30, OuterWidth = 60 }
}
};
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ShadekinComponent, ComponentStartup>(OnInit);
SubscribeLocalEvent<ShadekinComponent, EyeColorInitEvent>(OnEyeColorChange);
SubscribeLocalEvent<ShadekinComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeedModifiers);
SubscribeLocalEvent<ShadekinComponent, AfterFlashedEvent>(OnShadekinFlashed); // Delta V - Prevent Chain Flashing
SubscribeLocalEvent<ShadekinComponent, FlashDurationMultiplierEvent>(GetFlashModifier); // Delta V - Flash Modifier to Shadekin
}
private void OnInit(EntityUid uid, ShadekinComponent component, ComponentStartup args)
{
UpdateAlert(uid, component, (short)component.CurrentState);
RemComp<InternalsComponent>(uid);
}
private void OnEyeColorChange(EntityUid uid, ShadekinComponent component, EyeColorInitEvent args)
{
if (!TryComp<HumanoidProfileComponent>(uid, out var humanoid))
return;
// humanoid.EyeColor = Color.Black;
Dirty(uid, humanoid);
}
public void UpdateAlert(EntityUid uid, ShadekinComponent component, short state)
{
_alerts.ShowAlert(uid, component.ShadekinAlert, state);
}
private Angle GetAngle(EntityUid lightUid, SharedPointLightComponent lightComp, EntityUid targetUid)
{
var (lightPos, lightRot) = _transform.GetWorldPositionRotation(lightUid);
lightPos += lightRot.RotateVec(lightComp.Offset);
var (targetPos, targetRot) = _transform.GetWorldPositionRotation(targetUid);
var mapDiff = targetPos - lightPos;
var oppositeMapDiff = (-lightRot).RotateVec(mapDiff);
var angle = oppositeMapDiff.ToWorldAngle();
if (angle == double.NaN && _transform.ContainsEntity(targetUid, lightUid) || _transform.ContainsEntity(lightUid, targetUid))
{
angle = 0f;
}
return angle;
}
/// <summary>
/// Return an illumination float value with is how many "energy" of light is hitting our ent.
/// WARNING: This function might be expensive, Avoid calling it too much and CACHE THE RESULT!
/// </summary>
/// <param name="uid"></param>
/// <returns></returns>
public float GetLightExposure(EntityUid uid)
{
var illumination = 0f;
var lightQuery = _lookup.GetEntitiesInRange<PointLightComponent>(Transform(uid).Coordinates, 20, LookupFlags.Uncontained);
foreach (var light in lightQuery)
{
if (!light.Comp.Enabled
|| light.Comp.Radius < 1
|| light.Comp.Energy <= 0)
continue;
var (lightPos, lightRot) = _transform.GetWorldPositionRotation(light);
lightPos += lightRot.RotateVec(light.Comp.Offset);
if (!_examine.InRangeUnOccluded(light, uid, light.Comp.Radius, null))
continue;
Transform(uid).Coordinates.TryDistance(EntityManager, Transform(light).Coordinates, out var dist);
var denom = dist / light.Comp.Radius;
var attenuation = 1 - (denom * denom);
var calculatedLight = 0f;
if (light.Comp.MaskPath is not null)
{
var angleToTarget = GetAngle(light, light.Comp, uid);
foreach (var cone in lightMasks[light.Comp.MaskPath])
{
var coneLight = 0f;
var angleAttenuation = (float)Math.Min((float)Math.Max(cone.OuterWidth - angleToTarget, 0f), cone.InnerWidth) / cone.OuterWidth;
if (angleToTarget.Degrees - cone.Direction > cone.OuterWidth)
continue;
else if (angleToTarget.Degrees - cone.Direction > cone.InnerWidth
&& angleToTarget.Degrees - cone.Direction < cone.OuterWidth)
coneLight = light.Comp.Energy * attenuation * attenuation * angleAttenuation;
else
coneLight = light.Comp.Energy * attenuation * attenuation;
calculatedLight = Math.Max(calculatedLight, coneLight);
}
}
else
calculatedLight = light.Comp.Energy * attenuation * attenuation;
illumination += calculatedLight; //Math.Max(illumination, calculatedLight);
}
return illumination;
}
private void SetPassiveBuff(EntityUid uid, ShadekinState state)
{
if (!TryComp<PassiveDamageComponent>(uid, out var passive))
return;
if (state == ShadekinState.Extreme || state == ShadekinState.Annoying || state == ShadekinState.High)
{
// passive.DamageCap = 1;
}
else if (state == ShadekinState.Low)
{
// passive.DamageCap = 20;
passive.AllowedStates.Clear();
passive.AllowedStates.Add(MobState.Alive);
passive.Interval = 1f;
}
else if (state != ShadekinState.Dark)
{
// passive.DamageCap = 0;
passive.AllowedStates.Clear();
passive.AllowedStates.Add(MobState.Alive);
passive.AllowedStates.Add(MobState.Critical);
passive.AllowedStates.Add(MobState.Dead);
passive.Interval = 0.5f;
}
}
private void ToggleNightVision(EntityUid uid, ShadekinState state)
{
if (state == ShadekinState.Dark)
{
var nightVisionComponent = EnsureComp<NightVisionComponent>(uid);
nightVisionComponent.Color = Color.FromHex("#808080"); // Delta V - Change Night Vision Color
}
else
{
if (TryComp<NightVisionComponent>(uid, out var nightVision) && nightVision.IsActive)
_flashSystem.Flash(uid, uid, uid, TimeSpan.FromSeconds(0.5 * (int)state), 0.5f);
RemComp<NightVisionComponent>(uid);
}
}
private void ApplyLightDamage(EntityUid uid, float dmg)
{
var damage = new DamageSpecifier();
damage.DamageDict.Add("Heat", dmg);
_damageable.TryChangeDamage(uid, damage, true, false);
}
private void OnRefreshMovementSpeedModifiers(EntityUid uid, ShadekinComponent component, RefreshMovementSpeedModifiersEvent args)
{
if (component.CurrentState == ShadekinState.Low || component.CurrentState == ShadekinState.Annoying ||
component.CurrentState == ShadekinState.Dark || component.CurrentState == ShadekinState.Invalid)
return;
if (!TryComp<MovementSpeedModifierComponent>(uid, out var movement))
return;
var sprintDif = movement.BaseWalkSpeed / movement.BaseSprintSpeed;
args.ModifySpeed(1f, sprintDif);
}
private ShadekinState GetStateByThreshold(ShadekinComponent component, float lightExposure)
{
var returnState = ShadekinState.Dark;
foreach (var (threshold, shadekinState) in component.Thresholds.Reverse())
{
if (threshold <= lightExposure)
{
returnState = shadekinState;
break;
}
}
return returnState;
}
// Delta V - Begin Shadekin Flash Changes
private void GetFlashModifier(EntityUid uid, ShadekinComponent comp, FlashDurationMultiplierEvent args)
{
if (!TryComp<FlashModifierComponent>(uid, out var flashModifier))
return;
args.Multiplier = flashModifier.Modifier;
}
private void OnShadekinFlashed(EntityUid uid, ShadekinComponent comp, AfterFlashedEvent ev)
{
RemComp<NightVisionComponent>(uid);
}
// Delta V - End
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<ShadekinComponent>();
while (query.MoveNext(out var uid, out var component))
{
if (_timing.CurTime < component.NextUpdate)
continue;
component.NextUpdate = _timing.CurTime + component.UpdateCooldown;
var lightExposure = 0f;
if (!_container.IsEntityInContainer(uid))
lightExposure = GetLightExposure(uid);
component.CurrentState = GetStateByThreshold(component, lightExposure);
UpdateAlert(uid, component, (short)component.CurrentState);
SetPassiveBuff(uid, component.CurrentState);
ToggleNightVision(uid, component.CurrentState);
_speed.RefreshMovementSpeedModifiers(uid);
if (component.CurrentState == ShadekinState.Extreme)
ApplyLightDamage(uid, 5);
}
}
}

View File

@ -392,7 +392,6 @@ public abstract partial class SharedBuckleSystem
if (TryComp<PhysicsComponent>(buckle, out var physics))
_physics.ResetDynamics(buckle, physics);
// TOOD: DV - This fails when you try to buckle the entity you're carrying to something. Figure out why later.
DebugTools.AssertEqual(xform.ParentUid, strap.Owner);
}

View File

@ -28,4 +28,18 @@ public readonly partial record struct CargoBountyData
Bounty = bounty.ID;
Id = $"{bounty.IdPrefix}{uniqueIdentifier:D3}";
}
}
// Begin DeltaV bounty claiming
[DataField]
public string ClaimedBy { get; init; } = string.Empty;
[DataField]
public CargoBountyStatus Status { get; init; } = CargoBountyStatus.Undelivered;
}
public enum CargoBountyStatus
{
Undelivered,
Waiting,
OnShuttle,
} // End DeltaV bounty claiming

View File

@ -94,3 +94,4 @@ public sealed class BountySkipMessage : BoundUserInterfaceMessage
BountyId = bountyId;
}
}

View File

@ -26,6 +26,7 @@ using System.Linq;
using Content.Shared.Movement.Systems;
using Content.Shared.Random.Helpers;
using Content.Shared.Clothing.Components;
using Content.Shared._Starlight.Flash.Components; // Delta V - For Flash Duration
namespace Content.Shared.Flash;
@ -173,7 +174,7 @@ public abstract class SharedFlashSystem : EntitySystem
// Goobstation end
// don't paralyze, slowdown or convert to rev if the target is immune to flashes
if (!_statusEffectsSystem.TryAddStatusEffect<FlashedComponent>(target, FlashedKey, flashDuration, true) && !ignoreProtection) //DeltaV: allow flashing to ignore flash protection
if (!_statusEffectsSystem.TryAddStatusEffect<FlashedComponent>(target, FlashedKey, flashDuration * multiplier, true) && !ignoreProtection) //DeltaV: allow flashing to ignore flash protection. Added Flashduration Multiplier
return;
if (stunDuration != null)

View File

@ -2,8 +2,7 @@ using Content.Shared.Armor; // DeltaV - Addition of HandHeldArmor
using Content.Shared.Atmos;
using Content.Shared.Camera;
using Content.Shared.Cuffs;
using Content.Shared.Damage; // DeltaV End - Addition of HandHeldArmor
using Content.Shared.Damage.Systems; // DeltaV End - Addition of HandHeldArmor
using Content.Shared.Damage.Systems; // DeltaV - Addition of HandHeldArmor
using Content.Shared.Hands.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Projectiles;

View File

@ -68,6 +68,7 @@ namespace Content.Shared.Input
public static readonly BoundKeyFunction TakeScreenshotNoUI = "TakeScreenshotNoUI";
public static readonly BoundKeyFunction ToggleFullscreen = "ToggleFullscreen";
public static readonly BoundKeyFunction Point = "Point";
public static readonly BoundKeyFunction OfferItem = "OfferItem"; // Floofstation
public static readonly BoundKeyFunction ZoomOut = "ZoomOut";
public static readonly BoundKeyFunction ZoomIn = "ZoomIn";
public static readonly BoundKeyFunction ResetZoom = "ResetZoom";

View File

@ -1,4 +1,4 @@
using Content.Shared._DV.Overlays;
using Content.Shared._DV.Overlays; // DeltaV
using Content.Shared._DV.Psionics.Events; // DeltaV
using Content.Shared.Armor;
using Content.Shared.Atmos;
@ -85,13 +85,15 @@ public partial class InventorySystem
SubscribeLocalEvent<InventoryComponent, WieldAttemptEvent>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, UnwieldAttemptEvent>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, IngestionAttemptEvent>(RefRelayInventoryEvent);
// DeltaV Start - Psionic Events
// DeltaV Start
// Psionic Events
SubscribeLocalEvent<InventoryComponent, DispelledEvent>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, PsionicPowerUseAttemptEvent>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, TargetedByPsionicPowerEvent>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, NoosphericFryEvent>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, WeightlessnessChangedEvent>(RefRelayInventoryEvent); // Heavy Clothing
// DeltaV End - Psionic Events
// Heavy Clothing
SubscribeLocalEvent<InventoryComponent, WeightlessnessChangedEvent>(RefRelayInventoryEvent);
// DeltaV End
// Eye/vision events
SubscribeLocalEvent<InventoryComponent, CanSeeAttemptEvent>(RelayInventoryEvent);

View File

@ -103,6 +103,22 @@ public abstract class SharedItemSystem : EntitySystem
VisualsChanged(uid);
}
/// <summary>
/// DeltaV - clear item visuals
/// </summary>
public void ClearVisuals(Entity<ItemComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp))
return;
ent.Comp.RsiPath = null;
ent.Comp.InhandVisuals = new();
ent.Comp.HeldPrefix = null;
Dirty(ent, ent.Comp);
VisualsChanged(ent.Owner);
}
#endregion
private void OnHandInteract(EntityUid uid, ItemComponent component, InteractHandEvent args)

View File

@ -1,4 +1,5 @@
using Content.Shared._ST.Interaction; // Stellar - interaction particles
using Content.Shared._Floof.OfferItem; // Floof
using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Logs;
using Content.Shared.Alert;
@ -117,10 +118,11 @@ public sealed class PullingSystem : EntitySystem
if (TryComp(args.PullerUid, out PullerComponent? pullerComp) && !pullerComp.NeedsHands)
return;
if (!_virtual.TrySpawnVirtualItemInHand(args.PulledUid, uid))
if (!_virtual.TrySpawnVirtualItemInHand(args.PulledUid, uid, out var virt)) // Floofstation - store item
{
DebugTools.Assert("Unable to find available hand when starting pulling??");
}
EnsureComp<OfferableVirtualItemComponent>(virt.Value); // Floofstation - add a special component to allow offering it
}
private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStoppedMessage args)

View File

@ -1,8 +1,7 @@
using Content.Shared.Actions;
using Content.Shared.Bed.Sleep;
using Content.Shared.DoAfter;
using Content.Shared.Hands;
using Content.Shared.IdentityManagement;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Content.Shared.Item.PseudoItem;
@ -24,7 +23,8 @@ public abstract partial class SharedPseudoItemSystem : EntitySystem
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedContainerSystem _container = default!; // Floofstation
[Dependency] private readonly SharedHandsSystem _hands = default!; // Floofstation
private readonly ProtoId<TagPrototype> PreventTag = "PreventLabel";
private readonly EntProtoId SleepActionId = "ActionSleep"; // The action used for sleeping inside bags. Currently uses the default sleep action (same as beds)
@ -116,17 +116,35 @@ public abstract partial class SharedPseudoItemSystem : EntitySystem
protected virtual void OnGettingPickedUpAttempt(EntityUid uid, PseudoItemComponent component,
GettingPickedUpAttemptEvent args)
{
if (args.User == args.Item)
return;
args.Cancel(); // Floof - this is a terrible idea. This triggers every time ANY system checks if a pseudo-item can be picked up.
// WHY DID YOU DO THAT, NYANOTRASEN???
_transform.AttachToGridOrMap(uid);
args.Cancel();
// if (args.User == args.Item)
// return;
//
// _transform.AttachToGridOrMap(uid);
// args.Cancel();
}
private void OnDropAttempt(EntityUid uid, PseudoItemComponent component, DropAttemptEvent args)
{
if (component.Active)
args.Cancel();
if (!component.Active)
return;
// Floof - we try to get the containing container and try to drop it into it
// If possible, we do it, since a bagged cat probably can put things back into the bag just like they can pick them up.
string? failReason = null;
if (_hands.GetActiveItem(uid) is { Valid: true, } droppedItem
&& _container.TryGetContainingContainer(Transform(uid).ParentUid, uid, out var pseudoItemContainer)
&& TryComp<StorageComponent>(pseudoItemContainer.Owner, out var targetStorage)
&& _storage.CanInsert(pseudoItemContainer.Owner, droppedItem, out failReason, targetStorage, ignoreStacks: true)
)
_storage.Insert(pseudoItemContainer.Owner, droppedItem, out _, uid, targetStorage, stackAutomatically: false);
if (failReason != null)
_popupSystem.PopupEntity(Loc.GetString(failReason), uid, uid);
args.Cancel();
}
private void OnInsertAttempt(EntityUid uid, PseudoItemComponent component,
@ -141,8 +159,9 @@ public abstract partial class SharedPseudoItemSystem : EntitySystem
// Prevents moving within the bag :)
private void OnInteractAttempt(EntityUid uid, PseudoItemComponent component, InteractionAttemptEvent args)
{
if (args.Uid == args.Target && component.Active)
args.Cancelled = true;
// Floof - why the fuck.
// if (args.Uid == args.Target && component.Active)
// args.Cancelled = true;
}
private void OnDoAfter(EntityUid uid, PseudoItemComponent component, DoAfterEvent args)

View File

@ -1,4 +1,5 @@
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems; // DeltaV
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Doors.Components;
@ -19,6 +20,7 @@ namespace Content.Shared.Remotes.EntitySystems;
public abstract class SharedDoorRemoteSystem : EntitySystem
{
[Dependency] private readonly SharedAirlockSystem _airlock = default!;
[Dependency] private readonly AccessReaderSystem _accessReader = default!; // DeltaV
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
[Dependency] private readonly SharedElectrocutionSystem _electrify = default!;
@ -96,6 +98,19 @@ public abstract class SharedDoorRemoteSystem : EntitySystem
else if (entity.Comp.RequireTagWhitelist)
return;
// Begin DeltaV - Emergency access only bypasses open/close; bolting and toggling emergency access still require actual access.
if (entity.Comp.Mode != OperatingMode.OpenClose
&& accessComponent != null
&& !_accessReader.IsAllowed(accessTarget, args.Target.Value, accessComponent))
{
if (isAirlock)
_doorSystem.Deny(args.Target.Value, doorComp, user: args.User, predicted: true);
_popup.PopupClient(Loc.GetString("door-remote-denied"), args.User, args.User);
return;
}
// End DeltaV
switch (entity.Comp.Mode)
{
case OperatingMode.OpenClose:

View File

@ -1,6 +1,5 @@
using Content.Shared.Examine;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Systems;
using Content.Shared.Stealth.Components;
using Robust.Shared.GameStates;
using Robust.Shared.Timing;

View File

@ -188,12 +188,20 @@ public abstract class SharedStorageSystem : EntitySystem
return;
}
// Begin DeltaV - the grid check excludes the item being resized because it's already in the storage which defeats the purpose so yoink it out to remove that
storage.StoredItems.Remove(itemEnt.Owner);
UpdateOccupied((container.Owner, storage));
if (!ItemFitsInGridLocation((itemEnt.Owner, itemEnt.Comp), (container.Owner, storage), loc))
if (ItemFitsInGridLocation((itemEnt.Owner, itemEnt.Comp), (container.Owner, storage), loc))
{
storage.StoredItems.Add(itemEnt.Owner, loc);
UpdateOccupied((container.Owner, storage));
}
else
{
ContainerSystem.Remove(itemEnt.Owner, container, force: true);
}
// End DeltaV - the grid check excludes the item being resized because it's already in the storage which defeats the purpose so yoink it out to remove that
}
private void OnNestedStorageCvar(bool obj)

View File

@ -1,5 +1,6 @@
using Content.Shared._DV.Body.Components;
using Content.Shared._DV.Body.Events;
using Content.Shared.Buckle;
using Content.Shared.DoAfter;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
@ -13,6 +14,7 @@ public sealed class CPRSystem : EntitySystem
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedBuckleSystem _buckle = default!;
public override void Initialize()
{
@ -85,7 +87,7 @@ public sealed class CPRSystem : EntitySystem
{
Act = () => StartCPR(user, target, cprComp.TimeLength),
Text = Loc.GetString("cpr-verb-start"),
Priority = 2,
Priority = _buckle.IsBuckled(target) ? 3 : 1, // Higher priority if they are buckled. Otherwise, this conflicts with trying to carry.
Disabled = alreadyAffected,
Message = alreadyAffected ? Loc.GetString("cpr-verb-disabled-description") : Loc.GetString("cpr-verb-description"),
};

View File

@ -0,0 +1,31 @@
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared._DV.Cargo.Components;
[Serializable, NetSerializable]
public sealed class BountyClaimedMessage : BoundUserInterfaceMessage
{
public readonly string BountyId;
public BountyClaimedMessage(string bountyId)
{
BountyId = bountyId;
}
}
[Serializable, NetSerializable]
public sealed class BountySetStatusMessage : BoundUserInterfaceMessage
{
public readonly string BountyId;
public readonly int Status;
public BountySetStatusMessage(string bountyId, int status)
{
BountyId = bountyId;
Status = status;
}
}

View File

@ -28,7 +28,9 @@ using Robust.Shared.Network;
using Robust.Shared.Physics.Components;
using System.Numerics;
using Content.Shared._DV.Polymorph;
using Content.Shared._Floof.OfferItem;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Buckle;
namespace Content.Shared._DV.Carrying;
@ -69,7 +71,7 @@ public sealed class CarryingSystem : EntitySystem
SubscribeLocalEvent<BeingCarriedComponent, GettingInteractedWithAttemptEvent>(OnInteractedWith);
SubscribeLocalEvent<BeingCarriedComponent, PullAttemptEvent>(OnPullAttempt);
SubscribeLocalEvent<BeingCarriedComponent, StartClimbEvent>(OnDrop);
SubscribeLocalEvent<BeingCarriedComponent, BuckledEvent>(OnDrop);
SubscribeLocalEvent<BeingCarriedComponent, BuckledEvent>(OnBuckle);
SubscribeLocalEvent<BeingCarriedComponent, UnbuckledEvent>(OnDrop);
SubscribeLocalEvent<BeingCarriedComponent, StrappedEvent>(OnDrop);
SubscribeLocalEvent<BeingCarriedComponent, UnstrappedEvent>(OnDrop);
@ -181,14 +183,15 @@ public sealed class CarryingSystem : EntitySystem
/// </summary>
private void OnInteractionAttempt(Entity<BeingCarriedComponent> ent, ref InteractionAttemptEvent args)
{
if (args.Target is not {} target)
return;
var targetParent = Transform(target).ParentUid;
var carrier = ent.Comp.Carrier;
if (target != carrier && targetParent != carrier && targetParent != ent.Owner)
args.Cancelled = true;
// Floofstation - no - this prevents the person from escaping and more.
// if (args.Target is not {} target)
// return;
//
// var targetParent = Transform(target).ParentUid;
//
// var carrier = ent.Comp.Carrier;
// if (target != carrier && targetParent != carrier && targetParent != ent.Owner)
// args.Cancelled = true;
}
private void OnMoveAttempt(Entity<BeingCarriedComponent> ent, ref UpdateCanMoveEvent args)
@ -203,8 +206,9 @@ public sealed class CarryingSystem : EntitySystem
private void OnInteractedWith(Entity<BeingCarriedComponent> ent, ref GettingInteractedWithAttemptEvent args)
{
if (args.Uid != ent.Comp.Carrier)
args.Cancelled = true;
// Floofstation - why?
// if (args.Uid != ent.Comp.Carrier)
// args.Cancelled = true;
}
private void OnPullAttempt(Entity<BeingCarriedComponent> ent, ref PullAttemptEvent args)
@ -217,6 +221,13 @@ public sealed class CarryingSystem : EntitySystem
DropCarried(ent.Comp.Carrier, ent);
}
private void OnBuckle(Entity<BeingCarriedComponent> ent, ref BuckledEvent args)
{
// Buckling to a bed already handles the reparenting to the entity that the carried
// entity is buckled to, and then relays the BuckledEvent, so don't reparent to the grid.
DropCarried(ent.Comp.Carrier, ent, attachToGrid: false);
}
private void OnRemoved(Entity<BeingCarriedComponent> ent, ref ComponentRemove args)
{
/*
@ -242,7 +253,8 @@ public sealed class CarryingSystem : EntitySystem
args.Handled = true;
}
private void StartCarryDoAfter(EntityUid carrier, Entity<CarriableComponent> carried)
// Floofstation - made public
public void StartCarryDoAfter(EntityUid carrier, Entity<CarriableComponent> carried)
{
TimeSpan length = GetPickupDuration(carrier, carried);
@ -299,7 +311,8 @@ public sealed class CarryingSystem : EntitySystem
for (var x = 0; x < Comp<CarriableComponent>(carried).FreeHandsRequired; x++)
{
_virtualItem.TrySpawnVirtualItemInHand(carried, carrier);
if (_virtualItem.TrySpawnVirtualItemInHand(carried, carrier, out var virtualItem))
EnsureComp<OfferableVirtualItemComponent>(virtualItem.Value);
}
}
@ -322,9 +335,9 @@ public sealed class CarryingSystem : EntitySystem
return true;
}
public void DropCarried(EntityUid carrier, EntityUid carried)
public void DropCarried(EntityUid carrier, EntityUid carried, bool attachToGrid = true)
{
Drop(carried);
Drop(carried, attachToGrid);
CleanupCarrier(carrier, carried);
}
@ -336,12 +349,15 @@ public sealed class CarryingSystem : EntitySystem
_movementSpeed.RefreshMovementSpeedModifiers(carrier);
}
private void Drop(EntityUid carried)
private void Drop(EntityUid carried, bool attachToGrid = true)
{
RemComp<BeingCarriedComponent>(carried);
RemComp<KnockedDownComponent>(carried); // TODO SHITMED: make sure this doesnt let you make someone with no legs walk
_actionBlocker.UpdateCanMove(carried);
_transform.AttachToGridOrMap(carried);
// Some systems will handle re-parenting and then throw an event, and this changes the parent when it should not
if (attachToGrid)
_transform.AttachToGridOrMap(carried);
_standingState.Stand(carried);
}

View File

@ -0,0 +1,15 @@
using Content.Shared.Item;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._DV.Forensics;
[RegisterComponent, NetworkedComponent]
public sealed partial class DVExpandToInsertedItemSizeComponent : Component
{
[DataField(required: true)]
public string ItemSlot;
[DataField(required: true)]
public ProtoId<ItemSizePrototype> EmptySize;
}

View File

@ -0,0 +1,50 @@
using Content.Shared.Item;
using Robust.Shared.Containers;
namespace Content.Shared._DV.Forensics;
public sealed class DVExpandToInsertedItemSizeSystem : EntitySystem
{
[Dependency] private readonly SharedItemSystem _item = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DVExpandToInsertedItemSizeComponent, EntInsertedIntoContainerMessage>(OnItemInserted);
SubscribeLocalEvent<DVExpandToInsertedItemSizeComponent, EntRemovedFromContainerMessage>(OnItemRemoved);
}
private void Refresh(Entity<DVExpandToInsertedItemSizeComponent> ent)
{
var slot = _container.EnsureContainer<ContainerSlot>(ent, ent.Comp.ItemSlot);
if (slot.ContainedEntity is { } contained)
{
var item = Comp<ItemComponent>(contained);
_item.SetSize(ent, item.Size);
_item.SetShape(ent, item.Shape);
}
else
{
_item.SetSize(ent, ent.Comp.EmptySize);
_item.SetShape(ent, null);
}
}
private void OnItemInserted(Entity<DVExpandToInsertedItemSizeComponent> ent, ref EntInsertedIntoContainerMessage args)
{
if (ent.Comp.ItemSlot != args.Container.ID)
return;
Refresh(ent);
}
private void OnItemRemoved(Entity<DVExpandToInsertedItemSizeComponent> ent, ref EntRemovedFromContainerMessage args)
{
if (ent.Comp.ItemSlot != args.Container.ID)
return;
Refresh(ent);
}
}

View File

@ -0,0 +1,17 @@
using Robust.Shared.GameStates;
using Robust.Shared.Utility;
namespace Content.Shared._DV.Forensics;
[RegisterComponent, NetworkedComponent]
public sealed partial class DVItemSlotVisualsComponent : Component
{
[DataField(required: true)]
public string ItemSlot;
[DataField(required: true)]
public SpriteSpecifier FilledSprite;
[DataField(required: true)]
public SpriteSpecifier UnfilledSprite;
}

View File

@ -0,0 +1,10 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Forensics;
[RegisterComponent, NetworkedComponent]
public sealed partial class DVSeenInsertedItemComponent : Component
{
[DataField(required: true)]
public string ItemSlot;
}

View File

@ -0,0 +1,80 @@
using Content.Shared.Examine;
using Content.Shared.IdentityManagement;
using Content.Shared.Item;
using Content.Shared.NameModifier.EntitySystems;
using Robust.Shared.Containers;
using Robust.Shared.Timing;
namespace Content.Shared._DV.Forensics;
public sealed class DVSeenInsertedItemSystem : EntitySystem
{
[Dependency] private readonly NameModifierSystem _nameModifier = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly SharedItemSystem _item = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DVSeenInsertedItemComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<DVSeenInsertedItemComponent, RefreshNameModifiersEvent>(OnRefreshModifiers);
SubscribeLocalEvent<DVSeenInsertedItemComponent, EntInsertedIntoContainerMessage>(OnItemInserted);
SubscribeLocalEvent<DVSeenInsertedItemComponent, EntRemovedFromContainerMessage>(OnItemRemoved);
}
private void OnExamined(Entity<DVSeenInsertedItemComponent> ent, ref ExaminedEvent args)
{
var slot = _container.EnsureContainer<ContainerSlot>(ent, ent.Comp.ItemSlot);
if (slot.ContainedEntity is { } contained)
{
args.PushMarkup(Loc.GetString("dv-seen-inserted-item-examined.full", ("container", Identity.Entity(ent, EntityManager, args.Examiner)), ("contained", contained)));
}
else
{
args.PushMarkup(Loc.GetString("dv-seen-inserted-item-examined.empty", ("container", Identity.Entity(ent, EntityManager, args.Examiner))));
}
}
private void OnRefreshModifiers(Entity<DVSeenInsertedItemComponent> ent, ref RefreshNameModifiersEvent args)
{
var slot = _container.EnsureContainer<ContainerSlot>(ent, ent.Comp.ItemSlot);
if (slot.ContainedEntity is not { } contained)
return;
args.AddModifier("dv-seen-inserted-item-name-modifier", extraArgs: ("contained", contained));
}
private void Refresh(Entity<DVSeenInsertedItemComponent> ent)
{
_nameModifier.RefreshNameModifiers(ent.Owner);
var slot = _container.EnsureContainer<ContainerSlot>(ent, ent.Comp.ItemSlot);
if (slot.ContainedEntity is { } contained)
{
var item = Comp<ItemComponent>(contained);
_item.CopyVisuals(ent.Owner, item);
}
else
{
_item.ClearVisuals(ent.Owner);
}
}
private void OnItemInserted(Entity<DVSeenInsertedItemComponent> ent, ref EntInsertedIntoContainerMessage args)
{
if (args.Container.ID != ent.Comp.ItemSlot || _timing.ApplyingState)
return;
Refresh(ent);
}
private void OnItemRemoved(Entity<DVSeenInsertedItemComponent> ent, ref EntRemovedFromContainerMessage args)
{
if (args.Container.ID != ent.Comp.ItemSlot || _timing.ApplyingState)
return;
Refresh(ent);
}
}

View File

@ -0,0 +1,5 @@
using Content.Shared.Alert;
namespace Content.Shared._Floof.OfferItem;
public sealed partial class AcceptOfferAlertEvent : BaseAlertEvent;

View File

@ -0,0 +1,52 @@
using Content.Shared.Alert;
using Content.Shared.Inventory.VirtualItem;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._Floof.OfferItem;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
[Access(typeof(SharedOfferItemSystem))]
public sealed partial class OfferItemComponent : Component
{
/// <summary>
/// Apparently this indicates whether the entity is currently choosing an entity to offer (right after pressing F).
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
public bool IsInOfferMode;
/// <summary>
/// If this is true, then someone is currently offering an item to this entity, and <see cref="ReceivingFrom"/>
/// stores the ID of that entity.
/// </summary>
[DataField, AutoNetworkedField]
public bool IsInReceiveMode;
[DataField, AutoNetworkedField]
public string? Hand;
[DataField, AutoNetworkedField]
public EntityUid? Item;
/// <summary>
/// Floofstation note. So, this is EE shitcode, so prepare for an emotional rollercoaster.
/// This field can mean TWO things. It's either the target entity this entity is offering an item to,
/// or an entity that is offering an item to this entity.
/// Whether it's one or the other is distinguished by <see cref="IsInReceiveMode"/>.<br/><br/>
///
/// In rare cases it can be both. According to my research, if entity A offers an item to entity B, and entity B offers to entity A,
/// then both entities will end up in receive mode, and they will have each other as targets. There's a check preventing offer loops
/// of length more than 2.
/// </summary>
[DataField, AutoNetworkedField]
public EntityUid? ReceivingFrom;
[DataField]
public float MaxOfferDistance = 2f;
[DataField]
public ProtoId<AlertPrototype> OfferAlert = "Offer";
public EntityUid GetRealEntity(EntityManager entityManager) =>
entityManager.GetComponentOrNull<VirtualItemComponent>(Item)?.BlockingEntity ?? Item ?? EntityUid.Invalid;
}

View File

@ -0,0 +1,11 @@
using Robust.Shared.GameStates;
namespace Content.Shared._Floof.OfferItem;
/// <summary>
/// A marker component that, when applied to a virtual item, allows it to be offered using item offering.
/// Implementors have to listen on ItemTransferredEvent.
/// </summary>
[RegisterComponent]
[NetworkedComponent]
public sealed partial class OfferableVirtualItemComponent : Component;

View File

@ -0,0 +1,93 @@
using Content.Shared.ActionBlocker;
using Content.Shared.Alert;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Input;
using Content.Shared.Popups;
using Robust.Shared.Input.Binding;
using Robust.Shared.Player;
namespace Content.Shared._Floof.OfferItem;
public abstract partial class SharedOfferItemSystem
{
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
private void InitializeInteractions()
{
base.Initialize();
CommandBinds.Builder
.Bind(ContentKeyFunctions.OfferItem, InputCmdHandler.FromDelegate(SetInOfferMode, handle: false, outsidePrediction: false))
.Register<SharedOfferItemSystem>();
}
public override void Shutdown()
{
base.Shutdown();
CommandBinds.Unregister<SharedOfferItemSystem>();
}
/// <summary>
/// This sets IsInOfferMode to true, allowing the player to select whom to offer an item to with interaction.
/// </summary>
private void SetInOfferMode(ICommonSession? offerer)
{
if (offerer is not { } playerSession)
return;
if (playerSession.AttachedEntity is not { Valid: true } uid)
return;
if (!Exists(uid))
return;
if (!_actionBlocker.CanInteract(uid, null))
return;
if (!TryComp<OfferItemComponent>(uid, out var offerItem))
return;
if (!TryComp<HandsComponent>(uid, out var hands))
return;
if (_hands.GetActiveHand((uid, hands)) is not { } activeHandName)
return;
if (!_hands.TryGetHeldItem((uid, hands), activeHandName, out var heldItem))
return;
offerItem.Item = heldItem;
if (!offerItem.IsInOfferMode)
{
if (offerItem.Item == null)
{
_popup.PopupEntity(Loc.GetString("offer-item-empty-hand"), uid, uid);
return;
}
if (offerItem.Hand == null || offerItem.ReceivingFrom == null)
{
offerItem.IsInOfferMode = true;
offerItem.Hand = activeHandName;
Dirty(uid, offerItem);
return;
}
}
// If we're already offering an item to someone, cancel that offer
if (offerItem.ReceivingFrom != null)
{
UnReceive(offerItem.ReceivingFrom.Value, offererComp: offerItem);
offerItem.IsInOfferMode = false;
Dirty(uid, offerItem);
return;
}
UnOffer(uid, offerItem);
}
}

View File

@ -0,0 +1,414 @@
using Content.Shared._DV.Carrying;
using Content.Shared.Alert;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Nutrition.EntitySystems;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Timing;
// Dear contributor.
// This system is fucking unmaintainable.
// If you ever happen to touch this again, please do your best to document your changes and try to resolve mysteries surrounding this code.
// I did what I could to document the parts I managed to understand, but there is still more truth to be unveiled.
//
// HOURS_WASTED_HERE_FLOOFSTATION = 10
// HOURS_WASTED_HERE_DELTAV = 1
namespace Content.Shared._Floof.OfferItem;
public abstract partial class SharedOfferItemSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
[Dependency] private readonly CarryingSystem _carrying = default!;
[Dependency] private readonly PullingSystem _pulling = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<OfferItemComponent, AcceptOfferAlertEvent>(OnAcceptOffer);
SubscribeLocalEvent<OfferItemComponent, InteractUsingEvent>(OnInteractWithReceiver, before: [typeof(IngestionSystem)]);
SubscribeLocalEvent<OfferableVirtualItemComponent, BeforeRangedInteractEvent>(OnRangedInteractWithReceiver);
SubscribeLocalEvent<OfferItemComponent, MoveEvent>(OnMove);
SubscribeLocalEvent<BeingCarriedComponent, ItemTransferredEvent>(OnCarryTransfer);
SubscribeLocalEvent<PullableComponent, ItemTransferredEvent>(OnPulledTransfer);
InitializeInteractions();
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<OfferItemComponent, HandsComponent>();
while (query.MoveNext(out var uid, out var offerItem, out var hands))
{
// If the mob no longer holds an item in the original offering hand, clear offering mode
if (offerItem.Hand != null && !_hands.TryGetHeldItem((uid, hands), offerItem.Hand, out _))
{
if (offerItem.ReceivingFrom != null)
{
UnReceive(offerItem.ReceivingFrom.Value, offererComp: offerItem);
offerItem.IsInOfferMode = false;
Dirty(uid, offerItem);
}
else
UnOffer(uid, offerItem);
}
if (!offerItem.IsInReceiveMode)
{
_alertsSystem.ClearAlert(uid, offerItem.OfferAlert);
continue;
}
_alertsSystem.ShowAlert(uid, offerItem.OfferAlert);
}
}
#region Events
private void OnAcceptOffer(Entity<OfferItemComponent> ent, ref AcceptOfferAlertEvent args)
{
Receive((ent, ent.Comp));
}
private void OnInteractWithReceiver(Entity<OfferItemComponent> receiver, ref InteractUsingEvent args)
{
if (!_timing.IsFirstTimePredicted || _timing.ApplyingState || args.Handled)
return;
if (!TryComp<OfferItemComponent>(args.User, out var offererComponent))
return;
args.Handled = CreateOffer(receiver, (args.User, offererComponent));
}
private void OnRangedInteractWithReceiver(Entity<OfferableVirtualItemComponent> virtItem, ref BeforeRangedInteractEvent args)
{
// If the entity being offered is a virtual item, InteractUsing will not be raised
// because virtual items exclude themselves from being marked as used
// If this is the case, InteractHand will be raised instead, which we can use anyway because OfferItem.Item stores the offered item
//
// We also can't check Handled here because VirtualItemSystem handles it, ffs
// This won't lead you to accidentally offering someone your gun
//
// This is shitcode, this time my shitcode. My changes to the offering system allow you to transfer carrying and pulling,
// but in order to handle these, we need to be able to intercept interactions with virtual items.
//
// Ideally this code should be rewritten to:
// a) Have each different virtual item have a distinct component (e.g. CarryingVirtualItem) which would allow to distinguish them from the rest
// b) Not rely on the InteractionSystem.
// However, I'm not in the mood to do either. And I'm too deep into the rabbit hole of getting this shit to work.
if (!_timing.IsFirstTimePredicted || _timing.ApplyingState)
return;
var receiver = args.Target;
if (!TryComp<OfferItemComponent>(receiver, out var receiverComponent))
return;
var offerer = args.User;
if (!TryComp<OfferItemComponent>(offerer, out var offererComponent) || offererComponent.Item == null)
return;
// Since this is ranged, we must also check distance, because the interaction system wont check it for us in this case
if (!Transform(offerer).Coordinates.TryDistance(EntityManager, _transform, Transform(receiver.Value).Coordinates, out var dst)
|| dst > offererComponent.MaxOfferDistance)
return;
args.Handled = CreateOffer((receiver.Value, receiverComponent), (offerer, offererComponent));
}
private void OnMove(EntityUid uid, OfferItemComponent component, MoveEvent args)
{
if (_net.IsClient) // Client often mispredicts movement, we cant trust it here
return;
if (component.ReceivingFrom == null)
return;
if (_transform.InRange(args.NewPosition, Transform(component.ReceivingFrom.Value).Coordinates, component.MaxOfferDistance))
return;
UnOffer(uid, component);
}
private void OnCarryTransfer(Entity<BeingCarriedComponent> ent, ref ItemTransferredEvent args)
{
if (args.Handled
|| args.PassedItem == args.RealItem // Means the entity is transferred NOT via carrying
|| args.RealItem is not { Valid: true } carried
|| ent.Comp.Carrier is not { Valid: true } oldCarrier)
return;
_carrying.DropCarried(oldCarrier, ent);
args.Handled = _carrying.TryCarry(args.Target, carried);
}
private void OnPulledTransfer(Entity<PullableComponent> ent, ref ItemTransferredEvent args)
{
if (args.Handled
|| args.PassedItem == args.RealItem // Means the entity is transferred NOT via pulling
|| args.RealItem is not { Valid: true } pulled)
return;
_pulling.TryStopPull(pulled, ent);
args.Handled = _pulling.TryStartPull(args.Target, ent, null, ent.Comp);
}
#endregion
#region Offering / Recieving
/// <summary>
/// Attempts to create an offer. Expects offerer.Item to already be set to the offered item, offererComponent.InReceiveMode == true.
/// Will fail if offerer == receiver or if receiver already has a set TargetOrOfferer, and that person is not the current offerer
/// </summary>
private bool CreateOffer(Entity<OfferItemComponent> receiver, Entity<OfferItemComponent> offerer)
{
var offererComponent = offerer.Comp;
var receiverComponent = receiver.Comp;
if (offerer == receiver || receiverComponent.IsInReceiveMode || !offererComponent.IsInOfferMode)
return false;
if (offererComponent.IsInReceiveMode && offererComponent.ReceivingFrom != receiver)
return false;
receiverComponent.IsInReceiveMode = true;
receiverComponent.ReceivingFrom = offerer;
Dirty(receiver, receiverComponent);
offererComponent.ReceivingFrom = receiver; // TODO this is ee shitcode, may not be necessary?
offererComponent.IsInOfferMode = false;
Dirty(offerer, offererComponent);
if (offererComponent.Item == null)
return false;
// Sender popup (client-side only)
_popup.PopupClient(
Loc.GetString("offer-item-try-give",
("item", Identity.Entity(offererComponent.GetRealEntity(EntityManager), EntityManager)),
("target", Identity.Entity(receiver, EntityManager))),
offerer,
offerer);
// Receiver popup (server side only, not predicted because recipient != local player)
_popup.PopupEntity(
Loc.GetString("offer-item-try-give-target",
("user", Identity.Entity(receiverComponent.ReceivingFrom.Value, EntityManager)),
("item", Identity.Entity(offererComponent.GetRealEntity(EntityManager), EntityManager))),
offerer,
receiver,
Popups.PopupType.Medium);
return true;
}
/// <summary>
/// Resets the <see cref="OfferItemComponent"/> of the user and the target
/// </summary>
protected void UnOffer(EntityUid thisEntity, OfferItemComponent offererComp)
{
if (!TryComp<HandsComponent>(thisEntity, out var hands) || _hands.GetActiveHand((thisEntity, hands)) is null)
return;
if (offererComp.ReceivingFrom is { } otherEntity && TryComp<OfferItemComponent>(otherEntity, out var otherOfferer))
{
// So this tries to figure out which of these entities do what...
// if A.OfferItemComponent.Item != null, then A is currently offering an item to A.OfferItemComponent.TargetOrOfferer
// If it is null, then it is ONLY being offered an item TO.
if (offererComp.Item != null && _net.IsServer)
{
_popup.PopupEntity(
Loc.GetString("offer-item-no-give",
("item", Identity.Entity(offererComp.GetRealEntity(EntityManager), EntityManager)), // Floof - resolve virtual items
("target", Identity.Entity(otherEntity, EntityManager))),
thisEntity,
thisEntity);
_popup.PopupEntity(
Loc.GetString("offer-item-no-give-target",
("user", Identity.Entity(thisEntity, EntityManager)),
("item", Identity.Entity(offererComp.GetRealEntity(EntityManager), EntityManager))),
thisEntity,
otherEntity);
}
else if (otherOfferer.Item != null && _net.IsServer)
{
_popup.PopupEntity(
Loc.GetString("offer-item-no-give",
("item", Identity.Entity(otherOfferer.GetRealEntity(EntityManager), EntityManager)), // Floof - resolve virtual items
("target", Identity.Entity(thisEntity, EntityManager))),
otherEntity,
otherEntity);
_popup.PopupEntity(
Loc.GetString("offer-item-no-give-target",
("user", Identity.Entity(otherEntity, EntityManager)),
("item", Identity.Entity(otherOfferer.GetRealEntity(EntityManager), EntityManager))),
otherEntity,
thisEntity);
}
otherOfferer.IsInOfferMode = false;
otherOfferer.IsInReceiveMode = false;
otherOfferer.Hand = null;
otherOfferer.ReceivingFrom = null;
otherOfferer.Item = null;
Dirty(otherEntity, otherOfferer);
}
offererComp.IsInOfferMode = false;
offererComp.IsInReceiveMode = false;
offererComp.Hand = null;
offererComp.ReceivingFrom = null;
offererComp.Item = null;
Dirty(thisEntity, offererComp);
}
/// <summary>
/// Cancels the transfer of the item
/// </summary>
protected void UnReceive(EntityUid receiver, OfferItemComponent? receiverComp = null, OfferItemComponent? offererComp = null)
{
if (!Resolve(receiver, ref receiverComp)
|| receiverComp.ReceivingFrom is not {} offerer
|| !Resolve(offerer, ref offererComp))
return;
// Idk why this check is here
if (!TryComp<HandsComponent>(receiver, out var hands) || _hands.GetActiveHand((receiver, hands)) == null || receiverComp.ReceivingFrom == null)
return;
// If offererComp.Item != null, then they are actively offering to TargetOrOfferer
// Normally this method is called right after a transfer is done, but this part can be called from SetInOfferMode when the player presses F again to cancel an ongoing offer
if (offererComp.Item != null)
{
_popup.PopupClient(
Loc.GetString("offer-item-no-give",
("item", Identity.Entity(offererComp.GetRealEntity(EntityManager), EntityManager)), // Floof - resolve virtual items
("target", Identity.Entity(receiver, EntityManager))),
offerer,
offerer);
_popup.PopupEntity(
Loc.GetString("offer-item-no-give-target",
("user", Identity.Entity(receiverComp.ReceivingFrom.Value, EntityManager)), // Floof - resolve virtual items
("item", Identity.Entity(offererComp.GetRealEntity(EntityManager), EntityManager))),
offerer,
receiver);
}
if (!offererComp.IsInReceiveMode)
{
offererComp.ReceivingFrom = null;
receiverComp.ReceivingFrom = null;
}
offererComp.Item = null;
offererComp.Hand = null;
receiverComp.IsInReceiveMode = false;
Dirty(receiver, receiverComp);
}
/// <summary>
/// Accepting the offer and receive item
/// </summary>
public void Receive(Entity<OfferItemComponent?> receiver)
{
if (!_timing.IsFirstTimePredicted)
return;
if (!Resolve(receiver, ref receiver.Comp))
return;
if (!TryComp<OfferItemComponent>(receiver.Comp.ReceivingFrom, out var offererComponent) ||
offererComponent.Hand == null ||
receiver.Comp.ReceivingFrom is not {} sender ||
!TryComp<HandsComponent>(receiver, out var hands))
return;
if (offererComponent.Item != null)
{
// Floof - check if there's something else handling it first
var realItem = offererComponent.GetRealEntity(EntityManager);
if (!TryHandleExtendedTransfer(sender, receiver, offererComponent.Item.Value, realItem)
&& !_hands.TryPickup(receiver, offererComponent.Item.Value, handsComp: hands))
{
_popup.PopupEntity(Loc.GetString("offer-item-full-hand"), receiver, receiver);
return;
}
_popup.PopupEntity(
Loc.GetString("offer-item-give",
("item", Identity.Entity(realItem, EntityManager)), // FLoof - resolve virtual items
("target", Identity.Entity(receiver, EntityManager))),
sender,
sender);
_popup.PopupEntity(
Loc.GetString("offer-item-give-other",
("user", Identity.Entity(receiver.Comp.ReceivingFrom.Value, EntityManager)),
("item", Identity.Entity(realItem, EntityManager)), // FLoof - resolve virtual items
("target", Identity.Entity(receiver, EntityManager))),
sender,
Filter.PvsExcept(sender, entityManager: EntityManager),
true);
}
offererComponent.Item = null;
UnReceive(receiver, receiver.Comp, offererComponent);
}
#endregion
/// <summary>
/// Returns true if <see cref="OfferItemComponent.IsInOfferMode"/> = true
/// </summary>
protected bool IsInOfferMode(Entity<OfferItemComponent?> ent)
{
return Resolve(ent, ref ent.Comp, false) && ent.Comp.IsInOfferMode;
}
private bool TryHandleExtendedTransfer(EntityUid user, EntityUid target, EntityUid offeredItem, EntityUid realItem)
{
var ev = new ItemTransferredEvent
{
User = user,
Target = target,
PassedItem = offeredItem,
RealItem = realItem,
};
RaiseLocalEvent(realItem, ref ev);
return ev.Handled;
}
}
/// <summary>
/// Raised on the entity that was transferred via item offering.
/// </summary>
[ByRefEvent]
public sealed class ItemTransferredEvent : HandledEntityEventArgs
{
public EntityUid User;
public EntityUid Target;
/// <summary>
/// The actual item being passed around. Can be a virtual item.
/// </summary>
public EntityUid PassedItem;
/// <summary>
/// If <see cref="PassedItem"/> is a virtual item, this field contains the real item that was transferred.
/// </summary>
public EntityUid? RealItem;
}

View File

@ -0,0 +1,66 @@
using Robust.Shared.GameStates;
namespace Content.Shared._Impstation.Clothing;
/// <summary>
/// Adds examine text to the entity that wears item, for making things obvious.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(WearerGetsExamineTextSystem))]
public sealed partial class WearerGetsExamineTextComponent : Component
{
/// <summary>
/// The LocId that specifies what category of object this is.
/// i.e. "pin" or "scarf"
/// Should be redefined on a per-category basis, naturally.
/// </summary>
[DataField("thing")]
public LocId Category = "obvious-thing-default";
/// <summary>
/// The LocId that specifies what member of the category this is.
/// i.e. "lesbian pride"
/// Can be used to define text colors that are copied to all things
/// which share this specifier (i.e. the other items of the same pride).
/// (And summarily, makes accessibility-based changes for these colors a cinch.)
/// Should be defined by each thing that has this component.
/// </summary>
[DataField("thingType")]
public LocId Specifier = "obvious-type-default";
/// <summary>
/// The LocId that will be added to any wearing entity's examination.
/// Typically only needs redefining on a per-category basis,
/// but items that should have totally-unique obvious text can simply specify them here.
/// </summary>
[DataField("examineText", required: true)]
public LocId ExamineOnWearer = "obvious-desc-default";
/// <summary>
/// Reference to the entity wearing this clothing.
/// </summary>
[DataField, AutoNetworkedField]
public EntityUid? Wearer;
/// <summary>
/// The string that is attached to this item's ExamineOnWearer.
/// Typically doesn't need to be redefined.
/// </summary>
[DataField]
public LocId PrefixExamineOnWearer = "obvious-prefix-wearing";
/// <summary>
/// If true, an entity with this item in any slot (i.e. in pockets) will gain the examine text,
/// instead of when just equipped as clothing.
/// Should be used sparingly only when truly appropriate; this is effectively a half-measure for lack of a special pin slot.
/// </summary>
[DataField]
public bool PocketEvident;
/// <summary>
/// If true, the entity's description will inform examiners what others will see on the wearer (before they equip it).
/// If the item is contraband, the item will also warn that displaying it may cause undue attention.
/// Keep this false for good-natured jokes (i.e. the pride cloaks having funny, non-pride names)
/// </summary>
[DataField]
public bool WarnExamine = true;
}

View File

@ -0,0 +1,111 @@
using Content.Shared.Clothing.Components;
using Content.Shared.Examine;
using Content.Shared.IdentityManagement;
using Content.Shared.Inventory.Events;
using Content.Shared.Contraband;
using Content.Shared._Impstation.Examine;
using System.Text;
namespace Content.Shared._Impstation.Clothing;
/// <summary>
/// Adds examine text to the entity that wears item, for making things obvious.
/// </summary>
public sealed class WearerGetsExamineTextSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<WearerGetsExamineTextComponent, GotEquippedEvent>(OnEquipped);
SubscribeLocalEvent<WearerGetsExamineTextComponent, GotUnequippedEvent>(OnUnequipped);
SubscribeLocalEvent<WearerGetsExamineTextComponent, ExaminedEvent>(OnExamine);
}
private void OnEquipped(Entity<WearerGetsExamineTextComponent> entity, ref GotEquippedEvent args)
{
if (!TryComp(entity, out ClothingComponent? clothing))
return;
var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
if (!entity.Comp.PocketEvident) //if it can't be evident in our pockets
{
// Make sure the clothing item was equipped to the right slot, and not just held in a hand.
if (!isCorrectSlot)
return;
}
entity.Comp.Wearer = args.Equipee;
Dirty(entity);
//GIVE THEM INSPECT TEXT
var obviousExamine = EnsureComp<ExtraExamineTextComponent>(args.Equipee);
obviousExamine.Lines.TryAdd(entity.Owner, //using try so that we don't cause an error if we move something from slot to slot
ConstructExamineText(entity, !isCorrectSlot, args.Equipee));
}
private string ConstructExamineText(Entity<WearerGetsExamineTextComponent> entity, bool prefixFallback, EntityUid affecting)
{
//parameters (these are the same between both constructions)
var user = Identity.Entity(affecting, EntityManager);
var nomen = Identity.Name(affecting, EntityManager);
var thing = Loc.GetString(entity.Comp.Category);
var type = Loc.GetString(entity.Comp.Specifier);
var stringSpec = entity.Comp.Specifier.ToString();
var shortType = stringSpec.Substring(stringSpec.LastIndexOf('-')); // necessary for working with colored text...
var prefix = Loc.GetString(prefixFallback ? "obvious-prefix-default" : entity.Comp.PrefixExamineOnWearer, // uses a different prefix if worn / displayed
("user", user),
("name", nomen),
("thing", thing),
("type", type));
var suffix = Loc.GetString(entity.Comp.ExamineOnWearer,
("user", user),
("name", nomen),
("thing", thing),
("type", type),
("short-type", shortType));
return prefix + " " + suffix;
}
private void OnUnequipped(Entity<WearerGetsExamineTextComponent> entity, ref GotUnequippedEvent args)
{
if (entity.Comp.Wearer is not { } wearer)
return;
if (TryComp(wearer, out ExtraExamineTextComponent? obviousExamine))
{
obviousExamine.Lines.Remove(entity.Owner);
}
entity.Comp.Wearer = null;
Dirty(entity);
}
private void OnExamine(Entity<WearerGetsExamineTextComponent> entity, ref ExaminedEvent args)
{
var currentlyWorn = entity.Comp.Wearer != null;
var outString = new StringBuilder(Loc.GetString(currentlyWorn ? "obvious-on-item-currently" : "obvious-on-item",
("used", Loc.GetString(entity.Comp.PocketEvident ? "obvious-reveal-pockets" : "obvious-reveal-default")),
("thing", entity.Comp.Category),
("me", Identity.Entity(entity, EntityManager))));
if (entity.Comp.WarnExamine)
{
if (!currentlyWorn && TryComp(entity, out ContrabandComponent? contra)) // if the item's contra and we're not wearing it yet
{
var contraLocId = "obvious-on-item-contra-" + contra.Severity; // apply additional text if the item is contraband to note that displaying it might be really bad
if (Loc.HasString(contraLocId)) // saves us the trouble of making a switch block for this
outString.Append(" " + Loc.GetString(contraLocId));
}
var affecting = currentlyWorn ? entity.Comp.Wearer.GetValueOrDefault() : args.Examiner;
var testOut = ConstructExamineText(entity, false, affecting);
outString.Append("\n" + Loc.GetString("obvious-on-item-for-others",
("will", currentlyWorn ? "can" : "will"), // i love hardcoding strings it's my favorite thing ever
("output", testOut)));
}
args.PushMarkup(outString.ToString());
}
}

View File

@ -0,0 +1,40 @@
using Robust.Shared.GameStates;
namespace Content.Shared._Impstation.Examine;
[RegisterComponent, NetworkedComponent]
public sealed partial class DetailedInspectComponent : Component
{
[DataField]
public LocId VerbText = "verbs-detailed-inspect";
[DataField]
public LocId VerbMessage = "verbs-detailed-inspect-message";
[DataField(required: true)]
public List<LocId> ExamineText;
/// <summary>
/// Whether or not the entries in ExamineText are separated by linebreaks.
/// </summary>
[DataField]
public bool LineBreak = true;
/// <summary>
/// Whether or not the entries in ExamineText are preceded by ticks.
/// </summary>
[DataField]
public bool TickEntries = false;
/// <summary>
/// Whether or not entries in the list are numbered.
/// </summary>
[DataField]
public bool NumberedEntries = false;
/// <summary>
/// Rooted directory of the icon for the verb.
/// </summary>
[DataField]
public string Icon = "/Textures/Interface/VerbIcons/dot.svg.192dpi.png";
}

View File

@ -0,0 +1,50 @@
using Content.Shared.Examine;
using Content.Shared.Verbs;
using Robust.Shared.Network;
using Robust.Shared.Utility;
namespace Content.Shared._Impstation.Examine;
public sealed partial class DetailedInspectSystem : EntitySystem
{
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly ExamineSystemShared _examine = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DetailedInspectComponent, GetVerbsEvent<ExamineVerb>>(OnGetVerb);
}
public void OnGetVerb(Entity<DetailedInspectComponent> ent, ref GetVerbsEvent<ExamineVerb> args)
{
if (!args.CanInteract || !args.CanAccess || !_net.IsServer)
return;
var msg = new FormattedMessage();
var numberedIndex = 1;
foreach (var locId in ent.Comp.ExamineText)
{
if (ent.Comp.TickEntries)
msg.AddMarkupOrThrow("- ");
if (ent.Comp.NumberedEntries)
{
msg.AddMarkupOrThrow($"{numberedIndex}. ");
numberedIndex++;
}
msg.AddMarkupOrThrow(Loc.GetString(locId));
if (ent.Comp.LineBreak)
msg.PushNewline();
else
msg.AddMarkupOrThrow(" ");
}
_examine.AddDetailedExamineVerb(args, ent.Comp, msg, Loc.GetString(ent.Comp.VerbText), ent.Comp.Icon, Loc.GetString(ent.Comp.VerbMessage));
}
}

View File

@ -0,0 +1,24 @@
using Robust.Shared.GameStates;
using Content.Shared._Impstation.Clothing;
namespace Content.Shared._Impstation.Examine;
/// <summary>
/// Adds examine text to the entity, intentionally "obvious details".
/// Like, that's it. It's basic -- all it does is add the lines to the attached entity.
/// This is particularly used for assigning players unique examine text.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(ExtraExamineTextSystem), typeof(WearerGetsExamineTextSystem))]
public sealed partial class ExtraExamineTextComponent : Component
{
/// <summary>
/// The LocIds that will be added to the attached entity's examination.
///
/// The key is the source of the line, and the value is the text
/// (built by the component that creates this one).
/// </summary>
[DataField, AutoNetworkedField]
public Dictionary<EntityUid, LocId> Lines { get; set; } = new();
}

View File

@ -0,0 +1,33 @@
using Content.Shared.Examine;
using Content.Shared.IdentityManagement;
namespace Content.Shared._Impstation.Examine;
/// <summary>
/// Adds examine text to the entity, intentionally "obvious details".
/// Like, that's it. It's basic -- all it does is add the line to the attached entity.
/// This is particularly used for assigning players unique examine text.
/// </summary>
public sealed class ExtraExamineTextSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ExtraExamineTextComponent, ExaminedEvent>(OnExamine);
}
private void OnExamine(Entity<ExtraExamineTextComponent> entity, ref ExaminedEvent args)
{
if (entity.Comp.Lines.Count == 0)
{
RemCompDeferred<ExtraExamineTextComponent>(entity); // no more need for me!
return;
}
foreach (var l in entity.Comp.Lines)
{
args.PushMarkup(l.Value);
}
}
}

View File

@ -0,0 +1,8 @@
namespace Content.Shared._Starlight.Flash.Components;
[RegisterComponent]
public sealed partial class FlashModifierComponent : Component
{
[DataField]
public float Modifier = 1f;
}

View File

@ -0,0 +1,51 @@
namespace Content.Shared.Humanoid;
public static class EyeColor
{
public const float ShadekinBrightness = 0.251f;
public static bool VerifyShadekin(Color color)
{
var colorHsv = Color.ToHsv(color);
if (colorHsv.Z > ShadekinBrightness)
return false;
return true;
}
public static Color MakeShadekinValid(Color color)
{
var hsv = Color.ToHsv(color);
hsv.Z = Math.Clamp(hsv.Z, 0, ShadekinBrightness);
return Color.FromHsv(hsv);
}
public static bool VerifyEyeColor(HumanoidEyeColor type, Color color)
{
return type switch
{
HumanoidEyeColor.Shadekin => VerifyShadekin(color),
_ => false,
};
}
public static Color ValidEyeColor(HumanoidEyeColor type, Color color)
{
return type switch
{
HumanoidEyeColor.Shadekin => MakeShadekinValid(color),
_ => color
};
}
}
public enum HumanoidEyeColor : byte
{
Shadekin,
}
[ByRefEvent]
public record struct EyeColorInitEvent();

View File

@ -0,0 +1,45 @@
using Content.Shared.Alert;
using Content.Shared.FixedPoint;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._Starlight;
#region Shadekin
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause, AutoGenerateComponentState]
public sealed partial class ShadekinComponent : Component
{
[DataField]
public ProtoId<AlertPrototype> ShadekinAlert = "Shadekin";
[ViewVariables(VVAccess.ReadOnly), AutoPausedField]
public TimeSpan NextUpdate = TimeSpan.Zero;
[DataField]
public TimeSpan UpdateCooldown = TimeSpan.FromSeconds(1f);
[AutoNetworkedField, ViewVariables]
public ShadekinState CurrentState { get; set; } = ShadekinState.Dark;
[DataField("thresholds", required: true)]
public SortedDictionary<FixedPoint2, ShadekinState> Thresholds = new();
/// <summary>
/// whether to flicker lights or not. default on
/// </summary>
[DataField] public bool DoLightFlicker = true;
}
[Serializable, NetSerializable]
public enum ShadekinState : byte
{
Invalid = 0,
Dark = 1,
Low = 2,
Annoying = 3,
High = 4,
Extreme = 5
}
#endregion

View File

@ -0,0 +1,29 @@
- files: ["wurble.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Taken from CHOMPStation"
source: "https://github.com/CHOMPStation2/CHOMPStation2/blob/e846f4f73d14875a390ede039a11e4b9b699f420/sound/voice/wurble.ogg"
- files: ["mar.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Taken from CHOMPStation"
source: "https://github.com/CHOMPStation2/CHOMPStation2/blob/e846f4f73d14875a390ede039a11e4b9b699f420/sound/voice/mar.ogg"
- files: ["scream_f1.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Taken and remixed from CHOMPStation"
source: "https://github.com/CHOMPStation2/CHOMPStation2/blob/e846f4f73d14875a390ede039a11e4b9b699f420/sound/voice/scream_f1.ogg"
- files: ["scream_f2.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Taken and remixed from CHOMPStation"
source: "https://github.com/CHOMPStation2/CHOMPStation2/blob/e846f4f73d14875a390ede039a11e4b9b699f420/sound/voice/scream_f2.ogg"
- files: ["scream_m1.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Taken and remixed from CHOMPStation"
source: "https://github.com/CHOMPStation2/CHOMPStation2/blob/e846f4f73d14875a390ede039a11e4b9b699f420/sound/voice/scream_m1.ogg"
- files: ["scream_m2.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Taken and remixed from CHOMPStation"
source: "https://github.com/CHOMPStation2/CHOMPStation2/blob/e846f4f73d14875a390ede039a11e4b9b699f420/sound/voice/scream_f2.ogg"

Binary file not shown.

Binary file not shown.

View File

@ -348,5 +348,13 @@ Entries:
id: 38
time: '2026-07-19T16:18:47.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6317
- author: ShepardToTheStars
changes:
- message: '`entities` and a lot of other basic QUERY commare are now a DEBUG-level
commands.'
type: Tweak
id: 39
time: '2026-07-24T17:27:48.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6351
Name: DeltaVAdmin
Order: 5

View File

@ -1,215 +1,4 @@
Entries:
- author: Toby222
changes:
- message: Removed footprint prediction (for now)
type: Remove
id: 2074
time: '2026-01-16T19:07:45.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5224
- author: Velcroboy
changes:
- message: Prisoners can find dull bread knives in the ChefVend.
type: Tweak
id: 2075
time: '2026-01-17T21:13:59.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5234
- author: Halo3moth
changes:
- message: The T1 armored vest (slim) can now once again be picked in security loadouts.
type: Add
id: 2076
time: '2026-01-17T21:36:45.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5220
- author: biddygelson
changes:
- message: 'Academy: added anti-meteor zones and anti-anomaly zone, AI restoration
console in Mystagogue, Robotics Console in robo, fishing hole added to park,
materials is cargo at roundstart.'
type: Add
- message: 'Academy: station starts with distro and waste enabled, spare atmos chambers
start as vacuums, split artifacts'' APCs for power management, botany has more
trays and is slightly larger, department-front holopads in the department.'
type: Tweak
id: 2077
time: '2026-01-18T01:54:56.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5130
- author: DisposableCrewmember42
changes:
- message: You can no longer drink the deep fryer.
type: Fix
id: 2078
time: '2026-01-18T04:40:48.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5238
- author: snowywinters
changes:
- message: Reinforced cameras can now be built! They have additional HP and are
more resistant to explosions and meteors.
type: Add
- message: The AI has its own section in the security cameras console.
type: Add
id: 2079
time: '2026-01-18T17:18:45.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5027
- author: Dorragon
changes:
- message: Ready Manifest added to the lobby
type: Add
id: 2080
time: '2026-01-18T18:47:07.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5187
- author: MilonPL
changes:
- message: Added a new traits system, supporting conditions and new effects. Make
sure to check out the Traits tab in the character editor!
type: Add
- message: All character traits have been reset.
type: Remove
id: 2081
time: '2026-01-20T11:49:59.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5208
- author: pootslap
changes:
- message: You can now examine character descriptions from much further away.
type: Tweak
id: 2082
time: '2026-01-20T11:49:48.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5219
- author: Eternally-Confused
changes:
- message: (TEST MERGE) Space pens now contain a chemical called Stabilizine instead
of Leporazine, which is better at stabilizing body temperature.
type: Tweak
- message: Avali no longer die from space pens.
type: Tweak
id: 2083
time: '2026-01-20T21:15:21.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5181
- author: turtlemutt
changes:
- message: Added more names for ghost role spawns.
type: Add
id: 2084
time: '2026-01-21T05:17:05.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5254
- author: Cepelinas1
changes:
- message: Logistics Officers can now use the shipyard to buy shuttles. No need
to beg Captain to buy it anymore!
type: Add
id: 2085
time: '2026-01-21T05:52:10.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5251
- author: Colin-Tel
changes:
- message: Eked the Anglish tung tie. In other words, added a trait that lets you
talk like an Anglo Saxon with over 1,100 word replacements.
type: Add
id: 2086
time: '2026-01-21T09:37:07.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4092
- author: ShepardToTheStars
changes:
- message: The ninja is now revealed with hit with attacks, projectiles, explosions,
and grenades that do damage.
type: Tweak
- message: The ninja is now revealed with hit with attacks, projectiles, explosions,
and grenades that do damage.
type: Fix
id: 2087
time: '2026-01-21T14:18:56.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5217
- author: Toby222
changes:
- message: FTL times have been adjusted. Startup is shorter, travel is longer.
type: Tweak
- message: The FTL startup sound now lines up with the duration of startup again!!!
type: Fix
id: 2088
time: '2026-01-21T14:50:49.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5257
- author: ShepardToTheStars, foxcumberland
changes:
- message: Added fashionable longcoats for command, security, and crew. Find them
in the loadouts for command, security, and passengers, or in the ClothesMate
on station!
type: Add
id: 2089
time: '2026-01-21T16:23:14.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5235
- author: Apachito
changes:
- message: Made tide breaker actually craftable
type: Fix
id: 2090
time: '2026-01-21T16:28:54.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5239
- author: ShepardToTheStars
changes:
- message: Certain painkillers are more long-lasting than others, based on how strong
they are.
type: Tweak
- message: Players with the chronic pain trait start with ~5 minutes of suppressed
pain before they start feeling the effects.
type: Tweak
- message: Fixed chronic pain's effects from pulsing continuously.
type: Fix
id: 2091
time: '2026-01-23T10:24:32.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5180
- author: snowywinters
changes:
- message: Captain's armored winter coat a alternative to the carapace
type: Add
- message: Changed the defense of captain's EVA suit and carapace
type: Tweak
id: 2092
time: '2026-01-23T19:31:48.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5227
- author: pootslap
changes:
- message: Listening Post Operatives only have the ability to hear binary comms
now. They can no longer talk on binary.
type: Tweak
id: 2093
time: '2026-01-23T19:35:58.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5243
- author: MilonPL
changes:
- message: The traits points bar will now show up correctly.
type: Fix
id: 2094
time: '2026-01-24T20:55:31.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5275
- author: MilonPL
changes:
- message: The uncloneable trait no longer prevents your character from getting
revived by defibrillators.
type: Fix
id: 2095
time: '2026-01-24T21:29:58.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5273
- author: Halo3moth
changes:
- message: The energy magnums "magnum" rounds no longer pierce windows
type: Fix
id: 2096
time: '2026-01-25T13:48:54.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5270
- author: Toby222
changes:
- message: Added buttons for build info and credits to the escape menu
type: Tweak
id: 2097
time: '2026-01-25T14:42:09.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5260
- author: MilonPL
changes:
- message: Added the "Marked as Protected" trait which makes you immune to becoming
a target objective.
type: Add
id: 2098
time: '2026-01-25T16:30:18.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5274
- author: AeraAuling
changes:
- message: Shadow damage no longer removes all your blood immediately
@ -4369,4 +4158,209 @@
id: 2574
time: '2026-07-20T00:24:43.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6310
- author: kotobdev
changes:
- message: The Ardent Censer no longer requires fuel for deconversions.
type: Tweak
id: 2575
time: '2026-07-20T17:38:51.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5016
- author: Halo3rat, kotobdev
changes:
- message: The Freighter Crew's Jumpsuit is now available in the AutoDrobe, for
those who value their dental hygiene.
type: Add
id: 2576
time: '2026-07-20T20:06:59.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6331
- author: Mnemotechnician, kotobdev, ShepardToTheStars
changes:
- message: You can now offer items to other players by pressing F, and then clicking
on them! They can accept by clicking on the alert on the side of their screen.
type: Add
id: 2577
time: '2026-07-20T20:13:58.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5597
- author: ShepardToTheStars
changes:
- message: Glimmer probers can once again turn on and lock into place when glimmer
is over 750.
type: Add
- message: Glimmer prober explosions are 1.5x bigger if glimmer is over 900. You
better build drainers! :)
type: Tweak
- message: The oracle can now give normality crystals when completing her requests.
type: Tweak
id: 2578
time: '2026-07-20T20:29:02.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6292
- author: keekee38
changes:
- message: Added the novice mark, to help show that you're new! It is on intern
roles by default, and optional for everyone
type: Add
- message: Some clothes now have examine text, in particular pride clothes.
type: Tweak
id: 2579
time: '2026-07-20T22:08:57.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6118
- author: Coryler
changes:
- message: Shadekin has arrived in the DeltaV sector.
type: Add
id: 2580
time: '2026-07-20T22:10:50.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5481
- author: sowelipililimute
changes:
- message: The crew monitor now reports global coordinate positions.
type: Tweak
id: 2581
time: '2026-07-20T22:20:12.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6326
- author: keekee38
changes:
- message: Vulpkanins can yip now.
type: Tweak
id: 2582
time: '2026-07-20T22:21:24.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6301
- author: pootslap
changes:
- message: Improvised shotgun shells use the old upstream crafting recipe.
type: Tweak
- message: The improvised shotgun is craftable once again.
type: Fix
id: 2583
time: '2026-07-20T22:22:59.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6327
- author: kotobdev
changes:
- message: Security cold weather ponchos are gone from loadouts and the SecDrobe.
type: Remove
id: 2584
time: '2026-07-20T22:44:01.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6332
- author: sowelipililimute
changes:
- message: Security now has access to evidence bags, for storing all the illegal
things that can be stored.
type: Add
id: 2585
time: '2026-07-20T23:17:53.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6280
- author: jessiscah23
changes:
- message: Added organizational capabilities to the cargo bounty computer.
type: Add
id: 2586
time: '2026-07-20T23:23:28.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6306
- author: Proxysseia
changes:
- message: (TEST MERGE) Increased traitor starting telecrystal balance from 20 to
30.
type: Tweak
id: 2587
time: '2026-07-20T23:37:07.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6275
- author: ArionGekkota
changes:
- message: reintroduced researchable PKA modkits
type: Add
- message: Changed the PKA to its current upstream state
type: Tweak
- message: Reduced the health of some salvage mobs
type: Tweak
id: 2588
time: '2026-07-20T23:46:11.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6258
- author: nlaky
changes:
- message: Fixed incorrect Vulpkanin mask sprites.
type: Fix
id: 2589
time: '2026-07-21T00:05:09.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6201
- author: Stxcking
changes:
- message: Vulps can yip properly now.
type: Fix
id: 2590
time: '2026-07-21T02:20:30.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6354
- author: Coryler
changes:
- message: Shadekin meds working on them
type: Fix
- message: Shadekin emotes being available to other species
type: Fix
id: 2591
time: '2026-07-21T07:58:56.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6357
- author: Coryler
changes:
- message: Add back Felinid root ability
type: Add
id: 2592
time: '2026-07-21T18:44:49.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6358
- author: EmberAstra
changes:
- message: Cosmic Cult's Entropic Blades are now smaller (2x4 spaces)
type: Tweak
id: 2593
time: '2026-07-23T03:17:02.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6371
- author: turtlemutt
changes:
- message: Gave Lighthouse new doorbells and pagers. Have fun harassing departments!
type: Tweak
id: 2594
time: '2026-07-23T16:29:30.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6088
- author: ShepardToTheStars
changes:
- message: Buckling someone while they are being carried should work as intended
now.
type: Fix
- message: Picking someone up that is criticla will have priority over CPR unless
they are buckled to something.
type: Tweak
id: 2595
time: '2026-07-23T20:07:04.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6375
- author: ManuDemoen
changes:
- message: Emergency access no longer allows anyone with a remote to bolt the door
and checks if the remote has access to bolt.
type: Fix
id: 2596
time: '2026-07-24T17:28:01.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6141
- author: pootslap
changes:
- message: Monkeys now make sounds when using emotes!
type: Add
id: 2597
time: '2026-07-24T17:28:47.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5928
- author: keekee38
changes:
- message: The WT550 once again fires at 5.5 rounds a second and has 30-round magazines.
type: Tweak
id: 2598
time: '2026-07-24T17:34:48.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6364
- author: SirWarock
changes:
- message: The NT-3 cannot be parcel-wrapped anymore!
type: Tweak
- message: Adjusted the wielding sprites of the NT-3 to look less wrong.
type: Fix
- message: The NT-3 can pierce things again.
type: Fix
id: 2599
time: '2026-07-24T17:39:18.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5653
Order: 1

File diff suppressed because one or more lines are too long

View File

@ -16,3 +16,6 @@ delta-chat-emote-name-harpysquish = Squish
delta-chat-emote-name-quack = Quack
delta-chat-emote-name-squawk = Squawk
delta-chat-emote-name-horn = Horn
delta-chat-emote-name-yip = Yip
delta-chat-emote-msg-yip = yips.

View File

@ -0,0 +1,4 @@
dv-seen-inserted-item-name-modifier = {$baseName} with { INDEFINITE($contained) } { $contained }
dv-seen-inserted-item-examined =
.full = { CAPITALIZE(SUBJECT($container)) } has [color=paleturquoise]{ INDEFINITE($contained) } { $contained }[/color] in { SUBJECT($container) }.
.empty = { CAPITALIZE(SUBJECT($container)) } has nothing in { SUBJECT($container) }.

View File

@ -0,0 +1,154 @@
# Ears
marking-EarsShadekinMotley = Motley Ears
marking-EarsShadekinMotley-shadowkin = Ears
marking-EarsShadekinMotley-motley_secondary = Pattern
marking-EarsShadekinShady = Dark Ears
marking-EarsShadekinShady-shady = Ears
marking-EarsShadekinStriped = Striped Ears
marking-EarsShadowkinStriped-shadowkin = Ears
marking-EarsShadowkinStriped-shadowkin_stripes = Stripes
marking-EarsShadekinPiercingAll = Ears with Piercings
marking-EarsShadekinPiercingAll-shadowkin = Ears
marking-EarsShadekinPiercingAll-piercing_all = Piercings
marking-EarsShadekinPiercingLeft = Ears with Piercing (L)
marking-EarsShadekinPiercingLeft-shadowkin = Ears
marking-EarsShadekinPiercingLeft-piercing_left = Piercing
marking-EarsShadekinPiercingRight = Ears with Piercing (R)
marking-EarsShadekinPiercingRight-shadowkin = Ears
marking-EarsShadekinPiercingRight-piercing_right = Piercing
marking-EarsShadekinRingedAll = Ears with Rings
marking-EarsShadekinRingedAll-shadowkin = Ears
marking-EarsShadekinRingedAll-ringed_all_secondary = Rings
marking-EarsShadekinRingedLeft = Ears with Ring (L)
marking-EarsShadekinRingedLeft-shadowkin = Ears
marking-EarsShadekinRingedLeft-ringed_left_secondary = Ring
marking-EarsShadekinRingedRight = Ears with Ring (R)
marking-EarsShadekinRingedRight-shadowkin = Ears
marking-EarsShadekinRingedRight-ringed_right_secondary = Ring
marking-EarsShadekinGauzedAll = Gauzed Ears
marking-EarsShadekinGauzedAll-shadowkin = Ears
marking-EarsShadekinGauzedAll-gauze_ear_all_default = Gauze
marking-EarsShadekinGauzedLeft = Ear Bandage (L)
marking-EarsShadekinGauzedLeft-shadowkin = Ears
marking-EarsShadekinGauzedLeft-gauze_ear_l_default = Bandage
marking-EarsShadekinGauzedRight = Ear Bandage (R)
marking-EarsShadekinGauzedRight-shadowkin = Ears
marking-EarsShadekinGauzedRight-gauze_ear_r_default = Bandage
marking-EarsShadekinFluffy = Fluffy Ears
marking-EarsShadekinFluffy-fluffy_default = Ears
marking-EarsShadekinFluffyButterfly = Fluffy Ears, Butterfly
marking-EarsShadekinFluffyButterfly-fluffy_default = Ears
marking-EarsShadekinFluffyButterfly-fluffy_butterfly = Pattern
marking-EarsShadekinFluffyCowling = Fluffy Ears, Spotted
marking-EarsShadekinFluffyCowling-fluffy_default = Ears
marking-EarsShadekinFluffyCowling-fluffy_cowling = Pattern
marking-EarsShadekinFluffyCrow = Fluffy Ears, Crow
marking-EarsShadekinFluffyCrow-fluffy_default = Ears
marking-EarsShadekinFluffyCrow-fluffy_crow = Pattern
marking-EarsShadekinFluffyMotley = Fluffy Ears, Bright
marking-EarsShadekinFluffyMotley-fluffy_default = Ears
marking-EarsShadekinFluffyMotley-fluffy_motley = Pattern
marking-EarsShadekinFluffySpidy = Fluffy Ears, Spidy
marking-EarsShadekinFluffySpidy-fluffy_default = Ears
marking-EarsShadekinFluffySpidy-fluffy_spidy = Pattern
marking-EarsShadekinFluffyRingedAll = Fluffy Ears, with Rings
marking-EarsShadekinFluffyRingedAll-fluffy_default = Ears
marking-EarsShadekinFluffyRingedAll-fluffy_ringed_all = Rings
marking-EarsShadekinFluffyRingedLeft = Fluffy Ears, with Ring (L)
marking-EarsShadekinFluffyRingedLeft-fluffy_default = Ears
marking-EarsShadekinFluffyRingedLeft-fluffy_ringed_left = Ring
marking-EarsShadekinFluffyRingedRight = Fluffy Ears, with Ring (R)
marking-EarsShadekinFluffyRingedRight-fluffy_default = Ears
marking-EarsShadekinFluffyRingedRight-fluffy_ringed_right = Ring
marking-EarsShadekinSaggy = Drooping Ears
marking-EarsShadekinSaggy-saggy_default = Ears
marking-EarsShadekinSaggyBrawly = Drooping Ears, Patterned
marking-EarsShadekinSaggyBrawly-saggy_default = Ears
marking-EarsShadekinSaggyBrawly-saggy_brawly = Pattern
marking-EarsShadekinSaggyZebra = Drooping Ears, Striped
marking-EarsShadekinSaggyZebra-saggy_default = Ears
marking-EarsShadekinSaggyZebra-saggy_zebra = Stripes
marking-EarsShadekinSaggyGradient = Drooping Ears, Gradient
marking-EarsShadekinSaggyGradient-saggy_default = Ears
marking-EarsShadekinSaggyGradient-saggy_gradient = Gradient
marking-EarsShadekinSaggyGauzedAll = Drooping Ears, Gauzed
marking-EarsShadekinSaggyGauzedAll-saggy_default = Ears
marking-EarsShadekinSaggyGauzedAll-saggy_gauze_all = Gauze
marking-EarsShadekinSaggyGauzedLeft = Drooping Ears, Ear Bandage (L)
marking-EarsShadekinSaggyGauzedLeft-saggy_default = Ears
marking-EarsShadekinSaggyGauzedLeft-saggy_gauze_l = Bandage
marking-EarsShadekinSaggyGauzedRight = Drooping Ears, Ear Bandage (R)
marking-EarsShadekinSaggyGauzedRight-saggy_default = Ears
marking-EarsShadekinSaggyGauzedRight-saggy_gauze_r = Bandage
marking-EarsShadekinShort = Short Ears
marking-EarsShadekinShort-short_default = Ears
marking-EarsShadekinShortButterfly = Short Ears, Decorated
marking-EarsShadekinShortButterfly-short_default = Ears
marking-EarsShadekinShortButterfly-short_butterfly = Pattern
marking-EarsShadekinShortRingedAll = Short Ears, with Rings
marking-EarsShadekinShortRingedAll-short_default = Ears
marking-EarsShadekinShortRingedAll-short_ringed_all = Rings
marking-EarsShadekinShortRingedLeft = Short Ears, with Ring (L)
marking-EarsShadekinShortRingedLeft-short_default = Ears
marking-EarsShadekinShortRingedLeft-short_ringed_left = Ring
marking-EarsShadekinShortRingedRight = Short Ears, with Ring (R)
marking-EarsShadekinShortRingedRight-short_default = Ears
marking-EarsShadekinShortRingedRight-short_ringed_right = Ring
marking-EarsShadekinBull = Straight Ears
marking-EarsShadekinBull-bull_default = Ears
marking-EarsShadekinBullSmooth = Straight Ears, Outlined
marking-EarsShadekinBullSmooth-bull_default = Ears
marking-EarsShadekinBullSmooth-bull_smooth = Inner ears
marking-EarsShadekinAqua = Aqua Ears
marking-EarsShadekinAqua-aqua_default = Ears
marking-EarsShadekinAquaIncolor = Aqua Ears, Colored
marking-EarsShadekinAquaIncolor-aqua_default = Ears
marking-EarsShadekinAquaIncolor-aqua_incolor = Inner ears
# Tails
marking-TailShadekin = Long Tail
marking-TailShadekinBig = Big Tail
marking-TailShadekinShorter = Short Tail
marking-TailShadekinShorter-shadekin_shorter_over = Tail
marking-TailShadekinShorterBrush = Short Tail, Furry
marking-TailShadekinShorterBrush-shadekin_shorter_over = Tail
marking-TailShadekinShorterBrush-shorter_brush_over = Tail tip
marking-TailShadekinMedium = Medium Tail
marking-TailShadekinMedium-shadekin_medium_front = Tail
marking-TailShadekinMediumTwoColored = Medium Tail, Two-Toned
marking-TailShadekinMediumTwoColored-shadekin_medium_front = Tail
marking-TailShadekinMediumTwoColored-medium_twocolored_over = Tail underside
# Overlays
marking-BodyShadekinArrow = Arrow Marking
marking-BodyShadekinArrow-body_arrow = Marking
marking-BodyShadekinBlackHole = Black Hole Marking
marking-BodyShadekinBlackHole-body_blackhole = Marking
marking-BodyShadekinBrace = Bracelet Marking
marking-BodyShadekinBrace-body_brace = Marking
marking-BodyShadekinBrawly = Combat Marking
marking-BodyShadekinBrawly-body_brawly = Marking
marking-BodyShadekinHeart = Heart Marking
marking-BodyShadekinHeart-body_heart = Marking
marking-BodyShadekinInk = Ink Marking
marking-BodyShadekinInk-body_ink = Marking
marking-BodyShadekinInk2 = Ink Marking 2
marking-BodyShadekinInk2-body_ink2 = Marking
marking-BodyShadekinInk3 = Ink Marking 3
marking-BodyShadekinInk3-body_ink3 = Marking
marking-BodyShadekinInk4 = Ink Marking 4
marking-BodyShadekinInk4-body_ink4 = Marking
marking-BodyShadekinInk5 = Ink Marking 5
marking-BodyShadekinInk5-body_ink5 = Marking
marking-BodyShadekinInk6 = Spiritual Ink Marking
marking-BodyShadekinInk6-body_ink6 = Marking
marking-BodyShadekinInkAnim = Spiral Ink Marking
marking-BodyShadekinInkAnim-body_ink_anim = Marking
marking-BodyShadekinLines = Lines Marking
marking-BodyShadekinLines-body_lines = Marking
marking-BodyShadekinNeck = Neck Marking
marking-BodyShadekinNeck-body_neck = Marking
marking-BodyShadekinShield = Protective Marking
marking-BodyShadekinShield-body_shield = Marking
marking-BodyShadekinVacuum = Open Marking
marking-BodyShadekinVacuum-body_vacuum = Marking

View File

@ -0,0 +1,2 @@
command-description-doext-client = Sends a console command back to the calling user's own client to run locally, once per piped value, substituting $ID/$PID/$WX/$WY/$LX/$LY/$SELF as well as any toolshed variable by name.
command-description-doext-server = Runs a console command on the server as the calling session, once per piped value, substituting $ID/$PID/$WX/$WY/$LX/$LY/$SELF as well as any toolshed variable by name.

View File

@ -0,0 +1 @@
command-description-FieldCommand = Reads the value of a field or property (by name) off of the piped input, resolved the same way vvread does.

View File

@ -1,2 +0,0 @@
ent-MagazinePistolSubMachineGunTopMounted =
.desc = Unconventional 20-round top feeding magazine for the WT550 SMG. Intended to hold general-purpose kinetic ammunition.

View File

@ -0,0 +1,17 @@
offer-item-empty-hand = You don't have anything in your hand to give!
offer-item-full-hand = Your hand isn't free to receive the item.
offer-item-try-give = You offer {THE($item)} to {$target}.
offer-item-try-give-target = {CAPITALIZE(THE($user))} offers you {THE($item)}.
offer-item-give = You handed {THE($item)} to {$target}.
offer-item-give-other = {CAPITALIZE(THE($user))} handed {THE($item)} to {$target}.
offer-item-give-target = {CAPITALIZE(THE($user))} handed you {THE($item)}.
offer-item-no-give = You stop offering {THE($item)} to {$target}.
offer-item-no-give-target = {CAPITALIZE(THE($user))} is no longer offering {THE($item)} to you.
alerts-offer-name = Accept Offer
alerts-offer-desc = Click this alert to accept the item offered to you.

View File

@ -0,0 +1,65 @@
obvious-on-item = When {$used}, { SUBJECT($me) } will be [color=white]obvious[/color] to casual examination.
obvious-on-item-currently = { CAPITALIZE(SUBJECT($me)) } { CONJUGATE-BE($me) } [color=white]obvious[/color] to casual examination.
obvious-on-item-for-others = [italic][color=#777777]Others {$will} see:[/color] "{$output}"[/italic]
obvious-reveal-default = worn
obvious-reveal-pockets = worn or pocketed
obvious-on-item-contra-Syndicate = Might make you seem [color=#ff0000]evil[/color].
obvious-on-item-contra-Magical = Might make you seem [color=blue]mystical[/color].
obvious-on-item-contra-Major = Don't be surprised if [color=#cb0000]some people[/color] aren't a fan.
obvious-prefix-default = { CAPITALIZE(SUBJECT($user)) } { CONJUGATE-HAVE($user) }
obvious-prefix-wearing = { CAPITALIZE(SUBJECT($user)) } { CONJUGATE-BE($user) } wearing
## general descriptions
obvious-desc-default = { INDEFINITE($short-type) } { $type } { $thing }.
obvious-desc-colors = { INDEFINITE($thing) } { $thing } in { $type } colors.
obvious-desc-proves = { INDEFINITE($thing) } [color=white]{ $thing }[/color] that proves { SUBJECT($user) } { CONJUGATE-BE($user) } { INDEFINITE($short-type) } { $type }.
## item categories
obvious-thing-default = [italic]something-or-another[/italic]
obvious-thing-cloak = cloak
obvious-thing-pin = pin
obvious-thing-scarf = scarf
obvious-thing-badge = badge
obvious-thing-armband = armband
obvious-thing-medal = medal
## item types
obvious-type-default = [italic]nothing[/italic]
# prides
obvious-type-pride = [color=#d479d4]LGBTQ[/color] [color=white]pride[/color]
obvious-type-pride-aro = [color=#3DA542]aromantic[/color] [color=white]pride[/color]
obvious-type-pride-ace = [color=#efefef]asexual[/color] [color=#800080]pride[/color]
obvious-type-pride-aroace = [color=#efc337]aroace[/color] [color=#45bcee]pride[/color]
obvious-type-pride-lesbian = [color=#EF7627]lesbian[/color] [color=#B55690]pride[/color]
obvious-type-pride-bi = [color=#D60270]bisexual[/color] [color=#0038A8]pride[/color]
obvious-type-pride-pan = [color=#FF218C]pansexual[/color] [color=#21B1FF]pride[/color]
obvious-type-pride-gay = [color=#078D70]gay[/color] [color=#5049CC]pride[/color]
obvious-type-pride-omni = [color=#FE9ACE]omnisexual[/color] [color=8EA6FF]pride[/color]
obvious-type-pride-bear = [color=#FDDC62]bear[/color] [color=#613704]pride[/color]
obvious-type-pride-intersex = [color=#FFD800]intersex[/color] [color=#7902AA]pride[/color]
obvious-type-pride-nonbinary = [color=#FCF434]nonbinary[/color] [color=#9C59D1]pride[/color]
obvious-type-pride-trans = [color=#5BCEFA]transgender[/color] [color=#F5A9B8]pride[/color]
obvious-type-pride-gf = [color=#FF76A4]genderfluid[/color] [color=#2F3CBE]pride[/color]
obvious-type-pride-aut = [color=#FFD700]autism[/color] [color=white]pride[/color]
obvious-type-pride-straightally = [color=#7c7c7c]straight[/color] [color=#d479d4]LGBTQ ally[/color]
obvious-type-pride-gq = [color=#B57EDC]genderqueer[/color] [color=#4A8123]pride[/color]
# lawyers
obvious-type-law = [color=white]certified[/color] [color=#FFD700]lawyer[/color]
obvious-type-law-defense = [color=white]certified[/color] [color=#00C0C0]defense attorney[/color]
obvious-type-law-prosecution = [color=white]certified[/color] [color=#FF2222]prosecuting attorney[/color]
## SPECIFIC DESCRIPTIONS (skips the above description-builders)
obvious-x-scarf-lesbian-long = some [color=#EF7627]long[/color] [color=white]ba[/color][color=#B55690]con[/color].
obvious-x-medal-nothing = a [color=#FFD700]gleaming medal[/color]!
# medals should be in the description building functions but there's too many of them and this feature is already over-scope
# imp only items
obvious-x-pin-straight = a [color=#7c7c7c]straight[/color] [color=white]pride[/color] pin... [italic]Ew.[/italic]
obvious-x-cloak-straight = a cloak in the [color=#7c7c7c]straight[/color] [color=white]pride[/color] colors... [italic]Ew.[/italic]
obvious-x-pin-novice = a [color=#F4EE12]novice[/color] [color=#1CAC99]mark[/color], identifying as [color=#efefef]unskilled[/color]. [color=#1CAC99][italic]Be patient and considerate with { OBJECT($user) }.[/italic][/color]

View File

@ -0,0 +1,58 @@
# Sad
names-shadekin-dataset-1 = Fragile
names-shadekin-dataset-2 = Heartbreak
names-shadekin-dataset-3 = Inferior
names-shadekin-dataset-4 = Lone
names-shadekin-dataset-5 = Lonesome
names-shadekin-dataset-6 = Loss
names-shadekin-dataset-7 = Solitary
names-shadekin-dataset-8 = Solitude
names-shadekin-dataset-9 = Sorrow
names-shadekin-dataset-10 = Shade
# Angry
names-shadekin-dataset-11 = Fear
names-shadekin-dataset-12 = Fearful
names-shadekin-dataset-13 = Fury
names-shadekin-dataset-14 = Pain
names-shadekin-dataset-15 = Rage
names-shadekin-dataset-16 = Rush
names-shadekin-dataset-17 = Wrath
# Happy
names-shadekin-dataset-18 = Calm
names-shadekin-dataset-19 = Content
names-shadekin-dataset-20 = Contented
names-shadekin-dataset-21 = Happy
names-shadekin-dataset-22 = Hope
names-shadekin-dataset-23 = Joyous
names-shadekin-dataset-24 = Lovely
names-shadekin-dataset-25 = Peace
names-shadekin-dataset-26 = Peaceful
names-shadekin-dataset-27 = Quiet
names-shadekin-dataset-28 = Serene
names-shadekin-dataset-29 = Serenity
names-shadekin-dataset-30 = Tranquil
names-shadekin-dataset-31 = Tranquility
# Memory
names-shadekin-dataset-32 = Dillusioned
names-shadekin-dataset-33 = Forgotten
names-shadekin-dataset-34 = Focusless
names-shadekin-dataset-35 = Lost
names-shadekin-dataset-36 = Memory
names-shadekin-dataset-37 = Recollection
names-shadekin-dataset-38 = Remembrance
names-shadekin-dataset-39 = Reminisce
names-shadekin-dataset-40 = Reminiscence
# Other
names-shadekin-dataset-41 = Apathy
names-shadekin-dataset-42 = Collected
names-shadekin-dataset-43 = Curiosity
names-shadekin-dataset-44 = Free
names-shadekin-dataset-45 = Interest
names-shadekin-dataset-46 = Jax
names-shadekin-dataset-47 = Still
names-shadekin-dataset-48 = Unbound
names-shadekin-dataset-49 = Shadows

View File

@ -0,0 +1,5 @@
chat-emote-name-marr = Marr
chat-emote-name-wurble = Wurble
chat-emote-msg-marr = marrs!
chat-emote-msg-wurble = wurbles!

View File

@ -0,0 +1,14 @@
alerts-shadekin-name = Light Exposure
alerts-shadekin-desc = How much light is around you.
shadekin-alert-0 = [color=green]Light Exposure: Darkness...[/color]
The darkness is nice... It's like home... I feel my wounds healing...
shadekin-alert-1 = [color=green]Light Exposure: Low[/color]
shadekin-alert-2 = [color=green]Light Exposure: Annoying[/color]
The light is annoying in this place... I feel my wounds will not heal properly...
shadekin-alert-3 = [color=green]Light Exposure: High[/color]
Too many lights... too many! I feel exausted...
shadekin-alert-4 = [color=green]Light Exposure:[/color] [color=red]EXTREME[/color]
LIGHTS... I NEED DARKNESS! It burns!
phase-fail-generic = I can't phase!

View File

@ -0,0 +1 @@
species-name-shadekin = Shadekin

View File

@ -0,0 +1,8 @@
trait-clumsy-name = Clumsy
trait-clumsy-desc = You are a bit accident-prone
trait-highlightsensitivity-name = High Light Sensitivity
trait-highlightsensitivity-desc = You are much more sensitive to light than most shadekins
trait-extremelightsensitivity-name = Extreme Light Sensitivity
trait-extremelightsensitivity-desc = You are extremely more sensitive to light than most shadekins.

View File

@ -13,6 +13,25 @@ bounty-console-manifest-reward = Reward: ${$reward}
bounty-console-description-label = [color=gray]{$description}[/color]
bounty-console-id-label = ID#{$id}
## Begin DeltaV
bounty-console-claim-button-text = Claim
bounty-console-claimed-by-none = None
bounty-console-claimed-by-unknown = Unknown
bounty-console-claimed-by = Claimed by: {$claimant}
bounty-console-status-Undelivered = Undelivered
bounty-console-status-Waiting = Processing
bounty-console-status-OnShuttle = On Shuttle
bounty-console-status-formatted-Undelivered = [color=orange]Undelivered[/color]
bounty-console-status-formatted-Waiting = Processing
bounty-console-status-formatted-OnShuttle = [color=limegreen]On Shuttle[/color]
bounty-console-status-tooltip-Undelivered = This bounty has not yet been sent out for fulfilment
bounty-console-status-tooltip-Waiting = This bounty has been sent out, and is waiting to be fulfilled
bounty-console-status-tooltip-OnShuttle = This bounty is completed, ready to be delivered to the trade station
## End DeltaV
bounty-console-flavor-left = Bounties sourced from local unscrupulous dealers.
bounty-console-flavor-right = v1.4

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@
order:
- category: Health
- category: Stamina
- alertType: Shadeskin # Starlight
- alertType: SuitPower
- category: Internals
- alertType: Fire
@ -28,6 +29,7 @@
- alertType: Rooted
- alertType: Pacified
- alertType: Stealthy
- alertType: Offer # Floofstation - port from EE
- type: entity
id: AlertSpriteView

View File

@ -87,9 +87,9 @@
Kidneys: OrganVulpkaninKidneys
- type: HumanoidProfile
species: Vulpkanin
- type: Inventory
speciesId: vulpkanin
# Delta V - We got no displacement for Vulpkaning
# - type: Inventory
# speciesId: vulpkanin
# displacements:
# jumpsuit:
# sizeMaps:
@ -149,7 +149,7 @@
- type: Speech
speechSounds: Vulpkanin
speechVerb: Vulpkanin
allowedEmotes: [ 'Bark', 'Snarl', 'Whine', 'Howl', 'Growl' ]
allowedEmotes: [ 'Bark', 'Snarl', 'Whine', 'Howl', 'Growl', 'Yip' ] # DeltaV - Added Yip Emote
- type: Vocal
sounds:
Male: MaleVulpkanin

View File

@ -97,3 +97,4 @@
type: HumanoidMarkingModifierBoundUserInterface
enum.StrippingUiKey.Key:
type: StrippableBoundUserInterface
- type: OfferItem # Floofstation

View File

@ -6,6 +6,7 @@
PassengerIDCard: 5
ClothingHeadsetGrey: 5
ClothingHeadsetService: 5 # Delta-V
ClothingNeckNoviceMark: 5 # imp
RubberStampApproved: 1
RubberStampDenied: 1
BoxFolderPlasticClipboardEmpty: 2

View File

@ -31,6 +31,8 @@
ClothingHeadHelmetBasic: 2 # DeltaV - added helmets to the SecTech. Another line of defense between the tide and your grey matter.
BreachingCharge: 8 # DeltaV - added breaching charges
TicketPad: 5 # imp
DVEvidenceBag: 10 # DeltaV - evidence bags
DVBoxEvidenceBags: 2 # DeltaV - evidence bags
# security officers need to follow a diet regimen!
contrabandInventory:
WeaponMeleeNeedle: 2

View File

@ -41,7 +41,6 @@
ClothingOuterCoatStasec: 2 # DeltaV - replace ClothingOuterWinterSec
ClothingOuterCoatStasecCorpsman: 2 # DeltaV - added corpsman uniform to secdrobe
# ClothingOuterWinterSec: 2 # DeltaV - removed this
ClothingNeckCWPSec: 3 # DeltaV - add cold weather ponchos
## ClothingOuterArmorBasic: 2 # DeltaV - moved body armour from SecDrobe to SecTech
## ClothingOuterArmorBasicSlim: 2
ClothingNeckScarfStripedBlue: 3 # DeltaV - blue instead of red scarf. Was ClothingNeckScarfStripedRed

View File

@ -72,6 +72,7 @@
ClothingUniformJumpskirtWaitstaffFormal: 2
ClothingUniformJumpsuitWaitstaffFormal: 2
ClothingUniformJumpsuitWorkvest: 2
ClothingUniformJumpsuitFreighterCrew: 2
ClothingOuterDusterCoat: 2
ClothingOuterCoatNavalCoat: 2
ClothingNeckCloakWitchCloak: 2

View File

@ -136,7 +136,7 @@
sprite: Clothing/Neck/Cloaks/miner.rsi
- type: entity
parent: ClothingGenderCloakBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderCloakBase, BaseObviousCloak ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckCloakTrans
name: vampire cloak
description: Worn by high ranking vampires of the transylvanian society of vampires.
@ -145,6 +145,8 @@
sprite: Clothing/Neck/Cloaks/trans.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckCloakTrans # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-trans
- type: entity
parent: ClothingNeckBase
@ -214,7 +216,7 @@
proto: alien
- type: entity
parent: ClothingGenderCloakBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderCloakBase, BaseObviousCloak ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckCloakAce
name: pilot's cloak
description: Cloak awarded to Nanotrasen's finest space aces.
@ -223,9 +225,11 @@
sprite: Clothing/Neck/Cloaks/ace.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckCloakAce # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-ace
- type: entity
parent: ClothingGenderCloakBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderCloakBase, BaseObviousCloak ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckCloakAro
name: werewolf cloak
description: This cloak lets others know you're a lone wolf.
@ -234,9 +238,11 @@
sprite: Clothing/Neck/Cloaks/aro.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckCloakAro # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-aro
- type: entity
parent: ClothingGenderCloakBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderCloakBase, BaseObviousCloak ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckCloakAroace
name: aeropilot's cloak # thank you happyman442 this was the best name idea ever
description: Cloak awarded to Nanotrasen's finest pilots on planets with inhabitable atmospheres.
@ -245,9 +251,11 @@
sprite: Clothing/Neck/Cloaks/aroace.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckCloakAroace # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-aroace
- type: entity
parent: ClothingGenderCloakBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderCloakBase, BaseObviousCloak ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckCloakBi
name: poison cloak
description: The purple color is a clear indicator you are poisonous.
@ -256,9 +264,11 @@
sprite: Clothing/Neck/Cloaks/bi.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckCloakBi # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-bi
- type: entity
parent: ClothingGenderCloakBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderCloakBase, BaseObviousCloak ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckCloakIntersex
name: cyclops cloak
description: The circle on this cloak represents a cyclops' eye.
@ -267,9 +277,11 @@
sprite: Clothing/Neck/Cloaks/intersex.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckCloakIntersex # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-intersex
- type: entity
parent: ClothingGenderCloakBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderCloakBase, BaseObviousCloak ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckCloakLesbian
name: poet cloak
description: This cloak belonged to an ancient poet, you forgot which one.
@ -278,9 +290,11 @@
sprite: Clothing/Neck/Cloaks/les.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckCloakLesbian # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-lesbian
- type: entity
parent: ClothingGenderCloakBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderCloakBase, BaseObviousCloak ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckCloakGay
name: multi-level marketing cloak
description: This cloak is highly sought after in the Nanotrasen Marketing Offices.
@ -289,9 +303,11 @@
sprite: Clothing/Neck/Cloaks/gay.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckCloakGay # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-gay
- type: entity
parent: ClothingGenderCloakBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderCloakBase, BaseObviousCloak ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckCloakEnby
name: treasure hunter cloak
description: This cloak belonged to a greedy treasure hunter.
@ -300,9 +316,11 @@
sprite: Clothing/Neck/Cloaks/enby.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckCloakEnby # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-nonbinary
- type: entity
parent: ClothingGenderCloakBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderCloakBase, BaseObviousCloak ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckCloakPan
name: chef's cloak
description: Meant to be worn alongside a frying pan.
@ -311,4 +329,7 @@
sprite: Clothing/Neck/Cloaks/pan.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckCloakPan # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-pan

View File

@ -13,6 +13,11 @@
tags:
- Medal
- WhitelistChameleon
- type: WearerGetsExamineText # imp
thing: obvious-thing-medal
examineText: obvious-x-medal-nothing
pocketEvident: true
- type: entity
parent: ClothingNeckBase
@ -30,6 +35,10 @@
tags:
- Medal
- WhitelistChameleon
- type: WearerGetsExamineText # imp
thing: obvious-thing-medal
examineText: obvious-x-medal-nothing
pocketEvident: true
- type: entity
parent: ClothingNeckBase
@ -45,6 +54,10 @@
tags:
- Medal
- WhitelistChameleon
- type: WearerGetsExamineText # imp
thing: obvious-thing-medal
examineText: obvious-x-medal-nothing
pocketEvident: true
- type: entity
parent: ClothingNeckBase
@ -60,6 +73,10 @@
tags:
- Medal
- WhitelistChameleon
- type: WearerGetsExamineText # imp
thing: obvious-thing-medal
examineText: obvious-x-medal-nothing
pocketEvident: true
- type: entity
parent: ClothingNeckBase
@ -75,6 +92,10 @@
tags:
- Medal
- WhitelistChameleon
- type: WearerGetsExamineText # imp
thing: obvious-thing-medal
examineText: obvious-x-medal-nothing
pocketEvident: true
- type: entity
parent: ClothingNeckBase
@ -90,6 +111,10 @@
tags:
- Medal
- WhitelistChameleon
- type: WearerGetsExamineText # imp
thing: obvious-thing-medal
examineText: obvious-x-medal-nothing
pocketEvident: true
- type: entity
parent: ClothingNeckBase
@ -105,6 +130,10 @@
tags:
- Medal
- WhitelistChameleon
- type: WearerGetsExamineText # imp
thing: obvious-thing-medal
examineText: obvious-x-medal-nothing
pocketEvident: true
- type: entity
parent: ClothingNeckBase
@ -122,3 +151,8 @@
tags:
- Medal
- WhitelistChameleon
- type: WearerGetsExamineText # imp
thing: obvious-thing-medal
examineText: obvious-x-medal-nothing
pocketEvident: true

View File

@ -46,7 +46,7 @@
sprite: Clothing/Neck/Misc/bling.rsi
- type: entity
parent: ClothingNeckBase
parent: [ ClothingNeckBase, BaseObviousBadge ] # imp
id: ClothingNeckLawyerbadge
name: lawyer badge
description: A badge to show that the owner is a 'legitimate' lawyer who passed the NT bar exam required to practice law.
@ -57,6 +57,8 @@
sprite: Clothing/Neck/Misc/lawyerbadge.rsi
- type: TypingIndicatorClothing
proto: lawyer
- type: WearerGetsExamineText # imp
thingType: obvious-type-law
- type: entity
parent: ClothingNeckBase

View File

@ -11,6 +11,10 @@
sprite: Clothing/Neck/Misc/pins.rsi
- type: Clothing
sprite: Clothing/Neck/Misc/pins.rsi
- type: WearerGetsExamineText # imp
thing: obvious-thing-pin
examineText: obvious-desc-default
pocketEvident: true
- type: entity
parent: ClothingNeckPinBase
@ -46,6 +50,8 @@
equippedPrefix: lgbt
- type: ChameleonClothing
default: ClothingNeckLGBTPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride
- type: entity
parent: ClothingGenderPinBase
@ -59,6 +65,8 @@
equippedPrefix: ally
- type: ChameleonClothing
default: ClothingNeckAllyPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-straightally
- type: entity
parent: ClothingGenderPinBase
@ -72,6 +80,8 @@
equippedPrefix: aro
- type: ChameleonClothing
default: ClothingNeckAromanticPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-aro
- type: entity
parent: ClothingGenderPinBase
@ -85,6 +95,8 @@
equippedPrefix: aroace
- type: ChameleonClothing
default: ClothingNeckAroacePin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-aroace
- type: entity
parent: ClothingGenderPinBase
@ -98,6 +110,8 @@
equippedPrefix: asex
- type: ChameleonClothing
default: ClothingNeckAsexualPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-ace
- type: entity
parent: ClothingGenderPinBase
@ -111,6 +125,8 @@
equippedPrefix: bi
- type: ChameleonClothing
default: ClothingNeckBisexualPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-bi
- type: entity
parent: ClothingGenderPinBase
@ -124,6 +140,8 @@
equippedPrefix: gay
- type: ChameleonClothing
default: ClothingNeckGayPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-gay
- type: entity
parent: ClothingGenderPinBase
@ -137,6 +155,8 @@
equippedPrefix: inter
- type: ChameleonClothing
default: ClothingNeckIntersexPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-intersex
- type: entity
parent: ClothingGenderPinBase
@ -150,6 +170,8 @@
equippedPrefix: les
- type: ChameleonClothing
default: ClothingNeckLesbianPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-lesbian
- type: entity
parent: ClothingGenderPinBase
@ -163,6 +185,8 @@
equippedPrefix: non
- type: ChameleonClothing
default: ClothingNeckNonBinaryPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-nonbinary
- type: entity
parent: ClothingGenderPinBase
@ -176,6 +200,8 @@
equippedPrefix: pan
- type: ChameleonClothing
default: ClothingNeckPansexualPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-pan
- type: entity
parent: ClothingGenderPinBase
@ -202,6 +228,8 @@
equippedPrefix: omni
- type: ChameleonClothing
default: ClothingNeckOmnisexualPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-omni
- type: entity
parent: ClothingGenderPinBase
@ -215,6 +243,8 @@
equippedPrefix: gender
- type: ChameleonClothing
default: ClothingNeckGenderqueerPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-gq
- type: entity
parent: ClothingGenderPinBase
@ -241,6 +271,8 @@
equippedPrefix: trans
- type: ChameleonClothing
default: ClothingNeckTransPin
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-trans
- type: entity
parent: ClothingNeckPinBase
@ -252,6 +284,8 @@
state: autism
- type: Clothing
equippedPrefix: autism
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-aut
- type: entity
parent: ClothingNeckPinBase
@ -263,6 +297,8 @@
state: goldautism
- type: Clothing
equippedPrefix: goldautism
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-aut
- type: entity
parent: BaseItem

View File

@ -136,7 +136,7 @@
# Pride Scarves
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedAce
name: striped asexual scarf
description: A stylish striped asexual scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@ -147,9 +147,11 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/ace.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedAce # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-ace
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedAro
name: striped aromantic scarf
description: A stylish striped aromantic scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@ -160,9 +162,11 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/aro.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedAro # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-aro
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedAroace
name: striped aroace scarf
description: A stylish striped aroace scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@ -173,9 +177,11 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/aroace.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedAroace # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-aroace
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedBiSexual
name: striped bisexual scarf
description: A stylish striped bisexual scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@ -186,9 +192,11 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/bi.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedBiSexual # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-bi
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedGay
name: striped gay scarf
description: A stylish striped gay scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@ -199,9 +207,11 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/gay.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedGay # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-gay
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedInter
name: striped intersex scarf
description: A stylish striped intersex scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@ -212,9 +222,11 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/inter.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedInter # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-intersex
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedLesbian
name: striped lesbian scarf
description: A stylish striped lesbian scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@ -225,9 +237,11 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/lesbian.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedLesbian # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-lesbian
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedPan
name: striped pan scarf
description: A stylish striped pan scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@ -238,9 +252,11 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/pan.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedPan # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-pan
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedNonBinary
name: striped non-binary scarf
description: A stylish striped non-binary scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@ -251,9 +267,11 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/non.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedNonBinary # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-nonbinary
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedRainbow
name: rainbow scarf
description: A stylish rainbow scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@ -264,9 +282,11 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/rainbow.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedRainbow # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedTrans
name: striped trans scarf
description: A stylish striped trans scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
@ -277,9 +297,11 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/trans.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedTrans # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
thingType: obvious-type-pride-trans
- type: entity
parent: ClothingGenderScarfBase # DeltaV - Chameleon pride clothing
parent: [ ClothingGenderScarfBase, BaseObviousScarf ] # DeltaV - Chameleon pride clothing, porting imp's examine
id: ClothingNeckScarfStripedLesbianLong
name: long bacon
description: Long bacon! Perfect for sharing with your girlfriend!
@ -290,3 +312,6 @@
sprite: Clothing/Neck/Scarfs/PrideScarfs/lesbian-long.rsi
- type: ChameleonClothing # DeltaV - Chameleon pride clothing
default: ClothingNeckScarfStripedLesbianLong # DeltaV - Chameleon pride clothing
- type: WearerGetsExamineText # imp
examineText: obvious-x-scarf-lesbian-long

View File

@ -1,7 +1,7 @@
- type: marking
id: TattooHiveChest
bodyPart: Chest
groupWhitelist: [Human, Slime, Felinid, Oni, Kitsune, Avali] # DeltaV - SlimePerson-Avali
groupWhitelist: [Human, Slime, Felinid, Oni, Kitsune, Avali, Shadekin] # DeltaV - SlimePerson-Avali # Starlight - Shadekin
sexRestriction: [Male] # DeltaV: Splitting the scars and tattoos
coloring:
default:
@ -15,7 +15,7 @@
- type: marking
id: TattooNightlingChest
bodyPart: Chest
groupWhitelist: [Human, Slime, Felinid, Oni, Kitsune, Avali] # DeltaV - SlimePerson-Avali
groupWhitelist: [Human, Slime, Felinid, Oni, Kitsune, Avali, Shadekin] # DeltaV - SlimePerson-Avali # Starlight - Shadekin
sexRestriction: [Male] # DeltaV: Splitting the scars and tattoos
coloring:
default:
@ -23,13 +23,13 @@
!type:TattooColoring
fallbackColor: "#666666"
sprites:
- sprite: _DV/Mobs/Customization/tattoos.rsi # DeltaV: Splitting the scars and tattoos
- sprite: _DV/Mobs/Customization/tattoos.rsi # DeltaV: Splitting the scars and tattoos # Starlight - Shadekin
state: tattoo_nightling
- type: marking
id: TattooSilverburghLeftLeg
bodyPart: LLeg
groupWhitelist: [Human, Felinid, Oni, Kitsune] # DeltaV - Felinid-Kitsune
groupWhitelist: [Human, Felinid, Oni, Kitsune, Shadekin] # DeltaV - Felinid-Kitsune # Starlight - Shadekin
coloring:
default:
type:
@ -42,7 +42,7 @@
- type: marking
id: TattooSilverburghRightLeg
bodyPart: RLeg
groupWhitelist: [Human, Felinid, Oni, Kitsune] # DeltaV - Felinid-Kitsune
groupWhitelist: [Human, Felinid, Oni, Kitsune, Shadekin] # DeltaV - Felinid-Kitsune # Starlight - Shadekin
coloring:
default:
type:
@ -55,7 +55,7 @@
- type: marking
id: TattooCampbellLeftArm
bodyPart: LArm
groupWhitelist: [Human, Felinid, Oni, Kitsune] # DeltaV - Felinid-Kitsune
groupWhitelist: [Human, Felinid, Oni, Kitsune, Shadekin] # DeltaV - Felinid-Kitsune # Starlight - Shadekin
coloring:
default:
type:
@ -68,7 +68,7 @@
- type: marking
id: TattooCampbellRightArm
bodyPart: RArm
groupWhitelist: [Human, Felinid, Oni, Kitsune] # DeltaV - Felinid-Kitsune
groupWhitelist: [Human, Felinid, Oni, Kitsune, Shadekin] # DeltaV - Felinid-Kitsune # Starlight - Shadekin
coloring:
default:
type:
@ -81,7 +81,7 @@
- type: marking
id: TattooCampbellLeftLeg
bodyPart: LLeg
groupWhitelist: [Human, Felinid, Oni, Kitsune] # DeltaV - Felinid-Kitsune
groupWhitelist: [Human, Felinid, Oni, Kitsune, Shadekin] # DeltaV - Felinid-Kitsune # Starlight - Shadekin
coloring:
default:
type:
@ -94,7 +94,7 @@
- type: marking
id: TattooCampbellRightLeg
bodyPart: RLeg
groupWhitelist: [Human, Felinid, Oni, Kitsune] # DeltaV - Felinid-Kitsune
groupWhitelist: [Human, Felinid, Oni, Kitsune, Shadekin] # DeltaV - Felinid-Kitsune # Starlight - Shadekin
coloring:
default:
type:

View File

@ -1613,6 +1613,16 @@
Blunt: 1
clumsySound:
path: /Audio/Animals/monkey_scream.ogg
#Begin DeltaV additions
- type: Vocal
sounds:
Male: MonkeySounds
Female: MonkeySounds
Unsexed: MonkeySounds
wilhelmProbability: 0.01
- type: BodyEmotes
soundsId: MonkeySounds
#End DeltaV additions
- type: entity
@ -1647,6 +1657,16 @@
- type: GhostTakeoverAvailable
- type: Loadout
prototypes: [SyndicateOperativeGearMonkey]
#Begin DeltaV additions
- type: Vocal
sounds:
Male: MonkeySounds
Female: MonkeySounds
Unsexed: MonkeySounds
wilhelmProbability: 0.01
- type: BodyEmotes
soundsId: MonkeySounds
#End DeltaV additions
- type: entity
id: MobMonkeySyndicateAgent

Some files were not shown because too many files have changed in this diff Show More