Merge branch 'master' into moStuff

# Conflicts:
#	Barotrauma/BarotraumaShared/Source/Characters/Character.cs
This commit is contained in:
Alex Noir
2017-12-21 23:17:56 +03:00
46 changed files with 1801 additions and 494 deletions
@@ -14,6 +14,8 @@ namespace Barotrauma
private static GUIButton cprButton; private static GUIButton cprButton;
private static GUIButton grabHoldButton;
private static GUIButton suicideButton; private static GUIButton suicideButton;
private static GUIProgressBar oxygenBar, healthBar; private static GUIProgressBar oxygenBar, healthBar;
@@ -41,6 +43,8 @@ namespace Barotrauma
if (cprButton != null && cprButton.Visible) cprButton.AddToGUIUpdateList(); if (cprButton != null && cprButton.Visible) cprButton.AddToGUIUpdateList();
if (grabHoldButton != null && cprButton.Visible) grabHoldButton.AddToGUIUpdateList();
if (suicideButton != null && suicideButton.Visible) suicideButton.AddToGUIUpdateList(); if (suicideButton != null && suicideButton.Visible) suicideButton.AddToGUIUpdateList();
if (!character.IsUnconscious && character.Stun <= 0.0f) if (!character.IsUnconscious && character.Stun <= 0.0f)
@@ -89,6 +93,8 @@ namespace Barotrauma
if (cprButton != null && cprButton.Visible) cprButton.Update(deltaTime); if (cprButton != null && cprButton.Visible) cprButton.Update(deltaTime);
if (grabHoldButton != null && grabHoldButton.Visible) grabHoldButton.Update(deltaTime);
if (suicideButton != null && suicideButton.Visible) suicideButton.Update(deltaTime); if (suicideButton != null && suicideButton.Visible) suicideButton.Update(deltaTime);
if (damageOverlayTimer > 0.0f) damageOverlayTimer -= deltaTime; if (damageOverlayTimer > 0.0f) damageOverlayTimer -= deltaTime;
@@ -195,9 +201,37 @@ namespace Barotrauma
}; };
} }
if (grabHoldButton == null)
{
grabHoldButton = new GUIButton(
new Rectangle(character.SelectedCharacter.Inventory.SlotPositions[0].ToPoint() + new Point(320, -60), new Point(130, 20)),
"Grabbing: " + (character.AnimController.GrabLimb == LimbType.Torso ? "Torso" : "Hands"), "");
grabHoldButton.OnClicked = (button, userData) =>
{
if (Character.Controlled == null || Character.Controlled.SelectedCharacter == null) return false;
Character.Controlled.AnimController.GrabLimb = Character.Controlled.AnimController.GrabLimb == LimbType.None ? LimbType.Torso : LimbType.None;
foreach (Limb limb in Character.Controlled.SelectedCharacter.AnimController.Limbs)
{
limb.pullJoint.Enabled = false;
}
if (GameMain.Client != null)
{
GameMain.Client.CreateEntityEvent(Character.Controlled, new object[] { NetEntityEvent.Type.Control });
}
grabHoldButton.Text = "Grabbing: " + (Character.Controlled.AnimController.GrabLimb == LimbType.Torso ? "Torso" : "Hands");
return true;
};
}
//cprButton.Visible = character.GetSkillLevel("Medical") > 20.0f; //cprButton.Visible = character.GetSkillLevel("Medical") > 20.0f;
if (cprButton.Visible) cprButton.Draw(spriteBatch); if (cprButton.Visible) cprButton.Draw(spriteBatch);
if (grabHoldButton.Visible) grabHoldButton.Draw(spriteBatch);
} }
if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected) if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
@@ -17,15 +17,19 @@ namespace Barotrauma
switch ((NetEntityEvent.Type)extraData[0]) switch ((NetEntityEvent.Type)extraData[0])
{ {
case NetEntityEvent.Type.InventoryState: case NetEntityEvent.Type.InventoryState:
msg.WriteRangedInteger(0, 2, 0); msg.WriteRangedInteger(0, 3, 0);
inventory.ClientWrite(msg, extraData); inventory.ClientWrite(msg, extraData);
break; break;
case NetEntityEvent.Type.Repair: case NetEntityEvent.Type.Repair:
msg.WriteRangedInteger(0, 2, 1); msg.WriteRangedInteger(0, 3, 1);
msg.Write(AnimController.Anim == AnimController.Animation.CPR); msg.Write(AnimController.Anim == AnimController.Animation.CPR);
break; break;
case NetEntityEvent.Type.Status: case NetEntityEvent.Type.Status:
msg.WriteRangedInteger(0, 2, 2); msg.WriteRangedInteger(0, 3, 2);
break;
case NetEntityEvent.Type.Control:
msg.WriteRangedInteger(0, 3, 3);
msg.Write((UInt16)AnimController.GrabLimb);
break; break;
} }
} }
@@ -91,6 +95,7 @@ namespace Barotrauma
bool crouching = msg.ReadBoolean(); bool crouching = msg.ReadBoolean();
keys[(int)InputType.Crouch].Held = crouching; keys[(int)InputType.Crouch].Held = crouching;
keys[(int)InputType.Crouch].SetState(false, crouching); keys[(int)InputType.Crouch].SetState(false, crouching);
AnimController.GrabLimb = (LimbType)msg.ReadUInt16();
} }
bool hasAttackLimb = msg.ReadBoolean(); bool hasAttackLimb = msg.ReadBoolean();
@@ -367,6 +372,9 @@ namespace Barotrauma
SetStun(0.0f, true, true); SetStun(0.0f, true, true);
} }
bool ragdolled = msg.ReadBoolean();
IsRagdolled = ragdolled;
bool huskInfected = msg.ReadBoolean(); bool huskInfected = msg.ReadBoolean();
if (huskInfected) if (huskInfected)
{ {
@@ -15,6 +15,8 @@ namespace Barotrauma
private static Queue<ColoredText> queuedMessages = new Queue<ColoredText>(); private static Queue<ColoredText> queuedMessages = new Queue<ColoredText>();
private static GUITextBlock activeQuestionText;
public static bool IsOpen public static bool IsOpen
{ {
get get
@@ -69,6 +71,13 @@ namespace Barotrauma
} }
} }
if (activeQuestionText != null &&
(listBox.children.Count == 0 || listBox.children[listBox.children.Count - 1] != activeQuestionText))
{
listBox.children.Remove(activeQuestionText);
listBox.children.Add(activeQuestionText);
}
if (PlayerInput.KeyHit(Keys.F3)) if (PlayerInput.KeyHit(Keys.F3))
{ {
isOpen = !isOpen; isOpen = !isOpen;
@@ -105,7 +114,7 @@ namespace Barotrauma
if (PlayerInput.KeyHit(Keys.Enter)) if (PlayerInput.KeyHit(Keys.Enter))
{ {
ExecuteCommand(textBox.Text, game); ExecuteCommand(textBox.Text);
textBox.Text = ""; textBox.Text = "";
} }
} }
@@ -134,7 +143,7 @@ namespace Barotrauma
case "entitylist": case "entitylist":
return true; return true;
default: default:
return false; return client.HasConsoleCommandPermission(command);
} }
} }
@@ -177,13 +186,6 @@ namespace Barotrauma
} }
selectedIndex = Messages.Count; selectedIndex = Messages.Count;
if (activeQuestionText != null)
{
//make sure the active question stays at the bottom of the list
listBox.children.Remove(activeQuestionText);
listBox.children.Add(activeQuestionText);
}
} }
private static void InitProjectSpecific() private static void InitProjectSpecific()
@@ -115,10 +115,11 @@ namespace Barotrauma
listBox.AddChild(child); listBox.AddChild(child);
} }
public void AddItem(string text, object userData = null) public void AddItem(string text, object userData = null, string toolTip = "")
{ {
GUITextBlock textBlock = new GUITextBlock(new Rectangle(0,0,0,20), text, "ListBoxElement", Alignment.TopLeft, Alignment.CenterLeft, listBox); GUITextBlock textBlock = new GUITextBlock(new Rectangle(0,0,0,20), text, "ListBoxElement", Alignment.TopLeft, Alignment.CenterLeft, listBox);
textBlock.UserData = userData; textBlock.UserData = userData;
textBlock.ToolTip = toolTip;
} }
public override void ClearChildren() public override void ClearChildren()
@@ -171,12 +171,12 @@ namespace Barotrauma
{ {
for (int i = 0; i < children.Count; i++) for (int i = 0; i < children.Count; i++)
{ {
if (!children[i].UserData.Equals(userData)) continue; if ((children[i].UserData != null && children[i].UserData.Equals(userData)) ||
(children[i].UserData == null && userData == null))
Select(i, force); {
Select(i, force);
//if (OnSelected != null) OnSelected(Selected, Selected.UserData); if (!SelectMultiple) return;
if (!SelectMultiple) return; }
} }
} }
@@ -7,12 +7,8 @@ namespace Barotrauma
{ {
public static List<GUIComponent> MessageBoxes = new List<GUIComponent>(); public static List<GUIComponent> MessageBoxes = new List<GUIComponent>();
const int DefaultWidth=400, DefaultHeight=250; public const int DefaultWidth = 400, DefaultHeight = 250;
//public delegate bool OnClickedHandler(GUIButton button, object obj);
//public OnClickedHandler OnClicked;
//GUIFrame frame;
public GUIButton[] Buttons; public GUIButton[] Buttons;
public static GUIComponent VisibleBox public static GUIComponent VisibleBox
@@ -50,8 +50,8 @@ namespace Barotrauma
{ {
base.Rect = value; base.Rect = value;
box.Rect = new Rectangle(value.X,value.Y,box.Rect.Width,box.Rect.Height); if (box != null) box.Rect = new Rectangle(value.X,value.Y,box.Rect.Width,box.Rect.Height);
text.Rect = new Rectangle(box.Rect.Right, box.Rect.Y + 2, 20, box.Rect.Height); if (text != null) text.Rect = new Rectangle(box.Rect.Right, box.Rect.Y + 2, 20, box.Rect.Height);
} }
} }
@@ -1,9 +1,5 @@
using Lidgren.Network; using Lidgren.Network;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
@@ -40,6 +36,10 @@ namespace Barotrauma.Networking
{ {
new GUIMessageBox("", txt); new GUIMessageBox("", txt);
} }
else if (type == ChatMessageType.Console)
{
DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
}
else else
{ {
GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter); GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter);
@@ -19,6 +19,7 @@ namespace Barotrauma.Networking
private GUITickBox endVoteTickBox; private GUITickBox endVoteTickBox;
private ClientPermissions permissions = ClientPermissions.None; private ClientPermissions permissions = ClientPermissions.None;
private List<string> permittedConsoleCommands = new List<string>();
private bool connected; private bool connected;
@@ -598,16 +599,28 @@ namespace Barotrauma.Networking
private void ReadPermissions(NetIncomingMessage inc) private void ReadPermissions(NetIncomingMessage inc)
{ {
List<string> permittedConsoleCommands = new List<string>();
ClientPermissions newPermissions = (ClientPermissions)inc.ReadByte(); ClientPermissions newPermissions = (ClientPermissions)inc.ReadByte();
if (newPermissions != permissions) if (newPermissions.HasFlag(ClientPermissions.ConsoleCommands))
{ {
SetPermissions(newPermissions); UInt16 consoleCommandCount = inc.ReadUInt16();
for (int i = 0; i < consoleCommandCount; i++)
{
permittedConsoleCommands.Add(inc.ReadString());
}
} }
SetPermissions(newPermissions, permittedConsoleCommands);
} }
private void SetPermissions(ClientPermissions newPermissions) private void SetPermissions(ClientPermissions newPermissions, List<string> permittedConsoleCommands)
{ {
if (newPermissions == permissions) return; if (!(this.permittedConsoleCommands.Any(c => !permittedConsoleCommands.Contains(c)) ||
permittedConsoleCommands.Any(c => !this.permittedConsoleCommands.Contains(c))))
{
if (newPermissions == permissions) return;
}
GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData as string == "permissions"); GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData as string == "permissions");
string msg = ""; string msg = "";
@@ -626,8 +639,22 @@ namespace Barotrauma.Networking
msg += " - " + attributes[0].Description + "\n"; msg += " - " + attributes[0].Description + "\n";
} }
} }
permissions = newPermissions; permissions = newPermissions;
new GUIMessageBox("Permissions changed", msg).UserData = "permissions"; this.permittedConsoleCommands = new List<string>(permittedConsoleCommands);
GUIMessageBox msgBox = new GUIMessageBox("Permissions changed", msg, GUIMessageBox.DefaultWidth, 0);
msgBox.UserData = "permissions";
if (newPermissions.HasFlag(ClientPermissions.ConsoleCommands))
{
int listBoxWidth = (int)(msgBox.InnerFrame.Rect.Width - msgBox.InnerFrame.Padding.X - msgBox.InnerFrame.Padding.Z) / 2 - 30;
new GUITextBlock(new Rectangle(0, 0, listBoxWidth, 15), "Permitted console commands:", "", Alignment.TopRight, Alignment.TopLeft, msgBox.InnerFrame, true, GUI.SmallFont);
var commandList = new GUIListBox(new Rectangle(0, 20, listBoxWidth, 0), "", Alignment.BottomRight, msgBox.InnerFrame);
foreach (string permittedCommand in permittedConsoleCommands)
{
new GUITextBlock(new Rectangle(0, 0, 0, 15), permittedCommand, "", commandList, GUI.SmallFont).CanBeFocused = false;
}
}
GameMain.NetLobbyScreen.SubList.Enabled = Voting.AllowSubVoting || HasPermission(ClientPermissions.SelectSub); GameMain.NetLobbyScreen.SubList.Enabled = Voting.AllowSubVoting || HasPermission(ClientPermissions.SelectSub);
GameMain.NetLobbyScreen.ModeList.Enabled = Voting.AllowModeVoting || HasPermission(ClientPermissions.SelectMode); GameMain.NetLobbyScreen.ModeList.Enabled = Voting.AllowModeVoting || HasPermission(ClientPermissions.SelectMode);
@@ -805,7 +832,7 @@ namespace Barotrauma.Networking
gameStarted = inc.ReadBoolean(); gameStarted = inc.ReadBoolean();
bool allowSpectating = inc.ReadBoolean(); bool allowSpectating = inc.ReadBoolean();
SetPermissions((ClientPermissions)inc.ReadByte()); ReadPermissions(inc);
if (gameStarted) if (gameStarted)
{ {
@@ -1169,6 +1196,14 @@ namespace Barotrauma.Networking
return permissions.HasFlag(permission); return permissions.HasFlag(permission);
} }
public bool HasConsoleCommandPermission(string command)
{
if (!permissions.HasFlag(ClientPermissions.ConsoleCommands)) return false;
command = command.ToLowerInvariant();
return permittedConsoleCommands.Any(c => c.ToLowerInvariant() == command);
}
public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
{ {
base.Draw(spriteBatch); base.Draw(spriteBatch);
@@ -1359,6 +1394,25 @@ namespace Barotrauma.Networking
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
} }
public void SendConsoleCommand(string command)
{
if (string.IsNullOrWhiteSpace(command))
{
DebugConsole.ThrowError("Cannot send an empty console command to the server!\n" + Environment.StackTrace);
return;
}
NetOutgoingMessage msg = client.CreateMessage();
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
msg.Write((byte)ClientPermissions.ConsoleCommands);
msg.Write(command);
Vector2 cursorWorldPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
msg.Write(cursorWorldPos.X);
msg.Write(cursorWorldPos.Y);
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
}
/// <summary> /// <summary>
/// Tell the server to select a submarine (permission required) /// Tell the server to select a submarine (permission required)
/// </summary> /// </summary>
@@ -320,7 +320,7 @@ namespace Barotrauma.Networking
return true; return true;
}; };
y += 40; y += 20;
var voteKickBox = new GUITickBox(new Rectangle(0, y, 20, 20), "Allow vote kicking", Alignment.Left, settingsTabs[1]); var voteKickBox = new GUITickBox(new Rectangle(0, y, 20, 20), "Allow vote kicking", Alignment.Left, settingsTabs[1]);
voteKickBox.Selected = Voting.AllowVoteKick; voteKickBox.Selected = Voting.AllowVoteKick;
@@ -357,7 +357,7 @@ namespace Barotrauma.Networking
return true; return true;
}; };
y += 40; y += 20;
var randomizeLevelBox = new GUITickBox(new Rectangle(0, y, 20, 20), "Randomize level seed between rounds", Alignment.Left, settingsTabs[1]); var randomizeLevelBox = new GUITickBox(new Rectangle(0, y, 20, 20), "Randomize level seed between rounds", Alignment.Left, settingsTabs[1]);
randomizeLevelBox.Selected = RandomizeSeed; randomizeLevelBox.Selected = RandomizeSeed;
@@ -367,7 +367,7 @@ namespace Barotrauma.Networking
return true; return true;
}; };
y += 40; y += 20;
var saveLogsBox = new GUITickBox(new Rectangle(0, y, 20, 20), "Save server logs", Alignment.Left, settingsTabs[1]); var saveLogsBox = new GUITickBox(new Rectangle(0, y, 20, 20), "Save server logs", Alignment.Left, settingsTabs[1]);
saveLogsBox.Selected = SaveServerLogs; saveLogsBox.Selected = SaveServerLogs;
@@ -378,6 +378,83 @@ namespace Barotrauma.Networking
return true; return true;
}; };
y += 20;
var ragdollButtonBox = new GUITickBox(new Rectangle(0, y, 20, 20), "Allow ragdoll button", Alignment.Left, settingsTabs[1]);
ragdollButtonBox.Selected = AllowRagdollButton;
ragdollButtonBox.OnSelected = (GUITickBox) =>
{
AllowRagdollButton = GUITickBox.Selected;
return true;
};
y += 20;
var traitorRatioBox = new GUITickBox(new Rectangle(0, y, 20, 20), "Use % of players for max traitors", Alignment.Left, settingsTabs[1]);
var traitorRatioText = new GUITextBlock(new Rectangle(20, y + 20, 20, 20), "Traitor ratio: 20 %", "", settingsTabs[1], GUI.SmallFont);
var traitorRatioSlider = new GUIScrollBar(new Rectangle(150, y + 22, 100, 15), "", 0.1f, settingsTabs[1]);
//Prepare the slider before the tick box
if (TraitorUseRatio)
{
traitorRatioSlider.UserData = traitorRatioText;
traitorRatioSlider.Step = 0.01f; //Lots of fine-tuning
traitorRatioSlider.BarScroll = (TraitorRatio - 0.1f) / 0.9f;
}
else
{
traitorRatioSlider.UserData = traitorRatioText;
traitorRatioSlider.Step = 1f / (maxPlayers-1);
traitorRatioSlider.BarScroll = MathUtils.Round(TraitorRatio, 1f);
}
//Slider END
traitorRatioBox.Selected = TraitorUseRatio;
traitorRatioBox.OnSelected = (GUITickBox) =>
{
TraitorUseRatio = GUITickBox.Selected;
//Affect the slider graphics
if (TraitorUseRatio)
{
traitorRatioSlider.UserData = traitorRatioText;
traitorRatioSlider.Step = 0.01f; //Lots of fine-tuning
traitorRatioSlider.BarScroll = 0.2f; //default values
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll); //Update the scroll bar
}
else
{
traitorRatioSlider.UserData = traitorRatioText;
traitorRatioSlider.Step = 1f / (maxPlayers-1);
traitorRatioSlider.BarScroll = 1; //default values
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll); //Update the scroll bar
}
return true;
};
traitorRatioSlider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
GUITextBlock traitorText = scrollBar.UserData as GUITextBlock;
if (TraitorUseRatio)
{
TraitorRatio = barScroll * 0.9f + 0.1f;
traitorText.Text = "Traitor ratio: " + (int)MathUtils.Round(TraitorRatio * 100.0f, 1.0f) + " %";
}
else
{
TraitorRatio = MathUtils.Round(barScroll * (maxPlayers-1), 1f) + 1;
traitorText.Text = "Traitor count: " + TraitorRatio;
}
return true;
};
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
y += 45;
var karmaButtonBox = new GUITickBox(new Rectangle(0, y, 20, 20), "Use Karma", Alignment.Left, settingsTabs[1]);
karmaButtonBox.Selected = KarmaEnabled;
karmaButtonBox.OnSelected = (GUITickBox) =>
{
KarmaEnabled = GUITickBox.Selected;
return true;
};
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// banlist // banlist
@@ -882,24 +882,54 @@ namespace Barotrauma
playerFrame = new GUIFrame(new Rectangle(0, 0, 0, 0), Color.Black * 0.6f); playerFrame = new GUIFrame(new Rectangle(0, 0, 0, 0), Color.Black * 0.6f);
var playerFrameInner = new GUIFrame(new Rectangle(0, 0, 300, 280), null, Alignment.Center, "", playerFrame); var playerFrameInner = new GUIFrame(GameMain.Server != null ? new Rectangle(0, 0, 450, 370) : new Rectangle(0, 0, 450, 150), null, Alignment.Center, "", playerFrame);
playerFrameInner.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f); playerFrameInner.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f);
new GUITextBlock(new Rectangle(0, 0, 200, 20), component.UserData.ToString(), new GUITextBlock(new Rectangle(0, 0, 200, 20), obj.ToString(),
"", Alignment.TopLeft, Alignment.TopLeft, "", Alignment.TopLeft, Alignment.TopLeft,
playerFrameInner, false, GUI.LargeFont); playerFrameInner, false, GUI.LargeFont);
if (GameMain.Server != null) if (GameMain.Server != null)
{ {
var selectedClient = GameMain.Server.ConnectedClients.Find(c => c.Name == component.UserData.ToString()); var selectedClient = GameMain.Server.ConnectedClients.Find(c => c.Name == obj.ToString());
playerFrame.UserData = selectedClient;
new GUITextBlock(new Rectangle(0, 25, 150, 15), selectedClient.Connection.RemoteEndPoint.Address.ToString(), "", playerFrameInner); new GUITextBlock(new Rectangle(0, 25, 150, 15), selectedClient.Connection.RemoteEndPoint.Address.ToString(), "", playerFrameInner);
var permissionsBox = new GUIFrame(new Rectangle(0, 60, 0, 90), null, playerFrameInner); new GUITextBlock(new Rectangle(0, 45, 0, 15), "Rank", "", playerFrameInner);
var rankDropDown = new GUIDropDown(new Rectangle(0, 70, 150, 20), "Rank", "", playerFrameInner);
rankDropDown.UserData = selectedClient;
foreach (PermissionPreset permissionPreset in PermissionPreset.List)
{
rankDropDown.AddItem(permissionPreset.Name, permissionPreset, permissionPreset.Description);
}
rankDropDown.AddItem("Custom", null);
PermissionPreset currentPreset = PermissionPreset.List.Find(p =>
p.Permissions == selectedClient.Permissions &&
p.PermittedCommands.Count == selectedClient.PermittedConsoleCommands.Count && !p.PermittedCommands.Except(selectedClient.PermittedConsoleCommands).Any());
rankDropDown.SelectItem(currentPreset);
rankDropDown.OnSelected += (c, userdata) =>
{
PermissionPreset selectedPreset = (PermissionPreset)userdata;
if (selectedPreset != null)
{
var client = playerFrame.UserData as Client;
client.SetPermissions(selectedPreset.Permissions, selectedPreset.PermittedCommands);
GameMain.Server.UpdateClientPermissions(client);
playerFrame = null;
SelectPlayer(null, client.Name);
}
return true;
};
var permissionsBox = new GUIFrame(new Rectangle(0, 125, (int)(playerFrameInner.Rect.Width * 0.5f), 160), null, playerFrameInner);
permissionsBox.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); permissionsBox.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f);
permissionsBox.UserData = selectedClient; permissionsBox.UserData = selectedClient;
new GUITextBlock(new Rectangle(0, 0, 0, 15), "Permissions:", "", permissionsBox); new GUITextBlock(new Rectangle(0, 100, permissionsBox.Rect.Width, 15), "Permissions:", "", playerFrameInner);
int x = 0, y = 0; int x = 0, y = 0;
foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions)))
{ {
@@ -910,13 +940,16 @@ namespace Barotrauma
string permissionStr = attributes.Length > 0 ? attributes[0].Description : permission.ToString(); string permissionStr = attributes.Length > 0 ? attributes[0].Description : permission.ToString();
var permissionTick = new GUITickBox(new Rectangle(x, y + 25, 15, 15), permissionStr, Alignment.TopLeft, GUI.SmallFont, permissionsBox); var permissionTick = new GUITickBox(new Rectangle(x, y, 15, 15), permissionStr, Alignment.TopLeft, GUI.SmallFont, permissionsBox);
permissionTick.UserData = permission; permissionTick.UserData = permission;
permissionTick.Selected = selectedClient.HasPermission(permission); permissionTick.Selected = selectedClient.HasPermission(permission);
permissionTick.OnSelected = (tickBox) => permissionTick.OnSelected = (tickBox) =>
{ {
var client = tickBox.Parent.UserData as Client; //reset rank to custom
rankDropDown.SelectItem(null);
var client = playerFrame.UserData as Client;
if (client == null) return false; if (client == null) return false;
var thisPermission = (ClientPermissions)tickBox.UserData; var thisPermission = (ClientPermissions)tickBox.UserData;
@@ -931,19 +964,50 @@ namespace Barotrauma
return true; return true;
}; };
y += 20; y += 20;
if (y >= permissionsBox.Rect.Height - 40) if (y >= permissionsBox.Rect.Height - 15)
{ {
y = 0; y = 0;
x += 100; x += 120;
} }
} }
new GUITextBlock(new Rectangle(0, 100, (int)(playerFrameInner.Rect.Width * 0.5f), 15), "Permitted console commands:", "", Alignment.TopRight, Alignment.TopLeft, playerFrameInner, true);
var commandList = new GUIListBox(new Rectangle(0, 125, (int)(playerFrameInner.Rect.Width * 0.5f), 160), "", Alignment.TopRight, playerFrameInner);
commandList.UserData = selectedClient;
foreach (DebugConsole.Command command in DebugConsole.Commands)
{
var commandTickBox = new GUITickBox(new Rectangle(0, 0, 15, 15), command.names[0], Alignment.TopLeft, GUI.SmallFont, commandList);
commandTickBox.Selected = selectedClient.PermittedConsoleCommands.Contains(command);
commandTickBox.ToolTip = command.help;
commandTickBox.UserData = command;
commandTickBox.OnSelected += (GUITickBox tickBox) =>
{
//reset rank to custom
rankDropDown.SelectItem(null);
Client client = playerFrame.UserData as Client;
DebugConsole.Command selectedCommand = tickBox.UserData as DebugConsole.Command;
if (client == null) return false;
if (!tickBox.Selected)
{
client.PermittedConsoleCommands.Remove(selectedCommand);
}
else if (!client.PermittedConsoleCommands.Contains(selectedCommand))
{
client.PermittedConsoleCommands.Add(selectedCommand);
}
GameMain.Server.UpdateClientPermissions(client);
return true;
};
}
} }
if (GameMain.Server != null || GameMain.Client.HasPermission(ClientPermissions.Kick)) if (GameMain.Server != null || GameMain.Client.HasPermission(ClientPermissions.Kick))
{ {
var kickButton = new GUIButton(new Rectangle(0, -50, 100, 20), "Kick", Alignment.BottomLeft, "", playerFrameInner); var kickButton = new GUIButton(new Rectangle(0, 0, 80, 20), "Kick", Alignment.BottomLeft, "", playerFrameInner);
kickButton.UserData = obj; kickButton.UserData = obj;
kickButton.OnClicked += KickPlayer; kickButton.OnClicked += KickPlayer;
kickButton.OnClicked += ClosePlayerFrame; kickButton.OnClicked += ClosePlayerFrame;
@@ -951,12 +1015,12 @@ namespace Barotrauma
if (GameMain.Server != null || GameMain.Client.HasPermission(ClientPermissions.Ban)) if (GameMain.Server != null || GameMain.Client.HasPermission(ClientPermissions.Ban))
{ {
var banButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Ban", Alignment.BottomLeft, "", playerFrameInner); var banButton = new GUIButton(new Rectangle(90, 0, 80, 20), "Ban", Alignment.BottomLeft, "", playerFrameInner);
banButton.UserData = obj; banButton.UserData = obj;
banButton.OnClicked += BanPlayer; banButton.OnClicked += BanPlayer;
banButton.OnClicked += ClosePlayerFrame; banButton.OnClicked += ClosePlayerFrame;
var rangebanButton = new GUIButton(new Rectangle(0, -25, 100, 20), "Ban range", Alignment.BottomLeft, "", playerFrameInner); var rangebanButton = new GUIButton(new Rectangle(180, 0, 80, 20), "Ban range", Alignment.BottomLeft, "", playerFrameInner);
rangebanButton.UserData = obj; rangebanButton.UserData = obj;
rangebanButton.OnClicked += BanPlayerRange; rangebanButton.OnClicked += BanPlayerRange;
rangebanButton.OnClicked += ClosePlayerFrame; rangebanButton.OnClicked += ClosePlayerFrame;
@@ -2,7 +2,6 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -16,7 +15,7 @@ namespace Barotrauma
{ {
while (QueuedCommands.Count>0) while (QueuedCommands.Count>0)
{ {
ExecuteCommand(QueuedCommands[0], GameMain.Instance); ExecuteCommand(QueuedCommands[0]);
QueuedCommands.RemoveAt(0); QueuedCommands.RemoveAt(0);
} }
} }
@@ -745,6 +745,9 @@
<Content Include="$(MSBuildThisFileDirectory)Data\ContentPackages\Vanilla 0.7.xml"> <Content Include="$(MSBuildThisFileDirectory)Data\ContentPackages\Vanilla 0.7.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Data\permissionpresets.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Icon.ico" /> <Content Include="$(MSBuildThisFileDirectory)Icon.ico" />
<Content Include="$(MSBuildThisFileDirectory)Mods\info.txt"> <Content Include="$(MSBuildThisFileDirectory)Mods\info.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -1509,6 +1512,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\BanList.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\BanList.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\ChatMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\ChatMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Client.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Client.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\ClientPermissions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\EntitySpawner.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\EntitySpawner.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\FileTransfer\FileSender.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\FileTransfer\FileSender.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\GameServer.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\GameServer.cs" />
@@ -14,13 +14,16 @@
<RequiredItems name="Battery Cell" type="Contained"/> <RequiredItems name="Battery Cell" type="Contained"/>
</WifiComponent> </WifiComponent>
<ItemContainer capacity="1" hideitems="true"> <ItemContainer capacity="1" hideitems="true">
<Containable name="Battery Cell"/> <Containable name="Battery Cell">
<Containable name="Fulgurium Battery Cell"/> <StatusEffect type="OnContaining" target="Contained" Condition="-0.05"/>
</ItemContainer> </Containable>
<Containable name="Fulgurium Battery Cell">
<StatusEffect type="OnContaining" target="Contained" Condition="-0.05"/>
</Containable>
</ItemContainer>
<Wearable limbtype="Head" slots="Any,Face"> <Wearable limbtype="Head" slots="Any,Face">
<StatusEffect type="OnWearing" target="Contained" Condition="-0.1"/>
<sprite texture="headset.png" limb="Head" origin="0.5,0.5"/> <sprite texture="headset.png" limb="Head" origin="0.5,0.5"/>
</Wearable> </Wearable>
</Item> </Item>
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8" ?>
<PermissionPresets>
<Preset
name="None"
description="No special privileges."
permissions="None"/>
<Preset
name="Moderator"
description="Allowed to manage round settings and kick players."
permissions="EndRound,Kick,SelectSub,SelectMode,ManageCampaign,ConsoleCommands">
<Command name="clientlist"/>
<Command name="autorestart"/>
<Command name="autorestartinterval"/>
<Command name="autorestarttimer"/>
<Command name="kick"/>
<Command name="kickid"/>
<Command name="campaigninfo"/>
<Command name="campaigndestination"/>
</Preset>
<Preset
name="Admin"
description="Allowed to ban and kick players, manage round settings and use nearly all console commands."
permissions="EndRound,Kick,Ban,SelectSub,SelectMode,ManageCampaign,ConsoleCommands">
<Command name="clientlist"/>
<Command name="autorestart"/>
<Command name="autorestartinterval"/>
<Command name="autorestarttimer"/>
<Command name="kick"/>
<Command name="kickid"/>
<Command name="campaigninfo"/>
<Command name="campaigndestination"/>
<Command name="spawn"/>
<Command name="spawnitem"/>
<Command name="disablecrewai"/>
<Command name="enablecrewai"/>
<Command name="ban"/>
<Command name="banid"/>
<Command name="banip"/>
<Command name="teleportcharacter"/>
<Command name="godmode"/>
<Command name="lockx"/>
<Command name="locky"/>
<Command name="heal"/>
<Command name="revive"/>
<Command name="freeze"/>
<Command name="freecam"/>
<Command name="explosion"/>
<Command name="fixitems"/>
<Command name="fixhulls"/>
<Command name="power"/>
<Command name="oxygen"/>
<Command name="setclientcharacter"/>
</Preset>
</PermissionPresets>
@@ -64,7 +64,7 @@ namespace Barotrauma
protected override Vector2 DoSteeringSeek(Vector2 target, float speed = 1) protected override Vector2 DoSteeringSeek(Vector2 target, float speed = 1)
{ {
//find a new path if one hasn't been found yet or the target is different from the current target //find a new path if one hasn't been found yet or the target is different from the current target
if (currentPath == null || Vector2.Distance(target, currentTarget)>1.0f || findPathTimer < -5.0f) if (currentPath == null || Vector2.Distance(target, currentTarget) > 1.0f || findPathTimer < -1.0f)
{ {
if (findPathTimer > 0.0f) return Vector2.Zero; if (findPathTimer > 0.0f) return Vector2.Zero;
@@ -73,7 +73,7 @@ namespace Barotrauma
if (character != null && character.Submarine == null) if (character != null && character.Submarine == null)
{ {
var targetHull = Hull.FindHull(FarseerPhysics.ConvertUnits.ToDisplayUnits(target), null, false); var targetHull = Hull.FindHull(FarseerPhysics.ConvertUnits.ToDisplayUnits(target), null, false);
if (targetHull!=null && targetHull.Submarine != null) if (targetHull != null && targetHull.Submarine != null)
{ {
pos -= targetHull.SimPosition; pos -= targetHull.SimPosition;
} }
@@ -81,7 +81,7 @@ namespace Barotrauma
currentPath = pathFinder.FindPath(pos, target); currentPath = pathFinder.FindPath(pos, target);
findPathTimer = Rand.Range(1.0f,1.2f); findPathTimer = Rand.Range(1.0f, 1.2f);
return DiffToCurrentNode(); return DiffToCurrentNode();
} }
@@ -104,7 +104,7 @@ namespace Barotrauma
private Vector2 DiffToCurrentNode() private Vector2 DiffToCurrentNode()
{ {
if (currentPath == null || currentPath.Finished || currentPath.Unreachable) return Vector2.Zero; if (currentPath == null || currentPath.Unreachable) return Vector2.Zero;
if (currentPath.Finished) if (currentPath.Finished)
{ {
@@ -114,7 +114,7 @@ namespace Barotrauma
//todo: take multiple subs into account //todo: take multiple subs into account
pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition; pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition;
} }
return currentTarget-pos2; return currentTarget - pos2;
} }
if (canOpenDoors && !character.LockHands) CheckDoorsInPath(); if (canOpenDoors && !character.LockHands) CheckDoorsInPath();
@@ -4,21 +4,13 @@ using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
class AIObjective abstract class AIObjective
{ {
protected List<AIObjective> subObjectives; protected readonly List<AIObjective> subObjectives;
protected float priority; protected float priority;
protected readonly Character character;
protected Character character;
protected string option; protected string option;
public virtual bool IsCompleted()
{
return false;
}
public virtual bool CanBeCompleted public virtual bool CanBeCompleted
{ {
get { return true; } get { return true; }
@@ -29,13 +21,10 @@ namespace Barotrauma
get { return option; } get { return option; }
} }
public AIObjective(Character character, string option) public AIObjective(Character character, string option)
{ {
subObjectives = new List<AIObjective>(); subObjectives = new List<AIObjective>();
this.character = character; this.character = character;
this.option = option; this.option = option;
#if DEBUG #if DEBUG
@@ -60,8 +49,6 @@ namespace Barotrauma
Act(deltaTime); Act(deltaTime);
} }
protected virtual void Act(float deltaTime) { }
public void AddSubObjective(AIObjective objective) public void AddSubObjective(AIObjective objective)
{ {
if (subObjectives.Any(o => o.IsDuplicate(objective))) return; if (subObjectives.Any(o => o.IsDuplicate(objective))) return;
@@ -69,19 +56,20 @@ namespace Barotrauma
subObjectives.Add(objective); subObjectives.Add(objective);
} }
public virtual float GetPriority(Character character) public AIObjective GetCurrentSubObjective()
{ {
return 0.0f; AIObjective currentSubObjective = this;
while (currentSubObjective.subObjectives.Count > 0)
{
currentSubObjective = subObjectives[0];
}
return currentSubObjective;
} }
public virtual bool IsDuplicate(AIObjective otherObjective) protected abstract void Act(float deltaTime);
{
#if DEBUG
throw new NotImplementedException();
#else
return (this.GetType() == otherObjective.GetType());
#endif
} public abstract bool IsCompleted();
public abstract float GetPriority(AIObjectiveManager objectiveManager);
public abstract bool IsDuplicate(AIObjective otherObjective);
} }
} }
@@ -33,7 +33,6 @@ namespace Barotrauma
} }
coolDownTimer = CoolDown; coolDownTimer = CoolDown;
} }
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
@@ -42,16 +41,23 @@ namespace Barotrauma
var weapon = character.Inventory.FindItem("weapon"); var weapon = character.Inventory.FindItem("weapon");
if (weapon==null) if (weapon == null)
{ {
Escape(deltaTime); Escape(deltaTime);
} }
else else
{ {
//TODO: make sure the weapon is ready to use (projectiles/batteries loaded)
if (!character.SelectedItems.Contains(weapon)) if (!character.SelectedItems.Contains(weapon))
{ {
character.Inventory.TryPutItem(weapon, 3, false, character); if (character.Inventory.TryPutItem(weapon, 3, false, character))
weapon.Equip(character); {
weapon.Equip(character);
}
else
{
return;
}
} }
character.CursorPosition = enemy.Position; character.CursorPosition = enemy.Position;
character.SetInput(InputType.Aim, false, true); character.SetInput(InputType.Aim, false, true);
@@ -99,8 +105,13 @@ namespace Barotrauma
return enemy.IsDead || coolDownTimer <= 0.0f; return enemy.IsDead || coolDownTimer <= 0.0f;
} }
public override float GetPriority(Character character) public override float GetPriority(AIObjectiveManager objectiveManager)
{ {
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
//clamp the strength to the health of this character //clamp the strength to the health of this character
//(it doesn't make a difference whether the enemy does 200 or 600 damage, it's one hit kill anyway) //(it doesn't make a difference whether the enemy does 200 or 600 damage, it's one hit kill anyway)
@@ -19,15 +19,6 @@ namespace Barotrauma
{ {
this.itemName = itemName; this.itemName = itemName;
this.container = container; this.container = container;
//check if the container has room for more items
//canBeCompleted = false;
//foreach (Item contained in container.inventory.Items)
//{
// if (contained != null) continue;
// canBeCompleted = true;
// break;
//}
} }
public override bool IsCompleted() public override bool IsCompleted()
@@ -35,6 +26,16 @@ namespace Barotrauma
return isCompleted || container.Inventory.FindItem(itemName)!=null; return isCompleted || container.Inventory.FindItem(itemName)!=null;
} }
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
return 1.0f;
}
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
if (isCompleted) return; if (isCompleted) return;
@@ -15,7 +15,7 @@ namespace Barotrauma
if (item == null) return false; if (item == null) return false;
var containedItems = item.ContainedItems; var containedItems = item.ContainedItems;
var oxygenTank = Array.Find(containedItems, i => i.Name == "Oxygen Tank" && i.Condition > 0.0f); var oxygenTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Oxygen Tank") && i.Condition > 0.0f);
return oxygenTank != null; return oxygenTank != null;
} }
@@ -42,7 +42,7 @@ namespace Barotrauma
if (containedItems == null) return; if (containedItems == null) return;
//check if there's an oxygen tank in the mask //check if there's an oxygen tank in the mask
var oxygenTank = Array.Find(containedItems, i => i.Name == "Oxygen Tank"); var oxygenTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Oxygen Tank"));
if (oxygenTank != null) if (oxygenTank != null)
{ {
@@ -66,15 +66,18 @@ namespace Barotrauma
if (subObjective != null) if (subObjective != null)
{ {
subObjective.TryComplete(deltaTime); subObjective.TryComplete(deltaTime);
//isCompleted = subObjective.IsCompleted();
} }
} }
public override float GetPriority(Character character) public override float GetPriority(AIObjectiveManager objectiveManager)
{ {
if (character.AnimController.CurrentHull == null) return 100.0f; if (character.AnimController.CurrentHull == null) return 100.0f;
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
return 100.0f - character.Oxygen; return 100.0f - character.Oxygen;
} }
@@ -1,4 +1,5 @@
using System; using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -27,40 +28,24 @@ namespace Barotrauma
unreachable = new List<Hull>(); unreachable = new List<Hull>();
} }
public override bool IsCompleted()
{
return false;
}
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
var currentHull = character.AnimController.CurrentHull; var currentHull = character.AnimController.CurrentHull;
currenthullSafety = OverrideCurrentHullSafety == null ? currenthullSafety = OverrideCurrentHullSafety == null ?
GetHullSafety(currentHull, character) : (float)OverrideCurrentHullSafety; GetHullSafety(currentHull, character) : (float)OverrideCurrentHullSafety;
if (currentHull != null) if (NeedsDivingGear())
{ {
if (NeedsDivingGear()) if (!FindDivingGear(deltaTime)) return;
{
if (!FindDivingGear(deltaTime)) return;
}
if (currenthullSafety > MinSafety)
{
if (Math.Abs(currentHull.WorldPosition.X - character.WorldPosition.X) > 100.0f)
{
character.AIController.SteeringManager.SteeringSeek(currentHull.SimPosition, 0.5f);
}
else
{
character.AIController.SteeringManager.Reset();
}
character.AIController.SelectTarget(null);
goToObjective = null;
return;
}
} }
if (searchHullTimer > 0.0f) if (searchHullTimer > 0.0f)
{ {
searchHullTimer -= deltaTime; searchHullTimer -= deltaTime;
@@ -79,7 +64,7 @@ namespace Barotrauma
if (goToObjective != null) if (goToObjective != null)
{ {
var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager; var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
if (pathSteering!=null && pathSteering.CurrentPath!= null && if (pathSteering != null && pathSteering.CurrentPath != null &&
pathSteering.CurrentPath.Unreachable && !unreachable.Contains(goToObjective.Target)) pathSteering.CurrentPath.Unreachable && !unreachable.Contains(goToObjective.Target))
{ {
unreachable.Add(goToObjective.Target as Hull); unreachable.Add(goToObjective.Target as Hull);
@@ -92,7 +77,7 @@ namespace Barotrauma
private bool FindDivingGear(float deltaTime) private bool FindDivingGear(float deltaTime)
{ {
if (divingGearObjective==null) if (divingGearObjective == null)
{ {
divingGearObjective = new AIObjectiveFindDivingGear(character, false); divingGearObjective = new AIObjectiveFindDivingGear(character, false);
} }
@@ -113,8 +98,9 @@ namespace Barotrauma
if (hull == character.AnimController.CurrentHull || unreachable.Contains(hull)) continue; if (hull == character.AnimController.CurrentHull || unreachable.Contains(hull)) continue;
float hullValue = GetHullSafety(hull, character); float hullValue = GetHullSafety(hull, character);
hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.X - hull.Position.X)); //slight preference over hulls that are closer
hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.Y - hull.Position.Y) * 2.0f); hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.X - hull.Position.X)) * 0.1f;
hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.Y - hull.Position.Y)) * 0.2f;
if (bestHull == null || hullValue > bestValue) if (bestHull == null || hullValue > bestValue)
{ {
@@ -144,7 +130,7 @@ namespace Barotrauma
return false; return false;
} }
public override float GetPriority(Character character) public override float GetPriority(AIObjectiveManager objectiveManager)
{ {
if (character.Oxygen < 80.0f) if (character.Oxygen < 80.0f)
{ {
@@ -172,7 +158,6 @@ namespace Barotrauma
} }
} }
if (NeedsDivingGear()) if (NeedsDivingGear())
{ {
if (divingGearObjective != null && !divingGearObjective.IsCompleted()) priority += 20.0f; if (divingGearObjective != null && !divingGearObjective.IsCompleted()) priority += 20.0f;
@@ -185,11 +170,28 @@ namespace Barotrauma
{ {
if (hull == null) return 0.0f; if (hull == null) return 0.0f;
float safety = 100.0f;
float waterPercentage = (hull.WaterVolume / hull.Volume) * 100.0f; float waterPercentage = (hull.WaterVolume / hull.Volume) * 100.0f;
if (hull.LethalPressure > 0.0f && character.PressureProtection <= 0.0f)
{
safety -= 100.0f;
}
else if (character.OxygenAvailable <= 0.0f)
{
safety -= waterPercentage;
}
else
{
safety -= waterPercentage * 0.1f;
}
if (hull.OxygenPercentage < 30.0f) safety -= (30.0f - hull.OxygenPercentage) * 5.0f;
if (safety <= 0.0f) return 0.0f;
float fireAmount = 0.0f; float fireAmount = 0.0f;
var nearbyHulls = hull.GetConnectedHulls(3); var nearbyHulls = hull.GetConnectedHulls(3);
foreach (Hull hull2 in nearbyHulls) foreach (Hull hull2 in nearbyHulls)
{ {
foreach (FireSource fireSource in hull2.FireSources) foreach (FireSource fireSource in hull2.FireSources)
@@ -204,13 +206,9 @@ namespace Barotrauma
} }
} }
} }
safety -= fireAmount;
float safety = 100.0f - fireAmount; return MathHelper.Clamp(safety, 0.0f, 100.0f);
if (waterPercentage > 30.0f && character.OxygenAvailable <= 0.0f) safety -= waterPercentage;
if (hull.OxygenPercentage < 30.0f) safety -= (30.0f - hull.OxygenPercentage) * 5.0f;
return safety;
} }
} }
} }
@@ -7,7 +7,9 @@ namespace Barotrauma
{ {
class AIObjectiveFixLeak : AIObjective class AIObjectiveFixLeak : AIObjective
{ {
private Gap leak; private readonly Gap leak;
private AIObjectiveGoTo gotoObjective;
public Gap Leak public Gap Leak
{ {
@@ -20,15 +22,20 @@ namespace Barotrauma
this.leak = leak; this.leak = leak;
} }
public override float GetPriority(Character character) public override bool IsCompleted()
{
return leak.Open <= 0.0f || leak.Removed;
}
public override float GetPriority(AIObjectiveManager objectiveManager)
{ {
if (leak.Open == 0.0f) return 0.0f; if (leak.Open == 0.0f) return 0.0f;
float leakSize = (leak.IsHorizontal ? leak.Rect.Height : leak.Rect.Width) * Math.Max(leak.Open, 0.1f); float leakSize = (leak.IsHorizontal ? leak.Rect.Height : leak.Rect.Width) * Math.Max(leak.Open, 0.1f);
float dist = Vector2.DistanceSquared(character.SimPosition, leak.SimPosition); float dist = Vector2.DistanceSquared(character.SimPosition, leak.SimPosition);
dist = Math.Max(dist/100.0f, 1.0f); dist = Math.Max(dist / 100.0f, 1.0f);
return Math.Min(leakSize/dist, 40.0f); return Math.Min(leakSize / dist, 40.0f);
} }
public override bool IsDuplicate(AIObjective otherObjective) public override bool IsDuplicate(AIObjective otherObjective)
@@ -44,7 +51,7 @@ namespace Barotrauma
if (weldingTool == null) if (weldingTool == null)
{ {
subObjectives.Add(new AIObjectiveGetItem(character, "Welding Tool", true)); AddSubObjective(new AIObjectiveGetItem(character, "Welding Tool", true));
return; return;
} }
else else
@@ -52,25 +59,31 @@ namespace Barotrauma
var containedItems = weldingTool.ContainedItems; var containedItems = weldingTool.ContainedItems;
if (containedItems == null) return; if (containedItems == null) return;
var fuelTank = Array.Find(containedItems, i => i.Name == "Welding Fuel Tank" && i.Condition > 0.0f); var fuelTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Welding Fuel Tank") && i.Condition > 0.0f);
if (fuelTank == null) if (fuelTank == null)
{ {
AddSubObjective(new AIObjectiveContainItem(character, "Welding Fuel Tank", weldingTool.GetComponent<ItemContainer>())); AddSubObjective(new AIObjectiveContainItem(character, "Welding Fuel Tank", weldingTool.GetComponent<ItemContainer>()));
return;
} }
} }
var repairTool = weldingTool.GetComponent<RepairTool>(); var repairTool = weldingTool.GetComponent<RepairTool>();
if (repairTool == null) return; if (repairTool == null) return;
if (Vector2.Distance(character.WorldPosition, leak.WorldPosition) > 300.0f) Vector2 standPosition = GetStandPosition();
if (Vector2.DistanceSquared(character.WorldPosition, leak.WorldPosition) > 100.0f * 100.0f)
{ {
AddSubObjective(new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character)); var gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(standPosition), character);
} if (!gotoObjective.IsCompleted())
else {
{ AddSubObjective(gotoObjective);
AddSubObjective(new AIObjectiveOperateItem(repairTool, character, "", leak)); return;
}
} }
AddSubObjective(new AIObjectiveOperateItem(repairTool, character, "", true, leak));
} }
private Vector2 GetStandPosition() private Vector2 GetStandPosition()
@@ -7,38 +7,80 @@ namespace Barotrauma
{ {
class AIObjectiveFixLeaks : AIObjective class AIObjectiveFixLeaks : AIObjective
{ {
const float UpdateGapListInterval = 10.0f; const float UpdateGapListInterval = 5.0f;
private float updateGapListTimer; private double lastGapUpdate;
private AIObjectiveIdle idleObjective; private AIObjectiveIdle idleObjective;
private AIObjectiveFindDivingGear findDivingGear;
private List<AIObjectiveFixLeak> objectiveList; private List<AIObjectiveFixLeak> objectiveList;
public AIObjectiveFixLeaks(Character character) public AIObjectiveFixLeaks(Character character)
: base (character, "") : base (character, "")
{ {
objectiveList = new List<AIObjectiveFixLeak>();
} }
public override bool IsCompleted() public override bool IsCompleted()
{ {
return false; if (Timing.TotalTime > lastGapUpdate + UpdateGapListInterval || objectiveList == null)
{
UpdateGapList();
lastGapUpdate = Timing.TotalTime;
}
return objectiveList.Count == 0;
}
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (Timing.TotalTime > lastGapUpdate + UpdateGapListInterval || objectiveList == null)
{
UpdateGapList();
lastGapUpdate = Timing.TotalTime;
}
float priority = 0.0f;
foreach (AIObjectiveFixLeak fixObjective in objectiveList)
{
//gaps from outside to inside significantly increase the priority
if (!fixObjective.Leak.IsRoomToRoom)
{
priority = Math.Max(priority + fixObjective.Leak.Open * 100.0f, 50.0f);
}
else
{
priority += fixObjective.Leak.Open * 10.0f;
}
if (priority >= 100.0f) break;
}
return Math.Min(priority, 100.0f);
} }
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
updateGapListTimer -= deltaTime; if (Timing.TotalTime > lastGapUpdate + UpdateGapListInterval || objectiveList == null)
if (updateGapListTimer<=0.0f)
{ {
UpdateGapList(); UpdateGapList();
lastGapUpdate = Timing.TotalTime;
updateGapListTimer = UpdateGapListInterval;
} }
if (objectiveList.Any()) if (objectiveList.Any())
{ {
if (!objectiveList[objectiveList.Count - 1].Leak.IsRoomToRoom)
{
if (findDivingGear == null) findDivingGear = new AIObjectiveFindDivingGear(character, true);
if (!findDivingGear.IsCompleted() && findDivingGear.CanBeCompleted)
{
findDivingGear.TryComplete(deltaTime);
return;
}
}
objectiveList[objectiveList.Count - 1].TryComplete(deltaTime); objectiveList[objectiveList.Count - 1].TryComplete(deltaTime);
if (!objectiveList[objectiveList.Count - 1].CanBeCompleted || if (!objectiveList[objectiveList.Count - 1].CanBeCompleted ||
@@ -56,6 +98,7 @@ namespace Barotrauma
private void UpdateGapList() private void UpdateGapList()
{ {
if (objectiveList == null) objectiveList = new List<AIObjectiveFixLeak>();
objectiveList.Clear(); objectiveList.Clear();
foreach (Gap gap in Gap.GapList) foreach (Gap gap in Gap.GapList)
@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System;
namespace Barotrauma namespace Barotrauma
{ {
@@ -26,6 +27,16 @@ namespace Barotrauma
get { return canBeCompleted; } get { return canBeCompleted; }
} }
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
return 1.0f;
}
public AIObjectiveGetItem(Character character, Item targetItem, bool equip = false) public AIObjectiveGetItem(Character character, Item targetItem, bool equip = false)
: base(character, "") : base(character, "")
{ {
@@ -18,6 +18,16 @@ namespace Barotrauma
private bool getDivingGearIfNeeded; private bool getDivingGearIfNeeded;
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
return 1.0f;
}
public override bool CanBeCompleted public override bool CanBeCompleted
{ {
get get
@@ -93,7 +103,7 @@ namespace Barotrauma
} }
} }
if (Vector2.Distance(currTargetPos, character.SimPosition) < 1.0f) if (Vector2.DistanceSquared(currTargetPos, character.SimPosition) < 0.5f * 0.5f)
{ {
character.AIController.SteeringManager.Reset(); character.AIController.SteeringManager.Reset();
character.AnimController.TargetDir = currTargetPos.X > character.SimPosition.X ? Direction.Right : Direction.Left; character.AnimController.TargetDir = currTargetPos.X > character.SimPosition.X ? Direction.Right : Direction.Left;
@@ -104,7 +114,7 @@ namespace Barotrauma
var indoorsSteering = character.AIController.SteeringManager as IndoorsSteeringManager; var indoorsSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
if (indoorsSteering.CurrentPath==null || indoorsSteering.CurrentPath.Unreachable) if (indoorsSteering.CurrentPath == null || indoorsSteering.CurrentPath.Unreachable)
{ {
indoorsSteering.SteeringWander(); indoorsSteering.SteeringWander();
} }
@@ -130,7 +140,7 @@ namespace Barotrauma
if (item.IsInsideTrigger(character.WorldPosition)) completed = true; if (item.IsInsideTrigger(character.WorldPosition)) completed = true;
} }
completed = completed || Vector2.Distance(target != null ? target.SimPosition : targetPos, character.SimPosition) < allowedDistance; completed = completed || Vector2.DistanceSquared(target != null ? target.SimPosition : targetPos, character.SimPosition) < allowedDistance * allowedDistance;
if (completed) character.AIController.SteeringManager.Reset(); if (completed) character.AIController.SteeringManager.Reset();
@@ -9,15 +9,21 @@ namespace Barotrauma
{ {
const float WallAvoidDistance = 150.0f; const float WallAvoidDistance = 150.0f;
AITarget currentTarget; private AITarget currentTarget;
private float newTargetTimer; private float newTargetTimer;
private AIObjectiveFindSafety findSafety;
public AIObjectiveIdle(Character character) : base(character, "") public AIObjectiveIdle(Character character) : base(character, "")
{ {
} }
public override float GetPriority(Character character) public override bool IsCompleted()
{
return false;
}
public override float GetPriority(AIObjectiveManager objectiveManager)
{ {
return 1.0f; return 1.0f;
} }
@@ -32,6 +38,14 @@ namespace Barotrauma
return; return;
} }
if (character.AnimController.InWater)
{
//attempt to find a safer place if in water
if (findSafety == null) findSafety = new AIObjectiveFindSafety(character);
findSafety.TryComplete(deltaTime);
return;
}
if (newTargetTimer <= 0.0f) if (newTargetTimer <= 0.0f)
{ {
currentTarget = FindRandomTarget(); currentTarget = FindRandomTarget();
@@ -93,16 +107,9 @@ namespace Barotrauma
} }
} }
if (character.AnimController.InWater) character.AIController.SteeringManager.SteeringWander();
{ //reset vertical steering to prevent dropping down from platforms etc
character.AIController.SteeringManager.SteeringManual(deltaTime, new Vector2(0.0f, 0.5f)); character.AIController.SteeringManager.ResetY();
}
else
{
character.AIController.SteeringManager.SteeringWander();
//reset vertical steering to prevent dropping down from platforms etc
character.AIController.SteeringManager.ResetY();
}
return; return;
} }
@@ -11,15 +11,17 @@ namespace Barotrauma
private Character character; private Character character;
private AIObjective currentObjective; private AIObjective currentOrder;
public AIObjective CurrentOrder
{
get { return currentOrder; }
}
public AIObjective CurrentObjective public AIObjective CurrentObjective
{ {
get get;
{ private set;
if (currentObjective != null) return currentObjective;
return objectives.Any() ? objectives[0] : null;
}
} }
public AIObjectiveManager(Character character) public AIObjectiveManager(Character character)
@@ -47,8 +49,13 @@ namespace Barotrauma
public float GetCurrentPriority(Character character) public float GetCurrentPriority(Character character)
{ {
if (currentObjective != null) return OrderPriority; if (CurrentOrder != null &&
return (CurrentObjective == null) ? 0.0f : CurrentObjective.GetPriority(character); (objectives.Count == 0 || currentOrder.GetPriority(this) > objectives[0].GetPriority(this)))
{
return CurrentOrder.GetPriority(this);
}
return objectives.Count == 0 ? 0.0f : objectives[0].GetPriority(this);
} }
public void UpdateObjectives() public void UpdateObjectives()
@@ -59,43 +66,46 @@ namespace Barotrauma
objectives = objectives.FindAll(o => !o.IsCompleted() && o.CanBeCompleted); objectives = objectives.FindAll(o => !o.IsCompleted() && o.CanBeCompleted);
//sort objectives according to priority //sort objectives according to priority
objectives.Sort((x, y) => y.GetPriority(character).CompareTo(x.GetPriority(character))); objectives.Sort((x, y) => y.GetPriority(this).CompareTo(x.GetPriority(this)));
} }
public void DoCurrentObjective(float deltaTime) public void DoCurrentObjective(float deltaTime)
{ {
if (currentObjective != null && (!objectives.Any() || objectives[0].GetPriority(character) < OrderPriority)) if (currentOrder != null && (!objectives.Any() || objectives[0].GetPriority(this) < currentOrder.GetPriority(this)))
{ {
currentObjective.TryComplete(deltaTime); CurrentObjective = currentOrder;
currentOrder.TryComplete(deltaTime);
return; return;
} }
if (!objectives.Any()) return; if (!objectives.Any()) return;
objectives[0].TryComplete(deltaTime); objectives[0].TryComplete(deltaTime);
CurrentObjective = objectives[0];
} }
public void SetOrder(Order order, string option) public void SetOrder(Order order, string option)
{ {
if (order == null) return; if (order == null) return;
currentObjective = null; currentOrder = null;
switch (order.Name.ToLowerInvariant()) switch (order.Name.ToLowerInvariant())
{ {
case "follow": case "follow":
currentObjective = new AIObjectiveGoTo(Character.Controlled, character, true); currentOrder = new AIObjectiveGoTo(Character.Controlled, character, true);
break; break;
case "wait": case "wait":
currentObjective = new AIObjectiveGoTo(character, character, true); currentOrder = new AIObjectiveGoTo(character, character, true);
break; break;
case "fixleaks": case "fixleaks":
case "fix leaks": case "fix leaks":
currentObjective = new AIObjectiveFixLeaks(character); currentOrder = new AIObjectiveFixLeaks(character);
break; break;
default: default:
if (order.TargetItem == null) return; if (order.TargetItem == null) return;
currentObjective = new AIObjectiveOperateItem(order.TargetItem, character, option, null, order.UseController); currentOrder = new AIObjectiveOperateItem(order.TargetItem, character, option, false, null, order.UseController);
break; break;
} }
@@ -1,5 +1,6 @@
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Barotrauma namespace Barotrauma
@@ -14,6 +15,8 @@ namespace Barotrauma
private bool canBeCompleted; private bool canBeCompleted;
private bool requireEquip;
public override bool CanBeCompleted public override bool CanBeCompleted
{ {
get get
@@ -27,11 +30,21 @@ namespace Barotrauma
get { return operateTarget; } get { return operateTarget; }
} }
public AIObjectiveOperateItem(ItemComponent item, Character character, string option, Entity operateTarget = null, bool useController = false) public override float GetPriority(AIObjectiveManager objectiveManager)
:base (character, option) {
if (objectiveManager.CurrentOrder == this)
{
return AIObjectiveManager.OrderPriority;
}
return 1.0f;
}
public AIObjectiveOperateItem(ItemComponent item, Character character, string option, bool requireEquip, Entity operateTarget = null, bool useController = false)
: base (character, option)
{ {
this.component = item; this.component = item;
this.requireEquip = requireEquip;
this.operateTarget = operateTarget; this.operateTarget = operateTarget;
if (useController) if (useController)
@@ -72,6 +85,43 @@ namespace Barotrauma
} }
else else
{ {
if (requireEquip && !character.HasEquippedItem(component.Item))
{
//the item has to be equipped before using it if it's holdable
var holdable = component.Item.GetComponent<Holdable>();
if (holdable == null)
{
DebugConsole.ThrowError("AIObjectiveOperateItem failed - equipping item " + component.Item + " is required but the item has no Holdable component");
return;
}
for (int i = 0; i < CharacterInventory.limbSlots.Length; i++)
{
if (CharacterInventory.limbSlots[i] == InvSlotType.Any ||
!holdable.AllowedSlots.Any(s => s.HasFlag(CharacterInventory.limbSlots[i])))
{
continue;
}
//equip slot already taken
if (character.Inventory.Items[i] != null)
{
//try to put the item in an Any slot, and drop it if that fails
if (!character.Inventory.Items[i].AllowedSlots.Contains(InvSlotType.Any) ||
!character.Inventory.TryPutItem(character.Inventory.Items[i], character, new List<InvSlotType>() { InvSlotType.Any }))
{
character.Inventory.Items[i].Drop();
}
}
if (character.Inventory.TryPutItem(component.Item, i, true, character))
{
component.Item.Equip(character);
break;
}
}
return;
}
if (component.AIOperate(deltaTime, character, this)) isCompleted = true; if (component.AIOperate(deltaTime, character, this)) isCompleted = true;
} }
} }
@@ -3,7 +3,7 @@ using System.Diagnostics;
namespace Barotrauma namespace Barotrauma
{ {
class AIObjectiveRescue : AIObjective /*class AIObjectiveRescue : AIObjective
{ {
private readonly Character targetCharacter; private readonly Character targetCharacter;
@@ -30,5 +30,5 @@ namespace Barotrauma
return targetCharacter.IsDead ? 1000.0f / distance : 10000.0f / distance; return targetCharacter.IsDead ? 1000.0f / distance : 10000.0f / distance;
} }
} }*/
} }
@@ -3,7 +3,7 @@ using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
class AIObjectiveRescueAll : AIObjective /*class AIObjectiveRescueAll : AIObjective
{ {
private List<Character> rescueTargets; private List<Character> rescueTargets;
@@ -44,5 +44,5 @@ namespace Barotrauma
AddSubObjective(new AIObjectiveRescue(character, target)); AddSubObjective(new AIObjectiveRescue(character, target));
} }
} }
} }*/
} }
@@ -9,6 +9,8 @@ namespace Barotrauma
public enum Animation { None, Climbing, UsingConstruction, Struggle, CPR }; public enum Animation { None, Climbing, UsingConstruction, Struggle, CPR };
public Animation Anim; public Animation Anim;
public LimbType GrabLimb;
protected Character character; protected Character character;
protected float walkSpeed, swimSpeed; protected float walkSpeed, swimSpeed;
@@ -20,6 +20,7 @@ namespace Barotrauma
private float thighTorque; private float thighTorque;
private float cprAnimState; private float cprAnimState;
private float cprPump;
private float inWaterTimer; private float inWaterTimer;
private bool swimming; private bool swimming;
@@ -872,7 +873,34 @@ namespace Barotrauma
character.SelectedConstruction = null; character.SelectedConstruction = null;
IgnorePlatforms = false; IgnorePlatforms = false;
} }
else if (character.SelectedCharacter != null && !character.SelectedCharacter.AllowInput)
{
Limb targetLeftHand = character.SelectedCharacter.AnimController.GetLimb(LimbType.LeftHand);
Limb targetRightHand = character.SelectedCharacter.AnimController.GetLimb(LimbType.RightHand);
Limb targetTorso = character.SelectedCharacter.AnimController.GetLimb(LimbType.Torso);
if (character.SelectedCharacter.AnimController.Dir != Dir)
character.SelectedCharacter.AnimController.Flip();
targetTorso.pullJoint.Enabled = true;
targetTorso.pullJoint.WorldAnchorB = torso.SimPosition + (Vector2.UnitX * -Dir) * 0.2f;
targetTorso.pullJoint.MaxForce = 5000.0f;
if (!targetLeftHand.IsSevered)
{
targetLeftHand.pullJoint.Enabled = true;
targetLeftHand.pullJoint.WorldAnchorB = torso.SimPosition + (new Vector2(1 * Dir, 1)) * 0.2f;
targetLeftHand.pullJoint.MaxForce = 5000.0f;
}
if (!targetRightHand.IsSevered)
{
targetRightHand.pullJoint.Enabled = true;
targetRightHand.pullJoint.WorldAnchorB = torso.SimPosition + (new Vector2(1 * Dir, 1)) * 0.2f;
targetRightHand.pullJoint.MaxForce = 5000.0f;
}
character.SelectedCharacter.AnimController.IgnorePlatforms = true;
}
} }
private void UpdateCPR(float deltaTime) private void UpdateCPR(float deltaTime)
@@ -883,10 +911,15 @@ namespace Barotrauma
return; return;
} }
Character target = character.SelectedCharacter;
Crouching = true; Crouching = true;
Vector2 diff = character.SelectedCharacter.SimPosition - character.SimPosition; Vector2 diff = target.SimPosition - character.SimPosition;
var targetHead = character.SelectedCharacter.AnimController.GetLimb(LimbType.Head); Limb targetHead = target.AnimController.GetLimb(LimbType.Head);
Limb targetTorso = target.AnimController.GetLimb(LimbType.Torso);
Limb head = GetLimb(LimbType.Head);
Limb torso = GetLimb(LimbType.Torso);
Vector2 headDiff = targetHead == null ? diff : targetHead.SimPosition - character.SimPosition; Vector2 headDiff = targetHead == null ? diff : targetHead.SimPosition - character.SimPosition;
@@ -895,21 +928,98 @@ namespace Barotrauma
UpdateStanding(); UpdateStanding();
Vector2 handPos = character.SelectedCharacter.AnimController.GetLimb(LimbType.Torso).SimPosition + Vector2.UnitY * 0.2f; Vector2 handPos = targetTorso.SimPosition + Vector2.UnitY * 0.2f;
Grab(handPos, handPos); Grab(handPos, handPos);
float yPos = (float)Math.Sin(cprAnimState) * 0.1f; Vector2 colliderPos = GetColliderBottom();
cprAnimState += deltaTime * 8.0f;
var head = GetLimb(LimbType.Head); if (GameMain.Client == null) //Serverside code
head.pullJoint.WorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.6f + yPos); {
head.pullJoint.Enabled = true; if (target.Bleeding <= 0.5f && target.Oxygen <= 0.0f) //If they're bleeding too hard CPR will hurt them
{
target.Oxygen += deltaTime * 0.5f; //Stabilize them
}
}
int skill = character.GetSkillLevel("Medical");
if (cprAnimState % 17 > 15.0f)
{
float yPos = (float)Math.Sin(cprAnimState) * 0.2f;
head.pullJoint.WorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.3f + yPos);
head.pullJoint.Enabled = true;
torso.pullJoint.WorldAnchorB = new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition - 0.2f));
torso.pullJoint.Enabled = true;
if (GameMain.Client == null) //Serverside code
{
float cpr = skill / 2.0f; //Max possible oxygen addition is 20 per second
character.Oxygen -= (30.0f - cpr) * deltaTime; //Worse skill = more oxygen required
if (character.Oxygen > 0.0f) //we didn't suffocate yet did we
target.Oxygen += cpr * deltaTime;
//DebugConsole.NewMessage("CPR Us: " + character.Oxygen + " Them: " + target.Oxygen + " How good we are: restore " + cpr + " use " + (30.0f - cpr), Color.Aqua);
}
}
else
{
head.pullJoint.WorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.8f);
head.pullJoint.Enabled = true;
torso.pullJoint.WorldAnchorB = new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition - 0.1f));
torso.pullJoint.Enabled = true;
if (cprPump >= 1)
{
torso.body.ApplyForce(new Vector2(0, -1000f));
targetTorso.body.ApplyForce(new Vector2(0, -1000f));
cprPump = 0;
if (target.Bleeding <= 0.5f && target.Health <= 0.0f && !target.IsDead) //Have a chance to revive them to 2 HP if they were damaged.
{
if (GameMain.Client == null) //Serverside code
{
float reviveChance = (cprAnimState % 17) * (skill / 50.0f); //~5% max chance for 10 skill, ~50% max chance for 100 skill
float rng = Rand.Int(100, Rand.RandSync.Server);
//DebugConsole.NewMessage("CPR Pump cprAnimState: " + (cprAnimState % 17) + " revive chance: " + reviveChance + " rng: " + rng, Color.Aqua);
if (rng <= reviveChance) //HOLY CRAP YOU SAVED HIM!!!
{
target.Oxygen = Math.Max(target.Oxygen, 10.0f);
target.Health = 2.0f;
Anim = Animation.None;
return;
}
}
}
else if (target.Bleeding > 0.5f || skill < 50) //We will hurt them if they're bleeding or we suck
{
//If not bleeding: 10% skill causes 0.8 damage per pump, 40% skill causes only 0.2
if (target.Bleeding <= 0.5f)
target.AddDamage(CauseOfDeath.Damage, (50 - skill) * 0.02f, character);
else //If bleeding: 2 HP damage per pump. Basically speeds up their death. Don't pump bleeding people!
{
target.AddDamage(CauseOfDeath.Bloodloss, 1.0f, character);
#if CLIENT
SoundPlayer.PlayDamageSound(DamageSoundType.LimbBlunt, 25.0f, targetTorso.body);
for (int i = 0; i < 4; i++)
{
var blood = GameMain.ParticleManager.CreateParticle(inWater ? "waterblood" : "blood", targetTorso.WorldPosition, Rand.Vector(10.0f), 0.0f, target.AnimController.CurrentHull);
}
#endif
}
}
}
cprPump += deltaTime;
}
cprAnimState += deltaTime;
} }
public override void DragCharacter(Character target) public override void DragCharacter(Character target)
{ {
if (target == null) return; if (target == null) return;
Limb torso = GetLimb(LimbType.Torso);
Limb leftHand = GetLimb(LimbType.LeftHand); Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand); Limb rightHand = GetLimb(LimbType.RightHand);
@@ -922,31 +1032,34 @@ namespace Barotrauma
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
Limb targetLimb = target.AnimController.GetLimb(LimbType.Torso); Limb targetLimb = target.AnimController.GetLimb(GrabLimb);
if (i == 0) if (targetLimb == null || targetLimb.IsSevered)
{ {
if (!targetLeftHand.IsSevered) targetLimb = target.AnimController.GetLimb(LimbType.Torso);
if (i == 0)
{ {
targetLimb = targetLeftHand; if (!targetLeftHand.IsSevered)
{
targetLimb = targetLeftHand;
}
else if (!targetRightHand.IsSevered)
{
targetLimb = targetRightHand;
}
} }
else if (!targetRightHand.IsSevered) else
{ {
targetLimb = targetRightHand; if (!targetRightHand.IsSevered)
{
targetLimb = targetRightHand;
}
else if (!targetLeftHand.IsSevered)
{
targetLimb = targetLeftHand;
}
} }
} }
else
{
if (!targetRightHand.IsSevered)
{
targetLimb = targetRightHand;
}
else if (!targetLeftHand.IsSevered)
{
targetLimb = targetLeftHand;
}
}
Limb pullLimb = i == 0 ? leftHand : rightHand; Limb pullLimb = i == 0 ? leftHand : rightHand;
if (i == 1 && inWater) if (i == 1 && inWater)
@@ -958,12 +1071,32 @@ namespace Barotrauma
Vector2 diff = ConvertUnits.ToSimUnits(targetLimb.WorldPosition - pullLimb.WorldPosition); Vector2 diff = ConvertUnits.ToSimUnits(targetLimb.WorldPosition - pullLimb.WorldPosition);
pullLimb.pullJoint.Enabled = true; pullLimb.pullJoint.Enabled = true;
pullLimb.pullJoint.WorldAnchorB = pullLimb.SimPosition + diff; if (targetLimb.type == LimbType.Torso)
pullLimb.pullJoint.MaxForce = 10000.0f; {
pullLimb.pullJoint.WorldAnchorB = targetLimb.SimPosition;
pullLimb.pullJoint.MaxForce = 5000.0f;
targetMovement *= 0.7f; //Carrying people like that takes a lot of effort.
if (target.AnimController.Dir != Dir)
target.AnimController.Flip();
}
else
{
pullLimb.pullJoint.WorldAnchorB = pullLimb.SimPosition + diff;
pullLimb.pullJoint.MaxForce = 5000.0f;
}
targetLimb.pullJoint.Enabled = true; targetLimb.pullJoint.Enabled = true;
targetLimb.pullJoint.WorldAnchorB = targetLimb.SimPosition - diff; if (targetLimb.type == LimbType.Torso)
targetLimb.pullJoint.MaxForce = 10000.0f; {
targetLimb.pullJoint.WorldAnchorB = torso.SimPosition + (Vector2.UnitX * Dir) * 0.6f;
targetLimb.pullJoint.MaxForce = 300.0f;
}
else
{
targetLimb.pullJoint.WorldAnchorB = targetLimb.SimPosition - diff;
targetLimb.pullJoint.MaxForce = 5000.0f;
}
target.AnimController.movement = -diff; target.AnimController.movement = -diff;
} }
@@ -1384,7 +1384,7 @@ namespace Barotrauma
findFocusedTimer -= deltaTime; findFocusedTimer -= deltaTime;
} }
if (SelectedCharacter != null && IsKeyHit(InputType.Select)) if (SelectedCharacter != null && focusedItem == null && IsKeyHit(InputType.Select)) //Let people use ladders and buttons and stuff when dragging chars
{ {
DeselectCharacter(); DeselectCharacter();
} }
@@ -1542,26 +1542,42 @@ namespace Barotrauma
} }
} }
//Skip health effects as critical health handles it differently
if (IsUnconscious) if (IsUnconscious)
{ {
UpdateUnconscious(deltaTime); UpdateUnconscious(deltaTime);
return; return;
} }
//Do ragdoll shenanigans before Stun because it's still technically a stun, innit? Less network updates for us!
if (IsForceRagdolled) if (IsForceRagdolled)
IsRagdolled = IsForceRagdolled; IsRagdolled = IsForceRagdolled;
else if (!IsRagdolled || AnimController.Collider.LinearVelocity.Length() < 1f) //Keep us ragdolled if we were forced or we're too speedy to unragdoll else if ((GameMain.Server == null || GameMain.Server.AllowRagdollButton) && (!IsRagdolled || AnimController.Collider.LinearVelocity.Length() < 1f)) //Keep us ragdolled if we were forced or we're too speedy to unragdoll
IsRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves IsRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves
//Health effects
if (needsAir) UpdateOxygen(deltaTime);
Health -= bleeding * deltaTime;
Bleeding -= BleedingDecreaseSpeed * deltaTime;
if (health <= minHealth) Kill(CauseOfDeath.Bloodloss);
if (!IsDead) LockHands = false;
//ragdoll button
if (IsRagdolled) if (IsRagdolled)
{ {
if (AnimController is HumanoidAnimController) ((HumanoidAnimController)AnimController).Crouching = false; if (AnimController is HumanoidAnimController) ((HumanoidAnimController)AnimController).Crouching = false;
if(GameMain.Server != null)
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
AnimController.ResetPullJoints(); AnimController.ResetPullJoints();
selectedConstruction = null; selectedConstruction = null;
return; return;
} }
//AI and control stuff
Control(deltaTime, cam); Control(deltaTime, cam);
if (controlled != this && (!(this is AICharacter) || IsRemotePlayer)) if (controlled != this && (!(this is AICharacter) || IsRemotePlayer))
{ {
@@ -1574,11 +1590,6 @@ namespace Barotrauma
selectedConstruction = null; selectedConstruction = null;
} }
if (SelectedCharacter != null && AnimController.Anim == AnimController.Animation.CPR)
{
if (GameMain.Client == null) SelectedCharacter.Oxygen += (GetSkillLevel("Medical") / 10.0f) * deltaTime;
}
UpdateSightRange(); UpdateSightRange();
if (aiTarget != null) aiTarget.SoundRange = 0.0f; if (aiTarget != null) aiTarget.SoundRange = 0.0f;
@@ -1595,6 +1606,7 @@ namespace Barotrauma
if (health <= minHealth) Kill(CauseOfDeath.Bloodloss); if (health <= minHealth) Kill(CauseOfDeath.Bloodloss);
if (!IsDead) LockHands = false; if (!IsDead) LockHands = false;
//CPR stuff is handled in the UpdateCPR function in HumanoidAnimController
} }
partial void UpdateControlled(float deltaTime, Camera cam); partial void UpdateControlled(float deltaTime, Camera cam);
@@ -1634,12 +1646,17 @@ namespace Barotrauma
AnimController.ResetPullJoints(); AnimController.ResetPullJoints();
selectedConstruction = null; selectedConstruction = null;
if (oxygen <= 0.0f) Oxygen -= deltaTime * 0.5f; Oxygen -= deltaTime * 0.5f; //We're critical - our heart stopped!
if (health <= 0.0f) if (health <= 0.0f) //Critical health - use current state for crit time
{ {
AddDamage(bleeding > 0.5f ? CauseOfDeath.Bloodloss : CauseOfDeath.Damage, Math.Max(bleeding, 1.0f) * deltaTime, null); AddDamage(bleeding > 0.5f ? CauseOfDeath.Bloodloss : CauseOfDeath.Damage, Math.Max(bleeding, 1.0f) * deltaTime, null);
} }
else //Keep on bleedin'
{
Health -= bleeding * deltaTime;
Bleeding -= BleedingDecreaseSpeed * deltaTime;
}
} }
private void UpdateSightRange() private void UpdateSightRange()
@@ -325,7 +325,7 @@ namespace Barotrauma
break; break;
case ClientNetObject.ENTITY_STATE: case ClientNetObject.ENTITY_STATE:
int eventType = msg.ReadRangedInteger(0,2); int eventType = msg.ReadRangedInteger(0,3);
switch (eventType) switch (eventType)
{ {
case 0: case 0:
@@ -357,6 +357,9 @@ namespace Barotrauma
Kill(lastAttackCauseOfDeath); Kill(lastAttackCauseOfDeath);
} }
break; break;
case 3:
AnimController.GrabLimb = (LimbType)msg.ReadUInt16();
break;
} }
break; break;
} }
@@ -436,6 +439,7 @@ namespace Barotrauma
if (AnimController is HumanoidAnimController) if (AnimController is HumanoidAnimController)
{ {
tempBuffer.Write(((HumanoidAnimController)AnimController).Crouching); tempBuffer.Write(((HumanoidAnimController)AnimController).Crouching);
tempBuffer.Write((UInt16)AnimController.GrabLimb);
} }
bool hasAttackLimb = AnimController.Limbs.Any(l => l != null && l.attack != null); bool hasAttackLimb = AnimController.Limbs.Any(l => l != null && l.attack != null);
@@ -534,6 +538,8 @@ namespace Barotrauma
msg.WriteRangedSingle(MathHelper.Clamp(Stun, 0.0f, MaxStun), 0.0f, MaxStun, 8); msg.WriteRangedSingle(MathHelper.Clamp(Stun, 0.0f, MaxStun), 0.0f, MaxStun, 8);
} }
msg.Write(IsRagdolled);
msg.Write(HuskInfectionState > 0.0f); msg.Write(HuskInfectionState > 0.0f);
} }
} }
File diff suppressed because it is too large Load Diff
@@ -104,10 +104,6 @@ namespace Barotrauma.Items.Components
if (character == null) return false; if (character == null) return false;
if (!character.IsKeyDown(InputType.Aim)) return false; if (!character.IsKeyDown(InputType.Aim)) return false;
//if (DoesUseFail(Character)) return false;
//targetPosition = targetPosition.X, -targetPosition.Y);
float degreeOfSuccess = DegreeOfSuccess(character)/100.0f; float degreeOfSuccess = DegreeOfSuccess(character)/100.0f;
if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess) if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess)
@@ -258,9 +258,9 @@ namespace Barotrauma.Items.Components
if (batteryToLoad == null) return true; if (batteryToLoad == null) return true;
if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed*0.4f) if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed * 0.4f)
{ {
objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, "")); objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, "", false));
return false; return false;
} }
} }
@@ -678,14 +678,13 @@ namespace Barotrauma
if (!MathUtils.IsValid(damage)) return; if (!MathUtils.IsValid(damage)) return;
float damageDiff = damage - sections[sectionIndex].damage;
if (GameMain.Server != null && damage != sections[sectionIndex].damage) if (GameMain.Server != null && damage != sections[sectionIndex].damage)
{ {
GameMain.Server.CreateEntityEvent(this); GameMain.Server.CreateEntityEvent(this);
} }
AdjustKarma(attacker, damageDiff);
if (damage < prefab.Health*0.5f) if (damage < prefab.Health*0.5f)
{ {
if (sections[sectionIndex].gap != null) if (sections[sectionIndex].gap != null)
@@ -718,9 +717,13 @@ namespace Barotrauma
sections[sectionIndex].gap.Open = (damage / prefab.Health - 0.5f) * 2.0f; sections[sectionIndex].gap.Open = (damage / prefab.Health - 0.5f) * 2.0f;
} }
float damageDiff = damage - sections[sectionIndex].damage;
bool hadHole = SectionBodyDisabled(sectionIndex); bool hadHole = SectionBodyDisabled(sectionIndex);
sections[sectionIndex].damage = MathHelper.Clamp(damage, 0.0f, prefab.Health); sections[sectionIndex].damage = MathHelper.Clamp(damage, 0.0f, prefab.Health);
if (sections[sectionIndex].damage < prefab.Health) //otherwise it's possible to infinitely gain karma by welding fixed things
AdjustKarma(attacker, damageDiff);
bool hasHole = SectionBodyDisabled(sectionIndex); bool hasHole = SectionBodyDisabled(sectionIndex);
if (hadHole == hasHole) return; if (hadHole == hasHole) return;
@@ -7,7 +7,7 @@ namespace Barotrauma.Networking
{ {
enum ChatMessageType enum ChatMessageType
{ {
Default, Error, Dead, Server, Radio, Private, MessageBox Default, Error, Dead, Server, Radio, Private, Console, MessageBox
} }
partial class ChatMessage partial class ChatMessage
@@ -25,7 +25,8 @@ namespace Barotrauma.Networking
new Color(63, 72, 204), //dead new Color(63, 72, 204), //dead
new Color(157, 225, 160), //server new Color(157, 225, 160), //server
new Color(238, 208, 0), //radio new Color(238, 208, 0), //radio
new Color(64, 240, 89) //private new Color(64, 240, 89), //private
new Color(255, 255, 255) //console
}; };
public readonly string Text; public readonly string Text;
@@ -1,29 +1,10 @@
using Lidgren.Network; using Lidgren.Network;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
[Flags]
enum ClientPermissions
{
None = 0,
[Description("End round")]
EndRound = 1,
[Description("Kick")]
Kick = 2,
[Description("Ban")]
Ban = 4,
[Description("Select submarine")]
SelectSub = 8,
[Description("Select game mode")]
SelectMode = 16,
[Description("Manage campaign")]
ManageCampaign = 32
}
class Client class Client
{ {
public string Name; public string Name;
@@ -96,6 +77,11 @@ namespace Barotrauma.Networking
public float DeleteDisconnectedTimer; public float DeleteDisconnectedTimer;
public ClientPermissions Permissions = ClientPermissions.None; public ClientPermissions Permissions = ClientPermissions.None;
public List<DebugConsole.Command> PermittedConsoleCommands
{
get;
private set;
}
public bool SpectateOnly; public bool SpectateOnly;
@@ -130,6 +116,7 @@ namespace Barotrauma.Networking
this.Name = name; this.Name = name;
this.ID = ID; this.ID = ID;
PermittedConsoleCommands = new List<DebugConsole.Command>();
kickVoters = new List<Client>(); kickVoters = new List<Client>();
votes = new object[Enum.GetNames(typeof(VoteType)).Length]; votes = new object[Enum.GetNames(typeof(VoteType)).Length];
@@ -171,9 +158,10 @@ namespace Barotrauma.Networking
return rName; return rName;
} }
public void SetPermissions(ClientPermissions permissions) public void SetPermissions(ClientPermissions permissions, List<DebugConsole.Command> permittedConsoleCommands)
{ {
this.Permissions = permissions; this.Permissions = permissions;
this.PermittedConsoleCommands = new List<DebugConsole.Command>(permittedConsoleCommands);
} }
public void GivePermission(ClientPermissions permission) public void GivePermission(ClientPermissions permission)
@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Xml.Linq;
namespace Barotrauma.Networking
{
[Flags]
enum ClientPermissions
{
None = 0,
[Description("End round")]
EndRound = 1,
[Description("Kick")]
Kick = 2,
[Description("Ban")]
Ban = 4,
[Description("Select submarine")]
SelectSub = 8,
[Description("Select game mode")]
SelectMode = 16,
[Description("Manage campaign")]
ManageCampaign = 32,
[Description("Console commands")]
ConsoleCommands = 64
}
class PermissionPreset
{
public static List<PermissionPreset> List = new List<PermissionPreset>();
public readonly string Name;
public readonly string Description;
public readonly ClientPermissions Permissions;
public readonly List<DebugConsole.Command> PermittedCommands;
public PermissionPreset(XElement element)
{
Name = element.GetAttributeString("name", "");
Description = element.GetAttributeString("description", "");
string permissionsStr = element.GetAttributeString("permissions", "");
if (!Enum.TryParse(permissionsStr, out Permissions))
{
DebugConsole.ThrowError("Error in permission preset \"" + Name + "\" - " + permissionsStr + " is not a valid permission!");
}
PermittedCommands = new List<DebugConsole.Command>();
if (Permissions.HasFlag(ClientPermissions.ConsoleCommands))
{
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "command") continue;
string commandName = subElement.GetAttributeString("name", "");
DebugConsole.Command command = DebugConsole.FindCommand(commandName);
if (command == null)
{
DebugConsole.ThrowError("Error in permission preset \"" + Name + "\" - " + commandName + "\" is not a valid console command.");
continue;
}
PermittedCommands.Add(command);
}
}
}
public static void LoadAll(string file)
{
if (!File.Exists(file)) return;
XDocument doc = XMLExtensions.TryLoadXml(file);
if (doc == null || doc.Root == null) return;
foreach (XElement element in doc.Root.Elements())
{
List.Add(new PermissionPreset(element));
}
}
}
}
@@ -125,6 +125,7 @@ namespace Barotrauma.Networking
banList = new BanList(); banList = new BanList();
LoadSettings(); LoadSettings();
PermissionPreset.LoadAll(PermissionPresetFile);
LoadClientPermissions(); LoadClientPermissions();
CoroutineManager.StartCoroutine(StartServer(isPublic)); CoroutineManager.StartCoroutine(StartServer(isPublic));
@@ -795,6 +796,11 @@ namespace Barotrauma.Networking
campaign.ServerRead(inc, sender); campaign.ServerRead(inc, sender);
} }
break; break;
case ClientPermissions.ConsoleCommands:
string consoleCommand = inc.ReadString();
Vector2 clientCursorPos = new Vector2(inc.ReadSingle(), inc.ReadSingle());
DebugConsole.ExecuteClientCommand(sender, clientCursorPos, consoleCommand);
break;
} }
inc.ReadPadBits(); inc.ReadPadBits();
@@ -853,7 +859,7 @@ namespace Barotrauma.Networking
outmsg.Write(GameStarted); outmsg.Write(GameStarted);
outmsg.Write(AllowSpectating); outmsg.Write(AllowSpectating);
outmsg.Write((byte)c.Permissions); WritePermissions(outmsg, c);
} }
private void ClientWriteIngame(Client c) private void ClientWriteIngame(Client c)
@@ -1636,6 +1642,18 @@ namespace Barotrauma.Networking
} }
} }
public void SendChatMessage(string txt, Client recipient)
{
ChatMessage msg = ChatMessage.Create("", txt, ChatMessageType.Server, null);
SendChatMessage(msg, recipient);
}
public void SendConsoleMessage(string txt, Client recipient)
{
ChatMessage msg = ChatMessage.Create("", txt, ChatMessageType.Console, null);
SendChatMessage(msg, recipient);
}
public void SendChatMessage(ChatMessage msg, Client recipient) public void SendChatMessage(ChatMessage msg, Client recipient)
{ {
msg.NetStateID = recipient.ChatMsgQueue.Count > 0 ? msg.NetStateID = recipient.ChatMsgQueue.Count > 0 ?
@@ -1936,17 +1954,35 @@ namespace Barotrauma.Networking
clientPermissions.Add(new SavedClientPermission( clientPermissions.Add(new SavedClientPermission(
client.Name, client.Name,
client.Connection.RemoteEndPoint.Address.ToString(), client.Connection.RemoteEndPoint.Address.ToString(),
client.Permissions)); client.Permissions,
client.PermittedConsoleCommands));
} }
var msg = server.CreateMessage(); var msg = server.CreateMessage();
msg.Write((byte)ServerPacketHeader.PERMISSIONS); msg.Write((byte)ServerPacketHeader.PERMISSIONS);
msg.Write((byte)client.Permissions); WritePermissions(msg, client);
server.SendMessage(msg, client.Connection, NetDeliveryMethod.ReliableUnordered); server.SendMessage(msg, client.Connection, NetDeliveryMethod.ReliableUnordered);
SaveClientPermissions(); SaveClientPermissions();
} }
private void WritePermissions(NetBuffer msg, Client client)
{
msg.Write((byte)client.Permissions);
if (client.Permissions.HasFlag(ClientPermissions.ConsoleCommands))
{
msg.Write((UInt16)client.PermittedConsoleCommands.Sum(c => c.names.Length));
foreach (DebugConsole.Command command in client.PermittedConsoleCommands)
{
foreach (string commandName in command.names)
{
msg.Write(commandName);
}
}
}
}
public void SetClientCharacter(Client client, Character newCharacter) public void SetClientCharacter(Client client, Character newCharacter)
{ {
if (client == null) return; if (client == null) return;
@@ -179,7 +179,7 @@ namespace Barotrauma.Networking
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (name taken by the server)", Color.Red); DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (name taken by the server)", Color.Red);
return; return;
} }
Client nameTaken = ConnectedClients.Find(c => c.Name.ToLower() == clName.ToLower()); Client nameTaken = ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), clName.ToLower()));
if (nameTaken != null) if (nameTaken != null)
{ {
if (nameTaken.Connection.RemoteEndPoint.Address.ToString() == inc.SenderEndPoint.Address.ToString()) if (nameTaken.Connection.RemoteEndPoint.Address.ToString() == inc.SenderEndPoint.Address.ToString())
@@ -218,11 +218,11 @@ namespace Barotrauma.Networking
var savedPermissions = clientPermissions.Find(cp => cp.IP == newClient.Connection.RemoteEndPoint.Address.ToString()); var savedPermissions = clientPermissions.Find(cp => cp.IP == newClient.Connection.RemoteEndPoint.Address.ToString());
if (savedPermissions != null) if (savedPermissions != null)
{ {
newClient.SetPermissions(savedPermissions.Permissions); newClient.SetPermissions(savedPermissions.Permissions, savedPermissions.PermittedCommands);
} }
else else
{ {
newClient.SetPermissions(ClientPermissions.None); newClient.SetPermissions(ClientPermissions.None, new List<DebugConsole.Command>());
} }
} }
@@ -25,20 +25,23 @@ namespace Barotrauma.Networking
{ {
public readonly string IP; public readonly string IP;
public readonly string Name; public readonly string Name;
public List<DebugConsole.Command> PermittedCommands;
public ClientPermissions Permissions; public ClientPermissions Permissions;
public SavedClientPermission(string name, string ip, ClientPermissions permissions) public SavedClientPermission(string name, string ip, ClientPermissions permissions, List<DebugConsole.Command> permittedCommands)
{ {
this.Name = name; this.Name = name;
this.IP = ip; this.IP = ip;
this.Permissions = permissions; this.Permissions = permissions;
this.PermittedCommands = permittedCommands;
} }
} }
public const string SettingsFile = "serversettings.xml"; public const string SettingsFile = "serversettings.xml";
public static readonly string ClientPermissionsFile = "Data" + Path.DirectorySeparatorChar + "clientpermissions.txt"; public static readonly string PermissionPresetFile = "Data" + Path.DirectorySeparatorChar + "permissionpresets.xml";
public static readonly string ClientPermissionsFile = "Data" + Path.DirectorySeparatorChar + "clientpermissions.xml";
public Dictionary<string, SerializableProperty> SerializableProperties public Dictionary<string, SerializableProperty> SerializableProperties
{ {
@@ -130,6 +133,13 @@ namespace Barotrauma.Networking
private set; private set;
} }
[Serialize(true, true)]
public bool AllowRagdollButton
{
get;
private set;
}
[Serialize(true, true)] [Serialize(true, true)]
public bool AllowFileTransfers public bool AllowFileTransfers
{ {
@@ -210,6 +220,20 @@ namespace Barotrauma.Networking
private set; private set;
} }
[Serialize(true, true)]
public bool TraitorUseRatio
{
get;
private set;
}
[Serialize(0.2f, true)]
public float TraitorRatio
{
get;
private set;
}
[Serialize(false,true)] [Serialize(false,true)]
public bool KarmaEnabled public bool KarmaEnabled
{ {
@@ -323,12 +347,67 @@ namespace Barotrauma.Networking
public void LoadClientPermissions() public void LoadClientPermissions()
{ {
if (!File.Exists(ClientPermissionsFile)) return; clientPermissions.Clear();
if (File.Exists("Data/clientpermissions.txt") && !File.Exists(ClientPermissionsFile))
{
LoadClientPermissionsOld("Data/clientpermissions.txt");
return;
}
XDocument doc = XMLExtensions.TryLoadXml(ClientPermissionsFile);
foreach (XElement clientElement in doc.Root.Elements())
{
string clientName = clientElement.GetAttributeString("name", "");
string clientIP = clientElement.GetAttributeString("ip", "");
if (string.IsNullOrWhiteSpace(clientName) || string.IsNullOrWhiteSpace(clientIP))
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - all clients must have a name and an IP address.");
continue;
}
string permissionsStr = clientElement.GetAttributeString("permissions", "");
ClientPermissions permissions;
if (!Enum.TryParse(permissionsStr, out permissions))
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + permissionsStr + "\" is not a valid client permission.");
continue;
}
List<DebugConsole.Command> permittedCommands = new List<DebugConsole.Command>();
if (permissions.HasFlag(ClientPermissions.ConsoleCommands))
{
foreach (XElement commandElement in clientElement.Elements())
{
if (commandElement.Name.ToString().ToLowerInvariant() != "command") continue;
string commandName = commandElement.GetAttributeString("name", "");
DebugConsole.Command command = DebugConsole.FindCommand(commandName);
if (command == null)
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + commandName + "\" is not a valid console command.");
continue;
}
permittedCommands.Add(command);
}
}
clientPermissions.Add(new SavedClientPermission(clientName, clientIP, permissions, permittedCommands));
}
}
/// <summary>
/// Method for loading old .txt client permission files to provide backwards compatibility
/// </summary>
private void LoadClientPermissionsOld(string file)
{
if (!File.Exists(file)) return;
string[] lines; string[] lines;
try try
{ {
lines = File.ReadAllLines(ClientPermissionsFile); lines = File.ReadAllLines(file);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -337,36 +416,63 @@ namespace Barotrauma.Networking
} }
clientPermissions.Clear(); clientPermissions.Clear();
foreach (string line in lines) foreach (string line in lines)
{ {
string[] separatedLine = line.Split('|'); string[] separatedLine = line.Split('|');
if (separatedLine.Length < 3) continue; if (separatedLine.Length < 3) continue;
string name = String.Join("|", separatedLine.Take(separatedLine.Length - 2)); string name = string.Join("|", separatedLine.Take(separatedLine.Length - 2));
string ip = separatedLine[separatedLine.Length - 2]; string ip = separatedLine[separatedLine.Length - 2];
ClientPermissions permissions = ClientPermissions.None; ClientPermissions permissions = ClientPermissions.None;
if (Enum.TryParse<ClientPermissions>(separatedLine.Last(), out permissions)) if (Enum.TryParse(separatedLine.Last(), out permissions))
{ {
clientPermissions.Add(new SavedClientPermission(name, ip, permissions)); clientPermissions.Add(new SavedClientPermission(name, ip, permissions, new List<DebugConsole.Command>()));
} }
} }
} }
public void SaveClientPermissions() public void SaveClientPermissions()
{ {
GameServer.Log("Saving client permissions", ServerLog.MessageType.ServerMessage); //delete old client permission file
if (File.Exists("Data/clientpermissions.txt"))
{
File.Delete("Data/clientpermissions.txt");
}
List<string> lines = new List<string>(); Log("Saving client permissions", ServerLog.MessageType.ServerMessage);
XDocument doc = new XDocument(new XElement("ClientPermissions"));
foreach (SavedClientPermission clientPermission in clientPermissions) foreach (SavedClientPermission clientPermission in clientPermissions)
{ {
lines.Add(clientPermission.Name + "|" + clientPermission.IP+"|"+clientPermission.Permissions.ToString()); XElement clientElement = new XElement("Client",
new XAttribute("name", clientPermission.Name),
new XAttribute("ip", clientPermission.IP),
new XAttribute("permissions", clientPermission.Permissions.ToString()));
if (clientPermission.Permissions.HasFlag(ClientPermissions.ConsoleCommands))
{
foreach (DebugConsole.Command command in clientPermission.PermittedCommands)
{
clientElement.Add(new XElement("command", new XAttribute("name", command.names[0])));
}
}
doc.Root.Add(clientElement);
} }
try try
{ {
File.WriteAllLines(ClientPermissionsFile, lines); XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.NewLineOnAttributes = true;
using (var writer = XmlWriter.Create(ClientPermissionsFile, settings))
{
doc.Save(writer);
}
} }
catch (Exception e) catch (Exception e)
{ {
@@ -28,18 +28,20 @@ namespace Barotrauma.Networking
Attack, Attack,
Spawning, Spawning,
ServerMessage, ServerMessage,
ConsoleUsage,
Error Error
} }
private readonly Color[] messageColor = private readonly Color[] messageColor =
{ {
Color.LightBlue, Color.LightBlue, //Chat
new Color(255, 142, 0), new Color(255, 142, 0), //ItemInteraction
new Color(238, 208, 0), new Color(238, 208, 0), //Inventory
new Color(204, 74, 78), new Color(204, 74, 78), //Attack
new Color(163, 73, 164), new Color(163, 73, 164), //Spawning
new Color(157, 225, 160), new Color(157, 225, 160), //ServerMessage
Color.Red new Color(0, 162, 232), //ConsoleUsage
Color.Red //Error
}; };
private readonly string[] messageTypeName = private readonly string[] messageTypeName =
@@ -50,6 +52,7 @@ namespace Barotrauma.Networking
"Attack & death", "Attack & death",
"Spawning", "Spawning",
"Server message", "Server message",
"Console usage",
"Error" "Error"
}; };
@@ -15,11 +15,15 @@
allowspectating="True" allowspectating="True"
endroundatlevelend="True" endroundatlevelend="True"
saveserverlogs="True" saveserverlogs="True"
allowragdollbutton="True"
allowfiletransfers="True" allowfiletransfers="True"
allowrespawn="True" allowrespawn="True"
allowvotekick="True" allowvotekick="True"
endvoterequiredratio="0.6" endvoterequiredratio="0.6"
kickvoterequiredratio="0.6" kickvoterequiredratio="0.6"
traitoruseratio="True"
traitorratio="0.2"
karmaenabled="False"
SubSelection="Manual" SubSelection="Manual"
ModeSelection="Manual" ModeSelection="Manual"
TraitorsEnabled="No" TraitorsEnabled="No"