diff --git a/Subsurface/Content/Items/Diving/divinggear.xml b/Subsurface/Content/Items/Diving/divinggear.xml index 2a27490e8..dbd6e5b2d 100644 --- a/Subsurface/Content/Items/Diving/divinggear.xml +++ b/Subsurface/Content/Items/Diving/divinggear.xml @@ -38,7 +38,7 @@ - + diff --git a/Subsurface/Content/Items/Jobgear/doctorgear.xml b/Subsurface/Content/Items/Jobgear/doctorgear.xml index d7c5a2cd4..98e5e7412 100644 --- a/Subsurface/Content/Items/Jobgear/doctorgear.xml +++ b/Subsurface/Content/Items/Jobgear/doctorgear.xml @@ -10,7 +10,7 @@ - + diff --git a/Subsurface/Content/Items/Jobgear/headset.png b/Subsurface/Content/Items/Jobgear/headset.png new file mode 100644 index 000000000..c8ccc00c5 Binary files /dev/null and b/Subsurface/Content/Items/Jobgear/headset.png differ diff --git a/Subsurface/Content/Items/Jobgear/misc.xml b/Subsurface/Content/Items/Jobgear/misc.xml index 6029e6240..370b4e2b1 100644 --- a/Subsurface/Content/Items/Jobgear/misc.xml +++ b/Subsurface/Content/Items/Jobgear/misc.xml @@ -1,4 +1,29 @@  + + + + + + + + + + + + + + + + + + + + + - + diff --git a/Subsurface/Content/Jobs.xml b/Subsurface/Content/Jobs.xml index 0c2bc183a..1029f4b1a 100644 --- a/Subsurface/Content/Jobs.xml +++ b/Subsurface/Content/Jobs.xml @@ -1,17 +1,21 @@  - - - - - + + + + + + + + + @@ -21,10 +25,15 @@ - - - - + + + + + + + + + @@ -34,10 +43,15 @@ - - - - + + + + + + + + + @@ -47,12 +61,17 @@ - - - - - - + + + + + + + + + + + @@ -62,11 +81,16 @@ - - - - - + + + + + + + + + + @@ -76,8 +100,13 @@ - - - + + + + + + + + \ No newline at end of file diff --git a/Subsurface/Content/Sounds/UI/deadmsg.ogg b/Subsurface/Content/Sounds/UI/deadmsg.ogg new file mode 100644 index 000000000..bd694f2cf Binary files /dev/null and b/Subsurface/Content/Sounds/UI/deadmsg.ogg differ diff --git a/Subsurface/Content/Sounds/UI/radiomsg.ogg b/Subsurface/Content/Sounds/UI/radiomsg.ogg new file mode 100644 index 000000000..13fa4d47a Binary files /dev/null and b/Subsurface/Content/Sounds/UI/radiomsg.ogg differ diff --git a/Subsurface/Content/UI/inventoryIcons.png b/Subsurface/Content/UI/inventoryIcons.png index c4c5339c4..ab2341432 100644 Binary files a/Subsurface/Content/UI/inventoryIcons.png and b/Subsurface/Content/UI/inventoryIcons.png differ diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 3fd674551..c7ece6fc3 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -454,7 +454,7 @@ namespace Barotrauma { AnimController = new HumanoidAnimController(this, doc.Root.Element("ragdoll")); AnimController.TargetDir = Direction.Right; - inventory = new CharacterInventory(15, this); + inventory = new CharacterInventory(16, this); } else { @@ -594,39 +594,7 @@ namespace Barotrauma { if (info == null || info.Job == null) return; - for (int i = 0; i < info.Job.SpawnItemNames.Count; i++ ) - { - string itemName = info.Job.SpawnItemNames[i]; - - ItemPrefab itemPrefab = ItemPrefab.list.Find(ip => ip.Name == itemName) as ItemPrefab; - if (itemPrefab == null) - { - DebugConsole.ThrowError("Tried to spawn ''" + Name + "'' with the item ''" + itemName + "''. Matching item prefab not found."); - continue; - } - - Item item = new Item(itemPrefab, Position, null); - - if (info.Job.EquipSpawnItem[i]) - { - List allowedSlots = new List(item.AllowedSlots); - allowedSlots.Remove(LimbSlot.Any); - - inventory.TryPutItem(item, allowedSlots, false); - } - else - { - inventory.TryPutItem(item, item.AllowedSlots, false); - } - - if (item.Prefab.Name == "ID Card" && spawnPoint != null) - { - foreach (string s in spawnPoint.IdCardTags) - { - item.AddTag(s); - } - } - } + info.Job.GiveJobItems(this, spawnPoint); } public int GetSkillLevel(string skillName) @@ -988,6 +956,16 @@ namespace Barotrauma if (!Enabled) return; obstructVisionAmount = Math.Max(obstructVisionAmount - deltaTime, 0.0f); + + if (inventory!=null) + { + foreach (Item item in inventory.Items) + { + if (item == null || item.body == null || item.body.Enabled) continue; + item.Submarine = Submarine; + item.SetTransform(SimPosition, 0.0f); + } + } if (isDead) return; diff --git a/Subsurface/Source/Characters/Jobs/Job.cs b/Subsurface/Source/Characters/Jobs/Job.cs index c34b91089..d0e162094 100644 --- a/Subsurface/Source/Characters/Jobs/Job.cs +++ b/Subsurface/Source/Characters/Jobs/Job.cs @@ -26,15 +26,15 @@ namespace Barotrauma get { return prefab; } } - public List SpawnItemNames + public XElement SpawnItems { - get { return prefab.ItemNames; } + get { return prefab.Items; } } - public List EquipSpawnItem - { - get { return prefab.EquipItem; } - } + //public List EquipSpawnItem + //{ + // get { return prefab.EquipItem; } + //} public List Skills { @@ -84,6 +84,57 @@ namespace Barotrauma return (skill==null) ? 0 : skill.Level; } + public void GiveJobItems(Character character, WayPoint spawnPoint) + { + foreach (XElement itemElement in SpawnItems.Elements()) + { + InitializeJobItem(character, spawnPoint, itemElement); + + } + } + + private void InitializeJobItem(Character character, WayPoint spawnPoint, XElement itemElement, Item parentItem = null) + { + string itemName = ToolBox.GetAttributeString(itemElement, "name", ""); + + ItemPrefab itemPrefab = ItemPrefab.list.Find(ip => ip.Name == itemName) as ItemPrefab; + if (itemPrefab == null) + { + DebugConsole.ThrowError("Tried to spawn ''" + Name + "'' with the item ''" + itemName + "''. Matching item prefab not found."); + return; + } + + Item item = new Item(itemPrefab, character.Position, null); + + if (ToolBox.GetAttributeBool(itemElement, "equip", false)) + { + List allowedSlots = new List(item.AllowedSlots); + allowedSlots.Remove(LimbSlot.Any); + + character.Inventory.TryPutItem(item, allowedSlots, false); + } + else + { + character.Inventory.TryPutItem(item, item.AllowedSlots, false); + } + + if (item.Prefab.Name == "ID Card" && spawnPoint != null) + { + foreach (string s in spawnPoint.IdCardTags) + { + item.AddTag(s); + } + } + + + if (parentItem != null) parentItem.Combine(item); + + foreach (XElement childItemElement in itemElement.Elements()) + { + InitializeJobItem(character, spawnPoint, childItemElement, item); + } + } + public virtual XElement Save(XElement parentElement) { XElement jobElement = new XElement("job"); diff --git a/Subsurface/Source/Characters/Jobs/JobPrefab.cs b/Subsurface/Source/Characters/Jobs/JobPrefab.cs index 58b4f1452..426a1e3b4 100644 --- a/Subsurface/Source/Characters/Jobs/JobPrefab.cs +++ b/Subsurface/Source/Characters/Jobs/JobPrefab.cs @@ -10,9 +10,9 @@ namespace Barotrauma { public static List List; - //names of the items the Character spawns with - public List ItemNames; - public List EquipItem; + public readonly XElement Items; + public readonly List ItemNames; + //public List EquipItem; public List Skills; @@ -71,7 +71,7 @@ namespace Barotrauma AllowAlways = ToolBox.GetAttributeBool(element, "allowalways", false); ItemNames = new List(); - EquipItem = new List(); + //EquipItem = new List(); Skills = new List(); @@ -79,14 +79,20 @@ namespace Barotrauma { switch (subElement.Name.ToString().ToLower()) { - case "item": - string itemName = ToolBox.GetAttributeString(subElement, "name", ""); - bool equipItem = ToolBox.GetAttributeBool(subElement, "equip", false); - if (!string.IsNullOrEmpty(itemName)) + case "items": + Items = subElement; + foreach (XElement itemElement in subElement.Elements()) { + string itemName = ToolBox.GetAttributeString(subElement, "name", ""); ItemNames.Add(itemName); - EquipItem.Add(equipItem); } + //string itemName = ToolBox.GetAttributeString(subElement, "name", ""); + //bool equipItem = ToolBox.GetAttributeBool(subElement, "equip", false); + //if (!string.IsNullOrEmpty(itemName)) + //{ + // ItemNames.Add(itemName); + // EquipItem.Add(equipItem); + //} break; case "skills": foreach (XElement skillElement in subElement.Elements()) diff --git a/Subsurface/Source/GUI/GUI.cs b/Subsurface/Source/GUI/GUI.cs index 14d7e483b..19bb08050 100644 --- a/Subsurface/Source/GUI/GUI.cs +++ b/Subsurface/Source/GUI/GUI.cs @@ -18,8 +18,10 @@ namespace Barotrauma public enum GUISoundType { - Message = 0, - Click = 1 + Message, + RadioMessage, + DeadMessage, + Click } public class GUI @@ -75,8 +77,10 @@ namespace Barotrauma if (loadSounds) { - sounds = new Sound[2]; + sounds = new Sound[4]; sounds[(int)GUISoundType.Message] = Sound.Load("Content/Sounds/UI/UImsg.ogg", false); + sounds[(int)GUISoundType.RadioMessage] = Sound.Load("Content/Sounds/UI/radiomsg.ogg", false); + sounds[(int)GUISoundType.DeadMessage] = Sound.Load("Content/Sounds/UI/deadmsg.ogg", false); sounds[(int)GUISoundType.Click] = Sound.Load("Content/Sounds/UI/beep-shinymetal.ogg", false); } diff --git a/Subsurface/Source/GUI/GUITextBox.cs b/Subsurface/Source/GUI/GUITextBox.cs index 07a444ef2..eb5aaa945 100644 --- a/Subsurface/Source/GUI/GUITextBox.cs +++ b/Subsurface/Source/GUI/GUITextBox.cs @@ -72,6 +72,12 @@ namespace Barotrauma } } + public Color TextColor + { + get { return textBlock.TextColor; } + set { textBlock.TextColor = value; } + } + public override Color HoverColor { get diff --git a/Subsurface/Source/Items/CharacterInventory.cs b/Subsurface/Source/Items/CharacterInventory.cs index f4267c43f..ab9784bcf 100644 --- a/Subsurface/Source/Items/CharacterInventory.cs +++ b/Subsurface/Source/Items/CharacterInventory.cs @@ -11,7 +11,7 @@ namespace Barotrauma [Flags] public enum LimbSlot { - None = 0, Any = 1, RightHand = 2, LeftHand = 4, Head = 8, Torso = 16, Legs = 32 + None = 0, Any = 1, RightHand = 2, LeftHand = 4, Head = 8, Torso = 16, Legs = 32, Face=64 }; class CharacterInventory : Inventory @@ -21,7 +21,7 @@ namespace Barotrauma private Character character; public static LimbSlot[] limbSlots = new LimbSlot[] { - LimbSlot.Head, LimbSlot.Torso, LimbSlot.Legs, LimbSlot.LeftHand, LimbSlot.RightHand, + LimbSlot.Head, LimbSlot.Torso, LimbSlot.Legs, LimbSlot.LeftHand, LimbSlot.RightHand, LimbSlot.Face, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any}; @@ -58,7 +58,7 @@ namespace Barotrauma case 3: case 4: SlotPositions[i] = new Vector2( - spacing * 2 + rectWidth + (spacing + rectWidth) * (i - 3), + spacing * 2 + rectWidth + (spacing + rectWidth) * (i - 2), GameMain.GraphicsHeight - (spacing + rectHeight)*3); useOnSelfButton[i - 3] = new GUIButton( @@ -70,11 +70,17 @@ namespace Barotrauma }; + break; + case 5: + SlotPositions[i] = new Vector2( + spacing * 2 + rectWidth + (spacing + rectWidth) * (i - 5), + GameMain.GraphicsHeight - (spacing + rectHeight) * 3); + break; default: SlotPositions[i] = new Vector2( - spacing * 2 + rectWidth + (spacing + rectWidth) * ((i - 3)%5), - GameMain.GraphicsHeight - (spacing + rectHeight) * ((i>9) ? 2 : 1)); + spacing * 2 + rectWidth + (spacing + rectWidth) * ((i - 6)%5), + GameMain.GraphicsHeight - (spacing + rectHeight) * ((i>10) ? 2 : 1)); break; } } @@ -298,6 +304,13 @@ namespace Barotrauma new Vector2(18.0f, 20.0f), Vector2.One, SpriteEffects.None, 0.1f); } + else if (i==5) + { + spriteBatch.Draw(icons, new Vector2(slotRect.Center.X, slotRect.Center.Y), + new Rectangle(57,0,31,32), Color.White * 0.7f, 0.0f, + new Vector2(15.0f, 16.0f), Vector2.One, + SpriteEffects.None, 0.1f); + } } for (int i = 0; i < capacity; i++) @@ -334,7 +347,7 @@ namespace Barotrauma highlightedSlot = slotRect; } - UpdateSlot(spriteBatch, slotRect, i, Items[i], false, i>4 ? 0.2f : 0.4f); + UpdateSlot(spriteBatch, slotRect, i, Items[i], false, i>5 ? 0.2f : 0.4f); if (draggingItem!=null && draggingItem == Items[i]) draggingItemSlot = slotRect; } diff --git a/Subsurface/Source/Items/Components/Signal/WifiComponent.cs b/Subsurface/Source/Items/Components/Signal/WifiComponent.cs index 5211d7d29..554839f23 100644 --- a/Subsurface/Source/Items/Components/Signal/WifiComponent.cs +++ b/Subsurface/Source/Items/Components/Signal/WifiComponent.cs @@ -1,4 +1,6 @@ -using Microsoft.Xna.Framework; +using Barotrauma.Networking; +using Microsoft.Xna.Framework; +using System; using System.Collections.Generic; using System.Xml.Linq; @@ -6,11 +8,19 @@ namespace Barotrauma.Items.Components { class WifiComponent : ItemComponent { - private static List list = new List(); + private float range; + private int channel; + [HasDefaultValue(20000.0f, false)] + public float Range + { + get { return range; } + set { range = Math.Max(value, 0.0f); } + } + [InGameEditable, HasDefaultValue(1, true)] public int Channel { @@ -21,24 +31,66 @@ namespace Barotrauma.Items.Components } } + [Editable, HasDefaultValue(false, false)] + public bool LinkToChat + { + get; + set; + } + public WifiComponent(Item item, XElement element) : base (item, element) { list.Add(this); } + + public void Transmit(string signal) + { + if (!HasRequiredContainedItems(true)) return; + + var receivers = GetReceiversInRange(); + foreach (WifiComponent w in receivers) + { + var connections = w.item.Connections; + + w.ReceiveSignal(1, signal, connections == null ? null : connections.Find(c => c.Name == "signal_in"), item); + } + } + + private List GetReceiversInRange() + { + return list.FindAll(w => + w != this && w.channel == channel && + Vector2.Distance(item.WorldPosition, w.item.WorldPosition) <= Range); + } public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item sender, float power=0.0f) { - //prevent an ininite loop of wificomponents sending messages between each other - if (sender.GetComponent()!=null) return; + if (!HasRequiredContainedItems(false)) return; + + if (LinkToChat) + { + if (item.ParentInventory != null && + item.ParentInventory.Owner != null && + item.ParentInventory.Owner == Character.Controlled && + GameMain.NetworkMember != null) + { + signal = ChatMessage.ApplyDistanceEffect(item, sender, signal, range); + + GameMain.NetworkMember.AddChatMessage(signal, ChatMessageType.Radio); + } + } + + if (connection == null) return; switch (connection.Name) { case "signal_in": - foreach (WifiComponent wifiComp in list) + var receivers = GetReceiversInRange(); + + foreach (WifiComponent wifiComp in receivers) { - if (wifiComp == this || wifiComp.channel != channel) continue; wifiComp.item.SendSignal(stepsTaken, signal, "signal_out"); } break; diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index b195d2c94..8bc363a81 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -213,9 +213,9 @@ namespace Barotrauma if (linkedTo != null) { - foreach (MapEntity e in linkedTo) + for (int i = linkedTo.Count - 1; i >= 0; i-- ) { - e.RemoveLinked(this); + linkedTo[i].RemoveLinked(this); } linkedTo.Clear(); } diff --git a/Subsurface/Source/Networking/ChatMessage.cs b/Subsurface/Source/Networking/ChatMessage.cs new file mode 100644 index 000000000..28c8b33f9 --- /dev/null +++ b/Subsurface/Source/Networking/ChatMessage.cs @@ -0,0 +1,136 @@ +using Barotrauma.Items.Components; +using Barotrauma.Networking.ReliableMessages; +using Lidgren.Network; +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Barotrauma.Networking +{ + + enum ChatMessageType + { + Default, Error, Dead, Server, Radio + } + + class ChatMessage + { + public const float SpeakRange = 2000.0f; + + public static Color[] MessageColor = { Color.White, Color.Red, new Color(63, 72, 204), Color.LightGreen, Color.Yellow }; + + public readonly string Text; + + public ChatMessageType Type; + + public readonly Character Sender; + + public readonly string SenderName; + + public Color Color + { + get { return MessageColor[(int)Type]; } + } + + public string TextWithSender + { + get; + private set; + } + + private ChatMessage(string senderName, string text, ChatMessageType type, Character sender) + { + Text = text; + Type = type; + + Sender = sender; + + SenderName = senderName; + + TextWithSender = string.IsNullOrWhiteSpace(senderName) ? text : senderName + ": " + text; + } + + public static ChatMessage Create(string senderName, string text, ChatMessageType type, Character sender) + { + return new ChatMessage(senderName, text, type, sender); + } + + public static string GetChatMessageCommand(string message, out string messageWithoutCommand) + { + messageWithoutCommand = message; + + int separatorIndex = message.IndexOf(";"); + if (separatorIndex == -1) return ""; + + //int colonIndex = message.IndexOf(":"); + + string command = ""; + try + { + command = message.Substring(0, separatorIndex); + command = command.Trim(); + } + + catch + { + return command; + } + + messageWithoutCommand = message.Substring(separatorIndex + 1, message.Length - separatorIndex - 1).TrimStart(); + + return command; + } + + public string ApplyDistanceEffect(Character listener) + { + if (Sender == null) return Text; + + return ApplyDistanceEffect(listener, Sender, Text, SpeakRange); + } + + public static string ApplyDistanceEffect(Entity listener, Entity Sender, string text, float range) + { + if (listener.WorldPosition == Sender.WorldPosition) return text; + + float dist = Vector2.Distance(listener.WorldPosition, Sender.WorldPosition); + if (dist > range) return ""; + + if (Submarine.CheckVisibility(listener.SimPosition, Sender.SimPosition) != null) dist *= 2.0f; + if (dist > range) return ""; + + float garbleAmount = dist / range; + if (garbleAmount < 0.5f) return text; + + int startIndex = Math.Max(text.IndexOf(':') + 1, 1); + + StringBuilder sb = new StringBuilder(text.Length); + for (int i = 0; i < text.Length; i++) + { + sb.Append((i>startIndex && Rand.Range(0.0f, 1.0f) < garbleAmount) ? '-' : text[i]); + } + + return sb.ToString(); + } + + public void WriteNetworkMessage(NetOutgoingMessage msg) + { + msg.WriteRangedInteger(0, Enum.GetValues(typeof(ChatMessageType)).Length, (byte)Type); + msg.Write(Sender == null ? (ushort)0 : Sender.ID); + msg.Write(SenderName); + + msg.Write(Text); + } + + public static ChatMessage ReadNetworkMessage(NetBuffer msg) + { + ChatMessageType type = (ChatMessageType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(ChatMessageType)).Length); + ushort senderId = msg.ReadUInt16(); + string senderName = msg.ReadString(); + string text = msg.ReadString(); + + return new ChatMessage(senderName, text, type, Entity.FindEntityByID(senderId) as Character); + } + } +} diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 42a608e12..48636fcae 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using Barotrauma.Networking.ReliableMessages; using FarseerPhysics; using System.IO; +using System.Linq; +using Barotrauma.Items.Components; namespace Barotrauma.Networking { @@ -555,9 +557,9 @@ namespace Barotrauma.Networking break; case (byte)PacketTypes.Chatmessage: - ChatMessageType messageType = (ChatMessageType)inc.ReadByte(); + //ChatMessageType messageType = (ChatMessageType)inc.ReadByte(); - AddChatMessage(inc.ReadString(), messageType); + AddChatMessage(ChatMessage.ReadNetworkMessage(inc)); break; case (byte)PacketTypes.NetworkEvent: //read the data from the message and update client state accordingly @@ -958,19 +960,33 @@ namespace Barotrauma.Networking return character; } - public override void SendChatMessage(string message, ChatMessageType type = ChatMessageType.Default) + public override void SendChatMessage(string message) { //AddChatMessage(message); if (client.ServerConnection == null) return; - type = (Screen.Selected == GameMain.GameScreen && - (myCharacter == null || myCharacter.IsDead)) ? ChatMessageType.Dead : ChatMessageType.Default; + var type = ChatMessageType.Default; + + if (Screen.Selected == GameMain.GameScreen && (myCharacter == null || myCharacter.IsDead)) + { + type = ChatMessageType.Dead; + } + else + { + string command = ChatMessage.GetChatMessageCommand(message, out message).ToLower(); + + if (CanUseRadio(Character.Controlled)) type = ChatMessageType.Radio; + } + + var chatMessage = ChatMessage.Create( + gameStarted && myCharacter != null ? myCharacter.Name : name, + message, type, gameStarted ? myCharacter : null); + ReliableMessage msg = reliableChannel.CreateMessage(); msg.InnerMessage.Write((byte)PacketTypes.Chatmessage); - msg.InnerMessage.Write((byte)type); - msg.InnerMessage.Write(message); + chatMessage.WriteNetworkMessage(msg.InnerMessage); reliableChannel.SendMessage(msg, client.ServerConnection); } diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index cc142820e..7c260205e 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -7,6 +7,7 @@ using Lidgren.Network; using Microsoft.Xna.Framework; using RestSharp; using Barotrauma.Networking.ReliableMessages; +using Barotrauma.Items.Components; namespace Barotrauma.Networking { @@ -503,9 +504,10 @@ namespace Barotrauma.Networking break; case (byte)PacketTypes.Chatmessage: - ChatMessageType messageType = (ChatMessageType)inc.ReadByte(); + //SendChatMessage(ChatMessage.ReadNetworkMessage(inc)); + //!!!!!!!!!!! - SendChatMessage(inc.ReadString(), messageType); + ReadChatMessage(inc); break; case (byte)PacketTypes.PlayerLeft: @@ -953,7 +955,8 @@ namespace Barotrauma.Networking GameMain.GameScreen.Select(); - AddChatMessage("Press TAB to chat. Use ''d;'' to talk to dead players and spectators, and ''player name;'' to only send the message to a specific player.", ChatMessageType.Server); + AddChatMessage("Press TAB to chat. Use ''d;'' to talk to dead players and spectators, " + +"and ''player name;'' to only send the message to a specific player.", ChatMessageType.Server); yield return CoroutineStatus.Success; } @@ -1081,7 +1084,7 @@ namespace Barotrauma.Networking if (string.IsNullOrWhiteSpace(msg)) msg = client.name + " has left the server"; if (string.IsNullOrWhiteSpace(targetmsg)) targetmsg = "You have left the server"; - Log(msg, messageColor[(int)ChatMessageType.Server]); + Log(msg, ChatMessage.MessageColor[(int)ChatMessageType.Server]); NetOutgoingMessage outmsg = server.CreateMessage(); outmsg.Write((byte)PacketTypes.KickedOut); @@ -1370,33 +1373,79 @@ namespace Barotrauma.Networking } return true; - } - public override void SendChatMessage(string message, ChatMessageType type = ChatMessageType.Server) + private void ReadChatMessage(NetIncomingMessage inc) + { + ChatMessage message = ChatMessage.ReadNetworkMessage(inc); + + List recipients = new List(); + + foreach (Client c in ConnectedClients) + { + switch (message.Type) + { + case ChatMessageType.Dead: + if (c.Character != null && !c.Character.IsDead) continue; + break; + case ChatMessageType.Default: + if (message.Sender != null && c.Character != null && message.Sender != c.Character) + { + if (Vector2.Distance(message.Sender.WorldPosition, c.Character.WorldPosition) > ChatMessage.SpeakRange) continue; + } + break; + case ChatMessageType.Radio: + if (message.Sender == null) return; + var radio = message.Sender.Inventory.Items.First(i => i != null && i.GetComponent() != null); + if (radio == null) message.Type = ChatMessageType.Default; + break; + } + + recipients.Add(c); + } + + AddChatMessage(message); + + foreach (Client c in recipients) + { + ReliableMessage msg = c.ReliableChannel.CreateMessage(); + msg.InnerMessage.Write((byte)PacketTypes.Chatmessage); + //msg.InnerMessage.Write((byte)type); + //msg.InnerMessage.Write(message); + + message.WriteNetworkMessage(msg.InnerMessage); + + c.ReliableChannel.SendMessage(msg, c.Connection); + } + } + + public override void SendChatMessage(string message) { List recipients = new List(); Client targetClient = null; - if (type == ChatMessageType.Server) + ChatMessageType type = gameStarted && myCharacter != null ? ChatMessageType.Default : ChatMessageType.Server; + + string command = ChatMessage.GetChatMessageCommand(message, out message).ToLower(); + + if (command=="dead" || command=="d") { - string command = GetChatMessageCommand(message).ToLower(); + type = ChatMessageType.Dead; + } + else if (command=="radio" || command=="r") + { + if (CanUseRadio(Character.Controlled)) type = ChatMessageType.Radio; + } + else if (command != "") + { + targetClient = ConnectedClients.Find(c => + command == c.name.ToLower() || + c.Character != null && command == c.Character.Name.ToLower()); - if (command=="dead" || command=="d") + if (targetClient == null) { - type = ChatMessageType.Dead; - } - else if (command != "") - { - targetClient = ConnectedClients.Find(c => - command == c.name.ToLower() || - c.Character != null && command == c.Character.Name.ToLower()); - - if (targetClient == null) - { - AddChatMessage("Player ''" + command + "'' not found!", ChatMessageType.Admin); - return; - } + AddChatMessage("Player ''" + command + "'' not found!", ChatMessageType.Error); + return; } } @@ -1411,8 +1460,12 @@ namespace Barotrauma.Networking if (type != ChatMessageType.Dead || (c.Character == null || c.Character.IsDead)) recipients.Add(c); } } - - AddChatMessage(message, type); + + var chatMessage = ChatMessage.Create( + gameStarted && myCharacter != null ? myCharacter.Name : name, + message, type, gameStarted ? myCharacter : null); + + AddChatMessage(chatMessage); if (!server.Connections.Any()) return; @@ -1420,8 +1473,10 @@ namespace Barotrauma.Networking { ReliableMessage msg = c.ReliableChannel.CreateMessage(); msg.InnerMessage.Write((byte)PacketTypes.Chatmessage); - msg.InnerMessage.Write((byte)type); - msg.InnerMessage.Write(message); + //msg.InnerMessage.Write((byte)type); + //msg.InnerMessage.Write(message); + + chatMessage.WriteNetworkMessage(msg.InnerMessage); c.ReliableChannel.SendMessage(msg, c.Connection); } diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index 950385fb7..307f70a56 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -1,9 +1,11 @@ using System; +using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using System.Collections.Generic; using Lidgren.Network; +using Barotrauma.Items.Components; namespace Barotrauma.Networking { @@ -40,11 +42,6 @@ namespace Barotrauma.Networking SpectateRequest } - enum ChatMessageType - { - Default, Admin, Dead, Server - } - enum VoteType { Unknown, @@ -55,9 +52,6 @@ namespace Barotrauma.Networking class NetworkMember { - - protected static Color[] messageColor = { Color.White, Color.Red, new Color(63,72,204), Color.LightGreen }; - protected NetPeer netPeer; protected string name; @@ -139,6 +133,7 @@ namespace Barotrauma.Networking chatMsgBox.Font = GUI.SmallFont; chatMsgBox.Padding = Vector4.Zero; chatMsgBox.OnEnterPressed = EnterChatMessage; + chatMsgBox.OnTextChanged = TypingChatMessage; Voting = new Voting(); } @@ -183,39 +178,98 @@ namespace Barotrauma.Networking return message; } + public bool TypingChatMessage(GUITextBox textBox, string text) + { + string tempStr; + string command = ChatMessage.GetChatMessageCommand(text, out tempStr); + switch (command) + { + case "r": + case "radio": + textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Radio]; + break; + case "d": + case "dead": + textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Dead]; + break; + default: + textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default]; + break; + } + + return true; + } + + public bool CanUseRadio(Character sender) + { + if (sender == null) return false; + + var radio = sender.Inventory.Items.First(i => i != null && i.GetComponent() != null); + if (radio == null) return false; + + var radioComponent = radio.GetComponent(); + return radioComponent.HasRequiredContainedItems(false); + } + public bool EnterChatMessage(GUITextBox textBox, string message) { + textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default]; + if (string.IsNullOrWhiteSpace(message)) return false; - string senderName = gameStarted && characterInfo != null ? characterInfo.Name : name; - - SendChatMessage(senderName + ": " + message); + SendChatMessage(message); textBox.Deselect(); return true; } - - public void AddChatMessage(string message, ChatMessageType messageType) - { - GameMain.NetLobbyScreen.NewChatMessage(message, messageColor[(int)messageType]); - GameServer.Log(message, messageColor[(int)messageType]); + public void AddChatMessage(string message, ChatMessageType type, string senderName="", Character senderCharacter = null) + { + AddChatMessage(ChatMessage.Create(senderName, message, type, senderCharacter)); + } + + public void AddChatMessage(ChatMessage message) + { + if (message.Type == ChatMessageType.Radio && message.Sender != null && message.Sender != myCharacter) + { + var radio = message.Sender.Inventory.Items.First(i => i != null && i.GetComponent() != null); + if (radio == null) return; + + var radioComponent = radio.GetComponent(); + radioComponent.Transmit(message.TextWithSender); + return; + } + + string displayedText = message.Text; + + if (message.Type == ChatMessageType.Default && myCharacter != null && message.Sender != null) + { + displayedText = message.ApplyDistanceEffect(myCharacter); + if (string.IsNullOrWhiteSpace(displayedText)) return; + } + + GameMain.NetLobbyScreen.NewChatMessage(message); + + GameServer.Log(message.Text, message.Color); while (chatBox.CountChildren > 20) { chatBox.RemoveChild(chatBox.children[1]); } - GUITextBlock msg = new GUITextBlock(new Rectangle(0, 0, 0, 20), message, - ((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f, messageColor[(int)messageType], + if (!string.IsNullOrWhiteSpace(message.SenderName)) + { + displayedText = message.SenderName + ": " + displayedText; + } + + GUITextBlock msg = new GUITextBlock(new Rectangle(0, 0, 0, 20), displayedText, + ((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f, message.Color, Alignment.Left, null, null, true); msg.Font = GUI.SmallFont; msg.Padding = new Vector4(20.0f, 0, 0, 0); - //float prevScroll = chatBox.BarScroll; - float prevSize = chatBox.BarSize; msg.Padding = new Vector4(20, 0, 0, 0); @@ -223,29 +277,21 @@ namespace Barotrauma.Networking if ((prevSize == 1.0f && chatBox.BarScroll == 0.0f) || (prevSize < 1.0f && chatBox.BarScroll == 1.0f)) chatBox.BarScroll = 1.0f; - GUI.PlayUISound(GUISoundType.Message); - } - - public virtual void SendChatMessage(string message, ChatMessageType type = ChatMessageType.Server) { } - - protected string GetChatMessageCommand(string message) - { - int separatorIndex = message.IndexOf(";"); - if (separatorIndex == -1) return ""; - - int colonIndex = message.IndexOf(":"); - - string command = ""; - try + GUISoundType soundType = GUISoundType.Message; + if (message.Type == ChatMessageType.Radio) { - command = message.Substring(colonIndex + 2, separatorIndex - colonIndex - 2); + soundType = GUISoundType.RadioMessage; + } + else if (message.Type == ChatMessageType.Dead) + { + soundType = GUISoundType.DeadMessage; } - catch { } - - return command; + GUI.PlayUISound(soundType); } + public virtual void SendChatMessage(string message) { } + public virtual void Update(float deltaTime) { if (gameStarted && Screen.Selected == GameMain.GameScreen) diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index f8d0e42dc..187f6d2b2 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -157,7 +157,6 @@ namespace Barotrauma chatBox = new GUIListBox(new Rectangle(0,0,0,chatFrame.Rect.Height-80), Color.White, GUI.Style, chatFrame); textBox = new GUITextBox(new Rectangle(0, 25, 0, 25), Alignment.Bottom, GUI.Style, chatFrame); textBox.Font = GUI.SmallFont; - textBox.OnEnterPressed = EnterChatMessage; //player info panel ------------------------------------------------------------ @@ -292,6 +291,10 @@ namespace Barotrauma textBox.Select(); + + textBox.OnEnterPressed = GameMain.NetworkMember.EnterChatMessage; + textBox.OnTextChanged = GameMain.NetworkMember.TypingChatMessage; + Character.Controlled = null; //GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; @@ -753,7 +756,7 @@ namespace Barotrauma spriteBatch.End(); } - public void NewChatMessage(string message, Color color) + public void NewChatMessage(ChatMessage message) { float prevSize = chatBox.BarSize; @@ -763,8 +766,8 @@ namespace Barotrauma } GUITextBlock msg = new GUITextBlock(new Rectangle(0, 0, 0, 20), - message, - ((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black*0.1f, color, + message.TextWithSender, + ((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black*0.1f, message.Color, Alignment.Left, GUI.Style, null, true); msg.Font = GUI.SmallFont; msg.CanBeFocused = false; @@ -775,14 +778,14 @@ namespace Barotrauma if ((prevSize == 1.0f && chatBox.BarScroll == 0.0f) || (prevSize < 1.0f && chatBox.BarScroll == 1.0f)) chatBox.BarScroll = 1.0f; } - public bool EnterChatMessage(GUITextBox textBox, string message) - { - if (String.IsNullOrEmpty(message)) return false; + //public bool EnterChatMessage(GUITextBox textBox, string message) + //{ + // if (String.IsNullOrEmpty(message)) return false; - GameMain.NetworkMember.SendChatMessage(GameMain.NetworkMember.Name + ": " + message); + // GameMain.NetworkMember.SendChatMessage(ChatMessage.Create(message, ChatMessageType.Default, null)); - return true; - } + // return true; + //} private void UpdatePreviewPlayer(CharacterInfo characterInfo) {