Port fancy speech bubbles (#29349)
@@ -16,6 +16,7 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
|
||||
private readonly TimeSpan _typingTimeout = TimeSpan.FromSeconds(2);
|
||||
private TimeSpan _lastTextChange;
|
||||
private bool _isClientTyping;
|
||||
private bool _isClientChatFocused;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -31,7 +32,8 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
|
||||
return;
|
||||
|
||||
// client typed something - show typing indicator
|
||||
ClientUpdateTyping(true);
|
||||
_isClientTyping = true;
|
||||
ClientUpdateTyping();
|
||||
_lastTextChange = _time.CurTime;
|
||||
}
|
||||
|
||||
@@ -42,7 +44,19 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
|
||||
return;
|
||||
|
||||
// client submitted text - hide typing indicator
|
||||
ClientUpdateTyping(false);
|
||||
_isClientTyping = false;
|
||||
ClientUpdateTyping();
|
||||
}
|
||||
|
||||
public void ClientChangedChatFocus(bool isFocused)
|
||||
{
|
||||
// don't update it if player don't want to show typing
|
||||
if (!_cfg.GetCVar(CCVars.ChatShowTypingIndicator))
|
||||
return;
|
||||
|
||||
// client submitted text - hide typing indicator
|
||||
_isClientChatFocused = isFocused;
|
||||
ClientUpdateTyping();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
@@ -55,23 +69,25 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
|
||||
var dif = _time.CurTime - _lastTextChange;
|
||||
if (dif > _typingTimeout)
|
||||
{
|
||||
// client didn't typed anything for a long time - hide indicator
|
||||
ClientUpdateTyping(false);
|
||||
// client didn't typed anything for a long time - change indicator
|
||||
_isClientTyping = false;
|
||||
ClientUpdateTyping();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClientUpdateTyping(bool isClientTyping)
|
||||
private void ClientUpdateTyping()
|
||||
{
|
||||
if (_isClientTyping == isClientTyping)
|
||||
return;
|
||||
|
||||
// check if player controls any entity.
|
||||
// check if player controls any pawn
|
||||
if (_playerManager.LocalEntity == null)
|
||||
return;
|
||||
|
||||
_isClientTyping = isClientTyping;
|
||||
RaisePredictiveEvent(new TypingChangedEvent(isClientTyping));
|
||||
var state = TypingIndicatorState.None;
|
||||
if (_isClientChatFocused)
|
||||
state = _isClientTyping ? TypingIndicatorState.Typing : TypingIndicatorState.Idle;
|
||||
|
||||
// send a networked event to server
|
||||
RaisePredictiveEvent(new TypingChangedEvent(state));
|
||||
}
|
||||
|
||||
private void OnShowTypingChanged(bool showTyping)
|
||||
@@ -79,7 +95,8 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
|
||||
// hide typing indicator immediately if player don't want to show it anymore
|
||||
if (!showTyping)
|
||||
{
|
||||
ClientUpdateTyping(false);
|
||||
_isClientTyping = false;
|
||||
ClientUpdateTyping();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingInd
|
||||
return;
|
||||
}
|
||||
|
||||
AppearanceSystem.TryGetData<bool>(uid, TypingIndicatorVisuals.IsTyping, out var isTyping, args.Component);
|
||||
var layerExists = args.Sprite.LayerMapTryGet(TypingIndicatorLayers.Base, out var layer);
|
||||
if (!layerExists)
|
||||
layer = args.Sprite.LayerMapReserveBlank(TypingIndicatorLayers.Base);
|
||||
@@ -44,6 +43,17 @@ public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingInd
|
||||
args.Sprite.LayerSetState(layer, proto.TypingState);
|
||||
args.Sprite.LayerSetShader(layer, proto.Shader);
|
||||
args.Sprite.LayerSetOffset(layer, proto.Offset);
|
||||
args.Sprite.LayerSetVisible(layer, isTyping);
|
||||
|
||||
AppearanceSystem.TryGetData<TypingIndicatorState>(uid, TypingIndicatorVisuals.State, out var state);
|
||||
args.Sprite.LayerSetVisible(layer, state != TypingIndicatorState.None);
|
||||
switch (state)
|
||||
{
|
||||
case TypingIndicatorState.Idle:
|
||||
args.Sprite.LayerSetState(layer, proto.IdleState);
|
||||
break;
|
||||
case TypingIndicatorState.Typing:
|
||||
args.Sprite.LayerSetState(layer, proto.TypingState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
||||
if (!HasComp<HolopadUserComponent>(uid))
|
||||
return;
|
||||
|
||||
var netEv = new HolopadUserTypingChangedEvent(GetNetEntity(uid.Value), ev.IsTyping);
|
||||
var netEv = new HolopadUserTypingChangedEvent(GetNetEntity(uid.Value), ev.State);
|
||||
RaiseNetworkEvent(netEv);
|
||||
}
|
||||
|
||||
|
||||
@@ -918,6 +918,11 @@ public sealed class ChatUIController : UIController
|
||||
_typingIndicator?.ClientChangedChatText();
|
||||
}
|
||||
|
||||
public void NotifyChatFocus(bool isFocused)
|
||||
{
|
||||
_typingIndicator?.ClientChangedChatFocus(isFocused);
|
||||
}
|
||||
|
||||
public void Repopulate()
|
||||
{
|
||||
foreach (var chat in _chats)
|
||||
|
||||
@@ -34,6 +34,8 @@ public partial class ChatBox : UIWidget
|
||||
ChatInput.Input.OnTextEntered += OnTextEntered;
|
||||
ChatInput.Input.OnKeyBindDown += OnInputKeyBindDown;
|
||||
ChatInput.Input.OnTextChanged += OnTextChanged;
|
||||
ChatInput.Input.OnFocusEnter += OnFocusEnter;
|
||||
ChatInput.Input.OnFocusExit += OnFocusExit;
|
||||
ChatInput.ChannelSelector.OnChannelSelect += OnChannelSelect;
|
||||
ChatInput.FilterButton.Popup.OnChannelFilter += OnChannelFilter;
|
||||
|
||||
@@ -174,6 +176,18 @@ public partial class ChatBox : UIWidget
|
||||
_controller.NotifyChatTextChange();
|
||||
}
|
||||
|
||||
private void OnFocusEnter(LineEditEventArgs args)
|
||||
{
|
||||
// Warn typing indicator about focus
|
||||
_controller.NotifyChatFocus(true);
|
||||
}
|
||||
|
||||
private void OnFocusExit(LineEditEventArgs args)
|
||||
{
|
||||
// Warn typing indicator about focus
|
||||
_controller.NotifyChatFocus(false);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
@@ -309,7 +309,7 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
||||
if (receiverHolopad.Comp.Hologram == null)
|
||||
continue;
|
||||
|
||||
_appearanceSystem.SetData(receiverHolopad.Comp.Hologram.Value.Owner, TypingIndicatorVisuals.IsTyping, ev.IsTyping);
|
||||
_appearanceSystem.SetData(receiverHolopad.Comp.Hologram.Value.Owner, TypingIndicatorVisuals.State, ev.State);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -591,7 +591,7 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
||||
continue;
|
||||
|
||||
if (user == null)
|
||||
_appearanceSystem.SetData(linkedHolopad.Comp.Hologram.Value.Owner, TypingIndicatorVisuals.IsTyping, false);
|
||||
_appearanceSystem.SetData(linkedHolopad.Comp.Hologram.Value.Owner, TypingIndicatorVisuals.State, false);
|
||||
|
||||
linkedHolopad.Comp.Hologram.Value.Comp.LinkedEntity = user;
|
||||
Dirty(linkedHolopad.Comp.Hologram.Value);
|
||||
|
||||
@@ -45,7 +45,7 @@ public abstract class SharedTypingIndicatorSystem : EntitySystem
|
||||
private void OnPlayerDetached(EntityUid uid, TypingIndicatorComponent component, PlayerDetachedEvent args)
|
||||
{
|
||||
// player left entity body - hide typing indicator
|
||||
SetTypingIndicatorEnabled(uid, false);
|
||||
SetTypingIndicatorState(uid, TypingIndicatorState.None);
|
||||
}
|
||||
|
||||
private void OnGotEquipped(Entity<TypingIndicatorClothingComponent> entity, ref ClothingGotEquippedEvent args)
|
||||
@@ -76,18 +76,18 @@ public abstract class SharedTypingIndicatorSystem : EntitySystem
|
||||
if (!_actionBlocker.CanEmote(uid.Value) && !_actionBlocker.CanSpeak(uid.Value))
|
||||
{
|
||||
// nah, make sure that typing indicator is disabled
|
||||
SetTypingIndicatorEnabled(uid.Value, false);
|
||||
SetTypingIndicatorState(uid.Value, TypingIndicatorState.None);
|
||||
return;
|
||||
}
|
||||
|
||||
SetTypingIndicatorEnabled(uid.Value, ev.IsTyping);
|
||||
SetTypingIndicatorState(uid.Value, ev.State);
|
||||
}
|
||||
|
||||
private void SetTypingIndicatorEnabled(EntityUid uid, bool isEnabled, AppearanceComponent? appearance = null)
|
||||
private void SetTypingIndicatorState(EntityUid uid, TypingIndicatorState state, AppearanceComponent? appearance = null)
|
||||
{
|
||||
if (!Resolve(uid, ref appearance, false))
|
||||
return;
|
||||
|
||||
_appearance.SetData(uid, TypingIndicatorVisuals.IsTyping, isEnabled, appearance);
|
||||
_appearance.SetData(uid, TypingIndicatorVisuals.State, state, appearance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ namespace Content.Shared.Chat.TypingIndicator;
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class TypingChangedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly bool IsTyping;
|
||||
public readonly TypingIndicatorState State;
|
||||
|
||||
public TypingChangedEvent(bool isTyping)
|
||||
public TypingChangedEvent(TypingIndicatorState state)
|
||||
{
|
||||
IsTyping = isTyping;
|
||||
State = state;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ public sealed partial class TypingIndicatorPrototype : IPrototype
|
||||
[DataField("typingState", required: true)]
|
||||
public string TypingState = default!;
|
||||
|
||||
[DataField("idleState", required: true)]
|
||||
public string IdleState = default!;
|
||||
|
||||
[DataField("offset")]
|
||||
public Vector2 Offset = new(0, 0);
|
||||
|
||||
|
||||
11
Content.Shared/Chat/TypingIndicator/TypingIndicatorState.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Chat.TypingIndicator;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum TypingIndicatorState
|
||||
{
|
||||
None = 0,
|
||||
Idle = 1,
|
||||
Typing = 2,
|
||||
}
|
||||
@@ -5,7 +5,7 @@ namespace Content.Shared.Chat.TypingIndicator;
|
||||
[Serializable, NetSerializable]
|
||||
public enum TypingIndicatorVisuals : byte
|
||||
{
|
||||
IsTyping
|
||||
State
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared.Chat.TypingIndicator;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
@@ -34,11 +35,11 @@ public sealed class HolopadUserTypingChangedEvent : EntityEventArgs
|
||||
/// <summary>
|
||||
/// The typing indicator state
|
||||
/// </summary>
|
||||
public readonly bool IsTyping;
|
||||
public readonly TypingIndicatorState State;
|
||||
|
||||
public HolopadUserTypingChangedEvent(NetEntity user, bool isTyping)
|
||||
public HolopadUserTypingChangedEvent(NetEntity user, TypingIndicatorState state)
|
||||
{
|
||||
User = user;
|
||||
IsTyping = isTyping;
|
||||
State = state;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,73 @@
|
||||
- type: typingIndicator
|
||||
id: default
|
||||
typingState: default0
|
||||
idleState: default3
|
||||
|
||||
- type: typingIndicator
|
||||
id: robot
|
||||
typingState: robot0
|
||||
idleState: robot3
|
||||
|
||||
- type: typingIndicator
|
||||
id: alien
|
||||
typingState: alien0
|
||||
idleState: alien3
|
||||
|
||||
- type: typingIndicator
|
||||
id: guardian
|
||||
typingState: guardian0
|
||||
idleState: guardian3
|
||||
|
||||
- type: typingIndicator
|
||||
id: holo
|
||||
typingState: holo0
|
||||
idleState: holo3
|
||||
offset: 0, 0.0625
|
||||
|
||||
- type: typingIndicator
|
||||
id: lawyer
|
||||
typingState: lawyer0
|
||||
idleState: lawyer3
|
||||
offset: 0, 0.125
|
||||
|
||||
- type: typingIndicator
|
||||
id: moth
|
||||
typingState: moth0
|
||||
idleState: moth3
|
||||
offset: 0, 0.125
|
||||
|
||||
- type: typingIndicator
|
||||
id: spider
|
||||
typingState: spider0
|
||||
idleState: spider3
|
||||
offset: 0, 0.125
|
||||
|
||||
- type: typingIndicator
|
||||
id: vox
|
||||
typingState: vox0
|
||||
idleState: vox0 # TODO add idle state sprite
|
||||
offset: -0.125, 0.125
|
||||
|
||||
- type: typingIndicator
|
||||
id: lizard
|
||||
typingState: lizard0
|
||||
idleState: lizard3
|
||||
offset: 0, 0.0625
|
||||
|
||||
- type: typingIndicator
|
||||
id: slime
|
||||
typingState: slime0
|
||||
idleState: slime3
|
||||
offset: 0, 0.125
|
||||
|
||||
- type: typingIndicator
|
||||
id: gingerbread
|
||||
typingState: gingerbread0
|
||||
idleState: gingerbread0
|
||||
offset: 0, 0.125
|
||||
|
||||
- type: typingIndicator
|
||||
id: diona
|
||||
typingState: diona0
|
||||
idleState: diona0
|
||||
offset: 0, 0.125
|
||||
|
||||
BIN
Resources/Textures/Effects/speech.rsi/alien3.png
Normal file
|
After Width: | Height: | Size: 522 B |
BIN
Resources/Textures/Effects/speech.rsi/alienroyal3.png
Normal file
|
After Width: | Height: | Size: 759 B |
BIN
Resources/Textures/Effects/speech.rsi/default3.png
Normal file
|
After Width: | Height: | Size: 431 B |
BIN
Resources/Textures/Effects/speech.rsi/guardian3.png
Normal file
|
After Width: | Height: | Size: 540 B |
BIN
Resources/Textures/Effects/speech.rsi/holo3.png
Normal file
|
After Width: | Height: | Size: 610 B |
BIN
Resources/Textures/Effects/speech.rsi/lawyer3.png
Normal file
|
After Width: | Height: | Size: 839 B |
BIN
Resources/Textures/Effects/speech.rsi/lizard3.png
Normal file
|
After Width: | Height: | Size: 647 B |
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24 | Moth sprites made by PuroSlavKing (Github) | Spider sprites made by PixelTheKermit (Github) | Lizard sprites made by AmalgoMyte (Github) | Diona and Gingerbread sprites made by YoungThugSS14 (Github)",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24 | Moth sprites made by PuroSlavKing (Github) | Spider sprites made by PixelTheKermit (Github) | Lizard sprites made by AmalgoMyte (Github) | Diona and Gingerbread sprites made by YoungThugSS14 (Github)",
|
||||
"states": [
|
||||
{
|
||||
"name": "alien0",
|
||||
@@ -26,9 +26,20 @@
|
||||
{
|
||||
"name": "alien2"
|
||||
},
|
||||
{
|
||||
"name": "alien3",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.5,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "alienroyal0",
|
||||
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
@@ -46,6 +57,19 @@
|
||||
{
|
||||
"name": "alienroyal2"
|
||||
},
|
||||
{
|
||||
"name": "alienroyal3",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "blob0",
|
||||
"delays": [
|
||||
@@ -125,6 +149,18 @@
|
||||
{
|
||||
"name": "default2"
|
||||
},
|
||||
{
|
||||
"name": "default3",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.5,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "diona0",
|
||||
"delays": [
|
||||
@@ -176,6 +212,18 @@
|
||||
{
|
||||
"name": "guardian2"
|
||||
},
|
||||
{
|
||||
"name": "guardian3",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.5,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "holo0",
|
||||
"delays": [
|
||||
@@ -195,6 +243,18 @@
|
||||
{
|
||||
"name": "holo2"
|
||||
},
|
||||
{
|
||||
"name": "holo3",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.5,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "lawyer0",
|
||||
"delays": [
|
||||
@@ -232,6 +292,21 @@
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "lawyer3",
|
||||
"delays": [
|
||||
[
|
||||
0.15,
|
||||
0.15,
|
||||
0.15,
|
||||
0.15,
|
||||
0.125,
|
||||
0.1,
|
||||
0.125,
|
||||
0.15
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "lizard0",
|
||||
"delays": [
|
||||
@@ -249,6 +324,18 @@
|
||||
{
|
||||
"name": "lizard2"
|
||||
},
|
||||
{
|
||||
"name": "lizard3",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.5,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "moth0",
|
||||
"delays": [
|
||||
@@ -286,6 +373,27 @@
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "moth3",
|
||||
"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,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "machine0",
|
||||
"delays": [
|
||||
@@ -320,6 +428,20 @@
|
||||
{
|
||||
"name": "robot2"
|
||||
},
|
||||
{
|
||||
"name": "robot3",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "slime0",
|
||||
"delays": [
|
||||
@@ -361,6 +483,18 @@
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "slime3",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.5,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "swarmer0",
|
||||
"delays": [
|
||||
@@ -429,6 +563,20 @@
|
||||
{
|
||||
"name": "syndibot2"
|
||||
},
|
||||
{
|
||||
"name": "syndibot3",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spider0",
|
||||
"delays": [
|
||||
@@ -446,6 +594,18 @@
|
||||
{
|
||||
"name": "spider2"
|
||||
},
|
||||
{
|
||||
"name": "spider3",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.5,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "vox0",
|
||||
"delays": [
|
||||
|
||||
BIN
Resources/Textures/Effects/speech.rsi/moth3.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
Resources/Textures/Effects/speech.rsi/robot3.png
Normal file
|
After Width: | Height: | Size: 787 B |
BIN
Resources/Textures/Effects/speech.rsi/slime3.png
Normal file
|
After Width: | Height: | Size: 504 B |
BIN
Resources/Textures/Effects/speech.rsi/spider3.png
Normal file
|
After Width: | Height: | Size: 591 B |
BIN
Resources/Textures/Effects/speech.rsi/syndibot3.png
Normal file
|
After Width: | Height: | Size: 869 B |