(6eeea9b7c) v0.9.10.0.0

This commit is contained in:
Joonas Rikkonen
2020-06-04 16:41:07 +03:00
parent ce4ccd99ac
commit eeac247a8e
366 changed files with 7772 additions and 3692 deletions
+3
View File
@@ -37,5 +37,8 @@ Libraries/webm_mem_playback/opus_x64_linux/
# Mac # Mac
*.DS_Store *.DS_Store
# Win
desktop.ini
#Merge script #Merge script
temp.txt temp.txt
@@ -200,7 +200,7 @@ namespace Barotrauma
worldView = new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight); worldView = new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
viewMatrix = Matrix.CreateTranslation(new Vector3(GameMain.GraphicsWidth / 2.0f, GameMain.GraphicsHeight / 2.0f, 0)); viewMatrix = Matrix.CreateTranslation(new Vector3(GameMain.GraphicsWidth / 2.0f, GameMain.GraphicsHeight / 2.0f, 0));
globalZoomScale = (float)Math.Pow(new Vector2(resolution.X, resolution.Y).Length() / new Vector2(1920, 1080).Length(), 2); globalZoomScale = (float)Math.Pow(new Vector2(GUI.UIWidth, resolution.Y).Length() / GUI.ReferenceResolution.Length(), 2);
} }
public void UpdateTransform(bool interpolate = true) public void UpdateTransform(bool interpolate = true)
@@ -19,11 +19,14 @@ namespace Barotrauma
var target = _selectedAiTarget ?? _lastAiTarget; var target = _selectedAiTarget ?? _lastAiTarget;
if (target != null && target.Entity != null) if (target != null && target.Entity != null)
{ {
var memory = GetTargetMemory(target); var memory = GetTargetMemory(target, false);
Vector2 targetPos = memory.Location; if (memory != null)
targetPos.Y = -targetPos.Y; {
GUI.DrawLine(spriteBatch, pos, targetPos, Color.White * 0.5f, 0, 4); Vector2 targetPos = memory.Location;
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{target.Entity.ToString()} ({memory.Priority.FormatZeroDecimal()})", Color.White, Color.Black); targetPos.Y = -targetPos.Y;
GUI.DrawLine(spriteBatch, pos, targetPos, Color.White * 0.5f, 0, 4);
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{target.Entity} ({memory.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
} }
} }
else if (SelectedAiTarget?.Entity != null) else if (SelectedAiTarget?.Entity != null)
@@ -35,7 +38,7 @@ namespace Barotrauma
} }
targetPos.Y = -targetPos.Y; targetPos.Y = -targetPos.Y;
GUI.DrawLine(spriteBatch, pos, targetPos, GUI.Style.Red * 0.5f, 0, 4); GUI.DrawLine(spriteBatch, pos, targetPos, GUI.Style.Red * 0.5f, 0, 4);
if (wallTarget != null) if (wallTarget != null && (State == AIState.Attack || State == AIState.Aggressive || State == AIState.PassiveAggressive))
{ {
Vector2 wallTargetPos = wallTarget.Position; Vector2 wallTargetPos = wallTarget.Position;
if (wallTarget.Structure.Submarine != null) { wallTargetPos += wallTarget.Structure.Submarine.Position; } if (wallTarget.Structure.Submarine != null) { wallTargetPos += wallTarget.Structure.Submarine.Position; }
@@ -43,7 +46,7 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Orange, false); GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Orange, false);
GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5); GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5);
} }
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity.ToString()} ({GetTargetMemory(SelectedAiTarget).Priority.FormatZeroDecimal()})", GUI.Style.Red, Color.Black); GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity} ({GetTargetMemory(SelectedAiTarget, false)?.Priority.FormatZeroDecimal()})", GUI.Style.Red, Color.Black);
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 40.0f, $"({targetValue.FormatZeroDecimal()})", GUI.Style.Red, Color.Black); GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 40.0f, $"({targetValue.FormatZeroDecimal()})", GUI.Style.Red, Color.Black);
} }
@@ -44,19 +44,20 @@ namespace Barotrauma
var currentObjective = ObjectiveManager.CurrentObjective; var currentObjective = ObjectiveManager.CurrentObjective;
if (currentObjective != null) if (currentObjective != null)
{ {
if (currentOrder == null) int offset = currentOrder != null ? 20 : 0;
if (currentOrder == null || currentOrder.Priority <= 0)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20 + offset), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
var subObjective = currentObjective.CurrentSubObjective; var subObjective = currentObjective.CurrentSubObjective;
if (subObjective != null) if (subObjective != null)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40 + offset), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
var activeObjective = ObjectiveManager.GetActiveObjective(); var activeObjective = ObjectiveManager.GetActiveObjective();
if (activeObjective != null) if (activeObjective != null)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60 + offset), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
} }
} }
@@ -86,7 +87,7 @@ namespace Barotrauma
new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y), new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y),
Color.BlueViolet, 0, 3); Color.BlueViolet, 0, 3);
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 80), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 100), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
} }
} }
} }
@@ -389,7 +389,7 @@ namespace Barotrauma
{ {
foreach (Limb limb in Limbs) foreach (Limb limb in Limbs)
{ {
if (limb == null || limb.IsSevered || limb.ActiveSprite == null) continue; if (limb == null || limb.IsSevered || limb.ActiveSprite == null) { continue; }
Vector2 spriteOrigin = limb.ActiveSprite.Origin; Vector2 spriteOrigin = limb.ActiveSprite.Origin;
spriteOrigin.X = limb.ActiveSprite.SourceRect.Width - spriteOrigin.X; spriteOrigin.X = limb.ActiveSprite.SourceRect.Width - spriteOrigin.X;
@@ -404,8 +404,8 @@ namespace Barotrauma
float gibParticleAmount = MathHelper.Clamp(limb.Mass / character.AnimController.Mass, 0.1f, 1.0f); float gibParticleAmount = MathHelper.Clamp(limb.Mass / character.AnimController.Mass, 0.1f, 1.0f);
foreach (ParticleEmitter emitter in character.GibEmitters) foreach (ParticleEmitter emitter in character.GibEmitters)
{ {
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) continue; if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) continue; if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
emitter.Emit(1.0f, limb.WorldPosition, character.CurrentHull, amountMultiplier: gibParticleAmount); emitter.Emit(1.0f, limb.WorldPosition, character.CurrentHull, amountMultiplier: gibParticleAmount);
} }
@@ -418,7 +418,8 @@ namespace Barotrauma
if (playSound) if (playSound)
{ {
SoundPlayer.PlayDamageSound("Gore", 1.0f, limbJoint.LimbA.body); var damageSound = character.GetSound(s => s.Type == CharacterSound.SoundType.Damage);
SoundPlayer.PlayDamageSound(limbJoint.Params.BreakSound, 1.0f, limbJoint.LimbA.body.DrawPosition, range: damageSound != null ? damageSound.Range : 800);
} }
} }
@@ -446,9 +447,10 @@ namespace Barotrauma
float depthOffset = GetDepthOffset(); float depthOffset = GetDepthOffset();
for (int i = 0; i < limbs.Length; i++) for (int i = 0; i < limbs.Length; i++)
{ {
if (depthOffset != 0.0f) { inversedLimbDrawOrder[i].ActiveSprite.Depth += depthOffset; } var limb = inversedLimbDrawOrder[i];
inversedLimbDrawOrder[i].Draw(spriteBatch, cam, color); if (depthOffset != 0.0f) { limb.ActiveSprite.Depth += depthOffset; }
if (depthOffset != 0.0f) { inversedLimbDrawOrder[i].ActiveSprite.Depth -= depthOffset; } limb.Draw(spriteBatch, cam, color);
if (depthOffset != 0.0f) { limb.ActiveSprite.Depth -= depthOffset; }
} }
LimbJoints.ForEach(j => j.Draw(spriteBatch)); LimbJoints.ForEach(j => j.Draw(spriteBatch));
} }
@@ -489,8 +491,8 @@ namespace Barotrauma
public void DebugDraw(SpriteBatch spriteBatch) public void DebugDraw(SpriteBatch spriteBatch)
{ {
if (!GameMain.DebugDraw || !character.Enabled) return; if (!GameMain.DebugDraw || !character.Enabled) { return; }
if (simplePhysicsEnabled) return; if (simplePhysicsEnabled) { return; }
foreach (Limb limb in Limbs) foreach (Limb limb in Limbs)
{ {
@@ -508,7 +510,7 @@ namespace Barotrauma
Collider.DebugDraw(spriteBatch, frozen ? GUI.Style.Red : (inWater ? Color.SkyBlue : Color.Gray)); Collider.DebugDraw(spriteBatch, frozen ? GUI.Style.Red : (inWater ? Color.SkyBlue : Color.Gray));
GUI.Font.DrawString(spriteBatch, Collider.LinearVelocity.X.FormatSingleDecimal(), new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y), Color.Orange); GUI.Font.DrawString(spriteBatch, Collider.LinearVelocity.X.FormatSingleDecimal(), new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y), Color.Orange);
foreach (RevoluteJoint joint in LimbJoints) foreach (var joint in LimbJoints)
{ {
Vector2 pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorA); Vector2 pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorA);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true); GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true);
@@ -522,7 +524,10 @@ namespace Barotrauma
if (limb.body.TargetPosition != null) if (limb.body.TargetPosition != null)
{ {
Vector2 pos = ConvertUnits.ToDisplayUnits((Vector2)limb.body.TargetPosition); Vector2 pos = ConvertUnits.ToDisplayUnits((Vector2)limb.body.TargetPosition);
if (currentHull?.Submarine != null) pos += currentHull.Submarine.DrawPosition; if (currentHull?.Submarine != null)
{
pos += currentHull.Submarine.DrawPosition;
}
pos.Y = -pos.Y; pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X - 10, (int)pos.Y - 10, 20, 20), Color.Cyan, false, 0.01f); GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X - 10, (int)pos.Y - 10, 20, 20), Color.Cyan, false, 0.01f);
@@ -541,13 +546,19 @@ namespace Barotrauma
if (character.MemState.Count > 1) if (character.MemState.Count > 1)
{ {
Vector2 prevPos = ConvertUnits.ToDisplayUnits(character.MemState[0].Position); Vector2 prevPos = ConvertUnits.ToDisplayUnits(character.MemState[0].Position);
if (currentHull?.Submarine != null) prevPos += currentHull.Submarine.DrawPosition; if (currentHull?.Submarine != null)
{
prevPos += currentHull.Submarine.DrawPosition;
}
prevPos.Y = -prevPos.Y; prevPos.Y = -prevPos.Y;
for (int i = 1; i < character.MemState.Count; i++) for (int i = 1; i < character.MemState.Count; i++)
{ {
Vector2 currPos = ConvertUnits.ToDisplayUnits(character.MemState[i].Position); Vector2 currPos = ConvertUnits.ToDisplayUnits(character.MemState[i].Position);
if (currentHull?.Submarine != null) currPos += currentHull.Submarine.DrawPosition; if (currentHull?.Submarine != null)
{
currPos += currentHull.Submarine.DrawPosition;
}
currPos.Y = -currPos.Y; currPos.Y = -currPos.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)currPos.X - 3, (int)currPos.Y - 3, 6, 6), Color.Cyan * 0.6f, true, 0.01f); GUI.DrawRectangle(spriteBatch, new Rectangle((int)currPos.X - 3, (int)currPos.Y - 3, 6, 6), Color.Cyan * 0.6f, true, 0.01f);
@@ -10,6 +10,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma namespace Barotrauma
{ {
@@ -357,7 +358,16 @@ namespace Barotrauma
partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult, float stun) partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult, float stun)
{ {
if (attackResult.Damage <= 1.0f || IsDead) { return; } if (IsDead) { return; }
if (attacker != null)
{
if (attackResult.Damage <= 0.01f) { return; }
}
else
{
if (attackResult.Damage <= 1.0f) { return; }
}
if (soundTimer < soundInterval * 0.5f) if (soundTimer < soundInterval * 0.5f)
{ {
PlaySound(CharacterSound.SoundType.Damage); PlaySound(CharacterSound.SoundType.Damage);
@@ -813,20 +823,22 @@ namespace Barotrauma
return progressBar; return progressBar;
} }
private readonly List<CharacterSound> matchingSounds = new List<CharacterSound>();
private SoundChannel soundChannel; private SoundChannel soundChannel;
public void PlaySound(CharacterSound.SoundType soundType) public void PlaySound(CharacterSound.SoundType soundType)
{ {
if (sounds == null || sounds.Count == 0) { return; } if (sounds == null || sounds.Count == 0) { return; }
if (soundChannel != null && soundChannel.IsPlaying) { return; } if (soundChannel != null && soundChannel.IsPlaying) { return; }
if (GameMain.SoundManager?.Disabled ?? true) { return; } if (GameMain.SoundManager?.Disabled ?? true) { return; }
matchingSounds.Clear();
var matchingSounds = sounds.Where(s => foreach (var s in sounds)
s.Type == soundType && {
(s.Gender == Gender.None || (info != null && info.Gender == s.Gender))); if (s.Type == soundType && (s.Gender == Gender.None || (info != null && info.Gender == s.Gender)))
if (!matchingSounds.Any()) { return; } {
matchingSounds.Add(s);
var matchingSoundsList = matchingSounds.ToList(); }
var selectedSound = matchingSoundsList[Rand.Int(matchingSoundsList.Count)]; }
var selectedSound = matchingSounds.GetRandom();
if (selectedSound?.Sound == null) { return; } if (selectedSound?.Sound == null) { return; }
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, AnimController.WorldPosition, selectedSound.Volume, selectedSound.Range, CurrentHull); soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, AnimController.WorldPosition, selectedSound.Volume, selectedSound.Range, CurrentHull);
soundTimer = soundInterval; soundTimer = soundInterval;
@@ -846,6 +858,11 @@ namespace Barotrauma
activeObjectiveEntities.Remove(found); activeObjectiveEntities.Remove(found);
} }
/// <summary>
/// Note that when a predicate is provided, the random option uses Linq.Where() extension method, which creates a new collection.
/// </summary>
public CharacterSound GetSound(Func<CharacterSound, bool> predicate = null, bool random = false) => random ? sounds.GetRandom(predicate) : sounds.FirstOrDefault(predicate);
partial void ImplodeFX() partial void ImplodeFX()
{ {
Vector2 centerOfMass = AnimController.GetCenterOfMass(); Vector2 centerOfMass = AnimController.GetCenterOfMass();
@@ -29,7 +29,7 @@ namespace Barotrauma
{ {
if (hudFrame == null) if (hudFrame == null)
{ {
hudFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: null) hudFrame = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas), style: null)
{ {
CanBeFocused = false CanBeFocused = false
}; };
@@ -163,7 +163,7 @@ namespace Barotrauma
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
if (item.Submarine == null || item.Submarine.TeamID != character.TeamID || item.Submarine.Info.IsWreck) { continue; } if (item.Submarine == null || item.Submarine.TeamID != character.TeamID || item.Submarine.Info.IsWreck) { continue; }
if (!item.Repairables.Any(r => item.ConditionPercentage <= r.AIRepairThreshold)) { continue; } if (!item.Repairables.Any(r => item.ConditionPercentage <= r.RepairThreshold)) { continue; }
if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; } if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; }
Vector2 diff = item.WorldPosition - character.WorldPosition; Vector2 diff = item.WorldPosition - character.WorldPosition;
@@ -202,6 +202,7 @@ namespace Barotrauma
foreach (Item brokenItem in brokenItems) foreach (Item brokenItem in brokenItems)
{ {
if (brokenItem.NonInteractable) { continue; }
float dist = Vector2.Distance(character.WorldPosition, brokenItem.WorldPosition); float dist = Vector2.Distance(character.WorldPosition, brokenItem.WorldPosition);
Vector2 drawPos = brokenItem.DrawPosition; Vector2 drawPos = brokenItem.DrawPosition;
float alpha = Math.Min((1000.0f - dist) / 1000.0f * 2.0f, 1.0f); float alpha = Math.Min((1000.0f - dist) / 1000.0f * 2.0f, 1.0f);
@@ -373,7 +374,7 @@ namespace Barotrauma
{ {
GUIComponent.DrawToolTip( GUIComponent.DrawToolTip(
spriteBatch, spriteBatch,
character.Info?.Job == null ? character.DisplayName : character.Name + " (" + character.Info.Job.Name + ")", character.Info?.Job == null ? character.DisplayName : character.DisplayName + " (" + character.Info.Job.Name + ")",
HUDLayoutSettings.PortraitArea); HUDLayoutSettings.PortraitArea);
} }
} }
@@ -393,10 +394,6 @@ namespace Barotrauma
startPos = cam.WorldToScreen(startPos); startPos = cam.WorldToScreen(startPos);
string focusName = character.FocusedCharacter.DisplayName; string focusName = character.FocusedCharacter.DisplayName;
if (character.FocusedCharacter.Info != null)
{
focusName = character.FocusedCharacter.Info.DisplayName;
}
Vector2 textPos = startPos; Vector2 textPos = startPos;
Vector2 textSize = GUI.Font.MeasureString(focusName); Vector2 textSize = GUI.Font.MeasureString(focusName);
Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(focusName); Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(focusName);
@@ -146,7 +146,8 @@ namespace Barotrauma
"+" + ((int)((newLevel - prevLevel) * 100.0f)).ToString() + " XP", "+" + ((int)((newLevel - prevLevel) * 100.0f)).ToString() + " XP",
GUI.Style.Green, GUI.Style.Green,
textPopupPos, textPopupPos,
Vector2.UnitY * 10.0f); Vector2.UnitY * 10.0f,
playSound: Character.Controlled?.Info == this);
} }
else if (prevLevel % 0.1f > 0.05f && newLevel % 0.1f < 0.05f) else if (prevLevel % 0.1f > 0.05f && newLevel % 0.1f < 0.05f)
{ {
@@ -154,7 +155,8 @@ namespace Barotrauma
"+10 XP", "+10 XP",
GUI.Style.Green, GUI.Style.Green,
textPopupPos, textPopupPos,
Vector2.UnitY * 10.0f); Vector2.UnitY * 10.0f,
playSound: Character.Controlled?.Info == this);
} }
if ((int)newLevel > (int)prevLevel) if ((int)newLevel > (int)prevLevel)
@@ -502,11 +502,9 @@ namespace Barotrauma
causeOfDeathAffliction = AfflictionPrefab.Prefabs[afflictionName]; causeOfDeathAffliction = AfflictionPrefab.Prefabs[afflictionName];
} }
} }
byte severedLimbCount = msg.ReadByte();
if (!IsDead) if (!IsDead)
{ {
if (causeOfDeathType == CauseOfDeathType.Pressure) if (causeOfDeathType == CauseOfDeathType.Pressure || causeOfDeathAffliction == AfflictionPrefab.Pressure)
{ {
Implode(true); Implode(true);
} }
@@ -515,26 +513,26 @@ namespace Barotrauma
Kill(causeOfDeathType, causeOfDeathAffliction?.Instantiate(1.0f), true); Kill(causeOfDeathType, causeOfDeathAffliction?.Instantiate(1.0f), true);
} }
} }
for (int i = 0; i < severedLimbCount; i++)
{
int severedJointIndex = msg.ReadByte();
if (severedJointIndex < 0 || severedJointIndex >= AnimController.LimbJoints.Length)
{
string errorMsg = $"Error in CharacterNetworking.ReadStatus: severed joint index out of bounds (index: {severedJointIndex}, joint count: {AnimController.LimbJoints.Length})";
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:JointIndexOutOfBounts", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
else
{
AnimController.SeverLimbJoint(AnimController.LimbJoints[severedJointIndex]);
}
}
} }
else else
{ {
if (IsDead) { Revive(); } if (IsDead) { Revive(); }
CharacterHealth.ClientRead(msg); CharacterHealth.ClientRead(msg);
} }
byte severedLimbCount = msg.ReadByte();
for (int i = 0; i < severedLimbCount; i++)
{
int severedJointIndex = msg.ReadByte();
if (severedJointIndex < 0 || severedJointIndex >= AnimController.LimbJoints.Length)
{
string errorMsg = $"Error in CharacterNetworking.ReadStatus: severed joint index out of bounds (index: {severedJointIndex}, joint count: {AnimController.LimbJoints.Length})";
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:JointIndexOutOfBounts", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
else
{
AnimController.SeverLimbJoint(AnimController.LimbJoints[severedJointIndex]);
}
}
} }
} }
} }
@@ -1,6 +1,6 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -1,4 +1,5 @@
using Barotrauma.Extensions; using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -20,9 +21,9 @@ namespace Barotrauma
} }
const int MaxFakeFireSources = 10; const int MaxFakeFireSources = 10;
private float minFakeFireSourceInterval = 10.0f, maxFakeFireSourceInterval = 200.0f; const float MinFakeFireSourceInterval = 30.0f, MaxFakeFireSourceInterval = 240.0f;
private float createFireSourceTimer; private float createFireSourceTimer;
private List<FakeFireSource> fakeFireSources = new List<FakeFireSource>(); private readonly List<FakeFireSource> fakeFireSources = new List<FakeFireSource>();
enum FloodType enum FloodType
{ {
@@ -31,26 +32,30 @@ namespace Barotrauma
HideFlooding HideFlooding
} }
private float minSoundInterval = 10.0f, maxSoundInterval = 60.0f; const float MinSoundInterval = 10.0f, MaxSoundInterval = 180.0f;
private FloodType currentFloodType; private FloodType currentFloodType;
private float soundTimer; private float soundTimer;
private float minFloodInterval = 30.0f, maxFloodInterval = 180.0f; const float MinFloodInterval = 60.0f, MaxFloodInterval = 240.0f;
private float createFloodTimer; private float createFloodTimer;
private float currentFloodState; private float currentFloodState;
private float currentFloodDuration; private float currentFloodDuration;
private float fakeBrokenInterval = 30.0f;
private float fakeBrokenTimer = 0.0f;
partial void UpdateProjSpecific(CharacterHealth characterHealth, Limb targetLimb, float deltaTime) partial void UpdateProjSpecific(CharacterHealth characterHealth, Limb targetLimb, float deltaTime)
{ {
if (Character.Controlled != characterHealth.Character) return; if (Character.Controlled != characterHealth.Character) return;
UpdateFloods(deltaTime); UpdateFloods(deltaTime);
UpdateSounds(characterHealth.Character, deltaTime); UpdateSounds(characterHealth.Character, deltaTime);
UpdateFires(characterHealth.Character, deltaTime); UpdateFires(characterHealth.Character, deltaTime);
UpdateFakeBroken(deltaTime);
} }
private void UpdateSounds(Character character, float deltaTime) private void UpdateSounds(Character character, float deltaTime)
{ {
if (soundTimer < MathHelper.Lerp(maxSoundInterval, minSoundInterval, Strength / 100.0f)) if (soundTimer < MathHelper.Lerp(MaxSoundInterval, MinSoundInterval, Strength / 100.0f))
{ {
soundTimer += deltaTime; soundTimer += deltaTime;
return; return;
@@ -97,7 +102,7 @@ namespace Barotrauma
return; return;
} }
if (createFloodTimer < MathHelper.Lerp(maxFloodInterval, minFloodInterval, Strength / 100.0f)) if (createFloodTimer < MathHelper.Lerp(MaxFloodInterval, MinFloodInterval, Strength / 100.0f))
{ {
createFloodTimer += deltaTime; createFloodTimer += deltaTime;
return; return;
@@ -124,7 +129,7 @@ namespace Barotrauma
createFireSourceTimer += deltaTime; createFireSourceTimer += deltaTime;
if (fakeFireSources.Count < MaxFakeFireSources && if (fakeFireSources.Count < MaxFakeFireSources &&
character.Submarine != null && character.Submarine != null &&
createFireSourceTimer > MathHelper.Lerp(maxFakeFireSourceInterval, minFakeFireSourceInterval, Strength / 100.0f)) createFireSourceTimer > MathHelper.Lerp(MaxFakeFireSourceInterval, MinFakeFireSourceInterval, Strength / 100.0f))
{ {
Hull fireHull = Hull.hullList.GetRandom(h => h.Submarine == character.Submarine); Hull fireHull = Hull.hullList.GetRandom(h => h.Submarine == character.Submarine);
@@ -140,9 +145,9 @@ namespace Barotrauma
foreach (FakeFireSource fakeFireSource in fakeFireSources) foreach (FakeFireSource fakeFireSource in fakeFireSources)
{ {
if (fakeFireSource.Hull.Surface > fakeFireSource.Hull.Rect.Y - fakeFireSource.Hull.Rect.Height + fakeFireSource.Position.Y) if (fakeFireSource.Hull.DrawSurface > fakeFireSource.Hull.Rect.Y - fakeFireSource.Hull.Rect.Height + fakeFireSource.Position.Y)
{ {
fakeFireSource.LifeTime -= deltaTime * 10.0f; fakeFireSource.LifeTime -= deltaTime * 100.0f;
} }
fakeFireSource.LifeTime -= deltaTime; fakeFireSource.LifeTime -= deltaTime;
@@ -162,5 +167,28 @@ namespace Barotrauma
fakeFireSources.RemoveAll(fs => fs.LifeTime <= 0.0f); fakeFireSources.RemoveAll(fs => fs.LifeTime <= 0.0f);
} }
private void UpdateFakeBroken(float deltaTime)
{
fakeBrokenTimer -= deltaTime;
if (fakeBrokenTimer > 0.0f) { return; }
foreach (Item item in Item.ItemList)
{
var repairable = item.GetComponent<Repairable>();
if (repairable == null) { continue; }
if (ShouldFakeBrokenItem(item))
{
repairable.FakeBrokenTimer = 60.0f;
}
}
fakeBrokenTimer = fakeBrokenInterval;
}
private bool ShouldFakeBrokenItem(Item item)
{
return Rand.Range(0.0f, 1000.0f) < Strength;
}
} }
} }
@@ -251,8 +251,7 @@ namespace Barotrauma
{ {
get get
{ {
// 0.38775510204f = percentage of offset before reaching the healthbar portion of the graphic going from bottom upwards return new Point(Math.Max(2, GUI.IntScaleCeiling(1.5f)), Math.Min(GUI.IntScaleFloor(18f), 19));
return new Point(2, (int)(HUDLayoutSettings.HealthBarArea.Size.Y * 0.38775510204f));
} }
} }
@@ -260,7 +259,7 @@ namespace Barotrauma
{ {
get get
{ {
return new Point((int)Math.Ceiling(HUDLayoutSettings.HealthBarArea.Size.X - 45 * GUI.Scale), (int)(healthBarHolder.Rect.Height - Math.Min(23 * GUI.Scale, 25)) / 2); return new Point(healthBarHolder.Rect.Width - Math.Min(GUI.IntScale(45f), 47), GUI.IntScale(15f));
} }
} }
@@ -597,12 +596,14 @@ namespace Barotrauma
switch (alignment) switch (alignment)
{ {
case Alignment.Left: case Alignment.Left:
healthInterfaceFrame.RectTransform.SetPosition(Anchor.CenterLeft); healthInterfaceFrame.RectTransform.SetPosition(Anchor.BottomLeft);
break; break;
case Alignment.Right: case Alignment.Right:
healthInterfaceFrame.RectTransform.SetPosition(Anchor.CenterRight); healthInterfaceFrame.RectTransform.SetPosition(Anchor.BottomRight);
break; break;
} }
healthInterfaceFrame.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.Padding, screenResolution.Y - HUDLayoutSettings.ChatBoxArea.Y + HUDLayoutSettings.Padding);
healthInterfaceFrame.RectTransform.RecalculateChildren(false); healthInterfaceFrame.RectTransform.RecalculateChildren(false);
} }
@@ -1107,7 +1108,7 @@ namespace Barotrauma
float currHealth = healthBar.BarSize; float currHealth = healthBar.BarSize;
Color prevColor = healthBar.Color; Color prevColor = healthBar.Color;
healthBarShadow.BarSize = healthShadowSize; healthBarShadow.BarSize = healthShadowSize;
healthBarShadow.Color = GUI.Style.Red; healthBarShadow.Color = Color.Lerp(GUI.Style.Red, Color.Black, 0.5f);
healthBarShadow.Visible = true; healthBarShadow.Visible = true;
healthBar.BarSize = currHealth; healthBar.BarSize = currHealth;
healthBar.Color = prevColor; healthBar.Color = prevColor;
@@ -1822,7 +1823,7 @@ namespace Barotrauma
Vector2 iconPos = highlightArea.Center.ToVector2(); Vector2 iconPos = highlightArea.Center.ToVector2();
//Affliction mostSevereAffliction = thisAfflictions.FirstOrDefault(a => !a.Prefab.IsBuff && !thisAfflictions.Any(a2 => !a2.Prefab.IsBuff && a2.Strength > a.Strength)) ?? thisAfflictions.FirstOrDefault(); //Affliction mostSevereAffliction = thisAfflictions.FirstOrDefault(a => !a.Prefab.IsBuff && !thisAfflictions.Any(a2 => !a2.Prefab.IsBuff && a2.Strength > a.Strength)) ?? thisAfflictions.FirstOrDefault();
Affliction mostSevereAffliction = SortAfflictionsBySeverity(thisAfflictions).FirstOrDefault(); Affliction mostSevereAffliction = SortAfflictionsBySeverity(thisAfflictions, excludeBuffs: false).FirstOrDefault();
if (mostSevereAffliction != null) { DrawLimbAfflictionIcon(spriteBatch, mostSevereAffliction, iconScale, ref iconPos); } if (mostSevereAffliction != null) { DrawLimbAfflictionIcon(spriteBatch, mostSevereAffliction, iconScale, ref iconPos); }
if (thisAfflictions.Count() > 1) if (thisAfflictions.Count() > 1)
@@ -8,12 +8,14 @@ namespace Barotrauma
{ {
partial class JobPrefab : IPrefab, IDisposable partial class JobPrefab : IPrefab, IDisposable
{ {
public GUIButton CreateInfoFrame(int variant) public GUIButton CreateInfoFrame(out GUIComponent buttonContainer)
{ {
int width = 500, height = 400; int width = 500, height = 400;
GUIButton backFrame = new GUIButton(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker"); GUIButton frameHolder = new GUIButton(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null);
GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, height), backFrame.RectTransform, Anchor.Center)); new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, frameHolder.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, height), frameHolder.RectTransform, Anchor.Center));
GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null); GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), Name, font: GUI.LargeFont); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), Name, font: GUI.LargeFont);
@@ -32,6 +34,8 @@ namespace Barotrauma
font: GUI.SmallFont); font: GUI.SmallFont);
} }
buttonContainer = paddedFrame;
/*if (!ItemIdentifiers.TryGetValue(variant, out var itemIdentifiers)) { return backFrame; } /*if (!ItemIdentifiers.TryGetValue(variant, out var itemIdentifiers)) { return backFrame; }
var itemContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.5f), paddedFrame.RectTransform, Anchor.TopRight) var itemContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.5f), paddedFrame.RectTransform, Anchor.TopRight)
{ RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) }) { RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) })
@@ -49,7 +53,7 @@ namespace Barotrauma
font: GUI.SmallFont); font: GUI.SmallFont);
}*/ }*/
return backFrame; return frameHolder;
} }
@@ -3,25 +3,24 @@ using Barotrauma.Particles;
using Barotrauma.SpriteDeformations; using Barotrauma.SpriteDeformations;
using Barotrauma.Extensions; using Barotrauma.Extensions;
using FarseerPhysics; using FarseerPhysics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using SpriteParams = Barotrauma.RagdollParams.SpriteParams; using SpriteParams = Barotrauma.RagdollParams.SpriteParams;
namespace Barotrauma namespace Barotrauma
{ {
partial class LimbJoint : RevoluteJoint partial class LimbJoint
{ {
public void UpdateDeformations(float deltaTime) public void UpdateDeformations(float deltaTime)
{ {
float diff = Math.Abs(UpperLimit - LowerLimit); float diff = Math.Abs(UpperLimit - LowerLimit);
float strength = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, MathHelper.Pi, diff)); float strength = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, MathHelper.Pi, diff));
float jointAngle = this.JointAngle * strength; float jointAngle = JointAngle * strength;
JointBendDeformation limbADeformation = LimbA.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation; JointBendDeformation limbADeformation = LimbA.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation;
JointBendDeformation limbBDeformation = LimbB.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation; JointBendDeformation limbBDeformation = LimbB.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation;
@@ -70,7 +69,6 @@ namespace Barotrauma
} }
} }
} }
} }
public void Draw(SpriteBatch spriteBatch) public void Draw(SpriteBatch spriteBatch)
@@ -126,7 +124,7 @@ namespace Barotrauma
{ {
get get
{ {
var conditionalSprite = ConditionalSprites.FirstOrDefault(c => c.IsActive && c.DeformableSprite != null); var conditionalSprite = ConditionalSprites.FirstOrDefault(c => c.Exclusive && c.IsActive && c.DeformableSprite != null);
if (conditionalSprite != null) if (conditionalSprite != null)
{ {
return conditionalSprite.DeformableSprite; return conditionalSprite.DeformableSprite;
@@ -144,7 +142,7 @@ namespace Barotrauma
{ {
get get
{ {
var conditionalSprite = ConditionalSprites.FirstOrDefault(c => c.IsActive && c.ActiveSprite != null); var conditionalSprite = ConditionalSprites.FirstOrDefault(c => c.Exclusive && c.IsActive && c.ActiveSprite != null);
if (conditionalSprite != null) if (conditionalSprite != null)
{ {
return conditionalSprite.ActiveSprite; return conditionalSprite.ActiveSprite;
@@ -166,6 +164,12 @@ namespace Barotrauma
public Sprite DamagedSprite { get; private set; } public Sprite DamagedSprite { get; private set; }
public bool Hide
{
get => Params.Hide;
set => Params.Hide = value;
}
public List<ConditionalSprite> ConditionalSprites { get; private set; } = new List<ConditionalSprite>(); public List<ConditionalSprite> ConditionalSprites { get; private set; } = new List<ConditionalSprite>();
private Dictionary<DecorativeSprite, SpriteState> spriteAnimState = new Dictionary<DecorativeSprite, SpriteState>(); private Dictionary<DecorativeSprite, SpriteState> spriteAnimState = new Dictionary<DecorativeSprite, SpriteState>();
private Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>(); private Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
@@ -274,7 +278,17 @@ namespace Barotrauma
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams)); DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams));
break; break;
case "conditionalsprite": case "conditionalsprite":
var conditionalSprite = new ConditionalSprite(subElement, character, file: GetSpritePath(subElement, null)); ISerializableEntity targetEntity;
string target = subElement.GetAttributeString("target", null);
if (string.Equals(target, "character", StringComparison.OrdinalIgnoreCase))
{
targetEntity = character;
}
else
{
targetEntity = this;
}
var conditionalSprite = new ConditionalSprite(subElement, targetEntity, file: GetSpritePath(subElement, null));
ConditionalSprites.Add(conditionalSprite); ConditionalSprites.Add(conditionalSprite);
if (conditionalSprite.DeformableSprite != null) if (conditionalSprite.DeformableSprite != null)
{ {
@@ -373,12 +387,16 @@ namespace Barotrauma
private string GetSpritePath(XElement element, SpriteParams spriteParams) private string GetSpritePath(XElement element, SpriteParams spriteParams)
{ {
string texturePath = element.GetAttributeString("texture", null); if (spriteParams != null)
if (string.IsNullOrWhiteSpace(texturePath) && spriteParams != null)
{ {
texturePath = spriteParams.Ragdoll.Texture; return GetSpritePath(spriteParams.GetTexturePath());
}
else
{
string texturePath = element.GetAttributeString("texture", null);
texturePath = string.IsNullOrWhiteSpace(texturePath) ? ragdoll.RagdollParams.Texture : texturePath;
return GetSpritePath(texturePath);
} }
return GetSpritePath(texturePath);
} }
/// <summary> /// <summary>
@@ -419,12 +437,29 @@ namespace Barotrauma
} }
} }
partial void AddDamageProjSpecific(IEnumerable<Affliction> afflictions, bool playSound, IEnumerable<DamageModifier> appliedDamageModifiers) partial void AddDamageProjSpecific(bool playSound, AttackResult result)
{ {
float bleedingDamage = character.CharacterHealth.DoesBleed ? afflictions.Where(a => a is AfflictionBleeding).Sum(a => a.GetVitalityDecrease(character.CharacterHealth)) : 0; float bleedingDamage = 0;
float damage = afflictions.Where(a => a.Prefab.AfflictionType == "damage").Sum(a => a.GetVitalityDecrease(character.CharacterHealth)); if (character.CharacterHealth.DoesBleed)
{
foreach (var affliction in result.Afflictions)
{
if (affliction is AfflictionBleeding)
{
bleedingDamage += affliction.GetVitalityDecrease(character.CharacterHealth);
}
}
}
float damage = 0;
foreach (var affliction in result.Afflictions)
{
if (affliction.Prefab.AfflictionType == "damage")
{
damage += affliction.GetVitalityDecrease(character.CharacterHealth);
}
}
float damageMultiplier = 1; float damageMultiplier = 1;
foreach (DamageModifier damageModifier in appliedDamageModifiers) foreach (DamageModifier damageModifier in result.AppliedDamageModifiers)
{ {
foreach (var afflictionPrefab in AfflictionPrefab.List) foreach (var afflictionPrefab in AfflictionPrefab.List)
{ {
@@ -433,6 +468,7 @@ namespace Barotrauma
if (afflictionPrefab.Effects.Any(e => e.MaxVitalityDecrease > 0)) if (afflictionPrefab.Effects.Any(e => e.MaxVitalityDecrease > 0))
{ {
damageMultiplier *= damageModifier.DamageMultiplier; damageMultiplier *= damageModifier.DamageMultiplier;
break;
} }
} }
} }
@@ -440,7 +476,7 @@ namespace Barotrauma
if (playSound) if (playSound)
{ {
string damageSoundType = (bleedingDamage > damage) ? "LimbSlash" : "LimbBlunt"; string damageSoundType = (bleedingDamage > damage) ? "LimbSlash" : "LimbBlunt";
foreach (DamageModifier damageModifier in appliedDamageModifiers) foreach (DamageModifier damageModifier in result.AppliedDamageModifiers)
{ {
if (!string.IsNullOrWhiteSpace(damageModifier.DamageSound)) if (!string.IsNullOrWhiteSpace(damageModifier.DamageSound))
{ {
@@ -457,9 +493,8 @@ namespace Barotrauma
{ {
foreach (ParticleEmitter emitter in character.DamageEmitters) foreach (ParticleEmitter emitter in character.DamageEmitters)
{ {
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) continue; if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) continue; if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
emitter.Emit(1.0f, WorldPosition, character.CurrentHull, amountMultiplier: damageParticleAmount); emitter.Emit(1.0f, WorldPosition, character.CurrentHull, amountMultiplier: damageParticleAmount);
} }
} }
@@ -471,9 +506,8 @@ namespace Barotrauma
foreach (ParticleEmitter emitter in character.BloodEmitters) foreach (ParticleEmitter emitter in character.BloodEmitters)
{ {
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) continue; if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) continue; if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
emitter.Emit(1.0f, WorldPosition, character.CurrentHull, sizeMultiplier: bloodParticleSize, amountMultiplier: bloodParticleAmount); emitter.Emit(1.0f, WorldPosition, character.CurrentHull, sizeMultiplier: bloodParticleSize, amountMultiplier: bloodParticleAmount);
} }
@@ -482,14 +516,13 @@ namespace Barotrauma
character.CurrentHull.AddDecal(character.BloodDecalName, WorldPosition, MathHelper.Clamp(bloodParticleSize, 0.5f, 1.0f)); character.CurrentHull.AddDecal(character.BloodDecalName, WorldPosition, MathHelper.Clamp(bloodParticleSize, 0.5f, 1.0f));
} }
} }
} }
partial void UpdateProjSpecific(float deltaTime) partial void UpdateProjSpecific(float deltaTime)
{ {
if (!body.Enabled) { return; } if (!body.Enabled) { return; }
if (!character.IsDead) if (!IsDead)
{ {
DamageOverlayStrength -= deltaTime; DamageOverlayStrength -= deltaTime;
BurnOverlayStrength -= deltaTime; BurnOverlayStrength -= deltaTime;
@@ -534,6 +567,10 @@ namespace Barotrauma
{ {
LightSource.LightSprite.Depth = ActiveSprite.Depth; LightSource.LightSprite.Depth = ActiveSprite.Depth;
} }
if (LightSource.DeformableLightSprite != null)
{
LightSource.DeformableLightSprite.Sprite.Depth = ActiveSprite.Depth;
}
} }
UpdateSpriteStates(deltaTime); UpdateSpriteStates(deltaTime);
@@ -543,6 +580,8 @@ namespace Barotrauma
{ {
float brightness = 1.0f - (burnOverLayStrength / 100.0f) * 0.5f; float brightness = 1.0f - (burnOverLayStrength / 100.0f) * 0.5f;
var spriteParams = Params.GetSprite(); var spriteParams = Params.GetSprite();
if (spriteParams == null) { return; }
Color color = new Color(spriteParams.Color.R / 255f * brightness, spriteParams.Color.G / 255f * brightness, spriteParams.Color.B / 255f * brightness, spriteParams.Color.A / 255f); Color color = new Color(spriteParams.Color.R / 255f * brightness, spriteParams.Color.G / 255f * brightness, spriteParams.Color.B / 255f * brightness, spriteParams.Color.A / 255f);
if (deadTimer > 0) if (deadTimer > 0)
{ {
@@ -568,7 +607,7 @@ namespace Barotrauma
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes"); float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
bool hideLimb = Params.Hide || bool hideLimb = Hide ||
OtherWearables.Any(w => w.HideLimb) || OtherWearables.Any(w => w.HideLimb) ||
wearingItems.Any(w => w != null && w.HideLimb); wearingItems.Any(w => w != null && w.HideLimb);
@@ -589,6 +628,11 @@ namespace Barotrauma
{ {
var deformation = SpriteDeformation.GetDeformation(Deformations, deformSprite.Size); var deformation = SpriteDeformation.GetDeformation(Deformations, deformSprite.Size);
deformSprite.Deform(deformation); deformSprite.Deform(deformation);
if (LightSource != null && LightSource.DeformableLightSprite != null)
{
deformation = SpriteDeformation.GetDeformation(Deformations, deformSprite.Size, dir == Direction.Left);
LightSource.DeformableLightSprite.Deform(deformation);
}
} }
else else
{ {
@@ -600,6 +644,31 @@ namespace Barotrauma
{ {
body.Draw(spriteBatch, activeSprite, color, null, Scale * TextureScale, Params.MirrorHorizontally, Params.MirrorVertically); body.Draw(spriteBatch, activeSprite, color, null, Scale * TextureScale, Params.MirrorHorizontally, Params.MirrorVertically);
} }
// Handle non-exlusive, i.e. additional conditional sprites
foreach (var conditionalSprite in ConditionalSprites)
{
// Exclusive conditional sprites are handled in the Properties
if (conditionalSprite.Exclusive) { continue; }
if (!conditionalSprite.IsActive) { continue; }
if (conditionalSprite.DeformableSprite != null)
{
var defSprite = conditionalSprite.DeformableSprite;
if (Deformations != null && Deformations.Any())
{
var deformation = SpriteDeformation.GetDeformation(Deformations, defSprite.Size);
defSprite.Deform(deformation);
}
else
{
defSprite.Reset();
}
body.Draw(defSprite, cam, Vector2.One * Scale * TextureScale, color, Params.MirrorHorizontally);
}
else
{
body.Draw(spriteBatch, conditionalSprite.Sprite, color, null, Scale * TextureScale, Params.MirrorHorizontally, Params.MirrorVertically);
}
}
} }
SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally; SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
if (LightSource != null) if (LightSource != null)
@@ -5,7 +5,7 @@ using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
@@ -456,18 +456,6 @@ namespace Barotrauma
GameMain.CharacterEditorScreen.Select(); GameMain.CharacterEditorScreen.Select();
})); }));
commands.Add(new Command("money", "", args =>
{
if (args.Length == 0) { return; }
if (GameMain.GameSession.GameMode is CampaignMode campaign)
{
if (int.TryParse(args[0], out int money))
{
campaign.Money += money;
}
}
}, isCheat: true));
commands.Add(new Command("steamnetdebug", "steamnetdebug: Toggles Steamworks debug logging.", (string[] args) => commands.Add(new Command("steamnetdebug", "steamnetdebug: Toggles Steamworks debug logging.", (string[] args) =>
{ {
SteamManager.NetworkingDebugLog = !SteamManager.NetworkingDebugLog; SteamManager.NetworkingDebugLog = !SteamManager.NetworkingDebugLog;
@@ -504,6 +492,7 @@ namespace Barotrauma
AssignRelayToServer("setpassword", true); AssignRelayToServer("setpassword", true);
commands.Add(new Command("traitorlist", "", (string[] args) => { })); commands.Add(new Command("traitorlist", "", (string[] args) => { }));
AssignRelayToServer("traitorlist", true); AssignRelayToServer("traitorlist", true);
AssignRelayToServer("money", true);
AssignOnExecute("control", (string[] args) => AssignOnExecute("control", (string[] args) =>
{ {
@@ -669,6 +658,44 @@ namespace Barotrauma
} }
}, isCheat: true)); }, isCheat: true));
commands.Add(new Command("listcloudfiles", "Lists all of your files on the Steam Cloud.", args =>
{
int i = 0;
foreach (var file in Steamworks.SteamRemoteStorage.Files)
{
NewMessage($"* {i}: {file.Filename}, {file.Size} bytes", Color.Orange);
i++;
}
NewMessage($"Bytes remaining: {Steamworks.SteamRemoteStorage.QuotaRemainingBytes}/{Steamworks.SteamRemoteStorage.QuotaBytes}", Color.Yellow);
}));
commands.Add(new Command("removefromcloud", "Removes a file from Steam Cloud.", args =>
{
if (args.Length < 1) { return; }
var files = Steamworks.SteamRemoteStorage.Files;
Steamworks.SteamRemoteStorage.RemoteFile file;
if (int.TryParse(args[0], out int index) && index>=0 && index<files.Count)
{
file = files[index];
}
else
{
file = files.Find(f => f.Filename.Equals(args[0], StringComparison.InvariantCultureIgnoreCase));
}
if (!string.IsNullOrEmpty(file.Filename))
{
if (file.Delete())
{
NewMessage($"Deleting {file.Filename}", Color.Orange);
}
else
{
ThrowError($"Failed to delete {file.Filename}");
}
}
}));
commands.Add(new Command("resetall", "Reset all items and structures to prefabs. Only applicable in the subeditor.", args => commands.Add(new Command("resetall", "Reset all items and structures to prefabs. Only applicable in the subeditor.", args =>
{ {
if (Screen.Selected == GameMain.SubEditorScreen) if (Screen.Selected == GameMain.SubEditorScreen)
@@ -846,7 +873,7 @@ namespace Barotrauma
return; return;
} }
if (Submarine.MainSub.SaveAs(System.IO.Path.Combine(SubmarineInfo.SavePath, fileName + ".sub"))) if (Submarine.MainSub.SaveAs(Barotrauma.IO.Path.Combine(SubmarineInfo.SavePath, fileName + ".sub")))
{ {
NewMessage("Sub saved", Color.Green); NewMessage("Sub saved", Color.Green);
} }
@@ -1038,8 +1065,7 @@ namespace Barotrauma
foreach (var deconstructItem in itemPrefab.DeconstructItems) foreach (var deconstructItem in itemPrefab.DeconstructItems)
{ {
var targetItem = MapEntityPrefab.Find(null, deconstructItem.ItemIdentifier, showErrorMessages: false) as ItemPrefab; if (!(MapEntityPrefab.Find(null, deconstructItem.ItemIdentifier, showErrorMessages: false) is ItemPrefab targetItem))
if (targetItem == null)
{ {
ThrowError("Error in item \"" + itemPrefab.Name + "\" - could not find deconstruct item \"" + deconstructItem.ItemIdentifier + "\"!"); ThrowError("Error in item \"" + itemPrefab.Name + "\" - could not find deconstruct item \"" + deconstructItem.ItemIdentifier + "\"!");
continue; continue;
@@ -1054,9 +1080,14 @@ namespace Barotrauma
if (fabricationRecipe != null) if (fabricationRecipe != null)
{ {
if (!fabricationRecipe.RequiredItems.Any(r => r.ItemPrefab == targetItem)) var ingredient = fabricationRecipe.RequiredItems.Find(r => r.ItemPrefab == targetItem);
if (ingredient == null)
{ {
NewMessage("Deconstructing \"" + itemPrefab.Name + "\" produces \"" + deconstructItem.ItemIdentifier + "\", which isn't required in the fabrication recipe of the item.", Color.Orange); NewMessage("Deconstructing \"" + itemPrefab.Name + "\" produces \"" + deconstructItem.ItemIdentifier + "\", which isn't required in the fabrication recipe of the item.", Color.Red);
}
else if (ingredient.UseCondition && ingredient.MinCondition < deconstructItem.OutCondition)
{
NewMessage($"Deconstructing \"{itemPrefab.Name}\" produces more \"{deconstructItem.ItemIdentifier}\", than what's required to fabricate the item (required: {ingredient.ItemPrefab.Name} {(int)(ingredient.MinCondition * 100)}%, output: {deconstructItem.ItemIdentifier} {(int)(deconstructItem.OutCondition * 100)}%)", Color.Red);
} }
} }
} }
@@ -1267,6 +1298,13 @@ namespace Barotrauma
TextManager.Language = "English"; TextManager.Language = "English";
})); }));
commands.Add(new Command("eventstats", "", (string[] args) =>
{
var debugLines = ScriptedEventSet.GetDebugStatistics();
string filePath = "eventstats.txt";
File.WriteAllLines(filePath, debugLines);
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
}));
#if DEBUG #if DEBUG
commands.Add(new Command("printreceivertransfers", "", (string[] args) => commands.Add(new Command("printreceivertransfers", "", (string[] args) =>
{ {
@@ -1440,7 +1478,7 @@ namespace Barotrauma
element.Value = lines[i]; element.Value = lines[i];
i++; i++;
} }
doc.Save(destinationPath); doc.SaveSafe(destinationPath);
}, },
() => () =>
{ {
@@ -1475,7 +1513,7 @@ namespace Barotrauma
while ((!(nextNode is XElement) || nextNode == element) && nextNode != null) nextNode = nextNode.NextNode; while ((!(nextNode is XElement) || nextNode == element) && nextNode != null) nextNode = nextNode.NextNode;
destinationElement = nextNode as XElement; destinationElement = nextNode as XElement;
} }
destinationDoc.Save(destinationPath); destinationDoc.SaveSafe(destinationPath);
}, },
() => () =>
{ {
@@ -1730,69 +1768,69 @@ namespace Barotrauma
GameMain.Config.SaveNewPlayerConfig(); GameMain.Config.SaveNewPlayerConfig();
var saveFiles = System.IO.Directory.GetFiles(SaveUtil.SaveFolder); var saveFiles = Barotrauma.IO.Directory.GetFiles(SaveUtil.SaveFolder);
foreach (string saveFile in saveFiles) foreach (string saveFile in saveFiles)
{ {
System.IO.File.Delete(saveFile); Barotrauma.IO.File.Delete(saveFile);
NewMessage("Deleted " + saveFile, Color.Green); NewMessage("Deleted " + saveFile, Color.Green);
} }
if (System.IO.Directory.Exists(System.IO.Path.Combine(SaveUtil.SaveFolder, "temp"))) if (Barotrauma.IO.Directory.Exists(Barotrauma.IO.Path.Combine(SaveUtil.SaveFolder, "temp")))
{ {
System.IO.Directory.Delete(System.IO.Path.Combine(SaveUtil.SaveFolder, "temp"), true); Barotrauma.IO.Directory.Delete(Barotrauma.IO.Path.Combine(SaveUtil.SaveFolder, "temp"), true);
NewMessage("Deleted temp save folder", Color.Green); NewMessage("Deleted temp save folder", Color.Green);
} }
if (System.IO.Directory.Exists(ServerLog.SavePath)) if (Barotrauma.IO.Directory.Exists(ServerLog.SavePath))
{ {
var logFiles = System.IO.Directory.GetFiles(ServerLog.SavePath); var logFiles = Barotrauma.IO.Directory.GetFiles(ServerLog.SavePath);
foreach (string logFile in logFiles) foreach (string logFile in logFiles)
{ {
System.IO.File.Delete(logFile); Barotrauma.IO.File.Delete(logFile);
NewMessage("Deleted " + logFile, Color.Green); NewMessage("Deleted " + logFile, Color.Green);
} }
} }
if (System.IO.File.Exists("filelist.xml")) if (Barotrauma.IO.File.Exists("filelist.xml"))
{ {
System.IO.File.Delete("filelist.xml"); Barotrauma.IO.File.Delete("filelist.xml");
NewMessage("Deleted filelist", Color.Green); NewMessage("Deleted filelist", Color.Green);
} }
if (System.IO.File.Exists("Data/bannedplayers.txt")) if (Barotrauma.IO.File.Exists("Data/bannedplayers.txt"))
{ {
System.IO.File.Delete("Data/bannedplayers.txt"); Barotrauma.IO.File.Delete("Data/bannedplayers.txt");
NewMessage("Deleted bannedplayers.txt", Color.Green); NewMessage("Deleted bannedplayers.txt", Color.Green);
} }
if (System.IO.File.Exists("Submarines/TutorialSub.sub")) if (Barotrauma.IO.File.Exists("Submarines/TutorialSub.sub"))
{ {
System.IO.File.Delete("Submarines/TutorialSub.sub"); Barotrauma.IO.File.Delete("Submarines/TutorialSub.sub");
NewMessage("Deleted TutorialSub from the submarine folder", Color.Green); NewMessage("Deleted TutorialSub from the submarine folder", Color.Green);
} }
/*if (System.IO.File.Exists(GameServer.SettingsFile)) /*if (Barotrauma.IO.File.Exists(GameServer.SettingsFile))
{ {
System.IO.File.Delete(GameServer.SettingsFile); Barotrauma.IO.File.Delete(GameServer.SettingsFile);
NewMessage("Deleted server settings", Color.Green); NewMessage("Deleted server settings", Color.Green);
} }
if (System.IO.File.Exists(GameServer.ClientPermissionsFile)) if (Barotrauma.IO.File.Exists(GameServer.ClientPermissionsFile))
{ {
System.IO.File.Delete(GameServer.ClientPermissionsFile); Barotrauma.IO.File.Delete(GameServer.ClientPermissionsFile);
NewMessage("Deleted client permission file", Color.Green); NewMessage("Deleted client permission file", Color.Green);
}*/ }*/
if (System.IO.File.Exists("crashreport.log")) if (Barotrauma.IO.File.Exists("crashreport.log"))
{ {
System.IO.File.Delete("crashreport.log"); Barotrauma.IO.File.Delete("crashreport.log");
NewMessage("Deleted crashreport.log", Color.Green); NewMessage("Deleted crashreport.log", Color.Green);
} }
if (!System.IO.File.Exists("Content/Map/TutorialSub.sub")) if (!Barotrauma.IO.File.Exists("Content/Map/TutorialSub.sub"))
{ {
ThrowError("TutorialSub.sub not found!"); ThrowError("TutorialSub.sub not found!");
} }
@@ -1841,7 +1879,7 @@ namespace Barotrauma
"giveperm", "giveperm",
(string[] args) => (string[] args) =>
{ {
if (args.Length < 1) return; if (args.Length < 1) { return; }
NewMessage("Valid permissions are:", Color.White); NewMessage("Valid permissions are:", Color.White);
foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions)))
@@ -1898,7 +1936,7 @@ namespace Barotrauma
{ {
if (args.Length < 1) return; if (args.Length < 1) return;
ShowQuestionPrompt("Console command permissions to grant to client " + args[0] + "? You may enter multiple commands separated with a space.", (commandNames) => ShowQuestionPrompt("Console command permissions to grant to client " + args[0] + "? You may enter multiple commands separated with a space or use \"all\" to give the permission to use all console commands.", (commandNames) =>
{ {
GameMain.Client?.SendConsoleCommand("givecommandperm " + args[0] + " " + commandNames); GameMain.Client?.SendConsoleCommand("givecommandperm " + args[0] + " " + commandNames);
}, args, 1); }, args, 1);
@@ -1911,7 +1949,7 @@ namespace Barotrauma
{ {
if (args.Length < 1) return; if (args.Length < 1) return;
ShowQuestionPrompt("Console command permissions to revoke from client " + args[0] + "? You may enter multiple commands separated with a space.", (commandNames) => ShowQuestionPrompt("Console command permissions to revoke from client " + args[0] + "? You may enter multiple commands separated with a space or use \"all\" to revoke the permission to use any console commands.", (commandNames) =>
{ {
GameMain.Client?.SendConsoleCommand("revokecommandperm " + args[0] + " " + commandNames); GameMain.Client?.SendConsoleCommand("revokecommandperm " + args[0] + " " + commandNames);
}, args, 1); }, args, 1);
@@ -2206,7 +2244,7 @@ namespace Barotrauma
ThrowError("Cannot use the flipx command while playing online."); ThrowError("Cannot use the flipx command while playing online.");
return; return;
} }
Submarine.MainSub?.FlipX(); if (Submarine.MainSub.SubBody != null) { Submarine.MainSub?.FlipX(); }
}, isCheat: true)); }, isCheat: true));
commands.Add(new Command("gender", "Set the gender of the controlled character. Allowed parameters: Male, Female, None.", args => commands.Add(new Command("gender", "Set the gender of the controlled character. Allowed parameters: Male, Female, None.", args =>
@@ -2328,9 +2366,16 @@ namespace Barotrauma
} }
try try
{ {
SubmarineInfo subInfo = new SubmarineInfo(args[0]); var subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.DisplayName.Equals(args[0], StringComparison.OrdinalIgnoreCase));
Submarine spawnedSub = Submarine.Load(subInfo, false); if (subInfo == null)
spawnedSub.SetPosition(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition)); {
ThrowError($"Could not find a submarine with the name \"{args[0]}\".");
}
else
{
Submarine spawnedSub = Submarine.Load(subInfo, false);
spawnedSub.SetPosition(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition));
}
} }
catch (Exception e) catch (Exception e)
{ {
@@ -2338,7 +2383,15 @@ namespace Barotrauma
ThrowError(errorMsg, e); ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("DebugConsole.SpawnSubmarine:Error", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + '\n' + e.Message + '\n' + e.StackTrace); GameAnalyticsManager.AddErrorEventOnce("DebugConsole.SpawnSubmarine:Error", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + '\n' + e.Message + '\n' + e.StackTrace);
} }
}, isCheat: true)); },
() =>
{
return new string[][]
{
SubmarineInfo.SavedSubmarines.Select(s => s.DisplayName).ToArray()
};
},
isCheat: true));
commands.Add(new Command("pause", "Toggles the pause state when playing offline", (string[] args) => commands.Add(new Command("pause", "Toggles the pause state when playing offline", (string[] args) =>
{ {
@@ -83,10 +83,6 @@ namespace Barotrauma
} }
foreach (ScriptedEventSet eventSet in pendingEventSets) foreach (ScriptedEventSet eventSet in pendingEventSets)
{ {
float distanceTraveled = MathHelper.Clamp(
(Submarine.MainSub.WorldPosition.X - level.StartPosition.X) / (level.EndPosition.X - level.StartPosition.X),
0.0f, 1.0f);
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "New event (ID " + eventSet.DebugIdentifier + ") after: ", Color.Orange * 0.8f, null, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "New event (ID " + eventSet.DebugIdentifier + ") after: ", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12; y += 12;
@@ -6,6 +6,7 @@ namespace Barotrauma
{ {
public override void ClientReadInitial(IReadMessage msg) public override void ClientReadInitial(IReadMessage msg)
{ {
items.Clear();
ushort itemCount = msg.ReadUInt16(); ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++) for (int i = 0; i < itemCount; i++)
{ {
@@ -17,7 +18,7 @@ namespace Barotrauma
} }
if (items.Count != itemCount) if (items.Count != itemCount)
{ {
throw new System.Exception("Error in CargoMission.ClientReadInitial: item count does not match the server count (" + itemCount + " != " + items.Count + "mission: " + Prefab.Identifier + ")"); throw new System.Exception("Error in CargoMission.ClientReadInitial: item count does not match the server count (" + itemCount + " != " + items.Count + ", mission: " + Prefab.Identifier + ")");
} }
if (requiredDeliveryAmount == 0) { requiredDeliveryAmount = items.Count; } if (requiredDeliveryAmount == 0) { requiredDeliveryAmount = items.Count; }
} }
@@ -122,7 +122,7 @@ namespace Barotrauma
}; };
chatSendButton.RectTransform.AbsoluteOffset = new Point((int)(InputBox.Rect.Height * 0.15f), 0); chatSendButton.RectTransform.AbsoluteOffset = new Point((int)(InputBox.Rect.Height * 0.15f), 0);
InputBox.TextBlock.RectTransform.MaxSize InputBox.TextBlock.RectTransform.MaxSize
= new Point((int)(InputBox.Rect.Width - chatSendButton.Rect.Width * 1.25f - InputBox.TextBlock.Padding.Z), int.MaxValue); = new Point((int)(InputBox.Rect.Width - chatSendButton.Rect.Width * 1.25f - InputBox.TextBlock.Padding.X - chatSendButton.RectTransform.AbsoluteOffset.X), int.MaxValue);
showNewMessagesButton = new GUIButton(new RectTransform(new Vector2(1f, 0.075f), GUIFrame.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0.0f, 0.125f) }, TextManager.Get("chat.shownewmessages")); showNewMessagesButton = new GUIButton(new RectTransform(new Vector2(1f, 0.075f), GUIFrame.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0.0f, 0.125f) }, TextManager.Get("chat.shownewmessages"));
showNewMessagesButton.OnClicked += (GUIButton btn, object userdata) => showNewMessagesButton.OnClicked += (GUIButton btn, object userdata) =>
@@ -384,17 +384,6 @@ namespace Barotrauma
prevUIScale = GUI.Scale; prevUIScale = GUI.Scale;
} }
//hide chatbox when accessing the inventory of another character to prevent overlaps
if (Character.Controlled?.SelectedCharacter?.Inventory != null &&
Character.Controlled.SelectedCharacter.CanInventoryBeAccessed)
{
SetVisibility(false);
}
else
{
SetVisibility(true);
}
if (showNewMessagesButton.Visible && chatBox.ScrollBar.BarScroll == 1f) if (showNewMessagesButton.Visible && chatBox.ScrollBar.BarScroll == 1f)
{ {
showNewMessagesButton.Visible = false; showNewMessagesButton.Visible = false;
@@ -1,7 +1,7 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -38,7 +38,7 @@ namespace Barotrauma
private static GUIDropDown fileTypeDropdown; private static GUIDropDown fileTypeDropdown;
private static GUIButton openButton; private static GUIButton openButton;
private static FileSystemWatcher fileSystemWatcher; private static System.IO.FileSystemWatcher fileSystemWatcher;
private static string currentFileTypePattern; private static string currentFileTypePattern;
@@ -78,10 +78,10 @@ namespace Barotrauma
currentDirectory += "/"; currentDirectory += "/";
} }
fileSystemWatcher?.Dispose(); fileSystemWatcher?.Dispose();
fileSystemWatcher = new FileSystemWatcher(currentDirectory) fileSystemWatcher = new System.IO.FileSystemWatcher(currentDirectory)
{ {
Filter = "*", Filter = "*",
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName NotifyFilter = System.IO.NotifyFilters.LastWrite | System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName
}; };
fileSystemWatcher.Created += OnFileSystemChanges; fileSystemWatcher.Created += OnFileSystemChanges;
fileSystemWatcher.Deleted += OnFileSystemChanges; fileSystemWatcher.Deleted += OnFileSystemChanges;
@@ -97,11 +97,11 @@ namespace Barotrauma
set; set;
} }
private static void OnFileSystemChanges(object sender, FileSystemEventArgs e) private static void OnFileSystemChanges(object sender, System.IO.FileSystemEventArgs e)
{ {
switch (e.ChangeType) switch (e.ChangeType)
{ {
case WatcherChangeTypes.Created: case System.IO.WatcherChangeTypes.Created:
{ {
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), e.Name) var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), e.Name)
{ {
@@ -114,15 +114,15 @@ namespace Barotrauma
fileList.Content.RectTransform.SortChildren(SortFiles); fileList.Content.RectTransform.SortChildren(SortFiles);
} }
break; break;
case WatcherChangeTypes.Deleted: case System.IO.WatcherChangeTypes.Deleted:
{ {
var itemFrame = fileList.Content.FindChild(c => (c is GUITextBlock tb) && (tb.Text == e.Name || tb.Text == e.Name + "/")); var itemFrame = fileList.Content.FindChild(c => (c is GUITextBlock tb) && (tb.Text == e.Name || tb.Text == e.Name + "/"));
if (itemFrame != null) { fileList.RemoveChild(itemFrame); } if (itemFrame != null) { fileList.RemoveChild(itemFrame); }
} }
break; break;
case WatcherChangeTypes.Renamed: case System.IO.WatcherChangeTypes.Renamed:
{ {
RenamedEventArgs renameArgs = e as RenamedEventArgs; System.IO.RenamedEventArgs renameArgs = e as System.IO.RenamedEventArgs;
var itemFrame = fileList.Content.FindChild(c => (c is GUITextBlock tb) && (tb.Text == renameArgs.OldName || tb.Text == renameArgs.OldName + "/")) as GUITextBlock; var itemFrame = fileList.Content.FindChild(c => (c is GUITextBlock tb) && (tb.Text == renameArgs.OldName || tb.Text == renameArgs.OldName + "/")) as GUITextBlock;
itemFrame.UserData = (bool?)Directory.Exists(e.FullPath); itemFrame.UserData = (bool?)Directory.Exists(e.FullPath);
itemFrame.Text = renameArgs.Name; itemFrame.Text = renameArgs.Name;
@@ -156,7 +156,7 @@ namespace Barotrauma
public static void Init() public static void Init()
{ {
backgroundFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: null) backgroundFrame = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas), style: null)
{ {
Color = Color.Black * 0.5f, Color = Color.Black * 0.5f,
HoverColor = Color.Black * 0.5f, HoverColor = Color.Black * 0.5f,
@@ -169,10 +169,10 @@ namespace Barotrauma
var horizontalLayout = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, window.RectTransform, Anchor.Center), true); var horizontalLayout = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, window.RectTransform, Anchor.Center), true);
sidebar = new GUIListBox(new RectTransform(new Vector2(0.29f, 1.0f), horizontalLayout.RectTransform)); sidebar = new GUIListBox(new RectTransform(new Vector2(0.29f, 1.0f), horizontalLayout.RectTransform));
var drives = DriveInfo.GetDrives(); var drives = System.IO.DriveInfo.GetDrives();
foreach (var drive in drives) foreach (var drive in drives)
{ {
if (drive.DriveType == DriveType.Ram) { continue; } if (drive.DriveType == System.IO.DriveType.Ram) { continue; }
if (ignoredDrivePrefixes.Any(p => drive.Name.StartsWith(p))) { continue; } if (ignoredDrivePrefixes.Any(p => drive.Name.StartsWith(p))) { continue; }
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), sidebar.Content.RectTransform), drive.Name.Replace('\\','/')); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), sidebar.Content.RectTransform), drive.Name.Replace('\\','/'));
} }
@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.CharacterEditor; using Barotrauma.CharacterEditor;
@@ -80,8 +80,8 @@ namespace Barotrauma
public static readonly string[] colorComponentLabels = { "R", "G", "B", "A" }; public static readonly string[] colorComponentLabels = { "R", "G", "B", "A" };
public static Vector2 ReferenceResolution => new Vector2(1920f, 1080f); public static Vector2 ReferenceResolution => new Vector2(1920f, 1080f);
public static float Scale => (GameMain.GraphicsWidth / ReferenceResolution.X + GameMain.GraphicsHeight / ReferenceResolution.Y) / 2.0f * GameSettings.HUDScale; public static float Scale => (UIWidth / ReferenceResolution.X + GameMain.GraphicsHeight / ReferenceResolution.Y) / 2.0f * GameSettings.HUDScale;
public static float xScale => GameMain.GraphicsWidth / ReferenceResolution.X * GameSettings.HUDScale; public static float xScale => UIWidth / ReferenceResolution.X * GameSettings.HUDScale;
public static float yScale => GameMain.GraphicsHeight / ReferenceResolution.Y * GameSettings.HUDScale; public static float yScale => GameMain.GraphicsHeight / ReferenceResolution.Y * GameSettings.HUDScale;
public static int IntScale(float f) => (int)(f * Scale); public static int IntScale(float f) => (int)(f * Scale);
public static int IntScaleFloor(float f) => (int)Math.Floor(f * Scale); public static int IntScaleFloor(float f) => (int)Math.Floor(f * Scale);
@@ -90,6 +90,23 @@ namespace Barotrauma
public static float VerticalAspectRatio => GameMain.GraphicsHeight / (float)GameMain.GraphicsWidth; public static float VerticalAspectRatio => GameMain.GraphicsHeight / (float)GameMain.GraphicsWidth;
public static float RelativeHorizontalAspectRatio => HorizontalAspectRatio / (ReferenceResolution.X / ReferenceResolution.Y); public static float RelativeHorizontalAspectRatio => HorizontalAspectRatio / (ReferenceResolution.X / ReferenceResolution.Y);
public static float RelativeVerticalAspectRatio => VerticalAspectRatio / (ReferenceResolution.Y / ReferenceResolution.X); public static float RelativeVerticalAspectRatio => VerticalAspectRatio / (ReferenceResolution.Y / ReferenceResolution.X);
public static bool IsUltrawide => HorizontalAspectRatio > 2.0f;
public static int UIWidth
{
get
{
// Ultrawide
if (IsUltrawide)
{
return (int)(GameMain.GraphicsHeight * ReferenceResolution.X / ReferenceResolution.Y);
}
else
{
return GameMain.GraphicsWidth;
}
}
}
public static float SlicedSpriteScale public static float SlicedSpriteScale
{ {
@@ -521,6 +538,37 @@ namespace Barotrauma
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)anchorPivotStringSize.X - padding, yPos), anchorPivotString, Color.LightGreen, Color.Black, 0, SmallFont); DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)anchorPivotStringSize.X - padding, yPos), anchorPivotString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)anchorPivotStringSize.Y + padding / 2; yPos += (int)anchorPivotStringSize.Y + padding / 2;
} }
else
{
string guiScaleString = $"GUI.Scale: {Scale}";
string guixScaleString = $"GUI.xScale: {xScale}";
string guiyScaleString = $"GUI.yScale: {yScale}";
string relativeHorizontalAspectRatioString = $"RelativeHorizontalAspectRatio: {RelativeHorizontalAspectRatio}";
string relativeVerticalAspectRatioString = $"RelativeVerticalAspectRatio: {RelativeVerticalAspectRatio}";
Vector2 guiScaleStringSize = SmallFont.MeasureString(guiScaleString);
Vector2 guixScaleStringSize = SmallFont.MeasureString(guixScaleString);
Vector2 guiyScaleStringSize = SmallFont.MeasureString(guiyScaleString);
Vector2 relativeHorizontalAspectRatioStringSize = SmallFont.MeasureString(relativeHorizontalAspectRatioString);
Vector2 relativeVerticalAspectRatioStringSize = SmallFont.MeasureString(relativeVerticalAspectRatioString);
int padding = IntScale(10);
int yPos = padding;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)guiScaleStringSize.X - padding, yPos), guiScaleString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)guiScaleStringSize.Y + padding / 2;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)guixScaleStringSize.X - padding, yPos), guixScaleString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)guixScaleStringSize.Y + padding / 2;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)guiyScaleStringSize.X - padding, yPos), guiyScaleString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)guiyScaleStringSize.Y + padding / 2;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)relativeHorizontalAspectRatioStringSize.X - padding, yPos), relativeHorizontalAspectRatioString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)relativeHorizontalAspectRatioStringSize.Y + padding / 2;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)relativeVerticalAspectRatioStringSize.X - padding, yPos), relativeVerticalAspectRatioString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)relativeVerticalAspectRatioStringSize.Y + padding / 2;
}
} }
if (HUDLayoutSettings.DebugDraw) HUDLayoutSettings.Draw(spriteBatch); if (HUDLayoutSettings.DebugDraw) HUDLayoutSettings.Draw(spriteBatch);
@@ -1917,7 +1965,8 @@ namespace Barotrauma
Inventory.draggingItem = null; Inventory.draggingItem = null;
Inventory.DraggingInventory = null; Inventory.DraggingInventory = null;
PauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas), style: null, color: Color.Black * 0.5f); PauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas, Anchor.Center), style: null);
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, PauseMenu.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
var pauseMenuInner = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.3f), PauseMenu.RectTransform, Anchor.Center) { MinSize = new Point(250, 300) }); var pauseMenuInner = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.3f), PauseMenu.RectTransform, Anchor.Center) { MinSize = new Point(250, 300) });
@@ -8,7 +8,7 @@ namespace Barotrauma
{ {
public class GUICanvas : RectTransform public class GUICanvas : RectTransform
{ {
protected GUICanvas() : base(Vector2.One, parent: null) { } protected GUICanvas() : base(size, parent: null) { }
private static GUICanvas _instance; private static GUICanvas _instance;
public static GUICanvas Instance public static GUICanvas Instance
@@ -22,15 +22,63 @@ namespace Barotrauma
{ {
GameMain.Instance.OnResolutionChanged += RecalculateSize; GameMain.Instance.OnResolutionChanged += RecalculateSize;
} }
_instance.ItemComponentHolder = new GUIFrame(new RectTransform(Vector2.One, _instance, Anchor.Center)).RectTransform;
} }
return _instance; return _instance;
} }
} }
public RectTransform ItemComponentHolder;
private static Vector2 size => new Vector2(GameMain.GraphicsWidth / (float)GUI.UIWidth, 1f);
protected override Rectangle NonScaledUIRect => UIRect;
private enum ResizeAxis { Both = 0, X = 1, Y = 2 }
// Turn public, if there is a need to call this manually. // Turn public, if there is a need to call this manually.
private static void RecalculateSize() private static void RecalculateSize()
{ {
Instance.Resize(Vector2.One, resizeChildren: true); Vector2 recalculatedSize = size;
// Scale children that are supposed to encompass the whole screen so that they are properly scaled on ultrawide as well
for (int i = 0; i < Instance.Children.Count(); i++)
{
RectTransform target = Instance.GetChild(i);
if (target == null || target.RelativeSize.X < 1 && target.RelativeSize.Y < 1) continue;
ResizeAxis axis;
if (target.RelativeSize.X >= 1 && target.RelativeSize.Y >= 1)
{
axis = ResizeAxis.Both;
}
else if (target.RelativeSize.X >= 1)
{
axis = ResizeAxis.X;
}
else
{
axis = ResizeAxis.Y;
}
switch (axis)
{
case ResizeAxis.Both:
target.RelativeSize = recalculatedSize;
break;
case ResizeAxis.X:
target.RelativeSize = new Vector2(recalculatedSize.X, target.RelativeSize.Y);
break;
case ResizeAxis.Y:
target.RelativeSize = new Vector2(target.RelativeSize.X, recalculatedSize.Y);
break;
}
}
Instance.Resize(size, resizeChildren: true);
Instance.GetAllChildren().Select(c => c.GUIComponent as GUITextBlock).ForEach(t => t?.SetTextPos()); Instance.GetAllChildren().Select(c => c.GUIComponent as GUITextBlock).ForEach(t => t?.SetTextPos());
} }
} }
@@ -5,7 +5,7 @@ using System.Linq;
using Barotrauma.Extensions; using Barotrauma.Extensions;
using System; using System;
using System.Xml.Linq; using System.Xml.Linq;
using System.IO; using Barotrauma.IO;
using RestSharp; using RestSharp;
using System.Net; using System.Net;
@@ -394,7 +394,7 @@ namespace Barotrauma
if (Dropped) if (Dropped)
{ {
listBox.AddToGUIUpdateList(false, UpdateOrder); listBox.AddToGUIUpdateList(false, 1);
} }
} }
@@ -10,7 +10,17 @@ namespace Barotrauma
public class GUIImage : GUIComponent public class GUIImage : GUIComponent
{ {
//paths of the textures that are being loaded asynchronously //paths of the textures that are being loaded asynchronously
private static readonly HashSet<string> activeTextureLoads = new HashSet<string>(); private static readonly List<string> activeTextureLoads = new List<string>();
private static bool loadingTextures;
public static bool LoadingTextures
{
get
{
return loadingTextures;
}
}
public float Rotation; public float Rotation;
@@ -122,6 +132,7 @@ namespace Barotrauma
{ {
if (LoadAsynchronously) if (LoadAsynchronously)
{ {
loadingTextures = true;
loading = true; loading = true;
TaskPool.Add(LoadTextureAsync(), (Task) => TaskPool.Add(LoadTextureAsync(), (Task) =>
{ {
@@ -223,6 +234,7 @@ namespace Barotrauma
lock (activeTextureLoads) lock (activeTextureLoads)
{ {
activeTextureLoads.Remove(Sprite.FullPath); activeTextureLoads.Remove(Sprite.FullPath);
loadingTextures = activeTextureLoads.Count > 0;
} }
} }
return true; return true;
@@ -76,19 +76,7 @@ namespace Barotrauma
public GUILayoutGroup(RectTransform rectT, bool isHorizontal = false, Anchor childAnchor = Anchor.TopLeft) : base(null, rectT) public GUILayoutGroup(RectTransform rectT, bool isHorizontal = false, Anchor childAnchor = Anchor.TopLeft) : base(null, rectT)
{ {
#if DEBUG CanBeFocused = false;
if (GameMain.DebugDraw)
{
CanBeFocused = true;
}
else
{
#endif
CanBeFocused = false;
#if DEBUG
}
#endif
this.isHorizontal = isHorizontal; this.isHorizontal = isHorizontal;
this.childAnchor = childAnchor; this.childAnchor = childAnchor;
rectT.ChildrenChanged += (child) => needsToRecalculate = true; rectT.ChildrenChanged += (child) => needsToRecalculate = true;
@@ -11,7 +11,7 @@ namespace Barotrauma
public static List<GUIComponent> MessageBoxes = new List<GUIComponent>(); public static List<GUIComponent> MessageBoxes = new List<GUIComponent>();
private static int DefaultWidth private static int DefaultWidth
{ {
get { return Math.Max(400, 400 * (GameMain.GraphicsWidth / 1920)); } get { return Math.Max(400, (int)(400 * (GameMain.GraphicsWidth / GUI.ReferenceResolution.X))); }
} }
private float inGameCloseTimer = 0.0f; private float inGameCloseTimer = 0.0f;
@@ -63,7 +63,7 @@ namespace Barotrauma
} }
public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null) public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null)
: base(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: GUI.Style.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox") : base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUI.Style.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox")
{ {
int width = (int)(DefaultWidth * (type == Type.Default ? 1.0f : 1.5f)), height = 0; int width = (int)(DefaultWidth * (type == Type.Default ? 1.0f : 1.5f)), height = 0;
if (relativeSize.HasValue) if (relativeSize.HasValue)
@@ -510,6 +510,8 @@ namespace Barotrauma
var currPosition = positions[0]; var currPosition = positions[0];
float topY = positions.Min(p => p.Item1.Y);
for (int i = 1; i < positions.Count; i++) for (int i = 1; i < positions.Count; i++)
{ {
var p1 = positions[i]; var p1 = positions[i];
@@ -527,7 +529,7 @@ namespace Barotrauma
else else
{ {
diffY = Math.Abs(p1.Item1.Y - pos.Y); diffY = Math.Abs(p1.Item1.Y - pos.Y);
if (diffY < halfHeight) if (diffY < halfHeight || (p1.Item1.Y == topY && pos.Y < topY))
{ {
//we are on this line, select the nearest character //we are on this line, select the nearest character
float diffX = Math.Abs(p1.Item1.X - pos.X) - Math.Abs(p2.Item1.X - pos.X); float diffX = Math.Abs(p1.Item1.X - pos.X) - Math.Abs(p2.Item1.X - pos.X);
@@ -67,6 +67,8 @@ namespace Barotrauma
private Vector2 selectionEndPos; private Vector2 selectionEndPos;
private Vector2 selectionRectSize; private Vector2 selectionRectSize;
private bool mouseHeldInside;
private readonly Memento<string> memento = new Memento<string>(); private readonly Memento<string> memento = new Memento<string>();
// Skip one update cycle, fixes Enter key instantly deselecting the chatbox // Skip one update cycle, fixes Enter key instantly deselecting the chatbox
@@ -414,6 +416,7 @@ namespace Barotrauma
State = ComponentState.Hover; State = ComponentState.Hover;
if (PlayerInput.PrimaryMouseButtonDown()) if (PlayerInput.PrimaryMouseButtonDown())
{ {
mouseHeldInside = true;
Select(); Select();
} }
else else
@@ -436,7 +439,11 @@ namespace Barotrauma
} }
else else
{ {
if ((PlayerInput.LeftButtonClicked() || PlayerInput.RightButtonClicked()) && selected) Deselect(); if ((PlayerInput.LeftButtonClicked() || PlayerInput.RightButtonClicked()) && selected)
{
if (!mouseHeldInside) { Deselect(); }
mouseHeldInside = false;
}
isSelecting = false; isSelecting = false;
State = ComponentState.None; State = ComponentState.None;
} }
@@ -50,10 +50,6 @@ namespace Barotrauma
get; private set; get; private set;
} }
/*public static Rectangle HealthBarAreaRight
{
get; private set;
}*/
public static Rectangle HealthBarArea public static Rectangle HealthBarArea
{ {
get; private set; get; private set;
@@ -120,17 +116,11 @@ namespace Barotrauma
//horizontal slices at the corners of the screen for health bar and affliction icons //horizontal slices at the corners of the screen for health bar and affliction icons
int afflictionAreaHeight = (int)(50 * GUI.Scale); int afflictionAreaHeight = (int)(50 * GUI.Scale);
int healthBarWidth = BottomRightInfoArea.Width + CharacterInventory.SlotSize.X + CharacterInventory.Spacing * 2 + CharacterInventory.HideButtonWidth; int healthBarWidth = (int)(BottomRightInfoArea.Width * 1.58f);
int healthBarHeight = (int)(50f * GUI.Scale); int healthBarHeight = (int)(50f * GUI.Scale);
HealthBarArea = new Rectangle(BottomRightInfoArea.X - (healthBarWidth - BottomRightInfoArea.Width) + (int)(2 * GUI.Scale), BottomRightInfoArea.Y - healthBarHeight + (int)(10 * GUI.Scale), healthBarWidth, healthBarHeight); HealthBarArea = new Rectangle(BottomRightInfoArea.Right - healthBarWidth + (int)Math.Floor(1 / GUI.Scale), BottomRightInfoArea.Y - healthBarHeight + GUI.IntScale(10), healthBarWidth, healthBarHeight);
AfflictionAreaLeft = new Rectangle(HealthBarArea.X, HealthBarArea.Y - Padding - afflictionAreaHeight, HealthBarArea.Width, afflictionAreaHeight); AfflictionAreaLeft = new Rectangle(HealthBarArea.X, HealthBarArea.Y - Padding - afflictionAreaHeight, HealthBarArea.Width, afflictionAreaHeight);
//HealthBarAreaRight = new Rectangle(Padding, GameMain.GraphicsHeight - healthBarHeight - Padding, healthBarWidth, healthBarHeight);
/*if (HealthBarAreaRight.Y + healthBarHeight * 0.75f < PortraitArea.Y)
{
HealthBarAreaRight = new Rectangle(GameMain.GraphicsWidth - Padding - healthBarWidth, HealthBarAreaRight.Y, HealthBarAreaRight.Width, HealthBarAreaRight.Height);
}*/
//AfflictionAreaRight = new Rectangle(HealthBarAreaRight.X, HealthBarAreaRight.Y + healthBarHeight + Padding, healthBarWidth, afflictionAreaHeight);
int messageAreaWidth = GameMain.GraphicsWidth / 3; int messageAreaWidth = GameMain.GraphicsWidth / 3;
MessageAreaTop = new Rectangle((GameMain.GraphicsWidth - messageAreaWidth) / 2, ButtonAreaTop.Bottom, messageAreaWidth, ButtonAreaTop.Height); MessageAreaTop = new Rectangle((GameMain.GraphicsWidth - messageAreaWidth) / 2, ButtonAreaTop.Bottom, messageAreaWidth, ButtonAreaTop.Height);
@@ -146,7 +136,7 @@ namespace Barotrauma
CrewArea = new Rectangle(Padding, Padding, (int)Math.Max(400 * GUI.Scale, 220), ObjectiveAnchor.Top - Padding * 2); CrewArea = new Rectangle(Padding, Padding, (int)Math.Max(400 * GUI.Scale, 220), ObjectiveAnchor.Top - Padding * 2);
InventoryAreaLower = new Rectangle(Padding, inventoryTopY, GameMain.GraphicsWidth - Padding * 2, GameMain.GraphicsHeight - inventoryTopY); InventoryAreaLower = new Rectangle(ChatBoxArea.Right + Padding * 7, inventoryTopY, GameMain.GraphicsWidth - Padding * 9 - ChatBoxArea.Width, GameMain.GraphicsHeight - inventoryTopY);
int healthWindowWidth = (int)(GameMain.GraphicsWidth * 0.5f); int healthWindowWidth = (int)(GameMain.GraphicsWidth * 0.5f);
int healthWindowHeight = (int)(GameMain.GraphicsWidth * 0.5f * 0.65f); int healthWindowHeight = (int)(GameMain.GraphicsWidth * 0.5f * 0.65f);
@@ -253,11 +253,12 @@ namespace Barotrauma
return _rect; return _rect;
} }
} }
public Rectangle ParentRect => Parent != null ? Parent.Rect : ScreenRect; public Rectangle ParentRect => Parent != null ? Parent.Rect : UIRect;
protected Rectangle NonScaledRect => new Rectangle(NonScaledTopLeft, NonScaledSize); protected Rectangle NonScaledRect => new Rectangle(NonScaledTopLeft, NonScaledSize);
protected Rectangle NonScaledParentRect => parent != null ? Parent.NonScaledRect : ScreenRect; protected virtual Rectangle NonScaledUIRect => NonScaledRect;
protected Rectangle ScreenRect => new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight); protected Rectangle NonScaledParentRect => parent != null ? Parent.NonScaledRect : UIRect;
protected Rectangle NonScaledParentUIRect => parent != null ? Parent.NonScaledUIRect : UIRect;
protected Rectangle UIRect => new Rectangle(0, 0, GUI.UIWidth, GameMain.GraphicsHeight);
private Pivot pivot; private Pivot pivot;
/// <summary> /// <summary>
@@ -444,14 +445,14 @@ namespace Barotrauma
protected void RecalculateRelativeSize() protected void RecalculateRelativeSize()
{ {
relativeSize = new Vector2(NonScaledSize.X, NonScaledSize.Y) / new Vector2(NonScaledParentRect.Width, NonScaledParentRect.Height); relativeSize = new Vector2(NonScaledSize.X, NonScaledSize.Y) / new Vector2(NonScaledParentUIRect.Width, NonScaledParentUIRect.Height);
recalculateRect = true; recalculateRect = true;
SizeChanged?.Invoke(); SizeChanged?.Invoke();
} }
protected void RecalculateAbsoluteSize() protected void RecalculateAbsoluteSize()
{ {
Point size = NonScaledParentRect.Size; Point size = NonScaledParentUIRect.Size;
switch (ScaleBasis) switch (ScaleBasis)
{ {
case ScaleBasis.BothWidth: case ScaleBasis.BothWidth:
@@ -177,7 +177,8 @@ namespace Barotrauma
{ {
tabButtons.Clear(); tabButtons.Clear();
infoFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker"); infoFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null);
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, infoFrame.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
switch (selectedTab) switch (selectedTab)
{ {
@@ -488,6 +489,12 @@ namespace Barotrauma
Color = (GameMain.NetworkMember != null && GameMain.Client.Character == character) ? ownCharacterBGColor : Color.Transparent Color = (GameMain.NetworkMember != null && GameMain.Client.Character == character) ? ownCharacterBGColor : Color.Transparent
}; };
frame.OnSecondaryClicked += (component, data) =>
{
GameMain.GameSession?.CrewManager?.CreateModerationContextMenu(PlayerInput.MousePosition.ToPoint(), client);
return true;
};
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true) var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
{ {
AbsoluteSpacing = 2 AbsoluteSpacing = 2
@@ -593,6 +600,8 @@ namespace Barotrauma
{ {
GUITextBlock characterNameBlock; GUITextBlock characterNameBlock;
Sprite permissionIcon = GetPermissionIcon(client); Sprite permissionIcon = GetPermissionIcon(client);
JobPrefab prefab = client.Character?.Info?.Job?.Prefab;
Color nameColor = prefab != null ? prefab.UIColor : Color.White;
if (permissionIcon != null) if (permissionIcon != null)
{ {
@@ -600,7 +609,7 @@ namespace Barotrauma
float characterNameWidthAdjustment = (iconSize.X + paddedFrame.AbsoluteSpacing) / characterColumnWidth; float characterNameWidthAdjustment = (iconSize.X + paddedFrame.AbsoluteSpacing) / characterColumnWidth;
characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
ToolBox.LimitString(client.Name, GUI.Font, (int)(characterColumnWidth - paddedFrame.Rect.Width * characterNameWidthAdjustment)), textAlignment: Alignment.Center, textColor: client.Character != null ? client.Character.Info.Job.Prefab.UIColor : Color.White); ToolBox.LimitString(client.Name, GUI.Font, (int)(characterColumnWidth - paddedFrame.Rect.Width * characterNameWidthAdjustment)), textAlignment: Alignment.Center, textColor: nameColor);
float iconWidth = iconSize.X / (float)characterColumnWidth; float iconWidth = iconSize.X / (float)characterColumnWidth;
int xOffset = (int)(jobColumnWidth + characterNameBlock.TextPos.X - GUI.Font.MeasureString(characterNameBlock.Text).X / 2f - paddedFrame.AbsoluteSpacing - iconWidth * paddedFrame.Rect.Width); int xOffset = (int)(jobColumnWidth + characterNameBlock.TextPos.X - GUI.Font.MeasureString(characterNameBlock.Text).X / 2f - paddedFrame.AbsoluteSpacing - iconWidth * paddedFrame.Rect.Width);
@@ -609,7 +618,7 @@ namespace Barotrauma
else else
{ {
characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
ToolBox.LimitString(client.Name, GUI.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: client.Character != null ? client.Character.Info.Job.Prefab.UIColor : Color.White); ToolBox.LimitString(client.Name, GUI.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: nameColor);
} }
if (client.Character != null && client.Character.IsDead) if (client.Character != null && client.Character.IsDead)
@@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Media; using Barotrauma.Media;
using System.IO; using Barotrauma.IO;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
namespace Barotrauma namespace Barotrauma
@@ -56,7 +56,7 @@ namespace Barotrauma
public VideoPlayer() // GUI elements with size set to Point.Zero are resized based on content public VideoPlayer() // GUI elements with size set to Point.Zero are resized based on content
{ {
int screenWidth = (int)(GameMain.GraphicsWidth * 0.55f); int screenWidth = (int)(GameMain.GraphicsWidth * 0.65f);
scaledVideoResolution = new Point(screenWidth, (int)(screenWidth / 16f * 9f)); scaledVideoResolution = new Point(screenWidth, (int)(screenWidth / 16f * 9f));
int width = scaledVideoResolution.X; int width = scaledVideoResolution.X;
@@ -178,6 +178,7 @@ namespace Barotrauma
videoFrame.RectTransform.NonScaledSize = scaledVideoResolution + new Point(scaledBorderSize, scaledBorderSize); videoFrame.RectTransform.NonScaledSize = scaledVideoResolution + new Point(scaledBorderSize, scaledBorderSize);
videoView.RectTransform.NonScaledSize = scaledVideoResolution; videoView.RectTransform.NonScaledSize = scaledVideoResolution;
videoFrame.RectTransform.AbsoluteOffset = new Point(0, videoFrame.RectTransform.NonScaledSize.Y);
title.RectTransform.NonScaledSize = new Point(scaledTextWidth, scaledTitleHeight); title.RectTransform.NonScaledSize = new Point(scaledTextWidth, scaledTitleHeight);
title.RectTransform.AbsoluteOffset = new Point((int)(5 * GUI.Scale), (int)(10 * GUI.Scale)); title.RectTransform.AbsoluteOffset = new Point((int)(5 * GUI.Scale), (int)(10 * GUI.Scale));
@@ -247,7 +248,7 @@ namespace Barotrauma
} }
else else
{ {
videoFrame.RectTransform.AbsoluteOffset = new Point(0, (int)(100 * GUI.Scale)); videoFrame.RectTransform.AbsoluteOffset = new Point(0, 0);
okButton = new GUIButton(new RectTransform(scaledButtonSize, videoFrame.RectTransform, Anchor.TopLeft, Pivot.TopLeft) { AbsoluteOffset = new Point(scaledBorderSize, scaledBorderSize) }, TextManager.Get("Back")) okButton = new GUIButton(new RectTransform(scaledButtonSize, videoFrame.RectTransform, Anchor.TopLeft, Pivot.TopLeft) { AbsoluteOffset = new Point(scaledBorderSize, scaledBorderSize) }, TextManager.Get("Back"))
{ {
@@ -12,7 +12,7 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using GameAnalyticsSDK.Net; using GameAnalyticsSDK.Net;
using System.IO; using Barotrauma.IO;
using System.Threading; using System.Threading;
using Barotrauma.Tutorials; using Barotrauma.Tutorials;
using Barotrauma.Media; using Barotrauma.Media;
@@ -532,6 +532,8 @@ namespace Barotrauma
ScriptedEventSet.LoadPrefabs(); ScriptedEventSet.LoadPrefabs();
AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions)); AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions));
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings)); SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
Order.Init();
EventManagerSettings.Init();
TitleScreen.LoadState = 50.0f; TitleScreen.LoadState = 50.0f;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
@@ -860,7 +862,7 @@ namespace Barotrauma
//TODO: do we need to check Inventory.SelectedSlot? //TODO: do we need to check Inventory.SelectedSlot?
&& Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null && Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null
&& !CrewManager.IsCommandInterfaceOpen && !CrewManager.IsCommandInterfaceOpen
&& !(Screen.Selected is SubEditorScreen editor && !editor.WiringMode && Character.Controlled.SelectedConstruction != null)) && !(Screen.Selected is SubEditorScreen editor && !editor.WiringMode && Character.Controlled?.SelectedConstruction != null))
{ {
// Otherwise toggle pausing, unless another window/interface is open. // Otherwise toggle pausing, unless another window/interface is open.
GUI.TogglePauseMenu(); GUI.TogglePauseMenu();
@@ -1264,7 +1264,7 @@ namespace Barotrauma
} }
else else
{ {
CreateCommandUI(HUDLayoutSettings.PortraitArea.Contains(PlayerInput.MousePosition) ? Character.Controlled : GUI.MouseOn?.UserData as Character); CreateCommandUI(HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) ? Character.Controlled : GUI.MouseOn?.UserData as Character);
} }
GUI.PlayUISound(GUISoundType.PopupMenu); GUI.PlayUISound(GUISoundType.PopupMenu);
clicklessSelectionActive = isOpeningClick = true; clicklessSelectionActive = isOpeningClick = true;
@@ -1288,18 +1288,9 @@ namespace Barotrauma
else if (PlayerInput.SecondaryMouseButtonClicked() && characterContext == null && else if (PlayerInput.SecondaryMouseButtonClicked() && characterContext == null &&
(optionNodes.Any(n => GUI.IsMouseOn(n.Item1)) || shortcutNodes.Any(n => GUI.IsMouseOn(n)))) (optionNodes.Any(n => GUI.IsMouseOn(n.Item1)) || shortcutNodes.Any(n => GUI.IsMouseOn(n))))
{ {
var node = optionNodes.Find(n => GUI.IsMouseOn(n.Item1))?.Item1; var node = optionNodes.Find(n => GUI.IsMouseOn(n.Item1))?.Item1 ?? shortcutNodes.Find(n => GUI.IsMouseOn(n));
if (node == null) // Make sure the node is for an option-less order or an order option
{ if ((node.UserData is Order order && !order.HasOptions && (!order.MustSetTarget || itemContext != null)) || node.UserData is Tuple<Order, string>)
node = shortcutNodes.Find(n => GUI.IsMouseOn(n));
}
// Make sure the node is for an option-less order...
if (node.UserData is Order order && !order.HasOptions)
{
CreateAssignmentNodes(node);
}
// ...or an order option
else if (node.UserData is Tuple<Order, string>)
{ {
CreateAssignmentNodes(node); CreateAssignmentNodes(node);
} }
@@ -1543,31 +1534,31 @@ namespace Barotrauma
} }
private GUIFrame commandFrame, targetFrame; private GUIFrame commandFrame, targetFrame;
private GUIButton centerNode, returnNode, expandNode, shortcutCenterNode; private GUIButton centerNode, returnNode, expandNode, shortcutCenterNode;
private List<Tuple<GUIComponent, Keys>> optionNodes = new List<Tuple<GUIComponent, Keys>>(); private readonly List<Tuple<GUIComponent, Keys>> optionNodes = new List<Tuple<GUIComponent, Keys>>();
private Keys returnNodeHotkey = Keys.None, expandNodeHotkey = Keys.None; private Keys returnNodeHotkey = Keys.None, expandNodeHotkey = Keys.None;
private List<GUIComponent> shortcutNodes = new List<GUIComponent>(); private readonly List<GUIComponent> shortcutNodes = new List<GUIComponent>();
private List<GUIComponent> extraOptionNodes = new List<GUIComponent>(); private readonly List<GUIComponent> extraOptionNodes = new List<GUIComponent>();
private GUICustomComponent nodeConnectors; private GUICustomComponent nodeConnectors;
private GUIImage background; private GUIImage background;
private GUIButton selectedNode; private GUIButton selectedNode;
private float selectionTime = 0.75f, timeSelected = 0.0f; private readonly float selectionTime = 0.75f;
private float timeSelected = 0.0f;
private bool clicklessSelectionActive, isOpeningClick, isSelectionHighlighted; private bool clicklessSelectionActive, isOpeningClick, isSelectionHighlighted;
private Point centerNodeSize, nodeSize, shortcutCenterNodeSize, shortcutNodeSize, returnNodeSize; private Point centerNodeSize, nodeSize, shortcutCenterNodeSize, shortcutNodeSize, returnNodeSize, assignmentNodeSize;
private float centerNodeMargin, optionNodeMargin, shortcutCenterNodeMargin, shortcutNodeMargin, returnNodeMargin; private float centerNodeMargin, optionNodeMargin, shortcutCenterNodeMargin, shortcutNodeMargin, returnNodeMargin;
private List<OrderCategory> availableCategories; private List<OrderCategory> availableCategories;
private Stack<GUIButton> historyNodes = new Stack<GUIButton>(); private Stack<GUIButton> historyNodes = new Stack<GUIButton>();
private List<Character> extraOptionCharacters = new List<Character>(); private readonly List<Character> extraOptionCharacters = new List<Character>();
/// <summary> /// <summary>
/// node.Color = node.HighlightColor * nodeColorMultiplier /// node.Color = node.HighlightColor * nodeColorMultiplier
/// </summary> /// </summary>
private const float nodeColorMultiplier = 0.75f; private const float nodeColorMultiplier = 0.75f;
private const int assignmentNodeMaxCount = 8;
private int nodeDistance = (int)(GUI.Scale * 250); private int nodeDistance = (int)(GUI.Scale * 250);
private float returnNodeDistanceModifier = 0.65f; private const float returnNodeDistanceModifier = 0.65f;
private Order dismissedOrderPrefab; private Order dismissedOrderPrefab;
private Character characterContext; private Character characterContext;
private Item itemContext; private Item itemContext;
@@ -1576,6 +1567,7 @@ namespace Barotrauma
private readonly List<Order> contextualOrders = new List<Order>(); private readonly List<Order> contextualOrders = new List<Order>();
private Point shorcutCenterNodeOffset; private Point shorcutCenterNodeOffset;
private const int maxShorcutNodeCount = 4; private const int maxShorcutNodeCount = 4;
private bool WasCommandInterfaceDisabledThisUpdate { get; set; } private bool WasCommandInterfaceDisabledThisUpdate { get; set; }
private bool CanIssueOrders private bool CanIssueOrders
{ {
@@ -1584,7 +1576,7 @@ namespace Barotrauma
#if DEBUG #if DEBUG
return Character.Controlled == null || Character.Controlled.Info != null && Character.Controlled.SpeechImpediment < 100.0f; return Character.Controlled == null || Character.Controlled.Info != null && Character.Controlled.SpeechImpediment < 100.0f;
#else #else
return Character.Controlled != null && Character.Controlled.SpeechImpediment < 100.0f; return Character.Controlled?.Info != null && Character.Controlled.SpeechImpediment < 100.0f;
#endif #endif
} }
} }
@@ -1743,16 +1735,21 @@ namespace Barotrauma
private void ScaleCommandUI() private void ScaleCommandUI()
{ {
centerNodeSize = new Point((int)(100 * GUI.Scale)); // Node sizes
nodeSize = new Point((int)(100 * GUI.Scale)); nodeSize = new Point((int)(100 * GUI.Scale));
shortcutCenterNodeSize = new Point((int)(48 * GUI.Scale)); centerNodeSize = nodeSize;
shortcutNodeSize = new Point((int)(64 * GUI.Scale));
returnNodeSize = new Point((int)(48 * GUI.Scale)); returnNodeSize = new Point((int)(48 * GUI.Scale));
assignmentNodeSize = new Point((int)(64 * GUI.Scale));
shortcutCenterNodeSize = returnNodeSize;
shortcutNodeSize = assignmentNodeSize;
// Node margins (used in drawing the connecting lines)
centerNodeMargin = centerNodeSize.X * 0.5f; centerNodeMargin = centerNodeSize.X * 0.5f;
optionNodeMargin = nodeSize.X * 0.5f; optionNodeMargin = nodeSize.X * 0.5f;
shortcutCenterNodeMargin = shortcutCenterNodeSize.X * 0.45f; shortcutCenterNodeMargin = shortcutCenterNodeSize.X * 0.45f;
shortcutNodeMargin = shortcutNodeSize.X * 0.5f; shortcutNodeMargin = shortcutNodeSize.X * 0.5f;
returnNodeMargin = returnNodeSize.X * 0.5f; returnNodeMargin = returnNodeSize.X * 0.5f;
nodeDistance = (int)(150 * GUI.Scale); nodeDistance = (int)(150 * GUI.Scale);
shorcutCenterNodeOffset = new Point(0, (int)(1.25f * nodeDistance)); shorcutCenterNodeOffset = new Point(0, (int)(1.25f * nodeDistance));
} }
@@ -1783,12 +1780,12 @@ namespace Barotrauma
{ {
if (centerNode == null || optionNodes == null) { return; } if (centerNode == null || optionNodes == null) { return; }
var startNodePos = centerNode.Rect.Center.ToVector2(); var startNodePos = centerNode.Rect.Center.ToVector2();
if (targetFrame == null || !targetFrame.Visible) // Don't draw connectors for mini map options or assignment nodes
if ((targetFrame == null || !targetFrame.Visible) && !(optionNodes.FirstOrDefault()?.Item1.UserData is Character))
{ {
optionNodes.ForEach(n => DrawNodeConnector(startNodePos, centerNodeMargin, n.Item1, optionNodeMargin, spriteBatch)); optionNodes.ForEach(n => DrawNodeConnector(startNodePos, centerNodeMargin, n.Item1, optionNodeMargin, spriteBatch));
} }
DrawNodeConnector(startNodePos, centerNodeMargin, returnNode, returnNodeMargin, spriteBatch); DrawNodeConnector(startNodePos, centerNodeMargin, returnNode, returnNodeMargin, spriteBatch);
DrawNodeConnector(startNodePos, centerNodeMargin, expandNode, optionNodeMargin, spriteBatch);
if (shortcutCenterNode == null || !shortcutCenterNode.Visible) { return; } if (shortcutCenterNode == null || !shortcutCenterNode.Visible) { return; }
DrawNodeConnector(startNodePos, centerNodeMargin, shortcutCenterNode, shortcutCenterNodeMargin, spriteBatch); DrawNodeConnector(startNodePos, centerNodeMargin, shortcutCenterNode, shortcutCenterNodeMargin, spriteBatch);
startNodePos = shortcutCenterNode.Rect.Center.ToVector2(); startNodePos = shortcutCenterNode.Rect.Center.ToVector2();
@@ -1850,6 +1847,7 @@ namespace Barotrauma
shortcutNodes.Remove(node); shortcutNodes.Remove(node);
}; };
RemoveOptionNodes(); RemoveOptionNodes();
if (returnNode != null) if (returnNode != null)
{ {
returnNode.RemoveChild(returnNode.GetChildByUserData("hotkey")); returnNode.RemoveChild(returnNode.GetChildByUserData("hotkey"));
@@ -1857,15 +1855,20 @@ namespace Barotrauma
returnNode.Visible = false; returnNode.Visible = false;
historyNodes.Push(returnNode); historyNodes.Push(returnNode);
} }
SetReturnNode(centerNode, new Point(
(int)(node.RectTransform.AbsoluteOffset.X * -returnNodeDistanceModifier), // When the mini map is shown, always position the return node on the bottom
(int)(node.RectTransform.AbsoluteOffset.Y * -returnNodeDistanceModifier))); var offset = node?.UserData is Order order && order.GetMatchingItems(true).Count > 1 ?
new Point(0, (int)(returnNodeDistanceModifier * nodeDistance)) :
node.RectTransform.AbsoluteOffset.Multiply(-returnNodeDistanceModifier);
SetReturnNode(centerNode, offset);
SetCenterNode(node); SetCenterNode(node);
if (shortcutCenterNode != null) if (shortcutCenterNode != null)
{ {
commandFrame.RemoveChild(shortcutCenterNode); commandFrame.RemoveChild(shortcutCenterNode);
shortcutCenterNode = null; shortcutCenterNode = null;
} }
CreateNodes(userData); CreateNodes(userData);
CreateReturnNodeHotkey(); CreateReturnNodeHotkey();
return true; return true;
@@ -1914,9 +1917,14 @@ namespace Barotrauma
} }
} }
private void SetCenterNode(GUIButton node) private void SetCenterNode(GUIButton node, bool resetAnchor = false)
{ {
node.RectTransform.Parent = commandFrame.RectTransform; node.RectTransform.Parent = commandFrame.RectTransform;
if (resetAnchor)
{
node.RectTransform.SetPosition(Anchor.Center);
}
node.RectTransform.SetPosition(Anchor.Center);
node.RectTransform.MoveOverTime(Point.Zero, CommandNodeAnimDuration); node.RectTransform.MoveOverTime(Point.Zero, CommandNodeAnimDuration);
node.RectTransform.ScaleOverTime(centerNodeSize, CommandNodeAnimDuration); node.RectTransform.ScaleOverTime(centerNodeSize, CommandNodeAnimDuration);
node.RemoveChild(node.GetChildByUserData("hotkey")); node.RemoveChild(node.GetChildByUserData("hotkey"));
@@ -2021,14 +2029,13 @@ namespace Barotrauma
private void CreateShortcutNodes() private void CreateShortcutNodes()
{ {
var sub = Character.Controlled != null && Character.Controlled.TeamID == Character.TeamType.Team2 && Submarine.MainSubs.Length > 1 ? Submarine sub = GetTargetSubmarine();
Submarine.MainSubs[1] : Submarine.MainSub;
if (sub == null) { return; } if (sub == null) { return; }
shortcutNodes.Clear(); shortcutNodes.Clear();
if (shortcutNodes.Count < maxShorcutNodeCount && sub.GetItems(false).Find(i => i.HasTag("reactor"))?.GetComponent<Reactor>() is Reactor reactor) if (shortcutNodes.Count < maxShorcutNodeCount && sub.GetItems(false).Find(i => i.HasTag("reactor") && !i.NonInteractable)?.GetComponent<Reactor>() is Reactor reactor)
{ {
var reactorOutput = -reactor.CurrPowerConsumption; var reactorOutput = -reactor.CurrPowerConsumption;
// If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor // If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor
@@ -2037,8 +2044,9 @@ namespace Barotrauma
reactorOutput < float.Epsilon && characters.None(c => c.SelectedConstruction == reactor.Item)) reactorOutput < float.Epsilon && characters.None(c => c.SelectedConstruction == reactor.Item))
{ {
var order = new Order(Order.GetPrefab("operatereactor"), reactor.Item, reactor, Character.Controlled); var order = new Order(Order.GetPrefab("operatereactor"), reactor.Item, reactor, Character.Controlled);
var option = order.Prefab.Options[0];
shortcutNodes.Add( shortcutNodes.Add(
CreateOrderOptionNode(shortcutNodeSize, null, Point.Zero, order, order.Prefab.Options[0], order.Prefab.OptionNames[0], -1)); CreateOrderOptionNode(shortcutNodeSize, null, Point.Zero, order, option, order.Prefab.GetOptionName(option), -1));
} }
} }
@@ -2046,7 +2054,7 @@ namespace Barotrauma
// If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up // If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up
// --> Create shortcut node for Steer order // --> Create shortcut node for Steer order
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info.Job.Prefab != JobPrefab.Get("captain")) && if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info.Job.Prefab != JobPrefab.Get("captain")) &&
sub.GetItems(false).Find(i => i.HasTag("navterminal")) is Item nav && characters.None(c => c.SelectedConstruction == nav) && sub.GetItems(false).Find(i => i.HasTag("navterminal") && !i.NonInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) &&
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage) nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
{ {
shortcutNodes.Add( shortcutNodes.Add(
@@ -2097,7 +2105,10 @@ namespace Barotrauma
(n.UserData is Tuple<Order, string> orderWithOption && orderWithOption.Item1.Identifier == orderIdentifier)) && (n.UserData is Tuple<Order, string> orderWithOption && orderWithOption.Item1.Identifier == orderIdentifier)) &&
!orderPrefab.TargetAllCharacters && orderPrefab.Category != null) !orderPrefab.TargetAllCharacters && orderPrefab.Category != null)
{ {
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, orderPrefab, -1)); if (!orderPrefab.MustSetTarget || orderPrefab.GetMatchingItems(sub, true).Any())
{
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, orderPrefab, -1));
}
if (shortcutNodes.Count >= maxShorcutNodeCount) { break; } if (shortcutNodes.Count >= maxShorcutNodeCount) { break; }
} }
} }
@@ -2129,16 +2140,19 @@ namespace Barotrauma
private void CreateOrderNodes(OrderCategory orderCategory) private void CreateOrderNodes(OrderCategory orderCategory)
{ {
// TODO: Save the available orders for each category so we don't have to check them again during the same game?
var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.TargetAllCharacters); var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.TargetAllCharacters);
orders.RemoveAll(o => (o.ItemComponentType != null || o.ItemIdentifiers.Length > 0) && o.MustSetTarget && o.GetMatchingItems(true).None()); Order order;
bool disableNode;
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance, var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance,
GetCircumferencePointCount(orders.Count), GetFirstNodeAngle(orders.Count)); GetCircumferencePointCount(orders.Count), GetFirstNodeAngle(orders.Count));
for (int i = 0; i < orders.Count; i++) for (int i = 0; i < orders.Count; i++)
{ {
order = orders[i];
disableNode = !CanSomeoneHearCharacter() ||
(order.MustSetTarget && (order.ItemComponentType != null || order.ItemIdentifiers.Length > 0) && order.GetMatchingItems(true).None());
optionNodes.Add(new Tuple<GUIComponent, Keys>( optionNodes.Add(new Tuple<GUIComponent, Keys>(
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), orders[i], (i + 1) % 10), CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), order, (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false),
CanSomeoneHearCharacter() ? Keys.D0 + (i + 1) % 10 : Keys.None)); !disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None));
} }
} }
@@ -2150,7 +2164,7 @@ namespace Barotrauma
if (contextualOrders.None()) if (contextualOrders.None())
{ {
// Check if targeting an item or a hull // Check if targeting an item or a hull
if (itemContext != null) if (itemContext != null && !itemContext.NonInteractable)
{ {
foreach (Order p in Order.PrefabList) foreach (Order p in Order.PrefabList)
{ {
@@ -2165,11 +2179,11 @@ namespace Barotrauma
// If targeting a periscope connected to a turret, show the 'operateweapons' order // If targeting a periscope connected to a turret, show the 'operateweapons' order
var orderIdentifier = "operateweapons"; var orderIdentifier = "operateweapons";
var operateWeaponsPrefab = Order.GetPrefab(orderIdentifier); var operateWeaponsPrefab = Order.GetPrefab(orderIdentifier);
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Components.Any(c => c is Controller) && if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Components.Any(c => c is Controller))
(itemContext.GetConnectedComponents<Turret>().Any(c => operateWeaponsPrefab.ItemIdentifiers.Contains(c.Item.Prefab.Identifier)) ||
itemContext.GetConnectedComponents<Turret>(recursive: true).Any(c => operateWeaponsPrefab.ItemIdentifiers.Contains(c.Item.Prefab.Identifier))))
{ {
contextualOrders.Add(operateWeaponsPrefab); var turret = itemContext.GetConnectedComponents<Turret>().FirstOrDefault(c => operateWeaponsPrefab.ItemIdentifiers.Contains(c.Item.Prefab.Identifier)) ??
itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => operateWeaponsPrefab.ItemIdentifiers.Contains(c.Item.Prefab.Identifier));
if (turret != null) { contextualOrders.Add(new Order(operateWeaponsPrefab, turret.Item, turret, Character.Controlled)); }
} }
// If targeting a repairable item, show the 'repairsystems' order // If targeting a repairable item, show the 'repairsystems' order
@@ -2193,6 +2207,14 @@ namespace Barotrauma
{ {
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, null, Character.Controlled)); contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, null, Character.Controlled));
} }
// Remove the 'pumpwater' order if the target pump is auto-controlled (as it will immediately overwrite the work done by the bot)
orderIdentifier = "pumpwater";
if (contextualOrders.FirstOrDefault(o => o.Identifier.Equals(orderIdentifier)) is Order o &&
itemContext.Components.FirstOrDefault(c => c.GetType() == o.ItemComponentType) is Pump pump)
{
if (pump.IsAutoControlled) { contextualOrders.Remove(o); }
}
} }
else if(hullContext != null) else if(hullContext != null)
{ {
@@ -2218,11 +2240,12 @@ namespace Barotrauma
} }
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance, contextualOrders.Count, MathHelper.ToRadians(90f + 180f / contextualOrders.Count)); var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance, contextualOrders.Count, MathHelper.ToRadians(90f + 180f / contextualOrders.Count));
bool disableNode = !CanSomeoneHearCharacter();
for (int i = 0; i < contextualOrders.Count; i++) for (int i = 0; i < contextualOrders.Count; i++)
{ {
optionNodes.Add(new Tuple<GUIComponent, Keys>( optionNodes.Add(new Tuple<GUIComponent, Keys>(
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), contextualOrders[i], (i + 1) % 10), CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), contextualOrders[i], (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false),
CanSomeoneHearCharacter() ? Keys.D0 + (i + 1) % 10 : Keys.None)); !disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None));
} }
} }
@@ -2239,7 +2262,7 @@ namespace Barotrauma
item.GetConnectedComponents<Turret>(recursive: true).Any(c => operateWeaponsPrefab.ItemIdentifiers.Contains(c.Item.Prefab.Identifier))); item.GetConnectedComponents<Turret>(recursive: true).Any(c => operateWeaponsPrefab.ItemIdentifiers.Contains(c.Item.Prefab.Identifier)));
} }
private GUIButton CreateOrderNode(Point size, RectTransform parent, Point offset, Order order, int hotkey) private GUIButton CreateOrderNode(Point size, RectTransform parent, Point offset, Order order, int hotkey, bool disableNode = false, bool checkIfOrderCanBeHeard = true)
{ {
var node = new GUIButton( var node = new GUIButton(
new RectTransform(size, parent: parent, anchor: Anchor.Center), style: null) new RectTransform(size, parent: parent, anchor: Anchor.Center), style: null)
@@ -2249,16 +2272,17 @@ namespace Barotrauma
node.RectTransform.MoveOverTime(offset, CommandNodeAnimDuration); node.RectTransform.MoveOverTime(offset, CommandNodeAnimDuration);
var canSomeoneHearCharacter = CanSomeoneHearCharacter(); if (checkIfOrderCanBeHeard && !disableNode) { disableNode = !CanSomeoneHearCharacter(); }
var mustSetOptionOrTarget = order.HasOptions || (order.MustSetTarget && itemContext == null);
node.OnClicked = (button, userData) => node.OnClicked = (button, userData) =>
{ {
if (!canSomeoneHearCharacter || !CanIssueOrders) { return false; } if (disableNode || !CanIssueOrders) { return false; }
var o = userData as Order; var o = userData as Order;
if (o.MustManuallyAssign && characterContext == null) if (o.MustManuallyAssign && characterContext == null)
{ {
CreateAssignmentNodes(node); CreateAssignmentNodes(node);
} }
else if (o.HasOptions) else if (mustSetOptionOrTarget)
{ {
NavigateForward(button, userData); NavigateForward(button, userData);
} }
@@ -2270,11 +2294,11 @@ namespace Barotrauma
return true; return true;
}; };
var icon = CreateNodeIcon(node.RectTransform, order.SymbolSprite, order.Color, var icon = CreateNodeIcon(node.RectTransform, order.SymbolSprite, order.Color,
tooltip: order.HasOptions || characterContext != null ? order.Name : order.Name + tooltip: mustSetOptionOrTarget || characterContext != null ? order.Name : order.Name +
"\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") + "\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") +
"\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.rightmouse") : TextManager.Get("input.leftmouse")) + ": " + TextManager.Get("commandui.manualassigntooltip")); "\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.rightmouse") : TextManager.Get("input.leftmouse")) + ": " + TextManager.Get("commandui.manualassigntooltip"));
if (!canSomeoneHearCharacter) if (disableNode)
{ {
node.CanBeFocused = icon.CanBeFocused = false; node.CanBeFocused = icon.CanBeFocused = false;
CreateBlockIcon(node.RectTransform); CreateBlockIcon(node.RectTransform);
@@ -2288,11 +2312,7 @@ namespace Barotrauma
private void CreateOrderOptions(Order order) private void CreateOrderOptions(Order order)
{ {
// This is largely based on the CreateOrderTargetFrame() method Submarine submarine = GetTargetSubmarine();
Submarine submarine = Character.Controlled != null && Character.Controlled.TeamID == Character.TeamType.Team2 && Submarine.MainSubs.Length > 1 ?
Submarine.MainSubs[1] :
Submarine.MainSub;
var matchingItems = (itemContext == null && order.MustSetTarget) ? order.GetMatchingItems(submarine, true) : new List<Item>(); var matchingItems = (itemContext == null && order.MustSetTarget) ? order.GetMatchingItems(submarine, true) : new List<Item>();
//more than one target item -> create a minimap-like selection with a pic of the sub //more than one target item -> create a minimap-like selection with a pic of the sub
@@ -2332,7 +2352,7 @@ namespace Barotrauma
UserData = submarine UserData = submarine
}; };
List<GUIComponent> optionFrames = new List<GUIComponent>(); List<GUIComponent> optionElements = new List<GUIComponent>();
foreach (Item item in matchingItems) foreach (Item item in matchingItems)
{ {
var itemTargetFrame = targetFrame.Children.First().FindChild(item); var itemTargetFrame = targetFrame.Children.First().FindChild(item);
@@ -2353,52 +2373,87 @@ namespace Barotrauma
anchor = Anchor.TopRight; anchor = Anchor.TopRight;
} }
var optionFrame = new GUIFrame( GUIComponent optionElement;
new RectTransform( if (order.Options.Length > 1)
new Point((int)(250 * GUI.Scale), (int)((40 + order.Options.Length * 40) * GUI.Scale)),
parent: itemTargetFrame.RectTransform,
anchor: anchor),
style: "InnerFrame");
new GUIFrame(
new RectTransform(Vector2.One, optionFrame.RectTransform, anchor: Anchor.Center),
style: "OuterGlow",
color: Color.Black * 0.7f);
var optionContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.9f), optionFrame.RectTransform, anchor: Anchor.Center))
{ {
RelativeSpacing = 0.05f, optionElement = new GUIFrame(
Stretch = true new RectTransform(
}; new Point((int)(250 * GUI.Scale), (int)((40 + order.Options.Length * 40) * GUI.Scale)),
parent: itemTargetFrame.RectTransform,
anchor: anchor),
style: "InnerFrame");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), optionContainer.RectTransform), item != null ? item.Name : order.Name); new GUIFrame(
new RectTransform(Vector2.One, optionElement.RectTransform, anchor: Anchor.Center),
style: "OuterGlow",
color: Color.Black * 0.7f);
for (int i = 0; i < order.Options.Length; i++) var optionContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.9f), optionElement.RectTransform, anchor: Anchor.Center))
{ {
optionNodes.Add(new Tuple<GUIComponent, Keys>( RelativeSpacing = 0.05f,
new GUIButton( Stretch = true
new RectTransform(new Vector2(1.0f, 0.2f), optionContainer.RectTransform), };
text: order.OptionNames[i],
style: "GUITextBox") new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), optionContainer.RectTransform), item != null ? item.Name : order.Name);
{
UserData = new Tuple<Order, string>( for (int i = 0; i < order.Options.Length; i++)
item == null ? order : new Order(order, item, item.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType)), {
order.Options[i]), optionNodes.Add(new Tuple<GUIComponent, Keys>(
Font = GUI.SmallFont, new GUIButton(
OnClicked = (_, userData) => new RectTransform(new Vector2(1.0f, 0.2f), optionContainer.RectTransform),
text: order.GetOptionName(i),
style: "GUITextBox")
{ {
if (!CanIssueOrders) { return false; } UserData = new Tuple<Order, string>(
var o = userData as Tuple<Order, string>; item == null ? order : new Order(order, item, item.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType)),
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o.Item1), o.Item1, o.Item2, Character.Controlled); order.Options[i]),
DisableCommandUI(); Font = GUI.SmallFont,
return true; OnClicked = (_, userData) =>
} {
}, if (!CanIssueOrders) { return false; }
Keys.None)); var o = userData as Tuple<Order, string>;
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o.Item1), o.Item1, o.Item2, Character.Controlled);
DisableCommandUI();
return true;
}
},
Keys.None));
}
} }
optionFrames.Add(optionFrame); else
{
var userData = new Tuple<Order, string>(item == null ? order : new Order(order, item, item.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType)), "");
optionElement = new GUIButton(
new RectTransform(
new Point((int)(50 * GUI.Scale)),
parent: itemTargetFrame.RectTransform,
anchor: anchor),
style: null)
{
UserData = userData,
Font = GUI.SmallFont,
ToolTip = item?.Name ?? order.Name,
OnClicked = (_, userData) =>
{
if (!CanIssueOrders) { return false; }
var o = userData as Tuple<Order, string>;
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o.Item1), o.Item1, o.Item2, Character.Controlled);
DisableCommandUI();
return true;
}
};
Sprite icon = null;
order.MinimapIcons?.TryGetValue(item.Prefab.Identifier, out icon);
var colorMultiplier = characters.Any(c => c.CurrentOrder != null &&
c.CurrentOrder.Identifier == userData.Item1.Identifier &&
c.CurrentOrder.TargetEntity == userData.Item1.TargetEntity) ? 0.5f : 1f;
CreateNodeIcon(optionElement.RectTransform, icon ?? order.SymbolSprite, order.Color * colorMultiplier);
optionNodes.Add(new Tuple<GUIComponent, Keys>(optionElement, Keys.None));
}
optionElements.Add(optionElement);
} }
GUI.PreventElementOverlap(optionFrames, clampArea: new Rectangle(10, 10, GameMain.GraphicsWidth - 20, GameMain.GraphicsHeight - 20)); GUI.PreventElementOverlap(optionElements, clampArea: new Rectangle(10, 10, GameMain.GraphicsWidth - 20, GameMain.GraphicsHeight - 20));
var shadow = new GUIFrame( var shadow = new GUIFrame(
new RectTransform(targetFrame.Rect.Size + new Point((int)(200 * GUI.Scale)), targetFrame.RectTransform, anchor: Anchor.Center), new RectTransform(targetFrame.Rect.Size + new Point((int)(200 * GUI.Scale)), targetFrame.RectTransform, anchor: Anchor.Center),
@@ -2420,7 +2475,7 @@ namespace Barotrauma
for (int i = 0; i < order.Options.Length; i++) for (int i = 0; i < order.Options.Length; i++)
{ {
optionNodes.Add(new Tuple<GUIComponent, Keys>( optionNodes.Add(new Tuple<GUIComponent, Keys>(
CreateOrderOptionNode(nodeSize, commandFrame.RectTransform, offsets[offsetIndex++].ToPoint(), o, order.Options[i], order.OptionNames[i], (i + 1) % 10), CreateOrderOptionNode(nodeSize, commandFrame.RectTransform, offsets[offsetIndex++].ToPoint(), o, order.Options[i], order.GetOptionName(i), (i + 1) % 10),
Keys.D0 + (i + 1) % 10)); Keys.D0 + (i + 1) % 10));
} }
} }
@@ -2428,12 +2483,7 @@ namespace Barotrauma
private GUIButton CreateOrderOptionNode(Point size, RectTransform parent, Point offset, Order order, string option, string optionName, int hotkey) private GUIButton CreateOrderOptionNode(Point size, RectTransform parent, Point offset, Order order, string option, string optionName, int hotkey)
{ {
var node = new GUIButton( var node = new GUIButton(new RectTransform(size, parent: parent, anchor: Anchor.Center), style: null)
new RectTransform(size, parent: parent, anchor: Anchor.Center)
{
AbsoluteOffset = offset
},
style: null)
{ {
UserData = new Tuple<Order, string>(order, option), UserData = new Tuple<Order, string>(order, option),
OnClicked = (_, userData) => OnClicked = (_, userData) =>
@@ -2445,6 +2495,8 @@ namespace Barotrauma
return true; return true;
} }
}; };
node.RectTransform.MoveOverTime(offset, CommandNodeAnimDuration);
GUIImage icon = null; GUIImage icon = null;
if (order.Prefab.OptionSprites.TryGetValue(option, out Sprite sprite)) if (order.Prefab.OptionSprites.TryGetValue(option, out Sprite sprite))
{ {
@@ -2472,40 +2524,47 @@ namespace Barotrauma
new Tuple<Order, string>(node.UserData as Order, null) : new Tuple<Order, string>(node.UserData as Order, null) :
node.UserData as Tuple<Order, string>; node.UserData as Tuple<Order, string>;
var characters = GetCharactersForManualAssignment(order.Item1); var characters = GetCharactersForManualAssignment(order.Item1);
if (characters.Count < 1) { return; } if (characters.None()) { return; }
if (!(optionNodes.Find(n => n.Item1 == node) is Tuple<GUIComponent, Keys> optionNode) || !optionNodes.Remove(optionNode)) if (!(optionNodes.Find(n => n.Item1 == node) is Tuple<GUIComponent, Keys> optionNode) || !optionNodes.Remove(optionNode))
{ {
shortcutNodes.Remove(node); shortcutNodes.Remove(node);
}; };
RemoveOptionNodes(); RemoveOptionNodes();
if (returnNode != null) if (returnNode != null)
{ {
returnNode.Children.ForEach(child => child.Visible = false); returnNode.Children.ForEach(child => child.Visible = false);
returnNode.Visible = false; returnNode.Visible = false;
historyNodes.Push(returnNode); historyNodes.Push(returnNode);
} }
SetReturnNode(centerNode, new Point( SetReturnNode(centerNode, new Point(0, (int)(returnNodeDistanceModifier * nodeDistance)));
(int)(node.RectTransform.AbsoluteOffset.X * -returnNodeDistanceModifier),
(int)(node.RectTransform.AbsoluteOffset.Y * -returnNodeDistanceModifier)));
if (targetFrame == null || !targetFrame.Visible) if (targetFrame == null || !targetFrame.Visible)
{ {
SetCenterNode(node as GUIButton); SetCenterNode(node as GUIButton);
} }
else else
{ {
var clickedOptionNode = new GUIButton( if (string.IsNullOrEmpty(order.Item2))
{
SetCenterNode(node as GUIButton, resetAnchor: true);
}
else
{
var clickedOptionNode = new GUIButton(
new RectTransform(centerNodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center), new RectTransform(centerNodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center),
style: null) style: null)
{ {
UserData = node.UserData UserData = node.UserData
}; };
if (order.Item1.Prefab.OptionSprites.TryGetValue(order.Item2, out Sprite sprite)) if (order.Item1.Prefab.OptionSprites.TryGetValue(order.Item2, out Sprite sprite))
{ {
CreateNodeIcon(clickedOptionNode.RectTransform, sprite, order.Item1.Color, tooltip: order.Item2); CreateNodeIcon(clickedOptionNode.RectTransform, sprite, order.Item1.Color, tooltip: order.Item2);
}
SetCenterNode(clickedOptionNode);
node = null;
} }
SetCenterNode(clickedOptionNode);
node = null;
targetFrame.Visible = false; targetFrame.Visible = false;
} }
if (shortcutCenterNode != null) if (shortcutCenterNode != null)
@@ -2514,21 +2573,36 @@ namespace Barotrauma
shortcutCenterNode = null; shortcutCenterNode = null;
} }
var needToExpand = characters.Count > assignmentNodeMaxCount + 1; var characterCount = characters.Count;
var nodeCount = needToExpand ? assignmentNodeMaxCount + 1 : characters.Count; int hotkey = 1;
var extraNodeDistance = Math.Max(nodeCount - 6, 0) * (GUI.Scale * 30); Vector2[] offsets;
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance + extraNodeDistance, var needToExpand = characterCount > 10;
GetCircumferencePointCount(nodeCount), if (characterCount > 5)
GetFirstNodeAngle(nodeCount));
var i = 0;
var assignmentNodeCount = (needToExpand ? nodeCount - 1 : nodeCount);
for (; i < assignmentNodeCount; i++)
{ {
CreateAssignmentNode(order, characters[i], offsets[i].ToPoint(), (i + 1) % 10); // First ring
var charactersOnFirstRing = needToExpand ? 5 : (int)Math.Floor(characterCount / 2f);
offsets = GetAssignmentNodeOffsets(charactersOnFirstRing);
for (int i = 0; i < charactersOnFirstRing; i++)
{
CreateAssignmentNode(order, characters[i], offsets[i].ToPoint(), hotkey++ % 10);
}
// Second ring
var charactersOnSecondRing = needToExpand ? 4 : characterCount - charactersOnFirstRing;
offsets = GetAssignmentNodeOffsets(needToExpand ? 5 : charactersOnSecondRing, false);
for (int i = 0; i < charactersOnSecondRing; i++)
{
CreateAssignmentNode(order, characters[charactersOnFirstRing + i], offsets[i].ToPoint(), hotkey++ % 10);
}
}
else
{
offsets = GetAssignmentNodeOffsets(characterCount);
for (int i = 0; i < characterCount; i++)
{
CreateAssignmentNode(order, characters[i], offsets[i].ToPoint(), hotkey++ % 10);
}
} }
int hotkey;
if (!needToExpand) if (!needToExpand)
{ {
hotkey = optionNodes.Count + 1; hotkey = optionNodes.Count + 1;
@@ -2539,12 +2613,14 @@ namespace Barotrauma
} }
extraOptionCharacters.Clear(); extraOptionCharacters.Clear();
extraOptionCharacters.AddRange(characters.GetRange(i, characters.Count - i)); // Sort expanded assignment nodes by characters' jobs and then by their names
extraOptionCharacters.AddRange(characters.GetRange(hotkey - 1, characterCount - (hotkey - 1))
.OrderBy(c => c?.Info?.Job?.Name).ThenBy(c => c?.Info?.DisplayName));
expandNode = new GUIButton( expandNode = new GUIButton(
new RectTransform(nodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center) new RectTransform(assignmentNodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center)
{ {
AbsoluteOffset = offsets[i].ToPoint() AbsoluteOffset = offsets.Last().ToPoint()
}, },
style: null) style: null)
{ {
@@ -2560,6 +2636,22 @@ namespace Barotrauma
returnNodeHotkey = Keys.D0 + hotkey % 10; returnNodeHotkey = Keys.D0 + hotkey % 10;
} }
private Vector2[] GetAssignmentNodeOffsets(int characters, bool firstRing = true)
{
var nodeDistance = 1.8f * this.nodeDistance;
var nodePositionsOnEachSide = characters % 2 > 0 ? 7 : 6;
var nodeCountForCalculation = 2 * nodePositionsOnEachSide + 2;
var offsets = MathUtils.GetPointsOnCircumference(firstRing ? new Vector2(0f, 0.5f * nodeDistance) : Vector2.Zero,
nodeDistance, nodeCountForCalculation, MathHelper.ToRadians(180f + 360f / nodeCountForCalculation));
var emptySpacesPerSide = (nodePositionsOnEachSide - characters) / 2;
var offsetsInUse = new Vector2[nodePositionsOnEachSide - 2 * emptySpacesPerSide];
for (int i = 0; i < offsetsInUse.Length; i++)
{
offsetsInUse[i] = offsets[i + emptySpacesPerSide];
}
return offsetsInUse;
}
private bool ExpandAssignmentNodes(GUIButton node, object userData) private bool ExpandAssignmentNodes(GUIButton node, object userData)
{ {
node.OnClicked = (button, _) => node.OnClicked = (button, _) =>
@@ -2569,60 +2661,86 @@ namespace Barotrauma
return true; return true;
}; };
var order = userData as Tuple<Order, string>; var availableNodePositions = 20;
// TODO: The value 100 should be determined by how large the inner circle is var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, 2.7f * this.nodeDistance, availableNodePositions,
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, (nodeDistance + GUI.Scale * 100) * 1.55f, firstAngle: MathHelper.ToRadians(-90f - ((extraOptionCharacters.Count - 1) * 0.5f * (360f / availableNodePositions))));
GetCircumferencePointCount(extraOptionCharacters.Count),
GetFirstNodeAngle(extraOptionCharacters.Count));
for (int i = 0; i < extraOptionCharacters.Count; i++) for (int i = 0; i < extraOptionCharacters.Count; i++)
{ {
CreateAssignmentNode(order, extraOptionCharacters[i], offsets[i].ToPoint(), -1); CreateAssignmentNode(userData as Tuple<Order, string>, extraOptionCharacters[i], offsets[i].ToPoint(), -1, nameLabelScale: 1.15f);
} }
return true; return true;
} }
private void CreateAssignmentNode(Tuple<Order, string> order, Character character, Point offset, int hotkey) private void CreateAssignmentNode(Tuple<Order, string> order, Character character, Point offset, int hotkey, float nameLabelScale = 1f)
{ {
// Button // Button
var node = new GUIButton( var node = new GUIButton(
new RectTransform(nodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center), new RectTransform(assignmentNodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center),
style: null) style: null)
{ {
OnClicked = (button, userData) => UserData = character,
OnClicked = (_, userData) =>
{ {
if (!CanIssueOrders) { return false; } if (!CanIssueOrders) { return false; }
SetCharacterOrder(character, order.Item1, order.Item2, Character.Controlled); SetCharacterOrder(userData as Character, order.Item1, order.Item2, Character.Controlled);
DisableCommandUI(); DisableCommandUI();
return true; return true;
} }
}; };
node.RectTransform.MoveOverTime(offset, CommandNodeAnimDuration); node.RectTransform.MoveOverTime(offset, CommandNodeAnimDuration);
// Container
var icon = new GUIImage(
new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center),
"CommandNodeContainer",
scaleToFit: true)
{
Color = character.Info.Job.Prefab.UIColor * nodeColorMultiplier,
HoverColor = character.Info.Job.Prefab.UIColor,
PressedColor = character.Info.Job.Prefab.UIColor,
SelectedColor = character.Info.Job.Prefab.UIColor,
UserData = "colorsource"
};
// Character icon
new GUICustomComponent(
new RectTransform(Vector2.One, node.RectTransform),
(spriteBatch, _) =>
{
character.Info.DrawJobIcon(spriteBatch,
new Rectangle((int)(node.Rect.X + node.Rect.Width * 0.5f), (int)(node.Rect.Y + node.Rect.Height * 0.1f), (int)(node.Rect.Width * 0.6f), (int)(node.Rect.Height * 0.8f)));
character.Info.DrawIcon(spriteBatch, new Vector2(node.Rect.X + node.Rect.Width * 0.35f, node.Center.Y), node.Rect.Size.ToVector2() * 0.7f);
}) var jobColor = character.Info?.Job?.Prefab?.UIColor ?? Color.White;
// Order icon
GUIImage orderIcon;
if (character.CurrentOrder != null && !character.CurrentOrder.Identifier.Equals("dismissed"))
{ {
ToolTip = character.Info.DisplayName + " (" + character.Info.Job.Name + ")" orderIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), character.CurrentOrder.SymbolSprite, scaleToFit: true);
var tooltip = character.CurrentOrder.Name;
if (!string.IsNullOrWhiteSpace(character.CurrentOrderOption)) { tooltip += " (" + character.CurrentOrder.GetOptionName(character.CurrentOrderOption) + ")"; };
orderIcon.ToolTip = tooltip;
}
else
{
orderIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), "CommandIdleNode", scaleToFit: true);
}
orderIcon.Color = jobColor * nodeColorMultiplier;
orderIcon.HoverColor = jobColor;
orderIcon.PressedColor = jobColor;
orderIcon.SelectedColor = jobColor;
orderIcon.UserData = "colorsource";
// Name label
var width = (int)(nameLabelScale * nodeSize.X);
var font = GUI.SmallFont;
var nameLabel = new GUITextBlock(
new RectTransform(new Point(width, 0), parent: node.RectTransform, anchor: Anchor.TopCenter, pivot: Pivot.BottomCenter)
{
RelativeOffset = new Vector2(0f, -0.25f)
},
ToolBox.LimitString(character.Info?.DisplayName, font, width), textColor: jobColor * nodeColorMultiplier, font: font, textAlignment: Alignment.Center, style: null)
{
CanBeFocused = false,
ForceUpperCase = true,
HoverTextColor = jobColor
}; };
if (character.Info?.Job?.Prefab?.IconSmall is Sprite smallJobIcon)
{
// Job icon
new GUIImage(
new RectTransform(new Vector2(0.4f), node.RectTransform, anchor: Anchor.TopCenter, pivot: Pivot.Center)
{
RelativeOffset = new Vector2(0.0f, -((orderIcon.RectTransform.RelativeSize.Y - 1) / 2))
},
smallJobIcon, scaleToFit: true)
{
CanBeFocused = false,
Color = jobColor,
HoverColor = jobColor
};
}
#if DEBUG #if DEBUG
bool canHear = true; bool canHear = true;
#else #else
@@ -2630,7 +2748,7 @@ namespace Barotrauma
#endif #endif
if (!canHear) if (!canHear)
{ {
node.CanBeFocused = icon.CanBeFocused = false; node.CanBeFocused = orderIcon.CanBeFocused = false;
CreateBlockIcon(node.RectTransform); CreateBlockIcon(node.RectTransform);
} }
if (hotkey >= 0) if (hotkey >= 0)
@@ -2705,8 +2823,9 @@ namespace Barotrauma
private void CreateBlockIcon(RectTransform parent) private void CreateBlockIcon(RectTransform parent)
{ {
new GUIImage(new RectTransform(Vector2.One, parent, anchor: Anchor.Center), cancelIcon, scaleToFit: true) new GUIImage(new RectTransform(new Vector2(0.9f), parent, anchor: Anchor.Center), cancelIcon, scaleToFit: true)
{ {
CanBeFocused = false,
Color = GUI.Style.Red * nodeColorMultiplier, Color = GUI.Style.Red * nodeColorMultiplier,
HoverColor = GUI.Style.Red HoverColor = GUI.Style.Red
}; };
@@ -2749,6 +2868,7 @@ namespace Barotrauma
private bool TryGetBreachedHullAtHoveredWall(out Hull breachedHull) private bool TryGetBreachedHullAtHoveredWall(out Hull breachedHull)
{ {
breachedHull = null; breachedHull = null;
// Based on the IsValidTarget() method of AIObjectiveFixLeaks class
List<Gap> leaks = Gap.GapList.FindAll(g => List<Gap> leaks = Gap.GapList.FindAll(g =>
g != null && g.ConnectedWall != null && g.ConnectedDoor == null && g.Open > 0 && g.linkedTo.Any(l => l != null) && g != null && g.ConnectedWall != null && g.ConnectedDoor == null && g.Open > 0 && g.linkedTo.Any(l => l != null) &&
g.Submarine != null && (Character.Controlled != null && g.Submarine.TeamID == Character.Controlled.TeamID && g.Submarine.Info.IsPlayer)); g.Submarine != null && (Character.Controlled != null && g.Submarine.TeamID == Character.Controlled.TeamID && g.Submarine.Info.IsPlayer));
@@ -2765,6 +2885,25 @@ namespace Barotrauma
return false; return false;
} }
private Submarine GetTargetSubmarine()
{
var sub = Submarine.MainSub;
if (Character.Controlled != null)
{
// Pick the second main sub when we have two teams (in combat mission)
if (Character.Controlled.TeamID == Character.TeamType.Team2 && Submarine.MainSubs.Length > 1)
{
sub = Submarine.MainSubs[1];
}
// Target current submarine (likely a shuttle) when undocked from the main sub
if (Character.Controlled.Submarine is Submarine currentSub && currentSub != sub && currentSub.TeamID == Character.Controlled.TeamID && !currentSub.IsConnectedTo(sub))
{
sub = currentSub;
}
}
return sub;
}
#region Crew Member Assignment Logic #region Crew Member Assignment Logic
private Character GetCharacterForQuickAssignment(Order order) private Character GetCharacterForQuickAssignment(Order order)
@@ -2776,7 +2915,7 @@ namespace Barotrauma
{ {
return operatingCharacter; return operatingCharacter;
} }
return GetCharactersSortedForOrder(order, false).FirstOrDefault(); return GetCharactersSortedForOrder(order, false).FirstOrDefault() ?? Character.Controlled;
} }
private List<Character> GetCharactersForManualAssignment(Order order) private List<Character> GetCharactersForManualAssignment(Order order)
@@ -2784,6 +2923,11 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (Character.Controlled == null) { return new List<Character>(); } if (Character.Controlled == null) { return new List<Character>(); }
#endif #endif
if (order.Identifier == dismissedOrderPrefab.Identifier)
{
return characters.FindAll(c => c.CurrentOrder != null && c.CurrentOrder.Identifier != dismissedOrderPrefab.Identifier)
.OrderBy(c => c.Info.DisplayName).ToList();
}
return GetCharactersSortedForOrder(order, order.Identifier != "follow").ToList(); return GetCharactersSortedForOrder(order, order.Identifier != "follow").ToList();
} }
@@ -2822,10 +2966,7 @@ namespace Barotrauma
if (canIssueOrders) if (canIssueOrders)
{ {
//report buttons are hidden when accessing another character's inventory ReportButtonFrame.Visible = !Character.Controlled.ShouldLockHud();
ReportButtonFrame.Visible = !Character.Controlled.ShouldLockHud() &&
(Character.Controlled?.SelectedCharacter?.Inventory == null ||
!Character.Controlled.SelectedCharacter.CanInventoryBeAccessed);
if (!ReportButtonFrame.Visible) { return; } if (!ReportButtonFrame.Visible) { return; }
var reportButtonParent = ChatBox ?? GameMain.Client?.ChatBox; var reportButtonParent = ChatBox ?? GameMain.Client?.ChatBox;
@@ -25,7 +25,7 @@ namespace Barotrauma
: base(preset, param) : base(preset, param)
{ {
int buttonHeight = (int)(HUDLayoutSettings.ButtonAreaTop.Height * 0.7f); int buttonHeight = (int)(HUDLayoutSettings.ButtonAreaTop.Height * 0.7f);
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(HUDLayoutSettings.ButtonAreaTop.Right - 200, HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonHeight / 2, 200, buttonHeight), GUICanvas.Instance), endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(HUDLayoutSettings.ButtonAreaTop.Right - GUI.IntScale(200), HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonHeight / 2, GUI.IntScale(200), buttonHeight), GUICanvas.Instance),
TextManager.Get("EndRound"), textAlignment: Alignment.Center) TextManager.Get("EndRound"), textAlignment: Alignment.Center)
{ {
Font = GUI.SmallFont, Font = GUI.SmallFont,
@@ -195,7 +195,7 @@ namespace Barotrauma.Tutorials
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_security, "operateweapons", highlightColor, new Vector2(5, 5)); // GameMain.GameSession.CrewManager.HighlightOrderButton(captain_security, "operateweapons", highlightColor, new Vector2(5, 5));
HighlightOrderOption("fireatwill"); HighlightOrderOption("fireatwill");
} }
while (!HasOrder(captain_security, "operateweapons", "fireatwill")); while (!HasOrder(captain_security, "operateweapons"));
RemoveCompletedObjective(segments[2]); RemoveCompletedObjective(segments[2]);
yield return new WaitForSeconds(4f, false); yield return new WaitForSeconds(4f, false);
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Command)); TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Command));
@@ -1,6 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Xml.Linq; using System.Xml.Linq;
using System.Linq; using System.Linq;
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
@@ -315,6 +315,7 @@ namespace Barotrauma.Tutorials
yield return new WaitForSeconds(2f, false); yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.Deselect)); // Kill hammerhead TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.Deselect)); // Kill hammerhead
officer_hammerhead = SpawnMonster("hammerhead", officer_hammerheadSpawnPos); officer_hammerhead = SpawnMonster("hammerhead", officer_hammerheadSpawnPos);
((EnemyAIController)officer_hammerhead.AIController).StayInsideLevel = false;
officer_hammerhead.AIController.SelectTarget(officer.AiTarget); officer_hammerhead.AIController.SelectTarget(officer.AiTarget);
SetHighlight(officer_coilgunPeriscope, true); SetHighlight(officer_coilgunPeriscope, true);
float originalDistance = Vector2.Distance(officer_coilgunPeriscope.WorldPosition, officer_hammerheadSpawnPos); float originalDistance = Vector2.Distance(officer_coilgunPeriscope.WorldPosition, officer_hammerheadSpawnPos);
@@ -329,7 +330,13 @@ namespace Barotrauma.Tutorials
if (distance > originalDistance) if (distance > originalDistance)
{ {
// Ensure that the Hammerhead targets the player // Ensure that the Hammerhead targets the player
officer.AiTarget.SoundRange = float.MaxValue;
officer.AiTarget.SightRange = float.MaxValue;
officer_hammerhead.AIController.SelectTarget(officer.AiTarget); officer_hammerhead.AIController.SelectTarget(officer.AiTarget);
if ((officer_hammerhead.AIController as EnemyAIController)?.SelectedTargetingParams != null)
{
((EnemyAIController)officer_hammerhead.AIController).SelectedTargetingParams.ReactDistance = 5000.0f;
}
/*var ai = officer_hammerhead.AIController as EnemyAIController; /*var ai = officer_hammerhead.AIController as EnemyAIController;
ai.sight = 2.0f;*/ ai.sight = 2.0f;*/
} }
@@ -2,7 +2,7 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -515,7 +515,7 @@ namespace Barotrauma.Tutorials
height += (int)GUI.Font.MeasureString(title).Y + (int)(150 * GUI.Scale); height += (int)GUI.Font.MeasureString(title).Y + (int)(150 * GUI.Scale);
} }
var background = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center), style: null, Color.Black * 0.5f); var background = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center), style: "GUIBackgroundBlocker");
var infoBlock = new GUIFrame(new RectTransform(new Point(width, height), background.RectTransform, anchor)); var infoBlock = new GUIFrame(new RectTransform(new Point(width, height), background.RectTransform, anchor));
infoBlock.Flash(GUI.Style.Green); infoBlock.Flash(GUI.Style.Green);
@@ -32,7 +32,9 @@ namespace Barotrauma
SoundPlayer.OverrideMusicDuration = 18.0f; SoundPlayer.OverrideMusicDuration = 18.0f;
} }
GUIFrame frame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker") GUIFrame background = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: "GUIBackgroundBlocker");
GUIFrame frame = new GUIFrame(new RectTransform(Vector2.One, background.RectTransform, Anchor.Center), style: null)
{ {
UserData = "roundsummary" UserData = "roundsummary"
}; };
@@ -44,6 +44,8 @@ namespace Barotrauma
} }
} }
private const int inventoryHotkeyCount = 10;
public void SetDefaultBindings(XDocument doc = null, bool legacy = false) public void SetDefaultBindings(XDocument doc = null, bool legacy = false)
{ {
keyMapping = new KeyOrMouse[Enum.GetNames(typeof(InputType)).Length]; keyMapping = new KeyOrMouse[Enum.GetNames(typeof(InputType)).Length];
@@ -100,6 +102,13 @@ namespace Barotrauma
keyMapping[(int)InputType.Select] = new KeyOrMouse(MouseButton.PrimaryMouse); keyMapping[(int)InputType.Select] = new KeyOrMouse(MouseButton.PrimaryMouse);
// shoot and deselect are handled in CheckBindings() so that we don't override the legacy settings. // shoot and deselect are handled in CheckBindings() so that we don't override the legacy settings.
} }
inventoryKeyMapping = new KeyOrMouse[inventoryHotkeyCount];
for (int i = 0; i < inventoryKeyMapping.Length; i++)
{
inventoryKeyMapping[i] = new KeyOrMouse(Keys.D0 + (i + 1) % 10);
}
if (doc != null) if (doc != null)
{ {
LoadControls(doc); LoadControls(doc);
@@ -179,6 +188,26 @@ namespace Barotrauma
} }
} }
private void LoadInventoryKeybinds(XElement element)
{
for (int i = 0; i < inventoryKeyMapping.Length; i++)
{
XAttribute attribute = element.Attributes().ElementAt(i);
if (int.TryParse(attribute.Value.ToString(), out int mouseButtonInt))
{
inventoryKeyMapping[i] = new KeyOrMouse((MouseButton)mouseButtonInt);
}
else if (Enum.TryParse(attribute.Value.ToString(), true, out MouseButton mouseButton))
{
inventoryKeyMapping[i] = new KeyOrMouse(mouseButton);
}
else if (Enum.TryParse(attribute.Value.ToString(), true, out Keys key))
{
inventoryKeyMapping[i] = new KeyOrMouse(key);
}
}
}
private void LoadControls(XDocument doc) private void LoadControls(XDocument doc)
{ {
XElement keyMapping = doc.Root.Element("keymapping"); XElement keyMapping = doc.Root.Element("keymapping");
@@ -186,6 +215,12 @@ namespace Barotrauma
{ {
LoadKeyBinds(keyMapping); LoadKeyBinds(keyMapping);
} }
XElement inventoryKeyMapping = doc.Root.Element("inventorykeymapping");
if (inventoryKeyMapping != null)
{
LoadInventoryKeybinds(inventoryKeyMapping);
}
} }
public KeyOrMouse KeyBind(InputType inputType) public KeyOrMouse KeyBind(InputType inputType)
@@ -195,23 +230,12 @@ namespace Barotrauma
public string KeyBindText(InputType inputType) public string KeyBindText(InputType inputType)
{ {
KeyOrMouse bind = keyMapping[(int)inputType]; return keyMapping[(int)inputType].Name;
}
if (bind.MouseButton != MouseButton.None) public KeyOrMouse InventoryKeyBind(int index)
{ {
switch (bind.MouseButton) return inventoryKeyMapping[index];
{
case MouseButton.PrimaryMouse:
return PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.rightmouse") : TextManager.Get("input.leftmouse");
case MouseButton.SecondaryMouse:
return PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse");
default:
return TextManager.Get("input." + bind.MouseButton.ToString().ToLowerInvariant());
}
}
return bind.ToString();
} }
private GUIListBox contentPackageList; private GUIListBox contentPackageList;
@@ -259,7 +283,8 @@ namespace Barotrauma
} }
else else
{ {
settingsFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: null, color: Color.Black * 0.5f); settingsFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null);
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, settingsFrame.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
var settingsFrameContent = new GUIFrame(new RectTransform(new Vector2(0.8f, 0.8f), settingsFrame.RectTransform, Anchor.Center)); var settingsFrameContent = new GUIFrame(new RectTransform(new Vector2(0.8f, 0.8f), settingsFrame.RectTransform, Anchor.Center));
settingsHolder = settingsFrameContent.RectTransform; settingsHolder = settingsFrameContent.RectTransform;
} }
@@ -292,6 +317,25 @@ namespace Barotrauma
ButtonEnabled = ContentPackage.List.Count(cp => cp.CorePackage) > 1 ButtonEnabled = ContentPackage.List.Count(cp => cp.CorePackage) > 1
}; };
var filterContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), leftPanel.RectTransform), isHorizontal: true)
{
Stretch = true
};
var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("serverlog.filter"), textAlignment: Alignment.CenterLeft, font: GUI.Font);
var searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform, Anchor.CenterRight), font: GUI.Font, createClearButton: true);
filterContainer.RectTransform.MinSize = searchBox.RectTransform.MinSize;
searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };
searchBox.OnTextChanged += (textBox, text) =>
{
foreach (GUIComponent child in contentPackageList.Content.Children)
{
if (!(child.UserData is ContentPackage cp)) { continue; }
child.Visible = string.IsNullOrEmpty(text) ? true : cp.Name.ToLower().Contains(text.ToLower());
}
return true;
};
contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.70f), leftPanel.RectTransform)) contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.70f), leftPanel.RectTransform))
{ {
OnSelected = (gc, obj) => false, OnSelected = (gc, obj) => false,
@@ -551,6 +595,37 @@ namespace Barotrauma
}; };
GUITickBox textureCompressionTickBox = new GUITickBox(new RectTransform(tickBoxScale, leftColumn.RectTransform), TextManager.Get("EnableTextureCompression"))
{
ToolTip = TextManager.Get("EnableTextureCompressionToolTip"),
OnSelected = (GUITickBox box) =>
{
if (box.Selected == TextureCompressionEnabled) { return true; }
bool prevTextureCompressionEnabled = TextureCompressionEnabled;
TextureCompressionEnabled = box.Selected;
var msgBox = new GUIMessageBox(
TextManager.Get("RestartRequiredLabel"),
TextManager.Get("RestartRequiredGeneric"),
buttons: new string[] { TextManager.Get("OK"), TextManager.Get("Cancel") });
msgBox.Buttons[0].OnClicked += (btn, userdata) =>
{
ApplySettings();
GameMain.Instance.Exit();
return true;
}; msgBox.Buttons[1].OnClicked += (btn, userdata) =>
{
TextureCompressionEnabled = prevTextureCompressionEnabled;
box.Selected = prevTextureCompressionEnabled;
msgBox.Close();
return true;
};
return true;
},
Selected = TextureCompressionEnabled
};
GUITickBox pauseOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale, leftColumn.RectTransform), GUITickBox pauseOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale, leftColumn.RectTransform),
TextManager.Get("PauseOnFocusLost")) TextManager.Get("PauseOnFocusLost"))
{ {
@@ -612,7 +687,8 @@ namespace Barotrauma
{ {
ChangeSliderText(scrollBar, barScroll); ChangeSliderText(scrollBar, barScroll);
LightMapScale = MathHelper.Lerp(0.2f, 1.0f, barScroll); LightMapScale = MathHelper.Lerp(0.2f, 1.0f, barScroll);
UnsavedSettings = true; return true; UnsavedSettings = true;
return true;
}, },
Step = 0.25f Step = 0.25f
}; };
@@ -1113,6 +1189,24 @@ namespace Barotrauma
keyBox.SelectedColor = Color.Gold * 0.3f; keyBox.SelectedColor = Color.Gold * 0.3f;
} }
for (int i = 0; i < inventoryHotkeyCount; i++)
{
var inputContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.06f), ((i + 1) <= inventoryHotkeyCount / 2 ? inputColumnLeft : inputColumnRight).RectTransform))
{ Stretch = true, IsHorizontal = true, RelativeSpacing = 0.01f, Color = new Color(12, 14, 15, 215) };
var inputName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), inputContainer.RectTransform, Anchor.TopLeft) { MinSize = new Point(100, 0) },
TextManager.GetWithVariable("inventoryslotkeybind", "[slotnumber]", (i + 1).ToString()), font: GUI.SmallFont)
{ ForceUpperCase = true };
inputNameBlocks.Add(inputName);
var keyBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), inputContainer.RectTransform),
text: inventoryKeyMapping[i].Name, font: GUI.SmallFont, style: "GUITextBoxNoIcon")
{
UserData = i
};
keyBox.Text = ToolBox.LimitString(keyBox.Text, keyBox.Font, (int)(keyBox.Rect.Width - keyBox.Padding.X - keyBox.Padding.Z));
keyBox.OnSelected += InventoryKeyBoxSelected;
keyBox.SelectedColor = Color.Gold * 0.3f;
}
GUITextBlock.AutoScaleAndNormalize(inputNameBlocks); GUITextBlock.AutoScaleAndNormalize(inputNameBlocks);
var resetControlsArea = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.07f), controlsLayoutGroup.RectTransform), style: null); var resetControlsArea = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.07f), controlsLayoutGroup.RectTransform), style: null);
@@ -1380,7 +1474,13 @@ namespace Barotrauma
private void KeyBoxSelected(GUITextBox textBox, Keys key) private void KeyBoxSelected(GUITextBox textBox, Keys key)
{ {
textBox.Text = ""; textBox.Text = "";
CoroutineManager.StartCoroutine(WaitForKeyPress(textBox)); CoroutineManager.StartCoroutine(WaitForKeyPress(textBox, keyMapping));
}
private void InventoryKeyBoxSelected(GUITextBox textBox, Keys key)
{
textBox.Text = "";
CoroutineManager.StartCoroutine(WaitForKeyPress(textBox, inventoryKeyMapping));
} }
private void ResetControls(bool legacy) private void ResetControls(bool legacy)
@@ -1476,7 +1576,7 @@ namespace Barotrauma
return true; return true;
} }
private IEnumerable<object> WaitForKeyPress(GUITextBox keyBox) private IEnumerable<object> WaitForKeyPress(GUITextBox keyBox, KeyOrMouse[] keyArray)
{ {
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
@@ -1500,42 +1600,43 @@ namespace Barotrauma
if (PlayerInput.LeftButtonClicked()) if (PlayerInput.LeftButtonClicked())
{ {
keyMapping[keyIndex] = new KeyOrMouse(MouseButton.LeftMouse); keyArray[keyIndex] = new KeyOrMouse(MouseButton.LeftMouse);
} }
else if (PlayerInput.RightButtonClicked()) else if (PlayerInput.RightButtonClicked())
{ {
keyMapping[keyIndex] = new KeyOrMouse(MouseButton.RightMouse); keyArray[keyIndex] = new KeyOrMouse(MouseButton.RightMouse);
} }
else if (PlayerInput.MidButtonClicked()) else if (PlayerInput.MidButtonClicked())
{ {
keyMapping[keyIndex] = new KeyOrMouse(MouseButton.MiddleMouse); keyArray[keyIndex] = new KeyOrMouse(MouseButton.MiddleMouse);
} }
else if (PlayerInput.Mouse4ButtonClicked()) else if (PlayerInput.Mouse4ButtonClicked())
{ {
keyMapping[keyIndex] = new KeyOrMouse(MouseButton.MouseButton4); keyArray[keyIndex] = new KeyOrMouse(MouseButton.MouseButton4);
} }
else if (PlayerInput.Mouse5ButtonClicked()) else if (PlayerInput.Mouse5ButtonClicked())
{ {
keyMapping[keyIndex] = new KeyOrMouse(MouseButton.MouseButton5); keyArray[keyIndex] = new KeyOrMouse(MouseButton.MouseButton5);
} }
else if (PlayerInput.MouseWheelUpClicked()) else if (PlayerInput.MouseWheelUpClicked())
{ {
keyMapping[keyIndex] = new KeyOrMouse(MouseButton.MouseWheelUp); keyArray[keyIndex] = new KeyOrMouse(MouseButton.MouseWheelUp);
} }
else if (PlayerInput.MouseWheelDownClicked()) else if (PlayerInput.MouseWheelDownClicked())
{ {
keyMapping[keyIndex] = new KeyOrMouse(MouseButton.MouseWheelDown); keyArray[keyIndex] = new KeyOrMouse(MouseButton.MouseWheelDown);
} }
else if (PlayerInput.GetKeyboardState.GetPressedKeys().Length > 0) else if (PlayerInput.GetKeyboardState.GetPressedKeys().Length > 0)
{ {
Keys key = PlayerInput.GetKeyboardState.GetPressedKeys()[0]; Keys key = PlayerInput.GetKeyboardState.GetPressedKeys()[0];
keyMapping[keyIndex] = new KeyOrMouse(key); keyArray[keyIndex] = new KeyOrMouse(key);
} }
else else
{ {
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
keyBox.Text = KeyBindText((InputType)keyIndex);
keyBox.Text = keyArray[keyIndex].Name;
keyBox.Text = ToolBox.LimitString(keyBox.Text, keyBox.Font, keyBox.Rect.Width); keyBox.Text = ToolBox.LimitString(keyBox.Text, keyBox.Font, keyBox.Rect.Width);
keyBox.Deselect(); keyBox.Deselect();
@@ -354,7 +354,6 @@ namespace Barotrauma
break; break;
case Layout.Right: case Layout.Right:
{ {
int extraOffset = 0;
int x = HUDLayoutSettings.InventoryAreaLower.Right; int x = HUDLayoutSettings.InventoryAreaLower.Right;
int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - SlotSize.X - Spacing; int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - SlotSize.X - Spacing;
for (int i = 0; i < slots.Length; i++) for (int i = 0; i < slots.Length; i++)
@@ -371,17 +370,18 @@ namespace Barotrauma
} }
int lowerX = x; int lowerX = x;
int personalSlotY = GameMain.GraphicsHeight - bottomOffset * 2 - Spacing * 2 - (int)(!GUI.IsFourByThree() ? UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment : UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment * 2f);
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
{ {
if (HideSlot(i)) continue; if (HideSlot(i)) continue;
if (PersonalSlots.HasFlag(SlotTypes[i])) if (PersonalSlots.HasFlag(SlotTypes[i]))
{ {
SlotPositions[i] = new Vector2(personalSlotX, GameMain.GraphicsHeight - bottomOffset * 2 - extraOffset - Spacing * 2); SlotPositions[i] = new Vector2(personalSlotX, personalSlotY);
personalSlotX -= slots[i].Rect.Width + Spacing; personalSlotX -= slots[i].Rect.Width + Spacing;
} }
else else
{ {
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset - extraOffset); SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += slots[i].Rect.Width + Spacing; x += slots[i].Rect.Width + Spacing;
} }
} }
@@ -391,7 +391,7 @@ namespace Barotrauma
{ {
if (!HideSlot(i)) continue; if (!HideSlot(i)) continue;
x -= slots[i].Rect.Width + Spacing; x -= slots[i].Rect.Width + Spacing;
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset - extraOffset); SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
} }
} }
break; break;
@@ -399,12 +399,14 @@ namespace Barotrauma
{ {
int x = HUDLayoutSettings.InventoryAreaLower.X; int x = HUDLayoutSettings.InventoryAreaLower.X;
int personalSlotX = x; int personalSlotX = x;
int personalSlotY = GameMain.GraphicsHeight - bottomOffset * 2 - Spacing * 2 - (int)(!GUI.IsFourByThree() ? UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment : UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment * 2f);
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
{ {
if (HideSlot(i)) continue; if (HideSlot(i)) continue;
if (PersonalSlots.HasFlag(SlotTypes[i])) if (PersonalSlots.HasFlag(SlotTypes[i]))
{ {
SlotPositions[i] = new Vector2(personalSlotX, GameMain.GraphicsHeight - bottomOffset * 2 - Spacing * 2); SlotPositions[i] = new Vector2(personalSlotX, personalSlotY);
personalSlotX += slots[i].Rect.Width + Spacing; personalSlotX += slots[i].Rect.Width + Spacing;
} }
else else
@@ -523,8 +525,7 @@ namespace Barotrauma
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
if (Items[i] != null && Items[i] != draggingItem && Character.Controlled?.Inventory == this && if (Items[i] != null && Items[i] != draggingItem && Character.Controlled?.Inventory == this &&
GUI.KeyboardDispatcher.Subscriber == null && !CrewManager.IsCommandInterfaceOpen && GUI.KeyboardDispatcher.Subscriber == null && !CrewManager.IsCommandInterfaceOpen && PlayerInput.InventoryKeyHit(slots[i].InventoryKeyIndex))
slots[i].QuickUseKey != Keys.None && PlayerInput.KeyHit(slots[i].QuickUseKey))
{ {
QuickUseItem(Items[i], true, false, true); QuickUseItem(Items[i], true, false, true);
} }
@@ -782,21 +783,16 @@ namespace Barotrauma
} }
} }
private void AssignQuickUseNumKeys() public void AssignQuickUseNumKeys()
{ {
int num = 1; int keyBindIndex = 0;
for (int i = 0; i < slots.Length; i++) for (int i = 0; i < slots.Length; i++)
{ {
if (HideSlot(i)) if (HideSlot(i)) continue;
{
slots[i].QuickUseKey = Keys.None;
continue;
}
if (SlotTypes[i] == InvSlotType.Any) if (SlotTypes[i] == InvSlotType.Any)
{ {
slots[i].QuickUseKey = Keys.D0 + num % 10; slots[i].InventoryKeyIndex = keyBindIndex;
num++; keyBindIndex++;
} }
} }
} }
@@ -819,16 +815,21 @@ namespace Barotrauma
{ {
if (item.Container == null || character.Inventory.FindIndex(item.Container) == -1) // Not a subinventory in the character's inventory if (item.Container == null || character.Inventory.FindIndex(item.Container) == -1) // Not a subinventory in the character's inventory
{ {
return item.ParentInventory is CharacterInventory ? if (character.SelectedItems.Any(i => i?.OwnInventory != null && i.OwnInventory.CanBePut(item)))
QuickUseAction.TakeFromCharacter : QuickUseAction.TakeFromContainer; {
return QuickUseAction.PutToEquippedItem;
}
else
{
return item.ParentInventory is CharacterInventory ? QuickUseAction.TakeFromCharacter : QuickUseAction.TakeFromContainer;
}
} }
else else
{ {
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>(); var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>();
if (selectedContainer != null && if (selectedContainer != null &&
selectedContainer.Inventory != null && selectedContainer.Inventory != null &&
!selectedContainer.Inventory.Locked && !selectedContainer.Inventory.Locked)
allowInventorySwap)
{ {
// Move the item from the subinventory to the selected container // Move the item from the subinventory to the selected container
return QuickUseAction.PutToContainer; return QuickUseAction.PutToContainer;
@@ -928,24 +929,24 @@ namespace Barotrauma
//attempt to put in a free slot first //attempt to put in a free slot first
for (int i = capacity - 1; i >= 0; i--) for (int i = capacity - 1; i >= 0; i--)
{ {
if (Items[i] != null) continue; if (Items[i] != null) { continue; }
if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) continue; if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; }
success = TryPutItem(item, i, true, false, Character.Controlled, true); success = TryPutItem(item, i, true, false, Character.Controlled, true);
if (success) break; if (success) { break; }
} }
if (!success) if (!success)
{ {
for (int i = capacity - 1; i >= 0; i--) for (int i = capacity - 1; i >= 0; i--)
{ {
if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) continue; if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; }
//something else already equipped in the slot, attempt to unequip it // something else already equipped in a hand slot, attempt to unequip it so items aren't unnecessarily swapped to it
if (Items[i] != null && Items[i].AllowedSlots.Contains(InvSlotType.Any)) if (Items[i] != null && Items[i].AllowedSlots.Contains(InvSlotType.Any) && (SlotTypes[i] == InvSlotType.LeftHand || SlotTypes[i] == InvSlotType.RightHand))
{ {
TryPutItem(Items[i], Character.Controlled, new List<InvSlotType>() { InvSlotType.Any }, true); TryPutItem(Items[i], Character.Controlled, new List<InvSlotType>() { InvSlotType.Any }, true);
} }
success = TryPutItem(item, i, true, false, Character.Controlled, true); success = TryPutItem(item, i, true, false, Character.Controlled, true);
if (success) break; if (success) { break; }
} }
} }
break; break;
@@ -1041,6 +1042,7 @@ namespace Barotrauma
prevUIScale != UIScale || prevUIScale != UIScale ||
prevHUDScale != GUI.Scale) prevHUDScale != GUI.Scale)
{ {
CreateSlots();
SetSlotPositions(layout); SetSlotPositions(layout);
prevUIScale = UIScale; prevUIScale = UIScale;
prevHUDScale = GUI.Scale; prevHUDScale = GUI.Scale;
@@ -237,6 +237,7 @@ namespace Barotrauma.Items.Components
{ {
StopPicking(null); StopPicking(null);
PlaySound(forcedOpen ? ActionType.OnPicked : ActionType.OnUse); PlaySound(forcedOpen ? ActionType.OnPicked : ActionType.OnUse);
if (isOpen) { stuck = MathHelper.Clamp(stuck - StuckReductionOnOpen, 0.0f, 100.0f); }
} }
} }
} }
@@ -245,7 +246,8 @@ namespace Barotrauma.Items.Components
{ {
base.ClientRead(type, msg, sendingTime); base.ClientRead(type, msg, sendingTime);
bool open = msg.ReadBoolean(); bool open = msg.ReadBoolean();
bool broken = msg.ReadBoolean();
bool forcedOpen = msg.ReadBoolean(); bool forcedOpen = msg.ReadBoolean();
SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen); SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen);
Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8); Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
@@ -258,6 +260,7 @@ namespace Barotrauma.Items.Components
} }
if (isStuck) { OpenState = 0.0f; } if (isStuck) { OpenState = 0.0f; }
IsBroken = broken;
PredictedState = null; PredictedState = null;
} }
} }
@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework; using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
@@ -52,5 +53,13 @@ namespace Barotrauma.Items.Components
} }
} }
} }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
CurrPowerConsumption = powerConsumption;
charging = true;
timer = Duration;
IsActive = true;
}
} }
} }
@@ -17,7 +17,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{ {
if (!IsActive || picker == null || !CanBeAttached() || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled) { return; } if (!IsActive || picker == null || !CanBeAttached(picker) || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled) { return; }
Vector2 gridPos = picker.Position; Vector2 gridPos = picker.Position;
Vector2 roundedGridPos = new Vector2( Vector2 roundedGridPos = new Vector2(
@@ -71,6 +71,8 @@ namespace Barotrauma.Items.Components
base.ClientRead(type, msg, sendingTime); base.ClientRead(type, msg, sendingTime);
bool shouldBeAttached = msg.ReadBoolean(); bool shouldBeAttached = msg.ReadBoolean();
Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle()); Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
UInt16 submarineID = msg.ReadUInt16();
Submarine sub = Entity.FindEntityByID(submarineID) as Submarine;
if (!attachable) if (!attachable)
{ {
@@ -84,6 +86,7 @@ namespace Barotrauma.Items.Components
{ {
Drop(false, null); Drop(false, null);
item.SetTransform(simPosition, 0.0f); item.SetTransform(simPosition, 0.0f);
item.Submarine = sub;
AttachToWall(); AttachToWall();
} }
} }
@@ -4,7 +4,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
@@ -4,7 +4,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -436,8 +436,7 @@ namespace Barotrauma.Items.Components
if (subElement.Attribute("color") != null) color = subElement.GetAttributeColor("color", Color.White); if (subElement.Attribute("color") != null) color = subElement.GetAttributeColor("color", Color.White);
string style = subElement.Attribute("style") == null ? string style = subElement.Attribute("style") == null ?
null : subElement.GetAttributeString("style", ""); null : subElement.GetAttributeString("style", "");
GuiFrame = new GUIFrame(RectTransform.Load(subElement, GUI.Canvas.ItemComponentHolder, Anchor.Center), style, color);
GuiFrame = new GUIFrame(RectTransform.Load(subElement, GUI.Canvas, Anchor.Center), style, color);
DefaultLayout = GUILayoutSettings.Load(subElement); DefaultLayout = GUILayoutSettings.Load(subElement);
break; break;
case "alternativelayout": case "alternativelayout":
@@ -28,7 +28,7 @@ namespace Barotrauma.Items.Components
} }
private string text; private string text;
[Serialize("", true, translationTextTag: "Label.", description: "The text displayed in the label."), Editable(100)] [Serialize("", true, translationTextTag: "Label.", description: "The text displayed in the label.", alwaysUseInstanceValues: true), Editable(100)]
public string Text public string Text
{ {
get { return text; } get { return text; }
@@ -58,7 +58,7 @@ namespace Barotrauma.Items.Components
private set; private set;
} }
[Editable, Serialize("0,0,0,255", true, description: "The color of the text displayed on the label (R,G,B,A).")] [Editable, Serialize("0,0,0,255", true, description: "The color of the text displayed on the label (R,G,B,A).", alwaysUseInstanceValues: true)]
public Color TextColor public Color TextColor
{ {
get { return textColor; } get { return textColor; }
@@ -69,7 +69,7 @@ namespace Barotrauma.Items.Components
} }
} }
[Editable(0.0f, 10.0f), Serialize(1.0f, true, description: "The scale of the text displayed on the label.")] [Editable(0.0f, 10.0f), Serialize(1.0f, true, description: "The scale of the text displayed on the label.", alwaysUseInstanceValues: true)]
public float TextScale public float TextScale
{ {
get { return textBlock == null ? 1.0f : textBlock.TextScale; } get { return textBlock == null ? 1.0f : textBlock.TextScale; }
@@ -48,7 +48,10 @@ namespace Barotrauma.Items.Components
{ {
if (light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn) if (light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn)
{ {
light.LightSprite.Draw(spriteBatch, new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), lightColor * lightBrightness, 0.0f, item.Scale, SpriteEffects.None, item.SpriteDepth - 0.0001f); Vector2 origin = light.LightSprite.Origin;
if (light.LightSpriteEffect == SpriteEffects.FlipHorizontally) { origin.X = light.LightSprite.SourceRect.Width - origin.X; }
if (light.LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = light.LightSprite.SourceRect.Height - origin.Y; }
light.LightSprite.Draw(spriteBatch, new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), lightColor * lightBrightness, origin, -light.Rotation, item.Scale, light.LightSpriteEffect, item.SpriteDepth - 0.0001f);
} }
} }
@@ -76,7 +76,7 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
state = msg.ReadBoolean(); State = msg.ReadBoolean();
ushort userID = msg.ReadUInt16(); ushort userID = msg.ReadUInt16();
if (userID == 0) if (userID == 0)
{ {
@@ -16,6 +16,10 @@ namespace Barotrauma.Items.Components
private GUIScrollBar forceSlider; private GUIScrollBar forceSlider;
private GUITickBox autoControlIndicator; private GUITickBox autoControlIndicator;
private int particlesPerSec = 60;
private float particleTimer;
public float AnimSpeed public float AnimSpeed
{ {
get; get;
@@ -318,10 +318,10 @@ namespace Barotrauma.Items.Components
{ {
if (item.ParentInventory.slots[availableSlotIndex].HighlightTimer <= 0.0f) if (item.ParentInventory.slots[availableSlotIndex].HighlightTimer <= 0.0f)
{ {
item.ParentInventory.slots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f); item.ParentInventory.slots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
if (slotIndex < inputContainer.Capacity) if (slotIndex < inputContainer.Capacity)
{ {
inputContainer.Inventory.slots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f); inputContainer.Inventory.slots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
} }
} }
} }
@@ -338,9 +338,21 @@ namespace Barotrauma.Items.Components
color: requiredItem.ItemPrefab.InventoryIconColor * 0.3f, color: requiredItem.ItemPrefab.InventoryIconColor * 0.3f,
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y)); scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y));
if (requiredItem.UseCondition && requiredItem.MinCondition < 1.0f)
{
GUI.DrawRectangle(spriteBatch, new Rectangle(slotRect.X, slotRect.Bottom - 8, slotRect.Width, 8), Color.Black * 0.8f, true);
GUI.DrawRectangle(spriteBatch,
new Rectangle(slotRect.X, slotRect.Bottom - 8, (int)(slotRect.Width * requiredItem.MinCondition), 8),
GUI.Style.Green * 0.8f, true);
}
if (slotRect.Contains(PlayerInput.MousePosition)) if (slotRect.Contains(PlayerInput.MousePosition))
{ {
string toolTipText = requiredItem.ItemPrefab.Name; string toolTipText = requiredItem.ItemPrefab.Name;
if (requiredItem.UseCondition && requiredItem.MinCondition < 1.0f)
{
toolTipText += " " + (int)Math.Round(requiredItem.MinCondition * 100) + "%";
}
if (!string.IsNullOrEmpty(requiredItem.ItemPrefab.Description)) if (!string.IsNullOrEmpty(requiredItem.ItemPrefab.Description))
{ {
toolTipText += '\n' + requiredItem.ItemPrefab.Description; toolTipText += '\n' + requiredItem.ItemPrefab.Description;
@@ -163,7 +163,7 @@ namespace Barotrauma.Items.Components
{ {
pumpSpeedLockTimer -= deltaTime; pumpSpeedLockTimer -= deltaTime;
isActiveLockTimer -= deltaTime; isActiveLockTimer -= deltaTime;
autoControlIndicator.Selected = pumpSpeedLockTimer > 0.0f || isActiveLockTimer > 0.0f; autoControlIndicator.Selected = IsAutoControlled;
PowerButton.Enabled = isActiveLockTimer <= 0.0f; PowerButton.Enabled = isActiveLockTimer <= 0.0f;
if (HasPower) if (HasPower)
{ {
@@ -18,6 +18,8 @@ namespace Barotrauma.Items.Components
Disruption Disruption
} }
private PathFinder pathFinder;
private bool dynamicDockingIndicator = true; private bool dynamicDockingIndicator = true;
private bool unsentChanges; private bool unsentChanges;
@@ -45,7 +47,7 @@ namespace Barotrauma.Items.Components
private Sprite sonarBlip; private Sprite sonarBlip;
private Sprite lineSprite; private Sprite lineSprite;
private Dictionary<string, Sprite> targetIcons = new Dictionary<string, Sprite>(); private readonly Dictionary<string, Sprite> targetIcons = new Dictionary<string, Sprite>();
private float displayBorderSize; private float displayBorderSize;
@@ -65,7 +67,24 @@ namespace Barotrauma.Items.Components
//Vector2 = vector from the ping source to the position of the disruption //Vector2 = vector from the ping source to the position of the disruption
//float = strength of the disruption, between 0-1 //float = strength of the disruption, between 0-1
List<Pair<Vector2, float>> disruptedDirections = new List<Pair<Vector2, float>>(); private readonly List<Pair<Vector2, float>> disruptedDirections = new List<Pair<Vector2, float>>();
class CachedDistance
{
public readonly Vector2 TransducerWorldPos;
public readonly Vector2 WorldPos;
public readonly float Distance;
public double RecalculationTime;
public CachedDistance(Vector2 transducerWorldPos, Vector2 worldPos, float dist)
{
TransducerWorldPos = transducerWorldPos;
WorldPos = worldPos;
Distance = dist;
}
}
private readonly Dictionary<object, CachedDistance> markerDistances = new Dictionary<object, CachedDistance>();
private readonly Color positiveColor = Color.Green; private readonly Color positiveColor = Color.Green;
private readonly Color warningColor = Color.Orange; private readonly Color warningColor = Color.Orange;
@@ -74,7 +93,7 @@ namespace Barotrauma.Items.Components
public static readonly Vector2 controlBoxSize = new Vector2(0.33f, 0.32f); public static readonly Vector2 controlBoxSize = new Vector2(0.33f, 0.32f);
public static readonly Vector2 controlBoxOffset = new Vector2(0.025f, 0); public static readonly Vector2 controlBoxOffset = new Vector2(0.025f, 0);
public static readonly float sonarAreaSize = 1.09f; private static readonly float sonarAreaSize = 1.09f;
private static readonly Dictionary<BlipType, Color[]> blipColorGradient = new Dictionary<BlipType, Color[]>() private static readonly Dictionary<BlipType, Color[]> blipColorGradient = new Dictionary<BlipType, Color[]>()
{ {
@@ -94,6 +113,8 @@ namespace Barotrauma.Items.Components
public float DisplayRadius { get; private set; } public float DisplayRadius { get; private set; }
public static Vector2 GUISizeCalculation => Vector2.One * Math.Min(GUI.RelativeHorizontalAspectRatio, 1f) * sonarAreaSize;
partial void InitProjSpecific(XElement element) partial void InitProjSpecific(XElement element)
{ {
System.Diagnostics.Debug.Assert(Enum.GetValues(typeof(BlipType)).Cast<BlipType>().All(t => blipColorGradient.ContainsKey(t))); System.Diagnostics.Debug.Assert(Enum.GetValues(typeof(BlipType)).Cast<BlipType>().All(t => blipColorGradient.ContainsKey(t)));
@@ -254,7 +275,7 @@ namespace Barotrauma.Items.Components
controlContainer.RectTransform.SetPosition(Anchor.TopLeft); controlContainer.RectTransform.SetPosition(Anchor.TopLeft);
sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest; sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest;
sonarView.RectTransform.SetPosition(Anchor.CenterRight); sonarView.RectTransform.SetPosition(Anchor.CenterRight);
sonarView.RectTransform.Resize(Vector2.One * GUI.RelativeHorizontalAspectRatio * sonarAreaSize); sonarView.RectTransform.Resize(GUISizeCalculation);
GUITextBlock.AutoScaleAndNormalize(passiveTickBox.TextBlock, activeTickBox.TextBlock, zoomText, directionalModeSwitchText); GUITextBlock.AutoScaleAndNormalize(passiveTickBox.TextBlock, activeTickBox.TextBlock, zoomText, directionalModeSwitchText);
} }
} }
@@ -653,12 +674,16 @@ namespace Barotrauma.Items.Components
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
GameMain.GameSession.StartLocation.Name, GameMain.GameSession.StartLocation.Name,
"outpost", "outpost",
(Level.Loaded.StartPosition - transducerCenter), displayScale, center, DisplayRadius); GameMain.GameSession.StartLocation.Name,
Level.Loaded.StartPosition, transducerCenter,
displayScale, center, DisplayRadius);
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
GameMain.GameSession.EndLocation.Name, GameMain.GameSession.EndLocation.Name,
"outpost", "outpost",
(Level.Loaded.EndPosition - transducerCenter), displayScale, center, DisplayRadius); GameMain.GameSession.EndLocation.Name,
Level.Loaded.EndPosition, transducerCenter,
displayScale, center, DisplayRadius);
foreach (AITarget aiTarget in AITarget.List) foreach (AITarget aiTarget in AITarget.List)
{ {
@@ -670,7 +695,9 @@ namespace Barotrauma.Items.Components
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
aiTarget.SonarLabel, aiTarget.SonarLabel,
aiTarget.SonarIconIdentifier, aiTarget.SonarIconIdentifier,
aiTarget.WorldPosition - transducerCenter, displayScale, center, DisplayRadius * 0.975f); aiTarget,
aiTarget.WorldPosition, transducerCenter,
displayScale, center, DisplayRadius * 0.975f);
} }
} }
@@ -685,7 +712,9 @@ namespace Barotrauma.Items.Components
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
mission.SonarLabel, mission.SonarLabel,
mission.SonarIconIdentifier, mission.SonarIconIdentifier,
sonarPosition - transducerCenter, displayScale, center, DisplayRadius * 0.95f); mission,
sonarPosition, transducerCenter,
displayScale, center, DisplayRadius * 0.95f);
} }
} }
} }
@@ -704,7 +733,8 @@ namespace Barotrauma.Items.Components
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
sub.Info.DisplayName, sub.Info.DisplayName,
sub.Info.HasTag(SubmarineTag.Shuttle) ? "shuttle" : "submarine", sub.Info.HasTag(SubmarineTag.Shuttle) ? "shuttle" : "submarine",
sub.WorldPosition - transducerCenter, sub,
sub.WorldPosition, transducerCenter,
displayScale, center, DisplayRadius * 0.95f); displayScale, center, DisplayRadius * 0.95f);
} }
@@ -951,14 +981,13 @@ namespace Barotrauma.Items.Components
} }
foreach (AITarget aiTarget in AITarget.List) foreach (AITarget aiTarget in AITarget.List)
{ {
if (aiTarget.SonarDisruption <= 0.0f || !aiTarget.Enabled) { continue; } float disruption = aiTarget.Entity is Character c ? c.Params.SonarDisruption : aiTarget.SonarDisruption;
if (disruption <= 0.0f || !aiTarget.Enabled) { continue; }
float distSqr = Vector2.DistanceSquared(aiTarget.WorldPosition, pingSource); float distSqr = Vector2.DistanceSquared(aiTarget.WorldPosition, pingSource);
if (distSqr > worldPingRadiusSqr) { continue; } if (distSqr > worldPingRadiusSqr) { continue; }
float disruptionDist = (float)Math.Sqrt(distSqr); float disruptionDist = (float)Math.Sqrt(distSqr);
disruptedDirections.Add(new Pair<Vector2, float>((aiTarget.WorldPosition - pingSource) / disruptionDist, aiTarget.SonarDisruption)); disruptedDirections.Add(new Pair<Vector2, float>((aiTarget.WorldPosition - pingSource) / disruptionDist, aiTarget.SonarDisruption));
CreateBlipsForDisruption(aiTarget.WorldPosition, disruption);
CreateBlipsForDisruption(aiTarget.WorldPosition, aiTarget.SonarDisruption);
} }
} }
@@ -1303,9 +1332,48 @@ namespace Barotrauma.Items.Components
sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f, sonarBlip.Origin, 0, scale * 0.08f, SpriteEffects.None, 0); sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f, sonarBlip.Origin, 0, scale * 0.08f, SpriteEffects.None, 0);
} }
private void DrawMarker(SpriteBatch spriteBatch, string label, string iconIdentifier, Vector2 position, float scale, Vector2 center, float radius) private void DrawMarker(SpriteBatch spriteBatch, string label, string iconIdentifier, object targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, float scale, Vector2 center, float radius)
{ {
float dist = position.Length(); float linearDist = Vector2.Distance(worldPosition, transducerPosition);
float dist = linearDist;
if (linearDist > Range)
{
if (markerDistances.TryGetValue(targetIdentifier, out CachedDistance cachedDistance))
{
if (Timing.TotalTime > cachedDistance.RecalculationTime &&
(Vector2.DistanceSquared(cachedDistance.TransducerWorldPos, transducerPosition) > 500 * 500 ||
Vector2.DistanceSquared(cachedDistance.WorldPos, worldPosition) > 500 * 500))
{
markerDistances.Remove(targetIdentifier);
CalculateDistance();
}
else
{
dist = Math.Max(cachedDistance.Distance, linearDist);
}
}
else
{
CalculateDistance();
}
}
void CalculateDistance()
{
pathFinder ??= new PathFinder(WayPoint.WayPointList, indoorsSteering: false);
var path = pathFinder.FindPath(ConvertUnits.ToSimUnits(transducerPosition), ConvertUnits.ToSimUnits(worldPosition));
if (!path.Unreachable)
{
var cachedDistance = new CachedDistance(transducerPosition, worldPosition, path.TotalLength)
{
RecalculationTime = Timing.TotalTime + Rand.Range(1.0f, 5.0f)
};
markerDistances.Add(targetIdentifier, cachedDistance);
dist = path.TotalLength;
}
}
Vector2 position = worldPosition - transducerPosition;
position *= zoom; position *= zoom;
position *= scale; position *= scale;
@@ -1314,16 +1382,16 @@ namespace Barotrauma.Items.Components
float textAlpha = MathHelper.Clamp(1.5f - dist / 50000.0f, 0.5f, 1.0f); float textAlpha = MathHelper.Clamp(1.5f - dist / 50000.0f, 0.5f, 1.0f);
Vector2 dir = Vector2.Normalize(position); Vector2 dir = Vector2.Normalize(position);
Vector2 markerPos = (dist * zoom * scale > radius) ? dir * radius : position; Vector2 markerPos = (linearDist * zoom * scale > radius) ? dir * radius : position;
markerPos += center; markerPos += center;
markerPos.X = (int)markerPos.X; markerPos.X = (int)markerPos.X;
markerPos.Y = (int)markerPos.Y; markerPos.Y = (int)markerPos.Y;
float alpha = 1.0f; float alpha = 1.0f;
if (dist * scale < radius) if (linearDist * scale < radius)
{ {
float normalizedDist = dist * scale / radius; float normalizedDist = linearDist * scale / radius;
alpha = Math.Max(normalizedDist - 0.4f, 0.0f); alpha = Math.Max(normalizedDist - 0.4f, 0.0f);
float mouseDist = Vector2.Distance(PlayerInput.MousePosition, markerPos); float mouseDist = Vector2.Distance(PlayerInput.MousePosition, markerPos);
@@ -1334,14 +1402,6 @@ namespace Barotrauma.Items.Components
} }
} }
if (!GuiFrame.Children.First().Rect.Contains(markerPos))
{
if (MathUtils.GetLineRectangleIntersection(center, markerPos, GuiFrame.Children.First().Rect, out Vector2 intersection))
{
markerPos = intersection;
}
}
if (string.IsNullOrEmpty(iconIdentifier) || !targetIcons.ContainsKey(iconIdentifier)) if (string.IsNullOrEmpty(iconIdentifier) || !targetIcons.ContainsKey(iconIdentifier))
{ {
GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X - 3, (int)markerPos.Y - 3, 6, 6), markerColor, thickness: 2); GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X - 3, (int)markerPos.Y - 3, 6, 6), markerColor, thickness: 2);
@@ -390,7 +390,7 @@ namespace Barotrauma.Items.Components
}; };
// Sonar area // Sonar area
steerArea = new GUICustomComponent(new RectTransform(Vector2.One * GUI.RelativeHorizontalAspectRatio * Sonar.sonarAreaSize, GuiFrame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest), steerArea = new GUICustomComponent(new RectTransform(Sonar.GUISizeCalculation, GuiFrame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest),
(spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null); (spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null);
steerRadius = steerArea.Rect.Width / 2; steerRadius = steerArea.Rect.Width / 2;
@@ -438,10 +438,7 @@ namespace Barotrauma.Items.Components
if (Voltage < MinVoltage) { return; } if (Voltage < MinVoltage) { return; }
Rectangle velRect = new Rectangle(x + 20, y + 20, width - 40, height - 40); Rectangle velRect = new Rectangle(x + 20, y + 20, width - 40, height - 40);
Vector2 displaySubPos = (-sonar.DisplayOffset * sonar.Zoom) / sonar.Range * sonar.DisplayRadius * sonar.Zoom; Vector2 steeringOrigin = steerArea.Rect.Center.ToVector2();
displaySubPos.Y = -displaySubPos.Y;
displaySubPos = displaySubPos.ClampLength(velRect.Width / 2);
displaySubPos = steerArea.Rect.Center.ToVector2() + displaySubPos;
if (!AutoPilot) if (!AutoPilot)
{ {
@@ -450,18 +447,18 @@ namespace Barotrauma.Items.Components
Vector2 steeringInputPos = new Vector2( Vector2 steeringInputPos = new Vector2(
steeringInput.X * (float)Math.Sqrt(1.0f - 0.5f * unitSteeringInput.Y * unitSteeringInput.Y), steeringInput.X * (float)Math.Sqrt(1.0f - 0.5f * unitSteeringInput.Y * unitSteeringInput.Y),
-steeringInput.Y * (float)Math.Sqrt(1.0f - 0.5f * unitSteeringInput.X * unitSteeringInput.X)); -steeringInput.Y * (float)Math.Sqrt(1.0f - 0.5f * unitSteeringInput.X * unitSteeringInput.X));
steeringInputPos += displaySubPos; steeringInputPos += steeringOrigin;
if (steeringIndicator != null) if (steeringIndicator != null)
{ {
Vector2 dir = steeringInputPos - displaySubPos; Vector2 dir = steeringInputPos - steeringOrigin;
float angle = (float)Math.Atan2(dir.Y, dir.X); float angle = (float)Math.Atan2(dir.Y, dir.X);
steeringIndicator.Draw(spriteBatch, displaySubPos, Color.White, origin: steeringIndicator.Origin, rotate: angle, steeringIndicator.Draw(spriteBatch, steeringOrigin, Color.White, origin: steeringIndicator.Origin, rotate: angle,
scale: new Vector2(dir.Length() / steeringIndicator.size.X, 1.0f)); scale: new Vector2(dir.Length() / steeringIndicator.size.X, 1.0f));
} }
else else
{ {
GUI.DrawLine(spriteBatch, displaySubPos, steeringInputPos, Color.LightGray); GUI.DrawLine(spriteBatch, steeringOrigin, steeringInputPos, Color.LightGray);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)steeringInputPos.X - 5, (int)steeringInputPos.Y - 5, 10, 10), Color.White); GUI.DrawRectangle(spriteBatch, new Rectangle((int)steeringInputPos.X - 5, (int)steeringInputPos.Y - 5, 10, 10), Color.White);
} }
@@ -475,7 +472,7 @@ namespace Barotrauma.Items.Components
Sonar sonar = item.GetComponent<Sonar>(); Sonar sonar = item.GetComponent<Sonar>();
if (sonar != null && controlledSub != null) if (sonar != null && controlledSub != null)
{ {
Vector2 displayPosToMaintain = ((posToMaintain.Value - sonar.DisplayOffset * sonar.Zoom - controlledSub.WorldPosition)) / sonar.Range * sonar.DisplayRadius * sonar.Zoom; Vector2 displayPosToMaintain = ((posToMaintain.Value - controlledSub.WorldPosition)) / sonar.Range * sonar.DisplayRadius * sonar.Zoom;
displayPosToMaintain.Y = -displayPosToMaintain.Y; displayPosToMaintain.Y = -displayPosToMaintain.Y;
displayPosToMaintain = displayPosToMaintain.ClampLength(velRect.Width / 2); displayPosToMaintain = displayPosToMaintain.ClampLength(velRect.Width / 2);
displayPosToMaintain = steerArea.Rect.Center.ToVector2() + displayPosToMaintain; displayPosToMaintain = steerArea.Rect.Center.ToVector2() + displayPosToMaintain;
@@ -494,11 +491,11 @@ namespace Barotrauma.Items.Components
if (maintainPosOriginIndicator != null) if (maintainPosOriginIndicator != null)
{ {
maintainPosOriginIndicator.Draw(spriteBatch, displaySubPos, GUI.Style.Orange, scale: 0.5f * sonar.Zoom); maintainPosOriginIndicator.Draw(spriteBatch, steeringOrigin, GUI.Style.Orange, scale: 0.5f * sonar.Zoom);
} }
else else
{ {
GUI.DrawRectangle(spriteBatch, new Rectangle((int)displaySubPos.X - 5, (int)displaySubPos.Y - 5, 10, 10), GUI.Style.Orange); GUI.DrawRectangle(spriteBatch, new Rectangle((int)steeringOrigin.X - 5, (int)steeringOrigin.Y - 5, 10, 10), GUI.Style.Orange);
} }
} }
} }
@@ -508,20 +505,19 @@ namespace Barotrauma.Items.Components
Vector2 steeringPos = new Vector2( Vector2 steeringPos = new Vector2(
targetVelocity.X * 0.9f * (float)Math.Sqrt(1.0f - 0.5f * unitTargetVel.Y * unitTargetVel.Y), targetVelocity.X * 0.9f * (float)Math.Sqrt(1.0f - 0.5f * unitTargetVel.Y * unitTargetVel.Y),
-targetVelocity.Y * 0.9f * (float)Math.Sqrt(1.0f - 0.5f * unitTargetVel.X * unitTargetVel.X)); -targetVelocity.Y * 0.9f * (float)Math.Sqrt(1.0f - 0.5f * unitTargetVel.X * unitTargetVel.X));
steeringPos += displaySubPos; steeringPos += steeringOrigin;
if (steeringIndicator != null) if (steeringIndicator != null)
{ {
Vector2 dir = steeringPos - displaySubPos; Vector2 dir = steeringPos - steeringOrigin;
float angle = (float)Math.Atan2(dir.Y, dir.X); float angle = (float)Math.Atan2(dir.Y, dir.X);
steeringIndicator.Draw(spriteBatch, displaySubPos, Color.Gray, origin: steeringIndicator.Origin, rotate: angle, steeringIndicator.Draw(spriteBatch, steeringOrigin, Color.Gray, origin: steeringIndicator.Origin, rotate: angle,
scale: new Vector2(dir.Length() / steeringIndicator.size.X, 0.7f)); scale: new Vector2(dir.Length() / steeringIndicator.size.X, 0.7f));
} }
else else
{ {
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
displaySubPos, steeringOrigin,
steeringPos, steeringPos,
Color.CadetBlue, 0, 2); Color.CadetBlue, 0, 2);
} }
@@ -669,11 +665,7 @@ namespace Barotrauma.Items.Components
{ {
if (PlayerInput.PrimaryMouseButtonHeld() && !CrewManager.IsCommandInterfaceOpen && !GameSession.IsTabMenuOpen) if (PlayerInput.PrimaryMouseButtonHeld() && !CrewManager.IsCommandInterfaceOpen && !GameSession.IsTabMenuOpen)
{ {
Vector2 displaySubPos = (-sonar.DisplayOffset * sonar.Zoom) / sonar.Range * sonar.DisplayRadius * sonar.Zoom; Vector2 inputPos = PlayerInput.MousePosition - steerArea.Rect.Center.ToVector2();
displaySubPos.Y = -displaySubPos.Y;
displaySubPos = steerArea.Rect.Center.ToVector2() + displaySubPos;
Vector2 inputPos = PlayerInput.MousePosition - displaySubPos;
inputPos.Y = -inputPos.Y; inputPos.Y = -inputPos.Y;
if (AutoPilot && !LevelStartSelected && !LevelEndSelected) if (AutoPilot && !LevelStartSelected && !LevelEndSelected)
{ {
@@ -848,7 +840,6 @@ namespace Barotrauma.Items.Components
Vector2 newSteeringInput = steeringInput; Vector2 newSteeringInput = steeringInput;
Vector2 newTargetVelocity = targetVelocity; Vector2 newTargetVelocity = targetVelocity;
float newSteeringAdjustSpeed = steeringAdjustSpeed; float newSteeringAdjustSpeed = steeringAdjustSpeed;
bool maintainPos = false;
Vector2? newPosToMaintain = null; Vector2? newPosToMaintain = null;
bool headingToStart = false; bool headingToStart = false;
@@ -859,8 +850,7 @@ namespace Barotrauma.Items.Components
if (autoPilot) if (autoPilot)
{ {
maintainPos = msg.ReadBoolean(); if (msg.ReadBoolean())
if (maintainPos)
{ {
newPosToMaintain = new Vector2( newPosToMaintain = new Vector2(
msg.ReadSingle(), msg.ReadSingle(),
@@ -27,6 +27,8 @@ namespace Barotrauma.Items.Components
private FixActions requestStartFixAction; private FixActions requestStartFixAction;
public float FakeBrokenTimer;
[Serialize("", false, description: "An optional description of the needed repairs displayed in the repair interface.")] [Serialize("", false, description: "An optional description of the needed repairs displayed in the repair interface.")]
public string Description public string Description
{ {
@@ -43,7 +45,7 @@ namespace Barotrauma.Items.Components
public override bool ShouldDrawHUD(Character character) public override bool ShouldDrawHUD(Character character)
{ {
if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) return false; if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) return false;
return !item.IsFullCondition || character.IsTraitor && item.ConditionPercentage > MinSabotageCondition || (CurrentFixer == character && (!item.IsFullCondition || (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition))); return item.ConditionPercentage < RepairThreshold || character.IsTraitor && item.ConditionPercentage > MinSabotageCondition || (CurrentFixer == character && (!item.IsFullCondition || (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition)));
} }
partial void InitProjSpecific(XElement element) partial void InitProjSpecific(XElement element)
@@ -136,6 +138,17 @@ namespace Barotrauma.Items.Components
partial void UpdateProjSpecific(float deltaTime) partial void UpdateProjSpecific(float deltaTime)
{ {
if (Character.Controlled == null || (Character.Controlled.CharacterHealth.GetAffliction("psychosis")?.Strength ?? 0.0f) <= 0.0f)
{
FakeBrokenTimer = 0.0f;
}
else
{
FakeBrokenTimer -= deltaTime;
}
item.FakeBroken = FakeBrokenTimer > 0.0f;
if (!GameMain.IsMultiplayer) if (!GameMain.IsMultiplayer)
{ {
switch (requestStartFixAction) switch (requestStartFixAction)
@@ -153,7 +166,7 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < particleEmitters.Count; i++) for (int i = 0; i < particleEmitters.Count; i++)
{ {
if (item.ConditionPercentage >= particleEmitterConditionRanges[i].X && item.ConditionPercentage <= particleEmitterConditionRanges[i].Y) if ((item.ConditionPercentage >= particleEmitterConditionRanges[i].X && item.ConditionPercentage <= particleEmitterConditionRanges[i].Y) || FakeBrokenTimer > 0.0f)
{ {
particleEmitters[i].Emit(deltaTime, item.WorldPosition, item.CurrentHull); particleEmitters[i].Emit(deltaTime, item.WorldPosition, item.CurrentHull);
} }
@@ -379,7 +379,7 @@ namespace Barotrauma.Items.Components
ConnectionPanel.HighlightedWire = wire; ConnectionPanel.HighlightedWire = wire;
bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring; bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring;
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.PrimaryMouseButtonHeld()) { DraggingConnected = wire; } if (PlayerInput.PrimaryMouseButtonHeld()) { DraggingConnected = wire; }
@@ -285,6 +285,8 @@ namespace Barotrauma.Items.Components
public static void UpdateEditing(List<Wire> wires) public static void UpdateEditing(List<Wire> wires)
{ {
var doubleClicked = PlayerInput.DoubleClicked();
Wire equippedWire = Wire equippedWire =
Character.Controlled?.SelectedItems[0]?.GetComponent<Wire>() ?? Character.Controlled?.SelectedItems[0]?.GetComponent<Wire>() ??
Character.Controlled?.SelectedItems[1]?.GetComponent<Wire>(); Character.Controlled?.SelectedItems[1]?.GetComponent<Wire>();
@@ -298,7 +300,7 @@ namespace Barotrauma.Items.Components
} }
//dragging a node of some wire //dragging a node of some wire
if (draggingWire != null) if (draggingWire != null && !doubleClicked)
{ {
if (Character.Controlled != null) if (Character.Controlled != null)
{ {
@@ -329,15 +331,18 @@ namespace Barotrauma.Items.Components
if (selectedNodeIndex.HasValue) if (selectedNodeIndex.HasValue)
{ {
nodeWorldPos.X = MathUtils.Round(nodeWorldPos.X, Submarine.GridSize.X / 2.0f); if (!PlayerInput.IsShiftDown())
nodeWorldPos.Y = MathUtils.Round(nodeWorldPos.Y, Submarine.GridSize.Y / 2.0f); {
nodeWorldPos.X = MathUtils.Round(nodeWorldPos.X, Submarine.GridSize.X / 2.0f);
nodeWorldPos.Y = MathUtils.Round(nodeWorldPos.Y, Submarine.GridSize.Y / 2.0f);
}
draggingWire.nodes[(int)selectedNodeIndex] = nodeWorldPos; draggingWire.nodes[(int)selectedNodeIndex] = nodeWorldPos;
draggingWire.UpdateSections(); draggingWire.UpdateSections();
} }
else else
{ {
if (Vector2.DistanceSquared(nodeWorldPos, draggingWire.nodes[(int)highlightedNodeIndex]) > Submarine.GridSize.X * Submarine.GridSize.X) if (Vector2.DistanceSquared(nodeWorldPos, draggingWire.nodes[(int)highlightedNodeIndex]) > Submarine.GridSize.X * Submarine.GridSize.X || PlayerInput.IsShiftDown())
{ {
selectedNodeIndex = highlightedNodeIndex; selectedNodeIndex = highlightedNodeIndex;
} }
@@ -350,6 +355,8 @@ namespace Barotrauma.Items.Components
return; return;
} }
bool updateHighlight = true;
//a wire has been selected -> check if we should start dragging one of the nodes //a wire has been selected -> check if we should start dragging one of the nodes
float nodeSelectDist = 10, sectionSelectDist = 5; float nodeSelectDist = 10, sectionSelectDist = 5;
highlightedNodeIndex = null; highlightedNodeIndex = null;
@@ -406,6 +413,37 @@ namespace Barotrauma.Items.Components
selectedWire.nodes.RemoveAt(closestIndex); selectedWire.nodes.RemoveAt(closestIndex);
selectedWire.UpdateSections(); selectedWire.UpdateSections();
} }
// if only one end of the wire is disconnect pick it back up with double click
else if (doubleClicked && equippedWire == null && Character.Controlled != null && selectedWire.connections.Any(conn => conn != null))
{
if (selectedWire.connections[0] == null && closestIndex == 0 || selectedWire.connections[1] == null && closestIndex == selectedWire.nodes.Count - 1)
{
selectedWire.IsActive = true;
selectedWire.nodes.RemoveAt(closestIndex);
selectedWire.UpdateSections();
// flip the wire
if (closestIndex == 0)
{
selectedWire.nodes.Reverse();
selectedWire.connections[0] = selectedWire.connections[1];
selectedWire.connections[1] = null;
}
selectedWire.shouldClearConnections = false;
Character.Controlled.Inventory.TryPutItem(selectedWire.item, Character.Controlled, new List<InvSlotType> { InvSlotType.LeftHand, InvSlotType.RightHand });
foreach (var entity in MapEntity.mapEntityList)
{
if (entity is Item item)
{
item.GetComponent<ConnectionPanel>()?.DisconnectedWires.Remove(selectedWire);
}
}
MapEntity.SelectedList.Clear();
selectedWire.shouldClearConnections = true;
updateHighlight = false;
}
}
} }
} }
} }
@@ -446,7 +484,7 @@ namespace Barotrauma.Items.Components
} }
} }
if (highlighted != null) if (highlighted != null && updateHighlight)
{ {
highlighted.item.IsHighlighted = true; highlighted.item.IsHighlighted = true;
if (PlayerInput.PrimaryMouseButtonClicked()) if (PlayerInput.PrimaryMouseButtonClicked())
@@ -5,7 +5,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -31,7 +31,7 @@ namespace Barotrauma
public Sprite SlotSprite; public Sprite SlotSprite;
public Keys QuickUseKey; public int InventoryKeyIndex = -1;
public int SubInventoryDir = -1; public int SubInventoryDir = -1;
@@ -709,12 +709,15 @@ namespace Barotrauma
/// Is the mouse on any inventory element (slot, equip button, subinventory...) /// Is the mouse on any inventory element (slot, equip button, subinventory...)
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static bool IsMouseOnInventory() public static bool IsMouseOnInventory(bool ignoreDraggedItem = false)
{ {
var isSubEditor = Screen.Selected is SubEditorScreen editor && !editor.WiringMode; var isSubEditor = Screen.Selected is SubEditorScreen editor && !editor.WiringMode;
if (Character.Controlled == null) return false; if (Character.Controlled == null) { return false; }
if (draggingItem != null || DraggingInventory != null) return true; if (!ignoreDraggedItem)
{
if (draggingItem != null || DraggingInventory != null) { return true; }
}
if (Character.Controlled.Inventory != null && !isSubEditor) if (Character.Controlled.Inventory != null && !isSubEditor)
{ {
@@ -966,7 +969,8 @@ namespace Barotrauma
{ {
Character.Controlled.ClearInputs(); Character.Controlled.ClearInputs();
if (CharacterHealth.OpenHealthWindow != null && if (!IsMouseOnInventory(ignoreDraggedItem: true) &&
CharacterHealth.OpenHealthWindow != null &&
CharacterHealth.OpenHealthWindow.OnItemDropped(draggingItem, false)) CharacterHealth.OpenHealthWindow.OnItemDropped(draggingItem, false))
{ {
draggingItem = null; draggingItem = null;
@@ -1078,7 +1082,7 @@ namespace Barotrauma
protected static Rectangle GetSubInventoryHoverArea(SlotReference subSlot) protected static Rectangle GetSubInventoryHoverArea(SlotReference subSlot)
{ {
Rectangle hoverArea; Rectangle hoverArea;
if (!subSlot.Inventory.Movable()) if (!subSlot.Inventory.Movable() || Character.Controlled?.Inventory == subSlot.ParentInventory && !Character.Controlled.HasEquippedItem(subSlot.Item))
{ {
hoverArea = subSlot.Slot.Rect; hoverArea = subSlot.Slot.Rect;
hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint(); hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint();
@@ -1251,7 +1255,7 @@ namespace Barotrauma
if (item != null && drawItem) if (item != null && drawItem)
{ {
if (!item.IsFullCondition && (itemContainer == null || !itemContainer.ShowConditionInContainedStateIndicator)) if (!item.IsFullCondition && !item.Prefab.HideConditionBar && (itemContainer == null || !itemContainer.ShowConditionInContainedStateIndicator))
{ {
GUI.DrawRectangle(spriteBatch, new Rectangle(rect.X, rect.Bottom - 8, rect.Width, 8), Color.Black * 0.8f, true); GUI.DrawRectangle(spriteBatch, new Rectangle(rect.X, rect.Bottom - 8, rect.Width, 8), Color.Black * 0.8f, true);
GUI.DrawRectangle(spriteBatch, GUI.DrawRectangle(spriteBatch,
@@ -1378,10 +1382,10 @@ namespace Barotrauma
if (inventory != null && if (inventory != null &&
!inventory.Locked && !inventory.Locked &&
Character.Controlled?.Inventory == inventory && Character.Controlled?.Inventory == inventory &&
slot.QuickUseKey != Keys.None) slot.InventoryKeyIndex != -1)
{ {
spriteBatch.Draw(slotHotkeySprite.Texture, rect.ScaleSize(1.15f), slotHotkeySprite.SourceRect, slotColor); spriteBatch.Draw(slotHotkeySprite.Texture, rect.ScaleSize(1.15f), slotHotkeySprite.SourceRect, slotColor);
GUI.DrawString(spriteBatch, rect.Location.ToVector2() + new Vector2((int)(4.25f * UIScale), (int)Math.Ceiling(-1.5f * UIScale)), slot.QuickUseKey.ToString().Substring(1, 1), Color.Black, font: GUI.HotkeyFont); GUI.DrawString(spriteBatch, rect.Location.ToVector2() + new Vector2((int)(4.25f * UIScale), (int)Math.Ceiling(-1.5f * UIScale)), GameMain.Config.InventoryKeyBind(slot.InventoryKeyIndex).Name, Color.Black, font: GUI.HotkeyFont);
} }
} }
@@ -32,6 +32,20 @@ namespace Barotrauma
private readonly Dictionary<DecorativeSprite, DecorativeSprite.State> spriteAnimState = new Dictionary<DecorativeSprite, DecorativeSprite.State>(); private readonly Dictionary<DecorativeSprite, DecorativeSprite.State> spriteAnimState = new Dictionary<DecorativeSprite, DecorativeSprite.State>();
private bool fakeBroken;
public bool FakeBroken
{
get { return fakeBroken; }
set
{
if (value != fakeBroken)
{
fakeBroken = value;
SetActiveSprite();
}
}
}
private Sprite activeSprite; private Sprite activeSprite;
public override Sprite Sprite public override Sprite Sprite
{ {
@@ -71,6 +85,10 @@ namespace Barotrauma
{ {
get get
{ {
if (!GameMain.SubEditorScreen.ShowThalamus && prefab.Category.HasFlag(MapEntityCategory.Thalamus))
{
return false;
}
return parentInventory == null && (body == null || body.Enabled) && ShowItems; return parentInventory == null && (body == null || body.Enabled) && ShowItems;
} }
} }
@@ -139,11 +157,12 @@ namespace Barotrauma
} }
} }
float displayCondition = FakeBroken ? 0.0f : condition;
for (int i = 0; i < Prefab.BrokenSprites.Count;i++) for (int i = 0; i < Prefab.BrokenSprites.Count;i++)
{ {
if (Prefab.BrokenSprites[i].FadeIn) { continue; }
float minCondition = i > 0 ? Prefab.BrokenSprites[i - i].MaxCondition : 0.0f; float minCondition = i > 0 ? Prefab.BrokenSprites[i - i].MaxCondition : 0.0f;
if (condition <= minCondition || if (displayCondition <= minCondition || displayCondition <= Prefab.BrokenSprites[i].MaxCondition)
condition <= Prefab.BrokenSprites[i].MaxCondition && !Prefab.BrokenSprites[i].FadeIn)
{ {
activeSprite = Prefab.BrokenSprites[i].Sprite; activeSprite = Prefab.BrokenSprites[i].Sprite;
break; break;
@@ -225,7 +244,9 @@ namespace Barotrauma
BrokenItemSprite fadeInBrokenSprite = null; BrokenItemSprite fadeInBrokenSprite = null;
float fadeInBrokenSpriteAlpha = 0.0f; float fadeInBrokenSpriteAlpha = 0.0f;
if (condition < Prefab.Health) float displayCondition = FakeBroken ? 0.0f : condition;
Vector2 drawOffset = Vector2.Zero;
if (displayCondition < Prefab.Health)
{ {
for (int i = 0; i < Prefab.BrokenSprites.Count; i++) for (int i = 0; i < Prefab.BrokenSprites.Count; i++)
{ {
@@ -233,16 +254,17 @@ namespace Barotrauma
{ {
float min = i > 0 ? Prefab.BrokenSprites[i - i].MaxCondition : 0.0f; float min = i > 0 ? Prefab.BrokenSprites[i - i].MaxCondition : 0.0f;
float max = Prefab.BrokenSprites[i].MaxCondition; float max = Prefab.BrokenSprites[i].MaxCondition;
fadeInBrokenSpriteAlpha = 1.0f - ((condition - min) / (max - min)); fadeInBrokenSpriteAlpha = 1.0f - ((displayCondition - min) / (max - min));
if (fadeInBrokenSpriteAlpha > 0.0f && fadeInBrokenSpriteAlpha < 1.0f) if (fadeInBrokenSpriteAlpha > 0.0f && fadeInBrokenSpriteAlpha <= 1.0f)
{ {
fadeInBrokenSprite = Prefab.BrokenSprites[i]; fadeInBrokenSprite = Prefab.BrokenSprites[i];
} }
continue; continue;
} }
if (condition <= Prefab.BrokenSprites[i].MaxCondition) if (displayCondition <= Prefab.BrokenSprites[i].MaxCondition)
{ {
activeSprite = Prefab.BrokenSprites[i].Sprite; activeSprite = Prefab.BrokenSprites[i].Sprite;
drawOffset = Prefab.BrokenSprites[i].Offset.ToVector2() * Scale;
break; break;
} }
} }
@@ -264,9 +286,13 @@ namespace Barotrauma
{ {
if (prefab.ResizeHorizontal || prefab.ResizeVertical) if (prefab.ResizeHorizontal || prefab.ResizeVertical)
{ {
activeSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height), color: color, Vector2 size = new Vector2(rect.Width, rect.Height);
activeSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + drawOffset,
size, color: color,
textureScale: Vector2.One * Scale,
depth: depth); depth: depth);
fadeInBrokenSprite?.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height), color: color * fadeInBrokenSpriteAlpha, fadeInBrokenSprite?.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + fadeInBrokenSprite.Offset.ToVector2() * Scale, size, color: color * fadeInBrokenSpriteAlpha,
textureScale: Vector2.One * Scale,
depth: depth - 0.000001f); depth: depth - 0.000001f);
foreach (var decorativeSprite in Prefab.DecorativeSprites) foreach (var decorativeSprite in Prefab.DecorativeSprites)
{ {
@@ -280,8 +306,8 @@ namespace Barotrauma
} }
else else
{ {
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y), color, SpriteRotation, Scale, activeSprite.effects, depth); activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, SpriteRotation, Scale, activeSprite.effects, depth);
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y), color * fadeInBrokenSpriteAlpha, SpriteRotation, Scale, activeSprite.effects, depth - 0.000001f); fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, SpriteRotation, Scale, activeSprite.effects, depth - 0.000001f);
foreach (var decorativeSprite in Prefab.DecorativeSprites) foreach (var decorativeSprite in Prefab.DecorativeSprites)
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
@@ -302,19 +328,25 @@ namespace Barotrauma
if (holdable.Picker.SelectedItems[0] == this) if (holdable.Picker.SelectedItems[0] == this)
{ {
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightHand); Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightHand);
depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() + depthStep * 2; if (holdLimb != null)
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
{ {
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Max(wearableSprite.Sprite.Depth + depthStep, depth); } depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() + depthStep * 2;
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
{
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Max(wearableSprite.Sprite.Depth + depthStep, depth); }
}
} }
} }
else if (holdable.Picker.SelectedItems[1] == this) else if (holdable.Picker.SelectedItems[1] == this)
{ {
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftHand); Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftHand);
depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() - depthStep * 2; if (holdLimb != null)
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
{ {
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Min(wearableSprite.Sprite.Depth - depthStep, depth); } depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() - depthStep * 2;
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
{
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Min(wearableSprite.Sprite.Depth - depthStep, depth); }
}
} }
} }
} }
@@ -1263,6 +1295,14 @@ namespace Barotrauma
inventory = container.Inventory; inventory = container.Inventory;
} }
} }
else if (inventoryOwner == null)
{
DebugConsole.ThrowError($"Failed to spawn item \"{(itemIdentifier ?? "null")}\" in the inventory of an entity with the ID {inventoryId} (entity not found)");
}
else
{
DebugConsole.ThrowError($"Failed to spawn item \"{(itemIdentifier ?? "null")}\" in the inventory of \"{inventoryOwner} ({inventoryOwner.ID})\" (invalid entity, should be an item or a character)");
}
} }
var item = new Item(itemPrefab, pos, sub) var item = new Item(itemPrefab, pos, sub)
@@ -14,12 +14,14 @@ namespace Barotrauma
public readonly float MaxCondition; public readonly float MaxCondition;
public readonly Sprite Sprite; public readonly Sprite Sprite;
public readonly bool FadeIn; public readonly bool FadeIn;
public readonly Point Offset;
public BrokenItemSprite(Sprite sprite, float maxCondition, bool fadeIn) public BrokenItemSprite(Sprite sprite, float maxCondition, bool fadeIn, Point offset)
{ {
Sprite = sprite; Sprite = sprite;
MaxCondition = MathHelper.Clamp(maxCondition, 0.0f, 100.0f); MaxCondition = MathHelper.Clamp(maxCondition, 0.0f, 100.0f);
FadeIn = fadeIn; FadeIn = fadeIn;
Offset = offset;
} }
} }
@@ -60,10 +60,10 @@ namespace Barotrauma
var particle = GameMain.ParticleManager.CreateParticle("flame", var particle = GameMain.ParticleManager.CreateParticle("flame",
particlePos, particleVel, 0.0f, hull); particlePos, particleVel, 0.0f, hull);
if (particle == null) continue; if (particle == null) { continue; }
//make some of the particles create another firesource when they enter another hull //make some of the particles create another firesource when they enter another hull
if (Rand.Int(20) == 1) particle.OnChangeHull = onChangeHull; if (Rand.Int(20) == 1) { particle.OnChangeHull = onChangeHull; }
particle.Size *= MathHelper.Clamp(size.X / 60.0f * Math.Max(hull.Oxygen / hull.Volume, 0.4f), 0.5f, 1.0f); particle.Size *= MathHelper.Clamp(size.X / 60.0f * Math.Max(hull.Oxygen / hull.Volume, 0.4f), 0.5f, 1.0f);
@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -24,7 +25,7 @@ namespace Barotrauma
public override void Draw(SpriteBatch sb, bool editing, bool back = true) public override void Draw(SpriteBatch sb, bool editing, bool back = true)
{ {
if (!GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f) if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
{ {
Vector2 center = new Vector2(WorldRect.X + rect.Width / 2.0f, -(WorldRect.Y - rect.Height / 2.0f)); Vector2 center = new Vector2(WorldRect.X + rect.Width / 2.0f, -(WorldRect.Y - rect.Height / 2.0f));
GUI.DrawLine(sb, center, center + new Vector2(flowForce.X, -flowForce.Y) / 10.0f, GUI.Style.Red); GUI.DrawLine(sb, center, center + new Vector2(flowForce.X, -flowForce.Y) / 10.0f, GUI.Style.Red);
@@ -121,24 +122,36 @@ namespace Barotrauma
partial void EmitParticles(float deltaTime) partial void EmitParticles(float deltaTime)
{ {
if (flowTargetHull == null) return; if (flowTargetHull == null) { return; }
if (linkedTo.Count == 2 && linkedTo[0] is Hull hull1 && linkedTo[1] is Hull hull2)
{
//no flow particles between linked hulls (= rooms consisting of multiple hulls)
if (hull1.linkedTo.Contains(hull2)) { return; }
if (hull1.linkedTo.Any(h => h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2))) { return; }
if (hull2.linkedTo.Any(h => h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2))) { return; }
}
Vector2 pos = Position; Vector2 pos = Position;
if (IsHorizontal) if (IsHorizontal)
{ {
pos.X += Math.Sign(flowForce.X); pos.X += Math.Sign(flowForce.X);
pos.Y = MathHelper.Clamp((higherSurface + lowerSurface) / 2.0f, rect.Y - rect.Height, rect.Y) + 10; pos.Y = MathHelper.Clamp(Rand.Range(higherSurface, lowerSurface), rect.Y - rect.Height, rect.Y);
} }
else else
{ {
pos.Y += Math.Sign(flowForce.Y) * rect.Height / 2.0f; pos.Y += Math.Sign(flowForce.Y) * rect.Height / 2.0f;
} }
//spawn less particles when there's already a large number of them
float particleAmountMultiplier = 1.0f - GameMain.ParticleManager.ParticleCount / (float)GameMain.ParticleManager.MaxParticles;
particleAmountMultiplier *= particleAmountMultiplier;
//light dripping //light dripping
if (open < 0.2f && LerpedFlowForce.LengthSquared() > 100.0f) if (open < 0.2f && LerpedFlowForce.LengthSquared() > 100.0f)
{ {
particleTimer += deltaTime; particleTimer += deltaTime;
float particlesPerSec = open * 100.0f; float particlesPerSec = open * 100.0f * particleAmountMultiplier;
float emitInterval = 1.0f / particlesPerSec; float emitInterval = 1.0f / particlesPerSec;
while (particleTimer > emitInterval) while (particleTimer > emitInterval)
{ {
@@ -174,12 +187,13 @@ namespace Barotrauma
particleTimer += deltaTime; particleTimer += deltaTime;
if (IsHorizontal) if (IsHorizontal)
{ {
float particlesPerSec = open * rect.Height * 0.1f; float particlesPerSec = open * rect.Height * 0.1f * particleAmountMultiplier;
if (openedTimer > 0.0f) { particlesPerSec *= 1.0f + openedTimer * 10.0f; }
float emitInterval = 1.0f / particlesPerSec; float emitInterval = 1.0f / particlesPerSec;
while (particleTimer > emitInterval) while (particleTimer > emitInterval)
{ {
Vector2 velocity = new Vector2( Vector2 velocity = new Vector2(
MathHelper.Clamp(flowForce.X, -5000.0f, 5000.0f) * Rand.Range(0.5f, 0.7f), MathHelper.Clamp(flowForce.X, -5000.0f, 5000.0f) * Rand.Range(0.5f, 0.7f),
flowForce.Y * Rand.Range(0.5f, 0.7f)); flowForce.Y * Rand.Range(0.5f, 0.7f));
if (flowTargetHull.WaterVolume < flowTargetHull.Volume * 0.95f) if (flowTargetHull.WaterVolume < flowTargetHull.Volume * 0.95f)
@@ -191,11 +205,11 @@ namespace Barotrauma
if (particle != null) if (particle != null)
{ {
particle.Size = particle.Size * Math.Min(Math.Abs(flowForce.X / 1000.0f), 5.0f); particle.Size *= Math.Min(Math.Abs(flowForce.X / 500.0f), 5.0f);
} }
} }
if (Math.Abs(flowForce.X) > 300.0f) if (Math.Abs(flowForce.X) > 300.0f && flowTargetHull.WaterVolume > flowTargetHull.Volume * 0.1f)
{ {
pos.X += Math.Sign(flowForce.X) * 10.0f; pos.X += Math.Sign(flowForce.X) * 10.0f;
if (rect.Height < 32) if (rect.Height < 32)
@@ -211,7 +225,7 @@ namespace Barotrauma
GameMain.ParticleManager.CreateParticle( GameMain.ParticleManager.CreateParticle(
"bubbles", "bubbles",
Submarine == null ? pos : pos + Submarine.Position, Submarine == null ? pos : pos + Submarine.Position,
flowForce / 10.0f, 0, flowTargetHull); velocity, 0, flowTargetHull);
} }
particleTimer -= emitInterval; particleTimer -= emitInterval;
} }
@@ -220,7 +234,7 @@ namespace Barotrauma
{ {
if (Math.Sign(flowTargetHull.Rect.Y - rect.Y) != Math.Sign(lerpedFlowForce.Y)) return; if (Math.Sign(flowTargetHull.Rect.Y - rect.Y) != Math.Sign(lerpedFlowForce.Y)) return;
float particlesPerSec = open * rect.Width * 0.3f; float particlesPerSec = open * rect.Width * 0.3f * particleAmountMultiplier;
float emitInterval = 1.0f / particlesPerSec; float emitInterval = 1.0f / particlesPerSec;
while (particleTimer > emitInterval) while (particleTimer > emitInterval)
{ {
@@ -237,7 +251,7 @@ namespace Barotrauma
velocity, 0, FlowTargetHull); velocity, 0, FlowTargetHull);
if (splash != null) splash.Size = splash.Size * MathHelper.Clamp(rect.Width / 50.0f, 0.8f, 4.0f); if (splash != null) splash.Size = splash.Size * MathHelper.Clamp(rect.Width / 50.0f, 0.8f, 4.0f);
} }
if (Math.Abs(flowForce.Y) > 190.0f && Rand.Range(0.0f, 1.0f) < 0.3f) if (Math.Abs(flowForce.Y) > 190.0f && Rand.Range(0.0f, 1.0f) < 0.3f && flowTargetHull.WaterVolume > flowTargetHull.Volume * 0.1f)
{ {
GameMain.ParticleManager.CreateParticle( GameMain.ParticleManager.CreateParticle(
"bubbles", "bubbles",
@@ -14,7 +14,7 @@ namespace Barotrauma
{ {
public const int MaxDecalsPerHull = 10; public const int MaxDecalsPerHull = 10;
private List<Decal> decals = new List<Decal>(); private readonly List<Decal> decals = new List<Decal>();
private float serverUpdateDelay; private float serverUpdateDelay;
private float remoteWaterVolume, remoteOxygenPercentage; private float remoteWaterVolume, remoteOxygenPercentage;
@@ -23,6 +23,8 @@ namespace Barotrauma
private bool networkUpdatePending; private bool networkUpdatePending;
private float networkUpdateTimer; private float networkUpdateTimer;
private double lastAmbientLightEditTime;
public override bool SelectableInEditor public override bool SelectableInEditor
{ {
get get
@@ -232,33 +234,36 @@ namespace Barotrauma
return; return;
} }
/*if (!Visible) if (!ShowHulls && !GameMain.DebugDraw) { return; }
if (!editing && (!GameMain.DebugDraw || Screen.Selected.Cam.Zoom < 0.1f)) { return; }
float alpha = 1.0f;
float hideTimeAfterEdit = 3.0f;
if (lastAmbientLightEditTime > Timing.TotalTime - hideTimeAfterEdit * 2.0f)
{ {
drawRect = alpha = Math.Min((float)(Timing.TotalTime - lastAmbientLightEditTime) / hideTimeAfterEdit - 1.0f, 1.0f);
Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height); }
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X, -drawRect.Y),
new Vector2(rect.Width, rect.Height),
Color.Black, true,
0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f));
}*/
if (!ShowHulls && !GameMain.DebugDraw) return;
if (!editing && (!GameMain.DebugDraw || Screen.Selected.Cam.Zoom < 0.1f)) return;
Rectangle drawRect = Rectangle drawRect =
Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height); Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height);
if ((IsSelected || IsHighlighted) && editing)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X, -drawRect.Y),
new Vector2(rect.Width, rect.Height),
(IsHighlighted ? Color.LightBlue * 0.8f : GUI.Style.Red * 0.5f) * alpha, false, 0, (int)Math.Max(5.0f / Screen.Selected.Cam.Zoom, 1.0f));
}
GUI.DrawRectangle(spriteBatch, GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X, -drawRect.Y), new Vector2(drawRect.X, -drawRect.Y),
new Vector2(rect.Width, rect.Height), new Vector2(rect.Width, rect.Height),
Color.Blue, false, (ID % 255) * 0.000001f, (int)Math.Max((1.5f / Screen.Selected.Cam.Zoom), 1.0f)); Color.Blue * alpha, false, (ID % 255) * 0.000001f, (int)Math.Max(1.5f / Screen.Selected.Cam.Zoom, 1.0f));
GUI.DrawRectangle(spriteBatch, GUI.DrawRectangle(spriteBatch,
new Rectangle(drawRect.X, -drawRect.Y, rect.Width, rect.Height), new Rectangle(drawRect.X, -drawRect.Y, rect.Width, rect.Height),
GUI.Style.Red * ((100.0f - OxygenPercentage) / 400.0f), true, 0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f)); GUI.Style.Red * ((100.0f - OxygenPercentage) / 400.0f) * alpha, true, 0, (int)Math.Max(1.5f / Screen.Selected.Cam.Zoom, 1.0f));
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
{ {
@@ -277,10 +282,12 @@ namespace Barotrauma
foreach (FireSource fs in FireSources) foreach (FireSource fs in FireSources)
{ {
Rectangle fireSourceRect = new Rectangle((int)fs.WorldPosition.X, -(int)fs.WorldPosition.Y, (int)fs.Size.X, (int)fs.Size.Y); Rectangle fireSourceRect = new Rectangle((int)fs.WorldPosition.X, -(int)fs.WorldPosition.Y, (int)fs.Size.X, (int)fs.Size.Y);
GUI.DrawRectangle(spriteBatch, fireSourceRect, GUI.Style.Orange, false, 0, 5); GUI.DrawRectangle(spriteBatch, fireSourceRect, GUI.Style.Red, false, 0, 5);
GUI.DrawRectangle(spriteBatch, new Rectangle(fireSourceRect.X - (int)fs.DamageRange, fireSourceRect.Y, fireSourceRect.Width + (int)fs.DamageRange * 2, fireSourceRect.Height), GUI.Style.Orange, false, 0, 5);
//GUI.DrawRectangle(spriteBatch, new Rectangle((int)fs.LastExtinguishPos.X, (int)-fs.LastExtinguishPos.Y, 5,5), Color.Yellow, true); //GUI.DrawRectangle(spriteBatch, new Rectangle((int)fs.LastExtinguishPos.X, (int)-fs.LastExtinguishPos.Y, 5,5), Color.Yellow, true);
} }
/*GUI.DrawLine(spriteBatch, new Vector2(drawRect.X, -WorldSurface), new Vector2(drawRect.Right, -WorldSurface), Color.Cyan * 0.5f); /*GUI.DrawLine(spriteBatch, new Vector2(drawRect.X, -WorldSurface), new Vector2(drawRect.Right, -WorldSurface), Color.Cyan * 0.5f);
for (int i = 0; i < waveY.Length - 1; i++) for (int i = 0; i < waveY.Length - 1; i++)
{ {
@@ -290,19 +297,10 @@ namespace Barotrauma
}*/ }*/
} }
if ((IsSelected || IsHighlighted) && editing)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X + 5, -drawRect.Y + 5),
new Vector2(rect.Width - 10, rect.Height - 10),
IsHighlighted ? Color.LightBlue * 0.5f : GUI.Style.Red * 0.5f, true, 0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f));
}
foreach (MapEntity e in linkedTo) foreach (MapEntity e in linkedTo)
{ {
if (e is Hull) if (e is Hull linkedHull)
{ {
Hull linkedHull = (Hull)e;
Rectangle connectedHullRect = e.Submarine == null ? Rectangle connectedHullRect = e.Submarine == null ?
linkedHull.rect : linkedHull.rect :
new Rectangle( new Rectangle(
@@ -326,22 +324,22 @@ namespace Barotrauma
} }
} }
public static void UpdateVertices(GraphicsDevice graphicsDevice, Camera cam, WaterRenderer renderer) public static void UpdateVertices(Camera cam, WaterRenderer renderer)
{ {
foreach (EntityGrid entityGrid in EntityGrids) foreach (EntityGrid entityGrid in EntityGrids)
{ {
if (entityGrid.WorldRect.X > cam.WorldView.Right || entityGrid.WorldRect.Right < cam.WorldView.X) continue; if (entityGrid.WorldRect.X > cam.WorldView.Right || entityGrid.WorldRect.Right < cam.WorldView.X) { continue; }
if (entityGrid.WorldRect.Y - entityGrid.WorldRect.Height > cam.WorldView.Y || entityGrid.WorldRect.Y < cam.WorldView.Y - cam.WorldView.Height) continue; if (entityGrid.WorldRect.Y - entityGrid.WorldRect.Height > cam.WorldView.Y || entityGrid.WorldRect.Y < cam.WorldView.Y - cam.WorldView.Height) { continue; }
var allEntities = entityGrid.GetAllEntities(); var allEntities = entityGrid.GetAllEntities();
foreach (Hull hull in allEntities) foreach (Hull hull in allEntities)
{ {
hull.UpdateVertices(graphicsDevice, cam, entityGrid, renderer); hull.UpdateVertices(cam, entityGrid, renderer);
} }
} }
} }
private void UpdateVertices(GraphicsDevice graphicsDevice, Camera cam, EntityGrid entityGrid, WaterRenderer renderer) private void UpdateVertices(Camera cam, EntityGrid entityGrid, WaterRenderer renderer)
{ {
Vector2 submarinePos = Submarine == null ? Vector2.Zero : Submarine.DrawPosition; Vector2 submarinePos = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
@@ -554,11 +552,10 @@ namespace Barotrauma
remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);
bool hasFireSources = message.ReadBoolean(); bool hasFireSources = message.ReadBoolean();
int fireSourceCount = 0;
remoteFireSources = new List<Vector3>(); remoteFireSources = new List<Vector3>();
if (hasFireSources) if (hasFireSources)
{ {
fireSourceCount = message.ReadRangedInteger(0, 16); int fireSourceCount = message.ReadRangedInteger(0, 16);
for (int i = 0; i < fireSourceCount; i++) for (int i = 0; i < fireSourceCount; i++)
{ {
remoteFireSources.Add(new Vector3( remoteFireSources.Add(new Vector3(
@@ -18,6 +18,7 @@ namespace Barotrauma
foreach (Pair<MapEntityPrefab, Rectangle> entity in DisplayEntities) foreach (Pair<MapEntityPrefab, Rectangle> entity in DisplayEntities)
{ {
if (entity.First is CoreEntityPrefab) { continue; }
Rectangle drawRect = entity.Second; Rectangle drawRect = entity.Second;
drawRect = new Rectangle( drawRect = new Rectangle(
(int)(drawRect.X * scale) + drawArea.Center.X, (int)((drawRect.Y) * scale) - drawArea.Center.Y, (int)(drawRect.X * scale) + drawArea.Center.X, (int)((drawRect.Y) * scale) - drawArea.Center.Y,
@@ -93,9 +93,9 @@ namespace Barotrauma.Lights
public static BasicEffect shadowEffect; public static BasicEffect shadowEffect;
public static BasicEffect penumbraEffect; public static BasicEffect penumbraEffect;
private Segment[] segments = new Segment[4]; private readonly Segment[] segments = new Segment[4];
private SegmentPoint[] vertices = new SegmentPoint[4]; private readonly SegmentPoint[] vertices = new SegmentPoint[4];
private SegmentPoint[] losVertices = new SegmentPoint[4]; private readonly SegmentPoint[] losVertices = new SegmentPoint[4];
private readonly bool[] backFacing; private readonly bool[] backFacing;
private readonly bool[] ignoreEdge; private readonly bool[] ignoreEdge;
@@ -106,6 +106,8 @@ namespace Barotrauma.Lights
public VertexPositionTexture[] PenumbraVertices { get; private set; } public VertexPositionTexture[] PenumbraVertices { get; private set; }
public int ShadowVertexCount { get; private set; } public int ShadowVertexCount { get; private set; }
private readonly HashSet<ConvexHull> overlappingHulls = new HashSet<ConvexHull>();
public MapEntity ParentEntity { get; private set; } public MapEntity ParentEntity { get; private set; }
private bool enabled; private bool enabled;
@@ -176,7 +178,7 @@ namespace Barotrauma.Lights
if (door != null) { isHorizontal = door.IsHorizontal; } if (door != null) { isHorizontal = door.IsHorizontal; }
} }
var chList = HullLists.Find(x => x.Submarine == parent.Submarine); var chList = HullLists.Find(h => h.Submarine == parent.Submarine);
if (chList == null) if (chList == null)
{ {
chList = new ConvexHullList(parent.Submarine); chList = new ConvexHullList(parent.Submarine);
@@ -194,10 +196,12 @@ namespace Barotrauma.Lights
private void MergeOverlappingSegments(ConvexHull ch) private void MergeOverlappingSegments(ConvexHull ch)
{ {
if (ch == this) return; if (ch == this) { return; }
if (isHorizontal == ch.isHorizontal) if (isHorizontal == ch.isHorizontal)
{ {
if (BoundingBox == ch.BoundingBox) { return; }
//hide segments that are roughly at the some position as some other segment (e.g. the ends of two adjacent wall pieces) //hide segments that are roughly at the some position as some other segment (e.g. the ends of two adjacent wall pieces)
float mergeDist = 32; float mergeDist = 32;
float mergeDistSqr = mergeDist * mergeDist; float mergeDistSqr = mergeDist * mergeDist;
@@ -206,6 +210,7 @@ namespace Barotrauma.Lights
for (int j = 0; j < ch.segments.Length; j++) for (int j = 0; j < ch.segments.Length; j++)
{ {
if (segments[i].IsHorizontal != ch.segments[j].IsHorizontal) { continue; } if (segments[i].IsHorizontal != ch.segments[j].IsHorizontal) { continue; }
if (ignoreEdge[i] || ch.ignoreEdge[j]) { continue; }
//the segments must be at different sides of the convex hulls to be merged //the segments must be at different sides of the convex hulls to be merged
//(e.g. the right edge of a wall piece and the left edge of another one) //(e.g. the right edge of a wall piece and the left edge of another one)
@@ -247,6 +252,7 @@ namespace Barotrauma.Lights
p.Y >= ch.BoundingBox.Y && p.Y <= ch.BoundingBox.Bottom) p.Y >= ch.BoundingBox.Y && p.Y <= ch.BoundingBox.Bottom)
{ {
ignoreEdge[i] = true; ignoreEdge[i] = true;
overlappingHulls.Add(ch);
} }
} }
} }
@@ -283,11 +289,25 @@ namespace Barotrauma.Lights
} }
else else
{ {
losVertices[startPointIndex].Pos = segment2.ConvexHull.losVertices[startPoint2Index].Pos = if (Vector2.DistanceSquared(losVertices[startPointIndex].Pos, segment1.Start.Pos) <
(segment1.Start.Pos + segment2.End.Pos) / 2.0f; Vector2.DistanceSquared(losVertices[startPointIndex].Pos, segment1.End.Pos))
losVertices[endPointIndex].Pos = segment2.ConvexHull.losVertices[endPoint2Index].Pos = {
(segment1.End.Pos + segment2.Start.Pos) / 2.0f; losVertices[startPointIndex].Pos = segment2.ConvexHull.losVertices[startPoint2Index].Pos =
(segment1.Start.Pos + segment2.End.Pos) / 2.0f;
losVertices[endPointIndex].Pos = segment2.ConvexHull.losVertices[endPoint2Index].Pos =
(segment1.End.Pos + segment2.Start.Pos) / 2.0f;
}
else
{
losVertices[startPointIndex].Pos = segment2.ConvexHull.losVertices[startPoint2Index].Pos =
(segment1.End.Pos + segment2.Start.Pos) / 2.0f;
losVertices[endPointIndex].Pos = segment2.ConvexHull.losVertices[endPoint2Index].Pos =
(segment1.Start.Pos + segment2.End.Pos) / 2.0f;
}
} }
overlappingHulls.Add(segment2.ConvexHull);
segment2.ConvexHull.overlappingHulls.Add(this);
} }
public void Rotate(Vector2 origin, float amount) public void Rotate(Vector2 origin, float amount)
@@ -328,7 +348,50 @@ namespace Barotrauma.Lights
LastVertexChangeTime = (float)Timing.TotalTime; LastVertexChangeTime = (float)Timing.TotalTime;
overlappingHulls.Clear();
for (int i = 0; i < 4; i++)
{
ignoreEdge[i] = false;
}
CalculateDimensions(); CalculateDimensions();
if (ParentEntity == null) { return; }
var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine);
if (chList != null)
{
overlappingHulls.Clear();
foreach (ConvexHull ch in chList.List)
{
MergeOverlappingSegments(ch);
ch.MergeOverlappingSegments(this);
}
}
}
public static void RecalculateAll(Submarine sub)
{
var chList = HullLists.Find(h => h.Submarine == sub);
if (chList != null)
{
foreach (ConvexHull ch in chList.List)
{
ch.overlappingHulls.Clear();
for (int i = 0; i < 4; i++)
{
ch.ignoreEdge[i] = false;
}
}
for (int i = 0; i < chList.List.Count; i++)
{
for (int j = i + 1; j < chList.List.Count; j++)
{
chList.List[i].MergeOverlappingSegments(chList.List[j]);
chList.List[j].MergeOverlappingSegments(chList.List[i]);
}
}
}
} }
public void SetVertices(Vector2[] points, Matrix? rotationMatrix = null) public void SetVertices(Vector2[] points, Matrix? rotationMatrix = null)
@@ -348,6 +411,8 @@ namespace Barotrauma.Lights
ignoreEdge[i] = false; ignoreEdge[i] = false;
} }
overlappingHulls.Clear();
int margin = 0; int margin = 0;
if (Math.Abs(points[0].X - points[2].X) < Math.Abs(points[0].Y - points[2].Y)) if (Math.Abs(points[0].X - points[2].X) < Math.Abs(points[0].Y - points[2].Y))
{ {
@@ -381,9 +446,10 @@ namespace Barotrauma.Lights
if (ParentEntity == null) return; if (ParentEntity == null) return;
var chList = HullLists.Find(x => x.Submarine == ParentEntity.Submarine); var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine);
if (chList != null) if (chList != null)
{ {
overlappingHulls.Clear();
foreach (ConvexHull ch in chList.List) foreach (ConvexHull ch in chList.List)
{ {
MergeOverlappingSegments(ch); MergeOverlappingSegments(ch);
@@ -484,8 +550,8 @@ namespace Barotrauma.Lights
//find beginning and ending vertices which //find beginning and ending vertices which
//belong to the shadow //belong to the shadow
int startingIndex = 0; int startingIndex = -1;
int endingIndex = 0; int endingIndex = -1;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
int currentEdge = i; int currentEdge = i;
@@ -498,6 +564,8 @@ namespace Barotrauma.Lights
startingIndex = nextEdge; startingIndex = nextEdge;
} }
if (startingIndex == -1 || endingIndex == -1) { return; }
//nr of vertices that are in the shadow //nr of vertices that are in the shadow
if (endingIndex > startingIndex) if (endingIndex > startingIndex)
ShadowVertexCount = endingIndex - startingIndex + 1; ShadowVertexCount = endingIndex - startingIndex + 1;
@@ -663,7 +731,7 @@ namespace Barotrauma.Lights
public void Remove() public void Remove()
{ {
var chList = HullLists.Find(x => x.Submarine == ParentEntity.Submarine); var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine);
if (chList != null) if (chList != null)
{ {
@@ -672,8 +740,19 @@ namespace Barotrauma.Lights
{ {
HullLists.Remove(chList); HullLists.Remove(chList);
} }
foreach (ConvexHull ch2 in overlappingHulls)
{
for (int i = 0; i < 4; i++)
{
ch2.ignoreEdge[i] = false;
}
ch2.overlappingHulls.Remove(this);
foreach (ConvexHull ch in chList.List)
{
ch.MergeOverlappingSegments(ch2);
}
}
} }
} }
} }
} }
@@ -11,18 +11,6 @@ namespace Barotrauma.Lights
{ {
class LightManager class LightManager
{ {
private const float AmbientLightUpdateInterval = 0.2f;
private const float AmbientLightFalloff = 0.8f;
/// <summary>
/// Enables a feature that makes lights inside the hull increase the brightness of the entire hull
/// and adjacent ones to some extent, if there are gaps for the lights to pass through.
/// Prevents unnaturally dark looking shadows in otherwise well-lit submarines, but disabled at least for
/// the time being because it makes the lighting behave unpredictably and may cause rooms to appear
/// excessively bright if different lighting conditions aren't tested and accounted for.
/// </summary>
private static readonly bool UseHullSpecificAmbientLight = false;
public static Entity ViewTarget { get; set; } public static Entity ViewTarget { get; set; }
private float currLightMapScale; private float currLightMapScale;
@@ -57,7 +45,7 @@ namespace Barotrauma.Lights
public Effect LosEffect { get; private set; } public Effect LosEffect { get; private set; }
public Effect SolidColorEffect { get; private set; } public Effect SolidColorEffect { get; private set; }
private List<LightSource> lights; private readonly List<LightSource> lights;
public bool LosEnabled = true; public bool LosEnabled = true;
public LosMode LosMode = LosMode.Transparent; public LosMode LosMode = LosMode.Transparent;
@@ -66,12 +54,7 @@ namespace Barotrauma.Lights
public bool ObstructVision; public bool ObstructVision;
private Texture2D visionCircle; private readonly Texture2D visionCircle;
private Dictionary<Hull, Color> hullAmbientLights;
private Dictionary<Hull, Color> smoothedHullAmbientLights;
private float ambientLightUpdateTimer;
public IEnumerable<LightSource> Lights public IEnumerable<LightSource> Lights
{ {
@@ -80,7 +63,7 @@ namespace Barotrauma.Lights
public LightManager(GraphicsDevice graphics, ContentManager content) public LightManager(GraphicsDevice graphics, ContentManager content)
{ {
lights = new List<LightSource>(); lights = new List<LightSource>(100);
AmbientLight = new Color(20, 20, 20, 255); AmbientLight = new Color(20, 20, 20, 255);
@@ -114,9 +97,6 @@ namespace Barotrauma.Lights
}; };
} }
}); });
hullAmbientLights = new Dictionary<Hull, Color>();
smoothedHullAmbientLights = new Dictionary<Hull, Color>();
} }
private void CreateRenderTargets(GraphicsDevice graphics) private void CreateRenderTargets(GraphicsDevice graphics)
@@ -167,42 +147,11 @@ namespace Barotrauma.Lights
} }
} }
public void Update(float deltaTime) private readonly List<LightSource> activeLights = new List<LightSource>(capacity: 100);
{
if (UseHullSpecificAmbientLight)
{
if (ambientLightUpdateTimer > 0.0f)
{
ambientLightUpdateTimer -= deltaTime;
}
else
{
CalculateAmbientLights();
ambientLightUpdateTimer = AmbientLightUpdateInterval;
}
foreach (Hull hull in hullAmbientLights.Keys)
{
if (!smoothedHullAmbientLights.ContainsKey(hull))
{
smoothedHullAmbientLights.Add(hull, Color.TransparentBlack);
}
}
foreach (Hull hull in smoothedHullAmbientLights.Keys.ToList())
{
Color targetColor = Color.TransparentBlack;
hullAmbientLights.TryGetValue(hull, out targetColor);
smoothedHullAmbientLights[hull] = Color.Lerp(smoothedHullAmbientLights[hull], targetColor, deltaTime);
}
}
}
private List<LightSource> activeLights = new List<LightSource>(capacity: 100);
public void UpdateLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null) public void UpdateLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null)
{ {
if (!LightingEnabled) return; if (!LightingEnabled) { return; }
if (Math.Abs(currLightMapScale - GameMain.Config.LightMapScale) > 0.01f) if (Math.Abs(currLightMapScale - GameMain.Config.LightMapScale) > 0.01f)
{ {
@@ -261,16 +210,14 @@ namespace Barotrauma.Lights
//draw background lights //draw background lights
//--------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------
graphics.SetRenderTarget(LightMap); graphics.SetRenderTarget(LightMap);
graphics.Clear(Color.Black); graphics.Clear(AmbientLight);
graphics.BlendState = BlendState.Additive; graphics.BlendState = BlendState.Additive;
bool backgroundSpritesDrawn = false;
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
foreach (LightSource light in activeLights) foreach (LightSource light in activeLights)
{ {
if (!light.IsBackground) { continue; } if (!light.IsBackground) { continue; }
light.DrawSprite(spriteBatch, cam); light.DrawSprite(spriteBatch, cam);
if (light.Color.A > 0 && light.Range > 0.0f) { light.DrawLightVolume(spriteBatch, lightEffect, transform); } if (light.Color.A > 0 && light.Range > 0.0f) { light.DrawLightVolume(spriteBatch, lightEffect, transform); }
backgroundSpritesDrawn = true;
} }
GameMain.ParticleManager.Draw(spriteBatch, true, null, Particles.ParticleBlendState.Additive); GameMain.ParticleManager.Draw(spriteBatch, true, null, Particles.ParticleBlendState.Additive);
spriteBatch.End(); spriteBatch.End();
@@ -278,33 +225,34 @@ namespace Barotrauma.Lights
//draw a black rectangle on hulls to hide background lights behind subs //draw a black rectangle on hulls to hide background lights behind subs
//--------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------
Dictionary<Hull, Rectangle> visibleHulls = null; if (backgroundObstructor != null)
if (backgroundSpritesDrawn)
{ {
if (backgroundObstructor != null) spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
{ spriteBatch.Draw(backgroundObstructor, new Rectangle(0, 0,
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); (int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.Black);
spriteBatch.Draw(backgroundObstructor, new Rectangle(0, 0,
(int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.Black);
spriteBatch.End();
}
visibleHulls = GetVisibleHulls(cam);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, transformMatrix: spriteBatchTransform);
foreach (Rectangle drawRect in visibleHulls.Values)
{
//TODO: draw some sort of smoothed rectangle
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X, -drawRect.Y),
new Vector2(drawRect.Width, drawRect.Height),
Color.Black, true);
}
spriteBatch.End(); spriteBatch.End();
graphics.BlendState = BlendState.Additive;
} }
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, transformMatrix: spriteBatchTransform);
Dictionary<Hull, Rectangle> visibleHulls = GetVisibleHulls(cam);
foreach (KeyValuePair<Hull, Rectangle> hull in visibleHulls)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(hull.Value.X, -hull.Value.Y),
new Vector2(hull.Value.Width, hull.Value.Height),
hull.Key.AmbientLight == Color.TransparentBlack ? Color.Black : hull.Key.AmbientLight.Multiply(hull.Key.AmbientLight.A / 255.0f), true);
}
spriteBatch.End();
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"];
SolidColorEffect.Parameters["color"].SetValue(AmbientLight.ToVector4());
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform, effect: SolidColorEffect);
Submarine.DrawDamageable(spriteBatch, null);
spriteBatch.End();
graphics.BlendState = BlendState.Additive;
//draw the focused item and character to highlight them, //draw the focused item and character to highlight them,
//and light sprites (done before drawing the actual light volumes so we can make characters obstruct the highlights and sprites) //and light sprites (done before drawing the actual light volumes so we can make characters obstruct the highlights and sprites)
//--------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------
@@ -327,33 +275,37 @@ namespace Barotrauma.Lights
//draw characters to obstruct the highlighted items/characters and light sprites //draw characters to obstruct the highlighted items/characters and light sprites
//--------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"]; SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidVertexColor"];
SolidColorEffect.Parameters["color"].SetValue(Color.Black.ToVector4());
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, effect: SolidColorEffect, transformMatrix: spriteBatchTransform); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
foreach (Character character in Character.CharacterList) foreach (Character character in Character.CharacterList)
{ {
if (character.CurrentHull == null || !character.Enabled) continue; if (character.CurrentHull == null || !character.Enabled) { continue; }
if (Character.Controlled?.FocusedCharacter == character) continue; if (Character.Controlled?.FocusedCharacter == character) { continue; }
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
foreach (Limb limb in character.AnimController.Limbs) foreach (Limb limb in character.AnimController.Limbs)
{ {
if (limb.DeformSprite != null) continue; if (limb.DeformSprite != null) { continue; }
limb.Draw(spriteBatch, cam, Color.Black); limb.Draw(spriteBatch, cam, lightColor);
} }
} }
spriteBatch.End(); spriteBatch.End();
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidColor"]; DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidVertexColor"];
DeformableSprite.Effect.Parameters["solidColor"].SetValue(Color.Black.ToVector4());
DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply(); DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform);
foreach (Character character in Character.CharacterList) foreach (Character character in Character.CharacterList)
{ {
if (character.CurrentHull == null || !character.Enabled) continue; if (character.CurrentHull == null || !character.Enabled) { continue; }
if (Character.Controlled?.FocusedCharacter == character) continue; if (Character.Controlled?.FocusedCharacter == character) { continue; }
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
foreach (Limb limb in character.AnimController.Limbs) foreach (Limb limb in character.AnimController.Limbs)
{ {
if (limb.DeformSprite == null) continue; if (limb.DeformSprite == null) { continue; }
limb.Draw(spriteBatch, cam, Color.Black); limb.Draw(spriteBatch, cam, lightColor);
} }
} }
spriteBatch.End(); spriteBatch.End();
@@ -364,8 +316,6 @@ namespace Barotrauma.Lights
//--------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
GUI.DrawRectangle(spriteBatch, new Rectangle(cam.WorldView.X, -cam.WorldView.Y, cam.WorldView.Width, cam.WorldView.Height), AmbientLight, isFilled: true);
spriteBatch.Draw(LimbLightMap, new Rectangle(cam.WorldView.X, -cam.WorldView.Y, cam.WorldView.Width, cam.WorldView.Height), Color.White); spriteBatch.Draw(LimbLightMap, new Rectangle(cam.WorldView.X, -cam.WorldView.Y, cam.WorldView.Width, cam.WorldView.Height), Color.White);
foreach (ElectricalDischarger discharger in ElectricalDischarger.List) foreach (ElectricalDischarger discharger in ElectricalDischarger.List)
@@ -383,23 +333,6 @@ namespace Barotrauma.Lights
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive); GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
if (UseHullSpecificAmbientLight)
{
if (visibleHulls == null)
{
visibleHulls = GetVisibleHulls(cam);
}
foreach (Hull hull in smoothedHullAmbientLights.Keys)
{
if (smoothedHullAmbientLights[hull].A < 0.01f) continue;
if (!visibleHulls.TryGetValue(hull, out Rectangle drawRect)) continue;
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X, -drawRect.Y),
new Vector2(hull.Rect.Width, hull.Rect.Height),
smoothedHullAmbientLights[hull], true);
}
}
if (Character.Controlled != null) if (Character.Controlled != null)
{ {
Vector2 haloDrawPos = Character.Controlled.DrawPosition; Vector2 haloDrawPos = Character.Controlled.DrawPosition;
@@ -601,7 +534,10 @@ namespace Barotrauma.Lights
} }
} }
penumbraVerts.AddRange(convexHull.PenumbraVertices); if (convexHull.ShadowVertexCount > 0)
{
penumbraVerts.AddRange(convexHull.PenumbraVertices);
}
} }
if (shadowVerts.Count > 0) if (shadowVerts.Count > 0)
@@ -622,86 +558,6 @@ namespace Barotrauma.Lights
graphics.SetRenderTarget(null); graphics.SetRenderTarget(null);
} }
private void CalculateAmbientLights()
{
hullAmbientLights.Clear();
foreach (LightSource light in lights)
{
if (light.Color.A < 1f || light.Range < 1.0f || light.IsBackground) continue;
var newAmbientLights = AmbientLightHulls(light);
foreach (Hull hull in newAmbientLights.Keys)
{
if (hullAmbientLights.ContainsKey(hull))
{
//hull already lit by some other light source -> add the ambient lights up
hullAmbientLights[hull] = new Color(
hullAmbientLights[hull].R + newAmbientLights[hull].R,
hullAmbientLights[hull].G + newAmbientLights[hull].G,
hullAmbientLights[hull].B + newAmbientLights[hull].B,
hullAmbientLights[hull].A + newAmbientLights[hull].A);
}
else
{
hullAmbientLights.Add(hull, newAmbientLights[hull]);
}
}
}
}
/// <summary>
/// Add ambient light to the hull the lightsource is inside + all adjacent hulls connected by a gap
/// </summary>
private Dictionary<Hull, Color> AmbientLightHulls(LightSource light)
{
Dictionary<Hull, Color> hullAmbientLight = new Dictionary<Hull, Color>();
var hull = Hull.FindHull(light.WorldPosition);
if (hull == null) return hullAmbientLight;
return AmbientLightHulls(hull, hullAmbientLight, light.Color * Math.Min(light.Range / 1000.0f, 1.0f));
}
/// <summary>
/// A flood fill algorithm that adds ambient light to all hulls the starting hull is connected to
/// </summary>
private Dictionary<Hull, Color> AmbientLightHulls(Hull hull, Dictionary<Hull, Color> hullAmbientLight, Color currColor)
{
if (hullAmbientLight.ContainsKey(hull))
{
if (hullAmbientLight[hull].A > currColor.A)
return hullAmbientLight;
else
hullAmbientLight[hull] = currColor;
}
else
{
hullAmbientLight.Add(hull, currColor);
}
Color nextHullLight = currColor * AmbientLightFalloff;
//light getting too dark to notice -> no need to spread further
if (nextHullLight.A < 20) return hullAmbientLight;
//use hashset to make sure that each hull is only included once
HashSet<Hull> hulls = new HashSet<Hull>();
foreach (Gap g in hull.ConnectedGaps)
{
if (!g.IsRoomToRoom || !g.PassAmbientLight || g.Open < 0.5f) continue;
hulls.Add((g.linkedTo[0] == hull ? g.linkedTo[1] : g.linkedTo[0]) as Hull);
}
foreach (Hull h in hulls)
{
hullAmbientLight = AmbientLightHulls(h, hullAmbientLight, nextHullLight);
}
return hullAmbientLight;
}
public void ClearLights() public void ClearLights()
{ {
lights.Clear(); lights.Clear();
@@ -16,7 +16,7 @@ namespace Barotrauma.Lights
public Dictionary<string, SerializableProperty> SerializableProperties { get; private set; } = new Dictionary<string, SerializableProperty>(); public Dictionary<string, SerializableProperty> SerializableProperties { get; private set; } = new Dictionary<string, SerializableProperty>();
[Serialize("1.0,1.0,1.0,1.0", true), Editable] [Serialize("1.0,1.0,1.0,1.0", true, alwaysUseInstanceValues: true), Editable]
public Color Color public Color Color
{ {
get; get;
@@ -25,7 +25,7 @@ namespace Barotrauma.Lights
private float range; private float range;
[Serialize(100.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2048.0f)] [Serialize(100.0f, true, alwaysUseInstanceValues: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2048.0f)]
public float Range public float Range
{ {
get { return range; } get { return range; }
@@ -331,7 +331,7 @@ namespace Barotrauma.Lights
if (lightSourceParams.DeformableLightSpriteElement != null) if (lightSourceParams.DeformableLightSpriteElement != null)
{ {
DeformableLightSprite = new DeformableSprite(lightSourceParams.DeformableLightSpriteElement); DeformableLightSprite = new DeformableSprite(lightSourceParams.DeformableLightSpriteElement, invert: true);
} }
} }
@@ -342,7 +342,7 @@ namespace Barotrauma.Lights
lightSourceParams.Persistent = true; lightSourceParams.Persistent = true;
if (lightSourceParams.DeformableLightSpriteElement != null) if (lightSourceParams.DeformableLightSpriteElement != null)
{ {
DeformableLightSprite = new DeformableSprite(lightSourceParams.DeformableLightSpriteElement); DeformableLightSprite = new DeformableSprite(lightSourceParams.DeformableLightSpriteElement, invert: true);
} }
} }
@@ -952,23 +952,44 @@ namespace Barotrauma.Lights
{ {
Vector2 origin = DeformableLightSprite.Origin; Vector2 origin = DeformableLightSprite.Origin;
Vector2 drawPos = position; Vector2 drawPos = position;
if (ParentSub != null) drawPos += ParentSub.DrawPosition; if (ParentSub != null)
{
drawPos += ParentSub.DrawPosition;
}
if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
{
origin.X = DeformableLightSprite.Sprite.SourceRect.Width - origin.X;
}
if (LightSpriteEffect == SpriteEffects.FlipVertically)
{
origin.Y = DeformableLightSprite.Sprite.SourceRect.Height - origin.Y;
}
DeformableLightSprite.Draw( DeformableLightSprite.Draw(
cam, new Vector3(drawPos, 0.0f), cam, new Vector3(drawPos, 0.0f),
origin, -Rotation, SpriteScale, origin, -Rotation, SpriteScale,
new Color(Color, lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f), new Color(Color, lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f),
LightSpriteEffect == SpriteEffects.FlipHorizontally); LightSpriteEffect == SpriteEffects.FlipVertically);
} }
if (LightSprite != null) if (LightSprite != null)
{ {
Vector2 origin = LightSprite.Origin; Vector2 origin = LightSprite.Origin;
if (LightSpriteEffect == SpriteEffects.FlipHorizontally) origin.X = LightSprite.SourceRect.Width - origin.X; if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
if (LightSpriteEffect == SpriteEffects.FlipVertically) origin.Y = LightSprite.SourceRect.Height - origin.Y; {
origin.X = LightSprite.SourceRect.Width - origin.X;
}
if (LightSpriteEffect == SpriteEffects.FlipVertically)
{
origin.Y = LightSprite.SourceRect.Height - origin.Y;
}
Vector2 drawPos = position; Vector2 drawPos = position;
if (ParentSub != null) drawPos += ParentSub.DrawPosition; if (ParentSub != null)
{
drawPos += ParentSub.DrawPosition;
}
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
LightSprite.Draw( LightSprite.Draw(
@@ -2,7 +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.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -151,7 +151,8 @@ namespace Barotrauma
{ {
selectedList.ForEach(e => selectedList.ForEach(e =>
{ {
e.Remove(); //orphaned wires may already have been removed
if (!e.Removed) { e.Remove(); }
}); });
selectedList.Clear(); selectedList.Clear();
} }
@@ -24,6 +24,10 @@ namespace Barotrauma
{ {
get get
{ {
if (!GameMain.SubEditorScreen.ShowThalamus && prefab.Category.HasFlag(MapEntityCategory.Thalamus))
{
return false;
}
return HasBody ? ShowWalls : ShowStructures; return HasBody ? ShowWalls : ShowStructures;
} }
} }
@@ -6,7 +6,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
@@ -31,6 +31,7 @@ namespace Barotrauma
Stream = sound.Stream; Stream = sound.Stream;
Range = element.GetAttributeFloat("range", 1000.0f); Range = element.GetAttributeFloat("range", 1000.0f);
Volume = element.GetAttributeFloat("volume", 1.0f); Volume = element.GetAttributeFloat("volume", 1.0f);
sound.IgnoreMuffling = element.GetAttributeBool("dontmuffle", false);
} }
} }
@@ -87,7 +88,7 @@ namespace Barotrauma
existingSound = GameMain.SoundManager.LoadSound(filename, stream); existingSound = GameMain.SoundManager.LoadSound(filename, stream);
if (existingSound == null) { return null; } if (existingSound == null) { return null; }
} }
catch (FileNotFoundException e) catch (System.IO.FileNotFoundException e)
{ {
string errorMsg = "Failed to load sound file \"" + filename + "\"."; string errorMsg = "Failed to load sound file \"" + filename + "\".";
DebugConsole.ThrowError(errorMsg, e); DebugConsole.ThrowError(errorMsg, e);
@@ -1,7 +1,7 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
@@ -19,9 +19,9 @@ namespace Barotrauma
{ {
try try
{ {
using (MemoryStream mem = new MemoryStream(Convert.FromBase64String(previewImageData))) using (System.IO.MemoryStream mem = new System.IO.MemoryStream(Convert.FromBase64String(previewImageData)))
{ {
var texture = TextureLoader.FromStream(mem, path: FilePath); var texture = TextureLoader.FromStream(mem, path: FilePath, compress: false);
if (texture == null) { throw new Exception("PreviewImage texture returned null"); } if (texture == null) { throw new Exception("PreviewImage texture returned null"); }
PreviewImage = new Sprite(texture, null, null); PreviewImage = new Sprite(texture, null, null);
} }
@@ -9,7 +9,7 @@ namespace Barotrauma
{ {
partial class WayPoint : MapEntity partial class WayPoint : MapEntity
{ {
private static Dictionary<SpawnType, Sprite> iconSprites; private static Dictionary<string, Sprite> iconSprites;
private const int WaypointSize = 12, SpawnPointSize = 32; private const int WaypointSize = 12, SpawnPointSize = 32;
public override bool IsVisible(Rectangle worldView) public override bool IsVisible(Rectangle worldView)
@@ -56,10 +56,18 @@ namespace Barotrauma
Color.White); Color.White);
} }
Sprite sprite = iconSprites[SpawnType]; Sprite sprite = iconSprites[SpawnType.ToString()];
if (spawnType == SpawnType.Human && AssignedJob?.Icon != null) if (spawnType == SpawnType.Human && AssignedJob?.Icon != null)
{ {
sprite = iconSprites[SpawnType.Path]; sprite = iconSprites["Path"];
}
else if (ConnectedDoor != null)
{
sprite = iconSprites["Door"];
}
else if (Ladders != null)
{
sprite = iconSprites["Ladder"];
} }
sprite.Draw(spriteBatch, drawPos, clr, scale: iconSize / (float)sprite.SourceRect.Width, depth: 0.001f); sprite.Draw(spriteBatch, drawPos, clr, scale: iconSize / (float)sprite.SourceRect.Width, depth: 0.001f);
sprite.RelativeOrigin = Vector2.One * 0.5f; sprite.RelativeOrigin = Vector2.One * 0.5f;
@@ -1,5 +1,5 @@
using System; using System;
using System.IO; using Barotrauma.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using Barotrauma.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@@ -18,8 +18,8 @@ namespace Barotrauma.Networking
public static void Start(ProcessStartInfo processInfo) public static void Start(ProcessStartInfo processInfo)
{ {
writePipe = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable); writePipe = new AnonymousPipeServerStream(PipeDirection.Out, System.IO.HandleInheritability.Inheritable);
readPipe = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable); readPipe = new AnonymousPipeServerStream(PipeDirection.In, System.IO.HandleInheritability.Inheritable);
writeStream = writePipe; readStream = readPipe; writeStream = writePipe; readStream = readPipe;
@@ -38,6 +38,13 @@ namespace Barotrauma.Networking
localHandlesDisposed = true; localHandlesDisposed = true;
} }
public static void ClosePipes()
{
writePipe?.Close();
readPipe?.Close();
shutDown = true;
}
public static void ShutDown() public static void ShutDown()
{ {
Process?.Kill(); Process = null; Process?.Kill(); Process = null;
@@ -1,7 +1,7 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Xml; using System.Xml;
@@ -100,7 +100,7 @@ namespace Barotrauma.Networking
WriteStream = null; WriteStream = null;
} }
WriteStream = new FileStream(FilePath, FileMode.Create, FileAccess.Write, FileShare.None); WriteStream = File.Open(FilePath, System.IO.FileMode.Create, System.IO.FileAccess.Write);
TimeStarted = Environment.TickCount; TimeStarted = Environment.TickCount;
} }
@@ -259,7 +259,7 @@ namespace Barotrauma.Networking
{ {
newTransfer.OpenStream(); newTransfer.OpenStream();
} }
catch (IOException e) catch (System.IO.IOException e)
{ {
if (i < maxRetries) if (i < maxRetries)
{ {
@@ -422,7 +422,7 @@ namespace Barotrauma.Networking
} }
if (string.IsNullOrEmpty(fileName) || if (string.IsNullOrEmpty(fileName) ||
fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1) fileName.IndexOfAny(Path.GetInvalidFileNameChars().ToArray()) > -1)
{ {
errorMessage = "Illegal characters in file name ''" + fileName + "''"; errorMessage = "Illegal characters in file name ''" + fileName + "''";
return false; return false;
@@ -455,7 +455,7 @@ namespace Barotrauma.Networking
switch (fileTransfer.FileType) switch (fileTransfer.FileType)
{ {
case FileTransferType.Submarine: case FileTransferType.Submarine:
Stream stream; System.IO.Stream stream;
try try
{ {
stream = SaveUtil.DecompressFiletoStream(fileTransfer.FilePath); stream = SaveUtil.DecompressFiletoStream(fileTransfer.FilePath);
@@ -3,7 +3,7 @@ using Barotrauma.Steam;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -52,7 +52,7 @@ namespace Barotrauma.Networking
public GUITickBox EndVoteTickBox; public GUITickBox EndVoteTickBox;
private GUIComponent buttonContainer; private GUIComponent buttonContainer;
private NetStats netStats; public readonly NetStats NetStats;
protected GUITickBox cameraFollowsSub; protected GUITickBox cameraFollowsSub;
@@ -169,9 +169,9 @@ namespace Barotrauma.Networking
allowReconnect = true; allowReconnect = true;
netStats = new NetStats(); NetStats = new NetStats();
inGameHUD = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: null) inGameHUD = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas), style: null)
{ {
CanBeFocused = false CanBeFocused = false
}; };
@@ -569,14 +569,7 @@ namespace Barotrauma.Networking
} }
} }
/*TODO: reimplement NetStats.Update(deltaTime);
if (ShowNetStats && client?.ServerConnection != null)
{
netStats.AddValue(NetStats.NetStatType.ReceivedBytes, client.ServerConnection.Statistics.ReceivedBytes);
netStats.AddValue(NetStats.NetStatType.SentBytes, client.ServerConnection.Statistics.SentBytes);
netStats.AddValue(NetStats.NetStatType.ResentMessages, client.ServerConnection.Statistics.ResentMessages);
netStats.Update(deltaTime);
}*/
UpdateHUD(deltaTime); UpdateHUD(deltaTime);
@@ -781,7 +774,7 @@ namespace Barotrauma.Networking
if (readyToStart && !CoroutineManager.IsCoroutineRunning("WaitForStartRound")) if (readyToStart && !CoroutineManager.IsCoroutineRunning("WaitForStartRound"))
{ {
CoroutineManager.StartCoroutine(GameMain.NetLobbyScreen.WaitForStartRound(startButton: null, allowCancel: false), "WaitForStartRound"); CoroutineManager.StartCoroutine(GameMain.NetLobbyScreen.WaitForStartRound(startButton: null), "WaitForStartRound");
} }
break; break;
case ServerPacketHeader.STARTGAME: case ServerPacketHeader.STARTGAME:
@@ -1455,6 +1448,15 @@ namespace Barotrauma.Networking
var teamID = i == 0 ? Character.TeamType.Team1 : Character.TeamType.Team2; var teamID = i == 0 ? Character.TeamType.Team1 : Character.TeamType.Team2;
Submarine.MainSubs[i].TeamID = teamID; Submarine.MainSubs[i].TeamID = teamID;
foreach (Item item in Item.ItemList)
{
if (item.Submarine == null) { continue; }
if (item.Submarine != Submarine.MainSubs[i] && !Submarine.MainSubs[i].DockedTo.Contains(item.Submarine)) { continue; }
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
{
wifiComponent.TeamID = Submarine.MainSubs[i].TeamID;
}
}
foreach (Submarine sub in Submarine.MainSubs[i].DockedTo) foreach (Submarine sub in Submarine.MainSubs[i].DockedTo)
{ {
sub.TeamID = teamID; sub.TeamID = teamID;
@@ -1891,8 +1893,8 @@ namespace Barotrauma.Networking
DebugConsole.ThrowError("Writing object data to \"crashreport_object.bin\", please send this file to us at http://github.com/Regalis11/Barotrauma/issues"); DebugConsole.ThrowError("Writing object data to \"crashreport_object.bin\", please send this file to us at http://github.com/Regalis11/Barotrauma/issues");
using (FileStream fl = File.Open("crashreport_object.bin", FileMode.Create)) using (FileStream fl = File.Open("crashreport_object.bin", System.IO.FileMode.Create))
using (BinaryWriter sw = new BinaryWriter(fl)) using (System.IO.BinaryWriter sw = new System.IO.BinaryWriter(fl))
{ {
sw.Write(inc.Buffer, (int)(prevBytePos - prevByteLength), (int)(prevByteLength)); sw.Write(inc.Buffer, (int)(prevBytePos - prevByteLength), (int)(prevByteLength));
} }
@@ -2759,15 +2761,15 @@ namespace Barotrauma.Networking
if (!ShowNetStats) return; if (!ShowNetStats) return;
netStats.Draw(spriteBatch, new Rectangle(300, 10, 300, 150)); NetStats.Draw(spriteBatch, new Rectangle(300, 10, 300, 150));
/* TODO: reimplement
int width = 200, height = 300; int width = 200, height = 300;
int x = GameMain.GraphicsWidth - width, y = (int)(GameMain.GraphicsHeight * 0.3f); int x = GameMain.GraphicsWidth - width, y = (int)(GameMain.GraphicsHeight * 0.3f);
GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black * 0.7f, true); GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black * 0.7f, true);
GUI.Font.DrawString(spriteBatch, "Network statistics:", new Vector2(x + 10, y + 10), Color.White); GUI.Font.DrawString(spriteBatch, "Network statistics:", new Vector2(x + 10, y + 10), Color.White);
/* TODO: reimplement
if (client.ServerConnection != null) if (client.ServerConnection != null)
{ {
GUI.Font.DrawString(spriteBatch, "Ping: " + (int)(client.ServerConnection.AverageRoundtripTime * 1000.0f) + " ms", new Vector2(x + 10, y + 25), Color.White); GUI.Font.DrawString(spriteBatch, "Ping: " + (int)(client.ServerConnection.AverageRoundtripTime * 1000.0f) + " ms", new Vector2(x + 10, y + 25), Color.White);
@@ -14,10 +14,10 @@ namespace Barotrauma.Networking
ResentMessages = 2 ResentMessages = 2
} }
private Graph[] graphs; private readonly Graph[] graphs;
private float[] totalValue; private readonly float[] totalValue;
private float[] lastValue; private readonly float[] lastValue;
const float UpdateInterval = 0.1f; const float UpdateInterval = 0.1f;
float updateTimer; float updateTimer;
@@ -37,9 +37,7 @@ namespace Barotrauma.Networking
public void AddValue(NetStatType statType, float value) public void AddValue(NetStatType statType, float value)
{ {
float valueChange = value - lastValue[(int)statType]; float valueChange = value - lastValue[(int)statType];
totalValue[(int)statType] += valueChange; totalValue[(int)statType] += valueChange;
lastValue[(int)statType] = value; lastValue[(int)statType] = value;
} }
@@ -51,7 +49,6 @@ namespace Barotrauma.Networking
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
graphs[i].Update(totalValue[i] / UpdateInterval); graphs[i].Update(totalValue[i] / UpdateInterval);
totalValue[i] = 0.0f; totalValue[i] = 0.0f;
} }
@@ -64,23 +61,22 @@ namespace Barotrauma.Networking
GUI.DrawRectangle(spriteBatch, rect, Color.Black * 0.4f, true); GUI.DrawRectangle(spriteBatch, rect, Color.Black * 0.4f, true);
graphs[(int)NetStatType.ReceivedBytes].Draw(spriteBatch, rect, null, 0.0f, Color.Cyan); graphs[(int)NetStatType.ReceivedBytes].Draw(spriteBatch, rect, null, 0.0f, Color.Cyan);
graphs[(int)NetStatType.SentBytes].Draw(spriteBatch, rect, null, 0.0f, GUI.Style.Orange); graphs[(int)NetStatType.SentBytes].Draw(spriteBatch, rect, null, 0.0f, GUI.Style.Orange);
if (graphs[(int)NetStatType.ResentMessages].Average() > 0)
graphs[(int)NetStatType.ResentMessages].Draw(spriteBatch, rect, null, 0.0f, GUI.Style.Red); {
graphs[(int)NetStatType.ResentMessages].Draw(spriteBatch, rect, null, 0.0f, GUI.Style.Red);
GUI.SmallFont.DrawString(spriteBatch, "Peak resent: " + graphs[(int)NetStatType.ResentMessages].LargestValue() + " messages/s",
new Vector2(rect.Right + 10, rect.Y + 50), GUI.Style.Red);
}
GUI.SmallFont.DrawString(spriteBatch, GUI.SmallFont.DrawString(spriteBatch,
"Peak received: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.ReceivedBytes].LargestValue()) + "/s " + "Peak received: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.ReceivedBytes].LargestValue()) + "/s " +
"Avg received: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.ReceivedBytes].Average()) + "/s", "Avg received: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.ReceivedBytes].Average()) + "/s",
new Vector2(rect.Right + 10, rect.Y + 10), Color.Cyan); new Vector2(rect.Right + 10, rect.Y + 10), Color.Cyan);
GUI.SmallFont.DrawString(spriteBatch, "Peak sent: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.SentBytes].LargestValue()) + "/s " + GUI.SmallFont.DrawString(spriteBatch, "Peak sent: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.SentBytes].LargestValue()) + "/s " +
"Avg sent: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.SentBytes].Average()) + "/s", "Avg sent: " + MathUtils.GetBytesReadable((int)graphs[(int)NetStatType.SentBytes].Average()) + "/s",
new Vector2(rect.Right + 10, rect.Y + 30), GUI.Style.Orange); new Vector2(rect.Right + 10, rect.Y + 30), GUI.Style.Orange);
GUI.SmallFont.DrawString(spriteBatch, "Peak resent: " + graphs[(int)NetStatType.ResentMessages].LargestValue() + " messages/s",
new Vector2(rect.Right + 10, rect.Y + 50), GUI.Style.Red);
#if DEBUG #if DEBUG
/*int y = 10; /*int y = 10;
@@ -97,6 +97,9 @@ namespace Barotrauma.Networking
incomingLidgrenMessages.Clear(); incomingLidgrenMessages.Clear();
netClient.ReadMessages(incomingLidgrenMessages); netClient.ReadMessages(incomingLidgrenMessages);
GameMain.Client?.NetStats?.AddValue(NetStats.NetStatType.ReceivedBytes, netClient.Statistics.ReceivedBytes);
GameMain.Client?.NetStats?.AddValue(NetStats.NetStatType.SentBytes, netClient.Statistics.SentBytes);
foreach (NetIncomingMessage inc in incomingLidgrenMessages) foreach (NetIncomingMessage inc in incomingLidgrenMessages)
{ {
if (inc.SenderConnection != (ServerConnection as LidgrenConnection).NetConnection) { continue; } if (inc.SenderConnection != (ServerConnection as LidgrenConnection).NetConnection) { continue; }
@@ -18,6 +18,8 @@ namespace Barotrauma.Networking
private double timeout; private double timeout;
private double heartbeatTimer; private double heartbeatTimer;
private long sentBytes, receivedBytes;
private List<IReadMessage> incomingInitializationMessages; private List<IReadMessage> incomingInitializationMessages;
private List<IReadMessage> incomingDataMessages; private List<IReadMessage> incomingDataMessages;
@@ -63,6 +65,7 @@ namespace Barotrauma.Networking
outMsg.Write((byte)ConnectionInitialization.ConnectionStarted); outMsg.Write((byte)ConnectionInitialization.ConnectionStarted);
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable); Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
initializationStep = ConnectionInitialization.SteamTicketAndVersion; initializationStep = ConnectionInitialization.SteamTicketAndVersion;
@@ -99,11 +102,11 @@ namespace Barotrauma.Networking
if (isConnectionInitializationStep) if (isConnectionInitializationStep)
{ {
ulong low = Lidgren.Network.NetBitWriter.ReadUInt32(data, 32, 8); ulong low = Lidgren.Network.NetBitWriter.ReadUInt32(data, 32, 8);
ulong high = Lidgren.Network.NetBitWriter.ReadUInt32(data, 32, 8+32); ulong high = Lidgren.Network.NetBitWriter.ReadUInt32(data, 32, 8 + 32);
ulong lobbyId = low + (high << 32); ulong lobbyId = low + (high << 32);
Steam.SteamManager.JoinLobby(lobbyId, false); Steam.SteamManager.JoinLobby(lobbyId, false);
IReadMessage inc = new ReadOnlyMessage(data, false, 1+8, dataLength - 9, ServerConnection); IReadMessage inc = new ReadOnlyMessage(data, false, 1 + 8, dataLength - 9, ServerConnection);
if (initializationStep != ConnectionInitialization.Success) if (initializationStep != ConnectionInitialization.Success)
{ {
incomingInitializationMessages.Add(inc); incomingInitializationMessages.Add(inc);
@@ -137,16 +140,20 @@ namespace Barotrauma.Networking
timeout -= deltaTime; timeout -= deltaTime;
heartbeatTimer -= deltaTime; heartbeatTimer -= deltaTime;
for (int i=0;i<100;i++) for (int i = 0; i < 100; i++)
{ {
if (!Steamworks.SteamNetworking.IsP2PPacketAvailable()) { break; } if (!Steamworks.SteamNetworking.IsP2PPacketAvailable()) { break; }
var packet = Steamworks.SteamNetworking.ReadP2PPacket(); var packet = Steamworks.SteamNetworking.ReadP2PPacket();
if (packet.HasValue) if (packet.HasValue)
{ {
OnP2PData(packet?.SteamId ?? 0, packet?.Data, packet?.Data.Length ?? 0, 0); OnP2PData(packet?.SteamId ?? 0, packet?.Data, packet?.Data.Length ?? 0, 0);
receivedBytes += packet?.Data.Length ?? 0;
} }
} }
GameMain.Client?.NetStats?.AddValue(NetStats.NetStatType.ReceivedBytes, receivedBytes);
GameMain.Client?.NetStats?.AddValue(NetStats.NetStatType.SentBytes, sentBytes);
if (heartbeatTimer < 0.0) if (heartbeatTimer < 0.0)
{ {
IWriteMessage outMsg = new WriteOnlyMessage(); IWriteMessage outMsg = new WriteOnlyMessage();
@@ -154,6 +161,7 @@ namespace Barotrauma.Networking
outMsg.Write((byte)PacketHeader.IsHeartbeatMessage); outMsg.Write((byte)PacketHeader.IsHeartbeatMessage);
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Unreliable); Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Unreliable);
sentBytes += outMsg.LengthBytes;
heartbeatTimer = 5.0; heartbeatTimer = 5.0;
} }
@@ -227,6 +235,7 @@ namespace Barotrauma.Networking
heartbeatTimer = 5.0; heartbeatTimer = 5.0;
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable); Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
break; break;
case ConnectionInitialization.ContentPackageOrder: case ConnectionInitialization.ContentPackageOrder:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion || if (initializationStep == ConnectionInitialization.SteamTicketAndVersion ||
@@ -254,7 +263,7 @@ namespace Barotrauma.Networking
} }
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable); Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
break; break;
case ConnectionInitialization.Password: case ConnectionInitialization.Password:
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) { initializationStep = ConnectionInitialization.Password; } if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) { initializationStep = ConnectionInitialization.Password; }
@@ -334,6 +343,7 @@ namespace Barotrauma.Networking
private void Send(byte[] buf, int length, Steamworks.P2PSend sendType) private void Send(byte[] buf, int length, Steamworks.P2PSend sendType)
{ {
bool successSend = Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, buf, length + 4, 0, sendType); bool successSend = Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, buf, length + 4, 0, sendType);
sentBytes += length + 4;
if (!successSend) if (!successSend)
{ {
if (sendType != Steamworks.P2PSend.Reliable) if (sendType != Steamworks.P2PSend.Reliable)
@@ -341,6 +351,7 @@ namespace Barotrauma.Networking
DebugConsole.Log("WARNING: message couldn't be sent unreliably, forcing reliable send (" + length.ToString() + " bytes)"); DebugConsole.Log("WARNING: message couldn't be sent unreliably, forcing reliable send (" + length.ToString() + " bytes)");
sendType = Steamworks.P2PSend.Reliable; sendType = Steamworks.P2PSend.Reliable;
successSend = Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, buf, length + 4, 0, sendType); successSend = Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, buf, length + 4, 0, sendType);
sentBytes += length + 4;
} }
if (!successSend) if (!successSend)
{ {
@@ -364,6 +375,7 @@ namespace Barotrauma.Networking
heartbeatTimer = 5.0; heartbeatTimer = 5.0;
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable); Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
} }
public override void Close(string msg = null) public override void Close(string msg = null)
@@ -380,6 +392,7 @@ namespace Barotrauma.Networking
outMsg.Write(msg ?? "Disconnected"); outMsg.Write(msg ?? "Disconnected");
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable); Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
Thread.Sleep(100); Thread.Sleep(100);
@@ -13,7 +13,9 @@ namespace Barotrauma.Networking
private bool isActive; private bool isActive;
private ConnectionInitialization initializationStep; private ConnectionInitialization initializationStep;
private UInt64 selfSteamID; private readonly UInt64 selfSteamID;
private long sentBytes, receivedBytes;
class RemotePeer class RemotePeer
{ {
@@ -204,22 +206,24 @@ namespace Barotrauma.Networking
} }
} }
for (int i=0;i<100;i++) for (int i = 0; i < 100; i++)
{ {
if (!Steamworks.SteamNetworking.IsP2PPacketAvailable()) { break; } if (!Steamworks.SteamNetworking.IsP2PPacketAvailable()) { break; }
var packet = Steamworks.SteamNetworking.ReadP2PPacket(); var packet = Steamworks.SteamNetworking.ReadP2PPacket();
if (packet.HasValue) if (packet.HasValue)
{ {
OnP2PData(packet?.SteamId ?? 0, packet?.Data, packet?.Data.Length ?? 0, 0); OnP2PData(packet?.SteamId ?? 0, packet?.Data, packet?.Data.Length ?? 0, 0);
receivedBytes += packet?.Data.Length ?? 0;
} }
} }
GameMain.Client?.NetStats?.AddValue(NetStats.NetStatType.ReceivedBytes, receivedBytes);
GameMain.Client?.NetStats?.AddValue(NetStats.NetStatType.SentBytes, sentBytes);
while (ChildServerRelay.Read(out byte[] incBuf)) while (ChildServerRelay.Read(out byte[] incBuf))
{ {
ChildServerRelay.DisposeLocalHandles(); ChildServerRelay.DisposeLocalHandles();
IReadMessage inc = new ReadOnlyMessage(incBuf, false, 0, incBuf.Length, ServerConnection); IReadMessage inc = new ReadOnlyMessage(incBuf, false, 0, incBuf.Length, ServerConnection);
HandleDataMessage(inc); HandleDataMessage(inc);
} }
} }
@@ -295,6 +299,7 @@ namespace Barotrauma.Networking
} }
bool successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, 0, sendType); bool successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, 0, sendType);
sentBytes += p2pData.Length;
if (!successSend) if (!successSend)
{ {
@@ -303,6 +308,7 @@ namespace Barotrauma.Networking
DebugConsole.Log("WARNING: message couldn't be sent unreliably, forcing reliable send (" + p2pData.Length.ToString() + " bytes)"); DebugConsole.Log("WARNING: message couldn't be sent unreliably, forcing reliable send (" + p2pData.Length.ToString() + " bytes)");
sendType = Steamworks.P2PSend.Reliable; sendType = Steamworks.P2PSend.Reliable;
successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, 0, sendType); successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, 0, sendType);
sentBytes += p2pData.Length;
} }
if (!successSend) if (!successSend)
{ {
@@ -336,7 +342,6 @@ namespace Barotrauma.Networking
byte[] msgToSend = (byte[])outMsg.Buffer.Clone(); byte[] msgToSend = (byte[])outMsg.Buffer.Clone();
Array.Resize(ref msgToSend, outMsg.LengthBytes); Array.Resize(ref msgToSend, outMsg.LengthBytes);
ChildServerRelay.Write(msgToSend); ChildServerRelay.Write(msgToSend);
return; return;
} }
else else
@@ -369,6 +374,7 @@ namespace Barotrauma.Networking
outMsg.Write(msg); outMsg.Write(msg);
Steamworks.SteamNetworking.SendP2PPacket(peer.SteamID, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable); Steamworks.SteamNetworking.SendP2PPacket(peer.SteamID, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
} }
else else
{ {
@@ -405,7 +411,7 @@ namespace Barotrauma.Networking
ClosePeerSession(remotePeers[i]); ClosePeerSession(remotePeers[i]);
} }
ChildServerRelay.ShutDown(); ChildServerRelay.ClosePipes();
OnDisconnect?.Invoke(); OnDisconnect?.Invoke();
@@ -26,10 +26,13 @@ namespace Barotrauma.Networking
public void CreateLogFrame() public void CreateLogFrame()
{ {
LogFrame = new GUIButton(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker") LogFrame = new GUIButton(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null)
{ {
OnClicked = (btn, userdata) => { if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) LogFrame = null; return true; } OnClicked = (btn, userdata) => { if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) LogFrame = null; return true; }
}; };
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, LogFrame.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
new GUIButton(new RectTransform(Vector2.One, LogFrame.RectTransform), "", style: null).OnClicked += (btn, userData) => new GUIButton(new RectTransform(Vector2.One, LogFrame.RectTransform), "", style: null).OnClicked += (btn, userData) =>
{ {
LogFrame = null; LogFrame = null;
@@ -266,7 +266,9 @@ namespace Barotrauma.Networking
} }
//background frame //background frame
settingsFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: null, color: Color.Black * 0.5f); settingsFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null);
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, settingsFrame.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
new GUIButton(new RectTransform(Vector2.One, settingsFrame.RectTransform), "", style: null).OnClicked += (btn, userData) => new GUIButton(new RectTransform(Vector2.One, settingsFrame.RectTransform), "", style: null).OnClicked += (btn, userData) =>
{ {
if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) { ToggleSettingsFrame(btn, userData); } if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) { ToggleSettingsFrame(btn, userData); }
@@ -2,13 +2,12 @@
using RestSharp; using RestSharp;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using RestSharp.Contrib; using RestSharp.Contrib;
using System.Xml.Linq; using System.Xml.Linq;
using System.Xml;
using Color = Microsoft.Xna.Framework.Color; using Color = Microsoft.Xna.Framework.Color;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -250,7 +249,8 @@ namespace Barotrauma.Steam
} }
}; };
Steamworks.Data.LobbyQuery lobbyQuery = Steamworks.SteamMatchmaking.CreateLobbyQuery().FilterDistanceWorldwide(); //TODO: find a better strategy to fetch all lobbies, this is gonna take forever if we actually have 10000 lobbies
Steamworks.Data.LobbyQuery lobbyQuery = Steamworks.SteamMatchmaking.CreateLobbyQuery().FilterDistanceWorldwide().WithMaxResults(10000);
TaskPool.Add(Task.Run(async () => TaskPool.Add(Task.Run(async () =>
{ {
@@ -578,7 +578,7 @@ namespace Barotrauma.Steam
if (!isInitialized) return; if (!isInitialized) return;
var query = new Steamworks.Ugc.Query(Steamworks.UgcType.All) var query = new Steamworks.Ugc.Query(Steamworks.UgcType.All)
.RankedByTotalUniqueSubscriptions() .RankedByTrend()
.WithLongDescription(); .WithLongDescription();
if (requireTags != null) query.WithTags(requireTags); if (requireTags != null) query.WithTags(requireTags);
@@ -754,7 +754,7 @@ namespace Barotrauma.Steam
if (!CheckWorkshopItemEnabled(existingItem)) if (!CheckWorkshopItemEnabled(existingItem))
{ {
if (!EnableWorkShopItem(existingItem, false, out string errorMsg)) if (!EnableWorkShopItem(existingItem, out string errorMsg))
{ {
DebugConsole.NewMessage(errorMsg, Color.Red); DebugConsole.NewMessage(errorMsg, Color.Red);
new GUIMessageBox( new GUIMessageBox(
@@ -881,6 +881,10 @@ namespace Barotrauma.Steam
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);
contentPackage.SteamWorkshopUrl = $"http://steamcommunity.com/sharedfiles/filedetails/?source=Facepunch.Steamworks&id={task.Result.FileId.Value}"; contentPackage.SteamWorkshopUrl = $"http://steamcommunity.com/sharedfiles/filedetails/?source=Facepunch.Steamworks&id={task.Result.FileId.Value}";
//NOTE: This sets InstallTime one hour into the future to guarantee
//that the published content package won't be autoupdated incorrectly.
//Change if it causes issues.
contentPackage.InstallTime = DateTime.UtcNow + TimeSpan.FromHours(1);
contentPackage.Save(contentPackage.Path); contentPackage.Save(contentPackage.Path);
SubscribeToWorkshopItem(task.Result.FileId); SubscribeToWorkshopItem(task.Result.FileId);
@@ -892,7 +896,7 @@ namespace Barotrauma.Steam
/// <summary> /// <summary>
/// Enables a workshop item by moving it to the game folder. /// Enables a workshop item by moving it to the game folder.
/// </summary> /// </summary>
public static bool EnableWorkShopItem(Steamworks.Ugc.Item? item, bool allowFileOverwrite, out string errorMsg, bool selectContentPackage = false, bool suppressInstallNotif = false) public static bool EnableWorkShopItem(Steamworks.Ugc.Item? item, out string errorMsg, bool selectContentPackage = false, bool suppressInstallNotif = false)
{ {
if (!(item?.IsInstalled ?? false)) if (!(item?.IsInstalled ?? false))
{ {
@@ -916,7 +920,8 @@ namespace Barotrauma.Steam
}; };
string newContentPackagePath = GetWorkshopItemContentPackagePath(contentPackage); string newContentPackagePath = GetWorkshopItemContentPackagePath(contentPackage);
if (ContentPackage.List.Any(cp => cp.Path.CleanUpPath() == newContentPackagePath.CleanUpPath())) List<ContentPackage> existingPackages = ContentPackage.List.Where(cp => cp.Path.CleanUpPath() == newContentPackagePath.CleanUpPath()).ToList();
if (existingPackages.Any())
{ {
if (item?.Owner.Id != Steamworks.SteamClient.SteamId) if (item?.Owner.Id != Steamworks.SteamClient.SteamId)
{ {
@@ -952,15 +957,9 @@ namespace Barotrauma.Steam
{ {
if (modCopiesInProgress.ContainsKey(item.Value.Id)) if (modCopiesInProgress.ContainsKey(item.Value.Id))
{ {
if (!modCopiesInProgress[item.Value.Id].IsCompleted && errorMsg = ""; return true;
!modCopiesInProgress[item.Value.Id].IsFaulted &&
!modCopiesInProgress[item.Value.Id].IsCanceled)
{
errorMsg = ""; return true;
}
modCopiesInProgress.Remove(item.Value.Id);
} }
newTask = CopyWorkShopItemAsync(item, contentPackage, newContentPackagePath, metaDataFilePath, allowFileOverwrite); newTask = CopyWorkShopItemAsync(item, contentPackage, newContentPackagePath, metaDataFilePath);
modCopiesInProgress.Add(item.Value.Id, newTask); modCopiesInProgress.Add(item.Value.Id, newTask);
} }
@@ -968,67 +967,85 @@ namespace Barotrauma.Steam
contentPackage, contentPackage,
(task, cp) => (task, cp) =>
{ {
if (task.IsFaulted || task.IsCanceled) try
{ {
DebugConsole.ThrowError($"Failed to copy \"{item?.Title}\"", task.Exception); if (task.IsFaulted || task.IsCanceled)
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Red);
return;
}
if (!string.IsNullOrWhiteSpace(task.Result))
{
DebugConsole.ThrowError($"Failed to copy \"{item?.Title}\": {task.Result}");
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Red);
return;
}
GameMain.Config.SuppressModFolderWatcher = true;
var newPackage = new ContentPackage(cp.Path, newContentPackagePath)
{
SteamWorkshopUrl = item?.Url,
InstallTime = item?.Updated > item?.Created ? item?.Updated : item?.Created
};
foreach (ContentFile contentFile in newPackage.Files)
{
contentFile.Path = CorrectContentFilePath(contentFile.Path, cp, true);
}
if (!Directory.Exists(Path.GetDirectoryName(newContentPackagePath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(newContentPackagePath));
}
newPackage.Save(newContentPackagePath);
ContentPackage.List.Add(newPackage);
if (selectContentPackage)
{
if (newPackage.CorePackage)
{ {
GameMain.Config.SelectCorePackage(newPackage); DebugConsole.ThrowError($"Failed to copy \"{item?.Title}\"", task.Exception);
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Red);
return;
} }
else if (!string.IsNullOrWhiteSpace(task.Result))
{ {
GameMain.Config.SelectContentPackage(newPackage); DebugConsole.ThrowError($"Failed to copy \"{item?.Title}\": {task.Result}");
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Red);
return;
} }
GameMain.Config.SaveNewPlayerConfig();
GameMain.Config.WarnIfContentPackageSelectionDirty(); GameMain.Config.SuppressModFolderWatcher = true;
if (newPackage.Files.Any(f => f.Type == ContentType.Submarine)) var newPackage = new ContentPackage(cp.Path, newContentPackagePath)
{ {
SubmarineInfo.RefreshSavedSubs(); SteamWorkshopUrl = item?.Url,
InstallTime = item?.Updated > item?.Created ? item?.Updated : item?.Created
};
foreach (ContentFile contentFile in newPackage.Files)
{
contentFile.Path = CorrectContentFilePath(contentFile.Path, contentFile.Type, cp, true);
} }
foreach (ContentFile file in existingPackages.SelectMany(p => p.Files))
{
string path = CorrectContentFilePath(file.Path, file.Type, cp, true).CleanUpPath();
if (newPackage.Files.Any(f => f.Path.CleanUpPath() == path)) { continue; }
newPackage.AddFile(path, file.Type);
}
if (!Directory.Exists(Path.GetDirectoryName(newContentPackagePath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(newContentPackagePath));
}
newPackage.Save(newContentPackagePath);
ContentPackage.List.Add(newPackage);
if (selectContentPackage)
{
if (newPackage.CorePackage)
{
GameMain.Config.SelectCorePackage(newPackage);
}
else
{
GameMain.Config.SelectContentPackage(newPackage);
}
GameMain.Config.SaveNewPlayerConfig();
GameMain.Config.WarnIfContentPackageSelectionDirty();
if (newPackage.Files.Any(f => f.Type == ContentType.Submarine))
{
SubmarineInfo.RefreshSavedSubs();
}
}
else if (!suppressInstallNotif)
{
GameMain.MainMenuScreen?.SetEnableModsNotification(true);
}
GameMain.Config.SuppressModFolderWatcher = false;
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Green);
} }
else if (!suppressInstallNotif) catch
{ {
GameMain.MainMenuScreen?.SetEnableModsNotification(true); throw;
}
finally
{
modCopiesInProgress.Remove(item.Value.Id);
} }
GameMain.Config.SuppressModFolderWatcher = false;
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Green);
}); });
errorMsg = ""; errorMsg = "";
@@ -1039,7 +1056,7 @@ namespace Barotrauma.Steam
/// Asynchronously copies a Workshop item into the Mods folder. /// Asynchronously copies a Workshop item into the Mods folder.
/// </summary> /// </summary>
/// <returns>Returns an empty string on success, otherwise returns an error message.</returns> /// <returns>Returns an empty string on success, otherwise returns an error message.</returns>
private async static Task<string> CopyWorkShopItemAsync(Steamworks.Ugc.Item? item, ContentPackage contentPackage, string newContentPackagePath, string metaDataFilePath, bool allowFileOverwrite) private async static Task<string> CopyWorkShopItemAsync(Steamworks.Ugc.Item? item, ContentPackage contentPackage, string newContentPackagePath, string metaDataFilePath)
{ {
await Task.Yield(); await Task.Yield();
@@ -1052,13 +1069,13 @@ namespace Barotrauma.Steam
Directory.CreateDirectory(targetPath); Directory.CreateDirectory(targetPath);
File.WriteAllText(copyingPath, "TEMPORARY FILE"); File.WriteAllText(copyingPath, "TEMPORARY FILE");
SaveUtil.CopyFolder(item?.Directory, targetPath, copySubDirs: true, overwriteExisting: item?.Owner.Id != Steamworks.SteamClient.SteamId); SaveUtil.CopyFolder(item?.Directory, targetPath, copySubDirs: true, overwriteExisting: false);
File.Delete(copyingPath); File.Delete(copyingPath);
return ""; return "";
} }
var allPackageFiles = Directory.GetFiles(item?.Directory, "*", SearchOption.AllDirectories); var allPackageFiles = Directory.GetFiles(item?.Directory, "*", System.IO.SearchOption.AllDirectories);
List<string> nonContentFiles = new List<string>(); List<string> nonContentFiles = new List<string>();
foreach (string file in allPackageFiles) foreach (string file in allPackageFiles)
{ {
@@ -1069,27 +1086,24 @@ namespace Barotrauma.Steam
nonContentFiles.Add(relativePath); nonContentFiles.Add(relativePath);
} }
if (!allowFileOverwrite) /*if (File.Exists(newContentPackagePath) && !CheckFileEquality(newContentPackagePath, metaDataFilePath))
{ {
if (File.Exists(newContentPackagePath) && !CheckFileEquality(newContentPackagePath, metaDataFilePath)) errorMsg = TextManager.GetWithVariables("WorkshopErrorOverwriteOnEnable", new string[2] { "[itemname]", "[filename]" }, new string[2] { item?.Title, newContentPackagePath });
DebugConsole.NewMessage(errorMsg, Color.Red);
return errorMsg;
}
foreach (ContentFile contentFile in contentPackage.Files)
{
string sourceFile = Path.Combine(item?.Directory, contentFile.Path);
if (File.Exists(sourceFile) && File.Exists(contentFile.Path) && !CheckFileEquality(sourceFile, contentFile.Path))
{ {
errorMsg = TextManager.GetWithVariables("WorkshopErrorOverwriteOnEnable", new string[2] { "[itemname]", "[filename]" }, new string[2] { item?.Title, newContentPackagePath }); errorMsg = TextManager.GetWithVariables("WorkshopErrorOverwriteOnEnable", new string[2] { "[itemname]", "[filename]" }, new string[2] { item?.Title, contentFile.Path });
DebugConsole.NewMessage(errorMsg, Color.Red); DebugConsole.NewMessage(errorMsg, Color.Red);
return errorMsg; return errorMsg;
} }
}*/
foreach (ContentFile contentFile in contentPackage.Files)
{
string sourceFile = Path.Combine(item?.Directory, contentFile.Path);
if (File.Exists(sourceFile) && File.Exists(contentFile.Path) && !CheckFileEquality(sourceFile, contentFile.Path))
{
errorMsg = TextManager.GetWithVariables("WorkshopErrorOverwriteOnEnable", new string[2] { "[itemname]", "[filename]" }, new string[2] { item?.Title, contentFile.Path });
DebugConsole.NewMessage(errorMsg, Color.Red);
return errorMsg;
}
}
}
Directory.CreateDirectory(targetPath); Directory.CreateDirectory(targetPath);
File.WriteAllText(copyingPath, "TEMPORARY FILE"); File.WriteAllText(copyingPath, "TEMPORARY FILE");
@@ -1107,7 +1121,7 @@ namespace Barotrauma.Steam
} }
} }
contentFile.Path = CorrectContentFilePath(contentFile.Path, contentPackage, contentFile.Path = CorrectContentFilePath(contentFile.Path, contentFile.Type, contentPackage,
contentFile.Type != ContentType.Submarine); contentFile.Type != ContentType.Submarine);
//path not allowed -> the content file must be a reference to an external file (such as some vanilla file outside the Mods folder) //path not allowed -> the content file must be a reference to an external file (such as some vanilla file outside the Mods folder)
@@ -1145,16 +1159,16 @@ namespace Barotrauma.Steam
//make sure the destination directory exists //make sure the destination directory exists
Directory.CreateDirectory(Path.GetDirectoryName(contentFile.Path)); Directory.CreateDirectory(Path.GetDirectoryName(contentFile.Path));
CorrectContentFileCopy(contentPackage, sourceFile, contentFile.Path, overwrite: item?.Owner.Id != Steamworks.SteamClient.SteamId); CorrectContentFileCopy(contentPackage, sourceFile, contentFile.Path, overwrite: false);
} }
foreach (string nonContentFile in nonContentFiles) foreach (string nonContentFile in nonContentFiles)
{ {
string sourceFile = Path.Combine(item?.Directory, nonContentFile); string sourceFile = Path.Combine(item?.Directory, nonContentFile);
if (!File.Exists(sourceFile)) { continue; } if (!File.Exists(sourceFile)) { continue; }
string destinationPath = CorrectContentFilePath(nonContentFile, contentPackage, false); string destinationPath = CorrectContentFilePath(nonContentFile, ContentType.None, contentPackage, false);
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
CorrectContentFileCopy(contentPackage, sourceFile, destinationPath, overwrite: item?.Owner.Id != Steamworks.SteamClient.SteamId); CorrectContentFileCopy(contentPackage, sourceFile, destinationPath, overwrite: false);
} }
File.Delete(copyingPath); File.Delete(copyingPath);
@@ -1271,7 +1285,7 @@ namespace Barotrauma.Steam
string metaDataPath = Path.Combine(item?.Directory, MetadataFileName); string metaDataPath = Path.Combine(item?.Directory, MetadataFileName);
if (!File.Exists(metaDataPath)) if (!File.Exists(metaDataPath))
{ {
throw new FileNotFoundException("Metadata file for the Workshop item \"" + item?.Title + "\" not found. The file may be corrupted."); throw new System.IO.FileNotFoundException("Metadata file for the Workshop item \"" + item?.Title + "\" not found. The file may be corrupted.");
} }
ContentPackage contentPackage = new ContentPackage(metaDataPath); ContentPackage contentPackage = new ContentPackage(metaDataPath);
@@ -1294,7 +1308,7 @@ namespace Barotrauma.Steam
{ {
metaDataPath = Path.Combine(item?.Directory, MetadataFileName); metaDataPath = Path.Combine(item?.Directory, MetadataFileName);
} }
catch (ArgumentException e) catch (ArgumentException)
{ {
string errorMessage = "Metadata file for the Workshop item \"" + item?.Title + string errorMessage = "Metadata file for the Workshop item \"" + item?.Title +
"\" not found. Could not combine path (" + (item?.Directory ?? "directory name empty") + ")."; "\" not found. Could not combine path (" + (item?.Directory ?? "directory name empty") + ").";
@@ -1354,6 +1368,8 @@ namespace Barotrauma.Steam
public static async Task<bool> AutoUpdateWorkshopItemsAsync() public static async Task<bool> AutoUpdateWorkshopItemsAsync()
{ {
await Task.Yield();
if (!isInitialized) { return false; } if (!isInitialized) { return false; }
var query = new Steamworks.Ugc.Query(Steamworks.UgcType.All) var query = new Steamworks.Ugc.Query(Steamworks.UgcType.All)
@@ -1381,7 +1397,7 @@ namespace Barotrauma.Steam
string errorMsg; string errorMsg;
if (!CheckWorkshopItemEnabled(item)) if (!CheckWorkshopItemEnabled(item))
{ {
installedSuccessfully = EnableWorkShopItem(item, true, out errorMsg); installedSuccessfully = EnableWorkShopItem(item, out errorMsg);
} }
else if (!CheckWorkshopItemUpToDate(item)) else if (!CheckWorkshopItemUpToDate(item))
{ {
@@ -1442,10 +1458,12 @@ namespace Barotrauma.Steam
{ {
while (updateNotifications.Count > 0) while (updateNotifications.Count > 0)
{ {
float width = updateNotifications.Max(notif => GUI.Font.MeasureString(notif).X) * 1.25f;
int notificationsPerMsgBox = 20; int notificationsPerMsgBox = 20;
new GUIMessageBox("", string.Join('\n', updateNotifications.Take(notificationsPerMsgBox)), new GUIMessageBox("", string.Join('\n', updateNotifications.Take(notificationsPerMsgBox)),
relativeSize: new Microsoft.Xna.Framework.Vector2(0.5f, 0.0f), relativeSize: new Microsoft.Xna.Framework.Vector2(0.25f, 0.0f),
minSize: new Microsoft.Xna.Framework.Point(600, 0)); minSize: new Microsoft.Xna.Framework.Point((int)width, 0));
updateNotifications.RemoveRange(0, Math.Min(notificationsPerMsgBox, updateNotifications.Count)); updateNotifications.RemoveRange(0, Math.Min(notificationsPerMsgBox, updateNotifications.Count));
} }
}); });
@@ -1465,12 +1483,12 @@ namespace Barotrauma.Steam
{ {
errorMsg = ""; errorMsg = "";
if (!(item?.IsInstalled ?? false)) { return false; } if (!(item?.IsInstalled ?? false)) { return false; }
bool reenable = GameMain.Config.SelectedContentPackages.Any(p => !string.IsNullOrEmpty(p.SteamWorkshopUrl) && GetWorkshopItemIDFromUrl(p.SteamWorkshopUrl) == item?.Id);
if (item?.Owner.Id != Steamworks.SteamClient.SteamId) if (item?.Owner.Id != Steamworks.SteamClient.SteamId)
{ {
if (!DisableWorkShopItem(item, false, out errorMsg)) { return false; } if (!DisableWorkShopItem(item, false, out errorMsg)) { return false; }
} }
if (!EnableWorkShopItem(item, allowFileOverwrite: false, errorMsg: out errorMsg)) { return false; } if (!EnableWorkShopItem(item, errorMsg: out errorMsg, selectContentPackage: reenable)) { return false; }
return true; return true;
} }
@@ -1495,7 +1513,9 @@ namespace Barotrauma.Steam
attr.Name.ToString() == "characterfile") && attr.Name.ToString() == "characterfile") &&
attr.Value.CleanUpPath().Contains("/")) attr.Value.CleanUpPath().Contains("/"))
{ {
attr.Value = CorrectContentFilePath(attr.Value, package, true); ContentType type = ContentType.None;
Enum.TryParse(attr.Name.LocalName, true, out type);
attr.Value = CorrectContentFilePath(attr.Value, type, package, true);
} }
} }
@@ -1515,12 +1535,12 @@ namespace Barotrauma.Steam
if (doc != null) if (doc != null)
{ {
CorrectXMLFilePaths(package, doc.Root); CorrectXMLFilePaths(package, doc.Root);
using (MemoryStream stream = new MemoryStream()) using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
{ {
XmlWriterSettings settings = new XmlWriterSettings(); System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings();
settings.Indent = true; settings.Indent = true;
settings.Encoding = new System.Text.UTF8Encoding(false); settings.Encoding = new System.Text.UTF8Encoding(false);
using (var xmlWriter = XmlWriter.Create(stream, settings)) using (var xmlWriter = System.Xml.XmlWriter.Create(stream, settings))
{ {
doc.WriteTo(xmlWriter); doc.WriteTo(xmlWriter);
xmlWriter.Flush(); xmlWriter.Flush();
@@ -1540,15 +1560,24 @@ namespace Barotrauma.Steam
} }
} }
private static string CorrectContentFilePath(string contentFilePath, ContentPackage package, bool checkIfFileExists = false) private static string CorrectContentFilePath(string contentFilePath, ContentType type, ContentPackage package, bool checkIfFileExists = false)
{ {
string packageName = Path.GetDirectoryName(GetWorkshopItemContentPackagePath(package)); string packageName = Path.GetDirectoryName(GetWorkshopItemContentPackagePath(package));
contentFilePath = contentFilePath.CleanUpPathCrossPlatform(); contentFilePath = contentFilePath.CleanUpPathCrossPlatform();
if (checkIfFileExists && File.Exists(contentFilePath)) if (checkIfFileExists)
{ {
return contentFilePath; bool exists = File.Exists(contentFilePath);
if (type == ContentType.Executable ||
type == ContentType.ServerExecutable)
{
exists |= File.Exists(contentFilePath + ".dll");
}
if (exists)
{
return contentFilePath;
}
} }
string[] splitPath = contentFilePath.Split('/'); string[] splitPath = contentFilePath.Split('/');
@@ -275,14 +275,6 @@ namespace Barotrauma.Networking
} }
} }
public override void Write(IWriteMessage msg)
{
lock (buffers)
{
base.Write(msg);
}
}
public override void Dispose() public override void Dispose()
{ {
Instance = null; Instance = null;
@@ -10,6 +10,8 @@ namespace Barotrauma.Particles
{ {
private ParticlePrefab prefab; private ParticlePrefab prefab;
private string debugName = "Particle (uninitialized)";
public delegate void OnChangeHullHandler(Vector2 position, Hull currentHull); public delegate void OnChangeHullHandler(Vector2 position, Hull currentHull);
public OnChangeHullHandler OnChangeHull; public OnChangeHullHandler OnChangeHull;
@@ -93,9 +95,15 @@ namespace Barotrauma.Particles
get { return prefab; } get { return prefab; }
} }
public override string ToString()
{
return debugName;
}
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false) public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false)
{ {
this.prefab = prefab; this.prefab = prefab;
debugName = $"Particle ({prefab.Name})";
spriteIndex = Rand.Int(prefab.Sprites.Count); spriteIndex = Rand.Int(prefab.Sprites.Count);
@@ -89,7 +89,21 @@ namespace Barotrauma.Particles
{ {
public readonly string Name; public readonly string Name;
public readonly ParticlePrefab ParticlePrefab; private string particlePrefabName;
private ParticlePrefab particlePrefab;
public ParticlePrefab ParticlePrefab
{
get
{
if (particlePrefab == null && particlePrefabName != null)
{
particlePrefab = GameMain.ParticleManager?.FindPrefab(particlePrefabName);
if (particlePrefab == null) { particlePrefabName = null; }
}
return particlePrefab;
}
}
public readonly float AngleMin, AngleMax; public readonly float AngleMin, AngleMax;
@@ -114,8 +128,7 @@ namespace Barotrauma.Particles
public ParticleEmitterPrefab(XElement element) public ParticleEmitterPrefab(XElement element)
{ {
Name = element.Name.ToString(); Name = element.Name.ToString();
particlePrefabName = element.GetAttributeString("particle", "");
ParticlePrefab = GameMain.ParticleManager.FindPrefab(element.GetAttributeString("particle", ""));
if (element.Attribute("startrotation") == null) if (element.Attribute("startrotation") == null)
{ {
@@ -120,13 +120,13 @@ namespace Barotrauma.Particles
return CreateParticle(prefabName, position, new Vector2((float)Math.Cos(angle), (float)-Math.Sin(angle)) * speed, angle, hullGuess); return CreateParticle(prefabName, position, new Vector2((float)Math.Cos(angle), (float)-Math.Sin(angle)) * speed, angle, hullGuess);
} }
public Particle CreateParticle(string prefabName, Vector2 position, Vector2 velocity, float rotation=0.0f, Hull hullGuess = null) public Particle CreateParticle(string prefabName, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null)
{ {
ParticlePrefab prefab = FindPrefab(prefabName); ParticlePrefab prefab = FindPrefab(prefabName);
if (prefab == null) if (prefab == null)
{ {
DebugConsole.ThrowError("Particle prefab \"" + prefabName+"\" not found!"); DebugConsole.ThrowError("Particle prefab \"" + prefabName + "\" not found!");
return null; return null;
} }
@@ -135,7 +135,7 @@ namespace Barotrauma.Particles
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false) public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false)
{ {
if (particleCount >= MaxParticles || prefab == null) return null; if (particleCount >= MaxParticles || prefab == null || prefab.Sprites.Count == 0) { return null; }
Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity); Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity);
@@ -144,8 +144,8 @@ namespace Barotrauma.Particles
Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist); Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist);
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) return null; if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; }
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) return null; if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; }
if (particles[particleCount] == null) particles[particleCount] = new Particle(); if (particles[particleCount] == null) particles[particleCount] = new Particle();
@@ -255,6 +255,11 @@ namespace Barotrauma.Particles
} }
} }
if (Sprites.Count == 0)
{
DebugConsole.ThrowError($"Particle prefab \"{Name}\" in the file \"{file}\" has no sprites defined!");
}
//if velocity change in water is not given, it defaults to the normal velocity change //if velocity change in water is not given, it defaults to the normal velocity change
if (element.Attribute("velocitychangewater") == null) if (element.Attribute("velocitychangewater") == null)
{ {
@@ -17,7 +17,7 @@ namespace Barotrauma
get { return bodyShapeTexture; } get { return bodyShapeTexture; }
} }
public void Draw(DeformableSprite deformSprite, Camera cam, Vector2 scale, Color color, bool mirror = false) public void Draw(DeformableSprite deformSprite, Camera cam, Vector2 scale, Color color, bool invert = false)
{ {
if (!Enabled) return; if (!Enabled) return;
UpdateDrawPosition(); UpdateDrawPosition();
@@ -25,7 +25,7 @@ namespace Barotrauma
new Vector3(DrawPosition, MathHelper.Clamp(deformSprite.Sprite.Depth, 0, 1)), new Vector3(DrawPosition, MathHelper.Clamp(deformSprite.Sprite.Depth, 0, 1)),
deformSprite.Origin, deformSprite.Origin,
-DrawRotation, -DrawRotation,
scale, color, Dir < 0, mirror); scale, color, Dir < 0, invert);
} }
public void Draw(SpriteBatch spriteBatch, Sprite sprite, Color color, float? depth = null, float scale = 1.0f, bool mirrorX = false, bool mirrorY = false) public void Draw(SpriteBatch spriteBatch, Sprite sprite, Color color, float? depth = null, float scale = 1.0f, bool mirrorX = false, bool mirrorY = false)
@@ -25,6 +25,18 @@ namespace Barotrauma
public class KeyOrMouse public class KeyOrMouse
{ {
public Keys Key { get; private set; } public Keys Key { get; private set; }
private string name;
public string Name
{
get
{
if (name == null) { name = GetName(); }
return name;
}
}
public MouseButton MouseButton { get; private set; } public MouseButton MouseButton { get; private set; }
public KeyOrMouse(Keys keyBinding) public KeyOrMouse(Keys keyBinding)
@@ -133,6 +145,30 @@ namespace Barotrauma
hashCode = hashCode * -1521134295 + EqualityComparer<int?>.Default.GetHashCode((int)MouseButton); hashCode = hashCode * -1521134295 + EqualityComparer<int?>.Default.GetHashCode((int)MouseButton);
return hashCode; return hashCode;
} }
public string GetName()
{
if (PlayerInput.NumberKeys.Contains(Key))
{
return Key.ToString().Substring(1, 1);
}
if (MouseButton != MouseButton.None)
{
switch (MouseButton)
{
case MouseButton.PrimaryMouse:
return PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.rightmouse") : TextManager.Get("input.leftmouse");
case MouseButton.SecondaryMouse:
return PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse");
default:
return TextManager.Get("input." + MouseButton.ToString().ToLowerInvariant());
}
}
else
{
return Key.ToString();
}
}
} }
public static class PlayerInput public static class PlayerInput
@@ -155,6 +191,8 @@ namespace Barotrauma
static bool allowInput; static bool allowInput;
static bool wasWindowActive; static bool wasWindowActive;
public static readonly List<Keys> NumberKeys = new List<Keys> { Keys.D0, Keys.D1, Keys.D2, Keys.D3, Keys.D4, Keys.D5, Keys.D6, Keys.D7, Keys.D8, Keys.D9 };
#if WINDOWS #if WINDOWS
[DllImport("user32.dll")] [DllImport("user32.dll")]
static extern int GetSystemMetrics(int smIndex); static extern int GetSystemMetrics(int smIndex);
@@ -408,6 +446,12 @@ namespace Barotrauma
return (AllowInput && oldKeyboardState.IsKeyDown(button) && keyboardState.IsKeyUp(button)); return (AllowInput && oldKeyboardState.IsKeyDown(button) && keyboardState.IsKeyUp(button));
} }
public static bool InventoryKeyHit(int index)
{
if (index == -1) return false;
return AllowInput && GameMain.Config.InventoryKeyBind(index).IsHit();
}
public static bool KeyDown(Keys button) public static bool KeyDown(Keys button)
{ {
return (AllowInput && keyboardState.IsKeyDown(button)); return (AllowInput && keyboardState.IsKeyDown(button));
@@ -425,7 +469,11 @@ namespace Barotrauma
public static bool IsCtrlDown() public static bool IsCtrlDown()
{ {
#if !OSX
return KeyDown(Keys.LeftControl) || KeyDown(Keys.RightControl); return KeyDown(Keys.LeftControl) || KeyDown(Keys.RightControl);
#else
return KeyDown(Keys.LeftWindows) || KeyDown(Keys.RightWindows);
#endif
} }
public static void Update(double deltaTime) public static void Update(double deltaTime)
@@ -462,8 +510,9 @@ namespace Barotrauma
doubleClicked = false; doubleClicked = false;
if (PrimaryMouseButtonClicked()) if (PrimaryMouseButtonClicked())
{ {
if (timeSinceClick < DoubleClickDelay && float dist = (mouseState.Position - lastClickPosition).ToVector2().Length();
(mouseState.Position - lastClickPosition).ToVector2().Length() < MaxDoubleClickDistance)
if (timeSinceClick < DoubleClickDelay && dist < MaxDoubleClickDistance)
{ {
doubleClicked = true; doubleClicked = true;
timeSinceClick = DoubleClickDelay; timeSinceClick = DoubleClickDelay;
@@ -472,16 +521,15 @@ namespace Barotrauma
{ {
lastClickPosition = mouseState.Position; lastClickPosition = mouseState.Position;
} }
if (!doubleClicked && dist < MaxDoubleClickDistance)
timeSinceClick = 0.0; {
timeSinceClick = 0.0;
}
} }
if (PrimaryMouseButtonDown()) if (PrimaryMouseButtonDown())
{ {
if (timeSinceClick > DoubleClickDelay) lastClickPosition = mouseState.Position;
{
lastClickPosition = mouseState.Position;
}
} }
} }
@@ -1,7 +1,7 @@
#region Using Statements #region Using Statements
using System; using System;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using GameAnalyticsSDK.Net; using GameAnalyticsSDK.Net;
@@ -104,8 +104,6 @@ namespace Barotrauma
exeHash = new Md5Hash(stream); exeHash = new Md5Hash(stream);
} }
StreamWriter sw = new StreamWriter(filePath);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.AppendLine("Barotrauma Client crash report (generated on " + DateTime.Now + ")"); sb.AppendLine("Barotrauma Client crash report (generated on " + DateTime.Now + ")");
sb.AppendLine("\n"); sb.AppendLine("\n");
@@ -235,8 +233,7 @@ namespace Barotrauma
string crashReport = sb.ToString(); string crashReport = sb.ToString();
sw.WriteLine(crashReport); File.WriteAllText(filePath, crashReport);
sw.Close();
if (GameSettings.SaveDebugConsoleLogs) DebugConsole.SaveLogs(); if (GameSettings.SaveDebugConsoleLogs) DebugConsole.SaveLogs();
@@ -2,7 +2,7 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -2,13 +2,17 @@
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Extensions; using Barotrauma.Extensions;
using FarseerPhysics; using FarseerPhysics;
using FarseerPhysics.Dynamics; using FarseerPhysics.Dynamics;
#if DEBUG
using System.IO;
#else
using Barotrauma.IO;
#endif
namespace Barotrauma.CharacterEditor namespace Barotrauma.CharacterEditor
{ {
@@ -278,7 +282,7 @@ namespace Barotrauma.CharacterEditor
return TextManager.Get(screenTextTag + tag); return TextManager.Get(screenTextTag + tag);
} }
#region Main methods #region Main methods
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
{ {
rightArea.AddToGUIUpdateList(); rightArea.AddToGUIUpdateList();
@@ -662,9 +666,6 @@ namespace Barotrauma.CharacterEditor
} }
if (!isFrozen) if (!isFrozen)
{ {
Submarine.MainSub.SetPrevTransform(Submarine.MainSub.Position);
Submarine.MainSub.Update((float)deltaTime);
foreach (PhysicsBody body in PhysicsBody.List) foreach (PhysicsBody body in PhysicsBody.List)
{ {
body.SetPrevTransform(body.SimPosition, body.Rotation); body.SetPrevTransform(body.SimPosition, body.Rotation);
@@ -991,9 +992,9 @@ namespace Barotrauma.CharacterEditor
} }
spriteBatch.End(); spriteBatch.End();
} }
#endregion #endregion
#region Ragdoll Manipulation #region Ragdoll Manipulation
private void UpdateJointCreation() private void UpdateJointCreation()
{ {
if (jointCreationMode == JointCreationMode.None) if (jointCreationMode == JointCreationMode.None)
@@ -1320,9 +1321,9 @@ namespace Barotrauma.CharacterEditor
} }
RecreateRagdoll(); RecreateRagdoll();
} }
#endregion #endregion
#region Endless runner #region Endless runner
private int min; private int min;
private int max; private int max;
private void CalculateMovementLimits() private void CalculateMovementLimits()
@@ -1425,9 +1426,9 @@ namespace Barotrauma.CharacterEditor
AllWalls.ForEach(w => w.SetCollisionCategory(collisionCategory)); AllWalls.ForEach(w => w.SetCollisionCategory(collisionCategory));
GameMain.World.ProcessChanges(); GameMain.World.ProcessChanges();
} }
#endregion #endregion
#region Character spawning #region Character spawning
private int characterIndex = -1; private int characterIndex = -1;
private string currentCharacterConfig; private string currentCharacterConfig;
private string selectedJob = null; private string selectedJob = null;
@@ -1749,7 +1750,11 @@ namespace Barotrauma.CharacterEditor
{ {
Directory.CreateDirectory(mainFolder); Directory.CreateDirectory(mainFolder);
} }
#if DEBUG
doc.Save(configFilePath); doc.Save(configFilePath);
#else
doc.SaveSafe(configFilePath);
#endif
// Add to the selected content package // Add to the selected content package
contentPackage.AddFile(configFilePath, ContentType.Character); contentPackage.AddFile(configFilePath, ContentType.Character);
contentPackage.Save(contentPackage.Path); contentPackage.Save(contentPackage.Path);
@@ -1830,9 +1835,9 @@ namespace Barotrauma.CharacterEditor
{ {
character.Inventory?.Items.ForEachMod(i => i?.Unequip(character)); character.Inventory?.Items.ForEachMod(i => i?.Unequip(character));
} }
#endregion #endregion
#region GUI #region GUI
private static Vector2 innerScale = new Vector2(0.95f, 0.95f); private static Vector2 innerScale = new Vector2(0.95f, 0.95f);
private GUILayoutGroup rightArea, leftArea; private GUILayoutGroup rightArea, leftArea;
@@ -3147,9 +3152,9 @@ namespace Barotrauma.CharacterEditor
fileEditPanel.RectTransform.MinSize = new Point(0, (int)(layoutGroup.RectTransform.Children.Sum(c => c.MinSize.Y + layoutGroup.AbsoluteSpacing) * 1.2f)); fileEditPanel.RectTransform.MinSize = new Point(0, (int)(layoutGroup.RectTransform.Children.Sum(c => c.MinSize.Y + layoutGroup.AbsoluteSpacing) * 1.2f));
} }
#endregion #endregion
#region ToggleButtons #region ToggleButtons
private enum Direction private enum Direction
{ {
Left, Left,
@@ -3211,9 +3216,9 @@ namespace Barotrauma.CharacterEditor
} }
} }
#endregion #endregion
#region Params #region Params
private CharacterParams CharacterParams => character.Params; private CharacterParams CharacterParams => character.Params;
private List<AnimationParams> AnimParams => character.AnimController.AllAnimParams; private List<AnimationParams> AnimParams => character.AnimController.AllAnimParams;
private AnimationParams CurrentAnimation => character.AnimController.CurrentAnimationParams; private AnimationParams CurrentAnimation => character.AnimController.CurrentAnimationParams;
@@ -3464,9 +3469,9 @@ namespace Barotrauma.CharacterEditor
} }
} }
} }
#endregion #endregion
#region Helpers #region Helpers
private Vector2 ScreenToSim(float x, float y) => ScreenToSim(new Vector2(x, y)); private Vector2 ScreenToSim(float x, float y) => ScreenToSim(new Vector2(x, y));
private Vector2 ScreenToSim(Vector2 p) => ConvertUnits.ToSimUnits(Cam.ScreenToWorld(p)) + Submarine.MainSub.SimPosition; private Vector2 ScreenToSim(Vector2 p) => ConvertUnits.ToSimUnits(Cam.ScreenToWorld(p)) + Submarine.MainSub.SimPosition;
private Vector2 SimToScreen(float x, float y) => SimToScreen(new Vector2(x, y)); private Vector2 SimToScreen(float x, float y) => SimToScreen(new Vector2(x, y));
@@ -3707,9 +3712,9 @@ namespace Barotrauma.CharacterEditor
SetToggle(spritesheetToggle, true); SetToggle(spritesheetToggle, true);
} }
} }
#endregion #endregion
#region Animation Controls #region Animation Controls
private void DrawAnimationControls(SpriteBatch spriteBatch, float deltaTime) private void DrawAnimationControls(SpriteBatch spriteBatch, float deltaTime)
{ {
var collider = character.AnimController.Collider; var collider = character.AnimController.Collider;
@@ -4302,9 +4307,9 @@ namespace Barotrauma.CharacterEditor
} }
} }
} }
#endregion #endregion
#region Ragdoll #region Ragdoll
private Vector2[] corners = new Vector2[4]; private Vector2[] corners = new Vector2[4];
private Vector2[] GetLimbPhysicRect(Limb limb) private Vector2[] GetLimbPhysicRect(Limb limb)
{ {
@@ -4626,9 +4631,9 @@ namespace Barotrauma.CharacterEditor
} }
return otherLimbs; return otherLimbs;
} }
#endregion #endregion
#region Spritesheet #region Spritesheet
private List<Texture2D> textures; private List<Texture2D> textures;
private List<Texture2D> Textures private List<Texture2D> Textures
{ {
@@ -5223,9 +5228,9 @@ namespace Barotrauma.CharacterEditor
CalculateSpritesheetZoom(); CalculateSpritesheetZoom();
spriteSheetZoomBar.BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(spriteSheetMinZoom, spriteSheetMaxZoom, spriteSheetZoom)); spriteSheetZoomBar.BarScroll = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(spriteSheetMinZoom, spriteSheetMaxZoom, spriteSheetZoom));
} }
#endregion #endregion
#region Widgets as methods #region Widgets as methods
private void DrawRadialWidget(SpriteBatch spriteBatch, Vector2 drawPos, float value, string toolTip, Color color, Action<float> onClick, private void DrawRadialWidget(SpriteBatch spriteBatch, Vector2 drawPos, float value, string toolTip, Color color, Action<float> onClick,
float circleRadius = 30, int widgetSize = 10, float rotationOffset = 0, bool clockWise = true, bool displayAngle = true, bool? autoFreeze = null, bool wrapAnglePi = false, bool holdPosition = false, int rounding = 1) float circleRadius = 30, int widgetSize = 10, float rotationOffset = 0, bool clockWise = true, bool displayAngle = true, bool? autoFreeze = null, bool wrapAnglePi = false, bool holdPosition = false, int rounding = 1)
{ {
@@ -5334,9 +5339,9 @@ namespace Barotrauma.CharacterEditor
} }
} }
} }
#endregion #endregion
#region Widgets as classes #region Widgets as classes
private Dictionary<string, Widget> animationWidgets = new Dictionary<string, Widget>(); private Dictionary<string, Widget> animationWidgets = new Dictionary<string, Widget>();
private Dictionary<string, Widget> jointSelectionWidgets = new Dictionary<string, Widget>(); private Dictionary<string, Widget> jointSelectionWidgets = new Dictionary<string, Widget>();
private Dictionary<string, Widget> limbEditWidgets = new Dictionary<string, Widget>(); private Dictionary<string, Widget> limbEditWidgets = new Dictionary<string, Widget>();
@@ -5490,6 +5495,6 @@ namespace Barotrauma.CharacterEditor
return w; return w;
} }
} }
#endregion #endregion
} }
} }
@@ -1,6 +1,6 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.IO; using Barotrauma.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -151,7 +151,10 @@ namespace Barotrauma
GameMain.ParticleManager.UpdateTransforms(); GameMain.ParticleManager.UpdateTransforms();
GameMain.LightManager.ObstructVision = Character.Controlled != null && Character.Controlled.ObstructVision; GameMain.LightManager.ObstructVision =
Character.Controlled != null &&
Character.Controlled.ObstructVision &&
(Character.Controlled.ViewTarget == Character.Controlled || Character.Controlled.ViewTarget == null);
if (Character.Controlled != null) if (Character.Controlled != null)
{ {
@@ -261,7 +264,7 @@ namespace Barotrauma
graphics.SetRenderTarget(renderTargetFinal); graphics.SetRenderTarget(renderTargetFinal);
WaterRenderer.Instance.ResetBuffers(); WaterRenderer.Instance.ResetBuffers();
Hull.UpdateVertices(graphics, cam, WaterRenderer.Instance); Hull.UpdateVertices(cam, WaterRenderer.Instance);
WaterRenderer.Instance.RenderWater(spriteBatch, renderTargetWater, cam); WaterRenderer.Instance.RenderWater(spriteBatch, renderTargetWater, cam);
WaterRenderer.Instance.RenderAir(graphics, cam, renderTarget, Cam.ShaderTransform); WaterRenderer.Instance.RenderAir(graphics, cam, renderTarget, Cam.ShaderTransform);
graphics.DepthStencilState = DepthStencilState.None; graphics.DepthStencilState = DepthStencilState.None;
@@ -3,10 +3,14 @@ using Barotrauma.RuinGeneration;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
#if DEBUG
using System.IO;
using System.Xml;
#else
using Barotrauma.IO;
#endif
namespace Barotrauma namespace Barotrauma
{ {
@@ -459,7 +463,7 @@ namespace Barotrauma
Submarine.Draw(spriteBatch, false); Submarine.Draw(spriteBatch, false);
Submarine.DrawFront(spriteBatch); Submarine.DrawFront(spriteBatch);
Submarine.DrawDamageable(spriteBatch, null); Submarine.DrawDamageable(spriteBatch, null);
GUI.DrawRectangle(spriteBatch, new Rectangle(new Point(0, -Level.Loaded.Size.Y), Level.Loaded.Size), Color.White, thickness: (int)(1.0f / cam.Zoom)); GUI.DrawRectangle(spriteBatch, new Rectangle(new Point(0, -Level.Loaded.Size.Y), Level.Loaded.Size), Color.Gray, thickness: (int)(1.0f / cam.Zoom));
spriteBatch.End(); spriteBatch.End();
if (lightingEnabled.Selected) if (lightingEnabled.Selected)
@@ -497,7 +501,7 @@ namespace Barotrauma
private void SerializeAll() private void SerializeAll()
{ {
XmlWriterSettings settings = new XmlWriterSettings System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
{ {
Indent = true, Indent = true,
NewLineOnAttributes = true NewLineOnAttributes = true
@@ -578,7 +582,7 @@ namespace Barotrauma
if (elementFound) if (elementFound)
{ {
XmlWriterSettings settings = new XmlWriterSettings System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
{ {
Indent = true, Indent = true,
NewLineOnAttributes = true NewLineOnAttributes = true
@@ -595,7 +599,7 @@ namespace Barotrauma
} }
#region LevelObject Wizard #region LevelObject Wizard
private class Wizard private class Wizard
{ {
private LevelObjectPrefab newPrefab; private LevelObjectPrefab newPrefab;
@@ -677,7 +681,7 @@ namespace Barotrauma
newPrefab.Name = nameBox.Text; newPrefab.Name = nameBox.Text;
XmlWriterSettings settings = new XmlWriterSettings { Indent = true }; System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true };
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.LevelObjectPrefabs)) foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.LevelObjectPrefabs))
{ {
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path); XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
@@ -710,6 +714,6 @@ namespace Barotrauma
} }
} }
#endregion #endregion
} }
} }
@@ -10,7 +10,7 @@ using RestSharp;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
@@ -409,10 +409,9 @@ namespace Barotrauma
this.game = game; this.game = game;
menuTabs[(int)Tab.Credits] = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: null, color: Color.Black * 0.5f) menuTabs[(int)Tab.Credits] = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null);
{ new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, menuTabs[(int)Tab.Credits].RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
CanBeFocused = false
};
var creditsContainer = new GUIFrame(new RectTransform(new Vector2(0.75f, 1.5f), menuTabs[(int)Tab.Credits].RectTransform, Anchor.CenterRight), style: "OuterGlow", color: Color.Black * 0.8f); var creditsContainer = new GUIFrame(new RectTransform(new Vector2(0.75f, 1.5f), menuTabs[(int)Tab.Credits].RectTransform, Anchor.CenterRight), style: "OuterGlow", color: Color.Black * 0.8f);
creditsPlayer = new CreditsPlayer(new RectTransform(Vector2.One, creditsContainer.RectTransform), "Content/Texts/Credits.xml"); creditsPlayer = new CreditsPlayer(new RectTransform(Vector2.One, creditsContainer.RectTransform), "Content/Texts/Credits.xml");
@@ -1010,11 +1009,12 @@ namespace Barotrauma
GUI.Draw(Cam, spriteBatch); GUI.Draw(Cam, spriteBatch);
#if !UNSTABLE #if !UNSTABLE
GUI.Font.DrawString(spriteBatch, "Barotrauma v" + GameMain.Version + " (" + AssemblyInfo.GetBuildString() + ", branch " + AssemblyInfo.GetGitBranch() + ", revision " + AssemblyInfo.GetGitRevision() + ")", new Vector2(10, GameMain.GraphicsHeight - 20), Color.White * 0.7f); string versionString = "Barotrauma v" + GameMain.Version + " (" + AssemblyInfo.GetBuildString() + ", branch " + AssemblyInfo.GetGitBranch() + ", revision " + AssemblyInfo.GetGitRevision() + ")";
GUI.SmallFont.DrawString(spriteBatch, versionString, new Vector2(HUDLayoutSettings.Padding, GameMain.GraphicsHeight - GUI.SmallFont.MeasureString(versionString).Y - HUDLayoutSettings.Padding * 0.75f), Color.White * 0.7f);
#endif #endif
if (selectedTab != Tab.Credits) if (selectedTab != Tab.Credits)
{ {
Vector2 textPos = new Vector2(GameMain.GraphicsWidth - 10, GameMain.GraphicsHeight - 10); Vector2 textPos = new Vector2(GameMain.GraphicsWidth - HUDLayoutSettings.Padding, GameMain.GraphicsHeight - HUDLayoutSettings.Padding * 0.75f);
for (int i = legalCrap.Length - 1; i >= 0; i--) for (int i = legalCrap.Length - 1; i >= 0; i--)
{ {
Vector2 textSize = GUI.SmallFont.MeasureString(legalCrap[i]); Vector2 textSize = GUI.SmallFont.MeasureString(legalCrap[i]);
@@ -1069,7 +1069,7 @@ namespace Barotrauma
{ {
File.Copy(selectedSub.FilePath, Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"), true); File.Copy(selectedSub.FilePath, Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"), true);
} }
catch (IOException e) catch (System.IO.IOException e)
{ {
DebugConsole.ThrowError("Copying the file \"" + selectedSub.FilePath + "\" failed. The file may have been deleted or in use by another process. Try again or select another submarine.", e); DebugConsole.ThrowError("Copying the file \"" + selectedSub.FilePath + "\" failed. The file may have been deleted or in use by another process. Try again or select another submarine.", e);
GameAnalyticsManager.AddErrorEventOnce( GameAnalyticsManager.AddErrorEventOnce(

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