Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 69 additions & 11 deletions Content.Client/_TSF/DamageEffects/TSFDamageEffectsSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using Robust.Shared.Audio.Components;
using Robust.Shared.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
Expand Down Expand Up @@ -166,6 +167,7 @@ private void OnTraumaticShockStateHandled(EntityUid uid, TraumaticShockComponent
public override void Shutdown()
{
base.Shutdown();
CleanupLocalPlayerDamageEffects();
if (_overlay != null)
{
_overlayManager.RemoveOverlay(_overlay);
Expand All @@ -175,6 +177,7 @@ public override void Shutdown()

private void OnLocalPlayerAttached(LocalPlayerAttachedEvent ev)
{
CleanupLocalPlayerDamageEffects();
_overlayManager.AddOverlay(_overlay!);
UpdateOverlayIntensity(ev.Entity);
var result = _audio.PlayGlobal(DamageMusic, Filter.Local(), true);
Expand All @@ -198,10 +201,33 @@ private void OnLocalPlayerAttached(LocalPlayerAttachedEvent ev)

private void OnLocalPlayerDetached(LocalPlayerDetachedEvent ev)
{
_overlayManager.RemoveOverlay(_overlay!);
_damageMusicStream = _audio.Stop(_damageMusicStream);
_painSoundStream = _audio.Stop(_painSoundStream);
_tinnitusStream = _audio.Stop(_tinnitusStream);
CleanupLocalPlayerDamageEffects();
}

private void CleanupLocalPlayerDamageEffects()
{
if (_overlay != null)
_overlayManager.RemoveOverlay(_overlay);
if (_damageMusicStream is {} dm)
{
_damageMusicStream = null;
if (Exists(dm))
QueueDel(dm);
}

if (_painSoundStream is {} ps)
{
_painSoundStream = null;
if (Exists(ps))
QueueDel(ps);
}

if (_tinnitusStream is {} ts)
{
_tinnitusStream = null;
if (Exists(ts))
QueueDel(ts);
}
if (_overlay != null)
{
_overlay.DamageStrength = 0f;
Expand All @@ -222,6 +248,24 @@ private void OnLocalPlayerDetached(LocalPlayerDetachedEvent ev)
TSFStatusMessageState.Message = null;
}

private bool HasLiveLocalControlledEntity()
{
return _playerManager.LocalEntity is { } le && Exists(le);
}

private void TryCleanupOrphanedLocalPlayerEffects()
{
if (HasLiveLocalControlledEntity())
return;
CleanupLocalPlayerDamageEffects();
}

public override void Update(float frameTime)
{
TryCleanupOrphanedLocalPlayerEffects();
base.Update(frameTime);
}

private bool ShouldTriggerTinnitus(EntityUid uid, DamageableComponent damageable)
{
var piercingDelta = FixedPoint2.Zero;
Expand Down Expand Up @@ -273,11 +317,14 @@ private void OnThresholdChecked(ref MobThresholdChecked ev)
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
var local = _playerManager.LocalEntity;
if (local == null || _overlay == null)
TryCleanupOrphanedLocalPlayerEffects();
if (!HasLiveLocalControlledEntity())
return;

if (_overlay == null)
return;

var uid = local.Value;
var uid = _playerManager.LocalEntity!.Value;
if (TryComp(uid, out DamageableComponent? damageable))
{
var totalDamage = _damageable.GetTotalDamage((uid, damageable));
Expand Down Expand Up @@ -349,13 +396,13 @@ public override void FrameUpdate(float frameTime)
}

_disorientationBurstTime = Math.Max(0f, _disorientationBurstTime - frameTime);
if (local != null && _disorientationBurstTime > 0f && TryComp(local, out MobStateComponent? mobState) && mobState.CurrentState == MobState.Alive)
if (_disorientationBurstTime > 0f && TryComp(uid, out MobStateComponent? mobState) && mobState.CurrentState == MobState.Alive)
{
var strength = _disorientationBurstTime / DisorientationBurstDuration;
var kick = new Vector2(
(_random.NextFloat() - 0.5f) * 2f * DisorientationKickMagnitude * strength,
(_random.NextFloat() - 0.5f) * 2f * DisorientationKickMagnitude * strength);
_cameraRecoil.KickCamera(local.Value, kick);
_cameraRecoil.KickCamera(uid, kick);
}
}

Expand Down Expand Up @@ -558,7 +605,18 @@ private void TryUpdateStatusMessage(EntityUid uid)
var bucket = (int)(now / StatusMessageBucketSeconds);
var seed = bucket * 13 + phraseList.Length;
var idx = Math.Abs(seed % phraseList.Length);
TSFStatusMessageState.Message = phraseList[idx];
TSFStatusMessageState.DisplayUntil = now + StatusMessageDisplayDuration;
var phraseId = phraseList[idx];
var resolved = Loc.GetString(phraseId);
var revealSeconds = CountRunes(resolved) * TSFStatusMessageState.RevealSecondsPerRune;
TSFStatusMessageState.Message = phraseId;
TSFStatusMessageState.DisplayUntil = now + StatusMessageDisplayDuration + revealSeconds;
}

private static int CountRunes(string s)
{
var n = 0;
foreach (var _ in s.EnumerateRunes())
n++;
return n;
}
}
4 changes: 4 additions & 0 deletions Content.Client/_TSF/DamageEffects/TSFStatusMessageState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ namespace Content.Client._TSF.DamageEffects;
public static class TSFStatusMessageState
{
public static string? Message { get; set; }

public static double DisplayUntil { get; set; }

/// <summary> Wall-clock delay between revealing each Unicode extended grapheme (rune). </summary>
public const float RevealSecondsPerRune = 0.042f;
}
119 changes: 115 additions & 4 deletions Content.Client/_TSF/DamageEffects/TSFStatusMessageUIController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Timing;

Expand All @@ -25,6 +26,17 @@ public sealed class TSFStatusMessageUIController : UIController
private const float ShakeAmount = 3f;
private const int FontSize = 22;
private const int BottomMargin = 200;
private static readonly Color StatusTint = new(255, 45, 45, 255);
private const float HideFadeSeconds = 0.42f;

private string? _revealLocId;
private string? _revealFullText;
private int _revealTotalRunes;
private int _revealVisibleRunes;
private float _revealCarrySeconds;

private float _hideFadeRemaining;
private string? _hideFadeText;

public override void Initialize()
{
Expand Down Expand Up @@ -101,6 +113,10 @@ private void OnScreenUnload()
_panel = null;
_labelContainer = null;
_label = null;
_revealLocId = null;
_revealFullText = null;
_hideFadeRemaining = 0f;
_hideFadeText = null;
}
}

Expand All @@ -110,18 +126,113 @@ public override void FrameUpdate(FrameEventArgs args)
if (_label == null || _panel == null || _labelContainer == null)
return;
var now = _timing.RealTime.TotalSeconds;
if (TSFStatusMessageState.Message != null)
var msg = TSFStatusMessageState.Message;
if (msg != null)
{
_label.Text = Loc.GetString(TSFStatusMessageState.Message);
_hideFadeRemaining = 0f;
_hideFadeText = null;

if (msg != _revealLocId)
{
_revealLocId = msg;
_revealFullText = Loc.GetString(msg);
_revealTotalRunes = CountRunes(_revealFullText);
_revealVisibleRunes = 0;
_revealCarrySeconds = 0f;
}

_revealCarrySeconds += args.DeltaSeconds;
var step = TSFStatusMessageState.RevealSecondsPerRune;
while (_revealVisibleRunes < _revealTotalRunes && _revealCarrySeconds >= step)
{
_revealCarrySeconds -= step;
_revealVisibleRunes++;
}

_label.Text = TakeFirstRunes(_revealFullText!, _revealVisibleRunes);
_label.Modulate = StatusTint;
_panel.Visible = true;
var shakeX = (MathF.Sin((float)(now * 95)) + MathF.Sin((float)(now * 160) * 0.7f)) * ShakeAmount;
var shakeY = (MathF.Sin((float)(now * 110) + 1.3f) + MathF.Sin((float)(now * 180) * 0.6f)) * ShakeAmount;
LayoutContainer.SetPosition(_label, new Vector2(shakeX, shakeY));
}
else
{
_panel.Visible = false;
LayoutContainer.SetPosition(_label, Vector2.Zero);
if (_hideFadeRemaining <= 0f)
{
if (!string.IsNullOrEmpty(_revealFullText))
{
_hideFadeText = _revealFullText;
_hideFadeRemaining = HideFadeSeconds;
}
else if (_panel.Visible && !string.IsNullOrEmpty(_label.Text))
{
_hideFadeText = _label.Text;
_hideFadeRemaining = HideFadeSeconds;
}
}

_revealLocId = null;
_revealFullText = null;
_revealTotalRunes = 0;
_revealVisibleRunes = 0;
_revealCarrySeconds = 0f;

if (_hideFadeRemaining > 0f)
{
_hideFadeRemaining -= args.DeltaSeconds;
var alphaNorm = Math.Clamp(Math.Max(0f, _hideFadeRemaining) / HideFadeSeconds, 0f, 1f);
_label.Text = _hideFadeText ?? string.Empty;
_label.Modulate = StatusTint.WithAlpha(alphaNorm);
_panel.Visible = true;
var shakeScale = alphaNorm;
var shakeX = (MathF.Sin((float)(now * 95)) + MathF.Sin((float)(now * 160) * 0.7f)) * ShakeAmount * shakeScale;
var shakeY = (MathF.Sin((float)(now * 110) + 1.3f) + MathF.Sin((float)(now * 180) * 0.6f)) * ShakeAmount * shakeScale;
LayoutContainer.SetPosition(_label, new Vector2(shakeX, shakeY));

if (_hideFadeRemaining <= 0f)
{
_hideFadeText = null;
_label.Text = string.Empty;
_label.Modulate = StatusTint;
_panel.Visible = false;
LayoutContainer.SetPosition(_label, Vector2.Zero);
}
}
else
{
_hideFadeText = null;
_label.Text = string.Empty;
_label.Modulate = StatusTint;
_panel.Visible = false;
LayoutContainer.SetPosition(_label, Vector2.Zero);
}
}
}

private static int CountRunes(string s)
{
var n = 0;
foreach (var _ in s.EnumerateRunes())
n++;
return n;
}

private static string TakeFirstRunes(string s, int runeCount)
{
if (runeCount <= 0)
return string.Empty;

var seen = 0;
var endUtf16 = 0;
foreach (var r in s.EnumerateRunes())
{
if (seen >= runeCount)
break;
endUtf16 += r.Utf16SequenceLength;
seen++;
}

return s[..endUtf16];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ public sealed partial class VoteManager
private static readonly string[] TSFLobbyVotePresetIds =
{
"Extended",
"Nukeops",
"Secret",
"Zombie",
"Revolutionary",
"Secret"
};

private Dictionary<string, string> TSFGetLobbyVotePresets()
Expand Down
1 change: 0 additions & 1 deletion Content.Server/_TSF/Health/TSFPneumothoraxDamageSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ public override void Update(float frameTime)
continue;

pn.NextDamage = _timing.CurTime + Interval;
Dirty(uid, pn);

var spec = new DamageSpecifier();
spec.DamageDict["Asphyxiation"] = AsphyxPerTick;
Expand Down
18 changes: 0 additions & 18 deletions LICENSE-AGPLv3.txt
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@
Copyright (C) 2024-2026 The Space Frontier and TSF contributors

Исходный код на C#, прототипы YAML, шейдеры и прочие
человекочитаемые материалы в перечисленных ниже путях этого репозитория
распространяются на условиях GNU Affero General Public License v3.0
(полный текст лицензии следует ниже).

Content.Shared/_TSF/
Content.Server/_TSF/
Content.Client/_TSF/
Resources/Prototypes/_TSF/
Resources/Textures/_TSF/

Остальные материалы репозитория остаются на условиях, указанных в LICENSE.TXT,
в RobustToolbox/legal.md, а также в метаданных отдельных файлов и ассетов.

------------------------------------------------------------------------

GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007

Expand Down
Loading
Loading