From dcf21d1358bee2ae395b05ce7643e31d293cc1ed Mon Sep 17 00:00:00 2001 From: Vecortys <123871947+Vecortys@users.noreply.github.com> Date: Fri, 3 Jul 2026 22:16:02 +0300 Subject: [PATCH 1/6] =?UTF-8?q?speechbubble=20=D0=BF=D1=80=D0=BE=D1=8F?= =?UTF-8?q?=D0=B2=D0=BB=D1=8F=D0=B5=D1=82=20=D1=82=D0=B5=D0=BA=D1=81=D1=82?= =?UTF-8?q?=20=D0=BF=D0=BE=D1=81=D1=82=D0=B5=D0=BF=D0=B5=D0=BD=D0=BD=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Content.Client/Chat/UI/SpeechBubble.cs | 217 ++++++++++++++++++++++++- 1 file changed, 209 insertions(+), 8 deletions(-) diff --git a/Content.Client/Chat/UI/SpeechBubble.cs b/Content.Client/Chat/UI/SpeechBubble.cs index 2284015a6cb..b9042b3abfd 100644 --- a/Content.Client/Chat/UI/SpeechBubble.cs +++ b/Content.Client/Chat/UI/SpeechBubble.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Client.Chat.Managers; +using System.Text; // Arcane using Content.Shared.CCVar; using Content.Shared.Chat; using Content.Shared.Speech; @@ -38,6 +39,13 @@ public enum SpeechType : byte /// private static readonly TimeSpan FadeTime = TimeSpan.FromSeconds(0.25f); + // Arcane-start + private const float RevealRunesPerSecond = 15.5f; + private const float SpaceRevealWeight = 2.25f; + private static readonly TimeSpan MaxRevealTime = TimeSpan.FromSeconds(4); + protected virtual float RevealSpeedMultiplier => 1f; + // Arcane-end + /// /// The distance in world space to offset the speech bubble from the center of the entity. /// i.e. greater -> higher above the mob's head. @@ -55,6 +63,12 @@ public enum SpeechType : byte /// The time at which this bubble will die. /// private TimeSpan _deathTime; + // Arcane-start + private readonly TimeSpan _creationTime; + private readonly TimeSpan _revealTime; + private readonly float _maxRevealWeight; + private readonly List _textReveals = new(); + // Arcane-end public float VerticalOffset { get; set; } private float _verticalOffsetAchieved; @@ -95,6 +109,10 @@ public SpeechBubble(ChatMessage message, EntityUid senderEntity, string speechSt RectClipContent = true; var bubble = BuildBubble(message, speechStyleClass, fontColor); + // Arcane-start + bubble.HorizontalAlignment = HAlignment.Center; + bubble.VerticalAlignment = VAlignment.Bottom; + // Arcane-end AddChild(bubble); @@ -102,8 +120,15 @@ public SpeechBubble(ChatMessage message, EntityUid senderEntity, string speechSt bubble.Measure(Vector2Helpers.Infinity); ContentSize = bubble.DesiredSize; + // Arcane-start + bubble.MinWidth = ContentSize.X; + _creationTime = _timing.RealTime; + _maxRevealWeight = GetMaxRevealWeight(); + _revealTime = GetRevealTime(_maxRevealWeight); + _deathTime = _creationTime + TotalTime + _revealTime; + UpdateTextReveal(); _verticalOffsetAchieved = -ContentSize.Y; - _deathTime = _timing.RealTime + TotalTime; + // Arcane-end } protected abstract Control BuildBubble(ChatMessage message, string speechStyleClass, Color? fontColor = null); @@ -120,6 +145,10 @@ protected override void FrameUpdate(FrameEventArgs args) return; } + // Arcane-start + UpdateTextReveal(); + // Arcane-end + // Lerp to our new vertical offset if it's been modified. if (MathHelper.CloseToPercent(_verticalOffsetAchieved - VerticalOffset, 0, 0.1)) { @@ -175,6 +204,173 @@ private void Die() OnDied?.Invoke(_senderEntity, this); } + // Arcane-start + protected void SetRevealedMessage(RichTextLabel label, FormattedMessage message) + { + label.SetMessage(message); + + var revealWeight = CountRevealWeight(message); + if (revealWeight <= 0f) + return; + + _textReveals.Add(new SpeechTextReveal(label, message, revealWeight)); + } + + private float GetMaxRevealWeight() + { + var revealWeight = 0f; + + foreach (var reveal in _textReveals) + { + revealWeight = Math.Max(revealWeight, reveal.RevealWeight); + } + + return revealWeight; + } + + private TimeSpan GetRevealTime(float revealWeight) + { + if (revealWeight <= 0f) + return TimeSpan.Zero; + + var seconds = revealWeight / (RevealRunesPerSecond * RevealSpeedMultiplier); + return TimeSpan.FromSeconds(MathF.Min(seconds, (float) MaxRevealTime.TotalSeconds)); + } + + private void UpdateTextReveal() + { + if (_textReveals.Count == 0) + return; + + var progress = _revealTime <= TimeSpan.Zero + ? 1f + : MathHelper.Clamp((float) ((_timing.RealTime - _creationTime).TotalSeconds / _revealTime.TotalSeconds), 0f, 1f); + + var visibleWeight = _maxRevealWeight * progress; + + foreach (var reveal in _textReveals) + { + var visibleRunes = CountVisibleRunes(reveal.Message, Math.Min(visibleWeight, reveal.RevealWeight)); + if (visibleRunes == reveal.LastVisibleRunes) + continue; + + reveal.LastVisibleRunes = visibleRunes; + reveal.Label.SetMessage(CreateRevealedMessage(reveal.Message, visibleRunes)); + } + } + + private static float CountRevealWeight(FormattedMessage message) + { + var weight = 0f; + + foreach (var node in message.Nodes) + { + if (node.Name != null || node.Value.StringValue == null) + continue; + + foreach (var rune in node.Value.StringValue.EnumerateRunes()) + { + weight += GetRevealWeight(rune); + } + } + + return weight; + } + + private static int CountVisibleRunes(FormattedMessage message, float visibleWeight) + { + var remaining = visibleWeight; + var count = 0; + + foreach (var node in message.Nodes) + { + if (node.Name != null) + continue; + + var text = node.Value.StringValue; + if (text == null || remaining <= 0f) + continue; + + foreach (var rune in text.EnumerateRunes()) + { + if (remaining <= 0f) + break; + + count++; + remaining -= GetRevealWeight(rune); + } + } + + return count; + } + + private static FormattedMessage CreateRevealedMessage(FormattedMessage message, int visibleRunes) + { + var result = new FormattedMessage(message.Count); + var remaining = visibleRunes; + + foreach (var node in message.Nodes) + { + if (node.Name != null) + { + result.AddMarkupOrThrow(node.ToString()); + continue; + } + + var text = node.Value.StringValue; + if (text == null) + continue; + + AddRevealedText(result, text, ref remaining); + } + + return result; + } + + private static void AddRevealedText(FormattedMessage result, string text, ref int remainingVisibleRunes) + { + var visible = new StringBuilder(); + var hidden = new StringBuilder(); + + foreach (var rune in text.EnumerateRunes()) + { + if (remainingVisibleRunes > 0) + { + visible.Append(rune); + remainingVisibleRunes--; + continue; + } + + hidden.Append(rune); + } + + if (visible.Length > 0) + result.AddText(visible.ToString()); + + if (hidden.Length == 0) + return; + + result.PushColor(Color.Transparent); + result.AddText(hidden.ToString()); + result.Pop(); + } + + private static float GetRevealWeight(Rune rune) + { + return rune.Value is ' ' or '\n' or '\t' + ? SpaceRevealWeight + : 1f; + } + + private sealed class SpeechTextReveal(RichTextLabel label, FormattedMessage message, float revealWeight) + { + public readonly RichTextLabel Label = label; + public readonly FormattedMessage Message = message; + public readonly float RevealWeight = revealWeight; + public int LastVisibleRunes = -1; + } + // Arcane-end + /// /// Causes the speech bubble to start fading IMMEDIATELY. /// @@ -204,6 +400,8 @@ protected FormattedMessage ExtractAndFormatSpeechSubstring(ChatMessage message, public sealed class TextSpeechBubble : SpeechBubble { + protected override float RevealSpeedMultiplier => 3f; // Arcane + public TextSpeechBubble(ChatMessage message, EntityUid senderEntity, string speechStyleClass, Color? fontColor = null) : base(message, senderEntity, speechStyleClass, fontColor) { @@ -216,7 +414,7 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl MaxWidth = SpeechMaxWidth, }; - label.SetMessage(FormatSpeech(message.WrappedMessage, fontColor)); + SetRevealedMessage(label, FormatSpeech(message.WrappedMessage, fontColor)); // Arcane var panel = new PanelContainer { @@ -246,7 +444,7 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl MaxWidth = SpeechMaxWidth }; - label.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); + SetRevealedMessage(label, ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); // Arcane var unfanciedPanel = new PanelContainer { @@ -273,7 +471,7 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl //We'll be honest. *Yes* this is hacky. Doing this in a cleaner way would require a bottom-up refactor of how saycode handles sending chat messages. -Myr bubbleHeader.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleHeader", fontColor)); - bubbleContent.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); + SetRevealedMessage(bubbleContent, ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); // Arcane //As for below: Some day this could probably be converted to xaml. But that is not today. -Myr var mainPanel = new PanelContainer @@ -282,8 +480,7 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl Children = { bubbleContent }, ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.SpeechBubbleBackgroundOpacity)), HorizontalAlignment = HAlignment.Center, - VerticalAlignment = VAlignment.Bottom, - Margin = new Thickness(4, 14, 4, 2) + Margin = new Thickness(4, 0, 4, 2) // Arcane }; var headerPanel = new PanelContainer @@ -295,10 +492,14 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl VerticalAlignment = VAlignment.Top }; - var panel = new PanelContainer + // Arcane-start + var panel = new BoxContainer { - Children = { mainPanel, headerPanel } + Orientation = BoxContainer.LayoutOrientation.Vertical, + HorizontalAlignment = HAlignment.Center, + Children = { headerPanel, mainPanel } }; + // Arcane-end return panel; } From 6e051f216dcbe1b88552525fc2dea4865e048e67 Mon Sep 17 00:00:00 2001 From: PuroSlavKing <103608145+PuroSlavKing@users.noreply.github.com> Date: Sun, 4 May 2025 15:32:59 +0300 Subject: [PATCH 2/6] [Port] Doafter Cog (#1295) Co-authored-by: Doctor-Cpu <77215380+Doctor-Cpu@users.noreply.github.com> --- Content.Client/DoAfter/DoAfterOverlay.cs | 8 +++++++ .../Interface/Misc/progress_cog.rsi/cog.png | Bin 0 -> 663 bytes .../Interface/Misc/progress_cog.rsi/meta.json | 22 ++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 Resources/Textures/Backmen/Interface/Misc/progress_cog.rsi/cog.png create mode 100644 Resources/Textures/Backmen/Interface/Misc/progress_cog.rsi/meta.json diff --git a/Content.Client/DoAfter/DoAfterOverlay.cs b/Content.Client/DoAfter/DoAfterOverlay.cs index 09473ea0cfc..42ebb93df92 100644 --- a/Content.Client/DoAfter/DoAfterOverlay.cs +++ b/Content.Client/DoAfter/DoAfterOverlay.cs @@ -23,6 +23,7 @@ public sealed class DoAfterOverlay : Overlay private readonly SharedContainerSystem _container; private readonly Texture _barTexture; + private readonly SpriteSpecifier _cogTexture; // Backmen; private readonly ShaderInstance _unshadedShader; /// @@ -47,6 +48,7 @@ public DoAfterOverlay(IEntityManager entManager, IPrototypeManager protoManager, _progressColor = _entManager.System(); var sprite = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/progress_bar.rsi"), "icon"); _barTexture = _entManager.EntitySysManager.GetEntitySystem().Frame0(sprite); + _cogTexture = new SpriteSpecifier.Rsi(new("/Textures/Backmen/Interface/Misc/progress_cog.rsi"), "cog"); // Backmen _unshadedShader = protoManager.Index("unshaded").Instance(); } @@ -126,8 +128,14 @@ protected override void Draw(in OverlayDrawArgs args) var position = new Vector2(-_barTexture.Width / 2f / EyeManager.PixelsPerMeter, yOffset / scale + offset / EyeManager.PixelsPerMeter * scale); + // Backmen-Start + var cogPos = new Vector2(position.X + _barTexture.Width / scale / EyeManager.PixelsPerMeter, position.Y + _barTexture.Height * 2 / scale) / EyeManager.PixelsPerMeter; + var cogTexture = _entManager.System().GetFrame(_cogTexture, curTime); + // Backmen-End + // Draw the underlying bar texture handle.DrawTexture(_barTexture, position); + handle.DrawTexture(cogTexture, cogPos); // Backmen Color color; float elapsedRatio; diff --git a/Resources/Textures/Backmen/Interface/Misc/progress_cog.rsi/cog.png b/Resources/Textures/Backmen/Interface/Misc/progress_cog.rsi/cog.png new file mode 100644 index 0000000000000000000000000000000000000000..4d098e2717b0f76c1802362424df47b0da2f2b65 GIT binary patch literal 663 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|S$% zr;B4q#hkaZ_If!7@-!&_5aa7}6933}r>3K6@#-BGJ1u~?WA)+-?FFA1mv|q%c<}iO zU&Be#=eC}-RM(wR6Y%cs(n)jP|4Eqr#)jes5Y?eol#%neT1n$^wdG2{h6!*LZ9kpZvo0MtnFugZ2X9E?Y_7ftRGkk zHt>HhfBpOQ?Au#KY96NBY&-byV2VM7-2cjNpMLXfKXmnJi0FM42el2{8Lbm@{>9i` zPKaX^zx?@N&g9B@9oOo3Egst}>&ZW=!oc3g`6u<}Yl9=HYk$ruWeQ=cD462#AZvl- zgYSDMHb1}JaPNAT`C5iKwIU78u6FyyGO9ekuYSHRo#p6scKN;Q9aR~oi=1ORYZsR} zb0W_ZcaQR8kBT2|-FUWr!JYWH=4($bC~u2ucz$x<=^sCH1)3%^DOhMS;G&}b&8S=d zeE<3U&)W~n?d816 zq!yM6%{u)!MDo$s%KQr536Fx0-tr2r+|yFNy5|k&g!5~K6T}qQ--in7DTXJkv_H7j zvupaX;~(xZ%??{2T3qj-$+=HG+Ux2qnTqKg{b#G&wuF3TKcP}__uk?tpXTQuGlf#> zuKfs&UJx?>`yc*-HTDT#rRH#(e!9vlRIzmCi^%!corEg>$XU(^J5c{d@CVygh6KYC T@d;~yDUHF?)z4*}Q$iB}Z#X8K literal 0 HcmV?d00001 diff --git a/Resources/Textures/Backmen/Interface/Misc/progress_cog.rsi/meta.json b/Resources/Textures/Backmen/Interface/Misc/progress_cog.rsi/meta.json new file mode 100644 index 00000000000..df81cc1146e --- /dev/null +++ b/Resources/Textures/Backmen/Interface/Misc/progress_cog.rsi/meta.json @@ -0,0 +1,22 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/tgstation/tgstation/blob/886ca0f8dddf83ecaf10c92ff106172722352192/icons/effects/progessbar.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "cog", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} From 1b32c32e843d2abe0c248d3cc5e1df1c0ddacc9f Mon Sep 17 00:00:00 2001 From: PuroSlavKing <103608145+PuroSlavKing@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:05:23 +0300 Subject: [PATCH 3/6] [Port] Combat Indicator (#30) --- Content.Client/CombatMode/CombatModeSystem.cs | 52 +++++++++++++++++- .../Options/UI/Tabs/AccessibilityTab.xaml | 1 + .../Options/UI/Tabs/AccessibilityTab.xaml.cs | 1 + .../CombatMode/SharedCombatModeSystem.cs | 22 ++++++-- .../_Europa/CCVar/CCVars.Accessibility.cs | 12 ++++ .../_europa/escape-menu/ui/options-menu.ftl | 1 + .../_europa/escape-menu/ui/options-menu.ftl | 1 + .../Effects/combat_mode.rsi/combat_mode.png | Bin 0 -> 710 bytes .../_Europa/Effects/combat_mode.rsi/meta.json | 30 ++++++++++ 9 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 Content.Shared/_Europa/CCVar/CCVars.Accessibility.cs create mode 100644 Resources/Locale/en-US/_europa/escape-menu/ui/options-menu.ftl create mode 100644 Resources/Locale/ru-RU/_europa/escape-menu/ui/options-menu.ftl create mode 100644 Resources/Textures/_Europa/Effects/combat_mode.rsi/combat_mode.png create mode 100644 Resources/Textures/_Europa/Effects/combat_mode.rsi/meta.json diff --git a/Content.Client/CombatMode/CombatModeSystem.cs b/Content.Client/CombatMode/CombatModeSystem.cs index a364f107aeb..de2d36cc658 100644 --- a/Content.Client/CombatMode/CombatModeSystem.cs +++ b/Content.Client/CombatMode/CombatModeSystem.cs @@ -2,10 +2,13 @@ using Content.Client.NPC.HTN; using Content.Shared.CCVar; using Content.Shared.CombatMode; +using Content.Shared.StatusIcon.Components; +using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Input; using Robust.Client.Player; using Robust.Shared.Configuration; +using Robust.Shared.Utility; namespace Content.Client.CombatMode; @@ -21,14 +24,18 @@ public sealed partial class CombatModeSystem : SharedCombatModeSystem /// Raised whenever combat mode changes. /// public event Action? LocalPlayerCombatModeUpdated; + private EntityQuery _spriteQuery; // Europa public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnHandleState); - + SubscribeLocalEvent(UpdateCombatModeIndicator); // Europa Subs.CVar(_cfg, CCVars.CombatModeIndicatorsPointShow, OnShowCombatIndicatorsChanged, true); + Subs.CVar(_cfg, CCVars.CombatIndicator, (bool value) => OnShowCombatIndicatorChanged(value), true); // Europa + + _spriteQuery = GetEntityQuery(); // Europa } private void OnHandleState(EntityUid uid, CombatModeComponent component, ref AfterAutoHandleStateEvent args) @@ -91,4 +98,47 @@ private void OnShowCombatIndicatorsChanged(bool isShow) _overlayManager.RemoveOverlay(); } } + + // Europa-Start + private bool _combatIndicatorEnabled = false; + + private void OnShowCombatIndicatorChanged(bool value) + { + _combatIndicatorEnabled = value; + } + + private void UpdateCombatModeIndicator(EntityUid uid, CombatModeComponent comp, ref GetStatusIconsEvent _) + { + if (!_combatIndicatorEnabled) + { + if (_spriteQuery.TryComp(uid, out var sprite) && sprite.LayerMapTryGet("combat_mode_indicator", out var layerToRemove)) + { + sprite.RemoveLayer(layerToRemove); + } + return; + } + + if (comp.IsInCombatMode) + { + if (!_spriteQuery.TryComp(uid, out var sprite)) + return; + + if (!sprite.LayerMapTryGet("combat_mode_indicator", out var layer)) + { + if (!_spriteQuery.TryComp(uid, out var sprite2)) + return; + + layer = sprite2.AddLayer(new SpriteSpecifier.Rsi(new ResPath("_Europa/Effects/combat_mode.rsi"), "combat_mode")); + sprite2.LayerMapSet("combat_mode_indicator", layer); + } + } + else + { + if (_spriteQuery.TryComp(uid, out var sprite) && sprite.LayerMapTryGet("combat_mode_indicator", out var layerToRemove)) + { + sprite.RemoveLayer(layerToRemove); + } + } + } + // Europa-End } diff --git a/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml index 4e1508f710b..2ae993d4e48 100644 --- a/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml +++ b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml @@ -4,6 +4,7 @@ + diff --git a/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs index 7ae16b77929..52592f5d856 100644 --- a/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs @@ -14,6 +14,7 @@ public AccessibilityTab() Control.AddOptionCheckBox(CCVars.ChatEnableColorName, EnableColorNameCheckBox); Control.AddOptionCheckBox(CCVars.AccessibilityColorblindFriendly, ColorblindFriendlyCheckBox); + Control.AddOptionCheckBox(CCVars.CombatIndicator, CombatIndicatorCheckBox); // Europa Control.AddOptionCheckBox(CCVars.ReducedMotion, ReducedMotionCheckBox); Control.AddOptionPercentSlider(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider); Control.AddOptionPercentSlider(CCVars.ChatWindowOpacity, ChatWindowOpacitySlider); diff --git a/Content.Shared/CombatMode/SharedCombatModeSystem.cs b/Content.Shared/CombatMode/SharedCombatModeSystem.cs index d96e1371d63..717561e1fda 100644 --- a/Content.Shared/CombatMode/SharedCombatModeSystem.cs +++ b/Content.Shared/CombatMode/SharedCombatModeSystem.cs @@ -1,5 +1,8 @@ using Content.Shared.Actions; +using Content.Shared.Bed.Sleep; using Content.Shared.Mind; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Systems; using Content.Shared.MouseRotator; using Content.Shared.Movement.Components; using Content.Shared.Popups; @@ -11,9 +14,10 @@ namespace Content.Shared.CombatMode; public abstract partial class SharedCombatModeSystem : EntitySystem { [Dependency] protected IGameTiming Timing = default!; - [Dependency] private SharedActionsSystem _actionsSystem = default!; - [Dependency] private SharedPopupSystem _popup = default!; - [Dependency] private SharedMindSystem _mind = default!; + [Dependency] private SharedActionsSystem _actionsSystem = default!; + [Dependency] private SharedPopupSystem _popup = default!; + [Dependency] private SharedMindSystem _mind = default!; + [Dependency] private MobStateSystem _mobState = default!; // Europa public override void Initialize() { @@ -45,8 +49,8 @@ private void OnActionPerform(EntityUid uid, CombatModeComponent component, Toggl args.Handled = true; SetInCombatMode(uid, !component.IsInCombatMode, component); - var msg = component.IsInCombatMode ? "action-popup-combat-enabled" : "action-popup-combat-disabled"; - _popup.PopupClient(Loc.GetString(msg), args.Performer, args.Performer); +// var msg = component.IsInCombatMode ? "action-popup-combat-enabled" : "action-popup-combat-disabled"; // Europa-Remove +// _popup.PopupClient(Loc.GetString(msg), args.Performer, args.Performer); // Europa-Remove } public void SetCanDisarm(EntityUid entity, bool canDisarm, CombatModeComponent? component = null) @@ -70,6 +74,14 @@ public virtual void SetInCombatMode(EntityUid entity, bool value, CombatModeComp if (component.IsInCombatMode == value) return; + // Europa-Start | Dont let entity gone postal when unconscious + if (_mobState.IsDead(entity) || _mobState.IsCritical(entity) || HasComp(entity)) + { + if (value) + return; + } + // Europa-End + component.IsInCombatMode = value; Dirty(entity, component); diff --git a/Content.Shared/_Europa/CCVar/CCVars.Accessibility.cs b/Content.Shared/_Europa/CCVar/CCVars.Accessibility.cs new file mode 100644 index 00000000000..0b4a3928dfd --- /dev/null +++ b/Content.Shared/_Europa/CCVar/CCVars.Accessibility.cs @@ -0,0 +1,12 @@ +using Robust.Shared.Configuration; + +namespace Content.Shared.CCVar; + +public sealed partial class CCVars +{ + /// + /// When false - dont show combat indicator. + /// + public static readonly CVarDef CombatIndicator = + CVarDef.Create("accessibility.CombatIndicator", true, CVar.CLIENTONLY | CVar.ARCHIVE); +} diff --git a/Resources/Locale/en-US/_europa/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/_europa/escape-menu/ui/options-menu.ftl new file mode 100644 index 00000000000..58649621aab --- /dev/null +++ b/Resources/Locale/en-US/_europa/escape-menu/ui/options-menu.ftl @@ -0,0 +1 @@ +ui-options-combat-indicator = Show combat mode indicator diff --git a/Resources/Locale/ru-RU/_europa/escape-menu/ui/options-menu.ftl b/Resources/Locale/ru-RU/_europa/escape-menu/ui/options-menu.ftl new file mode 100644 index 00000000000..e46c6d02bcf --- /dev/null +++ b/Resources/Locale/ru-RU/_europa/escape-menu/ui/options-menu.ftl @@ -0,0 +1 @@ +ui-options-combat-indicator = Отображение индикатора боевого режима diff --git a/Resources/Textures/_Europa/Effects/combat_mode.rsi/combat_mode.png b/Resources/Textures/_Europa/Effects/combat_mode.rsi/combat_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..40c2444eb5085799873fe4481307aa3469020e7e GIT binary patch literal 710 zcmeAS@N?(olHy`uVBq!ia0vp^2|(Py!3HG1+{xJmq&N#aB8wRq_>O=u<5X=vX$A(S z3Qrfukcv5P@9r&J?I7atu(eQ=OT)3#YC%lb0v4vLOldzqdp-U!Z|=jI#<$|JkMUDWPx|{e{N(v7hm;v-(p?ELFLbWQ5t?Yi>jyyK7i4cQtQv+uj@|J3#K{^q|DT*eG%7z~&X@Gy|d_{&hS z?>FOznjHHD(?0(U{M7lz#P{nPc9+?wYgrQNg6DND`^G$NpZsTLOEG1q`^}&24*U`` zFst8R^Wsn1eDh`Ne=c0lkf8a)Y;A6_2&B8 zeb3riXY@Dkyz=wUpVQATU*z5)KViSg<&wCawq^DYsy;3J{PU;Fx1fi0PB9Hz?r&Kr zBe(9*&pUsaAKhMZdQ1tar%$9Q}Ze&;L8~9sU)0`{bPy(NYFHiJ&t6 aGS!@YT-~~4!9rldWbkzLb6Mw<&;$UX;3~fW literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Europa/Effects/combat_mode.rsi/meta.json b/Resources/Textures/_Europa/Effects/combat_mode.rsi/meta.json new file mode 100644 index 00000000000..7264508ae9b --- /dev/null +++ b/Resources/Textures/_Europa/Effects/combat_mode.rsi/meta.json @@ -0,0 +1,30 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/modular_sand/icons/mob/combat_indicator.dmi | Edited by PuroSlavKing (Github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "combat_mode", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} From 79c38cf0ba5937e4cadb50179d007804844873f1 Mon Sep 17 00:00:00 2001 From: fox76055 Date: Wed, 5 Aug 2026 09:52:48 +0500 Subject: [PATCH 4/6] Update SpeechBubble.cs --- Content.Client/Chat/UI/SpeechBubble.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Client/Chat/UI/SpeechBubble.cs b/Content.Client/Chat/UI/SpeechBubble.cs index b9042b3abfd..7596df7d68f 100644 --- a/Content.Client/Chat/UI/SpeechBubble.cs +++ b/Content.Client/Chat/UI/SpeechBubble.cs @@ -465,7 +465,7 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl { ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.SpeechBubbleTextOpacity)), MaxWidth = SpeechMaxWidth, - Margin = new Thickness(2, 6, 2, 2), + Margin = new Thickness(2, 2, 2, 2), StyleClasses = { "bubbleContent" }, }; From 7f029db74dca32ad78e1fdd50fd3366655d809fd Mon Sep 17 00:00:00 2001 From: fox76055 Date: Wed, 5 Aug 2026 16:17:39 +0500 Subject: [PATCH 5/6] request --- Content.Client/Chat/UI/SpeechBubble.cs | 16 ++++++++++------ Content.Client/CombatMode/CombatModeSystem.cs | 6 +++--- Content.Client/DoAfter/DoAfterOverlay.cs | 10 +++++----- .../CombatMode/SharedCombatModeSystem.cs | 4 ++-- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Content.Client/Chat/UI/SpeechBubble.cs b/Content.Client/Chat/UI/SpeechBubble.cs index 7596df7d68f..b2c58bb6436 100644 --- a/Content.Client/Chat/UI/SpeechBubble.cs +++ b/Content.Client/Chat/UI/SpeechBubble.cs @@ -127,8 +127,9 @@ public SpeechBubble(ChatMessage message, EntityUid senderEntity, string speechSt _revealTime = GetRevealTime(_maxRevealWeight); _deathTime = _creationTime + TotalTime + _revealTime; UpdateTextReveal(); - _verticalOffsetAchieved = -ContentSize.Y; // Arcane-end + _verticalOffsetAchieved = -ContentSize.Y; + // _deathTime = _timing.RealTime + TotalTime; Arcane delete } protected abstract Control BuildBubble(ChatMessage message, string speechStyleClass, Color? fontColor = null); @@ -414,7 +415,7 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl MaxWidth = SpeechMaxWidth, }; - SetRevealedMessage(label, FormatSpeech(message.WrappedMessage, fontColor)); // Arcane + SetRevealedMessage(label, FormatSpeech(message.WrappedMessage, fontColor)); // Arcane label.SetMessage(FormatSpeech(message.WrappedMessage, fontColor)); -> SetRevealedMessage(label, FormatSpeech(message.WrappedMessage, fontColor)); var panel = new PanelContainer { @@ -444,7 +445,7 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl MaxWidth = SpeechMaxWidth }; - SetRevealedMessage(label, ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); // Arcane + SetRevealedMessage(label, ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); // Arcane label.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); -> SetRevealedMessage(label, ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); var unfanciedPanel = new PanelContainer { @@ -465,13 +466,13 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl { ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.SpeechBubbleTextOpacity)), MaxWidth = SpeechMaxWidth, - Margin = new Thickness(2, 2, 2, 2), + Margin = new Thickness(2, 2, 2, 2), // LuaM Margin = new Thickness(2, 6, 2, 2), -> Margin = new Thickness(2, 2, 2, 2), StyleClasses = { "bubbleContent" }, }; //We'll be honest. *Yes* this is hacky. Doing this in a cleaner way would require a bottom-up refactor of how saycode handles sending chat messages. -Myr bubbleHeader.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleHeader", fontColor)); - SetRevealedMessage(bubbleContent, ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); // Arcane + SetRevealedMessage(bubbleContent, ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); // Arcane bubbleContent.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); -> SetRevealedMessage(bubbleContent, ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); //As for below: Some day this could probably be converted to xaml. But that is not today. -Myr var mainPanel = new PanelContainer @@ -480,7 +481,8 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl Children = { bubbleContent }, ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.SpeechBubbleBackgroundOpacity)), HorizontalAlignment = HAlignment.Center, - Margin = new Thickness(4, 0, 4, 2) // Arcane + // VerticalAlignment = VAlignment.Bottom, Arcane delete + Margin = new Thickness(4, 0, 4, 2) // Arcane Margin = new Thickness(4, 14, 4, 2) -> Margin = new Thickness(4, 0, 4, 2) }; var headerPanel = new PanelContainer @@ -492,6 +494,8 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl VerticalAlignment = VAlignment.Top }; + //var panel = new PanelContainer Arcane delete + // Arcane-start var panel = new BoxContainer { diff --git a/Content.Client/CombatMode/CombatModeSystem.cs b/Content.Client/CombatMode/CombatModeSystem.cs index de2d36cc658..0a17a49f083 100644 --- a/Content.Client/CombatMode/CombatModeSystem.cs +++ b/Content.Client/CombatMode/CombatModeSystem.cs @@ -2,13 +2,13 @@ using Content.Client.NPC.HTN; using Content.Shared.CCVar; using Content.Shared.CombatMode; -using Content.Shared.StatusIcon.Components; -using Robust.Client.GameObjects; +using Content.Shared.StatusIcon.Components; // Europa +using Robust.Client.GameObjects; // Europa using Robust.Client.Graphics; using Robust.Client.Input; using Robust.Client.Player; using Robust.Shared.Configuration; -using Robust.Shared.Utility; +using Robust.Shared.Utility; // Europa namespace Content.Client.CombatMode; diff --git a/Content.Client/DoAfter/DoAfterOverlay.cs b/Content.Client/DoAfter/DoAfterOverlay.cs index 42ebb93df92..962d2237665 100644 --- a/Content.Client/DoAfter/DoAfterOverlay.cs +++ b/Content.Client/DoAfter/DoAfterOverlay.cs @@ -23,7 +23,7 @@ public sealed class DoAfterOverlay : Overlay private readonly SharedContainerSystem _container; private readonly Texture _barTexture; - private readonly SpriteSpecifier _cogTexture; // Backmen; + private readonly SpriteSpecifier _cogTexture; // Europa private readonly ShaderInstance _unshadedShader; /// @@ -48,7 +48,7 @@ public DoAfterOverlay(IEntityManager entManager, IPrototypeManager protoManager, _progressColor = _entManager.System(); var sprite = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/progress_bar.rsi"), "icon"); _barTexture = _entManager.EntitySysManager.GetEntitySystem().Frame0(sprite); - _cogTexture = new SpriteSpecifier.Rsi(new("/Textures/Backmen/Interface/Misc/progress_cog.rsi"), "cog"); // Backmen + _cogTexture = new SpriteSpecifier.Rsi(new("/Textures/Backmen/Interface/Misc/progress_cog.rsi"), "cog"); // Europa _unshadedShader = protoManager.Index("unshaded").Instance(); } @@ -128,14 +128,14 @@ protected override void Draw(in OverlayDrawArgs args) var position = new Vector2(-_barTexture.Width / 2f / EyeManager.PixelsPerMeter, yOffset / scale + offset / EyeManager.PixelsPerMeter * scale); - // Backmen-Start + // Europa-Start var cogPos = new Vector2(position.X + _barTexture.Width / scale / EyeManager.PixelsPerMeter, position.Y + _barTexture.Height * 2 / scale) / EyeManager.PixelsPerMeter; var cogTexture = _entManager.System().GetFrame(_cogTexture, curTime); - // Backmen-End + // Europa-End // Draw the underlying bar texture handle.DrawTexture(_barTexture, position); - handle.DrawTexture(cogTexture, cogPos); // Backmen + handle.DrawTexture(cogTexture, cogPos); // Europa Color color; float elapsedRatio; diff --git a/Content.Shared/CombatMode/SharedCombatModeSystem.cs b/Content.Shared/CombatMode/SharedCombatModeSystem.cs index 717561e1fda..95a3054d05e 100644 --- a/Content.Shared/CombatMode/SharedCombatModeSystem.cs +++ b/Content.Shared/CombatMode/SharedCombatModeSystem.cs @@ -1,8 +1,8 @@ using Content.Shared.Actions; -using Content.Shared.Bed.Sleep; +using Content.Shared.Bed.Sleep; // Europa using Content.Shared.Mind; using Content.Shared.Mobs; -using Content.Shared.Mobs.Systems; +using Content.Shared.Mobs.Systems; // Europa using Content.Shared.MouseRotator; using Content.Shared.Movement.Components; using Content.Shared.Popups; From 3c5b70d2ed3577504612185d6c3e3ed09ccedbf5 Mon Sep 17 00:00:00 2001 From: fox76055 Date: Wed, 5 Aug 2026 16:19:25 +0500 Subject: [PATCH 6/6] this better --- Content.Client/Chat/UI/SpeechBubble.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Content.Client/Chat/UI/SpeechBubble.cs b/Content.Client/Chat/UI/SpeechBubble.cs index b2c58bb6436..4372e5e80fb 100644 --- a/Content.Client/Chat/UI/SpeechBubble.cs +++ b/Content.Client/Chat/UI/SpeechBubble.cs @@ -494,16 +494,12 @@ protected override Control BuildBubble(ChatMessage message, string speechStyleCl VerticalAlignment = VAlignment.Top }; - //var panel = new PanelContainer Arcane delete - - // Arcane-start - var panel = new BoxContainer + var panel = new BoxContainer // Arcane var panel = new PanelContainer -> var panel = new BoxContainer { - Orientation = BoxContainer.LayoutOrientation.Vertical, - HorizontalAlignment = HAlignment.Center, + Orientation = BoxContainer.LayoutOrientation.Vertical, // Arcane + HorizontalAlignment = HAlignment.Center, // Arcane Children = { headerPanel, mainPanel } }; - // Arcane-end return panel; }