Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Networking/ChatMessage.cs
2 using Microsoft.Xna.Framework;
3 using System;
4 using System.Linq;
5 using System.Text;
6 
7 namespace Barotrauma.Networking
8 {
9  public enum ChatMessageType
10  {
11  Default = 0,
12  Error = 1,
13  Dead = 2,
14  Server = 3,
15  Radio = 4,
16  Private = 5,
17  Console = 6,
18  MessageBox = 7,
19  Order = 8,
20  ServerLog = 9,
21  ServerMessageBox = 10,
23  }
24 
25  public enum PlayerConnectionChangeType { None = 0, Joined = 1, Kicked = 2, Disconnected = 3, Banned = 4 }
26 
27  partial class ChatMessage
28  {
29  public const int MaxLength = 200;
30 
31  public const int MaxMessagesPerPacket = 10;
32 
33  public const float SpeakRange = 2000.0f;
34 
39  public const float SpeakRangeVOIP = 1000.0f;
40 
41  private static readonly string dateTimeFormatLongTimePattern = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern;
42 
43  public static Color[] MessageColor =
44  {
45  new Color(190, 198, 205), //default
46  new Color(204, 74, 78), //error
47  new Color(136, 177, 255), //dead
48  new Color(157, 225, 160), //server
49  new Color(238, 208, 0), //radio
50  new Color(64, 240, 89), //private
51  new Color(255, 255, 255), //console
52  new Color(255, 255, 255), //messagebox
53  new Color(255, 128, 0) //order
54  };
55 
56  public string Text;
57 
58  private string translatedText;
59  public string TranslatedText
60  {
61  get
62  {
63  if (Type == ChatMessageType.Radio && Sender is Item)
64  {
65  if (translatedText.IsNullOrEmpty())
66  {
67  translatedText = TextManager.Get(Text).Fallback(Text).Value;
68  }
69 
70  return translatedText;
71  }
72  if (Type.HasFlag(ChatMessageType.Server) || Type.HasFlag(ChatMessageType.Error) || Type.HasFlag(ChatMessageType.ServerLog))
73  {
74  if (translatedText.IsNullOrEmpty())
75  {
76  translatedText = TextManager.GetServerMessage(Text).Value;
77  }
78 
79  return translatedText;
80  }
81  else
82  {
83  return Text;
84  }
85  }
86  }
87 
90  public string IconStyle;
91 
93  public readonly Entity Sender;
94  public readonly Client SenderClient;
95 
96  public readonly string SenderName;
97 
98  private Color? customTextColor = null;
99 
100  public Color Color
101  {
102  get
103  {
104  if(customTextColor == null) return MessageColor[(int)Type];
105  return (Color)customTextColor;
106  }
107  set { customTextColor = value; }
108  }
109 
110  public static string GetTimeStamp()
111  {
112  return $"[{DateTime.Now.ToString(dateTimeFormatLongTimePattern)}] ";
113  }
114 
115  public string TextWithSender
116  {
117  get
118  {
119  return string.IsNullOrWhiteSpace(SenderName) ? TranslatedText : NetworkMember.ClientLogName(SenderClient, SenderName) + ": " + TranslatedText;
120  }
121  }
122 
123  public static UInt16 LastID = 0;
124 
125  public UInt16 NetStateID
126  {
127  get;
128  set;
129  }
130 
131  public ChatMode ChatMode { get; set; } = ChatMode.None;
132 
133  protected ChatMessage(string senderName, string text, ChatMessageType type, Entity sender, Client client, PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None, Color? textColor = null)
134  {
135  Text = text;
136  Type = type;
137 
138  Sender = sender;
139  SenderClient = client;
140 
141  SenderName = senderName;
142  ChangeType = changeType;
143 
144  customTextColor = textColor;
145  }
146 
147  public static ChatMessage Create(string senderName, string text, ChatMessageType type, Entity sender, Client client = null, PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None, Color? textColor = null)
148  {
149  return new ChatMessage(senderName, text, type, sender, client ?? GameMain.NetworkMember?.ConnectedClients?.Find(c => c.Character != null && c.Character == sender), changeType, textColor);
150  }
151 
152  public static string GetChatMessageCommand(string message, out string messageWithoutCommand)
153  {
154  messageWithoutCommand = message;
155 
156  int separatorIndex = message.IndexOf(";");
157  if (separatorIndex == -1) return "";
158 
159  //int colonIndex = message.IndexOf(":");
160 
161  string command = "";
162  try
163  {
164  command = message.Substring(0, separatorIndex);
165  command = command.Trim();
166  }
167 
168  catch
169  {
170  return command;
171  }
172 
173  messageWithoutCommand = message.Substring(separatorIndex + 1, message.Length - separatorIndex - 1).TrimStart();
174 
175  return command;
176  }
177 
182  public static float GetGarbleAmount(Entity listener, Entity sender, float range, float obstructionMultiplier = 2.0f)
183  {
184  if (listener == null || sender == null)
185  {
186  return 0.0f;
187  }
188  if (listener.WorldPosition == sender.WorldPosition) { return 0.0f; }
189 
190  float dist = Vector2.Distance(listener.WorldPosition, sender.WorldPosition);
191  if (dist > range) { return 1.0f; }
192 
193  Hull listenerHull = listener == null ? null : Hull.FindHull(listener.WorldPosition);
194  Hull sourceHull = sender == null ? null : Hull.FindHull(sender.WorldPosition);
195  if (sourceHull != listenerHull && obstructionMultiplier >= 1.0f)
196  {
197  if ((sourceHull == null || !sourceHull.GetConnectedHulls(includingThis: false, searchDepth: 2, ignoreClosedGaps: true).Contains(listenerHull)) &&
198  Submarine.CheckVisibility(listener.SimPosition, sender.SimPosition) != null)
199  {
200  dist = (dist + 100f) * obstructionMultiplier;
201  }
202  }
203  if (dist > range) { return 1.0f; }
204 
205  return dist / range;
206  }
207 
208  public string ApplyDistanceEffect(Character listener)
209  {
210  if (Sender == null) return Text;
211 
212  return ApplyDistanceEffect(listener, Sender, Text, SpeakRange);
213  }
214 
215  public static string ApplyDistanceEffect(Entity listener, Entity sender, string text, float range, float obstructionMultiplier = 2.0f)
216  {
217  return ApplyDistanceEffect(text, GetGarbleAmount(listener, sender, range, obstructionMultiplier));
218  }
219 
220  public static string ApplyDistanceEffect(string text, float garbleAmount)
221  {
222  if (garbleAmount < 0.3f) return text;
223  if (garbleAmount >= 1.0f) return "";
224 
225  int startIndex = Math.Max(text.IndexOf(':') + 1, 1);
226 
227  StringBuilder sb = new StringBuilder(text.Length);
228  for (int i = 0; i < text.Length; i++)
229  {
230  sb.Append((i > startIndex && Rand.Range(0.0f, 1.0f) < garbleAmount) ? '-' : text[i]);
231  }
232 
233  return sb.ToString();
234  }
235 
236  public static string ApplyDistanceEffect(string message, ChatMessageType type, Character sender, Character receiver)
237  {
238  if (sender == null) { return ""; }
239  float range = SpeakRange;
240  if (type == ChatMessageType.Default && sender.SpeechImpediment > 0)
241  {
242  range *= 1.0f - sender.SpeechImpediment / 100.0f;
243  }
244  string spokenMsg = ApplyDistanceEffect(receiver, sender, message, range, 3.0f);
245 
246  switch (type)
247  {
248  case ChatMessageType.Default:
249  if (receiver != null && !receiver.IsDead)
250  {
251  return spokenMsg;
252  }
253  break;
254  case ChatMessageType.Radio:
255  case ChatMessageType.Order:
256  if (receiver?.Inventory != null && !receiver.IsDead)
257  {
258  foreach (Item receiverItem in receiver.Inventory.AllItems.Where(i => i.GetComponent<WifiComponent>()?.LinkToChat ?? false))
259  {
260  if (sender.Inventory == null || !receiver.HasEquippedItem(receiverItem)) { continue; }
261 
262  foreach (Item senderItem in sender.Inventory.AllItems.Where(i => i.GetComponent<WifiComponent>()?.LinkToChat ?? false))
263  {
264  if (!sender.HasEquippedItem(senderItem)) { continue; }
265 
266  var receiverRadio = receiverItem.GetComponent<WifiComponent>();
267  var senderRadio = senderItem.GetComponent<WifiComponent>();
268  if (!receiverRadio.CanReceive(senderRadio)) { continue; }
269 
270  string msg = ApplyDistanceEffect(receiverItem, senderItem, message, senderRadio.Range, obstructionMultiplier: 0);
271  if (sender.SpeechImpediment > 0.0f)
272  {
273  //speech impediment doesn't reduce the range when using a radio, but adds extra garbling
274  msg = ApplyDistanceEffect(msg, sender.SpeechImpediment / 100.0f);
275  }
276  return msg;
277  }
278  }
279  return spokenMsg;
280  }
281  break;
282  }
283 
284  return message;
285  }
286 
288  {
289  int length = 1 + //(byte)ServerNetObject.CHAT_MESSAGE
290  2 + //(UInt16)NetStateID
291  Encoding.UTF8.GetBytes(Text).Length + 2;
292 
293  return length;
294  }
295 
296  public static bool CanUseRadio(Character sender, bool ignoreJamming = false)
297  {
298  return CanUseRadio(sender, out _, ignoreJamming);
299  }
300 
301  public static bool CanUseRadio(Character sender, out WifiComponent radio, bool ignoreJamming = false)
302  {
303  radio = null;
304  if (sender?.Inventory == null || sender.Removed) { return false; }
305 
306  foreach (Item item in sender.Inventory.AllItems)
307  {
308  var wifiComponent = item.GetComponent<WifiComponent>();
309  if (wifiComponent == null || !wifiComponent.LinkToChat || !wifiComponent.CanTransmit(ignoreJamming) || !sender.HasEquippedItem(item)) { continue; }
310  if (radio == null || wifiComponent.Range > radio.Range)
311  {
312  radio = wifiComponent;
313  }
314  }
315  return radio?.Item != null;
316  }
317  }
318 }
bool HasEquippedItem(Item item, InvSlotType? slotType=null, Func< InvSlotType, bool > predicate=null)
virtual Vector2 WorldPosition
Definition: Entity.cs:49
virtual Vector2 SimPosition
Definition: Entity.cs:45
static NetworkMember NetworkMember
Definition: GameMain.cs:190
static Hull FindHull(Vector2 position, Hull guess=null, bool useWorldCoordinates=true, bool inclusive=true)
Returns the hull which contains the point (or null if it isn't inside any)
IEnumerable< Hull > GetConnectedHulls(bool includingThis, int? searchDepth=null, bool ignoreClosedGaps=false)
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
static string ApplyDistanceEffect(string text, float garbleAmount)
ChatMessage(string senderName, string text, ChatMessageType type, Entity sender, Client client, PlayerConnectionChangeType changeType=PlayerConnectionChangeType.None, Color? textColor=null)
static string GetChatMessageCommand(string message, out string messageWithoutCommand)
static string ApplyDistanceEffect(string message, ChatMessageType type, Character sender, Character receiver)
static bool CanUseRadio(Character sender, out WifiComponent radio, bool ignoreJamming=false)
const float SpeakRangeVOIP
This is shorter than the text chat speak range, because the voice chat is still intelligible (just qu...
static ChatMessage Create(string senderName, string text, ChatMessageType type, Entity sender, Client client=null, PlayerConnectionChangeType changeType=PlayerConnectionChangeType.None, Color? textColor=null)
static string ApplyDistanceEffect(Entity listener, Entity sender, string text, float range, float obstructionMultiplier=2.0f)
static float GetGarbleAmount(Entity listener, Entity sender, float range, float obstructionMultiplier=2.0f)
How much messages sent by sender should get garbled. Takes the distance between the entities and opt...
static bool CanUseRadio(Character sender, bool ignoreJamming=false)
static Body CheckVisibility(Vector2 rayStart, Vector2 rayEnd, bool ignoreLevel=false, bool ignoreSubs=false, bool ignoreSensors=true, bool ignoreDisabledWalls=true, bool ignoreBranches=true, Predicate< Fixture > blocksVisibilityPredicate=null)
Check visibility between two points (in sim units).