mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-29 23:57:29 +00:00
Compare commits
125
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc2d05f548 | ||
|
|
4c7f99296c | ||
|
|
df1e97986a | ||
|
|
88203bb6b2 | ||
|
|
158a114804 | ||
|
|
d438aec056 | ||
|
|
3134e91fa5 | ||
|
|
3ac30b7b7b | ||
|
|
4daea082bc | ||
|
|
6a37e1268e | ||
|
|
331a25c7d3 | ||
|
|
b5121e10e9 | ||
|
|
9584a7b0ae | ||
|
|
f9880c148b | ||
|
|
25ef15f162 | ||
|
|
997a6e31e6 | ||
|
|
5a342570a4 | ||
|
|
97bb68b2a6 | ||
|
|
ac9f8852b3 | ||
|
|
8c8f4b4f2c | ||
|
|
8eeb5f6523 | ||
|
|
45432aa3bb | ||
|
|
b9153f0f13 | ||
|
|
e3d3fe72f0 | ||
|
|
0a65236363 | ||
|
|
afe6021455 | ||
|
|
e5db02c736 | ||
|
|
cbc5f819c4 | ||
|
|
339b3fb32c | ||
|
|
e96faec07d | ||
|
|
a1bbf142a4 | ||
|
|
e47a0bf455 | ||
|
|
72f3edd378 | ||
|
|
030deb6f27 | ||
|
|
4397017c5a | ||
|
|
0f19340d41 | ||
|
|
3c28ee8d20 | ||
|
|
11cc04dd87 | ||
|
|
f87ed28744 | ||
|
|
62c76332f5 | ||
|
|
34c212e4fb | ||
|
|
353290e55b | ||
|
|
0cbe9c01fb | ||
|
|
d88938887b | ||
|
|
1656a91c5e | ||
|
|
c70cbcb9f5 | ||
|
|
3e22d89d1e | ||
|
|
a790954f06 | ||
|
|
0676186fe5 | ||
|
|
db6d1e3970 | ||
|
|
fd20b74b8f | ||
|
|
d016838687 | ||
|
|
160aa17052 | ||
|
|
2b6729fa64 | ||
|
|
3439340530 | ||
|
|
28c2223a8a | ||
|
|
1b47c246c9 | ||
|
|
8141512cc4 | ||
|
|
fd986fec6e | ||
|
|
dd2a6e1c28 | ||
|
|
6c7c9de253 | ||
|
|
2b96bb79f8 | ||
|
|
c1239ef7bf | ||
|
|
62b6401e48 | ||
|
|
f79a599c65 | ||
|
|
9acd3f2ecb | ||
|
|
47215d7876 | ||
|
|
fe53298d27 | ||
|
|
1aee2519df | ||
|
|
e8cd052c31 | ||
|
|
a27fa291e7 | ||
|
|
7e6f66a651 | ||
|
|
08c27979a1 | ||
|
|
b49a94100c | ||
|
|
cfa24eb20c | ||
|
|
1787683435 | ||
|
|
389fbf99e1 | ||
|
|
74c7bf0a05 | ||
|
|
6764276a47 | ||
|
|
03934d3fbd | ||
|
|
468150c817 | ||
|
|
310ac0809b | ||
|
|
7b71611dc9 | ||
|
|
f2153d66c3 | ||
|
|
b6833cbbcf | ||
|
|
4e2bd0c244 | ||
|
|
bf0abd7ffa | ||
|
|
c4d65da8ed | ||
|
|
6dac24f03a | ||
|
|
9550a7d53d | ||
|
|
d826a15ff2 | ||
|
|
23a9a75703 | ||
|
|
79a0000547 | ||
|
|
71badde1a0 | ||
|
|
5ab49cd46b | ||
|
|
0398c527a1 | ||
|
|
cdafd4c257 | ||
|
|
8dc06f010a | ||
|
|
fee62b449f | ||
|
|
c0d20b3d79 | ||
|
|
b2944fba52 | ||
|
|
2e39916957 | ||
|
|
874bbf4fd2 | ||
|
|
e1ff1ea820 | ||
|
|
79a4a15403 | ||
|
|
0bb2b6ee7b | ||
|
|
18d9325b60 | ||
|
|
f07648bc24 | ||
|
|
f5da329b98 | ||
|
|
ad6581f2d3 | ||
|
|
430fef2a10 | ||
|
|
6970cb9d28 | ||
|
|
ea2b8df2cc | ||
|
|
b87b113736 | ||
|
|
f087b2895e | ||
|
|
0a14310574 | ||
|
|
d9c2f26438 | ||
|
|
a4f11c63dc | ||
|
|
4ff05277fa | ||
|
|
a33e12e85e | ||
|
|
62e9ebc216 | ||
|
|
25b69a3f9e | ||
|
|
7ed7dbcbb1 | ||
|
|
fcdf1cbbf4 | ||
|
|
9f786a7c1c |
@@ -269,7 +269,7 @@ Sentencing modifiers are to be applied by the sentencing officer, judge, or arbi
|
||||
| style="border: 1px solid black;" | [[File:SL_BreakingAndEntering.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Breaking and Entering}}Breaking and Entering
|
||||
| style="border: 1px solid black;" | 5 minutes
|
||||
| style="border: 1px solid black;" | To break and enter into a high security area where one is not authorised nor invited, with intent to commit a crime within.
|
||||
| style="border: 1px solid black;" | To break and enter into an area where one is not authorised nor invited.
|
||||
|-
|
||||
| style="border: 1px solid black;" | 203
|
||||
| style="border: 1px solid black;" | [[File:SL_Rioting.png]]
|
||||
|
||||
@@ -237,11 +237,12 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
}
|
||||
|
||||
// DeltaV - SSD Time START
|
||||
if (_entityManager.TryGetComponent<SSDIndicatorComponent>(entity, out var ssdIndicator) && ssdIndicator.IsSSD)
|
||||
if (_entityManager.TryGetComponent<SSDIndicatorComponent>(entity, out var ssdIndicator)
|
||||
&& ssdIndicator.SsdSince is {} ssdSince)
|
||||
{
|
||||
color = Color.MediumPurple;
|
||||
color.A = alpha;
|
||||
var ssdText = Loc.GetString("admin-overlay-ssd-time", ("time", (_timing.CurTime - ssdIndicator.SsdSince).ToString("%hh':'mm':'ss")));
|
||||
var ssdText = Loc.GetString("admin-overlay-ssd-time", ("time", (_timing.CurTime - ssdSince).ToString("%hh':'mm':'ss")));
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, ssdText, uiScale, color);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<CheckBox Name="ShowLoocAboveHeadCheckBox" Text="{Loc 'ui-options-show-looc-on-head'}" />
|
||||
<CheckBox Name="FancySpeechBubblesCheckBox" Text="{Loc 'ui-options-fancy-speech'}" />
|
||||
<CheckBox Name="FancyNameBackgroundsCheckBox" Text="{Loc 'ui-options-fancy-name-background'}" />
|
||||
<CheckBox Name="ChatFollowButton" Text="{Loc 'ui-options-chat-follow-button'}" />
|
||||
<Label Text="{Loc 'ui-options-general-cursor'}"
|
||||
StyleClasses="LabelKeyText"/>
|
||||
<CheckBox Name="ShowHeldItemCheckBox" Text="{Loc 'ui-options-show-held-item'}" />
|
||||
|
||||
@@ -63,6 +63,7 @@ public sealed partial class MiscTab : Control
|
||||
Control.AddOptionCheckBox(CCVars.ChatEnableFancyBubbles, FancySpeechBubblesCheckBox);
|
||||
Control.AddOptionCheckBox(CCVars.ChatFancyNameBackground, FancyNameBackgroundsCheckBox);
|
||||
Control.AddOptionCheckBox(CCVars.StaticStorageUI, StaticStorageUI);
|
||||
Control.AddOptionCheckBox(CCVars.InterfaceChatFollowButton, ChatFollowButton);
|
||||
|
||||
Control.Initialize();
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
using Content.Shared._DV.Psionics.Systems.PsionicPowers;
|
||||
|
||||
namespace Content.Client._DV.Psionics.Systems.PsionicPowers;
|
||||
|
||||
// This does nothing here. The code is all in the shared/server version.
|
||||
public sealed class FracturedFormPowerSystem : SharedFracturedFormPowerSystem;
|
||||
@@ -20,7 +20,11 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
|
||||
|
||||
private const string AnimateKey = "particle-animation";
|
||||
|
||||
private static readonly EntProtoId InteractionParticleId = "StellarInteractionParticle";
|
||||
private static readonly Dictionary<StellarInteractionParticleType, EntProtoId> InteractionParticleIds = new ()
|
||||
{
|
||||
{ StellarInteractionParticleType.Use, "StellarInteractionParticleUse" },
|
||||
{ StellarInteractionParticleType.Pull, "StellarInteractionParticlePull" },
|
||||
};
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -38,6 +42,11 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
|
||||
if (!Exists(performer) || !Exists(target))
|
||||
return;
|
||||
|
||||
if (ev.Type == StellarInteractionParticleType.Pull)
|
||||
{
|
||||
(performer, target) = (target, performer);
|
||||
}
|
||||
|
||||
var performerXform = Transform(performer);
|
||||
var targetXform = Transform(target);
|
||||
if (performerXform.MapID == MapId.Nullspace || targetXform.MapID == MapId.Nullspace)
|
||||
@@ -47,7 +56,7 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
|
||||
return;
|
||||
|
||||
var performerTargetDelta = targetXform.LocalPosition - performerXform.LocalPosition;
|
||||
var particle = Spawn(InteractionParticleId, performerXform.Coordinates);
|
||||
var particle = Spawn(InteractionParticleIds[ev.Type], performerXform.Coordinates);
|
||||
|
||||
if (used is { } usedEntity && Exists(usedEntity) && TryComp<SpriteComponent>(usedEntity, out var usedSprite))
|
||||
{
|
||||
@@ -58,10 +67,16 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
|
||||
}
|
||||
|
||||
var spriteColor = Comp<SpriteComponent>(particle).Color;
|
||||
_animation.Play(particle, GetAnimation(performerTargetDelta, spriteColor), AnimateKey);
|
||||
var animation = ev.Type switch
|
||||
{
|
||||
StellarInteractionParticleType.Use => GetUseAnimation(performerTargetDelta, spriteColor),
|
||||
StellarInteractionParticleType.Pull => GetPullAnimation(performerTargetDelta, spriteColor),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(ev), $"Interaction particle event has unknown particle type {ev.Type}"),
|
||||
};
|
||||
_animation.Play(particle, animation, AnimateKey);
|
||||
}
|
||||
|
||||
private Animation GetAnimation(Vector2 endOffset, Color color)
|
||||
private Animation GetUseAnimation(Vector2 endOffset, Color color)
|
||||
{
|
||||
var startRotation = _random.NextAngle(Angle.FromDegrees(-40), Angle.FromDegrees(40));
|
||||
var endRotation = Angle.Zero;
|
||||
@@ -126,4 +141,44 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private Animation GetPullAnimation(Vector2 endOffset, Color color)
|
||||
{
|
||||
var rotationLength = TimeSpan.FromMilliseconds(8f * (1000f / 12f));
|
||||
|
||||
var startOffset = new Vector2();
|
||||
var offsetLength = TimeSpan.FromMilliseconds(4f * (1000f / 12f));
|
||||
|
||||
var endColor = color.WithAlpha(0f);
|
||||
|
||||
return new Animation
|
||||
{
|
||||
Length = rotationLength,
|
||||
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty()
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Offset),
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(startOffset, 0f),
|
||||
new AnimationTrackProperty.KeyFrame(endOffset, (float)rotationLength.TotalSeconds, Easings.InOutCirc),
|
||||
},
|
||||
},
|
||||
new AnimationTrackComponentProperty()
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Color),
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(color, 0f),
|
||||
new AnimationTrackProperty.KeyFrame(color, (float)offsetLength.TotalSeconds),
|
||||
new AnimationTrackProperty.KeyFrame(endColor, (float)rotationLength.TotalSeconds, Easings.InOutCirc),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +245,7 @@ namespace Content.IntegrationTests.Tests
|
||||
"TimedDespawnDetailed", // DeltaV
|
||||
// makes an announcement on mapInit.
|
||||
"AnnounceOnSpawn",
|
||||
"ESTimedDespawn" // DeltaV
|
||||
};
|
||||
|
||||
Assert.That(server.CfgMan.GetCVar(CVars.NetPVS), Is.False);
|
||||
|
||||
@@ -262,6 +262,32 @@ public sealed partial class AdminVerbSystem
|
||||
Message = string.Join(": ", ntAgent, Loc.GetString("admin-verb-text-make-NTAgent")),
|
||||
};
|
||||
args.Verbs.Add(agent);
|
||||
|
||||
args.Verbs.Add(new ()
|
||||
{
|
||||
Text = Loc.GetString("admin-verb-make-HeadInsurgent.text"),
|
||||
Category = VerbCategory.Antag,
|
||||
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Misc/job_icons.rsi"), "HeadRevolutionary"),
|
||||
Act = () =>
|
||||
{
|
||||
_antag.ForceMakeAntag<NTAgentRuleComponent>(targetPlayer, "UprisingRule", "HeadInsurgent");
|
||||
},
|
||||
Impact = LogImpact.High,
|
||||
Message = Loc.GetString("admin-verb-make-HeadInsurgent.message"),
|
||||
});
|
||||
|
||||
args.Verbs.Add(new ()
|
||||
{
|
||||
Text = Loc.GetString("admin-verb-make-HeadLoyalist.text"),
|
||||
Category = VerbCategory.Antag,
|
||||
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Misc/job_icons.rsi"), "Nanotrasen"),
|
||||
Act = () =>
|
||||
{
|
||||
_antag.ForceMakeAntag<NTAgentRuleComponent>(targetPlayer, "UprisingRule", "HeadLoyalist");
|
||||
},
|
||||
Impact = LogImpact.High,
|
||||
Message = Loc.GetString("admin-verb-make-HeadLoyalist.message"),
|
||||
});
|
||||
// End DeltaV Additions
|
||||
// start DeltaV Additions - add hitman
|
||||
var hitmanName = Loc.GetString("admin-verb-make-hitman");
|
||||
|
||||
@@ -87,7 +87,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
|
||||
if (anomaly.Comp.CurrentBehavior is not null)
|
||||
RemoveBehavior(anomaly, anomaly.Comp.CurrentBehavior.Value);
|
||||
|
||||
EndAnomaly(anomaly, spawnCore: false);
|
||||
EndAnomaly(anomaly, spawnCore: false, forced: true);
|
||||
}
|
||||
|
||||
private void OnStartCollide(Entity<AnomalyComponent> anomaly, ref StartCollideEvent args)
|
||||
@@ -143,7 +143,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
|
||||
return 0;
|
||||
|
||||
var multiplier = 1f;
|
||||
if (component.Stability > component.GrowthThreshold)
|
||||
if (component.AlwaysGrow || component.Stability > component.GrowthThreshold) // DeltaV - Add AlwaysGrow
|
||||
multiplier = component.GrowingPointMultiplier; //more points for unstable
|
||||
|
||||
//penalty of up to 50% based on health
|
||||
@@ -253,7 +253,11 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
|
||||
else
|
||||
{
|
||||
string stateLoc;
|
||||
if (anomalyComp.Stability < anomalyComp.DecayThreshold)
|
||||
// DeltaV - Colossus Additions START
|
||||
if (anomalyComp.AlwaysGrow)
|
||||
stateLoc = Loc.GetString("anomaly-scanner-stability-high");
|
||||
// DeltaV - Colossus Additions END
|
||||
else if (anomalyComp.Stability < anomalyComp.DecayThreshold) // DeltaV - Add else
|
||||
stateLoc = Loc.GetString("anomaly-scanner-stability-low");
|
||||
else if (anomalyComp.Stability > anomalyComp.GrowthThreshold)
|
||||
stateLoc = Loc.GetString("anomaly-scanner-stability-high");
|
||||
@@ -345,6 +349,15 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
|
||||
msg.AddMarkupOrThrow(Loc.GetString("anomaly-behavior-unknown"));
|
||||
else
|
||||
{
|
||||
// DeltaV - Colossus Additions START
|
||||
if (anomalyComp.AlwaysGrow)
|
||||
{
|
||||
msg.AddMarkupOrThrow("- " + Loc.GetString("anomaly-behavior-always-grow"));
|
||||
if (anomalyComp.CurrentBehavior != null)
|
||||
msg.PushNewline();
|
||||
}
|
||||
// DeltaV - Colossus Additions END
|
||||
|
||||
if (anomalyComp.CurrentBehavior != null)
|
||||
{
|
||||
var behavior = _prototype.Index(anomalyComp.CurrentBehavior.Value);
|
||||
@@ -354,7 +367,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
|
||||
var mod = Math.Floor((behavior.EarnPointModifier) * 100);
|
||||
msg.AddMarkupOrThrow("- " + Loc.GetString("anomaly-behavior-point", ("mod", mod)));
|
||||
}
|
||||
else
|
||||
else if(!anomalyComp.AlwaysGrow) // DeltaV - Add condition, previously regular else
|
||||
{
|
||||
msg.AddMarkupOrThrow(Loc.GetString("anomaly-behavior-balanced"));
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ public sealed partial class AntagSelectionSystem
|
||||
/// </summary>
|
||||
public bool TryGetNextAvailableDefinition(Entity<AntagSelectionComponent> ent,
|
||||
[NotNullWhen(true)] out AntagSelectionDefinition? definition,
|
||||
int? players = null)
|
||||
int? players = null,
|
||||
string? preferredRole = null) // DeltaV - preferrec roles
|
||||
{
|
||||
definition = null;
|
||||
|
||||
@@ -39,6 +40,11 @@ public sealed partial class AntagSelectionSystem
|
||||
// It needs to track selected minds for each definition independently.
|
||||
foreach (var def in ent.Comp.Definitions)
|
||||
{
|
||||
// Begin DeltaV - preferred roles
|
||||
if (preferredRole is not null && !def.PrefRoles.Contains(preferredRole))
|
||||
continue;
|
||||
// End DeltaV - preferred roles
|
||||
|
||||
var target = GetTargetAntagCount(ent, null, def);
|
||||
|
||||
if (mindCount < target)
|
||||
@@ -336,11 +342,11 @@ public sealed partial class AntagSelectionSystem
|
||||
/// This technically is a gamerule-ent-less way to make an entity an antag.
|
||||
/// You should almost never be using this.
|
||||
/// </summary>
|
||||
public void ForceMakeAntag<T>(ICommonSession? player, string defaultRule) where T : Component
|
||||
public void ForceMakeAntag<T>(ICommonSession? player, string defaultRule, string? preferredRole = null) where T : Component // DeltaV - preferred antag roles
|
||||
{
|
||||
var rule = ForceGetGameRuleEnt<T>(defaultRule);
|
||||
|
||||
if (!TryGetNextAvailableDefinition(rule, out var def))
|
||||
if (!TryGetNextAvailableDefinition(rule, out var def, preferredRole: preferredRole)) // DeltaV - preferred antag roles
|
||||
def = rule.Comp.Definitions.Last();
|
||||
MakeAntag(rule, player, def.Value);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.Administration.Logs;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.Discord.DiscordLink;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Server.Players.RateLimiting;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Shared.Administration;
|
||||
@@ -15,6 +16,7 @@ using Content.Shared.Mind;
|
||||
using Content.Shared.Players; // DeltaV - OOC muting
|
||||
using Content.Shared.Players.RateLimiting;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Replays;
|
||||
@@ -47,6 +49,7 @@ internal sealed partial class ChatManager : IChatManager
|
||||
[Dependency] private readonly ISharedPlayerManager _player = default!;
|
||||
[Dependency] private readonly DiscordChatLink _discordLink = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
@@ -342,12 +345,35 @@ internal sealed partial class ChatManager : IChatManager
|
||||
|
||||
#region Utility
|
||||
|
||||
private bool IsValidWarpDestination(EntityUid source)
|
||||
{
|
||||
if (!source.Valid)
|
||||
return false;
|
||||
|
||||
if (!_entityManager.TryGetComponent(source, out TransformComponent? transform))
|
||||
return false;
|
||||
|
||||
return transform.MapID != MapId.Nullspace;
|
||||
}
|
||||
|
||||
public string PrependFollowButtonIfAppropriate(string wrappedMessage, EntityUid source, INetChannel recipient)
|
||||
{
|
||||
if (IsValidWarpDestination(source) && ShouldShowFollowButton(recipient))
|
||||
{
|
||||
var btnText = _localizationManager.GetString("chat-manager-follow-button");
|
||||
return $"[cmdlink=\"{btnText}\" command=\"{GhostFollowEntityCommand.CommandName} {_entityManager.GetNetEntity(source)}\" /] " + wrappedMessage;
|
||||
}
|
||||
|
||||
return wrappedMessage;
|
||||
}
|
||||
|
||||
public void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0, NetUserId? author = null)
|
||||
{
|
||||
var user = author == null ? null : EnsurePlayer(author);
|
||||
var netSource = _entityManager.GetNetEntity(source);
|
||||
user?.AddEntity(netSource);
|
||||
|
||||
wrappedMessage = PrependFollowButtonIfAppropriate(wrappedMessage, source, client);
|
||||
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
|
||||
_netManager.ServerSendMessage(new MsgChatMessage() { Message = msg }, client);
|
||||
|
||||
@@ -370,8 +396,12 @@ internal sealed partial class ChatManager : IChatManager
|
||||
var netSource = _entityManager.GetNetEntity(source);
|
||||
user?.AddEntity(netSource);
|
||||
|
||||
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
|
||||
_netManager.ServerSendToMany(new MsgChatMessage() { Message = msg }, clients);
|
||||
foreach (var client in clients)
|
||||
{
|
||||
var customWrapMessage = PrependFollowButtonIfAppropriate(wrappedMessage, source, client);
|
||||
var msg = new ChatMessage(channel, message, customWrapMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
|
||||
_netManager.ServerSendMessage(new MsgChatMessage { Message = msg }, client);
|
||||
}
|
||||
|
||||
if (!recordReplay)
|
||||
return;
|
||||
@@ -379,6 +409,7 @@ internal sealed partial class ChatManager : IChatManager
|
||||
if ((channel & ChatChannel.AdminRelated) == 0 ||
|
||||
_configurationManager.GetCVar(CCVars.ReplayRecordAdminChat))
|
||||
{
|
||||
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
|
||||
_replay.RecordServerMessage(msg);
|
||||
}
|
||||
}
|
||||
@@ -439,6 +470,22 @@ internal sealed partial class ChatManager : IChatManager
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private bool ShouldShowFollowButton(INetChannel recipient)
|
||||
{
|
||||
if (!_player.TryGetSessionByChannel(recipient, out var session))
|
||||
return false;
|
||||
|
||||
if (_entityManager.TrySystem(out GhostSystem? ghost))
|
||||
{
|
||||
if (!ghost.CanGhostWarp(session, out _))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return _netConfigManager.GetClientCVar(recipient, CCVars.InterfaceChatFollowButton);
|
||||
}
|
||||
}
|
||||
|
||||
public enum OOCChatType : byte
|
||||
|
||||
@@ -49,5 +49,7 @@ namespace Content.Server.Chat.Managers
|
||||
/// <param name="player">The player sending a chat message.</param>
|
||||
/// <returns>False if the player has violated rate limits and should be blocked from sending further messages.</returns>
|
||||
RateLimitStatus HandleRateLimit(ICommonSession player);
|
||||
|
||||
string PrependFollowButtonIfAppropriate(string wrappedMessage, EntityUid source, INetChannel recipient);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,11 +382,6 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
||||
|
||||
var visualEnt = CreateExplosionVisualEntity(pos, queued.Proto.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity);
|
||||
|
||||
// camera shake
|
||||
// ES START
|
||||
// CameraShake(iterationIntensity.Count * 4f, pos, queued.TotalIntensity);
|
||||
// ES END
|
||||
|
||||
//For whatever bloody reason, sound system requires ENTITY coordinates.
|
||||
var mapEntityCoords = _transformSystem.ToCoordinates(_map.GetMap(pos.MapId), pos);
|
||||
|
||||
@@ -416,12 +411,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
||||
? queued.Proto.SmallSoundFar
|
||||
: queued.Proto.SoundFar;
|
||||
|
||||
// ES START
|
||||
var farTranslationShake = iterationIntensity.Count < queued.Proto.SmallSoundIterationThreshold
|
||||
? new ESScreenshakeParameters() { Trauma = 0.4f, DecayRate = 0.2f, Frequency = 0.014f }
|
||||
: new ESScreenshakeParameters() { Trauma = 0.6f, DecayRate = 0.05f, Frequency = 0.014f };
|
||||
_shake.Screenshake(filter, farTranslationShake, null);
|
||||
// ES END
|
||||
CameraShake(iterationIntensity, pos, queued); // Starlight
|
||||
|
||||
_audio.PlayGlobal(farSound, farFilter, true, farSound.Params);
|
||||
|
||||
@@ -444,8 +434,13 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
||||
_damageableSystem);
|
||||
}
|
||||
|
||||
private void CameraShake(float range, MapCoordinates epicenter, float totalIntensity)
|
||||
private void CameraShake(List<float> rangeList, MapCoordinates epicenter, QueuedExplosion queued) // Starlight - replace range with rangeList, totalIntensity with queued
|
||||
{
|
||||
// Starlight BEGIN
|
||||
var range = rangeList.Count * 4f;
|
||||
var totalIntensity = queued.TotalIntensity * 10f;
|
||||
// Starlight END
|
||||
|
||||
var players = Filter.Empty();
|
||||
players.AddInRange(epicenter, range, _playerManager, EntityManager);
|
||||
|
||||
@@ -463,7 +458,40 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
||||
var distance = delta.Length();
|
||||
var effect = 5 * MathF.Pow(totalIntensity, 0.5f) * (1 - distance / range);
|
||||
if (effect > 0.01f)
|
||||
_recoilSystem.KickCamera(uid, -delta.Normalized() * effect);
|
||||
{
|
||||
// DeltaV - Camera kick falloff START
|
||||
// _recoilSystem.KickCamera(uid, -delta.Normalized() * effect);
|
||||
|
||||
// Exponential decay: N(t) = N₀ ⋅ e^(-λ ⋅ t)
|
||||
// In this case, N = effect and we aren't decaying over time but with increasing distance from the epicenter,
|
||||
// so t = distance / range. Higher values of λ lead to faster decay, lower values to slower decay.
|
||||
//
|
||||
// severity is a fixed factor used to decrease the overall severity of the camera kick,
|
||||
// as the values were so high by default that decay was only becoming really noticeable near range.
|
||||
//
|
||||
// these changes are made here instead of directly assigning to the effect variable
|
||||
// above to maintain the same overall effect range as before.
|
||||
const float severity = 0.033f;
|
||||
const float lambda = 4f;
|
||||
_recoilSystem.KickCamera(uid, -delta.Normalized() * effect * severity * MathF.Exp(-lambda * (distance / range)));
|
||||
// DeltaV END
|
||||
|
||||
// Starlight START
|
||||
var shakeParams = rangeList.Count < queued.Proto.SmallSoundIterationThreshold
|
||||
? new ESScreenshakeParameters() { Trauma = 0.4f, DecayRate = 0.2f, Frequency = 0.014f }
|
||||
: new ESScreenshakeParameters() { Trauma = 0.6f, DecayRate = 0.05f, Frequency = 0.014f };
|
||||
|
||||
// DeltaV - Screenshake falloff START
|
||||
// Linear falloff with increasing distance from epicenter,
|
||||
// capped at certain values to avoid diminishing the effect completely near range.
|
||||
shakeParams.DecayRate *= MathF.Min(1 + distance / range, 1.75f);
|
||||
shakeParams.Frequency *= MathF.Max(1 - distance / range, 0.33f);
|
||||
shakeParams.Trauma *= MathF.Max(1 - distance / range, 0.33f);
|
||||
// DeltaV END
|
||||
|
||||
_shake.Screenshake(players, shakeParams, null);
|
||||
// Starlight END
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using Content.Shared.Database;
|
||||
using Content.Shared.DeviceNetwork;
|
||||
using Content.Shared.DeviceNetwork.Components;
|
||||
using Content.Shared.DeviceNetwork.Events;
|
||||
using Content.Shared.DeviceNetwork.Systems; // DeltaV - map init ordering
|
||||
using Content.Shared.Emag.Systems;
|
||||
using Content.Shared.Fax;
|
||||
using Content.Shared.Fax.Components;
|
||||
@@ -64,7 +65,7 @@ public sealed class FaxSystem : EntitySystem
|
||||
|
||||
// Hooks
|
||||
SubscribeLocalEvent<FaxMachineComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<FaxMachineComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<FaxMachineComponent, MapInitEvent>(OnMapInit, after: [typeof(SharedDeviceNetworkSystem)]); // DeltaV - map init order, we need address assigned first
|
||||
SubscribeLocalEvent<FaxMachineComponent, ComponentRemove>(OnComponentRemove);
|
||||
|
||||
SubscribeLocalEvent<FaxMachineComponent, EntInsertedIntoContainerMessage>(OnItemSlotChanged);
|
||||
@@ -301,6 +302,11 @@ public sealed class FaxSystem : EntitySystem
|
||||
if (!args.Data.TryGetValue(FaxConstants.FaxPaperNameData, out string? name) ||
|
||||
!args.Data.TryGetValue(FaxConstants.FaxPaperContentData, out string? content))
|
||||
return;
|
||||
// Begin DeltaV - we removed the power requirement from device network but we still don't want
|
||||
// unpowered faxes to happen
|
||||
if (TryComp<ApcPowerReceiverComponent>(uid, out var receiver) && !receiver.Powered)
|
||||
return;
|
||||
// End DeltaV
|
||||
|
||||
args.Data.TryGetValue(FaxConstants.FaxPaperLabelData, out string? label);
|
||||
args.Data.TryGetValue(FaxConstants.FaxPaperStampStateData, out string? stampState);
|
||||
|
||||
@@ -35,17 +35,17 @@ public sealed partial class PuddleSystem
|
||||
if (!entity.Comp.SpillWhenThrown || Openable.IsClosed(entity.Owner))
|
||||
return;
|
||||
|
||||
// DeltaV - Beer Goggles Safe Throw
|
||||
if ( args.User is { } user && _safeSolutionThrower.GetSafeThrow(user))
|
||||
{
|
||||
_physics.SetAngularVelocity(entity, 0);
|
||||
Transform(entity).LocalRotation = Angle.Zero;
|
||||
return;
|
||||
}
|
||||
// END DeltaV
|
||||
|
||||
if (TrySplashSpillAt(entity.Owner, Transform(entity).Coordinates, out _, out var solution) && args.User != null)
|
||||
{
|
||||
// DeltaV - Beer Goggles Safe Throw
|
||||
if (_safeSolutionThrower.GetSafeThrow(args.User.Value))
|
||||
{
|
||||
_physics.SetAngularVelocity(entity, 0);
|
||||
Transform(entity).LocalRotation = Angle.Zero;
|
||||
return;
|
||||
}
|
||||
// END DeltaV
|
||||
|
||||
AdminLogger.Add(LogType.Landed,
|
||||
$"{ToPrettyString(entity.Owner):entity} spilled a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution} on landing");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Ghost;
|
||||
|
||||
[AnyCommand]
|
||||
internal sealed partial class GhostFollowEntityCommand : LocalizedEntityCommands
|
||||
{
|
||||
public const string CommandName = "ghost_follow_entity";
|
||||
|
||||
[Dependency] private GhostSystem _ghost = null!;
|
||||
|
||||
public override string Command => CommandName;
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1 || shell.Player is not { } player)
|
||||
return;
|
||||
|
||||
var target = args[0];
|
||||
if (!NetEntity.TryParse(target, out var targetEnt))
|
||||
return;
|
||||
|
||||
_ghost.GhostWarpRequest(player, targetEnt);
|
||||
}
|
||||
}
|
||||
@@ -299,10 +299,22 @@ namespace Content.Server.Ghost
|
||||
|
||||
#region Warp
|
||||
|
||||
public bool CanGhostWarp(ICommonSession session, out EntityUid entity)
|
||||
{
|
||||
if (session.AttachedEntity is not { Valid: true } sessionEntity
|
||||
|| !_ghostQuery.HasComp(sessionEntity))
|
||||
{
|
||||
entity = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
entity = sessionEntity;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnGhostWarpsRequest(GhostWarpsRequestEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity is not {Valid: true} entity
|
||||
|| !_ghostQuery.HasComp(entity))
|
||||
if (!CanGhostWarp(args.SenderSession, out var entity))
|
||||
{
|
||||
Log.Warning($"User {args.SenderSession.Name} sent a {nameof(GhostWarpsRequestEvent)} without being a ghost.");
|
||||
return;
|
||||
@@ -312,30 +324,33 @@ namespace Content.Server.Ghost
|
||||
RaiseNetworkEvent(response, args.SenderSession.Channel);
|
||||
}
|
||||
|
||||
public void GhostWarpRequest(ICommonSession player, NetEntity target)
|
||||
{
|
||||
if (!CanGhostWarp(player, out var attached))
|
||||
{
|
||||
Log.Warning($"User {player.Name} tried to warp to {target} without being a ghost.");
|
||||
return;
|
||||
}
|
||||
|
||||
var realTarget = GetEntity(target);
|
||||
|
||||
if (!Exists(realTarget))
|
||||
{
|
||||
Log.Warning($"User {player.Name} tried to warp to an invalid entity id: {target}");
|
||||
return;
|
||||
}
|
||||
|
||||
WarpTo(attached, realTarget);
|
||||
}
|
||||
|
||||
private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity is not {Valid: true} attached
|
||||
|| !_ghostQuery.HasComp(attached))
|
||||
{
|
||||
Log.Warning($"User {args.SenderSession.Name} tried to warp to {msg.Target} without being a ghost.");
|
||||
return;
|
||||
}
|
||||
|
||||
var target = GetEntity(msg.Target);
|
||||
|
||||
if (!Exists(target))
|
||||
{
|
||||
Log.Warning($"User {args.SenderSession.Name} tried to warp to an invalid entity id: {msg.Target}");
|
||||
return;
|
||||
}
|
||||
|
||||
WarpTo(attached, target);
|
||||
GhostWarpRequest(args.SenderSession, msg.Target);
|
||||
}
|
||||
|
||||
private void OnGhostnadoRequest(GhostnadoRequestEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity is not {} uid
|
||||
|| !_ghostQuery.HasComp(uid))
|
||||
if (CanGhostWarp(args.SenderSession, out var uid))
|
||||
{
|
||||
Log.Warning($"User {args.SenderSession.Name} tried to ghostnado without being a ghost.");
|
||||
return;
|
||||
|
||||
@@ -211,7 +211,7 @@ namespace Content.Server.Medical.BiomassReclaimer
|
||||
_solution.ResolveSolution(toProcess, stream.BloodSolutionName, ref stream.BloodSolution, out var solution))
|
||||
{
|
||||
component.BloodReagents = solution.Clone();
|
||||
component.BloodReagents.ScaleSolution(50 / component.BloodReagents.Volume);
|
||||
//component.BloodReagents.ScaleSolution(50 / component.BloodReagents.Volume); // Delta V - This doesn't need to be here. It just always makes the solution ~50u but also might divide by 0. Just use the current blood level so more blood = more mess.
|
||||
}
|
||||
if (TryComp<ButcherableComponent>(toProcess, out var butcherableComponent))
|
||||
{
|
||||
|
||||
@@ -475,7 +475,7 @@ public sealed class NukeSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Force a nuclear bomb to start a countdown timer
|
||||
/// </summary>
|
||||
public void ArmBomb(EntityUid uid, NukeComponent? component = null)
|
||||
public void ArmBomb(EntityUid uid, NukeComponent? component = null, string? announcementId = null) // DeltaV - custom announcements
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
@@ -500,7 +500,7 @@ public sealed class NukeSystem : EntitySystem
|
||||
_selectedNukeSong = _audio.ResolveSound(component.ArmMusic);
|
||||
|
||||
// warn a crew
|
||||
var announcement = Loc.GetString("nuke-component-announcement-armed",
|
||||
var announcement = Loc.GetString(announcementId ?? "nuke-component-announcement-armed", // DeltaV - custom announcements
|
||||
("time", (int) component.RemainingTime),
|
||||
("location", FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString((uid, nukeXform)))));
|
||||
var sender = Loc.GetString("nuke-component-announcement-sender");
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared._DV.Chat;
|
||||
using Content.Shared.Chat;
|
||||
@@ -28,6 +30,8 @@ public sealed class RadioSystem : EntitySystem
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly GhostSystem _ghost = default!;
|
||||
|
||||
// set used to prevent radio feedback loops.
|
||||
private readonly HashSet<string> _messages = new();
|
||||
@@ -55,8 +59,25 @@ public sealed class RadioSystem : EntitySystem
|
||||
|
||||
private void OnIntrinsicReceive(EntityUid uid, IntrinsicRadioReceiverComponent component, ref RadioReceiveEvent args)
|
||||
{
|
||||
if (TryComp(uid, out ActorComponent? actor))
|
||||
_netMan.ServerSendMessage(args.ChatMsg, actor.PlayerSession.Channel);
|
||||
if (!TryComp(uid, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
var msg = args.ChatMsg;
|
||||
if (_ghost.CanGhostWarp(actor.PlayerSession, out _))
|
||||
{
|
||||
msg = new MsgChatMessage
|
||||
{
|
||||
Message = new ChatMessage(args.ChatMsg.Message)
|
||||
{
|
||||
WrappedMessage = _chatManager.PrependFollowButtonIfAppropriate(
|
||||
args.ChatMsg.Message.WrappedMessage,
|
||||
args.MessageSource,
|
||||
actor.PlayerSession.Channel),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_netMan.ServerSendMessage(msg, actor.PlayerSession.Channel);
|
||||
}
|
||||
|
||||
// DeltaV
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Linq; // DeltaV
|
||||
using System.Numerics;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Chat.Systems;
|
||||
@@ -8,6 +9,8 @@ using Content.Server.Popups;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Dataset;
|
||||
using Content.Shared.Mobs; // DeltaV
|
||||
using Content.Shared.Mobs.Components; // DeltaV
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Content.Shared.Pointing;
|
||||
@@ -47,14 +50,33 @@ namespace Content.Server.RatKing
|
||||
if (!TryComp<HungerComponent>(uid, out var hunger))
|
||||
return;
|
||||
|
||||
// DeltaV - modify cost of Raise Army based on the amount of alive servants
|
||||
// Check on how many servants are alive to calculate the cost of a new servant
|
||||
var aliveServants = component.Servants.Count(servant
|
||||
=> TryComp<MobStateComponent>(servant, out var mobState) && mobState.CurrentState == MobState.Alive);
|
||||
|
||||
// calculate the cost multiplier
|
||||
var multiplier = aliveServants switch
|
||||
{
|
||||
< 5 => 1.0f, // 1-5 servants: 10 hunger
|
||||
< 10 => 1.5f, // 6-10 servants: 15 hunger
|
||||
< 15 => 2.5f, // 11-15 servants: 25 hunger
|
||||
< 20 => 5.0f, // 16-20 servants: 50 hunger
|
||||
< 25 => 10.0f, // 21-25 servants: 100 hunger
|
||||
_ => 15.0f, // Above 25 servants: 150 hunger
|
||||
};
|
||||
var hungerPerArmyUseAdjusted = component.HungerPerArmyUse * multiplier;
|
||||
|
||||
//make sure the hunger doesn't go into the negatives
|
||||
if (_hunger.GetHunger(hunger) < component.HungerPerArmyUse)
|
||||
if (_hunger.GetHunger(hunger) < hungerPerArmyUseAdjusted)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("rat-king-too-hungry"), uid, uid);
|
||||
return;
|
||||
}
|
||||
args.Handled = true;
|
||||
_hunger.ModifyHunger(uid, -component.HungerPerArmyUse, hunger);
|
||||
_hunger.ModifyHunger(uid, - hungerPerArmyUseAdjusted, hunger);
|
||||
// DeltaV - end to the modified cost of Raise Army
|
||||
|
||||
var servant = Spawn(component.ArmyMobSpawnId, Transform(uid).Coordinates);
|
||||
var comp = EnsureComp<RatKingServantComponent>(servant);
|
||||
comp.King = uid;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
using Content.Server.Revolutionary.Components;
|
||||
using Content.Shared._DV.Uprising;
|
||||
using Content.Shared.Cuffs.Components;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Station;
|
||||
|
||||
namespace Content.Server.Uprising;
|
||||
|
||||
public sealed class CommandStatusObjectiveSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedRoleSystem _role = default!;
|
||||
[Dependency] private readonly SharedStationSystem _station = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CommandStatusObjectiveComponent, ObjectiveGetProgressEvent>(OnGetProgress);
|
||||
}
|
||||
|
||||
private bool CheckIncapacitated(EntityUid uid)
|
||||
{
|
||||
if (TryComp<CuffableComponent>(uid, out var cuffed) && cuffed.CuffedHandCount > 0)
|
||||
return true;
|
||||
|
||||
if (!TryComp<MobStateComponent>(uid, out var state))
|
||||
return true;
|
||||
|
||||
if (state.CurrentState is MobState.Dead or MobState.Invalid)
|
||||
return true;
|
||||
|
||||
if (_station.GetOwningStation(uid) == null)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnGetProgress(Entity<CommandStatusObjectiveComponent> ent, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
var total = 0;
|
||||
var counted = 0;
|
||||
|
||||
var query = AllEntityQuery<MindContainerComponent, CommandStaffComponent>();
|
||||
while (query.MoveNext(out var uid, out var mind, out _))
|
||||
{
|
||||
if (mind.Mind is not { } mindUid)
|
||||
continue;
|
||||
|
||||
total++;
|
||||
|
||||
var converted = false;
|
||||
foreach (var role in ent.Comp.ConvertedRoles)
|
||||
{
|
||||
if (!EntityManager.ComponentFactory.TryGetRegistration(role, out var roleReg))
|
||||
{
|
||||
Log.Error($"Role component not found for CrewConversionObjective: {role}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_role.MindHasRole(mindUid, roleReg.Type, out _))
|
||||
continue;
|
||||
|
||||
converted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var incapacitated = CheckIncapacitated(uid);
|
||||
|
||||
if (converted && !incapacitated || !converted && ent.Comp.ShouldUnconvertedBeIncapacitated && incapacitated)
|
||||
{
|
||||
counted++;
|
||||
}
|
||||
}
|
||||
|
||||
if (total == 0)
|
||||
return;
|
||||
|
||||
args.Progress = (float)counted / (float)total;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using Content.Server._DV.Psionics.Systems;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Body.Components;
|
||||
@@ -14,7 +13,6 @@ using Content.Server.NPC.HTN;
|
||||
using Content.Server.NPC.Systems;
|
||||
using Content.Server.StationEvents.Components;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared._DV.Psionics.Components; // DeltaV
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.CombatMode.Pacification;
|
||||
@@ -27,7 +25,6 @@ using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Movement.Pulling.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.NameModifier.EntitySystems;
|
||||
using Content.Shared.NPC.Components; // DeltaV
|
||||
using Content.Shared.NPC.Systems;
|
||||
using Content.Shared.Nutrition.AnimalHusbandry;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
@@ -72,7 +69,6 @@ public sealed partial class ZombieSystem
|
||||
[Dependency] private readonly NPCSystem _npc = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
[Dependency] private readonly ISharedPlayerManager _player = default!;
|
||||
[Dependency] private readonly PsionicSystem _psionic = default!; // DeltaV
|
||||
|
||||
private static readonly ProtoId<TagPrototype> InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell";
|
||||
private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide";
|
||||
@@ -134,6 +130,9 @@ public sealed partial class ZombieSystem
|
||||
//you're a real zombie now, son.
|
||||
var zombiecomp = AddComp<ZombieComponent>(target);
|
||||
|
||||
// DeltaV - Save factions, psionics, etc. before modifications are made.
|
||||
PreserveEntityComponentState((target, zombiecomp));
|
||||
|
||||
//we need to basically remove all of these because zombies shouldn't
|
||||
//get diseases, breath, be thirst, be hungry, die in space, get double sentience, have offspring or be paraplegic.
|
||||
RemComp<RespiratorComponent>(target);
|
||||
@@ -146,12 +145,6 @@ public sealed partial class ZombieSystem
|
||||
RemComp<ComplexInteractionComponent>(target);
|
||||
RemComp<SentienceTargetComponent>(target);
|
||||
|
||||
// DeltaV Start - Prevent Psionic Zombies
|
||||
RemComp<PotentialPsionicComponent>(target);
|
||||
if (HasComp<PsionicComponent>(target))
|
||||
_psionic.MindBreakEntity(target, false, true);
|
||||
// DeltaV End - Prevent Psionic Zombies
|
||||
|
||||
//funny voice
|
||||
var accentType = "zombie";
|
||||
if (TryComp<ZombieAccentOverrideComponent>(target, out var accent))
|
||||
@@ -245,6 +238,9 @@ public sealed partial class ZombieSystem
|
||||
//Should prevent instances of zombies using comms for information they shouldnt be able to have.
|
||||
_inventory.TryUnequip(target, "ears", true, true);
|
||||
|
||||
// DeltaV - Extra zombification removals, etc.
|
||||
ZombifyEntityDV((target, zombiecomp), mobState);
|
||||
|
||||
//popup
|
||||
_popup.PopupEntity(Loc.GetString("zombie-transform", ("target", target)), target, PopupType.LargeCaution);
|
||||
|
||||
@@ -261,7 +257,6 @@ public sealed partial class ZombieSystem
|
||||
|
||||
_faction.ClearFactions(target, dirty: false);
|
||||
_faction.AddFaction(target, ZombieFaction);
|
||||
EnsureComp<NoFriendlyFireComponent>(target); // DeltaV - prevent shitters biting other zombies
|
||||
|
||||
//gives it the funny "Zombie ___" name.
|
||||
_nameMod.RefreshNameModifiers(target);
|
||||
|
||||
@@ -78,6 +78,8 @@ namespace Content.Server.Zombies
|
||||
SubscribeLocalEvent<IncurableZombieComponent, MapInitEvent>(OnPendingMapInit);
|
||||
|
||||
SubscribeLocalEvent<ZombifyOnDeathComponent, MobStateChangedEvent>(OnDamageChanged);
|
||||
|
||||
InitializeDV(); // DeltaV - Additional Subscriptions for ZombieComponent
|
||||
}
|
||||
|
||||
private void OnBeforeRemoveAnomalyOnDeath(Entity<PendingZombieComponent> ent, ref BeforeRemoveAnomalyOnDeathEvent args)
|
||||
@@ -325,7 +327,7 @@ namespace Content.Server.Zombies
|
||||
// Remove the role when getting cloned, getting gibbed and borged, or leaving the body via any other method.
|
||||
private void OnMindRemoved(Entity<ZombieComponent> ent, ref MindRemovedMessage args)
|
||||
{
|
||||
_role.MindRemoveRole<ZombieRoleComponent>((args.Mind.Owner, args.Mind.Comp));
|
||||
_role.MindRemoveRole<ZombieRoleComponent>((args.Mind.Owner, args.Mind.Comp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
using Content.Server._DV.CosmicCult.Components;
|
||||
using Content.Shared._DV.CosmicCult.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._DV.CosmicCult.Abilities.Colossus;
|
||||
|
||||
public sealed class CosmicColossusBuffsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DamageableSystem _damage = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MultiplyAttackRateOnSupercriticalComponent, CosmicColossusEffigySupercriticalEvent>(HandleAttackRate);
|
||||
SubscribeLocalEvent<FlatAttackBonusOnSupercriticalComponent, CosmicColossusEffigySupercriticalEvent>(HandleDamageBonus);
|
||||
SubscribeLocalEvent<MultiplyCorruptingSpeedOnSupercriticalComponent, CosmicColossusEffigySupercriticalEvent>(HandleCorruptingSpeed);
|
||||
SubscribeLocalEvent<HealOnSupercriticalComponent, CosmicColossusEffigySupercriticalEvent>(HandleHeal);
|
||||
}
|
||||
|
||||
private void HandleAttackRate(Entity<MultiplyAttackRateOnSupercriticalComponent> ent,
|
||||
ref CosmicColossusEffigySupercriticalEvent args)
|
||||
{
|
||||
if (TryComp<MeleeWeaponComponent>(ent, out var weapon))
|
||||
{
|
||||
weapon.AttackRate *= ent.Comp.Multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDamageBonus(Entity<FlatAttackBonusOnSupercriticalComponent> ent,
|
||||
ref CosmicColossusEffigySupercriticalEvent args)
|
||||
{
|
||||
if (!TryComp<CosmicColossusComponent>(ent, out var colossusComp))
|
||||
return;
|
||||
|
||||
colossusComp.BonusDamage +=
|
||||
new DamageSpecifier(_proto.Index(ent.Comp.BonusDamageType), ent.Comp.BonusDamage);
|
||||
}
|
||||
|
||||
private void HandleCorruptingSpeed(Entity<MultiplyCorruptingSpeedOnSupercriticalComponent> ent,
|
||||
ref CosmicColossusEffigySupercriticalEvent args)
|
||||
{
|
||||
if (TryComp<CosmicCorruptingComponent>(ent, out var corrupting))
|
||||
{
|
||||
corrupting.CorruptionSpeed *= ent.Comp.Multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleHeal(Entity<HealOnSupercriticalComponent> ent, ref CosmicColossusEffigySupercriticalEvent args)
|
||||
{
|
||||
if (TryComp<DamageableComponent>(ent, out var damageable))
|
||||
{
|
||||
_damage.TryChangeDamage(ent.Owner,
|
||||
-damageable.Damage * Math.Abs(ent.Comp.DamageFractionHealed), // just in case someone sets a negative value in YML
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
using Content.Server._DV.CosmicCult.Components;
|
||||
|
||||
namespace Content.Server._DV.CosmicCult.Abilities.Colossus;
|
||||
|
||||
[ByRefEvent]
|
||||
public readonly record struct CosmicColossusEffigySupercriticalEvent(
|
||||
Entity<CosmicEffigyComponent> Effigy
|
||||
);
|
||||
@@ -1,15 +1,24 @@
|
||||
using System.Numerics;
|
||||
using Content.Server._DV.CosmicCult.Abilities.Colossus;
|
||||
using Content.Server._DV.CosmicCult.Components;
|
||||
using Content.Server.Actions;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared._DV.CosmicCult;
|
||||
using Content.Shared._DV.CosmicCult.Components;
|
||||
using Content.Shared.Anomaly.Components;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Warps;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server._DV.CosmicCult.Abilities;
|
||||
|
||||
@@ -23,12 +32,93 @@ public sealed class CosmicEffigySystem : EntitySystem
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly AudioSystem _audio = default!;
|
||||
[Dependency] private readonly CosmicCultObjectiveSystem _cultObjective = default!;
|
||||
[Dependency] private readonly IGameTiming _time = default!;
|
||||
[Dependency] private readonly IChatManager _chat = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CosmicColossusComponent, EventCosmicColossusEffigy>(OnColossusEffigy);
|
||||
SubscribeLocalEvent<CosmicEffigyComponent, AnomalySupercriticalEvent>(OnSupercritical);
|
||||
SubscribeLocalEvent<CosmicEffigyComponent, AnomalyShutdownEvent>(OnAnomShutdown);
|
||||
}
|
||||
|
||||
private void OnAnomShutdown(Entity<CosmicEffigyComponent> ent, ref AnomalyShutdownEvent args)
|
||||
{
|
||||
if (args.Forced || args.Supercritical || !Exists(ent.Comp.Colossus) || !TryComp<CosmicColossusComponent>(ent.Comp.Colossus, out var colossusComp))
|
||||
return;
|
||||
|
||||
colossusComp.DeathTimer = _time.CurTime;
|
||||
colossusComp.Timed = true;
|
||||
}
|
||||
|
||||
private void OnSupercritical(Entity<CosmicEffigyComponent> ent, ref AnomalySupercriticalEvent args)
|
||||
{
|
||||
if (!Exists(ent.Comp.Colossus)
|
||||
|| !TryComp<CosmicColossusComponent>(ent.Comp.Colossus, out var colossusComp)
|
||||
|| !_mind.TryGetMind(ent.Comp.Colossus.Value, out _, out var mind))
|
||||
return;
|
||||
|
||||
var colossus = ent.Comp.Colossus.Value;
|
||||
|
||||
var ev = new CosmicColossusEffigySupercriticalEvent(ent);
|
||||
RaiseLocalEvent(colossus, ref ev);
|
||||
|
||||
var transform = Transform(ent.Comp.Colossus.Value);
|
||||
Spawn(colossusComp.BuffVfx, transform.Coordinates);
|
||||
|
||||
if (colossusComp.CompletedEffigies == 0)
|
||||
{
|
||||
_audio.PlayStatic(colossusComp.ReawakenSfx,
|
||||
Filter.BroadcastMap(transform.MapID),
|
||||
transform.Coordinates,
|
||||
true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_audio.PlayPvs(colossusComp.ReawakenSfx, ent);
|
||||
}
|
||||
|
||||
colossusComp.CompletedEffigies += 1;
|
||||
|
||||
if (colossusComp.CompletedEffigies >= colossusComp.MaxEffigies)
|
||||
{
|
||||
colossusComp.Timed = false;
|
||||
_popup.PopupEntity(Loc.GetString("colossus-buff-final-popup"), colossus, PopupType.Large);
|
||||
return;
|
||||
}
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("colossus-buff-popup"), colossus, PopupType.Large);
|
||||
|
||||
var objIndex = mind.Objectives.FindIndex(HasComp<CosmicEffigyConditionComponent>);
|
||||
if (objIndex == -1 ||
|
||||
!TryComp<CosmicEffigyConditionComponent>(mind.Objectives[objIndex], out var conditionComp))
|
||||
{
|
||||
Log.Error($"Failed to find effigy objective on {ToPrettyString(colossus)}!");
|
||||
return;
|
||||
}
|
||||
|
||||
var objective = mind.Objectives[objIndex];
|
||||
if (_cultObjective.RandomizeEffigyTarget(objective, conditionComp, setDescription: true) is not {} nextTarget)
|
||||
{
|
||||
Log.Error("Failed to randomize effigy objective location!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mind.UserId is { } userId)
|
||||
{
|
||||
_chat.DispatchServerMessage(_player.GetSessionById(userId), Loc.GetString("colossus-next-target", ("location", nextTarget)));
|
||||
}
|
||||
|
||||
_codeCondition.SetCompleted(objective, false);
|
||||
|
||||
colossusComp.EffigyPlaceActionEntity = _actions.AddAction(colossus, colossusComp.EffigyPlaceAction);
|
||||
colossusComp.DeathTimer = _time.CurTime + colossusComp.DeathWaitEffigy;
|
||||
colossusComp.Timed = true;
|
||||
}
|
||||
|
||||
private void OnColossusEffigy(Entity<CosmicColossusComponent> ent, ref EventCosmicColossusEffigy args)
|
||||
@@ -38,8 +128,16 @@ public sealed class CosmicEffigySystem : EntitySystem
|
||||
|
||||
_actions.RemoveAction(ent.Owner, ent.Comp.EffigyPlaceActionEntity);
|
||||
_codeCondition.SetCompleted(ent.Owner, ent.Comp.EffigyObjective);
|
||||
Spawn(ent.Comp.EffigyPrototype, pos);
|
||||
var effigy = Spawn(ent.Comp.EffigyPrototype, pos);
|
||||
ent.Comp.Timed = false;
|
||||
|
||||
if (!TryComp<CosmicEffigyComponent>(effigy, out var effigyComp))
|
||||
{
|
||||
Log.Error("Colossus tried to place Effigy prototype missing CosmicEffigyComponent!");
|
||||
return;
|
||||
}
|
||||
|
||||
effigyComp.Colossus = ent.Owner;
|
||||
}
|
||||
|
||||
private bool VerifyPlacement(Entity<CosmicColossusComponent> ent, out EntityCoordinates outPos)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._DV.CosmicCult.Components;
|
||||
|
||||
/// <summary>
|
||||
/// The owning colossus's attack rate will be multiplied by
|
||||
/// the given multiplier once its effigy goes supercritical.
|
||||
/// Component meant to be applied to Cosmic Colossus.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class MultiplyAttackRateOnSupercriticalComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public float Multiplier = 1.1f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The owning colossus's corrupting speed will be multiplied by
|
||||
/// the given multiplier once its effigy goes supercritical.
|
||||
/// Component meant to be applied to Cosmic Colossus.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class MultiplyCorruptingSpeedOnSupercriticalComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public float Multiplier = 0.9f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The owning colossus will receive a flat bonus to
|
||||
/// its melee attack damage once its effigy goes supercritical.
|
||||
/// Component meant to be applied to Cosmic Colossus.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class FlatAttackBonusOnSupercriticalComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public FixedPoint2 BonusDamage = 10;
|
||||
|
||||
[DataField]
|
||||
public ProtoId<DamageTypePrototype> BonusDamageType = "Blunt";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The owning colossus will be healed for `-CurrentDamage * Abs(DamageFractionHealed)` damage
|
||||
/// once its effigy goes supercritical.
|
||||
/// Component meant to be applied to Cosmic Colossus.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class HealOnSupercriticalComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public float DamageFractionHealed = 1.0f;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Server._DV.CosmicCult.Abilities.Colossus;
|
||||
using Content.Server._DV.CosmicCult.EntitySystems;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -5,7 +6,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server._DV.CosmicCult.Components;
|
||||
|
||||
[RegisterComponent, Access(typeof(CosmicCorruptingSystem))]
|
||||
[RegisterComponent, Access(typeof(CosmicCorruptingSystem), typeof(CosmicColossusBuffsSystem))]
|
||||
[AutoGenerateComponentPause]
|
||||
public sealed partial class CosmicCorruptingComponent : Component
|
||||
{
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using Content.Server._DV.CosmicCult.Abilities;
|
||||
using Content.Server._DV.CosmicCult.EntitySystems;
|
||||
|
||||
namespace Content.Server._DV.CosmicCult.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Component for cosmic effigy anomalies, spawned by Cosmic Colossi.
|
||||
/// </summary>
|
||||
/// <seelso cref="CosmicEffigySystem"/>.
|
||||
/// <seelso cref="CosmicColossusSystem"/>.
|
||||
[RegisterComponent]
|
||||
public sealed partial class CosmicEffigyComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The colossus that placed this effigy.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? Colossus;
|
||||
}
|
||||
@@ -35,6 +35,12 @@ public sealed class CosmicCultObjectiveSystem : EntitySystem
|
||||
if (args.Cancelled || !_roles.MindHasRole<CosmicColossusRoleComponent>(args.MindId))
|
||||
return;
|
||||
|
||||
if (RandomizeEffigyTarget(uid, comp) is null)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
public string? RandomizeEffigyTarget(EntityUid uid, CosmicEffigyConditionComponent comp, bool setDescription = false)
|
||||
{
|
||||
var warps = new List<EntityUid>();
|
||||
var query = EntityQueryEnumerator<WarpPointComponent>();
|
||||
var effigyBlacklist = comp.Blacklist;
|
||||
@@ -51,10 +57,22 @@ public sealed class CosmicCultObjectiveSystem : EntitySystem
|
||||
|
||||
if (warps.Count <= 0)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
comp.EffigyTarget = _random.Pick(warps);
|
||||
|
||||
var newWarp = _random.Pick(warps);
|
||||
var warpComp = Comp<WarpPointComponent>(newWarp);
|
||||
|
||||
comp.EffigyTarget = newWarp;
|
||||
|
||||
if (setDescription)
|
||||
{
|
||||
_metaData.SetEntityDescription(uid,
|
||||
warpComp.Location != null
|
||||
? Loc.GetString("objective-condition-effigy", ("location", warpComp.Location))
|
||||
: Loc.GetString("objective-condition-effigy-no-target"));
|
||||
}
|
||||
return warpComp.Location;
|
||||
}
|
||||
|
||||
private void OnEffigyAfterAssign(EntityUid uid, CosmicEffigyConditionComponent comp, ref ObjectiveAfterAssignEvent args)
|
||||
|
||||
@@ -13,6 +13,7 @@ using Content.Shared.Popups;
|
||||
using Content.Shared.Station.Components;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Warps;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Physics.Components;
|
||||
@@ -41,6 +42,7 @@ public sealed class CosmicColossusSystem : EntitySystem
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<CosmicColossusComponent, ComponentInit>(OnSpawn);
|
||||
SubscribeLocalEvent<CosmicColossusComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<CosmicColossusComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
@@ -82,7 +84,7 @@ public sealed class CosmicColossusSystem : EntitySystem
|
||||
|
||||
private void OnSpawn(Entity<CosmicColossusComponent> ent, ref ComponentInit args) // I WANT THIS BIG GUY HURLED TOWARDS THE STATION
|
||||
{
|
||||
ent.Comp.DeathTimer = _timing.CurTime + ent.Comp.DeathWait;
|
||||
ent.Comp.DeathTimer = _timing.CurTime + ent.Comp.DeathWaitSpawn;
|
||||
var station = _station.GetStationInMap(Transform(ent).MapID);
|
||||
if (TryComp<StationDataComponent>(station, out var stationData))
|
||||
{
|
||||
@@ -115,4 +117,9 @@ public sealed class CosmicColossusSystem : EntitySystem
|
||||
RemComp<WarpPointComponent>(ent);
|
||||
RemComp<CosmicCorruptingComponent>(ent);
|
||||
}
|
||||
|
||||
private void OnMeleeHit(Entity<CosmicColossusComponent> colossus, ref MeleeHitEvent args)
|
||||
{
|
||||
args.BonusDamage += colossus.Comp.BonusDamage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
using Content.Server.Cloning;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared._DV.Psionics.Components;
|
||||
using Content.Shared._DV.Psionics.Components.PsionicPowers;
|
||||
using Content.Shared._DV.Psionics.Systems.PsionicPowers;
|
||||
using Content.Shared._DV.Species;
|
||||
using Content.Shared.Bed.Sleep;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server._DV.Psionics.Systems.PsionicPowers;
|
||||
|
||||
public sealed class FracturedFormPowerSystem : SharedFracturedFormPowerSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly CloningSystem _cloning = default!;
|
||||
[Dependency] private readonly MindSystem _mind = default!;
|
||||
[Dependency] private readonly StationSpawningSystem _stationSpawning = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
[Dependency] private readonly PvsOverrideSystem _pvsOverride = default!;
|
||||
|
||||
// holy initialize performance? but better for it to happen once than the double dict lookup every tick!!
|
||||
private EntityQuery<FracturedFormBodyComponent> _bodyQuery;
|
||||
private EntityQuery<SleepingComponent> _sleepingQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_bodyQuery = GetEntityQuery<FracturedFormBodyComponent>();
|
||||
_sleepingQuery = GetEntityQuery<SleepingComponent>();
|
||||
}
|
||||
|
||||
protected override void OnPowerInit(Entity<FracturedFormPowerComponent> power, ref MapInitEvent args)
|
||||
{
|
||||
base.OnPowerInit(power, ref args);
|
||||
|
||||
// The next random swap is between 5 and 20 minutes.
|
||||
var randomTime = Random.Next(power.Comp.NextSwapMinTime, power.Comp.NextSwapMaxTime);
|
||||
power.Comp.NextSwap = Timing.CurTime + randomTime;
|
||||
power.Comp.NextVoluntarySwap = Timing.CurTime + power.Comp.VoluntarySwapCooldown;
|
||||
|
||||
// Don't generate a new body if we're already part of a network.
|
||||
if (HasComp<FracturedFormBodyComponent>(power))
|
||||
return;
|
||||
|
||||
// Don't make bodies if there is no body. This is solely for test fails.
|
||||
if (!HasComp<BodyComponent>(power))
|
||||
return;
|
||||
|
||||
var bodyComp = AddComp<FracturedFormBodyComponent>(power);
|
||||
bodyComp.ControllingForm = power.Owner;
|
||||
power.Comp.Bodies.Add(power);
|
||||
var body = GenerateForm(power);
|
||||
// hide the SSD indicator.
|
||||
if (SsdQuery.TryComp(body, out var ssdComp))
|
||||
ssdComp.IsSSD = false;
|
||||
}
|
||||
|
||||
private EntityUid GenerateForm(Entity<FracturedFormPowerComponent> original)
|
||||
{
|
||||
// Form:
|
||||
// - Same appearance as original
|
||||
// - Different apperance, still humanoid
|
||||
// Equipment:
|
||||
// - Same as original body
|
||||
// - Nude and helpless
|
||||
|
||||
var xform = Transform(original);
|
||||
|
||||
var hasGear = Random.Prob(original.Comp.HasGearChance);
|
||||
|
||||
if (Random.Prob(original.Comp.DifferentSpeciesChance) || !_cloning.TryCloning(original, _transform.GetMapCoordinates(original), hasGear ? original.Comp.CopyClothed : original.Comp.CopyNaked, out var newBody)) // Slightly lower chance to copy the original body
|
||||
{
|
||||
// Either the dice rolled poorly, or the cloning failed. Either way, make a new body instead. (Or try to)
|
||||
var validSpecies = new List<ProtoId<SpeciesPrototype>>();
|
||||
var speciesPrototypes = _prototype.EnumeratePrototypes<SpeciesPrototype>();
|
||||
foreach (var proto in speciesPrototypes)
|
||||
{
|
||||
var speciesEntityPrototype = _prototype.Index<EntityPrototype>(proto.Prototype);
|
||||
// If they have the PotentialPsionicComponent, they can be psionic.
|
||||
if (proto.RoundStart && speciesEntityPrototype.TryGetComponent<PotentialPsionicComponent>(out _, Factory) && !SpeciesHiderSystem.IsHidden(proto.ID))
|
||||
validSpecies.Add(proto.ID);
|
||||
}
|
||||
var species = Random.Pick(validSpecies);
|
||||
var character = HumanoidCharacterProfile.RandomWithSpecies(species);
|
||||
newBody = _stationSpawning.SpawnPlayerMob(xform.Coordinates, hasGear ? original.Comp.VisitorJob : original.Comp.NakedJob, character, _station.GetOwningStation(original.Owner));
|
||||
if (newBody is not { } bodyV || Deleted(bodyV))
|
||||
{
|
||||
Log.Error($"Failed to create a new body for {ToPrettyString(original)}. This is a bug.");
|
||||
return EntityUid.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
if (newBody is not { } body || Deleted(body))
|
||||
return default!;
|
||||
|
||||
var bodyComp = AddComp<FracturedFormBodyComponent>(body);
|
||||
original.Comp.Bodies.Add(body);
|
||||
bodyComp.ControllingForm = original.Owner;
|
||||
|
||||
if (_player.TryGetSessionByEntity(original, out var session))
|
||||
_pvsOverride.AddSessionOverride(body, session);
|
||||
|
||||
Dirty(original);
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
private bool TryGetValidBody(Entity<FracturedFormPowerComponent> psionic, out EntityUid validBody)
|
||||
{
|
||||
foreach (var body in psionic.Comp.Bodies)
|
||||
{
|
||||
if (!IsValidBody(psionic, body))
|
||||
continue;
|
||||
|
||||
validBody = body;
|
||||
return true;
|
||||
}
|
||||
validBody = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void Swap(Entity<FracturedFormPowerComponent> psionic)
|
||||
{
|
||||
if (!TryGetValidBody(psionic, out var targetBody))
|
||||
return;
|
||||
|
||||
_audio.PlayPvs(psionic.Comp.SwapSound, psionic);
|
||||
// Transfer mind if present
|
||||
if (MindContainerQuery.TryComp(psionic, out var mindContainer) && mindContainer.Mind.HasValue)
|
||||
_mind.TransferTo(mindContainer.Mind.Value, targetBody);
|
||||
// Wake up the new body
|
||||
Sleeping.TryWaking(targetBody);
|
||||
// Remove the action.
|
||||
Action.RemoveAction(psionic.Comp.ActionEntity);
|
||||
// Create new component on target and copy data
|
||||
var duplicate = EnsureComp<FracturedFormPowerComponent>(targetBody);
|
||||
duplicate.Bodies = psionic.Comp.Bodies;
|
||||
// Update all body references
|
||||
foreach (var body in duplicate.Bodies)
|
||||
{
|
||||
if (_bodyQuery.TryComp(body, out var bodyComp))
|
||||
bodyComp.ControllingForm = targetBody;
|
||||
}
|
||||
|
||||
if (_player.TryGetSessionByEntity(targetBody, out var session))
|
||||
{
|
||||
_pvsOverride.AddSessionOverride(psionic, session);
|
||||
_pvsOverride.RemoveSessionOverride(targetBody, session);
|
||||
}
|
||||
|
||||
RemCompDeferred<FracturedFormPowerComponent>(psionic);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
List<Entity<FracturedFormPowerComponent>> swapTargets = [];
|
||||
|
||||
var entities = EntityQueryEnumerator<FracturedFormPowerComponent, MobStateComponent>();
|
||||
while (entities.MoveNext(out var uid, out var comp, out var mobState))
|
||||
{
|
||||
// Check sleep warning
|
||||
if (!comp.SleepWarned && Timing.CurTime > comp.NextSwap - comp.WarningTimeBeforeSleep)
|
||||
{
|
||||
comp.SleepWarned = true;
|
||||
Popup.PopupEntity(Loc.GetString("psionic-power-fractured-form-sleepy"), uid, uid, PopupType.LargeCaution);
|
||||
Chat.TryEmoteWithChat(uid, "Yawn");
|
||||
}
|
||||
// Swap check
|
||||
if ((_sleepingQuery.HasComp(uid) || MobState.IsIncapacitated(uid, mobState)) && Timing.CurTime > comp.NextVoluntarySwap
|
||||
|| Timing.CurTime > comp.NextSwap)
|
||||
swapTargets.Add((uid, comp));
|
||||
}
|
||||
|
||||
foreach (var target in swapTargets)
|
||||
{
|
||||
Swap(target);
|
||||
}
|
||||
|
||||
// Process bodies
|
||||
var bodies = EntityQueryEnumerator<FracturedFormBodyComponent, MobStateComponent>();
|
||||
while (bodies.MoveNext(out var uid, out var comp, out var mobState))
|
||||
{
|
||||
// Put to sleep if no sleeping component and no mind
|
||||
if (!_sleepingQuery.HasComp(uid) && !_mind.GetMind(uid).HasValue && !FracturedQuery.HasComp(uid))
|
||||
Sleeping.TrySleeping((uid, mobState));
|
||||
// Cleanup invalid bodies
|
||||
if (!comp.ControllingForm.IsValid()
|
||||
|| Deleted(comp.ControllingForm)
|
||||
|| !FracturedQuery.HasComp(comp.ControllingForm))
|
||||
{
|
||||
RemCompDeferred<FracturedFormBodyComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Server._DV.Uprising;
|
||||
|
||||
[RegisterComponent, Access(typeof(UprisingRuleSystem))]
|
||||
public sealed partial class UprisingConsoleComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public TimeSpan Delay = TimeSpan.FromSeconds(5);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Content.Shared._DV.Roles;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server._DV.Uprising;
|
||||
|
||||
[RegisterComponent, Access(typeof(UprisingRuleSystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class UprisingRuleComponent : Component
|
||||
{
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
|
||||
public TimeSpan? NukeAnnouncementAt;
|
||||
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
|
||||
public TimeSpan? NukeTimeAt;
|
||||
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
|
||||
public TimeSpan? FirstWarningAt;
|
||||
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
|
||||
public TimeSpan? ImpendingWarningAt;
|
||||
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
|
||||
public TimeSpan? FinalWarningAt;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan NukeAnnouncementDelay = TimeSpan.FromMinutes(30);
|
||||
|
||||
[DataField]
|
||||
public TimeSpan NukeTimeDelay = TimeSpan.FromMinutes(90);
|
||||
|
||||
[DataField]
|
||||
public TimeSpan FirstWarning = TimeSpan.FromMinutes(30);
|
||||
|
||||
[DataField]
|
||||
public TimeSpan ImpendingWarning = TimeSpan.FromMinutes(15);
|
||||
|
||||
[DataField]
|
||||
public TimeSpan FinalWarning = TimeSpan.FromMinutes(5);
|
||||
|
||||
[DataField]
|
||||
public float NukeDuration = 60f;
|
||||
|
||||
[DataField]
|
||||
public (UprisingSide, TimeSpan)? SideWinsAt;
|
||||
|
||||
[DataField]
|
||||
public UprisingSide? SideWon;
|
||||
|
||||
[DataField]
|
||||
public Dictionary<UprisingSide, TimeSpan> SideTimes = new()
|
||||
{
|
||||
{ UprisingSide.Insurgent , TimeSpan.FromMinutes(3) },
|
||||
{ UprisingSide.Loyalist , TimeSpan.FromMinutes(3) },
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Server.Antag;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.GameTicking.Rules;
|
||||
using Content.Server.Nuke;
|
||||
using Content.Server.Pinpointer;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared._DV.Roles;
|
||||
using Content.Shared._DV.Uprising;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Objectives.Systems;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server._DV.Uprising;
|
||||
|
||||
public sealed class UprisingRuleSystem : GameRuleSystem<UprisingRuleComponent>
|
||||
{
|
||||
[Dependency] private readonly SharedChatSystem _chat = default!;
|
||||
[Dependency] private readonly NukeSystem _nuke = default!;
|
||||
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
|
||||
[Dependency] private readonly NavMapSystem _navMap = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _role = default!;
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
[Dependency] private readonly ISharedPlayerManager _player = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<LoyalistRoleComponent, GetBriefingEvent>(OnGetLoyalistBriefing);
|
||||
SubscribeLocalEvent<InsurgentRoleComponent, GetBriefingEvent>(OnGetInsurgentBriefing);
|
||||
|
||||
SubscribeLocalEvent<UprisingVictoryObjectiveComponent, ObjectiveGetProgressEvent>(OnUprisingVictoryProgress);
|
||||
|
||||
SubscribeLocalEvent<UprisingConsoleComponent, GetVerbsEvent<AlternativeVerb>>(OnGetConsoleVerbs);
|
||||
SubscribeLocalEvent<UprisingConsoleComponent, UprisingArmDoAfter>(OnArm);
|
||||
SubscribeLocalEvent<UprisingConsoleComponent, UprisingDisarmDoAfter>(OnDisarm);
|
||||
}
|
||||
|
||||
private void OnUprisingVictoryProgress(Entity<UprisingVictoryObjectiveComponent> ent, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
if (GetRule() is not { } rule)
|
||||
return;
|
||||
|
||||
if (rule.Comp1.SideWinsAt is var (side, _) && ent.Comp.Side == side)
|
||||
args.Progress = 0.5f;
|
||||
else if (rule.Comp1.SideWon == ent.Comp.Side)
|
||||
args.Progress = 1f;
|
||||
else
|
||||
args.Progress = 0f;
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, UprisingRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
component.NukeAnnouncementAt = Timing.CurTime + component.NukeAnnouncementDelay;
|
||||
component.NukeTimeAt = Timing.CurTime + component.NukeTimeDelay;
|
||||
component.FirstWarningAt = component.NukeTimeAt - component.FirstWarning;
|
||||
component.ImpendingWarningAt = component.NukeTimeAt - component.ImpendingWarning;
|
||||
component.FinalWarningAt = component.NukeTimeAt - component.FinalWarning;
|
||||
}
|
||||
|
||||
protected override void ActiveTick(EntityUid uid, UprisingRuleComponent component, GameRuleComponent gameRule, float frameTime)
|
||||
{
|
||||
base.ActiveTick(uid, component, gameRule, frameTime);
|
||||
|
||||
if (component.NukeAnnouncementAt is { } nukeAnnouncement && nukeAnnouncement <= Timing.CurTime)
|
||||
{
|
||||
var time = component.NukeTimeAt.HasValue ? component.NukeTimeAt.Value - Timing.CurTime : component.NukeTimeDelay;
|
||||
_chat.DispatchGlobalAnnouncement(Loc.GetString("uprising-announcement-nuke-announcement", ("time", (int)Math.Round(time.TotalMinutes))), colorOverride: Color.Gold);
|
||||
component.NukeAnnouncementAt = null;
|
||||
}
|
||||
|
||||
if (component.FirstWarningAt is { } firstWarning && firstWarning <= Timing.CurTime)
|
||||
{
|
||||
var time = component.NukeTimeAt.HasValue ? component.NukeTimeAt.Value - Timing.CurTime : component.FirstWarning;
|
||||
_chat.DispatchGlobalAnnouncement(Loc.GetString("uprising-announcement-first-warning", ("time", (int)Math.Round(time.TotalMinutes))), colorOverride: Color.Gold);
|
||||
component.FirstWarningAt = null;
|
||||
}
|
||||
|
||||
if (component.ImpendingWarningAt is { } impendingWarning && impendingWarning <= Timing.CurTime)
|
||||
{
|
||||
var time = component.NukeTimeAt.HasValue ? component.NukeTimeAt.Value - Timing.CurTime : component.ImpendingWarning;
|
||||
_chat.DispatchGlobalAnnouncement(Loc.GetString("uprising-announcement-impending-warning", ("time", (int)Math.Round(time.TotalMinutes))), colorOverride: Color.Gold);
|
||||
component.ImpendingWarningAt = null;
|
||||
}
|
||||
|
||||
if (component.FinalWarningAt is { } finalWarning && finalWarning <= Timing.CurTime)
|
||||
{
|
||||
var time = component.NukeTimeAt.HasValue ? component.NukeTimeAt.Value - Timing.CurTime : component.FinalWarning;
|
||||
_chat.DispatchGlobalAnnouncement(Loc.GetString("uprising-announcement-final-warning", ("time", (int)Math.Round(time.TotalMinutes))), colorOverride: Color.Gold);
|
||||
component.FinalWarningAt = null;
|
||||
}
|
||||
|
||||
if (component.NukeTimeAt is { } nukeTime && nukeTime <= Timing.CurTime)
|
||||
{
|
||||
component.NukeTimeAt = null;
|
||||
var query = EntityQueryEnumerator<NukeComponent>();
|
||||
while (query.MoveNext(out var bombUid, out var bomb))
|
||||
{
|
||||
_nuke.SetRemainingTime(bombUid, component.NukeDuration, bomb);
|
||||
_itemSlots.SetLock(bombUid, bomb.DiskSlot, false);
|
||||
_itemSlots.TryEject(bombUid, bomb.DiskSlot, null, out _, true);
|
||||
_itemSlots.SetLock(bombUid, bomb.DiskSlot, true);
|
||||
_nuke.ArmBomb(bombUid, bomb, "uprising-announcement-nuke-armed");
|
||||
}
|
||||
}
|
||||
|
||||
if (component.SideWinsAt is var (winningSide, winningTime) && winningTime <= Timing.CurTime)
|
||||
{
|
||||
component.SideWinsAt = null;
|
||||
var query = EntityQueryEnumerator<NukeComponent>();
|
||||
while (query.MoveNext(out var bombUid, out var bomb))
|
||||
{
|
||||
_nuke.DisarmBomb(bombUid, bomb);
|
||||
}
|
||||
|
||||
component.SideWon = winningSide;
|
||||
_chat.DispatchGlobalAnnouncement(
|
||||
Loc.GetString($"uprising-announcement-victory-announcement-{winningSide}"),
|
||||
Loc.GetString($"uprising-announcement-victory-announcement-{winningSide}.sender"),
|
||||
colorOverride: Color.FromXaml(Loc.GetString($"uprising-announcement-victory-announcement-{winningSide}.color")));
|
||||
|
||||
GameTicker.EndRound();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AppendRoundEndText(EntityUid uid,
|
||||
UprisingRuleComponent component,
|
||||
GameRuleComponent gameRule,
|
||||
ref RoundEndTextAppendEvent args)
|
||||
{
|
||||
if (component.SideWinsAt is var (partialSide, _))
|
||||
{
|
||||
args.AddLine(Loc.GetString($"uprising-outcome-{partialSide}-partial"));
|
||||
}
|
||||
else if (component.SideWon is { } winningSide)
|
||||
{
|
||||
args.AddLine(Loc.GetString($"uprising-outcome-{winningSide}-full"));
|
||||
}
|
||||
else
|
||||
{
|
||||
args.AddLine(Loc.GetString("uprising-outcome-none"));
|
||||
}
|
||||
|
||||
args.AddLine(Loc.GetString("uprising-leads"));
|
||||
|
||||
var antags = _antag.GetAntagIdentifiers(uid);
|
||||
var leads = new HashSet<EntityUid>();
|
||||
|
||||
foreach (var (mind, sessionData, name) in antags)
|
||||
{
|
||||
leads.Add(mind);
|
||||
|
||||
if (_role.MindHasRole<UprisingRoleComponent>(mind, out var it))
|
||||
args.AddLine(Loc.GetString($"uprising-leads-user.{it.Value.Comp2.Side}", ("name", name), ("user", sessionData.UserName)));
|
||||
else
|
||||
args.AddLine(Loc.GetString("uprising-leads-user.Unknown", ("name", name), ("user", sessionData.UserName)));
|
||||
}
|
||||
args.AddLine(string.Empty);
|
||||
|
||||
var lines = new Dictionary<UprisingSide, List<string>>();
|
||||
foreach (var value in Enum.GetValues<UprisingSide>())
|
||||
{
|
||||
lines[value] = new();
|
||||
}
|
||||
|
||||
var minds = EntityQueryEnumerator<MindComponent>();
|
||||
while (minds.MoveNext(out var mindUid, out var mindComponent))
|
||||
{
|
||||
if (leads.Contains(mindUid))
|
||||
continue;
|
||||
|
||||
if (!_role.MindHasRole<UprisingRoleComponent>((mindUid, mindComponent), out var role) || mindComponent.OriginalOwnerUserId is not { } owner)
|
||||
continue;
|
||||
|
||||
if (!_player.TryGetPlayerData(owner, out var data))
|
||||
continue;
|
||||
|
||||
lines[role.Value.Comp2.Side].Add(Loc.GetString($"uprising-members-user", ("name", mindComponent.CharacterName ?? string.Empty), ("user", data.UserName)));
|
||||
}
|
||||
|
||||
foreach (var value in Enum.GetValues<UprisingSide>())
|
||||
{
|
||||
args.AddLine(Loc.GetString($"uprising-members.{value}"));
|
||||
foreach (var line in lines[value])
|
||||
{
|
||||
args.AddLine(line);
|
||||
}
|
||||
args.AddLine(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGetLoyalistBriefing(Entity<LoyalistRoleComponent> ent, ref GetBriefingEvent args)
|
||||
{
|
||||
args.Append(Loc.GetString("antag-Loyalist.briefing"));
|
||||
}
|
||||
|
||||
private void OnGetInsurgentBriefing(Entity<InsurgentRoleComponent> ent, ref GetBriefingEvent args)
|
||||
{
|
||||
args.Append(Loc.GetString("antag-Insurgent.briefing"));
|
||||
}
|
||||
|
||||
private Entity<UprisingRuleComponent, GameRuleComponent>? GetRule()
|
||||
{
|
||||
var enumerator = QueryActiveRules();
|
||||
while (enumerator.MoveNext(out var uid, out _, out var uprising, out var gamerule))
|
||||
{
|
||||
return (uid, uprising, gamerule);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void StartWinning(UprisingSide side, EntityUid console)
|
||||
{
|
||||
if (GetRule() is not { } rule)
|
||||
return;
|
||||
|
||||
var location = _navMap.GetNearestBeaconString(console);
|
||||
var announcement = Loc.GetString($"uprising-announcement-start-victory-announcement-{side}",
|
||||
("time", (int) Math.Round(rule.Comp1.SideTimes[side].TotalSeconds)),
|
||||
("location", FormattedMessage.RemoveMarkupOrThrow(location)));
|
||||
|
||||
_chat.DispatchGlobalAnnouncement(
|
||||
announcement,
|
||||
Loc.GetString($"uprising-announcement-start-victory-announcement-{side}.sender"),
|
||||
colorOverride: Color.FromXaml(Loc.GetString($"uprising-announcement-start-victory-announcement-{side}.color")));
|
||||
|
||||
rule.Comp1.SideWinsAt = (side, Timing.CurTime + rule.Comp1.SideTimes[side]);
|
||||
}
|
||||
|
||||
private void StopWinning()
|
||||
{
|
||||
if (GetRule() is not { } rule || rule.Comp1.SideWinsAt is not var (side, time))
|
||||
return;
|
||||
|
||||
rule.Comp1.SideTimes[side] = Timing.CurTime - time;
|
||||
|
||||
_chat.DispatchGlobalAnnouncement(
|
||||
Loc.GetString($"uprising-announcement-interrupted-victory-announcement-{side}"),
|
||||
Loc.GetString($"uprising-announcement-interrupted-victory-announcement-{side}.sender"),
|
||||
colorOverride: Color.FromXaml(Loc.GetString($"uprising-announcement-interrupted-victory-announcement-{side}.color")));
|
||||
|
||||
rule.Comp1.SideWinsAt = null;
|
||||
}
|
||||
|
||||
private void OnGetConsoleVerbs(Entity<UprisingConsoleComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (GetRule() is not { } rule)
|
||||
return;
|
||||
|
||||
var user = args.User;
|
||||
if (rule.Comp1.SideWinsAt is var (disarmSide, _))
|
||||
{
|
||||
args.Verbs.Add(new ()
|
||||
{
|
||||
Act = () => StartDisarming(ent, user),
|
||||
Text = Loc.GetString($"uprising-disarm-verb.{disarmSide}"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckCanArm(args.User, out var armSide))
|
||||
return;
|
||||
|
||||
args.Verbs.Add(new()
|
||||
{
|
||||
Act = () => StartArming(ent, user),
|
||||
Text = Loc.GetString($"uprising-arm-verb.{armSide}"),
|
||||
});
|
||||
}
|
||||
|
||||
private bool CheckCanArm(EntityUid user, [NotNullWhen(true)] out UprisingSide? side)
|
||||
{
|
||||
side = null;
|
||||
if (_mind.GetMind(user) is not { } mind || !TryComp<MindComponent>(mind, out var mindComp))
|
||||
return false;
|
||||
|
||||
if (!_role.MindHasRole<UprisingRoleComponent>(mind, out var uprisingRole))
|
||||
return false;
|
||||
|
||||
foreach (var objective in mindComp.Objectives)
|
||||
{
|
||||
if (!HasComp<UprisingArmRequiredObjectiveComponent>(objective))
|
||||
continue;
|
||||
|
||||
if (_objectives.GetProgress(objective, (mind, mindComp)) is not >= 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
side = uprisingRole.Value.Comp2.Side;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void StartDisarming(Entity<UprisingConsoleComponent> ent, EntityUid user)
|
||||
{
|
||||
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager,
|
||||
user,
|
||||
ent.Comp.Delay,
|
||||
new UprisingDisarmDoAfter(),
|
||||
ent.Owner));
|
||||
}
|
||||
|
||||
private void StartArming(Entity<UprisingConsoleComponent> ent, EntityUid user)
|
||||
{
|
||||
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager,
|
||||
user,
|
||||
ent.Comp.Delay,
|
||||
new UprisingArmDoAfter(),
|
||||
ent.Owner));
|
||||
}
|
||||
|
||||
private void OnArm(Entity<UprisingConsoleComponent> ent, ref UprisingArmDoAfter args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled || !CheckCanArm(args.User, out var side))
|
||||
return;
|
||||
|
||||
StartWinning(side.Value, ent);
|
||||
}
|
||||
|
||||
private void OnDisarm(Entity<UprisingConsoleComponent> ent, ref UprisingDisarmDoAfter args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled)
|
||||
return;
|
||||
|
||||
StopWinning();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using Content.Server._DV.Psionics.Systems;
|
||||
using Content.Server.Antag;
|
||||
using Content.Shared._DV.Movement.Components;
|
||||
using Content.Shared._DV.Psionics.Components;
|
||||
using Content.Shared.CharacterInfo;
|
||||
using Content.Shared.Cloning.Events;
|
||||
using Content.Shared.CombatMode.Pacification;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.NPC.Components;
|
||||
using Content.Shared.Radio.Components;
|
||||
using Content.Shared.Roles.Components;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Zombies;
|
||||
|
||||
public sealed partial class ZombieSystem
|
||||
{
|
||||
//private static readonly string MindRoleInitialInfected = "MindRoleInitialInfected";
|
||||
private static readonly EntProtoId InitialInfectedFailureSurviveObjective = "InitialInfectedFailureSurviveObjective";
|
||||
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
[Dependency] private readonly PsionicSystem _psionic = default!; // DeltaV
|
||||
[Dependency] private readonly SharedJetpackSystem _jetpack = default!; // DeltaV - Prevent Jetpacks on Zombies
|
||||
|
||||
private void InitializeDV()
|
||||
{
|
||||
// This needs to be done before CloneEvent or else the proper attributes won't be restored or modified on
|
||||
// the new entity.
|
||||
SubscribeLocalEvent<ZombieComponent, CloningAttemptEvent>(OnBeforeUnzombify);
|
||||
SubscribeLocalEvent<InitialInfectedComponent, CloningEvent>(OnInitialInfectedCloning);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Extra things we want to ensure, remove, or take care of when someone is zombified.
|
||||
/// </summary>
|
||||
private void ZombifyEntityDV(Entity<ZombieComponent> ent, MobStateComponent? mobState = null)
|
||||
{
|
||||
// Remove innate radio
|
||||
RemComp<ActiveRadioComponent>(ent); // If the zombie has an innate radio, get rid of it.
|
||||
|
||||
// Remove headsets in pockets
|
||||
for (var i = 1; i <= 4; i++) // Arachnids have 4 pockets
|
||||
{
|
||||
if (_inventory.TryGetSlotEntity(ent, $"pocket{i}", out var headset) && HasComp<HeadsetComponent>(headset))
|
||||
_inventory.TryUnequip(ent, $"pocket{i}", true, true);
|
||||
}
|
||||
|
||||
// Prevent Psionic Zombies
|
||||
RemComp<PotentialPsionicComponent>(ent);
|
||||
_psionic.MindBreakEntity(ent.Owner, false, true);
|
||||
|
||||
// Prevent Jetpacks on Zombies
|
||||
if (TryComp<JetpackUserComponent>(ent, out var jetpackUser))
|
||||
{
|
||||
if (TryComp<JetpackComponent>(jetpackUser.Jetpack, out var jetpack))
|
||||
_jetpack.SetEnabled(jetpackUser.Jetpack, jetpack, false, ent);
|
||||
}
|
||||
RemComp<AutomaticJetpackUserComponent>(ent);
|
||||
|
||||
// Prevent shitters biting other zombies
|
||||
EnsureComp<NoFriendlyFireComponent>(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - We need to save some extra things to the ZombieComponent so we can restore them when
|
||||
/// they are about to be cloned.
|
||||
/// </summary>
|
||||
/// <param name="ent"></param>
|
||||
private void PreserveEntityComponentState(Entity<ZombieComponent> ent)
|
||||
{
|
||||
SaveFactionsBeforeZombification(ent);
|
||||
|
||||
// Zombification removes pacifism but lets track if they have it so the pacifist players will be... pacified.
|
||||
ent.Comp.WasPacisfistBeforeZombification = HasComp<PacifiedComponent>(ent);
|
||||
}
|
||||
|
||||
private void SaveFactionsBeforeZombification(Entity<ZombieComponent> ent)
|
||||
{
|
||||
if (!TryComp<NpcFactionMemberComponent>(ent, out var faction))
|
||||
return;
|
||||
|
||||
ent.Comp.FactionsBeforeZombification.AddRange(faction.Factions);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Restores certain components to their previous state that the upstream unzombify doesn't handle.
|
||||
/// We need to do this before CloningEvent so the cloned body will have the updated components BEFORE cloning happens.
|
||||
/// </summary>
|
||||
/// <param name="ent"></param>
|
||||
/// <param name="args"></param>
|
||||
private void OnBeforeUnzombify(Entity<ZombieComponent> ent, ref CloningAttemptEvent args)
|
||||
{
|
||||
// Restore factions
|
||||
_faction.ClearFactions(ent.Owner); // Should only be zombie, but might as well clear the whole list in case.
|
||||
ent.Comp.FactionsBeforeZombification.ForEach(previousFaction => _faction.AddFaction(ent.Owner, previousFaction));
|
||||
|
||||
// Restore pacifism
|
||||
if (ent.Comp.WasPacisfistBeforeZombification)
|
||||
EnsureComp<PacifiedComponent>(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Adds a survival objective to Initial Infected who are cloned. Chances are that the crew has recovered, so
|
||||
/// bioterrorism time is probably over.
|
||||
/// </summary>
|
||||
/// <param name="ent"></param>
|
||||
/// <param name="args"></param>
|
||||
private void OnInitialInfectedCloning(Entity<InitialInfectedComponent> ent, ref CloningEvent args)
|
||||
{
|
||||
// Change II objectives so they no longer have infect objectives. Now, they'll have survival objectives. Live to infect another day. :)
|
||||
_mind.TryGetMind(ent.Owner, out var mindId, out var mindContainer);
|
||||
|
||||
// If we can't find the mind, oh well. We tried.
|
||||
if (mindContainer is not { } mind)
|
||||
return;
|
||||
|
||||
if (_role.MindHasRole<InitialInfectedRoleComponent>(mindId))
|
||||
_mind.TryAddObjective(mindId, mind, InitialInfectedFailureSurviveObjective);
|
||||
}
|
||||
}
|
||||
@@ -285,6 +285,11 @@ public sealed partial class AnomalyComponent : Component
|
||||
|
||||
[DataField]
|
||||
public bool DeleteEntity = true;
|
||||
|
||||
// DeltaV - Colossus additions START
|
||||
[DataField]
|
||||
public bool AlwaysGrow;
|
||||
// DeltaV - Colossus additions END
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -308,7 +313,7 @@ public readonly record struct AnomalySupercriticalEvent(EntityUid Anomaly, float
|
||||
/// <param name="Anomaly">The anomaly being shut down.</param>
|
||||
/// <param name="Supercritical">Whether or not the anomaly shut down passively or via a supercritical event.</param>
|
||||
[ByRefEvent]
|
||||
public readonly record struct AnomalyShutdownEvent(EntityUid Anomaly, bool Supercritical);
|
||||
public readonly record struct AnomalyShutdownEvent(EntityUid Anomaly, bool Supercritical, bool Forced); // DeltaV - Add Forced
|
||||
|
||||
/// <summary>
|
||||
/// Event broadcast when an anomaly's severity is changed.
|
||||
|
||||
@@ -84,7 +84,7 @@ public abstract partial class SharedAnomalySystem : EntitySystem // DeltaV - Mad
|
||||
Log.Info($"Performing anomaly pulse. Entity: {ToPrettyString(uid)}");
|
||||
|
||||
// if we are above the growth threshold, then grow before the pulse
|
||||
if (component.Stability > component.GrowthThreshold)
|
||||
if (component.AlwaysGrow || component.Stability > component.GrowthThreshold) // DeltaV - Add AlwaysGrow
|
||||
{
|
||||
ChangeAnomalySeverity(uid, GetSeverityIncreaseFromGrowth(component), component);
|
||||
}
|
||||
@@ -192,7 +192,8 @@ public abstract partial class SharedAnomalySystem : EntitySystem // DeltaV - Mad
|
||||
/// <param name="supercritical">Whether or not the anomaly ended via supercritical event</param>
|
||||
/// <param name="spawnCore">Create anomaly cores based on the result of completing an anomaly?</param>
|
||||
/// <param name="logged">Whether or not the anomaly decaying/going supercritical is logged</param>
|
||||
public void EndAnomaly(EntityUid uid, AnomalyComponent? component = null, bool supercritical = false, bool spawnCore = true, bool logged = false)
|
||||
/// <param name="forced">Whether or not the anomaly shutdown was caused by component shutdown</param> // DeltaV - Add forced
|
||||
public void EndAnomaly(EntityUid uid, AnomalyComponent? component = null, bool supercritical = false, bool spawnCore = true, bool logged = false, bool forced = false) // DeltaV - Add forced
|
||||
{
|
||||
if (logged)
|
||||
{
|
||||
@@ -207,7 +208,7 @@ public abstract partial class SharedAnomalySystem : EntitySystem // DeltaV - Mad
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
var ev = new AnomalyShutdownEvent(uid, supercritical);
|
||||
var ev = new AnomalyShutdownEvent(uid, supercritical, forced); // DeltaV - Add forced
|
||||
RaiseLocalEvent(uid, ref ev, true);
|
||||
|
||||
if (Terminating(uid) || _net.IsClient)
|
||||
@@ -345,7 +346,7 @@ public abstract partial class SharedAnomalySystem : EntitySystem // DeltaV - Mad
|
||||
|
||||
// if the stability is under the death threshold,
|
||||
// update it every second to start killing it slowly.
|
||||
if (anomaly.Stability < anomaly.DecayThreshold)
|
||||
if (!anomaly.AlwaysGrow && anomaly.Stability < anomaly.DecayThreshold) // DeltaV - Add AlwaysGrow
|
||||
{
|
||||
ChangeAnomalyHealth(ent, anomaly.HealthChangePerSecond * frameTime, anomaly);
|
||||
}
|
||||
@@ -479,6 +480,14 @@ public abstract partial class SharedAnomalySystem : EntitySystem // DeltaV - Mad
|
||||
if (!Resolve(ent, ref ent.Comp, logMissing: false))
|
||||
return false;
|
||||
|
||||
// DeltaV - Colossus Additions START
|
||||
if (ent.Comp.AlwaysGrow)
|
||||
{
|
||||
visual = AnomalyStabilityVisuals.Growing;
|
||||
return true;
|
||||
}
|
||||
// DeltaV - Colossus Additions END
|
||||
|
||||
visual = AnomalyStabilityVisuals.Stable;
|
||||
if (ent.Comp.Stability <= ent.Comp.DecayThreshold)
|
||||
{
|
||||
|
||||
@@ -135,4 +135,10 @@ public sealed partial class CCVars
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> AdminOverlayStackMax =
|
||||
CVarDef.Create("ui.admin_overlay_stack_max", 3, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// If true, ghosts will see an "(F)" button next to chat messages, which can be used to follow the sender.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> InterfaceChatFollowButton =
|
||||
CVarDef.Create("ui.chat_follow_button", true, CVar.CLIENT | CVar.REPLICATED | CVar.ARCHIVE);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,20 @@ namespace Content.Shared.Chat
|
||||
AudioPath = audioPath;
|
||||
AudioVolume = audioVolume;
|
||||
}
|
||||
|
||||
public ChatMessage(ChatMessage copyFrom)
|
||||
{
|
||||
Channel = copyFrom.Channel;
|
||||
Message = copyFrom.Message;
|
||||
WrappedMessage = copyFrom.WrappedMessage;
|
||||
SenderEntity = copyFrom.SenderEntity;
|
||||
SenderKey = copyFrom.SenderKey;
|
||||
HideChat = copyFrom.HideChat;
|
||||
MessageColorOverride = copyFrom.MessageColorOverride;
|
||||
AudioPath = copyFrom.AudioPath;
|
||||
AudioVolume = copyFrom.AudioVolume;
|
||||
Read = copyFrom.Read;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -30,6 +30,11 @@ public sealed class InteractUsingEvent : HandledEntityEventArgs
|
||||
/// </summary>
|
||||
public EntityCoordinates ClickLocation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Stellar - set to false if this interaction shouldn't have an interaction particle
|
||||
/// </summary>
|
||||
public bool InteractionParticle = true;
|
||||
|
||||
public InteractUsingEvent(EntityUid user, EntityUid used, EntityUid target, EntityCoordinates clickLocation)
|
||||
{
|
||||
// Interact using should not have the same used and target.
|
||||
|
||||
@@ -31,6 +31,7 @@ using Content.Shared.UserInterface;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Wall;
|
||||
using Content.Shared._Goobstation.DoAfter; // Goobstation
|
||||
using Content.Shared.Inventory.VirtualItem; // Stellar - interaction particles
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Input;
|
||||
@@ -1073,8 +1074,8 @@ namespace Content.Shared.Interaction
|
||||
var userInteractUsingEvent = new UserInteractUsingEvent(user, used, target, clickLocation);
|
||||
RaiseLocalEvent(user, userInteractUsingEvent, true);
|
||||
|
||||
DoContactInteraction(user, used, null, true, interactUsingEvent); // Stellar - interaction particles
|
||||
DoContactInteraction(user, target, used, true, interactUsingEvent); // Stellar - interaction particles
|
||||
DoContactInteraction(user, used, null, true, interactUsingEvent, interactionParticles: interactUsingEvent.InteractionParticle); // Stellar - interaction particles
|
||||
DoContactInteraction(user, target, used, true, interactUsingEvent, interactionParticles: interactUsingEvent.InteractionParticle); // Stellar - interaction particles
|
||||
// Contact interactions are currently only used for forensics, so we don't raise used -> target
|
||||
if (interactUsingEvent.Handled || userInteractUsingEvent.Handled)
|
||||
return true;
|
||||
@@ -1437,7 +1438,8 @@ namespace Content.Shared.Interaction
|
||||
/// <param name="predicted">Whether this interaction is predicted. <see cref="uidA"/> is assumed to be the client entity.</param>
|
||||
/// <param name="args">Optional handleable entity event to check.</param>
|
||||
/// <param name="interactionParticles">Whether to spawn interaction particles on this contact.</param>
|
||||
public void DoContactInteraction(EntityUid uidA, EntityUid? uidB, EntityUid? used, bool predicted, HandledEntityEventArgs? args = null, bool interactionParticles = true) // Stellar/ES - interaction particles
|
||||
/// <param name="interactionParticleType">The type of interaction particle to spawn for this event.</param>
|
||||
public void DoContactInteraction(EntityUid uidA, EntityUid? uidB, EntityUid? used, bool predicted, HandledEntityEventArgs? args = null, bool interactionParticles = true, StellarInteractionParticleType interactionParticleType = StellarInteractionParticleType.Use) // Stellar/ES - interaction particles
|
||||
{
|
||||
if (uidB == null || args?.Handled == false)
|
||||
return;
|
||||
@@ -1459,7 +1461,7 @@ namespace Content.Shared.Interaction
|
||||
RaiseLocalEvent(uidB.Value, ev);
|
||||
|
||||
// Begin Stellar/ES Additions - Interaction particles
|
||||
if (!interactionParticles)
|
||||
if (!interactionParticles || HasComp<VirtualItemComponent>(uidB))
|
||||
return;
|
||||
|
||||
if (_net.IsServer)
|
||||
@@ -1468,11 +1470,11 @@ namespace Content.Shared.Interaction
|
||||
? Filter.PvsExcept(uidA, entityManager: EntityManager)
|
||||
: Filter.Pvs(uidA, entityManager: EntityManager);
|
||||
|
||||
RaiseNetworkEvent(new StellarInteractionParticleEvent(GetNetEntity(uidA), GetNetEntity(used), GetNetEntity(uidB.Value), false), filter);
|
||||
RaiseNetworkEvent(new StellarInteractionParticleEvent(GetNetEntity(uidA), GetNetEntity(used), GetNetEntity(uidB.Value), false, interactionParticleType), filter);
|
||||
}
|
||||
else if (_gameTiming.IsFirstTimePredicted)
|
||||
{
|
||||
var evt = new StellarInteractionParticleEvent(GetNetEntity(uidA), GetNetEntity(used), GetNetEntity(uidB.Value), true);
|
||||
var evt = new StellarInteractionParticleEvent(GetNetEntity(uidA), GetNetEntity(used), GetNetEntity(uidB.Value), true, interactionParticleType);
|
||||
RaiseLocalEvent(evt);
|
||||
}
|
||||
// End Stellar/ES Additions - Interaction particles
|
||||
|
||||
@@ -122,6 +122,11 @@ public sealed class MindExamineSystem : EntitySystem
|
||||
else
|
||||
ent.Comp.State = MindState.None;
|
||||
|
||||
// DeltaV - SSD Recency START
|
||||
var ev = new MindStateUpdatedEvent(ent.Comp.State);
|
||||
RaiseLocalEvent(ent, ref ev);
|
||||
// DeltaV END
|
||||
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared._ST.Interaction; // Stellar - interaction particles
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Alert;
|
||||
@@ -545,7 +546,7 @@ public sealed class PullingSystem : EntitySystem
|
||||
|
||||
// Pulling confirmed
|
||||
|
||||
_interaction.DoContactInteraction(pullableUid, pullerUid, null, true); // Stellar - Interaction particles
|
||||
_interaction.DoContactInteraction(pullerUid, pullableUid,null, true, interactionParticleType: StellarInteractionParticleType.Pull); // Stellar - Interaction particles
|
||||
|
||||
// Use net entity so it's consistent across client and server.
|
||||
pullableComp.PullJointId = $"pull-joint-{GetNetEntity(pullableUid)}";
|
||||
|
||||
@@ -18,11 +18,13 @@ public sealed partial class RatKingComponent : Component
|
||||
[DataField("actionRaiseArmyEntity")]
|
||||
public EntityUid? ActionRaiseArmyEntity;
|
||||
|
||||
// Delta-V - switched to a base cost modified by the amount of living servants
|
||||
/// <summary>
|
||||
/// The amount of hunger one use of Raise Army consumes
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("hungerPerArmyUse", required: true)]
|
||||
public float HungerPerArmyUse = 25f;
|
||||
public float HungerPerArmyUse = 10f;
|
||||
// end DeltaV
|
||||
|
||||
/// <summary>
|
||||
/// The entity prototype of the mob that Raise Army summons
|
||||
|
||||
@@ -50,11 +50,12 @@ public sealed partial class SSDIndicatorComponent : Component
|
||||
// DeltaV - SSD Recency Additions START
|
||||
|
||||
/// <summary>
|
||||
/// The time at which the Entity became SSD.
|
||||
/// The time at which the player became SSD.
|
||||
/// This will remain unset on SSD entities that never had minds attached, such as newly spawn ghost roles.
|
||||
/// </summary>
|
||||
[AutoNetworkedField, AutoPausedField]
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan SsdSince = TimeSpan.Zero;
|
||||
public TimeSpan? SsdSince;
|
||||
|
||||
/// <summary>
|
||||
/// The icon displayed next to the associated entity when it is recently SSD (stage 2).
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using Content.Shared._DV.CCVars; // DeltaV - SSD Recency
|
||||
using Content.Shared._DV.Mind; // DeltaV - SSD Recency
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Examine; // DeltaV - SSD Recency
|
||||
using Content.Shared.Mobs.Systems; // DeltaV - SSD Recency
|
||||
using Content.Shared.Mind.Components; // DeltaV - SSD Recency
|
||||
using Content.Shared.StatusEffectNew;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Player;
|
||||
@@ -19,18 +22,20 @@ public sealed class SSDIndicatorSystem : EntitySystem
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!; // DeltaV - SSD Recency
|
||||
|
||||
private bool _icSsdSleep;
|
||||
private float _icSsdSleepTime;
|
||||
|
||||
private TimeSpan _cryoableSsdSeconds; // DeltaV
|
||||
private TimeSpan _recentSsdSeconds; // DeltaV
|
||||
private TimeSpan _cryoableSsdSeconds; // DeltaV - SSD Recency
|
||||
private TimeSpan _recentSsdSeconds; // DeltaV - SSD Recency
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<SSDIndicatorComponent, PlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<SSDIndicatorComponent, PlayerDetachedEvent>(OnPlayerDetached);
|
||||
SubscribeLocalEvent<SSDIndicatorComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<SSDIndicatorComponent, MindStateUpdatedEvent>(OnMindStateUpdated); // DeltaV - SSD Recency
|
||||
|
||||
_cfg.OnValueChanged(CCVars.ICSSDSleep, obj => _icSsdSleep = obj, true);
|
||||
_cfg.OnValueChanged(CCVars.ICSSDSleepTime, obj => _icSsdSleepTime = obj, true);
|
||||
@@ -43,12 +48,15 @@ public sealed class SSDIndicatorSystem : EntitySystem
|
||||
// DeltaV - SSD Recency START
|
||||
private void OnExamine(Entity<SSDIndicatorComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
if (!ent.Comp.IsSSD)
|
||||
if (ent.Comp.SsdSince is not { } ssdSince)
|
||||
return;
|
||||
|
||||
if (_mobState.IsDead(ent))
|
||||
return;
|
||||
|
||||
using (args.PushGroup(nameof(SSDIndicatorComponent)))
|
||||
{
|
||||
var timestamp = (_timing.CurTime - ent.Comp.SsdSince).ToString("%hh':'mm':'ss");
|
||||
var timestamp = (_timing.CurTime - ssdSince).ToString("%hh':'mm':'ss");
|
||||
args.PushMarkup(Loc.GetString("ssd-examine-duration", ("time", timestamp)));
|
||||
args.PushMarkup(Loc.GetString($"ssd-examine-{GetStage(ent).ToString().ToLower()}"));
|
||||
}
|
||||
@@ -58,17 +66,28 @@ public sealed class SSDIndicatorSystem : EntitySystem
|
||||
{
|
||||
var curTime = _timing.CurTime;
|
||||
|
||||
if (ent.Comp.SsdSince + _cryoableSsdSeconds < curTime)
|
||||
if (ent.Comp.SsdSince + _recentSsdSeconds >= curTime)
|
||||
{
|
||||
return SsdStage.Cryoable;
|
||||
return SsdStage.VeryRecent;
|
||||
}
|
||||
|
||||
if (ent.Comp.SsdSince + _recentSsdSeconds < curTime)
|
||||
if (ent.Comp.SsdSince + _cryoableSsdSeconds >= curTime)
|
||||
{
|
||||
return SsdStage.Recent;
|
||||
}
|
||||
|
||||
return SsdStage.VeryRecent;
|
||||
return SsdStage.Cryoable;
|
||||
}
|
||||
|
||||
public void OnMindStateUpdated(Entity<SSDIndicatorComponent> ent, ref MindStateUpdatedEvent args)
|
||||
{
|
||||
if (args.State is MindState.SSD or MindState.DeadSSD) {
|
||||
if (ent.Comp.SsdSince is null)
|
||||
ent.Comp.SsdSince = _timing.CurTime;
|
||||
}
|
||||
else
|
||||
ent.Comp.SsdSince = null;
|
||||
Dirty(ent, ent.Comp);
|
||||
}
|
||||
// DeltaV END
|
||||
|
||||
@@ -96,8 +115,6 @@ public sealed class SSDIndicatorSystem : EntitySystem
|
||||
component.FallAsleepTime = _timing.CurTime + TimeSpan.FromSeconds(_icSsdSleepTime);
|
||||
}
|
||||
|
||||
component.SsdSince = _timing.CurTime; // DeltaV - SSD Recency
|
||||
|
||||
Dirty(uid, component);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ public abstract class SharedEmitSoundSystem : EntitySystem
|
||||
if (_whitelistSystem.IsWhitelistFail(component.Blacklist, args.User))
|
||||
{
|
||||
TryEmitSound(uid, component, args.User);
|
||||
args.InteractionParticle = true; // Stellar - interaction particles
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ public abstract partial class SharedStackSystem : EntitySystem
|
||||
|
||||
var localRotation = Transform(args.Used).LocalRotation;
|
||||
_storage.PlayPickupAnimation(args.Used, popupPos, userCoords, localRotation, args.User);
|
||||
args.InteractionParticle = false; // Stellar
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Roles.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Uprising;
|
||||
|
||||
public sealed partial class ConversionAcceptActionEvent : InstantActionEvent
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public EntProtoId MindRole;
|
||||
|
||||
[DataField(required: true)]
|
||||
public EntProtoId<MindRoleComponent> RemoveMindRole;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Shared.Uprising;
|
||||
|
||||
public sealed class ConversionAcceptActionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _role = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MindContainerComponent, ConversionAcceptActionEvent>(OnAcceptConversion);
|
||||
}
|
||||
|
||||
private void OnAcceptConversion(Entity<MindContainerComponent> ent, ref ConversionAcceptActionEvent args)
|
||||
{
|
||||
if (_mind.GetMind(ent) is not { } mind)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
if (_net.IsServer)
|
||||
{
|
||||
_role.MindAddRole(mind, args.MindRole);
|
||||
_role.MindRemoveRole(mind, args.RemoveMindRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Content.Shared.Actions;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Uprising;
|
||||
|
||||
public sealed partial class ConversionOfferActionEvent : EntityTargetActionEvent
|
||||
{
|
||||
[DataField(readOnly: true)]
|
||||
public List<Type> IncompatibleMindRoleTypes;
|
||||
|
||||
[DataField]
|
||||
public EntProtoId AcceptAction;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Roles.Components;
|
||||
|
||||
namespace Content.Shared.Uprising;
|
||||
|
||||
public sealed class ConversionOfferActionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _role = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MindRoleComponent, ConversionOfferActionEvent>(OnOffer);
|
||||
}
|
||||
|
||||
private void OnOffer(Entity<MindRoleComponent> ent, ref ConversionOfferActionEvent args)
|
||||
{
|
||||
if (_mind.GetMind(args.Target) is not { } mind)
|
||||
return;
|
||||
|
||||
foreach (var incompatible in args.IncompatibleMindRoleTypes)
|
||||
{
|
||||
if (_role.MindHasRole(mind, incompatible, out _))
|
||||
return;
|
||||
}
|
||||
|
||||
_actions.AddAction(args.Target, args.AcceptAction);
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,11 @@ public sealed class AfterActivatableUIOpenEvent(EntityUid user) : EntityEventArg
|
||||
/// The player that opened the UI.
|
||||
/// </summary>
|
||||
public readonly EntityUid User = user;
|
||||
|
||||
/// <summary>
|
||||
/// Stellar - if an interaction particle should be played for this event.
|
||||
/// </summary>
|
||||
public bool InteractionParticle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -21,6 +21,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -72,7 +73,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
|
||||
args.Verbs.Add(new ActivationVerb
|
||||
{
|
||||
Act = () => InteractUI(args.User, uid, component),
|
||||
Act = () => InteractUI(args.User, (uid, component)), // Stellar - interaction particles
|
||||
Text = Loc.GetString(component.VerbText),
|
||||
// TODO VERB ICON find a better icon
|
||||
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")),
|
||||
@@ -86,7 +87,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
|
||||
args.Verbs.Add(new Verb
|
||||
{
|
||||
Act = () => InteractUI(args.User, uid, component),
|
||||
Act = () => InteractUI(args.User, (uid, component)), // Stellar - interaction particles
|
||||
Text = Loc.GetString(component.VerbText),
|
||||
// TODO VERB ICON find a better icon
|
||||
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")),
|
||||
@@ -133,7 +134,8 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
if (component.RequiredItems != null)
|
||||
return;
|
||||
|
||||
args.Handled = InteractUI(args.User, uid, component);
|
||||
var interactionParticle = false; // Stellar - interaction particles
|
||||
args.Handled = InteractUI(args.User, uid, component, ref interactionParticle); // Stellar - interaction particles
|
||||
}
|
||||
|
||||
private void OnActivate(EntityUid uid, ActivatableUIComponent component, ActivateInWorldEvent args)
|
||||
@@ -147,7 +149,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
if (component.RequiredItems != null)
|
||||
return;
|
||||
|
||||
args.Handled = InteractUI(args.User, uid, component);
|
||||
args.Handled = InteractUI(args.User, uid, component, ref args.InteractionParticle); // Stellar - interaction particles
|
||||
}
|
||||
|
||||
private void OnInteractUsing(EntityUid uid, ActivatableUIComponent component, InteractUsingEvent args)
|
||||
@@ -164,7 +166,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
if (_whitelistSystem.IsWhitelistFail(component.RequiredItems, args.Used))
|
||||
return;
|
||||
|
||||
args.Handled = InteractUI(args.User, uid, component);
|
||||
args.Handled = InteractUI(args.User, uid, component, ref args.InteractionParticle); // Stellar - interaction particles
|
||||
}
|
||||
|
||||
private void OnUIClose(EntityUid uid, ActivatableUIComponent component, BoundUIClosedEvent args)
|
||||
@@ -180,13 +182,23 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
SetCurrentSingleUser(uid, null, component);
|
||||
}
|
||||
|
||||
private bool InteractUI(EntityUid user, EntityUid uiEntity, ActivatableUIComponent aui)
|
||||
// Begin Stellar - interaction particles
|
||||
private void InteractUI(EntityUid user, Entity<ActivatableUIComponent> ui)
|
||||
{
|
||||
var interactionParticle = false;
|
||||
InteractUI(user, ui, ui, ref interactionParticle);
|
||||
_interaction.DoContactInteraction(user, ui, null, true, interactionParticles: interactionParticle);
|
||||
}
|
||||
// End Stellar - interaction particles
|
||||
|
||||
private bool InteractUI(EntityUid user, EntityUid uiEntity, ActivatableUIComponent aui, ref bool interactionParticle) // Stellar - interaction particles
|
||||
{
|
||||
if (aui.Key == null || !_uiSystem.HasUi(uiEntity, aui.Key))
|
||||
return false;
|
||||
|
||||
if (_uiSystem.IsUiOpen(uiEntity, aui.Key, user))
|
||||
{
|
||||
interactionParticle = false; // Stellar - interaction particles
|
||||
_uiSystem.CloseUi(uiEntity, aui.Key, user);
|
||||
return true;
|
||||
}
|
||||
@@ -243,6 +255,8 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
var aae = new AfterActivatableUIOpenEvent(user);
|
||||
RaiseLocalEvent(uiEntity, aae);
|
||||
|
||||
interactionParticle = aae.InteractionParticle; // Stellar
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ using Content.Shared.Weapons.Melee.Events;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Content.Shared.Weapons.Ranged.Events;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Content.Shared.Wieldable.Components; // Starlight | ES Screenshake
|
||||
using Content.Shared.Zombies; // DeltaV - Buff Zombies
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
@@ -597,18 +598,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
if (damageResult.GetTotal() > FixedPoint2.Zero)
|
||||
{
|
||||
DoDamageEffect(targets, user, targetXform);
|
||||
|
||||
// ES START
|
||||
// dog shit copy plaste but thats melee for you
|
||||
var userShakeRotation = new ESScreenshakeParameters()
|
||||
{ Trauma = 0.08f, DecayRate = 1.0f, Frequency = 0.009f };
|
||||
var otherShakeTranslation = new ESScreenshakeParameters() { Trauma = 0.45f, DecayRate = 1.1f, Frequency = 0.04f };
|
||||
_shake.Screenshake(user, null, userShakeRotation);
|
||||
foreach (var shakeTarget in targets)
|
||||
{
|
||||
_shake.Screenshake(shakeTarget, otherShakeTranslation, null);
|
||||
}
|
||||
// ES END
|
||||
DoScreenshake(meleeUid, damageResult, user, targets); // Starlight | ES Screenshake
|
||||
}
|
||||
}
|
||||
|
||||
@@ -774,21 +764,10 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
_meleeSound.PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component);
|
||||
}
|
||||
|
||||
// ES START
|
||||
// dog shit copy plaste but thats melee for you
|
||||
var userShakeRotation = new ESScreenshakeParameters()
|
||||
{ Trauma = 0.08f, DecayRate = 1.0f, Frequency = 0.009f };
|
||||
var otherShakeTranslation = new ESScreenshakeParameters() { Trauma = 0.45f, DecayRate = 1.1f, Frequency = 0.04f };
|
||||
_shake.Screenshake(user, null, userShakeRotation);
|
||||
foreach (var shakeTarget in targets)
|
||||
{
|
||||
_shake.Screenshake(shakeTarget, otherShakeTranslation, null);
|
||||
}
|
||||
// ES END
|
||||
|
||||
if (appliedDamage.GetTotal() > FixedPoint2.Zero)
|
||||
{
|
||||
DoDamageEffect(targets, user, Transform(targets[0]));
|
||||
DoScreenshake(meleeUid, damage, user, targets); // Starlight | ES Screenshake
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1104,4 +1083,55 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Starlight begin | ES Screenshake
|
||||
private void DoScreenshake(EntityUid weapon, DamageSpecifier damage, EntityUid attacker, List<EntityUid> targets)
|
||||
{
|
||||
if(damage.GetTotal()>4) // only show to others if it hurts real bad // DeltaV - reduce from 8 to 4
|
||||
{
|
||||
var otherTranslation = new ESScreenshakeParameters
|
||||
{
|
||||
Trauma = 0.45f,
|
||||
DecayRate = 1.1f,
|
||||
Frequency = 0.04f,
|
||||
};
|
||||
foreach(var target in targets)
|
||||
_shake.Screenshake(target, otherTranslation, null);
|
||||
}
|
||||
|
||||
// only show to attacker if they put real oompf into it, or the weapon is just THAT strong
|
||||
// var bluntRequirement = damage.DamageDict.TryGetValue(BluntDamageName, out var blunt) && blunt >= 20; // DeltaV - unused
|
||||
var isWielding = TryComp<WieldableComponent>(weapon, out var wieldable) && wieldable.Wielded;
|
||||
|
||||
// DeltaV - unused
|
||||
// if (!bluntRequirement && !wieldRequirement)
|
||||
// return;
|
||||
|
||||
// DeltaV - heavy/light screenshake variants START
|
||||
ESScreenshakeParameters userRotation;
|
||||
if (damage.GetTotal() >= 15 || isWielding)
|
||||
{
|
||||
// heavy damage or two-handed
|
||||
userRotation = new ESScreenshakeParameters
|
||||
{
|
||||
Trauma = 0.08f,
|
||||
DecayRate = 1,
|
||||
Frequency = 0.009f,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// light damage
|
||||
userRotation = new ESScreenshakeParameters
|
||||
{
|
||||
Trauma = 0.06f,
|
||||
DecayRate = 1,
|
||||
Frequency = 0.0045f,
|
||||
};
|
||||
}
|
||||
// DeltaV END
|
||||
|
||||
_shake.Screenshake(attacker, null, userRotation);
|
||||
}
|
||||
//Starlight end
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.Actions;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class DeleteOnPerformedActionComponent : Component;
|
||||
@@ -0,0 +1,18 @@
|
||||
using Content.Shared.Actions.Events;
|
||||
|
||||
namespace Content.Shared._DV.Actions;
|
||||
|
||||
public sealed class DeleteOnPerformedActionSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DeleteOnPerformedActionComponent, ActionPerformedEvent>(OnActionPerformed);
|
||||
}
|
||||
|
||||
private void OnActionPerformed(Entity<DeleteOnPerformedActionComponent> ent, ref ActionPerformedEvent args)
|
||||
{
|
||||
PredictedQueueDel(ent);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -36,6 +38,8 @@ public sealed partial class CosmicColossusComponent : Component
|
||||
|
||||
[DataField] public EntProtoId Attack1Vfx = "CosmicColossusAttack1Vfx";
|
||||
|
||||
[DataField] public EntProtoId BuffVfx = "ColossusBuffVfx";
|
||||
|
||||
[DataField] public EntProtoId TileDetonations = "MobTileDamageZone";
|
||||
|
||||
[DataField] public EntProtoId EffigyPrototype = "CosmicEffigy";
|
||||
@@ -52,13 +56,23 @@ public sealed partial class CosmicColossusComponent : Component
|
||||
|
||||
[DataField] public TimeSpan HibernationWait = TimeSpan.FromSeconds(30);
|
||||
|
||||
[DataField] public TimeSpan DeathWait = TimeSpan.FromMinutes(15);
|
||||
[DataField] public TimeSpan DeathWaitSpawn = TimeSpan.FromMinutes(15);
|
||||
|
||||
[DataField] public TimeSpan DeathWaitEffigy = TimeSpan.FromMinutes(10);
|
||||
|
||||
[DataField] public bool Attacking;
|
||||
|
||||
[DataField] public bool Hibernating;
|
||||
|
||||
[DataField] public bool Timed;
|
||||
|
||||
[DataField] public short CompletedEffigies;
|
||||
|
||||
[DataField] public short MaxEffigies = 3;
|
||||
|
||||
[DataField] public DamageSpecifier BonusDamage = new();
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Shared._DV.Mind;
|
||||
|
||||
using Content.Shared.Mind.Components;
|
||||
|
||||
[ByRefEvent]
|
||||
public record struct MindStateUpdatedEvent(MindState State);
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared._DV.Movement.Components;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
@@ -7,12 +8,19 @@ namespace Content.Shared.Movement.Systems;
|
||||
public abstract partial class SharedJetpackSystem
|
||||
{
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
|
||||
private void OnJetpackToggle(Entity<JetpackComponent> jetpack, ref ToggleJetpackEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!_actionBlocker.CanComplexInteract(args.Performer))
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("jetpack-too-complex"), jetpack, args.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
jetpack.Comp.AutomaticMode = !jetpack.Comp.AutomaticMode;
|
||||
jetpack.Comp.AutomaticUser = args.Performer;
|
||||
Dirty(jetpack);
|
||||
|
||||
@@ -42,6 +42,9 @@ public sealed class PagerSystem : EntitySystem
|
||||
|
||||
private void OnAfterInteract(Entity<PagerComponent> ent, ref AfterInteractEvent args)
|
||||
{
|
||||
if (!args.CanReach)
|
||||
return;
|
||||
|
||||
if (args.Handled || !TryComp<DeviceNetworkComponent>(args.Target, out var targetNetwork) || !HasComp<PageSenderComponent>(args.Target))
|
||||
return;
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.Psionics.Components.PsionicPowers;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class FracturedFormBodyComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity uid of the form that is currently being controlled.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid ControllingForm { get; set; }
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
using Content.Shared.Cloning;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._DV.Psionics.Components.PsionicPowers;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||
public sealed partial class FracturedFormPowerComponent : BasePsionicPowerComponent
|
||||
{
|
||||
public override EntProtoId ActionProtoId { get; set; } = "ActionFracturedForm";
|
||||
|
||||
public override string PowerName { get; set; } = "psionic-power-name-fractured-form";
|
||||
|
||||
public override int MinGlimmerChanged { get; set; } = 5;
|
||||
|
||||
public override int MaxGlimmerChanged { get; set; } = 15;
|
||||
|
||||
/// <summary>
|
||||
/// The chance for the clone to have the starting gear of the psionic.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float HasGearChance = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// The chance for the clone to have a different species than the psionic.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float DifferentSpeciesChance = 0.6f;
|
||||
|
||||
/// <summary>
|
||||
/// These are settings for creating the second body.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<CloningSettingsPrototype> CopyNaked = "CloningPod";
|
||||
|
||||
/// <summary>
|
||||
/// These are settings for creating the second body.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<CloningSettingsPrototype> CopyClothed = "Antag";
|
||||
|
||||
/// <summary>
|
||||
/// These are settings for creating the second body.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<JobPrototype> VisitorJob = "Passenger";
|
||||
|
||||
/// <summary>
|
||||
/// These are settings for creating the second body.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<JobPrototype>? NakedJob; // Scary null, but we explicitly want no job for naked spawns.
|
||||
|
||||
/// <summary>
|
||||
/// The minimum time between forced swaps.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan NextSwapMinTime = TimeSpan.FromMinutes(5);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum time between swaps.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan NextSwapMaxTime = TimeSpan.FromMinutes(20);
|
||||
|
||||
/// <summary>
|
||||
/// The current timer for when the next swap is forced.
|
||||
/// </summary>
|
||||
[DataField, AutoPausedField]
|
||||
public TimeSpan NextSwap = TimeSpan.FromMinutes(5);
|
||||
|
||||
/// <summary>
|
||||
/// The minimum time between voluntary swaps by sleeping.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan VoluntarySwapCooldown = TimeSpan.FromSeconds(10);
|
||||
|
||||
/// <summary>
|
||||
/// The minimum time between voluntary swaps by sleeping.
|
||||
/// </summary>
|
||||
[DataField, AutoPausedField]
|
||||
public TimeSpan NextVoluntarySwap = TimeSpan.FromSeconds(10);
|
||||
|
||||
/// <summary>
|
||||
/// The length of the DoAfter for swapping bodies.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float ManualSwapTime = 5f;
|
||||
|
||||
/// <summary>
|
||||
/// The time the psionic needs to be asleep to swap bodies.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan SwapTimeAsleep = TimeSpan.FromSeconds(3);
|
||||
|
||||
/// <summary>
|
||||
/// The time length where the warning is sent before the forced swap.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan WarningTimeBeforeSleep = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// <summary>
|
||||
/// A boolean check for when the person has been warned.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool SleepWarned;
|
||||
|
||||
/// <summary>
|
||||
/// The bodies that the psionic user can swap into.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<EntityUid> Bodies { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The sound that plays when the swap happens.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier SwapSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVariation(0.05f),
|
||||
};
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared._DV.Psionics.Events.PowerActionEvents;
|
||||
|
||||
public sealed partial class FracturedFormPowerActionEvent : InstantActionEvent;
|
||||
@@ -1,7 +0,0 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.Psionics.Events.PowerDoAfterEvents;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class FracturedFormDoAfterEvent : SimpleDoAfterEvent;
|
||||
@@ -1,150 +0,0 @@
|
||||
using Content.Shared._DV.Psionics.Components.PsionicPowers;
|
||||
using Content.Shared._DV.Psionics.Events;
|
||||
using Content.Shared._DV.Psionics.Events.PowerActionEvents;
|
||||
using Content.Shared._DV.Psionics.Events.PowerDoAfterEvents;
|
||||
using Content.Shared.Bed.Sleep;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.SSDIndicator;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Shared._DV.Psionics.Systems.PsionicPowers;
|
||||
|
||||
public abstract class SharedFracturedFormPowerSystem : BasePsionicPowerSystem<FracturedFormPowerComponent, FracturedFormPowerActionEvent>
|
||||
{
|
||||
[Dependency] protected readonly SharedChatSystem Chat = default!;
|
||||
[Dependency] protected readonly MobStateSystem MobState = default!;
|
||||
[Dependency] protected readonly SleepingSystem Sleeping = default!;
|
||||
|
||||
protected EntityQuery<FracturedFormPowerComponent> FracturedQuery;
|
||||
private EntityQuery<ForcedSleepingStatusEffectComponent> _forcedSleepQuery;
|
||||
protected EntityQuery<MindContainerComponent> MindContainerQuery;
|
||||
private EntityQuery<MobStateComponent> _mobStateQuery;
|
||||
protected EntityQuery<SSDIndicatorComponent> SsdQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<FracturedFormPowerComponent, FracturedFormDoAfterEvent>(OnDoAfter);
|
||||
SubscribeLocalEvent<FracturedFormBodyComponent, PlayerDetachedEvent>(OnPlayerDetached, after: [typeof(SSDIndicatorSystem)]);
|
||||
SubscribeLocalEvent<FracturedFormBodyComponent, ExaminedEvent>(OnExamine);
|
||||
|
||||
FracturedQuery = GetEntityQuery<FracturedFormPowerComponent>();
|
||||
_forcedSleepQuery = GetEntityQuery<ForcedSleepingStatusEffectComponent>();
|
||||
MindContainerQuery = GetEntityQuery<MindContainerComponent>();
|
||||
_mobStateQuery = GetEntityQuery<MobStateComponent>();
|
||||
SsdQuery = GetEntityQuery<SSDIndicatorComponent>();
|
||||
}
|
||||
|
||||
protected override void OnPowerUsed(Entity<FracturedFormPowerComponent> psionic, ref FracturedFormPowerActionEvent args)
|
||||
{
|
||||
if (!CanSwap(psionic))
|
||||
{
|
||||
Popup.PopupClient(Loc.GetString("psionic-power-fractured-form-nobodies"), psionic, args.Performer, PopupType.Large);
|
||||
return;
|
||||
}
|
||||
|
||||
Chat.TryEmoteWithChat(psionic.Owner, "Yawn");
|
||||
Popup.PopupClient(Loc.GetString("psionic-power-fractured-form-sleepy"), psionic, args.Performer, PopupType.LargeCaution);
|
||||
|
||||
var doAfterArgs = new DoAfterArgs(EntityManager, args.Performer, psionic.Comp.ManualSwapTime, new FracturedFormDoAfterEvent(), psionic);
|
||||
|
||||
if (!DoAfter.TryStartDoAfter(doAfterArgs, out var doAfterId))
|
||||
return;
|
||||
|
||||
psionic.Comp.SaveDoAfterId(doAfterId.Value);
|
||||
Dirty(psionic);
|
||||
AfterPowerUsed(psionic, args.Performer);
|
||||
}
|
||||
|
||||
private void OnDoAfter(Entity<FracturedFormPowerComponent> psionic, ref FracturedFormDoAfterEvent args)
|
||||
{
|
||||
if (args.Cancelled || args.Handled)
|
||||
return;
|
||||
args.Handled = true;
|
||||
psionic.Comp.RemoveSavedDoAfterId();
|
||||
|
||||
Dirty(psionic);
|
||||
Sleeping.TrySleeping(psionic.Owner);
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(Entity<FracturedFormBodyComponent> body, ref PlayerDetachedEvent args)
|
||||
{
|
||||
if (!SsdQuery.TryComp(body, out var comp)
|
||||
|| FracturedQuery.HasComp(body))
|
||||
return;
|
||||
|
||||
comp.IsSSD = false;
|
||||
}
|
||||
|
||||
private void OnExamine(Entity<FracturedFormBodyComponent> psionic, ref ExaminedEvent args)
|
||||
{
|
||||
if (HasComp<FracturedFormPowerComponent>(psionic))
|
||||
return;
|
||||
|
||||
if (TryComp<FracturedFormPowerComponent>(args.Examiner, out var fracturedHost) && fracturedHost.Bodies.Contains(psionic.Owner))
|
||||
args.PushMarkup($"[color=yellow]{Loc.GetString("psionic-power-fractured-form-examine-self", ("ent", psionic))}[/color]");
|
||||
else
|
||||
args.PushMarkup($"[color=yellow]{Loc.GetString("psionic-power-fractured-form-ssd", ("ent", psionic))}[/color]");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We're overriding the dispelled method to introduce some special behavior.
|
||||
/// </summary>
|
||||
protected override void OnDispelled(Entity<FracturedFormPowerComponent> psionic, ref DispelledEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
args.Handled = true;
|
||||
|
||||
// This resets the swap timer.
|
||||
var randomTime = Random.Next(psionic.Comp.NextSwapMinTime, psionic.Comp.NextSwapMaxTime);
|
||||
psionic.Comp.NextSwap = Timing.CurTime + randomTime;
|
||||
|
||||
if (psionic.Comp.GetDoAfterId() is not { } doAfterId)
|
||||
{
|
||||
Popup.PopupClient(Loc.GetString("psionic-power-fractured-form-dispelled"), psionic, PopupType.MediumCaution);
|
||||
return;
|
||||
}
|
||||
|
||||
DoAfter.Cancel(doAfterId);
|
||||
Popup.PopupClient(Loc.GetString("psionic-dispelled"), psionic, PopupType.MediumCaution);
|
||||
psionic.Comp.RemoveSavedDoAfterId();
|
||||
}
|
||||
|
||||
protected bool IsValidBody(Entity<FracturedFormPowerComponent> psionic, EntityUid otherBody)
|
||||
{
|
||||
if (otherBody == psionic.Owner)
|
||||
return false;
|
||||
if (!psionic.Comp.Bodies.Contains(otherBody))
|
||||
return false;
|
||||
if (!MindContainerQuery.TryComp(otherBody, out var cmind))
|
||||
return false;
|
||||
if (cmind.HasMind)
|
||||
return false;
|
||||
if (_forcedSleepQuery.HasComp(otherBody))
|
||||
return false;
|
||||
if (!_mobStateQuery.TryComp(otherBody, out var mobState))
|
||||
return false;
|
||||
if (MobState.IsIncapacitated(otherBody, mobState))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanSwap(Entity<FracturedFormPowerComponent> psionic)
|
||||
{
|
||||
foreach (var body in psionic.Comp.Bodies)
|
||||
{
|
||||
if (IsValidBody(psionic, body))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Mind;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Shared._DV.Roles;
|
||||
|
||||
public sealed class ActionGrantRoleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ActionGrantRoleComponent, EntGotInsertedIntoContainerMessage>(OnGotInserted);
|
||||
SubscribeLocalEvent<ActionGrantRoleComponent, EntGotRemovedFromContainerMessage>(OnGotRemoved);
|
||||
}
|
||||
|
||||
private void OnGotInserted(Entity<ActionGrantRoleComponent> ent, ref EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (_net.IsClient || !TryComp<MindComponent>(args.Container.Owner, out var mind) || mind.CurrentEntity is not { } mindContainer)
|
||||
return;
|
||||
|
||||
_actions.GrantContainedActions(mindContainer, ent.Owner);
|
||||
}
|
||||
|
||||
private void OnGotRemoved(Entity<ActionGrantRoleComponent> ent, ref EntGotRemovedFromContainerMessage args)
|
||||
{
|
||||
if (_net.IsClient || !TryComp<MindComponent>(args.Container.Owner, out var mind) || mind.CurrentEntity is not { } mindContainer)
|
||||
return;
|
||||
|
||||
_actions.RemoveProvidedActions(mindContainer, ent.Owner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.Roles;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class ActionGrantRoleComponent : Component;
|
||||
@@ -0,0 +1,6 @@
|
||||
using Content.Shared.Roles.Components;
|
||||
|
||||
namespace Content.Shared._DV.Roles;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class InsurgentRoleComponent : BaseMindRoleComponent;
|
||||
@@ -0,0 +1,6 @@
|
||||
using Content.Shared.Roles.Components;
|
||||
|
||||
namespace Content.Shared._DV.Roles;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class LoyalistRoleComponent : BaseMindRoleComponent;
|
||||
@@ -0,0 +1,12 @@
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Roles.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._DV.Roles;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class ObjectiveRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public List<EntProtoId<ObjectiveComponent>> Objectives;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using Content.Shared.Roles.Components;
|
||||
|
||||
namespace Content.Shared._DV.Roles;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class UprisingInconvertibleRoleComponent : BaseMindRoleComponent;
|
||||
@@ -0,0 +1,16 @@
|
||||
using Content.Shared.Roles.Components;
|
||||
|
||||
namespace Content.Shared._DV.Roles;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class UprisingRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public UprisingSide Side;
|
||||
}
|
||||
|
||||
public enum UprisingSide : byte
|
||||
{
|
||||
Insurgent,
|
||||
Loyalist,
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using Content.Shared.Mind;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Shared._DV.Roles;
|
||||
|
||||
public sealed class ObjectiveRoleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ObjectiveRoleComponent, EntGotInsertedIntoContainerMessage>(OnInsertedIntoMind);
|
||||
SubscribeLocalEvent<ObjectiveRoleComponent, ComponentShutdown>(OnShutdown);
|
||||
}
|
||||
|
||||
private void OnInsertedIntoMind(Entity<ObjectiveRoleComponent> ent, ref EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (_net.IsClient)
|
||||
return;
|
||||
|
||||
if (!TryComp<MindComponent>(args.Container.Owner, out var mind))
|
||||
return;
|
||||
|
||||
foreach (var objective in ent.Comp.Objectives)
|
||||
{
|
||||
_mind.TryAddObjective(args.Container.Owner, mind, objective);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<ObjectiveRoleComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (_net.IsClient)
|
||||
return;
|
||||
|
||||
if (!_container.TryGetContainingContainer(ent.Owner, out var container) ||
|
||||
!TryComp<MindComponent>(container.Owner, out var mind))
|
||||
return;
|
||||
|
||||
var indices = new List<int>();
|
||||
|
||||
for (var i = mind.Objectives.Count; i >= 0; i--)
|
||||
{
|
||||
var objective = mind.Objectives[i];
|
||||
if (MetaData(objective).EntityPrototype?.ID is { } id && ent.Comp.Objectives.Contains(id))
|
||||
{
|
||||
indices.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var index in indices)
|
||||
{
|
||||
_mind.TryRemoveObjective(container.Owner, mind, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.Trigger.Components.Effects;
|
||||
|
||||
/// <summary>
|
||||
/// is used for implants and such so the right paramters can be passed
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class CreateHitmanCardOnTriggerComponent : BaseXOnTriggerComponent;
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Shared._DV.Trigger.Components.Effects;
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
|
||||
namespace Content.Shared._DV.Trigger.Systems;
|
||||
|
||||
public sealed class CreateHitmanCardOnTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CreateHitmanCardOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
//just spawns a card on the given target. uses predicated or spawn next to based on if. as trigger effect doesnt seem to have predicted on it ill just check if this is sever first and if not assume its predicted.
|
||||
private void OnTrigger(Entity<CreateHitmanCardOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
if (args.Handled || args.User is not { } user)
|
||||
return;
|
||||
|
||||
var mapCoords = _transform.GetMapCoordinates(ent);
|
||||
var card = EntityManager.PredictedSpawn("HitmanBusinessCard", mapCoords);
|
||||
_hands.TryPickupAnyHand(user, card);
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
|
||||
|
||||
namespace Content.Shared._DV.Uprising;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CommandStatusObjectiveComponent : Component
|
||||
{
|
||||
[DataField(required: true, customTypeSerializer: typeof(CustomHashSetSerializer<string, ComponentNameSerializer>))]
|
||||
public HashSet<string> ConvertedRoles;
|
||||
|
||||
[DataField]
|
||||
public bool ShouldUnconvertedBeIncapacitated;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Content.Shared.NPC.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
|
||||
|
||||
namespace Content.Shared._DV.Uprising;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CrewConversionObjectiveComponent : Component
|
||||
{
|
||||
[DataField(required: true, customTypeSerializer: typeof(CustomHashSetSerializer<string, ComponentNameSerializer>))]
|
||||
public HashSet<string> ConvertedRoles;
|
||||
|
||||
[DataField(required: true)]
|
||||
public HashSet<ProtoId<NpcFactionPrototype>> ConversionSourceFactions;
|
||||
|
||||
[DataField(required: true)]
|
||||
public float TargetFraction;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.NPC.Components;
|
||||
using Content.Shared.NPC.Systems;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Shared._DV.Uprising;
|
||||
|
||||
public sealed class CrewConversionObjectiveSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _role = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CrewConversionObjectiveComponent, ObjectiveGetProgressEvent>(OnGetProgress);
|
||||
}
|
||||
|
||||
private void OnGetProgress(Entity<CrewConversionObjectiveComponent> ent, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
var total = 0;
|
||||
var counted = 0;
|
||||
|
||||
var query = AllEntityQuery<MindContainerComponent, NpcFactionMemberComponent>();
|
||||
while (query.MoveNext(out var uid, out var mind, out var faction))
|
||||
{
|
||||
if (mind.Mind is not { } mindUid)
|
||||
continue;
|
||||
|
||||
if (!_npcFaction.IsMemberOfAny((uid, faction), ent.Comp.ConversionSourceFactions))
|
||||
continue;
|
||||
|
||||
total++;
|
||||
|
||||
foreach (var role in ent.Comp.ConvertedRoles)
|
||||
{
|
||||
if (!EntityManager.ComponentFactory.TryGetRegistration(role, out var roleReg))
|
||||
{
|
||||
Log.Error($"Role component not found for CrewConversionObjective: {role}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_role.MindHasRole(mindUid, roleReg.Type, out _))
|
||||
continue;
|
||||
|
||||
counted++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (total == 0)
|
||||
return;
|
||||
|
||||
args.Progress = ((float)counted / (float)total) / ent.Comp.TargetFraction;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace Content.Shared._DV.Uprising;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class UprisingArmRequiredObjectiveComponent : Component;
|
||||
@@ -0,0 +1,10 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.Uprising;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class UprisingDisarmDoAfter : SimpleDoAfterEvent;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class UprisingArmDoAfter : SimpleDoAfterEvent;
|
||||
@@ -0,0 +1,10 @@
|
||||
using Content.Shared._DV.Roles;
|
||||
|
||||
namespace Content.Shared._DV.Uprising;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class UprisingVictoryObjectiveComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public UprisingSide Side;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Shared.NPC.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Zombies;
|
||||
|
||||
public sealed partial class ZombieComponent
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - The list of factions that an entity is part of from before they were zombified.
|
||||
/// The zombification process removes them, but cloning a zombie copies the NPCFactionMemberComponent
|
||||
/// so they are still considered a zombie faction-wise, so we need to keep track of what they had right
|
||||
/// before the factions are removed.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<ProtoId<NpcFactionPrototype>> FactionsBeforeZombification = new();
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Tracks if the entity was a pacifist before turning into a zombie. Although, there are other
|
||||
/// scenarios where the person might be pacified but those are usually temporary.
|
||||
/// </summary>
|
||||
public bool WasPacisfistBeforeZombification = false;
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Content.Shared._ST.Interaction;
|
||||
/// Data for interaction particles
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class StellarInteractionParticleEvent(NetEntity performer, NetEntity? used, NetEntity target, bool isClientEvent) : EntityEventArgs
|
||||
public sealed class StellarInteractionParticleEvent(NetEntity performer, NetEntity? used, NetEntity target, bool isClientEvent, StellarInteractionParticleType type) : EntityEventArgs
|
||||
{
|
||||
public NetEntity Performer = performer;
|
||||
|
||||
@@ -22,4 +22,13 @@ public sealed class StellarInteractionParticleEvent(NetEntity performer, NetEnti
|
||||
/// Workaround for event subscription not working w/ the session overload
|
||||
/// </summary>
|
||||
public bool IsClientEvent = isClientEvent;
|
||||
|
||||
public StellarInteractionParticleType Type = type;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum StellarInteractionParticleType
|
||||
{
|
||||
Use,
|
||||
Pull,
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,29 @@
|
||||
- files: ["alien_footstep_charge1.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Taken from cmss13"
|
||||
source: "https://github.com/cmss13-devs/cmss13/blob/183b50b774c6faef054b6f67f57ff40ecc15409f/sound/effects/alien_footstep_charge1.ogg"
|
||||
|
||||
- files: ["alien_footstep_charge2.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Taken from cmss13"
|
||||
source: "https://github.com/cmss13-devs/cmss13/blob/183b50b774c6faef054b6f67f57ff40ecc15409f/sound/effects/alien_footstep_charge2.ogg"
|
||||
|
||||
- files: ["alien_footstep_charge3.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Taken from cmss13"
|
||||
source: "https://github.com/cmss13-devs/cmss13/blob/183b50b774c6faef054b6f67f57ff40ecc15409f/sound/effects/alien_footstep_charge3.ogg"
|
||||
|
||||
- files: ["alien_footstep_large1.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Taken from cmss13"
|
||||
source: "https://github.com/cmss13-devs/cmss13/blob/183b50b774c6faef054b6f67f57ff40ecc15409f/sound/effects/alien_footstep_large1.ogg"
|
||||
|
||||
- files: ["alien_footstep_large2.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Taken from cmss13"
|
||||
source: "https://github.com/cmss13-devs/cmss13/blob/183b50b774c6faef054b6f67f57ff40ecc15409f/sound/effects/alien_footstep_large2.ogg"
|
||||
|
||||
- files: ["alien_footstep_large3.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Taken from cmss13"
|
||||
source: "https://github.com/cmss13-devs/cmss13/blob/183b50b774c6faef054b6f67f57ff40ecc15409f/sound/effects/alien_footstep_large3.ogg"
|
||||
@@ -314,5 +314,23 @@ Entries:
|
||||
id: 34
|
||||
time: '2026-06-06T19:14:04.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5699
|
||||
- author: FieldCommand
|
||||
changes:
|
||||
- message: 'Ghost bat: Syndicate Prison-themed ghost bar has been added and can
|
||||
be spawned in by anyone with loadgrid permissions.'
|
||||
type: Add
|
||||
id: 35
|
||||
time: '2026-06-08T17:23:24.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5578
|
||||
- author: FieldCommand, Stxcking
|
||||
changes:
|
||||
- message: Nanoblod vending machin syndicate counter to nanomed pluss.
|
||||
type: Add
|
||||
- message: Made some small changes to ghost bar syndicatre prison. SyndieJuice now
|
||||
has a new look to look more like its counterpart, Nanoblood.
|
||||
type: Tweak
|
||||
id: 36
|
||||
time: '2026-06-16T20:02:18.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6006
|
||||
Name: DeltaVAdmin
|
||||
Order: 5
|
||||
|
||||
@@ -1,456 +1,4 @@
|
||||
Entries:
|
||||
- author: verybigman311
|
||||
changes:
|
||||
- message: Lava Fountains are now an available decoration.
|
||||
type: Add
|
||||
- message: Drinking from fountains has become much easier.
|
||||
type: Fix
|
||||
id: 1885
|
||||
time: '2025-11-30T16:44:24.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4792
|
||||
- author: SirWarock
|
||||
changes:
|
||||
- message: Buffed the Advanced Truncheon to actually.. be better than the normal
|
||||
Truncheon.
|
||||
type: Tweak
|
||||
id: 1886
|
||||
time: '2025-11-30T16:46:38.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4736
|
||||
- author: cmorley191
|
||||
changes:
|
||||
- message: psychokinetic scream now works with "psionic disturbance" arti trigger
|
||||
type: Fix
|
||||
id: 1887
|
||||
time: '2025-11-30T17:00:04.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4816
|
||||
- author: Coryler
|
||||
changes:
|
||||
- message: The minor mass mind swap glimmer event has been added. If glimmer is
|
||||
above 750, it can mindswap up to 6 people temporarily.
|
||||
type: Add
|
||||
id: 1888
|
||||
time: '2025-11-30T17:46:12.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4728
|
||||
- author: TehFlaminTaco
|
||||
changes:
|
||||
- message: Fractured Form can now be mindbreaker'd without perma-sleeping
|
||||
type: Fix
|
||||
- message: Fractured Form will no-longer spawn an IPC body.
|
||||
type: Tweak
|
||||
id: 1889
|
||||
time: '2025-11-30T18:00:34.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4582
|
||||
- author: cmorley191
|
||||
changes:
|
||||
- message: Artifact node effects are sometimes hidden or unpredictable until after
|
||||
you trigger them for the first time. Be careful!
|
||||
type: Tweak
|
||||
id: 1890
|
||||
time: '2025-11-30T18:10:16.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4748
|
||||
- author: ShepardToTheStars, slarticodefast
|
||||
changes:
|
||||
- message: Crawling speed now depends on the number of free hands you have.
|
||||
type: Tweak
|
||||
id: 1891
|
||||
time: '2025-12-01T07:57:02.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4772
|
||||
- author: Cepelinas1
|
||||
changes:
|
||||
- message: Possession charges no longer stack. The highest offending category is
|
||||
now charged.
|
||||
type: Tweak
|
||||
id: 1892
|
||||
time: '2025-12-01T13:33:46.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4808
|
||||
- author: Apachit0
|
||||
changes:
|
||||
- message: Buffed the Makeshift Fishing rod
|
||||
type: Tweak
|
||||
id: 1893
|
||||
time: '2025-12-01T21:20:35.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4827
|
||||
- author: BunglesnortBoss
|
||||
changes:
|
||||
- message: Enabled Cyborg's to weld themselves! Takes a lot of time but at least
|
||||
you can weld up those scratches!
|
||||
type: Tweak
|
||||
- message: Cyborg's power draw rate lowered! Still get the TEG up fast however!
|
||||
type: Tweak
|
||||
- message: Increased Weightless Acceleration, Should feel a bit faster for the Cyborg's
|
||||
in space, Congrats Salvage!
|
||||
type: Tweak
|
||||
id: 1894
|
||||
time: '2025-12-02T00:13:08.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4819
|
||||
- author: Deltanedas, Bonktrauma
|
||||
changes:
|
||||
- message: Added Ovni Station.
|
||||
type: Add
|
||||
id: 1895
|
||||
time: '2025-12-02T04:42:19.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4602
|
||||
- author: Vapetastic-Gaming
|
||||
changes:
|
||||
- message: Patreon link in game has been updated
|
||||
type: Tweak
|
||||
id: 1896
|
||||
time: '2025-12-02T14:20:38.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4833
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: Healing from breathing miasma with rat lungs no longer scales by quantity
|
||||
of the gas in the air.
|
||||
type: Tweak
|
||||
id: 1897
|
||||
time: '2025-12-02T22:26:27.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4836
|
||||
- author: Eternally-Confused
|
||||
changes:
|
||||
- message: Aroace cloaks can now be found in the Pride-o-mat!
|
||||
type: Add
|
||||
- message: Pride-o-mat restocks now cost slightly more
|
||||
type: Tweak
|
||||
id: 1898
|
||||
time: '2025-12-03T05:01:47.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4847
|
||||
- author: Stxcking
|
||||
changes:
|
||||
- message: Syndicat Returns! with some tweaks
|
||||
type: Add
|
||||
id: 1899
|
||||
time: '2025-12-03T05:32:01.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4731
|
||||
- author: Proxysseia
|
||||
changes:
|
||||
- message: Larynx Implants are now purchaseable in the uplink for all species
|
||||
type: Tweak
|
||||
- message: Kanabou are now purchaseable in the uplink for all species
|
||||
type: Tweak
|
||||
- message: Samurai armors are now purchaseable in the uplink for all species
|
||||
type: Tweak
|
||||
id: 1900
|
||||
time: '2025-12-03T07:07:52.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4759
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: Stasicillin should once again force the target to fall asleep if they
|
||||
have over 11u.
|
||||
type: Fix
|
||||
id: 1901
|
||||
time: '2025-12-03T16:08:27.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4823
|
||||
- author: Stxcking
|
||||
changes:
|
||||
- message: Syndicat description fixed
|
||||
type: Fix
|
||||
id: 1902
|
||||
time: '2025-12-03T20:25:52.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4854
|
||||
- author: FieldCommand
|
||||
changes:
|
||||
- message: All ships from the shipyard cost 1000 more spesos due to inflation.
|
||||
type: Tweak
|
||||
id: 1903
|
||||
time: '2025-12-03T22:34:59.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4858
|
||||
- author: verybigman311
|
||||
changes:
|
||||
- message: Christmas lamps now display the correct rotation
|
||||
type: Fix
|
||||
id: 1904
|
||||
time: '2025-12-04T03:26:40.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4865
|
||||
- author: Woljif
|
||||
changes:
|
||||
- message: Gave the dragon a wide-sweep attack, more damage, and faster fireballs.
|
||||
type: Tweak
|
||||
id: 1905
|
||||
time: '2025-12-04T08:27:37.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4781
|
||||
- author: ShepardToTheStars, Niomi
|
||||
changes:
|
||||
- message: Borg ID chips from the exofab now have the same access as round-start
|
||||
borgs.
|
||||
type: Tweak
|
||||
- message: SRNs can no longer print normal borg ID chips.
|
||||
type: Tweak
|
||||
- message: Syndicate borg ID chips now cost a bit less to make.
|
||||
type: Tweak
|
||||
- message: Agent IDs can no longer copy access from borg ID chips.
|
||||
type: Tweak
|
||||
- message: The new derelict borgs now have the intended borg access to the station!
|
||||
type: Fix
|
||||
- message: Derelict syndicate borgs don't have usual borg access, but has enough
|
||||
to get into the station.
|
||||
type: Tweak
|
||||
- message: Derelict syndicate borgs can be unlocked by people with Robotics access
|
||||
(so they can get their access updated).
|
||||
type: Tweak
|
||||
id: 1906
|
||||
time: '2025-12-04T11:18:07.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4821
|
||||
- author: Dorragon
|
||||
changes:
|
||||
- message: Space dragons welcome sharkminnows in their family
|
||||
type: Add
|
||||
id: 1907
|
||||
time: '2025-12-04T13:27:57.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4826
|
||||
- author: Nesoryo, Nikovnik, kotobdev
|
||||
changes:
|
||||
- message: Added alternative typing indicators for whispering, emoting, using the
|
||||
radio, and (L)OOC-ing!
|
||||
type: Add
|
||||
id: 1908
|
||||
time: '2025-12-04T16:59:08.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4679
|
||||
- author: verybigman311
|
||||
changes:
|
||||
- message: Every single wieldable gun now has wielded sprites.
|
||||
type: Add
|
||||
- message: Wieldable guns that previously utilized upstream sprites, have been updated
|
||||
with Delta-V sprites.
|
||||
type: Add
|
||||
- message: Mark-1 series rifles are now wieldable.
|
||||
type: Tweak
|
||||
- message: Touched up a few miscolored cocktail sprites.
|
||||
type: Tweak
|
||||
- message: Practice security weapons now have orange tips for safety purposes.
|
||||
type: Tweak
|
||||
- message: Practice laser carbines are now blue.
|
||||
type: Tweak
|
||||
- message: Practice disablers not having inhand sprites.
|
||||
type: Fix
|
||||
id: 1909
|
||||
time: '2025-12-04T18:42:20.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4851
|
||||
- author: verybigman311
|
||||
changes:
|
||||
- message: Frag Grenades now have shrapnel!
|
||||
type: Add
|
||||
id: 1910
|
||||
time: '2025-12-04T22:28:59.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4856
|
||||
- author: kontakt, ShepardToTheStars
|
||||
changes:
|
||||
- message: The ninja's spider clan charge can now be placed on walls and windows
|
||||
again.
|
||||
type: Fix
|
||||
id: 1911
|
||||
time: '2025-12-05T01:44:35.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4861
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: The nukie agent's visor can finally see silicon health.
|
||||
type: Fix
|
||||
id: 1912
|
||||
time: '2025-12-05T01:58:46.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4864
|
||||
- author: cmorley191
|
||||
changes:
|
||||
- message: Rock anom won't make unbreakable rocks anymore
|
||||
type: Fix
|
||||
id: 1913
|
||||
time: '2025-12-05T02:04:53.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4873
|
||||
- author: Stxcking
|
||||
changes:
|
||||
- message: Security, medical, and roboticist's loadouts now have prescription glasses
|
||||
counterparts. (Security players make sure to save your option.)
|
||||
type: Tweak
|
||||
id: 1914
|
||||
time: '2025-12-05T02:14:01.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4871
|
||||
- author: verybigman311
|
||||
changes:
|
||||
- message: A large plethora of melee weapons now have the correct wideswing rotation.
|
||||
type: Fix
|
||||
- message: Drills and Saws now function similarly in combat to Mining Drills.
|
||||
type: Tweak
|
||||
- message: Electric and Bass guitars can now be used as weapons.
|
||||
type: Tweak
|
||||
- message: Numerous other tweaks have been made to melee weapons. Too many to list.
|
||||
type: Tweak
|
||||
id: 1915
|
||||
time: '2025-12-05T02:37:20.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4806
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: Silvia, the CMO's pet, can now equip medhuds and PDAs! No more dragging
|
||||
them around.
|
||||
type: Add
|
||||
- message: Silvia can no longer slip.
|
||||
type: Fix
|
||||
id: 1916
|
||||
time: '2025-12-05T02:40:36.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4867
|
||||
- author: HTMLSystem
|
||||
changes:
|
||||
- message: Chitnids will now use moth sprites for their glasses
|
||||
type: Tweak
|
||||
id: 1917
|
||||
time: '2025-12-05T02:49:32.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4835
|
||||
- author: Stxcking
|
||||
changes:
|
||||
- message: 'Fishing vendor: Exchange fish for fishing points!'
|
||||
type: Add
|
||||
- message: Fishing basket to easily store caught fish.
|
||||
type: Add
|
||||
id: 1918
|
||||
time: '2025-12-05T02:58:57.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4862
|
||||
- author: kotobdev
|
||||
changes:
|
||||
- message: Nukie commanders are now ~~threatened~~ *encouraged* to use the war declarator
|
||||
properly.
|
||||
type: Add
|
||||
- message: Nukies now have 8 minutes to declare war, up from 6.
|
||||
type: Tweak
|
||||
id: 1919
|
||||
time: '2025-12-05T04:48:30.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4876
|
||||
- author: ShepardToTheStars, Jakumba
|
||||
changes:
|
||||
- message: Holofans are no longer infinite duration. They last 5 minutes but also
|
||||
recharge over time. They can still be picked up.
|
||||
type: Tweak
|
||||
- message: All holoprojectors function the same way now. You can pick up janitor
|
||||
holosigns, security holobarriers, and they all recharge over time.
|
||||
type: Tweak
|
||||
id: 1920
|
||||
time: '2025-12-05T12:56:10.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4734
|
||||
- author: PureBreadBagel, Cepelinas1
|
||||
changes:
|
||||
- message: Guidebook has got new entries! The guidebook now contains Entries relating
|
||||
to Space Law, SOP, Commanding Officer, Security And Justice Regulations, Hearings
|
||||
and all the entries you could ever need to enforce law and legality!
|
||||
type: Add
|
||||
- message: Security and Justice's reading materials got upgraded! Security 101 and
|
||||
Space Law books now contain new entries! Such as Space Law, SOP, General Regulations,
|
||||
Commanding Officer...etc!
|
||||
type: Tweak
|
||||
- message: The Medical Reference book contains the Medical Regulations now.
|
||||
type: Tweak
|
||||
- message: Fixed a guidebook entry relating to Organ Manipulation. It no longer
|
||||
displays that replacing the heart cures rot, and that replacing the liver cures
|
||||
poison.
|
||||
type: Fix
|
||||
id: 1921
|
||||
time: '2025-12-05T17:56:38.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4857
|
||||
- author: Eternally-Confused and Toby222
|
||||
changes:
|
||||
- message: Pride pins, scarves, and cloaks are now able to change into similar pieces
|
||||
of pride clothing!
|
||||
type: Tweak
|
||||
id: 1922
|
||||
time: '2025-12-07T07:09:34.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4845
|
||||
- author: Proxysseia
|
||||
changes:
|
||||
- message: Removed Speedboots
|
||||
type: Remove
|
||||
id: 1923
|
||||
time: '2025-12-07T09:47:14.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4883
|
||||
- author: Halo3moth
|
||||
changes:
|
||||
- message: Feroxis can now thump! Sharks have tails too!
|
||||
type: Tweak
|
||||
id: 1924
|
||||
time: '2025-12-07T11:45:45.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4877
|
||||
- author: Stxcking
|
||||
changes:
|
||||
- message: Added chemical, surgeon, and paramedic wall lockers for mappers
|
||||
type: Add
|
||||
id: 1925
|
||||
time: '2025-12-07T17:54:34.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4899
|
||||
- author: beck-thompson
|
||||
changes:
|
||||
- message: Respawn timer now works when observing
|
||||
type: Fix
|
||||
id: 1926
|
||||
time: '2025-12-07T18:02:52.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4895
|
||||
- author: zelezniciar, ArtisticRoomba
|
||||
changes:
|
||||
- message: Atmospherics Delta-Pressure now has a short guidebook entry. You can
|
||||
find it in Jobs > Engineering > Atmospherics > Atmospheric Upsets > Delta Pressure.
|
||||
type: Add
|
||||
id: 1927
|
||||
time: '2025-12-08T00:03:47.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4906
|
||||
- author: IAmNotGray
|
||||
changes:
|
||||
- message: 'Elegance: Who changed the thermostat on Elegance? The pool froze over,
|
||||
dude!'
|
||||
type: Tweak
|
||||
id: 1928
|
||||
time: '2025-12-08T05:30:18.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4903
|
||||
- author: Velcroboy
|
||||
changes:
|
||||
- message: 'Hive: Added snow!'
|
||||
type: Add
|
||||
id: 1929
|
||||
time: '2025-12-08T06:07:51.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4898
|
||||
- author: verybigman311
|
||||
changes:
|
||||
- message: Tinted windows can now be reinforced.
|
||||
type: Add
|
||||
id: 1930
|
||||
time: '2025-12-08T09:01:15.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4875
|
||||
- author: SirSmith148
|
||||
changes:
|
||||
- message: Incorporated Glimmer within the new Xenoarchaeology system
|
||||
type: Tweak
|
||||
id: 1931
|
||||
time: '2025-12-08T04:11:00.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4486
|
||||
- author: ShepardToTheStars, gluesniffler
|
||||
changes:
|
||||
- message: Harpies can now take flight!
|
||||
type: Add
|
||||
id: 1932
|
||||
time: '2025-12-08T13:58:53.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4888
|
||||
- author: Dorragon
|
||||
changes:
|
||||
- message: Rat kings and xenos now see in the dark
|
||||
type: Add
|
||||
id: 1933
|
||||
time: '2025-12-08T14:34:58.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4881
|
||||
- author: BunglesnortBoss
|
||||
changes:
|
||||
- message: Salvage Cyborg's now have new laws! Give em' a read! Credit to AirlineFood
|
||||
for the actual laws.
|
||||
type: Tweak
|
||||
id: 1934
|
||||
time: '2025-12-08T14:37:23.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4866
|
||||
- author: cmorley191
|
||||
changes:
|
||||
- message: '"Impossible" arti trigger combinations (like low+high pressure) will
|
||||
no longer appear on artifacts.'
|
||||
type: Tweak
|
||||
id: 1935
|
||||
time: '2025-12-08T14:47:08.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4740
|
||||
- author: cmorley191
|
||||
changes:
|
||||
- message: The handheld Node Scanner tool has new artifact information available
|
||||
type: Add
|
||||
id: 1936
|
||||
time: '2025-12-08T14:48:49.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4814
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: All syndicate cyborg ghost roles now spawn with a neuroling rather than
|
||||
@@ -4395,4 +3943,457 @@
|
||||
id: 2385
|
||||
time: '2026-06-06T19:14:04.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5699
|
||||
- author: SirRequiem
|
||||
changes:
|
||||
- message: 'Terra: Sec is now properly blue!'
|
||||
type: Tweak
|
||||
- message: 'Terra: Medbay got a lil remodel'
|
||||
type: Tweak
|
||||
id: 2386
|
||||
time: '2026-06-07T16:22:57.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5956
|
||||
- author: biddygelson
|
||||
changes:
|
||||
- message: 'Academy: Department faxes and Poppy'
|
||||
type: Add
|
||||
- message: 'Academy: RED sec NO, BLU sec YES.'
|
||||
type: Tweak
|
||||
id: 2387
|
||||
time: '2026-06-07T18:13:12.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5972
|
||||
- author: Stxcking
|
||||
changes:
|
||||
- message: Glowsticks can now be use for glow in the dark stars wall decals!
|
||||
type: Fix
|
||||
id: 2388
|
||||
time: '2026-06-07T19:05:16.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5970
|
||||
- author: Stxcking
|
||||
changes:
|
||||
- message: Holoflares pellets' light actually hurt Skias.
|
||||
type: Fix
|
||||
id: 2389
|
||||
time: '2026-06-07T19:33:59.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5976
|
||||
- author: MantasDab360
|
||||
changes:
|
||||
- message: Removes duplicates of yellow and white towels in loadout.
|
||||
type: Fix
|
||||
id: 2390
|
||||
time: '2026-06-07T19:46:18.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5964
|
||||
- author: rebe83
|
||||
changes:
|
||||
- message: Entropic mobs now deal a small amount of blunt damage.
|
||||
type: Tweak
|
||||
id: 2391
|
||||
time: '2026-06-07T20:48:50.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5962
|
||||
- author: rebe83
|
||||
changes:
|
||||
- message: Ninja's EMP ability now has a 5 second cooldown
|
||||
type: Tweak
|
||||
id: 2392
|
||||
time: '2026-06-08T04:35:21.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5967
|
||||
- author: Eternally-Confused
|
||||
changes:
|
||||
- message: The nuclear fabricator now announces when it receives new recipes.
|
||||
type: Tweak
|
||||
id: 2393
|
||||
time: '2026-06-08T05:29:56.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5985
|
||||
- author: Vapetastic-Gaming
|
||||
changes:
|
||||
- message: Submarine has received nuclear power. Sub has also gained a VOX BOX and
|
||||
a new janitor closet. Due to how the reactor was transported on station, the
|
||||
station has been left a mess, enjoy.
|
||||
type: Tweak
|
||||
id: 2394
|
||||
time: '2026-06-08T19:36:02.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5760
|
||||
- author: Velcroboy
|
||||
changes:
|
||||
- message: Added an evac pod in north maints; and Poppy the engineering possum!
|
||||
type: Add
|
||||
- message: Security is blu; botany soil beds come prefilled now; and epi locker
|
||||
area moved into the back.
|
||||
type: Tweak
|
||||
id: 2395
|
||||
time: '2026-06-08T19:50:31.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5965
|
||||
- author: rebe83
|
||||
changes:
|
||||
- message: Shadow cats can no longer attack. They can now pounce though.
|
||||
type: Tweak
|
||||
id: 2396
|
||||
time: '2026-06-09T13:45:27.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5977
|
||||
- author: Cepelinas1
|
||||
changes:
|
||||
- message: Added 22 new tips!
|
||||
type: Add
|
||||
- message: Added missing commas on certain weapon descriptions.
|
||||
type: Tweak
|
||||
- message: Added missing scope descriptions to Laser Cannons and Vulcans.
|
||||
type: Tweak
|
||||
id: 2397
|
||||
time: '2026-06-09T18:10:02.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6004
|
||||
- author: rebe83
|
||||
changes:
|
||||
- message: Meatspikes now take 5 seconds to build instead of 30. He's right behind
|
||||
me, isn't he?
|
||||
type: Tweak
|
||||
id: 2398
|
||||
time: '2026-06-09T18:48:35.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5999
|
||||
- author: Vapetastic-Gaming
|
||||
changes:
|
||||
- message: Xenos are now much scarier, and should look familiar too !!
|
||||
type: Tweak
|
||||
id: 2399
|
||||
time: '2026-06-10T18:21:14.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5891
|
||||
- author: Velcroboy
|
||||
changes:
|
||||
- message: 'Arena: Security is bloo now; botany has filled soil beds; and evac pods
|
||||
are larger'
|
||||
type: Tweak
|
||||
id: 2400
|
||||
time: '2026-06-11T01:28:31.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6009
|
||||
- author: einknusprigestoast
|
||||
changes:
|
||||
- message: Removed DeathSquad
|
||||
type: Remove
|
||||
id: 2401
|
||||
time: '2026-06-13T02:29:04.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6003
|
||||
- author: rebe83
|
||||
changes:
|
||||
- message: The psionic power Fractured Form was removed.
|
||||
type: Remove
|
||||
id: 2402
|
||||
time: '2026-06-13T19:29:04.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6011
|
||||
- author: stormyatt
|
||||
changes:
|
||||
- message: ghost role hitman variant
|
||||
type: Add
|
||||
id: 2403
|
||||
time: '2026-06-13T20:03:15.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5848
|
||||
- author: CarolCuchara
|
||||
changes:
|
||||
- message: Secborgs now leave blue fibers behind. Red is no more!
|
||||
type: Fix
|
||||
id: 2404
|
||||
time: '2026-06-14T04:12:58.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6025
|
||||
- author: Stop-Signs
|
||||
changes:
|
||||
- message: WT550 fire rate has been reduced by 0.5
|
||||
type: Tweak
|
||||
- message: WT550 Magazine size has been reduced 30->20
|
||||
type: Tweak
|
||||
- message: WT550 is slightly less accurate
|
||||
type: Tweak
|
||||
id: 2405
|
||||
time: '2026-06-14T08:13:25.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5581
|
||||
- author: CarolCuchara
|
||||
changes:
|
||||
- message: Pager linking now only works when in range of the fax or holopad!
|
||||
type: Fix
|
||||
id: 2406
|
||||
time: '2026-06-14T18:30:35.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6032
|
||||
- author: sowelipililimute
|
||||
changes:
|
||||
- message: Pulling now has a unique particle effect
|
||||
type: Add
|
||||
- message: Interaction particles now only spawn on UIs if the UIs have an associated
|
||||
open sound
|
||||
type: Tweak
|
||||
- message: Interaction particles no longer spawn on closing a UI
|
||||
type: Fix
|
||||
id: 2407
|
||||
time: '2026-06-15T15:05:30.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6033
|
||||
- author: CarolCuchara
|
||||
changes:
|
||||
- message: Throwing glasses while wearing beer googles no longer spills the contents!
|
||||
type: Fix
|
||||
id: 2408
|
||||
time: '2026-06-15T15:10:00.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6031
|
||||
- author: StarboundSolaris
|
||||
changes:
|
||||
- message: Pitbulls and their genetically modified subspecies, Ventbulls, have been
|
||||
re introduced to the Delta Sector!
|
||||
type: Add
|
||||
- message: NanoTrasen is now deploying Ventbulls into station ventilation systems
|
||||
as a method of quick and inexpensive pest control! Don't be surprised if they
|
||||
crawl out of a vent while chasing a horde of mice!
|
||||
type: Add
|
||||
- message: Departments may sometimes come equipped with a unique pitbull!
|
||||
type: Add
|
||||
- message: Pitbulls no longer maul couriers and felinids!
|
||||
type: Tweak
|
||||
- message: Pitbulls are now more tolerant to being pet!
|
||||
type: Tweak
|
||||
- message: Pitbulls now come with an even greater variety of ridiculous names!
|
||||
type: Tweak
|
||||
id: 2409
|
||||
time: '2026-06-15T16:02:07.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5915
|
||||
- author: rebe83
|
||||
changes:
|
||||
- message: Security ponchos now only help with temperature. It's just plastic after
|
||||
all.
|
||||
type: Tweak
|
||||
- message: Gorlex ponchos now only offer modest protection. They just don't make
|
||||
them like they used to.
|
||||
type: Tweak
|
||||
id: 2410
|
||||
time: '2026-06-15T15:51:39.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6001
|
||||
- author: keekee38
|
||||
changes:
|
||||
- message: NT no longer stores guns with loaded chambers
|
||||
type: Tweak
|
||||
id: 2411
|
||||
time: '2026-06-15T16:46:35.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5819
|
||||
- author: CarolCuchara
|
||||
changes:
|
||||
- message: Zombified crew and other entities like Laika will drop headsets which
|
||||
are in their pockets. No more listening to survivor comms!
|
||||
type: Fix
|
||||
id: 2412
|
||||
time: '2026-06-15T17:27:01.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6030
|
||||
- author: JustTNE
|
||||
changes:
|
||||
- message: Don't display SSD duration when examining dead players or untaken ghost
|
||||
roles
|
||||
type: Fix
|
||||
id: 2413
|
||||
time: '2026-06-16T14:16:08.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6012
|
||||
- author: DisposableCrewmember42
|
||||
changes:
|
||||
- message: The midround Entropic Colossus has been reworked! It will now be buffed
|
||||
by effigies going supercritical and die when they decay. After the colossus
|
||||
places three effigies, it can roam the station freely.
|
||||
type: Add
|
||||
id: 2414
|
||||
time: '2026-06-16T17:09:33.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5565
|
||||
- author: CarolCuchara
|
||||
changes:
|
||||
- message: Rat Kings raise army ability is now dynamically priced. The first servants
|
||||
are cheaper, allowing for an easier early game and bounce back. Too many servants
|
||||
will increase the price drastically, with giant swarms killing the performance
|
||||
very hard to achieve!
|
||||
type: Tweak
|
||||
id: 2415
|
||||
time: '2026-06-16T17:43:10.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6017
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: Pitbulls no longer fit under doors.
|
||||
type: Fix
|
||||
- message: Pitbulls will now retaliate if you hit them. Be nice!
|
||||
type: Fix
|
||||
id: 2416
|
||||
time: '2026-06-17T01:06:56.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6046
|
||||
- author: rebe83
|
||||
changes:
|
||||
- message: .25 caseless poison ammo now uses lexorin and is faster acting.
|
||||
type: Tweak
|
||||
- message: The estoc once again shoots in 3 round bursts, but still faster than
|
||||
before.
|
||||
type: Tweak
|
||||
- message: Knuckledusters are now made of REAL plastitanium and deal 14 blunt per
|
||||
hit. Nanotrasen noggins can be rattled in 4 punches.
|
||||
type: Tweak
|
||||
id: 2417
|
||||
time: '2026-06-17T13:36:02.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5991
|
||||
- author: JustTNE
|
||||
changes:
|
||||
- message: Zombies can no longer use jetpacks
|
||||
type: Remove
|
||||
- message: Pets can no longer use jetpacks
|
||||
type: Remove
|
||||
id: 2418
|
||||
time: '2026-06-17T13:56:26.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5776
|
||||
- author: Proxysseia
|
||||
changes:
|
||||
- message: Breaking and Entering now means what you think it does
|
||||
type: Tweak
|
||||
id: 2419
|
||||
time: '2026-06-17T14:07:48.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6050
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: The biomasser is fixed and will now again properly biomass entities with
|
||||
0% blood.
|
||||
type: Fix
|
||||
id: 2420
|
||||
time: '2026-06-17T15:32:34.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6042
|
||||
- author: sowelipililimute
|
||||
changes:
|
||||
- message: Fax machines no longer need to be refreshed before their first use.
|
||||
type: Tweak
|
||||
id: 2421
|
||||
time: '2026-06-18T16:22:15.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6058
|
||||
- author: LordSugarcane
|
||||
changes:
|
||||
- message: All references to Security in the guidebook are now properly blue!
|
||||
type: Tweak
|
||||
id: 2422
|
||||
time: '2026-06-18T16:34:53.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6013
|
||||
- author: Stxcking
|
||||
changes:
|
||||
- message: Broken Station Maps can be Deconstructed
|
||||
type: Tweak
|
||||
id: 2423
|
||||
time: '2026-06-18T16:37:33.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6005
|
||||
- author: keekee38
|
||||
changes:
|
||||
- message: SV-213 pistols now come standard with medium power cells
|
||||
type: Tweak
|
||||
id: 2424
|
||||
time: '2026-06-19T15:48:58.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6065
|
||||
- author: DisposableCrewmember42
|
||||
changes:
|
||||
- message: Ghosts will now see chat messages prepended with clickable follow links.
|
||||
These can be turned off in the General settings. [upstream cherry-pick]
|
||||
type: Add
|
||||
id: 2425
|
||||
time: '2026-06-19T15:52:36.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6063
|
||||
- author: EmberAstra
|
||||
changes:
|
||||
- message: Purifying salts now fit into medical belts and medkits.
|
||||
type: Tweak
|
||||
id: 2426
|
||||
time: '2026-06-19T16:05:49.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6062
|
||||
- author: rebe83
|
||||
changes:
|
||||
- message: Security radio color has been tweaked for legibility. Looking a little
|
||||
burple... maybe even laughender?
|
||||
type: Tweak
|
||||
id: 2427
|
||||
time: '2026-06-19T16:15:59.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6016
|
||||
- author: Halo3moth
|
||||
changes:
|
||||
- message: Nanotrasen high command as released a statement on the existance of chameleon
|
||||
gear, syndicate operatives remember to lock your gear (chameleon gear is no
|
||||
longer metashielded)
|
||||
type: Tweak
|
||||
id: 2428
|
||||
time: '2026-06-19T17:48:59.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6000
|
||||
- author: rebe83
|
||||
changes:
|
||||
- message: You are no longer able to identify bomb pens based on damage values in
|
||||
the examine box. Because they don't do damage. Trust.
|
||||
type: Tweak
|
||||
id: 2429
|
||||
time: '2026-06-19T17:52:44.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6010
|
||||
- author: rebe83
|
||||
changes:
|
||||
- message: A bizarre mixup at the cube factory has been fixed, and kobold cubes
|
||||
once more produce actual kobolds.
|
||||
type: Fix
|
||||
id: 2430
|
||||
time: '2026-06-19T17:59:46.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5968
|
||||
- author: DisposableCrewmember42, neomoth
|
||||
changes:
|
||||
- message: Melee screenshake for the attacker now has a heavy and light damage variant.
|
||||
The heavy damage variant is also triggered by two-handed weapons being wielded.
|
||||
type: Tweak
|
||||
- message: Melee screenshake only occurs for the target if total damage > 4
|
||||
type: Tweak
|
||||
- message: Explosion screenshake now includes a directional camera kick that falls
|
||||
off with distance
|
||||
type: Tweak
|
||||
- message: Explosion screenshake now falls off with distance
|
||||
type: Tweak
|
||||
id: 2431
|
||||
time: '2026-06-19T18:12:00.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6059
|
||||
- author: Proxysseia
|
||||
changes:
|
||||
- message: '"Cosmic Cultists want to end the world" is now common knowledge. Their
|
||||
metashield has been removed.'
|
||||
type: Remove
|
||||
id: 2432
|
||||
time: '2026-06-19T18:35:33.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6026
|
||||
- author: rebe83
|
||||
changes:
|
||||
- message: Felinids can now use their claws to try and hold fast against space wind.
|
||||
or the warden.
|
||||
type: Add
|
||||
id: 2433
|
||||
time: '2026-06-19T19:26:22.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5996
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: Cats and other animals will no longer attack players who were cloned
|
||||
after becoming a zombie.
|
||||
type: Fix
|
||||
- message: The Pacifism trait is now properly re-applied to players who were cloned
|
||||
after becoming a zombie.
|
||||
type: Fix
|
||||
- message: Cloned Initial Infected now get an escape and live objective after being
|
||||
cloned.
|
||||
type: Tweak
|
||||
id: 2434
|
||||
time: '2026-06-19T21:42:45.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6053
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: The syndicate hardsuits now have 15% more aura when you turn the light
|
||||
on in the dark.
|
||||
type: Tweak
|
||||
- message: The vulpkanin syndicate suits have had a facelift and now match better
|
||||
with other syndicate hardsuits.
|
||||
type: Add
|
||||
id: 2435
|
||||
time: '2026-06-19T21:53:47.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6060
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: SRN-made borgs no longer have borg AA as soon as they are made. No more
|
||||
walking into the AI core.
|
||||
type: Fix
|
||||
- message: SRNs can print batteries in their exosuit fabricator again.
|
||||
type: Fix
|
||||
id: 2436
|
||||
time: '2026-06-19T22:06:51.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6045
|
||||
- author: stormyatt
|
||||
changes:
|
||||
- message: hitman card implant to hitmans. cause calling cards are fun!
|
||||
type: Add
|
||||
id: 2437
|
||||
time: '2026-06-20T10:46:03.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5896
|
||||
Order: 1
|
||||
|
||||
@@ -1045,4 +1045,30 @@
|
||||
id: 121
|
||||
time: '2026-06-05T15:07:41.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5880
|
||||
- author: snowy_winter
|
||||
changes:
|
||||
- message: 'Edge: Added department faxes and pagers.'
|
||||
type: Add
|
||||
- message: 'Edge: Main bridge is now more open; surgery room removed; and AI core
|
||||
fixed up a bit.'
|
||||
type: Tweak
|
||||
- message: 'Edge: EPI now has artifacts again yay'
|
||||
type: Fix
|
||||
id: 122
|
||||
time: '2026-06-08T21:57:40.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5983
|
||||
- author: Stxcking
|
||||
changes:
|
||||
- message: 'Shoko: Shoko is now 1/3 blue (BlueSec).'
|
||||
type: Tweak
|
||||
- message: 'Shoko: Engineering''s TEG and gas recycler set-ups have been improved!
|
||||
Atmos has two more APC as well!'
|
||||
type: Tweak
|
||||
- message: 'Shoko: Surgery rooms have been replaced.'
|
||||
type: Tweak
|
||||
- message: 'Shoko: Departmental pager doorbells and faxes have been implemented!'
|
||||
type: Tweak
|
||||
id: 123
|
||||
time: '2026-06-12T17:16:00.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/5940
|
||||
Order: 3
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,6 @@
|
||||
## Names of Psionic Powers
|
||||
psionic-power-name-dispel = Dispel
|
||||
psionic-power-name-eruption = Psionic Eruption
|
||||
psionic-power-name-fractured-form = Fractured Form
|
||||
psionic-power-name-mass-sleep = Mass Sleep
|
||||
psionic-power-name-mindswap = Mind Swap
|
||||
psionic-power-name-mindswap-return = Mind Swap Return
|
||||
@@ -27,13 +26,6 @@ psionic-equipped-shielded-in-doafter = The insulative gear broke your concentrat
|
||||
psionic-dispelled = Someone dispelled your psionic concentration!
|
||||
|
||||
## Specific Psionic messages
|
||||
# Fractured Form
|
||||
psionic-power-fractured-form-nobodies = You have no alternate forms to switch to!
|
||||
psionic-power-fractured-form-sleepy = You feel very sleepy... You should find somewhere to rest.
|
||||
psionic-power-fractured-form-ssd = { CAPITALIZE(SUBJECT($ent)) } { CONJUGATE-BE($ent) } in a deep sleep. { CAPITALIZE(POSS-ADJ($ent)) } eyes seem to be darting around as if dreaming.
|
||||
psionic-power-fractured-form-examine-self = You feel a strange connection to { OBJECT($ent) }.
|
||||
psionic-power-fractured-form-dispelled = Someone dispelled your sleepiness..
|
||||
|
||||
# Metapsionic Pulse
|
||||
psionic-power-metapsionic-success = You detect psychic presence there.
|
||||
psionic-power-metapsionic-failure = You don't detect any psychic presence there.
|
||||
@@ -100,6 +92,8 @@ psionic-power-precognition-unknown-shuttle-disaster-evac-pod-result-message = Yo
|
||||
psionic-power-precognition-syndicate-armsdealer-result-message = You see a vision of a ship lurking in the shadows, its cargo deadly.
|
||||
psionic-power-precognition-rift-spawn-result-message = You see a small spark of energy, quickly expanding as it tears reality apart, twisting everything around it.
|
||||
psionic-power-precognition-asakim-spawn-result-message = You smell stale air from a cryopod opening, and the faint echo of an intelligence far away but very near.
|
||||
psionic-power-precognition-pitbull-spawn-result-message = You see a vision of a hundred small creatures being torn apart by bloodthirsty beasts in a maze of steel.
|
||||
psionic-power-precognition-hitman-spawn-result-message = You see a man in a business suit, a corpse at their feet.
|
||||
|
||||
# Psionic Eruption
|
||||
psionic-eruption-begin = {CAPITALIZE(THE($user))} is being consumed by a psionic energy!
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
anomaly-behavior-always-grow = [color=red]Anomaly never stops growing.[/color]
|
||||
@@ -70,7 +70,6 @@ crime-assist-question-isitcategorycde = Is the item in question a category C, D,
|
||||
crime-assist-question-isitcontrolleditem = Was stolen object a controlled item?
|
||||
crime-assist-question-wassuspectinarestrictedlocation = Was the suspect in a restricted location, or anywhere the suspect did not have legal access to?
|
||||
crime-assist-question-wasentrancelocked = Did the suspect force through a locked access point to get to the location?
|
||||
crime-assist-question-questionwasithighsec = Was location that the suspect entered a High Security Area? (e.g. Bridge, Vault, Armory, Life support, AI etc)
|
||||
crime-assist-question-didsuspectbreaksomething = Did the suspect break something?
|
||||
crime-assist-question-weretheremanysuspects = Were there many suspects in a group causing a disturbance?
|
||||
crime-assist-question-wasdamagesmall = Was the damage minor, or easily undone?
|
||||
@@ -96,7 +95,7 @@ crime-assist-crimedetail-trespass = To enter into an area where one is not autho
|
||||
crime-assist-crimedetail-vandalism = To deface or superficially damage public property, or property belonging to another person.
|
||||
crime-assist-crimedetail-hooliganism = To intentionally and maliciously engage in disruptive conduct, where one refuses to cease.
|
||||
crime-assist-crimedetail-assault = To cause physical harm or to effect unwanted physical contact on an Employee, without the apparent intent to kill them, or to threaten such actions with both capability and intent to do so.
|
||||
crime-assist-crimedetail-breakingandentering = To break and enter into a high security area where one is not authorised nor invited, with intent to commit a crime within.
|
||||
crime-assist-crimedetail-breakingandentering = To break and enter into an area where one is not authorised nor invited.
|
||||
crime-assist-crimedetail-rioting = To partake in an unauthorised riotous, tumultuous, and disruptive public assembly that refuses to disperse after warning.
|
||||
crime-assist-crimedetail-endangerment = To recklessly abandon obligations involving the continued wellbeing and/or protection of life and property, through malpractice, action, or inaction.
|
||||
crime-assist-crimedetail-possession = To be in unauthorised possession of restricted items or items of particular danger.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user