(bf212a41f) v0.9.2.0 pre-release test version

This commit is contained in:
Joonas Rikkonen
2019-07-27 21:06:07 +03:00
parent afa2137bd2
commit 0f63da27b2
154 changed files with 3959 additions and 1428 deletions
+6 -3
View File
@@ -12,11 +12,11 @@ bld/
[Dd]ebug*/ [Dd]ebug*/
[Rr]elease*/ [Rr]elease*/
# Barotrauma content folder # Misc vs crap
BarotraumaShared/Content/
*.v12.suo *.v12.suo
*.suo *.suo
*.csproj.user
*.shproj.user
#performance reports & sessions #performance reports & sessions
*.vsp *.vsp
@@ -25,3 +25,6 @@ BarotraumaShared/Content/
# Mac # Mac
*.DS_Store *.DS_Store
#Merge script
temp.txt
@@ -160,6 +160,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\EntitySpawner.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\EntitySpawner.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\FileTransfer\FileReceiver.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\FileTransfer\FileReceiver.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\GameClient.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\GameClient.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\KarmaManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetEntityEvent\ClientEntityEventManager.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetEntityEvent\ClientEntityEventManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetEntityEvent\NetEntityEvent.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetEntityEvent\NetEntityEvent.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetStats.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetStats.cs" />
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.1.0")] [assembly: AssemblyVersion("0.9.200.0")]
[assembly: AssemblyFileVersion("0.9.1.0")] [assembly: AssemblyFileVersion("0.9.1.0")]
+17 -1
View File
@@ -1,4 +1,6 @@
using Microsoft.Xna.Framework; using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using System; using System;
@@ -171,6 +173,11 @@ namespace Barotrauma
get { return targetPos; } get { return targetPos; }
set { targetPos = value; } set { targetPos = value; }
} }
public Vector2 GetPosition()
{
return position;
}
// Auxiliary function to move the camera // Auxiliary function to move the camera
public void Translate(Vector2 amount) public void Translate(Vector2 amount)
@@ -178,6 +185,15 @@ namespace Barotrauma
position += amount; position += amount;
} }
public void ClientWrite(NetOutgoingMessage msg)
{
if (Character.Controlled != null && !Character.Controlled.IsDead) return;
msg.Write((byte)ClientNetObject.SPECTATING_POS);
msg.Write(position.X);
msg.Write(position.Y);
}
private void CreateMatrices() private void CreateMatrices()
{ {
resolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); resolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
@@ -14,6 +14,13 @@ namespace Barotrauma
{ {
if (this != Controlled) if (this != Controlled)
{ {
if (GameMain.Client.EndCinematic != null) // Freezes the characters during the ending cinematic
{
AnimController.Frozen = true;
memState.Clear();
return;
}
//freeze AI characters if more than 1 seconds have passed since last update from the server //freeze AI characters if more than 1 seconds have passed since last update from the server
if (lastRecvPositionUpdateTime < NetTime.Now - 1.0f) if (lastRecvPositionUpdateTime < NetTime.Now - 1.0f)
{ {
@@ -128,6 +128,7 @@ namespace Barotrauma
} }
public WearableSprite HuskSprite { get; private set; } public WearableSprite HuskSprite { get; private set; }
public WearableSprite HerpesSprite { get; private set; }
public void LoadHuskSprite() public void LoadHuskSprite()
{ {
@@ -139,6 +140,16 @@ namespace Barotrauma
HuskSprite = new WearableSprite(element.Element("sprite"), WearableType.Husk); HuskSprite = new WearableSprite(element.Element("sprite"), WearableType.Husk);
} }
} }
public void LoadHerpesSprite()
{
var info = character.Info;
if (info == null) { return; }
var element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), WearableType.Herpes).FirstOrDefault();
if (element != null)
{
HerpesSprite = new WearableSprite(element.Element("sprite"), WearableType.Herpes);
}
}
public float TextureScale => limbParams.Ragdoll.TextureScale; public float TextureScale => limbParams.Ragdoll.TextureScale;
@@ -387,7 +398,7 @@ namespace Barotrauma
color *= SeveredFadeOutTime - severedFadeOutTimer; color *= SeveredFadeOutTime - severedFadeOutTimer;
} }
} }
body.Dir = Dir; body.Dir = Dir;
bool hideLimb = wearingItems.Any(w => w != null && w.HideLimb); bool hideLimb = wearingItems.Any(w => w != null && w.HideLimb);
@@ -425,10 +436,20 @@ namespace Barotrauma
SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally; SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
if (onlyDrawable == null) if (onlyDrawable == null)
{ {
if (HerpesSprite != null)
{
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
if (herpesStrength > 0.0f)
{
DrawWearable(HerpesSprite, depthStep, spriteBatch, color * (herpesStrength / 100.0f), spriteEffect);
depthStep += 0.000001f;
}
}
if (HuskSprite != null && (character.SpeciesName == "Humanhusk" || (character.SpeciesName == "Human" && if (HuskSprite != null && (character.SpeciesName == "Humanhusk" || (character.SpeciesName == "Human" &&
character.CharacterHealth.GetAffliction<AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active))) character.CharacterHealth.GetAffliction<AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active)))
{ {
DrawWearable(HuskSprite, depthStep, spriteBatch, color, spriteEffect); DrawWearable(HuskSprite, depthStep, spriteBatch, color, spriteEffect);
depthStep += 0.000001f;
} }
foreach (WearableSprite wearable in OtherWearables) foreach (WearableSprite wearable in OtherWearables)
{ {
@@ -585,6 +606,9 @@ namespace Barotrauma
HuskSprite?.Sprite.Remove(); HuskSprite?.Sprite.Remove();
HuskSprite = null; HuskSprite = null;
HerpesSprite?.Sprite.Remove();
HerpesSprite = null;
} }
} }
} }
@@ -133,17 +133,7 @@ namespace Barotrauma
if (PlayerInput.KeyHit(Keys.F3)) if (PlayerInput.KeyHit(Keys.F3))
{ {
isOpen = !isOpen; Toggle();
if (isOpen)
{
textBox.Select();
AddToGUIUpdateList();
}
else
{
GUI.ForceMouseOn(null);
textBox.Deselect();
}
} }
else if (isOpen && PlayerInput.KeyHit(Keys.Escape)) else if (isOpen && PlayerInput.KeyHit(Keys.Escape))
{ {
@@ -179,6 +169,21 @@ namespace Barotrauma
} }
} }
public static void Toggle()
{
isOpen = !isOpen;
if (isOpen)
{
textBox.Select();
AddToGUIUpdateList();
}
else
{
GUI.ForceMouseOn(null);
textBox.Deselect();
}
}
public static void Draw(SpriteBatch spriteBatch) public static void Draw(SpriteBatch spriteBatch)
{ {
if (!isOpen) return; if (!isOpen) return;
@@ -298,9 +303,16 @@ namespace Barotrauma
private static void AssignOnClientExecute(string names, Action<string[]> onClientExecute) private static void AssignOnClientExecute(string names, Action<string[]> onClientExecute)
{ {
Command command = commands.First(c => c.names.Intersect(names.Split('|')).Count() > 0); Command command = commands.Find(c => c.names.Intersect(names.Split('|')).Count() > 0);
command.OnClientExecute = onClientExecute; if (command == null)
command.RelayToServer = false; {
throw new Exception("AssignOnClientExecute failed. Command matching the name(s) \"" + names + "\" not found.");
}
else
{
command.OnClientExecute = onClientExecute;
command.RelayToServer = false;
}
} }
private static void AssignRelayToServer(string names, bool relay) private static void AssignRelayToServer(string names, bool relay)
@@ -1797,6 +1809,11 @@ namespace Barotrauma
limb.HuskSprite.Sprite.ReloadXML(); limb.HuskSprite.Sprite.ReloadXML();
limb.HuskSprite.Sprite.ReloadTexture(); limb.HuskSprite.Sprite.ReloadTexture();
} }
if (limb.HerpesSprite != null)
{
limb.HerpesSprite.Sprite.ReloadXML();
limb.HerpesSprite.Sprite.ReloadTexture();
}
} }
} }
@@ -278,7 +278,7 @@ namespace Barotrauma
Color.White, Color.Black * 0.5f, 0, SmallFont); Color.White, Color.Black * 0.5f, 0, SmallFont);
DrawString(spriteBatch, new Vector2(10, 40), DrawString(spriteBatch, new Vector2(10, 40),
"Bodies: " + GameMain.World.BodyList.Count + " (" + GameMain.World.BodyList.FindAll(b => b.Awake && b.Enabled).Count + " awake)", $"Bodies: {GameMain.World.BodyList.Count} ({GameMain.World.BodyList.FindAll(b => b.Awake && b.Enabled).Count} awake, {GameMain.World.BodyList.FindAll(b => b.Awake && b.BodyType == FarseerPhysics.Dynamics.BodyType.Dynamic && b.Enabled).Count} dynamic)",
Color.White, Color.Black * 0.5f, 0, SmallFont); Color.White, Color.Black * 0.5f, 0, SmallFont);
if (Screen.Selected.Cam != null) if (Screen.Selected.Cam != null)
@@ -1040,6 +1040,8 @@ namespace Barotrauma
public static Texture2D CreateRectangle(int width, int height) public static Texture2D CreateRectangle(int width, int height)
{ {
width = Math.Max(width, 1);
height = Math.Max(height, 1);
Color[] data = new Color[width * height]; Color[] data = new Color[width * height];
for (int i = 0; i < data.Length; i++) for (int i = 0; i < data.Length; i++)
@@ -1057,7 +1059,6 @@ namespace Barotrauma
TrySetArray(data, (height - 1) * width + x, Color.White); TrySetArray(data, (height - 1) * width + x, Color.White);
} }
Texture2D texture = null; Texture2D texture = null;
CrossThread.RequestExecutionOnMainThread(() => CrossThread.RequestExecutionOnMainThread(() =>
{ {
@@ -178,25 +178,27 @@ namespace Barotrauma
} }
else if (DrawLoadingText) else if (DrawLoadingText)
{ {
string loadText = ""; if (TextManager.Initialized)
if (LoadState == 100.0f)
{ {
loadText = TextManager.Get("PressAnyKey"); string loadText;
} if (LoadState == 100.0f)
else
{
loadText = TextManager.Get("Loading");
if (LoadState != null)
{ {
loadText += " " + (int)LoadState + " %"; loadText = TextManager.Get("PressAnyKey");
}
else
{
loadText = TextManager.Get("Loading");
if (LoadState != null)
{
loadText += " " + (int)LoadState + " %";
}
}
if (GUI.LargeFont != null)
{
GUI.LargeFont.DrawString(spriteBatch, loadText.ToUpper(),
new Vector2(GameMain.GraphicsWidth / 2.0f - GUI.LargeFont.MeasureString(loadText).X / 2.0f, GameMain.GraphicsHeight * 0.7f),
Color.White);
} }
}
if (GUI.LargeFont != null)
{
GUI.LargeFont.DrawString(spriteBatch, loadText.ToUpper(),
new Vector2(GameMain.GraphicsWidth / 2.0f - GUI.LargeFont.MeasureString(loadText).X / 2.0f, GameMain.GraphicsHeight * 0.7f),
Color.White);
} }
if (GUI.Font != null && selectedTip != null) if (GUI.Font != null && selectedTip != null)
@@ -58,6 +58,8 @@ namespace Barotrauma
public event Action<SpriteBatch, float> PreDraw; public event Action<SpriteBatch, float> PreDraw;
public event Action<SpriteBatch, float> PostDraw; public event Action<SpriteBatch, float> PostDraw;
public bool RequireMouseOn = true;
public Action refresh; public Action refresh;
public object data; public object data;
@@ -109,7 +111,7 @@ namespace Barotrauma
{ {
PreUpdate?.Invoke(deltaTime); PreUpdate?.Invoke(deltaTime);
if (!enabled) { return; } if (!enabled) { return; }
if (IsMouseOver) if (IsMouseOver || (!RequireMouseOn && selectedWidgets.Contains(this) && PlayerInput.LeftButtonHeld()))
{ {
Hovered?.Invoke(); Hovered?.Invoke();
if ((multiselect && !selectedWidgets.Contains(this)) || selectedWidgets.None()) if ((multiselect && !selectedWidgets.Contains(this)) || selectedWidgets.None())
+70 -55
View File
@@ -404,54 +404,13 @@ namespace Barotrauma
DebugConsole.Log("Selected content packages: " + string.Join(", ", SelectedPackages.Select(cp => cp.Name))); DebugConsole.Log("Selected content packages: " + string.Join(", ", SelectedPackages.Select(cp => cp.Name)));
} }
/*#if DEBUG #if DEBUG
GameSettings.ShowUserStatisticsPrompt = false; GameSettings.ShowUserStatisticsPrompt = false;
GameSettings.SendUserStatistics = false; GameSettings.SendUserStatistics = false;
#endif*/ #endif
InitUserStats(); InitUserStats();
yield return CoroutineStatus.Running;
LightManager = new Lights.LightManager(base.GraphicsDevice, Content);
TitleScreen.LoadState = 1.0f;
yield return CoroutineStatus.Running;
GUI.LoadContent();
TitleScreen.LoadState = 2.0f;
yield return CoroutineStatus.Running;
MissionPrefab.Init();
MapEntityPrefab.Init();
Tutorials.Tutorial.Init();
MapGenerationParams.Init();
LevelGenerationParams.LoadPresets();
ScriptedEventSet.LoadPrefabs();
AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions));
TitleScreen.LoadState = 10.0f;
yield return CoroutineStatus.Running;
StructurePrefab.LoadAll(GetFilesOfType(ContentType.Structure));
TitleScreen.LoadState = 15.0f;
yield return CoroutineStatus.Running;
ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item));
TitleScreen.LoadState = 25.0f;
yield return CoroutineStatus.Running;
JobPrefab.LoadAll(GetFilesOfType(ContentType.Jobs));
// Add any missing jobs from the prefab into Config.JobNamePreferences.
foreach (JobPrefab job in JobPrefab.List)
{
if (!Config.JobPreferences.Contains(job.Identifier)) { Config.JobPreferences.Add(job.Identifier); }
}
NPCConversation.LoadAll(GetFilesOfType(ContentType.NPCConversations));
ItemAssemblyPrefab.LoadAll();
TitleScreen.LoadState = 30.0f;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
Debug.WriteLine("sounds"); Debug.WriteLine("sounds");
@@ -464,55 +423,105 @@ namespace Barotrauma
i++; i++;
TitleScreen.LoadState = SoundPlayer.SoundCount == 0 ? TitleScreen.LoadState = SoundPlayer.SoundCount == 0 ?
30.0f : 1.0f :
Math.Min(30.0f + 40.0f * i / Math.Max(SoundPlayer.SoundCount, 1), 70.0f); Math.Min(40.0f * i / Math.Max(SoundPlayer.SoundCount, 1), 40.0f);
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
TitleScreen.LoadState = 70.0f; TitleScreen.LoadState = 40.0f;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
LightManager = new Lights.LightManager(base.GraphicsDevice, Content);
TitleScreen.LoadState = 41.0f;
yield return CoroutineStatus.Running;
GUI.LoadContent();
TitleScreen.LoadState = 42.0f;
yield return CoroutineStatus.Running;
MissionPrefab.Init();
MapEntityPrefab.Init();
Tutorials.Tutorial.Init();
MapGenerationParams.Init();
LevelGenerationParams.LoadPresets();
ScriptedEventSet.LoadPrefabs();
AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions));
TitleScreen.LoadState = 50.0f;
yield return CoroutineStatus.Running;
StructurePrefab.LoadAll(GetFilesOfType(ContentType.Structure));
TitleScreen.LoadState = 53.0f;
yield return CoroutineStatus.Running;
ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item));
TitleScreen.LoadState = 55.0f;
yield return CoroutineStatus.Running;
JobPrefab.LoadAll(GetFilesOfType(ContentType.Jobs));
// Add any missing jobs from the prefab into Config.JobNamePreferences.
foreach (JobPrefab job in JobPrefab.List)
{
if (!Config.JobPreferences.Contains(job.Identifier)) { Config.JobPreferences.Add(job.Identifier); }
}
NPCConversation.LoadAll(GetFilesOfType(ContentType.NPCConversations));
ItemAssemblyPrefab.LoadAll();
TitleScreen.LoadState = 60.0f;
yield return CoroutineStatus.Running;
GameModePreset.Init(); GameModePreset.Init();
Submarine.RefreshSavedSubs(); Submarine.RefreshSavedSubs();
TitleScreen.LoadState = 80.0f; TitleScreen.LoadState = 65.0f;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
GameScreen = new GameScreen(GraphicsDeviceManager.GraphicsDevice, Content); GameScreen = new GameScreen(GraphicsDeviceManager.GraphicsDevice, Content);
TitleScreen.LoadState = 90.0f; TitleScreen.LoadState = 68.0f;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
MainMenuScreen = new MainMenuScreen(this); MainMenuScreen = new MainMenuScreen(this);
LobbyScreen = new LobbyScreen(); LobbyScreen = new LobbyScreen();
ServerListScreen = new ServerListScreen(); ServerListScreen = new ServerListScreen();
TitleScreen.LoadState = 70.0f;
yield return CoroutineStatus.Running;
if (SteamManager.USE_STEAM) if (SteamManager.USE_STEAM)
{ {
SteamWorkshopScreen = new SteamWorkshopScreen(); SteamWorkshopScreen = new SteamWorkshopScreen();
} }
SubEditorScreen = new SubEditorScreen(); SubEditorScreen = new SubEditorScreen();
TitleScreen.LoadState = 75.0f;
yield return CoroutineStatus.Running;
ParticleEditorScreen = new ParticleEditorScreen(); ParticleEditorScreen = new ParticleEditorScreen();
TitleScreen.LoadState = 80.0f;
yield return CoroutineStatus.Running;
LevelEditorScreen = new LevelEditorScreen(); LevelEditorScreen = new LevelEditorScreen();
SpriteEditorScreen = new SpriteEditorScreen(); SpriteEditorScreen = new SpriteEditorScreen();
CharacterEditorScreen = new CharacterEditorScreen(); CharacterEditorScreen = new CharacterEditorScreen();
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
TitleScreen.LoadState = 95.0f; TitleScreen.LoadState = 85.0f;
ParticleManager = new ParticleManager(GameScreen.Cam); ParticleManager = new ParticleManager(GameScreen.Cam);
ParticleManager.LoadPrefabs(); ParticleManager.LoadPrefabs();
TitleScreen.LoadState = 97.0f; TitleScreen.LoadState = 88.0f;
LevelObjectPrefab.LoadAll(); LevelObjectPrefab.LoadAll();
DecalManager = new DecalManager();
TitleScreen.LoadState = 99.0f; TitleScreen.LoadState = 90.0f;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
DecalManager = new DecalManager();
LocationType.Init(); LocationType.Init();
MainMenuScreen.Select(); MainMenuScreen.Select();
@@ -634,6 +643,7 @@ namespace Barotrauma
PlayerInput.Update(Timing.Step); PlayerInput.Update(Timing.Step);
if (loadingScreenOpen) if (loadingScreenOpen)
{ {
//reset accumulator if loading //reset accumulator if loading
@@ -641,6 +651,11 @@ namespace Barotrauma
// -> no pause caused by leftover time in the accumulator when starting a new shift // -> no pause caused by leftover time in the accumulator when starting a new shift
GameMain.ResetFrameTime(); GameMain.ResetFrameTime();
if (!TitleScreen.PlayingSplashScreen)
{
SoundPlayer.Update((float)Timing.Step);
}
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen &&
(!waitForKeyHit || ((PlayerInput.GetKeyboardState.GetPressedKeys().Length > 0 || PlayerInput.LeftButtonClicked()) && WindowActive))) (!waitForKeyHit || ((PlayerInput.GetKeyboardState.GetPressedKeys().Length > 0 || PlayerInput.LeftButtonClicked()) && WindowActive)))
{ {
@@ -455,10 +455,14 @@ namespace Barotrauma
isHorizontal: true, childAnchor: Anchor.CenterLeft) isHorizontal: true, childAnchor: Anchor.CenterLeft)
{ {
AbsoluteSpacing = (int)(10 * GUI.Scale), AbsoluteSpacing = (int)(10 * GUI.Scale),
UserData = "orderbuttons", UserData = "orderbuttons"
CanBeFocused = false
}; };
var spacer = new GUIFrame(new RectTransform(new Point(spacing, orderButtonFrame.Rect.Height), frame.RectTransform)
{
AbsoluteOffset = new Point(characterInfoWidth, 0)
});
//listbox for holding the orders inappropriate for this character //listbox for holding the orders inappropriate for this character
//(so we can easily toggle their visibility) //(so we can easily toggle their visibility)
var wrongOrderList = new GUIListBox(new RectTransform(new Point(50, orderButtonFrame.Rect.Height), orderButtonFrame.RectTransform), isHorizontal: true, style: null) var wrongOrderList = new GUIListBox(new RectTransform(new Point(50, orderButtonFrame.Rect.Height), orderButtonFrame.RectTransform), isHorizontal: true, style: null)
@@ -32,7 +32,6 @@ namespace Barotrauma.Tutorials
private LightComponent mechanic_thirdDoorLight; private LightComponent mechanic_thirdDoorLight;
private Structure mechanic_brokenWall_1; private Structure mechanic_brokenWall_1;
private Hull mechanic_brokenhull_1; private Hull mechanic_brokenhull_1;
private MotionSensor mechanic_ladderSensor;
// Room 4 // Room 4
private MotionSensor mechanic_craftingObjectiveSensor; private MotionSensor mechanic_craftingObjectiveSensor;
@@ -752,11 +752,11 @@ namespace Barotrauma
character.SelectedBy.Inventory != null) character.SelectedBy.Inventory != null)
{ {
//item is in the inventory of another character -> attempt to get the item from there //item is in the inventory of another character -> attempt to get the item from there
success = character.SelectedBy.Inventory.TryPutItem(item, Character.Controlled, item.AllowedSlots, true); success = character.SelectedBy.Inventory.TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true);
} }
break; break;
case QuickUseAction.TakeFromContainer: case QuickUseAction.TakeFromContainer:
success = TryPutItem(item, Character.Controlled, item.AllowedSlots, true); success = TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true);
break; break;
} }
@@ -182,7 +182,7 @@ namespace Barotrauma.Items.Components
} }
partial void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage) partial void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage, bool forcedOpen)
{ {
if (isStuck || if (isStuck ||
(PredictedState == null && isOpen == open) || (PredictedState == null && isOpen == open) ||
@@ -200,12 +200,16 @@ namespace Barotrauma.Items.Components
//sent by the server, or reverting it back to its old state if no msg from server was received //sent by the server, or reverting it back to its old state if no msg from server was received
PredictedState = open; PredictedState = open;
resetPredictionTimer = CorrectionDelay; resetPredictionTimer = CorrectionDelay;
if (stateChanged) PlaySound(ActionType.OnUse, item.WorldPosition); if (stateChanged) PlaySound(forcedOpen ? ActionType.OnPicked : ActionType.OnUse, item.WorldPosition);
} }
else else
{ {
isOpen = open; isOpen = open;
if (!isNetworkMessage || open != PredictedState) PlaySound(ActionType.OnUse, item.WorldPosition); if (!isNetworkMessage || open != PredictedState)
{
StopPicking(null);
PlaySound(forcedOpen ? ActionType.OnPicked : ActionType.OnUse, item.WorldPosition);
}
} }
//opening a partially stuck door makes it less stuck //opening a partially stuck door makes it less stuck
@@ -217,7 +221,9 @@ namespace Barotrauma.Items.Components
{ {
base.ClientRead(type, msg, sendingTime); base.ClientRead(type, msg, sendingTime);
SetState(msg.ReadBoolean(), isNetworkMessage: true, sendNetworkMessage: false); bool open = msg.ReadBoolean();
bool forcedOpen = msg.ReadBoolean();
SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen);
Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8); Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
PredictedState = null; PredictedState = null;
@@ -291,16 +291,16 @@ namespace Barotrauma.Items.Components
else else
{ {
float volume = GetSoundVolume(itemSound); float volume = GetSoundVolume(itemSound);
if (volume <= 0.0f) return; if (volume <= 0.0f) { return; }
SoundPlayer.PlaySound(itemSound.RoundSound.Sound, position, volume, itemSound.Range, item.CurrentHull); SoundPlayer.PlaySound(itemSound.RoundSound.Sound, position, volume, itemSound.Range, item.CurrentHull);
} }
} }
public void StopSounds(ActionType type) public void StopSounds(ActionType type)
{ {
if (loopingSound == null) return; if (loopingSound == null) { return; }
if (loopingSound.Type != type) return; if (loopingSound.Type != type) { return; }
if (loopingSoundChannel != null) if (loopingSoundChannel != null)
{ {
@@ -25,12 +25,12 @@ namespace Barotrauma.Items.Components
{ {
noPowerTip = TextManager.Get("SteeringNoPowerTip"); noPowerTip = TextManager.Get("SteeringNoPowerTip");
GuiFrame.RectTransform.RelativeOffset = new Vector2(0.4f, 0.0f); GuiFrame.RectTransform.RelativeOffset = new Vector2(0.05f, 0.0f);
new GUICustomComponent(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center), new GUICustomComponent(new RectTransform(new Vector2(0.95f, 0.9f), GuiFrame.RectTransform, Anchor.Center),
DrawHUDBack, null); DrawHUDBack, null);
submarineContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center), style: null); submarineContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.9f), GuiFrame.RectTransform, Anchor.Center), style: null);
new GUICustomComponent(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center), new GUICustomComponent(new RectTransform(new Vector2(0.95f, 0.9f), GuiFrame.RectTransform, Anchor.Center),
DrawHUDFront, null) DrawHUDFront, null)
{ {
CanBeFocused = false CanBeFocused = false
@@ -179,11 +179,21 @@ namespace Barotrauma.Items.Components
GetLinkedHulls(hull, hullData.LinkedHulls); GetLinkedHulls(hull, hullData.LinkedHulls);
hullDatas.Add(hull, hullData); hullDatas.Add(hull, hullData);
} }
Color neutralColor = Color.DarkCyan;
if (hull.RoomName != null)
{
if (hull.RoomName.Contains("ballast") || hull.RoomName.Contains("Ballast") ||
hull.RoomName.Contains("airlock") || hull.RoomName.Contains("Airlock"))
{
neutralColor = new Color(9, 80, 159);
}
}
if (hullData.Distort) if (hullData.Distort)
{ {
hullFrame.Children.First().Color = Color.Lerp(Color.Black, Color.DarkGray * 0.5f, Rand.Range(0.0f, 1.0f)); hullFrame.Children.First().Color = Color.Lerp(Color.Black, Color.DarkGray * 0.5f, Rand.Range(0.0f, 1.0f));
hullFrame.Color = Color.DarkGray * 0.5f; hullFrame.Color = neutralColor * 0.5f;
continue; continue;
} }
@@ -192,20 +202,23 @@ namespace Barotrauma.Items.Components
hullFrame.Parent.Rect.Width / (float)hull.Submarine.Borders.Width, hullFrame.Parent.Rect.Width / (float)hull.Submarine.Borders.Width,
hullFrame.Parent.Rect.Height / (float)hull.Submarine.Borders.Height); hullFrame.Parent.Rect.Height / (float)hull.Submarine.Borders.Height);
Color borderColor = Color.DarkCyan; Color borderColor = neutralColor;
float? gapOpenSum = 0.0f; float? gapOpenSum = 0.0f;
if (ShowHullIntegrity) if (ShowHullIntegrity)
{ {
gapOpenSum = hull.ConnectedGaps.Where(g => !g.IsRoomToRoom).Sum(g => g.Open); gapOpenSum = hull.ConnectedGaps.Where(g => !g.IsRoomToRoom).Sum(g => g.Open);
borderColor = Color.Lerp(Color.DarkCyan, Color.Red, Math.Min((float)gapOpenSum, 1.0f)); borderColor = Color.Lerp(neutralColor, Color.Red, Math.Min((float)gapOpenSum, 1.0f));
} }
float? oxygenAmount = null; float? oxygenAmount = null;
if (!RequireOxygenDetectors || hullData?.Oxygen != null) if (!RequireOxygenDetectors || hullData?.Oxygen != null)
{ {
oxygenAmount = RequireOxygenDetectors ? hullData.Oxygen : hull.OxygenPercentage; oxygenAmount = RequireOxygenDetectors ? hullData.Oxygen : hull.OxygenPercentage;
GUI.DrawRectangle(spriteBatch, hullFrame.Rect, Color.Lerp(Color.Red * 0.5f, Color.Green * 0.3f, (float)oxygenAmount / 100.0f), true); GUI.DrawRectangle(
spriteBatch, hullFrame.Rect,
Color.Lerp(Color.Red * 0.5f, Color.Green * 0.3f, (float)oxygenAmount / 100.0f),
true);
} }
float? waterAmount = null; float? waterAmount = null;
@@ -234,7 +247,7 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
hullFrame.Children.First().Color = Color.DarkCyan * 0.8f; hullFrame.Children.First().Color = neutralColor * 0.8f;
} }
if (mouseOnHull == hull) if (mouseOnHull == hull)
@@ -291,8 +304,6 @@ namespace Barotrauma.Items.Components
GUI.DrawLine(spriteBatch, center + start, center + end, Color.DarkCyan * Rand.Range(0.3f, 0.35f), width: (int)(10 * GUI.Scale)); GUI.DrawLine(spriteBatch, center + start, center + end, Color.DarkCyan * Rand.Range(0.3f, 0.35f), width: (int)(10 * GUI.Scale));
} }
} }
} }
private void GetLinkedHulls(Hull hull, List<Hull> linkedHulls) private void GetLinkedHulls(Hull hull, List<Hull> linkedHulls)
@@ -58,6 +58,10 @@ namespace Barotrauma.Items.Components
private GUIFrame inventoryContainer; private GUIFrame inventoryContainer;
private GUIComponent leftHUDColumn; private GUIComponent leftHUDColumn;
private GUIComponent midHUDColumn;
private GUIComponent rightHUDColumn;
private GUILayoutGroup sliderControlsContainer;
private Dictionary<string, GUIButton> warningButtons = new Dictionary<string, GUIButton>(); private Dictionary<string, GUIButton> warningButtons = new Dictionary<string, GUIButton>();
@@ -107,11 +111,13 @@ namespace Barotrauma.Items.Components
RelativeSpacing = 0.012f, RelativeSpacing = 0.012f,
Stretch = true Stretch = true
}; };
GUIFrame columnLeft = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f), paddedFrame.RectTransform), style: null); GUIFrame columnLeft = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f), paddedFrame.RectTransform), style: null);
leftHUDColumn = columnLeft;
GUIFrame columnMid = new GUIFrame(new RectTransform(new Vector2(0.45f, 1.0f), paddedFrame.RectTransform), style: null); GUIFrame columnMid = new GUIFrame(new RectTransform(new Vector2(0.45f, 1.0f), paddedFrame.RectTransform), style: null);
GUIFrame columnRight = new GUIFrame(new RectTransform(new Vector2(0.3f, 1.0f), paddedFrame.RectTransform), style: null); GUIFrame columnRight = new GUIFrame(new RectTransform(new Vector2(0.3f, 1.0f), paddedFrame.RectTransform), style: null);
leftHUDColumn = columnLeft;
midHUDColumn = columnMid;
rightHUDColumn = columnRight;
//---------------------------------------------------------- //----------------------------------------------------------
//left column //left column
@@ -180,9 +186,16 @@ namespace Barotrauma.Items.Components
ToolTip = TextManager.Get("ReactorTipTurbineOutput") ToolTip = TextManager.Get("ReactorTipTurbineOutput")
}; };
new GUITextBlock(new RectTransform(new Point(0, (int)(20 * GUI.Scale)), columnMid.RectTransform, Anchor.BottomLeft) { AbsoluteOffset = new Point(0, (int)(90 * GUI.Scale)) }, GUILayoutGroup sliderControls = new GUILayoutGroup(new RectTransform(new Point(columnMid.Rect.Width, (int)(114 * GUI.Scale)), columnMid.RectTransform, Anchor.BottomCenter))
{
Stretch = true,
AbsoluteSpacing = (int)(5 * GUI.Scale)
};
sliderControlsContainer = sliderControls;
new GUITextBlock(new RectTransform(new Point(0, (int)(20 * GUI.Scale)), sliderControls.RectTransform, Anchor.TopLeft),
TextManager.Get("ReactorFissionRate")); TextManager.Get("ReactorFissionRate"));
fissionRateScrollBar = new GUIScrollBar(new RectTransform(new Point(columnMid.Rect.Width, (int)(30 * GUI.Scale)), columnMid.RectTransform, Anchor.BottomCenter) { AbsoluteOffset = new Point(0, (int)(60 * GUI.Scale)) }, fissionRateScrollBar = new GUIScrollBar(new RectTransform(new Point(sliderControls.Rect.Width, (int)(30 * GUI.Scale)), sliderControls.RectTransform, Anchor.TopCenter),
style: "GUISlider", barSize: 0.1f) style: "GUISlider", barSize: 0.1f)
{ {
OnMoved = (GUIScrollBar bar, float scrollAmount) => OnMoved = (GUIScrollBar bar, float scrollAmount) =>
@@ -194,10 +207,10 @@ namespace Barotrauma.Items.Components
return false; return false;
} }
}; };
new GUITextBlock(new RectTransform(new Point(0, (int)(20 * GUI.Scale)), columnMid.RectTransform, Anchor.BottomLeft) { AbsoluteOffset = new Point(0, (int)(30 * GUI.Scale)) }, new GUITextBlock(new RectTransform(new Point(0, (int)(20 * GUI.Scale)), sliderControls.RectTransform, Anchor.BottomLeft),
TextManager.Get("ReactorTurbineOutput")); TextManager.Get("ReactorTurbineOutput"));
turbineOutputScrollBar = new GUIScrollBar(new RectTransform(new Point(columnMid.Rect.Width, (int)(30 * GUI.Scale)), columnMid.RectTransform, Anchor.BottomCenter), turbineOutputScrollBar = new GUIScrollBar(new RectTransform(new Point(sliderControls.Rect.Width, (int)(30 * GUI.Scale)), sliderControls.RectTransform, Anchor.BottomCenter),
style: "GUISlider", barSize: 0.1f, isHorizontal: true) style: "GUISlider", barSize: 0.1f, isHorizontal: true)
{ {
OnMoved = (GUIScrollBar bar, float scrollAmount) => OnMoved = (GUIScrollBar bar, float scrollAmount) =>
@@ -263,11 +276,11 @@ namespace Barotrauma.Items.Components
"Load", textColor: Color.LightBlue, textAlignment: Alignment.CenterLeft) "Load", textColor: Color.LightBlue, textAlignment: Alignment.CenterLeft)
{ {
ToolTip = TextManager.Get("ReactorTipLoad") ToolTip = TextManager.Get("ReactorTipLoad")
}; };
string loadStr = TextManager.Get("ReactorLoad"); string loadStr = TextManager.Get("ReactorLoad");
loadText.TextGetter += () => { return loadStr.Replace("[kw]", ((int)load).ToString()); }; loadText.TextGetter += () => { return loadStr.Replace("[kw]", ((int)load).ToString()); };
var outputText = new GUITextBlock(new RectTransform(textSize, graphArea.RectTransform, Anchor.BottomLeft, Pivot.TopLeft), var outputText = new GUITextBlock(new RectTransform(textSize, graphArea.RectTransform, Anchor.BottomLeft, Pivot.TopLeft),
"Output", textColor: Color.LightGreen, textAlignment: Alignment.CenterLeft) "Output", textColor: Color.LightGreen, textAlignment: Alignment.CenterLeft)
{ {
ToolTip = TextManager.Get("ReactorTipPower") ToolTip = TextManager.Get("ReactorTipPower")
@@ -321,9 +334,9 @@ namespace Barotrauma.Items.Components
if (temperature > optimalTemperature.Y) if (temperature > optimalTemperature.Y)
{ {
GUI.DrawRectangle(spriteBatch, GUI.DrawRectangle(spriteBatch,
new Vector2(graphArea.X - 30, graphArea.Y), new Vector2(graphArea.X - 30, graphArea.Y),
new Vector2(tempMeterFrame.SourceRect.Width, (graphArea.Bottom - graphArea.Height * optimalTemperature.Y / 100.0f) - graphArea.Y), new Vector2(tempMeterFrame.SourceRect.Width, (graphArea.Bottom - graphArea.Height * optimalTemperature.Y / 100.0f) - graphArea.Y),
Color.Red * (float)Math.Sin(Timing.TotalTime * 5.0f) * 0.7f, isFilled: true); Color.Red * (float)Math.Sin(Timing.TotalTime * 5.0f) * 0.7f, isFilled: true);
} }
if (temperature < optimalTemperature.X) if (temperature < optimalTemperature.X)
@@ -415,15 +428,52 @@ namespace Barotrauma.Items.Components
warningButtons["ReactorWarningLowFuel"].Selected = prevAvailableFuel < fissionRate && lightOn; warningButtons["ReactorWarningLowFuel"].Selected = prevAvailableFuel < fissionRate && lightOn;
warningButtons["ReactorWarningMeltdown"].Selected = meltDownTimer > MeltdownDelay * 0.5f || item.Condition == 0.0f && lightOn; warningButtons["ReactorWarningMeltdown"].Selected = meltDownTimer > MeltdownDelay * 0.5f || item.Condition == 0.0f && lightOn;
warningButtons["ReactorWarningSCRAM"].Selected = temperature > 0.1f && onOffSwitch.BarScroll > 0.5f; warningButtons["ReactorWarningSCRAM"].Selected = temperature > 0.1f && onOffSwitch.BarScroll > 0.5f;
AutoTemp = autoTempSlider.BarScroll < 0.5f; AutoTemp = autoTempSlider.BarScroll < 0.5f;
shutDown = onOffSwitch.BarScroll > 0.5f; shutDown = onOffSwitch.BarScroll > 0.5f;
if ((sliderControlsContainer.Rect.Contains(PlayerInput.MousePosition) || sliderControlsContainer.Children.Contains(GUIScrollBar.draggingBar)) &&
!PlayerInput.KeyDown(InputType.Deselect) && !PlayerInput.KeyHit(InputType.Deselect))
{
Character.DisableControls = true;
}
if (shutDown) if (shutDown)
{ {
fissionRateScrollBar.BarScroll = FissionRate / 100.0f; fissionRateScrollBar.BarScroll = FissionRate / 100.0f;
turbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f; turbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f;
} }
else if (!autoTemp && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null)
{
Vector2 input = Vector2.Zero;
float rate = 50.0f; //percentage per second
if (PlayerInput.KeyDown(InputType.Left)) input.X += -1.0f;
if (PlayerInput.KeyDown(InputType.Right)) input.X += 1.0f;
if (PlayerInput.KeyDown(InputType.Up)) input.Y += 1.0f;
if (PlayerInput.KeyDown(InputType.Down)) input.Y += -1.0f;
if (PlayerInput.KeyDown(InputType.Run)) rate = 200.0f;
rate *= deltaTime;
input.X *= rate;
input.Y *= rate;
if (input.LengthSquared() > 0)
{
LastUser = Character.Controlled;
unsentChanges = true;
if (input.X != 0.0f && GUIScrollBar.draggingBar != fissionRateScrollBar)
{
targetFissionRate = MathHelper.Clamp(targetFissionRate + input.X, 0.0f, 100.0f);
fissionRateScrollBar.BarScroll += input.X / 100.0f;
}
if (input.Y != 0.0f && GUIScrollBar.draggingBar != turbineOutputScrollBar)
{
targetTurbineOutput = MathHelper.Clamp(targetTurbineOutput + input.Y, 0.0f, 100.0f);
turbineOutputScrollBar.BarScroll += input.Y / 100.0f;
}
}
}
} }
private bool ToggleAutoTemp(GUITickBox tickBox) private bool ToggleAutoTemp(GUITickBox tickBox)
@@ -11,12 +11,6 @@ namespace Barotrauma.Items.Components
{ {
partial class Sonar : Powered, IServerSerializable, IClientSerializable partial class Sonar : Powered, IServerSerializable, IClientSerializable
{ {
enum Mode
{
Active,
Passive
};
private bool dynamicDockingIndicator = true; private bool dynamicDockingIndicator = true;
private bool unsentChanges; private bool unsentChanges;
@@ -43,8 +37,6 @@ namespace Barotrauma.Items.Components
private List<SonarBlip> sonarBlips; private List<SonarBlip> sonarBlips;
private float prevPingRadius;
private float prevPassivePingRadius; private float prevPassivePingRadius;
private Vector2 center; private Vector2 center;
@@ -102,7 +94,7 @@ namespace Barotrauma.Items.Components
ToolTip = TextManager.Get("SonarTipActive"), ToolTip = TextManager.Get("SonarTipActive"),
OnSelected = (GUITickBox box) => OnSelected = (GUITickBox box) =>
{ {
IsActive = box.Selected; CurrentMode = box.Selected ? Mode.Active : Mode.Passive;
if (GameMain.Client != null) if (GameMain.Client != null)
{ {
unsentChanges = true; unsentChanges = true;
@@ -262,7 +254,7 @@ namespace Barotrauma.Items.Components
{ {
if (component is GUITextBlock textBlock) if (component is GUITextBlock textBlock)
{ {
textBlock.TextColor = IsActive ? textBlock.Style.textColor : textBlock.Style.textColor * 0.5f; textBlock.TextColor = currentMode == Mode.Active ? textBlock.Style.textColor : textBlock.Style.textColor * 0.5f;
} }
} }
@@ -281,14 +273,21 @@ namespace Barotrauma.Items.Components
if (Level.Loaded != null) if (Level.Loaded != null)
{ {
Dictionary<LevelTrigger, Vector2> levelTriggerFlows = new Dictionary<LevelTrigger, Vector2>(); Dictionary<LevelTrigger, Vector2> levelTriggerFlows = new Dictionary<LevelTrigger, Vector2>();
foreach (LevelObject levelObject in Level.Loaded.LevelObjectManager.GetAllObjects(transducerCenter, range * pingState / zoom)) for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
{ {
//gather all nearby triggers that are causing the water to flow into the dictionary var activePing = activePings[pingIndex];
foreach (LevelTrigger trigger in levelObject.Triggers) foreach (LevelObject levelObject in Level.Loaded.LevelObjectManager.GetAllObjects(transducerCenter, range * activePing.State / zoom))
{ {
Vector2 flow = trigger.GetWaterFlowVelocity(); //gather all nearby triggers that are causing the water to flow into the dictionary
//ignore ones that are barely doing anything (flow^2 < 1) foreach (LevelTrigger trigger in levelObject.Triggers)
if (flow.LengthSquared() > 1.0f) levelTriggerFlows.Add(trigger, flow); {
Vector2 flow = trigger.GetWaterFlowVelocity();
//ignore ones that are barely doing anything (flow^2 < 1)
if (flow.LengthSquared() > 1.0f && !levelTriggerFlows.ContainsKey(trigger))
{
levelTriggerFlows.Add(trigger, flow);
}
}
} }
} }
@@ -350,13 +349,18 @@ namespace Barotrauma.Items.Components
prevDockingDist = float.MaxValue; prevDockingDist = float.MaxValue;
} }
if (IsActive) for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
{
var activePing = activePings[pingIndex];
float pingRadius = DisplayRadius * activePing.State / zoom;
UpdateDisruptions(transducerCenter, pingRadius / displayScale, activePing.PrevPingRadius / displayScale);
Ping(transducerCenter, transducerCenter,
pingRadius, activePing.PrevPingRadius, displayScale, range / zoom, passive: false, pingStrength: 2.0f);
activePing.PrevPingRadius = pingRadius;
}
if (currentMode == Mode.Active && currentPingIndex != -1)
{ {
float pingRadius = DisplayRadius * pingState / zoom;
UpdateDisruptions(transducerCenter, pingRadius / displayScale, prevPingRadius / displayScale);
Ping(transducerCenter, transducerCenter,
pingRadius, prevPingRadius, displayScale, range / zoom, passive: false, pingStrength: 2.0f);
prevPingRadius = pingRadius;
return; return;
} }
@@ -396,17 +400,18 @@ namespace Barotrauma.Items.Components
screenBackground.Draw(spriteBatch, center, 0.0f, rect.Width / screenBackground.size.X); screenBackground.Draw(spriteBatch, center, 0.0f, rect.Width / screenBackground.size.X);
} }
if (IsActive) if (currentMode == Mode.Active && currentPingIndex != -1)
{ {
if (isLastPingDirectional && directionalPingCircle != null) var activePing = activePings[currentPingIndex];
if (activePing.IsDirectional && directionalPingCircle != null)
{ {
directionalPingCircle.Draw(spriteBatch, center, Color.White * (1.0f - pingState), directionalPingCircle.Draw(spriteBatch, center, Color.White * (1.0f - activePing.State),
rotate: MathUtils.VectorToAngle(lastPingDirection), rotate: MathUtils.VectorToAngle(activePing.Direction),
scale: (DisplayRadius / directionalPingCircle.size.X) * pingState); scale: (DisplayRadius / directionalPingCircle.size.X) * activePing.State);
} }
else else
{ {
pingCircle.Draw(spriteBatch, center, Color.White * (1.0f - pingState), 0.0f, (DisplayRadius * 2 / pingCircle.size.X) * pingState); pingCircle.Draw(spriteBatch, center, Color.White * (1.0f - activePing.State), 0.0f, (DisplayRadius * 2 / pingCircle.size.X) * activePing.State);
} }
} }
@@ -448,7 +453,7 @@ namespace Barotrauma.Items.Components
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
} }
float directionalPingVisibility = useDirectionalPing && IsActive ? 1.0f : showDirectionalIndicatorTimer; float directionalPingVisibility = useDirectionalPing && currentMode == Mode.Active ? 1.0f : showDirectionalIndicatorTimer;
if (directionalPingVisibility > 0.0f) if (directionalPingVisibility > 0.0f)
{ {
Vector2 sector1 = MathUtils.RotatePointAroundTarget(pingDirection * DisplayRadius, Vector2.Zero, DirectionalPingSector * 0.5f); Vector2 sector1 = MathUtils.RotatePointAroundTarget(pingDirection * DisplayRadius, Vector2.Zero, DirectionalPingSector * 0.5f);
@@ -753,24 +758,47 @@ namespace Barotrauma.Items.Components
disruptedDirections.Clear(); disruptedDirections.Clear();
if (Level.Loaded == null) { return; } if (Level.Loaded == null) { return; }
foreach (LevelObject levelObject in Level.Loaded.LevelObjectManager.GetAllObjects(pingSource, range * pingState)) for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
{ {
if (levelObject.ActivePrefab?.SonarDisruption <= 0.0f) { continue; } var activePing = activePings[pingIndex];
foreach (LevelObject levelObject in Level.Loaded.LevelObjectManager.GetAllObjects(pingSource, range * activePing.State))
float disruptionStrength = levelObject.ActivePrefab.SonarDisruption;
Vector2 disruptionPos = new Vector2(levelObject.Position.X, levelObject.Position.Y);
float disruptionDist = Vector2.Distance(pingSource, disruptionPos);
disruptedDirections.Add(new Pair<Vector2, float>((disruptionPos - pingSource) / disruptionDist, disruptionStrength));
if (disruptionDist > worldPrevPingRadius && disruptionDist <= worldPingRadius)
{ {
for (int i = 0; i < disruptionStrength * Level.GridCellSize * 0.02f; i++) if (levelObject.ActivePrefab?.SonarDisruption <= 0.0f) { continue; }
float disruptionStrength = levelObject.ActivePrefab.SonarDisruption;
Vector2 disruptionPos = new Vector2(levelObject.Position.X, levelObject.Position.Y);
float disruptionDist = Vector2.Distance(pingSource, disruptionPos);
disruptedDirections.Add(new Pair<Vector2, float>((disruptionPos - pingSource) / disruptionDist, disruptionStrength));
if (disruptionDist > worldPrevPingRadius && disruptionDist <= worldPingRadius)
{ {
var blip = new SonarBlip(disruptionPos + Rand.Vector(Rand.Range(0.0f, Level.GridCellSize * 4 * disruptionStrength)), MathHelper.Lerp(1.0f, 1.5f, disruptionStrength), Rand.Range(1.0f, 2.0f + disruptionStrength)); CreateBlipsForDisruption(disruptionPos, disruptionStrength);
sonarBlips.Add(blip);
} }
} }
foreach (AITarget aiTarget in AITarget.List)
{
if (aiTarget.SonarDisruption <= 0.0f || !aiTarget.Enabled) { continue; }
float distSqr = Vector2.DistanceSquared(aiTarget.WorldPosition, pingSource);
if (distSqr > worldPingRadiusSqr) { continue; }
float disruptionDist = (float)Math.Sqrt(distSqr);
disruptedDirections.Add(new Pair<Vector2, float>((aiTarget.WorldPosition - pingSource) / disruptionDist, aiTarget.SonarDisruption));
if (disruptionDist > worldPrevPingRadius && disruptionDist <= worldPingRadius)
{
CreateBlipsForDisruption(aiTarget.WorldPosition, aiTarget.SonarDisruption);
}
}
}
void CreateBlipsForDisruption(Vector2 disruptionPos, float disruptionStrength)
{
for (int i = 0; i < disruptionStrength * Level.GridCellSize * 0.02f; i++)
{
var blip = new SonarBlip(disruptionPos + Rand.Vector(Rand.Range(0.0f, Level.GridCellSize * 4 * disruptionStrength)), MathHelper.Lerp(1.0f, 1.5f, disruptionStrength), Rand.Range(1.0f, 2.0f + disruptionStrength));
sonarBlips.Add(blip);
}
} }
} }
@@ -1045,9 +1073,9 @@ namespace Barotrauma.Items.Components
} }
Vector2 dir = pos / (float)Math.Sqrt(posDistSqr); Vector2 dir = pos / (float)Math.Sqrt(posDistSqr);
if (isLastPingDirectional) if (currentPingIndex != -1 && activePings[currentPingIndex].IsDirectional)
{ {
if (Vector2.Dot(lastPingDirection, dir) < DirectionalPingDotProduct) if (Vector2.Dot(activePings[currentPingIndex].Direction, dir) < DirectionalPingDotProduct)
{ {
blip.FadeTimer = 0.0f; blip.FadeTimer = 0.0f;
return false; return false;
@@ -1155,8 +1183,8 @@ namespace Barotrauma.Items.Components
public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null) public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null)
{ {
msg.Write(IsActive); msg.Write(currentMode == Mode.Active);
if (IsActive) if (currentMode == Mode.Active)
{ {
msg.WriteRangedSingle(zoom, MinZoom, MaxZoom, 8); msg.WriteRangedSingle(zoom, MinZoom, MaxZoom, 8);
msg.Write(useDirectionalPing); msg.Write(useDirectionalPing);
@@ -1192,8 +1220,8 @@ namespace Barotrauma.Items.Components
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime); StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
return; return;
} }
IsActive = isActive; CurrentMode = isActive ? Mode.Active : Mode.Passive;
if (isActive) if (isActive)
{ {
activeTickBox.Selected = true; activeTickBox.Selected = true;
@@ -735,9 +735,7 @@ namespace Barotrauma.Items.Components
if (sourcePort.Docked || sourcePort.Item.Submarine == null) { continue; } if (sourcePort.Docked || sourcePort.Item.Submarine == null) { continue; }
if (sourcePort.Item.Submarine != controlledSub) { continue; } if (sourcePort.Item.Submarine != controlledSub) { continue; }
int sourceDir = sourcePort.IsHorizontal ? int sourceDir = sourcePort.GetDir();
Math.Sign(sourcePort.Item.WorldPosition.X - sourcePort.Item.Submarine.WorldPosition.X) :
Math.Sign(sourcePort.Item.WorldPosition.Y - sourcePort.Item.Submarine.WorldPosition.Y);
foreach (DockingPort targetPort in DockingPort.List) foreach (DockingPort targetPort in DockingPort.List)
{ {
@@ -745,9 +743,7 @@ namespace Barotrauma.Items.Components
if (targetPort.Item.Submarine == controlledSub || targetPort.IsHorizontal != sourcePort.IsHorizontal) { continue; } if (targetPort.Item.Submarine == controlledSub || targetPort.IsHorizontal != sourcePort.IsHorizontal) { continue; }
if (Level.Loaded != null && targetPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; } if (Level.Loaded != null && targetPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
int targetDir = targetPort.IsHorizontal ? int targetDir = targetPort.GetDir();
Math.Sign(targetPort.Item.WorldPosition.X - targetPort.Item.Submarine.WorldPosition.X) :
Math.Sign(targetPort.Item.WorldPosition.Y - targetPort.Item.Submarine.WorldPosition.Y);
if (sourceDir == targetDir) { continue; } if (sourceDir == targetDir) { continue; }
@@ -1,19 +1,16 @@
using System.Collections.Generic; using Barotrauma.Sounds;
using System.Collections.Generic;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Sounds;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
partial class Powered : ItemComponent partial class Powered : ItemComponent
{ {
protected List<RoundSound> sparkSounds;
private RoundSound powerOnSound; private RoundSound powerOnSound;
private bool powerOnSoundPlayed; private bool powerOnSoundPlayed;
partial void InitProjectSpecific(XElement element) partial void InitProjectSpecific(XElement element)
{ {
sparkSounds = new List<RoundSound>();
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
@@ -21,10 +18,6 @@ namespace Barotrauma.Items.Components
case "poweronsound": case "poweronsound":
powerOnSound = Submarine.LoadRoundSound(subElement, false); powerOnSound = Submarine.LoadRoundSound(subElement, false);
break; break;
case "sparksound":
var sparkSound = Submarine.LoadRoundSound(subElement, false);
if (sparkSound != null) { sparkSounds.Add(sparkSound); }
break;
} }
} }
} }
@@ -32,6 +32,8 @@ namespace Barotrauma.Items.Components
private List<ParticleEmitter> ParticleEmitterHitCharacter = new List<ParticleEmitter>(); private List<ParticleEmitter> ParticleEmitterHitCharacter = new List<ParticleEmitter>();
private List<Pair<RelatedItem, ParticleEmitter>> ParticleEmitterHitItem = new List<Pair<RelatedItem, ParticleEmitter>>(); private List<Pair<RelatedItem, ParticleEmitter>> ParticleEmitterHitItem = new List<Pair<RelatedItem, ParticleEmitter>>();
private float prevProgressBarState;
partial void InitProjSpecific(XElement element) partial void InitProjSpecific(XElement element)
{ {
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
@@ -70,7 +72,7 @@ namespace Barotrauma.Items.Components
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi); float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
ParticleEmitter.Emit( ParticleEmitter.Emit(
deltaTime, item.WorldPosition + TransformedBarrelPos, deltaTime, item.WorldPosition + TransformedBarrelPos,
item.CurrentHull, particleAngle, -particleAngle); item.CurrentHull, particleAngle, ParticleEmitter.Prefab.CopyEntityAngle ? -particleAngle : 0);
} }
} }
@@ -110,19 +112,22 @@ namespace Barotrauma.Items.Components
} }
} }
partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem, float prevCondition) partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem)
{ {
if (prevCondition != targetItem.Condition) float progressBarState = targetItem.ConditionPercentage / 100.0f;
if (!MathUtils.NearlyEqual(progressBarState, prevProgressBarState))
{ {
Vector2 progressBarPos = targetItem.DrawPosition; Vector2 progressBarPos = targetItem.DrawPosition;
var progressBar = user.UpdateHUDProgressBar( var progressBar = user.UpdateHUDProgressBar(
targetItem, targetItem,
progressBarPos, progressBarPos,
targetItem.Condition / item.MaxCondition, progressBarState,
Color.Red, Color.Green); Color.Red, Color.Green);
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); } if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
} }
prevProgressBarState = progressBarState;
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition); Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
if (targetItem.Submarine != null) particlePos += targetItem.Submarine.DrawPosition; if (targetItem.Submarine != null) particlePos += targetItem.Submarine.DrawPosition;
foreach (var emitter in ParticleEmitterHitItem) foreach (var emitter in ParticleEmitterHitItem)
@@ -74,10 +74,16 @@ namespace Barotrauma.Items.Components
//dropped or dragged from the panel to the players inventory //dropped or dragged from the panel to the players inventory
if (draggingConnected != null) if (draggingConnected != null)
{ {
int linkIndex = c.FindWireIndex(draggingConnected.Item); //the wire can only be dragged out if it's not connected to anything at the other end
if (linkIndex > -1) if (Screen.Selected == GameMain.SubEditorScreen ||
(draggingConnected.Connections[0] == null && draggingConnected.Connections[1] == null) ||
(draggingConnected.Connections.Contains(c) && draggingConnected.Connections.Contains(null)))
{ {
Inventory.draggingItem = c.wires[linkIndex].Item; int linkIndex = c.FindWireIndex(draggingConnected.Item);
if (linkIndex > -1 || panel.DisconnectedWires.Contains(draggingConnected))
{
Inventory.draggingItem = draggingConnected.Item;
}
} }
} }
@@ -109,59 +115,79 @@ namespace Barotrauma.Items.Components
if (draggingConnected != null) if (draggingConnected != null)
{ {
DrawWire(spriteBatch, draggingConnected, draggingConnected.Item, PlayerInput.MousePosition, new Vector2(x + width / 2, y + height - 10), mouseInRect, null, panel, ""); if (mouseInRect)
{
DrawWire(spriteBatch, draggingConnected, PlayerInput.MousePosition, new Vector2(x + width / 2, y + height - 10), null, panel, "");
}
if (!PlayerInput.LeftButtonHeld()) if (!PlayerInput.LeftButtonHeld())
{ {
if (GameMain.Client != null) if (draggingConnected.Connections[0]?.ConnectionPanel == panel ||
draggingConnected.Connections[1]?.ConnectionPanel == panel)
{ {
panel.Item.CreateClientEvent(panel); draggingConnected.RemoveConnection(panel.Item);
panel.DisconnectedWires.Add(draggingConnected);
} }
if (GameMain.Client != null) { panel.Item.CreateClientEvent(panel); }
draggingConnected = null; draggingConnected = null;
} }
} }
//if the Character using the panel has a wire item equipped //if the Character using the panel has a wire item equipped
//and the wire hasn't been connected yet, draw it on the panel //and the wire hasn't been connected yet, draw it on the panel
if (equippedWire != null) if (equippedWire != null && (draggingConnected != equippedWire || !mouseInRect))
{ {
if (panel.Connections.Find(c => c.Wires.Contains(equippedWire)) == null) if (panel.Connections.Find(c => c.Wires.Contains(equippedWire)) == null)
{ {
DrawWire(spriteBatch, equippedWire, equippedWire.Item, DrawWire(spriteBatch, equippedWire, new Vector2(x + width / 2, y + height - 150 * GUI.Scale),
new Vector2(x + width / 2, y + height - 100), new Vector2(x + width / 2, y + height),
new Vector2(x + width / 2, y + height), mouseInRect, null, panel, ""); null, panel, "");
if (draggingConnected == equippedWire) Inventory.draggingItem = equippedWire.Item; if (draggingConnected == equippedWire) { Inventory.draggingItem = equippedWire.Item; }
} }
} }
float step = (width * 0.75f) / panel.DisconnectedWires.Count();
x = (int)(x + width / 2 - step * (panel.DisconnectedWires.Count() - 1) / 2);
foreach (Wire wire in panel.DisconnectedWires)
{
if (wire == draggingConnected && mouseInRect) { continue; }
Connection recipient = wire.OtherConnection(null);
string label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})";
if (wire.Locked) { label += "\n" + TextManager.Get("ConnectionLocked"); }
DrawWire(spriteBatch, wire, new Vector2(x, y + height - 100 * GUI.Scale),
new Vector2(x, y + height),
null, panel, label);
x += (int)step;
}
//stop dragging a wire item if the cursor is within any connection panel //stop dragging a wire item if the cursor is within any connection panel
//(so we don't drop the item when dropping the wire on a connection) //(so we don't drop the item when dropping the wire on a connection)
if (mouseInRect || GUI.MouseOn?.UserData is ConnectionPanel) Inventory.draggingItem = null; if (mouseInRect || GUI.MouseOn?.UserData is ConnectionPanel) { Inventory.draggingItem = null; }
} }
private void Draw(SpriteBatch spriteBatch, ConnectionPanel panel, Vector2 position, Vector2 labelPos, Vector2 wirePosition, bool mouseIn, Wire equippedWire, float wireInterval) private void Draw(SpriteBatch spriteBatch, ConnectionPanel panel, Vector2 position, Vector2 labelPos, Vector2 wirePosition, bool mouseIn, Wire equippedWire, float wireInterval)
{ {
//spriteBatch.DrawString(GUI.SmallFont, Name, new Vector2(labelPos.X, labelPos.Y-10), Color.White);
GUI.DrawString(spriteBatch, labelPos, DisplayName, IsPower ? Color.Red : Color.White, Color.Black, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, labelPos, DisplayName, IsPower ? Color.Red : Color.White, Color.Black, 0, GUI.SmallFont);
connectionSprite.Draw(spriteBatch, position); connectionSprite.Draw(spriteBatch, position);
for (int i = 0; i < MaxLinked; i++) for (int i = 0; i < MaxLinked; i++)
{ {
if (wires[i] == null || wires[i].Hidden || draggingConnected == wires[i]) continue; if (wires[i] == null || wires[i].Hidden || (draggingConnected == wires[i] && (mouseIn || Screen.Selected == GameMain.SubEditorScreen))) { continue; }
Connection recipient = wires[i].OtherConnection(this); Connection recipient = wires[i].OtherConnection(this);
string label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})";
string label = recipient == null ? "" : if (wires[i].Locked) { label += "\n" + TextManager.Get("ConnectionLocked"); }
wires[i].Locked ? recipient.item.Name + "\n" + TextManager.Get("ConnectionLocked") : recipient.item.Name; DrawWire(spriteBatch, wires[i], position, wirePosition, equippedWire, panel, label);
DrawWire(spriteBatch, wires[i], (recipient == null) ? wires[i].Item : recipient.item, position, wirePosition, mouseIn, equippedWire, panel, label);
wirePosition.Y += wireInterval; wirePosition.Y += wireInterval;
} }
if (draggingConnected != null && Vector2.Distance(position, PlayerInput.MousePosition) < 13.0f) if (draggingConnected != null && Vector2.Distance(position, PlayerInput.MousePosition) < (20.0f * GUI.Scale))
{ {
connectionSpriteHighlight.Draw(spriteBatch, position); connectionSpriteHighlight.Draw(spriteBatch, position);
@@ -172,15 +198,15 @@ namespace Barotrauma.Items.Components
if (index > -1 && !Wires.Contains(draggingConnected)) if (index > -1 && !Wires.Contains(draggingConnected))
{ {
bool alreadyConnected = draggingConnected.IsConnectedTo(panel.Item); bool alreadyConnected = draggingConnected.IsConnectedTo(panel.Item);
draggingConnected.RemoveConnection(panel.Item); draggingConnected.RemoveConnection(panel.Item);
if (draggingConnected.Connect(this, !alreadyConnected, true)) if (draggingConnected.Connect(this, !alreadyConnected, true))
{ {
var otherConnection = draggingConnected.OtherConnection(this); var otherConnection = draggingConnected.OtherConnection(this);
SetWire(index, draggingConnected); SetWire(index, draggingConnected);
} }
} }
if (GameMain.Client != null) { panel.Item.CreateClientEvent(panel); }
draggingConnected = null;
} }
} }
@@ -215,15 +241,8 @@ namespace Barotrauma.Items.Components
flashTimer -= deltaTime; flashTimer -= deltaTime;
} }
private static void DrawWire(SpriteBatch spriteBatch, Wire wire, Item item, Vector2 end, Vector2 start, bool mouseIn, Wire equippedWire, ConnectionPanel panel, string label) private static void DrawWire(SpriteBatch spriteBatch, Wire wire, Vector2 end, Vector2 start, Wire equippedWire, ConnectionPanel panel, string label)
{ {
if (draggingConnected == wire)
{
if (!mouseIn) return;
end = PlayerInput.MousePosition;
start.X = (start.X + end.X) / 2.0f;
}
int textX = (int)start.X; int textX = (int)start.X;
if (start.X < end.X) if (start.X < end.X)
textX -= 10; textX -= 10;
@@ -244,11 +263,19 @@ namespace Barotrauma.Items.Components
if (!string.IsNullOrEmpty(label)) if (!string.IsNullOrEmpty(label))
{ {
GUI.DrawString(spriteBatch, if (start.Y > panel.GuiFrame.Rect.Bottom - 1.0f)
new Vector2(start.X < end.X ? textX - GUI.SmallFont.MeasureString(label).X : textX, start.Y - 5.0f), {
label, //wire at the bottom of the panel -> draw the text below the panel, tilted 45 degrees
(mouseOn ? Color.Gold : Color.White) * (wire.Locked ? 0.6f : 1.0f), Color.Black * 0.8f, GUI.SmallFont.DrawString(spriteBatch, label, start + Vector2.UnitY * 20 * GUI.Scale, Color.White, 45.0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0.0f);
3, GUI.SmallFont); }
else
{
GUI.DrawString(spriteBatch,
new Vector2(start.X < end.X ? textX - GUI.SmallFont.MeasureString(label).X : textX, start.Y - 5.0f),
label,
(mouseOn ? Color.Gold : Color.White) * (wire.Locked ? 0.6f : 1.0f), Color.Black * 0.8f,
3, GUI.SmallFont);
}
} }
var wireEnd = end + Vector2.Normalize(start - end) * 30.0f; var wireEnd = end + Vector2.Normalize(start - end) * 30.0f;
@@ -259,14 +286,14 @@ namespace Barotrauma.Items.Components
{ {
spriteBatch.Draw(wireVertical.Texture, new Rectangle(wireEnd.ToPoint(), new Point(18, (int)dist)), wireVertical.SourceRect, spriteBatch.Draw(wireVertical.Texture, new Rectangle(wireEnd.ToPoint(), new Point(18, (int)dist)), wireVertical.SourceRect,
Color.Gold, Color.Gold,
MathUtils.VectorToAngle(end - start) + MathHelper.PiOver2, //angle of line (calulated above) MathUtils.VectorToAngle(end - start) + MathHelper.PiOver2,
new Vector2(6, 0), // point in line about which to rotate new Vector2(6, 0), // point in line about which to rotate
SpriteEffects.None, SpriteEffects.None,
0.0f); 0.0f);
} }
spriteBatch.Draw(wireVertical.Texture, new Rectangle(wireEnd.ToPoint(), new Point(12, (int)dist)), wireVertical.SourceRect, spriteBatch.Draw(wireVertical.Texture, new Rectangle(wireEnd.ToPoint(), new Point(12, (int)dist)), wireVertical.SourceRect,
wire.Item.Color * alpha, wire.Item.Color * alpha,
MathUtils.VectorToAngle(end - start) + MathHelper.PiOver2, //angle of line (calulated above) MathUtils.VectorToAngle(end - start) + MathHelper.PiOver2,
new Vector2(6, 0), // point in line about which to rotate new Vector2(6, 0), // point in line about which to rotate
SpriteEffects.None, SpriteEffects.None,
0.0f); 0.0f);
@@ -283,7 +310,7 @@ namespace Barotrauma.Items.Components
if (allowRewiring && !wire.Locked && (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen)) if (allowRewiring && !wire.Locked && (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen))
{ {
//start dragging the wire //start dragging the wire
if (PlayerInput.LeftButtonHeld()) draggingConnected = wire; if (PlayerInput.LeftButtonHeld()) { draggingConnected = wire; }
} }
} }
} }
@@ -52,7 +52,7 @@ namespace Barotrauma.Items.Components
private void DrawConnections(SpriteBatch spriteBatch, GUICustomComponent container) private void DrawConnections(SpriteBatch spriteBatch, GUICustomComponent container)
{ {
if (user != Character.Controlled || user == null) return; if (user != Character.Controlled || user == null) { return; }
HighlightedWire = null; HighlightedWire = null;
Connection.DrawConnections(spriteBatch, this, user); Connection.DrawConnections(spriteBatch, this, user);
@@ -70,8 +70,22 @@ namespace Barotrauma.Items.Components
{ {
//delay reading the state until midround syncing is done //delay reading the state until midround syncing is done
//because some of the wires connected to the panel may not exist yet //because some of the wires connected to the panel may not exist yet
int bitsToRead = Connections.Count * Connection.MaxLinked * 16; long msgStartPos = msg.Position;
StartDelayedCorrection(type, msg.ExtractBits(bitsToRead), sendingTime, waitForMidRoundSync: true); foreach (Connection connection in Connections)
{
for (int i = 0; i < Connection.MaxLinked; i++)
{
msg.ReadUInt16();
}
}
ushort disconnectedWireCount = msg.ReadUInt16();
for (int i = 0; i < disconnectedWireCount; i++)
{
msg.ReadUInt16();
}
int msgLength = (int)(msg.Position - msgStartPos);
msg.Position = msgStartPos;
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime, waitForMidRoundSync: true);
} }
else else
{ {
@@ -95,11 +109,9 @@ namespace Barotrauma.Items.Components
{ {
ushort wireId = msg.ReadUInt16(); ushort wireId = msg.ReadUInt16();
Item wireItem = Entity.FindEntityByID(wireId) as Item; if (!(Entity.FindEntityByID(wireId) is Item wireItem)) { continue; }
if (wireItem == null) continue;
Wire wireComponent = wireItem.GetComponent<Wire>(); Wire wireComponent = wireItem.GetComponent<Wire>();
if (wireComponent == null) continue; if (wireComponent == null) { continue; }
newWires.Add(wireComponent); newWires.Add(wireComponent);
@@ -108,18 +120,46 @@ namespace Barotrauma.Items.Components
} }
} }
List<Wire> previousDisconnectedWires = new List<Wire>(DisconnectedWires);
DisconnectedWires.Clear();
ushort disconnectedWireCount = msg.ReadUInt16();
for (int i = 0; i < disconnectedWireCount; i++)
{
ushort wireId = msg.ReadUInt16();
if (!(Entity.FindEntityByID(wireId) is Item wireItem)) { continue; }
Wire wireComponent = wireItem.GetComponent<Wire>();
if (wireComponent == null) { continue; }
DisconnectedWires.Add(wireComponent);
}
foreach (Wire wire in prevWires) foreach (Wire wire in prevWires)
{ {
if (wire.Connections[0] == null && wire.Connections[1] == null) bool connected = wire.Connections[0] != null || wire.Connections[1] != null;
if (!connected)
{
foreach (Item item in Item.ItemList)
{
var connectionPanel = item.GetComponent<ConnectionPanel>();
if (connectionPanel != null && connectionPanel.DisconnectedWires.Contains(wire))
{
connected = true;
break;
}
}
}
if (wire.Item.ParentInventory == null && !connected)
{ {
wire.Item.Drop(null); wire.Item.Drop(null);
} }
//wires that are not in anyone's inventory (i.e. not currently being rewired) can never be connected to only one connection }
// -> someone must have dropped the wire from the connection panel
else if (wire.Item.ParentInventory == null && foreach (Wire disconnectedWire in previousDisconnectedWires)
(wire.Connections[0] != null ^ wire.Connections[1] != null)) {
if (disconnectedWire.Connections[0] == null &&
disconnectedWire.Connections[1] == null &&
!DisconnectedWires.Contains(disconnectedWire))
{ {
wire.Item.Drop(null); disconnectedWire.Item.Drop(dropper: null);
} }
} }
} }
@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -90,18 +91,26 @@ namespace Barotrauma.Items.Components
section.Draw(spriteBatch, item.Color, drawOffset, depth, 0.3f); section.Draw(spriteBatch, item.Color, drawOffset, depth, 0.3f);
} }
if (IsActive && nodes.Count > 0 && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance) if (nodes.Count > 0)
{ {
WireSection.Draw( if (!IsActive)
spriteBatch, {
new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset, if (connections[0] == null) { DrawHangingWire(spriteBatch, nodes[0] + drawOffset, depth); }
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset, if (connections[1] == null) { DrawHangingWire(spriteBatch, nodes.Last() + drawOffset, depth); }
item.Color * 0.5f, }
depth, if (IsActive && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance)
0.3f); {
WireSection.Draw(
spriteBatch,
new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset,
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
item.Color * 0.5f,
depth,
0.3f);
}
} }
if (!editing || !GameMain.SubEditorScreen.WiringMode) return; if (!editing || !GameMain.SubEditorScreen.WiringMode) { return; }
for (int i = 0; i < nodes.Count; i++) for (int i = 0; i < nodes.Count; i++)
{ {
@@ -126,6 +135,23 @@ namespace Barotrauma.Items.Components
} }
} }
private void DrawHangingWire(SpriteBatch spriteBatch, Vector2 start, float depth)
{
float angle = (float)Math.Sin(GameMain.GameScreen.GameTime * 2.0f + item.ID) * 0.2f;
Vector2 endPos = start + new Vector2((float)Math.Sin(angle), -(float)Math.Cos(angle)) * 50.0f;
WireSection.Draw(
spriteBatch,
start, endPos,
Color.Orange, depth + 0.00001f, 0.2f);
WireSection.Draw(
spriteBatch,
start, start + (endPos - start) * 0.7f,
item.Color, depth, 0.3f);
}
public static void UpdateEditing(List<Wire> wires) public static void UpdateEditing(List<Wire> wires)
{ {
//dragging a node of some wire //dragging a node of some wire
@@ -25,6 +25,9 @@ namespace Barotrauma.Items.Components
private SoundChannel moveSoundChannel; private SoundChannel moveSoundChannel;
private Vector2 crosshairPos, crosshairPointerPos; private Vector2 crosshairPos, crosshairPointerPos;
private Dictionary<string, Widget> widgets = new Dictionary<string, Widget>();
private float prevAngle;
private bool flashLowPower; private bool flashLowPower;
private bool flashNoAmmo; private bool flashNoAmmo;
@@ -117,6 +120,25 @@ namespace Barotrauma.Items.Components
barSize: 0.0f); barSize: 0.0f);
} }
private void InitializeRotationLimitWidget(Widget widget)
{
widget.Hovered += () =>
{
widget.secondaryColor = Color.Green;
};
widget.Selected += () =>
{
widget.color = Color.Green;
};
widget.MouseHeld += (deltaTime) =>
{
widget.DrawPos = PlayerInput.MousePosition;
};
widget.Deselected += () =>
{
widget.color = Color.Red;
};
}
partial void LaunchProjSpecific() partial void LaunchProjSpecific()
{ {
@@ -246,24 +268,153 @@ namespace Barotrauma.Items.Components
item.SpriteColor, item.SpriteColor,
rotation + MathHelper.PiOver2, item.Scale, rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth)); SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
if (!editing) { return; }
if (!editing) return;
float widgetRadius = 60.0f;
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
drawPos, drawPos,
drawPos + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * 60.0f, drawPos + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * widgetRadius,
Color.Green); Color.Green);
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
drawPos, drawPos,
drawPos + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * 60.0f, drawPos + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * widgetRadius,
Color.Green); Color.Green);
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
drawPos, drawPos,
drawPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * 60.0f, drawPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * widgetRadius,
Color.LightGreen); Color.LightGreen);
if (!item.IsSelected) { return; }
Widget minRotationWidget = GetWidget("minrotation", spriteBatch, size: 10, initMethod: (widget) =>
{
widget.MouseDown += () =>
{
widget.color = Color.Green;
prevAngle = minRotation;
};
widget.Deselected += () =>
{
widget.color = Color.Yellow;
item.CreateEditingHUD();
};
widget.MouseHeld += (deltaTime) =>
{
minRotation = GetRotationAngle(drawPos);
if (minRotation > maxRotation)
{
float temp = minRotation;
minRotation = maxRotation;
maxRotation = temp;
}
MapEntity.DisableSelect = true;
};
widget.PreUpdate += (deltaTime) =>
{
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y);
widget.DrawPos = Screen.Selected.Cam.WorldToScreen(widget.DrawPos);
};
widget.PostUpdate += (deltaTime) =>
{
widget.DrawPos = Screen.Selected.Cam.ScreenToWorld(widget.DrawPos);
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y);
};
widget.PreDraw += (sprtBtch, deltaTime) =>
{
widget.tooltip = "Min: " + (int)MathHelper.ToDegrees(minRotation);
widget.DrawPos = drawPos + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * widgetRadius;
widget.Update(deltaTime);
};
});
Widget maxRotationWidget = GetWidget("maxrotation", spriteBatch, size: 10, initMethod: (widget) =>
{
widget.MouseDown += () =>
{
widget.color = Color.Green;
prevAngle = minRotation;
};
widget.Deselected += () =>
{
widget.color = Color.Yellow;
item.CreateEditingHUD();
};
widget.MouseHeld += (deltaTime) =>
{
maxRotation = GetRotationAngle(drawPos);
if (minRotation > maxRotation)
{
float temp = minRotation;
minRotation = maxRotation;
maxRotation = temp;
}
MapEntity.DisableSelect = true;
};
widget.PreUpdate += (deltaTime) =>
{
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y);
widget.DrawPos = Screen.Selected.Cam.WorldToScreen(widget.DrawPos);
};
widget.PostUpdate += (deltaTime) =>
{
widget.DrawPos = Screen.Selected.Cam.ScreenToWorld(widget.DrawPos);
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y);
};
widget.PreDraw += (sprtBtch, deltaTime) =>
{
widget.tooltip = "Max: " + (int)MathHelper.ToDegrees(maxRotation);
widget.DrawPos = drawPos + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * widgetRadius;
widget.Update(deltaTime);
};
});
minRotationWidget.Draw(spriteBatch, (float)Timing.Step);
maxRotationWidget.Draw(spriteBatch, (float)Timing.Step);
}
private Widget GetWidget(string id, SpriteBatch spriteBatch, int size = 5, Action<Widget> initMethod = null)
{
if (!widgets.TryGetValue(id, out Widget widget))
{
widget = new Widget(id, size, Widget.Shape.Rectangle)
{
color = Color.Yellow,
tooltipOffset = new Vector2(size / 2 + 5, -10),
inputAreaMargin = 20,
RequireMouseOn = false
};
widgets.Add(id, widget);
initMethod?.Invoke(widget);
}
return widget;
}
/// <summary>
/// Returns correct angle between -2PI and +2PI
/// </summary>
/// <param name="drawPosition"></param>
/// <returns></returns>
private float GetRotationAngle(Vector2 drawPosition)
{
Vector2 mouseVector = Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition);
mouseVector.Y = -mouseVector.Y;
Vector2 rotationVector = mouseVector - drawPosition;
rotationVector.Normalize();
double angle = Math.Atan2(MathHelper.ToRadians(rotationVector.Y), MathHelper.ToRadians(rotationVector.X));
if (angle < 0)
{// calculates which coterminal angle is closer to previous angle
angle = Math.Abs(angle - prevAngle) < Math.Abs((angle + Math.PI * 2) - prevAngle) ? angle : angle + Math.PI * 2;
}
else if (angle > 0)
{
angle = Math.Abs(angle - prevAngle) < Math.Abs((angle - Math.PI * 2) - prevAngle) ? angle : angle - Math.PI * 2;
}
angle = MathHelper.Clamp((float)angle, -((float)Math.PI * 2), (float)Math.PI * 2);
prevAngle = (float)angle;
return (float)angle;
} }
public override void DrawHUD(SpriteBatch spriteBatch, Character character) public override void DrawHUD(SpriteBatch spriteBatch, Character character)
@@ -329,6 +329,10 @@ namespace Barotrauma
item.AiTarget?.Draw(spriteBatch); item.AiTarget?.Draw(spriteBatch);
} }
} }
if (body != null)
{
body.DebugDraw(spriteBatch, Color.White);
}
} }
if (!editing || (body != null && !body.Enabled)) if (!editing || (body != null && !body.Enabled))
@@ -880,7 +884,6 @@ namespace Barotrauma
} }
} }
break; break;
case NetEntityEvent.Type.InventoryState: case NetEntityEvent.Type.InventoryState:
{ {
int containerIndex = msg.ReadRangedInteger(0, components.Count - 1); int containerIndex = msg.ReadRangedInteger(0, components.Count - 1);
@@ -969,6 +972,10 @@ namespace Barotrauma
case NetEntityEvent.Type.ChangeProperty: case NetEntityEvent.Type.ChangeProperty:
WritePropertyChange(msg, extraData, true); WritePropertyChange(msg, extraData, true);
break; break;
case NetEntityEvent.Type.Combine:
UInt16 combineTargetID = (UInt16)extraData[1];
msg.Write(combineTargetID);
break;
} }
msg.WritePadBits(); msg.WritePadBits();
} }
@@ -90,6 +90,15 @@ namespace Barotrauma
} }
} }
private Vector2 ClampParticlePos(Vector2 particlePos, Hull hull)
{
if (hull == null) return particlePos;
return new Vector2(
MathHelper.Clamp(particlePos.X, hull.WorldRect.X, hull.WorldRect.Right),
MathHelper.Clamp(particlePos.Y, hull.WorldRect.Y - hull.WorldRect.Height, hull.WorldRect.Y));
}
private IEnumerable<object> DimLight(LightSource light) private IEnumerable<object> DimLight(LightSource light)
{ {
float currBrightness = 1.0f; float currBrightness = 1.0f;
@@ -260,20 +260,19 @@ namespace Barotrauma.Lights
(int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.Black); (int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.Black);
spriteBatch.End(); spriteBatch.End();
} }
else
visibleHulls = GetVisibleHulls(cam);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, transformMatrix: spriteBatchTransform);
foreach (Rectangle drawRect in visibleHulls.Values)
{ {
visibleHulls = GetVisibleHulls(cam); //TODO: draw some sort of smoothed rectangle
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, transformMatrix: spriteBatchTransform); GUI.DrawRectangle(spriteBatch,
foreach (Rectangle drawRect in visibleHulls.Values) new Vector2(drawRect.X, -drawRect.Y),
{ new Vector2(drawRect.Width, drawRect.Height),
//TODO: draw some sort of smoothed rectangle Color.Black, true);
GUI.DrawRectangle(spriteBatch, }
new Vector2(drawRect.X, -drawRect.Y), spriteBatch.End();
new Vector2(drawRect.Width, drawRect.Height),
Color.Black, true);
}
spriteBatch.End();
}
graphics.BlendState = BlendState.Additive; graphics.BlendState = BlendState.Additive;
} }
@@ -382,8 +382,28 @@ namespace Barotrauma
} }
else else
{ {
selectedList.Clear(); selectedList = newSelection;
newSelection.ForEach(e => AddSelection(e)); //selectedList.Clear();
//newSelection.ForEach(e => AddSelection(e));
foreach (var entity in newSelection)
{
HandleDoorGapLinks(entity,
onGapFound: (door, gap) =>
{
door.RefreshLinkedGap();
if (!selectedList.Contains(gap))
{
selectedList.Add(gap);
}
},
onDoorFound: (door, gap) =>
{
if (!selectedList.Contains(door.Item))
{
selectedList.Add(door.Item);
}
});
}
} }
//select wire if both items it's connected to are selected //select wire if both items it's connected to are selected
@@ -12,11 +12,6 @@ using System.Collections.Generic;
namespace Barotrauma namespace Barotrauma
{ {
partial class WallSection
{
public ConvexHull hull;
}
partial class Structure : MapEntity, IDamageable, IServerSerializable partial class Structure : MapEntity, IDamageable, IServerSerializable
{ {
public static bool ShowWalls = true, ShowStructures = true; public static bool ShowWalls = true, ShowStructures = true;
@@ -204,8 +204,7 @@ namespace Barotrauma.Networking
ulong fileSize = inc.ReadUInt64(); ulong fileSize = inc.ReadUInt64();
string fileName = inc.ReadString(); string fileName = inc.ReadString();
string errorMsg; if (!ValidateInitialData(fileType, fileName, fileSize, out string errorMsg))
if (!ValidateInitialData(fileType, fileName, fileSize, out errorMsg))
{ {
GameMain.Client.CancelFileTransfer(inc.SequenceChannel); GameMain.Client.CancelFileTransfer(inc.SequenceChannel);
DebugConsole.ThrowError("File transfer failed (" + errorMsg + ")"); DebugConsole.ThrowError("File transfer failed (" + errorMsg + ")");
@@ -32,6 +32,8 @@ namespace Barotrauma.Networking
protected GUITickBox cameraFollowsSub; protected GUITickBox cameraFollowsSub;
public RoundEndCinematic EndCinematic;
private ClientPermissions permissions = ClientPermissions.None; private ClientPermissions permissions = ClientPermissions.None;
private List<string> permittedConsoleCommands = new List<string>(); private List<string> permittedConsoleCommands = new List<string>();
@@ -209,7 +211,7 @@ namespace Barotrauma.Networking
otherClients = new List<Client>(); otherClients = new List<Client>();
serverSettings = new ServerSettings("Server", 0, 0, 0, false, false); serverSettings = new ServerSettings(this, "Server", 0, 0, 0, false, false);
ConnectToServer(ip, serverName); ConnectToServer(ip, serverName);
@@ -800,7 +802,7 @@ namespace Barotrauma.Networking
GameMain.GameSession.WinningTeam = winningTeam; GameMain.GameSession.WinningTeam = winningTeam;
GameMain.GameSession.Mission.Completed = true; GameMain.GameSession.Mission.Completed = true;
} }
CoroutineManager.StartCoroutine(EndGame(endMessage)); CoroutineManager.StartCoroutine(EndGame(endMessage), "EndGame");
break; break;
case ServerPacketHeader.CAMPAIGN_SETUP_INFO: case ServerPacketHeader.CAMPAIGN_SETUP_INFO:
UInt16 saveCount = inc.ReadUInt16(); UInt16 saveCount = inc.ReadUInt16();
@@ -1059,6 +1061,12 @@ namespace Barotrauma.Networking
if (Character != null) Character.Remove(); if (Character != null) Character.Remove();
HasSpawned = false; HasSpawned = false;
while (CoroutineManager.IsCoroutineRunning("EndGame"))
{
if (EndCinematic != null) { EndCinematic.Stop(); }
yield return CoroutineStatus.Running;
}
GameMain.LightManager.LightingEnabled = true; GameMain.LightManager.LightingEnabled = true;
//enable spectate button in case we fail to start the round now //enable spectate button in case we fail to start the round now
@@ -1192,6 +1200,15 @@ namespace Barotrauma.Networking
if (GameMain.GameSession != null) { GameMain.GameSession.GameMode.End(endMessage); } if (GameMain.GameSession != null) { GameMain.GameSession.GameMode.End(endMessage); }
// Enable characters near the main sub for the endCinematic
foreach (Character c in Character.CharacterList)
{
if (Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition) < NetConfig.EnableCharacterDistSqr)
{
c.Enabled = true;
}
}
ServerSettings.ServerDetailsChanged = true; ServerSettings.ServerDetailsChanged = true;
gameStarted = false; gameStarted = false;
@@ -1199,17 +1216,15 @@ namespace Barotrauma.Networking
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
GameMain.LightManager.LosEnabled = false; GameMain.LightManager.LosEnabled = false;
respawnManager = null; respawnManager = null;
float endPreviewLength = 10.0f;
if (Screen.Selected == GameMain.GameScreen) if (Screen.Selected == GameMain.GameScreen)
{ {
new RoundEndCinematic(Submarine.MainSub, GameMain.GameScreen.Cam, endPreviewLength); EndCinematic = new RoundEndCinematic(Submarine.MainSub, GameMain.GameScreen.Cam);
float secondsLeft = endPreviewLength; while (EndCinematic.Running && Screen.Selected == GameMain.GameScreen)
do
{ {
secondsLeft -= CoroutineManager.UnscaledDeltaTime;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} while (secondsLeft > 0.0f && Screen.Selected == GameMain.GameScreen); }
EndCinematic = null;
} }
Submarine.Unload(); Submarine.Unload();
@@ -1628,6 +1643,7 @@ namespace Barotrauma.Networking
outmsg.Write(LastClientListUpdateID); outmsg.Write(LastClientListUpdateID);
Character.Controlled?.ClientWrite(outmsg); Character.Controlled?.ClientWrite(outmsg);
GameMain.GameScreen.Cam?.ClientWrite(outmsg);
entityEventManager.Write(outmsg, client.ServerConnection); entityEventManager.Write(outmsg, client.ServerConnection);
@@ -1829,9 +1845,10 @@ namespace Barotrauma.Networking
steamAuthTicket?.Cancel(); steamAuthTicket?.Cancel();
steamAuthTicket = null; steamAuthTicket = null;
foreach (var fileTransfer in FileReceiver.ActiveTransfers) List<FileReceiver.FileTransferIn> activeTransfers = new List<FileReceiver.FileTransferIn>(FileReceiver.ActiveTransfers);
foreach (var fileTransfer in activeTransfers)
{ {
fileTransfer.Dispose(); FileReceiver.StopTransfer(fileTransfer, deleteFile: true);
} }
if (HasPermission(ClientPermissions.ServerLog)) if (HasPermission(ClientPermissions.ServerLog))
@@ -1842,7 +1859,8 @@ namespace Barotrauma.Networking
if (GameMain.ServerChildProcess != null) if (GameMain.ServerChildProcess != null)
{ {
int checks = 0; int checks = 0;
while (!GameMain.ServerChildProcess.HasExited) { while (!GameMain.ServerChildProcess.HasExited)
{
if (checks > 10) if (checks > 10)
{ {
GameMain.ServerChildProcess.Kill(); GameMain.ServerChildProcess.Kill();
@@ -2329,24 +2347,34 @@ namespace Barotrauma.Networking
if (respawnManager != null) if (respawnManager != null)
{ {
string respawnInfo = ""; string respawnText = "";
float textScale = 1.0f;
Color textColor = Color.White;
if (respawnManager.CurrentState == RespawnManager.State.Waiting && if (respawnManager.CurrentState == RespawnManager.State.Waiting &&
respawnManager.CountdownStarted) respawnManager.RespawnCountdownStarted)
{ {
respawnInfo = TextManager.GetWithVariable(respawnManager.UsingShuttle ? "RespawnShuttleDispatching" : "RespawningIn", "[time]", ToolBox.SecondsToReadableTime(respawnManager.RespawnTimer)); float timeLeft = (float)(respawnManager.RespawnTime - DateTime.Now).TotalSeconds;
respawnText = TextManager.GetWithVariable(respawnManager.UsingShuttle ? "RespawnShuttleDispatching" : "RespawningIn", "[time]", ToolBox.SecondsToReadableTime(timeLeft));
} }
else if (respawnManager.CurrentState == RespawnManager.State.Transporting) else if (respawnManager.CurrentState == RespawnManager.State.Transporting &&
respawnManager.ReturnCountdownStarted)
{ {
respawnInfo = respawnManager.TransportTimer <= 0.0f ? float timeLeft = (float)(respawnManager.ReturnTime - DateTime.Now).TotalSeconds;
respawnText = timeLeft <= 0.0f ?
"" : "" :
TextManager.GetWithVariable("RespawnShuttleLeavingIn", "[time]", ToolBox.SecondsToReadableTime(respawnManager.TransportTimer)); TextManager.GetWithVariable("RespawnShuttleLeavingIn", "[time]", ToolBox.SecondsToReadableTime(timeLeft));
if (timeLeft < 20.0f)
{
//oscillate between 0-1
float phase = (float)(Math.Sin(timeLeft * MathHelper.Pi) + 1.0f) * 0.5f;
textScale = 1.0f + phase * 0.5f;
textColor = Color.Lerp(Color.Red, Color.White, 1.0f - phase);
}
} }
if (respawnManager != null) if (!string.IsNullOrEmpty(respawnText))
{ {
GUI.DrawString(spriteBatch, GUI.SmallFont.DrawString(spriteBatch, respawnText, new Vector2(120.0f, 10), textColor, 0.0f, Vector2.Zero, textScale, Microsoft.Xna.Framework.Graphics.SpriteEffects.None, 0.0f);
new Vector2(120.0f, 10),
respawnInfo, Color.White, null, 0, GUI.SmallFont);
} }
} }
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
partial class KarmaManager : ISerializableEntity
{
public void CreateSettingsFrame(GUIComponent parent)
{
CreateLabeledSlider(parent, 0.0f, 40.0f, 1.0f, "KickBanThreshold");
CreateLabeledSlider(parent, 0.0f, 50.0f, 1.0f, "HerpesThreshold");
CreateLabeledSlider(parent, 0.0f, 0.5f, 0.01f, "KarmaDecay");
CreateLabeledSlider(parent, 50.0f, 100.0f, 1.0f, "KarmaDecayThreshold");
CreateLabeledSlider(parent, 0.0f, 0.5f, 0.01f, "KarmaIncrease");
CreateLabeledSlider(parent, 0.0f, 50.0f, 1.0f, "KarmaIncreaseThreshold");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.12f), parent.RectTransform), TextManager.Get("Karma.PositiveActions"), textAlignment: Alignment.Center)
{
CanBeFocused = false
};
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, "StructureRepairKarmaIncrease");
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, "HealFriendlyKarmaIncrease");
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, "DamageEnemyKarmaIncrease");
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, "ItemRepairKarmaIncrease");
CreateLabeledSlider(parent, 0.0f, 10.0f, 0.05f, "ExtinguishFireKarmaIncrease");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.12f), parent.RectTransform), TextManager.Get("Karma.NegativeActions"), textAlignment: Alignment.Center)
{
CanBeFocused = false
};
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, "StructureDamageKarmaDecrease");
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, "DamageFriendlyKarmaDecrease");
CreateLabeledSlider(parent, 0.0f, 100.0f, 1.0f, "ReactorMeltdownKarmaDecrease");
CreateLabeledSlider(parent, 0.0f, 10.0f, 0.05f, "ReactorOverheatKarmaDecrease");
CreateLabeledSlider(parent, 0.0f, 20.0f, 1f, "AllowedWireDisconnectionsPerMinute");
CreateLabeledSlider(parent, 0.0f, 20.0f, 0.5f, "WireDisconnectionKarmaDecrease");
CreateLabeledSlider(parent, 0.0f, 30.0f, 1.0f, "SpamFilterKarmaDecrease");
}
private void CreateLabeledSlider(GUIComponent parent, float min, float max, float step, string propertyName)
{
var container = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f,
ToolTip = TextManager.Get("Karma." + propertyName + "ToolTip")
};
string labelText = TextManager.Get("Karma." + propertyName);
var label = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.8f), container.RectTransform),
labelText, font: GUI.SmallFont)
{
ToolTip = TextManager.Get("Karma." + propertyName + "ToolTip")
};
var slider = new GUIScrollBar(new RectTransform(new Vector2(0.3f, 0.8f), container.RectTransform), barSize: 0.1f)
{
Step = step <= 0.0f ? 0.0f : step / (max - min),
Range = new Vector2(min, max),
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
string formattedValueStr = step >= 1.0f ?
((int)scrollBar.BarScrollValue).ToString() :
scrollBar.BarScrollValue.Format(decimalCount: step <= 0.1f ? 2 : 1);
label.Text = TextManager.AddPunctuation(':', labelText, formattedValueStr);
return true;
}
};
GameMain.NetworkMember.ServerSettings.AssignGUIComponent(propertyName, slider);
slider.OnMoved(slider, slider.BarScroll);
}
}
}
@@ -1,25 +1,59 @@
using System; using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
partial class RespawnManager partial class RespawnManager
{ {
partial void UpdateWaiting(float deltaTime) private DateTime lastShuttleLeavingWarningTime;
{
if (CountdownStarted)
{
respawnTimer = Math.Max(0.0f, respawnTimer - deltaTime);
}
}
partial void UpdateTransportingProjSpecific(float deltaTime) partial void UpdateTransportingProjSpecific(float deltaTime)
{ {
if (shuttleTransportTimer + deltaTime > 15.0f && shuttleTransportTimer <= 15.0f && if (GameMain.Client?.Character == null || GameMain.Client.Character.Submarine != RespawnShuttle) { return; }
GameMain.Client?.Character != null && if (!ReturnCountdownStarted) { return; }
GameMain.Client.Character.Submarine == respawnShuttle)
//show a warning when there's 20 seconds until the shuttle leaves
if ((ReturnTime - DateTime.Now).TotalSeconds < 20.0f &&
(DateTime.Now - lastShuttleLeavingWarningTime).TotalSeconds > 30.0f)
{ {
lastShuttleLeavingWarningTime = DateTime.Now;
GameMain.Client.AddChatMessage("ServerMessage.ShuttleLeaving", ChatMessageType.Server); GameMain.Client.AddChatMessage("ServerMessage.ShuttleLeaving", ChatMessageType.Server);
} }
} }
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
var newState = (State)msg.ReadRangedInteger(0, Enum.GetNames(typeof(State)).Length);
switch (newState)
{
case State.Transporting:
ReturnCountdownStarted = msg.ReadBoolean();
maxTransportTime = msg.ReadSingle();
float transportTimeLeft = msg.ReadSingle();
ReturnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(transportTimeLeft * 1000.0f));
RespawnCountdownStarted = false;
if (CurrentState != newState)
{
CoroutineManager.StopCoroutines("forcepos");
//CoroutineManager.StartCoroutine(ForceShuttleToPos(Level.Loaded.StartPosition - Vector2.UnitY * Level.ShaftHeight, 100.0f), "forcepos");
}
break;
case State.Waiting:
RespawnCountdownStarted = msg.ReadBoolean();
ResetShuttle();
float newRespawnTime = msg.ReadSingle();
RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(newRespawnTime * 1000.0f));
break;
case State.Returning:
RespawnCountdownStarted = false;
break;
}
CurrentState = newState;
msg.ReadPadBits();
}
} }
} }
@@ -51,7 +51,22 @@ namespace Barotrauma.Networking
public bool ContentPackagesMatch(IEnumerable<ContentPackage> myContentPackages) public bool ContentPackagesMatch(IEnumerable<ContentPackage> myContentPackages)
{ {
return ContentPackagesMatch(myContentPackages.Select(cp => cp.MD5hash.Hash)); //make sure we have all the packages the server requires
foreach (string hash in ContentPackageHashes)
{
if (!myContentPackages.Any(myPackage => myPackage.MD5hash.Hash == hash)) { return false; }
}
//make sure the server isn't missing any of our packages that cause multiplayer incompatibility
foreach (ContentPackage myPackage in myContentPackages)
{
if (myPackage.HasMultiplayerIncompatibleContent)
{
if (!ContentPackageHashes.Any(hash => hash == myPackage.MD5hash.Hash)) { return false; }
}
}
return true;
} }
public bool ContentPackagesMatch(IEnumerable<string> myContentPackageHashes) public bool ContentPackagesMatch(IEnumerable<string> myContentPackageHashes)
@@ -17,7 +17,7 @@ namespace Barotrauma.Networking
public void AssignGUIComponent(GUIComponent component) public void AssignGUIComponent(GUIComponent component)
{ {
GUIComponent = component; GUIComponent = component;
GUIComponentValue = property.GetValue(serverSettings); GUIComponentValue = property.GetValue(parentObject);
TempValue = GUIComponentValue; TempValue = GUIComponentValue;
} }
@@ -30,6 +30,7 @@ namespace Barotrauma.Networking
else if (GUIComponent is GUITextBox textBox) return textBox.Text; else if (GUIComponent is GUITextBox textBox) return textBox.Text;
else if (GUIComponent is GUIScrollBar scrollBar) return scrollBar.BarScrollValue; else if (GUIComponent is GUIScrollBar scrollBar) return scrollBar.BarScrollValue;
else if (GUIComponent is GUIRadioButtonGroup radioButtonGroup) return radioButtonGroup.Selected; else if (GUIComponent is GUIRadioButtonGroup radioButtonGroup) return radioButtonGroup.Selected;
else if (GUIComponent is GUIDropDown dropdown) return dropdown.SelectedData;
return null; return null;
} }
set set
@@ -39,6 +40,7 @@ namespace Barotrauma.Networking
else if (GUIComponent is GUITextBox textBox) textBox.Text = (string)value; else if (GUIComponent is GUITextBox textBox) textBox.Text = (string)value;
else if (GUIComponent is GUIScrollBar scrollBar) scrollBar.BarScrollValue = (float)value; else if (GUIComponent is GUIScrollBar scrollBar) scrollBar.BarScrollValue = (float)value;
else if (GUIComponent is GUIRadioButtonGroup radioButtonGroup) radioButtonGroup.Selected = (Enum)value; else if (GUIComponent is GUIRadioButtonGroup radioButtonGroup) radioButtonGroup.Selected = (Enum)value;
else if (GUIComponent is GUIDropDown dropdown) dropdown.SelectItem(value);
} }
} }
@@ -50,31 +52,6 @@ namespace Barotrauma.Networking
return !PropEquals(TempValue, GUIComponentValue); return !PropEquals(TempValue, GUIComponentValue);
} }
} }
public bool PropEquals(object a,object b)
{
switch (typeString)
{
case "float":
if (!(a is float?)) return false;
if (!(b is float?)) return false;
return (float)a == (float)b;
case "int":
if (!(a is int?)) return false;
if (!(b is int?)) return false;
return (int)a == (int)b;
case "bool":
if (!(a is bool?)) return false;
if (!(b is bool?)) return false;
return (bool)a == (bool)b;
case "Enum":
if (!(a is Enum)) return false;
if (!(b is Enum)) return false;
return ((Enum)a).Equals((Enum)b);
default:
return a.ToString().Equals(b.ToString(),StringComparison.InvariantCulture);
}
}
} }
private Dictionary<string, bool> tempMonsterEnabled; private Dictionary<string, bool> tempMonsterEnabled;
@@ -222,11 +199,15 @@ namespace Barotrauma.Networking
private GUIFrame[] settingsTabs; private GUIFrame[] settingsTabs;
private GUIButton[] tabButtons; private GUIButton[] tabButtons;
private int settingsTabIndex; private int settingsTabIndex;
private GUIDropDown karmaPresetDD;
private GUIComponent karmaSettingsBlocker;
enum SettingsTab enum SettingsTab
{ {
General,
Rounds, Rounds,
Server, Antigriefing,
Banlist, Banlist,
Whitelist Whitelist
} }
@@ -236,6 +217,11 @@ namespace Barotrauma.Networking
return netProperties.First(p => p.Value.Name == name).Value; return netProperties.First(p => p.Value.Name == name).Value;
} }
public void AssignGUIComponent(string propertyName, GUIComponent component)
{
GetPropertyData(propertyName).AssignGUIComponent(component);
}
public void AddToGUIUpdateList() public void AddToGUIUpdateList()
{ {
if (GUI.DisableHUD) return; if (GUI.DisableHUD) return;
@@ -264,7 +250,7 @@ namespace Barotrauma.Networking
}; };
//center frames //center frames
GUIFrame innerFrame = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.7f), settingsFrame.RectTransform, Anchor.Center) { MinSize = new Point(400, 430) }); GUIFrame innerFrame = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.75f), settingsFrame.RectTransform, Anchor.Center) { MinSize = new Point(400, 430) });
GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), innerFrame.RectTransform, Anchor.Center), style: null); GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), innerFrame.RectTransform, Anchor.Center), style: null);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), paddedFrame.RectTransform), TextManager.Get("Settings"), font: GUI.LargeFont); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), paddedFrame.RectTransform), TextManager.Get("Settings"), font: GUI.LargeFont);
@@ -304,18 +290,21 @@ namespace Barotrauma.Networking
OnClicked = ToggleSettingsFrame OnClicked = ToggleSettingsFrame
}; };
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// game settings // server settings
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
var roundsTab = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), settingsTabs[(int)SettingsTab.Rounds].RectTransform, Anchor.Center)) var serverTab = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), settingsTabs[(int)SettingsTab.General].RectTransform, Anchor.Center))
{ {
Stretch = true, Stretch = true,
RelativeSpacing = 0.02f RelativeSpacing = 0.02f
}; };
//***********************************************
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsSubSelection")); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsSubSelection"));
var selectionFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), isHorizontal: true) var selectionFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), isHorizontal: true)
{ {
Stretch = true, Stretch = true,
RelativeSpacing = 0.05f RelativeSpacing = 0.05f
@@ -327,11 +316,11 @@ namespace Barotrauma.Networking
var selectionTick = new GUITickBox(new RectTransform(new Vector2(0.3f, 1.0f), selectionFrame.RectTransform), TextManager.Get(((SelectionMode)i).ToString()), font: GUI.SmallFont); var selectionTick = new GUITickBox(new RectTransform(new Vector2(0.3f, 1.0f), selectionFrame.RectTransform), TextManager.Get(((SelectionMode)i).ToString()), font: GUI.SmallFont);
selectionMode.AddRadioButton((SelectionMode)i, selectionTick); selectionMode.AddRadioButton((SelectionMode)i, selectionTick);
} }
DebugConsole.NewMessage(SubSelectionMode.ToString(),Color.White); DebugConsole.NewMessage(SubSelectionMode.ToString(), Color.White);
GetPropertyData("SubSelectionMode").AssignGUIComponent(selectionMode); GetPropertyData("SubSelectionMode").AssignGUIComponent(selectionMode);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsModeSelection")); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsModeSelection"));
selectionFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), isHorizontal: true) selectionFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), isHorizontal: true)
{ {
Stretch = true, Stretch = true,
RelativeSpacing = 0.05f RelativeSpacing = 0.05f
@@ -345,6 +334,84 @@ namespace Barotrauma.Networking
} }
GetPropertyData("ModeSelectionMode").AssignGUIComponent(selectionMode); GetPropertyData("ModeSelectionMode").AssignGUIComponent(selectionMode);
//***********************************************
var voiceChatEnabled = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform),
TextManager.Get("ServerSettingsVoiceChatEnabled"));
GetPropertyData("VoiceChatEnabled").AssignGUIComponent(voiceChatEnabled);
//***********************************************
string autoRestartDelayLabel = TextManager.Get("ServerSettingsAutoRestartDelay") + " ";
var startIntervalText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), autoRestartDelayLabel);
var startIntervalSlider = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), barSize: 0.1f)
{
UserData = startIntervalText,
Step = 0.05f,
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
GUITextBlock text = scrollBar.UserData as GUITextBlock;
text.Text = autoRestartDelayLabel + ToolBox.SecondsToReadableTime(scrollBar.BarScrollValue);
return true;
}
};
startIntervalSlider.Range = new Vector2(10.0f, 300.0f);
GetPropertyData("AutoRestartInterval").AssignGUIComponent(startIntervalSlider);
startIntervalSlider.OnMoved(startIntervalSlider, startIntervalSlider.BarScroll);
//***********************************************
var startWhenClientsReady = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform),
TextManager.Get("ServerSettingsStartWhenClientsReady"));
GetPropertyData("StartWhenClientsReady").AssignGUIComponent(startWhenClientsReady);
CreateLabeledSlider(serverTab, "ServerSettingsStartWhenClientsReadyRatio", out GUIScrollBar slider, out GUITextBlock sliderLabel);
string clientsReadyRequiredLabel = sliderLabel.Text;
slider.Step = 0.2f;
slider.Range = new Vector2(0.5f, 1.0f);
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
((GUITextBlock)scrollBar.UserData).Text = clientsReadyRequiredLabel.Replace("[percentage]", ((int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f)).ToString());
return true;
};
GetPropertyData("StartWhenClientsReadyRatio").AssignGUIComponent(slider);
slider.OnMoved(slider, slider.BarScroll);
//***********************************************
var allowSpecBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsAllowSpectating"));
GetPropertyData("AllowSpectating").AssignGUIComponent(allowSpecBox);
var shareSubsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsShareSubFiles"));
GetPropertyData("AllowFileTransfers").AssignGUIComponent(shareSubsBox);
var randomizeLevelBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsRandomizeSeed"));
GetPropertyData("RandomizeSeed").AssignGUIComponent(randomizeLevelBox);
var saveLogsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsSaveLogs"))
{
OnSelected = (GUITickBox) =>
{
//TODO: fix?
//showLogButton.Visible = SaveServerLogs;
return true;
}
};
GetPropertyData("SaveServerLogs").AssignGUIComponent(saveLogsBox);
//--------------------------------------------------------------------------------
// game settings
//--------------------------------------------------------------------------------
var roundsTab = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), settingsTabs[(int)SettingsTab.Rounds].RectTransform, Anchor.Center))
{
Stretch = true,
RelativeSpacing = 0.02f
};
var endBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), var endBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform),
TextManager.Get("ServerSettingsEndRoundWhenDestReached")); TextManager.Get("ServerSettingsEndRoundWhenDestReached"));
GetPropertyData("EndRoundAtLevelEnd").AssignGUIComponent(endBox); GetPropertyData("EndRoundAtLevelEnd").AssignGUIComponent(endBox);
@@ -353,7 +420,7 @@ namespace Barotrauma.Networking
TextManager.Get("ServerSettingsEndRoundVoting")); TextManager.Get("ServerSettingsEndRoundVoting"));
GetPropertyData("AllowEndVoting").AssignGUIComponent(endVoteBox); GetPropertyData("AllowEndVoting").AssignGUIComponent(endVoteBox);
CreateLabeledSlider(roundsTab, "ServerSettingsEndRoundVotesRequired", out GUIScrollBar slider, out GUITextBlock sliderLabel); CreateLabeledSlider(roundsTab, "ServerSettingsEndRoundVotesRequired", out slider, out sliderLabel);
string endRoundLabel = sliderLabel.Text; string endRoundLabel = sliderLabel.Text;
slider.Step = 0.2f; slider.Step = 0.2f;
@@ -361,7 +428,7 @@ namespace Barotrauma.Networking
GetPropertyData("EndVoteRequiredRatio").AssignGUIComponent(slider); GetPropertyData("EndVoteRequiredRatio").AssignGUIComponent(slider);
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) => slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{ {
((GUITextBlock)scrollBar.UserData).Text = endRoundLabel + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f) + " %"; ((GUITextBlock)scrollBar.UserData).Text = endRoundLabel + " " + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f) + " %";
return true; return true;
}; };
slider.OnMoved(slider, slider.BarScroll); slider.OnMoved(slider, slider.BarScroll);
@@ -378,7 +445,7 @@ namespace Barotrauma.Networking
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) => slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{ {
GUITextBlock text = scrollBar.UserData as GUITextBlock; GUITextBlock text = scrollBar.UserData as GUITextBlock;
text.Text = intervalLabel + ToolBox.SecondsToReadableTime(scrollBar.BarScrollValue); text.Text = intervalLabel + " " + ToolBox.SecondsToReadableTime(scrollBar.BarScrollValue);
return true; return true;
}; };
slider.OnMoved(slider, slider.BarScroll); slider.OnMoved(slider, slider.BarScroll);
@@ -437,6 +504,57 @@ namespace Barotrauma.Networking
}; };
slider.OnMoved(slider, slider.BarScroll); slider.OnMoved(slider, slider.BarScroll);
var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton"));
GetPropertyData("AllowRagdollButton").AssignGUIComponent(ragdollButtonBox);
var traitorRatioBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsUseTraitorRatio"));
CreateLabeledSlider(roundsTab, "", out slider, out sliderLabel);
var traitorRatioSlider = slider;
traitorRatioBox.OnSelected = (GUITickBox) =>
{
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
return true;
};
if (TraitorUseRatio)
{
traitorRatioSlider.Range = new Vector2(0.1f, 1.0f);
}
else
{
traitorRatioSlider.Range = new Vector2(1.0f, maxPlayers);
}
string traitorRatioLabel = TextManager.Get("ServerSettingsTraitorRatio") + " ";
string traitorCountLabel = TextManager.Get("ServerSettingsTraitorCount") + " ";
traitorRatioSlider.Range = new Vector2(0.1f, 1.0f);
traitorRatioSlider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
GUITextBlock traitorText = scrollBar.UserData as GUITextBlock;
if (traitorRatioBox.Selected)
{
scrollBar.Step = 0.01f;
scrollBar.Range = new Vector2(0.1f, 1.0f);
traitorText.Text = traitorRatioLabel + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 1.0f) + " %";
}
else
{
scrollBar.Step = 1f / (maxPlayers - 1);
scrollBar.Range = new Vector2(1.0f, maxPlayers);
traitorText.Text = traitorCountLabel + scrollBar.BarScrollValue;
}
return true;
};
GetPropertyData("TraitorUseRatio").AssignGUIComponent(traitorRatioBox);
GetPropertyData("TraitorRatio").AssignGUIComponent(traitorRatioSlider);
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
traitorRatioBox.OnSelected(traitorRatioBox);
var buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), roundsTab.RectTransform), isHorizontal: true) var buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), roundsTab.RectTransform), isHorizontal: true)
{ {
Stretch = true, Stretch = true,
@@ -558,68 +676,21 @@ namespace Barotrauma.Networking
}; };
} }
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// server settings // antigriefing
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
var serverTab = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), settingsTabs[(int)SettingsTab.Server].RectTransform, Anchor.Center)) var antigriefingTab = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), settingsTabs[(int)SettingsTab.Antigriefing].RectTransform, Anchor.Center))
{ {
Stretch = true, Stretch = true,
RelativeSpacing = 0.02f RelativeSpacing = 0.02f
}; };
//*********************************************** var voteKickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), antigriefingTab.RectTransform), TextManager.Get("ServerSettingsAllowVoteKick"));
var voiceChatEnabled = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform),
TextManager.Get("ServerSettingsVoiceChatEnabled"));
GetPropertyData("VoiceChatEnabled").AssignGUIComponent(voiceChatEnabled);
//***********************************************
string autoRestartDelayLabel = TextManager.Get("ServerSettingsAutoRestartDelay") + " ";
var startIntervalText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), autoRestartDelayLabel);
var startIntervalSlider = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), barSize: 0.1f)
{
UserData = startIntervalText,
Step = 0.05f,
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
GUITextBlock text = scrollBar.UserData as GUITextBlock;
text.Text = autoRestartDelayLabel + ToolBox.SecondsToReadableTime(scrollBar.BarScrollValue);
return true;
}
};
startIntervalSlider.Range = new Vector2(10.0f, 300.0f);
GetPropertyData("AutoRestartInterval").AssignGUIComponent(startIntervalSlider);
startIntervalSlider.OnMoved(startIntervalSlider, startIntervalSlider.BarScroll);
//***********************************************
var startWhenClientsReady = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform),
TextManager.Get("ServerSettingsStartWhenClientsReady"));
GetPropertyData("StartWhenClientsReady").AssignGUIComponent(startWhenClientsReady);
CreateLabeledSlider(serverTab, "ServerSettingsStartWhenClientsReadyRatio", out slider, out sliderLabel);
string clientsReadyRequiredLabel = sliderLabel.Text;
slider.Step = 0.2f;
slider.Range = new Vector2(0.5f, 1.0f);
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
((GUITextBlock)scrollBar.UserData).Text = clientsReadyRequiredLabel.Replace("[percentage]", ((int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f)).ToString());
return true;
};
GetPropertyData("StartWhenClientsReadyRatio").AssignGUIComponent(slider);
slider.OnMoved(slider, slider.BarScroll);
//***********************************************
var allowSpecBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsAllowSpectating"));
GetPropertyData("AllowSpectating").AssignGUIComponent(allowSpecBox);
var voteKickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsAllowVoteKick"));
GetPropertyData("AllowVoteKick").AssignGUIComponent(voteKickBox); GetPropertyData("AllowVoteKick").AssignGUIComponent(voteKickBox);
CreateLabeledSlider(serverTab, "ServerSettingsKickVotesRequired", out slider, out sliderLabel); CreateLabeledSlider(antigriefingTab, "ServerSettingsKickVotesRequired", out slider, out sliderLabel);
string votesRequiredLabel = sliderLabel.Text + " "; string votesRequiredLabel = sliderLabel.Text + " ";
slider.Step = 0.2f; slider.Step = 0.2f;
slider.Range = new Vector2(0.5f, 1.0f); slider.Range = new Vector2(0.5f, 1.0f);
@@ -631,9 +702,9 @@ namespace Barotrauma.Networking
GetPropertyData("KickVoteRequiredRatio").AssignGUIComponent(slider); GetPropertyData("KickVoteRequiredRatio").AssignGUIComponent(slider);
slider.OnMoved(slider, slider.BarScroll); slider.OnMoved(slider, slider.BarScroll);
CreateLabeledSlider(serverTab, "ServerSettingsAutobanTime", out slider, out sliderLabel); CreateLabeledSlider(antigriefingTab, "ServerSettingsAutobanTime", out slider, out sliderLabel);
string autobanLabel = sliderLabel.Text + " "; string autobanLabel = sliderLabel.Text + " ";
slider.Step = 0.05f; slider.Step = 0.01f;
slider.Range = new Vector2(0.0f, MaxAutoBanTime); slider.Range = new Vector2(0.0f, MaxAutoBanTime);
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) => slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{ {
@@ -643,91 +714,66 @@ namespace Barotrauma.Networking
GetPropertyData("AutoBanTime").AssignGUIComponent(slider); GetPropertyData("AutoBanTime").AssignGUIComponent(slider);
slider.OnMoved(slider, slider.BarScroll); slider.OnMoved(slider, slider.BarScroll);
var shareSubsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsShareSubFiles")); // karma --------------------------------------------------------------------------
GetPropertyData("AllowFileTransfers").AssignGUIComponent(shareSubsBox);
var randomizeLevelBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsRandomizeSeed")); var karmaBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), antigriefingTab.RectTransform), TextManager.Get("ServerSettingsUseKarma"));
GetPropertyData("RandomizeSeed").AssignGUIComponent(randomizeLevelBox);
var saveLogsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsSaveLogs"))
{
OnSelected = (GUITickBox) =>
{
//TODO: fix?
//showLogButton.Visible = SaveServerLogs;
return true;
}
};
GetPropertyData("SaveServerLogs").AssignGUIComponent(saveLogsBox);
var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton"));
GetPropertyData("AllowRagdollButton").AssignGUIComponent(ragdollButtonBox);
var traitorRatioBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsUseTraitorRatio"));
CreateLabeledSlider(serverTab, "", out slider, out sliderLabel);
/*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]);*/
var traitorRatioSlider = slider;
traitorRatioBox.OnSelected = (GUITickBox) =>
{
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
return true;
};
if (TraitorUseRatio)
{
traitorRatioSlider.Range = new Vector2(0.1f, 1.0f);
}
else
{
traitorRatioSlider.Range = new Vector2(1.0f, maxPlayers);
}
string traitorRatioLabel = TextManager.Get("ServerSettingsTraitorRatio") + " ";
string traitorCountLabel = TextManager.Get("ServerSettingsTraitorCount") + " ";
traitorRatioSlider.Range = new Vector2(0.1f, 1.0f);
traitorRatioSlider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
GUITextBlock traitorText = scrollBar.UserData as GUITextBlock;
if (traitorRatioBox.Selected)
{
scrollBar.Step = 0.01f;
scrollBar.Range = new Vector2(0.1f, 1.0f);
traitorText.Text = traitorRatioLabel + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 1.0f) + " %";
}
else
{
scrollBar.Step = 1f / (maxPlayers - 1);
scrollBar.Range = new Vector2(1.0f, maxPlayers);
traitorText.Text = traitorCountLabel + scrollBar.BarScrollValue;
}
return true;
};
GetPropertyData("TraitorUseRatio").AssignGUIComponent(traitorRatioBox);
GetPropertyData("TraitorRatio").AssignGUIComponent(traitorRatioSlider);
traitorRatioSlider.OnMoved(traitorRatioSlider, traitorRatioSlider.BarScroll);
traitorRatioBox.OnSelected(traitorRatioBox);
var karmaBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform), TextManager.Get("ServerSettingsUseKarma"));
GetPropertyData("KarmaEnabled").AssignGUIComponent(karmaBox); GetPropertyData("KarmaEnabled").AssignGUIComponent(karmaBox);
karmaPresetDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), antigriefingTab.RectTransform));
foreach (string karmaPreset in GameMain.NetworkMember.KarmaManager.Presets.Keys)
{
karmaPresetDD.AddItem(TextManager.Get("KarmaPreset." + karmaPreset), karmaPreset);
}
var karmaSettingsContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), antigriefingTab.RectTransform), style: null);
var karmaSettingsList = new GUIListBox(new RectTransform(Vector2.One, karmaSettingsContainer.RectTransform));
karmaSettingsBlocker = new GUIFrame(new RectTransform(Vector2.One, karmaSettingsContainer.RectTransform, Anchor.CenterLeft) { MaxSize = new Point(karmaSettingsList.Content.Rect.Width, int.MaxValue) },
style: "InnerFrame");
karmaPresetDD.OnSelected = (selected, obj) =>
{
List<NetPropertyData> properties = netProperties.Values.ToList();
List<object> prevValues = new List<object>();
foreach (NetPropertyData prop in netProperties.Values)
{
prevValues.Add(prop.TempValue);
if (prop.GUIComponent != null) { prop.Value = prop.GUIComponentValue; }
}
if (KarmaPreset == "custom")
{
GameMain.NetworkMember?.KarmaManager?.SaveCustomPreset();
GameMain.NetworkMember?.KarmaManager?.Save();
}
KarmaPreset = obj as string;
GameMain.NetworkMember.KarmaManager.SelectPreset(KarmaPreset);
karmaSettingsList.Content.ClearChildren();
karmaSettingsBlocker.Visible = !karmaBox.Selected || KarmaPreset != "custom";
GameMain.NetworkMember.KarmaManager.CreateSettingsFrame(karmaSettingsList.Content);
for (int i = 0; i < netProperties.Count; i++)
{
properties[i].TempValue = prevValues[i];
}
return true;
};
karmaPresetDD.SelectItem(KarmaPreset);
AssignGUIComponent("KarmaPreset", karmaPresetDD);
karmaBox.OnSelected = (tb) =>
{
karmaSettingsBlocker.Visible = !tb.Selected || KarmaPreset != "custom";
return true;
};
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// banlist // banlist
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
BanList.CreateBanFrame(settingsTabs[2]); BanList.CreateBanFrame(settingsTabs[(int)SettingsTab.Banlist]);
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// whitelist // whitelist
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
Whitelist.CreateWhiteListFrame(settingsTabs[3]); Whitelist.CreateWhiteListFrame(settingsTabs[(int)SettingsTab.Whitelist]);
} }
private void CreateLabeledSlider(GUIComponent parent, string labelTag, out GUIScrollBar slider, out GUITextBlock label) private void CreateLabeledSlider(GUIComponent parent, string labelTag, out GUIScrollBar slider, out GUITextBlock label)
@@ -763,6 +809,11 @@ namespace Barotrauma.Networking
{ {
if (settingsFrame == null) if (settingsFrame == null)
{ {
if (KarmaPreset == "custom")
{
GameMain.NetworkMember?.KarmaManager?.SaveCustomPreset();
GameMain.NetworkMember?.KarmaManager?.Save();
}
CreateSettingsFrame(); CreateSettingsFrame();
} }
else else
@@ -777,44 +828,5 @@ namespace Barotrauma.Networking
return false; return false;
} }
public void ManagePlayersFrame(GUIFrame infoFrame)
{
GUIListBox cList = new GUIListBox(new RectTransform(Vector2.One, infoFrame.RectTransform));
/*foreach (Client c in ConnectedClients)
{
var frame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), cList.Content.RectTransform),
c.Name + " (" + c.Connection.RemoteEndPoint.Address.ToString() + ")", style: "ListBoxElement")
{
Color = (c.InGame && c.Character != null && !c.Character.IsDead) ? Color.Gold * 0.2f : Color.Transparent,
HoverColor = Color.LightGray * 0.5f,
SelectedColor = Color.Gold * 0.5f
};
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.85f), frame.RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.05f, 0.0f) },
isHorizontal: true);
var kickButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonArea.RectTransform),
TextManager.Get("Kick"))
{
UserData = c.Name,
OnClicked = GameMain.NetLobbyScreen.KickPlayer
};
var banButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonArea.RectTransform),
TextManager.Get("Ban"))
{
UserData = c.Name,
OnClicked = GameMain.NetLobbyScreen.BanPlayer
};
var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonArea.RectTransform),
TextManager.Get("BanRange"))
{
UserData = c.Name,
OnClicked = GameMain.NetLobbyScreen.BanPlayerRange
};
}*/ //TODO: reimplement
}
} }
} }
@@ -597,6 +597,8 @@ namespace Barotrauma.Steam
if (string.IsNullOrEmpty(item.Error)) if (string.IsNullOrEmpty(item.Error))
{ {
DebugConsole.NewMessage("Published workshop item " + item.Title + " successfully.", Microsoft.Xna.Framework.Color.LightGreen); DebugConsole.NewMessage("Published workshop item " + item.Title + " successfully.", Microsoft.Xna.Framework.Color.LightGreen);
var newItem = instance.client.Workshop.GetItem(item.Id);
newItem?.Subscribe();
} }
else else
{ {
@@ -56,6 +56,8 @@ namespace Barotrauma.Particles
private int animFrame; private int animFrame;
private float collisionUpdateTimer; private float collisionUpdateTimer;
public bool HighQualityCollisionDetection;
public ParticlePrefab.DrawTargetType DrawTarget public ParticlePrefab.DrawTargetType DrawTarget
{ {
@@ -133,6 +135,8 @@ namespace Barotrauma.Particles
velocityChange = prefab.VelocityChangeDisplay; velocityChange = prefab.VelocityChangeDisplay;
velocityChangeWater = prefab.VelocityChangeWaterDisplay; velocityChangeWater = prefab.VelocityChangeWaterDisplay;
HighQualityCollisionDetection = false;
OnChangeHull = null; OnChangeHull = null;
subEmitters.Clear(); subEmitters.Clear();
@@ -224,7 +228,7 @@ namespace Barotrauma.Particles
} }
lifeTime -= deltaTime; lifeTime -= deltaTime;
if (lifeTime <= 0.0f || color.A <= 0 || size.X <= 0.0f || size.Y <= 0.0f) return false; if (lifeTime <= 0.0f || color.A <= 0 || size.X <= 0.0f || size.Y <= 0.0f) { return false; }
if (hasSubEmitters) if (hasSubEmitters)
{ {
@@ -234,16 +238,23 @@ namespace Barotrauma.Particles
} }
} }
if (!prefab.UseCollision) return true; if (!prefab.UseCollision) { return true; }
collisionUpdateTimer -= deltaTime; if (HighQualityCollisionDetection)
if (collisionUpdateTimer <= 0.0f)
{ {
//more frequent collision updates if the particle is moving fast
collisionUpdateTimer = 0.5f - Math.Min((Math.Abs(velocity.X) + Math.Abs(velocity.Y)) * 0.001f, 0.4f);
return CollisionUpdate(); return CollisionUpdate();
} }
else
{
collisionUpdateTimer -= deltaTime;
if (collisionUpdateTimer <= 0.0f)
{
//more frequent collision updates if the particle is moving fast
collisionUpdateTimer = 0.5f - Math.Min((Math.Abs(velocity.X) + Math.Abs(velocity.Y)) * 0.01f, 0.45f);
return CollisionUpdate();
}
}
return true; return true;
} }
@@ -288,7 +299,7 @@ namespace Barotrauma.Particles
bool gapFound = false; bool gapFound = false;
foreach (Gap gap in hullGaps) foreach (Gap gap in hullGaps)
{ {
if (gap.Open <= 0.0f || gap.IsHorizontal != (collisionNormal.X != 0.0f)) continue; if (gap.Open <= 0.9f || gap.IsHorizontal != (collisionNormal.X != 0.0f)) continue;
if (gap.IsHorizontal) if (gap.IsHorizontal)
{ {
@@ -56,6 +56,7 @@ namespace Barotrauma.Particles
if (particle != null) if (particle != null)
{ {
particle.Size *= Rand.Range(Prefab.ScaleMin, Prefab.ScaleMax); particle.Size *= Rand.Range(Prefab.ScaleMin, Prefab.ScaleMax);
particle.HighQualityCollisionDetection = Prefab.HighQualityCollisionDetection;
} }
} }
@@ -98,6 +99,8 @@ namespace Barotrauma.Particles
public readonly float ParticlesPerSecond; public readonly float ParticlesPerSecond;
public readonly bool HighQualityCollisionDetection;
public readonly bool CopyEntityAngle; public readonly bool CopyEntityAngle;
public ParticleEmitterPrefab(XElement element) public ParticleEmitterPrefab(XElement element)
@@ -145,7 +148,7 @@ namespace Barotrauma.Particles
EmitInterval = element.GetAttributeFloat("emitinterval", 0.0f); EmitInterval = element.GetAttributeFloat("emitinterval", 0.0f);
ParticlesPerSecond = element.GetAttributeInt("particlespersecond", 0); ParticlesPerSecond = element.GetAttributeInt("particlespersecond", 0);
ParticleAmount = element.GetAttributeInt("particleamount", 0); ParticleAmount = element.GetAttributeInt("particleamount", 0);
HighQualityCollisionDetection = element.GetAttributeBool("highqualitycollisiondetection", false);
CopyEntityAngle = element.GetAttributeBool("copyentityangle", false); CopyEntityAngle = element.GetAttributeBool("copyentityangle", false);
} }
} }
@@ -45,7 +45,7 @@ namespace Barotrauma
{ {
if (!body.Enabled) if (!body.Enabled)
{ {
color = Color.Gray; color = Color.Black;
} }
else if (!body.Awake) else if (!body.Awake)
{ {
@@ -267,6 +267,17 @@ namespace Barotrauma
sb.AppendLine(exception.StackTrace); sb.AppendLine(exception.StackTrace);
sb.AppendLine("\n"); sb.AppendLine("\n");
if (exception.InnerException != null)
{
sb.AppendLine("InnerException: " + exception.InnerException.Message);
if (exception.InnerException.TargetSite != null)
{
sb.AppendLine("Target site: " + exception.InnerException.TargetSite.ToString());
}
sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.InnerException.StackTrace);
}
sb.AppendLine("Last debug messages:"); sb.AppendLine("Last debug messages:");
for (int i = DebugConsole.Messages.Count - 1; i >= 0; i--) for (int i = DebugConsole.Messages.Count - 1; i >= 0; i--)
{ {
@@ -2788,6 +2788,7 @@ namespace Barotrauma
if (editRagdoll || !editLimbs && !editJoints) if (editRagdoll || !editLimbs && !editJoints)
{ {
RagdollParams.AddToEditor(ParamsEditor.Instance, alsoChildren: false); RagdollParams.AddToEditor(ParamsEditor.Instance, alsoChildren: false);
RagdollParams.ColliderParams.ForEach(c => c.AddToEditor(ParamsEditor.Instance));
} }
if (editJoints) if (editJoints)
{ {
@@ -3041,14 +3042,16 @@ namespace Barotrauma
private void CalculateSpritesheetZoom() private void CalculateSpritesheetZoom()
{ {
float width = textures.OrderByDescending(t => t.Width).First().Width; var texture = textures.OrderByDescending(t => t.Width).FirstOrDefault();
if (texture == null)
{
spriteSheetZoom = 1;
return;
}
float width = texture.Width;
float height = textures.Sum(t => t.Height); float height = textures.Sum(t => t.Height);
float margin = 20; float margin = 20;
if (textures == null || textures.None()) if (height > width)
{
spriteSheetMaxZoom = 1;
}
else if (height > width)
{ {
spriteSheetMaxZoom = (centerArea.Rect.Bottom - spriteSheetOffsetY - margin) / height; spriteSheetMaxZoom = (centerArea.Rect.Bottom - spriteSheetOffsetY - margin) / height;
} }
@@ -415,9 +415,22 @@ namespace Barotrauma
ServerInfo s1 = c1.GUIComponent.UserData as ServerInfo; ServerInfo s1 = c1.GUIComponent.UserData as ServerInfo;
ServerInfo s2 = c2.GUIComponent.UserData as ServerInfo; ServerInfo s2 = c2.GUIComponent.UserData as ServerInfo;
if (s1 == null && s2 == null)
{
return 0;
}
else if (s1 == null)
{
return ascending ? 1 : -1;
}
else if (s2 == null)
{
return ascending ? -1 : 1;
}
switch (sortBy) switch (sortBy)
{ {
case "ServerListCompatible": case "ServerListCompatible":
bool? s1Compatible = NetworkMember.IsCompatible(GameMain.Version.ToString(), s1.GameVersion); bool? s1Compatible = NetworkMember.IsCompatible(GameMain.Version.ToString(), s1.GameVersion);
if (!s1.ContentPackageHashes.Any()) { s1Compatible = null; } if (!s1.ContentPackageHashes.Any()) { s1Compatible = null; }
if (s1Compatible.HasValue) { s1Compatible = s1Compatible.Value && s1.ContentPackagesMatch(GameMain.SelectedPackages); }; if (s1Compatible.HasValue) { s1Compatible = s1Compatible.Value && s1.ContentPackagesMatch(GameMain.SelectedPackages); };
@@ -441,7 +454,7 @@ namespace Barotrauma
if (s1.HasPassword == s2.HasPassword) { return 0; } if (s1.HasPassword == s2.HasPassword) { return 0; }
return (s1.HasPassword ? 1 : -1) * (ascending ? 1 : -1); return (s1.HasPassword ? 1 : -1) * (ascending ? 1 : -1);
case "ServerListName": case "ServerListName":
return s1.ServerName.CompareTo(s2.ServerName) * (ascending ? 1 : -1); return string.Compare(s1.ServerName, s2.ServerName) * (ascending ? 1 : -1);
case "ServerListRoundStarted": case "ServerListRoundStarted":
if (s1.GameStarted == s2.GameStarted) { return 0; } if (s1.GameStarted == s2.GameStarted) { return 0; }
return (s1.GameStarted ? 1 : -1) * (ascending ? 1 : -1); return (s1.GameStarted ? 1 : -1) * (ascending ? 1 : -1);
@@ -20,6 +20,8 @@ namespace Barotrauma
private GUIFrame bottomPanel; private GUIFrame bottomPanel;
private GUIFrame backgroundColorPanel; private GUIFrame backgroundColorPanel;
private bool drawGrid, snapToGrid;
private GUIFrame topPanelContents; private GUIFrame topPanelContents;
private GUITextBlock texturePathText; private GUITextBlock texturePathText;
private GUITextBlock xmlPathText; private GUITextBlock xmlPathText;
@@ -137,7 +139,7 @@ namespace Barotrauma
return true; return true;
} }
}; };
new GUIButton(new RectTransform(new Vector2(0.05f, 0.35f), topPanelContents.RectTransform, Anchor.TopCenter, Pivot.CenterLeft) { RelativeOffset = new Vector2(0.055f, 0.3f) }, "Reset Zoom") var resetBtn = new GUIButton(new RectTransform(new Vector2(0.05f, 0.35f), topPanelContents.RectTransform, Anchor.TopCenter, Pivot.CenterLeft) { RelativeOffset = new Vector2(0.055f, 0.3f) }, "Reset Zoom")
{ {
OnClicked = (box, data) => OnClicked = (box, data) =>
{ {
@@ -145,6 +147,26 @@ namespace Barotrauma
return true; return true;
} }
}; };
resetBtn.TextBlock.AutoScale = true;
new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), topPanelContents.RectTransform, Anchor.BottomCenter, Pivot.CenterRight) { RelativeOffset = new Vector2(0, 0.3f) }, "Show grid")
{
Selected = drawGrid,
OnSelected = (tickBox) =>
{
drawGrid = tickBox.Selected;
return true;
}
};
new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), topPanelContents.RectTransform, Anchor.BottomCenter, Pivot.CenterRight) { RelativeOffset = new Vector2(0.17f, 0.3f) }, "Snap to grid")
{
Selected = snapToGrid,
OnSelected = (tickBox) =>
{
snapToGrid = tickBox.Selected;
return true;
}
};
texturePathText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.4f), topPanelContents.RectTransform, Anchor.Center, Pivot.BottomCenter) { RelativeOffset = new Vector2(0.4f, 0) }, "", Color.LightGray); texturePathText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.4f), topPanelContents.RectTransform, Anchor.Center, Pivot.BottomCenter) { RelativeOffset = new Vector2(0.4f, 0) }, "", Color.LightGray);
xmlPathText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.4f), topPanelContents.RectTransform, Anchor.Center, Pivot.TopCenter) { RelativeOffset = new Vector2(0.4f, 0) }, "", Color.LightGray); xmlPathText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.4f), topPanelContents.RectTransform, Anchor.Center, Pivot.TopCenter) { RelativeOffset = new Vector2(0.4f, 0) }, "", Color.LightGray);
@@ -230,8 +252,7 @@ namespace Barotrauma
}, style: null, color: Color.Black * 0.6f); }, style: null, color: Color.Black * 0.6f);
var colorLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), element.RectTransform, Anchor.CenterLeft), colorComponentLabels[i], var colorLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), element.RectTransform, Anchor.CenterLeft), colorComponentLabels[i],
font: GUI.SmallFont, textAlignment: Alignment.CenterLeft); font: GUI.SmallFont, textAlignment: Alignment.CenterLeft);
GUINumberInput numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight), var numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight), GUINumberInput.NumberType.Int)
GUINumberInput.NumberType.Int)
{ {
Font = GUI.SmallFont Font = GUI.SmallFont
}; };
@@ -259,26 +280,32 @@ namespace Barotrauma
} }
} }
private HashSet<Sprite> loadedSprites = new HashSet<Sprite>(); private readonly HashSet<Sprite> loadedSprites = new HashSet<Sprite>();
private void LoadSprites() private void LoadSprites()
{ {
loadedSprites.ForEach(s => s.Remove()); loadedSprites.ForEach(s => s.Remove());
loadedSprites.Clear(); loadedSprites.Clear();
//foreach (string filePath in ContentPackage.GetAllContentFiles(GameMain.SelectedPackages)) var contentPackages = GameMain.Config.SelectedContentPackages.ToList();
//{
// XDocument doc = XMLExtensions.TryLoadXml(filePath);
// if (doc != null && doc.Root != null)
// {
// LoadSprites(doc.Root);
// }
//}
foreach (string filePath in Directory.GetFiles("Content/", "*.xml", SearchOption.AllDirectories)) #if !DEBUG
var vanilla = GameMain.VanillaContent;
if (vanilla != null)
{ {
XDocument doc = XMLExtensions.TryLoadXml(filePath); contentPackages.Remove(vanilla);
if (doc != null && doc.Root != null) }
#endif
foreach (var contentPackage in contentPackages)
{
foreach (var file in contentPackage.Files)
{ {
LoadSprites(doc.Root); if (file.Path.EndsWith(".xml"))
{
XDocument doc = XMLExtensions.TryLoadXml(file.Path);
if (doc != null && doc.Root != null)
{
LoadSprites(doc.Root);
}
}
} }
} }
@@ -304,11 +331,12 @@ namespace Barotrauma
{ {
string spriteFolder = ""; string spriteFolder = "";
string textureElement = element.GetAttributeString("texture", ""); string textureElement = element.GetAttributeString("texture", "");
// TODO: parse and create // TODO: parse and create?
if (textureElement.Contains("[GENDER]") || textureElement.Contains("[HEADID]") || textureElement.Contains("[RACE]")) { return; } if (textureElement.Contains("[GENDER]") || textureElement.Contains("[HEADID]") || textureElement.Contains("[RACE]")) { return; }
if (!textureElement.Contains("/")) if (!textureElement.Contains("/"))
{ {
spriteFolder = Path.GetDirectoryName(element.ParseContentPathFromUri()); var parsedPath = element.ParseContentPathFromUri();
spriteFolder = Path.GetDirectoryName(parsedPath);
} }
// Uncomment if we do multiple passes -> there can be duplicates // Uncomment if we do multiple passes -> there can be duplicates
//string identifier = Sprite.GetID(element); //string identifier = Sprite.GetID(element);
@@ -344,7 +372,7 @@ namespace Barotrauma
xmlPathText.TextColor = Color.LightGreen; xmlPathText.TextColor = Color.LightGreen;
return true; return true;
} }
#endregion #endregion
#region Public methods #region Public methods
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
@@ -466,6 +494,11 @@ namespace Barotrauma
//GUI.DrawRectangle(spriteBatch, viewArea, Color.Green, isFilled: false); //GUI.DrawRectangle(spriteBatch, viewArea, Color.Green, isFilled: false);
GUI.DrawRectangle(spriteBatch, textureRect, Color.Gray, isFilled: false); GUI.DrawRectangle(spriteBatch, textureRect, Color.Gray, isFilled: false);
if (drawGrid)
{
DrawGrid(spriteBatch, textureRect, zoom, Submarine.GridSize);
}
foreach (GUIComponent element in spriteList.Content.Children) foreach (GUIComponent element in spriteList.Content.Children)
{ {
Sprite sprite = element.UserData as Sprite; Sprite sprite = element.UserData as Sprite;
@@ -507,8 +540,11 @@ namespace Barotrauma
w.tooltip = $"Position: {sprite.SourceRect.Location}"; w.tooltip = $"Position: {sprite.SourceRect.Location}";
w.MouseHeld += dTime => w.MouseHeld += dTime =>
{ {
w.DrawPos = PlayerInput.MousePosition; w.DrawPos = (drawGrid && snapToGrid) ?
sprite.SourceRect = new Rectangle(((w.DrawPos + new Vector2(w.size / 2) - textureRect.Location.ToVector2()) / zoom).ToPoint(), sprite.SourceRect.Size); SnapToGrid(PlayerInput.MousePosition, textureRect, zoom, Submarine.GridSize, Submarine.GridSize.X / 4.0f * zoom) :
PlayerInput.MousePosition;
w.DrawPos = new Vector2((float)Math.Ceiling(w.DrawPos.X), (float)Math.Ceiling(w.DrawPos.Y));
sprite.SourceRect = new Rectangle(((w.DrawPos - textureRect.Location.ToVector2()) / zoom).ToPoint(), sprite.SourceRect.Size);
if (spriteList.SelectedComponent is GUITextBlock textBox) if (spriteList.SelectedComponent is GUITextBlock textBox)
{ {
// TODO: cache the sprite name? // TODO: cache the sprite name?
@@ -516,15 +552,18 @@ namespace Barotrauma
} }
w.tooltip = $"Position: {sprite.SourceRect.Location}"; w.tooltip = $"Position: {sprite.SourceRect.Location}";
}; };
w.refresh = () => w.DrawPos = textureRect.Location.ToVector2() + sprite.SourceRect.Location.ToVector2() * zoom - new Vector2(w.size / 2); w.refresh = () => w.DrawPos = textureRect.Location.ToVector2() + sprite.SourceRect.Location.ToVector2() * zoom;
}); });
var sizeWidget = GetWidget($"{id}_size", sprite, widgetSize, Widget.Shape.Rectangle, initMethod: w => var sizeWidget = GetWidget($"{id}_size", sprite, widgetSize, Widget.Shape.Rectangle, initMethod: w =>
{ {
w.tooltip = $"Size: {sprite.SourceRect.Size}"; w.tooltip = $"Size: {sprite.SourceRect.Size}";
w.MouseHeld += dTime => w.MouseHeld += dTime =>
{ {
w.DrawPos = PlayerInput.MousePosition; w.DrawPos = (drawGrid && snapToGrid) ?
sprite.SourceRect = new Rectangle(sprite.SourceRect.Location, ((w.DrawPos - new Vector2(w.size) - positionWidget.DrawPos) / zoom).ToPoint()); SnapToGrid(PlayerInput.MousePosition, textureRect, zoom, Submarine.GridSize, Submarine.GridSize.X / 4.0f * zoom) :
PlayerInput.MousePosition;
w.DrawPos = new Vector2((float)Math.Ceiling(w.DrawPos.X), (float)Math.Ceiling(w.DrawPos.Y));
sprite.SourceRect = new Rectangle(sprite.SourceRect.Location, ((w.DrawPos - positionWidget.DrawPos) / zoom).ToPoint());
// TODO: allow to lock the origin // TODO: allow to lock the origin
sprite.RelativeOrigin = sprite.RelativeOrigin; sprite.RelativeOrigin = sprite.RelativeOrigin;
if (spriteList.SelectedComponent is GUITextBlock textBox) if (spriteList.SelectedComponent is GUITextBlock textBox)
@@ -534,7 +573,7 @@ namespace Barotrauma
} }
w.tooltip = $"Size: {sprite.SourceRect.Size}"; w.tooltip = $"Size: {sprite.SourceRect.Size}";
}; };
w.refresh = () => w.DrawPos = textureRect.Location.ToVector2() + new Vector2(sprite.SourceRect.Right, sprite.SourceRect.Bottom) * zoom + new Vector2(w.size / 2); w.refresh = () => w.DrawPos = textureRect.Location.ToVector2() + new Vector2(sprite.SourceRect.Right, sprite.SourceRect.Bottom) * zoom;
}); });
if (isSelected) if (isSelected)
{ {
@@ -553,6 +592,58 @@ namespace Barotrauma
spriteBatch.End(); spriteBatch.End();
} }
private void DrawGrid(SpriteBatch spriteBatch, Rectangle gridArea, float zoom, Vector2 gridSize)
{
gridSize *= zoom;
if (gridSize.X < 1.0f) { return; }
if (gridSize.Y < 1.0f) { return; }
int xLines = (int)(gridArea.Width / gridSize.X);
int yLines = (int)(gridArea.Height / gridSize.Y);
for (int x = 0; x <= xLines; x++)
{
GUI.DrawLine(spriteBatch,
new Vector2(gridArea.X + x * gridSize.X, gridArea.Y),
new Vector2(gridArea.X + x * gridSize.X, gridArea.Bottom),
Color.White * 0.25f);
}
for (int y = 0; y <= yLines; y++)
{
GUI.DrawLine(spriteBatch,
new Vector2(gridArea.X, gridArea.Y + y * gridSize.Y),
new Vector2(gridArea.Right, gridArea.Y + y * gridSize.Y),
Color.White * 0.25f);
}
}
private Vector2 SnapToGrid(Vector2 position, Rectangle gridArea, float zoom, Vector2 gridSize, float tolerance)
{
gridSize *= zoom;
if (gridSize.X < 1.0f) { return position; }
if (gridSize.Y < 1.0f) { return position; }
Vector2 snappedPos = position;
snappedPos.X -= gridArea.X;
snappedPos.Y -= gridArea.Y;
Vector2 gridPos = new Vector2(
MathUtils.RoundTowardsClosest(snappedPos.X, gridSize.X),
MathUtils.RoundTowardsClosest(snappedPos.Y, gridSize.Y));
if (Math.Abs(gridPos.X - snappedPos.X) < tolerance)
{
snappedPos.X = gridPos.X;
}
if (Math.Abs(gridPos.Y - snappedPos.Y) < tolerance)
{
snappedPos.Y = gridPos.Y;
}
snappedPos.X += gridArea.X;
snappedPos.Y += gridArea.Y;
return snappedPos;
}
public override void Select() public override void Select()
{ {
base.Select(); base.Select();
@@ -706,7 +797,7 @@ namespace Barotrauma
zoomBar.BarScroll = GetBarScrollValue(); zoomBar.BarScroll = GetBarScrollValue();
viewAreaOffset = Point.Zero; viewAreaOffset = Point.Zero;
} }
#endregion #endregion
#region Helpers #region Helpers
private Point viewAreaOffset; private Point viewAreaOffset;
@@ -750,7 +841,7 @@ namespace Barotrauma
// Keeps the relative origin unchanged. The absolute origin will be recalculated. // Keeps the relative origin unchanged. The absolute origin will be recalculated.
sprite.RelativeOrigin = sprite.RelativeOrigin; sprite.RelativeOrigin = sprite.RelativeOrigin;
} }
#endregion #endregion
#region Widgets #region Widgets
private Dictionary<string, Widget> widgets = new Dictionary<string, Widget>(); private Dictionary<string, Widget> widgets = new Dictionary<string, Widget>();
@@ -792,6 +883,6 @@ namespace Barotrauma
widgets.Clear(); widgets.Clear();
Widget.selectedWidgets.Clear(); Widget.selectedWidgets.Clear();
} }
#endregion #endregion
} }
} }
@@ -51,7 +51,7 @@ namespace Barotrauma
tabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length]; tabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length];
menu = new GUIFrame(new RectTransform(new Vector2(0.85f, 0.8f), GUI.Canvas, Anchor.Center) { MinSize = new Point(GameMain.GraphicsHeight, 0) }); menu = new GUIFrame(new RectTransform(new Vector2(0.85f, 0.85f), GUI.Canvas, Anchor.Center) { MinSize = new Point(GameMain.GraphicsHeight, 0) });
var container = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.85f), menu.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.0f, 0.05f) }) { Stretch = true }; var container = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.85f), menu.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.0f, 0.05f) }) { Stretch = true };
@@ -111,6 +111,7 @@ namespace Barotrauma
var listContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), tabs[(int)Tab.Browse].RectTransform)) var listContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), tabs[(int)Tab.Browse].RectTransform))
{ {
RelativeSpacing = 0.01f,
Stretch = true Stretch = true
}; };
@@ -125,7 +126,7 @@ namespace Barotrauma
}; };
var findModsButtonContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), listContainer.RectTransform), style: null); var findModsButtonContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), listContainer.RectTransform), style: null);
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), findModsButtonContainer.RectTransform, Anchor.Center), TextManager.Get("FindModsButton"), style: null) new GUIButton(new RectTransform(new Vector2(1.0f, 0.9f), findModsButtonContainer.RectTransform, Anchor.Center), TextManager.Get("FindModsButton"), style: null)
{ {
Color = new Color(38, 86, 38, 75), Color = new Color(38, 86, 38, 75),
HoverColor = new Color(85, 203, 99, 50), HoverColor = new Color(85, 203, 99, 50),
@@ -260,7 +261,12 @@ namespace Barotrauma
private void RefreshItemLists() private void RefreshItemLists()
{ {
SteamManager.GetSubscribedWorkshopItems((items) => { OnItemsReceived(items, subscribedItemList); }); SteamManager.GetSubscribedWorkshopItems((items) =>
{
//filter out the items published by the player (they're shown in the publish tab)
var mySteamID = SteamManager.GetSteamID();
OnItemsReceived(items.Where(it => it.OwnerId != mySteamID).ToList(), subscribedItemList);
});
SteamManager.GetPopularWorkshopItems((items) => { OnItemsReceived(items, topItemList); }, 20); SteamManager.GetPopularWorkshopItems((items) => { OnItemsReceived(items, topItemList); }, 20);
SteamManager.GetPublishedWorkshopItems((items) => { OnItemsReceived(items, publishedItemList); }); SteamManager.GetPublishedWorkshopItems((items) => { OnItemsReceived(items, publishedItemList); });
@@ -704,19 +710,17 @@ namespace Barotrauma
} }
}; };
var headerAreaBackground = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform, maxSize: new Point(int.MaxValue, 235))) { Color = Color.Black }; var headerArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform)) { Color = Color.Black };
var headerArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), headerAreaBackground.RectTransform), childAnchor: Anchor.Center);
if (itemPreviewSprites.ContainsKey(item.PreviewImageUrl)) if (itemPreviewSprites.ContainsKey(item.PreviewImageUrl))
{ {
new GUIImage(new RectTransform(new Point(headerArea.Rect.Width, headerArea.Rect.Height), headerArea.RectTransform), itemPreviewSprites[item.PreviewImageUrl], scaleToFit: true); new GUIImage(new RectTransform(Vector2.One, headerArea.RectTransform), itemPreviewSprites[item.PreviewImageUrl], scaleToFit: true);
} }
else else
{ {
new GUIImage(new RectTransform(new Point(headerArea.Rect.Width, headerArea.Rect.Height), headerArea.RectTransform), SteamManager.Instance.DefaultPreviewImage, scaleToFit: true); new GUIImage(new RectTransform(Vector2.One, headerArea.RectTransform), SteamManager.Instance.DefaultPreviewImage, scaleToFit: true);
} }
var descriptionContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), content.RectTransform)) { ScrollBarVisible = true }; var descriptionContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), content.RectTransform)) { ScrollBarVisible = true };
//spacing //spacing
@@ -944,21 +948,25 @@ namespace Barotrauma
tagBtn.TextBlock.AutoScale = true; tagBtn.TextBlock.AutoScale = true;
tagBtn.Color *= 0.5f; tagBtn.Color *= 0.5f;
tagBtn.SelectedColor = Color.LightGreen; tagBtn.SelectedColor = Color.LightGreen;
tagBtn.HoverColor = Color.Lerp(tagBtn.HoverColor, Color.LightGreen, 0.5f);
tagBtn.Selected = itemEditor.Tags.Any(t => t.ToLowerInvariant() == tag); tagBtn.Selected = itemEditor.Tags.Any(t => t.ToLowerInvariant() == tag);
Color defaultTextColor = tagBtn.TextColor;
tagBtn.TextColor = tagBtn.Selected ? Color.LightGreen : defaultTextColor;
tagBtn.OnClicked = (btn, userdata) => tagBtn.OnClicked = (btn, userdata) =>
{ {
if (!tagBtn.Selected) if (!tagBtn.Selected)
{ {
if (!itemEditor.Tags.Any(t => t.ToLowerInvariant() == tag)) { itemEditor.Tags.Add(tagBtn.Text); } if (!itemEditor.Tags.Any(t => t.ToLowerInvariant() == tag)) { itemEditor.Tags.Add(tagBtn.Text); }
tagBtn.Selected = true; tagBtn.Selected = true;
tagBtn.TextBlock.TextColor = Color.LightGreen; tagBtn.TextColor = Color.LightGreen;
} }
else else
{ {
itemEditor.Tags.RemoveAll(t => t.ToLowerInvariant() == tagBtn.Text.ToLowerInvariant()); itemEditor.Tags.RemoveAll(t => t.ToLowerInvariant() == tagBtn.Text.ToLowerInvariant());
tagBtn.Selected = false; tagBtn.Selected = false;
tagBtn.TextBlock.TextColor = tagBtn.TextColor; tagBtn.TextColor = defaultTextColor;
} }
return true; return true;
}; };
@@ -71,6 +71,12 @@ namespace Barotrauma
private Color primaryColor = new Color(12, 14, 15, 190); private Color primaryColor = new Color(12, 14, 15, 190);
private Color secondaryColor = new Color(12, 14, 15, 215); private Color secondaryColor = new Color(12, 14, 15, 215);
private const int submarineNameLimit = 30;
private GUITextBlock submarineNameCharacterCount;
private const int submarineDescriptionLimit = 500;
private GUITextBlock submarineDescriptionCharacterCount;
public override Camera Cam public override Camera Cam
{ {
get { return cam; } get { return cam; }
@@ -346,7 +352,6 @@ namespace Barotrauma
UseGridLayout = true, UseGridLayout = true,
CheckSelected = MapEntityPrefab.GetSelected CheckSelected = MapEntityPrefab.GetSelected
}; };
UpdateEntityList();
//empty guiframe as a separator //empty guiframe as a separator
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), paddedLeftPanel.RectTransform), style: null); new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), paddedLeftPanel.RectTransform), style: null);
@@ -603,14 +608,16 @@ namespace Barotrauma
} }
entityList.Content.RectTransform.SortChildren((i1, i2) => entityList.Content.RectTransform.SortChildren((i1, i2) =>
(i1.GUIComponent.UserData as MapEntityPrefab).Name.CompareTo((i2.GUIComponent.UserData as MapEntityPrefab).Name)); (i1.GUIComponent.UserData as MapEntityPrefab).Name.CompareTo((i2.GUIComponent.UserData as MapEntityPrefab).Name));
} }
public override void Select() public override void Select()
{ {
base.Select(); base.Select();
UpdateEntityList();
foreach (MapEntityPrefab prefab in MapEntityPrefab.List) foreach (MapEntityPrefab prefab in MapEntityPrefab.List)
{ {
prefab.sprite?.EnsureLazyLoaded(); prefab.sprite?.EnsureLazyLoaded();
@@ -950,32 +957,60 @@ namespace Barotrauma
OnClicked = (btn, userdata) => { if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) saveFrame = null; return true; } OnClicked = (btn, userdata) => { if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) saveFrame = null; return true; }
}; };
var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.45f), saveFrame.RectTransform, Anchor.Center) { MinSize = new Point(750, 400) }); var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.5f), saveFrame.RectTransform, Anchor.Center) { MinSize = new Point(750, 400) });
var paddedSaveFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), innerFrame.RectTransform, Anchor.Center)) { Stretch = true, RelativeSpacing = 0.02f }; var paddedSaveFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), innerFrame.RectTransform, Anchor.Center)) { Stretch = true, RelativeSpacing = 0.02f };
//var header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedSaveFrame.RectTransform), TextManager.Get("SaveSubDialogHeader"), font: GUI.LargeFont); //var header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedSaveFrame.RectTransform), TextManager.Get("SaveSubDialogHeader"), font: GUI.LargeFont);
var columnArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), paddedSaveFrame.RectTransform), isHorizontal: true) { Stretch = true }; var columnArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), paddedSaveFrame.RectTransform), isHorizontal: true) { Stretch = true };
var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.55f, 1.0f), columnArea.RectTransform)) { RelativeSpacing = 0.02f, Stretch = true }; var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.55f, 1.0f), columnArea.RectTransform)) { RelativeSpacing = 0.01f, Stretch = true };
var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.42f, 1.0f), columnArea.RectTransform)) { RelativeSpacing = 0.02f, Stretch = true }; var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.42f, 1.0f), columnArea.RectTransform)) { RelativeSpacing = 0.02f, Stretch = true };
// left column ----------------------------------------------------------------------- // left column -----------------------------------------------------------------------
var saveSubLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 0.03f), leftColumn.RectTransform), var nameHeaderGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.03f), leftColumn.RectTransform), true);
var saveSubLabel = new GUITextBlock(new RectTransform(new Vector2(.5f, 1f), nameHeaderGroup.RectTransform),
TextManager.Get("SaveSubDialogName")); TextManager.Get("SaveSubDialogName"));
nameBox = new GUITextBox(new RectTransform(new Vector2(0.65f, 0.05f), leftColumn.RectTransform)) submarineNameCharacterCount = new GUITextBlock(new RectTransform(new Vector2(.5f, 1f), nameHeaderGroup.RectTransform), string.Empty, textAlignment: Alignment.TopRight);
nameBox = new GUITextBox(new RectTransform(new Vector2(.95f, 0.05f), leftColumn.RectTransform))
{ {
OnEnterPressed = ChangeSubName, OnEnterPressed = ChangeSubName,
Text = GetSubName() Text = GetSubName()
}; };
nameBox.OnTextChanged += (textBox, text) =>
{
if (text.Length > submarineNameLimit)
{
nameBox.Text = text.Substring(0, submarineNameLimit);
nameBox.Flash(Color.Red);
return true;
}
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.03f), leftColumn.RectTransform), TextManager.Get("SaveSubDialogDescription")); submarineNameCharacterCount.Text = text.Length + " / " + submarineNameLimit;
return true;
};
submarineNameCharacterCount.Text = nameBox.Text.Length + " / " + submarineNameLimit;
var descriptionHeaderGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.03f), leftColumn.RectTransform), true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), descriptionHeaderGroup.RectTransform), TextManager.Get("SaveSubDialogDescription"));
submarineDescriptionCharacterCount = new GUITextBlock(new RectTransform(new Vector2(.5f, 1f), descriptionHeaderGroup.RectTransform), string.Empty, textAlignment: Alignment.TopRight);
var descriptionContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.25f), leftColumn.RectTransform)); var descriptionContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.25f), leftColumn.RectTransform));
descriptionBox = new GUITextBox(new RectTransform(Vector2.One, descriptionContainer.Content.RectTransform), font: GUI.SmallFont, wrap: true); descriptionBox = new GUITextBox(new RectTransform(Vector2.One, descriptionContainer.Content.RectTransform, Anchor.Center), font: GUI.SmallFont, wrap: true);
descriptionBox.OnTextChanged += (textBox, text) => descriptionBox.OnTextChanged += (textBox, text) =>
{ {
if (text.Length > submarineDescriptionLimit)
{
descriptionBox.Text = text.Substring(0, submarineDescriptionLimit);
descriptionBox.Flash(Color.Red);
return true;
}
Vector2 textSize = textBox.Font.MeasureString(descriptionBox.WrappedText); Vector2 textSize = textBox.Font.MeasureString(descriptionBox.WrappedText);
textBox.RectTransform.NonScaledSize = new Point(textBox.RectTransform.NonScaledSize.X, Math.Max(descriptionContainer.Rect.Height, (int)textSize.Y + 10)); textBox.RectTransform.NonScaledSize = new Point(textBox.RectTransform.NonScaledSize.X, Math.Max(descriptionContainer.Rect.Height, (int)textSize.Y + 10));
descriptionContainer.UpdateScrollBarSize(); descriptionContainer.UpdateScrollBarSize();
@@ -984,6 +1019,7 @@ namespace Barotrauma
return true; return true;
}; };
descriptionBox.Text = Submarine.MainSub == null ? "" : Submarine.MainSub.Description; descriptionBox.Text = Submarine.MainSub == null ? "" : Submarine.MainSub.Description;
submarineDescriptionCharacterCount.Text = descriptionBox.Text.Length + " / " + submarineDescriptionLimit;
var crewSizeArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.03f), leftColumn.RectTransform), isHorizontal: true) { AbsoluteSpacing = 5 }; var crewSizeArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.03f), leftColumn.RectTransform), isHorizontal: true) { AbsoluteSpacing = 5 };
@@ -1493,8 +1529,21 @@ namespace Barotrauma
private void TryDeleteSub(Submarine sub) private void TryDeleteSub(Submarine sub)
{ {
if (sub == null) return; if (sub == null) { return; }
//if the sub is included in a content package that only defines that one sub,
//delete the content package as well
ContentPackage subPackage = null;
foreach (ContentPackage cp in ContentPackage.List)
{
if (cp.Files.Count == 1 && Path.GetFullPath(cp.Files[0].Path) == Path.GetFullPath(sub.FilePath))
{
subPackage = cp;
break;
}
}
subPackage?.Delete();
var msgBox = new GUIMessageBox( var msgBox = new GUIMessageBox(
TextManager.Get("DeleteDialogLabel"), TextManager.Get("DeleteDialogLabel"),
TextManager.GetWithVariable("DeleteDialogQuestion", "[file]", sub.Name), TextManager.GetWithVariable("DeleteDialogQuestion", "[file]", sub.Name),
@@ -1524,7 +1573,6 @@ namespace Barotrauma
if (CharacterMode) SetCharacterMode(false); if (CharacterMode) SetCharacterMode(false);
if (WiringMode) SetWiringMode(false); if (WiringMode) SetWiringMode(false);
saveFrame = null; saveFrame = null;
loadFrame = null; loadFrame = null;
@@ -1754,7 +1802,9 @@ namespace Barotrauma
{ {
textBox.UserData = text; textBox.UserData = text;
} }
submarineDescriptionCharacterCount.Text = text.Length + " / " + submarineDescriptionLimit;
return true; return true;
} }
@@ -2131,6 +2181,10 @@ namespace Barotrauma
{ {
dummyCharacter.SelectedConstruction.AddToGUIUpdateList(); dummyCharacter.SelectedConstruction.AddToGUIUpdateList();
} }
else if (WiringMode && MapEntity.SelectedList.Count == 1 && MapEntity.SelectedList[0] is Item item && item.GetComponent<Wire>() != null)
{
MapEntity.SelectedList[0].AddToGUIUpdateList();
}
} }
else else
{ {
@@ -2290,9 +2344,9 @@ namespace Barotrauma
dummyCharacter.SelectedConstruction = null; dummyCharacter.SelectedConstruction = null;
}*/ }*/
} }
else if (MapEntity.FilteredSelectedList.Count == 1) else if (MapEntity.SelectedList.Count == 1)
{ {
(MapEntity.FilteredSelectedList[0] as Item)?.UpdateHUD(cam, dummyCharacter, (float)deltaTime); (MapEntity.SelectedList[0] as Item)?.UpdateHUD(cam, dummyCharacter, (float)deltaTime);
} }
CharacterHUD.Update((float)deltaTime, dummyCharacter, cam); CharacterHUD.Update((float)deltaTime, dummyCharacter, cam);
@@ -203,6 +203,8 @@ namespace Barotrauma
public static void Update(float deltaTime) public static void Update(float deltaTime)
{ {
if (!Initialized) { return; }
UpdateMusic(deltaTime); UpdateMusic(deltaTime);
if (startUpSound != null && !GameMain.SoundManager.IsPlaying(startUpSound)) if (startUpSound != null && !GameMain.SoundManager.IsPlaying(startUpSound))
@@ -629,7 +631,7 @@ namespace Barotrauma
{ {
if (OverrideMusicType != null) return OverrideMusicType; if (OverrideMusicType != null) return OverrideMusicType;
if (Screen.Selected != GameMain.GameScreen) if (Screen.Selected == null || Screen.Selected != GameMain.GameScreen)
{ {
return "menu"; return "menu";
} }
@@ -644,7 +646,7 @@ namespace Barotrauma
Submarine targetSubmarine = Character.Controlled?.Submarine; Submarine targetSubmarine = Character.Controlled?.Submarine;
if ((targetSubmarine != null && targetSubmarine.AtDamageDepth) || if ((targetSubmarine != null && targetSubmarine.AtDamageDepth) ||
(Screen.Selected == GameMain.GameScreen && GameMain.GameScreen.Cam.Position.Y < SubmarineBody.DamageDepth)) (GameMain.GameScreen != null && Screen.Selected == GameMain.GameScreen && GameMain.GameScreen.Cam.Position.Y < SubmarineBody.DamageDepth))
{ {
return "deep"; return "deep";
} }
@@ -54,7 +54,9 @@ namespace Barotrauma
partial void ApplyProjSpecific(float deltaTime, Entity entity, List<ISerializableEntity> targets, Hull hull) partial void ApplyProjSpecific(float deltaTime, Entity entity, List<ISerializableEntity> targets, Hull hull)
{ {
if (entity != null && sounds.Count > 0) if (entity == null) { return; }
if (sounds.Count > 0)
{ {
if (soundChannel == null || !soundChannel.IsPlaying) if (soundChannel == null || !soundChannel.IsPlaying)
{ {
@@ -95,22 +97,20 @@ namespace Barotrauma
} }
} }
if (entity != null) foreach (ParticleEmitter emitter in particleEmitters)
{ {
foreach (ParticleEmitter emitter in particleEmitters) float angle = 0.0f;
if (emitter.Prefab.CopyEntityAngle)
{ {
float angle = 0.0f; if (entity is Item item && item.body != null)
if (emitter.Prefab.CopyEntityAngle)
{ {
if (entity is Item it) angle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
{
angle = it.body == null ? 0.0f : it.body.Rotation;
}
} }
emitter.Emit(deltaTime, entity.WorldPosition, hull, angle);
} }
emitter.Emit(deltaTime, entity.WorldPosition, hull, angle);
} }
} }
static partial void UpdateAllProjSpecific(float deltaTime) static partial void UpdateAllProjSpecific(float deltaTime)
Binary file not shown.
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.1.0")] [assembly: AssemblyVersion("0.9.200.0")]
[assembly: AssemblyFileVersion("0.9.1.0")] [assembly: AssemblyFileVersion("0.9.1.0")]
@@ -212,6 +212,7 @@
<Compile Include="Source\Networking\FileTransfer\FileSender.cs" /> <Compile Include="Source\Networking\FileTransfer\FileSender.cs" />
<Compile Include="Source\Networking\GameServer.cs" /> <Compile Include="Source\Networking\GameServer.cs" />
<Compile Include="Source\Networking\GameServerLogin.cs" /> <Compile Include="Source\Networking\GameServerLogin.cs" />
<Compile Include="Source\Networking\KarmaManager.cs" />
<Compile Include="Source\Networking\NetEntityEvent\ServerEntityEventManager.cs" /> <Compile Include="Source\Networking\NetEntityEvent\ServerEntityEventManager.cs" />
<Compile Include="Source\Networking\NetworkMember.cs" /> <Compile Include="Source\Networking\NetworkMember.cs" />
<Compile Include="Source\Networking\OrderChatMessage.cs" /> <Compile Include="Source\Networking\OrderChatMessage.cs" />
+5 -1
View File
@@ -120,12 +120,16 @@ namespace Barotrauma
get { return targetPos; } get { return targetPos; }
set { targetPos = value; } set { targetPos = value; }
} }
public Vector2 GetPosition()
{
return position;
}
// Auxiliary function to move the camera // Auxiliary function to move the camera
public void Translate(Vector2 amount) public void Translate(Vector2 amount)
{ {
position += amount; position += amount;
} }
public void UpdateTransform(bool interpolate = true, bool clampPos = false) public void UpdateTransform(bool interpolate = true, bool clampPos = false)
@@ -11,22 +11,9 @@ namespace Barotrauma
{ {
} }
partial void AdjustKarma(Character attacker, AttackResult attackResult) partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult)
{ {
if (attacker == null) return; GameMain.Server.KarmaManager.OnCharacterHealthChanged(this, attacker, attackResult.Damage, attackResult.Afflictions);
Client attackerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == attacker);
if (attackerClient == null) return;
Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == this);
if (targetClient != null)
{
if (attacker.TeamID == TeamID)
{
attackerClient.Karma -= attackResult.Damage * 0.01f;
if (CharacterHealth.MaxVitality <= CharacterHealth.MinVitality) attackerClient.Karma = 0.0f;
}
}
} }
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction) partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction)
@@ -21,29 +21,25 @@ namespace Barotrauma
{ {
if (!Enabled) { return 1000.0f; } if (!Enabled) { return 1000.0f; }
if (recipient.Character == null || recipient.Character.IsDead) Vector2 comparePosition = recipient.SpectatePos == null ? recipient.Character.WorldPosition : recipient.SpectatePos.Value;
{
return 0.2f;
}
else
{
float distance = Vector2.Distance(recipient.Character.WorldPosition, WorldPosition);
float priority = 1.0f - MathUtils.InverseLerp(
NetConfig.HighPrioCharacterPositionUpdateDistance,
NetConfig.LowPrioCharacterPositionUpdateDistance,
distance);
float interval = MathHelper.Lerp( float distance = Vector2.Distance(comparePosition, WorldPosition);
NetConfig.LowPrioCharacterPositionUpdateInterval, float priority = 1.0f - MathUtils.InverseLerp(
NetConfig.HighPrioCharacterPositionUpdateInterval, NetConfig.HighPrioCharacterPositionUpdateDistance,
priority); NetConfig.LowPrioCharacterPositionUpdateDistance,
distance);
if (IsDead) float interval = MathHelper.Lerp(
{ NetConfig.LowPrioCharacterPositionUpdateInterval,
interval = Math.Max(interval * 2, 0.1f); NetConfig.HighPrioCharacterPositionUpdateInterval,
} priority);
return interval;
if (IsDead)
{
interval = Math.Max(interval * 2, 0.1f);
} }
return interval;
} }
partial void UpdateNetInput() partial void UpdateNetInput()
@@ -86,7 +86,8 @@ namespace Barotrauma
int inputLines = Math.Max((int)Math.Ceiling(input.Length / (float)Console.WindowWidth), 1); int inputLines = Math.Max((int)Math.Ceiling(input.Length / (float)Console.WindowWidth), 1);
Console.CursorLeft = 0; Console.CursorLeft = 0;
Console.Write(new string(' ', consoleWidth)); Console.Write(new string(' ', consoleWidth));
Console.CursorTop -= inputLines; Console.CursorLeft = 0; Console.CursorTop = Math.Max(Console.CursorTop - inputLines, 0);
Console.CursorLeft = 0;
while (queuedMessages.Count > 0) while (queuedMessages.Count > 0)
{ {
ColoredText msg = queuedMessages.Dequeue(); ColoredText msg = queuedMessages.Dequeue();
@@ -121,9 +122,9 @@ namespace Barotrauma
switch (key.Key) switch (key.Key)
{ {
case ConsoleKey.Enter: case ConsoleKey.Enter:
lock (DebugConsole.QueuedCommands) lock (QueuedCommands)
{ {
DebugConsole.QueuedCommands.Add(input); QueuedCommands.Add(input);
} }
input = ""; input = "";
memoryIndex = -1; memoryIndex = -1;
@@ -219,7 +220,15 @@ namespace Barotrauma
private static void AssignOnClientRequestExecute(string names, Action<Client, Vector2, string[]> onClientRequestExecute) private static void AssignOnClientRequestExecute(string names, Action<Client, Vector2, string[]> onClientRequestExecute)
{ {
commands.First(c => c.names.Intersect(names.Split('|')).Count() > 0).OnClientRequestExecute = onClientRequestExecute; var matchingCommand = commands.Find(c => c.names.Intersect(names.Split('|')).Count() > 0);
if (matchingCommand == null)
{
throw new Exception("AssignOnClientRequestExecute failed. Command matching the name(s) \"" + names + "\" not found.");
}
else
{
matchingCommand.OnClientRequestExecute = onClientRequestExecute;
}
} }
private static void InitProjectSpecific() private static void InitProjectSpecific()
@@ -568,10 +577,8 @@ namespace Barotrauma
NewMessage(client.Name + " has the following permissions:", Color.White); NewMessage(client.Name + " has the following permissions:", Color.White);
foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions)))
{ {
if (permission == ClientPermissions.None || !client.HasPermission(permission)) continue; if (permission == ClientPermissions.None || !client.HasPermission(permission)) { continue; }
System.Reflection.FieldInfo fi = typeof(ClientPermissions).GetField(permission.ToString()); NewMessage(" - " + TextManager.Get("ClientPermission." + permission), Color.White);
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
NewMessage(" - " + attributes[0].Description, Color.White);
} }
if (client.HasPermission(ClientPermissions.ConsoleCommands)) if (client.HasPermission(ClientPermissions.ConsoleCommands))
{ {
@@ -590,12 +597,100 @@ namespace Barotrauma
} }
}); });
/*AssignOnExecute("togglekarma", (string[] args) => AssignOnExecute("togglekarma", (string[] args) =>
{ {
return;
if (GameMain.Server == null) return; if (GameMain.Server == null) return;
GameMain.Server.ServerSettings.KarmaEnabled = !GameMain.Server.ServerSettings.KarmaEnabled; GameMain.Server.ServerSettings.KarmaEnabled = !GameMain.Server.ServerSettings.KarmaEnabled;
});*/ NewMessage(GameMain.Server.ServerSettings.KarmaEnabled ? "Karma system enabled." : "Karma system disabled.", Color.LightGreen);
});
AssignOnExecute("resetkarma", (string[] args) =>
{
if (GameMain.Server == null || args.Length == 0) return;
var client = GameMain.Server.ConnectedClients.Find(c => c.Name == args[0]);
if (client == null)
{
ThrowError("Client \"" + args[0] + "\" not found.");
return;
}
client.Karma = 100.0f;
NewMessage("Set the karma of the client \"" + args[0] + "\" to 100.", Color.LightGreen);
});
AssignOnClientRequestExecute("resetkarma", (Client client, Vector2 cursorWorldPos, string[] args) =>
{
if (GameMain.Server == null || args.Length == 0) return;
var targetClient = GameMain.Server.ConnectedClients.Find(c => c.Name == args[0]);
if (targetClient == null)
{
ThrowError("Client \"" + args[0] + "\" not found.");
return;
}
targetClient.Karma = 100.0f;
GameMain.Server.SendDirectChatMessage("Set the karma of the client \"" + args[0] + "\" to 100.", client);
NewMessage("Client \"" + client.Name + "\" set the karma of \"" + args[0] + "\" to 100.", Color.LightGreen);
});
AssignOnExecute("setkarma", (string[] args) =>
{
if (GameMain.Server == null || args.Length < 2) return;
var client = GameMain.Server.ConnectedClients.Find(c => c.Name == args[0]);
if (client == null)
{
ThrowError("Client \"" + args[0] + "\" not found.");
return;
}
if (!float.TryParse(args[1], out float karmaValue) || karmaValue < 0.0f || karmaValue > 100.0f)
{
ThrowError("\"" + args[1] + "\" is not a valid karma value. You need to enter a number between 0-100.");
return;
}
client.Karma = karmaValue;
NewMessage("Set the karma of the client \"" + args[0] + "\" to " + karmaValue + ".", Color.LightGreen);
});
AssignOnClientRequestExecute("setkarma", (Client client, Vector2 cursorWorldPos, string[] args) =>
{
if (GameMain.Server == null || args.Length < 2) return;
var targetClient = GameMain.Server.ConnectedClients.Find(c => c.Name == args[0]);
if (targetClient == null)
{
GameMain.Server.SendDirectChatMessage("Client \"" + args[0] + "\" not found.", client);
return;
}
if (!float.TryParse(args[1], out float karmaValue) || karmaValue < 0.0f || karmaValue > 100.0f)
{
GameMain.Server.SendDirectChatMessage("\"" + args[1] + "\" is not a valid karma value. You need to enter a number between 0-100.", client);
return;
}
targetClient.Karma = karmaValue;
GameMain.Server.SendDirectChatMessage("Set the karma of the client \"" + args[0] + "\" to " + karmaValue + ".", client);
NewMessage("Client \"" + client.Name + "\" set the karma of \"" + args[0] + "\" to " + karmaValue + ".", Color.LightGreen);
});
AssignOnExecute("showkarma", (string[] args) =>
{
if (GameMain.Server == null) return;
NewMessage("***************", Color.Cyan);
foreach (Client c in GameMain.Server.ConnectedClients)
{
NewMessage("- " + c.ID.ToString() + ": " + c.Name + (c.Character != null ? " playing " + c.Character.LogName : "") + ", " + c.Karma, Color.Cyan);
}
NewMessage("***************", Color.Cyan);
});
AssignOnClientRequestExecute("showkarma", (Client client, Vector2 cursorWorldPos, string[] args) =>
{
GameMain.Server.SendConsoleMessage("***************", client);
foreach (Client c in GameMain.Server.ConnectedClients)
{
GameMain.Server.SendConsoleMessage("- " + c.ID.ToString() + ": " + c.Name + (c.Character != null ? " playing " + c.Character.LogName : "") + ", " + c.Karma, client);
}
GameMain.Server.SendConsoleMessage("***************", client);
});
AssignOnExecute("togglekarmatestmode|karmatestmode", (string[] args) =>
{
if (GameMain.Server?.KarmaManager == null) return;
GameMain.Server.KarmaManager.TestMode = !GameMain.Server.KarmaManager.TestMode;
NewMessage(GameMain.Server.KarmaManager.TestMode ? "Karma test mode enabled." : "Karma test mode disabled.", Color.LightGreen);
});
AssignOnExecute("banip", (string[] args) => AssignOnExecute("banip", (string[] args) =>
{ {
@@ -989,6 +1084,16 @@ namespace Barotrauma
}; };
})); }));
AssignOnExecute("respawnnow", (string[] args) =>
{
if (GameMain.Server?.RespawnManager == null) { return; }
if (GameMain.Server.RespawnManager.CurrentState != RespawnManager.State.Transporting)
{
GameMain.Server.RespawnManager.ForceRespawn();
}
});
commands.Add(new Command("startgame|startround|start", "start/startgame/startround: Start a new round.", (string[] args) => commands.Add(new Command("startgame|startround|start", "start/startgame/startround: Start a new round.", (string[] args) =>
{ {
if (Screen.Selected == GameMain.GameScreen) return; if (Screen.Selected == GameMain.GameScreen) return;
@@ -1523,7 +1628,11 @@ namespace Barotrauma
"showperm", "showperm",
(Client senderClient, Vector2 cursorWorldPos, string[] args) => (Client senderClient, Vector2 cursorWorldPos, string[] args) =>
{ {
if (args.Length < 2) return; if (args.Length < 1)
{
GameMain.Server.SendConsoleMessage("showperm [id]: Shows the current administrative permissions of the client with the specified client ID.", senderClient);
return;
}
int.TryParse(args[0], out int id); int.TryParse(args[0], out int id);
var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id); var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
@@ -1542,10 +1651,8 @@ namespace Barotrauma
GameMain.Server.SendConsoleMessage(client.Name + " has the following permissions:", senderClient); GameMain.Server.SendConsoleMessage(client.Name + " has the following permissions:", senderClient);
foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions)))
{ {
if (permission == ClientPermissions.None || !client.HasPermission(permission)) continue; if (permission == ClientPermissions.None || !client.HasPermission(permission)) { continue; }
System.Reflection.FieldInfo fi = typeof(ClientPermissions).GetField(permission.ToString()); GameMain.Server.SendConsoleMessage(" - " + TextManager.Get("ClientPermission." + permission), senderClient);
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
GameMain.Server.SendConsoleMessage(" - " + attributes[0].Description, senderClient);
} }
if (client.HasPermission(ClientPermissions.ConsoleCommands)) if (client.HasPermission(ClientPermissions.ConsoleCommands))
{ {
@@ -8,6 +8,9 @@ namespace Barotrauma
{ {
private bool[] teamDead = new bool[2]; private bool[] teamDead = new bool[2];
private bool initialized = false;
private int state = 0;
public override string Description public override string Description
{ {
get get
@@ -5,7 +5,7 @@ namespace Barotrauma.Items.Components
{ {
partial class Door partial class Door
{ {
partial void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage) partial void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage, bool forcedOpen)
{ {
if (isStuck || isOpen == open) if (isStuck || isOpen == open)
{ {
@@ -18,7 +18,7 @@ namespace Barotrauma.Items.Components
if (sendNetworkMessage) if (sendNetworkMessage)
{ {
item.CreateServerEvent(this); GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, item.GetComponentIndex(this), forcedOpen });
} }
} }
@@ -27,6 +27,7 @@ namespace Barotrauma.Items.Components
base.ServerWrite(msg, c, extraData); base.ServerWrite(msg, c, extraData);
msg.Write(isOpen); msg.Write(isOpen);
msg.Write(extraData.Length == 3 ? (bool)extraData[2] : false); //forced open
msg.WriteRangedSingle(stuck, 0.0f, 100.0f, 8); msg.WriteRangedSingle(stuck, 0.0f, 100.0f, 8);
} }
} }
@@ -7,5 +7,11 @@ namespace Barotrauma.Items.Components
public bool MaintainPos; public bool MaintainPos;
public bool LevelStartSelected; public bool LevelStartSelected;
public bool LevelEndSelected; public bool LevelEndSelected;
public bool UnsentChanges
{
get { return unsentChanges; }
set { unsentChanges = value; }
}
} }
} }
@@ -5,12 +5,6 @@ namespace Barotrauma.Items.Components
{ {
partial class Repairable : ItemComponent, IServerSerializable, IClientSerializable partial class Repairable : ItemComponent, IServerSerializable, IClientSerializable
{ {
void InitProjSpecific()
{
//let the clients know the initial deterioration delay
item.CreateServerEvent(this);
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{ {
if (c.Character == null) return; if (c.Character == null) return;
@@ -33,6 +33,18 @@ namespace Barotrauma.Items.Components
} }
} }
List<Wire> clientSideDisconnectedWires = new List<Wire>();
ushort disconnectedWireCount = msg.ReadUInt16();
for (int i = 0; i < disconnectedWireCount; i++)
{
ushort wireId = msg.ReadUInt16();
if (!(Entity.FindEntityByID(wireId) is Item wireItem)) { continue; }
Wire wireComponent = wireItem.GetComponent<Wire>();
if (wireComponent == null) { continue; }
clientSideDisconnectedWires.Add(wireComponent);
}
//don't allow rewiring locked panels //don't allow rewiring locked panels
if (Locked || !GameMain.NetworkMember.ServerSettings.AllowRewiring) { return; } if (Locked || !GameMain.NetworkMember.ServerSettings.AllowRewiring) { return; }
@@ -47,7 +59,7 @@ namespace Barotrauma.Items.Components
{ {
//wire not found in any of the connections yet (client is trying to connect a new wire) //wire not found in any of the connections yet (client is trying to connect a new wire)
// -> we need to check if the client has access to it // -> we need to check if the client has access to it
if (!Connections.Any(connection => connection.Wires.Contains(wire))) if (!Connections.Any(connection => connection.Wires.Contains(wire)) && !DisconnectedWires.Contains(wire))
{ {
if (!wire.Item.CanClientAccess(c)) { return; } if (!wire.Item.CanClientAccess(c)) { return; }
} }
@@ -75,11 +87,19 @@ namespace Barotrauma.Items.Components
} }
existingWire.RemoveConnection(item); existingWire.RemoveConnection(item);
item.GetComponent<ConnectionPanel>()?.DisconnectedWires.Add(existingWire);
GameMain.Server.KarmaManager.OnWireDisconnected(c.Character, existingWire);
if (existingWire.Connections[0] == null && existingWire.Connections[1] == null) if (existingWire.Connections[0] == null && existingWire.Connections[1] == null)
{ {
GameServer.Log(c.Character.LogName + " disconnected a wire from " + GameServer.Log(c.Character.LogName + " disconnected a wire from " +
Connections[i].Item.Name + " (" + Connections[i].Name + ")", ServerLog.MessageType.ItemInteraction); Connections[i].Item.Name + " (" + Connections[i].Name + ")", ServerLog.MessageType.ItemInteraction);
if (!clientSideDisconnectedWires.Contains(existingWire))
{
existingWire.Item.Drop(c.Character);
}
} }
else if (existingWire.Connections[0] != null) else if (existingWire.Connections[0] != null)
{ {
@@ -89,24 +109,24 @@ namespace Barotrauma.Items.Components
//wires that are not in anyone's inventory (i.e. not currently being rewired) //wires that are not in anyone's inventory (i.e. not currently being rewired)
//can never be connected to only one connection //can never be connected to only one connection
// -> the client must have dropped the wire from the connection panel // -> the client must have dropped the wire from the connection panel
if (existingWire.Item.ParentInventory == null && !wires.Any(w => w.Contains(existingWire))) /*if (existingWire.Item.ParentInventory == null && !wires.Any(w => w.Contains(existingWire)))
{ {
//let other clients know the item was also disconnected from the other connection //let other clients know the item was also disconnected from the other connection
existingWire.Connections[0].Item.CreateServerEvent(existingWire.Connections[0].Item.GetComponent<ConnectionPanel>()); existingWire.Connections[0].Item.CreateServerEvent(existingWire.Connections[0].Item.GetComponent<ConnectionPanel>());
existingWire.Item.Drop(c.Character); existingWire.Item.Drop(c.Character);
} }*/
} }
else if (existingWire.Connections[1] != null) else if (existingWire.Connections[1] != null)
{ {
GameServer.Log(c.Character.LogName + " disconnected a wire from " + GameServer.Log(c.Character.LogName + " disconnected a wire from " +
Connections[i].Item.Name + " (" + Connections[i].Name + ") to " + existingWire.Connections[1].Item.Name + " (" + existingWire.Connections[1].Name + ")", ServerLog.MessageType.ItemInteraction); Connections[i].Item.Name + " (" + Connections[i].Name + ") to " + existingWire.Connections[1].Item.Name + " (" + existingWire.Connections[1].Name + ")", ServerLog.MessageType.ItemInteraction);
if (existingWire.Item.ParentInventory == null && !wires.Any(w => w.Contains(existingWire))) /*if (existingWire.Item.ParentInventory == null && !wires.Any(w => w.Contains(existingWire)))
{ {
//let other clients know the item was also disconnected from the other connection //let other clients know the item was also disconnected from the other connection
existingWire.Connections[1].Item.CreateServerEvent(existingWire.Connections[1].Item.GetComponent<ConnectionPanel>()); existingWire.Connections[1].Item.CreateServerEvent(existingWire.Connections[1].Item.GetComponent<ConnectionPanel>());
existingWire.Item.Drop(c.Character); existingWire.Item.Drop(c.Character);
} }*/
} }
Connections[i].SetWire(j, null); Connections[i].SetWire(j, null);
@@ -114,6 +134,17 @@ namespace Barotrauma.Items.Components
} }
} }
foreach (Wire disconnectedWire in DisconnectedWires.ToList())
{
if (disconnectedWire.Connections[0] == null &&
disconnectedWire.Connections[1] == null &&
!clientSideDisconnectedWires.Contains(disconnectedWire))
{
disconnectedWire.Item.Drop(c.Character);
GameServer.Log(c.Character.LogName + " dropped " + disconnectedWire.Name, ServerLog.MessageType.Inventory);
}
}
//go through new wires //go through new wires
for (int i = 0; i < Connections.Count; i++) for (int i = 0; i < Connections.Count; i++)
{ {
@@ -186,6 +186,15 @@ namespace Barotrauma
case NetEntityEvent.Type.ChangeProperty: case NetEntityEvent.Type.ChangeProperty:
ReadPropertyChange(msg, true, c); ReadPropertyChange(msg, true, c);
break; break;
case NetEntityEvent.Type.Combine:
UInt16 combineTargetID = msg.ReadUInt16();
Item combineTarget = FindEntityByID(combineTargetID) as Item;
if (combineTarget == null || !c.Character.CanInteractWith(this) || !c.Character.CanInteractWith(combineTarget))
{
return;
}
Combine(combineTarget);
break;
} }
} }
@@ -5,24 +5,9 @@ namespace Barotrauma
{ {
partial class Structure : MapEntity, IDamageable, IServerSerializable, ISerializableEntity partial class Structure : MapEntity, IDamageable, IServerSerializable, ISerializableEntity
{ {
partial void AdjustKarma(IDamageable attacker, float amount) partial void OnHealthChangedProjSpecific(Character attacker, float damageAmount)
{ {
if (GameMain.Server != null) GameMain.Server.KarmaManager.OnStructureHealthChanged(this, attacker, damageAmount);
{
if (Submarine == null) return;
if (attacker == null) return;
if (attacker is Character attackerCharacter)
{
Client attackerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == attackerCharacter);
if (attackerClient != null)
{
if (attackerCharacter.TeamID == Submarine.TeamID)
{
attackerClient.Karma -= amount * 0.001f;
}
}
}
}
} }
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
@@ -83,8 +83,9 @@ namespace Barotrauma.Networking
if (similarity + c.ChatSpamSpeed > 5.0f && !isOwner) if (similarity + c.ChatSpamSpeed > 5.0f && !isOwner)
{ {
c.ChatSpamCount++; GameMain.Server.KarmaManager.OnSpamFilterTriggered(c);
c.ChatSpamCount++;
if (c.ChatSpamCount > 3) if (c.ChatSpamCount > 3)
{ {
//kick for spamming too much //kick for spamming too much
@@ -65,20 +65,19 @@ namespace Barotrauma.Networking
public bool SpectateOnly; public bool SpectateOnly;
private float karma = 1.0f; private float karma = 100.0f;
public float Karma public float Karma
{ {
get get
{ {
if (GameMain.Server == null) return 1.0f; if (GameMain.Server == null || !GameMain.Server.ServerSettings.KarmaEnabled) { return 100.0f; }
if (!GameMain.Server.ServerSettings.KarmaEnabled) return 1.0f; if (HasPermission(ClientPermissions.KarmaImmunity)) { return 100.0f; }
return karma; return karma;
} }
set set
{ {
if (GameMain.Server == null) return; if (GameMain.Server == null || !GameMain.Server.ServerSettings.KarmaEnabled) { return; }
if (!GameMain.Server.ServerSettings.KarmaEnabled) return; karma = Math.Min(Math.Max(value, 0.0f), 100.0f);
karma = Math.Min(Math.Max(value, 0.0f), 1.0f);
} }
} }
@@ -78,7 +78,7 @@ namespace Barotrauma.Networking
{ {
get { return entityEventManager; } get { return entityEventManager; }
} }
public TimeSpan UpdateInterval public TimeSpan UpdateInterval
{ {
get { return updateInterval; } get { return updateInterval; }
@@ -109,7 +109,6 @@ namespace Barotrauma.Networking
LastClientListUpdateID = 0; LastClientListUpdateID = 0;
NetPeerConfiguration = new NetPeerConfiguration("barotrauma"); NetPeerConfiguration = new NetPeerConfiguration("barotrauma");
NetPeerConfiguration.Port = port; NetPeerConfiguration.Port = port;
Port = port; Port = port;
QueryPort = queryPort; QueryPort = queryPort;
@@ -119,12 +118,12 @@ namespace Barotrauma.Networking
NetPeerConfiguration.EnableUPnP = true; NetPeerConfiguration.EnableUPnP = true;
} }
serverSettings = new ServerSettings(name, port, queryPort, maxPlayers, isPublic, attemptUPnP); serverSettings = new ServerSettings(this, name, port, queryPort, maxPlayers, isPublic, attemptUPnP);
if (!string.IsNullOrEmpty(password)) if (!string.IsNullOrEmpty(password))
{ {
serverSettings.SetPassword(password); serverSettings.SetPassword(password);
} }
NetPeerConfiguration.MaximumConnections = maxPlayers * 2; //double the lidgren connections for unauthenticated players NetPeerConfiguration.MaximumConnections = maxPlayers * 2; //double the lidgren connections for unauthenticated players
NetPeerConfiguration.DisableMessageType(NetIncomingMessageType.DebugMessage | NetPeerConfiguration.DisableMessageType(NetIncomingMessageType.DebugMessage |
@@ -353,6 +352,7 @@ namespace Barotrauma.Networking
unauthenticatedClients.RemoveAll(uc => uc.AuthTimer <= 0.0f); unauthenticatedClients.RemoveAll(uc => uc.AuthTimer <= 0.0f);
fileSender.Update(deltaTime); fileSender.Update(deltaTime);
KarmaManager.UpdateClients(ConnectedClients, deltaTime);
if (serverSettings.VoiceChatEnabled) if (serverSettings.VoiceChatEnabled)
{ {
@@ -361,7 +361,7 @@ namespace Barotrauma.Networking
if (gameStarted) if (gameStarted)
{ {
if (respawnManager != null) respawnManager.Update(deltaTime); if (respawnManager != null) { respawnManager.Update(deltaTime); }
entityEventManager.Update(connectedClients); entityEventManager.Update(connectedClients);
@@ -415,25 +415,32 @@ namespace Barotrauma.Networking
} }
} }
if (isCrewDead && respawnManager == null) float endRoundDelay = 1.0f;
if (serverSettings.AutoRestart && isCrewDead)
{
endRoundDelay = 5.0f;
endRoundTimer += deltaTime;
}
else if (serverSettings.EndRoundAtLevelEnd && subAtLevelEnd)
{
endRoundDelay = 5.0f;
endRoundTimer += deltaTime;
}
else if (isCrewDead && respawnManager == null)
{ {
if (endRoundTimer <= 0.0f) if (endRoundTimer <= 0.0f)
{ {
SendChatMessage(TextManager.GetWithVariable("CrewDeadNoRespawns", "[time]", "60"), ChatMessageType.Server); SendChatMessage(TextManager.GetWithVariable("CrewDeadNoRespawns", "[time]", "60"), ChatMessageType.Server);
} }
endRoundDelay = 60.0f;
endRoundTimer += deltaTime; endRoundTimer += deltaTime;
} }
else else
{ {
endRoundTimer = 0.0f; endRoundTimer = 0.0f;
} }
//restart if all characters are dead or submarine is at the end of the level if (endRoundTimer >= endRoundDelay)
if ((serverSettings.AutoRestart && isCrewDead)
||
(serverSettings.EndRoundAtLevelEnd && subAtLevelEnd)
||
(isCrewDead && respawnManager == null && endRoundTimer >= 60.0f))
{ {
if (serverSettings.AutoRestart && isCrewDead) if (serverSettings.AutoRestart && isCrewDead)
{ {
@@ -1029,6 +1036,9 @@ namespace Barotrauma.Networking
case ClientNetObject.VOTE: case ClientNetObject.VOTE:
serverSettings.Voting.ServerRead(inc, c); serverSettings.Voting.ServerRead(inc, c);
break; break;
case ClientNetObject.SPECTATING_POS:
c.SpectatePos = new Vector2(inc.ReadFloat(), inc.ReadFloat());
break;
default: default:
return; return;
} }
@@ -1346,11 +1356,20 @@ namespace Barotrauma.Networking
foreach (Character character in Character.CharacterList) foreach (Character character in Character.CharacterList)
{ {
if (!character.Enabled) continue; if (!character.Enabled) continue;
if (c.Character != null &&
Vector2.DistanceSquared(character.WorldPosition, c.Character.WorldPosition) >= if (c.SpectatePos == null)
NetConfig.DisableCharacterDistSqr)
{ {
continue; if (c.Character != null && Vector2.DistanceSquared(character.WorldPosition, c.Character.WorldPosition) >= NetConfig.DisableCharacterDistSqr)
{
continue;
}
}
else
{
if (Vector2.DistanceSquared(character.WorldPosition, c.SpectatePos.Value) >= NetConfig.DisableCharacterDistSqr)
{
continue;
}
} }
float updateInterval = character.GetPositionUpdateInterval(c); float updateInterval = character.GetPositionUpdateInterval(c);
@@ -2005,8 +2024,16 @@ namespace Barotrauma.Networking
public void EndGame() public void EndGame()
{ {
if (!gameStarted) return; if (!gameStarted) { return; }
Log("Ending the round...", ServerLog.MessageType.ServerMessage); if (GameSettings.VerboseLogging)
{
Log("Ending the round...\n" + Environment.StackTrace, ServerLog.MessageType.ServerMessage);
}
else
{
Log("Ending the round...", ServerLog.MessageType.ServerMessage);
}
string endMessage = "The round has ended." + '\n'; string endMessage = "The round has ended." + '\n';
@@ -2068,31 +2095,14 @@ namespace Barotrauma.Networking
} }
} }
CoroutineManager.StartCoroutine(EndCinematic(), "EndCinematic");
GameMain.NetLobbyScreen.RandomizeSettings();
}
public IEnumerable<object> EndCinematic()
{
float endPreviewLength = 10.0f;
var cinematic = new RoundEndCinematic(Submarine.MainSub, GameMain.GameScreen.Cam, endPreviewLength);
do
{
yield return CoroutineStatus.Running;
} while (cinematic.Running);
Submarine.Unload(); Submarine.Unload();
entityEventManager.Clear(); entityEventManager.Clear();
GameMain.NetLobbyScreen.Select(); GameMain.NetLobbyScreen.Select();
Log("Round ended.", ServerLog.MessageType.ServerMessage); Log("Round ended.", ServerLog.MessageType.ServerMessage);
yield return CoroutineStatus.Success; GameMain.NetLobbyScreen.RandomizeSettings();
} }
public override void AddChatMessage(ChatMessage message) public override void AddChatMessage(ChatMessage message)
{ {
if (string.IsNullOrEmpty(message.Text)) { return; } if (string.IsNullOrEmpty(message.Text)) { return; }
@@ -2257,6 +2267,7 @@ namespace Barotrauma.Networking
previousPlayers.Add(previousPlayer); previousPlayers.Add(previousPlayer);
} }
previousPlayer.Name = client.Name; previousPlayer.Name = client.Name;
previousPlayer.Karma = client.Karma;
previousPlayer.KickVoters.Clear(); previousPlayer.KickVoters.Clear();
foreach (Client c in connectedClients) foreach (Client c in connectedClients)
{ {
@@ -2267,6 +2278,8 @@ namespace Barotrauma.Networking
client.Dispose(); client.Dispose();
connectedClients.Remove(client); connectedClients.Remove(client);
KarmaManager.OnClientDisconnected(client);
UpdateVoteStatus(); UpdateVoteStatus();
SendChatMessage(msg, ChatMessageType.Server); SendChatMessage(msg, ChatMessageType.Server);
@@ -3077,6 +3090,7 @@ namespace Barotrauma.Networking
public string Name; public string Name;
public string IP; public string IP;
public UInt64 SteamID; public UInt64 SteamID;
public float Karma;
public readonly List<Client> KickVoters = new List<Client>(); public readonly List<Client> KickVoters = new List<Client>();
public PreviousPlayer(Client c) public PreviousPlayer(Client c)
@@ -472,9 +472,10 @@ namespace Barotrauma.Networking
unauthClient = null; unauthClient = null;
ConnectedClients.Add(newClient); ConnectedClients.Add(newClient);
var previousPlayer = previousPlayers.Find(p => p.MatchesClient(newClient)); var previousPlayer = previousPlayers.Find(p => p.MatchesClient(newClient));
if (previousPlayer != null) if (previousPlayer != null)
{ {
newClient.Karma = previousPlayer.Karma;
foreach (Client c in previousPlayer.KickVoters) foreach (Client c in previousPlayer.KickVoters)
{ {
if (!connectedClients.Contains(c)) { continue; } if (!connectedClients.Contains(c)) { continue; }
@@ -0,0 +1,337 @@
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
partial class KarmaManager : ISerializableEntity
{
private class ClientMemory
{
public List<Pair<Wire, float>> WireDisconnectTime = new List<Pair<Wire, float>>();
//the client's karma value when they were last sent a notification about it (e.g. "your karma is very low")
public float PreviousNotifiedKarma;
public float StructureDamageAccumulator;
private float structureDamagePerSecond;
public float StructureDamagePerSecond
{
get { return Math.Max(StructureDamageAccumulator, structureDamagePerSecond); }
set { structureDamagePerSecond = value; }
}
}
public bool TestMode = false;
private readonly Dictionary<Client, ClientMemory> clientMemories = new Dictionary<Client, ClientMemory>();
private readonly List<Client> bannedClients = new List<Client>();
private DateTime perSecondUpdate;
private double KarmaNotificationTime;
public void UpdateClients(IEnumerable<Client> clients, float deltaTime)
{
if (!GameMain.Server.GameStarted) { return; }
bannedClients.Clear();
foreach (Client client in clients)
{
var clientMemory = GetClientMemory(client);
UpdateClient(client, deltaTime);
if (perSecondUpdate < DateTime.Now)
{
clientMemory.StructureDamagePerSecond = clientMemory.StructureDamageAccumulator;
clientMemory.StructureDamageAccumulator = 0.0f;
}
}
if (perSecondUpdate < DateTime.Now)
{
perSecondUpdate = DateTime.Now + new TimeSpan(0, 0, 1);
}
if (TestMode || Timing.TotalTime > KarmaNotificationTime)
{
foreach (Client client in clients)
{
SendKarmaNotifications(client);
}
KarmaNotificationTime = Timing.TotalTime + KarmaNotificationInterval;
}
foreach (Client bannedClient in bannedClients)
{
GameMain.Server.BanClient(bannedClient, $"KarmaBanned~[banthreshold]={(int)KickBanThreshold}", duration: TimeSpan.FromSeconds(GameMain.Server.ServerSettings.AutoBanTime));
}
}
private void SendKarmaNotifications(Client client, string debugKarmaChangeReason = "")
{
var clientMemory = GetClientMemory(client);
float karmaChange = client.Karma - clientMemory.PreviousNotifiedKarma;
if (Math.Abs(karmaChange) > KarmaNotificationInterval || (TestMode && Math.Abs(karmaChange) > 2.0f))
{
if (TestMode)
{
string msg =
karmaChange < 0 ? $"You karma has decreased to {client.Karma}" : $"You karma has increased to {client.Karma}";
if (!string.IsNullOrEmpty(debugKarmaChangeReason))
{
msg += $". Reason: {debugKarmaChangeReason}";
}
GameMain.Server.SendDirectChatMessage(msg, client);
}
else if (Math.Abs(KickBanThreshold - client.Karma) < KarmaNotificationInterval)
{
GameMain.Server.SendDirectChatMessage(TextManager.Get("KarmaBanWarning"), client);
}
else
{
GameMain.Server.SendDirectChatMessage(TextManager.Get(karmaChange < 0 ? "KarmaDecreasedUnknownAmount" : "KarmaIncreasedUnknownAmount"), client);
}
clientMemory.PreviousNotifiedKarma = client.Karma;
}
}
private void UpdateClient(Client client, float deltaTime)
{
if (client.Karma > KarmaDecayThreshold)
{
client.Karma -= KarmaDecay * deltaTime;
}
else if (client.Karma < KarmaIncreaseThreshold)
{
client.Karma += KarmaIncrease * deltaTime;
}
if (client.Character != null && !client.Character.Removed)
{
//increase the strength of the herpes affliction in steps instead of linearly
//otherwise clients could determine their exact karma value from the strength
float herpesStrength = 0.0f;
if (client.Karma < 20)
herpesStrength = 100.0f;
else if (client.Karma < 30)
herpesStrength = 60.0f;
else if (client.Karma < 40.0f)
herpesStrength = 30.0f;
var existingAffliction = client.Character.CharacterHealth.GetAffliction<AfflictionSpaceHerpes>("spaceherpes");
if (existingAffliction == null && herpesStrength > 0.0f)
{
client.Character.CharacterHealth.ApplyAffliction(null, new Affliction(herpesAffliction, herpesStrength));
}
else if (existingAffliction != null)
{
existingAffliction.Strength = herpesStrength;
}
//check if the client has disconnected an excessive number of wires
var clientMemory = GetClientMemory(client);
if (clientMemory.WireDisconnectTime.Count > (int)AllowedWireDisconnectionsPerMinute)
{
clientMemory.WireDisconnectTime.RemoveRange(0, clientMemory.WireDisconnectTime.Count - (int)AllowedWireDisconnectionsPerMinute);
if (clientMemory.WireDisconnectTime.All(w => Timing.TotalTime - w.Second < 60.0f))
{
float karmaDecrease = -WireDisconnectionKarmaDecrease;
//engineers don't lose as much karma for removing lots of wires
if (client.Character.Info?.Job.Prefab.Identifier == "engineer") { karmaDecrease *= 0.5f; }
AdjustKarma(client.Character, karmaDecrease, "Disconnected excessive number of wires");
}
}
if (client.Character?.Info?.Job.Prefab.Identifier == "captain" && client.Character.SelectedConstruction != null)
{
if (client.Character.SelectedConstruction.GetComponent<Steering>() != null)
{
AdjustKarma(client.Character, SteerSubKarmaIncrease * deltaTime, "Steering the sub");
}
}
}
if (client.Karma < KickBanThreshold && client.Connection != GameMain.Server.OwnerConnection)
{
if (TestMode)
{
client.Karma = 50.0f;
GameMain.Server.SendDirectChatMessage("BANNED! (not really because karma test mode is enabled)", client);
}
else
{
bannedClients.Add(client);
}
}
}
public void OnClientDisconnected(Client client)
{
clientMemories.Remove(client);
}
public void OnCharacterHealthChanged(Character target, Character attacker, float damage, IEnumerable<Affliction> appliedAfflictions = null)
{
if (target == null || attacker == null) { return; }
if (target == attacker) { return; }
//damaging dead characters doesn't affect karma
if (target.IsDead || target.Removed) { return; }
bool isEnemy = target.AIController is EnemyAIController || target.TeamID != attacker.TeamID;
if (GameMain.Server.TraitorManager != null)
{
if (GameMain.Server.TraitorManager.TraitorList.Any(t => t.Character == target))
{
//traitors always count as enemies
isEnemy = true;
}
if (GameMain.Server.TraitorManager.TraitorList.Any(t => t.Character == attacker && t.TargetCharacter == target))
{
//target counts as an enemy to the traitor
isEnemy = true;
}
}
if (appliedAfflictions != null)
{
foreach (Affliction affliction in appliedAfflictions)
{
if (MathUtils.NearlyEqual(affliction.Prefab.KarmaChangeOnApplied, 0.0f)) { continue; }
damage -= affliction.Prefab.KarmaChangeOnApplied * affliction.Strength;
}
}
if (target.AIController is EnemyAIController || target.TeamID != attacker.TeamID)
{
if (damage > 0)
{
float karmaIncrease = damage * DamageEnemyKarmaIncrease;
if (attacker?.Info?.Job.Prefab.Identifier == "securityofficer") { karmaIncrease *= 2.0f; }
AdjustKarma(attacker, karmaIncrease, "Damaged enemy");
}
}
else
{
if (damage > 0)
{
AdjustKarma(attacker, -damage * DamageFriendlyKarmaDecrease, "Damaged friendly");
}
else
{
float karmaIncrease = -damage * HealFriendlyKarmaIncrease;
if (attacker?.Info?.Job.Prefab.Identifier == "medicaldoctor") { karmaIncrease *= 2.0f; }
AdjustKarma(attacker, karmaIncrease, "Healed friendly");
}
}
}
public void OnStructureHealthChanged(Structure structure, Character attacker, float damageAmount)
{
if (attacker == null) { return; }
//damaging/repairing ruin structures or enemy subs doesn't affect karma
if (structure.Submarine == null || structure.Submarine.TeamID != attacker.TeamID)
{
return;
}
if (damageAmount > 0)
{
if (StructureDamageKarmaDecrease <= 0.0f) { return; }
Client client = GameMain.Server.ConnectedClients.Find(c => c.Character == attacker);
if (client != null)
{
//cap the damage so the karma can't decrease by more than MaxStructureDamageKarmaDecreasePerSecond per second
var clientMemory = GetClientMemory(client);
clientMemory.StructureDamageAccumulator += damageAmount;
if (clientMemory.StructureDamagePerSecond + damageAmount >= MaxStructureDamageKarmaDecreasePerSecond / StructureDamageKarmaDecrease)
{
damageAmount -= (MaxStructureDamageKarmaDecreasePerSecond / StructureDamageKarmaDecrease) - clientMemory.StructureDamagePerSecond;
if (damageAmount <= 0.0f) { return; }
}
}
AdjustKarma(attacker, -damageAmount * StructureDamageKarmaDecrease, "Damaged structures");
}
else
{
float karmaIncrease = -damageAmount * StructureRepairKarmaIncrease;
//mechanics get twice as much karma for repairing walls
if (attacker.Info?.Job.Prefab.Identifier == "mechanic") { karmaIncrease *= 2.0f; }
AdjustKarma(attacker, karmaIncrease, "Repaired structures");
}
}
public void OnItemRepaired(Character character, Repairable repairable, float repairAmount)
{
float karmaIncrease = repairAmount * ItemRepairKarmaIncrease;
if (repairable.HasRequiredSkills(character)) { karmaIncrease *= 2.0f; }
AdjustKarma(character, karmaIncrease, "Repaired item");
}
public void OnReactorOverHeating(Character character, float deltaTime)
{
AdjustKarma(character, -ReactorOverheatKarmaDecrease * deltaTime, "Caused reactor to overheat");
}
public void OnReactorMeltdown(Character character)
{
AdjustKarma(character, -ReactorMeltdownKarmaDecrease, "Caused a reactor meltdown");
}
public void OnExtinguishingFire(Character character, float deltaTime)
{
AdjustKarma(character, ExtinguishFireKarmaIncrease * deltaTime, "Extinguished a fire");
}
public void OnWireDisconnected(Character character, Wire wire)
{
if (character == null || wire == null) { return; }
Client client = GameMain.Server.ConnectedClients.Find(c => c.Character == character);
if (client == null) { return; }
if (!clientMemories.ContainsKey(client)) { clientMemories[client] = new ClientMemory(); }
clientMemories[client].WireDisconnectTime.RemoveAll(w => w.First == wire);
clientMemories[client].WireDisconnectTime.Add(new Pair<Wire, float>(wire, (float)Timing.TotalTime));
}
private ClientMemory GetClientMemory(Client client)
{
if (!clientMemories.ContainsKey(client))
{
clientMemories[client] = new ClientMemory()
{
PreviousNotifiedKarma = client.Karma
};
}
return clientMemories[client];
}
public void OnSpamFilterTriggered(Client client)
{
if (client != null)
{
client.Karma -= SpamFilterKarmaDecrease;
SendKarmaNotifications(client, "Triggered the spam filter");
}
}
private void AdjustKarma(Character target, float amount, string debugKarmaChangeReason = "")
{
if (target == null) { return; }
Client client = GameMain.Server.ConnectedClients.Find(c => c.Character == target);
if (client == null) { return; }
client.Karma += amount;
if (TestMode)
{
SendKarmaNotifications(client, debugKarmaChangeReason);
}
}
}
}
@@ -1,4 +1,5 @@
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
using Lidgren.Network;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -53,36 +54,34 @@ namespace Barotrauma.Networking
return botsToRespawn; return botsToRespawn;
} }
partial void UpdateWaiting(float deltaTime) private bool RespawnPending()
{ {
int characterToRespawnCount = GetClientsToRespawn().Count; int characterToRespawnCount = GetClientsToRespawn().Count;
int totalCharacterCount = GameMain.Server.ConnectedClients.Count; int totalCharacterCount = GameMain.Server.ConnectedClients.Count;
/*if (server.Character != null) return (float)characterToRespawnCount >= Math.Max((float)totalCharacterCount * GameMain.Server.ServerSettings.MinRespawnRatio, 1.0f);
{ }
totalCharacterCount++;
if (server.Character.IsDead) characterToRespawnCount++;
}*/
bool startCountdown = (float)characterToRespawnCount >= Math.Max((float)totalCharacterCount * GameMain.Server.ServerSettings.MinRespawnRatio, 1.0f);
if (startCountdown != CountdownStarted) partial void UpdateWaiting(float deltaTime)
{
bool respawnPending = RespawnPending();
if (respawnPending != RespawnCountdownStarted)
{ {
CountdownStarted = startCountdown; RespawnCountdownStarted = respawnPending;
RespawnTime = DateTime.Now + new TimeSpan(0,0,0,0, (int)(GameMain.Server.ServerSettings.RespawnInterval * 1000.0f));
GameMain.Server.CreateEntityEvent(this); GameMain.Server.CreateEntityEvent(this);
} }
if (!CountdownStarted) return; if (!RespawnCountdownStarted) { return; }
respawnTimer -= deltaTime; if (DateTime.Now > RespawnTime)
if (respawnTimer <= 0.0f)
{ {
respawnTimer = GameMain.Server.ServerSettings.RespawnInterval;
DispatchShuttle(); DispatchShuttle();
RespawnCountdownStarted = false;
} }
if (respawnShuttle == null) return; if (RespawnShuttle == null) { return; }
respawnShuttle.Velocity = Vector2.Zero; RespawnShuttle.Velocity = Vector2.Zero;
if (shuttleSteering != null) if (shuttleSteering != null)
{ {
@@ -93,9 +92,9 @@ namespace Barotrauma.Networking
partial void DispatchShuttle() partial void DispatchShuttle()
{ {
if (respawnShuttle != null) if (RespawnShuttle != null)
{ {
state = State.Transporting; CurrentState = State.Transporting;
GameMain.Server.CreateEntityEvent(this); GameMain.Server.CreateEntityEvent(this);
ResetShuttle(); ResetShuttle();
@@ -110,11 +109,27 @@ namespace Barotrauma.Networking
RespawnCharacters(); RespawnCharacters();
CoroutineManager.StopCoroutines("forcepos"); CoroutineManager.StopCoroutines("forcepos");
CoroutineManager.StartCoroutine(ForceShuttleToPos(Level.Loaded.StartPosition - Vector2.UnitY * Level.ShaftHeight, 100.0f), "forcepos"); Vector2 spawnPos = FindSpawnPos();
if (spawnPos.Y > Level.Loaded.Size.Y)
{
CoroutineManager.StartCoroutine(ForceShuttleToPos(Level.Loaded.StartPosition - Vector2.UnitY * Level.ShaftHeight, 100.0f), "forcepos");
}
else
{
RespawnShuttle.SetPosition(spawnPos);
RespawnShuttle.Velocity = Vector2.Zero;
if (shuttleSteering != null)
{
shuttleSteering.AutoPilot = true;
shuttleSteering.MaintainPos = true;
shuttleSteering.PosToMaintain = RespawnShuttle.WorldPosition;
shuttleSteering.UnsentChanges = true;
}
}
} }
else else
{ {
state = State.Waiting; CurrentState = State.Waiting;
GameServer.Log("Respawning everyone in main sub.", ServerLog.MessageType.Spawning); GameServer.Log("Respawning everyone in main sub.", ServerLog.MessageType.Spawning);
GameMain.Server.CreateEntityEvent(this); GameMain.Server.CreateEntityEvent(this);
@@ -129,18 +144,18 @@ namespace Barotrauma.Networking
if (door.IsOpen) door.TrySetState(false, false, true); if (door.IsOpen) door.TrySetState(false, false, true);
} }
var shuttleGaps = Gap.GapList.FindAll(g => g.Submarine == respawnShuttle && g.ConnectedWall != null); var shuttleGaps = Gap.GapList.FindAll(g => g.Submarine == RespawnShuttle && g.ConnectedWall != null);
shuttleGaps.ForEach(g => Spawner.AddToRemoveQueue(g)); shuttleGaps.ForEach(g => Spawner.AddToRemoveQueue(g));
var dockingPorts = Item.ItemList.FindAll(i => i.Submarine == respawnShuttle && i.GetComponent<DockingPort>() != null); var dockingPorts = Item.ItemList.FindAll(i => i.Submarine == RespawnShuttle && i.GetComponent<DockingPort>() != null);
dockingPorts.ForEach(d => d.GetComponent<DockingPort>().Undock()); dockingPorts.ForEach(d => d.GetComponent<DockingPort>().Undock());
//shuttle has returned if the path has been traversed or the shuttle is close enough to the exit //shuttle has returned if the path has been traversed or the shuttle is close enough to the exit
if (!CoroutineManager.IsCoroutineRunning("forcepos")) if (!CoroutineManager.IsCoroutineRunning("forcepos"))
{ {
if ((shuttleSteering?.SteeringPath != null && shuttleSteering.SteeringPath.Finished) if ((shuttleSteering?.SteeringPath != null && shuttleSteering.SteeringPath.Finished)
|| (respawnShuttle.WorldPosition.Y + respawnShuttle.Borders.Y > Level.Loaded.StartPosition.Y - Level.ShaftHeight && || (RespawnShuttle.WorldPosition.Y + RespawnShuttle.Borders.Y > Level.Loaded.StartPosition.Y - Level.ShaftHeight &&
Math.Abs(Level.Loaded.StartPosition.X - respawnShuttle.WorldPosition.X) < 1000.0f)) Math.Abs(Level.Loaded.StartPosition.X - RespawnShuttle.WorldPosition.X) < 1000.0f))
{ {
CoroutineManager.StopCoroutines("forcepos"); CoroutineManager.StopCoroutines("forcepos");
CoroutineManager.StartCoroutine( CoroutineManager.StartCoroutine(
@@ -149,46 +164,60 @@ namespace Barotrauma.Networking
} }
} }
if (respawnShuttle.WorldPosition.Y > Level.Loaded.Size.Y || shuttleReturnTimer <= 0.0f) if (RespawnShuttle.WorldPosition.Y > Level.Loaded.Size.Y || DateTime.Now > despawnTime)
{ {
CoroutineManager.StopCoroutines("forcepos"); CoroutineManager.StopCoroutines("forcepos");
ResetShuttle(); ResetShuttle();
state = State.Waiting; CurrentState = State.Waiting;
GameServer.Log("The respawn shuttle has left.", ServerLog.MessageType.Spawning); GameServer.Log("The respawn shuttle has left.", ServerLog.MessageType.Spawning);
GameMain.Server.CreateEntityEvent(this); GameMain.Server.CreateEntityEvent(this);
respawnTimer = GameMain.Server.ServerSettings.RespawnInterval; RespawnCountdownStarted = false;
CountdownStarted = false;
} }
} }
partial void UpdateTransportingProjSpecific(float deltaTime) partial void UpdateTransportingProjSpecific(float deltaTime)
{ {
//if there are no living chracters inside, transporting can be stopped immediately
if (!Character.CharacterList.Any(c => c.Submarine == respawnShuttle && !c.IsDead)) if (!ReturnCountdownStarted)
{ {
shuttleTransportTimer = 0.0f; //if there are no living chracters inside, transporting can be stopped immediately
if (!Character.CharacterList.Any(c => c.Submarine == RespawnShuttle && !c.IsDead))
{
ReturnTime = DateTime.Now;
ReturnCountdownStarted = true;
}
else if (!RespawnPending())
{
//don't start counting down until someone else needs to respawn
ReturnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(maxTransportTime * 1000));
despawnTime = ReturnTime + new TimeSpan(0, 0, seconds: 30);
return;
}
else
{
ReturnCountdownStarted = true;
GameMain.Server.CreateEntityEvent(this);
}
} }
if (shuttleTransportTimer <= 0.0f) if (DateTime.Now > ReturnTime)
{ {
GameServer.Log("The respawn shuttle is leaving.", ServerLog.MessageType.ServerMessage); GameServer.Log("The respawn shuttle is leaving.", ServerLog.MessageType.ServerMessage);
state = State.Returning; CurrentState = State.Returning;
GameMain.Server.CreateEntityEvent(this); GameMain.Server.CreateEntityEvent(this);
CountdownStarted = false; RespawnCountdownStarted = false;
maxTransportTime = GameMain.Server.ServerSettings.MaxTransportTime; maxTransportTime = GameMain.Server.ServerSettings.MaxTransportTime;
shuttleReturnTimer = maxTransportTime;
shuttleTransportTimer = maxTransportTime;
} }
} }
partial void RespawnCharactersProjSpecific() partial void RespawnCharactersProjSpecific()
{ {
var respawnSub = respawnShuttle ?? Submarine.MainSub; var respawnSub = RespawnShuttle ?? Submarine.MainSub;
var clients = GetClientsToRespawn(); var clients = GetClientsToRespawn();
foreach (Client c in clients) foreach (Client c in clients)
@@ -250,7 +279,7 @@ namespace Barotrauma.Networking
GameServer.Log(string.Format("Respawning {0} ({1}) as {2}", clients[i].Name, clients[i].Connection?.RemoteEndPoint?.Address, characterInfos[i].Job.Name), ServerLog.MessageType.Spawning); GameServer.Log(string.Format("Respawning {0} ({1}) as {2}", clients[i].Name, clients[i].Connection?.RemoteEndPoint?.Address, characterInfos[i].Job.Name), ServerLog.MessageType.Spawning);
} }
if (divingSuitPrefab != null && oxyPrefab != null && respawnShuttle != null) if (divingSuitPrefab != null && oxyPrefab != null && RespawnShuttle != null)
{ {
Vector2 pos = cargoSp == null ? character.Position : cargoSp.Position; Vector2 pos = cargoSp == null ? character.Position : cargoSp.Position;
if (divingSuitPrefab != null && oxyPrefab != null) if (divingSuitPrefab != null && oxyPrefab != null)
@@ -295,5 +324,27 @@ namespace Barotrauma.Networking
} }
} }
} }
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
msg.WriteRangedInteger(0, Enum.GetNames(typeof(State)).Length, (int)CurrentState);
switch (CurrentState)
{
case State.Transporting:
msg.Write(ReturnCountdownStarted);
msg.Write(GameMain.Server.ServerSettings.MaxTransportTime);
msg.Write((float)(ReturnTime - DateTime.Now).TotalSeconds);
break;
case State.Waiting:
msg.Write(RespawnCountdownStarted);
msg.Write((float)(RespawnTime - DateTime.Now).TotalSeconds);
break;
case State.Returning:
break;
}
msg.WritePadBits();
}
} }
} }
@@ -101,8 +101,12 @@ namespace Barotrauma.Networking
if (netProperties.ContainsKey(key)) if (netProperties.ContainsKey(key))
{ {
object prevValue = netProperties[key].Value;
netProperties[key].Read(incMsg); netProperties[key].Read(incMsg);
GameServer.Log(c.Name + " changed " + netProperties[key].Name + " to " + netProperties[key].Value.ToString(), ServerLog.MessageType.ServerMessage); if (!netProperties[key].PropEquals(prevValue, netProperties[key]))
{
GameServer.Log(c.Name + " changed " + netProperties[key].Name + " to " + netProperties[key].Value.ToString(), ServerLog.MessageType.ServerMessage);
}
changed = true; changed = true;
} }
else else
@@ -205,6 +209,12 @@ namespace Barotrauma.Networking
{ {
doc.Save(writer); doc.Save(writer);
} }
if (KarmaPreset == "custom")
{
GameMain.Server?.KarmaManager?.SaveCustomPreset();
}
GameMain.Server?.KarmaManager?.Save();
} }
private void LoadSettings() private void LoadSettings()
@@ -300,9 +310,6 @@ namespace Barotrauma.Networking
{ {
if (!MonsterEnabled.ContainsKey(s)) MonsterEnabled.Add(s, true); if (!MonsterEnabled.ContainsKey(s)) MonsterEnabled.Add(s, true);
} }
AutoBanTime = doc.Root.GetAttributeFloat("autobantime", 60);
MaxAutoBanTime = doc.Root.GetAttributeFloat("maxautobantime", 360);
} }
public void LoadClientPermissions() public void LoadClientPermissions()
@@ -89,6 +89,17 @@ namespace Barotrauma
sb.AppendLine(exception.StackTrace); sb.AppendLine(exception.StackTrace);
sb.AppendLine("\n"); sb.AppendLine("\n");
if (exception.InnerException != null)
{
sb.AppendLine("InnerException: " + exception.InnerException.Message);
if (exception.InnerException.TargetSite != null)
{
sb.AppendLine("Target site: " + exception.InnerException.TargetSite.ToString());
}
sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.InnerException.StackTrace);
}
sb.AppendLine("Last debug messages:"); sb.AppendLine("Last debug messages:");
for (int i = DebugConsole.Messages.Count - 1; i > 0 && i > DebugConsole.Messages.Count - 15; i-- ) for (int i = DebugConsole.Messages.Count - 1; i > 0 && i > DebugConsole.Messages.Count - 15; i-- )
{ {
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<KarmaManager>
<Preset
name="Default"
karmadecay="0.08"
karmadecaythreshold="50"
karmaincrease="0.1"
karmaincreasethreshold="50"
structurerepairkarmaincrease="0.05"
structuredamagekarmadecrease="0.1"
itemrepairkarmaincrease="0.03"
reactoroverheatkarmadecrease="0.5"
reactormeltdownkarmadecrease="30"
damageenemykarmaincrease="0.1"
damagefriendlykarmadecrease="0.2"
extinguishfirekarmaincrease="1"
allowedwiredisconnectionsperminute="5"
wiredisconnectionkarmadecrease="6.0"
steersubkarmaincrease="0.15"
spamfilterkarmadecrease="15"
herpesthreshold="40"
kickbanthreshold="1"
karmanotificationinterval="10" />
<Preset
name="Strict"
karmadecay="0.08"
karmadecaythreshold="50"
karmaincrease="0.08"
karmaincreasethreshold="45"
structurerepairkarmaincrease="0.05"
structuredamagekarmadecrease="0.15"
itemrepairkarmaincrease="0.03"
reactoroverheatkarmadecrease="1.0"
reactormeltdownkarmadecrease="35"
damageenemykarmaincrease="0.1"
damagefriendlykarmadecrease="0.3"
extinguishfirekarmaincrease="1"
allowedwiredisconnectionsperminute="4"
wiredisconnectionkarmadecrease="10.0"
steersubkarmaincrease="0.15"
spamfilterkarmadecrease="25"
herpesthreshold="40"
kickbanthreshold="1"
karmanotificationinterval="10" />
<Preset
name="Custom"
karmadecay="0.08"
karmadecaythreshold="50"
karmaincrease="0.1"
karmaincreasethreshold="50"
structurerepairkarmaincrease="0.05"
structuredamagekarmadecrease="0.1"
itemrepairkarmaincrease="0.03"
reactoroverheatkarmadecrease="0.5"
reactormeltdownkarmadecrease="30"
damageenemykarmaincrease="0.1"
damagefriendlykarmadecrease="0.2"
extinguishfirekarmaincrease="1"
allowedwiredisconnectionsperminute="5"
wiredisconnectionkarmadecrease="6.0"
steersubkarmaincrease="0.15"
spamfilterkarmadecrease="15"
herpesthreshold="40"
kickbanthreshold="1"
karmanotificationinterval="10" />
</KarmaManager>
@@ -25,6 +25,7 @@
<Command name="kickid"/> <Command name="kickid"/>
<Command name="campaigninfo"/> <Command name="campaigninfo"/>
<Command name="campaigndestination"/> <Command name="campaigndestination"/>
<Command name="respawnnow"/>
</Preset> </Preset>
<Preset <Preset
@@ -76,5 +77,6 @@
<Command name="power"/> <Command name="power"/>
<Command name="oxygen"/> <Command name="oxygen"/>
<Command name="setclientcharacter"/> <Command name="setclientcharacter"/>
<Command name="respawnnow"/>
</Preset> </Preset>
</PermissionPresets> </PermissionPresets>
@@ -66,6 +66,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\Afflictions\AfflictionHusk.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\Afflictions\AfflictionHusk.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\Afflictions\AfflictionPrefab.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\Afflictions\AfflictionPrefab.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\Afflictions\AfflictionPsychosis.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\Afflictions\AfflictionPsychosis.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\Afflictions\AfflictionSpaceHerpes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\Buffs\BuffDurationIncrease.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\Buffs\BuffDurationIncrease.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\CharacterHealth.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\CharacterHealth.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\DamageModifier.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\DamageModifier.cs" />
@@ -226,6 +227,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\EntitySpawner.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\EntitySpawner.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\FileTransfer\FileTransfer.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\FileTransfer\FileTransfer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\INetSerializable.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\INetSerializable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\KarmaManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetBufferExtensions.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetBufferExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetConfig.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetConfig.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetEntityEvent\NetEntityEvent.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetEntityEvent\NetEntityEvent.cs" />
@@ -481,6 +481,9 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Map\LabelLetters.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Map\LabelLetters.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\MapPieces.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\OpbgMaintenanceBay.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Map\OpbgMaintenanceBay.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@@ -683,6 +686,9 @@
<Content Include="$(MSBuildThisFileDirectory)Content\UI\tutorialAtlas.png"> <Content Include="$(MSBuildThisFileDirectory)Content\UI\tutorialAtlas.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Data\karmasettings.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="$(MSBuildThisFileDirectory)Concentus_LICENSE"> <None Include="$(MSBuildThisFileDirectory)Concentus_LICENSE">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@@ -1640,54 +1646,6 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapLine.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Map\MapLine.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_01.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_02.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_03.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_04.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_05.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_06.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_07.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_08.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_09.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_10.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_11.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_12.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_13.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_14.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_15.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\MapPieces\btMAP_Test_16.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\Reticles.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Map\Reticles.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@@ -2183,9 +2141,105 @@
<None Include="$(MSBuildThisFileDirectory)Content\Items\PowerOnLight3.ogg"> <None Include="$(MSBuildThisFileDirectory)Content\Items\PowerOnLight3.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Reactor\ReactorOverheatAlarm.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ElectricalDischarger.ogg"> <None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ElectricalDischarger.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\FlameThrowerLoop.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\FragGrenade40mmShot1.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\FragGrenade40mmShot2.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\GrenadeLauncherShot1.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\GrenadeLauncherShot2.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\GrenadeLauncherShot3.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ShotgunLoad1.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ShotgunLoad2.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ShotgunLoad3.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ShotgunShot1.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ShotgunShot2.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ShotgunShot3.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ShotgunShot4.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\SMGsingleShot1.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\SMGsingleShot2.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\SMGsingleShot3.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\SMGsingleShot4.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\SMGsingleShot5.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\SMGsingleShot6.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\StunGrenade40mmShot1.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\StunGrenade40mmShot2.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\TaserHit1.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\TaserHit2.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\TaserShot1.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\TaserShot2.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ToyHammerHit1.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ToyHammerHit2.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ToyHammerHit3.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ToyHammerHit4.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ToyHammerHit5.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\ToyHammerHit6.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Map\Outposts\Outpost2.sub"> <None Include="$(MSBuildThisFileDirectory)Content\Map\Outposts\Outpost2.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@@ -64,6 +64,12 @@ namespace Barotrauma
} }
} }
public float SonarDisruption
{
get;
set;
}
public string SonarLabel; public string SonarLabel;
public bool Enabled = true; public bool Enabled = true;
@@ -127,6 +133,7 @@ namespace Barotrauma
MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange); MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange);
MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange); MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange);
FadeOutTime = element.GetAttributeFloat("fadeouttime", FadeOutTime); FadeOutTime = element.GetAttributeFloat("fadeouttime", FadeOutTime);
SonarDisruption = element.GetAttributeFloat("sonardisruption", 0.0f);
SonarLabel = element.GetAttributeString("sonarlabel", ""); SonarLabel = element.GetAttributeString("sonarlabel", "");
string typeString = element.GetAttributeString("type", "Any"); string typeString = element.GetAttributeString("type", "Any");
if (Enum.TryParse(typeString, out TargetType t)) if (Enum.TryParse(typeString, out TargetType t))
@@ -841,7 +841,7 @@ namespace Barotrauma
private Limb GetAttackLimb(Vector2 attackWorldPos, Limb ignoredLimb = null) private Limb GetAttackLimb(Vector2 attackWorldPos, Limb ignoredLimb = null)
{ {
AttackContext currentContext = Character.GetAttackContext(); AttackContext currentContext = Character.GetAttackContext();
var target = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity; var target = wallTarget != null ? wallTarget.Structure : SelectedAiTarget?.Entity;
Limb selectedLimb = null; Limb selectedLimb = null;
float currentPriority = 0; float currentPriority = 0;
foreach (Limb limb in Character.AnimController.Limbs) foreach (Limb limb in Character.AnimController.Limbs)
@@ -163,7 +163,7 @@ namespace Barotrauma
{ {
Weapon = null; Weapon = null;
} }
else if (!WeaponComponent.HasRequiredContainedItems(false)) else if (!WeaponComponent.HasRequiredContainedItems(character, addMessage: false))
{ {
// Seek ammunition only if cannot find a new weapon // Seek ammunition only if cannot find a new weapon
if (!Reload(!HoldPosition, () => GetWeapon(out _) == null)) if (!Reload(!HoldPosition, () => GetWeapon(out _) == null))
@@ -234,14 +234,14 @@ namespace Barotrauma
{ {
if (component is RangedWeapon rw) if (component is RangedWeapon rw)
{ {
if (ignoreRequiredItems || rw.HasRequiredContainedItems(false)) if (ignoreRequiredItems || rw.HasRequiredContainedItems(character, addMessage: false))
{ {
weapons.Add(rw); weapons.Add(rw);
} }
} }
else if (component is MeleeWeapon mw) else if (component is MeleeWeapon mw)
{ {
if (ignoreRequiredItems || mw.HasRequiredContainedItems(false)) if (ignoreRequiredItems || mw.HasRequiredContainedItems(character, addMessage: false))
{ {
weapons.Add(mw); weapons.Add(mw);
} }
@@ -257,7 +257,7 @@ namespace Barotrauma
{ {
if (statusEffect.Afflictions.Any()) if (statusEffect.Afflictions.Any())
{ {
if (ignoreRequiredItems || component.HasRequiredContainedItems(false)) if (ignoreRequiredItems || component.HasRequiredContainedItems(character, addMessage: false))
{ {
weapons.Add(component); weapons.Add(component);
} }
@@ -284,7 +284,7 @@ namespace Barotrauma
private bool Equip() private bool Equip()
{ {
if (character.LockHands) { return false; } if (character.LockHands) { return false; }
if (!WeaponComponent.HasRequiredContainedItems(false)) if (!WeaponComponent.HasRequiredContainedItems(character, addMessage: false))
{ {
Mode = CombatMode.Retreat; Mode = CombatMode.Retreat;
return false; return false;
@@ -428,7 +428,7 @@ namespace Barotrauma
} }
} }
} }
if (WeaponComponent.HasRequiredContainedItems(false)) if (WeaponComponent.HasRequiredContainedItems(character, addMessage: false))
{ {
return true; return true;
} }
@@ -108,6 +108,7 @@ namespace Barotrauma
else else
{ {
move = false; move = false;
character.SetInput(extinguisher.Item.IsShootable ? InputType.Shoot : InputType.Use, false, true);
extinguisher.Use(deltaTime, character); extinguisher.Use(deltaTime, character);
if (!targetHull.FireSources.Contains(fs)) if (!targetHull.FireSources.Contains(fs))
{ {
@@ -4,7 +4,6 @@ using System;
using System.Linq; using System.Linq;
using Barotrauma.Extensions; using Barotrauma.Extensions;
using FarseerPhysics; using FarseerPhysics;
using Barotrauma.Items.Components;
namespace Barotrauma namespace Barotrauma
{ {
@@ -207,7 +207,7 @@ namespace Barotrauma
bool remove = false; bool remove = false;
foreach (ItemComponent ic in item.Components) foreach (ItemComponent ic in item.Components)
{ {
if (!ic.HasRequiredContainedItems(addMessage: false)) { continue; } if (!ic.HasRequiredContainedItems(user: character, addMessage: false)) { continue; }
#if CLIENT #if CLIENT
ic.PlaySound(ActionType.OnUse, character.WorldPosition, character); ic.PlaySound(ActionType.OnUse, character.WorldPosition, character);
#endif #endif
@@ -50,23 +50,13 @@ namespace Barotrauma
if (!IsRemotePlayer) if (!IsRemotePlayer)
{ {
float characterDist = Vector2.DistanceSquared(cam.WorldViewCenter, WorldPosition); float characterDist = float.MaxValue;
#if SERVER #if CLIENT
characterDist = Vector2.DistanceSquared(cam.GetPosition(), WorldPosition);
#elif SERVER
if (GameMain.Server != null) if (GameMain.Server != null)
{ {
//get the distance from the closest player to this character characterDist = GetClosestDistance();
foreach (Character c in CharacterList)
{
if (c != this && c.IsRemotePlayer)
{
float dist = Vector2.DistanceSquared(c.WorldPosition, WorldPosition);
if (dist < characterDist)
{
characterDist = dist;
if (characterDist < DisableSimplePhysicsDistSqr) break;
}
}
}
} }
#endif #endif
@@ -90,5 +80,50 @@ namespace Barotrauma
aiController.Update(deltaTime); aiController.Update(deltaTime);
} }
} }
#if SERVER
// Gets the closest distance, either an active player character or spectator
private float GetClosestDistance()
{
float minDist = float.MaxValue;
for (int i = 0; i < GameMain.Server.ConnectedClients.Count; i++)
{
var spectatePos = GameMain.Server.ConnectedClients[i].SpectatePos;
if (spectatePos != null)
{
float dist = Vector2.DistanceSquared(spectatePos.Value, WorldPosition);
if (dist < minDist)
{
minDist = dist;
}
if (dist < DisableSimplePhysicsDistSqr)
{
return dist;
}
}
}
foreach (Character c in CharacterList)
{
if (c != this && c.IsRemotePlayer)
{
float dist = Vector2.DistanceSquared(c.WorldPosition, WorldPosition);
if (dist < minDist)
{
minDist = dist;
}
if (dist < DisableSimplePhysicsDistSqr)
{
return dist;
}
}
}
return minDist;
}
#endif
} }
} }
@@ -139,8 +139,9 @@ namespace Barotrauma
Collider.FarseerBody.FixedRotation = false; Collider.FarseerBody.FixedRotation = false;
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
{ {
Collider.LinearVelocity = MainLimb.LinearVelocity; Collider.Enabled = false;
Collider.FarseerBody.FixedRotation = false; Collider.FarseerBody.FixedRotation = false;
Collider.LinearVelocity = MainLimb.LinearVelocity;
Collider.SetTransformIgnoreContacts(MainLimb.SimPosition, MainLimb.Rotation); Collider.SetTransformIgnoreContacts(MainLimb.SimPosition, MainLimb.Rotation);
} }
if (character.IsDead && deathAnimTimer < deathAnimDuration) if (character.IsDead && deathAnimTimer < deathAnimDuration)
@@ -286,7 +286,7 @@ namespace Barotrauma
var waistJoint = GetJointBetweenLimbs(LimbType.Waist, upperLegType); var waistJoint = GetJointBetweenLimbs(LimbType.Waist, upperLegType);
Vector2 localAnchorWaist = Vector2.Zero; Vector2 localAnchorWaist = Vector2.Zero;
Vector2 localAnchorKnee = Vector2.Zero; Vector2 localAnchorKnee = Vector2.Zero;
if (shoulder != null) if (waistJoint != null)
{ {
localAnchorWaist = waistJoint.LimbA.type == upperLegType ? waistJoint.LocalAnchorA : waistJoint.LocalAnchorB; localAnchorWaist = waistJoint.LimbA.type == upperLegType ? waistJoint.LocalAnchorA : waistJoint.LocalAnchorB;
} }
@@ -298,6 +298,7 @@ namespace Barotrauma
upperLegLength = Vector2.Distance(localAnchorWaist, localAnchorKnee); upperLegLength = Vector2.Distance(localAnchorWaist, localAnchorKnee);
LimbJoint ankleJoint = GetJointBetweenLimbs(lowerLegType, footType); LimbJoint ankleJoint = GetJointBetweenLimbs(lowerLegType, footType);
if (ankleJoint == null || kneeJoint == null) { return; }
lowerLegLength = Vector2.Distance( lowerLegLength = Vector2.Distance(
kneeJoint.LimbA.type == lowerLegType ? kneeJoint.LocalAnchorA : kneeJoint.LocalAnchorB, kneeJoint.LimbA.type == lowerLegType ? kneeJoint.LocalAnchorA : kneeJoint.LocalAnchorB,
ankleJoint.LimbA.type == lowerLegType ? ankleJoint.LocalAnchorA : ankleJoint.LocalAnchorB); ankleJoint.LimbA.type == lowerLegType ? ankleJoint.LocalAnchorA : ankleJoint.LocalAnchorB);
@@ -537,14 +538,13 @@ namespace Barotrauma
float limpAmount = float limpAmount =
character.CharacterHealth.GetAfflictionStrength("damage", leftFoot, true) + character.CharacterHealth.GetAfflictionStrength("damage", leftFoot, true) +
character.CharacterHealth.GetAfflictionStrength("damage", rightFoot, true); character.CharacterHealth.GetAfflictionStrength("damage", rightFoot, true) +
character.CharacterHealth.GetAfflictionStrength("spaceherpes");
limpAmount = MathHelper.Clamp(limpAmount / 100.0f, 0.0f, 1.0f); limpAmount = MathHelper.Clamp(limpAmount / 100.0f, 0.0f, 1.0f);
float walkCycleMultiplier = 1.0f; float walkCycleMultiplier = 1.0f;
if (Stairs != null) if (Stairs != null)
{ {
//TODO: allow editing these values in character editor?
bool running = Math.Abs(targetMovement.X) > 2.0f;
TargetMovement = new Vector2(MathHelper.Clamp(TargetMovement.X, -1.7f, 1.7f), TargetMovement.Y); TargetMovement = new Vector2(MathHelper.Clamp(TargetMovement.X, -1.7f, 1.7f), TargetMovement.Y);
walkCycleMultiplier *= 1.5f; walkCycleMultiplier *= 1.5f;
} }
@@ -579,7 +579,7 @@ namespace Barotrauma
if (limpAmount > 0.0f) if (limpAmount > 0.0f)
{ {
//make the footpos oscillate when limping //make the footpos oscillate when limping
footMid += (Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(TargetMovement.X), 0.3f)); footMid += (Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(TargetMovement.X), 0.3f)) * Dir;
} }
movement = overrideTargetMovement == Vector2.Zero ? movement = overrideTargetMovement == Vector2.Zero ?
@@ -663,7 +663,13 @@ namespace Barotrauma
} }
} }
if (TorsoAngle.HasValue) torso.body.SmoothRotate(TorsoAngle.Value * Dir, 50.0f); if (TorsoAngle.HasValue)
{
float torsoAngle = TorsoAngle.Value;
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
torsoAngle -= herpesStrength / 150.0f;
torso.body.SmoothRotate(torsoAngle * Dir, 50.0f);
}
if (HeadAngle.HasValue) head.body.SmoothRotate(HeadAngle.Value * Dir, 50.0f); if (HeadAngle.HasValue) head.body.SmoothRotate(HeadAngle.Value * Dir, 50.0f);
if (!onGround) if (!onGround)
@@ -689,7 +695,6 @@ namespace Barotrauma
for (int i = -1; i < 2; i += 2) for (int i = -1; i < 2; i += 2)
{ {
Limb foot = i == -1 ? leftFoot : rightFoot; Limb foot = i == -1 ? leftFoot : rightFoot;
Limb leg = i == -1 ? leftLeg : rightLeg;
Vector2 footPos = stepSize * -i; Vector2 footPos = stepSize * -i;
footPos += new Vector2(Math.Sign(movement.X) * FootMoveOffset.X, FootMoveOffset.Y); footPos += new Vector2(Math.Sign(movement.X) * FootMoveOffset.X, FootMoveOffset.Y);
@@ -706,6 +711,15 @@ namespace Barotrauma
} }
footPos.Y = Math.Min(waistPos.Y - colliderPos.Y - 0.4f, footPos.Y); footPos.Y = Math.Min(waistPos.Y - colliderPos.Y - 0.4f, footPos.Y);
#if CLIENT
if ((i == 1 && Math.Sign(Math.Sin(WalkPos)) > 0 && Math.Sign(walkPosY) < 0) ||
(i == -1 && Math.Sign(Math.Sin(WalkPos)) < 0 && Math.Sign(walkPosY) > 0))
{
PlayImpactSound(foot);
}
#endif
if (!foot.Disabled) if (!foot.Disabled)
{ {
foot.DebugRefPos = colliderPos; foot.DebugRefPos = colliderPos;
@@ -766,7 +780,6 @@ namespace Barotrauma
} }
var foot = i == -1 ? rightFoot : leftFoot; var foot = i == -1 ? rightFoot : leftFoot;
Limb leg = i == -1 ? rightLeg : leftLeg;
if (!foot.Disabled) if (!foot.Disabled)
{ {
@@ -96,16 +96,16 @@ namespace Barotrauma
public virtual AnimationType AnimationType { get; protected set; } public virtual AnimationType AnimationType { get; protected set; }
public static string GetDefaultFileName(string speciesName, AnimationType animType) => $"{speciesName.CapitaliseFirstInvariant()}{animType.ToString()}"; public static string GetDefaultFileName(string speciesName, AnimationType animType) => $"{speciesName.CapitaliseFirstInvariant()}{animType.ToString()}";
public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Animations/";
public static string GetDefaultFile(string speciesName, AnimationType animType, ContentPackage contentPackage = null) => public static string GetDefaultFile(string speciesName, AnimationType animType, ContentPackage contentPackage = null) =>
$"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName, animType)}.xml"; $"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName, animType)}.xml";
public static string GetFolder(string speciesName, ContentPackage contentPackage = null) public static string GetFolder(string speciesName, ContentPackage contentPackage = null)
{ {
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName, contentPackage))?.Root?.Element("animations")?.GetAttributeString("folder", string.Empty); string configFilePath = Character.GetConfigFile(speciesName, contentPackage);
var folder = XMLExtensions.TryLoadXml(configFilePath)?.Root?.Element("animations")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default") if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
{ {
folder = GetDefaultFolder(speciesName); folder = Path.Combine(Path.GetDirectoryName(configFilePath), "Animations");
} }
return folder; return folder;
} }
@@ -66,7 +66,6 @@ namespace Barotrauma
.Concat(Joints.Select(j => j as RagdollSubParams))); .Concat(Joints.Select(j => j as RagdollSubParams)));
public static string GetDefaultFileName(string speciesName) => $"{speciesName.CapitaliseFirstInvariant()}DefaultRagdoll"; public static string GetDefaultFileName(string speciesName) => $"{speciesName.CapitaliseFirstInvariant()}DefaultRagdoll";
public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Ragdolls/";
public static string GetDefaultFile(string speciesName, ContentPackage contentPackage = null) => $"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName)}.xml"; public static string GetDefaultFile(string speciesName, ContentPackage contentPackage = null) => $"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName)}.xml";
private static readonly object[] dummyParams = new object[] private static readonly object[] dummyParams = new object[]
@@ -84,11 +83,11 @@ namespace Barotrauma
public static string GetFolder(string speciesName, ContentPackage contentPackage = null) public static string GetFolder(string speciesName, ContentPackage contentPackage = null)
{ {
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName, contentPackage))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty); string configFilePath = Character.GetConfigFile(speciesName, contentPackage);
var folder = XMLExtensions.TryLoadXml(configFilePath)?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default") if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
{ {
//DebugConsole.NewMessage("[RagollParams] Using the default folder."); folder = Path.Combine(Path.GetDirectoryName(configFilePath), "Ragdolls") + Path.DirectorySeparatorChar;
folder = GetDefaultFolder(speciesName);
} }
return folder; return folder;
} }
@@ -1047,11 +1047,16 @@ namespace Barotrauma
protected bool levitatingCollider = true; protected bool levitatingCollider = true;
/// <summary>
/// How long has the ragdoll stayed motionless
/// </summary>
private float bodyInRestTimer;
public bool forceStanding; public bool forceStanding;
public void Update(float deltaTime, Camera cam) public void Update(float deltaTime, Camera cam)
{ {
if (!character.Enabled || Frozen || Invalid) return; if (!character.Enabled || Frozen || Invalid) { return; }
CheckValidity(); CheckValidity();
@@ -1063,6 +1068,8 @@ namespace Barotrauma
FindHull(); FindHull();
PreventOutsideCollision(); PreventOutsideCollision();
CheckBodyInRest(deltaTime);
splashSoundTimer -= deltaTime; splashSoundTimer -= deltaTime;
@@ -1315,6 +1322,29 @@ namespace Barotrauma
UpdateProjSpecific(deltaTime); UpdateProjSpecific(deltaTime);
} }
private void CheckBodyInRest(float deltaTime)
{
if (Collider.LinearVelocity.LengthSquared() > 0.01f || character.SelectedBy != null || !character.IsDead)
{
bodyInRestTimer = 0.0f;
foreach (Limb limb in Limbs)
{
limb.body.PhysEnabled = true;
}
}
else if (Limbs.All(l => l != null && !l.body.Enabled || l.LinearVelocity.LengthSquared() < 0.001f))
{
bodyInRestTimer += deltaTime;
if (bodyInRestTimer > 1.0f)
{
foreach (Limb limb in Limbs)
{
limb.body.PhysEnabled = false;
}
}
}
}
public bool Invalid { get; private set; } public bool Invalid { get; private set; }
private int validityResets; private int validityResets;
private bool CheckValidity() private bool CheckValidity()
@@ -298,7 +298,10 @@ namespace Barotrauma
} }
float afflictionStrength = subElement.GetAttributeFloat(1.0f, "amount", "strength"); float afflictionStrength = subElement.GetAttributeFloat(1.0f, "amount", "strength");
Afflictions.Add(afflictionPrefab.Instantiate(afflictionStrength)); var affliction = afflictionPrefab.Instantiate(afflictionStrength);
affliction.ApplyProbability = subElement.GetAttributeFloat("probability", 1.0f);
Afflictions.Add(affliction);
break; break;
case "conditional": case "conditional":
foreach (XAttribute attribute in subElement.Attributes()) foreach (XAttribute attribute in subElement.Attributes())
@@ -743,31 +743,33 @@ namespace Barotrauma
PressureProtection = 100.0f; PressureProtection = 100.0f;
} }
List<XElement> inventoryElements = new List<XElement>();
List<float> inventoryCommonness = new List<float>();
List<XElement> healthElements = new List<XElement>();
List<float> healthCommonness = new List<float>();
foreach (XElement subElement in doc.Root.Elements()) foreach (XElement subElement in doc.Root.Elements())
{ {
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
{ {
case "inventory": case "inventory":
Inventory = new CharacterInventory(subElement, this); inventoryElements.Add(subElement);
inventoryCommonness.Add(subElement.GetAttributeFloat("commonness", 1.0f));
break; break;
case "health": case "health":
CharacterHealth = new CharacterHealth(subElement, this); healthElements.Add(subElement);
healthCommonness.Add(subElement.GetAttributeFloat("commonness", 1.0f));
break; break;
case "statuseffect": case "statuseffect":
statusEffects.Add(StatusEffect.Load(subElement, Name)); statusEffects.Add(StatusEffect.Load(subElement, Name));
break; break;
} }
} }
if (inventoryElements.Count > 0)
List<XElement> healthElements = new List<XElement>();
List<float> healthCommonness = new List<float>();
foreach (XElement element in doc.Root.Elements())
{ {
if (element.Name.ToString().ToLowerInvariant() != "health") continue; Inventory = new CharacterInventory(
healthElements.Add(element); inventoryElements.Count == 1 ? inventoryElements[0] : ToolBox.SelectWeightedRandom(inventoryElements, inventoryCommonness, random),
healthCommonness.Add(element.GetAttributeFloat("commonness", 1.0f)); this);
} }
if (healthElements.Count == 0) if (healthElements.Count == 0)
{ {
CharacterHealth = new CharacterHealth(this); CharacterHealth = new CharacterHealth(this);
@@ -834,6 +836,7 @@ namespace Barotrauma
#if CLIENT #if CLIENT
head.LoadHuskSprite(); head.LoadHuskSprite();
head.LoadHerpesSprite();
#endif #endif
} }
@@ -977,7 +980,30 @@ namespace Barotrauma
return false; return false;
} }
#endif #endif
if (inputType == InputType.Up || inputType == InputType.Down ||
inputType == InputType.Left || inputType == InputType.Right)
{
var invertControls = CharacterHealth.GetAffliction("invertcontrols");
if (invertControls != null)
{
switch (inputType)
{
case InputType.Left:
inputType = InputType.Right;
break;
case InputType.Right:
inputType = InputType.Left;
break;
case InputType.Up:
inputType = InputType.Down;
break;
case InputType.Down:
inputType = InputType.Up;
break;
}
}
}
return keys[(int)inputType].Held; return keys[(int)inputType].Held;
} }
@@ -1579,9 +1605,16 @@ namespace Barotrauma
//locked wires are never interactable //locked wires are never interactable
if (wire.Locked) return false; if (wire.Locked) return false;
//wires are interactable if the character has selected either of the items the wire is connected to //wires are interactable if the character has selected an item the wire is connected to,
if (wire.Connections[0]?.Item != null && SelectedConstruction == wire.Connections[0].Item) return true; //and it's disconnected from the other end
if (wire.Connections[1]?.Item != null && SelectedConstruction == wire.Connections[1].Item) return true; if (wire.Connections[0]?.Item != null && SelectedConstruction == wire.Connections[0].Item)
{
return wire.Connections[1] == null;
}
if (wire.Connections[1]?.Item != null && SelectedConstruction == wire.Connections[1].Item)
{
return wire.Connections[0] == null;
}
} }
if (checkLinked && item.DisplaySideBySideWhenLinked) if (checkLinked && item.DisplaySideBySideWhenLinked)
@@ -1891,15 +1924,30 @@ namespace Barotrauma
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(otherCharacter.WorldPosition, c.WorldPosition)); distSqr = Math.Min(distSqr, Vector2.DistanceSquared(otherCharacter.WorldPosition, c.WorldPosition));
} }
#if SERVER
for (int i = 0; i < GameMain.Server.ConnectedClients.Count; i++)
{
var spectatePos = GameMain.Server.ConnectedClients[i].SpectatePos;
if (spectatePos != null)
{
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(spectatePos.Value, c.WorldPosition));
}
}
#endif
if (distSqr > NetConfig.DisableCharacterDistSqr) if (distSqr > NetConfig.DisableCharacterDistSqr)
{ {
c.Enabled = false; c.Enabled = false;
if (c.IsDead && c.AIController is EnemyAIController)
{
Entity.Spawner?.AddToRemoveQueue(c);
}
} }
else if (distSqr < NetConfig.EnableCharacterDistSqr) else if (distSqr < NetConfig.EnableCharacterDistSqr)
{ {
c.Enabled = true; c.Enabled = true;
} }
} }
} }
else if (Submarine.MainSub != null) else if (Submarine.MainSub != null)
{ {
@@ -1907,19 +1955,22 @@ namespace Barotrauma
float distSqr = Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition); float distSqr = Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition);
if (Controlled != null) if (Controlled != null)
{ {
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(Controlled.WorldPosition, c.WorldPosition)); distSqr = Math.Min(distSqr, Vector2.DistanceSquared(GameMain.GameScreen.Cam.GetPosition(), c.WorldPosition));
} }
if (distSqr > NetConfig.DisableCharacterDistSqr) if (distSqr > NetConfig.DisableCharacterDistSqr)
{ {
c.Enabled = false; c.Enabled = false;
if (c.IsDead && c.AIController is EnemyAIController)
{
Entity.Spawner?.AddToRemoveQueue(c);
}
} }
else if ( distSqr < NetConfig.EnableCharacterDistSqr) else if (distSqr < NetConfig.EnableCharacterDistSqr)
{ {
c.Enabled = true; c.Enabled = true;
} }
} }
} }
} }
@@ -2261,8 +2312,6 @@ namespace Barotrauma
speechBubbleColor = color; speechBubbleColor = color;
} }
partial void AdjustKarma(Character attacker, AttackResult attackResult);
partial void DamageHUD(float amount); partial void DamageHUD(float amount);
public void SetAllDamage(float damageAmount, float bleedingDamageAmount, float burnDamageAmount) public void SetAllDamage(float damageAmount, float bleedingDamageAmount, float burnDamageAmount)
@@ -2358,7 +2407,12 @@ namespace Barotrauma
{ {
hitLimb = null; hitLimb = null;
if (Removed) return new AttackResult(); if (Removed) { return new AttackResult(); }
if (attacker != null && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
{
if (attacker.TeamID == TeamID) { return new AttackResult(); }
}
float closestDistance = 0.0f; float closestDistance = 0.0f;
foreach (Limb limb in AnimController.Limbs) foreach (Limb limb in AnimController.Limbs)
@@ -2376,7 +2430,12 @@ namespace Barotrauma
public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, List<Affliction> afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null) public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, List<Affliction> afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null)
{ {
if (Removed) return new AttackResult(); if (Removed) { return new AttackResult(); }
if (attacker != null && attacker != this && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
{
if (attacker.TeamID == TeamID) { return new AttackResult(); }
}
SetStun(stun); SetStun(stun);
Vector2 dir = hitLimb.WorldPosition - worldPosition; Vector2 dir = hitLimb.WorldPosition - worldPosition;
@@ -2395,7 +2454,6 @@ namespace Barotrauma
OnAttacked?.Invoke(attacker, attackResult); OnAttacked?.Invoke(attacker, attackResult);
OnAttackedProjSpecific(attacker, attackResult); OnAttackedProjSpecific(attacker, attackResult);
}; };
AdjustKarma(attacker, attackResult);
if (attacker != null && attackResult.Damage > 0.0f) if (attacker != null && attackResult.Damage > 0.0f)
{ {
@@ -18,6 +18,11 @@ namespace Barotrauma
public float StrengthDiminishMultiplier = 1.0f; public float StrengthDiminishMultiplier = 1.0f;
public Affliction MultiplierSource; public Affliction MultiplierSource;
/// <summary>
/// Probability for the affliction to be applied. Used by attacks.
/// </summary>
public float ApplyProbability;
/// <summary> /// <summary>
/// Which character gave this affliction /// Which character gave this affliction
/// </summary> /// </summary>
@@ -162,6 +167,7 @@ namespace Barotrauma
foreach (StatusEffect statusEffect in currentEffect.StatusEffects) foreach (StatusEffect statusEffect in currentEffect.StatusEffects)
{ {
statusEffect.SetUser(Source);
if (statusEffect.HasTargetType(StatusEffect.TargetType.Character)) if (statusEffect.HasTargetType(StatusEffect.TargetType.Character))
{ {
statusEffect.Apply(ActionType.OnActive, deltaTime, characterHealth.Character, characterHealth.Character); statusEffect.Apply(ActionType.OnActive, deltaTime, characterHealth.Character, characterHealth.Character);
@@ -157,6 +157,9 @@ namespace Barotrauma
//how high the strength has to be for the affliction icon to be shown with a health scanner //how high the strength has to be for the affliction icon to be shown with a health scanner
public readonly float ShowInHealthScannerThreshold = 0.05f; public readonly float ShowInHealthScannerThreshold = 0.05f;
//how much karma changes when a player applies this affliction to someone (per strength of the affliction)
public float KarmaChangeOnApplied;
public float BurnOverlayAlpha; public float BurnOverlayAlpha;
public float DamageOverlayAlpha; public float DamageOverlayAlpha;
@@ -265,9 +268,12 @@ namespace Barotrauma
DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f); DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f);
BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f); BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f);
KarmaChangeOnApplied = element.GetAttributeFloat("karmachangeonapplied", 0.0f);
CauseOfDeathDescription = TextManager.Get("AfflictionCauseOfDeath." + Identifier, true) ?? element.GetAttributeString("causeofdeathdescription", ""); CauseOfDeathDescription = TextManager.Get("AfflictionCauseOfDeath." + Identifier, true) ?? element.GetAttributeString("causeofdeathdescription", "");
SelfCauseOfDeathDescription = TextManager.Get("AfflictionCauseOfDeathSelf." + Identifier, true) ?? element.GetAttributeString("selfcauseofdeathdescription", ""); SelfCauseOfDeathDescription = TextManager.Get("AfflictionCauseOfDeathSelf." + Identifier, true) ?? element.GetAttributeString("selfcauseofdeathdescription", "");
AchievementOnRemoved = element.GetAttributeString("achievementonremoved", ""); AchievementOnRemoved = element.GetAttributeString("achievementonremoved", "");
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
@@ -0,0 +1,47 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma
{
class AfflictionSpaceHerpes : Affliction
{
private float invertControlsCooldown = 60.0f;
private float stunCoolDown = 60.0f;
public AfflictionSpaceHerpes(AfflictionPrefab prefab, float strength) : base(prefab, strength)
{
}
public override void Update(CharacterHealth characterHealth, Limb targetLimb, float deltaTime)
{
base.Update(characterHealth, targetLimb, deltaTime);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
invertControlsCooldown -= deltaTime;
if (invertControlsCooldown <= 0.0f)
{
//invert controls every 126-234 seconds when strength is close to 0
//every 56-104 seconds when strength is close to 100
invertControlsCooldown = (180.0f - Strength) * Rand.Range(0.7f, 1.3f);
var invertControlsAffliction = AfflictionPrefab.List.Find(ap => ap.Identifier == "invertcontrols");
float invertControlsDuration = MathHelper.Lerp(10.0f, 60.0f, Strength / 100.0f) * Rand.Range(0.7f, 1.3f);
characterHealth.ApplyAffliction(null, new Affliction(invertControlsAffliction, invertControlsDuration));
}
if (Strength > 50.0f)
{
stunCoolDown -= deltaTime;
if (stunCoolDown <= 0.0f)
{
//stun every 126-234 seconds when strength is close to 0
//stun 56-104 seconds when strength is close to 100
stunCoolDown = (180.0f - Strength) * Rand.Range(0.7f, 1.3f);
float stunDuration = MathHelper.Lerp(3.0f, 10.0f, Strength / 100.0f) * Rand.Range(0.7f, 1.3f);
characterHealth.Character.SetStun(stunDuration);
}
}
}
}
}
@@ -411,7 +411,7 @@ namespace Barotrauma
amount -= reduceAmount; amount -= reduceAmount;
} }
} }
CalculateVitality();
} }
public void ApplyDamage(Limb hitLimb, AttackResult attackResult) public void ApplyDamage(Limb hitLimb, AttackResult attackResult)
@@ -407,7 +407,7 @@ namespace Barotrauma
{ {
List<DamageModifier> appliedDamageModifiers = new List<DamageModifier>(); List<DamageModifier> appliedDamageModifiers = new List<DamageModifier>();
//create a copy of the original affliction list to prevent modifying the afflictions of an Attack/StatusEffect etc //create a copy of the original affliction list to prevent modifying the afflictions of an Attack/StatusEffect etc
afflictions = new List<Affliction>(afflictions); afflictions = new List<Affliction>(afflictions.Where(a => Rand.Range(0.0f, 1.0f) <= a.ApplyProbability));
for (int i = 0; i < afflictions.Count; i++) for (int i = 0; i < afflictions.Count; i++)
{ {
foreach (DamageModifier damageModifier in damageModifiers) foreach (DamageModifier damageModifier in damageModifiers)
@@ -485,6 +485,22 @@ namespace Barotrauma
List.Add(package); List.Add(package);
} }
} }
public void Delete()
{
try
{
File.Delete(Path);
GameMain.Config.SelectedContentPackages.Remove(this);
GameMain.Config.SaveNewPlayerConfig();
}
catch (IOException e)
{
DebugConsole.ThrowError("Failed to delete content package \"" + Name + "\".", e);
return;
}
List.Remove(this);
}
} }
public class ContentFile public class ContentFile
@@ -136,7 +136,7 @@ namespace Barotrauma
if (!handle.Coroutine.MoveNext()) return; if (!handle.Coroutine.MoveNext()) return;
} }
} }
catch (ThreadAbortException tae) catch (ThreadAbortException)
{ {
//not an error, don't worry about it //not an error, don't worry about it
} }
@@ -110,7 +110,15 @@ namespace Barotrauma
private static void AssignOnExecute(string names, Action<string[]> onExecute) private static void AssignOnExecute(string names, Action<string[]> onExecute)
{ {
commands.First(c => c.names.Intersect(names.Split('|')).Count() > 0).OnExecute = onExecute; var matchingCommand = commands.Find(c => c.names.Intersect(names.Split('|')).Count() > 0);
if (matchingCommand == null)
{
throw new Exception("AssignOnExecute failed. Command matching the name(s) \""+names+"\" not found.");
}
else
{
matchingCommand.OnExecute = onExecute;
}
} }
static DebugConsole() static DebugConsole()
@@ -251,16 +259,11 @@ namespace Barotrauma
spawnPosParams.ToArray() spawnPosParams.ToArray()
}; };
}, isCheat: true)); }, isCheat: true));
commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) => commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) =>
{ {
HumanAIController.DisableCrewAI = true; HumanAIController.DisableCrewAI = true;
NewMessage("Crew AI disabled", Color.Red); NewMessage("Crew AI disabled", Color.Red);
// This is probably not where it should be?
//ThrowError("Karma has not been fully implemented yet, and is disabled in this version of Barotrauma.");
/*if (GameMain.Server == null) return;
GameMain.Server.KarmaEnabled = !GameMain.Server.KarmaEnabled;*/
})); }));
commands.Add(new Command("enablecrewai", "enablecrewai: Enable the AI of the NPCs in the crew.", (string[] args) => commands.Add(new Command("enablecrewai", "enablecrewai: Enable the AI of the NPCs in the crew.", (string[] args) =>
@@ -304,8 +307,31 @@ namespace Barotrauma
commands.Add(new Command("revokecommandperm", "revokecommandperm [id]: Revokes permission to use the specified console commands from the player with the specified client ID.", null)); commands.Add(new Command("revokecommandperm", "revokecommandperm [id]: Revokes permission to use the specified console commands from the player with the specified client ID.", null));
commands.Add(new Command("showperm", "showperm [id]: Shows the current administrative permissions of the client with the specified client ID.", null)); commands.Add(new Command("showperm", "showperm [id]: Shows the current administrative permissions of the client with the specified client ID.", null));
commands.Add(new Command("respawnnow", "respawnnow: Trigger a respawn immediately if there are any clients waiting to respawn.", null));
//commands.Add(new Command("togglekarma", "togglekarma: Toggles the karma system.", null)); commands.Add(new Command("showkarma", "showkarma: Show the current karma values of the players.", null));
commands.Add(new Command("togglekarma", "togglekarma: Toggle the karma system on/off.", null));
commands.Add(new Command("resetkarma", "resetkarma [client]: Resets the karma value of the specified client to 100.", null,
() =>
{
if (GameMain.NetworkMember?.ConnectedClients == null) { return null; }
return new string[][]
{
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray()
};
}));
commands.Add(new Command("setkarma", "setkarma [client] [0-100]: Sets the karma of the specified client to the specified value.", null,
() =>
{
if (GameMain.NetworkMember?.ConnectedClients == null) { return null; }
return new string[][]
{
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray(),
new string[] { "50" }
};
}));
commands.Add(new Command("togglekarmatestmode", "togglekarmatestmode: Toggle the karma test mode on/off. When test mode is enabled, clients get notified when their karma value changes (including the reason for the increase/decrease) and the server doesn't ban clients whose karma decreases below the ban threshold.", null));
commands.Add(new Command("kick", "kick [name]: Kick a player out of the server.", (string[] args) => commands.Add(new Command("kick", "kick [name]: Kick a player out of the server.", (string[] args) =>
{ {
@@ -654,11 +680,11 @@ namespace Barotrauma
if (args.Length > 0 && args[0].ToLowerInvariant() == "start") if (args.Length > 0 && args[0].ToLowerInvariant() == "start")
{ {
Submarine.MainSub.SetPosition(Level.Loaded.StartPosition); Submarine.MainSub.SetPosition(Level.Loaded.StartPosition - Vector2.UnitY * Submarine.MainSub.Borders.Height);
} }
else else
{ {
Submarine.MainSub.SetPosition(Level.Loaded.EndPosition); Submarine.MainSub.SetPosition(Level.Loaded.EndPosition - Vector2.UnitY * Submarine.MainSub.Borders.Height);
} }
}, isCheat: true)); }, isCheat: true));
@@ -280,16 +280,23 @@ namespace Barotrauma
float holeCount = 0.0f; float holeCount = 0.0f;
floodingAmount = 0.0f; floodingAmount = 0.0f;
int hullCount = 0;
foreach (Hull hull in Hull.hullList) foreach (Hull hull in Hull.hullList)
{ {
if (hull.Submarine == null || hull.Submarine.IsOutpost) { continue; } if (hull.Submarine == null || hull.Submarine.IsOutpost) { continue; }
hullCount++;
foreach (Gap gap in hull.ConnectedGaps) foreach (Gap gap in hull.ConnectedGaps)
{ {
if (!gap.IsRoomToRoom) holeCount += gap.Open; if (!gap.IsRoomToRoom) holeCount += gap.Open;
} }
floodingAmount += hull.WaterVolume / hull.Volume / Hull.hullList.Count; floodingAmount += hull.WaterVolume / hull.Volume;
fireAmount += hull.FireSources.Sum(fs => fs.Size.X); fireAmount += hull.FireSources.Sum(fs => fs.Size.X);
} }
if (hullCount > 0)
{
floodingAmount = floodingAmount / hullCount;
}
//hull integrity at 0.0 if there are 10 or more wide-open holes //hull integrity at 0.0 if there are 10 or more wide-open holes
avgHullIntegrity = MathHelper.Clamp(1.0f - holeCount / 10.0f, 0.0f, 1.0f); avgHullIntegrity = MathHelper.Clamp(1.0f - holeCount / 10.0f, 0.0f, 1.0f);
@@ -1,9 +1,5 @@
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -12,9 +8,6 @@ namespace Barotrauma
private Submarine[] subs; private Submarine[] subs;
private List<Character>[] crews; private List<Character>[] crews;
private bool initialized = false;
private int state = 0;
private string[] descriptions; private string[] descriptions;
private static string[] teamNames = { "Team A", "Team B" }; private static string[] teamNames = { "Team A", "Team B" };
@@ -81,10 +81,8 @@ namespace Barotrauma
bool success = bool success =
GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead); GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead);
#if CLIENT
success = success || (GameMain.Server.Character != null && !GameMain.Server.Character.IsDead); success = success || (GameMain.Server.Character != null && !GameMain.Server.Character.IsDead);
#endif
/*if (success) /*if (success)
{ {
@@ -119,11 +117,7 @@ namespace Barotrauma
{ {
c.Inventory?.DeleteAllItems(); c.Inventory?.DeleteAllItems();
} }
#if CLIENT
GameMain.GameSession.CrewManager.EndRound();
#endif
if (success) if (success)
{ {
bool atEndPosition = Submarine.MainSub.AtEndPosition; bool atEndPosition = Submarine.MainSub.AtEndPosition;

Some files were not shown because too many files have changed in this diff Show More